Index: trunk/downstream.go
===================================================================
--- trunk/downstream.go	(revision 680)
+++ trunk/downstream.go	(revision 681)
@@ -1838,46 +1838,32 @@
 		// TODO: support ELIST when supported by all upstreams
 
-		pl := pendingLIST{
-			downstreamID:    dc.id,
-			pendingCommands: make(map[int64]*irc.Message),
-		}
-		var upstream *upstreamConn
-		var upstreamChannels map[int64][]string
-		if len(msg.Params) > 0 {
-			uc, upstreamMask, err := dc.unmarshalEntity(msg.Params[0])
-			if err == nil && upstreamMask == "*" { // LIST */network: send LIST only to one network
-				upstream = uc
-			} else {
-				upstreamChannels = make(map[int64][]string)
-				channels := strings.Split(msg.Params[0], ",")
-				for _, channel := range channels {
-					uc, upstreamChannel, err := dc.unmarshalEntity(channel)
-					if err != nil {
-						return err
-					}
-					upstreamChannels[uc.network.ID] = append(upstreamChannels[uc.network.ID], upstreamChannel)
-				}
-			}
-		}
-
-		dc.user.pendingLISTs = append(dc.user.pendingLISTs, pl)
-		dc.forEachUpstream(func(uc *upstreamConn) {
-			if upstream != nil && upstream != uc {
-				return
-			}
-			var params []string
-			if upstreamChannels != nil {
-				if channels, ok := upstreamChannels[uc.network.ID]; ok {
-					params = []string{strings.Join(channels, ",")}
-				} else {
-					return
-				}
-			}
-			pl.pendingCommands[uc.network.ID] = &irc.Message{
-				Command: "LIST",
-				Params:  params,
-			}
-			uc.trySendLIST(dc.id)
-		})
+		network := dc.network
+		if network == nil && len(msg.Params) > 0 {
+			var err error
+			network, msg.Params[0], err = dc.unmarshalEntityNetwork(msg.Params[0])
+			if err != nil {
+				return err
+			}
+		}
+		if network == nil {
+			dc.SendMessage(&irc.Message{
+				Prefix:  dc.srv.prefix(),
+				Command: irc.RPL_LISTEND,
+				Params:  []string{dc.nick, "LIST without a network suffix is not supported in multi-upstream mode"},
+			})
+			return nil
+		}
+
+		uc := network.conn
+		if uc == nil {
+			dc.SendMessage(&irc.Message{
+				Prefix:  dc.srv.prefix(),
+				Command: irc.RPL_LISTEND,
+				Params:  []string{dc.nick, "Disconnected from upstream server"},
+			})
+			return nil
+		}
+
+		uc.enqueueLIST(dc, msg)
 	case "NAMES":
 		if len(msg.Params) == 0 {
Index: trunk/upstream.go
===================================================================
--- trunk/upstream.go	(revision 680)
+++ trunk/upstream.go	(revision 681)
@@ -73,4 +73,9 @@
 }
 
+type pendingUpstreamCommand struct {
+	downstreamID uint64
+	cmd          *irc.Message
+}
+
 type upstreamConn struct {
 	conn
@@ -105,6 +110,8 @@
 	casemapIsSet bool
 
-	// set of LIST commands in progress, per downstream
-	pendingLISTDownstreamSet map[uint64]struct{}
+	// Queue of LIST commands in progress. The first entry has been sent to the
+	// server and is awaiting reply. The following entries have not been sent
+	// yet.
+	pendingLIST []pendingUpstreamCommand
 
 	gotMotd bool
@@ -191,16 +198,15 @@
 
 	uc := &upstreamConn{
-		conn:                     *newConn(network.user.srv, newNetIRCConn(netConn), &options),
-		network:                  network,
-		user:                     network.user,
-		channels:                 upstreamChannelCasemapMap{newCasemapMap(0)},
-		supportedCaps:            make(map[string]string),
-		caps:                     make(map[string]bool),
-		batches:                  make(map[string]batch),
-		availableChannelTypes:    stdChannelTypes,
-		availableChannelModes:    stdChannelModes,
-		availableMemberships:     stdMemberships,
-		isupport:                 make(map[string]*string),
-		pendingLISTDownstreamSet: make(map[uint64]struct{}),
+		conn:                  *newConn(network.user.srv, newNetIRCConn(netConn), &options),
+		network:               network,
+		user:                  network.user,
+		channels:              upstreamChannelCasemapMap{newCasemapMap(0)},
+		supportedCaps:         make(map[string]string),
+		caps:                  make(map[string]bool),
+		batches:               make(map[string]batch),
+		availableChannelTypes: stdChannelTypes,
+		availableChannelModes: stdChannelModes,
+		availableMemberships:  stdMemberships,
+		isupport:              make(map[string]*string),
 	}
 	return uc, nil
@@ -236,71 +242,61 @@
 }
 
-func (uc *upstreamConn) getPendingLIST() *pendingLIST {
-	for _, pl := range uc.user.pendingLISTs {
-		if _, ok := pl.pendingCommands[uc.network.ID]; !ok {
-			continue
-		}
-		return &pl
-	}
-	return nil
-}
-
-func (uc *upstreamConn) endPendingLISTs(all bool) (found bool) {
-	found = false
-	for i := 0; i < len(uc.user.pendingLISTs); i++ {
-		pl := uc.user.pendingLISTs[i]
-		if _, ok := pl.pendingCommands[uc.network.ID]; !ok {
-			continue
-		}
-		delete(pl.pendingCommands, uc.network.ID)
-		if len(pl.pendingCommands) == 0 {
-			uc.user.pendingLISTs = append(uc.user.pendingLISTs[:i], uc.user.pendingLISTs[i+1:]...)
-			i--
-			uc.forEachDownstreamByID(pl.downstreamID, func(dc *downstreamConn) {
-				dc.SendMessage(&irc.Message{
-					Prefix:  dc.srv.prefix(),
-					Command: irc.RPL_LISTEND,
-					Params:  []string{dc.nick, "End of /LIST"},
-				})
-			})
-		}
-		found = true
-		if !all {
-			delete(uc.pendingLISTDownstreamSet, pl.downstreamID)
-			uc.user.forEachUpstream(func(uc *upstreamConn) {
-				uc.trySendLIST(pl.downstreamID)
-			})
-			return
-		}
-	}
-	return
-}
-
-func (uc *upstreamConn) trySendLIST(downstreamID uint64) {
-	if _, ok := uc.pendingLISTDownstreamSet[downstreamID]; ok {
-		// a LIST command is already pending
-		// we will try again when that command is completed
+func (uc *upstreamConn) endPendingLISTs() {
+	for _, pendingCmd := range uc.pendingLIST {
+		uc.forEachDownstreamByID(pendingCmd.downstreamID, func(dc *downstreamConn) {
+			dc.SendMessage(&irc.Message{
+				Prefix:  dc.srv.prefix(),
+				Command: irc.RPL_LISTEND,
+				Params:  []string{dc.nick, "End of /LIST"},
+			})
+		})
+	}
+	uc.pendingLIST = nil
+}
+
+func (uc *upstreamConn) sendNextPendingLIST() {
+	if len(uc.pendingLIST) == 0 {
 		return
 	}
-
-	for _, pl := range uc.user.pendingLISTs {
-		if pl.downstreamID != downstreamID {
-			continue
-		}
-		// this is the first pending LIST command list of the downstream
-		listCommand, ok := pl.pendingCommands[uc.network.ID]
-		if !ok {
-			// there is no command for this upstream in these LIST commands
-			// do not send anything
-			continue
-		}
-		// there is a command for this upstream in these LIST commands
-		// send it now
-
-		uc.SendMessageLabeled(downstreamID, listCommand)
-
-		uc.pendingLISTDownstreamSet[downstreamID] = struct{}{}
-		return
-	}
+	uc.SendMessage(uc.pendingLIST[0].cmd)
+}
+
+func (uc *upstreamConn) enqueueLIST(dc *downstreamConn, cmd *irc.Message) {
+	uc.pendingLIST = append(uc.pendingLIST, pendingUpstreamCommand{
+		downstreamID: dc.id,
+		cmd:          cmd,
+	})
+
+	if len(uc.pendingLIST) == 1 {
+		uc.sendNextPendingLIST()
+	}
+}
+
+func (uc *upstreamConn) currentPendingLIST() (*downstreamConn, *irc.Message) {
+	if len(uc.pendingLIST) == 0 {
+		return nil, nil
+	}
+
+	pendingCmd := uc.pendingLIST[0]
+	for _, dc := range uc.user.downstreamConns {
+		if dc.id == pendingCmd.downstreamID {
+			return dc, pendingCmd.cmd
+		}
+	}
+
+	return nil, pendingCmd.cmd
+}
+
+func (uc *upstreamConn) dequeueLIST() (*downstreamConn, *irc.Message) {
+	dc, cmd := uc.currentPendingLIST()
+
+	if len(uc.pendingLIST) > 0 {
+		copy(uc.pendingLIST, uc.pendingLIST[1:])
+		uc.pendingLIST = uc.pendingLIST[:len(uc.pendingLIST)-1]
+	}
+
+	uc.sendNextPendingLIST()
+
+	return dc, cmd
 }
 
@@ -1100,21 +1096,29 @@
 		}
 
-		pl := uc.getPendingLIST()
-		if pl == nil {
+		dc, cmd := uc.currentPendingLIST()
+		if cmd == nil {
 			return fmt.Errorf("unexpected RPL_LIST: no matching pending LIST")
-		}
-
-		uc.forEachDownstreamByID(pl.downstreamID, func(dc *downstreamConn) {
-			dc.SendMessage(&irc.Message{
-				Prefix:  dc.srv.prefix(),
-				Command: irc.RPL_LIST,
-				Params:  []string{dc.nick, dc.marshalEntity(uc.network, channel), clients, topic},
-			})
+		} else if dc == nil {
+			return nil
+		}
+
+		dc.SendMessage(&irc.Message{
+			Prefix:  dc.srv.prefix(),
+			Command: irc.RPL_LIST,
+			Params:  []string{dc.nick, dc.marshalEntity(uc.network, channel), clients, topic},
 		})
 	case irc.RPL_LISTEND:
-		ok := uc.endPendingLISTs(false)
-		if !ok {
+		dc, cmd := uc.dequeueLIST()
+		if cmd == nil {
 			return fmt.Errorf("unexpected RPL_LISTEND: no matching pending LIST")
-		}
+		} else if dc == nil {
+			return nil
+		}
+
+		dc.SendMessage(&irc.Message{
+			Prefix:  dc.srv.prefix(),
+			Command: irc.RPL_LISTEND,
+			Params:  []string{dc.nick, "End of /LIST"},
+		})
 	case irc.RPL_NAMREPLY:
 		var name, statusStr, members string
@@ -1434,8 +1438,5 @@
 
 		if command == "LIST" {
-			ok := uc.endPendingLISTs(false)
-			if !ok {
-				return fmt.Errorf("unexpected response for LIST: %q: no matching pending LIST", msg.Command)
-			}
+			uc.endPendingLISTs()
 		}
 
Index: trunk/user.go
===================================================================
--- trunk/user.go	(revision 680)
+++ trunk/user.go	(revision 681)
@@ -406,13 +406,4 @@
 	downstreamConns []*downstreamConn
 	msgStore        messageStore
-
-	// LIST commands in progress
-	pendingLISTs []pendingLIST
-}
-
-type pendingLIST struct {
-	downstreamID uint64
-	// list of per-upstream LIST commands not yet sent or completed
-	pendingCommands map[int64]*irc.Message
 }
 
@@ -691,5 +682,5 @@
 	uc.network.conn = nil
 
-	uc.endPendingLISTs(true)
+	uc.endPendingLISTs()
 
 	for _, entry := range uc.channels.innerMap {
