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]
|
key := m.Raw[m.parseIndex : m.parseIndex+next]
|
||||||
m.parseIndex += next + 1
|
m.parseIndex += next + 1
|
||||||
eot := m.findNext(";", " ")
|
eot := m.findNext(";", " ")
|
||||||
tags[key] = m.Raw[m.parseIndex : m.parseIndex+eot]
|
tags[key] = unescapeTag(m.Raw[m.parseIndex : m.parseIndex+eot])
|
||||||
m.parseIndex += len(tags[key]) + 1
|
m.parseIndex += len(m.Raw[m.parseIndex:m.parseIndex+eot]) + 1
|
||||||
} else if m.Raw[m.parseIndex+next] == ';' {
|
} else if m.Raw[m.parseIndex+next] == ';' {
|
||||||
key := m.Raw[m.parseIndex : m.parseIndex+next]
|
key := m.Raw[m.parseIndex : m.parseIndex+next]
|
||||||
m.parseIndex += next + 1
|
m.parseIndex += next + 1
|
||||||
@ -69,9 +69,10 @@ func (m *Message) parseCommand() (command, error) {
|
|||||||
start := m.parseIndex
|
start := m.parseIndex
|
||||||
endofparse := strings.Index(m.Raw[m.parseIndex:], " ")
|
endofparse := strings.Index(m.Raw[m.parseIndex:], " ")
|
||||||
if endofparse == -1 {
|
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
|
return command(m.Raw[start:m.parseIndex]), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,6 +132,45 @@ func (m *Message) findNext(bs ...string) int {
|
|||||||
return r
|
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 {
|
func (m *Message) ToBytes() []byte {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,12 @@ func TestParsing(t *testing.T) {
|
|||||||
Command: PRIVMSG,
|
Command: PRIVMSG,
|
||||||
Parameters: []string{"#chan", "Hey what's up!"},
|
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!`,
|
input: `@tag1=value1;tag2;vendor1/tag3=value2;vendor2/tag4= :dan!d@localhost PRIVMSG #chan :Hey what's up!`,
|
||||||
output: &Message{
|
output: &Message{
|
||||||
@ -41,6 +47,18 @@ func TestParsing(t *testing.T) {
|
|||||||
Command: PRIVMSG,
|
Command: PRIVMSG,
|
||||||
Parameters: []string{"#chan", "Hey what's up!"},
|
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`,
|
input: `CAP REQ :sasl`,
|
||||||
output: &Message{
|
output: &Message{
|
||||||
@ -65,6 +83,13 @@ func TestParsing(t *testing.T) {
|
|||||||
Command: CAP,
|
Command: CAP,
|
||||||
Parameters: []string{"REQ", " asdf qwer"},
|
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 :) `,
|
input: `:coolguy PRIVMSG bar :lol :) `,
|
||||||
output: &Message{
|
output: &Message{
|
||||||
@ -86,6 +111,57 @@ func TestParsing(t *testing.T) {
|
|||||||
Command: MODE,
|
Command: MODE,
|
||||||
Parameters: []string{"#tckk", "+n"},
|
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 {
|
for _, tc := range testcases {
|
||||||
m := &Message{Raw: tc.input}
|
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.Logf("tags: actual: %v, expected: %v", m.Tags, tc.output.Tags)
|
||||||
t.Fail()
|
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 {
|
if tc.output.Source != m.Source {
|
||||||
t.Logf("source: actual: %v, expected: %v", m.Source, tc.output.Source)
|
t.Logf("source: actual: %v, expected: %v", m.Source, tc.output.Source)
|
||||||
t.Fail()
|
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.Logf("parameters: actual: %v (%d), expected: %v (%d)", m.Parameters, len(m.Parameters), tc.output.Parameters, len(tc.output.Parameters))
|
||||||
t.Fail()
|
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