using System; using System.Collections.Generic; using System.IO; using System.Runtime.Serialization.Formatters.Binary; using System.Timers; using MinecraftClient.Protocol.Session; namespace MinecraftClient.Protocol.Keys { /// /// Handle keys caching and storage. /// public static class KeysCache { private const string KeysCacheFilePlaintext = "ProfileKeyCache.ini"; private static FileMonitor cachemonitor; private static Dictionary keys = new Dictionary(); private static Timer updatetimer = new Timer(100); private static List> pendingadds = new List>(); private static BinaryFormatter formatter = new BinaryFormatter(); /// /// Retrieve whether KeysCache contains a keys for the given login. /// /// User login used with Minecraft.net /// TRUE if keys are available public static bool Contains(string login) { return keys.ContainsKey(login); } /// /// Store keys and save it to disk if required. /// /// User login used with Minecraft.net /// User keys public static void Store(string login, PlayerKeyPair playerKeyPair) { if (Contains(login)) { keys[login] = playerKeyPair; } else { keys.Add(login, playerKeyPair); } if (Settings.ProfileKeyCaching == CacheType.Disk && updatetimer.Enabled == true) { pendingadds.Add(new KeyValuePair(login, playerKeyPair)); } else if (Settings.ProfileKeyCaching == CacheType.Disk) { SaveToDisk(); } } /// /// Retrieve keys for the given login. /// /// User login used with Minecraft.net /// PlayerKeyPair for given login public static PlayerKeyPair Get(string login) { return keys[login]; } /// /// Initialize cache monitoring to keep cache updated with external changes. /// /// TRUE if keys are seeded from file public static bool InitializeDiskCache() { cachemonitor = new FileMonitor(AppDomain.CurrentDomain.BaseDirectory, KeysCacheFilePlaintext, new FileSystemEventHandler(OnChanged)); updatetimer.Elapsed += HandlePending; return LoadFromDisk(); } /// /// Reloads cache on external cache file change. /// /// Sender /// Event data private static void OnChanged(object sender, FileSystemEventArgs e) { updatetimer.Stop(); updatetimer.Start(); } /// /// Called after timer elapsed. Reads disk cache and adds new/modified keys back. /// /// Sender /// Event data private static void HandlePending(object sender, ElapsedEventArgs e) { updatetimer.Stop(); LoadFromDisk(); foreach (KeyValuePair pending in pendingadds.ToArray()) { Store(pending.Key, pending.Value); pendingadds.Remove(pending); } } /// /// Reads cache file and loads KeysInfos into KeysCache. /// /// True if data is successfully loaded private static bool LoadFromDisk() { //User-editable keys cache file in text format if (File.Exists(KeysCacheFilePlaintext)) { if (Settings.DebugMessages) ConsoleIO.WriteLineFormatted(Translations.Get("cache.loading_keys", KeysCacheFilePlaintext)); try { foreach (string line in FileMonitor.ReadAllLinesWithRetries(KeysCacheFilePlaintext)) { if (!line.Trim().StartsWith("#")) { int separatorIdx = line.IndexOf('='); if (separatorIdx >= 1 && line.Length > separatorIdx + 1) { string login = line.Substring(0, separatorIdx); string value = line.Substring(separatorIdx + 1); try { PlayerKeyPair playerKeyPair = PlayerKeyPair.FromString(value); keys[login] = playerKeyPair; if (Settings.DebugMessages) ConsoleIO.WriteLineFormatted(Translations.Get("cache.loaded_keys", playerKeyPair.ExpiresAt.ToString())); } catch (InvalidDataException e) { if (Settings.DebugMessages) ConsoleIO.WriteLineFormatted(Translations.Get("cache.ignore_string_keys", value, e.Message)); } catch (FormatException e) { if (Settings.DebugMessages) ConsoleIO.WriteLineFormatted(Translations.Get("cache.ignore_string_keys", value, e.Message)); } catch (ArgumentNullException e) { if (Settings.DebugMessages) ConsoleIO.WriteLineFormatted(Translations.Get("cache.ignore_string_keys", value, e.Message)); } } else if (Settings.DebugMessages) { ConsoleIO.WriteLineFormatted(Translations.Get("cache.ignore_line_keys", line)); } } } } catch (IOException e) { ConsoleIO.WriteLineFormatted(Translations.Get("cache.read_fail_plain_keys", e.Message)); } } return keys.Count > 0; } /// /// Saves player's keypair from KeysCache into cache file. /// private static void SaveToDisk() { if (Settings.DebugMessages) Translations.WriteLineFormatted("cache.saving_keys"); List KeysCacheLines = new List(); KeysCacheLines.Add("# Generated by MCC v" + Program.Version + " - Keep it secret & Edit at own risk!"); KeysCacheLines.Add("# ProfileKey=PublicKey(base64),PublicKeySignature(base64),PublicKeySignatureV2(base64),PrivateKey(base64),ExpiresAt,RefreshAfter"); foreach (KeyValuePair entry in keys) KeysCacheLines.Add(entry.Key + '=' + entry.Value.ToString()); try { FileMonitor.WriteAllLinesWithRetries(KeysCacheFilePlaintext, KeysCacheLines); } catch (IOException e) { ConsoleIO.WriteLineFormatted(Translations.Get("cache.save_fail_keys", e.Message)); } } } }