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