Index: /trunk/downstream.go
===================================================================
--- /trunk/downstream.go	(revision 539)
+++ /trunk/downstream.go	(revision 540)
@@ -123,4 +123,5 @@
 	"sasl":          "PLAIN",
 	"server-time":   "",
+	"setname":       "",
 
 	"soju.im/bouncer-networks":        "",
@@ -155,4 +156,5 @@
 	"MAXTARGETS": true,
 	"MODES":      true,
+	"NAMELEN":    true,
 	"NETWORK":    true,
 	"NICKLEN":    true,
@@ -380,4 +382,7 @@
 	if msg.Command == "JOIN" && !dc.caps["extended-join"] {
 		msg.Params = msg.Params[:1]
+	}
+	if msg.Command == "SETNAME" && !dc.caps["setname"] {
+		return
 	}
 
@@ -460,5 +465,5 @@
 	case "TOPIC":
 		msg.Params[0] = dc.marshalEntity(net, msg.Params[0])
-	case "QUIT":
+	case "QUIT", "SETNAME":
 		// This space is intentionally left blank
 	default:
@@ -867,4 +872,15 @@
 		dc.nick = uc.nick
 		dc.nickCM = casemapASCII(dc.nick)
+	}
+}
+
+func (dc *downstreamConn) updateRealname() {
+	if uc := dc.upstream(); uc != nil && uc.realname != dc.realname && dc.caps["setname"] {
+		dc.SendMessage(&irc.Message{
+			Prefix:  dc.prefix(),
+			Command: "SETNAME",
+			Params:  []string{uc.realname},
+		})
+		dc.realname = uc.realname
 	}
 }
@@ -1063,4 +1079,5 @@
 
 	dc.updateNick()
+	dc.updateRealname()
 	dc.updateSupportedCaps()
 
@@ -1343,4 +1360,56 @@
 			dc.nick = nick
 			dc.nickCM = casemapASCII(dc.nick)
+		}
+	case "SETNAME":
+		var realname string
+		if err := parseMessageParams(msg, &realname); err != nil {
+			return err
+		}
+
+		var storeErr error
+		var needUpdate []Network
+		dc.forEachNetwork(func(n *network) {
+			// We only need to call updateNetwork for upstreams that don't
+			// support setname
+			if uc := n.conn; uc != nil && uc.caps["setname"] {
+				uc.SendMessageLabeled(dc.id, &irc.Message{
+					Command: "SETNAME",
+					Params:  []string{realname},
+				})
+
+				n.Realname = realname
+				if err := dc.srv.db.StoreNetwork(dc.user.ID, &n.Network); err != nil {
+					dc.logger.Printf("failed to store network realname: %v", err)
+					storeErr = err
+				}
+				return
+			}
+
+			record := n.Network // copy network record because we'll mutate it
+			record.Realname = realname
+			needUpdate = append(needUpdate, record)
+		})
+
+		// Walk the network list as a second step, because updateNetwork
+		// mutates the original list
+		for _, record := range needUpdate {
+			if _, err := dc.user.updateNetwork(&record); err != nil {
+				dc.logger.Printf("failed to update network realname: %v", err)
+				storeErr = err
+			}
+		}
+		if storeErr != nil {
+			return ircError{&irc.Message{
+				Command: "FAIL",
+				Params:  []string{"SETNAME", "CANNOT_CHANGE_REALNAME", "Failed to update realname"},
+			}}
+		}
+
+		if dc.upstream() == nil && dc.caps["setname"] {
+			dc.SendMessage(&irc.Message{
+				Prefix:  dc.prefix(),
+				Command: "SETNAME",
+				Params:  []string{realname},
+			})
 		}
 	case "JOIN":
Index: /trunk/upstream.go
===================================================================
--- /trunk/upstream.go	(revision 539)
+++ /trunk/upstream.go	(revision 540)
@@ -30,4 +30,5 @@
 	"multi-prefix":     true,
 	"server-time":      true,
+	"setname":          true,
 }
 
@@ -733,4 +734,28 @@
 			})
 		}
+	case "SETNAME":
+		if msg.Prefix == nil {
+			return fmt.Errorf("expected a prefix")
+		}
+
+		var newRealname string
+		if err := parseMessageParams(msg, &newRealname); err != nil {
+			return err
+		}
+
+		// TODO: consider appending this message to logs
+
+		if uc.isOurNick(msg.Prefix.Name) {
+			uc.logger.Printf("changed realname from %q to %q", uc.realname, newRealname)
+			uc.realname = newRealname
+
+			uc.forEachDownstream(func(dc *downstreamConn) {
+				dc.updateRealname()
+			})
+		} else {
+			uc.forEachDownstream(func(dc *downstreamConn) {
+				dc.SendMessage(dc.marshalMessage(msg, uc.network))
+			})
+		}
 	case "JOIN":
 		if msg.Prefix == nil {
Index: /trunk/user.go
===================================================================
--- /trunk/user.go	(revision 539)
+++ /trunk/user.go	(revision 540)
@@ -530,4 +530,5 @@
 
 				dc.updateNick()
+				dc.updateRealname()
 			})
 			uc.network.lastError = nil
