2019-12-03 16:23:59 +00:00
|
|
|
package activitypub
|
2017-09-16 10:31:23 +00:00
|
|
|
|
2021-03-14 18:36:32 +00:00
|
|
|
import (
|
2021-12-30 18:01:15 +00:00
|
|
|
"bytes"
|
|
|
|
"encoding/gob"
|
|
|
|
"errors"
|
2021-03-14 18:36:32 +00:00
|
|
|
"fmt"
|
2021-08-15 11:41:01 +00:00
|
|
|
|
|
|
|
"github.com/valyala/fastjson"
|
2021-03-14 18:36:32 +00:00
|
|
|
)
|
|
|
|
|
2021-11-12 19:05:08 +00:00
|
|
|
// LinkTypes represent the valid values for a Link object
|
2020-06-30 11:35:04 +00:00
|
|
|
var LinkTypes = ActivityVocabularyTypes{
|
|
|
|
LinkType,
|
2017-09-16 10:31:23 +00:00
|
|
|
MentionType,
|
|
|
|
}
|
|
|
|
|
2018-07-18 15:39:27 +00:00
|
|
|
// A Link is an indirect, qualified reference to a resource identified by a URL.
|
2017-09-16 10:31:23 +00:00
|
|
|
// The fundamental model for links is established by [ RFC5988].
|
2018-07-18 15:39:27 +00:00
|
|
|
// Many of the properties defined by the Activity Vocabulary allow values that are either instances of APObject or Link.
|
|
|
|
// When a Link is used, it establishes a qualified relation connecting the subject
|
2018-10-18 09:48:02 +00:00
|
|
|
// (the containing object) to the resource identified by the href.
|
2018-07-18 15:39:27 +00:00
|
|
|
// Properties of the Link are properties of the reference as opposed to properties of the resource.
|
2017-09-16 10:31:23 +00:00
|
|
|
type Link struct {
|
2018-07-18 15:39:27 +00:00
|
|
|
// Provides the globally unique identifier for an APObject or Link.
|
2019-12-05 18:02:15 +00:00
|
|
|
ID ID `jsonld:"id,omitempty"`
|
2018-10-18 09:48:02 +00:00
|
|
|
// Identifies the APObject or Link type. Multiple values may be specified.
|
2017-10-05 10:38:11 +00:00
|
|
|
Type ActivityVocabularyType `jsonld:"type,omitempty"`
|
|
|
|
// A simple, human-readable, plain-text name for the object.
|
|
|
|
// HTML markup MUST NOT be included. The name MAY be expressed using multiple language-tagged values.
|
2019-02-21 16:54:11 +00:00
|
|
|
Name NaturalLanguageValues `jsonld:"name,omitempty,collapsible"`
|
2018-07-18 15:39:27 +00:00
|
|
|
// A link relation associated with a Link. The value must conform to both the [HTML5] and
|
2018-10-18 09:48:02 +00:00
|
|
|
// [RFC5988](https://tools.ietf.org/html/rfc5988) "link relation" definitions.
|
2017-09-16 10:31:23 +00:00
|
|
|
// In the [HTML5], any string not containing the "space" U+0020, "tab" (U+0009), "LF" (U+000A),
|
2018-10-18 09:48:02 +00:00
|
|
|
// "FF" (U+000C), "CR" (U+000D) or "," (U+002C) characters can be used as a valid link relation.
|
2019-05-04 22:15:23 +00:00
|
|
|
Rel IRI `jsonld:"rel,omitempty"`
|
2018-07-18 14:19:50 +00:00
|
|
|
// When used on a Link, identifies the MIME media type of the referenced resource.
|
2017-10-02 09:53:09 +00:00
|
|
|
MediaType MimeType `jsonld:"mediaType,omitempty"`
|
2018-07-18 15:39:27 +00:00
|
|
|
// On a Link, specifies a hint as to the rendering height in device-independent pixels of the linked resource.
|
2017-10-02 09:53:09 +00:00
|
|
|
Height uint `jsonld:"height,omitempty"`
|
2018-07-18 15:39:27 +00:00
|
|
|
// On a Link, specifies a hint as to the rendering width in device-independent pixels of the linked resource.
|
2017-10-02 09:53:09 +00:00
|
|
|
Width uint `jsonld:"width,omitempty"`
|
2017-09-16 10:31:23 +00:00
|
|
|
// Identifies an entity that provides a preview of this object.
|
2018-10-04 18:33:32 +00:00
|
|
|
Preview Item `jsonld:"preview,omitempty"`
|
2018-07-18 15:39:27 +00:00
|
|
|
// The target resource pointed to by a Link.
|
2018-10-11 18:13:34 +00:00
|
|
|
Href IRI `jsonld:"href,omitempty"`
|
2017-09-16 10:31:23 +00:00
|
|
|
// Hints as to the language used by the target resource.
|
|
|
|
// Value must be a [BCP47](https://tools.ietf.org/html/bcp47) Language-Tag.
|
2017-10-02 09:53:09 +00:00
|
|
|
HrefLang LangRef `jsonld:"hrefLang,omitempty"`
|
2017-09-16 10:31:23 +00:00
|
|
|
}
|
|
|
|
|
2018-07-18 15:39:27 +00:00
|
|
|
// Mention is a specialized Link that represents an @mention.
|
2019-12-03 14:33:20 +00:00
|
|
|
type Mention = Link
|
2017-09-16 10:31:23 +00:00
|
|
|
|
2018-07-18 15:39:27 +00:00
|
|
|
// LinkNew initializes a new Link
|
2019-12-05 18:02:15 +00:00
|
|
|
func LinkNew(id ID, typ ActivityVocabularyType) *Link {
|
2020-06-30 11:35:04 +00:00
|
|
|
if !LinkTypes.Contains(typ) {
|
2018-06-09 12:05:46 +00:00
|
|
|
typ = LinkType
|
2017-09-16 10:31:23 +00:00
|
|
|
}
|
2018-06-09 12:05:46 +00:00
|
|
|
return &Link{ID: id, Type: typ}
|
2017-09-16 10:31:23 +00:00
|
|
|
}
|
2017-10-02 09:53:09 +00:00
|
|
|
|
2018-03-25 18:54:51 +00:00
|
|
|
// MentionNew initializes a new Mention
|
2019-12-05 18:02:15 +00:00
|
|
|
func MentionNew(id ID) *Mention {
|
2018-06-07 15:46:22 +00:00
|
|
|
return &Mention{ID: id, Type: MentionType}
|
2017-10-02 09:53:09 +00:00
|
|
|
}
|
|
|
|
|
2018-07-18 15:39:27 +00:00
|
|
|
// IsLink validates if current Link is a Link
|
2017-10-05 11:28:34 +00:00
|
|
|
func (l Link) IsLink() bool {
|
2020-06-30 11:35:04 +00:00
|
|
|
return l.Type == LinkType || LinkTypes.Contains(l.Type)
|
2017-10-02 09:53:09 +00:00
|
|
|
}
|
|
|
|
|
2018-07-18 15:39:27 +00:00
|
|
|
// IsObject validates if current Link is an GetID
|
2017-10-05 11:28:34 +00:00
|
|
|
func (l Link) IsObject() bool {
|
2019-05-16 09:08:27 +00:00
|
|
|
return l.Type == ObjectType || ObjectTypes.Contains(l.Type)
|
2017-10-02 09:53:09 +00:00
|
|
|
}
|
|
|
|
|
2019-12-01 18:27:45 +00:00
|
|
|
// IsCollection returns false for Link objects
|
|
|
|
func (l Link) IsCollection() bool {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2019-12-05 18:02:15 +00:00
|
|
|
// GetID returns the ID corresponding to the Link object
|
|
|
|
func (l Link) GetID() ID {
|
2019-12-03 15:26:43 +00:00
|
|
|
return l.ID
|
2018-06-07 15:46:22 +00:00
|
|
|
}
|
|
|
|
|
2018-10-11 18:48:38 +00:00
|
|
|
// GetLink returns the IRI corresponding to the current Link
|
|
|
|
func (l Link) GetLink() IRI {
|
|
|
|
return IRI(l.ID)
|
|
|
|
}
|
|
|
|
|
2018-07-18 15:39:27 +00:00
|
|
|
// GetType returns the Type corresponding to the Mention object
|
2018-06-09 12:05:46 +00:00
|
|
|
func (l Link) GetType() ActivityVocabularyType {
|
|
|
|
return l.Type
|
2018-06-07 15:46:22 +00:00
|
|
|
}
|
|
|
|
|
2021-11-12 18:10:31 +00:00
|
|
|
// MarshalJSON encodes the receiver object to a JSON document.
|
2019-12-22 13:08:56 +00:00
|
|
|
func (l Link) MarshalJSON() ([]byte, error) {
|
|
|
|
b := make([]byte, 0)
|
|
|
|
write(&b, '{')
|
|
|
|
|
2021-01-01 12:59:01 +00:00
|
|
|
if writeLinkJSONValue(&b, l) {
|
2019-12-22 13:08:56 +00:00
|
|
|
write(&b, '}')
|
|
|
|
return b, nil
|
|
|
|
}
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2021-11-12 18:10:31 +00:00
|
|
|
// UnmarshalJSON decodes an incoming JSON document into the receiver object.
|
2018-07-24 21:11:08 +00:00
|
|
|
func (l *Link) UnmarshalJSON(data []byte) error {
|
2021-08-15 11:41:01 +00:00
|
|
|
p := fastjson.Parser{}
|
|
|
|
val, err := p.ParseBytes(data)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return loadLink(val, l)
|
2018-07-24 21:11:08 +00:00
|
|
|
}
|
2019-12-21 14:25:22 +00:00
|
|
|
|
2021-03-14 18:36:32 +00:00
|
|
|
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface.
|
|
|
|
func (l *Link) UnmarshalBinary(data []byte) error {
|
2021-12-30 18:01:15 +00:00
|
|
|
return l.GobDecode(data)
|
2021-03-14 18:36:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// MarshalBinary implements the encoding.BinaryMarshaler interface.
|
|
|
|
func (l Link) MarshalBinary() ([]byte, error) {
|
2021-12-30 18:01:15 +00:00
|
|
|
return l.GobEncode()
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO(marius): when migrating to go1.18, use a numeric constraint for this
|
|
|
|
func gobEncodeUint(i uint) ([]byte, error) {
|
|
|
|
b := bytes.Buffer{}
|
|
|
|
gg := gob.NewEncoder(&b)
|
|
|
|
if err := gg.Encode(i); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return b.Bytes(), nil
|
2021-03-14 18:36:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (l Link) GobEncode() ([]byte, error) {
|
2021-12-30 18:01:15 +00:00
|
|
|
var (
|
|
|
|
mm = make(map[string][]byte)
|
|
|
|
err error
|
|
|
|
hasData bool
|
|
|
|
)
|
|
|
|
if len(l.ID) > 0 {
|
|
|
|
if mm["id"], err = l.ID.GobEncode(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
hasData = true
|
|
|
|
}
|
|
|
|
if len(l.Type) > 0 {
|
|
|
|
if mm["type"], err = l.Type.GobEncode(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
hasData = true
|
|
|
|
}
|
|
|
|
if len(l.MediaType) > 0 {
|
|
|
|
if mm["mediaType"], err = l.MediaType.GobEncode(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
hasData = true
|
|
|
|
}
|
|
|
|
if len(l.Href) > 0 {
|
|
|
|
if mm["href"], err = l.Href.GobEncode(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
hasData = true
|
|
|
|
}
|
|
|
|
if len(l.HrefLang) > 0 {
|
|
|
|
if mm["hrefLang"], err = l.HrefLang.GobEncode(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
hasData = true
|
|
|
|
}
|
|
|
|
if len(l.Name) > 0 {
|
|
|
|
if mm["name"], err = l.Name.GobEncode(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
hasData = true
|
|
|
|
}
|
|
|
|
if len(l.Rel) > 0 {
|
|
|
|
if mm["rel"], err = l.Rel.GobEncode(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
hasData = true
|
|
|
|
}
|
|
|
|
if l.Width > 0 {
|
|
|
|
if mm["width"], err = gobEncodeUint(l.Width); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
hasData = true
|
|
|
|
}
|
|
|
|
if l.Height > 0 {
|
|
|
|
if mm["height"], err = gobEncodeUint(l.Height); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
hasData = true
|
|
|
|
}
|
|
|
|
if !hasData {
|
|
|
|
return []byte{}, nil
|
|
|
|
}
|
|
|
|
bb := bytes.Buffer{}
|
|
|
|
g := gob.NewEncoder(&bb)
|
|
|
|
if err := g.Encode(mm); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return bb.Bytes(), nil
|
2021-03-14 18:36:32 +00:00
|
|
|
}
|
|
|
|
|
2021-12-30 18:01:15 +00:00
|
|
|
func gobDecodeUint(i *uint, data []byte) error {
|
|
|
|
g := gob.NewDecoder(bytes.NewReader(data))
|
|
|
|
return g.Decode(i)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *Link) GobDecode(data []byte) error {
|
|
|
|
mm := make(map[string][]byte)
|
|
|
|
g := gob.NewDecoder(bytes.NewReader(data))
|
|
|
|
if err := g.Decode(&mm); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if raw, ok := mm["id"]; ok {
|
|
|
|
if err := l.ID.GobDecode(raw); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if raw, ok := mm["type"]; ok {
|
|
|
|
if err := l.Type.GobDecode(raw); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if raw, ok := mm["mediaType"]; ok {
|
|
|
|
if err := l.MediaType.GobDecode(raw); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if raw, ok := mm["href"]; ok {
|
|
|
|
if err := l.Href.GobDecode(raw); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if raw, ok := mm["hrefLang"]; ok {
|
|
|
|
if err := l.HrefLang.GobDecode(raw); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if raw, ok := mm["name"]; ok {
|
|
|
|
if err := l.Name.GobDecode(raw); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if raw, ok := mm["rel"]; ok {
|
|
|
|
if err := l.Rel.GobDecode(raw); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if raw, ok := mm["width"]; ok {
|
|
|
|
if err := gobDecodeUint(&l.Width, raw); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if raw, ok := mm["height"]; ok {
|
|
|
|
if err := gobDecodeUint(&l.Height, raw); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2021-03-14 18:36:32 +00:00
|
|
|
return errors.New(fmt.Sprintf("GobDecode is not implemented for %T", *l))
|
|
|
|
}
|