diff --git a/exec/ircd.js b/exec/ircd.js
index 6b7c878632283546a82e1147c971628e3debc0df..57123e0e3c0db363fb7d354fdbbe3bb0a08634ed 100644
--- a/exec/ircd.js
+++ b/exec/ircd.js
@@ -148,17 +148,6 @@ var CHANMODE_SECRET		=(1<<9); // s
 var CHANMODE_TOPIC		=(1<<10); // t
 var CHANMODE_VOICE		=(1<<11); // v
 
-// Channel lists.  Inverses of a mode MUST always be 1 'away' from each other
-// in the reverse direction.  For example, -o is +1 from +o, and +o is -1
-// from -o.
-const CHANLIST_NONE		=0; // null
-const CHANLIST_OP		=1; // +o
-const CHANLIST_DEOP		=2; // -o
-const CHANLIST_VOICE		=3; // +v
-const CHANLIST_DEVOICE		=4; // -v
-const CHANLIST_BAN		=5; // +b
-const CHANLIST_UNBAN		=6; // -b
-
 // These are used in the mode crunching section to figure out what character
 // to display in the crunched MODE line.
 function Mode (modechar,args,state,list) {
@@ -169,22 +158,17 @@ function Mode (modechar,args,state,list) {
 }
 
 MODE = new Array();
-MODE[CHANMODE_BAN] 		= new Mode("b",true,false,CHANLIST_BAN);
-MODE[CHANMODE_INVITE] 		= new Mode("i",false,true,0);
-MODE[CHANMODE_KEY] 		= new Mode("k",true,true,0);
-MODE[CHANMODE_LIMIT] 		= new Mode("l",true,true,0);
-MODE[CHANMODE_MODERATED] 	= new Mode("m",false,true,0);
-MODE[CHANMODE_NOOUTSIDE] 	= new Mode("n",false,true,0);
-MODE[CHANMODE_OP] 		= new Mode("o",true,false,CHANLIST_OP);
-MODE[CHANMODE_PRIVATE] 		= new Mode("p",false,true,0);
-MODE[CHANMODE_SECRET] 		= new Mode("s",false,true,0);
-MODE[CHANMODE_TOPIC] 		= new Mode("t",false,true,0);
-MODE[CHANMODE_VOICE]		= new Mode("v",true,false,CHANLIST_VOICE);
-
-MODECHAR = new Array();
-MODECHAR[CHANLIST_BAN]		= "b";
-MODECHAR[CHANLIST_OP]		= "o";
-MODECHAR[CHANLIST_VOICE]	= "v";
+MODE[CHANMODE_BAN] 		= new Mode("b",true,false,true);
+MODE[CHANMODE_INVITE] 		= new Mode("i",false,true,false);
+MODE[CHANMODE_KEY] 		= new Mode("k",true,true,false);
+MODE[CHANMODE_LIMIT] 		= new Mode("l",true,true,false);
+MODE[CHANMODE_MODERATED] 	= new Mode("m",false,true,false);
+MODE[CHANMODE_NOOUTSIDE] 	= new Mode("n",false,true,false);
+MODE[CHANMODE_OP] 		= new Mode("o",true,false,true);
+MODE[CHANMODE_PRIVATE] 		= new Mode("p",false,true,false);
+MODE[CHANMODE_SECRET] 		= new Mode("s",false,true,false);
+MODE[CHANMODE_TOPIC] 		= new Mode("t",false,true,false);
+MODE[CHANMODE_VOICE]		= new Mode("v",true,false,true);
 
 // Connection Types
 const TYPE_EMPTY		=0;
@@ -660,63 +644,46 @@ function search_nickbuf(bufnick) {
 	return 0;
 }
 
-function IRCClient_tweaktmpmode(tmp_bit,chan) {
-	if ((!chan.ismode(this.id,CHANLIST_OP)) && (!this.server) && (!this.parent)) {
-		this.numeric482(chan.nam);
+function ChanMode_tweaktmpmode(tmp_bit,add) {
+	if ((!this.chan.ismode(this.user.id,CHANMODE_OP)) &&
+	    (!this.user.server) && (!this.user.parent)) {
+		this.user.numeric482(this.chan.nam);
 		return 0;
 	}
 	if (add) {
-		addbits|=tmp_bit;
-		delbits&=~tmp_bit;
+		this.addbits|=tmp_bit;
+		this.delbits&=~tmp_bit;
 	} else {
-		addbits&=~tmp_bit;
-		delbits|=tmp_bit;
+		this.addbits&=~tmp_bit;
+		this.delbits|=tmp_bit;
 	}
 }
 
-function IRCClient_tweaktmpmodelist(tmp_cl,tmp_ncl,chan) {
-	if ((!chan.ismode(this.id,CHANLIST_OP)) &&
-	    (!this.server) && (!this.parent)) {
-		this.numeric482(chan.nam);
+function ChanMode_tweaktmpmodelist(tmp_bit,add,arg) {
+	if ((!this.chan.ismode(this.user.id,CHANMODE_OP)) &&
+	    (!this.user.server) && (!this.user.parent)) {
+		this.user.numeric482(this.chan.nam);
 		return 0;
 	}
-	if (add) {
-		tmp_match = false;
-		for (lstitem in chan_tmplist[tmp_cl]) {
-			if (chan_tmplist[tmp_cl][lstitem] &&
-			    (chan_tmplist[tmp_cl][lstitem].toUpperCase() ==
-			     cm_args[mode_args_counter].toUpperCase())
-			   )
-				tmp_match = true;
-		}
-		if(!tmp_match) {
-			chan_tmplist[tmp_cl][chan_tmplist[tmp_cl].length] = cm_args[mode_args_counter];
-			for (lstitem in chan_tmplist[tmp_ncl]) {
-				if (chan_tmplist[tmp_ncl][lstitem] &&
-				    (chan_tmplist[tmp_ncl][lstitem].toUpperCase() == 
-				     cm_args[mode_args_counter].toUpperCase())
-				   )
-					chan_tmplist[tmp_ncl][lstitem] = "";
-			}
-		}
-	} else {
-		tmp_match = false;
-		for (lstitem in chan_tmplist[tmp_ncl]) {
-			if (chan_tmplist[tmp_ncl][lstitem] &&
-			    (chan_tmplist[tmp_ncl][lstitem].toUpperCase() ==
-			     cm_args[mode_args_counter].toUpperCase())
-			   )
-				tmp_match = true;
-		}
-		if(!tmp_match) {
-			chan_tmplist[tmp_ncl][chan_tmplist[tmp_ncl].length] = cm_args[mode_args_counter];
-			for (lstitem in chan_tmplist[tmp_cl]) {
-				if (chan_tmplist[tmp_cl][lstitem] &&
-				    (chan_tmplist[tmp_cl][lstitem].toUpperCase() ==
-				     cm_args[mode_args_counter].toUpperCase())
-				   )
-					chan_tmplist[tmp_cl][lstitem] = "";
-			}
+	for (lstitem in this.tmplist[tmp_bit][add]) {
+		// Is this argument in our list for this mode already?
+		if (this.tmplist[tmp_bit][add][lstitem].toUpperCase() ==
+		     arg.toUpperCase())
+			return 0;
+	}
+	// It doesn't exist on our mode, push it in.
+	this.tmplist[tmp_bit][add].push(arg);
+	// Check for it against the other mode, and maybe nuke it.
+	var oadd;
+	if (add)
+		oadd = false;
+	else
+		oadd = true;
+	for (x in this.tmplist[tmp_bit][oadd]) {
+		if (this.tmplist[tmp_bit][oadd][x].toUpperCase() ==
+		    arg.toUpperCase()) {
+			delete this.tmplist[tmp_bit][oadd][x];
+			return 0;
 		}
 	}
 }
@@ -1158,9 +1125,6 @@ function IRCClient(socket,new_id,local_client,do_newconn) {
 	this.server=false;
 	this.hub=false;
 	this.servername = servername;
-	this.affect_mode_list=IRCClient_affect_mode_list;
-	this.tweaktmpmode=IRCClient_tweaktmpmode;
-	this.tweaktmpmodelist=IRCClient_tweaktmpmodelist;
 	this.rmchan=IRCClient_RMChan;
 	this.rawout=IRCClient_rawout;
 	this.originatorout=IRCClient_originatorout;
@@ -1424,7 +1388,7 @@ function IRCClient_server_info(sni_server) {
 }
 
 function IRCClient_server_nick_info(sni_client) {
-	this.rawout("NICK " + sni_client.nick + " " + sni_client.hops + " " + sni_client.created + " " + sni_client.get_usermode() + " " + sni_client.uprefix + " " + sni_client.hostname + " " + sni_client.servername + " 0 " + ip_to_int(sni_client.ip) + " :" + sni_client.realname);
+	this.rawout("NICK " + sni_client.nick + " " + sni_client.hops + " " + sni_client.created + " " + sni_client.get_usermode(true) + " " + sni_client.uprefix + " " + sni_client.hostname + " " + sni_client.servername + " 0 " + ip_to_int(sni_client.ip) + " :" + sni_client.realname);
 }
 
 function IRCClient_reintroduce_nick(nick) {
@@ -1443,9 +1407,9 @@ function IRCClient_reintroduce_nick(nick) {
 		cmodes = "";
 		if (nick.channels[uchan]) {
 			chan = Channels[nick.channels[uchan]];
-			if (chan.ismode(nick.id,CHANLIST_OP))
+			if (chan.ismode(nick.id,CHANMODE_OP))
 				cmodes += "@";
-			if (chan.ismode(nick.id,CHANLIST_VOICE))
+			if (chan.ismode(nick.id,CHANMODE_VOICE))
 				cmodes += "+";
 			this.rawout("SJOIN " + chan.created + " " + chan.nam + " " + chan.chanmode(true) + " :" + cmodes + nick.nick);
 			if (chan.topic)
@@ -1459,12 +1423,12 @@ function IRCClient_server_chan_info(sni_chan) {
 	var modecounter=0;
 	var modestr="+";
 	var modeargs="";
-	for (aBan in sni_chan.modelist[CHANLIST_BAN]) {
+	for (aBan in sni_chan.modelist[CHANMODE_BAN]) {
 		modecounter++;
 		modestr += "b";
 		if (modeargs)
 			modeargs += " ";
-		modeargs += sni_chan.modelist[CHANLIST_BAN][aBan];
+		modeargs += sni_chan.modelist[CHANMODE_BAN][aBan];
 		if (modecounter >= max_modes) {
 			this.ircout("MODE " + sni_chan.nam + " " + modestr + " " + modeargs);
 			modecounter=0;
@@ -1487,8 +1451,8 @@ function IRCClient_RMChan(rmchan_obj) {
 		if (this.channels[j] == rmchan_obj.nam.toUpperCase())
 			delete this.channels[j];
 	}
-	rmchan_obj.del_modelist(this.id,CHANLIST_OP);
-	rmchan_obj.del_modelist(this.id,CHANLIST_VOICE);
+	rmchan_obj.del_modelist(this.id,CHANMODE_OP);
+	rmchan_obj.del_modelist(this.id,CHANMODE_VOICE);
 	if (!rmchan_obj.count_users()) {
 		delete rmchan_obj.users;
 		delete rmchan_obj.mode;
@@ -1637,9 +1601,9 @@ function IRCClient_numeric352(user,show_ips_only,chan) {
 	else
 		who_mode += "H";
 	if (chan) {
-		if (chan.ismode(user.id,CHANLIST_OP))
+		if (chan.ismode(user.id,CHANMODE_OP))
 			who_mode += "@";
-		else if (chan.ismode(user.id,CHANLIST_VOICE))
+		else if (chan.ismode(user.id,CHANMODE_VOICE))
 			who_mode += "+";
 	}
 	if (user.mode&USERMODE_OPER)
@@ -1772,9 +1736,9 @@ function IRCClient_names(chan) {
 		    (this.onchannel(chan)) ) ) {
 			if (numnicks)
 				tmp += " ";
-			if (Channels[chan].ismode(Channel_user.id,CHANLIST_OP))
+			if (Channels[chan].ismode(Channel_user.id,CHANMODE_OP))
 				tmp += "@";
-			else if (Channels[chan].ismode(Channel_user.id,CHANLIST_VOICE))
+			else if (Channels[chan].ismode(Channel_user.id,CHANMODE_VOICE))
 				tmp += "+";
 			tmp += Channel_user.nick;
 			numnicks++;
@@ -1978,9 +1942,9 @@ function IRCClient_do_whois(wi) {
 		     this.mode&USERMODE_OPER)) {
 			if (userchans)
 				userchans += " ";
-			if (Channels[wi.channels[i]].ismode(wi.id,CHANLIST_OP))
+			if (Channels[wi.channels[i]].ismode(wi.id,CHANMODE_OP))
 				userchans += "@";
-			else if (Channels[wi.channels[i]].ismode(wi.id,CHANLIST_VOICE))
+			else if (Channels[wi.channels[i]].ismode(wi.id,CHANMODE_VOICE))
 				userchans += "+";
 			userchans += Channels[wi.channels[i]].nam;
 		}
@@ -2071,10 +2035,10 @@ function IRCClient_do_msg(target,type_str,send_str) {
 
 	send_to_list = -1;
 	if (target[0] == "@" && ( (target[1] == "#") || target[1] == "&") ) {
-		send_to_list = CHANLIST_OP;
+		send_to_list = CHANMODE_OP;
 		target = target.slice(1);
 	} else if (target[0]=="+" && ((target[1] == "#")|| target[1] == "&")) {
-		send_to_list = CHANLIST_VOICE;
+		send_to_list = CHANMODE_VOICE;
 		target = target.slice(1);
 	}
 		
@@ -2095,14 +2059,14 @@ function IRCClient_do_msg(target,type_str,send_str) {
 			return 0;
 		}
 		if ( (chan.mode&CHANMODE_MODERATED) &&
-		     !chan.ismode(this.id,CHANLIST_VOICE) &&
-		     !chan.ismode(this.id,CHANLIST_OP) ) {
+		     !chan.ismode(this.id,CHANMODE_VOICE) &&
+		     !chan.ismode(this.id,CHANMODE_OP) ) {
 			this.numeric(404, chan.nam + " :Cannot send to channel (+m: moderated)");
 			return 0;
 		}
 		if (chan.isbanned(this.ircnuh) &&
-		   !chan.ismode(this.id,CHANLIST_VOICE) &&
-		   !chan.ismode(this.id,CHANLIST_OP) ) {
+		   !chan.ismode(this.id,CHANMODE_VOICE) &&
+		   !chan.ismode(this.id,CHANMODE_OP) ) {
 			this.numeric(404, chan.nam + " :Cannot send to channel (+b: you're banned!)");
 			return 0;
 		}
@@ -2111,9 +2075,9 @@ function IRCClient_do_msg(target,type_str,send_str) {
 			this.bcast_to_channel(chan.nam, str, false);
 			this.bcast_to_channel_servers(chan.nam, str);
 		} else {
-			if (send_to_list == CHANLIST_OP)
+			if (send_to_list == CHANMODE_OP)
 				prefix_chr="@";
-			else if (send_to_list == CHANLIST_VOICE)
+			else if (send_to_list == CHANMODE_VOICE)
 				prefix_chr="+";
 			str = type_str +" " + prefix_chr + chan.nam + " :"+ send_str;
 			this.bcast_to_list(chan, str, false, send_to_list);
@@ -2655,22 +2619,22 @@ function IRCClient_do_complex_who(cmd) {
 					continue;
 				if (sf_op && Channels[who.Channel.toUpperCase()]&&
 				    !Channels[who.Channel.toUpperCase()].ismode(
-				     wc.id,CHANLIST_OP))
+				     wc.id,CHANMODE_OP))
 					continue;
 				if(sf_voice&&Channels[who.Channel.toUpperCase()]&&
 				    !Channels[who.Channel.toUpperCase()].ismode(
-				     wc.id,CHANLIST_VOICE))
+				     wc.id,CHANMODE_VIOCE))
 					continue;
 			} else if (who.del_flags&WHO_CHANNEL) {	
 				if (wc.onchannel(who.Channel.toUpperCase()))
 					continue;
 				if (sf_op && Channels[who.Channel.toUpperCase()]&&
 				    Channels[who.Channel.toUpperCase()].ismode(
-				     wc.id,CHANLIST_OP))
+				     wc.id,CHANMODE_OP))
 					continue;
 				if(sf_voice&&Channels[who.Channel.toUpperCase()]&&
 				    Channels[who.Channel.toUpperCase()].ismode(
-				     wc.id,CHANLIST_VOICE))
+				     wc.id,CHANMODE_VOICE))
 					continue;
 			}
 			if ((who.add_flags&WHO_REALNAME) &&
@@ -3308,11 +3272,11 @@ function IRCClient_do_join(chan_name,join_key) {
 		Channels[chan].created=time();
 		Channels[chan].users = new Array();
 		Channels[chan].users[0] = this.id;
-		Channels[chan].modelist[CHANLIST_BAN] = new Array();
-		Channels[chan].modelist[CHANLIST_VOICE] = new Array();
-		Channels[chan].modelist[CHANLIST_OP] = new Array();
-		Channels[chan].modelist[CHANLIST_OP].push(this.id);
-		str="JOIN :" + chan_name;
+		Channels[chan].modelist[CHANMODE_BAN] = new Array();
+		Channels[chan].modelist[CHANMODE_VOICE] = new Array();
+		Channels[chan].modelist[CHANMODE_OP] = new Array();
+		Channels[chan].modelist[CHANMODE_OP].push(this.id);
+		var str="JOIN :" + chan_name;
 		this.originatorout(str,this);
 		if (chan_name[0] != "&")
 			server_bcast_to_servers(":" + servername + " SJOIN " + Channels[chan].created + " " + Channels[chan].nam + " " + Channels[chan].chanmode() + " :@" + this.nick);
@@ -3363,117 +3327,141 @@ function IRCClient_part_all() {
 	}
 }
 
-function IRCClient_get_usermode() {
+function IRCClient_get_usermode(bcast_modes) {
 	var tmp_mode = "+";
-
-	if (this.mode&USERMODE_INVISIBLE)
-		tmp_mode += "i";
-	if (this.mode&USERMODE_OPER)
-		tmp_mode += "o";
-
+	for (ch in USERMODE_CHAR) {
+		if ((!bcast_modes || (bcast_modes && USERMODE_BCAST[ch])) &&
+		    this.mode&USERMODE_CHAR[ch])
+			tmp_mode += ch;
+	}
 	return tmp_mode;
 }
 
+// Yay, version 3.0 of this.set_chanmode(), eradicates any global variables.
+function ChanMode(chan,user) {
+	this.tmplist = new Array();
+	this.tmplist[CHANMODE_OP] = new Array();
+	this.tmplist[CHANMODE_OP][false] = new Array(); //deop
+	this.tmplist[CHANMODE_OP][true] = new Array(); //op
+	this.tmplist[CHANMODE_VOICE] = new Array();
+	this.tmplist[CHANMODE_VOICE][false] = new Array(); //devoice
+	this.tmplist[CHANMODE_VOICE][true] = new Array(); //voice
+	this.tmplist[CHANMODE_BAN] = new Array();
+	this.tmplist[CHANMODE_BAN][false] = new Array(); //unban
+	this.tmplist[CHANMODE_BAN][true] = new Array(); //ban
+	this.state_arg = new Array();
+	this.state_arg[CHANMODE_KEY] = "";
+	this.state_arg[CHANMODE_LIMIT] = "";
+	this.addbits = 0;
+	this.delbits = 0;
+	this.addmodes = "";
+	this.addmodeargs = "";
+	this.delmodes = "";
+	this.delmodeargs = "";
+	this.chan = chan;
+	this.user = user;
+	// Functions.
+	this.tweaktmpmodelist = ChanMode_tweaktmpmodelist;
+	this.tweaktmpmode = ChanMode_tweaktmpmode;
+	this.affect_mode_list = ChanMode_affect_mode_list;
+}
+
 function IRCClient_set_chanmode(chan,modeline,bounce_modes) {
 	if (!chan || !modeline)
 		return;
 
-	cm_args = modeline.split(' ');
+	var cmode = new ChanMode(chan,this);
 
-	add=true;
-	addbits=CHANMODE_NONE;
-	delbits=CHANMODE_NONE;
+	var cm_args = modeline.split(' ');
 
-	state_arg = new Array();
-	state_arg[CHANMODE_KEY] = "";
-	state_arg[CHANMODE_LIMIT] = "";
+	var add=true;
 
-	chan_tmplist=new Array();
-	chan_tmplist[CHANLIST_OP]=new Array();
-	chan_tmplist[CHANLIST_DEOP]=new Array();
-	chan_tmplist[CHANLIST_VOICE]=new Array();
-	chan_tmplist[CHANLIST_DEVOICE]=new Array();
-	chan_tmplist[CHANLIST_BAN]=new Array();
-	chan_tmplist[CHANLIST_UNBAN]=new Array();
-	mode_counter=0;
-	mode_args_counter=1; // start counting at the args, not the modestring
+	var mode_counter=0;
+	var mode_args_counter=1; // start counting at args, not the modestring
 
 	for (modechar in cm_args[0]) {
 		mode_counter++;
 		switch (cm_args[0][modechar]) {
 			case "+":
-				add=true;
+				if (!add)
+					add=true;
 				mode_counter--;
 				break;
 			case "-":
-				add=false;
+				if (add)
+					add=false;
 				mode_counter--;
 				break;
 			case "b":
-				if(add && (cm_args.length <= mode_args_counter)) {
-					addbits|=CHANMODE_BAN; // list bans
+				if(add && (cm_args.length<=mode_args_counter)) {
+					cmode.addbits|=CHANMODE_BAN;//list bans
 					break;
 				}
-				this.tweaktmpmodelist(CHANLIST_BAN,CHANLIST_UNBAN,chan);
+				cmode.tweaktmpmodelist(CHANMODE_BAN,add,
+					cm_args[mode_args_counter]);
 				mode_args_counter++;
 				break;
 			case "i":
-				this.tweaktmpmode(CHANMODE_INVITE,chan);
+				cmode.tweaktmpmode(CHANMODE_INVITE,add);
 				break;
 			case "k":
 				if(cm_args.length > mode_args_counter) {
-					this.tweaktmpmode(CHANMODE_KEY,chan);
-					state_arg[CHANMODE_KEY]=cm_args[mode_args_counter];
+					cmode.tweaktmpmode(CHANMODE_KEY,add);
+					cmode.state_arg[CHANMODE_KEY]=cm_args[mode_args_counter];
 					mode_args_counter++;
 				}
 				break;
 			case "l":
 				if (add && (cm_args.length > mode_args_counter)) {
-					this.tweaktmpmode(CHANMODE_LIMIT,chan);
-					regexp = "^[0-9]{1,4}$";
+					cmode.tweaktmpmode(CHANMODE_LIMIT,true);
+					var regexp = "^[0-9]{1,5}$";
 					if(cm_args[mode_args_counter].match(regexp))
-						state_arg[CHANMODE_LIMIT]=cm_args[mode_args_counter];
+						cmode.state_arg[CHANMODE_LIMIT]=cm_args[mode_args_counter];
 					mode_args_counter++;
 				} else if (!add) {
-					this.tweaktmpmode(CHANMODE_LIMIT,chan);
+					cmode.tweaktmpmode(CHANMODE_LIMIT,false);
 					if (cm_args.length > mode_args_counter)
 						mode_args_counter++;
 				}
 				break;
 			case "m":
-				this.tweaktmpmode(CHANMODE_MODERATED,chan);
+				cmode.tweaktmpmode(CHANMODE_MODERATED,add);
 				break;
 			case "n":
-				this.tweaktmpmode(CHANMODE_NOOUTSIDE,chan);
+				cmode.tweaktmpmode(CHANMODE_NOOUTSIDE,add);
 				break;
 			case "o":
 				if (cm_args.length <= mode_args_counter)
 					break;
-				this.tweaktmpmodelist(CHANLIST_OP,CHANLIST_DEOP,chan);
+				cmode.tweaktmpmodelist(CHANMODE_OP,add,
+					cm_args[mode_args_counter]);
 				mode_args_counter++;
 				break;
 			case "p":
 				if( (add && !(chan.mode&CHANMODE_SECRET) ||
-				     (delbits&CHANMODE_SECRET) ) || (!add) )
-					this.tweaktmpmode(CHANMODE_PRIVATE,chan);
+				     (cmode.delbits&CHANMODE_SECRET) ) ||
+				    (!add) )
+					cmode.tweaktmpmode(CHANMODE_PRIVATE,add);
 				break;
 			case "s":
 				if( (add && !(chan.mode&CHANMODE_PRIVATE) ||
-				     (delbits&CHANMODE_PRIVATE) ) || (!add) )
-					this.tweaktmpmode(CHANMODE_SECRET,chan);
+				     (cmode.delbits&CHANMODE_PRIVATE) ) ||
+				    (!add) )
+					cmode.tweaktmpmode(CHANMODE_SECRET,add);
 				break;
 			case "t":
-				this.tweaktmpmode(CHANMODE_TOPIC,chan);
+				cmode.tweaktmpmode(CHANMODE_TOPIC,add);
 				break;
 			case "v":
 				if (cm_args.length <= mode_args_counter)
 					break;
-				this.tweaktmpmodelist(CHANLIST_VOICE,CHANLIST_DEVOICE,chan);
+				cmode.tweaktmpmodelist(CHANMODE_VOICE,add,
+					cm_args[mode_args_counter]);
 				mode_args_counter++;
 				break;
 			default:
 				if ((!this.parent) && (!this.server))
-					this.numeric("472", cm_args[0][modechar] + " :is unknown mode char to me.");
+					this.numeric(472, cm_args[0][modechar] + " :is unknown mode char to me.");
 				mode_counter--;
 				break;
 		}
@@ -3481,24 +3469,23 @@ function IRCClient_set_chanmode(chan,modeline,bounce_modes) {
 			break;
 	}
 
-	addmodes = "";
-	delmodes = "";
-	addmodeargs = "";
-	delmodeargs = "";
-
 	// If we're bouncing modes, traverse our side of what the modes look
 	// like and remove any modes not mentioned by what was passed to the
 	// function.  Or, clear any ops, voiced members, or bans on the 'bad'
-	// side of the network sync.  FIXME: Bans get synchronized later.
+	// side of the network sync.
 	if (bounce_modes) {
 		for (cm in MODE) {
-			if (MODE[cm].state && (chan.mode&cm) && !(addbits&cm)) {
-				delbits |= cm;
+			if (MODE[cm].state && (chan.mode&cm) && 
+			    !(cmode.addbits&cm)) {
+				cmode.delbits |= cm;
 			} else if (MODE[cm].list) {
-				for (member in chan.modelist[MODE[cm].list]) {
-					delmodes += MODE[cm].modechar;
-					delmodeargs += " " + Clients[chan.modelist[MODE[cm].list][member]].nick;
-					chan.del_modelist(chan.modelist[MODE[cm].list][member],MODE[cm].list);
+				for (member in chan.modelist[cm]) {
+					cmode.delmodes += MODE[cm].modechar;
+					cmode.delmodeargs += " " +
+						Clients[chan.modelist
+						[cm][member]].nick;
+					chan.del_modelist(chan.modelist
+						[cm][member],cm);
 				}
 			}
 		}
@@ -3508,22 +3495,27 @@ function IRCClient_set_chanmode(chan,modeline,bounce_modes) {
 	// later display.  We also play with the channel bit switches here.
 	for (cm in MODE) {
 		if (MODE[cm].state) {
-			if ((cm&CHANMODE_KEY) && (addbits&CHANMODE_KEY) && 
-			    state_arg[cm] && chan.arg[cm] && !this.server &&
-			    !this.parent && !bounce_modes) {
-				this.numeric(467, chan.nam + " :Channel key already set.");
-			} else if ((addbits&cm) && !(chan.mode&cm)) {
-				addmodes += MODE[cm].modechar;
+			if ((cm&CHANMODE_KEY) && (cmode.addbits&CHANMODE_KEY)&& 
+			    cmode.state_arg[cm] && chan.arg[cm] &&
+			    !this.server && !this.parent && !bounce_modes) {
+				this.numeric(467, chan.nam +
+					" :Channel key already set.");
+			} else if ((cmode.addbits&cm) && (!(chan.mode&cm) ||
+			    ((cm==CHANMODE_LIMIT)&&(chan.arg[CHANMODE_LIMIT]!=
+			     cmode.state_arg[CHANMODE_LIMIT])) ) ) {
+				cmode.addmodes += MODE[cm].modechar;
 				chan.mode |= cm;
 				if (MODE[cm].args && MODE[cm].state) {
-					addmodeargs += " " + state_arg[cm];
-					chan.arg[cm] = state_arg[cm];
+					cmode.addmodeargs += " " +
+						cmode.state_arg[cm];
+					chan.arg[cm] = cmode.state_arg[cm];
 				}
-			} else if ((delbits&cm) && (chan.mode&cm)) {
-				delmodes += MODE[cm].modechar;
+			} else if ((cmode.delbits&cm) && (chan.mode&cm)) {
+				cmode.delmodes += MODE[cm].modechar;
 				chan.mode &= ~cm;
 				if (MODE[cm].args && MODE[cm].state) {
-					delmodeargs += " " + state_arg[cm];
+					cmode.delmodeargs += " " +
+						cmode.state_arg[cm];
 					chan.arg[cm] = "";
 				}
 			}
@@ -3532,32 +3524,62 @@ function IRCClient_set_chanmode(chan,modeline,bounce_modes) {
 
 	// This is a special case, if +b was passed to us without arguments,
 	// we simply display a list of bans on the channel.
-	if (addbits&CHANMODE_BAN) {
-		for (the_ban in chan.modelist[CHANLIST_BAN]) {
-			this.numeric(367, chan.nam + " " + chan.modelist[CHANLIST_BAN][the_ban] + " " + chan.bancreator[the_ban] + " " + chan.bantime[the_ban]);
+	if (cmode.addbits&CHANMODE_BAN) {
+		for (the_ban in chan.modelist[CHANMODE_BAN]) {
+			this.numeric(367, chan.nam + " " + chan.modelist[CHANMODE_BAN][the_ban] + " " + chan.bancreator[the_ban] + " " + chan.bantime[the_ban]);
 		}
 		this.numeric(368, chan.nam + " :End of Channel Ban List.");
 	}
 
-	// Now we play with the channel lists by adding or removing what was
-	// given to us on the modeline.
-	this.affect_mode_list(CHANLIST_OP,chan)
-	this.affect_mode_list(CHANLIST_VOICE,chan);
-	this.affect_mode_list(CHANLIST_BAN,chan);
+	// Bans are a specialized case, sigh.
+	for (z in cmode.tmplist[CHANMODE_BAN][true]) { // +b
+		var set_ban = create_ban_mask(
+			cmode.tmplist[CHANMODE_BAN][add][z]);
+		if ((chan.count_modelist(CHANMODE_BAN) >= max_bans) &&
+		     !this.server && !this.parent) {
+			this.numeric(478, chan.nam + " " + set_ban + " :" +
+				"Cannot add ban, channel's ban list is full.");
+		} else if (set_ban && !chan.isbanned(set_ban)) {
+			cmode.addmodes += "b";
+			cmode.addmodeargs += " " + set_ban;
+			var banid = chan.add_modelist(set_ban,CHANMODE_BAN);
+			chan.bantime[banid] = time();
+			chan.bancreator[banid] = this.ircnuh;
+		}
+	}
 
-	if (!addmodes && !delmodes)
+	for (z in cmode.tmplist[CHANMODE_BAN][false]) { // -b
+		for (ban in chan.modelist[CHANMODE_BAN]) {
+			if (cmode.tmplist[CHANMODE_BAN][false][z].toUpperCase()
+			    == chan.modelist[CHANMODE_BAN][ban].toUpperCase()) {
+				cmode.delmodes += "b";
+				cmode.delmodeargs += " " +
+					cmode.tmplist[CHANMODE_BAN][false][z];
+				var banid = chan.del_modelist(cmode.tmplist
+					[CHANMODE_BAN][false][z],CHANMODE_BAN);
+				delete chan.bantime[banid];
+				delete chan.bancreator[banid];
+			}
+		}
+	}
+
+	// Modes where we just deal with lists of nicks.
+	cmode.affect_mode_list(CHANMODE_OP);
+	cmode.affect_mode_list(CHANMODE_VOICE);
+
+	if (!cmode.addmodes && !cmode.delmodes)
 		return 0;
 
-	final_modestr = "";
+	var final_modestr = "";
 
-	if (addmodes)
-		final_modestr += "+" + addmodes;
-	if (delmodes)
-		final_modestr += "-" + delmodes;
-	if (addmodeargs)
-		final_modestr += addmodeargs;
-	if (delmodeargs)
-		final_modestr += delmodeargs;
+	if (cmode.addmodes)
+		final_modestr += "+" + cmode.addmodes;
+	if (cmode.delmodes)
+		final_modestr += "-" + cmode.delmodes;
+	if (cmode.addmodeargs)
+		final_modestr += cmode.addmodeargs;
+	if (cmode.delmodeargs)
+		final_modestr += cmode.delmodeargs;
 
 	var final_args = final_modestr.split(' ');
 	var arg_counter = 0;
@@ -3732,49 +3754,29 @@ function IRCClient_setusermode(modestr) {
 	return 1;
 }
 
-function IRCClient_affect_mode_list(list_bit,chan) {
-	for (x=list_bit;x<=(list_bit+1);x++) {
-		for (tmp_index in chan_tmplist[x]) {
-			if (list_bit >= CHANLIST_BAN) {
-				if (x == CHANLIST_BAN) {
-					var set_ban = create_ban_mask(chan_tmplist[x][tmp_index]);
-					if (chan.count_modelist(CHANLIST_BAN) >= max_bans) {
-						this.numeric(478, chan.nam + " " + set_ban + " :Cannot add ban, channel's ban list is full.");
-					} else if (set_ban && !chan.isbanned(set_ban)) {
-						addmodes += "b";
-						addmodeargs += " " + set_ban;
-						var banid = chan.add_modelist(set_ban,CHANLIST_BAN);
-						chan.bantime[banid] = time();
-						chan.bancreator[banid] = this.ircnuh;
-					}
-				} else if (x == CHANLIST_UNBAN) {
-					for (ban in chan.modelist[CHANLIST_BAN]) {
-						if (chan_tmplist[CHANLIST_UNBAN][tmp_index].toUpperCase() == chan.modelist[CHANLIST_BAN][ban].toUpperCase()) {
-							delmodes += "b";
-							delmodeargs += " " + chan_tmplist[CHANLIST_UNBAN][tmp_index];
-							var banid = chan.del_modelist(chan_tmplist[CHANLIST_UNBAN][tmp_index],CHANLIST_BAN);
-							delete chan.bantime[banid];
-							delete chan.bancreator[banid];
-						}
-					}
-				}
-			} else {
-				var tmp_nick = searchbynick(chan_tmplist[x][tmp_index]);
-				if (!tmp_nick)
-					tmp_nick = searchbynick(search_nickbuf(chan_tmplist[x][tmp_index]));
-				if (tmp_nick) { // FIXME: check for user existing on channel?
-					if ((x == list_bit) && (!chan.ismode(tmp_nick.id,list_bit))) {
-						addmodes += MODECHAR[list_bit];
-						addmodeargs += " " + tmp_nick.nick;
-						chan.add_modelist(tmp_nick.id,x);
-					} else if (chan.ismode(tmp_nick.id,list_bit)) {
-						delmodes += MODECHAR[list_bit];
-						delmodeargs += " " + tmp_nick.nick;
-						chan.del_modelist(tmp_nick.id,list_bit);
-					}
-				} else {
-					this.numeric401(chan_tmplist[x][tmp_index]);
-				}
+// What was I thinking?! Agh. v2.0 of this function
+function ChanMode_affect_mode_list(list_bit) {
+	var tmp_nick;
+	for (add in this.tmplist[list_bit]) {
+		for (z in this.tmplist[list_bit][add]) {
+			tmp_nick = searchbynick(this.tmplist[list_bit][add][z]);
+			if (!tmp_nick)
+				tmp_nick = searchbynick(search_nickbuf(
+					this.tmplist[list_bit][add][z]));
+			if (tmp_nick && (add=="true") &&
+			    !this.chan.ismode(tmp_nick.id,list_bit)) {
+				this.addmodes += MODE[list_bit].modechar;
+				this.addmodeargs += " " + tmp_nick.nick;
+				this.chan.add_modelist(tmp_nick.id,list_bit);
+			} else if (tmp_nick && (add=="false") && 
+			           this.chan.ismode(tmp_nick.id,list_bit)) {
+				this.delmodes += MODE[list_bit].modechar;
+				this.delmodeargs += " " + tmp_nick.nick;
+				this.chan.del_modelist(tmp_nick.id,list_bit);
+			} else if (!tmp_nick && !this.user.server &&
+			           !this.user.parent) { // no nick? :(
+				this.user.numeric401(this.tmplist[list_bit]
+					[add][z]);
 			}
 		}
 	}
@@ -3786,7 +3788,7 @@ function IRCClient_affect_mode_list(list_bit,chan) {
 function IRCClient_unregistered_commands(command, cmdline) {
 	if (command.match(/^[0-9]+/))
 		return 0; // we ignore all numerics from unregistered clients.
-	cmd=cmdline.split(' ');
+	var cmd=cmdline.split(' ');
 	switch(command) {
 		case "CAPAB":
 			break; // silently ignore for now.
@@ -3947,7 +3949,7 @@ function IRCClient_unregistered_commands(command, cmdline) {
 		this.numeric("001", ":Welcome to the Synchronet IRC Service, " + this.ircnuh);
 		this.numeric("002", ":Your host is " + servername + ", running " + VERSION);
 		this.numeric("003", ":This server was created " + strftime("%a %b %e %Y at %H:%M:%S %Z",server_uptime));
-		this.numeric("004", servername + " " + VERSION + " oi biklmnopstv");
+		this.numeric("004", servername + " " + VERSION + " oiwbgscrkfydnhF biklmnopstv");
 		this.numeric("005", "MODES=" + max_modes + " MAXCHANNELS=" + max_user_chans + " CHANNELLEN=" + max_chanlen + " MAXBANS=" + max_bans + " NICKLEN=" + max_nicklen + " TOPICLEN=" + max_topiclen + " KICKLEN=" + max_kicklen + " CHANTYPES=#& PREFIX=(ov)@+ NETWORK=Synchronet CASEMAPPING=ascii CHANMODES=b,k,l,imnpst STATUSMSG=@+ :are available on this server.");
 		this.lusers();
 		this.motd();
@@ -3956,7 +3958,7 @@ function IRCClient_unregistered_commands(command, cmdline) {
 			") [" + this.socket.remote_ip_address + "] {1}");
 		if (server.client_update != undefined)
 			server.client_update(this.socket, this.nick, this.hostname);
-		server_bcast_to_servers("NICK " + this.nick + " 1 " + this.created + " " + this.get_usermode() + " " + this.uprefix + " " + this.hostname + " " + servername + " 0 " + ip_to_int(this.ip) + " :" + this.realname);
+		server_bcast_to_servers("NICK " + this.nick + " 1 " + this.created + " " + this.get_usermode(true) + " " + this.uprefix + " " + this.hostname + " " + servername + " 0 " + ip_to_int(this.ip) + " :" + this.realname);
 	/// Otherwise, it's a server trying to connect.
 	} else if (this.nick.match("[.]") && this.hops && this.realname &&
 		   this.server && (this.conntype == TYPE_SERVER)) {
@@ -4112,7 +4114,7 @@ function IRCClient_registered_commands(command, cmdline) {
 				this.numeric403(cmd[2]);
 				break;
 			}
-			if (!chanid.ismode(this.id,CHANLIST_OP)) {
+			if (!chanid.ismode(this.id,CHANMODE_OP)) {
 				this.numeric482(chanid.nam);
 				break;
 			}
@@ -4184,7 +4186,7 @@ function IRCClient_registered_commands(command, cmdline) {
 				this.numeric403(cmd[1]);
 				break;
 			}
-			if (!chanid.ismode(this.id,CHANLIST_OP)) {
+			if (!chanid.ismode(this.id,CHANMODE_OP)) {
 				this.numeric482(chanid.nam);
 				break;
 			}
@@ -4743,7 +4745,7 @@ function IRCClient_registered_commands(command, cmdline) {
 			}
 			if (cmd[2]) {
 				if (!(chanid.mode&CHANMODE_TOPIC) ||
-				     chanid.ismode(this.id,CHANLIST_OP) ) {
+				     chanid.ismode(this.id,CHANMODE_OP) ) {
 					var tmp_topic = ircstring(cmdline).slice(0,max_topiclen);
 					if (tmp_topic == chanid.topic)
 						break;
@@ -5152,7 +5154,7 @@ function IRCClient_server_commands(origin, command, cmdline) {
 			var chanid = searchbychannel(cmd[2]);
 			if (!chanid)
 				break;
-			if (!chanid.ismode(ThisOrigin.id,CHANLIST_OP))
+			if (!chanid.ismode(ThisOrigin.id,CHANMODE_OP))
 				break;
 			var nickid = searchbynick(cmd[1]);
 			if (!nickid)
@@ -5330,9 +5332,9 @@ function IRCClient_server_commands(origin, command, cmdline) {
 				chan.created = parseInt(cmd[1]);
 				chan.topic = "";
 				chan.users = new Array();
-				chan.modelist[CHANLIST_BAN] = new Array();
-				chan.modelist[CHANLIST_VOICE] = new Array();
-				chan.modelist[CHANLIST_OP] = new Array();
+				chan.modelist[CHANMODE_BAN] = new Array();
+				chan.modelist[CHANMODE_VOICE] = new Array();
+				chan.modelist[CHANMODE_OP] = new Array();
 				chan.mode = CHANMODE_NONE;
 			}
 			if (cmd[3]) {
@@ -5368,7 +5370,7 @@ function IRCClient_server_commands(origin, command, cmdline) {
 					member_obj.bcast_to_channel(chan.nam, "JOIN " + chan.nam, false);
 					if (chan.created >= parseInt(cmd[1])) {
 						if (is_op) {
-							chan.modelist[CHANLIST_OP].push(member_obj.id);
+							chan.modelist[CHANMODE_OP].push(member_obj.id);
 							push_sync_modes += "o";
 							push_sync_args += " " + member_obj.nick;
 							num_sync_modes++;
@@ -5381,7 +5383,7 @@ function IRCClient_server_commands(origin, command, cmdline) {
 							num_sync_modes = 0;
 						}
 						if (is_voice) {
-							chan.modelist[CHANLIST_VOICE].push(member_obj.id);
+							chan.modelist[CHANMODE_VOICE].push(member_obj.id);
 							push_sync_modes += "v";
 							push_sync_args += " " + member_obj.nick;
 							num_sync_modes++;
@@ -5600,7 +5602,7 @@ function IRCClient_server_commands(origin, command, cmdline) {
 				NewNick.ip = int_to_ip(cmd[9]);
 				NewNick.setusermode(cmd[4]);
 				true_hops = parseInt(NewNick.hops)+1;
-				this.bcast_to_servers_raw("NICK " + NewNick.nick + " " + true_hops + " " + NewNick.created + " " + NewNick.get_usermode() + " " + NewNick.uprefix + " " + NewNick.hostname + " " + NewNick.servername + " 0 " + cmd[9] + " :" + NewNick.realname);
+				this.bcast_to_servers_raw("NICK " + NewNick.nick + " " + true_hops + " " + NewNick.created + " " + NewNick.get_usermode(true) + " " + NewNick.uprefix + " " + NewNick.hostname + " " + NewNick.servername + " 0 " + cmd[9] + " :" + NewNick.realname);
 			}
 			break;
 		case "NOTICE":
@@ -5987,9 +5989,9 @@ function Channel(nam)  {
 	this.arg[CHANMODE_KEY] = "";
 	this.users=new Array;
 	this.modelist=new Array;
-	this.modelist[CHANLIST_OP]=new Array;
-	this.modelist[CHANLIST_VOICE]=new Array;
-	this.modelist[CHANLIST_BAN]=new Array;
+	this.modelist[CHANMODE_OP]=new Array;
+	this.modelist[CHANMODE_VOICE]=new Array;
+	this.modelist[CHANMODE_BAN]=new Array;
 	this.bantime=new Array;
 	this.bancreator=new Array;
 	this.created=time();
@@ -6026,8 +6028,8 @@ function Channel_locate_on_list(tmp_str,mode_bit) {
 function Channel_add_modelist(tmp_nickid,list_bit) {
 	if (this.ismode(tmp_nickid,list_bit))
 		return 0;
-	pushed = this.modelist[list_bit].push(tmp_nickid);
-	return pushed-1;
+	var pushed = this.modelist[list_bit].push(tmp_nickid);
+	return pushed;
 }
 
 function Channel_del_modelist(tmp_nickid,list_bit) {
@@ -6108,8 +6110,8 @@ function inverse_chanmode(bitlist) {
 }
 
 function Channel_isbanned(banned_nuh) {
-	for (this_ban in this.modelist[CHANLIST_BAN]) {
-		if (match_irc_mask(banned_nuh,this.modelist[CHANLIST_BAN][this_ban]))
+	for (this_ban in this.modelist[CHANMODE_BAN]) {
+		if (match_irc_mask(banned_nuh,this.modelist[CHANMODE_BAN][this_ban]))
 			return 1;
 	}
 	return 0;
@@ -6123,9 +6125,9 @@ function Channel_occupants() {
 		    (Channel_user.conntype != TYPE_SERVER)) {
 			if (chan_occupants)
 				chan_occupants += " ";
-			if (this.ismode(Channel_user.id,CHANLIST_OP))
+			if (this.ismode(Channel_user.id,CHANMODE_OP))
 				chan_occupants += "@";
-			if (this.ismode(Channel_user.id,CHANLIST_VOICE))
+			if (this.ismode(Channel_user.id,CHANMODE_VOICE))
 				chan_occupants += "+";
 			chan_occupants += Channel_user.nick;
 		}