Compare commits
No commits in common. "main" and "v1.7.3" have entirely different histories.
32
.github/workflows/build.yml
vendored
@ -1,32 +0,0 @@
|
|||||||
name: Build project
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
paths:
|
|
||||||
- Source/**
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
paths:
|
|
||||||
- Source/**
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: windows-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Setup MSBuild.exe
|
|
||||||
uses: microsoft/setup-msbuild@v1.1.3
|
|
||||||
|
|
||||||
- name: Build release
|
|
||||||
run: msbuild Source/GrasscutterTools.sln /p:Configuration=Release /t:build /restore
|
|
||||||
|
|
||||||
- name: Upload build artifacts
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: GrasscutterTools
|
|
||||||
path: Source/GrasscutterTools/bin/Release/GrasscutterTools.exe
|
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 36 KiB |
BIN
Doc/Screenshots-ru/0-Home.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
Doc/Screenshots-ru/1-CustomCommands.png
Normal file
After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 18 KiB |
BIN
Doc/Screenshots-ru/10-Waypoints.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
Doc/Screenshots-ru/11-Manage.png
Normal file
After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 18 KiB |
BIN
Doc/Screenshots-ru/12-Credits.png
Normal file
After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 54 KiB |
BIN
Doc/Screenshots-ru/13-Remote.png
Normal file
After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 52 KiB |
BIN
Doc/Screenshots-ru/14-GachaBannerEditor.png
Normal file
After Width: | Height: | Size: 57 KiB |
Before Width: | Height: | Size: 95 KiB |
BIN
Doc/Screenshots-ru/15-DropEditor.png
Normal file
After Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 48 KiB |
BIN
Doc/Screenshots-ru/15-TextMapBrowser.png
Normal file
After Width: | Height: | Size: 52 KiB |
BIN
Doc/Screenshots-ru/16-MailEditor.png
Normal file
After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 7.1 KiB |
BIN
Doc/Screenshots-ru/17-ShopEditor.png
Normal file
After Width: | Height: | Size: 78 KiB |
Before Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 40 KiB |
BIN
Doc/Screenshots-ru/2-CustomArtifact.png
Normal file
After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 20 KiB |
BIN
Doc/Screenshots-ru/3-Quest.png
Normal file
After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 14 KiB |
BIN
Doc/Screenshots-ru/4-SpawnEntity.png
Normal file
After Width: | Height: | Size: 35 KiB |
BIN
Doc/Screenshots-ru/5-GiveAvatar.png
Normal file
After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 16 KiB |
BIN
Doc/Screenshots-ru/7-CustomWeapon.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
Doc/Screenshots-ru/8-GiveItem.png
Normal file
After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 15 KiB |
BIN
Doc/Screenshots-ru/9-ChangeScene.png
Normal file
After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 136 KiB |
Before Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 74 KiB |
Before Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 86 KiB |
Before Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 57 KiB |
Before Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 3.6 MiB |
Before Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 821 KiB |
Before Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 42 KiB |
75
README.md
@ -3,69 +3,19 @@
|
|||||||
[](https://github.com/jie65535/GrasscutterCommandGenerator/blob/main/LICENSE)
|
[](https://github.com/jie65535/GrasscutterCommandGenerator/blob/main/LICENSE)
|
||||||
[](https://github.com/jie65535/GrasscutterCommandGenerator/stargazers)
|
[](https://github.com/jie65535/GrasscutterCommandGenerator/stargazers)
|
||||||
[](https://github.com/jie65535/GrasscutterCommandGenerator/releases)
|
[](https://github.com/jie65535/GrasscutterCommandGenerator/releases)
|
||||||
[](https://github.com/jie65535/GrasscutterCommandGenerator/releases/latest)
|
[](https://qm.qq.com/cgi-bin/qm/qr?k=PdS9--b-n8LEAmYjX8fNFXtKDcsp4NHN&jump_from=webapi&authKey=7ty3ZCKYMKLGWLmO8O84qiNAZ0EuCnSGF+acP+74xuDMKYXXNjuPP7iUzffHz4r2)
|
||||||
[](https://github.com/jie65535/GrasscutterCommandGenerator/actions/workflows/build.yml)
|
|
||||||
[](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=fBizzp6RwJsIY7gFlmd4L-WG0V3aF8X3&authKey=mTjf%2B7jCIZess1HTRi05e5yi%2FHKA1auMwE8%2FJ960PFWk8WMATST654gWPi4OTHTZ&noverify=0&group_code=835489603)
|
|
||||||
|
|
||||||
English | [简体中文](README_zh-cn.md) | [繁體中文](README_zh-tw.md) | [Русский](README_ru-RU.md)
|
English | [简体中文](README_zh-cn.md) | [Русский](README_ru-RU.md)
|
||||||
|
|
||||||
## Commands Generator
|
## Commands Generator
|
||||||
|
|
||||||
Please download the latest committed automated build from [Action](https://github.com/jie65535/GrasscutterCommandGenerator/actions/workflows/build.yml), or a release from [Releases](https://github.com/jie65535/GrasscutterCommandGenerator/releases) (may be behind)
|
Please download the latest version from [Releases](https://github.com/jie65535/GrasscutterCommandGenerator/releases)
|
||||||
|
|
||||||
Support 简体中文, 繁體中文, English and Русский languages.
|
Support 简体中文, 繁体中文, English and Русский languages.
|
||||||
|
|
||||||
> **Warning**: app look may be different rather than on screenshots. It may also contain translation errors and a lack of certain resources. **We're welcome everyone to contribute to their [improvement](/Source/GrasscutterTools/Resources/en-us)**
|
Welcome everyone to improve the [ID Resource](/Source/GrasscutterTools/Resources/en-us).
|
||||||
|
|
||||||
## Remote command
|
|
||||||
|
|
||||||
The server require [gc-opencommand-plugin](https://github.com/jie65535/gc-opencommand-plugin) support
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
> If you cannot connect to the server, please make sure the server address is correct.
|
|
||||||
>
|
|
||||||
> It is recommended to configure the server to HTTP mode, as shown in the figure(config.json):
|
|
||||||
> 
|
|
||||||
>
|
|
||||||
> You can visit http://127.0.0.1/status/server with a browser to test whether the service is working properly.
|
|
||||||
>
|
|
||||||
> If you are not using port `80`, specify the port number to access in the url: http://127.0.0.1:443
|
|
||||||
|
|
||||||
## Update log
|
## Update log
|
||||||
|
|
||||||
### GrasscutterTools-v1.13
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### GrasscutterTools-v1.11
|
|
||||||

|
|
||||||
|
|
||||||
Commandline Usages:
|
|
||||||
```bash
|
|
||||||
GcTools.exe -help
|
|
||||||
GcTools.exe -version
|
|
||||||
GcTools.exe -c "cmd arg"
|
|
||||||
GcTools.exe -c "cmd1 arg" && GcTools -c "cmd2 arg1 arg2"
|
|
||||||
GcTools.exe -host http://127.0.0.1:443 -token 123456 -c "cmd1 arg1 arg2 | cmd2 | cmd3 arg"
|
|
||||||
```
|
|
||||||
|
|
||||||
### GrasscutterTools-v1.10
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### GrasscutterTools-v1.9
|
|
||||||

|
|
||||||
|
|
||||||
### GrasscutterTools-v1.8
|
|
||||||

|
|
||||||
|
|
||||||
### GrasscutterTools-v1.7.3
|
### GrasscutterTools-v1.7.3
|
||||||

|

|
||||||
|
|
||||||
@ -87,6 +37,21 @@ Added [AttackModifier](https://github.com/NotThorny/AttackModifier), [AttackInfu
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
## Remote command
|
||||||
|
|
||||||
|
The server require [gc-opencommand-plugin](https://github.com/jie65535/gc-opencommand-plugin) support
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
> If you cannot connect to the server, please make sure the server address is correct.
|
||||||
|
>
|
||||||
|
> It is recommended to configure the server to HTTP mode, as shown in the figure(config.json):
|
||||||
|
> 
|
||||||
|
>
|
||||||
|
> You can visit http://127.0.0.1/status/server with a browser to test whether the service is working properly.
|
||||||
|
>
|
||||||
|
> If you are not using port `80`, specify the port number to access in the url: http://127.0.0.1:443
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
|
121
README_ru-RU.md
@ -3,18 +3,39 @@
|
|||||||
[](https://github.com/jie65535/GrasscutterCommandGenerator/blob/main/LICENSE)
|
[](https://github.com/jie65535/GrasscutterCommandGenerator/blob/main/LICENSE)
|
||||||
[](https://github.com/jie65535/GrasscutterCommandGenerator/stargazers)
|
[](https://github.com/jie65535/GrasscutterCommandGenerator/stargazers)
|
||||||
[](https://github.com/jie65535/GrasscutterCommandGenerator/releases)
|
[](https://github.com/jie65535/GrasscutterCommandGenerator/releases)
|
||||||
[](https://github.com/jie65535/GrasscutterCommandGenerator/releases/latest)
|
|
||||||
[](https://github.com/jie65535/GrasscutterCommandGenerator/actions/workflows/build.yml)
|
|
||||||
|
|
||||||
[English](README.md) | [简体中文](README_zh-cn.md) | [繁體中文](README_zh-tw.md) | Русский - Перевод [Юрий Дворецкий](https://github.com/yurikenjx) (с исправлениями от [EgorBron](https://github.com/EgorBron))
|
[English](README.md) | [简体中文](README_zh-cn.md) | Русский - Перевод [Юрий Дворецкий](https://github.com/yurikenjx) (с исправлениями от [EgorBron](https://github.com/EgorBron))
|
||||||
|
|
||||||
## Генератор команд (GCG)
|
## Генератор команд (GCG)
|
||||||
|
|
||||||
Пожалуйста, загрузите последнюю подтвержденную автоматизированную сборку из [Action](https://github.com/jie65535/GrasscutterCommandGenerator/actions/workflows/build.yml) или выпуск из [Releases](https://github.com/jie65535/GrasscutterCommandGenerator/releases) (может отставать)
|
Загрузите последнюю версию из вкладки [Releases](https://github.com/jie65535/GrasscutterCommandGenerator/releases).
|
||||||
|
|
||||||
GCG поддерживает 简体中文 (китайский упр.), 繁體中文 (китайский трад.), English (английский) и Русский языки.
|
GCG поддерживает 简体中文 (китайский упр.), 繁体中文 (китайский трад.), English (английский) и Русский языки.
|
||||||
|
|
||||||
> **Warning**: вид приложения может отличаться от скриншотов. Также в нём могут присутствовать ошибки в переводе и отсутсвие некоторых ресурсов. **Мы приглашаем всех сделать вклад в их [улучшение](/Source/GrasscutterTools/Resources/ru-ru)**
|
Приглашаем всех улучшить [ID ресурсов и перевод](/Source/GrasscutterTools/Resources/ru-ru)
|
||||||
|
|
||||||
|
## Update log
|
||||||
|
|
||||||
|
### GrasscutterTools-v1.7.3
|
||||||
|

|
||||||
|
|
||||||
|
Added [AttackModifier](https://github.com/NotThorny/AttackModifier), [AttackInfusedWithItem](https://github.com/snoobi-seggs/AttackInfusedWithItem), [SwitchElementTraveller](https://github.com/Penelopeep/SwitchElementTraveller) plugins command generation
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### GrasscutterTools-v1.7.2
|
||||||
|

|
||||||
|
|
||||||
|
### GrasscutterTools-v1.7.1
|
||||||
|
- Gadgets (CHS Only)
|
||||||
|
|
||||||
|
### GrasscutterTools-v1.7.0
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## Удаленная команда (OpenCommand)
|
## Удаленная команда (OpenCommand)
|
||||||
|
|
||||||
@ -32,96 +53,34 @@ GCG поддерживает 简体中文 (китайский упр.), 繁體
|
|||||||
>
|
>
|
||||||
> Если вы не указали порт `80` в конфиге, вам нужно указать свой порт в URL-адресе (например, http://127.0.0.1:443)
|
> Если вы не указали порт `80` в конфиге, вам нужно указать свой порт в URL-адресе (например, http://127.0.0.1:443)
|
||||||
|
|
||||||
|
|
||||||
## Лог обновлений
|
|
||||||
|
|
||||||
### GrasscutterTools-v1.13
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### GrasscutterTools-v1.11
|
|
||||||

|
|
||||||
|
|
||||||
Commandline Usages:
|
|
||||||
```bash
|
|
||||||
GcTools.exe -help
|
|
||||||
GcTools.exe -version
|
|
||||||
GcTools.exe -c "cmd arg"
|
|
||||||
GcTools.exe -c "cmd1 arg" && GcTools -c "cmd2 arg1 arg2"
|
|
||||||
GcTools.exe -host http://127.0.0.1:443 -token 123456 -c "cmd1 arg1 arg2 | cmd2 | cmd3 arg"
|
|
||||||
```
|
|
||||||
|
|
||||||
### GrasscutterTools-v1.10
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### GrasscutterTools-v1.9
|
|
||||||

|
|
||||||
|
|
||||||
### GrasscutterTools-v1.8
|
|
||||||

|
|
||||||
|
|
||||||
### GrasscutterTools-v1.7.3
|
|
||||||

|
|
||||||
|
|
||||||
Добавлена поддержка генерации команд для плагинов [AttackModifier](https://github.com/NotThorny/AttackModifier), [AttackInfusedWithItem](https://github.com/snoobi-seggs/AttackInfusedWithItem), [SwitchElementTraveller](https://github.com/Penelopeep/SwitchElementTraveller).
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### GrasscutterTools-v1.7.2
|
|
||||||

|
|
||||||
|
|
||||||
### GrasscutterTools-v1.7.1
|
|
||||||
- Гаджеты (пока что только на китайском)
|
|
||||||
|
|
||||||
### GrasscutterTools-v1.7.0
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Скриншоты
|
## Скриншоты
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
@ -3,69 +3,19 @@
|
|||||||
[](https://github.com/jie65535/GrasscutterCommandGenerator/blob/main/LICENSE)
|
[](https://github.com/jie65535/GrasscutterCommandGenerator/blob/main/LICENSE)
|
||||||
[](https://github.com/jie65535/GrasscutterCommandGenerator/stargazers)
|
[](https://github.com/jie65535/GrasscutterCommandGenerator/stargazers)
|
||||||
[](https://github.com/jie65535/GrasscutterCommandGenerator/releases)
|
[](https://github.com/jie65535/GrasscutterCommandGenerator/releases)
|
||||||
[](https://github.com/jie65535/GrasscutterCommandGenerator/releases/latest)
|
[](https://qm.qq.com/cgi-bin/qm/qr?k=PdS9--b-n8LEAmYjX8fNFXtKDcsp4NHN&jump_from=webapi&authKey=7ty3ZCKYMKLGWLmO8O84qiNAZ0EuCnSGF+acP+74xuDMKYXXNjuPP7iUzffHz4r2)
|
||||||
[](https://github.com/jie65535/GrasscutterCommandGenerator/actions/workflows/build.yml)
|
|
||||||
[](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=fBizzp6RwJsIY7gFlmd4L-WG0V3aF8X3&authKey=mTjf%2B7jCIZess1HTRi05e5yi%2FHKA1auMwE8%2FJ960PFWk8WMATST654gWPi4OTHTZ&noverify=0&group_code=835489603)
|
|
||||||
|
|
||||||
[English](README.md) | 简体中文 | [繁體中文](README_zh-tw.md) | [Русский](README_ru-RU.md)
|
[English](README.md) | 简体中文 | [Русский](README_ru-RU.md)
|
||||||
|
|
||||||
## Commands Generator
|
## Commands Generator
|
||||||
|
|
||||||
请从 [Action](https://github.com/jie65535/GrasscutterCommandGenerator/actions/workflows/build.yml) 中下载最新提交的自动构建版本,或者从 [Releases](https://github.com/jie65535/GrasscutterCommandGenerator/releases) 中下载发布版本(可能落后)
|
请从 [Releases](https://github.com/jie65535/GrasscutterCommandGenerator/releases) 中获取最新版本
|
||||||
|
|
||||||
本工具支持 简体中文, 繁體中文, English 与 Русский 语言。
|
本工具支持 简体中文, 繁体中文, English 与 Русский 语言。
|
||||||
|
|
||||||
> **Warning**: 应用程序的外观可能与截图上的不同。它也可能包含翻译错误和缺乏某些资源。**我们欢迎各位为此工具做出贡献并<a href="./Source/GrasscutterTools/Resources/zh-cn">改进</a> : )**
|
欢迎大家一起来完善工具的[内置资源](/Source/GrasscutterTools/Resources/zh-cn)。
|
||||||
|
|
||||||
## 远程执行
|
## Update log
|
||||||
|
|
||||||
服务端需要 [gc-opencommand-plugin](https://github.com/jie65535/gc-opencommand-plugin) 插件支持
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
> 如果你无法连接到服务器,请确认填写的服务器地址是否正确。
|
|
||||||
>
|
|
||||||
> 建议配置服务器为HTTP模式,如图所示(config.json):
|
|
||||||
> 
|
|
||||||
>
|
|
||||||
> 你可以用浏览器访问 http://127.0.0.1/status/server 来测试服务是否正常工作。
|
|
||||||
>
|
|
||||||
> 如果使用的不是`80`端口,则要在url中指定访问的端口号:http://127.0.0.1:443
|
|
||||||
|
|
||||||
|
|
||||||
## 更新概要
|
|
||||||
|
|
||||||
### GrasscutterTools-v1.13
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### GrasscutterTools-v1.11
|
|
||||||

|
|
||||||
|
|
||||||
命令行用法:
|
|
||||||
```bash
|
|
||||||
GcTools.exe -help
|
|
||||||
GcTools.exe -version
|
|
||||||
GcTools.exe -c "cmd arg"
|
|
||||||
GcTools.exe -c "cmd1 arg" && GcTools -c "cmd2 arg1 arg2"
|
|
||||||
GcTools.exe -host http://127.0.0.1:443 -token 123456 -c "cmd1 arg1 arg2 | cmd2 | cmd3 arg"
|
|
||||||
```
|
|
||||||
|
|
||||||
### GrasscutterTools-v1.10
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### GrasscutterTools-v1.9
|
|
||||||

|
|
||||||
|
|
||||||
### GrasscutterTools-v1.8
|
|
||||||

|
|
||||||
|
|
||||||
### GrasscutterTools-v1.7.3
|
### GrasscutterTools-v1.7.3
|
||||||

|

|
||||||
@ -92,6 +42,21 @@ GcTools.exe -host http://127.0.0.1:443 -token 123456 -c "cmd1 arg1 arg2 | cmd2 |
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
## 远程执行
|
||||||
|
|
||||||
|
服务端需要 [gc-opencommand-plugin](https://github.com/jie65535/gc-opencommand-plugin) 插件支持
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
> 如果你无法连接到服务器,请确认填写的服务器地址是否正确。
|
||||||
|
>
|
||||||
|
> 建议配置服务器为HTTP模式,如图所示(config.json):
|
||||||
|
> 
|
||||||
|
>
|
||||||
|
> 你可以用浏览器访问 http://127.0.0.1/status/server 来测试服务是否正常工作。
|
||||||
|
>
|
||||||
|
> 如果使用的不是`80`端口,则要在url中指定访问的端口号:http://127.0.0.1:443
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 软件截图
|
## 软件截图
|
||||||
|
125
README_zh-tw.md
@ -1,125 +0,0 @@
|
|||||||
# Grasscutter Tools
|
|
||||||
|
|
||||||
[](https://github.com/jie65535/GrasscutterCommandGenerator/blob/main/LICENSE)
|
|
||||||
[](https://github.com/jie65535/GrasscutterCommandGenerator/stargazers)
|
|
||||||
[](https://github.com/jie65535/GrasscutterCommandGenerator/releases)
|
|
||||||
[](https://github.com/jie65535/GrasscutterCommandGenerator/releases/latest)
|
|
||||||
[](https://github.com/jie65535/GrasscutterCommandGenerator/actions/workflows/build.yml)
|
|
||||||
[](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=fBizzp6RwJsIY7gFlmd4L-WG0V3aF8X3&authKey=mTjf%2B7jCIZess1HTRi05e5yi%2FHKA1auMwE8%2FJ960PFWk8WMATST654gWPi4OTHTZ&noverify=0&group_code=835489603)
|
|
||||||
|
|
||||||
[English](README.md) | [简体中文](README_zh-cn.md) | 繁體中文 | [Русский](README_ru-RU.md)
|
|
||||||
|
|
||||||
## 指令產生工具
|
|
||||||
|
|
||||||
請從 [Action](https://github.com/jie65535/GrasscutterCommandGenerator/actions/workflows/build.yml) 中下載最新提交的自動構建版本,或者從 [Releases](https://github.com/jie65535/GrasscutterCommandGenerator/releases) 中下載發布版本(可能落後)
|
|
||||||
|
|
||||||
本工具支援 简体中文、繁體中文、English 及 Русский 上述語言。
|
|
||||||
|
|
||||||
> **Warning**: 程式中的實際外觀可能會與截圖中的內容不同。其中也可能包含翻譯錯誤及缺乏特定資源。**我們歡迎各位為此工具做出貢獻並[改進](/Source/GrasscutterTools/Resources/zh-tw)**
|
|
||||||
|
|
||||||
## 遠端控制
|
|
||||||
|
|
||||||
伺服器需要安裝 [gc-opencommand-plugin](https://github.com/jie65535/gc-opencommand-plugin) 插件
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
> 如果你無法連接至伺服器,請確認輸入的伺服器位址是否正確。
|
|
||||||
>
|
|
||||||
> 建議將伺服器調整為HTTP模式,如下圖所示(config.json):
|
|
||||||
> 
|
|
||||||
>
|
|
||||||
> 你可藉由任何瀏覽器輸入網址 http://127.0.0.1/status/server 以測試伺服器是否正常運作。
|
|
||||||
>
|
|
||||||
> 如果你並非使用`80`端口, 則須在網址後輸入指定端口: http://127.0.0.1:443
|
|
||||||
|
|
||||||
## 更新日誌
|
|
||||||
|
|
||||||
### GrasscutterTools-v1.13
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### GrasscutterTools-v1.11
|
|
||||||

|
|
||||||
|
|
||||||
Commandline Usages:
|
|
||||||
```bash
|
|
||||||
GcTools.exe -help
|
|
||||||
GcTools.exe -version
|
|
||||||
GcTools.exe -c "cmd arg"
|
|
||||||
GcTools.exe -c "cmd1 arg" && GcTools -c "cmd2 arg1 arg2"
|
|
||||||
GcTools.exe -host http://127.0.0.1:443 -token 123456 -c "cmd1 arg1 arg2 | cmd2 | cmd3 arg"
|
|
||||||
```
|
|
||||||
|
|
||||||
### GrasscutterTools-v1.10
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### GrasscutterTools-v1.9
|
|
||||||

|
|
||||||
|
|
||||||
### GrasscutterTools-v1.8
|
|
||||||

|
|
||||||
|
|
||||||
### GrasscutterTools-v1.7.3
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
新增 [攻擊修改](https://github.com/NotThorny/AttackModifier)、[攻擊注入](https://github.com/snoobi-seggs/AttackInfusedWithItem)、[主角切換元素](https://github.com/Penelopeep/SwitchElementTraveller)等插件指令產生
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### GrasscutterTools-v1.7.2
|
|
||||||

|
|
||||||
|
|
||||||
### GrasscutterTools-v1.7.1
|
|
||||||
- 新增 Gadgets
|
|
||||||
|
|
||||||
### GrasscutterTools-v1.7.0
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
## 工具截圖
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
@ -29,7 +29,7 @@
|
|||||||
<value>10001</value>
|
<value>10001</value>
|
||||||
</setting>
|
</setting>
|
||||||
<setting name="Host" serializeAs="String">
|
<setting name="Host" serializeAs="String">
|
||||||
<value>http://127.0.0.1:443</value>
|
<value>https://127.0.0.1</value>
|
||||||
</setting>
|
</setting>
|
||||||
<setting name="CheckedLastVersion" serializeAs="String">
|
<setting name="CheckedLastVersion" serializeAs="String">
|
||||||
<value />
|
<value />
|
||||||
@ -58,33 +58,6 @@
|
|||||||
<setting name="MainFormSize" serializeAs="String">
|
<setting name="MainFormSize" serializeAs="String">
|
||||||
<value>0, 0</value>
|
<value>0, 0</value>
|
||||||
</setting>
|
</setting>
|
||||||
<setting name="BannersJsonPath" serializeAs="String">
|
|
||||||
<value />
|
|
||||||
</setting>
|
|
||||||
<setting name="IsIncludeUID" serializeAs="String">
|
|
||||||
<value>False</value>
|
|
||||||
</setting>
|
|
||||||
<setting name="NavContainerSplitterDistance" serializeAs="String">
|
|
||||||
<value>0</value>
|
|
||||||
</setting>
|
|
||||||
<setting name="ActivityConfigJsonPath" serializeAs="String">
|
|
||||||
<value />
|
|
||||||
</setting>
|
|
||||||
<setting name="ProjectResourcePath" serializeAs="String">
|
|
||||||
<value />
|
|
||||||
</setting>
|
|
||||||
<setting name="IsUpgraded" serializeAs="String">
|
|
||||||
<value>False</value>
|
|
||||||
</setting>
|
|
||||||
<setting name="IsHotkeyEenabled" serializeAs="String">
|
|
||||||
<value>True</value>
|
|
||||||
</setting>
|
|
||||||
<setting name="AutoStartProxy" serializeAs="String">
|
|
||||||
<value>False</value>
|
|
||||||
</setting>
|
|
||||||
<setting name="WindowOpacity" serializeAs="String">
|
|
||||||
<value>100</value>
|
|
||||||
</setting>
|
|
||||||
</GrasscutterTools.Properties.Settings>
|
</GrasscutterTools.Properties.Settings>
|
||||||
</userSettings>
|
</userSettings>
|
||||||
</configuration>
|
</configuration>
|
186
Source/GrasscutterTools/Controls/TextBoxXP.cs
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
|
||||||
|
namespace GrasscutterTools.Controls
|
||||||
|
{
|
||||||
|
[ToolboxItem(true)]
|
||||||
|
public class TextBoxXP : TextBox
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获得当前进程,以便重绘控件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hWnd"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern IntPtr GetWindowDC(IntPtr hWnd);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
|
||||||
|
|
||||||
|
private const int EM_SETCUEBANNER = 0x1501;
|
||||||
|
|
||||||
|
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
||||||
|
private static extern Int32 SendMessage
|
||||||
|
(IntPtr hWnd, int msg, int wParam, [MarshalAs(UnmanagedType.LPWStr)] string lParam);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 水印文本
|
||||||
|
/// </summary>
|
||||||
|
private string _Watermark = "";
|
||||||
|
|
||||||
|
private float maximum;
|
||||||
|
private float minimum;
|
||||||
|
|
||||||
|
#region 属性
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否启用热点效果
|
||||||
|
/// </summary>
|
||||||
|
[Category("外观")]
|
||||||
|
[Browsable(true)]
|
||||||
|
[Localizable(true)]
|
||||||
|
[Description("获取或设置输入框水印文本")]
|
||||||
|
[DefaultValue("")]
|
||||||
|
public string Watermark
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return this._Watermark;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
this._Watermark = value;
|
||||||
|
SendMessage(Handle, EM_SETCUEBANNER, 0, _Watermark);
|
||||||
|
this.Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否只能输入数字
|
||||||
|
/// </summary>
|
||||||
|
[Category("行为")]
|
||||||
|
[Browsable(true)]
|
||||||
|
[Description("获取或设置TextBox是否只允许输入数字")]
|
||||||
|
[DefaultValue(false)]
|
||||||
|
public bool DigitOnly { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 转为数值
|
||||||
|
/// </summary>
|
||||||
|
public float Number
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (float.TryParse(Text, out float value))
|
||||||
|
return value;
|
||||||
|
else
|
||||||
|
return 0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Category("数据")]
|
||||||
|
[Browsable(true)]
|
||||||
|
[DefaultValue(0)]
|
||||||
|
[Description("指示小数点后位数")]
|
||||||
|
public int DecimalPlaces { get; set; }
|
||||||
|
|
||||||
|
[Category("数据")]
|
||||||
|
[Description("获取或设置限制的最大值")]
|
||||||
|
public float Maximum
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return maximum;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
maximum = value;
|
||||||
|
if (minimum > maximum)
|
||||||
|
{
|
||||||
|
minimum = maximum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Category("数据")]
|
||||||
|
[Browsable(true)]
|
||||||
|
[Description("获取或设置限制的最小值")]
|
||||||
|
public float Minimum
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return minimum;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
minimum = value;
|
||||||
|
if (minimum > maximum)
|
||||||
|
{
|
||||||
|
maximum = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion 属性
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public TextBoxXP()
|
||||||
|
: base()
|
||||||
|
{
|
||||||
|
//BorderStyle = BorderStyle.FixedSingle;
|
||||||
|
//Font = Styles.StaticResources.DefaultFont;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnKeyPress(KeyPressEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnKeyPress(e);
|
||||||
|
// 如果只允许输入数字,则判断输入是否为退格或者数字
|
||||||
|
if (DigitOnly)
|
||||||
|
{
|
||||||
|
//IsNumber:指定字符串中位于指定位置的字符是否属于数字类别
|
||||||
|
//IsPunctuation:指定字符串中位于指定位置的字符是否属于标点符号类别
|
||||||
|
//IsControl:指定字符串中位于指定位置的字符是否属于控制字符类别
|
||||||
|
if (!Char.IsNumber(e.KeyChar) && !Char.IsPunctuation(e.KeyChar) && !Char.IsControl(e.KeyChar))
|
||||||
|
{
|
||||||
|
e.Handled = true; //获取或设置一个值,指示是否处理过System.Windows.Forms.Control.KeyPress事件
|
||||||
|
}
|
||||||
|
else if (Char.IsPunctuation(e.KeyChar) && DecimalPlaces > 0)
|
||||||
|
{
|
||||||
|
if (e.KeyChar == '.')
|
||||||
|
{
|
||||||
|
if (Text.LastIndexOf('.') != -1)
|
||||||
|
{
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnLeave(EventArgs e)
|
||||||
|
{
|
||||||
|
base.OnLeave(e);
|
||||||
|
|
||||||
|
if (DigitOnly)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(Text))
|
||||||
|
{
|
||||||
|
if (Number > Maximum)
|
||||||
|
Text = Maximum.ToString("F" + DecimalPlaces);
|
||||||
|
if (Number < Minimum)
|
||||||
|
Text = Minimum.ToString("F" + DecimalPlaces);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -14,9 +14,8 @@
|
|||||||
*
|
*
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
**/
|
**/
|
||||||
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using GrasscutterTools.DispatchServer.Model;
|
using GrasscutterTools.DispatchServer.Model;
|
||||||
@ -24,7 +23,7 @@ using GrasscutterTools.Utils;
|
|||||||
|
|
||||||
namespace GrasscutterTools.DispatchServer
|
namespace GrasscutterTools.DispatchServer
|
||||||
{
|
{
|
||||||
internal static class DispatchServerAPI
|
public static class DispatchServerAPI
|
||||||
{
|
{
|
||||||
public static async Task<ServerStatus> QueryServerStatus(string host)
|
public static async Task<ServerStatus> QueryServerStatus(string host)
|
||||||
{
|
{
|
||||||
|
@ -14,14 +14,13 @@
|
|||||||
*
|
*
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
**/
|
**/
|
||||||
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace GrasscutterTools.DispatchServer.Model
|
namespace GrasscutterTools.DispatchServer.Model
|
||||||
{
|
{
|
||||||
internal class ServerStatus
|
public class ServerStatus
|
||||||
{
|
{
|
||||||
[JsonProperty("playerCount")]
|
[JsonProperty("playerCount")]
|
||||||
public int PlayerCount { get; set; }
|
public int PlayerCount { get; set; }
|
||||||
@ -33,7 +32,7 @@ namespace GrasscutterTools.DispatchServer.Model
|
|||||||
public string Version { get; set; }
|
public string Version { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class ServerStatusResponse
|
public class ServerStatusResponse
|
||||||
{
|
{
|
||||||
[JsonProperty("retcode")]
|
[JsonProperty("retcode")]
|
||||||
public int RetCode { get; set; }
|
public int RetCode { get; set; }
|
||||||
|
@ -1,202 +0,0 @@
|
|||||||
#nullable enable
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System;
|
|
||||||
using System.Security.Cryptography;
|
|
||||||
using System.Security.Cryptography.X509Certificates;
|
|
||||||
|
|
||||||
namespace Eavesdrop;
|
|
||||||
|
|
||||||
public sealed class Certifier : IDisposable
|
|
||||||
{
|
|
||||||
private readonly X509Store _rootStore, _myStore;
|
|
||||||
private readonly IDictionary<string, X509Certificate2> _certificateCache;
|
|
||||||
|
|
||||||
public string Issuer { get; }
|
|
||||||
public string CertificateAuthorityName { get; }
|
|
||||||
|
|
||||||
public DateTime NotAfter { get; set; }
|
|
||||||
public DateTime NotBefore { get; set; }
|
|
||||||
public int KeyLength { get; set; } = 1024;
|
|
||||||
public bool IsCachingSignedCertificates { get; set; }
|
|
||||||
|
|
||||||
public X509Certificate2? Authority { get; private set; }
|
|
||||||
|
|
||||||
public Certifier()
|
|
||||||
: this("Eavesdrop")
|
|
||||||
{ }
|
|
||||||
public Certifier(string issuer)
|
|
||||||
: this(issuer, $"{issuer} Root Certificate Authority", StoreLocation.CurrentUser)
|
|
||||||
{ }
|
|
||||||
public Certifier(string issuer, string certificateAuthorityName)
|
|
||||||
: this(issuer, certificateAuthorityName, StoreLocation.CurrentUser)
|
|
||||||
{ }
|
|
||||||
public Certifier(string issuer, string certificateAuthorityName, StoreLocation location)
|
|
||||||
{
|
|
||||||
_myStore = new X509Store(StoreName.My, location);
|
|
||||||
_rootStore = new X509Store(StoreName.Root, location);
|
|
||||||
_certificateCache = new Dictionary<string, X509Certificate2>();
|
|
||||||
|
|
||||||
NotBefore = DateTime.Now;
|
|
||||||
NotAfter = NotBefore.AddMonths(1);
|
|
||||||
|
|
||||||
Issuer = issuer;
|
|
||||||
CertificateAuthorityName = certificateAuthorityName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool CreateTrustedRootCertificate()
|
|
||||||
{
|
|
||||||
return (Authority = InstallCertificate(_rootStore, CertificateAuthorityName)) != null;
|
|
||||||
}
|
|
||||||
public bool DestroyTrustedRootCertificate()
|
|
||||||
{
|
|
||||||
return DestroyCertificates(_rootStore);
|
|
||||||
}
|
|
||||||
public bool ExportTrustedRootCertificate(string path)
|
|
||||||
{
|
|
||||||
X509Certificate2? rootCertificate = InstallCertificate(_rootStore, CertificateAuthorityName);
|
|
||||||
|
|
||||||
path = Path.GetFullPath(path);
|
|
||||||
if (rootCertificate != null)
|
|
||||||
{
|
|
||||||
byte[] data = rootCertificate.Export(X509ContentType.Cert);
|
|
||||||
File.WriteAllBytes(path, data);
|
|
||||||
}
|
|
||||||
return File.Exists(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
public X509Certificate2? GenerateCertificate(string certificateName)
|
|
||||||
{
|
|
||||||
return InstallCertificate(_myStore, certificateName);
|
|
||||||
}
|
|
||||||
public X509Certificate2 CreateCertificate(string subjectName, string alternateName)
|
|
||||||
{
|
|
||||||
using var rsa = RSA.Create(KeyLength);
|
|
||||||
var certificateRequest = new CertificateRequest(subjectName, rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
|
||||||
if (Authority == null)
|
|
||||||
{
|
|
||||||
certificateRequest.CertificateExtensions.Add(new X509BasicConstraintsExtension(true, false, 0, true));
|
|
||||||
certificateRequest.CertificateExtensions.Add(new X509SubjectKeyIdentifierExtension(certificateRequest.PublicKey, false));
|
|
||||||
|
|
||||||
using X509Certificate2 certificate = certificateRequest.CreateSelfSigned(NotBefore.ToUniversalTime(), NotAfter.ToUniversalTime());
|
|
||||||
|
|
||||||
certificate.FriendlyName = alternateName;
|
|
||||||
return new X509Certificate2(certificate.Export(X509ContentType.Pfx, string.Empty), string.Empty, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var sanBuilder = new SubjectAlternativeNameBuilder();
|
|
||||||
sanBuilder.AddDnsName(alternateName);
|
|
||||||
|
|
||||||
certificateRequest.CertificateExtensions.Add(sanBuilder.Build());
|
|
||||||
certificateRequest.CertificateExtensions.Add(new X509BasicConstraintsExtension(false, false, 0, false));
|
|
||||||
certificateRequest.CertificateExtensions.Add(new X509SubjectKeyIdentifierExtension(certificateRequest.PublicKey, false));
|
|
||||||
|
|
||||||
using X509Certificate2 certificate = certificateRequest.Create(Authority, Authority.NotBefore, Authority.NotAfter, Guid.NewGuid().ToByteArray());
|
|
||||||
using X509Certificate2 certificateWithPrivateKey = certificate.CopyWithPrivateKey(rsa);
|
|
||||||
|
|
||||||
certificateWithPrivateKey.FriendlyName = alternateName;
|
|
||||||
return new X509Certificate2(certificateWithPrivateKey.Export(X509ContentType.Pfx, string.Empty), string.Empty, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private X509Certificate2? InstallCertificate(X509Store store, string certificateName)
|
|
||||||
{
|
|
||||||
if (_certificateCache.TryGetValue(certificateName, out X509Certificate2? certificate))
|
|
||||||
{
|
|
||||||
if (DateTime.Now >= certificate.NotAfter)
|
|
||||||
{
|
|
||||||
_certificateCache.Remove(certificateName);
|
|
||||||
}
|
|
||||||
else return certificate;
|
|
||||||
}
|
|
||||||
lock (store)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
store.Open(OpenFlags.ReadWrite);
|
|
||||||
string subjectName = $"CN={certificateName}, O={Issuer}";
|
|
||||||
|
|
||||||
certificate = FindCertificates(store, subjectName)?[0];
|
|
||||||
if (certificate != null && DateTime.Now >= certificate.NotAfter)
|
|
||||||
{
|
|
||||||
if (Authority == null)
|
|
||||||
{
|
|
||||||
DestroyCertificates();
|
|
||||||
store.Open(OpenFlags.ReadWrite);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
store.Remove(certificate);
|
|
||||||
}
|
|
||||||
certificate = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (certificate == null)
|
|
||||||
{
|
|
||||||
certificate = CreateCertificate(subjectName, certificateName);
|
|
||||||
if (certificate != null)
|
|
||||||
{
|
|
||||||
if (store == _rootStore || IsCachingSignedCertificates)
|
|
||||||
{
|
|
||||||
store.Add(certificate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return certificate;
|
|
||||||
}
|
|
||||||
catch { return certificate = null; }
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
store.Close();
|
|
||||||
if (certificate != null && !_certificateCache.ContainsKey(certificateName))
|
|
||||||
{
|
|
||||||
_certificateCache.Add(certificateName, certificate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool DestroyCertificates(X509Store store)
|
|
||||||
{
|
|
||||||
lock (store)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
store.Open(OpenFlags.ReadWrite);
|
|
||||||
X509Certificate2Collection certificates = store.Certificates.Find(X509FindType.FindByIssuerName, Issuer, false);
|
|
||||||
|
|
||||||
store.RemoveRange(certificates);
|
|
||||||
IEnumerable<string> subjectNames = certificates.Cast<X509Certificate2>().Select(c => c.GetNameInfo(X509NameType.SimpleName, false));
|
|
||||||
|
|
||||||
foreach (string subjectName in subjectNames)
|
|
||||||
{
|
|
||||||
if (!_certificateCache.ContainsKey(subjectName)) continue;
|
|
||||||
_certificateCache.Remove(subjectName);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch { return false; }
|
|
||||||
finally { store.Close(); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public bool DestroyCertificates() => DestroyCertificates(_myStore) && DestroyCertificates(_rootStore);
|
|
||||||
|
|
||||||
private static X509Certificate2Collection? FindCertificates(X509Store store, string subjectName)
|
|
||||||
{
|
|
||||||
X509Certificate2Collection certificates = store.Certificates
|
|
||||||
.Find(X509FindType.FindBySubjectDistinguishedName, subjectName, false);
|
|
||||||
|
|
||||||
return certificates.Count > 0 ? certificates : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_myStore.Close();
|
|
||||||
_rootStore.Close();
|
|
||||||
|
|
||||||
_myStore.Dispose();
|
|
||||||
_rootStore.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,200 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
using Eavesdrop.Network;
|
|
||||||
|
|
||||||
namespace Eavesdrop
|
|
||||||
{
|
|
||||||
public static class Eavesdropper
|
|
||||||
{
|
|
||||||
private static TcpListener _listener;
|
|
||||||
private static readonly object _stateLock;
|
|
||||||
|
|
||||||
public delegate Task AsyncEventHandler<TEventArgs>(object sender, TEventArgs e);
|
|
||||||
|
|
||||||
public static event AsyncEventHandler<RequestInterceptedEventArgs> RequestInterceptedAsync;
|
|
||||||
private static async Task OnRequestInterceptedAsync(RequestInterceptedEventArgs e)
|
|
||||||
{
|
|
||||||
Task interceptedTask = RequestInterceptedAsync?.Invoke(null, e);
|
|
||||||
if (interceptedTask != null)
|
|
||||||
{
|
|
||||||
await interceptedTask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static event AsyncEventHandler<ResponseInterceptedEventArgs> ResponseInterceptedAsync;
|
|
||||||
private static async Task OnResponseInterceptedAsync(ResponseInterceptedEventArgs e)
|
|
||||||
{
|
|
||||||
Task interceptedTask = ResponseInterceptedAsync?.Invoke(null, e);
|
|
||||||
if (interceptedTask != null)
|
|
||||||
{
|
|
||||||
await interceptedTask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<string> Overrides { get; }
|
|
||||||
public static bool IsRunning { get; private set; }
|
|
||||||
public static Certifier Certifier { get; set; }
|
|
||||||
|
|
||||||
static Eavesdropper()
|
|
||||||
{
|
|
||||||
_stateLock = new object();
|
|
||||||
|
|
||||||
ServicePointManager.Expect100Continue = true;
|
|
||||||
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
|
|
||||||
|
|
||||||
Overrides = new List<string>();
|
|
||||||
Certifier = new Certifier("Eavesdrop", "Eavesdrop Root Certificate Authority");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Terminate()
|
|
||||||
{
|
|
||||||
lock (_stateLock)
|
|
||||||
{
|
|
||||||
ResetMachineProxy();
|
|
||||||
IsRunning = false;
|
|
||||||
|
|
||||||
if (_listener != null)
|
|
||||||
{
|
|
||||||
_listener.Stop();
|
|
||||||
_listener = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public static void Initiate(int port)
|
|
||||||
{
|
|
||||||
Initiate(port, Interceptors.Default);
|
|
||||||
}
|
|
||||||
public static void Initiate(int port, Interceptors interceptors)
|
|
||||||
{
|
|
||||||
Initiate(port, interceptors, true);
|
|
||||||
}
|
|
||||||
public static void Initiate(int port, Interceptors interceptors, bool setSystemProxy)
|
|
||||||
{
|
|
||||||
lock (_stateLock)
|
|
||||||
{
|
|
||||||
Terminate();
|
|
||||||
|
|
||||||
_listener = new TcpListener(IPAddress.Any, port);
|
|
||||||
_listener.Start();
|
|
||||||
|
|
||||||
IsRunning = true;
|
|
||||||
|
|
||||||
Task.Factory.StartNew(InterceptRequestAsync, TaskCreationOptions.LongRunning);
|
|
||||||
if (setSystemProxy)
|
|
||||||
{
|
|
||||||
SetMachineProxy(port, interceptors);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task InterceptRequestAsync()
|
|
||||||
{
|
|
||||||
while (IsRunning && _listener != null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
TcpClient client = await _listener.AcceptTcpClientAsync().ConfigureAwait(false);
|
|
||||||
Task handleClientAsync = HandleClientAsync(client);
|
|
||||||
}
|
|
||||||
catch (ObjectDisposedException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task HandleClientAsync(TcpClient client)
|
|
||||||
{
|
|
||||||
using var local = new EavesNode(Certifier, client);
|
|
||||||
WebRequest request = await local.ReadRequestAsync().ConfigureAwait(false);
|
|
||||||
if (request == null) return;
|
|
||||||
|
|
||||||
HttpContent requestContent = null;
|
|
||||||
var requestArgs = new RequestInterceptedEventArgs(request);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
requestArgs.Content = requestContent = await local.ReadRequestContentAsync(request).ConfigureAwait(false);
|
|
||||||
await OnRequestInterceptedAsync(requestArgs).ConfigureAwait(false);
|
|
||||||
if (requestArgs.Cancel) return;
|
|
||||||
|
|
||||||
request = requestArgs.Request;
|
|
||||||
if (requestArgs.Content != null)
|
|
||||||
{
|
|
||||||
await local.WriteRequestContentAsync(request, requestArgs.Content).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
requestContent?.Dispose();
|
|
||||||
requestArgs.Content?.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
WebResponse response = null;
|
|
||||||
try { response = await request.GetResponseAsync().ConfigureAwait(false); }
|
|
||||||
catch (WebException ex) { response = ex.Response; }
|
|
||||||
catch (ProtocolViolationException)
|
|
||||||
{
|
|
||||||
response?.Dispose();
|
|
||||||
response = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response == null) return;
|
|
||||||
HttpContent responseContent = null;
|
|
||||||
var responseArgs = new ResponseInterceptedEventArgs(request, response);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
responseArgs.Content = responseContent = EavesNode.ReadResponseContent(response);
|
|
||||||
await OnResponseInterceptedAsync(responseArgs).ConfigureAwait(false);
|
|
||||||
if (responseArgs.Cancel) return;
|
|
||||||
|
|
||||||
await local.SendResponseAsync(responseArgs.Response, responseArgs.Content).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
response.Dispose();
|
|
||||||
responseArgs.Response.Dispose();
|
|
||||||
|
|
||||||
responseContent?.Dispose();
|
|
||||||
responseArgs.Content?.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ResetMachineProxy()
|
|
||||||
{
|
|
||||||
INETOptions.Overrides.Clear();
|
|
||||||
INETOptions.IsIgnoringLocalTraffic = false;
|
|
||||||
|
|
||||||
INETOptions.HTTPAddress = null;
|
|
||||||
INETOptions.HTTPSAddress = null;
|
|
||||||
INETOptions.IsProxyEnabled = false;
|
|
||||||
|
|
||||||
INETOptions.Save();
|
|
||||||
}
|
|
||||||
private static void SetMachineProxy(int port, Interceptors interceptors)
|
|
||||||
{
|
|
||||||
foreach (string @override in Overrides)
|
|
||||||
{
|
|
||||||
if (INETOptions.Overrides.Contains(@override)) continue;
|
|
||||||
INETOptions.Overrides.Add(@override);
|
|
||||||
}
|
|
||||||
|
|
||||||
string address = ("127.0.0.1:" + port);
|
|
||||||
if (interceptors.HasFlag(Interceptors.HTTP))
|
|
||||||
{
|
|
||||||
INETOptions.HTTPAddress = address;
|
|
||||||
}
|
|
||||||
if (interceptors.HasFlag(Interceptors.HTTPS))
|
|
||||||
{
|
|
||||||
INETOptions.HTTPSAddress = address;
|
|
||||||
}
|
|
||||||
INETOptions.IsProxyEnabled = true;
|
|
||||||
INETOptions.IsIgnoringLocalTraffic = true;
|
|
||||||
|
|
||||||
INETOptions.Save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,78 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.ComponentModel;
|
|
||||||
|
|
||||||
namespace Eavesdrop
|
|
||||||
{
|
|
||||||
public class RequestInterceptedEventArgs : CancelEventArgs
|
|
||||||
{
|
|
||||||
private HttpWebRequest _httpRequest;
|
|
||||||
|
|
||||||
public HttpContent Content { get; set; }
|
|
||||||
|
|
||||||
private WebRequest _request;
|
|
||||||
public WebRequest Request
|
|
||||||
{
|
|
||||||
get => _request;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_request = value;
|
|
||||||
_httpRequest = (value as HttpWebRequest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Uri Uri => Request?.RequestUri;
|
|
||||||
public CookieContainer CookieContainer => _httpRequest?.CookieContainer;
|
|
||||||
|
|
||||||
public string Method
|
|
||||||
{
|
|
||||||
get => Request?.Method;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (Request != null)
|
|
||||||
{
|
|
||||||
Request.Method = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public IWebProxy Proxy
|
|
||||||
{
|
|
||||||
get => Request?.Proxy;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (Request != null)
|
|
||||||
{
|
|
||||||
Request.Proxy = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public string ContentType
|
|
||||||
{
|
|
||||||
get => Request?.ContentType;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (Request != null)
|
|
||||||
{
|
|
||||||
Request.ContentType = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public WebHeaderCollection Headers
|
|
||||||
{
|
|
||||||
get => Request?.Headers;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (Request != null)
|
|
||||||
{
|
|
||||||
Request.Headers = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public RequestInterceptedEventArgs(WebRequest request)
|
|
||||||
{
|
|
||||||
Request = request;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
using Eavesdrop.Network;
|
|
||||||
|
|
||||||
namespace Eavesdrop
|
|
||||||
{
|
|
||||||
public class ResponseInterceptedEventArgs : CancelEventArgs
|
|
||||||
{
|
|
||||||
private WebResponse _response;
|
|
||||||
public WebResponse Response
|
|
||||||
{
|
|
||||||
get => _response;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_response = value;
|
|
||||||
if (value is HttpWebResponse httpResponse)
|
|
||||||
{
|
|
||||||
CookieContainer = new CookieContainer();
|
|
||||||
CookieContainer.Add(httpResponse.Cookies);
|
|
||||||
}
|
|
||||||
else CookieContainer = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public WebRequest Request { get; }
|
|
||||||
public Uri Uri => Response?.ResponseUri;
|
|
||||||
|
|
||||||
public HttpContent Content { get; set; }
|
|
||||||
public CookieContainer CookieContainer { get; private set; }
|
|
||||||
|
|
||||||
public string ContentType
|
|
||||||
{
|
|
||||||
get => Response?.ContentType;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (Response != null)
|
|
||||||
{
|
|
||||||
Response.ContentType = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public WebHeaderCollection Headers
|
|
||||||
{
|
|
||||||
get => Response?.Headers;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (Response == null) return;
|
|
||||||
foreach (string header in value.AllKeys)
|
|
||||||
{
|
|
||||||
Response.Headers[header] = value[header];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ResponseInterceptedEventArgs(WebRequest request, WebResponse response)
|
|
||||||
{
|
|
||||||
Request = request;
|
|
||||||
Response = response;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Eavesdrop
|
|
||||||
{
|
|
||||||
[Flags]
|
|
||||||
public enum Interceptors
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
HTTP = 1,
|
|
||||||
HTTPS = 2,
|
|
||||||
|
|
||||||
Default = (HTTP | HTTPS)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,241 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
using Microsoft.Win32;
|
|
||||||
|
|
||||||
namespace Eavesdrop
|
|
||||||
{
|
|
||||||
public static class INETOptions
|
|
||||||
{
|
|
||||||
private static readonly object _stateLock;
|
|
||||||
private static readonly int _iNetOptionSize;
|
|
||||||
private static readonly int _iNetPackageSize;
|
|
||||||
private static readonly RegistryKey _proxyKey;
|
|
||||||
|
|
||||||
public static List<string> Overrides { get; }
|
|
||||||
|
|
||||||
public static string HTTPAddress { get; set; }
|
|
||||||
public static string HTTPSAddress { get; set; }
|
|
||||||
|
|
||||||
public static bool IsProxyEnabled { get; set; }
|
|
||||||
public static bool IsIgnoringLocalTraffic { get; set; }
|
|
||||||
|
|
||||||
static INETOptions()
|
|
||||||
{
|
|
||||||
_stateLock = new object();
|
|
||||||
_iNetOptionSize = Marshal.SizeOf(typeof(INETOption));
|
|
||||||
_iNetPackageSize = Marshal.SizeOf(typeof(INETPackage));
|
|
||||||
_proxyKey = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Internet Settings", true);
|
|
||||||
|
|
||||||
Overrides = new List<string>();
|
|
||||||
Load();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Save()
|
|
||||||
{
|
|
||||||
lock (_stateLock)
|
|
||||||
{
|
|
||||||
var options = new List<INETOption>(3);
|
|
||||||
string joinedAddresses = (IsProxyEnabled ? GetJoinedAddresses() : null);
|
|
||||||
string joinedOverrides = (IsProxyEnabled ? GetJoinedOverrides() : null);
|
|
||||||
|
|
||||||
var kind = ProxyKind.PROXY_TYPE_DIRECT;
|
|
||||||
if (!string.IsNullOrWhiteSpace(joinedAddresses))
|
|
||||||
{
|
|
||||||
options.Add(new INETOption(OptionKind.INTERNET_PER_CONN_PROXY_SERVER, joinedAddresses));
|
|
||||||
if (!string.IsNullOrWhiteSpace(joinedOverrides))
|
|
||||||
{
|
|
||||||
options.Add(new INETOption(OptionKind.INTERNET_PER_CONN_PROXY_BYPASS, joinedOverrides));
|
|
||||||
}
|
|
||||||
kind |= ProxyKind.PROXY_TYPE_PROXY;
|
|
||||||
}
|
|
||||||
options.Insert(0, new INETOption(OptionKind.INTERNET_PER_CONN_FLAGS, (int)kind));
|
|
||||||
|
|
||||||
var inetPackage = new INETPackage
|
|
||||||
{
|
|
||||||
_optionError = 0,
|
|
||||||
_size = _iNetPackageSize,
|
|
||||||
_connection = IntPtr.Zero,
|
|
||||||
_optionCount = options.Count
|
|
||||||
};
|
|
||||||
|
|
||||||
IntPtr optionsPtr = Marshal.AllocCoTaskMem(_iNetOptionSize * options.Count);
|
|
||||||
for (int i = 0; i < options.Count; ++i)
|
|
||||||
{
|
|
||||||
var optionPtr = new IntPtr((IntPtr.Size == 4 ? optionsPtr.ToInt32() : optionsPtr.ToInt64()) + (i * _iNetOptionSize));
|
|
||||||
Marshal.StructureToPtr(options[i], optionPtr, false);
|
|
||||||
}
|
|
||||||
inetPackage._optionsPtr = optionsPtr;
|
|
||||||
|
|
||||||
IntPtr iNetPackagePtr = Marshal.AllocCoTaskMem(_iNetPackageSize);
|
|
||||||
Marshal.StructureToPtr(inetPackage, iNetPackagePtr, false);
|
|
||||||
|
|
||||||
int returnvalue = (NativeMethods.InternetSetOption(IntPtr.Zero, 75, iNetPackagePtr, _iNetPackageSize) ? -1 : 0);
|
|
||||||
if (returnvalue == 0)
|
|
||||||
{
|
|
||||||
returnvalue = Marshal.GetLastWin32Error();
|
|
||||||
}
|
|
||||||
|
|
||||||
Marshal.FreeCoTaskMem(optionsPtr);
|
|
||||||
Marshal.FreeCoTaskMem(iNetPackagePtr);
|
|
||||||
if (returnvalue > 0)
|
|
||||||
{
|
|
||||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
|
||||||
}
|
|
||||||
|
|
||||||
NativeMethods.InternetSetOption(IntPtr.Zero, 39, iNetPackagePtr, _iNetPackageSize);
|
|
||||||
NativeMethods.InternetSetOption(IntPtr.Zero, 37, iNetPackagePtr, _iNetPackageSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public static void Load()
|
|
||||||
{
|
|
||||||
lock (_stateLock)
|
|
||||||
{
|
|
||||||
LoadAddresses();
|
|
||||||
LoadOverrides();
|
|
||||||
IsProxyEnabled = (_proxyKey.GetValue("ProxyEnable")?.ToString() == "1");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void LoadOverrides()
|
|
||||||
{
|
|
||||||
string proxyOverride = _proxyKey.GetValue("ProxyOverride")?.ToString();
|
|
||||||
if (string.IsNullOrWhiteSpace(proxyOverride)) return;
|
|
||||||
|
|
||||||
string[] overrides = proxyOverride.Split(';');
|
|
||||||
foreach (string @override in overrides)
|
|
||||||
{
|
|
||||||
if (@override == "<local>")
|
|
||||||
{
|
|
||||||
IsIgnoringLocalTraffic = true;
|
|
||||||
}
|
|
||||||
else if (!Overrides.Contains(@override))
|
|
||||||
{
|
|
||||||
Overrides.Add(@override);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private static void LoadAddresses()
|
|
||||||
{
|
|
||||||
string proxyServer = _proxyKey.GetValue("ProxyServer")?.ToString();
|
|
||||||
if (string.IsNullOrWhiteSpace(proxyServer)) return;
|
|
||||||
|
|
||||||
string[] values = proxyServer.Split(';');
|
|
||||||
foreach (string value in values)
|
|
||||||
{
|
|
||||||
string[] pair = value.Split('=');
|
|
||||||
if (pair.Length != 2)
|
|
||||||
{
|
|
||||||
HTTPAddress = value;
|
|
||||||
HTTPSAddress = value;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
string address = pair[1];
|
|
||||||
string protocol = pair[0];
|
|
||||||
switch (protocol)
|
|
||||||
{
|
|
||||||
case "http": HTTPAddress = address; break;
|
|
||||||
case "https": HTTPSAddress = address; break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetJoinedAddresses()
|
|
||||||
{
|
|
||||||
var addresses = new List<string>(2);
|
|
||||||
if (!string.IsNullOrWhiteSpace(HTTPAddress))
|
|
||||||
{
|
|
||||||
addresses.Add("http=" + HTTPAddress);
|
|
||||||
}
|
|
||||||
if (!string.IsNullOrWhiteSpace(HTTPSAddress))
|
|
||||||
{
|
|
||||||
addresses.Add("https=" + HTTPSAddress);
|
|
||||||
}
|
|
||||||
return string.Join(";", addresses);
|
|
||||||
}
|
|
||||||
private static string GetJoinedOverrides()
|
|
||||||
{
|
|
||||||
var overrides = new List<string>(Overrides);
|
|
||||||
if (IsIgnoringLocalTraffic)
|
|
||||||
{
|
|
||||||
overrides.Add("<local>");
|
|
||||||
}
|
|
||||||
return string.Join(";", overrides);
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
|
|
||||||
private struct INETOption
|
|
||||||
{
|
|
||||||
private readonly OptionKind _kind;
|
|
||||||
private readonly INETOptionValue _value;
|
|
||||||
|
|
||||||
public INETOption(OptionKind kind, int value)
|
|
||||||
{
|
|
||||||
_kind = kind;
|
|
||||||
_value = CreateValue(value);
|
|
||||||
}
|
|
||||||
public INETOption(OptionKind kind, string value)
|
|
||||||
{
|
|
||||||
_kind = kind;
|
|
||||||
_value = CreateValue(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static INETOptionValue CreateValue(int value)
|
|
||||||
{
|
|
||||||
return new INETOptionValue
|
|
||||||
{
|
|
||||||
_intValue = value
|
|
||||||
};
|
|
||||||
}
|
|
||||||
private static INETOptionValue CreateValue(string value)
|
|
||||||
{
|
|
||||||
return new INETOptionValue
|
|
||||||
{
|
|
||||||
_stringPointer = Marshal.StringToHGlobalAuto(value)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Explicit)]
|
|
||||||
private struct INETOptionValue
|
|
||||||
{
|
|
||||||
[FieldOffset(0)]
|
|
||||||
public int _intValue;
|
|
||||||
|
|
||||||
[FieldOffset(0)]
|
|
||||||
public IntPtr _stringPointer;
|
|
||||||
|
|
||||||
[FieldOffset(0)]
|
|
||||||
public System.Runtime.InteropServices.ComTypes.FILETIME _fileTime;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
|
|
||||||
private struct INETPackage
|
|
||||||
{
|
|
||||||
public int _size;
|
|
||||||
public IntPtr _connection;
|
|
||||||
public int _optionCount;
|
|
||||||
public int _optionError;
|
|
||||||
public IntPtr _optionsPtr;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Flags]
|
|
||||||
private enum ProxyKind
|
|
||||||
{
|
|
||||||
PROXY_TYPE_DIRECT = 1,
|
|
||||||
PROXY_TYPE_PROXY = 2,
|
|
||||||
PROXY_TYPE_AUTO_PROXY_URL = 4,
|
|
||||||
PROXY_TYPE_AUTO_DETECT = 8
|
|
||||||
}
|
|
||||||
private enum OptionKind
|
|
||||||
{
|
|
||||||
INTERNET_PER_CONN_FLAGS = 1,
|
|
||||||
INTERNET_PER_CONN_PROXY_SERVER = 2,
|
|
||||||
INTERNET_PER_CONN_PROXY_BYPASS = 3,
|
|
||||||
INTERNET_PER_CONN_AUTOCONFIG_URL = 4
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Eavesdrop
|
|
||||||
{
|
|
||||||
internal static class NativeMethods
|
|
||||||
{
|
|
||||||
[DllImport("wininet.dll", SetLastError = true, CharSet = CharSet.Auto)]
|
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
public static extern bool InternetSetOption(IntPtr hInternet, int dwOption, IntPtr lpBuffer, int dwBufferLength);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2022 ArachisH
|
|
||||||
|
|
||||||
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.
|
|