diff options
Diffstat (limited to 'internal/api')
| -rw-r--r-- | internal/api/util/mime.go | 114 | ||||
| -rw-r--r-- | internal/api/util/mime_test.go | 75 | 
2 files changed, 188 insertions, 1 deletions
diff --git a/internal/api/util/mime.go b/internal/api/util/mime.go index ad1b405cd..455a84de9 100644 --- a/internal/api/util/mime.go +++ b/internal/api/util/mime.go @@ -17,6 +17,8 @@  package util +import "strings" +  const (  	// Possible GoToSocial mimetypes.  	AppJSON           = `application/json` @@ -24,7 +26,8 @@ const (  	AppXMLXRD         = `application/xrd+xml`  	AppRSSXML         = `application/rss+xml`  	AppActivityJSON   = `application/activity+json` -	AppActivityLDJSON = `application/ld+json; profile="https://www.w3.org/ns/activitystreams"` +	appActivityLDJSON = `application/ld+json` // without profile +	AppActivityLDJSON = appActivityLDJSON + `; profile="https://www.w3.org/ns/activitystreams"`  	AppJRDJSON        = `application/jrd+json` // https://www.rfc-editor.org/rfc/rfc7033#section-10.2  	AppForm           = `application/x-www-form-urlencoded`  	MultipartForm     = `multipart/form-data` @@ -32,3 +35,112 @@ const (  	TextHTML          = `text/html`  	TextCSS           = `text/css`  ) + +// JSONContentType returns whether is application/json(;charset=utf-8)? content-type. +func JSONContentType(ct string) bool { +	p := splitContentType(ct) +	p, ok := isUTF8ContentType(p) +	return ok && len(p) == 1 && +		p[0] == AppJSON +} + +// JSONJRDContentType returns whether is application/(jrd+)?json(;charset=utf-8)? content-type. +func JSONJRDContentType(ct string) bool { +	p := splitContentType(ct) +	p, ok := isUTF8ContentType(p) +	return ok && len(p) == 1 && +		p[0] == AppJSON || +		p[0] == AppJRDJSON +} + +// XMLContentType returns whether is application/xml(;charset=utf-8)? content-type. +func XMLContentType(ct string) bool { +	p := splitContentType(ct) +	p, ok := isUTF8ContentType(p) +	return ok && len(p) == 1 && +		p[0] == AppXML +} + +// XMLXRDContentType returns whether is application/(xrd+)?xml(;charset=utf-8)? content-type. +func XMLXRDContentType(ct string) bool { +	p := splitContentType(ct) +	p, ok := isUTF8ContentType(p) +	return ok && len(p) == 1 && +		p[0] == AppXML || +		p[0] == AppXMLXRD +} + +// ASContentType returns whether is valid ActivityStreams content-types: +// - application/activity+json +// - application/ld+json;profile=https://w3.org/ns/activitystreams +func ASContentType(ct string) bool { +	p := splitContentType(ct) +	p, ok := isUTF8ContentType(p) +	if !ok { +		return false +	} +	switch len(p) { +	case 1: +		return p[0] == AppActivityJSON +	case 2: +		return p[0] == appActivityLDJSON && +			p[1] == "profile=https://www.w3.org/ns/activitystreams" || +			p[1] == "profile=\"https://www.w3.org/ns/activitystreams\"" +	default: +		return false +	} +} + +// NodeInfo2ContentType returns whether is nodeinfo schema 2.0 content-type. +func NodeInfo2ContentType(ct string) bool { +	p := splitContentType(ct) +	p, ok := isUTF8ContentType(p) +	if !ok { +		return false +	} +	switch len(p) { +	case 1: +		return p[0] == AppJSON +	case 2: +		return p[0] == AppJSON && +			p[1] == "profile=\"http://nodeinfo.diaspora.software/ns/schema/2.0#\"" || +			p[1] == "profile=http://nodeinfo.diaspora.software/ns/schema/2.0#" +	default: +		return false +	} +} + +// isUTF8ContentType checks for a provided charset in given +// type parts list, removes it and returns whether is utf-8. +func isUTF8ContentType(p []string) ([]string, bool) { +	const charset = "charset=" +	const charsetUTF8 = charset + "utf-8" +	for i, part := range p { + +		// Only handle charset slice parts. +		if part[:len(charset)] == charset { + +			// Check if is UTF-8 charset. +			ok := (part == charsetUTF8) + +			// Drop this slice part. +			_ = copy(p[i:], p[i+1:]) +			p = p[:len(p)-1] + +			return p, ok +		} +	} +	return p, true +} + +// splitContentType splits content-type into semi-colon +// separated parts. useful when a charset is provided. +// note this also maps all chars to their lowercase form. +func splitContentType(ct string) []string { +	s := strings.Split(ct, ";") +	for i := range s { +		s[i] = strings.TrimSpace(s[i]) +		s[i] = strings.ToLower(s[i]) +	} +	return s +} diff --git a/internal/api/util/mime_test.go b/internal/api/util/mime_test.go new file mode 100644 index 000000000..6b12d1436 --- /dev/null +++ b/internal/api/util/mime_test.go @@ -0,0 +1,75 @@ +package util_test + +import ( +	"testing" + +	"github.com/superseriousbusiness/gotosocial/internal/api/util" +) + +func TestIsASContentType(t *testing.T) { +	for _, test := range []struct { +		Input  string +		Expect bool +	}{ +		{ +			Input:  "application/activity+json", +			Expect: true, +		}, +		{ +			Input:  "application/activity+json; charset=utf-8", +			Expect: true, +		}, +		{ +			Input:  "application/activity+json;charset=utf-8", +			Expect: true, +		}, +		{ +			Input:  "application/activity+json ;charset=utf-8", +			Expect: true, +		}, +		{ +			Input:  "application/activity+json ; charset=utf-8", +			Expect: true, +		}, +		{ +			Input:  "application/ld+json;profile=https://www.w3.org/ns/activitystreams", +			Expect: true, +		}, +		{ +			Input:  "application/ld+json;profile=\"https://www.w3.org/ns/activitystreams\"", +			Expect: true, +		}, +		{ +			Input:  "application/ld+json ;profile=https://www.w3.org/ns/activitystreams", +			Expect: true, +		}, +		{ +			Input:  "application/ld+json ;profile=\"https://www.w3.org/ns/activitystreams\"", +			Expect: true, +		}, +		{ +			Input:  "application/ld+json ; profile=https://www.w3.org/ns/activitystreams", +			Expect: true, +		}, +		{ +			Input:  "application/ld+json ; profile=\"https://www.w3.org/ns/activitystreams\"", +			Expect: true, +		}, +		{ +			Input:  "application/ld+json; profile=https://www.w3.org/ns/activitystreams", +			Expect: true, +		}, +		{ +			Input:  "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"", +			Expect: true, +		}, +		{ +			Input:  "application/ld+json", +			Expect: false, +		}, +	} { +		if util.ASContentType(test.Input) != test.Expect { +			t.Errorf("did not get expected result %v for input: %s", test.Expect, test.Input) +		} +	} +}  | 
