mirror of
https://github.com/BeyondDimension/SteamTools.git
synced 2026-10-28 14:08:16 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c86b53000 | ||
|
|
efb6adbd9b | ||
|
|
209202b5cf | ||
|
|
c0a081e226 |
@@ -0,0 +1,13 @@
|
||||
### 该问题是怎么引起的?
|
||||
|
||||
|
||||
|
||||
### 重现步骤
|
||||
|
||||
|
||||
|
||||
### 报错信息
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
### 相关的Issue
|
||||
|
||||
|
||||
### 原因(目的、解决的问题等)
|
||||
|
||||
|
||||
### 描述(做了什么,变更了什么)
|
||||
|
||||
|
||||
### 测试用例(新增、改动、可能影响的功能)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
[submodule "references/Titanium-Web-Proxy"]
|
||||
path = references/Titanium-Web-Proxy
|
||||
url = https://github.com/rmbadmin/Titanium-Web-Proxy.git
|
||||
[submodule "references/MetroRadiance"]
|
||||
path = references/MetroRadiance
|
||||
url = https://github.com/rmbadmin/MetroRadiance.git
|
||||
+52
-67
@@ -1,92 +1,79 @@
|
||||
<img src="https://raw.githubusercontent.com/rmbadmin/SteamTools/develop/resources/icon/LOGO1.png" alt="logo" width="130" height="130" align="left" />
|
||||
# Steam++工具箱 v1.0
|
||||
|
||||
<h1>Steam++ Tools</h1>
|
||||
|
||||
> 「Steam++」is a toolkit that contains a variety of Steam tools, most of which require you to download and install Steam in order to use them.
|
||||
<br/>
|
||||
|
||||

|
||||
[](https://github.com/rmbadmin/SteamTools/releases/latest)
|
||||
[](LICENSE)
|
||||
[](https://github.com/rmbadmin/SteamTools/stargazers)
|
||||
[](https://github.com/rmbadmin/SteamTools/network/members)
|
||||

|
||||
|
||||
## Samples
|
||||
-------
|
||||

|
||||

|
||||

|
||||

|
||||
------
|
||||
|
||||
## Core Functions
|
||||
「Steam++」是一个包含多种Steam工具功能的工具箱。
|
||||
|
||||
此工具的大部分功能都是需要您下载安装Steam才能使用。
|
||||
|
||||
------
|
||||
|
||||
## 核心功能
|
||||
|
||||
### 1. 反代Steam的社区网页使其能正常访问
|
||||
|
||||
功能类似羽翼城大佬的Steam302,使用[Titanium-Web-Proxy](https://github.com/justcoding121/Titanium-Web-Proxy)开源项目进行本地反代,相比302工具具有更快的启动速度,以及支持简单的脚本注入。
|
||||
|
||||
### 1. Local reverse proxy for Steam's community webpage to enable normal access in mainland China
|
||||

|
||||
---
|
||||
### 2. Quickly switch the Steam account that the current PC has remembered to log into
|
||||
### 2. 快速切换当前PC已经记住登陆的Steam账号
|
||||
|
||||
This feature is to read the local user login records stored under the Steam path to directly display the operation, you can switch between multiple accounts without re-entering passwords and tokens.
|
||||
该功能是读取Steam路径下存储的本地用户登录记录直接展示操作,可以多账号切换无需重新输入密码和令牌。
|
||||
|
||||

|
||||
---
|
||||
### 3. Achievement stat modifications for Steam games
|
||||
Function reference [SteamAchievementManager](https://github.com/gibbed/SteamAchievementManager) for secondary development, modified the game list loading and operation ease of use.
|
||||

|
||||
---
|
||||
|
||||
### 4. Steam Local Two-Step Authenticator
|
||||
The function reference [WinAuth](https://github.com/winauth/winauth) is developed to enable you to view your token without launching the mobile version of Steam App, similar function software are [WinAuth](https://github.com/winauth/winauth), [SteamDesktopAuthenticator](https://github.com/Jessecar96/SteamDesktopAuthenticator).
|
||||
! [](https://raw.githubusercontent.com/rmbadmin/SteamTools/develop/resources/4.png)
|
||||
### 3. Steam游戏的成就统计管理功能
|
||||
功能参考[SteamAchievementManager](https://github.com/gibbed/SteamAchievementManager)进行二次开发,修改了游戏列表的加载和操作易用性。
|
||||
|
||||
---
|
||||
|
||||
### 5. Some game tools
|
||||
Currently there is a forced game borderless windowing, CSGO fix VAC shield.
|
||||
This piece is randomly do some of my often used or idle tinkering with the function.
|
||||

|
||||

|
||||
---
|
||||
|
||||
## Features expected to be added subsequently
|
||||
|
||||
### Steam Idle Card
|
||||
The goal is to enable direct access to badge card information and start hanging cards without logging into your Steam account within the software.
|
||||
|
||||
### Plug-in form to load and run ASF
|
||||
Support ASF as a plug-in to run within the tool and enhance the use of ASF in Windows Desktop environment.
|
||||
|
||||
### Steam Custom Cover Management
|
||||
Enhanced management of Steam custom covers and quick matching of downloaded app covers from [SteamGridDB](https://www.steamgriddb.com/).
|
||||
|
||||
### More other game platform features
|
||||
|
||||
|
||||
### More non-cheat game features
|
||||
### 4. Steam本地两步身份验证器
|
||||
功能参考[WinAuth](https://github.com/winauth/winauth)开发,可以使您不用启动移动版Steam App也能查看您的令牌,功能类似的软件有[WinAuth](https://github.com/winauth/winauth)、[SteamDesktopAuthenticator](https://github.com/Jessecar96/SteamDesktopAuthenticator)。
|
||||
|
||||
---
|
||||
|
||||
## Operating Environment
|
||||
> The program was developed using C# WPF in .,If it does not work please download and install[.NET Framework 4.7.2](https://dotnet.microsoft.com/download/dotnet-framework/net472)。
|
||||
> The missing assembly is my own modified project
|
||||
* [Titanium-Web-Proxy](https://github.com/rmbadmin/Titanium-Web-Proxy)
|
||||
* [MetroRadiance](https://github.com/rmbadmin/MetroRadiance)
|
||||
### 5. 一些游戏工具
|
||||
目前已有强制游戏无边框窗口化,CSGO修复VAC屏蔽。
|
||||
这一块是随缘做一些我经常用或者闲着没事捣鼓的功能。
|
||||
|
||||
---
|
||||
|
||||
## 预计后续添加的功能
|
||||
|
||||
### Steam自动挂卡
|
||||
尝试用社区反代功能结合成就解锁功能来重新实现,目的是实现在软件内登录Steam即可直接获取徽章卡片信息并开始挂卡。
|
||||
|
||||
### Steam皮肤设计器
|
||||
挖坑画大饼,可视化编辑Steam皮肤,而且如果软件能上架Steam的话打算支持创意工坊分享设计的Steam皮肤,短期内肯定做不完。
|
||||
|
||||
### 插件形式加载运行ASF
|
||||
以插件形式支持ASF在工具内运行并增强ASF在Windows Desktop环境下的使用。
|
||||
|
||||
### Steam自定义封面管理
|
||||
增强Steam自定义封面的管理以及从[SteamGridDB](https://www.steamgriddb.com/)快速匹配下载应用封面。
|
||||
|
||||
### 更多其它游戏平台功能
|
||||
|
||||
|
||||
### 更多非作弊游戏功能
|
||||
|
||||
---
|
||||
|
||||
## 运行环境
|
||||
> 程序使用C# WPF在 .NET Framework4.7.2环境下开发,如果无法运行请下载安装[.NET Framework 4.7.2](https://dotnet.microsoft.com/download/dotnet-framework/net472)。
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
> [GPL-3.0 License](https://github.com/rmbadmin/SteamTools/blob/develop/LICENSE)
|
||||
> Open source/free software released under the GPLv3 license.
|
||||
> [GPL-3.0 License](LICENSE.txt)
|
||||
> 根据GPLv3许可发布的开源/免费软件。
|
||||
|
||||
---
|
||||
|
||||
## Thanks to the following open source projects
|
||||
## 感谢以下开源项目
|
||||
* [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json)
|
||||
* [MetroRadiance](https://github.com/Grabacr07/MetroRadiance)
|
||||
* [MetroTrilithon](https://github.com/Grabacr07/MetroTrilithon)
|
||||
* [Livet](https://github.com/runceel/Livet)
|
||||
* [StatefulModel](https://github.com/ugaya40/StatefulModel)
|
||||
* [StatefulModel](https://github.com/Grabacr07/MetroTrilithonhttps://github.com/ugaya40/StatefulModel)
|
||||
* [Hardcodet.NotifyIcon](https://github.com/HavenDV/Hardcodet.NotifyIcon.Wpf.NetCore)
|
||||
* [System.Reactive](https://github.com/dotnet/reactive)
|
||||
* [Titanium-Web-Proxy](https://github.com/justcoding121/Titanium-Web-Proxy)
|
||||
@@ -96,6 +83,4 @@ Enhanced management of Steam custom covers and quick matching of downloaded app
|
||||
* [SteamAchievementManager](https://github.com/gibbed/SteamAchievementManager)
|
||||
* [ArchiSteamFarm](https://github.com/JustArchiNET/ArchiSteamFarm)
|
||||
* [WinAuth](https://github.com/winauth/winauth)
|
||||
* [SteamDesktopAuthenticator](https://github.com/Jessecar96/SteamDesktopAuthenticator)
|
||||
* [Gameloop.Vdf](https://github.com/shravan2x/Gameloop.Vdf)
|
||||
* [Costura.Fody](https://github.com/Fody/Costura)
|
||||
* [SteamDesktopAuthenticator](https://github.com/Jessecar96/SteamDesktopAuthenticator)
|
||||
@@ -1,60 +1,45 @@
|
||||
<img src="https://raw.githubusercontent.com/rmbadmin/SteamTools/develop/resources/icon/LOGO1.png" alt="logo" width="130" height="130" align="left" />
|
||||
# Steam++工具箱 v1.0
|
||||
|
||||
<h1>Steam++ 工具箱</h1>
|
||||
------
|
||||
|
||||
> 「Steam++」是一个包含多种Steam工具功能的工具箱,
|
||||
> 此工具的大部分功能都是需要您下载安装Steam才能使用。
|
||||
<br/>
|
||||
「Steam++」是一个包含多种Steam工具功能的工具箱。
|
||||
|
||||

|
||||
[](https://github.com/rmbadmin/SteamTools/releases/latest)
|
||||
[](LICENSE)
|
||||
[](https://github.com/rmbadmin/SteamTools/stargazers)
|
||||
[](https://github.com/rmbadmin/SteamTools/network/members)
|
||||

|
||||
此工具的大部分功能都是需要您下载安装Steam才能使用。
|
||||
|
||||
# Steam++
|
||||
|
||||
## 效果展示
|
||||
-------
|
||||

|
||||

|
||||

|
||||

|
||||
------
|
||||
|
||||
## 核心功能
|
||||
|
||||
### 1. 反代Steam的社区网页使其能正常访问
|
||||
功能类似羽翼城大佬的[steamcommunity_302](https://www.dogfight360.com/blog/686/),使用[Titanium-Web-Proxy](https://github.com/justcoding121/Titanium-Web-Proxy)开源项目进行本地反代,使国内用户可以正常访问steam社区页,相比302工具具有更快的启动速度,以及支持简单的脚本注入。(还顺便支持了Pixiv、Discord、Twitch等网站的反代支持)
|
||||

|
||||
|
||||
功能类似羽翼城大佬的Steam302,使用[Titanium-Web-Proxy](https://github.com/justcoding121/Titanium-Web-Proxy)开源项目进行本地反代,相比302工具具有更快的启动速度,以及支持简单的脚本注入。
|
||||
|
||||
---
|
||||
### 2. 快速切换当前PC已经记住登陆的Steam账号
|
||||
|
||||
该功能是读取Steam路径下存储的本地用户登录记录直接展示操作,可以多账号切换无需重新输入密码和令牌。
|
||||

|
||||
|
||||
---
|
||||
### 3. Steam游戏的成就统计管理功能
|
||||
功能参考[SteamAchievementManager](https://github.com/gibbed/SteamAchievementManager)进行二次开发,修改了游戏列表的加载和操作易用性。
|
||||

|
||||
|
||||
---
|
||||
|
||||
### 4. Steam本地两步身份验证器
|
||||
功能参考[WinAuth](https://github.com/winauth/winauth)开发,可以使您不用启动移动版Steam App也能查看您的令牌,功能类似的软件有[WinAuth](https://github.com/winauth/winauth)、[SteamDesktopAuthenticator](https://github.com/Jessecar96/SteamDesktopAuthenticator)。
|
||||

|
||||
|
||||
---
|
||||
|
||||
### 5. 一些游戏工具
|
||||
目前已有强制游戏无边框窗口化,CSGO修复VAC屏蔽。
|
||||
这一块是随缘做一些我经常用或者闲着没事捣鼓的功能。
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
## 预计后续添加的功能
|
||||
|
||||
### Steam自动挂卡
|
||||
尝试用社区反代功能结合成就解锁功能来重新实现,目的是实现在软件内无需登录Steam帐户即可直接获取徽章卡片信息并开始挂卡。
|
||||
尝试用社区反代功能结合成就解锁功能来重新实现,目的是实现在软件内登录Steam即可直接获取徽章卡片信息并开始挂卡。
|
||||
|
||||
### Steam皮肤设计器
|
||||
挖坑画大饼,可视化编辑Steam皮肤,而且如果软件能上架Steam的话打算支持创意工坊分享设计的Steam皮肤,短期内肯定做不完。
|
||||
@@ -72,15 +57,13 @@
|
||||
|
||||
---
|
||||
|
||||
## 开发环境
|
||||
> 开发工具 [Visual Studio 2019](https://visualstudio.microsoft.com/)
|
||||
> 开发语言 C# WPF
|
||||
> .NET版本 .NET Framework4.7.2 和 .NET5
|
||||
> 如果无法运行请下载安装[.NET Framework 4.7.2](https://dotnet.microsoft.com/download/dotnet-framework/net472)或[.NET 5](https://dotnet.microsoft.com/download/dotnet/5.0)。
|
||||
## 运行环境
|
||||
> 程序使用C# WPF在 .NET Framework4.7.2环境下开发,如果无法运行请下载安装[.NET Framework 4.7.2](https://dotnet.microsoft.com/download/dotnet-framework/net472)。
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
> [GPL-3.0 License](https://github.com/rmbadmin/SteamTools/blob/develop/LICENSE)
|
||||
> [GPL-3.0 License](LICENSE.txt)
|
||||
> 根据GPLv3许可发布的开源/免费软件。
|
||||
|
||||
---
|
||||
@@ -90,7 +73,7 @@
|
||||
* [MetroRadiance](https://github.com/Grabacr07/MetroRadiance)
|
||||
* [MetroTrilithon](https://github.com/Grabacr07/MetroTrilithon)
|
||||
* [Livet](https://github.com/runceel/Livet)
|
||||
* [StatefulModel](https://github.com/ugaya40/StatefulModel)
|
||||
* [StatefulModel](https://github.com/Grabacr07/MetroTrilithonhttps://github.com/ugaya40/StatefulModel)
|
||||
* [Hardcodet.NotifyIcon](https://github.com/HavenDV/Hardcodet.NotifyIcon.Wpf.NetCore)
|
||||
* [System.Reactive](https://github.com/dotnet/reactive)
|
||||
* [Titanium-Web-Proxy](https://github.com/justcoding121/Titanium-Web-Proxy)
|
||||
@@ -100,6 +83,4 @@
|
||||
* [SteamAchievementManager](https://github.com/gibbed/SteamAchievementManager)
|
||||
* [ArchiSteamFarm](https://github.com/JustArchiNET/ArchiSteamFarm)
|
||||
* [WinAuth](https://github.com/winauth/winauth)
|
||||
* [SteamDesktopAuthenticator](https://github.com/Jessecar96/SteamDesktopAuthenticator)
|
||||
* [Gameloop.Vdf](https://github.com/shravan2x/Gameloop.Vdf)
|
||||
* [Costura.Fody](https://github.com/Fody/Costura)
|
||||
* [SteamDesktopAuthenticator](https://github.com/Jessecar96/SteamDesktopAuthenticator)
|
||||
@@ -0,0 +1,249 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web;
|
||||
using WinAuth;
|
||||
|
||||
namespace SteamTool.Auth
|
||||
{
|
||||
public static class AuthService
|
||||
{
|
||||
/// <summary>
|
||||
/// Import a file containing authenticators in the KeyUriFormat. The file might be plain text, encrypted zip or encrypted pgp.
|
||||
/// </summary>
|
||||
/// <param name="parent">parent Form</param>
|
||||
/// <param name="file">file name to import</param>
|
||||
/// <returns>list of imported authenticators</returns>
|
||||
public static List<WinAuthAuthenticator> ImportAuthenticators(string file)
|
||||
{
|
||||
List<WinAuthAuthenticator> authenticators = new List<WinAuthAuthenticator>();
|
||||
|
||||
StringBuilder lines = new StringBuilder();
|
||||
bool retry;
|
||||
do
|
||||
{
|
||||
retry = false;
|
||||
lines.Length = 0;
|
||||
// read a plain text file
|
||||
lines.Append(File.ReadAllText(file));
|
||||
} while (retry);
|
||||
|
||||
int linenumber = 0;
|
||||
try
|
||||
{
|
||||
using (var sr = new StringReader(lines.ToString()))
|
||||
{
|
||||
string line;
|
||||
while ((line = sr.ReadLine()) != null)
|
||||
{
|
||||
linenumber++;
|
||||
|
||||
// ignore blank lines or comments
|
||||
line = line.Trim();
|
||||
if (line.Length == 0 || line.IndexOf("#") == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// bug if there is a hash before ?
|
||||
var hash = line.IndexOf("#");
|
||||
var qm = line.IndexOf("?");
|
||||
if (hash != -1 && hash < qm)
|
||||
{
|
||||
line = line.Substring(0, hash) + "%23" + line.Substring(hash + 1);
|
||||
}
|
||||
|
||||
// parse and validate URI
|
||||
var uri = new Uri(line);
|
||||
|
||||
// we only support "otpauth"
|
||||
if (uri.Scheme != "otpauth")
|
||||
{
|
||||
throw new ApplicationException("Import only supports otpauth://");
|
||||
}
|
||||
// we only support totp (not hotp)
|
||||
if (uri.Host != "totp" && uri.Host != "hotp")
|
||||
{
|
||||
throw new ApplicationException("Import only supports otpauth://totp/ or otpauth://hotp/");
|
||||
}
|
||||
|
||||
// get the label and optional issuer
|
||||
string issuer = string.Empty;
|
||||
string label = (string.IsNullOrEmpty(uri.LocalPath) == false ? uri.LocalPath.Substring(1) : string.Empty); // skip past initial /
|
||||
int p = label.IndexOf(":");
|
||||
if (p != -1)
|
||||
{
|
||||
issuer = label.Substring(0, p);
|
||||
label = label.Substring(p + 1);
|
||||
}
|
||||
// + aren't decoded
|
||||
label = label.Replace("+", " ");
|
||||
|
||||
var query = HttpUtility.ParseQueryString(uri.Query);
|
||||
string secret = query["secret"];
|
||||
if (string.IsNullOrEmpty(secret) == true)
|
||||
{
|
||||
throw new ApplicationException("Authenticator does not contain secret");
|
||||
}
|
||||
|
||||
string counter = query["counter"];
|
||||
if (uri.Host == "hotp" && string.IsNullOrEmpty(counter) == true)
|
||||
{
|
||||
throw new ApplicationException("HOTP authenticator should have a counter");
|
||||
}
|
||||
|
||||
WinAuthAuthenticator importedAuthenticator = new WinAuthAuthenticator
|
||||
{
|
||||
AutoRefresh = false
|
||||
};
|
||||
//
|
||||
Authenticator auth;
|
||||
if (string.Compare(issuer, "BattleNet", true) == 0)
|
||||
{
|
||||
string serial = query["serial"];
|
||||
if (string.IsNullOrEmpty(serial) == true)
|
||||
{
|
||||
throw new ApplicationException("Battle.net Authenticator does not have a serial");
|
||||
}
|
||||
serial = serial.ToUpper();
|
||||
if (Regex.IsMatch(serial, @"^[A-Z]{2}-?[\d]{4}-?[\d]{4}-?[\d]{4}$") == false)
|
||||
{
|
||||
throw new ApplicationException("Invalid serial for Battle.net Authenticator");
|
||||
}
|
||||
auth = new BattleNetAuthenticator();
|
||||
//char[] decoded = Base32.getInstance().Decode(secret).Select(c => Convert.ToChar(c)).ToArray(); // this is hex string values
|
||||
//string hex = new string(decoded);
|
||||
//((BattleNetAuthenticator)auth).SecretKey = Authenticator.StringToByteArray(hex);
|
||||
|
||||
((BattleNetAuthenticator)auth).SecretKey = Base32.getInstance().Decode(secret);
|
||||
|
||||
((BattleNetAuthenticator)auth).Serial = serial;
|
||||
|
||||
issuer = string.Empty;
|
||||
}
|
||||
else if (string.Compare(issuer, "Steam", true) == 0)
|
||||
{
|
||||
auth = new SteamAuthenticator();
|
||||
((SteamAuthenticator)auth).SecretKey = Base32.getInstance().Decode(secret);
|
||||
((SteamAuthenticator)auth).Serial = string.Empty;
|
||||
((SteamAuthenticator)auth).DeviceId = query["deviceid"] ?? string.Empty;
|
||||
((SteamAuthenticator)auth).SteamData = query["data"] ?? string.Empty;
|
||||
issuer = string.Empty;
|
||||
}
|
||||
else if (uri.Host == "hotp")
|
||||
{
|
||||
auth = new HOTPAuthenticator();
|
||||
((HOTPAuthenticator)auth).SecretKey = Base32.getInstance().Decode(secret);
|
||||
((HOTPAuthenticator)auth).Counter = int.Parse(counter);
|
||||
|
||||
if (string.IsNullOrEmpty(issuer) == false)
|
||||
{
|
||||
auth.Issuer = issuer;
|
||||
}
|
||||
}
|
||||
else // if (string.Compare(issuer, "Google", true) == 0)
|
||||
{
|
||||
auth = new GoogleAuthenticator();
|
||||
((GoogleAuthenticator)auth).Enroll(secret);
|
||||
|
||||
if (string.Compare(issuer, "Google", true) == 0)
|
||||
{
|
||||
issuer = string.Empty;
|
||||
}
|
||||
else if (string.IsNullOrEmpty(issuer) == false)
|
||||
{
|
||||
auth.Issuer = issuer;
|
||||
}
|
||||
}
|
||||
|
||||
int period = 0;
|
||||
int.TryParse(query["period"], out period);
|
||||
if (period != 0)
|
||||
{
|
||||
auth.Period = period;
|
||||
}
|
||||
|
||||
int digits = 0;
|
||||
int.TryParse(query["digits"], out digits);
|
||||
if (digits != 0)
|
||||
{
|
||||
auth.CodeDigits = digits;
|
||||
}
|
||||
|
||||
Authenticator.HMACTypes hmactype;
|
||||
|
||||
if (Enum.TryParse<Authenticator.HMACTypes>(query["algorithm"], true, out hmactype) == true)
|
||||
{
|
||||
auth.HMACType = hmactype;
|
||||
}
|
||||
|
||||
//
|
||||
if (label.Length != 0)
|
||||
{
|
||||
importedAuthenticator.Name = (issuer.Length != 0 ? issuer + " (" + label + ")" : label);
|
||||
}
|
||||
else if (issuer.Length != 0)
|
||||
{
|
||||
importedAuthenticator.Name = issuer;
|
||||
}
|
||||
else
|
||||
{
|
||||
importedAuthenticator.Name = "Imported";
|
||||
}
|
||||
//
|
||||
importedAuthenticator.AuthenticatorData = auth;
|
||||
|
||||
// sync
|
||||
importedAuthenticator.Sync();
|
||||
|
||||
authenticators.Add(importedAuthenticator);
|
||||
}
|
||||
}
|
||||
|
||||
return authenticators;
|
||||
}
|
||||
catch (UriFormatException ex)
|
||||
{
|
||||
throw new ImportException(string.Format("Invalid authenticator at line {0}", linenumber), ex);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new ImportException(string.Format("Error importing at line {0}:{1}", ex.Message), ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static string ExportAuthenticators(IList<WinAuthAuthenticator> authenticators, string password)
|
||||
{
|
||||
// create file in memory
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
using (var sw = new StreamWriter(ms))
|
||||
{
|
||||
List<WinAuthAuthenticator> unprotected = new List<WinAuthAuthenticator>();
|
||||
foreach (var auth in authenticators)
|
||||
{
|
||||
unprotected.Add(auth);
|
||||
string line = auth.ToUrl();
|
||||
sw.WriteLine(line);
|
||||
}
|
||||
|
||||
// reprotect
|
||||
foreach (var auth in unprotected)
|
||||
{
|
||||
auth.AuthenticatorData.Protect();
|
||||
}
|
||||
|
||||
// reset and write stream out to disk or as zip
|
||||
sw.Flush();
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
var result = Encoding.UTF8.GetString(ms.ToArray());
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace SteamTool.Auth
|
||||
{
|
||||
/// <summary>
|
||||
/// Exception class wrapper for importing error
|
||||
/// </summary>
|
||||
public class ImportException : ApplicationException
|
||||
{
|
||||
public ImportException() : base() { }
|
||||
|
||||
public ImportException(string message) : base(message) { }
|
||||
|
||||
public ImportException(string message, Exception ex) : base(message, ex) { }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<Platforms>AnyCPU;x86</Platforms>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SteamTool.Authenticator\SteamTool.Authenticator.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="PresentationCore">
|
||||
<HintPath>C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\PresentationCore.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace SteamTool.Auth.Win32
|
||||
{
|
||||
public class WinAPI
|
||||
{
|
||||
public const string LibraryName = "user32.dll";
|
||||
|
||||
[DllImport(LibraryName, CharSet = CharSet.Auto)]
|
||||
internal static extern IntPtr GetOpenClipboardWindow();
|
||||
|
||||
[DllImport(LibraryName, SetLastError = true, CharSet = CharSet.Auto)]
|
||||
internal static extern int GetWindowTextLength(IntPtr hWnd);
|
||||
|
||||
[DllImport(LibraryName, CharSet = CharSet.Auto, SetLastError = true)]
|
||||
internal static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,597 @@
|
||||
using SteamTool.Auth.Win32;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web;
|
||||
using System.Windows;
|
||||
using System.Xml;
|
||||
using WinAuth;
|
||||
|
||||
namespace SteamTool.Auth
|
||||
{
|
||||
public interface IWinAuthAuthenticatorChangedListener
|
||||
{
|
||||
void OnWinAuthAuthenticatorChanged(WinAuthAuthenticator sender, WinAuthAuthenticatorChangedEventArgs e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wrapper for real authenticator data used to save to file with other application information
|
||||
/// </summary>
|
||||
public class WinAuthAuthenticator : ICloneable
|
||||
{
|
||||
/// <summary>
|
||||
/// Event handler fired when property is changed
|
||||
/// </summary>
|
||||
public event WinAuthAuthenticatorChangedHandler OnWinAuthAuthenticatorChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Unique Id of authenticator saved in config
|
||||
/// </summary>
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Index for authenticator when in sorted list
|
||||
/// </summary>
|
||||
public int Index { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Actual authenticator data
|
||||
/// </summary>
|
||||
public Authenticator AuthenticatorData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// When this authenticator was created
|
||||
/// </summary>
|
||||
public DateTime Created { get; set; }
|
||||
|
||||
private string _name;
|
||||
private bool _autoRefresh;
|
||||
private bool _allowCopy;
|
||||
private bool _copyOnCode;
|
||||
private bool _hideSerial;
|
||||
|
||||
/// <summary>
|
||||
/// Create the authenticator wrapper
|
||||
/// </summary>
|
||||
public WinAuthAuthenticator()
|
||||
{
|
||||
Id = Guid.NewGuid();
|
||||
Created = DateTime.Now;
|
||||
_autoRefresh = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clone this authenticator
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public object Clone()
|
||||
{
|
||||
WinAuthAuthenticator clone = this.MemberwiseClone() as WinAuthAuthenticator;
|
||||
|
||||
clone.Id = Guid.NewGuid();
|
||||
clone.OnWinAuthAuthenticatorChanged = null;
|
||||
clone.AuthenticatorData = (this.AuthenticatorData != null ? this.AuthenticatorData.Clone() as Authenticator : null);
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mark this authenticator as having changed
|
||||
/// </summary>
|
||||
public void MarkChanged()
|
||||
{
|
||||
if (OnWinAuthAuthenticatorChanged != null)
|
||||
{
|
||||
OnWinAuthAuthenticatorChanged(this, new WinAuthAuthenticatorChangedEventArgs());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get/set the name of this authenticator
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
set
|
||||
{
|
||||
_name = value;
|
||||
if (OnWinAuthAuthenticatorChanged != null)
|
||||
{
|
||||
OnWinAuthAuthenticatorChanged(this, new WinAuthAuthenticatorChangedEventArgs("Name"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get/set auto refresh flag
|
||||
/// </summary>
|
||||
public bool AutoRefresh
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.AuthenticatorData != null && this.AuthenticatorData is HOTPAuthenticator)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _autoRefresh;
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
// HTOP must always be false
|
||||
if (this.AuthenticatorData != null && this.AuthenticatorData is HOTPAuthenticator)
|
||||
{
|
||||
_autoRefresh = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_autoRefresh = value;
|
||||
}
|
||||
if (OnWinAuthAuthenticatorChanged != null)
|
||||
{
|
||||
OnWinAuthAuthenticatorChanged(this, new WinAuthAuthenticatorChangedEventArgs("AutoRefresh"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get/set allow copy flag
|
||||
/// </summary>
|
||||
public bool AllowCopy
|
||||
{
|
||||
get
|
||||
{
|
||||
return _allowCopy;
|
||||
}
|
||||
set
|
||||
{
|
||||
_allowCopy = value;
|
||||
if (OnWinAuthAuthenticatorChanged != null)
|
||||
{
|
||||
OnWinAuthAuthenticatorChanged(this, new WinAuthAuthenticatorChangedEventArgs("AllowCopy"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get/set auto copy flag
|
||||
/// </summary>
|
||||
public bool CopyOnCode
|
||||
{
|
||||
get
|
||||
{
|
||||
return _copyOnCode;
|
||||
}
|
||||
set
|
||||
{
|
||||
_copyOnCode = value;
|
||||
if (OnWinAuthAuthenticatorChanged != null)
|
||||
{
|
||||
OnWinAuthAuthenticatorChanged(this, new WinAuthAuthenticatorChangedEventArgs("CopyOnCode"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get/set hide serial flag
|
||||
/// </summary>
|
||||
public bool HideSerial
|
||||
{
|
||||
get
|
||||
{
|
||||
return _hideSerial;
|
||||
}
|
||||
set
|
||||
{
|
||||
_hideSerial = value;
|
||||
if (OnWinAuthAuthenticatorChanged != null)
|
||||
{
|
||||
OnWinAuthAuthenticatorChanged(this, new WinAuthAuthenticatorChangedEventArgs("HideSerial"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public string CurrentCode
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.AuthenticatorData == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
string code = this.AuthenticatorData.CurrentCode;
|
||||
|
||||
if (this.AuthenticatorData is HOTPAuthenticator)
|
||||
{
|
||||
if (OnWinAuthAuthenticatorChanged != null)
|
||||
{
|
||||
OnWinAuthAuthenticatorChanged(this, new WinAuthAuthenticatorChangedEventArgs("HOTP", this.AuthenticatorData));
|
||||
}
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sync the current authenticator's time with its server
|
||||
/// </summary>
|
||||
public void Sync()
|
||||
{
|
||||
if (AuthenticatorData != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
AuthenticatorData.Sync();
|
||||
}
|
||||
catch (EncryptedSecretDataException)
|
||||
{
|
||||
// reset lastsync to force sync on next decryption
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy the current code to the clipboard
|
||||
/// </summary>
|
||||
public void CopyCodeToClipboard(string code = null, bool showError = false)
|
||||
{
|
||||
if (code == null)
|
||||
{
|
||||
code = this.CurrentCode;
|
||||
}
|
||||
|
||||
bool clipRetry = false;
|
||||
do
|
||||
{
|
||||
bool failed = false;
|
||||
// check if the clipboard is locked
|
||||
IntPtr hWnd = WinAPI.GetOpenClipboardWindow();
|
||||
if (hWnd != IntPtr.Zero)
|
||||
{
|
||||
int len = WinAPI.GetWindowTextLength(hWnd);
|
||||
if (len == 0)
|
||||
{
|
||||
//WinAuthMain.LogException(new ApplicationException("Clipboard in use by another process"));
|
||||
}
|
||||
else
|
||||
{
|
||||
StringBuilder sb = new StringBuilder(len + 1);
|
||||
WinAPI.GetWindowText(hWnd, sb, sb.Capacity);
|
||||
//WinAuthMain.LogException(new ApplicationException("Clipboard in use by '" + sb.ToString() + "'"));
|
||||
}
|
||||
|
||||
failed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Issue#170: can still get error copying even though it works, so just increase retries and ignore error
|
||||
try
|
||||
{
|
||||
Clipboard.Clear();
|
||||
|
||||
// add delay for clip error
|
||||
System.Threading.Thread.Sleep(100);
|
||||
Clipboard.SetDataObject(code, true);
|
||||
}
|
||||
catch (ExternalException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
if (failed == true && showError == true)
|
||||
{
|
||||
// only show an error the first time
|
||||
//clipRetry = (MessageBox.Show(form, strings.ClipboardInUse,
|
||||
// WinAuthMain.APPLICATION_NAME,
|
||||
// MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) == DialogResult.Yes);
|
||||
|
||||
}
|
||||
}
|
||||
while (clipRetry == true);
|
||||
}
|
||||
|
||||
public bool ReadXml(XmlReader reader, string password)
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
Guid id;
|
||||
if (Guid.TryParse(reader.GetAttribute("id"), out id) == true)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
|
||||
string authenticatorType = reader.GetAttribute("type");
|
||||
if (string.IsNullOrEmpty(authenticatorType) == false)
|
||||
{
|
||||
Type type = typeof(Authenticator).Assembly.GetType(authenticatorType, false, true);
|
||||
this.AuthenticatorData = Activator.CreateInstance(type) as Authenticator;
|
||||
}
|
||||
|
||||
//string encrypted = reader.GetAttribute("encrypted");
|
||||
//if (string.IsNullOrEmpty(encrypted) == false)
|
||||
//{
|
||||
// // read the encrypted text from the node
|
||||
// string data = reader.ReadElementContentAsString();
|
||||
// // decrypt
|
||||
// Authenticator.PasswordTypes passwordType;
|
||||
// data = Authenticator.DecryptSequence(data, encrypted, password, out passwordType);
|
||||
|
||||
// using (MemoryStream ms = new MemoryStream(Authenticator.StringToByteArray(data)))
|
||||
// {
|
||||
// reader = XmlReader.Create(ms);
|
||||
// ReadXml(reader, password);
|
||||
// }
|
||||
// this.PasswordType = passwordType;
|
||||
// this.Password = password;
|
||||
|
||||
// return;
|
||||
//}
|
||||
|
||||
reader.MoveToContent();
|
||||
|
||||
if (reader.IsEmptyElement)
|
||||
{
|
||||
reader.Read();
|
||||
return changed;
|
||||
}
|
||||
|
||||
reader.Read();
|
||||
while (reader.EOF == false)
|
||||
{
|
||||
if (reader.IsStartElement())
|
||||
{
|
||||
switch (reader.Name)
|
||||
{
|
||||
case "name":
|
||||
Name = reader.ReadElementContentAsString();
|
||||
break;
|
||||
|
||||
case "created":
|
||||
long t = reader.ReadElementContentAsLong();
|
||||
t += Convert.ToInt64(new TimeSpan(new DateTime(1970, 1, 1).Ticks).TotalMilliseconds);
|
||||
t *= TimeSpan.TicksPerMillisecond;
|
||||
Created = new DateTime(t).ToLocalTime();
|
||||
break;
|
||||
|
||||
case "autorefresh":
|
||||
_autoRefresh = reader.ReadElementContentAsBoolean();
|
||||
break;
|
||||
|
||||
case "allowcopy":
|
||||
_allowCopy = reader.ReadElementContentAsBoolean();
|
||||
break;
|
||||
|
||||
case "copyoncode":
|
||||
_copyOnCode = reader.ReadElementContentAsBoolean();
|
||||
break;
|
||||
|
||||
case "hideserial":
|
||||
_hideSerial = reader.ReadElementContentAsBoolean();
|
||||
break;
|
||||
|
||||
case "authenticatordata":
|
||||
try
|
||||
{
|
||||
// we don't pass the password as they are locked till clicked
|
||||
changed = this.AuthenticatorData.ReadXml(reader) || changed;
|
||||
}
|
||||
catch (EncryptedSecretDataException)
|
||||
{
|
||||
// no action needed
|
||||
}
|
||||
catch (BadPasswordException)
|
||||
{
|
||||
// no action needed
|
||||
}
|
||||
break;
|
||||
|
||||
// v2
|
||||
case "authenticator":
|
||||
this.AuthenticatorData = Authenticator.ReadXmlv2(reader, password);
|
||||
break;
|
||||
// v2
|
||||
case "servertimediff":
|
||||
this.AuthenticatorData.ServerTimeDiff = reader.ReadElementContentAsLong();
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
reader.Skip();
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reader.Read();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write the data as xml into an XmlWriter
|
||||
/// </summary>
|
||||
/// <param name="writer">XmlWriter to write config</param>
|
||||
public void WriteXmlString(XmlWriter writer)
|
||||
{
|
||||
writer.WriteStartElement(typeof(WinAuthAuthenticator).Name);
|
||||
writer.WriteAttributeString("id", this.Id.ToString());
|
||||
if (this.AuthenticatorData != null)
|
||||
{
|
||||
writer.WriteAttributeString("type", this.AuthenticatorData.GetType().FullName);
|
||||
}
|
||||
|
||||
//if (this.PasswordType != Authenticator.PasswordTypes.None)
|
||||
//{
|
||||
// string data;
|
||||
|
||||
// using (MemoryStream ms = new MemoryStream())
|
||||
// {
|
||||
// XmlWriterSettings settings = new XmlWriterSettings();
|
||||
// settings.Indent = true;
|
||||
// settings.Encoding = Encoding.UTF8;
|
||||
// using (XmlWriter encryptedwriter = XmlWriter.Create(ms, settings))
|
||||
// {
|
||||
// Authenticator.PasswordTypes savedpasswordType = PasswordType;
|
||||
// PasswordType = Authenticator.PasswordTypes.None;
|
||||
// WriteXmlString(encryptedwriter);
|
||||
// PasswordType = savedpasswordType;
|
||||
// }
|
||||
// //data = Encoding.UTF8.GetString(ms.ToArray());
|
||||
// data = Authenticator.ByteArrayToString(ms.ToArray());
|
||||
// }
|
||||
|
||||
// string encryptedTypes;
|
||||
// data = Authenticator.EncryptSequence(data, PasswordType, Password, out encryptedTypes);
|
||||
// writer.WriteAttributeString("encrypted", encryptedTypes);
|
||||
// writer.WriteString(data);
|
||||
// writer.WriteEndElement();
|
||||
|
||||
// return;
|
||||
//}
|
||||
|
||||
writer.WriteStartElement("name");
|
||||
writer.WriteValue(this.Name ?? string.Empty);
|
||||
writer.WriteEndElement();
|
||||
|
||||
writer.WriteStartElement("created");
|
||||
writer.WriteValue(Convert.ToInt64((this.Created.ToUniversalTime() - new DateTime(1970, 1, 1)).TotalMilliseconds));
|
||||
writer.WriteEndElement();
|
||||
|
||||
writer.WriteStartElement("autorefresh");
|
||||
writer.WriteValue(this.AutoRefresh);
|
||||
writer.WriteEndElement();
|
||||
//
|
||||
writer.WriteStartElement("allowcopy");
|
||||
writer.WriteValue(this.AllowCopy);
|
||||
writer.WriteEndElement();
|
||||
//
|
||||
writer.WriteStartElement("copyoncode");
|
||||
writer.WriteValue(this.CopyOnCode);
|
||||
writer.WriteEndElement();
|
||||
//
|
||||
writer.WriteStartElement("hideserial");
|
||||
writer.WriteValue(this.HideSerial);
|
||||
writer.WriteEndElement();
|
||||
//
|
||||
|
||||
// save the authenticator to the config file
|
||||
if (this.AuthenticatorData != null)
|
||||
{
|
||||
this.AuthenticatorData.WriteToWriter(writer);
|
||||
|
||||
// save script with password and generated salt
|
||||
//if (this.AutoLogin != null)
|
||||
//{
|
||||
// this.AutoLogin.WriteXmlString(writer, this.AuthenticatorData.PasswordType, this.AuthenticatorData.Password);
|
||||
//}
|
||||
}
|
||||
|
||||
writer.WriteEndElement();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a KeyUriFormat compatible URL
|
||||
/// See https://code.google.com/p/google-authenticator/wiki/KeyUriFormat
|
||||
/// </summary>
|
||||
/// <returns>string</returns>
|
||||
public virtual string ToUrl(bool compat = false)
|
||||
{
|
||||
string type = "totp";
|
||||
string extraparams = string.Empty;
|
||||
|
||||
Match match;
|
||||
string issuer = this.AuthenticatorData.Issuer;
|
||||
string label = this.Name;
|
||||
if (string.IsNullOrEmpty(issuer) == true && (match = Regex.Match(label, @"^([^\(]+)\s+\((.*?)\)(.*)")).Success == true)
|
||||
{
|
||||
issuer = match.Groups[1].Value;
|
||||
label = match.Groups[2].Value + match.Groups[3].Value;
|
||||
}
|
||||
if (string.IsNullOrEmpty(issuer) == false && (match = Regex.Match(label, @"^" + issuer + @"\s+\((.*?)\)(.*)")).Success == true)
|
||||
{
|
||||
label = match.Groups[1].Value + match.Groups[2].Value;
|
||||
}
|
||||
if (string.IsNullOrEmpty(issuer) == false)
|
||||
{
|
||||
extraparams += "&issuer=" + HttpUtility.UrlEncode(issuer);
|
||||
}
|
||||
|
||||
if (this.AuthenticatorData.HMACType != Authenticator.DEFAULT_HMAC_TYPE)
|
||||
{
|
||||
extraparams += "&algorithm=" + this.AuthenticatorData.HMACType.ToString();
|
||||
}
|
||||
|
||||
if (this.AuthenticatorData is BattleNetAuthenticator)
|
||||
{
|
||||
extraparams += "&serial=" + HttpUtility.UrlEncode(((BattleNetAuthenticator)this.AuthenticatorData).Serial.Replace("-", ""));
|
||||
}
|
||||
else if (this.AuthenticatorData is SteamAuthenticator)
|
||||
{
|
||||
if (compat == false)
|
||||
{
|
||||
extraparams += "&deviceid=" + HttpUtility.UrlEncode(((SteamAuthenticator)this.AuthenticatorData).DeviceId);
|
||||
extraparams += "&data=" + HttpUtility.UrlEncode(((SteamAuthenticator)this.AuthenticatorData).SteamData);
|
||||
}
|
||||
}
|
||||
else if (this.AuthenticatorData is HOTPAuthenticator)
|
||||
{
|
||||
type = "hotp";
|
||||
extraparams += "&counter=" + ((HOTPAuthenticator)this.AuthenticatorData).Counter;
|
||||
}
|
||||
|
||||
string secret = HttpUtility.UrlEncode(Base32.getInstance().Encode(this.AuthenticatorData.SecretKey));
|
||||
|
||||
if (this.AuthenticatorData.Period != Authenticator.DEFAULT_PERIOD)
|
||||
{
|
||||
extraparams += "&period=" + this.AuthenticatorData.Period;
|
||||
}
|
||||
|
||||
var url = string.Format("otpauth://" + type + "/{0}?secret={1}&digits={2}{3}",
|
||||
(string.IsNullOrEmpty(issuer) == false ? HttpUtility.UrlPathEncode(issuer) + ":" + HttpUtility.UrlPathEncode(label) : HttpUtility.UrlPathEncode(label)),
|
||||
secret,
|
||||
this.AuthenticatorData.CodeDigits,
|
||||
extraparams);
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delegate for ConfigChange event
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="args"></param>
|
||||
public delegate void WinAuthAuthenticatorChangedHandler(WinAuthAuthenticator source, WinAuthAuthenticatorChangedEventArgs args);
|
||||
|
||||
/// <summary>
|
||||
/// Change event arguments
|
||||
/// </summary>
|
||||
public class WinAuthAuthenticatorChangedEventArgs : EventArgs
|
||||
{
|
||||
public string Property { get; private set; }
|
||||
public Authenticator Authenticator { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor
|
||||
/// </summary>
|
||||
public WinAuthAuthenticatorChangedEventArgs(string property = null, Authenticator authenticator = null)
|
||||
{
|
||||
Property = property;
|
||||
Authenticator = authenticator;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -82,7 +82,7 @@ namespace WinAuth
|
||||
/// <summary>
|
||||
/// Time for http request when calling Sync in ms
|
||||
/// </summary>
|
||||
private const int SYNC_TIMEOUT = 30000;
|
||||
private const int SYNC_TIMEOUT = 5000;
|
||||
|
||||
/// <summary>
|
||||
/// Steam issuer for KeyUri
|
||||
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -7,7 +7,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="Portable.BouncyCastle" Version="1.8.9" />
|
||||
<PackageReference Include="Portable.BouncyCastle" Version="1.8.8" />
|
||||
<PackageReference Include="System.Security.Cryptography.ProtectedData" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.IO;
|
||||
using SteamTools.Model;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SteamTools.Helper
|
||||
{
|
||||
public static class ConfigHelper
|
||||
{
|
||||
private const string ConfigFileName = "SteamToolData.dat";
|
||||
|
||||
private static readonly string ConfigPath =
|
||||
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), ConfigFileName);
|
||||
|
||||
public static SteamToolConfig SteamToolConfig { get; set; }
|
||||
|
||||
public static void SaveConfig()
|
||||
{
|
||||
var jsonData = JsonConvert.SerializeObject(SteamToolConfig);
|
||||
File.WriteAllText(ConfigPath, jsonData, Encoding.UTF8);
|
||||
}
|
||||
|
||||
public static SteamToolConfig ReadConfig()
|
||||
{
|
||||
SteamToolConfig = JsonConvert.DeserializeObject<SteamToolConfig>(File.ReadAllText(ConfigPath, Encoding.UTF8));
|
||||
return SteamToolConfig;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace SteamTools.Helper
|
||||
{
|
||||
public static class RegistryKeyHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建注册表项
|
||||
/// </summary>
|
||||
/// <param name="registryKey"></param>
|
||||
/// <param name="path"></param>
|
||||
/// <returns></returns>
|
||||
public static RegistryKey CreateRegistryKey(RegistryKey registryKey, string path)
|
||||
{
|
||||
return registryKey.CreateSubKey(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除注册表项
|
||||
/// </summary>
|
||||
/// <param name="registryKey"></param>
|
||||
/// <param name="path"></param>
|
||||
/// <returns></returns>
|
||||
public static void DeleteRegistryKey(RegistryKey registryKey, string path)
|
||||
{
|
||||
registryKey.DeleteSubKey(path, true); //该方法无返回值,直接调用即可
|
||||
registryKey.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取注册表值
|
||||
/// </summary>
|
||||
/// <param name="registryKey"></param>
|
||||
/// <param name="path"></param>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public static string ReadRegistryKey(RegistryKey registryKey, string path, string key)
|
||||
{
|
||||
var myreg = registryKey.OpenSubKey(path);
|
||||
|
||||
if (myreg != null)
|
||||
{
|
||||
var info = myreg.GetValue(key).ToString();
|
||||
myreg.Close();
|
||||
return info;
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 新增或修改注册表值
|
||||
/// </summary>
|
||||
/// <param name="registryKey"></param>
|
||||
/// <param name="path"></param>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="registryValueKind"></param>
|
||||
/// <returns></returns>
|
||||
public static void AddOrUpdateRegistryKey(RegistryKey registryKey, string path, string key, string value, RegistryValueKind registryValueKind)
|
||||
{
|
||||
RegistryKey myreg = registryKey.OpenSubKey(path, true); //该项必须已存在
|
||||
if (myreg != null)
|
||||
{
|
||||
myreg.SetValue(key, value, registryValueKind);
|
||||
myreg.Close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Gameloop.Vdf.Linq;
|
||||
using Microsoft.Win32;
|
||||
using SteamTool.Common.Helper;
|
||||
using SteamTools.Model;
|
||||
|
||||
namespace SteamTools.Helper
|
||||
{
|
||||
public class SteamHelper
|
||||
{
|
||||
public static readonly SteamHelper Instance = new SteamHelper();
|
||||
|
||||
public string SteamPath { get; set; }
|
||||
|
||||
public string SteamExePath { get; set; }
|
||||
|
||||
private const string SteamRegistryPath = @"SOFTWARE\Valve\Steam";
|
||||
|
||||
public SteamHelper()
|
||||
{
|
||||
var steamExePath = RegistryKeyHelper.ReadRegistryKey(Registry.CurrentUser, SteamRegistryPath, "SteamExe");
|
||||
var steamPath = RegistryKeyHelper.ReadRegistryKey(Registry.CurrentUser, SteamRegistryPath, "SteamPath");
|
||||
if (File.Exists(steamExePath))
|
||||
{
|
||||
SteamExePath = steamExePath;
|
||||
}
|
||||
if (Directory.Exists(steamPath))
|
||||
{
|
||||
SteamPath = steamPath;
|
||||
}
|
||||
}
|
||||
|
||||
public void KillSteamProcess()
|
||||
{
|
||||
Process[] processes = Process.GetProcesses();
|
||||
foreach (Process process in processes)
|
||||
{
|
||||
if (process.ProcessName == "Steam" || process.ProcessName == "SteamService" || process.ProcessName == "steamwebhelper")
|
||||
{
|
||||
process.Kill();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void StartSteam(string param = "")
|
||||
{
|
||||
if (!string.IsNullOrEmpty(SteamExePath))
|
||||
{
|
||||
Process.Start(SteamExePath, param);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取最后一次自动登陆steam用户名称
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public string GetLastLoginUserName()
|
||||
{
|
||||
return RegistryKeyHelper.ReadRegistryKey(Registry.CurrentUser, SteamRegistryPath, "AutoLoginUser");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有记住登陆steam用户信息
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public List<SteamUser> GetAllUser()
|
||||
{
|
||||
var users = new List<SteamUser>();
|
||||
if (SteamPath != null)
|
||||
{
|
||||
var v = VdfHelper.GetVdfModelByPath(SteamPath + "/config/loginusers.vdf");
|
||||
|
||||
foreach (var item in v.Value)
|
||||
{
|
||||
var i = item.Value;
|
||||
var user = new SteamUser
|
||||
{
|
||||
AccountName = i.AccountName.ToString(),
|
||||
PersonaName = i.PersonaName.ToString(),
|
||||
RememberPassword = Convert.ToBoolean(Convert.ToInt64(i.RememberPassword.ToString())),
|
||||
MostRecent = Convert.ToBoolean(Convert.ToInt64(i.MostRecent.ToString())),
|
||||
Timestamp = Convert.ToInt64(i.Timestamp.ToString())
|
||||
};
|
||||
user.LastLoginTime= new DateTime(1970, 1, 1, 8, 0, 0).AddSeconds(user.Timestamp);
|
||||
users.Add(user);
|
||||
}
|
||||
}
|
||||
|
||||
return users;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置下次登陆steam用户
|
||||
/// </summary>
|
||||
public void SetCurrentUser(string username)
|
||||
{
|
||||
RegistryKeyHelper.AddOrUpdateRegistryKey(Registry.CurrentUser, SteamRegistryPath, "AutoLoginUser", username, RegistryValueKind.String);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Gameloop.Vdf;
|
||||
using Gameloop.Vdf.Linq;
|
||||
|
||||
namespace SteamTool.Common.Helper
|
||||
{
|
||||
public static class VdfHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取VDF类型内容
|
||||
/// </summary>
|
||||
/// <param name="path">vdf文件内容</param>
|
||||
/// <returns></returns>
|
||||
public static dynamic GetVdfModel(string text)
|
||||
{
|
||||
return VdfConvert.Deserialize(text);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据路径获取VDF类型内容
|
||||
/// </summary>
|
||||
/// <param name="path">vdf文件路径</param>
|
||||
/// <returns></returns>
|
||||
public static dynamic GetVdfModelByPath(string path)
|
||||
{
|
||||
string text = File.ReadAllText(path,Encoding.UTF8);
|
||||
return GetVdfModel(text); ;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SteamTools.Model
|
||||
{
|
||||
public class SteamToolConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// 启动参数
|
||||
/// </summary>
|
||||
public string SteamRunParam { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 保存用户
|
||||
/// </summary>
|
||||
public List<SteamUser> SteamUsers { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SteamTools.Model
|
||||
{
|
||||
public class SteamUser
|
||||
{
|
||||
|
||||
public string SteamId3 { get; set; }
|
||||
public int SteamId3_Int { get; }
|
||||
|
||||
public string SteamId32 { get; set; }
|
||||
public int SteamId32_Int { get; }
|
||||
|
||||
public long SteamId64 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 个人资料链接
|
||||
/// </summary>
|
||||
public string ProfileUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 昵称
|
||||
/// </summary>
|
||||
public string PersonaName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户名
|
||||
/// </summary>
|
||||
public string AccountName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否记住密码
|
||||
/// </summary>
|
||||
public bool RememberPassword { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 密码
|
||||
/// </summary>
|
||||
public string PassWord { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 最后登录时间戳
|
||||
/// </summary>
|
||||
public long Timestamp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 最后登录时间
|
||||
/// </summary>
|
||||
public DateTime LastLoginTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 最近登录
|
||||
/// </summary>
|
||||
public bool MostRecent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 备注
|
||||
/// </summary>
|
||||
public string Remark { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// 有关程序集的一般信息由以下
|
||||
// 控制。更改这些特性值可修改
|
||||
// 与程序集关联的信息。
|
||||
[assembly: AssemblyTitle("SteamTool.Common")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("SteamTool.Common")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2018")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// 将 ComVisible 设置为 false 会使此程序集中的类型
|
||||
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
|
||||
//请将此类型的 ComVisible 特性设置为 true。
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
|
||||
[assembly: Guid("75e148c7-5f32-4f01-9444-cedefa28748a")]
|
||||
|
||||
// 程序集的版本信息由下列四个值组成:
|
||||
//
|
||||
// 主版本
|
||||
// 次版本
|
||||
// 生成号
|
||||
// 修订号
|
||||
//
|
||||
// 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
|
||||
//通过使用 "*",如下所示:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SteamTool.Common.Service
|
||||
{
|
||||
public class SteamToolsService
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{75E148C7-5F32-4F01-9444-CEDEFA28748A}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>SteamTool.Common</RootNamespace>
|
||||
<AssemblyName>SteamTool.Common</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == '发布|AnyCPU'">
|
||||
<OutputPath>bin\发布\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Gameloop.Vdf, Version=0.6.1.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Gameloop.Vdf.0.6.1\lib\net45\Gameloop.Vdf.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Helper\ConfigHelper.cs" />
|
||||
<Compile Include="Helper\RegistryKeyHelper.cs" />
|
||||
<Compile Include="Helper\SteamHelper.cs" />
|
||||
<Compile Include="Helper\VdfHelper.cs" />
|
||||
<Compile Include="Model\SteamToolConfig.cs" />
|
||||
<Compile Include="Model\SteamUser.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Service\SteamToolsService.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Gameloop.Vdf" version="0.6.1" targetFramework="net46" />
|
||||
<package id="Newtonsoft.Json" version="12.0.3" targetFramework="net46" />
|
||||
</packages>
|
||||
@@ -0,0 +1,26 @@
|
||||
using SteamTool.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
|
||||
namespace SteamTool.Proxy
|
||||
{
|
||||
public static class CertificateEx
|
||||
{
|
||||
public static string GetPublicPemCertificateString(this X509Certificate2 @this)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.AppendLine(Const.HostTag);
|
||||
builder.AppendLine("-----BEGIN CERTIFICATE-----");
|
||||
builder.AppendLine(
|
||||
Convert.ToBase64String(@this.RawData, Base64FormattingOptions.InsertLineBreaks));
|
||||
builder.AppendLine("-----END CERTIFICATE-----");
|
||||
builder.AppendLine(Const.HostTag);
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,15 +9,6 @@ namespace SteamTool.Core.Common
|
||||
{
|
||||
public static class Common
|
||||
{
|
||||
/// <summary>
|
||||
/// 时间戳转DateTime(系统时区)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static DateTime ToDateTime(this long timestamp)
|
||||
{
|
||||
return TimeZoneInfo.ConvertTimeFromUtc(new DateTime(1970, 1, 1, 0, 0, 0).AddSeconds(timestamp), TimeZoneInfo.Local);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回当前时间戳
|
||||
/// </summary>
|
||||
@@ -46,16 +37,6 @@ namespace SteamTool.Core.Common
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除字符串内所有\r \n \t
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <returns></returns>
|
||||
public static string RemovePattern(this string inputString)
|
||||
{
|
||||
return inputString.Replace("\t", "").Replace("\r", "").Replace("\n", "");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取两个字符串中间的字符串
|
||||
/// </summary>
|
||||
-8
@@ -15,10 +15,6 @@ namespace SteamTool.Core.Common
|
||||
/// <returns></returns>
|
||||
public static string CompressString(this string text)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
byte[] buffer = Encoding.UTF8.GetBytes(text);
|
||||
var memoryStream = new MemoryStream();
|
||||
using (var gZipStream = new GZipStream(memoryStream, CompressionMode.Compress, true))
|
||||
@@ -44,10 +40,6 @@ namespace SteamTool.Core.Common
|
||||
/// <returns></returns>
|
||||
public static string DecompressString(this string compressedText)
|
||||
{
|
||||
if (string.IsNullOrEmpty(compressedText))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
byte[] gZipBuffer = Convert.FromBase64String(compressedText);
|
||||
using var memoryStream = new MemoryStream();
|
||||
int dataLength = BitConverter.ToInt32(gZipBuffer, 0);
|
||||
@@ -1,14 +1,12 @@
|
||||
using log4net;
|
||||
using log4net.Config;
|
||||
using log4net.Repository;
|
||||
using SteamTool.Core.Properties;
|
||||
using SteamTool.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
|
||||
namespace SteamTool.Core.Common
|
||||
{
|
||||
@@ -23,16 +21,7 @@ namespace SteamTool.Core.Common
|
||||
static Logger()
|
||||
{
|
||||
ILoggerRepository repository = LogManager.CreateRepository(Assembly.GetCallingAssembly().GetName().Name);
|
||||
if (File.Exists(Path.Combine(AppContext.BaseDirectory, @"log\Log4net.config")))
|
||||
{
|
||||
XmlConfigurator.Configure(repository, new FileInfo(@"log\Log4net.config"));
|
||||
}
|
||||
else
|
||||
{
|
||||
var doc = new XmlDocument();
|
||||
doc.LoadXml(Resources.Log4netXml);
|
||||
XmlConfigurator.Configure(repository, doc.DocumentElement);
|
||||
}
|
||||
XmlConfigurator.Configure(repository, new FileInfo(@"log\Log4net.config"));
|
||||
_textLog = LogManager.GetLogger(repository.Name, "TextLog");
|
||||
}
|
||||
|
||||
@@ -13,25 +13,12 @@ namespace SteamTool.Core
|
||||
{
|
||||
public class HostsService
|
||||
{
|
||||
public readonly static string HostsPath = @$"{Environment.SystemDirectory}\drivers\etc\hosts";
|
||||
public readonly static string HostsPath = $@"{Environment.GetFolderPath(Environment.SpecialFolder.System)}\drivers\etc\hosts";
|
||||
|
||||
static HostsService()
|
||||
{
|
||||
if (!File.Exists(HostsPath))
|
||||
{
|
||||
using var fs = File.Create(HostsPath);
|
||||
fs.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public OperationResult<List<string>> ReadHostsAllLines()
|
||||
{
|
||||
var result = new OperationResult<List<string>>(OperationResultType.Error, Resources.Hosts_ReadError);
|
||||
if (!File.Exists(HostsPath))
|
||||
{
|
||||
result.Message = "hosts file was not found";
|
||||
return result;
|
||||
}
|
||||
try
|
||||
{
|
||||
var dataLines = File.ReadAllLines(HostsPath).ToList();
|
||||
@@ -61,11 +48,7 @@ namespace SteamTool.Core
|
||||
public OperationResult UpdateHosts(string ip, string domain)
|
||||
{
|
||||
var result = new OperationResult(OperationResultType.Error, Resources.Hosts_WirteError);
|
||||
if (!File.Exists(HostsPath))
|
||||
{
|
||||
result.Message = "hosts file was not found";
|
||||
return result;
|
||||
}
|
||||
|
||||
//操作前取消只读属性
|
||||
File.SetAttributes(HostsPath, FileAttributes.Normal);
|
||||
|
||||
@@ -91,8 +74,8 @@ namespace SteamTool.Core
|
||||
|
||||
try
|
||||
{
|
||||
dataLines.Add($"{ip} {domain} {Const.HOST_TAG}");
|
||||
File.WriteAllLines(HostsPath, dataLines, Encoding.UTF8);
|
||||
dataLines.Add($"{ip} {domain} {Const.HostTag}");
|
||||
File.WriteAllLines(HostsPath, dataLines);
|
||||
|
||||
//File.SetAttributes(HostsPath, FileAttributes.ReadOnly);
|
||||
result.ResultType = OperationResultType.Success;
|
||||
@@ -111,12 +94,8 @@ namespace SteamTool.Core
|
||||
public OperationResult UpdateHosts(List<(string ip, string domain)> hosts)
|
||||
{
|
||||
var result = new OperationResult(OperationResultType.Error, Resources.Hosts_WirteError);
|
||||
if (!File.Exists(HostsPath))
|
||||
{
|
||||
result.Message = "hosts file was not found";
|
||||
return result;
|
||||
}
|
||||
if (hosts.Count == 0)
|
||||
|
||||
if (hosts.Count == 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
@@ -149,11 +128,11 @@ namespace SteamTool.Core
|
||||
|
||||
foreach (var (ip, domain) in hosts)
|
||||
{
|
||||
dataLines.Add($"{ip} {domain} {Const.HOST_TAG}");
|
||||
dataLines.Add($"{ip} {domain} {Const.HostTag}");
|
||||
}
|
||||
try
|
||||
{
|
||||
File.WriteAllLines(HostsPath, dataLines, Encoding.UTF8);
|
||||
File.WriteAllLines(HostsPath, dataLines);
|
||||
//File.SetAttributes(HostsPath, FileAttributes.ReadOnly);
|
||||
result.ResultType = OperationResultType.Success;
|
||||
result.Message = Resources.Hosts_UpdateSuccess;
|
||||
@@ -171,11 +150,7 @@ namespace SteamTool.Core
|
||||
public OperationResult RemoveHosts(string ip, string domain)
|
||||
{
|
||||
var result = new OperationResult(OperationResultType.Error, Resources.Hosts_WirteError);
|
||||
if (!File.Exists(HostsPath))
|
||||
{
|
||||
result.Message = "hosts file was not found";
|
||||
return result;
|
||||
}
|
||||
|
||||
//操作前取消只读属性
|
||||
File.SetAttributes(HostsPath, FileAttributes.Normal);
|
||||
|
||||
@@ -197,7 +172,7 @@ namespace SteamTool.Core
|
||||
|
||||
try
|
||||
{
|
||||
File.WriteAllLines(HostsPath, dataLines, Encoding.UTF8);
|
||||
File.WriteAllLines(HostsPath, dataLines);
|
||||
|
||||
//File.SetAttributes(HostsPath, FileAttributes.ReadOnly);
|
||||
result.ResultType = OperationResultType.Success;
|
||||
@@ -216,23 +191,21 @@ namespace SteamTool.Core
|
||||
public OperationResult RemoveHostsByTag()
|
||||
{
|
||||
var result = new OperationResult(OperationResultType.Error, Resources.Hosts_WirteError);
|
||||
if (!File.Exists(HostsPath))
|
||||
{
|
||||
result.Message = "hosts file was not found";
|
||||
return result;
|
||||
}
|
||||
|
||||
//操作前取消只读属性
|
||||
File.SetAttributes(HostsPath, FileAttributes.Normal);
|
||||
|
||||
//避免重复写入
|
||||
var dataLines = File.ReadAllLines(HostsPath, Encoding.Default).ToList().FindAll(s =>
|
||||
{
|
||||
var temp = s.Trim().Split(' ').ToList().FindAll(w => !string.IsNullOrEmpty(w));
|
||||
//有效数据一行内至少要有两列数据
|
||||
var temp = s.Trim().Split(' ').ToList();
|
||||
temp = temp.FindAll(s => !string.IsNullOrEmpty(s));
|
||||
//一行内至少要有两列数据
|
||||
if (temp.Count >= 2)
|
||||
{
|
||||
if (!temp[0].StartsWith("#"))
|
||||
{
|
||||
return !temp.Contains(Const.HOST_TAG);
|
||||
return !temp.Contains(Const.HostTag);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -240,7 +213,7 @@ namespace SteamTool.Core
|
||||
|
||||
try
|
||||
{
|
||||
File.WriteAllLines(HostsPath, dataLines, Encoding.UTF8);
|
||||
File.WriteAllLines(HostsPath, dataLines);
|
||||
//File.SetAttributes(HostsPath, FileAttributes.ReadOnly);
|
||||
result.ResultType = OperationResultType.Success;
|
||||
result.Message = Resources.Hosts_UpdateSuccess;
|
||||
@@ -255,7 +228,7 @@ namespace SteamTool.Core
|
||||
return result;
|
||||
}
|
||||
|
||||
public void StartNotepadEditHosts()
|
||||
public void StartNotepadEditHosts()
|
||||
{
|
||||
System.Diagnostics.Process.Start("notepad.exe", HostsPath);
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
using Newtonsoft.Json;
|
||||
using Ninject.Modules;
|
||||
using SteamTool.Core.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Dynamic;
|
||||
@@ -16,51 +15,27 @@ namespace SteamTool.Core
|
||||
{
|
||||
private readonly HttpClient _client = new HttpClient();
|
||||
|
||||
public string UserAgent { get; set; } = "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko";
|
||||
public string UserAgent { get; set; } = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36";
|
||||
|
||||
public string Accept { get; set; } = "application/json";
|
||||
|
||||
private async Task<string> SendAsync(HttpRequestMessage request)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _client.SendAsync(request).ConfigureAwait(false);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
return await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error(ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private async Task<Stream> SendAsyncAsStream(HttpRequestMessage request)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _client.SendAsync(request).ConfigureAwait(false);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
return await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error(ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<string> Get(string url)
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, url);
|
||||
request.Headers.Add("User-Agent", UserAgent);
|
||||
request.Headers.Add("Accept", Accept);
|
||||
return await SendAsync(request);
|
||||
|
||||
var response = await _client.SendAsync(request).ConfigureAwait(false);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
return await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
public async Task<string> Get(string url, Dictionary<string, string> keyValuePairs)
|
||||
{
|
||||
@@ -78,7 +53,20 @@ namespace SteamTool.Core
|
||||
{
|
||||
request.Headers.Add("Accept", Accept);
|
||||
}
|
||||
return await SendAsync(request);
|
||||
|
||||
|
||||
var response = await _client.SendAsync(request).ConfigureAwait(false);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
return await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
public async Task<Stream> GetStream(string url, Dictionary<string, string> keyValuePairs)
|
||||
{
|
||||
@@ -97,7 +85,18 @@ namespace SteamTool.Core
|
||||
request.Headers.Add("Accept", Accept);
|
||||
}
|
||||
|
||||
return await SendAsyncAsStream(request);
|
||||
|
||||
var response = await _client.SendAsync(request).ConfigureAwait(false);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
return await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public async Task<string> Post(string url, string content)
|
||||
@@ -108,7 +107,18 @@ namespace SteamTool.Core
|
||||
};
|
||||
request.Headers.Add("User-Agent", UserAgent);
|
||||
request.Headers.Add("Accept", Accept);
|
||||
return await SendAsync(request);
|
||||
|
||||
var response = await _client.SendAsync(request).ConfigureAwait(false);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
return await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
public async Task<string> Post(string url, Dictionary<string, string> keyValuePairs, string content)
|
||||
{
|
||||
@@ -129,7 +139,18 @@ namespace SteamTool.Core
|
||||
request.Headers.Add("Accept", Accept);
|
||||
}
|
||||
|
||||
return await SendAsync(request);
|
||||
|
||||
var response = await _client.SendAsync(request).ConfigureAwait(false);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
return await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
public async Task<Stream> PostStream(string url, Dictionary<string, string> keyValuePairs, Stream content)
|
||||
{
|
||||
@@ -150,7 +171,18 @@ namespace SteamTool.Core
|
||||
request.Headers.Add("Accept", Accept);
|
||||
}
|
||||
|
||||
return await SendAsyncAsStream(request);
|
||||
|
||||
var response = await _client.SendAsync(request).ConfigureAwait(false);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
return await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -19,11 +19,11 @@ namespace SteamTool.Core
|
||||
public SteamToolCore()
|
||||
{
|
||||
Kernel.Bind<ConfigService>().ToConstant(new ConfigService());
|
||||
Kernel.Bind<RegistryKeyService>().ToConstant(new RegistryKeyService());
|
||||
Kernel.Bind<RegistryKeyService>().To<RegistryKeyService>();
|
||||
Kernel.Bind<SteamToolService>().ToSelf();
|
||||
Kernel.Bind<VdfService>().To<VdfService>();
|
||||
Kernel.Bind<HttpServices>().ToConstant(new HttpServices());
|
||||
Kernel.Bind<HostsService>().ToConstant(new HostsService());
|
||||
Kernel.Bind<HostsService>().To<HostsService>();
|
||||
}
|
||||
|
||||
public TInterface Get<TInterface>()
|
||||
+11
-47
@@ -22,7 +22,7 @@ namespace SteamTool.Core.Properties {
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
public class Resources {
|
||||
internal class Resources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace SteamTool.Core.Properties {
|
||||
/// 返回此类使用的缓存的 ResourceManager 实例。
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
public static global::System.Resources.ResourceManager ResourceManager {
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SteamTool.Core.Properties.Resources", typeof(Resources).Assembly);
|
||||
@@ -47,11 +47,11 @@ namespace SteamTool.Core.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重写当前线程的 CurrentUICulture 属性,对
|
||||
/// 使用此强类型资源类的所有资源查找执行重写。
|
||||
/// 重写当前线程的 CurrentUICulture 属性
|
||||
/// 重写当前线程的 CurrentUICulture 属性。
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
public static global::System.Globalization.CultureInfo Culture {
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
@@ -74,7 +74,7 @@ namespace SteamTool.Core.Properties {
|
||||
///# Additionally, comments (such as these) may be inserted on individual
|
||||
///# lines or following th [字符串的其余部分被截断]"; 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string DefaultHosts {
|
||||
internal static string DefaultHosts {
|
||||
get {
|
||||
return ResourceManager.GetString("DefaultHosts", resourceCulture);
|
||||
}
|
||||
@@ -83,7 +83,7 @@ namespace SteamTool.Core.Properties {
|
||||
/// <summary>
|
||||
/// 查找类似 写入的Hosts已经存在 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string Hosts_Exists {
|
||||
internal static string Hosts_Exists {
|
||||
get {
|
||||
return ResourceManager.GetString("Hosts_Exists", resourceCulture);
|
||||
}
|
||||
@@ -92,7 +92,7 @@ namespace SteamTool.Core.Properties {
|
||||
/// <summary>
|
||||
/// 查找类似 读取Hosts时发生错误 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string Hosts_ReadError {
|
||||
internal static string Hosts_ReadError {
|
||||
get {
|
||||
return ResourceManager.GetString("Hosts_ReadError", resourceCulture);
|
||||
}
|
||||
@@ -101,7 +101,7 @@ namespace SteamTool.Core.Properties {
|
||||
/// <summary>
|
||||
/// 查找类似 读取Hosts成功 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string Hosts_ReadSuccess {
|
||||
internal static string Hosts_ReadSuccess {
|
||||
get {
|
||||
return ResourceManager.GetString("Hosts_ReadSuccess", resourceCulture);
|
||||
}
|
||||
@@ -110,7 +110,7 @@ namespace SteamTool.Core.Properties {
|
||||
/// <summary>
|
||||
/// 查找类似 写入Hosts成功 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string Hosts_UpdateSuccess {
|
||||
internal static string Hosts_UpdateSuccess {
|
||||
get {
|
||||
return ResourceManager.GetString("Hosts_UpdateSuccess", resourceCulture);
|
||||
}
|
||||
@@ -119,46 +119,10 @@ namespace SteamTool.Core.Properties {
|
||||
/// <summary>
|
||||
/// 查找类似 写入Hosts时发生错误 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string Hosts_WirteError {
|
||||
internal static string Hosts_WirteError {
|
||||
get {
|
||||
return ResourceManager.GetString("Hosts_WirteError", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 <?xml version="1.0"?>
|
||||
///<configuration>
|
||||
/// <configSections>
|
||||
/// <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
|
||||
/// </configSections>
|
||||
/// <log4net>
|
||||
/// <logger name="TextLog">
|
||||
/// <level value="ALL" />
|
||||
/// <appender-ref ref="RollingLogFileAppender" />
|
||||
/// </logger>
|
||||
/// <!--写文本-->
|
||||
/// <appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
|
||||
/// <param name="File" value="log\\" />
|
||||
/// <param name="AppendToFile" value="true" [字符串的其余部分被截断]"; 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string Log4netXml {
|
||||
get {
|
||||
return ResourceManager.GetString("Log4netXml", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 echo "Steam++替换升级中..."
|
||||
///taskkill /f /im {0}
|
||||
///xcopy /y /c /h /r /s "{1}\*.*" "{2}"
|
||||
///rmdir /s /q {1}
|
||||
///{3}
|
||||
///del %0 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string ProgramUpdateCmd {
|
||||
get {
|
||||
return ResourceManager.GetString("ProgramUpdateCmd", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-34
@@ -154,38 +154,4 @@
|
||||
<data name="Hosts_WirteError" xml:space="preserve">
|
||||
<value>写入Hosts时发生错误</value>
|
||||
</data>
|
||||
<data name="Log4netXml" xml:space="preserve">
|
||||
<value><?xml version="1.0"?>
|
||||
<configuration>
|
||||
<configSections>
|
||||
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
|
||||
</configSections>
|
||||
<log4net>
|
||||
<logger name="TextLog">
|
||||
<level value="ALL" />
|
||||
<appender-ref ref="RollingLogFileAppender" />
|
||||
</logger>
|
||||
<!--写文本-->
|
||||
<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
|
||||
<param name="File" value="log\\" />
|
||||
<param name="AppendToFile" value="true" />
|
||||
<param name="MaxSizeRollBackups" value="200" />
|
||||
<param name="StaticLogFileName" value="false" />
|
||||
<param name="DatePattern" value="yyyyMM\\yyyyMMdd'.log'" />
|
||||
<param name="RollingStyle" value="Date" />
|
||||
<layout type="log4net.Layout.PatternLayout,log4net">
|
||||
<param name="ConversionPattern" value="%d %-5p >> %m%n" />
|
||||
</layout>
|
||||
</appender>
|
||||
</log4net>
|
||||
</configuration></value>
|
||||
</data>
|
||||
<data name="ProgramUpdateCmd" xml:space="preserve">
|
||||
<value>echo "Steam++替换升级中..."
|
||||
taskkill /f /im {0}
|
||||
xcopy /y /c /h /r /s "{1}\*.*" "{2}"
|
||||
rmdir /s /q {1}
|
||||
{3}
|
||||
del %0</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -12,8 +12,6 @@
|
||||
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="Ninject" Version="3.3.4" />
|
||||
<PackageReference Include="Portable.BouncyCastle" Version="1.8.9" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.3.1" />
|
||||
<PackageReference Include="System.Management" Version="5.0.0" />
|
||||
<PackageReference Include="TaskScheduler" Version="2.9.0" />
|
||||
</ItemGroup>
|
||||
@@ -32,7 +30,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\Resources.resx">
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
@@ -35,13 +35,6 @@ namespace SteamTool.Core
|
||||
|
||||
public string SteamExePath { get; set; }
|
||||
|
||||
public const string UserDataDirectory = @"\userdata";
|
||||
|
||||
/*
|
||||
Windows: ~\Steam\config\loginusers.vdf
|
||||
Linux: ~/.steam/steam/config/loginusers.vdf
|
||||
Mac: ~/Library/Application Support/Steam/config/loginusers.vdf
|
||||
*/
|
||||
public const string UserVdfPath = @"\config\loginusers.vdf";
|
||||
|
||||
private const string SteamRegistryPath = @"SOFTWARE\Valve\Steam";
|
||||
@@ -50,13 +43,15 @@ namespace SteamTool.Core
|
||||
{
|
||||
var steamExePath = registryKeyService.ReadRegistryKey(Registry.CurrentUser, SteamRegistryPath, "SteamExe");
|
||||
var steamPath = registryKeyService.ReadRegistryKey(Registry.CurrentUser, SteamRegistryPath, "SteamPath");
|
||||
//steamExePath = Path.GetFullPath(steamExePath);
|
||||
//steamPath = Path.GetFullPath(steamPath);
|
||||
if (File.Exists(steamExePath))
|
||||
{
|
||||
SteamExePath = Path.GetFullPath(steamExePath);
|
||||
SteamExePath = steamExePath;
|
||||
}
|
||||
if (Directory.Exists(steamPath))
|
||||
{
|
||||
SteamPath = Path.GetFullPath(steamPath);
|
||||
SteamPath = steamPath;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +60,7 @@ namespace SteamTool.Core
|
||||
Process[] processes = Process.GetProcesses();
|
||||
foreach (Process process in processes)
|
||||
{
|
||||
if (process.ProcessName.Equals("steam", StringComparison.OrdinalIgnoreCase) || process.ProcessName.Equals("steamservice", StringComparison.OrdinalIgnoreCase) || process.ProcessName.Equals("steamwebhelper", StringComparison.OrdinalIgnoreCase))
|
||||
if (process.ProcessName.ToLower() == "steam" || process.ProcessName.ToLower() == "steamService" || process.ProcessName.ToLower() == "steamwebhelper")
|
||||
{
|
||||
process.Kill();
|
||||
}
|
||||
@@ -93,40 +88,37 @@ namespace SteamTool.Core
|
||||
/// 获取所有记住登陆steam用户信息
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public List<SteamUser> GetRememberUserList()
|
||||
public List<SteamUser> GetAllUser()
|
||||
{
|
||||
var users = new List<SteamUser>();
|
||||
if (File.Exists(SteamPath + UserVdfPath))
|
||||
{
|
||||
var v = vdfService.GetVdfModelByPath(SteamPath + UserVdfPath);
|
||||
|
||||
|
||||
foreach (var item in v.Value)
|
||||
{
|
||||
var i = item.Value;
|
||||
var user = new SteamUser(item.ToString())
|
||||
var user = new SteamUser
|
||||
{
|
||||
SteamId64 = Convert.ToInt64(item.Key.ToString()),
|
||||
AccountName = i.AccountName?.ToString(),
|
||||
SteamID = i.PersonaName?.ToString(),
|
||||
PersonaName = i.PersonaName?.ToString(),
|
||||
RememberPassword = Convert.ToBoolean(Convert.ToInt64(i.RememberPassword?.ToString())),
|
||||
Timestamp = Convert.ToInt64(i.Timestamp?.ToString())
|
||||
};
|
||||
user.LastLoginTime = user.Timestamp.ToDateTime();
|
||||
|
||||
//老版本Steam数据 小写mostrecent 支持
|
||||
user.MostRecent = i.mostrecent != null ?
|
||||
Convert.ToBoolean(Convert.ToByte(i.mostrecent.ToString())) :
|
||||
Convert.ToBoolean(Convert.ToByte(i.MostRecent.ToString()));
|
||||
|
||||
user.WantsOfflineMode = i.WantsOfflineMode != null ?
|
||||
Convert.ToBoolean(Convert.ToByte(i.WantsOfflineMode.ToString())) : false;
|
||||
|
||||
// 因为警告这个东西应该都不需要所以直接默认跳过好了
|
||||
user.SkipOfflineModeWarning = true;
|
||||
//user.SkipOfflineModeWarning = i.SkipOfflineModeWarning != null ?
|
||||
// Convert.ToBoolean(Convert.ToByte(i.SkipOfflineModeWarning.ToString())) : false;
|
||||
|
||||
user.LastLoginTime = new DateTime(1970, 1, 1, 8, 0, 0).AddSeconds(user.Timestamp);
|
||||
if (i.mostrecent != null)//老版本Steam数据 小写mostrecent 支持
|
||||
{
|
||||
user.MostRecent = Convert.ToBoolean(Convert.ToInt64(i.mostrecent.ToString()));
|
||||
}
|
||||
else if (i.MostRecent != null)
|
||||
{
|
||||
user.MostRecent = Convert.ToBoolean(Convert.ToInt64(i.MostRecent.ToString()));
|
||||
}
|
||||
else
|
||||
{
|
||||
user.MostRecent = false;
|
||||
}
|
||||
users.Add(user);
|
||||
}
|
||||
}
|
||||
@@ -142,6 +134,7 @@ namespace SteamTool.Core
|
||||
registryKeyService.AddOrUpdateRegistryKey(Registry.CurrentUser, SteamRegistryPath, "AutoLoginUser", username, RegistryValueKind.String);
|
||||
}
|
||||
|
||||
|
||||
public List<SteamApp> GetAppListJson(string filepath)
|
||||
{
|
||||
if (!File.Exists(filepath))
|
||||
@@ -161,6 +154,7 @@ namespace SteamTool.Core
|
||||
return apps.AppList.Apps;
|
||||
}
|
||||
|
||||
|
||||
public bool UpdateAppListJson(List<SteamApp> apps, string filepath)
|
||||
{
|
||||
var json = JsonConvert.SerializeObject(apps);
|
||||
@@ -190,17 +184,28 @@ namespace SteamTool.Core
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public void SetWindowsStartupAutoRun(bool IsAutoRun, string Name = "Steam++")
|
||||
{
|
||||
// Get the service on the local machine
|
||||
if (IsAutoRun)
|
||||
{
|
||||
using TaskDefinition td = TaskService.Instance.NewTask();
|
||||
// Create a new task definition and assign properties
|
||||
td.RegistrationInfo.Description = Name + "System Boot Run";
|
||||
|
||||
td.Settings.Priority = System.Diagnostics.ProcessPriorityClass.Normal;
|
||||
|
||||
// Create a trigger that will fire after the system boot
|
||||
td.Triggers.Add(new LogonTrigger());
|
||||
|
||||
// Create an action that will launch Notepad whenever the trigger fires
|
||||
td.Actions.Add(new ExecAction(Assembly.GetCallingAssembly().Location, "-minimized", Path.GetDirectoryName(Assembly.GetCallingAssembly().Location)));
|
||||
|
||||
if (IsAdministrator)
|
||||
td.Principal.RunLevel = TaskRunLevel.Highest;
|
||||
|
||||
// Register the task in the root folder
|
||||
TaskService.Instance.RootFolder.RegisterTaskDefinition(Name, td);
|
||||
//TaskService.Instance.RootFolder.AddTask(Name, QuickTriggerType.Boot, Assembly.GetCallingAssembly().Location, "-a");
|
||||
}
|
||||
@@ -209,20 +214,5 @@ namespace SteamTool.Core
|
||||
TaskService.Instance.RootFolder.DeleteTask(Name);
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteSteamLocalUserData(SteamUser user)
|
||||
{
|
||||
vdfService.DeleteVdfValueByKey(SteamPath + UserVdfPath, user.SteamId64.ToString());
|
||||
var temp = SteamPath + Path.Combine(UserDataDirectory, user.SteamId3_Int.ToString());
|
||||
if (Directory.Exists(temp))
|
||||
{
|
||||
Directory.Delete(temp, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateSteamLocalUserData(SteamUser user)
|
||||
{
|
||||
vdfService.UpdateVdfValueByReplace(SteamPath + UserVdfPath, user.OriginVdfString, user.CurrentVdfString);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Gameloop.Vdf;
|
||||
using Gameloop.Vdf.Linq;
|
||||
|
||||
namespace SteamTool.Core
|
||||
{
|
||||
public class VdfService
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 获取VDF类型内容
|
||||
/// </summary>
|
||||
/// <param name="path">vdf文件内容</param>
|
||||
/// <returns></returns>
|
||||
public dynamic GetVdfModel(string text)
|
||||
{
|
||||
return VdfConvert.Deserialize(text);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据路径获取VDF类型内容
|
||||
/// </summary>
|
||||
/// <param name="path">vdf文件路径</param>
|
||||
/// <returns></returns>
|
||||
public dynamic GetVdfModelByPath(string path)
|
||||
{
|
||||
string text = File.ReadAllText(path,Encoding.UTF8);
|
||||
return GetVdfModel(text); ;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,30 +5,19 @@ namespace SteamTool.Model
|
||||
public class Const
|
||||
{
|
||||
public const string APP_LIST_FILE = "apps.json";
|
||||
public const string AUTHDATA_FILE = "authenticators.dat";
|
||||
public const string SETTINGS_FILE = "settings.json";
|
||||
public const string SCRIPT_DIR = "scripts";
|
||||
public const string LOG_DIR = "log";
|
||||
public const string HOST_TAG = "#S302";
|
||||
public const string HostTag = "#S302";
|
||||
|
||||
public const string REWARDMELIST_URL = "https://gitee.com/rmbgame/steam-tools_-data/raw/master/RewardRecord.json";
|
||||
//public const string REWARDMELIST_URL = "https://raw.githubusercontent.com/rmbadmin/SteamTools/develop/Data/RewardRecord.json";
|
||||
|
||||
public const string GITHUB_LATEST_RELEASEAPI_URL = "https://api.github.com/repos/rmbadmin/SteamTools/releases/latest";
|
||||
public const string GITHUB_RELEASEAPI_URL = "https://api.github.com/repos/rmbadmin/SteamTools/releases";
|
||||
|
||||
public const string GITHUB_URL = "https://github.com/rmbadmin/SteamTools";
|
||||
public const string GITHUB_RELEASES_URL = "https://github.com/rmbadmin/SteamTools/releases";
|
||||
public const string MY_PROFILE_URL = "https://steamcommunity.com/profiles/76561198289531723/";
|
||||
public const string MY_WISHLIST_URL = "https://store.steampowered.com/wishlist/profiles/76561198289531723/";
|
||||
public const string MY_MINIPROFILE_URL = "https://steamcommunity.com/miniprofile/329265995";
|
||||
|
||||
public const string STEAM_LOGIN_URL = "https://steamcommunity.com/login/home/?goto=my/profile";
|
||||
public const string STEAM_BADGES_URL = "https://steamcommunity.com/profiles/{0}/badges/";
|
||||
public const string STEAMAPP_LIST_URL = "https://api.steampowered.com/ISteamApps/GetAppList/v2";
|
||||
public const string STORE_APP_URL = "https://store.steampowered.com/app/{0}";
|
||||
public const string STEAMAPP_LOGO_URL = "https://steamcdn-a.akamaihd.net/steamcommunity/public/images/apps/{0}/{1}.jpg";
|
||||
public const string STEAMAPP_ICON_URL = "https://steamcdn-a.akamaihd.net/steamcommunity/public/images/apps/{0}/{1}";
|
||||
public const string STEAMAPP_ICON_URL = "http://steamcdn-a.akamaihd.net/steamcommunity/public/images/apps/{0}/{1}";
|
||||
public const string STEAMAPP_CAPSULE_URL = "https://steamcdn-a.akamaihd.net/steam/apps/{0}/capsule_184x69.jpg";
|
||||
public const string STEAMAPP_HEADIMAGE_URL = "https://steamcdn-a.akamaihd.net/steam/apps/{0}/header.jpg";
|
||||
public const string STEAM_MEDIA_URL = "https://steamcdn-a.akamaihd.net/steam/apps/{0}/movie_max.webm";
|
||||
@@ -38,15 +27,9 @@ namespace SteamTool.Model
|
||||
public const string STEAM_INSTALL_URL = "steam://install/{0}";
|
||||
public const string STEAM_RUNGAME_URL = "steam://rungameid/{0}";
|
||||
public const string STEAM_OPENURL = "steam://openurl/{0}";
|
||||
public const string STEAM_RUN_KEYURL = "steam://openurl/https://store.steampowered.com/account/registerkey?key={0}";
|
||||
public const string STEAM_RUN_KeyURL = "steam://openurl/https://store.steampowered.com/account/registerkey?key={0}";
|
||||
|
||||
public const string STEAMDB_USERINFO_URL = "https://api.steamdb.ml/v1/users/{0}";
|
||||
public const string STEAMDB_APPINFO_URL = "https://api.steamdb.ml/v1/apps/{0}";
|
||||
|
||||
//https://steamcommunity.com/profiles/76561198289531723?xml=1
|
||||
public const string STEAM_USERINFO_XML_URL = "https://steamcommunity.com/profiles/{0}?xml=1";
|
||||
|
||||
|
||||
public const string STEAMCN_USERINFO_XML_URL = "https://my.steamchina.com/profiles/76561198289531723?xml=1";
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -16,7 +16,7 @@ namespace SteamTool.Model
|
||||
public bool IsHidden { get; set; }
|
||||
public bool IsAchieved { get; set; }
|
||||
public bool IsChecked { get; set; }
|
||||
public long UnlockTimeUnix { get; set; }
|
||||
public int UnlockTimeUnix { get; set; }
|
||||
public DateTime UnlockTime { get; set; }
|
||||
public string IconUrl => IsAchieved ? string.Format(Const.STEAMAPP_ICON_URL, AppId, IconNormal) : string.Format(Const.STEAMAPP_ICON_URL, AppId, IconLocked);
|
||||
public int Permission { get; set; }
|
||||
@@ -0,0 +1,87 @@
|
||||
using System;
|
||||
|
||||
namespace SteamTool.Model
|
||||
{
|
||||
public class SteamUser
|
||||
{
|
||||
public string SteamId3 { get; set; }
|
||||
public long SteamId3_Int => ((SteamId64 >> (ushort)0) & 0xFFFFFFFF);
|
||||
|
||||
public string SteamId32 { get; set; }
|
||||
public long SteamId32_Int { get; set; }
|
||||
|
||||
public long SteamId64 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 个人资料链接
|
||||
/// </summary>
|
||||
public string ProfileUrl => string.Format("https://steamcommunity.com/profiles/{0}", SteamId64);
|
||||
|
||||
/// <summary>
|
||||
/// 在线状态
|
||||
/// </summary>
|
||||
public string OnlineState { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 公开状态
|
||||
/// friendsonly
|
||||
/// public
|
||||
/// </summary>
|
||||
public string PrivacyState { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 头像链接
|
||||
/// </summary>
|
||||
public string AvatarIcon { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 头像链接
|
||||
/// </summary>
|
||||
public string AvatarMedium { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 头像链接
|
||||
/// </summary>
|
||||
public string AvatarFull { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 昵称
|
||||
/// </summary>
|
||||
public string SteamID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户名
|
||||
/// </summary>
|
||||
public string AccountName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否记住密码
|
||||
/// </summary>
|
||||
public bool RememberPassword { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 密码
|
||||
/// </summary>
|
||||
public string PassWord { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 最后登录时间戳
|
||||
/// </summary>
|
||||
public long Timestamp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 最后登录时间
|
||||
/// </summary>
|
||||
public DateTime LastLoginTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 最近登录
|
||||
/// </summary>
|
||||
public bool MostRecent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 备注
|
||||
/// </summary>
|
||||
public string Remark { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -13,10 +13,6 @@ using Titanium.Web.Proxy.EventArguments;
|
||||
using Titanium.Web.Proxy.Http;
|
||||
using Titanium.Web.Proxy.Models;
|
||||
using SteamTool.Model;
|
||||
using System.Security.Cryptography;
|
||||
using Org.BouncyCastle.Asn1;
|
||||
using Org.BouncyCastle.Asn1.X509;
|
||||
using Org.BouncyCastle.X509;
|
||||
|
||||
namespace SteamTool.Proxy
|
||||
{
|
||||
@@ -30,26 +26,18 @@ namespace SteamTool.Proxy
|
||||
public IReadOnlyCollection<ProxyDomainModel> ProxyDomains { get; set; }
|
||||
public List<ProxyScript> Scripts { get; set; }
|
||||
public bool IsEnableScript { get; set; }
|
||||
public bool IsOnlyWorkSteamBrowser { get; set; }
|
||||
public string CertificateName { get; }
|
||||
|
||||
public HttpProxy(IReadOnlyCollection<ProxyDomainModel> proxyDomains, string certificateName)
|
||||
{
|
||||
ProxyDomains = proxyDomains;
|
||||
CertificateName = certificateName;
|
||||
//proxyServer.CertificateManager.PfxPassword = $"{CertificateName}";
|
||||
proxyServer.ThreadPoolWorkerThread = Environment.ProcessorCount * 8;
|
||||
proxyServer.CertificateManager.PfxFilePath = Path.Combine(AppContext.BaseDirectory, $@"{CertificateName}.Certificate.pfx");
|
||||
proxyServer.CertificateManager.RootCertificateIssuerName = $"{CertificateName} Certificate Authority";
|
||||
proxyServer.CertificateManager.RootCertificateName = $"{CertificateName} Certificate";
|
||||
proxyServer.CertificateManager.CertificateEngine = Titanium.Web.Proxy.Network.CertificateEngine.DefaultWindows;
|
||||
}
|
||||
|
||||
public bool ProxyRunning => proxyServer.ProxyRunning;
|
||||
|
||||
public async Task OnRequest(object sender, SessionEventArgs e)
|
||||
{
|
||||
#if DEBUG
|
||||
#region 测试用
|
||||
/*
|
||||
if (e.HttpClient.Request.RequestUri.AbsoluteUri.Contains("steampowered.com"))
|
||||
@@ -73,21 +61,9 @@ namespace SteamTool.Proxy
|
||||
}
|
||||
*/
|
||||
#endregion
|
||||
Debug.WriteLine("OnRequest " + e.HttpClient.Request.RequestUri.AbsoluteUri);
|
||||
Debug.WriteLine("OnRequest HTTP " + e.HttpClient.Request.HttpVersion);
|
||||
//Logger.Info("OnRequest" + e.HttpClient.Request.RequestUri.AbsoluteUri);
|
||||
#if DEBUG
|
||||
Debug.WriteLine("OnRequest" + e.HttpClient.Request.RequestUri.AbsoluteUri);
|
||||
#endif
|
||||
// Dns.GetHostAddressesAsync(e.HttpClient.Request.Host).ContinueWith(s =>
|
||||
//{
|
||||
// //部分运营商将奇怪的域名解析到127.0.0.1 再此排除这些不支持的代理域名
|
||||
// if (IPAddress.IsLoopback(s.Result.FirstOrDefault())
|
||||
// && ProxyDomains.Count(w => w.IsEnable && w.Hosts.Contains(e.HttpClient.Request.Host)) == 0)
|
||||
// {
|
||||
// e.Ok($"URL : {e.HttpClient.Request.RequestUri.AbsoluteUri} \r\n not support proxy");
|
||||
// return;
|
||||
// }
|
||||
//});
|
||||
//Logger.Info("Steam++ OnRequest: " + e.HttpClient.Request.RequestUri.AbsoluteUri);
|
||||
foreach (var item in ProxyDomains)
|
||||
{
|
||||
if (!item.IsEnable)
|
||||
@@ -98,10 +74,6 @@ namespace SteamTool.Proxy
|
||||
{
|
||||
if (e.HttpClient.Request.RequestUri.AbsoluteUri.Contains(host))
|
||||
{
|
||||
if (e.HttpClient.Request.RequestUri.Scheme.Equals("http", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
e.Redirect(e.HttpClient.Request.RequestUri.AbsoluteUri.Remove(0, 4).Insert(0, "https"));
|
||||
}
|
||||
IPAddress iP = null;
|
||||
if (!string.IsNullOrEmpty(item.ProxyIPAddres))
|
||||
{
|
||||
@@ -110,8 +82,10 @@ namespace SteamTool.Proxy
|
||||
else
|
||||
{
|
||||
var iPs = await Dns.GetHostAddressesAsync(item.ToDomain);
|
||||
iP = iPs.FirstOrDefault();
|
||||
//Logger.Info("Proxy IP: " + iP);
|
||||
if (iPs.Length > 0)
|
||||
{
|
||||
iP = iPs[0];
|
||||
}
|
||||
}
|
||||
if (iP != null)
|
||||
{
|
||||
@@ -119,7 +93,6 @@ namespace SteamTool.Proxy
|
||||
}
|
||||
if (e.HttpClient.ConnectRequest?.ClientHelloInfo != null)
|
||||
{
|
||||
//Logger.Info("ClientHelloInfo Info: " + e.HttpClient.ConnectRequest.ClientHelloInfo);
|
||||
if (!string.IsNullOrEmpty(item.ServerName))
|
||||
{
|
||||
var sni = e.HttpClient.ConnectRequest.ClientHelloInfo.Extensions["server_name"];
|
||||
@@ -131,36 +104,20 @@ namespace SteamTool.Proxy
|
||||
e.HttpClient.ConnectRequest.ClientHelloInfo.Extensions.Remove("server_name");
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//没有匹配到的结果直接返回不支持,避免出现Loopback死循环内存溢出
|
||||
e.Ok($"URL : {e.HttpClient.Request.RequestUri.AbsoluteUri} {Environment.NewLine}not support proxy");
|
||||
return;
|
||||
}
|
||||
public async Task OnResponse(object sender, SessionEventArgs e)
|
||||
{
|
||||
// read response headers
|
||||
//var responseHeaders = e.HttpClient.Response.Headers;
|
||||
//if (!e.ProxySession.Request.Host.Equals("medeczane.sgk.gov.tr")) return;
|
||||
#if DEBUG
|
||||
Debug.WriteLine("OnResponse" + e.HttpClient.Request.RequestUri.AbsoluteUri);
|
||||
Logger.Info("OnResponse" + e.HttpClient.Request.RequestUri.AbsoluteUri);
|
||||
#endif
|
||||
if (IsEnableScript)
|
||||
{
|
||||
if (IsOnlyWorkSteamBrowser)
|
||||
{
|
||||
var ua = e.HttpClient.Request.Headers.GetHeaders("User-Agent");
|
||||
if (ua.Any())
|
||||
{
|
||||
if (!ua.First().Value.Contains("Valve Steam"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
return;
|
||||
}
|
||||
foreach (var script in Scripts)
|
||||
{
|
||||
if (script.Enable)
|
||||
@@ -181,12 +138,12 @@ namespace SteamTool.Proxy
|
||||
if (e.HttpClient.Request.RequestUri.AbsoluteUri.IsWildcard(host))
|
||||
{
|
||||
var doc = await e.GetResponseBodyAsString();
|
||||
if (script.Require.Length > 0 || script.Grant.Length > 0)
|
||||
if (script.Require.Length > 0)
|
||||
{
|
||||
//var headIndex = doc.LastIndexOf("</head>", StringComparison.OrdinalIgnoreCase);
|
||||
//doc = doc.Insert(headIndex, "<meta http-equiv=\"Content-Security-Policy\" content=\"default - src 'self' data: gap: https://ssl.gstatic.com 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *\">");
|
||||
var t = e.HttpClient.Response.Headers.GetFirstHeader("Content-Security-Policy");
|
||||
if (!string.IsNullOrEmpty(t?.Value))
|
||||
if (!string.IsNullOrEmpty(t.Value))
|
||||
{
|
||||
//var tt = t.Value.Split(';');
|
||||
//for (var i = 0; i < tt.Length; i++)
|
||||
@@ -207,7 +164,7 @@ namespace SteamTool.Proxy
|
||||
//e.HttpClient.Response.Headers.AddHeader("Content-Security-Policy", result);
|
||||
}
|
||||
#if DEBUG
|
||||
Debug.WriteLine(e.HttpClient.Request.RequestUri.AbsoluteUri);
|
||||
Debug.WriteLine(t);
|
||||
#endif
|
||||
foreach (var req in script.Require)
|
||||
{
|
||||
@@ -231,23 +188,26 @@ namespace SteamTool.Proxy
|
||||
close:;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
//// 允许重写默认的证书验证逻辑
|
||||
//public Task OnCertificateValidation(object sender, CertificateValidationEventArgs e)
|
||||
//{
|
||||
// // 根据证书错误,设置IsValid为真/假
|
||||
// //if (e.SslPolicyErrors == System.Net.Security.SslPolicyErrors.None)
|
||||
// e.IsValid = true;
|
||||
// return Task.CompletedTask;
|
||||
//}
|
||||
// 允许重写默认的证书验证逻辑
|
||||
public Task OnCertificateValidation(object sender, CertificateValidationEventArgs e)
|
||||
{
|
||||
// 根据证书错误,设置IsValid为真/假
|
||||
if (e.SslPolicyErrors == System.Net.Security.SslPolicyErrors.None)
|
||||
e.IsValid = true;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
//// 允许在相互身份验证期间重写默认客户端证书选择逻辑
|
||||
//public Task OnCertificateSelection(object sender, CertificateSelectionEventArgs e)
|
||||
//{
|
||||
// // set e.clientCertificate to override
|
||||
// return Task.CompletedTask;
|
||||
//}
|
||||
// 允许在相互身份验证期间重写默认客户端证书选择逻辑
|
||||
public Task OnCertificateSelection(object sender, CertificateSelectionEventArgs e)
|
||||
{
|
||||
// set e.clientCertificate to override
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public bool SetupCertificate()
|
||||
{
|
||||
@@ -256,22 +216,20 @@ namespace SteamTool.Proxy
|
||||
//proxyServer.CertificateManager
|
||||
// .CreateServerCertificate($"{Assembly.GetCallingAssembly().GetName().Name} Certificate")
|
||||
// .ContinueWith(c => proxyServer.CertificateManager.RootCertificate = c.Result);
|
||||
proxyServer.CertificateManager.PfxFilePath = $@"{CertificateName}.Certificate.pfx";
|
||||
//proxyServer.CertificateManager.PfxPassword = $"{CertificateName}";
|
||||
proxyServer.CertificateManager.RootCertificateIssuerName = CertificateName;
|
||||
proxyServer.CertificateManager.RootCertificateName = $"{CertificateName} Certificate";
|
||||
proxyServer.CertificateManager.CertificateEngine = Titanium.Web.Proxy.Network.CertificateEngine.DefaultWindows;
|
||||
|
||||
// 可选地设置证书引擎
|
||||
// 在Mono之下,只有BouncyCastle将得到支持
|
||||
//proxyServer.CertificateManager.CertificateEngine = Network.CertificateEngine.BouncyCastle;
|
||||
//proxyServer.CertificateManager.SaveFakeCertificates = true;
|
||||
|
||||
var result = proxyServer.CertificateManager.CreateRootCertificate(true);
|
||||
//if (result)
|
||||
//{
|
||||
if (!result)
|
||||
{
|
||||
Logger.Error("创建证书失败");
|
||||
}
|
||||
proxyServer.CertificateManager.RootCertificate.SaveCerCertificateFile(Path.Combine(AppContext.BaseDirectory, $@"{CertificateName}.Certificate.cer"));
|
||||
proxyServer.CertificateManager.SaveFakeCertificates = false;
|
||||
proxyServer.CertificateManager.CreateRootCertificate(true);
|
||||
proxyServer.CertificateManager.EnsureRootCertificate();
|
||||
//}
|
||||
//proxyServer.CertificateManager.TrustRootCertificate(true);
|
||||
|
||||
return IsCertificateInstalled(proxyServer.CertificateManager.RootCertificate);
|
||||
}
|
||||
|
||||
@@ -279,36 +237,104 @@ namespace SteamTool.Proxy
|
||||
{
|
||||
if (ProxyRunning)
|
||||
return false;
|
||||
try
|
||||
using (var store = new X509Store(StoreName.Root, StoreLocation.CurrentUser))
|
||||
{
|
||||
//using (var store = new X509Store(StoreName.Root, StoreLocation.CurrentUser))
|
||||
//{
|
||||
// store.Open(OpenFlags.MaxAllowed);
|
||||
// var test = store.Certificates.Find(X509FindType.FindByIssuerName, CertificateName, true);
|
||||
// foreach (var item in test)
|
||||
// {
|
||||
// store.Remove(item);
|
||||
// }
|
||||
//}
|
||||
//proxyServer.CertificateManager.ClearRootCertificate();
|
||||
proxyServer.CertificateManager.RemoveTrustedRootCertificate();
|
||||
//proxyServer.CertificateManager.RemoveTrustedRootCertificateAsAdmin();
|
||||
//proxyServer.CertificateManager.CertificateStorage.Clear();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (ex is CryptographicException)
|
||||
store.Open(OpenFlags.MaxAllowed);
|
||||
//var test = store.Certificates.Find(X509FindType.FindByIssuerName, "titan", true);
|
||||
var test = store.Certificates.Find(X509FindType.FindByIssuerName, CertificateName, true);
|
||||
foreach (var item in test)
|
||||
{
|
||||
//取消删除证书
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
store.Remove(item);
|
||||
}
|
||||
//if (store.Certificates.Contains(proxyServer.CertificateManager.RootCertificate))
|
||||
// store.Remove(proxyServer.CertificateManager.RootCertificate);
|
||||
}
|
||||
proxyServer.CertificateManager.ClearRootCertificate();
|
||||
proxyServer.CertificateManager.RemoveTrustedRootCertificate(true);
|
||||
proxyServer.CertificateManager.RemoveTrustedRootCertificateAsAdmin(true);
|
||||
proxyServer.CertificateManager.CertificateStorage.Clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool StartProxy(bool IsProxyGOG = false)
|
||||
{
|
||||
if (!IsCertificateInstalled(proxyServer.CertificateManager.RootCertificate))
|
||||
{
|
||||
var isOk = SetupCertificate();
|
||||
if (!isOk)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (PortInUse(443))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (IsProxyGOG) { WirtePemCertificateToGoGSteamPlugins(); }
|
||||
|
||||
#region 写入Hosts
|
||||
var hosts = new List<(string, string)>();
|
||||
foreach (var proxyDomain in ProxyDomains)
|
||||
{
|
||||
if (proxyDomain.IsEnable)
|
||||
{
|
||||
foreach (var host in proxyDomain.Hosts)
|
||||
{
|
||||
hosts.Add((IPAddress.Loopback.ToString(), host));
|
||||
}
|
||||
}
|
||||
}
|
||||
hostsService.UpdateHosts(hosts);
|
||||
#endregion
|
||||
|
||||
#region 启动代理
|
||||
proxyServer.BeforeRequest += OnRequest;
|
||||
proxyServer.BeforeResponse += OnResponse;
|
||||
//proxyServer.ServerCertificateValidationCallback += OnCertificateValidation;
|
||||
//proxyServer.ClientCertificateSelectionCallback += OnCertificateSelection;
|
||||
|
||||
//var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 443, true)
|
||||
//{
|
||||
// // 在所有https请求上使用自颁发的通用证书
|
||||
// // 通过不为每个启用http的域创建证书来优化性能
|
||||
// // 当代理客户端不需要证书信任时非常有用
|
||||
// //GenericCertificate = new X509Certificate2(Path.Combine(AppContext.BaseDirectory, "genericcert.pfx"), "password")
|
||||
//};
|
||||
//当接收到连接请求时触发
|
||||
//explicitEndPoint.BeforeTunnelConnectRequest += OnBeforeTunnelRequest;
|
||||
//explicit endpoint 是客户端知道代理存在的地方
|
||||
//因此,客户端以代理友好的方式发送请求
|
||||
//proxyServer.AddEndPoint(explicitEndPoint);
|
||||
// 透明endpoint 对于反向代理很有用(客户端不知道代理的存在)
|
||||
// 透明endpoint 通常需要一个网络路由器端口来转发HTTP(S)包或DNS
|
||||
// 发送数据到此endpoint
|
||||
var transparentEndPoint = new TransparentProxyEndPoint(IPAddress.Any, 443, true)
|
||||
{
|
||||
//GenericCertificate = proxyServer.CertificateManager.RootCertificate
|
||||
//GenericCertificateName= "steamcommunity-a.akamaihd.net"
|
||||
};
|
||||
proxyServer.AddEndPoint(transparentEndPoint);
|
||||
try
|
||||
{
|
||||
proxyServer.Start();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error(ex);
|
||||
return false;
|
||||
}
|
||||
//proxyServer.UpStreamHttpProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 };
|
||||
//proxyServer.UpStreamHttpsProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 };
|
||||
#endregion
|
||||
|
||||
#if DEBUG
|
||||
foreach (var endPoint in proxyServer.ProxyEndPoints)
|
||||
Debug.WriteLine("Listening on '{0}' endpoint at Ip {1} and port: {2} ",
|
||||
endPoint.GetType().Name, endPoint.IpAddress, endPoint.Port);
|
||||
#endif
|
||||
return true;
|
||||
|
||||
}
|
||||
public static bool PortInUse(int port)
|
||||
{
|
||||
bool inUse = false;
|
||||
@@ -328,100 +354,14 @@ namespace SteamTool.Proxy
|
||||
return inUse;
|
||||
}
|
||||
|
||||
public bool StartProxy(bool IsProxyGOG = false)
|
||||
{
|
||||
if (!IsCertificateInstalled(proxyServer.CertificateManager.RootCertificate))
|
||||
{
|
||||
var isOk = SetupCertificate();
|
||||
if (!isOk)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
//if (PortInUse(443))
|
||||
//{
|
||||
// return false;
|
||||
//}
|
||||
if (IsProxyGOG) { WirtePemCertificateToGoGSteamPlugins(); }
|
||||
|
||||
#region 写入Hosts
|
||||
var hosts = new List<(string, string)>();
|
||||
foreach (var proxyDomain in ProxyDomains)
|
||||
{
|
||||
if (proxyDomain.IsEnable)
|
||||
{
|
||||
foreach (var host in proxyDomain.Hosts)
|
||||
{
|
||||
if (host.Contains(" "))
|
||||
{
|
||||
var h = host.Split(' ');
|
||||
hosts.Add((h[0], h[1]));
|
||||
}
|
||||
else
|
||||
hosts.Add((IPAddress.Loopback.ToString(), host));
|
||||
}
|
||||
}
|
||||
}
|
||||
hostsService.UpdateHosts(hosts);
|
||||
#endregion
|
||||
|
||||
#region 启动代理
|
||||
proxyServer.BeforeRequest += OnRequest;
|
||||
proxyServer.BeforeResponse += OnResponse;
|
||||
//proxyServer.ServerCertificateValidationCallback += OnCertificateValidation;
|
||||
//proxyServer.ClientCertificateSelectionCallback += OnCertificateSelection;
|
||||
|
||||
//var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8888, true)
|
||||
//{
|
||||
// // 在所有https请求上使用自颁发的通用证书
|
||||
// // 当代理客户端不需要证书信任时非常有用
|
||||
// //GenericCertificate = new X509Certificate2(Path.Combine(AppContext.BaseDirectory, "genericcert.pfx"), "password")
|
||||
//};
|
||||
//当接收到连接请求时触发
|
||||
//explicitEndPoint.BeforeTunnelConnectRequest += OnBeforeTunnelRequest;
|
||||
//explicit endpoint 是客户端知道代理存在的地方
|
||||
//proxyServer.AddEndPoint(explicitEndPoint);
|
||||
proxyServer.AddEndPoint(new TransparentProxyEndPoint(IPAddress.Any, 443, true)
|
||||
{
|
||||
// 通过不启用为每个http的域创建证书来优化性能
|
||||
//GenericCertificate = proxyServer.CertificateManager.RootCertificate
|
||||
});
|
||||
proxyServer.AddEndPoint(new TransparentProxyEndPoint(IPAddress.Any, 80, true));
|
||||
proxyServer.ExceptionFunc = ((Exception exception) =>
|
||||
{
|
||||
Logger.Error(exception);
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
proxyServer.Start();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error(ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
//proxyServer.UpStreamHttpProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 };
|
||||
//proxyServer.UpStreamHttpsProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 };
|
||||
#endregion
|
||||
#if DEBUG
|
||||
foreach (var endPoint in proxyServer.ProxyEndPoints)
|
||||
Debug.WriteLine("Listening on '{0}' endpoint at Ip {1} and port: {2} ",
|
||||
endPoint.GetType().Name, endPoint.IpAddress, endPoint.Port);
|
||||
#endif
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
public void StopProxy()
|
||||
{
|
||||
if (proxyServer.ProxyRunning)
|
||||
{
|
||||
proxyServer.BeforeRequest -= OnRequest;
|
||||
proxyServer.BeforeResponse -= OnResponse;
|
||||
//proxyServer.ServerCertificateValidationCallback -= OnCertificateValidation;
|
||||
//proxyServer.ClientCertificateSelectionCallback -= OnCertificateSelection;
|
||||
proxyServer.ServerCertificateValidationCallback -= OnCertificateValidation;
|
||||
proxyServer.ClientCertificateSelectionCallback -= OnCertificateSelection;
|
||||
proxyServer.Stop();
|
||||
hostsService.RemoveHostsByTag();
|
||||
}
|
||||
@@ -442,14 +382,14 @@ namespace SteamTool.Proxy
|
||||
if (File.Exists(certifi))
|
||||
{
|
||||
var file = File.ReadAllText(certifi);
|
||||
var s = file.Substring(Const.HOST_TAG, Const.HOST_TAG, true);
|
||||
var s = file.Substring(Const.HostTag, Const.HostTag, true);
|
||||
if (string.IsNullOrEmpty(s))
|
||||
{
|
||||
File.AppendAllText(certifi, Environment.NewLine + pem);
|
||||
File.AppendAllText(certifi, "\r\n" + pem);
|
||||
}
|
||||
else if (s.Trim() != pem.Trim())
|
||||
{
|
||||
var index = file.IndexOf(Const.HOST_TAG);
|
||||
var index = file.IndexOf(Const.HostTag);
|
||||
File.WriteAllText(certifi, file.Remove(index, s.Length) + pem);
|
||||
}
|
||||
return true;
|
||||
+15
-19
@@ -9,6 +9,8 @@ namespace SteamTool.Proxy
|
||||
{
|
||||
public class ProxyDomainModel
|
||||
{
|
||||
public int Index { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 显示在UI上的名称
|
||||
/// </summary>
|
||||
@@ -17,7 +19,7 @@ namespace SteamTool.Proxy
|
||||
/// <summary>
|
||||
/// 代理域名合集
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<string> Domains { get; set; }
|
||||
public List<string> Domains { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 端口
|
||||
@@ -44,7 +46,7 @@ namespace SteamTool.Proxy
|
||||
/// </summary>
|
||||
public bool IsEnable { get; set; }
|
||||
|
||||
public IReadOnlyCollection<string> Hosts { get; set; }
|
||||
public IEnumerable<string> Hosts { get; set; }
|
||||
|
||||
public DomainTag DomainTag { get; set; }
|
||||
}
|
||||
@@ -52,8 +54,14 @@ namespace SteamTool.Proxy
|
||||
[TypeConverter(typeof(EnumDescriptionTypeConverter))]
|
||||
public enum DomainTag : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// steam社区
|
||||
/// </summary>
|
||||
[Description("steam社区")]
|
||||
SteamCommunity = 0,
|
||||
/// <summary>
|
||||
/// steam商店
|
||||
/// </summary>
|
||||
[Description("steam商店")]
|
||||
SteamStore = 1,
|
||||
[Description("steam部分图片修复")]
|
||||
@@ -64,25 +72,13 @@ namespace SteamTool.Proxy
|
||||
Discord = 4,
|
||||
[Description("Twitch直播")]
|
||||
Twitch = 5,
|
||||
[Description("Origin下载(Akamai)")]
|
||||
[Description("Origin下载加速(Akamai)")]
|
||||
OriginGameDownload = 6,
|
||||
[Description("GOG.COM")]
|
||||
GOG = 7,
|
||||
[Description("Uplay更新防劫持")]
|
||||
UplayUpdate = 7,
|
||||
[Description("Google(Recaptcha)")]
|
||||
GoogleCode = 8,
|
||||
[Description("Github图片及资源修复")]
|
||||
GithubContent = 9,
|
||||
[Description("Pixiv")]
|
||||
Pixiv = 10,
|
||||
[Description("Discord更新下载")]
|
||||
DiscordNet = 11,
|
||||
[Description("Uplay更新防劫持")]
|
||||
UplayUpdate = 12,
|
||||
[Description("Twitch聊天")]
|
||||
TwitchChat = 13,
|
||||
[Description("Github Gist")]
|
||||
GithubGist = 14,
|
||||
[Description("Pixiv图片")]
|
||||
PixivImage = 15,
|
||||
[Description("GOG.COM")]
|
||||
GOG = 8,
|
||||
}
|
||||
}
|
||||
-54
@@ -69,33 +69,6 @@ namespace SteamTool.Proxy.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Discord图片及更新下载 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string DiscordNet {
|
||||
get {
|
||||
return ResourceManager.GetString("DiscordNet", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Github图片资源修复 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string GithubContent {
|
||||
get {
|
||||
return ResourceManager.GetString("GithubContent", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Github Gist 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string GithubGist {
|
||||
get {
|
||||
return ResourceManager.GetString("GithubGist", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 GOG Galaxy 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -132,24 +105,6 @@ namespace SteamTool.Proxy.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Pixiv 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string Pixiv {
|
||||
get {
|
||||
return ResourceManager.GetString("Pixiv", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Pixiv图片 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string PixivImgae {
|
||||
get {
|
||||
return ResourceManager.GetString("PixivImgae", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Steam好友聊天 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -195,15 +150,6 @@ namespace SteamTool.Proxy.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Twitch聊天 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TwitchChat {
|
||||
get {
|
||||
return ResourceManager.GetString("TwitchChat", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Uplay更新 的本地化字符串。
|
||||
/// </summary>
|
||||
-18
@@ -120,15 +120,6 @@
|
||||
<data name="Discord" xml:space="preserve">
|
||||
<value>Discord语音</value>
|
||||
</data>
|
||||
<data name="DiscordNet" xml:space="preserve">
|
||||
<value>Discord图片及更新下载</value>
|
||||
</data>
|
||||
<data name="GithubContent" xml:space="preserve">
|
||||
<value>Github图片资源修复</value>
|
||||
</data>
|
||||
<data name="GithubGist" xml:space="preserve">
|
||||
<value>Github Gist</value>
|
||||
</data>
|
||||
<data name="GOG" xml:space="preserve">
|
||||
<value>GOG Galaxy</value>
|
||||
</data>
|
||||
@@ -141,12 +132,6 @@
|
||||
<data name="OriginDownload" xml:space="preserve">
|
||||
<value>Origin游戏下载(Akamai)</value>
|
||||
</data>
|
||||
<data name="Pixiv" xml:space="preserve">
|
||||
<value>Pixiv</value>
|
||||
</data>
|
||||
<data name="PixivImgae" xml:space="preserve">
|
||||
<value>Pixiv图片</value>
|
||||
</data>
|
||||
<data name="SteamChat" xml:space="preserve">
|
||||
<value>Steam好友聊天</value>
|
||||
</data>
|
||||
@@ -162,9 +147,6 @@
|
||||
<data name="Twitch" xml:space="preserve">
|
||||
<value>Twitch直播</value>
|
||||
</data>
|
||||
<data name="TwitchChat" xml:space="preserve">
|
||||
<value>Twitch聊天</value>
|
||||
</data>
|
||||
<data name="UplayUpdate" xml:space="preserve">
|
||||
<value>Uplay更新</value>
|
||||
</data>
|
||||
+7
-2
@@ -11,7 +11,7 @@
|
||||
<PackageReference Include="BrotliSharpLib" Version="0.3.3" />
|
||||
<PackageReference Include="DnsClient" Version="1.3.2" />
|
||||
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
|
||||
<PackageReference Include="Portable.BouncyCastle" Version="1.8.9" />
|
||||
<PackageReference Include="Portable.BouncyCastle" Version="1.8.8" />
|
||||
<PackageReference Include="System.Buffers" Version="4.5.1" />
|
||||
<PackageReference Include="System.Memory" Version="4.5.4" />
|
||||
<PackageReference Include="System.Runtime.InteropServices.RuntimeInformation" Version="4.3.0" />
|
||||
@@ -21,7 +21,12 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SteamTool.Core\SteamTool.Core.csproj" />
|
||||
<ProjectReference Include="..\..\references\Titanium-Web-Proxy\src\Titanium.Web.Proxy\Titanium.Web.Proxy.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Titanium.Web.Proxy">
|
||||
<HintPath>..\Lib\Titanium.Web.Proxy.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user