mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-04 00:32:24 -06:00 
			
		
		
		
	
		
			
				
	
	
		
			178 lines
		
	
	
	
		
			8.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			178 lines
		
	
	
	
		
			8.3 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 regexes
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"regexp"
 | 
						|
	"sync"
 | 
						|
 | 
						|
	"mvdan.cc/xurls/v2"
 | 
						|
)
 | 
						|
 | 
						|
const (
 | 
						|
	users     = "users"
 | 
						|
	actors    = "actors"
 | 
						|
	statuses  = "statuses"
 | 
						|
	inbox     = "inbox"
 | 
						|
	outbox    = "outbox"
 | 
						|
	followers = "followers"
 | 
						|
	following = "following"
 | 
						|
	liked     = "liked"
 | 
						|
	publicKey = "main-key"
 | 
						|
	follow    = "follow"
 | 
						|
	blocks    = "blocks"
 | 
						|
	reports   = "reports"
 | 
						|
 | 
						|
	schemes                  = `(http|https)://`                                         // Allowed URI protocols for parsing links in text.
 | 
						|
	alphaNumeric             = `\p{L}\p{M}*|\p{N}`                                       // A single number or script character in any language, including chars with accents.
 | 
						|
	usernameGrp              = `(?:` + alphaNumeric + `|\.|\-|\_)`                       // Non-capturing group that matches against a single valid username character.
 | 
						|
	domainGrp                = `(?:` + alphaNumeric + `|\.|\-|\:)`                       // Non-capturing group that matches against a single valid domain character.
 | 
						|
	mentionName              = `^@(` + usernameGrp + `+)(?:@(` + domainGrp + `+))?$`     // Extract parts of one mention, maybe including domain.
 | 
						|
	mentionFinder            = `(?:^|\s)(@` + usernameGrp + `+(?:@` + domainGrp + `+)?)` // Extract all mentions from a text, each mention may include domain.
 | 
						|
	emojiShortcode           = `\w{2,30}`                                                // Pattern for emoji shortcodes. maximumEmojiShortcodeLength = 30
 | 
						|
	emojiFinder              = `(?:\b)?:(` + emojiShortcode + `):(?:\b)?`                // Extract all emoji shortcodes from a text.
 | 
						|
	usernameStrict           = `^[a-z0-9_]{1,64}$`                                       // Pattern for usernames on THIS instance. maximumUsernameLength = 64
 | 
						|
	usernameRelaxed          = `[a-z0-9_\.]{1,}`                                         // Relaxed version of username that can match instance accounts too.
 | 
						|
	misskeyReportNotesFinder = `(?m)(?:^Note: ((?:http|https):\/\/.*)$)`                 // Extract reported Note URIs from the text of a Misskey report/flag.
 | 
						|
	ulid                     = `[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{26}`                  // Pattern for ULID.
 | 
						|
	ulidValidate             = `^` + ulid + `$`                                          // Validate one ULID.
 | 
						|
 | 
						|
	/*
 | 
						|
		Path parts / capture.
 | 
						|
	*/
 | 
						|
 | 
						|
	userPathPrefix = `^/?` + users + `/(` + usernameRelaxed + `)`
 | 
						|
	userPath       = userPathPrefix + `$`
 | 
						|
	publicKeyPath  = userPathPrefix + `/` + publicKey + `$`
 | 
						|
	inboxPath      = userPathPrefix + `/` + inbox + `$`
 | 
						|
	outboxPath     = userPathPrefix + `/` + outbox + `$`
 | 
						|
	followersPath  = userPathPrefix + `/` + followers + `$`
 | 
						|
	followingPath  = userPathPrefix + `/` + following + `$`
 | 
						|
	likedPath      = userPathPrefix + `/` + liked + `$`
 | 
						|
	followPath     = userPathPrefix + `/` + follow + `/(` + ulid + `)$`
 | 
						|
	likePath       = userPathPrefix + `/` + liked + `/(` + ulid + `)$`
 | 
						|
	statusesPath   = userPathPrefix + `/` + statuses + `/(` + ulid + `)$`
 | 
						|
	blockPath      = userPathPrefix + `/` + blocks + `/(` + ulid + `)$`
 | 
						|
	reportPath     = `^/?` + reports + `/(` + ulid + `)$`
 | 
						|
	filePath       = `^/?(` + ulid + `)/([a-z]+)/([a-z]+)/(` + ulid + `)\.([a-z0-9]+)$`
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	// LinkScheme captures http/https schemes in URLs.
 | 
						|
	LinkScheme = func() *regexp.Regexp {
 | 
						|
		rgx, err := xurls.StrictMatchingScheme(schemes)
 | 
						|
		if err != nil {
 | 
						|
			panic(err)
 | 
						|
		}
 | 
						|
		return rgx
 | 
						|
	}()
 | 
						|
 | 
						|
	// MentionName captures the username and domain part from
 | 
						|
	// a mention string such as @whatever_user@example.org,
 | 
						|
	// returning whatever_user and example.org (without the @ symbols).
 | 
						|
	// Will also work for characters with umlauts and other accents.
 | 
						|
	// See: https://regex101.com/r/9tjNUy/1 for explanation and examples.
 | 
						|
	MentionName = regexp.MustCompile(mentionName)
 | 
						|
 | 
						|
	// MentionFinder extracts whole mentions from a piece of text.
 | 
						|
	MentionFinder = regexp.MustCompile(mentionFinder)
 | 
						|
 | 
						|
	// EmojiShortcode validates an emoji name.
 | 
						|
	EmojiShortcode = regexp.MustCompile(emojiShortcode)
 | 
						|
 | 
						|
	// EmojiFinder extracts emoji strings from a piece of text.
 | 
						|
	// See: https://regex101.com/r/478XGM/1
 | 
						|
	EmojiFinder = regexp.MustCompile(emojiFinder)
 | 
						|
 | 
						|
	// Username can be used to validate usernames of new signups on this instance.
 | 
						|
	Username = regexp.MustCompile(usernameStrict)
 | 
						|
 | 
						|
	// MisskeyReportNotes captures a list of Note URIs from report content created by Misskey.
 | 
						|
	// See: https://regex101.com/r/EnTOBV/1
 | 
						|
	MisskeyReportNotes = regexp.MustCompile(misskeyReportNotesFinder)
 | 
						|
 | 
						|
	// UserPath validates and captures the username part from eg /users/example_username.
 | 
						|
	UserPath = regexp.MustCompile(userPath)
 | 
						|
 | 
						|
	// PublicKeyPath parses a path that validates and captures the username part from eg /users/example_username/main-key
 | 
						|
	PublicKeyPath = regexp.MustCompile(publicKeyPath)
 | 
						|
 | 
						|
	// InboxPath parses a path that validates and captures the username part from eg /users/example_username/inbox
 | 
						|
	InboxPath = regexp.MustCompile(inboxPath)
 | 
						|
 | 
						|
	// OutboxPath parses a path that validates and captures the username part from eg /users/example_username/outbox
 | 
						|
	OutboxPath = regexp.MustCompile(outboxPath)
 | 
						|
 | 
						|
	// FollowersPath parses a path that validates and captures the username part from eg /users/example_username/followers
 | 
						|
	FollowersPath = regexp.MustCompile(followersPath)
 | 
						|
 | 
						|
	// FollowingPath parses a path that validates and captures the username part from eg /users/example_username/following
 | 
						|
	FollowingPath = regexp.MustCompile(followingPath)
 | 
						|
 | 
						|
	// LikedPath parses a path that validates and captures the username part from eg /users/example_username/liked
 | 
						|
	LikedPath = regexp.MustCompile(likedPath)
 | 
						|
 | 
						|
	// ULID parses and validate a ULID.
 | 
						|
	ULID = regexp.MustCompile(ulidValidate)
 | 
						|
 | 
						|
	// FollowPath parses a path that validates and captures the username part and the ulid part
 | 
						|
	// from eg /users/example_username/follow/01F7XT5JZW1WMVSW1KADS8PVDH
 | 
						|
	FollowPath = regexp.MustCompile(followPath)
 | 
						|
 | 
						|
	// LikePath parses a path that validates and captures the username part and the ulid part
 | 
						|
	// from eg /users/example_username/liked/01F7XT5JZW1WMVSW1KADS8PVDH
 | 
						|
	LikePath = regexp.MustCompile(likePath)
 | 
						|
 | 
						|
	// StatusesPath parses a path that validates and captures the username part and the ulid part
 | 
						|
	// from eg /users/example_username/statuses/01F7XT5JZW1WMVSW1KADS8PVDH
 | 
						|
	// The regex can be played with here: https://regex101.com/r/G9zuxQ/1
 | 
						|
	StatusesPath = regexp.MustCompile(statusesPath)
 | 
						|
 | 
						|
	// BlockPath parses a path that validates and captures the username part and the ulid part
 | 
						|
	// from eg /users/example_username/blocks/01F7XT5JZW1WMVSW1KADS8PVDH
 | 
						|
	BlockPath = regexp.MustCompile(blockPath)
 | 
						|
 | 
						|
	// ReportPath parses a path that validates and captures the ulid part
 | 
						|
	// from eg /reports/01GP3AWY4CRDVRNZKW0TEAMB5R
 | 
						|
	ReportPath = regexp.MustCompile(reportPath)
 | 
						|
 | 
						|
	// FilePath parses a file storage path of the form [ACCOUNT_ID]/[MEDIA_TYPE]/[MEDIA_SIZE]/[FILE_NAME]
 | 
						|
	// eg 01F8MH1H7YV1Z7D2C8K2730QBF/attachment/small/01F8MH8RMYQ6MSNY3JM2XT1CQ5.jpeg
 | 
						|
	// It captures the account id, media type, media size, file name, and file extension, eg
 | 
						|
	// `01F8MH1H7YV1Z7D2C8K2730QBF`, `attachment`, `small`, `01F8MH8RMYQ6MSNY3JM2XT1CQ5`, `jpeg`.
 | 
						|
	FilePath = regexp.MustCompile(filePath)
 | 
						|
)
 | 
						|
 | 
						|
// bufpool is a memory pool of byte buffers for use in our regex utility functions.
 | 
						|
var bufpool = sync.Pool{
 | 
						|
	New: func() any {
 | 
						|
		buf := bytes.NewBuffer(make([]byte, 0, 512))
 | 
						|
		return buf
 | 
						|
	},
 | 
						|
}
 | 
						|
 | 
						|
// ReplaceAllStringFunc will call through to .ReplaceAllStringFunc in the provided regex, but provide you a clean byte buffer for optimized string writes.
 | 
						|
func ReplaceAllStringFunc(rgx *regexp.Regexp, src string, repl func(match string, buf *bytes.Buffer) string) string {
 | 
						|
	buf := bufpool.Get().(*bytes.Buffer) //nolint
 | 
						|
	defer bufpool.Put(buf)
 | 
						|
	return rgx.ReplaceAllStringFunc(src, func(match string) string {
 | 
						|
		buf.Reset() // reset use
 | 
						|
		return repl(match, buf)
 | 
						|
	})
 | 
						|
}
 |