Files
hbot/hbot/irc_caps.go
Jason A. Donenfeld b52ec14d2d Initial commit of utilities
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2021-06-03 18:38:26 +02:00

155 lines
3.2 KiB
Go

package hbot
import (
"bytes"
"encoding/base64"
"strings"
"sync"
)
type ircCaps struct {
saslOn bool
saslUser string
saslPass string
caps []string
capsEnabled map[string]bool
mu sync.Mutex
done bool
}
func (c *ircCaps) saslEnable() {
c.mu.Lock()
c.saslOn = true
c.mu.Unlock()
}
func (c *ircCaps) saslCreds(user, pass string) {
c.mu.Lock()
c.saslUser = user
c.saslPass = pass
c.mu.Unlock()
}
func (c *ircCaps) reset() {
c.mu.Lock()
c.saslOn = false
c.done = false
c.caps = []string{}
c.capsEnabled = make(map[string]bool)
c.mu.Unlock()
}
func (c *ircCaps) saslAuth(m *Message) bool {
return m.Command == "AUTHENTICATE" && m.Param(0) == "+"
}
func (c *ircCaps) capLS(m *Message) bool {
return m.Command == "CAP" && m.Param(1) == "LS"
}
func (c *ircCaps) capACK(m *Message) bool {
return m.Command == "CAP" && m.Param(1) == "ACK"
}
func (c *ircCaps) Handle(bot *Bot, m *Message) {
c.mu.Lock()
defer c.mu.Unlock()
if c.done {
return
}
if c.capLS(m) {
for _, cap := range strings.Split(m.Trailing(), " ") {
if _, ok := allowedCAPs[cap]; ok {
c.capsEnabled[cap] = true
c.caps = append(c.caps, cap)
} else {
c.capsEnabled[cap] = false
}
}
bot.Send("CAP REQ :" + strings.Join(c.caps, " "))
}
if c.capACK(m) {
if c.saslOn && strings.Contains(m.Trailing(), "sasl") {
bot.Send("AUTHENTICATE PLAIN")
} else {
if c.saslOn {
bot.config.Logger.Errorf("SASL is not supported, despite request")
}
bot.Send("CAP END")
c.done = true
}
}
if c.saslAuth(m) {
out := bytes.Join([][]byte{[]byte(c.saslUser), []byte(c.saslUser), []byte(c.saslPass)}, []byte{0})
encpass := base64.StdEncoding.EncodeToString(out)
bot.Send("AUTHENTICATE " + encpass)
bot.Send("CAP END")
c.done = true
}
}
// Capabilities we can deal with
// without doing crazy things in the library
var allowedCAPs = map[string]struct{}{
CapAccountNotify: {},
CapAwayNotify: {},
CapExtendedJoin: {},
CapSASL: {},
CapChghost: {},
CapInviteNotify: {},
CapMultiPrefix: {},
CapCapNotify: {},
CapSetName: {},
CapServerTime: {},
CapAccountTag: {},
CapMessageTags: {},
}
// CapAccountNotify is account-notify CAP
const CapAccountNotify = "account-notify"
// CapAwayNotify is away-notify CAP
const CapAwayNotify = "away-notify"
// CapExtendedJoin is extended-join CAP
const CapExtendedJoin = "extended-join"
// CapSASL is SASL CAP
const CapSASL = "sasl"
// CapChghost is chghost CAP
const CapChghost = "chghost"
// CapInviteNotify is invite-notify CAP
const CapInviteNotify = "invite-notify"
// CapMultiPrefix is multi-prefix CAP
const CapMultiPrefix = "multi-prefix"
// CapUserhostInNames is userhost-in-names CAP
const CapUserhostInNames = "userhost-in-names"
// CapCapNotify is cap-notify CAP
const CapCapNotify = "cap-notify"
// CapIdentifyMsg is identify-msg CAP
const CapIdentifyMsg = "identify-msg"
// CapTLS is tls CAP
const CapTLS = "tls"
// CapSetName is setname CAP
const CapSetName = "setname"
// CapServerTime is server-time CAP
const CapServerTime = "server-time"
// CapAccountTag is account-tag CAP
const CapAccountTag = "account-tag"
// CapMessageTags is message-tags CAP
const CapMessageTags = "message-tags"