Add WHO support

This commit is contained in:
delthas 2020-03-20 00:23:19 +01:00 committed by Simon Ser
parent 549fbf62b1
commit 54275c25ac
2 changed files with 87 additions and 26 deletions

View File

@ -169,49 +169,31 @@ func (dc *downstreamConn) marshalChannel(uc *upstreamConn, name string) string {
return name + "/" + uc.network.GetName() 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 { if uc := dc.upstream(); uc != nil {
return uc, name, nil return uc, name, nil
} }
network := ""
if i := strings.LastIndexByte(name, '/'); i >= 0 {
network = name[i+1:]
name = name[:i]
}
if network != "" {
var conn *upstreamConn var conn *upstreamConn
if i := strings.LastIndexByte(name, '/'); i >= 0 {
network := name[i+1:]
name = name[:i]
dc.forEachUpstream(func(uc *upstreamConn) { dc.forEachUpstream(func(uc *upstreamConn) {
if network != uc.network.GetName() { if network != uc.network.GetName() {
return return
} }
conn = uc conn = uc
}) })
return conn, name, nil
} }
var channel *upstreamChannel if conn == nil {
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 {
return nil, "", ircError{&irc.Message{ return nil, "", ircError{&irc.Message{
Command: irc.ERR_NOSUCHCHANNEL, Command: irc.ERR_NOSUCHCHANNEL,
Params: []string{name, "No such channel"}, 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 { func (dc *downstreamConn) marshalNick(uc *upstreamConn, nick string) string {
@ -843,7 +825,7 @@ func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
return err return err
} }
uc, upstreamName, err := dc.unmarshalChannel(name) uc, upstreamName, err := dc.unmarshalEntity(name)
if err != nil { if err != nil {
return ircError{&irc.Message{ return ircError{&irc.Message{
Command: irc.ERR_NOSUCHCHANNEL, Command: irc.ERR_NOSUCHCHANNEL,
@ -885,7 +867,7 @@ func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
} }
if msg.Prefix.Name != name { if msg.Prefix.Name != name {
uc, upstreamName, err := dc.unmarshalChannel(name) uc, upstreamName, err := dc.unmarshalEntity(name)
if err != nil { if err != nil {
return err 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": case "PRIVMSG":
var targetsStr, text string var targetsStr, text string
if err := parseMessageParams(msg, &targetsStr, &text); err != nil { if err := parseMessageParams(msg, &targetsStr, &text); err != nil {
@ -945,7 +957,7 @@ func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
continue continue
} }
uc, upstreamName, err := dc.unmarshalChannel(name) uc, upstreamName, err := dc.unmarshalEntity(name)
if err != nil { if err != nil {
return err return err
} }

View File

@ -557,6 +557,55 @@ func (uc *upstreamConn) handleMessage(msg *irc.Message) error {
uc.forEachDownstream(func(dc *downstreamConn) { uc.forEachDownstream(func(dc *downstreamConn) {
forwardChannel(dc, ch) 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": case "PRIVMSG":
if msg.Prefix == nil { if msg.Prefix == nil {
return fmt.Errorf("expected a prefix") return fmt.Errorf("expected a prefix")