summaryrefslogtreecommitdiff
path: root/internal/federation/dereferencing/collectionpage.go
blob: dc5c6827360489ddd05606702fa6edfdc0903e46 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package dereferencing

import (
	"context"
	"encoding/json"
	"net/url"

	"github.com/superseriousbusiness/activity/streams"
	"github.com/superseriousbusiness/activity/streams/vocab"
	"github.com/superseriousbusiness/gotosocial/internal/ap"
	"github.com/superseriousbusiness/gotosocial/internal/gtserror"
	"github.com/superseriousbusiness/gotosocial/internal/log"
)

// dereferenceCollectionPage returns the activitystreams CollectionPage at the specified IRI, or an error if something goes wrong.
func (d *Dereferencer) dereferenceCollectionPage(ctx context.Context, username string, pageIRI *url.URL) (ap.CollectionPageIterator, error) {
	if blocked, err := d.state.DB.IsDomainBlocked(ctx, pageIRI.Host); blocked || err != nil {
		return nil, gtserror.Newf("domain %s is blocked", pageIRI.Host)
	}

	transport, err := d.transportController.NewTransportForUsername(ctx, username)
	if err != nil {
		return nil, gtserror.Newf("error creating transport: %w", err)
	}

	b, err := transport.Dereference(ctx, pageIRI)
	if err != nil {
		return nil, gtserror.Newf("error deferencing %s: %w", pageIRI.String(), err)
	}

	m := make(map[string]interface{})
	if err := json.Unmarshal(b, &m); err != nil {
		return nil, gtserror.Newf("error unmarshalling bytes into json: %w", err)
	}

	t, err := streams.ToType(ctx, m)
	if err != nil {
		return nil, gtserror.Newf("error resolving json into ap vocab type: %w", err)
	}

	page, err := ap.ToCollectionPageIterator(t)
	if err != nil {
		return nil, gtserror.Newf("error resolving vocab type as page: %w", err)
	}

	return page, nil
}

// getAttachedStatusCollection is a small utility function to fetch the first page of an
// attached activity streams collection from a provided statusable object, along with a URI.
func getAttachedStatusCollectionPage(status ap.Statusable) (ap.CollectionPageIterator, string) { //nolint:gocritic
	// Look for an attached status replies (as collection)
	replies := status.GetActivityStreamsReplies()
	if replies == nil {
		return nil, ""
	}

	// Look for an attached collection page, wrap and return.
	if page := getRepliesCollectionPage(replies); page != nil {
		return ap.WrapCollectionPage(page), getIDString(page)
	}

	// Look for an attached ordered collection page, wrap and return.
	if page := getRepliesOrderedCollectionPage(replies); page != nil {
		return ap.WrapOrderedCollectionPage(page), getIDString(page)
	}

	log.Warnf(nil, "replies without collection page: %s", getIDString(status))
	return nil, ""
}

func getRepliesCollectionPage(replies vocab.ActivityStreamsRepliesProperty) vocab.ActivityStreamsCollectionPage {
	// Get the status replies collection
	collection := replies.GetActivityStreamsCollection()
	if collection == nil {
		return nil
	}

	// Get the "first" property of the replies collection
	first := collection.GetActivityStreamsFirst()
	if first == nil {
		return nil
	}

	// Return the first activity stream collection page
	return first.GetActivityStreamsCollectionPage()
}

func getRepliesOrderedCollectionPage(replies vocab.ActivityStreamsRepliesProperty) vocab.ActivityStreamsOrderedCollectionPage {
	// Get the status replies collection
	collection := replies.GetActivityStreamsOrderedCollection()
	if collection == nil {
		return nil
	}

	// Get the "first" property of the replies collection
	first := collection.GetActivityStreamsFirst()
	if first == nil {
		return nil
	}

	// Return the first activity stream collection page
	return first.GetActivityStreamsOrderedCollectionPage()
}

// getIDString is shorthand to fetch an ID URI string from AP type with attached JSONLDId.
func getIDString(a ap.WithJSONLDId) string {
	id := a.GetJSONLDId()
	if id == nil {
		return ""
	}
	uri := id.Get()
	if uri == nil {
		return ""
	}
	return uri.String()
}