diff --git a/irc/capability.go b/irc/capability.go new file mode 100644 index 00000000..3ec4d3f9 --- /dev/null +++ b/irc/capability.go @@ -0,0 +1,116 @@ +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_CLEAR CapSubCommand = "CLEAR" + 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_CLEAR: + reply := RplCap(client, CAP_ACK, client.capabilities.DisableString()) + client.capabilities = make(CapabilitySet) + client.Reply(reply) + + case CAP_END: + client.capState = CapNegotiated + server.tryRegister(client) + + default: + client.ErrInvalidCapCmd(msg.subCommand) + } +} diff --git a/irc/constants.go b/irc/constants.go index cb0bde98..11af3983 100644 --- a/irc/constants.go +++ b/irc/constants.go @@ -196,14 +196,6 @@ const ( ERR_UMODEUNKNOWNFLAG NumericCode = 501 ERR_USERSDONTMATCH NumericCode = 502 - CAP_LS CapSubCommand = "LS" - CAP_LIST CapSubCommand = "LIST" - CAP_REQ CapSubCommand = "REQ" - CAP_ACK CapSubCommand = "ACK" - CAP_NAK CapSubCommand = "NAK" - CAP_CLEAR CapSubCommand = "CLEAR" - CAP_END CapSubCommand = "END" - Add ModeOp = '+' List ModeOp = '=' Remove ModeOp = '-' @@ -234,28 +226,9 @@ const ( Secret ChannelMode = 's' // flag, deprecated UserLimit ChannelMode = 'l' // flag arg Voice ChannelMode = 'v' // arg - - MultiPrefix Capability = "multi-prefix" - SASL Capability = "sasl" - - Disable CapModifier = '-' - Ack CapModifier = '~' - Sticky CapModifier = '=' -) - -var ( - SupportedCapabilities = CapabilitySet{ - MultiPrefix: true, - } ) const ( Registration Phase = iota Normal Phase = iota ) - -const ( - CapNone CapState = iota - CapNegotiating CapState = iota - CapNegotiated CapState = iota -) diff --git a/irc/server.go b/irc/server.go index 66576743..bf4b150c 100644 --- a/irc/server.go +++ b/irc/server.go @@ -275,43 +275,6 @@ func (msg *ProxyCommand) HandleRegServer(server *Server) { msg.Client().hostname = msg.hostname } -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_CLEAR: - reply := RplCap(client, CAP_ACK, client.capabilities.DisableString()) - client.capabilities = make(CapabilitySet) - client.Reply(reply) - - case CAP_END: - client.capState = CapNegotiated - server.tryRegister(client) - - default: - client.ErrInvalidCapCmd(msg.subCommand) - } -} - func (m *NickCommand) HandleRegServer(s *Server) { client := m.Client() if !client.authorized { diff --git a/irc/types.go b/irc/types.go index 75938807..90631099 100644 --- a/irc/types.go +++ b/irc/types.go @@ -9,44 +9,6 @@ import ( // simple types // -type CapSubCommand string - -type Capability string - -func (capability Capability) String() string { - return string(capability) -} - -type CapModifier rune - -func (mod CapModifier) String() string { - return string(mod) -} - -type CapState uint - -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, " ") -} - // add, remove, list modes type ModeOp rune