From ab89f07493fcb0df77090530081fd4564d60bb49 Mon Sep 17 00:00:00 2001 From: olcxja Date: Thu, 28 May 2026 12:55:51 +0200 Subject: [PATCH] optimizations and bug fixes: - Fixed directory cache bug in Fs - Optimized DmId creation - Optimized most splits --- LarpixServer/Account/Requests.cs | 24 +++--- LarpixServer/Account/Utils.cs | 122 ++++++++++++---------------- LarpixServer/Federation/Receiver.cs | 4 +- LarpixServer/Filesystem/Fs.cs | 14 +++- LarpixServer/Room/Requests.cs | 63 ++++++++------ 5 files changed, 117 insertions(+), 110 deletions(-) diff --git a/LarpixServer/Account/Requests.cs b/LarpixServer/Account/Requests.cs index 1fa12e6..e4e785d 100644 --- a/LarpixServer/Account/Requests.cs +++ b/LarpixServer/Account/Requests.cs @@ -31,7 +31,7 @@ public class Requests return; } - string id = idQuery.ToString().Split(":")[0]; + string id = Utils.GetIdFromUsernameWD(idQuery.ToString()); string password = await Utils.GetPassword(id); secret = await Utils.NonceDecryptBody(id, password, secret); string auth = await Utils.Auth(id, password, secret); @@ -292,7 +292,7 @@ public class Requests return; } - string id = idQuery.ToString().Split(":")[0]; + string id = Utils.GetIdFromUsernameWD(idQuery.ToString()); string password = await Utils.GetPassword(id); secret = await Utils.NonceDecryptBody(id, password, secret); string auth = await Utils.Auth(id, password, secret); @@ -312,7 +312,7 @@ public class Requests { return; } - username = username.ToString().Split(":")[0]; + username = Utils.GetIdFromUsernameWD(username.ToString()); await context.Response.WriteAsync(await Utils.NameFromId(await Utils.IdFromName(username))); return; @@ -328,7 +328,7 @@ public class Requests if (!Utils.IsUserLocal(username, out string domain)) //federation { - username = username.ToString().Split(":")[0]; + username = Utils.GetIdFromUsernameWD(username.ToString()); string remoteId = await Federation.Sender.GetUserId(username, domain); if (remoteId != "0" && !string.IsNullOrEmpty(remoteId)) { @@ -336,7 +336,7 @@ public class Requests } return; } - username = username.ToString().Split(":")[0]; + username = Utils.GetIdFromUsernameWD(username.ToString()); await context.Response.WriteAsync(await Utils.IdFromName(username) + $":{DOMAIN}"); return; @@ -353,7 +353,7 @@ public class Requests { return; } - id = id.ToString().Split(":")[0]; + id = Utils.GetIdFromUsernameWD(id.ToString()); await context.Response.WriteAsync(await Utils.NameFromId(id) + $":{DOMAIN}"); return; @@ -367,7 +367,7 @@ public class Requests return; } - string id = idQuery.ToString().Split(":")[0]; + string id = Utils.GetIdFromUsernameWD(idQuery.ToString()); string plainPass = await Utils.GetPassword(id); foreach (var kvp in nonceHolder) //clearowanie nieuzytych nonce { @@ -403,7 +403,7 @@ public class Requests return; } - string id = idQuery.ToString().Split(":")[0]; + string id = Utils.GetIdFromUsernameWD(idQuery.ToString()); string body = await LoadBody(bodyReader); string password = await Utils.GetPassword(id); string newPass = await Utils.NonceDecryptBody(id, password, body, false); @@ -451,7 +451,7 @@ public class Requests return; } - string id = idQuery.ToString().Split(":")[0]; + string id = Utils.GetIdFromUsernameWD(idQuery.ToString()); string body = await LoadBody(bodyReader); string password = await Utils.GetPassword(id); @@ -503,7 +503,7 @@ public class Requests { return; } - string id = idQuery.ToString().Split(":")[0]; + string id = Utils.GetIdFromUsernameWD(idQuery.ToString()); string keysRaw = await Utils.GetUserKeys(id); if (string.IsNullOrEmpty(keysRaw)) { @@ -533,7 +533,7 @@ public class Requests { return; } - string id = idQuery.ToString().Split(":")[0]; + string id = Utils.GetIdFromUsernameWD(idQuery.ToString()); byte[] entryByte = await Utils.GetUserPublicStorageEntry(id, entry); @@ -557,7 +557,7 @@ public class Requests return; } - string id = idQuery.ToString().Split(":")[0]; + string id = Utils.GetIdFromUsernameWD(idQuery.ToString()); string body = await LoadBody(bodyReader); string password = await Utils.GetPassword(id); body = await Utils.NonceDecryptBody(id, password, body, false); diff --git a/LarpixServer/Account/Utils.cs b/LarpixServer/Account/Utils.cs index 8a7f8ad..308cdfc 100644 --- a/LarpixServer/Account/Utils.cs +++ b/LarpixServer/Account/Utils.cs @@ -45,14 +45,28 @@ public class Utils return semLock; } + public static string GetIdFromUsernameWD(string usernameWD) + { + int colonIndex = usernameWD.IndexOf(':'); + return colonIndex == -1 ? usernameWD : usernameWD.Substring(0, colonIndex); + } + + public static string GetDmId(string id1, string domain1, string id2, string domain2) + { + string u1 = $"{id1};{domain1}"; + string u2 = $"{id2};{domain2}"; + return string.CompareOrdinal(u1, u2) < 0 ? $"{u1}_{u2}" : $"{u2}_{u1}"; + } + public static bool IsUserLocal(string usernameWD, out string domain) { - if (!usernameWD.Contains(':') || usernameWD.EndsWith(":" + DOMAIN)) + int colonIndex = usernameWD.IndexOf(':'); + if (colonIndex == -1 || usernameWD.EndsWith(":" + DOMAIN)) { domain = DOMAIN; return true; } - domain = usernameWD.Split(':', 2)[1]; + domain = usernameWD.Substring(colonIndex + 1); return false; } @@ -182,103 +196,72 @@ public class Utils public static async Task GetUserSentInvites(string id) { - string dmString = "\"dms\": {"; - string groupString = "\"groups\": {"; + StringBuilder dmBuilder = new StringBuilder("\"dms\": {"); + StringBuilder groupBuilder = new StringBuilder("\"groups\": {"); string path = $"{ACCOUNTS_DATA_DIR}/{id}/dminvites/sent"; if (Fs.Exists(path)) { string[] invites = Fs.ReadDirectory(path); - if (invites.Length != 0) + for (int i = 0; i < invites.Length; i++) { - foreach (var invite in invites) - { - string content = Encoding.UTF8.GetString(await Fs.ReadFile($"{path}/{invite}")); - if (dmString == "\"dms\": {") - { - dmString += $"\"{invite}\": {{ \"timestamp\": {content} }}"; - } - else - { - dmString += $",\"{invite}\": {{ \"timestamp\": {content} }}"; - } - } + string invite = invites[i]; + string content = Encoding.UTF8.GetString(await Fs.ReadFile($"{path}/{invite}")); + if (i > 0) dmBuilder.Append(','); + dmBuilder.Append($"\"{invite}\": {{ \"timestamp\": {content} }}"); } } - dmString += "}"; + dmBuilder.Append("}"); path = $"{ACCOUNTS_DATA_DIR}/{id}/groupinvites/sent"; if (Fs.Exists(path)) { string[] invites = Fs.ReadDirectory(path); - if (invites.Length != 0) + for (int i = 0; i < invites.Length; i++) { - foreach (var invite in invites) //filename - group;domain-receiverid;domain - { string[] inviteArray = invite.Split('-'); - string content = Encoding.UTF8.GetString(await Fs.ReadFile($"{path}/{invite}")); - if (groupString == "\"groups\": {") - { - groupString += $"\"{inviteArray[0]}\": {{ \"timestamp\": {content}, \"receiver\": \"{inviteArray[1]}\" }}"; - } - else - { - groupString += $",\"{inviteArray[0]}\": {{ \"timestamp\": {content}, \"receiver\": \"{inviteArray[1]}\" }}"; - } - } + string invite = invites[i]; + string[] inviteArray = invite.Split('-'); + string content = Encoding.UTF8.GetString(await Fs.ReadFile($"{path}/{invite}")); + if (i > 0) groupBuilder.Append(','); + groupBuilder.Append($"\"{inviteArray[0]}\": {{ \"timestamp\": {content}, \"receiver\": \"{inviteArray[1]}\" }}"); } } - groupString += "}"; + groupBuilder.Append("}"); - return $"{{{dmString},{groupString}}}"; + return $"{{{dmBuilder.ToString()},{groupBuilder.ToString()}}}"; } public static async Task GetUserReceivedInvites(string id) { - string dmString = "\"dms\": {"; - string groupString = "\"groups\": {"; + StringBuilder dmBuilder = new StringBuilder("\"dms\": {"); + StringBuilder groupBuilder = new StringBuilder("\"groups\": {"); string path = $"{ACCOUNTS_DATA_DIR}/{id}/dminvites/recv"; if (Fs.Exists(path)) { string[] invites = Fs.ReadDirectory(path); - if (invites.Length != 0) + for (int i = 0; i < invites.Length; i++) { - foreach (var invite in invites) - { - string content = Encoding.UTF8.GetString(await Fs.ReadFile($"{path}/{invite}")); - if (dmString == "\"dms\": {") - { - dmString += $"\"{invite}\": {{ \"timestamp\": {content} }}"; - } - else - { - dmString += $",\"{invite}\": {{ \"timestamp\": {content} }}"; - } - } + string invite = invites[i]; + string content = Encoding.UTF8.GetString(await Fs.ReadFile($"{path}/{invite}")); + if (i > 0) dmBuilder.Append(','); + dmBuilder.Append($"\"{invite}\": {{ \"timestamp\": {content} }}"); } } - dmString += "}"; + dmBuilder.Append("}"); path = $"{ACCOUNTS_DATA_DIR}/{id}/groupinvites/recv"; if (Fs.Exists(path)) { string[] invites = Fs.ReadDirectory(path); - if (invites.Length != 0) + for (int i = 0; i < invites.Length; i++) { - foreach (var invite in invites) - { - string[] content = Encoding.UTF8.GetString(await Fs.ReadFile($"{path}/{invite}")).Split(':'); - if (groupString == "\"groups\": {") - { - groupString += $"\"{invite}\": {{ \"timestamp\": {content[1]}, \"sender\": \"{content[0]}\" }}"; - } - else - { - groupString += $",\"{invite}\": {{ \"timestamp\": {content[1]}, \"sender\": \"{content[0]}\" }}"; - } - } + string invite = invites[i]; + string[] content = Encoding.UTF8.GetString(await Fs.ReadFile($"{path}/{invite}")).Split(':'); + if (i > 0) groupBuilder.Append(','); + groupBuilder.Append($"\"{invite}\": {{ \"timestamp\": {content[1]}, \"sender\": \"{content[0]}\" }}"); } } - groupString += "}"; + groupBuilder.Append("}"); - return $"{{{dmString},{groupString}}}"; + return $"{{{dmBuilder.ToString()},{groupBuilder.ToString()}}}"; } public static async Task UpdateUserKeys(string id, string body) @@ -348,13 +331,16 @@ public class Utils return "{}"; } - string dms = "{"; - foreach (var dmfile in Fs.ReadDirectory(path)) + StringBuilder dmsBuilder = new StringBuilder("{"); + string[] dmFiles = Fs.ReadDirectory(path); + for (int i = 0; i < dmFiles.Length; i++) { - dms += $"{Encoding.UTF8.GetString(await Fs.ReadFile($"{path}/{dmfile}"))},"; + if (i > 0) dmsBuilder.Append(','); + dmsBuilder.Append(Encoding.UTF8.GetString(await Fs.ReadFile($"{path}/{dmFiles[i]}"))); } + dmsBuilder.Append("}"); - return dms.Substring(0, dms.Length - 1) + "}"; + return dmsBuilder.ToString(); } public static async Task RemoveOldestDmIndex(string id) //i wont implement this, client should just warn users that they have like 99999999 dms and should leave some diff --git a/LarpixServer/Federation/Receiver.cs b/LarpixServer/Federation/Receiver.cs index eb1ad16..fd1b2c1 100644 --- a/LarpixServer/Federation/Receiver.cs +++ b/LarpixServer/Federation/Receiver.cs @@ -121,9 +121,7 @@ public class Receiver try { //best dmId creation ever - List users = new List(){$"{ids[0]};{DOMAIN}", $"{ids[1]};{domain}"}; - users.Sort(); - string dmId = $"{users[0]}_{users[1]}"; + string dmId = Account.Utils.GetDmId(ids[0], DOMAIN, ids[1], domain); await Account.Utils.UpdateUserDm(ids[0], dmId, "false", diff --git a/LarpixServer/Filesystem/Fs.cs b/LarpixServer/Filesystem/Fs.cs index d3c48ab..637ad3e 100644 --- a/LarpixServer/Filesystem/Fs.cs +++ b/LarpixServer/Filesystem/Fs.cs @@ -290,8 +290,18 @@ public class Fs try { Directory.CreateDirectory(path); - existCache[path] = true; - InvalidateDirCacheFor(path); + + var current = path; + while (!string.IsNullOrEmpty(current)) + { + existCache[current] = true; + InvalidateDirCacheFor(current); + + var parent = Path.GetDirectoryName(current)?.Replace("\\", "/").TrimEnd('/'); + if (string.IsNullOrEmpty(parent) || parent == current) break; + + current = parent; + } } finally { diff --git a/LarpixServer/Room/Requests.cs b/LarpixServer/Room/Requests.cs index 910b9c9..2c3796a 100644 --- a/LarpixServer/Room/Requests.cs +++ b/LarpixServer/Room/Requests.cs @@ -65,15 +65,22 @@ public class Requests public static async Task DmInvite(string id, string targetId) { - if (!Account.Utils.IsUserLocal(targetId, out string domain)) //federation + bool isLocal = Account.Utils.IsUserLocal(targetId, out string domain); + string tId = Account.Utils.GetIdFromUsernameWD(targetId); + + string checkDmId = Account.Utils.GetDmId(id, DOMAIN, tId, domain); + if (Fs.Exists($"{ROOMS_DIR}/dms/{DOMAIN}/{checkDmId}")) { - targetId = targetId.Split(":")[0]; - await Fs.WriteFile(ACCOUNTS_DATA_DIR + $"/{id}/dminvites/sent/{targetId};{domain}", Encoding.UTF8.GetBytes(DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString())); - return await Federation.Sender.DmInvite(id, domain, targetId); - + return "error:dm.already.exists"; } - targetId = targetId.Split(":")[0]; - string id2 = targetId; + + if (!isLocal) //federation + { + await Fs.WriteFile(ACCOUNTS_DATA_DIR + $"/{id}/dminvites/sent/{tId};{domain}", Encoding.UTF8.GetBytes(DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString())); + return await Federation.Sender.DmInvite(id, domain, tId); + } + + string id2 = tId; if (id2 == "0" || string.IsNullOrEmpty(id2)) { return "error:user.not.found"; @@ -108,14 +115,16 @@ public class Requests public static async Task DmInviteRevoke(string id, string targetId) { - if (!Account.Utils.IsUserLocal(targetId, out string domain)) //federation + bool isLocal = Account.Utils.IsUserLocal(targetId, out string domain); + string tId = Account.Utils.GetIdFromUsernameWD(targetId); + + if (!isLocal) //federation { - targetId = targetId.Split(":")[0]; - Fs.DeleteFile(ACCOUNTS_DATA_DIR + $"/{id}/dminvites/sent/{targetId};{domain}"); - return await Federation.Sender.DmInviteRevoke(id, domain, targetId); + Fs.DeleteFile(ACCOUNTS_DATA_DIR + $"/{id}/dminvites/sent/{tId};{domain}"); + return await Federation.Sender.DmInviteRevoke(id, domain, tId); } - targetId = targetId.Split(":")[0]; - string id2 = targetId; + + string id2 = tId; if (id2 == "0" || string.IsNullOrEmpty(id2)) return "error:user.not.found"; SemaphoreSlim userLock2 = Account.Utils.GetUserLock(id2); @@ -135,14 +144,16 @@ public class Requests public static async Task DmInviteDecline(string id, string targetId) { - if (!Account.Utils.IsUserLocal(targetId, out string domain)) //federation + bool isLocal = Account.Utils.IsUserLocal(targetId, out string domain); + string tId = Account.Utils.GetIdFromUsernameWD(targetId); + + if (!isLocal) //federation { - targetId = targetId.Split(":")[0]; - Fs.DeleteFile(ACCOUNTS_DATA_DIR + $"/{id}/dminvites/recv/{targetId};{domain}"); - return await Federation.Sender.DmInviteDecline(id, domain, targetId); + Fs.DeleteFile(ACCOUNTS_DATA_DIR + $"/{id}/dminvites/recv/{tId};{domain}"); + return await Federation.Sender.DmInviteDecline(id, domain, tId); } - targetId = targetId.Split(":")[0]; - string id2 = targetId; + + string id2 = tId; if (id2 == "0" || string.IsNullOrEmpty(id2)) return "error:user.not.found"; SemaphoreSlim userLock2 = Account.Utils.GetUserLock(id2); @@ -172,15 +183,19 @@ public class Requests string targetId = serializedBody.string1; bool isUserLocal = Account.Utils.IsUserLocal(targetId, out string domain); - string id2; + string id2 = Account.Utils.GetIdFromUsernameWD(targetId); - targetId = targetId.Split(":")[0]; - id2 = targetId; if (id2 == "0" || string.IsNullOrEmpty(id2)) { return "error:user.not.found"; } + string checkDmId = Account.Utils.GetDmId(id, DOMAIN, id2, domain); + if (Fs.Exists($"{ROOMS_DIR}/dms/{DOMAIN}/{checkDmId}")) + { + return "error:dm.already.exists"; + } + Universal2String keys = JsonSerializer.Deserialize( //we need to pull keys before we do anything, because if user do NOT have them, dm creation will crash await Account.Utils.GetUserKeys(id), AppJsonSerializerContext.Default.Universal2String @@ -214,9 +229,7 @@ public class Requests //best dmId creation ever - List users = new List(){$"{id};{DOMAIN}", $"{id2};{domain}"}; - users.Sort(); - string dmId = $"{users[0]}_{users[1]}"; + string dmId = checkDmId; Fs.CreateDirectory($"{ROOMS_DIR}/dms/{DOMAIN}/{dmId}/members");