| 
									
										
										
										
											2023-03-12 16:00:57 +01:00
										 |  |  | // GoToSocial | 
					
						
							|  |  |  | // Copyright (C) GoToSocial Authors admin@gotosocial.org | 
					
						
							|  |  |  | // SPDX-License-Identifier: AGPL-3.0-or-later | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // This program is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  | // it under the terms of the GNU Affero General Public License as published by | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  | // (at your option) any later version. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  | // GNU Affero General Public License for more details. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // You should have received a copy of the GNU Affero General Public License | 
					
						
							|  |  |  | // along with this program.  If not, see <http://www.gnu.org/licenses/>. | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-10 13:32:39 +02:00
										 |  |  | package ap | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2022-11-30 23:13:13 +01:00
										 |  |  | 	"crypto" | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 	"crypto/rsa" | 
					
						
							|  |  |  | 	"crypto/x509" | 
					
						
							|  |  |  | 	"encoding/pem" | 
					
						
							| 
									
										
										
										
											2023-11-08 14:32:17 +00:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"net/url" | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	"strings" | 
					
						
							|  |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-02 12:48:00 +01:00
										 |  |  | 	"codeberg.org/superseriousbusiness/activity/pub" | 
					
						
							|  |  |  | 	"codeberg.org/superseriousbusiness/activity/streams/vocab" | 
					
						
							| 
									
										
										
										
											2023-06-13 16:47:56 +02:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/gtserror" | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" | 
					
						
							| 
									
										
										
										
											2023-07-31 15:47:35 +02:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/text" | 
					
						
							| 
									
										
										
										
											2024-02-27 13:22:05 +01:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/util" | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-04 13:09:42 +01:00
										 |  |  | // ExtractObjects will extract object vocab.Types from given implementing interface. | 
					
						
							|  |  |  | func ExtractObjects(with WithObject) []TypeOrIRI { | 
					
						
							| 
									
										
										
										
											2023-10-03 14:59:30 +01:00
										 |  |  | 	// Extract the attached object (if any). | 
					
						
							| 
									
										
										
										
											2023-10-04 13:09:42 +01:00
										 |  |  | 	objProp := with.GetActivityStreamsObject() | 
					
						
							|  |  |  | 	if objProp == nil { | 
					
						
							| 
									
										
										
										
											2023-10-03 14:59:30 +01:00
										 |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-04 13:09:42 +01:00
										 |  |  | 	// Check for zero len. | 
					
						
							|  |  |  | 	if objProp.Len() == 0 { | 
					
						
							| 
									
										
										
										
											2023-10-03 14:59:30 +01:00
										 |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-04 13:09:42 +01:00
										 |  |  | 	// Accumulate all of the objects into a slice. | 
					
						
							|  |  |  | 	objs := make([]TypeOrIRI, objProp.Len()) | 
					
						
							|  |  |  | 	for i := 0; i < objProp.Len(); i++ { | 
					
						
							|  |  |  | 		objs[i] = objProp.At(i) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return objs | 
					
						
							| 
									
										
										
										
											2023-10-03 14:59:30 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ExtractActivityData will extract the usable data type (e.g. Note, Question, etc) and corresponding JSON, from activity. | 
					
						
							| 
									
										
										
										
											2023-10-04 13:09:42 +01:00
										 |  |  | func ExtractActivityData(activity pub.Activity, rawJSON map[string]any) ([]TypeOrIRI, []any, bool) { | 
					
						
							| 
									
										
										
										
											2023-10-03 14:59:30 +01:00
										 |  |  | 	switch typeName := activity.GetTypeName(); { | 
					
						
							|  |  |  | 	// Activity (has "object"). | 
					
						
							|  |  |  | 	case isActivity(typeName): | 
					
						
							| 
									
										
										
										
											2023-10-04 13:09:42 +01:00
										 |  |  | 		objTypes := ExtractObjects(activity) | 
					
						
							|  |  |  | 		if len(objTypes) == 0 { | 
					
						
							| 
									
										
										
										
											2023-10-03 14:59:30 +01:00
										 |  |  | 			return nil, nil, false | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-10-04 13:09:42 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		var objJSON []any | 
					
						
							|  |  |  | 		switch json := rawJSON["object"].(type) { | 
					
						
							|  |  |  | 		case nil: | 
					
						
							|  |  |  | 			// do nothing | 
					
						
							|  |  |  | 		case map[string]any: | 
					
						
							|  |  |  | 			// Wrap map in slice. | 
					
						
							|  |  |  | 			objJSON = []any{json} | 
					
						
							|  |  |  | 		case []any: | 
					
						
							|  |  |  | 			// Use existing slice. | 
					
						
							|  |  |  | 			objJSON = json | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return objTypes, objJSON, true | 
					
						
							| 
									
										
										
										
											2023-10-03 14:59:30 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// IntransitiveAcitivity (no "object"). | 
					
						
							|  |  |  | 	case isIntransitiveActivity(typeName): | 
					
						
							| 
									
										
										
										
											2023-10-04 13:09:42 +01:00
										 |  |  | 		asTypeOrIRI := _TypeOrIRI{activity} // wrap activity. | 
					
						
							|  |  |  | 		return []TypeOrIRI{&asTypeOrIRI}, []any{rawJSON}, true | 
					
						
							| 
									
										
										
										
											2023-10-03 14:59:30 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Unknown. | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return nil, nil, false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-04 20:21:20 +00:00
										 |  |  | // ExtractAccountables extracts Accountable objects from a slice TypeOrIRI, returning extracted and remaining TypeOrIRIs. | 
					
						
							|  |  |  | func ExtractAccountables(arr []TypeOrIRI) ([]Accountable, []TypeOrIRI) { | 
					
						
							|  |  |  | 	var accounts []Accountable | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i := 0; i < len(arr); i++ { | 
					
						
							|  |  |  | 		elem := arr[i] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if elem.IsIRI() { | 
					
						
							|  |  |  | 			// skip IRIs | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Extract AS vocab type | 
					
						
							|  |  |  | 		// associated with elem. | 
					
						
							|  |  |  | 		t := elem.GetType() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Try cast AS type as Accountable. | 
					
						
							|  |  |  | 		account, ok := ToAccountable(t) | 
					
						
							|  |  |  | 		if !ok { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Add casted accountable type. | 
					
						
							|  |  |  | 		accounts = append(accounts, account) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Drop elem from slice. | 
					
						
							|  |  |  | 		copy(arr[:i], arr[i+1:]) | 
					
						
							|  |  |  | 		arr = arr[:len(arr)-1] | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return accounts, arr | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ExtractStatusables extracts Statusable objects from a slice TypeOrIRI, returning extracted and remaining TypeOrIRIs. | 
					
						
							|  |  |  | func ExtractStatusables(arr []TypeOrIRI) ([]Statusable, []TypeOrIRI) { | 
					
						
							|  |  |  | 	var statuses []Statusable | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i := 0; i < len(arr); i++ { | 
					
						
							|  |  |  | 		elem := arr[i] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if elem.IsIRI() { | 
					
						
							|  |  |  | 			// skip IRIs | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Extract AS vocab type | 
					
						
							|  |  |  | 		// associated with elem. | 
					
						
							|  |  |  | 		t := elem.GetType() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Try cast AS type as Statusable. | 
					
						
							|  |  |  | 		status, ok := ToStatusable(t) | 
					
						
							|  |  |  | 		if !ok { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Add casted Statusable type. | 
					
						
							|  |  |  | 		statuses = append(statuses, status) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Drop elem from slice. | 
					
						
							|  |  |  | 		copy(arr[:i], arr[i+1:]) | 
					
						
							|  |  |  | 		arr = arr[:len(arr)-1] | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return statuses, arr | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ExtractPollOptionables extracts PollOptionable objects from a slice TypeOrIRI, returning extracted and remaining TypeOrIRIs. | 
					
						
							|  |  |  | func ExtractPollOptionables(arr []TypeOrIRI) ([]PollOptionable, []TypeOrIRI) { | 
					
						
							|  |  |  | 	var options []PollOptionable | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i := 0; i < len(arr); i++ { | 
					
						
							|  |  |  | 		elem := arr[i] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if elem.IsIRI() { | 
					
						
							|  |  |  | 			// skip IRIs | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Extract AS vocab type | 
					
						
							|  |  |  | 		// associated with elem. | 
					
						
							|  |  |  | 		t := elem.GetType() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Try cast as PollOptionable. | 
					
						
							|  |  |  | 		option, ok := ToPollOptionable(t) | 
					
						
							|  |  |  | 		if !ok { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Add casted PollOptionable type. | 
					
						
							|  |  |  | 		options = append(options, option) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Drop elem from slice. | 
					
						
							|  |  |  | 		copy(arr[:i], arr[i+1:]) | 
					
						
							|  |  |  | 		arr = arr[:len(arr)-1] | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return options, arr | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | // ExtractPreferredUsername returns a string representation of | 
					
						
							|  |  |  | // an interface's preferredUsername property. Will return an | 
					
						
							|  |  |  | // error if preferredUsername is nil, not a string, or empty. | 
					
						
							| 
									
										
										
										
											2024-08-13 09:01:50 +00:00
										 |  |  | func ExtractPreferredUsername(i WithPreferredUsername) string { | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 	u := i.GetActivityStreamsPreferredUsername() | 
					
						
							|  |  |  | 	if u == nil || !u.IsXMLSchemaString() { | 
					
						
							| 
									
										
										
										
											2024-08-13 09:01:50 +00:00
										 |  |  | 		return "" | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-08-13 09:01:50 +00:00
										 |  |  | 	return u.GetXMLSchemaString() | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | // ExtractName returns the first string representation it | 
					
						
							|  |  |  | // can find of an interface's name property, or an empty | 
					
						
							|  |  |  | // string if this is not found. | 
					
						
							| 
									
										
										
										
											2023-02-02 16:41:02 +01:00
										 |  |  | func ExtractName(i WithName) string { | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 	nameProp := i.GetActivityStreamsName() | 
					
						
							|  |  |  | 	if nameProp == nil { | 
					
						
							| 
									
										
										
										
											2023-02-02 16:41:02 +01:00
										 |  |  | 		return "" | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	for iter := nameProp.Begin(); iter != nameProp.End(); iter = iter.Next() { | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		// Name may be parsed as IRI, depending on | 
					
						
							|  |  |  | 		// how it's formatted, so account for this. | 
					
						
							| 
									
										
										
										
											2023-04-26 17:17:22 +02:00
										 |  |  | 		switch { | 
					
						
							|  |  |  | 		case iter.IsXMLSchemaString(): | 
					
						
							| 
									
										
										
										
											2023-02-02 16:41:02 +01:00
										 |  |  | 			return iter.GetXMLSchemaString() | 
					
						
							| 
									
										
										
										
											2023-04-26 17:17:22 +02:00
										 |  |  | 		case iter.IsIRI(): | 
					
						
							|  |  |  | 			return iter.GetIRI().String() | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-02 16:41:02 +01:00
										 |  |  | 	return "" | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | // ExtractInReplyToURI extracts the first inReplyTo URI | 
					
						
							|  |  |  | // property it can find from an interface. Will return | 
					
						
							|  |  |  | // nil if no valid URI can be found. | 
					
						
							| 
									
										
										
										
											2021-08-10 13:32:39 +02:00
										 |  |  | func ExtractInReplyToURI(i WithInReplyTo) *url.URL { | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	inReplyToProp := i.GetActivityStreamsInReplyTo() | 
					
						
							| 
									
										
										
										
											2021-05-23 18:07:04 +02:00
										 |  |  | 	if inReplyToProp == nil { | 
					
						
							| 
									
										
										
										
											2021-08-10 13:32:39 +02:00
										 |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2021-05-23 18:07:04 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	for iter := inReplyToProp.Begin(); iter != inReplyToProp.End(); iter = iter.Next() { | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		iri, err := pub.ToId(iter) | 
					
						
							|  |  |  | 		if err == nil && iri != nil { | 
					
						
							|  |  |  | 			// Found one we can use. | 
					
						
							|  |  |  | 			return iri | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-10 13:32:39 +02:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | // ExtractItemsURIs extracts each URI it can | 
					
						
							|  |  |  | // find for an item from the provided WithItems. | 
					
						
							|  |  |  | func ExtractItemsURIs(i WithItems) []*url.URL { | 
					
						
							|  |  |  | 	itemsProp := i.GetActivityStreamsItems() | 
					
						
							|  |  |  | 	if itemsProp == nil { | 
					
						
							|  |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2021-08-10 13:32:39 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 	uris := make([]*url.URL, 0, itemsProp.Len()) | 
					
						
							|  |  |  | 	for iter := itemsProp.Begin(); iter != itemsProp.End(); iter = iter.Next() { | 
					
						
							|  |  |  | 		uri, err := pub.ToId(iter) | 
					
						
							|  |  |  | 		if err == nil { | 
					
						
							|  |  |  | 			// Found one we can use. | 
					
						
							|  |  |  | 			uris = append(uris, uri) | 
					
						
							| 
									
										
										
										
											2021-08-10 13:32:39 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return uris | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | // ExtractToURIs returns a slice of URIs | 
					
						
							|  |  |  | // that the given WithTo addresses as To. | 
					
						
							|  |  |  | func ExtractToURIs(i WithTo) []*url.URL { | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	toProp := i.GetActivityStreamsTo() | 
					
						
							| 
									
										
										
										
											2021-05-23 18:07:04 +02:00
										 |  |  | 	if toProp == nil { | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2021-05-23 18:07:04 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	uris := make([]*url.URL, 0, toProp.Len()) | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	for iter := toProp.Begin(); iter != toProp.End(); iter = iter.Next() { | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		uri, err := pub.ToId(iter) | 
					
						
							|  |  |  | 		if err == nil { | 
					
						
							|  |  |  | 			// Found one we can use. | 
					
						
							|  |  |  | 			uris = append(uris, uri) | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return uris | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | // ExtractCcURIs returns a slice of URIs | 
					
						
							|  |  |  | // that the given WithCC addresses as Cc. | 
					
						
							| 
									
										
										
										
											2023-11-04 20:21:20 +00:00
										 |  |  | func ExtractCcURIs(i WithCc) []*url.URL { | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	ccProp := i.GetActivityStreamsCc() | 
					
						
							| 
									
										
										
										
											2021-05-23 18:07:04 +02:00
										 |  |  | 	if ccProp == nil { | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2021-05-23 18:07:04 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	urls := make([]*url.URL, 0, ccProp.Len()) | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	for iter := ccProp.Begin(); iter != ccProp.End(); iter = iter.Next() { | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		uri, err := pub.ToId(iter) | 
					
						
							|  |  |  | 		if err == nil { | 
					
						
							|  |  |  | 			// Found one we can use. | 
					
						
							|  |  |  | 			urls = append(urls, uri) | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return urls | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | // ExtractAttributedToURI returns the first URI it can find in the | 
					
						
							|  |  |  | // given WithAttributedTo, or an error if no URI can be found. | 
					
						
							|  |  |  | func ExtractAttributedToURI(i WithAttributedTo) (*url.URL, error) { | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	attributedToProp := i.GetActivityStreamsAttributedTo() | 
					
						
							| 
									
										
										
										
											2021-05-23 18:07:04 +02:00
										 |  |  | 	if attributedToProp == nil { | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		return nil, gtserror.New("attributedToProp was nil") | 
					
						
							| 
									
										
										
										
											2021-05-23 18:07:04 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	for iter := attributedToProp.Begin(); iter != attributedToProp.End(); iter = iter.Next() { | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		id, err := pub.ToId(iter) | 
					
						
							|  |  |  | 		if err == nil { | 
					
						
							|  |  |  | 			return id, nil | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return nil, gtserror.New("couldn't find iri for attributed to") | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | // ExtractIconURI extracts the first URI it can find from | 
					
						
							|  |  |  | // the given WithIcon which links to a supported image file. | 
					
						
							|  |  |  | // Input will look something like this: | 
					
						
							| 
									
										
										
										
											2022-09-28 18:30:40 +01:00
										 |  |  | // | 
					
						
							|  |  |  | //	"icon": { | 
					
						
							|  |  |  | //	  "mediaType": "image/jpeg", | 
					
						
							|  |  |  | //	  "type": "Image", | 
					
						
							|  |  |  | //	  "url": "http://example.org/path/to/some/file.jpeg" | 
					
						
							|  |  |  | //	}, | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | // | 
					
						
							|  |  |  | // If no valid URI can be found, this will return an error. | 
					
						
							|  |  |  | func ExtractIconURI(i WithIcon) (*url.URL, error) { | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 	iconProp := i.GetActivityStreamsIcon() | 
					
						
							|  |  |  | 	if iconProp == nil { | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		return nil, gtserror.New("icon property was nil") | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 	// Icon can potentially contain multiple entries, | 
					
						
							|  |  |  | 	// so we iterate through all of them here in order | 
					
						
							|  |  |  | 	// to find the first one that meets these criteria: | 
					
						
							|  |  |  | 	// | 
					
						
							|  |  |  | 	//   1. Is an image. | 
					
						
							|  |  |  | 	//   2. Has a URL that we can use to derefereince it. | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	for iter := iconProp.Begin(); iter != iconProp.End(); iter = iter.Next() { | 
					
						
							|  |  |  | 		if !iter.IsActivityStreamsImage() { | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		image := iter.GetActivityStreamsImage() | 
					
						
							|  |  |  | 		if image == nil { | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		imageURL, err := ExtractURL(image) | 
					
						
							|  |  |  | 		if err == nil && imageURL != nil { | 
					
						
							|  |  |  | 			return imageURL, nil | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return nil, gtserror.New("could not extract valid image URI from icon") | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | // ExtractImageURI extracts the first URI it can find from | 
					
						
							|  |  |  | // the given WithImage which links to a supported image file. | 
					
						
							|  |  |  | // Input will look something like this: | 
					
						
							| 
									
										
										
										
											2022-09-28 18:30:40 +01:00
										 |  |  | // | 
					
						
							|  |  |  | //	"image": { | 
					
						
							|  |  |  | //	  "mediaType": "image/jpeg", | 
					
						
							|  |  |  | //	  "type": "Image", | 
					
						
							|  |  |  | //	  "url": "http://example.org/path/to/some/file.jpeg" | 
					
						
							|  |  |  | //	}, | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | // | 
					
						
							|  |  |  | // If no valid URI can be found, this will return an error. | 
					
						
							|  |  |  | func ExtractImageURI(i WithImage) (*url.URL, error) { | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 	imageProp := i.GetActivityStreamsImage() | 
					
						
							|  |  |  | 	if imageProp == nil { | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		return nil, gtserror.New("image property was nil") | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 	// Image can potentially contain multiple entries, | 
					
						
							|  |  |  | 	// so we iterate through all of them here in order | 
					
						
							|  |  |  | 	// to find the first one that meets these criteria: | 
					
						
							|  |  |  | 	// | 
					
						
							|  |  |  | 	//   1. Is an image. | 
					
						
							|  |  |  | 	//   2. Has a URL that we can use to derefereince it. | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	for iter := imageProp.Begin(); iter != imageProp.End(); iter = iter.Next() { | 
					
						
							|  |  |  | 		if !iter.IsActivityStreamsImage() { | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		image := iter.GetActivityStreamsImage() | 
					
						
							|  |  |  | 		if image == nil { | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		imageURL, err := ExtractURL(image) | 
					
						
							|  |  |  | 		if err == nil && imageURL != nil { | 
					
						
							|  |  |  | 			return imageURL, nil | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return nil, gtserror.New("could not extract valid image URI from image") | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | // ExtractSummary extracts the summary/content warning of | 
					
						
							|  |  |  | // the given WithSummary interface. Will return an empty | 
					
						
							|  |  |  | // string if no summary/content warning was present. | 
					
						
							| 
									
										
										
										
											2023-02-02 16:41:02 +01:00
										 |  |  | func ExtractSummary(i WithSummary) string { | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 	summaryProp := i.GetActivityStreamsSummary() | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 	if summaryProp == nil { | 
					
						
							| 
									
										
										
										
											2023-02-02 16:41:02 +01:00
										 |  |  | 		return "" | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	for iter := summaryProp.Begin(); iter != summaryProp.End(); iter = iter.Next() { | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		// Summary may be parsed as IRI, depending on | 
					
						
							|  |  |  | 		// how it's formatted, so account for this. | 
					
						
							| 
									
										
										
										
											2022-04-26 10:47:21 +02:00
										 |  |  | 		switch { | 
					
						
							|  |  |  | 		case iter.IsXMLSchemaString(): | 
					
						
							| 
									
										
										
										
											2023-02-02 16:41:02 +01:00
										 |  |  | 			return iter.GetXMLSchemaString() | 
					
						
							| 
									
										
										
										
											2023-04-26 17:17:22 +02:00
										 |  |  | 		case iter.IsIRI(): | 
					
						
							|  |  |  | 			return iter.GetIRI().String() | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-02 16:41:02 +01:00
										 |  |  | 	return "" | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | // ExtractFields extracts property/value fields from the given | 
					
						
							|  |  |  | // WithAttachment interface. Will return an empty slice if no | 
					
						
							|  |  |  | // property/value fields can be found. Attachments that are not | 
					
						
							|  |  |  | // (well-formed) PropertyValues will be ignored. | 
					
						
							| 
									
										
										
										
											2023-05-09 12:16:10 +02:00
										 |  |  | func ExtractFields(i WithAttachment) []*gtsmodel.Field { | 
					
						
							|  |  |  | 	attachmentProp := i.GetActivityStreamsAttachment() | 
					
						
							|  |  |  | 	if attachmentProp == nil { | 
					
						
							|  |  |  | 		// Nothing to do. | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	l := attachmentProp.Len() | 
					
						
							|  |  |  | 	if l == 0 { | 
					
						
							|  |  |  | 		// Nothing to do. | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	fields := make([]*gtsmodel.Field, 0, l) | 
					
						
							|  |  |  | 	for iter := attachmentProp.Begin(); iter != attachmentProp.End(); iter = iter.Next() { | 
					
						
							|  |  |  | 		if !iter.IsSchemaPropertyValue() { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		propertyValue := iter.GetSchemaPropertyValue() | 
					
						
							|  |  |  | 		if propertyValue == nil { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		nameProp := propertyValue.GetActivityStreamsName() | 
					
						
							|  |  |  | 		if nameProp == nil || nameProp.Len() != 1 { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		name := nameProp.At(0).GetXMLSchemaString() | 
					
						
							|  |  |  | 		if name == "" { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		valueProp := propertyValue.GetSchemaValue() | 
					
						
							|  |  |  | 		if valueProp == nil || !valueProp.IsXMLSchemaString() { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		value := valueProp.Get() | 
					
						
							|  |  |  | 		if value == "" { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		fields = append(fields, >smodel.Field{ | 
					
						
							|  |  |  | 			Name:  name, | 
					
						
							|  |  |  | 			Value: value, | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return fields | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | // ExtractURL extracts the first URI it can find from the | 
					
						
							|  |  |  | // given WithURL interface, or an error if no URL was set. | 
					
						
							|  |  |  | // The ID of a type will not work, this function wants a URI | 
					
						
							|  |  |  | // specifically. | 
					
						
							| 
									
										
										
										
											2021-08-10 13:32:39 +02:00
										 |  |  | func ExtractURL(i WithURL) (*url.URL, error) { | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 	urlProp := i.GetActivityStreamsUrl() | 
					
						
							|  |  |  | 	if urlProp == nil { | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		return nil, gtserror.New("url property was nil") | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	for iter := urlProp.Begin(); iter != urlProp.End(); iter = iter.Next() { | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		if !iter.IsIRI() { | 
					
						
							|  |  |  | 			continue | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// Found it. | 
					
						
							|  |  |  | 		return iter.GetIRI(), nil | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 	return nil, gtserror.New("no valid URL property found") | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 12:30:10 +02:00
										 |  |  | // ExtractPubKeyFromActor extracts the public key, public key ID, and public | 
					
						
							| 
									
										
										
										
											2023-06-13 16:47:56 +02:00
										 |  |  | // key owner ID from an interface, or an error if something goes wrong. | 
					
						
							| 
									
										
										
										
											2024-04-02 12:30:10 +02:00
										 |  |  | func ExtractPubKeyFromActor(i WithPublicKey) ( | 
					
						
							| 
									
										
										
										
											2023-06-13 16:47:56 +02:00
										 |  |  | 	*rsa.PublicKey, // pubkey | 
					
						
							|  |  |  | 	*url.URL, // pubkey ID | 
					
						
							|  |  |  | 	*url.URL, // pubkey owner | 
					
						
							|  |  |  | 	error, | 
					
						
							|  |  |  | ) { | 
					
						
							|  |  |  | 	pubKeyProp := i.GetW3IDSecurityV1PublicKey() | 
					
						
							|  |  |  | 	if pubKeyProp == nil { | 
					
						
							|  |  |  | 		return nil, nil, nil, gtserror.New("public key property was nil") | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 12:30:10 +02:00
										 |  |  | 	// Take the first public key we can find. | 
					
						
							| 
									
										
										
										
											2023-06-13 16:47:56 +02:00
										 |  |  | 	for iter := pubKeyProp.Begin(); iter != pubKeyProp.End(); iter = iter.Next() { | 
					
						
							|  |  |  | 		if !iter.IsW3IDSecurityV1PublicKey() { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 		pkey := iter.Get() | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 		if pkey == nil { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 12:30:10 +02:00
										 |  |  | 		return ExtractPubKeyFromKey(pkey) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-06-13 16:47:56 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 12:30:10 +02:00
										 |  |  | 	return nil, nil, nil, gtserror.New("couldn't find valid public key") | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 12:30:10 +02:00
										 |  |  | // ExtractPubKeyFromActor extracts the public key, public key ID, and public | 
					
						
							|  |  |  | // key owner ID from an interface, or an error if something goes wrong. | 
					
						
							|  |  |  | func ExtractPubKeyFromKey(pkey vocab.W3IDSecurityV1PublicKey) ( | 
					
						
							|  |  |  | 	*rsa.PublicKey, // pubkey | 
					
						
							|  |  |  | 	*url.URL, // pubkey ID | 
					
						
							|  |  |  | 	*url.URL, // pubkey owner | 
					
						
							|  |  |  | 	error, | 
					
						
							|  |  |  | ) { | 
					
						
							|  |  |  | 	pubKeyID, err := pub.GetId(pkey) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, nil, nil, errors.New("no id set on public key") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 12:30:10 +02:00
										 |  |  | 	pubKeyOwnerProp := pkey.GetW3IDSecurityV1Owner() | 
					
						
							|  |  |  | 	if pubKeyOwnerProp == nil { | 
					
						
							|  |  |  | 		return nil, nil, nil, errors.New("nil pubKeyOwnerProp") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 12:30:10 +02:00
										 |  |  | 	pubKeyOwner := pubKeyOwnerProp.GetIRI() | 
					
						
							|  |  |  | 	if pubKeyOwner == nil { | 
					
						
							|  |  |  | 		return nil, nil, nil, errors.New("nil iri on pubKeyOwnerProp") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 12:30:10 +02:00
										 |  |  | 	pubKeyPemProp := pkey.GetW3IDSecurityV1PublicKeyPem() | 
					
						
							|  |  |  | 	if pubKeyPemProp == nil { | 
					
						
							|  |  |  | 		return nil, nil, nil, errors.New("nil pubKeyPemProp") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-06-13 16:47:56 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 12:30:10 +02:00
										 |  |  | 	pkeyPem := pubKeyPemProp.Get() | 
					
						
							|  |  |  | 	if pkeyPem == "" { | 
					
						
							|  |  |  | 		return nil, nil, nil, errors.New("empty pubKeyPemProp") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-06-13 16:47:56 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 12:30:10 +02:00
										 |  |  | 	block, _ := pem.Decode([]byte(pkeyPem)) | 
					
						
							|  |  |  | 	if block == nil { | 
					
						
							|  |  |  | 		return nil, nil, nil, errors.New("nil pubKeyPem") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-06-13 16:47:56 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 12:30:10 +02:00
										 |  |  | 	var p crypto.PublicKey | 
					
						
							|  |  |  | 	switch block.Type { | 
					
						
							|  |  |  | 	case "PUBLIC KEY": | 
					
						
							|  |  |  | 		p, err = x509.ParsePKIXPublicKey(block.Bytes) | 
					
						
							|  |  |  | 	case "RSA PUBLIC KEY": | 
					
						
							|  |  |  | 		p, err = x509.ParsePKCS1PublicKey(block.Bytes) | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		err = fmt.Errorf("unknown block type: %q", block.Type) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		err = fmt.Errorf("could not parse public key from block bytes: %w", err) | 
					
						
							|  |  |  | 		return nil, nil, nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if p == nil { | 
					
						
							|  |  |  | 		return nil, nil, nil, fmt.Errorf("returned public key was empty") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-06-13 16:47:56 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 12:30:10 +02:00
										 |  |  | 	pubKey, ok := p.(*rsa.PublicKey) | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							|  |  |  | 		return nil, nil, nil, fmt.Errorf("could not type pubKey to *rsa.PublicKey") | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-06-13 16:47:56 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 12:30:10 +02:00
										 |  |  | 	return pubKey, pubKeyID, pubKeyOwner, nil | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-21 15:13:30 +01:00
										 |  |  | // ExtractContent returns an intermediary representation of | 
					
						
							|  |  |  | // the given interface's Content and/or ContentMap property. | 
					
						
							|  |  |  | func ExtractContent(i WithContent) gtsmodel.Content { | 
					
						
							|  |  |  | 	content := gtsmodel.Content{} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	contentProp := i.GetActivityStreamsContent() | 
					
						
							|  |  |  | 	if contentProp == nil { | 
					
						
							|  |  |  | 		// No content at all. | 
					
						
							|  |  |  | 		return content | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-05-23 17:12:46 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-21 15:13:30 +01:00
										 |  |  | 	for iter := contentProp.Begin(); iter != contentProp.End(); iter = iter.Next() { | 
					
						
							| 
									
										
										
										
											2023-04-26 17:17:22 +02:00
										 |  |  | 		switch { | 
					
						
							| 
									
										
										
										
											2023-11-21 15:13:30 +01:00
										 |  |  | 		case iter.IsRDFLangString() && | 
					
						
							|  |  |  | 			len(content.ContentMap) == 0: | 
					
						
							|  |  |  | 			content.ContentMap = iter.GetRDFLangString() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		case iter.IsXMLSchemaString() && | 
					
						
							|  |  |  | 			content.Content == "": | 
					
						
							|  |  |  | 			content.Content = iter.GetXMLSchemaString() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		case iter.IsIRI() && | 
					
						
							|  |  |  | 			content.Content == "": | 
					
						
							|  |  |  | 			content.Content = iter.GetIRI().String() | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-05-23 17:12:46 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-21 15:13:30 +01:00
										 |  |  | 	return content | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-03 14:59:30 +01:00
										 |  |  | // ExtractAttachments attempts to extract barebones MediaAttachment objects from given AS interface type. | 
					
						
							|  |  |  | func ExtractAttachments(i WithAttachment) ([]*gtsmodel.MediaAttachment, error) { | 
					
						
							|  |  |  | 	attachmentProp := i.GetActivityStreamsAttachment() | 
					
						
							|  |  |  | 	if attachmentProp == nil { | 
					
						
							|  |  |  | 		return nil, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var errs gtserror.MultiError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	attachments := make([]*gtsmodel.MediaAttachment, 0, attachmentProp.Len()) | 
					
						
							|  |  |  | 	for iter := attachmentProp.Begin(); iter != attachmentProp.End(); iter = iter.Next() { | 
					
						
							|  |  |  | 		t := iter.GetType() | 
					
						
							|  |  |  | 		if t == nil { | 
					
						
							|  |  |  | 			errs.Appendf("nil attachment type") | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		attachmentable, ok := t.(Attachmentable) | 
					
						
							|  |  |  | 		if !ok { | 
					
						
							|  |  |  | 			errs.Appendf("incorrect attachment type: %T", t) | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		attachment, err := ExtractAttachment(attachmentable) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			errs.Appendf("error extracting attachment: %w", err) | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		attachments = append(attachments, attachment) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return attachments, errs.Combine() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | // ExtractAttachment extracts a minimal gtsmodel.Attachment | 
					
						
							|  |  |  | // (just remote URL, description, and blurhash) from the given | 
					
						
							|  |  |  | // Attachmentable interface, or an error if no remote URL is set. | 
					
						
							| 
									
										
										
										
											2021-08-10 13:32:39 +02:00
										 |  |  | func ExtractAttachment(i Attachmentable) (*gtsmodel.MediaAttachment, error) { | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 	// Get the URL for the attachment file. | 
					
						
							|  |  |  | 	// If no URL is set, we can't do anything. | 
					
						
							|  |  |  | 	remoteURL, err := ExtractURL(i) | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		return nil, gtserror.Newf("error extracting attachment URL: %w", err) | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-01-09 18:41:22 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 	return >smodel.MediaAttachment{ | 
					
						
							|  |  |  | 		RemoteURL:   remoteURL.String(), | 
					
						
							| 
									
										
										
										
											2023-10-26 11:59:10 +02:00
										 |  |  | 		Description: ExtractDescription(i), | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		Blurhash:    ExtractBlurhash(i), | 
					
						
							|  |  |  | 		Processing:  gtsmodel.ProcessingStatusReceived, | 
					
						
							|  |  |  | 	}, nil | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-26 11:59:10 +02:00
										 |  |  | // ExtractDescription extracts the image description | 
					
						
							|  |  |  | // of an attachmentable, if present. Will try the | 
					
						
							|  |  |  | // 'summary' prop first, then fall back to 'name'. | 
					
						
							|  |  |  | func ExtractDescription(i Attachmentable) string { | 
					
						
							|  |  |  | 	if summary := ExtractSummary(i); summary != "" { | 
					
						
							|  |  |  | 		return summary | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ExtractName(i) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | // ExtractBlurhash extracts the blurhash string value | 
					
						
							|  |  |  | // from the given WithBlurhash interface, or returns | 
					
						
							|  |  |  | // an empty string if nothing is found. | 
					
						
							| 
									
										
										
										
											2022-01-09 18:41:22 +01:00
										 |  |  | func ExtractBlurhash(i WithBlurhash) string { | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 	blurhashProp := i.GetTootBlurhash() | 
					
						
							|  |  |  | 	if blurhashProp == nil { | 
					
						
							| 
									
										
										
										
											2022-01-09 18:41:22 +01:00
										 |  |  | 		return "" | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return blurhashProp.Get() | 
					
						
							| 
									
										
										
										
											2022-01-09 18:41:22 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | // ExtractHashtags extracts a slice of minimal gtsmodel.Tags | 
					
						
							|  |  |  | // from a WithTag. If an entry in the WithTag is not a hashtag, | 
					
						
							| 
									
										
										
										
											2023-07-31 15:47:35 +02:00
										 |  |  | // or has a name that cannot be normalized, it will be ignored. | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | // | 
					
						
							|  |  |  | // TODO: find a better heuristic for determining if something | 
					
						
							|  |  |  | // is a hashtag or not, since looking for type name "Hashtag" | 
					
						
							|  |  |  | // is non-normative. Perhaps look for things that are either | 
					
						
							|  |  |  | // type "Hashtag" or have no type name set at all? | 
					
						
							| 
									
										
										
										
											2021-08-10 13:32:39 +02:00
										 |  |  | func ExtractHashtags(i WithTag) ([]*gtsmodel.Tag, error) { | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	tagsProp := i.GetActivityStreamsTag() | 
					
						
							| 
									
										
										
										
											2021-05-23 18:07:04 +02:00
										 |  |  | 	if tagsProp == nil { | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		return nil, nil | 
					
						
							| 
									
										
										
										
											2021-05-23 18:07:04 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	var ( | 
					
						
							|  |  |  | 		l    = tagsProp.Len() | 
					
						
							|  |  |  | 		tags = make([]*gtsmodel.Tag, 0, l) | 
					
						
							|  |  |  | 		keys = make(map[string]any, l) // Use map to dedupe items. | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	for iter := tagsProp.Begin(); iter != tagsProp.End(); iter = iter.Next() { | 
					
						
							|  |  |  | 		t := iter.GetType() | 
					
						
							|  |  |  | 		if t == nil { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		if t.GetTypeName() != TagHashtag { | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		hashtaggable, ok := t.(Hashtaggable) | 
					
						
							|  |  |  | 		if !ok { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-31 15:47:35 +02:00
										 |  |  | 		tag, err := extractHashtag(hashtaggable) | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-31 15:47:35 +02:00
										 |  |  | 		// "Normalize" this tag by combining diacritics + | 
					
						
							|  |  |  | 		// unicode chars. If this returns false, it means | 
					
						
							|  |  |  | 		// we couldn't normalize it well enough to make it | 
					
						
							|  |  |  | 		// valid on our instance, so just ignore it. | 
					
						
							|  |  |  | 		normalized, ok := text.NormalizeHashtag(tag.Name) | 
					
						
							|  |  |  | 		if !ok { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// We store tag names lowercased, might | 
					
						
							|  |  |  | 		// as well change case here already. | 
					
						
							|  |  |  | 		tag.Name = strings.ToLower(normalized) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		// Only append this tag if we haven't | 
					
						
							|  |  |  | 		// seen it already, to avoid duplicates | 
					
						
							|  |  |  | 		// in the slice. | 
					
						
							| 
									
										
										
										
											2023-07-31 15:47:35 +02:00
										 |  |  | 		if _, set := keys[tag.Name]; !set { | 
					
						
							|  |  |  | 			keys[tag.Name] = nil // Value doesn't matter. | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 			tags = append(tags, tag) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	return tags, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-31 15:47:35 +02:00
										 |  |  | // extractHashtag extracts a minimal gtsmodel.Tag from the given | 
					
						
							|  |  |  | // Hashtaggable, without yet doing any normalization on it. | 
					
						
							|  |  |  | func extractHashtag(i Hashtaggable) (*gtsmodel.Tag, error) { | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 	// Extract name for the tag; trim leading hash | 
					
						
							|  |  |  | 	// character, so '#example' becomes 'example'. | 
					
						
							| 
									
										
										
										
											2023-02-02 16:41:02 +01:00
										 |  |  | 	name := ExtractName(i) | 
					
						
							|  |  |  | 	if name == "" { | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		return nil, gtserror.New("name prop empty") | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 	tagName := strings.TrimPrefix(name, "#") | 
					
						
							| 
									
										
										
										
											2023-02-02 16:41:02 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-27 13:22:05 +01:00
										 |  |  | 	// Extract href for the tag, if set. | 
					
						
							|  |  |  | 	// | 
					
						
							|  |  |  | 	// Fine if not, it's only used for spam | 
					
						
							|  |  |  | 	// checking anyway so not critical. | 
					
						
							|  |  |  | 	var href string | 
					
						
							|  |  |  | 	hrefProp := i.GetActivityStreamsHref() | 
					
						
							|  |  |  | 	if hrefProp != nil && hrefProp.IsIRI() { | 
					
						
							|  |  |  | 		href = hrefProp.GetIRI().String() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 	return >smodel.Tag{ | 
					
						
							| 
									
										
										
										
											2023-07-31 15:47:35 +02:00
										 |  |  | 		Name:     tagName, | 
					
						
							| 
									
										
										
										
											2024-02-27 13:22:05 +01:00
										 |  |  | 		Useable:  util.Ptr(true), // Assume true by default. | 
					
						
							|  |  |  | 		Listable: util.Ptr(true), // Assume true by default. | 
					
						
							|  |  |  | 		Href:     href, | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 	}, nil | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | // ExtractEmojis extracts a slice of minimal gtsmodel.Emojis | 
					
						
							|  |  |  | // from a WithTag. If an entry in the WithTag is not an emoji, | 
					
						
							|  |  |  | // it will be quietly ignored. | 
					
						
							| 
									
										
										
										
											2025-01-28 13:32:37 +01:00
										 |  |  | func ExtractEmojis(i WithTag, host string) ([]*gtsmodel.Emoji, error) { | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	tagsProp := i.GetActivityStreamsTag() | 
					
						
							| 
									
										
										
										
											2021-05-23 18:07:04 +02:00
										 |  |  | 	if tagsProp == nil { | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		return nil, nil | 
					
						
							| 
									
										
										
										
											2021-05-23 18:07:04 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 	var ( | 
					
						
							|  |  |  | 		l      = tagsProp.Len() | 
					
						
							|  |  |  | 		emojis = make([]*gtsmodel.Emoji, 0, l) | 
					
						
							|  |  |  | 		keys   = make(map[string]any, l) // Use map to dedupe items. | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for iter := tagsProp.Begin(); iter != tagsProp.End(); iter = iter.Next() { | 
					
						
							|  |  |  | 		if !iter.IsTootEmoji() { | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		tootEmoji := iter.GetTootEmoji() | 
					
						
							|  |  |  | 		if tootEmoji == nil { | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-28 13:32:37 +01:00
										 |  |  | 		emoji, err := ExtractEmoji(tootEmoji, host) | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 			return nil, err | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		// Only append this emoji if we haven't | 
					
						
							|  |  |  | 		// seen it already, to avoid duplicates | 
					
						
							|  |  |  | 		// in the slice. | 
					
						
							|  |  |  | 		if _, set := keys[emoji.URI]; !set { | 
					
						
							|  |  |  | 			keys[emoji.URI] = nil // Value doesn't matter. | 
					
						
							|  |  |  | 			emojis = append(emojis, emoji) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	return emojis, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-28 13:32:37 +01:00
										 |  |  | // ExtractEmoji extracts a minimal gtsmodel.Emoji from | 
					
						
							|  |  |  | // the given Emojiable. The host (eg., "example.org") | 
					
						
							|  |  |  | // of the emoji should be passed in as well, so that a | 
					
						
							|  |  |  | // dummy URI for the emoji can be constructed in case | 
					
						
							|  |  |  | // there's no id property or id property is null. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // https://github.com/superseriousbusiness/gotosocial/issues/3384) | 
					
						
							|  |  |  | func ExtractEmoji( | 
					
						
							|  |  |  | 	e Emojiable, | 
					
						
							|  |  |  | 	host string, | 
					
						
							|  |  |  | ) (*gtsmodel.Emoji, error) { | 
					
						
							|  |  |  | 	// Extract emoji name, | 
					
						
							|  |  |  | 	// eg., ":some_emoji". | 
					
						
							|  |  |  | 	name := ExtractName(e) | 
					
						
							| 
									
										
										
										
											2023-02-02 16:41:02 +01:00
										 |  |  | 	if name == "" { | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		return nil, gtserror.New("name prop empty") | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2025-01-28 13:32:37 +01:00
										 |  |  | 	name = strings.TrimSpace(name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Derive shortcode from | 
					
						
							|  |  |  | 	// name, eg., "some_emoji". | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 	shortcode := strings.Trim(name, ":") | 
					
						
							| 
									
										
										
										
											2025-01-28 13:32:37 +01:00
										 |  |  | 	shortcode = strings.TrimSpace(shortcode) | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-28 13:32:37 +01:00
										 |  |  | 	// Extract emoji image | 
					
						
							|  |  |  | 	// URL from Icon property. | 
					
						
							|  |  |  | 	imageRemoteURL, err := ExtractIconURI(e) | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		return nil, gtserror.New("no url for emoji image") | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 	imageRemoteURLStr := imageRemoteURL.String() | 
					
						
							| 
									
										
										
										
											2022-08-15 12:35:05 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-28 13:32:37 +01:00
										 |  |  | 	// Use AP ID as emoji URI, or fall | 
					
						
							|  |  |  | 	// back to dummy URI if not present. | 
					
						
							|  |  |  | 	uri := GetJSONLDId(e) | 
					
						
							|  |  |  | 	if uri == nil { | 
					
						
							|  |  |  | 		// No ID was set, | 
					
						
							|  |  |  | 		// construct dummy. | 
					
						
							|  |  |  | 		uri, err = url.Parse( | 
					
						
							|  |  |  | 			// eg., https://example.org/dummy_emoji_path?shortcode=some_emoji | 
					
						
							|  |  |  | 			"https://" + host + "/dummy_emoji_path?shortcode=" + url.QueryEscape(shortcode), | 
					
						
							|  |  |  | 		) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, gtserror.Newf("error constructing dummy path: %w", err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 	return >smodel.Emoji{ | 
					
						
							| 
									
										
										
										
											2025-01-28 13:32:37 +01:00
										 |  |  | 		UpdatedAt:       GetUpdated(e), | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		Shortcode:       shortcode, | 
					
						
							| 
									
										
										
										
											2025-01-28 13:32:37 +01:00
										 |  |  | 		Domain:          host, | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		ImageRemoteURL:  imageRemoteURLStr, | 
					
						
							|  |  |  | 		URI:             uri.String(), | 
					
						
							|  |  |  | 		Disabled:        new(bool), // Assume false by default. | 
					
						
							|  |  |  | 		VisibleInPicker: new(bool), // Assume false by default. | 
					
						
							|  |  |  | 	}, nil | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | // ExtractMentions extracts a slice of minimal gtsmodel.Mentions | 
					
						
							|  |  |  | // from a WithTag. If an entry in the WithTag is not a mention, | 
					
						
							|  |  |  | // it will be quietly ignored. | 
					
						
							| 
									
										
										
										
											2021-08-10 13:32:39 +02:00
										 |  |  | func ExtractMentions(i WithTag) ([]*gtsmodel.Mention, error) { | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	tagsProp := i.GetActivityStreamsTag() | 
					
						
							| 
									
										
										
										
											2021-05-23 18:07:04 +02:00
										 |  |  | 	if tagsProp == nil { | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		return nil, nil | 
					
						
							| 
									
										
										
										
											2021-05-23 18:07:04 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	var ( | 
					
						
							|  |  |  | 		l        = tagsProp.Len() | 
					
						
							|  |  |  | 		mentions = make([]*gtsmodel.Mention, 0, l) | 
					
						
							|  |  |  | 		keys     = make(map[string]any, l) // Use map to dedupe items. | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	for iter := tagsProp.Begin(); iter != tagsProp.End(); iter = iter.Next() { | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		if !iter.IsActivityStreamsMention() { | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		asMention := iter.GetActivityStreamsMention() | 
					
						
							|  |  |  | 		if asMention == nil { | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		mention, err := ExtractMention(asMention) | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2021-08-29 12:03:08 +02:00
										 |  |  | 			return nil, err | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		// Only append this mention if we haven't | 
					
						
							|  |  |  | 		// seen it already, to avoid duplicates | 
					
						
							|  |  |  | 		// in the slice. | 
					
						
							|  |  |  | 		if _, set := keys[mention.TargetAccountURI]; !set { | 
					
						
							|  |  |  | 			keys[mention.TargetAccountURI] = nil // Value doesn't matter. | 
					
						
							|  |  |  | 			mentions = append(mentions, mention) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	return mentions, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | // ExtractMention extracts a minimal gtsmodel.Mention from a Mentionable. | 
					
						
							| 
									
										
										
										
											2021-08-10 13:32:39 +02:00
										 |  |  | func ExtractMention(i Mentionable) (*gtsmodel.Mention, error) { | 
					
						
							| 
									
										
										
										
											2023-10-31 12:05:17 +01:00
										 |  |  | 	// See if a name has been set in the | 
					
						
							|  |  |  | 	// format `@someone@example.org`. | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 	nameString := ExtractName(i) | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 	// The href prop should be the AP URI | 
					
						
							| 
									
										
										
										
											2023-10-31 12:05:17 +01:00
										 |  |  | 	// of the target account; it could also | 
					
						
							|  |  |  | 	// be the URL, but we'll check this later. | 
					
						
							|  |  |  | 	var href string | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	hrefProp := i.GetActivityStreamsHref() | 
					
						
							| 
									
										
										
										
											2023-10-31 12:05:17 +01:00
										 |  |  | 	if hrefProp != nil && hrefProp.IsIRI() { | 
					
						
							|  |  |  | 		href = hrefProp.GetIRI().String() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// One of nameString and hrefProp must be set. | 
					
						
							|  |  |  | 	if nameString == "" && href == "" { | 
					
						
							|  |  |  | 		return nil, gtserror.Newf("neither Name nor Href were set") | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return >smodel.Mention{ | 
					
						
							|  |  |  | 		NameString:       nameString, | 
					
						
							| 
									
										
										
										
											2023-10-31 12:05:17 +01:00
										 |  |  | 		TargetAccountURI: href, | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 	}, nil | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | // ExtractActorURI extracts the first Actor URI | 
					
						
							|  |  |  | // it can find from a WithActor interface. | 
					
						
							|  |  |  | func ExtractActorURI(withActor WithActor) (*url.URL, error) { | 
					
						
							|  |  |  | 	actorProp := withActor.GetActivityStreamsActor() | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	if actorProp == nil { | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		return nil, gtserror.New("actor property was nil") | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	for iter := actorProp.Begin(); iter != actorProp.End(); iter = iter.Next() { | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		id, err := pub.ToId(iter) | 
					
						
							|  |  |  | 		if err == nil { | 
					
						
							|  |  |  | 			// Found one we can use. | 
					
						
							|  |  |  | 			return id, nil | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return nil, gtserror.New("no iri found for actor prop") | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | // ExtractObjectURI extracts the first Object URI | 
					
						
							|  |  |  | // it can find from a WithObject interface. | 
					
						
							|  |  |  | func ExtractObjectURI(withObject WithObject) (*url.URL, error) { | 
					
						
							|  |  |  | 	objectProp := withObject.GetActivityStreamsObject() | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	if objectProp == nil { | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		return nil, gtserror.New("object property was nil") | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 	for iter := objectProp.Begin(); iter != objectProp.End(); iter = iter.Next() { | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		id, err := pub.ToId(iter) | 
					
						
							|  |  |  | 		if err == nil { | 
					
						
							|  |  |  | 			// Found one we can use. | 
					
						
							|  |  |  | 			return id, nil | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return nil, gtserror.New("no iri found for object prop") | 
					
						
							| 
									
										
										
										
											2021-05-15 11:58:11 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-10-06 18:18:02 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | // ExtractObjectURIs extracts the URLs of each Object | 
					
						
							|  |  |  | // it can find from a WithObject interface. | 
					
						
							|  |  |  | func ExtractObjectURIs(withObject WithObject) ([]*url.URL, error) { | 
					
						
							|  |  |  | 	objectProp := withObject.GetActivityStreamsObject() | 
					
						
							| 
									
										
										
										
											2023-01-25 11:12:27 +01:00
										 |  |  | 	if objectProp == nil { | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		return nil, gtserror.New("object property was nil") | 
					
						
							| 
									
										
										
										
											2023-01-25 11:12:27 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	urls := make([]*url.URL, 0, objectProp.Len()) | 
					
						
							|  |  |  | 	for iter := objectProp.Begin(); iter != objectProp.End(); iter = iter.Next() { | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		id, err := pub.ToId(iter) | 
					
						
							|  |  |  | 		if err == nil { | 
					
						
							|  |  |  | 			// Found one we can use. | 
					
						
							|  |  |  | 			urls = append(urls, id) | 
					
						
							| 
									
										
										
										
											2023-01-25 11:12:27 +01:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return urls, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | // ExtractVisibility extracts the gtsmodel.Visibility | 
					
						
							|  |  |  | // of a given addressable with a To and CC property. | 
					
						
							| 
									
										
										
										
											2021-10-06 18:18:02 +02:00
										 |  |  | // | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | // ActorFollowersURI is needed to check whether the | 
					
						
							|  |  |  | // visibility is FollowersOnly or not. The passed-in | 
					
						
							|  |  |  | // value should just be the string value representation | 
					
						
							|  |  |  | // of the followers URI of the actor who created the activity, | 
					
						
							|  |  |  | // eg., `https://example.org/users/whoever/followers`. | 
					
						
							| 
									
										
										
										
											2021-10-06 18:18:02 +02:00
										 |  |  | func ExtractVisibility(addressable Addressable, actorFollowersURI string) (gtsmodel.Visibility, error) { | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 	var ( | 
					
						
							|  |  |  | 		to = ExtractToURIs(addressable) | 
					
						
							|  |  |  | 		cc = ExtractCcURIs(addressable) | 
					
						
							|  |  |  | 	) | 
					
						
							| 
									
										
										
										
											2021-10-06 18:18:02 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if len(to) == 0 && len(cc) == 0 { | 
					
						
							| 
									
										
										
										
											2024-11-25 13:48:59 +00:00
										 |  |  | 		return 0, gtserror.Newf("message wasn't TO or CC anyone") | 
					
						
							| 
									
										
										
										
											2021-10-06 18:18:02 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 	// Assume most restrictive visibility, | 
					
						
							|  |  |  | 	// and work our way up from there. | 
					
						
							| 
									
										
										
										
											2021-10-06 18:18:02 +02:00
										 |  |  | 	visibility := gtsmodel.VisibilityDirect | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if isFollowers(to, actorFollowersURI) { | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		// Followers in TO: it's at least followers only. | 
					
						
							| 
									
										
										
										
											2021-10-06 18:18:02 +02:00
										 |  |  | 		visibility = gtsmodel.VisibilityFollowersOnly | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if isPublic(cc) { | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		// CC'd to public: it's at least unlocked. | 
					
						
							| 
									
										
										
										
											2021-10-06 18:18:02 +02:00
										 |  |  | 		visibility = gtsmodel.VisibilityUnlocked | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if isPublic(to) { | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		// TO'd to public: it's a public post. | 
					
						
							| 
									
										
										
										
											2021-10-06 18:18:02 +02:00
										 |  |  | 		visibility = gtsmodel.VisibilityPublic | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return visibility, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | // ExtractInteractionPolicy extracts a *gtsmodel.InteractionPolicy | 
					
						
							|  |  |  | // from the given Statusable created by by the given *gtsmodel.Account. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Will be nil (default policy) for Statusables that have no policy | 
					
						
							|  |  |  | // set on them, or have a null policy. In such a case, the caller | 
					
						
							|  |  |  | // should assume the default policy for the status's visibility level. | 
					
						
							|  |  |  | func ExtractInteractionPolicy( | 
					
						
							|  |  |  | 	statusable Statusable, | 
					
						
							|  |  |  | 	owner *gtsmodel.Account, | 
					
						
							|  |  |  | ) *gtsmodel.InteractionPolicy { | 
					
						
							|  |  |  | 	policyProp := statusable.GetGoToSocialInteractionPolicy() | 
					
						
							|  |  |  | 	if policyProp == nil || policyProp.Len() != 1 { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	policyPropIter := policyProp.At(0) | 
					
						
							|  |  |  | 	if !policyPropIter.IsGoToSocialInteractionPolicy() { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	policy := policyPropIter.Get() | 
					
						
							|  |  |  | 	if policy == nil { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return >smodel.InteractionPolicy{ | 
					
						
							|  |  |  | 		CanLike:     extractCanLike(policy.GetGoToSocialCanLike(), owner), | 
					
						
							|  |  |  | 		CanReply:    extractCanReply(policy.GetGoToSocialCanReply(), owner), | 
					
						
							|  |  |  | 		CanAnnounce: extractCanAnnounce(policy.GetGoToSocialCanAnnounce(), owner), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func extractCanLike( | 
					
						
							|  |  |  | 	prop vocab.GoToSocialCanLikeProperty, | 
					
						
							|  |  |  | 	owner *gtsmodel.Account, | 
					
						
							|  |  |  | ) gtsmodel.PolicyRules { | 
					
						
							|  |  |  | 	if prop == nil || prop.Len() != 1 { | 
					
						
							|  |  |  | 		return gtsmodel.PolicyRules{} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	propIter := prop.At(0) | 
					
						
							|  |  |  | 	if !propIter.IsGoToSocialCanLike() { | 
					
						
							|  |  |  | 		return gtsmodel.PolicyRules{} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	withRules := propIter.Get() | 
					
						
							|  |  |  | 	if withRules == nil { | 
					
						
							|  |  |  | 		return gtsmodel.PolicyRules{} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return gtsmodel.PolicyRules{ | 
					
						
							|  |  |  | 		Always:       extractPolicyValues(withRules.GetGoToSocialAlways(), owner), | 
					
						
							|  |  |  | 		WithApproval: extractPolicyValues(withRules.GetGoToSocialApprovalRequired(), owner), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func extractCanReply( | 
					
						
							|  |  |  | 	prop vocab.GoToSocialCanReplyProperty, | 
					
						
							|  |  |  | 	owner *gtsmodel.Account, | 
					
						
							|  |  |  | ) gtsmodel.PolicyRules { | 
					
						
							|  |  |  | 	if prop == nil || prop.Len() != 1 { | 
					
						
							|  |  |  | 		return gtsmodel.PolicyRules{} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	propIter := prop.At(0) | 
					
						
							|  |  |  | 	if !propIter.IsGoToSocialCanReply() { | 
					
						
							|  |  |  | 		return gtsmodel.PolicyRules{} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	withRules := propIter.Get() | 
					
						
							|  |  |  | 	if withRules == nil { | 
					
						
							|  |  |  | 		return gtsmodel.PolicyRules{} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return gtsmodel.PolicyRules{ | 
					
						
							|  |  |  | 		Always:       extractPolicyValues(withRules.GetGoToSocialAlways(), owner), | 
					
						
							|  |  |  | 		WithApproval: extractPolicyValues(withRules.GetGoToSocialApprovalRequired(), owner), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func extractCanAnnounce( | 
					
						
							|  |  |  | 	prop vocab.GoToSocialCanAnnounceProperty, | 
					
						
							|  |  |  | 	owner *gtsmodel.Account, | 
					
						
							|  |  |  | ) gtsmodel.PolicyRules { | 
					
						
							|  |  |  | 	if prop == nil || prop.Len() != 1 { | 
					
						
							|  |  |  | 		return gtsmodel.PolicyRules{} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	propIter := prop.At(0) | 
					
						
							|  |  |  | 	if !propIter.IsGoToSocialCanAnnounce() { | 
					
						
							|  |  |  | 		return gtsmodel.PolicyRules{} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	withRules := propIter.Get() | 
					
						
							|  |  |  | 	if withRules == nil { | 
					
						
							|  |  |  | 		return gtsmodel.PolicyRules{} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return gtsmodel.PolicyRules{ | 
					
						
							|  |  |  | 		Always:       extractPolicyValues(withRules.GetGoToSocialAlways(), owner), | 
					
						
							|  |  |  | 		WithApproval: extractPolicyValues(withRules.GetGoToSocialApprovalRequired(), owner), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func extractPolicyValues[T WithIRI]( | 
					
						
							|  |  |  | 	prop Property[T], | 
					
						
							|  |  |  | 	owner *gtsmodel.Account, | 
					
						
							|  |  |  | ) gtsmodel.PolicyValues { | 
					
						
							|  |  |  | 	iris := getIRIs(prop) | 
					
						
							|  |  |  | 	PolicyValues := make(gtsmodel.PolicyValues, 0, len(iris)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, iri := range iris { | 
					
						
							|  |  |  | 		switch iriStr := iri.String(); iriStr { | 
					
						
							|  |  |  | 		case pub.PublicActivityPubIRI: | 
					
						
							|  |  |  | 			PolicyValues = append(PolicyValues, gtsmodel.PolicyValuePublic) | 
					
						
							|  |  |  | 		case owner.FollowersURI: | 
					
						
							|  |  |  | 			PolicyValues = append(PolicyValues, gtsmodel.PolicyValueFollowers) | 
					
						
							|  |  |  | 		case owner.FollowingURI: | 
					
						
							| 
									
										
										
										
											2024-09-17 21:22:45 +02:00
										 |  |  | 			PolicyValues = append(PolicyValues, gtsmodel.PolicyValueFollowing) | 
					
						
							| 
									
										
										
										
											2024-07-26 12:04:28 +02:00
										 |  |  | 		case owner.URI: | 
					
						
							|  |  |  | 			PolicyValues = append(PolicyValues, gtsmodel.PolicyValueAuthor) | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			if iri.Scheme == "http" || iri.Scheme == "https" { | 
					
						
							|  |  |  | 				PolicyValues = append(PolicyValues, gtsmodel.PolicyValue(iriStr)) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return PolicyValues | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | // ExtractSensitive extracts whether or not an item should | 
					
						
							|  |  |  | // be marked as sensitive according to its ActivityStreams | 
					
						
							|  |  |  | // sensitive property. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // If no sensitive property is set on the item at all, or | 
					
						
							|  |  |  | // if this property isn't a boolean, then false will be | 
					
						
							|  |  |  | // returned by default. | 
					
						
							| 
									
										
										
										
											2021-11-13 17:29:43 +01:00
										 |  |  | func ExtractSensitive(withSensitive WithSensitive) bool { | 
					
						
							|  |  |  | 	sensitiveProp := withSensitive.GetActivityStreamsSensitive() | 
					
						
							|  |  |  | 	if sensitiveProp == nil { | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for iter := sensitiveProp.Begin(); iter != sensitiveProp.End(); iter = iter.Next() { | 
					
						
							|  |  |  | 		if iter.IsXMLSchemaBoolean() { | 
					
						
							|  |  |  | 			return iter.Get() | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2022-09-23 21:27:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | // ExtractSharedInbox extracts the sharedInbox URI property | 
					
						
							|  |  |  | // from an Actor. Returns nil if this property is not set. | 
					
						
							| 
									
										
										
										
											2022-09-23 21:27:35 +02:00
										 |  |  | func ExtractSharedInbox(withEndpoints WithEndpoints) *url.URL { | 
					
						
							|  |  |  | 	endpointsProp := withEndpoints.GetActivityStreamsEndpoints() | 
					
						
							|  |  |  | 	if endpointsProp == nil { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for iter := endpointsProp.Begin(); iter != endpointsProp.End(); iter = iter.Next() { | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		if !iter.IsActivityStreamsEndpoints() { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-09-23 21:27:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		endpoints := iter.Get() | 
					
						
							|  |  |  | 		if endpoints == nil { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-09-23 21:27:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 		sharedInboxProp := endpoints.GetActivityStreamsSharedInbox() | 
					
						
							|  |  |  | 		if sharedInboxProp == nil || !sharedInboxProp.IsIRI() { | 
					
						
							|  |  |  | 			continue | 
					
						
							| 
									
										
										
										
											2022-09-23 21:27:35 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		return sharedInboxProp.GetIRI() | 
					
						
							| 
									
										
										
										
											2022-09-23 21:27:35 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-08 14:32:17 +00:00
										 |  |  | // ExtractPoll extracts a placeholder Poll from Pollable interface, with available options and flags populated. | 
					
						
							|  |  |  | func ExtractPoll(poll Pollable) (*gtsmodel.Poll, error) { | 
					
						
							|  |  |  | 	var closed time.Time | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Extract the options (votes if any) and 'multiple choice' flag. | 
					
						
							| 
									
										
										
										
											2023-12-12 13:47:07 +00:00
										 |  |  | 	options, multi, hideCounts, err := extractPollOptions(poll) | 
					
						
							| 
									
										
										
										
											2023-11-08 14:32:17 +00:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-11 10:15:04 +00:00
										 |  |  | 	// Extract the poll closed time, | 
					
						
							|  |  |  | 	// it's okay for this to be zero. | 
					
						
							| 
									
										
										
										
											2023-11-08 14:32:17 +00:00
										 |  |  | 	closedSlice := GetClosed(poll) | 
					
						
							|  |  |  | 	if len(closedSlice) == 1 { | 
					
						
							|  |  |  | 		closed = closedSlice[0] | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-11 10:15:04 +00:00
										 |  |  | 	// Extract the poll end time, again | 
					
						
							|  |  |  | 	// this isn't necessarily set as some | 
					
						
							|  |  |  | 	// servers support "endless" polls. | 
					
						
							|  |  |  | 	endTime := GetEndTime(poll) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if endTime.IsZero() && !closed.IsZero() { | 
					
						
							|  |  |  | 		// If no endTime is provided, but the | 
					
						
							|  |  |  | 		// poll is marked as closed, infer the | 
					
						
							|  |  |  | 		// endTime from the closed time. | 
					
						
							|  |  |  | 		endTime = closed | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-08 14:32:17 +00:00
										 |  |  | 	// Extract the number of voters. | 
					
						
							|  |  |  | 	voters := GetVotersCount(poll) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return >smodel.Poll{ | 
					
						
							| 
									
										
										
										
											2023-12-12 13:47:07 +00:00
										 |  |  | 		Options:    optionNames(options), | 
					
						
							| 
									
										
										
										
											2023-11-08 14:32:17 +00:00
										 |  |  | 		Multiple:   &multi, | 
					
						
							|  |  |  | 		HideCounts: &hideCounts, | 
					
						
							| 
									
										
										
										
											2023-12-12 13:47:07 +00:00
										 |  |  | 		Votes:      optionVotes(options), | 
					
						
							| 
									
										
										
										
											2023-11-08 14:32:17 +00:00
										 |  |  | 		Voters:     &voters, | 
					
						
							|  |  |  | 		ExpiresAt:  endTime, | 
					
						
							|  |  |  | 		ClosedAt:   closed, | 
					
						
							|  |  |  | 	}, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-12 13:47:07 +00:00
										 |  |  | // pollOption is a simple type | 
					
						
							|  |  |  | // to unify a poll option name | 
					
						
							|  |  |  | // with the number of votes. | 
					
						
							|  |  |  | type pollOption struct { | 
					
						
							|  |  |  | 	Name  string | 
					
						
							|  |  |  | 	Votes int | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // optionNames extracts name strings from a slice of poll options. | 
					
						
							|  |  |  | func optionNames(in []pollOption) []string { | 
					
						
							|  |  |  | 	out := make([]string, len(in)) | 
					
						
							|  |  |  | 	for i := range in { | 
					
						
							|  |  |  | 		out[i] = in[i].Name | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return out | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // optionVotes extracts vote counts from a slice of poll options. | 
					
						
							|  |  |  | func optionVotes(in []pollOption) []int { | 
					
						
							|  |  |  | 	out := make([]int, len(in)) | 
					
						
							|  |  |  | 	for i := range in { | 
					
						
							|  |  |  | 		out[i] = in[i].Votes | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return out | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // extractPollOptions extracts poll option name strings, the 'multiple choice flag', and 'hideCounts' intrinsic flag properties value from Pollable. | 
					
						
							|  |  |  | func extractPollOptions(poll Pollable) (options []pollOption, multi bool, hide bool, err error) { | 
					
						
							| 
									
										
										
										
											2023-11-08 14:32:17 +00:00
										 |  |  | 	var errs gtserror.MultiError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Iterate the oneOf property and gather poll single-choice options. | 
					
						
							|  |  |  | 	IterateOneOf(poll, func(iter vocab.ActivityStreamsOneOfPropertyIterator) { | 
					
						
							| 
									
										
										
										
											2023-12-12 13:47:07 +00:00
										 |  |  | 		name, votes, err := extractPollOption(iter.GetType()) | 
					
						
							| 
									
										
										
										
											2023-11-08 14:32:17 +00:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			errs.Append(err) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-12-12 13:47:07 +00:00
										 |  |  | 		if votes == nil { | 
					
						
							|  |  |  | 			hide = true | 
					
						
							|  |  |  | 			votes = new(int) | 
					
						
							| 
									
										
										
										
											2023-11-08 14:32:17 +00:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-12-12 13:47:07 +00:00
										 |  |  | 		options = append(options, pollOption{ | 
					
						
							|  |  |  | 			Name:  name, | 
					
						
							|  |  |  | 			Votes: *votes, | 
					
						
							|  |  |  | 		}) | 
					
						
							| 
									
										
										
										
											2023-11-08 14:32:17 +00:00
										 |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2023-12-12 13:47:07 +00:00
										 |  |  | 	if len(options) > 0 || len(errs) > 0 { | 
					
						
							|  |  |  | 		return options, false, hide, errs.Combine() | 
					
						
							| 
									
										
										
										
											2023-11-08 14:32:17 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Iterate the anyOf property and gather poll multi-choice options. | 
					
						
							|  |  |  | 	IterateAnyOf(poll, func(iter vocab.ActivityStreamsAnyOfPropertyIterator) { | 
					
						
							| 
									
										
										
										
											2023-12-12 13:47:07 +00:00
										 |  |  | 		name, votes, err := extractPollOption(iter.GetType()) | 
					
						
							| 
									
										
										
										
											2023-11-08 14:32:17 +00:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			errs.Append(err) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-12-12 13:47:07 +00:00
										 |  |  | 		if votes == nil { | 
					
						
							|  |  |  | 			hide = true | 
					
						
							|  |  |  | 			votes = new(int) | 
					
						
							| 
									
										
										
										
											2023-11-08 14:32:17 +00:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-12-12 13:47:07 +00:00
										 |  |  | 		options = append(options, pollOption{ | 
					
						
							|  |  |  | 			Name:  name, | 
					
						
							|  |  |  | 			Votes: *votes, | 
					
						
							|  |  |  | 		}) | 
					
						
							| 
									
										
										
										
											2023-11-08 14:32:17 +00:00
										 |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2023-12-12 13:47:07 +00:00
										 |  |  | 	if len(options) > 0 || len(errs) > 0 { | 
					
						
							|  |  |  | 		return options, true, hide, errs.Combine() | 
					
						
							| 
									
										
										
										
											2023-11-08 14:32:17 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-12 13:47:07 +00:00
										 |  |  | 	return nil, false, false, errors.New("poll without options") | 
					
						
							| 
									
										
										
										
											2023-11-08 14:32:17 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-03 14:59:30 +01:00
										 |  |  | // IterateOneOf will attempt to extract oneOf property from given interface, and passes each iterated item to function. | 
					
						
							|  |  |  | func IterateOneOf(withOneOf WithOneOf, foreach func(vocab.ActivityStreamsOneOfPropertyIterator)) { | 
					
						
							|  |  |  | 	if foreach == nil { | 
					
						
							|  |  |  | 		// nil check outside loop. | 
					
						
							|  |  |  | 		panic("nil function") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Extract the one-of property from interface. | 
					
						
							|  |  |  | 	oneOfProp := withOneOf.GetActivityStreamsOneOf() | 
					
						
							|  |  |  | 	if oneOfProp == nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Get start and end of iter. | 
					
						
							|  |  |  | 	start := oneOfProp.Begin() | 
					
						
							|  |  |  | 	end := oneOfProp.End() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Pass iterated oneOf entries to given function. | 
					
						
							|  |  |  | 	for iter := start; iter != end; iter = iter.Next() { | 
					
						
							|  |  |  | 		foreach(iter) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // IterateAnyOf will attempt to extract anyOf property from given interface, and passes each iterated item to function. | 
					
						
							|  |  |  | func IterateAnyOf(withAnyOf WithAnyOf, foreach func(vocab.ActivityStreamsAnyOfPropertyIterator)) { | 
					
						
							|  |  |  | 	if foreach == nil { | 
					
						
							|  |  |  | 		// nil check outside loop. | 
					
						
							|  |  |  | 		panic("nil function") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Extract the any-of property from interface. | 
					
						
							|  |  |  | 	anyOfProp := withAnyOf.GetActivityStreamsAnyOf() | 
					
						
							|  |  |  | 	if anyOfProp == nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Get start and end of iter. | 
					
						
							|  |  |  | 	start := anyOfProp.Begin() | 
					
						
							|  |  |  | 	end := anyOfProp.End() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Pass iterated anyOf entries to given function. | 
					
						
							|  |  |  | 	for iter := start; iter != end; iter = iter.Next() { | 
					
						
							|  |  |  | 		foreach(iter) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-08 14:32:17 +00:00
										 |  |  | // extractPollOption extracts a usable poll option name from vocab.Type, or error. | 
					
						
							|  |  |  | func extractPollOption(t vocab.Type) (name string, votes *int, err error) { | 
					
						
							|  |  |  | 	// Check fulfills PollOptionable type | 
					
						
							|  |  |  | 	// (this accounts for nil input type). | 
					
						
							|  |  |  | 	optionable, ok := t.(PollOptionable) | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							|  |  |  | 		return "", nil, fmt.Errorf("incorrect option type: %T", t) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Extract PollOption from interface. | 
					
						
							|  |  |  | 	name = ExtractName(optionable) | 
					
						
							|  |  |  | 	if name == "" { | 
					
						
							|  |  |  | 		return "", nil, errors.New("empty option name") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Check PollOptionable for attached 'replies' property. | 
					
						
							|  |  |  | 	repliesProp := optionable.GetActivityStreamsReplies() | 
					
						
							|  |  |  | 	if repliesProp != nil { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Get repliesProp as the AS collection type it should be. | 
					
						
							|  |  |  | 		collection := repliesProp.GetActivityStreamsCollection() | 
					
						
							|  |  |  | 		if collection != nil { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// Extract integer value from the collection 'totalItems' property. | 
					
						
							|  |  |  | 			totalItemsProp := collection.GetActivityStreamsTotalItems() | 
					
						
							|  |  |  | 			if totalItemsProp != nil { | 
					
						
							|  |  |  | 				i := totalItemsProp.Get() | 
					
						
							|  |  |  | 				votes = &i | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return name, votes, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-17 17:49:11 +02:00
										 |  |  | // isPublic checks if at least one entry in the given | 
					
						
							|  |  |  | // uris slice equals the activitystreams public uri. | 
					
						
							|  |  |  | func isPublic(uris []*url.URL) bool { | 
					
						
							|  |  |  | 	for _, uri := range uris { | 
					
						
							|  |  |  | 		if pub.IsPublic(uri.String()) { | 
					
						
							|  |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // isFollowers checks if at least one entry in the given | 
					
						
							|  |  |  | // uris slice equals the given followersURI. | 
					
						
							|  |  |  | func isFollowers(uris []*url.URL, followersURI string) bool { | 
					
						
							|  |  |  | 	for _, uri := range uris { | 
					
						
							|  |  |  | 		if strings.EqualFold(uri.String(), followersURI) { | 
					
						
							|  |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } |