diff options
Diffstat (limited to 'internal/federation/federatingdb/undo.go')
-rw-r--r-- | internal/federation/federatingdb/undo.go | 200 |
1 files changed, 146 insertions, 54 deletions
diff --git a/internal/federation/federatingdb/undo.go b/internal/federation/federatingdb/undo.go index 517aa9cc6..c17bd2e90 100644 --- a/internal/federation/federatingdb/undo.go +++ b/internal/federation/federatingdb/undo.go @@ -26,11 +26,13 @@ import ( "github.com/superseriousbusiness/activity/streams/vocab" "github.com/superseriousbusiness/gotosocial/internal/ap" "github.com/superseriousbusiness/gotosocial/internal/db" + "github.com/superseriousbusiness/gotosocial/internal/gtscontext" + "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/log" ) func (f *federatingDB) Undo(ctx context.Context, undo vocab.ActivityStreamsUndo) error { - l := log.Entry{}.WithContext(ctx) + l := log.WithContext(ctx) if log.Level() >= level.DEBUG { i, err := marshalItem(undo) @@ -55,70 +57,160 @@ func (f *federatingDB) Undo(ctx context.Context, undo vocab.ActivityStreamsUndo) } for iter := undoObject.Begin(); iter != undoObject.End(); iter = iter.Next() { - if iter.GetType() == nil { + t := iter.GetType() + if t == nil { continue } - switch iter.GetType().GetTypeName() { + + switch t.GetTypeName() { case ap.ActivityFollow: - // UNDO FOLLOW - ASFollow, ok := iter.GetType().(vocab.ActivityStreamsFollow) - if !ok { - return errors.New("UNDO: couldn't parse follow into vocab.ActivityStreamsFollow") - } - // make sure the actor owns the follow - if !sameActor(undo.GetActivityStreamsActor(), ASFollow.GetActivityStreamsActor()) { - return errors.New("UNDO: follow actor and activity actor not the same") - } - // convert the follow to something we can understand - gtsFollow, err := f.typeConverter.ASFollowToFollow(ctx, ASFollow) - if err != nil { - return fmt.Errorf("UNDO: error converting asfollow to gtsfollow: %s", err) - } - // make sure the addressee of the original follow is the same as whatever inbox this landed in - if gtsFollow.TargetAccountID != receivingAccount.ID { - return errors.New("UNDO: follow object account and inbox account were not the same") + if err := f.undoFollow(ctx, receivingAccount, undo, t); err != nil { + return err } - // delete any existing FOLLOW - if err := f.state.DB.DeleteFollowByURI(ctx, gtsFollow.URI); err != nil && !errors.Is(err, db.ErrNoEntries) { - return fmt.Errorf("UNDO: db error removing follow: %s", err) - } - // delete any existing FOLLOW REQUEST - if err := f.state.DB.DeleteFollowRequestByURI(ctx, gtsFollow.URI); err != nil && !errors.Is(err, db.ErrNoEntries) { - return fmt.Errorf("UNDO: db error removing follow request: %s", err) - } - l.Debug("follow undone") - return nil case ap.ActivityLike: - // UNDO LIKE + if err := f.undoLike(ctx, receivingAccount, undo, t); err != nil { + return err + } case ap.ActivityAnnounce: - // UNDO BOOST/REBLOG/ANNOUNCE + // todo: undo boost / reblog / announce case ap.ActivityBlock: - // UNDO BLOCK - ASBlock, ok := iter.GetType().(vocab.ActivityStreamsBlock) - if !ok { - return errors.New("UNDO: couldn't parse block into vocab.ActivityStreamsBlock") + if err := f.undoBlock(ctx, receivingAccount, undo, t); err != nil { + return err } - // make sure the actor owns the follow - if !sameActor(undo.GetActivityStreamsActor(), ASBlock.GetActivityStreamsActor()) { - return errors.New("UNDO: block actor and activity actor not the same") - } - // convert the block to something we can understand - gtsBlock, err := f.typeConverter.ASBlockToBlock(ctx, ASBlock) - if err != nil { - return fmt.Errorf("UNDO: error converting asblock to gtsblock: %s", err) - } - // make sure the addressee of the original block is the same as whatever inbox this landed in - if gtsBlock.TargetAccountID != receivingAccount.ID { - return errors.New("UNDO: block object account and inbox account were not the same") - } - // delete any existing BLOCK - if err := f.state.DB.DeleteBlockByURI(ctx, gtsBlock.URI); err != nil { - return fmt.Errorf("UNDO: db error removing block: %s", err) - } - l.Debug("block undone") + } + } + + return nil +} + +func (f *federatingDB) undoFollow( + ctx context.Context, + receivingAccount *gtsmodel.Account, + undo vocab.ActivityStreamsUndo, + t vocab.Type, +) error { + Follow, ok := t.(vocab.ActivityStreamsFollow) + if !ok { + return errors.New("undoFollow: couldn't parse vocab.Type into vocab.ActivityStreamsFollow") + } + + // Make sure the undo actor owns the target. + if !sameActor(undo.GetActivityStreamsActor(), Follow.GetActivityStreamsActor()) { + // Ignore this Activity. + return nil + } + + follow, err := f.typeConverter.ASFollowToFollow(ctx, Follow) + if err != nil { + return fmt.Errorf("undoFollow: error converting ActivityStreams Follow to follow: %w", err) + } + + // Ensure addressee is follow target. + if follow.TargetAccountID != receivingAccount.ID { + // Ignore this Activity. + return nil + } + + // Delete any existing follow with this URI. + if err := f.state.DB.DeleteFollowByURI(ctx, follow.URI); err != nil && !errors.Is(err, db.ErrNoEntries) { + return fmt.Errorf("undoFollow: db error removing follow: %w", err) + } + + // Delete any existing follow request with this URI. + if err := f.state.DB.DeleteFollowRequestByURI(ctx, follow.URI); err != nil && !errors.Is(err, db.ErrNoEntries) { + return fmt.Errorf("undoFollow: db error removing follow request: %w", err) + } + + log.Debug(ctx, "Follow undone") + return nil +} + +func (f *federatingDB) undoLike( + ctx context.Context, + receivingAccount *gtsmodel.Account, + undo vocab.ActivityStreamsUndo, + t vocab.Type, +) error { + Like, ok := t.(vocab.ActivityStreamsLike) + if !ok { + return errors.New("undoLike: couldn't parse vocab.Type into vocab.ActivityStreamsLike") + } + + // Make sure the undo actor owns the target. + if !sameActor(undo.GetActivityStreamsActor(), Like.GetActivityStreamsActor()) { + // Ignore this Activity. + return nil + } + + fave, err := f.typeConverter.ASLikeToFave(ctx, Like) + if err != nil { + return fmt.Errorf("undoLike: error converting ActivityStreams Like to fave: %w", err) + } + + // Ensure addressee is fave target. + if fave.TargetAccountID != receivingAccount.ID { + // Ignore this Activity. + return nil + } + + // Ignore URI on Likes, since we often get multiple Likes + // with the same target and account ID, but differing URIs. + // Instead, we'll select using account and target status. + // Regardless of the URI, we can read an Undo Like to mean + // "I don't want to fave this post anymore". + fave, err = f.state.DB.GetStatusFave(gtscontext.SetBarebones(ctx), fave.AccountID, fave.StatusID) + if err != nil { + if errors.Is(err, db.ErrNoEntries) { + // We didn't have a like/fave + // for this combo anyway, ignore. return nil } + // Real error. + return fmt.Errorf("undoLike: db error getting fave from %s targeting %s: %w", fave.AccountID, fave.StatusID, err) + } + + // Delete the status fave. + if err := f.state.DB.DeleteStatusFaveByID(ctx, fave.ID); err != nil { + return fmt.Errorf("undoLike: db error deleting fave %s: %w", fave.ID, err) + } + + log.Debug(ctx, "Like undone") + return nil +} + +func (f *federatingDB) undoBlock( + ctx context.Context, + receivingAccount *gtsmodel.Account, + undo vocab.ActivityStreamsUndo, + t vocab.Type, +) error { + Block, ok := t.(vocab.ActivityStreamsBlock) + if !ok { + return errors.New("undoBlock: couldn't parse vocab.Type into vocab.ActivityStreamsBlock") + } + + // Make sure the undo actor owns the target. + if !sameActor(undo.GetActivityStreamsActor(), Block.GetActivityStreamsActor()) { + // Ignore this Activity. + return nil + } + + block, err := f.typeConverter.ASBlockToBlock(ctx, Block) + if err != nil { + return fmt.Errorf("undoBlock: error converting ActivityStreams Block to block: %w", err) + } + + // Ensure addressee is block target. + if block.TargetAccountID != receivingAccount.ID { + // Ignore this Activity. + return nil + } + + // Delete any existing BLOCK + if err := f.state.DB.DeleteBlockByURI(ctx, block.URI); err != nil && !errors.Is(err, db.ErrNoEntries) { + return fmt.Errorf("undoBlock: db error removing block: %w", err) } + log.Debug(ctx, "Block undone") return nil } |