Browse Source

Merge pull request #2074 from slingamn/ircgo_upgrade

upgrade to irc-go v0.4.0
tags/v2.12.0-rc1
Shivaram Lingamneni 11 months ago
parent
commit
6d642bfe93
No account linked to committer's email address

+ 1
- 1
go.mod View File

@@ -8,7 +8,7 @@ require (
8 8
 	github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
9 9
 	github.com/ergochat/confusables v0.0.0-20201108231250-4ab98ab61fb1
10 10
 	github.com/ergochat/go-ident v0.0.0-20200511222032-830550b1d775
11
-	github.com/ergochat/irc-go v0.2.0
11
+	github.com/ergochat/irc-go v0.4.0
12 12
 	github.com/go-sql-driver/mysql v1.7.0
13 13
 	github.com/go-test/deep v1.0.6 // indirect
14 14
 	github.com/gofrs/flock v0.8.1

+ 2
- 2
go.sum View File

@@ -10,8 +10,8 @@ github.com/ergochat/confusables v0.0.0-20201108231250-4ab98ab61fb1 h1:WLHTOodthV
10 10
 github.com/ergochat/confusables v0.0.0-20201108231250-4ab98ab61fb1/go.mod h1:mov+uh1DPWsltdQnOdzn08UO9GsJ3MEvhtu0Ci37fdk=
11 11
 github.com/ergochat/go-ident v0.0.0-20200511222032-830550b1d775 h1:QSJIdpr3HOzJDPwxT7hp7WbjoZcS+5GqVvsBscqChk0=
12 12
 github.com/ergochat/go-ident v0.0.0-20200511222032-830550b1d775/go.mod h1:d2qvgjD0TvGNSvUs+mZgX090RiJlrzUYW6vtANGOy3A=
13
-github.com/ergochat/irc-go v0.2.0 h1:3vHdy4c56UTY6+/rTBrQc1fmt32N5G8PrEZacJDOr+E=
14
-github.com/ergochat/irc-go v0.2.0/go.mod h1:2vi7KNpIPWnReB5hmLpl92eMywQvuIeIIGdt/FQCph0=
13
+github.com/ergochat/irc-go v0.4.0 h1:0YibCKfAAtwxQdNjLQd9xpIEPisLcJ45f8FNsMHAuZc=
14
+github.com/ergochat/irc-go v0.4.0/go.mod h1:2vi7KNpIPWnReB5hmLpl92eMywQvuIeIIGdt/FQCph0=
15 15
 github.com/ergochat/scram v1.0.2-ergo1 h1:2bYXiRFQH636pT0msOG39fmEYl4Eq+OuutcyDsCix/g=
16 16
 github.com/ergochat/scram v1.0.2-ergo1/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
17 17
 github.com/ergochat/websocket v1.4.2-oragono1 h1:plMUunFBM6UoSCIYCKKclTdy/TkkHfUslhOfJQzfueM=

+ 3
- 3
irc/channel.go View File

@@ -13,7 +13,7 @@ import (
13 13
 
14 14
 	"sync"
15 15
 
16
-	"github.com/ergochat/irc-go/ircutils"
16
+	"github.com/ergochat/irc-go/ircmsg"
17 17
 
18 18
 	"github.com/ergochat/ergo/irc/caps"
19 19
 	"github.com/ergochat/ergo/irc/datastore"
@@ -1191,7 +1191,7 @@ func (channel *Channel) SetTopic(client *Client, topic string, rb *ResponseBuffe
1191 1191
 		return
1192 1192
 	}
1193 1193
 
1194
-	topic = ircutils.TruncateUTF8Safe(topic, client.server.Config().Limits.TopicLen)
1194
+	topic = ircmsg.TruncateUTF8Safe(topic, client.server.Config().Limits.TopicLen)
1195 1195
 
1196 1196
 	channel.stateMutex.Lock()
1197 1197
 	chname := channel.name
@@ -1450,7 +1450,7 @@ func (channel *Channel) Kick(client *Client, target *Client, comment string, rb
1450 1450
 		return
1451 1451
 	}
1452 1452
 
1453
-	comment = ircutils.TruncateUTF8Safe(comment, channel.server.Config().Limits.KickLen)
1453
+	comment = ircmsg.TruncateUTF8Safe(comment, channel.server.Config().Limits.KickLen)
1454 1454
 
1455 1455
 	message := utils.MakeMessage(comment)
1456 1456
 	details := client.Details()

+ 1
- 1
irc/handlers.go View File

@@ -451,7 +451,7 @@ func awayHandler(server *Server, client *Client, msg ircmsg.Message, rb *Respons
451 451
 	var awayMessage string
452 452
 	if len(msg.Params) > 0 {
453 453
 		awayMessage = msg.Params[0]
454
-		awayMessage = ircutils.TruncateUTF8Safe(awayMessage, server.Config().Limits.AwayLen)
454
+		awayMessage = ircmsg.TruncateUTF8Safe(awayMessage, server.Config().Limits.AwayLen)
455 455
 	}
456 456
 
457 457
 	wasAway, nowAway := rb.session.SetAway(awayMessage)

+ 137
- 57
vendor/github.com/ergochat/irc-go/ircfmt/ircfmt.go View File

@@ -5,6 +5,7 @@ package ircfmt
5 5
 
6 6
 import (
7 7
 	"regexp"
8
+	"strconv"
8 9
 	"strings"
9 10
 )
10 11
 
@@ -19,24 +20,126 @@ const (
19 20
 	underline     string = "\x1f"
20 21
 	reset         string = "\x0f"
21 22
 
22
-	runecolour        rune = '\x03'
23
-	runebold          rune = '\x02'
24
-	runemonospace     rune = '\x11'
25
-	runereverseColour rune = '\x16'
26
-	runeitalic        rune = '\x1d'
27
-	runestrikethrough rune = '\x1e'
28
-	runereset         rune = '\x0f'
29
-	runeunderline     rune = '\x1f'
30
-
31
-	// valid characters in a colour code character, for speed
32
-	colours1 string = "0123456789"
23
+	metacharacters = (bold + colour + monospace + reverseColour + italic + strikethrough + underline + reset)
33 24
 )
34 25
 
26
+// ColorCode is a normalized representation of an IRC color code,
27
+// as per this de facto specification: https://modern.ircdocs.horse/formatting.html#color
28
+// The zero value of the type represents a default or unset color,
29
+// whereas ColorCode{true, 0} represents the color white.
30
+type ColorCode struct {
31
+	IsSet bool
32
+	Value uint8
33
+}
34
+
35
+// ParseColor converts a string representation of an IRC color code, e.g. "04",
36
+// into a normalized ColorCode, e.g. ColorCode{true, 4}.
37
+func ParseColor(str string) (color ColorCode) {
38
+	// "99 - Default Foreground/Background - Not universally supported."
39
+	// normalize 99 to ColorCode{} meaning "unset":
40
+	if code, err := strconv.ParseUint(str, 10, 8); err == nil && code < 99 {
41
+		color.IsSet = true
42
+		color.Value = uint8(code)
43
+	}
44
+	return
45
+}
46
+
47
+// FormattedSubstring represents a section of an IRC message with associated
48
+// formatting data.
49
+type FormattedSubstring struct {
50
+	Content         string
51
+	ForegroundColor ColorCode
52
+	BackgroundColor ColorCode
53
+	Bold            bool
54
+	Monospace       bool
55
+	Strikethrough   bool
56
+	Underline       bool
57
+	Italic          bool
58
+	ReverseColor    bool
59
+}
60
+
61
+// IsFormatted returns whether the section has any formatting flags switched on.
62
+func (f *FormattedSubstring) IsFormatted() bool {
63
+	// could rely on value receiver but if this is to be a public API,
64
+	// let's make it a pointer receiver
65
+	g := *f
66
+	g.Content = ""
67
+	return g != FormattedSubstring{}
68
+}
69
+
70
+var (
71
+	// "If there are two ASCII digits available where a <COLOR> is allowed,
72
+	// then two characters MUST always be read for it and displayed as described below."
73
+	// we rely on greedy matching to implement this for both forms:
74
+	// (\x03)00,01
75
+	colorForeBackRe = regexp.MustCompile(`^([0-9]{1,2}),([0-9]{1,2})`)
76
+	// (\x03)00
77
+	colorForeRe = regexp.MustCompile(`^([0-9]{1,2})`)
78
+)
79
+
80
+// Split takes an IRC message (typically a PRIVMSG or NOTICE final parameter)
81
+// containing IRC formatting control codes, and splits it into substrings with
82
+// associated formatting information.
83
+func Split(raw string) (result []FormattedSubstring) {
84
+	var chunk FormattedSubstring
85
+	for {
86
+		// skip to the next metacharacter, or the end of the string
87
+		if idx := strings.IndexAny(raw, metacharacters); idx != 0 {
88
+			if idx == -1 {
89
+				idx = len(raw)
90
+			}
91
+			chunk.Content = raw[:idx]
92
+			if len(chunk.Content) != 0 {
93
+				result = append(result, chunk)
94
+			}
95
+			raw = raw[idx:]
96
+		}
97
+
98
+		if len(raw) == 0 {
99
+			return
100
+		}
101
+
102
+		// we're at a metacharacter. by default, all previous formatting carries over
103
+		metacharacter := raw[0]
104
+		raw = raw[1:]
105
+		switch metacharacter {
106
+		case bold[0]:
107
+			chunk.Bold = !chunk.Bold
108
+		case monospace[0]:
109
+			chunk.Monospace = !chunk.Monospace
110
+		case strikethrough[0]:
111
+			chunk.Strikethrough = !chunk.Strikethrough
112
+		case underline[0]:
113
+			chunk.Underline = !chunk.Underline
114
+		case italic[0]:
115
+			chunk.Italic = !chunk.Italic
116
+		case reverseColour[0]:
117
+			chunk.ReverseColor = !chunk.ReverseColor
118
+		case reset[0]:
119
+			chunk = FormattedSubstring{}
120
+		case colour[0]:
121
+			// preferentially match the "\x0399,01" form, then "\x0399";
122
+			// if neither of those matches, then it's a reset
123
+			if matches := colorForeBackRe.FindStringSubmatch(raw); len(matches) != 0 {
124
+				chunk.ForegroundColor = ParseColor(matches[1])
125
+				chunk.BackgroundColor = ParseColor(matches[2])
126
+				raw = raw[len(matches[0]):]
127
+			} else if matches := colorForeRe.FindStringSubmatch(raw); len(matches) != 0 {
128
+				chunk.ForegroundColor = ParseColor(matches[1])
129
+				raw = raw[len(matches[0]):]
130
+			} else {
131
+				chunk.ForegroundColor = ColorCode{}
132
+				chunk.BackgroundColor = ColorCode{}
133
+			}
134
+		default:
135
+			// should be impossible, but just ignore it
136
+		}
137
+	}
138
+}
139
+
35 140
 var (
36 141
 	// valtoescape replaces most of IRC characters with our escapes.
37 142
 	valtoescape = strings.NewReplacer("$", "$$", colour, "$c", reverseColour, "$v", bold, "$b", italic, "$i", strikethrough, "$s", underline, "$u", monospace, "$m", reset, "$r")
38
-	// valToStrip replaces most of the IRC characters with nothing
39
-	valToStrip = strings.NewReplacer(colour, "$c", reverseColour, "", bold, "", italic, "", strikethrough, "", underline, "", monospace, "", reset, "")
40 143
 
41 144
 	// escapetoval contains most of our escapes and how they map to real IRC characters.
42 145
 	// intentionally skips colour, since that's handled elsewhere.
@@ -98,7 +201,9 @@ var (
98 201
 		"light blue":  "12",
99 202
 		"pink":        "13",
100 203
 		"grey":        "14",
204
+		"gray":        "14",
101 205
 		"light grey":  "15",
206
+		"light gray":  "15",
102 207
 		"default":     "99",
103 208
 	}
104 209
 
@@ -123,7 +228,7 @@ func Escape(in string) string {
123 228
 			out.WriteString("$c")
124 229
 			inRunes = inRunes[2:] // strip colour code chars
125 230
 
126
-			if len(inRunes) < 1 || !strings.Contains(colours1, string(inRunes[0])) {
231
+			if len(inRunes) < 1 || !isDigit(inRunes[0]) {
127 232
 				out.WriteString("[]")
128 233
 				continue
129 234
 			}
@@ -131,14 +236,14 @@ func Escape(in string) string {
131 236
 			var foreBuffer, backBuffer string
132 237
 			foreBuffer += string(inRunes[0])
133 238
 			inRunes = inRunes[1:]
134
-			if 0 < len(inRunes) && strings.Contains(colours1, string(inRunes[0])) {
239
+			if 0 < len(inRunes) && isDigit(inRunes[0]) {
135 240
 				foreBuffer += string(inRunes[0])
136 241
 				inRunes = inRunes[1:]
137 242
 			}
138
-			if 1 < len(inRunes) && inRunes[0] == ',' && strings.Contains(colours1, string(inRunes[1])) {
243
+			if 1 < len(inRunes) && inRunes[0] == ',' && isDigit(inRunes[1]) {
139 244
 				backBuffer += string(inRunes[1])
140 245
 				inRunes = inRunes[2:]
141
-				if 0 < len(inRunes) && strings.Contains(colours1, string(inRunes[0])) {
246
+				if 0 < len(inRunes) && isDigit(inRunes[1]) {
142 247
 					backBuffer += string(inRunes[0])
143 248
 					inRunes = inRunes[1:]
144 249
 				}
@@ -178,52 +283,27 @@ func Escape(in string) string {
178 283
 	return out.String()
179 284
 }
180 285
 
286
+func isDigit(r rune) bool {
287
+	return '0' <= r && r <= '9' // don't use unicode.IsDigit, it includes non-ASCII numerals
288
+}
289
+
181 290
 // Strip takes a raw IRC string and removes it with all formatting codes removed
182 291
 // IE, it turns this: "This is a \x02cool\x02, \x034red\x0f message!"
183 292
 // into: "This is a cool, red message!"
184 293
 func Strip(in string) string {
185
-	out := strings.Builder{}
186
-	runes := []rune(in)
187
-	if out.Len() < len(runes) { // Reduce allocations where needed
188
-		out.Grow(len(in) - out.Len())
189
-	}
190
-	for len(runes) > 0 {
191
-		switch runes[0] {
192
-		case runebold, runemonospace, runereverseColour, runeitalic, runestrikethrough, runeunderline, runereset:
193
-			runes = runes[1:]
194
-		case runecolour:
195
-			runes = removeColour(runes)
196
-		default:
197
-			out.WriteRune(runes[0])
198
-			runes = runes[1:]
199
-		}
200
-	}
201
-	return out.String()
202
-}
203
-
204
-func removeNumber(runes []rune) []rune {
205
-	if len(runes) > 0 && runes[0] >= '0' && runes[0] <= '9' {
206
-		runes = runes[1:]
207
-	}
208
-	return runes
209
-}
210
-
211
-func removeColour(runes []rune) []rune {
212
-	if runes[0] != runecolour {
213
-		return runes
214
-	}
215
-
216
-	runes = runes[1:]
217
-	runes = removeNumber(runes)
218
-	runes = removeNumber(runes)
219
-
220
-	if len(runes) > 1 && runes[0] == ',' && runes[1] >= '0' && runes[1] <= '9' {
221
-		runes = runes[2:]
294
+	splitChunks := Split(in)
295
+	if len(splitChunks) == 0 {
296
+		return ""
297
+	} else if len(splitChunks) == 1 {
298
+		return splitChunks[0].Content
222 299
 	} else {
223
-		return runes // Nothing else because we dont have a comma
300
+		var buf strings.Builder
301
+		buf.Grow(len(in))
302
+		for _, chunk := range splitChunks {
303
+			buf.WriteString(chunk.Content)
304
+		}
305
+		return buf.String()
224 306
 	}
225
-	runes = removeNumber(runes)
226
-	return runes
227 307
 }
228 308
 
229 309
 // resolve "light blue" to "12", "12" to "12", "asdf" to "", etc.

+ 1
- 1
vendor/github.com/ergochat/irc-go/ircmsg/message.go View File

@@ -238,7 +238,7 @@ func parseLine(line string, maxTagDataLength int, truncateLen int) (ircmsg Messa
238 238
 	// truncate if desired
239 239
 	if truncateLen != 0 && truncateLen < len(line) {
240 240
 		err = ErrorBodyTooLong
241
-		line = line[:truncateLen]
241
+		line = TruncateUTF8Safe(line, truncateLen)
242 242
 	}
243 243
 
244 244
 	// modern: "These message parts, and parameters themselves, are separated

+ 29
- 0
vendor/github.com/ergochat/irc-go/ircmsg/unicode.go View File

@@ -0,0 +1,29 @@
1
+// Copyright (c) 2021 Shivaram Lingamneni
2
+// Released under the MIT License
3
+
4
+package ircmsg
5
+
6
+import (
7
+	"unicode/utf8"
8
+)
9
+
10
+// TruncateUTF8Safe truncates a message, respecting UTF8 boundaries. If a message
11
+// was originally valid UTF8, TruncateUTF8Safe will not make it invalid; instead
12
+// it will truncate additional bytes as needed, back to the last valid
13
+// UTF8-encoded codepoint. If a message is not UTF8, TruncateUTF8Safe will truncate
14
+// at most 3 additional bytes before giving up.
15
+func TruncateUTF8Safe(message string, byteLimit int) (result string) {
16
+	if len(message) <= byteLimit {
17
+		return message
18
+	}
19
+	message = message[:byteLimit]
20
+	for i := 0; i < (utf8.UTFMax - 1); i++ {
21
+		r, n := utf8.DecodeLastRuneInString(message)
22
+		if r == utf8.RuneError && n <= 1 {
23
+			message = message[:len(message)-1]
24
+		} else {
25
+			break
26
+		}
27
+	}
28
+	return message
29
+}

+ 3
- 16
vendor/github.com/ergochat/irc-go/ircutils/unicode.go View File

@@ -7,24 +7,11 @@ import (
7 7
 	"strings"
8 8
 	"unicode"
9 9
 	"unicode/utf8"
10
+
11
+	"github.com/ergochat/irc-go/ircmsg"
10 12
 )
11 13
 
12
-// truncate a message, taking care not to make valid UTF8 into invalid UTF8
13
-func TruncateUTF8Safe(message string, byteLimit int) (result string) {
14
-	if len(message) <= byteLimit {
15
-		return message
16
-	}
17
-	message = message[:byteLimit]
18
-	for i := 0; i < (utf8.UTFMax - 1); i++ {
19
-		r, n := utf8.DecodeLastRuneInString(message)
20
-		if r == utf8.RuneError && n <= 1 {
21
-			message = message[:len(message)-1]
22
-		} else {
23
-			break
24
-		}
25
-	}
26
-	return message
27
-}
14
+var TruncateUTF8Safe = ircmsg.TruncateUTF8Safe
28 15
 
29 16
 // Sanitizes human-readable text to make it safe for IRC;
30 17
 // assumes UTF-8 and uses the replacement character where

+ 1
- 1
vendor/modules.txt View File

@@ -16,7 +16,7 @@ github.com/ergochat/confusables
16 16
 # github.com/ergochat/go-ident v0.0.0-20200511222032-830550b1d775
17 17
 ## explicit
18 18
 github.com/ergochat/go-ident
19
-# github.com/ergochat/irc-go v0.2.0
19
+# github.com/ergochat/irc-go v0.4.0
20 20
 ## explicit; go 1.15
21 21
 github.com/ergochat/irc-go/ircfmt
22 22
 github.com/ergochat/irc-go/ircmsg

Loading…
Cancel
Save