using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http; using System.Text; using System.Threading; using System.Threading.Tasks; using JacobTechEncryption; using JacobTechEncryption.Enums; using Luski.net.Enums; using Luski.net.Interfaces; using Luski.net.JsonTypes; using Luski.net.JsonTypes.HTTP; using Luski.net.Structures; using Luski.net.Structures.Public; using Luski.Shared.PublicServers.V1.ClientToServer.HTTP; using Luski.Shared.PublicServers.V1.Enums; using Luski.Shared.PublicServers.V1.ServerToClient.HTTP; using Luski.Shared.PublicServers.V1.Shared; using Role = Luski.net.Structures.Public.Role; using SocketUser = Luski.net.Structures.Public.SocketUser; namespace Luski.net; public partial class PublicServer : Server { public event Func? MessageReceived; public event Func? StatusUpdate; public event Func? RoleEvent; public event Func? RoleDeleted; public event Func? RoleMemberEvent; public PictureType PictureType { get; private set; } public List chans { get; } = new(); public List cats { get; } = new(); public List roles { get; } = new(); public List roleso { get; } = new(); public SocketAppUser User { get; private set; } = null!; private PublicServer(string Domain, string API_Version, bool Secure = true) : base(Domain, API_Version, Secure) { } internal static async Task GetServer(List Failed, string Domain, string API_Version, bool Secure = true, bool GenerateEncryption = true, bool LogConsole = false) { DateTime dt = DateTime.UtcNow; Console.WriteLine("Connecting to public server '{0}' using API {1}.", Domain, API_Version); PublicServer s = new(Domain, API_Version, Secure); s.PrintServerMessages = LogConsole; if (GenerateEncryption) { Thread t = new(_ => { s.EncryptionHandler.GenerateKeys(); }); t.Start(); } ServerInfoSTC? si = null; try { si = await s.GetFromServer("socketserver", ServerInfoSTCContext.Default.ServerInfoSTC, CancellationToken.None); s.EncryptionHandler.ServerPublicKey = await (await new HttpClient() .GetAsync($"{(s.Secure ? "https" : "http")}://{s.Domain}/{s.ApiVersion}/Keys/PublicKey")) .Content .ReadAsStringAsync(); } catch (Exception e) { LocalServerInfo ServerListing = s.Storage.GetJson(StorageDirectory.ServerInfo, "server.json", true, LocalServerInfoContext.Default.LocalServerInfo); s.Name = ServerListing.Name; s.Description = ServerListing.Description; s.PictureType = ServerListing.PictureType; if (ServerListing.AlternateServers.Length > 0) { Console.WriteLine("Failed to connect to public server '{0}' using API {1}. Attempting to connect to alternate servers.", Domain, API_Version); foreach (ServerData Server in ServerListing.AlternateServers) { s.Secure = Server.Secure; s.Domain = Server.DomainAndPort; try { si = await s.GetFromServer("socketserver", ServerInfoSTCContext.Default.ServerInfoSTC, CancellationToken.None); s.EncryptionHandler.ServerPublicKey = await (await new HttpClient() .GetAsync($"{(s.Secure ? "https" : "http")}://{s.Domain}/{s.ApiVersion}/Keys/PublicKey")) .Content .ReadAsStringAsync(); Console.WriteLine("Public server '{0}' connection restored by alternate server '{1}' using API {2}.", Domain, s.Domain, API_Version); break; } catch { // ignored } } } if (si is null) { Failed.Add(s); s.ConnectionStatus = ConnectionStatus.FailedToConnect; throw; } } s.ConnectionStatus = ConnectionStatus.ConnectedNotLoggedIn; s.Storage.SetJson(StorageDirectory.ServerInfo, "server.json", new() { AlternateServers = Array.Empty(), PictureType = si.PictureType, Name = si.Name, Description = si.Description }, LocalServerInfoContext.Default.LocalServerInfo); s.Name = si.Name; s.PictureType = si.PictureType; s.Description = si.Description; s.wssurl = si.WSSv4Address; s.ServerType = ServerType.Public; s.OwnerID = si.Owner; Console.WriteLine("Connected to public server '{0}' using API {1} in {4}.\nServer Name: {2}\nServer Description: {3}", Domain, API_Version, s.Name, s.Description, DateTime.UtcNow.Subtract(dt).ToString("g")); return s; } public long OwnerID { get; private set; } = 0; public async Task GetCategory(long id, CancellationToken CancellationToken) where TCategory : SocketCategory, new() { CategorySTC request; if (cats.Count > 0 && cats.Any(s => s.ID == id)) { return (cats.Where(s => s is TCategory && s.ID == id).First() as TCategory)!; } while (true) { if (CanRequest) { request = await GetFromServer($"SocketCategory", CategorySTCContext.Default.CategorySTC, 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; if (request.DescriptionEncryptionKey != 0) deckey = EncryptionHandler.GetKey(request.DescriptionEncryptionKey); else deckey = new() { EncryptionType = EncryptionType.None, Key = string.Empty }; string dec = deckey.EncryptionType switch { EncryptionType.RSA => Encryption.RSA.Decrypt(Convert.FromBase64String(request.Description), deckey.Key, request.DescriptionEncoderType), 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; if (request.TitleEncryptionKey != 0) nkey = EncryptionHandler.GetKey(request.TitleEncryptionKey); else nkey = new() { EncryptionType = EncryptionType.None, Key = string.Empty }; string n = nkey.EncryptionType switch { EncryptionType.RSA => Encryption.RSA.Decrypt(Convert.FromBase64String(request.Name), nkey.Key, request.TitleEncoderType), 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.RoleOverrides, UserOverides = request.UserOverrides, TitleEncoderType = request.TitleEncoderType, TitleEncryptionKey = request.TitleEncryptionKey, Server = this }; 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]; RoleSTC s = await GetFromServer("SocketRole?id=" + id.ToString(), RoleSTCContext.Default.RoleSTC, CancellationToken.None); List cols = new(); if (s.ColorType == ColorType.Full) { Color nc = new(s.Color); cols.Add(nc); } else { for (int i = 0; i < s.Color.Length - 7; i+=8) { cols.Add(new(s.Color[i..(i+8)])); } } Role role = new() { Server = this, ID = s.ID, ColorType = s.ColorType, Colors = cols.ToArray(), Description = s.Description, DisplayName = s.DisplayName, MembersListID = s.Members, Index = s.Index, ServerPermissions = s.ServerPermissions }; roles.Add(role); return role; } public async Task GetRoles() { RolesSTC s = await GetFromServer("SocketRole/GetAll", RolesSTCContext.Default.RolesSTC, CancellationToken.None); roles.Clear(); foreach (var ServerRole in s.Roles) { List cols = new(); if (ServerRole.ColorType == ColorType.Full) { Color nc = new(ServerRole.Color); cols.Add(nc); } else { for (int i = 0; i < ServerRole.Color.Length - 7; i+=8) { cols.Add(new(ServerRole.Color[i..(i+8)])); } } roles.Add(new Role() { Server = this, ID = ServerRole.ID, ColorType = ServerRole.ColorType, Colors = cols.ToArray(), Description = ServerRole.Description, DisplayName = ServerRole.DisplayName, MembersListID = ServerRole.Members, Index = ServerRole.Index, ServerPermissions = ServerRole.ServerPermissions }); } return roles.ToArray(); } public async Task SendMessage(TChannel channel, string msg, SocketMessage? ReplyTo = null, ServerProfile? FakeProfile = null, params string[] files) where TChannel : SocketChannel, new() { List lll = new(); foreach (var f in files) { lll.Add(await UploadFile(f)); } return await SendMessage(channel, msg, ReplyTo, FakeProfile, lll.ToArray()); } public async Task UploadFile(string File) { FileInfo FI = new FileInfo(File); ServerFileInfoSTC res = await SendServer("SocketFile", File, ServerFileInfoSTCContext.Default.ServerFileInfoSTC, CancellationToken.None, new("name_encoder", "0"), new("encoder", "0"), new("name_encryption", "0"), new("encryption", "0"), new("name", Convert.ToBase64String(Encoding.UTF8.GetBytes(FI.Name)))); if (res.ErrorMessage is not null || res.Error is not null) { Console.WriteLine("Error {0}: {1}", (res.Error is null ? "Unknown" : res.Error.Value), res.ErrorMessage); } return new SocketFile() { ID = res.ID, Encoder = EncoderType.UTF8, Key = 0, Name = FI.Name, NameEncoder = EncoderType.UTF8, NameKey = 0, Server = this, Size = FI.Length }; } public async Task SendMessage(TChannel channel, string msg, SocketMessage? ReplyTo = null, ServerProfile? FakeProfile = null, params SocketFile[] files) where TChannel : SocketChannel, new() { string bc = ""; if (channel.EncryptionKeys[0] == 0) { if (!string.IsNullOrEmpty(msg)) bc = Convert.ToBase64String(Encryption.Generic.Encoders[(int)channel.EncoderTypes[0]].GetBytes(msg)); } else { LocalKeyInfo key = channel.Server.EncryptionHandler.GetKey(channel.EncryptionKeys[0]); bc = Convert.ToBase64String(key.EncryptionType switch { EncryptionType.RSA => Encryption.RSA.Encrypt(msg, key.Key, channel.EncoderTypes[0]), _ => Encryption.AES.Encrypt(Encryption.Generic.Encoders[(int)channel.EncoderTypes[0]].GetBytes(msg), key.Key) }); } List lll = new(); foreach (var f in files) { lll.Add(f.ID); } MessageCTS pcsm = new() { Files = lll.ToArray(), ChannelID = channel.ID, EncryptionKey = channel.EncryptionKeys[0], Encoding = channel.EncoderTypes[0], Base64Context = bc, }; if (FakeProfile is not null) { pcsm.Profile = FakeProfile.ID; } MessageSTC smsg = await channel.Server.SendServer("socketmessage", pcsm, MessageCTSContext.Default.MessageCTS, MessageSTCContext.Default.MessageSTC, CancellationToken.None); List fl = new(); foreach (var VARIABLE in smsg.Files) { fl.Add(VARIABLE.ID); } SocketMessage sm = new() { ID = smsg.ID, AuthorID = smsg.AuthorID, ProfileID = smsg.ProfileID, ChannelID = channel.ID, TimeStamp = smsg.Timestamp, Context = msg, EncoderType = smsg.EncoderType, EncryptionKey = smsg.EncryptionKey, FileIDs = fl.ToArray(), Server = channel.Server, _Files = files.ToList() }; return sm; } public async Task CreateRole(RolePostCTS Req) { RoleSTC res = await SendServer("SocketRole", Req, RolePostCTSContext.Default.RolePostCTS, RoleSTCContext.Default.RoleSTC, CancellationToken.None); List cols = new(); if (res.ColorType == ColorType.Full) { Color nc = new(res.Color); cols.Add(nc); } else { for (int i = 0; i < res.Color.Length - 7; i+=8) { cols.Add(new(res.Color[i..(i+8)])); } } Role role = new Role() { DisplayName = res.DisplayName, ServerPermissions = res.ServerPermissions, Description = res.Description, Index = res.Index, MembersListID = res.Members, ColorType = res.ColorType, Colors = cols.ToArray(), Server = this, ID = res.ID }; roles.Add(role); return role; } public async Task EditRole(Role role, string? Name = null, string? Description = null, int? Index = null, ServerPermission? Permissions = null, string? Color = null, ColorType? colorType = null) { RoleSTC res = await SendServerPatch("SocketRole", new() { ID = role.ID, Description = Description, DisplayName = Name, Index = Index, ColorType = colorType, Color = Color, ServerPermissions = Permissions }, RolePatchCTSContext.Default.RolePatchCTS, RoleSTCContext.Default.RoleSTC, CancellationToken.None); if (Permissions is not null) role.ServerPermissions = res.ServerPermissions; if (Description is not null) role.Description = res.Description; if (Index is not null) role.Index = res.Index; if (Name is not null) role.DisplayName = res.DisplayName; if (Color is not null) role.Colors = new []{new Color(res.Color)}; if (colorType is not null) role.ColorType = res.ColorType; return role; } public async Task EditRoleMembers(Role r) { return r; } public async Task GetChannel(long id, CancellationToken CancellationToken) where TChannel : SocketChannel, new() { ChannelSTC request; if (chans.Count > 0 && chans.Any(s => s.ID == id)) { return (chans.Where(s => s is TChannel && s.ID == id).First() as TChannel)!; } while (true) { if (CanRequest) { request = await GetFromServer($"SocketChannel", ChannelSTCContext.Default.ChannelSTC, 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; if (request.DescriptionEncryptionKey != 0) deckey = EncryptionHandler.GetKey(request.DescriptionEncryptionKey); else deckey = new() { EncryptionType = EncryptionType.None, Key = string.Empty }; string dec = deckey.EncryptionType switch { EncryptionType.RSA => Encryption.RSA.Decrypt(Convert.FromBase64String(request.Description), deckey.Key, request.DescriptionEncoderType), 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; if (request.TitleEncryptionKey != 0) nkey = EncryptionHandler.GetKey(request.TitleEncryptionKey); else nkey = new() { EncryptionType = EncryptionType.None, Key = string.Empty }; string n = nkey.EncryptionType switch { EncryptionType.RSA => Encryption.RSA.Decrypt(Convert.FromBase64String(request.Name), nkey.Key, request.TitleEncoderType), 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, RoleOverrides = request.RoleOverrides, UserOverrides = request.UserOverrides, Type = request.Type, TitleEncoderType = request.TitleEncoderType, TitleEncryptionKey = request.TitleEncryptionKey, PictureType = request.PictureType, Server = this, }; 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 MakeChannel(SocketCategory parent, string Name, string Decription) { ChannelSTC res = await SendServer( "SocketChannel", new ChannelPostCTS() { Name = Convert.ToBase64String(Encoding.UTF8.GetBytes(Name)), Description = Convert.ToBase64String(Encoding.UTF8.GetBytes(Decription)), EncoderTypes = new[] { EncoderType.UTF16 }, EncryptionKeys = new long[] { 0 }, DescriptionEncoderType = EncoderType.UTF8, TitleEncoderType = EncoderType.UTF8, Parent = parent.ID, DescriptionEncryptionKey = 0, TitleEncryptionKey = 0, RoleOverrides = Array.Empty(), UserOverrides = Array.Empty(), Type = ChannelType.TextAndVoice, PictureType = PictureType.none }, ChannelPostCTSContext.Default.ChannelPostCTS, ChannelSTCContext.Default.ChannelSTC, CancellationToken.None); return new SocketChannel() { ID = res.ID, CategoryID = res.Parent, Description = Decription, DescriptionEncoderType = res.DescriptionEncoderType, DescriptionEncryptionKey = res.DescriptionEncryptionKey, EncoderTypes = res.EncoderTypes, EncryptionKeys = res.EncryptionKeys, Epoch = res.Epoch, Name = Name, RoleOverrides = res.RoleOverrides, UserOverrides = res.UserOverrides, Type = res.Type, TitleEncoderType = res.TitleEncoderType, TitleEncryptionKey = res.TitleEncryptionKey, PictureType = res.PictureType, Server = this }; } public async Task GetUser(long UserId, CancellationToken CancellationToken) where Tuser : SocketUser, new() { SocketUserSTC user; if (poeople.Count > 0 && poeople.Any(s => s.Id == UserId)) { Tuser temp = poeople.Where(s => s is Tuser && s.Id == UserId).Cast().FirstOrDefault()!; return temp; } while (true) { if (CanRequest) { user = await GetFromServer("socketuser", SocketUserSTCContext.Default.SocketUserSTC, 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 (typeof(Tuser).FullName == typeof(SocketUser).FullName) { u = new() { Server = this, Id = user.ID, ServerProfile = user.ServerProfile, RoleIds = user.RoleIds, Status = user.Status }; } else { u = (new SocketAppUser() { Server = this, Id = user.ID, ServerProfile = user.ServerProfile, SelectedChannel = user.SelectedChannel, RoleIds = user.RoleIds, Status = user.Status, } as Tuser)!; } poeople.Add(u); return u; } public Tuser GetUser(long UserId) where Tuser : SocketUser, new() { SocketUserSTC user; if (poeople.Count > 0 && poeople.Any(s => s.Id == UserId)) { Tuser temp = poeople.Where(s => s is Tuser && s.Id == UserId).Cast().FirstOrDefault()!; return temp; } while (true) { if (CanRequest) { user = GetFromServerRaw("socketuser", SocketUserSTCContext.Default.SocketUserSTC, new KeyValuePair("id", UserId.ToString())); break; } } if (user is null) throw new Exception("Server did not return a user"); if (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 (typeof(Tuser).FullName == typeof(SocketUser).FullName) { u = new() { Server = this, Id = user.ID, ServerProfile = user.ServerProfile, RoleIds = user.RoleIds, Status = user.Status }; } else { u = (new SocketAppUser() { Server = this, Id = user.ID, ServerProfile = user.ServerProfile, SelectedChannel = user.SelectedChannel, RoleIds = user.RoleIds, Status = user.Status, } as Tuser)!; } poeople.Add(u); return u; } public async Task GetProfile(long ProfileId, CancellationToken CancellationToken) { ProfileSTC user; if (profiles.Count > 0 && profiles.Any(s => s.ID == ProfileId)) { ServerProfile temp = profiles.Where(s => s.ID == ProfileId).FirstOrDefault()!; return temp; } while (true) { if (CanRequest) { user = await GetFromServer("socketprofile", ChannelProfileSTCContext.Default.ProfileSTC, CancellationToken, new KeyValuePair("id", ProfileId.ToString())); break; } } if (user is null) throw new Exception("Server did not return a user"); if (profiles.Count > 0 && profiles.Any(s => s.ID == ProfileId)) { _ = profiles.RemoveAll(s => s.ID == ProfileId); } if (user is null || user.Error is not null) { string error = "User was null"; if (user is not null && user.Error is not null) error = $"{user.Error}: {user.ErrorMessage}"; throw new Exception($"Something went wrong getting your user information\n{error}"); } ServerProfile u = new() { Server = this, ID = user.ID, DisplayName = user.DisplayName, PictureType = user.PictureType, Controllers = user.Controllers, RoleControllers = user.RoleControllers }; profiles.Add(u); return u; } public async Task GetMyProfiles(CancellationToken CancellationToken) { ProfileListSTC Prof; while (true) { if (CanRequest) { Prof = await GetFromServer("socketprofile/myprofiles", ProfileListSTCContext.Default.ProfileListSTC, CancellationToken); break; } } if (Prof is null) throw new Exception("Server did not return a Profile List"); List profiles_ = new(); foreach (ProfileSTC user in Prof.Profiles) { if (profiles.Count > 0 && profiles.Any(s => s.ID == user.ID)) { _ = profiles.RemoveAll(s => s.ID == user.ID); } if (user is null || user.Error is not null) { string error = "Profile was null"; if (user is not null && user.Error is not null) error = $"{user.Error}: {user.ErrorMessage}"; throw new Exception($"Something went wrong getting your profile information\n{error}"); } ServerProfile u = new() { Server = this, ID = user.ID, DisplayName = user.DisplayName, PictureType = user.PictureType, Controllers = user.Controllers, RoleControllers = user.RoleControllers }; profiles_.Add(u); profiles.Add(u); } return profiles_.ToArray(); } /// /// 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) { STC? data = await SendServer("SocketUserProfile/Status", new StatusUpdateCTS() { Status = Status }, StatusUpdateCTSContext.Default.StatusUpdateCTS, STCContext.Default.STC, CancellationToken); if (data.Error is not null && ((int)data.StatusCode < 200 || (int)data.StatusCode > 299)) { if (data?.ErrorMessage is not null) throw new Exception(data.ErrorMessage); 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; private set; } public string Description { get; private set; } }