gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

util.go (28634B)


      1 package pub
      2 
      3 import (
      4 	"bytes"
      5 	"context"
      6 	"crypto/sha256"
      7 	"encoding/base64"
      8 	"encoding/json"
      9 	"errors"
     10 	"fmt"
     11 	"net/http"
     12 	"net/url"
     13 	"strings"
     14 	"time"
     15 
     16 	"github.com/superseriousbusiness/activity/streams"
     17 	"github.com/superseriousbusiness/activity/streams/vocab"
     18 )
     19 
     20 var (
     21 	// ErrObjectRequired indicates the activity needs its object property
     22 	// set. Can be returned by DelegateActor's PostInbox or PostOutbox so a
     23 	// Bad Request response is set.
     24 	ErrObjectRequired = errors.New("object property required on the provided activity")
     25 	// ErrTargetRequired indicates the activity needs its target property
     26 	// set. Can be returned by DelegateActor's PostInbox or PostOutbox so a
     27 	// Bad Request response is set.
     28 	ErrTargetRequired = errors.New("target property required on the provided activity")
     29 )
     30 
     31 // activityStreamsMediaTypes contains all of the accepted ActivityStreams media
     32 // types. Generated at init time.
     33 var activityStreamsMediaTypes []string
     34 
     35 func init() {
     36 	activityStreamsMediaTypes = []string{
     37 		"application/activity+json",
     38 	}
     39 	jsonLdType := "application/ld+json"
     40 	for _, semi := range []string{";", " ;", " ; ", "; "} {
     41 		for _, profile := range []string{
     42 			"profile=https://www.w3.org/ns/activitystreams",
     43 			"profile=\"https://www.w3.org/ns/activitystreams\"",
     44 		} {
     45 			activityStreamsMediaTypes = append(
     46 				activityStreamsMediaTypes,
     47 				fmt.Sprintf("%s%s%s", jsonLdType, semi, profile))
     48 		}
     49 	}
     50 }
     51 
     52 // headerIsActivityPubMediaType returns true if the header string contains one
     53 // of the accepted ActivityStreams media types.
     54 //
     55 // Note we don't try to build a comprehensive parser and instead accept a
     56 // tolerable amount of whitespace since the HTTP specification is ambiguous
     57 // about the format and significance of whitespace.
     58 func headerIsActivityPubMediaType(header string) bool {
     59 	for _, mediaType := range activityStreamsMediaTypes {
     60 		if strings.Contains(header, mediaType) {
     61 			return true
     62 		}
     63 	}
     64 	return false
     65 }
     66 
     67 const (
     68 	// The Content-Type header.
     69 	contentTypeHeader = "Content-Type"
     70 	// The Accept header.
     71 	acceptHeader = "Accept"
     72 )
     73 
     74 // isActivityPubPost returns true if the request is a POST request that has the
     75 // ActivityStreams content type header
     76 func isActivityPubPost(r *http.Request) bool {
     77 	return r.Method == "POST" && headerIsActivityPubMediaType(r.Header.Get(contentTypeHeader))
     78 }
     79 
     80 // isActivityPubGet returns true if the request is a GET request that has the
     81 // ActivityStreams content type header
     82 func isActivityPubGet(r *http.Request) bool {
     83 	return r.Method == "GET" && headerIsActivityPubMediaType(r.Header.Get(acceptHeader))
     84 }
     85 
     86 // dedupeOrderedItems deduplicates the 'orderedItems' within an ordered
     87 // collection type. Deduplication happens by the 'id' property.
     88 func dedupeOrderedItems(oc orderedItemser) error {
     89 	oi := oc.GetActivityStreamsOrderedItems()
     90 	if oi == nil {
     91 		return nil
     92 	}
     93 	seen := make(map[string]bool, oi.Len())
     94 	for i := 0; i < oi.Len(); {
     95 		var id *url.URL
     96 
     97 		iter := oi.At(i)
     98 		asType := iter.GetType()
     99 		if asType != nil {
    100 			var err error
    101 			id, err = GetId(asType)
    102 			if err != nil {
    103 				return err
    104 			}
    105 		} else if iter.IsIRI() {
    106 			id = iter.GetIRI()
    107 		} else {
    108 			return fmt.Errorf("element %d in OrderedCollection does not have an ID nor is an IRI", i)
    109 		}
    110 		if seen[id.String()] {
    111 			oi.Remove(i)
    112 		} else {
    113 			seen[id.String()] = true
    114 			i++
    115 		}
    116 	}
    117 	return nil
    118 }
    119 
    120 const (
    121 	// The Location header
    122 	locationHeader = "Location"
    123 	// Contains the ActivityStreams Content-Type value.
    124 	contentTypeHeaderValue = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
    125 	// The Date header.
    126 	dateHeader = "Date"
    127 	// The Digest header.
    128 	digestHeader = "Digest"
    129 	// The delimiter used in the Digest header.
    130 	digestDelimiter = "="
    131 	// SHA-256 string for the Digest header.
    132 	sha256Digest = "SHA-256"
    133 )
    134 
    135 // addResponseHeaders sets headers needed in the HTTP response, such but not
    136 // limited to the Content-Type, Date, and Digest headers.
    137 func addResponseHeaders(h http.Header, c Clock, responseContent []byte) {
    138 	h.Set(contentTypeHeader, contentTypeHeaderValue)
    139 	// RFC 7231 ยง7.1.1.2
    140 	h.Set(dateHeader, c.Now().UTC().Format("Mon, 02 Jan 2006 15:04:05")+" GMT")
    141 	// RFC 3230 and RFC 5843
    142 	var b bytes.Buffer
    143 	b.WriteString(sha256Digest)
    144 	b.WriteString(digestDelimiter)
    145 	hashed := sha256.Sum256(responseContent)
    146 	b.WriteString(base64.StdEncoding.EncodeToString(hashed[:]))
    147 	h.Set(digestHeader, b.String())
    148 }
    149 
    150 // IdProperty is a property that can readily have its id obtained
    151 type IdProperty interface {
    152 	// GetIRI returns the IRI of this property. When IsIRI returns false,
    153 	// GetIRI will return an arbitrary value.
    154 	GetIRI() *url.URL
    155 	// GetType returns the value in this property as a Type. Returns nil if
    156 	// the value is not an ActivityStreams type, such as an IRI or another
    157 	// value.
    158 	GetType() vocab.Type
    159 	// IsIRI returns true if this property is an IRI.
    160 	IsIRI() bool
    161 }
    162 
    163 // ToId returns an IdProperty's id.
    164 func ToId(i IdProperty) (*url.URL, error) {
    165 	if i.GetType() != nil {
    166 		return GetId(i.GetType())
    167 	} else if i.IsIRI() {
    168 		return i.GetIRI(), nil
    169 	}
    170 	return nil, fmt.Errorf("cannot determine id of activitystreams property")
    171 }
    172 
    173 // GetId will attempt to find the 'id' property or, if it happens to be a
    174 // Link or derived from Link type, the 'href' property instead.
    175 //
    176 // Returns an error if the id is not set and either the 'href' property is not
    177 // valid on this type, or it is also not set.
    178 func GetId(t vocab.Type) (*url.URL, error) {
    179 	if id := t.GetJSONLDId(); id != nil {
    180 		return id.Get(), nil
    181 	} else if h, ok := t.(hrefer); ok {
    182 		if href := h.GetActivityStreamsHref(); href != nil {
    183 			return href.Get(), nil
    184 		}
    185 	}
    186 	return nil, fmt.Errorf("cannot determine id of activitystreams value")
    187 }
    188 
    189 // getInboxForwardingValues obtains the 'inReplyTo', 'object', 'target', and
    190 // 'tag' values on an ActivityStreams value.
    191 func getInboxForwardingValues(o vocab.Type) (t []vocab.Type, iri []*url.URL) {
    192 	// 'inReplyTo'
    193 	if i, ok := o.(inReplyToer); ok {
    194 		if irt := i.GetActivityStreamsInReplyTo(); irt != nil {
    195 			for iter := irt.Begin(); iter != irt.End(); iter = iter.Next() {
    196 				if tv := iter.GetType(); tv != nil {
    197 					t = append(t, tv)
    198 				} else {
    199 					iri = append(iri, iter.GetIRI())
    200 				}
    201 			}
    202 		}
    203 	}
    204 	// 'tag'
    205 	if i, ok := o.(tagger); ok {
    206 		if tag := i.GetActivityStreamsTag(); tag != nil {
    207 			for iter := tag.Begin(); iter != tag.End(); iter = iter.Next() {
    208 				if tv := iter.GetType(); tv != nil {
    209 					t = append(t, tv)
    210 				} else {
    211 					iri = append(iri, iter.GetIRI())
    212 				}
    213 			}
    214 		}
    215 	}
    216 	// 'object'
    217 	if i, ok := o.(objecter); ok {
    218 		if obj := i.GetActivityStreamsObject(); obj != nil {
    219 			for iter := obj.Begin(); iter != obj.End(); iter = iter.Next() {
    220 				if tv := iter.GetType(); tv != nil {
    221 					t = append(t, tv)
    222 				} else {
    223 					iri = append(iri, iter.GetIRI())
    224 				}
    225 			}
    226 		}
    227 	}
    228 	// 'target'
    229 	if i, ok := o.(targeter); ok {
    230 		if tar := i.GetActivityStreamsTarget(); tar != nil {
    231 			for iter := tar.Begin(); iter != tar.End(); iter = iter.Next() {
    232 				if tv := iter.GetType(); tv != nil {
    233 					t = append(t, tv)
    234 				} else {
    235 					iri = append(iri, iter.GetIRI())
    236 				}
    237 			}
    238 		}
    239 	}
    240 	return
    241 }
    242 
    243 // wrapInCreate will automatically wrap the provided object in a Create
    244 // activity. This will copy over the 'to', 'bto', 'cc', 'bcc', and 'audience'
    245 // properties. It will also copy over the published time if present.
    246 func wrapInCreate(ctx context.Context, o vocab.Type, actor *url.URL) (c vocab.ActivityStreamsCreate, err error) {
    247 	c = streams.NewActivityStreamsCreate()
    248 	// Object property
    249 	oProp := streams.NewActivityStreamsObjectProperty()
    250 	oProp.AppendType(o)
    251 	c.SetActivityStreamsObject(oProp)
    252 	// Actor Property
    253 	actorProp := streams.NewActivityStreamsActorProperty()
    254 	actorProp.AppendIRI(actor)
    255 	c.SetActivityStreamsActor(actorProp)
    256 	// Published Property
    257 	if v, ok := o.(publisheder); ok {
    258 		c.SetActivityStreamsPublished(v.GetActivityStreamsPublished())
    259 	}
    260 	// Copying over properties.
    261 	if v, ok := o.(toer); ok {
    262 		if to := v.GetActivityStreamsTo(); to != nil {
    263 			activityTo := streams.NewActivityStreamsToProperty()
    264 			for iter := to.Begin(); iter != to.End(); iter = iter.Next() {
    265 				var id *url.URL
    266 				id, err = ToId(iter)
    267 				if err != nil {
    268 					return
    269 				}
    270 				activityTo.AppendIRI(id)
    271 			}
    272 			c.SetActivityStreamsTo(activityTo)
    273 		}
    274 	}
    275 	if v, ok := o.(btoer); ok {
    276 		if bto := v.GetActivityStreamsBto(); bto != nil {
    277 			activityBto := streams.NewActivityStreamsBtoProperty()
    278 			for iter := bto.Begin(); iter != bto.End(); iter = iter.Next() {
    279 				var id *url.URL
    280 				id, err = ToId(iter)
    281 				if err != nil {
    282 					return
    283 				}
    284 				activityBto.AppendIRI(id)
    285 			}
    286 			c.SetActivityStreamsBto(activityBto)
    287 		}
    288 	}
    289 	if v, ok := o.(ccer); ok {
    290 		if cc := v.GetActivityStreamsCc(); cc != nil {
    291 			activityCc := streams.NewActivityStreamsCcProperty()
    292 			for iter := cc.Begin(); iter != cc.End(); iter = iter.Next() {
    293 				var id *url.URL
    294 				id, err = ToId(iter)
    295 				if err != nil {
    296 					return
    297 				}
    298 				activityCc.AppendIRI(id)
    299 			}
    300 			c.SetActivityStreamsCc(activityCc)
    301 		}
    302 	}
    303 	if v, ok := o.(bccer); ok {
    304 		if bcc := v.GetActivityStreamsBcc(); bcc != nil {
    305 			activityBcc := streams.NewActivityStreamsBccProperty()
    306 			for iter := bcc.Begin(); iter != bcc.End(); iter = iter.Next() {
    307 				var id *url.URL
    308 				id, err = ToId(iter)
    309 				if err != nil {
    310 					return
    311 				}
    312 				activityBcc.AppendIRI(id)
    313 			}
    314 			c.SetActivityStreamsBcc(activityBcc)
    315 		}
    316 	}
    317 	if v, ok := o.(audiencer); ok {
    318 		if aud := v.GetActivityStreamsAudience(); aud != nil {
    319 			activityAudience := streams.NewActivityStreamsAudienceProperty()
    320 			for iter := aud.Begin(); iter != aud.End(); iter = iter.Next() {
    321 				var id *url.URL
    322 				id, err = ToId(iter)
    323 				if err != nil {
    324 					return
    325 				}
    326 				activityAudience.AppendIRI(id)
    327 			}
    328 			c.SetActivityStreamsAudience(activityAudience)
    329 		}
    330 	}
    331 	return
    332 }
    333 
    334 // filterURLs removes urls whose strings match the provided filter
    335 func filterURLs(u []*url.URL, fn func(s string) bool) []*url.URL {
    336 	i := 0
    337 	for i < len(u) {
    338 		if fn(u[i].String()) {
    339 			u = append(u[:i], u[i+1:]...)
    340 		} else {
    341 			i++
    342 		}
    343 	}
    344 	return u
    345 }
    346 
    347 const (
    348 	// PublicActivityPubIRI is the IRI that indicates an Activity is meant
    349 	// to be visible for general public consumption.
    350 	PublicActivityPubIRI = "https://www.w3.org/ns/activitystreams#Public"
    351 	publicJsonLD         = "Public"
    352 	publicJsonLDAS       = "as:Public"
    353 )
    354 
    355 // IsPublic determines if an IRI string is the Public collection as defined in
    356 // the spec, including JSON-LD compliant collections.
    357 func IsPublic(s string) bool {
    358 	return s == PublicActivityPubIRI || s == publicJsonLD || s == publicJsonLDAS
    359 }
    360 
    361 // getInboxes extracts the 'inbox' IRIs from actor types.
    362 func getInboxes(t []vocab.Type) (u []*url.URL, err error) {
    363 	for _, elem := range t {
    364 		var iri *url.URL
    365 		iri, err = getInbox(elem)
    366 		if err != nil {
    367 			return
    368 		}
    369 		u = append(u, iri)
    370 	}
    371 	return
    372 }
    373 
    374 // getInbox extracts the 'inbox' IRI from an actor type.
    375 func getInbox(t vocab.Type) (u *url.URL, err error) {
    376 	ib, ok := t.(inboxer)
    377 	if !ok {
    378 		err = fmt.Errorf("actor type %T has no inbox", t)
    379 		return
    380 	}
    381 	inbox := ib.GetActivityStreamsInbox()
    382 	return ToId(inbox)
    383 }
    384 
    385 // dedupeIRIs will deduplicate final inbox IRIs. The ignore list is applied to
    386 // the final list.
    387 func dedupeIRIs(recipients, ignored []*url.URL) (out []*url.URL) {
    388 	ignoredMap := make(map[string]bool, len(ignored))
    389 	for _, elem := range ignored {
    390 		ignoredMap[elem.String()] = true
    391 	}
    392 	outMap := make(map[string]bool, len(recipients))
    393 	for _, k := range recipients {
    394 		kStr := k.String()
    395 		if !ignoredMap[kStr] && !outMap[kStr] {
    396 			out = append(out, k)
    397 			outMap[kStr] = true
    398 		}
    399 	}
    400 	return
    401 }
    402 
    403 // removeOne removes any occurrences of entry from a slice of entries.
    404 func removeOne(entries []*url.URL, entry *url.URL) (out []*url.URL) {
    405 	for _, e := range entries {
    406 		if e.String() != entry.String() {
    407 			out = append(out, e)
    408 		}
    409 	}
    410 	return out
    411 }
    412 
    413 // stripHiddenRecipients removes "bto" and "bcc" from the activity.
    414 //
    415 // Note that this requirement of the specification is under "Section 6: Client
    416 // to Server Interactions", the Social API, and not the Federative API.
    417 func stripHiddenRecipients(activity Activity) {
    418 	activity.SetActivityStreamsBto(nil)
    419 	activity.SetActivityStreamsBcc(nil)
    420 	op := activity.GetActivityStreamsObject()
    421 	if op != nil {
    422 		for iter := op.Begin(); iter != op.End(); iter = iter.Next() {
    423 			if v, ok := iter.GetType().(btoer); ok {
    424 				v.SetActivityStreamsBto(nil)
    425 			}
    426 			if v, ok := iter.GetType().(bccer); ok {
    427 				v.SetActivityStreamsBcc(nil)
    428 			}
    429 		}
    430 	}
    431 }
    432 
    433 // mustHaveActivityOriginMatchObjects ensures that the Host in the activity id
    434 // IRI matches all of the Hosts in the object id IRIs.
    435 func mustHaveActivityOriginMatchObjects(a Activity) error {
    436 	originIRI, err := GetId(a)
    437 	if err != nil {
    438 		return err
    439 	}
    440 	originHost := originIRI.Host
    441 	op := a.GetActivityStreamsObject()
    442 	if op == nil || op.Len() == 0 {
    443 		return nil
    444 	}
    445 	for iter := op.Begin(); iter != op.End(); iter = iter.Next() {
    446 		iri, err := ToId(iter)
    447 		if err != nil {
    448 			return err
    449 		}
    450 		if originHost != iri.Host {
    451 			return fmt.Errorf("object %q: not in activity origin", iri)
    452 		}
    453 	}
    454 	return nil
    455 }
    456 
    457 // normalizeRecipients ensures the activity and object have the same 'to',
    458 // 'bto', 'cc', 'bcc', and 'audience' properties. Copy the Activity's recipients
    459 // to objects, and the objects to the activity, but does NOT copy objects'
    460 // recipients to each other.
    461 func normalizeRecipients(a vocab.ActivityStreamsCreate) error {
    462 	// Phase 0: Acquire all recipients on the activity.
    463 	//
    464 	// Obtain the actorTo map
    465 	actorToMap := make(map[string]*url.URL)
    466 	actorTo := a.GetActivityStreamsTo()
    467 	if actorTo == nil {
    468 		actorTo = streams.NewActivityStreamsToProperty()
    469 		a.SetActivityStreamsTo(actorTo)
    470 	}
    471 	for iter := actorTo.Begin(); iter != actorTo.End(); iter = iter.Next() {
    472 		id, err := ToId(iter)
    473 		if err != nil {
    474 			return err
    475 		}
    476 		actorToMap[id.String()] = id
    477 	}
    478 	// Obtain the actorBto map
    479 	actorBtoMap := make(map[string]*url.URL)
    480 	actorBto := a.GetActivityStreamsBto()
    481 	if actorBto == nil {
    482 		actorBto = streams.NewActivityStreamsBtoProperty()
    483 		a.SetActivityStreamsBto(actorBto)
    484 	}
    485 	for iter := actorBto.Begin(); iter != actorBto.End(); iter = iter.Next() {
    486 		id, err := ToId(iter)
    487 		if err != nil {
    488 			return err
    489 		}
    490 		actorBtoMap[id.String()] = id
    491 	}
    492 	// Obtain the actorCc map
    493 	actorCcMap := make(map[string]*url.URL)
    494 	actorCc := a.GetActivityStreamsCc()
    495 	if actorCc == nil {
    496 		actorCc = streams.NewActivityStreamsCcProperty()
    497 		a.SetActivityStreamsCc(actorCc)
    498 	}
    499 	for iter := actorCc.Begin(); iter != actorCc.End(); iter = iter.Next() {
    500 		id, err := ToId(iter)
    501 		if err != nil {
    502 			return err
    503 		}
    504 		actorCcMap[id.String()] = id
    505 	}
    506 	// Obtain the actorBcc map
    507 	actorBccMap := make(map[string]*url.URL)
    508 	actorBcc := a.GetActivityStreamsBcc()
    509 	if actorBcc == nil {
    510 		actorBcc = streams.NewActivityStreamsBccProperty()
    511 		a.SetActivityStreamsBcc(actorBcc)
    512 	}
    513 	for iter := actorBcc.Begin(); iter != actorBcc.End(); iter = iter.Next() {
    514 		id, err := ToId(iter)
    515 		if err != nil {
    516 			return err
    517 		}
    518 		actorBccMap[id.String()] = id
    519 	}
    520 	// Obtain the actorAudience map
    521 	actorAudienceMap := make(map[string]*url.URL)
    522 	actorAudience := a.GetActivityStreamsAudience()
    523 	if actorAudience == nil {
    524 		actorAudience = streams.NewActivityStreamsAudienceProperty()
    525 		a.SetActivityStreamsAudience(actorAudience)
    526 	}
    527 	for iter := actorAudience.Begin(); iter != actorAudience.End(); iter = iter.Next() {
    528 		id, err := ToId(iter)
    529 		if err != nil {
    530 			return err
    531 		}
    532 		actorAudienceMap[id.String()] = id
    533 	}
    534 	// Obtain the objects maps for each recipient type.
    535 	o := a.GetActivityStreamsObject()
    536 	objsTo := make([]map[string]*url.URL, o.Len())
    537 	objsBto := make([]map[string]*url.URL, o.Len())
    538 	objsCc := make([]map[string]*url.URL, o.Len())
    539 	objsBcc := make([]map[string]*url.URL, o.Len())
    540 	objsAudience := make([]map[string]*url.URL, o.Len())
    541 	for i := 0; i < o.Len(); i++ {
    542 		iter := o.At(i)
    543 		// Phase 1: Acquire all existing recipients on the object.
    544 		//
    545 		// Object to
    546 		objsTo[i] = make(map[string]*url.URL)
    547 		var oTo vocab.ActivityStreamsToProperty
    548 		if tr, ok := iter.GetType().(toer); !ok {
    549 			return fmt.Errorf("the Create object at %d has no 'to' property", i)
    550 		} else {
    551 			oTo = tr.GetActivityStreamsTo()
    552 			if oTo == nil {
    553 				oTo = streams.NewActivityStreamsToProperty()
    554 				tr.SetActivityStreamsTo(oTo)
    555 			}
    556 		}
    557 		for iter := oTo.Begin(); iter != oTo.End(); iter = iter.Next() {
    558 			id, err := ToId(iter)
    559 			if err != nil {
    560 				return err
    561 			}
    562 			objsTo[i][id.String()] = id
    563 		}
    564 		// Object bto
    565 		objsBto[i] = make(map[string]*url.URL)
    566 		var oBto vocab.ActivityStreamsBtoProperty
    567 		if tr, ok := iter.GetType().(btoer); !ok {
    568 			return fmt.Errorf("the Create object at %d has no 'bto' property", i)
    569 		} else {
    570 			oBto = tr.GetActivityStreamsBto()
    571 			if oBto == nil {
    572 				oBto = streams.NewActivityStreamsBtoProperty()
    573 				tr.SetActivityStreamsBto(oBto)
    574 			}
    575 		}
    576 		for iter := oBto.Begin(); iter != oBto.End(); iter = iter.Next() {
    577 			id, err := ToId(iter)
    578 			if err != nil {
    579 				return err
    580 			}
    581 			objsBto[i][id.String()] = id
    582 		}
    583 		// Object cc
    584 		objsCc[i] = make(map[string]*url.URL)
    585 		var oCc vocab.ActivityStreamsCcProperty
    586 		if tr, ok := iter.GetType().(ccer); !ok {
    587 			return fmt.Errorf("the Create object at %d has no 'cc' property", i)
    588 		} else {
    589 			oCc = tr.GetActivityStreamsCc()
    590 			if oCc == nil {
    591 				oCc = streams.NewActivityStreamsCcProperty()
    592 				tr.SetActivityStreamsCc(oCc)
    593 			}
    594 		}
    595 		for iter := oCc.Begin(); iter != oCc.End(); iter = iter.Next() {
    596 			id, err := ToId(iter)
    597 			if err != nil {
    598 				return err
    599 			}
    600 			objsCc[i][id.String()] = id
    601 		}
    602 		// Object bcc
    603 		objsBcc[i] = make(map[string]*url.URL)
    604 		var oBcc vocab.ActivityStreamsBccProperty
    605 		if tr, ok := iter.GetType().(bccer); !ok {
    606 			return fmt.Errorf("the Create object at %d has no 'bcc' property", i)
    607 		} else {
    608 			oBcc = tr.GetActivityStreamsBcc()
    609 			if oBcc == nil {
    610 				oBcc = streams.NewActivityStreamsBccProperty()
    611 				tr.SetActivityStreamsBcc(oBcc)
    612 			}
    613 		}
    614 		for iter := oBcc.Begin(); iter != oBcc.End(); iter = iter.Next() {
    615 			id, err := ToId(iter)
    616 			if err != nil {
    617 				return err
    618 			}
    619 			objsBcc[i][id.String()] = id
    620 		}
    621 		// Object audience
    622 		objsAudience[i] = make(map[string]*url.URL)
    623 		var oAudience vocab.ActivityStreamsAudienceProperty
    624 		if tr, ok := iter.GetType().(audiencer); !ok {
    625 			return fmt.Errorf("the Create object at %d has no 'audience' property", i)
    626 		} else {
    627 			oAudience = tr.GetActivityStreamsAudience()
    628 			if oAudience == nil {
    629 				oAudience = streams.NewActivityStreamsAudienceProperty()
    630 				tr.SetActivityStreamsAudience(oAudience)
    631 			}
    632 		}
    633 		for iter := oAudience.Begin(); iter != oAudience.End(); iter = iter.Next() {
    634 			id, err := ToId(iter)
    635 			if err != nil {
    636 				return err
    637 			}
    638 			objsAudience[i][id.String()] = id
    639 		}
    640 		// Phase 2: Apply missing recipients to the object from the
    641 		// activity.
    642 		//
    643 		// Activity to -> Object to
    644 		for k, v := range actorToMap {
    645 			if _, ok := objsTo[i][k]; !ok {
    646 				oTo.AppendIRI(v)
    647 			}
    648 		}
    649 		// Activity bto -> Object bto
    650 		for k, v := range actorBtoMap {
    651 			if _, ok := objsBto[i][k]; !ok {
    652 				oBto.AppendIRI(v)
    653 			}
    654 		}
    655 		// Activity cc -> Object cc
    656 		for k, v := range actorCcMap {
    657 			if _, ok := objsCc[i][k]; !ok {
    658 				oCc.AppendIRI(v)
    659 			}
    660 		}
    661 		// Activity bcc -> Object bcc
    662 		for k, v := range actorBccMap {
    663 			if _, ok := objsBcc[i][k]; !ok {
    664 				oBcc.AppendIRI(v)
    665 			}
    666 		}
    667 		// Activity audience -> Object audience
    668 		for k, v := range actorAudienceMap {
    669 			if _, ok := objsAudience[i][k]; !ok {
    670 				oAudience.AppendIRI(v)
    671 			}
    672 		}
    673 	}
    674 	// Phase 3: Apply missing recipients to the activity from the objects.
    675 	//
    676 	// Object to -> Activity to
    677 	for i := 0; i < len(objsTo); i++ {
    678 		for k, v := range objsTo[i] {
    679 			if _, ok := actorToMap[k]; !ok {
    680 				actorTo.AppendIRI(v)
    681 			}
    682 		}
    683 	}
    684 	// Object bto -> Activity bto
    685 	for i := 0; i < len(objsBto); i++ {
    686 		for k, v := range objsBto[i] {
    687 			if _, ok := actorBtoMap[k]; !ok {
    688 				actorBto.AppendIRI(v)
    689 			}
    690 		}
    691 	}
    692 	// Object cc -> Activity cc
    693 	for i := 0; i < len(objsCc); i++ {
    694 		for k, v := range objsCc[i] {
    695 			if _, ok := actorCcMap[k]; !ok {
    696 				actorCc.AppendIRI(v)
    697 			}
    698 		}
    699 	}
    700 	// Object bcc -> Activity bcc
    701 	for i := 0; i < len(objsBcc); i++ {
    702 		for k, v := range objsBcc[i] {
    703 			if _, ok := actorBccMap[k]; !ok {
    704 				actorBcc.AppendIRI(v)
    705 			}
    706 		}
    707 	}
    708 	// Object audience -> Activity audience
    709 	for i := 0; i < len(objsAudience); i++ {
    710 		for k, v := range objsAudience[i] {
    711 			if _, ok := actorAudienceMap[k]; !ok {
    712 				actorAudience.AppendIRI(v)
    713 			}
    714 		}
    715 	}
    716 	return nil
    717 }
    718 
    719 // toTombstone creates a Tombstone object for the given ActivityStreams value.
    720 func toTombstone(obj vocab.Type, id *url.URL, now time.Time) vocab.ActivityStreamsTombstone {
    721 	tomb := streams.NewActivityStreamsTombstone()
    722 	// id property
    723 	idProp := streams.NewJSONLDIdProperty()
    724 	idProp.Set(id)
    725 	tomb.SetJSONLDId(idProp)
    726 	// formerType property
    727 	former := streams.NewActivityStreamsFormerTypeProperty()
    728 	tomb.SetActivityStreamsFormerType(former)
    729 	// Populate Former Type
    730 	former.AppendXMLSchemaString(obj.GetTypeName())
    731 	// Copy over the published property if it existed
    732 	if pubber, ok := obj.(publisheder); ok {
    733 		if pub := pubber.GetActivityStreamsPublished(); pub != nil {
    734 			tomb.SetActivityStreamsPublished(pub)
    735 		}
    736 	}
    737 	// Copy over the updated property if it existed
    738 	if upder, ok := obj.(updateder); ok {
    739 		if upd := upder.GetActivityStreamsUpdated(); upd != nil {
    740 			tomb.SetActivityStreamsUpdated(upd)
    741 		}
    742 	}
    743 	// Set deleted time to now.
    744 	deleted := streams.NewActivityStreamsDeletedProperty()
    745 	deleted.Set(now)
    746 	tomb.SetActivityStreamsDeleted(deleted)
    747 	return tomb
    748 }
    749 
    750 // mustHaveActivityActorsMatchObjectActors ensures that the actors on types in
    751 // the 'object' property are all listed in the 'actor' property.
    752 func mustHaveActivityActorsMatchObjectActors(c context.Context,
    753 	actors vocab.ActivityStreamsActorProperty,
    754 	op vocab.ActivityStreamsObjectProperty,
    755 	newTransport func(c context.Context, actorBoxIRI *url.URL, gofedAgent string) (t Transport, err error),
    756 	boxIRI *url.URL,
    757 ) error {
    758 	activityActorMap := make(map[string]bool, actors.Len())
    759 	for iter := actors.Begin(); iter != actors.End(); iter = iter.Next() {
    760 		id, err := ToId(iter)
    761 		if err != nil {
    762 			return err
    763 		}
    764 		activityActorMap[id.String()] = true
    765 	}
    766 	for iter := op.Begin(); iter != op.End(); iter = iter.Next() {
    767 		iri, err := ToId(iter)
    768 		if err != nil {
    769 			return err
    770 		}
    771 		// Attempt to dereference the IRI, regardless whether it is a
    772 		// type or IRI
    773 		tport, err := newTransport(c, boxIRI, goFedUserAgent())
    774 		if err != nil {
    775 			return err
    776 		}
    777 		b, err := tport.Dereference(c, iri)
    778 		if err != nil {
    779 			return err
    780 		}
    781 		var m map[string]interface{}
    782 		if err = json.Unmarshal(b, &m); err != nil {
    783 			return err
    784 		}
    785 		t, err := streams.ToType(c, m)
    786 		if err != nil {
    787 			return err
    788 		}
    789 		ac, ok := t.(actorer)
    790 		if !ok {
    791 			return fmt.Errorf("cannot verify actors: object value has no 'actor' property")
    792 		}
    793 		objActors := ac.GetActivityStreamsActor()
    794 		for iter := objActors.Begin(); iter != objActors.End(); iter = iter.Next() {
    795 			id, err := ToId(iter)
    796 			if err != nil {
    797 				return err
    798 			}
    799 			if !activityActorMap[id.String()] {
    800 				return fmt.Errorf("activity does not have all actors from its object's actors")
    801 			}
    802 		}
    803 	}
    804 	return nil
    805 }
    806 
    807 // add implements the logic of adding object ids to a target Collection or
    808 // OrderedCollection. This logic is shared by both the C2S and S2S protocols.
    809 func add(c context.Context,
    810 	op vocab.ActivityStreamsObjectProperty,
    811 	target vocab.ActivityStreamsTargetProperty,
    812 	db Database,
    813 ) error {
    814 	opIds := make([]*url.URL, 0, op.Len())
    815 	for iter := op.Begin(); iter != op.End(); iter = iter.Next() {
    816 		id, err := ToId(iter)
    817 		if err != nil {
    818 			return err
    819 		}
    820 		opIds = append(opIds, id)
    821 	}
    822 	targetIds := make([]*url.URL, 0, op.Len())
    823 	for iter := target.Begin(); iter != target.End(); iter = iter.Next() {
    824 		id, err := ToId(iter)
    825 		if err != nil {
    826 			return err
    827 		}
    828 		targetIds = append(targetIds, id)
    829 	}
    830 	// Create anonymous loop function to be able to properly scope the defer
    831 	// for the database lock at each iteration.
    832 	loopFn := func(t *url.URL) error {
    833 		unlock, err := db.Lock(c, t)
    834 		if err != nil {
    835 			return err
    836 		}
    837 		defer unlock()
    838 		if owns, err := db.Owns(c, t); err != nil {
    839 			return err
    840 		} else if !owns {
    841 			return nil
    842 		}
    843 		tp, err := db.Get(c, t)
    844 		if err != nil {
    845 			return err
    846 		}
    847 		if streams.IsOrExtendsActivityStreamsOrderedCollection(tp) {
    848 			oi, ok := tp.(orderedItemser)
    849 			if !ok {
    850 				return fmt.Errorf("type extending from OrderedCollection cannot convert to orderedItemser interface")
    851 			}
    852 			oiProp := oi.GetActivityStreamsOrderedItems()
    853 			if oiProp == nil {
    854 				oiProp = streams.NewActivityStreamsOrderedItemsProperty()
    855 				oi.SetActivityStreamsOrderedItems(oiProp)
    856 			}
    857 			for _, objId := range opIds {
    858 				oiProp.AppendIRI(objId)
    859 			}
    860 		} else if streams.IsOrExtendsActivityStreamsCollection(tp) {
    861 			i, ok := tp.(itemser)
    862 			if !ok {
    863 				return fmt.Errorf("type extending from Collection cannot convert to itemser interface")
    864 			}
    865 			iProp := i.GetActivityStreamsItems()
    866 			if iProp == nil {
    867 				iProp = streams.NewActivityStreamsItemsProperty()
    868 				i.SetActivityStreamsItems(iProp)
    869 			}
    870 			for _, objId := range opIds {
    871 				iProp.AppendIRI(objId)
    872 			}
    873 		} else {
    874 			return fmt.Errorf("target in Add is neither a Collection nor an OrderedCollection")
    875 		}
    876 		err = db.Update(c, tp)
    877 		if err != nil {
    878 			return err
    879 		}
    880 		return nil
    881 	}
    882 	for _, t := range targetIds {
    883 		if err := loopFn(t); err != nil {
    884 			return err
    885 		}
    886 	}
    887 	return nil
    888 }
    889 
    890 // remove implements the logic of removing object ids to a target Collection or
    891 // OrderedCollection. This logic is shared by both the C2S and S2S protocols.
    892 func remove(c context.Context,
    893 	op vocab.ActivityStreamsObjectProperty,
    894 	target vocab.ActivityStreamsTargetProperty,
    895 	db Database,
    896 ) error {
    897 	opIds := make(map[string]bool, op.Len())
    898 	for iter := op.Begin(); iter != op.End(); iter = iter.Next() {
    899 		id, err := ToId(iter)
    900 		if err != nil {
    901 			return err
    902 		}
    903 		opIds[id.String()] = true
    904 	}
    905 	targetIds := make([]*url.URL, 0, op.Len())
    906 	for iter := target.Begin(); iter != target.End(); iter = iter.Next() {
    907 		id, err := ToId(iter)
    908 		if err != nil {
    909 			return err
    910 		}
    911 		targetIds = append(targetIds, id)
    912 	}
    913 	// Create anonymous loop function to be able to properly scope the defer
    914 	// for the database lock at each iteration.
    915 	loopFn := func(t *url.URL) error {
    916 		unlock, err := db.Lock(c, t)
    917 		if err != nil {
    918 			return err
    919 		}
    920 		defer unlock()
    921 		if owns, err := db.Owns(c, t); err != nil {
    922 			return err
    923 		} else if !owns {
    924 			return nil
    925 		}
    926 		tp, err := db.Get(c, t)
    927 		if err != nil {
    928 			return err
    929 		}
    930 		if streams.IsOrExtendsActivityStreamsOrderedCollection(tp) {
    931 			oi, ok := tp.(orderedItemser)
    932 			if !ok {
    933 				return fmt.Errorf("type extending from OrderedCollection cannot convert to orderedItemser interface")
    934 			}
    935 			oiProp := oi.GetActivityStreamsOrderedItems()
    936 			if oiProp != nil {
    937 				for i := 0; i < oiProp.Len(); /*Conditional*/ {
    938 					id, err := ToId(oiProp.At(i))
    939 					if err != nil {
    940 						return err
    941 					}
    942 					if opIds[id.String()] {
    943 						oiProp.Remove(i)
    944 					} else {
    945 						i++
    946 					}
    947 				}
    948 			}
    949 		} else if streams.IsOrExtendsActivityStreamsCollection(tp) {
    950 			i, ok := tp.(itemser)
    951 			if !ok {
    952 				return fmt.Errorf("type extending from Collection cannot convert to itemser interface")
    953 			}
    954 			iProp := i.GetActivityStreamsItems()
    955 			if iProp != nil {
    956 				for i := 0; i < iProp.Len(); /*Conditional*/ {
    957 					id, err := ToId(iProp.At(i))
    958 					if err != nil {
    959 						return err
    960 					}
    961 					if opIds[id.String()] {
    962 						iProp.Remove(i)
    963 					} else {
    964 						i++
    965 					}
    966 				}
    967 			}
    968 		} else {
    969 			return fmt.Errorf("target in Remove is neither a Collection nor an OrderedCollection")
    970 		}
    971 		err = db.Update(c, tp)
    972 		if err != nil {
    973 			return err
    974 		}
    975 		return nil
    976 	}
    977 	for _, t := range targetIds {
    978 		if err := loopFn(t); err != nil {
    979 			return err
    980 		}
    981 	}
    982 	return nil
    983 }
    984 
    985 // clearSensitiveFields removes the 'bto' and 'bcc' entries on the given value
    986 // and recursively on every 'object' property value.
    987 func clearSensitiveFields(obj vocab.Type) {
    988 	if t, ok := obj.(btoer); ok {
    989 		t.SetActivityStreamsBto(nil)
    990 	}
    991 	if t, ok := obj.(bccer); ok {
    992 		t.SetActivityStreamsBcc(nil)
    993 	}
    994 	if t, ok := obj.(objecter); ok {
    995 		op := t.GetActivityStreamsObject()
    996 		if op != nil {
    997 			for iter := op.Begin(); iter != op.End(); iter = iter.Next() {
    998 				clearSensitiveFields(iter.GetType())
    999 			}
   1000 		}
   1001 	}
   1002 }
   1003 
   1004 // requestId forms an ActivityPub id based on the HTTP request. Always assumes
   1005 // that the id is HTTPS.
   1006 func requestId(r *http.Request, scheme string) *url.URL {
   1007 	id := r.URL
   1008 	id.Host = r.Host
   1009 	id.Scheme = scheme
   1010 	return id
   1011 }