Index: trunk/downstream.go
===================================================================
--- trunk/downstream.go	(revision 244)
+++ trunk/downstream.go	(revision 245)
@@ -223,15 +223,33 @@
 }
 
-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
 }
 
@@ -685,5 +703,17 @@
 			}
 
-			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))
 		}
 	})
Index: trunk/upstream.go
===================================================================
--- trunk/upstream.go	(revision 244)
+++ trunk/upstream.go	(revision 245)
@@ -563,10 +563,7 @@
 
 		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))
 			})
 		}
@@ -602,13 +599,7 @@
 			}
 
-			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":
@@ -620,9 +611,4 @@
 		if err := parseMessageParams(msg, &channels); err != nil {
 			return err
-		}
-
-		var reason string
-		if len(msg.Params) > 1 {
-			reason = msg.Params[1]
 		}
 
@@ -639,17 +625,7 @@
 			}
 
-			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":
@@ -661,9 +637,4 @@
 		if err := parseMessageParams(msg, &channel, &user); err != nil {
 			return err
-		}
-
-		var reason string
-		if len(msg.Params) > 2 {
-			reason = msg.Params[2]
 		}
 
@@ -679,17 +650,5 @@
 		}
 
-		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 {
@@ -710,10 +669,7 @@
 
 		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))
 			})
 		}
@@ -746,16 +702,5 @@
 			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
@@ -782,16 +727,5 @@
 			}
 
-			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:
@@ -1363,4 +1297,9 @@
 }
 
+// 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 != "" {
@@ -1372,5 +1311,5 @@
 	uc.forEachDownstream(func(dc *downstreamConn) {
 		if dc != origin || dc.caps["echo-message"] {
-			dc.sendFromUpstream(msg, uc)
+			dc.SendMessage(dc.marshalMessage(msg, uc))
 		}
 	})
