using PrivaPub.Federation.Objects; using PrivaPub.Infrastructure.Jobs; using PrivaPub.Models.Jobs; using PrivaPub.Models.User; using System.Text.Json; using System.Text.Json.Nodes; using static PrivaPub.Federation.Objects.ActivityJson; namespace PrivaPub.Federation.Inbox { // FEP-171b conversation containers (Hubzilla, Streams, Forte): a thread's owner adds each activity in it to the // thread's context, a collection of its own, and tells the thread's audience with Add{activity}. The activity it adds // is another's, so it is taken as forwarded by the owner: a Create, Update or Delete of the actor's own object, // believed on its FEP-8b32 proof or as its origin has it (Forwarded). Hubzilla also passes the activity on by itself; // the two are one job. public static class ConversationContainers { public static async Task Unwrap(JsonNode add, ForeignAvatar owner, IJobQueue queue, CancellationToken token) { if (add["object"] is not JsonObject inner || Value(inner, "type") is not ("Create" or "Update" or "Delete") || Id(inner) is not { } innerId) return false; var target = add["target"]; var context = Id(target); if (context == default || !Origin.Same(context, owner.ActorURI) || target is JsonObject collection && Id(collection["attributedTo"]) is { } attributedTo && attributedTo != owner.ActorURI) return false; var actorUri = Id(inner["actor"]); if (actorUri == default || !Forwarded.Takeable(Value(inner, "type"), inner, actorUri)) { Arrival.Drop("container-unusable"); return true; } var payload = JsonSerializer.Serialize(new InboxPayload(actorUri, inner.ToJsonString(), "shared", ReceivedAt: DateTime.UtcNow, ForwardedBy: owner.ActorURI)); var host = new Uri(actorUri).Host.ToLowerInvariant(); var dedupe = "inbox|forwarded|" + innerId; var queued = await queue.Enqueue(JobKind.ProcessInbox, payload, host, dedupe, token); // one id for two activities (Forte's Update of a post has its Create's id): what each carries tells them apart if (!queued && InboxReceiver.CarriesOther(await queue.Payload(dedupe, token), inner)) queued = await queue.Enqueue(JobKind.ProcessInbox, payload, host, $"{dedupe}|{InboxReceiver.Digest(inner)}", token); if (queued) Arrival.Accept("unwrapped"); else Arrival.Drop("duplicate"); return true; } } }