Index: trunk/upstream.go
===================================================================
--- trunk/upstream.go	(revision 469)
+++ trunk/upstream.go	(revision 478)
@@ -49,5 +49,5 @@
 	modes        channelModes
 	creationTime string
-	Members      map[string]*memberships
+	Members      membershipsCasemapMap
 	complete     bool
 	detachTimer  *time.Timer
@@ -87,8 +87,9 @@
 	registered    bool
 	nick          string
+	nickCM        string
 	username      string
 	realname      string
 	modes         userModes
-	channels      map[string]*upstreamChannel
+	channels      upstreamChannelCasemapMap
 	supportedCaps map[string]string
 	caps          map[string]bool
@@ -99,4 +100,6 @@
 	saslClient  sasl.Client
 	saslStarted bool
+
+	casemapIsSet bool
 
 	// set of LIST commands in progress, per downstream
@@ -187,5 +190,5 @@
 		network:                  network,
 		user:                     network.user,
-		channels:                 make(map[string]*upstreamChannel),
+		channels:                 upstreamChannelCasemapMap{newCasemapMap(0)},
 		supportedCaps:            make(map[string]string),
 		caps:                     make(map[string]bool),
@@ -214,6 +217,6 @@
 
 func (uc *upstreamConn) getChannel(name string) (*upstreamChannel, error) {
-	ch, ok := uc.channels[name]
-	if !ok {
+	ch := uc.channels.Value(name)
+	if ch == nil {
 		return nil, fmt.Errorf("unknown channel %q", name)
 	}
@@ -223,4 +226,8 @@
 func (uc *upstreamConn) isChannel(entity string) bool {
 	return strings.ContainsRune(uc.availableChannelTypes, rune(entity[0]))
+}
+
+func (uc *upstreamConn) isOurNick(nick string) bool {
+	return uc.nickCM == uc.network.casemap(nick)
 }
 
@@ -414,9 +421,10 @@
 		} else { // regular user message
 			target := entity
-			if target == uc.nick {
+			if uc.isOurNick(target) {
 				target = msg.Prefix.Name
 			}
 
-			if ch, ok := uc.network.channels[target]; ok {
+			ch := uc.network.channels.Value(target)
+			if ch != nil {
 				if ch.Detached {
 					uc.handleDetachedMessage(msg.Prefix.Name, text, ch)
@@ -591,7 +599,8 @@
 		uc.logger.Printf("connection registered")
 
-		if len(uc.network.channels) > 0 {
+		if uc.network.channels.Len() > 0 {
 			var channels, keys []string
-			for _, ch := range uc.network.channels {
+			for _, entry := range uc.network.channels.innerMap {
+				ch := entry.value.(*Channel)
 				channels = append(channels, ch.Name)
 				keys = append(keys, ch.Key)
@@ -635,4 +644,12 @@
 			var err error
 			switch parameter {
+			case "CASEMAPPING":
+				casemap, ok := parseCasemappingToken(value)
+				if !ok {
+					casemap = casemapRFC1459
+				}
+				uc.network.updateCasemapping(casemap)
+				uc.nickCM = uc.network.casemap(uc.nick)
+				uc.casemapIsSet = true
 			case "CHANMODES":
 				if !negate {
@@ -672,4 +689,12 @@
 			}
 		})
+	case irc.ERR_NOMOTD, irc.RPL_ENDOFMOTD:
+		if !uc.casemapIsSet {
+			// upstream did not send any CASEMAPPING token, thus
+			// we assume it implements the old RFCs with rfc1459.
+			uc.casemapIsSet = true
+			uc.network.updateCasemapping(casemapRFC1459)
+			uc.nickCM = uc.network.casemap(uc.nick)
+		}
 	case "BATCH":
 		var tag string
@@ -717,14 +742,17 @@
 
 		me := false
-		if msg.Prefix.Name == uc.nick {
+		if uc.isOurNick(msg.Prefix.Name) {
 			uc.logger.Printf("changed nick from %q to %q", uc.nick, newNick)
 			me = true
 			uc.nick = newNick
-		}
-
-		for _, ch := range uc.channels {
-			if memberships, ok := ch.Members[msg.Prefix.Name]; ok {
-				delete(ch.Members, msg.Prefix.Name)
-				ch.Members[newNick] = memberships
+			uc.nickCM = uc.network.casemap(uc.nick)
+		}
+
+		for _, entry := range uc.channels.innerMap {
+			ch := entry.value.(*upstreamChannel)
+			memberships := ch.Members.Value(msg.Prefix.Name)
+			if memberships != nil {
+				ch.Members.Delete(msg.Prefix.Name)
+				ch.Members.SetValue(newNick, memberships)
 				uc.appendLog(ch.Name, msg)
 			}
@@ -751,11 +779,13 @@
 
 		for _, ch := range strings.Split(channels, ",") {
-			if msg.Prefix.Name == uc.nick {
+			if uc.isOurNick(msg.Prefix.Name) {
 				uc.logger.Printf("joined channel %q", ch)
-				uc.channels[ch] = &upstreamChannel{
+				members := membershipsCasemapMap{newCasemapMap(0)}
+				members.casemap = uc.network.casemap
+				uc.channels.SetValue(ch, &upstreamChannel{
 					Name:    ch,
 					conn:    uc,
-					Members: make(map[string]*memberships),
-				}
+					Members: members,
+				})
 				uc.updateChannelAutoDetach(ch)
 
@@ -769,5 +799,5 @@
 					return err
 				}
-				ch.Members[msg.Prefix.Name] = &memberships{}
+				ch.Members.SetValue(msg.Prefix.Name, &memberships{})
 			}
 
@@ -787,8 +817,9 @@
 
 		for _, ch := range strings.Split(channels, ",") {
-			if msg.Prefix.Name == uc.nick {
+			if uc.isOurNick(msg.Prefix.Name) {
 				uc.logger.Printf("parted channel %q", ch)
-				if uch, ok := uc.channels[ch]; ok {
-					delete(uc.channels, ch)
+				uch := uc.channels.Value(ch)
+				if uch != nil {
+					uc.channels.Delete(ch)
 					uch.updateAutoDetach(0)
 				}
@@ -798,5 +829,5 @@
 					return err
 				}
-				delete(ch.Members, msg.Prefix.Name)
+				ch.Members.Delete(msg.Prefix.Name)
 			}
 
@@ -815,7 +846,7 @@
 		}
 
-		if user == uc.nick {
+		if uc.isOurNick(user) {
 			uc.logger.Printf("kicked from channel %q by %s", channel, msg.Prefix.Name)
-			delete(uc.channels, channel)
+			uc.channels.Delete(channel)
 		} else {
 			ch, err := uc.getChannel(channel)
@@ -823,5 +854,5 @@
 				return err
 			}
-			delete(ch.Members, user)
+			ch.Members.Delete(user)
 		}
 
@@ -832,11 +863,12 @@
 		}
 
-		if msg.Prefix.Name == uc.nick {
+		if uc.isOurNick(msg.Prefix.Name) {
 			uc.logger.Printf("quit")
 		}
 
-		for _, ch := range uc.channels {
-			if _, ok := ch.Members[msg.Prefix.Name]; ok {
-				delete(ch.Members, msg.Prefix.Name)
+		for _, entry := range uc.channels.innerMap {
+			ch := entry.value.(*upstreamChannel)
+			if ch.Members.Has(msg.Prefix.Name) {
+				ch.Members.Delete(msg.Prefix.Name)
 
 				uc.appendLog(ch.Name, msg)
@@ -909,5 +941,6 @@
 			uc.appendLog(ch.Name, msg)
 
-			if ch, ok := uc.network.channels[name]; !ok || !ch.Detached {
+			c := uc.network.channels.Value(name)
+			if c == nil || !c.Detached {
 				uc.forEachDownstream(func(dc *downstreamConn) {
 					params := make([]string, len(msg.Params))
@@ -965,5 +998,6 @@
 		}
 		if firstMode {
-			if c, ok := uc.network.channels[channel]; !ok || !c.Detached {
+			c := uc.network.channels.Value(channel)
+			if c == nil || !c.Detached {
 				modeStr, modeParams := ch.modes.Format()
 
@@ -1062,6 +1096,6 @@
 		}
 
-		ch, ok := uc.channels[name]
-		if !ok {
+		ch := uc.channels.Value(name)
+		if ch == nil {
 			// NAMES on a channel we have not joined, forward to downstream
 			uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
@@ -1091,5 +1125,5 @@
 		for _, s := range splitSpace(members) {
 			memberships, nick := uc.parseMembershipPrefix(s)
-			ch.Members[nick] = memberships
+			ch.Members.SetValue(nick, memberships)
 		}
 	case irc.RPL_ENDOFNAMES:
@@ -1099,6 +1133,6 @@
 		}
 
-		ch, ok := uc.channels[name]
-		if !ok {
+		ch := uc.channels.Value(name)
+		if ch == nil {
 			// NAMES on a channel we have not joined, forward to downstream
 			uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
@@ -1119,5 +1153,6 @@
 		ch.complete = true
 
-		if c, ok := uc.network.channels[name]; !ok || !c.Detached {
+		c := uc.network.channels.Value(name)
+		if c == nil || !c.Detached {
 			uc.forEachDownstream(func(dc *downstreamConn) {
 				forwardChannel(dc, ch)
@@ -1273,5 +1308,5 @@
 		}
 
-		weAreInvited := nick == uc.nick
+		weAreInvited := uc.isOurNick(nick)
 
 		uc.forEachDownstream(func(dc *downstreamConn) {
@@ -1396,5 +1431,5 @@
 	case irc.RPL_LUSERCLIENT, irc.RPL_LUSEROP, irc.RPL_LUSERUNKNOWN, irc.RPL_LUSERCHANNELS, irc.RPL_LUSERME:
 		// Ignore
-	case irc.RPL_MOTDSTART, irc.RPL_MOTD, irc.RPL_ENDOFMOTD:
+	case irc.RPL_MOTDSTART, irc.RPL_MOTD:
 		// Ignore
 	case irc.RPL_LISTSTART:
@@ -1602,4 +1637,5 @@
 func (uc *upstreamConn) register() {
 	uc.nick = uc.network.Nick
+	uc.nickCM = uc.network.casemap(uc.nick)
 	uc.username = uc.network.GetUsername()
 	uc.realname = uc.network.GetRealname()
@@ -1706,11 +1742,12 @@
 
 	detached := false
-	if ch, ok := uc.network.channels[entity]; ok {
+	if ch := uc.network.channels.Value(entity); ch != nil {
 		detached = ch.Detached
 	}
 
-	delivered, ok := uc.network.delivered[entity]
-	if !ok {
-		lastID, err := uc.user.msgStore.LastMsgID(uc.network, entity, time.Now())
+	delivered := uc.network.delivered.Value(entity)
+	entityCM := uc.network.casemap(entity)
+	if delivered == nil {
+		lastID, err := uc.user.msgStore.LastMsgID(uc.network, entityCM, time.Now())
 		if err != nil {
 			uc.logger.Printf("failed to log message: failed to get last message ID: %v", err)
@@ -1719,5 +1756,5 @@
 
 		delivered = make(map[string]string)
-		uc.network.delivered[entity] = delivered
+		uc.network.delivered.SetValue(entity, delivered)
 
 		for clientName, _ := range uc.network.offlineClients {
@@ -1734,5 +1771,5 @@
 	}
 
-	msgID, err := uc.user.msgStore.Append(uc.network, entity, msg)
+	msgID, err := uc.user.msgStore.Append(uc.network, entityCM, msg)
 	if err != nil {
 		uc.logger.Printf("failed to log message: %v", err)
@@ -1755,5 +1792,6 @@
 
 	// Don't forward messages if it's a detached channel
-	if ch, ok := uc.network.channels[target]; ok && ch.Detached {
+	ch := uc.network.channels.Value(target)
+	if ch != nil && ch.Detached {
 		return
 	}
@@ -1790,8 +1828,12 @@
 
 func (uc *upstreamConn) updateChannelAutoDetach(name string) {
-	if uch, ok := uc.channels[name]; ok {
-		if ch, ok := uc.network.channels[name]; ok && !ch.Detached {
-			uch.updateAutoDetach(ch.DetachAfter)
-		}
-	}
-}
+	uch := uc.channels.Value(name)
+	if uch == nil {
+		return
+	}
+	ch := uc.network.channels.Value(name)
+	if ch == nil || ch.Detached {
+		return
+	}
+	uch.updateAutoDetach(ch.DetachAfter)
+}
