summaryrefslogtreecommitdiff
path: root/internal/federation/federatingdb/create.go
diff options
context:
space:
mode:
authorLibravatar tobi <31960611+tsmethurst@users.noreply.github.com>2021-10-10 12:39:25 +0200
committerLibravatar GitHub <noreply@github.com>2021-10-10 12:39:25 +0200
commit367bdca25093ee76b36506d8a5e6733b0aa2e2bb (patch)
tree37b1b82ae6e9fad7e6d95b8abbb58bdb42049707 /internal/federation/federatingdb/create.go
parentDerive visibility fixes (#271) (diff)
downloadgotosocial-367bdca25093ee76b36506d8a5e6733b0aa2e2bb.tar.xz
Handle forwarded messages (#273)
* correct path of foss_satan * add APIri and notes * test create forward note * rename target => receiving account * split up create into separate funcs * update extractFromCtx * tidy up from federator processing * foss satan => http not https * check if status in db * mock dereference of status from IRI * add forward message deref test * update test with activities * add remote_account_2 to test rig
Diffstat (limited to 'internal/federation/federatingdb/create.go')
-rw-r--r--internal/federation/federatingdb/create.go337
1 files changed, 228 insertions, 109 deletions
diff --git a/internal/federation/federatingdb/create.go b/internal/federation/federatingdb/create.go
index 2a31108f2..4262dfcb4 100644
--- a/internal/federation/federatingdb/create.go
+++ b/internal/federation/federatingdb/create.go
@@ -22,11 +22,13 @@ import (
"context"
"errors"
"fmt"
+ "strings"
"github.com/go-fed/activity/streams/vocab"
"github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/ap"
"github.com/superseriousbusiness/gotosocial/internal/db"
+ "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/id"
"github.com/superseriousbusiness/gotosocial/internal/messages"
)
@@ -59,144 +61,261 @@ func (f *federatingDB) Create(ctx context.Context, asType vocab.Type) error {
l.Debug("entering Create")
}
- targetAcct, fromFederatorChan, err := extractFromCtx(ctx)
- if err != nil {
- return err
- }
- if targetAcct == nil || fromFederatorChan == nil {
- // If the target account or federator channel wasn't set on the context, that means this request didn't pass
+ receivingAccount, requestingAccount, fromFederatorChan := extractFromCtx(ctx)
+ if receivingAccount == nil || fromFederatorChan == nil {
+ // If the receiving account or federator channel wasn't set on the context, that means this request didn't pass
// through the API, but came from inside GtS as the result of another activity on this instance. That being so,
// we can safely just ignore this activity, since we know we've already processed it elsewhere.
return nil
}
switch asType.GetTypeName() {
+ case ap.ActivityBlock:
+ // BLOCK SOMETHING
+ return f.activityBlock(ctx, asType, receivingAccount, requestingAccount, fromFederatorChan)
case ap.ActivityCreate:
// CREATE SOMETHING
- create, ok := asType.(vocab.ActivityStreamsCreate)
- if !ok {
- return errors.New("CREATE: could not convert type to create")
- }
- object := create.GetActivityStreamsObject()
- for objectIter := object.Begin(); objectIter != object.End(); objectIter = objectIter.Next() {
- switch objectIter.GetType().GetTypeName() {
- case ap.ObjectNote:
- // CREATE A NOTE
- note := objectIter.GetActivityStreamsNote()
- status, err := f.typeConverter.ASStatusToStatus(ctx, note)
- if err != nil {
- return fmt.Errorf("CREATE: error converting note to status: %s", err)
- }
-
- // id the status based on the time it was created
- statusID, err := id.NewULIDFromTime(status.CreatedAt)
- if err != nil {
- return err
- }
- status.ID = statusID
-
- if err := f.db.PutStatus(ctx, status); err != nil {
- if err == db.ErrAlreadyExists {
- // the status already exists in the database, which means we've already handled everything else,
- // so we can just return nil here and be done with it.
- return nil
- }
- // an actual error has happened
- return fmt.Errorf("CREATE: database error inserting status: %s", err)
- }
-
- fromFederatorChan <- messages.FromFederator{
- APObjectType: ap.ObjectNote,
- APActivityType: ap.ActivityCreate,
- GTSModel: status,
- ReceivingAccount: targetAcct,
- }
- }
- }
+ return f.activityCreate(ctx, asType, receivingAccount, requestingAccount, fromFederatorChan)
case ap.ActivityFollow:
// FOLLOW SOMETHING
- follow, ok := asType.(vocab.ActivityStreamsFollow)
- if !ok {
- return errors.New("CREATE: could not convert type to follow")
- }
+ return f.activityFollow(ctx, asType, receivingAccount, requestingAccount, fromFederatorChan)
+ case ap.ActivityLike:
+ // LIKE SOMETHING
+ return f.activityLike(ctx, asType, receivingAccount, requestingAccount, fromFederatorChan)
+ }
+ return nil
+}
- followRequest, err := f.typeConverter.ASFollowToFollowRequest(ctx, follow)
- if err != nil {
- return fmt.Errorf("CREATE: could not convert Follow to follow request: %s", err)
- }
+/*
+ BLOCK HANDLERS
+*/
- newID, err := id.NewULID()
- if err != nil {
- return err
- }
- followRequest.ID = newID
+func (f *federatingDB) activityBlock(ctx context.Context, asType vocab.Type, receiving *gtsmodel.Account, requestingAccount *gtsmodel.Account, fromFederatorChan chan messages.FromFederator) error {
+ blockable, ok := asType.(vocab.ActivityStreamsBlock)
+ if !ok {
+ return errors.New("activityBlock: could not convert type to block")
+ }
- if err := f.db.Put(ctx, followRequest); err != nil {
- return fmt.Errorf("CREATE: database error inserting follow request: %s", err)
- }
+ block, err := f.typeConverter.ASBlockToBlock(ctx, blockable)
+ if err != nil {
+ return fmt.Errorf("activityBlock: could not convert Block to gts model block")
+ }
- fromFederatorChan <- messages.FromFederator{
- APObjectType: ap.ActivityFollow,
- APActivityType: ap.ActivityCreate,
- GTSModel: followRequest,
- ReceivingAccount: targetAcct,
- }
- case ap.ActivityLike:
- // LIKE SOMETHING
- like, ok := asType.(vocab.ActivityStreamsLike)
- if !ok {
- return errors.New("CREATE: could not convert type to like")
- }
+ newID, err := id.NewULID()
+ if err != nil {
+ return err
+ }
+ block.ID = newID
- fave, err := f.typeConverter.ASLikeToFave(ctx, like)
- if err != nil {
- return fmt.Errorf("CREATE: could not convert Like to fave: %s", err)
+ if err := f.db.Put(ctx, block); err != nil {
+ return fmt.Errorf("activityBlock: database error inserting block: %s", err)
+ }
+
+ fromFederatorChan <- messages.FromFederator{
+ APObjectType: ap.ActivityBlock,
+ APActivityType: ap.ActivityCreate,
+ GTSModel: block,
+ ReceivingAccount: receiving,
+ }
+ return nil
+}
+
+/*
+ CREATE HANDLERS
+*/
+
+func (f *federatingDB) activityCreate(ctx context.Context, asType vocab.Type, receivingAccount *gtsmodel.Account, requestingAccount *gtsmodel.Account, fromFederatorChan chan messages.FromFederator) error {
+ create, ok := asType.(vocab.ActivityStreamsCreate)
+ if !ok {
+ return errors.New("activityCreate: could not convert type to create")
+ }
+
+ // create should have an object
+ object := create.GetActivityStreamsObject()
+ if object == nil {
+ return errors.New("Create had no Object")
+ }
+
+ errs := []string{}
+ // iterate through the object(s) to see what we're meant to be creating
+ for objectIter := object.Begin(); objectIter != object.End(); objectIter = objectIter.Next() {
+ asObjectType := objectIter.GetType()
+ if asObjectType == nil {
+ // currently we can't do anything with just a Create of something that's not an Object with a type
+ // TODO: process a Create with an Object that's just a URI or something
+ errs = append(errs, "object of Create was not a Type")
+ continue
}
- newID, err := id.NewULID()
- if err != nil {
- return err
+ // we have a type -- what is it?
+ asObjectTypeName := asObjectType.GetTypeName()
+ switch asObjectTypeName {
+ case ap.ObjectNote:
+ // CREATE A NOTE
+ if err := f.createNote(ctx, objectIter.GetActivityStreamsNote(), receivingAccount, requestingAccount, fromFederatorChan); err != nil {
+ errs = append(errs, err.Error())
+ }
+ default:
+ errs = append(errs, fmt.Sprintf("received an object on a Create that we couldn't handle: %s", asObjectType.GetTypeName()))
}
- fave.ID = newID
+ }
+
+ if len(errs) != 0 {
+ return fmt.Errorf("activityCreate: one or more errors while processing activity: %s", strings.Join(errs, "; "))
+ }
- if err := f.db.Put(ctx, fave); err != nil {
- return fmt.Errorf("CREATE: database error inserting fave: %s", err)
+ return nil
+}
+
+// createNote handles a Create activity with a Note type.
+func (f *federatingDB) createNote(ctx context.Context, note vocab.ActivityStreamsNote, receivingAccount *gtsmodel.Account, requestingAccount *gtsmodel.Account, fromFederatorChan chan messages.FromFederator) error {
+ l := f.log.WithFields(logrus.Fields{
+ "func": "createNote",
+ "receivingAccount": receivingAccount.URI,
+ "requestingAccount": requestingAccount.URI,
+ })
+
+ // Check if we have a forward.
+ // In other words, was the note posted to our inbox by at least one actor who actually created the note, or are they just forwarding it?
+ forward := true
+
+ // note should have an attributedTo
+ noteAttributedTo := note.GetActivityStreamsAttributedTo()
+ if noteAttributedTo == nil {
+ return errors.New("createNote: note had no attributedTo")
+ }
+
+ // compare the attributedTo(s) with the actor who posted this to our inbox
+ for attributedToIter := noteAttributedTo.Begin(); attributedToIter != noteAttributedTo.End(); attributedToIter = attributedToIter.Next() {
+ if !attributedToIter.IsIRI() {
+ continue
+ }
+ iri := attributedToIter.GetIRI()
+ if requestingAccount.URI == iri.String() {
+ // at least one creator of the note, and the actor who posted the note to our inbox, are the same, so it's not a forward
+ forward = false
}
+ }
+ // If we do have a forward, we should ignore the content for now and just dereference based on the URL/ID of the note instead, to get the note straight from the horse's mouth
+ if forward {
+ l.Trace("note is a forward")
+ id := note.GetJSONLDId()
+ if !id.IsIRI() {
+ // if the note id isn't an IRI, there's nothing we can do here
+ return nil
+ }
+ // pass the note iri into the processor and have it do the dereferencing instead of doing it here
fromFederatorChan <- messages.FromFederator{
- APObjectType: ap.ActivityLike,
+ APObjectType: ap.ObjectNote,
APActivityType: ap.ActivityCreate,
- GTSModel: fave,
- ReceivingAccount: targetAcct,
- }
- case ap.ActivityBlock:
- // BLOCK SOMETHING
- blockable, ok := asType.(vocab.ActivityStreamsBlock)
- if !ok {
- return errors.New("CREATE: could not convert type to block")
+ APIri: id.GetIRI(),
+ GTSModel: nil,
+ ReceivingAccount: receivingAccount,
}
+ return nil
+ }
- block, err := f.typeConverter.ASBlockToBlock(ctx, blockable)
- if err != nil {
- return fmt.Errorf("CREATE: could not convert Block to gts model block")
- }
+ // if we reach this point, we know it's not a forwarded status, so proceed with processing it as normal
- newID, err := id.NewULID()
- if err != nil {
- return err
- }
- block.ID = newID
+ status, err := f.typeConverter.ASStatusToStatus(ctx, note)
+ if err != nil {
+ return fmt.Errorf("createNote: error converting note to status: %s", err)
+ }
- if err := f.db.Put(ctx, block); err != nil {
- return fmt.Errorf("CREATE: database error inserting block: %s", err)
- }
+ // id the status based on the time it was created
+ statusID, err := id.NewULIDFromTime(status.CreatedAt)
+ if err != nil {
+ return err
+ }
+ status.ID = statusID
- fromFederatorChan <- messages.FromFederator{
- APObjectType: ap.ActivityBlock,
- APActivityType: ap.ActivityCreate,
- GTSModel: block,
- ReceivingAccount: targetAcct,
+ if err := f.db.PutStatus(ctx, status); err != nil {
+ if err == db.ErrAlreadyExists {
+ // the status already exists in the database, which means we've already handled everything else,
+ // so we can just return nil here and be done with it.
+ return nil
}
+ // an actual error has happened
+ return fmt.Errorf("createNote: database error inserting status: %s", err)
}
+
+ fromFederatorChan <- messages.FromFederator{
+ APObjectType: ap.ObjectNote,
+ APActivityType: ap.ActivityCreate,
+ GTSModel: status,
+ ReceivingAccount: receivingAccount,
+ }
+
+ return nil
+}
+
+/*
+ FOLLOW HANDLERS
+*/
+
+func (f *federatingDB) activityFollow(ctx context.Context, asType vocab.Type, receivingAccount *gtsmodel.Account, requestingAccount *gtsmodel.Account, fromFederatorChan chan messages.FromFederator) error {
+ follow, ok := asType.(vocab.ActivityStreamsFollow)
+ if !ok {
+ return errors.New("activityFollow: could not convert type to follow")
+ }
+
+ followRequest, err := f.typeConverter.ASFollowToFollowRequest(ctx, follow)
+ if err != nil {
+ return fmt.Errorf("activityFollow: could not convert Follow to follow request: %s", err)
+ }
+
+ newID, err := id.NewULID()
+ if err != nil {
+ return err
+ }
+ followRequest.ID = newID
+
+ if err := f.db.Put(ctx, followRequest); err != nil {
+ return fmt.Errorf("activityFollow: database error inserting follow request: %s", err)
+ }
+
+ fromFederatorChan <- messages.FromFederator{
+ APObjectType: ap.ActivityFollow,
+ APActivityType: ap.ActivityCreate,
+ GTSModel: followRequest,
+ ReceivingAccount: receivingAccount,
+ }
+
+ return nil
+}
+
+/*
+ LIKE HANDLERS
+*/
+
+func (f *federatingDB) activityLike(ctx context.Context, asType vocab.Type, receivingAccount *gtsmodel.Account, requestingAccount *gtsmodel.Account, fromFederatorChan chan messages.FromFederator) error {
+ like, ok := asType.(vocab.ActivityStreamsLike)
+ if !ok {
+ return errors.New("activityLike: could not convert type to like")
+ }
+
+ fave, err := f.typeConverter.ASLikeToFave(ctx, like)
+ if err != nil {
+ return fmt.Errorf("activityLike: could not convert Like to fave: %s", err)
+ }
+
+ newID, err := id.NewULID()
+ if err != nil {
+ return err
+ }
+ fave.ID = newID
+
+ if err := f.db.Put(ctx, fave); err != nil {
+ return fmt.Errorf("activityLike: database error inserting fave: %s", err)
+ }
+
+ fromFederatorChan <- messages.FromFederator{
+ APObjectType: ap.ActivityLike,
+ APActivityType: ap.ActivityCreate,
+ GTSModel: fave,
+ ReceivingAccount: receivingAccount,
+ }
+
return nil
}