From 4ee49f8450d044c9bc7b15a8a4ef9e18f0f52a2a Mon Sep 17 00:00:00 2001 From: Daniel Oaks Date: Mon, 8 Jun 2020 10:19:28 +1000 Subject: [PATCH 1/7] Initial RELAYMSG implementation --- conventional.yaml | 1 + gencapdefs.py | 6 +++++ irc/caps/defs.go | 7 ++++- irc/client_lookup_set.go | 4 +++ irc/commands.go | 4 +++ irc/config.go | 3 +++ irc/handlers.go | 56 ++++++++++++++++++++++++++++++++++++++++ irc/help.go | 10 +++++++ oragono.yaml | 1 + 9 files changed, 91 insertions(+), 1 deletion(-) diff --git a/conventional.yaml b/conventional.yaml index 2cab3fab..cc29d405 100644 --- a/conventional.yaml +++ b/conventional.yaml @@ -583,6 +583,7 @@ oper-classes: - "vhosts" - "chanreg" - "history" + - "relaymsg-anywhere" # ircd operators opers: diff --git a/gencapdefs.py b/gencapdefs.py index df0ca9a1..e717c0bd 100644 --- a/gencapdefs.py +++ b/gencapdefs.py @@ -93,6 +93,12 @@ CAPDEFS = [ url="https://ircv3.net/specs/extensions/multi-prefix-3.1.html", standard="IRCv3", ), + CapDef( + identifier="Relaymsg", + name="draft/relaymsg", + url="https://github.com/ircv3/ircv3-specifications/pull/417", + standard="proposed IRCv3", + ), CapDef( identifier="Rename", name="draft/rename", diff --git a/irc/caps/defs.go b/irc/caps/defs.go index 64455d48..f226cda5 100644 --- a/irc/caps/defs.go +++ b/irc/caps/defs.go @@ -7,7 +7,7 @@ package caps const ( // number of recognized capabilities: - numCapabs = 26 + numCapabs = 27 // length of the uint64 array that represents the bitset: bitsetLen = 1 ) @@ -53,6 +53,10 @@ const ( // https://github.com/ircv3/ircv3-specifications/pull/398 Multiline Capability = iota + // Relaymsg is the proposed IRCv3 capability named "draft/relaymsg": + // https://github.com/ircv3/ircv3-specifications/pull/417 + Relaymsg Capability = iota + // Rename is the proposed IRCv3 capability named "draft/rename": // https://github.com/SaberUK/ircv3-specifications/blob/rename/extensions/rename.md Rename Capability = iota @@ -131,6 +135,7 @@ var ( "draft/event-playback", "draft/languages", "draft/multiline", + "draft/relaymsg", "draft/rename", "draft/resume-0.5", "echo-message", diff --git a/irc/client_lookup_set.go b/irc/client_lookup_set.go index 26544453..d0d49b1e 100644 --- a/irc/client_lookup_set.go +++ b/irc/client_lookup_set.go @@ -173,6 +173,10 @@ func (clients *ClientManager) SetNick(client *Client, session *Session, newNick return "", errNicknameInvalid, false } + if strings.Contains(newCfNick, "/") { + return "", errNicknameInvalid, false + } + if restrictedCasefoldedNicks[newCfNick] || restrictedSkeletons[newSkeleton] { return "", errNicknameInvalid, false } diff --git a/irc/commands.go b/irc/commands.go index aff2d57a..7ccff5e6 100644 --- a/irc/commands.go +++ b/irc/commands.go @@ -250,6 +250,10 @@ func init() { minParams: 2, allowedInBatch: true, }, + "RELAYMSG": { + handler: relaymsgHandler, + minParams: 3, + }, "RENAME": { handler: renameHandler, minParams: 2, diff --git a/irc/config.go b/irc/config.go index c9db3f0b..a65eafe5 100644 --- a/irc/config.go +++ b/irc/config.go @@ -1068,6 +1068,9 @@ func LoadConfig(filename string) (config *Config, err error) { } config.Server.capValues[caps.Languages] = config.languageManager.CapValue() + // intentionally not configurable + config.Server.capValues[caps.Relaymsg] = "/" + config.Debug.recoverFromErrors = utils.BoolDefaultTrue(config.Debug.RecoverFromErrors) // process operator definitions, store them to config.operators diff --git a/irc/handlers.go b/irc/handlers.go index c68b2170..7b5d1655 100644 --- a/irc/handlers.go +++ b/irc/handlers.go @@ -1887,6 +1887,15 @@ func messageHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *R if i == maxTargets { break } + + if strings.Contains(targetString, "/") { + if histType == history.Privmsg { + rb.Add(nil, server.name, ERR_NOSUCHNICK, client.Nick(), targetString, client.t("Relayed users cannot be sent private messages")) + } + // TAGMSG/NOTICEs are intentionally silently dropped + continue + } + // each target gets distinct msgids splitMsg := utils.MakeMessage(message) dispatchMessageToTarget(client, clientOnlyTags, histType, msg.Command, targetString, splitMsg, rb) @@ -2270,6 +2279,53 @@ func rehashHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re return false } +// RELAYMSG : +func relaymsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) (result bool) { + channel := server.channels.Get(msg.Params[0]) + if channel == nil { + rb.Add(nil, server.name, ERR_NOSUCHCHANNEL, client.Nick(), utils.SafeErrorParam(msg.Params[0]), client.t("No such channel")) + return false + } + + if !(channel.ClientIsAtLeast(client, modes.ChannelOperator) || client.HasRoleCapabs("relaymsg-anywhere")) { + rb.Add(nil, server.name, "FAIL", "RELAYMSG", "NOT_PRIVED", client.t("Only channel operators or ircops with the 'relaymsg-anywhere' role can relay messages")) + return false + } + + rawMessage := msg.Params[2] + if strings.TrimSpace(rawMessage) == "" { + rb.Add(nil, server.name, "FAIL", "RELAYMSG", "BLANK_MSG", client.t("The message must not be blank")) + return false + } + message := utils.MakeMessage(rawMessage) + + nick := msg.Params[1] + _, err := CasefoldName(nick) + if err != nil { + rb.Add(nil, server.name, "FAIL", "RELAYMSG", "INVALID_NICK", client.t("Invalid nickname")) + return false + } + if !strings.Contains(nick, "/") { + rb.Add(nil, server.name, "FAIL", "RELAYMSG", "INVALID_NICK", fmt.Sprintf(client.t("Relayed nicknames MUST contain the relaymsg separator %s"), "/")) + return false + } + + //TODO: add to history here?? + + // send msg + for _, member := range channel.Members() { + for _, session := range member.Sessions() { + tagsToUse := make(map[string]string) + if session.capabilities.Has(caps.Relaymsg) { + tagsToUse["relaymsg"] = client.Nick() + } + + session.sendSplitMsgFromClientInternal(false, nick, "", tagsToUse, "PRIVMSG", channel.Name(), message) + } + } + return false +} + // RENAME [] func renameHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) (result bool) { result = false diff --git a/irc/help.go b/irc/help.go index 4dbd2c82..5c421096 100644 --- a/irc/help.go +++ b/irc/help.go @@ -393,6 +393,16 @@ Replies to a PING. Used to check link connectivity.`, text: `PRIVMSG {,} Sends the text to the given targets as a PRIVMSG.`, + }, + "relaymsg": { + text: `RELAYMSG : + +This command lets channel operators relay messages to their +channel from other messaging systems using relay bots. The +spoofed nickname MUST contain a forwardslash. + +For example: + RELAYMSG #ircv3 Mallory/D :Welp, we linked Discord...`, }, "rename": { text: `RENAME [] diff --git a/oragono.yaml b/oragono.yaml index c5d2aadc..ba40d8f5 100644 --- a/oragono.yaml +++ b/oragono.yaml @@ -609,6 +609,7 @@ oper-classes: - "vhosts" - "chanreg" - "history" + - "relaymsg-anywhere" # ircd operators opers: From 4ecd7fdf438618a17a3ad3a734f5b2f92bd2e48e Mon Sep 17 00:00:00 2001 From: Daniel Oaks Date: Mon, 8 Jun 2020 15:17:45 +1000 Subject: [PATCH 2/7] Allow configuring relay --- conventional.yaml | 13 +++++++++++++ irc/client_lookup_set.go | 9 +++++++-- irc/config.go | 33 +++++++++++++++++++++++---------- irc/handlers.go | 31 ++++++++++++++++++++++++------- irc/server.go | 4 ++++ irc/strings.go | 20 +++++++++++--------- oragono.yaml | 13 +++++++++++++ 7 files changed, 95 insertions(+), 28 deletions(-) diff --git a/conventional.yaml b/conventional.yaml index cc29d405..eaac8384 100644 --- a/conventional.yaml +++ b/conventional.yaml @@ -134,6 +134,19 @@ server: # if this is true, the motd is escaped using formatting codes like $c, $b, and $i motd-formatting: true + # relaying using the RELAYMSG command + relaying: + # is relaying enabled at all? + enabled: true + + # which character(s) are reserved for relayed nicks? + separators: "/" + + # can channel operators use RELAYMSG in their channels? + # our implementation of RELAYMSG makes it safe for chanops to use without the + # possibility of real users being silently spoofed + available-to-chanops: true + # addresses/CIDRs the PROXY command can be used from # this should be restricted to localhost (127.0.0.1/8, ::1/128, and unix sockets), # unless you have a good reason. you should also add these addresses to the diff --git a/irc/client_lookup_set.go b/irc/client_lookup_set.go index d0d49b1e..d24b5c55 100644 --- a/irc/client_lookup_set.go +++ b/irc/client_lookup_set.go @@ -173,8 +173,13 @@ func (clients *ClientManager) SetNick(client *Client, session *Session, newNick return "", errNicknameInvalid, false } - if strings.Contains(newCfNick, "/") { - return "", errNicknameInvalid, false + config := client.server.Config() + if config.Server.Relaying.Enabled { + for _, char := range config.Server.Relaying.Separators { + if strings.ContainsRune(newCfNick, char) { + return "", errNicknameInvalid, false + } + } } if restrictedCasefoldedNicks[newCfNick] || restrictedSkeletons[newSkeleton] { diff --git a/irc/config.go b/irc/config.go index a65eafe5..09724b19 100644 --- a/irc/config.go +++ b/irc/config.go @@ -499,14 +499,19 @@ type Config struct { CheckIdent bool `yaml:"check-ident"` MOTD string motdLines []string - MOTDFormatting bool `yaml:"motd-formatting"` - ProxyAllowedFrom []string `yaml:"proxy-allowed-from"` - proxyAllowedFromNets []net.IPNet - WebIRC []webircConfig `yaml:"webirc"` - MaxSendQString string `yaml:"max-sendq"` - MaxSendQBytes int - AllowPlaintextResume bool `yaml:"allow-plaintext-resume"` - Compatibility struct { + MOTDFormatting bool `yaml:"motd-formatting"` + Relaying struct { + Enabled bool + Separators string + AvailableToChanops bool `yaml:"available-to-chanops"` + } + ProxyAllowedFrom []string `yaml:"proxy-allowed-from"` + proxyAllowedFromNets []net.IPNet + WebIRC []webircConfig `yaml:"webirc"` + MaxSendQString string `yaml:"max-sendq"` + MaxSendQBytes int + AllowPlaintextResume bool `yaml:"allow-plaintext-resume"` + Compatibility struct { ForceTrailing *bool `yaml:"force-trailing"` forceTrailing bool SendUnprefixedSasl bool `yaml:"send-unprefixed-sasl"` @@ -1068,8 +1073,16 @@ func LoadConfig(filename string) (config *Config, err error) { } config.Server.capValues[caps.Languages] = config.languageManager.CapValue() - // intentionally not configurable - config.Server.capValues[caps.Relaymsg] = "/" + if config.Server.Relaying.Enabled { + for _, char := range protocolBreakingNameCharacters { + if strings.ContainsRune(config.Server.Relaying.Separators, char) { + return nil, fmt.Errorf("Relaying separators cannot include the characters %s", protocolBreakingNameCharacters) + } + } + config.Server.capValues[caps.Relaymsg] = config.Server.Relaying.Separators + } else { + config.Server.supportedCaps.Disable(caps.Relaymsg) + } config.Debug.recoverFromErrors = utils.BoolDefaultTrue(config.Debug.RecoverFromErrors) diff --git a/irc/handlers.go b/irc/handlers.go index 7b5d1655..e1dc4ad1 100644 --- a/irc/handlers.go +++ b/irc/handlers.go @@ -1888,12 +1888,22 @@ func messageHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *R break } - if strings.Contains(targetString, "/") { - if histType == history.Privmsg { - rb.Add(nil, server.name, ERR_NOSUCHNICK, client.Nick(), targetString, client.t("Relayed users cannot be sent private messages")) + config := server.Config() + if config.Server.Relaying.Enabled { + var isForRelayClient bool + for _, char := range config.Server.Relaying.Separators { + if strings.ContainsRune(targetString, char) { + isForRelayClient = true + break + } + } + if isForRelayClient { + if histType == history.Privmsg { + rb.Add(nil, server.name, ERR_NOSUCHNICK, client.Nick(), targetString, client.t("Relayed users cannot be sent private messages")) + } + // TAGMSG/NOTICEs are intentionally silently dropped + continue } - // TAGMSG/NOTICEs are intentionally silently dropped - continue } // each target gets distinct msgids @@ -2281,14 +2291,21 @@ func rehashHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re // RELAYMSG : func relaymsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) (result bool) { + config := server.Config() + if !config.Server.Relaying.Enabled { + rb.Add(nil, server.name, "FAIL", "RELAYMSG", "NOT_ENABLED", client.t("Relaying has been disabled")) + return false + } + channel := server.channels.Get(msg.Params[0]) if channel == nil { rb.Add(nil, server.name, ERR_NOSUCHCHANNEL, client.Nick(), utils.SafeErrorParam(msg.Params[0]), client.t("No such channel")) return false } - if !(channel.ClientIsAtLeast(client, modes.ChannelOperator) || client.HasRoleCapabs("relaymsg-anywhere")) { - rb.Add(nil, server.name, "FAIL", "RELAYMSG", "NOT_PRIVED", client.t("Only channel operators or ircops with the 'relaymsg-anywhere' role can relay messages")) + allowedToRelay := client.HasRoleCapabs("relaymsg-anywhere") || (config.Server.Relaying.AvailableToChanops && channel.ClientIsAtLeast(client, modes.ChannelOperator)) + if !allowedToRelay { + rb.Add(nil, server.name, "FAIL", "RELAYMSG", "NOT_PRIVED", client.t("You cannot relay messages to this channel")) return false } diff --git a/irc/server.go b/irc/server.go index a6f2c168..8b828ab1 100644 --- a/irc/server.go +++ b/irc/server.go @@ -497,6 +497,10 @@ func (server *Server) applyConfig(config *Config) (err error) { return fmt.Errorf("Casemapping cannot be changed after launching the server, rehash aborted") } else if oldConfig.Accounts.Multiclient.AlwaysOn != config.Accounts.Multiclient.AlwaysOn { return fmt.Errorf("Default always-on setting cannot be changed after launching the server, rehash aborted") + } else if oldConfig.Server.Relaying.Enabled != config.Server.Relaying.Enabled { + return fmt.Errorf("Cannot enable or disable relaying after launching the server, rehash aborted") + } else if oldConfig.Server.Relaying.Separators != config.Server.Relaying.Separators { + return fmt.Errorf("Cannot change relaying separators after launching the server, rehash aborted") } } diff --git a/irc/strings.go b/irc/strings.go index 6c8100f6..a80bb08d 100644 --- a/irc/strings.go +++ b/irc/strings.go @@ -19,6 +19,16 @@ import ( const ( precisUTF8MappingToken = "rfc8265" + + // space can't be used + // , is used as a separator + // * is used in mask matching + // ? is used in mask matching + // . denotes a server name + // ! separates nickname from username + // @ separates username from hostname + // : means trailing + protocolBreakingNameCharacters = " ,*?.!@:" ) var ( @@ -132,18 +142,10 @@ func CasefoldName(name string) (string, error) { return "", errStringIsEmpty } - // space can't be used - // , is used as a separator - // * is used in mask matching - // ? is used in mask matching - // . denotes a server name - // ! separates nickname from username - // @ separates username from hostname - // : means trailing // # is a channel prefix // ~&@%+ are channel membership prefixes // - I feel like disallowing - if strings.ContainsAny(lowered, " ,*?.!@:") || strings.ContainsAny(string(lowered[0]), "#~&@%+-") { + if strings.ContainsAny(lowered, protocolBreakingNameCharacters) || strings.ContainsAny(string(lowered[0]), "#~&@%+-") { return "", errInvalidCharacter } diff --git a/oragono.yaml b/oragono.yaml index ba40d8f5..d94f73eb 100644 --- a/oragono.yaml +++ b/oragono.yaml @@ -160,6 +160,19 @@ server: # if this is true, the motd is escaped using formatting codes like $c, $b, and $i motd-formatting: true + # relaying using the RELAYMSG command + relaying: + # is relaying enabled at all? + enabled: true + + # which character(s) are reserved for relayed nicks? + separators: "/" + + # can channel operators use RELAYMSG in their channels? + # our implementation of RELAYMSG makes it safe for chanops to use without the + # possibility of real users being silently spoofed + available-to-chanops: true + # addresses/CIDRs the PROXY command can be used from # this should be restricted to localhost (127.0.0.1/8, ::1/128, and unix sockets), # unless you have a good reason. you should also add these addresses to the From efd3152bfb63bf6f639c61810932164be198bc6f Mon Sep 17 00:00:00 2001 From: Daniel Oaks Date: Mon, 8 Jun 2020 15:22:41 +1000 Subject: [PATCH 3/7] Add relayed messages to channel history --- irc/handlers.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/irc/handlers.go b/irc/handlers.go index e1dc4ad1..8f328f16 100644 --- a/irc/handlers.go +++ b/irc/handlers.go @@ -2327,7 +2327,11 @@ func relaymsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb * return false } - //TODO: add to history here?? + channel.AddHistoryItem(history.Item{ + Type: history.Privmsg, + Message: message, + Nick: nick, + }, "") // send msg for _, member := range channel.Members() { From 4535c82fd31903fee15045739e0632b0fb161780 Mon Sep 17 00:00:00 2001 From: Daniel Oaks Date: Mon, 8 Jun 2020 15:25:59 +1000 Subject: [PATCH 4/7] Review fix --- irc/handlers.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/irc/handlers.go b/irc/handlers.go index 8f328f16..542db35d 100644 --- a/irc/handlers.go +++ b/irc/handlers.go @@ -2336,9 +2336,11 @@ func relaymsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb * // send msg for _, member := range channel.Members() { for _, session := range member.Sessions() { - tagsToUse := make(map[string]string) + var tagsToUse map[string]string if session.capabilities.Has(caps.Relaymsg) { - tagsToUse["relaymsg"] = client.Nick() + tagsToUse = map[string]string{ + "relaymsg": client.Nick(), + } } session.sendSplitMsgFromClientInternal(false, nick, "", tagsToUse, "PRIVMSG", channel.Name(), message) From 48509ea852ba3cad9b0ff5999846e151ad39b474 Mon Sep 17 00:00:00 2001 From: Daniel Oaks Date: Tue, 9 Jun 2020 02:05:29 +1000 Subject: [PATCH 5/7] Review fixes --- irc/handlers.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/irc/handlers.go b/irc/handlers.go index 542db35d..af282dd5 100644 --- a/irc/handlers.go +++ b/irc/handlers.go @@ -1899,7 +1899,7 @@ func messageHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *R } if isForRelayClient { if histType == history.Privmsg { - rb.Add(nil, server.name, ERR_NOSUCHNICK, client.Nick(), targetString, client.t("Relayed users cannot be sent private messages")) + rb.Add(nil, server.name, ERR_NOSUCHNICK, client.Nick(), targetString, client.t("Relayed users cannot receive private messages")) } // TAGMSG/NOTICEs are intentionally silently dropped continue @@ -2334,6 +2334,7 @@ func relaymsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb * }, "") // send msg + channelName := channel.Name() for _, member := range channel.Members() { for _, session := range member.Sessions() { var tagsToUse map[string]string @@ -2343,7 +2344,7 @@ func relaymsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb * } } - session.sendSplitMsgFromClientInternal(false, nick, "", tagsToUse, "PRIVMSG", channel.Name(), message) + session.sendSplitMsgFromClientInternal(false, nick, "", tagsToUse, "PRIVMSG", channelName, message) } } return false From 3962ff864343f3dd398d715547d96491f5af0d7e Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Wed, 9 Sep 2020 03:57:51 -0400 Subject: [PATCH 6/7] deprecate roleplay commands See #1240 --- conventional.yaml | 4 ++-- default.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/conventional.yaml b/conventional.yaml index 49bd495b..5ccc879d 100644 --- a/conventional.yaml +++ b/conventional.yaml @@ -528,6 +528,7 @@ oper-classes: - "local_unban" - "nofakelag" - "roleplay" + - "relaymsg-anywhere" # network operator "network-oper": @@ -561,7 +562,6 @@ oper-classes: - "vhosts" - "chanreg" - "history" - - "relaymsg-anywhere" - "defcon" # ircd operators @@ -748,7 +748,7 @@ fakelag: roleplay: # are roleplay commands enabled at all? (channels and clients still have to # opt in individually with the +E mode) - enabled: true + enabled: false # require the "roleplay" oper capability to send roleplay messages? require-oper: false diff --git a/default.yaml b/default.yaml index cc02e554..2b0980bb 100644 --- a/default.yaml +++ b/default.yaml @@ -556,6 +556,7 @@ oper-classes: - "local_unban" - "nofakelag" - "roleplay" + - "relaymsg-anywhere" # network operator "network-oper": @@ -589,7 +590,6 @@ oper-classes: - "vhosts" - "chanreg" - "history" - - "relaymsg-anywhere" - "defcon" # ircd operators @@ -776,7 +776,7 @@ fakelag: roleplay: # are roleplay commands enabled at all? (channels and clients still have to # opt in individually with the +E mode) - enabled: true + enabled: false # require the "roleplay" oper capability to send roleplay messages? require-oper: false From af056f26a92acfaed7e9b1453900e7d09b37a1c3 Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Wed, 9 Sep 2020 04:01:46 -0400 Subject: [PATCH 7/7] fixes and refactoring --- conventional.yaml | 4 ++-- default.yaml | 4 ++-- irc/client_lookup_set.go | 8 ++------ irc/config.go | 23 +++++++++++++++++----- irc/handlers.go | 42 ++++++++++++++++++---------------------- irc/roleplay.go | 4 ++-- irc/server.go | 4 ++-- 7 files changed, 47 insertions(+), 42 deletions(-) diff --git a/conventional.yaml b/conventional.yaml index 5ccc879d..1a976932 100644 --- a/conventional.yaml +++ b/conventional.yaml @@ -144,8 +144,8 @@ server: motd-formatting: true # relaying using the RELAYMSG command - relaying: - # is relaying enabled at all? + relaymsg: + # is relaymsg enabled at all? enabled: true # which character(s) are reserved for relayed nicks? diff --git a/default.yaml b/default.yaml index 2b0980bb..519dc331 100644 --- a/default.yaml +++ b/default.yaml @@ -171,8 +171,8 @@ server: motd-formatting: true # relaying using the RELAYMSG command - relaying: - # is relaying enabled at all? + relaymsg: + # is relaymsg enabled at all? enabled: true # which character(s) are reserved for relayed nicks? diff --git a/irc/client_lookup_set.go b/irc/client_lookup_set.go index 945df6a7..de10f6d3 100644 --- a/irc/client_lookup_set.go +++ b/irc/client_lookup_set.go @@ -165,12 +165,8 @@ func (clients *ClientManager) SetNick(client *Client, session *Session, newNick return "", errNicknameInvalid, false } - if config.Server.Relaying.Enabled { - for _, char := range config.Server.Relaying.Separators { - if strings.ContainsRune(newCfNick, char) { - return "", errNicknameInvalid, false - } - } + if config.isRelaymsgIdentifier(newNick) { + return "", errNicknameInvalid, false } if restrictedCasefoldedNicks.Has(newCfNick) || restrictedSkeletons.Has(newSkeleton) { diff --git a/irc/config.go b/irc/config.go index 020eecee..e04e0e4f 100644 --- a/irc/config.go +++ b/irc/config.go @@ -502,7 +502,7 @@ type Config struct { MOTD string motdLines []string MOTDFormatting bool `yaml:"motd-formatting"` - Relaying struct { + Relaymsg struct { Enabled bool Separators string AvailableToChanops bool `yaml:"available-to-chanops"` @@ -1111,13 +1111,13 @@ func LoadConfig(filename string) (config *Config, err error) { } config.Server.capValues[caps.Languages] = config.languageManager.CapValue() - if config.Server.Relaying.Enabled { + if config.Server.Relaymsg.Enabled { for _, char := range protocolBreakingNameCharacters { - if strings.ContainsRune(config.Server.Relaying.Separators, char) { - return nil, fmt.Errorf("Relaying separators cannot include the characters %s", protocolBreakingNameCharacters) + if strings.ContainsRune(config.Server.Relaymsg.Separators, char) { + return nil, fmt.Errorf("RELAYMSG separators cannot include the characters %s", protocolBreakingNameCharacters) } } - config.Server.capValues[caps.Relaymsg] = config.Server.Relaying.Separators + config.Server.capValues[caps.Relaymsg] = config.Server.Relaymsg.Separators } else { config.Server.supportedCaps.Disable(caps.Relaymsg) } @@ -1222,6 +1222,19 @@ func (config *Config) getOutputPath(filename string) string { return filepath.Join(config.Server.OutputPath, filename) } +func (config *Config) isRelaymsgIdentifier(nick string) bool { + if !config.Server.Relaymsg.Enabled { + return false + } + + for _, char := range config.Server.Relaymsg.Separators { + if strings.ContainsRune(nick, char) { + return true + } + } + return false +} + // setISupport sets up our RPL_ISUPPORT reply. func (config *Config) generateISupport() (err error) { maxTargetsString := strconv.Itoa(maxTargets) diff --git a/irc/handlers.go b/irc/handlers.go index f3bac945..ee2daf69 100644 --- a/irc/handlers.go +++ b/irc/handlers.go @@ -2015,21 +2015,12 @@ func messageHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *R } config := server.Config() - if config.Server.Relaying.Enabled { - var isForRelayClient bool - for _, char := range config.Server.Relaying.Separators { - if strings.ContainsRune(targetString, char) { - isForRelayClient = true - break - } - } - if isForRelayClient { - if histType == history.Privmsg { - rb.Add(nil, server.name, ERR_NOSUCHNICK, client.Nick(), targetString, client.t("Relayed users cannot receive private messages")) - } - // TAGMSG/NOTICEs are intentionally silently dropped - continue + if config.isRelaymsgIdentifier(targetString) { + if histType == history.Privmsg { + rb.Add(nil, server.name, ERR_NOSUCHNICK, client.Nick(), targetString, client.t("Relayed users cannot receive private messages")) } + // TAGMSG/NOTICEs are intentionally silently dropped + continue } // each target gets distinct msgids @@ -2443,8 +2434,8 @@ func rehashHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re // RELAYMSG : func relaymsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) (result bool) { config := server.Config() - if !config.Server.Relaying.Enabled { - rb.Add(nil, server.name, "FAIL", "RELAYMSG", "NOT_ENABLED", client.t("Relaying has been disabled")) + if !config.Server.Relaymsg.Enabled { + rb.Add(nil, server.name, "FAIL", "RELAYMSG", "NOT_ENABLED", client.t("RELAYMSG has been disabled")) return false } @@ -2454,7 +2445,7 @@ func relaymsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb * return false } - allowedToRelay := client.HasRoleCapabs("relaymsg-anywhere") || (config.Server.Relaying.AvailableToChanops && channel.ClientIsAtLeast(client, modes.ChannelOperator)) + allowedToRelay := client.HasRoleCapabs("relaymsg-anywhere") || (config.Server.Relaymsg.AvailableToChanops && channel.ClientIsAtLeast(client, modes.ChannelOperator)) if !allowedToRelay { rb.Add(nil, server.name, "FAIL", "RELAYMSG", "NOT_PRIVED", client.t("You cannot relay messages to this channel")) return false @@ -2473,8 +2464,8 @@ func relaymsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb * rb.Add(nil, server.name, "FAIL", "RELAYMSG", "INVALID_NICK", client.t("Invalid nickname")) return false } - if !strings.Contains(nick, "/") { - rb.Add(nil, server.name, "FAIL", "RELAYMSG", "INVALID_NICK", fmt.Sprintf(client.t("Relayed nicknames MUST contain the relaymsg separator %s"), "/")) + if !config.isRelaymsgIdentifier(nick) { + rb.Add(nil, server.name, "FAIL", "RELAYMSG", "INVALID_NICK", fmt.Sprintf(client.t("Relayed nicknames MUST contain a relaymsg separator from this set: %s"), config.Server.Relaymsg.Separators)) return false } @@ -2486,16 +2477,21 @@ func relaymsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb * // send msg channelName := channel.Name() + relayTags := map[string]string{ + "relaymsg": client.Nick(), + } for _, member := range channel.Members() { for _, session := range member.Sessions() { var tagsToUse map[string]string if session.capabilities.Has(caps.Relaymsg) { - tagsToUse = map[string]string{ - "relaymsg": client.Nick(), - } + tagsToUse = relayTags } - session.sendSplitMsgFromClientInternal(false, nick, "", tagsToUse, "PRIVMSG", channelName, message) + if session == rb.session { + rb.AddSplitMessageFromClient(nick, "*", tagsToUse, "PRIVMSG", channelName, message) + } else { + session.sendSplitMsgFromClientInternal(false, nick, "*", tagsToUse, "PRIVMSG", channelName, message) + } } } return false diff --git a/irc/roleplay.go b/irc/roleplay.go index 42a87e74..fdd59584 100644 --- a/irc/roleplay.go +++ b/irc/roleplay.go @@ -82,7 +82,7 @@ func sendRoleplayMessage(server *Server, client *Client, source string, targetSt if rb.session == session { rb.AddSplitMessageFromClient(source, "", nil, "PRIVMSG", targetString, splitMessage) } else { - session.sendSplitMsgFromClientInternal(false, source, "", nil, "PRIVMSG", targetString, splitMessage) + session.sendSplitMsgFromClientInternal(false, source, "*", nil, "PRIVMSG", targetString, splitMessage) } } } @@ -108,7 +108,7 @@ func sendRoleplayMessage(server *Server, client *Client, source string, targetSt cnick := client.Nick() tnick := user.Nick() for _, session := range user.Sessions() { - session.sendSplitMsgFromClientInternal(false, source, "", nil, "PRIVMSG", tnick, splitMessage) + session.sendSplitMsgFromClientInternal(false, source, "*", nil, "PRIVMSG", tnick, splitMessage) } if away, awayMessage := user.Away(); away { //TODO(dan): possibly implement cooldown of away notifications to users diff --git a/irc/server.go b/irc/server.go index c83b070b..4b178f18 100644 --- a/irc/server.go +++ b/irc/server.go @@ -485,9 +485,9 @@ func (server *Server) applyConfig(config *Config) (err error) { return fmt.Errorf("UTF-8 enforcement cannot be changed after launching the server, rehash aborted") } else if oldConfig.Accounts.Multiclient.AlwaysOn != config.Accounts.Multiclient.AlwaysOn { return fmt.Errorf("Default always-on setting cannot be changed after launching the server, rehash aborted") - } else if oldConfig.Server.Relaying.Enabled != config.Server.Relaying.Enabled { + } else if oldConfig.Server.Relaymsg.Enabled != config.Server.Relaymsg.Enabled { return fmt.Errorf("Cannot enable or disable relaying after launching the server, rehash aborted") - } else if oldConfig.Server.Relaying.Separators != config.Server.Relaying.Separators { + } else if oldConfig.Server.Relaymsg.Separators != config.Server.Relaymsg.Separators { return fmt.Errorf("Cannot change relaying separators after launching the server, rehash aborted") } }