implement the escaping rules
This commit is contained in:
parent
765d35b399
commit
092bc50199
48
message.go
48
message.go
@ -42,8 +42,8 @@ func (m *Message) parseTags() (map[string]string, error) {
|
||||
key := m.Raw[m.parseIndex : m.parseIndex+next]
|
||||
m.parseIndex += next + 1
|
||||
eot := m.findNext(";", " ")
|
||||
tags[key] = m.Raw[m.parseIndex : m.parseIndex+eot]
|
||||
m.parseIndex += len(tags[key]) + 1
|
||||
tags[key] = unescapeTag(m.Raw[m.parseIndex : m.parseIndex+eot])
|
||||
m.parseIndex += len(m.Raw[m.parseIndex:m.parseIndex+eot]) + 1
|
||||
} else if m.Raw[m.parseIndex+next] == ';' {
|
||||
key := m.Raw[m.parseIndex : m.parseIndex+next]
|
||||
m.parseIndex += next + 1
|
||||
@ -69,9 +69,10 @@ func (m *Message) parseCommand() (command, error) {
|
||||
start := m.parseIndex
|
||||
endofparse := strings.Index(m.Raw[m.parseIndex:], " ")
|
||||
if endofparse == -1 {
|
||||
return "", fmt.Errorf("end of string encountered while parsing command")
|
||||
m.parseIndex = len(m.Raw) // a command with no parameters
|
||||
} else {
|
||||
m.parseIndex += endofparse
|
||||
}
|
||||
m.parseIndex += endofparse
|
||||
return command(m.Raw[start:m.parseIndex]), nil
|
||||
}
|
||||
|
||||
@ -131,6 +132,45 @@ func (m *Message) findNext(bs ...string) int {
|
||||
return r
|
||||
}
|
||||
|
||||
func unescapeTag(s string) string {
|
||||
sb := strings.Builder{}
|
||||
escaping := false
|
||||
i := 0
|
||||
for i < len(s) {
|
||||
if escaping {
|
||||
if i+1 > len(s) {
|
||||
break
|
||||
}
|
||||
switch s[i] {
|
||||
case ':':
|
||||
sb.WriteByte(';')
|
||||
case '\\':
|
||||
sb.WriteByte('\\')
|
||||
case 's':
|
||||
sb.WriteByte(' ')
|
||||
case 'r':
|
||||
sb.WriteByte('\r')
|
||||
case 'n':
|
||||
sb.WriteByte('\n')
|
||||
default:
|
||||
sb.WriteByte(s[i])
|
||||
}
|
||||
i++
|
||||
escaping = false
|
||||
} else if s[i] == '\\' {
|
||||
if !escaping {
|
||||
escaping = true
|
||||
i++
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
sb.WriteByte(s[i])
|
||||
i++
|
||||
}
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func (m *Message) ToBytes() []byte {
|
||||
return nil
|
||||
}
|
||||
|
@ -33,6 +33,12 @@ func TestParsing(t *testing.T) {
|
||||
Command: PRIVMSG,
|
||||
Parameters: []string{"#chan", "Hey what's up!"},
|
||||
}},
|
||||
{
|
||||
input: `@a=b\\and\nk;c=72\s45;d=gh\:764 CAP`,
|
||||
output: &Message{
|
||||
Tags: map[string]string{"a": "b\\and\nk", "c": "72 45", "d": `gh;764`},
|
||||
Command: command("CAP"),
|
||||
}},
|
||||
{
|
||||
input: `@tag1=value1;tag2;vendor1/tag3=value2;vendor2/tag4= :dan!d@localhost PRIVMSG #chan :Hey what's up!`,
|
||||
output: &Message{
|
||||
@ -41,6 +47,18 @@ func TestParsing(t *testing.T) {
|
||||
Command: PRIVMSG,
|
||||
Parameters: []string{"#chan", "Hey what's up!"},
|
||||
}},
|
||||
{
|
||||
input: ":src AWAY",
|
||||
output: &Message{
|
||||
Source: "src",
|
||||
Command: AWAY,
|
||||
}},
|
||||
{
|
||||
input: ":src AWAY ",
|
||||
output: &Message{
|
||||
Source: "src",
|
||||
Command: AWAY,
|
||||
}},
|
||||
{
|
||||
input: `CAP REQ :sasl`,
|
||||
output: &Message{
|
||||
@ -65,6 +83,13 @@ func TestParsing(t *testing.T) {
|
||||
Command: CAP,
|
||||
Parameters: []string{"REQ", " asdf qwer"},
|
||||
}},
|
||||
{
|
||||
input: ":coolguy!ag@net\x035w\x03ork.admin PRIVMSG foo :bar baz",
|
||||
output: &Message{
|
||||
Source: "coolguy!ag@net\x035w\x03ork.admin",
|
||||
Command: PRIVMSG,
|
||||
Parameters: []string{"foo", "bar baz"},
|
||||
}},
|
||||
{
|
||||
input: `:coolguy PRIVMSG bar :lol :) `,
|
||||
output: &Message{
|
||||
@ -86,6 +111,57 @@ func TestParsing(t *testing.T) {
|
||||
Command: MODE,
|
||||
Parameters: []string{"#tckk", "+n"},
|
||||
}},
|
||||
{
|
||||
input: ":services.esper.net MODE #foo-bar +o foobar ",
|
||||
output: &Message{
|
||||
Source: "services.esper.net",
|
||||
Command: MODE,
|
||||
Parameters: []string{"#foo-bar", "+o", "foobar"},
|
||||
}},
|
||||
{
|
||||
input: `@tag1=value\\ntest COMMAND`,
|
||||
output: &Message{
|
||||
Tags: map[string]string{"tag1": `value\ntest`},
|
||||
Command: command("COMMAND"),
|
||||
}},
|
||||
{
|
||||
input: `@tag1=value\1 COMMAND`,
|
||||
output: &Message{
|
||||
Tags: map[string]string{"tag1": `value1`},
|
||||
Command: command("COMMAND"),
|
||||
}},
|
||||
{
|
||||
input: `@tag1=value\ COMMAND`,
|
||||
output: &Message{
|
||||
Tags: map[string]string{"tag1": `value`},
|
||||
Command: command("COMMAND"),
|
||||
}},
|
||||
{
|
||||
input: `@tag1=1;tag2=3;tag3=4;tag1=5 COMMAND`,
|
||||
output: &Message{
|
||||
Tags: map[string]string{"tag1": "5", "tag2": "3", "tag3": "4"},
|
||||
Command: command("COMMAND"),
|
||||
}},
|
||||
{
|
||||
input: `@tag1=1;tag2=3;tag3=4;tag1=5;vendor/tag2=8 COMMAND`,
|
||||
output: &Message{
|
||||
Tags: map[string]string{"tag1": "5", "tag2": "3", "tag3": "4", "vendor/tag2": "8"},
|
||||
Command: command("COMMAND"),
|
||||
}},
|
||||
{
|
||||
input: `:SomeOp MODE #channel :+i`,
|
||||
output: &Message{
|
||||
Source: "SomeOp",
|
||||
Command: MODE,
|
||||
Parameters: []string{"#channel", "+i"},
|
||||
}},
|
||||
{
|
||||
input: `:SomeOp MODE #channel +oo SomeUser :AnotherUser`,
|
||||
output: &Message{
|
||||
Source: "SomeOp",
|
||||
Command: MODE,
|
||||
Parameters: []string{"#channel", "+oo", "SomeUser", "AnotherUser"},
|
||||
}},
|
||||
}
|
||||
for _, tc := range testcases {
|
||||
m := &Message{Raw: tc.input}
|
||||
@ -94,6 +170,12 @@ func TestParsing(t *testing.T) {
|
||||
t.Logf("tags: actual: %v, expected: %v", m.Tags, tc.output.Tags)
|
||||
t.Fail()
|
||||
}
|
||||
for i := range tc.output.Tags {
|
||||
if tc.output.Tags[i] != m.Tags[i] {
|
||||
t.Logf("tags: actual: %v, expected: %v", m.Tags[i], tc.output.Tags[i])
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
if tc.output.Source != m.Source {
|
||||
t.Logf("source: actual: %v, expected: %v", m.Source, tc.output.Source)
|
||||
t.Fail()
|
||||
@ -106,5 +188,11 @@ func TestParsing(t *testing.T) {
|
||||
t.Logf("parameters: actual: %v (%d), expected: %v (%d)", m.Parameters, len(m.Parameters), tc.output.Parameters, len(tc.output.Parameters))
|
||||
t.Fail()
|
||||
}
|
||||
for i := range tc.output.Parameters {
|
||||
if tc.output.Parameters[i] != m.Parameters[i] {
|
||||
t.Logf("parameters: actual: %v, expected: %v", m.Parameters[i], tc.output.Parameters[i])
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user