mirror of
https://github.com/2dust/v2rayN.git
synced 2025-05-01 20:58:50 +00:00
551 lines
20 KiB
C#
551 lines
20 KiB
C#
using DynamicData;
|
|
using Splat;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
using System.Windows;
|
|
using v2rayN.Base;
|
|
using v2rayN.Mode;
|
|
using v2rayN.Resx;
|
|
|
|
namespace v2rayN.Handler
|
|
{
|
|
class UpdateHandle
|
|
{
|
|
Action<bool, string> _updateFunc;
|
|
private Config _config;
|
|
|
|
public event EventHandler<ResultEventArgs> AbsoluteCompleted;
|
|
|
|
public class ResultEventArgs : EventArgs
|
|
{
|
|
public bool Success;
|
|
public string Msg;
|
|
|
|
public ResultEventArgs(bool success, string msg)
|
|
{
|
|
Success = success;
|
|
Msg = msg;
|
|
}
|
|
}
|
|
|
|
public void CheckUpdateGuiN(Config config, Action<bool, string> update, bool preRelease)
|
|
{
|
|
_config = config;
|
|
_updateFunc = update;
|
|
var url = string.Empty;
|
|
|
|
DownloadHandle downloadHandle = new();
|
|
downloadHandle.UpdateCompleted += (sender2, args) =>
|
|
{
|
|
if (args.Success)
|
|
{
|
|
_updateFunc(false, ResUI.MsgDownloadV2rayCoreSuccessfully);
|
|
|
|
try
|
|
{
|
|
string fileName = Utils.GetTempPath(Utils.GetDownloadFileName(url));
|
|
fileName = Utils.UrlEncode(fileName);
|
|
Process process = new()
|
|
{
|
|
StartInfo = new ProcessStartInfo
|
|
{
|
|
FileName = "v2rayUpgrade.exe",
|
|
Arguments = $"\"{fileName}\"",
|
|
WorkingDirectory = Utils.StartupPath()
|
|
}
|
|
};
|
|
process.Start();
|
|
if (process.Id > 0)
|
|
{
|
|
_updateFunc(true, "");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_updateFunc(false, ex.Message);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_updateFunc(false, args.Msg);
|
|
}
|
|
};
|
|
downloadHandle.Error += (sender2, args) =>
|
|
{
|
|
_updateFunc(false, args.GetException().Message);
|
|
};
|
|
AbsoluteCompleted += (sender2, args) =>
|
|
{
|
|
if (args.Success)
|
|
{
|
|
_updateFunc(false, string.Format(ResUI.MsgParsingSuccessfully, "v2rayN"));
|
|
|
|
url = args.Msg;
|
|
askToDownload(downloadHandle, url, true);
|
|
}
|
|
else
|
|
{
|
|
Locator.Current.GetService<NoticeHandler>()?.Enqueue(args.Msg);
|
|
_updateFunc(false, args.Msg);
|
|
}
|
|
};
|
|
_updateFunc(false, string.Format(ResUI.MsgStartUpdating, "v2rayN"));
|
|
CheckUpdateAsync(ECoreType.v2rayN, preRelease);
|
|
}
|
|
|
|
|
|
public void CheckUpdateCore(ECoreType type, Config config, Action<bool, string> update, bool preRelease)
|
|
{
|
|
_config = config;
|
|
_updateFunc = update;
|
|
var url = string.Empty;
|
|
|
|
DownloadHandle downloadHandle = new();
|
|
downloadHandle.UpdateCompleted += (sender2, args) =>
|
|
{
|
|
if (args.Success)
|
|
{
|
|
_updateFunc(false, ResUI.MsgDownloadV2rayCoreSuccessfully);
|
|
_updateFunc(false, ResUI.MsgUnpacking);
|
|
|
|
try
|
|
{
|
|
_updateFunc(true, url);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_updateFunc(false, ex.Message);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_updateFunc(false, args.Msg);
|
|
}
|
|
};
|
|
downloadHandle.Error += (sender2, args) =>
|
|
{
|
|
_updateFunc(true, args.GetException().Message);
|
|
};
|
|
|
|
AbsoluteCompleted += (sender2, args) =>
|
|
{
|
|
if (args.Success)
|
|
{
|
|
_updateFunc(false, string.Format(ResUI.MsgParsingSuccessfully, "Core"));
|
|
url = args.Msg;
|
|
askToDownload(downloadHandle, url, true);
|
|
}
|
|
else
|
|
{
|
|
Locator.Current.GetService<NoticeHandler>()?.Enqueue(args.Msg);
|
|
_updateFunc(false, args.Msg);
|
|
}
|
|
};
|
|
_updateFunc(false, string.Format(ResUI.MsgStartUpdating, "Core"));
|
|
CheckUpdateAsync(type, preRelease);
|
|
}
|
|
|
|
|
|
public void UpdateSubscriptionProcess(Config config, string subId, bool blProxy, Action<bool, string> update)
|
|
{
|
|
_config = config;
|
|
_updateFunc = update;
|
|
|
|
_updateFunc(false, ResUI.MsgUpdateSubscriptionStart);
|
|
var subItem = LazyConfig.Instance.SubItems();
|
|
|
|
if (subItem == null || subItem.Count <= 0)
|
|
{
|
|
_updateFunc(false, ResUI.MsgNoValidSubscription);
|
|
return;
|
|
}
|
|
|
|
Task.Run(async () =>
|
|
{
|
|
foreach (var item in subItem)
|
|
{
|
|
string id = item.id.TrimEx();
|
|
string url = item.url.TrimEx();
|
|
string userAgent = item.userAgent.TrimEx();
|
|
string hashCode = $"{item.remarks}->";
|
|
if (Utils.IsNullOrEmpty(id) || Utils.IsNullOrEmpty(url) || (!Utils.IsNullOrEmpty(subId) && item.id != subId))
|
|
{
|
|
//_updateFunc(false, $"{hashCode}{ResUI.MsgNoValidSubscription}");
|
|
continue;
|
|
}
|
|
if (item.enabled == false)
|
|
{
|
|
_updateFunc(false, $"{hashCode}{ResUI.MsgSkipSubscriptionUpdate}");
|
|
continue;
|
|
}
|
|
|
|
var downloadHandle = new DownloadHandle();
|
|
downloadHandle.Error += (sender2, args) =>
|
|
{
|
|
_updateFunc(false, $"{hashCode}{args.GetException().Message}");
|
|
};
|
|
|
|
_updateFunc(false, $"{hashCode}{ResUI.MsgStartGettingSubscriptions}");
|
|
|
|
//one url
|
|
url = Utils.GetPunycode(url);
|
|
var result = await downloadHandle.TryDownloadString(url, blProxy, userAgent);
|
|
if (blProxy && Utils.IsNullOrEmpty(result))
|
|
{
|
|
result = await downloadHandle.TryDownloadString(url, false, userAgent);
|
|
}
|
|
|
|
//more url
|
|
if (!Utils.IsNullOrEmpty(item.moreUrl.TrimEx()))
|
|
{
|
|
if (!Utils.IsNullOrEmpty(result) && Utils.IsBase64String(result))
|
|
{
|
|
result = Utils.Base64Decode(result);
|
|
}
|
|
|
|
var lstUrl = new List<string>
|
|
{
|
|
item.moreUrl.TrimEx().Split(",")
|
|
};
|
|
foreach (var it in lstUrl)
|
|
{
|
|
var url2 = Utils.GetPunycode(it);
|
|
if (Utils.IsNullOrEmpty(url2))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var result2 = await downloadHandle.TryDownloadString(url2, blProxy, userAgent);
|
|
if (blProxy && Utils.IsNullOrEmpty(result2))
|
|
{
|
|
result2 = await downloadHandle.TryDownloadString(url2, false, userAgent);
|
|
}
|
|
if (!Utils.IsNullOrEmpty(result2))
|
|
{
|
|
if (Utils.IsBase64String(result2))
|
|
{
|
|
result += Utils.Base64Decode(result2);
|
|
}
|
|
else
|
|
{
|
|
result += result2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Utils.IsNullOrEmpty(result))
|
|
{
|
|
_updateFunc(false, $"{hashCode}{ResUI.MsgSubscriptionDecodingFailed}");
|
|
}
|
|
else
|
|
{
|
|
_updateFunc(false, $"{hashCode}{ResUI.MsgGetSubscriptionSuccessfully}");
|
|
if (result!.Length < 99)
|
|
{
|
|
_updateFunc(false, $"{hashCode}{result}");
|
|
}
|
|
|
|
int ret = ConfigHandler.AddBatchServers(ref config, result, id, true);
|
|
if (ret <= 0)
|
|
{
|
|
Utils.SaveLog("FailedImportSubscription");
|
|
Utils.SaveLog(result);
|
|
}
|
|
_updateFunc(false,
|
|
ret > 0
|
|
? $"{hashCode}{ResUI.MsgUpdateSubscriptionEnd}"
|
|
: $"{hashCode}{ResUI.MsgFailedImportSubscription}");
|
|
}
|
|
_updateFunc(false, "-------------------------------------------------------");
|
|
}
|
|
|
|
_updateFunc(true, $"{ResUI.MsgUpdateSubscriptionEnd}");
|
|
|
|
});
|
|
}
|
|
|
|
public void UpdateGeoFile(string geoName, Config config, Action<bool, string> update)
|
|
{
|
|
_config = config;
|
|
_updateFunc = update;
|
|
var url = string.Format(Global.geoUrl, geoName);
|
|
|
|
DownloadHandle downloadHandle = new();
|
|
downloadHandle.UpdateCompleted += (sender2, args) =>
|
|
{
|
|
if (args.Success)
|
|
{
|
|
_updateFunc(false, string.Format(ResUI.MsgDownloadGeoFileSuccessfully, geoName));
|
|
|
|
try
|
|
{
|
|
string fileName = Utils.GetTempPath(Utils.GetDownloadFileName(url));
|
|
if (File.Exists(fileName))
|
|
{
|
|
//Global.coreTypes.ForEach(it =>
|
|
//{
|
|
// string targetPath = Utils.GetBinPath($"{geoName}.dat", (ECoreType)Enum.Parse(typeof(ECoreType), it));
|
|
// File.Copy(fileName, targetPath, true);
|
|
//});
|
|
string targetPath = Utils.GetBinPath($"{geoName}.dat");
|
|
File.Copy(fileName, targetPath, true);
|
|
|
|
File.Delete(fileName);
|
|
//_updateFunc(true, "");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_updateFunc(false, ex.Message);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_updateFunc(false, args.Msg);
|
|
}
|
|
};
|
|
downloadHandle.Error += (sender2, args) =>
|
|
{
|
|
_updateFunc(false, args.GetException().Message);
|
|
};
|
|
askToDownload(downloadHandle, url, false);
|
|
|
|
}
|
|
|
|
public void RunAvailabilityCheck(Action<bool, string> update)
|
|
{
|
|
Task.Run(() =>
|
|
{
|
|
var time = (new DownloadHandle()).RunAvailabilityCheck(null);
|
|
|
|
update(false, string.Format(ResUI.TestMeOutput, time));
|
|
});
|
|
}
|
|
|
|
#region private
|
|
|
|
private async void CheckUpdateAsync(ECoreType type, bool preRelease)
|
|
{
|
|
try
|
|
{
|
|
var coreInfo = LazyConfig.Instance.GetCoreInfo(type);
|
|
string url = coreInfo.coreReleaseApiUrl;
|
|
|
|
var result = await (new DownloadHandle()).DownloadStringAsync(url, true, "");
|
|
if (!Utils.IsNullOrEmpty(result))
|
|
{
|
|
responseHandler(type, result, preRelease);
|
|
}
|
|
else
|
|
{
|
|
Utils.SaveLog("StatusCode error: " + url);
|
|
return;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Utils.SaveLog(ex.Message, ex);
|
|
_updateFunc(false, ex.Message);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取V2RayCore版本
|
|
/// </summary>
|
|
private string getCoreVersion(ECoreType type)
|
|
{
|
|
try
|
|
{
|
|
|
|
var coreInfo = LazyConfig.Instance.GetCoreInfo(type);
|
|
string filePath = string.Empty;
|
|
foreach (string name in coreInfo.coreExes)
|
|
{
|
|
string vName = $"{name}.exe";
|
|
vName = Utils.GetBinPath(vName, coreInfo.coreType);
|
|
if (File.Exists(vName))
|
|
{
|
|
filePath = vName;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!File.Exists(filePath))
|
|
{
|
|
string msg = string.Format(ResUI.NotFoundCore, @"", "", "");
|
|
//ShowMsg(true, msg);
|
|
return "";
|
|
}
|
|
|
|
using Process p = new();
|
|
p.StartInfo.FileName = filePath;
|
|
p.StartInfo.Arguments = coreInfo.versionArg;
|
|
p.StartInfo.WorkingDirectory = Utils.StartupPath();
|
|
p.StartInfo.UseShellExecute = false;
|
|
p.StartInfo.RedirectStandardOutput = true;
|
|
p.StartInfo.CreateNoWindow = true;
|
|
p.StartInfo.StandardOutputEncoding = Encoding.UTF8;
|
|
p.Start();
|
|
p.WaitForExit(5000);
|
|
string echo = p.StandardOutput.ReadToEnd();
|
|
string version = string.Empty;
|
|
switch (type)
|
|
{
|
|
case ECoreType.v2fly:
|
|
case ECoreType.SagerNet:
|
|
case ECoreType.Xray:
|
|
case ECoreType.v2fly_v5:
|
|
version = Regex.Match(echo, $"{coreInfo.match} ([0-9.]+) \\(").Groups[1].Value;
|
|
break;
|
|
case ECoreType.clash:
|
|
case ECoreType.clash_meta:
|
|
version = Regex.Match(echo, $"v[0-9.]+").Groups[0].Value;
|
|
break;
|
|
}
|
|
return version;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Utils.SaveLog(ex.Message, ex);
|
|
_updateFunc(false, ex.Message);
|
|
return "";
|
|
}
|
|
}
|
|
private void responseHandler(ECoreType type, string gitHubReleaseApi, bool preRelease)
|
|
{
|
|
try
|
|
{
|
|
var gitHubReleases = Utils.FromJson<List<GitHubRelease>>(gitHubReleaseApi);
|
|
string version;
|
|
if (preRelease)
|
|
{
|
|
version = gitHubReleases!.First().TagName;
|
|
}
|
|
else
|
|
{
|
|
version = gitHubReleases!.First(r => r.Prerelease == false).TagName;
|
|
}
|
|
var coreInfo = LazyConfig.Instance.GetCoreInfo(type);
|
|
|
|
string curVersion;
|
|
string message;
|
|
string url;
|
|
switch (type)
|
|
{
|
|
case ECoreType.v2fly:
|
|
case ECoreType.SagerNet:
|
|
case ECoreType.Xray:
|
|
case ECoreType.v2fly_v5:
|
|
{
|
|
curVersion = "v" + getCoreVersion(type);
|
|
message = string.Format(ResUI.IsLatestCore, curVersion);
|
|
string osBit = "64";
|
|
switch (RuntimeInformation.ProcessArchitecture)
|
|
{
|
|
case Architecture.Arm64:
|
|
osBit = "arm64-v8a";
|
|
break;
|
|
case Architecture.X86:
|
|
osBit = "32";
|
|
break;
|
|
default:
|
|
osBit = "64";
|
|
break;
|
|
}
|
|
|
|
url = string.Format(coreInfo.coreDownloadUrl64, version, osBit);
|
|
break;
|
|
}
|
|
case ECoreType.clash:
|
|
case ECoreType.clash_meta:
|
|
{
|
|
curVersion = getCoreVersion(type);
|
|
message = string.Format(ResUI.IsLatestCore, curVersion);
|
|
switch (RuntimeInformation.ProcessArchitecture)
|
|
{
|
|
case Architecture.Arm64:
|
|
url = string.Format(coreInfo.coreDownloadUrlArm64, version);
|
|
break;
|
|
case Architecture.X86:
|
|
url = string.Format(coreInfo.coreDownloadUrl32, version);
|
|
break;
|
|
default:
|
|
url = string.Format(coreInfo.coreDownloadUrl64, version);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case ECoreType.v2rayN:
|
|
{
|
|
curVersion = FileVersionInfo.GetVersionInfo(Utils.GetExePath()).FileVersion.ToString();
|
|
message = string.Format(ResUI.IsLatestN, curVersion);
|
|
switch (RuntimeInformation.ProcessArchitecture)
|
|
{
|
|
case Architecture.Arm64:
|
|
url = string.Format(coreInfo.coreDownloadUrlArm64, version);
|
|
break;
|
|
case Architecture.X86:
|
|
url = string.Format(coreInfo.coreDownloadUrl32, version);
|
|
break;
|
|
default:
|
|
url = string.Format(coreInfo.coreDownloadUrl64, version);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
throw new ArgumentException("Type");
|
|
}
|
|
|
|
if (type == ECoreType.v2rayN)
|
|
{
|
|
decimal.TryParse(curVersion, out decimal decCur);
|
|
decimal.TryParse(version, out decimal dec);
|
|
if (decCur >= dec)
|
|
{
|
|
AbsoluteCompleted?.Invoke(this, new ResultEventArgs(false, message));
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (curVersion == version)
|
|
{
|
|
AbsoluteCompleted?.Invoke(this, new ResultEventArgs(false, message));
|
|
return;
|
|
}
|
|
|
|
AbsoluteCompleted?.Invoke(this, new ResultEventArgs(true, url));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Utils.SaveLog(ex.Message, ex);
|
|
_updateFunc(false, ex.Message);
|
|
}
|
|
}
|
|
|
|
private void askToDownload(DownloadHandle downloadHandle, string url, bool blAsk)
|
|
{
|
|
bool blDownload = false;
|
|
if (blAsk)
|
|
{
|
|
if (UI.ShowYesNo(string.Format(ResUI.DownloadYesNo, url)) == MessageBoxResult.Yes)
|
|
{
|
|
blDownload = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
blDownload = true;
|
|
}
|
|
if (blDownload)
|
|
{
|
|
downloadHandle.DownloadFileAsync(url, true, 600);
|
|
}
|
|
}
|
|
#endregion
|
|
}
|
|
}
|