Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

tags.go 2.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. // written by Daniel Oaks <daniel@danieloaks.net>
  2. // released under the ISC license
  3. package ircmsg
  4. import (
  5. "strings"
  6. "unicode/utf8"
  7. )
  8. var (
  9. // valtoescape replaces real characters with message tag escapes.
  10. valtoescape = strings.NewReplacer("\\", "\\\\", ";", "\\:", " ", "\\s", "\r", "\\r", "\n", "\\n")
  11. escapedCharLookupTable [256]byte
  12. )
  13. func init() {
  14. // most chars escape to themselves
  15. for i := 0; i < 256; i += 1 {
  16. escapedCharLookupTable[i] = byte(i)
  17. }
  18. // these are the exceptions
  19. escapedCharLookupTable[':'] = ';'
  20. escapedCharLookupTable['s'] = ' '
  21. escapedCharLookupTable['r'] = '\r'
  22. escapedCharLookupTable['n'] = '\n'
  23. }
  24. // EscapeTagValue takes a value, and returns an escaped message tag value.
  25. //
  26. // This function is automatically used when lines are created from an
  27. // Message, so you don't need to call it yourself before creating a line.
  28. func EscapeTagValue(inString string) string {
  29. return valtoescape.Replace(inString)
  30. }
  31. // UnescapeTagValue takes an escaped message tag value, and returns the raw value.
  32. //
  33. // This function is automatically used when lines are interpreted by ParseLine,
  34. // so you don't need to call it yourself after parsing a line.
  35. func UnescapeTagValue(inString string) string {
  36. // buf.Len() == 0 is the fastpath where we have not needed to unescape any chars
  37. var buf strings.Builder
  38. remainder := inString
  39. for {
  40. backslashPos := strings.IndexByte(remainder, '\\')
  41. if backslashPos == -1 {
  42. if buf.Len() == 0 {
  43. return inString
  44. } else {
  45. buf.WriteString(remainder)
  46. break
  47. }
  48. } else if backslashPos == len(remainder)-1 {
  49. // trailing backslash, which we strip
  50. if buf.Len() == 0 {
  51. return inString[:len(inString)-1]
  52. } else {
  53. buf.WriteString(remainder[:len(remainder)-1])
  54. break
  55. }
  56. }
  57. // non-trailing backslash detected; we're now on the slowpath
  58. // where we modify the string
  59. if buf.Len() == 0 {
  60. buf.Grow(len(inString)) // just an optimization
  61. }
  62. buf.WriteString(remainder[:backslashPos])
  63. buf.WriteByte(escapedCharLookupTable[remainder[backslashPos+1]])
  64. remainder = remainder[backslashPos+2:]
  65. }
  66. return buf.String()
  67. }
  68. // https://ircv3.net/specs/extensions/message-tags.html#rules-for-naming-message-tags
  69. func validateTagName(name string) bool {
  70. if len(name) == 0 {
  71. return false
  72. }
  73. if name[0] == '+' {
  74. name = name[1:]
  75. }
  76. if len(name) == 0 {
  77. return false
  78. }
  79. // let's err on the side of leniency here; allow -./ (45-47) in any position
  80. for i := 0; i < len(name); i++ {
  81. c := name[i]
  82. if !(('-' <= c && c <= '/') || ('0' <= c && c <= '9') || ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')) {
  83. return false
  84. }
  85. }
  86. return true
  87. }
  88. // "Tag values MUST be encoded as UTF8."
  89. func validateTagValue(value string) bool {
  90. return utf8.ValidString(value)
  91. }