mirror of
https://github.com/IllusionMan1212/gorc.git
synced 2026-10-24 02:12:33 +00:00
feat: implement ISUPPORT and add support for a few easy features
fix: CAP ACK commands with no acknowledged capabilities causing an OOB. Also log unsupported and unknown capabilities and features.
This commit is contained in:
@@ -95,9 +95,13 @@ type Client struct {
|
||||
|
||||
// The acknowledged capabilities
|
||||
EnabledCapabilities Capabilities
|
||||
|
||||
// The features currently enabled for this client
|
||||
EnabledFeatures Features
|
||||
}
|
||||
|
||||
type Capabilities map[string]string
|
||||
type Features map[string]string
|
||||
|
||||
type MsgFmtOpts struct {
|
||||
WithTimestamp bool
|
||||
|
||||
+57
-54
@@ -58,7 +58,7 @@ const (
|
||||
NOTICE = "NOTICE" // Send notices between users and to channels.
|
||||
|
||||
// Operator Messages
|
||||
KILL = "KILL" // CLose connection between a client and the server. IRC Operators only (do we need this ??)
|
||||
KILL = "KILL" // Close connection between a client and the server. IRC Operators only (do we need this ??)
|
||||
|
||||
// Optional Messages
|
||||
AWAY = "AWAY" // Indicate that the client (user) is away/afk/etc..
|
||||
@@ -305,64 +305,67 @@ const (
|
||||
|
||||
/* --- RPL_ISUPPORT Parameters --- */
|
||||
var Features = map[string]bool{
|
||||
"ACCEPT": true,
|
||||
"ACCEPT": false,
|
||||
// The AWAYLEN parameter indicates the maximum length for the <reason> of an AWAY command.
|
||||
// If an AWAY <reason> has more characters than this parameter, it may be silently truncated by the server before being passed on to other clients.
|
||||
// Clients MAY receive an AWAY <reason> that has more characters than this parameter
|
||||
"AWAYLEN": true,
|
||||
"BOT": true,
|
||||
"CALLERID": true,
|
||||
"CASEMAPPING": true,
|
||||
"CHANLIMIT": true,
|
||||
"CHANMODES": true,
|
||||
"CHANNELLEN": true,
|
||||
"CHANTYPES": true, // Channel Types. Default is #. Available are #&
|
||||
"CHARSET": true, // Deprecated but might still be used
|
||||
"CLIENTVER": true, // Deprecated but might still be used
|
||||
"CNOTICE": true,
|
||||
"CPRIVMSG": true,
|
||||
"DEAF": true,
|
||||
"ELIST": true,
|
||||
"ESILENCE": true,
|
||||
"ETRACE": true,
|
||||
"EXCEPTS": true,
|
||||
"EXTBAN": true,
|
||||
"FNC": true, // Deprecated but might still be used
|
||||
"INVEX": true,
|
||||
"KEYLEN": true,
|
||||
"BOT": false,
|
||||
"CALLERID": false,
|
||||
"CASEMAPPING": false,
|
||||
"CHANLIMIT": false,
|
||||
"CHANMODES": false,
|
||||
"CHANNELLEN": false,
|
||||
"CHANTYPES": false, // Channel Types. Default is #. Available are #&
|
||||
"CHARSET": false, // Deprecated but might still be used
|
||||
"CLIENTVER": false, // Deprecated but might still be used
|
||||
"CNOTICE": false,
|
||||
"CPRIVMSG": false,
|
||||
"DEAF": false,
|
||||
"ELIST": false,
|
||||
"ESILENCE": false,
|
||||
"ETRACE": false,
|
||||
"EXCEPTS": false,
|
||||
"EXTBAN": false,
|
||||
"FNC": false, // Deprecated but might still be used
|
||||
"INVEX": false,
|
||||
"KEYLEN": false,
|
||||
"KICKLEN": true,
|
||||
"KNOCK": true,
|
||||
"LINELEN": true, // Proposed
|
||||
"MAP": true, // Deprecated but might still be used
|
||||
"MAXBANS": true, // Deprecated but might still be used
|
||||
"MAXCHANNELS": true, // Deprecated but might still be used
|
||||
"MAXLIST": true,
|
||||
"MAXNICKLEN": true,
|
||||
"MAXPARA": true, // Deprecated but might still be used
|
||||
"MAXTARGETS": true,
|
||||
"METADATA": true,
|
||||
"MODES": true,
|
||||
"MONITOR": true,
|
||||
"NAMESX": true, // Deprecated but might still be used
|
||||
"KNOCK": false,
|
||||
"LINELEN": false, // Proposed
|
||||
"MAP": false, // Deprecated but might still be used
|
||||
"MAXBANS": false, // Deprecated but might still be used
|
||||
"MAXCHANNELS": false, // Deprecated but might still be used
|
||||
"MAXLIST": false,
|
||||
"MAXNICKLEN": false,
|
||||
"MAXPARA": false, // Deprecated but might still be used
|
||||
"MAXTARGETS": false,
|
||||
"METADATA": false,
|
||||
"MODES": false,
|
||||
"MONITOR": false,
|
||||
"NAMESX": false, // Deprecated but might still be used
|
||||
"NETWORK": true,
|
||||
"NICKLEN": true,
|
||||
"OVERRIDE": true,
|
||||
"PREFIX": true,
|
||||
"SAFELIST": true,
|
||||
"SECURELIST": true,
|
||||
"SILENCE": true,
|
||||
"SSL": true, // Deprecated but might still be used
|
||||
"STARTTLS": true, // Deprecated but might still be used
|
||||
"STATUSMSG": true,
|
||||
"STD": true, // Deprecated but might still be used
|
||||
"TARGMAX": true,
|
||||
"OVERRIDE": false,
|
||||
"PREFIX": false,
|
||||
"SAFELIST": false,
|
||||
"SECURELIST": false,
|
||||
"SILENCE": false,
|
||||
"SSL": false, // Deprecated but might still be used
|
||||
"STARTTLS": false, // Deprecated but might still be used
|
||||
"STATUSMSG": false,
|
||||
"STD": false, // Deprecated but might still be used
|
||||
"TARGMAX": false,
|
||||
"TOPICLEN": true,
|
||||
"UHNAMES": true, // Deprecated but might still be used
|
||||
"USERIP": true,
|
||||
"USERLEN": true, // Proposed
|
||||
"VBANLIST": true, // Deprecared but might still be used
|
||||
"VLIST": true,
|
||||
"WALLCHOPS": true, // Deprecated but might still be used
|
||||
"WALLVOICES": true, // Deprecated but might still be used
|
||||
"WATCH": true,
|
||||
"WHOX": true,
|
||||
"UHNAMES": false, // Deprecated but might still be used
|
||||
"USERIP": false,
|
||||
"USERLEN": false, // Proposed
|
||||
"VBANLIST": false, // Deprecared but might still be used
|
||||
"VLIST": false,
|
||||
"WALLCHOPS": false, // Deprecated but might still be used
|
||||
"WALLVOICES": false, // Deprecated but might still be used
|
||||
"WATCH": false,
|
||||
"WHOX": false,
|
||||
}
|
||||
|
||||
var Capabilities = map[string]bool{
|
||||
|
||||
+49
-11
@@ -35,6 +35,7 @@ import (
|
||||
|
||||
func ReadLoop(client *irc.Client) {
|
||||
// 512 bytes as a base + 8192 additional bytes for tags
|
||||
// as specified here: https://modern.ircdocs.horse/#message-format
|
||||
r := bufio.NewReaderSize(client.TCPConn, 8192+512)
|
||||
|
||||
for {
|
||||
@@ -235,18 +236,22 @@ func handleCAP(msg irc.Message, client *irc.Client) {
|
||||
value = parts[1]
|
||||
}
|
||||
|
||||
if commands.Capabilities[key] {
|
||||
// When the final parameter approaches 510 bytes,
|
||||
// we send multiple REQ commands
|
||||
// (https://ircv3.net/specs/extensions/capability-negotiation.html#the-cap-req-subcommand)
|
||||
if length+len(capability) >= 500 {
|
||||
recognizedCaps = append(recognizedCaps, capabilities)
|
||||
capabilities = make(irc.Capabilities, 0)
|
||||
length = 0
|
||||
}
|
||||
if capEnabled, ok := commands.Capabilities[key]; ok {
|
||||
if capEnabled {
|
||||
// When the final parameter approaches 510 bytes,
|
||||
// we send multiple REQ commands
|
||||
// (https://ircv3.net/specs/extensions/capability-negotiation.html#the-cap-req-subcommand)
|
||||
if length+len(capability) >= 500 {
|
||||
recognizedCaps = append(recognizedCaps, capabilities)
|
||||
capabilities = make(irc.Capabilities, 0)
|
||||
length = 0
|
||||
}
|
||||
|
||||
capabilities[key] = value
|
||||
length += len(capability)
|
||||
capabilities[key] = value
|
||||
length += len(capability)
|
||||
} else {
|
||||
log.Println("Unsupported capability:", capability)
|
||||
}
|
||||
} else {
|
||||
log.Println("Unknown capability:", capability)
|
||||
}
|
||||
@@ -266,6 +271,9 @@ func handleCAP(msg irc.Message, client *irc.Client) {
|
||||
}
|
||||
client.SendCommand(commands.CAP, "END")
|
||||
case "ACK":
|
||||
if msg.Parameters[2] == "" {
|
||||
return
|
||||
}
|
||||
caps := strings.Split(msg.Parameters[2], " ")
|
||||
for _, capability := range caps {
|
||||
if capability[0] == '-' {
|
||||
@@ -342,6 +350,34 @@ func handleMYINFO(msg irc.Message, client *irc.Client) {
|
||||
client.Channels[0].AppendMsg(msg.Timestamp, info, msgOpts)
|
||||
}
|
||||
|
||||
func handleISUPPORT(msg irc.Message, client *irc.Client) {
|
||||
msgOpts := irc.MsgFmtOpts{
|
||||
WithTimestamp: true,
|
||||
AsServerMsg: true,
|
||||
}
|
||||
|
||||
for _, token := range msg.Parameters[1 : len(msg.Parameters)-1] {
|
||||
splits := strings.Split(token, "=")
|
||||
key := splits[0]
|
||||
value := ""
|
||||
if len(splits) > 1 {
|
||||
value = splits[1]
|
||||
}
|
||||
|
||||
if supported, known := commands.Features[key]; known {
|
||||
if supported {
|
||||
client.EnabledFeatures[key] = value
|
||||
} else {
|
||||
log.Printf("Unsupported feature: \"%v\" with value: \"%v\"\n", key, value)
|
||||
}
|
||||
} else {
|
||||
log.Printf("Unknown feature: \"%v\" with value: \"%v\"\n", key, value)
|
||||
}
|
||||
|
||||
client.Channels[0].AppendMsg(msg.Timestamp, token, msgOpts)
|
||||
}
|
||||
}
|
||||
|
||||
func handleLUSERCLIENT(msg irc.Message, client *irc.Client) {
|
||||
message := msg.Parameters[1]
|
||||
|
||||
@@ -695,6 +731,8 @@ func HandleCommand(msg irc.Message, client *irc.Client) {
|
||||
handleCREATED(msg, client)
|
||||
case commands.RPL_MYINFO:
|
||||
handleMYINFO(msg, client)
|
||||
case commands.RPL_ISUPPORT:
|
||||
handleISUPPORT(msg, client)
|
||||
case commands.RPL_LUSERCLIENT:
|
||||
handleLUSERCLIENT(msg, client)
|
||||
case commands.RPL_LUSEROP:
|
||||
|
||||
Reference in New Issue
Block a user