Compare commits

...

13 Commits
stable ... dev

Author SHA1 Message Date
JacobTech 42290dc983 Server Update 2024-08-27 10:57:22 -04:00
JacobTech 82ccdd8307 Overrides & Members 2024-03-31 23:57:12 -04:00
JacobTech e94151e814 Luski.Shared HTTP 2024-03-20 23:18:34 -04:00
JacobTech 97ed89b627 Framework Update
A few changes, mostly to update the .NET framework version.
2023-12-22 11:13:31 -05:00
JacobTech 69168acd22 Fixed loop.
Server class can't have an API class property, or a loop will prevent building.
2023-10-01 13:12:27 -04:00
JacobTech 0740c6daca Try method for public server.
Added TryGetPublicServer to API handler.
2023-10-01 12:28:10 -04:00
JacobTech 0349745944 Alternate servers.
On load, the client can find alternate servers to connect to.
2023-08-25 12:07:36 -04:00
JacobTech 9f641e7429 Public Servers.
Added a bunch of API calls to use in public servers.
2023-08-21 10:58:17 -04:00
JacobTech ea39d93cf5 Friends.
Friends have been copied to the main server.
2023-07-10 14:19:03 -04:00
JacobTech c3bb39b21b Better Storage.
The example library now has better storage for local information and cache.
2023-07-10 07:35:05 -04:00
JacobTech d8fbf281d0 Server Separation.
I moved the two server types to their own classes to prevent API calls to a server, not of that type.
2023-07-08 09:06:13 -04:00
JacobTech 106d0d6078 Migration starts
• A lot of work needs to be done to make sure this works.
• I already know this push won't work, but it will build.
• I need to come up with a new way of storing local info. This will also bee needed to fix the very broken key system in this rushed commit.
2023-07-03 23:24:35 -04:00
JacobTech 2eb1abe526 init 2023-01-01 22:50:39 -05:00
76 changed files with 6231 additions and 0 deletions

25
Luski.net.sln Executable file
View File

@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31717.71
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Luski.net", "Luski.net\Luski.net.csproj", "{3DF9B870-51B3-4338-84EC-75E4B8802F0C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{3DF9B870-51B3-4338-84EC-75E4B8802F0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3DF9B870-51B3-4338-84EC-75E4B8802F0C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3DF9B870-51B3-4338-84EC-75E4B8802F0C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3DF9B870-51B3-4338-84EC-75E4B8802F0C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {49AFEA24-10EC-4D2C-B99C-C3E70124E443}
EndGlobalSection
EndGlobal

163
Luski.net/API.cs Normal file
View File

@ -0,0 +1,163 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
using System.Threading;
using System.Threading.Tasks;
using Luski.net.Enums;
using Luski.Shared.PublicServers.V1.Enums;
using Luski.Shared.PublicServers.V1.ServerToClient.HTTP;
namespace Luski.net;
public class API
{
[JsonIgnore]
public MainServer MainServer { get; internal set; }
public bool IsAnyServerLoggedin { get; internal set; }
public const string DefaultVersion = "v1";
internal List<string> Versions = new()
{
DefaultVersion
};
public IReadOnlyList<string> SupportedVersions => Versions.AsReadOnly();
internal List<PublicServer> InternalServers { get; } = new();
public IReadOnlyList<PublicServer> LoadedServers => InternalServers.AsReadOnly();
internal List<PublicServer> InternalFailedServers { get; } = new();
public IReadOnlyList<PublicServer> FailedServers => InternalFailedServers.AsReadOnly();
private static HttpResponseMessage GetFromServer(string Domain, string ApiVersion, bool Secure, string Path, CancellationToken CancellationToken, params KeyValuePair<string, string?>[] Headers)
{
using HttpClient web = new();
web.Timeout = TimeSpan.FromSeconds(10);
if (Headers is not null && Headers.Length > 0) foreach (KeyValuePair<string, string?> header in Headers) web.DefaultRequestHeaders.Add(header.Key, header.Value);
return web.GetAsync($"{(Secure ? "https" : "http" )}://{Domain}/{ApiVersion}/{Path}", cancellationToken: CancellationToken).Result;
}
private static Task<Tresult> GetFromServer<Tresult>(string Domain, string ApiVersion, bool Secure, string Path, JsonTypeInfo<Tresult> Type, CancellationToken CancellationToken, params KeyValuePair<string, string?>[] Headers) where Tresult : STC, new()
{
HttpResponseMessage ServerResponce = GetFromServer(Domain, ApiVersion, Secure, Path, CancellationToken, Headers);
Tresult temp = new();
string raw = ServerResponce.Content.ReadAsStringAsync(CancellationToken).Result;
try
{
temp = JsonSerializer.Deserialize(raw, Type)!;
}
catch (Exception e)
{
Console.WriteLine("JSON parse failed for the following data as type {0}\n{1}", temp.GetType(), raw);
}
if (temp is null) return Task.FromResult(new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with empty data" });
return Task.FromResult(temp);
}
public Task<ServerInfoSTC> GetServerInfo(string Domain, string Version = DefaultVersion,
bool Secure = true)
{
ServerInfoSTC? si = GetFromServer(Domain, Version, Secure, "socketserver", ServerInfoSTCContext.Default.ServerInfoSTC, CancellationToken.None).Result;
if (si is null) throw new Exception("Bad Response");
return Task.FromResult(si);
}
public Task<bool> TryGetServerInfo([NotNullWhen(true)]out ServerInfoSTC? si, string Domain, string Version = DefaultVersion,
bool Secure = true)
{
try
{
si = GetServerInfo(Domain, Version, Secure).Result;
return Task.FromResult(true);
}
catch (Exception e)
{
si = null;
return Task.FromResult(false);
}
}
public Task<bool> TryGetPublicServer(out PublicServer Server, string Domain, string Version = DefaultVersion,
bool Secure = true, bool GenerateEncryption = true, bool LogConsole = false)
{
try
{
Task<PublicServer> result = GetPublicServer(Domain, Version, Secure, GenerateEncryption, LogConsole);
Task.WaitAll(result);
Server = result.Result;
return Task.FromResult(true);
}
catch (Exception e)
{
if (!e.Message.Contains("Connection refused")) Console.WriteLine(e);
Server = null!;
return Task.FromResult(false);
}
}
public async Task<PublicServer> GetPublicServer(string Domain, string Version = DefaultVersion, bool Secure = true, bool GenerateEncryption = true, bool LogConsole = false)
{
PublicServer s;
try
{
IEnumerable<PublicServer> isl = InternalServers.Where(a => (a.Domain == Domain && a.ApiVersion == Version));
if (isl.Any()) return isl.First();
s = await PublicServer.GetServer(InternalFailedServers, Domain, Version, Secure, GenerateEncryption, LogConsole);
}
catch (Exception e)
{
Console.WriteLine("Failed to connect to public server '{0}' using API {1}. No alternate server was found.", Domain, Version);
throw;
}
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)
{
IsAnyServerLoggedin = true;
}
else
{
s.Token = null;
s.Error = null;
}
}
}
catch
{
}
InternalServers.Add(s);
return s;
}
public MainServer GetMainServer(string Domain, string Version = DefaultVersion)
{
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;
}
}

View File

@ -0,0 +1,13 @@
using System.Collections;
using System.Collections.Generic;
using Luski.net.Structures.Public;
namespace Luski.net.Classes;
public class RoleComparer : IComparer<Role>
{
public int Compare(Role x, Role y)
{
return y.Index - x.Index;
}
}

458
Luski.net/ClientEncryption.cs Executable file
View File

@ -0,0 +1,458 @@
/*using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
namespace Luski.net
{
public static class ClientEncryption
{
internal static string pw = "";
public static class File
{/*
internal static void SetOfflineKey(string key)
{
MakeFile("Server.GetKeyFilePath", pw);
LuskiDataFile? fileLayout = JsonSerializer.Deserialize<LuskiDataFile>(FileString("Server.GetKeyFilePath", pw));
fileLayout.OfflineKey = key;
fileLayout.Save("Server.GetKeyFilePath", pw);
}
internal static string? GetOfflineKey()
{
MakeFile("Server.GetKeyFilePath", pw);
LuskiDataFile? fileLayout = JsonSerializer.Deserialize<LuskiDataFile>(FileString("Server.GetKeyFilePath", pw));
return fileLayout?.OfflineKey;
}
public static LuskiDataFile GetFile()
{
MakeFile("Server.GetKeyFilePath", pw);
return JsonSerializer.Deserialize<LuskiDataFile>(FileString("Server.GetKeyFilePath", pw))!;
}
private static string FileString(string path, string password)
{
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
byte[] salt = new byte[100];
FileStream fsCrypt = new(path, FileMode.Open);
fsCrypt.Read(salt, 0, salt.Length);
RijndaelManaged AES = new()
{
KeySize = 256,
BlockSize = 128
};
Rfc2898DeriveBytes key = new(passwordBytes, salt, 50000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Padding = PaddingMode.PKCS7;
AES.Mode = CipherMode.CFB;
CryptoStream cs = new(fsCrypt, AES.CreateDecryptor(), CryptoStreamMode.Read);
MemoryStream fsOut = new();
int read;
byte[] buffer = new byte[1048576];
try
{
while ((read = cs.Read(buffer, 0, buffer.Length)) > 0)
{
fsOut.Write(buffer, 0, read);
}
}
catch (CryptographicException ex_CryptographicException)
{
Console.WriteLine("CryptographicException error: " + ex_CryptographicException.Message);
}
catch (Exception ex)
{
Console.WriteLine("Error: " + ex.Message);
}
fsOut.Seek(0, SeekOrigin.Begin);
using BinaryReader reader = new(fsOut);
byte[] by = reader.ReadBytes((int)fsOut.Length);
fsOut.Close();
fsCrypt.Close();
return Encoding.UTF8.GetString(by);
}
public static class Channels
{
private static string GetKey(long channel)
{
LuskiDataFile? fileLayout;
IEnumerable<ChannelLayout>? lis;
try
{
#pragma warning disable CS8603 // Possible null reference return.
// if (channel == 0) return myPrivateKey;
#pragma warning restore CS8603 // Possible null reference return.
MakeFile("Server.GetKeyFilePath", pw);
fileLayout = JsonSerializer.Deserialize<LuskiDataFile>(FileString("Server.GetKeyFilePath", pw));
lis = fileLayout?.channels?.Where(s => s.id == channel);
if (lis?.Count() > 0)
{
return lis.First().key;
}
throw new Exception("You dont have a key for that channel");
}
finally
{
fileLayout = null;
lis = null;
}
}
internal static string GetKeyBranch(long channel)
{
LuskiDataFile? fileLayout;
IEnumerable<ChannelLayout>? lis;
try
{
#pragma warning disable CS8603 // Possible null reference return.
// if (channel == 0) return myPrivateKey;
#pragma warning restore CS8603 // Possible null reference return.
MakeFile("Server.GetKeyFilePathBr(branch.ToString())", pw);
fileLayout = JsonSerializer.Deserialize<LuskiDataFile>(FileString("", pw));
lis = fileLayout?.channels?.Where(s => s.id == channel);
if (lis?.Count() > 0)
{
return lis.First().key;
}
throw new Exception("You dont have a key for that channel");
}
finally
{
fileLayout = null;
lis = null;
}
}
public static void AddKey(long channel, string key)
{
MakeFile("Server.GetKeyFilePath", pw);
LuskiDataFile? fileLayout = JsonSerializer.Deserialize<LuskiDataFile>(FileString("Server.GetKeyFilePath", pw));
fileLayout?.Addchannelkey(channel, key);
fileLayout?.Save("Server.GetKeyFilePath", pw);
}
}
private static void MakeFile(string dir, string password)
{
if (!System.IO.File.Exists(dir))
{
LuskiDataFile? l = JsonSerializer.Deserialize<LuskiDataFile>("{\"channels\":[]}");
l?.Save(dir, password);
}
}
public class LuskiDataFile
{
public static LuskiDataFile GetDataFile(string path, string password)
{
MakeFile(path, password);
return JsonSerializer.Deserialize<LuskiDataFile>(FileString(path, password));
}
internal static LuskiDataFile GetDefualtDataFile()
{
return GetDataFile("Server.GetKeyFilePath", pw);
}
public ChannelLayout[]? channels { get; set; } = default!;
public string? OfflineKey { get; set; } = default!;
public void Save(string file, string password)
{
byte[] salt = new byte[100];
RandomNumberGenerator? provider = RandomNumberGenerator.Create();
provider.GetBytes(salt);
FileStream fsCrypt = new(file, FileMode.Create);
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
RijndaelManaged AES = new()
{
KeySize = 256,
BlockSize = 128,
Padding = PaddingMode.PKCS7
};
Rfc2898DeriveBytes key = new(passwordBytes, salt, 50000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CFB;
fsCrypt.Write(salt, 0, salt.Length);
CryptoStream cs = new(fsCrypt, AES.CreateEncryptor(), CryptoStreamMode.Write);
string tempp = JsonSerializer.Serialize(this);
MemoryStream fsIn = new(Encoding.UTF8.GetBytes(tempp));
byte[] buffer = new byte[1048576];
int read;
try
{
while ((read = fsIn.Read(buffer, 0, buffer.Length)) > 0)
{
cs.Write(buffer, 0, read);
}
fsIn.Close();
}
catch (Exception ex)
{
Console.WriteLine("Error: " + ex.Message);
}
finally
{
cs.Close();
fsCrypt.Close();
}
}
public void Addchannelkey(long chan, string Key)
{
List<ChannelLayout>? chans = channels?.ToList();
if (chans is null) chans = new();
if (!(chans?.Where(s => s.id == chan).Count() > 0))
{
ChannelLayout l = new()
{
id = chan,
key = Key
};
chans?.Add(l);
channels = chans?.ToArray();
}
else
{
chans.Remove(chans.Where(s => s.id == chan).First());
ChannelLayout l = new()
{
id = chan,
key = Key
};
chans?.Add(l);
channels = chans?.ToArray();
}
}
}
public class ChannelLayout
{
public long id { get; set; } = default!;
public string key { get; set; } = default!;
}
}
public class AES
{
public static string Encrypt(string path, string Password)
{
string p = Path.GetTempFileName();
byte[] salt = RandomNumberGenerator.GetBytes(100);
byte[] passwordBytes = Encoding.UTF8.GetBytes(Password);
Rfc2898DeriveBytes key = new(passwordBytes, salt, 50000);
byte[] data = System.IO.File.ReadAllBytes(path);
using Aes aesAlg = Aes.Create();
aesAlg.KeySize = 256;
aesAlg.BlockSize = 128;
aesAlg.Padding = PaddingMode.PKCS7;
aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
aesAlg.IV = key.GetBytes(aesAlg.BlockSize / 8);
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
using FileStream msEncrypt = new(p, FileMode.Open);
msEncrypt.Write(salt, 0, salt.Length);
using CryptoStream csEncrypt = new(msEncrypt, encryptor, CryptoStreamMode.Write);
csEncrypt.Write(data, 0, data.Length);
csEncrypt.Dispose();
msEncrypt.Dispose();
return p;
string p = Path.GetTempFileName();
byte[] salt = new byte[100];
RNGCryptoServiceProvider provider = new();
provider.GetBytes(salt);
FileStream fsCrypt = new(p, FileMode.Open);
byte[] passwordBytes = Encoding.UTF8.GetBytes(Password);
Aes AES = Aes.Create();
AES.KeySize = 256;
AES.BlockSize = 128;
AES.Padding = PaddingMode.PKCS7;
Rfc2898DeriveBytes key = new(passwordBytes, salt, 50000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CFB;
fsCrypt.Write(salt, 0, salt.Length);
key.Dispose();
CryptoStream cs = new(fsCrypt, AES.CreateEncryptor(), CryptoStreamMode.Write);
FileStream fsIn = new(path, FileMode.Open);
try
{
FileInfo FI = new(path);
byte[] buffer = new byte[FI.Length];
int read;
while ((read = fsIn.Read(buffer, 0, buffer.Length)) > 0)
{
cs.Write(buffer, 0, read);
}
}
catch (OutOfMemoryException ex)
{
throw new Exception("Buffer", ex);
}
fsIn.Close();
fsIn.Dispose();
cs.Close();
cs.Dispose();
fsCrypt.Close();
fsCrypt.Dispose();
NewPath = p;
}
public static void Decrypt(byte[] data, string Password, string File)
{
byte[] salt = new byte[100];
using MemoryStream fsCrypt = new(data);
fsCrypt.Read(salt, 0, salt.Length);
byte[] passwordBytes = Encoding.UTF8.GetBytes(Password);
Rfc2898DeriveBytes key = new(passwordBytes, salt, 50000);
byte[] decrypted = new byte[data.Length - salt.Length];
using Aes aesAlg = Aes.Create();
aesAlg.KeySize = 256;
aesAlg.BlockSize = 128;
aesAlg.Padding = PaddingMode.PKCS7;
aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
aesAlg.IV = key.GetBytes(aesAlg.BlockSize / 8);
ICryptoTransform encryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
using CryptoStream csEncrypt = new(fsCrypt, encryptor, CryptoStreamMode.Read);
FileStream fsOut = new(File, FileMode.Create);
int read;
byte[] buffer = new byte[data.Length];
while ((read = csEncrypt.Read(buffer, 0, buffer.Length)) > 0)
{
fsOut.Write(buffer, 0, read);
}
csEncrypt.Dispose();
fsCrypt.Dispose();
fsOut.Dispose();
}
}
public static byte[] Hash(byte[] data)
{
return SHA256.Create().ComputeHash(data);
}
internal static byte[] Encrypt(string data)
{
return Encrypt(data, ServerPublicKey);
}
internal static byte[] Encrypt(byte[] data)
{
return Encrypt(data, ServerPublicKey);
}
internal static byte[] Encrypt(string data, string key, bool multithread = false)
{
return Encrypt(Encoder.GetBytes(data), key, multithread);
}
internal static byte[] Encrypt(byte[] data, string key, bool multithread = false)
{
using RSACryptoServiceProvider rsa = new();
rsa.FromXmlString(key);
int size = ((rsa.KeySize - 384) / 8) + 7;
double x = data.Length / (double)size;
int bbb = int.Parse(x.ToString().Split('.')[0]);
if (x.ToString().Contains('.')) bbb++;
byte[]? datasplitout = Array.Empty<byte>();
if (multithread)
{
byte[][]? decccc = Array.Empty<byte[]>();
Array.Resize(ref decccc, bbb);
int num = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * Server.Percent) * 2.0));
if (num == 0) num = 1;
Parallel.For(0, bbb, new ParallelOptions()
{
MaxDegreeOfParallelism = num
}, i =>
{
decccc[i] = rsa.Encrypt(data.Skip(i * size).Take(size).ToArray(), true);
});
foreach (byte[] dataa in decccc)
{
datasplitout = Combine(datasplitout, dataa);
}
}
else
{
for (int i = 0; i < bbb; i++)
{
datasplitout = Combine(datasplitout, rsa.Encrypt(data.Skip(i * size).Take(size).ToArray(), false));
}
}
return datasplitout;
}
private static byte[] Combine(byte[] first, byte[] second)
{
byte[]? bytes = new byte[first.Length + second.Length];
Buffer.BlockCopy(first, 0, bytes, 0, first.Length);
Buffer.BlockCopy(second, 0, bytes, first.Length, second.Length);
return bytes;
}
internal static byte[] Decrypt(byte[] EncryptedText, bool multithread = false)
{
return Decrypt(EncryptedText, myPrivateKey, multithread);
}
internal static byte[] Decrypt(byte[]? EncryptedText, string? key, bool multithread = false)
{
if (key is null) throw new ArgumentNullException(nameof(key));
if (EncryptedText is null) throw new ArgumentNullException(nameof(EncryptedText));
using RSACryptoServiceProvider rsa = new();
rsa.FromXmlString(key);
int size = rsa.KeySize / 8;
double x = EncryptedText.Length / (double)size;
int bbb = int.Parse(x.ToString().Split('.')[0]);
if (x.ToString().Contains('.')) bbb++;
byte[]? datasplitout = Array.Empty<byte>();
if (multithread)
{
byte[][]? decccc = Array.Empty<byte[]>();
Array.Resize(ref decccc, bbb);
int num = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * Server.Percent) * 2.0));
if (num == 0) num = 1;
Parallel.For(0, bbb, new ParallelOptions()
{
MaxDegreeOfParallelism = num
}, i =>
{
decccc[i] = rsa.Decrypt(EncryptedText.Skip(i * size).Take(size).ToArray(), true);
});
foreach (byte[] data in decccc)
{
datasplitout = Combine(datasplitout, data);
}
}
else
{
for (int i = 0; i < bbb; i++)
{
datasplitout = Combine(datasplitout, rsa.Decrypt(EncryptedText.Skip(i * size).Take(size).ToArray(), false));
}
}
return datasplitout;
}
}
}*/

5
Luski.net/Converters.cs Normal file
View File

@ -0,0 +1,5 @@
namespace Luski.net;
public static class Converters
{
}

View File

@ -0,0 +1,8 @@
namespace Luski.net.Enums;
public enum CacheMode : byte
{
None,
Encrypted,
Unencrypted
}

View File

@ -0,0 +1,18 @@
namespace Luski.net.Enums;
public enum DataType
{
Message_Create,
Status_Update,
Friend_Request_Result,
Friend_Request,
Change_Channel,
Join_Call,
Leave_Call,
Call_Info,
Call_Data,
Login,
Error,
Key_Exchange,
MAX
}

View File

@ -0,0 +1,7 @@
namespace Luski.net.Enums.Main;
public enum ChannelType : short
{
DM,
GROUP,
}

View File

@ -0,0 +1,9 @@
namespace Luski.net.Enums.Main;
public enum FriendStatus
{
NotFriends,
Friends,
PendingOut,
PendingIn
}

View File

@ -0,0 +1,11 @@
using System;
namespace Luski.net.Enums.Main;
[Flags]
public enum UserFlag : short
{
Dev = 0b_001,
Early = 0b_010,
Tester = 0b_100
}

View File

@ -0,0 +1,9 @@
namespace Luski.net.Enums;
public enum ServerType : byte
{
Public = 0,
Main = 1,
PublicSub = 2,
MainSub = 3
}

View File

@ -0,0 +1,15 @@
namespace Luski.net.Enums;
public enum StorageDirectory : byte
{
ServerInfo,
ServerAssets,
ChannelKeys,
ServerKeys,
Avatars,
ChannelIcons,
Messages,
StorageInfo,
Files,
ProfileAvatars
}

30
Luski.net/Exceptions.cs Executable file
View File

@ -0,0 +1,30 @@
using System;
namespace Luski.net
{
public class Exceptions
{
[Serializable]
public class MissingEventException : Exception
{
public string EventName;
public MissingEventException(string Event) : base(Event)
{
EventName = Event;
}
}
[Serializable]
public class NotConnectedException : Exception
{
public NotConnectedException(object sender, string message) : base(message)
{
Sender = sender;
}
public object Sender { get; }
}
}
}

View File

@ -0,0 +1,10 @@
using Luski.Shared.PublicServers.V1.Enums;
namespace Luski.net.Interfaces;
public interface IAppUser : IUser
{
public ErrorCode? Error { get; }
public string? ErrorMessage { get; }
public string Username { get; }
}

View File

@ -0,0 +1,38 @@
using System.Collections.Generic;
using System.Net.Http;
using System.Text.Json.Serialization.Metadata;
using System.Threading;
using System.Threading.Tasks;
using Luski.net.JsonTypes.BaseTypes;
using Luski.net.Structures;
using Luski.net.Structures.Public;
using Luski.Shared.PublicServers.V1.ServerToClient.HTTP;
namespace Luski.net.Interfaces;
public interface IServer
{
public IAppUser IAppUser { get; }
public string Cache { get; }
public void SendServer<Tvalue>(Tvalue Payload, JsonTypeInfo<Tvalue> jsonTypeInfo) where Tvalue : IncomingWSS;
public HttpResponseMessage GetFromServer(string Path, CancellationToken CancellationToken,
params KeyValuePair<string, string?>[] Headers);
public Task GetFromServer(string Path, string File, CancellationToken CancellationToken,
params KeyValuePair<string, string?>[] Headers);
public Task<Tresult> GetFromServer<Tresult>(string Path, JsonTypeInfo<Tresult> Type,
CancellationToken CancellationToken, params KeyValuePair<string, string?>[] Headers)
where Tresult : STC, new();
public Task<Tresult> SendServer<Tvalue, Tresult>(string Path, Tvalue Payload,
JsonTypeInfo<Tvalue> jsonTypeInfo, JsonTypeInfo<Tresult> ReturnjsonTypeInfo,
CancellationToken CancellationToken, params KeyValuePair<string, string?>[] Headers)
where Tvalue : IWebRequest where Tresult : STC, new();
public Task<Tresult> SendServer<Tresult>(string Path, string File, JsonTypeInfo<Tresult> ReturnjsonTypeInfo,
CancellationToken CancellationToken, params KeyValuePair<string, string?>[] Headers)
where Tresult : STC, new();
}

View File

@ -0,0 +1,6 @@
namespace Luski.net.Interfaces;
public interface IServerEvent
{
}

29
Luski.net/Interfaces/IUser.cs Executable file
View File

@ -0,0 +1,29 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Luski.net.Structures;
using Luski.net.Structures.Public;
using Luski.Shared.PublicServers.V1.Enums;
namespace Luski.net.Interfaces;
/// <summary>
/// Represents the curent user
/// </summary>
public interface IUser
{
/// <summary>
/// The current Id of the user
/// </summary>
long Id { get; }
/// <summary>
/// The current status of the user
/// </summary>
UserStatus Status { get; }
/// <summary>
/// Gets the current user keys
/// </summary>
/// <returns></returns>
Task<PublicKeyInfo[]> GetUserKeys(CancellationToken CancellationToken);
}

View File

@ -0,0 +1,6 @@
namespace Luski.net.Interfaces;
public interface IWebRequest
{
}

28
Luski.net/JsonRequest.cs Executable file
View File

@ -0,0 +1,28 @@
using Luski.net.Enums;
using System;
namespace Luski.net
{
internal static class JsonRequest
{
internal static string SendCallData(byte[] Data, long channel)
{
return $"{{\"data\": \"{Convert.ToBase64String(Data)}\", \"id\": {channel}}}";
}
internal static string JoinCall(long Channel)
{
return $"{{\"id\": {Channel}}}";
}
internal static string Send(DataType Request, string Data)
{
return $"{{\"type\": {(int)Request}, \"data\": {Data}}}";
}
internal static string FriendRequestResult(long User, bool Result)
{
return $"{{\"id\": {User},\"result\": {Result.ToString().ToLower()}}}";
}
}
}

View File

@ -0,0 +1,12 @@
using System.Text.Json.Serialization;
using Luski.net.Enums;
using Luski.net.Interfaces;
namespace Luski.net.JsonTypes.BaseTypes;
internal class HTTPRequest : IWebRequest
{
[JsonPropertyName("data_type")]
[JsonInclude]
public DataType Type { get; set; } = default!;
}

View File

@ -0,0 +1,24 @@
using Luski.net.Enums;
using System.Text.Json.Serialization;
namespace Luski.net.JsonTypes.BaseTypes;
public class IncomingWSS
{
[JsonPropertyName("type")]
[JsonInclude]
public DataType? Type { get; set; } = DataType.Login!;
[JsonPropertyName("error")]
[JsonInclude]
public string Error { get; set; } = default!;
}
[JsonSerializable(typeof(IncomingWSS))]
[JsonSourceGenerationOptions(
GenerationMode = JsonSourceGenerationMode.Default,
PropertyNamingPolicy = JsonKnownNamingPolicy.Unspecified,
WriteIndented = false)]
public partial class IncomingWSSContext : JsonSerializerContext
{
}

View File

@ -0,0 +1,28 @@
using Luski.net.JsonTypes.BaseTypes;
using System.Text.Json.Serialization;
using Luski.Shared.PublicServers.V1.ServerToClient.HTTP;
namespace Luski.net.JsonTypes;
internal class FriendRequestResult : STC
{
[JsonPropertyName("channel")]
[JsonInclude]
public long? Channel { get; set; }
[JsonPropertyName("id")]
[JsonInclude]
public long? Id { get; set; }
[JsonPropertyName("result")]
[JsonInclude]
public bool? Result { get; set; }
}
[JsonSerializable(typeof(FriendRequestResult))]
[JsonSourceGenerationOptions(
GenerationMode = JsonSourceGenerationMode.Default,
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
WriteIndented = false)]
internal partial class FriendRequestResultContext : JsonSerializerContext
{
}

View File

@ -0,0 +1,25 @@
using Luski.net.JsonTypes.BaseTypes;
using System.Text.Json.Serialization;
using Luski.Shared.PublicServers.V1.ClientToServer.HTTP;
namespace Luski.net.JsonTypes.HTTP;
internal class FriendRequest : CTS
{
[JsonPropertyName("code")]
[JsonInclude]
public string code { get; set; } = default!;
[JsonPropertyName("id")]
[JsonInclude]
public long Id { get; set; } = default!;
}
[JsonSerializable(typeof(FriendRequest))]
[JsonSourceGenerationOptions(
GenerationMode = JsonSourceGenerationMode.Default,
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
WriteIndented = false)]
internal partial class FriendRequestContext : JsonSerializerContext
{
}

View File

@ -0,0 +1,25 @@
using Luski.net.JsonTypes.BaseTypes;
using System.Text.Json.Serialization;
using Luski.Shared.PublicServers.V1.ClientToServer.HTTP;
namespace Luski.net.JsonTypes.HTTP;
internal class FriendRequestResultOut : CTS
{
[JsonPropertyName("id")]
[JsonInclude]
public long? Id { get; set; }
[JsonPropertyName("result")]
[JsonInclude]
public bool? Result { get; set; }
}
[JsonSerializable(typeof(FriendRequestResultOut))]
[JsonSourceGenerationOptions(
GenerationMode = JsonSourceGenerationMode.Default,
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
WriteIndented = false)]
internal partial class FriendRequestResultOutContext : JsonSerializerContext
{
}

View File

@ -0,0 +1,11 @@
namespace Luski.net.JsonTypes
{
internal class KeyExchange
{
public long channel { get; set; } = default!;
public long id { get; set; } = default!;
public string key { get; set; } = default!;
public long? to { get; set; } = default!;
}
}

View File

@ -0,0 +1,29 @@
using System;
using System.Text.Json.Serialization;
using Luski.Shared.PublicServers.V1.Enums;
using Luski.Shared.PublicServers.V1.Shared;
namespace Luski.net.JsonTypes;
public class LocalServerInfo
{
[JsonInclude]
[JsonPropertyName("alternate_servers")]
public ServerData[] AlternateServers { get; set; } = Array.Empty<ServerData>();
[JsonInclude]
[JsonPropertyName("picture_type")]
public PictureType PictureType { get; set; }
[JsonInclude]
[JsonPropertyName("name")]
public string Name { get; set; } = default!;
[JsonInclude]
[JsonPropertyName("description")]
public string Description { get; set; } = default!;
}
[JsonSerializable(typeof(LocalServerInfo))]
[JsonSourceGenerationOptions(
GenerationMode = JsonSourceGenerationMode.Default,
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
WriteIndented = false)]
internal partial class LocalServerInfoContext : JsonSerializerContext;

View File

@ -0,0 +1,20 @@
using Luski.net.JsonTypes.BaseTypes;
using System.Text.Json.Serialization;
using Luski.Shared.PublicServers.V1.ServerToClient.HTTP;
namespace Luski.net.JsonTypes;
internal class OfflineKeyData : STC
{
public KeyExchange[]? keys { get; internal set; } = default!;
}
[JsonSerializable(typeof(OfflineKeyData))]
[JsonSourceGenerationOptions(
GenerationMode = JsonSourceGenerationMode.Default,
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
WriteIndented = false)]
internal partial class OfflineKeyDataContext : JsonSerializerContext
{
}

View File

@ -0,0 +1,27 @@
using System.Text.Json.Serialization;
using Luski.net.Enums;
namespace Luski.net.JsonTypes;
public class ServerStorageInfo
{
[JsonInclude]
[JsonPropertyName("cache_mode")]
public CacheMode CacheMode { get; set; } = CacheMode.Encrypted;
[JsonInclude]
[JsonPropertyName("storage_id")]
public long StorageID { get; set; } = default!;
[JsonInclude]
[JsonPropertyName("prevent_deletion")]
public bool DontDelete { get; set; } = false;
}
[JsonSerializable(typeof(ServerStorageInfo))]
[JsonSourceGenerationOptions(
GenerationMode = JsonSourceGenerationMode.Default,
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
WriteIndented = true)]
internal partial class ServerStorageInfoContext : JsonSerializerContext
{
}

View File

@ -0,0 +1,17 @@
using System.Text.Json.Serialization;
using Luski.Shared.PublicServers.V1.Enums;
namespace Luski.net.JsonTypes;
internal class StatusUpdate
{
[JsonInclude]
[JsonPropertyName("id")]
public long id { get; set; } = default!;
[JsonInclude]
[JsonPropertyName("before")]
public UserStatus before { get; set; } = default!;
[JsonInclude]
[JsonPropertyName("after")]
public UserStatus after { get; set; } = default!;
}

View File

@ -0,0 +1,27 @@
using System.Text.Json.Serialization;
using Luski.Shared.PublicServers.V1.Enums;
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!;
[JsonPropertyName("data")]
[JsonInclude]
public object? Data { get; set; }
}
[JsonSerializable(typeof(BetterIncomingWSS))]
[JsonSourceGenerationOptions(
GenerationMode = JsonSourceGenerationMode.Default,
PropertyNamingPolicy = JsonKnownNamingPolicy.Unspecified,
WriteIndented = false)]
public partial class BetterIncomingWSSContext : JsonSerializerContext
{
}

View File

@ -0,0 +1,15 @@
using System.Text.Json.Serialization;
using Luski.Shared.PublicServers.V1.Enums;
namespace Luski.net.JsonTypes.WSS;
public class ServerEvent
{
[JsonInclude]
[JsonPropertyName("type")]
public DataType Type { get; set; }
[JsonInclude]
[JsonPropertyName("data")]
public object Data { get; set; }
}

View File

@ -0,0 +1,31 @@
using Luski.net.Enums;
using Luski.net.JsonTypes.BaseTypes;
using System.Text.Json.Serialization;
namespace Luski.net.JsonTypes.WSS;
internal class WSSKeyExchange : IncomingWSS
{
[JsonPropertyName("type")]
[JsonInclude]
new public DataType? Type { get; set; } = DataType.Key_Exchange;
[JsonPropertyName("channel")]
[JsonInclude]
public long channel { get; set; } = default!;
[JsonPropertyName("key")]
[JsonInclude]
public string key { get; set; } = default!;
[JsonPropertyName("to")]
[JsonInclude]
public long? to { get; set; } = default!;
}
[JsonSerializable(typeof(WSSKeyExchange))]
[JsonSourceGenerationOptions(
GenerationMode = JsonSourceGenerationMode.Default,
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
WriteIndented = false)]
internal partial class WSSKeyExchangeContext : JsonSerializerContext
{
}

View File

@ -0,0 +1,29 @@
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, 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;
}
[JsonSerializable(typeof(WSSLogin))]
[JsonSourceGenerationOptions(
GenerationMode = JsonSourceGenerationMode.Default,
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
WriteIndented = false)]
internal partial class WSSLoginContext : JsonSerializerContext
{
}

View File

@ -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
{
}

30
Luski.net/Luski.net.csproj Executable file
View File

@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Title>Luski.net</Title>
<Authors>JacobTech</Authors>
<Company>JacobTech, LLC</Company>
<Description>A wrapper for the luski API</Description>
<PackageProjectUrl>https://www.jacobtech.com/Luski/Documentation</PackageProjectUrl>
<RepositoryUrl>https://github.com/JacobTech-com/Luski.net</RepositoryUrl>
<IncludeSymbols>True</IncludeSymbols>
<FileVersion>1.0.0</FileVersion>
<Version>2.0.1-alpha15</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Luski.Shared" Version="1.1.0-alpha44" />
<PackageReference Include="websocketsharp.core" Version="1.0.0" />
</ItemGroup>
<Target Name="CustomActionsAfterPublish" AfterTargets="Pack">
<Message Text="Actions AfterPublish: $(PackageId).$(PackageVersion).nupkg" Importance="high" />
<Exec Command="nuget push -Source https://nuget.jacobtech.com/v3/index.json bin/Release/$(PackageId).$(PackageVersion).nupkg" />
</Target>
</Project>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<_LastSelectedProfileId>C:\Users\techn\source\repos\JacobTech-com\Luski.net\Luski.net\Luski.net\Properties\PublishProfiles\FolderProfile.pubxml</_LastSelectedProfileId>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,157 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Security.Authentication;
using System.Text;
using System.Text.Json;
using System.Threading;
using JacobTechEncryption;
using JacobTechEncryption.Enums;
using Luski.net.Enums;
using Luski.net.JsonTypes;
using Luski.net.JsonTypes.WSS;
using Luski.net.Structures.Main;
using Luski.Shared.PublicServers.V1.Enums;
using Luski.Shared.PublicServers.V1.ServerToClient.HTTP;
using WebSocketSharp;
namespace Luski.net;
public partial class MainServer
{
public void Login(string Username, string Password, CancellationToken CancellationToken)
{
Both(Username, Password, CancellationToken);
}
public void CreateAccount(string Username, string Password, string Displayname, string PFP, CancellationToken CancellationToken)
{
Both(Username, Password, CancellationToken, Displayname, PFP);
}
private void Both(string Username, string Password, CancellationToken CancellationToken, string? Displayname = null, string? pfp = null)
{
if (!EncryptionHandler.Generating)
{
EncryptionHandler.GenerateKeys();
}
while (!EncryptionHandler.Generated) { }
login = true;
LoginSTC json;
List<KeyValuePair<string, string?>> heads = new()
{
new("key", EncryptionHandler.MyPublicKey),
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.Unicode.GetBytes(Password), lpv)));
heads.Add(new("old_version", lpv.ToString()));
}
if (pfp is not null)
{
heads.Add(new("username", Displayname));
json = SendServer(
"CreateAccount",
pfp,
LoginSTCContext.Default.LoginSTC,
CancellationToken,
heads.ToArray()).Result;
}
else
{
json = GetFromServer(
"Login",
LoginSTCContext.Default.LoginSTC,
CancellationToken,
heads.ToArray()).Result;
}
if (json.Error is not null) throw new Exception($"Luski appears to be down at the current moment: {json.ErrorMessage}");
if (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($"{(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();
SendServerOld(new WSSLogin() { Token = json.Token! }, WSSLoginContext.Default.WSSLogin);
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 = GetUser(id, MainSocketAppUserContext.Default.MainSocketAppUser,
CancellationToken)
.Result;
if (User is null || User.Error is not null)
{
string error = "User was null";
if (User is not null && User.Error is not null) error = $"{User.Error}: {User.ErrorMessage}";
throw new Exception($"Something went wrong getting your user infermation\n{error}");
}
_ = UpdateStatus(UserStatus.Online, CancellationToken);
if (pfp is not null)
{
//post
}
else
{
}
EncryptionHandler.Hash = EncryptionHandler.LocalPasswordEncrypt(Encoding.Unicode.GetBytes(Username.ToLower() + Password));
OfflineDataBlobSTC offlinedata = GetFromServer("Keys/GetOfflineData", OfflineDataBlobSTCContext.Default.OfflineDataBlobSTC, CancellationToken).Result;
if (offlinedata is not null && offlinedata.Error is null && offlinedata.Data is not null && offlinedata.Data.Length > 0)
{
foreach (string keyex in offlinedata.Data)
{
KeyExchange? okd = JsonSerializer.Deserialize<KeyExchange>(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),
Storage.GetResourceKeyRaw(
StorageDirectory.ServerKeys,
"pkey",
EncryptionHandler.Hash
)
)
)
);
}
}
}
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($"{(Secure ? "https" : "http" )}://{Domain}/{ApiVersion}/Keys/SetOfflineKey", new StringContent(EncryptionHandler.OfflinePublicKey)).Result;
foreach (var ch in chans)
{
_ = ch.Members;
}
EncryptionHandler.OfflinePublicKey = null;
EncryptionHandler.OfflinePrivateKey = null;
}
else throw new Exception(json?.ErrorMessage);
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Threading.Tasks;
using Luski.net.Structures;
using Luski.net.Structures.Main;
namespace Luski.net;
public partial class MainServer
{
public event Func<MainSocketMessage, Task>? MessageReceived;
public event Func<MainSocketRemoteUser, Task>? ReceivedFriendRequest;
public event Func<MainSocketRemoteUser, bool, Task>? FriendRequestResult;
}

View File

@ -0,0 +1,125 @@
using System;
using System.Text;
using System.Text.Json;
using System.Threading;
using JacobTechEncryption;
using Luski.net.Enums;
using Luski.net.JsonTypes;
using Luski.net.JsonTypes.BaseTypes;
using Luski.net.JsonTypes.HTTP;
using Luski.net.JsonTypes.WSS;
using Luski.net.Structures;
using Luski.net.Structures.Main;
using WebSocketSharp;
namespace Luski.net;
public partial class MainServer
{
private void DataFromServer(object? sender, MessageEventArgs e)
{
if (e.IsPing) return;
try
{
Console.WriteLine("From Server: {0}", e.Data);
IncomingWSS? data = JsonSerializer.Deserialize(e.Data, IncomingWSSContext.Default.IncomingWSS);
switch (data?.Type)
{
case DataType.Login:
Console.WriteLine("Pre auth");
WSSLogin n = JsonSerializer.Deserialize(e.Data, WSSLoginContext.Default.WSSLogin)!;
Token = n.Token;
Console.WriteLine("Token: {0}",Token);
break;
case DataType.Error:
if (Token is null)
{
Error = data.Error;
}
else
{
Exception(new Exception(data.Error));
}
break;
case DataType.Message_Create:
if (MessageReceived is not null)
{
MainSocketMessage? m = JsonSerializer.Deserialize<MainSocketMessage>(e.Data);
if (m is not null)
{
m.decrypt(EncryptionHandler.GetChannelKey(m.ChannelID), CancellationToken.None);
_ = MessageReceived.Invoke(m);
}
}
break;
case DataType.Status_Update:
StatusUpdate? SU = JsonSerializer.Deserialize<StatusUpdate>(e.Data);
if (SU is not null)
{
MainSocketRemoteUser after = GetUser(SU.id, MainSocketRemoteUserContext.Default.MainSocketRemoteUser, CancellationToken.None).Result;
after.Status = SU.after;
MainSocketRemoteUser before = after.Clone();
before.Status = SU.before;
StatusUpdate(before, after);
}
break;
case DataType.Friend_Request:
if (ReceivedFriendRequest is not null)
{
FriendRequest? request =
JsonSerializer.Deserialize(e.Data, FriendRequestContext.Default.FriendRequest);
if (request is not null)
_ = ReceivedFriendRequest.Invoke(GetUser(request.Id,
MainSocketRemoteUserContext.Default.MainSocketRemoteUser,
CancellationToken.None).Result);
}
break;
case DataType.Friend_Request_Result:
FriendRequestResult? FRR = JsonSerializer.Deserialize<FriendRequestResult>(e.Data);
if (FRR is not null && FRR.Channel is not null && FRR.Id is not null &&
FRR.Result is not null)
{
MainSocketDMChannel chan = GetChannel((long)FRR.Channel,
MainSocketDMChannelContext.Default.MainSocketDMChannel,
CancellationToken.None).Result;
chans.Add(chan);
MainSocketRemoteUser from1 = GetUser((long)FRR.Id,
MainSocketRemoteUserContext.Default.MainSocketRemoteUser,
CancellationToken.None).Result;
//from1.Channel = chan;
if (FriendRequestResult is not null) _ = FriendRequestResult.Invoke(from1, (bool)FRR.Result);
}
break;
case DataType.Key_Exchange:
try
{
KeyExchange? KE = JsonSerializer.Deserialize<KeyExchange>(e.Data);
if (KE is not null)
{
EncryptionHandler.SetChannelKey(
KE.channel,
Encoding.UTF8.GetString(
Encryption.RSA.Decrypt(
Convert.FromBase64String(KE.key),
EncryptionHandler.myPrivateKey)
)
);
}
}
catch (Exception ex)
{
Exception(ex);
}
break;
default:
Console.WriteLine("Unknown");
break;
}
}
catch (Exception exception)
{
Exception(exception);
}
}
}

278
Luski.net/MainServer.cs Normal file
View File

@ -0,0 +1,278 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.Json.Serialization.Metadata;
using System.Threading;
using System.Threading.Tasks;
using JacobTechEncryption;
using Luski.net.Enums.Main;
using Luski.net.Interfaces;
using Luski.net.JsonTypes;
using Luski.net.JsonTypes.HTTP;
using Luski.net.Structures.Main;
using Luski.Shared.PublicServers.V1.ClientToServer.HTTP;
using Luski.Shared.PublicServers.V1.Enums;
using Luski.Shared.PublicServers.V1.ServerToClient.HTTP;
using ChannelType = Luski.net.Enums.Main.ChannelType;
namespace Luski.net;
public partial class MainServer : Server
{
internal MainServer(string Domain, string API_Version):
base(Domain, API_Version)
{
}
public MainSocketAppUser User { get; internal set; } = default!;
public List<MainSocketChannel> chans { get; } = new();
public async Task<MainSocketRemoteUser> SendFriendResult(long user, bool answer, CancellationToken CancellationToken)
{
FriendRequestResult json = await SendServer("FriendRequestResult",
new FriendRequestResultOut()
{
Id = user,
Result = answer
},
FriendRequestResultOutContext.Default.FriendRequestResultOut,
FriendRequestResultContext.Default.FriendRequestResult,
CancellationToken);
if (json is not null && json.Error is null && json.ErrorMessage is null && answer && json.Channel is not null)
{
MainSocketDMChannel chan = await GetChannel((long)json.Channel, MainSocketDMChannelContext.Default.MainSocketDMChannel, CancellationToken);
_ = chan.StartKeyProcessAsync(CancellationToken);
chans.Add(chan);
}
else
{
throw new Exception(json?.Error.ToString());
}
return GetUser(user, MainSocketRemoteUserContext.Default.MainSocketRemoteUser, CancellationToken).Result;
}
public async Task<MainSocketRemoteUser> SendFriendRequest(long code, CancellationToken CancellationToken)
{
string ccode = Convert.ToBase64String(Encryption.Hashing.SHA256(Encoding.Unicode.GetBytes(code.ToString())));
FriendRequestResult? json = await SendServer("FriendRequest", new FriendRequest() { code = ccode}, FriendRequestContext.Default.FriendRequest, FriendRequestResultContext.Default.FriendRequestResult, CancellationToken);
if (json.StatusCode != HttpStatusCode.Accepted)
{
if (json is not null && json.Error is not null)
{
switch ((ErrorCode)(int)json.Error)
{
case ErrorCode.InvalidToken:
throw new Exception("Your current token is no longer valid");
case ErrorCode.ServerError:
throw new Exception($"Error from server: {json.ErrorMessage}");
case ErrorCode.InvalidPostData:
throw new Exception("The post data dent to the server is not the correct format. This may be because you app is couropt or you are using the wron API version");
case ErrorCode.Forbidden:
throw new Exception("You already have an outgoing request or the persone is not real");
}
}
if (json is not null && json.Channel is not null)
{
MainSocketDMChannel chan = await GetChannel((long)json.Channel, MainSocketDMChannelContext.Default.MainSocketDMChannel, CancellationToken);
_ = chan.StartKeyProcessAsync(CancellationToken);
chans.Add(chan);
}
}
MainSocketRemoteUser b = await GetUser(code, MainSocketRemoteUserContext.Default.MainSocketRemoteUser, CancellationToken);
if (json.Channel is not null)
b.FriendStatus = FriendStatus.Friends;
else
b.FriendStatus = FriendStatus.PendingOut;
return b;
}
public async Task<TChannel> GetChannel<TChannel>(long Channel, CancellationToken CancellationToken) where TChannel : MainSocketChannel, new()
{
TChannel Return = new();
switch (Return)
{
case MainSocketDMChannel:
Return = (await GetChannel(Channel, MainSocketDMChannelContext.Default.MainSocketDMChannel, CancellationToken) as TChannel)!;
break;
case MainSocketGroupChannel:
Return = (await GetChannel(Channel, MainSocketGroupChannelContext.Default.MainSocketGroupChannel, CancellationToken) as TChannel)!;
break;
case MainSocketTextChannel:
Return = (await GetChannel(Channel, MainSocketTextChannelContext.Default.MainSocketTextChannel, CancellationToken) as TChannel)!;
break;
case MainSocketChannel:
Return = (await GetChannel(Channel, MainSocketChannelContext.Default.MainSocketChannel, CancellationToken) as TChannel)!;
break;
case null:
throw new NullReferenceException(nameof(TChannel));
default:
throw new Exception("Unknown channel type");
}
return Return;
}
internal async Task<TChannel> GetChannel<TChannel>(long id, JsonTypeInfo<TChannel> Json, CancellationToken CancellationToken) where TChannel : MainSocketChannel, new()
{
TChannel request;
if (chans.Count > 0 && chans.Any(s => s.Id == id))
{
return (chans.Where(s => s is TChannel && s.Id == id).First() as TChannel)!;
}
while (true)
{
if (CanRequest)
{
request = await GetFromServer($"SocketChannel/Get/{id}", Json, CancellationToken);
break;
}
}
if (request is null) throw new Exception("Something was wrong with the server responce");
if (request.Error is null)
{
if (chans.Count > 0 && chans.Any(s => s.Id == request.Id))
{
foreach (MainSocketChannel? p in chans.Where(s => s.Id == request.Id))
{
chans.Remove(p);
}
}
request.Server = this;
chans.Add(request);
return request;
}
throw request.Error switch
{
ErrorCode.InvalidToken => new Exception("Your current token is no longer valid"),
ErrorCode.Forbidden => new Exception("The server rejected your request"),
ErrorCode.ServerError => new Exception("Error from server: " + request.ErrorMessage),
ErrorCode.InvalidURL or ErrorCode.MissingHeader => new Exception(request.ErrorMessage),
_ => new Exception($"Unknown data: '{request.ErrorMessage}'"),
};
}
public Task<Tuser> GetUser<Tuser>(long UserID, CancellationToken CancellationToken) where Tuser : MainSocketUserBase, new()
{
Tuser user = new();
switch (user)
{
case MainSocketAppUser:
user = (GetUser(UserID, MainSocketAppUserContext.Default.MainSocketAppUser, CancellationToken).Result as Tuser)!;
break;
case MainSocketRemoteUser:
user = (GetUser(UserID, MainSocketRemoteUserContext.Default.MainSocketRemoteUser, CancellationToken).Result as Tuser)!;
break;
case MainSocketUserBase:
user = (GetUser(UserID, MainSocketUserBaseContext.Default.MainSocketUserBase, CancellationToken).Result as Tuser)!;
break;
case null:
throw new NullReferenceException(nameof(Tuser));
default:
throw new Exception("Unknown channel type");
}
return Task.FromResult(user);
}
public async Task<MainSocketMessage> GetMessage(long id, CancellationToken CancellationToken)
{
MainSocketMessage message;
while (true)
{
if (CanRequest)
{
message = await GetFromServer("socketmessage",
MainSocketMessageContext.Default.MainSocketMessage,
CancellationToken,
new KeyValuePair<string, string?>("msg_id", id.ToString()));
break;
}
}
if (message is not null)
{
message.Server = this;
return message;
}
throw new Exception("Server did not return a message");
}
/// <summary>
/// Sends the server a request to update the <paramref name="Status"/> of you account
/// </summary>
/// <param name="Status">The <see cref="UserStatus"/> you want to set your status to</param>
/// <exception cref="Exception"></exception>
public async Task<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;
}
internal async Task<Tuser> GetUser<Tuser>(long UserId, JsonTypeInfo<Tuser> Json, CancellationToken CancellationToken) where Tuser : MainSocketUserBase, new()
{
Tuser user;
if (poeople.Count > 0 && poeople.Any(s => s.Id == UserId))
{
Tuser temp = poeople.Where(s => s is Tuser && s.Id == UserId).Cast<Tuser>().FirstOrDefault()!;
if (temp is MainSocketRemoteUser && (temp as MainSocketRemoteUser)!.Channel == null)
{
foreach (MainSocketDMChannel chan in chans.Where(s => s is MainSocketDMChannel).Cast<MainSocketDMChannel>())
{
if (chan.Type == ChannelType.DM && chan.Id != 0 && chan.MemberIdList is not null)
{
if (chan.MemberIdList.Any(s => s == UserId)) (temp as MainSocketRemoteUser)!.Channel = chan;
}
}
}
return temp;
}
while (true)
{
if (CanRequest)
{
user = await GetFromServer("socketuser",
Json,
CancellationToken,
new KeyValuePair<string, string?>("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);
}
}
user.Server = this;
if (user is MainSocketRemoteUser && (user as MainSocketRemoteUser)!.Channel == null)
{
foreach (MainSocketDMChannel chan in chans.Where(s => s is MainSocketDMChannel).Cast<MainSocketDMChannel>())
{
if (chan.Type == ChannelType.DM && chan.Id != 0 && chan.MemberIdList is not null)
{
if (chan.MemberIdList.Any(s => s == UserId)) (user as MainSocketRemoteUser)!.Channel = chan;
}
}
}
poeople.Add(user);
return user;
}
}

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project>
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<PublishDir>bin\Release\net6.0\publish\</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol>
<_TargetId>Folder</_TargetId>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project>
<PropertyGroup>
<History>True|2022-11-23T16:09:01.7347068Z;True|2022-11-23T11:07:47.9880607-05:00;True|2022-11-23T11:07:08.8325322-05:00;True|2022-11-23T11:05:40.5859900-05:00;True|2022-09-21T18:57:48.1433890-04:00;False|2022-09-21T18:56:37.2624157-04:00;True|2022-07-05T22:55:54.9271108-04:00;</History>
<LastFailureDetails />
</PropertyGroup>
</Project>

View File

@ -0,0 +1,292 @@
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.WSS;
using Luski.net.Structures.Public;
using Luski.Shared.PublicServers.V1.ClientToServer.HTTP;
using Luski.Shared.PublicServers.V1.Enums;
using Luski.Shared.PublicServers.V1.ServerToClient.HTTP;
using WebSocketSharp;
using DataType = Luski.Shared.PublicServers.V1.Enums.DataType;
namespace Luski.net;
public partial class PublicServer
{
public async Task<bool> Login(string Username, string Password, CancellationToken CancellationToken)
{
return await All(Username, CancellationToken, Password);
}
public async Task<bool> CreateAccount(string Username, string Password, string DisplayName, string PFP, CancellationToken CancellationToken)
{
return await All(Username, CancellationToken, Password, DisplayName, PFP);
}
internal async Task<bool> LoginViaToken(string t)
{
Console.WriteLine("Starting Auto Login:");
bool b = await All(t, CancellationToken.None);
Console.WriteLine($"Auto Login {(b ? "Successful" : "Failed")}");
return b;
}
private async Task<bool> All(string Username, CancellationToken CancellationToken, string? Password = null, string? Displayname = null, string? pfp = null)
{
DateTime dt = DateTime.UtcNow;
Console.WriteLine("Encryption: " + DateTime.UtcNow.Subtract(dt).ToString("g"));
if (!EncryptionHandler.Generating)
{
EncryptionHandler.GenerateKeys();
}
while (!EncryptionHandler.Generated) { Thread.Sleep(200); }
Console.WriteLine("Encryption 2: " + DateTime.UtcNow.Subtract(dt).ToString("g"));
List<Tuple<string ,string, Exception>> FailedSystems = new();
login = true;
LoginSTC json;
List<KeyValuePair<string, string?>> heads = new()
{
new("key", EncryptionHandler.MyPublicKey),
new((Password is null ? "token" : "username"), Convert.ToBase64String(Encryption.RSA.Encrypt(Username, EncryptionHandler.ServerPublicKey, EncoderType.UTF16)))
};
if (Password is not null)
{
heads.Add(new("password", EncryptionHandler.RemotePasswordEncrypt(Encryption.Generic.Encoders[(int)EncoderType.UTF16].GetBytes(Password))));
}
if (File.Exists("LastPassVer.txt") && int.TryParse(File.ReadAllText("LastPassVer.txt"), out int lpv) && lpv < EncryptionHandler.PasswordVersion && lpv >= 0)
{
heads.Add(new("old_password", EncryptionHandler.RemotePasswordEncrypt(Encoding.Unicode.GetBytes(Password), lpv)));
heads.Add(new("old_version", lpv.ToString()));
}
if (pfp is not null)
{
heads.Add(new("displayname", Displayname));
json = await SendServer(
"SocketAccount",
pfp,
LoginSTCContext.Default.LoginSTC,
CancellationToken,
heads.ToArray());
}
else
{
json = await GetFromServer(
(Password is null ? "SocketAccount/AccessToken": "SocketAccount"),
LoginSTCContext.Default.LoginSTC,
CancellationToken,
heads.ToArray());
}
Console.WriteLine("Account Result: " + DateTime.UtcNow.Subtract(dt).ToString("g"));
if (json.Error is not null)
{
Console.WriteLine(json.ErrorMessage);
return false;
throw new Exception($"Luski appears to be down at the current moment: {json.ErrorMessage}");
}
if (EncryptionHandler.OfflinePrivateKey is null || EncryptionHandler.OfflinePublicKey is null) throw new Exception("Something went wrong generating the offline keys");
login = false;
if (json is not null && json.Error is null)
{
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();
Console.WriteLine("WSS Connection: " + DateTime.UtcNow.Subtract(dt).ToString("g"));
SendServer(DataType.Token, new WSSLogin() { Token = json.Token! });
while (Token is null && Error is null)
{
Thread.Sleep(200);
}
if (Error is not null)
{
throw new Exception(Error);
}
Console.WriteLine("WSS Login: " + DateTime.UtcNow.Subtract(dt).ToString("g"));
if (Token is null) throw new Exception("Server did not send a token");
CanRequest = true;
long id = long.Parse(Encoding.UTF8.GetString(Convert.FromBase64String(
Token.Split('.')[0]
)));
User = await GetUser<SocketAppUser>(id, CancellationToken);
User.Username = Username;
Console.WriteLine("Get our info: " + DateTime.UtcNow.Subtract(dt).ToString("g"));
#region Extra Systems
Task.Run(async () => {
#region Data Storage
try
{
StorageInfoSTC data;
if (Storage.StorageID == 0)
{
EncryptionHandler.Hash = Storage.GenerateStorage();
data = await SendServer("OfflineData/Info",
new StorageInfoCTS()
{
Password = Convert.ToBase64String(Encryption.AES.Encrypt(EncryptionHandler.Hash, Storage.GetResourceBytes(StorageDirectory.StorageInfo, "lpk")))
},
StorageInfoCTSContext.Default.StorageInfoCTS,
StorageInfoSTCContext.Default.StorageInfoSTC, CancellationToken,
new KeyValuePair<string, string?>("storage_id", Storage.StorageID.ToString()));
Storage.setid(data.ID);
}
else
{
data = await GetFromServer("OfflineData/Info", StorageInfoSTCContext.Default.StorageInfoSTC, CancellationToken, new KeyValuePair<string, string?>("storage_id", Storage.StorageID.ToString()));
}
Console.WriteLine("Offline Data Info: " + DateTime.UtcNow.Subtract(dt).ToString("g"));
if (data.Update)
{
EncryptionHandler.Hash = Storage.UpdateStorage(Convert.FromBase64String(data.Password));
_ = await SendServerPatch("OfflineData/Info",
new StorageInfoCTS()
{
Password = Convert.ToBase64String(Encryption.AES.Encrypt(EncryptionHandler.Hash, Storage.GetResourceBytes(StorageDirectory.StorageInfo, "lpk")))
},
StorageInfoCTSContext.Default.StorageInfoCTS,
StorageInfoSTCContext.Default.StorageInfoSTC, CancellationToken,
new KeyValuePair<string, string?>("storage_id", Storage.StorageID.ToString()));
Console.WriteLine("Data Update: " + DateTime.UtcNow.Subtract(dt).ToString("g"));
}
}
catch (Exception e)
{
FailedSystems.Add(new("Data Storage", e.Message, e));
}
#endregion
#region Key Generation
try
{
try
{
_ = EncryptionHandler.GetKey(0);
}
catch (Exception e)
{
EncryptionHandler.SetKey(0, new()
{
EncryptionType = EncryptionType.None,
Key = string.Empty
});
Console.WriteLine("Key 0: " + DateTime.UtcNow.Subtract(dt).ToString("g"));
}
}
catch (Exception e)
{
FailedSystems.Add(new("Key Generation", "Key 0 Failed to generate", e));
}
#endregion
#region Auto Status
try
{
_ = await UpdateStatus(UserStatus.Online, CancellationToken);
Console.WriteLine("Status: " + DateTime.UtcNow.Subtract(dt).ToString("g"));
}
catch (Exception e)
{
FailedSystems.Add(new("Auto Status", "Failed to set status on the server", e));
}
#endregion
#region GetRoles
try
{
_ = await GetRoles();
}
catch (Exception e)
{
FailedSystems.Add(new("Get Roles", "Failed to get roles on the server", e));
}
#endregion
#region Local Storage Cleanup
try
{
KeyValuePair<string, string?> stor = new("storage_id", Storage.StorageID.ToString());
OfflineDataBlobSTC offlinedata = await GetFromServer("OfflineData", OfflineDataBlobSTCContext.Default.OfflineDataBlobSTC, CancellationToken, stor);
Console.WriteLine("Offline Data: " + DateTime.UtcNow.Subtract(dt).ToString("g"));
if (offlinedata is not null && offlinedata.Error is null && offlinedata.Data is not null && offlinedata.Data.Length > 0)
{
string pkey = Storage.GetResourceKeyRaw(
StorageDirectory.ServerKeys,
"pkey",
EncryptionHandler.Hash
);
foreach (string keyexx in offlinedata.Data)
{
string keyex = Encoding.UTF8.GetString(Convert.FromBase64String(keyexx));
KeyExchange? okd = JsonSerializer.Deserialize<KeyExchange>(keyex);
if (okd is not null && !string.IsNullOrEmpty(okd.key))
{
Storage.SetResourceKey(
StorageDirectory.ServerKeys,
okd.id.ToString(),
EncryptionHandler.Hash,
Encoding.Unicode.GetString(
Encryption.RSA.Decrypt(
Convert.FromBase64String(okd.key),
pkey
)
)
);
}
}
}
System.IO.File.WriteAllText("LastPassVer.txt", EncryptionHandler.PasswordVersion.ToString());
Storage.SetResourceKey(StorageDirectory.ServerKeys, "pkey", EncryptionHandler.Hash, EncryptionHandler.OfflinePrivateKey);
KeyPostCTS OfflineKeySetRequest = new()
{
Data = Convert.ToBase64String(Encoding.UTF8.GetBytes(EncryptionHandler.OfflinePublicKey)),
EncryptionType = EncryptionType.RSA
};
_ = await SendServer("Keys/SetOfflineKey",
OfflineKeySetRequest,
KeyPostCTSContext.Default.KeyPostCTS,
STCContext.Default.STC,
CancellationToken.None,
stor);
Console.WriteLine("Offline Key: " + DateTime.UtcNow.Subtract(dt).ToString("g"));
EncryptionHandler.OfflinePublicKey = null!;
EncryptionHandler.OfflinePrivateKey = null!;
}
catch (Exception e)
{
FailedSystems.Add(new("Local Storage Cleanup", "Failed to clean the local storage", e));
}
#endregion
if (FailedSystems.Count > 0)
{
Console.WriteLine("Some systems have failed:");
foreach (Tuple<string, string, Exception> System in FailedSystems)
{
Console.WriteLine($"\t{System.Item1}:\n\t\tMessage: {System.Item2}\n\t\tError Details: {System.Item3}");
}
} });
#endregion
return true;
}
else throw new Exception(json?.ErrorMessage);
return true;
}
}

View File

@ -0,0 +1,196 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using JacobTechEncryption;
using JacobTechEncryption.Enums;
using Luski.net.Enums;
using Luski.net.JsonTypes.BaseTypes;
using Luski.net.JsonTypes.WSS;
using Luski.net.Structures;
using Luski.net.Structures.Public;
using Luski.Shared.PublicServers.V1.Enums;
using Luski.Shared.PublicServers.V1.ServerToClient.HTTP;
using Luski.Shared.PublicServers.V1.Shared;
using WebSocketSharp;
using File = System.IO.File;
using DataType = Luski.Shared.PublicServers.V1.Enums.DataType;
namespace Luski.net;
public partial class PublicServer
{
private void DataFromServer(object? sender, MessageEventArgs e)
{
if (e.IsPing) return;
if (PrintServerMessages)
{
Console.WriteLine(e.Data);
}
try
{
BetterIncomingWSS? data = JsonSerializer.Deserialize(e.Data, BetterIncomingWSSContext.Default.BetterIncomingWSS);
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;
case DataType.StatusUpdate:
StatusEvent se = JsonSerializer.Deserialize(data.Data!.ToString()!, StatusEventContext.Default.StatusEvent)!;
SocketUser u;
if (se.User == User.Id) u = User;
else
{
u = GetUser<SocketUser>(se.User);
u.Status = se.After;
}
if (StatusUpdate is not null)
{
StatusUpdate.Invoke(se.Before, u);
}
break;
case DataType.Role:
RoleEvent re = JsonSerializer.Deserialize(data.Data!.ToString()!, RoleEventContext.Default.RoleEvent)!;
Role[] ra = roles.Where(s => s.ID == re.ID).ToArray();
Role r;
bool @new = false;
if (ra.Length > 0)
{
r = ra[0];
if (re.ServerPermissions is not null) r.ServerPermissions = re.ServerPermissions.Value;
if (re.Description is not null) r.Description = re.Description;
if (re.DisplayName is not null) r.DisplayName = re.DisplayName;
if (re.ColorType is not null) r.ColorType = re.ColorType.Value;
if (re.Color is not null)
{
if (r.ColorType == ColorType.Full)
{
Color nc = new(re.Color);
r.Colors = new []{nc};
}
else
{
List<Color> cols = new();
for (int i = 0; i < re.Color.Length - 7; i+=8)
{
cols.Add(new(re.Color[i..(i+8)]));
}
r.Colors = cols.ToArray();
}
}
if (re.Index is not null) r.Index = re.Index.Value;
}
else
{
@new = true;
Task<Role> rr = GetRole(re.ID);
rr.Wait();
r = rr.Result;
}
if (RoleEvent is not null) _ = RoleEvent.Invoke(@new, r);
break;
case DataType.RoleMember:
RoleMemberEvent rme = JsonSerializer.Deserialize(data.Data!.ToString()!, RoleMemberEventContext.Default.RoleMemberEvent)!;
break;
case DataType.RoleDelete:
break;
case DataType.MessageCreate:
MessageSTC smsg = JsonSerializer.Deserialize(data.Data!.ToString()!, MessageSTCContext.Default.MessageSTC)!;
List<long> fl = new();
List<SocketFile> sf = new();
foreach (ServerFileInfoSTC v in smsg.Files)
{
if (v.NameKey == 0)
{
if (string.IsNullOrEmpty(v.Name))
{
v.Name = "";
}
else v.Name = Encryption.Generic.Encoders[(int)v.NameEncoder]
.GetString(Convert.FromBase64String(v.Name));
}
else
{
LocalKeyInfo key = EncryptionHandler.GetKey(v.NameKey);
switch (key.EncryptionType)
{
case EncryptionType.RSA:
v.Name = Encryption.RSA.Decrypt(Convert.FromBase64String(v.Name), key.Key,
v.NameEncoder);
break;
default:
v.Name = Encryption.Generic.Encoders[(int)v.NameEncoder]
.GetString(Convert.FromBase64String(v.Name));
break;
}
}
fl.Add(v.ID);
sf.Add(new()
{
ID = v.ID,
Size = v.Size,
Name = v.Name,
Encoder = v.Encoder,
NameEncoder = v.NameEncoder,
Key = v.Key,
NameKey = v.NameKey,
Server = this
});
}
if (smsg.EncryptionKey == 0)
{
if (string.IsNullOrEmpty(smsg.Context))
{
smsg.Context = "";
}
else smsg.Context = Encryption.Generic.Encoders[(int)smsg.EncoderType]
.GetString(Convert.FromBase64String(smsg.Context));
}
else
{
LocalKeyInfo key = EncryptionHandler.GetKey(smsg.EncryptionKey);
switch (key.EncryptionType)
{
case EncryptionType.RSA:
smsg.Context = Encryption.RSA.Decrypt(Convert.FromBase64String(smsg.Context), key.Key,
smsg.EncoderType);
break;
default:
smsg.Context = Encryption.Generic.Encoders[(int)smsg.EncoderType]
.GetString(Convert.FromBase64String(smsg.Context));
break;
}
}
SocketMessage sm = new()
{
ID = smsg.ID,
AuthorID = smsg.AuthorID,
TimeStamp = smsg.Timestamp,
ChannelID = smsg.ChannelID,
Context = smsg.Context,
EncoderType = smsg.EncoderType,
EncryptionKey = smsg.EncryptionKey,
FileIDs = fl.ToArray(),
Server = this,
ProfileID = smsg.ProfileID,
_Files = sf
};
if (MessageReceived is not null) MessageReceived.Invoke(sm);
break;
default:
Console.WriteLine("Unknown");
break;
}
}
catch (Exception exception)
{
Exception(exception);
}
}
}

920
Luski.net/PublicServer.cs Normal file
View File

@ -0,0 +1,920 @@
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<SocketMessage, Task>? MessageReceived;
public event Func<UserStatus, SocketUser, Task>? StatusUpdate;
public event Func<bool, Role, Task>? RoleEvent;
public event Func<Role, Task>? RoleDeleted;
public event Func<Role, IUser[], IUser[], Task>? RoleMemberEvent;
public PictureType PictureType { get; private set; }
public List<SocketChannel> chans { get; } = new();
public List<SocketCategory> cats { get; } = new();
public List<Role> roles { get; } = new();
public List<RoleOverride> 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<PublicServer> GetServer(List<PublicServer> 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);
throw;
}
}
s.Storage.SetJson(StorageDirectory.ServerInfo, "server.json", new()
{
AlternateServers = Array.Empty<ServerData>(),
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<TCategory> GetCategory<TCategory>(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<string, string?>("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))
};
List<Color> cols = new();
if (request.ColorType == ColorType.Full)
{
Color nc = new(request.Color);
cols.Add(nc);
}
else
{
for (int i = 0; i < request.Color.Length - 7; i+=8)
{
cols.Add(new(request.Color[i..(i+8)]));
}
}
TCategory bob = new()
{
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,
ColorType = request.ColorType,
Colors = cols.ToArray()
};
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<Role> 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<Color> 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<Role[]> GetRoles()
{
RolesSTC s = await GetFromServer("SocketRole/GetAll", RolesSTCContext.Default.RolesSTC, CancellationToken.None);
roles.Clear();
foreach (var ServerRole in s.Roles)
{
List<Color> 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<SocketMessage> SendMessage<TChannel>(TChannel channel, string msg, SocketMessage? ReplyTo = null,
ServerProfile? FakeProfile = null, params string[] files) where TChannel : SocketChannel, new()
{
List<SocketFile> lll = new();
foreach (var f in files)
{
lll.Add(await UploadFile(f));
}
return await SendMessage(channel, msg, ReplyTo, FakeProfile, lll.ToArray());
}
public async Task<SocketFile> 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<SocketMessage> SendMessage<TChannel>(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<long> 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<long> 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<Role> CreateRole(RolePostCTS Req)
{
RoleSTC res = await SendServer("SocketRole",
Req,
RolePostCTSContext.Default.RolePostCTS,
RoleSTCContext.Default.RoleSTC,
CancellationToken.None);
List<Color> 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<Role> 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<Role> EditRoleMembers(Role r)
{
return r;
}
public async Task<TChannel> GetChannel<TChannel>(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<string, string?>("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))
};
List<Color> cols = new();
if (request.ColorType == ColorType.Full)
{
Color nc = new(request.Color);
cols.Add(nc);
}
else
{
for (int i = 0; i < request.Color.Length - 7; i+=8)
{
cols.Add(new(request.Color[i..(i+8)]));
}
}
TChannel bob = new()
{
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,
ColorType = request.ColorType,
Colors = cols.ToArray()
};
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<SocketChannel> 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<RoleOverrideCTS>(),
UserOverrides = Array.Empty<UserOverrideCTS>(),
Type = ChannelType.TextAndVoice,
Color = "FFFFFFFF",
PictureType = PictureType.none
},
ChannelPostCTSContext.Default.ChannelPostCTS,
ChannelSTCContext.Default.ChannelSTC,
CancellationToken.None);
List<Color> cols = new();
if (res.ColorType == ColorType.Full)
{
Color nc = new(res.Color);
cols.Add(nc);
}
else
{
for (int i = 0; i < res.Color.Length - 7; i+=8)
{
cols.Add(new(res.Color[i..(i+8)]));
}
}
return new SocketChannel()
{
ID = res.ID,
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,
ColorType = res.ColorType,
Colors = cols.ToArray()
};
}
public async Task<Tuser> GetUser<Tuser>(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<Tuser>().FirstOrDefault()!;
return temp;
}
while (true)
{
if (CanRequest)
{
user = await GetFromServer("socketuser",
SocketUserSTCContext.Default.SocketUserSTC,
CancellationToken,
new KeyValuePair<string, string?>("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<Tuser>(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<Tuser>().FirstOrDefault()!;
return temp;
}
while (true)
{
if (CanRequest)
{
user = GetFromServerRaw("socketuser",
SocketUserSTCContext.Default.SocketUserSTC,
new KeyValuePair<string, string?>("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<ServerProfile> 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<string, string?>("id", ProfileId.ToString()));
break;
}
}
if (user is null) throw new Exception("Server did not return a user");
if (profiles.Count > 0 && profiles.Any(s => s.ID == ProfileId))
{
_ = profiles.RemoveAll(s => s.ID == ProfileId);
}
if (user is null || user.Error is not null)
{
string error = "User was null";
if (user is not null && user.Error is not null) error = $"{user.Error}: {user.ErrorMessage}";
throw new Exception($"Something went wrong getting your user information\n{error}");
}
Color[]? colss = null;
if (user.Color is not null)
{
List<Color> cols = new();
if (user.ColorType == ColorType.Full)
{
Color nc = new(user.Color);
cols.Add(nc);
}
else
{
for (int i = 0; i < user.Color.Length - 7; i+=8)
{
cols.Add(new(user.Color[i..(i+8)]));
}
}
colss = cols.ToArray();
}
ServerProfile u = new()
{
Server = this,
ID = user.ID,
DisplayName = user.DisplayName,
PictureType = user.PictureType,
Controllers = user.Controllers,
RoleControllers = user.RoleControllers,
ColorType = user.ColorType,
Color = colss
};
profiles.Add(u);
return u;
}
public async Task<ServerProfile[]> 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<ServerProfile> profiles_ = new();
foreach (ProfileSTC user in Prof.Profiles)
{
if (profiles.Count > 0 && profiles.Any(s => s.ID == user.ID))
{
_ = profiles.RemoveAll(s => s.ID == user.ID);
}
if (user is null || user.Error is not null)
{
string error = "Profile was null";
if (user is not null && user.Error is not null) error = $"{user.Error}: {user.ErrorMessage}";
throw new Exception($"Something went wrong getting your profile information\n{error}");
}
Color[]? colss = null;
if (user.Color is not null)
{
List<Color> cols = new();
if (user.ColorType == ColorType.Full)
{
Color nc = new(user.Color);
cols.Add(nc);
}
else
{
for (int i = 0; i < user.Color.Length - 7; i+=8)
{
cols.Add(new(user.Color[i..(i+8)]));
}
}
colss = cols.ToArray();
}
ServerProfile u = new()
{
Server = this,
ID = user.ID,
DisplayName = user.DisplayName,
PictureType = user.PictureType,
Controllers = user.Controllers,
RoleControllers = user.RoleControllers,
ColorType = user.ColorType,
Color = colss
};
profiles_.Add(u);
profiles.Add(u);
}
return profiles_.ToArray();
}
/// <summary>
/// Sends the server a request to update the <paramref name="Status"/> of you account
/// </summary>
/// <param name="Status">The <see cref="UserStatus"/> you want to set your status to</param>
/// <exception cref="Exception"></exception>
public async Task<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; }
}

View File

@ -0,0 +1,83 @@
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;
public class ServerEncryption
{
internal bool Generating, Generated;
internal string ServerPublicKey = "", MyPublicKey = "", myPrivateKey = "", OfflinePrivateKey = "", OfflinePublicKey = "";
internal byte[] Hash = default!;
internal ServerEncryption(ServerStorage Storage)
{
this.Storage = Storage;
}
public string GetChannelKey(long Channel)
{
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)
{
Storage.SetResourceKey(StorageDirectory.ChannelKeys, Channel.ToString(), Hash, Key);
}
private ServerStorage Storage { get; }
internal int PasswordVersion = 0;
internal byte[] LocalPasswordEncrypt(byte[] Password) => LocalPasswordEncrypt(Password, PasswordVersion);
internal string RemotePasswordEncrypt(byte[] Password) => RemotePasswordEncrypt(Password, PasswordVersion);
internal byte[] LocalPasswordEncrypt(byte[] Password, int PasswordVersion)
{
return PasswordVersion switch
{
0 => SHA256.Create().ComputeHash(Password),
_ => throw new ArgumentException("The value provided was not accepted", nameof(PasswordVersion)),
};
}
internal string RemotePasswordEncrypt(byte[] Password, int PasswordVersion)
{
return PasswordVersion switch
{
0 => Convert.ToBase64String(Encryption.RSA.Encrypt(LocalPasswordEncrypt(Password, PasswordVersion), ServerPublicKey)),
_ => throw new ArgumentException("The value provided was not accepted", nameof(PasswordVersion)),
};
}
public void GenerateKeys()
{
if (!Generating && !Generated)
{
Generating = true;
GenerateNewKeys(out MyPublicKey, out myPrivateKey);
GenerateNewKeys(out OfflinePublicKey, out OfflinePrivateKey);
Generated = true;
}
}
public static void GenerateNewKeys(out string Public, out string Private, int KeySize = 4096)
{
using RSACryptoServiceProvider r = new(KeySize);
Private = r.ToXmlString(true);
Public = r.ToXmlString(false);
}
}

View File

@ -0,0 +1,22 @@
using System;
using System.Threading.Tasks;
using Luski.net.Interfaces;
namespace Luski.net;
public partial class Server
{
public event Func<IUser, IUser, Task>? UserStatusUpdate;
public event Func<Exception, Task>? OnError;
internal void Exception(Exception e)
{
if (OnError is not null) OnError.Invoke(e);
}
internal void StatusUpdate(IUser u1, IUser u2)
{
if (UserStatusUpdate is not null) UserStatusUpdate.Invoke(u1, u2);
}
}

View File

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.IO;
using Luski.net.Enums;
using Luski.net.Interfaces;
using Luski.net.JsonTypes;
using Luski.net.Structures;
using Luski.net.Structures.Main;
using Luski.net.Structures.Public;
using WebSocketSharp;
namespace Luski.net;
public partial class Server
{
public ServerType ServerType { get; internal set; } = ServerType.Public;
public string Domain { get; set; } = default!;
public bool PrintServerMessages { get; set; } = false;
public string ApiVersion { get; } = "v1";
internal WebSocket? ServerOut;
internal string? Token = null, Error = null, gen = null;
public bool IsLogedIn => Token is not null;
internal bool CanRequest = false, login = false;
internal List<IUser> poeople = new();
internal List<ServerProfile> profiles = new();
}

View File

@ -0,0 +1,12 @@
using System;
using WebSocketSharp;
namespace Luski.net;
public partial class Server
{
internal void ServerOut_OnError(object? sender, ErrorEventArgs e)
{
this.Exception(new Exception(e.Message));
}
}

369
Luski.net/Server.Storage.cs Normal file
View File

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

184
Luski.net/Server.cs Normal file
View File

@ -0,0 +1,184 @@
using System;
using System.Collections.Generic;
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;
using System.Threading.Tasks;
using Luski.net.Enums;
using Luski.net.Interfaces;
using Luski.net.JsonTypes.BaseTypes;
using Luski.net.JsonTypes.WSS;
using Luski.Shared.PublicServers.V1.ClientToServer.HTTP;
using Luski.Shared.PublicServers.V1.Enums;
using Luski.Shared.PublicServers.V1.ServerToClient.HTTP;
using File = System.IO.File;
namespace Luski.net;
public partial class Server
{
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(Storage);
}
internal bool Secure = true;
internal string wssurl { get; set; } = "";
public ServerEncryption EncryptionHandler { get; }
public ServerStorage Storage { get; }
public async Task<Stream> GetAvatar(CancellationToken CancellationToken)
{
bool isc = File.Exists(Storage.GetStorageDirectory(StorageDirectory.ServerAssets) + "Icon");
if (!isc) await GetFromServer($"socketserver/Icon/", Storage.GetStorageDirectory(StorageDirectory.ServerAssets) + "Icon", CancellationToken);
return Storage.GetResourceStream(StorageDirectory.ServerAssets, "Icon");
}
public void SendServerOld<Tvalue>(Tvalue Payload, JsonTypeInfo<Tvalue> jsonTypeInfo) where Tvalue : IncomingWSS
{
ServerOut?.Send(JsonSerializer.Serialize(Payload, jsonTypeInfo));
}
public void SendServer(Luski.Shared.PublicServers.V1.Enums.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<string, string?>[] Headers)
{
using HttpClient web = new();
web.Timeout = TimeSpan.FromSeconds(10);
if (!login) web.DefaultRequestHeaders.Add("token", Token);
if (Headers is not null && Headers.Length > 0) foreach (KeyValuePair<string, string?> header in Headers) web.DefaultRequestHeaders.Add(header.Key, header.Value);
return web.GetAsync($"{(Secure ? "https" : "http" )}://{Domain}/{ApiVersion}/{Path}", cancellationToken: CancellationToken).Result;
}
public Task GetFromServer(string Path, string File, CancellationToken CancellationToken, params KeyValuePair<string, string?>[] Headers)
{
using HttpClient web = new();
web.Timeout = TimeSpan.FromMinutes(10);
if (!login) web.DefaultRequestHeaders.Add("token", Token);
if (Headers is not null && Headers.Length > 0) foreach (KeyValuePair<string, string?> header in Headers) web.DefaultRequestHeaders.Add(header.Key, header.Value);
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);
return Task.CompletedTask;
}
public async Task<Tresult> GetFromServer<Tresult>(string Path, JsonTypeInfo<Tresult> Type, CancellationToken CancellationToken, params KeyValuePair<string, string?>[] Headers) where Tresult : STC, new()
{
HttpResponseMessage ServerResponce = GetFromServer(Path, CancellationToken, Headers);
Tresult temp = new();
string raw = ServerResponce.Content.ReadAsStringAsync(CancellationToken).Result;
try
{
temp = JsonSerializer.Deserialize(raw, Type)!;
}
catch (Exception e)
{
Console.WriteLine("JSON parse failed for the following data as type {0}\n{1}", temp.GetType(), raw);
}
if (temp is null) return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with empty data" };
return temp;
}
public Tresult GetFromServerRaw<Tresult>(string Path, JsonTypeInfo<Tresult> Type, params KeyValuePair<string, string?>[] Headers) where Tresult : STC, new()
{
HttpResponseMessage ServerResponce = GetFromServer(Path, CancellationToken.None, Headers);
Tresult temp = new();
string raw ="";
try
{
temp = JsonSerializer.Deserialize(ServerResponce.Content.ReadAsStream(), Type)!;
}
catch (Exception e)
{
Console.WriteLine("JSON parse failed for the following data as type {0}\n{1}", temp.GetType(), raw);
}
if (temp is null) return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with empty data" };
return temp;
}
public async Task<Tresult> SendServer<Tvalue, Tresult>(string Path, Tvalue Payload, JsonTypeInfo<Tvalue> jsonTypeInfo, JsonTypeInfo<Tresult> ReturnjsonTypeInfo, CancellationToken CancellationToken, params KeyValuePair<string, string?>[] Headers) where Tvalue : CTS where Tresult : STC, new()
{
using HttpClient web = new();
if (!login) web.DefaultRequestHeaders.Add("token", Token);
if (Headers is not null && Headers.Length > 0) foreach (KeyValuePair<string, string?> header in Headers) web.DefaultRequestHeaders.Add(header.Key, header.Value);
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<Tresult> SendServerPatch<Tvalue, Tresult>(string Path, Tvalue Payload, JsonTypeInfo<Tvalue> jsonTypeInfo, JsonTypeInfo<Tresult> ReturnjsonTypeInfo, CancellationToken CancellationToken, params KeyValuePair<string, string?>[] Headers) where Tvalue : CTS where Tresult : STC, new()
{
using HttpClient web = new();
if (!login) web.DefaultRequestHeaders.Add("token", Token);
if (Headers is not null && Headers.Length > 0) foreach (KeyValuePair<string, string?> 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
{
Tresult? temp = JsonSerializer.Deserialize(ServerResponce.Content.ReadAsStreamAsync(CancellationToken).Result, ReturnjsonTypeInfo);
if (temp is null) return error;
return temp;
}
catch { return error; }
}
public async Task<Tresult> SendServer<Tresult>(string Path, string File, JsonTypeInfo<Tresult> ReturnjsonTypeInfo, CancellationToken CancellationToken, params KeyValuePair<string, string?>[] Headers) where Tresult : STC, new()
{
var fs = System.IO.File.OpenRead(File);
try
{
using HttpClient web = new();
if (!login) web.DefaultRequestHeaders.Add("token", Token);
web.Timeout = new TimeSpan(0, 10, 0);
if (Headers is not null && Headers.Length > 0) foreach (KeyValuePair<string, string?> header in Headers) web.DefaultRequestHeaders.Add(header.Key, header.Value);
//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 = 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" }; }
}
finally
{
fs.Close();
}
}
}

97
Luski.net/Server.old.Globals.cs Executable file
View File

@ -0,0 +1,97 @@
/*using Luski.net.Enums;
using Luski.net.Interfaces;
using Luski.net.JsonTypes;
using Luski.net.Sockets;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
using WebSocketSharp;
namespace Luski.net;
public sealed partial class Serverold
{
internal static string JT
{
get
{
string tmp = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "JacobTech");
if (OperatingSystem.IsLinux())
{
tmp = Path.Combine(Environment.GetEnvironmentVariable("HOME")!, ".config/");
tmp += "JacobTech";
}
return tmp;
}
}
internal static SocketAudioClient? AudioClient = null;
internal static string? Token = null, Error = null;
internal static bool CanRequest = false;
internal static SocketAppUser? _user;
internal static string platform = "win-x64";
internal static Branch Branch;
internal static double Percent = 0.5;
private static WebSocket? ServerOut;
private static string? gen = null;
private static bool login = false;
public static SocketServer CurrentServer = null!;
internal static string Cache
{
get
{
if (gen is null)
{
if (!Directory.Exists(JT)) Directory.CreateDirectory(JT);
string path = JT + "/Luski/";
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
path += Branch.ToString() + "/";
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
path += platform + "/";
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
path += "Data/";
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
path += _user?.Id + "/";
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
path += "Cache/";
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
path += Path.GetRandomFileName() + "/";
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
gen = path;
}
if (!Directory.Exists($"{gen}/avatars")) Directory.CreateDirectory($"{gen}/avatars");
if (!Directory.Exists($"{gen}/channels")) Directory.CreateDirectory($"{gen}/channels");
return gen;
}
}
internal static List<IUser> poeople = new();
internal static List<SocketChannel> chans { get; set; } = new();
internal static string GetKeyFilePath
{
get
{
return GetKeyFilePathBr(Branch.ToString());
}
}
internal static string GetKeyFilePathBr(string br)
{
if (!Directory.Exists(JT)) Directory.CreateDirectory(JT);
string path = JT + "/Luski/";
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
path += br + "/";
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
path += platform + "/";
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
path += "Data/";
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
path += _user?.Id + "/";
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
path += "keys.lsk";
return path;
}
}
*/

281
Luski.net/Server.old.cs Executable file
View File

@ -0,0 +1,281 @@
/*using Luski.net.Enums;
using Luski.net.Interfaces;
using Luski.net.JsonTypes;
using Luski.net.JsonTypes.BaseTypes;
using Luski.net.JsonTypes.HTTP;
using Luski.net.Sockets;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json;
using System.Text.Json.Serialization.Metadata;
using System.Threading;
using System.Threading.Tasks;
namespace Luski.net;
public sealed partial class Serverold
{
#pragma warning disable CA1822 // Mark members as static
public async Task<MainSocketRemoteUser> SendFriendResult(long user, bool answer, CancellationToken CancellationToken)
{
FriendRequestResult json = await SendServer(_user!.Servers.First(),"FriendRequestResult",
new FriendRequestResultOut()
{
Id = user,
Result = answer
},
FriendRequestResultOutContext.Default.FriendRequestResultOut,
FriendRequestResultContext.Default.FriendRequestResult,
CancellationToken);
if (json is not null && json.Error is null && json.ErrorMessage is null && answer && json.Channel is not null)
{
SocketDMChannel chan = await SocketChannel.GetChannel((long)json.Channel, SocketDMChannelContext.Default.SocketDMChannel, CancellationToken, _user!.Servers.First());
_ = chan.StartKeyProcessAsync(CancellationToken);
chans.Add(chan);
}
else
{
throw new Exception(json?.Error.ToString());
}
return SocketUserBase.GetUser(user, SocketRemoteUserContext.Default.SocketRemoteUser, CancellationToken, _user!.Servers.First()).Result;
}
public async Task<MainSocketRemoteUser> SendFriendRequest(long code, CancellationToken CancellationToken)
{
string ccode = Convert.ToBase64String(Encryption.Hash(Encryption.Encoder.GetBytes(code.ToString())));
FriendRequestResult? json = await SendServer(_user!.Servers.First(),"FriendRequest", new FriendRequest() { code = ccode}, FriendRequestContext.Default.FriendRequest, FriendRequestResultContext.Default.FriendRequestResult, CancellationToken);
if (json.StatusCode != HttpStatusCode.Accepted)
{
if (json is not null && json.Error is not null)
{
switch ((ErrorCode)(int)json.Error)
{
case ErrorCode.InvalidToken:
throw new Exception("Your current token is no longer valid");
case ErrorCode.ServerError:
throw new Exception($"Error from server: {json.ErrorMessage}");
case ErrorCode.InvalidPostData:
throw new Exception("The post data dent to the server is not the correct format. This may be because you app is couropt or you are using the wron API version");
case ErrorCode.Forbidden:
throw new Exception("You already have an outgoing request or the persone is not real");
}
}
if (json is not null && json.Channel is not null)
{
SocketDMChannel chan = await SocketChannel.GetChannel((long)json.Channel, (JsonTypeInfo<SocketDMChannel>)SocketDMChannelContext.Default.SocketDMChannel, CancellationToken, _user!.Servers.First());
_ = chan.StartKeyProcessAsync(CancellationToken);
chans.Add(chan);
}
}
MainSocketRemoteUser b = await SocketUserBase.GetUser(code, SocketRemoteUserContext.Default.SocketRemoteUser, CancellationToken, _user!.Servers.First());
if (json.Channel is not null)
b.FriendStatus = FriendStatus.Friends;
else
b.FriendStatus = FriendStatus.PendingOut;
return b;
}
/// <summary>
/// Sends the server a request to update the <paramref name="Status"/> of you account
/// </summary>
/// <param name="Status">The <see cref="UserStatus"/> you want to set your status to</param>
/// <exception cref="Exception"></exception>
public async Task<Task> UpdateStatus(UserStatus Status, CancellationToken CancellationToken)
{
if (_user is null) throw new Exception("You must login to make a request");
IncomingHTTP? data = await SendServer(_user!.Servers.First(),"SocketUserProfile/Status", new Status() { UserStatus = Status }, StatusContext.Default.Status, IncomingHTTPContext.Default.IncomingHTTP, CancellationToken);
if (data.Error is not null && ((int)data.StatusCode < 200 || (int)data.StatusCode > 299))
{
if (data?.ErrorMessage is not null) throw new Exception(data.ErrorMessage);
if (data?.Error is not null) throw new Exception(((int)data.Error).ToString());
else throw new Exception("Something went worng");
}
_user.Status = Status;
return Task.CompletedTask;
}
public async Task ChangeChannel(long Channel, CancellationToken CancellationToken, SocketServer Server)
{
if (_user is null) throw new Exception("You must login to make a request");
IncomingHTTP? data = await SendServer(Server, "ChangeChannel", new Channel() { Id = Channel }, ChannelContext.Default.Channel, IncomingHTTPContext.Default.IncomingHTTP, CancellationToken);
if (data.StatusCode != HttpStatusCode.Accepted)
{
if (data?.Error is not null)
{
switch (data.Error)
{
case ErrorCode.InvalidToken:
throw new Exception("Your current token is no longer valid");
case ErrorCode.ServerError:
throw new Exception("Error from server: " + data.ErrorMessage);
}
}
else throw new Exception("Something went worng");
}
_user.SelectedChannel = Channel;
}
public Task SendMessage(string Message, long Channel, SocketServer Server, CancellationToken CancellationToken, params JsonTypes.File[] Files)
{
try
{
_ = GetChannel<SocketTextChannel>(Channel, CancellationToken, Server).Result.SendMessage(Message, CancellationToken, Files);
}
catch (Exception e)
{
if (OnError is not null) _ = OnError.Invoke(e);
else throw e;
}
return Task.CompletedTask;
}
public void SetMultiThreadPercent(double num)
{
if (num < 1 || num > 100) throw new Exception("Number must be from 1 - 100");
Percent = num / 100;
}
public async Task<SocketMessage> GetMessage(long MessageId, CancellationToken CancellationToken, SocketServer Server) => await SocketMessage.GetMessage(MessageId, CancellationToken, Server);
public async Task<MainSocketRemoteUser> GetUser(long UserID, CancellationToken CancellationToken, SocketServer Server) => await SocketUserBase.GetUser(UserID, SocketRemoteUserContext.Default.SocketRemoteUser, CancellationToken, Server);
public async Task<TChannel> GetChannel<TChannel>(long Channel, CancellationToken CancellationToken, SocketServer Server) where TChannel : SocketChannel, new()
{
TChannel Return = new();
switch (Return)
{
case SocketDMChannel:
Return = (await SocketChannel.GetChannel(Channel, SocketDMChannelContext.Default.SocketDMChannel, CancellationToken, Server) as TChannel)!;
break;
case SocketGroupChannel:
Return = (await SocketChannel.GetChannel(Channel, SocketGroupChannelContext.Default.SocketGroupChannel, CancellationToken, Server) as TChannel)!;
break;
case SocketTextChannel:
Return = (await SocketChannel.GetChannel(Channel, SocketTextChannelContext.Default.SocketTextChannel, CancellationToken, Server) as TChannel)!;
break;
case SocketChannel:
Return = (await SocketChannel.GetChannel(Channel, SocketChannelContext.Default.SocketChannel, CancellationToken, Server) as TChannel)!;
break;
case null:
throw new NullReferenceException(nameof(TChannel));
default:
throw new Exception("Unknown channel type");
}
return Return;
}
public SocketAppUser CurrentUser
{
get
{
if (_user is null) throw new Exception("You must Login first");
return _user;
}
}
#pragma warning restore CA1822 // Mark members as static
private void ServerOut_OnError(object? sender, WebSocketSharp.ErrorEventArgs e)
{
if (OnError is not null) OnError.Invoke(new Exception(e.Message));
}
[Obsolete("Move to new Data layout")]
internal static void SendServer(string data)
{
ServerOut?.Send(data);
}
internal static void SendServer<Tvalue>(Tvalue Payload, JsonTypeInfo<Tvalue> jsonTypeInfo) where Tvalue : IncomingWSS
{
ServerOut?.Send(JsonSerializer.Serialize(Payload, jsonTypeInfo));
}
internal static HttpResponseMessage GetFromServer(SocketServer Server, string Path, CancellationToken CancellationToken, params KeyValuePair<string, string?>[] Headers)
{
using HttpClient web = new();
web.Timeout = TimeSpan.FromSeconds(10);
if (!login) web.DefaultRequestHeaders.Add("token", Token);
if (Headers is not null && Headers.Length > 0) foreach (KeyValuePair<string, string?> header in Headers) web.DefaultRequestHeaders.Add(header.Key, header.Value);
return web.GetAsync($"https://{Server.Domain}/{Server.ApiVersion}/{Path}", cancellationToken: CancellationToken).Result;
}
internal static Task GetFromServer(SocketServer Server, string Path, string File, CancellationToken CancellationToken, params KeyValuePair<string, string?>[] Headers)
{
using HttpClient web = new();
web.Timeout = TimeSpan.FromMinutes(10);
if (!login) web.DefaultRequestHeaders.Add("token", Token);
if (Headers is not null && Headers.Length > 0) foreach (KeyValuePair<string, string?> header in Headers) web.DefaultRequestHeaders.Add(header.Key, header.Value);
HttpResponseMessage Response = web.GetAsync($"https://{Server.Domain}/{Server.ApiVersion}/{Path}", CancellationToken).Result;
Stream stream = Response.Content.ReadAsStreamAsync(CancellationToken).Result;
using FileStream fs = System.IO.File.Create(File);
stream.CopyTo(fs);
return Task.CompletedTask;
}
internal static async Task<Tresult> GetFromServer<Tresult>(SocketServer Server, string Path, JsonTypeInfo<Tresult> Type, CancellationToken CancellationToken, params KeyValuePair<string, string?>[] Headers) where Tresult : IncomingHTTP, new()
{
HttpResponseMessage ServerResponce = GetFromServer(Server, Path, CancellationToken, Headers);
if (!ServerResponce.IsSuccessStatusCode) return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with status code {(int)ServerResponce.StatusCode}:{ServerResponce.StatusCode}" };
Tresult? temp = JsonSerializer.Deserialize(ServerResponce.Content.ReadAsStreamAsync(CancellationToken).Result, Type);
if (temp is null) return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with empty data" };
return temp;
}
internal static async Task<Tresult> SendServer<Tvalue, Tresult>(SocketServer Server, string Path, Tvalue Payload, JsonTypeInfo<Tvalue> jsonTypeInfo, JsonTypeInfo<Tresult> ReturnjsonTypeInfo, CancellationToken CancellationToken, params KeyValuePair<string, string?>[] 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<string, string?> header in Headers) web.DefaultRequestHeaders.Add(header.Key, header.Value);
HttpResponseMessage ServerResponce = web.PostAsJsonAsync($"https://{Server.Domain}/{Server.ApiVersion}/{Path}", Payload, jsonTypeInfo, CancellationToken).Result;
if (!ServerResponce.IsSuccessStatusCode) return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with status code {(int)ServerResponce.StatusCode}:{ServerResponce.StatusCode}" };
Tresult error = new() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with empty data" };
if (string.IsNullOrWhiteSpace(ServerResponce.Content.ReadAsStringAsync(CancellationToken).Result)) return error;
try
{
Tresult? temp = JsonSerializer.Deserialize(ServerResponce.Content.ReadAsStreamAsync(CancellationToken).Result, ReturnjsonTypeInfo);
if (temp is null) return error;
return temp;
}
catch { return error; }
}
internal static async Task<Tresult> SendServer<Tresult>(SocketServer Server, string Path, string File, JsonTypeInfo<Tresult> ReturnjsonTypeInfo, CancellationToken CancellationToken, params KeyValuePair<string, string?>[] Headers) where Tresult : IncomingHTTP, new()
{
var fs = System.IO.File.OpenRead(File);
try
{
using HttpClient web = new();
if (!login) web.DefaultRequestHeaders.Add("token", Token);
web.Timeout = new TimeSpan(0, 10, 0);
if (Headers is not null && Headers.Length > 0) foreach (KeyValuePair<string, string?> header in Headers) web.DefaultRequestHeaders.Add(header.Key, header.Value);
HttpResponseMessage ServerResponce = web.PostAsync($"https://{Server.Domain}/{Server.ApiVersion}/{Path}", new StreamContent(fs), CancellationToken).Result;
if (!ServerResponce.IsSuccessStatusCode) return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with status code {(int)ServerResponce.StatusCode}:{ServerResponce.StatusCode}" };
try
{
Tresult? temp = JsonSerializer.Deserialize(ServerResponce.Content.ReadAsStreamAsync(CancellationToken).Result, ReturnjsonTypeInfo);
if (temp is null) return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with empty data" };
return temp;
}
catch { return new Tresult() { StatusCode = ServerResponce.StatusCode, Error = ErrorCode.ServerError, ErrorMessage = $"Server responded with empty data" }; }
}
finally
{
fs.Close();
}
}
}
*/

118
Luski.net/Structures/File.cs Executable file
View File

@ -0,0 +1,118 @@
using Luski.net.Enums;
using Luski.net.JsonTypes.BaseTypes;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using JacobTechEncryption;
using Luski.net.Interfaces;
using Luski.Shared.PublicServers.V1.ServerToClient.HTTP;
namespace Luski.net.Structures;
public class File : STC
{
public MainServer Server { get; internal set; }
[JsonInclude]
[JsonPropertyName("name")]
public string Name { get; internal set; } = default!;
[JsonInclude]
[JsonPropertyName("size")]
public ulong Size { get; internal set; } = default!;
[JsonInclude]
[JsonPropertyName("id")]
public long Id { get; internal set; } = default!;
[JsonIgnore]
internal string? key { get; set; } = default!;
[JsonIgnore]
internal string loc { get; set; } = default!;
public async Task DownloadBytes(string Loc, long key, CancellationToken CancellationToken)
{
//TODO make better
//using HttpClient web = new();
//web.DefaultRequestHeaders.Add("token", Server.Token);
//web.DefaultRequestHeaders.Add("id", id.ToString());
//IncomingHTTP? request = JsonSerializer.Deserialize(web.GetAsync($"https://{Server.Domain}/{Server.API_Ver}/SocketMessage/GetFile").Result.Content.ReadAsStringAsync().Result, IncomingHTTPContext.Default.IncomingHTTP);
string path = Path.GetTempFileName();
await Server.GetFromServer($"SocketMessage/GetFile/{Id}", path, CancellationToken);
string Key = (key == 0 ? Server.EncryptionHandler.MyPublicKey : "") ;// ClientEncryption.File.Channels.GetKey(key))!;
Encryption.AES.DecryptToFile(System.IO.File.ReadAllBytes(path), Key, Loc);
/*
if (request is not null && request.Error is not null)
{
switch (request.Error)
{
case ErrorCode.InvalidToken:
throw new Exception("Your current token is no longer valid");
case ErrorCode.ServerError:
throw new Exception("Error from server: " + request.ErrorMessage);
case ErrorCode.Forbidden:
throw new Exception("Your request was denied by the server");
default:
MemoryStream? ms = new();
JsonSerializer.Serialize(new Utf8JsonWriter(ms),
request,
IncomingHTTPContext.Default.IncomingHTTP);
throw new Exception(Encoding.UTF8.GetString(ms.ToArray()));
}
}
if (request?.data is not null)
{
foreach (string raw in request.data)
{
Encryption.AES.Decrypt(Convert.FromBase64String(raw), Encryption.File.Channels.GetKey(key), Loc);
}
}*/
}
public void SetFile(string path)
{
FileInfo fi = new(path);
Name = fi.Name;
Size = (ulong)fi.Length;
loc = path;
}
private bool Uploaded = false;
internal async Task<long> Upload(string keyy, CancellationToken CancellationToken)
{
if (Uploaded) return Id;
if (Name != null) Name = Convert.ToBase64String(Encryption.RSA.Encrypt(Encoding.UTF8.GetBytes(Name), keyy));
string NPath = Encryption.AES.EncryptFile(loc, keyy);
File sf = await Server.SendServer(
"SocketMessage/UploadFile",
NPath,
FileContext.Default.File,
CancellationToken,
new KeyValuePair<string, string?>("name", Name));
try { System.IO.File.Delete(NPath); } catch { }
if (sf.Error is not null) throw new Exception(sf.ErrorMessage);
Id = sf.Id;
Uploaded = true;
return sf.Id;
}
internal void decrypt()
{
if (Name is not null) Name = Encoding.UTF8.GetString(Encryption.RSA.Decrypt(Convert.FromBase64String(Name), key));
}
}
[JsonSerializable(typeof(File))]
internal partial class FileContext : JsonSerializerContext
{
}

View File

@ -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!;
}

View File

@ -0,0 +1,201 @@
using System;
using Luski.net.Interfaces;
using Luski.net.JsonTypes.BaseTypes;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
using System.Threading;
using System.Threading.Tasks;
using JacobTechEncryption;
using Luski.net.Enums;
using Luski.net.Enums.Main;
using Luski.net.JsonTypes;
namespace Luski.net.Structures.Main;
public class MainSocketAppUser : MainSocketUserBase, IAppUser
{
[JsonPropertyName("selected_channel")]
[JsonInclude]
public long SelectedChannel { get; internal set; } = default!;
[JsonPropertyName("email")]
[JsonInclude]
public string Username { get; internal set; } = default!;
[JsonPropertyName("flags")]
[JsonInclude]
public UserFlag Flags { get; internal set; } = default!;
[JsonIgnore]
public IReadOnlyList<MainSocketChannel> Channels
{
get
{
if (_Channels is null && ChannelIdList is not null)
{
if (ChannelIdList.Length != 0)
{
_Channels = new List<MainSocketChannel>();
foreach (long channel in ChannelIdList)
{
try
{
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);
}
}
}
else _Channels = new List<MainSocketChannel>();
}
return _Channels.AsReadOnly();
}
}
[JsonIgnore]
public IReadOnlyList<MainSocketRemoteUser> FriendRequests
{
get
{
if (_FriendRequests is null || FriendRequestsRaw is not null)
{
_FriendRequests = new();
if (ChannelIdList.Length != 0 && FriendRequestsRaw is not null)
{
foreach (FR person in FriendRequestsRaw)
{
//_Friends.Add(SocketRemoteUser.GetUser(person));
long id = person.user_id == Id ? person.from : person.user_id;
MainSocketRemoteUser frq = Server.GetUser<MainSocketRemoteUser>(id, CancellationToken.None).Result;
_FriendRequests.Add(frq);
}
}
else _FriendRequests = new();
}
return _FriendRequests.AsReadOnly();
}
}
[JsonIgnore]
public IReadOnlyList<MainSocketRemoteUser> Friends
{
get
{
if (_Friends is null || FriendIdList is not null)
{
if (ChannelIdList.Length != 0)
{
_Friends = new List<MainSocketRemoteUser>();
foreach (long person in FriendIdList)
{
_Friends.Add(Server.GetUser<MainSocketRemoteUser>(person, CancellationToken.None).Result);
}
}
else _Friends = new List<MainSocketRemoteUser>();
}
return _Friends.AsReadOnly();
}
}
[JsonPropertyName("channels")]
[JsonInclude]
public long[] ChannelIdList { get; internal set; } = default!;
[JsonPropertyName("friends")]
[JsonInclude]
public long[] FriendIdList { get; internal set; } = default!;
[JsonPropertyName("friend_requests")]
[JsonInclude]
public FR[] FriendRequestsRaw { get; internal set; } = default!;
[JsonIgnore]
private List<MainSocketChannel> _Channels = null!;
[JsonIgnore]
private List<MainSocketRemoteUser> _Friends = default!;
[JsonIgnore]
private List<MainSocketRemoteUser> _FriendRequests = default!;
public class FR
{
public long from { get; set; } = default!;
public long user_id { get; set; } = default!;
}
internal void AddFriend(MainSocketRemoteUser User)
{
if (Server.poeople.Any(s => s.Id == User.Id))
{
IEnumerable<IUser> b = Server.poeople.Where(s => s.Id == User.Id);
foreach (IUser item in b)
{
Server.poeople.Remove(item);
}
Server.poeople.Add(User);
}
else
{
Server.poeople.Add(User);
}
_Friends.Add(User);
}
internal void RemoveFriendRequest(MainSocketRemoteUser User)
{
if (Server.poeople.Any(s => s.Id == User.Id))
{
IEnumerable<IUser> b = Server.poeople.Where(s => s.Id == User.Id);
foreach (IUser item in b)
{
Server.poeople.Remove(item);
}
}
Server.poeople.Add(User);
foreach (MainSocketRemoteUser user in _FriendRequests)
{
if (User.Id == user.Id)
{
_FriendRequests.Remove(User);
}
}
}
internal void AddFriendRequest(MainSocketRemoteUser User)
{
if (Server.poeople.Any(s => s.Id == User.Id))
{
IEnumerable<IUser> b = Server.poeople.Where(s => s.Id == User.Id);
foreach (IUser item in b)
{
Server.poeople.Remove(item);
}
Server.poeople.Add(User);
}
else
{
Server.poeople.Add(User);
}
_FriendRequests.Add(User);
}
}
[JsonSerializable(typeof(MainSocketAppUser))]
[JsonSourceGenerationOptions(
GenerationMode = JsonSourceGenerationMode.Default,
PropertyNamingPolicy = JsonKnownNamingPolicy.Unspecified,
WriteIndented = false,
DefaultIgnoreCondition = JsonIgnoreCondition.Never)]
internal partial class MainSocketAppUserContext : JsonSerializerContext
{
}

View File

@ -0,0 +1,145 @@
using Luski.net.Enums;
using Luski.net.Interfaces;
using Luski.net.JsonTypes.BaseTypes;
using System.Collections.Generic;
using System;
using System.Text.Json.Serialization;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Luski.net.JsonTypes.WSS;
using System.Text.Json.Serialization.Metadata;
using System.Threading;
using JacobTechEncryption;
using JacobTechEncryption.Enums;
using Luski.net.Enums.Main;
using Luski.net.JsonTypes;
using Luski.Shared.PublicServers.V1.ServerToClient.HTTP;
namespace Luski.net.Structures.Main;
public class MainSocketChannel : STC
{
[JsonInclude]
[JsonPropertyName("id")]
public long Id { get; internal set; } = default!;
[JsonInclude]
[JsonPropertyName("title")]
public string Title { get; internal set; } = default!;
[JsonInclude]
[JsonPropertyName("description")]
public string Description { get; internal set; } = default!;
[JsonInclude]
[JsonPropertyName("key")]
public string Key { get; internal set; } = default!;
[JsonPropertyName("type")]
[JsonInclude]
public ChannelType Type { get; internal set; } = default!;
public MainServer Server { get; internal set; } = default!;
[JsonPropertyName("members")]
[JsonInclude]
public long[] MemberIdList { get; internal set; } = default!;
[JsonIgnore]
public IReadOnlyList<IUser> Members
{
get
{
if (MemberIdList is null || MemberIdList.Length == 0) return Array.Empty<IUser>().ToList().AsReadOnly();
if (_members is null || !_members.Any())
{
_members = new();
foreach (long member in MemberIdList)
{
if (member != Server.User.Id) _members.Add(Server.GetUser<MainSocketRemoteUser>(member, CancellationToken.None).Result);
else _members.Add(Server.User);
}
}
return _members.AsReadOnly();
}
}
[JsonIgnore]
private List<IUser> _members = new();
public Task SendKeysToUsers(CancellationToken CancellationToken)
{
if (Key is null)
{
StartKeyProcessAsync(CancellationToken).Wait();
return Task.CompletedTask;
}
int num = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * 50) * 2.0));
if (num == 0) num = 1;
string lkey = Server.EncryptionHandler.GetChannelKey(Id);
Parallel.ForEach(Members, new ParallelOptions()
{
MaxDegreeOfParallelism = num
}, i =>
{
if (i.Id != Server.User.Id)
{
long key = i.GetUserKeys(CancellationToken).Result.First().Id;
if (true)
{
WSSKeyExchange send = new()
{
to = i.Id,
channel = Id,
key = Convert.ToBase64String(Encryption.RSA.Encrypt(lkey, key.ToString(), EncoderType.UTF8))
};
Server.SendServerOld(send, WSSKeyExchangeContext.Default.WSSKeyExchange);
}
}
});
return Task.CompletedTask;
}
internal Task StartKeyProcessAsync(CancellationToken CancellationToken)
{
//TODO code key exchange
/*
ClientEncryption.GenerateNewKeys(out string Public, out string Private);
Key = Public;
HttpResponseMessage b;
using (HttpClient web = new())
{
web.DefaultRequestHeaders.Add("token", Luski.net.Server.Token);
b = web.PostAsync($"https://{Server.Domain}/{Server.ApiVersion}/SocketChannel/SetKey/{Id}", new StringContent(Key), CancellationToken).Result;
}
int num = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * Luski.net.Server.Percent) * 2.0));
if (num == 0) num = 1;
Encryption.File.Channels.AddKey(Id, Private);
Parallel.ForEach(Members, new ParallelOptions()
{
MaxDegreeOfParallelism = num
}, i =>
{
if (i.Id != Luski.net.Server._user?.Id)
{
string key = i.GetUserKey(CancellationToken, Server).Result;
if (!string.IsNullOrEmpty(key))
{
WSSKeyExchange send = new()
{
to = i.Id,
channel = Id,
key = Convert.ToBase64String(Encryption.Encrypt(Private, key))
};
Luski.net.Server.SendServer(send, WSSKeyExchangeContext.Default.WSSKeyExchange);
}
}
});*/
return Task.CompletedTask;
}
}
[JsonSerializable(typeof(MainSocketChannel))]
[JsonSourceGenerationOptions(
GenerationMode = JsonSourceGenerationMode.Default,
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
WriteIndented = false)]
internal partial class MainSocketChannelContext : JsonSerializerContext
{
}

View File

@ -0,0 +1,34 @@
using Luski.net.JsonTypes.BaseTypes;
using System.Linq;
using System.Text.Json.Serialization;
using System.Threading;
namespace Luski.net.Structures.Main;
public class MainSocketDMChannel : MainSocketTextChannel
{
public MainSocketRemoteUser User
{
get
{
if (_user is null)
{
var list = MemberIdList.ToList();
list.Remove(Server.User.Id);
_user = Server.GetUser<MainSocketRemoteUser>(list.FirstOrDefault(), CancellationToken.None).Result;
}
return _user;
}
}
public MainSocketRemoteUser _user = null!;
}
[JsonSerializable(typeof(MainSocketDMChannel))]
[JsonSourceGenerationOptions(
GenerationMode = JsonSourceGenerationMode.Default,
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
WriteIndented = false)]
internal partial class MainSocketDMChannelContext : JsonSerializerContext
{
}

View File

@ -0,0 +1,20 @@
using System.Text.Json.Serialization;
namespace Luski.net.Structures.Main;
public class MainSocketGroupChannel : MainSocketTextChannel
{
[JsonPropertyName("owner")]
[JsonInclude]
public long Owner { get; internal set; } = default!;
}
[JsonSerializable(typeof(MainSocketGroupChannel))]
[JsonSourceGenerationOptions(
GenerationMode = JsonSourceGenerationMode.Default,
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
WriteIndented = false)]
internal partial class MainSocketGroupChannelContext : JsonSerializerContext
{
}

View File

@ -0,0 +1,60 @@
using Luski.net.Interfaces;
using Luski.net.JsonTypes.BaseTypes;
using System;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using JacobTechEncryption;
using Luski.Shared.PublicServers.V1.ServerToClient.HTTP;
namespace Luski.net.Structures.Main;
public class MainSocketMessage : STC
{
public MainServer Server { get; internal set; } = default!;
[JsonPropertyName("id")]
[JsonInclude]
public long Id { get; internal set; } = default!;
[JsonPropertyName("user_id")]
[JsonInclude]
public long AuthorID { get; internal set; } = default!;
[JsonPropertyName("content")]
[JsonInclude]
public string Context { get; internal set; } = default!;
[JsonPropertyName("channel_id")]
[JsonInclude]
public long ChannelID { get; internal set; } = default!;
[JsonPropertyName("files")]
[JsonInclude]
public File[]? Files { get; internal set; } = default!;
public async Task<IUser> GetAuthor(CancellationToken CancellationToken)
{
if (Server.User.Id != AuthorID) return await Server.GetUser<MainSocketUserBase>(AuthorID, CancellationToken);
else return Server.User;
}
internal void decrypt(string? key, CancellationToken CancellationToken)
{
if (string.IsNullOrEmpty(key)) throw new ArgumentNullException(nameof(key));
Context = Encoding.UTF8.GetString(Encryption.AES.Decrypt(Convert.FromBase64String(Context), key));
if (Files is not null && Files.Length > 0)
{
for (int i = 0; i < Files.Length; i++)
{
Files[i].key = key;
Files[i].decrypt();
}
}
}
}
[JsonSerializable(typeof(MainSocketMessage))]
[JsonSourceGenerationOptions(
GenerationMode = JsonSourceGenerationMode.Default,
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
WriteIndented = false)]
public partial class MainSocketMessageContext : JsonSerializerContext
{
}

View File

@ -0,0 +1,37 @@
using Luski.net.Enums;
using Luski.net.Interfaces;
using Luski.net.JsonTypes.BaseTypes;
using System;
using System.Linq;
using System.Net.Http;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using Luski.net.Enums.Main;
using Luski.net.Structures;
using Luski.net.Structures.Main;
namespace Luski.net.Structures.Main;
public class MainSocketRemoteUser : MainSocketUserBase
{
[JsonPropertyName("friend_status")]
[JsonInclude]
public FriendStatus FriendStatus { get; internal set; } = default!;
public MainSocketDMChannel Channel { get; internal set; }
internal MainSocketRemoteUser Clone()
{
return (MainSocketRemoteUser)MemberwiseClone();
}
}
[JsonSerializable(typeof(MainSocketRemoteUser))]
[JsonSourceGenerationOptions(
GenerationMode = JsonSourceGenerationMode.Default,
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
WriteIndented = false)]
internal partial class MainSocketRemoteUserContext : JsonSerializerContext
{
}

View File

@ -0,0 +1,181 @@
using Luski.net.Enums;
using Luski.net.Interfaces;
using Luski.net.JsonTypes.BaseTypes;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using JacobTechEncryption;
using JacobTechEncryption.Enums;
using Luski.Shared.PublicServers.V1.ClientToServer.HTTP;
using Luski.Shared.PublicServers.V1.Enums;
using Luski.Shared.PublicServers.V1.ServerToClient.HTTP;
using ChannelType = Luski.net.Enums.Main.ChannelType;
namespace Luski.net.Structures.Main;
public class MainSocketTextChannel : MainSocketChannel
{
public async Task<MainSocketMessage> GetMessage(long ID, CancellationToken CancellationToken)
{
return await Server.GetMessage(ID, CancellationToken);
}
public async Task<Stream> GetPicture(CancellationToken CancellationToken)
{
throw new NotImplementedException();
//if (Type == ChannelType.DM) return Members.First().GetAvatar(CancellationToken).Result;
// else
{
bool isc = System.IO.File.Exists(Server.Storage.GetStorageDirectory(StorageDirectory.ChannelIcons) + Id.ToString());
if (!isc) await Server.GetFromServer($"SocketChannel/GetPicture/{Id}", Server.Storage.GetStorageDirectory(StorageDirectory.ChannelIcons) + Id.ToString(), CancellationToken);
return Server.Storage.GetResourceStream(StorageDirectory.ChannelIcons, Id.ToString());
}
}
public async Task<IReadOnlyList<MainSocketMessage>> GetMessages(long Message_Id, CancellationToken CancellationToken, int count = 50)
{
if (count > 200)
{
throw new Exception("You can not request more than 200 messages at a time");
}
else if (count < 1)
{
throw new Exception("You must request at least 1 message");
}
else
{
SocketBulkMessage data = await Server.GetFromServer("SocketBulkMessage",
SocketBulkMessageContext.Default.SocketBulkMessage,
CancellationToken,
new KeyValuePair<string, string?>("channel_id", Id.ToString()),
new KeyValuePair<string, string?>("messages", count.ToString()),
new KeyValuePair<string, string?>("mostrecentid", Message_Id.ToString()));
if (data.Error is null)
{
int num = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * 5) * 2.0));
if (num == 0) num = 1;
string key = Server.EncryptionHandler.GetChannelKey(Id);
if (data is null) throw new Exception("Invalid data from server");
if (data.Messages is null) data.Messages = Array.Empty<MainSocketMessage>();
Parallel.ForEach(data.Messages, new ParallelOptions()
{
MaxDegreeOfParallelism = num
}, i =>
{
i.decrypt(key, CancellationToken);
});
key = null;
return await Task.FromResult(data.Messages.ToList().AsReadOnly() as IReadOnlyList<MainSocketMessage>);
}
else
{
throw new Exception(data.ErrorMessage);
}
}
}
public async Task<IReadOnlyList<MainSocketMessage>> 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
{
DateTime start = DateTime.Now;
SocketBulkMessage data = await Server.GetFromServer("SocketBulkMessage",
SocketBulkMessageContext.Default.SocketBulkMessage,
CancellationToken,
new KeyValuePair<string, string?>("id", Id.ToString()),
new KeyValuePair<string, string?>("messages", count.ToString()));
Console.WriteLine($"Messages downloaded in {(DateTime.Now - start).TotalSeconds}");
start = DateTime.Now;
if (data is not null && !data.Error.HasValue)
{
int num = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * 5) * 2.0));
if (num == 0) num = 1;
string key = Server.EncryptionHandler.GetChannelKey(Id);
if (data.Messages is null) data.Messages = Array.Empty<MainSocketMessage>();
Parallel.ForEach(data.Messages, new ParallelOptions()
{
MaxDegreeOfParallelism = num
}, i =>
{
i.decrypt(key, CancellationToken);
});
key = null;
Console.WriteLine($"Messages decrypted in {(DateTime.Now - start).TotalSeconds}");
return await Task.FromResult(data.Messages.ToList().AsReadOnly() as IReadOnlyList<MainSocketMessage>);
}
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<Task> SendMessage(string Message, CancellationToken CancellationToken, params File?[] Files)
{
string key = Server.EncryptionHandler.GetChannelKey(Id);
MessageCTS m = new()
{
Base64Context = Convert.ToBase64String(Encryption.RSA.Encrypt(Message, key, EncoderType.UTF8)),
ChannelID = Id,
Profile = null,
EncryptionKey = 0,
Encoding = EncoderType.UTF8
};
if (Files is not null && Files.Length > 0)
{
List<long> bb = new();
for (int i = 0; i < Files.Length; i++)
{
File? ff = Files[i];
if (ff is not null)
{
bb.Add(await ff.Upload(key, CancellationToken));
Files[i] = null;
}
}
m.Files = bb.ToArray();
}
STC data = await Server.SendServer("socketmessage", m, MessageCTSContext.Default.MessageCTS, STCContext.Default.STC, CancellationToken);
if (data.Error is not null && data.ErrorMessage != "Server responded with empty data") throw new Exception(data.ErrorMessage);
return Task.CompletedTask;
}
}
[JsonSerializable(typeof(MainSocketTextChannel))]
[JsonSourceGenerationOptions(
GenerationMode = JsonSourceGenerationMode.Default,
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
WriteIndented = false)]
internal partial class MainSocketTextChannelContext : JsonSerializerContext
{
}

View File

@ -0,0 +1,57 @@
using System;
using System.IO;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using Luski.net.Enums;
using Luski.net.Interfaces;
using Luski.net.Structures.Public;
using Luski.Shared.PublicServers.V1.Enums;
using Luski.Shared.PublicServers.V1.ServerToClient.HTTP;
namespace Luski.net.Structures.Main;
public class MainSocketUserBase : STC, IUser
{
public MainServer 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 Task<Color> GetColor()
{
throw new NotImplementedException();
}
public async Task<Stream> 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<PublicKeyInfo[]> GetUserKeys(CancellationToken CancellationToken)
{
string data = Server.GetFromServer($"Keys/GetUserKey/{Id}", CancellationToken).Content.ReadAsStringAsync().Result;
return Task.FromResult(new[] { new PublicKeyInfo() { Id = long.Parse(data) }});
}
}
[JsonSerializable(typeof(MainSocketUserBase))]
[JsonSourceGenerationOptions(
GenerationMode = JsonSourceGenerationMode.Default,
PropertyNamingPolicy = JsonKnownNamingPolicy.Unspecified,
WriteIndented = false,
DefaultIgnoreCondition = JsonIgnoreCondition.Never)]
internal partial class MainSocketUserBaseContext : JsonSerializerContext
{
}

View File

@ -0,0 +1,23 @@
using System.Text.Json.Serialization;
using Luski.Shared.PublicServers.V1.ServerToClient.HTTP;
namespace Luski.net.Structures.Main;
internal class SocketBulkMessage : STC
{
[JsonPropertyName("messages")]
[JsonInclude]
public MainSocketMessage[]? Messages { get; set; } = default!;
}
[JsonSerializable(typeof(SocketBulkMessage))]
[JsonSourceGenerationOptions(
GenerationMode = JsonSourceGenerationMode.Default,
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
WriteIndented = false,
DefaultIgnoreCondition = JsonIgnoreCondition.Never)]
internal partial class SocketBulkMessageContext : JsonSerializerContext
{
}

View File

@ -0,0 +1,60 @@
using System;
namespace Luski.net.Structures.Public;
public struct Color
{
public Color(string servercol)
{
byte[] t = Convert.FromHexString(servercol);
r = t[0];
g = t[1];
b = t[2];
a = t[3];
}
public static bool operator ==(Color a, Color b)
{
return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a;
}
public static bool operator !=(Color a, Color b)
{
return a.r != b.r || a.g != b.g || a.b != b.b || a.a != b.a;
}
public Color(byte R, byte G, byte B, byte A)
{
r = R;
g = G;
b = B;
a = A;
}
public string ToDatabaseStr()
{
return Convert.ToHexString(new []{r,g,b,a});
}
private byte r;
private byte g;
private byte b;
private byte a;
public byte A
{
get => a;
}
public byte R
{
get => r;
}
public byte G
{
get => g;
}
public byte B
{
get => b;
}
}

View File

@ -0,0 +1,44 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Luski.Shared.PublicServers.V1.Enums;
namespace Luski.net.Structures.Public;
public class Role
{
public required PublicServer Server { get; init; } = default!;
public required long ID { get; init; } = default!;
public int Index { get; internal set; } = default!;
public ColorType ColorType { get; internal set; } = ColorType.Full;
public Color[] Colors { get; internal set; } = default!;
public string Description { get; internal set; } = default!;
public string DisplayName { get; internal set; } = default!;
public ServerPermission ServerPermissions { get; internal set; } = default!;
public long[] MLID = default!;
public long[] MembersListID
{
get => MLID;
internal set
{
MLID = value;
RawUsers = null;
}
}
private List<SocketUser>? RawUsers = null;
public async Task<SocketUser[]> GetMembers()
{
if (RawUsers is null || RawUsers.Count != MembersListID.Length)
{
RawUsers = new();
foreach (long m in MembersListID)
{
RawUsers.Add(await Server.GetUser<SocketUser>(m, CancellationToken.None));
}
}
return RawUsers!.ToArray();
}
}

View File

@ -0,0 +1,21 @@
using System.Threading.Tasks;
using Luski.Shared.PublicServers.V1.Enums;
using Luski.Shared.PublicServers.V1.Shared;
namespace Luski.net.Structures.Public;
public class RoleOverride
{
public long ParentRoleID { get; init; }
public ServerPermission GoodPermissions { get; set; }
public ServerPermission BadPermissions { get; set; }
public required PublicServer Server { get; init; }
public async Task<Role> GetRole()
{
return await Server.GetRole(ParentRoleID);
}
}

View File

@ -0,0 +1,42 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Luski.net.Enums;
using Luski.Shared.PublicServers.V1.Enums;
namespace Luski.net.Structures.Public;
public class ServerProfile
{
public PublicServer Server { get; init; } = default!;
public long ID { get; init; } = default!;
public string DisplayName { get; init; } = default!;
public PictureType? PictureType { get; init; } = default!;
public long[] Controllers { get; internal set; } = default!;
public long[] RoleControllers { get; internal set; } = default!;
internal ColorType? ColorType { get; set; } = default!;
internal Color[]? Color { get; set; } = default!;
public Task<Color[]?> GetColors()
{
return Task.FromResult(Color);
}
public Task<ColorType?> GetColorType()
{
return Task.FromResult(ColorType);
}
public async Task<Stream> GetAvatar(CancellationToken CancellationToken)
{
bool isc = System.IO.File.Exists(Server.Storage.GetStorageDirectory(StorageDirectory.ProfileAvatars) + ID.ToString());
if (!isc) await Server.GetFromServer($"socketprofile/Avatar/{ID}", Server.Storage.GetStorageDirectory(StorageDirectory.ProfileAvatars) + ID.ToString(), CancellationToken);
return Server.Storage.GetResourceStream(StorageDirectory.ProfileAvatars, ID.ToString());
}
public Task<PublicKeyInfo[]> GetUserKeys(CancellationToken CancellationToken)
{
throw new NotImplementedException();
}
}

View File

@ -0,0 +1,78 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Luski.Shared.PublicServers.V1.Enums;
using Luski.Shared.PublicServers.V1.Shared;
namespace Luski.net.Structures.Public;
public class SocketAppUser : SocketUser
{
public long SelectedChannel { get; init; } = default!;
private List<ServerProfile>? _serverProfiles;
public async Task<SocketChannel> GetSelectedChannel(CancellationToken Token)
{
return await Server.GetChannel<SocketChannel>(SelectedChannel, Token);
}
public async Task<ServerProfile[]> GetProfiles(CancellationToken Token)
{
if (_serverProfiles is null)
{
_serverProfiles = (await Server.GetMyProfiles(Token)).ToList();
}
return _serverProfiles.ToArray();
}
public async Task<bool> HasAccessToCategory(SocketCategory Category, ServerPermission RequiredPerms = ServerPermission.ViewThis)
{
if (Category.Server != Server) return false;
if (Server.OwnerID == Id) return true;
Role[] UserRoleIDList = await GetRoles();
RequiredPerms |= ServerPermission.ViewThis;
ServerPermission GoodPerms = ServerPermission.None;
UserOverride[] CatUserOverides = await Category.GetUserOverrides();
foreach (UserOverride CatUserOveride in CatUserOverides)
{
if (CatUserOveride.UserID != Id) continue;
if ((CatUserOveride.BadPermissions & RequiredPerms) > ServerPermission.None) return false;
if ((CatUserOveride.GoodPermissions & RequiredPerms) == RequiredPerms) return true;
GoodPerms |= CatUserOveride.GoodPermissions;
break;
}
RoleOverride[] CatRoleOverides = await Category.GetRoleOverrides();
int bad_index = -1;
int good_index = -1;
foreach (RoleOverride CatRoleOveride in CatRoleOverides)
{
if (!RoleIds.Contains(CatRoleOveride.ParentRoleID)) continue;
var index = (await CatRoleOveride.GetRole()).Index;
if ((CatRoleOveride.BadPermissions & RequiredPerms) > ServerPermission.None)
{
if (bad_index < index)
bad_index = index;
}
else good_index = index;
GoodPerms |= CatRoleOveride.GoodPermissions;
}
if (bad_index > good_index) return false;
foreach (Role RoleID in UserRoleIDList)
{
GoodPerms |= RoleID.ServerPermissions;
}
return GoodPerms.HasPermission(RequiredPerms);
}
public string Username { get; internal set; } = default!;
}

View File

@ -0,0 +1,107 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using JacobTechEncryption.Enums;
using Luski.Shared.PublicServers.V1.Enums;
using Luski.Shared.PublicServers.V1.ServerToClient.HTTP;
namespace Luski.net.Structures.Public;
public class SocketCategory
{
public PublicServer Server { get; init; } = default!;
public long ID { get; init; }
public ColorType ColorType { get; init; } = ColorType.Full;
public Color[] Colors { get; init; }
internal long ParentID { get; set; }
internal RoleOverrideSTC[] RoleOverides { get; set; }
internal UserOverrideSTC[] UserOverides { get; set; }
internal long[] Channels { get; set; }
internal long[] Categories { get; set; }
SocketCategory? RawParent = null;
List<RoleOverride>? RawRoleOverides = null;
List<UserOverride>? RawUserOverides = null;
List<SocketChannel>? RawChan = null;
List<SocketCategory>? RawCat = null;
public async Task<SocketCategory?> GetParent()
{
if (ParentID != -1 && RawParent is null)
{
RawParent = await Server.GetCategory<SocketCategory>(ParentID, CancellationToken.None);
}
return RawParent;
}
public async Task<SocketChannel[]> GetChannels()
{
if (RawChan is null)
{
RawChan = new();
foreach (long chan in Channels)
{
RawChan.Add(await Server.GetChannel<SocketChannel>(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<SocketChannel>(chan, CancellationToken.None));
}
}
return RawChan.ToArray();
}
public async Task<SocketCategory[]> GetCategories()
{
if (RawCat is null)
{
RawCat = new();
foreach (long chan in Categories)
{
RawCat.Add(await Server.GetCategory<SocketCategory>(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<SocketCategory>(chan, CancellationToken.None));
}
}
return RawCat.ToArray();
}
public string Name { get; internal set; }
public string Description { get; internal set; }
public Task<RoleOverride[]> GetRoleOverrides()
{
if (RawRoleOverides is null)
{
RawRoleOverides = new();
}
return Task.FromResult(RawRoleOverides!.ToArray());
}
public Task<UserOverride[]> GetUserOverrides()
{
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; }
}

View File

@ -0,0 +1,444 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using JacobTechEncryption;
using JacobTechEncryption.Enums;
using Luski.net.Enums;
using Luski.Shared.PublicServers.V1.Enums;
using Luski.Shared.PublicServers.V1.ServerToClient.HTTP;
namespace Luski.net.Structures.Public;
public class SocketChannel
{
public PublicServer Server { get; init; } = default!;
public ColorType ColorType { get; init; } = ColorType.Full;
public Color[] Colors { get; init; }
public long ID { get; internal set; }
internal long CategoryID { get; set; }
internal RoleOverrideSTC[] RoleOverrides { get; set; }
internal UserOverrideSTC[] UserOverrides { get; set; }
SocketCategory? RawParent = null;
List<RoleOverride>? RawRoleOverides = null;
List<UserOverride>? RawUserOverides = null;
public PictureType PictureType { get; internal set; }
public async Task<IReadOnlyList<SocketMessage>> GetMessages(CancellationToken CancellationToken, SocketMessage Last, int count = 50)
{
try
{
if (count > 200)
{
throw new Exception("You can not request more than 200 messages at a time");
}
else if (count < 1)
{
throw new Exception("You must request at least 1 message");
}
else
{
SocketBulkMessageSTC data = await Server.GetFromServer("SocketBulkMessage",
SocketBulkMessageSTCContext.Default.SocketBulkMessageSTC,
CancellationToken,
new KeyValuePair<string, string?>("id", ID.ToString()),
new KeyValuePair<string, string?>("mostrecentid", Last.ID.ToString()),
new KeyValuePair<string, string?>("messages", count.ToString()));
if (data is not null && !data.Error.HasValue)
{
int num = Convert.ToInt32(6);
if (num == 0) num = 1;
if (data.Messages is null) data.Messages = Array.Empty<MessageSTC>();
List<SocketMessage> mmmm = new();
ParallelLoopResult p = Parallel.ForEach(data.Messages, new ParallelOptions()
{
MaxDegreeOfParallelism = num
}, i =>
{
if (i.EncryptionKey == 0)
{
if (string.IsNullOrEmpty(i.Context))
{
i.Context = "";
}
else i.Context = Encryption.Generic.Encoders[(int)i.EncoderType]
.GetString(Convert.FromBase64String(i.Context));
}
else
{
LocalKeyInfo key = Server.EncryptionHandler.GetKey(i.EncryptionKey);
switch (key.EncryptionType)
{
case EncryptionType.RSA:
i.Context = Encryption.RSA.Decrypt(Convert.FromBase64String(i.Context), key.Key,
i.EncoderType);
break;
default:
i.Context = Encryption.Generic.Encoders[(int)i.EncoderType]
.GetString(Convert.FromBase64String(i.Context));
break;
}
}
if (i.Files.Length > 0)
{
for (int j = 0; j < i.Files.Length; j++)
{
if (i.Files[j].Key == 0)
{
if (string.IsNullOrEmpty(i.Files[j].Name))
{
i.Files[j].Name = "";
}
else i.Files[j].Name = Encryption.Generic.Encoders[(int)i.Files[j].NameEncoder]
.GetString(Convert.FromBase64String(i.Files[j].Name));
}
else
{
LocalKeyInfo key = Server.EncryptionHandler.GetKey(i.Files[j].NameKey);
switch (key.EncryptionType)
{
case EncryptionType.RSA:
i.Files[j].Name = Encryption.RSA.Decrypt(Convert.FromBase64String(i.Context), key.Key,
i.Files[j].NameEncoder);
break;
default:
i.Files[j].Name = Encryption.Generic.Encoders[(int)i.Files[j].NameEncoder]
.GetString(Convert.FromBase64String(i.Context));
break;
}
}
}
}
});
foreach (MessageSTC i in data.Messages)
{
var ff = new List<long>();
List<SocketFile> sf = new();
foreach (ServerFileInfoSTC v in i.Files)
{
sf.Add(new()
{
ID = v.ID,
Size = v.Size,
Name = v.Name,
Encoder = v.Encoder,
NameEncoder = v.NameEncoder,
Key = v.Key,
NameKey = v.NameKey,
Server = Server
});
ff.Add(v.ID);
}
mmmm.Add(new()
{
Server = Server,
ID = i.ID,
TimeStamp = i.Timestamp,
ChannelID = ID,
AuthorID = i.AuthorID,
Context = i.Context,
EncryptionKey = i.EncryptionKey,
EncoderType = i.EncoderType,
FileIDs = ff.ToArray(),
_Files = sf,
ProfileID = i.ProfileID
});
}
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<IReadOnlyList<SocketMessage>> GetMessages(CancellationToken CancellationToken, int count = 50)
{
try
{
if (count > 200)
{
throw new Exception("You can not request more than 200 messages at a time");
}
else if (count < 1)
{
throw new Exception("You must request at least 1 message");
}
else
{
SocketBulkMessageSTC data = await Server.GetFromServer("SocketBulkMessage",
SocketBulkMessageSTCContext.Default.SocketBulkMessageSTC,
CancellationToken,
new KeyValuePair<string, string?>("id", ID.ToString()),
new KeyValuePair<string, string?>("messages", count.ToString()));
if (data is not null && !data.Error.HasValue)
{
int num = Convert.ToInt32(6);
if (num == 0) num = 1;
if (data.Messages is null) data.Messages = Array.Empty<MessageSTC>();
List<SocketMessage> mmmm = new();
ParallelLoopResult p = Parallel.ForEach(data.Messages, new ParallelOptions()
{
MaxDegreeOfParallelism = num
}, i =>
{
if (i.EncryptionKey == 0)
{
if (string.IsNullOrEmpty(i.Context))
{
i.Context = "";
}
else i.Context = Encryption.Generic.Encoders[(int)i.EncoderType]
.GetString(Convert.FromBase64String(i.Context));
}
else
{
LocalKeyInfo key = Server.EncryptionHandler.GetKey(i.EncryptionKey);
switch (key.EncryptionType)
{
case EncryptionType.RSA:
i.Context = Encryption.RSA.Decrypt(Convert.FromBase64String(i.Context), key.Key,
i.EncoderType);
break;
default:
i.Context = Encryption.Generic.Encoders[(int)i.EncoderType]
.GetString(Convert.FromBase64String(i.Context));
break;
}
}
if (i.Files.Length > 0)
{
for (int j = 0; j < i.Files.Length; j++)
{
if (i.Files[j].Key == 0)
{
if (string.IsNullOrEmpty(i.Files[j].Name))
{
i.Files[j].Name = "";
}
else i.Files[j].Name = Encryption.Generic.Encoders[(int)i.Files[j].NameEncoder]
.GetString(Convert.FromBase64String(i.Files[j].Name));
}
else
{
LocalKeyInfo key = Server.EncryptionHandler.GetKey(i.Files[j].NameKey);
switch (key.EncryptionType)
{
case EncryptionType.RSA:
i.Files[j].Name = Encryption.RSA.Decrypt(Convert.FromBase64String(i.Context), key.Key,
i.Files[j].NameEncoder);
break;
default:
i.Files[j].Name = Encryption.Generic.Encoders[(int)i.Files[j].NameEncoder]
.GetString(Convert.FromBase64String(i.Context));
break;
}
}
}
}
});
foreach (MessageSTC i in data.Messages)
{
var ff = new List<long>();
List<SocketFile> sf = new();
foreach (ServerFileInfoSTC v in i.Files)
{
sf.Add(new()
{
ID = v.ID,
Size = v.Size,
Name = v.Name,
Encoder = v.Encoder,
NameEncoder = v.NameEncoder,
Key = v.Key,
NameKey = v.NameKey,
Server = Server
});
ff.Add(v.ID);
}
mmmm.Add(new()
{
Server = Server,
ID = i.ID,
ChannelID = ID,
AuthorID = i.AuthorID,
Context = i.Context,
TimeStamp = i.Timestamp,
EncryptionKey = i.EncryptionKey,
EncoderType = i.EncoderType,
FileIDs = ff.ToArray(),
_Files = sf,
ProfileID = i.ProfileID
});
}
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<SocketMessage> SendMessage(string msg, SocketMessage? ReplyTo = null,
ServerProfile? Profile = null, params string[] files)
{
return await Server.SendMessage(this, msg, ReplyTo, Profile, files);
}
public async Task<SocketMessage> SendMessage(string msg, SocketMessage? ReplyTo = null,
ServerProfile? Profile = null, params SocketFile[] files)
{
return await Server.SendMessage(this, msg, ReplyTo, Profile, files);
}
public async Task<Stream> 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<string, string?>("id", ID.ToString()));
return Server.Storage.GetResourceStream(StorageDirectory.ChannelIcons, ID.ToString());
}
public async Task<SocketCategory> GetParent()
{
if (CategoryID != -1 && RawParent is null)
{
RawParent = await Server.GetCategory<SocketCategory>(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<RoleOverride[]> GetRoleOverrides()
{
if (RawRoleOverides is null)
{
RawRoleOverides = new();
foreach (var ro in RoleOverrides)
{
RawRoleOverides.Add(new()
{
Server = this.Server,
ParentRoleID = ro.RoleID,
GoodPermissions = ro.GoodPermissions,
BadPermissions = ro.BadPermissions
});
}
}
return Task.FromResult(RawRoleOverides!.ToArray());
}
public Task<UserOverride[]> GetUserOverride()
{
if (RawUserOverides is null)
{
RawUserOverides = new();
}
return Task.FromResult(RawUserOverides!.ToArray());
}
public async Task<SocketUser[]> GetMembers()
{
ServerPermission req = ServerPermission.ViewThis;
List<long> GoodMembers = new();
List<long> GoodRoles = new();
List<long> BadRoles = new();
List<SocketUser> GoodPeople = new();
foreach (UserOverride cro in await GetUserOverride())
{
if ((cro.GoodPermissions & req) == req) GoodMembers.Add(cro.UserID);
}
foreach (RoleOverride ro in (await GetRoleOverrides()))
{
if ((ro.GoodPermissions & req) == req)
{
GoodRoles.Add(ro.ParentRoleID);
}
else if ((ro.BadPermissions & req) == req)
{
BadRoles.Add(ro.ParentRoleID);
}
}
bool bad = false;
foreach (Role Role in Server.roles.OrderBy(s => s.Index))
{
if (BadRoles.Contains(Role.ID))
{
bad = true;
}
if (bad && GoodRoles.Contains(Role.ID))
{
bad = false;
}
if (!bad)
{
foreach (var m in await Role.GetMembers())
{
var t = GoodPeople.Where(s => s.Id == m.Id);
if (t.Count() == 0) GoodPeople.Add(m);
}
}
}
foreach (long m in GoodMembers)
{
var t = GoodPeople.Where(s => s.Id == m);
if (t.Count() == 0) GoodPeople.Add(await Server.GetUser<SocketUser>(m, CancellationToken.None));
}
return GoodPeople.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; }
}

View File

@ -0,0 +1,28 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using JacobTechEncryption.Enums;
using Luski.net.Enums;
using Luski.Shared.PublicServers.V1.Enums;
namespace Luski.net.Structures.Public;
public class SocketFile
{
public required PublicServer Server { get; init; }
public required long ID { 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<Stream> GetCache(CancellationToken CancellationToken)
{
string d = Server.Storage.GetStorageDirectory(StorageDirectory.Files) + ID.ToString();
bool isc = System.IO.File.Exists(d);
if (!isc) await Server.GetFromServer($"socketfile?id={ID}", d, CancellationToken);
return Server.Storage.GetResourceStream(StorageDirectory.Files, ID.ToString());
}
}

View File

@ -0,0 +1,62 @@
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 required PublicServer Server { get; init; } = default!;
public required long ID { get; init; }
public required long TimeStamp { get; init; }
public required long ChannelID { get; init; }
public required long AuthorID { get; init; }
public required long ProfileID { get; init; }
public string Context { get; internal set; }
public required long EncryptionKey { get; init; }
public long[] FileIDs { get; internal set; }
public EncoderType EncoderType { get; internal set; }
private SocketChannel? RawParent;
private IUser? au;
private ServerProfile? ap;
internal List<SocketFile> _Files { get; set; } = new();
public IReadOnlyList<SocketFile> Files
{
get => _Files.AsReadOnly();
}
public async Task<SocketChannel> GetParent(CancellationToken token)
{
if (RawParent is null)
{
RawParent = await Server.GetChannel<SocketChannel>(ChannelID, token);
}
return RawParent;
}
public async Task<IUser> GetAuthor(CancellationToken token)
{
if (au is null)
{
if (AuthorID == Server.User.Id) au = Server.User;
else au = await Server.GetUser<SocketUser>(AuthorID, token);
}
return au;
}
public async Task<ServerProfile> GetProfile(CancellationToken token)
{
if (ap is null)
{
ap = await Server.GetProfile(ProfileID, token);
}
return ap;
}
}

View File

@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.IO;
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.Shared.PublicServers.V1.Enums;
using Luski.Shared.PublicServers.V1.ServerToClient.HTTP;
using Luski.Shared.PublicServers.V1.Shared;
namespace Luski.net.Structures.Public;
public class SocketUser : IUser
{
public PublicServer Server { get; init; } = default!;
public long Id { get; init; } = default!;
public virtual UserStatus Status { get; internal set; } = default!;
public long ServerProfile { get; init; }
public long[] RoleIds { get; init; } = default!;
private List<Role>? RawRoles = null;
public async Task<ColorType> GetColorType()
{
return (await GetRoles())[0].ColorType;
}
public async Task<Color[]> GetColors()
{
return (await GetRoles())[0].Colors;
}
public async Task<Role[]> 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<Stream> 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 async Task<bool> HasPermissions(ServerPermission RequiredPerms)
{
if (Server.OwnerID == Id) return true;
Role[] UserRoleIDList = await GetRoles();
ServerPermission op = ServerPermission.None;
foreach (Role RoleID in UserRoleIDList)
{
op |= RoleID.ServerPermissions;
}
return op.HasPermission(RequiredPerms);
}
public Task<PublicKeyInfo[]> GetUserKeys(CancellationToken CancellationToken)
{
KeysGetSTC data = Server.GetFromServer($"Keys/UserKeys/{Id}", KeysGetSTCContext.Default.KeysGetSTC, CancellationToken).Result;
List<PublicKeyInfo> pki = new();
foreach (KeyGetSTC key in data.Keys)
{
pki.Add(new()
{
Id = key.ID,
Owner = key.Owner,
EncryptionType = key.EncryptionType,
Data = Convert.FromBase64String(key.Data)
});
}
return Task.FromResult(pki.ToArray());
}
}

View File

@ -0,0 +1,14 @@
using Luski.Shared.PublicServers.V1.Enums;
using Luski.Shared.PublicServers.V1.Shared;
namespace Luski.net.Structures.Public;
public class UserOverride
{
public required long ID { get; init; } = default!;
public required long UserID { get; init; } = default!;
public ServerPermission GoodPermissions { get; set; }
public ServerPermission BadPermissions { get; set; }
}

View File

@ -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; }
}