Blame


1 1d5ddf5d 2024-02-20 o package apub
2 1d5ddf5d 2024-02-20 o
3 1d5ddf5d 2024-02-20 o import (
4 1d5ddf5d 2024-02-20 o "bytes"
5 1d5ddf5d 2024-02-20 o "crypto/rsa"
6 1d5ddf5d 2024-02-20 o "encoding/json"
7 1d5ddf5d 2024-02-20 o "fmt"
8 71191436 2024-02-28 o "io"
9 1d5ddf5d 2024-02-20 o "net/http"
10 71191436 2024-02-28 o "os"
11 1d5ddf5d 2024-02-20 o "strings"
12 1d5ddf5d 2024-02-20 o )
13 1d5ddf5d 2024-02-20 o
14 d3dfb672 2024-03-18 o var DefaultClient Client = Client{Client: http.DefaultClient}
15 1d5ddf5d 2024-02-20 o
16 1d5ddf5d 2024-02-20 o func Lookup(id string) (*Activity, error) {
17 d3dfb672 2024-03-18 o return DefaultClient.Lookup(id)
18 1d5ddf5d 2024-02-20 o }
19 1d5ddf5d 2024-02-20 o
20 1d5ddf5d 2024-02-20 o func LookupActor(id string) (*Actor, error) {
21 d3dfb672 2024-03-18 o return DefaultClient.LookupActor(id)
22 1d5ddf5d 2024-02-20 o }
23 1d5ddf5d 2024-02-20 o
24 1d5ddf5d 2024-02-20 o type Client struct {
25 1d5ddf5d 2024-02-20 o *http.Client
26 25fed994 2024-03-12 o // Key is a RSA private key which will be used to sign requests.
27 25fed994 2024-03-12 o Key *rsa.PrivateKey
28 25fed994 2024-03-12 o // PubKeyID is a URL where the corresponding public key of Key
29 25fed994 2024-03-12 o // may be accessed. This must be set if Key is also set.
30 25fed994 2024-03-12 o PubKeyID string // actor.PublicKey.ID
31 1d5ddf5d 2024-02-20 o }
32 1d5ddf5d 2024-02-20 o
33 1d5ddf5d 2024-02-20 o func (c *Client) Lookup(id string) (*Activity, error) {
34 1d5ddf5d 2024-02-20 o if !strings.HasPrefix(id, "http") {
35 1d5ddf5d 2024-02-20 o return nil, fmt.Errorf("id is not a HTTP URL")
36 1d5ddf5d 2024-02-20 o }
37 1d5ddf5d 2024-02-20 o if c.Client == nil {
38 1d5ddf5d 2024-02-20 o c.Client = http.DefaultClient
39 1d5ddf5d 2024-02-20 o }
40 1d5ddf5d 2024-02-20 o
41 d3dfb672 2024-03-18 o req, err := newRequest(http.MethodGet, id, nil, c.Key, c.PubKeyID)
42 1d5ddf5d 2024-02-20 o if err != nil {
43 d3dfb672 2024-03-18 o return nil, fmt.Errorf("new request: %w", err)
44 1d5ddf5d 2024-02-20 o }
45 1d5ddf5d 2024-02-20 o resp, err := c.Do(req)
46 1d5ddf5d 2024-02-20 o if err != nil {
47 1d5ddf5d 2024-02-20 o return nil, err
48 1d5ddf5d 2024-02-20 o }
49 1d5ddf5d 2024-02-20 o defer resp.Body.Close()
50 1d5ddf5d 2024-02-20 o if resp.StatusCode == http.StatusNotFound {
51 71191436 2024-02-28 o return nil, ErrNotExist
52 1d5ddf5d 2024-02-20 o } else if resp.StatusCode >= 400 {
53 1d5ddf5d 2024-02-20 o return nil, fmt.Errorf("non-ok response status %s", resp.Status)
54 1d5ddf5d 2024-02-20 o }
55 1d5ddf5d 2024-02-20 o return Decode(resp.Body)
56 1d5ddf5d 2024-02-20 o }
57 1d5ddf5d 2024-02-20 o
58 1d5ddf5d 2024-02-20 o func (c *Client) LookupActor(id string) (*Actor, error) {
59 1d5ddf5d 2024-02-20 o activity, err := c.Lookup(id)
60 1d5ddf5d 2024-02-20 o if err != nil {
61 1d5ddf5d 2024-02-20 o return nil, err
62 1d5ddf5d 2024-02-20 o }
63 d3dfb672 2024-03-18 o switch activity.Type {
64 d3dfb672 2024-03-18 o case "Application", "Group", "Organization", "Person", "Service":
65 d3dfb672 2024-03-18 o return activityToActor(activity), nil
66 d3dfb672 2024-03-18 o case "Collection", "OrderedCollection":
67 d3dfb672 2024-03-18 o // probably followers. let caller work out what it wants to do
68 d3dfb672 2024-03-18 o return activityToActor(activity), nil
69 d3dfb672 2024-03-18 o }
70 d3dfb672 2024-03-18 o return nil, fmt.Errorf("bad object Type %s", activity.Type)
71 71191436 2024-02-28 o }
72 71191436 2024-02-28 o
73 71191436 2024-02-28 o func activityToActor(activity *Activity) *Actor {
74 77918b00 2024-03-07 o actor := &Actor{
75 1d5ddf5d 2024-02-20 o AtContext: activity.AtContext,
76 1d5ddf5d 2024-02-20 o ID: activity.ID,
77 1d5ddf5d 2024-02-20 o Type: activity.Type,
78 1d5ddf5d 2024-02-20 o Name: activity.Name,
79 1d5ddf5d 2024-02-20 o Username: activity.Username,
80 1d5ddf5d 2024-02-20 o Inbox: activity.Inbox,
81 1d5ddf5d 2024-02-20 o Outbox: activity.Outbox,
82 77918b00 2024-03-07 o Followers: activity.Followers,
83 71191436 2024-02-28 o Published: activity.Published,
84 71191436 2024-02-28 o Summary: activity.Summary,
85 8403ab16 2024-03-21 o Endpoints: activity.Endpoints,
86 71191436 2024-02-28 o }
87 77918b00 2024-03-07 o if activity.PublicKey != nil {
88 77918b00 2024-03-07 o actor.PublicKey = *activity.PublicKey
89 77918b00 2024-03-07 o }
90 77918b00 2024-03-07 o return actor
91 1d5ddf5d 2024-02-20 o }
92 1d5ddf5d 2024-02-20 o
93 1d5ddf5d 2024-02-20 o func (c *Client) Send(inbox string, activity *Activity) (*Activity, error) {
94 1d5ddf5d 2024-02-20 o b, err := json.Marshal(activity)
95 1d5ddf5d 2024-02-20 o if err != nil {
96 1d5ddf5d 2024-02-20 o return nil, fmt.Errorf("encode outgoing activity: %w", err)
97 1d5ddf5d 2024-02-20 o }
98 d3dfb672 2024-03-18 o req, err := newRequest(http.MethodPost, inbox, bytes.NewReader(b), c.Key, c.PubKeyID)
99 1d5ddf5d 2024-02-20 o if err != nil {
100 1d5ddf5d 2024-02-20 o return nil, err
101 1d5ddf5d 2024-02-20 o }
102 1d5ddf5d 2024-02-20 o resp, err := c.Do(req)
103 1d5ddf5d 2024-02-20 o if err != nil {
104 1d5ddf5d 2024-02-20 o return nil, err
105 1d5ddf5d 2024-02-20 o }
106 1d5ddf5d 2024-02-20 o switch resp.StatusCode {
107 77918b00 2024-03-07 o case http.StatusOK, http.StatusAccepted, http.StatusNoContent:
108 1d5ddf5d 2024-02-20 o return nil, nil
109 1d5ddf5d 2024-02-20 o case http.StatusNotFound:
110 1d5ddf5d 2024-02-20 o return nil, fmt.Errorf("no such inbox %s", inbox)
111 1d5ddf5d 2024-02-20 o default:
112 1d5ddf5d 2024-02-20 o io.Copy(os.Stderr, resp.Body)
113 1d5ddf5d 2024-02-20 o resp.Body.Close()
114 1d5ddf5d 2024-02-20 o return nil, fmt.Errorf("non-ok response status %s", resp.Status)
115 1d5ddf5d 2024-02-20 o }
116 1d5ddf5d 2024-02-20 o }
117 d3dfb672 2024-03-18 o
118 d3dfb672 2024-03-18 o func newRequest(method, url string, body io.Reader, key *rsa.PrivateKey, pubkeyURL string) (*http.Request, error) {
119 d3dfb672 2024-03-18 o req, err := http.NewRequest(method, url, body)
120 d3dfb672 2024-03-18 o if err != nil {
121 d3dfb672 2024-03-18 o return nil, err
122 d3dfb672 2024-03-18 o }
123 d3dfb672 2024-03-18 o req.Header.Set("Accept", ContentType)
124 d3dfb672 2024-03-18 o if body != nil {
125 d3dfb672 2024-03-18 o req.Header.Set("Content-Type", ContentType)
126 d3dfb672 2024-03-18 o }
127 d3dfb672 2024-03-18 o if key != nil {
128 d3dfb672 2024-03-18 o if err := Sign(req, key, pubkeyURL); err != nil {
129 d3dfb672 2024-03-18 o return nil, fmt.Errorf("sign request: %w", err)
130 d3dfb672 2024-03-18 o }
131 d3dfb672 2024-03-18 o }
132 d3dfb672 2024-03-18 o return req, nil
133 d3dfb672 2024-03-18 o }