using System; using System.IO; using System.Linq; using System.Security.Cryptography; using System.Text; using System.Text.Json; using System.Text.Json.Serialization.Metadata; using JacobTechEncryption; using JacobTechEncryption.Enums; using Luski.net.Enums; using Luski.net.JsonTypes; using Luski.net.Structures; using File = System.IO.File; namespace Luski.net; public class ServerStorage { public static readonly string[] Directories = new[] { "Info", "Assets", "Channels/Keys", "Keys", "Avatars", "Channels/Icons", "Channels/Messages", "StorageInfo", "Files", "Channels/Profiles" }; private static readonly int[] CantDelete = new[] { (int)StorageDirectory.ServerInfo, (int)StorageDirectory.ChannelKeys, (int)StorageDirectory.ServerKeys, (int)StorageDirectory.StorageInfo }; internal ServerStorage(string Domain) { if (!Directory.Exists(JT)) Directory.CreateDirectory(JT); Location = JT + "/Luski/"; if (!Directory.Exists(Location)) Directory.CreateDirectory(Location); Location += "Storage/"; if (!Directory.Exists(Location)) Directory.CreateDirectory(Location); Location += "Servers/"; if (!Directory.Exists(Location)) Directory.CreateDirectory(Location); Location += Domain.ToLower() + "/"; if (!Directory.Exists(Location)) Directory.CreateDirectory(Location); if (!File.Exists(Location + "storage.json")) File.WriteAllText(Location + "storage.json", JsonSerializer.Serialize(new ServerStorageInfo(),ServerStorageInfoContext.Default.ServerStorageInfo)); RawInfo = JsonSerializer.Deserialize(File.ReadAllText(Location + "storage.json"), ServerStorageInfoContext.Default.ServerStorageInfo)!; CacheMode = RawInfo.CacheMode; StorageID = RawInfo.StorageID; for (int i = 0; i < Directories.Length; i++) { string full = Location; string[] spl = Directories[i].Split('/'); foreach (string d in spl) { full += d + "/"; if (!Directory.Exists(full)) Directory.CreateDirectory(full); } } } internal void setid(long id) { StorageID = id; RawInfo.StorageID = id; File.WriteAllText(Location + "storage.json", JsonSerializer.Serialize(RawInfo,ServerStorageInfoContext.Default.ServerStorageInfo)); } internal ServerStorageInfo RawInfo { get; } public TResult GetJson(StorageDirectory Directory, string Resource, bool CreateOnMissing, JsonTypeInfo JsonInfo) where TResult : new() { string FilePath = GetResourceDirectory(Directory, Resource); if (!File.Exists(FilePath)) { TResult res = new(); File.WriteAllText(FilePath, JsonSerializer.Serialize(res, JsonInfo)); return res; } Stream s = GetResourceStream(FilePath); return JsonSerializer.Deserialize(s, JsonInfo)!; } public void SetJson(StorageDirectory Directory, string Resource, TResult obj, JsonTypeInfo JsonInfo) where TResult : new() { string FilePath = GetResourceDirectory(Directory, Resource); File.WriteAllText(FilePath, JsonSerializer.Serialize(obj, JsonInfo)); } public byte[] UpdateStorage(byte[] OldPassword) { try { byte[] NewPassword = new byte[100]; byte[] lpk = GetResourceBytes(StorageDirectory.StorageInfo, "lpk"); try { OldPassword = Encryption.AES.Decrypt(OldPassword, lpk); } catch (Exception e) { Console.WriteLine("Attempting to generate a new storage"); try { return GenerateStorage(); } catch { throw new Exception("Failed To Decrypt Local Storage.", e); } } using (RandomNumberGenerator provider = RandomNumberGenerator.Create()) { provider.GetBytes(NewPassword); } void UpdateDir(string Dir) { DirectoryInfo DI = new(Dir); foreach (DirectoryInfo DIR in DI.GetDirectories()) { UpdateDir(DIR.FullName); } foreach (FileInfo FI in DI.GetFiles()) { byte[] raw = Array.Empty(); try { raw = Encryption.AES.Decrypt( File.ReadAllBytes(FI.FullName), OldPassword ); } catch (Exception e) { Console.WriteLine("Failed to decrypt file: '{0}'\nError: {1}", FI.FullName, e.ToString()); FI.Delete(); continue; } try { raw = Encryption.AES.Encrypt(raw, NewPassword ); } catch (Exception e) { Console.WriteLine("Failed to encrypt file data: '{0}'\nError: {1}", FI.FullName, e.ToString()); continue; } try { File.WriteAllBytes(FI.FullName, raw); } catch (Exception e) { Console.WriteLine("Failed to write file data: '{0}'\nError: {1}", FI.FullName, e.ToString()); } } } UpdateDir(GetStorageDirectory(StorageDirectory.ServerKeys)); UpdateDir(GetStorageDirectory(StorageDirectory.ChannelKeys)); for (int i = 0; i < Directories.Length; i++) { string full = Location; string[] spl = Directories[i].Split('/'); if (!RawInfo.DontDelete && !CantDelete.Contains(i)) { try { if (Directory.Exists(full + Directories[i])) Directory.Delete(full + Directories[i], true); } catch { // ignored } } foreach (string d in spl) { full += d + "/"; if (!Directory.Exists(full)) Directory.CreateDirectory(full); } } SetResourceBytes(StorageDirectory.StorageInfo, "lpk", OldPassword); return NewPassword; } catch (Exception e) { Console.WriteLine(e); throw; } } public byte[] GenerateStorage() { byte[] NewPassword = new byte[100]; byte[] OldPassword = new byte[100]; using (RandomNumberGenerator provider = RandomNumberGenerator.Create()) { provider.GetBytes(OldPassword); } using (RandomNumberGenerator provider = RandomNumberGenerator.Create()) { provider.GetBytes(NewPassword); } for (int i = 0; i < Directories.Length; i++) { string full = Location; string[] spl = Directories[i].Split('/'); if (!RawInfo.DontDelete && !CantDelete.Contains(i)) { try { if (Directory.Exists(full + Directories[i])) Directory.Delete(full + Directories[i], true); } catch { // ignored } } foreach (string d in spl) { full += d + "/"; if (!Directory.Exists(full)) Directory.CreateDirectory(full); } } SetResourceBytes(StorageDirectory.StorageInfo, "lpk", OldPassword); return NewPassword; } public long StorageID { get; internal set; } public string Location { get; internal set; } public string GetStorageDirectory(StorageDirectory Directory) { return Location + Directories[(byte)Directory] + "/"; } public string GetResourceDirectory(StorageDirectory Directory, string Resourse) { return Location + Directories[(byte)Directory] + "/" + Resourse; } public string GetResourceKey(StorageDirectory Directory, string Resource, string Key) { return Encoding.UTF8.GetString(Encryption.AES.Decrypt(GetResourceBytes(Directory, Resource), Key)); } public string GetResourceKeyRaw(StorageDirectory Directory, string Resource, byte[] Key) { return Encoding.UTF8.GetString(Encryption.AES.Decrypt(GetResourceBytes(Directory, Resource), Key)); } public LocalKeyInfo GetResourceKey(StorageDirectory Directory, string Resource, byte[] Key) { string str = Encoding.UTF8.GetString(Encryption.AES.Decrypt(GetResourceBytes(Directory, Resource), Key)); string b = str.Split(':')[0]; str = str.Remove(0, b.Length + 1); return new() { Key = str, EncryptionType = (EncryptionType)short.Parse(b) }; } public void SetResourceKey(StorageDirectory Directory, string Resource, LocalKeyInfo Info, byte[] Key) { SetResourceKey(Directory, Resource, Key, $"{(int)Info.EncryptionType}:{Info.Key}"); } public void SetResourceKey(StorageDirectory Directory, string Resource, string Key, string value) { File.WriteAllBytes(GetStorageDirectory(Directory) + Resource, Encryption.AES.Encrypt(Encoding.UTF8.GetBytes(value), Key)); } public void SetResourceKey(StorageDirectory Directory, string Resource, byte[] Key, string value) { File.WriteAllBytes(GetStorageDirectory(Directory) + Resource, Encryption.AES.Encrypt(Encoding.UTF8.GetBytes(value), Key)); } public void SetResourceKey(StorageDirectory Directory, string Resource, byte[] Key, byte[] value) { File.WriteAllBytes(GetStorageDirectory(Directory) + Resource, Encryption.AES.Encrypt(value, Key)); } public byte[] GetResourceBytes(StorageDirectory Directory, string Resource) { return File.ReadAllBytes(Location + Directories[(byte)Directory] + "/" + Resource); } public Stream GetResourceStream(StorageDirectory Directory, string Resource) { return File.OpenRead(Location + Directories[(byte)Directory] + "/" + Resource); byte[] buffer = new byte[16 * 1024]; MemoryStream ms = new(); using (FileStream r = File.OpenRead(Location + Directories[(byte)Directory] + "/" + Resource)) { int readBytes; while ((readBytes = r.Read(buffer, 0, buffer.Length)) > 0) { ms.Write(buffer, 0, readBytes); } } ms.Position = 0; return ms; } public Stream GetResourceStream(string dir) { return File.OpenRead(dir); byte[] buffer = new byte[16 * 1024]; MemoryStream ms = new(); using (FileStream r = File.OpenRead(dir)) { int readBytes; while ((readBytes = r.Read(buffer, 0, buffer.Length)) > 0) { ms.Write(buffer, 0, readBytes); } } ms.Position = 0; return ms; } public void SetResourceBytes(StorageDirectory Directory, string Resource, byte[] data) { File.WriteAllBytes(Location + Directories[(byte)Directory] + "/" + Resource, data); } public CacheMode CacheMode { get; internal set; } internal static string JT { get { string tmp = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "JacobTech"); if (OperatingSystem.IsLinux()) { tmp = Path.Combine(Environment.GetEnvironmentVariable("HOME")!, ".config/"); tmp += "JacobTech"; } return tmp; } } }