From 54275c25ac950fecfd277a8ebc67bda5f32a3413 Mon Sep 17 00:00:00 2001 From: delthas Date: Fri, 20 Mar 2020 00:23:19 +0100 Subject: [PATCH] Add WHO support --- downstream.go | 64 ++++++++++++++++++++++++++++++--------------------- upstream.go | 49 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 26 deletions(-) diff --git a/downstream.go b/downstream.go index 6adf14c..5de6f8a 100644 --- a/downstream.go +++ b/downstream.go @@ -169,49 +169,31 @@ func (dc *downstreamConn) marshalChannel(uc *upstreamConn, name string) string { return name + "/" + uc.network.GetName() } -func (dc *downstreamConn) unmarshalChannel(name string) (*upstreamConn, string, error) { +func (dc *downstreamConn) unmarshalEntity(name string) (*upstreamConn, string, error) { if uc := dc.upstream(); uc != nil { return uc, name, nil } - network := "" + var conn *upstreamConn if i := strings.LastIndexByte(name, '/'); i >= 0 { - network = name[i+1:] + network := name[i+1:] name = name[:i] - } - if network != "" { - var conn *upstreamConn dc.forEachUpstream(func(uc *upstreamConn) { if network != uc.network.GetName() { return } conn = uc }) - return conn, name, nil } - var channel *upstreamChannel - var err error - dc.forEachUpstream(func(uc *upstreamConn) { - if err != nil { - return - } - if ch, ok := uc.channels[name]; ok { - if channel != nil { - err = fmt.Errorf("ambiguous channel name %q", name) - } else { - channel = ch - } - } - }) - if channel == nil { + if conn == nil { return nil, "", ircError{&irc.Message{ Command: irc.ERR_NOSUCHCHANNEL, Params: []string{name, "No such channel"}, }} } - return channel.conn, channel.Name, nil + return conn, name, nil } func (dc *downstreamConn) marshalNick(uc *upstreamConn, nick string) string { @@ -843,7 +825,7 @@ func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error { return err } - uc, upstreamName, err := dc.unmarshalChannel(name) + uc, upstreamName, err := dc.unmarshalEntity(name) if err != nil { return ircError{&irc.Message{ Command: irc.ERR_NOSUCHCHANNEL, @@ -885,7 +867,7 @@ func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error { } if msg.Prefix.Name != name { - uc, upstreamName, err := dc.unmarshalChannel(name) + uc, upstreamName, err := dc.unmarshalEntity(name) if err != nil { return err } @@ -933,6 +915,36 @@ func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error { }) } } + case "WHO": + if len(msg.Params) == 0 { + // TODO: support WHO without parameters + dc.SendMessage(&irc.Message{ + Prefix: dc.srv.prefix(), + Command: irc.RPL_ENDOFWHO, + Params: []string{dc.nick, "*", "End of /WHO list."}, + }) + return nil + } + + // TODO: support WHO masks + entity := msg.Params[0] + + uc, upstreamName, err := dc.unmarshalEntity(entity) + if err != nil { + return err + } + + var params []string + if len(msg.Params) == 2 { + params = []string{upstreamName, msg.Params[1]} + } else { + params = []string{upstreamName} + } + + uc.SendMessage(&irc.Message{ + Command: "WHO", + Params: params, + }) case "PRIVMSG": var targetsStr, text string if err := parseMessageParams(msg, &targetsStr, &text); err != nil { @@ -945,7 +957,7 @@ func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error { continue } - uc, upstreamName, err := dc.unmarshalChannel(name) + uc, upstreamName, err := dc.unmarshalEntity(name) if err != nil { return err } diff --git a/upstream.go b/upstream.go index 672b94b..c0e3868 100644 --- a/upstream.go +++ b/upstream.go @@ -557,6 +557,55 @@ func (uc *upstreamConn) handleMessage(msg *irc.Message) error { uc.forEachDownstream(func(dc *downstreamConn) { forwardChannel(dc, ch) }) + case irc.RPL_WHOREPLY: + var channel, username, host, server, nick, mode, trailing string + if err := parseMessageParams(msg, nil, &channel, &username, &host, &server, &nick, &mode, &trailing); err != nil { + return err + } + + parts := strings.SplitN(trailing, " ", 2) + if len(parts) != 2 { + return fmt.Errorf("received malformed RPL_WHOREPLY: wrong trailing parameter: %s", trailing) + } + realname := parts[1] + hops, err := strconv.Atoi(parts[0]) + if err != nil { + return fmt.Errorf("received malformed RPL_WHOREPLY: wrong hop count: %s", parts[0]) + } + hops++ + + trailing = strconv.Itoa(hops) + " " + realname + + uc.forEachDownstream(func(dc *downstreamConn) { + channel := channel + if channel != "*" { + channel = dc.marshalChannel(uc, channel) + } + nick := dc.marshalNick(uc, nick) + dc.SendMessage(&irc.Message{ + Prefix: dc.srv.prefix(), + Command: irc.RPL_WHOREPLY, + Params: []string{dc.nick, channel, username, host, server, nick, mode, trailing}, + }) + }) + case irc.RPL_ENDOFWHO: + var name string + if err := parseMessageParams(msg, nil, &name); err != nil { + return err + } + + uc.forEachDownstream(func(dc *downstreamConn) { + name := name + if name != "*" { + // TODO: support WHO masks + name = dc.marshalEntity(uc, name) + } + dc.SendMessage(&irc.Message{ + Prefix: dc.srv.prefix(), + Command: irc.RPL_ENDOFWHO, + Params: []string{dc.nick, name, "End of /WHO list."}, + }) + }) case "PRIVMSG": if msg.Prefix == nil { return fmt.Errorf("expected a prefix")