utils.activitypub.protocol.go

  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
package main

import (
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"

	"fmt"
	"net/http"
	"net/url"
	"time"
)

type UserMeta struct {
	Context           []string      `json:"@context"`
	ID                string        `json:"id"`
	Type              string        `json:"type"`
	Inbox             string        `json:"inbox"`
	Outbox            string        `json:"outbox"`
	PrefferedUsername string        `json:"preferredUsername"`
	Name              string        `json:"name"`
	Summary           string        `json:"summary"`
	Icon              ImageMeta     `json:"icon"`
	PublicKey         PublicKeyMeta `json:"publicKey"`
}

type ImageMeta struct {
	Type      string `json:"type"`
	MediaType string `json:"mediaType"`
	URL       string `json:"url"`
}

func getSigned(user *User, inbox string) ([]byte, error) {
	if u, err := url.Parse(inbox); err == nil {
		host := u.Host
		path := u.Path
		date := time.Now().Format(http.TimeFormat)
		stringToSign := fmt.Sprintf("(request-target): get %s\nhost: %s\ndate: %s\ncontent-type: %s", path, host, date, "application/activity+json")
		if actSig, err := user.SignBlob("(request-target) host date content-type", stringToSign); err == nil {

			c, err := NewHTTPClient("GET", inbox, nil)
			if err != nil {
				return nil, err
			}
			return c.DoSigned(host, date, actSig)
		} else {
			return nil, err
		}
	} else {
		return nil, err
	}
}

func postSigned(user *User, data []byte, inbox string) error {
	if u, err := url.Parse(inbox); err == nil {
		host := u.Host
		path := u.Path
		date := time.Now().Format(http.TimeFormat)

		hashs := sha256.Sum256(data)
		digest := base64.StdEncoding.EncodeToString(hashs[:])

		stringToSign := fmt.Sprintf("(request-target): post %s\nhost: %s\ndate: %s\ndigest: SHA-256=%s\ncontent-type: %s", path, host, date, digest, "application/activity+json")
		if actSig, err := user.SignBlob("(request-target) host date digest content-type", stringToSign); err == nil {
			c, err := NewHTTPClient("POST", inbox, data)
			if err != nil {
				return err
			}
			_, err = c.DoSignedDigest(host, date, actSig, "SHA-256="+digest)
			return err
		} else {
			return err
		}
	} else {
		return err
	}
}

func (user *User) fetchActorInfo(actor string, usecache bool) (*Actor, error) {

	// short circuit a check for ourselves...
	if actor == user.ID {
		return &Actor{ID: user.ID,
			Name:      user.Name,
			UserName:  user.UserNamePlain,
			Icon:      ImageMeta{URL: user.Icon, MediaType: "image/jpeg", Type: "Image"},
			Inbox:     user.ID + "/inbox",
			PublicKey: PublicKeyMeta{PublicKey: user.PublicKey, Owner: user.ID, ID: (user.ID + "#main-key")},
		}, nil
	}

	if usecache {
		if data, err := FromCache(user, "actors", "", actor); err == nil {
			actor := &Actor{}
			if err := json.Unmarshal(data, actor); err == nil {
				return actor, nil
			}
		}
	}

	if body, err := getSigned(user, actor); err == nil {
		actorInfo := &Actor{}
		if err := json.Unmarshal([]byte(body), &actorInfo); err == nil {
			CacheData(user, "actors", "", actorInfo.ID, []byte(body))
			return actorInfo, nil
		} else {
			return nil, err
		}
	} else {
		return nil, err
	}
}