2016-06-15 13:50:56 +02:00
|
|
|
// Copyright (c) 2012-2014 Jeremy Latt
|
2017-03-27 14:15:02 +02:00
|
|
|
// Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
|
2016-06-15 13:50:56 +02:00
|
|
|
// released under the MIT license
|
|
|
|
|
2014-03-13 01:38:11 +01:00
|
|
|
package irc
|
|
|
|
|
|
|
|
import (
|
|
|
|
"strings"
|
|
|
|
|
2017-06-15 18:14:19 +02:00
|
|
|
"github.com/goshuirc/irc-go/ircmsg"
|
2017-09-29 04:07:52 +02:00
|
|
|
"github.com/oragono/oragono/irc/caps"
|
2014-03-13 01:38:11 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2017-03-25 00:19:13 +01:00
|
|
|
// SupportedCapabilities are the caps we advertise.
|
2017-09-29 09:25:58 +02:00
|
|
|
// MaxLine, SASL and STS are set during server startup.
|
2018-01-21 07:11:16 +01:00
|
|
|
SupportedCapabilities = caps.NewSet(caps.AccountTag, caps.AccountNotify, caps.AwayNotify, caps.CapNotify, caps.ChgHost, caps.EchoMessage, caps.ExtendedJoin, caps.InviteNotify, caps.Languages, caps.MessageTags, caps.MultiPrefix, caps.Rename, caps.ServerTime, caps.UserhostInNames)
|
2017-09-29 09:25:58 +02:00
|
|
|
|
2017-03-25 00:19:13 +01:00
|
|
|
// CapValues are the actual values we advertise to v3.2 clients.
|
2017-09-29 09:25:58 +02:00
|
|
|
// actual values are set during server startup.
|
|
|
|
CapValues = caps.NewValues()
|
2014-03-13 01:38:11 +01:00
|
|
|
)
|
|
|
|
|
2017-03-25 00:19:13 +01:00
|
|
|
// CapState shows whether we're negotiating caps, finished, etc for connection registration.
|
2014-03-13 01:38:11 +01:00
|
|
|
type CapState uint
|
|
|
|
|
|
|
|
const (
|
2017-04-16 03:31:33 +02:00
|
|
|
// CapNone means CAP hasn't been negotiated at all.
|
|
|
|
CapNone CapState = iota
|
|
|
|
// CapNegotiating means CAP is being negotiated and registration should be paused.
|
2014-03-13 01:38:11 +01:00
|
|
|
CapNegotiating CapState = iota
|
2017-04-16 03:31:33 +02:00
|
|
|
// CapNegotiated means CAP negotiation has been successfully ended and reg should complete.
|
|
|
|
CapNegotiated CapState = iota
|
2014-03-13 01:38:11 +01:00
|
|
|
)
|
|
|
|
|
2016-06-19 02:01:30 +02:00
|
|
|
// CAP <subcmd> [<caps>]
|
|
|
|
func capHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
|
|
|
subCommand := strings.ToUpper(msg.Params[0])
|
2017-09-29 09:25:58 +02:00
|
|
|
capabilities := caps.NewSet()
|
2016-06-19 02:01:30 +02:00
|
|
|
var capString string
|
|
|
|
|
|
|
|
if len(msg.Params) > 1 {
|
|
|
|
capString = msg.Params[1]
|
|
|
|
strs := strings.Split(capString, " ")
|
|
|
|
for _, str := range strs {
|
|
|
|
if len(str) > 0 {
|
2017-09-29 09:25:58 +02:00
|
|
|
capabilities.Enable(caps.Capability(str))
|
2014-03-13 01:38:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-04-13 23:30:14 +02:00
|
|
|
|
2016-06-19 02:01:30 +02:00
|
|
|
switch subCommand {
|
|
|
|
case "LS":
|
|
|
|
if !client.registered {
|
|
|
|
client.capState = CapNegotiating
|
|
|
|
}
|
2016-10-16 05:54:09 +02:00
|
|
|
if len(msg.Params) > 1 && msg.Params[1] == "302" {
|
|
|
|
client.capVersion = 302
|
|
|
|
}
|
|
|
|
// weechat 1.4 has a bug here where it won't accept the CAP reply unless it contains
|
|
|
|
// the server.name source... otherwise it doesn't respond to the CAP message with
|
|
|
|
// anything and just hangs on connection.
|
|
|
|
//TODO(dan): limit number of caps and send it multiline in 3.2 style as appropriate.
|
2017-09-29 09:25:58 +02:00
|
|
|
client.Send(nil, server.name, "CAP", client.nick, subCommand, SupportedCapabilities.String(client.capVersion, CapValues))
|
2016-04-13 23:30:14 +02:00
|
|
|
|
2016-06-19 02:01:30 +02:00
|
|
|
case "LIST":
|
2017-09-29 09:25:58 +02:00
|
|
|
client.Send(nil, server.name, "CAP", client.nick, subCommand, client.capabilities.String(caps.Cap301, CapValues)) // values not sent on LIST so force 3.1
|
2016-04-13 23:30:14 +02:00
|
|
|
|
2016-06-19 02:01:30 +02:00
|
|
|
case "REQ":
|
2017-12-28 04:50:42 +01:00
|
|
|
if !client.registered {
|
|
|
|
client.capState = CapNegotiating
|
|
|
|
}
|
|
|
|
|
2016-06-19 02:01:30 +02:00
|
|
|
// make sure all capabilities actually exist
|
2017-09-29 09:25:58 +02:00
|
|
|
for _, capability := range capabilities.List() {
|
|
|
|
if !SupportedCapabilities.Has(capability) {
|
2016-10-11 15:51:46 +02:00
|
|
|
client.Send(nil, server.name, "CAP", client.nick, "NAK", capString)
|
2016-06-19 02:01:30 +02:00
|
|
|
return false
|
2016-04-13 23:30:14 +02:00
|
|
|
}
|
|
|
|
}
|
2017-09-29 09:25:58 +02:00
|
|
|
client.capabilities.Enable(capabilities.List()...)
|
2016-10-11 15:51:46 +02:00
|
|
|
client.Send(nil, server.name, "CAP", client.nick, "ACK", capString)
|
2016-04-13 23:30:14 +02:00
|
|
|
|
2016-06-19 02:01:30 +02:00
|
|
|
case "END":
|
|
|
|
if !client.registered {
|
|
|
|
client.capState = CapNegotiated
|
|
|
|
server.tryRegister(client)
|
|
|
|
}
|
2016-04-13 23:30:14 +02:00
|
|
|
|
|
|
|
default:
|
2016-10-11 15:51:46 +02:00
|
|
|
client.Send(nil, server.name, ERR_INVALIDCAPCMD, client.nick, subCommand, "Invalid CAP subcommand")
|
2016-04-13 23:30:14 +02:00
|
|
|
}
|
2016-06-19 02:01:30 +02:00
|
|
|
return false
|
2016-04-13 23:30:14 +02:00
|
|
|
}
|