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.

text.go 3.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. // Copyright (c) 2017 Daniel Oaks <daniel@danieloaks.net>
  2. // released under the MIT license
  3. package utils
  4. import (
  5. "bytes"
  6. "strings"
  7. "time"
  8. )
  9. func IsRestrictedCTCPMessage(message string) bool {
  10. // block all CTCP privmsgs to Tor clients except for ACTION
  11. // DCC can potentially be used for deanonymization, the others for fingerprinting
  12. return strings.HasPrefix(message, "\x01") && !strings.HasPrefix(message, "\x01ACTION")
  13. }
  14. type MessagePair struct {
  15. Message string
  16. Concat bool // should be relayed with the multiline-concat tag
  17. }
  18. // SplitMessage represents a message that's been split for sending.
  19. // Two possibilities:
  20. // (a) Standard message that can be relayed on a single 512-byte line
  21. // (MessagePair contains the message, Split == nil)
  22. // (b) multiline message that was split on the client side
  23. // (Message == "", Split contains the split lines)
  24. type SplitMessage struct {
  25. Message string
  26. Msgid string
  27. Split []MessagePair
  28. Time time.Time
  29. }
  30. func MakeMessage(original string) (result SplitMessage) {
  31. result.Message = original
  32. result.Msgid = GenerateSecretToken()
  33. result.SetTime()
  34. return
  35. }
  36. func (sm *SplitMessage) Append(message string, concat bool) {
  37. if sm.Msgid == "" {
  38. sm.Msgid = GenerateSecretToken()
  39. }
  40. sm.Split = append(sm.Split, MessagePair{
  41. Message: message,
  42. Concat: concat,
  43. })
  44. }
  45. func (sm *SplitMessage) SetTime() {
  46. // strip the monotonic time, it's a potential source of problems:
  47. sm.Time = time.Now().UTC().Round(0)
  48. }
  49. func (sm *SplitMessage) LenLines() int {
  50. if sm.Split == nil {
  51. if sm.Message == "" {
  52. return 0
  53. } else {
  54. return 1
  55. }
  56. }
  57. return len(sm.Split)
  58. }
  59. func (sm *SplitMessage) ValidMultiline() bool {
  60. // must contain at least one nonblank line
  61. for i := 0; i < len(sm.Split); i++ {
  62. if len(sm.Split[i].Message) != 0 {
  63. return true
  64. }
  65. }
  66. return false
  67. }
  68. func (sm *SplitMessage) IsRestrictedCTCPMessage() bool {
  69. if IsRestrictedCTCPMessage(sm.Message) {
  70. return true
  71. }
  72. for i := 0; i < len(sm.Split); i++ {
  73. if IsRestrictedCTCPMessage(sm.Split[i].Message) {
  74. return true
  75. }
  76. }
  77. return false
  78. }
  79. func (sm *SplitMessage) Is512() bool {
  80. return sm.Split == nil
  81. }
  82. // TokenLineBuilder is a helper for building IRC lines composed of delimited tokens,
  83. // with a maximum line length.
  84. type TokenLineBuilder struct {
  85. lineLen int
  86. delim string
  87. buf bytes.Buffer
  88. result []string
  89. }
  90. func (t *TokenLineBuilder) Initialize(lineLen int, delim string) {
  91. t.lineLen = lineLen
  92. t.delim = delim
  93. }
  94. // Add adds a token to the line, creating a new line if necessary.
  95. func (t *TokenLineBuilder) Add(token string) {
  96. tokenLen := len(token)
  97. if t.buf.Len() != 0 {
  98. tokenLen += len(t.delim)
  99. }
  100. if t.lineLen < t.buf.Len()+tokenLen {
  101. t.result = append(t.result, t.buf.String())
  102. t.buf.Reset()
  103. }
  104. if t.buf.Len() != 0 {
  105. t.buf.WriteString(t.delim)
  106. }
  107. t.buf.WriteString(token)
  108. }
  109. // Lines terminates the line-building and returns all the lines.
  110. func (t *TokenLineBuilder) Lines() (result []string) {
  111. result = t.result
  112. t.result = nil
  113. if t.buf.Len() != 0 {
  114. result = append(result, t.buf.String())
  115. t.buf.Reset()
  116. }
  117. return
  118. }