152 lines
2.7 KiB
Go
152 lines
2.7 KiB
Go
package irc
|
|
|
|
import (
|
|
"strings"
|
|
)
|
|
|
|
func (c *Client) HasCapability(name string, values ...string) bool {
|
|
c.lock.Lock()
|
|
defer c.lock.Unlock()
|
|
|
|
if capValues, ok := c.enabledCapabilities[name]; ok {
|
|
if len(values) == 0 || capValues == nil {
|
|
return true
|
|
}
|
|
|
|
for _, v := range values {
|
|
for _, vCap := range capValues {
|
|
if v == vCap {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
var clientWantedCaps = []string{"cap-notify"}
|
|
|
|
func (c *Client) beginCAP() {
|
|
c.write("CAP LS 302")
|
|
}
|
|
|
|
func (c *Client) beginSASL() bool {
|
|
if c.negotiating {
|
|
for i, mech := range c.saslMechanisms {
|
|
if c.HasCapability("sasl", mech.Name()) {
|
|
c.currentSASLIndex = i
|
|
c.authenticate(mech.Name())
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (c *Client) finishCAP() {
|
|
if c.negotiating {
|
|
c.negotiating = false
|
|
c.write("CAP END")
|
|
}
|
|
}
|
|
|
|
func (c *Client) handleCAP(msg *Message) {
|
|
if len(msg.Params) < 3 {
|
|
c.write("CAP END")
|
|
return
|
|
}
|
|
|
|
caps := parseCaps(msg.LastParam())
|
|
|
|
switch msg.Params[1] {
|
|
case "LS":
|
|
for cap, values := range caps {
|
|
for _, wanted := range c.wantedCapabilities {
|
|
if cap == wanted {
|
|
c.requestedCapabilities[cap] = values
|
|
}
|
|
}
|
|
}
|
|
|
|
if len(msg.Params) == 3 {
|
|
if len(c.requestedCapabilities) == 0 {
|
|
c.write("CAP END")
|
|
return
|
|
}
|
|
|
|
c.negotiating = true
|
|
|
|
reqCaps := []string{}
|
|
for cap := range c.requestedCapabilities {
|
|
reqCaps = append(reqCaps, cap)
|
|
}
|
|
|
|
c.write("CAP REQ :" + strings.Join(reqCaps, " "))
|
|
}
|
|
|
|
case "ACK":
|
|
c.lock.Lock()
|
|
for cap := range caps {
|
|
if v, ok := c.requestedCapabilities[cap]; ok {
|
|
c.enabledCapabilities[cap] = v
|
|
delete(c.requestedCapabilities, cap)
|
|
}
|
|
}
|
|
c.lock.Unlock()
|
|
|
|
if len(c.requestedCapabilities) == 0 && !c.beginSASL() {
|
|
c.finishCAP()
|
|
}
|
|
|
|
case "NAK":
|
|
for cap := range caps {
|
|
delete(c.requestedCapabilities, cap)
|
|
}
|
|
|
|
if len(c.requestedCapabilities) == 0 && !c.beginSASL() {
|
|
c.finishCAP()
|
|
}
|
|
|
|
case "NEW":
|
|
reqCaps := []string{}
|
|
for cap, values := range caps {
|
|
for _, wanted := range c.wantedCapabilities {
|
|
if cap == wanted && !c.HasCapability(cap) {
|
|
c.requestedCapabilities[cap] = values
|
|
reqCaps = append(reqCaps, cap)
|
|
}
|
|
}
|
|
}
|
|
|
|
if len(reqCaps) > 0 {
|
|
c.write("CAP REQ :" + strings.Join(reqCaps, " "))
|
|
}
|
|
|
|
case "DEL":
|
|
c.lock.Lock()
|
|
for cap := range caps {
|
|
delete(c.enabledCapabilities, cap)
|
|
}
|
|
c.lock.Unlock()
|
|
}
|
|
}
|
|
|
|
func parseCaps(caps string) map[string][]string {
|
|
result := map[string][]string{}
|
|
|
|
parts := strings.Split(caps, " ")
|
|
for _, part := range parts {
|
|
capParts := strings.Split(part, "=")
|
|
name := capParts[0]
|
|
|
|
if len(capParts) > 1 {
|
|
result[name] = strings.Split(capParts[1], ",")
|
|
} else {
|
|
result[name] = nil
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|