1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
package main
import (
"strings"
)
type Msg struct {
RawMessage string
RawSource string
Command string
Tags map[string]string
Params []string
}
// Partially adapted from https://github.com/ergochat/irc-go.git
func parseIRCMsg(line string) (msg Msg, err error) {
msg = Msg{
RawMessage: line,
}
line = strings.TrimSuffix(line, "\n")
line = strings.TrimSuffix(line, "\r")
if len(line) == 0 {
err = ErrEmptyMessage
return
}
for _, v := range line {
if v == '\x00' || v == '\r' || v == '\n' {
err = ErrIllegalByte
return
}
}
// IRCv3 tags
if line[0] == '@' {
tagEnd := strings.IndexByte(line, ' ')
if tagEnd == -1 {
err = ErrEmptyMessage
return
}
tagsString := line[1:tagEnd]
if 0 < MaxlenTagData && MaxlenTagData < len(tagsString) {
err = ErrTagsTooLong
return
}
msg.Tags, err = parseTags(tagsString)
if err != nil {
return
}
// Skip over the tags and the separating space
line = line[tagEnd+1:]
}
if len(line) > MaxlenBody {
err = ErrBodyTooLong
line = line[:MaxlenBody]
}
line = trimInitialSpaces(line)
// Source
if 0 < len(line) && line[0] == ':' {
sourceEnd := strings.IndexByte(line, ' ')
if sourceEnd == -1 {
err = ErrEmptyMessage
return
}
msg.RawSource = line[1:sourceEnd]
// Skip over the source and the separating space
line = line[sourceEnd+1:]
}
// Command
commandEnd := strings.IndexByte(line, ' ')
paramStart := commandEnd + 1
if commandEnd == -1 {
commandEnd = len(line)
paramStart = len(line)
}
baseCommand := line[:commandEnd]
if len(baseCommand) == 0 {
err = ErrEmptyMessage
return
}
// TODO: Actually must be either letters or a 3-digit numeric
if !isASCII(baseCommand) {
err = ErrIllegalByte
return
}
msg.Command = strings.ToUpper(baseCommand)
line = line[paramStart:]
// Other arguments
for {
line = trimInitialSpaces(line)
if len(line) == 0 {
break
}
// Trailing
if line[0] == ':' {
msg.Params = append(msg.Params, line[1:])
break
}
paramEnd := strings.IndexByte(line, ' ')
if paramEnd == -1 {
msg.Params = append(msg.Params, line)
break
}
msg.Params = append(msg.Params, line[:paramEnd])
line = line[paramEnd+1:]
}
return
}
func isASCII(str string) bool {
for i := 0; i < len(str); i++ {
if str[i] > 127 {
return false
}
}
return true
}
|