add ATTACHMENT_SIZE_LIMIT, MAX_MESSAGE_SIZE, MAX_REACTION_SIZE & replace System.IO with custom Fs class
All checks were successful
Server Build / publish (push) Successful in 33s
Voice Build / publish (push) Successful in 24s

This commit is contained in:
olcxja 2026-07-18 15:05:38 +02:00
commit a17100910e
6 changed files with 99 additions and 48 deletions

View file

@ -21,9 +21,10 @@ public class BackgroundDelete : BackgroundService
{
try
{
foreach (var account in Directory.EnumerateDirectories(LarpixServer.Utils.Utils.ACCOUNTS_DATA_DIR, "*",
SearchOption.TopDirectoryOnly))
if (!Fs.Exists(LarpixServer.Utils.Utils.ACCOUNTS_DATA_DIR)) return;
foreach (var accountName in Fs.ReadDirectory(LarpixServer.Utils.Utils.ACCOUNTS_DATA_DIR))
{
string account = Path.Combine(LarpixServer.Utils.Utils.ACCOUNTS_DATA_DIR, accountName);
if (stoppingToken.IsCancellationRequested) return;
try
{

View file

@ -885,7 +885,7 @@ public static async Task Auth(HttpContext context, Func<Task> next, IQueryCollec
return;
}
byte[] fileBytes = await LarpixServer.Utils.Utils.LoadBodyBytes(context.Request.Body, 50 * 1024 * 1024); // 50MB limit for attachments
byte[] fileBytes = await LoadBodyBytes(context.Request.Body, ATTACHMENT_SIZE_LIMIT); // attachment size limit
if (fileBytes == null || fileBytes.Length == 0)
{
await context.Response.WriteAsync(Encryption.Encryption.EncryptString("error:empty.body", password));

View file

@ -332,12 +332,16 @@ public class Utils
return "error:invalid.entry.name";
}
if (contentBytes.Length > PUBLIC_STORAGE_LIMIT)
string dirPath = $"{ACCOUNTS_DATA_DIR}/{id}/storage/public";
string path = $"{dirPath}/{entry}";
long currentTotalSize = LarpixServer.Utils.Utils.GetDirectorySize(dirPath);
long fileExistingSize = File.Exists(path) ? new FileInfo(path).Length : 0;
if (currentTotalSize - fileExistingSize + contentBytes.Length > PUBLIC_STORAGE_LIMIT)
{
return "error:storage.limit.exceeded";
}
string path = $"{ACCOUNTS_DATA_DIR}/{id}/storage/public/{entry}";
await Fs.WriteFile(path, contentBytes);
return "success:storage.entry.updated";
}
@ -358,12 +362,16 @@ public class Utils
contentBytes = Encoding.UTF8.GetBytes(contentBase64);
}
if (contentBytes.Length > PUBLIC_STORAGE_LIMIT)
string dirPath = $"{ACCOUNTS_DATA_DIR}/{id}/storage/public";
string path = $"{dirPath}/{entry}";
long currentTotalSize = LarpixServer.Utils.Utils.GetDirectorySize(dirPath);
long fileExistingSize = File.Exists(path) ? new FileInfo(path).Length : 0;
if (currentTotalSize - fileExistingSize + contentBytes.Length > PUBLIC_STORAGE_LIMIT)
{
return "error:storage.limit.exceeded";
}
string path = $"{ACCOUNTS_DATA_DIR}/{id}/storage/public/{entry}";
await Fs.WriteFile(path, contentBytes);
return "success:storage.entry.updated";
}
@ -398,12 +406,16 @@ public class Utils
contentBytes = Encoding.UTF8.GetBytes(contentBase64);
}
if (contentBytes.Length > PRIVATE_STORAGE_LIMIT)
string dirPath = $"{ACCOUNTS_DATA_DIR}/{id}/storage/private";
string path = $"{dirPath}/{entry}";
long currentTotalSize = LarpixServer.Utils.Utils.GetDirectorySize(dirPath);
long fileExistingSize = File.Exists(path) ? new FileInfo(path).Length : 0;
if (currentTotalSize - fileExistingSize + contentBytes.Length > PRIVATE_STORAGE_LIMIT)
{
return "error:storage.limit.exceeded";
}
string path = $"{ACCOUNTS_DATA_DIR}/{id}/storage/private/{entry}";
await Fs.WriteFile(path, contentBytes);
return "success:storage.entry.updated";
}

View file

@ -21,15 +21,17 @@ public class Program
{
serverOptions.AddServerHeader = false;
serverOptions.ListenAnyIP(PORT);
serverOptions.Limits.MaxRequestBodySize = null;
});
var app = builder.Build();
string stagingDir = $"{LarpixServer.Utils.Utils.ROOMS_DIR}/dms_staging";
if (Directory.Exists(stagingDir))
if (Fs.Exists(stagingDir))
{
foreach (var file in Directory.GetFiles(stagingDir))
foreach (var fileName in Fs.ReadDirectory(stagingDir))
{
var file = Path.Combine(stagingDir, fileName);
var fileInfo = new FileInfo(file);
var age = DateTime.UtcNow - fileInfo.LastWriteTimeUtc;
var remaining = TimeSpan.FromMinutes(10) - age;

View file

@ -381,6 +381,12 @@ public class Requests
string responded = serializedBody.string4;
if (!await IsMemberOfDm(id, dmId)) return "error:forbidden";
if (content != null && content.Length > LarpixServer.Utils.Utils.MAX_MESSAGE_SIZE)
{
string sizeStr = LarpixServer.Utils.Utils.MAX_MESSAGE_SIZE >= 1024 ? $"{LarpixServer.Utils.Utils.MAX_MESSAGE_SIZE / 1024.0:0.##} KB" : $"{LarpixServer.Utils.Utils.MAX_MESSAGE_SIZE} B";
return $"error:message.too.long:{sizeStr}";
}
string msgDir = $"{ROOMS_DIR}/dms/{DOMAIN}/{dmId}/messages";
long last = 0;
@ -409,7 +415,7 @@ public class Requests
if (attachmentsNode is System.Text.Json.Nodes.JsonArray arr)
{
string attDir = $"{ROOMS_DIR}/dms/{DOMAIN}/{dmId}/attachments/{nextId}";
if (!Directory.Exists(attDir)) Directory.CreateDirectory(attDir);
if (!Fs.Exists(attDir)) Fs.CreateDirectory(attDir);
for (int i = 0; i < arr.Count; i++)
{
@ -422,7 +428,7 @@ public class Requests
{
string attId = attObj["id"].ToString();
string destPath = $"{attDir}/{attId}";
System.IO.File.Move(stagingPath, destPath);
Fs.MoveFile(stagingPath, destPath);
}
attObj.Remove("staging_id"); // Clean it up before saving
}
@ -529,6 +535,12 @@ public class Requests
if (!IsSafeMsgId(msgId)) return "error:invalid.msg.id";
if (!await IsMemberOfDm(id, dmId)) return "error:forbidden";
if (reactions != null && reactions.Length > LarpixServer.Utils.Utils.MAX_REACTION_SIZE)
{
string sizeStr = LarpixServer.Utils.Utils.MAX_REACTION_SIZE >= 1024 ? $"{LarpixServer.Utils.Utils.MAX_REACTION_SIZE / 1024.0:0.##} KB" : $"{LarpixServer.Utils.Utils.MAX_REACTION_SIZE} B";
return $"error:reaction.too.long:{sizeStr}";
}
string msgPath = $"{ROOMS_DIR}/dms/{DOMAIN}/{dmId}/messages/{msgId}";
if (!Fs.Exists(msgPath)) return "error:message.not.found";

View file

@ -1,6 +1,7 @@
using System.Collections.Concurrent;
using System.Text;
using System.Text.Json;
using LarpixServer.Filesystem;
using LarpixServer.Utils.Jsons;
namespace LarpixServer.Utils;
@ -36,6 +37,10 @@ public class Utils
public static int PRIVATE_STORAGE_LIMIT = 5242880;
public static int PUBLIC_STORAGE_LIMIT = 10485760;
public static int ATTACHMENT_SIZE_LIMIT = 52428800; //50mb
public static int MAX_MESSAGE_SIZE = 4000;
public static int MAX_REACTION_SIZE = 100;
public static Dictionary<string, string> mimeTypes = new()
{
["unknown"] = "application/octet-stream",
@ -206,7 +211,7 @@ public class Utils
totalRead += read;
if (totalRead > MAX_BODY_SIZE)
throw new Exception("error:body.too.large");
throw new Exception($"error:body.too.large:{MAX_BODY_SIZE / 1024 / 1024}");
bodyBuilder.Append(buffer, 0, read);
}
@ -225,7 +230,7 @@ public class Utils
{
totalRead += read;
if (totalRead > maxBodySize)
throw new Exception("error:body.too.large");
throw new Exception($"error:body.too.large:{maxBodySize / 1024 / 1024}");
ms.Write(buffer, 0, read);
}
@ -233,6 +238,19 @@ public class Utils
return ms.ToArray();
}
public static long GetDirectorySize(string directoryPath)
{
if (!Directory.Exists(directoryPath)) return 0;
long size = 0;
DirectoryInfo d = new DirectoryInfo(directoryPath);
foreach (FileInfo fi in d.GetFiles("*", SearchOption.AllDirectories))
{
size += fi.Length;
}
return size;
}
public static async Task LoadEnv()
{
if (File.Exists(".env"))
@ -298,6 +316,10 @@ public class Utils
PRIVATE_STORAGE_LIMIT = int.TryParse(Environment.GetEnvironmentVariable("PRIVATE_STORAGE_LIMIT"), out res) ? res : PRIVATE_STORAGE_LIMIT;
PUBLIC_STORAGE_LIMIT = int.TryParse(Environment.GetEnvironmentVariable("PUBLIC_STORAGE_LIMIT"), out res) ? res : PUBLIC_STORAGE_LIMIT;
MAX_BODY_SIZE = int.TryParse(Environment.GetEnvironmentVariable("MAX_BODY_SIZE"), out res) ? res : MAX_BODY_SIZE;
ATTACHMENT_SIZE_LIMIT = int.TryParse(Environment.GetEnvironmentVariable("ATTACHMENT_SIZE_LIMIT"), out res) ? res : ATTACHMENT_SIZE_LIMIT;
MAX_MESSAGE_SIZE = int.TryParse(Environment.GetEnvironmentVariable("MAX_MESSAGE_SIZE"), out res) ? res : MAX_MESSAGE_SIZE;
MAX_REACTION_SIZE = int.TryParse(Environment.GetEnvironmentVariable("MAX_REACTION_SIZE"), out res) ? res : MAX_REACTION_SIZE;
ROOMS_DIR = DATA_DIR + "/rooms";
ACCOUNTS_DIR = DATA_DIR + "/accounts";
@ -305,67 +327,69 @@ public class Utils
ACCOUNTS_DATA_DIR = ACCOUNTS_DIR + "/data";
ACCOUNTS_FREEID_DIR = ACCOUNTS_DIR + "/freeid";
if (!Directory.Exists(DATA_DIR))
if (!Fs.Exists(DATA_DIR))
{
Directory.CreateDirectory(DATA_DIR);
Fs.CreateDirectory(DATA_DIR);
}
if (!Directory.Exists(ROOMS_DIR))
if (!Fs.Exists(ROOMS_DIR))
{
Directory.CreateDirectory(ROOMS_DIR);
Fs.CreateDirectory(ROOMS_DIR);
}
if (!Directory.Exists(ROOMS_DIR + "/dms"))
Directory.CreateDirectory(ROOMS_DIR + "/dms");
if (!Directory.Exists(ROOMS_DIR + "/dms_staging"))
Directory.CreateDirectory(ROOMS_DIR + "/dms_staging");
if (!Directory.Exists(ROOMS_DIR + "/groups"))
if (!Fs.Exists(ROOMS_DIR + "/dms"))
Fs.CreateDirectory(ROOMS_DIR + "/dms");
if (!Fs.Exists(ROOMS_DIR + "/dms_staging"))
Fs.CreateDirectory(ROOMS_DIR + "/dms_staging");
if (!Fs.Exists(ROOMS_DIR + "/groups"))
{
Directory.CreateDirectory(ROOMS_DIR + "/groups");
Fs.CreateDirectory(ROOMS_DIR + "/groups");
}
if (!Directory.Exists(ROOMS_DIR + "/spaces"))
if (!Fs.Exists(ROOMS_DIR + "/spaces"))
{
Directory.CreateDirectory(ROOMS_DIR + "/spaces");
Fs.CreateDirectory(ROOMS_DIR + "/spaces");
}
if (!Directory.Exists(ROOMS_DIR + "/freegroups"))
if (!Fs.Exists(ROOMS_DIR + "/freegroups"))
{
Directory.CreateDirectory(ROOMS_DIR + "/freegroups");
Fs.CreateDirectory(ROOMS_DIR + "/freegroups");
}
if (!Directory.Exists(ROOMS_DIR + "/freespaces"))
if (!Fs.Exists(ROOMS_DIR + "/freespaces"))
{
Directory.CreateDirectory(ROOMS_DIR + "/freespaces");
}
if (!File.Exists(ROOMS_DIR + "/lastgroup"))
{
await File.WriteAllTextAsync(ROOMS_DIR + "/lastgroup", "0");
}
if (!File.Exists(ROOMS_DIR + "/lastspace"))
{
await File.WriteAllTextAsync(ROOMS_DIR + "/lastspace", "0");
Fs.CreateDirectory(ROOMS_DIR + "/freespaces");
}
if (!Directory.Exists(ACCOUNTS_DIR))
if (!Fs.Exists(ROOMS_DIR + "/lastgroup"))
{
Directory.CreateDirectory(ACCOUNTS_DIR);
await Fs.WriteFile(ROOMS_DIR + "/lastgroup", Encoding.UTF8.GetBytes("0"));
}
if (!Directory.Exists(ACCOUNTS_NAME_DIR))
if (!Fs.Exists(ROOMS_DIR + "/lastspace"))
{
Directory.CreateDirectory(ACCOUNTS_NAME_DIR);
await Fs.WriteFile(ROOMS_DIR + "/lastspace", Encoding.UTF8.GetBytes("0"));
}
if (!Directory.Exists(ACCOUNTS_DATA_DIR))
if (!Fs.Exists(ACCOUNTS_DIR))
{
Directory.CreateDirectory(ACCOUNTS_DATA_DIR);
Fs.CreateDirectory(ACCOUNTS_DIR);
}
if (!Directory.Exists(ACCOUNTS_FREEID_DIR))
if (!Fs.Exists(ACCOUNTS_NAME_DIR))
{
Directory.CreateDirectory(ACCOUNTS_FREEID_DIR);
Fs.CreateDirectory(ACCOUNTS_NAME_DIR);
}
if (!File.Exists(ACCOUNTS_DIR + "/last"))
if (!Fs.Exists(ACCOUNTS_DATA_DIR))
{
await File.WriteAllTextAsync(ACCOUNTS_DIR + "/last", "0");
Fs.CreateDirectory(ACCOUNTS_DATA_DIR);
}
if (!File.Exists($"{ACCOUNTS_DIR}/registration"))
if (!Fs.Exists(ACCOUNTS_FREEID_DIR))
{
await File.WriteAllTextAsync($"{ACCOUNTS_DIR}/registration", "first;");
Fs.CreateDirectory(ACCOUNTS_FREEID_DIR);
}
if (!Fs.Exists(ACCOUNTS_DIR + "/last"))
{
await Fs.WriteFile(ACCOUNTS_DIR + "/last", Encoding.UTF8.GetBytes("0"));
}
if (!Fs.Exists($"{ACCOUNTS_DIR}/registration"))
{
await Fs.WriteFile($"{ACCOUNTS_DIR}/registration", Encoding.UTF8.GetBytes("first;"));
}
}
}