Merge pull request 'dev' (#1) from dev into stable

Reviewed-on: #1
This commit is contained in:
JacobTech 2024-11-18 23:39:54 -05:00
commit 34633ad2e4
77 changed files with 6128 additions and 0 deletions

25
Luski.net.sln Executable file
View File

@ -0,0 +1,25 @@

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

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

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

View File

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

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

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

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

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

View File

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

View File

@ -0,0 +1,10 @@
namespace Luski.net.Enums;
public enum ConnectionStatus
{
FailedToConnect,
CommunicationError,
FailedToLogin,
ConnectedNotLoggedIn,
LoggedIn,
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

View File

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

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

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,29 @@
using Luski.net.Enums;
using Luski.net.JsonTypes.BaseTypes;
using System.Text.Json.Serialization;
using Luski.net.Interfaces;
namespace Luski.net.JsonTypes.WSS;
internal class WSSLogin : IncomingWSS, IServerEvent
{
[JsonPropertyName("token")]
[JsonInclude]
public string Token { get; set; } = default!;
[JsonPropertyName("session_token")]
[JsonInclude]
public string? SessionToken { get; set; } = default!;
[JsonPropertyName("type")]
[JsonInclude]
new public DataType? Type { get; set; } = DataType.Login;
}
[JsonSerializable(typeof(WSSLogin))]
[JsonSourceGenerationOptions(
GenerationMode = JsonSourceGenerationMode.Default,
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
WriteIndented = false)]
internal partial class WSSLoginContext : JsonSerializerContext
{
}

View File

@ -0,0 +1,23 @@
using System.Text.Json.Serialization;
namespace Luski.net.JsonTypes.WSS;
public class WSSOut
{
[JsonInclude]
[JsonPropertyName("data")]
public ServerEvent Data { get; set; }
[JsonInclude]
[JsonPropertyName("send_type")]
public int SendType { get; set; } = 1;
}
[JsonSerializable(typeof(WSSOut))]
[JsonSourceGenerationOptions(
GenerationMode = JsonSourceGenerationMode.Default,
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
WriteIndented = false)]
internal partial class WSSOutContext : JsonSerializerContext
{
}

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

@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Title>Luski.net</Title>
<Authors>JacobTech</Authors>
<Company>JacobTech, LLC</Company>
<Description>A wrapper for the luski API</Description>
<PackageProjectUrl>https://www.jacobtech.com/Luski/Documentation</PackageProjectUrl>
<RepositoryUrl>https://github.com/JacobTech-com/Luski.net</RepositoryUrl>
<IncludeSymbols>True</IncludeSymbols>
<FileVersion>1.0.0</FileVersion>
<Version>2.0.1-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>

View File

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

View File

@ -0,0 +1,157 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Security.Authentication;
using System.Text;
using System.Text.Json;
using System.Threading;
using JacobTechEncryption;
using JacobTechEncryption.Enums;
using Luski.net.Enums;
using Luski.net.JsonTypes;
using Luski.net.JsonTypes.WSS;
using Luski.net.Structures.Main;
using Luski.Shared.PublicServers.V1.Enums;