From 2eb1abe526910ac69771675ad7f645cd6694841c Mon Sep 17 00:00:00 2001 From: JacobTech Date: Sun, 1 Jan 2023 22:50:39 -0500 Subject: [PATCH 01/14] init --- Luski.net.sln | 31 ++ Luski.net/Encryption.cs | 515 ++++++++++++++++++ Luski.net/Enums/Branch.cs | 8 + Luski.net/Enums/ChannelType.cs | 7 + Luski.net/Enums/DataType.cs | 18 + Luski.net/Enums/ErrorCode.cs | 14 + Luski.net/Enums/FriendStatus.cs | 9 + Luski.net/Enums/PictureType.cs | 13 + Luski.net/Enums/UserFlag.cs | 11 + Luski.net/Enums/UserStatus.cs | 10 + Luski.net/Exceptions.cs | 30 + Luski.net/Interfaces/IAudioClient.cs | 48 ++ Luski.net/Interfaces/IChannel.cs | 32 ++ Luski.net/Interfaces/IUser.cs | 45 ++ Luski.net/JsonRequest.cs | 28 + Luski.net/JsonTypes/BaseTypes/HTTPRequest.cs | 11 + Luski.net/JsonTypes/BaseTypes/IncomingHTTP.cs | 32 ++ Luski.net/JsonTypes/BaseTypes/IncomingWSS.cs | 24 + .../JsonTypes/BaseTypes/SocketUserBase.cs | 118 ++++ Luski.net/JsonTypes/File.cs | 107 ++++ Luski.net/JsonTypes/FriendRequestResult.cs | 27 + Luski.net/JsonTypes/HTTP/Channel.cs | 21 + Luski.net/JsonTypes/HTTP/FriendRequest.cs | 30 + .../JsonTypes/HTTP/FriendRequestResultOut.cs | 24 + Luski.net/JsonTypes/HTTP/Message.cs | 27 + Luski.net/JsonTypes/HTTP/Status.cs | 22 + Luski.net/JsonTypes/KeyExchange.cs | 10 + Luski.net/JsonTypes/Login.cs | 20 + Luski.net/JsonTypes/OfflineKeyData.cs | 19 + Luski.net/JsonTypes/SocketAppUser.cs | 178 ++++++ Luski.net/JsonTypes/SocketBulkMessage.cs | 23 + Luski.net/JsonTypes/SocketChannel.cs | 173 ++++++ Luski.net/JsonTypes/SocketDMChannel.cs | 33 ++ Luski.net/JsonTypes/SocketGroupChannel.cs | 20 + Luski.net/JsonTypes/SocketMessage.cs | 87 +++ Luski.net/JsonTypes/SocketRemoteUser.cs | 35 ++ Luski.net/JsonTypes/SocketTextChannel.cs | 168 ++++++ Luski.net/JsonTypes/StatusUpdate.cs | 17 + Luski.net/JsonTypes/WSS/WSSKeyExchange.cs | 31 ++ Luski.net/JsonTypes/WSS/WSSLogin.cs | 25 + Luski.net/Luski.net.csproj | 33 ++ Luski.net/Luski.net.csproj.user | 6 + .../PublishProfiles/FolderProfile.pubxml | 13 + .../PublishProfiles/FolderProfile.pubxml.user | 10 + Luski.net/Server.Cleanup.cs | 17 + Luski.net/Server.Constructors.cs | 129 +++++ Luski.net/Server.CreateAccount.cs | 93 ++++ Luski.net/Server.Events.cs | 21 + Luski.net/Server.Globals.cs | 83 +++ Luski.net/Server.Incoming.cs | 118 ++++ Luski.net/Server.Login.cs | 12 + Luski.net/Server.cs | 298 ++++++++++ Luski.net/Sockets/SocketAudioClient.cs | 388 +++++++++++++ Luski.net/Sound/Devices.cs | 58 ++ Luski.net/Sound/JitterBuffer.cs | 124 +++++ Luski.net/Sound/Player.cs | 417 ++++++++++++++ Luski.net/Sound/Protocol.cs | 95 ++++ Luski.net/Sound/RTPPacket.cs | 139 +++++ Luski.net/Sound/Recorder.cs | 340 ++++++++++++ Luski.net/Sound/Timer.cs | 64 +++ Luski.net/Sound/Utils.cs | 195 +++++++ Luski.net/Sound/WaveFile.cs | 104 ++++ Luski.net/Sound/Win32.cs | 241 ++++++++ Luski.net/Sound/WinSound.cs | 94 ++++ 64 files changed, 5193 insertions(+) create mode 100755 Luski.net.sln create mode 100755 Luski.net/Encryption.cs create mode 100755 Luski.net/Enums/Branch.cs create mode 100755 Luski.net/Enums/ChannelType.cs create mode 100755 Luski.net/Enums/DataType.cs create mode 100755 Luski.net/Enums/ErrorCode.cs create mode 100755 Luski.net/Enums/FriendStatus.cs create mode 100755 Luski.net/Enums/PictureType.cs create mode 100755 Luski.net/Enums/UserFlag.cs create mode 100755 Luski.net/Enums/UserStatus.cs create mode 100755 Luski.net/Exceptions.cs create mode 100755 Luski.net/Interfaces/IAudioClient.cs create mode 100755 Luski.net/Interfaces/IChannel.cs create mode 100755 Luski.net/Interfaces/IUser.cs create mode 100755 Luski.net/JsonRequest.cs create mode 100755 Luski.net/JsonTypes/BaseTypes/HTTPRequest.cs create mode 100755 Luski.net/JsonTypes/BaseTypes/IncomingHTTP.cs create mode 100755 Luski.net/JsonTypes/BaseTypes/IncomingWSS.cs create mode 100755 Luski.net/JsonTypes/BaseTypes/SocketUserBase.cs create mode 100755 Luski.net/JsonTypes/File.cs create mode 100755 Luski.net/JsonTypes/FriendRequestResult.cs create mode 100755 Luski.net/JsonTypes/HTTP/Channel.cs create mode 100755 Luski.net/JsonTypes/HTTP/FriendRequest.cs create mode 100755 Luski.net/JsonTypes/HTTP/FriendRequestResultOut.cs create mode 100755 Luski.net/JsonTypes/HTTP/Message.cs create mode 100755 Luski.net/JsonTypes/HTTP/Status.cs create mode 100755 Luski.net/JsonTypes/KeyExchange.cs create mode 100755 Luski.net/JsonTypes/Login.cs create mode 100755 Luski.net/JsonTypes/OfflineKeyData.cs create mode 100755 Luski.net/JsonTypes/SocketAppUser.cs create mode 100755 Luski.net/JsonTypes/SocketBulkMessage.cs create mode 100755 Luski.net/JsonTypes/SocketChannel.cs create mode 100755 Luski.net/JsonTypes/SocketDMChannel.cs create mode 100755 Luski.net/JsonTypes/SocketGroupChannel.cs create mode 100755 Luski.net/JsonTypes/SocketMessage.cs create mode 100755 Luski.net/JsonTypes/SocketRemoteUser.cs create mode 100755 Luski.net/JsonTypes/SocketTextChannel.cs create mode 100755 Luski.net/JsonTypes/StatusUpdate.cs create mode 100755 Luski.net/JsonTypes/WSS/WSSKeyExchange.cs create mode 100755 Luski.net/JsonTypes/WSS/WSSLogin.cs create mode 100755 Luski.net/Luski.net.csproj create mode 100755 Luski.net/Luski.net.csproj.user create mode 100755 Luski.net/Properties/PublishProfiles/FolderProfile.pubxml create mode 100755 Luski.net/Properties/PublishProfiles/FolderProfile.pubxml.user create mode 100755 Luski.net/Server.Cleanup.cs create mode 100755 Luski.net/Server.Constructors.cs create mode 100755 Luski.net/Server.CreateAccount.cs create mode 100755 Luski.net/Server.Events.cs create mode 100755 Luski.net/Server.Globals.cs create mode 100755 Luski.net/Server.Incoming.cs create mode 100755 Luski.net/Server.Login.cs create mode 100755 Luski.net/Server.cs create mode 100755 Luski.net/Sockets/SocketAudioClient.cs create mode 100755 Luski.net/Sound/Devices.cs create mode 100755 Luski.net/Sound/JitterBuffer.cs create mode 100755 Luski.net/Sound/Player.cs create mode 100755 Luski.net/Sound/Protocol.cs create mode 100755 Luski.net/Sound/RTPPacket.cs create mode 100755 Luski.net/Sound/Recorder.cs create mode 100755 Luski.net/Sound/Timer.cs create mode 100755 Luski.net/Sound/Utils.cs create mode 100755 Luski.net/Sound/WaveFile.cs create mode 100755 Luski.net/Sound/Win32.cs create mode 100755 Luski.net/Sound/WinSound.cs diff --git a/Luski.net.sln b/Luski.net.sln new file mode 100755 index 0000000..f68549b --- /dev/null +++ b/Luski.net.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31717.71 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Luski.net", "Luski.net\Luski.net.csproj", "{3DF9B870-51B3-4338-84EC-75E4B8802F0C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Luski.net.tests", "Luski.net.tests\Luski.net.tests.csproj", "{FCA149C8-379B-454A-962A-856F30965C4E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3DF9B870-51B3-4338-84EC-75E4B8802F0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3DF9B870-51B3-4338-84EC-75E4B8802F0C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3DF9B870-51B3-4338-84EC-75E4B8802F0C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3DF9B870-51B3-4338-84EC-75E4B8802F0C}.Release|Any CPU.Build.0 = Release|Any CPU + {FCA149C8-379B-454A-962A-856F30965C4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FCA149C8-379B-454A-962A-856F30965C4E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FCA149C8-379B-454A-962A-856F30965C4E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FCA149C8-379B-454A-962A-856F30965C4E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {49AFEA24-10EC-4D2C-B99C-C3E70124E443} + EndGlobalSection +EndGlobal diff --git a/Luski.net/Encryption.cs b/Luski.net/Encryption.cs new file mode 100755 index 0000000..8d62272 --- /dev/null +++ b/Luski.net/Encryption.cs @@ -0,0 +1,515 @@ +using Luski.net.Enums; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Security.Cryptography; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; + +namespace Luski.net +{ + public static class Encryption + { + internal static string? MyPublicKey; + internal static readonly UnicodeEncoding Encoder = new(); + private static string? myPrivateKey; + internal static bool Generating = false; + internal static bool Generated = false; + private static string? _serverpublickey = null; + internal static string? ofkey = null; + internal static string? outofkey = null; + internal static string pw = ""; + public static int NewKeySize = 4096; + internal static string ServerPublicKey + { + get + { + if (_serverpublickey is null) _serverpublickey = new HttpClient().GetAsync($"https://{Server.InternalDomain}/{Server.API_Ver}/Keys/PublicKey").Result.Content.ReadAsStringAsync().Result; + return _serverpublickey; + } + } + + public static void GenerateKeys() + { + if (!Generating) + { + Generating = true; + GenerateNewKeys(out MyPublicKey, out myPrivateKey); + GenerateNewKeys(out outofkey, out ofkey); + Generated = true; + } + } + + internal static void GenerateNewKeys(out string Public, out string Private) + { + using RSACryptoServiceProvider r = new(NewKeySize); + Private = r.ToXmlString(true); + Public = r.ToXmlString(false); + return; + } + + public static class File + { + internal static void SetOfflineKey(string key) + { + MakeFile(Server.GetKeyFilePath, pw); + LuskiDataFile? fileLayout = JsonSerializer.Deserialize(FileString(Server.GetKeyFilePath, pw)); + fileLayout.OfflineKey = key; + fileLayout.Save(Server.GetKeyFilePath, pw); + } + + internal static string? GetOfflineKey() + { + MakeFile(Server.GetKeyFilePath, pw); + LuskiDataFile? fileLayout = JsonSerializer.Deserialize(FileString(Server.GetKeyFilePath, pw)); + return fileLayout?.OfflineKey; + } + + private static string FileString(string path, string password) + { + byte[] passwordBytes = Encoding.UTF8.GetBytes(password); + byte[] salt = new byte[100]; + FileStream fsCrypt = new(path, FileMode.Open); + fsCrypt.Read(salt, 0, salt.Length); + RijndaelManaged AES = new() + { + KeySize = 256, + BlockSize = 128 + }; + Rfc2898DeriveBytes key = new(passwordBytes, salt, 50000); + AES.Key = key.GetBytes(AES.KeySize / 8); + AES.IV = key.GetBytes(AES.BlockSize / 8); + AES.Padding = PaddingMode.PKCS7; + AES.Mode = CipherMode.CFB; + CryptoStream cs = new(fsCrypt, AES.CreateDecryptor(), CryptoStreamMode.Read); + MemoryStream fsOut = new(); + int read; + byte[] buffer = new byte[1048576]; + try + { + while ((read = cs.Read(buffer, 0, buffer.Length)) > 0) + { + fsOut.Write(buffer, 0, read); + } + } + catch (CryptographicException ex_CryptographicException) + { + Console.WriteLine("CryptographicException error: " + ex_CryptographicException.Message); + } + catch (Exception ex) + { + Console.WriteLine("Error: " + ex.Message); + } + fsOut.Seek(0, SeekOrigin.Begin); + using BinaryReader reader = new(fsOut); + byte[] by = reader.ReadBytes((int)fsOut.Length); + fsOut.Close(); + fsCrypt.Close(); + return Encoding.UTF8.GetString(by); + } + + public static class Channels + { + public static string GetKey(long channel) + { + LuskiDataFile? fileLayout; + IEnumerable? lis; + try + { +#pragma warning disable CS8603 // Possible null reference return. + if (channel == 0) return myPrivateKey; +#pragma warning restore CS8603 // Possible null reference return. + MakeFile(Server.GetKeyFilePath, pw); + fileLayout = JsonSerializer.Deserialize(FileString(Server.GetKeyFilePath, pw)); + lis = fileLayout?.channels?.Where(s => s.id == channel); + if (lis?.Count() > 0) + { + return lis.First().key; + } + foreach (Branch b in (Branch[])Enum.GetValues(typeof(Branch))) + { + if (b != Server.Branch) + { + try + { + string temp = GetKeyBranch(channel, b); + if (temp is not null) + { + AddKey(channel, temp); + return temp; + } + } + catch + { + + } + } + } + throw new Exception("You dont have a key for that channel"); + } + finally + { + fileLayout = null; + lis = null; + } + } + + internal static string GetKeyBranch(long channel, Branch branch) + { + LuskiDataFile? fileLayout; + IEnumerable? lis; + try + { +#pragma warning disable CS8603 // Possible null reference return. + if (channel == 0) return myPrivateKey; +#pragma warning restore CS8603 // Possible null reference return. + MakeFile(Server.GetKeyFilePathBr(branch.ToString()), pw); + fileLayout = JsonSerializer.Deserialize(FileString(Server.GetKeyFilePathBr(branch.ToString()), pw)); + lis = fileLayout?.channels?.Where(s => s.id == channel); + if (lis?.Count() > 0) + { + return lis.First().key; + } + throw new Exception("You dont have a key for that channel"); + } + finally + { + fileLayout = null; + lis = null; + } + } + + public static void AddKey(long channel, string key) + { + MakeFile(Server.GetKeyFilePath, pw); + LuskiDataFile? fileLayout = JsonSerializer.Deserialize(FileString(Server.GetKeyFilePath, pw)); + fileLayout?.Addchannelkey(channel, key); + fileLayout?.Save(Server.GetKeyFilePath, pw); + } + } + + private static void MakeFile(string dir, string password) + { + if (!System.IO.File.Exists(dir)) + { + LuskiDataFile? l = JsonSerializer.Deserialize("{\"channels\":[]}"); + l?.Save(dir, password); + } + } + + public class LuskiDataFile + { + public static LuskiDataFile GetDataFile(string path, string password) + { + MakeFile(path, password); + return JsonSerializer.Deserialize(FileString(path, password)); + } + + internal static LuskiDataFile GetDefualtDataFile() + { + return GetDataFile(Server.GetKeyFilePath, pw); + } + + public ChannelLayout[]? channels { get; set; } = default!; + + public string? OfflineKey { get; set; } = default!; + + public void Save(string file, string password) + { + byte[] salt = new byte[100]; + RandomNumberGenerator? provider = RandomNumberGenerator.Create(); + provider.GetBytes(salt); + FileStream fsCrypt = new(file, FileMode.Create); + byte[] passwordBytes = Encoding.UTF8.GetBytes(password); + RijndaelManaged AES = new() + { + KeySize = 256, + BlockSize = 128, + Padding = PaddingMode.PKCS7 + }; + Rfc2898DeriveBytes key = new(passwordBytes, salt, 50000); + AES.Key = key.GetBytes(AES.KeySize / 8); + AES.IV = key.GetBytes(AES.BlockSize / 8); + AES.Mode = CipherMode.CFB; + fsCrypt.Write(salt, 0, salt.Length); + CryptoStream cs = new(fsCrypt, AES.CreateEncryptor(), CryptoStreamMode.Write); + string tempp = JsonSerializer.Serialize(this); + MemoryStream fsIn = new(Encoding.UTF8.GetBytes(tempp)); + byte[] buffer = new byte[1048576]; + int read; + try + { + while ((read = fsIn.Read(buffer, 0, buffer.Length)) > 0) + { + cs.Write(buffer, 0, read); + } + fsIn.Close(); + } + catch (Exception ex) + { + Console.WriteLine("Error: " + ex.Message); + } + finally + { + cs.Close(); + fsCrypt.Close(); + } + } + + public void Addchannelkey(long chan, string Key) + { + List? chans = channels?.ToList(); + if (chans is null) chans = new(); + if (!(chans?.Where(s => s.id == chan).Count() > 0)) + { + ChannelLayout l = new() + { + id = chan, + key = Key + }; + chans?.Add(l); + channels = chans?.ToArray(); + } + } + } + + public class ChannelLayout + { + public long id { get; set; } = default!; + public string key { get; set; } = default!; + } + } + + public class AES + { + public static string Encrypt(string path, string Password) + { + string p = Path.GetTempFileName(); + byte[] salt = RandomNumberGenerator.GetBytes(100); + byte[] passwordBytes = Encoding.UTF8.GetBytes(Password); + Rfc2898DeriveBytes key = new(passwordBytes, salt, 50000); + byte[] data = System.IO.File.ReadAllBytes(path); + + using Aes aesAlg = Aes.Create(); + aesAlg.KeySize = 256; + aesAlg.BlockSize = 128; + aesAlg.Padding = PaddingMode.PKCS7; + aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8); + aesAlg.IV = key.GetBytes(aesAlg.BlockSize / 8); + + ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV); + + using FileStream msEncrypt = new(p, FileMode.Open); + msEncrypt.Write(salt, 0, salt.Length); + using CryptoStream csEncrypt = new(msEncrypt, encryptor, CryptoStreamMode.Write); + csEncrypt.Write(data, 0, data.Length); + csEncrypt.Dispose(); + msEncrypt.Dispose(); + return p; + + /* + + + + + string p = Path.GetTempFileName(); + byte[] salt = new byte[100]; + RNGCryptoServiceProvider provider = new(); + provider.GetBytes(salt); + FileStream fsCrypt = new(p, FileMode.Open); + byte[] passwordBytes = Encoding.UTF8.GetBytes(Password); + Aes AES = Aes.Create(); + AES.KeySize = 256; + AES.BlockSize = 128; + AES.Padding = PaddingMode.PKCS7; + Rfc2898DeriveBytes key = new(passwordBytes, salt, 50000); + AES.Key = key.GetBytes(AES.KeySize / 8); + AES.IV = key.GetBytes(AES.BlockSize / 8); + AES.Mode = CipherMode.CFB; + fsCrypt.Write(salt, 0, salt.Length); + key.Dispose(); + CryptoStream cs = new(fsCrypt, AES.CreateEncryptor(), CryptoStreamMode.Write); + FileStream fsIn = new(path, FileMode.Open); + try + { + FileInfo FI = new(path); + byte[] buffer = new byte[FI.Length]; + int read; + while ((read = fsIn.Read(buffer, 0, buffer.Length)) > 0) + { + cs.Write(buffer, 0, read); + } + } + catch (OutOfMemoryException ex) + { + throw new Exception("Buffer", ex); + } + fsIn.Close(); + fsIn.Dispose(); + cs.Close(); + cs.Dispose(); + fsCrypt.Close(); + fsCrypt.Dispose(); + NewPath = p;*/ + } + + public static void Decrypt(byte[] data, string Password, string File) + { + byte[] salt = new byte[100]; + using MemoryStream fsCrypt = new(data); + fsCrypt.Read(salt, 0, salt.Length); + byte[] passwordBytes = Encoding.UTF8.GetBytes(Password); + Rfc2898DeriveBytes key = new(passwordBytes, salt, 50000); + byte[] decrypted = new byte[data.Length - salt.Length]; + + using Aes aesAlg = Aes.Create(); + aesAlg.KeySize = 256; + aesAlg.BlockSize = 128; + aesAlg.Padding = PaddingMode.PKCS7; + aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8); + aesAlg.IV = key.GetBytes(aesAlg.BlockSize / 8); + + ICryptoTransform encryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV); + + using CryptoStream csEncrypt = new(fsCrypt, encryptor, CryptoStreamMode.Read); + FileStream fsOut = new(File, FileMode.Create); + int read; + byte[] buffer = new byte[data.Length]; + while ((read = csEncrypt.Read(buffer, 0, buffer.Length)) > 0) + { + fsOut.Write(buffer, 0, read); + } + csEncrypt.Dispose(); + fsCrypt.Dispose(); + fsOut.Dispose(); + } + } + + internal const int PasswordVersion = 0; + + internal static byte[] LocalPasswordEncrypt(byte[] Password, int PasswordVersion = PasswordVersion) + { + return PasswordVersion switch + { + 0 => SHA256.Create().ComputeHash(Password), + _ => throw new ArgumentException("The value provided was not accepted", nameof(PasswordVersion)), + }; + } + + internal static string RemotePasswordEncrypt(byte[] Password, int PasswordVersion = PasswordVersion) + { + return PasswordVersion switch + { + 0 => Convert.ToBase64String(Encrypt(LocalPasswordEncrypt(Password, PasswordVersion))), + _ => throw new ArgumentException("The value provided was not accepted", nameof(PasswordVersion)), + }; + } + + internal static byte[] Encrypt(string data) + { + return Encrypt(data, ServerPublicKey); + } + + internal static byte[] Encrypt(byte[] data) + { + return Encrypt(data, ServerPublicKey); + } + + internal static byte[] Encrypt(string data, string key, bool multithread = false) + { + return Encrypt(Encoder.GetBytes(data), key, multithread); + } + + internal static byte[] Encrypt(byte[] data, string key, bool multithread = false) + { + using RSACryptoServiceProvider rsa = new(); + rsa.FromXmlString(key); + int size = rsa.KeySize / 8; + double x = data.Length / (double)size; + int bbb = int.Parse(x.ToString().Split('.')[0]); + if (x.ToString().Contains('.')) bbb++; + byte[]? datasplitout = Array.Empty(); + if (multithread) + { + byte[][]? decccc = Array.Empty(); + Array.Resize(ref decccc, bbb); + int num = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * Server.Percent) * 2.0)); + if (num == 0) num = 1; + Parallel.For(0, bbb, new ParallelOptions() + { + MaxDegreeOfParallelism = num + }, i => + { + decccc[i] = rsa.Encrypt(data.Skip(i * size).Take(size).ToArray(), false); + }); + foreach (byte[] dataa in decccc) + { + datasplitout = Combine(datasplitout, dataa); + } + } + else + { + for (int i = 0; i < bbb; i++) + { + datasplitout = Combine(datasplitout, rsa.Encrypt(data.Skip(i * size).Take(size).ToArray(), false)); + } + } + return datasplitout; + } + + private static byte[] Combine(byte[] first, byte[] second) + { + byte[]? bytes = new byte[first.Length + second.Length]; + Buffer.BlockCopy(first, 0, bytes, 0, first.Length); + Buffer.BlockCopy(second, 0, bytes, first.Length, second.Length); + return bytes; + } + + internal static byte[] Decrypt(byte[] EncryptedText, bool multithread = false) + { + return Decrypt(EncryptedText, myPrivateKey, multithread); + } + + internal static byte[] Decrypt(byte[]? EncryptedText, string? key, bool multithread = false) + { + if (key is null) throw new ArgumentNullException(nameof(key)); + if (EncryptedText is null) throw new ArgumentNullException(nameof(EncryptedText)); + using RSACryptoServiceProvider rsa = new(); + rsa.FromXmlString(key); + int size = rsa.KeySize / 8; + double x = EncryptedText.Length / (double)size; + int bbb = int.Parse(x.ToString().Split('.')[0]); + if (x.ToString().Contains('.')) bbb++; + byte[]? datasplitout = Array.Empty(); + if (multithread) + { + byte[][]? decccc = Array.Empty(); + Array.Resize(ref decccc, bbb); + int num = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * Server.Percent) * 2.0)); + if (num == 0) num = 1; + Parallel.For(0, bbb, new ParallelOptions() + { + MaxDegreeOfParallelism = num + }, i => + { + decccc[i] = rsa.Decrypt(EncryptedText.Skip(i * size).Take(size).ToArray(), false); + }); + foreach (byte[] data in decccc) + { + datasplitout = Combine(datasplitout, data); + } + } + else + { + for (int i = 0; i < bbb; i++) + { + datasplitout = Combine(datasplitout, rsa.Decrypt(EncryptedText.Skip(i * size).Take(size).ToArray(), false)); + } + } + return datasplitout; + } + } +} diff --git a/Luski.net/Enums/Branch.cs b/Luski.net/Enums/Branch.cs new file mode 100755 index 0000000..ccc27b3 --- /dev/null +++ b/Luski.net/Enums/Branch.cs @@ -0,0 +1,8 @@ +namespace Luski.net.Enums; + +public enum Branch : short +{ + Dev, + Beta, + Master, +} diff --git a/Luski.net/Enums/ChannelType.cs b/Luski.net/Enums/ChannelType.cs new file mode 100755 index 0000000..c2bf1c5 --- /dev/null +++ b/Luski.net/Enums/ChannelType.cs @@ -0,0 +1,7 @@ +namespace Luski.net.Enums; + +public enum ChannelType : short +{ + DM, + GROUP, +} diff --git a/Luski.net/Enums/DataType.cs b/Luski.net/Enums/DataType.cs new file mode 100755 index 0000000..ff8ca21 --- /dev/null +++ b/Luski.net/Enums/DataType.cs @@ -0,0 +1,18 @@ +namespace Luski.net.Enums; + +internal enum DataType +{ + Message_Create, + Status_Update, + Friend_Request_Result, + Friend_Request, + Change_Channel, + Join_Call, + Leave_Call, + Call_Info, + Call_Data, + Login, + Error, + Key_Exchange, + MAX +} diff --git a/Luski.net/Enums/ErrorCode.cs b/Luski.net/Enums/ErrorCode.cs new file mode 100755 index 0000000..70139d1 --- /dev/null +++ b/Luski.net/Enums/ErrorCode.cs @@ -0,0 +1,14 @@ +namespace Luski.net.Enums; + +public enum ErrorCode +{ + MissingToken, + InvalidToken, + MissingPostData, + InvalidPostData, + Forbidden, + ServerError, + MissingHeader, + InvalidHeader, + InvalidURL +} diff --git a/Luski.net/Enums/FriendStatus.cs b/Luski.net/Enums/FriendStatus.cs new file mode 100755 index 0000000..1651479 --- /dev/null +++ b/Luski.net/Enums/FriendStatus.cs @@ -0,0 +1,9 @@ +namespace Luski.net.Enums; + +public enum FriendStatus +{ + NotFriends, + Friends, + PendingOut, + PendingIn +} diff --git a/Luski.net/Enums/PictureType.cs b/Luski.net/Enums/PictureType.cs new file mode 100755 index 0000000..ca5686b --- /dev/null +++ b/Luski.net/Enums/PictureType.cs @@ -0,0 +1,13 @@ +namespace Luski.net.Enums; + +public enum PictureType : short +{ + png, + jpeg, + bmp, + gif, + ico, + svg, + tif, + webp +} diff --git a/Luski.net/Enums/UserFlag.cs b/Luski.net/Enums/UserFlag.cs new file mode 100755 index 0000000..d4d0a35 --- /dev/null +++ b/Luski.net/Enums/UserFlag.cs @@ -0,0 +1,11 @@ +using System; + +namespace Luski.net.Enums; + +[Flags] +public enum UserFlag : short +{ + Dev = 0b_001, + Early = 0b_010, + Tester = 0b_100 +} diff --git a/Luski.net/Enums/UserStatus.cs b/Luski.net/Enums/UserStatus.cs new file mode 100755 index 0000000..439d9fb --- /dev/null +++ b/Luski.net/Enums/UserStatus.cs @@ -0,0 +1,10 @@ +namespace Luski.net.Enums; + +public enum UserStatus : short +{ + Offline, + Online, + Idle, + DoNotDisturb, + Invisible +} diff --git a/Luski.net/Exceptions.cs b/Luski.net/Exceptions.cs new file mode 100755 index 0000000..8b26354 --- /dev/null +++ b/Luski.net/Exceptions.cs @@ -0,0 +1,30 @@ +using System; + +namespace Luski.net +{ + public class Exceptions + { + + [Serializable] + public class MissingEventException : Exception + { + public string EventName; + public MissingEventException(string Event) : base(Event) + { + EventName = Event; + } + } + + + [Serializable] + public class NotConnectedException : Exception + { + public NotConnectedException(object sender, string message) : base(message) + { + Sender = sender; + } + + public object Sender { get; } + } + } +} diff --git a/Luski.net/Interfaces/IAudioClient.cs b/Luski.net/Interfaces/IAudioClient.cs new file mode 100755 index 0000000..dc21ee5 --- /dev/null +++ b/Luski.net/Interfaces/IAudioClient.cs @@ -0,0 +1,48 @@ +using Luski.net.Sound; +using System; +using System.Threading.Tasks; +using static Luski.net.Exceptions; + +namespace Luski.net.Interfaces; + +public interface IAudioClient +{ + /// + /// the event is fired when your has joined the call + /// + event Func Connected; + /// + /// Tells you if you are muted + /// + bool Muted { get; } + /// + /// Tells you if you are deafned + /// + bool Deafened { get; } + /// + /// Toggles if you are speaking to your friends + /// + void ToggleMic(); + /// + /// Toggles if you can hear audio + /// + void ToggleAudio(); + /// + /// Changes what the call gets its data from + /// + /// This is the you want to recored from + /// + void RecordSoundFrom(RecordingDevice Device); + /// + /// Changes what the call gets its data from + /// + /// This is the you want to heare outhers + /// + void PlaySoundTo(PlaybackDevice Device); + /// + /// Joins the Voice call + /// + /// + void JoinCall(); + void LeaveCall(); +} diff --git a/Luski.net/Interfaces/IChannel.cs b/Luski.net/Interfaces/IChannel.cs new file mode 100755 index 0000000..98ab035 --- /dev/null +++ b/Luski.net/Interfaces/IChannel.cs @@ -0,0 +1,32 @@ +using Luski.net.Enums; +using Luski.net.JsonTypes; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Luski.net.Interfaces; + +/// +/// contains a list of variables that all channels from luski have +/// +public interface ITextChannel +{ + long Id { get; } + string Title { get; } + string Description { get; } + string Key { get; } + /// + /// returns the current of the + /// + ChannelType Type { get; } + /// + /// Sends a to the server for the currently selected + /// + /// The messate you want to send to the server + Task SendMessage(string Message, params File[] Files); + Task SendKeysToUsers(); + Task GetMessage(long ID); + Task> GetMessages(long Message_Id, int count = 50); + Task> GetMessages(int count = 50); + Task GetPicture(); + IReadOnlyList Members { get; } +} diff --git a/Luski.net/Interfaces/IUser.cs b/Luski.net/Interfaces/IUser.cs new file mode 100755 index 0000000..1355fea --- /dev/null +++ b/Luski.net/Interfaces/IUser.cs @@ -0,0 +1,45 @@ +using Luski.net.Enums; +using System.Threading.Tasks; + +namespace Luski.net.Interfaces; + +/// +/// Represents the curent user +/// +public interface IUser +{ + /// + /// The current Id of the user + /// + long Id { get; } + /// + /// The cerrent username of the user + /// + string Username { get; } + /// + /// The current tag for the user + /// Ex: #1234 + /// + //short Tag { get; } + /// + /// The current status of the user + /// + UserStatus Status { get; } + /// + /// will returen the picture type of the user + /// + PictureType PictureType { get; } + /// + /// the current flags of a user + /// + UserFlag Flags { get; } + /// + /// Gets the current avatar of the user + /// + Task GetAvatar(); + /// + /// Gets the current user key + /// + /// + Task GetUserKey(); +} diff --git a/Luski.net/JsonRequest.cs b/Luski.net/JsonRequest.cs new file mode 100755 index 0000000..4a898e3 --- /dev/null +++ b/Luski.net/JsonRequest.cs @@ -0,0 +1,28 @@ +using Luski.net.Enums; +using System; + +namespace Luski.net +{ + internal static class JsonRequest + { + internal static string SendCallData(byte[] Data, long channel) + { + return $"{{\"data\": \"{Convert.ToBase64String(Data)}\", \"id\": {channel}}}"; + } + + internal static string JoinCall(long Channel) + { + return $"{{\"id\": {Channel}}}"; + } + + internal static string Send(DataType Request, string Data) + { + return $"{{\"type\": {(int)Request}, \"data\": {Data}}}"; + } + + internal static string FriendRequestResult(long User, bool Result) + { + return $"{{\"id\": {User},\"result\": {Result.ToString().ToLower()}}}"; + } + } +} diff --git a/Luski.net/JsonTypes/BaseTypes/HTTPRequest.cs b/Luski.net/JsonTypes/BaseTypes/HTTPRequest.cs new file mode 100755 index 0000000..f539e17 --- /dev/null +++ b/Luski.net/JsonTypes/BaseTypes/HTTPRequest.cs @@ -0,0 +1,11 @@ +using System.Text.Json.Serialization; +using Luski.net.Enums; + +namespace Luski.net.JsonTypes.BaseTypes; + +internal class HTTPRequest +{ + [JsonPropertyName("data_type")] + [JsonInclude] + public DataType Type { get; set; } = default!; +} diff --git a/Luski.net/JsonTypes/BaseTypes/IncomingHTTP.cs b/Luski.net/JsonTypes/BaseTypes/IncomingHTTP.cs new file mode 100755 index 0000000..ff6c3f8 --- /dev/null +++ b/Luski.net/JsonTypes/BaseTypes/IncomingHTTP.cs @@ -0,0 +1,32 @@ +using Luski.net.Enums; +using System.ComponentModel; +using System.Net; +using System.Text.Json.Serialization; + +namespace Luski.net.JsonTypes.BaseTypes; + +[Browsable(false)] +[EditorBrowsable(EditorBrowsableState.Never)] +public class IncomingHTTP +{ + [JsonPropertyName("error")] + [JsonInclude] + public ErrorCode? Error { get; internal set; } = default!; +#pragma warning disable SYSLIB1037 // Deserialization of init-only properties is currently not supported in source generation mode. + [JsonIgnore] + public HttpStatusCode StatusCode { get; init; } +#pragma warning restore SYSLIB1037 // Deserialization of init-only properties is currently not supported in source generation mode. + [JsonPropertyName("error_message")] + [JsonInclude] + public string? ErrorMessage { get; internal set; } = default!; +} + +[JsonSerializable(typeof(IncomingHTTP))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + WriteIndented = false)] +internal partial class IncomingHTTPContext : JsonSerializerContext +{ + +} diff --git a/Luski.net/JsonTypes/BaseTypes/IncomingWSS.cs b/Luski.net/JsonTypes/BaseTypes/IncomingWSS.cs new file mode 100755 index 0000000..ec2ba6f --- /dev/null +++ b/Luski.net/JsonTypes/BaseTypes/IncomingWSS.cs @@ -0,0 +1,24 @@ +using Luski.net.Enums; +using System.Text.Json.Serialization; + +namespace Luski.net.JsonTypes.BaseTypes; + +internal class IncomingWSS +{ + [JsonPropertyName("type")] + [JsonInclude] + public DataType? Type { get; set; } = default!; + [JsonPropertyName("error")] + [JsonInclude] + public string Error { get; set; } = default!; +} + +[JsonSerializable(typeof(IncomingWSS))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.Unspecified, + WriteIndented = false)] +internal partial class IncomingWSSContext : JsonSerializerContext +{ + +} diff --git a/Luski.net/JsonTypes/BaseTypes/SocketUserBase.cs b/Luski.net/JsonTypes/BaseTypes/SocketUserBase.cs new file mode 100755 index 0000000..4577c75 --- /dev/null +++ b/Luski.net/JsonTypes/BaseTypes/SocketUserBase.cs @@ -0,0 +1,118 @@ +using Luski.net.Enums; +using Luski.net.Interfaces; +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Metadata; +using System.Threading.Tasks; + +namespace Luski.net.JsonTypes.BaseTypes; + +[Browsable(false)] +[EditorBrowsable(EditorBrowsableState.Never)] +public abstract class SocketUserBase : IncomingHTTP, IUser +{ + [JsonPropertyName("id")] + [JsonInclude] + public long Id { get; internal set; } = default!; + [JsonPropertyName("username")] + [JsonInclude] + public string Username { get; internal set; } = default!; + // [JsonPropertyName("tag")] + //[JsonInclude] + //public short Tag { get; internal set; } = default!; + [JsonPropertyName("status")] + [JsonInclude] + public UserStatus Status { get; internal set; } = default!; + [JsonPropertyName("picture_type")] + [JsonInclude] + public PictureType PictureType { get; internal set; } = default!; + + [JsonPropertyName("flags")] + [JsonInclude] + public UserFlag Flags { get; internal set; } = default!; + + public async Task GetAvatar() + { + if (Server.Cache != null) + { + bool isc = System.IO.File.Exists($"{Server.Cache}/avatars/{Id}"); + if (!isc) await Server.GetFromServer($"socketuserprofile/Avatar/{Id}", $"{Server.Cache}/avatars/{Id}"); + } + return System.IO.File.ReadAllBytes($"{Server.Cache}/avatars/{Id}"); + } + + public Task GetUserKey() + { + if (Server._user is null) throw new Exception("you are not loged in"); + if (Id == Server._user.Id && Encryption.MyPublicKey is not null) return Task.FromResult(Encryption.MyPublicKey); + string data = Server.GetFromServer($"Keys/GetUserKey/{Id}").Content.ReadAsStringAsync().Result; + return Task.FromResult(data); + } + + internal static async Task GetUser(long UserId, JsonTypeInfo Json) where TUser : SocketUserBase, new() + { + TUser user; + if (Server.poeople is null) Server.poeople = new(); + if (Server.poeople.Count > 0 && Server.poeople.Any(s => s.Id == UserId)) + { + TUser temp = Server.poeople.Where(s => s is TUser && s.Id == UserId).Cast().FirstOrDefault()!; + if (temp is SocketRemoteUser && (temp as SocketRemoteUser)!.Channel == null) + { + foreach (SocketDMChannel chan in Server.chans.Where(s => s is SocketDMChannel).Cast()) + { + if (chan.Type == ChannelType.DM && chan.Id != 0 && chan.MemberIdList is not null) + { + if (chan.MemberIdList.Any(s => s == UserId)) (temp as SocketRemoteUser)!.Channel = chan; + } + } + } + return temp!; + } + while (true) + { + if (Server.CanRequest) + { + user = await Server.GetFromServer("socketuser", + Json, + new System.Collections.Generic.KeyValuePair("id", UserId.ToString())); + break; + } + } + + if (user is null) throw new Exception("Server did not return a user"); + if (Server.poeople.Count > 0 && Server.poeople.Any(s => s.Id == UserId)) + { + foreach (IUser? p in Server.poeople.Where(s => s.Id == UserId)) + { + Server.poeople.Remove(p); + } + } + if (Server._user is not null && UserId != 0 && UserId != Server._user.Id) + { + foreach (SocketDMChannel chan in Server.chans.Where(s => s is SocketDMChannel).Cast()) + { + if (chan.Id != 0 && chan.MemberIdList is not null) + { + if (chan.MemberIdList.Any(s => s == UserId)) (user as SocketRemoteUser)!.Channel = chan; + } + } + } + Server.poeople.Add(user); + return user; + } +} + +[JsonSerializable(typeof(SocketUserBase))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.Unspecified, + WriteIndented = false, + DefaultIgnoreCondition = JsonIgnoreCondition.Never)] +internal partial class SocketUserBaseContext : JsonSerializerContext +{ + +} diff --git a/Luski.net/JsonTypes/File.cs b/Luski.net/JsonTypes/File.cs new file mode 100755 index 0000000..901cd14 --- /dev/null +++ b/Luski.net/JsonTypes/File.cs @@ -0,0 +1,107 @@ +using Luski.net.Enums; +using Luski.net.JsonTypes.BaseTypes; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace Luski.net.JsonTypes; + +public class File : IncomingHTTP +{ + [JsonInclude] + [JsonPropertyName("name")] + public string Name { get; internal set; } = default!; + [JsonInclude] + [JsonPropertyName("size")] + public ulong Size { get; internal set; } = default!; + [JsonInclude] + [JsonPropertyName("id")] + public long Id { get; internal set; } = default!; + [JsonIgnore] + internal string? key { get; set; } = default!; + [JsonIgnore] + internal string loc { get; set; } = default!; + + public async void DownloadBytes(string Loc, long key) + { + //using HttpClient web = new(); + //web.DefaultRequestHeaders.Add("token", Server.Token); + //web.DefaultRequestHeaders.Add("id", id.ToString()); + //IncomingHTTP? request = JsonSerializer.Deserialize(web.GetAsync($"https://{Server.Domain}/{Server.API_Ver}/SocketMessage/GetFile").Result.Content.ReadAsStringAsync().Result, IncomingHTTPContext.Default.IncomingHTTP); + string path = Path.GetTempFileName(); + + await Server.GetFromServer($"SocketMessage/GetFile/{Id}", path); + string Key = (key == 0 ? Encryption.MyPublicKey : Encryption.File.Channels.GetKey(key))!; + Encryption.AES.Decrypt(System.IO.File.ReadAllBytes(path), Key, Loc); + /* + if (request is not null && request.Error is not null) + { + switch (request.Error) + { + case ErrorCode.InvalidToken: + throw new Exception("Your current token is no longer valid"); + case ErrorCode.ServerError: + throw new Exception("Error from server: " + request.ErrorMessage); + case ErrorCode.Forbidden: + throw new Exception("Your request was denied by the server"); + default: + MemoryStream? ms = new(); + JsonSerializer.Serialize(new Utf8JsonWriter(ms), + request, + IncomingHTTPContext.Default.IncomingHTTP); + throw new Exception(Encoding.UTF8.GetString(ms.ToArray())); + } + } + + + if (request?.data is not null) + { + foreach (string raw in request.data) + { + Encryption.AES.Decrypt(Convert.FromBase64String(raw), Encryption.File.Channels.GetKey(key), Loc); + } + }*/ + } + + public void SetFile(string path) + { + FileInfo fi = new(path); + Name = fi.Name; + Size = (ulong)fi.Length; + loc = path; + } + + internal async Task Upload(string keyy) + { + if (Name != null) Name = Convert.ToBase64String(Encryption.Encrypt(Name, keyy)); + Debug.WriteLine("uploading"); + string NPath = Encryption.AES.Encrypt(loc, keyy); + File sf = await Server.SendServer( + "SocketMessage/UploadFile", + NPath, + FileContext.Default.File, + new KeyValuePair("name", Name)); + try { System.IO.File.Delete(NPath); } catch { } + Debug.WriteLine("done uploading"); + if (sf.Error is not null) throw new Exception(sf.ErrorMessage); + return sf.Id; + } + + internal void decrypt() + { + if (Name is not null) Name = Encryption.Encoder.GetString(Encryption.Decrypt(Convert.FromBase64String(Name), key)); + } +} + +[JsonSerializable(typeof(File))] +internal partial class FileContext : JsonSerializerContext +{ + +} diff --git a/Luski.net/JsonTypes/FriendRequestResult.cs b/Luski.net/JsonTypes/FriendRequestResult.cs new file mode 100755 index 0000000..2d78f98 --- /dev/null +++ b/Luski.net/JsonTypes/FriendRequestResult.cs @@ -0,0 +1,27 @@ +using Luski.net.JsonTypes.BaseTypes; +using System.Text.Json.Serialization; + +namespace Luski.net.JsonTypes; + +internal class FriendRequestResult : IncomingHTTP +{ + [JsonPropertyName("channel")] + [JsonInclude] + public long? Channel { get; set; } + [JsonPropertyName("id")] + [JsonInclude] + public long? Id { get; set; } + [JsonPropertyName("result")] + [JsonInclude] + public bool? Result { get; set; } +} + +[JsonSerializable(typeof(FriendRequestResult))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + WriteIndented = false)] +internal partial class FriendRequestResultContext : JsonSerializerContext +{ + +} diff --git a/Luski.net/JsonTypes/HTTP/Channel.cs b/Luski.net/JsonTypes/HTTP/Channel.cs new file mode 100755 index 0000000..1150e5f --- /dev/null +++ b/Luski.net/JsonTypes/HTTP/Channel.cs @@ -0,0 +1,21 @@ +using Luski.net.JsonTypes.BaseTypes; +using System.Text.Json.Serialization; + +namespace Luski.net.JsonTypes.HTTP; + +internal class Channel : HTTPRequest +{ + [JsonPropertyName("id")] + [JsonInclude] + public long Id { get; set; } = default!; +} + +[JsonSerializable(typeof(Channel))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + WriteIndented = false)] +internal partial class ChannelContext : JsonSerializerContext +{ + +} diff --git a/Luski.net/JsonTypes/HTTP/FriendRequest.cs b/Luski.net/JsonTypes/HTTP/FriendRequest.cs new file mode 100755 index 0000000..528d4c4 --- /dev/null +++ b/Luski.net/JsonTypes/HTTP/FriendRequest.cs @@ -0,0 +1,30 @@ +using Luski.net.JsonTypes.BaseTypes; +using System.Text.Json.Serialization; + +namespace Luski.net.JsonTypes.HTTP; + +internal class FriendRequest : HTTPRequest +{ + [JsonPropertyName("id")] + [JsonInclude] + public long Id { get; set; } = default!; + [JsonPropertyName("subtype")] + [JsonInclude] + public int SubType { get; set; } = default!; + [JsonPropertyName("username")] + [JsonInclude] + public string Username { get; set; } = default!; + [JsonPropertyName("tag")] + [JsonInclude] + public short Tag { get; set; } = default!; +} + +[JsonSerializable(typeof(FriendRequest))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + WriteIndented = false)] +internal partial class FriendRequestContext : JsonSerializerContext +{ + +} diff --git a/Luski.net/JsonTypes/HTTP/FriendRequestResultOut.cs b/Luski.net/JsonTypes/HTTP/FriendRequestResultOut.cs new file mode 100755 index 0000000..7517c7a --- /dev/null +++ b/Luski.net/JsonTypes/HTTP/FriendRequestResultOut.cs @@ -0,0 +1,24 @@ +using Luski.net.JsonTypes.BaseTypes; +using System.Text.Json.Serialization; + +namespace Luski.net.JsonTypes.HTTP; + +internal class FriendRequestResultOut : HTTPRequest +{ + [JsonPropertyName("id")] + [JsonInclude] + public long? Id { get; set; } + [JsonPropertyName("result")] + [JsonInclude] + public bool? Result { get; set; } +} + +[JsonSerializable(typeof(FriendRequestResultOut))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + WriteIndented = false)] +internal partial class FriendRequestResultOutContext : JsonSerializerContext +{ + +} diff --git a/Luski.net/JsonTypes/HTTP/Message.cs b/Luski.net/JsonTypes/HTTP/Message.cs new file mode 100755 index 0000000..ee6f9a5 --- /dev/null +++ b/Luski.net/JsonTypes/HTTP/Message.cs @@ -0,0 +1,27 @@ +using Luski.net.JsonTypes.BaseTypes; +using System.Text.Json.Serialization; + +namespace Luski.net.JsonTypes.HTTP; + +internal class Message : HTTPRequest +{ + [JsonPropertyName("channel_id")] + [JsonInclude] + public long Channel { get; set; } = default!; + [JsonPropertyName("content")] + [JsonInclude] + public string Context { get; set; } = default!; + [JsonPropertyName("files")] + [JsonInclude] + public long[] Files { get; set; } = default!; +} + +[JsonSerializable(typeof(Message))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + WriteIndented = false)] +internal partial class MessageContext : JsonSerializerContext +{ + +} diff --git a/Luski.net/JsonTypes/HTTP/Status.cs b/Luski.net/JsonTypes/HTTP/Status.cs new file mode 100755 index 0000000..f447753 --- /dev/null +++ b/Luski.net/JsonTypes/HTTP/Status.cs @@ -0,0 +1,22 @@ +using System.Text.Json.Serialization; +using Luski.net.Enums; +using Luski.net.JsonTypes.BaseTypes; + +namespace Luski.net.JsonTypes.HTTP; + +internal class Status : HTTPRequest +{ + [JsonPropertyName("status")] + [JsonInclude] + public UserStatus UserStatus { get; set; } = default!; +} + +[JsonSerializable(typeof(Status))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + WriteIndented = false)] +internal partial class StatusContext : JsonSerializerContext +{ + +} diff --git a/Luski.net/JsonTypes/KeyExchange.cs b/Luski.net/JsonTypes/KeyExchange.cs new file mode 100755 index 0000000..310f846 --- /dev/null +++ b/Luski.net/JsonTypes/KeyExchange.cs @@ -0,0 +1,10 @@ +namespace Luski.net.JsonTypes +{ + internal class KeyExchange + { + public long channel { get; set; } = default!; + public string key { get; set; } = default!; + + public long? to { get; set; } = default!; + } +} diff --git a/Luski.net/JsonTypes/Login.cs b/Luski.net/JsonTypes/Login.cs new file mode 100755 index 0000000..8492d70 --- /dev/null +++ b/Luski.net/JsonTypes/Login.cs @@ -0,0 +1,20 @@ +using Luski.net.JsonTypes.BaseTypes; +using System.Text.Json.Serialization; + +namespace Luski.net.JsonTypes; + +internal class Login : IncomingHTTP +{ + [JsonPropertyName("login_token")] + public string? Token { get; set; } = default!; +} + +[JsonSerializable(typeof(Login))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + WriteIndented = false)] +internal partial class LoginContext : JsonSerializerContext +{ + +} diff --git a/Luski.net/JsonTypes/OfflineKeyData.cs b/Luski.net/JsonTypes/OfflineKeyData.cs new file mode 100755 index 0000000..1f4b978 --- /dev/null +++ b/Luski.net/JsonTypes/OfflineKeyData.cs @@ -0,0 +1,19 @@ +using Luski.net.JsonTypes.BaseTypes; +using System.Text.Json.Serialization; + +namespace Luski.net.JsonTypes; + +internal class OfflineKeyData : IncomingHTTP +{ + public KeyExchange[]? keys { get; internal set; } = default!; +} + +[JsonSerializable(typeof(OfflineKeyData))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + WriteIndented = false)] +internal partial class OfflineKeyDataContext : JsonSerializerContext +{ + +} diff --git a/Luski.net/JsonTypes/SocketAppUser.cs b/Luski.net/JsonTypes/SocketAppUser.cs new file mode 100755 index 0000000..15b5e60 --- /dev/null +++ b/Luski.net/JsonTypes/SocketAppUser.cs @@ -0,0 +1,178 @@ +using Luski.net.Interfaces; +using Luski.net.JsonTypes.BaseTypes; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json.Serialization; + +namespace Luski.net.JsonTypes; + +public class SocketAppUser : SocketUserBase +{ + [JsonPropertyName("email")] + [JsonInclude] + public string Email { get; internal set; } = default!; + [JsonIgnore] + public IReadOnlyList Channels + { + get + { + if (_Channels is null || ChannelIdList is not null) + { + if (ChannelIdList.Length != 0) + { + _Channels = new List(); + foreach (long channel in ChannelIdList) + { + SocketChannel s = SocketChannel.GetChannel(channel, SocketChannelContext.Default.SocketChannel).Result; + Server.chans.Remove(s); + switch (s.Type) + { + case Enums.ChannelType.GROUP: + _Channels.Add(SocketChannel.GetChannel(channel, SocketGroupChannelContext.Default.SocketGroupChannel).Result); + break; + case Enums.ChannelType.DM: + _Channels.Add(SocketChannel.GetChannel(channel, SocketDMChannelContext.Default.SocketDMChannel).Result); + break; + } + } + } + else _Channels = new List(); + } + return _Channels.AsReadOnly(); + } + } + [JsonIgnore] + public IReadOnlyList FriendRequests + { + get + { + if (_FriendRequests is null || FriendRequestsRaw is not null) + { + _FriendRequests = new(); + if (ChannelIdList.Length != 0 && FriendRequestsRaw is not null) + { + foreach (FR person in FriendRequestsRaw) + { + //_Friends.Add(SocketRemoteUser.GetUser(person)); + long id = person.user_id == Id ? person.from : person.user_id; + SocketRemoteUser frq = GetUser(id, SocketRemoteUserContext.Default.SocketRemoteUser).Result; + _FriendRequests.Add(frq); + } + } + else _FriendRequests = new(); + } + return _FriendRequests.AsReadOnly(); + } + } + [JsonIgnore] + public IReadOnlyList Friends + { + get + { + if (_Friends is null || FriendIdList is not null) + { + if (ChannelIdList.Length != 0) + { + _Friends = new List(); + foreach (long person in FriendIdList) + { + _Friends.Add(GetUser(person, SocketRemoteUserContext.Default.SocketRemoteUser).Result); + } + } + else _Friends = new List(); + } + return _Friends.AsReadOnly(); + } + } + [JsonPropertyName("selected_channel")] + [JsonInclude] + public long SelectedChannel { get; internal set; } = default!; + [JsonPropertyName("channels")] + [JsonInclude] + public long[] ChannelIdList { get; internal set; } = default!; + [JsonPropertyName("friends")] + [JsonInclude] + public long[] FriendIdList { get; internal set; } = default!; + [JsonPropertyName("friend_requests")] + [JsonInclude] + public FR[] FriendRequestsRaw { get; internal set; } = default!; + [JsonIgnore] + private List _Channels = default!; + [JsonIgnore] + private List _Friends = default!; + [JsonIgnore] + private List _FriendRequests = default!; + + public class FR + { + public long from { get; set; } = default!; + public long user_id { get; set; } = default!; + } + + internal void AddFriend(SocketRemoteUser User) + { + if (Server.poeople.Any(s => s.Id == User.Id)) + { + IEnumerable b = Server.poeople.Where(s => s.Id == User.Id); + foreach (IUser item in b) + { + Server.poeople.Remove(item); + } + Server.poeople.Add(User); + } + else + { + Server.poeople.Add(User); + } + _Friends.Add(User); + } + + internal void RemoveFriendRequest(SocketRemoteUser User) + { + if (Server.poeople.Any(s => s.Id == User.Id)) + { + IEnumerable b = Server.poeople.Where(s => s.Id == User.Id); + foreach (IUser item in b) + { + Server.poeople.Remove(item); + } + } + Server.poeople.Add(User); + foreach (SocketRemoteUser user in _FriendRequests) + { + if (User.Id == user.Id) + { + _FriendRequests.Remove(User); + } + } + } + + internal void AddFriendRequest(SocketRemoteUser User) + { + if (Server.poeople.Any(s => s.Id == User.Id)) + { + IEnumerable b = Server.poeople.Where(s => s.Id == User.Id); + foreach (IUser item in b) + { + Server.poeople.Remove(item); + } + Server.poeople.Add(User); + } + else + { + Server.poeople.Add(User); + } + _FriendRequests.Add(User); + } +} + +[JsonSerializable(typeof(SocketAppUser))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.Unspecified, + WriteIndented = false, + DefaultIgnoreCondition = JsonIgnoreCondition.Never)] +internal partial class SocketAppUserContext : JsonSerializerContext +{ + +} diff --git a/Luski.net/JsonTypes/SocketBulkMessage.cs b/Luski.net/JsonTypes/SocketBulkMessage.cs new file mode 100755 index 0000000..7ca8518 --- /dev/null +++ b/Luski.net/JsonTypes/SocketBulkMessage.cs @@ -0,0 +1,23 @@ +using Luski.net.JsonTypes.BaseTypes; +using System.Text.Json.Serialization; + +namespace Luski.net.JsonTypes; + +internal class SocketBulkMessage : IncomingHTTP +{ + [JsonPropertyName("messages")] + [JsonInclude] + public SocketMessage[]? Messages { get; set; } = default!; +} + +[JsonSerializable(typeof(SocketBulkMessage))] +[JsonSerializable(typeof(SocketAppUser))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + WriteIndented = false, + DefaultIgnoreCondition = JsonIgnoreCondition.Never)] +internal partial class SocketBulkMessageContext : JsonSerializerContext +{ + +} diff --git a/Luski.net/JsonTypes/SocketChannel.cs b/Luski.net/JsonTypes/SocketChannel.cs new file mode 100755 index 0000000..95af482 --- /dev/null +++ b/Luski.net/JsonTypes/SocketChannel.cs @@ -0,0 +1,173 @@ +using Luski.net.Enums; +using Luski.net.Interfaces; +using Luski.net.JsonTypes.BaseTypes; +using System.Collections.Generic; +using System; +using System.Text.Json.Serialization; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; +using Luski.net.JsonTypes.WSS; +using System.Text.Json.Serialization.Metadata; + +namespace Luski.net.JsonTypes; + +public class SocketChannel : IncomingHTTP +{ + [JsonInclude] + [JsonPropertyName("id")] + public long Id { get; internal set; } = default!; + [JsonInclude] + [JsonPropertyName("title")] + public string Title { get; internal set; } = default!; + [JsonInclude] + [JsonPropertyName("description")] + public string Description { get; internal set; } = default!; + [JsonInclude] + [JsonPropertyName("key")] + public string Key { get; internal set; } = default!; + [JsonPropertyName("type")] + [JsonInclude] + public ChannelType Type { get; internal set; } = default!; + [JsonPropertyName("members")] + [JsonInclude] + public long[] MemberIdList { get; internal set; } = default!; + [JsonIgnore] + public IReadOnlyList Members + { + get + { + if (MemberIdList is null || MemberIdList.Length == 0) return Array.Empty().ToList().AsReadOnly(); + if (_members is null || !_members.Any()) + { + _members = new(); + foreach (long member in MemberIdList) + { + if (member != Server._user!.Id) _members.Add(SocketUserBase.GetUser(member, SocketRemoteUserContext.Default.SocketRemoteUser).Result); + else _members.Add(Server._user); + } + } + return _members.AsReadOnly(); + } + } + [JsonIgnore] + private List _members = new(); + + public async Task SendKeysToUsers() + { + if (Key is null) + { + StartKeyProcessAsync().Wait(); + return Task.CompletedTask; + } + int num = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * Server.Percent) * 2.0)); + if (num == 0) num = 1; + string? lkey = Encryption.File.Channels.GetKey(Id); + Parallel.ForEach(Members, new ParallelOptions() + { + MaxDegreeOfParallelism = num + }, async i => + { + if (i.Id != Server._user?.Id) + { + string key = await i.GetUserKey(); + if (!string.IsNullOrEmpty(key)) + { + WSSKeyExchange send = new() + { + to = i.Id, + channel = Id, + key = Convert.ToBase64String(Encryption.Encrypt(lkey, key)) + }; + Server.SendServer(send, WSSKeyExchangeContext.Default.WSSKeyExchange); + } + } + }); + return Task.CompletedTask; + } + + internal async Task StartKeyProcessAsync() + { + Encryption.GenerateNewKeys(out string Public, out string Private); + Key = Public; + HttpResponseMessage b; + using (HttpClient web = new()) + { + web.DefaultRequestHeaders.Add("token", Server.Token); + b = web.PostAsync($"https://{Server.InternalDomain}/{Server.API_Ver}/SocketChannel/SetKey/{Id}", new StringContent(Key)).Result; + } + int num = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * Server.Percent) * 2.0)); + if (num == 0) num = 1; + Encryption.File.Channels.AddKey(Id, Private); + Parallel.ForEach(Members, new ParallelOptions() + { + MaxDegreeOfParallelism = num + }, i => + { + if (i.Id != Server._user?.Id) + { + string key = i.GetUserKey().Result; + if (!string.IsNullOrEmpty(key)) + { + WSSKeyExchange send = new() + { + to = i.Id, + channel = Id, + key = Convert.ToBase64String(Encryption.Encrypt(Private, key)) + }; + Server.SendServer(send, WSSKeyExchangeContext.Default.WSSKeyExchange); + } + } + }); + } + + internal static async Task GetChannel(long id, JsonTypeInfo Json) where TChannel : SocketChannel, new() + { + TChannel request; + if (Server.chans is null) Server.chans = new(); + if (Server.chans.Count > 0 && Server.chans.Any(s => s.Id == id)) + { + return Server.chans.Where(s => s is TChannel && s.Id == id).Cast().FirstOrDefault()!; + } + while (true) + { + if (Server.CanRequest) + { + request = await Server.GetFromServer($"SocketChannel/Get/{id}", Json); + break; + } + } + if (request is null) throw new Exception("Something was wrong with the server responce"); + if (request.Error is null) + { + if (Server.chans is null) Server.chans = new(); + if (Server.chans.Count > 0 && Server.chans.Any(s => s.Id == request.Id)) + { + foreach (SocketChannel? p in Server.chans.Where(s => s.Id == request.Id)) + { + Server.chans.Remove(p); + } + } + Server.chans.Add(request); + return request; + } + throw request.Error switch + { + ErrorCode.InvalidToken => new Exception("Your current token is no longer valid"), + ErrorCode.Forbidden => new Exception("The server rejected your request"), + ErrorCode.ServerError => new Exception("Error from server: " + request.ErrorMessage), + ErrorCode.InvalidURL or ErrorCode.MissingHeader => new Exception(request.ErrorMessage), + _ => new Exception($"Unknown data: '{request.ErrorMessage}'"), + }; + } +} + +[JsonSerializable(typeof(SocketChannel))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + WriteIndented = false)] +internal partial class SocketChannelContext : JsonSerializerContext +{ + +} \ No newline at end of file diff --git a/Luski.net/JsonTypes/SocketDMChannel.cs b/Luski.net/JsonTypes/SocketDMChannel.cs new file mode 100755 index 0000000..2c6ea6a --- /dev/null +++ b/Luski.net/JsonTypes/SocketDMChannel.cs @@ -0,0 +1,33 @@ +using Luski.net.JsonTypes.BaseTypes; +using System.Linq; +using System.Text.Json.Serialization; + +namespace Luski.net.JsonTypes; + +public class SocketDMChannel : SocketTextChannel +{ + public SocketRemoteUser User + { + get + { + if (_user is null) + { + var list = MemberIdList.ToList(); + list.Remove(Server._user!.Id); + _user = SocketUserBase.GetUser(list.FirstOrDefault(), SocketRemoteUserContext.Default.SocketRemoteUser).Result; + } + return _user; + } + } + public SocketRemoteUser _user = null!; +} + +[JsonSerializable(typeof(SocketDMChannel))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + WriteIndented = false)] +internal partial class SocketDMChannelContext : JsonSerializerContext +{ + +} diff --git a/Luski.net/JsonTypes/SocketGroupChannel.cs b/Luski.net/JsonTypes/SocketGroupChannel.cs new file mode 100755 index 0000000..f7a3300 --- /dev/null +++ b/Luski.net/JsonTypes/SocketGroupChannel.cs @@ -0,0 +1,20 @@ +using System.Text.Json.Serialization; + +namespace Luski.net.JsonTypes; + +public class SocketGroupChannel : SocketTextChannel +{ + [JsonPropertyName("owner")] + [JsonInclude] + public long Owner { get; internal set; } = default!; +} + +[JsonSerializable(typeof(SocketGroupChannel))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + WriteIndented = false)] +internal partial class SocketGroupChannelContext : JsonSerializerContext +{ + +} diff --git a/Luski.net/JsonTypes/SocketMessage.cs b/Luski.net/JsonTypes/SocketMessage.cs new file mode 100755 index 0000000..aa4c948 --- /dev/null +++ b/Luski.net/JsonTypes/SocketMessage.cs @@ -0,0 +1,87 @@ +using Luski.net.Interfaces; +using Luski.net.JsonTypes.BaseTypes; +using System; +using System.Linq; +using System.Net.Http; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace Luski.net.JsonTypes; + +public class SocketMessage : IncomingHTTP +{ + [JsonPropertyName("id")] + [JsonInclude] + public long Id { get; internal set; } = default!; + [JsonPropertyName("user_id")] + [JsonInclude] + public long AuthorID { get; internal set; } = default!; + [JsonPropertyName("content")] + [JsonInclude] + public string Context { get; internal set; } = default!; + [JsonPropertyName("channel_id")] + [JsonInclude] + public long ChannelID { get; internal set; } = default!; + [JsonPropertyName("files")] + [JsonInclude] + public File[]? Files { get; internal set; } = default!; + public async Task GetChannel() + { + if (Server.chans.Any(s => s.Id == ChannelID)) + { + return (SocketTextChannel)Server.chans.Where(s => s.Id == ChannelID).First(); + } + else + { + SocketTextChannel ch = await SocketChannel.GetChannel(ChannelID, SocketTextChannelContext.Default.SocketTextChannel); + Server.chans.Add(ch); + return ch; + } + } + public async Task GetAuthor() + { + if (Server._user!.Id != AuthorID) return await SocketUserBase.GetUser(AuthorID, SocketRemoteUserContext.Default.SocketRemoteUser); + else return Server._user; + } + + internal void decrypt(string? key) + { + if (string.IsNullOrEmpty(key)) throw new ArgumentNullException(nameof(key)); + Context = Encryption.Encoder.GetString(Encryption.Decrypt(Convert.FromBase64String(Context), key)); + if (Files is not null && Files.Length > 0) + { + for (int i = 0; i < Files.Length; i++) + { + Files[i].key = key; + Files[i].decrypt(); + } + } + } + internal static async Task GetMessage(long id) + { + SocketMessage message; + while (true) + { + if (Server.CanRequest) + { + message = await Server.GetFromServer("socketmessage", + SocketMessageContext.Default.SocketMessage, + new System.Collections.Generic.KeyValuePair("msg_id", id.ToString())); + break; + } + } + if (message is not null) return message; + throw new Exception("Server did not return a message"); + } +} + +[JsonSerializable(typeof(SocketMessage))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + WriteIndented = false)] +public partial class SocketMessageContext : JsonSerializerContext +{ + +} diff --git a/Luski.net/JsonTypes/SocketRemoteUser.cs b/Luski.net/JsonTypes/SocketRemoteUser.cs new file mode 100755 index 0000000..11277b4 --- /dev/null +++ b/Luski.net/JsonTypes/SocketRemoteUser.cs @@ -0,0 +1,35 @@ +using Luski.net.Enums; +using Luski.net.Interfaces; +using Luski.net.JsonTypes.BaseTypes; +using System; +using System.Linq; +using System.Net.Http; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace Luski.net.JsonTypes; + +public class SocketRemoteUser : SocketUserBase +{ + [JsonPropertyName("friend_status")] + [JsonInclude] + public FriendStatus FriendStatus { get; internal set; } = default!; + [JsonIgnore] + public SocketDMChannel Channel { get; internal set; } = default!; + + internal SocketRemoteUser Clone() + { + return (SocketRemoteUser)MemberwiseClone(); + } +} + +[JsonSerializable(typeof(SocketRemoteUser))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + WriteIndented = false)] +internal partial class SocketRemoteUserContext : JsonSerializerContext +{ + +} diff --git a/Luski.net/JsonTypes/SocketTextChannel.cs b/Luski.net/JsonTypes/SocketTextChannel.cs new file mode 100755 index 0000000..5b61339 --- /dev/null +++ b/Luski.net/JsonTypes/SocketTextChannel.cs @@ -0,0 +1,168 @@ +using Luski.net.Enums; +using Luski.net.Interfaces; +using Luski.net.JsonTypes.BaseTypes; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace Luski.net.JsonTypes; + +public class SocketTextChannel : SocketChannel, ITextChannel +{ + public async Task GetMessage(long ID) + { + return await SocketMessage.GetMessage(ID); + } + + public async Task GetPicture() + { + if (Type == ChannelType.DM) return Members.First().GetAvatar().Result; + else + { + if (Server.Cache != null) + { + bool isc = System.IO.File.Exists($"{Server.Cache}/channels/{Id}"); + if (!isc) await Server.GetFromServer($"SocketChannel/GetPicture/{Id}", $"{Server.Cache}/channels/{Id}"); + } + return System.IO.File.ReadAllBytes($"{Server.Cache}/channels/{Id}"); + } + } + + public async Task> GetMessages(long Message_Id, int count = 50) + { + if (count > 200) + { + throw new Exception("You can not request more than 200 messages at a time"); + } + else if (count < 1) + { + throw new Exception("You must request at least 1 message"); + } + else + { + SocketBulkMessage data = await Server.GetFromServer("SocketBulkMessage", + SocketBulkMessageContext.Default.SocketBulkMessage, + new KeyValuePair("channel_id", Id.ToString()), + new KeyValuePair("messages", count.ToString()), + new KeyValuePair("mostrecentid", Message_Id.ToString())); + if (data.Error is null) + { + int num = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * Server.Percent) * 2.0)); + if (num == 0) num = 1; + + string? key = Encryption.File.Channels.GetKey(Id); + if (data is null) throw new Exception("Invalid data from server"); + if (data.Messages is null) data.Messages = Array.Empty(); + Parallel.ForEach(data.Messages, new ParallelOptions() + { + MaxDegreeOfParallelism = num + }, i => + { + i.decrypt(key); + }); + key = null; + return await Task.FromResult(data.Messages.ToList().AsReadOnly() as IReadOnlyList); + } + else + { + throw new Exception(data.ErrorMessage); + } + } + } + + public async Task> GetMessages(int count = 50) + { + try + { + if (count > 200) + { + throw new Exception("You can not request more than 200 messages at a time"); + } + else if (count < 1) + { + throw new Exception("You must request at least 1 message"); + } + else + { + SocketBulkMessage data = await Server.GetFromServer("SocketBulkMessage", + SocketBulkMessageContext.Default.SocketBulkMessage, + new KeyValuePair("id", Id.ToString()), + new KeyValuePair("messages", count.ToString())); + if (data is not null && !data.Error.HasValue) + { + int num = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * Server.Percent) * 2.0)); + if (num == 0) num = 1; + string? key = Encryption.File.Channels.GetKey(Id); + if (data.Messages is null) data.Messages = Array.Empty(); + Parallel.ForEach(data.Messages, new ParallelOptions() + { + MaxDegreeOfParallelism = num + }, i => + { + i.decrypt(key); + }); + key = null; + return await Task.FromResult(data.Messages.ToList().AsReadOnly() as IReadOnlyList); + } + else + { + throw data?.Error switch + { + ErrorCode.InvalidToken => new Exception("Your current token is no longer valid"), + ErrorCode.ServerError => new Exception($"Error from server: {data.ErrorMessage}"), + ErrorCode.InvalidHeader => new Exception(data.ErrorMessage), + ErrorCode.MissingHeader => new Exception("The header sent to the server was not found. This may be because you app is couropt or you are using the wron API version"), + ErrorCode.Forbidden => new Exception("You are not allowed to do this request"), + _ => new Exception(data?.Error.ToString()), + }; + } + } + } + catch (Exception) + { + throw; + } + } + + public async Task SendMessage(string Message, params File?[] Files) + { + string key = Encryption.File.Channels.GetKey(Id); + if (Id == 0) key = Encryption.ServerPublicKey; + HTTP.Message m = new() + { + Context = Convert.ToBase64String(Encryption.Encrypt(Message, key)), + Channel = Id, + }; + if (Files is not null && Files.Length > 0) + { + List bb = new(); + for (int i = 0; i < Files.Length; i++) + { + File? ff = Files[i]; + if (ff is not null) + { + bb.Add(await ff.Upload(key)); + Files[i] = null; + } + } + m.Files = bb.ToArray(); + } + IncomingHTTP data = await Server.SendServer("socketmessage", m, HTTP.MessageContext.Default.Message, IncomingHTTPContext.Default.IncomingHTTP); + if (data.Error is not null && data.ErrorMessage != "Server responded with empty data") throw new Exception(data.ErrorMessage); + return Task.CompletedTask; + } +} + +[JsonSerializable(typeof(SocketTextChannel))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + WriteIndented = false)] +internal partial class SocketTextChannelContext : JsonSerializerContext +{ + +} diff --git a/Luski.net/JsonTypes/StatusUpdate.cs b/Luski.net/JsonTypes/StatusUpdate.cs new file mode 100755 index 0000000..7719a7d --- /dev/null +++ b/Luski.net/JsonTypes/StatusUpdate.cs @@ -0,0 +1,17 @@ +using Luski.net.Enums; +using System.Text.Json.Serialization; + +namespace Luski.net.JsonTypes; + +internal class StatusUpdate +{ + [JsonInclude] + [JsonPropertyName("id")] + public long id { get; set; } = default!; + [JsonInclude] + [JsonPropertyName("before")] + public UserStatus before { get; set; } = default!; + [JsonInclude] + [JsonPropertyName("after")] + public UserStatus after { get; set; } = default!; +} diff --git a/Luski.net/JsonTypes/WSS/WSSKeyExchange.cs b/Luski.net/JsonTypes/WSS/WSSKeyExchange.cs new file mode 100755 index 0000000..fd18e13 --- /dev/null +++ b/Luski.net/JsonTypes/WSS/WSSKeyExchange.cs @@ -0,0 +1,31 @@ +using Luski.net.Enums; +using Luski.net.JsonTypes.BaseTypes; +using System.Text.Json.Serialization; + +namespace Luski.net.JsonTypes.WSS; + +internal class WSSKeyExchange : IncomingWSS +{ + [JsonPropertyName("type")] + [JsonInclude] + new public DataType? Type { get; set; } = DataType.Key_Exchange; + [JsonPropertyName("channel")] + [JsonInclude] + public long channel { get; set; } = default!; + [JsonPropertyName("key")] + [JsonInclude] + public string key { get; set; } = default!; + [JsonPropertyName("to")] + [JsonInclude] + public long? to { get; set; } = default!; +} + +[JsonSerializable(typeof(WSSKeyExchange))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + WriteIndented = false)] +internal partial class WSSKeyExchangeContext : JsonSerializerContext +{ + +} diff --git a/Luski.net/JsonTypes/WSS/WSSLogin.cs b/Luski.net/JsonTypes/WSS/WSSLogin.cs new file mode 100755 index 0000000..d50b8bc --- /dev/null +++ b/Luski.net/JsonTypes/WSS/WSSLogin.cs @@ -0,0 +1,25 @@ +using Luski.net.Enums; +using Luski.net.JsonTypes.BaseTypes; +using System.Text.Json.Serialization; + +namespace Luski.net.JsonTypes.WSS; + +internal class WSSLogin : IncomingWSS +{ + [JsonPropertyName("token")] + [JsonInclude] + public string Token { get; set; } = default!; + [JsonPropertyName("type")] + [JsonInclude] + new public DataType? Type { get; set; } = DataType.Login; +} + +[JsonSerializable(typeof(WSSLogin))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + WriteIndented = false)] +internal partial class WSSLoginContext : JsonSerializerContext +{ + +} diff --git a/Luski.net/Luski.net.csproj b/Luski.net/Luski.net.csproj new file mode 100755 index 0000000..87b0f19 --- /dev/null +++ b/Luski.net/Luski.net.csproj @@ -0,0 +1,33 @@ + + + + net6.0 + disable + enable + true + Luski.net + JacobTech + JacobTech, LLC + A wrapper for the luski API + https://www.jacobtech.com/Luski/Documentation + https://github.com/JacobTech-com/Luski.net + True + 1.0.0 + 1.1.4-alpha + + + + + + + + + + + + + + + + + diff --git a/Luski.net/Luski.net.csproj.user b/Luski.net/Luski.net.csproj.user new file mode 100755 index 0000000..c5a8f00 --- /dev/null +++ b/Luski.net/Luski.net.csproj.user @@ -0,0 +1,6 @@ + + + + <_LastSelectedProfileId>C:\Users\techn\source\repos\JacobTech-com\Luski.net\Luski.net\Luski.net\Properties\PublishProfiles\FolderProfile.pubxml + + \ No newline at end of file diff --git a/Luski.net/Properties/PublishProfiles/FolderProfile.pubxml b/Luski.net/Properties/PublishProfiles/FolderProfile.pubxml new file mode 100755 index 0000000..154e035 --- /dev/null +++ b/Luski.net/Properties/PublishProfiles/FolderProfile.pubxml @@ -0,0 +1,13 @@ + + + + + Release + Any CPU + bin\Release\net6.0\publish\ + FileSystem + <_TargetId>Folder + + \ No newline at end of file diff --git a/Luski.net/Properties/PublishProfiles/FolderProfile.pubxml.user b/Luski.net/Properties/PublishProfiles/FolderProfile.pubxml.user new file mode 100755 index 0000000..d26c1f6 --- /dev/null +++ b/Luski.net/Properties/PublishProfiles/FolderProfile.pubxml.user @@ -0,0 +1,10 @@ + + + + + True|2022-11-23T16:09:01.7347068Z;True|2022-11-23T11:07:47.9880607-05:00;True|2022-11-23T11:07:08.8325322-05:00;True|2022-11-23T11:05:40.5859900-05:00;True|2022-09-21T18:57:48.1433890-04:00;False|2022-09-21T18:56:37.2624157-04:00;True|2022-07-05T22:55:54.9271108-04:00; + + + \ No newline at end of file diff --git a/Luski.net/Server.Cleanup.cs b/Luski.net/Server.Cleanup.cs new file mode 100755 index 0000000..8fbc84c --- /dev/null +++ b/Luski.net/Server.Cleanup.cs @@ -0,0 +1,17 @@ +using System; +using System.IO; + +namespace Luski.net; + +public sealed partial class Server : IDisposable +{ + ~Server() + { + try { if (Directory.Exists(Cache)) Directory.Delete(Cache, true); } catch { } + } + + public void Dispose() + { + try { if (Directory.Exists(Cache)) Directory.Delete(Cache, true); } catch { } + } +} diff --git a/Luski.net/Server.Constructors.cs b/Luski.net/Server.Constructors.cs new file mode 100755 index 0000000..6cce45f --- /dev/null +++ b/Luski.net/Server.Constructors.cs @@ -0,0 +1,129 @@ +using Luski.net.Enums; +using Luski.net.JsonTypes; +using Luski.net.JsonTypes.BaseTypes; +using Luski.net.JsonTypes.WSS; +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Text; +using WebSocketSharp; +using File = System.IO.File; + +namespace Luski.net; + +public sealed partial class Server +{ + internal Server(string Email, string Password, Branch branch = Branch.Master, string? Username = null, string? pfp = null) + { + if (!Encryption.Generating) + { + Encryption.GenerateKeys(); + } + while (!Encryption.Generated) { } + InternalDomain = $"api.{branch}.luski.JacobTech.com"; + Branch = branch; + login = true; + Login json; + List> heads = new() + { + new("key", Encryption.MyPublicKey), + new("email", Convert.ToBase64String(Encryption.Encrypt(Email))), + new("password", Encryption.RemotePasswordEncrypt(Encryption.Encoder.GetBytes(Password))) + }; + if (File.Exists("LastPassVer.txt") && int.TryParse(File.ReadAllText("LastPassVer.txt"), out int lpv) && lpv < Encryption.PasswordVersion && lpv >= 0) + { + heads.Add(new("old_password", Encryption.RemotePasswordEncrypt(Encryption.Encoder.GetBytes(Password), lpv))); + heads.Add(new("old_version", lpv.ToString())); + } + if (pfp is not null) + { + heads.Add(new("username", Username)); + json = SendServer( + "CreateAccount", + pfp, + LoginContext.Default.Login, + heads.ToArray()).Result; + } + else + { + json = GetFromServer( + "Login", + LoginContext.Default.Login, + heads.ToArray()).Result; + } + + if (json.Error is not null) throw new Exception($"Luski appears to be down at the current moment: {json.ErrorMessage}"); + if (Encryption.ofkey is null || Encryption.outofkey is null) throw new Exception("Something went wrong generating the offline keys"); + login = false; + if (json is not null && json.Error is null) + { + ServerOut = new WebSocket($"wss://{InternalDomain}/WSS/{API_Ver}"); + ServerOut.SslConfiguration.EnabledSslProtocols = System.Security.Authentication.SslProtocols.Tls13; + ServerOut.OnMessage += DataFromServer; + ServerOut.WaitTime = new TimeSpan(0, 0, 5); + ServerOut.OnError += ServerOut_OnError; + ServerOut.Connect(); + string Infermation = $"{{\"token\": \"{json.Token}\"}}"; + SendServer(new WSSLogin() { Token = json.Token! }, WSSLoginContext.Default.WSSLogin); + while (Token is null && Error is null) + { + + } + if (Error is not null) + { + throw new Exception(Error); + } + if (Token is null) throw new Exception("Server did not send a token"); + CanRequest = true; + _user = SocketUserBase.GetUser(long.Parse(Encoding.UTF8.GetString(Convert.FromBase64String(Token.Split('.')[0]))), SocketAppUserContext.Default.SocketAppUser).Result; + if (_user is null || _user.Error is not null) throw new Exception("Something went wrong getting your user infermation"); + _ = _user.Channels; + foreach (var ch in chans) + { + _ = ch.Members; + } + _user.Email = Email; + _ = UpdateStatus(UserStatus.Online); + + try + { + Encryption.pw = Email.ToLower() + Password; + _ = Encryption.File.GetOfflineKey(); + } + catch + { + try + { + Encryption.pw = Email + Password; + var temp222 = Encryption.File.LuskiDataFile.GetDefualtDataFile(); + Encryption.pw = Email.ToLower() + Password; + if (temp222 is not null) temp222.Save(GetKeyFilePath, Encryption.pw); + } + catch + { + Token = null; + Error = null; + ServerOut.Close(); + throw new Exception("The key file you have is getting the wrong pasword. Type your Email in the same way you creaated your account to fix this error."); + } + } + OfflineKeyData offlinedata = GetFromServer("Keys/GetOfflineData", OfflineKeyDataContext.Default.OfflineKeyData).Result; + if (string.IsNullOrEmpty(Encryption.File.GetOfflineKey())) Encryption.File.SetOfflineKey(Encryption.ofkey); + if (offlinedata is not null && offlinedata.Error is null && offlinedata.keys is not null) + { + foreach (KeyExchange key in offlinedata.keys) + { + Encryption.File.Channels.AddKey(key.channel, Encryption.Encoder.GetString(Encryption.Decrypt(Convert.FromBase64String(key.key), Encryption.File.GetOfflineKey()))); + } + } + System.IO.File.WriteAllText("LastPassVer.txt", Encryption.PasswordVersion.ToString()); + Encryption.File.SetOfflineKey(Encryption.ofkey); + using HttpClient setkey = new(); + setkey.DefaultRequestHeaders.Add("token", Token); + _ = setkey.PostAsync($"https://{InternalDomain}/{API_Ver}/Keys/SetOfflineKey", new StringContent(Encryption.outofkey)).Result; + Encryption.outofkey = null; + Encryption.ofkey = null; + } + else throw new Exception(json?.ErrorMessage); + } +} diff --git a/Luski.net/Server.CreateAccount.cs b/Luski.net/Server.CreateAccount.cs new file mode 100755 index 0000000..8dd53b7 --- /dev/null +++ b/Luski.net/Server.CreateAccount.cs @@ -0,0 +1,93 @@ +using System; +using System.Net.Http; +using System.Text; +using System.Text.Json; +using Luski.net.Enums; +using Luski.net.JsonTypes; +using Luski.net.JsonTypes.WSS; +using WebSocketSharp; + +namespace Luski.net; + +public sealed partial class Server +{ + internal Server(string Email, string Password, string Username, byte[] PFP, Branch branch = Branch.Master) + { + Encryption.pw = Email.ToLower() + Password; + if (!Encryption.Generating) + { + Encryption.GenerateKeys(); + } + while (!Encryption.Generated) { } + if (Encryption.ofkey is null || Encryption.outofkey is null) throw new Exception("Something went wrong generating the offline keys"); + string Result; + InternalDomain = $"api.{branch}.luski.JacobTech.com"; + Branch = branch; + using (HttpClient web = new()) + { + web.DefaultRequestHeaders.Add("key", Encryption.MyPublicKey); + web.DefaultRequestHeaders.Add("email", Convert.ToBase64String(Encryption.Encrypt(Email))); + web.DefaultRequestHeaders.Add("password", Convert.ToBase64String(Encryption.Encrypt(Password))); + web.DefaultRequestHeaders.Add("username", Username); + HttpResponseMessage? d = web.PostAsync($"https://{InternalDomain}/{API_Ver}/CreateAccount", new StringContent(Convert.ToBase64String(PFP))).Result; + if (d is null || !d.IsSuccessStatusCode) throw new Exception("Luski appears to be down at the current moment"); + Result = d.Content.ReadAsStringAsync().Result; + web.DefaultRequestHeaders.Clear(); + } + Login? json = JsonSerializer.Deserialize(Result, LoginContext.Default.Login); + if (json is not null && json.Error is null) + { + ServerOut = new WebSocket($"wss://{InternalDomain}/{API_Ver}"); + ServerOut.SslConfiguration.EnabledSslProtocols = System.Security.Authentication.SslProtocols.Tls13; + ServerOut.OnMessage += DataFromServer; + ServerOut.WaitTime = new TimeSpan(0, 0, 5); + ServerOut.OnError += ServerOut_OnError; + ServerOut.Connect(); + string Infermation = $"{{\"token\": \"{json.Token}\"}}"; + SendServer(new WSSLogin() { Token = json.Token}, WSSLoginContext.Default.WSSLogin); + while (Token is null && Error is null) + { + + } + if (Error is not null) + { + throw new Exception(Error); + } + if (Token is null) throw new Exception("Server did not send a token"); + CanRequest = true; + string data; + using (HttpClient web = new()) + { + web.DefaultRequestHeaders.Add("token", Token); + web.DefaultRequestHeaders.Add("id", Encoding.UTF8.GetString(Convert.FromBase64String(Token.Split('.')[0]))); + data = web.GetAsync($"https://{InternalDomain}/{API_Ver}/SocketUser").Result.Content.ReadAsStringAsync().Result; + } + _user = JsonSerializer.Deserialize(data); + if (_user is null || _user.Error is not null) throw new Exception("Something went wrong getting your user infermation"); + _ = _user.Channels; + foreach (var ch in chans) + { + _ = ch.Members; + } + _user.Email = Email; + UpdateStatus(UserStatus.Online); + Encryption.File.SetOfflineKey(Encryption.ofkey); + using HttpClient setkey = new(); + setkey.DefaultRequestHeaders.Add("token", Token); + _ = setkey.PostAsync($"https://{InternalDomain}/{API_Ver}/Keys/SetOfflineKey", new StringContent(Encryption.outofkey)).Result; + Encryption.outofkey = null; + Encryption.ofkey = null; + } + else throw new Exception(json?.ErrorMessage); + } + + public static Server CreateAccount(string Email, string Password, string Username, byte[] PFP, Branch branch = Branch.Master) + { + return new Server(Email, Password, Username, PFP, branch); + } + + public static Server CreateAccount(string Email, string Password, string Username, string PFP, Branch branch = Branch.Master) + { + return new Server(Email, Password, branch, Username, PFP); + } +} diff --git a/Luski.net/Server.Events.cs b/Luski.net/Server.Events.cs new file mode 100755 index 0000000..8ed8955 --- /dev/null +++ b/Luski.net/Server.Events.cs @@ -0,0 +1,21 @@ +using Luski.net.Interfaces; +using Luski.net.JsonTypes; +using System; +using System.Threading.Tasks; + +namespace Luski.net; + +public sealed partial class Server +{ + public event Func? MessageReceived; + + public event Func? UserStatusUpdate; + + public event Func? ReceivedFriendRequest; + + public event Func? FriendRequestResult; + + public event Func? IncommingCall; + + public event Func? OnError; +} diff --git a/Luski.net/Server.Globals.cs b/Luski.net/Server.Globals.cs new file mode 100755 index 0000000..0832213 --- /dev/null +++ b/Luski.net/Server.Globals.cs @@ -0,0 +1,83 @@ +using Luski.net.Enums; +using Luski.net.Interfaces; +using Luski.net.JsonTypes; +using Luski.net.Sockets; +using System; +using System.Collections.Generic; +using System.IO; +using WebSocketSharp; + +namespace Luski.net; + +public sealed partial class Server +{ + internal static string JT { get { return Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "/JacobTech"; } } + internal static SocketAudioClient? AudioClient = null; + internal static string? Token = null, Error = null; + internal static bool CanRequest = false; + internal static SocketAppUser? _user; + internal static string InternalDomain = "api.master.luski.jacobtech.com", platform = "win-x64"; + internal static Branch Branch; + internal static double Percent = 0.5; + private static WebSocket? ServerOut; + private static string? gen = null; + private static bool login = false; + + public string Domain { get { return InternalDomain; } } + + internal static string Cache + { + get + { + if (gen is null) + { + if (!Directory.Exists(JT)) Directory.CreateDirectory(JT); + string path = JT + "/Luski/"; + if (!Directory.Exists(path)) Directory.CreateDirectory(path); + path += Branch.ToString() + "/"; + if (!Directory.Exists(path)) Directory.CreateDirectory(path); + path += platform + "/"; + if (!Directory.Exists(path)) Directory.CreateDirectory(path); + path += "Data/"; + if (!Directory.Exists(path)) Directory.CreateDirectory(path); + path += _user?.Id + "/"; + if (!Directory.Exists(path)) Directory.CreateDirectory(path); + path += "Cache/"; + if (!Directory.Exists(path)) Directory.CreateDirectory(path); + path += Path.GetRandomFileName() + "/"; + if (!Directory.Exists(path)) Directory.CreateDirectory(path); + gen = path; + } + if (!Directory.Exists($"{gen}/avatars")) Directory.CreateDirectory($"{gen}/avatars"); + if (!Directory.Exists($"{gen}/channels")) Directory.CreateDirectory($"{gen}/channels"); + return gen; + } + } + internal const string API_Ver = "v1"; + internal static List poeople = new(); + internal static List chans { get; set; } = new(); + internal static string GetKeyFilePath + { + get + { + return GetKeyFilePathBr(Branch.ToString()); + } + } + + internal static string GetKeyFilePathBr(string br) + { + if (!Directory.Exists(JT)) Directory.CreateDirectory(JT); + string path = JT + "/Luski/"; + if (!Directory.Exists(path)) Directory.CreateDirectory(path); + path += br + "/"; + if (!Directory.Exists(path)) Directory.CreateDirectory(path); + path += platform + "/"; + if (!Directory.Exists(path)) Directory.CreateDirectory(path); + path += "Data/"; + if (!Directory.Exists(path)) Directory.CreateDirectory(path); + path += _user?.Id + "/"; + if (!Directory.Exists(path)) Directory.CreateDirectory(path); + path += "keys.lsk"; + return path; + } +} diff --git a/Luski.net/Server.Incoming.cs b/Luski.net/Server.Incoming.cs new file mode 100755 index 0000000..cb5aae3 --- /dev/null +++ b/Luski.net/Server.Incoming.cs @@ -0,0 +1,118 @@ +using Luski.net.Enums; +using Luski.net.JsonTypes; +using Luski.net.JsonTypes.BaseTypes; +using Luski.net.JsonTypes.HTTP; +using Luski.net.JsonTypes.WSS; +using System; +using System.Text.Json; +using WebSocketSharp; + +namespace Luski.net +{ + public sealed partial class Server + { + private void DataFromServer(object? sender, MessageEventArgs e) + { + if (e.IsPing) return; + IncomingWSS? data = JsonSerializer.Deserialize(e.Data, IncomingWSSContext.Default.IncomingWSS); + switch (data?.Type) + { + case DataType.Login: + WSSLogin n = JsonSerializer.Deserialize(e.Data, WSSLoginContext.Default.WSSLogin)!; + Token = n.Token; + break; + case DataType.Error: + if (Token is null) + { + Error = data.Error; + } + else + { + if (OnError is not null) + { + _ = OnError.Invoke(new Exception(data.Error)); + } + } + break; + case DataType.Message_Create: + if (MessageReceived is not null) + { + SocketMessage? m = JsonSerializer.Deserialize(e.Data); + if (m is not null) + { + m.decrypt(Encryption.File.Channels.GetKey(m.ChannelID)); + _ = MessageReceived.Invoke(m); + } + } + break; + case DataType.Status_Update: + if (UserStatusUpdate is not null) + { + StatusUpdate? SU = JsonSerializer.Deserialize(e.Data); + if (SU is not null) + { + SocketRemoteUser after = SocketUserBase.GetUser(SU.id, SocketRemoteUserContext.Default.SocketRemoteUser).Result; + after.Status = SU.after; + SocketRemoteUser before = after.Clone(); + before.Status = SU.before; + _ = UserStatusUpdate.Invoke(before, after); + } + } + break; + case DataType.Friend_Request: + if (ReceivedFriendRequest is not null) + { + FriendRequest? request = JsonSerializer.Deserialize(e.Data, FriendRequestContext.Default.FriendRequest); + if (request is not null) _ = ReceivedFriendRequest.Invoke(SocketUserBase.GetUser(request.Id, SocketRemoteUserContext.Default.SocketRemoteUser).Result); + } + break; + case DataType.Friend_Request_Result: + if (FriendRequestResult is not null) + { + FriendRequestResult? FRR = JsonSerializer.Deserialize(e.Data); + if (FRR is not null && FRR.Channel is not null && FRR.Id is not null && FRR.Result is not null) + { + SocketDMChannel chan = SocketChannel.GetChannel((long)FRR.Channel, SocketDMChannelContext.Default.SocketDMChannel).Result; + chans.Add(chan); + SocketRemoteUser from1 = SocketUserBase.GetUser((long)FRR.Id, SocketRemoteUserContext.Default.SocketRemoteUser).Result; + from1.Channel = chan; + _ = FriendRequestResult.Invoke(from1, (bool)FRR.Result); + } + } + break; + case DataType.Call_Info: + if (IncommingCall is not null) + { + callinfoinc? ci = JsonSerializer.Deserialize(e.Data); + if (ci is not null) _ = IncommingCall.Invoke(SocketChannel.GetChannel(ci.channel, SocketTextChannelContext.Default.SocketTextChannel).Result, SocketUserBase.GetUser(ci.from, SocketRemoteUserContext.Default.SocketRemoteUser).Result); + } + break; + case DataType.Call_Data: + if (AudioClient is not null) + { + AudioClient.Givedata(e.Data); + } + break; + case DataType.Key_Exchange: + try + { + KeyExchange? KE = JsonSerializer.Deserialize(e.Data); + if (KE is not null) Encryption.File.Channels.AddKey(KE.channel, Encryption.Encoder.GetString(Encryption.Decrypt(Convert.FromBase64String(KE.key)))); + } + catch (Exception ex) + { + if (OnError is not null) OnError.Invoke(ex); + } + break; + default: + break; + } + } + + private class callinfoinc + { + public long channel { get; set; } = default!; + public long from { get; set; } = default!; + } + } +} diff --git a/Luski.net/Server.Login.cs b/Luski.net/Server.Login.cs new file mode 100755 index 0000000..e0b1596 --- /dev/null +++ b/Luski.net/Server.Login.cs @@ -0,0 +1,12 @@ +using Luski.net.Enums; +using System.Threading.Tasks; + +namespace Luski.net; + +public sealed partial class Server +{ + public static async Task Login(string Email, string Password, Branch branch = Branch.Master) + { + return new Server(Email, Password, branch); + } +} diff --git a/Luski.net/Server.cs b/Luski.net/Server.cs new file mode 100755 index 0000000..b6cfbe9 --- /dev/null +++ b/Luski.net/Server.cs @@ -0,0 +1,298 @@ +using Luski.net.Enums; +using Luski.net.Interfaces; +using Luski.net.JsonTypes; +using Luski.net.JsonTypes.BaseTypes; +using Luski.net.JsonTypes.HTTP; +using Luski.net.Sockets; +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Net.Http; +using System.Net.Http.Json; +using System.Text.Json; +using System.Text.Json.Serialization.Metadata; +using System.Threading.Tasks; + +namespace Luski.net; + +public sealed partial class Server +{ +#pragma warning disable CA1822 // Mark members as static + /// + /// Creates an audio client for the you want to talk on + /// + /// The channel you want to talk on + /// + public IAudioClient CreateAudioClient(long channel_id) + { + // if (AudioClient != null) throw new Exception("audio client alread created"); + SocketAudioClient client = new(channel_id, OnError); + AudioClient = client; + return client; + } + + public async Task SendFriendResult(long user, bool answer) + { + + FriendRequestResult json = await SendServer("FriendRequestResult", + new FriendRequestResultOut() + { + Id = user, + Result = answer + }, + FriendRequestResultOutContext.Default.FriendRequestResultOut, + FriendRequestResultContext.Default.FriendRequestResult); + + if (json is not null && json.Error is null && json.ErrorMessage is null && answer && json.Channel is not null) + { + SocketDMChannel chan = await SocketChannel.GetChannel((long)json.Channel, SocketDMChannelContext.Default.SocketDMChannel); + _ = chan.StartKeyProcessAsync(); + chans.Add(chan); + } + else + { + throw new Exception(json?.Error.ToString()); + } + return SocketUserBase.GetUser(user, SocketRemoteUserContext.Default.SocketRemoteUser).Result; + } + + public async Task SendFriendRequest(long user) + { + FriendRequestResult? json = await SendServer("FriendRequest", new FriendRequest() { Id = user, SubType = 0 }, FriendRequestContext.Default.FriendRequest, FriendRequestResultContext.Default.FriendRequestResult); + + if (json.StatusCode != HttpStatusCode.Accepted) + { + if (json is not null && json.Error is not null) + { + switch ((ErrorCode)(int)json.Error) + { + case ErrorCode.InvalidToken: + throw new Exception("Your current token is no longer valid"); + case ErrorCode.ServerError: + throw new Exception($"Error from server: {json.ErrorMessage}"); + case ErrorCode.InvalidPostData: + throw new Exception("The post data dent to the server is not the correct format. This may be because you app is couropt or you are using the wron API version"); + case ErrorCode.Forbidden: + throw new Exception("You already have an outgoing request or the persone is not real"); + } + } + + if (json is not null && json.Channel is not null) + { + SocketDMChannel chan = await SocketChannel.GetChannel((long)json.Channel, (JsonTypeInfo)SocketDMChannelContext.Default.SocketDMChannel); + _ = chan.StartKeyProcessAsync(); + chans.Add(chan); + } + } + + SocketRemoteUser b = await SocketUserBase.GetUser(user, SocketRemoteUserContext.Default.SocketRemoteUser); + b.FriendStatus = FriendStatus.PendingOut; + return b; + } + + public async Task SendFriendRequest(string username, short tag) + { + FriendRequestResult json = await SendServer("FriendRequest", new FriendRequest() { Username = username, Tag = tag, SubType = 1 }, FriendRequestContext.Default.FriendRequest, FriendRequestResultContext.Default.FriendRequestResult); + + if (json is not null && json.Error is not null) + { + throw (ErrorCode)(int)json.Error switch + { + ErrorCode.InvalidToken => new Exception("Your current token is no longer valid"), + ErrorCode.ServerError => new Exception("Error from server: " + json.ErrorMessage), + ErrorCode.InvalidPostData => new Exception("The post data dent to the server is not the correct format. This may be because you app is couropt or you are using the wron API version"), + ErrorCode.Forbidden => new Exception("You already have an outgoing request or the persone is not real"), + _ => new Exception(JsonSerializer.Serialize(json)), + }; + } + else if (json is not null && json.Channel is not null && json.Id is not null) + { + SocketDMChannel chan = await SocketChannel.GetChannel(json.Channel.Value, (JsonTypeInfo)SocketDMChannelContext.Default.SocketDMChannel); + _ = chan.StartKeyProcessAsync(); + chans.Add(chan); + return await SocketUserBase.GetUser((long)json.Id, SocketRemoteUserContext.Default.SocketRemoteUser); + } + else throw new Exception("missing data from server"); + } + + /// + /// Sends the server a request to update the of you account + /// + /// The you want to set your status to + /// + public async Task UpdateStatus(UserStatus Status) + { + if (_user is null) throw new Exception("You must login to make a request"); + IncomingHTTP? data = await SendServer("SocketUserProfile/Status", new Status() { UserStatus = Status }, StatusContext.Default.Status, IncomingHTTPContext.Default.IncomingHTTP); + if (data.Error is not null && ((int)data.StatusCode < 200 || (int)data.StatusCode > 299)) + { + if (data?.ErrorMessage is not null) throw new Exception(data.ErrorMessage); + if (data?.Error is not null) throw new Exception(((int)data.Error).ToString()); + else throw new Exception("Something went worng"); + } + + _user.Status = Status; + return Task.CompletedTask; + } + + public async Task ChangeChannel(long Channel) + { + if (_user is null) throw new Exception("You must login to make a request"); + IncomingHTTP? data = await SendServer("ChangeChannel", new Channel() { Id = Channel }, ChannelContext.Default.Channel, IncomingHTTPContext.Default.IncomingHTTP); + if (data.StatusCode != HttpStatusCode.Accepted) + { + if (data?.Error is not null) + { + switch (data.Error) + { + case ErrorCode.InvalidToken: + throw new Exception("Your current token is no longer valid"); + case ErrorCode.ServerError: + throw new Exception("Error from server: " + data.ErrorMessage); + } + } + else throw new Exception("Something went worng"); + } + + _user.SelectedChannel = Channel; + } + + public async Task SendMessage(string Message, long Channel, params JsonTypes.File[] Files) => (await GetChannel(Channel)).SendMessage(Message, Files); + + public void SetMultiThreadPercent(double num) + { + if (num < 1 || num > 100) throw new Exception("Number must be from 1 - 100"); + Percent = num / 100; + } + + public async Task GetMessage(long MessageId) => await SocketMessage.GetMessage(MessageId); + + public async Task GetUser(long UserID) => await SocketUserBase.GetUser(UserID, SocketRemoteUserContext.Default.SocketRemoteUser); + + public async Task GetChannel(long Channel) where TChannel : SocketChannel, new() + { + TChannel Return = new(); + switch (Return) + { + case SocketDMChannel: + Return = (await SocketChannel.GetChannel(Channel, SocketDMChannelContext.Default.SocketDMChannel) as TChannel)!; + break; + case SocketGroupChannel: + Return = (await SocketChannel.GetChannel(Channel, SocketGroupChannelContext.Default.SocketGroupChannel) as TChannel)!; + break; + case SocketTextChannel: + Return = (await SocketChannel.GetChannel(Channel, SocketTextChannelContext.Default.SocketTextChannel) as TChannel)!; + break; + case SocketChannel: + Return = (await SocketChannel.GetChannel(Channel, SocketChannelContext.Default.SocketChannel) as TChannel)!; + break; + case null: + throw new NullReferenceException(nameof(TChannel)); + default: + throw new Exception("Unknown channel type"); + } + return Return; + } + + + public SocketAppUser CurrentUser + { + get + { + if (_user is null) throw new Exception("You must Login first"); + return _user; + } + } +#pragma warning restore CA1822 // Mark members as static + + private void ServerOut_OnError(object? sender, WebSocketSharp.ErrorEventArgs e) + { + if (OnError is not null) OnError.Invoke(new Exception(e.Message)); + } + + [Obsolete("Move to new Data layout")] + internal static void SendServer(string data) + { + ServerOut?.Send(data); + } + + internal static void SendServer(Tvalue Payload, JsonTypeInfo jsonTypeInfo) where Tvalue : IncomingWSS + { + ServerOut?.Send(JsonSerializer.Serialize(Payload, jsonTypeInfo)); + } + + internal static HttpResponseMessage GetFromServer(string Path, params KeyValuePair[] Headers) + { + using HttpClient web = new(); + web.Timeout = TimeSpan.FromSeconds(10); + if (!login) web.DefaultRequestHeaders.Add("token", Token); + if (Headers is not null && Headers.Length > 0) foreach (KeyValuePair header in Headers) web.DefaultRequestHeaders.Add(header.Key, header.Value); + return web.GetAsync($"https://{InternalDomain}/{API_Ver}/{Path}").Result; + } + + internal static Task GetFromServer(string Path, string File, params KeyValuePair[] Headers) + { + using HttpClient web = new(); + web.Timeout = TimeSpan.FromMinutes(10); + if (!login) web.DefaultRequestHeaders.Add("token", Token); + if (Headers is not null && Headers.Length > 0) foreach (KeyValuePair header in Headers) web.DefaultRequestHeaders.Add(header.Key, header.Value); + HttpResponseMessage Response = web.GetAsync($"https://{InternalDomain}/{API_Ver}/{Path}").Result; + Stream stream = Response.Content.ReadAsStreamAsync().Result; + using FileStream fs = System.IO.File.Create(File); + stream.CopyTo(fs); + return Task.CompletedTask; + } + + internal static async Task GetFromServer(string Path, JsonTypeInfo Type, params KeyValuePair[] Headers) where Tresult : IncomingHTTP, new() + { + HttpResponseMessage ServerResponce = GetFromServer(Path, Headers); + if (!ServerResponce.IsSuccessStatusCode) return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with status code {(int)ServerResponce.StatusCode}:{ServerResponce.StatusCode}" }; + Tresult? temp = JsonSerializer.Deserialize(ServerResponce.Content.ReadAsStreamAsync().Result, Type); + if (temp is null) return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with empty data" }; + return temp; + } + + internal static async Task SendServer(string Path, Tvalue Payload, JsonTypeInfo jsonTypeInfo, JsonTypeInfo ReturnjsonTypeInfo, params KeyValuePair[] Headers) where Tvalue : HTTPRequest where Tresult : IncomingHTTP, new() + { + using HttpClient web = new(); + if (!login) web.DefaultRequestHeaders.Add("token", Token); + if (Headers is not null && Headers.Length > 0) foreach (KeyValuePair header in Headers) web.DefaultRequestHeaders.Add(header.Key, header.Value); + HttpResponseMessage ServerResponce = web.PostAsJsonAsync($"https://{InternalDomain}/{API_Ver}/{Path}", Payload, jsonTypeInfo).Result; + if (!ServerResponce.IsSuccessStatusCode) return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with status code {(int)ServerResponce.StatusCode}:{ServerResponce.StatusCode}" }; + Tresult error = new() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with empty data" }; + if (string.IsNullOrWhiteSpace(ServerResponce.Content.ReadAsStringAsync().Result)) return error; + try + { + Tresult? temp = JsonSerializer.Deserialize(ServerResponce.Content.ReadAsStreamAsync().Result, ReturnjsonTypeInfo); + if (temp is null) return error; + return temp; + } + catch { return error; } + } + + internal static async Task SendServer(string Path, string File, JsonTypeInfo ReturnjsonTypeInfo, params KeyValuePair[] Headers) where Tresult : IncomingHTTP, new() + { + var fs = System.IO.File.OpenRead(File); + try + { + using HttpClient web = new(); + if (!login) web.DefaultRequestHeaders.Add("token", Token); + web.Timeout = new TimeSpan(0, 10, 0); + if (Headers is not null && Headers.Length > 0) foreach (KeyValuePair header in Headers) web.DefaultRequestHeaders.Add(header.Key, header.Value); + HttpResponseMessage ServerResponce = web.PostAsync($"https://{InternalDomain}/{API_Ver}/{Path}", new StreamContent(fs)).Result; + if (!ServerResponce.IsSuccessStatusCode) return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with status code {(int)ServerResponce.StatusCode}:{ServerResponce.StatusCode}" }; + try + { + Tresult? temp = JsonSerializer.Deserialize(ServerResponce.Content.ReadAsStreamAsync().Result, ReturnjsonTypeInfo); + if (temp is null) return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with empty data" }; + return temp; + } + catch { return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with empty data" }; } + } + finally + { + fs.Close(); + } + } +} diff --git a/Luski.net/Sockets/SocketAudioClient.cs b/Luski.net/Sockets/SocketAudioClient.cs new file mode 100755 index 0000000..7388aaa --- /dev/null +++ b/Luski.net/Sockets/SocketAudioClient.cs @@ -0,0 +1,388 @@ +using Luski.net.Enums; +using Luski.net.Interfaces; +using Luski.net.JsonTypes.BaseTypes; +using Luski.net.Sound; +using System; +using System.Net.Http; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using static Luski.net.Exceptions; + +namespace Luski.net.Sockets +{ + internal class SocketAudioClient : IAudioClient + { + internal SocketAudioClient(long Channel, Func? error) + { + this.Channel = Channel; + errorin = error; + Muted = false; + PrototolClient.DataComplete += new Protocol.DelegateDataComplete(OnProtocolClient_DataComplete); + DataRecived += SocketAudioClient_DataRecived; + } + + public event Func? Connected; + + public bool Muted { get; private set; } + + public bool Deafened { get; private set; } + + public void ToggleMic() + { + if (Muted == true) + { + Muted = false; + } + else + { + Muted = true; + } + } + + public void ToggleAudio() + { + if (Deafened == true) + { + Deafened = false; + } + else + { + Deafened = true; + } + } + + public void RecordSoundFrom(RecordingDevice Device) + { + if (Connectedb) + { + StartRecordingFromSounddevice_Client(Device); + } + else + { + throw new NotConnectedException(this, "The call has not been connected yet!"); + } + } + + public void PlaySoundTo(PlaybackDevice Device) + { + if (Connectedb) + { + StartPlayingToSounddevice_Client(Device); + } + else + { + throw new NotConnectedException(this, "The call has not been connected yet!"); + } + } + + public void JoinCall() + { + if (Connected == null) + { + throw new MissingEventException("Connected"); + } + else + { + //get info + string data; + while (true) + { + if (Server.CanRequest) + { + using HttpClient web = new(); + web.DefaultRequestHeaders.Add("token", Server.Token); + web.DefaultRequestHeaders.Add("id", Channel.ToString()); + data = web.GetAsync($"https://{Server.InternalDomain}/{Server.API_Ver}/GetCallInfo").Result.Content.ReadAsStringAsync().Result; + break; + } + } + call? json = JsonSerializer.Deserialize(data); + Server.SendServer(JsonRequest.Send(DataType.Join_Call, JsonRequest.JoinCall(Channel)).ToString()); + Samples = json.samples; + } + } + + private class call : IncomingHTTP + { + public int samples { get; set; } = default!; + public string[] members { get; set; } = default!; + } + + public void LeaveCall() + { + Server.SendServer(JsonRequest.Send(DataType.Leave_Call, JsonRequest.JoinCall(Channel)).ToString()); + StopRecordingFromSounddevice_Client(); + } + + private readonly Protocol PrototolClient = new(ProtocolTypes.LH, Encoding.Default); + private JitterBuffer RecordingJitterBuffer = new(null, JitterBuffer, 20); + private JitterBuffer PlayingJitterBuffer = new(null, JitterBuffer, 20); + private readonly Func? errorin; + private event Func DataRecived; + private static readonly uint JitterBuffer = 5; + private readonly int BitsPerSample = 16; + private long SequenceNumber = 4596; + private readonly int Channels = 1; + private Recorder? RecorderClient; + private bool Connectedb = false; + private bool recording = false; + private long m_TimeStamp = 0; + private Player? PlayerClient; + private readonly long Channel; + + private void StopPlayingToSounddevice_Client() + { + if (PlayerClient != null) + { + PlayerClient.Close(); + PlayerClient = null; + } + + if (PlayingJitterBuffer != null) + { + PlayingJitterBuffer.Stop(); + } + } + + private async Task SocketAudioClient_DataRecived(string arg) + { + cdata d = JsonSerializer.Deserialize(arg); + byte[] data = Convert.FromBase64String(d.data); + PrototolClient.Receive_LH(this, data); + } + + private class cdata + { + public string data { get; set; } = default!; + public long from { get; set; } = default!; + } + + private void SendData(byte[] data) + { + if (!Connectedb) + { + return; + } + + Server.SendServer(JsonRequest.Send(DataType.Call_Data, JsonRequest.SendCallData(PrototolClient.ToBytes(data), Channel))); + } + + internal void Givedata(dynamic data) + { + DataRecived.Invoke(((object)data).ToString()); + } + + private int _samp; + + internal int Samples + { + get => _samp; + set + { + _samp = value; + Connectedb = true; + if (Connected is not null) Connected.Invoke(); + PlaySoundTo(Devices.GetDefaltPlaybackDevice()); + RecordSoundFrom(Devices.GetDefaltRecordingDevice()); + } + } + + private bool playing = false; + + private void StartPlayingToSounddevice_Client(PlaybackDevice device) + { + if (playing) + { + StopPlayingToSounddevice_Client(); + } + playing = true; + if (PlayingJitterBuffer != null) + { + PlayingJitterBuffer.DataAvailable -= new JitterBuffer.DelegateDataAvailable(OnJitterBufferClientDataAvailablePlaying); + + PlayingJitterBuffer = new JitterBuffer(null, JitterBuffer, 20); + PlayingJitterBuffer.DataAvailable += new JitterBuffer.DelegateDataAvailable(OnJitterBufferClientDataAvailablePlaying); + PlayingJitterBuffer.Start(); + } + + if (PlayerClient == null) + { + PlayerClient = new Player(); + PlayerClient.Open(device.Name, Samples, BitsPerSample, Channels, (int)JitterBuffer); + } + } + + private void OnJitterBufferClientDataAvailablePlaying(object sender, RTPPacket rtp) + { + try + { + if (PlayerClient != null) + { + if (PlayerClient.Opened) + { + if (Deafened == false) + { + byte[] linearBytes = Utils.MuLawToLinear(rtp.Data, BitsPerSample, Channels); + PlayerClient.PlayData(linearBytes, false); + } + } + } + } + catch (Exception ex) + { + System.Diagnostics.StackFrame sf = new(true); + errorin?.Invoke(new Exception(string.Format("Exception: {0} StackTrace: {1}. FileName: {2} Method: {3} Line: {4}", ex.Message, ex.StackTrace, sf.GetFileName(), sf.GetMethod(), sf.GetFileLineNumber()))); + } + } + + private void StartRecordingFromSounddevice_Client(RecordingDevice device) + { + try + { + if (recording) + { + StopRecordingFromSounddevice_Client(); + } + recording = true; + InitJitterBufferClientRecording(); + int bufferSize = 0; + bufferSize = Utils.GetBytesPerInterval((uint)Samples, BitsPerSample, Channels) * 4; + + if (bufferSize > 0) + { + RecorderClient = new Recorder(); + RecorderClient.DataRecorded += new Recorder.DelegateDataRecorded(OnDataReceivedFromSoundcard_Client); + + if (RecorderClient.Start(device.Name, Samples, BitsPerSample, Channels, 8, bufferSize)) + { + + RecordingJitterBuffer.Start(); + } + } + } + catch (Exception ex) + { + errorin.Invoke(ex); + } + } + + private void StopRecordingFromSounddevice_Client() + { + RecorderClient.Stop(); + + RecorderClient.DataRecorded -= new Recorder.DelegateDataRecorded(OnDataReceivedFromSoundcard_Client); + RecorderClient = null; + + RecordingJitterBuffer.Stop(); + } + + private void InitJitterBufferClientRecording() + { + if (RecordingJitterBuffer != null) + { + RecordingJitterBuffer.DataAvailable -= new JitterBuffer.DelegateDataAvailable(OnJitterBufferClientDataAvailableRecording); + } + + RecordingJitterBuffer = new JitterBuffer(null, 8, 20); + RecordingJitterBuffer.DataAvailable += new JitterBuffer.DelegateDataAvailable(OnJitterBufferClientDataAvailableRecording); + } + + private void OnJitterBufferClientDataAvailableRecording(object sender, RTPPacket rtp) + { + if (Muted == false && rtp != null && rtp.Data != null && rtp.Data.Length > 0) + { + byte[] rtpBytes = rtp.ToBytes(); + SendData(rtpBytes); + } + } + + private void OnDataReceivedFromSoundcard_Client(byte[] data) + { + try + { + lock (this) + { + int bytesPerInterval = Utils.GetBytesPerInterval((uint)Samples, BitsPerSample, Channels); + int count = data.Length / bytesPerInterval; + int currentPos = 0; + for (int i = 0; i < count; i++) + { + byte[] partBytes = new byte[bytesPerInterval]; + Array.Copy(data, currentPos, partBytes, 0, bytesPerInterval); + currentPos += bytesPerInterval; + RTPPacket rtp = ToRTPPacket(partBytes, BitsPerSample, Channels); + + RecordingJitterBuffer.AddData(rtp); + } + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + } + } + + private RTPPacket ToRTPPacket(byte[] linearData, int bitsPerSample, int channels) + { + byte[] mulaws = Utils.LinearToMulaw(linearData, bitsPerSample, channels); + + RTPPacket rtp = new() + { + Data = mulaws, + CSRCCount = 0, + Extension = false, + HeaderLength = RTPPacket.MinHeaderLength, + Marker = false, + Padding = false, + PayloadType = 0, + Version = 2, + SourceId = 0 + }; + + try + { + rtp.SequenceNumber = Convert.ToUInt16(SequenceNumber); + SequenceNumber++; + } + catch (Exception) + { + SequenceNumber = 0; + } + try + { + rtp.Timestamp = Convert.ToUInt32(m_TimeStamp); + m_TimeStamp += mulaws.Length; + } + catch (Exception) + { + m_TimeStamp = 0; + } + + return rtp; + } + + private void OnProtocolClient_DataComplete(object sender, byte[] data) + { + try + { + if (PlayerClient != null && PlayerClient.Opened) + { + RTPPacket rtp = new(data); + + if (rtp.Data != null) + { + if (PlayingJitterBuffer != null) + { + PlayingJitterBuffer.AddData(rtp); + } + } + } + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + } + } +} diff --git a/Luski.net/Sound/Devices.cs b/Luski.net/Sound/Devices.cs new file mode 100755 index 0000000..8bf90f2 --- /dev/null +++ b/Luski.net/Sound/Devices.cs @@ -0,0 +1,58 @@ +using System.Collections.Generic; + +namespace Luski.net.Sound +{ + public static class Devices + { + public static RecordingDevice GetDefaltRecordingDevice() + { + return GetRecordingDevices()[0]; + } + + public static PlaybackDevice GetDefaltPlaybackDevice() + { + return GetPlaybackDevices()[0]; + } + + public static IReadOnlyList GetRecordingDevices() + { + List RecordingNames = WinSound.GetRecordingNames(); + List RecordingDevices = new(); + foreach (string Device in RecordingNames) + { + RecordingDevices.Add(new RecordingDevice(Device)); + } + return RecordingDevices.AsReadOnly(); + } + public static IReadOnlyList GetPlaybackDevices() + { + List PlaybackName = WinSound.GetPlaybackNames(); + List PlaybackDevices = new(); + foreach (string Device in PlaybackName) + { + PlaybackDevices.Add(new PlaybackDevice(Device)); + } + return PlaybackDevices.AsReadOnly(); + } + } + + public class RecordingDevice + { + internal RecordingDevice(string name) + { + Name = name; + } + + public string Name { get; } + } + + public class PlaybackDevice + { + internal PlaybackDevice(string name) + { + Name = name; + } + + public string Name { get; } + } +} diff --git a/Luski.net/Sound/JitterBuffer.cs b/Luski.net/Sound/JitterBuffer.cs new file mode 100755 index 0000000..27348ab --- /dev/null +++ b/Luski.net/Sound/JitterBuffer.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; + +namespace Luski.net.Sound +{ + internal class JitterBuffer + { + internal JitterBuffer(object? sender, uint maxRTPPackets, uint timerIntervalInMilliseconds) + { + if (maxRTPPackets < 2) + { + throw new Exception("Wrong Arguments. Minimum maxRTPPackets is 2"); + } + + m_Sender = sender; + Maximum = maxRTPPackets; + IntervalInMilliseconds = timerIntervalInMilliseconds; + + Init(); + } + + private readonly object? m_Sender = null; + private readonly EventTimer m_Timer = new(); + private readonly Queue m_Buffer = new(); + private RTPPacket m_LastRTPPacket = new(); + private bool m_Underflow = true; + private bool m_Overflow = false; + + internal delegate void DelegateDataAvailable(object sender, RTPPacket packet); + internal event DelegateDataAvailable? DataAvailable; + + internal uint Maximum { get; } = 10; + internal uint IntervalInMilliseconds { get; } = 20; + private void Init() + { + InitTimer(); + } + private void InitTimer() + { + m_Timer.TimerTick += new EventTimer.DelegateTimerTick(OnTimerTick); + } + internal void Start() + { + m_Timer.Start(IntervalInMilliseconds); + m_Underflow = true; + } + internal void Stop() + { + m_Timer.Stop(); + m_Buffer.Clear(); + } + private void OnTimerTick() + { + try + { + if (DataAvailable != null) + { + if (m_Buffer.Count > 0) + { + if (m_Overflow) + { + if (m_Buffer.Count <= Maximum / 2) + { + m_Overflow = false; + } + } + + if (m_Underflow) + { + if (m_Buffer.Count < Maximum / 2) + { + return; + } + else + { + m_Underflow = false; + } + } + + m_LastRTPPacket = m_Buffer.Dequeue(); + DataAvailable(m_Sender, m_LastRTPPacket); + } + else + { + m_Overflow = false; + + if (m_LastRTPPacket != null && m_Underflow == false) + { + if (m_LastRTPPacket.Data != null) + { + m_Underflow = true; + } + } + } + } + } + catch (Exception ex) + { + Console.WriteLine(string.Format("JitterBuffer.cs | OnTimerTick() | {0}", ex.Message)); + } + } + internal void AddData(RTPPacket packet) + { + try + { + if (m_Overflow == false) + { + if (m_Buffer.Count <= Maximum) + { + m_Buffer.Enqueue(packet); + } + else + { + m_Overflow = true; + } + } + } + catch (Exception ex) + { + Console.WriteLine(string.Format("JitterBuffer.cs | AddData() | {0}", ex.Message)); + } + } + } +} diff --git a/Luski.net/Sound/Player.cs b/Luski.net/Sound/Player.cs new file mode 100755 index 0000000..173594d --- /dev/null +++ b/Luski.net/Sound/Player.cs @@ -0,0 +1,417 @@ +using System; +using System.Runtime.InteropServices; +using System.Threading; + +namespace Luski.net.Sound +{ + internal unsafe class Player + { + internal Player() + { + + delegateWaveOutProc = new Win32.DelegateWaveOutProc(WaveOutProc); + } + + private readonly LockerClass Locker = new(); + private readonly LockerClass LockerCopy = new(); + private IntPtr hWaveOut = IntPtr.Zero; + private string WaveOutDeviceName = ""; + private bool IsWaveOutOpened = false; + private bool IsThreadPlayWaveOutRunning = false; + private bool IsClosed = false; + private bool IsPaused = false; + private bool IsStarted = false; + private bool IsBlocking = false; + private int SamplesPerSecond = 8000; + private int BitsPerSample = 16; + private int Channels = 1; + private int BufferCount = 8; + private readonly int BufferLength = 1024; + private Win32.WAVEHDR*[] WaveOutHeaders; + private readonly Win32.DelegateWaveOutProc delegateWaveOutProc; + private Thread? ThreadPlayWaveOut; + private readonly AutoResetEvent AutoResetEventDataPlayed = new(false); + + internal delegate void DelegateStopped(); + internal event DelegateStopped? PlayerClosed; + internal event DelegateStopped? PlayerStopped; + + internal bool Opened => IsWaveOutOpened & IsClosed == false; + + internal bool Playing + { + get + { + if (Opened && IsStarted) + { + foreach (Win32.WAVEHDR* pHeader in WaveOutHeaders) + { + if (IsHeaderInqueue(*pHeader)) + { + return true; + } + } + } + return false; + } + } + + private bool CreateWaveOutHeaders() + { + WaveOutHeaders = new Win32.WAVEHDR*[BufferCount]; + int createdHeaders = 0; + + for (int i = 0; i < BufferCount; i++) + { + WaveOutHeaders[i] = (Win32.WAVEHDR*)Marshal.AllocHGlobal(sizeof(Win32.WAVEHDR)); + + WaveOutHeaders[i]->dwLoops = 0; + WaveOutHeaders[i]->dwUser = IntPtr.Zero; + WaveOutHeaders[i]->lpNext = IntPtr.Zero; + WaveOutHeaders[i]->reserved = IntPtr.Zero; + WaveOutHeaders[i]->lpData = Marshal.AllocHGlobal(BufferLength); + WaveOutHeaders[i]->dwBufferLength = (uint)BufferLength; + WaveOutHeaders[i]->dwBytesRecorded = 0; + WaveOutHeaders[i]->dwFlags = 0; + + Win32.MMRESULT hr = Win32.waveOutPrepareHeader(hWaveOut, WaveOutHeaders[i], sizeof(Win32.WAVEHDR)); + if (hr == Win32.MMRESULT.MMSYSERR_NOERROR) + { + createdHeaders++; + } + } + + return (createdHeaders == BufferCount); + } + + private void FreeWaveOutHeaders() + { + try + { + if (WaveOutHeaders != null) + { + for (int i = 0; i < WaveOutHeaders.Length; i++) + { + Win32.MMRESULT hr = Win32.waveOutUnprepareHeader(hWaveOut, WaveOutHeaders[i], sizeof(Win32.WAVEHDR)); + + int count = 0; + while (count <= 100 && (WaveOutHeaders[i]->dwFlags & Win32.WaveHdrFlags.WHDR_INQUEUE) == Win32.WaveHdrFlags.WHDR_INQUEUE) + { + Thread.Sleep(20); + count++; + } + + if ((WaveOutHeaders[i]->dwFlags & Win32.WaveHdrFlags.WHDR_INQUEUE) != Win32.WaveHdrFlags.WHDR_INQUEUE) + { + if (WaveOutHeaders[i]->lpData != IntPtr.Zero) + { + Marshal.FreeHGlobal(WaveOutHeaders[i]->lpData); + WaveOutHeaders[i]->lpData = IntPtr.Zero; + } + } + } + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.Write(ex.Message); + } + } + + private void StartThreadPlayWaveOut() + { + if (IsThreadPlayWaveOutRunning == false) + { + ThreadPlayWaveOut = new System.Threading.Thread(new System.Threading.ThreadStart(OnThreadPlayWaveOut)); + IsThreadPlayWaveOutRunning = true; + ThreadPlayWaveOut.Name = "PlayWaveOut"; + ThreadPlayWaveOut.Priority = System.Threading.ThreadPriority.Highest; + ThreadPlayWaveOut.Start(); + } + } + + private bool OpenWaveOut() + { + if (hWaveOut == IntPtr.Zero) + { + if (IsWaveOutOpened == false) + { + Win32.WAVEFORMATEX waveFormatEx = new() + { + wFormatTag = (ushort)Win32.WaveFormatFlags.WAVE_FORMAT_PCM, + nChannels = (ushort)Channels, + nSamplesPerSec = (ushort)SamplesPerSecond, + wBitsPerSample = (ushort)BitsPerSample + }; + waveFormatEx.nBlockAlign = (ushort)((waveFormatEx.wBitsPerSample * waveFormatEx.nChannels) >> 3); + waveFormatEx.nAvgBytesPerSec = waveFormatEx.nBlockAlign * waveFormatEx.nSamplesPerSec; + + int deviceId = WinSound.GetWaveOutDeviceIdByName(WaveOutDeviceName); + Win32.MMRESULT hr = Win32.waveOutOpen(ref hWaveOut, deviceId, ref waveFormatEx, delegateWaveOutProc, 0, (int)Win32.WaveProcFlags.CALLBACK_FUNCTION); + + if (hr != Win32.MMRESULT.MMSYSERR_NOERROR) + { + IsWaveOutOpened = false; + return false; + } + + GCHandle.Alloc(hWaveOut, GCHandleType.Pinned); + } + } + + IsWaveOutOpened = true; + return true; + } + + internal bool Open(string waveOutDeviceName, int samplesPerSecond, int bitsPerSample, int channels, int bufferCount) + { + try + { + lock (Locker) + { + if (Opened == false) + { + + WaveOutDeviceName = waveOutDeviceName; + SamplesPerSecond = samplesPerSecond; + BitsPerSample = bitsPerSample; + Channels = channels; + BufferCount = Math.Max(bufferCount, 1); + + if (OpenWaveOut()) + { + if (CreateWaveOutHeaders()) + { + StartThreadPlayWaveOut(); + IsClosed = false; + return true; + } + } + } + + return false; + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(string.Format("Start | {0}", ex.Message)); + return false; + } + } + + internal bool PlayData(byte[] datas, bool isBlocking) + { + try + { + if (Opened) + { + int index = GetNextFreeWaveOutHeaderIndex(); + if (index != -1) + { + IsBlocking = isBlocking; + + if (WaveOutHeaders[index]->dwBufferLength != datas.Length) + { + Marshal.FreeHGlobal(WaveOutHeaders[index]->lpData); + WaveOutHeaders[index]->lpData = Marshal.AllocHGlobal(datas.Length); + WaveOutHeaders[index]->dwBufferLength = (uint)datas.Length; + } + + WaveOutHeaders[index]->dwBufferLength = (uint)datas.Length; + WaveOutHeaders[index]->dwUser = (IntPtr)index; + Marshal.Copy(datas, 0, WaveOutHeaders[index]->lpData, datas.Length); + + IsStarted = true; + Win32.MMRESULT hr = Win32.waveOutWrite(hWaveOut, WaveOutHeaders[index], sizeof(Win32.WAVEHDR)); + if (hr == Win32.MMRESULT.MMSYSERR_NOERROR) + { + if (isBlocking) + { + AutoResetEventDataPlayed.WaitOne(); + AutoResetEventDataPlayed.Set(); + } + return true; + } + else + { + AutoResetEventDataPlayed.Set(); + return false; + } + } + else + { + System.Diagnostics.Debug.WriteLine(string.Format("No free WaveOut Buffer found | {0}", DateTime.Now.ToLongTimeString())); + return false; + } + } + else + { + return false; + } + + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(string.Format("PlayData | {0}", ex.Message)); + return false; + } + } + + internal bool Close() + { + try + { + lock (Locker) + { + if (Opened) + { + IsClosed = true; + + int count = 0; + while (Win32.waveOutReset(hWaveOut) != Win32.MMRESULT.MMSYSERR_NOERROR && count <= 100) + { + Thread.Sleep(50); + count++; + } + + FreeWaveOutHeaders(); + + count = 0; + while (Win32.waveOutClose(hWaveOut) != Win32.MMRESULT.MMSYSERR_NOERROR && count <= 100) + { + Thread.Sleep(50); + count++; + } + + IsWaveOutOpened = false; + AutoResetEventDataPlayed.Set(); + return true; + } + return false; + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(string.Format("Close | {0}", ex.Message)); + return false; + } + } + + private int GetNextFreeWaveOutHeaderIndex() + { + for (int i = 0; i < WaveOutHeaders.Length; i++) + { + if (IsHeaderPrepared(*WaveOutHeaders[i]) && !IsHeaderInqueue(*WaveOutHeaders[i])) + { + return i; + } + } + return -1; + } + + private static bool IsHeaderPrepared(Win32.WAVEHDR header) + { + return (header.dwFlags & Win32.WaveHdrFlags.WHDR_PREPARED) > 0; + } + + private static bool IsHeaderInqueue(Win32.WAVEHDR header) + { + return (header.dwFlags & Win32.WaveHdrFlags.WHDR_INQUEUE) > 0; + } + + private void WaveOutProc(IntPtr hWaveOut, Win32.WOM_Messages msg, IntPtr dwInstance, Win32.WAVEHDR* pWaveHeader, IntPtr lParam) + { + try + { + switch (msg) + { + //Open + case Win32.WOM_Messages.OPEN: + break; + + //Done + case Win32.WOM_Messages.DONE: + IsStarted = true; + AutoResetEventDataPlayed.Set(); + break; + + //Close + case Win32.WOM_Messages.CLOSE: + IsStarted = false; + IsWaveOutOpened = false; + IsPaused = false; + IsClosed = true; + AutoResetEventDataPlayed.Set(); + hWaveOut = IntPtr.Zero; + break; + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(string.Format("Player.cs | waveOutProc() | {0}", ex.Message)); + AutoResetEventDataPlayed.Set(); + } + } + + private void OnThreadPlayWaveOut() + { + while (Opened && !IsClosed) + { + AutoResetEventDataPlayed.WaitOne(); + + lock (Locker) + { + if (Opened && !IsClosed) + { + IsThreadPlayWaveOutRunning = true; + + if (!Playing) + { + if (IsStarted) + { + IsStarted = false; + if (PlayerStopped != null) + { + try + { + PlayerStopped(); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(string.Format("Player Stopped | {0}", ex.Message)); + } + finally + { + AutoResetEventDataPlayed.Set(); + } + } + } + } + } + } + + if (IsBlocking) + { + AutoResetEventDataPlayed.Set(); + } + } + + lock (Locker) + { + IsThreadPlayWaveOutRunning = false; + } + + if (PlayerClosed != null) + { + try + { + PlayerClosed(); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(string.Format("Player Closed | {0}", ex.Message)); + } + } + } + } +} diff --git a/Luski.net/Sound/Protocol.cs b/Luski.net/Sound/Protocol.cs new file mode 100755 index 0000000..2ba7550 --- /dev/null +++ b/Luski.net/Sound/Protocol.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Luski.net.Sound +{ + internal enum ProtocolTypes + { + LH + } + + internal class Protocol + { + internal Protocol(ProtocolTypes type, Encoding encoding) + { + m_ProtocolType = type; + m_Encoding = encoding; + } + + private readonly List m_DataBuffer = new(); + private const int m_MaxBufferLength = 10000; + private readonly ProtocolTypes m_ProtocolType = ProtocolTypes.LH; + private readonly Encoding m_Encoding = Encoding.Default; + internal object m_LockerReceive = new(); + + internal delegate void DelegateDataComplete(object sender, byte[] data); + internal delegate void DelegateExceptionAppeared(object sender, Exception ex); + internal event DelegateDataComplete DataComplete; + internal event DelegateExceptionAppeared ExceptionAppeared; + + internal byte[] ToBytes(byte[] data) + { + try + { + byte[] bytesLength = BitConverter.GetBytes(data.Length); + + byte[] allBytes = new byte[bytesLength.Length + data.Length]; + Array.Copy(bytesLength, allBytes, bytesLength.Length); + Array.Copy(data, 0, allBytes, bytesLength.Length, data.Length); + + return allBytes; + } + catch (Exception ex) + { + ExceptionAppeared(null, ex); + } + + return data; + } + + internal void Receive_LH(object sender, byte[] data) + { + lock (m_LockerReceive) + { + try + { + m_DataBuffer.AddRange(data); + + if (m_DataBuffer.Count > m_MaxBufferLength) + { + m_DataBuffer.Clear(); + } + + byte[] bytes = m_DataBuffer.Take(4).ToArray(); + int length = BitConverter.ToInt32(bytes.ToArray(), 0); + + if (length > m_MaxBufferLength) + { + m_DataBuffer.Clear(); + } + + while (m_DataBuffer.Count >= length + 4) + { + byte[] message = m_DataBuffer.Skip(4).Take(length).ToArray(); + + DataComplete?.Invoke(sender, message); + m_DataBuffer.RemoveRange(0, length + 4); + + if (m_DataBuffer.Count > 4) + { + bytes = m_DataBuffer.Take(4).ToArray(); + length = BitConverter.ToInt32(bytes.ToArray(), 0); + } + } + } + catch (Exception ex) + { + m_DataBuffer.Clear(); + ExceptionAppeared(null, ex); + } + } + } + } +} diff --git a/Luski.net/Sound/RTPPacket.cs b/Luski.net/Sound/RTPPacket.cs new file mode 100755 index 0000000..c446114 --- /dev/null +++ b/Luski.net/Sound/RTPPacket.cs @@ -0,0 +1,139 @@ +using System; +using System.Linq; + +namespace Luski.net.Sound +{ + internal class RTPPacket + { + internal RTPPacket() + { + + } + + internal RTPPacket(byte[] data) + { + Parse(data); + } + + internal static int MinHeaderLength = 12; + internal int HeaderLength = MinHeaderLength; + internal int Version = 0; + internal bool Padding = false; + internal bool Extension = false; + internal int CSRCCount = 0; + internal bool Marker = false; + internal int PayloadType = 0; + internal ushort SequenceNumber = 0; + internal uint Timestamp = 0; + internal uint SourceId = 0; + internal byte[]? Data; + internal ushort ExtensionHeaderId = 0; + internal ushort ExtensionLengthAsCount = 0; + internal int ExtensionLengthInBytes = 0; + + private void Parse(byte[] data) + { + if (data.Length >= MinHeaderLength) + { + Version = ValueFromByte(data[0], 6, 2); + Padding = Convert.ToBoolean(ValueFromByte(data[0], 5, 1)); + Extension = Convert.ToBoolean(ValueFromByte(data[0], 4, 1)); + CSRCCount = ValueFromByte(data[0], 0, 4); + Marker = Convert.ToBoolean(ValueFromByte(data[1], 7, 1)); + PayloadType = ValueFromByte(data[1], 0, 7); + HeaderLength = MinHeaderLength + (CSRCCount * 4); + + //Sequence Nummer + byte[] seqNum = new byte[2]; + seqNum[0] = data[3]; + seqNum[1] = data[2]; + SequenceNumber = BitConverter.ToUInt16(seqNum, 0); + + //TimeStamp + byte[] timeStmp = new byte[4]; + timeStmp[0] = data[7]; + timeStmp[1] = data[6]; + timeStmp[2] = data[5]; + timeStmp[3] = data[4]; + Timestamp = BitConverter.ToUInt32(timeStmp, 0); + + //SourceId + byte[] srcId = new byte[4]; + srcId[0] = data[8]; + srcId[1] = data[9]; + srcId[2] = data[10]; + srcId[3] = data[11]; + SourceId = BitConverter.ToUInt32(srcId, 0); + + if (Extension) + { + byte[] extHeaderId = new byte[2]; + extHeaderId[1] = data[HeaderLength + 0]; + extHeaderId[0] = data[HeaderLength + 1]; + ExtensionHeaderId = BitConverter.ToUInt16(extHeaderId, 0); + + byte[] extHeaderLength16 = new byte[2]; + extHeaderLength16[1] = data[HeaderLength + 2]; + extHeaderLength16[0] = data[HeaderLength + 3]; + ExtensionLengthAsCount = BitConverter.ToUInt16(extHeaderLength16.ToArray(), 0); + + ExtensionLengthInBytes = ExtensionLengthAsCount * 4; + HeaderLength += ExtensionLengthInBytes + 4; + } + + Data = new byte[data.Length - HeaderLength]; + Array.Copy(data, HeaderLength, Data, 0, data.Length - HeaderLength); + } + } + + private static int ValueFromByte(byte value, int startPos, int length) + { + byte mask = 0; + for (int i = 0; i < length; i++) + { + mask = (byte)(mask | 0x1 << startPos + i); + } + + byte result = (byte)((value & mask) >> startPos); + return Convert.ToInt32(result); + } + + internal byte[] ToBytes() + { + byte[] bytes = new byte[HeaderLength + Data.Length]; + + //Byte 0 + bytes[0] = (byte)(Version << 6); + bytes[0] |= (byte)(Convert.ToInt32(Padding) << 5); + bytes[0] |= (byte)(Convert.ToInt32(Extension) << 4); + bytes[0] |= (byte)(Convert.ToInt32(CSRCCount)); + + //Byte 1 + bytes[1] = (byte)(Convert.ToInt32(Marker) << 7); + bytes[1] |= (byte)(Convert.ToInt32(PayloadType)); + + //Byte 2 + 3 + byte[] bytesSequenceNumber = BitConverter.GetBytes(SequenceNumber); + bytes[2] = bytesSequenceNumber[1]; + bytes[3] = bytesSequenceNumber[0]; + + //Byte 4 bis 7 + byte[] bytesTimeStamp = BitConverter.GetBytes(Timestamp); + bytes[4] = bytesTimeStamp[3]; + bytes[5] = bytesTimeStamp[2]; + bytes[6] = bytesTimeStamp[1]; + bytes[7] = bytesTimeStamp[0]; + + //Byte 8 bis 11 + byte[] bytesSourceId = BitConverter.GetBytes(SourceId); + bytes[8] = bytesSourceId[3]; + bytes[9] = bytesSourceId[2]; + bytes[10] = bytesSourceId[1]; + bytes[11] = bytesSourceId[0]; + + Array.Copy(Data, 0, bytes, HeaderLength, Data.Length); + + return bytes; + } + } +} diff --git a/Luski.net/Sound/Recorder.cs b/Luski.net/Sound/Recorder.cs new file mode 100755 index 0000000..93a0093 --- /dev/null +++ b/Luski.net/Sound/Recorder.cs @@ -0,0 +1,340 @@ +using System; +using System.Runtime.InteropServices; +using System.Threading; + +namespace Luski.net.Sound +{ + internal unsafe class Recorder + { + internal Recorder() + { + delegateWaveInProc = new Win32.DelegateWaveInProc(WaveInProc); + } + + private readonly LockerClass Locker = new(); + private readonly LockerClass LockerCopy = new(); + private IntPtr hWaveIn = IntPtr.Zero; + private string WaveInDeviceName = ""; + private bool IsWaveInOpened = false; + private bool IsWaveInStarted = false; + private bool IsThreadRecordingRunning = false; + private bool IsDataIncomming = false; + private bool Stopped = false; + private int SamplesPerSecond = 8000; + private int BitsPerSample = 16; + private int Channels = 1; + private int BufferCount = 8; + private int BufferSize = 1024; + private Win32.WAVEHDR*[] WaveInHeaders; + private Win32.WAVEHDR* CurrentRecordedHeader; + private readonly Win32.DelegateWaveInProc delegateWaveInProc; + private Thread ThreadRecording; + private readonly AutoResetEvent AutoResetEventDataRecorded = new(false); + + internal delegate void DelegateStopped(); + internal delegate void DelegateDataRecorded(byte[] bytes); + internal event DelegateStopped RecordingStopped; + internal event DelegateDataRecorded DataRecorded; + + internal bool Started => IsWaveInStarted && IsWaveInOpened && IsThreadRecordingRunning; + + private bool CreateWaveInHeaders() + { + WaveInHeaders = new Win32.WAVEHDR*[BufferCount]; + int createdHeaders = 0; + + for (int i = 0; i < BufferCount; i++) + { + WaveInHeaders[i] = (Win32.WAVEHDR*)Marshal.AllocHGlobal(sizeof(Win32.WAVEHDR)); + + WaveInHeaders[i]->dwLoops = 0; + WaveInHeaders[i]->dwUser = IntPtr.Zero; + WaveInHeaders[i]->lpNext = IntPtr.Zero; + WaveInHeaders[i]->reserved = IntPtr.Zero; + WaveInHeaders[i]->lpData = Marshal.AllocHGlobal(BufferSize); + WaveInHeaders[i]->dwBufferLength = (uint)BufferSize; + WaveInHeaders[i]->dwBytesRecorded = 0; + WaveInHeaders[i]->dwFlags = 0; + + Win32.MMRESULT hr = Win32.waveInPrepareHeader(hWaveIn, WaveInHeaders[i], sizeof(Win32.WAVEHDR)); + if (hr == Win32.MMRESULT.MMSYSERR_NOERROR) + { + if (i == 0) + { + hr = Win32.waveInAddBuffer(hWaveIn, WaveInHeaders[i], sizeof(Win32.WAVEHDR)); + } + createdHeaders++; + } + } + + return (createdHeaders == BufferCount); + } + + private void FreeWaveInHeaders() + { + try + { + if (WaveInHeaders != null) + { + for (int i = 0; i < WaveInHeaders.Length; i++) + { + Win32.MMRESULT hr = Win32.waveInUnprepareHeader(hWaveIn, WaveInHeaders[i], sizeof(Win32.WAVEHDR)); + + int count = 0; + while (count <= 100 && (WaveInHeaders[i]->dwFlags & Win32.WaveHdrFlags.WHDR_INQUEUE) == Win32.WaveHdrFlags.WHDR_INQUEUE) + { + Thread.Sleep(20); + count++; + } + + if ((WaveInHeaders[i]->dwFlags & Win32.WaveHdrFlags.WHDR_INQUEUE) != Win32.WaveHdrFlags.WHDR_INQUEUE) + { + if (WaveInHeaders[i]->lpData != IntPtr.Zero) + { + Marshal.FreeHGlobal(WaveInHeaders[i]->lpData); + WaveInHeaders[i]->lpData = IntPtr.Zero; + } + } + } + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.Write(ex.Message); + } + } + + private void StartThreadRecording() + { + if (Started == false) + { + ThreadRecording = new Thread(new ThreadStart(OnThreadRecording)); + IsThreadRecordingRunning = true; + ThreadRecording.Name = "Recording"; + ThreadRecording.Priority = ThreadPriority.Highest; + ThreadRecording.Start(); + } + } + + private bool OpenWaveIn() + { + if (hWaveIn == IntPtr.Zero) + { + if (IsWaveInOpened == false) + { + Win32.WAVEFORMATEX waveFormatEx = new() + { + wFormatTag = (ushort)Win32.WaveFormatFlags.WAVE_FORMAT_PCM, + nChannels = (ushort)Channels, + nSamplesPerSec = (ushort)SamplesPerSecond, + wBitsPerSample = (ushort)BitsPerSample + }; + waveFormatEx.nBlockAlign = (ushort)((waveFormatEx.wBitsPerSample * waveFormatEx.nChannels) >> 3); + waveFormatEx.nAvgBytesPerSec = waveFormatEx.nBlockAlign * waveFormatEx.nSamplesPerSec; + + int deviceId = WinSound.GetWaveInDeviceIdByName(WaveInDeviceName); + Win32.MMRESULT hr = Win32.waveInOpen(ref hWaveIn, deviceId, ref waveFormatEx, delegateWaveInProc, 0, (int)Win32.WaveProcFlags.CALLBACK_FUNCTION); + + if (hWaveIn == IntPtr.Zero) + { + IsWaveInOpened = false; + return false; + } + + GCHandle.Alloc(hWaveIn, GCHandleType.Pinned); + } + } + + IsWaveInOpened = true; + return true; + } + + internal bool Start(string waveInDeviceName, int samplesPerSecond, int bitsPerSample, int channels, int bufferCount, int bufferSize) + { + try + { + lock (Locker) + { + if (Started == false) + { + WaveInDeviceName = waveInDeviceName; + SamplesPerSecond = samplesPerSecond; + BitsPerSample = bitsPerSample; + Channels = channels; + BufferCount = bufferCount; + BufferSize = bufferSize; + + if (OpenWaveIn()) + { + if (CreateWaveInHeaders()) + { + Win32.MMRESULT hr = Win32.waveInStart(hWaveIn); + if (hr == Win32.MMRESULT.MMSYSERR_NOERROR) + { + IsWaveInStarted = true; + StartThreadRecording(); + Stopped = false; + return true; + } + else + { + return false; + } + } + } + } + + return false; + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(string.Format("Start | {0}", ex.Message)); + return false; + } + } + + internal bool Stop() + { + try + { + lock (Locker) + { + if (Started) + { + Stopped = true; + IsThreadRecordingRunning = false; + + CloseWaveIn(); + + AutoResetEventDataRecorded.Set(); + return true; + } + return false; + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(string.Format("Stop | {0}", ex.Message)); + return false; + } + } + + private void CloseWaveIn() + { + Win32.MMRESULT hr = Win32.waveInStop(hWaveIn); + + int resetCount = 0; + while (IsAnyWaveInHeaderInState(Win32.WaveHdrFlags.WHDR_INQUEUE) & resetCount < 20) + { + hr = Win32.waveInReset(hWaveIn); + Thread.Sleep(50); + resetCount++; + } + + FreeWaveInHeaders(); + hr = Win32.waveInClose(hWaveIn); + } + + private bool IsAnyWaveInHeaderInState(Win32.WaveHdrFlags state) + { + for (int i = 0; i < WaveInHeaders.Length; i++) + { + if ((WaveInHeaders[i]->dwFlags & state) == state) + { + return true; + } + } + return false; + } + + private void WaveInProc(IntPtr hWaveIn, Win32.WIM_Messages msg, IntPtr dwInstance, Win32.WAVEHDR* pWaveHdr, IntPtr lParam) + { + switch (msg) + { + //Open + case Win32.WIM_Messages.OPEN: + break; + + //Data + case Win32.WIM_Messages.DATA: + IsDataIncomming = true; + CurrentRecordedHeader = pWaveHdr; + AutoResetEventDataRecorded.Set(); + break; + + //Close + case Win32.WIM_Messages.CLOSE: + IsDataIncomming = false; + IsWaveInOpened = false; + AutoResetEventDataRecorded.Set(); + this.hWaveIn = IntPtr.Zero; + break; + } + } + + private void OnThreadRecording() + { + while (Started && !Stopped) + { + AutoResetEventDataRecorded.WaitOne(); + + try + { + if (Started && !Stopped) + { + if (CurrentRecordedHeader->dwBytesRecorded > 0) + { + if (DataRecorded != null && IsDataIncomming) + { + try + { + byte[] bytes = new byte[CurrentRecordedHeader->dwBytesRecorded]; + Marshal.Copy(CurrentRecordedHeader->lpData, bytes, 0, (int)CurrentRecordedHeader->dwBytesRecorded); + + DataRecorded(bytes); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(string.Format("Recorder.cs | OnThreadRecording() | {0}", ex.Message)); + } + } + + for (int i = 0; i < WaveInHeaders.Length; i++) + { + if ((WaveInHeaders[i]->dwFlags & Win32.WaveHdrFlags.WHDR_INQUEUE) == 0) + { + Win32.MMRESULT hr = Win32.waveInAddBuffer(hWaveIn, WaveInHeaders[i], sizeof(Win32.WAVEHDR)); + } + } + + } + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + } + } + + + lock (Locker) + { + IsWaveInStarted = false; + IsThreadRecordingRunning = false; + } + + if (RecordingStopped != null) + { + try + { + RecordingStopped(); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(string.Format("Recording Stopped | {0}", ex.Message)); + } + } + } + } +} diff --git a/Luski.net/Sound/Timer.cs b/Luski.net/Sound/Timer.cs new file mode 100755 index 0000000..3884ad9 --- /dev/null +++ b/Luski.net/Sound/Timer.cs @@ -0,0 +1,64 @@ +using System; +using System.Runtime.InteropServices; + +namespace Luski.net.Sound +{ + internal class EventTimer + { + internal EventTimer() + { + m_DelegateTimeEvent = new Win32.TimerEventHandler(OnTimer); + } + + private bool m_IsRunning = false; + private uint m_Milliseconds = 20; + private uint m_TimerId = 0; + private GCHandle m_GCHandleTimer; + private uint m_UserData = 0; + private uint m_ResolutionInMilliseconds = 0; + + private readonly Win32.TimerEventHandler m_DelegateTimeEvent; + internal delegate void DelegateTimerTick(); + internal event DelegateTimerTick? TimerTick; + + internal void Start(uint milliseconds) + { + m_Milliseconds = milliseconds; + + Win32.TimeCaps tc = new(); + Win32.TimeGetDevCaps(ref tc, (uint)Marshal.SizeOf(typeof(Win32.TimeCaps))); + m_ResolutionInMilliseconds = Math.Max(tc.wPeriodMin, 0); + + Win32.TimeBeginPeriod(m_ResolutionInMilliseconds); + + m_TimerId = Win32.TimeSetEvent(m_Milliseconds, m_ResolutionInMilliseconds, m_DelegateTimeEvent, ref m_UserData, Win32.TIME_PERIODIC); + if (m_TimerId > 0) + { + m_GCHandleTimer = GCHandle.Alloc(m_TimerId, GCHandleType.Pinned); + m_IsRunning = true; + } + } + + internal void Stop() + { + if (m_TimerId > 0) + { + _ = Win32.TimeKillEvent(m_TimerId); + Win32.TimeEndPeriod(m_ResolutionInMilliseconds); + + if (m_GCHandleTimer.IsAllocated) + { + m_GCHandleTimer.Free(); + } + + m_TimerId = 0; + m_IsRunning = false; + } + } + + private void OnTimer(uint id, uint msg, ref uint userCtx, uint rsv1, uint rsv2) + { + TimerTick?.Invoke(); + } + } +} diff --git a/Luski.net/Sound/Utils.cs b/Luski.net/Sound/Utils.cs new file mode 100755 index 0000000..2ccb7ae --- /dev/null +++ b/Luski.net/Sound/Utils.cs @@ -0,0 +1,195 @@ +using System; + +namespace Luski.net.Sound +{ + internal class Utils + { + internal Utils() + { + + } + + private const int SIGN_BIT = 0x80; + private const int QUANT_MASK = 0xf; + private const int SEG_SHIFT = 4; + private const int SEG_MASK = 0x70; + private const int BIAS = 0x84; + private const int CLIP = 8159; + private static readonly short[] seg_uend = new short[] { 0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF }; + + internal static int GetBytesPerInterval(uint SamplesPerSecond, int BitsPerSample, int Channels) + { + int blockAlign = ((BitsPerSample * Channels) >> 3); + int bytesPerSec = (int)(blockAlign * SamplesPerSecond); + uint sleepIntervalFactor = 1000 / 20; + int bytesPerInterval = (int)(bytesPerSec / sleepIntervalFactor); + + return bytesPerInterval; + } + + internal static int MulawToLinear(int ulaw) + { + ulaw = ~ulaw; + int t = ((ulaw & QUANT_MASK) << 3) + BIAS; + t <<= (ulaw & SEG_MASK) >> SEG_SHIFT; + return ((ulaw & SIGN_BIT) > 0 ? (BIAS - t) : (t - BIAS)); + } + + private static short Search(short val, short[] table, short size) + { + short i; + int index = 0; + for (i = 0; i < size; i++) + { + if (val <= table[index]) + { + return (i); + } + index++; + } + return (size); + } + + internal static byte Linear2ulaw(short pcm_val) + { + + /* Get the sign and the magnitude of the value. */ + pcm_val = (short)(pcm_val >> 2); + short mask; + if (pcm_val < 0) + { + pcm_val = (short)-pcm_val; + mask = 0x7F; + } + else + { + mask = 0xFF; + } + /* clip the magnitude */ + if (pcm_val > CLIP) + { + pcm_val = CLIP; + } + pcm_val += (BIAS >> 2); + + /* Convert the scaled magnitude to segment number. */ + short seg = Search(pcm_val, seg_uend, 8); + + /* + * Combine the sign, segment, quantization bits; + * and complement the code word. + */ + /* out of range, return maximum value. */ + if (seg >= 8) + { + return (byte)(0x7F ^ mask); + } + else + { + byte uval = (byte)((seg << 4) | ((pcm_val >> (seg + 1)) & 0xF)); + return ((byte)(uval ^ mask)); + } + } + + internal static byte[] MuLawToLinear(byte[] bytes, int bitsPerSample, int channels) + { + int blockAlign = channels * bitsPerSample / 8; + + byte[] result = new byte[bytes.Length * blockAlign]; + for (int i = 0, counter = 0; i < bytes.Length; i++, counter += blockAlign) + { + int value = MulawToLinear(bytes[i]); + byte[] values = BitConverter.GetBytes(value); + + switch (bitsPerSample) + { + case 8: + switch (channels) + { + //8 Bit 1 Channel + case 1: + result[counter] = values[0]; + break; + + //8 Bit 2 Channel + case 2: + result[counter] = values[0]; + result[counter + 1] = values[0]; + break; + } + break; + + case 16: + switch (channels) + { + //16 Bit 1 Channel + case 1: + result[counter] = values[0]; + result[counter + 1] = values[1]; + break; + + //16 Bit 2 Channels + case 2: + result[counter] = values[0]; + result[counter + 1] = values[1]; + result[counter + 2] = values[0]; + result[counter + 3] = values[1]; + break; + } + break; + } + } + + return result; + } + + internal static byte[] LinearToMulaw(byte[] bytes, int bitsPerSample, int channels) + { + int blockAlign = channels * bitsPerSample / 8; + + byte[] result = new byte[bytes.Length / blockAlign]; + int resultIndex = 0; + for (int i = 0; i < result.Length; i++) + { + switch (bitsPerSample) + { + case 8: + switch (channels) + { + //8 Bit 1 Channel + case 1: + result[i] = Linear2ulaw(bytes[resultIndex]); + resultIndex += 1; + break; + + //8 Bit 2 Channel + case 2: + result[i] = Linear2ulaw(bytes[resultIndex]); + resultIndex += 2; + break; + } + break; + + case 16: + switch (channels) + { + //16 Bit 1 Channel + case 1: + result[i] = Linear2ulaw(BitConverter.ToInt16(bytes, resultIndex)); + resultIndex += 2; + break; + + //16 Bit 2 Channels + case 2: + result[i] = Linear2ulaw(BitConverter.ToInt16(bytes, resultIndex)); + resultIndex += 4; + break; + } + break; + } + } + + return result; + } + } +} diff --git a/Luski.net/Sound/WaveFile.cs b/Luski.net/Sound/WaveFile.cs new file mode 100755 index 0000000..398c5e3 --- /dev/null +++ b/Luski.net/Sound/WaveFile.cs @@ -0,0 +1,104 @@ +using System; +using System.IO; +using System.Text; + +namespace Luski.net.Sound +{ + internal class WaveFile + { + internal WaveFile() + { + + } + + internal const int WAVE_FORMAT_PCM = 1; + + + internal static WaveFileHeader Read(string fileName) + { + WaveFileHeader header = ReadHeader(fileName); + + return header; + } + + private static WaveFileHeader ReadHeader(string fileName) + { + WaveFileHeader header = new(); + + if (File.Exists(fileName)) + { + FileStream fs = new(fileName, FileMode.Open, FileAccess.Read); + BinaryReader rd = new(fs, Encoding.UTF8); + + if (fs.CanRead) + { + header.RIFF = rd.ReadChars(4); + header.RiffSize = (uint)rd.ReadInt32(); + header.RiffFormat = rd.ReadChars(4); + + header.FMT = rd.ReadChars(4); + header.FMTSize = (uint)rd.ReadInt32(); + header.FMTPos = fs.Position; + header.AudioFormat = rd.ReadInt16(); + header.Channels = rd.ReadInt16(); + header.SamplesPerSecond = (uint)rd.ReadInt32(); + header.BytesPerSecond = (uint)rd.ReadInt32(); + header.BlockAlign = rd.ReadInt16(); + header.BitsPerSample = rd.ReadInt16(); + + fs.Seek(header.FMTPos + header.FMTSize, SeekOrigin.Begin); + + header.DATA = rd.ReadChars(4); + header.DATASize = (uint)rd.ReadInt32(); + header.DATAPos = (int)fs.Position; + + if (new string(header.DATA).ToUpper() != "DATA") + { + uint DataChunkSize = header.DATASize + 8; + fs.Seek(DataChunkSize, SeekOrigin.Current); + header.DATASize = (uint)(fs.Length - header.DATAPos - DataChunkSize); + } + + if (header.DATASize <= fs.Length - header.DATAPos) + { + header.Payload = rd.ReadBytes((int)header.DATASize); + } + } + + rd.Close(); + fs.Close(); + } + + return header; + } + } + + internal class WaveFileHeader + { + internal WaveFileHeader() + { + + } + + internal char[] RIFF = new char[4]; + internal uint RiffSize = 8; + internal char[] RiffFormat = new char[4]; + + internal char[] FMT = new char[4]; + internal uint FMTSize = 16; + internal short AudioFormat; + internal short Channels; + internal uint SamplesPerSecond; + internal uint BytesPerSecond; + internal short BlockAlign; + internal short BitsPerSample; + + internal char[] DATA = new char[4]; + internal uint DATASize; + + internal byte[] Payload = Array.Empty(); + + internal int DATAPos = 44; + internal long FMTPos = 20; + } +} diff --git a/Luski.net/Sound/Win32.cs b/Luski.net/Sound/Win32.cs new file mode 100755 index 0000000..0275eaf --- /dev/null +++ b/Luski.net/Sound/Win32.cs @@ -0,0 +1,241 @@ +using System; +using System.Runtime.InteropServices; + +namespace Luski.net.Sound +{ + internal unsafe class Win32 + { + internal Win32() + { + + } + + internal const int WAVE_MAPPER = -1; + + internal const int WT_EXECUTEDEFAULT = 0x00000000; + internal const int WT_EXECUTEINIOTHREAD = 0x00000001; + internal const int WT_EXECUTEINTIMERTHREAD = 0x00000020; + internal const int WT_EXECUTEINPERSISTENTTHREAD = 0x00000080; + + internal const int TIME_ONESHOT = 0; + internal const int TIME_PERIODIC = 1; + + [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Auto)] + internal struct WAVEOUTCAPS + { + internal short wMid; + internal short wPid; + internal int vDriverVersion; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] + internal string szPname; + internal uint dwFormats; + internal short wChannels; + internal short wReserved; + internal int dwSupport; + } + + [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Auto)] + internal struct WAVEINCAPS + { + internal short wMid; + internal short wPid; + internal int vDriverVersion; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] + internal string szPname; + internal uint dwFormats; + internal short wChannels; + internal short wReserved; + internal int dwSupport; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct WAVEFORMATEX + { + internal ushort wFormatTag; + internal ushort nChannels; + internal uint nSamplesPerSec; + internal uint nAvgBytesPerSec; + internal ushort nBlockAlign; + internal ushort wBitsPerSample; + internal ushort cbSize; + } + + internal enum MMRESULT : uint + { + MMSYSERR_NOERROR = 0, + MMSYSERR_ERROR = 1, + MMSYSERR_BADDEVICEID = 2, + MMSYSERR_NOTENABLED = 3, + MMSYSERR_ALLOCATED = 4, + MMSYSERR_INVALHANDLE = 5, + MMSYSERR_NODRIVER = 6, + MMSYSERR_NOMEM = 7, + MMSYSERR_NOTSUPPORTED = 8, + MMSYSERR_BADERRNUM = 9, + MMSYSERR_INVALFLAG = 10, + MMSYSERR_INVALPARAM = 11, + MMSYSERR_HANDLEBUSY = 12, + MMSYSERR_INVALIDALIAS = 13, + MMSYSERR_BADDB = 14, + MMSYSERR_KEYNOTFOUND = 15, + MMSYSERR_READERROR = 16, + MMSYSERR_WRITEERROR = 17, + MMSYSERR_DELETEERROR = 18, + MMSYSERR_VALNOTFOUND = 19, + MMSYSERR_NODRIVERCB = 20, + WAVERR_BADFORMAT = 32, + WAVERR_STILLPLAYING = 33, + WAVERR_UNPREPARED = 34 + } + + [Flags] + internal enum WaveHdrFlags : uint + { + WHDR_DONE = 1, + WHDR_PREPARED = 2, + WHDR_BEGINLOOP = 4, + WHDR_ENDLOOP = 8, + WHDR_INQUEUE = 16 + } + + [Flags] + internal enum WaveProcFlags : int + { + CALLBACK_NULL = 0, + CALLBACK_FUNCTION = 0x30000, + CALLBACK_EVENT = 0x50000, + CALLBACK_WINDOW = 0x10000, + CALLBACK_THREAD = 0x20000, + WAVE_FORMAT_QUERY = 1, + WAVE_MAPPED = 4, + WAVE_FORMAT_DIRECT = 8 + } + + [Flags] + internal enum HRESULT : long + { + S_OK = 0L, + S_FALSE = 1L + } + + [Flags] + internal enum WaveFormatFlags : int + { + WAVE_FORMAT_PCM = 0x0001 + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + internal struct WAVEHDR + { + internal IntPtr lpData; + internal uint dwBufferLength; + internal uint dwBytesRecorded; + internal IntPtr dwUser; + internal WaveHdrFlags dwFlags; + internal uint dwLoops; + internal IntPtr lpNext; + internal IntPtr reserved; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct TimeCaps + { + internal uint wPeriodMin; + internal uint wPeriodMax; + }; + + internal enum WOM_Messages : int + { + OPEN = 0x03BB, + CLOSE = 0x03BC, + DONE = 0x03BD + } + + internal enum WIM_Messages : int + { + OPEN = 0x03BE, + CLOSE = 0x03BF, + DATA = 0x03C0 + } + + internal delegate void DelegateWaveOutProc(IntPtr hWaveOut, WOM_Messages msg, IntPtr dwInstance, WAVEHDR* pWaveHdr, IntPtr lParam); + internal delegate void DelegateWaveInProc(IntPtr hWaveIn, WIM_Messages msg, IntPtr dwInstance, WAVEHDR* pWaveHdr, IntPtr lParam); + internal delegate void DelegateTimerProc(IntPtr lpParameter, bool TimerOrWaitFired); + internal delegate void TimerEventHandler(uint id, uint msg, ref uint userCtx, uint rsv1, uint rsv2); + + [DllImport("winmm.dll", SetLastError = true, EntryPoint = "timeSetEvent")] + internal static extern uint TimeSetEvent(uint msDelay, uint msResolution, TimerEventHandler handler, ref uint userCtx, uint eventType); + + [DllImport("winmm.dll", SetLastError = true, EntryPoint = "timeKillEvent")] + internal static extern uint TimeKillEvent(uint timerId); + + [DllImport("winmm.dll", SetLastError = true, EntryPoint = "timeGetDevCaps")] + internal static extern MMRESULT TimeGetDevCaps(ref TimeCaps timeCaps, uint sizeTimeCaps); + + [DllImport("winmm.dll", SetLastError = true, EntryPoint = "timeBeginPeriod")] + internal static extern MMRESULT TimeBeginPeriod(uint uPeriod); + + [DllImport("winmm.dll", SetLastError = true, EntryPoint = "timeEndPeriod")] + internal static extern MMRESULT TimeEndPeriod(uint uPeriod); + + [DllImport("winmm.dll", SetLastError = true, CharSet = CharSet.Auto)] + internal static extern MMRESULT waveOutOpen(ref IntPtr hWaveOut, int uDeviceID, ref WAVEFORMATEX lpFormat, DelegateWaveOutProc dwCallBack, int dwInstance, int dwFlags); + + [DllImport("winmm.dll")] + internal static extern MMRESULT waveInOpen(ref IntPtr hWaveIn, int deviceId, ref WAVEFORMATEX wfx, DelegateWaveInProc dwCallBack, int dwInstance, int dwFlags); + + [DllImport("winmm.dll", SetLastError = true)] + internal static extern MMRESULT waveInStart(IntPtr hWaveIn); + + [DllImport("winmm.dll", SetLastError = true, CharSet = CharSet.Auto)] + internal static extern uint waveInGetDevCaps(int index, ref WAVEINCAPS pwic, int cbwic); + + [DllImport("winmm.dll", SetLastError = true)] + internal static extern uint waveInGetNumDevs(); + + [DllImport("winmm.dll", SetLastError = true, CharSet = CharSet.Auto)] + internal static extern uint waveOutGetDevCaps(int index, ref WAVEOUTCAPS pwoc, int cbwoc); + + [DllImport("winmm.dll", SetLastError = true)] + internal static extern uint waveOutGetNumDevs(); + + [DllImport("winmm.dll", SetLastError = true, CharSet = CharSet.Auto)] + internal static extern MMRESULT waveOutWrite(IntPtr hWaveOut, WAVEHDR* pwh, int cbwh); + + [DllImport("winmm.dll", SetLastError = true, EntryPoint = "waveOutPrepareHeader", CharSet = CharSet.Auto)] + internal static extern MMRESULT waveOutPrepareHeader(IntPtr hWaveOut, WAVEHDR* lpWaveOutHdr, int uSize); + + [DllImport("winmm.dll", SetLastError = true, EntryPoint = "waveOutUnprepareHeader", CharSet = CharSet.Auto)] + internal static extern MMRESULT waveOutUnprepareHeader(IntPtr hWaveOut, WAVEHDR* lpWaveOutHdr, int uSize); + + [DllImport("winmm.dll", EntryPoint = "waveInStop", SetLastError = true)] + internal static extern MMRESULT waveInStop(IntPtr hWaveIn); + + [DllImport("winmm.dll", EntryPoint = "waveInReset", SetLastError = true)] + internal static extern MMRESULT waveInReset(IntPtr hWaveIn); + + [DllImport("winmm.dll", EntryPoint = "waveOutReset", SetLastError = true)] + internal static extern MMRESULT waveOutReset(IntPtr hWaveOut); + + [DllImport("winmm.dll", SetLastError = true)] + internal static extern MMRESULT waveInPrepareHeader(IntPtr hWaveIn, WAVEHDR* pwh, int cbwh); + + [DllImport("winmm.dll", SetLastError = true)] + internal static extern MMRESULT waveInUnprepareHeader(IntPtr hWaveIn, WAVEHDR* pwh, int cbwh); + + [DllImport("winmm.dll", EntryPoint = "waveInAddBuffer", SetLastError = true)] + internal static extern MMRESULT waveInAddBuffer(IntPtr hWaveIn, WAVEHDR* pwh, int cbwh); + + [DllImport("winmm.dll", SetLastError = true)] + internal static extern MMRESULT waveInClose(IntPtr hWaveIn); + + [DllImport("winmm.dll", SetLastError = true, CharSet = CharSet.Auto)] + internal static extern MMRESULT waveOutClose(IntPtr hWaveOut); + + [DllImport("winmm.dll")] + internal static extern MMRESULT waveOutPause(IntPtr hWaveOut); + + [DllImport("winmm.dll", EntryPoint = "waveOutRestart", SetLastError = true)] + internal static extern MMRESULT waveOutRestart(IntPtr hWaveOut); + } +} diff --git a/Luski.net/Sound/WinSound.cs b/Luski.net/Sound/WinSound.cs new file mode 100755 index 0000000..c5d48ce --- /dev/null +++ b/Luski.net/Sound/WinSound.cs @@ -0,0 +1,94 @@ +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace Luski.net.Sound +{ + internal class LockerClass + { + + } + + internal class WinSound + { + internal WinSound() + { + + } + + internal static List GetPlaybackNames() + { + List list = new(); + Win32.WAVEOUTCAPS waveOutCap = new(); + + uint num = Win32.waveOutGetNumDevs(); + for (int i = 0; i < num; i++) + { + uint hr = Win32.waveOutGetDevCaps(i, ref waveOutCap, Marshal.SizeOf(typeof(Win32.WAVEOUTCAPS))); + if (hr == (int)Win32.HRESULT.S_OK) + { + list.Add(waveOutCap.szPname); + } + } + + return list; + } + + internal static List GetRecordingNames() + { + List list = new(); + Win32.WAVEINCAPS waveInCap = new(); + + uint num = Win32.waveInGetNumDevs(); + for (int i = 0; i < num; i++) + { + uint hr = Win32.waveInGetDevCaps(i, ref waveInCap, Marshal.SizeOf(typeof(Win32.WAVEINCAPS))); + if (hr == (int)Win32.HRESULT.S_OK) + { + list.Add(waveInCap.szPname); + } + } + + return list; + } + + internal static int GetWaveInDeviceIdByName(string name) + { + uint num = Win32.waveInGetNumDevs(); + + Win32.WAVEINCAPS caps = new(); + for (int i = 0; i < num; i++) + { + Win32.HRESULT hr = (Win32.HRESULT)Win32.waveInGetDevCaps(i, ref caps, Marshal.SizeOf(typeof(Win32.WAVEINCAPS))); + if (hr == Win32.HRESULT.S_OK) + { + if (caps.szPname == name) + { + return i; + } + } + } + + return Win32.WAVE_MAPPER; + } + + internal static int GetWaveOutDeviceIdByName(string name) + { + uint num = Win32.waveOutGetNumDevs(); + + Win32.WAVEOUTCAPS caps = new(); + for (int i = 0; i < num; i++) + { + Win32.HRESULT hr = (Win32.HRESULT)Win32.waveOutGetDevCaps(i, ref caps, Marshal.SizeOf(typeof(Win32.WAVEOUTCAPS))); + if (hr == Win32.HRESULT.S_OK) + { + if (caps.szPname == name) + { + return i; + } + } + } + + return Win32.WAVE_MAPPER; + } + } +} From 106d0d60787ab8a0407fbc56764c0021c241e0ae Mon Sep 17 00:00:00 2001 From: JacobTech Date: Mon, 3 Jul 2023 23:24:35 -0400 Subject: [PATCH 02/14] Migration starts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • A lot of work needs to be done to make sure this works. • I already know this push won't work, but it will build. • I need to come up with a new way of storing local info. This will also bee needed to fix the very broken key system in this rushed commit. --- Luski.net.sln | 6 - Luski.net/API.cs | 36 ++ .../{Encryption.cs => ClientEncryption.cs} | 104 +++-- Luski.net/Enums/Branch.cs | 8 - Luski.net/Enums/DataType.cs | 2 +- Luski.net/Enums/{ => Main}/ChannelType.cs | 2 +- Luski.net/Enums/{ => Main}/FriendStatus.cs | 2 +- Luski.net/Enums/{ => Main}/UserFlag.cs | 2 +- Luski.net/Enums/Public/ChannelType.cs | 6 + Luski.net/Enums/ServerType.cs | 9 + Luski.net/Interfaces/IAppUser.cs | 11 + Luski.net/Interfaces/IAudioClient.cs | 48 -- Luski.net/Interfaces/IChannel.cs | 32 -- Luski.net/Interfaces/IServer.cs | 38 ++ Luski.net/Interfaces/IUser.cs | 23 +- Luski.net/Interfaces/IWebRequest.cs | 6 + Luski.net/JsonTypes/BaseTypes/HTTPRequest.cs | 3 +- Luski.net/JsonTypes/BaseTypes/IncomingWSS.cs | 4 +- .../JsonTypes/BaseTypes/SocketUserBase.cs | 118 ----- Luski.net/JsonTypes/HTTP/FriendRequest.cs | 12 +- Luski.net/JsonTypes/OfflineData.cs | 21 + Luski.net/JsonTypes/SocketAppUser.cs | 178 -------- Luski.net/JsonTypes/SocketDMChannel.cs | 33 -- Luski.net/Luski.net.csproj | 10 +- Luski.net/Server.Account.cs | 174 ++++++++ Luski.net/Server.Cleanup.cs | 17 - Luski.net/Server.Constructors.cs | 129 ------ Luski.net/Server.CreateAccount.cs | 93 ---- Luski.net/Server.Events.cs | 17 +- Luski.net/Server.Globals.cs | 76 ++-- Luski.net/Server.Incoming.cs | 83 ++-- Luski.net/Server.Login.cs | 12 - Luski.net/Server.cs | 359 +++++++-------- Luski.net/Server.old.Globals.cs | 97 ++++ Luski.net/Server.old.cs | 281 ++++++++++++ Luski.net/Sockets/SocketAudioClient.cs | 388 ---------------- Luski.net/Sound/Devices.cs | 58 --- Luski.net/Sound/JitterBuffer.cs | 124 ------ Luski.net/Sound/Player.cs | 417 ------------------ Luski.net/Sound/Protocol.cs | 95 ---- Luski.net/Sound/RTPPacket.cs | 139 ------ Luski.net/Sound/Recorder.cs | 340 -------------- Luski.net/Sound/Timer.cs | 64 --- Luski.net/Sound/Utils.cs | 195 -------- Luski.net/Sound/WaveFile.cs | 104 ----- Luski.net/Sound/Win32.cs | 241 ---------- Luski.net/Sound/WinSound.cs | 94 ---- Luski.net/{JsonTypes => Structures}/File.cs | 32 +- .../Structures/Main/MainSocketAppUser.cs | 194 ++++++++ .../Main/MainSocketChannel.cs} | 97 ++-- .../Structures/Main/MainSocketDMChannel.cs | 34 ++ .../Main/MainSocketGroupChannel.cs} | 8 +- .../Main/MainSocketRemoteUser.cs} | 16 +- .../Main/MainSocketTextChannel.cs} | 58 ++- .../Structures/Public/PublicSocketAppUser.cs | 35 ++ .../SocketBulkMessage.cs | 3 +- .../SocketMessage.cs | 45 +- Luski.net/Structures/SocketUserBase.cs | 53 +++ 58 files changed, 1428 insertions(+), 3458 deletions(-) create mode 100644 Luski.net/API.cs rename Luski.net/{Encryption.cs => ClientEncryption.cs} (88%) delete mode 100755 Luski.net/Enums/Branch.cs rename Luski.net/Enums/{ => Main}/ChannelType.cs (61%) rename Luski.net/Enums/{ => Main}/FriendStatus.cs (71%) rename Luski.net/Enums/{ => Main}/UserFlag.cs (79%) create mode 100755 Luski.net/Enums/Public/ChannelType.cs create mode 100644 Luski.net/Enums/ServerType.cs create mode 100644 Luski.net/Interfaces/IAppUser.cs delete mode 100755 Luski.net/Interfaces/IAudioClient.cs delete mode 100755 Luski.net/Interfaces/IChannel.cs create mode 100644 Luski.net/Interfaces/IServer.cs create mode 100644 Luski.net/Interfaces/IWebRequest.cs delete mode 100755 Luski.net/JsonTypes/BaseTypes/SocketUserBase.cs create mode 100644 Luski.net/JsonTypes/OfflineData.cs delete mode 100755 Luski.net/JsonTypes/SocketAppUser.cs delete mode 100755 Luski.net/JsonTypes/SocketDMChannel.cs create mode 100644 Luski.net/Server.Account.cs delete mode 100755 Luski.net/Server.Cleanup.cs delete mode 100755 Luski.net/Server.Constructors.cs delete mode 100755 Luski.net/Server.CreateAccount.cs mode change 100755 => 100644 Luski.net/Server.Events.cs mode change 100755 => 100644 Luski.net/Server.Globals.cs mode change 100755 => 100644 Luski.net/Server.Incoming.cs delete mode 100755 Luski.net/Server.Login.cs mode change 100755 => 100644 Luski.net/Server.cs create mode 100755 Luski.net/Server.old.Globals.cs create mode 100755 Luski.net/Server.old.cs delete mode 100755 Luski.net/Sockets/SocketAudioClient.cs delete mode 100755 Luski.net/Sound/Devices.cs delete mode 100755 Luski.net/Sound/JitterBuffer.cs delete mode 100755 Luski.net/Sound/Player.cs delete mode 100755 Luski.net/Sound/Protocol.cs delete mode 100755 Luski.net/Sound/RTPPacket.cs delete mode 100755 Luski.net/Sound/Recorder.cs delete mode 100755 Luski.net/Sound/Timer.cs delete mode 100755 Luski.net/Sound/Utils.cs delete mode 100755 Luski.net/Sound/WaveFile.cs delete mode 100755 Luski.net/Sound/Win32.cs delete mode 100755 Luski.net/Sound/WinSound.cs rename Luski.net/{JsonTypes => Structures}/File.cs (76%) create mode 100755 Luski.net/Structures/Main/MainSocketAppUser.cs rename Luski.net/{JsonTypes/SocketChannel.cs => Structures/Main/MainSocketChannel.cs} (51%) create mode 100755 Luski.net/Structures/Main/MainSocketDMChannel.cs rename Luski.net/{JsonTypes/SocketGroupChannel.cs => Structures/Main/MainSocketGroupChannel.cs} (59%) rename Luski.net/{JsonTypes/SocketRemoteUser.cs => Structures/Main/MainSocketRemoteUser.cs} (60%) rename Luski.net/{JsonTypes/SocketTextChannel.cs => Structures/Main/MainSocketTextChannel.cs} (71%) create mode 100755 Luski.net/Structures/Public/PublicSocketAppUser.cs rename Luski.net/{JsonTypes => Structures}/SocketBulkMessage.cs (89%) rename Luski.net/{JsonTypes => Structures}/SocketMessage.cs (50%) create mode 100644 Luski.net/Structures/SocketUserBase.cs diff --git a/Luski.net.sln b/Luski.net.sln index f68549b..4c1e359 100755 --- a/Luski.net.sln +++ b/Luski.net.sln @@ -5,8 +5,6 @@ VisualStudioVersion = 17.0.31717.71 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Luski.net", "Luski.net\Luski.net.csproj", "{3DF9B870-51B3-4338-84EC-75E4B8802F0C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Luski.net.tests", "Luski.net.tests\Luski.net.tests.csproj", "{FCA149C8-379B-454A-962A-856F30965C4E}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -17,10 +15,6 @@ Global {3DF9B870-51B3-4338-84EC-75E4B8802F0C}.Debug|Any CPU.Build.0 = Debug|Any CPU {3DF9B870-51B3-4338-84EC-75E4B8802F0C}.Release|Any CPU.ActiveCfg = Release|Any CPU {3DF9B870-51B3-4338-84EC-75E4B8802F0C}.Release|Any CPU.Build.0 = Release|Any CPU - {FCA149C8-379B-454A-962A-856F30965C4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FCA149C8-379B-454A-962A-856F30965C4E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FCA149C8-379B-454A-962A-856F30965C4E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FCA149C8-379B-454A-962A-856F30965C4E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Luski.net/API.cs b/Luski.net/API.cs new file mode 100644 index 0000000..a6c87f0 --- /dev/null +++ b/Luski.net/API.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; +using System.Linq; +using Luski.net.Structures.Main; +using Luski.net.Structures.Public; + +namespace Luski.net; + +public class API +{ + public Server MainServer { get; internal set; } + internal List> InternalServers { get; } = new(); + public IReadOnlyList> LoadedServers => InternalServers.AsReadOnly(); + + public Server GetPublicServer(string Domain, string Version = "v1") + { + IEnumerable> isl = InternalServers.Where(a => (a.Domain == Domain && a.ApiVersion == Version)); + if (isl.Any()) return isl.First(); + Server s = new() + { + Domain = Domain, + ApiVersion = Version, + }; + InternalServers.Add(s); + return s; + } + + public Server GetMainServer(string Domain, string Version = "v1") + { + MainServer = new() + { + Domain = Domain, + ApiVersion = Version, + }; + return MainServer; + } +} \ No newline at end of file diff --git a/Luski.net/Encryption.cs b/Luski.net/ClientEncryption.cs similarity index 88% rename from Luski.net/Encryption.cs rename to Luski.net/ClientEncryption.cs index 8d62272..4d86d68 100755 --- a/Luski.net/Encryption.cs +++ b/Luski.net/ClientEncryption.cs @@ -3,15 +3,16 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net; using System.Net.Http; using System.Security.Cryptography; using System.Text; using System.Text.Json; -using System.Threading.Tasks; +using JacobTechEncryption; namespace Luski.net { - public static class Encryption + public static class ClientEncryption { internal static string? MyPublicKey; internal static readonly UnicodeEncoding Encoder = new(); @@ -23,15 +24,7 @@ namespace Luski.net internal static string? outofkey = null; internal static string pw = ""; public static int NewKeySize = 4096; - internal static string ServerPublicKey - { - get - { - if (_serverpublickey is null) _serverpublickey = new HttpClient().GetAsync($"https://{Server.InternalDomain}/{Server.API_Ver}/Keys/PublicKey").Result.Content.ReadAsStringAsync().Result; - return _serverpublickey; - } - } - + public static void GenerateKeys() { if (!Generating) @@ -43,7 +36,7 @@ namespace Luski.net } } - internal static void GenerateNewKeys(out string Public, out string Private) + public static void GenerateNewKeys(out string Public, out string Private) { using RSACryptoServiceProvider r = new(NewKeySize); Private = r.ToXmlString(true); @@ -55,16 +48,22 @@ namespace Luski.net { internal static void SetOfflineKey(string key) { - MakeFile(Server.GetKeyFilePath, pw); - LuskiDataFile? fileLayout = JsonSerializer.Deserialize(FileString(Server.GetKeyFilePath, pw)); + MakeFile("Server.GetKeyFilePath", pw); + LuskiDataFile? fileLayout = JsonSerializer.Deserialize(FileString("Server.GetKeyFilePath", pw)); fileLayout.OfflineKey = key; - fileLayout.Save(Server.GetKeyFilePath, pw); + fileLayout.Save("Server.GetKeyFilePath", pw); + } + + public static LuskiDataFile GetFile() + { + MakeFile("Server.GetKeyFilePath", pw); + return JsonSerializer.Deserialize(FileString("Server.GetKeyFilePath", pw))!; } internal static string? GetOfflineKey() { - MakeFile(Server.GetKeyFilePath, pw); - LuskiDataFile? fileLayout = JsonSerializer.Deserialize(FileString(Server.GetKeyFilePath, pw)); + MakeFile("Server.GetKeyFilePath", pw); + LuskiDataFile? fileLayout = JsonSerializer.Deserialize(FileString("Server.GetKeyFilePath", pw)); return fileLayout?.OfflineKey; } @@ -122,32 +121,13 @@ namespace Luski.net #pragma warning disable CS8603 // Possible null reference return. if (channel == 0) return myPrivateKey; #pragma warning restore CS8603 // Possible null reference return. - MakeFile(Server.GetKeyFilePath, pw); - fileLayout = JsonSerializer.Deserialize(FileString(Server.GetKeyFilePath, pw)); + MakeFile("Server.GetKeyFilePath", pw); + fileLayout = JsonSerializer.Deserialize(FileString("Server.GetKeyFilePath", pw)); lis = fileLayout?.channels?.Where(s => s.id == channel); if (lis?.Count() > 0) { return lis.First().key; } - foreach (Branch b in (Branch[])Enum.GetValues(typeof(Branch))) - { - if (b != Server.Branch) - { - try - { - string temp = GetKeyBranch(channel, b); - if (temp is not null) - { - AddKey(channel, temp); - return temp; - } - } - catch - { - - } - } - } throw new Exception("You dont have a key for that channel"); } finally @@ -157,7 +137,7 @@ namespace Luski.net } } - internal static string GetKeyBranch(long channel, Branch branch) + internal static string GetKeyBranch(long channel) { LuskiDataFile? fileLayout; IEnumerable? lis; @@ -166,8 +146,8 @@ namespace Luski.net #pragma warning disable CS8603 // Possible null reference return. if (channel == 0) return myPrivateKey; #pragma warning restore CS8603 // Possible null reference return. - MakeFile(Server.GetKeyFilePathBr(branch.ToString()), pw); - fileLayout = JsonSerializer.Deserialize(FileString(Server.GetKeyFilePathBr(branch.ToString()), pw)); + MakeFile("Server.GetKeyFilePathBr(branch.ToString())", pw); + fileLayout = JsonSerializer.Deserialize(FileString("", pw)); lis = fileLayout?.channels?.Where(s => s.id == channel); if (lis?.Count() > 0) { @@ -184,10 +164,10 @@ namespace Luski.net public static void AddKey(long channel, string key) { - MakeFile(Server.GetKeyFilePath, pw); - LuskiDataFile? fileLayout = JsonSerializer.Deserialize(FileString(Server.GetKeyFilePath, pw)); + MakeFile("Server.GetKeyFilePath", pw); + LuskiDataFile? fileLayout = JsonSerializer.Deserialize(FileString("Server.GetKeyFilePath", pw)); fileLayout?.Addchannelkey(channel, key); - fileLayout?.Save(Server.GetKeyFilePath, pw); + fileLayout?.Save("Server.GetKeyFilePath", pw); } } @@ -210,9 +190,9 @@ namespace Luski.net internal static LuskiDataFile GetDefualtDataFile() { - return GetDataFile(Server.GetKeyFilePath, pw); + return GetDataFile("Server.GetKeyFilePath", pw); } - + public ChannelLayout[]? channels { get; set; } = default!; public string? OfflineKey { get; set; } = default!; @@ -273,6 +253,17 @@ namespace Luski.net chans?.Add(l); channels = chans?.ToArray(); } + else + { + chans.Remove(chans.Where(s => s.id == chan).First()); + ChannelLayout l = new() + { + id = chan, + key = Key + }; + chans?.Add(l); + channels = chans?.ToArray(); + } } } @@ -282,7 +273,7 @@ namespace Luski.net public string key { get; set; } = default!; } } - + /* public class AES { public static string Encrypt(string path, string Password) @@ -353,7 +344,7 @@ namespace Luski.net cs.Dispose(); fsCrypt.Close(); fsCrypt.Dispose(); - NewPath = p;*/ + NewPath = p; } public static void Decrypt(byte[] data, string Password, string File) @@ -387,7 +378,7 @@ namespace Luski.net fsOut.Dispose(); } } - +*/ internal const int PasswordVersion = 0; internal static byte[] LocalPasswordEncrypt(byte[] Password, int PasswordVersion = PasswordVersion) @@ -403,10 +394,15 @@ namespace Luski.net { return PasswordVersion switch { - 0 => Convert.ToBase64String(Encrypt(LocalPasswordEncrypt(Password, PasswordVersion))), + 0 => Convert.ToBase64String(Encryption.RSA.Encrypt(LocalPasswordEncrypt(Password, PasswordVersion), MyPublicKey)), _ => throw new ArgumentException("The value provided was not accepted", nameof(PasswordVersion)), }; } +/* + public static byte[] Hash(byte[] data) + { + return SHA256.Create().ComputeHash(data); + } internal static byte[] Encrypt(string data) { @@ -427,7 +423,7 @@ namespace Luski.net { using RSACryptoServiceProvider rsa = new(); rsa.FromXmlString(key); - int size = rsa.KeySize / 8; + int size = ((rsa.KeySize - 384) / 8) + 7; double x = data.Length / (double)size; int bbb = int.Parse(x.ToString().Split('.')[0]); if (x.ToString().Contains('.')) bbb++; @@ -443,7 +439,7 @@ namespace Luski.net MaxDegreeOfParallelism = num }, i => { - decccc[i] = rsa.Encrypt(data.Skip(i * size).Take(size).ToArray(), false); + decccc[i] = rsa.Encrypt(data.Skip(i * size).Take(size).ToArray(), true); }); foreach (byte[] dataa in decccc) { @@ -495,7 +491,7 @@ namespace Luski.net MaxDegreeOfParallelism = num }, i => { - decccc[i] = rsa.Decrypt(EncryptedText.Skip(i * size).Take(size).ToArray(), false); + decccc[i] = rsa.Decrypt(EncryptedText.Skip(i * size).Take(size).ToArray(), true); }); foreach (byte[] data in decccc) { @@ -510,6 +506,6 @@ namespace Luski.net } } return datasplitout; - } + }*/ } } diff --git a/Luski.net/Enums/Branch.cs b/Luski.net/Enums/Branch.cs deleted file mode 100755 index ccc27b3..0000000 --- a/Luski.net/Enums/Branch.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Luski.net.Enums; - -public enum Branch : short -{ - Dev, - Beta, - Master, -} diff --git a/Luski.net/Enums/DataType.cs b/Luski.net/Enums/DataType.cs index ff8ca21..37dea2a 100755 --- a/Luski.net/Enums/DataType.cs +++ b/Luski.net/Enums/DataType.cs @@ -1,6 +1,6 @@ namespace Luski.net.Enums; -internal enum DataType +public enum DataType { Message_Create, Status_Update, diff --git a/Luski.net/Enums/ChannelType.cs b/Luski.net/Enums/Main/ChannelType.cs similarity index 61% rename from Luski.net/Enums/ChannelType.cs rename to Luski.net/Enums/Main/ChannelType.cs index c2bf1c5..09fe9c0 100755 --- a/Luski.net/Enums/ChannelType.cs +++ b/Luski.net/Enums/Main/ChannelType.cs @@ -1,4 +1,4 @@ -namespace Luski.net.Enums; +namespace Luski.net.Enums.Main; public enum ChannelType : short { diff --git a/Luski.net/Enums/FriendStatus.cs b/Luski.net/Enums/Main/FriendStatus.cs similarity index 71% rename from Luski.net/Enums/FriendStatus.cs rename to Luski.net/Enums/Main/FriendStatus.cs index 1651479..75aad9b 100755 --- a/Luski.net/Enums/FriendStatus.cs +++ b/Luski.net/Enums/Main/FriendStatus.cs @@ -1,4 +1,4 @@ -namespace Luski.net.Enums; +namespace Luski.net.Enums.Main; public enum FriendStatus { diff --git a/Luski.net/Enums/UserFlag.cs b/Luski.net/Enums/Main/UserFlag.cs similarity index 79% rename from Luski.net/Enums/UserFlag.cs rename to Luski.net/Enums/Main/UserFlag.cs index d4d0a35..aa1378a 100755 --- a/Luski.net/Enums/UserFlag.cs +++ b/Luski.net/Enums/Main/UserFlag.cs @@ -1,6 +1,6 @@ using System; -namespace Luski.net.Enums; +namespace Luski.net.Enums.Main; [Flags] public enum UserFlag : short diff --git a/Luski.net/Enums/Public/ChannelType.cs b/Luski.net/Enums/Public/ChannelType.cs new file mode 100755 index 0000000..e52037b --- /dev/null +++ b/Luski.net/Enums/Public/ChannelType.cs @@ -0,0 +1,6 @@ +namespace Luski.net.Enums.Public; + +public enum ChannelType : short +{ + TextAndVoice = 0 +} diff --git a/Luski.net/Enums/ServerType.cs b/Luski.net/Enums/ServerType.cs new file mode 100644 index 0000000..cbace6a --- /dev/null +++ b/Luski.net/Enums/ServerType.cs @@ -0,0 +1,9 @@ +namespace Luski.net.Enums; + +public enum ServerType : byte +{ + Public = 0, + Main = 1, + PublicSub = 2, + MainSub = 3 +} \ No newline at end of file diff --git a/Luski.net/Interfaces/IAppUser.cs b/Luski.net/Interfaces/IAppUser.cs new file mode 100644 index 0000000..d29c9b3 --- /dev/null +++ b/Luski.net/Interfaces/IAppUser.cs @@ -0,0 +1,11 @@ +using Luski.net.Enums; +using Luski.net.Structures.Public; + +namespace Luski.net.Interfaces; + +public interface IAppUser : IUser +{ + public ErrorCode? Error { get; } + public string? ErrorMessage { get; } + public string Username { get; } +} \ No newline at end of file diff --git a/Luski.net/Interfaces/IAudioClient.cs b/Luski.net/Interfaces/IAudioClient.cs deleted file mode 100755 index dc21ee5..0000000 --- a/Luski.net/Interfaces/IAudioClient.cs +++ /dev/null @@ -1,48 +0,0 @@ -using Luski.net.Sound; -using System; -using System.Threading.Tasks; -using static Luski.net.Exceptions; - -namespace Luski.net.Interfaces; - -public interface IAudioClient -{ - /// - /// the event is fired when your has joined the call - /// - event Func Connected; - /// - /// Tells you if you are muted - /// - bool Muted { get; } - /// - /// Tells you if you are deafned - /// - bool Deafened { get; } - /// - /// Toggles if you are speaking to your friends - /// - void ToggleMic(); - /// - /// Toggles if you can hear audio - /// - void ToggleAudio(); - /// - /// Changes what the call gets its data from - /// - /// This is the you want to recored from - /// - void RecordSoundFrom(RecordingDevice Device); - /// - /// Changes what the call gets its data from - /// - /// This is the you want to heare outhers - /// - void PlaySoundTo(PlaybackDevice Device); - /// - /// Joins the Voice call - /// - /// - void JoinCall(); - void LeaveCall(); -} diff --git a/Luski.net/Interfaces/IChannel.cs b/Luski.net/Interfaces/IChannel.cs deleted file mode 100755 index 98ab035..0000000 --- a/Luski.net/Interfaces/IChannel.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Luski.net.Enums; -using Luski.net.JsonTypes; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace Luski.net.Interfaces; - -/// -/// contains a list of variables that all channels from luski have -/// -public interface ITextChannel -{ - long Id { get; } - string Title { get; } - string Description { get; } - string Key { get; } - /// - /// returns the current of the - /// - ChannelType Type { get; } - /// - /// Sends a to the server for the currently selected - /// - /// The messate you want to send to the server - Task SendMessage(string Message, params File[] Files); - Task SendKeysToUsers(); - Task GetMessage(long ID); - Task> GetMessages(long Message_Id, int count = 50); - Task> GetMessages(int count = 50); - Task GetPicture(); - IReadOnlyList Members { get; } -} diff --git a/Luski.net/Interfaces/IServer.cs b/Luski.net/Interfaces/IServer.cs new file mode 100644 index 0000000..5214204 --- /dev/null +++ b/Luski.net/Interfaces/IServer.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using System.Net.Http; +using System.Text.Json.Serialization.Metadata; +using System.Threading; +using System.Threading.Tasks; +using Luski.net.JsonTypes.BaseTypes; +using Luski.net.Structures; +using Luski.net.Structures.Public; + +namespace Luski.net.Interfaces; + +public interface IServer +{ + public IAppUser IAppUser { get; } + public string Cache { get; } + public void SendServer(Tvalue Payload, JsonTypeInfo jsonTypeInfo) where Tvalue : IncomingWSS; + + public HttpResponseMessage GetFromServer(string Path, CancellationToken CancellationToken, + params KeyValuePair[] Headers); + + public Task GetFromServer(string Path, string File, CancellationToken CancellationToken, + params KeyValuePair[] Headers); + + public Task GetFromServer(string Path, JsonTypeInfo Type, + CancellationToken CancellationToken, params KeyValuePair[] Headers) + where Tresult : IncomingHTTP, new(); + + public Task SendServer(string Path, Tvalue Payload, + JsonTypeInfo jsonTypeInfo, JsonTypeInfo ReturnjsonTypeInfo, + CancellationToken CancellationToken, params KeyValuePair[] Headers) + where Tvalue : IWebRequest where Tresult : IncomingHTTP, new(); + + public Task SendServer(string Path, string File, JsonTypeInfo ReturnjsonTypeInfo, + CancellationToken CancellationToken, params KeyValuePair[] Headers) + where Tresult : IncomingHTTP, new(); + public Task GetUser(long UserID, CancellationToken CancellationToken) where Tuser : SocketUserBase, new(); + +} \ No newline at end of file diff --git a/Luski.net/Interfaces/IUser.cs b/Luski.net/Interfaces/IUser.cs index 1355fea..d06108f 100755 --- a/Luski.net/Interfaces/IUser.cs +++ b/Luski.net/Interfaces/IUser.cs @@ -1,5 +1,7 @@ -using Luski.net.Enums; +using System.Threading; +using Luski.net.Enums; using System.Threading.Tasks; +using Luski.net.JsonTypes; namespace Luski.net.Interfaces; @@ -15,12 +17,7 @@ public interface IUser /// /// The cerrent username of the user /// - string Username { get; } - /// - /// The current tag for the user - /// Ex: #1234 - /// - //short Tag { get; } + string DisplayName { get; } /// /// The current status of the user /// @@ -30,16 +27,16 @@ public interface IUser /// PictureType PictureType { get; } /// - /// the current flags of a user - /// - UserFlag Flags { get; } - /// /// Gets the current avatar of the user /// - Task GetAvatar(); + Task GetAvatar(CancellationToken CancellationToken); /// /// Gets the current user key /// /// - Task GetUserKey(); + Task GetUserKey(CancellationToken CancellationToken); + /// + /// The server that the user comes from + /// + IServer Server { get; } } diff --git a/Luski.net/Interfaces/IWebRequest.cs b/Luski.net/Interfaces/IWebRequest.cs new file mode 100644 index 0000000..8ebc582 --- /dev/null +++ b/Luski.net/Interfaces/IWebRequest.cs @@ -0,0 +1,6 @@ +namespace Luski.net.Interfaces; + +public interface IWebRequest +{ + +} \ No newline at end of file diff --git a/Luski.net/JsonTypes/BaseTypes/HTTPRequest.cs b/Luski.net/JsonTypes/BaseTypes/HTTPRequest.cs index f539e17..f54a522 100755 --- a/Luski.net/JsonTypes/BaseTypes/HTTPRequest.cs +++ b/Luski.net/JsonTypes/BaseTypes/HTTPRequest.cs @@ -1,9 +1,10 @@ using System.Text.Json.Serialization; using Luski.net.Enums; +using Luski.net.Interfaces; namespace Luski.net.JsonTypes.BaseTypes; -internal class HTTPRequest +internal class HTTPRequest : IWebRequest { [JsonPropertyName("data_type")] [JsonInclude] diff --git a/Luski.net/JsonTypes/BaseTypes/IncomingWSS.cs b/Luski.net/JsonTypes/BaseTypes/IncomingWSS.cs index ec2ba6f..66b4fd9 100755 --- a/Luski.net/JsonTypes/BaseTypes/IncomingWSS.cs +++ b/Luski.net/JsonTypes/BaseTypes/IncomingWSS.cs @@ -3,7 +3,7 @@ using System.Text.Json.Serialization; namespace Luski.net.JsonTypes.BaseTypes; -internal class IncomingWSS +public class IncomingWSS { [JsonPropertyName("type")] [JsonInclude] @@ -18,7 +18,7 @@ internal class IncomingWSS GenerationMode = JsonSourceGenerationMode.Default, PropertyNamingPolicy = JsonKnownNamingPolicy.Unspecified, WriteIndented = false)] -internal partial class IncomingWSSContext : JsonSerializerContext +public partial class IncomingWSSContext : JsonSerializerContext { } diff --git a/Luski.net/JsonTypes/BaseTypes/SocketUserBase.cs b/Luski.net/JsonTypes/BaseTypes/SocketUserBase.cs deleted file mode 100755 index 4577c75..0000000 --- a/Luski.net/JsonTypes/BaseTypes/SocketUserBase.cs +++ /dev/null @@ -1,118 +0,0 @@ -using Luski.net.Enums; -using Luski.net.Interfaces; -using System; -using System.ComponentModel; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text.Json.Serialization; -using System.Text.Json.Serialization.Metadata; -using System.Threading.Tasks; - -namespace Luski.net.JsonTypes.BaseTypes; - -[Browsable(false)] -[EditorBrowsable(EditorBrowsableState.Never)] -public abstract class SocketUserBase : IncomingHTTP, IUser -{ - [JsonPropertyName("id")] - [JsonInclude] - public long Id { get; internal set; } = default!; - [JsonPropertyName("username")] - [JsonInclude] - public string Username { get; internal set; } = default!; - // [JsonPropertyName("tag")] - //[JsonInclude] - //public short Tag { get; internal set; } = default!; - [JsonPropertyName("status")] - [JsonInclude] - public UserStatus Status { get; internal set; } = default!; - [JsonPropertyName("picture_type")] - [JsonInclude] - public PictureType PictureType { get; internal set; } = default!; - - [JsonPropertyName("flags")] - [JsonInclude] - public UserFlag Flags { get; internal set; } = default!; - - public async Task GetAvatar() - { - if (Server.Cache != null) - { - bool isc = System.IO.File.Exists($"{Server.Cache}/avatars/{Id}"); - if (!isc) await Server.GetFromServer($"socketuserprofile/Avatar/{Id}", $"{Server.Cache}/avatars/{Id}"); - } - return System.IO.File.ReadAllBytes($"{Server.Cache}/avatars/{Id}"); - } - - public Task GetUserKey() - { - if (Server._user is null) throw new Exception("you are not loged in"); - if (Id == Server._user.Id && Encryption.MyPublicKey is not null) return Task.FromResult(Encryption.MyPublicKey); - string data = Server.GetFromServer($"Keys/GetUserKey/{Id}").Content.ReadAsStringAsync().Result; - return Task.FromResult(data); - } - - internal static async Task GetUser(long UserId, JsonTypeInfo Json) where TUser : SocketUserBase, new() - { - TUser user; - if (Server.poeople is null) Server.poeople = new(); - if (Server.poeople.Count > 0 && Server.poeople.Any(s => s.Id == UserId)) - { - TUser temp = Server.poeople.Where(s => s is TUser && s.Id == UserId).Cast().FirstOrDefault()!; - if (temp is SocketRemoteUser && (temp as SocketRemoteUser)!.Channel == null) - { - foreach (SocketDMChannel chan in Server.chans.Where(s => s is SocketDMChannel).Cast()) - { - if (chan.Type == ChannelType.DM && chan.Id != 0 && chan.MemberIdList is not null) - { - if (chan.MemberIdList.Any(s => s == UserId)) (temp as SocketRemoteUser)!.Channel = chan; - } - } - } - return temp!; - } - while (true) - { - if (Server.CanRequest) - { - user = await Server.GetFromServer("socketuser", - Json, - new System.Collections.Generic.KeyValuePair("id", UserId.ToString())); - break; - } - } - - if (user is null) throw new Exception("Server did not return a user"); - if (Server.poeople.Count > 0 && Server.poeople.Any(s => s.Id == UserId)) - { - foreach (IUser? p in Server.poeople.Where(s => s.Id == UserId)) - { - Server.poeople.Remove(p); - } - } - if (Server._user is not null && UserId != 0 && UserId != Server._user.Id) - { - foreach (SocketDMChannel chan in Server.chans.Where(s => s is SocketDMChannel).Cast()) - { - if (chan.Id != 0 && chan.MemberIdList is not null) - { - if (chan.MemberIdList.Any(s => s == UserId)) (user as SocketRemoteUser)!.Channel = chan; - } - } - } - Server.poeople.Add(user); - return user; - } -} - -[JsonSerializable(typeof(SocketUserBase))] -[JsonSourceGenerationOptions( - GenerationMode = JsonSourceGenerationMode.Default, - PropertyNamingPolicy = JsonKnownNamingPolicy.Unspecified, - WriteIndented = false, - DefaultIgnoreCondition = JsonIgnoreCondition.Never)] -internal partial class SocketUserBaseContext : JsonSerializerContext -{ - -} diff --git a/Luski.net/JsonTypes/HTTP/FriendRequest.cs b/Luski.net/JsonTypes/HTTP/FriendRequest.cs index 528d4c4..707818b 100755 --- a/Luski.net/JsonTypes/HTTP/FriendRequest.cs +++ b/Luski.net/JsonTypes/HTTP/FriendRequest.cs @@ -5,18 +5,12 @@ namespace Luski.net.JsonTypes.HTTP; internal class FriendRequest : HTTPRequest { + [JsonPropertyName("code")] + [JsonInclude] + public string code { get; set; } = default!; [JsonPropertyName("id")] [JsonInclude] public long Id { get; set; } = default!; - [JsonPropertyName("subtype")] - [JsonInclude] - public int SubType { get; set; } = default!; - [JsonPropertyName("username")] - [JsonInclude] - public string Username { get; set; } = default!; - [JsonPropertyName("tag")] - [JsonInclude] - public short Tag { get; set; } = default!; } [JsonSerializable(typeof(FriendRequest))] diff --git a/Luski.net/JsonTypes/OfflineData.cs b/Luski.net/JsonTypes/OfflineData.cs new file mode 100644 index 0000000..2d88763 --- /dev/null +++ b/Luski.net/JsonTypes/OfflineData.cs @@ -0,0 +1,21 @@ +using System.Text.Json.Serialization; +using Luski.net.JsonTypes.BaseTypes; + +namespace Luski.net.JsonTypes; + +public class OfflineData : IncomingHTTP +{ + [JsonPropertyName("data")] + [JsonInclude] + public string[]? Data { get; internal set; } = default!; +} + +[JsonSerializable(typeof(OfflineData))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + WriteIndented = false)] +internal partial class OfflineDataContext : JsonSerializerContext +{ + +} \ No newline at end of file diff --git a/Luski.net/JsonTypes/SocketAppUser.cs b/Luski.net/JsonTypes/SocketAppUser.cs deleted file mode 100755 index 15b5e60..0000000 --- a/Luski.net/JsonTypes/SocketAppUser.cs +++ /dev/null @@ -1,178 +0,0 @@ -using Luski.net.Interfaces; -using Luski.net.JsonTypes.BaseTypes; -using System.Collections.Generic; -using System.Linq; -using System.Text.Json.Serialization; - -namespace Luski.net.JsonTypes; - -public class SocketAppUser : SocketUserBase -{ - [JsonPropertyName("email")] - [JsonInclude] - public string Email { get; internal set; } = default!; - [JsonIgnore] - public IReadOnlyList Channels - { - get - { - if (_Channels is null || ChannelIdList is not null) - { - if (ChannelIdList.Length != 0) - { - _Channels = new List(); - foreach (long channel in ChannelIdList) - { - SocketChannel s = SocketChannel.GetChannel(channel, SocketChannelContext.Default.SocketChannel).Result; - Server.chans.Remove(s); - switch (s.Type) - { - case Enums.ChannelType.GROUP: - _Channels.Add(SocketChannel.GetChannel(channel, SocketGroupChannelContext.Default.SocketGroupChannel).Result); - break; - case Enums.ChannelType.DM: - _Channels.Add(SocketChannel.GetChannel(channel, SocketDMChannelContext.Default.SocketDMChannel).Result); - break; - } - } - } - else _Channels = new List(); - } - return _Channels.AsReadOnly(); - } - } - [JsonIgnore] - public IReadOnlyList FriendRequests - { - get - { - if (_FriendRequests is null || FriendRequestsRaw is not null) - { - _FriendRequests = new(); - if (ChannelIdList.Length != 0 && FriendRequestsRaw is not null) - { - foreach (FR person in FriendRequestsRaw) - { - //_Friends.Add(SocketRemoteUser.GetUser(person)); - long id = person.user_id == Id ? person.from : person.user_id; - SocketRemoteUser frq = GetUser(id, SocketRemoteUserContext.Default.SocketRemoteUser).Result; - _FriendRequests.Add(frq); - } - } - else _FriendRequests = new(); - } - return _FriendRequests.AsReadOnly(); - } - } - [JsonIgnore] - public IReadOnlyList Friends - { - get - { - if (_Friends is null || FriendIdList is not null) - { - if (ChannelIdList.Length != 0) - { - _Friends = new List(); - foreach (long person in FriendIdList) - { - _Friends.Add(GetUser(person, SocketRemoteUserContext.Default.SocketRemoteUser).Result); - } - } - else _Friends = new List(); - } - return _Friends.AsReadOnly(); - } - } - [JsonPropertyName("selected_channel")] - [JsonInclude] - public long SelectedChannel { get; internal set; } = default!; - [JsonPropertyName("channels")] - [JsonInclude] - public long[] ChannelIdList { get; internal set; } = default!; - [JsonPropertyName("friends")] - [JsonInclude] - public long[] FriendIdList { get; internal set; } = default!; - [JsonPropertyName("friend_requests")] - [JsonInclude] - public FR[] FriendRequestsRaw { get; internal set; } = default!; - [JsonIgnore] - private List _Channels = default!; - [JsonIgnore] - private List _Friends = default!; - [JsonIgnore] - private List _FriendRequests = default!; - - public class FR - { - public long from { get; set; } = default!; - public long user_id { get; set; } = default!; - } - - internal void AddFriend(SocketRemoteUser User) - { - if (Server.poeople.Any(s => s.Id == User.Id)) - { - IEnumerable b = Server.poeople.Where(s => s.Id == User.Id); - foreach (IUser item in b) - { - Server.poeople.Remove(item); - } - Server.poeople.Add(User); - } - else - { - Server.poeople.Add(User); - } - _Friends.Add(User); - } - - internal void RemoveFriendRequest(SocketRemoteUser User) - { - if (Server.poeople.Any(s => s.Id == User.Id)) - { - IEnumerable b = Server.poeople.Where(s => s.Id == User.Id); - foreach (IUser item in b) - { - Server.poeople.Remove(item); - } - } - Server.poeople.Add(User); - foreach (SocketRemoteUser user in _FriendRequests) - { - if (User.Id == user.Id) - { - _FriendRequests.Remove(User); - } - } - } - - internal void AddFriendRequest(SocketRemoteUser User) - { - if (Server.poeople.Any(s => s.Id == User.Id)) - { - IEnumerable b = Server.poeople.Where(s => s.Id == User.Id); - foreach (IUser item in b) - { - Server.poeople.Remove(item); - } - Server.poeople.Add(User); - } - else - { - Server.poeople.Add(User); - } - _FriendRequests.Add(User); - } -} - -[JsonSerializable(typeof(SocketAppUser))] -[JsonSourceGenerationOptions( - GenerationMode = JsonSourceGenerationMode.Default, - PropertyNamingPolicy = JsonKnownNamingPolicy.Unspecified, - WriteIndented = false, - DefaultIgnoreCondition = JsonIgnoreCondition.Never)] -internal partial class SocketAppUserContext : JsonSerializerContext -{ - -} diff --git a/Luski.net/JsonTypes/SocketDMChannel.cs b/Luski.net/JsonTypes/SocketDMChannel.cs deleted file mode 100755 index 2c6ea6a..0000000 --- a/Luski.net/JsonTypes/SocketDMChannel.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Luski.net.JsonTypes.BaseTypes; -using System.Linq; -using System.Text.Json.Serialization; - -namespace Luski.net.JsonTypes; - -public class SocketDMChannel : SocketTextChannel -{ - public SocketRemoteUser User - { - get - { - if (_user is null) - { - var list = MemberIdList.ToList(); - list.Remove(Server._user!.Id); - _user = SocketUserBase.GetUser(list.FirstOrDefault(), SocketRemoteUserContext.Default.SocketRemoteUser).Result; - } - return _user; - } - } - public SocketRemoteUser _user = null!; -} - -[JsonSerializable(typeof(SocketDMChannel))] -[JsonSourceGenerationOptions( - GenerationMode = JsonSourceGenerationMode.Default, - PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, - WriteIndented = false)] -internal partial class SocketDMChannelContext : JsonSerializerContext -{ - -} diff --git a/Luski.net/Luski.net.csproj b/Luski.net/Luski.net.csproj index 87b0f19..a4622e6 100755 --- a/Luski.net/Luski.net.csproj +++ b/Luski.net/Luski.net.csproj @@ -13,21 +13,17 @@ https://github.com/JacobTech-com/Luski.net True 1.0.0 - 1.1.4-alpha + 1.1.3-alpha23 + - - - - - - + diff --git a/Luski.net/Server.Account.cs b/Luski.net/Server.Account.cs new file mode 100644 index 0000000..e450715 --- /dev/null +++ b/Luski.net/Server.Account.cs @@ -0,0 +1,174 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Security.Authentication; +using System.Text; +using System.Text.Json; +using System.Threading; +using JacobTechEncryption; +using JacobTechEncryption.Enums; +using Luski.net.Enums; +using Luski.net.JsonTypes; +using Luski.net.JsonTypes.WSS; +using Luski.net.Structures.Main; +using Luski.net.Structures.Public; +using WebSocketSharp; +using File = System.IO.File; + +namespace Luski.net; + +public partial class Server +{ + public void Login(string Email, string Password, CancellationToken CancellationToken) + { + Both(Email, Password, CancellationToken); + } + + public void CreateAccount(string Email, string Password, string Username, string PFP, CancellationToken CancellationToken) + { + Both(Email, Password, CancellationToken, Username, PFP); + } + + private void Both(string Email, string Password, CancellationToken CancellationToken, string? Username = null, string? pfp = null) + { + if (!ClientEncryption.Generating) + { + ClientEncryption.GenerateKeys(); + } + while (!ClientEncryption.Generated) { } + + login = true; + Login json; + List> heads = new() + { + new("key", ClientEncryption.MyPublicKey), + new("username", Convert.ToBase64String(Encryption.RSA.Encrypt(Email, ClientEncryption.MyPublicKey, EncoderType.UTF8))), + new("password", ClientEncryption.RemotePasswordEncrypt(Encoding.UTF8.GetBytes(Password))) + }; + if (File.Exists("LastPassVer.txt") && int.TryParse(File.ReadAllText("LastPassVer.txt"), out int lpv) && lpv < ClientEncryption.PasswordVersion && lpv >= 0) + { + heads.Add(new("old_password", ClientEncryption.RemotePasswordEncrypt(Encoding.UTF8.GetBytes(Password), lpv))); + heads.Add(new("old_version", lpv.ToString())); + } + if (pfp is not null) + { + heads.Add(new("username", Username)); + json = SendServer( + "CreateAccount", + pfp, + LoginContext.Default.Login, + CancellationToken, + heads.ToArray()).Result; + } + else + { + json = GetFromServer( + "Login", + LoginContext.Default.Login, + CancellationToken, + heads.ToArray()).Result; + } + if (json.Error is not null) throw new Exception($"Luski appears to be down at the current moment: {json.ErrorMessage}"); + if (ClientEncryption.ofkey is null || ClientEncryption.outofkey is null) throw new Exception("Something went wrong generating the offline keys"); + login = false; + if (json is not null && json.Error is null) + { + ServerOut = new WebSocket($"wss://{Domain}/WSS/{ApiVersion}"); + ServerOut.SslConfiguration.EnabledSslProtocols = SslProtocols.Tls13 | SslProtocols.Tls12; + ServerOut.OnMessage += DataFromServer; + ServerOut.WaitTime = new TimeSpan(0, 0, 5); + ServerOut.OnError += ServerOut_OnError; + ServerOut.Connect(); + SendServer(new WSSLogin() { Token = json.Token! }, WSSLoginContext.Default.WSSLogin); + while (Token is null && Error is null) + { + + } + if (Error is not null) + { + throw new Exception(Error); + } + + if (Token is null) throw new Exception("Server did not send a token"); + CanRequest = true; + long id = long.Parse(Encoding.UTF8.GetString(Convert.FromBase64String( + Token.Split('.')[0] + ))); + TUser t = new(); + User = t switch + { + MainSocketAppUser => (GetUser(id, MainSocketAppUserContext.Default.MainSocketAppUser, + CancellationToken.None) + .Result as TUser)!, + _ => (GetUser(id, PublicSocketAppUserContext.Default.PublicSocketAppUser, CancellationToken.None) + .Result as TUser)! + }; + if (User is null || User.Error is not null) + { + string error = "User was null"; + if (User is not null && User.Error is not null) error = $"{User.Error}: {User.ErrorMessage}"; + throw new Exception($"Something went wrong getting your user infermation\n{error}"); + } + Console.WriteLine("User got"); + Console.WriteLine("Insert"); + //User.Email = Email; + _ = UpdateStatus(UserStatus.Online, CancellationToken); + Console.WriteLine("stat"); + try + { + ClientEncryption.pw = Email.ToLower() + Password; + _ = ClientEncryption.File.GetOfflineKey(); + } + catch + { + try + { + ClientEncryption.pw = Email + Password; + var temp222 = ClientEncryption.File.LuskiDataFile.GetDefualtDataFile(); + ClientEncryption.pw = Email.ToLower() + Password; + if (temp222 is not null) temp222.Save("key.lsk", ClientEncryption.pw); + } + catch + { + Token = null; + Error = null; + ServerOut.Close(); + throw new Exception("The key file you have is getting the wrong pasword. Type your Email in the same way you creaated your account to fix this error."); + } + } + Console.WriteLine("req offline"); + OfflineData offlinedata = GetFromServer("Keys/GetOfflineData", OfflineDataContext.Default.OfflineData, CancellationToken).Result; + if (offlinedata.Data is not null && offlinedata.Data.Any()) Console.WriteLine(offlinedata.Data); + if (offlinedata is not null && offlinedata.Error is null && offlinedata.Data is not null && offlinedata.Data.Length > 0) + { + foreach (string keyex in offlinedata.Data) + { + Console.WriteLine(keyex); + KeyExchange? okd = JsonSerializer.Deserialize(keyex); + Console.WriteLine(okd); + if (okd is not null && !string.IsNullOrEmpty(okd.key)) + { + Console.WriteLine(okd.channel); + Console.WriteLine(okd.key); + ClientEncryption.File.Channels.AddKey(okd.channel, Encoding.UTF8.GetString(Encryption.RSA.Decrypt(Convert.FromBase64String(okd.key), ClientEncryption.File.GetOfflineKey()))); + } + } + } + Console.WriteLine("lpv"); + System.IO.File.WriteAllText("LastPassVer.txt", ClientEncryption.PasswordVersion.ToString()); + ClientEncryption.File.SetOfflineKey(ClientEncryption.ofkey); + using HttpClient setkey = new(); + setkey.DefaultRequestHeaders.Add("token", Token); + _ = setkey.PostAsync($"https://{Domain}/{ApiVersion}/Keys/SetOfflineKey", new StringContent(ClientEncryption.outofkey)).Result; + //_ = User.Channels; + foreach (var ch in chans) + { + _ = ch.Members; + } + ClientEncryption.outofkey = null; + ClientEncryption.ofkey = null; + } + else throw new Exception(json?.ErrorMessage); + } +} \ No newline at end of file diff --git a/Luski.net/Server.Cleanup.cs b/Luski.net/Server.Cleanup.cs deleted file mode 100755 index 8fbc84c..0000000 --- a/Luski.net/Server.Cleanup.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.IO; - -namespace Luski.net; - -public sealed partial class Server : IDisposable -{ - ~Server() - { - try { if (Directory.Exists(Cache)) Directory.Delete(Cache, true); } catch { } - } - - public void Dispose() - { - try { if (Directory.Exists(Cache)) Directory.Delete(Cache, true); } catch { } - } -} diff --git a/Luski.net/Server.Constructors.cs b/Luski.net/Server.Constructors.cs deleted file mode 100755 index 6cce45f..0000000 --- a/Luski.net/Server.Constructors.cs +++ /dev/null @@ -1,129 +0,0 @@ -using Luski.net.Enums; -using Luski.net.JsonTypes; -using Luski.net.JsonTypes.BaseTypes; -using Luski.net.JsonTypes.WSS; -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Text; -using WebSocketSharp; -using File = System.IO.File; - -namespace Luski.net; - -public sealed partial class Server -{ - internal Server(string Email, string Password, Branch branch = Branch.Master, string? Username = null, string? pfp = null) - { - if (!Encryption.Generating) - { - Encryption.GenerateKeys(); - } - while (!Encryption.Generated) { } - InternalDomain = $"api.{branch}.luski.JacobTech.com"; - Branch = branch; - login = true; - Login json; - List> heads = new() - { - new("key", Encryption.MyPublicKey), - new("email", Convert.ToBase64String(Encryption.Encrypt(Email))), - new("password", Encryption.RemotePasswordEncrypt(Encryption.Encoder.GetBytes(Password))) - }; - if (File.Exists("LastPassVer.txt") && int.TryParse(File.ReadAllText("LastPassVer.txt"), out int lpv) && lpv < Encryption.PasswordVersion && lpv >= 0) - { - heads.Add(new("old_password", Encryption.RemotePasswordEncrypt(Encryption.Encoder.GetBytes(Password), lpv))); - heads.Add(new("old_version", lpv.ToString())); - } - if (pfp is not null) - { - heads.Add(new("username", Username)); - json = SendServer( - "CreateAccount", - pfp, - LoginContext.Default.Login, - heads.ToArray()).Result; - } - else - { - json = GetFromServer( - "Login", - LoginContext.Default.Login, - heads.ToArray()).Result; - } - - if (json.Error is not null) throw new Exception($"Luski appears to be down at the current moment: {json.ErrorMessage}"); - if (Encryption.ofkey is null || Encryption.outofkey is null) throw new Exception("Something went wrong generating the offline keys"); - login = false; - if (json is not null && json.Error is null) - { - ServerOut = new WebSocket($"wss://{InternalDomain}/WSS/{API_Ver}"); - ServerOut.SslConfiguration.EnabledSslProtocols = System.Security.Authentication.SslProtocols.Tls13; - ServerOut.OnMessage += DataFromServer; - ServerOut.WaitTime = new TimeSpan(0, 0, 5); - ServerOut.OnError += ServerOut_OnError; - ServerOut.Connect(); - string Infermation = $"{{\"token\": \"{json.Token}\"}}"; - SendServer(new WSSLogin() { Token = json.Token! }, WSSLoginContext.Default.WSSLogin); - while (Token is null && Error is null) - { - - } - if (Error is not null) - { - throw new Exception(Error); - } - if (Token is null) throw new Exception("Server did not send a token"); - CanRequest = true; - _user = SocketUserBase.GetUser(long.Parse(Encoding.UTF8.GetString(Convert.FromBase64String(Token.Split('.')[0]))), SocketAppUserContext.Default.SocketAppUser).Result; - if (_user is null || _user.Error is not null) throw new Exception("Something went wrong getting your user infermation"); - _ = _user.Channels; - foreach (var ch in chans) - { - _ = ch.Members; - } - _user.Email = Email; - _ = UpdateStatus(UserStatus.Online); - - try - { - Encryption.pw = Email.ToLower() + Password; - _ = Encryption.File.GetOfflineKey(); - } - catch - { - try - { - Encryption.pw = Email + Password; - var temp222 = Encryption.File.LuskiDataFile.GetDefualtDataFile(); - Encryption.pw = Email.ToLower() + Password; - if (temp222 is not null) temp222.Save(GetKeyFilePath, Encryption.pw); - } - catch - { - Token = null; - Error = null; - ServerOut.Close(); - throw new Exception("The key file you have is getting the wrong pasword. Type your Email in the same way you creaated your account to fix this error."); - } - } - OfflineKeyData offlinedata = GetFromServer("Keys/GetOfflineData", OfflineKeyDataContext.Default.OfflineKeyData).Result; - if (string.IsNullOrEmpty(Encryption.File.GetOfflineKey())) Encryption.File.SetOfflineKey(Encryption.ofkey); - if (offlinedata is not null && offlinedata.Error is null && offlinedata.keys is not null) - { - foreach (KeyExchange key in offlinedata.keys) - { - Encryption.File.Channels.AddKey(key.channel, Encryption.Encoder.GetString(Encryption.Decrypt(Convert.FromBase64String(key.key), Encryption.File.GetOfflineKey()))); - } - } - System.IO.File.WriteAllText("LastPassVer.txt", Encryption.PasswordVersion.ToString()); - Encryption.File.SetOfflineKey(Encryption.ofkey); - using HttpClient setkey = new(); - setkey.DefaultRequestHeaders.Add("token", Token); - _ = setkey.PostAsync($"https://{InternalDomain}/{API_Ver}/Keys/SetOfflineKey", new StringContent(Encryption.outofkey)).Result; - Encryption.outofkey = null; - Encryption.ofkey = null; - } - else throw new Exception(json?.ErrorMessage); - } -} diff --git a/Luski.net/Server.CreateAccount.cs b/Luski.net/Server.CreateAccount.cs deleted file mode 100755 index 8dd53b7..0000000 --- a/Luski.net/Server.CreateAccount.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using System.Net.Http; -using System.Text; -using System.Text.Json; -using Luski.net.Enums; -using Luski.net.JsonTypes; -using Luski.net.JsonTypes.WSS; -using WebSocketSharp; - -namespace Luski.net; - -public sealed partial class Server -{ - internal Server(string Email, string Password, string Username, byte[] PFP, Branch branch = Branch.Master) - { - Encryption.pw = Email.ToLower() + Password; - if (!Encryption.Generating) - { - Encryption.GenerateKeys(); - } - while (!Encryption.Generated) { } - if (Encryption.ofkey is null || Encryption.outofkey is null) throw new Exception("Something went wrong generating the offline keys"); - string Result; - InternalDomain = $"api.{branch}.luski.JacobTech.com"; - Branch = branch; - using (HttpClient web = new()) - { - web.DefaultRequestHeaders.Add("key", Encryption.MyPublicKey); - web.DefaultRequestHeaders.Add("email", Convert.ToBase64String(Encryption.Encrypt(Email))); - web.DefaultRequestHeaders.Add("password", Convert.ToBase64String(Encryption.Encrypt(Password))); - web.DefaultRequestHeaders.Add("username", Username); - HttpResponseMessage? d = web.PostAsync($"https://{InternalDomain}/{API_Ver}/CreateAccount", new StringContent(Convert.ToBase64String(PFP))).Result; - if (d is null || !d.IsSuccessStatusCode) throw new Exception("Luski appears to be down at the current moment"); - Result = d.Content.ReadAsStringAsync().Result; - web.DefaultRequestHeaders.Clear(); - } - Login? json = JsonSerializer.Deserialize(Result, LoginContext.Default.Login); - if (json is not null && json.Error is null) - { - ServerOut = new WebSocket($"wss://{InternalDomain}/{API_Ver}"); - ServerOut.SslConfiguration.EnabledSslProtocols = System.Security.Authentication.SslProtocols.Tls13; - ServerOut.OnMessage += DataFromServer; - ServerOut.WaitTime = new TimeSpan(0, 0, 5); - ServerOut.OnError += ServerOut_OnError; - ServerOut.Connect(); - string Infermation = $"{{\"token\": \"{json.Token}\"}}"; - SendServer(new WSSLogin() { Token = json.Token}, WSSLoginContext.Default.WSSLogin); - while (Token is null && Error is null) - { - - } - if (Error is not null) - { - throw new Exception(Error); - } - if (Token is null) throw new Exception("Server did not send a token"); - CanRequest = true; - string data; - using (HttpClient web = new()) - { - web.DefaultRequestHeaders.Add("token", Token); - web.DefaultRequestHeaders.Add("id", Encoding.UTF8.GetString(Convert.FromBase64String(Token.Split('.')[0]))); - data = web.GetAsync($"https://{InternalDomain}/{API_Ver}/SocketUser").Result.Content.ReadAsStringAsync().Result; - } - _user = JsonSerializer.Deserialize(data); - if (_user is null || _user.Error is not null) throw new Exception("Something went wrong getting your user infermation"); - _ = _user.Channels; - foreach (var ch in chans) - { - _ = ch.Members; - } - _user.Email = Email; - UpdateStatus(UserStatus.Online); - Encryption.File.SetOfflineKey(Encryption.ofkey); - using HttpClient setkey = new(); - setkey.DefaultRequestHeaders.Add("token", Token); - _ = setkey.PostAsync($"https://{InternalDomain}/{API_Ver}/Keys/SetOfflineKey", new StringContent(Encryption.outofkey)).Result; - Encryption.outofkey = null; - Encryption.ofkey = null; - } - else throw new Exception(json?.ErrorMessage); - } - - public static Server CreateAccount(string Email, string Password, string Username, byte[] PFP, Branch branch = Branch.Master) - { - return new Server(Email, Password, Username, PFP, branch); - } - - public static Server CreateAccount(string Email, string Password, string Username, string PFP, Branch branch = Branch.Master) - { - return new Server(Email, Password, branch, Username, PFP); - } -} diff --git a/Luski.net/Server.Events.cs b/Luski.net/Server.Events.cs old mode 100755 new mode 100644 index 8ed8955..132efcd --- a/Luski.net/Server.Events.cs +++ b/Luski.net/Server.Events.cs @@ -1,21 +1,20 @@ -using Luski.net.Interfaces; -using Luski.net.JsonTypes; using System; using System.Threading.Tasks; +using Luski.net.Interfaces; +using Luski.net.JsonTypes; +using Luski.net.Structures; namespace Luski.net; -public sealed partial class Server +public partial class Server { public event Func? MessageReceived; public event Func? UserStatusUpdate; - public event Func? ReceivedFriendRequest; - - public event Func? FriendRequestResult; - - public event Func? IncommingCall; + public event Func? ReceivedFriendRequest; + public event Func? FriendRequestResult; + public event Func? OnError; -} +} \ No newline at end of file diff --git a/Luski.net/Server.Globals.cs b/Luski.net/Server.Globals.cs old mode 100755 new mode 100644 index 0832213..4959487 --- a/Luski.net/Server.Globals.cs +++ b/Luski.net/Server.Globals.cs @@ -1,31 +1,28 @@ -using Luski.net.Enums; -using Luski.net.Interfaces; -using Luski.net.JsonTypes; -using Luski.net.Sockets; using System; using System.Collections.Generic; using System.IO; +using Luski.net.Enums; +using Luski.net.Interfaces; +using Luski.net.JsonTypes; +using Luski.net.Structures.Main; +using Luski.net.Structures.Public; using WebSocketSharp; namespace Luski.net; -public sealed partial class Server +public partial class Server where TUser : class, IAppUser, new() { - internal static string JT { get { return Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "/JacobTech"; } } - internal static SocketAudioClient? AudioClient = null; - internal static string? Token = null, Error = null; - internal static bool CanRequest = false; - internal static SocketAppUser? _user; - internal static string InternalDomain = "api.master.luski.jacobtech.com", platform = "win-x64"; - internal static Branch Branch; - internal static double Percent = 0.5; - private static WebSocket? ServerOut; - private static string? gen = null; - private static bool login = false; - - public string Domain { get { return InternalDomain; } } - - internal static string Cache + public ServerType ServerType { get; internal set; } = ServerType.Public; + public string Domain { get; internal set; } = default!; + public IAppUser IAppUser => User; + public TUser User { get; internal set; } + public string ApiVersion { get; internal set; } = "v1"; + private WebSocket? ServerOut; + internal string? Token = null, Error = null, gen = null; + internal bool CanRequest = false, login = false; + internal List poeople = new(); + internal List chans { get; set; } = new(); + public string Cache { get { @@ -34,13 +31,9 @@ public sealed partial class Server if (!Directory.Exists(JT)) Directory.CreateDirectory(JT); string path = JT + "/Luski/"; if (!Directory.Exists(path)) Directory.CreateDirectory(path); - path += Branch.ToString() + "/"; - if (!Directory.Exists(path)) Directory.CreateDirectory(path); - path += platform + "/"; - if (!Directory.Exists(path)) Directory.CreateDirectory(path); path += "Data/"; if (!Directory.Exists(path)) Directory.CreateDirectory(path); - path += _user?.Id + "/"; + path += User.Id + "/"; if (!Directory.Exists(path)) Directory.CreateDirectory(path); path += "Cache/"; if (!Directory.Exists(path)) Directory.CreateDirectory(path); @@ -53,31 +46,18 @@ public sealed partial class Server return gen; } } - internal const string API_Ver = "v1"; - internal static List poeople = new(); - internal static List chans { get; set; } = new(); - internal static string GetKeyFilePath + + internal static string JT { get { - return GetKeyFilePathBr(Branch.ToString()); + string tmp = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "JacobTech"); + if (OperatingSystem.IsLinux()) + { + tmp = Path.Combine(Environment.GetEnvironmentVariable("HOME")!, ".config/"); + tmp += "JacobTech"; + } + return tmp; } } - - internal static string GetKeyFilePathBr(string br) - { - if (!Directory.Exists(JT)) Directory.CreateDirectory(JT); - string path = JT + "/Luski/"; - if (!Directory.Exists(path)) Directory.CreateDirectory(path); - path += br + "/"; - if (!Directory.Exists(path)) Directory.CreateDirectory(path); - path += platform + "/"; - if (!Directory.Exists(path)) Directory.CreateDirectory(path); - path += "Data/"; - if (!Directory.Exists(path)) Directory.CreateDirectory(path); - path += _user?.Id + "/"; - if (!Directory.Exists(path)) Directory.CreateDirectory(path); - path += "keys.lsk"; - return path; - } -} +} \ No newline at end of file diff --git a/Luski.net/Server.Incoming.cs b/Luski.net/Server.Incoming.cs old mode 100755 new mode 100644 index cb5aae3..75f9347 --- a/Luski.net/Server.Incoming.cs +++ b/Luski.net/Server.Incoming.cs @@ -1,25 +1,39 @@ -using Luski.net.Enums; +using System; +using System.Text.Json; +using System.Threading; +using JacobTechEncryption; +using Luski.net.Enums; using Luski.net.JsonTypes; using Luski.net.JsonTypes.BaseTypes; using Luski.net.JsonTypes.HTTP; using Luski.net.JsonTypes.WSS; -using System; -using System.Text.Json; +using Luski.net.Structures; +using Luski.net.Structures.Main; using WebSocketSharp; -namespace Luski.net +namespace Luski.net; + +public partial class Server { - public sealed partial class Server + private void ServerOut_OnError(object? sender, WebSocketSharp.ErrorEventArgs e) { - private void DataFromServer(object? sender, MessageEventArgs e) + if (OnError is not null) OnError.Invoke(new Exception(e.Message)); + } + + private void DataFromServer(object? sender, MessageEventArgs e) + { + if (e.IsPing) return; + try { - if (e.IsPing) return; + Console.WriteLine("From Server: {0}", e.Data); IncomingWSS? data = JsonSerializer.Deserialize(e.Data, IncomingWSSContext.Default.IncomingWSS); switch (data?.Type) { case DataType.Login: + Console.WriteLine("Pre auth"); WSSLogin n = JsonSerializer.Deserialize(e.Data, WSSLoginContext.Default.WSSLogin)!; Token = n.Token; + Console.WriteLine("Token: {0}",Token); break; case DataType.Error: if (Token is null) @@ -40,7 +54,7 @@ namespace Luski.net SocketMessage? m = JsonSerializer.Deserialize(e.Data); if (m is not null) { - m.decrypt(Encryption.File.Channels.GetKey(m.ChannelID)); + m.decrypt(ClientEncryption.File.Channels.GetKey(m.ChannelID), CancellationToken.None); _ = MessageReceived.Invoke(m); } } @@ -51,9 +65,9 @@ namespace Luski.net StatusUpdate? SU = JsonSerializer.Deserialize(e.Data); if (SU is not null) { - SocketRemoteUser after = SocketUserBase.GetUser(SU.id, SocketRemoteUserContext.Default.SocketRemoteUser).Result; + MainSocketRemoteUser after = GetUser(SU.id, MainSocketRemoteUserContext.Default.MainSocketRemoteUser, CancellationToken.None).Result; after.Status = SU.after; - SocketRemoteUser before = after.Clone(); + MainSocketRemoteUser before = after.Clone(); before.Status = SU.before; _ = UserStatusUpdate.Invoke(before, after); } @@ -62,57 +76,56 @@ namespace Luski.net case DataType.Friend_Request: if (ReceivedFriendRequest is not null) { - FriendRequest? request = JsonSerializer.Deserialize(e.Data, FriendRequestContext.Default.FriendRequest); - if (request is not null) _ = ReceivedFriendRequest.Invoke(SocketUserBase.GetUser(request.Id, SocketRemoteUserContext.Default.SocketRemoteUser).Result); + FriendRequest? request = + JsonSerializer.Deserialize(e.Data, FriendRequestContext.Default.FriendRequest); + if (request is not null) + _ = ReceivedFriendRequest.Invoke(GetUser(request.Id, + MainSocketRemoteUserContext.Default.MainSocketRemoteUser, + CancellationToken.None).Result); } break; case DataType.Friend_Request_Result: if (FriendRequestResult is not null) { FriendRequestResult? FRR = JsonSerializer.Deserialize(e.Data); - if (FRR is not null && FRR.Channel is not null && FRR.Id is not null && FRR.Result is not null) + if (FRR is not null && FRR.Channel is not null && FRR.Id is not null && + FRR.Result is not null) { - SocketDMChannel chan = SocketChannel.GetChannel((long)FRR.Channel, SocketDMChannelContext.Default.SocketDMChannel).Result; + MainSocketDMChannel chan = GetChannel((long)FRR.Channel, + MainSocketDMChannelContext.Default.MainSocketDMChannel, + CancellationToken.None).Result; chans.Add(chan); - SocketRemoteUser from1 = SocketUserBase.GetUser((long)FRR.Id, SocketRemoteUserContext.Default.SocketRemoteUser).Result; - from1.Channel = chan; + MainSocketRemoteUser from1 = GetUser((long)FRR.Id, + MainSocketRemoteUserContext.Default.MainSocketRemoteUser, + CancellationToken.None).Result; + //from1.Channel = chan; _ = FriendRequestResult.Invoke(from1, (bool)FRR.Result); } } break; - case DataType.Call_Info: - if (IncommingCall is not null) - { - callinfoinc? ci = JsonSerializer.Deserialize(e.Data); - if (ci is not null) _ = IncommingCall.Invoke(SocketChannel.GetChannel(ci.channel, SocketTextChannelContext.Default.SocketTextChannel).Result, SocketUserBase.GetUser(ci.from, SocketRemoteUserContext.Default.SocketRemoteUser).Result); - } - break; - case DataType.Call_Data: - if (AudioClient is not null) - { - AudioClient.Givedata(e.Data); - } - break; case DataType.Key_Exchange: try { KeyExchange? KE = JsonSerializer.Deserialize(e.Data); - if (KE is not null) Encryption.File.Channels.AddKey(KE.channel, Encryption.Encoder.GetString(Encryption.Decrypt(Convert.FromBase64String(KE.key)))); + if (KE is not null) + ClientEncryption.File.Channels.AddKey(KE.channel, + ClientEncryption.Encoder.GetString(Encryption.RSA.Decrypt(Convert.FromBase64String(KE.key), ClientEncryption.ofkey))); } catch (Exception ex) { if (OnError is not null) OnError.Invoke(ex); } + break; default: + Console.WriteLine("Unknown"); break; } } - - private class callinfoinc + catch (Exception exception) { - public long channel { get; set; } = default!; - public long from { get; set; } = default!; + if (OnError is not null) _ = OnError.Invoke(exception); + else throw exception; } } -} +} \ No newline at end of file diff --git a/Luski.net/Server.Login.cs b/Luski.net/Server.Login.cs deleted file mode 100755 index e0b1596..0000000 --- a/Luski.net/Server.Login.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Luski.net.Enums; -using System.Threading.Tasks; - -namespace Luski.net; - -public sealed partial class Server -{ - public static async Task Login(string Email, string Password, Branch branch = Branch.Master) - { - return new Server(Email, Password, branch); - } -} diff --git a/Luski.net/Server.cs b/Luski.net/Server.cs old mode 100755 new mode 100644 index b6cfbe9..814b71d --- a/Luski.net/Server.cs +++ b/Luski.net/Server.cs @@ -1,191 +1,79 @@ -using Luski.net.Enums; -using Luski.net.Interfaces; -using Luski.net.JsonTypes; -using Luski.net.JsonTypes.BaseTypes; -using Luski.net.JsonTypes.HTTP; -using Luski.net.Sockets; using System; using System.Collections.Generic; using System.IO; -using System.Net; +using System.Linq; using System.Net.Http; using System.Net.Http.Json; using System.Text.Json; using System.Text.Json.Serialization.Metadata; +using System.Threading; using System.Threading.Tasks; +using Luski.net.Enums; +using Luski.net.Interfaces; +using Luski.net.JsonTypes; +using Luski.net.JsonTypes.BaseTypes; +using Luski.net.JsonTypes.HTTP; +using Luski.net.Structures; +using Luski.net.Structures.Main; +using Luski.net.Structures.Public; +using File = System.IO.File; namespace Luski.net; -public sealed partial class Server +public partial class Server : IServer { -#pragma warning disable CA1822 // Mark members as static - /// - /// Creates an audio client for the you want to talk on - /// - /// The channel you want to talk on - /// - public IAudioClient CreateAudioClient(long channel_id) + internal Server() + { } + + public async Task GetAvatar(CancellationToken CancellationToken) { - // if (AudioClient != null) throw new Exception("audio client alread created"); - SocketAudioClient client = new(channel_id, OnError); - AudioClient = client; - return client; + if (Cache != null) + { + bool isc = File.Exists($"{Cache}/servers/{Domain}"); + if (!isc) await GetFromServer($"socketserver/Avatar/", $"{Cache}/servers/{Domain}-{ApiVersion}", CancellationToken); + } + return File.ReadAllBytes($"{Cache}/servers/{Domain}"); } - public async Task SendFriendResult(long user, bool answer) + public async Task GetUser(long UserID, CancellationToken CancellationToken) where Tuser : SocketUserBase, new() { - - FriendRequestResult json = await SendServer("FriendRequestResult", - new FriendRequestResultOut() - { - Id = user, - Result = answer - }, - FriendRequestResultOutContext.Default.FriendRequestResultOut, - FriendRequestResultContext.Default.FriendRequestResult); - - if (json is not null && json.Error is null && json.ErrorMessage is null && answer && json.Channel is not null) + Tuser user = new(); + switch (user) { - SocketDMChannel chan = await SocketChannel.GetChannel((long)json.Channel, SocketDMChannelContext.Default.SocketDMChannel); - _ = chan.StartKeyProcessAsync(); - chans.Add(chan); + case MainSocketAppUser: + user = (GetUser(UserID, MainSocketAppUserContext.Default.MainSocketAppUser, CancellationToken).Result as Tuser)!; + break; + case PublicSocketAppUser: + user = (GetUser(UserID, PublicSocketAppUserContext.Default.PublicSocketAppUser, CancellationToken).Result as Tuser)!; + break; + case SocketUserBase: + user = (GetUser(UserID, SocketUserBaseContext.Default.SocketUserBase, CancellationToken).Result as Tuser)!; + break; + case null: + throw new NullReferenceException(nameof(Tuser)); + default: + throw new Exception("Unknown channel type"); } - else - { - throw new Exception(json?.Error.ToString()); - } - return SocketUserBase.GetUser(user, SocketRemoteUserContext.Default.SocketRemoteUser).Result; + + return user; } - - public async Task SendFriendRequest(long user) - { - FriendRequestResult? json = await SendServer("FriendRequest", new FriendRequest() { Id = user, SubType = 0 }, FriendRequestContext.Default.FriendRequest, FriendRequestResultContext.Default.FriendRequestResult); - - if (json.StatusCode != HttpStatusCode.Accepted) - { - if (json is not null && json.Error is not null) - { - switch ((ErrorCode)(int)json.Error) - { - case ErrorCode.InvalidToken: - throw new Exception("Your current token is no longer valid"); - case ErrorCode.ServerError: - throw new Exception($"Error from server: {json.ErrorMessage}"); - case ErrorCode.InvalidPostData: - throw new Exception("The post data dent to the server is not the correct format. This may be because you app is couropt or you are using the wron API version"); - case ErrorCode.Forbidden: - throw new Exception("You already have an outgoing request or the persone is not real"); - } - } - - if (json is not null && json.Channel is not null) - { - SocketDMChannel chan = await SocketChannel.GetChannel((long)json.Channel, (JsonTypeInfo)SocketDMChannelContext.Default.SocketDMChannel); - _ = chan.StartKeyProcessAsync(); - chans.Add(chan); - } - } - - SocketRemoteUser b = await SocketUserBase.GetUser(user, SocketRemoteUserContext.Default.SocketRemoteUser); - b.FriendStatus = FriendStatus.PendingOut; - return b; - } - - public async Task SendFriendRequest(string username, short tag) - { - FriendRequestResult json = await SendServer("FriendRequest", new FriendRequest() { Username = username, Tag = tag, SubType = 1 }, FriendRequestContext.Default.FriendRequest, FriendRequestResultContext.Default.FriendRequestResult); - - if (json is not null && json.Error is not null) - { - throw (ErrorCode)(int)json.Error switch - { - ErrorCode.InvalidToken => new Exception("Your current token is no longer valid"), - ErrorCode.ServerError => new Exception("Error from server: " + json.ErrorMessage), - ErrorCode.InvalidPostData => new Exception("The post data dent to the server is not the correct format. This may be because you app is couropt or you are using the wron API version"), - ErrorCode.Forbidden => new Exception("You already have an outgoing request or the persone is not real"), - _ => new Exception(JsonSerializer.Serialize(json)), - }; - } - else if (json is not null && json.Channel is not null && json.Id is not null) - { - SocketDMChannel chan = await SocketChannel.GetChannel(json.Channel.Value, (JsonTypeInfo)SocketDMChannelContext.Default.SocketDMChannel); - _ = chan.StartKeyProcessAsync(); - chans.Add(chan); - return await SocketUserBase.GetUser((long)json.Id, SocketRemoteUserContext.Default.SocketRemoteUser); - } - else throw new Exception("missing data from server"); - } - - /// - /// Sends the server a request to update the of you account - /// - /// The you want to set your status to - /// - public async Task UpdateStatus(UserStatus Status) - { - if (_user is null) throw new Exception("You must login to make a request"); - IncomingHTTP? data = await SendServer("SocketUserProfile/Status", new Status() { UserStatus = Status }, StatusContext.Default.Status, IncomingHTTPContext.Default.IncomingHTTP); - if (data.Error is not null && ((int)data.StatusCode < 200 || (int)data.StatusCode > 299)) - { - if (data?.ErrorMessage is not null) throw new Exception(data.ErrorMessage); - if (data?.Error is not null) throw new Exception(((int)data.Error).ToString()); - else throw new Exception("Something went worng"); - } - - _user.Status = Status; - return Task.CompletedTask; - } - - public async Task ChangeChannel(long Channel) - { - if (_user is null) throw new Exception("You must login to make a request"); - IncomingHTTP? data = await SendServer("ChangeChannel", new Channel() { Id = Channel }, ChannelContext.Default.Channel, IncomingHTTPContext.Default.IncomingHTTP); - if (data.StatusCode != HttpStatusCode.Accepted) - { - if (data?.Error is not null) - { - switch (data.Error) - { - case ErrorCode.InvalidToken: - throw new Exception("Your current token is no longer valid"); - case ErrorCode.ServerError: - throw new Exception("Error from server: " + data.ErrorMessage); - } - } - else throw new Exception("Something went worng"); - } - - _user.SelectedChannel = Channel; - } - - public async Task SendMessage(string Message, long Channel, params JsonTypes.File[] Files) => (await GetChannel(Channel)).SendMessage(Message, Files); - - public void SetMultiThreadPercent(double num) - { - if (num < 1 || num > 100) throw new Exception("Number must be from 1 - 100"); - Percent = num / 100; - } - - public async Task GetMessage(long MessageId) => await SocketMessage.GetMessage(MessageId); - - public async Task GetUser(long UserID) => await SocketUserBase.GetUser(UserID, SocketRemoteUserContext.Default.SocketRemoteUser); - - public async Task GetChannel(long Channel) where TChannel : SocketChannel, new() + + public async Task GetChannel(long Channel, CancellationToken CancellationToken) where TChannel : MainSocketChannel, new() { TChannel Return = new(); switch (Return) { - case SocketDMChannel: - Return = (await SocketChannel.GetChannel(Channel, SocketDMChannelContext.Default.SocketDMChannel) as TChannel)!; + case MainSocketDMChannel: + Return = (await GetChannel(Channel, MainSocketDMChannelContext.Default.MainSocketDMChannel, CancellationToken) as TChannel)!; break; - case SocketGroupChannel: - Return = (await SocketChannel.GetChannel(Channel, SocketGroupChannelContext.Default.SocketGroupChannel) as TChannel)!; + case MainSocketGroupChannel: + Return = (await GetChannel(Channel, MainSocketGroupChannelContext.Default.MainSocketGroupChannel, CancellationToken) as TChannel)!; break; - case SocketTextChannel: - Return = (await SocketChannel.GetChannel(Channel, SocketTextChannelContext.Default.SocketTextChannel) as TChannel)!; + case MainSocketTextChannel: + Return = (await GetChannel(Channel, MainSocketTextChannelContext.Default.MainSocketTextChannel, CancellationToken) as TChannel)!; break; - case SocketChannel: - Return = (await SocketChannel.GetChannel(Channel, SocketChannelContext.Default.SocketChannel) as TChannel)!; + case MainSocketChannel: + Return = (await GetChannel(Channel, MainSocketChannelContext.Default.MainSocketChannel, CancellationToken) as TChannel)!; break; case null: throw new NullReferenceException(nameof(TChannel)); @@ -194,84 +82,169 @@ public sealed partial class Server } return Return; } - - - public SocketAppUser CurrentUser + + internal async Task GetChannel(long id, JsonTypeInfo Json, CancellationToken CancellationToken) where TChannel : MainSocketChannel, new() { - get + TChannel request; + if (chans.Count > 0 && chans.Any(s => s.Id == id)) { - if (_user is null) throw new Exception("You must Login first"); - return _user; + return chans.Where(s => s is TChannel && s.Id == id).Cast().FirstOrDefault()!; } + while (true) + { + if (CanRequest) + { + request = await GetFromServer($"SocketChannel/Get/{id}", Json, CancellationToken); + break; + } + } + if (request is null) throw new Exception("Something was wrong with the server responce"); + if (request.Error is null) + { + if (chans.Count > 0 && chans.Any(s => s.Id == request.Id)) + { + foreach (MainSocketChannel? p in chans.Where(s => s.Id == request.Id)) + { + chans.Remove(p); + } + } + chans.Add(request); + return request; + } + throw request.Error switch + { + ErrorCode.InvalidToken => new Exception("Your current token is no longer valid"), + ErrorCode.Forbidden => new Exception("The server rejected your request"), + ErrorCode.ServerError => new Exception("Error from server: " + request.ErrorMessage), + ErrorCode.InvalidURL or ErrorCode.MissingHeader => new Exception(request.ErrorMessage), + _ => new Exception($"Unknown data: '{request.ErrorMessage}'"), + }; } -#pragma warning restore CA1822 // Mark members as static - - private void ServerOut_OnError(object? sender, WebSocketSharp.ErrorEventArgs e) + + public async Task GetMessage(long id, CancellationToken CancellationToken) { - if (OnError is not null) OnError.Invoke(new Exception(e.Message)); + SocketMessage message; + while (true) + { + if (CanRequest) + { + message = await GetFromServer("socketmessage", + SocketMessageContext.Default.SocketMessage, + CancellationToken, + new System.Collections.Generic.KeyValuePair("msg_id", id.ToString())); + break; + } + } + if (message is not null) return message; + throw new Exception("Server did not return a message"); } - - [Obsolete("Move to new Data layout")] - internal static void SendServer(string data) + + /// + /// Sends the server a request to update the of you account + /// + /// The you want to set your status to + /// + public async Task UpdateStatus(UserStatus Status, CancellationToken CancellationToken) { - ServerOut?.Send(data); + IncomingHTTP? data = await SendServer("SocketUserProfile/Status", new Status() { UserStatus = Status }, StatusContext.Default.Status, IncomingHTTPContext.Default.IncomingHTTP, CancellationToken); + if (data.Error is not null && ((int)data.StatusCode < 200 || (int)data.StatusCode > 299)) + { + if (data?.ErrorMessage is not null) throw new Exception(data.ErrorMessage); + if (data?.Error is not null) throw new Exception(((int)data.Error).ToString()); + else throw new Exception("Something went worng"); + } + + (User as SocketUserBase)!.Status = Status; + return Task.CompletedTask; + } + + internal async Task GetUser(long UserId, JsonTypeInfo Json, CancellationToken CancellationToken) where Tuser : SocketUserBase, new() + { + Tuser user; + if (poeople.Count > 0 && poeople.Any(s => s.Id == UserId)) + { + Tuser temp = poeople.Where(s => s is Tuser && s.Id == UserId).Cast().FirstOrDefault()!; + return temp; + } + while (true) + { + if (CanRequest) + { + user = await GetFromServer("socketuser", + Json, + CancellationToken, + new KeyValuePair("id", UserId.ToString())); + break; + } + } + + if (user is null) throw new Exception("Server did not return a user"); + if (poeople.Count > 0 && poeople.Any(s => s.Id == UserId)) + { + foreach (IUser? p in poeople.Where(s => s.Id == UserId)) + { + poeople.Remove(p); + } + } + poeople.Add(user); + return user; } - internal static void SendServer(Tvalue Payload, JsonTypeInfo jsonTypeInfo) where Tvalue : IncomingWSS + public void SendServer(Tvalue Payload, JsonTypeInfo jsonTypeInfo) where Tvalue : IncomingWSS { ServerOut?.Send(JsonSerializer.Serialize(Payload, jsonTypeInfo)); } - internal static HttpResponseMessage GetFromServer(string Path, params KeyValuePair[] Headers) + public HttpResponseMessage GetFromServer(string Path, CancellationToken CancellationToken, params KeyValuePair[] Headers) { using HttpClient web = new(); web.Timeout = TimeSpan.FromSeconds(10); if (!login) web.DefaultRequestHeaders.Add("token", Token); if (Headers is not null && Headers.Length > 0) foreach (KeyValuePair header in Headers) web.DefaultRequestHeaders.Add(header.Key, header.Value); - return web.GetAsync($"https://{InternalDomain}/{API_Ver}/{Path}").Result; + return web.GetAsync($"https://{Domain}/{ApiVersion}/{Path}", cancellationToken: CancellationToken).Result; } - internal static Task GetFromServer(string Path, string File, params KeyValuePair[] Headers) + public Task GetFromServer(string Path, string File, CancellationToken CancellationToken, params KeyValuePair[] Headers) { using HttpClient web = new(); web.Timeout = TimeSpan.FromMinutes(10); if (!login) web.DefaultRequestHeaders.Add("token", Token); if (Headers is not null && Headers.Length > 0) foreach (KeyValuePair header in Headers) web.DefaultRequestHeaders.Add(header.Key, header.Value); - HttpResponseMessage Response = web.GetAsync($"https://{InternalDomain}/{API_Ver}/{Path}").Result; - Stream stream = Response.Content.ReadAsStreamAsync().Result; + HttpResponseMessage Response = web.GetAsync($"https://{Domain}/{ApiVersion}/{Path}", CancellationToken).Result; + Stream stream = Response.Content.ReadAsStreamAsync(CancellationToken).Result; using FileStream fs = System.IO.File.Create(File); stream.CopyTo(fs); return Task.CompletedTask; } - internal static async Task GetFromServer(string Path, JsonTypeInfo Type, params KeyValuePair[] Headers) where Tresult : IncomingHTTP, new() + public async Task GetFromServer(string Path, JsonTypeInfo Type, CancellationToken CancellationToken, params KeyValuePair[] Headers) where Tresult : IncomingHTTP, new() { - HttpResponseMessage ServerResponce = GetFromServer(Path, Headers); + HttpResponseMessage ServerResponce = GetFromServer(Path, CancellationToken, Headers); if (!ServerResponce.IsSuccessStatusCode) return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with status code {(int)ServerResponce.StatusCode}:{ServerResponce.StatusCode}" }; - Tresult? temp = JsonSerializer.Deserialize(ServerResponce.Content.ReadAsStreamAsync().Result, Type); + Tresult? temp = JsonSerializer.Deserialize(ServerResponce.Content.ReadAsStreamAsync(CancellationToken).Result, Type); if (temp is null) return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with empty data" }; return temp; } - internal static async Task SendServer(string Path, Tvalue Payload, JsonTypeInfo jsonTypeInfo, JsonTypeInfo ReturnjsonTypeInfo, params KeyValuePair[] Headers) where Tvalue : HTTPRequest where Tresult : IncomingHTTP, new() + public async Task SendServer(string Path, Tvalue Payload, JsonTypeInfo jsonTypeInfo, JsonTypeInfo ReturnjsonTypeInfo, CancellationToken CancellationToken, params KeyValuePair[] Headers) where Tvalue : IWebRequest where Tresult : IncomingHTTP, new() { using HttpClient web = new(); if (!login) web.DefaultRequestHeaders.Add("token", Token); if (Headers is not null && Headers.Length > 0) foreach (KeyValuePair header in Headers) web.DefaultRequestHeaders.Add(header.Key, header.Value); - HttpResponseMessage ServerResponce = web.PostAsJsonAsync($"https://{InternalDomain}/{API_Ver}/{Path}", Payload, jsonTypeInfo).Result; + HttpResponseMessage ServerResponce = web.PostAsJsonAsync($"https://{Domain}/{ApiVersion}/{Path}", Payload, jsonTypeInfo, CancellationToken).Result; if (!ServerResponce.IsSuccessStatusCode) return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with status code {(int)ServerResponce.StatusCode}:{ServerResponce.StatusCode}" }; Tresult error = new() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with empty data" }; - if (string.IsNullOrWhiteSpace(ServerResponce.Content.ReadAsStringAsync().Result)) return error; + if (string.IsNullOrWhiteSpace(ServerResponce.Content.ReadAsStringAsync(CancellationToken).Result)) return error; try { - Tresult? temp = JsonSerializer.Deserialize(ServerResponce.Content.ReadAsStreamAsync().Result, ReturnjsonTypeInfo); + Tresult? temp = JsonSerializer.Deserialize(ServerResponce.Content.ReadAsStreamAsync(CancellationToken).Result, ReturnjsonTypeInfo); if (temp is null) return error; return temp; } catch { return error; } } - internal static async Task SendServer(string Path, string File, JsonTypeInfo ReturnjsonTypeInfo, params KeyValuePair[] Headers) where Tresult : IncomingHTTP, new() + public async Task SendServer(string Path, string File, JsonTypeInfo ReturnjsonTypeInfo, CancellationToken CancellationToken, params KeyValuePair[] Headers) where Tresult : IncomingHTTP, new() { var fs = System.IO.File.OpenRead(File); try @@ -280,11 +253,11 @@ public sealed partial class Server if (!login) web.DefaultRequestHeaders.Add("token", Token); web.Timeout = new TimeSpan(0, 10, 0); if (Headers is not null && Headers.Length > 0) foreach (KeyValuePair header in Headers) web.DefaultRequestHeaders.Add(header.Key, header.Value); - HttpResponseMessage ServerResponce = web.PostAsync($"https://{InternalDomain}/{API_Ver}/{Path}", new StreamContent(fs)).Result; + HttpResponseMessage ServerResponce = web.PostAsync($"https://{Domain}/{ApiVersion}/{Path}", new StreamContent(fs), CancellationToken).Result; if (!ServerResponce.IsSuccessStatusCode) return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with status code {(int)ServerResponce.StatusCode}:{ServerResponce.StatusCode}" }; try { - Tresult? temp = JsonSerializer.Deserialize(ServerResponce.Content.ReadAsStreamAsync().Result, ReturnjsonTypeInfo); + Tresult? temp = JsonSerializer.Deserialize(ServerResponce.Content.ReadAsStreamAsync(CancellationToken).Result, ReturnjsonTypeInfo); if (temp is null) return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with empty data" }; return temp; } @@ -295,4 +268,4 @@ public sealed partial class Server fs.Close(); } } -} +} \ No newline at end of file diff --git a/Luski.net/Server.old.Globals.cs b/Luski.net/Server.old.Globals.cs new file mode 100755 index 0000000..5d0d5a6 --- /dev/null +++ b/Luski.net/Server.old.Globals.cs @@ -0,0 +1,97 @@ +/*using Luski.net.Enums; +using Luski.net.Interfaces; +using Luski.net.JsonTypes; +using Luski.net.Sockets; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Xml; +using WebSocketSharp; + +namespace Luski.net; + +public sealed partial class Serverold +{ + 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; + } + } + internal static SocketAudioClient? AudioClient = null; + internal static string? Token = null, Error = null; + internal static bool CanRequest = false; + internal static SocketAppUser? _user; + internal static string platform = "win-x64"; + internal static Branch Branch; + internal static double Percent = 0.5; + private static WebSocket? ServerOut; + private static string? gen = null; + private static bool login = false; + + public static SocketServer CurrentServer = null!; + + internal static string Cache + { + get + { + if (gen is null) + { + if (!Directory.Exists(JT)) Directory.CreateDirectory(JT); + string path = JT + "/Luski/"; + if (!Directory.Exists(path)) Directory.CreateDirectory(path); + path += Branch.ToString() + "/"; + if (!Directory.Exists(path)) Directory.CreateDirectory(path); + path += platform + "/"; + if (!Directory.Exists(path)) Directory.CreateDirectory(path); + path += "Data/"; + if (!Directory.Exists(path)) Directory.CreateDirectory(path); + path += _user?.Id + "/"; + if (!Directory.Exists(path)) Directory.CreateDirectory(path); + path += "Cache/"; + if (!Directory.Exists(path)) Directory.CreateDirectory(path); + path += Path.GetRandomFileName() + "/"; + if (!Directory.Exists(path)) Directory.CreateDirectory(path); + gen = path; + } + if (!Directory.Exists($"{gen}/avatars")) Directory.CreateDirectory($"{gen}/avatars"); + if (!Directory.Exists($"{gen}/channels")) Directory.CreateDirectory($"{gen}/channels"); + return gen; + } + } + internal static List poeople = new(); + internal static List chans { get; set; } = new(); + internal static string GetKeyFilePath + { + get + { + return GetKeyFilePathBr(Branch.ToString()); + } + } + + internal static string GetKeyFilePathBr(string br) + { + if (!Directory.Exists(JT)) Directory.CreateDirectory(JT); + string path = JT + "/Luski/"; + if (!Directory.Exists(path)) Directory.CreateDirectory(path); + path += br + "/"; + if (!Directory.Exists(path)) Directory.CreateDirectory(path); + path += platform + "/"; + if (!Directory.Exists(path)) Directory.CreateDirectory(path); + path += "Data/"; + if (!Directory.Exists(path)) Directory.CreateDirectory(path); + path += _user?.Id + "/"; + if (!Directory.Exists(path)) Directory.CreateDirectory(path); + path += "keys.lsk"; + return path; + } +} +*/ \ No newline at end of file diff --git a/Luski.net/Server.old.cs b/Luski.net/Server.old.cs new file mode 100755 index 0000000..025c3ea --- /dev/null +++ b/Luski.net/Server.old.cs @@ -0,0 +1,281 @@ +/*using Luski.net.Enums; +using Luski.net.Interfaces; +using Luski.net.JsonTypes; +using Luski.net.JsonTypes.BaseTypes; +using Luski.net.JsonTypes.HTTP; +using Luski.net.Sockets; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.Http.Json; +using System.Text.Json; +using System.Text.Json.Serialization.Metadata; +using System.Threading; +using System.Threading.Tasks; + +namespace Luski.net; + +public sealed partial class Serverold +{ +#pragma warning disable CA1822 // Mark members as static + public async Task SendFriendResult(long user, bool answer, CancellationToken CancellationToken) + { + + FriendRequestResult json = await SendServer(_user!.Servers.First(),"FriendRequestResult", + new FriendRequestResultOut() + { + Id = user, + Result = answer + }, + FriendRequestResultOutContext.Default.FriendRequestResultOut, + FriendRequestResultContext.Default.FriendRequestResult, + CancellationToken); + + if (json is not null && json.Error is null && json.ErrorMessage is null && answer && json.Channel is not null) + { + SocketDMChannel chan = await SocketChannel.GetChannel((long)json.Channel, SocketDMChannelContext.Default.SocketDMChannel, CancellationToken, _user!.Servers.First()); + _ = chan.StartKeyProcessAsync(CancellationToken); + chans.Add(chan); + } + else + { + throw new Exception(json?.Error.ToString()); + } + return SocketUserBase.GetUser(user, SocketRemoteUserContext.Default.SocketRemoteUser, CancellationToken, _user!.Servers.First()).Result; + } + + public async Task SendFriendRequest(long code, CancellationToken CancellationToken) + { + string ccode = Convert.ToBase64String(Encryption.Hash(Encryption.Encoder.GetBytes(code.ToString()))); + FriendRequestResult? json = await SendServer(_user!.Servers.First(),"FriendRequest", new FriendRequest() { code = ccode}, FriendRequestContext.Default.FriendRequest, FriendRequestResultContext.Default.FriendRequestResult, CancellationToken); + + if (json.StatusCode != HttpStatusCode.Accepted) + { + if (json is not null && json.Error is not null) + { + switch ((ErrorCode)(int)json.Error) + { + case ErrorCode.InvalidToken: + throw new Exception("Your current token is no longer valid"); + case ErrorCode.ServerError: + throw new Exception($"Error from server: {json.ErrorMessage}"); + case ErrorCode.InvalidPostData: + throw new Exception("The post data dent to the server is not the correct format. This may be because you app is couropt or you are using the wron API version"); + case ErrorCode.Forbidden: + throw new Exception("You already have an outgoing request or the persone is not real"); + } + } + + if (json is not null && json.Channel is not null) + { + SocketDMChannel chan = await SocketChannel.GetChannel((long)json.Channel, (JsonTypeInfo)SocketDMChannelContext.Default.SocketDMChannel, CancellationToken, _user!.Servers.First()); + _ = chan.StartKeyProcessAsync(CancellationToken); + chans.Add(chan); + } + } + + MainSocketRemoteUser b = await SocketUserBase.GetUser(code, SocketRemoteUserContext.Default.SocketRemoteUser, CancellationToken, _user!.Servers.First()); + if (json.Channel is not null) + b.FriendStatus = FriendStatus.Friends; + else + b.FriendStatus = FriendStatus.PendingOut; + return b; + } + + /// + /// Sends the server a request to update the of you account + /// + /// The you want to set your status to + /// + public async Task UpdateStatus(UserStatus Status, CancellationToken CancellationToken) + { + if (_user is null) throw new Exception("You must login to make a request"); + IncomingHTTP? data = await SendServer(_user!.Servers.First(),"SocketUserProfile/Status", new Status() { UserStatus = Status }, StatusContext.Default.Status, IncomingHTTPContext.Default.IncomingHTTP, CancellationToken); + if (data.Error is not null && ((int)data.StatusCode < 200 || (int)data.StatusCode > 299)) + { + if (data?.ErrorMessage is not null) throw new Exception(data.ErrorMessage); + if (data?.Error is not null) throw new Exception(((int)data.Error).ToString()); + else throw new Exception("Something went worng"); + } + + _user.Status = Status; + return Task.CompletedTask; + } + + public async Task ChangeChannel(long Channel, CancellationToken CancellationToken, SocketServer Server) + { + if (_user is null) throw new Exception("You must login to make a request"); + IncomingHTTP? data = await SendServer(Server, "ChangeChannel", new Channel() { Id = Channel }, ChannelContext.Default.Channel, IncomingHTTPContext.Default.IncomingHTTP, CancellationToken); + if (data.StatusCode != HttpStatusCode.Accepted) + { + if (data?.Error is not null) + { + switch (data.Error) + { + case ErrorCode.InvalidToken: + throw new Exception("Your current token is no longer valid"); + case ErrorCode.ServerError: + throw new Exception("Error from server: " + data.ErrorMessage); + } + } + else throw new Exception("Something went worng"); + } + + _user.SelectedChannel = Channel; + } + + public Task SendMessage(string Message, long Channel, SocketServer Server, CancellationToken CancellationToken, params JsonTypes.File[] Files) + { + try + { + _ = GetChannel(Channel, CancellationToken, Server).Result.SendMessage(Message, CancellationToken, Files); + } + catch (Exception e) + { + if (OnError is not null) _ = OnError.Invoke(e); + else throw e; + } + + return Task.CompletedTask; + } + + public void SetMultiThreadPercent(double num) + { + if (num < 1 || num > 100) throw new Exception("Number must be from 1 - 100"); + Percent = num / 100; + } + + public async Task GetMessage(long MessageId, CancellationToken CancellationToken, SocketServer Server) => await SocketMessage.GetMessage(MessageId, CancellationToken, Server); + + public async Task GetUser(long UserID, CancellationToken CancellationToken, SocketServer Server) => await SocketUserBase.GetUser(UserID, SocketRemoteUserContext.Default.SocketRemoteUser, CancellationToken, Server); + + public async Task GetChannel(long Channel, CancellationToken CancellationToken, SocketServer Server) where TChannel : SocketChannel, new() + { + TChannel Return = new(); + switch (Return) + { + case SocketDMChannel: + Return = (await SocketChannel.GetChannel(Channel, SocketDMChannelContext.Default.SocketDMChannel, CancellationToken, Server) as TChannel)!; + break; + case SocketGroupChannel: + Return = (await SocketChannel.GetChannel(Channel, SocketGroupChannelContext.Default.SocketGroupChannel, CancellationToken, Server) as TChannel)!; + break; + case SocketTextChannel: + Return = (await SocketChannel.GetChannel(Channel, SocketTextChannelContext.Default.SocketTextChannel, CancellationToken, Server) as TChannel)!; + break; + case SocketChannel: + Return = (await SocketChannel.GetChannel(Channel, SocketChannelContext.Default.SocketChannel, CancellationToken, Server) as TChannel)!; + break; + case null: + throw new NullReferenceException(nameof(TChannel)); + default: + throw new Exception("Unknown channel type"); + } + return Return; + } + + + public SocketAppUser CurrentUser + { + get + { + if (_user is null) throw new Exception("You must Login first"); + return _user; + } + } +#pragma warning restore CA1822 // Mark members as static + + private void ServerOut_OnError(object? sender, WebSocketSharp.ErrorEventArgs e) + { + if (OnError is not null) OnError.Invoke(new Exception(e.Message)); + } + + [Obsolete("Move to new Data layout")] + internal static void SendServer(string data) + { + ServerOut?.Send(data); + } + + internal static void SendServer(Tvalue Payload, JsonTypeInfo jsonTypeInfo) where Tvalue : IncomingWSS + { + ServerOut?.Send(JsonSerializer.Serialize(Payload, jsonTypeInfo)); + } + + internal static HttpResponseMessage GetFromServer(SocketServer Server, string Path, CancellationToken CancellationToken, params KeyValuePair[] Headers) + { + using HttpClient web = new(); + web.Timeout = TimeSpan.FromSeconds(10); + if (!login) web.DefaultRequestHeaders.Add("token", Token); + if (Headers is not null && Headers.Length > 0) foreach (KeyValuePair header in Headers) web.DefaultRequestHeaders.Add(header.Key, header.Value); + return web.GetAsync($"https://{Server.Domain}/{Server.ApiVersion}/{Path}", cancellationToken: CancellationToken).Result; + } + + internal static Task GetFromServer(SocketServer Server, string Path, string File, CancellationToken CancellationToken, params KeyValuePair[] Headers) + { + using HttpClient web = new(); + web.Timeout = TimeSpan.FromMinutes(10); + if (!login) web.DefaultRequestHeaders.Add("token", Token); + if (Headers is not null && Headers.Length > 0) foreach (KeyValuePair header in Headers) web.DefaultRequestHeaders.Add(header.Key, header.Value); + HttpResponseMessage Response = web.GetAsync($"https://{Server.Domain}/{Server.ApiVersion}/{Path}", CancellationToken).Result; + Stream stream = Response.Content.ReadAsStreamAsync(CancellationToken).Result; + using FileStream fs = System.IO.File.Create(File); + stream.CopyTo(fs); + return Task.CompletedTask; + } + + internal static async Task GetFromServer(SocketServer Server, string Path, JsonTypeInfo Type, CancellationToken CancellationToken, params KeyValuePair[] Headers) where Tresult : IncomingHTTP, new() + { + HttpResponseMessage ServerResponce = GetFromServer(Server, Path, CancellationToken, Headers); + if (!ServerResponce.IsSuccessStatusCode) return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with status code {(int)ServerResponce.StatusCode}:{ServerResponce.StatusCode}" }; + Tresult? temp = JsonSerializer.Deserialize(ServerResponce.Content.ReadAsStreamAsync(CancellationToken).Result, Type); + if (temp is null) return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with empty data" }; + return temp; + } + + internal static async Task SendServer(SocketServer Server, string Path, Tvalue Payload, JsonTypeInfo jsonTypeInfo, JsonTypeInfo ReturnjsonTypeInfo, CancellationToken CancellationToken, params KeyValuePair[] Headers) where Tvalue : IWebRequest where Tresult : IncomingHTTP, new() + { + using HttpClient web = new(); + if (!login) web.DefaultRequestHeaders.Add("token", Token); + if (Headers is not null && Headers.Length > 0) foreach (KeyValuePair header in Headers) web.DefaultRequestHeaders.Add(header.Key, header.Value); + HttpResponseMessage ServerResponce = web.PostAsJsonAsync($"https://{Server.Domain}/{Server.ApiVersion}/{Path}", Payload, jsonTypeInfo, CancellationToken).Result; + if (!ServerResponce.IsSuccessStatusCode) return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with status code {(int)ServerResponce.StatusCode}:{ServerResponce.StatusCode}" }; + Tresult error = new() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with empty data" }; + if (string.IsNullOrWhiteSpace(ServerResponce.Content.ReadAsStringAsync(CancellationToken).Result)) return error; + try + { + Tresult? temp = JsonSerializer.Deserialize(ServerResponce.Content.ReadAsStreamAsync(CancellationToken).Result, ReturnjsonTypeInfo); + if (temp is null) return error; + return temp; + } + catch { return error; } + } + + internal static async Task SendServer(SocketServer Server, string Path, string File, JsonTypeInfo ReturnjsonTypeInfo, CancellationToken CancellationToken, params KeyValuePair[] Headers) where Tresult : IncomingHTTP, new() + { + var fs = System.IO.File.OpenRead(File); + try + { + using HttpClient web = new(); + if (!login) web.DefaultRequestHeaders.Add("token", Token); + web.Timeout = new TimeSpan(0, 10, 0); + if (Headers is not null && Headers.Length > 0) foreach (KeyValuePair header in Headers) web.DefaultRequestHeaders.Add(header.Key, header.Value); + HttpResponseMessage ServerResponce = web.PostAsync($"https://{Server.Domain}/{Server.ApiVersion}/{Path}", new StreamContent(fs), CancellationToken).Result; + if (!ServerResponce.IsSuccessStatusCode) return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with status code {(int)ServerResponce.StatusCode}:{ServerResponce.StatusCode}" }; + try + { + Tresult? temp = JsonSerializer.Deserialize(ServerResponce.Content.ReadAsStreamAsync(CancellationToken).Result, ReturnjsonTypeInfo); + if (temp is null) return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with empty data" }; + return temp; + } + catch { return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with empty data" }; } + } + finally + { + fs.Close(); + } + } +} +*/ \ No newline at end of file diff --git a/Luski.net/Sockets/SocketAudioClient.cs b/Luski.net/Sockets/SocketAudioClient.cs deleted file mode 100755 index 7388aaa..0000000 --- a/Luski.net/Sockets/SocketAudioClient.cs +++ /dev/null @@ -1,388 +0,0 @@ -using Luski.net.Enums; -using Luski.net.Interfaces; -using Luski.net.JsonTypes.BaseTypes; -using Luski.net.Sound; -using System; -using System.Net.Http; -using System.Text; -using System.Text.Json; -using System.Threading.Tasks; -using static Luski.net.Exceptions; - -namespace Luski.net.Sockets -{ - internal class SocketAudioClient : IAudioClient - { - internal SocketAudioClient(long Channel, Func? error) - { - this.Channel = Channel; - errorin = error; - Muted = false; - PrototolClient.DataComplete += new Protocol.DelegateDataComplete(OnProtocolClient_DataComplete); - DataRecived += SocketAudioClient_DataRecived; - } - - public event Func? Connected; - - public bool Muted { get; private set; } - - public bool Deafened { get; private set; } - - public void ToggleMic() - { - if (Muted == true) - { - Muted = false; - } - else - { - Muted = true; - } - } - - public void ToggleAudio() - { - if (Deafened == true) - { - Deafened = false; - } - else - { - Deafened = true; - } - } - - public void RecordSoundFrom(RecordingDevice Device) - { - if (Connectedb) - { - StartRecordingFromSounddevice_Client(Device); - } - else - { - throw new NotConnectedException(this, "The call has not been connected yet!"); - } - } - - public void PlaySoundTo(PlaybackDevice Device) - { - if (Connectedb) - { - StartPlayingToSounddevice_Client(Device); - } - else - { - throw new NotConnectedException(this, "The call has not been connected yet!"); - } - } - - public void JoinCall() - { - if (Connected == null) - { - throw new MissingEventException("Connected"); - } - else - { - //get info - string data; - while (true) - { - if (Server.CanRequest) - { - using HttpClient web = new(); - web.DefaultRequestHeaders.Add("token", Server.Token); - web.DefaultRequestHeaders.Add("id", Channel.ToString()); - data = web.GetAsync($"https://{Server.InternalDomain}/{Server.API_Ver}/GetCallInfo").Result.Content.ReadAsStringAsync().Result; - break; - } - } - call? json = JsonSerializer.Deserialize(data); - Server.SendServer(JsonRequest.Send(DataType.Join_Call, JsonRequest.JoinCall(Channel)).ToString()); - Samples = json.samples; - } - } - - private class call : IncomingHTTP - { - public int samples { get; set; } = default!; - public string[] members { get; set; } = default!; - } - - public void LeaveCall() - { - Server.SendServer(JsonRequest.Send(DataType.Leave_Call, JsonRequest.JoinCall(Channel)).ToString()); - StopRecordingFromSounddevice_Client(); - } - - private readonly Protocol PrototolClient = new(ProtocolTypes.LH, Encoding.Default); - private JitterBuffer RecordingJitterBuffer = new(null, JitterBuffer, 20); - private JitterBuffer PlayingJitterBuffer = new(null, JitterBuffer, 20); - private readonly Func? errorin; - private event Func DataRecived; - private static readonly uint JitterBuffer = 5; - private readonly int BitsPerSample = 16; - private long SequenceNumber = 4596; - private readonly int Channels = 1; - private Recorder? RecorderClient; - private bool Connectedb = false; - private bool recording = false; - private long m_TimeStamp = 0; - private Player? PlayerClient; - private readonly long Channel; - - private void StopPlayingToSounddevice_Client() - { - if (PlayerClient != null) - { - PlayerClient.Close(); - PlayerClient = null; - } - - if (PlayingJitterBuffer != null) - { - PlayingJitterBuffer.Stop(); - } - } - - private async Task SocketAudioClient_DataRecived(string arg) - { - cdata d = JsonSerializer.Deserialize(arg); - byte[] data = Convert.FromBase64String(d.data); - PrototolClient.Receive_LH(this, data); - } - - private class cdata - { - public string data { get; set; } = default!; - public long from { get; set; } = default!; - } - - private void SendData(byte[] data) - { - if (!Connectedb) - { - return; - } - - Server.SendServer(JsonRequest.Send(DataType.Call_Data, JsonRequest.SendCallData(PrototolClient.ToBytes(data), Channel))); - } - - internal void Givedata(dynamic data) - { - DataRecived.Invoke(((object)data).ToString()); - } - - private int _samp; - - internal int Samples - { - get => _samp; - set - { - _samp = value; - Connectedb = true; - if (Connected is not null) Connected.Invoke(); - PlaySoundTo(Devices.GetDefaltPlaybackDevice()); - RecordSoundFrom(Devices.GetDefaltRecordingDevice()); - } - } - - private bool playing = false; - - private void StartPlayingToSounddevice_Client(PlaybackDevice device) - { - if (playing) - { - StopPlayingToSounddevice_Client(); - } - playing = true; - if (PlayingJitterBuffer != null) - { - PlayingJitterBuffer.DataAvailable -= new JitterBuffer.DelegateDataAvailable(OnJitterBufferClientDataAvailablePlaying); - - PlayingJitterBuffer = new JitterBuffer(null, JitterBuffer, 20); - PlayingJitterBuffer.DataAvailable += new JitterBuffer.DelegateDataAvailable(OnJitterBufferClientDataAvailablePlaying); - PlayingJitterBuffer.Start(); - } - - if (PlayerClient == null) - { - PlayerClient = new Player(); - PlayerClient.Open(device.Name, Samples, BitsPerSample, Channels, (int)JitterBuffer); - } - } - - private void OnJitterBufferClientDataAvailablePlaying(object sender, RTPPacket rtp) - { - try - { - if (PlayerClient != null) - { - if (PlayerClient.Opened) - { - if (Deafened == false) - { - byte[] linearBytes = Utils.MuLawToLinear(rtp.Data, BitsPerSample, Channels); - PlayerClient.PlayData(linearBytes, false); - } - } - } - } - catch (Exception ex) - { - System.Diagnostics.StackFrame sf = new(true); - errorin?.Invoke(new Exception(string.Format("Exception: {0} StackTrace: {1}. FileName: {2} Method: {3} Line: {4}", ex.Message, ex.StackTrace, sf.GetFileName(), sf.GetMethod(), sf.GetFileLineNumber()))); - } - } - - private void StartRecordingFromSounddevice_Client(RecordingDevice device) - { - try - { - if (recording) - { - StopRecordingFromSounddevice_Client(); - } - recording = true; - InitJitterBufferClientRecording(); - int bufferSize = 0; - bufferSize = Utils.GetBytesPerInterval((uint)Samples, BitsPerSample, Channels) * 4; - - if (bufferSize > 0) - { - RecorderClient = new Recorder(); - RecorderClient.DataRecorded += new Recorder.DelegateDataRecorded(OnDataReceivedFromSoundcard_Client); - - if (RecorderClient.Start(device.Name, Samples, BitsPerSample, Channels, 8, bufferSize)) - { - - RecordingJitterBuffer.Start(); - } - } - } - catch (Exception ex) - { - errorin.Invoke(ex); - } - } - - private void StopRecordingFromSounddevice_Client() - { - RecorderClient.Stop(); - - RecorderClient.DataRecorded -= new Recorder.DelegateDataRecorded(OnDataReceivedFromSoundcard_Client); - RecorderClient = null; - - RecordingJitterBuffer.Stop(); - } - - private void InitJitterBufferClientRecording() - { - if (RecordingJitterBuffer != null) - { - RecordingJitterBuffer.DataAvailable -= new JitterBuffer.DelegateDataAvailable(OnJitterBufferClientDataAvailableRecording); - } - - RecordingJitterBuffer = new JitterBuffer(null, 8, 20); - RecordingJitterBuffer.DataAvailable += new JitterBuffer.DelegateDataAvailable(OnJitterBufferClientDataAvailableRecording); - } - - private void OnJitterBufferClientDataAvailableRecording(object sender, RTPPacket rtp) - { - if (Muted == false && rtp != null && rtp.Data != null && rtp.Data.Length > 0) - { - byte[] rtpBytes = rtp.ToBytes(); - SendData(rtpBytes); - } - } - - private void OnDataReceivedFromSoundcard_Client(byte[] data) - { - try - { - lock (this) - { - int bytesPerInterval = Utils.GetBytesPerInterval((uint)Samples, BitsPerSample, Channels); - int count = data.Length / bytesPerInterval; - int currentPos = 0; - for (int i = 0; i < count; i++) - { - byte[] partBytes = new byte[bytesPerInterval]; - Array.Copy(data, currentPos, partBytes, 0, bytesPerInterval); - currentPos += bytesPerInterval; - RTPPacket rtp = ToRTPPacket(partBytes, BitsPerSample, Channels); - - RecordingJitterBuffer.AddData(rtp); - } - } - } - catch (Exception ex) - { - System.Diagnostics.Debug.WriteLine(ex.Message); - } - } - - private RTPPacket ToRTPPacket(byte[] linearData, int bitsPerSample, int channels) - { - byte[] mulaws = Utils.LinearToMulaw(linearData, bitsPerSample, channels); - - RTPPacket rtp = new() - { - Data = mulaws, - CSRCCount = 0, - Extension = false, - HeaderLength = RTPPacket.MinHeaderLength, - Marker = false, - Padding = false, - PayloadType = 0, - Version = 2, - SourceId = 0 - }; - - try - { - rtp.SequenceNumber = Convert.ToUInt16(SequenceNumber); - SequenceNumber++; - } - catch (Exception) - { - SequenceNumber = 0; - } - try - { - rtp.Timestamp = Convert.ToUInt32(m_TimeStamp); - m_TimeStamp += mulaws.Length; - } - catch (Exception) - { - m_TimeStamp = 0; - } - - return rtp; - } - - private void OnProtocolClient_DataComplete(object sender, byte[] data) - { - try - { - if (PlayerClient != null && PlayerClient.Opened) - { - RTPPacket rtp = new(data); - - if (rtp.Data != null) - { - if (PlayingJitterBuffer != null) - { - PlayingJitterBuffer.AddData(rtp); - } - } - } - } - catch (Exception ex) - { - Console.WriteLine(ex.Message); - } - } - } -} diff --git a/Luski.net/Sound/Devices.cs b/Luski.net/Sound/Devices.cs deleted file mode 100755 index 8bf90f2..0000000 --- a/Luski.net/Sound/Devices.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System.Collections.Generic; - -namespace Luski.net.Sound -{ - public static class Devices - { - public static RecordingDevice GetDefaltRecordingDevice() - { - return GetRecordingDevices()[0]; - } - - public static PlaybackDevice GetDefaltPlaybackDevice() - { - return GetPlaybackDevices()[0]; - } - - public static IReadOnlyList GetRecordingDevices() - { - List RecordingNames = WinSound.GetRecordingNames(); - List RecordingDevices = new(); - foreach (string Device in RecordingNames) - { - RecordingDevices.Add(new RecordingDevice(Device)); - } - return RecordingDevices.AsReadOnly(); - } - public static IReadOnlyList GetPlaybackDevices() - { - List PlaybackName = WinSound.GetPlaybackNames(); - List PlaybackDevices = new(); - foreach (string Device in PlaybackName) - { - PlaybackDevices.Add(new PlaybackDevice(Device)); - } - return PlaybackDevices.AsReadOnly(); - } - } - - public class RecordingDevice - { - internal RecordingDevice(string name) - { - Name = name; - } - - public string Name { get; } - } - - public class PlaybackDevice - { - internal PlaybackDevice(string name) - { - Name = name; - } - - public string Name { get; } - } -} diff --git a/Luski.net/Sound/JitterBuffer.cs b/Luski.net/Sound/JitterBuffer.cs deleted file mode 100755 index 27348ab..0000000 --- a/Luski.net/Sound/JitterBuffer.cs +++ /dev/null @@ -1,124 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Luski.net.Sound -{ - internal class JitterBuffer - { - internal JitterBuffer(object? sender, uint maxRTPPackets, uint timerIntervalInMilliseconds) - { - if (maxRTPPackets < 2) - { - throw new Exception("Wrong Arguments. Minimum maxRTPPackets is 2"); - } - - m_Sender = sender; - Maximum = maxRTPPackets; - IntervalInMilliseconds = timerIntervalInMilliseconds; - - Init(); - } - - private readonly object? m_Sender = null; - private readonly EventTimer m_Timer = new(); - private readonly Queue m_Buffer = new(); - private RTPPacket m_LastRTPPacket = new(); - private bool m_Underflow = true; - private bool m_Overflow = false; - - internal delegate void DelegateDataAvailable(object sender, RTPPacket packet); - internal event DelegateDataAvailable? DataAvailable; - - internal uint Maximum { get; } = 10; - internal uint IntervalInMilliseconds { get; } = 20; - private void Init() - { - InitTimer(); - } - private void InitTimer() - { - m_Timer.TimerTick += new EventTimer.DelegateTimerTick(OnTimerTick); - } - internal void Start() - { - m_Timer.Start(IntervalInMilliseconds); - m_Underflow = true; - } - internal void Stop() - { - m_Timer.Stop(); - m_Buffer.Clear(); - } - private void OnTimerTick() - { - try - { - if (DataAvailable != null) - { - if (m_Buffer.Count > 0) - { - if (m_Overflow) - { - if (m_Buffer.Count <= Maximum / 2) - { - m_Overflow = false; - } - } - - if (m_Underflow) - { - if (m_Buffer.Count < Maximum / 2) - { - return; - } - else - { - m_Underflow = false; - } - } - - m_LastRTPPacket = m_Buffer.Dequeue(); - DataAvailable(m_Sender, m_LastRTPPacket); - } - else - { - m_Overflow = false; - - if (m_LastRTPPacket != null && m_Underflow == false) - { - if (m_LastRTPPacket.Data != null) - { - m_Underflow = true; - } - } - } - } - } - catch (Exception ex) - { - Console.WriteLine(string.Format("JitterBuffer.cs | OnTimerTick() | {0}", ex.Message)); - } - } - internal void AddData(RTPPacket packet) - { - try - { - if (m_Overflow == false) - { - if (m_Buffer.Count <= Maximum) - { - m_Buffer.Enqueue(packet); - } - else - { - m_Overflow = true; - } - } - } - catch (Exception ex) - { - Console.WriteLine(string.Format("JitterBuffer.cs | AddData() | {0}", ex.Message)); - } - } - } -} diff --git a/Luski.net/Sound/Player.cs b/Luski.net/Sound/Player.cs deleted file mode 100755 index 173594d..0000000 --- a/Luski.net/Sound/Player.cs +++ /dev/null @@ -1,417 +0,0 @@ -using System; -using System.Runtime.InteropServices; -using System.Threading; - -namespace Luski.net.Sound -{ - internal unsafe class Player - { - internal Player() - { - - delegateWaveOutProc = new Win32.DelegateWaveOutProc(WaveOutProc); - } - - private readonly LockerClass Locker = new(); - private readonly LockerClass LockerCopy = new(); - private IntPtr hWaveOut = IntPtr.Zero; - private string WaveOutDeviceName = ""; - private bool IsWaveOutOpened = false; - private bool IsThreadPlayWaveOutRunning = false; - private bool IsClosed = false; - private bool IsPaused = false; - private bool IsStarted = false; - private bool IsBlocking = false; - private int SamplesPerSecond = 8000; - private int BitsPerSample = 16; - private int Channels = 1; - private int BufferCount = 8; - private readonly int BufferLength = 1024; - private Win32.WAVEHDR*[] WaveOutHeaders; - private readonly Win32.DelegateWaveOutProc delegateWaveOutProc; - private Thread? ThreadPlayWaveOut; - private readonly AutoResetEvent AutoResetEventDataPlayed = new(false); - - internal delegate void DelegateStopped(); - internal event DelegateStopped? PlayerClosed; - internal event DelegateStopped? PlayerStopped; - - internal bool Opened => IsWaveOutOpened & IsClosed == false; - - internal bool Playing - { - get - { - if (Opened && IsStarted) - { - foreach (Win32.WAVEHDR* pHeader in WaveOutHeaders) - { - if (IsHeaderInqueue(*pHeader)) - { - return true; - } - } - } - return false; - } - } - - private bool CreateWaveOutHeaders() - { - WaveOutHeaders = new Win32.WAVEHDR*[BufferCount]; - int createdHeaders = 0; - - for (int i = 0; i < BufferCount; i++) - { - WaveOutHeaders[i] = (Win32.WAVEHDR*)Marshal.AllocHGlobal(sizeof(Win32.WAVEHDR)); - - WaveOutHeaders[i]->dwLoops = 0; - WaveOutHeaders[i]->dwUser = IntPtr.Zero; - WaveOutHeaders[i]->lpNext = IntPtr.Zero; - WaveOutHeaders[i]->reserved = IntPtr.Zero; - WaveOutHeaders[i]->lpData = Marshal.AllocHGlobal(BufferLength); - WaveOutHeaders[i]->dwBufferLength = (uint)BufferLength; - WaveOutHeaders[i]->dwBytesRecorded = 0; - WaveOutHeaders[i]->dwFlags = 0; - - Win32.MMRESULT hr = Win32.waveOutPrepareHeader(hWaveOut, WaveOutHeaders[i], sizeof(Win32.WAVEHDR)); - if (hr == Win32.MMRESULT.MMSYSERR_NOERROR) - { - createdHeaders++; - } - } - - return (createdHeaders == BufferCount); - } - - private void FreeWaveOutHeaders() - { - try - { - if (WaveOutHeaders != null) - { - for (int i = 0; i < WaveOutHeaders.Length; i++) - { - Win32.MMRESULT hr = Win32.waveOutUnprepareHeader(hWaveOut, WaveOutHeaders[i], sizeof(Win32.WAVEHDR)); - - int count = 0; - while (count <= 100 && (WaveOutHeaders[i]->dwFlags & Win32.WaveHdrFlags.WHDR_INQUEUE) == Win32.WaveHdrFlags.WHDR_INQUEUE) - { - Thread.Sleep(20); - count++; - } - - if ((WaveOutHeaders[i]->dwFlags & Win32.WaveHdrFlags.WHDR_INQUEUE) != Win32.WaveHdrFlags.WHDR_INQUEUE) - { - if (WaveOutHeaders[i]->lpData != IntPtr.Zero) - { - Marshal.FreeHGlobal(WaveOutHeaders[i]->lpData); - WaveOutHeaders[i]->lpData = IntPtr.Zero; - } - } - } - } - } - catch (Exception ex) - { - System.Diagnostics.Debug.Write(ex.Message); - } - } - - private void StartThreadPlayWaveOut() - { - if (IsThreadPlayWaveOutRunning == false) - { - ThreadPlayWaveOut = new System.Threading.Thread(new System.Threading.ThreadStart(OnThreadPlayWaveOut)); - IsThreadPlayWaveOutRunning = true; - ThreadPlayWaveOut.Name = "PlayWaveOut"; - ThreadPlayWaveOut.Priority = System.Threading.ThreadPriority.Highest; - ThreadPlayWaveOut.Start(); - } - } - - private bool OpenWaveOut() - { - if (hWaveOut == IntPtr.Zero) - { - if (IsWaveOutOpened == false) - { - Win32.WAVEFORMATEX waveFormatEx = new() - { - wFormatTag = (ushort)Win32.WaveFormatFlags.WAVE_FORMAT_PCM, - nChannels = (ushort)Channels, - nSamplesPerSec = (ushort)SamplesPerSecond, - wBitsPerSample = (ushort)BitsPerSample - }; - waveFormatEx.nBlockAlign = (ushort)((waveFormatEx.wBitsPerSample * waveFormatEx.nChannels) >> 3); - waveFormatEx.nAvgBytesPerSec = waveFormatEx.nBlockAlign * waveFormatEx.nSamplesPerSec; - - int deviceId = WinSound.GetWaveOutDeviceIdByName(WaveOutDeviceName); - Win32.MMRESULT hr = Win32.waveOutOpen(ref hWaveOut, deviceId, ref waveFormatEx, delegateWaveOutProc, 0, (int)Win32.WaveProcFlags.CALLBACK_FUNCTION); - - if (hr != Win32.MMRESULT.MMSYSERR_NOERROR) - { - IsWaveOutOpened = false; - return false; - } - - GCHandle.Alloc(hWaveOut, GCHandleType.Pinned); - } - } - - IsWaveOutOpened = true; - return true; - } - - internal bool Open(string waveOutDeviceName, int samplesPerSecond, int bitsPerSample, int channels, int bufferCount) - { - try - { - lock (Locker) - { - if (Opened == false) - { - - WaveOutDeviceName = waveOutDeviceName; - SamplesPerSecond = samplesPerSecond; - BitsPerSample = bitsPerSample; - Channels = channels; - BufferCount = Math.Max(bufferCount, 1); - - if (OpenWaveOut()) - { - if (CreateWaveOutHeaders()) - { - StartThreadPlayWaveOut(); - IsClosed = false; - return true; - } - } - } - - return false; - } - } - catch (Exception ex) - { - System.Diagnostics.Debug.WriteLine(string.Format("Start | {0}", ex.Message)); - return false; - } - } - - internal bool PlayData(byte[] datas, bool isBlocking) - { - try - { - if (Opened) - { - int index = GetNextFreeWaveOutHeaderIndex(); - if (index != -1) - { - IsBlocking = isBlocking; - - if (WaveOutHeaders[index]->dwBufferLength != datas.Length) - { - Marshal.FreeHGlobal(WaveOutHeaders[index]->lpData); - WaveOutHeaders[index]->lpData = Marshal.AllocHGlobal(datas.Length); - WaveOutHeaders[index]->dwBufferLength = (uint)datas.Length; - } - - WaveOutHeaders[index]->dwBufferLength = (uint)datas.Length; - WaveOutHeaders[index]->dwUser = (IntPtr)index; - Marshal.Copy(datas, 0, WaveOutHeaders[index]->lpData, datas.Length); - - IsStarted = true; - Win32.MMRESULT hr = Win32.waveOutWrite(hWaveOut, WaveOutHeaders[index], sizeof(Win32.WAVEHDR)); - if (hr == Win32.MMRESULT.MMSYSERR_NOERROR) - { - if (isBlocking) - { - AutoResetEventDataPlayed.WaitOne(); - AutoResetEventDataPlayed.Set(); - } - return true; - } - else - { - AutoResetEventDataPlayed.Set(); - return false; - } - } - else - { - System.Diagnostics.Debug.WriteLine(string.Format("No free WaveOut Buffer found | {0}", DateTime.Now.ToLongTimeString())); - return false; - } - } - else - { - return false; - } - - } - catch (Exception ex) - { - System.Diagnostics.Debug.WriteLine(string.Format("PlayData | {0}", ex.Message)); - return false; - } - } - - internal bool Close() - { - try - { - lock (Locker) - { - if (Opened) - { - IsClosed = true; - - int count = 0; - while (Win32.waveOutReset(hWaveOut) != Win32.MMRESULT.MMSYSERR_NOERROR && count <= 100) - { - Thread.Sleep(50); - count++; - } - - FreeWaveOutHeaders(); - - count = 0; - while (Win32.waveOutClose(hWaveOut) != Win32.MMRESULT.MMSYSERR_NOERROR && count <= 100) - { - Thread.Sleep(50); - count++; - } - - IsWaveOutOpened = false; - AutoResetEventDataPlayed.Set(); - return true; - } - return false; - } - } - catch (Exception ex) - { - System.Diagnostics.Debug.WriteLine(string.Format("Close | {0}", ex.Message)); - return false; - } - } - - private int GetNextFreeWaveOutHeaderIndex() - { - for (int i = 0; i < WaveOutHeaders.Length; i++) - { - if (IsHeaderPrepared(*WaveOutHeaders[i]) && !IsHeaderInqueue(*WaveOutHeaders[i])) - { - return i; - } - } - return -1; - } - - private static bool IsHeaderPrepared(Win32.WAVEHDR header) - { - return (header.dwFlags & Win32.WaveHdrFlags.WHDR_PREPARED) > 0; - } - - private static bool IsHeaderInqueue(Win32.WAVEHDR header) - { - return (header.dwFlags & Win32.WaveHdrFlags.WHDR_INQUEUE) > 0; - } - - private void WaveOutProc(IntPtr hWaveOut, Win32.WOM_Messages msg, IntPtr dwInstance, Win32.WAVEHDR* pWaveHeader, IntPtr lParam) - { - try - { - switch (msg) - { - //Open - case Win32.WOM_Messages.OPEN: - break; - - //Done - case Win32.WOM_Messages.DONE: - IsStarted = true; - AutoResetEventDataPlayed.Set(); - break; - - //Close - case Win32.WOM_Messages.CLOSE: - IsStarted = false; - IsWaveOutOpened = false; - IsPaused = false; - IsClosed = true; - AutoResetEventDataPlayed.Set(); - hWaveOut = IntPtr.Zero; - break; - } - } - catch (Exception ex) - { - System.Diagnostics.Debug.WriteLine(string.Format("Player.cs | waveOutProc() | {0}", ex.Message)); - AutoResetEventDataPlayed.Set(); - } - } - - private void OnThreadPlayWaveOut() - { - while (Opened && !IsClosed) - { - AutoResetEventDataPlayed.WaitOne(); - - lock (Locker) - { - if (Opened && !IsClosed) - { - IsThreadPlayWaveOutRunning = true; - - if (!Playing) - { - if (IsStarted) - { - IsStarted = false; - if (PlayerStopped != null) - { - try - { - PlayerStopped(); - } - catch (Exception ex) - { - System.Diagnostics.Debug.WriteLine(string.Format("Player Stopped | {0}", ex.Message)); - } - finally - { - AutoResetEventDataPlayed.Set(); - } - } - } - } - } - } - - if (IsBlocking) - { - AutoResetEventDataPlayed.Set(); - } - } - - lock (Locker) - { - IsThreadPlayWaveOutRunning = false; - } - - if (PlayerClosed != null) - { - try - { - PlayerClosed(); - } - catch (Exception ex) - { - System.Diagnostics.Debug.WriteLine(string.Format("Player Closed | {0}", ex.Message)); - } - } - } - } -} diff --git a/Luski.net/Sound/Protocol.cs b/Luski.net/Sound/Protocol.cs deleted file mode 100755 index 2ba7550..0000000 --- a/Luski.net/Sound/Protocol.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Luski.net.Sound -{ - internal enum ProtocolTypes - { - LH - } - - internal class Protocol - { - internal Protocol(ProtocolTypes type, Encoding encoding) - { - m_ProtocolType = type; - m_Encoding = encoding; - } - - private readonly List m_DataBuffer = new(); - private const int m_MaxBufferLength = 10000; - private readonly ProtocolTypes m_ProtocolType = ProtocolTypes.LH; - private readonly Encoding m_Encoding = Encoding.Default; - internal object m_LockerReceive = new(); - - internal delegate void DelegateDataComplete(object sender, byte[] data); - internal delegate void DelegateExceptionAppeared(object sender, Exception ex); - internal event DelegateDataComplete DataComplete; - internal event DelegateExceptionAppeared ExceptionAppeared; - - internal byte[] ToBytes(byte[] data) - { - try - { - byte[] bytesLength = BitConverter.GetBytes(data.Length); - - byte[] allBytes = new byte[bytesLength.Length + data.Length]; - Array.Copy(bytesLength, allBytes, bytesLength.Length); - Array.Copy(data, 0, allBytes, bytesLength.Length, data.Length); - - return allBytes; - } - catch (Exception ex) - { - ExceptionAppeared(null, ex); - } - - return data; - } - - internal void Receive_LH(object sender, byte[] data) - { - lock (m_LockerReceive) - { - try - { - m_DataBuffer.AddRange(data); - - if (m_DataBuffer.Count > m_MaxBufferLength) - { - m_DataBuffer.Clear(); - } - - byte[] bytes = m_DataBuffer.Take(4).ToArray(); - int length = BitConverter.ToInt32(bytes.ToArray(), 0); - - if (length > m_MaxBufferLength) - { - m_DataBuffer.Clear(); - } - - while (m_DataBuffer.Count >= length + 4) - { - byte[] message = m_DataBuffer.Skip(4).Take(length).ToArray(); - - DataComplete?.Invoke(sender, message); - m_DataBuffer.RemoveRange(0, length + 4); - - if (m_DataBuffer.Count > 4) - { - bytes = m_DataBuffer.Take(4).ToArray(); - length = BitConverter.ToInt32(bytes.ToArray(), 0); - } - } - } - catch (Exception ex) - { - m_DataBuffer.Clear(); - ExceptionAppeared(null, ex); - } - } - } - } -} diff --git a/Luski.net/Sound/RTPPacket.cs b/Luski.net/Sound/RTPPacket.cs deleted file mode 100755 index c446114..0000000 --- a/Luski.net/Sound/RTPPacket.cs +++ /dev/null @@ -1,139 +0,0 @@ -using System; -using System.Linq; - -namespace Luski.net.Sound -{ - internal class RTPPacket - { - internal RTPPacket() - { - - } - - internal RTPPacket(byte[] data) - { - Parse(data); - } - - internal static int MinHeaderLength = 12; - internal int HeaderLength = MinHeaderLength; - internal int Version = 0; - internal bool Padding = false; - internal bool Extension = false; - internal int CSRCCount = 0; - internal bool Marker = false; - internal int PayloadType = 0; - internal ushort SequenceNumber = 0; - internal uint Timestamp = 0; - internal uint SourceId = 0; - internal byte[]? Data; - internal ushort ExtensionHeaderId = 0; - internal ushort ExtensionLengthAsCount = 0; - internal int ExtensionLengthInBytes = 0; - - private void Parse(byte[] data) - { - if (data.Length >= MinHeaderLength) - { - Version = ValueFromByte(data[0], 6, 2); - Padding = Convert.ToBoolean(ValueFromByte(data[0], 5, 1)); - Extension = Convert.ToBoolean(ValueFromByte(data[0], 4, 1)); - CSRCCount = ValueFromByte(data[0], 0, 4); - Marker = Convert.ToBoolean(ValueFromByte(data[1], 7, 1)); - PayloadType = ValueFromByte(data[1], 0, 7); - HeaderLength = MinHeaderLength + (CSRCCount * 4); - - //Sequence Nummer - byte[] seqNum = new byte[2]; - seqNum[0] = data[3]; - seqNum[1] = data[2]; - SequenceNumber = BitConverter.ToUInt16(seqNum, 0); - - //TimeStamp - byte[] timeStmp = new byte[4]; - timeStmp[0] = data[7]; - timeStmp[1] = data[6]; - timeStmp[2] = data[5]; - timeStmp[3] = data[4]; - Timestamp = BitConverter.ToUInt32(timeStmp, 0); - - //SourceId - byte[] srcId = new byte[4]; - srcId[0] = data[8]; - srcId[1] = data[9]; - srcId[2] = data[10]; - srcId[3] = data[11]; - SourceId = BitConverter.ToUInt32(srcId, 0); - - if (Extension) - { - byte[] extHeaderId = new byte[2]; - extHeaderId[1] = data[HeaderLength + 0]; - extHeaderId[0] = data[HeaderLength + 1]; - ExtensionHeaderId = BitConverter.ToUInt16(extHeaderId, 0); - - byte[] extHeaderLength16 = new byte[2]; - extHeaderLength16[1] = data[HeaderLength + 2]; - extHeaderLength16[0] = data[HeaderLength + 3]; - ExtensionLengthAsCount = BitConverter.ToUInt16(extHeaderLength16.ToArray(), 0); - - ExtensionLengthInBytes = ExtensionLengthAsCount * 4; - HeaderLength += ExtensionLengthInBytes + 4; - } - - Data = new byte[data.Length - HeaderLength]; - Array.Copy(data, HeaderLength, Data, 0, data.Length - HeaderLength); - } - } - - private static int ValueFromByte(byte value, int startPos, int length) - { - byte mask = 0; - for (int i = 0; i < length; i++) - { - mask = (byte)(mask | 0x1 << startPos + i); - } - - byte result = (byte)((value & mask) >> startPos); - return Convert.ToInt32(result); - } - - internal byte[] ToBytes() - { - byte[] bytes = new byte[HeaderLength + Data.Length]; - - //Byte 0 - bytes[0] = (byte)(Version << 6); - bytes[0] |= (byte)(Convert.ToInt32(Padding) << 5); - bytes[0] |= (byte)(Convert.ToInt32(Extension) << 4); - bytes[0] |= (byte)(Convert.ToInt32(CSRCCount)); - - //Byte 1 - bytes[1] = (byte)(Convert.ToInt32(Marker) << 7); - bytes[1] |= (byte)(Convert.ToInt32(PayloadType)); - - //Byte 2 + 3 - byte[] bytesSequenceNumber = BitConverter.GetBytes(SequenceNumber); - bytes[2] = bytesSequenceNumber[1]; - bytes[3] = bytesSequenceNumber[0]; - - //Byte 4 bis 7 - byte[] bytesTimeStamp = BitConverter.GetBytes(Timestamp); - bytes[4] = bytesTimeStamp[3]; - bytes[5] = bytesTimeStamp[2]; - bytes[6] = bytesTimeStamp[1]; - bytes[7] = bytesTimeStamp[0]; - - //Byte 8 bis 11 - byte[] bytesSourceId = BitConverter.GetBytes(SourceId); - bytes[8] = bytesSourceId[3]; - bytes[9] = bytesSourceId[2]; - bytes[10] = bytesSourceId[1]; - bytes[11] = bytesSourceId[0]; - - Array.Copy(Data, 0, bytes, HeaderLength, Data.Length); - - return bytes; - } - } -} diff --git a/Luski.net/Sound/Recorder.cs b/Luski.net/Sound/Recorder.cs deleted file mode 100755 index 93a0093..0000000 --- a/Luski.net/Sound/Recorder.cs +++ /dev/null @@ -1,340 +0,0 @@ -using System; -using System.Runtime.InteropServices; -using System.Threading; - -namespace Luski.net.Sound -{ - internal unsafe class Recorder - { - internal Recorder() - { - delegateWaveInProc = new Win32.DelegateWaveInProc(WaveInProc); - } - - private readonly LockerClass Locker = new(); - private readonly LockerClass LockerCopy = new(); - private IntPtr hWaveIn = IntPtr.Zero; - private string WaveInDeviceName = ""; - private bool IsWaveInOpened = false; - private bool IsWaveInStarted = false; - private bool IsThreadRecordingRunning = false; - private bool IsDataIncomming = false; - private bool Stopped = false; - private int SamplesPerSecond = 8000; - private int BitsPerSample = 16; - private int Channels = 1; - private int BufferCount = 8; - private int BufferSize = 1024; - private Win32.WAVEHDR*[] WaveInHeaders; - private Win32.WAVEHDR* CurrentRecordedHeader; - private readonly Win32.DelegateWaveInProc delegateWaveInProc; - private Thread ThreadRecording; - private readonly AutoResetEvent AutoResetEventDataRecorded = new(false); - - internal delegate void DelegateStopped(); - internal delegate void DelegateDataRecorded(byte[] bytes); - internal event DelegateStopped RecordingStopped; - internal event DelegateDataRecorded DataRecorded; - - internal bool Started => IsWaveInStarted && IsWaveInOpened && IsThreadRecordingRunning; - - private bool CreateWaveInHeaders() - { - WaveInHeaders = new Win32.WAVEHDR*[BufferCount]; - int createdHeaders = 0; - - for (int i = 0; i < BufferCount; i++) - { - WaveInHeaders[i] = (Win32.WAVEHDR*)Marshal.AllocHGlobal(sizeof(Win32.WAVEHDR)); - - WaveInHeaders[i]->dwLoops = 0; - WaveInHeaders[i]->dwUser = IntPtr.Zero; - WaveInHeaders[i]->lpNext = IntPtr.Zero; - WaveInHeaders[i]->reserved = IntPtr.Zero; - WaveInHeaders[i]->lpData = Marshal.AllocHGlobal(BufferSize); - WaveInHeaders[i]->dwBufferLength = (uint)BufferSize; - WaveInHeaders[i]->dwBytesRecorded = 0; - WaveInHeaders[i]->dwFlags = 0; - - Win32.MMRESULT hr = Win32.waveInPrepareHeader(hWaveIn, WaveInHeaders[i], sizeof(Win32.WAVEHDR)); - if (hr == Win32.MMRESULT.MMSYSERR_NOERROR) - { - if (i == 0) - { - hr = Win32.waveInAddBuffer(hWaveIn, WaveInHeaders[i], sizeof(Win32.WAVEHDR)); - } - createdHeaders++; - } - } - - return (createdHeaders == BufferCount); - } - - private void FreeWaveInHeaders() - { - try - { - if (WaveInHeaders != null) - { - for (int i = 0; i < WaveInHeaders.Length; i++) - { - Win32.MMRESULT hr = Win32.waveInUnprepareHeader(hWaveIn, WaveInHeaders[i], sizeof(Win32.WAVEHDR)); - - int count = 0; - while (count <= 100 && (WaveInHeaders[i]->dwFlags & Win32.WaveHdrFlags.WHDR_INQUEUE) == Win32.WaveHdrFlags.WHDR_INQUEUE) - { - Thread.Sleep(20); - count++; - } - - if ((WaveInHeaders[i]->dwFlags & Win32.WaveHdrFlags.WHDR_INQUEUE) != Win32.WaveHdrFlags.WHDR_INQUEUE) - { - if (WaveInHeaders[i]->lpData != IntPtr.Zero) - { - Marshal.FreeHGlobal(WaveInHeaders[i]->lpData); - WaveInHeaders[i]->lpData = IntPtr.Zero; - } - } - } - } - } - catch (Exception ex) - { - System.Diagnostics.Debug.Write(ex.Message); - } - } - - private void StartThreadRecording() - { - if (Started == false) - { - ThreadRecording = new Thread(new ThreadStart(OnThreadRecording)); - IsThreadRecordingRunning = true; - ThreadRecording.Name = "Recording"; - ThreadRecording.Priority = ThreadPriority.Highest; - ThreadRecording.Start(); - } - } - - private bool OpenWaveIn() - { - if (hWaveIn == IntPtr.Zero) - { - if (IsWaveInOpened == false) - { - Win32.WAVEFORMATEX waveFormatEx = new() - { - wFormatTag = (ushort)Win32.WaveFormatFlags.WAVE_FORMAT_PCM, - nChannels = (ushort)Channels, - nSamplesPerSec = (ushort)SamplesPerSecond, - wBitsPerSample = (ushort)BitsPerSample - }; - waveFormatEx.nBlockAlign = (ushort)((waveFormatEx.wBitsPerSample * waveFormatEx.nChannels) >> 3); - waveFormatEx.nAvgBytesPerSec = waveFormatEx.nBlockAlign * waveFormatEx.nSamplesPerSec; - - int deviceId = WinSound.GetWaveInDeviceIdByName(WaveInDeviceName); - Win32.MMRESULT hr = Win32.waveInOpen(ref hWaveIn, deviceId, ref waveFormatEx, delegateWaveInProc, 0, (int)Win32.WaveProcFlags.CALLBACK_FUNCTION); - - if (hWaveIn == IntPtr.Zero) - { - IsWaveInOpened = false; - return false; - } - - GCHandle.Alloc(hWaveIn, GCHandleType.Pinned); - } - } - - IsWaveInOpened = true; - return true; - } - - internal bool Start(string waveInDeviceName, int samplesPerSecond, int bitsPerSample, int channels, int bufferCount, int bufferSize) - { - try - { - lock (Locker) - { - if (Started == false) - { - WaveInDeviceName = waveInDeviceName; - SamplesPerSecond = samplesPerSecond; - BitsPerSample = bitsPerSample; - Channels = channels; - BufferCount = bufferCount; - BufferSize = bufferSize; - - if (OpenWaveIn()) - { - if (CreateWaveInHeaders()) - { - Win32.MMRESULT hr = Win32.waveInStart(hWaveIn); - if (hr == Win32.MMRESULT.MMSYSERR_NOERROR) - { - IsWaveInStarted = true; - StartThreadRecording(); - Stopped = false; - return true; - } - else - { - return false; - } - } - } - } - - return false; - } - } - catch (Exception ex) - { - System.Diagnostics.Debug.WriteLine(string.Format("Start | {0}", ex.Message)); - return false; - } - } - - internal bool Stop() - { - try - { - lock (Locker) - { - if (Started) - { - Stopped = true; - IsThreadRecordingRunning = false; - - CloseWaveIn(); - - AutoResetEventDataRecorded.Set(); - return true; - } - return false; - } - } - catch (Exception ex) - { - System.Diagnostics.Debug.WriteLine(string.Format("Stop | {0}", ex.Message)); - return false; - } - } - - private void CloseWaveIn() - { - Win32.MMRESULT hr = Win32.waveInStop(hWaveIn); - - int resetCount = 0; - while (IsAnyWaveInHeaderInState(Win32.WaveHdrFlags.WHDR_INQUEUE) & resetCount < 20) - { - hr = Win32.waveInReset(hWaveIn); - Thread.Sleep(50); - resetCount++; - } - - FreeWaveInHeaders(); - hr = Win32.waveInClose(hWaveIn); - } - - private bool IsAnyWaveInHeaderInState(Win32.WaveHdrFlags state) - { - for (int i = 0; i < WaveInHeaders.Length; i++) - { - if ((WaveInHeaders[i]->dwFlags & state) == state) - { - return true; - } - } - return false; - } - - private void WaveInProc(IntPtr hWaveIn, Win32.WIM_Messages msg, IntPtr dwInstance, Win32.WAVEHDR* pWaveHdr, IntPtr lParam) - { - switch (msg) - { - //Open - case Win32.WIM_Messages.OPEN: - break; - - //Data - case Win32.WIM_Messages.DATA: - IsDataIncomming = true; - CurrentRecordedHeader = pWaveHdr; - AutoResetEventDataRecorded.Set(); - break; - - //Close - case Win32.WIM_Messages.CLOSE: - IsDataIncomming = false; - IsWaveInOpened = false; - AutoResetEventDataRecorded.Set(); - this.hWaveIn = IntPtr.Zero; - break; - } - } - - private void OnThreadRecording() - { - while (Started && !Stopped) - { - AutoResetEventDataRecorded.WaitOne(); - - try - { - if (Started && !Stopped) - { - if (CurrentRecordedHeader->dwBytesRecorded > 0) - { - if (DataRecorded != null && IsDataIncomming) - { - try - { - byte[] bytes = new byte[CurrentRecordedHeader->dwBytesRecorded]; - Marshal.Copy(CurrentRecordedHeader->lpData, bytes, 0, (int)CurrentRecordedHeader->dwBytesRecorded); - - DataRecorded(bytes); - } - catch (Exception ex) - { - System.Diagnostics.Debug.WriteLine(string.Format("Recorder.cs | OnThreadRecording() | {0}", ex.Message)); - } - } - - for (int i = 0; i < WaveInHeaders.Length; i++) - { - if ((WaveInHeaders[i]->dwFlags & Win32.WaveHdrFlags.WHDR_INQUEUE) == 0) - { - Win32.MMRESULT hr = Win32.waveInAddBuffer(hWaveIn, WaveInHeaders[i], sizeof(Win32.WAVEHDR)); - } - } - - } - } - } - catch (Exception ex) - { - System.Diagnostics.Debug.WriteLine(ex.Message); - } - } - - - lock (Locker) - { - IsWaveInStarted = false; - IsThreadRecordingRunning = false; - } - - if (RecordingStopped != null) - { - try - { - RecordingStopped(); - } - catch (Exception ex) - { - System.Diagnostics.Debug.WriteLine(string.Format("Recording Stopped | {0}", ex.Message)); - } - } - } - } -} diff --git a/Luski.net/Sound/Timer.cs b/Luski.net/Sound/Timer.cs deleted file mode 100755 index 3884ad9..0000000 --- a/Luski.net/Sound/Timer.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace Luski.net.Sound -{ - internal class EventTimer - { - internal EventTimer() - { - m_DelegateTimeEvent = new Win32.TimerEventHandler(OnTimer); - } - - private bool m_IsRunning = false; - private uint m_Milliseconds = 20; - private uint m_TimerId = 0; - private GCHandle m_GCHandleTimer; - private uint m_UserData = 0; - private uint m_ResolutionInMilliseconds = 0; - - private readonly Win32.TimerEventHandler m_DelegateTimeEvent; - internal delegate void DelegateTimerTick(); - internal event DelegateTimerTick? TimerTick; - - internal void Start(uint milliseconds) - { - m_Milliseconds = milliseconds; - - Win32.TimeCaps tc = new(); - Win32.TimeGetDevCaps(ref tc, (uint)Marshal.SizeOf(typeof(Win32.TimeCaps))); - m_ResolutionInMilliseconds = Math.Max(tc.wPeriodMin, 0); - - Win32.TimeBeginPeriod(m_ResolutionInMilliseconds); - - m_TimerId = Win32.TimeSetEvent(m_Milliseconds, m_ResolutionInMilliseconds, m_DelegateTimeEvent, ref m_UserData, Win32.TIME_PERIODIC); - if (m_TimerId > 0) - { - m_GCHandleTimer = GCHandle.Alloc(m_TimerId, GCHandleType.Pinned); - m_IsRunning = true; - } - } - - internal void Stop() - { - if (m_TimerId > 0) - { - _ = Win32.TimeKillEvent(m_TimerId); - Win32.TimeEndPeriod(m_ResolutionInMilliseconds); - - if (m_GCHandleTimer.IsAllocated) - { - m_GCHandleTimer.Free(); - } - - m_TimerId = 0; - m_IsRunning = false; - } - } - - private void OnTimer(uint id, uint msg, ref uint userCtx, uint rsv1, uint rsv2) - { - TimerTick?.Invoke(); - } - } -} diff --git a/Luski.net/Sound/Utils.cs b/Luski.net/Sound/Utils.cs deleted file mode 100755 index 2ccb7ae..0000000 --- a/Luski.net/Sound/Utils.cs +++ /dev/null @@ -1,195 +0,0 @@ -using System; - -namespace Luski.net.Sound -{ - internal class Utils - { - internal Utils() - { - - } - - private const int SIGN_BIT = 0x80; - private const int QUANT_MASK = 0xf; - private const int SEG_SHIFT = 4; - private const int SEG_MASK = 0x70; - private const int BIAS = 0x84; - private const int CLIP = 8159; - private static readonly short[] seg_uend = new short[] { 0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF }; - - internal static int GetBytesPerInterval(uint SamplesPerSecond, int BitsPerSample, int Channels) - { - int blockAlign = ((BitsPerSample * Channels) >> 3); - int bytesPerSec = (int)(blockAlign * SamplesPerSecond); - uint sleepIntervalFactor = 1000 / 20; - int bytesPerInterval = (int)(bytesPerSec / sleepIntervalFactor); - - return bytesPerInterval; - } - - internal static int MulawToLinear(int ulaw) - { - ulaw = ~ulaw; - int t = ((ulaw & QUANT_MASK) << 3) + BIAS; - t <<= (ulaw & SEG_MASK) >> SEG_SHIFT; - return ((ulaw & SIGN_BIT) > 0 ? (BIAS - t) : (t - BIAS)); - } - - private static short Search(short val, short[] table, short size) - { - short i; - int index = 0; - for (i = 0; i < size; i++) - { - if (val <= table[index]) - { - return (i); - } - index++; - } - return (size); - } - - internal static byte Linear2ulaw(short pcm_val) - { - - /* Get the sign and the magnitude of the value. */ - pcm_val = (short)(pcm_val >> 2); - short mask; - if (pcm_val < 0) - { - pcm_val = (short)-pcm_val; - mask = 0x7F; - } - else - { - mask = 0xFF; - } - /* clip the magnitude */ - if (pcm_val > CLIP) - { - pcm_val = CLIP; - } - pcm_val += (BIAS >> 2); - - /* Convert the scaled magnitude to segment number. */ - short seg = Search(pcm_val, seg_uend, 8); - - /* - * Combine the sign, segment, quantization bits; - * and complement the code word. - */ - /* out of range, return maximum value. */ - if (seg >= 8) - { - return (byte)(0x7F ^ mask); - } - else - { - byte uval = (byte)((seg << 4) | ((pcm_val >> (seg + 1)) & 0xF)); - return ((byte)(uval ^ mask)); - } - } - - internal static byte[] MuLawToLinear(byte[] bytes, int bitsPerSample, int channels) - { - int blockAlign = channels * bitsPerSample / 8; - - byte[] result = new byte[bytes.Length * blockAlign]; - for (int i = 0, counter = 0; i < bytes.Length; i++, counter += blockAlign) - { - int value = MulawToLinear(bytes[i]); - byte[] values = BitConverter.GetBytes(value); - - switch (bitsPerSample) - { - case 8: - switch (channels) - { - //8 Bit 1 Channel - case 1: - result[counter] = values[0]; - break; - - //8 Bit 2 Channel - case 2: - result[counter] = values[0]; - result[counter + 1] = values[0]; - break; - } - break; - - case 16: - switch (channels) - { - //16 Bit 1 Channel - case 1: - result[counter] = values[0]; - result[counter + 1] = values[1]; - break; - - //16 Bit 2 Channels - case 2: - result[counter] = values[0]; - result[counter + 1] = values[1]; - result[counter + 2] = values[0]; - result[counter + 3] = values[1]; - break; - } - break; - } - } - - return result; - } - - internal static byte[] LinearToMulaw(byte[] bytes, int bitsPerSample, int channels) - { - int blockAlign = channels * bitsPerSample / 8; - - byte[] result = new byte[bytes.Length / blockAlign]; - int resultIndex = 0; - for (int i = 0; i < result.Length; i++) - { - switch (bitsPerSample) - { - case 8: - switch (channels) - { - //8 Bit 1 Channel - case 1: - result[i] = Linear2ulaw(bytes[resultIndex]); - resultIndex += 1; - break; - - //8 Bit 2 Channel - case 2: - result[i] = Linear2ulaw(bytes[resultIndex]); - resultIndex += 2; - break; - } - break; - - case 16: - switch (channels) - { - //16 Bit 1 Channel - case 1: - result[i] = Linear2ulaw(BitConverter.ToInt16(bytes, resultIndex)); - resultIndex += 2; - break; - - //16 Bit 2 Channels - case 2: - result[i] = Linear2ulaw(BitConverter.ToInt16(bytes, resultIndex)); - resultIndex += 4; - break; - } - break; - } - } - - return result; - } - } -} diff --git a/Luski.net/Sound/WaveFile.cs b/Luski.net/Sound/WaveFile.cs deleted file mode 100755 index 398c5e3..0000000 --- a/Luski.net/Sound/WaveFile.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System; -using System.IO; -using System.Text; - -namespace Luski.net.Sound -{ - internal class WaveFile - { - internal WaveFile() - { - - } - - internal const int WAVE_FORMAT_PCM = 1; - - - internal static WaveFileHeader Read(string fileName) - { - WaveFileHeader header = ReadHeader(fileName); - - return header; - } - - private static WaveFileHeader ReadHeader(string fileName) - { - WaveFileHeader header = new(); - - if (File.Exists(fileName)) - { - FileStream fs = new(fileName, FileMode.Open, FileAccess.Read); - BinaryReader rd = new(fs, Encoding.UTF8); - - if (fs.CanRead) - { - header.RIFF = rd.ReadChars(4); - header.RiffSize = (uint)rd.ReadInt32(); - header.RiffFormat = rd.ReadChars(4); - - header.FMT = rd.ReadChars(4); - header.FMTSize = (uint)rd.ReadInt32(); - header.FMTPos = fs.Position; - header.AudioFormat = rd.ReadInt16(); - header.Channels = rd.ReadInt16(); - header.SamplesPerSecond = (uint)rd.ReadInt32(); - header.BytesPerSecond = (uint)rd.ReadInt32(); - header.BlockAlign = rd.ReadInt16(); - header.BitsPerSample = rd.ReadInt16(); - - fs.Seek(header.FMTPos + header.FMTSize, SeekOrigin.Begin); - - header.DATA = rd.ReadChars(4); - header.DATASize = (uint)rd.ReadInt32(); - header.DATAPos = (int)fs.Position; - - if (new string(header.DATA).ToUpper() != "DATA") - { - uint DataChunkSize = header.DATASize + 8; - fs.Seek(DataChunkSize, SeekOrigin.Current); - header.DATASize = (uint)(fs.Length - header.DATAPos - DataChunkSize); - } - - if (header.DATASize <= fs.Length - header.DATAPos) - { - header.Payload = rd.ReadBytes((int)header.DATASize); - } - } - - rd.Close(); - fs.Close(); - } - - return header; - } - } - - internal class WaveFileHeader - { - internal WaveFileHeader() - { - - } - - internal char[] RIFF = new char[4]; - internal uint RiffSize = 8; - internal char[] RiffFormat = new char[4]; - - internal char[] FMT = new char[4]; - internal uint FMTSize = 16; - internal short AudioFormat; - internal short Channels; - internal uint SamplesPerSecond; - internal uint BytesPerSecond; - internal short BlockAlign; - internal short BitsPerSample; - - internal char[] DATA = new char[4]; - internal uint DATASize; - - internal byte[] Payload = Array.Empty(); - - internal int DATAPos = 44; - internal long FMTPos = 20; - } -} diff --git a/Luski.net/Sound/Win32.cs b/Luski.net/Sound/Win32.cs deleted file mode 100755 index 0275eaf..0000000 --- a/Luski.net/Sound/Win32.cs +++ /dev/null @@ -1,241 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace Luski.net.Sound -{ - internal unsafe class Win32 - { - internal Win32() - { - - } - - internal const int WAVE_MAPPER = -1; - - internal const int WT_EXECUTEDEFAULT = 0x00000000; - internal const int WT_EXECUTEINIOTHREAD = 0x00000001; - internal const int WT_EXECUTEINTIMERTHREAD = 0x00000020; - internal const int WT_EXECUTEINPERSISTENTTHREAD = 0x00000080; - - internal const int TIME_ONESHOT = 0; - internal const int TIME_PERIODIC = 1; - - [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Auto)] - internal struct WAVEOUTCAPS - { - internal short wMid; - internal short wPid; - internal int vDriverVersion; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] - internal string szPname; - internal uint dwFormats; - internal short wChannels; - internal short wReserved; - internal int dwSupport; - } - - [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Auto)] - internal struct WAVEINCAPS - { - internal short wMid; - internal short wPid; - internal int vDriverVersion; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] - internal string szPname; - internal uint dwFormats; - internal short wChannels; - internal short wReserved; - internal int dwSupport; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct WAVEFORMATEX - { - internal ushort wFormatTag; - internal ushort nChannels; - internal uint nSamplesPerSec; - internal uint nAvgBytesPerSec; - internal ushort nBlockAlign; - internal ushort wBitsPerSample; - internal ushort cbSize; - } - - internal enum MMRESULT : uint - { - MMSYSERR_NOERROR = 0, - MMSYSERR_ERROR = 1, - MMSYSERR_BADDEVICEID = 2, - MMSYSERR_NOTENABLED = 3, - MMSYSERR_ALLOCATED = 4, - MMSYSERR_INVALHANDLE = 5, - MMSYSERR_NODRIVER = 6, - MMSYSERR_NOMEM = 7, - MMSYSERR_NOTSUPPORTED = 8, - MMSYSERR_BADERRNUM = 9, - MMSYSERR_INVALFLAG = 10, - MMSYSERR_INVALPARAM = 11, - MMSYSERR_HANDLEBUSY = 12, - MMSYSERR_INVALIDALIAS = 13, - MMSYSERR_BADDB = 14, - MMSYSERR_KEYNOTFOUND = 15, - MMSYSERR_READERROR = 16, - MMSYSERR_WRITEERROR = 17, - MMSYSERR_DELETEERROR = 18, - MMSYSERR_VALNOTFOUND = 19, - MMSYSERR_NODRIVERCB = 20, - WAVERR_BADFORMAT = 32, - WAVERR_STILLPLAYING = 33, - WAVERR_UNPREPARED = 34 - } - - [Flags] - internal enum WaveHdrFlags : uint - { - WHDR_DONE = 1, - WHDR_PREPARED = 2, - WHDR_BEGINLOOP = 4, - WHDR_ENDLOOP = 8, - WHDR_INQUEUE = 16 - } - - [Flags] - internal enum WaveProcFlags : int - { - CALLBACK_NULL = 0, - CALLBACK_FUNCTION = 0x30000, - CALLBACK_EVENT = 0x50000, - CALLBACK_WINDOW = 0x10000, - CALLBACK_THREAD = 0x20000, - WAVE_FORMAT_QUERY = 1, - WAVE_MAPPED = 4, - WAVE_FORMAT_DIRECT = 8 - } - - [Flags] - internal enum HRESULT : long - { - S_OK = 0L, - S_FALSE = 1L - } - - [Flags] - internal enum WaveFormatFlags : int - { - WAVE_FORMAT_PCM = 0x0001 - } - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] - internal struct WAVEHDR - { - internal IntPtr lpData; - internal uint dwBufferLength; - internal uint dwBytesRecorded; - internal IntPtr dwUser; - internal WaveHdrFlags dwFlags; - internal uint dwLoops; - internal IntPtr lpNext; - internal IntPtr reserved; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct TimeCaps - { - internal uint wPeriodMin; - internal uint wPeriodMax; - }; - - internal enum WOM_Messages : int - { - OPEN = 0x03BB, - CLOSE = 0x03BC, - DONE = 0x03BD - } - - internal enum WIM_Messages : int - { - OPEN = 0x03BE, - CLOSE = 0x03BF, - DATA = 0x03C0 - } - - internal delegate void DelegateWaveOutProc(IntPtr hWaveOut, WOM_Messages msg, IntPtr dwInstance, WAVEHDR* pWaveHdr, IntPtr lParam); - internal delegate void DelegateWaveInProc(IntPtr hWaveIn, WIM_Messages msg, IntPtr dwInstance, WAVEHDR* pWaveHdr, IntPtr lParam); - internal delegate void DelegateTimerProc(IntPtr lpParameter, bool TimerOrWaitFired); - internal delegate void TimerEventHandler(uint id, uint msg, ref uint userCtx, uint rsv1, uint rsv2); - - [DllImport("winmm.dll", SetLastError = true, EntryPoint = "timeSetEvent")] - internal static extern uint TimeSetEvent(uint msDelay, uint msResolution, TimerEventHandler handler, ref uint userCtx, uint eventType); - - [DllImport("winmm.dll", SetLastError = true, EntryPoint = "timeKillEvent")] - internal static extern uint TimeKillEvent(uint timerId); - - [DllImport("winmm.dll", SetLastError = true, EntryPoint = "timeGetDevCaps")] - internal static extern MMRESULT TimeGetDevCaps(ref TimeCaps timeCaps, uint sizeTimeCaps); - - [DllImport("winmm.dll", SetLastError = true, EntryPoint = "timeBeginPeriod")] - internal static extern MMRESULT TimeBeginPeriod(uint uPeriod); - - [DllImport("winmm.dll", SetLastError = true, EntryPoint = "timeEndPeriod")] - internal static extern MMRESULT TimeEndPeriod(uint uPeriod); - - [DllImport("winmm.dll", SetLastError = true, CharSet = CharSet.Auto)] - internal static extern MMRESULT waveOutOpen(ref IntPtr hWaveOut, int uDeviceID, ref WAVEFORMATEX lpFormat, DelegateWaveOutProc dwCallBack, int dwInstance, int dwFlags); - - [DllImport("winmm.dll")] - internal static extern MMRESULT waveInOpen(ref IntPtr hWaveIn, int deviceId, ref WAVEFORMATEX wfx, DelegateWaveInProc dwCallBack, int dwInstance, int dwFlags); - - [DllImport("winmm.dll", SetLastError = true)] - internal static extern MMRESULT waveInStart(IntPtr hWaveIn); - - [DllImport("winmm.dll", SetLastError = true, CharSet = CharSet.Auto)] - internal static extern uint waveInGetDevCaps(int index, ref WAVEINCAPS pwic, int cbwic); - - [DllImport("winmm.dll", SetLastError = true)] - internal static extern uint waveInGetNumDevs(); - - [DllImport("winmm.dll", SetLastError = true, CharSet = CharSet.Auto)] - internal static extern uint waveOutGetDevCaps(int index, ref WAVEOUTCAPS pwoc, int cbwoc); - - [DllImport("winmm.dll", SetLastError = true)] - internal static extern uint waveOutGetNumDevs(); - - [DllImport("winmm.dll", SetLastError = true, CharSet = CharSet.Auto)] - internal static extern MMRESULT waveOutWrite(IntPtr hWaveOut, WAVEHDR* pwh, int cbwh); - - [DllImport("winmm.dll", SetLastError = true, EntryPoint = "waveOutPrepareHeader", CharSet = CharSet.Auto)] - internal static extern MMRESULT waveOutPrepareHeader(IntPtr hWaveOut, WAVEHDR* lpWaveOutHdr, int uSize); - - [DllImport("winmm.dll", SetLastError = true, EntryPoint = "waveOutUnprepareHeader", CharSet = CharSet.Auto)] - internal static extern MMRESULT waveOutUnprepareHeader(IntPtr hWaveOut, WAVEHDR* lpWaveOutHdr, int uSize); - - [DllImport("winmm.dll", EntryPoint = "waveInStop", SetLastError = true)] - internal static extern MMRESULT waveInStop(IntPtr hWaveIn); - - [DllImport("winmm.dll", EntryPoint = "waveInReset", SetLastError = true)] - internal static extern MMRESULT waveInReset(IntPtr hWaveIn); - - [DllImport("winmm.dll", EntryPoint = "waveOutReset", SetLastError = true)] - internal static extern MMRESULT waveOutReset(IntPtr hWaveOut); - - [DllImport("winmm.dll", SetLastError = true)] - internal static extern MMRESULT waveInPrepareHeader(IntPtr hWaveIn, WAVEHDR* pwh, int cbwh); - - [DllImport("winmm.dll", SetLastError = true)] - internal static extern MMRESULT waveInUnprepareHeader(IntPtr hWaveIn, WAVEHDR* pwh, int cbwh); - - [DllImport("winmm.dll", EntryPoint = "waveInAddBuffer", SetLastError = true)] - internal static extern MMRESULT waveInAddBuffer(IntPtr hWaveIn, WAVEHDR* pwh, int cbwh); - - [DllImport("winmm.dll", SetLastError = true)] - internal static extern MMRESULT waveInClose(IntPtr hWaveIn); - - [DllImport("winmm.dll", SetLastError = true, CharSet = CharSet.Auto)] - internal static extern MMRESULT waveOutClose(IntPtr hWaveOut); - - [DllImport("winmm.dll")] - internal static extern MMRESULT waveOutPause(IntPtr hWaveOut); - - [DllImport("winmm.dll", EntryPoint = "waveOutRestart", SetLastError = true)] - internal static extern MMRESULT waveOutRestart(IntPtr hWaveOut); - } -} diff --git a/Luski.net/Sound/WinSound.cs b/Luski.net/Sound/WinSound.cs deleted file mode 100755 index c5d48ce..0000000 --- a/Luski.net/Sound/WinSound.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System.Collections.Generic; -using System.Runtime.InteropServices; - -namespace Luski.net.Sound -{ - internal class LockerClass - { - - } - - internal class WinSound - { - internal WinSound() - { - - } - - internal static List GetPlaybackNames() - { - List list = new(); - Win32.WAVEOUTCAPS waveOutCap = new(); - - uint num = Win32.waveOutGetNumDevs(); - for (int i = 0; i < num; i++) - { - uint hr = Win32.waveOutGetDevCaps(i, ref waveOutCap, Marshal.SizeOf(typeof(Win32.WAVEOUTCAPS))); - if (hr == (int)Win32.HRESULT.S_OK) - { - list.Add(waveOutCap.szPname); - } - } - - return list; - } - - internal static List GetRecordingNames() - { - List list = new(); - Win32.WAVEINCAPS waveInCap = new(); - - uint num = Win32.waveInGetNumDevs(); - for (int i = 0; i < num; i++) - { - uint hr = Win32.waveInGetDevCaps(i, ref waveInCap, Marshal.SizeOf(typeof(Win32.WAVEINCAPS))); - if (hr == (int)Win32.HRESULT.S_OK) - { - list.Add(waveInCap.szPname); - } - } - - return list; - } - - internal static int GetWaveInDeviceIdByName(string name) - { - uint num = Win32.waveInGetNumDevs(); - - Win32.WAVEINCAPS caps = new(); - for (int i = 0; i < num; i++) - { - Win32.HRESULT hr = (Win32.HRESULT)Win32.waveInGetDevCaps(i, ref caps, Marshal.SizeOf(typeof(Win32.WAVEINCAPS))); - if (hr == Win32.HRESULT.S_OK) - { - if (caps.szPname == name) - { - return i; - } - } - } - - return Win32.WAVE_MAPPER; - } - - internal static int GetWaveOutDeviceIdByName(string name) - { - uint num = Win32.waveOutGetNumDevs(); - - Win32.WAVEOUTCAPS caps = new(); - for (int i = 0; i < num; i++) - { - Win32.HRESULT hr = (Win32.HRESULT)Win32.waveOutGetDevCaps(i, ref caps, Marshal.SizeOf(typeof(Win32.WAVEOUTCAPS))); - if (hr == Win32.HRESULT.S_OK) - { - if (caps.szPname == name) - { - return i; - } - } - } - - return Win32.WAVE_MAPPER; - } - } -} diff --git a/Luski.net/JsonTypes/File.cs b/Luski.net/Structures/File.cs similarity index 76% rename from Luski.net/JsonTypes/File.cs rename to Luski.net/Structures/File.cs index 901cd14..8c3e76f 100755 --- a/Luski.net/JsonTypes/File.cs +++ b/Luski.net/Structures/File.cs @@ -9,12 +9,16 @@ using System.Net.Http; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; +using System.Threading; using System.Threading.Tasks; +using JacobTechEncryption; +using Luski.net.Interfaces; -namespace Luski.net.JsonTypes; +namespace Luski.net.Structures; public class File : IncomingHTTP { + public IServer Server { get; internal set; } [JsonInclude] [JsonPropertyName("name")] public string Name { get; internal set; } = default!; @@ -29,7 +33,7 @@ public class File : IncomingHTTP [JsonIgnore] internal string loc { get; set; } = default!; - public async void DownloadBytes(string Loc, long key) + public async Task DownloadBytes(string Loc, long key, CancellationToken CancellationToken) { //using HttpClient web = new(); //web.DefaultRequestHeaders.Add("token", Server.Token); @@ -37,9 +41,9 @@ public class File : IncomingHTTP //IncomingHTTP? request = JsonSerializer.Deserialize(web.GetAsync($"https://{Server.Domain}/{Server.API_Ver}/SocketMessage/GetFile").Result.Content.ReadAsStringAsync().Result, IncomingHTTPContext.Default.IncomingHTTP); string path = Path.GetTempFileName(); - await Server.GetFromServer($"SocketMessage/GetFile/{Id}", path); - string Key = (key == 0 ? Encryption.MyPublicKey : Encryption.File.Channels.GetKey(key))!; - Encryption.AES.Decrypt(System.IO.File.ReadAllBytes(path), Key, Loc); + await Server.GetFromServer($"SocketMessage/GetFile/{Id}", path, CancellationToken); + string Key = (key == 0 ? ClientEncryption.MyPublicKey : ClientEncryption.File.Channels.GetKey(key))!; + Encryption.AES.DecryptToFile(System.IO.File.ReadAllBytes(path), Key, Loc); /* if (request is not null && request.Error is not null) { @@ -78,25 +82,29 @@ public class File : IncomingHTTP loc = path; } - internal async Task Upload(string keyy) + private bool Uploaded = false; + + internal async Task Upload(string keyy, CancellationToken CancellationToken) { - if (Name != null) Name = Convert.ToBase64String(Encryption.Encrypt(Name, keyy)); - Debug.WriteLine("uploading"); - string NPath = Encryption.AES.Encrypt(loc, keyy); - File sf = await Server.SendServer( + if (Uploaded) return Id; + if (Name != null) Name = Convert.ToBase64String(Encryption.RSA.Encrypt(Encoding.UTF8.GetBytes(Name), keyy)); + string NPath = Encryption.AES.EncryptFile(loc, keyy); + File sf = await Server.SendServer( "SocketMessage/UploadFile", NPath, FileContext.Default.File, + CancellationToken, new KeyValuePair("name", Name)); try { System.IO.File.Delete(NPath); } catch { } - Debug.WriteLine("done uploading"); if (sf.Error is not null) throw new Exception(sf.ErrorMessage); + Id = sf.Id; + Uploaded = true; return sf.Id; } internal void decrypt() { - if (Name is not null) Name = Encryption.Encoder.GetString(Encryption.Decrypt(Convert.FromBase64String(Name), key)); + if (Name is not null) Name = Encoding.UTF8.GetString(Encryption.RSA.Decrypt(Convert.FromBase64String(Name), key)); } } diff --git a/Luski.net/Structures/Main/MainSocketAppUser.cs b/Luski.net/Structures/Main/MainSocketAppUser.cs new file mode 100755 index 0000000..7d3dc8d --- /dev/null +++ b/Luski.net/Structures/Main/MainSocketAppUser.cs @@ -0,0 +1,194 @@ +using System; +using Luski.net.Interfaces; +using Luski.net.JsonTypes.BaseTypes; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Metadata; +using System.Threading; +using System.Threading.Tasks; +using JacobTechEncryption; +using Luski.net.Enums; +using Luski.net.Enums.Main; +using Luski.net.JsonTypes; + +namespace Luski.net.Structures.Main; + +public class MainSocketAppUser : SocketUserBase, IAppUser +{ + [JsonPropertyName("username")] + [JsonInclude] + public string Username { get; internal set; } = default!; + [JsonPropertyName("flags")] + [JsonInclude] + public UserFlag Flags { get; internal set; } = default!; + + [JsonIgnore] + public IReadOnlyList Channels + { + get + { + if (_Channels is null || ChannelIdList is not null) + { + if (ChannelIdList.Length != 0) + { + _Channels = new List(); + foreach (long channel in ChannelIdList) + { + MainSocketChannel s = (Server as Server).GetChannel(channel, + MainSocketChannelContext.Default.MainSocketChannel, CancellationToken.None).Result; + (Server as Server)!.chans.Remove(s); + switch (s.Type) + { + case ChannelType.GROUP: + _Channels.Add((Server as Server).GetChannel(channel, + MainSocketGroupChannelContext.Default.MainSocketGroupChannel, CancellationToken.None).Result); + break; + case ChannelType.DM: + _Channels.Add((Server as Server).GetChannel(channel, + MainSocketDMChannelContext.Default.MainSocketDMChannel, CancellationToken.None).Result); + break; + } + } + } + else _Channels = new List(); + } + return _Channels.AsReadOnly(); + } + } + + [JsonIgnore] + public IReadOnlyList FriendRequests + { + get + { + if (_FriendRequests is null || FriendRequestsRaw is not null) + { + _FriendRequests = new(); + if (ChannelIdList.Length != 0 && FriendRequestsRaw is not null) + { + foreach (FR person in FriendRequestsRaw) + { + //_Friends.Add(SocketRemoteUser.GetUser(person)); + long id = person.user_id == Id ? person.from : person.user_id; + MainSocketRemoteUser frq = Server.GetUser(id, CancellationToken.None).Result; + _FriendRequests.Add(frq); + } + } + else _FriendRequests = new(); + } + return _FriendRequests.AsReadOnly(); + } + } + [JsonIgnore] + public IReadOnlyList Friends + { + get + { + if (_Friends is null || FriendIdList is not null) + { + if (ChannelIdList.Length != 0) + { + _Friends = new List(); + foreach (long person in FriendIdList) + { + _Friends.Add(Server.GetUser(person, CancellationToken.None).Result); + } + } + else _Friends = new List(); + } + return _Friends.AsReadOnly(); + } + } + [JsonPropertyName("selected_channel")] + [JsonInclude] + public long SelectedChannel { get; internal set; } = default!; + [JsonPropertyName("channels")] + [JsonInclude] + public long[] ChannelIdList { get; internal set; } = default!; + [JsonPropertyName("friends")] + [JsonInclude] + public long[] FriendIdList { get; internal set; } = default!; + [JsonPropertyName("friend_requests")] + [JsonInclude] + public FR[] FriendRequestsRaw { get; internal set; } = default!; + [JsonIgnore] + private List _Channels = default!; + [JsonIgnore] + private List _Friends = default!; + [JsonIgnore] + private List _FriendRequests = default!; + + public class FR + { + public long from { get; set; } = default!; + public long user_id { get; set; } = default!; + } + + internal void AddFriend(MainSocketRemoteUser User) + { + if ((Server as Server)!.poeople.Any(s => s.Id == User.Id)) + { + IEnumerable b = (Server as Server)!.poeople.Where(s => s.Id == User.Id); + foreach (IUser item in b) + { + (Server as Server)!.poeople.Remove(item); + } + (Server as Server)!.poeople.Add(User); + } + else + { + (Server as Server)!.poeople.Add(User); + } + _Friends.Add(User); + } + + internal void RemoveFriendRequest(MainSocketRemoteUser User) + { + if ((Server as Server)!.poeople.Any(s => s.Id == User.Id)) + { + IEnumerable b = (Server as Server)!.poeople.Where(s => s.Id == User.Id); + foreach (IUser item in b) + { + (Server as Server)!.poeople.Remove(item); + } + } + (Server as Server)!.poeople.Add(User); + foreach (MainSocketRemoteUser user in _FriendRequests) + { + if (User.Id == user.Id) + { + _FriendRequests.Remove(User); + } + } + } + + internal void AddFriendRequest(MainSocketRemoteUser User) + { + if ((Server as Server)!.poeople.Any(s => s.Id == User.Id)) + { + IEnumerable b = (Server as Server)!.poeople.Where(s => s.Id == User.Id); + foreach (IUser item in b) + { + (Server as Server)!.poeople.Remove(item); + } + (Server as Server)!.poeople.Add(User); + } + else + { + (Server as Server)!.poeople.Add(User); + } + _FriendRequests.Add(User); + } +} + +[JsonSerializable(typeof(MainSocketAppUser))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.Unspecified, + WriteIndented = false, + DefaultIgnoreCondition = JsonIgnoreCondition.Never)] +internal partial class MainSocketAppUserContext : JsonSerializerContext +{ + +} diff --git a/Luski.net/JsonTypes/SocketChannel.cs b/Luski.net/Structures/Main/MainSocketChannel.cs similarity index 51% rename from Luski.net/JsonTypes/SocketChannel.cs rename to Luski.net/Structures/Main/MainSocketChannel.cs index 95af482..a46b6e1 100755 --- a/Luski.net/JsonTypes/SocketChannel.cs +++ b/Luski.net/Structures/Main/MainSocketChannel.cs @@ -9,10 +9,15 @@ using System.Net.Http; using System.Threading.Tasks; using Luski.net.JsonTypes.WSS; using System.Text.Json.Serialization.Metadata; +using System.Threading; +using JacobTechEncryption; +using JacobTechEncryption.Enums; +using Luski.net.Enums.Main; +using Luski.net.JsonTypes; -namespace Luski.net.JsonTypes; +namespace Luski.net.Structures.Main; -public class SocketChannel : IncomingHTTP +public class MainSocketChannel : IncomingHTTP { [JsonInclude] [JsonPropertyName("id")] @@ -29,6 +34,7 @@ public class SocketChannel : IncomingHTTP [JsonPropertyName("type")] [JsonInclude] public ChannelType Type { get; internal set; } = default!; + public IServer Server { get; internal set; } [JsonPropertyName("members")] [JsonInclude] public long[] MemberIdList { get; internal set; } = default!; @@ -43,8 +49,8 @@ public class SocketChannel : IncomingHTTP _members = new(); foreach (long member in MemberIdList) { - if (member != Server._user!.Id) _members.Add(SocketUserBase.GetUser(member, SocketRemoteUserContext.Default.SocketRemoteUser).Result); - else _members.Add(Server._user); + if (member != Server.IAppUser!.Id) _members.Add(Server.GetUser(member, CancellationToken.None).Result); + else _members.Add(Server.IAppUser); } } return _members.AsReadOnly(); @@ -53,31 +59,31 @@ public class SocketChannel : IncomingHTTP [JsonIgnore] private List _members = new(); - public async Task SendKeysToUsers() + public Task SendKeysToUsers(CancellationToken CancellationToken) { if (Key is null) { - StartKeyProcessAsync().Wait(); + StartKeyProcessAsync(CancellationToken).Wait(); return Task.CompletedTask; } - int num = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * Server.Percent) * 2.0)); + int num = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * 50) * 2.0)); if (num == 0) num = 1; - string? lkey = Encryption.File.Channels.GetKey(Id); + string? lkey = ClientEncryption.File.Channels.GetKey(Id); Parallel.ForEach(Members, new ParallelOptions() { MaxDegreeOfParallelism = num - }, async i => + }, i => { - if (i.Id != Server._user?.Id) + if (i.Id != (Server as Server).User?.Id) { - string key = await i.GetUserKey(); - if (!string.IsNullOrEmpty(key)) + long key = i.GetUserKey(CancellationToken).Result; + if (true) { WSSKeyExchange send = new() { to = i.Id, channel = Id, - key = Convert.ToBase64String(Encryption.Encrypt(lkey, key)) + key = Convert.ToBase64String(Encryption.RSA.Encrypt(lkey, key.ToString(), EncoderType.UTF8)) }; Server.SendServer(send, WSSKeyExchangeContext.Default.WSSKeyExchange); } @@ -86,17 +92,17 @@ public class SocketChannel : IncomingHTTP return Task.CompletedTask; } - internal async Task StartKeyProcessAsync() - { - Encryption.GenerateNewKeys(out string Public, out string Private); + internal Task StartKeyProcessAsync(CancellationToken CancellationToken) + {/* + ClientEncryption.GenerateNewKeys(out string Public, out string Private); Key = Public; HttpResponseMessage b; using (HttpClient web = new()) { - web.DefaultRequestHeaders.Add("token", Server.Token); - b = web.PostAsync($"https://{Server.InternalDomain}/{Server.API_Ver}/SocketChannel/SetKey/{Id}", new StringContent(Key)).Result; + web.DefaultRequestHeaders.Add("token", Luski.net.Server.Token); + b = web.PostAsync($"https://{Server.Domain}/{Server.ApiVersion}/SocketChannel/SetKey/{Id}", new StringContent(Key), CancellationToken).Result; } - int num = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * Server.Percent) * 2.0)); + int num = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * Luski.net.Server.Percent) * 2.0)); if (num == 0) num = 1; Encryption.File.Channels.AddKey(Id, Private); Parallel.ForEach(Members, new ParallelOptions() @@ -104,9 +110,9 @@ public class SocketChannel : IncomingHTTP MaxDegreeOfParallelism = num }, i => { - if (i.Id != Server._user?.Id) + if (i.Id != Luski.net.Server._user?.Id) { - string key = i.GetUserKey().Result; + string key = i.GetUserKey(CancellationToken, Server).Result; if (!string.IsNullOrEmpty(key)) { WSSKeyExchange send = new() @@ -115,59 +121,20 @@ public class SocketChannel : IncomingHTTP channel = Id, key = Convert.ToBase64String(Encryption.Encrypt(Private, key)) }; - Server.SendServer(send, WSSKeyExchangeContext.Default.WSSKeyExchange); + Luski.net.Server.SendServer(send, WSSKeyExchangeContext.Default.WSSKeyExchange); } } - }); - } - - internal static async Task GetChannel(long id, JsonTypeInfo Json) where TChannel : SocketChannel, new() - { - TChannel request; - if (Server.chans is null) Server.chans = new(); - if (Server.chans.Count > 0 && Server.chans.Any(s => s.Id == id)) - { - return Server.chans.Where(s => s is TChannel && s.Id == id).Cast().FirstOrDefault()!; - } - while (true) - { - if (Server.CanRequest) - { - request = await Server.GetFromServer($"SocketChannel/Get/{id}", Json); - break; - } - } - if (request is null) throw new Exception("Something was wrong with the server responce"); - if (request.Error is null) - { - if (Server.chans is null) Server.chans = new(); - if (Server.chans.Count > 0 && Server.chans.Any(s => s.Id == request.Id)) - { - foreach (SocketChannel? p in Server.chans.Where(s => s.Id == request.Id)) - { - Server.chans.Remove(p); - } - } - Server.chans.Add(request); - return request; - } - throw request.Error switch - { - ErrorCode.InvalidToken => new Exception("Your current token is no longer valid"), - ErrorCode.Forbidden => new Exception("The server rejected your request"), - ErrorCode.ServerError => new Exception("Error from server: " + request.ErrorMessage), - ErrorCode.InvalidURL or ErrorCode.MissingHeader => new Exception(request.ErrorMessage), - _ => new Exception($"Unknown data: '{request.ErrorMessage}'"), - }; + });*/ + return Task.CompletedTask; } } -[JsonSerializable(typeof(SocketChannel))] +[JsonSerializable(typeof(MainSocketChannel))] [JsonSourceGenerationOptions( GenerationMode = JsonSourceGenerationMode.Default, PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, WriteIndented = false)] -internal partial class SocketChannelContext : JsonSerializerContext +internal partial class MainSocketChannelContext : JsonSerializerContext { } \ No newline at end of file diff --git a/Luski.net/Structures/Main/MainSocketDMChannel.cs b/Luski.net/Structures/Main/MainSocketDMChannel.cs new file mode 100755 index 0000000..c330a3d --- /dev/null +++ b/Luski.net/Structures/Main/MainSocketDMChannel.cs @@ -0,0 +1,34 @@ +using Luski.net.JsonTypes.BaseTypes; +using System.Linq; +using System.Text.Json.Serialization; +using System.Threading; + +namespace Luski.net.Structures.Main; + +public class MainSocketDMChannel : MainSocketTextChannel +{ + public MainSocketRemoteUser User + { + get + { + if (_user is null) + { + var list = MemberIdList.ToList(); + list.Remove(Server.IAppUser.Id); + _user = Server.GetUser(list.FirstOrDefault(), CancellationToken.None).Result; + } + return _user; + } + } + public MainSocketRemoteUser _user = null!; +} + +[JsonSerializable(typeof(MainSocketDMChannel))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + WriteIndented = false)] +internal partial class MainSocketDMChannelContext : JsonSerializerContext +{ + +} diff --git a/Luski.net/JsonTypes/SocketGroupChannel.cs b/Luski.net/Structures/Main/MainSocketGroupChannel.cs similarity index 59% rename from Luski.net/JsonTypes/SocketGroupChannel.cs rename to Luski.net/Structures/Main/MainSocketGroupChannel.cs index f7a3300..1e865d2 100755 --- a/Luski.net/JsonTypes/SocketGroupChannel.cs +++ b/Luski.net/Structures/Main/MainSocketGroupChannel.cs @@ -1,20 +1,20 @@ using System.Text.Json.Serialization; -namespace Luski.net.JsonTypes; +namespace Luski.net.Structures.Main; -public class SocketGroupChannel : SocketTextChannel +public class MainSocketGroupChannel : MainSocketTextChannel { [JsonPropertyName("owner")] [JsonInclude] public long Owner { get; internal set; } = default!; } -[JsonSerializable(typeof(SocketGroupChannel))] +[JsonSerializable(typeof(MainSocketGroupChannel))] [JsonSourceGenerationOptions( GenerationMode = JsonSourceGenerationMode.Default, PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, WriteIndented = false)] -internal partial class SocketGroupChannelContext : JsonSerializerContext +internal partial class MainSocketGroupChannelContext : JsonSerializerContext { } diff --git a/Luski.net/JsonTypes/SocketRemoteUser.cs b/Luski.net/Structures/Main/MainSocketRemoteUser.cs similarity index 60% rename from Luski.net/JsonTypes/SocketRemoteUser.cs rename to Luski.net/Structures/Main/MainSocketRemoteUser.cs index 11277b4..9ac8862 100755 --- a/Luski.net/JsonTypes/SocketRemoteUser.cs +++ b/Luski.net/Structures/Main/MainSocketRemoteUser.cs @@ -7,29 +7,29 @@ using System.Net.Http; using System.Text.Json; using System.Text.Json.Serialization; using System.Threading.Tasks; +using Luski.net.Enums.Main; +using Luski.net.Structures; -namespace Luski.net.JsonTypes; +namespace Luski.net.Structures; -public class SocketRemoteUser : SocketUserBase +public class MainSocketRemoteUser : SocketUserBase { [JsonPropertyName("friend_status")] [JsonInclude] public FriendStatus FriendStatus { get; internal set; } = default!; - [JsonIgnore] - public SocketDMChannel Channel { get; internal set; } = default!; - internal SocketRemoteUser Clone() + internal MainSocketRemoteUser Clone() { - return (SocketRemoteUser)MemberwiseClone(); + return (MainSocketRemoteUser)MemberwiseClone(); } } -[JsonSerializable(typeof(SocketRemoteUser))] +[JsonSerializable(typeof(MainSocketRemoteUser))] [JsonSourceGenerationOptions( GenerationMode = JsonSourceGenerationMode.Default, PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, WriteIndented = false)] -internal partial class SocketRemoteUserContext : JsonSerializerContext +internal partial class MainSocketRemoteUserContext : JsonSerializerContext { } diff --git a/Luski.net/JsonTypes/SocketTextChannel.cs b/Luski.net/Structures/Main/MainSocketTextChannel.cs similarity index 71% rename from Luski.net/JsonTypes/SocketTextChannel.cs rename to Luski.net/Structures/Main/MainSocketTextChannel.cs index 5b61339..ce83d2e 100755 --- a/Luski.net/JsonTypes/SocketTextChannel.cs +++ b/Luski.net/Structures/Main/MainSocketTextChannel.cs @@ -7,32 +7,36 @@ using System.Diagnostics; using System.IO; using System.Linq; using System.Text.Json.Serialization; +using System.Threading; using System.Threading.Tasks; +using JacobTechEncryption; +using JacobTechEncryption.Enums; +using Luski.net.Enums.Main; -namespace Luski.net.JsonTypes; +namespace Luski.net.Structures.Main; -public class SocketTextChannel : SocketChannel, ITextChannel +public class MainSocketTextChannel : MainSocketChannel { - public async Task GetMessage(long ID) + public async Task GetMessage(long ID, CancellationToken CancellationToken) { - return await SocketMessage.GetMessage(ID); + return await (Server as Server)!.GetMessage(ID, CancellationToken); } - public async Task GetPicture() + public async Task GetPicture(CancellationToken CancellationToken) { - if (Type == ChannelType.DM) return Members.First().GetAvatar().Result; + if (Type == ChannelType.DM) return Members.First().GetAvatar(CancellationToken).Result; else { if (Server.Cache != null) { bool isc = System.IO.File.Exists($"{Server.Cache}/channels/{Id}"); - if (!isc) await Server.GetFromServer($"SocketChannel/GetPicture/{Id}", $"{Server.Cache}/channels/{Id}"); + if (!isc) await Server.GetFromServer($"SocketChannel/GetPicture/{Id}", $"{Server.Cache}/channels/{Id}", CancellationToken); } return System.IO.File.ReadAllBytes($"{Server.Cache}/channels/{Id}"); } } - public async Task> GetMessages(long Message_Id, int count = 50) + public async Task> GetMessages(long Message_Id, CancellationToken CancellationToken, int count = 50) { if (count > 200) { @@ -46,15 +50,16 @@ public class SocketTextChannel : SocketChannel, ITextChannel { SocketBulkMessage data = await Server.GetFromServer("SocketBulkMessage", SocketBulkMessageContext.Default.SocketBulkMessage, + CancellationToken, new KeyValuePair("channel_id", Id.ToString()), new KeyValuePair("messages", count.ToString()), new KeyValuePair("mostrecentid", Message_Id.ToString())); if (data.Error is null) { - int num = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * Server.Percent) * 2.0)); + int num = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * 5) * 2.0)); if (num == 0) num = 1; - string? key = Encryption.File.Channels.GetKey(Id); + string? key = ClientEncryption.File.Channels.GetKey(Id); if (data is null) throw new Exception("Invalid data from server"); if (data.Messages is null) data.Messages = Array.Empty(); Parallel.ForEach(data.Messages, new ParallelOptions() @@ -62,7 +67,7 @@ public class SocketTextChannel : SocketChannel, ITextChannel MaxDegreeOfParallelism = num }, i => { - i.decrypt(key); + i.decrypt(key, CancellationToken); }); key = null; return await Task.FromResult(data.Messages.ToList().AsReadOnly() as IReadOnlyList); @@ -74,7 +79,7 @@ public class SocketTextChannel : SocketChannel, ITextChannel } } - public async Task> GetMessages(int count = 50) + public async Task> GetMessages(CancellationToken CancellationToken, int count = 50) { try { @@ -88,24 +93,29 @@ public class SocketTextChannel : SocketChannel, ITextChannel } else { + DateTime start = DateTime.Now; SocketBulkMessage data = await Server.GetFromServer("SocketBulkMessage", SocketBulkMessageContext.Default.SocketBulkMessage, + CancellationToken, new KeyValuePair("id", Id.ToString()), new KeyValuePair("messages", count.ToString())); + Console.WriteLine($"Messages downloaded in {(DateTime.Now - start).TotalSeconds}"); + start = DateTime.Now; if (data is not null && !data.Error.HasValue) { - int num = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * Server.Percent) * 2.0)); + int num = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * 5) * 2.0)); if (num == 0) num = 1; - string? key = Encryption.File.Channels.GetKey(Id); + string? key = ClientEncryption.File.Channels.GetKey(Id); if (data.Messages is null) data.Messages = Array.Empty(); Parallel.ForEach(data.Messages, new ParallelOptions() { MaxDegreeOfParallelism = num }, i => { - i.decrypt(key); + i.decrypt(key, CancellationToken); }); key = null; + Console.WriteLine($"Messages decrypted in {(DateTime.Now - start).TotalSeconds}"); return await Task.FromResult(data.Messages.ToList().AsReadOnly() as IReadOnlyList); } else @@ -128,13 +138,13 @@ public class SocketTextChannel : SocketChannel, ITextChannel } } - public async Task SendMessage(string Message, params File?[] Files) + public async Task SendMessage(string Message, CancellationToken CancellationToken, params File?[] Files) { - string key = Encryption.File.Channels.GetKey(Id); - if (Id == 0) key = Encryption.ServerPublicKey; - HTTP.Message m = new() + string key = ClientEncryption.File.Channels.GetKey(Id); + if (Id == 0) key = "";//ClientEncryption.ServerPublicKey; + JsonTypes.HTTP.Message m = new() { - Context = Convert.ToBase64String(Encryption.Encrypt(Message, key)), + Context = Convert.ToBase64String(Encryption.RSA.Encrypt(Message, key, EncoderType.UTF8)), Channel = Id, }; if (Files is not null && Files.Length > 0) @@ -145,24 +155,24 @@ public class SocketTextChannel : SocketChannel, ITextChannel File? ff = Files[i]; if (ff is not null) { - bb.Add(await ff.Upload(key)); + bb.Add(await ff.Upload(key, CancellationToken)); Files[i] = null; } } m.Files = bb.ToArray(); } - IncomingHTTP data = await Server.SendServer("socketmessage", m, HTTP.MessageContext.Default.Message, IncomingHTTPContext.Default.IncomingHTTP); + IncomingHTTP data = await Server.SendServer("socketmessage", m, net.JsonTypes.HTTP.MessageContext.Default.Message, IncomingHTTPContext.Default.IncomingHTTP, CancellationToken); if (data.Error is not null && data.ErrorMessage != "Server responded with empty data") throw new Exception(data.ErrorMessage); return Task.CompletedTask; } } -[JsonSerializable(typeof(SocketTextChannel))] +[JsonSerializable(typeof(MainSocketTextChannel))] [JsonSourceGenerationOptions( GenerationMode = JsonSourceGenerationMode.Default, PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, WriteIndented = false)] -internal partial class SocketTextChannelContext : JsonSerializerContext +internal partial class MainSocketTextChannelContext : JsonSerializerContext { } diff --git a/Luski.net/Structures/Public/PublicSocketAppUser.cs b/Luski.net/Structures/Public/PublicSocketAppUser.cs new file mode 100755 index 0000000..0b7250b --- /dev/null +++ b/Luski.net/Structures/Public/PublicSocketAppUser.cs @@ -0,0 +1,35 @@ +using System; +using Luski.net.Interfaces; +using Luski.net.JsonTypes.BaseTypes; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Metadata; +using System.Threading; +using System.Threading.Tasks; +using JacobTechEncryption; +using Luski.net.Enums; +using Luski.net.Enums.Main; + +namespace Luski.net.Structures.Public; + +public class PublicSocketAppUser : SocketUserBase, IAppUser +{ + [JsonPropertyName("selected_channel")] + [JsonInclude] + public long SelectedChannel { get; internal set; } = default!; + [JsonPropertyName("username")] + [JsonInclude] + public string Username { get; internal set; } = default!; +} + +[JsonSerializable(typeof(PublicSocketAppUser))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.Unspecified, + WriteIndented = false, + DefaultIgnoreCondition = JsonIgnoreCondition.Never)] +internal partial class PublicSocketAppUserContext : JsonSerializerContext +{ + +} diff --git a/Luski.net/JsonTypes/SocketBulkMessage.cs b/Luski.net/Structures/SocketBulkMessage.cs similarity index 89% rename from Luski.net/JsonTypes/SocketBulkMessage.cs rename to Luski.net/Structures/SocketBulkMessage.cs index 7ca8518..3ec44d4 100755 --- a/Luski.net/JsonTypes/SocketBulkMessage.cs +++ b/Luski.net/Structures/SocketBulkMessage.cs @@ -1,7 +1,7 @@ using Luski.net.JsonTypes.BaseTypes; using System.Text.Json.Serialization; -namespace Luski.net.JsonTypes; +namespace Luski.net.Structures; internal class SocketBulkMessage : IncomingHTTP { @@ -11,7 +11,6 @@ internal class SocketBulkMessage : IncomingHTTP } [JsonSerializable(typeof(SocketBulkMessage))] -[JsonSerializable(typeof(SocketAppUser))] [JsonSourceGenerationOptions( GenerationMode = JsonSourceGenerationMode.Default, PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, diff --git a/Luski.net/JsonTypes/SocketMessage.cs b/Luski.net/Structures/SocketMessage.cs similarity index 50% rename from Luski.net/JsonTypes/SocketMessage.cs rename to Luski.net/Structures/SocketMessage.cs index aa4c948..5e41bda 100755 --- a/Luski.net/JsonTypes/SocketMessage.cs +++ b/Luski.net/Structures/SocketMessage.cs @@ -3,14 +3,18 @@ using Luski.net.JsonTypes.BaseTypes; using System; using System.Linq; using System.Net.Http; +using System.Text; using System.Text.Json; using System.Text.Json.Serialization; +using System.Threading; using System.Threading.Tasks; +using JacobTechEncryption; -namespace Luski.net.JsonTypes; +namespace Luski.net.Structures; public class SocketMessage : IncomingHTTP { + public IServer Server { get; } [JsonPropertyName("id")] [JsonInclude] public long Id { get; internal set; } = default!; @@ -26,29 +30,16 @@ public class SocketMessage : IncomingHTTP [JsonPropertyName("files")] [JsonInclude] public File[]? Files { get; internal set; } = default!; - public async Task GetChannel() + public async Task GetAuthor(CancellationToken CancellationToken) { - if (Server.chans.Any(s => s.Id == ChannelID)) - { - return (SocketTextChannel)Server.chans.Where(s => s.Id == ChannelID).First(); - } - else - { - SocketTextChannel ch = await SocketChannel.GetChannel(ChannelID, SocketTextChannelContext.Default.SocketTextChannel); - Server.chans.Add(ch); - return ch; - } - } - public async Task GetAuthor() - { - if (Server._user!.Id != AuthorID) return await SocketUserBase.GetUser(AuthorID, SocketRemoteUserContext.Default.SocketRemoteUser); - else return Server._user; + if (Server.IAppUser!.Id != AuthorID) return await Server.GetUser(AuthorID, CancellationToken); + else return Server.IAppUser; } - internal void decrypt(string? key) + internal void decrypt(string? key, CancellationToken CancellationToken) { if (string.IsNullOrEmpty(key)) throw new ArgumentNullException(nameof(key)); - Context = Encryption.Encoder.GetString(Encryption.Decrypt(Convert.FromBase64String(Context), key)); + Context = Encoding.UTF8.GetString(Encryption.AES.Decrypt(Convert.FromBase64String(Context), key)); if (Files is not null && Files.Length > 0) { for (int i = 0; i < Files.Length; i++) @@ -58,22 +49,6 @@ public class SocketMessage : IncomingHTTP } } } - internal static async Task GetMessage(long id) - { - SocketMessage message; - while (true) - { - if (Server.CanRequest) - { - message = await Server.GetFromServer("socketmessage", - SocketMessageContext.Default.SocketMessage, - new System.Collections.Generic.KeyValuePair("msg_id", id.ToString())); - break; - } - } - if (message is not null) return message; - throw new Exception("Server did not return a message"); - } } [JsonSerializable(typeof(SocketMessage))] diff --git a/Luski.net/Structures/SocketUserBase.cs b/Luski.net/Structures/SocketUserBase.cs new file mode 100644 index 0000000..f29c9ec --- /dev/null +++ b/Luski.net/Structures/SocketUserBase.cs @@ -0,0 +1,53 @@ +using System.Text.Json.Serialization; +using System.Threading; +using System.Threading.Tasks; +using Luski.net.Enums; +using Luski.net.Interfaces; +using Luski.net.JsonTypes.BaseTypes; + +namespace Luski.net.Structures; + +public class SocketUserBase : IncomingHTTP, IUser +{ + [JsonPropertyName("id")] + [JsonInclude] + public long Id { get; internal set; } = default!; + [JsonPropertyName("username")] + [JsonInclude] + public string DisplayName { get; internal set; } = default!; + [JsonPropertyName("status")] + [JsonInclude] + public UserStatus Status { get; internal set; } = default!; + [JsonPropertyName("picture_type")] + [JsonInclude] + public PictureType PictureType { get; internal set; } = default!; + [JsonIgnore] + public IServer Server { get; internal set; } = default!; + + public async Task GetAvatar(CancellationToken CancellationToken) + { + if (Server.Cache != null) + { + bool isc = System.IO.File.Exists($"{Server.Cache}/avatars/{Id}"); + if (!isc) await Server.GetFromServer($"socketuserprofile/Avatar/{Id}", $"{Server.Cache}/avatars/{Id}", CancellationToken); + } + return System.IO.File.ReadAllBytes($"{Server.Cache}/avatars/{Id}"); + } + + public Task GetUserKey(CancellationToken CancellationToken) + { + string data = Server.GetFromServer($"Keys/GetUserKey/{Id}", CancellationToken).Content.ReadAsStringAsync().Result; + return Task.FromResult(long.Parse(data)); + } +} + +[JsonSerializable(typeof(SocketUserBase))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.Unspecified, + WriteIndented = false, + DefaultIgnoreCondition = JsonIgnoreCondition.Never)] +internal partial class SocketUserBaseContext : JsonSerializerContext +{ + +} \ No newline at end of file From d8fbf281d01bb8cd869f6fe44c0b06a9170d1fc3 Mon Sep 17 00:00:00 2001 From: JacobTech Date: Sat, 8 Jul 2023 09:06:13 -0400 Subject: [PATCH 03/14] Server Separation. I moved the two server types to their own classes to prevent API calls to a server, not of that type. --- Luski.net/API.cs | 14 +- Luski.net/Enums/ServerCacheMode.cs | 8 + Luski.net/Interfaces/IServer.cs | 1 - Luski.net/Interfaces/IUser.cs | 5 +- Luski.net/Luski.net.csproj | 2 +- ...erver.Account.cs => MainServer.Account.cs} | 22 +-- Luski.net/MainServer.Events.cs | 15 ++ Luski.net/MainServer.Incoming.cs | 116 ++++++++++++ Luski.net/MainServer.cs | 171 ++++++++++++++++++ Luski.net/PublicServer.cs | 6 + Luski.net/Server.Events.cs | 20 +- Luski.net/Server.Globals.cs | 11 +- Luski.net/Server.Incoming.cs | 125 +------------ Luski.net/Server.cs | 157 +--------------- .../Structures/Main/MainSocketAppUser.cs | 48 ++--- .../Structures/Main/MainSocketChannel.cs | 10 +- .../Structures/Main/MainSocketDMChannel.cs | 2 +- .../MainSocketMessage.cs} | 17 +- .../Structures/Main/MainSocketRemoteUser.cs | 5 +- .../Structures/Main/MainSocketTextChannel.cs | 16 +- .../MainSocketUserBase.cs} | 12 +- .../{ => Main}/SocketBulkMessage.cs | 4 +- .../Structures/Public/MainSocketUserBase.cs | 51 ++++++ .../Structures/Public/PublicSocketAppUser.cs | 3 +- 24 files changed, 464 insertions(+), 377 deletions(-) create mode 100644 Luski.net/Enums/ServerCacheMode.cs rename Luski.net/{Server.Account.cs => MainServer.Account.cs} (89%) create mode 100644 Luski.net/MainServer.Events.cs create mode 100644 Luski.net/MainServer.Incoming.cs create mode 100644 Luski.net/MainServer.cs create mode 100644 Luski.net/PublicServer.cs rename Luski.net/Structures/{SocketMessage.cs => Main/MainSocketMessage.cs} (77%) rename Luski.net/Structures/{SocketUserBase.cs => Main/MainSocketUserBase.cs} (85%) rename Luski.net/Structures/{ => Main}/SocketBulkMessage.cs (83%) create mode 100644 Luski.net/Structures/Public/MainSocketUserBase.cs diff --git a/Luski.net/API.cs b/Luski.net/API.cs index a6c87f0..7b239f5 100644 --- a/Luski.net/API.cs +++ b/Luski.net/API.cs @@ -7,15 +7,15 @@ namespace Luski.net; public class API { - public Server MainServer { get; internal set; } - internal List> InternalServers { get; } = new(); - public IReadOnlyList> LoadedServers => InternalServers.AsReadOnly(); + public MainServer MainServer { get; internal set; } + internal List InternalServers { get; } = new(); + public IReadOnlyList LoadedServers => InternalServers.AsReadOnly(); - public Server GetPublicServer(string Domain, string Version = "v1") + public PublicServer GetPublicServer(string Domain, string Version = "v1") { - IEnumerable> isl = InternalServers.Where(a => (a.Domain == Domain && a.ApiVersion == Version)); + IEnumerable isl = InternalServers.Where(a => (a.Domain == Domain && a.ApiVersion == Version)); if (isl.Any()) return isl.First(); - Server s = new() + PublicServer s = new() { Domain = Domain, ApiVersion = Version, @@ -24,7 +24,7 @@ public class API return s; } - public Server GetMainServer(string Domain, string Version = "v1") + public MainServer GetMainServer(string Domain, string Version = "v1") { MainServer = new() { diff --git a/Luski.net/Enums/ServerCacheMode.cs b/Luski.net/Enums/ServerCacheMode.cs new file mode 100644 index 0000000..fa9b152 --- /dev/null +++ b/Luski.net/Enums/ServerCacheMode.cs @@ -0,0 +1,8 @@ +namespace Luski.net.Enums; + +public enum ServerCacheMode : byte +{ + None, + Encrypted, + Unencrypted +} \ No newline at end of file diff --git a/Luski.net/Interfaces/IServer.cs b/Luski.net/Interfaces/IServer.cs index 5214204..e20115c 100644 --- a/Luski.net/Interfaces/IServer.cs +++ b/Luski.net/Interfaces/IServer.cs @@ -33,6 +33,5 @@ public interface IServer public Task SendServer(string Path, string File, JsonTypeInfo ReturnjsonTypeInfo, CancellationToken CancellationToken, params KeyValuePair[] Headers) where Tresult : IncomingHTTP, new(); - public Task GetUser(long UserID, CancellationToken CancellationToken) where Tuser : SocketUserBase, new(); } \ No newline at end of file diff --git a/Luski.net/Interfaces/IUser.cs b/Luski.net/Interfaces/IUser.cs index d06108f..26d8bc5 100755 --- a/Luski.net/Interfaces/IUser.cs +++ b/Luski.net/Interfaces/IUser.cs @@ -35,8 +35,5 @@ public interface IUser /// /// Task GetUserKey(CancellationToken CancellationToken); - /// - /// The server that the user comes from - /// - IServer Server { get; } + } diff --git a/Luski.net/Luski.net.csproj b/Luski.net/Luski.net.csproj index a4622e6..7e53d0b 100755 --- a/Luski.net/Luski.net.csproj +++ b/Luski.net/Luski.net.csproj @@ -13,7 +13,7 @@ https://github.com/JacobTech-com/Luski.net True 1.0.0 - 1.1.3-alpha23 + 1.1.3-alpha24 diff --git a/Luski.net/Server.Account.cs b/Luski.net/MainServer.Account.cs similarity index 89% rename from Luski.net/Server.Account.cs rename to Luski.net/MainServer.Account.cs index e450715..72d3bf7 100644 --- a/Luski.net/Server.Account.cs +++ b/Luski.net/MainServer.Account.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Net.Http; using System.Security.Authentication; @@ -14,23 +15,22 @@ using Luski.net.JsonTypes.WSS; using Luski.net.Structures.Main; using Luski.net.Structures.Public; using WebSocketSharp; -using File = System.IO.File; namespace Luski.net; -public partial class Server +public partial class MainServer { - public void Login(string Email, string Password, CancellationToken CancellationToken) + public void Login(string Email, string Password, System.Threading.CancellationToken CancellationToken) { Both(Email, Password, CancellationToken); } - public void CreateAccount(string Email, string Password, string Username, string PFP, CancellationToken CancellationToken) + public void CreateAccount(string Email, string Password, string Username, string PFP, System.Threading.CancellationToken CancellationToken) { Both(Email, Password, CancellationToken, Username, PFP); } - private void Both(string Email, string Password, CancellationToken CancellationToken, string? Username = null, string? pfp = null) + private void Both(string Email, string Password, System.Threading.CancellationToken CancellationToken, string? Username = null, string? pfp = null) { if (!ClientEncryption.Generating) { @@ -95,15 +95,9 @@ public partial class Server long id = long.Parse(Encoding.UTF8.GetString(Convert.FromBase64String( Token.Split('.')[0] ))); - TUser t = new(); - User = t switch - { - MainSocketAppUser => (GetUser(id, MainSocketAppUserContext.Default.MainSocketAppUser, - CancellationToken.None) - .Result as TUser)!, - _ => (GetUser(id, PublicSocketAppUserContext.Default.PublicSocketAppUser, CancellationToken.None) - .Result as TUser)! - }; + User = GetUser(id, MainSocketAppUserContext.Default.MainSocketAppUser, + CancellationToken) + .Result; if (User is null || User.Error is not null) { string error = "User was null"; diff --git a/Luski.net/MainServer.Events.cs b/Luski.net/MainServer.Events.cs new file mode 100644 index 0000000..011ebe1 --- /dev/null +++ b/Luski.net/MainServer.Events.cs @@ -0,0 +1,15 @@ +using System; +using System.Threading.Tasks; +using Luski.net.Structures; +using Luski.net.Structures.Main; + +namespace Luski.net; + +public partial class MainServer +{ + public event Func? MessageReceived; + + public event Func? ReceivedFriendRequest; + + public event Func? FriendRequestResult; +} \ No newline at end of file diff --git a/Luski.net/MainServer.Incoming.cs b/Luski.net/MainServer.Incoming.cs new file mode 100644 index 0000000..7d769e6 --- /dev/null +++ b/Luski.net/MainServer.Incoming.cs @@ -0,0 +1,116 @@ +using System; +using System.Text.Json; +using System.Threading; +using JacobTechEncryption; +using Luski.net.Enums; +using Luski.net.JsonTypes; +using Luski.net.JsonTypes.BaseTypes; +using Luski.net.JsonTypes.HTTP; +using Luski.net.JsonTypes.WSS; +using Luski.net.Structures; +using Luski.net.Structures.Main; +using WebSocketSharp; + +namespace Luski.net; + +public partial class MainServer +{ + private void DataFromServer(object? sender, MessageEventArgs e) + { + if (e.IsPing) return; + try + { + Console.WriteLine("From Server: {0}", e.Data); + IncomingWSS? data = JsonSerializer.Deserialize(e.Data, IncomingWSSContext.Default.IncomingWSS); + switch (data?.Type) + { + case DataType.Login: + Console.WriteLine("Pre auth"); + WSSLogin n = JsonSerializer.Deserialize(e.Data, WSSLoginContext.Default.WSSLogin)!; + Token = n.Token; + Console.WriteLine("Token: {0}",Token); + break; + case DataType.Error: + if (Token is null) + { + Error = data.Error; + } + else + { + Exception(new Exception(data.Error)); + } + break; + case DataType.Message_Create: + if (MessageReceived is not null) + { + MainSocketMessage? m = JsonSerializer.Deserialize(e.Data); + if (m is not null) + { + m.decrypt(ClientEncryption.File.Channels.GetKey(m.ChannelID), CancellationToken.None); + _ = MessageReceived.Invoke(m); + } + } + break; + case DataType.Status_Update: + StatusUpdate? SU = JsonSerializer.Deserialize(e.Data); + if (SU is not null) + { + MainSocketRemoteUser after = GetUser(SU.id, MainSocketRemoteUserContext.Default.MainSocketRemoteUser, CancellationToken.None).Result; + after.Status = SU.after; + MainSocketRemoteUser before = after.Clone(); + before.Status = SU.before; + StatusUpdate(before, after); + } + break; + case DataType.Friend_Request: + if (ReceivedFriendRequest is not null) + { + FriendRequest? request = + JsonSerializer.Deserialize(e.Data, FriendRequestContext.Default.FriendRequest); + if (request is not null) + _ = ReceivedFriendRequest.Invoke(GetUser(request.Id, + MainSocketRemoteUserContext.Default.MainSocketRemoteUser, + CancellationToken.None).Result); + } + break; + case DataType.Friend_Request_Result: + FriendRequestResult? FRR = JsonSerializer.Deserialize(e.Data); + if (FRR is not null && FRR.Channel is not null && FRR.Id is not null && + FRR.Result is not null) + { + MainSocketDMChannel chan = GetChannel((long)FRR.Channel, + MainSocketDMChannelContext.Default.MainSocketDMChannel, + CancellationToken.None).Result; + chans.Add(chan); + MainSocketRemoteUser from1 = GetUser((long)FRR.Id, + MainSocketRemoteUserContext.Default.MainSocketRemoteUser, + CancellationToken.None).Result; + //from1.Channel = chan; + if (FriendRequestResult is not null) _ = FriendRequestResult.Invoke(from1, (bool)FRR.Result); + } + break; + case DataType.Key_Exchange: + try + { + KeyExchange? KE = JsonSerializer.Deserialize(e.Data); + if (KE is not null) + ClientEncryption.File.Channels.AddKey(KE.channel, + ClientEncryption.Encoder.GetString(Encryption.RSA.Decrypt(Convert.FromBase64String(KE.key), ClientEncryption.ofkey))); + } + catch (Exception ex) + { + Exception(ex); + } + + break; + default: + Console.WriteLine("Unknown"); + break; + } + } + catch (Exception exception) + { + Exception(exception); + } + } +} \ No newline at end of file diff --git a/Luski.net/MainServer.cs b/Luski.net/MainServer.cs new file mode 100644 index 0000000..1b23222 --- /dev/null +++ b/Luski.net/MainServer.cs @@ -0,0 +1,171 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json.Serialization.Metadata; +using System.Threading; +using System.Threading.Tasks; +using Luski.net.Enums; +using Luski.net.Interfaces; +using Luski.net.JsonTypes.BaseTypes; +using Luski.net.JsonTypes.HTTP; +using Luski.net.Structures.Main; +using Luski.net.Structures.Public; + +namespace Luski.net; + +public partial class MainServer : Server +{ + public MainSocketAppUser User { get; internal set; } = default!; + + public async Task GetChannel(long Channel, CancellationToken CancellationToken) where TChannel : MainSocketChannel, new() + { + TChannel Return = new(); + switch (Return) + { + case MainSocketDMChannel: + Return = (await GetChannel(Channel, MainSocketDMChannelContext.Default.MainSocketDMChannel, CancellationToken) as TChannel)!; + break; + case MainSocketGroupChannel: + Return = (await GetChannel(Channel, MainSocketGroupChannelContext.Default.MainSocketGroupChannel, CancellationToken) as TChannel)!; + break; + case MainSocketTextChannel: + Return = (await GetChannel(Channel, MainSocketTextChannelContext.Default.MainSocketTextChannel, CancellationToken) as TChannel)!; + break; + case MainSocketChannel: + Return = (await GetChannel(Channel, MainSocketChannelContext.Default.MainSocketChannel, CancellationToken) as TChannel)!; + break; + case null: + throw new NullReferenceException(nameof(TChannel)); + default: + throw new Exception("Unknown channel type"); + } + return Return; + } + + internal async Task GetChannel(long id, JsonTypeInfo Json, CancellationToken CancellationToken) where TChannel : MainSocketChannel, new() + { + TChannel request; + if (chans.Count > 0 && chans.Any(s => s.Id == id)) + { + return chans.Where(s => s is TChannel && s.Id == id).Cast().FirstOrDefault()!; + } + while (true) + { + if (CanRequest) + { + request = await GetFromServer($"SocketChannel/Get/{id}", Json, CancellationToken); + break; + } + } + if (request is null) throw new Exception("Something was wrong with the server responce"); + if (request.Error is null) + { + if (chans.Count > 0 && chans.Any(s => s.Id == request.Id)) + { + foreach (MainSocketChannel? p in chans.Where(s => s.Id == request.Id)) + { + chans.Remove(p); + } + } + chans.Add(request); + return request; + } + throw request.Error switch + { + ErrorCode.InvalidToken => new Exception("Your current token is no longer valid"), + ErrorCode.Forbidden => new Exception("The server rejected your request"), + ErrorCode.ServerError => new Exception("Error from server: " + request.ErrorMessage), + ErrorCode.InvalidURL or ErrorCode.MissingHeader => new Exception(request.ErrorMessage), + _ => new Exception($"Unknown data: '{request.ErrorMessage}'"), + }; + } + + public Task GetUser(long UserID, CancellationToken CancellationToken) where Tuser : MainSocketUserBase, new() + { + Tuser user = new(); + switch (user) + { + case MainSocketAppUser: + user = (GetUser(UserID, MainSocketAppUserContext.Default.MainSocketAppUser, CancellationToken).Result as Tuser)!; + break; + case MainSocketUserBase: + user = (GetUser(UserID, MainSocketUserBaseContext.Default.MainSocketUserBase, CancellationToken).Result as Tuser)!; + break; + case null: + throw new NullReferenceException(nameof(Tuser)); + default: + throw new Exception("Unknown channel type"); + } + + return Task.FromResult(user); + } + + public async Task GetMessage(long id, CancellationToken CancellationToken) + { + MainSocketMessage message; + while (true) + { + if (CanRequest) + { + message = await GetFromServer("socketmessage", + MainSocketMessageContext.Default.MainSocketMessage, + CancellationToken, + new System.Collections.Generic.KeyValuePair("msg_id", id.ToString())); + break; + } + } + if (message is not null) return message; + throw new Exception("Server did not return a message"); + } + + /// + /// Sends the server a request to update the of you account + /// + /// The you want to set your status to + /// + public async Task UpdateStatus(UserStatus Status, CancellationToken CancellationToken) + { + IncomingHTTP? data = await SendServer("SocketUserProfile/Status", new Status() { UserStatus = Status }, StatusContext.Default.Status, IncomingHTTPContext.Default.IncomingHTTP, CancellationToken); + if (data.Error is not null && ((int)data.StatusCode < 200 || (int)data.StatusCode > 299)) + { + if (data?.ErrorMessage is not null) throw new Exception(data.ErrorMessage); + if (data?.Error is not null) throw new Exception(((int)data.Error).ToString()); + else throw new Exception("Something went worng"); + } + + User.Status = Status; + return Task.CompletedTask; + } + + internal async Task GetUser(long UserId, JsonTypeInfo Json, CancellationToken CancellationToken) where Tuser : MainSocketUserBase, new() + { + Tuser user; + if (poeople.Count > 0 && poeople.Any(s => s.Id == UserId)) + { + Tuser temp = poeople.Where(s => s is Tuser && s.Id == UserId).Cast().FirstOrDefault()!; + return temp; + } + while (true) + { + if (CanRequest) + { + user = await GetFromServer("socketuser", + Json, + CancellationToken, + new KeyValuePair("id", UserId.ToString())); + break; + } + } + + if (user is null) throw new Exception("Server did not return a user"); + if (poeople.Count > 0 && poeople.Any(s => s.Id == UserId)) + { + foreach (IUser? p in poeople.Where(s => s.Id == UserId)) + { + poeople.Remove(p); + } + } + poeople.Add(user); + return user; + } +} \ No newline at end of file diff --git a/Luski.net/PublicServer.cs b/Luski.net/PublicServer.cs new file mode 100644 index 0000000..63bbd3e --- /dev/null +++ b/Luski.net/PublicServer.cs @@ -0,0 +1,6 @@ +namespace Luski.net; + +public class PublicServer : Server +{ + +} \ No newline at end of file diff --git a/Luski.net/Server.Events.cs b/Luski.net/Server.Events.cs index 132efcd..e1cb506 100644 --- a/Luski.net/Server.Events.cs +++ b/Luski.net/Server.Events.cs @@ -1,20 +1,22 @@ using System; using System.Threading.Tasks; using Luski.net.Interfaces; -using Luski.net.JsonTypes; -using Luski.net.Structures; namespace Luski.net; -public partial class Server +public partial class Server { - public event Func? MessageReceived; - public event Func? UserStatusUpdate; - public event Func? ReceivedFriendRequest; - - public event Func? FriendRequestResult; - public event Func? OnError; + + internal void Exception(Exception e) + { + if (OnError is not null) OnError.Invoke(e); + } + + internal void StatusUpdate(IUser u1, IUser u2) + { + if (UserStatusUpdate is not null) UserStatusUpdate.Invoke(u1, u2); + } } \ No newline at end of file diff --git a/Luski.net/Server.Globals.cs b/Luski.net/Server.Globals.cs index 4959487..1a81f79 100644 --- a/Luski.net/Server.Globals.cs +++ b/Luski.net/Server.Globals.cs @@ -4,23 +4,24 @@ using System.IO; using Luski.net.Enums; using Luski.net.Interfaces; using Luski.net.JsonTypes; +using Luski.net.Structures; using Luski.net.Structures.Main; using Luski.net.Structures.Public; using WebSocketSharp; namespace Luski.net; -public partial class Server where TUser : class, IAppUser, new() +public partial class Server { public ServerType ServerType { get; internal set; } = ServerType.Public; public string Domain { get; internal set; } = default!; - public IAppUser IAppUser => User; - public TUser User { get; internal set; } + public ServerCacheMode CacheMode { get; set; } = ServerCacheMode.None; public string ApiVersion { get; internal set; } = "v1"; - private WebSocket? ServerOut; + internal WebSocket? ServerOut; internal string? Token = null, Error = null, gen = null; internal bool CanRequest = false, login = false; internal List poeople = new(); + public long UserID { get; internal set; } = 0; internal List chans { get; set; } = new(); public string Cache { @@ -33,7 +34,7 @@ public partial class Server where TUser : class, IAppUser, new() if (!Directory.Exists(path)) Directory.CreateDirectory(path); path += "Data/"; if (!Directory.Exists(path)) Directory.CreateDirectory(path); - path += User.Id + "/"; + path += UserID + "/"; if (!Directory.Exists(path)) Directory.CreateDirectory(path); path += "Cache/"; if (!Directory.Exists(path)) Directory.CreateDirectory(path); diff --git a/Luski.net/Server.Incoming.cs b/Luski.net/Server.Incoming.cs index 75f9347..f749c14 100644 --- a/Luski.net/Server.Incoming.cs +++ b/Luski.net/Server.Incoming.cs @@ -1,131 +1,12 @@ using System; -using System.Text.Json; -using System.Threading; -using JacobTechEncryption; -using Luski.net.Enums; -using Luski.net.JsonTypes; -using Luski.net.JsonTypes.BaseTypes; -using Luski.net.JsonTypes.HTTP; -using Luski.net.JsonTypes.WSS; -using Luski.net.Structures; -using Luski.net.Structures.Main; using WebSocketSharp; namespace Luski.net; -public partial class Server +public partial class Server { - private void ServerOut_OnError(object? sender, WebSocketSharp.ErrorEventArgs e) + internal void ServerOut_OnError(object? sender, ErrorEventArgs e) { - if (OnError is not null) OnError.Invoke(new Exception(e.Message)); - } - - private void DataFromServer(object? sender, MessageEventArgs e) - { - if (e.IsPing) return; - try - { - Console.WriteLine("From Server: {0}", e.Data); - IncomingWSS? data = JsonSerializer.Deserialize(e.Data, IncomingWSSContext.Default.IncomingWSS); - switch (data?.Type) - { - case DataType.Login: - Console.WriteLine("Pre auth"); - WSSLogin n = JsonSerializer.Deserialize(e.Data, WSSLoginContext.Default.WSSLogin)!; - Token = n.Token; - Console.WriteLine("Token: {0}",Token); - break; - case DataType.Error: - if (Token is null) - { - Error = data.Error; - } - else - { - if (OnError is not null) - { - _ = OnError.Invoke(new Exception(data.Error)); - } - } - break; - case DataType.Message_Create: - if (MessageReceived is not null) - { - SocketMessage? m = JsonSerializer.Deserialize(e.Data); - if (m is not null) - { - m.decrypt(ClientEncryption.File.Channels.GetKey(m.ChannelID), CancellationToken.None); - _ = MessageReceived.Invoke(m); - } - } - break; - case DataType.Status_Update: - if (UserStatusUpdate is not null) - { - StatusUpdate? SU = JsonSerializer.Deserialize(e.Data); - if (SU is not null) - { - MainSocketRemoteUser after = GetUser(SU.id, MainSocketRemoteUserContext.Default.MainSocketRemoteUser, CancellationToken.None).Result; - after.Status = SU.after; - MainSocketRemoteUser before = after.Clone(); - before.Status = SU.before; - _ = UserStatusUpdate.Invoke(before, after); - } - } - break; - case DataType.Friend_Request: - if (ReceivedFriendRequest is not null) - { - FriendRequest? request = - JsonSerializer.Deserialize(e.Data, FriendRequestContext.Default.FriendRequest); - if (request is not null) - _ = ReceivedFriendRequest.Invoke(GetUser(request.Id, - MainSocketRemoteUserContext.Default.MainSocketRemoteUser, - CancellationToken.None).Result); - } - break; - case DataType.Friend_Request_Result: - if (FriendRequestResult is not null) - { - FriendRequestResult? FRR = JsonSerializer.Deserialize(e.Data); - if (FRR is not null && FRR.Channel is not null && FRR.Id is not null && - FRR.Result is not null) - { - MainSocketDMChannel chan = GetChannel((long)FRR.Channel, - MainSocketDMChannelContext.Default.MainSocketDMChannel, - CancellationToken.None).Result; - chans.Add(chan); - MainSocketRemoteUser from1 = GetUser((long)FRR.Id, - MainSocketRemoteUserContext.Default.MainSocketRemoteUser, - CancellationToken.None).Result; - //from1.Channel = chan; - _ = FriendRequestResult.Invoke(from1, (bool)FRR.Result); - } - } - break; - case DataType.Key_Exchange: - try - { - KeyExchange? KE = JsonSerializer.Deserialize(e.Data); - if (KE is not null) - ClientEncryption.File.Channels.AddKey(KE.channel, - ClientEncryption.Encoder.GetString(Encryption.RSA.Decrypt(Convert.FromBase64String(KE.key), ClientEncryption.ofkey))); - } - catch (Exception ex) - { - if (OnError is not null) OnError.Invoke(ex); - } - - break; - default: - Console.WriteLine("Unknown"); - break; - } - } - catch (Exception exception) - { - if (OnError is not null) _ = OnError.Invoke(exception); - else throw exception; - } + this.Exception(new Exception(e.Message)); } } \ No newline at end of file diff --git a/Luski.net/Server.cs b/Luski.net/Server.cs index 814b71d..6420dbd 100644 --- a/Luski.net/Server.cs +++ b/Luski.net/Server.cs @@ -20,7 +20,7 @@ using File = System.IO.File; namespace Luski.net; -public partial class Server : IServer +public partial class Server { internal Server() { } @@ -35,161 +35,6 @@ public partial class Server : IServer return File.ReadAllBytes($"{Cache}/servers/{Domain}"); } - public async Task GetUser(long UserID, CancellationToken CancellationToken) where Tuser : SocketUserBase, new() - { - Tuser user = new(); - switch (user) - { - case MainSocketAppUser: - user = (GetUser(UserID, MainSocketAppUserContext.Default.MainSocketAppUser, CancellationToken).Result as Tuser)!; - break; - case PublicSocketAppUser: - user = (GetUser(UserID, PublicSocketAppUserContext.Default.PublicSocketAppUser, CancellationToken).Result as Tuser)!; - break; - case SocketUserBase: - user = (GetUser(UserID, SocketUserBaseContext.Default.SocketUserBase, CancellationToken).Result as Tuser)!; - break; - case null: - throw new NullReferenceException(nameof(Tuser)); - default: - throw new Exception("Unknown channel type"); - } - - return user; - } - - public async Task GetChannel(long Channel, CancellationToken CancellationToken) where TChannel : MainSocketChannel, new() - { - TChannel Return = new(); - switch (Return) - { - case MainSocketDMChannel: - Return = (await GetChannel(Channel, MainSocketDMChannelContext.Default.MainSocketDMChannel, CancellationToken) as TChannel)!; - break; - case MainSocketGroupChannel: - Return = (await GetChannel(Channel, MainSocketGroupChannelContext.Default.MainSocketGroupChannel, CancellationToken) as TChannel)!; - break; - case MainSocketTextChannel: - Return = (await GetChannel(Channel, MainSocketTextChannelContext.Default.MainSocketTextChannel, CancellationToken) as TChannel)!; - break; - case MainSocketChannel: - Return = (await GetChannel(Channel, MainSocketChannelContext.Default.MainSocketChannel, CancellationToken) as TChannel)!; - break; - case null: - throw new NullReferenceException(nameof(TChannel)); - default: - throw new Exception("Unknown channel type"); - } - return Return; - } - - internal async Task GetChannel(long id, JsonTypeInfo Json, CancellationToken CancellationToken) where TChannel : MainSocketChannel, new() - { - TChannel request; - if (chans.Count > 0 && chans.Any(s => s.Id == id)) - { - return chans.Where(s => s is TChannel && s.Id == id).Cast().FirstOrDefault()!; - } - while (true) - { - if (CanRequest) - { - request = await GetFromServer($"SocketChannel/Get/{id}", Json, CancellationToken); - break; - } - } - if (request is null) throw new Exception("Something was wrong with the server responce"); - if (request.Error is null) - { - if (chans.Count > 0 && chans.Any(s => s.Id == request.Id)) - { - foreach (MainSocketChannel? p in chans.Where(s => s.Id == request.Id)) - { - chans.Remove(p); - } - } - chans.Add(request); - return request; - } - throw request.Error switch - { - ErrorCode.InvalidToken => new Exception("Your current token is no longer valid"), - ErrorCode.Forbidden => new Exception("The server rejected your request"), - ErrorCode.ServerError => new Exception("Error from server: " + request.ErrorMessage), - ErrorCode.InvalidURL or ErrorCode.MissingHeader => new Exception(request.ErrorMessage), - _ => new Exception($"Unknown data: '{request.ErrorMessage}'"), - }; - } - - public async Task GetMessage(long id, CancellationToken CancellationToken) - { - SocketMessage message; - while (true) - { - if (CanRequest) - { - message = await GetFromServer("socketmessage", - SocketMessageContext.Default.SocketMessage, - CancellationToken, - new System.Collections.Generic.KeyValuePair("msg_id", id.ToString())); - break; - } - } - if (message is not null) return message; - throw new Exception("Server did not return a message"); - } - - /// - /// Sends the server a request to update the of you account - /// - /// The you want to set your status to - /// - public async Task UpdateStatus(UserStatus Status, CancellationToken CancellationToken) - { - IncomingHTTP? data = await SendServer("SocketUserProfile/Status", new Status() { UserStatus = Status }, StatusContext.Default.Status, IncomingHTTPContext.Default.IncomingHTTP, CancellationToken); - if (data.Error is not null && ((int)data.StatusCode < 200 || (int)data.StatusCode > 299)) - { - if (data?.ErrorMessage is not null) throw new Exception(data.ErrorMessage); - if (data?.Error is not null) throw new Exception(((int)data.Error).ToString()); - else throw new Exception("Something went worng"); - } - - (User as SocketUserBase)!.Status = Status; - return Task.CompletedTask; - } - - internal async Task GetUser(long UserId, JsonTypeInfo Json, CancellationToken CancellationToken) where Tuser : SocketUserBase, new() - { - Tuser user; - if (poeople.Count > 0 && poeople.Any(s => s.Id == UserId)) - { - Tuser temp = poeople.Where(s => s is Tuser && s.Id == UserId).Cast().FirstOrDefault()!; - return temp; - } - while (true) - { - if (CanRequest) - { - user = await GetFromServer("socketuser", - Json, - CancellationToken, - new KeyValuePair("id", UserId.ToString())); - break; - } - } - - if (user is null) throw new Exception("Server did not return a user"); - if (poeople.Count > 0 && poeople.Any(s => s.Id == UserId)) - { - foreach (IUser? p in poeople.Where(s => s.Id == UserId)) - { - poeople.Remove(p); - } - } - poeople.Add(user); - return user; - } - public void SendServer(Tvalue Payload, JsonTypeInfo jsonTypeInfo) where Tvalue : IncomingWSS { ServerOut?.Send(JsonSerializer.Serialize(Payload, jsonTypeInfo)); diff --git a/Luski.net/Structures/Main/MainSocketAppUser.cs b/Luski.net/Structures/Main/MainSocketAppUser.cs index 7d3dc8d..20e3a59 100755 --- a/Luski.net/Structures/Main/MainSocketAppUser.cs +++ b/Luski.net/Structures/Main/MainSocketAppUser.cs @@ -14,14 +14,19 @@ using Luski.net.JsonTypes; namespace Luski.net.Structures.Main; -public class MainSocketAppUser : SocketUserBase, IAppUser +public class MainSocketAppUser : MainSocketUserBase, IAppUser { + [JsonPropertyName("selected_channel")] + [JsonInclude] + public long SelectedChannel { get; internal set; } = default!; [JsonPropertyName("username")] [JsonInclude] public string Username { get; internal set; } = default!; [JsonPropertyName("flags")] [JsonInclude] public UserFlag Flags { get; internal set; } = default!; + + public MainServer Server { get; internal set; } = default!; [JsonIgnore] public IReadOnlyList Channels @@ -35,17 +40,17 @@ public class MainSocketAppUser : SocketUserBase, IAppUser _Channels = new List(); foreach (long channel in ChannelIdList) { - MainSocketChannel s = (Server as Server).GetChannel(channel, + MainSocketChannel s = Server.GetChannel(channel, MainSocketChannelContext.Default.MainSocketChannel, CancellationToken.None).Result; - (Server as Server)!.chans.Remove(s); + Server.chans.Remove(s); switch (s.Type) { case ChannelType.GROUP: - _Channels.Add((Server as Server).GetChannel(channel, + _Channels.Add(Server.GetChannel(channel, MainSocketGroupChannelContext.Default.MainSocketGroupChannel, CancellationToken.None).Result); break; case ChannelType.DM: - _Channels.Add((Server as Server).GetChannel(channel, + _Channels.Add(Server.GetChannel(channel, MainSocketDMChannelContext.Default.MainSocketDMChannel, CancellationToken.None).Result); break; } @@ -100,9 +105,6 @@ public class MainSocketAppUser : SocketUserBase, IAppUser return _Friends.AsReadOnly(); } } - [JsonPropertyName("selected_channel")] - [JsonInclude] - public long SelectedChannel { get; internal set; } = default!; [JsonPropertyName("channels")] [JsonInclude] public long[] ChannelIdList { get; internal set; } = default!; @@ -127,33 +129,33 @@ public class MainSocketAppUser : SocketUserBase, IAppUser internal void AddFriend(MainSocketRemoteUser User) { - if ((Server as Server)!.poeople.Any(s => s.Id == User.Id)) + if (Server.poeople.Any(s => s.Id == User.Id)) { - IEnumerable b = (Server as Server)!.poeople.Where(s => s.Id == User.Id); + IEnumerable b = Server.poeople.Where(s => s.Id == User.Id); foreach (IUser item in b) { - (Server as Server)!.poeople.Remove(item); + Server.poeople.Remove(item); } - (Server as Server)!.poeople.Add(User); + Server.poeople.Add(User); } else { - (Server as Server)!.poeople.Add(User); + Server.poeople.Add(User); } _Friends.Add(User); } internal void RemoveFriendRequest(MainSocketRemoteUser User) { - if ((Server as Server)!.poeople.Any(s => s.Id == User.Id)) + if (Server.poeople.Any(s => s.Id == User.Id)) { - IEnumerable b = (Server as Server)!.poeople.Where(s => s.Id == User.Id); + IEnumerable b = Server.poeople.Where(s => s.Id == User.Id); foreach (IUser item in b) { - (Server as Server)!.poeople.Remove(item); + Server.poeople.Remove(item); } - } - (Server as Server)!.poeople.Add(User); + } + Server.poeople.Add(User); foreach (MainSocketRemoteUser user in _FriendRequests) { if (User.Id == user.Id) @@ -165,18 +167,18 @@ public class MainSocketAppUser : SocketUserBase, IAppUser internal void AddFriendRequest(MainSocketRemoteUser User) { - if ((Server as Server)!.poeople.Any(s => s.Id == User.Id)) + if (Server.poeople.Any(s => s.Id == User.Id)) { - IEnumerable b = (Server as Server)!.poeople.Where(s => s.Id == User.Id); + IEnumerable b = Server.poeople.Where(s => s.Id == User.Id); foreach (IUser item in b) { - (Server as Server)!.poeople.Remove(item); + Server.poeople.Remove(item); } - (Server as Server)!.poeople.Add(User); + Server.poeople.Add(User); } else { - (Server as Server)!.poeople.Add(User); + Server.poeople.Add(User); } _FriendRequests.Add(User); } diff --git a/Luski.net/Structures/Main/MainSocketChannel.cs b/Luski.net/Structures/Main/MainSocketChannel.cs index a46b6e1..5d5bdd2 100755 --- a/Luski.net/Structures/Main/MainSocketChannel.cs +++ b/Luski.net/Structures/Main/MainSocketChannel.cs @@ -34,7 +34,9 @@ public class MainSocketChannel : IncomingHTTP [JsonPropertyName("type")] [JsonInclude] public ChannelType Type { get; internal set; } = default!; - public IServer Server { get; internal set; } + + public MainServer Server { get; internal set; } = default!; + [JsonPropertyName("members")] [JsonInclude] public long[] MemberIdList { get; internal set; } = default!; @@ -49,8 +51,8 @@ public class MainSocketChannel : IncomingHTTP _members = new(); foreach (long member in MemberIdList) { - if (member != Server.IAppUser!.Id) _members.Add(Server.GetUser(member, CancellationToken.None).Result); - else _members.Add(Server.IAppUser); + if (member != Server.User.Id) _members.Add(Server.GetUser(member, CancellationToken.None).Result); + else _members.Add(Server.User); } } return _members.AsReadOnly(); @@ -74,7 +76,7 @@ public class MainSocketChannel : IncomingHTTP MaxDegreeOfParallelism = num }, i => { - if (i.Id != (Server as Server).User?.Id) + if (i.Id != Server.User.Id) { long key = i.GetUserKey(CancellationToken).Result; if (true) diff --git a/Luski.net/Structures/Main/MainSocketDMChannel.cs b/Luski.net/Structures/Main/MainSocketDMChannel.cs index c330a3d..8e9b77f 100755 --- a/Luski.net/Structures/Main/MainSocketDMChannel.cs +++ b/Luski.net/Structures/Main/MainSocketDMChannel.cs @@ -14,7 +14,7 @@ public class MainSocketDMChannel : MainSocketTextChannel if (_user is null) { var list = MemberIdList.ToList(); - list.Remove(Server.IAppUser.Id); + list.Remove(Server.User.Id); _user = Server.GetUser(list.FirstOrDefault(), CancellationToken.None).Result; } return _user; diff --git a/Luski.net/Structures/SocketMessage.cs b/Luski.net/Structures/Main/MainSocketMessage.cs similarity index 77% rename from Luski.net/Structures/SocketMessage.cs rename to Luski.net/Structures/Main/MainSocketMessage.cs index 5e41bda..f51ca04 100755 --- a/Luski.net/Structures/SocketMessage.cs +++ b/Luski.net/Structures/Main/MainSocketMessage.cs @@ -1,20 +1,17 @@ using Luski.net.Interfaces; using Luski.net.JsonTypes.BaseTypes; using System; -using System.Linq; -using System.Net.Http; using System.Text; -using System.Text.Json; using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; using JacobTechEncryption; -namespace Luski.net.Structures; +namespace Luski.net.Structures.Main; -public class SocketMessage : IncomingHTTP +public class MainSocketMessage : IncomingHTTP { - public IServer Server { get; } + public MainServer Server { get; internal set; } = default!; [JsonPropertyName("id")] [JsonInclude] public long Id { get; internal set; } = default!; @@ -32,8 +29,8 @@ public class SocketMessage : IncomingHTTP public File[]? Files { get; internal set; } = default!; public async Task GetAuthor(CancellationToken CancellationToken) { - if (Server.IAppUser!.Id != AuthorID) return await Server.GetUser(AuthorID, CancellationToken); - else return Server.IAppUser; + if (Server.User.Id != AuthorID) return await Server.GetUser(AuthorID, CancellationToken); + else return Server.User; } internal void decrypt(string? key, CancellationToken CancellationToken) @@ -51,12 +48,12 @@ public class SocketMessage : IncomingHTTP } } -[JsonSerializable(typeof(SocketMessage))] +[JsonSerializable(typeof(MainSocketMessage))] [JsonSourceGenerationOptions( GenerationMode = JsonSourceGenerationMode.Default, PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, WriteIndented = false)] -public partial class SocketMessageContext : JsonSerializerContext +public partial class MainSocketMessageContext : JsonSerializerContext { } diff --git a/Luski.net/Structures/Main/MainSocketRemoteUser.cs b/Luski.net/Structures/Main/MainSocketRemoteUser.cs index 9ac8862..34ab431 100755 --- a/Luski.net/Structures/Main/MainSocketRemoteUser.cs +++ b/Luski.net/Structures/Main/MainSocketRemoteUser.cs @@ -9,10 +9,11 @@ using System.Text.Json.Serialization; using System.Threading.Tasks; using Luski.net.Enums.Main; using Luski.net.Structures; +using Luski.net.Structures.Main; -namespace Luski.net.Structures; +namespace Luski.net.Structures.Main; -public class MainSocketRemoteUser : SocketUserBase +public class MainSocketRemoteUser : MainSocketUserBase { [JsonPropertyName("friend_status")] [JsonInclude] diff --git a/Luski.net/Structures/Main/MainSocketTextChannel.cs b/Luski.net/Structures/Main/MainSocketTextChannel.cs index ce83d2e..f07062a 100755 --- a/Luski.net/Structures/Main/MainSocketTextChannel.cs +++ b/Luski.net/Structures/Main/MainSocketTextChannel.cs @@ -17,9 +17,9 @@ namespace Luski.net.Structures.Main; public class MainSocketTextChannel : MainSocketChannel { - public async Task GetMessage(long ID, CancellationToken CancellationToken) + public async Task GetMessage(long ID, CancellationToken CancellationToken) { - return await (Server as Server)!.GetMessage(ID, CancellationToken); + return await Server.GetMessage(ID, CancellationToken); } public async Task GetPicture(CancellationToken CancellationToken) @@ -36,7 +36,7 @@ public class MainSocketTextChannel : MainSocketChannel } } - public async Task> GetMessages(long Message_Id, CancellationToken CancellationToken, int count = 50) + public async Task> GetMessages(long Message_Id, CancellationToken CancellationToken, int count = 50) { if (count > 200) { @@ -61,7 +61,7 @@ public class MainSocketTextChannel : MainSocketChannel string? key = ClientEncryption.File.Channels.GetKey(Id); if (data is null) throw new Exception("Invalid data from server"); - if (data.Messages is null) data.Messages = Array.Empty(); + if (data.Messages is null) data.Messages = Array.Empty(); Parallel.ForEach(data.Messages, new ParallelOptions() { MaxDegreeOfParallelism = num @@ -70,7 +70,7 @@ public class MainSocketTextChannel : MainSocketChannel i.decrypt(key, CancellationToken); }); key = null; - return await Task.FromResult(data.Messages.ToList().AsReadOnly() as IReadOnlyList); + return await Task.FromResult(data.Messages.ToList().AsReadOnly() as IReadOnlyList); } else { @@ -79,7 +79,7 @@ public class MainSocketTextChannel : MainSocketChannel } } - public async Task> GetMessages(CancellationToken CancellationToken, int count = 50) + public async Task> GetMessages(CancellationToken CancellationToken, int count = 50) { try { @@ -106,7 +106,7 @@ public class MainSocketTextChannel : MainSocketChannel int num = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * 5) * 2.0)); if (num == 0) num = 1; string? key = ClientEncryption.File.Channels.GetKey(Id); - if (data.Messages is null) data.Messages = Array.Empty(); + if (data.Messages is null) data.Messages = Array.Empty(); Parallel.ForEach(data.Messages, new ParallelOptions() { MaxDegreeOfParallelism = num @@ -116,7 +116,7 @@ public class MainSocketTextChannel : MainSocketChannel }); key = null; Console.WriteLine($"Messages decrypted in {(DateTime.Now - start).TotalSeconds}"); - return await Task.FromResult(data.Messages.ToList().AsReadOnly() as IReadOnlyList); + return await Task.FromResult(data.Messages.ToList().AsReadOnly() as IReadOnlyList); } else { diff --git a/Luski.net/Structures/SocketUserBase.cs b/Luski.net/Structures/Main/MainSocketUserBase.cs similarity index 85% rename from Luski.net/Structures/SocketUserBase.cs rename to Luski.net/Structures/Main/MainSocketUserBase.cs index f29c9ec..7b63322 100644 --- a/Luski.net/Structures/SocketUserBase.cs +++ b/Luski.net/Structures/Main/MainSocketUserBase.cs @@ -5,10 +5,11 @@ using Luski.net.Enums; using Luski.net.Interfaces; using Luski.net.JsonTypes.BaseTypes; -namespace Luski.net.Structures; +namespace Luski.net.Structures.Main; -public class SocketUserBase : IncomingHTTP, IUser +public class MainSocketUserBase : IncomingHTTP, IUser { + public MainServer Server { get; internal set; } = default!; [JsonPropertyName("id")] [JsonInclude] public long Id { get; internal set; } = default!; @@ -21,9 +22,6 @@ public class SocketUserBase : IncomingHTTP, IUser [JsonPropertyName("picture_type")] [JsonInclude] public PictureType PictureType { get; internal set; } = default!; - [JsonIgnore] - public IServer Server { get; internal set; } = default!; - public async Task GetAvatar(CancellationToken CancellationToken) { if (Server.Cache != null) @@ -41,13 +39,13 @@ public class SocketUserBase : IncomingHTTP, IUser } } -[JsonSerializable(typeof(SocketUserBase))] +[JsonSerializable(typeof(MainSocketUserBase))] [JsonSourceGenerationOptions( GenerationMode = JsonSourceGenerationMode.Default, PropertyNamingPolicy = JsonKnownNamingPolicy.Unspecified, WriteIndented = false, DefaultIgnoreCondition = JsonIgnoreCondition.Never)] -internal partial class SocketUserBaseContext : JsonSerializerContext +internal partial class MainSocketUserBaseContext : JsonSerializerContext { } \ No newline at end of file diff --git a/Luski.net/Structures/SocketBulkMessage.cs b/Luski.net/Structures/Main/SocketBulkMessage.cs similarity index 83% rename from Luski.net/Structures/SocketBulkMessage.cs rename to Luski.net/Structures/Main/SocketBulkMessage.cs index 3ec44d4..5a5b336 100755 --- a/Luski.net/Structures/SocketBulkMessage.cs +++ b/Luski.net/Structures/Main/SocketBulkMessage.cs @@ -1,13 +1,13 @@ using Luski.net.JsonTypes.BaseTypes; using System.Text.Json.Serialization; -namespace Luski.net.Structures; +namespace Luski.net.Structures.Main; internal class SocketBulkMessage : IncomingHTTP { [JsonPropertyName("messages")] [JsonInclude] - public SocketMessage[]? Messages { get; set; } = default!; + public MainSocketMessage[]? Messages { get; set; } = default!; } [JsonSerializable(typeof(SocketBulkMessage))] diff --git a/Luski.net/Structures/Public/MainSocketUserBase.cs b/Luski.net/Structures/Public/MainSocketUserBase.cs new file mode 100644 index 0000000..0252a20 --- /dev/null +++ b/Luski.net/Structures/Public/MainSocketUserBase.cs @@ -0,0 +1,51 @@ +using System.Text.Json.Serialization; +using System.Threading; +using System.Threading.Tasks; +using Luski.net.Enums; +using Luski.net.Interfaces; +using Luski.net.JsonTypes.BaseTypes; + +namespace Luski.net.Structures.Public; + +public class PublicSocketUserBase : IncomingHTTP, IUser +{ + public PublicServer Server { get; internal set; } = default!; + [JsonPropertyName("id")] + [JsonInclude] + public long Id { get; internal set; } = default!; + [JsonPropertyName("username")] + [JsonInclude] + public string DisplayName { get; internal set; } = default!; + [JsonPropertyName("status")] + [JsonInclude] + public UserStatus Status { get; internal set; } = default!; + [JsonPropertyName("picture_type")] + [JsonInclude] + public PictureType PictureType { get; internal set; } = default!; + public async Task GetAvatar(CancellationToken CancellationToken) + { + if (Server.Cache != null) + { + bool isc = System.IO.File.Exists($"{Server.Cache}/avatars/{Id}"); + if (!isc) await Server.GetFromServer($"socketuserprofile/Avatar/{Id}", $"{Server.Cache}/avatars/{Id}", CancellationToken); + } + return System.IO.File.ReadAllBytes($"{Server.Cache}/avatars/{Id}"); + } + + public Task GetUserKey(CancellationToken CancellationToken) + { + string data = Server.GetFromServer($"Keys/GetUserKey/{Id}", CancellationToken).Content.ReadAsStringAsync().Result; + return Task.FromResult(long.Parse(data)); + } +} + +[JsonSerializable(typeof(PublicSocketUserBase))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.Unspecified, + WriteIndented = false, + DefaultIgnoreCondition = JsonIgnoreCondition.Never)] +internal partial class PublicSocketUserBaseContext : JsonSerializerContext +{ + +} \ No newline at end of file diff --git a/Luski.net/Structures/Public/PublicSocketAppUser.cs b/Luski.net/Structures/Public/PublicSocketAppUser.cs index 0b7250b..65706f5 100755 --- a/Luski.net/Structures/Public/PublicSocketAppUser.cs +++ b/Luski.net/Structures/Public/PublicSocketAppUser.cs @@ -10,10 +10,11 @@ using System.Threading.Tasks; using JacobTechEncryption; using Luski.net.Enums; using Luski.net.Enums.Main; +using Luski.net.Structures.Main; namespace Luski.net.Structures.Public; -public class PublicSocketAppUser : SocketUserBase, IAppUser +public class PublicSocketAppUser : PublicSocketUserBase, IAppUser { [JsonPropertyName("selected_channel")] [JsonInclude] From c3bb39b21bc0972b810f162b8c067b533d00e32b Mon Sep 17 00:00:00 2001 From: JacobTech Date: Mon, 10 Jul 2023 07:35:05 -0400 Subject: [PATCH 04/14] Better Storage. The example library now has better storage for local information and cache. --- Luski.net/API.cs | 7 ++ Luski.net/ClientEncryption.cs | 85 +++---------- .../{ServerCacheMode.cs => CacheMode.cs} | 2 +- Luski.net/Enums/StorageDirectory.cs | 12 ++ Luski.net/JsonTypes/ServerStorageInfo.cs | 24 ++++ Luski.net/Luski.net.csproj | 2 +- Luski.net/MainServer.Account.cs | 81 ++++++------ Luski.net/MainServer.Incoming.cs | 19 ++- Luski.net/MainServer.cs | 37 +++++- Luski.net/Server.Globals.cs | 40 ------ Luski.net/Server.cs | 20 ++- Luski.net/ServerEncryption.cs | 56 +++++++++ Luski.net/ServerStorage.cs | 116 ++++++++++++++++++ Luski.net/Structures/File.cs | 6 +- .../Structures/Main/MainSocketChannel.cs | 7 +- .../Structures/Main/MainSocketRemoteUser.cs | 1 + .../Structures/Main/MainSocketTextChannel.cs | 19 ++- .../Structures/Main/MainSocketUserBase.cs | 9 +- ...ketUserBase.cs => PublicSocketUserBase.cs} | 9 +- 19 files changed, 353 insertions(+), 199 deletions(-) rename Luski.net/Enums/{ServerCacheMode.cs => CacheMode.cs} (67%) create mode 100644 Luski.net/Enums/StorageDirectory.cs create mode 100644 Luski.net/JsonTypes/ServerStorageInfo.cs create mode 100644 Luski.net/ServerEncryption.cs create mode 100644 Luski.net/ServerStorage.cs rename Luski.net/Structures/Public/{MainSocketUserBase.cs => PublicSocketUserBase.cs} (79%) diff --git a/Luski.net/API.cs b/Luski.net/API.cs index 7b239f5..c9bd04d 100644 --- a/Luski.net/API.cs +++ b/Luski.net/API.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Luski.net.Enums; using Luski.net.Structures.Main; using Luski.net.Structures.Public; @@ -19,6 +20,9 @@ public class API { Domain = Domain, ApiVersion = Version, + Storage = new(Domain), + EncryptionHandler = new(Domain), + ServerType = ServerType.Public }; InternalServers.Add(s); return s; @@ -30,6 +34,9 @@ public class API { Domain = Domain, ApiVersion = Version, + Storage = new(Domain), + EncryptionHandler = new(Domain), + ServerType = ServerType.Main }; return MainServer; } diff --git a/Luski.net/ClientEncryption.cs b/Luski.net/ClientEncryption.cs index 4d86d68..61b358f 100755 --- a/Luski.net/ClientEncryption.cs +++ b/Luski.net/ClientEncryption.cs @@ -1,51 +1,19 @@ -using Luski.net.Enums; -using System; +/*using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Net; -using System.Net.Http; using System.Security.Cryptography; using System.Text; using System.Text.Json; -using JacobTechEncryption; namespace Luski.net { public static class ClientEncryption { - internal static string? MyPublicKey; - internal static readonly UnicodeEncoding Encoder = new(); - private static string? myPrivateKey; - internal static bool Generating = false; - internal static bool Generated = false; - private static string? _serverpublickey = null; - internal static string? ofkey = null; - internal static string? outofkey = null; internal static string pw = ""; - public static int NewKeySize = 4096; - - public static void GenerateKeys() - { - if (!Generating) - { - Generating = true; - GenerateNewKeys(out MyPublicKey, out myPrivateKey); - GenerateNewKeys(out outofkey, out ofkey); - Generated = true; - } - } - - public static void GenerateNewKeys(out string Public, out string Private) - { - using RSACryptoServiceProvider r = new(NewKeySize); - Private = r.ToXmlString(true); - Public = r.ToXmlString(false); - return; - } public static class File - { + {/* internal static void SetOfflineKey(string key) { MakeFile("Server.GetKeyFilePath", pw); @@ -54,11 +22,7 @@ namespace Luski.net fileLayout.Save("Server.GetKeyFilePath", pw); } - public static LuskiDataFile GetFile() - { - MakeFile("Server.GetKeyFilePath", pw); - return JsonSerializer.Deserialize(FileString("Server.GetKeyFilePath", pw))!; - } + internal static string? GetOfflineKey() { @@ -66,6 +30,12 @@ namespace Luski.net LuskiDataFile? fileLayout = JsonSerializer.Deserialize(FileString("Server.GetKeyFilePath", pw)); return fileLayout?.OfflineKey; } + + public static LuskiDataFile GetFile() + { + MakeFile("Server.GetKeyFilePath", pw); + return JsonSerializer.Deserialize(FileString("Server.GetKeyFilePath", pw))!; + } private static string FileString(string path, string password) { @@ -112,14 +82,14 @@ namespace Luski.net public static class Channels { - public static string GetKey(long channel) + private static string GetKey(long channel) { LuskiDataFile? fileLayout; IEnumerable? lis; try { #pragma warning disable CS8603 // Possible null reference return. - if (channel == 0) return myPrivateKey; + // if (channel == 0) return myPrivateKey; #pragma warning restore CS8603 // Possible null reference return. MakeFile("Server.GetKeyFilePath", pw); fileLayout = JsonSerializer.Deserialize(FileString("Server.GetKeyFilePath", pw)); @@ -144,7 +114,7 @@ namespace Luski.net try { #pragma warning disable CS8603 // Possible null reference return. - if (channel == 0) return myPrivateKey; + // if (channel == 0) return myPrivateKey; #pragma warning restore CS8603 // Possible null reference return. MakeFile("Server.GetKeyFilePathBr(branch.ToString())", pw); fileLayout = JsonSerializer.Deserialize(FileString("", pw)); @@ -273,8 +243,7 @@ namespace Luski.net public string key { get; set; } = default!; } } - /* - public class AES +public class AES { public static string Encrypt(string path, string Password) { @@ -301,12 +270,10 @@ namespace Luski.net msEncrypt.Dispose(); return p; - /* - - string p = Path.GetTempFileName(); + string p = Path.GetTempFileName(); byte[] salt = new byte[100]; RNGCryptoServiceProvider provider = new(); provider.GetBytes(salt); @@ -378,27 +345,7 @@ namespace Luski.net fsOut.Dispose(); } } -*/ - internal const int PasswordVersion = 0; - internal static byte[] LocalPasswordEncrypt(byte[] Password, int PasswordVersion = PasswordVersion) - { - return PasswordVersion switch - { - 0 => SHA256.Create().ComputeHash(Password), - _ => throw new ArgumentException("The value provided was not accepted", nameof(PasswordVersion)), - }; - } - - internal static string RemotePasswordEncrypt(byte[] Password, int PasswordVersion = PasswordVersion) - { - return PasswordVersion switch - { - 0 => Convert.ToBase64String(Encryption.RSA.Encrypt(LocalPasswordEncrypt(Password, PasswordVersion), MyPublicKey)), - _ => throw new ArgumentException("The value provided was not accepted", nameof(PasswordVersion)), - }; - } -/* public static byte[] Hash(byte[] data) { return SHA256.Create().ComputeHash(data); @@ -506,6 +453,6 @@ namespace Luski.net } } return datasplitout; - }*/ + } } -} +}*/ diff --git a/Luski.net/Enums/ServerCacheMode.cs b/Luski.net/Enums/CacheMode.cs similarity index 67% rename from Luski.net/Enums/ServerCacheMode.cs rename to Luski.net/Enums/CacheMode.cs index fa9b152..6fc1243 100644 --- a/Luski.net/Enums/ServerCacheMode.cs +++ b/Luski.net/Enums/CacheMode.cs @@ -1,6 +1,6 @@ namespace Luski.net.Enums; -public enum ServerCacheMode : byte +public enum CacheMode : byte { None, Encrypted, diff --git a/Luski.net/Enums/StorageDirectory.cs b/Luski.net/Enums/StorageDirectory.cs new file mode 100644 index 0000000..73266bc --- /dev/null +++ b/Luski.net/Enums/StorageDirectory.cs @@ -0,0 +1,12 @@ +namespace Luski.net.Enums; + +public enum StorageDirectory : byte +{ + ServerInfo, + ServerAssets, + ChannelKeys, + ServerKeys, + Avatars, + ChannelIcons, + Messages +} \ No newline at end of file diff --git a/Luski.net/JsonTypes/ServerStorageInfo.cs b/Luski.net/JsonTypes/ServerStorageInfo.cs new file mode 100644 index 0000000..c54ce8b --- /dev/null +++ b/Luski.net/JsonTypes/ServerStorageInfo.cs @@ -0,0 +1,24 @@ +using System.Text.Json.Serialization; +using Luski.net.Enums; + +namespace Luski.net.JsonTypes; + +public class ServerStorageInfo +{ + [JsonInclude] + [JsonPropertyName("cache_mode")] + public CacheMode CacheMode { get; set; } = CacheMode.Encrypted; + [JsonInclude] + [JsonPropertyName("prevent_deletion")] + public bool DontDelete { get; set; } = false; +} + +[JsonSerializable(typeof(ServerStorageInfo))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + WriteIndented = true)] +internal partial class ServerStorageInfoContext : JsonSerializerContext +{ + +} \ No newline at end of file diff --git a/Luski.net/Luski.net.csproj b/Luski.net/Luski.net.csproj index 7e53d0b..c2118d4 100755 --- a/Luski.net/Luski.net.csproj +++ b/Luski.net/Luski.net.csproj @@ -13,7 +13,7 @@ https://github.com/JacobTech-com/Luski.net True 1.0.0 - 1.1.3-alpha24 + 1.1.3-alpha25 diff --git a/Luski.net/MainServer.Account.cs b/Luski.net/MainServer.Account.cs index 72d3bf7..421d6fb 100644 --- a/Luski.net/MainServer.Account.cs +++ b/Luski.net/MainServer.Account.cs @@ -13,47 +13,46 @@ using Luski.net.Enums; using Luski.net.JsonTypes; using Luski.net.JsonTypes.WSS; using Luski.net.Structures.Main; -using Luski.net.Structures.Public; using WebSocketSharp; namespace Luski.net; public partial class MainServer { - public void Login(string Email, string Password, System.Threading.CancellationToken CancellationToken) + public void Login(string Username, string Password, CancellationToken CancellationToken) { - Both(Email, Password, CancellationToken); + Both(Username, Password, CancellationToken); } - public void CreateAccount(string Email, string Password, string Username, string PFP, System.Threading.CancellationToken CancellationToken) + public void CreateAccount(string Username, string Password, string Displayname, string PFP, CancellationToken CancellationToken) { - Both(Email, Password, CancellationToken, Username, PFP); + Both(Username, Password, CancellationToken, Displayname, PFP); } - private void Both(string Email, string Password, System.Threading.CancellationToken CancellationToken, string? Username = null, string? pfp = null) + private void Both(string Username, string Password, CancellationToken CancellationToken, string? Displayname = null, string? pfp = null) { - if (!ClientEncryption.Generating) + if (!EncryptionHandler.Generating) { - ClientEncryption.GenerateKeys(); + EncryptionHandler.GenerateKeys(); } - while (!ClientEncryption.Generated) { } + while (!EncryptionHandler.Generated) { } login = true; Login json; List> heads = new() { - new("key", ClientEncryption.MyPublicKey), - new("username", Convert.ToBase64String(Encryption.RSA.Encrypt(Email, ClientEncryption.MyPublicKey, EncoderType.UTF8))), - new("password", ClientEncryption.RemotePasswordEncrypt(Encoding.UTF8.GetBytes(Password))) + new("key", EncryptionHandler.MyPublicKey), + new("username", Convert.ToBase64String(Encryption.RSA.Encrypt(Username, EncryptionHandler.MyPublicKey, EncoderType.UTF8))), + new("password", EncryptionHandler.RemotePasswordEncrypt(Encoding.UTF8.GetBytes(Password))) }; - if (File.Exists("LastPassVer.txt") && int.TryParse(File.ReadAllText("LastPassVer.txt"), out int lpv) && lpv < ClientEncryption.PasswordVersion && lpv >= 0) + if (File.Exists("LastPassVer.txt") && int.TryParse(File.ReadAllText("LastPassVer.txt"), out int lpv) && lpv < EncryptionHandler.PasswordVersion && lpv >= 0) { - heads.Add(new("old_password", ClientEncryption.RemotePasswordEncrypt(Encoding.UTF8.GetBytes(Password), lpv))); + heads.Add(new("old_password", EncryptionHandler.RemotePasswordEncrypt(Encoding.UTF8.GetBytes(Password), lpv))); heads.Add(new("old_version", lpv.ToString())); } if (pfp is not null) { - heads.Add(new("username", Username)); + heads.Add(new("username", Displayname)); json = SendServer( "CreateAccount", pfp, @@ -70,7 +69,7 @@ public partial class MainServer heads.ToArray()).Result; } if (json.Error is not null) throw new Exception($"Luski appears to be down at the current moment: {json.ErrorMessage}"); - if (ClientEncryption.ofkey is null || ClientEncryption.outofkey is null) throw new Exception("Something went wrong generating the offline keys"); + if (EncryptionHandler.OfflinePrivateKey is null || EncryptionHandler.OfflinePublicKey is null) throw new Exception("Something went wrong generating the offline keys"); login = false; if (json is not null && json.Error is null) { @@ -106,31 +105,9 @@ public partial class MainServer } Console.WriteLine("User got"); Console.WriteLine("Insert"); - //User.Email = Email; _ = UpdateStatus(UserStatus.Online, CancellationToken); Console.WriteLine("stat"); - try - { - ClientEncryption.pw = Email.ToLower() + Password; - _ = ClientEncryption.File.GetOfflineKey(); - } - catch - { - try - { - ClientEncryption.pw = Email + Password; - var temp222 = ClientEncryption.File.LuskiDataFile.GetDefualtDataFile(); - ClientEncryption.pw = Email.ToLower() + Password; - if (temp222 is not null) temp222.Save("key.lsk", ClientEncryption.pw); - } - catch - { - Token = null; - Error = null; - ServerOut.Close(); - throw new Exception("The key file you have is getting the wrong pasword. Type your Email in the same way you creaated your account to fix this error."); - } - } + EncryptionHandler.Hash = EncryptionHandler.LocalPasswordEncrypt(Encoding.UTF8.GetBytes(Username.ToLower() + Password)); Console.WriteLine("req offline"); OfflineData offlinedata = GetFromServer("Keys/GetOfflineData", OfflineDataContext.Default.OfflineData, CancellationToken).Result; if (offlinedata.Data is not null && offlinedata.Data.Any()) Console.WriteLine(offlinedata.Data); @@ -145,23 +122,37 @@ public partial class MainServer { Console.WriteLine(okd.channel); Console.WriteLine(okd.key); - ClientEncryption.File.Channels.AddKey(okd.channel, Encoding.UTF8.GetString(Encryption.RSA.Decrypt(Convert.FromBase64String(okd.key), ClientEncryption.File.GetOfflineKey()))); + Storage.SetResourceKey( + StorageDirectory.ChannelKeys, + okd.channel.ToString(), + EncryptionHandler.Hash, + Encoding.UTF8.GetString( + Encryption.RSA.Decrypt( + Convert.FromBase64String(okd.key), + Storage.GetResourceKey( + StorageDirectory.ServerKeys, + "pkey", + EncryptionHandler.Hash + ) + ) + ) + ); } } } Console.WriteLine("lpv"); - System.IO.File.WriteAllText("LastPassVer.txt", ClientEncryption.PasswordVersion.ToString()); - ClientEncryption.File.SetOfflineKey(ClientEncryption.ofkey); + System.IO.File.WriteAllText("LastPassVer.txt", EncryptionHandler.PasswordVersion.ToString()); + Storage.SetResourceKey(StorageDirectory.ServerKeys, "pkey", EncryptionHandler.Hash, EncryptionHandler.OfflinePrivateKey); using HttpClient setkey = new(); setkey.DefaultRequestHeaders.Add("token", Token); - _ = setkey.PostAsync($"https://{Domain}/{ApiVersion}/Keys/SetOfflineKey", new StringContent(ClientEncryption.outofkey)).Result; + _ = setkey.PostAsync($"https://{Domain}/{ApiVersion}/Keys/SetOfflineKey", new StringContent(EncryptionHandler.OfflinePublicKey)).Result; //_ = User.Channels; foreach (var ch in chans) { _ = ch.Members; } - ClientEncryption.outofkey = null; - ClientEncryption.ofkey = null; + EncryptionHandler.OfflinePublicKey = null; + EncryptionHandler.OfflinePrivateKey = null; } else throw new Exception(json?.ErrorMessage); } diff --git a/Luski.net/MainServer.Incoming.cs b/Luski.net/MainServer.Incoming.cs index 7d769e6..db82b06 100644 --- a/Luski.net/MainServer.Incoming.cs +++ b/Luski.net/MainServer.Incoming.cs @@ -1,4 +1,5 @@ using System; +using System.Text; using System.Text.Json; using System.Threading; using JacobTechEncryption; @@ -46,7 +47,8 @@ public partial class MainServer MainSocketMessage? m = JsonSerializer.Deserialize(e.Data); if (m is not null) { - m.decrypt(ClientEncryption.File.Channels.GetKey(m.ChannelID), CancellationToken.None); + m.decrypt(Storage.GetResourceKey(StorageDirectory.ChannelKeys, m.ChannelID.ToString(), + EncryptionHandler.Hash), CancellationToken.None); _ = MessageReceived.Invoke(m); } } @@ -94,8 +96,19 @@ public partial class MainServer { KeyExchange? KE = JsonSerializer.Deserialize(e.Data); if (KE is not null) - ClientEncryption.File.Channels.AddKey(KE.channel, - ClientEncryption.Encoder.GetString(Encryption.RSA.Decrypt(Convert.FromBase64String(KE.key), ClientEncryption.ofkey))); + { + Storage.SetResourceKey( + StorageDirectory.ChannelKeys, + KE.channel.ToString(), + EncryptionHandler.Hash, + Encoding.UTF8.GetString( + Encryption.RSA.Decrypt( + Convert.FromBase64String(KE.key), + EncryptionHandler.myPrivateKey + ) + ) + ); + } } catch (Exception ex) { diff --git a/Luski.net/MainServer.cs b/Luski.net/MainServer.cs index 1b23222..2076b42 100644 --- a/Luski.net/MainServer.cs +++ b/Luski.net/MainServer.cs @@ -5,6 +5,7 @@ using System.Text.Json.Serialization.Metadata; using System.Threading; using System.Threading.Tasks; using Luski.net.Enums; +using Luski.net.Enums.Main; using Luski.net.Interfaces; using Luski.net.JsonTypes.BaseTypes; using Luski.net.JsonTypes.HTTP; @@ -67,6 +68,8 @@ public partial class MainServer : Server chans.Remove(p); } } + + request.Server = this; chans.Add(request); return request; } @@ -88,6 +91,9 @@ public partial class MainServer : Server case MainSocketAppUser: user = (GetUser(UserID, MainSocketAppUserContext.Default.MainSocketAppUser, CancellationToken).Result as Tuser)!; break; + case MainSocketRemoteUser: + user = (GetUser(UserID, MainSocketRemoteUserContext.Default.MainSocketRemoteUser, CancellationToken).Result as Tuser)!; + break; case MainSocketUserBase: user = (GetUser(UserID, MainSocketUserBaseContext.Default.MainSocketUserBase, CancellationToken).Result as Tuser)!; break; @@ -110,11 +116,16 @@ public partial class MainServer : Server message = await GetFromServer("socketmessage", MainSocketMessageContext.Default.MainSocketMessage, CancellationToken, - new System.Collections.Generic.KeyValuePair("msg_id", id.ToString())); + new KeyValuePair("msg_id", id.ToString())); break; } } - if (message is not null) return message; + + if (message is not null) + { + message.Server = this; + return message; + } throw new Exception("Server did not return a message"); } @@ -143,6 +154,16 @@ public partial class MainServer : Server if (poeople.Count > 0 && poeople.Any(s => s.Id == UserId)) { Tuser temp = poeople.Where(s => s is Tuser && s.Id == UserId).Cast().FirstOrDefault()!; + if (temp is MainSocketRemoteUser && (temp as MainSocketRemoteUser)!.Channel == null) + { + foreach (MainSocketDMChannel chan in chans.Where(s => s is MainSocketDMChannel).Cast()) + { + if (chan.Type == ChannelType.DM && chan.Id != 0 && chan.MemberIdList is not null) + { + if (chan.MemberIdList.Any(s => s == UserId)) (temp as MainSocketRemoteUser)!.Channel = chan; + } + } + } return temp; } while (true) @@ -165,6 +186,18 @@ public partial class MainServer : Server poeople.Remove(p); } } + + user.Server = this; + if (user is MainSocketRemoteUser && (user as MainSocketRemoteUser)!.Channel == null) + { + foreach (MainSocketDMChannel chan in chans.Where(s => s is MainSocketDMChannel).Cast()) + { + if (chan.Type == ChannelType.DM && chan.Id != 0 && chan.MemberIdList is not null) + { + if (chan.MemberIdList.Any(s => s == UserId)) (user as MainSocketRemoteUser)!.Channel = chan; + } + } + } poeople.Add(user); return user; } diff --git a/Luski.net/Server.Globals.cs b/Luski.net/Server.Globals.cs index 1a81f79..120898e 100644 --- a/Luski.net/Server.Globals.cs +++ b/Luski.net/Server.Globals.cs @@ -15,50 +15,10 @@ public partial class Server { public ServerType ServerType { get; internal set; } = ServerType.Public; public string Domain { get; internal set; } = default!; - public ServerCacheMode CacheMode { get; set; } = ServerCacheMode.None; public string ApiVersion { get; internal set; } = "v1"; internal WebSocket? ServerOut; internal string? Token = null, Error = null, gen = null; internal bool CanRequest = false, login = false; internal List poeople = new(); - public long UserID { get; internal set; } = 0; internal List chans { get; set; } = new(); - public string Cache - { - get - { - if (gen is null) - { - if (!Directory.Exists(JT)) Directory.CreateDirectory(JT); - string path = JT + "/Luski/"; - if (!Directory.Exists(path)) Directory.CreateDirectory(path); - path += "Data/"; - if (!Directory.Exists(path)) Directory.CreateDirectory(path); - path += UserID + "/"; - if (!Directory.Exists(path)) Directory.CreateDirectory(path); - path += "Cache/"; - if (!Directory.Exists(path)) Directory.CreateDirectory(path); - path += Path.GetRandomFileName() + "/"; - if (!Directory.Exists(path)) Directory.CreateDirectory(path); - gen = path; - } - if (!Directory.Exists($"{gen}/avatars")) Directory.CreateDirectory($"{gen}/avatars"); - if (!Directory.Exists($"{gen}/channels")) Directory.CreateDirectory($"{gen}/channels"); - return gen; - } - } - - 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; - } - } } \ No newline at end of file diff --git a/Luski.net/Server.cs b/Luski.net/Server.cs index 6420dbd..7dfbbde 100644 --- a/Luski.net/Server.cs +++ b/Luski.net/Server.cs @@ -10,12 +10,7 @@ using System.Threading; using System.Threading.Tasks; using Luski.net.Enums; using Luski.net.Interfaces; -using Luski.net.JsonTypes; using Luski.net.JsonTypes.BaseTypes; -using Luski.net.JsonTypes.HTTP; -using Luski.net.Structures; -using Luski.net.Structures.Main; -using Luski.net.Structures.Public; using File = System.IO.File; namespace Luski.net; @@ -23,16 +18,17 @@ namespace Luski.net; public partial class Server { internal Server() - { } + { + } + + public ServerEncryption EncryptionHandler { get; internal set; } = null!; + public ServerStorage Storage { get; internal set; } = null!; public async Task GetAvatar(CancellationToken CancellationToken) { - if (Cache != null) - { - bool isc = File.Exists($"{Cache}/servers/{Domain}"); - if (!isc) await GetFromServer($"socketserver/Avatar/", $"{Cache}/servers/{Domain}-{ApiVersion}", CancellationToken); - } - return File.ReadAllBytes($"{Cache}/servers/{Domain}"); + bool isc = File.Exists(Storage.GetStorageDirectory(StorageDirectory.ServerAssets) + "Icon"); + if (!isc) await GetFromServer($"socketserver/Avatar/", Storage.GetStorageDirectory(StorageDirectory.ServerAssets) + "Icon", CancellationToken); + return Storage.GetResourceBytes(StorageDirectory.ServerAssets, "Icon"); } public void SendServer(Tvalue Payload, JsonTypeInfo jsonTypeInfo) where Tvalue : IncomingWSS diff --git a/Luski.net/ServerEncryption.cs b/Luski.net/ServerEncryption.cs new file mode 100644 index 0000000..ef7b8e2 --- /dev/null +++ b/Luski.net/ServerEncryption.cs @@ -0,0 +1,56 @@ +using System; +using System.Security.Cryptography; +using JacobTechEncryption; + +namespace Luski.net; + +public class ServerEncryption +{ + internal bool Generating, Generated; + internal string ServerPublicKey = "", MyPublicKey = "", myPrivateKey = "", OfflinePrivateKey = "", OfflinePublicKey = ""; + internal byte[] Hash = default!; + internal ServerEncryption(string Domain) + { + //TODO Get server p key + } + + internal int PasswordVersion = 0; + internal byte[] LocalPasswordEncrypt(byte[] Password) => LocalPasswordEncrypt(Password, PasswordVersion); + internal string RemotePasswordEncrypt(byte[] Password) => RemotePasswordEncrypt(Password, PasswordVersion); + + internal byte[] LocalPasswordEncrypt(byte[] Password, int PasswordVersion) + { + return PasswordVersion switch + { + 0 => SHA256.Create().ComputeHash(Password), + _ => throw new ArgumentException("The value provided was not accepted", nameof(PasswordVersion)), + }; + } + + internal string RemotePasswordEncrypt(byte[] Password, int PasswordVersion) + { + return PasswordVersion switch + { + 0 => Convert.ToBase64String(Encryption.RSA.Encrypt(LocalPasswordEncrypt(Password, PasswordVersion), ServerPublicKey)), + _ => throw new ArgumentException("The value provided was not accepted", nameof(PasswordVersion)), + }; + } + + public void GenerateKeys() + { + if (!Generating) + { + Generating = true; + GenerateNewKeys(out MyPublicKey, out myPrivateKey); + GenerateNewKeys(out OfflinePublicKey, out OfflinePrivateKey); + Generated = true; + } + } + + public static void GenerateNewKeys(out string Public, out string Private, int KeySize = 4096) + { + using RSACryptoServiceProvider r = new(KeySize); + Private = r.ToXmlString(true); + Public = r.ToXmlString(false); + } +} \ No newline at end of file diff --git a/Luski.net/ServerStorage.cs b/Luski.net/ServerStorage.cs new file mode 100644 index 0000000..f492e36 --- /dev/null +++ b/Luski.net/ServerStorage.cs @@ -0,0 +1,116 @@ +using System; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.Json; +using JacobTechEncryption; +using Luski.net.Enums; +using Luski.net.JsonTypes; + +namespace Luski.net; + +public class ServerStorage +{ + public static readonly string[] Directories = new[] + { + "Info", + "Assets", + "Channels/Keys", + "Keys", + "Avatars", + "Channels/Icons", + "Channels/Messages" + }; + + private static readonly int[] CantDelete = new[] + { + (int)StorageDirectory.ChannelKeys, + (int)StorageDirectory.ServerKeys + }; + + 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+ "/"; + 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)); + ServerStorageInfo info = JsonSerializer.Deserialize(File.ReadAllText(Location + "storage.json"), + ServerStorageInfoContext.Default.ServerStorageInfo)!; + CacheMode = info.CacheMode; + for (int i = 0; i < Directories.Length; i++) + { + string full = Location; + string[] spl = Directories[i].Split('/'); + if (!info.DontDelete && !CantDelete.Contains(i)) + { + try + { + if (Directory.Exists(full + spl[0])) Directory.Delete(full + spl[0], true); + } + catch + { + // ignored + } + } + + foreach (string d in spl) + { + full += d + "/"; + if (!Directory.Exists(full)) Directory.CreateDirectory(full); + } + } + } + + public string Location { get; internal set; } + + public string GetStorageDirectory(StorageDirectory Directory) + { + return Location + Directories[(byte)Directory] + "/"; + } + + public string GetResourceKey(StorageDirectory Directory, string Resource, string Key) + { + return Encoding.UTF8.GetString(Encryption.AES.Decrypt(GetResourceBytes(Directory, Resource), Key)); + } + + public string GetResourceKey(StorageDirectory Directory, string Resource, byte[] Key) + { + return Encoding.UTF8.GetString(Encryption.AES.Decrypt(GetResourceBytes(Directory, Resource), Encoding.UTF8.GetString(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), Encoding.UTF8.GetString(Key))); + } + + public byte[] GetResourceBytes(StorageDirectory Directory, string Resource) + { + return File.ReadAllBytes(Location + Directories[(byte)Directory] + "/" + Resource); + } + + 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; + } + } +} \ No newline at end of file diff --git a/Luski.net/Structures/File.cs b/Luski.net/Structures/File.cs index 8c3e76f..d012a44 100755 --- a/Luski.net/Structures/File.cs +++ b/Luski.net/Structures/File.cs @@ -18,7 +18,7 @@ namespace Luski.net.Structures; public class File : IncomingHTTP { - public IServer Server { get; internal set; } + public MainServer Server { get; internal set; } [JsonInclude] [JsonPropertyName("name")] public string Name { get; internal set; } = default!; @@ -35,6 +35,8 @@ public class File : IncomingHTTP public async Task DownloadBytes(string Loc, long key, CancellationToken CancellationToken) { + //TODO make better + //using HttpClient web = new(); //web.DefaultRequestHeaders.Add("token", Server.Token); //web.DefaultRequestHeaders.Add("id", id.ToString()); @@ -42,7 +44,7 @@ public class File : IncomingHTTP string path = Path.GetTempFileName(); await Server.GetFromServer($"SocketMessage/GetFile/{Id}", path, CancellationToken); - string Key = (key == 0 ? ClientEncryption.MyPublicKey : ClientEncryption.File.Channels.GetKey(key))!; + string Key = (key == 0 ? Server.EncryptionHandler.MyPublicKey : "") ;// ClientEncryption.File.Channels.GetKey(key))!; Encryption.AES.DecryptToFile(System.IO.File.ReadAllBytes(path), Key, Loc); /* if (request is not null && request.Error is not null) diff --git a/Luski.net/Structures/Main/MainSocketChannel.cs b/Luski.net/Structures/Main/MainSocketChannel.cs index 5d5bdd2..050c19c 100755 --- a/Luski.net/Structures/Main/MainSocketChannel.cs +++ b/Luski.net/Structures/Main/MainSocketChannel.cs @@ -70,7 +70,8 @@ public class MainSocketChannel : IncomingHTTP } int num = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * 50) * 2.0)); if (num == 0) num = 1; - string? lkey = ClientEncryption.File.Channels.GetKey(Id); + string lkey = Server.Storage.GetResourceKey(StorageDirectory.ChannelKeys, Id.ToString(), + Server.EncryptionHandler.Hash); Parallel.ForEach(Members, new ParallelOptions() { MaxDegreeOfParallelism = num @@ -95,7 +96,9 @@ public class MainSocketChannel : IncomingHTTP } internal Task StartKeyProcessAsync(CancellationToken CancellationToken) - {/* + { + //TODO code key exchange + /* ClientEncryption.GenerateNewKeys(out string Public, out string Private); Key = Public; HttpResponseMessage b; diff --git a/Luski.net/Structures/Main/MainSocketRemoteUser.cs b/Luski.net/Structures/Main/MainSocketRemoteUser.cs index 34ab431..83bdd63 100755 --- a/Luski.net/Structures/Main/MainSocketRemoteUser.cs +++ b/Luski.net/Structures/Main/MainSocketRemoteUser.cs @@ -18,6 +18,7 @@ public class MainSocketRemoteUser : MainSocketUserBase [JsonPropertyName("friend_status")] [JsonInclude] public FriendStatus FriendStatus { get; internal set; } = default!; + public MainSocketDMChannel Channel { get; internal set; } internal MainSocketRemoteUser Clone() { diff --git a/Luski.net/Structures/Main/MainSocketTextChannel.cs b/Luski.net/Structures/Main/MainSocketTextChannel.cs index f07062a..3147f0c 100755 --- a/Luski.net/Structures/Main/MainSocketTextChannel.cs +++ b/Luski.net/Structures/Main/MainSocketTextChannel.cs @@ -27,12 +27,9 @@ public class MainSocketTextChannel : MainSocketChannel if (Type == ChannelType.DM) return Members.First().GetAvatar(CancellationToken).Result; else { - if (Server.Cache != null) - { - bool isc = System.IO.File.Exists($"{Server.Cache}/channels/{Id}"); - if (!isc) await Server.GetFromServer($"SocketChannel/GetPicture/{Id}", $"{Server.Cache}/channels/{Id}", CancellationToken); - } - return System.IO.File.ReadAllBytes($"{Server.Cache}/channels/{Id}"); + bool isc = System.IO.File.Exists(Server.Storage.GetStorageDirectory(StorageDirectory.ChannelIcons) + Id.ToString()); + if (!isc) await Server.GetFromServer($"SocketChannel/GetPicture/{Id}", Server.Storage.GetStorageDirectory(StorageDirectory.ChannelIcons) + Id.ToString(), CancellationToken); + return Server.Storage.GetResourceBytes(StorageDirectory.ChannelIcons, Id.ToString()); } } @@ -59,7 +56,8 @@ public class MainSocketTextChannel : MainSocketChannel int num = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * 5) * 2.0)); if (num == 0) num = 1; - string? key = ClientEncryption.File.Channels.GetKey(Id); + string key = Server.Storage.GetResourceKey(StorageDirectory.ChannelKeys, Id.ToString(), + Server.EncryptionHandler.Hash); if (data is null) throw new Exception("Invalid data from server"); if (data.Messages is null) data.Messages = Array.Empty(); Parallel.ForEach(data.Messages, new ParallelOptions() @@ -105,7 +103,8 @@ public class MainSocketTextChannel : MainSocketChannel { int num = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * 5) * 2.0)); if (num == 0) num = 1; - string? key = ClientEncryption.File.Channels.GetKey(Id); + string key = Server.Storage.GetResourceKey(StorageDirectory.ChannelKeys, Id.ToString(), + Server.EncryptionHandler.Hash); if (data.Messages is null) data.Messages = Array.Empty(); Parallel.ForEach(data.Messages, new ParallelOptions() { @@ -140,8 +139,8 @@ public class MainSocketTextChannel : MainSocketChannel public async Task SendMessage(string Message, CancellationToken CancellationToken, params File?[] Files) { - string key = ClientEncryption.File.Channels.GetKey(Id); - if (Id == 0) key = "";//ClientEncryption.ServerPublicKey; + string key = Server.Storage.GetResourceKey(StorageDirectory.ChannelKeys, Id.ToString(), + Server.EncryptionHandler.Hash); JsonTypes.HTTP.Message m = new() { Context = Convert.ToBase64String(Encryption.RSA.Encrypt(Message, key, EncoderType.UTF8)), diff --git a/Luski.net/Structures/Main/MainSocketUserBase.cs b/Luski.net/Structures/Main/MainSocketUserBase.cs index 7b63322..35c9ded 100644 --- a/Luski.net/Structures/Main/MainSocketUserBase.cs +++ b/Luski.net/Structures/Main/MainSocketUserBase.cs @@ -24,12 +24,9 @@ public class MainSocketUserBase : IncomingHTTP, IUser public PictureType PictureType { get; internal set; } = default!; public async Task GetAvatar(CancellationToken CancellationToken) { - if (Server.Cache != null) - { - bool isc = System.IO.File.Exists($"{Server.Cache}/avatars/{Id}"); - if (!isc) await Server.GetFromServer($"socketuserprofile/Avatar/{Id}", $"{Server.Cache}/avatars/{Id}", CancellationToken); - } - return System.IO.File.ReadAllBytes($"{Server.Cache}/avatars/{Id}"); + bool isc = System.IO.File.Exists(Server.Storage.GetStorageDirectory(StorageDirectory.Avatars) + Id.ToString()); + if (!isc) await Server.GetFromServer($"socketuserprofile/Avatar/{Id}", Server.Storage.GetStorageDirectory(StorageDirectory.Avatars) + Id.ToString(), CancellationToken); + return Server.Storage.GetResourceBytes(StorageDirectory.Avatars, Id.ToString()); } public Task GetUserKey(CancellationToken CancellationToken) diff --git a/Luski.net/Structures/Public/MainSocketUserBase.cs b/Luski.net/Structures/Public/PublicSocketUserBase.cs similarity index 79% rename from Luski.net/Structures/Public/MainSocketUserBase.cs rename to Luski.net/Structures/Public/PublicSocketUserBase.cs index 0252a20..4fab6c2 100644 --- a/Luski.net/Structures/Public/MainSocketUserBase.cs +++ b/Luski.net/Structures/Public/PublicSocketUserBase.cs @@ -24,12 +24,9 @@ public class PublicSocketUserBase : IncomingHTTP, IUser public PictureType PictureType { get; internal set; } = default!; public async Task GetAvatar(CancellationToken CancellationToken) { - if (Server.Cache != null) - { - bool isc = System.IO.File.Exists($"{Server.Cache}/avatars/{Id}"); - if (!isc) await Server.GetFromServer($"socketuserprofile/Avatar/{Id}", $"{Server.Cache}/avatars/{Id}", CancellationToken); - } - return System.IO.File.ReadAllBytes($"{Server.Cache}/avatars/{Id}"); + bool isc = System.IO.File.Exists(Server.Storage.GetStorageDirectory(StorageDirectory.Avatars) + Id.ToString()); + if (!isc) await Server.GetFromServer($"socketuserprofile/Avatar/{Id}", Server.Storage.GetStorageDirectory(StorageDirectory.Avatars) + Id.ToString(), CancellationToken); + return Server.Storage.GetResourceBytes(StorageDirectory.Avatars, Id.ToString()); } public Task GetUserKey(CancellationToken CancellationToken) From ea39d93cf582428138e28dbe9d015b80ec433964 Mon Sep 17 00:00:00 2001 From: JacobTech Date: Mon, 10 Jul 2023 14:19:03 -0400 Subject: [PATCH 05/14] Friends. Friends have been copied to the main server. --- Luski.net/API.cs | 12 +--- Luski.net/MainServer.Incoming.cs | 16 ++--- Luski.net/MainServer.cs | 72 +++++++++++++++++++ Luski.net/PublicServer.cs | 5 +- Luski.net/Server.Globals.cs | 4 +- Luski.net/Server.cs | 10 ++- Luski.net/ServerEncryption.cs | 16 ++++- .../Structures/Main/MainSocketChannel.cs | 3 +- .../Structures/Main/MainSocketTextChannel.cs | 11 ++- 9 files changed, 113 insertions(+), 36 deletions(-) diff --git a/Luski.net/API.cs b/Luski.net/API.cs index c9bd04d..5934520 100644 --- a/Luski.net/API.cs +++ b/Luski.net/API.cs @@ -16,12 +16,8 @@ public class API { IEnumerable isl = InternalServers.Where(a => (a.Domain == Domain && a.ApiVersion == Version)); if (isl.Any()) return isl.First(); - PublicServer s = new() + PublicServer s = new(Domain, Version) { - Domain = Domain, - ApiVersion = Version, - Storage = new(Domain), - EncryptionHandler = new(Domain), ServerType = ServerType.Public }; InternalServers.Add(s); @@ -30,12 +26,8 @@ public class API public MainServer GetMainServer(string Domain, string Version = "v1") { - MainServer = new() + MainServer = new(Domain, Version) { - Domain = Domain, - ApiVersion = Version, - Storage = new(Domain), - EncryptionHandler = new(Domain), ServerType = ServerType.Main }; return MainServer; diff --git a/Luski.net/MainServer.Incoming.cs b/Luski.net/MainServer.Incoming.cs index db82b06..457df1a 100644 --- a/Luski.net/MainServer.Incoming.cs +++ b/Luski.net/MainServer.Incoming.cs @@ -47,8 +47,7 @@ public partial class MainServer MainSocketMessage? m = JsonSerializer.Deserialize(e.Data); if (m is not null) { - m.decrypt(Storage.GetResourceKey(StorageDirectory.ChannelKeys, m.ChannelID.ToString(), - EncryptionHandler.Hash), CancellationToken.None); + m.decrypt(EncryptionHandler.GetChannelKey(m.ChannelID), CancellationToken.None); _ = MessageReceived.Invoke(m); } } @@ -97,15 +96,12 @@ public partial class MainServer KeyExchange? KE = JsonSerializer.Deserialize(e.Data); if (KE is not null) { - Storage.SetResourceKey( - StorageDirectory.ChannelKeys, - KE.channel.ToString(), - EncryptionHandler.Hash, + EncryptionHandler.SetChannelKey( + KE.channel, Encoding.UTF8.GetString( - Encryption.RSA.Decrypt( - Convert.FromBase64String(KE.key), - EncryptionHandler.myPrivateKey - ) + Encryption.RSA.Decrypt( + Convert.FromBase64String(KE.key), + EncryptionHandler.myPrivateKey) ) ); } diff --git a/Luski.net/MainServer.cs b/Luski.net/MainServer.cs index 2076b42..979bc85 100644 --- a/Luski.net/MainServer.cs +++ b/Luski.net/MainServer.cs @@ -1,12 +1,16 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net; +using System.Text; using System.Text.Json.Serialization.Metadata; using System.Threading; using System.Threading.Tasks; +using JacobTechEncryption; using Luski.net.Enums; using Luski.net.Enums.Main; using Luski.net.Interfaces; +using Luski.net.JsonTypes; using Luski.net.JsonTypes.BaseTypes; using Luski.net.JsonTypes.HTTP; using Luski.net.Structures.Main; @@ -16,8 +20,76 @@ namespace Luski.net; public partial class MainServer : Server { + internal MainServer(string Domain, string API_Version): + base(Domain, API_Version) + { + } + public MainSocketAppUser User { get; internal set; } = default!; + public async Task SendFriendResult(long user, bool answer, CancellationToken CancellationToken) + { + FriendRequestResult json = await SendServer("FriendRequestResult", + new FriendRequestResultOut() + { + Id = user, + Result = answer + }, + FriendRequestResultOutContext.Default.FriendRequestResultOut, + FriendRequestResultContext.Default.FriendRequestResult, + CancellationToken); + + if (json is not null && json.Error is null && json.ErrorMessage is null && answer && json.Channel is not null) + { + MainSocketDMChannel chan = await GetChannel((long)json.Channel, MainSocketDMChannelContext.Default.MainSocketDMChannel, CancellationToken); + _ = chan.StartKeyProcessAsync(CancellationToken); + chans.Add(chan); + } + else + { + throw new Exception(json?.Error.ToString()); + } + return GetUser(user, MainSocketRemoteUserContext.Default.MainSocketRemoteUser, CancellationToken).Result; + } + + public async Task SendFriendRequest(long code, CancellationToken CancellationToken) + { + string ccode = Convert.ToBase64String(Encryption.Hashing.SHA256(Encoding.Unicode.GetBytes(code.ToString()))); + FriendRequestResult? json = await SendServer("FriendRequest", new FriendRequest() { code = ccode}, FriendRequestContext.Default.FriendRequest, FriendRequestResultContext.Default.FriendRequestResult, CancellationToken); + + if (json.StatusCode != HttpStatusCode.Accepted) + { + if (json is not null && json.Error is not null) + { + switch ((ErrorCode)(int)json.Error) + { + case ErrorCode.InvalidToken: + throw new Exception("Your current token is no longer valid"); + case ErrorCode.ServerError: + throw new Exception($"Error from server: {json.ErrorMessage}"); + case ErrorCode.InvalidPostData: + throw new Exception("The post data dent to the server is not the correct format. This may be because you app is couropt or you are using the wron API version"); + case ErrorCode.Forbidden: + throw new Exception("You already have an outgoing request or the persone is not real"); + } + } + + if (json is not null && json.Channel is not null) + { + MainSocketDMChannel chan = await GetChannel((long)json.Channel, MainSocketDMChannelContext.Default.MainSocketDMChannel, CancellationToken); + _ = chan.StartKeyProcessAsync(CancellationToken); + chans.Add(chan); + } + } + + MainSocketRemoteUser b = await GetUser(code, MainSocketRemoteUserContext.Default.MainSocketRemoteUser, CancellationToken); + if (json.Channel is not null) + b.FriendStatus = FriendStatus.Friends; + else + b.FriendStatus = FriendStatus.PendingOut; + return b; + } + public async Task GetChannel(long Channel, CancellationToken CancellationToken) where TChannel : MainSocketChannel, new() { TChannel Return = new(); diff --git a/Luski.net/PublicServer.cs b/Luski.net/PublicServer.cs index 63bbd3e..f23f971 100644 --- a/Luski.net/PublicServer.cs +++ b/Luski.net/PublicServer.cs @@ -2,5 +2,8 @@ namespace Luski.net; public class PublicServer : Server { - + internal PublicServer(string Domain, string API_Version): + base(Domain, API_Version) + { + } } \ No newline at end of file diff --git a/Luski.net/Server.Globals.cs b/Luski.net/Server.Globals.cs index 120898e..df5b3f1 100644 --- a/Luski.net/Server.Globals.cs +++ b/Luski.net/Server.Globals.cs @@ -14,8 +14,8 @@ namespace Luski.net; public partial class Server { public ServerType ServerType { get; internal set; } = ServerType.Public; - public string Domain { get; internal set; } = default!; - public string ApiVersion { get; internal set; } = "v1"; + public string Domain { get; } = default!; + public string ApiVersion { get; } = "v1"; internal WebSocket? ServerOut; internal string? Token = null, Error = null, gen = null; internal bool CanRequest = false, login = false; diff --git a/Luski.net/Server.cs b/Luski.net/Server.cs index 7dfbbde..4aad3bc 100644 --- a/Luski.net/Server.cs +++ b/Luski.net/Server.cs @@ -17,12 +17,16 @@ namespace Luski.net; public partial class Server { - internal Server() + internal Server(string Domain, string API_Version) { + this.Domain = Domain; + this.ApiVersion = API_Version; + Storage = new(Domain); + EncryptionHandler = new(Domain, API_Version, Storage); } - public ServerEncryption EncryptionHandler { get; internal set; } = null!; - public ServerStorage Storage { get; internal set; } = null!; + public ServerEncryption EncryptionHandler { get; } + public ServerStorage Storage { get; } public async Task GetAvatar(CancellationToken CancellationToken) { diff --git a/Luski.net/ServerEncryption.cs b/Luski.net/ServerEncryption.cs index ef7b8e2..057b698 100644 --- a/Luski.net/ServerEncryption.cs +++ b/Luski.net/ServerEncryption.cs @@ -1,6 +1,7 @@ using System; using System.Security.Cryptography; using JacobTechEncryption; +using Luski.net.Enums; namespace Luski.net; @@ -9,10 +10,23 @@ public class ServerEncryption internal bool Generating, Generated; internal string ServerPublicKey = "", MyPublicKey = "", myPrivateKey = "", OfflinePrivateKey = "", OfflinePublicKey = ""; internal byte[] Hash = default!; - internal ServerEncryption(string Domain) + internal ServerEncryption(string Domain, string API_Version, ServerStorage Storage) { + this.Storage = Storage; //TODO Get server p key } + + public string GetChannelKey(long Channel) + { + return Storage.GetResourceKey(StorageDirectory.ChannelKeys, Channel.ToString(), Hash); + } + + public void SetChannelKey(long Channel, string Key) + { + Storage.SetResourceKey(StorageDirectory.ChannelKeys, Channel.ToString(), Hash, Key); + } + + private ServerStorage Storage { get; } internal int PasswordVersion = 0; internal byte[] LocalPasswordEncrypt(byte[] Password) => LocalPasswordEncrypt(Password, PasswordVersion); diff --git a/Luski.net/Structures/Main/MainSocketChannel.cs b/Luski.net/Structures/Main/MainSocketChannel.cs index 050c19c..cc67919 100755 --- a/Luski.net/Structures/Main/MainSocketChannel.cs +++ b/Luski.net/Structures/Main/MainSocketChannel.cs @@ -70,8 +70,7 @@ public class MainSocketChannel : IncomingHTTP } int num = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * 50) * 2.0)); if (num == 0) num = 1; - string lkey = Server.Storage.GetResourceKey(StorageDirectory.ChannelKeys, Id.ToString(), - Server.EncryptionHandler.Hash); + string lkey = Server.EncryptionHandler.GetChannelKey(Id); Parallel.ForEach(Members, new ParallelOptions() { MaxDegreeOfParallelism = num diff --git a/Luski.net/Structures/Main/MainSocketTextChannel.cs b/Luski.net/Structures/Main/MainSocketTextChannel.cs index 3147f0c..a8cae22 100755 --- a/Luski.net/Structures/Main/MainSocketTextChannel.cs +++ b/Luski.net/Structures/Main/MainSocketTextChannel.cs @@ -55,9 +55,8 @@ public class MainSocketTextChannel : MainSocketChannel { int num = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * 5) * 2.0)); if (num == 0) num = 1; - - string key = Server.Storage.GetResourceKey(StorageDirectory.ChannelKeys, Id.ToString(), - Server.EncryptionHandler.Hash); + + string key = Server.EncryptionHandler.GetChannelKey(Id); if (data is null) throw new Exception("Invalid data from server"); if (data.Messages is null) data.Messages = Array.Empty(); Parallel.ForEach(data.Messages, new ParallelOptions() @@ -103,8 +102,7 @@ public class MainSocketTextChannel : MainSocketChannel { int num = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * 5) * 2.0)); if (num == 0) num = 1; - string key = Server.Storage.GetResourceKey(StorageDirectory.ChannelKeys, Id.ToString(), - Server.EncryptionHandler.Hash); + string key = Server.EncryptionHandler.GetChannelKey(Id); if (data.Messages is null) data.Messages = Array.Empty(); Parallel.ForEach(data.Messages, new ParallelOptions() { @@ -139,8 +137,7 @@ public class MainSocketTextChannel : MainSocketChannel public async Task SendMessage(string Message, CancellationToken CancellationToken, params File?[] Files) { - string key = Server.Storage.GetResourceKey(StorageDirectory.ChannelKeys, Id.ToString(), - Server.EncryptionHandler.Hash); + string key = Server.EncryptionHandler.GetChannelKey(Id); JsonTypes.HTTP.Message m = new() { Context = Convert.ToBase64String(Encryption.RSA.Encrypt(Message, key, EncoderType.UTF8)), From 9f641e7429846378da7c348e5d8035ff2564cee4 Mon Sep 17 00:00:00 2001 From: JacobTech Date: Mon, 21 Aug 2023 10:58:17 -0400 Subject: [PATCH 06/14] Public Servers. Added a bunch of API calls to use in public servers. --- Luski.net/API.cs | 56 ++- Luski.net/Classes/RoleComparer.cs | 13 + Luski.net/Converters.cs | 8 + Luski.net/Enums/PictureType.cs | 1 + Luski.net/Enums/Public/DataType.cs | 7 + Luski.net/Enums/Public/ServerPermission.cs | 38 ++ Luski.net/Enums/StorageDirectory.cs | 4 +- Luski.net/Interfaces/IServerEvent.cs | 6 + Luski.net/Interfaces/IUser.cs | 10 +- Luski.net/JsonTypes/BaseTypes/IncomingWSS.cs | 2 +- Luski.net/JsonTypes/Login.cs | 1 + Luski.net/JsonTypes/Public/Category.cs | 63 ++++ Luski.net/JsonTypes/Public/Channel.cs | 71 ++++ Luski.net/JsonTypes/Public/PublicMessage.cs | 32 ++ .../Public/PublicSocketBulkMessage.cs | 22 ++ Luski.net/JsonTypes/Public/Role.cs | 28 ++ Luski.net/JsonTypes/Public/SocketUser.cs | 41 ++ Luski.net/JsonTypes/Public/pFile.cs | 32 ++ Luski.net/JsonTypes/ServerInfo.cs | 22 ++ Luski.net/JsonTypes/ServerStorageInfo.cs | 3 + Luski.net/JsonTypes/StorageInfoJSON.cs | 22 ++ Luski.net/JsonTypes/UserKeyGetRequest.cs | 23 ++ Luski.net/JsonTypes/UserKeysGetRequest.cs | 19 + Luski.net/JsonTypes/WSS/IncomingWSS.cs | 24 ++ Luski.net/JsonTypes/WSS/ServerEvent.cs | 16 + Luski.net/JsonTypes/WSS/WSSLogin.cs | 6 +- Luski.net/JsonTypes/WSS/WSSOut.cs | 23 ++ Luski.net/Luski.net.csproj | 7 +- Luski.net/MainServer.Account.cs | 41 +- Luski.net/MainServer.cs | 7 +- Luski.net/PublicServer.Account.cs | 353 ++++++++++++++++++ Luski.net/PublicServer.Incoming.cs | 38 ++ Luski.net/PublicServer.cs | 297 ++++++++++++++- ...rverEncryption.cs => Server.Encryption.cs} | 20 +- Luski.net/Server.Globals.cs | 2 +- Luski.net/Server.Storage.cs | 306 +++++++++++++++ Luski.net/Server.cs | 80 +++- Luski.net/ServerStorage.cs | 116 ------ Luski.net/Structures/LocalKeyInfo.cs | 9 + .../Structures/Main/MainSocketAppUser.cs | 39 +- .../Structures/Main/MainSocketChannel.cs | 4 +- .../Structures/Main/MainSocketTextChannel.cs | 4 +- .../Structures/Main/MainSocketUserBase.cs | 10 +- Luski.net/Structures/Public/Color.cs | 40 ++ .../Structures/Public/PublicSocketAppUser.cs | 36 -- .../Structures/Public/PublicSocketUserBase.cs | 48 --- Luski.net/Structures/Public/Role.cs | 29 ++ Luski.net/Structures/Public/RoleOveride.cs | 21 ++ Luski.net/Structures/Public/SocketAppUser.cs | 17 + Luski.net/Structures/Public/SocketCategory.cs | 106 ++++++ Luski.net/Structures/Public/SocketChannel.cs | 217 +++++++++++ Luski.net/Structures/Public/SocketFile.cs | 28 ++ Luski.net/Structures/Public/SocketMessage.cs | 49 +++ Luski.net/Structures/Public/SocketUser.cs | 64 ++++ Luski.net/Structures/Public/UserOveride.cs | 6 + Luski.net/Structures/PublicKeyInfo.cs | 11 + 56 files changed, 2307 insertions(+), 291 deletions(-) create mode 100644 Luski.net/Classes/RoleComparer.cs create mode 100644 Luski.net/Converters.cs create mode 100644 Luski.net/Enums/Public/DataType.cs create mode 100644 Luski.net/Enums/Public/ServerPermission.cs create mode 100644 Luski.net/Interfaces/IServerEvent.cs create mode 100644 Luski.net/JsonTypes/Public/Category.cs create mode 100644 Luski.net/JsonTypes/Public/Channel.cs create mode 100644 Luski.net/JsonTypes/Public/PublicMessage.cs create mode 100644 Luski.net/JsonTypes/Public/PublicSocketBulkMessage.cs create mode 100644 Luski.net/JsonTypes/Public/Role.cs create mode 100644 Luski.net/JsonTypes/Public/SocketUser.cs create mode 100644 Luski.net/JsonTypes/Public/pFile.cs create mode 100644 Luski.net/JsonTypes/ServerInfo.cs create mode 100644 Luski.net/JsonTypes/StorageInfoJSON.cs create mode 100644 Luski.net/JsonTypes/UserKeyGetRequest.cs create mode 100644 Luski.net/JsonTypes/UserKeysGetRequest.cs create mode 100755 Luski.net/JsonTypes/WSS/IncomingWSS.cs create mode 100644 Luski.net/JsonTypes/WSS/ServerEvent.cs create mode 100644 Luski.net/JsonTypes/WSS/WSSOut.cs create mode 100644 Luski.net/PublicServer.Account.cs create mode 100644 Luski.net/PublicServer.Incoming.cs rename Luski.net/{ServerEncryption.cs => Server.Encryption.cs} (76%) create mode 100644 Luski.net/Server.Storage.cs delete mode 100644 Luski.net/ServerStorage.cs create mode 100644 Luski.net/Structures/LocalKeyInfo.cs create mode 100644 Luski.net/Structures/Public/Color.cs delete mode 100755 Luski.net/Structures/Public/PublicSocketAppUser.cs delete mode 100644 Luski.net/Structures/Public/PublicSocketUserBase.cs create mode 100644 Luski.net/Structures/Public/Role.cs create mode 100644 Luski.net/Structures/Public/RoleOveride.cs create mode 100755 Luski.net/Structures/Public/SocketAppUser.cs create mode 100644 Luski.net/Structures/Public/SocketCategory.cs create mode 100644 Luski.net/Structures/Public/SocketChannel.cs create mode 100644 Luski.net/Structures/Public/SocketFile.cs create mode 100644 Luski.net/Structures/Public/SocketMessage.cs create mode 100644 Luski.net/Structures/Public/SocketUser.cs create mode 100644 Luski.net/Structures/Public/UserOveride.cs create mode 100644 Luski.net/Structures/PublicKeyInfo.cs diff --git a/Luski.net/API.cs b/Luski.net/API.cs index 5934520..951adbe 100644 --- a/Luski.net/API.cs +++ b/Luski.net/API.cs @@ -1,8 +1,9 @@ +using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Threading.Tasks; using Luski.net.Enums; -using Luski.net.Structures.Main; -using Luski.net.Structures.Public; namespace Luski.net; @@ -12,24 +13,63 @@ public class API internal List InternalServers { get; } = new(); public IReadOnlyList LoadedServers => InternalServers.AsReadOnly(); - public PublicServer GetPublicServer(string Domain, string Version = "v1") + public async Task GetPublicServer(string Domain, string Version = "v1", bool Secure = true) { - IEnumerable isl = InternalServers.Where(a => (a.Domain == Domain && a.ApiVersion == Version)); - if (isl.Any()) return isl.First(); - PublicServer s = new(Domain, Version) + DateTime dt = DateTime.UtcNow; + Console.WriteLine("Connecting to public server '{0}' using API {1}.", Domain, Version); + PublicServer s; + try { - ServerType = ServerType.Public - }; + IEnumerable isl = InternalServers.Where(a => (a.Domain == Domain && a.ApiVersion == Version)); + if (isl.Any()) return isl.First(); + s = new(Domain, Version, Secure) + { + ServerType = ServerType.Public + }; + } + catch (Exception e) + { + Console.WriteLine("Failed to connect to public server '{0}' using API {1}.", Domain, Version); + throw; + } + Console.WriteLine("Connected to public server '{0}' using API {1} in {4}.\nServer Name: {2}\nServer Description: {3}", Domain, Version, s.Name, s.Description, DateTime.UtcNow.Subtract(dt).ToString("g")); + string? f = s.Storage.GetStorageDirectory(StorageDirectory.StorageInfo) + "token"; + if (File.Exists(f)) + { + f = File.ReadAllText(f); + } + else f = null; + + try + { + if (f is not null) + { + bool b = await s.LoginViaToken(f); + if (b) Console.WriteLine("Auto Login Successful"); + else + { + s.Token = null; + s.Error = null; + } + } + } + catch + { + } + InternalServers.Add(s); return s; } public MainServer GetMainServer(string Domain, string Version = "v1") { + DateTime dt = DateTime.UtcNow; + Console.WriteLine("Conecting to main server '{0}' using API {1}.", Domain, Version); MainServer = new(Domain, Version) { ServerType = ServerType.Main }; + Console.WriteLine("Connected to main server '{0}' using API {1} in {2}.", Domain, Version, DateTime.UtcNow.Subtract(dt).ToString("g")); return MainServer; } } \ No newline at end of file diff --git a/Luski.net/Classes/RoleComparer.cs b/Luski.net/Classes/RoleComparer.cs new file mode 100644 index 0000000..78405dc --- /dev/null +++ b/Luski.net/Classes/RoleComparer.cs @@ -0,0 +1,13 @@ +using System.Collections; +using System.Collections.Generic; +using Luski.net.Structures.Public; + +namespace Luski.net.Classes; + +public class RoleComparer : IComparer +{ + public int Compare(Role x, Role y) + { + return y.Index - x.Index; + } +} \ No newline at end of file diff --git a/Luski.net/Converters.cs b/Luski.net/Converters.cs new file mode 100644 index 0000000..edd770e --- /dev/null +++ b/Luski.net/Converters.cs @@ -0,0 +1,8 @@ +using Luski.net.JsonTypes.Public; +using Luski.net.Structures.Public; + +namespace Luski.net; + +public static class Converters +{ +} \ No newline at end of file diff --git a/Luski.net/Enums/PictureType.cs b/Luski.net/Enums/PictureType.cs index ca5686b..721bc28 100755 --- a/Luski.net/Enums/PictureType.cs +++ b/Luski.net/Enums/PictureType.cs @@ -2,6 +2,7 @@ public enum PictureType : short { + none, png, jpeg, bmp, diff --git a/Luski.net/Enums/Public/DataType.cs b/Luski.net/Enums/Public/DataType.cs new file mode 100644 index 0000000..723e4bf --- /dev/null +++ b/Luski.net/Enums/Public/DataType.cs @@ -0,0 +1,7 @@ +namespace Luski.net.Enums.Public; + +public enum DataType +{ + Token, + MessageCreate +} \ No newline at end of file diff --git a/Luski.net/Enums/Public/ServerPermission.cs b/Luski.net/Enums/Public/ServerPermission.cs new file mode 100644 index 0000000..89c9ac4 --- /dev/null +++ b/Luski.net/Enums/Public/ServerPermission.cs @@ -0,0 +1,38 @@ +namespace Luski.net.Enums.Public; + +public enum ServerPermission : long +{ + ViewChannels, + MoveChannels, + EditChannels, + EditChannelPermissions, + CreateChannels, + DeleteChannels, + ViewCategories, + MoveCategories, + EditCategories, + EditCategoryPermissions, + CreateCategories, + DeleteCategories, + DeleteKeys, + ManageRoles, + ViewLogs, + ManageServer, + Invite, + Nickname, + ManageNacknames, + Kick, + Ban, + SendMessages, + SendFiles, + ChannelAndServerPings, + PingSomeone, + ManageMessages, + ReadMessageHistory, + UseServerCommands, + JoinVoice, + SpeakInVoice, + MuteMembers, + DeafenMembers, + MoveMembers +} \ No newline at end of file diff --git a/Luski.net/Enums/StorageDirectory.cs b/Luski.net/Enums/StorageDirectory.cs index 73266bc..2f32ac4 100644 --- a/Luski.net/Enums/StorageDirectory.cs +++ b/Luski.net/Enums/StorageDirectory.cs @@ -8,5 +8,7 @@ public enum StorageDirectory : byte ServerKeys, Avatars, ChannelIcons, - Messages + Messages, + StorageInfo, + Files } \ No newline at end of file diff --git a/Luski.net/Interfaces/IServerEvent.cs b/Luski.net/Interfaces/IServerEvent.cs new file mode 100644 index 0000000..fbf895f --- /dev/null +++ b/Luski.net/Interfaces/IServerEvent.cs @@ -0,0 +1,6 @@ +namespace Luski.net.Interfaces; + +public interface IServerEvent +{ + +} \ No newline at end of file diff --git a/Luski.net/Interfaces/IUser.cs b/Luski.net/Interfaces/IUser.cs index 26d8bc5..609b996 100755 --- a/Luski.net/Interfaces/IUser.cs +++ b/Luski.net/Interfaces/IUser.cs @@ -1,7 +1,9 @@ -using System.Threading; +using System.IO; +using System.Threading; using Luski.net.Enums; using System.Threading.Tasks; using Luski.net.JsonTypes; +using Luski.net.Structures; namespace Luski.net.Interfaces; @@ -29,11 +31,11 @@ public interface IUser /// /// Gets the current avatar of the user /// - Task GetAvatar(CancellationToken CancellationToken); + Task GetAvatar(CancellationToken CancellationToken); /// - /// Gets the current user key + /// Gets the current user keys /// /// - Task GetUserKey(CancellationToken CancellationToken); + Task GetUserKeys(CancellationToken CancellationToken); } diff --git a/Luski.net/JsonTypes/BaseTypes/IncomingWSS.cs b/Luski.net/JsonTypes/BaseTypes/IncomingWSS.cs index 66b4fd9..84b380d 100755 --- a/Luski.net/JsonTypes/BaseTypes/IncomingWSS.cs +++ b/Luski.net/JsonTypes/BaseTypes/IncomingWSS.cs @@ -7,7 +7,7 @@ public class IncomingWSS { [JsonPropertyName("type")] [JsonInclude] - public DataType? Type { get; set; } = default!; + public DataType? Type { get; set; } = DataType.Login!; [JsonPropertyName("error")] [JsonInclude] public string Error { get; set; } = default!; diff --git a/Luski.net/JsonTypes/Login.cs b/Luski.net/JsonTypes/Login.cs index 8492d70..be52591 100755 --- a/Luski.net/JsonTypes/Login.cs +++ b/Luski.net/JsonTypes/Login.cs @@ -7,6 +7,7 @@ internal class Login : IncomingHTTP { [JsonPropertyName("login_token")] public string? Token { get; set; } = default!; + } [JsonSerializable(typeof(Login))] diff --git a/Luski.net/JsonTypes/Public/Category.cs b/Luski.net/JsonTypes/Public/Category.cs new file mode 100644 index 0000000..ba19fd2 --- /dev/null +++ b/Luski.net/JsonTypes/Public/Category.cs @@ -0,0 +1,63 @@ +using System.Text.Json.Serialization; +using JacobTechEncryption.Enums; +using Luski.net.Enums; +using Luski.net.JsonTypes.BaseTypes; + +namespace Luski.net.JsonTypes.Public; + +public class Category : IncomingHTTP +{ + [JsonInclude] + [JsonPropertyName("id")] + public long ID { get; set; } + [JsonInclude] + [JsonPropertyName("picture_type")] + public PictureType PictureType { get; set; } + [JsonInclude] + [JsonPropertyName("color")] + public string Color { get; set; } + [JsonInclude] + [JsonPropertyName("name")] + public string Name { get; set; } + [JsonInclude] + [JsonPropertyName("description")] + public string Description { get; set; } + [JsonInclude] + [JsonPropertyName("parent")] + public long Parent { get; set; } + [JsonInclude] + [JsonPropertyName("inner_categories")] + public long[] InnerCategories { get; set; } + [JsonInclude] + [JsonPropertyName("channels")] + public long[] Channels { get; set; } + [JsonInclude] + [JsonPropertyName("role_overides")] + public long[] RoleOverides { get; set; } + [JsonInclude] + [JsonPropertyName("member_overides")] + public long[] UserOverides { get; set; } + [JsonInclude] + [JsonPropertyName("title_encryption_key")] + public long TitleEncryptionKey { get; set; } + [JsonInclude] + [JsonPropertyName("description_encryption_key")] + public long DescriptionEncryptionKey { get; set; } + [JsonInclude] + [JsonPropertyName("title_encoder_type")] + public EncoderType TitleEncoderType { get; set; } + [JsonInclude] + [JsonPropertyName("description_encoder_type")] + public EncoderType DescriptionEncoderType { get; set; } +} + +[JsonSerializable(typeof(Category))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.Unspecified, + WriteIndented = false, + DefaultIgnoreCondition = JsonIgnoreCondition.Never)] +internal partial class PublicCategoryContext : JsonSerializerContext +{ + +} \ No newline at end of file diff --git a/Luski.net/JsonTypes/Public/Channel.cs b/Luski.net/JsonTypes/Public/Channel.cs new file mode 100644 index 0000000..938cb4b --- /dev/null +++ b/Luski.net/JsonTypes/Public/Channel.cs @@ -0,0 +1,71 @@ +using System; +using System.Text.Json.Serialization; +using JacobTechEncryption.Enums; +using Luski.net.Enums; +using Luski.net.Enums.Public; +using Luski.net.JsonTypes.BaseTypes; + +namespace Luski.net.JsonTypes.Public; + +public class Channel : IncomingHTTP +{ + [JsonInclude] + [JsonPropertyName("id")] + public long ID { get; set; } + [JsonInclude] + [JsonPropertyName("parent")] + public long Parent { get; set; } + [JsonInclude] + [JsonPropertyName("color")] + public string Color { get; set; } + [JsonInclude] + [JsonPropertyName("type")] + public ChannelType Type { get; set; } + [JsonInclude] + [JsonPropertyName("epoch")] + public DateTime Epoch { get; set; } + [JsonInclude] + [JsonPropertyName("name")] + public string Name { get; set; } + [JsonInclude] + [JsonPropertyName("description")] + public string Description { get; set; } + [JsonInclude] + [JsonPropertyName("role_overides")] + public long[] RoleOverides { get; set; } + [JsonInclude] + [JsonPropertyName("member_overides")] + public long[] UserOverides { get; set; } + [JsonInclude] + [JsonPropertyName("title_encryption_key")] + public long TitleEncryptionKey { get; set; } + [JsonInclude] + [JsonPropertyName("description_encryption_key")] + public long DescriptionEncryptionKey { get; set; } + [JsonInclude] + [JsonPropertyName("encryption_keys")] + public long[] EncryptionKeys { get; set; } + [JsonInclude] + [JsonPropertyName("title_encoder_type")] + public EncoderType TitleEncoderType { get; set; } + [JsonInclude] + [JsonPropertyName("description_encoder_type")] + public EncoderType DescriptionEncoderType { get; set; } + [JsonInclude] + [JsonPropertyName("encoder_types")] + public EncoderType[] EncoderTypes { get; set; } + [JsonInclude] + [JsonPropertyName("picture_type")] + public PictureType PictureType { get; set; } +} + +[JsonSerializable(typeof(Channel))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.Unspecified, + WriteIndented = false, + DefaultIgnoreCondition = JsonIgnoreCondition.Never)] +internal partial class PublicChannelContext : JsonSerializerContext +{ + +} \ No newline at end of file diff --git a/Luski.net/JsonTypes/Public/PublicMessage.cs b/Luski.net/JsonTypes/Public/PublicMessage.cs new file mode 100644 index 0000000..d06f627 --- /dev/null +++ b/Luski.net/JsonTypes/Public/PublicMessage.cs @@ -0,0 +1,32 @@ +using System.Text.Json.Serialization; +using JacobTechEncryption.Enums; + +namespace Luski.net.JsonTypes.Public; + +public class PublicMessage +{ + [JsonInclude] + [JsonPropertyName("id")] + public long ID { get; internal set; } + [JsonInclude] + [JsonPropertyName("channel_id")] + public long ChannelID { get; internal set; } + [JsonInclude] + [JsonPropertyName("author_id")] + public long AuthorID { get; internal set; } + [JsonInclude] + [JsonPropertyName("ts")] + public long TimeStamp { get; internal set; } + [JsonInclude] + [JsonPropertyName("context")] + public string Context { get; internal set; } + [JsonInclude] + [JsonPropertyName("encryption_key")] + public long EncryptionKey { get; internal set; } + [JsonInclude] + [JsonPropertyName("files")] + public pFile[] Files { get; internal set; } + [JsonInclude] + [JsonPropertyName("encoder_type")] + public EncoderType EncoderType { get; internal set; } +} \ No newline at end of file diff --git a/Luski.net/JsonTypes/Public/PublicSocketBulkMessage.cs b/Luski.net/JsonTypes/Public/PublicSocketBulkMessage.cs new file mode 100644 index 0000000..50ebef3 --- /dev/null +++ b/Luski.net/JsonTypes/Public/PublicSocketBulkMessage.cs @@ -0,0 +1,22 @@ +using System.Text.Json.Serialization; +using Luski.net.JsonTypes.BaseTypes; + +namespace Luski.net.JsonTypes.Public; + +public class PublicSocketBulkMessage : IncomingHTTP +{ + [JsonInclude] + [JsonPropertyName("messages")] + public PublicMessage[]? Messages { get; set; } = default!; +} + +[JsonSerializable(typeof(PublicSocketBulkMessage))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + WriteIndented = false, + DefaultIgnoreCondition = JsonIgnoreCondition.Never)] +internal partial class PublicSocketBulkMessageContext : JsonSerializerContext +{ + +} \ No newline at end of file diff --git a/Luski.net/JsonTypes/Public/Role.cs b/Luski.net/JsonTypes/Public/Role.cs new file mode 100644 index 0000000..7b32ed0 --- /dev/null +++ b/Luski.net/JsonTypes/Public/Role.cs @@ -0,0 +1,28 @@ +using System.Text.Json.Serialization; +using Luski.net.Enums.Public; +using Luski.net.JsonTypes.BaseTypes; + +namespace Luski.net.JsonTypes.Public; + +public class Role : IncomingHTTP +{ + public long id { get; set; } + public string name { get; set; } + public string display_name { get; set; } + public int index { get; set; } + public string color { get; set; } + public string description { get; set; } + public ServerPermission[] server_permissions { get; set; } + public long[] members_list { get; set; } +} + +[JsonSerializable(typeof(Role))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.Unspecified, + WriteIndented = false, + DefaultIgnoreCondition = JsonIgnoreCondition.Never)] +internal partial class RoleContext : JsonSerializerContext +{ + +} \ No newline at end of file diff --git a/Luski.net/JsonTypes/Public/SocketUser.cs b/Luski.net/JsonTypes/Public/SocketUser.cs new file mode 100644 index 0000000..bd8ba68 --- /dev/null +++ b/Luski.net/JsonTypes/Public/SocketUser.cs @@ -0,0 +1,41 @@ +using System.Text.Json.Serialization; +using Luski.net.Enums; +using Luski.net.JsonTypes.BaseTypes; + +namespace Luski.net.JsonTypes.Public; + +public class SocketUser : IncomingHTTP +{ + [JsonInclude] + [JsonPropertyName("id")] + public long ID { get; internal set; } = default!; + [JsonInclude] + [JsonPropertyName("displayname")] + public string DisplayName { get; internal set; } = default!; + [JsonInclude] + [JsonPropertyName("selected_channel")] + public long SelectedChannel { get; internal set; } = default!; + [JsonInclude] + [JsonPropertyName("status")] + public UserStatus Status { get; internal set; } = default!; + [JsonInclude] + [JsonPropertyName("picture_type")] + public PictureType PictureType { get; internal set; } = default!; + [JsonInclude] + [JsonPropertyName("roles")] + public long[] RoleIds { get; internal set; } = default!; + [JsonInclude] + [JsonPropertyName("username")] + public string Username { get; internal set; } = default!; +} + +[JsonSerializable(typeof(SocketUser))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.Unspecified, + WriteIndented = false, + DefaultIgnoreCondition = JsonIgnoreCondition.Never)] +internal partial class PublicSocketUserContext : JsonSerializerContext +{ + +} \ No newline at end of file diff --git a/Luski.net/JsonTypes/Public/pFile.cs b/Luski.net/JsonTypes/Public/pFile.cs new file mode 100644 index 0000000..0a749ba --- /dev/null +++ b/Luski.net/JsonTypes/Public/pFile.cs @@ -0,0 +1,32 @@ +using System.Text.Json.Serialization; +using JacobTechEncryption.Enums; + +namespace Luski.net.JsonTypes.Public; + +public class pFile +{ + [JsonInclude] + [JsonPropertyName("id")] + public long ID { get; set; } + [JsonInclude] + [JsonPropertyName("name")] + public string Name { get; set; } + [JsonInclude] + [JsonPropertyName("channel")] + public long Channel { get; set; } + [JsonInclude] + [JsonPropertyName("encoder_type")] + public EncoderType Encoder { get; set; } + [JsonInclude] + [JsonPropertyName("name_encoder_type")] + public EncoderType NameEncoder { get; set; } + [JsonInclude] + [JsonPropertyName("encryption_key")] + public long Key { get; set; } + [JsonInclude] + [JsonPropertyName("name_encryption_key")] + public long NameKey { get; set; } + [JsonInclude] + [JsonPropertyName("size")] + public long Size { get; set; } +} \ No newline at end of file diff --git a/Luski.net/JsonTypes/ServerInfo.cs b/Luski.net/JsonTypes/ServerInfo.cs new file mode 100644 index 0000000..04685a8 --- /dev/null +++ b/Luski.net/JsonTypes/ServerInfo.cs @@ -0,0 +1,22 @@ +using System.Text.Json.Serialization; +using Luski.net.JsonTypes.BaseTypes; + +namespace Luski.net.JsonTypes; + +public class ServerInfo : IncomingHTTP +{ + public string name { get; set; } + public string wssv4 { get; set; } + public string description { get; set; } + public long owner { get; set; } +} + +[JsonSerializable(typeof(ServerInfo))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + WriteIndented = false)] +internal partial class ServerInfoContext : JsonSerializerContext +{ + +} \ No newline at end of file diff --git a/Luski.net/JsonTypes/ServerStorageInfo.cs b/Luski.net/JsonTypes/ServerStorageInfo.cs index c54ce8b..fc87170 100644 --- a/Luski.net/JsonTypes/ServerStorageInfo.cs +++ b/Luski.net/JsonTypes/ServerStorageInfo.cs @@ -9,6 +9,9 @@ public class ServerStorageInfo [JsonPropertyName("cache_mode")] public CacheMode CacheMode { get; set; } = CacheMode.Encrypted; [JsonInclude] + [JsonPropertyName("storage_id")] + public long StorageID { get; set; } = default!; + [JsonInclude] [JsonPropertyName("prevent_deletion")] public bool DontDelete { get; set; } = false; } diff --git a/Luski.net/JsonTypes/StorageInfoJSON.cs b/Luski.net/JsonTypes/StorageInfoJSON.cs new file mode 100644 index 0000000..40ce429 --- /dev/null +++ b/Luski.net/JsonTypes/StorageInfoJSON.cs @@ -0,0 +1,22 @@ +using System.Text.Json.Serialization; +using Luski.net.Interfaces; +using Luski.net.JsonTypes.BaseTypes; + +namespace Luski.net.JsonTypes; + +public class StorageInfoJSON : IncomingHTTP, IWebRequest +{ + public long id { get; set; } + public string password { get; set; } + public bool update { get; set; } +} + +[JsonSerializable(typeof(StorageInfoJSON))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + WriteIndented = true)] +internal partial class StorageInfoJSONContext : JsonSerializerContext +{ + +} \ No newline at end of file diff --git a/Luski.net/JsonTypes/UserKeyGetRequest.cs b/Luski.net/JsonTypes/UserKeyGetRequest.cs new file mode 100644 index 0000000..7cae546 --- /dev/null +++ b/Luski.net/JsonTypes/UserKeyGetRequest.cs @@ -0,0 +1,23 @@ +using System.Text.Json.Serialization; +using JacobTechEncryption.Enums; +using Luski.net.Interfaces; + +namespace Luski.net.JsonTypes; + +public class UserKeyGetRequest : IWebRequest +{ + public long id { get; set; } + public long owner { get; set; } + public EncryptionType encryption_type { get; set; } + public string key_data { get; set; } +} + +[JsonSerializable(typeof(UserKeyGetRequest))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + WriteIndented = false)] +internal partial class UserKeyGetRequestContext : JsonSerializerContext +{ + +} \ No newline at end of file diff --git a/Luski.net/JsonTypes/UserKeysGetRequest.cs b/Luski.net/JsonTypes/UserKeysGetRequest.cs new file mode 100644 index 0000000..3e140e9 --- /dev/null +++ b/Luski.net/JsonTypes/UserKeysGetRequest.cs @@ -0,0 +1,19 @@ +using System.Text.Json.Serialization; +using Luski.net.JsonTypes.BaseTypes; + +namespace Luski.net.JsonTypes; + +public class UserKeysGetRequest : IncomingHTTP +{ + public UserKeyGetRequest[] keys { get; set; } +} + +[JsonSerializable(typeof(UserKeysGetRequest))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + WriteIndented = true)] +internal partial class UserKeysGetRequestContext : JsonSerializerContext +{ + +} \ No newline at end of file diff --git a/Luski.net/JsonTypes/WSS/IncomingWSS.cs b/Luski.net/JsonTypes/WSS/IncomingWSS.cs new file mode 100755 index 0000000..d7fce29 --- /dev/null +++ b/Luski.net/JsonTypes/WSS/IncomingWSS.cs @@ -0,0 +1,24 @@ +using Luski.net.Enums.Public; +using System.Text.Json.Serialization; + +namespace Luski.net.JsonTypes.BaseTypes; + +public class BetterIncomingWSS +{ + [JsonPropertyName("type")] + [JsonInclude] + public DataType? Type { get; set; } = DataType.Token!; + [JsonPropertyName("error")] + [JsonInclude] + public string Error { get; set; } = default!; +} + +[JsonSerializable(typeof(BetterIncomingWSS))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.Unspecified, + WriteIndented = false)] +public partial class BetterIncomingWSSContext : JsonSerializerContext +{ + +} diff --git a/Luski.net/JsonTypes/WSS/ServerEvent.cs b/Luski.net/JsonTypes/WSS/ServerEvent.cs new file mode 100644 index 0000000..7ce8406 --- /dev/null +++ b/Luski.net/JsonTypes/WSS/ServerEvent.cs @@ -0,0 +1,16 @@ +using System.Text.Json.Serialization; +using Luski.net.Enums.Public; +using Luski.net.Interfaces; + +namespace Luski.net.JsonTypes.WSS; + +public class ServerEvent +{ + [JsonInclude] + [JsonPropertyName("type")] + public DataType Type { get; set; } + + [JsonInclude] + [JsonPropertyName("data")] + public object Data { get; set; } +} \ No newline at end of file diff --git a/Luski.net/JsonTypes/WSS/WSSLogin.cs b/Luski.net/JsonTypes/WSS/WSSLogin.cs index d50b8bc..7f1c690 100755 --- a/Luski.net/JsonTypes/WSS/WSSLogin.cs +++ b/Luski.net/JsonTypes/WSS/WSSLogin.cs @@ -1,14 +1,18 @@ using Luski.net.Enums; using Luski.net.JsonTypes.BaseTypes; using System.Text.Json.Serialization; +using Luski.net.Interfaces; namespace Luski.net.JsonTypes.WSS; -internal class WSSLogin : IncomingWSS +internal class WSSLogin : IncomingWSS, IServerEvent { [JsonPropertyName("token")] [JsonInclude] public string Token { get; set; } = default!; + [JsonPropertyName("session_token")] + [JsonInclude] + public string? SessionToken { get; set; } = default!; [JsonPropertyName("type")] [JsonInclude] new public DataType? Type { get; set; } = DataType.Login; diff --git a/Luski.net/JsonTypes/WSS/WSSOut.cs b/Luski.net/JsonTypes/WSS/WSSOut.cs new file mode 100644 index 0000000..581f28a --- /dev/null +++ b/Luski.net/JsonTypes/WSS/WSSOut.cs @@ -0,0 +1,23 @@ +using System.Text.Json.Serialization; + +namespace Luski.net.JsonTypes.WSS; + +public class WSSOut +{ + [JsonInclude] + [JsonPropertyName("data")] + public ServerEvent Data { get; set; } + [JsonInclude] + [JsonPropertyName("send_type")] + public int SendType { get; set; } = 1; +} + +[JsonSerializable(typeof(WSSOut))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + WriteIndented = false)] +internal partial class WSSOutContext : JsonSerializerContext +{ + +} \ No newline at end of file diff --git a/Luski.net/Luski.net.csproj b/Luski.net/Luski.net.csproj index c2118d4..a003ead 100755 --- a/Luski.net/Luski.net.csproj +++ b/Luski.net/Luski.net.csproj @@ -1,7 +1,7 @@  - net6.0 + net7.0 disable enable true @@ -13,14 +13,15 @@ https://github.com/JacobTech-com/Luski.net True 1.0.0 - 1.1.3-alpha25 + 2.0.0-alpha12 - + + diff --git a/Luski.net/MainServer.Account.cs b/Luski.net/MainServer.Account.cs index 421d6fb..b47a5fd 100644 --- a/Luski.net/MainServer.Account.cs +++ b/Luski.net/MainServer.Account.cs @@ -42,12 +42,12 @@ public partial class MainServer List> heads = new() { new("key", EncryptionHandler.MyPublicKey), - new("username", Convert.ToBase64String(Encryption.RSA.Encrypt(Username, EncryptionHandler.MyPublicKey, EncoderType.UTF8))), - new("password", EncryptionHandler.RemotePasswordEncrypt(Encoding.UTF8.GetBytes(Password))) + new("email", Convert.ToBase64String(Encryption.RSA.Encrypt(Username, EncryptionHandler.ServerPublicKey, EncoderType.UTF16))), + new("password", EncryptionHandler.RemotePasswordEncrypt(Encoding.Unicode.GetBytes(Password))) }; if (File.Exists("LastPassVer.txt") && int.TryParse(File.ReadAllText("LastPassVer.txt"), out int lpv) && lpv < EncryptionHandler.PasswordVersion && lpv >= 0) { - heads.Add(new("old_password", EncryptionHandler.RemotePasswordEncrypt(Encoding.UTF8.GetBytes(Password), lpv))); + heads.Add(new("old_password", EncryptionHandler.RemotePasswordEncrypt(Encoding.Unicode.GetBytes(Password), lpv))); heads.Add(new("old_version", lpv.ToString())); } if (pfp is not null) @@ -73,16 +73,16 @@ public partial class MainServer login = false; if (json is not null && json.Error is null) { - ServerOut = new WebSocket($"wss://{Domain}/WSS/{ApiVersion}"); - ServerOut.SslConfiguration.EnabledSslProtocols = SslProtocols.Tls13 | SslProtocols.Tls12; + ServerOut = new WebSocket($"{(Secure ? "wss" : "ws" )}://{Domain}/WSS/{ApiVersion}"); + if (Secure) ServerOut.SslConfiguration.EnabledSslProtocols = SslProtocols.Tls13 | SslProtocols.Tls12; ServerOut.OnMessage += DataFromServer; ServerOut.WaitTime = new TimeSpan(0, 0, 5); ServerOut.OnError += ServerOut_OnError; ServerOut.Connect(); - SendServer(new WSSLogin() { Token = json.Token! }, WSSLoginContext.Default.WSSLogin); + SendServerOld(new WSSLogin() { Token = json.Token! }, WSSLoginContext.Default.WSSLogin); while (Token is null && Error is null) { - + Thread.Sleep(500); } if (Error is not null) { @@ -103,33 +103,32 @@ public partial class MainServer if (User is not null && User.Error is not null) error = $"{User.Error}: {User.ErrorMessage}"; throw new Exception($"Something went wrong getting your user infermation\n{error}"); } - Console.WriteLine("User got"); - Console.WriteLine("Insert"); _ = UpdateStatus(UserStatus.Online, CancellationToken); - Console.WriteLine("stat"); - EncryptionHandler.Hash = EncryptionHandler.LocalPasswordEncrypt(Encoding.UTF8.GetBytes(Username.ToLower() + Password)); - Console.WriteLine("req offline"); + if (pfp is not null) + { + //post + } + else + { + + } + EncryptionHandler.Hash = EncryptionHandler.LocalPasswordEncrypt(Encoding.Unicode.GetBytes(Username.ToLower() + Password)); OfflineData offlinedata = GetFromServer("Keys/GetOfflineData", OfflineDataContext.Default.OfflineData, CancellationToken).Result; - if (offlinedata.Data is not null && offlinedata.Data.Any()) Console.WriteLine(offlinedata.Data); if (offlinedata is not null && offlinedata.Error is null && offlinedata.Data is not null && offlinedata.Data.Length > 0) { foreach (string keyex in offlinedata.Data) { - Console.WriteLine(keyex); KeyExchange? okd = JsonSerializer.Deserialize(keyex); - Console.WriteLine(okd); if (okd is not null && !string.IsNullOrEmpty(okd.key)) { - Console.WriteLine(okd.channel); - Console.WriteLine(okd.key); Storage.SetResourceKey( StorageDirectory.ChannelKeys, okd.channel.ToString(), EncryptionHandler.Hash, - Encoding.UTF8.GetString( + Encoding.Unicode.GetString( Encryption.RSA.Decrypt( Convert.FromBase64String(okd.key), - Storage.GetResourceKey( + Storage.GetResourceKeyRaw( StorageDirectory.ServerKeys, "pkey", EncryptionHandler.Hash @@ -140,13 +139,11 @@ public partial class MainServer } } } - Console.WriteLine("lpv"); System.IO.File.WriteAllText("LastPassVer.txt", EncryptionHandler.PasswordVersion.ToString()); Storage.SetResourceKey(StorageDirectory.ServerKeys, "pkey", EncryptionHandler.Hash, EncryptionHandler.OfflinePrivateKey); using HttpClient setkey = new(); setkey.DefaultRequestHeaders.Add("token", Token); - _ = setkey.PostAsync($"https://{Domain}/{ApiVersion}/Keys/SetOfflineKey", new StringContent(EncryptionHandler.OfflinePublicKey)).Result; - //_ = User.Channels; + _ = setkey.PostAsync($"{(Secure ? "https" : "http" )}://{Domain}/{ApiVersion}/Keys/SetOfflineKey", new StringContent(EncryptionHandler.OfflinePublicKey)).Result; foreach (var ch in chans) { _ = ch.Members; diff --git a/Luski.net/MainServer.cs b/Luski.net/MainServer.cs index 979bc85..26f2f76 100644 --- a/Luski.net/MainServer.cs +++ b/Luski.net/MainServer.cs @@ -26,7 +26,9 @@ public partial class MainServer : Server } public MainSocketAppUser User { get; internal set; } = default!; - + + public List chans { get; } = new(); + public async Task SendFriendResult(long user, bool answer, CancellationToken CancellationToken) { FriendRequestResult json = await SendServer("FriendRequestResult", @@ -120,7 +122,7 @@ public partial class MainServer : Server TChannel request; if (chans.Count > 0 && chans.Any(s => s.Id == id)) { - return chans.Where(s => s is TChannel && s.Id == id).Cast().FirstOrDefault()!; + return (chans.Where(s => s is TChannel && s.Id == id).First() as TChannel)!; } while (true) { @@ -140,7 +142,6 @@ public partial class MainServer : Server chans.Remove(p); } } - request.Server = this; chans.Add(request); return request; diff --git a/Luski.net/PublicServer.Account.cs b/Luski.net/PublicServer.Account.cs new file mode 100644 index 0000000..92f2b5e --- /dev/null +++ b/Luski.net/PublicServer.Account.cs @@ -0,0 +1,353 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Security.Authentication; +using System.Text; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using JacobTechEncryption; +using JacobTechEncryption.Enums; +using Luski.net.Enums; +using Luski.net.JsonTypes; +using Luski.net.JsonTypes.BaseTypes; +using Luski.net.JsonTypes.WSS; +using Luski.net.Structures.Public; +using WebSocketSharp; +using DataType = Luski.net.Enums.Public.DataType; + +namespace Luski.net; + +public partial class PublicServer +{ + public async Task Login(string Username, string Password, CancellationToken CancellationToken) + { + return await Both(Username, Password, CancellationToken); + } + + public async Task CreateAccount(string Username, string Password, string Displayname, string PFP, CancellationToken CancellationToken) + { + return await Both(Username, Password, CancellationToken, Displayname, PFP); + } + + internal async Task LoginViaToken(string t) + { + CancellationToken CancellationToken = CancellationToken.None; + if (!EncryptionHandler.Generating) + { + EncryptionHandler.GenerateKeys(); + } + while (!EncryptionHandler.Generated) { } + + login = true; + Login json; + List> heads = new() + { + new("key", EncryptionHandler.MyPublicKey), + new("token", Convert.ToBase64String(Encryption.RSA.Encrypt(t, EncryptionHandler.ServerPublicKey, EncoderType.UTF16))), + }; + + json = await GetFromServer( + "SocketAccount/AccessToken", + LoginContext.Default.Login, + CancellationToken, + heads.ToArray()); + if (json.Error is not null) throw new Exception($"Luski appears to be down at the current moment: {json.ErrorMessage}"); + if (EncryptionHandler.OfflinePrivateKey is null || EncryptionHandler.OfflinePublicKey is null) throw new Exception("Something went wrong generating the offline keys"); + login = false; + if (json is not null && json.Error is null) + { + ServerOut = new WebSocket(wssurl); + ServerOut.SslConfiguration.EnabledSslProtocols = SslProtocols.Tls13 | SslProtocols.Tls12; + ServerOut.OnMessage += DataFromServer; + ServerOut.EmitOnPing = true; + ServerOut.WaitTime = new TimeSpan(0, 0, 5); + ServerOut.OnError += ServerOut_OnError; + ServerOut.Connect(); + SendServer(DataType.Token, new WSSLogin() { Token = json.Token! }); + while (Token is null && Error is null) + { + Thread.Sleep(500); + } + if (Error is not null) + { + throw new Exception(Error); + } + + if (Token is null) throw new Exception("Server did not send a token"); + CanRequest = true; + long id = long.Parse(Encoding.UTF8.GetString(Convert.FromBase64String( + Token.Split('.')[0] + ))); + User = await GetUser(id, CancellationToken); + StorageInfoJSON data; + if (Storage.StorageID == 0) + { + EncryptionHandler.Hash = Storage.GenerateStorage(); + data = await SendServer("OfflineData/Info", + new StorageInfoJSON() + { + id = 0, + update = false, + password = Convert.ToBase64String(Encryption.AES.Encrypt(EncryptionHandler.Hash, Storage.GetResourceBytes(StorageDirectory.StorageInfo, "lpk"))) + }, + StorageInfoJSONContext.Default.StorageInfoJSON, + StorageInfoJSONContext.Default.StorageInfoJSON, CancellationToken, + new KeyValuePair("storage_id", Storage.StorageID.ToString())); + Storage.setid(data.id); + } + else + { + data = await GetFromServer("OfflineData/Info", StorageInfoJSONContext.Default.StorageInfoJSON, CancellationToken, new KeyValuePair("storage_id", Storage.StorageID.ToString())); + } + + if (data.update) + { + EncryptionHandler.Hash = Storage.UpdateStorage(Convert.FromBase64String(data.password)); + _ = await SendServerPatch("OfflineData/Info", + new StorageInfoJSON() + { + id = 0, + update = false, + password = Convert.ToBase64String(Encryption.AES.Encrypt(EncryptionHandler.Hash, Storage.GetResourceBytes(StorageDirectory.StorageInfo, "lpk"))) + }, + StorageInfoJSONContext.Default.StorageInfoJSON, + StorageInfoJSONContext.Default.StorageInfoJSON, CancellationToken, + new KeyValuePair("storage_id", Storage.StorageID.ToString())); + } + try + { + _ = EncryptionHandler.GetKey(0); + } + catch (Exception e) + { + EncryptionHandler.SetKey(0, new() + { + EncryptionType = EncryptionType.None, + Key = string.Empty + }); + } + _ = await UpdateStatus(UserStatus.Online, CancellationToken); + KeyValuePair stor = new("storage_id", Storage.StorageID.ToString()); + OfflineData offlinedata = await GetFromServer("OfflineData", OfflineDataContext.Default.OfflineData, CancellationToken, stor); + if (offlinedata is not null && offlinedata.Error is null && offlinedata.Data is not null && offlinedata.Data.Length > 0) + { + string pkey = Storage.GetResourceKeyRaw( + StorageDirectory.ServerKeys, + "pkey", + EncryptionHandler.Hash + ); + foreach (string keyexx in offlinedata.Data) + { + string keyex = Encoding.UTF8.GetString(Convert.FromBase64String(keyexx)); + KeyExchange? okd = JsonSerializer.Deserialize(keyex); + if (okd is not null && !string.IsNullOrEmpty(okd.key)) + { + Storage.SetResourceKey( + StorageDirectory.ChannelKeys, + okd.channel.ToString(), + EncryptionHandler.Hash, + Encoding.Unicode.GetString( + Encryption.RSA.Decrypt( + Convert.FromBase64String(okd.key), + pkey + ) + ) + ); + } + } + } + Storage.SetResourceKey(StorageDirectory.ServerKeys, "pkey", EncryptionHandler.Hash, EncryptionHandler.OfflinePrivateKey); + UserKeyGetRequest OfflineKeySetRequest = new() + { + key_data = Convert.ToBase64String(Encoding.UTF8.GetBytes(EncryptionHandler.OfflinePublicKey)), + encryption_type = EncryptionType.RSA + }; + _ = await SendServer("Keys/SetOfflineKey", + OfflineKeySetRequest, + UserKeyGetRequestContext.Default.UserKeyGetRequest, + IncomingHTTPContext.Default.IncomingHTTP, + CancellationToken.None, + stor); + EncryptionHandler.OfflinePublicKey = null!; + EncryptionHandler.OfflinePrivateKey = null!; + return true; + } + else + { + throw new Exception(json?.ErrorMessage); + } + + return true; + } + + private async Task Both(string Username, string Password, CancellationToken CancellationToken, string? Displayname = null, string? pfp = null) + { + if (!EncryptionHandler.Generating) + { + EncryptionHandler.GenerateKeys(); + } + while (!EncryptionHandler.Generated) { } + + login = true; + Login json; + List> heads = new() + { + new("key", EncryptionHandler.MyPublicKey), + new("username", Convert.ToBase64String(Encryption.RSA.Encrypt(Username, EncryptionHandler.ServerPublicKey, EncoderType.UTF16))), + new("password", EncryptionHandler.RemotePasswordEncrypt(Encryption.Generic.Encoders[(int)EncoderType.UTF16].GetBytes(Password))) + }; + if (File.Exists("LastPassVer.txt") && int.TryParse(File.ReadAllText("LastPassVer.txt"), out int lpv) && lpv < EncryptionHandler.PasswordVersion && lpv >= 0) + { + heads.Add(new("old_password", EncryptionHandler.RemotePasswordEncrypt(Encoding.Unicode.GetBytes(Password), lpv))); + heads.Add(new("old_version", lpv.ToString())); + } + if (pfp is not null) + { + heads.Add(new("displayname", Displayname)); + json = await SendServer( + "SocketAccount", + pfp, + LoginContext.Default.Login, + CancellationToken, + heads.ToArray()); + } + else + { + json = await GetFromServer( + "SocketAccount", + LoginContext.Default.Login, + CancellationToken, + heads.ToArray()); + } + if (json.Error is not null) throw new Exception($"Luski appears to be down at the current moment: {json.ErrorMessage}"); + if (EncryptionHandler.OfflinePrivateKey is null || EncryptionHandler.OfflinePublicKey is null) throw new Exception("Something went wrong generating the offline keys"); + login = false; + if (json is not null && json.Error is null) + { + ServerOut = new WebSocket(wssurl); + ServerOut.SslConfiguration.EnabledSslProtocols = SslProtocols.Tls13 | SslProtocols.Tls12; + ServerOut.OnMessage += DataFromServer; + ServerOut.WaitTime = new TimeSpan(0, 0, 5); + ServerOut.EmitOnPing = true; + ServerOut.OnError += ServerOut_OnError; + ServerOut.Connect(); + SendServer(DataType.Token, new WSSLogin() { Token = json.Token! }); + while (Token is null && Error is null) + { + Thread.Sleep(500); + } + if (Error is not null) + { + throw new Exception(Error); + } + + if (Token is null) throw new Exception("Server did not send a token"); + CanRequest = true; + long id = long.Parse(Encoding.UTF8.GetString(Convert.FromBase64String( + Token.Split('.')[0] + ))); + User = await GetUser(id, CancellationToken); + StorageInfoJSON data; + if (Storage.StorageID == 0) + { + EncryptionHandler.Hash = Storage.GenerateStorage(); + data = await SendServer("OfflineData/Info", + new StorageInfoJSON() + { + id = 0, + update = false, + password = Convert.ToBase64String(Encryption.AES.Encrypt(EncryptionHandler.Hash, Storage.GetResourceBytes(StorageDirectory.StorageInfo, "lpk"))) + }, + StorageInfoJSONContext.Default.StorageInfoJSON, + StorageInfoJSONContext.Default.StorageInfoJSON, CancellationToken, + new KeyValuePair("storage_id", Storage.StorageID.ToString())); + Storage.setid(data.id); + } + else + { + data = await GetFromServer("OfflineData/Info", StorageInfoJSONContext.Default.StorageInfoJSON, CancellationToken, new KeyValuePair("storage_id", Storage.StorageID.ToString())); + } + + if (data.update) + { + EncryptionHandler.Hash = Storage.UpdateStorage(Convert.FromBase64String(data.password)); + _ = await SendServerPatch("OfflineData/Info", + new StorageInfoJSON() + { + id = 0, + update = false, + password = Convert.ToBase64String(Encryption.AES.Encrypt(EncryptionHandler.Hash, Storage.GetResourceBytes(StorageDirectory.StorageInfo, "lpk"))) + }, + StorageInfoJSONContext.Default.StorageInfoJSON, + StorageInfoJSONContext.Default.StorageInfoJSON, CancellationToken, + new KeyValuePair("storage_id", Storage.StorageID.ToString())); + } + try + { + _ = EncryptionHandler.GetKey(0); + } + catch (Exception e) + { + EncryptionHandler.SetKey(0, new() + { + EncryptionType = EncryptionType.None, + Key = string.Empty + }); + } + _ = await UpdateStatus(UserStatus.Online, CancellationToken); + //EncryptionHandler.Hash = EncryptionHandler.LocalPasswordEncrypt(Encoding.Unicode.GetBytes(Username.ToLower() + Password)); + KeyValuePair stor = new("storage_id", Storage.StorageID.ToString()); + OfflineData offlinedata = await GetFromServer("OfflineData", OfflineDataContext.Default.OfflineData, CancellationToken, stor); + if (offlinedata is not null && offlinedata.Error is null && offlinedata.Data is not null && offlinedata.Data.Length > 0) + { + string pkey = Storage.GetResourceKeyRaw( + StorageDirectory.ServerKeys, + "pkey", + EncryptionHandler.Hash + ); + foreach (string keyexx in offlinedata.Data) + { + string keyex = Encoding.UTF8.GetString(Convert.FromBase64String(keyexx)); + KeyExchange? okd = JsonSerializer.Deserialize(keyex); + if (okd is not null && !string.IsNullOrEmpty(okd.key)) + { + Storage.SetResourceKey( + StorageDirectory.ChannelKeys, + okd.channel.ToString(), + EncryptionHandler.Hash, + Encoding.Unicode.GetString( + Encryption.RSA.Decrypt( + Convert.FromBase64String(okd.key), + pkey + ) + ) + ); + } + } + } + System.IO.File.WriteAllText("LastPassVer.txt", EncryptionHandler.PasswordVersion.ToString()); + Storage.SetResourceKey(StorageDirectory.ServerKeys, "pkey", EncryptionHandler.Hash, EncryptionHandler.OfflinePrivateKey); + UserKeyGetRequest OfflineKeySetRequest = new() + { + key_data = Convert.ToBase64String(Encoding.UTF8.GetBytes(EncryptionHandler.OfflinePublicKey)), + encryption_type = EncryptionType.RSA + }; + _ = await SendServer("Keys/SetOfflineKey", + OfflineKeySetRequest, + UserKeyGetRequestContext.Default.UserKeyGetRequest, + IncomingHTTPContext.Default.IncomingHTTP, + CancellationToken.None, + stor); + // using HttpClient setkey = new(); + // setkey.DefaultRequestHeaders.Add("token", Token); + // _ = await setkey.PostAsync($"{(Secure ? "https" : "http" )}://{Domain}/{ApiVersion}/Keys/SetOfflineKey", new StringContent(EncryptionHandler.OfflinePublicKey)); + EncryptionHandler.OfflinePublicKey = null!; + EncryptionHandler.OfflinePrivateKey = null!; + return true; + } + else throw new Exception(json?.ErrorMessage); + + return true; + } +} \ No newline at end of file diff --git a/Luski.net/PublicServer.Incoming.cs b/Luski.net/PublicServer.Incoming.cs new file mode 100644 index 0000000..926c9df --- /dev/null +++ b/Luski.net/PublicServer.Incoming.cs @@ -0,0 +1,38 @@ +using System; +using System.IO; +using System.Text.Json; +using Luski.net.Enums; +using Luski.net.JsonTypes.BaseTypes; +using Luski.net.JsonTypes.WSS; +using WebSocketSharp; +using DataType = Luski.net.Enums.Public.DataType; + +namespace Luski.net; + +public partial class PublicServer +{ + private void DataFromServer(object? sender, MessageEventArgs e) + { + if (e.IsPing) return; + try + { + BetterIncomingWSS? data = JsonSerializer.Deserialize(e.Data, BetterIncomingWSSContext.Default.BetterIncomingWSS); + switch (data?.Type) + { + case DataType.Token: + WSSLogin n = JsonSerializer.Deserialize(e.Data, WSSLoginContext.Default.WSSLogin)!; + File.WriteAllText(Storage.GetStorageDirectory(StorageDirectory.StorageInfo) + "token", + n.SessionToken); + Token = n.Token; + break; + default: + Console.WriteLine("Unknown"); + break; + } + } + catch (Exception exception) + { + Exception(exception); + } + } +} \ No newline at end of file diff --git a/Luski.net/PublicServer.cs b/Luski.net/PublicServer.cs index f23f971..fffe07a 100644 --- a/Luski.net/PublicServer.cs +++ b/Luski.net/PublicServer.cs @@ -1,9 +1,300 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json.Serialization.Metadata; +using System.Threading; +using System.Threading.Tasks; +using JacobTechEncryption; +using JacobTechEncryption.Enums; +using Luski.net.Enums; +using Luski.net.Enums.Main; +using Luski.net.Interfaces; +using Luski.net.JsonTypes; +using Luski.net.JsonTypes.BaseTypes; +using Luski.net.JsonTypes.HTTP; +using Luski.net.JsonTypes.Public; +using Luski.net.Structures; +using Luski.net.Structures.Public; +using Channel = Luski.net.JsonTypes.Public.Channel; +using Role = Luski.net.Structures.Public.Role; +using SocketUser = Luski.net.Structures.Public.SocketUser; + namespace Luski.net; -public class PublicServer : Server +public partial class PublicServer : Server { - internal PublicServer(string Domain, string API_Version): - base(Domain, API_Version) + public List chans { get; } = new(); + public List cats { get; } = new(); + public List roles { get; } = new(); + + public SocketAppUser User { get; private set; } = null!; + + internal PublicServer(string Domain, string API_Version, bool Secure = true): + base(Domain, API_Version, Secure) { + ServerInfo si = GetFromServer("socketserver", ServerInfoContext.Default.ServerInfo, CancellationToken.None).Result; + Name = si.name; + Description = si.description; + wssurl = si.wssv4; } + + public async Task GetCategory(long id, CancellationToken CancellationToken) where TCategory : SocketCategory, new() + { + Category request; + if (cats.Count > 0 && cats.Any(s => s.ID == id)) + { + return (cats.Where(s => s is TCategory && s.ID == id).First() as TCategory)!; + } + while (true) + { + if (CanRequest) + { + request = await GetFromServer($"SocketCategory", PublicCategoryContext.Default.Category, CancellationToken, new KeyValuePair("id", id.ToString())); + break; + } + } + if (request is null) throw new Exception("Something was wrong with the server responce"); + if (request.Error is null) + { + if (cats.Count > 0 && cats.Any(s => s.ID == request.ID)) + { + foreach (SocketCategory? p in cats.Where(s => s.ID == request.ID)) + { + cats.Remove(p); + } + } + + LocalKeyInfo deckey = EncryptionHandler.GetKey(request.DescriptionEncryptionKey); + string dec = deckey.EncryptionType switch + { + EncryptionType.RSA => Encryption.RSA.Decrypt(Convert.FromBase64String(request.Description), deckey.Key, request.DescriptionEncoderType), + EncryptionType.AES => Encryption.Generic.Encoders[(short)request.DescriptionEncoderType].GetString( + Encryption.AES.Decrypt(Convert.FromBase64String(request.Description), deckey.Key)), + _ => Encryption.Generic.Encoders[(short)request.DescriptionEncoderType].GetString(Convert.FromBase64String(request.Description)) + }; + LocalKeyInfo nkey = EncryptionHandler.GetKey(request.TitleEncryptionKey); + string n = nkey.EncryptionType switch + { + EncryptionType.RSA => Encryption.RSA.Decrypt(Convert.FromBase64String(request.Name), nkey.Key, request.TitleEncoderType), + EncryptionType.AES => Encryption.Generic.Encoders[(short)request.TitleEncoderType].GetString( + Encryption.AES.Decrypt(Convert.FromBase64String(request.Name), nkey.Key)), + _ => Encryption.Generic.Encoders[(short)request.TitleEncoderType].GetString(Convert.FromBase64String(request.Name)) + }; + + TCategory bob = new() + { + ID = request.ID, + ParentID = request.Parent, + Description = dec, + DescriptionEncoderType = request.DescriptionEncoderType, + DescriptionEncryptionKey = request.DescriptionEncryptionKey, + Channels = request.Channels, + Categories = request.InnerCategories, + Name = n, + RoleOverides = request.RoleOverides, + UserOverides = request.UserOverides, + TitleEncoderType = request.TitleEncoderType, + TitleEncryptionKey = request.TitleEncryptionKey, + Server = this, + Color = new(request.Color) + }; + cats.Add(bob); + return bob; + } + throw request.Error switch + { + ErrorCode.InvalidToken => new Exception("Your current token is no longer valid"), + ErrorCode.Forbidden => new Exception("The server rejected your request"), + ErrorCode.ServerError => new Exception("Error from server: " + request.ErrorMessage), + ErrorCode.InvalidURL or ErrorCode.MissingHeader => new Exception(request.ErrorMessage), + _ => new Exception($"Unknown data: '{request.ErrorMessage}'"), + }; + } + + public async Task GetRole(long id) + { + Role[] r = roles.Where(s => s.ID == id).ToArray(); + if (r.Length > 0) return r[0]; + JsonTypes.Public.Role s = await GetFromServer("SocketRole?id=" + id.ToString(), RoleContext.Default.Role, CancellationToken.None); + + Role role = new() + { + Server = this, + ID = s.id, + Color = new(s.color), + Description = s.description, + DisplayName = s.display_name, + MembersListID = s.members_list, + Name = s.name, + Index = s.index, + ServerPermissions = s.server_permissions + }; + roles.Add(role); + return role; + } + + public async Task GetChannel(long id, CancellationToken CancellationToken) where TChannel : SocketChannel, new() + { + Channel request; + if (chans.Count > 0 && chans.Any(s => s.ID == id)) + { + return (chans.Where(s => s is TChannel && s.ID == id).First() as TChannel)!; + } + while (true) + { + if (CanRequest) + { + request = await GetFromServer($"SocketChannel", PublicChannelContext.Default.Channel, CancellationToken, new KeyValuePair("id", id.ToString())); + break; + } + } + if (request is null) throw new Exception("Something was wrong with the server responce"); + if (request.Error is null) + { + if (chans.Count > 0 && chans.Any(s => s.ID == request.ID)) + { + foreach (SocketChannel? p in chans.Where(s => s.ID == request.ID)) + { + chans.Remove(p); + } + } + + LocalKeyInfo deckey = EncryptionHandler.GetKey(request.DescriptionEncryptionKey); + string dec = deckey.EncryptionType switch + { + EncryptionType.RSA => Encryption.RSA.Decrypt(Convert.FromBase64String(request.Description), deckey.Key, request.DescriptionEncoderType), + EncryptionType.AES => Encryption.Generic.Encoders[(short)request.DescriptionEncoderType].GetString( + Encryption.AES.Decrypt(Convert.FromBase64String(request.Description), deckey.Key)), + _ => Encryption.Generic.Encoders[(short)request.DescriptionEncoderType].GetString(Convert.FromBase64String(request.Description)) + }; + LocalKeyInfo nkey = EncryptionHandler.GetKey(request.TitleEncryptionKey); + string n = nkey.EncryptionType switch + { + EncryptionType.RSA => Encryption.RSA.Decrypt(Convert.FromBase64String(request.Name), nkey.Key, request.TitleEncoderType), + EncryptionType.AES => Encryption.Generic.Encoders[(short)request.TitleEncoderType].GetString( + Encryption.AES.Decrypt(Convert.FromBase64String(request.Name), nkey.Key)), + _ => Encryption.Generic.Encoders[(short)request.TitleEncoderType].GetString(Convert.FromBase64String(request.Name)) + }; + + TChannel bob = new() + { + ID = request.ID, + CategoryID = request.Parent, + Description = dec, + DescriptionEncoderType = request.DescriptionEncoderType, + DescriptionEncryptionKey = request.DescriptionEncryptionKey, + EncoderTypes = request.EncoderTypes, + EncryptionKeys = request.EncryptionKeys, + Epoch = request.Epoch, + Name = n, + RoleOverides = request.RoleOverides, + UserOverides = request.UserOverides, + Type = request.Type, + TitleEncoderType = request.TitleEncoderType, + TitleEncryptionKey = request.TitleEncryptionKey, + PictureType = request.PictureType, + Server = this, + Color = new(request.Color) + }; + chans.Add(bob); + return bob; + } + throw request.Error switch + { + ErrorCode.InvalidToken => new Exception("Your current token is no longer valid"), + ErrorCode.Forbidden => new Exception("The server rejected your request"), + ErrorCode.ServerError => new Exception("Error from server: " + request.ErrorMessage), + ErrorCode.InvalidURL or ErrorCode.MissingHeader => new Exception(request.ErrorMessage), + _ => new Exception($"Unknown data: '{request.ErrorMessage}'"), + }; + } + + public async Task GetUser(long UserId, CancellationToken CancellationToken) where Tuser : SocketUser, new() + { + JsonTypes.Public.SocketUser user; + if (poeople.Count > 0 && poeople.Any(s => s.Id == UserId)) + { + Tuser temp = poeople.Where(s => s is Tuser && s.Id == UserId).Cast().FirstOrDefault()!; + return temp; + } + while (true) + { + if (CanRequest) + { + user = await GetFromServer("socketuser", + PublicSocketUserContext.Default.SocketUser, + CancellationToken, + new KeyValuePair("id", UserId.ToString())); + break; + } + } + + if (user is null) throw new Exception("Server did not return a user"); + if (poeople.Count > 0 && poeople.Any(s => s.Id == UserId)) + { + foreach (IUser? p in poeople.Where(s => s.Id == UserId)) + { + poeople.Remove(p); + } + } + if (user is null || user.Error is not null) + { + string error = "User was null"; + if (user is not null && user.Error is not null) error = $"{user.Error}: {user.ErrorMessage}"; + throw new Exception($"Something went wrong getting your user information\n{error}"); + } + + Tuser u = new(); + if (u is SocketUser) + { + u = new() + { + Server = this, + Id = user.ID, + DisplayName = user.DisplayName, + PictureType = user.PictureType, + RoleIds = user.RoleIds, + Status = user.Status + }; + } + else + { + u = (new SocketAppUser() + { + Server = this, + Id = user.ID, + DisplayName = user.DisplayName, + SelectedChannel = user.SelectedChannel, + PictureType = user.PictureType, + RoleIds = user.RoleIds, + Status = user.Status, + Username = user.Username + } as Tuser)!; + } + poeople.Add(u); + return u; + } + + /// + /// Sends the server a request to update the of you account + /// + /// The you want to set your status to + /// + public async Task UpdateStatus(UserStatus Status, CancellationToken CancellationToken) + { + IncomingHTTP? data = await SendServer("SocketUserProfile/Status", new Status() { UserStatus = Status }, StatusContext.Default.Status, IncomingHTTPContext.Default.IncomingHTTP, CancellationToken); + if (data.Error is not null && ((int)data.StatusCode < 200 || (int)data.StatusCode > 299)) + { + if (data?.ErrorMessage is not null) throw new Exception(data.ErrorMessage); + if (data?.Error is not null) throw new Exception(((int)data.Error).ToString()); + else throw new Exception("Something went worng"); + } + + User.Status = Status; + return Task.CompletedTask; + } + + public string Name { get; } + public string Description { get; } } \ No newline at end of file diff --git a/Luski.net/ServerEncryption.cs b/Luski.net/Server.Encryption.cs similarity index 76% rename from Luski.net/ServerEncryption.cs rename to Luski.net/Server.Encryption.cs index 057b698..ef0820d 100644 --- a/Luski.net/ServerEncryption.cs +++ b/Luski.net/Server.Encryption.cs @@ -1,7 +1,10 @@ using System; +using System.Net.Http; using System.Security.Cryptography; using JacobTechEncryption; +using JacobTechEncryption.Enums; using Luski.net.Enums; +using Luski.net.Structures; namespace Luski.net; @@ -10,15 +13,26 @@ public class ServerEncryption internal bool Generating, Generated; internal string ServerPublicKey = "", MyPublicKey = "", myPrivateKey = "", OfflinePrivateKey = "", OfflinePublicKey = ""; internal byte[] Hash = default!; - internal ServerEncryption(string Domain, string API_Version, ServerStorage Storage) + internal ServerEncryption(string Domain, string API_Version, ServerStorage Storage, bool Secure) { this.Storage = Storage; - //TODO Get server p key + ServerPublicKey = new HttpClient().GetAsync($"{(Secure ? "https" : "http" )}://{Domain}/{API_Version}/Keys/PublicKey").Result.Content + .ReadAsStringAsync().Result; } public string GetChannelKey(long Channel) { - return Storage.GetResourceKey(StorageDirectory.ChannelKeys, Channel.ToString(), Hash); + return Storage.GetResourceKeyRaw(StorageDirectory.ChannelKeys, Channel.ToString(), Hash); + } + + public LocalKeyInfo GetKey(long Key) + { + return Storage.GetResourceKey(StorageDirectory.ServerKeys, Key.ToString(), Hash); + } + + public void SetKey(long Key, LocalKeyInfo Info) + { + Storage.SetResourceKey(StorageDirectory.ServerKeys, Key.ToString(), Info, Hash); } public void SetChannelKey(long Channel, string Key) diff --git a/Luski.net/Server.Globals.cs b/Luski.net/Server.Globals.cs index df5b3f1..a39d109 100644 --- a/Luski.net/Server.Globals.cs +++ b/Luski.net/Server.Globals.cs @@ -18,7 +18,7 @@ public partial class Server public string ApiVersion { get; } = "v1"; internal WebSocket? ServerOut; internal string? Token = null, Error = null, gen = null; + public bool IsLogedIn => Token is not null; internal bool CanRequest = false, login = false; internal List poeople = new(); - internal List chans { get; set; } = new(); } \ No newline at end of file diff --git a/Luski.net/Server.Storage.cs b/Luski.net/Server.Storage.cs new file mode 100644 index 0000000..80e079c --- /dev/null +++ b/Luski.net/Server.Storage.cs @@ -0,0 +1,306 @@ +using System; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Text.Json; +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", + "Channels/Files" + }; + + private static readonly int[] CantDelete = new[] + { + (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 byte[] UpdateStorage(byte[] OldPassword) + { + try + { + byte[] NewPassword = new byte[100]; + byte[] lpk = GetResourceBytes(StorageDirectory.StorageInfo, "lpk"); + OldPassword = Encryption.AES.Decrypt(OldPassword, lpk); + 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 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) + { + 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 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; + } + } +} \ No newline at end of file diff --git a/Luski.net/Server.cs b/Luski.net/Server.cs index 4aad3bc..9f514ec 100644 --- a/Luski.net/Server.cs +++ b/Luski.net/Server.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Net.Http; using System.Net.Http.Json; +using System.Net.Mime; using System.Text.Json; using System.Text.Json.Serialization.Metadata; using System.Threading; @@ -11,34 +12,52 @@ using System.Threading.Tasks; using Luski.net.Enums; using Luski.net.Interfaces; using Luski.net.JsonTypes.BaseTypes; +using Luski.net.JsonTypes.WSS; using File = System.IO.File; namespace Luski.net; public partial class Server { - internal Server(string Domain, string API_Version) + internal Server(string Domain, string API_Version, bool Secure = true) { this.Domain = Domain; this.ApiVersion = API_Version; + this.Secure = Secure; Storage = new(Domain); - EncryptionHandler = new(Domain, API_Version, Storage); + EncryptionHandler = new(Domain, API_Version, Storage, this.Secure); } + internal bool Secure = true; + internal string wssurl; + public ServerEncryption EncryptionHandler { get; } public ServerStorage Storage { get; } - public async Task GetAvatar(CancellationToken CancellationToken) + public async Task GetAvatar(CancellationToken CancellationToken) { bool isc = File.Exists(Storage.GetStorageDirectory(StorageDirectory.ServerAssets) + "Icon"); - if (!isc) await GetFromServer($"socketserver/Avatar/", Storage.GetStorageDirectory(StorageDirectory.ServerAssets) + "Icon", CancellationToken); - return Storage.GetResourceBytes(StorageDirectory.ServerAssets, "Icon"); + if (!isc) await GetFromServer($"socketserver/Icon/", Storage.GetStorageDirectory(StorageDirectory.ServerAssets) + "Icon", CancellationToken); + return Storage.GetResourceStream(StorageDirectory.ServerAssets, "Icon"); } - public void SendServer(Tvalue Payload, JsonTypeInfo jsonTypeInfo) where Tvalue : IncomingWSS + public void SendServerOld(Tvalue Payload, JsonTypeInfo jsonTypeInfo) where Tvalue : IncomingWSS { ServerOut?.Send(JsonSerializer.Serialize(Payload, jsonTypeInfo)); } + + public void SendServer(Enums.Public.DataType Type, IServerEvent Payload) + { + ServerOut?.Send(JsonSerializer.Serialize(new WSSOut() + { + Data = new() + { + Type = Type, + Data = Payload + } + })); + } + public HttpResponseMessage GetFromServer(string Path, CancellationToken CancellationToken, params KeyValuePair[] Headers) { @@ -46,7 +65,7 @@ public partial class Server web.Timeout = TimeSpan.FromSeconds(10); if (!login) web.DefaultRequestHeaders.Add("token", Token); if (Headers is not null && Headers.Length > 0) foreach (KeyValuePair header in Headers) web.DefaultRequestHeaders.Add(header.Key, header.Value); - return web.GetAsync($"https://{Domain}/{ApiVersion}/{Path}", cancellationToken: CancellationToken).Result; + return web.GetAsync($"{(Secure ? "https" : "http" )}://{Domain}/{ApiVersion}/{Path}", cancellationToken: CancellationToken).Result; } public Task GetFromServer(string Path, string File, CancellationToken CancellationToken, params KeyValuePair[] Headers) @@ -55,7 +74,7 @@ public partial class Server web.Timeout = TimeSpan.FromMinutes(10); if (!login) web.DefaultRequestHeaders.Add("token", Token); if (Headers is not null && Headers.Length > 0) foreach (KeyValuePair header in Headers) web.DefaultRequestHeaders.Add(header.Key, header.Value); - HttpResponseMessage Response = web.GetAsync($"https://{Domain}/{ApiVersion}/{Path}", CancellationToken).Result; + HttpResponseMessage Response = web.GetAsync($"{(Secure ? "https" : "http" )}://{Domain}/{ApiVersion}/{Path}", CancellationToken).Result; Stream stream = Response.Content.ReadAsStreamAsync(CancellationToken).Result; using FileStream fs = System.IO.File.Create(File); stream.CopyTo(fs); @@ -65,8 +84,17 @@ public partial class Server public async Task GetFromServer(string Path, JsonTypeInfo Type, CancellationToken CancellationToken, params KeyValuePair[] Headers) where Tresult : IncomingHTTP, new() { HttpResponseMessage ServerResponce = GetFromServer(Path, CancellationToken, Headers); - if (!ServerResponce.IsSuccessStatusCode) return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with status code {(int)ServerResponce.StatusCode}:{ServerResponce.StatusCode}" }; - Tresult? temp = JsonSerializer.Deserialize(ServerResponce.Content.ReadAsStreamAsync(CancellationToken).Result, Type); + //if (!ServerResponce.IsSuccessStatusCode) return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with status code {(int)ServerResponce.StatusCode}:{ServerResponce.StatusCode}" }; + Tresult temp = new(); + string raw = ServerResponce.Content.ReadAsStringAsync(CancellationToken).Result; + try + { + temp = JsonSerializer.Deserialize(raw, Type)!; + } + catch (Exception e) + { + Console.WriteLine("JSON parse failed for the following data as type {0}\n{1}", temp.GetType(), raw); + } if (temp is null) return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with empty data" }; return temp; } @@ -76,9 +104,29 @@ public partial class Server using HttpClient web = new(); if (!login) web.DefaultRequestHeaders.Add("token", Token); if (Headers is not null && Headers.Length > 0) foreach (KeyValuePair header in Headers) web.DefaultRequestHeaders.Add(header.Key, header.Value); - HttpResponseMessage ServerResponce = web.PostAsJsonAsync($"https://{Domain}/{ApiVersion}/{Path}", Payload, jsonTypeInfo, CancellationToken).Result; - if (!ServerResponce.IsSuccessStatusCode) return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with status code {(int)ServerResponce.StatusCode}:{ServerResponce.StatusCode}" }; - Tresult error = new() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with empty data" }; + HttpResponseMessage ServerResponce = web.PostAsJsonAsync($"{(Secure ? "https" : "http" )}://{Domain}/{ApiVersion}/{Path}", Payload, jsonTypeInfo, CancellationToken).Result; + //if (!ServerResponce.IsSuccessStatusCode) return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with status code {(int)ServerResponce.StatusCode}:{ServerResponce.StatusCode}" }; + Tresult error = new() { StatusCode = ServerResponce.StatusCode, Error = null, ErrorMessage = $"Server responded with empty data" }; + if (string.IsNullOrWhiteSpace(ServerResponce.Content.ReadAsStringAsync(CancellationToken).Result)) return error; + try + { + Tresult? temp = JsonSerializer.Deserialize(ServerResponce.Content.ReadAsStreamAsync(CancellationToken).Result, ReturnjsonTypeInfo); + if (temp is null) return error; + return temp; + } + catch { return error; } + } + + public async Task SendServerPatch(string Path, Tvalue Payload, JsonTypeInfo jsonTypeInfo, JsonTypeInfo ReturnjsonTypeInfo, CancellationToken CancellationToken, params KeyValuePair[] Headers) where Tvalue : IWebRequest where Tresult : IncomingHTTP, new() + { + using HttpClient web = new(); + if (!login) web.DefaultRequestHeaders.Add("token", Token); + if (Headers is not null && Headers.Length > 0) foreach (KeyValuePair header in Headers) web.DefaultRequestHeaders.Add(header.Key, header.Value); + HttpResponseMessage ServerResponce = await web.PatchAsJsonAsync($"{(Secure ? "https" : "http" )}://{Domain}/{ApiVersion}/{Path}", Payload, jsonTypeInfo, CancellationToken); + // HttpResponseMessage ServerResponce = await web.PatchAsync($"{(Secure ? "https" : "http" )}://{Domain}/{ApiVersion}/{Path}", new StringContent(JsonSerializer.Serialize(Payload, jsonTypeInfo)), + // CancellationToken); + //if (!ServerResponce.IsSuccessStatusCode) return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with status code {(int)ServerResponce.StatusCode}:{ServerResponce.StatusCode}" }; + Tresult error = new() { StatusCode = ServerResponce.StatusCode, Error = null, ErrorMessage = $"Server responded with empty data" }; if (string.IsNullOrWhiteSpace(ServerResponce.Content.ReadAsStringAsync(CancellationToken).Result)) return error; try { @@ -98,12 +146,12 @@ public partial class Server if (!login) web.DefaultRequestHeaders.Add("token", Token); web.Timeout = new TimeSpan(0, 10, 0); if (Headers is not null && Headers.Length > 0) foreach (KeyValuePair header in Headers) web.DefaultRequestHeaders.Add(header.Key, header.Value); - HttpResponseMessage ServerResponce = web.PostAsync($"https://{Domain}/{ApiVersion}/{Path}", new StreamContent(fs), CancellationToken).Result; - if (!ServerResponce.IsSuccessStatusCode) return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with status code {(int)ServerResponce.StatusCode}:{ServerResponce.StatusCode}" }; + //web.DefaultRequestHeaders.Add("Content-Type", MediaTypeNames.Application.Octet); + HttpResponseMessage ServerResponce = web.PostAsync($"{(Secure ? "https" : "http" )}://{Domain}/{ApiVersion}/{Path}", new StreamContent(fs), CancellationToken).Result; try { Tresult? temp = JsonSerializer.Deserialize(ServerResponce.Content.ReadAsStreamAsync(CancellationToken).Result, ReturnjsonTypeInfo); - if (temp is null) return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with empty data" }; + if (temp is null) return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = null, ErrorMessage = $"Server responded with empty data" }; return temp; } catch { return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with empty data" }; } diff --git a/Luski.net/ServerStorage.cs b/Luski.net/ServerStorage.cs deleted file mode 100644 index f492e36..0000000 --- a/Luski.net/ServerStorage.cs +++ /dev/null @@ -1,116 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Text; -using System.Text.Json; -using JacobTechEncryption; -using Luski.net.Enums; -using Luski.net.JsonTypes; - -namespace Luski.net; - -public class ServerStorage -{ - public static readonly string[] Directories = new[] - { - "Info", - "Assets", - "Channels/Keys", - "Keys", - "Avatars", - "Channels/Icons", - "Channels/Messages" - }; - - private static readonly int[] CantDelete = new[] - { - (int)StorageDirectory.ChannelKeys, - (int)StorageDirectory.ServerKeys - }; - - 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+ "/"; - 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)); - ServerStorageInfo info = JsonSerializer.Deserialize(File.ReadAllText(Location + "storage.json"), - ServerStorageInfoContext.Default.ServerStorageInfo)!; - CacheMode = info.CacheMode; - for (int i = 0; i < Directories.Length; i++) - { - string full = Location; - string[] spl = Directories[i].Split('/'); - if (!info.DontDelete && !CantDelete.Contains(i)) - { - try - { - if (Directory.Exists(full + spl[0])) Directory.Delete(full + spl[0], true); - } - catch - { - // ignored - } - } - - foreach (string d in spl) - { - full += d + "/"; - if (!Directory.Exists(full)) Directory.CreateDirectory(full); - } - } - } - - public string Location { get; internal set; } - - public string GetStorageDirectory(StorageDirectory Directory) - { - return Location + Directories[(byte)Directory] + "/"; - } - - public string GetResourceKey(StorageDirectory Directory, string Resource, string Key) - { - return Encoding.UTF8.GetString(Encryption.AES.Decrypt(GetResourceBytes(Directory, Resource), Key)); - } - - public string GetResourceKey(StorageDirectory Directory, string Resource, byte[] Key) - { - return Encoding.UTF8.GetString(Encryption.AES.Decrypt(GetResourceBytes(Directory, Resource), Encoding.UTF8.GetString(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), Encoding.UTF8.GetString(Key))); - } - - public byte[] GetResourceBytes(StorageDirectory Directory, string Resource) - { - return File.ReadAllBytes(Location + Directories[(byte)Directory] + "/" + Resource); - } - - 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; - } - } -} \ No newline at end of file diff --git a/Luski.net/Structures/LocalKeyInfo.cs b/Luski.net/Structures/LocalKeyInfo.cs new file mode 100644 index 0000000..ae92b7a --- /dev/null +++ b/Luski.net/Structures/LocalKeyInfo.cs @@ -0,0 +1,9 @@ +using JacobTechEncryption.Enums; + +namespace Luski.net.Structures; + +public class LocalKeyInfo +{ + public string Key { get; init; } = default!; + public EncryptionType EncryptionType { get; init; } = default!; +} \ No newline at end of file diff --git a/Luski.net/Structures/Main/MainSocketAppUser.cs b/Luski.net/Structures/Main/MainSocketAppUser.cs index 20e3a59..2a52317 100755 --- a/Luski.net/Structures/Main/MainSocketAppUser.cs +++ b/Luski.net/Structures/Main/MainSocketAppUser.cs @@ -19,40 +19,45 @@ public class MainSocketAppUser : MainSocketUserBase, IAppUser [JsonPropertyName("selected_channel")] [JsonInclude] public long SelectedChannel { get; internal set; } = default!; - [JsonPropertyName("username")] + [JsonPropertyName("email")] [JsonInclude] public string Username { get; internal set; } = default!; [JsonPropertyName("flags")] [JsonInclude] public UserFlag Flags { get; internal set; } = default!; - - public MainServer Server { get; internal set; } = default!; [JsonIgnore] public IReadOnlyList Channels { get { - if (_Channels is null || ChannelIdList is not null) + if (_Channels is null && ChannelIdList is not null) { if (ChannelIdList.Length != 0) { _Channels = new List(); foreach (long channel in ChannelIdList) { - MainSocketChannel s = Server.GetChannel(channel, - MainSocketChannelContext.Default.MainSocketChannel, CancellationToken.None).Result; - Server.chans.Remove(s); - switch (s.Type) + try { - case ChannelType.GROUP: - _Channels.Add(Server.GetChannel(channel, - MainSocketGroupChannelContext.Default.MainSocketGroupChannel, CancellationToken.None).Result); - break; - case ChannelType.DM: - _Channels.Add(Server.GetChannel(channel, - MainSocketDMChannelContext.Default.MainSocketDMChannel, CancellationToken.None).Result); - break; + MainSocketChannel s = Server.GetChannel(channel, + MainSocketChannelContext.Default.MainSocketChannel, CancellationToken.None).Result; + Server.chans.Remove(s); + switch (s.Type) + { + case ChannelType.GROUP: + _Channels.Add(Server.GetChannel(channel, + MainSocketGroupChannelContext.Default.MainSocketGroupChannel, CancellationToken.None).Result); + break; + case ChannelType.DM: + _Channels.Add(Server.GetChannel(channel, + MainSocketDMChannelContext.Default.MainSocketDMChannel, CancellationToken.None).Result); + break; + } + } + catch (Exception e) + { + Console.WriteLine(e); } } } @@ -115,7 +120,7 @@ public class MainSocketAppUser : MainSocketUserBase, IAppUser [JsonInclude] public FR[] FriendRequestsRaw { get; internal set; } = default!; [JsonIgnore] - private List _Channels = default!; + private List _Channels = null!; [JsonIgnore] private List _Friends = default!; [JsonIgnore] diff --git a/Luski.net/Structures/Main/MainSocketChannel.cs b/Luski.net/Structures/Main/MainSocketChannel.cs index cc67919..a71e541 100755 --- a/Luski.net/Structures/Main/MainSocketChannel.cs +++ b/Luski.net/Structures/Main/MainSocketChannel.cs @@ -78,7 +78,7 @@ public class MainSocketChannel : IncomingHTTP { if (i.Id != Server.User.Id) { - long key = i.GetUserKey(CancellationToken).Result; + long key = i.GetUserKeys(CancellationToken).Result.First().Id; if (true) { WSSKeyExchange send = new() @@ -87,7 +87,7 @@ public class MainSocketChannel : IncomingHTTP channel = Id, key = Convert.ToBase64String(Encryption.RSA.Encrypt(lkey, key.ToString(), EncoderType.UTF8)) }; - Server.SendServer(send, WSSKeyExchangeContext.Default.WSSKeyExchange); + Server.SendServerOld(send, WSSKeyExchangeContext.Default.WSSKeyExchange); } } }); diff --git a/Luski.net/Structures/Main/MainSocketTextChannel.cs b/Luski.net/Structures/Main/MainSocketTextChannel.cs index a8cae22..f0a596b 100755 --- a/Luski.net/Structures/Main/MainSocketTextChannel.cs +++ b/Luski.net/Structures/Main/MainSocketTextChannel.cs @@ -22,14 +22,14 @@ public class MainSocketTextChannel : MainSocketChannel return await Server.GetMessage(ID, CancellationToken); } - public async Task GetPicture(CancellationToken CancellationToken) + public async Task GetPicture(CancellationToken CancellationToken) { if (Type == ChannelType.DM) return Members.First().GetAvatar(CancellationToken).Result; else { bool isc = System.IO.File.Exists(Server.Storage.GetStorageDirectory(StorageDirectory.ChannelIcons) + Id.ToString()); if (!isc) await Server.GetFromServer($"SocketChannel/GetPicture/{Id}", Server.Storage.GetStorageDirectory(StorageDirectory.ChannelIcons) + Id.ToString(), CancellationToken); - return Server.Storage.GetResourceBytes(StorageDirectory.ChannelIcons, Id.ToString()); + return Server.Storage.GetResourceStream(StorageDirectory.ChannelIcons, Id.ToString()); } } diff --git a/Luski.net/Structures/Main/MainSocketUserBase.cs b/Luski.net/Structures/Main/MainSocketUserBase.cs index 35c9ded..2892b77 100644 --- a/Luski.net/Structures/Main/MainSocketUserBase.cs +++ b/Luski.net/Structures/Main/MainSocketUserBase.cs @@ -1,3 +1,4 @@ +using System.IO; using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; @@ -22,17 +23,18 @@ public class MainSocketUserBase : IncomingHTTP, IUser [JsonPropertyName("picture_type")] [JsonInclude] public PictureType PictureType { get; internal set; } = default!; - public async Task GetAvatar(CancellationToken CancellationToken) + public async Task GetAvatar(CancellationToken CancellationToken) { bool isc = System.IO.File.Exists(Server.Storage.GetStorageDirectory(StorageDirectory.Avatars) + Id.ToString()); if (!isc) await Server.GetFromServer($"socketuserprofile/Avatar/{Id}", Server.Storage.GetStorageDirectory(StorageDirectory.Avatars) + Id.ToString(), CancellationToken); - return Server.Storage.GetResourceBytes(StorageDirectory.Avatars, Id.ToString()); + return Server.Storage.GetResourceStream(StorageDirectory.Avatars, Id.ToString()); } - public Task GetUserKey(CancellationToken CancellationToken) + public Task GetUserKeys(CancellationToken CancellationToken) { string data = Server.GetFromServer($"Keys/GetUserKey/{Id}", CancellationToken).Content.ReadAsStringAsync().Result; - return Task.FromResult(long.Parse(data)); + //return Task.FromResult(new long[] {long.Parse(data)}); + return Task.FromResult(new[] { new PublicKeyInfo() { Id = long.Parse(data) }}); } } diff --git a/Luski.net/Structures/Public/Color.cs b/Luski.net/Structures/Public/Color.cs new file mode 100644 index 0000000..bc225b2 --- /dev/null +++ b/Luski.net/Structures/Public/Color.cs @@ -0,0 +1,40 @@ +using System; + +namespace Luski.net.Structures.Public; + +public class Color +{ + public Color(string servercol) + { + Bytes = servercol; + } + + public Color(byte R, byte G, byte B, byte A) + { + Bytes = $"{Convert.ToChar(R)}{Convert.ToChar(G)}{Convert.ToChar(B)}{Convert.ToChar(A)}"; + } + + private string Bytes; + + public string ToDB() + { + return Bytes; + } + + public byte A + { + get => (byte)(Bytes[3]); + } + public byte R + { + get => (byte)(Bytes[0]); + } + public byte G + { + get => (byte)(Bytes[1]); + } + public byte B + { + get => (byte)(Bytes[2]); + } +} \ No newline at end of file diff --git a/Luski.net/Structures/Public/PublicSocketAppUser.cs b/Luski.net/Structures/Public/PublicSocketAppUser.cs deleted file mode 100755 index 65706f5..0000000 --- a/Luski.net/Structures/Public/PublicSocketAppUser.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using Luski.net.Interfaces; -using Luski.net.JsonTypes.BaseTypes; -using System.Collections.Generic; -using System.Linq; -using System.Text.Json.Serialization; -using System.Text.Json.Serialization.Metadata; -using System.Threading; -using System.Threading.Tasks; -using JacobTechEncryption; -using Luski.net.Enums; -using Luski.net.Enums.Main; -using Luski.net.Structures.Main; - -namespace Luski.net.Structures.Public; - -public class PublicSocketAppUser : PublicSocketUserBase, IAppUser -{ - [JsonPropertyName("selected_channel")] - [JsonInclude] - public long SelectedChannel { get; internal set; } = default!; - [JsonPropertyName("username")] - [JsonInclude] - public string Username { get; internal set; } = default!; -} - -[JsonSerializable(typeof(PublicSocketAppUser))] -[JsonSourceGenerationOptions( - GenerationMode = JsonSourceGenerationMode.Default, - PropertyNamingPolicy = JsonKnownNamingPolicy.Unspecified, - WriteIndented = false, - DefaultIgnoreCondition = JsonIgnoreCondition.Never)] -internal partial class PublicSocketAppUserContext : JsonSerializerContext -{ - -} diff --git a/Luski.net/Structures/Public/PublicSocketUserBase.cs b/Luski.net/Structures/Public/PublicSocketUserBase.cs deleted file mode 100644 index 4fab6c2..0000000 --- a/Luski.net/Structures/Public/PublicSocketUserBase.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System.Text.Json.Serialization; -using System.Threading; -using System.Threading.Tasks; -using Luski.net.Enums; -using Luski.net.Interfaces; -using Luski.net.JsonTypes.BaseTypes; - -namespace Luski.net.Structures.Public; - -public class PublicSocketUserBase : IncomingHTTP, IUser -{ - public PublicServer Server { get; internal set; } = default!; - [JsonPropertyName("id")] - [JsonInclude] - public long Id { get; internal set; } = default!; - [JsonPropertyName("username")] - [JsonInclude] - public string DisplayName { get; internal set; } = default!; - [JsonPropertyName("status")] - [JsonInclude] - public UserStatus Status { get; internal set; } = default!; - [JsonPropertyName("picture_type")] - [JsonInclude] - public PictureType PictureType { get; internal set; } = default!; - public async Task GetAvatar(CancellationToken CancellationToken) - { - bool isc = System.IO.File.Exists(Server.Storage.GetStorageDirectory(StorageDirectory.Avatars) + Id.ToString()); - if (!isc) await Server.GetFromServer($"socketuserprofile/Avatar/{Id}", Server.Storage.GetStorageDirectory(StorageDirectory.Avatars) + Id.ToString(), CancellationToken); - return Server.Storage.GetResourceBytes(StorageDirectory.Avatars, Id.ToString()); - } - - public Task GetUserKey(CancellationToken CancellationToken) - { - string data = Server.GetFromServer($"Keys/GetUserKey/{Id}", CancellationToken).Content.ReadAsStringAsync().Result; - return Task.FromResult(long.Parse(data)); - } -} - -[JsonSerializable(typeof(PublicSocketUserBase))] -[JsonSourceGenerationOptions( - GenerationMode = JsonSourceGenerationMode.Default, - PropertyNamingPolicy = JsonKnownNamingPolicy.Unspecified, - WriteIndented = false, - DefaultIgnoreCondition = JsonIgnoreCondition.Never)] -internal partial class PublicSocketUserBaseContext : JsonSerializerContext -{ - -} \ No newline at end of file diff --git a/Luski.net/Structures/Public/Role.cs b/Luski.net/Structures/Public/Role.cs new file mode 100644 index 0000000..d5a3b78 --- /dev/null +++ b/Luski.net/Structures/Public/Role.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Luski.net.Enums.Public; + +namespace Luski.net.Structures.Public; + +public class Role +{ + public required PublicServer Server { get; init; } = default!; + public required long ID { get; init; } = default!; + public required string Name { get; init; } = default!; + public required int Index { get; init; } = default!; + public required Color Color { get; init; } = default!; + public required string Description { get; init; } = default!; + public required string DisplayName { get; init; } = default!; + public required ServerPermission[] ServerPermissions { get; init; } = default!; + public required long[] MembersListID { get; init; } = default!; + private List? RawUsers = null; + + public Task GetMembers() + { + if (RawUsers is null) + { + + } + + return Task.FromResult(RawUsers!.ToArray()); + } +} \ No newline at end of file diff --git a/Luski.net/Structures/Public/RoleOveride.cs b/Luski.net/Structures/Public/RoleOveride.cs new file mode 100644 index 0000000..c79ebce --- /dev/null +++ b/Luski.net/Structures/Public/RoleOveride.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; + +namespace Luski.net.Structures.Public; + +public class RoleOveride +{ + public long ID { get; init; } + public long ParentRoleID { get; init; } + internal string[] RawOverides { get; init; } + private Role? Parent = null; + + public Task GetRole() + { + if (Parent is null) + { + + } + + return Task.FromResult(Parent)!; + } +} \ No newline at end of file diff --git a/Luski.net/Structures/Public/SocketAppUser.cs b/Luski.net/Structures/Public/SocketAppUser.cs new file mode 100755 index 0000000..b29e5c5 --- /dev/null +++ b/Luski.net/Structures/Public/SocketAppUser.cs @@ -0,0 +1,17 @@ +using System.Threading; +using System.Threading.Tasks; +using Luski.net.Enums; + +namespace Luski.net.Structures.Public; + +public class SocketAppUser : SocketUser +{ + public long SelectedChannel { get; init; } = default!; + + public async Task GetSelectedChannel(CancellationToken Token) + { + return await Server.GetChannel(SelectedChannel, Token); + } + + public string Username { get; init; } = default!; +} \ No newline at end of file diff --git a/Luski.net/Structures/Public/SocketCategory.cs b/Luski.net/Structures/Public/SocketCategory.cs new file mode 100644 index 0000000..0e60844 --- /dev/null +++ b/Luski.net/Structures/Public/SocketCategory.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using JacobTechEncryption.Enums; +using Luski.net.Enums.Public; + +namespace Luski.net.Structures.Public; + +public class SocketCategory +{ + public PublicServer Server { get; init; } = default!; + public long ID { get; init; } + public Color Color { get; init; } + internal long ParentID { get; set; } + internal long[] RoleOverides { get; set; } + internal long[] UserOverides { get; set; } + internal long[] Channels { get; set; } + internal long[] Categories { get; set; } + SocketCategory? RawParent = null; + List? RawRoleOverides = null; + List? RawUserOverides = null; + List? RawChan = null; + List? RawCat = null; + + public async Task GetParent() + { + if (ParentID != -1 && RawParent is null) + { + RawParent = await Server.GetCategory(ParentID, CancellationToken.None); + } + + return RawParent; + } + + public async Task GetChannels() + { + if (RawChan is null) + { + RawChan = new(); + foreach (long chan in Channels) + { + RawChan.Add(await Server.GetChannel(chan, CancellationToken.None)); + } + } + + if (RawChan.Count != Channels.Length) + { + foreach (long chan in Channels) + { + if (RawChan.Any(s => s.ID == chan)) continue; + RawChan.Add(await Server.GetChannel(chan, CancellationToken.None)); + } + } + + return RawChan.ToArray(); + } + public async Task GetCategories() + { + if (RawCat is null) + { + RawCat = new(); + foreach (long chan in Categories) + { + RawCat.Add(await Server.GetCategory(chan, CancellationToken.None)); + } + } + + if (RawCat.Count != Channels.Length) + { + foreach (long chan in Categories) + { + if (RawCat.Any(s => s.ID == chan)) continue; + RawCat.Add(await Server.GetCategory(chan, CancellationToken.None)); + } + } + + return RawCat.ToArray(); + } + public string Name { get; internal set; } + public string Description { get; internal set; } + + public Task GetRoleOverides() + { + if (RawRoleOverides is null) + { + RawRoleOverides = new(); + } + + return Task.FromResult(RawRoleOverides!.ToArray()); + } + public Task GetUserOveride() + { + if (RawUserOverides is null) + { + RawUserOverides = new(); + } + + return Task.FromResult(RawUserOverides!.ToArray()); + } + public long TitleEncryptionKey { get; internal set; } + public long DescriptionEncryptionKey { get; internal set; } + public EncoderType TitleEncoderType { get; internal set; } + public EncoderType DescriptionEncoderType { get; internal set; } +} \ No newline at end of file diff --git a/Luski.net/Structures/Public/SocketChannel.cs b/Luski.net/Structures/Public/SocketChannel.cs new file mode 100644 index 0000000..0c184e1 --- /dev/null +++ b/Luski.net/Structures/Public/SocketChannel.cs @@ -0,0 +1,217 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using JacobTechEncryption; +using JacobTechEncryption.Enums; +using Luski.net.Enums; +using Luski.net.Enums.Public; +using Luski.net.JsonTypes.Public; + +namespace Luski.net.Structures.Public; + +public class SocketChannel +{ + public PublicServer Server { get; init; } = default!; + public Color Color { get; init; } + public long ID { get; internal set; } + internal long CategoryID { get; set; } + internal long[] RoleOverides { get; set; } + internal long[] UserOverides { get; set; } + SocketCategory? RawParent = null; + List? RawRoleOverides = null; + List? RawUserOverides = null; + public PictureType PictureType { get; internal set; } + + public async Task> GetMessages(CancellationToken CancellationToken, int count = 50) + { + try + { + if (count > 200) + { + throw new Exception("You can not request more than 200 messages at a time"); + } + else if (count < 1) + { + throw new Exception("You must request at least 1 message"); + } + else + { + PublicSocketBulkMessage data = await Server.GetFromServer("SocketBulkMessage", + PublicSocketBulkMessageContext.Default.PublicSocketBulkMessage, + CancellationToken, + new KeyValuePair("id", ID.ToString()), + new KeyValuePair("messages", count.ToString())); + if (data is not null && !data.Error.HasValue) + { + int num = Convert.ToInt32(6); + if (num == 0) num = 1; + //string key = Server.EncryptionHandler.GetKey(enc) + //string key = Server.EncryptionHandler.GetChannelKey(ID); + if (data.Messages is null) data.Messages = Array.Empty(); + List mmmm = new(); + ParallelLoopResult p = Parallel.ForEach(data.Messages, new ParallelOptions() + { + MaxDegreeOfParallelism = num + }, i => + { + if (i.EncryptionKey == 0) + { + if (string.IsNullOrEmpty(i.Context)) + { + i.Context = ""; + } + else i.Context = Encryption.Generic.Encoders[(int)i.EncoderType] + .GetString(Convert.FromBase64String(i.Context)); + } + else + { + LocalKeyInfo key = Server.EncryptionHandler.GetKey(i.EncryptionKey); + switch (key.EncryptionType) + { + case EncryptionType.RSA: + i.Context = Encryption.RSA.Decrypt(Convert.FromBase64String(i.Context), key.Key, + i.EncoderType); + break; + default: + i.Context = Encryption.Generic.Encoders[(int)i.EncoderType] + .GetString(Convert.FromBase64String(i.Context)); + break; + } + } + + if (i.Files.Length > 0) + { + for (int j = 0; j < i.Files.Length; j++) + { + if (i.Files[j].Key == 0) + { + if (string.IsNullOrEmpty(i.Files[j].Name)) + { + i.Files[j].Name = ""; + } + else i.Files[j].Name = Encryption.Generic.Encoders[(int)i.Files[j].NameEncoder] + .GetString(Convert.FromBase64String(i.Files[j].Name)); + } + else + { + LocalKeyInfo key = Server.EncryptionHandler.GetKey(i.Files[j].NameKey); + switch (key.EncryptionType) + { + case EncryptionType.RSA: + i.Files[j].Name = Encryption.RSA.Decrypt(Convert.FromBase64String(i.Context), key.Key, + i.Files[j].NameEncoder); + break; + default: + i.Files[j].Name = Encryption.Generic.Encoders[(int)i.Files[j].NameEncoder] + .GetString(Convert.FromBase64String(i.Context)); + break; + } + } + } + } + }); + + foreach (PublicMessage i in data.Messages) + { + var ff = new List(); + List sf = new(); + foreach (pFile v in i.Files) + { + sf.Add(new() + { + ID = v.ID, + Size = v.Size, + Name = v.Name, + Encoder = v.Encoder, + NameEncoder = v.NameEncoder, + Key = v.Key, + NameKey = v.NameKey, + Channel = v.Channel, + Server = Server + }); + ff.Add(v.ID); + } + mmmm.Add(new() + { + Server = Server, + ID = i.ID, + ChannelID = ID, + AuthorID = i.AuthorID, + TimeStamp = i.TimeStamp, + Context = i.Context, + EncryptionKey = i.EncryptionKey, + EncoderType = i.EncoderType, + FileIDs = ff.ToArray(), + _Files = sf + }); + } + return await Task.FromResult(mmmm.AsReadOnly()); + } + else + { + throw data?.Error switch + { + ErrorCode.InvalidToken => new Exception("Your current token is no longer valid"), + ErrorCode.ServerError => new Exception($"Error from server: {data.ErrorMessage}"), + ErrorCode.InvalidHeader => new Exception(data.ErrorMessage), + ErrorCode.MissingHeader => new Exception("The header sent to the server was not found. This may be because you app is couropt or you are using the wron API version"), + ErrorCode.Forbidden => new Exception("You are not allowed to do this request"), + _ => new Exception(data?.Error.ToString()), + }; + } + } + } + catch (Exception) + { + throw; + } + } + + public async Task GetPicture(CancellationToken CancellationToken) + { + bool isc = System.IO.File.Exists(Server.Storage.GetStorageDirectory(StorageDirectory.ChannelIcons) + ID.ToString()); + if (!isc) await Server.GetFromServer($"SocketChannel/GetPicture", Server.Storage.GetStorageDirectory(StorageDirectory.ChannelIcons) + ID.ToString(), CancellationToken, + new KeyValuePair("id", ID.ToString())); + return Server.Storage.GetResourceStream(StorageDirectory.ChannelIcons, ID.ToString()); + } + + public async Task GetParent() + { + if (CategoryID != -1 && RawParent is null) + { + RawParent = await Server.GetCategory(CategoryID, CancellationToken.None); + } + + return RawParent!; + } + public ChannelType Type { get; internal set; } + public DateTime Epoch { get; internal set; } + public string Name { get; internal set; } + public string Description { get; internal set; } + public Task GetRoleOverides() + { + if (RawRoleOverides is null) + { + RawRoleOverides = new(); + } + + return Task.FromResult(RawRoleOverides!.ToArray()); + } + public Task GetUserOveride() + { + if (RawUserOverides is null) + { + RawUserOverides = new(); + } + + return Task.FromResult(RawUserOverides!.ToArray()); + } + public long TitleEncryptionKey { get; internal set; } + public long DescriptionEncryptionKey { get; internal set; } + public long[] EncryptionKeys { get; internal set; } + public EncoderType TitleEncoderType { get; internal set; } + public EncoderType DescriptionEncoderType { get; internal set; } + public EncoderType[] EncoderTypes { get; internal set; } +} \ No newline at end of file diff --git a/Luski.net/Structures/Public/SocketFile.cs b/Luski.net/Structures/Public/SocketFile.cs new file mode 100644 index 0000000..b167566 --- /dev/null +++ b/Luski.net/Structures/Public/SocketFile.cs @@ -0,0 +1,28 @@ +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using JacobTechEncryption.Enums; +using Luski.net.Enums; + +namespace Luski.net.Structures.Public; + +public class SocketFile +{ + public required PublicServer Server { get; init; } + public required long ID { get; init; } + public required long Channel { get; init; } + public required string Name { get; init; } + public required EncoderType Encoder { get; init; } + public required EncoderType NameEncoder { get; init; } + public required long Key { get; init; } + public required long NameKey { get; init; } + public required long Size { get; init; } + + public async Task GetCache(CancellationToken CancellationToken) + { + string d = Server.Storage.GetStorageDirectory(StorageDirectory.Files) + Channel.ToString() + "-" + ID.ToString(); + bool isc = System.IO.File.Exists(d); + if (!isc) await Server.GetFromServer($"socketfile?id={ID}&channel={Channel}", d, CancellationToken); + return Server.Storage.GetResourceStream(StorageDirectory.Files, Channel.ToString() + "-" + ID.ToString()); + } +} \ No newline at end of file diff --git a/Luski.net/Structures/Public/SocketMessage.cs b/Luski.net/Structures/Public/SocketMessage.cs new file mode 100644 index 0000000..6b5617a --- /dev/null +++ b/Luski.net/Structures/Public/SocketMessage.cs @@ -0,0 +1,49 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using JacobTechEncryption.Enums; +using Luski.net.Interfaces; + +namespace Luski.net.Structures.Public; + +public class SocketMessage +{ + public PublicServer Server { get; init; } = default!; + public long ID { get; internal set; } + public long ChannelID { get; internal set; } + public long AuthorID { get; internal set; } + public long TimeStamp { get; internal set; } + public string Context { get; internal set; } + public long EncryptionKey { get; internal set; } + public long[] FileIDs { get; internal set; } + public EncoderType EncoderType { get; internal set; } + private SocketChannel? RawParent; + private IUser? au; + internal List _Files = new(); + + public IReadOnlyList Files + { + get => _Files.AsReadOnly(); + } + + public async Task GetParent(CancellationToken token) + { + if (RawParent is null) + { + RawParent = await Server.GetChannel(ChannelID, token); + } + + return RawParent; + } + + public async Task GetAuthor(CancellationToken token) + { + if (au is null) + { + if (AuthorID == Server.User.Id) au = Server.User; + else au = await Server.GetUser(ChannelID, token); + } + + return au; + } +} \ No newline at end of file diff --git a/Luski.net/Structures/Public/SocketUser.cs b/Luski.net/Structures/Public/SocketUser.cs new file mode 100644 index 0000000..82a1761 --- /dev/null +++ b/Luski.net/Structures/Public/SocketUser.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.Json.Serialization; +using System.Threading; +using System.Threading.Tasks; +using Luski.net.Classes; +using Luski.net.Enums; +using Luski.net.Interfaces; +using Luski.net.JsonTypes; +using Luski.net.JsonTypes.BaseTypes; + +namespace Luski.net.Structures.Public; + +public class SocketUser : IUser +{ + public PublicServer Server { get; init; } = default!; + public long Id { get; init; } = default!; + public string DisplayName { get; init; } = default!; + public virtual UserStatus Status { get; internal set; } = default!; + public PictureType PictureType { get; init; } = default!; + public long[] RoleIds { get; init; } = default!; + private List? RawRoles = null; + + public async Task GetRoles() + { + if (RawRoles is null) + { + RawRoles = new(); + foreach (long r in RoleIds) + { + Role f = await Server.GetRole(r); + RawRoles.Add(f); + } + RawRoles.Sort(new RoleComparer()); + } + + return RawRoles.ToArray(); + } + + public async Task GetAvatar(CancellationToken CancellationToken) + { + bool isc = System.IO.File.Exists(Server.Storage.GetStorageDirectory(StorageDirectory.Avatars) + Id.ToString()); + if (!isc) await Server.GetFromServer($"socketuserprofile/Avatar/{Id}", Server.Storage.GetStorageDirectory(StorageDirectory.Avatars) + Id.ToString(), CancellationToken); + return Server.Storage.GetResourceStream(StorageDirectory.Avatars, Id.ToString()); + } + + public Task GetUserKeys(CancellationToken CancellationToken) + { + UserKeysGetRequest data = Server.GetFromServer($"Keys/UserKeys/{Id}", UserKeysGetRequestContext.Default.UserKeysGetRequest, CancellationToken).Result; + List pki = new(); + foreach (UserKeyGetRequest key in data.keys) + { + pki.Add(new() + { + Id = key.id, + Owner = key.owner, + EncryptionType = key.encryption_type, + Data = Convert.FromBase64String(key.key_data) + }); + } + return Task.FromResult(pki.ToArray()); + } +} \ No newline at end of file diff --git a/Luski.net/Structures/Public/UserOveride.cs b/Luski.net/Structures/Public/UserOveride.cs new file mode 100644 index 0000000..35771ec --- /dev/null +++ b/Luski.net/Structures/Public/UserOveride.cs @@ -0,0 +1,6 @@ +namespace Luski.net.Structures.Public; + +public class UserOveride +{ + +} \ No newline at end of file diff --git a/Luski.net/Structures/PublicKeyInfo.cs b/Luski.net/Structures/PublicKeyInfo.cs new file mode 100644 index 0000000..e680be8 --- /dev/null +++ b/Luski.net/Structures/PublicKeyInfo.cs @@ -0,0 +1,11 @@ +using JacobTechEncryption.Enums; + +namespace Luski.net.Structures; + +public class PublicKeyInfo +{ + public long Id { get; init; } + public byte[] Data { get; init; } + public long Owner { get; init; } + public EncryptionType EncryptionType { get; init; } +} \ No newline at end of file From 0349745944e302d8836534833fe4ae2ed6c13a33 Mon Sep 17 00:00:00 2001 From: JacobTech Date: Fri, 25 Aug 2023 12:07:36 -0400 Subject: [PATCH 07/14] Alternate servers. On load, the client can find alternate servers to connect to. --- Luski.net/API.cs | 11 +--- Luski.net/JsonTypes/LocalServerInfo.cs | 21 ++++++ Luski.net/JsonTypes/ServerData.cs | 23 +++++++ Luski.net/JsonTypes/ServerInfo.cs | 4 ++ Luski.net/Luski.net.csproj | 2 +- Luski.net/PublicServer.cs | 66 ++++++++++++++++--- Luski.net/Server.Encryption.cs | 5 +- Luski.net/Server.Globals.cs | 2 +- Luski.net/Server.Storage.cs | 37 +++++++++++ Luski.net/Server.cs | 2 +- .../Structures/Main/MainSocketUserBase.cs | 1 - 11 files changed, 151 insertions(+), 23 deletions(-) create mode 100644 Luski.net/JsonTypes/LocalServerInfo.cs create mode 100644 Luski.net/JsonTypes/ServerData.cs diff --git a/Luski.net/API.cs b/Luski.net/API.cs index 951adbe..7cc78c5 100644 --- a/Luski.net/API.cs +++ b/Luski.net/API.cs @@ -15,24 +15,19 @@ public class API public async Task GetPublicServer(string Domain, string Version = "v1", bool Secure = true) { - DateTime dt = DateTime.UtcNow; - Console.WriteLine("Connecting to public server '{0}' using API {1}.", Domain, Version); PublicServer s; try { IEnumerable isl = InternalServers.Where(a => (a.Domain == Domain && a.ApiVersion == Version)); if (isl.Any()) return isl.First(); - s = new(Domain, Version, Secure) - { - ServerType = ServerType.Public - }; + s = await PublicServer.GetServer(Domain, Version, Secure); } catch (Exception e) { - Console.WriteLine("Failed to connect to public server '{0}' using API {1}.", Domain, Version); + Console.WriteLine("Failed to connect to public server '{0}' using API {1}. No alternate server was found.", Domain, Version); throw; } - Console.WriteLine("Connected to public server '{0}' using API {1} in {4}.\nServer Name: {2}\nServer Description: {3}", Domain, Version, s.Name, s.Description, DateTime.UtcNow.Subtract(dt).ToString("g")); + string? f = s.Storage.GetStorageDirectory(StorageDirectory.StorageInfo) + "token"; if (File.Exists(f)) { diff --git a/Luski.net/JsonTypes/LocalServerInfo.cs b/Luski.net/JsonTypes/LocalServerInfo.cs new file mode 100644 index 0000000..8489c80 --- /dev/null +++ b/Luski.net/JsonTypes/LocalServerInfo.cs @@ -0,0 +1,21 @@ +using System; +using System.Text.Json.Serialization; + +namespace Luski.net.JsonTypes; + +public class LocalServerInfo +{ + [JsonInclude] + [JsonPropertyName("alternate_servers")] + public ServerData[] AlternateServers { get; set; } = Array.Empty(); +} + +[JsonSerializable(typeof(LocalServerInfo))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + WriteIndented = false)] +internal partial class LocalServerInfoContext : JsonSerializerContext +{ + +} \ No newline at end of file diff --git a/Luski.net/JsonTypes/ServerData.cs b/Luski.net/JsonTypes/ServerData.cs new file mode 100644 index 0000000..3e25f0b --- /dev/null +++ b/Luski.net/JsonTypes/ServerData.cs @@ -0,0 +1,23 @@ +using System.Text.Json.Serialization; + +namespace Luski.net.JsonTypes; + +public class ServerData +{ + [JsonInclude] + [JsonPropertyName("address")] + public string DomainAndPort = default!; + [JsonInclude] + [JsonPropertyName("secure")] + public bool Secure; +} + +[JsonSerializable(typeof(ServerData))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Default, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + WriteIndented = false)] +internal partial class ServerDataContext : JsonSerializerContext +{ + +} \ No newline at end of file diff --git a/Luski.net/JsonTypes/ServerInfo.cs b/Luski.net/JsonTypes/ServerInfo.cs index 04685a8..f341418 100644 --- a/Luski.net/JsonTypes/ServerInfo.cs +++ b/Luski.net/JsonTypes/ServerInfo.cs @@ -9,6 +9,10 @@ public class ServerInfo : IncomingHTTP public string wssv4 { get; set; } public string description { get; set; } public long owner { get; set; } + + [JsonInclude] + [JsonPropertyName("alternate_servers")] + public ServerData[] AlternateServers { get; set; } = default!; } [JsonSerializable(typeof(ServerInfo))] diff --git a/Luski.net/Luski.net.csproj b/Luski.net/Luski.net.csproj index a003ead..62ec8b5 100755 --- a/Luski.net/Luski.net.csproj +++ b/Luski.net/Luski.net.csproj @@ -13,7 +13,7 @@ https://github.com/JacobTech-com/Luski.net True 1.0.0 - 2.0.0-alpha12 + 2.0.0-alpha23 diff --git a/Luski.net/PublicServer.cs b/Luski.net/PublicServer.cs index fffe07a..1539d63 100644 --- a/Luski.net/PublicServer.cs +++ b/Luski.net/PublicServer.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net.Http; using System.Text; using System.Text.Json.Serialization.Metadata; using System.Threading; @@ -29,14 +30,63 @@ public partial class PublicServer : Server public List roles { get; } = new(); public SocketAppUser User { get; private set; } = null!; - - internal PublicServer(string Domain, string API_Version, bool Secure = true): + + private PublicServer(string Domain, string API_Version, bool Secure = true) : base(Domain, API_Version, Secure) + { } + + internal static async Task GetServer(string Domain, string API_Version, bool Secure = true) { - ServerInfo si = GetFromServer("socketserver", ServerInfoContext.Default.ServerInfo, CancellationToken.None).Result; - Name = si.name; - Description = si.description; - wssurl = si.wssv4; + DateTime dt = DateTime.UtcNow; + Console.WriteLine("Connecting to public server '{0}' using API {1}.", Domain, API_Version); + PublicServer s = new(Domain, API_Version, Secure); + ServerInfo? si = null; + try + { + si = await s.GetFromServer("socketserver", ServerInfoContext.Default.ServerInfo, CancellationToken.None); + s.EncryptionHandler.ServerPublicKey = await (await new HttpClient() + .GetAsync($"{(s.Secure ? "https" : "http")}://{s.Domain}/{s.ApiVersion}/Keys/PublicKey")) + .Content + .ReadAsStringAsync(); + } + catch (Exception e) + { + LocalServerInfo ServerListing = s.Storage.GetJson(StorageDirectory.ServerInfo, "Servers.json", true, + LocalServerInfoContext.Default.LocalServerInfo); + if (ServerListing.AlternateServers.Length > 0) + { + Console.WriteLine("Failed to connect to public server '{0}' using API {1}. Attempting to connect to alternate servers.", Domain, API_Version); + foreach (ServerData Server in ServerListing.AlternateServers) + { + s.Secure = Server.Secure; + s.Domain = Server.DomainAndPort; + + try + { + + si = await s.GetFromServer("socketserver", ServerInfoContext.Default.ServerInfo, CancellationToken.None); + s.EncryptionHandler.ServerPublicKey = await (await new HttpClient() + .GetAsync($"{(s.Secure ? "https" : "http")}://{s.Domain}/{s.ApiVersion}/Keys/PublicKey")) + .Content + .ReadAsStringAsync(); + Console.WriteLine("Public server '{0}' connection restored by alternate server '{1}' using API {2}.", Domain, s.Domain, API_Version); + break; + } + catch + { + // ignored + } + } + } + + if (si is null) throw; + } + s.Name = si.name; + s.Description = si.description; + s.wssurl = si.wssv4; + s.ServerType = ServerType.Public; + Console.WriteLine("Connected to public server '{0}' using API {1} in {4}.\nServer Name: {2}\nServer Description: {3}", Domain, API_Version, s.Name, s.Description, DateTime.UtcNow.Subtract(dt).ToString("g")); + return s; } public async Task GetCategory(long id, CancellationToken CancellationToken) where TCategory : SocketCategory, new() @@ -295,6 +345,6 @@ public partial class PublicServer : Server return Task.CompletedTask; } - public string Name { get; } - public string Description { get; } + public string Name { get; private set; } + public string Description { get; private set; } } \ No newline at end of file diff --git a/Luski.net/Server.Encryption.cs b/Luski.net/Server.Encryption.cs index ef0820d..0cfa80c 100644 --- a/Luski.net/Server.Encryption.cs +++ b/Luski.net/Server.Encryption.cs @@ -12,12 +12,11 @@ public class ServerEncryption { internal bool Generating, Generated; internal string ServerPublicKey = "", MyPublicKey = "", myPrivateKey = "", OfflinePrivateKey = "", OfflinePublicKey = ""; + internal byte[] Hash = default!; - internal ServerEncryption(string Domain, string API_Version, ServerStorage Storage, bool Secure) + internal ServerEncryption(ServerStorage Storage) { this.Storage = Storage; - ServerPublicKey = new HttpClient().GetAsync($"{(Secure ? "https" : "http" )}://{Domain}/{API_Version}/Keys/PublicKey").Result.Content - .ReadAsStringAsync().Result; } public string GetChannelKey(long Channel) diff --git a/Luski.net/Server.Globals.cs b/Luski.net/Server.Globals.cs index a39d109..27893e5 100644 --- a/Luski.net/Server.Globals.cs +++ b/Luski.net/Server.Globals.cs @@ -14,7 +14,7 @@ namespace Luski.net; public partial class Server { public ServerType ServerType { get; internal set; } = ServerType.Public; - public string Domain { get; } = default!; + public string Domain { get; set; } = default!; public string ApiVersion { get; } = "v1"; internal WebSocket? ServerOut; internal string? Token = null, Error = null, gen = null; diff --git a/Luski.net/Server.Storage.cs b/Luski.net/Server.Storage.cs index 80e079c..af191e4 100644 --- a/Luski.net/Server.Storage.cs +++ b/Luski.net/Server.Storage.cs @@ -4,6 +4,7 @@ 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; @@ -73,6 +74,20 @@ public class ServerStorage 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 byte[] UpdateStorage(byte[] OldPassword) { try @@ -218,6 +233,11 @@ public class ServerStorage 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)); @@ -283,6 +303,23 @@ public class ServerStorage return ms; } + public Stream GetResourceStream(string 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); diff --git a/Luski.net/Server.cs b/Luski.net/Server.cs index 9f514ec..00ca01d 100644 --- a/Luski.net/Server.cs +++ b/Luski.net/Server.cs @@ -25,7 +25,7 @@ public partial class Server this.ApiVersion = API_Version; this.Secure = Secure; Storage = new(Domain); - EncryptionHandler = new(Domain, API_Version, Storage, this.Secure); + EncryptionHandler = new(Storage); } internal bool Secure = true; diff --git a/Luski.net/Structures/Main/MainSocketUserBase.cs b/Luski.net/Structures/Main/MainSocketUserBase.cs index 2892b77..bf3eb69 100644 --- a/Luski.net/Structures/Main/MainSocketUserBase.cs +++ b/Luski.net/Structures/Main/MainSocketUserBase.cs @@ -33,7 +33,6 @@ public class MainSocketUserBase : IncomingHTTP, IUser public Task GetUserKeys(CancellationToken CancellationToken) { string data = Server.GetFromServer($"Keys/GetUserKey/{Id}", CancellationToken).Content.ReadAsStringAsync().Result; - //return Task.FromResult(new long[] {long.Parse(data)}); return Task.FromResult(new[] { new PublicKeyInfo() { Id = long.Parse(data) }}); } } From 0740c6daca5e02c6e45c8186f4107c7f29fee69b Mon Sep 17 00:00:00 2001 From: JacobTech Date: Sun, 1 Oct 2023 12:28:10 -0400 Subject: [PATCH 08/14] Try method for public server. Added TryGetPublicServer to API handler. --- Luski.net/API.cs | 30 +++++++++++++++++++++++++++--- Luski.net/Luski.net.csproj | 2 +- Luski.net/MainServer.cs | 4 ++-- Luski.net/PublicServer.Account.cs | 4 ++++ Luski.net/PublicServer.cs | 8 ++++---- Luski.net/Server.cs | 4 +++- 6 files changed, 41 insertions(+), 11 deletions(-) diff --git a/Luski.net/API.cs b/Luski.net/API.cs index 7cc78c5..4688b37 100644 --- a/Luski.net/API.cs +++ b/Luski.net/API.cs @@ -10,8 +10,28 @@ namespace Luski.net; public class API { public MainServer MainServer { get; internal set; } + + public bool IsAnyServerLoggedin { get; internal set; } internal List InternalServers { get; } = new(); public IReadOnlyList LoadedServers => InternalServers.AsReadOnly(); + + public Task TryGetPublicServer(out PublicServer Server, string Domain, string Version = "v1", + bool Secure = true) + { + try + { + Task result = GetPublicServer(Domain, Version, Secure); + Task.WaitAll(result); + Server = result.Result; + return Task.FromResult(true); + } + catch (Exception e) + { + Console.WriteLine(e); + Server = null!; + return Task.FromResult(false); + } + } public async Task GetPublicServer(string Domain, string Version = "v1", bool Secure = true) { @@ -20,7 +40,7 @@ public class API { IEnumerable isl = InternalServers.Where(a => (a.Domain == Domain && a.ApiVersion == Version)); if (isl.Any()) return isl.First(); - s = await PublicServer.GetServer(Domain, Version, Secure); + s = await PublicServer.GetServer(this, Domain, Version, Secure); } catch (Exception e) { @@ -40,7 +60,11 @@ public class API if (f is not null) { bool b = await s.LoginViaToken(f); - if (b) Console.WriteLine("Auto Login Successful"); + if (b) + { + Console.WriteLine("Auto Login Successful"); + IsAnyServerLoggedin = true; + } else { s.Token = null; @@ -60,7 +84,7 @@ public class API { DateTime dt = DateTime.UtcNow; Console.WriteLine("Conecting to main server '{0}' using API {1}.", Domain, Version); - MainServer = new(Domain, Version) + MainServer = new(this, Domain, Version) { ServerType = ServerType.Main }; diff --git a/Luski.net/Luski.net.csproj b/Luski.net/Luski.net.csproj index 62ec8b5..204f3c7 100755 --- a/Luski.net/Luski.net.csproj +++ b/Luski.net/Luski.net.csproj @@ -13,7 +13,7 @@ https://github.com/JacobTech-com/Luski.net True 1.0.0 - 2.0.0-alpha23 + 2.0.0-alpha24 diff --git a/Luski.net/MainServer.cs b/Luski.net/MainServer.cs index 26f2f76..876c1f5 100644 --- a/Luski.net/MainServer.cs +++ b/Luski.net/MainServer.cs @@ -20,8 +20,8 @@ namespace Luski.net; public partial class MainServer : Server { - internal MainServer(string Domain, string API_Version): - base(Domain, API_Version) + internal MainServer(API api, string Domain, string API_Version): + base(api, Domain, API_Version) { } diff --git a/Luski.net/PublicServer.Account.cs b/Luski.net/PublicServer.Account.cs index 92f2b5e..435e5a0 100644 --- a/Luski.net/PublicServer.Account.cs +++ b/Luski.net/PublicServer.Account.cs @@ -171,6 +171,7 @@ public partial class PublicServer stor); EncryptionHandler.OfflinePublicKey = null!; EncryptionHandler.OfflinePrivateKey = null!; + API_Handler.IsAnyServerLoggedin = true; return true; } else @@ -178,6 +179,7 @@ public partial class PublicServer throw new Exception(json?.ErrorMessage); } + API_Handler.IsAnyServerLoggedin = true; return true; } @@ -344,10 +346,12 @@ public partial class PublicServer // _ = await setkey.PostAsync($"{(Secure ? "https" : "http" )}://{Domain}/{ApiVersion}/Keys/SetOfflineKey", new StringContent(EncryptionHandler.OfflinePublicKey)); EncryptionHandler.OfflinePublicKey = null!; EncryptionHandler.OfflinePrivateKey = null!; + API_Handler.IsAnyServerLoggedin = true; return true; } else throw new Exception(json?.ErrorMessage); + API_Handler.IsAnyServerLoggedin = true; return true; } } \ No newline at end of file diff --git a/Luski.net/PublicServer.cs b/Luski.net/PublicServer.cs index 1539d63..8a7b9f3 100644 --- a/Luski.net/PublicServer.cs +++ b/Luski.net/PublicServer.cs @@ -31,15 +31,15 @@ public partial class PublicServer : Server public SocketAppUser User { get; private set; } = null!; - private PublicServer(string Domain, string API_Version, bool Secure = true) : - base(Domain, API_Version, Secure) + private PublicServer(API api,string Domain, string API_Version, bool Secure = true) : + base(api, Domain, API_Version, Secure) { } - internal static async Task GetServer(string Domain, string API_Version, bool Secure = true) + internal static async Task GetServer(API api, string Domain, string API_Version, bool Secure = true) { DateTime dt = DateTime.UtcNow; Console.WriteLine("Connecting to public server '{0}' using API {1}.", Domain, API_Version); - PublicServer s = new(Domain, API_Version, Secure); + PublicServer s = new(api, Domain, API_Version, Secure); ServerInfo? si = null; try { diff --git a/Luski.net/Server.cs b/Luski.net/Server.cs index 00ca01d..4cd4916 100644 --- a/Luski.net/Server.cs +++ b/Luski.net/Server.cs @@ -19,8 +19,10 @@ namespace Luski.net; public partial class Server { - internal Server(string Domain, string API_Version, bool Secure = true) + internal API API_Handler; + internal Server(API api_handle, string Domain, string API_Version, bool Secure = true) { + API_Handler = api_handle; this.Domain = Domain; this.ApiVersion = API_Version; this.Secure = Secure; From 69168acd2250899486653f75186065df3b9adc5d Mon Sep 17 00:00:00 2001 From: JacobTech Date: Sun, 1 Oct 2023 13:12:27 -0400 Subject: [PATCH 09/14] Fixed loop. Server class can't have an API class property, or a loop will prevent building. --- Luski.net/API.cs | 6 ++++-- Luski.net/MainServer.cs | 4 ++-- Luski.net/PublicServer.Account.cs | 8 ++++---- Luski.net/PublicServer.cs | 8 ++++---- Luski.net/Server.cs | 7 +++---- 5 files changed, 17 insertions(+), 16 deletions(-) diff --git a/Luski.net/API.cs b/Luski.net/API.cs index 4688b37..885b2cf 100644 --- a/Luski.net/API.cs +++ b/Luski.net/API.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text.Json.Serialization; using System.Threading.Tasks; using Luski.net.Enums; @@ -9,6 +10,7 @@ namespace Luski.net; public class API { + [JsonIgnore] public MainServer MainServer { get; internal set; } public bool IsAnyServerLoggedin { get; internal set; } @@ -40,7 +42,7 @@ public class API { IEnumerable isl = InternalServers.Where(a => (a.Domain == Domain && a.ApiVersion == Version)); if (isl.Any()) return isl.First(); - s = await PublicServer.GetServer(this, Domain, Version, Secure); + s = await PublicServer.GetServer(Domain, Version, Secure); } catch (Exception e) { @@ -84,7 +86,7 @@ public class API { DateTime dt = DateTime.UtcNow; Console.WriteLine("Conecting to main server '{0}' using API {1}.", Domain, Version); - MainServer = new(this, Domain, Version) + MainServer = new(Domain, Version) { ServerType = ServerType.Main }; diff --git a/Luski.net/MainServer.cs b/Luski.net/MainServer.cs index 876c1f5..26f2f76 100644 --- a/Luski.net/MainServer.cs +++ b/Luski.net/MainServer.cs @@ -20,8 +20,8 @@ namespace Luski.net; public partial class MainServer : Server { - internal MainServer(API api, string Domain, string API_Version): - base(api, Domain, API_Version) + internal MainServer(string Domain, string API_Version): + base(Domain, API_Version) { } diff --git a/Luski.net/PublicServer.Account.cs b/Luski.net/PublicServer.Account.cs index 435e5a0..900aa6a 100644 --- a/Luski.net/PublicServer.Account.cs +++ b/Luski.net/PublicServer.Account.cs @@ -171,7 +171,7 @@ public partial class PublicServer stor); EncryptionHandler.OfflinePublicKey = null!; EncryptionHandler.OfflinePrivateKey = null!; - API_Handler.IsAnyServerLoggedin = true; + //API_Handler.IsAnyServerLoggedin = true; return true; } else @@ -179,7 +179,7 @@ public partial class PublicServer throw new Exception(json?.ErrorMessage); } - API_Handler.IsAnyServerLoggedin = true; + //API_Handler.IsAnyServerLoggedin = true; return true; } @@ -346,12 +346,12 @@ public partial class PublicServer // _ = await setkey.PostAsync($"{(Secure ? "https" : "http" )}://{Domain}/{ApiVersion}/Keys/SetOfflineKey", new StringContent(EncryptionHandler.OfflinePublicKey)); EncryptionHandler.OfflinePublicKey = null!; EncryptionHandler.OfflinePrivateKey = null!; - API_Handler.IsAnyServerLoggedin = true; + //API_Handler.IsAnyServerLoggedin = true; return true; } else throw new Exception(json?.ErrorMessage); - API_Handler.IsAnyServerLoggedin = true; + //API_Handler.IsAnyServerLoggedin = true; return true; } } \ No newline at end of file diff --git a/Luski.net/PublicServer.cs b/Luski.net/PublicServer.cs index 8a7b9f3..1539d63 100644 --- a/Luski.net/PublicServer.cs +++ b/Luski.net/PublicServer.cs @@ -31,15 +31,15 @@ public partial class PublicServer : Server public SocketAppUser User { get; private set; } = null!; - private PublicServer(API api,string Domain, string API_Version, bool Secure = true) : - base(api, Domain, API_Version, Secure) + private PublicServer(string Domain, string API_Version, bool Secure = true) : + base(Domain, API_Version, Secure) { } - internal static async Task GetServer(API api, string Domain, string API_Version, bool Secure = true) + internal static async Task GetServer(string Domain, string API_Version, bool Secure = true) { DateTime dt = DateTime.UtcNow; Console.WriteLine("Connecting to public server '{0}' using API {1}.", Domain, API_Version); - PublicServer s = new(api, Domain, API_Version, Secure); + PublicServer s = new(Domain, API_Version, Secure); ServerInfo? si = null; try { diff --git a/Luski.net/Server.cs b/Luski.net/Server.cs index 4cd4916..d77dbf9 100644 --- a/Luski.net/Server.cs +++ b/Luski.net/Server.cs @@ -19,17 +19,16 @@ namespace Luski.net; public partial class Server { - internal API API_Handler; - internal Server(API api_handle, string Domain, string API_Version, bool Secure = true) + + internal Server(string Domain, string API_Version, bool Secure = true) { - API_Handler = api_handle; this.Domain = Domain; this.ApiVersion = API_Version; this.Secure = Secure; Storage = new(Domain); EncryptionHandler = new(Storage); } - + internal bool Secure = true; internal string wssurl; From 97ed89b6279df1e075d7b3ff8a6a323cca580369 Mon Sep 17 00:00:00 2001 From: JacobTech Date: Fri, 22 Dec 2023 11:13:31 -0500 Subject: [PATCH 10/14] Framework Update A few changes, mostly to update the .NET framework version. --- Luski.net/Luski.net.csproj | 4 ++-- Luski.net/Server.Storage.cs | 2 ++ Luski.net/Server.cs | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Luski.net/Luski.net.csproj b/Luski.net/Luski.net.csproj index 204f3c7..3501b6c 100755 --- a/Luski.net/Luski.net.csproj +++ b/Luski.net/Luski.net.csproj @@ -1,7 +1,7 @@  - net7.0 + net8.0 disable enable true @@ -13,7 +13,7 @@ https://github.com/JacobTech-com/Luski.net True 1.0.0 - 2.0.0-alpha24 + 2.0.0-alpha25 diff --git a/Luski.net/Server.Storage.cs b/Luski.net/Server.Storage.cs index af191e4..b550410 100644 --- a/Luski.net/Server.Storage.cs +++ b/Luski.net/Server.Storage.cs @@ -288,6 +288,7 @@ public class ServerStorage 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)) @@ -305,6 +306,7 @@ public class ServerStorage public Stream GetResourceStream(string dir) { + return File.OpenRead(dir); byte[] buffer = new byte[16 * 1024]; MemoryStream ms = new(); using (FileStream r = File.OpenRead(dir)) diff --git a/Luski.net/Server.cs b/Luski.net/Server.cs index d77dbf9..c1d62f4 100644 --- a/Luski.net/Server.cs +++ b/Luski.net/Server.cs @@ -30,7 +30,7 @@ public partial class Server } internal bool Secure = true; - internal string wssurl; + internal string wssurl { get; set; } = ""; public ServerEncryption EncryptionHandler { get; } public ServerStorage Storage { get; } From e94151e8149bdeb03e729252924f05640d7b653b Mon Sep 17 00:00:00 2001 From: JacobTech Date: Wed, 20 Mar 2024 23:18:34 -0400 Subject: [PATCH 11/14] Luski.Shared HTTP --- Luski.net/API.cs | 78 +++- Luski.net/Converters.cs | 3 - Luski.net/Enums/DataType.cs | 4 +- Luski.net/Enums/ErrorCode.cs | 14 - Luski.net/Enums/PictureType.cs | 14 - Luski.net/Enums/Public/ChannelType.cs | 6 - Luski.net/Enums/Public/DataType.cs | 7 - Luski.net/Enums/Public/ServerPermission.cs | 38 -- Luski.net/Enums/StorageDirectory.cs | 3 +- Luski.net/Enums/UserStatus.cs | 10 - Luski.net/Interfaces/IAppUser.cs | 3 +- Luski.net/Interfaces/IServer.cs | 7 +- Luski.net/Interfaces/IUser.cs | 9 +- Luski.net/JsonTypes/BaseTypes/IncomingHTTP.cs | 32 -- Luski.net/JsonTypes/FriendRequestResult.cs | 3 +- Luski.net/JsonTypes/HTTP/Channel.cs | 21 - Luski.net/JsonTypes/HTTP/FriendRequest.cs | 3 +- .../JsonTypes/HTTP/FriendRequestResultOut.cs | 3 +- Luski.net/JsonTypes/HTTP/Message.cs | 27 -- Luski.net/JsonTypes/HTTP/Status.cs | 22 - Luski.net/JsonTypes/KeyExchange.cs | 1 + Luski.net/JsonTypes/LocalServerInfo.cs | 1 + Luski.net/JsonTypes/Login.cs | 21 - Luski.net/JsonTypes/OfflineData.cs | 21 - Luski.net/JsonTypes/OfflineKeyData.cs | 3 +- Luski.net/JsonTypes/Public/Category.cs | 63 --- Luski.net/JsonTypes/Public/Channel.cs | 71 --- Luski.net/JsonTypes/Public/PublicMessage.cs | 32 -- .../Public/PublicSocketBulkMessage.cs | 22 - Luski.net/JsonTypes/Public/Role.cs | 28 -- Luski.net/JsonTypes/Public/SocketUser.cs | 41 -- Luski.net/JsonTypes/Public/pFile.cs | 32 -- Luski.net/JsonTypes/ServerData.cs | 23 - Luski.net/JsonTypes/ServerInfo.cs | 26 -- Luski.net/JsonTypes/StatusUpdate.cs | 4 +- Luski.net/JsonTypes/StorageInfoJSON.cs | 22 - Luski.net/JsonTypes/UserKeyGetRequest.cs | 23 - Luski.net/JsonTypes/UserKeysGetRequest.cs | 19 - Luski.net/JsonTypes/WSS/IncomingWSS.cs | 7 +- Luski.net/JsonTypes/WSS/ServerEvent.cs | 3 +- Luski.net/Luski.net.csproj | 4 +- Luski.net/MainServer.Account.cs | 11 +- Luski.net/MainServer.cs | 9 +- Luski.net/PublicServer.Account.cs | 422 +++++++----------- Luski.net/PublicServer.Incoming.cs | 60 ++- Luski.net/PublicServer.cs | 266 +++++++++-- Luski.net/Server.Encryption.cs | 2 +- Luski.net/Server.Globals.cs | 3 + Luski.net/Server.Storage.cs | 21 +- Luski.net/Server.cs | 14 +- Luski.net/Structures/File.cs | 3 +- .../Structures/Main/MainSocketChannel.cs | 3 +- .../Structures/Main/MainSocketMessage.cs | 3 +- .../Structures/Main/MainSocketTextChannel.cs | 16 +- .../Structures/Main/MainSocketUserBase.cs | 12 +- .../Structures/Main/SocketBulkMessage.cs | 7 +- Luski.net/Structures/Public/Color.cs | 20 +- Luski.net/Structures/Public/Role.cs | 4 +- Luski.net/Structures/Public/RoleOveride.cs | 7 +- Luski.net/Structures/Public/SocketAppUser.cs | 43 +- Luski.net/Structures/Public/SocketCategory.cs | 8 +- Luski.net/Structures/Public/SocketChannel.cs | 180 +++++++- .../Structures/Public/SocketChannelProfile.cs | 38 ++ Luski.net/Structures/Public/SocketFile.cs | 7 +- Luski.net/Structures/Public/SocketMessage.cs | 24 +- Luski.net/Structures/Public/SocketUser.cs | 21 +- Luski.net/Structures/Public/UserOveride.cs | 6 - Luski.net/Structures/Public/UserOverride.cs | 14 + 68 files changed, 947 insertions(+), 1051 deletions(-) mode change 100755 => 100644 Luski.net/Enums/DataType.cs delete mode 100755 Luski.net/Enums/ErrorCode.cs delete mode 100755 Luski.net/Enums/PictureType.cs delete mode 100755 Luski.net/Enums/Public/ChannelType.cs delete mode 100644 Luski.net/Enums/Public/DataType.cs delete mode 100644 Luski.net/Enums/Public/ServerPermission.cs delete mode 100755 Luski.net/Enums/UserStatus.cs delete mode 100755 Luski.net/JsonTypes/BaseTypes/IncomingHTTP.cs delete mode 100755 Luski.net/JsonTypes/HTTP/Channel.cs delete mode 100755 Luski.net/JsonTypes/HTTP/Message.cs delete mode 100755 Luski.net/JsonTypes/HTTP/Status.cs delete mode 100755 Luski.net/JsonTypes/Login.cs delete mode 100644 Luski.net/JsonTypes/OfflineData.cs delete mode 100644 Luski.net/JsonTypes/Public/Category.cs delete mode 100644 Luski.net/JsonTypes/Public/Channel.cs delete mode 100644 Luski.net/JsonTypes/Public/PublicMessage.cs delete mode 100644 Luski.net/JsonTypes/Public/PublicSocketBulkMessage.cs delete mode 100644 Luski.net/JsonTypes/Public/Role.cs delete mode 100644 Luski.net/JsonTypes/Public/SocketUser.cs delete mode 100644 Luski.net/JsonTypes/Public/pFile.cs delete mode 100644 Luski.net/JsonTypes/ServerData.cs delete mode 100644 Luski.net/JsonTypes/ServerInfo.cs delete mode 100644 Luski.net/JsonTypes/StorageInfoJSON.cs delete mode 100644 Luski.net/JsonTypes/UserKeyGetRequest.cs delete mode 100644 Luski.net/JsonTypes/UserKeysGetRequest.cs create mode 100644 Luski.net/Structures/Public/SocketChannelProfile.cs delete mode 100644 Luski.net/Structures/Public/UserOveride.cs create mode 100644 Luski.net/Structures/Public/UserOverride.cs diff --git a/Luski.net/API.cs b/Luski.net/API.cs index 885b2cf..306244d 100644 --- a/Luski.net/API.cs +++ b/Luski.net/API.cs @@ -1,10 +1,17 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; +using System.Net.Http; +using System.Text.Json; using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Metadata; +using System.Threading; using System.Threading.Tasks; using Luski.net.Enums; +using Luski.Shared.PublicServers.V1.Enums; +using Luski.Shared.PublicServers.V1.ServerToClient.HTTP; namespace Luski.net; @@ -14,28 +21,86 @@ public class API public MainServer MainServer { get; internal set; } public bool IsAnyServerLoggedin { get; internal set; } + + public const string DefaultVersion = "v1"; + + internal List Versions = new() + { + DefaultVersion + }; + + public IReadOnlyList SupportedVersions => Versions.AsReadOnly(); internal List InternalServers { get; } = new(); public IReadOnlyList LoadedServers => InternalServers.AsReadOnly(); - - public Task TryGetPublicServer(out PublicServer Server, string Domain, string Version = "v1", + + private static HttpResponseMessage GetFromServer(string Domain, string ApiVersion, bool Secure, string Path, CancellationToken CancellationToken, params KeyValuePair[] Headers) + { + using HttpClient web = new(); + web.Timeout = TimeSpan.FromSeconds(10); + if (Headers is not null && Headers.Length > 0) foreach (KeyValuePair header in Headers) web.DefaultRequestHeaders.Add(header.Key, header.Value); + return web.GetAsync($"{(Secure ? "https" : "http" )}://{Domain}/{ApiVersion}/{Path}", cancellationToken: CancellationToken).Result; + } + private static Task GetFromServer(string Domain, string ApiVersion, bool Secure, string Path, JsonTypeInfo Type, CancellationToken CancellationToken, params KeyValuePair[] Headers) where Tresult : STC, new() + { + HttpResponseMessage ServerResponce = GetFromServer(Domain, ApiVersion, Secure, Path, CancellationToken, Headers); + Tresult temp = new(); + string raw = ServerResponce.Content.ReadAsStringAsync(CancellationToken).Result; + try + { + temp = JsonSerializer.Deserialize(raw, Type)!; + } + catch (Exception e) + { + Console.WriteLine("JSON parse failed for the following data as type {0}\n{1}", temp.GetType(), raw); + } + if (temp is null) return Task.FromResult(new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with empty data" }); + return Task.FromResult(temp); + } + + public Task GetServerInfo(string Domain, string Version = DefaultVersion, bool Secure = true) + { + ServerInfoSTC? si = GetFromServer(Domain, Version, Secure, "socketserver", ServerInfoSTCContext.Default.ServerInfoSTC, CancellationToken.None).Result; + if (si is null) throw new Exception("Bad Response"); + return Task.FromResult(si); + } + + public Task TryGetServerInfo([NotNullWhen(true)]out ServerInfoSTC? si, string Domain, string Version = DefaultVersion, + bool Secure = true) + { + + try + { + si = GetServerInfo(Domain, Version, Secure).Result; + return Task.FromResult(true); + } + catch (Exception e) + { + si = null; + return Task.FromResult(false); + } + } + + public Task TryGetPublicServer(out PublicServer Server, string Domain, string Version = DefaultVersion, + bool Secure = true, bool GenerateEncryption = true, bool LogConsole = false) { try { - Task result = GetPublicServer(Domain, Version, Secure); + Task result = GetPublicServer(Domain, Version, Secure, GenerateEncryption, LogConsole); Task.WaitAll(result); Server = result.Result; return Task.FromResult(true); } catch (Exception e) { - Console.WriteLine(e); + if (!e.Message.Contains("Connection refused")) Console.WriteLine(e); Server = null!; return Task.FromResult(false); } } - public async Task GetPublicServer(string Domain, string Version = "v1", bool Secure = true) + + public async Task GetPublicServer(string Domain, string Version = DefaultVersion, bool Secure = true, bool GenerateEncryption = true, bool LogConsole = false) { PublicServer s; try @@ -64,7 +129,6 @@ public class API bool b = await s.LoginViaToken(f); if (b) { - Console.WriteLine("Auto Login Successful"); IsAnyServerLoggedin = true; } else @@ -82,7 +146,7 @@ public class API return s; } - public MainServer GetMainServer(string Domain, string Version = "v1") + public MainServer GetMainServer(string Domain, string Version = DefaultVersion) { DateTime dt = DateTime.UtcNow; Console.WriteLine("Conecting to main server '{0}' using API {1}.", Domain, Version); diff --git a/Luski.net/Converters.cs b/Luski.net/Converters.cs index edd770e..102371b 100644 --- a/Luski.net/Converters.cs +++ b/Luski.net/Converters.cs @@ -1,6 +1,3 @@ -using Luski.net.JsonTypes.Public; -using Luski.net.Structures.Public; - namespace Luski.net; public static class Converters diff --git a/Luski.net/Enums/DataType.cs b/Luski.net/Enums/DataType.cs old mode 100755 new mode 100644 index 37dea2a..6a5189a --- a/Luski.net/Enums/DataType.cs +++ b/Luski.net/Enums/DataType.cs @@ -1,4 +1,4 @@ -namespace Luski.net.Enums; +namespace Luski.net.Enums; public enum DataType { @@ -15,4 +15,4 @@ public enum DataType Error, Key_Exchange, MAX -} +} \ No newline at end of file diff --git a/Luski.net/Enums/ErrorCode.cs b/Luski.net/Enums/ErrorCode.cs deleted file mode 100755 index 70139d1..0000000 --- a/Luski.net/Enums/ErrorCode.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Luski.net.Enums; - -public enum ErrorCode -{ - MissingToken, - InvalidToken, - MissingPostData, - InvalidPostData, - Forbidden, - ServerError, - MissingHeader, - InvalidHeader, - InvalidURL -} diff --git a/Luski.net/Enums/PictureType.cs b/Luski.net/Enums/PictureType.cs deleted file mode 100755 index 721bc28..0000000 --- a/Luski.net/Enums/PictureType.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Luski.net.Enums; - -public enum PictureType : short -{ - none, - png, - jpeg, - bmp, - gif, - ico, - svg, - tif, - webp -} diff --git a/Luski.net/Enums/Public/ChannelType.cs b/Luski.net/Enums/Public/ChannelType.cs deleted file mode 100755 index e52037b..0000000 --- a/Luski.net/Enums/Public/ChannelType.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Luski.net.Enums.Public; - -public enum ChannelType : short -{ - TextAndVoice = 0 -} diff --git a/Luski.net/Enums/Public/DataType.cs b/Luski.net/Enums/Public/DataType.cs deleted file mode 100644 index 723e4bf..0000000 --- a/Luski.net/Enums/Public/DataType.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Luski.net.Enums.Public; - -public enum DataType -{ - Token, - MessageCreate -} \ No newline at end of file diff --git a/Luski.net/Enums/Public/ServerPermission.cs b/Luski.net/Enums/Public/ServerPermission.cs deleted file mode 100644 index 89c9ac4..0000000 --- a/Luski.net/Enums/Public/ServerPermission.cs +++ /dev/null @@ -1,38 +0,0 @@ -namespace Luski.net.Enums.Public; - -public enum ServerPermission : long -{ - ViewChannels, - MoveChannels, - EditChannels, - EditChannelPermissions, - CreateChannels, - DeleteChannels, - ViewCategories, - MoveCategories, - EditCategories, - EditCategoryPermissions, - CreateCategories, - DeleteCategories, - DeleteKeys, - ManageRoles, - ViewLogs, - ManageServer, - Invite, - Nickname, - ManageNacknames, - Kick, - Ban, - SendMessages, - SendFiles, - ChannelAndServerPings, - PingSomeone, - ManageMessages, - ReadMessageHistory, - UseServerCommands, - JoinVoice, - SpeakInVoice, - MuteMembers, - DeafenMembers, - MoveMembers -} \ No newline at end of file diff --git a/Luski.net/Enums/StorageDirectory.cs b/Luski.net/Enums/StorageDirectory.cs index 2f32ac4..f524ff1 100644 --- a/Luski.net/Enums/StorageDirectory.cs +++ b/Luski.net/Enums/StorageDirectory.cs @@ -10,5 +10,6 @@ public enum StorageDirectory : byte ChannelIcons, Messages, StorageInfo, - Files + Files, + ProfileAvatars } \ No newline at end of file diff --git a/Luski.net/Enums/UserStatus.cs b/Luski.net/Enums/UserStatus.cs deleted file mode 100755 index 439d9fb..0000000 --- a/Luski.net/Enums/UserStatus.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Luski.net.Enums; - -public enum UserStatus : short -{ - Offline, - Online, - Idle, - DoNotDisturb, - Invisible -} diff --git a/Luski.net/Interfaces/IAppUser.cs b/Luski.net/Interfaces/IAppUser.cs index d29c9b3..f49a1e6 100644 --- a/Luski.net/Interfaces/IAppUser.cs +++ b/Luski.net/Interfaces/IAppUser.cs @@ -1,5 +1,4 @@ -using Luski.net.Enums; -using Luski.net.Structures.Public; +using Luski.Shared.PublicServers.V1.Enums; namespace Luski.net.Interfaces; diff --git a/Luski.net/Interfaces/IServer.cs b/Luski.net/Interfaces/IServer.cs index e20115c..908bb89 100644 --- a/Luski.net/Interfaces/IServer.cs +++ b/Luski.net/Interfaces/IServer.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Luski.net.JsonTypes.BaseTypes; using Luski.net.Structures; using Luski.net.Structures.Public; +using Luski.Shared.PublicServers.V1.ServerToClient.HTTP; namespace Luski.net.Interfaces; @@ -23,15 +24,15 @@ public interface IServer public Task GetFromServer(string Path, JsonTypeInfo Type, CancellationToken CancellationToken, params KeyValuePair[] Headers) - where Tresult : IncomingHTTP, new(); + where Tresult : STC, new(); public Task SendServer(string Path, Tvalue Payload, JsonTypeInfo jsonTypeInfo, JsonTypeInfo ReturnjsonTypeInfo, CancellationToken CancellationToken, params KeyValuePair[] Headers) - where Tvalue : IWebRequest where Tresult : IncomingHTTP, new(); + where Tvalue : IWebRequest where Tresult : STC, new(); public Task SendServer(string Path, string File, JsonTypeInfo ReturnjsonTypeInfo, CancellationToken CancellationToken, params KeyValuePair[] Headers) - where Tresult : IncomingHTTP, new(); + where Tresult : STC, new(); } \ No newline at end of file diff --git a/Luski.net/Interfaces/IUser.cs b/Luski.net/Interfaces/IUser.cs index 609b996..68d4de9 100755 --- a/Luski.net/Interfaces/IUser.cs +++ b/Luski.net/Interfaces/IUser.cs @@ -1,9 +1,9 @@ using System.IO; using System.Threading; -using Luski.net.Enums; using System.Threading.Tasks; -using Luski.net.JsonTypes; using Luski.net.Structures; +using Luski.net.Structures.Public; +using Luski.Shared.PublicServers.V1.Enums; namespace Luski.net.Interfaces; @@ -24,6 +24,11 @@ public interface IUser /// The current status of the user /// UserStatus Status { get; } + + /// + /// The color of the display name + /// + Task GetColor(); /// /// will returen the picture type of the user /// diff --git a/Luski.net/JsonTypes/BaseTypes/IncomingHTTP.cs b/Luski.net/JsonTypes/BaseTypes/IncomingHTTP.cs deleted file mode 100755 index ff6c3f8..0000000 --- a/Luski.net/JsonTypes/BaseTypes/IncomingHTTP.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Luski.net.Enums; -using System.ComponentModel; -using System.Net; -using System.Text.Json.Serialization; - -namespace Luski.net.JsonTypes.BaseTypes; - -[Browsable(false)] -[EditorBrowsable(EditorBrowsableState.Never)] -public class IncomingHTTP -{ - [JsonPropertyName("error")] - [JsonInclude] - public ErrorCode? Error { get; internal set; } = default!; -#pragma warning disable SYSLIB1037 // Deserialization of init-only properties is currently not supported in source generation mode. - [JsonIgnore] - public HttpStatusCode StatusCode { get; init; } -#pragma warning restore SYSLIB1037 // Deserialization of init-only properties is currently not supported in source generation mode. - [JsonPropertyName("error_message")] - [JsonInclude] - public string? ErrorMessage { get; internal set; } = default!; -} - -[JsonSerializable(typeof(IncomingHTTP))] -[JsonSourceGenerationOptions( - GenerationMode = JsonSourceGenerationMode.Default, - PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, - WriteIndented = false)] -internal partial class IncomingHTTPContext : JsonSerializerContext -{ - -} diff --git a/Luski.net/JsonTypes/FriendRequestResult.cs b/Luski.net/JsonTypes/FriendRequestResult.cs index 2d78f98..5d39096 100755 --- a/Luski.net/JsonTypes/FriendRequestResult.cs +++ b/Luski.net/JsonTypes/FriendRequestResult.cs @@ -1,9 +1,10 @@ using Luski.net.JsonTypes.BaseTypes; using System.Text.Json.Serialization; +using Luski.Shared.PublicServers.V1.ServerToClient.HTTP; namespace Luski.net.JsonTypes; -internal class FriendRequestResult : IncomingHTTP +internal class FriendRequestResult : STC { [JsonPropertyName("channel")] [JsonInclude] diff --git a/Luski.net/JsonTypes/HTTP/Channel.cs b/Luski.net/JsonTypes/HTTP/Channel.cs deleted file mode 100755 index 1150e5f..0000000 --- a/Luski.net/JsonTypes/HTTP/Channel.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Luski.net.JsonTypes.BaseTypes; -using System.Text.Json.Serialization; - -namespace Luski.net.JsonTypes.HTTP; - -internal class Channel : HTTPRequest -{ - [JsonPropertyName("id")] - [JsonInclude] - public long Id { get; set; } = default!; -} - -[JsonSerializable(typeof(Channel))] -[JsonSourceGenerationOptions( - GenerationMode = JsonSourceGenerationMode.Default, - PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, - WriteIndented = false)] -internal partial class ChannelContext : JsonSerializerContext -{ - -} diff --git a/Luski.net/JsonTypes/HTTP/FriendRequest.cs b/Luski.net/JsonTypes/HTTP/FriendRequest.cs index 707818b..ce6fc6e 100755 --- a/Luski.net/JsonTypes/HTTP/FriendRequest.cs +++ b/Luski.net/JsonTypes/HTTP/FriendRequest.cs @@ -1,9 +1,10 @@ using Luski.net.JsonTypes.BaseTypes; using System.Text.Json.Serialization; +using Luski.Shared.PublicServers.V1.ClientToServer.HTTP; namespace Luski.net.JsonTypes.HTTP; -internal class FriendRequest : HTTPRequest +internal class FriendRequest : CTS { [JsonPropertyName("code")] [JsonInclude] diff --git a/Luski.net/JsonTypes/HTTP/FriendRequestResultOut.cs b/Luski.net/JsonTypes/HTTP/FriendRequestResultOut.cs index 7517c7a..d5cd88a 100755 --- a/Luski.net/JsonTypes/HTTP/FriendRequestResultOut.cs +++ b/Luski.net/JsonTypes/HTTP/FriendRequestResultOut.cs @@ -1,9 +1,10 @@ using Luski.net.JsonTypes.BaseTypes; using System.Text.Json.Serialization; +using Luski.Shared.PublicServers.V1.ClientToServer.HTTP; namespace Luski.net.JsonTypes.HTTP; -internal class FriendRequestResultOut : HTTPRequest +internal class FriendRequestResultOut : CTS { [JsonPropertyName("id")] [JsonInclude] diff --git a/Luski.net/JsonTypes/HTTP/Message.cs b/Luski.net/JsonTypes/HTTP/Message.cs deleted file mode 100755 index ee6f9a5..0000000 --- a/Luski.net/JsonTypes/HTTP/Message.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Luski.net.JsonTypes.BaseTypes; -using System.Text.Json.Serialization; - -namespace Luski.net.JsonTypes.HTTP; - -internal class Message : HTTPRequest -{ - [JsonPropertyName("channel_id")] - [JsonInclude] - public long Channel { get; set; } = default!; - [JsonPropertyName("content")] - [JsonInclude] - public string Context { get; set; } = default!; - [JsonPropertyName("files")] - [JsonInclude] - public long[] Files { get; set; } = default!; -} - -[JsonSerializable(typeof(Message))] -[JsonSourceGenerationOptions( - GenerationMode = JsonSourceGenerationMode.Default, - PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, - WriteIndented = false)] -internal partial class MessageContext : JsonSerializerContext -{ - -} diff --git a/Luski.net/JsonTypes/HTTP/Status.cs b/Luski.net/JsonTypes/HTTP/Status.cs deleted file mode 100755 index f447753..0000000 --- a/Luski.net/JsonTypes/HTTP/Status.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Text.Json.Serialization; -using Luski.net.Enums; -using Luski.net.JsonTypes.BaseTypes; - -namespace Luski.net.JsonTypes.HTTP; - -internal class Status : HTTPRequest -{ - [JsonPropertyName("status")] - [JsonInclude] - public UserStatus UserStatus { get; set; } = default!; -} - -[JsonSerializable(typeof(Status))] -[JsonSourceGenerationOptions( - GenerationMode = JsonSourceGenerationMode.Default, - PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, - WriteIndented = false)] -internal partial class StatusContext : JsonSerializerContext -{ - -} diff --git a/Luski.net/JsonTypes/KeyExchange.cs b/Luski.net/JsonTypes/KeyExchange.cs index 310f846..ff25400 100755 --- a/Luski.net/JsonTypes/KeyExchange.cs +++ b/Luski.net/JsonTypes/KeyExchange.cs @@ -3,6 +3,7 @@ internal class KeyExchange { public long channel { get; set; } = default!; + public long id { get; set; } = default!; public string key { get; set; } = default!; public long? to { get; set; } = default!; diff --git a/Luski.net/JsonTypes/LocalServerInfo.cs b/Luski.net/JsonTypes/LocalServerInfo.cs index 8489c80..4d65bb0 100644 --- a/Luski.net/JsonTypes/LocalServerInfo.cs +++ b/Luski.net/JsonTypes/LocalServerInfo.cs @@ -1,5 +1,6 @@ using System; using System.Text.Json.Serialization; +using Luski.Shared.PublicServers.V1.Shared; namespace Luski.net.JsonTypes; diff --git a/Luski.net/JsonTypes/Login.cs b/Luski.net/JsonTypes/Login.cs deleted file mode 100755 index be52591..0000000 --- a/Luski.net/JsonTypes/Login.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Luski.net.JsonTypes.BaseTypes; -using System.Text.Json.Serialization; - -namespace Luski.net.JsonTypes; - -internal class Login : IncomingHTTP -{ - [JsonPropertyName("login_token")] - public string? Token { get; set; } = default!; - -} - -[JsonSerializable(typeof(Login))] -[JsonSourceGenerationOptions( - GenerationMode = JsonSourceGenerationMode.Default, - PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, - WriteIndented = false)] -internal partial class LoginContext : JsonSerializerContext -{ - -} diff --git a/Luski.net/JsonTypes/OfflineData.cs b/Luski.net/JsonTypes/OfflineData.cs deleted file mode 100644 index 2d88763..0000000 --- a/Luski.net/JsonTypes/OfflineData.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Text.Json.Serialization; -using Luski.net.JsonTypes.BaseTypes; - -namespace Luski.net.JsonTypes; - -public class OfflineData : IncomingHTTP -{ - [JsonPropertyName("data")] - [JsonInclude] - public string[]? Data { get; internal set; } = default!; -} - -[JsonSerializable(typeof(OfflineData))] -[JsonSourceGenerationOptions( - GenerationMode = JsonSourceGenerationMode.Default, - PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, - WriteIndented = false)] -internal partial class OfflineDataContext : JsonSerializerContext -{ - -} \ No newline at end of file diff --git a/Luski.net/JsonTypes/OfflineKeyData.cs b/Luski.net/JsonTypes/OfflineKeyData.cs index 1f4b978..3aaf56b 100755 --- a/Luski.net/JsonTypes/OfflineKeyData.cs +++ b/Luski.net/JsonTypes/OfflineKeyData.cs @@ -1,9 +1,10 @@ using Luski.net.JsonTypes.BaseTypes; using System.Text.Json.Serialization; +using Luski.Shared.PublicServers.V1.ServerToClient.HTTP; namespace Luski.net.JsonTypes; -internal class OfflineKeyData : IncomingHTTP +internal class OfflineKeyData : STC { public KeyExchange[]? keys { get; internal set; } = default!; } diff --git a/Luski.net/JsonTypes/Public/Category.cs b/Luski.net/JsonTypes/Public/Category.cs deleted file mode 100644 index ba19fd2..0000000 --- a/Luski.net/JsonTypes/Public/Category.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System.Text.Json.Serialization; -using JacobTechEncryption.Enums; -using Luski.net.Enums; -using Luski.net.JsonTypes.BaseTypes; - -namespace Luski.net.JsonTypes.Public; - -public class Category : IncomingHTTP -{ - [JsonInclude] - [JsonPropertyName("id")] - public long ID { get; set; } - [JsonInclude] - [JsonPropertyName("picture_type")] - public PictureType PictureType { get; set; } - [JsonInclude] - [JsonPropertyName("color")] - public string Color { get; set; } - [JsonInclude] - [JsonPropertyName("name")] - public string Name { get; set; } - [JsonInclude] - [JsonPropertyName("description")] - public string Description { get; set; } - [JsonInclude] - [JsonPropertyName("parent")] - public long Parent { get; set; } - [JsonInclude] - [JsonPropertyName("inner_categories")] - public long[] InnerCategories { get; set; } - [JsonInclude] - [JsonPropertyName("channels")] - public long[] Channels { get; set; } - [JsonInclude] - [JsonPropertyName("role_overides")] - public long[] RoleOverides { get; set; } - [JsonInclude] - [JsonPropertyName("member_overides")] - public long[] UserOverides { get; set; } - [JsonInclude] - [JsonPropertyName("title_encryption_key")] - public long TitleEncryptionKey { get; set; } - [JsonInclude] - [JsonPropertyName("description_encryption_key")] - public long DescriptionEncryptionKey { get; set; } - [JsonInclude] - [JsonPropertyName("title_encoder_type")] - public EncoderType TitleEncoderType { get; set; } - [JsonInclude] - [JsonPropertyName("description_encoder_type")] - public EncoderType DescriptionEncoderType { get; set; } -} - -[JsonSerializable(typeof(Category))] -[JsonSourceGenerationOptions( - GenerationMode = JsonSourceGenerationMode.Default, - PropertyNamingPolicy = JsonKnownNamingPolicy.Unspecified, - WriteIndented = false, - DefaultIgnoreCondition = JsonIgnoreCondition.Never)] -internal partial class PublicCategoryContext : JsonSerializerContext -{ - -} \ No newline at end of file diff --git a/Luski.net/JsonTypes/Public/Channel.cs b/Luski.net/JsonTypes/Public/Channel.cs deleted file mode 100644 index 938cb4b..0000000 --- a/Luski.net/JsonTypes/Public/Channel.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System; -using System.Text.Json.Serialization; -using JacobTechEncryption.Enums; -using Luski.net.Enums; -using Luski.net.Enums.Public; -using Luski.net.JsonTypes.BaseTypes; - -namespace Luski.net.JsonTypes.Public; - -public class Channel : IncomingHTTP -{ - [JsonInclude] - [JsonPropertyName("id")] - public long ID { get; set; } - [JsonInclude] - [JsonPropertyName("parent")] - public long Parent { get; set; } - [JsonInclude] - [JsonPropertyName("color")] - public string Color { get; set; } - [JsonInclude] - [JsonPropertyName("type")] - public ChannelType Type { get; set; } - [JsonInclude] - [JsonPropertyName("epoch")] - public DateTime Epoch { get; set; } - [JsonInclude] - [JsonPropertyName("name")] - public string Name { get; set; } - [JsonInclude] - [JsonPropertyName("description")] - public string Description { get; set; } - [JsonInclude] - [JsonPropertyName("role_overides")] - public long[] RoleOverides { get; set; } - [JsonInclude] - [JsonPropertyName("member_overides")] - public long[] UserOverides { get; set; } - [JsonInclude] - [JsonPropertyName("title_encryption_key")] - public long TitleEncryptionKey { get; set; } - [JsonInclude] - [JsonPropertyName("description_encryption_key")] - public long DescriptionEncryptionKey { get; set; } - [JsonInclude] - [JsonPropertyName("encryption_keys")] - public long[] EncryptionKeys { get; set; } - [JsonInclude] - [JsonPropertyName("title_encoder_type")] - public EncoderType TitleEncoderType { get; set; } - [JsonInclude] - [JsonPropertyName("description_encoder_type")] - public EncoderType DescriptionEncoderType { get; set; } - [JsonInclude] - [JsonPropertyName("encoder_types")] - public EncoderType[] EncoderTypes { get; set; } - [JsonInclude] - [JsonPropertyName("picture_type")] - public PictureType PictureType { get; set; } -} - -[JsonSerializable(typeof(Channel))] -[JsonSourceGenerationOptions( - GenerationMode = JsonSourceGenerationMode.Default, - PropertyNamingPolicy = JsonKnownNamingPolicy.Unspecified, - WriteIndented = false, - DefaultIgnoreCondition = JsonIgnoreCondition.Never)] -internal partial class PublicChannelContext : JsonSerializerContext -{ - -} \ No newline at end of file diff --git a/Luski.net/JsonTypes/Public/PublicMessage.cs b/Luski.net/JsonTypes/Public/PublicMessage.cs deleted file mode 100644 index d06f627..0000000 --- a/Luski.net/JsonTypes/Public/PublicMessage.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Text.Json.Serialization; -using JacobTechEncryption.Enums; - -namespace Luski.net.JsonTypes.Public; - -public class PublicMessage -{ - [JsonInclude] - [JsonPropertyName("id")] - public long ID { get; internal set; } - [JsonInclude] - [JsonPropertyName("channel_id")] - public long ChannelID { get; internal set; } - [JsonInclude] - [JsonPropertyName("author_id")] - public long AuthorID { get; internal set; } - [JsonInclude] - [JsonPropertyName("ts")] - public long TimeStamp { get; internal set; } - [JsonInclude] - [JsonPropertyName("context")] - public string Context { get; internal set; } - [JsonInclude] - [JsonPropertyName("encryption_key")] - public long EncryptionKey { get; internal set; } - [JsonInclude] - [JsonPropertyName("files")] - public pFile[] Files { get; internal set; } - [JsonInclude] - [JsonPropertyName("encoder_type")] - public EncoderType EncoderType { get; internal set; } -} \ No newline at end of file diff --git a/Luski.net/JsonTypes/Public/PublicSocketBulkMessage.cs b/Luski.net/JsonTypes/Public/PublicSocketBulkMessage.cs deleted file mode 100644 index 50ebef3..0000000 --- a/Luski.net/JsonTypes/Public/PublicSocketBulkMessage.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Text.Json.Serialization; -using Luski.net.JsonTypes.BaseTypes; - -namespace Luski.net.JsonTypes.Public; - -public class PublicSocketBulkMessage : IncomingHTTP -{ - [JsonInclude] - [JsonPropertyName("messages")] - public PublicMessage[]? Messages { get; set; } = default!; -} - -[JsonSerializable(typeof(PublicSocketBulkMessage))] -[JsonSourceGenerationOptions( - GenerationMode = JsonSourceGenerationMode.Default, - PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, - WriteIndented = false, - DefaultIgnoreCondition = JsonIgnoreCondition.Never)] -internal partial class PublicSocketBulkMessageContext : JsonSerializerContext -{ - -} \ No newline at end of file diff --git a/Luski.net/JsonTypes/Public/Role.cs b/Luski.net/JsonTypes/Public/Role.cs deleted file mode 100644 index 7b32ed0..0000000 --- a/Luski.net/JsonTypes/Public/Role.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Text.Json.Serialization; -using Luski.net.Enums.Public; -using Luski.net.JsonTypes.BaseTypes; - -namespace Luski.net.JsonTypes.Public; - -public class Role : IncomingHTTP -{ - public long id { get; set; } - public string name { get; set; } - public string display_name { get; set; } - public int index { get; set; } - public string color { get; set; } - public string description { get; set; } - public ServerPermission[] server_permissions { get; set; } - public long[] members_list { get; set; } -} - -[JsonSerializable(typeof(Role))] -[JsonSourceGenerationOptions( - GenerationMode = JsonSourceGenerationMode.Default, - PropertyNamingPolicy = JsonKnownNamingPolicy.Unspecified, - WriteIndented = false, - DefaultIgnoreCondition = JsonIgnoreCondition.Never)] -internal partial class RoleContext : JsonSerializerContext -{ - -} \ No newline at end of file diff --git a/Luski.net/JsonTypes/Public/SocketUser.cs b/Luski.net/JsonTypes/Public/SocketUser.cs deleted file mode 100644 index bd8ba68..0000000 --- a/Luski.net/JsonTypes/Public/SocketUser.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System.Text.Json.Serialization; -using Luski.net.Enums; -using Luski.net.JsonTypes.BaseTypes; - -namespace Luski.net.JsonTypes.Public; - -public class SocketUser : IncomingHTTP -{ - [JsonInclude] - [JsonPropertyName("id")] - public long ID { get; internal set; } = default!; - [JsonInclude] - [JsonPropertyName("displayname")] - public string DisplayName { get; internal set; } = default!; - [JsonInclude] - [JsonPropertyName("selected_channel")] - public long SelectedChannel { get; internal set; } = default!; - [JsonInclude] - [JsonPropertyName("status")] - public UserStatus Status { get; internal set; } = default!; - [JsonInclude] - [JsonPropertyName("picture_type")] - public PictureType PictureType { get; internal set; } = default!; - [JsonInclude] - [JsonPropertyName("roles")] - public long[] RoleIds { get; internal set; } = default!; - [JsonInclude] - [JsonPropertyName("username")] - public string Username { get; internal set; } = default!; -} - -[JsonSerializable(typeof(SocketUser))] -[JsonSourceGenerationOptions( - GenerationMode = JsonSourceGenerationMode.Default, - PropertyNamingPolicy = JsonKnownNamingPolicy.Unspecified, - WriteIndented = false, - DefaultIgnoreCondition = JsonIgnoreCondition.Never)] -internal partial class PublicSocketUserContext : JsonSerializerContext -{ - -} \ No newline at end of file diff --git a/Luski.net/JsonTypes/Public/pFile.cs b/Luski.net/JsonTypes/Public/pFile.cs deleted file mode 100644 index 0a749ba..0000000 --- a/Luski.net/JsonTypes/Public/pFile.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Text.Json.Serialization; -using JacobTechEncryption.Enums; - -namespace Luski.net.JsonTypes.Public; - -public class pFile -{ - [JsonInclude] - [JsonPropertyName("id")] - public long ID { get; set; } - [JsonInclude] - [JsonPropertyName("name")] - public string Name { get; set; } - [JsonInclude] - [JsonPropertyName("channel")] - public long Channel { get; set; } - [JsonInclude] - [JsonPropertyName("encoder_type")] - public EncoderType Encoder { get; set; } - [JsonInclude] - [JsonPropertyName("name_encoder_type")] - public EncoderType NameEncoder { get; set; } - [JsonInclude] - [JsonPropertyName("encryption_key")] - public long Key { get; set; } - [JsonInclude] - [JsonPropertyName("name_encryption_key")] - public long NameKey { get; set; } - [JsonInclude] - [JsonPropertyName("size")] - public long Size { get; set; } -} \ No newline at end of file diff --git a/Luski.net/JsonTypes/ServerData.cs b/Luski.net/JsonTypes/ServerData.cs deleted file mode 100644 index 3e25f0b..0000000 --- a/Luski.net/JsonTypes/ServerData.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Luski.net.JsonTypes; - -public class ServerData -{ - [JsonInclude] - [JsonPropertyName("address")] - public string DomainAndPort = default!; - [JsonInclude] - [JsonPropertyName("secure")] - public bool Secure; -} - -[JsonSerializable(typeof(ServerData))] -[JsonSourceGenerationOptions( - GenerationMode = JsonSourceGenerationMode.Default, - PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, - WriteIndented = false)] -internal partial class ServerDataContext : JsonSerializerContext -{ - -} \ No newline at end of file diff --git a/Luski.net/JsonTypes/ServerInfo.cs b/Luski.net/JsonTypes/ServerInfo.cs deleted file mode 100644 index f341418..0000000 --- a/Luski.net/JsonTypes/ServerInfo.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Text.Json.Serialization; -using Luski.net.JsonTypes.BaseTypes; - -namespace Luski.net.JsonTypes; - -public class ServerInfo : IncomingHTTP -{ - public string name { get; set; } - public string wssv4 { get; set; } - public string description { get; set; } - public long owner { get; set; } - - [JsonInclude] - [JsonPropertyName("alternate_servers")] - public ServerData[] AlternateServers { get; set; } = default!; -} - -[JsonSerializable(typeof(ServerInfo))] -[JsonSourceGenerationOptions( - GenerationMode = JsonSourceGenerationMode.Default, - PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, - WriteIndented = false)] -internal partial class ServerInfoContext : JsonSerializerContext -{ - -} \ No newline at end of file diff --git a/Luski.net/JsonTypes/StatusUpdate.cs b/Luski.net/JsonTypes/StatusUpdate.cs index 7719a7d..e274f78 100755 --- a/Luski.net/JsonTypes/StatusUpdate.cs +++ b/Luski.net/JsonTypes/StatusUpdate.cs @@ -1,5 +1,5 @@ -using Luski.net.Enums; -using System.Text.Json.Serialization; +using System.Text.Json.Serialization; +using Luski.Shared.PublicServers.V1.Enums; namespace Luski.net.JsonTypes; diff --git a/Luski.net/JsonTypes/StorageInfoJSON.cs b/Luski.net/JsonTypes/StorageInfoJSON.cs deleted file mode 100644 index 40ce429..0000000 --- a/Luski.net/JsonTypes/StorageInfoJSON.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Text.Json.Serialization; -using Luski.net.Interfaces; -using Luski.net.JsonTypes.BaseTypes; - -namespace Luski.net.JsonTypes; - -public class StorageInfoJSON : IncomingHTTP, IWebRequest -{ - public long id { get; set; } - public string password { get; set; } - public bool update { get; set; } -} - -[JsonSerializable(typeof(StorageInfoJSON))] -[JsonSourceGenerationOptions( - GenerationMode = JsonSourceGenerationMode.Default, - PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, - WriteIndented = true)] -internal partial class StorageInfoJSONContext : JsonSerializerContext -{ - -} \ No newline at end of file diff --git a/Luski.net/JsonTypes/UserKeyGetRequest.cs b/Luski.net/JsonTypes/UserKeyGetRequest.cs deleted file mode 100644 index 7cae546..0000000 --- a/Luski.net/JsonTypes/UserKeyGetRequest.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Text.Json.Serialization; -using JacobTechEncryption.Enums; -using Luski.net.Interfaces; - -namespace Luski.net.JsonTypes; - -public class UserKeyGetRequest : IWebRequest -{ - public long id { get; set; } - public long owner { get; set; } - public EncryptionType encryption_type { get; set; } - public string key_data { get; set; } -} - -[JsonSerializable(typeof(UserKeyGetRequest))] -[JsonSourceGenerationOptions( - GenerationMode = JsonSourceGenerationMode.Default, - PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, - WriteIndented = false)] -internal partial class UserKeyGetRequestContext : JsonSerializerContext -{ - -} \ No newline at end of file diff --git a/Luski.net/JsonTypes/UserKeysGetRequest.cs b/Luski.net/JsonTypes/UserKeysGetRequest.cs deleted file mode 100644 index 3e140e9..0000000 --- a/Luski.net/JsonTypes/UserKeysGetRequest.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Text.Json.Serialization; -using Luski.net.JsonTypes.BaseTypes; - -namespace Luski.net.JsonTypes; - -public class UserKeysGetRequest : IncomingHTTP -{ - public UserKeyGetRequest[] keys { get; set; } -} - -[JsonSerializable(typeof(UserKeysGetRequest))] -[JsonSourceGenerationOptions( - GenerationMode = JsonSourceGenerationMode.Default, - PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, - WriteIndented = true)] -internal partial class UserKeysGetRequestContext : JsonSerializerContext -{ - -} \ No newline at end of file diff --git a/Luski.net/JsonTypes/WSS/IncomingWSS.cs b/Luski.net/JsonTypes/WSS/IncomingWSS.cs index d7fce29..36501d0 100755 --- a/Luski.net/JsonTypes/WSS/IncomingWSS.cs +++ b/Luski.net/JsonTypes/WSS/IncomingWSS.cs @@ -1,5 +1,5 @@ -using Luski.net.Enums.Public; -using System.Text.Json.Serialization; +using System.Text.Json.Serialization; +using Luski.Shared.PublicServers.V1.Enums; namespace Luski.net.JsonTypes.BaseTypes; @@ -11,6 +11,9 @@ public class BetterIncomingWSS [JsonPropertyName("error")] [JsonInclude] public string Error { get; set; } = default!; + [JsonPropertyName("data")] + [JsonInclude] + public object? Data { get; set; } } [JsonSerializable(typeof(BetterIncomingWSS))] diff --git a/Luski.net/JsonTypes/WSS/ServerEvent.cs b/Luski.net/JsonTypes/WSS/ServerEvent.cs index 7ce8406..33cfa5e 100644 --- a/Luski.net/JsonTypes/WSS/ServerEvent.cs +++ b/Luski.net/JsonTypes/WSS/ServerEvent.cs @@ -1,6 +1,5 @@ using System.Text.Json.Serialization; -using Luski.net.Enums.Public; -using Luski.net.Interfaces; +using Luski.Shared.PublicServers.V1.Enums; namespace Luski.net.JsonTypes.WSS; diff --git a/Luski.net/Luski.net.csproj b/Luski.net/Luski.net.csproj index 3501b6c..ad568a1 100755 --- a/Luski.net/Luski.net.csproj +++ b/Luski.net/Luski.net.csproj @@ -13,11 +13,11 @@ https://github.com/JacobTech-com/Luski.net True 1.0.0 - 2.0.0-alpha25 + 2.0.0-alpha65 - + diff --git a/Luski.net/MainServer.Account.cs b/Luski.net/MainServer.Account.cs index b47a5fd..9d97239 100644 --- a/Luski.net/MainServer.Account.cs +++ b/Luski.net/MainServer.Account.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Net.Http; using System.Security.Authentication; using System.Text; @@ -13,6 +12,8 @@ using Luski.net.Enums; using Luski.net.JsonTypes; using Luski.net.JsonTypes.WSS; using Luski.net.Structures.Main; +using Luski.Shared.PublicServers.V1.Enums; +using Luski.Shared.PublicServers.V1.ServerToClient.HTTP; using WebSocketSharp; namespace Luski.net; @@ -38,7 +39,7 @@ public partial class MainServer while (!EncryptionHandler.Generated) { } login = true; - Login json; + LoginSTC json; List> heads = new() { new("key", EncryptionHandler.MyPublicKey), @@ -56,7 +57,7 @@ public partial class MainServer json = SendServer( "CreateAccount", pfp, - LoginContext.Default.Login, + LoginSTCContext.Default.LoginSTC, CancellationToken, heads.ToArray()).Result; } @@ -64,7 +65,7 @@ public partial class MainServer { json = GetFromServer( "Login", - LoginContext.Default.Login, + LoginSTCContext.Default.LoginSTC, CancellationToken, heads.ToArray()).Result; } @@ -113,7 +114,7 @@ public partial class MainServer } EncryptionHandler.Hash = EncryptionHandler.LocalPasswordEncrypt(Encoding.Unicode.GetBytes(Username.ToLower() + Password)); - OfflineData offlinedata = GetFromServer("Keys/GetOfflineData", OfflineDataContext.Default.OfflineData, CancellationToken).Result; + OfflineDataBlobSTC offlinedata = GetFromServer("Keys/GetOfflineData", OfflineDataBlobSTCContext.Default.OfflineDataBlobSTC, CancellationToken).Result; if (offlinedata is not null && offlinedata.Error is null && offlinedata.Data is not null && offlinedata.Data.Length > 0) { foreach (string keyex in offlinedata.Data) diff --git a/Luski.net/MainServer.cs b/Luski.net/MainServer.cs index 26f2f76..f8c3c47 100644 --- a/Luski.net/MainServer.cs +++ b/Luski.net/MainServer.cs @@ -7,14 +7,15 @@ using System.Text.Json.Serialization.Metadata; using System.Threading; using System.Threading.Tasks; using JacobTechEncryption; -using Luski.net.Enums; using Luski.net.Enums.Main; using Luski.net.Interfaces; using Luski.net.JsonTypes; -using Luski.net.JsonTypes.BaseTypes; using Luski.net.JsonTypes.HTTP; using Luski.net.Structures.Main; -using Luski.net.Structures.Public; +using Luski.Shared.PublicServers.V1.ClientToServer.HTTP; +using Luski.Shared.PublicServers.V1.Enums; +using Luski.Shared.PublicServers.V1.ServerToClient.HTTP; +using ChannelType = Luski.net.Enums.Main.ChannelType; namespace Luski.net; @@ -209,7 +210,7 @@ public partial class MainServer : Server /// public async Task UpdateStatus(UserStatus Status, CancellationToken CancellationToken) { - IncomingHTTP? data = await SendServer("SocketUserProfile/Status", new Status() { UserStatus = Status }, StatusContext.Default.Status, IncomingHTTPContext.Default.IncomingHTTP, CancellationToken); + STC? data = await SendServer("SocketUserProfile/Status", new StatusUpdateCTS() { Status = Status }, StatusUpdateCTSContext.Default.StatusUpdateCTS, STCContext.Default.STC, CancellationToken); if (data.Error is not null && ((int)data.StatusCode < 200 || (int)data.StatusCode > 299)) { if (data?.ErrorMessage is not null) throw new Exception(data.ErrorMessage); diff --git a/Luski.net/PublicServer.Account.cs b/Luski.net/PublicServer.Account.cs index 900aa6a..0542b7a 100644 --- a/Luski.net/PublicServer.Account.cs +++ b/Luski.net/PublicServer.Account.cs @@ -10,11 +10,13 @@ using JacobTechEncryption; using JacobTechEncryption.Enums; using Luski.net.Enums; using Luski.net.JsonTypes; -using Luski.net.JsonTypes.BaseTypes; using Luski.net.JsonTypes.WSS; using Luski.net.Structures.Public; +using Luski.Shared.PublicServers.V1.ClientToServer.HTTP; +using Luski.Shared.PublicServers.V1.Enums; +using Luski.Shared.PublicServers.V1.ServerToClient.HTTP; using WebSocketSharp; -using DataType = Luski.net.Enums.Public.DataType; +using DataType = Luski.Shared.PublicServers.V1.Enums.DataType; namespace Luski.net; @@ -22,183 +24,44 @@ public partial class PublicServer { public async Task Login(string Username, string Password, CancellationToken CancellationToken) { - return await Both(Username, Password, CancellationToken); + return await All(Username, CancellationToken, Password); } - public async Task CreateAccount(string Username, string Password, string Displayname, string PFP, CancellationToken CancellationToken) + public async Task CreateAccount(string Username, string Password, string DisplayName, string PFP, CancellationToken CancellationToken) { - return await Both(Username, Password, CancellationToken, Displayname, PFP); + return await All(Username, CancellationToken, Password, DisplayName, PFP); } internal async Task LoginViaToken(string t) { - CancellationToken CancellationToken = CancellationToken.None; - if (!EncryptionHandler.Generating) - { - EncryptionHandler.GenerateKeys(); - } - while (!EncryptionHandler.Generated) { } - - login = true; - Login json; - List> heads = new() - { - new("key", EncryptionHandler.MyPublicKey), - new("token", Convert.ToBase64String(Encryption.RSA.Encrypt(t, EncryptionHandler.ServerPublicKey, EncoderType.UTF16))), - }; - - json = await GetFromServer( - "SocketAccount/AccessToken", - LoginContext.Default.Login, - CancellationToken, - heads.ToArray()); - if (json.Error is not null) throw new Exception($"Luski appears to be down at the current moment: {json.ErrorMessage}"); - if (EncryptionHandler.OfflinePrivateKey is null || EncryptionHandler.OfflinePublicKey is null) throw new Exception("Something went wrong generating the offline keys"); - login = false; - if (json is not null && json.Error is null) - { - ServerOut = new WebSocket(wssurl); - ServerOut.SslConfiguration.EnabledSslProtocols = SslProtocols.Tls13 | SslProtocols.Tls12; - ServerOut.OnMessage += DataFromServer; - ServerOut.EmitOnPing = true; - ServerOut.WaitTime = new TimeSpan(0, 0, 5); - ServerOut.OnError += ServerOut_OnError; - ServerOut.Connect(); - SendServer(DataType.Token, new WSSLogin() { Token = json.Token! }); - while (Token is null && Error is null) - { - Thread.Sleep(500); - } - if (Error is not null) - { - throw new Exception(Error); - } - - if (Token is null) throw new Exception("Server did not send a token"); - CanRequest = true; - long id = long.Parse(Encoding.UTF8.GetString(Convert.FromBase64String( - Token.Split('.')[0] - ))); - User = await GetUser(id, CancellationToken); - StorageInfoJSON data; - if (Storage.StorageID == 0) - { - EncryptionHandler.Hash = Storage.GenerateStorage(); - data = await SendServer("OfflineData/Info", - new StorageInfoJSON() - { - id = 0, - update = false, - password = Convert.ToBase64String(Encryption.AES.Encrypt(EncryptionHandler.Hash, Storage.GetResourceBytes(StorageDirectory.StorageInfo, "lpk"))) - }, - StorageInfoJSONContext.Default.StorageInfoJSON, - StorageInfoJSONContext.Default.StorageInfoJSON, CancellationToken, - new KeyValuePair("storage_id", Storage.StorageID.ToString())); - Storage.setid(data.id); - } - else - { - data = await GetFromServer("OfflineData/Info", StorageInfoJSONContext.Default.StorageInfoJSON, CancellationToken, new KeyValuePair("storage_id", Storage.StorageID.ToString())); - } - - if (data.update) - { - EncryptionHandler.Hash = Storage.UpdateStorage(Convert.FromBase64String(data.password)); - _ = await SendServerPatch("OfflineData/Info", - new StorageInfoJSON() - { - id = 0, - update = false, - password = Convert.ToBase64String(Encryption.AES.Encrypt(EncryptionHandler.Hash, Storage.GetResourceBytes(StorageDirectory.StorageInfo, "lpk"))) - }, - StorageInfoJSONContext.Default.StorageInfoJSON, - StorageInfoJSONContext.Default.StorageInfoJSON, CancellationToken, - new KeyValuePair("storage_id", Storage.StorageID.ToString())); - } - try - { - _ = EncryptionHandler.GetKey(0); - } - catch (Exception e) - { - EncryptionHandler.SetKey(0, new() - { - EncryptionType = EncryptionType.None, - Key = string.Empty - }); - } - _ = await UpdateStatus(UserStatus.Online, CancellationToken); - KeyValuePair stor = new("storage_id", Storage.StorageID.ToString()); - OfflineData offlinedata = await GetFromServer("OfflineData", OfflineDataContext.Default.OfflineData, CancellationToken, stor); - if (offlinedata is not null && offlinedata.Error is null && offlinedata.Data is not null && offlinedata.Data.Length > 0) - { - string pkey = Storage.GetResourceKeyRaw( - StorageDirectory.ServerKeys, - "pkey", - EncryptionHandler.Hash - ); - foreach (string keyexx in offlinedata.Data) - { - string keyex = Encoding.UTF8.GetString(Convert.FromBase64String(keyexx)); - KeyExchange? okd = JsonSerializer.Deserialize(keyex); - if (okd is not null && !string.IsNullOrEmpty(okd.key)) - { - Storage.SetResourceKey( - StorageDirectory.ChannelKeys, - okd.channel.ToString(), - EncryptionHandler.Hash, - Encoding.Unicode.GetString( - Encryption.RSA.Decrypt( - Convert.FromBase64String(okd.key), - pkey - ) - ) - ); - } - } - } - Storage.SetResourceKey(StorageDirectory.ServerKeys, "pkey", EncryptionHandler.Hash, EncryptionHandler.OfflinePrivateKey); - UserKeyGetRequest OfflineKeySetRequest = new() - { - key_data = Convert.ToBase64String(Encoding.UTF8.GetBytes(EncryptionHandler.OfflinePublicKey)), - encryption_type = EncryptionType.RSA - }; - _ = await SendServer("Keys/SetOfflineKey", - OfflineKeySetRequest, - UserKeyGetRequestContext.Default.UserKeyGetRequest, - IncomingHTTPContext.Default.IncomingHTTP, - CancellationToken.None, - stor); - EncryptionHandler.OfflinePublicKey = null!; - EncryptionHandler.OfflinePrivateKey = null!; - //API_Handler.IsAnyServerLoggedin = true; - return true; - } - else - { - throw new Exception(json?.ErrorMessage); - } - - //API_Handler.IsAnyServerLoggedin = true; - return true; + Console.WriteLine("Starting Auto Login:"); + bool b = await All(t, CancellationToken.None); + Console.WriteLine($"Auto Login {(b ? "Successful" : "Failed")}"); + return b; } - private async Task Both(string Username, string Password, CancellationToken CancellationToken, string? Displayname = null, string? pfp = null) + private async Task All(string Username, CancellationToken CancellationToken, string? Password = null, string? Displayname = null, string? pfp = null) { + DateTime dt = DateTime.UtcNow; + Console.WriteLine("Encryption: " + DateTime.UtcNow.Subtract(dt).ToString("g")); if (!EncryptionHandler.Generating) { EncryptionHandler.GenerateKeys(); } while (!EncryptionHandler.Generated) { } - + Console.WriteLine("Encryption 2: " + DateTime.UtcNow.Subtract(dt).ToString("g")); + List> FailedSystems = new(); login = true; - Login json; + LoginSTC json; List> heads = new() { new("key", EncryptionHandler.MyPublicKey), - new("username", Convert.ToBase64String(Encryption.RSA.Encrypt(Username, EncryptionHandler.ServerPublicKey, EncoderType.UTF16))), - new("password", EncryptionHandler.RemotePasswordEncrypt(Encryption.Generic.Encoders[(int)EncoderType.UTF16].GetBytes(Password))) + new((Password is null ? "token" : "username"), Convert.ToBase64String(Encryption.RSA.Encrypt(Username, EncryptionHandler.ServerPublicKey, EncoderType.UTF16))) }; + if (Password is not null) + { + heads.Add(new("password", EncryptionHandler.RemotePasswordEncrypt(Encryption.Generic.Encoders[(int)EncoderType.UTF16].GetBytes(Password)))); + } if (File.Exists("LastPassVer.txt") && int.TryParse(File.ReadAllText("LastPassVer.txt"), out int lpv) && lpv < EncryptionHandler.PasswordVersion && lpv >= 0) { heads.Add(new("old_password", EncryptionHandler.RemotePasswordEncrypt(Encoding.Unicode.GetBytes(Password), lpv))); @@ -210,19 +73,25 @@ public partial class PublicServer json = await SendServer( "SocketAccount", pfp, - LoginContext.Default.Login, + LoginSTCContext.Default.LoginSTC, CancellationToken, heads.ToArray()); } else { json = await GetFromServer( - "SocketAccount", - LoginContext.Default.Login, + (Password is null ? "SocketAccount/AccessToken": "SocketAccount"), + LoginSTCContext.Default.LoginSTC, CancellationToken, heads.ToArray()); } - if (json.Error is not null) throw new Exception($"Luski appears to be down at the current moment: {json.ErrorMessage}"); + Console.WriteLine("Account Result: " + DateTime.UtcNow.Subtract(dt).ToString("g")); + if (json.Error is not null) + { + Console.WriteLine(json.ErrorMessage); + return false; + throw new Exception($"Luski appears to be down at the current moment: {json.ErrorMessage}"); + } if (EncryptionHandler.OfflinePrivateKey is null || EncryptionHandler.OfflinePublicKey is null) throw new Exception("Something went wrong generating the offline keys"); login = false; if (json is not null && json.Error is null) @@ -234,15 +103,17 @@ public partial class PublicServer ServerOut.EmitOnPing = true; ServerOut.OnError += ServerOut_OnError; ServerOut.Connect(); + Console.WriteLine("WSS Connection: " + DateTime.UtcNow.Subtract(dt).ToString("g")); SendServer(DataType.Token, new WSSLogin() { Token = json.Token! }); while (Token is null && Error is null) { - Thread.Sleep(500); + Thread.Sleep(200); } if (Error is not null) { throw new Exception(Error); } + Console.WriteLine("WSS Login: " + DateTime.UtcNow.Subtract(dt).ToString("g")); if (Token is null) throw new Exception("Server did not send a token"); CanRequest = true; @@ -250,108 +121,161 @@ public partial class PublicServer Token.Split('.')[0] ))); User = await GetUser(id, CancellationToken); - StorageInfoJSON data; - if (Storage.StorageID == 0) - { - EncryptionHandler.Hash = Storage.GenerateStorage(); - data = await SendServer("OfflineData/Info", - new StorageInfoJSON() - { - id = 0, - update = false, - password = Convert.ToBase64String(Encryption.AES.Encrypt(EncryptionHandler.Hash, Storage.GetResourceBytes(StorageDirectory.StorageInfo, "lpk"))) - }, - StorageInfoJSONContext.Default.StorageInfoJSON, - StorageInfoJSONContext.Default.StorageInfoJSON, CancellationToken, - new KeyValuePair("storage_id", Storage.StorageID.ToString())); - Storage.setid(data.id); - } - else - { - data = await GetFromServer("OfflineData/Info", StorageInfoJSONContext.Default.StorageInfoJSON, CancellationToken, new KeyValuePair("storage_id", Storage.StorageID.ToString())); - } + User.Username = Username; + Console.WriteLine("Get our info: " + DateTime.UtcNow.Subtract(dt).ToString("g")); - if (data.update) - { - EncryptionHandler.Hash = Storage.UpdateStorage(Convert.FromBase64String(data.password)); - _ = await SendServerPatch("OfflineData/Info", - new StorageInfoJSON() - { - id = 0, - update = false, - password = Convert.ToBase64String(Encryption.AES.Encrypt(EncryptionHandler.Hash, Storage.GetResourceBytes(StorageDirectory.StorageInfo, "lpk"))) - }, - StorageInfoJSONContext.Default.StorageInfoJSON, - StorageInfoJSONContext.Default.StorageInfoJSON, CancellationToken, - new KeyValuePair("storage_id", Storage.StorageID.ToString())); - } + #region Extra Systems + + Task.Run(async () => { + #region Data Storage try { - _ = EncryptionHandler.GetKey(0); + StorageInfoSTC data; + if (Storage.StorageID == 0) + { + EncryptionHandler.Hash = Storage.GenerateStorage(); + data = await SendServer("OfflineData/Info", + new StorageInfoCTS() + { + Password = Convert.ToBase64String(Encryption.AES.Encrypt(EncryptionHandler.Hash, Storage.GetResourceBytes(StorageDirectory.StorageInfo, "lpk"))) + }, + StorageInfoCTSContext.Default.StorageInfoCTS, + StorageInfoSTCContext.Default.StorageInfoSTC, CancellationToken, + new KeyValuePair("storage_id", Storage.StorageID.ToString())); + Storage.setid(data.ID); + } + else + { + data = await GetFromServer("OfflineData/Info", StorageInfoSTCContext.Default.StorageInfoSTC, CancellationToken, new KeyValuePair("storage_id", Storage.StorageID.ToString())); + } + Console.WriteLine("Offline Data Info: " + DateTime.UtcNow.Subtract(dt).ToString("g")); + if (data.Update) + { + EncryptionHandler.Hash = Storage.UpdateStorage(Convert.FromBase64String(data.Password)); + _ = await SendServerPatch("OfflineData/Info", + new StorageInfoCTS() + { + Password = Convert.ToBase64String(Encryption.AES.Encrypt(EncryptionHandler.Hash, Storage.GetResourceBytes(StorageDirectory.StorageInfo, "lpk"))) + }, + StorageInfoCTSContext.Default.StorageInfoCTS, + StorageInfoSTCContext.Default.StorageInfoSTC, CancellationToken, + new KeyValuePair("storage_id", Storage.StorageID.ToString())); + Console.WriteLine("Data Update: " + DateTime.UtcNow.Subtract(dt).ToString("g")); + } } catch (Exception e) { - EncryptionHandler.SetKey(0, new() - { - EncryptionType = EncryptionType.None, - Key = string.Empty - }); + FailedSystems.Add(new("Data Storage", e.Message, e)); } - _ = await UpdateStatus(UserStatus.Online, CancellationToken); - //EncryptionHandler.Hash = EncryptionHandler.LocalPasswordEncrypt(Encoding.Unicode.GetBytes(Username.ToLower() + Password)); - KeyValuePair stor = new("storage_id", Storage.StorageID.ToString()); - OfflineData offlinedata = await GetFromServer("OfflineData", OfflineDataContext.Default.OfflineData, CancellationToken, stor); - if (offlinedata is not null && offlinedata.Error is null && offlinedata.Data is not null && offlinedata.Data.Length > 0) + #endregion + + #region Key Generation + try { - string pkey = Storage.GetResourceKeyRaw( - StorageDirectory.ServerKeys, - "pkey", - EncryptionHandler.Hash - ); - foreach (string keyexx in offlinedata.Data) + try { - string keyex = Encoding.UTF8.GetString(Convert.FromBase64String(keyexx)); - KeyExchange? okd = JsonSerializer.Deserialize(keyex); - if (okd is not null && !string.IsNullOrEmpty(okd.key)) + _ = EncryptionHandler.GetKey(0); + } + catch (Exception e) + { + EncryptionHandler.SetKey(0, new() { - Storage.SetResourceKey( - StorageDirectory.ChannelKeys, - okd.channel.ToString(), - EncryptionHandler.Hash, - Encoding.Unicode.GetString( - Encryption.RSA.Decrypt( - Convert.FromBase64String(okd.key), - pkey - ) - ) - ); - } + EncryptionType = EncryptionType.None, + Key = string.Empty + }); + Console.WriteLine("Key 0: " + DateTime.UtcNow.Subtract(dt).ToString("g")); } } - System.IO.File.WriteAllText("LastPassVer.txt", EncryptionHandler.PasswordVersion.ToString()); - Storage.SetResourceKey(StorageDirectory.ServerKeys, "pkey", EncryptionHandler.Hash, EncryptionHandler.OfflinePrivateKey); - UserKeyGetRequest OfflineKeySetRequest = new() + catch (Exception e) { - key_data = Convert.ToBase64String(Encoding.UTF8.GetBytes(EncryptionHandler.OfflinePublicKey)), - encryption_type = EncryptionType.RSA - }; - _ = await SendServer("Keys/SetOfflineKey", - OfflineKeySetRequest, - UserKeyGetRequestContext.Default.UserKeyGetRequest, - IncomingHTTPContext.Default.IncomingHTTP, - CancellationToken.None, - stor); - // using HttpClient setkey = new(); - // setkey.DefaultRequestHeaders.Add("token", Token); - // _ = await setkey.PostAsync($"{(Secure ? "https" : "http" )}://{Domain}/{ApiVersion}/Keys/SetOfflineKey", new StringContent(EncryptionHandler.OfflinePublicKey)); - EncryptionHandler.OfflinePublicKey = null!; - EncryptionHandler.OfflinePrivateKey = null!; - //API_Handler.IsAnyServerLoggedin = true; + FailedSystems.Add(new("Key Generation", "Key 0 Failed to generate", e)); + } + #endregion + + #region Auto Status + try + { + _ = await UpdateStatus(UserStatus.Online, CancellationToken); + Console.WriteLine("Status: " + DateTime.UtcNow.Subtract(dt).ToString("g")); + } + catch (Exception e) + { + FailedSystems.Add(new("Auto Status", "Failed to set status on the server", e)); + } + #endregion + + #region Local Storage Cleanup + try + { + KeyValuePair stor = new("storage_id", Storage.StorageID.ToString()); + OfflineDataBlobSTC offlinedata = await GetFromServer("OfflineData", OfflineDataBlobSTCContext.Default.OfflineDataBlobSTC, CancellationToken, stor); + Console.WriteLine("Offline Data: " + DateTime.UtcNow.Subtract(dt).ToString("g")); + if (offlinedata is not null && offlinedata.Error is null && offlinedata.Data is not null && offlinedata.Data.Length > 0) + { + string pkey = Storage.GetResourceKeyRaw( + StorageDirectory.ServerKeys, + "pkey", + EncryptionHandler.Hash + ); + foreach (string keyexx in offlinedata.Data) + { + string keyex = Encoding.UTF8.GetString(Convert.FromBase64String(keyexx)); + KeyExchange? okd = JsonSerializer.Deserialize(keyex); + if (okd is not null && !string.IsNullOrEmpty(okd.key)) + { + Storage.SetResourceKey( + StorageDirectory.ServerKeys, + okd.id.ToString(), + EncryptionHandler.Hash, + Encoding.Unicode.GetString( + Encryption.RSA.Decrypt( + Convert.FromBase64String(okd.key), + pkey + ) + ) + ); + } + } + } + System.IO.File.WriteAllText("LastPassVer.txt", EncryptionHandler.PasswordVersion.ToString()); + Storage.SetResourceKey(StorageDirectory.ServerKeys, "pkey", EncryptionHandler.Hash, EncryptionHandler.OfflinePrivateKey); + KeyPostCTS OfflineKeySetRequest = new() + { + Data = Convert.ToBase64String(Encoding.UTF8.GetBytes(EncryptionHandler.OfflinePublicKey)), + EncryptionType = EncryptionType.RSA + }; + _ = await SendServer("Keys/SetOfflineKey", + OfflineKeySetRequest, + KeyPostCTSContext.Default.KeyPostCTS, + STCContext.Default.STC, + CancellationToken.None, + stor); + Console.WriteLine("Offline Key: " + DateTime.UtcNow.Subtract(dt).ToString("g")); + EncryptionHandler.OfflinePublicKey = null!; + EncryptionHandler.OfflinePrivateKey = null!; + } + catch (Exception e) + { + FailedSystems.Add(new("Local Storage Cleanup", "Failed to clean the local storage", e)); + } + #endregion + + if (FailedSystems.Count > 0) + { + Console.WriteLine("Some systems have failed:"); + foreach (Tuple System in FailedSystems) + { + Console.WriteLine($"\t{System.Item1}:\n\t\tMessage: {System.Item2}\n\t\tError Details: {System.Item3}"); + } + } }); + + + #endregion + return true; } else throw new Exception(json?.ErrorMessage); - //API_Handler.IsAnyServerLoggedin = true; return true; } } \ No newline at end of file diff --git a/Luski.net/PublicServer.Incoming.cs b/Luski.net/PublicServer.Incoming.cs index 926c9df..3c01ab7 100644 --- a/Luski.net/PublicServer.Incoming.cs +++ b/Luski.net/PublicServer.Incoming.cs @@ -1,11 +1,17 @@ using System; -using System.IO; +using System.Collections.Generic; using System.Text.Json; +using JacobTechEncryption; +using JacobTechEncryption.Enums; using Luski.net.Enums; using Luski.net.JsonTypes.BaseTypes; using Luski.net.JsonTypes.WSS; +using Luski.net.Structures; +using Luski.net.Structures.Public; +using Luski.Shared.PublicServers.V1.ServerToClient.HTTP; using WebSocketSharp; -using DataType = Luski.net.Enums.Public.DataType; +using File = System.IO.File; +using DataType = Luski.Shared.PublicServers.V1.Enums.DataType; namespace Luski.net; @@ -14,6 +20,10 @@ public partial class PublicServer private void DataFromServer(object? sender, MessageEventArgs e) { if (e.IsPing) return; + if (PrintServerMessages) + { + Console.WriteLine(e.Data); + } try { BetterIncomingWSS? data = JsonSerializer.Deserialize(e.Data, BetterIncomingWSSContext.Default.BetterIncomingWSS); @@ -25,6 +35,52 @@ public partial class PublicServer n.SessionToken); Token = n.Token; break; + case DataType.MessageCreate: + MessageSTC smsg = JsonSerializer.Deserialize(data.Data!.ToString()!, MessageSTCContext.Default.MessageSTC)!; + List fl = new(); + foreach (var VARIABLE in smsg.Files) + { + fl.Add(VARIABLE.ID); + } + + if (smsg.EncryptionKey == 0) + { + if (string.IsNullOrEmpty(smsg.Context)) + { + smsg.Context = ""; + } + else smsg.Context = Encryption.Generic.Encoders[(int)smsg.EncoderType] + .GetString(Convert.FromBase64String(smsg.Context)); + } + else + { + LocalKeyInfo key = EncryptionHandler.GetKey(smsg.EncryptionKey); + switch (key.EncryptionType) + { + case EncryptionType.RSA: + smsg.Context = Encryption.RSA.Decrypt(Convert.FromBase64String(smsg.Context), key.Key, + smsg.EncoderType); + break; + default: + smsg.Context = Encryption.Generic.Encoders[(int)smsg.EncoderType] + .GetString(Convert.FromBase64String(smsg.Context)); + break; + } + } + SocketMessage sm = new() + { + ID = smsg.ID, + AuthorID = smsg.AuthorID, + ChannelID = smsg.ChannelID, + Context = smsg.Context, + EncoderType = smsg.EncoderType, + EncryptionKey = smsg.EncryptionKey, + FileIDs = fl.ToArray(), + Server = this, + IsProfile = smsg.IsProfile + }; + if (MessageReceived is not null) MessageReceived.Invoke(sm); + break; default: Console.WriteLine("Unknown"); break; diff --git a/Luski.net/PublicServer.cs b/Luski.net/PublicServer.cs index 1539d63..6412524 100644 --- a/Luski.net/PublicServer.cs +++ b/Luski.net/PublicServer.cs @@ -3,28 +3,29 @@ using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Text; -using System.Text.Json.Serialization.Metadata; using System.Threading; using System.Threading.Tasks; using JacobTechEncryption; using JacobTechEncryption.Enums; using Luski.net.Enums; -using Luski.net.Enums.Main; using Luski.net.Interfaces; using Luski.net.JsonTypes; -using Luski.net.JsonTypes.BaseTypes; using Luski.net.JsonTypes.HTTP; -using Luski.net.JsonTypes.Public; using Luski.net.Structures; using Luski.net.Structures.Public; -using Channel = Luski.net.JsonTypes.Public.Channel; +using Luski.Shared.PublicServers.V1.ClientToServer.HTTP; +using Luski.Shared.PublicServers.V1.Enums; +using Luski.Shared.PublicServers.V1.ServerToClient.HTTP; +using Luski.Shared.PublicServers.V1.Shared; using Role = Luski.net.Structures.Public.Role; +using SocketChannelProfile = Luski.net.Structures.Public.SocketChannelProfile; using SocketUser = Luski.net.Structures.Public.SocketUser; namespace Luski.net; public partial class PublicServer : Server { + public event Func? MessageReceived; public List chans { get; } = new(); public List cats { get; } = new(); public List roles { get; } = new(); @@ -35,15 +36,24 @@ public partial class PublicServer : Server base(Domain, API_Version, Secure) { } - internal static async Task GetServer(string Domain, string API_Version, bool Secure = true) + internal static async Task GetServer(string Domain, string API_Version, bool Secure = true, bool GenerateEncryption = true, bool LogConsole = false) { DateTime dt = DateTime.UtcNow; Console.WriteLine("Connecting to public server '{0}' using API {1}.", Domain, API_Version); PublicServer s = new(Domain, API_Version, Secure); - ServerInfo? si = null; + s.PrintServerMessages = LogConsole; + if (GenerateEncryption) + { + Thread t = new(_ => + { + s.EncryptionHandler.GenerateKeys(); + }); + t.Start(); + } + ServerInfoSTC? si = null; try { - si = await s.GetFromServer("socketserver", ServerInfoContext.Default.ServerInfo, CancellationToken.None); + si = await s.GetFromServer("socketserver", ServerInfoSTCContext.Default.ServerInfoSTC, CancellationToken.None); s.EncryptionHandler.ServerPublicKey = await (await new HttpClient() .GetAsync($"{(s.Secure ? "https" : "http")}://{s.Domain}/{s.ApiVersion}/Keys/PublicKey")) .Content @@ -64,7 +74,7 @@ public partial class PublicServer : Server try { - si = await s.GetFromServer("socketserver", ServerInfoContext.Default.ServerInfo, CancellationToken.None); + si = await s.GetFromServer("socketserver", ServerInfoSTCContext.Default.ServerInfoSTC, CancellationToken.None); s.EncryptionHandler.ServerPublicKey = await (await new HttpClient() .GetAsync($"{(s.Secure ? "https" : "http")}://{s.Domain}/{s.ApiVersion}/Keys/PublicKey")) .Content @@ -81,17 +91,20 @@ public partial class PublicServer : Server if (si is null) throw; } - s.Name = si.name; - s.Description = si.description; - s.wssurl = si.wssv4; + s.Name = si.Name; + s.Description = si.Description; + s.wssurl = si.WSSv4Address; s.ServerType = ServerType.Public; + s.OwnerID = si.Owner; Console.WriteLine("Connected to public server '{0}' using API {1} in {4}.\nServer Name: {2}\nServer Description: {3}", Domain, API_Version, s.Name, s.Description, DateTime.UtcNow.Subtract(dt).ToString("g")); return s; } + + public long OwnerID { get; private set; } = 0; public async Task GetCategory(long id, CancellationToken CancellationToken) where TCategory : SocketCategory, new() { - Category request; + CategorySTC request; if (cats.Count > 0 && cats.Any(s => s.ID == id)) { return (cats.Where(s => s is TCategory && s.ID == id).First() as TCategory)!; @@ -100,7 +113,7 @@ public partial class PublicServer : Server { if (CanRequest) { - request = await GetFromServer($"SocketCategory", PublicCategoryContext.Default.Category, CancellationToken, new KeyValuePair("id", id.ToString())); + request = await GetFromServer($"SocketCategory", CategorySTCContext.Default.CategorySTC, CancellationToken, new KeyValuePair("id", id.ToString())); break; } } @@ -115,7 +128,14 @@ public partial class PublicServer : Server } } - LocalKeyInfo deckey = EncryptionHandler.GetKey(request.DescriptionEncryptionKey); + LocalKeyInfo deckey; + if (request.DescriptionEncryptionKey != 0) + deckey = EncryptionHandler.GetKey(request.DescriptionEncryptionKey); + else deckey = new() + { + EncryptionType = EncryptionType.None, + Key = string.Empty + }; string dec = deckey.EncryptionType switch { EncryptionType.RSA => Encryption.RSA.Decrypt(Convert.FromBase64String(request.Description), deckey.Key, request.DescriptionEncoderType), @@ -123,7 +143,14 @@ public partial class PublicServer : Server Encryption.AES.Decrypt(Convert.FromBase64String(request.Description), deckey.Key)), _ => Encryption.Generic.Encoders[(short)request.DescriptionEncoderType].GetString(Convert.FromBase64String(request.Description)) }; - LocalKeyInfo nkey = EncryptionHandler.GetKey(request.TitleEncryptionKey); + LocalKeyInfo nkey; + if (request.TitleEncryptionKey != 0) + nkey = EncryptionHandler.GetKey(request.TitleEncryptionKey); + else nkey = new() + { + EncryptionType = EncryptionType.None, + Key = string.Empty + }; string n = nkey.EncryptionType switch { EncryptionType.RSA => Encryption.RSA.Decrypt(Convert.FromBase64String(request.Name), nkey.Key, request.TitleEncoderType), @@ -142,8 +169,8 @@ public partial class PublicServer : Server Channels = request.Channels, Categories = request.InnerCategories, Name = n, - RoleOverides = request.RoleOverides, - UserOverides = request.UserOverides, + RoleOverides = request.RoleOverrides, + UserOverides = request.UserOverrides, TitleEncoderType = request.TitleEncoderType, TitleEncryptionKey = request.TitleEncryptionKey, Server = this, @@ -161,32 +188,88 @@ public partial class PublicServer : Server _ => new Exception($"Unknown data: '{request.ErrorMessage}'"), }; } - + public async Task GetRole(long id) { Role[] r = roles.Where(s => s.ID == id).ToArray(); if (r.Length > 0) return r[0]; - JsonTypes.Public.Role s = await GetFromServer("SocketRole?id=" + id.ToString(), RoleContext.Default.Role, CancellationToken.None); + RoleSTC s = await GetFromServer("SocketRole?id=" + id.ToString(), RoleSTCContext.Default.RoleSTC, CancellationToken.None); Role role = new() { Server = this, - ID = s.id, - Color = new(s.color), - Description = s.description, - DisplayName = s.display_name, - MembersListID = s.members_list, - Name = s.name, - Index = s.index, - ServerPermissions = s.server_permissions + ID = s.ID, + Color = new(s.Color), + Description = s.Description, + DisplayName = s.DisplayName, + MembersListID = s.Members, + Name = s.Name, + Index = s.Index, + ServerPermissions = s.ServerPermissions }; roles.Add(role); return role; } + + public async Task SendMessage(TChannel channel, string msg, SocketMessage? ReplyTo = null, + SocketChannelProfile? FakeProfile = null) where TChannel : SocketChannel, new() + { + string bc = ""; + if (channel.EncryptionKeys[0] == 0) + { + if (!string.IsNullOrEmpty(msg)) + bc = Convert.ToBase64String(Encryption.Generic.Encoders[(int)channel.EncoderTypes[0]].GetBytes(msg)); + } + else + { + LocalKeyInfo key = channel.Server.EncryptionHandler.GetKey(channel.EncryptionKeys[0]); + bc = Convert.ToBase64String(key.EncryptionType switch + { + EncryptionType.RSA => Encryption.RSA.Encrypt(msg, key.Key, channel.EncoderTypes[0]), + _ => Encryption.AES.Encrypt(Encryption.Generic.Encoders[(int)channel.EncoderTypes[0]].GetBytes(msg), key.Key) + }); + } + MessageCTS pcsm = new() + { + Files = Array.Empty(), + ChannelID = channel.ID, + EncryptionKey = channel.EncryptionKeys[0], + Encoding = channel.EncoderTypes[0], + Base64Context = bc + }; + if (FakeProfile is not null) + { + pcsm.Profile = FakeProfile.Id; + } + + + + MessageSTC smsg = await channel.Server.SendServer("socketmessage", pcsm, + MessageCTSContext.Default.MessageCTS, MessageSTCContext.Default.MessageSTC, + CancellationToken.None); + List fl = new(); + foreach (var VARIABLE in smsg.Files) + { + fl.Add(VARIABLE.ID); + } + SocketMessage sm = new() + { + ID = smsg.ID, + AuthorID = smsg.AuthorID, + ChannelID = channel.ID, + Context = msg, + EncoderType = smsg.EncoderType, + EncryptionKey = smsg.EncryptionKey, + FileIDs = fl.ToArray(), + Server = channel.Server, + IsProfile = smsg.IsProfile + }; + return sm; + } public async Task GetChannel(long id, CancellationToken CancellationToken) where TChannel : SocketChannel, new() { - Channel request; + ChannelSTC request; if (chans.Count > 0 && chans.Any(s => s.ID == id)) { return (chans.Where(s => s is TChannel && s.ID == id).First() as TChannel)!; @@ -195,7 +278,7 @@ public partial class PublicServer : Server { if (CanRequest) { - request = await GetFromServer($"SocketChannel", PublicChannelContext.Default.Channel, CancellationToken, new KeyValuePair("id", id.ToString())); + request = await GetFromServer($"SocketChannel", ChannelSTCContext.Default.ChannelSTC, CancellationToken, new KeyValuePair("id", id.ToString())); break; } } @@ -210,7 +293,14 @@ public partial class PublicServer : Server } } - LocalKeyInfo deckey = EncryptionHandler.GetKey(request.DescriptionEncryptionKey); + LocalKeyInfo deckey; + if (request.DescriptionEncryptionKey != 0) + deckey = EncryptionHandler.GetKey(request.DescriptionEncryptionKey); + else deckey = new() + { + EncryptionType = EncryptionType.None, + Key = string.Empty + }; string dec = deckey.EncryptionType switch { EncryptionType.RSA => Encryption.RSA.Decrypt(Convert.FromBase64String(request.Description), deckey.Key, request.DescriptionEncoderType), @@ -218,7 +308,14 @@ public partial class PublicServer : Server Encryption.AES.Decrypt(Convert.FromBase64String(request.Description), deckey.Key)), _ => Encryption.Generic.Encoders[(short)request.DescriptionEncoderType].GetString(Convert.FromBase64String(request.Description)) }; - LocalKeyInfo nkey = EncryptionHandler.GetKey(request.TitleEncryptionKey); + LocalKeyInfo nkey; + if (request.TitleEncryptionKey != 0) + nkey = EncryptionHandler.GetKey(request.TitleEncryptionKey); + else nkey = new() + { + EncryptionType = EncryptionType.None, + Key = string.Empty + }; string n = nkey.EncryptionType switch { EncryptionType.RSA => Encryption.RSA.Decrypt(Convert.FromBase64String(request.Name), nkey.Key, request.TitleEncoderType), @@ -238,8 +335,8 @@ public partial class PublicServer : Server EncryptionKeys = request.EncryptionKeys, Epoch = request.Epoch, Name = n, - RoleOverides = request.RoleOverides, - UserOverides = request.UserOverides, + RoleOverrides = request.RoleOverrides, + UserOverrides = request.UserOverrides, Type = request.Type, TitleEncoderType = request.TitleEncoderType, TitleEncryptionKey = request.TitleEncryptionKey, @@ -259,10 +356,56 @@ public partial class PublicServer : Server _ => new Exception($"Unknown data: '{request.ErrorMessage}'"), }; } + + public async Task MakeChannel(SocketCategory parent, string Name, string Decription) + { + ChannelSTC res = await SendServer( + "SocketChannel", + new ChannelPostCTS() + { + Name = Convert.ToBase64String(Encoding.UTF8.GetBytes(Name)), + Description = Convert.ToBase64String(Encoding.UTF8.GetBytes(Description)), + EncoderTypes = new[] { EncoderType.UTF16 }, + EncryptionKeys = new long[] { 0 }, + DescriptionEncoderType = EncoderType.UTF8, + TitleEncoderType = EncoderType.UTF8, + Parent = parent.ID, + DescriptionEncryptionKey = 0, + TitleEncryptionKey = 0, + RoleOverrides = Array.Empty(), + UserOverrides = Array.Empty(), + Type = ChannelType.TextAndVoice, + Color = "FFFFFFFF", + PictureType = PictureType.none + }, + ChannelPostCTSContext.Default.ChannelPostCTS, + ChannelSTCContext.Default.ChannelSTC, + CancellationToken.None); + return new SocketChannel() + { + ID = res.ID, + CategoryID = res.Parent, + Description = Description, + DescriptionEncoderType = res.DescriptionEncoderType, + DescriptionEncryptionKey = res.DescriptionEncryptionKey, + EncoderTypes = res.EncoderTypes, + EncryptionKeys = res.EncryptionKeys, + Epoch = res.Epoch, + Name = Name, + RoleOverrides = res.RoleOverrides, + UserOverrides = res.UserOverrides, + Type = res.Type, + TitleEncoderType = res.TitleEncoderType, + TitleEncryptionKey = res.TitleEncryptionKey, + PictureType = res.PictureType, + Server = this, + Color = new(res.Color) + }; + } public async Task GetUser(long UserId, CancellationToken CancellationToken) where Tuser : SocketUser, new() { - JsonTypes.Public.SocketUser user; + SocketUserSTC user; if (poeople.Count > 0 && poeople.Any(s => s.Id == UserId)) { Tuser temp = poeople.Where(s => s is Tuser && s.Id == UserId).Cast().FirstOrDefault()!; @@ -273,7 +416,7 @@ public partial class PublicServer : Server if (CanRequest) { user = await GetFromServer("socketuser", - PublicSocketUserContext.Default.SocketUser, + SocketUserSTCContext.Default.SocketUserSTC, CancellationToken, new KeyValuePair("id", UserId.ToString())); break; @@ -319,13 +462,60 @@ public partial class PublicServer : Server PictureType = user.PictureType, RoleIds = user.RoleIds, Status = user.Status, - Username = user.Username } as Tuser)!; } poeople.Add(u); return u; } + public async Task GetChannelProfile(long ProfileId, CancellationToken CancellationToken) + { + ChannelProfileSTC user; + if (profiles.Count > 0 && profiles.Any(s => s.Id == ProfileId)) + { + SocketChannelProfile temp = profiles.Where(s => s.Id == ProfileId).FirstOrDefault()!; + return temp; + } + while (true) + { + if (CanRequest) + { + user = await GetFromServer("socketchannelprofile", + ChannelProfileSTCContext.Default.ChannelProfileSTC, + CancellationToken, + new KeyValuePair("id", ProfileId.ToString())); + break; + } + } + + if (user is null) throw new Exception("Server did not return a user"); + if (profiles.Count > 0 && profiles.Any(s => s.Id == ProfileId)) + { + foreach (SocketChannelProfile? p in profiles.Where(s => s.Id == ProfileId)) + { + profiles.Remove(p); + } + } + if (user is null || user.Error is not null) + { + string error = "User was null"; + if (user is not null && user.Error is not null) error = $"{user.Error}: {user.ErrorMessage}"; + throw new Exception($"Something went wrong getting your user information\n{error}"); + } + + SocketChannelProfile u = new() + { + Server = this, + Id = user.ID, + DisplayName = user.DisplayName, + PictureType = user.PictureType, + Controllers = user.Controllers, + Color = new(user.Color) + }; + poeople.Add(u); + return u; + } + /// /// Sends the server a request to update the of you account /// @@ -333,7 +523,7 @@ public partial class PublicServer : Server /// public async Task UpdateStatus(UserStatus Status, CancellationToken CancellationToken) { - IncomingHTTP? data = await SendServer("SocketUserProfile/Status", new Status() { UserStatus = Status }, StatusContext.Default.Status, IncomingHTTPContext.Default.IncomingHTTP, CancellationToken); + STC? data = await SendServer("SocketUserProfile/Status", new StatusUpdateCTS() { Status = Status }, StatusUpdateCTSContext.Default.StatusUpdateCTS, STCContext.Default.STC, CancellationToken); if (data.Error is not null && ((int)data.StatusCode < 200 || (int)data.StatusCode > 299)) { if (data?.ErrorMessage is not null) throw new Exception(data.ErrorMessage); diff --git a/Luski.net/Server.Encryption.cs b/Luski.net/Server.Encryption.cs index 0cfa80c..d8a47f0 100644 --- a/Luski.net/Server.Encryption.cs +++ b/Luski.net/Server.Encryption.cs @@ -65,7 +65,7 @@ public class ServerEncryption public void GenerateKeys() { - if (!Generating) + if (!Generating && !Generated) { Generating = true; GenerateNewKeys(out MyPublicKey, out myPrivateKey); diff --git a/Luski.net/Server.Globals.cs b/Luski.net/Server.Globals.cs index 27893e5..ef7ddbb 100644 --- a/Luski.net/Server.Globals.cs +++ b/Luski.net/Server.Globals.cs @@ -15,10 +15,13 @@ public partial class Server { public ServerType ServerType { get; internal set; } = ServerType.Public; public string Domain { get; set; } = default!; + + public bool PrintServerMessages { get; set; } = false; public string ApiVersion { get; } = "v1"; internal WebSocket? ServerOut; internal string? Token = null, Error = null, gen = null; public bool IsLogedIn => Token is not null; internal bool CanRequest = false, login = false; internal List poeople = new(); + internal List profiles = new(); } \ No newline at end of file diff --git a/Luski.net/Server.Storage.cs b/Luski.net/Server.Storage.cs index b550410..4804315 100644 --- a/Luski.net/Server.Storage.cs +++ b/Luski.net/Server.Storage.cs @@ -26,7 +26,8 @@ public class ServerStorage "Channels/Icons", "Channels/Messages", "StorageInfo", - "Channels/Files" + "Files", + "Channels/Profiles" }; private static readonly int[] CantDelete = new[] @@ -94,7 +95,23 @@ public class ServerStorage { byte[] NewPassword = new byte[100]; byte[] lpk = GetResourceBytes(StorageDirectory.StorageInfo, "lpk"); - OldPassword = Encryption.AES.Decrypt(OldPassword, 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); diff --git a/Luski.net/Server.cs b/Luski.net/Server.cs index c1d62f4..441aa11 100644 --- a/Luski.net/Server.cs +++ b/Luski.net/Server.cs @@ -13,6 +13,9 @@ using Luski.net.Enums; using Luski.net.Interfaces; using Luski.net.JsonTypes.BaseTypes; using Luski.net.JsonTypes.WSS; +using Luski.Shared.PublicServers.V1.ClientToServer.HTTP; +using Luski.Shared.PublicServers.V1.Enums; +using Luski.Shared.PublicServers.V1.ServerToClient.HTTP; using File = System.IO.File; namespace Luski.net; @@ -47,7 +50,7 @@ public partial class Server ServerOut?.Send(JsonSerializer.Serialize(Payload, jsonTypeInfo)); } - public void SendServer(Enums.Public.DataType Type, IServerEvent Payload) + public void SendServer(Luski.Shared.PublicServers.V1.Enums.DataType Type, IServerEvent Payload) { ServerOut?.Send(JsonSerializer.Serialize(new WSSOut() { @@ -82,10 +85,9 @@ public partial class Server return Task.CompletedTask; } - public async Task GetFromServer(string Path, JsonTypeInfo Type, CancellationToken CancellationToken, params KeyValuePair[] Headers) where Tresult : IncomingHTTP, new() + public async Task GetFromServer(string Path, JsonTypeInfo Type, CancellationToken CancellationToken, params KeyValuePair[] Headers) where Tresult : STC, new() { HttpResponseMessage ServerResponce = GetFromServer(Path, CancellationToken, Headers); - //if (!ServerResponce.IsSuccessStatusCode) return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with status code {(int)ServerResponce.StatusCode}:{ServerResponce.StatusCode}" }; Tresult temp = new(); string raw = ServerResponce.Content.ReadAsStringAsync(CancellationToken).Result; try @@ -100,7 +102,7 @@ public partial class Server return temp; } - public async Task SendServer(string Path, Tvalue Payload, JsonTypeInfo jsonTypeInfo, JsonTypeInfo ReturnjsonTypeInfo, CancellationToken CancellationToken, params KeyValuePair[] Headers) where Tvalue : IWebRequest where Tresult : IncomingHTTP, new() + public async Task SendServer(string Path, Tvalue Payload, JsonTypeInfo jsonTypeInfo, JsonTypeInfo ReturnjsonTypeInfo, CancellationToken CancellationToken, params KeyValuePair[] Headers) where Tvalue : CTS where Tresult : STC, new() { using HttpClient web = new(); if (!login) web.DefaultRequestHeaders.Add("token", Token); @@ -118,7 +120,7 @@ public partial class Server catch { return error; } } - public async Task SendServerPatch(string Path, Tvalue Payload, JsonTypeInfo jsonTypeInfo, JsonTypeInfo ReturnjsonTypeInfo, CancellationToken CancellationToken, params KeyValuePair[] Headers) where Tvalue : IWebRequest where Tresult : IncomingHTTP, new() + public async Task SendServerPatch(string Path, Tvalue Payload, JsonTypeInfo jsonTypeInfo, JsonTypeInfo ReturnjsonTypeInfo, CancellationToken CancellationToken, params KeyValuePair[] Headers) where Tvalue : CTS where Tresult : STC, new() { using HttpClient web = new(); if (!login) web.DefaultRequestHeaders.Add("token", Token); @@ -138,7 +140,7 @@ public partial class Server catch { return error; } } - public async Task SendServer(string Path, string File, JsonTypeInfo ReturnjsonTypeInfo, CancellationToken CancellationToken, params KeyValuePair[] Headers) where Tresult : IncomingHTTP, new() + public async Task SendServer(string Path, string File, JsonTypeInfo ReturnjsonTypeInfo, CancellationToken CancellationToken, params KeyValuePair[] Headers) where Tresult : STC, new() { var fs = System.IO.File.OpenRead(File); try diff --git a/Luski.net/Structures/File.cs b/Luski.net/Structures/File.cs index d012a44..29b7be6 100755 --- a/Luski.net/Structures/File.cs +++ b/Luski.net/Structures/File.cs @@ -13,10 +13,11 @@ using System.Threading; using System.Threading.Tasks; using JacobTechEncryption; using Luski.net.Interfaces; +using Luski.Shared.PublicServers.V1.ServerToClient.HTTP; namespace Luski.net.Structures; -public class File : IncomingHTTP +public class File : STC { public MainServer Server { get; internal set; } [JsonInclude] diff --git a/Luski.net/Structures/Main/MainSocketChannel.cs b/Luski.net/Structures/Main/MainSocketChannel.cs index a71e541..3fa3c14 100755 --- a/Luski.net/Structures/Main/MainSocketChannel.cs +++ b/Luski.net/Structures/Main/MainSocketChannel.cs @@ -14,10 +14,11 @@ using JacobTechEncryption; using JacobTechEncryption.Enums; using Luski.net.Enums.Main; using Luski.net.JsonTypes; +using Luski.Shared.PublicServers.V1.ServerToClient.HTTP; namespace Luski.net.Structures.Main; -public class MainSocketChannel : IncomingHTTP +public class MainSocketChannel : STC { [JsonInclude] [JsonPropertyName("id")] diff --git a/Luski.net/Structures/Main/MainSocketMessage.cs b/Luski.net/Structures/Main/MainSocketMessage.cs index f51ca04..e59a425 100755 --- a/Luski.net/Structures/Main/MainSocketMessage.cs +++ b/Luski.net/Structures/Main/MainSocketMessage.cs @@ -6,10 +6,11 @@ using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; using JacobTechEncryption; +using Luski.Shared.PublicServers.V1.ServerToClient.HTTP; namespace Luski.net.Structures.Main; -public class MainSocketMessage : IncomingHTTP +public class MainSocketMessage : STC { public MainServer Server { get; internal set; } = default!; [JsonPropertyName("id")] diff --git a/Luski.net/Structures/Main/MainSocketTextChannel.cs b/Luski.net/Structures/Main/MainSocketTextChannel.cs index f0a596b..4674af9 100755 --- a/Luski.net/Structures/Main/MainSocketTextChannel.cs +++ b/Luski.net/Structures/Main/MainSocketTextChannel.cs @@ -11,7 +11,10 @@ using System.Threading; using System.Threading.Tasks; using JacobTechEncryption; using JacobTechEncryption.Enums; -using Luski.net.Enums.Main; +using Luski.Shared.PublicServers.V1.ClientToServer.HTTP; +using Luski.Shared.PublicServers.V1.Enums; +using Luski.Shared.PublicServers.V1.ServerToClient.HTTP; +using ChannelType = Luski.net.Enums.Main.ChannelType; namespace Luski.net.Structures.Main; @@ -138,10 +141,13 @@ public class MainSocketTextChannel : MainSocketChannel public async Task SendMessage(string Message, CancellationToken CancellationToken, params File?[] Files) { string key = Server.EncryptionHandler.GetChannelKey(Id); - JsonTypes.HTTP.Message m = new() + MessageCTS m = new() { - Context = Convert.ToBase64String(Encryption.RSA.Encrypt(Message, key, EncoderType.UTF8)), - Channel = Id, + Base64Context = Convert.ToBase64String(Encryption.RSA.Encrypt(Message, key, EncoderType.UTF8)), + ChannelID = Id, + Profile = null, + EncryptionKey = 0, + Encoding = EncoderType.UTF8 }; if (Files is not null && Files.Length > 0) { @@ -157,7 +163,7 @@ public class MainSocketTextChannel : MainSocketChannel } m.Files = bb.ToArray(); } - IncomingHTTP data = await Server.SendServer("socketmessage", m, net.JsonTypes.HTTP.MessageContext.Default.Message, IncomingHTTPContext.Default.IncomingHTTP, CancellationToken); + STC data = await Server.SendServer("socketmessage", m, MessageCTSContext.Default.MessageCTS, STCContext.Default.STC, CancellationToken); if (data.Error is not null && data.ErrorMessage != "Server responded with empty data") throw new Exception(data.ErrorMessage); return Task.CompletedTask; } diff --git a/Luski.net/Structures/Main/MainSocketUserBase.cs b/Luski.net/Structures/Main/MainSocketUserBase.cs index bf3eb69..1f6fbb6 100644 --- a/Luski.net/Structures/Main/MainSocketUserBase.cs +++ b/Luski.net/Structures/Main/MainSocketUserBase.cs @@ -1,14 +1,17 @@ +using System; using System.IO; using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; using Luski.net.Enums; using Luski.net.Interfaces; -using Luski.net.JsonTypes.BaseTypes; +using Luski.net.Structures.Public; +using Luski.Shared.PublicServers.V1.Enums; +using Luski.Shared.PublicServers.V1.ServerToClient.HTTP; namespace Luski.net.Structures.Main; -public class MainSocketUserBase : IncomingHTTP, IUser +public class MainSocketUserBase : STC, IUser { public MainServer Server { get; internal set; } = default!; [JsonPropertyName("id")] @@ -23,6 +26,11 @@ public class MainSocketUserBase : IncomingHTTP, IUser [JsonPropertyName("picture_type")] [JsonInclude] public PictureType PictureType { get; internal set; } = default!; + + public Task GetColor() + { + throw new NotImplementedException(); + } public async Task GetAvatar(CancellationToken CancellationToken) { bool isc = System.IO.File.Exists(Server.Storage.GetStorageDirectory(StorageDirectory.Avatars) + Id.ToString()); diff --git a/Luski.net/Structures/Main/SocketBulkMessage.cs b/Luski.net/Structures/Main/SocketBulkMessage.cs index 5a5b336..f936006 100755 --- a/Luski.net/Structures/Main/SocketBulkMessage.cs +++ b/Luski.net/Structures/Main/SocketBulkMessage.cs @@ -1,9 +1,10 @@ -using Luski.net.JsonTypes.BaseTypes; -using System.Text.Json.Serialization; +using System.Text.Json.Serialization; +using Luski.Shared.PublicServers.V1.ServerToClient.HTTP; namespace Luski.net.Structures.Main; -internal class SocketBulkMessage : IncomingHTTP +internal class SocketBulkMessage : STC + { [JsonPropertyName("messages")] [JsonInclude] diff --git a/Luski.net/Structures/Public/Color.cs b/Luski.net/Structures/Public/Color.cs index bc225b2..02156c6 100644 --- a/Luski.net/Structures/Public/Color.cs +++ b/Luski.net/Structures/Public/Color.cs @@ -6,35 +6,35 @@ public class Color { public Color(string servercol) { - Bytes = servercol; + Bytes = Convert.FromHexString(servercol); } public Color(byte R, byte G, byte B, byte A) { - Bytes = $"{Convert.ToChar(R)}{Convert.ToChar(G)}{Convert.ToChar(B)}{Convert.ToChar(A)}"; + Bytes = new byte[] {R, G, B, A}; } - private string Bytes; - - public string ToDB() + public string ToDatabaseStr() { - return Bytes; + return Convert.ToHexString(Bytes); } + + private byte[] Bytes; public byte A { - get => (byte)(Bytes[3]); + get => Bytes[3]; } public byte R { - get => (byte)(Bytes[0]); + get => Bytes[0]; } public byte G { - get => (byte)(Bytes[1]); + get => Bytes[1]; } public byte B { - get => (byte)(Bytes[2]); + get => Bytes[2]; } } \ No newline at end of file diff --git a/Luski.net/Structures/Public/Role.cs b/Luski.net/Structures/Public/Role.cs index d5a3b78..bed89d0 100644 --- a/Luski.net/Structures/Public/Role.cs +++ b/Luski.net/Structures/Public/Role.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; using System.Threading.Tasks; -using Luski.net.Enums.Public; +using Luski.Shared.PublicServers.V1.Enums; namespace Luski.net.Structures.Public; @@ -13,7 +13,7 @@ public class Role public required Color Color { get; init; } = default!; public required string Description { get; init; } = default!; public required string DisplayName { get; init; } = default!; - public required ServerPermission[] ServerPermissions { get; init; } = default!; + public required ServerPermission ServerPermissions { get; init; } = default!; public required long[] MembersListID { get; init; } = default!; private List? RawUsers = null; diff --git a/Luski.net/Structures/Public/RoleOveride.cs b/Luski.net/Structures/Public/RoleOveride.cs index c79ebce..27dd63a 100644 --- a/Luski.net/Structures/Public/RoleOveride.cs +++ b/Luski.net/Structures/Public/RoleOveride.cs @@ -1,4 +1,6 @@ using System.Threading.Tasks; +using Luski.Shared.PublicServers.V1.Enums; +using Luski.Shared.PublicServers.V1.Shared; namespace Luski.net.Structures.Public; @@ -6,7 +8,10 @@ public class RoleOveride { public long ID { get; init; } public long ParentRoleID { get; init; } - internal string[] RawOverides { get; init; } + + public ServerPermission GoodPermissions { get; set; } + + public ServerPermission BadPermissions { get; set; } private Role? Parent = null; public Task GetRole() diff --git a/Luski.net/Structures/Public/SocketAppUser.cs b/Luski.net/Structures/Public/SocketAppUser.cs index b29e5c5..0b91c08 100755 --- a/Luski.net/Structures/Public/SocketAppUser.cs +++ b/Luski.net/Structures/Public/SocketAppUser.cs @@ -1,6 +1,9 @@ -using System.Threading; +using System.Collections.Generic; +using System.Linq; +using System.Threading; using System.Threading.Tasks; -using Luski.net.Enums; +using Luski.Shared.PublicServers.V1.Enums; +using Luski.Shared.PublicServers.V1.Shared; namespace Luski.net.Structures.Public; @@ -13,5 +16,39 @@ public class SocketAppUser : SocketUser return await Server.GetChannel(SelectedChannel, Token); } - public string Username { get; init; } = default!; + public async Task HasAccessToCategory(SocketCategory Category, ServerPermission RequiredPerms) + { + if (Category.Server != Server) return false; + if (Server.OwnerID == Id) return true; + Role[] UserRoleIDList = await GetRoles(); + RequiredPerms |= ServerPermission.ViewThis; + + ServerPermission GoodPerms = ServerPermission.None; + + UserOverride[] CatUserOverides = await Category.GetUserOverrides(); + foreach (UserOverride CatUserOveride in CatUserOverides) + { + if (CatUserOveride.UserID != Id) continue; + if ((CatUserOveride.BadPermissions & RequiredPerms) > ServerPermission.None) return false; + + GoodPerms |= CatUserOveride.GoodPermissions; + } + RoleOveride[] CatRoleOverides = await Category.GetRoleOverrides(); + foreach (RoleOveride CatRoleOveride in CatRoleOverides) + { + if (!RoleIds.Contains(CatRoleOveride.ParentRoleID)) continue; + if ((CatRoleOveride.BadPermissions & RequiredPerms) > ServerPermission.None) return false; + + GoodPerms |= CatRoleOveride.GoodPermissions; + } + + foreach (Role RoleID in UserRoleIDList) + { + if (((RoleID.ServerPermissions & RequiredPerms) ^ GoodPerms) > ServerPermission.None) return false; + GoodPerms |= RoleID.ServerPermissions; + } + return GoodPerms.HasPermission(RequiredPerms); + } + + public string Username { get; internal set; } = default!; } \ No newline at end of file diff --git a/Luski.net/Structures/Public/SocketCategory.cs b/Luski.net/Structures/Public/SocketCategory.cs index 0e60844..fe0511a 100644 --- a/Luski.net/Structures/Public/SocketCategory.cs +++ b/Luski.net/Structures/Public/SocketCategory.cs @@ -1,10 +1,8 @@ -using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using JacobTechEncryption.Enums; -using Luski.net.Enums.Public; namespace Luski.net.Structures.Public; @@ -20,7 +18,7 @@ public class SocketCategory internal long[] Categories { get; set; } SocketCategory? RawParent = null; List? RawRoleOverides = null; - List? RawUserOverides = null; + List? RawUserOverides = null; List? RawChan = null; List? RawCat = null; @@ -81,7 +79,7 @@ public class SocketCategory public string Name { get; internal set; } public string Description { get; internal set; } - public Task GetRoleOverides() + public Task GetRoleOverrides() { if (RawRoleOverides is null) { @@ -90,7 +88,7 @@ public class SocketCategory return Task.FromResult(RawRoleOverides!.ToArray()); } - public Task GetUserOveride() + public Task GetUserOverrides() { if (RawUserOverides is null) { diff --git a/Luski.net/Structures/Public/SocketChannel.cs b/Luski.net/Structures/Public/SocketChannel.cs index 0c184e1..938f26e 100644 --- a/Luski.net/Structures/Public/SocketChannel.cs +++ b/Luski.net/Structures/Public/SocketChannel.cs @@ -6,8 +6,8 @@ using System.Threading.Tasks; using JacobTechEncryption; using JacobTechEncryption.Enums; using Luski.net.Enums; -using Luski.net.Enums.Public; -using Luski.net.JsonTypes.Public; +using Luski.Shared.PublicServers.V1.Enums; +using Luski.Shared.PublicServers.V1.ServerToClient.HTTP; namespace Luski.net.Structures.Public; @@ -17,14 +17,14 @@ public class SocketChannel public Color Color { get; init; } public long ID { get; internal set; } internal long CategoryID { get; set; } - internal long[] RoleOverides { get; set; } - internal long[] UserOverides { get; set; } + internal long[] RoleOverrides { get; set; } + internal long[] UserOverrides { get; set; } SocketCategory? RawParent = null; List? RawRoleOverides = null; - List? RawUserOverides = null; + List? RawUserOverides = null; public PictureType PictureType { get; internal set; } - public async Task> GetMessages(CancellationToken CancellationToken, int count = 50) + public async Task> GetMessages(CancellationToken CancellationToken, SocketMessage Last, int count = 50) { try { @@ -38,18 +38,17 @@ public class SocketChannel } else { - PublicSocketBulkMessage data = await Server.GetFromServer("SocketBulkMessage", - PublicSocketBulkMessageContext.Default.PublicSocketBulkMessage, + SocketBulkMessageSTC data = await Server.GetFromServer("SocketBulkMessage", + SocketBulkMessageSTCContext.Default.SocketBulkMessageSTC, CancellationToken, new KeyValuePair("id", ID.ToString()), + new KeyValuePair("mostrecentid", Last.ID.ToString()), new KeyValuePair("messages", count.ToString())); if (data is not null && !data.Error.HasValue) { int num = Convert.ToInt32(6); if (num == 0) num = 1; - //string key = Server.EncryptionHandler.GetKey(enc) - //string key = Server.EncryptionHandler.GetChannelKey(ID); - if (data.Messages is null) data.Messages = Array.Empty(); + if (data.Messages is null) data.Messages = Array.Empty(); List mmmm = new(); ParallelLoopResult p = Parallel.ForEach(data.Messages, new ParallelOptions() { @@ -113,11 +112,11 @@ public class SocketChannel } }); - foreach (PublicMessage i in data.Messages) + foreach (MessageSTC i in data.Messages) { var ff = new List(); List sf = new(); - foreach (pFile v in i.Files) + foreach (ServerFileInfoSTC v in i.Files) { sf.Add(new() { @@ -128,7 +127,6 @@ public class SocketChannel NameEncoder = v.NameEncoder, Key = v.Key, NameKey = v.NameKey, - Channel = v.Channel, Server = Server }); ff.Add(v.ID); @@ -139,12 +137,12 @@ public class SocketChannel ID = i.ID, ChannelID = ID, AuthorID = i.AuthorID, - TimeStamp = i.TimeStamp, Context = i.Context, EncryptionKey = i.EncryptionKey, EncoderType = i.EncoderType, FileIDs = ff.ToArray(), - _Files = sf + _Files = sf, + IsProfile = i.IsProfile }); } return await Task.FromResult(mmmm.AsReadOnly()); @@ -169,6 +167,154 @@ public class SocketChannel } } + public async Task> GetMessages(CancellationToken CancellationToken, int count = 50) + { + try + { + if (count > 200) + { + throw new Exception("You can not request more than 200 messages at a time"); + } + else if (count < 1) + { + throw new Exception("You must request at least 1 message"); + } + else + { + SocketBulkMessageSTC data = await Server.GetFromServer("SocketBulkMessage", + SocketBulkMessageSTCContext.Default.SocketBulkMessageSTC, + CancellationToken, + new KeyValuePair("id", ID.ToString()), + new KeyValuePair("messages", count.ToString())); + if (data is not null && !data.Error.HasValue) + { + int num = Convert.ToInt32(6); + if (num == 0) num = 1; + if (data.Messages is null) data.Messages = Array.Empty(); + List mmmm = new(); + ParallelLoopResult p = Parallel.ForEach(data.Messages, new ParallelOptions() + { + MaxDegreeOfParallelism = num + }, i => + { + if (i.EncryptionKey == 0) + { + if (string.IsNullOrEmpty(i.Context)) + { + i.Context = ""; + } + else i.Context = Encryption.Generic.Encoders[(int)i.EncoderType] + .GetString(Convert.FromBase64String(i.Context)); + } + else + { + LocalKeyInfo key = Server.EncryptionHandler.GetKey(i.EncryptionKey); + switch (key.EncryptionType) + { + case EncryptionType.RSA: + i.Context = Encryption.RSA.Decrypt(Convert.FromBase64String(i.Context), key.Key, + i.EncoderType); + break; + default: + i.Context = Encryption.Generic.Encoders[(int)i.EncoderType] + .GetString(Convert.FromBase64String(i.Context)); + break; + } + } + + if (i.Files.Length > 0) + { + for (int j = 0; j < i.Files.Length; j++) + { + if (i.Files[j].Key == 0) + { + if (string.IsNullOrEmpty(i.Files[j].Name)) + { + i.Files[j].Name = ""; + } + else i.Files[j].Name = Encryption.Generic.Encoders[(int)i.Files[j].NameEncoder] + .GetString(Convert.FromBase64String(i.Files[j].Name)); + } + else + { + LocalKeyInfo key = Server.EncryptionHandler.GetKey(i.Files[j].NameKey); + switch (key.EncryptionType) + { + case EncryptionType.RSA: + i.Files[j].Name = Encryption.RSA.Decrypt(Convert.FromBase64String(i.Context), key.Key, + i.Files[j].NameEncoder); + break; + default: + i.Files[j].Name = Encryption.Generic.Encoders[(int)i.Files[j].NameEncoder] + .GetString(Convert.FromBase64String(i.Context)); + break; + } + } + } + } + }); + + foreach (MessageSTC i in data.Messages) + { + var ff = new List(); + List sf = new(); + foreach (ServerFileInfoSTC v in i.Files) + { + sf.Add(new() + { + ID = v.ID, + Size = v.Size, + Name = v.Name, + Encoder = v.Encoder, + NameEncoder = v.NameEncoder, + Key = v.Key, + NameKey = v.NameKey, + Server = Server + }); + ff.Add(v.ID); + } + mmmm.Add(new() + { + Server = Server, + ID = i.ID, + ChannelID = ID, + AuthorID = i.AuthorID, + Context = i.Context, + EncryptionKey = i.EncryptionKey, + EncoderType = i.EncoderType, + FileIDs = ff.ToArray(), + _Files = sf, + IsProfile = i.IsProfile + }); + } + return await Task.FromResult(mmmm.AsReadOnly()); + } + else + { + throw data?.Error switch + { + ErrorCode.InvalidToken => new Exception("Your current token is no longer valid"), + ErrorCode.ServerError => new Exception($"Error from server: {data.ErrorMessage}"), + ErrorCode.InvalidHeader => new Exception(data.ErrorMessage), + ErrorCode.MissingHeader => new Exception("The header sent to the server was not found. This may be because you app is couropt or you are using the wron API version"), + ErrorCode.Forbidden => new Exception("You are not allowed to do this request"), + _ => new Exception(data?.Error.ToString()), + }; + } + } + } + catch (Exception) + { + throw; + } + } + + public async Task SendMessage(string msg, SocketMessage? ReplyTo = null, + SocketChannelProfile? FakeProfile = null) + { + return await Server.SendMessage(this, msg, ReplyTo, FakeProfile); + } + public async Task GetPicture(CancellationToken CancellationToken) { bool isc = System.IO.File.Exists(Server.Storage.GetStorageDirectory(StorageDirectory.ChannelIcons) + ID.ToString()); @@ -199,7 +345,7 @@ public class SocketChannel return Task.FromResult(RawRoleOverides!.ToArray()); } - public Task GetUserOveride() + public Task GetUserOveride() { if (RawUserOverides is null) { diff --git a/Luski.net/Structures/Public/SocketChannelProfile.cs b/Luski.net/Structures/Public/SocketChannelProfile.cs new file mode 100644 index 0000000..4a32322 --- /dev/null +++ b/Luski.net/Structures/Public/SocketChannelProfile.cs @@ -0,0 +1,38 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Luski.net.Enums; +using Luski.net.Interfaces; +using Luski.Shared.PublicServers.V1.Enums; + +namespace Luski.net.Structures.Public; + +public class SocketChannelProfile : IUser +{ + public PublicServer Server { get; init; } = default!; + public long Id { get; init; } = default!; + public string DisplayName { get; init; } = default!; + public virtual UserStatus Status { get; init; } = UserStatus.Online; + public PictureType PictureType { get; init; } = default!; + public long[] Controllers { get; internal set; } = default!; + + internal Color Color { get; set; } = default!; + + public Task GetColor() + { + return Task.FromResult(Color); + } + + public async Task GetAvatar(CancellationToken CancellationToken) + { + bool isc = System.IO.File.Exists(Server.Storage.GetStorageDirectory(StorageDirectory.ProfileAvatars) + Id.ToString()); + if (!isc) await Server.GetFromServer($"socketchannelprofile/Avatar/{Id}", Server.Storage.GetStorageDirectory(StorageDirectory.ProfileAvatars) + Id.ToString(), CancellationToken); + return Server.Storage.GetResourceStream(StorageDirectory.ProfileAvatars, Id.ToString()); + } + + public Task GetUserKeys(CancellationToken CancellationToken) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/Luski.net/Structures/Public/SocketFile.cs b/Luski.net/Structures/Public/SocketFile.cs index b167566..db14fdc 100644 --- a/Luski.net/Structures/Public/SocketFile.cs +++ b/Luski.net/Structures/Public/SocketFile.cs @@ -10,7 +10,6 @@ public class SocketFile { public required PublicServer Server { get; init; } public required long ID { get; init; } - public required long Channel { get; init; } public required string Name { get; init; } public required EncoderType Encoder { get; init; } public required EncoderType NameEncoder { get; init; } @@ -20,9 +19,9 @@ public class SocketFile public async Task GetCache(CancellationToken CancellationToken) { - string d = Server.Storage.GetStorageDirectory(StorageDirectory.Files) + Channel.ToString() + "-" + ID.ToString(); + string d = Server.Storage.GetStorageDirectory(StorageDirectory.Files) + ID.ToString(); bool isc = System.IO.File.Exists(d); - if (!isc) await Server.GetFromServer($"socketfile?id={ID}&channel={Channel}", d, CancellationToken); - return Server.Storage.GetResourceStream(StorageDirectory.Files, Channel.ToString() + "-" + ID.ToString()); + if (!isc) await Server.GetFromServer($"socketfile?id={ID}", d, CancellationToken); + return Server.Storage.GetResourceStream(StorageDirectory.Files, ID.ToString()); } } \ No newline at end of file diff --git a/Luski.net/Structures/Public/SocketMessage.cs b/Luski.net/Structures/Public/SocketMessage.cs index 6b5617a..fec334c 100644 --- a/Luski.net/Structures/Public/SocketMessage.cs +++ b/Luski.net/Structures/Public/SocketMessage.cs @@ -8,14 +8,14 @@ namespace Luski.net.Structures.Public; public class SocketMessage { - public PublicServer Server { get; init; } = default!; - public long ID { get; internal set; } - public long ChannelID { get; internal set; } - public long AuthorID { get; internal set; } - public long TimeStamp { get; internal set; } + public required PublicServer Server { get; init; } = default!; + public required long ID { get; init; } + public required long ChannelID { get; init; } + public required long AuthorID { get; init; } public string Context { get; internal set; } - public long EncryptionKey { get; internal set; } + public required long EncryptionKey { get; init; } public long[] FileIDs { get; internal set; } + public required bool IsProfile { get; init; } public EncoderType EncoderType { get; internal set; } private SocketChannel? RawParent; private IUser? au; @@ -40,8 +40,16 @@ public class SocketMessage { if (au is null) { - if (AuthorID == Server.User.Id) au = Server.User; - else au = await Server.GetUser(ChannelID, token); + if (IsProfile) + { + au = await Server.GetChannelProfile(AuthorID, token); + } + else + { + if (AuthorID == Server.User.Id) au = Server.User; + else au = await Server.GetUser(AuthorID, token); + } + } return au; diff --git a/Luski.net/Structures/Public/SocketUser.cs b/Luski.net/Structures/Public/SocketUser.cs index 82a1761..cdee224 100644 --- a/Luski.net/Structures/Public/SocketUser.cs +++ b/Luski.net/Structures/Public/SocketUser.cs @@ -1,14 +1,14 @@ using System; using System.Collections.Generic; using System.IO; -using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; using Luski.net.Classes; using Luski.net.Enums; using Luski.net.Interfaces; using Luski.net.JsonTypes; -using Luski.net.JsonTypes.BaseTypes; +using Luski.Shared.PublicServers.V1.Enums; +using Luski.Shared.PublicServers.V1.ServerToClient.HTTP; namespace Luski.net.Structures.Public; @@ -21,6 +21,11 @@ public class SocketUser : IUser public PictureType PictureType { get; init; } = default!; public long[] RoleIds { get; init; } = default!; private List? RawRoles = null; + + public async Task GetColor() + { + return (await GetRoles())[0].Color; + } public async Task GetRoles() { @@ -47,16 +52,16 @@ public class SocketUser : IUser public Task GetUserKeys(CancellationToken CancellationToken) { - UserKeysGetRequest data = Server.GetFromServer($"Keys/UserKeys/{Id}", UserKeysGetRequestContext.Default.UserKeysGetRequest, CancellationToken).Result; + KeysGetSTC data = Server.GetFromServer($"Keys/UserKeys/{Id}", KeysGetSTCContext.Default.KeysGetSTC, CancellationToken).Result; List pki = new(); - foreach (UserKeyGetRequest key in data.keys) + foreach (KeyGetSTC key in data.Keys) { pki.Add(new() { - Id = key.id, - Owner = key.owner, - EncryptionType = key.encryption_type, - Data = Convert.FromBase64String(key.key_data) + Id = key.ID, + Owner = key.Owner, + EncryptionType = key.EncryptionType, + Data = Convert.FromBase64String(key.Data) }); } return Task.FromResult(pki.ToArray()); diff --git a/Luski.net/Structures/Public/UserOveride.cs b/Luski.net/Structures/Public/UserOveride.cs deleted file mode 100644 index 35771ec..0000000 --- a/Luski.net/Structures/Public/UserOveride.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Luski.net.Structures.Public; - -public class UserOveride -{ - -} \ No newline at end of file diff --git a/Luski.net/Structures/Public/UserOverride.cs b/Luski.net/Structures/Public/UserOverride.cs new file mode 100644 index 0000000..e7e7955 --- /dev/null +++ b/Luski.net/Structures/Public/UserOverride.cs @@ -0,0 +1,14 @@ +using Luski.Shared.PublicServers.V1.Enums; +using Luski.Shared.PublicServers.V1.Shared; + +namespace Luski.net.Structures.Public; + +public class UserOverride +{ + public required long ID { get; init; } = default!; + public required long UserID { get; init; } = default!; + + public ServerPermission GoodPermissions { get; set; } + + public ServerPermission BadPermissions { get; set; } +} \ No newline at end of file From 82ccdd83075bec8d95a57c429f4458b3d2e3e51c Mon Sep 17 00:00:00 2001 From: JacobTech Date: Sun, 31 Mar 2024 23:57:12 -0400 Subject: [PATCH 12/14] Overrides & Members --- Luski.net/API.cs | 2 +- Luski.net/Luski.net.csproj | 4 +- Luski.net/PublicServer.Account.cs | 11 +++ Luski.net/PublicServer.cs | 49 ++++++++++++- Luski.net/Structures/Public/Role.cs | 11 ++- .../{RoleOveride.cs => RoleOverride.cs} | 14 ++-- Luski.net/Structures/Public/SocketAppUser.cs | 21 ++++-- Luski.net/Structures/Public/SocketCategory.cs | 4 +- Luski.net/Structures/Public/SocketChannel.cs | 72 +++++++++++++++++-- 9 files changed, 159 insertions(+), 29 deletions(-) rename Luski.net/Structures/Public/{RoleOveride.cs => RoleOverride.cs} (65%) diff --git a/Luski.net/API.cs b/Luski.net/API.cs index 306244d..ec74a72 100644 --- a/Luski.net/API.cs +++ b/Luski.net/API.cs @@ -107,7 +107,7 @@ public class API { IEnumerable isl = InternalServers.Where(a => (a.Domain == Domain && a.ApiVersion == Version)); if (isl.Any()) return isl.First(); - s = await PublicServer.GetServer(Domain, Version, Secure); + s = await PublicServer.GetServer(Domain, Version, Secure, GenerateEncryption, LogConsole); } catch (Exception e) { diff --git a/Luski.net/Luski.net.csproj b/Luski.net/Luski.net.csproj index ad568a1..b4b67ea 100755 --- a/Luski.net/Luski.net.csproj +++ b/Luski.net/Luski.net.csproj @@ -13,11 +13,11 @@ https://github.com/JacobTech-com/Luski.net True 1.0.0 - 2.0.0-alpha65 + 2.0.0-alpha76 - + diff --git a/Luski.net/PublicServer.Account.cs b/Luski.net/PublicServer.Account.cs index 0542b7a..6cbc47a 100644 --- a/Luski.net/PublicServer.Account.cs +++ b/Luski.net/PublicServer.Account.cs @@ -203,6 +203,17 @@ public partial class PublicServer FailedSystems.Add(new("Auto Status", "Failed to set status on the server", e)); } #endregion + + #region GetRoles + try + { + _ = await GetRoles(); + } + catch (Exception e) + { + FailedSystems.Add(new("Get Roles", "Failed to get roles on the server", e)); + } + #endregion #region Local Storage Cleanup try diff --git a/Luski.net/PublicServer.cs b/Luski.net/PublicServer.cs index 6412524..e2e6b7c 100644 --- a/Luski.net/PublicServer.cs +++ b/Luski.net/PublicServer.cs @@ -29,6 +29,7 @@ public partial class PublicServer : Server public List chans { get; } = new(); public List cats { get; } = new(); public List roles { get; } = new(); + public List roleso { get; } = new(); public SocketAppUser User { get; private set; } = null!; @@ -210,6 +211,47 @@ public partial class PublicServer : Server roles.Add(role); return role; } + + public async Task GetRoleOverride(long id) + { + RoleOverride[] r = roleso.Where(s => s.ID == id).ToArray(); + if (r.Length > 0) return r[0]; + UserRoleOverrideSTC s = await GetFromServer("SocketOverrides/RoleOverride/" + id.ToString(), UserRoleOverrideSTCContext.Default.UserRoleOverrideSTC, CancellationToken.None); + + RoleOverride role = new() + { + ID = s.Id, + BadPermissions = s.BadPermissions, + GoodPermissions = s.GoodPermissions, + ParentRoleID = s.RoleID, + Server = this + }; + roleso.Add(role); + return role; + } + + public async Task GetRoles() + { + RolesSTC s = await GetFromServer("SocketRole/GetAll", RolesSTCContext.Default.RolesSTC, CancellationToken.None); + roles.Clear(); + foreach (var ServerRole in s.Roles) + { + roles.Add(new Role() + { + Server = this, + ID = ServerRole.ID, + Color = new(ServerRole.Color), + Description = ServerRole.Description, + DisplayName = ServerRole.DisplayName, + MembersListID = ServerRole.Members, + Name = ServerRole.Name, + Index = ServerRole.Index, + ServerPermissions = ServerRole.ServerPermissions + }); + } + + return roles.ToArray(); + } public async Task SendMessage(TChannel channel, string msg, SocketMessage? ReplyTo = null, SocketChannelProfile? FakeProfile = null) where TChannel : SocketChannel, new() @@ -344,6 +386,7 @@ public partial class PublicServer : Server Server = this, Color = new(request.Color) }; + chans.Add(bob); return bob; } @@ -364,7 +407,7 @@ public partial class PublicServer : Server new ChannelPostCTS() { Name = Convert.ToBase64String(Encoding.UTF8.GetBytes(Name)), - Description = Convert.ToBase64String(Encoding.UTF8.GetBytes(Description)), + Description = Convert.ToBase64String(Encoding.UTF8.GetBytes(Decription)), EncoderTypes = new[] { EncoderType.UTF16 }, EncryptionKeys = new long[] { 0 }, DescriptionEncoderType = EncoderType.UTF8, @@ -385,7 +428,7 @@ public partial class PublicServer : Server { ID = res.ID, CategoryID = res.Parent, - Description = Description, + Description = Decription, DescriptionEncoderType = res.DescriptionEncoderType, DescriptionEncryptionKey = res.DescriptionEncryptionKey, EncoderTypes = res.EncoderTypes, @@ -439,7 +482,7 @@ public partial class PublicServer : Server } Tuser u = new(); - if (u is SocketUser) + if (typeof(Tuser).FullName == typeof(SocketUser).FullName) { u = new() { diff --git a/Luski.net/Structures/Public/Role.cs b/Luski.net/Structures/Public/Role.cs index bed89d0..9a8e332 100644 --- a/Luski.net/Structures/Public/Role.cs +++ b/Luski.net/Structures/Public/Role.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using Luski.Shared.PublicServers.V1.Enums; @@ -17,13 +18,17 @@ public class Role public required long[] MembersListID { get; init; } = default!; private List? RawUsers = null; - public Task GetMembers() + public async Task GetMembers() { if (RawUsers is null) { - + RawUsers = new(); + foreach (long m in MembersListID) + { + RawUsers.Add(await Server.GetUser(m, CancellationToken.None)); + } } - return Task.FromResult(RawUsers!.ToArray()); + return RawUsers!.ToArray(); } } \ No newline at end of file diff --git a/Luski.net/Structures/Public/RoleOveride.cs b/Luski.net/Structures/Public/RoleOverride.cs similarity index 65% rename from Luski.net/Structures/Public/RoleOveride.cs rename to Luski.net/Structures/Public/RoleOverride.cs index 27dd63a..a83cd7a 100644 --- a/Luski.net/Structures/Public/RoleOveride.cs +++ b/Luski.net/Structures/Public/RoleOverride.cs @@ -4,7 +4,7 @@ using Luski.Shared.PublicServers.V1.Shared; namespace Luski.net.Structures.Public; -public class RoleOveride +public class RoleOverride { public long ID { get; init; } public long ParentRoleID { get; init; } @@ -12,15 +12,11 @@ public class RoleOveride public ServerPermission GoodPermissions { get; set; } public ServerPermission BadPermissions { get; set; } - private Role? Parent = null; + + public required PublicServer Server { get; init; } - public Task GetRole() + public async Task GetRole() { - if (Parent is null) - { - - } - - return Task.FromResult(Parent)!; + return await Server.GetRole(ParentRoleID); } } \ No newline at end of file diff --git a/Luski.net/Structures/Public/SocketAppUser.cs b/Luski.net/Structures/Public/SocketAppUser.cs index 0b91c08..9300ac6 100755 --- a/Luski.net/Structures/Public/SocketAppUser.cs +++ b/Luski.net/Structures/Public/SocketAppUser.cs @@ -16,7 +16,7 @@ public class SocketAppUser : SocketUser return await Server.GetChannel(SelectedChannel, Token); } - public async Task HasAccessToCategory(SocketCategory Category, ServerPermission RequiredPerms) + public async Task HasAccessToCategory(SocketCategory Category, ServerPermission RequiredPerms = ServerPermission.ViewThis) { if (Category.Server != Server) return false; if (Server.OwnerID == Id) return true; @@ -33,18 +33,29 @@ public class SocketAppUser : SocketUser GoodPerms |= CatUserOveride.GoodPermissions; } - RoleOveride[] CatRoleOverides = await Category.GetRoleOverrides(); - foreach (RoleOveride CatRoleOveride in CatRoleOverides) + + RoleOverride[] CatRoleOverides = await Category.GetRoleOverrides(); + int bad_index = -1; + int good_index = -1; + + foreach (RoleOverride CatRoleOveride in CatRoleOverides) { if (!RoleIds.Contains(CatRoleOveride.ParentRoleID)) continue; - if ((CatRoleOveride.BadPermissions & RequiredPerms) > ServerPermission.None) return false; + var index = (await CatRoleOveride.GetRole()).Index; + if ((CatRoleOveride.BadPermissions & RequiredPerms) > ServerPermission.None) + { + if (bad_index < index) + bad_index = index; + } + else good_index = index; GoodPerms |= CatRoleOveride.GoodPermissions; } + if (bad_index > good_index) return false; + foreach (Role RoleID in UserRoleIDList) { - if (((RoleID.ServerPermissions & RequiredPerms) ^ GoodPerms) > ServerPermission.None) return false; GoodPerms |= RoleID.ServerPermissions; } return GoodPerms.HasPermission(RequiredPerms); diff --git a/Luski.net/Structures/Public/SocketCategory.cs b/Luski.net/Structures/Public/SocketCategory.cs index fe0511a..630ba13 100644 --- a/Luski.net/Structures/Public/SocketCategory.cs +++ b/Luski.net/Structures/Public/SocketCategory.cs @@ -17,7 +17,7 @@ public class SocketCategory internal long[] Channels { get; set; } internal long[] Categories { get; set; } SocketCategory? RawParent = null; - List? RawRoleOverides = null; + List? RawRoleOverides = null; List? RawUserOverides = null; List? RawChan = null; List? RawCat = null; @@ -79,7 +79,7 @@ public class SocketCategory public string Name { get; internal set; } public string Description { get; internal set; } - public Task GetRoleOverrides() + public Task GetRoleOverrides() { if (RawRoleOverides is null) { diff --git a/Luski.net/Structures/Public/SocketChannel.cs b/Luski.net/Structures/Public/SocketChannel.cs index 938f26e..ad4b9fc 100644 --- a/Luski.net/Structures/Public/SocketChannel.cs +++ b/Luski.net/Structures/Public/SocketChannel.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Threading; using System.Threading.Tasks; using JacobTechEncryption; @@ -20,7 +21,7 @@ public class SocketChannel internal long[] RoleOverrides { get; set; } internal long[] UserOverrides { get; set; } SocketCategory? RawParent = null; - List? RawRoleOverides = null; + List? RawRoleOverides = null; List? RawUserOverides = null; public PictureType PictureType { get; internal set; } @@ -336,16 +337,20 @@ public class SocketChannel public DateTime Epoch { get; internal set; } public string Name { get; internal set; } public string Description { get; internal set; } - public Task GetRoleOverides() + public async Task GetRoleOverrides() { if (RawRoleOverides is null) { RawRoleOverides = new(); + foreach (long ro in RoleOverrides) + { + RawRoleOverides.Add(await Server.GetRoleOverride(ro)); + } } - return Task.FromResult(RawRoleOverides!.ToArray()); + return RawRoleOverides!.ToArray(); } - public Task GetUserOveride() + public Task GetUserOverride() { if (RawUserOverides is null) { @@ -354,6 +359,65 @@ public class SocketChannel return Task.FromResult(RawUserOverides!.ToArray()); } + + public async Task GetMembers() + { + ServerPermission req = ServerPermission.ViewThis; + List GoodMembers = new(); + List GoodRoles = new(); + List BadRoles = new(); + List GoodPeople = new(); + + foreach (UserOverride cro in await GetUserOverride()) + { + if ((cro.GoodPermissions & req) == req) GoodMembers.Add(cro.UserID); + } + + foreach (RoleOverride ro in (await GetRoleOverrides())) + { + if ((ro.GoodPermissions & req) == req) + { + GoodRoles.Add(ro.ParentRoleID); + } + else if ((ro.BadPermissions & req) == req) + { + BadRoles.Add(ro.ParentRoleID); + } + } + + bool bad = false; + + foreach (Role Role in Server.roles.OrderBy(s => s.Index)) + { + if (BadRoles.Contains(Role.ID)) + { + bad = true; + } + + if (bad && GoodRoles.Contains(Role.ID)) + { + bad = false; + } + + if (!bad) + { + foreach (var m in await Role.GetMembers()) + { + var t = GoodPeople.Where(s => s.Id == m.Id); + if (t.Count() == 0) GoodPeople.Add(m); + } + } + } + + foreach (long m in GoodMembers) + { + var t = GoodPeople.Where(s => s.Id == m); + if (t.Count() == 0) GoodPeople.Add(await Server.GetUser(m, CancellationToken.None)); + } + + return GoodPeople.ToArray(); + } + public long TitleEncryptionKey { get; internal set; } public long DescriptionEncryptionKey { get; internal set; } public long[] EncryptionKeys { get; internal set; } From 42290dc9839c2057052c2e78e9616e98ff58589f Mon Sep 17 00:00:00 2001 From: JacobTech Date: Tue, 27 Aug 2024 10:57:22 -0400 Subject: [PATCH 13/14] Server Update --- Luski.net/API.cs | 5 +- Luski.net/Interfaces/IUser.cs | 17 - Luski.net/JsonTypes/LocalServerInfo.cs | 15 +- Luski.net/Luski.net.csproj | 4 +- Luski.net/PublicServer.Account.cs | 2 +- Luski.net/PublicServer.Incoming.cs | 108 ++++- Luski.net/PublicServer.cs | 447 +++++++++++++++--- Luski.net/Server.Globals.cs | 2 +- Luski.net/Server.Storage.cs | 7 + Luski.net/Server.cs | 17 + .../Structures/Main/MainSocketTextChannel.cs | 5 +- Luski.net/Structures/Public/Color.cs | 38 +- Luski.net/Structures/Public/Role.cs | 26 +- Luski.net/Structures/Public/RoleOverride.cs | 1 - ...cketChannelProfile.cs => ServerProfile.cs} | 26 +- Luski.net/Structures/Public/SocketAppUser.cs | 13 + Luski.net/Structures/Public/SocketCategory.cs | 9 +- Luski.net/Structures/Public/SocketChannel.cs | 39 +- Luski.net/Structures/Public/SocketFile.cs | 1 + Luski.net/Structures/Public/SocketMessage.cs | 31 +- Luski.net/Structures/Public/SocketUser.cs | 26 +- 21 files changed, 693 insertions(+), 146 deletions(-) rename Luski.net/Structures/Public/{SocketChannelProfile.cs => ServerProfile.cs} (52%) diff --git a/Luski.net/API.cs b/Luski.net/API.cs index ec74a72..0a26552 100644 --- a/Luski.net/API.cs +++ b/Luski.net/API.cs @@ -33,6 +33,9 @@ public class API internal List InternalServers { get; } = new(); public IReadOnlyList LoadedServers => InternalServers.AsReadOnly(); + internal List InternalFailedServers { get; } = new(); + public IReadOnlyList FailedServers => InternalFailedServers.AsReadOnly(); + private static HttpResponseMessage GetFromServer(string Domain, string ApiVersion, bool Secure, string Path, CancellationToken CancellationToken, params KeyValuePair[] Headers) { using HttpClient web = new(); @@ -107,7 +110,7 @@ public class API { IEnumerable isl = InternalServers.Where(a => (a.Domain == Domain && a.ApiVersion == Version)); if (isl.Any()) return isl.First(); - s = await PublicServer.GetServer(Domain, Version, Secure, GenerateEncryption, LogConsole); + s = await PublicServer.GetServer(InternalFailedServers, Domain, Version, Secure, GenerateEncryption, LogConsole); } catch (Exception e) { diff --git a/Luski.net/Interfaces/IUser.cs b/Luski.net/Interfaces/IUser.cs index 68d4de9..bd17814 100755 --- a/Luski.net/Interfaces/IUser.cs +++ b/Luski.net/Interfaces/IUser.cs @@ -17,26 +17,9 @@ public interface IUser /// long Id { get; } /// - /// The cerrent username of the user - /// - string DisplayName { get; } - /// /// The current status of the user /// UserStatus Status { get; } - - /// - /// The color of the display name - /// - Task GetColor(); - /// - /// will returen the picture type of the user - /// - PictureType PictureType { get; } - /// - /// Gets the current avatar of the user - /// - Task GetAvatar(CancellationToken CancellationToken); /// /// Gets the current user keys /// diff --git a/Luski.net/JsonTypes/LocalServerInfo.cs b/Luski.net/JsonTypes/LocalServerInfo.cs index 4d65bb0..b124597 100644 --- a/Luski.net/JsonTypes/LocalServerInfo.cs +++ b/Luski.net/JsonTypes/LocalServerInfo.cs @@ -1,5 +1,6 @@ using System; using System.Text.Json.Serialization; +using Luski.Shared.PublicServers.V1.Enums; using Luski.Shared.PublicServers.V1.Shared; namespace Luski.net.JsonTypes; @@ -9,6 +10,15 @@ public class LocalServerInfo [JsonInclude] [JsonPropertyName("alternate_servers")] public ServerData[] AlternateServers { get; set; } = Array.Empty(); + [JsonInclude] + [JsonPropertyName("picture_type")] + public PictureType PictureType { get; set; } + [JsonInclude] + [JsonPropertyName("name")] + public string Name { get; set; } = default!; + [JsonInclude] + [JsonPropertyName("description")] + public string Description { get; set; } = default!; } [JsonSerializable(typeof(LocalServerInfo))] @@ -16,7 +26,4 @@ public class LocalServerInfo GenerationMode = JsonSourceGenerationMode.Default, PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, WriteIndented = false)] -internal partial class LocalServerInfoContext : JsonSerializerContext -{ - -} \ No newline at end of file +internal partial class LocalServerInfoContext : JsonSerializerContext; \ No newline at end of file diff --git a/Luski.net/Luski.net.csproj b/Luski.net/Luski.net.csproj index b4b67ea..7520fae 100755 --- a/Luski.net/Luski.net.csproj +++ b/Luski.net/Luski.net.csproj @@ -13,11 +13,11 @@ https://github.com/JacobTech-com/Luski.net True 1.0.0 - 2.0.0-alpha76 + 2.0.1-alpha15 - + diff --git a/Luski.net/PublicServer.Account.cs b/Luski.net/PublicServer.Account.cs index 6cbc47a..d1353de 100644 --- a/Luski.net/PublicServer.Account.cs +++ b/Luski.net/PublicServer.Account.cs @@ -48,7 +48,7 @@ public partial class PublicServer { EncryptionHandler.GenerateKeys(); } - while (!EncryptionHandler.Generated) { } + while (!EncryptionHandler.Generated) { Thread.Sleep(200); } Console.WriteLine("Encryption 2: " + DateTime.UtcNow.Subtract(dt).ToString("g")); List> FailedSystems = new(); login = true; diff --git a/Luski.net/PublicServer.Incoming.cs b/Luski.net/PublicServer.Incoming.cs index 3c01ab7..d2ba892 100644 --- a/Luski.net/PublicServer.Incoming.cs +++ b/Luski.net/PublicServer.Incoming.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text.Json; +using System.Threading.Tasks; using JacobTechEncryption; using JacobTechEncryption.Enums; using Luski.net.Enums; @@ -8,7 +10,9 @@ using Luski.net.JsonTypes.BaseTypes; using Luski.net.JsonTypes.WSS; using Luski.net.Structures; using Luski.net.Structures.Public; +using Luski.Shared.PublicServers.V1.Enums; using Luski.Shared.PublicServers.V1.ServerToClient.HTTP; +using Luski.Shared.PublicServers.V1.Shared; using WebSocketSharp; using File = System.IO.File; using DataType = Luski.Shared.PublicServers.V1.Enums.DataType; @@ -35,12 +39,108 @@ public partial class PublicServer n.SessionToken); Token = n.Token; break; + case DataType.StatusUpdate: + StatusEvent se = JsonSerializer.Deserialize(data.Data!.ToString()!, StatusEventContext.Default.StatusEvent)!; + SocketUser u; + if (se.User == User.Id) u = User; + else + { + u = GetUser(se.User); + u.Status = se.After; + } + if (StatusUpdate is not null) + { + StatusUpdate.Invoke(se.Before, u); + } + break; + case DataType.Role: + RoleEvent re = JsonSerializer.Deserialize(data.Data!.ToString()!, RoleEventContext.Default.RoleEvent)!; + Role[] ra = roles.Where(s => s.ID == re.ID).ToArray(); + Role r; + bool @new = false; + if (ra.Length > 0) + { + r = ra[0]; + if (re.ServerPermissions is not null) r.ServerPermissions = re.ServerPermissions.Value; + if (re.Description is not null) r.Description = re.Description; + if (re.DisplayName is not null) r.DisplayName = re.DisplayName; + if (re.ColorType is not null) r.ColorType = re.ColorType.Value; + if (re.Color is not null) + { + if (r.ColorType == ColorType.Full) + { + Color nc = new(re.Color); + r.Colors = new []{nc}; + } + else + { + List cols = new(); + for (int i = 0; i < re.Color.Length - 7; i+=8) + { + cols.Add(new(re.Color[i..(i+8)])); + } + r.Colors = cols.ToArray(); + } + } + if (re.Index is not null) r.Index = re.Index.Value; + } + else + { + @new = true; + Task rr = GetRole(re.ID); + rr.Wait(); + r = rr.Result; + } + + if (RoleEvent is not null) _ = RoleEvent.Invoke(@new, r); + break; + case DataType.RoleMember: + RoleMemberEvent rme = JsonSerializer.Deserialize(data.Data!.ToString()!, RoleMemberEventContext.Default.RoleMemberEvent)!; + break; + case DataType.RoleDelete: + break; case DataType.MessageCreate: MessageSTC smsg = JsonSerializer.Deserialize(data.Data!.ToString()!, MessageSTCContext.Default.MessageSTC)!; List fl = new(); - foreach (var VARIABLE in smsg.Files) + List sf = new(); + foreach (ServerFileInfoSTC v in smsg.Files) { - fl.Add(VARIABLE.ID); + if (v.NameKey == 0) + { + if (string.IsNullOrEmpty(v.Name)) + { + v.Name = ""; + } + else v.Name = Encryption.Generic.Encoders[(int)v.NameEncoder] + .GetString(Convert.FromBase64String(v.Name)); + } + else + { + LocalKeyInfo key = EncryptionHandler.GetKey(v.NameKey); + switch (key.EncryptionType) + { + case EncryptionType.RSA: + v.Name = Encryption.RSA.Decrypt(Convert.FromBase64String(v.Name), key.Key, + v.NameEncoder); + break; + default: + v.Name = Encryption.Generic.Encoders[(int)v.NameEncoder] + .GetString(Convert.FromBase64String(v.Name)); + break; + } + } + fl.Add(v.ID); + sf.Add(new() + { + ID = v.ID, + Size = v.Size, + Name = v.Name, + Encoder = v.Encoder, + NameEncoder = v.NameEncoder, + Key = v.Key, + NameKey = v.NameKey, + Server = this + }); } if (smsg.EncryptionKey == 0) @@ -71,13 +171,15 @@ public partial class PublicServer { ID = smsg.ID, AuthorID = smsg.AuthorID, + TimeStamp = smsg.Timestamp, ChannelID = smsg.ChannelID, Context = smsg.Context, EncoderType = smsg.EncoderType, EncryptionKey = smsg.EncryptionKey, FileIDs = fl.ToArray(), Server = this, - IsProfile = smsg.IsProfile + ProfileID = smsg.ProfileID, + _Files = sf }; if (MessageReceived is not null) MessageReceived.Invoke(sm); break; diff --git a/Luski.net/PublicServer.cs b/Luski.net/PublicServer.cs index e2e6b7c..8da6be1 100644 --- a/Luski.net/PublicServer.cs +++ b/Luski.net/PublicServer.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Net.Http; using System.Text; @@ -18,7 +19,6 @@ using Luski.Shared.PublicServers.V1.Enums; using Luski.Shared.PublicServers.V1.ServerToClient.HTTP; using Luski.Shared.PublicServers.V1.Shared; using Role = Luski.net.Structures.Public.Role; -using SocketChannelProfile = Luski.net.Structures.Public.SocketChannelProfile; using SocketUser = Luski.net.Structures.Public.SocketUser; namespace Luski.net; @@ -26,6 +26,11 @@ namespace Luski.net; public partial class PublicServer : Server { public event Func? MessageReceived; + public event Func? StatusUpdate; + public event Func? RoleEvent; + public event Func? RoleDeleted; + public event Func? RoleMemberEvent; + public PictureType PictureType { get; private set; } public List chans { get; } = new(); public List cats { get; } = new(); public List roles { get; } = new(); @@ -37,7 +42,7 @@ public partial class PublicServer : Server base(Domain, API_Version, Secure) { } - internal static async Task GetServer(string Domain, string API_Version, bool Secure = true, bool GenerateEncryption = true, bool LogConsole = false) + internal static async Task GetServer(List Failed, string Domain, string API_Version, bool Secure = true, bool GenerateEncryption = true, bool LogConsole = false) { DateTime dt = DateTime.UtcNow; Console.WriteLine("Connecting to public server '{0}' using API {1}.", Domain, API_Version); @@ -62,8 +67,11 @@ public partial class PublicServer : Server } catch (Exception e) { - LocalServerInfo ServerListing = s.Storage.GetJson(StorageDirectory.ServerInfo, "Servers.json", true, + LocalServerInfo ServerListing = s.Storage.GetJson(StorageDirectory.ServerInfo, "server.json", true, LocalServerInfoContext.Default.LocalServerInfo); + s.Name = ServerListing.Name; + s.Description = ServerListing.Description; + s.PictureType = ServerListing.PictureType; if (ServerListing.AlternateServers.Length > 0) { Console.WriteLine("Failed to connect to public server '{0}' using API {1}. Attempting to connect to alternate servers.", Domain, API_Version); @@ -89,10 +97,23 @@ public partial class PublicServer : Server } } } - - if (si is null) throw; + + if (si is null) + { + Failed.Add(s); + throw; + } } + s.Storage.SetJson(StorageDirectory.ServerInfo, "server.json", new() + { + AlternateServers = Array.Empty(), + PictureType = si.PictureType, + Name = si.Name, + Description = si.Description + }, + LocalServerInfoContext.Default.LocalServerInfo); s.Name = si.Name; + s.PictureType = si.PictureType; s.Description = si.Description; s.wssurl = si.WSSv4Address; s.ServerType = ServerType.Public; @@ -159,6 +180,19 @@ public partial class PublicServer : Server Encryption.AES.Decrypt(Convert.FromBase64String(request.Name), nkey.Key)), _ => Encryption.Generic.Encoders[(short)request.TitleEncoderType].GetString(Convert.FromBase64String(request.Name)) }; + List cols = new(); + if (request.ColorType == ColorType.Full) + { + Color nc = new(request.Color); + cols.Add(nc); + } + else + { + for (int i = 0; i < request.Color.Length - 7; i+=8) + { + cols.Add(new(request.Color[i..(i+8)])); + } + } TCategory bob = new() { @@ -175,7 +209,8 @@ public partial class PublicServer : Server TitleEncoderType = request.TitleEncoderType, TitleEncryptionKey = request.TitleEncryptionKey, Server = this, - Color = new(request.Color) + ColorType = request.ColorType, + Colors = cols.ToArray() }; cats.Add(bob); return bob; @@ -196,15 +231,29 @@ public partial class PublicServer : Server if (r.Length > 0) return r[0]; RoleSTC s = await GetFromServer("SocketRole?id=" + id.ToString(), RoleSTCContext.Default.RoleSTC, CancellationToken.None); + List cols = new(); + if (s.ColorType == ColorType.Full) + { + Color nc = new(s.Color); + cols.Add(nc); + } + else + { + for (int i = 0; i < s.Color.Length - 7; i+=8) + { + cols.Add(new(s.Color[i..(i+8)])); + } + } + Role role = new() { Server = this, ID = s.ID, - Color = new(s.Color), + ColorType = s.ColorType, + Colors = cols.ToArray(), Description = s.Description, DisplayName = s.DisplayName, MembersListID = s.Members, - Name = s.Name, Index = s.Index, ServerPermissions = s.ServerPermissions }; @@ -212,39 +261,35 @@ public partial class PublicServer : Server return role; } - public async Task GetRoleOverride(long id) - { - RoleOverride[] r = roleso.Where(s => s.ID == id).ToArray(); - if (r.Length > 0) return r[0]; - UserRoleOverrideSTC s = await GetFromServer("SocketOverrides/RoleOverride/" + id.ToString(), UserRoleOverrideSTCContext.Default.UserRoleOverrideSTC, CancellationToken.None); - - RoleOverride role = new() - { - ID = s.Id, - BadPermissions = s.BadPermissions, - GoodPermissions = s.GoodPermissions, - ParentRoleID = s.RoleID, - Server = this - }; - roleso.Add(role); - return role; - } - public async Task GetRoles() { RolesSTC s = await GetFromServer("SocketRole/GetAll", RolesSTCContext.Default.RolesSTC, CancellationToken.None); roles.Clear(); foreach (var ServerRole in s.Roles) { + List cols = new(); + if (ServerRole.ColorType == ColorType.Full) + { + Color nc = new(ServerRole.Color); + cols.Add(nc); + } + else + { + for (int i = 0; i < ServerRole.Color.Length - 7; i+=8) + { + cols.Add(new(ServerRole.Color[i..(i+8)])); + } + } + roles.Add(new Role() { Server = this, ID = ServerRole.ID, - Color = new(ServerRole.Color), + ColorType = ServerRole.ColorType, + Colors = cols.ToArray(), Description = ServerRole.Description, DisplayName = ServerRole.DisplayName, MembersListID = ServerRole.Members, - Name = ServerRole.Name, Index = ServerRole.Index, ServerPermissions = ServerRole.ServerPermissions }); @@ -254,7 +299,47 @@ public partial class PublicServer : Server } public async Task SendMessage(TChannel channel, string msg, SocketMessage? ReplyTo = null, - SocketChannelProfile? FakeProfile = null) where TChannel : SocketChannel, new() + ServerProfile? FakeProfile = null, params string[] files) where TChannel : SocketChannel, new() + { + List lll = new(); + + foreach (var f in files) + { + lll.Add(await UploadFile(f)); + } + + return await SendMessage(channel, msg, ReplyTo, FakeProfile, lll.ToArray()); + } + + public async Task UploadFile(string File) + { + FileInfo FI = new FileInfo(File); + ServerFileInfoSTC res = await SendServer("SocketFile", File, ServerFileInfoSTCContext.Default.ServerFileInfoSTC, + CancellationToken.None, + new("name_encoder", "0"), + new("encoder", "0"), + new("name_encryption", "0"), + new("encryption", "0"), + new("name", Convert.ToBase64String(Encoding.UTF8.GetBytes(FI.Name)))); + if (res.ErrorMessage is not null || res.Error is not null) + { + Console.WriteLine("Error {0}: {1}", (res.Error is null ? "Unknown" : res.Error.Value), res.ErrorMessage); + } + return new SocketFile() + { + ID = res.ID, + Encoder = EncoderType.UTF8, + Key = 0, + Name = FI.Name, + NameEncoder = EncoderType.UTF8, + NameKey = 0, + Server = this, + Size = FI.Length + }; + } + + public async Task SendMessage(TChannel channel, string msg, SocketMessage? ReplyTo = null, + ServerProfile? FakeProfile = null, params SocketFile[] files) where TChannel : SocketChannel, new() { string bc = ""; if (channel.EncryptionKeys[0] == 0) @@ -271,17 +356,26 @@ public partial class PublicServer : Server _ => Encryption.AES.Encrypt(Encryption.Generic.Encoders[(int)channel.EncoderTypes[0]].GetBytes(msg), key.Key) }); } + + List lll = new(); + + foreach (var f in files) + { + lll.Add(f.ID); + } + + MessageCTS pcsm = new() { - Files = Array.Empty(), + Files = lll.ToArray(), ChannelID = channel.ID, EncryptionKey = channel.EncryptionKeys[0], Encoding = channel.EncoderTypes[0], - Base64Context = bc + Base64Context = bc, }; if (FakeProfile is not null) { - pcsm.Profile = FakeProfile.Id; + pcsm.Profile = FakeProfile.ID; } @@ -298,16 +392,85 @@ public partial class PublicServer : Server { ID = smsg.ID, AuthorID = smsg.AuthorID, + ProfileID = smsg.ProfileID, ChannelID = channel.ID, + TimeStamp = smsg.Timestamp, Context = msg, EncoderType = smsg.EncoderType, EncryptionKey = smsg.EncryptionKey, FileIDs = fl.ToArray(), Server = channel.Server, - IsProfile = smsg.IsProfile + _Files = files.ToList() }; return sm; } + + public async Task CreateRole(RolePostCTS Req) + { + RoleSTC res = await SendServer("SocketRole", + Req, + RolePostCTSContext.Default.RolePostCTS, + RoleSTCContext.Default.RoleSTC, + CancellationToken.None); + List cols = new(); + if (res.ColorType == ColorType.Full) + { + Color nc = new(res.Color); + cols.Add(nc); + } + else + { + for (int i = 0; i < res.Color.Length - 7; i+=8) + { + cols.Add(new(res.Color[i..(i+8)])); + } + } + + Role role = new Role() + { + DisplayName = res.DisplayName, + ServerPermissions = res.ServerPermissions, + Description = res.Description, + Index = res.Index, + MembersListID = res.Members, + ColorType = res.ColorType, + Colors = cols.ToArray(), + Server = this, + ID = res.ID + }; + roles.Add(role); + return role; + } + + public async Task EditRole(Role role, string? Name = null, string? Description = null, int? Index = null, ServerPermission? Permissions = null, string? Color = null, ColorType? colorType = null) + { + RoleSTC res = await SendServerPatch("SocketRole", + new() + { + ID = role.ID, + Description = Description, + DisplayName = Name, + Index = Index, + ColorType = colorType, + Color = Color, + ServerPermissions = Permissions + }, + RolePatchCTSContext.Default.RolePatchCTS, + RoleSTCContext.Default.RoleSTC, + CancellationToken.None); + if (Permissions is not null) role.ServerPermissions = res.ServerPermissions; + if (Description is not null) role.Description = res.Description; + if (Index is not null) role.Index = res.Index; + if (Name is not null) role.DisplayName = res.DisplayName; + if (Color is not null) role.Colors = new []{new Color(res.Color)}; + if (colorType is not null) role.ColorType = res.ColorType; + return role; + } + + public async Task EditRoleMembers(Role r) + { + return r; + } public async Task GetChannel(long id, CancellationToken CancellationToken) where TChannel : SocketChannel, new() { @@ -365,6 +528,19 @@ public partial class PublicServer : Server Encryption.AES.Decrypt(Convert.FromBase64String(request.Name), nkey.Key)), _ => Encryption.Generic.Encoders[(short)request.TitleEncoderType].GetString(Convert.FromBase64String(request.Name)) }; + List cols = new(); + if (request.ColorType == ColorType.Full) + { + Color nc = new(request.Color); + cols.Add(nc); + } + else + { + for (int i = 0; i < request.Color.Length - 7; i+=8) + { + cols.Add(new(request.Color[i..(i+8)])); + } + } TChannel bob = new() { @@ -384,7 +560,8 @@ public partial class PublicServer : Server TitleEncryptionKey = request.TitleEncryptionKey, PictureType = request.PictureType, Server = this, - Color = new(request.Color) + ColorType = request.ColorType, + Colors = cols.ToArray() }; chans.Add(bob); @@ -415,7 +592,7 @@ public partial class PublicServer : Server Parent = parent.ID, DescriptionEncryptionKey = 0, TitleEncryptionKey = 0, - RoleOverrides = Array.Empty(), + RoleOverrides = Array.Empty(), UserOverrides = Array.Empty(), Type = ChannelType.TextAndVoice, Color = "FFFFFFFF", @@ -424,6 +601,20 @@ public partial class PublicServer : Server ChannelPostCTSContext.Default.ChannelPostCTS, ChannelSTCContext.Default.ChannelSTC, CancellationToken.None); + List cols = new(); + if (res.ColorType == ColorType.Full) + { + Color nc = new(res.Color); + cols.Add(nc); + } + else + { + for (int i = 0; i < res.Color.Length - 7; i+=8) + { + cols.Add(new(res.Color[i..(i+8)])); + } + } + return new SocketChannel() { ID = res.ID, @@ -442,7 +633,8 @@ public partial class PublicServer : Server TitleEncryptionKey = res.TitleEncryptionKey, PictureType = res.PictureType, Server = this, - Color = new(res.Color) + ColorType = res.ColorType, + Colors = cols.ToArray() }; } @@ -488,8 +680,7 @@ public partial class PublicServer : Server { Server = this, Id = user.ID, - DisplayName = user.DisplayName, - PictureType = user.PictureType, + ServerProfile = user.ServerProfile, RoleIds = user.RoleIds, Status = user.Status }; @@ -500,9 +691,8 @@ public partial class PublicServer : Server { Server = this, Id = user.ID, - DisplayName = user.DisplayName, + ServerProfile = user.ServerProfile, SelectedChannel = user.SelectedChannel, - PictureType = user.PictureType, RoleIds = user.RoleIds, Status = user.Status, } as Tuser)!; @@ -511,32 +701,31 @@ public partial class PublicServer : Server return u; } - public async Task GetChannelProfile(long ProfileId, CancellationToken CancellationToken) + public Tuser GetUser(long UserId) where Tuser : SocketUser, new() { - ChannelProfileSTC user; - if (profiles.Count > 0 && profiles.Any(s => s.Id == ProfileId)) + SocketUserSTC user; + if (poeople.Count > 0 && poeople.Any(s => s.Id == UserId)) { - SocketChannelProfile temp = profiles.Where(s => s.Id == ProfileId).FirstOrDefault()!; + Tuser temp = poeople.Where(s => s is Tuser && s.Id == UserId).Cast().FirstOrDefault()!; return temp; } while (true) { if (CanRequest) { - user = await GetFromServer("socketchannelprofile", - ChannelProfileSTCContext.Default.ChannelProfileSTC, - CancellationToken, - new KeyValuePair("id", ProfileId.ToString())); + user = GetFromServerRaw("socketuser", + SocketUserSTCContext.Default.SocketUserSTC, + new KeyValuePair("id", UserId.ToString())); break; } } if (user is null) throw new Exception("Server did not return a user"); - if (profiles.Count > 0 && profiles.Any(s => s.Id == ProfileId)) + if (poeople.Count > 0 && poeople.Any(s => s.Id == UserId)) { - foreach (SocketChannelProfile? p in profiles.Where(s => s.Id == ProfileId)) + foreach (IUser? p in poeople.Where(s => s.Id == UserId)) { - profiles.Remove(p); + poeople.Remove(p); } } if (user is null || user.Error is not null) @@ -546,19 +735,167 @@ public partial class PublicServer : Server throw new Exception($"Something went wrong getting your user information\n{error}"); } - SocketChannelProfile u = new() + Tuser u = new(); + if (typeof(Tuser).FullName == typeof(SocketUser).FullName) + { + u = new() + { + Server = this, + Id = user.ID, + ServerProfile = user.ServerProfile, + RoleIds = user.RoleIds, + Status = user.Status + }; + } + else + { + u = (new SocketAppUser() + { + Server = this, + Id = user.ID, + ServerProfile = user.ServerProfile, + SelectedChannel = user.SelectedChannel, + RoleIds = user.RoleIds, + Status = user.Status, + } as Tuser)!; + } + poeople.Add(u); + return u; + } + + public async Task GetProfile(long ProfileId, CancellationToken CancellationToken) + { + ProfileSTC user; + if (profiles.Count > 0 && profiles.Any(s => s.ID == ProfileId)) + { + ServerProfile temp = profiles.Where(s => s.ID == ProfileId).FirstOrDefault()!; + return temp; + } + while (true) + { + if (CanRequest) + { + user = await GetFromServer("socketprofile", + ChannelProfileSTCContext.Default.ProfileSTC, + CancellationToken, + new KeyValuePair("id", ProfileId.ToString())); + break; + } + } + + if (user is null) throw new Exception("Server did not return a user"); + if (profiles.Count > 0 && profiles.Any(s => s.ID == ProfileId)) + { + _ = profiles.RemoveAll(s => s.ID == ProfileId); + } + if (user is null || user.Error is not null) + { + string error = "User was null"; + if (user is not null && user.Error is not null) error = $"{user.Error}: {user.ErrorMessage}"; + throw new Exception($"Something went wrong getting your user information\n{error}"); + } + + Color[]? colss = null; + if (user.Color is not null) + { + List cols = new(); + if (user.ColorType == ColorType.Full) + { + Color nc = new(user.Color); + cols.Add(nc); + } + else + { + for (int i = 0; i < user.Color.Length - 7; i+=8) + { + cols.Add(new(user.Color[i..(i+8)])); + } + } + colss = cols.ToArray(); + } + + + ServerProfile u = new() { Server = this, - Id = user.ID, + ID = user.ID, DisplayName = user.DisplayName, PictureType = user.PictureType, Controllers = user.Controllers, - Color = new(user.Color) + RoleControllers = user.RoleControllers, + ColorType = user.ColorType, + Color = colss }; - poeople.Add(u); + profiles.Add(u); return u; } + public async Task GetMyProfiles(CancellationToken CancellationToken) + { + ProfileListSTC Prof; + while (true) + { + if (CanRequest) + { + Prof = await GetFromServer("socketprofile/myprofiles", + ProfileListSTCContext.Default.ProfileListSTC, + CancellationToken); + break; + } + } + + if (Prof is null) throw new Exception("Server did not return a Profile List"); + List profiles_ = new(); + foreach (ProfileSTC user in Prof.Profiles) + { + if (profiles.Count > 0 && profiles.Any(s => s.ID == user.ID)) + { + _ = profiles.RemoveAll(s => s.ID == user.ID); + } + if (user is null || user.Error is not null) + { + string error = "Profile was null"; + if (user is not null && user.Error is not null) error = $"{user.Error}: {user.ErrorMessage}"; + throw new Exception($"Something went wrong getting your profile information\n{error}"); + } + + Color[]? colss = null; + if (user.Color is not null) + { + List cols = new(); + if (user.ColorType == ColorType.Full) + { + Color nc = new(user.Color); + cols.Add(nc); + } + else + { + for (int i = 0; i < user.Color.Length - 7; i+=8) + { + cols.Add(new(user.Color[i..(i+8)])); + } + } + colss = cols.ToArray(); + } + + ServerProfile u = new() + { + Server = this, + ID = user.ID, + DisplayName = user.DisplayName, + PictureType = user.PictureType, + Controllers = user.Controllers, + RoleControllers = user.RoleControllers, + ColorType = user.ColorType, + Color = colss + }; + profiles_.Add(u); + profiles.Add(u); + } + + return profiles_.ToArray(); + } + /// /// Sends the server a request to update the of you account /// diff --git a/Luski.net/Server.Globals.cs b/Luski.net/Server.Globals.cs index ef7ddbb..a3f4e04 100644 --- a/Luski.net/Server.Globals.cs +++ b/Luski.net/Server.Globals.cs @@ -23,5 +23,5 @@ public partial class Server public bool IsLogedIn => Token is not null; internal bool CanRequest = false, login = false; internal List poeople = new(); - internal List profiles = new(); + internal List profiles = new(); } \ No newline at end of file diff --git a/Luski.net/Server.Storage.cs b/Luski.net/Server.Storage.cs index 4804315..eba88b6 100644 --- a/Luski.net/Server.Storage.cs +++ b/Luski.net/Server.Storage.cs @@ -32,6 +32,7 @@ public class ServerStorage private static readonly int[] CantDelete = new[] { + (int)StorageDirectory.ServerInfo, (int)StorageDirectory.ChannelKeys, (int)StorageDirectory.ServerKeys, (int)StorageDirectory.StorageInfo @@ -88,6 +89,12 @@ public class ServerStorage 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) { diff --git a/Luski.net/Server.cs b/Luski.net/Server.cs index 441aa11..c67ce58 100644 --- a/Luski.net/Server.cs +++ b/Luski.net/Server.cs @@ -101,6 +101,23 @@ public partial class Server if (temp is null) return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with empty data" }; return temp; } + + public Tresult GetFromServerRaw(string Path, JsonTypeInfo Type, params KeyValuePair[] Headers) where Tresult : STC, new() + { + HttpResponseMessage ServerResponce = GetFromServer(Path, CancellationToken.None, Headers); + Tresult temp = new(); + string raw =""; + try + { + temp = JsonSerializer.Deserialize(ServerResponce.Content.ReadAsStream(), Type)!; + } + catch (Exception e) + { + Console.WriteLine("JSON parse failed for the following data as type {0}\n{1}", temp.GetType(), raw); + } + if (temp is null) return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with empty data" }; + return temp; + } public async Task SendServer(string Path, Tvalue Payload, JsonTypeInfo jsonTypeInfo, JsonTypeInfo ReturnjsonTypeInfo, CancellationToken CancellationToken, params KeyValuePair[] Headers) where Tvalue : CTS where Tresult : STC, new() { diff --git a/Luski.net/Structures/Main/MainSocketTextChannel.cs b/Luski.net/Structures/Main/MainSocketTextChannel.cs index 4674af9..6a2a64b 100755 --- a/Luski.net/Structures/Main/MainSocketTextChannel.cs +++ b/Luski.net/Structures/Main/MainSocketTextChannel.cs @@ -27,8 +27,9 @@ public class MainSocketTextChannel : MainSocketChannel public async Task GetPicture(CancellationToken CancellationToken) { - if (Type == ChannelType.DM) return Members.First().GetAvatar(CancellationToken).Result; - else + throw new NotImplementedException(); + //if (Type == ChannelType.DM) return Members.First().GetAvatar(CancellationToken).Result; + // else { bool isc = System.IO.File.Exists(Server.Storage.GetStorageDirectory(StorageDirectory.ChannelIcons) + Id.ToString()); if (!isc) await Server.GetFromServer($"SocketChannel/GetPicture/{Id}", Server.Storage.GetStorageDirectory(StorageDirectory.ChannelIcons) + Id.ToString(), CancellationToken); diff --git a/Luski.net/Structures/Public/Color.cs b/Luski.net/Structures/Public/Color.cs index 02156c6..1d01bd9 100644 --- a/Luski.net/Structures/Public/Color.cs +++ b/Luski.net/Structures/Public/Color.cs @@ -2,39 +2,59 @@ using System; namespace Luski.net.Structures.Public; -public class Color +public struct Color { public Color(string servercol) { - Bytes = Convert.FromHexString(servercol); + byte[] t = Convert.FromHexString(servercol); + r = t[0]; + g = t[1]; + b = t[2]; + a = t[3]; + } + + public static bool operator ==(Color a, Color b) + { + return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a; + } + + public static bool operator !=(Color a, Color b) + { + return a.r != b.r || a.g != b.g || a.b != b.b || a.a != b.a; } public Color(byte R, byte G, byte B, byte A) { - Bytes = new byte[] {R, G, B, A}; + r = R; + g = G; + b = B; + a = A; } public string ToDatabaseStr() { - return Convert.ToHexString(Bytes); + return Convert.ToHexString(new []{r,g,b,a}); } - private byte[] Bytes; + private byte r; + private byte g; + private byte b; + private byte a; public byte A { - get => Bytes[3]; + get => a; } public byte R { - get => Bytes[0]; + get => r; } public byte G { - get => Bytes[1]; + get => g; } public byte B { - get => Bytes[2]; + get => b; } } \ No newline at end of file diff --git a/Luski.net/Structures/Public/Role.cs b/Luski.net/Structures/Public/Role.cs index 9a8e332..b201e89 100644 --- a/Luski.net/Structures/Public/Role.cs +++ b/Luski.net/Structures/Public/Role.cs @@ -9,18 +9,28 @@ public class Role { public required PublicServer Server { get; init; } = default!; public required long ID { get; init; } = default!; - public required string Name { get; init; } = default!; - public required int Index { get; init; } = default!; - public required Color Color { get; init; } = default!; - public required string Description { get; init; } = default!; - public required string DisplayName { get; init; } = default!; - public required ServerPermission ServerPermissions { get; init; } = default!; - public required long[] MembersListID { get; init; } = default!; + public int Index { get; internal set; } = default!; + public ColorType ColorType { get; internal set; } = ColorType.Full; + public Color[] Colors { get; internal set; } = default!; + public string Description { get; internal set; } = default!; + public string DisplayName { get; internal set; } = default!; + public ServerPermission ServerPermissions { get; internal set; } = default!; + public long[] MLID = default!; + + public long[] MembersListID + { + get => MLID; + internal set + { + MLID = value; + RawUsers = null; + } + } private List? RawUsers = null; public async Task GetMembers() { - if (RawUsers is null) + if (RawUsers is null || RawUsers.Count != MembersListID.Length) { RawUsers = new(); foreach (long m in MembersListID) diff --git a/Luski.net/Structures/Public/RoleOverride.cs b/Luski.net/Structures/Public/RoleOverride.cs index a83cd7a..17f52a3 100644 --- a/Luski.net/Structures/Public/RoleOverride.cs +++ b/Luski.net/Structures/Public/RoleOverride.cs @@ -6,7 +6,6 @@ namespace Luski.net.Structures.Public; public class RoleOverride { - public long ID { get; init; } public long ParentRoleID { get; init; } public ServerPermission GoodPermissions { get; set; } diff --git a/Luski.net/Structures/Public/SocketChannelProfile.cs b/Luski.net/Structures/Public/ServerProfile.cs similarity index 52% rename from Luski.net/Structures/Public/SocketChannelProfile.cs rename to Luski.net/Structures/Public/ServerProfile.cs index 4a32322..2ac1694 100644 --- a/Luski.net/Structures/Public/SocketChannelProfile.cs +++ b/Luski.net/Structures/Public/ServerProfile.cs @@ -3,32 +3,36 @@ using System.IO; using System.Threading; using System.Threading.Tasks; using Luski.net.Enums; -using Luski.net.Interfaces; using Luski.Shared.PublicServers.V1.Enums; namespace Luski.net.Structures.Public; -public class SocketChannelProfile : IUser +public class ServerProfile { public PublicServer Server { get; init; } = default!; - public long Id { get; init; } = default!; + public long ID { get; init; } = default!; public string DisplayName { get; init; } = default!; - public virtual UserStatus Status { get; init; } = UserStatus.Online; - public PictureType PictureType { get; init; } = default!; + public PictureType? PictureType { get; init; } = default!; public long[] Controllers { get; internal set; } = default!; + public long[] RoleControllers { get; internal set; } = default!; + internal ColorType? ColorType { get; set; } = default!; + internal Color[]? Color { get; set; } = default!; - internal Color Color { get; set; } = default!; - - public Task GetColor() + public Task GetColors() { return Task.FromResult(Color); } + public Task GetColorType() + { + return Task.FromResult(ColorType); + } + public async Task GetAvatar(CancellationToken CancellationToken) { - bool isc = System.IO.File.Exists(Server.Storage.GetStorageDirectory(StorageDirectory.ProfileAvatars) + Id.ToString()); - if (!isc) await Server.GetFromServer($"socketchannelprofile/Avatar/{Id}", Server.Storage.GetStorageDirectory(StorageDirectory.ProfileAvatars) + Id.ToString(), CancellationToken); - return Server.Storage.GetResourceStream(StorageDirectory.ProfileAvatars, Id.ToString()); + bool isc = System.IO.File.Exists(Server.Storage.GetStorageDirectory(StorageDirectory.ProfileAvatars) + ID.ToString()); + if (!isc) await Server.GetFromServer($"socketprofile/Avatar/{ID}", Server.Storage.GetStorageDirectory(StorageDirectory.ProfileAvatars) + ID.ToString(), CancellationToken); + return Server.Storage.GetResourceStream(StorageDirectory.ProfileAvatars, ID.ToString()); } public Task GetUserKeys(CancellationToken CancellationToken) diff --git a/Luski.net/Structures/Public/SocketAppUser.cs b/Luski.net/Structures/Public/SocketAppUser.cs index 9300ac6..68cd69c 100755 --- a/Luski.net/Structures/Public/SocketAppUser.cs +++ b/Luski.net/Structures/Public/SocketAppUser.cs @@ -10,12 +10,23 @@ namespace Luski.net.Structures.Public; public class SocketAppUser : SocketUser { public long SelectedChannel { get; init; } = default!; + + private List? _serverProfiles; public async Task GetSelectedChannel(CancellationToken Token) { return await Server.GetChannel(SelectedChannel, Token); } + public async Task GetProfiles(CancellationToken Token) + { + if (_serverProfiles is null) + { + _serverProfiles = (await Server.GetMyProfiles(Token)).ToList(); + } + return _serverProfiles.ToArray(); + } + public async Task HasAccessToCategory(SocketCategory Category, ServerPermission RequiredPerms = ServerPermission.ViewThis) { if (Category.Server != Server) return false; @@ -30,8 +41,10 @@ public class SocketAppUser : SocketUser { if (CatUserOveride.UserID != Id) continue; if ((CatUserOveride.BadPermissions & RequiredPerms) > ServerPermission.None) return false; + if ((CatUserOveride.GoodPermissions & RequiredPerms) == RequiredPerms) return true; GoodPerms |= CatUserOveride.GoodPermissions; + break; } RoleOverride[] CatRoleOverides = await Category.GetRoleOverrides(); diff --git a/Luski.net/Structures/Public/SocketCategory.cs b/Luski.net/Structures/Public/SocketCategory.cs index 630ba13..fd8aef7 100644 --- a/Luski.net/Structures/Public/SocketCategory.cs +++ b/Luski.net/Structures/Public/SocketCategory.cs @@ -3,6 +3,8 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using JacobTechEncryption.Enums; +using Luski.Shared.PublicServers.V1.Enums; +using Luski.Shared.PublicServers.V1.ServerToClient.HTTP; namespace Luski.net.Structures.Public; @@ -10,10 +12,11 @@ public class SocketCategory { public PublicServer Server { get; init; } = default!; public long ID { get; init; } - public Color Color { get; init; } + public ColorType ColorType { get; init; } = ColorType.Full; + public Color[] Colors { get; init; } internal long ParentID { get; set; } - internal long[] RoleOverides { get; set; } - internal long[] UserOverides { get; set; } + internal RoleOverrideSTC[] RoleOverides { get; set; } + internal UserOverrideSTC[] UserOverides { get; set; } internal long[] Channels { get; set; } internal long[] Categories { get; set; } SocketCategory? RawParent = null; diff --git a/Luski.net/Structures/Public/SocketChannel.cs b/Luski.net/Structures/Public/SocketChannel.cs index ad4b9fc..328f757 100644 --- a/Luski.net/Structures/Public/SocketChannel.cs +++ b/Luski.net/Structures/Public/SocketChannel.cs @@ -15,11 +15,12 @@ namespace Luski.net.Structures.Public; public class SocketChannel { public PublicServer Server { get; init; } = default!; - public Color Color { get; init; } + public ColorType ColorType { get; init; } = ColorType.Full; + public Color[] Colors { get; init; } public long ID { get; internal set; } internal long CategoryID { get; set; } - internal long[] RoleOverrides { get; set; } - internal long[] UserOverrides { get; set; } + internal RoleOverrideSTC[] RoleOverrides { get; set; } + internal UserOverrideSTC[] UserOverrides { get; set; } SocketCategory? RawParent = null; List? RawRoleOverides = null; List? RawUserOverides = null; @@ -136,6 +137,7 @@ public class SocketChannel { Server = Server, ID = i.ID, + TimeStamp = i.Timestamp, ChannelID = ID, AuthorID = i.AuthorID, Context = i.Context, @@ -143,7 +145,7 @@ public class SocketChannel EncoderType = i.EncoderType, FileIDs = ff.ToArray(), _Files = sf, - IsProfile = i.IsProfile + ProfileID = i.ProfileID }); } return await Task.FromResult(mmmm.AsReadOnly()); @@ -281,11 +283,12 @@ public class SocketChannel ChannelID = ID, AuthorID = i.AuthorID, Context = i.Context, + TimeStamp = i.Timestamp, EncryptionKey = i.EncryptionKey, EncoderType = i.EncoderType, FileIDs = ff.ToArray(), _Files = sf, - IsProfile = i.IsProfile + ProfileID = i.ProfileID }); } return await Task.FromResult(mmmm.AsReadOnly()); @@ -311,11 +314,19 @@ public class SocketChannel } public async Task SendMessage(string msg, SocketMessage? ReplyTo = null, - SocketChannelProfile? FakeProfile = null) + ServerProfile? Profile = null, params string[] files) { - return await Server.SendMessage(this, msg, ReplyTo, FakeProfile); + return await Server.SendMessage(this, msg, ReplyTo, Profile, files); } + public async Task SendMessage(string msg, SocketMessage? ReplyTo = null, + ServerProfile? Profile = null, params SocketFile[] files) + { + return await Server.SendMessage(this, msg, ReplyTo, Profile, files); + } + + + public async Task GetPicture(CancellationToken CancellationToken) { bool isc = System.IO.File.Exists(Server.Storage.GetStorageDirectory(StorageDirectory.ChannelIcons) + ID.ToString()); @@ -337,18 +348,24 @@ public class SocketChannel public DateTime Epoch { get; internal set; } public string Name { get; internal set; } public string Description { get; internal set; } - public async Task GetRoleOverrides() + public Task GetRoleOverrides() { if (RawRoleOverides is null) { RawRoleOverides = new(); - foreach (long ro in RoleOverrides) + foreach (var ro in RoleOverrides) { - RawRoleOverides.Add(await Server.GetRoleOverride(ro)); + RawRoleOverides.Add(new() + { + Server = this.Server, + ParentRoleID = ro.RoleID, + GoodPermissions = ro.GoodPermissions, + BadPermissions = ro.BadPermissions + }); } } - return RawRoleOverides!.ToArray(); + return Task.FromResult(RawRoleOverides!.ToArray()); } public Task GetUserOverride() { diff --git a/Luski.net/Structures/Public/SocketFile.cs b/Luski.net/Structures/Public/SocketFile.cs index db14fdc..8651e77 100644 --- a/Luski.net/Structures/Public/SocketFile.cs +++ b/Luski.net/Structures/Public/SocketFile.cs @@ -3,6 +3,7 @@ using System.Threading; using System.Threading.Tasks; using JacobTechEncryption.Enums; using Luski.net.Enums; +using Luski.Shared.PublicServers.V1.Enums; namespace Luski.net.Structures.Public; diff --git a/Luski.net/Structures/Public/SocketMessage.cs b/Luski.net/Structures/Public/SocketMessage.cs index fec334c..ac10319 100644 --- a/Luski.net/Structures/Public/SocketMessage.cs +++ b/Luski.net/Structures/Public/SocketMessage.cs @@ -10,17 +10,19 @@ public class SocketMessage { public required PublicServer Server { get; init; } = default!; public required long ID { get; init; } + public required long TimeStamp { get; init; } public required long ChannelID { get; init; } public required long AuthorID { get; init; } + public required long ProfileID { get; init; } public string Context { get; internal set; } public required long EncryptionKey { get; init; } public long[] FileIDs { get; internal set; } - public required bool IsProfile { get; init; } public EncoderType EncoderType { get; internal set; } private SocketChannel? RawParent; private IUser? au; - internal List _Files = new(); - + private ServerProfile? ap; + internal List _Files { get; set; } = new(); + public IReadOnlyList Files { get => _Files.AsReadOnly(); @@ -40,18 +42,21 @@ public class SocketMessage { if (au is null) { - if (IsProfile) - { - au = await Server.GetChannelProfile(AuthorID, token); - } - else - { - if (AuthorID == Server.User.Id) au = Server.User; - else au = await Server.GetUser(AuthorID, token); - } - + if (AuthorID == Server.User.Id) au = Server.User; + else au = await Server.GetUser(AuthorID, token); } return au; } + + public async Task GetProfile(CancellationToken token) + { + if (ap is null) + { + ap = await Server.GetProfile(ProfileID, token); + + } + + return ap; + } } \ No newline at end of file diff --git a/Luski.net/Structures/Public/SocketUser.cs b/Luski.net/Structures/Public/SocketUser.cs index cdee224..1d6d889 100644 --- a/Luski.net/Structures/Public/SocketUser.cs +++ b/Luski.net/Structures/Public/SocketUser.cs @@ -9,6 +9,7 @@ using Luski.net.Interfaces; using Luski.net.JsonTypes; using Luski.Shared.PublicServers.V1.Enums; using Luski.Shared.PublicServers.V1.ServerToClient.HTTP; +using Luski.Shared.PublicServers.V1.Shared; namespace Luski.net.Structures.Public; @@ -16,15 +17,19 @@ public class SocketUser : IUser { public PublicServer Server { get; init; } = default!; public long Id { get; init; } = default!; - public string DisplayName { get; init; } = default!; public virtual UserStatus Status { get; internal set; } = default!; - public PictureType PictureType { get; init; } = default!; + public long ServerProfile { get; init; } public long[] RoleIds { get; init; } = default!; private List? RawRoles = null; - public async Task GetColor() + public async Task GetColorType() { - return (await GetRoles())[0].Color; + return (await GetRoles())[0].ColorType; + } + + public async Task GetColors() + { + return (await GetRoles())[0].Colors; } public async Task GetRoles() @@ -49,6 +54,19 @@ public class SocketUser : IUser if (!isc) await Server.GetFromServer($"socketuserprofile/Avatar/{Id}", Server.Storage.GetStorageDirectory(StorageDirectory.Avatars) + Id.ToString(), CancellationToken); return Server.Storage.GetResourceStream(StorageDirectory.Avatars, Id.ToString()); } + + public async Task HasPermissions(ServerPermission RequiredPerms) + { + if (Server.OwnerID == Id) return true; + Role[] UserRoleIDList = await GetRoles(); + ServerPermission op = ServerPermission.None; + foreach (Role RoleID in UserRoleIDList) + { + op |= RoleID.ServerPermissions; + } + + return op.HasPermission(RequiredPerms); + } public Task GetUserKeys(CancellationToken CancellationToken) { From f950957b76c7924a2de2ce1a48de491daa4f9eb0 Mon Sep 17 00:00:00 2001 From: JacobTech Date: Mon, 18 Nov 2024 23:24:23 -0500 Subject: [PATCH 14/14] Project --- Luski.net/Enums/ConnectionStatus.cs | 10 ++ Luski.net/Luski.net.csproj | 4 +- Luski.net/PublicServer.Account.cs | 2 + Luski.net/PublicServer.cs | 100 ++---------------- Luski.net/Server.Globals.cs | 1 + .../Structures/Main/MainSocketUserBase.cs | 6 +- Luski.net/Structures/Public/ServerProfile.cs | 12 --- Luski.net/Structures/Public/SocketCategory.cs | 2 - Luski.net/Structures/Public/SocketChannel.cs | 2 - Luski.net/Structures/Public/SocketUser.cs | 10 -- 10 files changed, 23 insertions(+), 126 deletions(-) create mode 100644 Luski.net/Enums/ConnectionStatus.cs diff --git a/Luski.net/Enums/ConnectionStatus.cs b/Luski.net/Enums/ConnectionStatus.cs new file mode 100644 index 0000000..bf6993f --- /dev/null +++ b/Luski.net/Enums/ConnectionStatus.cs @@ -0,0 +1,10 @@ +namespace Luski.net.Enums; + +public enum ConnectionStatus +{ + FailedToConnect, + CommunicationError, + FailedToLogin, + ConnectedNotLoggedIn, + LoggedIn, +} \ No newline at end of file diff --git a/Luski.net/Luski.net.csproj b/Luski.net/Luski.net.csproj index 7520fae..485e5cb 100755 --- a/Luski.net/Luski.net.csproj +++ b/Luski.net/Luski.net.csproj @@ -13,11 +13,11 @@ https://github.com/JacobTech-com/Luski.net True 1.0.0 - 2.0.1-alpha15 + 2.0.1-alpha18 - + diff --git a/Luski.net/PublicServer.Account.cs b/Luski.net/PublicServer.Account.cs index d1353de..7af40df 100644 --- a/Luski.net/PublicServer.Account.cs +++ b/Luski.net/PublicServer.Account.cs @@ -88,6 +88,7 @@ public partial class PublicServer Console.WriteLine("Account Result: " + DateTime.UtcNow.Subtract(dt).ToString("g")); if (json.Error is not null) { + ConnectionStatus = ConnectionStatus.FailedToLogin; Console.WriteLine(json.ErrorMessage); return false; throw new Exception($"Luski appears to be down at the current moment: {json.ErrorMessage}"); @@ -121,6 +122,7 @@ public partial class PublicServer Token.Split('.')[0] ))); User = await GetUser(id, CancellationToken); + ConnectionStatus = ConnectionStatus.LoggedIn; User.Username = Username; Console.WriteLine("Get our info: " + DateTime.UtcNow.Subtract(dt).ToString("g")); diff --git a/Luski.net/PublicServer.cs b/Luski.net/PublicServer.cs index 8da6be1..436fabd 100644 --- a/Luski.net/PublicServer.cs +++ b/Luski.net/PublicServer.cs @@ -101,9 +101,12 @@ public partial class PublicServer : Server if (si is null) { Failed.Add(s); + s.ConnectionStatus = ConnectionStatus.FailedToConnect; throw; } } + + s.ConnectionStatus = ConnectionStatus.ConnectedNotLoggedIn; s.Storage.SetJson(StorageDirectory.ServerInfo, "server.json", new() { AlternateServers = Array.Empty(), @@ -180,19 +183,6 @@ public partial class PublicServer : Server Encryption.AES.Decrypt(Convert.FromBase64String(request.Name), nkey.Key)), _ => Encryption.Generic.Encoders[(short)request.TitleEncoderType].GetString(Convert.FromBase64String(request.Name)) }; - List cols = new(); - if (request.ColorType == ColorType.Full) - { - Color nc = new(request.Color); - cols.Add(nc); - } - else - { - for (int i = 0; i < request.Color.Length - 7; i+=8) - { - cols.Add(new(request.Color[i..(i+8)])); - } - } TCategory bob = new() { @@ -208,9 +198,7 @@ public partial class PublicServer : Server UserOverides = request.UserOverrides, TitleEncoderType = request.TitleEncoderType, TitleEncryptionKey = request.TitleEncryptionKey, - Server = this, - ColorType = request.ColorType, - Colors = cols.ToArray() + Server = this }; cats.Add(bob); return bob; @@ -528,19 +516,6 @@ public partial class PublicServer : Server Encryption.AES.Decrypt(Convert.FromBase64String(request.Name), nkey.Key)), _ => Encryption.Generic.Encoders[(short)request.TitleEncoderType].GetString(Convert.FromBase64String(request.Name)) }; - List cols = new(); - if (request.ColorType == ColorType.Full) - { - Color nc = new(request.Color); - cols.Add(nc); - } - else - { - for (int i = 0; i < request.Color.Length - 7; i+=8) - { - cols.Add(new(request.Color[i..(i+8)])); - } - } TChannel bob = new() { @@ -560,8 +535,6 @@ public partial class PublicServer : Server TitleEncryptionKey = request.TitleEncryptionKey, PictureType = request.PictureType, Server = this, - ColorType = request.ColorType, - Colors = cols.ToArray() }; chans.Add(bob); @@ -595,25 +568,11 @@ public partial class PublicServer : Server RoleOverrides = Array.Empty(), UserOverrides = Array.Empty(), Type = ChannelType.TextAndVoice, - Color = "FFFFFFFF", PictureType = PictureType.none }, ChannelPostCTSContext.Default.ChannelPostCTS, ChannelSTCContext.Default.ChannelSTC, CancellationToken.None); - List cols = new(); - if (res.ColorType == ColorType.Full) - { - Color nc = new(res.Color); - cols.Add(nc); - } - else - { - for (int i = 0; i < res.Color.Length - 7; i+=8) - { - cols.Add(new(res.Color[i..(i+8)])); - } - } return new SocketChannel() { @@ -632,9 +591,7 @@ public partial class PublicServer : Server TitleEncoderType = res.TitleEncoderType, TitleEncryptionKey = res.TitleEncryptionKey, PictureType = res.PictureType, - Server = this, - ColorType = res.ColorType, - Colors = cols.ToArray() + Server = this }; } @@ -795,26 +752,6 @@ public partial class PublicServer : Server throw new Exception($"Something went wrong getting your user information\n{error}"); } - Color[]? colss = null; - if (user.Color is not null) - { - List cols = new(); - if (user.ColorType == ColorType.Full) - { - Color nc = new(user.Color); - cols.Add(nc); - } - else - { - for (int i = 0; i < user.Color.Length - 7; i+=8) - { - cols.Add(new(user.Color[i..(i+8)])); - } - } - colss = cols.ToArray(); - } - - ServerProfile u = new() { Server = this, @@ -822,9 +759,7 @@ public partial class PublicServer : Server DisplayName = user.DisplayName, PictureType = user.PictureType, Controllers = user.Controllers, - RoleControllers = user.RoleControllers, - ColorType = user.ColorType, - Color = colss + RoleControllers = user.RoleControllers }; profiles.Add(u); return u; @@ -859,25 +794,6 @@ public partial class PublicServer : Server throw new Exception($"Something went wrong getting your profile information\n{error}"); } - Color[]? colss = null; - if (user.Color is not null) - { - List cols = new(); - if (user.ColorType == ColorType.Full) - { - Color nc = new(user.Color); - cols.Add(nc); - } - else - { - for (int i = 0; i < user.Color.Length - 7; i+=8) - { - cols.Add(new(user.Color[i..(i+8)])); - } - } - colss = cols.ToArray(); - } - ServerProfile u = new() { Server = this, @@ -885,9 +801,7 @@ public partial class PublicServer : Server DisplayName = user.DisplayName, PictureType = user.PictureType, Controllers = user.Controllers, - RoleControllers = user.RoleControllers, - ColorType = user.ColorType, - Color = colss + RoleControllers = user.RoleControllers }; profiles_.Add(u); profiles.Add(u); diff --git a/Luski.net/Server.Globals.cs b/Luski.net/Server.Globals.cs index a3f4e04..5776ad3 100644 --- a/Luski.net/Server.Globals.cs +++ b/Luski.net/Server.Globals.cs @@ -24,4 +24,5 @@ public partial class Server internal bool CanRequest = false, login = false; internal List poeople = new(); internal List profiles = new(); + public ConnectionStatus ConnectionStatus { get; protected set; } = ConnectionStatus.FailedToConnect; } \ No newline at end of file diff --git a/Luski.net/Structures/Main/MainSocketUserBase.cs b/Luski.net/Structures/Main/MainSocketUserBase.cs index 1f6fbb6..4183825 100644 --- a/Luski.net/Structures/Main/MainSocketUserBase.cs +++ b/Luski.net/Structures/Main/MainSocketUserBase.cs @@ -26,11 +26,7 @@ public class MainSocketUserBase : STC, IUser [JsonPropertyName("picture_type")] [JsonInclude] public PictureType PictureType { get; internal set; } = default!; - - public Task GetColor() - { - throw new NotImplementedException(); - } + public async Task GetAvatar(CancellationToken CancellationToken) { bool isc = System.IO.File.Exists(Server.Storage.GetStorageDirectory(StorageDirectory.Avatars) + Id.ToString()); diff --git a/Luski.net/Structures/Public/ServerProfile.cs b/Luski.net/Structures/Public/ServerProfile.cs index 2ac1694..92af256 100644 --- a/Luski.net/Structures/Public/ServerProfile.cs +++ b/Luski.net/Structures/Public/ServerProfile.cs @@ -15,18 +15,6 @@ public class ServerProfile public PictureType? PictureType { get; init; } = default!; public long[] Controllers { get; internal set; } = default!; public long[] RoleControllers { get; internal set; } = default!; - internal ColorType? ColorType { get; set; } = default!; - internal Color[]? Color { get; set; } = default!; - - public Task GetColors() - { - return Task.FromResult(Color); - } - - public Task GetColorType() - { - return Task.FromResult(ColorType); - } public async Task GetAvatar(CancellationToken CancellationToken) { diff --git a/Luski.net/Structures/Public/SocketCategory.cs b/Luski.net/Structures/Public/SocketCategory.cs index fd8aef7..9f49dd5 100644 --- a/Luski.net/Structures/Public/SocketCategory.cs +++ b/Luski.net/Structures/Public/SocketCategory.cs @@ -12,8 +12,6 @@ public class SocketCategory { public PublicServer Server { get; init; } = default!; public long ID { get; init; } - public ColorType ColorType { get; init; } = ColorType.Full; - public Color[] Colors { get; init; } internal long ParentID { get; set; } internal RoleOverrideSTC[] RoleOverides { get; set; } internal UserOverrideSTC[] UserOverides { get; set; } diff --git a/Luski.net/Structures/Public/SocketChannel.cs b/Luski.net/Structures/Public/SocketChannel.cs index 328f757..301a027 100644 --- a/Luski.net/Structures/Public/SocketChannel.cs +++ b/Luski.net/Structures/Public/SocketChannel.cs @@ -15,8 +15,6 @@ namespace Luski.net.Structures.Public; public class SocketChannel { public PublicServer Server { get; init; } = default!; - public ColorType ColorType { get; init; } = ColorType.Full; - public Color[] Colors { get; init; } public long ID { get; internal set; } internal long CategoryID { get; set; } internal RoleOverrideSTC[] RoleOverrides { get; set; } diff --git a/Luski.net/Structures/Public/SocketUser.cs b/Luski.net/Structures/Public/SocketUser.cs index 1d6d889..4e705d8 100644 --- a/Luski.net/Structures/Public/SocketUser.cs +++ b/Luski.net/Structures/Public/SocketUser.cs @@ -22,16 +22,6 @@ public class SocketUser : IUser public long[] RoleIds { get; init; } = default!; private List? RawRoles = null; - public async Task GetColorType() - { - return (await GetRoles())[0].ColorType; - } - - public async Task GetColors() - { - return (await GetRoles())[0].Colors; - } - public async Task GetRoles() { if (RawRoles is null)