Support Inbox Forwarding.
Client libraries can also filter the collections being targeted for the inbox forwarding.
このコミットが含まれているのは:
コミット
7b63dca953
65
pub/fed.go
65
pub/fed.go
|
@ -49,7 +49,7 @@ type Pubber interface {
|
|||
GetOutbox(c context.Context, w http.ResponseWriter, r *http.Request) (bool, error)
|
||||
}
|
||||
|
||||
// NewPubber provides a Pubber that implements only the Social API in
|
||||
// NewSocialPubber provides a Pubber that implements only the Social API in
|
||||
// ActivityPub.
|
||||
func NewSocialPubber(clock Clock, app Application, socialApp SocialApp, cb Callbacker) Pubber {
|
||||
return &federator{
|
||||
|
@ -61,38 +61,40 @@ func NewSocialPubber(clock Clock, app Application, socialApp SocialApp, cb Callb
|
|||
}
|
||||
}
|
||||
|
||||
// NewPubber provides a Pubber that implements only the Federating API in
|
||||
// ActivityPub.
|
||||
func NewFederatingPubber(clock Clock, app Application, fApp FederateApp, cb Callbacker, d Deliverer, client HttpClient, userAgent string, maxDeliveryDepth int) Pubber {
|
||||
// NewFederatingPubber provides a Pubber that implements only the Federating API
|
||||
// in ActivityPub.
|
||||
func NewFederatingPubber(clock Clock, app Application, fApp FederateApp, cb Callbacker, d Deliverer, client HttpClient, userAgent string, maxDeliveryDepth, maxForwardingDepth int) Pubber {
|
||||
return &federator{
|
||||
Clock: clock,
|
||||
App: app,
|
||||
FederateApp: fApp,
|
||||
ServerCallbacker: cb,
|
||||
Client: client,
|
||||
Agent: userAgent,
|
||||
MaxDeliveryDepth: maxDeliveryDepth,
|
||||
EnableServer: true,
|
||||
deliverer: d,
|
||||
Clock: clock,
|
||||
App: app,
|
||||
FederateApp: fApp,
|
||||
ServerCallbacker: cb,
|
||||
Client: client,
|
||||
Agent: userAgent,
|
||||
MaxDeliveryDepth: maxDeliveryDepth,
|
||||
MaxInboxForwardingDepth: maxForwardingDepth,
|
||||
EnableServer: true,
|
||||
deliverer: d,
|
||||
}
|
||||
}
|
||||
|
||||
// NewPubber provides a Pubber that implements both the Social API and the
|
||||
// Federating API in ActivityPub.
|
||||
func NewPubber(clock Clock, app Application, sApp SocialApp, fApp FederateApp, client, server Callbacker, d Deliverer, httpClient HttpClient, userAgent string, maxDeliveryDepth int) Pubber {
|
||||
func NewPubber(clock Clock, app Application, sApp SocialApp, fApp FederateApp, client, server Callbacker, d Deliverer, httpClient HttpClient, userAgent string, maxDeliveryDepth, maxForwardingDepth int) Pubber {
|
||||
return &federator{
|
||||
Clock: clock,
|
||||
App: app,
|
||||
SocialApp: sApp,
|
||||
FederateApp: fApp,
|
||||
Client: httpClient,
|
||||
Agent: userAgent,
|
||||
MaxDeliveryDepth: maxDeliveryDepth,
|
||||
ServerCallbacker: server,
|
||||
ClientCallbacker: client,
|
||||
EnableClient: true,
|
||||
EnableServer: true,
|
||||
deliverer: d,
|
||||
Clock: clock,
|
||||
App: app,
|
||||
SocialApp: sApp,
|
||||
FederateApp: fApp,
|
||||
Client: httpClient,
|
||||
Agent: userAgent,
|
||||
MaxDeliveryDepth: maxDeliveryDepth,
|
||||
MaxInboxForwardingDepth: maxForwardingDepth,
|
||||
ServerCallbacker: server,
|
||||
ClientCallbacker: client,
|
||||
EnableClient: true,
|
||||
EnableServer: true,
|
||||
deliverer: d,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -141,6 +143,13 @@ type federator struct {
|
|||
//
|
||||
// It is only required if EnableServer is true.
|
||||
MaxDeliveryDepth int
|
||||
// MaxInboxForwardingDepth is how deep the values are examined for
|
||||
// determining ownership of whether to forward an Activity to
|
||||
// collections or followers. Once this maximum is exceeded, the ghost
|
||||
// replies issue may become a problem, but users may not mind.
|
||||
//
|
||||
// It is only required if EnableServer is true.
|
||||
MaxInboxForwardingDepth int
|
||||
// deliverer handles deliveries to other federated servers.
|
||||
//
|
||||
// It is only required if EnableServer is true.
|
||||
|
@ -192,7 +201,9 @@ func (f *federator) PostInbox(c context.Context, w http.ResponseWriter, r *http.
|
|||
if err := f.addToInbox(c, r, m); err != nil {
|
||||
return true, err
|
||||
}
|
||||
// TODO: 7.1.2 Inbox forwarding
|
||||
if err := f.inboxForwarding(c, m); err != nil {
|
||||
return true, err
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return true, nil
|
||||
}
|
||||
|
|
|
@ -442,6 +442,7 @@ type MockApplication struct {
|
|||
t *testing.T
|
||||
owns func(c context.Context, id url.URL) bool
|
||||
get func(c context.Context, id url.URL) (PubObject, error)
|
||||
has func(c context.Context, id url.URL) (bool, error)
|
||||
set func(c context.Context, o PubObject) error
|
||||
getInbox func(c context.Context, r *http.Request) (vocab.OrderedCollectionType, error)
|
||||
getOutbox func(c context.Context, r *http.Request) (vocab.OrderedCollectionType, error)
|
||||
|
@ -462,6 +463,13 @@ func (m *MockApplication) Get(c context.Context, id url.URL) (PubObject, error)
|
|||
return m.get(c, id)
|
||||
}
|
||||
|
||||
func (m *MockApplication) Has(c context.Context, id url.URL) (bool, error) {
|
||||
if m.has == nil {
|
||||
m.t.Fatal("unexpected call to MockApplication Has")
|
||||
}
|
||||
return m.has(c, id)
|
||||
}
|
||||
|
||||
func (m *MockApplication) Set(c context.Context, o PubObject) error {
|
||||
if m.set == nil {
|
||||
m.t.Fatal("unexpected call to MockApplication Set")
|
||||
|
@ -647,12 +655,13 @@ func (m *MockCallbacker) Reject(c context.Context, s *streams.Reject) error {
|
|||
var _ FederateApp = &MockFederateApp{}
|
||||
|
||||
type MockFederateApp struct {
|
||||
t *testing.T
|
||||
canAdd func(c context.Context, obj vocab.ObjectType, target vocab.ObjectType) bool
|
||||
canRemove func(c context.Context, obj vocab.ObjectType, target vocab.ObjectType) bool
|
||||
onFollow func(c context.Context, s *streams.Follow) FollowResponse
|
||||
unblocked func(c context.Context, actorIRIs []url.URL) error
|
||||
getFollowing func(c context.Context, actor url.URL) (vocab.CollectionType, error)
|
||||
t *testing.T
|
||||
canAdd func(c context.Context, obj vocab.ObjectType, target vocab.ObjectType) bool
|
||||
canRemove func(c context.Context, obj vocab.ObjectType, target vocab.ObjectType) bool
|
||||
onFollow func(c context.Context, s *streams.Follow) FollowResponse
|
||||
unblocked func(c context.Context, actorIRIs []url.URL) error
|
||||
getFollowing func(c context.Context, actor url.URL) (vocab.CollectionType, error)
|
||||
filterForwarding func(c context.Context, activity vocab.ActivityType, iris []url.URL) ([]url.URL, error)
|
||||
}
|
||||
|
||||
func (m *MockFederateApp) CanAdd(c context.Context, obj vocab.ObjectType, target vocab.ObjectType) bool {
|
||||
|
@ -690,6 +699,13 @@ func (m *MockFederateApp) GetFollowing(c context.Context, actor url.URL) (vocab.
|
|||
return m.getFollowing(c, actor)
|
||||
}
|
||||
|
||||
func (m *MockFederateApp) FilterForwarding(c context.Context, activity vocab.ActivityType, iris []url.URL) ([]url.URL, error) {
|
||||
if m.filterForwarding == nil {
|
||||
m.t.Fatal("unexpected call to MockFederateApp FilterForwarding")
|
||||
}
|
||||
return m.filterForwarding(c, activity, iris)
|
||||
}
|
||||
|
||||
var _ Deliverer = &MockDeliverer{}
|
||||
|
||||
type MockDeliverer struct {
|
||||
|
@ -734,7 +750,7 @@ func NewFederatingPubberTest(t *testing.T) (app *MockApplication, fedApp *MockFe
|
|||
cb = &MockCallbacker{t: t}
|
||||
d = &MockDeliverer{t: t}
|
||||
h = &MockHttpClient{t: t}
|
||||
p = NewFederatingPubber(clock, app, fedApp, cb, d, h, testAgent, 1)
|
||||
p = NewFederatingPubber(clock, app, fedApp, cb, d, h, testAgent, 1, 1)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -747,7 +763,7 @@ func NewPubberTest(t *testing.T) (app *MockApplication, socialApp *MockSocialApp
|
|||
fedCb = &MockCallbacker{t: t}
|
||||
d = &MockDeliverer{t: t}
|
||||
h = &MockHttpClient{t: t}
|
||||
p = NewPubber(clock, app, socialApp, fedApp, socialCb, fedCb, d, h, testAgent, 1)
|
||||
p = NewPubber(clock, app, socialApp, fedApp, socialCb, fedCb, d, h, testAgent, 1, 1)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -763,6 +779,9 @@ func PreparePostInboxTest(t *testing.T, app *MockApplication, socialApp *MockSoc
|
|||
app.set = func(c context.Context, o PubObject) error {
|
||||
return nil
|
||||
}
|
||||
app.has = func(c context.Context, id url.URL) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -981,6 +1000,26 @@ func TestFederatingPubber_PostInbox(t *testing.T) {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
gotHas := 0
|
||||
var hasIriActivity url.URL
|
||||
var hasIriTo url.URL
|
||||
app.has = func(c context.Context, id url.URL) (bool, error) {
|
||||
gotHas++
|
||||
if gotHas == 1 {
|
||||
hasIriActivity = id
|
||||
return false, nil
|
||||
} else {
|
||||
hasIriTo = id
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
gotGet := 0
|
||||
var gotIri url.URL
|
||||
app.get = func(c context.Context, iri url.URL) (PubObject, error) {
|
||||
gotGet++
|
||||
gotIri = iri
|
||||
return samActor, nil
|
||||
}
|
||||
gotCreate := 0
|
||||
var gotCreateCallback *streams.Create
|
||||
cb.create = func(c context.Context, s *streams.Create) error {
|
||||
|
@ -1005,6 +1044,16 @@ func TestFederatingPubber_PostInbox(t *testing.T) {
|
|||
t.Fatalf("expected %s, got %s", "OrderedCollection", l)
|
||||
} else if l := setObject.GetType(0).(string); l != "Note" {
|
||||
t.Fatalf("expected %s, got %s", "Note", l)
|
||||
} else if gotHas != 2 {
|
||||
t.Fatalf("expected %d, got %d", 2, gotHas)
|
||||
} else if hasIriActivityString := (&hasIriActivity).String(); hasIriActivityString != noteActivityURIString {
|
||||
t.Fatalf("expected %s, got %s", noteActivityURIString, hasIriActivityString)
|
||||
} else if hasIriToString := (&hasIriTo).String(); hasIriToString != samIRIString {
|
||||
t.Fatalf("expected %s, got %s", samIRIString, hasIriToString)
|
||||
} else if gotGet != 1 {
|
||||
t.Fatalf("expected %d, got %d", 1, gotGet)
|
||||
} else if gotIriString := (&gotIri).String(); gotIriString != samIRIString {
|
||||
t.Fatalf("expected %s, got %s", samIRIString, gotIriString)
|
||||
} else if gotCreate != 1 {
|
||||
t.Fatalf("expected %d, got %d", 1, gotCreate)
|
||||
} else if s := gotCreateCallback.Raw().GetActorObject(0).GetId(); s.String() != sallyIRIString {
|
||||
|
@ -1112,6 +1161,26 @@ func TestPubber_PostInbox(t *testing.T) {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
gotHas := 0
|
||||
var hasIriActivity url.URL
|
||||
var hasIriTo url.URL
|
||||
app.has = func(c context.Context, id url.URL) (bool, error) {
|
||||
gotHas++
|
||||
if gotHas == 1 {
|
||||
hasIriActivity = id
|
||||
return false, nil
|
||||
} else {
|
||||
hasIriTo = id
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
gotGet := 0
|
||||
var gotIri url.URL
|
||||
app.get = func(c context.Context, iri url.URL) (PubObject, error) {
|
||||
gotGet++
|
||||
gotIri = iri
|
||||
return samActor, nil
|
||||
}
|
||||
gotCreate := 0
|
||||
var gotCreateCallback *streams.Create
|
||||
fedCb.create = func(c context.Context, s *streams.Create) error {
|
||||
|
@ -1136,6 +1205,16 @@ func TestPubber_PostInbox(t *testing.T) {
|
|||
t.Fatalf("expected %s, got %s", "OrderedCollection", l)
|
||||
} else if l := setObject.GetType(0).(string); l != "Note" {
|
||||
t.Fatalf("expected %s, got %s", "Note", l)
|
||||
} else if gotHas != 2 {
|
||||
t.Fatalf("expected %d, got %d", 2, gotHas)
|
||||
} else if hasIriActivityString := (&hasIriActivity).String(); hasIriActivityString != noteActivityURIString {
|
||||
t.Fatalf("expected %s, got %s", noteActivityURIString, hasIriActivityString)
|
||||
} else if hasIriToString := (&hasIriTo).String(); hasIriToString != samIRIString {
|
||||
t.Fatalf("expected %s, got %s", samIRIString, hasIriToString)
|
||||
} else if gotGet != 1 {
|
||||
t.Fatalf("expected %d, got %d", 1, gotGet)
|
||||
} else if gotIriString := (&gotIri).String(); gotIriString != samIRIString {
|
||||
t.Fatalf("expected %s, got %s", samIRIString, gotIriString)
|
||||
} else if gotCreate != 1 {
|
||||
t.Fatalf("expected %d, got %d", 1, gotCreate)
|
||||
} else if s := gotCreateCallback.Raw().GetActorObject(0).GetId(); s.String() != sallyIRIString {
|
||||
|
|
|
@ -41,6 +41,9 @@ type Application interface {
|
|||
Owns(c context.Context, id url.URL) bool
|
||||
// Get fetches the ActivityStream representation of the given id.
|
||||
Get(c context.Context, id url.URL) (PubObject, error)
|
||||
// Has determines if the server already knows about the object or
|
||||
// Activity specified by the given id.
|
||||
Has(c context.Context, id url.URL) (bool, error)
|
||||
// Set should write or overwrite the value of the provided object for
|
||||
// its 'id'.
|
||||
Set(c context.Context, o PubObject) error
|
||||
|
@ -96,6 +99,17 @@ type FederateApp interface {
|
|||
// If nil error is returned, then the received activity is processed as
|
||||
// a normal unblocked interaction.
|
||||
Unblocked(c context.Context, actorIRIs []url.URL) error
|
||||
// FilterForwarding is invoked when a received activity needs to be
|
||||
// forwarded to specific inboxes owned by this server in order to avoid
|
||||
// the ghost reply problem. The IRIs provided are collections owned by
|
||||
// this server that the federate peer requested inbox forwarding to.
|
||||
//
|
||||
// Implementors must apply some sort of filtering to the provided IRI
|
||||
// collections. Without any filtering, the resulting application is
|
||||
// vulnerable to becoming a spam bot for a malicious federate peer.
|
||||
// Typical implementations will filter the iris down to be only the
|
||||
// follower collections owned by the actors targeted in the activity.
|
||||
FilterForwarding(c context.Context, activity vocab.ActivityType, iris []url.URL) ([]url.URL, error)
|
||||
}
|
||||
|
||||
// FollowResponse instructs how to proceed upon immediately receiving a request
|
||||
|
@ -113,6 +127,9 @@ const (
|
|||
// interactions. These callbacks are called after their spec-compliant actions
|
||||
// are completed, but before inbox forwarding and before delivery.
|
||||
//
|
||||
// Note that at minimum, for inbox forwarding to work correctly, these
|
||||
// Activities must be stored in the client application as a system of record.
|
||||
//
|
||||
// Note that modifying the ActivityStream objects in a callback may cause
|
||||
// unintentionally non-standard behavior if modifying core attributes, but
|
||||
// otherwise affords clients powerful flexibility. Use responsibly.
|
||||
|
|
212
pub/internal.go
212
pub/internal.go
|
@ -483,6 +483,12 @@ func (f *federator) deliver(obj vocab.ActivityType) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return f.deliverToRecipients(obj, recipients)
|
||||
}
|
||||
|
||||
// deliverToRecipients will take a prepared Activity and send it to specific
|
||||
// recipients without examining the activity.
|
||||
func (f *federator) deliverToRecipients(obj vocab.ActivityType, recipients []url.URL) error {
|
||||
m, err := obj.Serialize()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -1335,6 +1341,212 @@ func (f *federator) addToInbox(c context.Context, r *http.Request, m map[string]
|
|||
return f.App.Set(c, inbox)
|
||||
}
|
||||
|
||||
// Note: This is a mechanism for causing other victim servers to DDOS
|
||||
// or forward spam on a malicious user's behalf. The trick is a simple
|
||||
// one: Reply to a user, and CC a ton of 'follower' collections owned
|
||||
// by the victim server. Bonus points for listing more 'follower'
|
||||
// collections from other popular instances as well. Leveraging the
|
||||
// Inbox Forwarding mechanism, a storm of messages will ensue.
|
||||
//
|
||||
// I don't want users of this library to be vulnerable to this kind of
|
||||
// spam/DDOS storm. So here we allow the client application to filter
|
||||
// out recipient collections.
|
||||
func (f *federator) inboxForwarding(c context.Context, m map[string]interface{}) error {
|
||||
a, err := toAnyActivity(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 1. Must be first time we have seen this Activity.
|
||||
if ok, err := f.App.Has(c, a.GetId()); err != nil {
|
||||
return err
|
||||
} else if ok {
|
||||
return nil
|
||||
}
|
||||
// 2. The values of 'to', 'cc', or 'audience' are Collections owned by
|
||||
// this server.
|
||||
var r []url.URL
|
||||
r = append(r, getToIRIs(a)...)
|
||||
r = append(r, getCcIRIs(a)...)
|
||||
r = append(r, getAudienceIRIs(a)...)
|
||||
var myIRIs []url.URL
|
||||
col := make(map[string]vocab.CollectionType, 0)
|
||||
oCol := make(map[string]vocab.OrderedCollectionType, 0)
|
||||
for _, iri := range r {
|
||||
if ok, err := f.App.Has(c, iri); err != nil {
|
||||
return err
|
||||
} else if !ok {
|
||||
continue
|
||||
}
|
||||
obj, err := f.App.Get(c, iri)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c, ok := obj.(vocab.CollectionType); ok {
|
||||
col[(&iri).String()] = c
|
||||
myIRIs = append(myIRIs, iri)
|
||||
} else if oc, ok := obj.(vocab.OrderedCollectionType); ok {
|
||||
oCol[(&iri).String()] = oc
|
||||
myIRIs = append(myIRIs, iri)
|
||||
}
|
||||
}
|
||||
if len(myIRIs) == 0 {
|
||||
return nil
|
||||
}
|
||||
// 3. The values of 'inReplyTo', 'object', 'target', or 'tag' are owned
|
||||
// by this server.
|
||||
ownsValue := false
|
||||
objs, l, iris := getInboxForwardingValues(a)
|
||||
for _, obj := range objs {
|
||||
if f.hasInboxForwardingValues(c, 0, f.MaxInboxForwardingDepth, obj) {
|
||||
ownsValue = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !ownsValue && f.ownsAnyLinks(c, l) {
|
||||
ownsValue = true
|
||||
}
|
||||
if !ownsValue && f.ownsAnyIRIs(c, iris) {
|
||||
ownsValue = true
|
||||
}
|
||||
if !ownsValue {
|
||||
return nil
|
||||
}
|
||||
// Do the inbox forwarding since the above conditions hold true. Support
|
||||
// the behavior of letting the application filter out the resulting
|
||||
// collections to be targeted.
|
||||
toSend, err := f.FederateApp.FilterForwarding(c, a, myIRIs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
recipients := make([]url.URL, 0, len(toSend))
|
||||
for _, iri := range toSend {
|
||||
if c, ok := col[(&iri).String()]; ok {
|
||||
for i := 0; i < c.ItemsLen(); i++ {
|
||||
if c.IsItemsObject(i) {
|
||||
obj := c.GetItemsObject(i)
|
||||
if obj.HasId() {
|
||||
recipients = append(recipients, obj.GetId())
|
||||
}
|
||||
} else if c.IsItemsLink(i) {
|
||||
l := c.GetItemsLink(i)
|
||||
if l.HasHref() {
|
||||
recipients = append(recipients, l.GetHref())
|
||||
}
|
||||
} else if c.IsItemsIRI(i) {
|
||||
recipients = append(recipients, c.GetItemsIRI(i))
|
||||
}
|
||||
}
|
||||
} else if oc, ok := oCol[(&iri).String()]; ok {
|
||||
for i := 0; i < oc.OrderedItemsLen(); i++ {
|
||||
if oc.IsOrderedItemsObject(i) {
|
||||
obj := oc.GetOrderedItemsObject(i)
|
||||
if obj.HasId() {
|
||||
recipients = append(recipients, obj.GetId())
|
||||
}
|
||||
} else if oc.IsOrderedItemsLink(i) {
|
||||
l := oc.GetItemsLink(i)
|
||||
if l.HasHref() {
|
||||
recipients = append(recipients, l.GetHref())
|
||||
}
|
||||
} else if oc.IsOrderedItemsIRI(i) {
|
||||
recipients = append(recipients, oc.GetOrderedItemsIRI(i))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return f.deliverToRecipients(a, recipients)
|
||||
}
|
||||
|
||||
// Given an 'inReplyTo', 'object', 'target', or 'tag' object, recursively
|
||||
// examines those same values to determine if the app owns any, up to a maximum
|
||||
// depth.
|
||||
func (f *federator) hasInboxForwardingValues(c context.Context, depth, maxDepth int, o vocab.ObjectType) bool {
|
||||
if depth == maxDepth {
|
||||
return false
|
||||
}
|
||||
if f.App.Owns(c, o.GetId()) {
|
||||
return true
|
||||
}
|
||||
objs, l, iris := getInboxForwardingValues(o)
|
||||
for _, obj := range objs {
|
||||
if f.hasInboxForwardingValues(c, depth+1, maxDepth, obj) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if f.ownsAnyLinks(c, l) {
|
||||
return true
|
||||
}
|
||||
return f.ownsAnyIRIs(c, iris)
|
||||
}
|
||||
|
||||
func (f *federator) ownsAnyIRIs(c context.Context, iris []url.URL) bool {
|
||||
for _, iri := range iris {
|
||||
if f.App.Owns(c, iri) {
|
||||
return true
|
||||
}
|
||||
// TODO: Dereference the IRI
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (f *federator) ownsAnyLinks(c context.Context, links []vocab.LinkType) bool {
|
||||
for _, link := range links {
|
||||
if !link.HasHref() {
|
||||
continue
|
||||
}
|
||||
href := link.GetHref()
|
||||
if f.App.Owns(c, href) {
|
||||
return true
|
||||
}
|
||||
// TODO: Dereference the IRI
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getInboxForwardingValues(o vocab.ObjectType) (objs []vocab.ObjectType, l []vocab.LinkType, iri []url.URL) {
|
||||
// 'inReplyTo'
|
||||
for i := 0; i < o.InReplyToLen(); i++ {
|
||||
if o.IsInReplyToObject(i) {
|
||||
objs = append(objs, o.GetInReplyToObject(i))
|
||||
} else if o.IsInReplyToLink(i) {
|
||||
l = append(l, o.GetInReplyToLink(i))
|
||||
} else if o.IsInReplyToIRI(i) {
|
||||
iri = append(iri, o.GetInReplyToIRI(i))
|
||||
}
|
||||
}
|
||||
// 'tag'
|
||||
for i := 0; i < o.TagLen(); i++ {
|
||||
if o.IsTagObject(i) {
|
||||
objs = append(objs, o.GetTagObject(i))
|
||||
} else if o.IsTagLink(i) {
|
||||
l = append(l, o.GetTagLink(i))
|
||||
} else if o.IsTagIRI(i) {
|
||||
iri = append(iri, o.GetTagIRI(i))
|
||||
}
|
||||
}
|
||||
if a, ok := o.(vocab.ActivityType); ok {
|
||||
// 'object'
|
||||
for i := 0; i < a.ObjectLen(); i++ {
|
||||
if a.IsObject(i) {
|
||||
objs = append(objs, a.GetObject(i))
|
||||
} else if a.IsObjectIRI(i) {
|
||||
iri = append(iri, a.GetObjectIRI(i))
|
||||
}
|
||||
}
|
||||
// 'target'
|
||||
for i := 0; i < a.TargetLen(); i++ {
|
||||
if a.IsTargetObject(i) {
|
||||
objs = append(objs, a.GetTargetObject(i))
|
||||
} else if a.IsTargetLink(i) {
|
||||
l = append(l, a.GetTargetLink(i))
|
||||
} else if a.IsTargetIRI(i) {
|
||||
iri = append(iri, a.GetTargetIRI(i))
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Fetches an "object" on a raw JSON map of an Activity with the matching 'id'
|
||||
// field. If there is no object matching the IRI, or the object just is an IRI,
|
||||
// or the object wth the matching id is not in the array of objects, then a nil
|
||||
|
|
読み込み中…
新しいイシューから参照