mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-29 19:52:24 -05:00 
			
		
		
		
	* feat: check X-Robots-Tag when accessing /api/v1/instance or /nodeinfo endpoints respect X-Robots-Tag * chore: go fmt ./... * Check robots.txt as well, add tests --------- Co-authored-by: tobi <tobi.smethurst@protonmail.com>
		
			
				
	
	
		
			160 lines
		
	
	
	
		
			4.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			160 lines
		
	
	
	
		
			4.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // 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/>.
 | |
| 
 | |
| package util
 | |
| 
 | |
| import "strings"
 | |
| 
 | |
| const (
 | |
| 	// Possible GoToSocial mimetypes.
 | |
| 	AppJSON           = `application/json`
 | |
| 	AppXML            = `application/xml`
 | |
| 	appXMLText        = `text/xml` // AppXML is only *recommended* in RFC7303
 | |
| 	AppXMLXRD         = `application/xrd+xml`
 | |
| 	AppRSSXML         = `application/rss+xml`
 | |
| 	AppActivityJSON   = `application/activity+json`
 | |
| 	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`
 | |
| 	TextXML           = `text/xml`
 | |
| 	TextHTML          = `text/html`
 | |
| 	TextCSS           = `text/css`
 | |
| 	TextCSV           = `text/csv`
 | |
| 	TextPlain         = `text/plain`
 | |
| 	UTF8              = `utf-8`
 | |
| )
 | |
| 
 | |
| // 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 ||
 | |
| 		p[0] == appXMLText
 | |
| }
 | |
| 
 | |
| // 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] == appXMLText ||
 | |
| 		p[0] == AppXMLXRD
 | |
| }
 | |
| 
 | |
| // TextPlainContentType returns whether is text/plain(;charset=utf-8)? content-type.
 | |
| func TextPlainContentType(ct string) bool {
 | |
| 	p := splitContentType(ct)
 | |
| 	p, ok := isUTF8ContentType(p)
 | |
| 	return ok && len(p) == 1 &&
 | |
| 		p[0] == TextPlain
 | |
| }
 | |
| 
 | |
| // 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 + UTF8
 | |
| 	for i, part := range p {
 | |
| 
 | |
| 		// Only handle charset slice parts.
 | |
| 		if strings.HasPrefix(part, 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
 | |
| }
 |