This commit is contained in:
132
xS/irc.go
Normal file
132
xS/irc.go
Normal file
@@ -0,0 +1,132 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ircToLower(c byte) byte {
|
||||
switch c {
|
||||
case '[':
|
||||
return '{'
|
||||
case ']':
|
||||
return '}'
|
||||
case '\\':
|
||||
return '|'
|
||||
case '~':
|
||||
return '^'
|
||||
}
|
||||
if c >= 'A' && c <= 'Z' {
|
||||
return c + ('a' - 'A')
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func ircToUpper(c byte) byte {
|
||||
switch c {
|
||||
case '{':
|
||||
return '['
|
||||
case '}':
|
||||
return ']'
|
||||
case '|':
|
||||
return '\\'
|
||||
case '^':
|
||||
return '~'
|
||||
}
|
||||
if c >= 'a' && c <= 'z' {
|
||||
return c - ('a' - 'A')
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// Convert identifier to a canonical form for case-insensitive comparisons.
|
||||
// ircToUpper is used so that statically initialized maps can be in uppercase.
|
||||
func ircToCanon(ident string) string {
|
||||
var canon []byte
|
||||
for _, c := range []byte(ident) {
|
||||
canon = append(canon, ircToUpper(c))
|
||||
}
|
||||
return string(canon)
|
||||
}
|
||||
|
||||
func ircEqual(s1, s2 string) bool {
|
||||
return ircToCanon(s1) == ircToCanon(s2)
|
||||
}
|
||||
|
||||
func ircFnmatch(pattern string, s string) bool {
|
||||
pattern, s = ircToCanon(pattern), ircToCanon(s)
|
||||
// FIXME: This should not support [] ranges and handle '/' specially.
|
||||
// We could translate the pattern to a regular expression.
|
||||
matched, _ := filepath.Match(pattern, s)
|
||||
return matched
|
||||
}
|
||||
|
||||
var reMsg = regexp.MustCompile(
|
||||
`^(?:@([^ ]*) +)?(?::([^! ]*)(?:!([^@]*)@([^ ]*))? +)?([^ ]+)(.*)?$`)
|
||||
var reArgs = regexp.MustCompile(`:.*| [^: ][^ ]*`)
|
||||
|
||||
type message struct {
|
||||
tags map[string]string // IRC 3.2 message tags
|
||||
nick string // optional nickname
|
||||
user string // optional username
|
||||
host string // optional hostname or IP address
|
||||
command string // command name
|
||||
params []string // arguments
|
||||
}
|
||||
|
||||
func ircUnescapeMessageTag(value string) string {
|
||||
var buf []byte
|
||||
escape := false
|
||||
for i := 0; i < len(value); i++ {
|
||||
if escape {
|
||||
switch value[i] {
|
||||
case ':':
|
||||
buf = append(buf, ';')
|
||||
case 's':
|
||||
buf = append(buf, ' ')
|
||||
case 'r':
|
||||
buf = append(buf, '\r')
|
||||
case 'n':
|
||||
buf = append(buf, '\n')
|
||||
default:
|
||||
buf = append(buf, value[i])
|
||||
}
|
||||
escape = false
|
||||
} else if value[i] == '\\' {
|
||||
escape = true
|
||||
} else {
|
||||
buf = append(buf, value[i])
|
||||
}
|
||||
}
|
||||
return string(buf)
|
||||
}
|
||||
|
||||
func ircParseMessageTags(tags string, out map[string]string) {
|
||||
for _, tag := range strings.Split(tags, ";") {
|
||||
if tag == "" {
|
||||
// Ignore empty.
|
||||
} else if equal := strings.IndexByte(tag, '='); equal < 0 {
|
||||
out[tag] = ""
|
||||
} else {
|
||||
out[tag[:equal]] = ircUnescapeMessageTag(tag[equal+1:])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ircParseMessage(line string) *message {
|
||||
m := reMsg.FindStringSubmatch(line)
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
msg := message{nil, m[2], m[3], m[4], m[5], nil}
|
||||
if m[1] != "" {
|
||||
msg.tags = make(map[string]string)
|
||||
ircParseMessageTags(m[1], msg.tags)
|
||||
}
|
||||
for _, x := range reArgs.FindAllString(m[6], -1) {
|
||||
msg.params = append(msg.params, x[1:])
|
||||
}
|
||||
return &msg
|
||||
}
|
||||
126
xS/xS.go
126
xS/xS.go
@@ -456,132 +456,6 @@ func (fd *floodDetector) check() bool {
|
||||
return count <= fd.limit
|
||||
}
|
||||
|
||||
// --- IRC protocol ------------------------------------------------------------
|
||||
|
||||
func ircToLower(c byte) byte {
|
||||
switch c {
|
||||
case '[':
|
||||
return '{'
|
||||
case ']':
|
||||
return '}'
|
||||
case '\\':
|
||||
return '|'
|
||||
case '~':
|
||||
return '^'
|
||||
}
|
||||
if c >= 'A' && c <= 'Z' {
|
||||
return c + ('a' - 'A')
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func ircToUpper(c byte) byte {
|
||||
switch c {
|
||||
case '{':
|
||||
return '['
|
||||
case '}':
|
||||
return ']'
|
||||
case '|':
|
||||
return '\\'
|
||||
case '^':
|
||||
return '~'
|
||||
}
|
||||
if c >= 'a' && c <= 'z' {
|
||||
return c - ('a' - 'A')
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// Convert identifier to a canonical form for case-insensitive comparisons.
|
||||
// ircToUpper is used so that statically initialized maps can be in uppercase.
|
||||
func ircToCanon(ident string) string {
|
||||
var canon []byte
|
||||
for _, c := range []byte(ident) {
|
||||
canon = append(canon, ircToUpper(c))
|
||||
}
|
||||
return string(canon)
|
||||
}
|
||||
|
||||
func ircEqual(s1, s2 string) bool {
|
||||
return ircToCanon(s1) == ircToCanon(s2)
|
||||
}
|
||||
|
||||
func ircFnmatch(pattern string, s string) bool {
|
||||
pattern, s = ircToCanon(pattern), ircToCanon(s)
|
||||
// FIXME: This should not support [] ranges and handle '/' specially.
|
||||
// We could translate the pattern to a regular expression.
|
||||
matched, _ := filepath.Match(pattern, s)
|
||||
return matched
|
||||
}
|
||||
|
||||
var reMsg = regexp.MustCompile(
|
||||
`^(?:@([^ ]*) +)?(?::([^! ]*)(?:!([^@]*)@([^ ]*))? +)?([^ ]+)(.*)?$`)
|
||||
var reArgs = regexp.MustCompile(`:.*| [^: ][^ ]*`)
|
||||
|
||||
type message struct {
|
||||
tags map[string]string // IRC 3.2 message tags
|
||||
nick string // optional nickname
|
||||
user string // optional username
|
||||
host string // optional hostname or IP address
|
||||
command string // command name
|
||||
params []string // arguments
|
||||
}
|
||||
|
||||
func ircUnescapeMessageTag(value string) string {
|
||||
var buf []byte
|
||||
escape := false
|
||||
for i := 0; i < len(value); i++ {
|
||||
if escape {
|
||||
switch value[i] {
|
||||
case ':':
|
||||
buf = append(buf, ';')
|
||||
case 's':
|
||||
buf = append(buf, ' ')
|
||||
case 'r':
|
||||
buf = append(buf, '\r')
|
||||
case 'n':
|
||||
buf = append(buf, '\n')
|
||||
default:
|
||||
buf = append(buf, value[i])
|
||||
}
|
||||
escape = false
|
||||
} else if value[i] == '\\' {
|
||||
escape = true
|
||||
} else {
|
||||
buf = append(buf, value[i])
|
||||
}
|
||||
}
|
||||
return string(buf)
|
||||
}
|
||||
|
||||
func ircParseMessageTags(tags string, out map[string]string) {
|
||||
for _, tag := range splitString(tags, ";", true /* ignoreEmpty */) {
|
||||
if equal := strings.IndexByte(tag, '='); equal < 0 {
|
||||
out[tag] = ""
|
||||
} else {
|
||||
out[tag[:equal]] = ircUnescapeMessageTag(tag[equal+1:])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ircParseMessage(line string) *message {
|
||||
m := reMsg.FindStringSubmatch(line)
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
msg := message{nil, m[2], m[3], m[4], m[5], nil}
|
||||
if m[1] != "" {
|
||||
msg.tags = make(map[string]string)
|
||||
ircParseMessageTags(m[1], msg.tags)
|
||||
}
|
||||
for _, x := range reArgs.FindAllString(m[6], -1) {
|
||||
msg.params = append(msg.params, x[1:])
|
||||
}
|
||||
return &msg
|
||||
|
||||
}
|
||||
|
||||
// --- IRC token validation ----------------------------------------------------
|
||||
|
||||
// Everything as per RFC 2812
|
||||
|
||||
Reference in New Issue
Block a user