diff --git a/downstream.go b/downstream.go index 72cbff8..465c4c4 100644 --- a/downstream.go +++ b/downstream.go @@ -1132,6 +1132,17 @@ func (dc *downstreamConn) updateNick() { } } +func (dc *downstreamConn) updateHost() { + if uc := dc.upstream(); uc != nil && uc.hostname != "" && uc.hostname != dc.hostname { + dc.SendMessage(&irc.Message{ + Prefix: dc.prefix(), + Command: rpl_visiblehost, + Params: []string{dc.nick, uc.hostname, "is now your visible host"}, + }) + dc.hostname = uc.hostname + } +} + func (dc *downstreamConn) updateRealname() { if uc := dc.upstream(); uc != nil && uc.realname != dc.realname && dc.caps.IsEnabled("setname") { dc.SendMessage(&irc.Message{ @@ -1471,6 +1482,7 @@ func (dc *downstreamConn) welcome(ctx context.Context) error { }) } + dc.updateHost() dc.updateRealname() dc.updateAccount() diff --git a/irc.go b/irc.go index 720d3f2..876bd90 100644 --- a/irc.go +++ b/irc.go @@ -19,6 +19,7 @@ const ( rpl_topicwhotime = "333" rpl_whospcrpl = "354" rpl_whoisaccount = "330" + rpl_visiblehost = "396" err_invalidcapcmd = "410" // https://ircv3.net/specs/extensions/bot-mode diff --git a/upstream.go b/upstream.go index 1a8e066..d140b70 100644 --- a/upstream.go +++ b/upstream.go @@ -122,6 +122,7 @@ type upstreamConn struct { nickCM string username string realname string + hostname string modes userModes channels upstreamChannelCasemapMap caps capRegistry @@ -637,18 +638,52 @@ func (uc *upstreamConn) handleMessage(ctx context.Context, msg *irc.Message) err }) } case irc.RPL_LOGGEDIN: - if err := parseMessageParams(msg, nil, nil, &uc.account); err != nil { + var rawPrefix string + if err := parseMessageParams(msg, nil, &rawPrefix, &uc.account); err != nil { return err } + + prefix := irc.ParsePrefix(rawPrefix) + uc.username = prefix.User + uc.hostname = prefix.Host + uc.logger.Printf("logged in with account %q", uc.account) uc.forEachDownstream(func(dc *downstreamConn) { dc.updateAccount() + dc.updateHost() }) case irc.RPL_LOGGEDOUT: + var rawPrefix string + if err := parseMessageParams(msg, nil, &rawPrefix); err != nil { + return err + } + uc.account = "" + + prefix := irc.ParsePrefix(rawPrefix) + uc.username = prefix.User + uc.hostname = prefix.Host + uc.logger.Printf("logged out") uc.forEachDownstream(func(dc *downstreamConn) { dc.updateAccount() + dc.updateHost() + }) + case rpl_visiblehost: + var rawHost string + if err := parseMessageParams(msg, nil, &rawHost); err != nil { + return err + } + + parts := strings.SplitN(rawHost, "@", 2) + if len(parts) == 2 { + uc.username, uc.hostname = parts[0], parts[1] + } else { + uc.hostname = rawHost + } + + uc.forEachDownstream(func(dc *downstreamConn) { + dc.updateHost() }) case irc.ERR_NICKLOCKED, irc.RPL_SASLSUCCESS, irc.ERR_SASLFAIL, irc.ERR_SASLTOOLONG, irc.ERR_SASLABORTED: var info string diff --git a/user.go b/user.go index e8a05f7..bf1909f 100644 --- a/user.go +++ b/user.go @@ -567,6 +567,7 @@ func (u *user) run() { } dc.updateNick() + dc.updateHost() dc.updateRealname() dc.updateAccount() })