Files
Nebula-Auth/NebulaAuth/Model/Storage.cs
T
Давид Чернопятов 9016f96b6a Prepared for 1.5.3 Release.
- Code clean-ups
- Added "Copy File" support in mafile context menu
- Added hotkeys support to "Copy Login" and "Copy File" commands
- Added experimental feature "Ignore Patch Tuesday errors"
- Added MAAC batch control
- Added SessionID client-side generation in MafileSerializer to support mafiles without SessionID (especially from SDA)
- SplashScreen.png was slightly enhanced
2024-10-28 00:14:44 +02:00

306 lines
9.4 KiB
C#

using NebulaAuth.Model.Entities;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SteamLib;
using SteamLib.Account;
using SteamLib.Exceptions;
using SteamLib.SteamMobile;
using SteamLib.Utility.MaFiles;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
namespace NebulaAuth.Model;
public static class Storage
{
public const string MAFILE_F = "maFiles";
public const string REMOVED_F = "maFiles_removed";
public static string MafileFolder { get; } = Path.GetFullPath(MAFILE_F);
public static string RemovedMafileFolder { get; } = Path.GetFullPath(REMOVED_F);
public static ObservableCollection<Mafile> MaFiles { get; } = new();
public static readonly int DuplicateFound;
static Storage()
{
if (Directory.Exists(MafileFolder) == false)
{
Directory.CreateDirectory(MafileFolder);
}
if (Directory.Exists(RemovedMafileFolder) == false)
{
Directory.CreateDirectory(RemovedMafileFolder);
}
var files = Directory.GetFiles(MafileFolder);
var hashNames = new HashSet<string>();
var hashIds = new HashSet<SteamId>();
var comparer = new MafileNameComparer(Settings.Instance.UseAccountNameAsMafileName);
var ordered = files.Order(comparer).ToList();
foreach (var file in ordered)
{
if (Path.GetExtension(file).Equals(".mafile", StringComparison.OrdinalIgnoreCase) == false) continue;
try
{
var data = ReadMafile(file);
if (hashNames.Contains(data.AccountName) || (data.SessionData != null && hashIds.Contains(data.SessionData.SteamId)))
{
DuplicateFound++;
Shell.Logger.Error("Duplicate mafile {file}", Path.GetFileName(file));
continue;
}
hashNames.Add(data.AccountName);
if (data.SessionData != null) hashIds.Add(data.SessionData.SteamId);
MaFiles.Add(data);
}
catch (Exception ex)
{
Shell.Logger.Error(ex, "Can't load mafile {file}", Path.GetFileName(file));
}
}
MaFiles = new ObservableCollection<Mafile>(MaFiles.OrderBy(m => m.AccountName));
}
public static void AddNewMafile(string path, bool overwrite)
{
Mafile data;
try
{
data = ReadMafile(path);
}
catch (Exception ex)
{
Shell.Logger.Warn(ex, "Can't load mafile");
throw new FormatException("File data is not valid", ex);
}
if (string.IsNullOrWhiteSpace(data.AccountName))
throw new FormatException("File data is not valid. Missing AccountName");
try
{
_ = SteamGuardCodeGenerator.GenerateCode(data.SharedSecret);
}
catch (Exception ex)
{
throw new FormatException("Can't generate code on this mafile", ex);
}
if (data.SessionData == null)
throw new SessionInvalidException("File data is not valid. Missing SessionData")
{
Data = { { "mafile", data } }
};
if (overwrite == false && File.Exists(CreatePathForMafile(data)))
{
throw new IOException("File already exist and overwrite is False");
}
SaveMafile(data);
}
public static Mafile ReadMafile(string path)
{
var str = File.ReadAllText(path);
var mafile = MafileSerializer.Deserialize(str, true, out var mafileData);
if (mafileData.IsExtended == false)
throw new FormatException("Mafile is not extended data");
var props = mafileData.UnusedProperties ?? new Dictionary<string, JProperty>();
var proxy = GetPropertyValue<MaProxy>("Proxy", props);
var group = GetPropertyValue<string>("Group", props);
var password = GetPropertyValue<string>("Password", props);
return Mafile.FromMobileDataExtended((MobileDataExtended)mafile, proxy, group, password);
}
public static string SerializeMafile(Mafile data)
{
var props = new Dictionary<string, object?>
{
{nameof(Mafile.Proxy), data.Proxy},
{nameof(Mafile.Group), data.Group},
{nameof(Mafile.Password), data.Password}
};
return SerializeMafile(data, props);
}
public static string SerializeMafile(MobileDataExtended data, Dictionary<string, object?>? properties)
{
if (Settings.Instance.LegacyMode)
{
return MafileSerializer.SerializeLegacy(data, Formatting.Indented, properties);
}
else
{
return MafileSerializer.Serialize(data);
}
}
private static T? GetPropertyValue<T>(string name, Dictionary<string, JProperty> dictionary)
{
if (dictionary.TryGetValue(name, out var prop) == false) return default;
var value = prop.Value;
try
{
return value.ToObject<T>();
}
catch (Exception ex)
{
Shell.Logger.Warn(ex, "Can't deserialize property {name}", name);
return default;
}
}
public static void SaveMafile(Mafile data)
{
var path = CreatePathForMafile(data);
var str = SerializeMafile(data);
File.WriteAllText(Path.GetFullPath(path), str);
var existed = MaFiles.SingleOrDefault(m => m.AccountName == data.AccountName);
if (existed != null)
{
var index = MaFiles.IndexOf(existed);
MaFiles[index] = data;
}
else
{
MaFiles.Add(data);
}
}
public static void UpdateMafile(Mafile data)
{
var path = CreatePathForMafile(data);
var str = SerializeMafile(data);
File.WriteAllText(Path.GetFullPath(path), str);
}
public static void RemoveMafile(Mafile data)
{
var path = CreatePathForMafile(data);
if (File.Exists(path))
{
File.Delete(path);
}
MaFiles.Remove(data);
}
public static void MoveToRemoved(Mafile data)
{
var path = CreatePathForMafile(data);
var copyPath = Path.Combine(REMOVED_F, data.SessionData!.SteamId + ".mafile");
var copyPathCompleted = copyPath;
var i = 0;
while (File.Exists(copyPathCompleted))
{
i++;
copyPathCompleted = copyPath + $" ({i})";
}
File.Copy(path, copyPathCompleted, false);
File.Delete(path);
MaFiles.Remove(data);
}
private static string CreatePathForMafile(Mafile data)
{
string fileName;
if (Settings.Instance.UseAccountNameAsMafileName)
{
fileName = CreateFileNameWithAccountName(data.AccountName);
}
else
{
if(data.SessionData == null)
throw new NullReferenceException("SessionData was null can't retrieve SteamId"); //FIXME: think about better way to handle
fileName = CreateFileNameWithSteamId(data.SessionData.SteamId);
}
return Path.Combine(MafileFolder, fileName);
}
private static string CreateFileNameWithAccountName(string accountName) => accountName + ".mafile";
private static string CreateFileNameWithSteamId(SteamId steamId) => steamId.Steam64.Id + ".mafile";
public static string? TryFindMafilePath(Mafile data)
//FIXME: write mode to mafile instead of searching it. Search sometimes represents not actual mafile (in case of duplicate steamId + accountName)
{
var pathFileName = Path.Combine(MafileFolder, CreateFileNameWithAccountName(data.AccountName));
string? pathSteamId = null;
if (data.SessionData != null)
{
pathSteamId = Path.Combine(MafileFolder, CreateFileNameWithSteamId(data.SessionData.SteamId));
}
var steamIdExist = pathSteamId != null && File.Exists(pathSteamId);
var accountNameExist = File.Exists(pathFileName);
if (steamIdExist && accountNameExist)
{
return Settings.Instance.UseAccountNameAsMafileName ? pathFileName : pathSteamId;
}
if (steamIdExist ^ accountNameExist)
{
return steamIdExist ? pathSteamId : pathFileName;
}
return null;
}
public static bool ValidateCanSave(Mafile data)
{
return Settings.Instance.UseAccountNameAsMafileName || data.SessionData != null;
}
}
internal class MafileNameComparer : IComparer<string>
{
public bool MafileNameMode { get; }
private const string MAF_64_START = "765";
private static readonly IComparer<string> DefaultComparer = Comparer<string>.Default;
public MafileNameComparer(bool mafileNameMode)
{
MafileNameMode = mafileNameMode;
}
public int Compare(string? x, string? y)
{
if (x == null && y == null) return 0;
if (x == null) return -1;
if (y == null) return 1;
bool xisSteamId = Path.GetFileName(x).StartsWith(MAF_64_START);
bool yisSteamId = Path.GetFileName(y).StartsWith(MAF_64_START);
if (xisSteamId ^ yisSteamId)
{
if (MafileNameMode)
{
return xisSteamId ? 1 : -1;
}
else
{
return yisSteamId ? 1 : -1;
}
}
return DefaultComparer.Compare(x, y);
}
}