gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

undo.go (6386B)


      1 // GoToSocial
      2 // Copyright (C) GoToSocial Authors admin@gotosocial.org
      3 // SPDX-License-Identifier: AGPL-3.0-or-later
      4 //
      5 // This program is free software: you can redistribute it and/or modify
      6 // it under the terms of the GNU Affero General Public License as published by
      7 // the Free Software Foundation, either version 3 of the License, or
      8 // (at your option) any later version.
      9 //
     10 // This program is distributed in the hope that it will be useful,
     11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
     12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13 // GNU Affero General Public License for more details.
     14 //
     15 // You should have received a copy of the GNU Affero General Public License
     16 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
     17 
     18 package federatingdb
     19 
     20 import (
     21 	"context"
     22 	"errors"
     23 	"fmt"
     24 
     25 	"codeberg.org/gruf/go-logger/v2/level"
     26 	"github.com/superseriousbusiness/activity/streams/vocab"
     27 	"github.com/superseriousbusiness/gotosocial/internal/ap"
     28 	"github.com/superseriousbusiness/gotosocial/internal/db"
     29 	"github.com/superseriousbusiness/gotosocial/internal/gtscontext"
     30 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
     31 	"github.com/superseriousbusiness/gotosocial/internal/log"
     32 )
     33 
     34 func (f *federatingDB) Undo(ctx context.Context, undo vocab.ActivityStreamsUndo) error {
     35 	l := log.WithContext(ctx)
     36 
     37 	if log.Level() >= level.DEBUG {
     38 		i, err := marshalItem(undo)
     39 		if err != nil {
     40 			return err
     41 		}
     42 		l = l.WithField("undo", i)
     43 		l.Debug("entering Undo")
     44 	}
     45 
     46 	receivingAccount, _, internal := extractFromCtx(ctx)
     47 	if internal {
     48 		return nil // Already processed.
     49 	}
     50 
     51 	undoObject := undo.GetActivityStreamsObject()
     52 	if undoObject == nil {
     53 		return errors.New("UNDO: no object set on vocab.ActivityStreamsUndo")
     54 	}
     55 
     56 	for iter := undoObject.Begin(); iter != undoObject.End(); iter = iter.Next() {
     57 		t := iter.GetType()
     58 		if t == nil {
     59 			continue
     60 		}
     61 
     62 		switch t.GetTypeName() {
     63 		case ap.ActivityFollow:
     64 			if err := f.undoFollow(ctx, receivingAccount, undo, t); err != nil {
     65 				return err
     66 			}
     67 		case ap.ActivityLike:
     68 			if err := f.undoLike(ctx, receivingAccount, undo, t); err != nil {
     69 				return err
     70 			}
     71 		case ap.ActivityAnnounce:
     72 			// todo: undo boost / reblog / announce
     73 		case ap.ActivityBlock:
     74 			if err := f.undoBlock(ctx, receivingAccount, undo, t); err != nil {
     75 				return err
     76 			}
     77 		}
     78 	}
     79 
     80 	return nil
     81 }
     82 
     83 func (f *federatingDB) undoFollow(
     84 	ctx context.Context,
     85 	receivingAccount *gtsmodel.Account,
     86 	undo vocab.ActivityStreamsUndo,
     87 	t vocab.Type,
     88 ) error {
     89 	Follow, ok := t.(vocab.ActivityStreamsFollow)
     90 	if !ok {
     91 		return errors.New("undoFollow: couldn't parse vocab.Type into vocab.ActivityStreamsFollow")
     92 	}
     93 
     94 	// Make sure the undo actor owns the target.
     95 	if !sameActor(undo.GetActivityStreamsActor(), Follow.GetActivityStreamsActor()) {
     96 		// Ignore this Activity.
     97 		return nil
     98 	}
     99 
    100 	follow, err := f.typeConverter.ASFollowToFollow(ctx, Follow)
    101 	if err != nil {
    102 		return fmt.Errorf("undoFollow: error converting ActivityStreams Follow to follow: %w", err)
    103 	}
    104 
    105 	// Ensure addressee is follow target.
    106 	if follow.TargetAccountID != receivingAccount.ID {
    107 		// Ignore this Activity.
    108 		return nil
    109 	}
    110 
    111 	// Delete any existing follow with this URI.
    112 	if err := f.state.DB.DeleteFollowByURI(ctx, follow.URI); err != nil && !errors.Is(err, db.ErrNoEntries) {
    113 		return fmt.Errorf("undoFollow: db error removing follow: %w", err)
    114 	}
    115 
    116 	// Delete any existing follow request with this URI.
    117 	if err := f.state.DB.DeleteFollowRequestByURI(ctx, follow.URI); err != nil && !errors.Is(err, db.ErrNoEntries) {
    118 		return fmt.Errorf("undoFollow: db error removing follow request: %w", err)
    119 	}
    120 
    121 	log.Debug(ctx, "Follow undone")
    122 	return nil
    123 }
    124 
    125 func (f *federatingDB) undoLike(
    126 	ctx context.Context,
    127 	receivingAccount *gtsmodel.Account,
    128 	undo vocab.ActivityStreamsUndo,
    129 	t vocab.Type,
    130 ) error {
    131 	Like, ok := t.(vocab.ActivityStreamsLike)
    132 	if !ok {
    133 		return errors.New("undoLike: couldn't parse vocab.Type into vocab.ActivityStreamsLike")
    134 	}
    135 
    136 	// Make sure the undo actor owns the target.
    137 	if !sameActor(undo.GetActivityStreamsActor(), Like.GetActivityStreamsActor()) {
    138 		// Ignore this Activity.
    139 		return nil
    140 	}
    141 
    142 	fave, err := f.typeConverter.ASLikeToFave(ctx, Like)
    143 	if err != nil {
    144 		return fmt.Errorf("undoLike: error converting ActivityStreams Like to fave: %w", err)
    145 	}
    146 
    147 	// Ensure addressee is fave target.
    148 	if fave.TargetAccountID != receivingAccount.ID {
    149 		// Ignore this Activity.
    150 		return nil
    151 	}
    152 
    153 	// Ignore URI on Likes, since we often get multiple Likes
    154 	// with the same target and account ID, but differing URIs.
    155 	// Instead, we'll select using account and target status.
    156 	// Regardless of the URI, we can read an Undo Like to mean
    157 	// "I don't want to fave this post anymore".
    158 	fave, err = f.state.DB.GetStatusFave(gtscontext.SetBarebones(ctx), fave.AccountID, fave.StatusID)
    159 	if err != nil {
    160 		if errors.Is(err, db.ErrNoEntries) {
    161 			// We didn't have a like/fave
    162 			// for this combo anyway, ignore.
    163 			return nil
    164 		}
    165 		// Real error.
    166 		return fmt.Errorf("undoLike: db error getting fave from %s targeting %s: %w", fave.AccountID, fave.StatusID, err)
    167 	}
    168 
    169 	// Delete the status fave.
    170 	if err := f.state.DB.DeleteStatusFaveByID(ctx, fave.ID); err != nil {
    171 		return fmt.Errorf("undoLike: db error deleting fave %s: %w", fave.ID, err)
    172 	}
    173 
    174 	log.Debug(ctx, "Like undone")
    175 	return nil
    176 }
    177 
    178 func (f *federatingDB) undoBlock(
    179 	ctx context.Context,
    180 	receivingAccount *gtsmodel.Account,
    181 	undo vocab.ActivityStreamsUndo,
    182 	t vocab.Type,
    183 ) error {
    184 	Block, ok := t.(vocab.ActivityStreamsBlock)
    185 	if !ok {
    186 		return errors.New("undoBlock: couldn't parse vocab.Type into vocab.ActivityStreamsBlock")
    187 	}
    188 
    189 	// Make sure the undo actor owns the target.
    190 	if !sameActor(undo.GetActivityStreamsActor(), Block.GetActivityStreamsActor()) {
    191 		// Ignore this Activity.
    192 		return nil
    193 	}
    194 
    195 	block, err := f.typeConverter.ASBlockToBlock(ctx, Block)
    196 	if err != nil {
    197 		return fmt.Errorf("undoBlock: error converting ActivityStreams Block to block: %w", err)
    198 	}
    199 
    200 	// Ensure addressee is block target.
    201 	if block.TargetAccountID != receivingAccount.ID {
    202 		// Ignore this Activity.
    203 		return nil
    204 	}
    205 
    206 	// Delete any existing BLOCK
    207 	if err := f.state.DB.DeleteBlockByURI(ctx, block.URI); err != nil && !errors.Is(err, db.ErrNoEntries) {
    208 		return fmt.Errorf("undoBlock: db error removing block: %w", err)
    209 	}
    210 
    211 	log.Debug(ctx, "Block undone")
    212 	return nil
    213 }