diff options
Diffstat (limited to 'internal/processing/workers/federate.go')
-rw-r--r-- | internal/processing/workers/federate.go | 217 |
1 files changed, 199 insertions, 18 deletions
diff --git a/internal/processing/workers/federate.go b/internal/processing/workers/federate.go index 3538c9958..d71bb0a83 100644 --- a/internal/processing/workers/federate.go +++ b/internal/processing/workers/federate.go @@ -23,12 +23,14 @@ import ( "github.com/superseriousbusiness/activity/pub" "github.com/superseriousbusiness/activity/streams" + "github.com/superseriousbusiness/activity/streams/vocab" "github.com/superseriousbusiness/gotosocial/internal/ap" "github.com/superseriousbusiness/gotosocial/internal/federation" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/typeutils" + "github.com/superseriousbusiness/gotosocial/internal/util" ) // federate wraps functions for federating @@ -135,6 +137,12 @@ func (f *federate) DeleteAccount(ctx context.Context, account *gtsmodel.Account) return nil } +// CreateStatus sends the given status out to relevant +// recipients with the Outbox of the status creator. +// +// If the status is pending approval, then it will be +// sent **ONLY** to the inbox of the account it replies to, +// ignoring shared inboxes. func (f *federate) CreateStatus(ctx context.Context, status *gtsmodel.Status) error { // Do nothing if the status // shouldn't be federated. @@ -153,18 +161,32 @@ func (f *federate) CreateStatus(ctx context.Context, status *gtsmodel.Status) er return gtserror.Newf("error populating status: %w", err) } - // Parse the outbox URI of the status author. - outboxIRI, err := parseURI(status.Account.OutboxURI) - if err != nil { - return err - } - // Convert status to AS Statusable implementing type. statusable, err := f.converter.StatusToAS(ctx, status) if err != nil { return gtserror.Newf("error converting status to Statusable: %w", err) } + // If status is pending approval, + // it must be a reply. Deliver it + // **ONLY** to the account it replies + // to, on behalf of the replier. + if util.PtrOrValue(status.PendingApproval, false) { + return f.deliverToInboxOnly( + ctx, + status.Account, + status.InReplyToAccount, + // Status has to be wrapped in Create activity. + typeutils.WrapStatusableInCreate(statusable, false), + ) + } + + // Parse the outbox URI of the status author. + outboxIRI, err := parseURI(status.Account.OutboxURI) + if err != nil { + return err + } + // Send a Create activity with Statusable via the Actor's outbox. create := typeutils.WrapStatusableInCreate(statusable, false) if _, err := f.FederatingActor().Send(ctx, outboxIRI, create); err != nil { @@ -672,6 +694,12 @@ func (f *federate) RejectFollow(ctx context.Context, follow *gtsmodel.Follow) er return nil } +// Like sends the given fave out to relevant +// recipients with the Outbox of the status creator. +// +// If the fave is pending approval, then it will be +// sent **ONLY** to the inbox of the account it faves, +// ignoring shared inboxes. func (f *federate) Like(ctx context.Context, fave *gtsmodel.StatusFave) error { // Populate model. if err := f.state.DB.PopulateStatusFave(ctx, fave); err != nil { @@ -684,18 +712,30 @@ func (f *federate) Like(ctx context.Context, fave *gtsmodel.StatusFave) error { return nil } - // Parse relevant URI(s). - outboxIRI, err := parseURI(fave.Account.OutboxURI) - if err != nil { - return err - } - // Create the ActivityStreams Like. like, err := f.converter.FaveToAS(ctx, fave) if err != nil { return gtserror.Newf("error converting fave to AS Like: %w", err) } + // If fave is pending approval, + // deliver it **ONLY** to the account + // it faves, on behalf of the faver. + if util.PtrOrValue(fave.PendingApproval, false) { + return f.deliverToInboxOnly( + ctx, + fave.Account, + fave.TargetAccount, + like, + ) + } + + // Parse relevant URI(s). + outboxIRI, err := parseURI(fave.Account.OutboxURI) + if err != nil { + return err + } + // Send the Like via the Actor's outbox. if _, err := f.FederatingActor().Send( ctx, outboxIRI, like, @@ -709,6 +749,12 @@ func (f *federate) Like(ctx context.Context, fave *gtsmodel.StatusFave) error { return nil } +// Announce sends the given boost out to relevant +// recipients with the Outbox of the status creator. +// +// If the boost is pending approval, then it will be +// sent **ONLY** to the inbox of the account it boosts, +// ignoring shared inboxes. func (f *federate) Announce(ctx context.Context, boost *gtsmodel.Status) error { // Populate model. if err := f.state.DB.PopulateStatus(ctx, boost); err != nil { @@ -721,12 +767,6 @@ func (f *federate) Announce(ctx context.Context, boost *gtsmodel.Status) error { return nil } - // Parse relevant URI(s). - outboxIRI, err := parseURI(boost.Account.OutboxURI) - if err != nil { - return err - } - // Create the ActivityStreams Announce. announce, err := f.converter.BoostToAS( ctx, @@ -738,6 +778,24 @@ func (f *federate) Announce(ctx context.Context, boost *gtsmodel.Status) error { return gtserror.Newf("error converting boost to AS: %w", err) } + // If announce is pending approval, + // deliver it **ONLY** to the account + // it boosts, on behalf of the booster. + if util.PtrOrValue(boost.PendingApproval, false) { + return f.deliverToInboxOnly( + ctx, + boost.Account, + boost.BoostOfAccount, + announce, + ) + } + + // Parse relevant URI(s). + outboxIRI, err := parseURI(boost.Account.OutboxURI) + if err != nil { + return err + } + // Send the Announce via the Actor's outbox. if _, err := f.FederatingActor().Send( ctx, outboxIRI, announce, @@ -751,6 +809,57 @@ func (f *federate) Announce(ctx context.Context, boost *gtsmodel.Status) error { return nil } +// deliverToInboxOnly delivers the given Activity +// *only* to the inbox of targetAcct, on behalf of +// sendingAcct, regardless of the `to` and `cc` values +// set on the activity. This should be used specifically +// for sending "pending approval" activities. +func (f *federate) deliverToInboxOnly( + ctx context.Context, + sendingAcct *gtsmodel.Account, + targetAcct *gtsmodel.Account, + t vocab.Type, +) error { + if targetAcct.IsLocal() { + // If this is a local target, + // they've already received it. + return nil + } + + toInbox, err := url.Parse(targetAcct.InboxURI) + if err != nil { + return gtserror.Newf( + "error parsing target inbox uri: %w", + err, + ) + } + + tsport, err := f.TransportController().NewTransportForUsername( + ctx, + sendingAcct.Username, + ) + if err != nil { + return gtserror.Newf( + "error getting transport to deliver activity %T to target inbox %s: %w", + t, targetAcct.InboxURI, err, + ) + } + + m, err := ap.Serialize(t) + if err != nil { + return err + } + + if err := tsport.Deliver(ctx, m, toInbox); err != nil { + return gtserror.Newf( + "error delivering activity %T to target inbox %s: %w", + t, targetAcct.InboxURI, err, + ) + } + + return nil +} + func (f *federate) UpdateAccount(ctx context.Context, account *gtsmodel.Account) error { // Populate model. if err := f.state.DB.PopulateAccount(ctx, account); err != nil { @@ -1015,3 +1124,75 @@ func (f *federate) MoveAccount(ctx context.Context, account *gtsmodel.Account) e return nil } + +func (f *federate) AcceptInteraction( + ctx context.Context, + approval *gtsmodel.InteractionApproval, +) error { + // Populate model. + if err := f.state.DB.PopulateInteractionApproval(ctx, approval); err != nil { + return gtserror.Newf("error populating approval: %w", err) + } + + // Bail if interacting account is ours: + // we've already accepted internally and + // shouldn't send an Accept to ourselves. + if approval.InteractingAccount.IsLocal() { + return nil + } + + // Bail if account isn't ours: + // we can't Accept on another + // instance's behalf. (This + // should never happen but...) + if approval.Account.IsRemote() { + return nil + } + + // Parse relevant URI(s). + outboxIRI, err := parseURI(approval.Account.OutboxURI) + if err != nil { + return err + } + + acceptingAcctIRI, err := parseURI(approval.Account.URI) + if err != nil { + return err + } + + interactingAcctURI, err := parseURI(approval.InteractingAccount.URI) + if err != nil { + return err + } + + interactionURI, err := parseURI(approval.InteractionURI) + if err != nil { + return err + } + + // Create a new Accept. + accept := streams.NewActivityStreamsAccept() + + // Set interacted-with account + // as Actor of the Accept. + ap.AppendActorIRIs(accept, acceptingAcctIRI) + + // Set the interacted-with object + // as Object of the Accept. + ap.AppendObjectIRIs(accept, interactionURI) + + // Address the Accept To the interacting acct. + ap.AppendTo(accept, interactingAcctURI) + + // Send the Accept via the Actor's outbox. + if _, err := f.FederatingActor().Send( + ctx, outboxIRI, accept, + ); err != nil { + return gtserror.Newf( + "error sending activity %T for %v via outbox %s: %w", + accept, approval.InteractionType, outboxIRI, err, + ) + } + + return nil +} |