mirror of
https://github.com/ergochat/ergo.git
synced 2024-11-22 11:59:40 +01:00
142 lines
2.9 KiB
Go
142 lines
2.9 KiB
Go
package irc
|
|
|
|
import (
|
|
"strings"
|
|
)
|
|
|
|
type CapSubCommand string
|
|
|
|
const (
|
|
CAP_LS CapSubCommand = "LS"
|
|
CAP_LIST CapSubCommand = "LIST"
|
|
CAP_REQ CapSubCommand = "REQ"
|
|
CAP_ACK CapSubCommand = "ACK"
|
|
CAP_NAK CapSubCommand = "NAK"
|
|
CAP_END CapSubCommand = "END"
|
|
)
|
|
|
|
// Capabilities are optional features a client may request from a server.
|
|
type Capability string
|
|
|
|
const (
|
|
MultiPrefix Capability = "multi-prefix"
|
|
SASL Capability = "sasl"
|
|
)
|
|
|
|
var (
|
|
SupportedCapabilities = CapabilitySet{
|
|
MultiPrefix: true,
|
|
}
|
|
)
|
|
|
|
func (capability Capability) String() string {
|
|
return string(capability)
|
|
}
|
|
|
|
// CapModifiers are indicators showing the state of a capability after a REQ or
|
|
// ACK.
|
|
type CapModifier rune
|
|
|
|
const (
|
|
Ack CapModifier = '~'
|
|
Disable CapModifier = '-'
|
|
Sticky CapModifier = '='
|
|
)
|
|
|
|
func (mod CapModifier) String() string {
|
|
return string(mod)
|
|
}
|
|
|
|
type CapState uint
|
|
|
|
const (
|
|
CapNone CapState = iota
|
|
CapNegotiating CapState = iota
|
|
CapNegotiated CapState = iota
|
|
)
|
|
|
|
type CapabilitySet map[Capability]bool
|
|
|
|
func (set CapabilitySet) String() string {
|
|
strs := make([]string, len(set))
|
|
index := 0
|
|
for capability := range set {
|
|
strs[index] = string(capability)
|
|
index += 1
|
|
}
|
|
return strings.Join(strs, " ")
|
|
}
|
|
|
|
func (set CapabilitySet) DisableString() string {
|
|
parts := make([]string, len(set))
|
|
index := 0
|
|
for capability := range set {
|
|
parts[index] = Disable.String() + capability.String()
|
|
index += 1
|
|
}
|
|
return strings.Join(parts, " ")
|
|
}
|
|
|
|
func (msg *CapCommand) HandleRegServer(server *Server) {
|
|
client := msg.Client()
|
|
|
|
switch msg.subCommand {
|
|
case CAP_LS:
|
|
client.capState = CapNegotiating
|
|
client.Reply(RplCap(client, CAP_LS, SupportedCapabilities))
|
|
|
|
case CAP_LIST:
|
|
client.Reply(RplCap(client, CAP_LIST, client.capabilities))
|
|
|
|
case CAP_REQ:
|
|
for capability := range msg.capabilities {
|
|
if !SupportedCapabilities[capability] {
|
|
client.Reply(RplCap(client, CAP_NAK, msg.capabilities))
|
|
return
|
|
}
|
|
}
|
|
for capability := range msg.capabilities {
|
|
client.capabilities[capability] = true
|
|
}
|
|
client.Reply(RplCap(client, CAP_ACK, msg.capabilities))
|
|
|
|
case CAP_END:
|
|
client.capState = CapNegotiated
|
|
server.tryRegister(client)
|
|
|
|
default:
|
|
client.ErrInvalidCapCmd(msg.subCommand)
|
|
}
|
|
}
|
|
|
|
func (msg *CapCommand) HandleServer(server *Server) {
|
|
client := msg.Client()
|
|
|
|
switch msg.subCommand {
|
|
case CAP_LS:
|
|
client.Reply(RplCap(client, CAP_LS, SupportedCapabilities))
|
|
|
|
case CAP_LIST:
|
|
client.Reply(RplCap(client, CAP_LIST, client.capabilities))
|
|
|
|
case CAP_REQ:
|
|
for capability := range msg.capabilities {
|
|
if !SupportedCapabilities[capability] {
|
|
client.Reply(RplCap(client, CAP_NAK, msg.capabilities))
|
|
return
|
|
}
|
|
}
|
|
for capability := range msg.capabilities {
|
|
client.capabilities[capability] = true
|
|
}
|
|
client.Reply(RplCap(client, CAP_ACK, msg.capabilities))
|
|
|
|
case CAP_END:
|
|
// no-op after registration performed
|
|
return
|
|
|
|
default:
|
|
client.ErrInvalidCapCmd(msg.subCommand)
|
|
}
|
|
}
|