Add support for stateless bridging via draft/relaymsg (irc) (#1339)

* irc: add support for stateless bridging via draft/relaymsg

As discussed at https://github.com/42wim/matterbridge/issues/667#issuecomment-634214165

* irc: handle the draft/relaymsg tag in spoofed messages too

* Apply suggestions from code review

Co-authored-by: Wim <wim@42.be>

* Run gofmt on irc.go

* Document relaymsg in matterbridge.toml.sample

Co-authored-by: Wim <wim@42.be>
This commit is contained in:
James Lu 2020-12-30 09:21:32 -08:00 committed by GitHub
parent 58ea1e07d2
commit a9f89dbc64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 70 additions and 16 deletions

View File

@ -205,27 +205,58 @@ func (b *Birc) doConnect() {
} }
} }
// Sanitize nicks for RELAYMSG: replace IRC characters with special meanings with "-"
func sanitizeNick(nick string) string {
sanitize := func(r rune) rune {
if strings.ContainsRune("!+%@&#$:'\"?*,. ", r) {
return '-'
}
return r
}
return strings.Map(sanitize, nick)
}
func (b *Birc) doSend() { func (b *Birc) doSend() {
rate := time.Millisecond * time.Duration(b.MessageDelay) rate := time.Millisecond * time.Duration(b.MessageDelay)
throttle := time.NewTicker(rate) throttle := time.NewTicker(rate)
for msg := range b.Local { for msg := range b.Local {
<-throttle.C <-throttle.C
username := msg.Username username := msg.Username
if b.GetBool("Colornicks") && len(username) > 1 { // Optional support for the proposed RELAYMSG extension, described at
checksum := crc32.ChecksumIEEE([]byte(msg.Username)) // https://github.com/jlu5/ircv3-specifications/blob/master/extensions/relaymsg.md
colorCode := checksum%14 + 2 // quick fix - prevent white or black color codes // nolint:nestif
username = fmt.Sprintf("\x03%02d%s\x0F", colorCode, msg.Username) if (b.i.HasCapability("overdrivenetworks.com/relaymsg") || b.i.HasCapability("draft/relaymsg")) &&
} b.GetBool("UseRelayMsg") {
username = sanitizeNick(username)
text := msg.Text
switch msg.Event { // Work around girc chomping leading commas on single word messages?
case config.EventUserAction: if strings.HasPrefix(text, ":") && !strings.ContainsRune(text, ' ') {
b.i.Cmd.Action(msg.Channel, username+msg.Text) text = ":" + text
case config.EventNoticeIRC: }
b.Log.Debugf("Sending notice to channel %s", msg.Channel)
b.i.Cmd.Notice(msg.Channel, username+msg.Text) if msg.Event == config.EventUserAction {
default: b.i.Cmd.SendRawf("RELAYMSG %s %s :\x01ACTION %s\x01", msg.Channel, username, text) //nolint:errcheck
b.Log.Debugf("Sending to channel %s", msg.Channel) } else {
b.i.Cmd.Message(msg.Channel, username+msg.Text) b.Log.Debugf("Sending RELAYMSG to channel %s: nick=%s", msg.Channel, username)
b.i.Cmd.SendRawf("RELAYMSG %s %s :%s", msg.Channel, username, text) //nolint:errcheck
}
} else {
if b.GetBool("Colornicks") {
checksum := crc32.ChecksumIEEE([]byte(msg.Username))
colorCode := checksum%14 + 2 // quick fix - prevent white or black color codes
username = fmt.Sprintf("\x03%02d%s\x0F", colorCode, msg.Username)
}
switch msg.Event {
case config.EventUserAction:
b.i.Cmd.Action(msg.Channel, username+msg.Text)
case config.EventNoticeIRC:
b.Log.Debugf("Sending notice to channel %s", msg.Channel)
b.i.Cmd.Notice(msg.Channel, username+msg.Text)
default:
b.Log.Debugf("Sending to channel %s", msg.Channel)
b.i.Cmd.Message(msg.Channel, username+msg.Text)
}
} }
} }
} }
@ -273,8 +304,9 @@ func (b *Birc) getClient() (*girc.Client, error) {
TLSConfig: &tls.Config{InsecureSkipVerify: b.GetBool("SkipTLSVerify"), ServerName: server}, //nolint:gosec TLSConfig: &tls.Config{InsecureSkipVerify: b.GetBool("SkipTLSVerify"), ServerName: server}, //nolint:gosec
PingDelay: pingDelay, PingDelay: pingDelay,
// skip gIRC internal rate limiting, since we have our own throttling // skip gIRC internal rate limiting, since we have our own throttling
AllowFlood: true, AllowFlood: true,
Debug: debug, Debug: debug,
SupportedCaps: map[string][]string{"overdrivenetworks.com/relaymsg": nil, "draft/relaymsg": nil},
}) })
return i, nil return i, nil
} }
@ -311,6 +343,15 @@ func (b *Birc) skipPrivMsg(event girc.Event) bool {
if event.Source.Name == b.Nick { if event.Source.Name == b.Nick {
return true return true
} }
// don't forward messages we sent via RELAYMSG
if relayedNick, ok := event.Tags.Get("draft/relaymsg"); ok && relayedNick == b.Nick {
return true
}
// This is the old name of the cap sent in spoofed messages; I've kept this in
// for compatibility reasons
if relayedNick, ok := event.Tags.Get("relaymsg"); ok && relayedNick == b.Nick {
return true
}
return false return false
} }

View File

@ -193,6 +193,19 @@ ShowTopicChange=false
#OPTIONAL (default 0) #OPTIONAL (default 0)
JoinDelay=0 JoinDelay=0
#Use the optional RELAYMSG extension for username spoofing on IRC.
#This requires an IRCd that supports the draft/relaymsg specification: currently this includes
#Oragono 2.4.0+ and InspIRCd 3 with the m_relaymsg contrib module.
#See https://github.com/42wim/matterbridge/issues/667#issuecomment-634214165 for more details.
#Spoofed nicks will use the configured RemoteNickFormat, replacing reserved IRC characters
#(!+%@&#$:'"?*,.) with a hyphen (-).
#On most configurations, the RemoteNickFormat must include a separator character such as "/".
#You should make sure that the settings here match your IRCd.
#This option overrides ColorNicks.
#OPTIONAL (default false)
UseRelayMsg=false
#RemoteNickFormat="{NICK}/{PROTOCOL}"
################################################################### ###################################################################
#XMPP section #XMPP section
################################################################### ###################################################################