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 }