gtsocial-umbx

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

fromclientapi.go (34395B)


      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 processing
     19 
     20 import (
     21 	"context"
     22 	"errors"
     23 	"fmt"
     24 	"net/url"
     25 
     26 	"codeberg.org/gruf/go-kv"
     27 	"codeberg.org/gruf/go-logger/v2/level"
     28 	"github.com/superseriousbusiness/activity/pub"
     29 	"github.com/superseriousbusiness/activity/streams"
     30 	"github.com/superseriousbusiness/gotosocial/internal/ap"
     31 	"github.com/superseriousbusiness/gotosocial/internal/gtserror"
     32 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
     33 	"github.com/superseriousbusiness/gotosocial/internal/log"
     34 	"github.com/superseriousbusiness/gotosocial/internal/messages"
     35 )
     36 
     37 func (p *Processor) ProcessFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
     38 	// Allocate new log fields slice
     39 	fields := make([]kv.Field, 3, 4)
     40 	fields[0] = kv.Field{"activityType", clientMsg.APActivityType}
     41 	fields[1] = kv.Field{"objectType", clientMsg.APObjectType}
     42 	fields[2] = kv.Field{"fromAccount", clientMsg.OriginAccount.Username}
     43 
     44 	if clientMsg.GTSModel != nil &&
     45 		log.Level() >= level.DEBUG {
     46 		// Append converted model to log
     47 		fields = append(fields, kv.Field{
     48 			"model", clientMsg.GTSModel,
     49 		})
     50 	}
     51 
     52 	// Log this federated message
     53 	l := log.WithContext(ctx).WithFields(fields...)
     54 	l.Info("processing from client")
     55 
     56 	switch clientMsg.APActivityType {
     57 	case ap.ActivityCreate:
     58 		// CREATE
     59 		switch clientMsg.APObjectType {
     60 		case ap.ObjectProfile, ap.ActorPerson:
     61 			// CREATE ACCOUNT/PROFILE
     62 			return p.processCreateAccountFromClientAPI(ctx, clientMsg)
     63 		case ap.ObjectNote:
     64 			// CREATE NOTE
     65 			return p.processCreateStatusFromClientAPI(ctx, clientMsg)
     66 		case ap.ActivityFollow:
     67 			// CREATE FOLLOW REQUEST
     68 			return p.processCreateFollowRequestFromClientAPI(ctx, clientMsg)
     69 		case ap.ActivityLike:
     70 			// CREATE LIKE/FAVE
     71 			return p.processCreateFaveFromClientAPI(ctx, clientMsg)
     72 		case ap.ActivityAnnounce:
     73 			// CREATE BOOST/ANNOUNCE
     74 			return p.processCreateAnnounceFromClientAPI(ctx, clientMsg)
     75 		case ap.ActivityBlock:
     76 			// CREATE BLOCK
     77 			return p.processCreateBlockFromClientAPI(ctx, clientMsg)
     78 		}
     79 	case ap.ActivityUpdate:
     80 		// UPDATE
     81 		switch clientMsg.APObjectType {
     82 		case ap.ObjectProfile, ap.ActorPerson:
     83 			// UPDATE ACCOUNT/PROFILE
     84 			return p.processUpdateAccountFromClientAPI(ctx, clientMsg)
     85 		case ap.ActivityFlag:
     86 			// UPDATE A FLAG/REPORT (mark as resolved/closed)
     87 			return p.processUpdateReportFromClientAPI(ctx, clientMsg)
     88 		}
     89 	case ap.ActivityAccept:
     90 		// ACCEPT
     91 		if clientMsg.APObjectType == ap.ActivityFollow {
     92 			// ACCEPT FOLLOW
     93 			return p.processAcceptFollowFromClientAPI(ctx, clientMsg)
     94 		}
     95 	case ap.ActivityReject:
     96 		// REJECT
     97 		if clientMsg.APObjectType == ap.ActivityFollow {
     98 			// REJECT FOLLOW (request)
     99 			return p.processRejectFollowFromClientAPI(ctx, clientMsg)
    100 		}
    101 	case ap.ActivityUndo:
    102 		// UNDO
    103 		switch clientMsg.APObjectType {
    104 		case ap.ActivityFollow:
    105 			// UNDO FOLLOW
    106 			return p.processUndoFollowFromClientAPI(ctx, clientMsg)
    107 		case ap.ActivityBlock:
    108 			// UNDO BLOCK
    109 			return p.processUndoBlockFromClientAPI(ctx, clientMsg)
    110 		case ap.ActivityLike:
    111 			// UNDO LIKE/FAVE
    112 			return p.processUndoFaveFromClientAPI(ctx, clientMsg)
    113 		case ap.ActivityAnnounce:
    114 			// UNDO ANNOUNCE/BOOST
    115 			return p.processUndoAnnounceFromClientAPI(ctx, clientMsg)
    116 		}
    117 	case ap.ActivityDelete:
    118 		// DELETE
    119 		switch clientMsg.APObjectType {
    120 		case ap.ObjectNote:
    121 			// DELETE STATUS/NOTE
    122 			return p.processDeleteStatusFromClientAPI(ctx, clientMsg)
    123 		case ap.ObjectProfile, ap.ActorPerson:
    124 			// DELETE ACCOUNT/PROFILE
    125 			return p.processDeleteAccountFromClientAPI(ctx, clientMsg)
    126 		}
    127 	case ap.ActivityFlag:
    128 		// FLAG
    129 		if clientMsg.APObjectType == ap.ObjectProfile {
    130 			// FLAG/REPORT A PROFILE
    131 			return p.processReportAccountFromClientAPI(ctx, clientMsg)
    132 		}
    133 	}
    134 	return nil
    135 }
    136 
    137 func (p *Processor) processCreateAccountFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
    138 	account, ok := clientMsg.GTSModel.(*gtsmodel.Account)
    139 	if !ok {
    140 		return errors.New("account was not parseable as *gtsmodel.Account")
    141 	}
    142 
    143 	// Do nothing if this isn't our activity.
    144 	if !account.IsLocal() {
    145 		return nil
    146 	}
    147 
    148 	// get the user this account belongs to
    149 	user, err := p.state.DB.GetUserByAccountID(ctx, account.ID)
    150 	if err != nil {
    151 		return err
    152 	}
    153 
    154 	// email a confirmation to this user
    155 	return p.User().EmailSendConfirmation(ctx, user, account.Username)
    156 }
    157 
    158 func (p *Processor) processCreateStatusFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
    159 	status, ok := clientMsg.GTSModel.(*gtsmodel.Status)
    160 	if !ok {
    161 		return gtserror.New("status was not parseable as *gtsmodel.Status")
    162 	}
    163 
    164 	if err := p.timelineAndNotifyStatus(ctx, status); err != nil {
    165 		return gtserror.Newf("error timelining status: %w", err)
    166 	}
    167 
    168 	if status.InReplyToID != "" {
    169 		// Interaction counts changed on the replied status;
    170 		// uncache the prepared version from all timelines.
    171 		p.invalidateStatusFromTimelines(ctx, status.InReplyToID)
    172 	}
    173 
    174 	if err := p.federateStatus(ctx, status); err != nil {
    175 		return gtserror.Newf("error federating status: %w", err)
    176 	}
    177 
    178 	return nil
    179 }
    180 
    181 func (p *Processor) processCreateFollowRequestFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
    182 	followRequest, ok := clientMsg.GTSModel.(*gtsmodel.FollowRequest)
    183 	if !ok {
    184 		return errors.New("followrequest was not parseable as *gtsmodel.FollowRequest")
    185 	}
    186 
    187 	if err := p.notifyFollowRequest(ctx, followRequest); err != nil {
    188 		return err
    189 	}
    190 
    191 	return p.federateFollow(ctx, followRequest, clientMsg.OriginAccount, clientMsg.TargetAccount)
    192 }
    193 
    194 func (p *Processor) processCreateFaveFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
    195 	statusFave, ok := clientMsg.GTSModel.(*gtsmodel.StatusFave)
    196 	if !ok {
    197 		return gtserror.New("statusFave was not parseable as *gtsmodel.StatusFave")
    198 	}
    199 
    200 	if err := p.notifyFave(ctx, statusFave); err != nil {
    201 		return gtserror.Newf("error notifying status fave: %w", err)
    202 	}
    203 
    204 	// Interaction counts changed on the faved status;
    205 	// uncache the prepared version from all timelines.
    206 	p.invalidateStatusFromTimelines(ctx, statusFave.StatusID)
    207 
    208 	if err := p.federateFave(ctx, statusFave, clientMsg.OriginAccount, clientMsg.TargetAccount); err != nil {
    209 		return gtserror.Newf("error federating status fave: %w", err)
    210 	}
    211 
    212 	return nil
    213 }
    214 
    215 func (p *Processor) processCreateAnnounceFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
    216 	status, ok := clientMsg.GTSModel.(*gtsmodel.Status)
    217 	if !ok {
    218 		return errors.New("boost was not parseable as *gtsmodel.Status")
    219 	}
    220 
    221 	// Timeline and notify.
    222 	if err := p.timelineAndNotifyStatus(ctx, status); err != nil {
    223 		return gtserror.Newf("error timelining boost: %w", err)
    224 	}
    225 
    226 	if err := p.notifyAnnounce(ctx, status); err != nil {
    227 		return gtserror.Newf("error notifying boost: %w", err)
    228 	}
    229 
    230 	// Interaction counts changed on the boosted status;
    231 	// uncache the prepared version from all timelines.
    232 	p.invalidateStatusFromTimelines(ctx, status.BoostOfID)
    233 
    234 	if err := p.federateAnnounce(ctx, status, clientMsg.OriginAccount, clientMsg.TargetAccount); err != nil {
    235 		return gtserror.Newf("error federating boost: %w", err)
    236 	}
    237 
    238 	return nil
    239 }
    240 
    241 func (p *Processor) processCreateBlockFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
    242 	block, ok := clientMsg.GTSModel.(*gtsmodel.Block)
    243 	if !ok {
    244 		return errors.New("block was not parseable as *gtsmodel.Block")
    245 	}
    246 
    247 	// remove any of the blocking account's statuses from the blocked account's timeline, and vice versa
    248 	if err := p.state.Timelines.Home.WipeItemsFromAccountID(ctx, block.AccountID, block.TargetAccountID); err != nil {
    249 		return err
    250 	}
    251 	if err := p.state.Timelines.Home.WipeItemsFromAccountID(ctx, block.TargetAccountID, block.AccountID); err != nil {
    252 		return err
    253 	}
    254 
    255 	// TODO: same with notifications
    256 	// TODO: same with bookmarks
    257 
    258 	return p.federateBlock(ctx, block)
    259 }
    260 
    261 func (p *Processor) processUpdateAccountFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
    262 	account, ok := clientMsg.GTSModel.(*gtsmodel.Account)
    263 	if !ok {
    264 		return errors.New("account was not parseable as *gtsmodel.Account")
    265 	}
    266 
    267 	return p.federateAccountUpdate(ctx, account, clientMsg.OriginAccount)
    268 }
    269 
    270 func (p *Processor) processUpdateReportFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
    271 	report, ok := clientMsg.GTSModel.(*gtsmodel.Report)
    272 	if !ok {
    273 		return errors.New("report was not parseable as *gtsmodel.Report")
    274 	}
    275 
    276 	if report.Account.IsRemote() {
    277 		// Report creator is a remote account,
    278 		// we shouldn't email or notify them.
    279 		return nil
    280 	}
    281 
    282 	return p.emailReportClosed(ctx, report)
    283 }
    284 
    285 func (p *Processor) processAcceptFollowFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
    286 	follow, ok := clientMsg.GTSModel.(*gtsmodel.Follow)
    287 	if !ok {
    288 		return errors.New("accept was not parseable as *gtsmodel.Follow")
    289 	}
    290 
    291 	if err := p.notifyFollow(ctx, follow, clientMsg.TargetAccount); err != nil {
    292 		return err
    293 	}
    294 
    295 	return p.federateAcceptFollowRequest(ctx, follow)
    296 }
    297 
    298 func (p *Processor) processRejectFollowFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
    299 	followRequest, ok := clientMsg.GTSModel.(*gtsmodel.FollowRequest)
    300 	if !ok {
    301 		return errors.New("reject was not parseable as *gtsmodel.FollowRequest")
    302 	}
    303 
    304 	return p.federateRejectFollowRequest(ctx, followRequest)
    305 }
    306 
    307 func (p *Processor) processUndoFollowFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
    308 	follow, ok := clientMsg.GTSModel.(*gtsmodel.Follow)
    309 	if !ok {
    310 		return errors.New("undo was not parseable as *gtsmodel.Follow")
    311 	}
    312 	return p.federateUnfollow(ctx, follow, clientMsg.OriginAccount, clientMsg.TargetAccount)
    313 }
    314 
    315 func (p *Processor) processUndoBlockFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
    316 	block, ok := clientMsg.GTSModel.(*gtsmodel.Block)
    317 	if !ok {
    318 		return errors.New("undo was not parseable as *gtsmodel.Block")
    319 	}
    320 	return p.federateUnblock(ctx, block)
    321 }
    322 
    323 func (p *Processor) processUndoFaveFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
    324 	statusFave, ok := clientMsg.GTSModel.(*gtsmodel.StatusFave)
    325 	if !ok {
    326 		return gtserror.New("statusFave was not parseable as *gtsmodel.StatusFave")
    327 	}
    328 
    329 	// Interaction counts changed on the faved status;
    330 	// uncache the prepared version from all timelines.
    331 	p.invalidateStatusFromTimelines(ctx, statusFave.StatusID)
    332 
    333 	if err := p.federateUnfave(ctx, statusFave, clientMsg.OriginAccount, clientMsg.TargetAccount); err != nil {
    334 		return gtserror.Newf("error federating status unfave: %w", err)
    335 	}
    336 
    337 	return nil
    338 }
    339 
    340 func (p *Processor) processUndoAnnounceFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
    341 	status, ok := clientMsg.GTSModel.(*gtsmodel.Status)
    342 	if !ok {
    343 		return errors.New("boost was not parseable as *gtsmodel.Status")
    344 	}
    345 
    346 	if err := p.state.DB.DeleteStatusByID(ctx, status.ID); err != nil {
    347 		return gtserror.Newf("db error deleting boost: %w", err)
    348 	}
    349 
    350 	if err := p.deleteStatusFromTimelines(ctx, status.ID); err != nil {
    351 		return gtserror.Newf("error removing boost from timelines: %w", err)
    352 	}
    353 
    354 	// Interaction counts changed on the boosted status;
    355 	// uncache the prepared version from all timelines.
    356 	p.invalidateStatusFromTimelines(ctx, status.BoostOfID)
    357 
    358 	if err := p.federateUnannounce(ctx, status, clientMsg.OriginAccount, clientMsg.TargetAccount); err != nil {
    359 		return gtserror.Newf("error federating status unboost: %w", err)
    360 	}
    361 
    362 	return nil
    363 }
    364 
    365 func (p *Processor) processDeleteStatusFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
    366 	status, ok := clientMsg.GTSModel.(*gtsmodel.Status)
    367 	if !ok {
    368 		return gtserror.New("status was not parseable as *gtsmodel.Status")
    369 	}
    370 
    371 	if err := p.state.DB.PopulateStatus(ctx, status); err != nil {
    372 		return gtserror.Newf("db error populating status: %w", err)
    373 	}
    374 
    375 	// Don't delete attachments, just unattach them: this
    376 	// request comes from the client API and the poster
    377 	// may want to use attachments again in a new post.
    378 	deleteAttachments := false
    379 	if err := p.wipeStatus(ctx, status, deleteAttachments); err != nil {
    380 		return gtserror.Newf("error wiping status: %w", err)
    381 	}
    382 
    383 	if status.InReplyToID != "" {
    384 		// Interaction counts changed on the replied status;
    385 		// uncache the prepared version from all timelines.
    386 		p.invalidateStatusFromTimelines(ctx, status.InReplyToID)
    387 	}
    388 
    389 	if err := p.federateStatusDelete(ctx, status); err != nil {
    390 		return gtserror.Newf("error federating status delete: %w", err)
    391 	}
    392 
    393 	return nil
    394 }
    395 
    396 func (p *Processor) processDeleteAccountFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
    397 	// the origin of the delete could be either a domain block, or an action by another (or this) account
    398 	var origin string
    399 	if domainBlock, ok := clientMsg.GTSModel.(*gtsmodel.DomainBlock); ok {
    400 		// origin is a domain block
    401 		origin = domainBlock.ID
    402 	} else {
    403 		// origin is whichever account caused this message
    404 		origin = clientMsg.OriginAccount.ID
    405 	}
    406 
    407 	if err := p.federateAccountDelete(ctx, clientMsg.TargetAccount); err != nil {
    408 		return err
    409 	}
    410 
    411 	return p.account.Delete(ctx, clientMsg.TargetAccount, origin)
    412 }
    413 
    414 func (p *Processor) processReportAccountFromClientAPI(ctx context.Context, clientMsg messages.FromClientAPI) error {
    415 	report, ok := clientMsg.GTSModel.(*gtsmodel.Report)
    416 	if !ok {
    417 		return errors.New("report was not parseable as *gtsmodel.Report")
    418 	}
    419 
    420 	if *report.Forwarded {
    421 		if err := p.federateReport(ctx, report); err != nil {
    422 			return fmt.Errorf("processReportAccountFromClientAPI: error federating report: %w", err)
    423 		}
    424 	}
    425 
    426 	if err := p.emailReport(ctx, report); err != nil {
    427 		return fmt.Errorf("processReportAccountFromClientAPI: error notifying report: %w", err)
    428 	}
    429 
    430 	return nil
    431 }
    432 
    433 // TODO: move all the below functions into federation.Federator
    434 
    435 func (p *Processor) federateAccountDelete(ctx context.Context, account *gtsmodel.Account) error {
    436 	// Do nothing if this isn't our activity.
    437 	if !account.IsLocal() {
    438 		return nil
    439 	}
    440 
    441 	outboxIRI, err := url.Parse(account.OutboxURI)
    442 	if err != nil {
    443 		return fmt.Errorf("federateAccountDelete: error parsing outboxURI %s: %s", account.OutboxURI, err)
    444 	}
    445 
    446 	actorIRI, err := url.Parse(account.URI)
    447 	if err != nil {
    448 		return fmt.Errorf("federateAccountDelete: error parsing actorIRI %s: %s", account.URI, err)
    449 	}
    450 
    451 	followersIRI, err := url.Parse(account.FollowersURI)
    452 	if err != nil {
    453 		return fmt.Errorf("federateAccountDelete: error parsing followersIRI %s: %s", account.FollowersURI, err)
    454 	}
    455 
    456 	publicIRI, err := url.Parse(pub.PublicActivityPubIRI)
    457 	if err != nil {
    458 		return fmt.Errorf("federateAccountDelete: error parsing url %s: %s", pub.PublicActivityPubIRI, err)
    459 	}
    460 
    461 	// create a delete and set the appropriate actor on it
    462 	delete := streams.NewActivityStreamsDelete()
    463 
    464 	// set the actor for the delete; no matter who deleted it we should use the account owner for this
    465 	deleteActor := streams.NewActivityStreamsActorProperty()
    466 	deleteActor.AppendIRI(actorIRI)
    467 	delete.SetActivityStreamsActor(deleteActor)
    468 
    469 	// Set the account IRI as the 'object' property.
    470 	deleteObject := streams.NewActivityStreamsObjectProperty()
    471 	deleteObject.AppendIRI(actorIRI)
    472 	delete.SetActivityStreamsObject(deleteObject)
    473 
    474 	// send to followers...
    475 	deleteTo := streams.NewActivityStreamsToProperty()
    476 	deleteTo.AppendIRI(followersIRI)
    477 	delete.SetActivityStreamsTo(deleteTo)
    478 
    479 	// ... and CC to public
    480 	deleteCC := streams.NewActivityStreamsCcProperty()
    481 	deleteCC.AppendIRI(publicIRI)
    482 	delete.SetActivityStreamsCc(deleteCC)
    483 
    484 	_, err = p.federator.FederatingActor().Send(ctx, outboxIRI, delete)
    485 	return err
    486 }
    487 
    488 func (p *Processor) federateStatus(ctx context.Context, status *gtsmodel.Status) error {
    489 	// do nothing if the status shouldn't be federated
    490 	if !*status.Federated {
    491 		return nil
    492 	}
    493 
    494 	if status.Account == nil {
    495 		statusAccount, err := p.state.DB.GetAccountByID(ctx, status.AccountID)
    496 		if err != nil {
    497 			return fmt.Errorf("federateStatus: error fetching status author account: %s", err)
    498 		}
    499 		status.Account = statusAccount
    500 	}
    501 
    502 	// Do nothing if this isn't our activity.
    503 	if !status.Account.IsLocal() {
    504 		return nil
    505 	}
    506 
    507 	asStatus, err := p.tc.StatusToAS(ctx, status)
    508 	if err != nil {
    509 		return fmt.Errorf("federateStatus: error converting status to as format: %s", err)
    510 	}
    511 
    512 	create, err := p.tc.WrapNoteInCreate(asStatus, false)
    513 	if err != nil {
    514 		return fmt.Errorf("federateStatus: error wrapping status in create: %s", err)
    515 	}
    516 
    517 	outboxIRI, err := url.Parse(status.Account.OutboxURI)
    518 	if err != nil {
    519 		return fmt.Errorf("federateStatus: error parsing outboxURI %s: %s", status.Account.OutboxURI, err)
    520 	}
    521 
    522 	_, err = p.federator.FederatingActor().Send(ctx, outboxIRI, create)
    523 	return err
    524 }
    525 
    526 func (p *Processor) federateStatusDelete(ctx context.Context, status *gtsmodel.Status) error {
    527 	if status.Account == nil {
    528 		statusAccount, err := p.state.DB.GetAccountByID(ctx, status.AccountID)
    529 		if err != nil {
    530 			return fmt.Errorf("federateStatusDelete: error fetching status author account: %s", err)
    531 		}
    532 		status.Account = statusAccount
    533 	}
    534 
    535 	// Do nothing if this isn't our activity.
    536 	if !status.Account.IsLocal() {
    537 		return nil
    538 	}
    539 
    540 	delete, err := p.tc.StatusToASDelete(ctx, status)
    541 	if err != nil {
    542 		return fmt.Errorf("federateStatusDelete: error creating Delete: %w", err)
    543 	}
    544 
    545 	outboxIRI, err := url.Parse(status.Account.OutboxURI)
    546 	if err != nil {
    547 		return fmt.Errorf("federateStatusDelete: error parsing outboxURI %s: %w", status.Account.OutboxURI, err)
    548 	}
    549 
    550 	_, err = p.federator.FederatingActor().Send(ctx, outboxIRI, delete)
    551 	return err
    552 }
    553 
    554 func (p *Processor) federateFollow(ctx context.Context, followRequest *gtsmodel.FollowRequest, originAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) error {
    555 	// Do nothing if both accounts are local.
    556 	if originAccount.IsLocal() && targetAccount.IsLocal() {
    557 		return nil
    558 	}
    559 
    560 	follow := p.tc.FollowRequestToFollow(ctx, followRequest)
    561 
    562 	asFollow, err := p.tc.FollowToAS(ctx, follow, originAccount, targetAccount)
    563 	if err != nil {
    564 		return fmt.Errorf("federateFollow: error converting follow to as format: %s", err)
    565 	}
    566 
    567 	outboxIRI, err := url.Parse(originAccount.OutboxURI)
    568 	if err != nil {
    569 		return fmt.Errorf("federateFollow: error parsing outboxURI %s: %s", originAccount.OutboxURI, err)
    570 	}
    571 
    572 	_, err = p.federator.FederatingActor().Send(ctx, outboxIRI, asFollow)
    573 	return err
    574 }
    575 
    576 func (p *Processor) federateUnfollow(ctx context.Context, follow *gtsmodel.Follow, originAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) error {
    577 	// Do nothing if both accounts are local.
    578 	if originAccount.IsLocal() && targetAccount.IsLocal() {
    579 		return nil
    580 	}
    581 
    582 	// recreate the follow
    583 	asFollow, err := p.tc.FollowToAS(ctx, follow, originAccount, targetAccount)
    584 	if err != nil {
    585 		return fmt.Errorf("federateUnfollow: error converting follow to as format: %s", err)
    586 	}
    587 
    588 	targetAccountURI, err := url.Parse(targetAccount.URI)
    589 	if err != nil {
    590 		return fmt.Errorf("error parsing uri %s: %s", targetAccount.URI, err)
    591 	}
    592 
    593 	// create an Undo and set the appropriate actor on it
    594 	undo := streams.NewActivityStreamsUndo()
    595 	undo.SetActivityStreamsActor(asFollow.GetActivityStreamsActor())
    596 
    597 	// Set the recreated follow as the 'object' property.
    598 	undoObject := streams.NewActivityStreamsObjectProperty()
    599 	undoObject.AppendActivityStreamsFollow(asFollow)
    600 	undo.SetActivityStreamsObject(undoObject)
    601 
    602 	// Set the To of the undo as the target of the recreated follow
    603 	undoTo := streams.NewActivityStreamsToProperty()
    604 	undoTo.AppendIRI(targetAccountURI)
    605 	undo.SetActivityStreamsTo(undoTo)
    606 
    607 	outboxIRI, err := url.Parse(originAccount.OutboxURI)
    608 	if err != nil {
    609 		return fmt.Errorf("federateUnfollow: error parsing outboxURI %s: %s", originAccount.OutboxURI, err)
    610 	}
    611 
    612 	// send off the Undo
    613 	_, err = p.federator.FederatingActor().Send(ctx, outboxIRI, undo)
    614 	return err
    615 }
    616 
    617 func (p *Processor) federateUnfave(ctx context.Context, fave *gtsmodel.StatusFave, originAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) error {
    618 	// Do nothing if both accounts are local.
    619 	if originAccount.IsLocal() && targetAccount.IsLocal() {
    620 		return nil
    621 	}
    622 
    623 	// create the AS fave
    624 	asFave, err := p.tc.FaveToAS(ctx, fave)
    625 	if err != nil {
    626 		return fmt.Errorf("federateFave: error converting fave to as format: %s", err)
    627 	}
    628 
    629 	targetAccountURI, err := url.Parse(targetAccount.URI)
    630 	if err != nil {
    631 		return fmt.Errorf("error parsing uri %s: %s", targetAccount.URI, err)
    632 	}
    633 
    634 	// create an Undo and set the appropriate actor on it
    635 	undo := streams.NewActivityStreamsUndo()
    636 	undo.SetActivityStreamsActor(asFave.GetActivityStreamsActor())
    637 
    638 	// Set the fave as the 'object' property.
    639 	undoObject := streams.NewActivityStreamsObjectProperty()
    640 	undoObject.AppendActivityStreamsLike(asFave)
    641 	undo.SetActivityStreamsObject(undoObject)
    642 
    643 	// Set the To of the undo as the target of the fave
    644 	undoTo := streams.NewActivityStreamsToProperty()
    645 	undoTo.AppendIRI(targetAccountURI)
    646 	undo.SetActivityStreamsTo(undoTo)
    647 
    648 	outboxIRI, err := url.Parse(originAccount.OutboxURI)
    649 	if err != nil {
    650 		return fmt.Errorf("federateFave: error parsing outboxURI %s: %s", originAccount.OutboxURI, err)
    651 	}
    652 	_, err = p.federator.FederatingActor().Send(ctx, outboxIRI, undo)
    653 	return err
    654 }
    655 
    656 func (p *Processor) federateUnannounce(ctx context.Context, boost *gtsmodel.Status, originAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) error {
    657 	// Do nothing if this isn't our activity.
    658 	if !originAccount.IsLocal() {
    659 		return nil
    660 	}
    661 
    662 	asAnnounce, err := p.tc.BoostToAS(ctx, boost, originAccount, targetAccount)
    663 	if err != nil {
    664 		return fmt.Errorf("federateUnannounce: error converting status to announce: %s", err)
    665 	}
    666 
    667 	// create an Undo and set the appropriate actor on it
    668 	undo := streams.NewActivityStreamsUndo()
    669 	undo.SetActivityStreamsActor(asAnnounce.GetActivityStreamsActor())
    670 
    671 	// Set the boost as the 'object' property.
    672 	undoObject := streams.NewActivityStreamsObjectProperty()
    673 	undoObject.AppendActivityStreamsAnnounce(asAnnounce)
    674 	undo.SetActivityStreamsObject(undoObject)
    675 
    676 	// set the to
    677 	undo.SetActivityStreamsTo(asAnnounce.GetActivityStreamsTo())
    678 
    679 	// set the cc
    680 	undo.SetActivityStreamsCc(asAnnounce.GetActivityStreamsCc())
    681 
    682 	outboxIRI, err := url.Parse(originAccount.OutboxURI)
    683 	if err != nil {
    684 		return fmt.Errorf("federateUnannounce: error parsing outboxURI %s: %s", originAccount.OutboxURI, err)
    685 	}
    686 
    687 	_, err = p.federator.FederatingActor().Send(ctx, outboxIRI, undo)
    688 	return err
    689 }
    690 
    691 func (p *Processor) federateAcceptFollowRequest(ctx context.Context, follow *gtsmodel.Follow) error {
    692 	if follow.Account == nil {
    693 		a, err := p.state.DB.GetAccountByID(ctx, follow.AccountID)
    694 		if err != nil {
    695 			return err
    696 		}
    697 		follow.Account = a
    698 	}
    699 	originAccount := follow.Account
    700 
    701 	if follow.TargetAccount == nil {
    702 		a, err := p.state.DB.GetAccountByID(ctx, follow.TargetAccountID)
    703 		if err != nil {
    704 			return err
    705 		}
    706 		follow.TargetAccount = a
    707 	}
    708 	targetAccount := follow.TargetAccount
    709 
    710 	// Do nothing if target account *isn't* local,
    711 	// or both origin + target *are* local.
    712 	if targetAccount.IsRemote() || originAccount.IsLocal() {
    713 		return nil
    714 	}
    715 
    716 	// recreate the AS follow
    717 	asFollow, err := p.tc.FollowToAS(ctx, follow, originAccount, targetAccount)
    718 	if err != nil {
    719 		return fmt.Errorf("federateUnfollow: error converting follow to as format: %s", err)
    720 	}
    721 
    722 	acceptingAccountURI, err := url.Parse(targetAccount.URI)
    723 	if err != nil {
    724 		return fmt.Errorf("error parsing uri %s: %s", targetAccount.URI, err)
    725 	}
    726 
    727 	requestingAccountURI, err := url.Parse(originAccount.URI)
    728 	if err != nil {
    729 		return fmt.Errorf("error parsing uri %s: %s", targetAccount.URI, err)
    730 	}
    731 
    732 	// create an Accept
    733 	accept := streams.NewActivityStreamsAccept()
    734 
    735 	// set the accepting actor on it
    736 	acceptActorProp := streams.NewActivityStreamsActorProperty()
    737 	acceptActorProp.AppendIRI(acceptingAccountURI)
    738 	accept.SetActivityStreamsActor(acceptActorProp)
    739 
    740 	// Set the recreated follow as the 'object' property.
    741 	acceptObject := streams.NewActivityStreamsObjectProperty()
    742 	acceptObject.AppendActivityStreamsFollow(asFollow)
    743 	accept.SetActivityStreamsObject(acceptObject)
    744 
    745 	// Set the To of the accept as the originator of the follow
    746 	acceptTo := streams.NewActivityStreamsToProperty()
    747 	acceptTo.AppendIRI(requestingAccountURI)
    748 	accept.SetActivityStreamsTo(acceptTo)
    749 
    750 	outboxIRI, err := url.Parse(targetAccount.OutboxURI)
    751 	if err != nil {
    752 		return fmt.Errorf("federateAcceptFollowRequest: error parsing outboxURI %s: %s", originAccount.OutboxURI, err)
    753 	}
    754 
    755 	// send off the accept using the accepter's outbox
    756 	_, err = p.federator.FederatingActor().Send(ctx, outboxIRI, accept)
    757 	return err
    758 }
    759 
    760 func (p *Processor) federateRejectFollowRequest(ctx context.Context, followRequest *gtsmodel.FollowRequest) error {
    761 	if followRequest.Account == nil {
    762 		a, err := p.state.DB.GetAccountByID(ctx, followRequest.AccountID)
    763 		if err != nil {
    764 			return err
    765 		}
    766 		followRequest.Account = a
    767 	}
    768 	originAccount := followRequest.Account
    769 
    770 	if followRequest.TargetAccount == nil {
    771 		a, err := p.state.DB.GetAccountByID(ctx, followRequest.TargetAccountID)
    772 		if err != nil {
    773 			return err
    774 		}
    775 		followRequest.TargetAccount = a
    776 	}
    777 	targetAccount := followRequest.TargetAccount
    778 
    779 	// Do nothing if target account *isn't* local,
    780 	// or both origin + target *are* local.
    781 	if targetAccount.IsRemote() || originAccount.IsLocal() {
    782 		return nil
    783 	}
    784 
    785 	// recreate the AS follow
    786 	follow := p.tc.FollowRequestToFollow(ctx, followRequest)
    787 	asFollow, err := p.tc.FollowToAS(ctx, follow, originAccount, targetAccount)
    788 	if err != nil {
    789 		return fmt.Errorf("federateUnfollow: error converting follow to as format: %s", err)
    790 	}
    791 
    792 	rejectingAccountURI, err := url.Parse(targetAccount.URI)
    793 	if err != nil {
    794 		return fmt.Errorf("error parsing uri %s: %s", targetAccount.URI, err)
    795 	}
    796 
    797 	requestingAccountURI, err := url.Parse(originAccount.URI)
    798 	if err != nil {
    799 		return fmt.Errorf("error parsing uri %s: %s", targetAccount.URI, err)
    800 	}
    801 
    802 	// create a Reject
    803 	reject := streams.NewActivityStreamsReject()
    804 
    805 	// set the rejecting actor on it
    806 	acceptActorProp := streams.NewActivityStreamsActorProperty()
    807 	acceptActorProp.AppendIRI(rejectingAccountURI)
    808 	reject.SetActivityStreamsActor(acceptActorProp)
    809 
    810 	// Set the recreated follow as the 'object' property.
    811 	acceptObject := streams.NewActivityStreamsObjectProperty()
    812 	acceptObject.AppendActivityStreamsFollow(asFollow)
    813 	reject.SetActivityStreamsObject(acceptObject)
    814 
    815 	// Set the To of the reject as the originator of the follow
    816 	acceptTo := streams.NewActivityStreamsToProperty()
    817 	acceptTo.AppendIRI(requestingAccountURI)
    818 	reject.SetActivityStreamsTo(acceptTo)
    819 
    820 	outboxIRI, err := url.Parse(targetAccount.OutboxURI)
    821 	if err != nil {
    822 		return fmt.Errorf("federateRejectFollowRequest: error parsing outboxURI %s: %s", originAccount.OutboxURI, err)
    823 	}
    824 
    825 	// send off the reject using the rejecting account's outbox
    826 	_, err = p.federator.FederatingActor().Send(ctx, outboxIRI, reject)
    827 	return err
    828 }
    829 
    830 func (p *Processor) federateFave(ctx context.Context, fave *gtsmodel.StatusFave, originAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) error {
    831 	// Do nothing if both accounts are local.
    832 	if originAccount.IsLocal() && targetAccount.IsLocal() {
    833 		return nil
    834 	}
    835 
    836 	// create the AS fave
    837 	asFave, err := p.tc.FaveToAS(ctx, fave)
    838 	if err != nil {
    839 		return fmt.Errorf("federateFave: error converting fave to as format: %s", err)
    840 	}
    841 
    842 	outboxIRI, err := url.Parse(originAccount.OutboxURI)
    843 	if err != nil {
    844 		return fmt.Errorf("federateFave: error parsing outboxURI %s: %s", originAccount.OutboxURI, err)
    845 	}
    846 	_, err = p.federator.FederatingActor().Send(ctx, outboxIRI, asFave)
    847 	return err
    848 }
    849 
    850 func (p *Processor) federateAnnounce(ctx context.Context, boostWrapperStatus *gtsmodel.Status, boostingAccount *gtsmodel.Account, boostedAccount *gtsmodel.Account) error {
    851 	announce, err := p.tc.BoostToAS(ctx, boostWrapperStatus, boostingAccount, boostedAccount)
    852 	if err != nil {
    853 		return fmt.Errorf("federateAnnounce: error converting status to announce: %s", err)
    854 	}
    855 
    856 	outboxIRI, err := url.Parse(boostingAccount.OutboxURI)
    857 	if err != nil {
    858 		return fmt.Errorf("federateAnnounce: error parsing outboxURI %s: %s", boostingAccount.OutboxURI, err)
    859 	}
    860 
    861 	_, err = p.federator.FederatingActor().Send(ctx, outboxIRI, announce)
    862 	return err
    863 }
    864 
    865 func (p *Processor) federateAccountUpdate(ctx context.Context, updatedAccount *gtsmodel.Account, originAccount *gtsmodel.Account) error {
    866 	person, err := p.tc.AccountToAS(ctx, updatedAccount)
    867 	if err != nil {
    868 		return fmt.Errorf("federateAccountUpdate: error converting account to person: %s", err)
    869 	}
    870 
    871 	update, err := p.tc.WrapPersonInUpdate(person, originAccount)
    872 	if err != nil {
    873 		return fmt.Errorf("federateAccountUpdate: error wrapping person in update: %s", err)
    874 	}
    875 
    876 	outboxIRI, err := url.Parse(originAccount.OutboxURI)
    877 	if err != nil {
    878 		return fmt.Errorf("federateAnnounce: error parsing outboxURI %s: %s", originAccount.OutboxURI, err)
    879 	}
    880 
    881 	_, err = p.federator.FederatingActor().Send(ctx, outboxIRI, update)
    882 	return err
    883 }
    884 
    885 func (p *Processor) federateBlock(ctx context.Context, block *gtsmodel.Block) error {
    886 	if block.Account == nil {
    887 		blockAccount, err := p.state.DB.GetAccountByID(ctx, block.AccountID)
    888 		if err != nil {
    889 			return fmt.Errorf("federateBlock: error getting block account from database: %s", err)
    890 		}
    891 		block.Account = blockAccount
    892 	}
    893 
    894 	if block.TargetAccount == nil {
    895 		blockTargetAccount, err := p.state.DB.GetAccountByID(ctx, block.TargetAccountID)
    896 		if err != nil {
    897 			return fmt.Errorf("federateBlock: error getting block target account from database: %s", err)
    898 		}
    899 		block.TargetAccount = blockTargetAccount
    900 	}
    901 
    902 	// Do nothing if both accounts are local.
    903 	if block.Account.IsLocal() && block.TargetAccount.IsLocal() {
    904 		return nil
    905 	}
    906 
    907 	asBlock, err := p.tc.BlockToAS(ctx, block)
    908 	if err != nil {
    909 		return fmt.Errorf("federateBlock: error converting block to AS format: %s", err)
    910 	}
    911 
    912 	outboxIRI, err := url.Parse(block.Account.OutboxURI)
    913 	if err != nil {
    914 		return fmt.Errorf("federateBlock: error parsing outboxURI %s: %s", block.Account.OutboxURI, err)
    915 	}
    916 
    917 	_, err = p.federator.FederatingActor().Send(ctx, outboxIRI, asBlock)
    918 	return err
    919 }
    920 
    921 func (p *Processor) federateUnblock(ctx context.Context, block *gtsmodel.Block) error {
    922 	if block.Account == nil {
    923 		blockAccount, err := p.state.DB.GetAccountByID(ctx, block.AccountID)
    924 		if err != nil {
    925 			return fmt.Errorf("federateUnblock: error getting block account from database: %s", err)
    926 		}
    927 		block.Account = blockAccount
    928 	}
    929 
    930 	if block.TargetAccount == nil {
    931 		blockTargetAccount, err := p.state.DB.GetAccountByID(ctx, block.TargetAccountID)
    932 		if err != nil {
    933 			return fmt.Errorf("federateUnblock: error getting block target account from database: %s", err)
    934 		}
    935 		block.TargetAccount = blockTargetAccount
    936 	}
    937 
    938 	// Do nothing if both accounts are local.
    939 	if block.Account.IsLocal() && block.TargetAccount.IsLocal() {
    940 		return nil
    941 	}
    942 
    943 	asBlock, err := p.tc.BlockToAS(ctx, block)
    944 	if err != nil {
    945 		return fmt.Errorf("federateUnblock: error converting block to AS format: %s", err)
    946 	}
    947 
    948 	targetAccountURI, err := url.Parse(block.TargetAccount.URI)
    949 	if err != nil {
    950 		return fmt.Errorf("federateUnblock: error parsing uri %s: %s", block.TargetAccount.URI, err)
    951 	}
    952 
    953 	// create an Undo and set the appropriate actor on it
    954 	undo := streams.NewActivityStreamsUndo()
    955 	undo.SetActivityStreamsActor(asBlock.GetActivityStreamsActor())
    956 
    957 	// Set the block as the 'object' property.
    958 	undoObject := streams.NewActivityStreamsObjectProperty()
    959 	undoObject.AppendActivityStreamsBlock(asBlock)
    960 	undo.SetActivityStreamsObject(undoObject)
    961 
    962 	// Set the To of the undo as the target of the block
    963 	undoTo := streams.NewActivityStreamsToProperty()
    964 	undoTo.AppendIRI(targetAccountURI)
    965 	undo.SetActivityStreamsTo(undoTo)
    966 
    967 	outboxIRI, err := url.Parse(block.Account.OutboxURI)
    968 	if err != nil {
    969 		return fmt.Errorf("federateUnblock: error parsing outboxURI %s: %s", block.Account.OutboxURI, err)
    970 	}
    971 	_, err = p.federator.FederatingActor().Send(ctx, outboxIRI, undo)
    972 	return err
    973 }
    974 
    975 func (p *Processor) federateReport(ctx context.Context, report *gtsmodel.Report) error {
    976 	if report.TargetAccount == nil {
    977 		reportTargetAccount, err := p.state.DB.GetAccountByID(ctx, report.TargetAccountID)
    978 		if err != nil {
    979 			return fmt.Errorf("federateReport: error getting report target account from database: %w", err)
    980 		}
    981 		report.TargetAccount = reportTargetAccount
    982 	}
    983 
    984 	if len(report.StatusIDs) > 0 && len(report.Statuses) == 0 {
    985 		statuses, err := p.state.DB.GetStatuses(ctx, report.StatusIDs)
    986 		if err != nil {
    987 			return fmt.Errorf("federateReport: error getting report statuses from database: %w", err)
    988 		}
    989 		report.Statuses = statuses
    990 	}
    991 
    992 	flag, err := p.tc.ReportToASFlag(ctx, report)
    993 	if err != nil {
    994 		return fmt.Errorf("federateReport: error converting report to AS flag: %w", err)
    995 	}
    996 
    997 	// add bto so that our federating actor knows where to
    998 	// send the Flag; it'll still use a shared inbox if possible
    999 	reportTargetURI, err := url.Parse(report.TargetAccount.URI)
   1000 	if err != nil {
   1001 		return fmt.Errorf("federateReport: error parsing outboxURI %s: %w", report.TargetAccount.URI, err)
   1002 	}
   1003 	bTo := streams.NewActivityStreamsBtoProperty()
   1004 	bTo.AppendIRI(reportTargetURI)
   1005 	flag.SetActivityStreamsBto(bTo)
   1006 
   1007 	// deliver the flag using the outbox of the
   1008 	// instance account to anonymize the report
   1009 	instanceAccount, err := p.state.DB.GetInstanceAccount(ctx, "")
   1010 	if err != nil {
   1011 		return fmt.Errorf("federateReport: error getting instance account: %w", err)
   1012 	}
   1013 
   1014 	outboxIRI, err := url.Parse(instanceAccount.OutboxURI)
   1015 	if err != nil {
   1016 		return fmt.Errorf("federateReport: error parsing outboxURI %s: %w", instanceAccount.OutboxURI, err)
   1017 	}
   1018 
   1019 	_, err = p.federator.FederatingActor().Send(ctx, outboxIRI, flag)
   1020 	return err
   1021 }