|
@@ -9,7 +9,6 @@ import (
|
9
|
9
|
"bytes"
|
10
|
10
|
"errors"
|
11
|
11
|
"strings"
|
12
|
|
- "unicode/utf8"
|
13
|
12
|
)
|
14
|
13
|
|
15
|
14
|
const (
|
|
@@ -41,8 +40,8 @@ var (
|
41
|
40
|
// (the name references 417 ERR_INPUTTOOLONG; we reserve the right to return it
|
42
|
41
|
// for messages that exceed the non-tag length limit)
|
43
|
42
|
ErrorLineTooLong = errors.New("Line could not be parsed because a specified length limit was exceeded")
|
44
|
|
- // ErrorInvalidTagContent indicates that a tag value was invalid
|
45
|
|
- ErrorInvalidTagContent = errors.New("Line could not be parsed because it contained an invalid tag value")
|
|
43
|
+ // ErrorInvalidTagContent indicates that a tag name or value was invalid
|
|
44
|
+ ErrorInvalidTagContent = errors.New("Line could not be processed because it contained an invalid tag name or value")
|
46
|
45
|
|
47
|
46
|
ErrorCommandMissing = errors.New("IRC messages MUST have a command")
|
48
|
47
|
ErrorBadParam = errors.New("Cannot have an empty param, a param with spaces, or a param that starts with ':' before the last parameter")
|
|
@@ -168,19 +167,12 @@ func trimInitialSpaces(str string) string {
|
168
|
167
|
}
|
169
|
168
|
|
170
|
169
|
func parseLine(line string, maxTagDataLength int, truncateLen int) (ircmsg IRCMessage, err error) {
|
171
|
|
- if strings.IndexByte(line, '\x00') != -1 {
|
172
|
|
- err = ErrorLineContainsBadChar
|
173
|
|
- return
|
174
|
|
- }
|
175
|
|
-
|
176
|
|
- // trim to the first appearance of either '\r' or '\n':
|
177
|
|
- lineEnd := strings.IndexByte(line, '\r')
|
178
|
|
- newlineIndex := strings.IndexByte(line, '\n')
|
179
|
|
- if newlineIndex != -1 && (lineEnd == -1 || newlineIndex < lineEnd) {
|
180
|
|
- lineEnd = newlineIndex
|
181
|
|
- }
|
182
|
|
- if lineEnd != -1 {
|
183
|
|
- line = line[:lineEnd]
|
|
170
|
+ // remove either \n or \r\n from the end of the line:
|
|
171
|
+ line = strings.TrimSuffix(line, "\n")
|
|
172
|
+ line = strings.TrimSuffix(line, "\r")
|
|
173
|
+ // now validate for the 3 forbidden bytes:
|
|
174
|
+ if strings.IndexByte(line, '\x00') != -1 || strings.IndexByte(line, '\n') != -1 || strings.IndexByte(line, '\r') != -1 {
|
|
175
|
+ return ircmsg, ErrorLineContainsBadChar
|
184
|
176
|
}
|
185
|
177
|
|
186
|
178
|
if len(line) < 1 {
|
|
@@ -285,8 +277,7 @@ func (ircmsg *IRCMessage) parseTags(tags string) (err error) {
|
285
|
277
|
// "Implementations [...] MUST NOT perform any validation that would
|
286
|
278
|
// reject the message if an invalid tag key name is used."
|
287
|
279
|
if validateTagName(tagName) {
|
288
|
|
- // "Tag values MUST be encoded as UTF8."
|
289
|
|
- if !utf8.ValidString(tagValue) {
|
|
280
|
+ if !validateTagValue(tagValue) {
|
290
|
281
|
return ErrorInvalidTagContent
|
291
|
282
|
}
|
292
|
283
|
ircmsg.SetTag(tagName, UnescapeTagValue(tagValue))
|
|
@@ -356,10 +347,14 @@ func (ircmsg *IRCMessage) line(tagLimit, clientOnlyTagDataLimit, serverAddedTagD
|
356
|
347
|
// write the tags, computing the budgets for client-only tags and regular tags
|
357
|
348
|
var lenRegularTags, lenClientOnlyTags, lenTags int
|
358
|
349
|
if 0 < len(ircmsg.tags) || 0 < len(ircmsg.clientOnlyTags) {
|
|
350
|
+ var tagError error
|
359
|
351
|
buf.WriteByte('@')
|
360
|
352
|
firstTag := true
|
361
|
353
|
writeTags := func(tags map[string]string) {
|
362
|
354
|
for tag, val := range tags {
|
|
355
|
+ if !(validateTagName(tag) && validateTagValue(val)) {
|
|
356
|
+ tagError = ErrorInvalidTagContent
|
|
357
|
+ }
|
363
|
358
|
if !firstTag {
|
364
|
359
|
buf.WriteByte(';') // delimiter
|
365
|
360
|
}
|
|
@@ -380,6 +375,9 @@ func (ircmsg *IRCMessage) line(tagLimit, clientOnlyTagDataLimit, serverAddedTagD
|
380
|
375
|
lenClientOnlyTags -= 1
|
381
|
376
|
}
|
382
|
377
|
buf.WriteByte(' ')
|
|
378
|
+ if tagError != nil {
|
|
379
|
+ return nil, tagError
|
|
380
|
+ }
|
383
|
381
|
}
|
384
|
382
|
lenTags = buf.Len()
|
385
|
383
|
|