Index: trunk/downstream.go
===================================================================
--- trunk/downstream.go	(revision 498)
+++ trunk/downstream.go	(revision 499)
@@ -1031,7 +1031,6 @@
 		return
 	}
-	if ch := net.channels.Value(target); ch != nil && ch.Detached {
-		return
-	}
+
+	ch := net.channels.Value(target)
 
 	limit := 4000
@@ -1057,8 +1056,14 @@
 		}
 
-		if dc.caps["batch"] {
-			msg.Tags["batch"] = irc.TagValue(batchRef)
-		}
-		dc.SendMessage(dc.marshalMessage(msg, net))
+		if ch != nil && ch.Detached {
+			if net.detachedMessageNeedsRelay(ch, msg) {
+				dc.relayDetachedMessage(net, msg)
+			}
+		} else {
+			if dc.caps["batch"] {
+				msg.Tags["batch"] = irc.TagValue(batchRef)
+			}
+			dc.SendMessage(dc.marshalMessage(msg, net))
+		}
 	}
 
@@ -1069,4 +1074,18 @@
 			Params:  []string{"-" + batchRef},
 		})
+	}
+}
+
+func (dc *downstreamConn) relayDetachedMessage(net *network, msg *irc.Message) {
+	if msg.Command != "PRIVMSG" && msg.Command != "NOTICE" {
+		return
+	}
+
+	sender := msg.Prefix.Name
+	target, text := msg.Params[0], msg.Params[1]
+	if net.isHighlight(msg) {
+		sendServiceNOTICE(dc, fmt.Sprintf("highlight in %v: <%v> %v", dc.marshalEntity(net, target), sender, text))
+	} else {
+		sendServiceNOTICE(dc, fmt.Sprintf("message in %v: <%v> %v", dc.marshalEntity(net, target), sender, text))
 	}
 }
Index: trunk/upstream.go
===================================================================
--- trunk/upstream.go	(revision 498)
+++ trunk/upstream.go	(revision 499)
@@ -392,8 +392,8 @@
 			if ch != nil {
 				if ch.Detached {
-					uc.handleDetachedMessage(msg.Prefix.Name, text, ch)
+					uc.handleDetachedMessage(ch, msg)
 				}
 
-				highlight := msg.Prefix.Name != uc.nick && isHighlight(text, uc.nick)
+				highlight := uc.network.isHighlight(msg)
 				if ch.DetachOn == FilterMessage || ch.DetachOn == FilterDefault || (ch.DetachOn == FilterHighlight && highlight) {
 					uc.updateChannelAutoDetach(target)
@@ -1438,16 +1438,11 @@
 }
 
-func (uc *upstreamConn) handleDetachedMessage(sender string, text string, ch *Channel) {
-	highlight := sender != uc.nick && isHighlight(text, uc.nick)
-	if ch.RelayDetached == FilterMessage || ((ch.RelayDetached == FilterHighlight || ch.RelayDetached == FilterDefault) && highlight) {
+func (uc *upstreamConn) handleDetachedMessage(ch *Channel, msg *irc.Message) {
+	if uc.network.detachedMessageNeedsRelay(ch, msg) {
 		uc.forEachDownstream(func(dc *downstreamConn) {
-			if highlight {
-				sendServiceNOTICE(dc, fmt.Sprintf("highlight in %v: <%v> %v", dc.marshalEntity(uc.network, ch.Name), sender, text))
-			} else {
-				sendServiceNOTICE(dc, fmt.Sprintf("message in %v: <%v> %v", dc.marshalEntity(uc.network, ch.Name), sender, text))
-			}
-		})
-	}
-	if ch.ReattachOn == FilterMessage || (ch.ReattachOn == FilterHighlight && highlight) {
+			dc.relayDetachedMessage(uc.network, msg)
+		})
+	}
+	if ch.ReattachOn == FilterMessage || (ch.ReattachOn == FilterHighlight && uc.network.isHighlight(msg)) {
 		uc.network.attach(ch)
 		if err := uc.srv.db.StoreChannel(uc.network.ID, ch); err != nil {
@@ -1744,10 +1739,8 @@
 	// Don't forward messages if it's a detached channel
 	ch := uc.network.channels.Value(target)
-	if ch != nil && ch.Detached {
-		return
-	}
+	detached := ch != nil && ch.Detached
 
 	uc.forEachDownstream(func(dc *downstreamConn) {
-		if dc != origin || dc.caps["echo-message"] {
+		if !detached && (dc != origin || dc.caps["echo-message"]) {
 			dc.sendMessageWithID(dc.marshalMessage(msg, uc.network), msgID)
 		} else {
Index: trunk/user.go
===================================================================
--- trunk/user.go	(revision 498)
+++ trunk/user.go	(revision 499)
@@ -352,4 +352,25 @@
 }
 
+func (net *network) isHighlight(msg *irc.Message) bool {
+	if msg.Command != "PRIVMSG" && msg.Command != "NOTICE" {
+		return false
+	}
+
+	text := msg.Params[1]
+
+	nick := net.Nick
+	if net.conn != nil {
+		nick = net.conn.nick
+	}
+
+	// TODO: use case-mapping aware comparison here
+	return msg.Prefix.Name != nick && isHighlight(text, nick)
+}
+
+func (net *network) detachedMessageNeedsRelay(ch *Channel, msg *irc.Message) bool {
+	highlight := net.isHighlight(msg)
+	return ch.RelayDetached == FilterMessage || ((ch.RelayDetached == FilterHighlight || ch.RelayDetached == FilterDefault) && highlight)
+}
+
 type user struct {
 	User
