2022-08-15 23:55:44 +08:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.IO;
|
|
|
|
|
|
using System.Runtime.Serialization.Formatters.Binary;
|
|
|
|
|
|
using System.Timers;
|
2022-10-05 15:02:30 +08:00
|
|
|
|
using static MinecraftClient.Settings;
|
|
|
|
|
|
using static MinecraftClient.Settings.MainConfigHealper.MainConfig.AdvancedConfig;
|
2022-08-15 23:55:44 +08:00
|
|
|
|
|
2022-12-06 15:50:17 +08:00
|
|
|
|
namespace MinecraftClient.Protocol.ProfileKey
|
2022-08-15 23:55:44 +08:00
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Handle keys caching and storage.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public static class KeysCache
|
|
|
|
|
|
{
|
|
|
|
|
|
private const string KeysCacheFilePlaintext = "ProfileKeyCache.ini";
|
|
|
|
|
|
|
2022-10-02 18:31:08 +08:00
|
|
|
|
private static FileMonitor? cachemonitor;
|
|
|
|
|
|
private static readonly Dictionary<string, PlayerKeyPair> keys = new();
|
|
|
|
|
|
private static readonly Timer updatetimer = new(100);
|
|
|
|
|
|
private static readonly List<KeyValuePair<string, PlayerKeyPair>> pendingadds = new();
|
|
|
|
|
|
private static readonly BinaryFormatter formatter = new();
|
2022-08-15 23:55:44 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Retrieve whether KeysCache contains a keys for the given login.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="login">User login used with Minecraft.net</param>
|
|
|
|
|
|
/// <returns>TRUE if keys are available</returns>
|
|
|
|
|
|
public static bool Contains(string login)
|
|
|
|
|
|
{
|
|
|
|
|
|
return keys.ContainsKey(login);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Store keys and save it to disk if required.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="login">User login used with Minecraft.net</param>
|
|
|
|
|
|
/// <param name="playerKeyPair">User keys</param>
|
|
|
|
|
|
public static void Store(string login, PlayerKeyPair playerKeyPair)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (Contains(login))
|
|
|
|
|
|
{
|
|
|
|
|
|
keys[login] = playerKeyPair;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
keys.Add(login, playerKeyPair);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-10-05 15:02:30 +08:00
|
|
|
|
if (Config.Main.Advanced.ProfileKeyCache == CacheType.disk && updatetimer.Enabled == true)
|
2022-08-15 23:55:44 +08:00
|
|
|
|
{
|
|
|
|
|
|
pendingadds.Add(new KeyValuePair<string, PlayerKeyPair>(login, playerKeyPair));
|
|
|
|
|
|
}
|
2022-10-05 15:02:30 +08:00
|
|
|
|
else if (Config.Main.Advanced.ProfileKeyCache == CacheType.disk)
|
2022-08-15 23:55:44 +08:00
|
|
|
|
{
|
|
|
|
|
|
SaveToDisk();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Retrieve keys for the given login.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="login">User login used with Minecraft.net</param>
|
|
|
|
|
|
/// <returns>PlayerKeyPair for given login</returns>
|
|
|
|
|
|
public static PlayerKeyPair Get(string login)
|
|
|
|
|
|
{
|
|
|
|
|
|
return keys[login];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Initialize cache monitoring to keep cache updated with external changes.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>TRUE if keys are seeded from file</returns>
|
|
|
|
|
|
public static bool InitializeDiskCache()
|
|
|
|
|
|
{
|
|
|
|
|
|
cachemonitor = new FileMonitor(AppDomain.CurrentDomain.BaseDirectory, KeysCacheFilePlaintext, new FileSystemEventHandler(OnChanged));
|
|
|
|
|
|
updatetimer.Elapsed += HandlePending;
|
|
|
|
|
|
return LoadFromDisk();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Reloads cache on external cache file change.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="sender">Sender</param>
|
|
|
|
|
|
/// <param name="e">Event data</param>
|
|
|
|
|
|
private static void OnChanged(object sender, FileSystemEventArgs e)
|
|
|
|
|
|
{
|
|
|
|
|
|
updatetimer.Stop();
|
|
|
|
|
|
updatetimer.Start();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Called after timer elapsed. Reads disk cache and adds new/modified keys back.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="sender">Sender</param>
|
|
|
|
|
|
/// <param name="e">Event data</param>
|
2022-10-02 18:31:08 +08:00
|
|
|
|
private static void HandlePending(object? sender, ElapsedEventArgs e)
|
2022-08-15 23:55:44 +08:00
|
|
|
|
{
|
|
|
|
|
|
updatetimer.Stop();
|
|
|
|
|
|
LoadFromDisk();
|
|
|
|
|
|
|
|
|
|
|
|
foreach (KeyValuePair<string, PlayerKeyPair> pending in pendingadds.ToArray())
|
|
|
|
|
|
{
|
|
|
|
|
|
Store(pending.Key, pending.Value);
|
|
|
|
|
|
pendingadds.Remove(pending);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Reads cache file and loads KeysInfos into KeysCache.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>True if data is successfully loaded</returns>
|
|
|
|
|
|
private static bool LoadFromDisk()
|
|
|
|
|
|
{
|
|
|
|
|
|
//User-editable keys cache file in text format
|
|
|
|
|
|
if (File.Exists(KeysCacheFilePlaintext))
|
|
|
|
|
|
{
|
2022-12-06 15:50:17 +08:00
|
|
|
|
if (Config.Logging.DebugMessages)
|
2022-10-28 11:13:20 +08:00
|
|
|
|
ConsoleIO.WriteLineFormatted(string.Format(Translations.cache_loading_keys, KeysCacheFilePlaintext));
|
2022-08-15 23:55:44 +08:00
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (string line in FileMonitor.ReadAllLinesWithRetries(KeysCacheFilePlaintext))
|
|
|
|
|
|
{
|
2022-08-25 14:36:15 +08:00
|
|
|
|
if (!line.TrimStart().StartsWith("#"))
|
2022-08-15 23:55:44 +08:00
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
int separatorIdx = line.IndexOf('=');
|
|
|
|
|
|
if (separatorIdx >= 1 && line.Length > separatorIdx + 1)
|
|
|
|
|
|
{
|
2022-08-25 14:36:15 +08:00
|
|
|
|
string login = line[..separatorIdx];
|
|
|
|
|
|
string value = line[(separatorIdx + 1)..];
|
2022-08-15 23:55:44 +08:00
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
PlayerKeyPair playerKeyPair = PlayerKeyPair.FromString(value);
|
|
|
|
|
|
keys[login] = playerKeyPair;
|
2022-12-06 15:50:17 +08:00
|
|
|
|
if (Config.Logging.DebugMessages)
|
2022-10-28 11:13:20 +08:00
|
|
|
|
ConsoleIO.WriteLineFormatted(string.Format(Translations.cache_loaded_keys, playerKeyPair.ExpiresAt.ToString()));
|
2022-08-15 23:55:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
catch (InvalidDataException e)
|
|
|
|
|
|
{
|
2022-12-06 15:50:17 +08:00
|
|
|
|
if (Config.Logging.DebugMessages)
|
2022-10-28 11:13:20 +08:00
|
|
|
|
ConsoleIO.WriteLineFormatted(string.Format(Translations.cache_ignore_string_keys, value, e.Message));
|
2022-08-15 23:55:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
catch (FormatException e)
|
|
|
|
|
|
{
|
2022-12-06 15:50:17 +08:00
|
|
|
|
if (Config.Logging.DebugMessages)
|
2022-10-28 11:13:20 +08:00
|
|
|
|
ConsoleIO.WriteLineFormatted(string.Format(Translations.cache_ignore_string_keys, value, e.Message));
|
2022-08-15 23:55:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
catch (ArgumentNullException e)
|
|
|
|
|
|
{
|
2022-12-06 15:50:17 +08:00
|
|
|
|
if (Config.Logging.DebugMessages)
|
2022-10-28 11:13:20 +08:00
|
|
|
|
ConsoleIO.WriteLineFormatted(string.Format(Translations.cache_ignore_string_keys, value, e.Message));
|
2022-08-15 23:55:44 +08:00
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2022-12-06 15:50:17 +08:00
|
|
|
|
else if (Config.Logging.DebugMessages)
|
2022-08-15 23:55:44 +08:00
|
|
|
|
{
|
2022-10-28 11:13:20 +08:00
|
|
|
|
ConsoleIO.WriteLineFormatted(string.Format(Translations.cache_ignore_line_keys, line));
|
2022-08-15 23:55:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (IOException e)
|
|
|
|
|
|
{
|
2022-11-30 16:08:55 +08:00
|
|
|
|
ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.cache_read_fail_plain_keys, e.Message));
|
2022-08-15 23:55:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return keys.Count > 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Saves player's keypair from KeysCache into cache file.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private static void SaveToDisk()
|
|
|
|
|
|
{
|
2022-10-05 15:02:30 +08:00
|
|
|
|
if (Config.Logging.DebugMessages)
|
2022-11-30 16:08:55 +08:00
|
|
|
|
ConsoleIO.WriteLineFormatted("§8" + Translations.cache_saving_keys, acceptnewlines: true);
|
2022-08-15 23:55:44 +08:00
|
|
|
|
|
2022-10-02 18:31:08 +08:00
|
|
|
|
List<string> KeysCacheLines = new()
|
|
|
|
|
|
{
|
|
|
|
|
|
"# Generated by MCC v" + Program.Version + " - Keep it secret & Edit at own risk!",
|
|
|
|
|
|
"# ProfileKey=PublicKey(base64),PublicKeySignature(base64),PublicKeySignatureV2(base64),PrivateKey(base64),ExpiresAt,RefreshAfter"
|
|
|
|
|
|
};
|
2022-08-15 23:55:44 +08:00
|
|
|
|
foreach (KeyValuePair<string, PlayerKeyPair> entry in keys)
|
|
|
|
|
|
KeysCacheLines.Add(entry.Key + '=' + entry.Value.ToString());
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
FileMonitor.WriteAllLinesWithRetries(KeysCacheFilePlaintext, KeysCacheLines);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (IOException e)
|
|
|
|
|
|
{
|
2022-11-30 16:08:55 +08:00
|
|
|
|
ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.cache_save_fail_keys, e.Message));
|
2022-08-15 23:55:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|