aboutsummaryrefslogtreecommitdiff
path: root/xS
diff options
context:
space:
mode:
authorPřemysl Eric Janouch <p@janouch.name>2024-04-02 16:44:01 +0200
committerPřemysl Eric Janouch <p@janouch.name>2024-04-03 15:56:33 +0200
commit53ba996ec9c5c8fc64f66934d8c98509bd7ed06d (patch)
treefe396b25631e5a325c0e7d470dbb3e7e045d66b4 /xS
parentd450c6cc5fe4caa2f9a1db3f0ec76c84b6a110af (diff)
downloadxK-53ba996ec9c5c8fc64f66934d8c98509bd7ed06d.tar.gz
xK-53ba996ec9c5c8fc64f66934d8c98509bd7ed06d.tar.xz
xK-53ba996ec9c5c8fc64f66934d8c98509bd7ed06d.zip
Add a simple IRC notifier utility
Diffstat (limited to 'xS')
-rw-r--r--xS/irc.go132
-rw-r--r--xS/xS.go126
2 files changed, 132 insertions, 126 deletions
diff --git a/xS/irc.go b/xS/irc.go
new file mode 100644
index 0000000..71bf961
--- /dev/null
+++ b/xS/irc.go
@@ -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
+}
diff --git a/xS/xS.go b/xS/xS.go
index 25db005..69a9b36 100644
--- a/xS/xS.go
+++ b/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