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
|
package logic
import (
"crypto/rsa"
"encoding/json"
"errors"
"fmt"
"github.com/go-fed/httpsig"
"io"
"net/http"
"rss_parrot/dto"
"rss_parrot/shared"
"strings"
"time"
)
type IUserRetriever interface {
Retrieve(userUrl string) (info *dto.UserInfo, err error)
}
const retrieveTimeoutSec = 10
type userRetriever struct {
cfg *shared.Config
userAgent shared.IUserAgent
keyStore IKeyStore
idb shared.IdBuilder
}
func NewUserRetriever(cfg *shared.Config, userAgent shared.IUserAgent, keyStore IKeyStore) IUserRetriever {
return &userRetriever{cfg, userAgent, keyStore, shared.IdBuilder{Host: cfg.Host}}
}
func (ur *userRetriever) Retrieve(userUrl string) (info *dto.UserInfo, err error) {
host := strings.Replace(userUrl, "https://", "", -1)
slashIx := strings.IndexByte(host, '/')
host = host[:slashIx]
dateStr := time.Now().UTC().Format(http.TimeFormat)
var req *http.Request
if req, err = http.NewRequest("GET", userUrl, nil); err != nil {
return nil, err
}
ur.userAgent.AddUserAgent(req)
req.Header.Set("Accept", "application/activity+json")
req.Header.Set("host", host)
req.Header.Set("date", dateStr)
signer, _, err := httpsig.NewSigner(
[]httpsig.Algorithm{httpsig.RSA_SHA256},
httpsig.DigestSha256,
[]string{httpsig.RequestTarget, "Host", "date", "digest"},
httpsig.Signature,
0)
if err != nil {
return nil, err
}
var privKey *rsa.PrivateKey
privKey, err = ur.keyStore.GetPrivKey(ur.cfg.Birb.User)
if err != nil {
return nil, err
}
keyId := ur.idb.UserKeyId(ur.cfg.Birb.User)
err = signer.SignRequest(privKey, keyId, req, []byte{})
if err != nil {
return nil, err
}
client := http.Client{}
client.Timeout = time.Second * retrieveTimeoutSec
var resp *http.Response
if resp, err = client.Do(req); err != nil {
return nil, err
}
defer resp.Body.Close()
var bodyBytes []byte
var bodyErr error
bodyBytes, bodyErr = io.ReadAll(resp.Body)
if resp.StatusCode != http.StatusOK {
var msg string
if bodyErr != nil {
msg = fmt.Sprintf("failed to get user profile; got status %v; body: %s", resp.StatusCode, string(bodyBytes))
} else {
msg = fmt.Sprintf("failed to get user profile; got status %v (no body)", resp.StatusCode)
}
return nil, errors.New(msg)
}
if bodyErr != nil {
return nil, bodyErr
}
var obj dto.UserInfo
if err = json.Unmarshal(bodyBytes, &obj); err != nil {
return nil, err
}
return &obj, nil
}
|