using System.Text.Json.Nodes; using System.Text.RegularExpressions; using MongoDB.Entities; using PostEntity = PrivaPub.Models.Post.Post; namespace PrivaPub.Federation.Inbox { // Some servers name one of our posts by the address of its page instead of its id (Pixelfed likes /@name/, the // post's `url`). Before an activity is handled, such a reference to a post of ours, as the activity's object or as the // object of the activity it undoes, is replaced by the post's id, so that every handler finds the post the same way. public static partial class LocalReferences { [GeneratedRegex("^/@[^/?#]+/([0-9a-f]{24})$")] private static partial Regex PostPage(); public static async Task Canonicalise(JsonNode activity, string baseAddress, CancellationToken token) { if (activity is not JsonObject outer || string.IsNullOrEmpty(baseAddress)) return; await Replace(outer, baseAddress, token); if (outer["object"] is JsonObject inner) await Replace(inner, baseAddress, token); } static async Task Replace(JsonObject node, string baseAddress, CancellationToken token) { if (node["object"] is not JsonValue value || !value.TryGetValue(out var uri) || PostId(uri, baseAddress) is not { } id) return; var post = await DB.Default.Find().Match(p => p.ID == id && !p.IsFederatedCopy).ExecuteFirstAsync(token); if (post?.ObjectURI is { } canonical) node["object"] = canonical; } // the id of the post whose page the address is, on our own site public static string PostId(string uri, string baseAddress) => uri.StartsWith(baseAddress + "/@", StringComparison.OrdinalIgnoreCase) && PostPage().Match(uri[baseAddress.Length..]) is { Success: true } page ? page.Groups[1].Value : default; } }