You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

strings.go 2.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. // Copyright (c) 2012-2014 Jeremy Latt
  2. // Copyright (c) 2014-2015 Edmund Huber
  3. // Copyright (c) 2016- Daniel Oaks <daniel@danieloaks.net>
  4. // released under the MIT license
  5. package irc
  6. import (
  7. "regexp"
  8. "strings"
  9. "golang.org/x/text/unicode/norm"
  10. )
  11. var (
  12. // regexps
  13. ChannelNameExpr = regexp.MustCompile(`^[&!#+][\pL\pN]{1,63}$`)
  14. NicknameExpr = regexp.MustCompile("^[\\pL\\pN\\pP\\pS]{1,32}$")
  15. )
  16. // Names are normalized and canonicalized to remove formatting marks
  17. // and simplify usage. They are things like hostnames and usermasks.
  18. type Name string
  19. func NewName(str string) Name {
  20. return Name(norm.NFKC.String(str))
  21. }
  22. func NewNames(strs []string) []Name {
  23. names := make([]Name, len(strs))
  24. for index, str := range strs {
  25. names[index] = NewName(str)
  26. }
  27. return names
  28. }
  29. // tests
  30. func (name Name) IsChannel() bool {
  31. return ChannelNameExpr.MatchString(name.String())
  32. }
  33. func (name Name) IsNickname() bool {
  34. namestr := name.String()
  35. // * is used for unregistered clients
  36. // * is used for mask matching
  37. // ? is used for mask matching
  38. // , is used as a separator by the protocol
  39. // ! separates username from nickname
  40. // @ separates nick+user from hostname
  41. // # is a channel prefix
  42. // @+ are channel membership prefixes
  43. if strings.Contains(namestr, "*") || strings.Contains(namestr, "?") ||
  44. strings.Contains(namestr, ",") || strings.Contains(namestr, "!") ||
  45. strings.Contains(namestr, "@") || strings.Contains("#@+", string(namestr[0])) {
  46. return false
  47. }
  48. // names that look like hostnames are restricted to servers, as with other ircds
  49. if IsHostname(namestr) {
  50. return false
  51. }
  52. return NicknameExpr.MatchString(namestr)
  53. }
  54. // conversions
  55. func (name Name) String() string {
  56. return string(name)
  57. }
  58. func (name Name) ToLower() Name {
  59. return Name(strings.ToLower(name.String()))
  60. }
  61. // It's safe to coerce a Name to Text. Name is a strict subset of Text.
  62. func (name Name) Text() Text {
  63. return Text(name)
  64. }
  65. // Text is PRIVMSG, NOTICE, or TOPIC data. It's canonicalized UTF8
  66. // data to simplify but keeps all formatting.
  67. type Text string
  68. func NewText(str string) Text {
  69. return Text(norm.NFC.String(str))
  70. }
  71. func (text Text) String() string {
  72. return string(text)
  73. }
  74. // CTCPText is text suitably escaped for CTCP.
  75. type CTCPText string
  76. var ctcpEscaper = strings.NewReplacer("\x00", "\x200", "\n", "\x20n", "\r", "\x20r")
  77. func NewCTCPText(str string) CTCPText {
  78. return CTCPText(ctcpEscaper.Replace(str))
  79. }