From 3195809c3089c7543c1126d1db686e5bdaa54a84 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 7 Apr 2020 19:45:29 +0200 Subject: [PATCH] Centralize logged messages marshaling This allows messages added to logs to be handled just like messages added to the ring buffer. --- downstream.go | 38 +++++++++++++++++--- upstream.go | 99 ++++++++++----------------------------------------- 2 files changed, 53 insertions(+), 84 deletions(-) diff --git a/downstream.go b/downstream.go index 0dcd2b7..c493520 100644 --- a/downstream.go +++ b/downstream.go @@ -222,17 +222,35 @@ func (dc *downstreamConn) SendMessage(msg *irc.Message) { dc.conn.SendMessage(msg) } -func (dc *downstreamConn) sendFromUpstream(msg *irc.Message, uc *upstreamConn) { +// marshalMessage re-formats a message coming from an upstream connection so +// that it's suitable for being sent on this downstream connection. Only +// messages that may appear in logs are supported. +func (dc *downstreamConn) marshalMessage(msg *irc.Message, uc *upstreamConn) *irc.Message { msg = msg.Copy() + msg.Prefix = dc.marshalUserPrefix(uc, msg.Prefix) + switch msg.Command { case "PRIVMSG", "NOTICE": - msg.Prefix = dc.marshalUserPrefix(uc, msg.Prefix) msg.Params[0] = dc.marshalEntity(uc, msg.Params[0]) + case "NICK": + // Nick change for another user + msg.Params[0] = dc.marshalNick(uc, msg.Params[0]) + case "JOIN", "PART": + msg.Params[0] = dc.marshalChannel(uc, msg.Params[0]) + case "KICK": + msg.Params[0] = dc.marshalChannel(uc, msg.Params[0]) + msg.Params[1] = dc.marshalNick(uc, msg.Params[1]) + case "TOPIC": + msg.Params[0] = dc.marshalChannel(uc, msg.Params[0]) + case "MODE": + msg.Params[0] = dc.marshalEntity(uc, msg.Params[0]) + case "QUIT": + // This space is intentinally left blank default: panic(fmt.Sprintf("unexpected %q message", msg.Command)) } - dc.SendMessage(msg) + return msg } func (dc *downstreamConn) handleMessage(msg *irc.Message) error { @@ -684,7 +702,19 @@ func (dc *downstreamConn) welcome() error { break } - dc.sendFromUpstream(msg, uc) + // Don't replay all messages, because that would mess up client + // state. For instance we just sent the list of users, sending + // PART messages for one of these users would be incorrect. + ignore := true + switch msg.Command { + case "PRIVMSG", "NOTICE": + ignore = false + } + if ignore { + continue + } + + dc.SendMessage(dc.marshalMessage(msg, uc)) } }) diff --git a/upstream.go b/upstream.go index 888f6a7..122a3d1 100644 --- a/upstream.go +++ b/upstream.go @@ -562,12 +562,9 @@ func (uc *upstreamConn) handleMessage(msg *irc.Message) error { } if !me { + uc.network.ring.Produce(msg) uc.forEachDownstream(func(dc *downstreamConn) { - dc.SendMessage(&irc.Message{ - Prefix: dc.marshalUserPrefix(uc, msg.Prefix), - Command: "NICK", - Params: []string{dc.marshalEntity(uc, newNick)}, - }) + dc.SendMessage(dc.marshalMessage(msg, uc)) }) } case "JOIN": @@ -601,15 +598,9 @@ func (uc *upstreamConn) handleMessage(msg *irc.Message) error { ch.Members[msg.Prefix.Name] = nil } - uc.appendLog(ch, msg) - - uc.forEachDownstream(func(dc *downstreamConn) { - dc.SendMessage(&irc.Message{ - Prefix: dc.marshalUserPrefix(uc, msg.Prefix), - Command: "JOIN", - Params: []string{dc.marshalChannel(uc, ch)}, - }) - }) + chMsg := msg.Copy() + chMsg.Params[0] = ch + uc.produce(ch, chMsg, nil) } case "PART": if msg.Prefix == nil { @@ -621,11 +612,6 @@ func (uc *upstreamConn) handleMessage(msg *irc.Message) error { return err } - var reason string - if len(msg.Params) > 1 { - reason = msg.Params[1] - } - for _, ch := range strings.Split(channels, ",") { if msg.Prefix.Name == uc.nick { uc.logger.Printf("parted channel %q", ch) @@ -638,19 +624,9 @@ func (uc *upstreamConn) handleMessage(msg *irc.Message) error { delete(ch.Members, msg.Prefix.Name) } - uc.appendLog(ch, msg) - - uc.forEachDownstream(func(dc *downstreamConn) { - params := []string{dc.marshalChannel(uc, ch)} - if reason != "" { - params = append(params, reason) - } - dc.SendMessage(&irc.Message{ - Prefix: dc.marshalUserPrefix(uc, msg.Prefix), - Command: "PART", - Params: params, - }) - }) + chMsg := msg.Copy() + chMsg.Params[0] = ch + uc.produce(ch, chMsg, nil) } case "KICK": if msg.Prefix == nil { @@ -662,11 +638,6 @@ func (uc *upstreamConn) handleMessage(msg *irc.Message) error { return err } - var reason string - if len(msg.Params) > 2 { - reason = msg.Params[2] - } - if user == uc.nick { uc.logger.Printf("kicked from channel %q by %s", channel, msg.Prefix.Name) delete(uc.channels, channel) @@ -678,19 +649,7 @@ func (uc *upstreamConn) handleMessage(msg *irc.Message) error { delete(ch.Members, user) } - uc.appendLog(channel, msg) - - uc.forEachDownstream(func(dc *downstreamConn) { - params := []string{dc.marshalChannel(uc, channel), dc.marshalNick(uc, user)} - if reason != "" { - params = append(params, reason) - } - dc.SendMessage(&irc.Message{ - Prefix: dc.marshalUserPrefix(uc, msg.Prefix), - Command: "KICK", - Params: params, - }) - }) + uc.produce(channel, msg, nil) case "QUIT": if msg.Prefix == nil { return fmt.Errorf("expected a prefix") @@ -709,12 +668,9 @@ func (uc *upstreamConn) handleMessage(msg *irc.Message) error { } if msg.Prefix.Name != uc.nick { + uc.network.ring.Produce(msg) uc.forEachDownstream(func(dc *downstreamConn) { - dc.SendMessage(&irc.Message{ - Prefix: dc.marshalUserPrefix(uc, msg.Prefix), - Command: "QUIT", - Params: msg.Params, - }) + dc.SendMessage(dc.marshalMessage(msg, uc)) }) } case irc.RPL_TOPIC, irc.RPL_NOTOPIC: @@ -745,18 +701,7 @@ func (uc *upstreamConn) handleMessage(msg *irc.Message) error { } else { ch.Topic = "" } - uc.appendLog(ch.Name, msg) - uc.forEachDownstream(func(dc *downstreamConn) { - params := []string{dc.marshalChannel(uc, name)} - if ch.Topic != "" { - params = append(params, ch.Topic) - } - dc.SendMessage(&irc.Message{ - Prefix: dc.marshalUserPrefix(uc, msg.Prefix), - Command: "TOPIC", - Params: params, - }) - }) + uc.produce(ch.Name, msg, nil) case "MODE": var name, modeStr string if err := parseMessageParams(msg, &name, &modeStr); err != nil { @@ -781,18 +726,7 @@ func (uc *upstreamConn) handleMessage(msg *irc.Message) error { } } - uc.appendLog(ch.Name, msg) - - uc.forEachDownstream(func(dc *downstreamConn) { - params := []string{dc.marshalChannel(uc, name), modeStr} - params = append(params, msg.Params[2:]...) - - dc.SendMessage(&irc.Message{ - Prefix: dc.marshalUserPrefix(uc, msg.Prefix), - Command: "MODE", - Params: params, - }) - }) + uc.produce(ch.Name, msg, nil) } case irc.RPL_UMODEIS: if err := parseMessageParams(msg, nil); err != nil { @@ -1362,6 +1296,11 @@ func (uc *upstreamConn) appendLog(entity string, msg *irc.Message) { } } +// produce appends a message to the logs, adds it to the history and forwards +// it to connected downstream connections. +// +// If origin is not nil and origin doesn't support echo-message, the message is +// forwarded to all connections except origin. func (uc *upstreamConn) produce(target string, msg *irc.Message, origin *downstreamConn) { if target != "" { uc.appendLog(target, msg) @@ -1371,7 +1310,7 @@ func (uc *upstreamConn) produce(target string, msg *irc.Message, origin *downstr uc.forEachDownstream(func(dc *downstreamConn) { if dc != origin || dc.caps["echo-message"] { - dc.sendFromUpstream(msg, uc) + dc.SendMessage(dc.marshalMessage(msg, uc)) } }) }