@@ -14,6 +14,7 @@ import (
1414 "path/filepath"
1515 "regexp"
1616 "strings"
17+ "sync"
1718 "time"
1819 "unicode"
1920
@@ -417,19 +418,9 @@ func (u *User) DisplayName() string {
417418 return u .Name
418419}
419420
420- var emailToReplacer = strings .NewReplacer (
421- "\n " , "" ,
422- "\r " , "" ,
423- "<" , "" ,
424- ">" , "" ,
425- "," , "" ,
426- ":" , "" ,
427- ";" , "" ,
428- )
429-
430421// EmailTo returns a string suitable to be put into a e-mail `To:` header.
431422func (u * User ) EmailTo () string {
432- sanitizedDisplayName := emailToReplacer .Replace (u .DisplayName ())
423+ sanitizedDisplayName := globalVars (). emailToReplacer .Replace (u .DisplayName ())
433424
434425 // should be an edge case but nice to have
435426 if sanitizedDisplayName == u .Email {
@@ -526,28 +517,52 @@ func GetUserSalt() (string, error) {
526517 if err != nil {
527518 return "" , err
528519 }
529- // Returns a 32 bytes long string.
520+ // Returns a 32-byte long string.
530521 return hex .EncodeToString (rBytes ), nil
531522}
532523
533- // Note: The set of characters here can safely expand without a breaking change,
534- // but characters removed from this set can cause user account linking to break
535- var (
536- customCharsReplacement = strings .NewReplacer ("Æ" , "AE" )
537- removeCharsRE = regexp .MustCompile ("['`´]" )
538- transformDiacritics = transform .Chain (norm .NFD , runes .Remove (runes .In (unicode .Mn )), norm .NFC )
539- replaceCharsHyphenRE = regexp .MustCompile (`[\s~+]` )
540- )
524+ type globalVarsStruct struct {
525+ customCharsReplacement * strings.Replacer
526+ removeCharsRE * regexp.Regexp
527+ transformDiacritics transform.Transformer
528+ replaceCharsHyphenRE * regexp.Regexp
529+ emailToReplacer * strings.Replacer
530+ emailRegexp * regexp.Regexp
531+ }
532+
533+ var globalVars = sync .OnceValue (func () * globalVarsStruct {
534+ return & globalVarsStruct {
535+ // Note: The set of characters here can safely expand without a breaking change,
536+ // but characters removed from this set can cause user account linking to break
537+ customCharsReplacement : strings .NewReplacer ("Æ" , "AE" ),
538+
539+ removeCharsRE : regexp .MustCompile ("['`´]" ),
540+ transformDiacritics : transform .Chain (norm .NFD , runes .Remove (runes .In (unicode .Mn )), norm .NFC ),
541+ replaceCharsHyphenRE : regexp .MustCompile (`[\s~+]` ),
542+
543+ emailToReplacer : strings .NewReplacer (
544+ "\n " , "" ,
545+ "\r " , "" ,
546+ "<" , "" ,
547+ ">" , "" ,
548+ "," , "" ,
549+ ":" , "" ,
550+ ";" , "" ,
551+ ),
552+ emailRegexp : regexp .MustCompile ("^[a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\ .[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$" ),
553+ }
554+ })
541555
542556// NormalizeUserName only takes the name part if it is an email address, transforms it diacritics to ASCII characters.
543557// It returns a string with the single-quotes removed, and any other non-supported username characters are replaced with a `-` character
544558func NormalizeUserName (s string ) (string , error ) {
559+ vars := globalVars ()
545560 s , _ , _ = strings .Cut (s , "@" )
546- strDiacriticsRemoved , n , err := transform .String (transformDiacritics , customCharsReplacement .Replace (s ))
561+ strDiacriticsRemoved , n , err := transform .String (vars . transformDiacritics , vars . customCharsReplacement .Replace (s ))
547562 if err != nil {
548563 return "" , fmt .Errorf ("failed to normalize the string of provided username %q at position %d" , s , n )
549564 }
550- return replaceCharsHyphenRE .ReplaceAllLiteralString (removeCharsRE .ReplaceAllLiteralString (strDiacriticsRemoved , "" ), "-" ), nil
565+ return vars . replaceCharsHyphenRE .ReplaceAllLiteralString (vars . removeCharsRE .ReplaceAllLiteralString (strDiacriticsRemoved , "" ), "-" ), nil
551566}
552567
553568var (
0 commit comments