Compare commits
15 Commits
652dcbd0c8
...
34633ad2e4
Author | SHA1 | Date | |
---|---|---|---|
34633ad2e4 | |||
f950957b76 | |||
42290dc983 | |||
82ccdd8307 | |||
e94151e814 | |||
97ed89b627 | |||
69168acd22 | |||
0740c6daca | |||
0349745944 | |||
9f641e7429 | |||
ea39d93cf5 | |||
c3bb39b21b | |||
d8fbf281d0 | |||
106d0d6078 | |||
|
2eb1abe526 |
25
Luski.net.sln
Executable file
25
Luski.net.sln
Executable 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
163
Luski.net/API.cs
Normal 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;
|
||||
}
|
||||
}
|
13
Luski.net/Classes/RoleComparer.cs
Normal file
13
Luski.net/Classes/RoleComparer.cs
Normal 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
458
Luski.net/ClientEncryption.cs
Executable 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
5
Luski.net/Converters.cs
Normal file
@ -0,0 +1,5 @@
|
||||
namespace Luski.net;
|
||||
|
||||
public static class Converters
|
||||
{
|
||||
}
|
8
Luski.net/Enums/CacheMode.cs
Normal file
8
Luski.net/Enums/CacheMode.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace Luski.net.Enums;
|
||||
|
||||
public enum CacheMode : byte
|
||||
{
|
||||
None,
|
||||
Encrypted,
|
||||
Unencrypted
|
||||
}
|
10
Luski.net/Enums/ConnectionStatus.cs
Normal file
10
Luski.net/Enums/ConnectionStatus.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace Luski.net.Enums;
|
||||
|
||||
public enum ConnectionStatus
|
||||
{
|
||||
FailedToConnect,
|
||||
CommunicationError,
|
||||
FailedToLogin,
|
||||
ConnectedNotLoggedIn,
|
||||
LoggedIn,
|
||||
}
|
18
Luski.net/Enums/DataType.cs
Normal file
18
Luski.net/Enums/DataType.cs
Normal 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
|
||||
}
|
7
Luski.net/Enums/Main/ChannelType.cs
Executable file
7
Luski.net/Enums/Main/ChannelType.cs
Executable file
@ -0,0 +1,7 @@
|
||||
namespace Luski.net.Enums.Main;
|
||||
|
||||
public enum ChannelType : short
|
||||
{
|
||||
DM,
|
||||
GROUP,
|
||||
}
|
9
Luski.net/Enums/Main/FriendStatus.cs
Executable file
9
Luski.net/Enums/Main/FriendStatus.cs
Executable file
@ -0,0 +1,9 @@
|
||||
namespace Luski.net.Enums.Main;
|
||||
|
||||
public enum FriendStatus
|
||||
{
|
||||
NotFriends,
|
||||
Friends,
|
||||
PendingOut,
|
||||
PendingIn
|
||||
}
|
11
Luski.net/Enums/Main/UserFlag.cs
Executable file
11
Luski.net/Enums/Main/UserFlag.cs
Executable 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
|
||||
}
|
9
Luski.net/Enums/ServerType.cs
Normal file
9
Luski.net/Enums/ServerType.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace Luski.net.Enums;
|
||||
|
||||
public enum ServerType : byte
|
||||
{
|
||||
Public = 0,
|
||||
Main = 1,
|
||||
PublicSub = 2,
|
||||
MainSub = 3
|
||||
}
|
15
Luski.net/Enums/StorageDirectory.cs
Normal file
15
Luski.net/Enums/StorageDirectory.cs
Normal 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
30
Luski.net/Exceptions.cs
Executable 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; }
|
||||
}
|
||||
}
|
||||
}
|
10
Luski.net/Interfaces/IAppUser.cs
Normal file
10
Luski.net/Interfaces/IAppUser.cs
Normal 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; }
|
||||
}
|
38
Luski.net/Interfaces/IServer.cs
Normal file
38
Luski.net/Interfaces/IServer.cs
Normal 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();
|
||||
|
||||
}
|
6
Luski.net/Interfaces/IServerEvent.cs
Normal file
6
Luski.net/Interfaces/IServerEvent.cs
Normal file
@ -0,0 +1,6 @@
|
||||
namespace Luski.net.Interfaces;
|
||||
|
||||
public interface IServerEvent
|
||||
{
|
||||
|
||||
}
|
29
Luski.net/Interfaces/IUser.cs
Executable file
29
Luski.net/Interfaces/IUser.cs
Executable 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);
|
||||
|
||||
}
|
6
Luski.net/Interfaces/IWebRequest.cs
Normal file
6
Luski.net/Interfaces/IWebRequest.cs
Normal file
@ -0,0 +1,6 @@
|
||||
namespace Luski.net.Interfaces;
|
||||
|
||||
public interface IWebRequest
|
||||
{
|
||||
|
||||
}
|
28
Luski.net/JsonRequest.cs
Executable file
28
Luski.net/JsonRequest.cs
Executable 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()}}}";
|
||||
}
|
||||
}
|
||||
}
|
12
Luski.net/JsonTypes/BaseTypes/HTTPRequest.cs
Executable file
12
Luski.net/JsonTypes/BaseTypes/HTTPRequest.cs
Executable 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!;
|
||||
}
|
24
Luski.net/JsonTypes/BaseTypes/IncomingWSS.cs
Executable file
24
Luski.net/JsonTypes/BaseTypes/IncomingWSS.cs
Executable 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
|
||||
{
|
||||
|
||||
}
|
28
Luski.net/JsonTypes/FriendRequestResult.cs
Executable file
28
Luski.net/JsonTypes/FriendRequestResult.cs
Executable 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
|
||||
{
|
||||
|
||||
}
|
25
Luski.net/JsonTypes/HTTP/FriendRequest.cs
Executable file
25
Luski.net/JsonTypes/HTTP/FriendRequest.cs
Executable 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
|
||||
{
|
||||
|
||||
}
|
25
Luski.net/JsonTypes/HTTP/FriendRequestResultOut.cs
Executable file
25
Luski.net/JsonTypes/HTTP/FriendRequestResultOut.cs
Executable 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
|
||||
{
|
||||
|
||||
}
|
11
Luski.net/JsonTypes/KeyExchange.cs
Executable file
11
Luski.net/JsonTypes/KeyExchange.cs
Executable 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!;
|
||||
}
|
||||
}
|
29
Luski.net/JsonTypes/LocalServerInfo.cs
Normal file
29
Luski.net/JsonTypes/LocalServerInfo.cs
Normal 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;
|
20
Luski.net/JsonTypes/OfflineKeyData.cs
Executable file
20
Luski.net/JsonTypes/OfflineKeyData.cs
Executable 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
|
||||
{
|
||||
|
||||
}
|
27
Luski.net/JsonTypes/ServerStorageInfo.cs
Normal file
27
Luski.net/JsonTypes/ServerStorageInfo.cs
Normal 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
|
||||
{
|
||||
|
||||
}
|
17
Luski.net/JsonTypes/StatusUpdate.cs
Executable file
17
Luski.net/JsonTypes/StatusUpdate.cs
Executable 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!;
|
||||
}
|
27
Luski.net/JsonTypes/WSS/IncomingWSS.cs
Executable file
27
Luski.net/JsonTypes/WSS/IncomingWSS.cs
Executable 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
|
||||
{
|
||||
|
||||
}
|
15
Luski.net/JsonTypes/WSS/ServerEvent.cs
Normal file
15
Luski.net/JsonTypes/WSS/ServerEvent.cs
Normal 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; }
|
||||
}
|
31
Luski.net/JsonTypes/WSS/WSSKeyExchange.cs
Executable file
31
Luski.net/JsonTypes/WSS/WSSKeyExchange.cs
Executable 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
|
||||
{
|
||||
|
||||
}
|
29
Luski.net/JsonTypes/WSS/WSSLogin.cs
Executable file
29
Luski.net/JsonTypes/WSS/WSSLogin.cs
Executable 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
|
||||
{
|
||||
|
||||
}
|
23
Luski.net/JsonTypes/WSS/WSSOut.cs
Normal file
23
Luski.net/JsonTypes/WSS/WSSOut.cs
Normal 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
30
Luski.net/Luski.net.csproj
Executable 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-alpha18</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Luski.Shared" Version="1.1.0-alpha48" />
|
||||
<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>
|
6
Luski.net/Luski.net.csproj.user
Executable file
6
Luski.net/Luski.net.csproj.user
Executable 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>
|
157
Luski.net/MainServer.Account.cs
Normal file
157
Luski.net/MainServer.Account.cs
Normal 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);
|
||||
}
|
||||
}
|
15
Luski.net/MainServer.Events.cs
Normal file
15
Luski.net/MainServer.Events.cs
Normal 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;
|
||||
}
|
125
Luski.net/MainServer.Incoming.cs
Normal file
125
Luski.net/MainServer.Incoming.cs
Normal 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
278
Luski.net/MainServer.cs
Normal 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;
|
||||
}
|
||||
}
|
13
Luski.net/Properties/PublishProfiles/FolderProfile.pubxml
Executable file
13
Luski.net/Properties/PublishProfiles/FolderProfile.pubxml
Executable 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>
|
10
Luski.net/Properties/PublishProfiles/FolderProfile.pubxml.user
Executable file
10
Luski.net/Properties/PublishProfiles/FolderProfile.pubxml.user
Executable 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>
|
294
Luski.net/PublicServer.Account.cs
Normal file
294
Luski.net/PublicServer.Account.cs
Normal file
@ -0,0 +1,294 @@
|
||||
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)
|
||||
{
|
||||
ConnectionStatus = ConnectionStatus.FailedToLogin;
|
||||
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);
|
||||
ConnectionStatus = ConnectionStatus.LoggedIn;
|
||||
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;
|
||||
}
|
||||
}
|
196
Luski.net/PublicServer.Incoming.cs
Normal file
196
Luski.net/PublicServer.Incoming.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
834
Luski.net/PublicServer.cs
Normal file
834
Luski.net/PublicServer.cs
Normal file
@ -0,0 +1,834 @@
|
||||
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);
|
||||
s.ConnectionStatus = ConnectionStatus.FailedToConnect;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
s.ConnectionStatus = ConnectionStatus.ConnectedNotLoggedIn;
|
||||
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))
|
||||
};
|
||||
|
||||
TCategory bob = new()
|
||||
{
|
||||
ID = request.ID,
|
||||
ParentID = request.Parent,
|
||||
Description = dec,
|
||||
DescriptionEncoderType = request.DescriptionEncoderType,
|
||||
DescriptionEncryptionKey = request.DescriptionEncryptionKey,
|
||||
Channels = request.Channels,
|
||||
Categories = request.InnerCategories,
|
||||
Name = n,
|
||||
RoleOverides = request.RoleOverrides,
|
||||
UserOverides = request.UserOverrides,
|
||||
TitleEncoderType = request.TitleEncoderType,
|
||||
TitleEncryptionKey = request.TitleEncryptionKey,
|
||||
Server = this
|
||||
};
|
||||
cats.Add(bob);
|
||||
return bob;
|
||||
}
|
||||
throw request.Error switch
|
||||
{
|
||||
ErrorCode.InvalidToken => new Exception("Your current token is no longer valid"),
|
||||
ErrorCode.Forbidden => new Exception("The server rejected your request"),
|
||||
ErrorCode.ServerError => new Exception("Error from server: " + request.ErrorMessage),
|
||||
ErrorCode.InvalidURL or ErrorCode.MissingHeader => new Exception(request.ErrorMessage),
|
||||
_ => new Exception($"Unknown data: '{request.ErrorMessage}'"),
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<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))
|
||||
};
|
||||
|
||||
TChannel bob = new()
|
||||
{
|
||||
ID = request.ID,
|
||||
CategoryID = request.Parent,
|
||||
Description = dec,
|
||||
DescriptionEncoderType = request.DescriptionEncoderType,
|
||||
DescriptionEncryptionKey = request.DescriptionEncryptionKey,
|
||||
EncoderTypes = request.EncoderTypes,
|
||||
EncryptionKeys = request.EncryptionKeys,
|
||||
Epoch = request.Epoch,
|
||||
Name = n,
|
||||
RoleOverrides = request.RoleOverrides,
|
||||
UserOverrides = request.UserOverrides,
|
||||
Type = request.Type,
|
||||
TitleEncoderType = request.TitleEncoderType,
|
||||
TitleEncryptionKey = request.TitleEncryptionKey,
|
||||
PictureType = request.PictureType,
|
||||
Server = this,
|
||||
};
|
||||
|
||||
chans.Add(bob);
|
||||
return bob;
|
||||
}
|
||||
throw request.Error switch
|
||||
{
|
||||
ErrorCode.InvalidToken => new Exception("Your current token is no longer valid"),
|
||||
ErrorCode.Forbidden => new Exception("The server rejected your request"),
|
||||
ErrorCode.ServerError => new Exception("Error from server: " + request.ErrorMessage),
|
||||
ErrorCode.InvalidURL or ErrorCode.MissingHeader => new Exception(request.ErrorMessage),
|
||||
_ => new Exception($"Unknown data: '{request.ErrorMessage}'"),
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<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,
|
||||
PictureType = PictureType.none
|
||||
},
|
||||
ChannelPostCTSContext.Default.ChannelPostCTS,
|
||||
ChannelSTCContext.Default.ChannelSTC,
|
||||
CancellationToken.None);
|
||||
|
||||
return new SocketChannel()
|
||||
{
|
||||
ID = res.ID,
|
||||
CategoryID = res.Parent,
|
||||
Description = Decription,
|
||||
DescriptionEncoderType = res.DescriptionEncoderType,
|
||||
DescriptionEncryptionKey = res.DescriptionEncryptionKey,
|
||||
EncoderTypes = res.EncoderTypes,
|
||||
EncryptionKeys = res.EncryptionKeys,
|
||||
Epoch = res.Epoch,
|
||||
Name = Name,
|
||||
RoleOverrides = res.RoleOverrides,
|
||||
UserOverrides = res.UserOverrides,
|
||||
Type = res.Type,
|
||||
TitleEncoderType = res.TitleEncoderType,
|
||||
TitleEncryptionKey = res.TitleEncryptionKey,
|
||||
PictureType = res.PictureType,
|
||||
Server = this
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<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}");
|
||||
}
|
||||
|
||||
ServerProfile u = new()
|
||||
{
|
||||
Server = this,
|
||||
ID = user.ID,
|
||||
DisplayName = user.DisplayName,
|
||||
PictureType = user.PictureType,
|
||||
Controllers = user.Controllers,
|
||||
RoleControllers = user.RoleControllers
|
||||
};
|
||||
profiles.Add(u);
|
||||
return u;
|
||||
}
|
||||
|
||||
public async Task<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}");
|
||||
}
|
||||
|
||||
ServerProfile u = new()
|
||||
{
|
||||
Server = this,
|
||||
ID = user.ID,
|
||||
DisplayName = user.DisplayName,
|
||||
PictureType = user.PictureType,
|
||||
Controllers = user.Controllers,
|
||||
RoleControllers = user.RoleControllers
|
||||
};
|
||||
profiles_.Add(u);
|
||||
profiles.Add(u);
|
||||
}
|
||||
|
||||
return profiles_.ToArray();
|
||||
}
|
||||
|
||||
/// <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; }
|
||||
}
|
83
Luski.net/Server.Encryption.cs
Normal file
83
Luski.net/Server.Encryption.cs
Normal 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);
|
||||
}
|
||||
}
|
22
Luski.net/Server.Events.cs
Normal file
22
Luski.net/Server.Events.cs
Normal 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);
|
||||
}
|
||||
}
|
28
Luski.net/Server.Globals.cs
Normal file
28
Luski.net/Server.Globals.cs
Normal file
@ -0,0 +1,28 @@
|
||||
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();
|
||||
public ConnectionStatus ConnectionStatus { get; protected set; } = ConnectionStatus.FailedToConnect;
|
||||
}
|
12
Luski.net/Server.Incoming.cs
Normal file
12
Luski.net/Server.Incoming.cs
Normal 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
369
Luski.net/Server.Storage.cs
Normal 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
184
Luski.net/Server.cs
Normal 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
97
Luski.net/Server.old.Globals.cs
Executable 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
281
Luski.net/Server.old.cs
Executable 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
118
Luski.net/Structures/File.cs
Executable 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
|
||||
{
|
||||
|
||||
}
|
9
Luski.net/Structures/LocalKeyInfo.cs
Normal file
9
Luski.net/Structures/LocalKeyInfo.cs
Normal 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!;
|
||||
}
|
201
Luski.net/Structures/Main/MainSocketAppUser.cs
Executable file
201
Luski.net/Structures/Main/MainSocketAppUser.cs
Executable 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
|
||||
{
|
||||
|
||||
}
|
145
Luski.net/Structures/Main/MainSocketChannel.cs
Executable file
145
Luski.net/Structures/Main/MainSocketChannel.cs
Executable 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
|
||||
{
|
||||
|
||||
}
|
34
Luski.net/Structures/Main/MainSocketDMChannel.cs
Executable file
34
Luski.net/Structures/Main/MainSocketDMChannel.cs
Executable 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
|
||||
{
|
||||
|
||||
}
|
20
Luski.net/Structures/Main/MainSocketGroupChannel.cs
Executable file
20
Luski.net/Structures/Main/MainSocketGroupChannel.cs
Executable 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
|
||||
{
|
||||
|
||||
}
|
60
Luski.net/Structures/Main/MainSocketMessage.cs
Executable file
60
Luski.net/Structures/Main/MainSocketMessage.cs
Executable 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
|
||||
{
|
||||
|
||||
}
|
37
Luski.net/Structures/Main/MainSocketRemoteUser.cs
Executable file
37
Luski.net/Structures/Main/MainSocketRemoteUser.cs
Executable 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
|
||||
{
|
||||
|
||||
}
|
181
Luski.net/Structures/Main/MainSocketTextChannel.cs
Executable file
181
Luski.net/Structures/Main/MainSocketTextChannel.cs
Executable 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
|
||||
{
|
||||
|
||||
}
|
53
Luski.net/Structures/Main/MainSocketUserBase.cs
Normal file
53
Luski.net/Structures/Main/MainSocketUserBase.cs
Normal file
@ -0,0 +1,53 @@
|
||||
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 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
|
||||
{
|
||||
|
||||
}
|
23
Luski.net/Structures/Main/SocketBulkMessage.cs
Executable file
23
Luski.net/Structures/Main/SocketBulkMessage.cs
Executable 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
|
||||
{
|
||||
|
||||
}
|
60
Luski.net/Structures/Public/Color.cs
Normal file
60
Luski.net/Structures/Public/Color.cs
Normal 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;
|
||||
}
|
||||
}
|
44
Luski.net/Structures/Public/Role.cs
Normal file
44
Luski.net/Structures/Public/Role.cs
Normal 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();
|
||||
}
|
||||
}
|
21
Luski.net/Structures/Public/RoleOverride.cs
Normal file
21
Luski.net/Structures/Public/RoleOverride.cs
Normal 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);
|
||||
}
|
||||
}
|
30
Luski.net/Structures/Public/ServerProfile.cs
Normal file
30
Luski.net/Structures/Public/ServerProfile.cs
Normal file
@ -0,0 +1,30 @@
|
||||
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!;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
78
Luski.net/Structures/Public/SocketAppUser.cs
Executable file
78
Luski.net/Structures/Public/SocketAppUser.cs
Executable 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!;
|
||||
}
|
105
Luski.net/Structures/Public/SocketCategory.cs
Normal file
105
Luski.net/Structures/Public/SocketCategory.cs
Normal file
@ -0,0 +1,105 @@
|
||||
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; }
|
||||
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; }
|
||||
}
|
442
Luski.net/Structures/Public/SocketChannel.cs
Normal file
442
Luski.net/Structures/Public/SocketChannel.cs
Normal file
@ -0,0 +1,442 @@
|
||||
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 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; }
|
||||
}
|
28
Luski.net/Structures/Public/SocketFile.cs
Normal file
28
Luski.net/Structures/Public/SocketFile.cs
Normal 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());
|
||||
}
|
||||
}
|
62
Luski.net/Structures/Public/SocketMessage.cs
Normal file
62
Luski.net/Structures/Public/SocketMessage.cs
Normal 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;
|
||||
}
|
||||
}
|
77
Luski.net/Structures/Public/SocketUser.cs
Normal file
77
Luski.net/Structures/Public/SocketUser.cs
Normal file
@ -0,0 +1,77 @@
|
||||
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<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());
|
||||
}
|
||||
}
|
14
Luski.net/Structures/Public/UserOverride.cs
Normal file
14
Luski.net/Structures/Public/UserOverride.cs
Normal 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; }
|
||||
}
|
11
Luski.net/Structures/PublicKeyInfo.cs
Normal file
11
Luski.net/Structures/PublicKeyInfo.cs
Normal 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; }
|
||||
}
|
Loading…
Reference in New Issue
Block a user