From 4ee49f8450d044c9bc7b15a8a4ef9e18f0f52a2a Mon Sep 17 00:00:00 2001 From: Daniel Oaks Date: Mon, 8 Jun 2020 10:19:28 +1000 Subject: [PATCH] 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: