From a845544e1e719eb21da908a12b8fb2054a102ee1 Mon Sep 17 00:00:00 2001
From: Randy Sommerfeld <synchronet-gitlab@rrx.ca>
Date: Mon, 5 Apr 2021 00:12:40 -0700
Subject: [PATCH] IRCd 1.9

---
 exec/ircd.js                                  | 3077 +----------------
 .../load/{ircd_channel.js => ircd/channel.js} |  154 +-
 exec/load/ircd/config.js                      |  419 +++
 exec/load/ircd/core.js                        | 2521 ++++++++++++++
 exec/load/{ircd_server.js => ircd/server.js}  |  348 +-
 .../{ircd_unreg.js => ircd/unregistered.js}   |  143 +-
 exec/load/{ircd_user.js => ircd/user.js}      |  123 +-
 7 files changed, 3444 insertions(+), 3341 deletions(-)
 rename exec/load/{ircd_channel.js => ircd/channel.js} (84%)
 create mode 100644 exec/load/ircd/config.js
 create mode 100644 exec/load/ircd/core.js
 rename exec/load/{ircd_server.js => ircd/server.js} (82%)
 rename exec/load/{ircd_unreg.js => ircd/unregistered.js} (80%)
 rename exec/load/{ircd_user.js => ircd/user.js} (93%)

diff --git a/exec/ircd.js b/exec/ircd.js
index 3f96884b94..2d834646da 100644
--- a/exec/ircd.js
+++ b/exec/ircd.js
@@ -1,53 +1,51 @@
-// $Id: ircd.js,v 1.193 2020/04/04 08:32:04 deuce Exp $
-//
-// ircd.js
-//
-// This program is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation; either version 2 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details:
-// http://www.gnu.org/licenses/gpl.txt
-//
-// Synchronet IRC Daemon as per RFC 1459, link compatible with Bahamut 1.4
-//
-// Copyright 2003-2009 Randolph Erwin Sommerfeld <sysop@rrx.ca>
-//
+/*
+
+ ircd.js
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details:
+ https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
 
-//load("synchronet-json.js");
+ Synchronet IRC Daemon. Link compatible with Bahamut.
 
+ Copyright 2003-2021 Randy Sommerfeld <cyan@synchro.net>
+
+*/
+
+"use strict";
+
+/* Synchronet libraries */
 load("sbbsdefs.js");
 load("sockdefs.js");
 load("nodedefs.js");
-
 load("irclib.js");
 
-load("ircd_unreg.js");
-load("ircd_user.js");
-load("ircd_channel.js");
-load("ircd_server.js");
+/* Libraries specific to the IRCd */
+load("ircd/core.js");
+load("ircd/unregistered.js");
+load("ircd/user.js");
+load("ircd/channel.js");
+load("ircd/server.js");
+load("ircd/config.js");
 
-// CVS revision
-const MAIN_REVISION = "$Revision: 1.193 $".split(' ')[1];
+/* Global Constants */
 
-// Please don't play with this, unless you're making custom hacks.
-// IF you're making a custom version, it'd be appreciated if you left the
-// version number alone, and add a token in the form of +hack (i.e. 1.0+cyan)
-// This is so everyone knows your revision base, AND type of hack used.
-const VERSION = "SynchronetIRCd-1.3a(" + MAIN_REVISION + ")";
-const VERSION_STR = "Synchronet " 
-	+ system.version + system.revision + "-" + system.platform 
-	+ system.beta_version + " (IRCd by Randy Sommerfeld)";
+const VERSION = "SynchronetIRCd-1.9a";
+const VERSION_STR = format(
+	"Synchronet %s%s-%s%s (IRCd by Randy Sommerfeld)",
+	system.version, system.revision,
+	system.platform, system.beta_version
+);
 
-// This will dump all I/O to and from the server to your Synchronet console.
-// It also enables some more verbose WALLOPS, especially as they pertain to
-// blocking functions.
-// The special "DEBUG" oper command also switches this value.
-var debug = false;
+/* This will be replaced with a dynamic CAPAB system later. */
+const Server_CAPAB = "TS3 NOQUIT SSJOIN BURST UNCONNECT NICKIP TSMODE";
 
 // The number of seconds to block before giving up on outbound CONNECT
 // attempts (when connecting to another IRC server -- i.e. a hub)  This value
@@ -63,16 +61,6 @@ const ob_sock_timeout = 3;
 // seeing who's on an arbitrary BBS or summoning them to IRC.
 const enable_users_summon = true;
 
-// what our server is capable of from a server point of view.
-// TS3 = Version 3 of accepted interserver timestamp protocol.
-// NOQUIT = QUIT clients on behalf of a SQUIT server? (no netsplit spam)
-// SSJOIN = SJOIN interserver command without dual TS, single TS only.
-// BURST = Sending of network synch data is done in a 3-stage burst (BURST cmd)
-// UNCONNECT = Server SQUIT is routable.
-// NICKIP = 9th parameter of interserver NICK command is an integer IP.
-// TSMODE = 2nd arg to standard MODE is the channel's TS.
-const server_capab = "TS3 NOQUIT SSJOIN BURST UNCONNECT NICKIP TSMODE";
-
 // EVERY server on the network MUST have the same values in ALL of these
 // categories.  If you change these, you WILL NOT be able to link to the
 // Synchronet IRC network.  Linking servers with different values here WILL
@@ -81,75 +69,78 @@ const server_capab = "TS3 NOQUIT SSJOIN BURST UNCONNECT NICKIP TSMODE";
 const max_chanlen = 100;	// Maximum channel name length.
 const max_nicklen = 30;		// Maximum nickname length.
 const max_modes = 6;		// Maximum modes on single MODE command
-const max_user_chans = 10;	// Maximum channels users can join
+const max_user_chans = 100;	// Maximum channels users can join
 const max_bans = 25;		// Maximum bans (+b) per channel
 const max_topiclen = 307;	// Maximum length of topic per channel
 const max_kicklen = 307;	// Maximum length of kick reasons
 const max_who = 100;		// Maximum replies to WHO for non-oper users
 const max_silence = 10;		// Maximum entries on a user's SILENCE list
 
-/* Server types */
-const BAHAMUT = 1;
-const DREAMFORGE = 2;
+const server_uptime = time();
 
-var default_port = 6667;
+/* Global Variables */
 
-log(VERSION + " started.");
+// This will dump all I/O to and from the server to your Synchronet console.
+// It also enables some more verbose WALLOPS, especially as they pertain to
+// blocking functions.
+// The special "DEBUG" oper command also switches this value.
+var debug = false;
+var default_port = 6667;
 
-// Our primary arrays.
-Unregistered = new Object;
-Users = new Object;
-Servers = new Object;
-Channels = new Object;
+/* This was previously on its own in the functions
+   Maybe there was a reason why? */
+var time_config_read;
 
-Local_Sockets = new Object;
-Local_Sockets_Map = new Object;
+/* Primary arrays */
+var Unregistered = new Object;
+var Users = new Object;
+var Servers = new Object;
+var Channels = new Object;
 
-Selectable_Sockets = new Object;
-Selectable_Sockets_Map = new Object;
+var Local_Sockets = new Object;
+var Local_Sockets_Map = new Object;
 
-Global_CommandLine = ""; // We use this to track if a cmdline causes a crash.
+var Selectable_Sockets = new Object;
+var Selectable_Sockets_Map = new Object;
 
-hcc_total = 0;
-hcc_users = 0;
-hcc_counter = 0;
-server_uptime = time();
+/* Highest Connection Count tracking */
+var hcc_total = 0;
+var hcc_users = 0;
+var hcc_counter = 0;
 
-WhoWas = new Object;	/* Stores uppercase nicks */
-WhoWasMap = new Array;	/* A true push/pop array pointing to WhoWas entries */
-WhoWas_Buffer = 1000;	/* Maximum number of WhoWas entries to keep track of */
+var WhoWas = new Object;	/* Stores uppercase nicks */
+var WhoWasMap = new Array;	/* A true push/pop array pointing to WhoWas entries */
+var WhoWas_Buffer = 1000;	/* Maximum number of WhoWas entries to keep track of */
 
-NickHistory = new Array;	/* A true array using push and pop */
-nick_buffer = 1000;
-nick_pointer = 0;
+var NickHistory = new Array;	/* A true array using push and pop */
+var NickHistorySize = 1000;
 
 /* Keep track of commands and how long they take to execute. */
-Profile = new Object;
+var Profile = new Object;
 
-// This is where our unique ID for each client comes from for unreg'd clients.
-next_client_id = 0;
+/* This is where our unique ID for each client comes from for unreg'd clients. */
+var next_client_id = 0;
 
 // An array containing all the objects containing local sockets that we need
 // to poll.
-Local_Users = new Object;
-Local_Servers = new Object;
+var Local_Users = new Object;
+var Local_Servers = new Object;
 
-rebuild_socksel_array = true;
+var rebuild_socksel_array = true;
 
-network_debug = false;
+var network_debug = false;
 
-last_recvq_check = 0;
+var last_recvq_check = 0;
 
-/*
- * A tri-state variable indicating if socket.send is "old" (ie: returns bool)
- * or "new" (ie: returns count of bytes sent).
- */
-var new_socket_send;
+var servername = "server.invalid";
+var serverdesc = "No description provided.";
+
+log(VERSION + " started.");
 
 // Parse command-line arguments.
-config_filename="";
+var config_filename="";
 var cmdline_port;
-var cmdline_addr;
+var cmdarg;
 for (cmdarg=0;cmdarg<argc;cmdarg++) {
 	switch(argv[cmdarg].toLowerCase()) {
 		case "-f":
@@ -167,19 +158,30 @@ for (cmdarg=0;cmdarg<argc;cmdarg++) {
 	}
 }
 
+/* Temporary hack to make JSexec testing code not complain */
+var mline_port;
+
 read_config_file();
 
-if(this.server==undefined) {		// Running from JSexec?
+/* This tests if we're running from JSexec or not */
+if(this.server==undefined) {
 	if (!jsexec_revision_detail)
-		jsexec_revision_detail = "JSexec";
+		var jsexec_revision_detail = "JSexec";
+
 	if (cmdline_port)
 		default_port = cmdline_port;
-	else if (mline_port)
+	else if (typeof mline_port !== undefined)
 		default_port = mline_port;
 
-	server = { socket: false, terminated: false,
-		version_detail: jsexec_revision_detail, interface_ip_addr_list: (cmdline_addr || ["0.0.0.0","::"]) };
+	var server = {
+		socket: false,
+		terminated: false,
+		version_detail: jsexec_revision_detail,
+		interface_ip_addr_list: ["0.0.0.0","::"]
+	};
+
 	server.socket = create_new_socket(default_port)
+
 	if (!server.socket)
 		exit();
 }
@@ -188,7 +190,7 @@ server.socket.nonblocking = true;	// REQUIRED!
 server.socket.debug = false;		// Will spam your log if true :)
 
 // Now open additional listening sockets as defined on the P:Line in ircd.conf
-open_plines = new Array(); /* True Array */
+var open_plines = new Array(); /* True Array */
 // Make our 'server' object the first open P:Line
 open_plines[0] = server.socket;
 for (pl in PLines) {
@@ -203,13 +205,13 @@ for (pl in PLines) {
 js.branch_limit=0; // we're not an infinite loop.
 js.auto_terminate=false; // we handle our own termination requests
 
-///// Main Loop /////
+/*** Main Loop ***/
 while (!js.terminated) {
 
 	if(file_date(system.ctrl_dir + "ircd.rehash") > time_config_read)
 		read_config_file();
 
-	// Setup a new socket if a connection is accepted.
+	/* Setup a new socket if a connection is accepted. */
 	for (pl in open_plines) {
 		if (open_plines[pl].poll()) {
 			var client_sock=open_plines[pl].accept();
@@ -226,8 +228,10 @@ while (!js.terminated) {
 					log(LOG_DEBUG,"Socket has no IP address.  Closing.");
 					client_sock.close();
 				} else if (iszlined(client_sock.remote_ip_address)) {
-					client_sock.send(":" + servername
-						+ " 465 * :You've been Z:Lined from this server.\r\n");
+					client_sock.send(format(
+						":%s 465 * :You've been Z:Lined from this server.\r\n",
+						servername
+					));
 					client_sock.close();
 				} else {
 					var new_id = "id" + next_client_id;
@@ -248,7 +252,7 @@ while (!js.terminated) {
 	// Check for pending DNS hostname resolutions.
 	for(this_unreg in Unregistered) {
 		if (Unregistered[this_unreg] &&
-		    Unregistered[this_unreg].pending_resolve_time)
+			Unregistered[this_unreg].pending_resolve_time)
 			Unregistered[this_unreg].resolve_check();
 	}
 
@@ -268,9 +272,9 @@ while (!js.terminated) {
 	for(this_sock in Selectable_Sockets) {
 		if (Selectable_Sockets_Map[this_sock]) {
 			Selectable_Sockets_Map[this_sock].check_timeout();
-            Selectable_Sockets_Map[this_sock].check_queues();
-        }
-    }
+			Selectable_Sockets_Map[this_sock].check_queues();
+		}
+	}
 
 	// do some work.
 	if (Selectable_Sockets.length) {
@@ -287,12 +291,12 @@ while (!js.terminated) {
 				}
 			}
 		} catch(e) {
-			gnotice("FATAL ERROR: " + e + " CMDLINE: " + Global_CommandLine);
-			log(LOG_ERR,"JavaScript exception: " + e + " CMDLINE: "
-				+ Global_CommandLine);
+			gnotice("FATAL ERROR: " + e);
+			log(LOG_ERR,"JavaScript exception: " + e);
 			terminate_everything("A fatal error occured!", /* ERROR? */true);
 		}
 	} else {
+		/* Nothing's connected to us, so hang out for a bit */
 		mswait(100);
 	}
 
@@ -300,2851 +304,24 @@ while (!js.terminated) {
 	var my_cline;
 	for(thisCL in CLines) {
 		my_cline = CLines[thisCL];
-		if (my_cline.port && YLines[my_cline.ircclass].connfreq &&
-		    (YLines[my_cline.ircclass].maxlinks > YLines[my_cline.ircclass].active) &&
-		    (search_server_only(my_cline.servername) < 1) &&
-		     ((time() - my_cline.lastconnect) >
-		     YLines[my_cline.ircclass].connfreq)
-		   ) {
-			umode_notice(USERMODE_ROUTING,"Routing",
-				"Auto-connecting to " +
-				CLines[thisCL].servername + " ("+CLines[thisCL].host+")");
+		if (   my_cline.port
+			&& YLines[my_cline.ircclass].connfreq
+			&& (YLines[my_cline.ircclass].maxlinks > YLines[my_cline.ircclass].active)
+			&& (search_server_only(my_cline.servername) < 1)
+			&& ((time() - my_cline.lastconnect) > YLines[my_cline.ircclass].connfreq)
+		) {
+			umode_notice(
+				USERMODE_ROUTING,
+				"Routing",
+				format("Auto-connecting to %s (%s)",
+					CLines[thisCL].servername,
+					CLines[thisCL].host
+				)
+			);
 			connect_to_server(CLines[thisCL]);
 		}
 	}
-
 }
 
-// End of our run, so terminate everything before we go.
+/* We've exited the main loop, so terminate everything. */
 terminate_everything("Terminated.");
-
-//////////////////////////////// END OF MAIN ////////////////////////////////
-
-// Okay, welcome to my world.
-// str = The string used for the quit reason UNLESS 'is_netsplit' is set to
-//       true, in which case it becomes the string used to QUIT individual
-//       clients in a netsplit (i.e. "server.one server.two")
-// suppress_bcast = Set to TRUE if you don't want the message to be broadcast
-//       accross the entire network.  Useful for netsplits, global kills, or
-//       other network messages where the rest of the network is nuking the
-//       client on their own.
-// is_netsplit = Should never be used except in a recursive call from the
-//       'this.netsplit()' function.  Tells the function that we're recursive
-//       and to use 'str' as the reason for quiting all the clients
-// origin = an object typically only passed in the case of a SQUIT, contains
-//       the client who originated the message (i.e. for generating netsplit
-//       messages.)
-// FIXME: this function split into three. comments kept for now, but nuke later
-
-////////// Functions not linked to an object //////////
-
-// Sigh, there's no way to tell the length of an associative array in JS, so,
-// we have this to help us:
-function true_array_len(my_array) {
-	var counter = 0;
-	for (i in my_array) {
-		counter++;
-	}
-	return counter;
-}
-
-function terminate_everything(terminate_reason, error) {
-	log(error ? LOG_ERR : LOG_NOTICE, "Terminating: " + terminate_reason);
-	for(thisClient in Local_Sockets_Map) {
-		var Client = Local_Sockets_Map[thisClient];
-		Client.rawout("ERROR :" + terminate_reason);
-		Client.socket.close();
-	}
-	exit(error);
-}
-
-function search_server_only(server_name) {
-	if (!server_name)
-		return 0;
-	for(thisServer in Servers) {
-		var Server=Servers[thisServer];
-		if (wildmatch(Server.nick,server_name))
-			return Server;
-	}
-	if (wildmatch(servername,server_name))
-		return -1; // the server passed to us is our own.
-	// No success.
-	return 0;
-}
-
-function searchbyserver(servnick) {
-	if (!servnick)
-		return 0;
-	var server_try = search_server_only(servnick);
-	if (server_try) {
-		return server_try;
-	} else {
-		for(thisNick in Users) {
-			var Nick=Users[thisNick];
-			if (wildmatch(Nick.nick,servnick))
-				return search_server_only(Nick.servername);
-		}
-	}
-	return 0; // looks like we failed after all that hard work :(
-}
-
-// Only allow letters, numbers and underscore in username to a maximum of
-// 9 characters for 'anonymous' users (i.e. not using PASS to authenticate.)
-// hostile characters like !,@,: etc would be bad here :)
-function parse_username(str) {
-	str = str.replace(/[^\w]/g,"").toLowerCase();
-	if (!str)
-		str = "user"; // nothing? we'll give you something boring.
-	return str.slice(0,9);
-}
-
-function parse_nline_flags(flags) {
-	var nline_flags = 0;
-	for(thisflag in flags) {
-		switch(flags[thisflag]) {
-			case "q":
-				nline_flags |= NLINE_CHECK_QWKPASSWD;
-				break;
-			case "w":
-				nline_flags |= NLINE_IS_QWKMASTER;
-				break;
-			case "k":
-				nline_flags |= NLINE_CHECK_WITH_QWKMASTER;
-				break;
-			case "d":
-				nline_flags |= NLINE_IS_DREAMFORGE;
-				break;
-			default:
-				log(LOG_WARNING,"!WARNING Unknown N:Line flag '"
-					+ flags[thisflag] + "' in config.");
-				break;
-		}
-	}
-	return nline_flags;
-}
-
-function parse_oline_flags(flags) {
-	var oline_flags = 0;
-	for(thisflag in flags) {
-		switch(flags[thisflag]) {
-			case "r":
-				oline_flags |= OLINE_CAN_REHASH;
-				break;
-			case "R":
-				oline_flags |= OLINE_CAN_RESTART;
-				break;
-			case "D":
-				oline_flags |= OLINE_CAN_DIE;
-				break;
-			case "g":
-				oline_flags |= OLINE_CAN_GLOBOPS;
-				break;
-			case "w":
-				oline_flags |= OLINE_CAN_WALLOPS;
-				break;
-			case "l":
-				oline_flags |= OLINE_CAN_LOCOPS;
-				break;
-			case "c":
-				oline_flags |= OLINE_CAN_LSQUITCON;
-				break;
-			case "C":
-				oline_flags |= OLINE_CAN_GSQUITCON;
-				break;
-			case "k":
-				oline_flags |= OLINE_CAN_LKILL;
-				break;
-			case "K":
-				oline_flags |= OLINE_CAN_GKILL;
-				break;
-			case "b":
-				oline_flags |= OLINE_CAN_KLINE;
-				break;
-			case "B":
-				oline_flags |= OLINE_CAN_UNKLINE;
-				break;
-			case "n":
-				oline_flags |= OLINE_CAN_LGNOTICE;
-				break;
-			case "N":
-				oline_flags |= OLINE_CAN_GGNOTICE;
-				break;
-			case "u":
-				oline_flags |= OLINE_CAN_UMODEC;
-				break;
-			case "A":
-				oline_flags |= OLINE_IS_ADMIN;
-				break;
-			case "a":
-			case "f":
-			case "F":
-				break; // All reserved for future use.
-			case "s":
-				oline_flags |= OLINE_CAN_CHATOPS;
-				break;
-			case "S":
-				oline_flags |= OLINE_CHECK_SYSPASSWD;
-				break;
-			case "x":
-			case "X":
-				oline_flags |= OLINE_CAN_DEBUG;
-				break;
-			case "O":
-				oline_flags |= OLINE_IS_GOPER;
-				oline_flags |= OLINE_CAN_GSQUITCON;
-				oline_flags |= OLINE_CAN_GKILL;
-				oline_flags |= OLINE_CAN_GGNOTICE;
-				oline_flags |= OLINE_CAN_CHATOPS;
-			case "o":
-				oline_flags |= OLINE_CAN_REHASH;
-				oline_flags |= OLINE_CAN_GLOBOPS;
-				oline_flags |= OLINE_CAN_WALLOPS;
-				oline_flags |= OLINE_CAN_LOCOPS;
-				oline_flags |= OLINE_CAN_LSQUITCON;
-				oline_flags |= OLINE_CAN_LKILL;
-				oline_flags |= OLINE_CAN_KLINE;
-				oline_flags |= OLINE_CAN_UNKLINE;
-				oline_flags |= OLINE_CAN_LGNOTICE;
-				oline_flags |= OLINE_CAN_UMODEC;
-				break;
-			default:
-				log(LOG_WARNING,"!WARNING Unknown O:Line flag '"
-					+ flags[thisflag] + "' in config.");
-				break;
-		}
-	}
-	return oline_flags;
-}
-
-function umode_notice(bit,ntype,nmessage) {
-	log(ntype + ": " + nmessage);
-	for (thisuser in Local_Users) {
-		var user = Local_Users[thisuser];
-		if (user.mode && ((user.mode&bit)==bit))
-			user.rawout(":" + servername + " NOTICE " + user.nick
-				+ " :*** " + ntype + " -- " + nmessage);
-	}
-
-}
-
-function create_ban_mask(str,kline) {
-	var tmp_banstr = new Array;
-	tmp_banstr[0] = "";
-	tmp_banstr[1] = "";
-	tmp_banstr[2] = "";
-	var bchar_counter = 0;
-	var part_counter = 0; // BAN: 0!1@2 KLINE: 0@1
-	var regexp="[A-Za-z\{\}\`\^\_\|\\]\\[\\\\0-9\-.*?\~:]";
-	var finalstr;
-	for (bchar in str) {
-		if (str[bchar].match(regexp)) {
-			tmp_banstr[part_counter] += str[bchar];
-			bchar_counter++;
-		} else if ((str[bchar] == "!") && (part_counter == 0) &&
-			   !kline) {
-			part_counter = 1;
-			bchar_counter = 0;
-		} else if ((str[bchar] == "@") && (part_counter == 1) &&
-			   !kline) {
-			part_counter = 2;
-			bchar_counter = 0;
-		} else if ((str[bchar] == "@") && (part_counter == 0)) {
-			if (kline) {
-				part_counter = 1;
-			} else {
-				tmp_banstr[1] = tmp_banstr[0];
-				tmp_banstr[0] = "*";
-				part_counter = 2;
-			}
-			bchar_counter = 0;
-		}
-	}
-	if (!tmp_banstr[0] && !tmp_banstr[1] && !tmp_banstr[2])
-		return 0;
-	if (tmp_banstr[0].match(/[.]/) && !tmp_banstr[1] && !tmp_banstr[2]) {
-		if (kline)
-			tmp_banstr[1] = tmp_banstr[0];
-		else
-			tmp_banstr[2] = tmp_banstr[0];
-		tmp_banstr[0] = "";
-	}
-	if (!tmp_banstr[0])
-		tmp_banstr[0] = "*";
-	if (!tmp_banstr[1])
-		tmp_banstr[1] = "*";
-	if (!tmp_banstr[2] && !kline)
-		tmp_banstr[2] = "*";
-	if (kline)
-		finalstr = tmp_banstr[0].slice(0,10) + "@" + tmp_banstr[1].slice(0,80);
-	else
-		finalstr = tmp_banstr[0].slice(0,max_nicklen) + "!"
-			+ tmp_banstr[1].slice(0,10) + "@" + tmp_banstr[2].slice(0,80);
-        while (finalstr.match(/[*][*]/)) {
-                finalstr=finalstr.replace(/[*][*]/g,"*");
-        }
-	return finalstr;
-}
-
-function isklined(kl_str) {
-	for(the_kl in KLines) {
-		if (KLines[the_kl].hostmask &&
-		    wildmatch(kl_str,KLines[the_kl].hostmask))
-			return KLines[the_kl];
-	}
-	return 0;
-}
-
-function iszlined(zl_ip) {
-	for(the_zl in ZLines) {
-		if (ZLines[the_zl].ipmask &&
-		    wildmatch(zl_ip,ZLines[the_zl].ipmask))
-			return 1;
-	}
-	return 0;
-}
-
-function scan_for_klined_clients() {
-	for(thisUser in Local_Users) {
-		var theuser=Local_Users[thisUser];
-		var kline=isklined(theuser.uprefix + "@" + theuser.hostname);
-		if (kline)
-			theuser.quit("User has been K:Lined (" + kline.reason + ")");
-		if (iszlined(theuser.ip))
-			theuser.quit("User has been Z:Lined");
-	}
-}
-
-function remove_kline(kl_hm) {
-	for(the_kl in KLines) {
-		if (KLines[the_kl].hostmask &&
-		    wildmatch(kl_hm,KLines[the_kl].hostmask)) {
-			KLines[the_kl].hostmask = "";
-			KLines[the_kl].reason = "";
-			KLines[the_kl].type = "";
-			return 1;
-		}
-	}
-	return 0; // failure.
-}
-
-function connect_to_server(this_cline,the_port) {
-	var connect_sock;
-	var new_id;
-
-	if (!the_port && this_cline.port)
-		the_port = this_cline.port;
-	else if (!the_port)
-		the_port = default_port; // try a safe default.
-	if (js.global.ConnectedSocket != undefined) {
-		try {
-			connect_sock = new ConnectedSocket(this_cline.host, the_port, {timeout:ob_sock_timeout, bindaddrs:server.interface_ip_addr_list});
-		}
-		catch(e) {
-			connect_sock = new Socket();
-		}
-	}
-	else {
-		connect_sock = new Socket();
-		connect_sock.bind(0,server.interface_ip_address);
-		connect_sock.connect(this_cline.host,the_port,ob_sock_timeout);
-	}
-
-	var sendts = true; /* Assume Bahamut */
-
-	for (nl in NLines) {
-		var mynl = NLines[nl];
-		if ((mynl.flags&NLINE_IS_DREAMFORGE) && 
-		    (mynl.servername == this_cline.servername)) {
-			sendts = false;
-			break;
-		}
-	}
-
-	if (connect_sock.is_connected) {
-		umode_notice(USERMODE_ROUTING,"Routing",
-			"Connected!  Sending info...");
-		var sendstr = "PASS " + this_cline.password;
-		if (sendts)
-			sendstr += " :TS";
-		connect_sock.send(sendstr + "\r\n");
-		connect_sock.send("CAPAB " + server_capab + "\r\n");
-		connect_sock.send("SERVER " + servername + " 1 :" + serverdesc +"\r\n");
-		new_id = "id" + next_client_id;
-		next_client_id++;
-		Unregistered[new_id]=new Unregistered_Client(new_id,connect_sock);
-		Unregistered[new_id].sendps = false; // Don't do P/S pair again
-		Unregistered[new_id].outgoing = true; /* Outgoing Connection */
-		Unregistered[new_id].ircclass = this_cline.ircclass;
-		YLines[this_cline.ircclass].active++;
-		log(LOG_DEBUG, "Class "+this_cline.ircclass+" up to "+YLines[this_cline.ircclass].active+" active out of "+YLines[this_cline.ircclass].maxlinks);
-	}
-	else {
-		umode_notice(USERMODE_ROUTING,"Routing",
-			"Failed to connect to " +
-			this_cline.servername + " ("+this_cline.host+")");
-		connect_sock.close();
-	}
-	this_cline.lastconnect = time();
-}
-
-function wallopers(str) {
-	for(thisoper in Local_Users) {
-		var oper=Local_Users[thisoper];
-		if (oper.mode&USERMODE_OPER)
-			oper.rawout(str);
-	}
-}
-
-function push_nickbuf(oldnick,newnick) {
-	NickHistory.unshift(new NickBuf(oldnick,newnick));
-	if(NickHistory.length >= nick_buffer)
-		NickHistory.pop();
-}
-
-function search_nickbuf(bufnick) {
-	for (nb=NickHistory.length-1;nb>-1;nb--) {
-		if (bufnick.toUpperCase() == NickHistory[nb].oldnick.toUpperCase()) {
-			if (!Users[NickHistory[nb].newnick.toUpperCase()])
-				bufnick = NickHistory[nb].newnick;
-			else
-				return Users[NickHistory[nb].newnick.toUpperCase()];
-		}
-	}
-	return 0;
-}
-
-var time_config_read;
-
-function read_config_file() {
-	/* All of these variables are global. */
-	Admin1 = "";
-	Admin2 = "";
-	Admin3 = "";
-	CLines = new Array;
-	HLines = new Array;
-	ILines = new Array;
-	KLines = new Array;
-	NLines = new Array;
-	OLines = new Array;
-	PLines = new Array;
-	QLines = new Array;
-	ULines = new Array;
-	diepass = "";
-	restartpass = "";
-	YLines = new Array;
-	ZLines = new Array;
-	/* End of global variables */
-	var fname="";
-	if (config_filename && config_filename.length) {
-		if(config_filename.indexOf('/')>=0 || config_filename.indexOf('\\')>=0)
-			fname=config_filename;
-		else
-			fname=system.ctrl_dir + config_filename;
-	} else {
-		fname=system.ctrl_dir + "ircd." + system.local_host_name + ".conf";
-		if(!file_exists(fname))
-			fname=system.ctrl_dir + "ircd." + system.host_name + ".conf";
-		if(!file_exists(fname))
-			fname=system.ctrl_dir + "ircd.conf";
-	}
-	log(LOG_INFO,"Reading Config: " + fname);
-	if (fname.substr(fname.length-3,3) == "ini")
-		read_ini_config(fname);
-	else
-		read_conf_config(fname);
-
-	time_config_read = time();
-}
-
-function read_ini_config(fname) {
-	var conf = new File(fname);
-	if (conf.open("r")) {
-		/* Global Variables */
-	}
-	conf.close();
-}
-
-function read_conf_config(fname) {
-	var conf = new File(fname);
-
-	function fancy_split(line) {
-		var ret = [];
-		var i;
-		var s = 0;
-		var inb = false;
-		var str;
-
-		for (i = 0; i < line.length; i++) {
-			if (line[i] == ':') {
-				if (inb)
-					continue;
-				if (i > 0 && line[i-1] == ']')
-					str = line.slice(s, i-1);
-				else
-					str = line.slice(s, i);
-				ret.push(str);
-				s = i + 1;
-			}
-			else if (!inb && line[i] == '[' && s == i) {
-				inb = true;
-				s = i + 1;
-			}
-			else if (line[i] == ']' && (i+1 == line.length || line[i+1] == ':')) {
-				inb = false;
-			}
-		}
-		if (s < line.length) {
-			if (i > 0 && line[i-1] == ']')
-				str = line.slice(s, i-1);
-			else
-				str = line.slice(s, i);
-			ret.push(str);
-		}
-
-		return ret;
-	}
-
-	if (conf.open("r")) {
-		while (!conf.eof) {
-			var conf_line = conf.readln();
-			if ((conf_line != null) && conf_line.match("[:]")) {
-				var arg = fancy_split(conf_line);
-				for(argument in arg) {
-					arg[argument]=arg[argument].replace(
-						/SYSTEM_HOST_NAME/g,system.host_name);
-					arg[argument]=arg[argument].replace(
-						/SYSTEM_NAME/g,system.name);
-					arg[argument]=arg[argument].replace(
-						/SYSTEM_QWKID/g,system.qwk_id.toLowerCase());
-					arg[argument]=arg[argument].replace(
-						/VERSION_NOTICE/g,system.version_notice);
-				}
-				switch (conf_line[0].toUpperCase()) {
-					case "A":
-						if (!arg[3])
-							break;
-						Admin1 = arg[1];
-						Admin2 = arg[2];
-						Admin3 = arg[3];
-						break;
-					case "C":
-						if (!arg[5])
-							break;
-						CLines.push(new CLine(arg[1],arg[2],arg[3],arg[4],
-							parseInt(arg[5]) ));
-						break;
-					case "H":
-						if (!arg[3])
-							break;
-						HLines.push(new HLine(arg[1],arg[3]));
-						break;
-					case "I":
-						if (!arg[5])
-							break;
-						ILines.push(new ILine(arg[1],arg[2],arg[3],arg[4],
-							arg[5]));
-						break;
-					case "K":
-						if (!arg[2])
-							break;
-						var kline_mask = create_ban_mask(arg[1],true);
-						if (!kline_mask) {
-							log(LOG_WARNING,"!WARNING Invalid K:Line ("
-								+ arg[1] + ")");
-							break;
-						}
-						KLines.push(new KLine(kline_mask,arg[2],"K"));
-						break;
-					case "M":
-						if (!arg[3])
-							break;
-						servername = arg[1];
-						serverdesc = arg[3];
-						mline_port = parseInt(arg[4]);
-						break;
-					case "N":
-						if (!arg[5])
-							break;
-						NLines.push(new NLine(arg[1],arg[2],arg[3],
-							parse_nline_flags(arg[4]),arg[5]) );
-						break;
-					case "O":
-						if (!arg[5])
-							break;
-						OLines.push(new OLine(arg[1],arg[2],arg[3],
-							parse_oline_flags(arg[4]),parseInt(arg[5]) ));
-						break;
-					case "P":
-						PLines.push(parseInt(arg[4]));
-						break;
-					case "Q":
-						if (!arg[3])
-							break;
-						QLines.push(new QLine(arg[3],arg[2]));
-						break;
-					case "U":
-						if (!arg[1])
-							break;
-						ULines.push(arg[1]);
-						break;
-					case "X":
-						diepass = arg[1];
-						restartpass = arg[2];
-						break;
-					case "Y":
-						if (!arg[5])
-							break;
-						YLines[parseInt(arg[1])] = new YLine(parseInt(arg[2]),
-							parseInt(arg[3]),parseInt(arg[4]),parseInt(arg[5]));
-						break;
-					case "Z":
-						if (!arg[2])
-							break;
-						ZLines.push(new ZLine(arg[1],arg[2]));
-						break;
-					case "#":
-					case ";":
-					default:
-						break;
-				}
-			}
-		}
-		conf.close();
-	} else {
-		log ("WARNING! No config file found or unable to open."
-			+ " Proceeding with defaults.");
-	}
-	scan_for_klined_clients();
-	YLines[0] = new YLine(120,600,1,5050000); // default irc class
-}
-
-function create_new_socket(port) {
-	var newsock;
-
-	log(LOG_DEBUG,"Creating new socket object on port " + port);
-	if (js.global.ListeningSocket != undefined) {
-		try {
-			newsock = new ListeningSocket(server.interface_ip_addr_list, port, "IRCd");
-			log(format("IRC server socket bound to TCP port " + port));
-		}
-		catch(e) {
-			log(LOG_ERR,"!Error " + e + " creating listening socket on port "
-				+ port);
-			return 0;
-		}
-	}
-	else {
-		newsock = new Socket();
-		if(!newsock.bind(port,server.interface_ip_address)) {
-			log(LOG_ERR,"!Error " + newsock.error + " binding socket to TCP port "
-				+ port);
-			return 0;
-		}
-		log(format("%04u ",newsock.descriptor)
-			+ "IRC server socket bound to TCP port " + port);
-		if(!newsock.listen(5 /* backlog */)) {
-			log(LOG_ERR,"!Error " + newsock.error
-				+ " setting up socket for listening");
-			return 0;
-		}
-	}
-	return newsock;
-}
-
-function check_qwk_passwd(qwkid,password) {
-	if (!password || !qwkid)
-		return 0;
-	qwkid = qwkid.toUpperCase();
-	var usernum = system.matchuser(qwkid);
-	var bbsuser = new User(usernum);
-	if ((password.toUpperCase() ==
-	     bbsuser.security.password.toUpperCase()) &&
-	    (bbsuser.security.restrictions&UFLAG_Q) )
-		return 1;
-	return 0;
-}
-function IRCClient_netsplit(ns_reason) {
-	if (!ns_reason)
-		ns_reason = "net.split.net.split net.split.net.split";
-	for (sqclient in Users) {
-		if (Users[sqclient] &&
-		    (Users[sqclient].servername == this.nick)
-		   )
-			Users[sqclient].quit(ns_reason,true,true);
-	}
-	for (sqserver in Servers) {
-		if (Servers[sqserver] &&
-		    (Servers[sqserver].linkparent == this.nick)
-		   )
-			Servers[sqserver].quit(ns_reason,true,true);
-	}
-}
-
-function IRCClient_RMChan(rmchan_obj) {
-	if (!rmchan_obj)
-		return 0;
-	if (rmchan_obj.users[this.id])
-		delete rmchan_obj.users[this.id];
-	if (this.channels[rmchan_obj.nam.toUpperCase()])
-		delete this.channels[rmchan_obj.nam.toUpperCase()];
-	delete rmchan_obj.modelist[CHANMODE_OP][this.id];
-	delete rmchan_obj.modelist[CHANMODE_VOICE][this.id];
-	if (!true_array_len(rmchan_obj.users))
-		delete Channels[rmchan_obj.nam.toUpperCase()];
-}
-
-//////////////////// Output Helper Functions ////////////////////
-function rawout(str) {
-	var sendconn;
-	var str_end;
-	var str_beg;
-
-	if (debug)
-		log(format("[RAW->%s]: %s",this.nick,str));
-
-	if (this.local) {
-		sendconn = this;
-	} else if (!this.local) {
-		if ((str[0] == ":") && str[0].match(["!"])) {
-			str_end = str.slice(str.indexOf(" ")+1);
-			str_beg = str.slice(0,str.indexOf("!"));
-			str = str_beg + " " + str_end;
-		}
-		sendconn = Servers[this.parent.toLowerCase()];
-	} else {
-		log(LOG_ERR,"!ERROR: No connection to send to?");
-		return 0;
-	}
-
-	sendconn.sendq.add(str);
-}
-
-function originatorout(str,origin) {
-	var send_data;
-	var sendconn;
-
-	if (debug)
-		log(format("[%s->%s]: %s",origin.nick,this.nick,str));
-
-	sendconn = this;
-	if(this.local && !this.server) {
-		if (origin.server)
-			send_data = ":" + origin.nick + " " + str;
-		else
-			send_data = ":" + origin.nuh + " " + str;
-	} else if (this.server) {
-		send_data = ":" + origin.nick + " " + str;
-	} else if (!this.local) {
-		sendconn = Servers[this.parent.toLowerCase()];
-		send_data = ":" + origin.nick + " " + str;
-	} else {
-		log(LOG_ERR,"!ERROR: No connection to send to?");
-		return 0;
-	}
-
-	sendconn.sendq.add(send_data);
-}
-
-function ircout(str) {
-	var send_data;
-	var sendconn;
-
-	if (debug)
-		log(format("[%s->%s]: %s",servername,this.nick,str));
-
-	if(this.local) {
-		sendconn = this;
-	} else if (this.parent) {
-		sendconn = Servers[this.parent.toLowerCase()];
-	} else {
-		log(LOG_ERR,"!ERROR: No socket to send to?");
-		return 0;
-	}
-
-	send_data = ":" + servername + " " + str;
-	sendconn.sendq.add(send_data);
-}
-
-function Queue_Recv(sock) {
-	var pos;
-	var cmd;
-	var str;
-
-	str = sock.recv(65536,0);
-	if (str !== null && str.length > 0) {
-		this._recv_bytes += str;
-		while ((pos = this._recv_bytes.search('\n')) != -1) {
-			cmd = this._recv_bytes.substr(0, pos);
-			this._recv_bytes = this._recv_bytes.substr(pos+1);
-			if (cmd[cmd.length-1] == '\r')
-				cmd = cmd.substr(0, cmd.length - 1);
-			this.add(cmd);
-		}
-	}
-}
-
-function Queue_Send(sock) {
-	var sent;
-	var oldnb;
-
-	if (this.queue.length) {
-		this._send_bytes += this.queue.join('\r\n')+'\r\n';
-		this.queue = [];
-	}
-	if (this._send_bytes.length) {
-		if (new_socket_send === undefined || new_socket_send === false) {
-			oldnb = sock.nonblocking;
-			sock.nonblocking = false;
-		}
-		sent = sock.send(this._send_bytes);
-		if (new_socket_send === undefined) {
-			if (sent === true)
-				new_socket_send = false;
-			else if (sent === false)
-				new_socket_send = false;
-			else {
-				new_socket_send = true;
-				sock.nonblocking = oldnb;
-			}
-		}
-		if (new_socket_send === false) {
-			sock.nonblocking = oldnb;
-			this._send_bytes = '';
-		}
-		if (new_socket_send === true) {
-			if (sent > 0)
-				this._send_bytes = this._send_bytes.substr(sent);
-		}
-	}
-}
-
-function Queue_Prepend(str) {
-	this.queue.unshift(str);
-}
-
-function Queue_Add(str) {
-	this.queue.push(str);
-}
-
-function Queue_Del() {
-	return this.queue.shift();
-}
-
-function IRCClient_server_notice(str) {
-	this.ircout("NOTICE " + this.nick + " :" + str);
-}
-
-function IRCClient_numeric(num, str) {
-	this.ircout(num + " " + this.nick + " " + str);
-}
-
-//////////////////// Numeric Functions ////////////////////
-function IRCClient_numeric200(dest,next) {
-	this.numeric(200, "Link " + VERSION + " " + dest + " " + next);
-}
-
-function IRCClient_numeric201(ircclass,server) {
-	this.numeric(201, "Try. " + ircclass + " " + server);
-}
-
-function IRCClient_numeric202(ircclass,server) {
-	this.numeric(202, "H.S. " + ircclass + " " + server);
-}
-
-function IRCClient_numeric203(ircclass,ip) {
-	this.numeric(203, "???? " + ircclass + " [" + ip + "]");
-}
-
-function IRCClient_numeric204(nick) {
-	this.numeric(204, "Oper " + nick.ircclass + " " + nick.nick);
-}
-
-function IRCClient_numeric205(nick) {
-	this.numeric(205, "User " + nick.ircclass + " " + nick.nick);
-}
-
-function IRCClient_numeric206(ircclass,sint,cint,server) {
-	this.numeric(206, "Serv " + ircclass + " " + sint + "S " + cint
-		+ "C *!*@" + server);
-}
-
-function IRCClient_numeric208(type,clientname) {
-	this.numeric(208, type + " 0 " + clientname);
-}
-
-function IRCClient_numeric261(file) {
-	this.numeric(261, "File " + file + " " + debug);
-}
-
-function IRCClient_numeric321() {
-	this.numeric("321", "Channel :Users  Name");
-}
-
-function IRCClient_numeric322(chan,show_modes) {
-	var channel_name;
-	var disp_topic = "";
-	var is_onchan = this.channels[chan.nam.toUpperCase()];
-
-	if (show_modes) {
-		var chanm = chan.chanmode()
-		disp_topic += "[" + chanm.slice(0,chanm.length-1) + "]"
-	}
-
-	if ((chan.mode&CHANMODE_PRIVATE) && !(this.mode&USERMODE_OPER) &&
-	    !is_onchan ) {
-		channel_name = "*";
-	} else {
-		channel_name = chan.nam;
-		if (disp_topic)
-			disp_topic += " ";
-		disp_topic += chan.topic;
-	}
-	if (!(chan.mode&CHANMODE_SECRET) || (this.mode&USERMODE_OPER) ||
-	    is_onchan )
-		this.numeric(322, channel_name + " " + true_array_len(chan.users)
-			+ " :" + disp_topic);
-}
-
-function IRCClient_numeric331(chan) {
-	this.numeric(331, chan.nam + " :No topic is set.");
-}
-
-function IRCClient_numeric332(chan) {
-	this.numeric(332, chan.nam + " :" + chan.topic);
-}
-
-function IRCClient_numeric333(chan) {
-	this.numeric(333, chan.nam + " " + chan.topicchangedby + " "
-		+ chan.topictime);
-}
-
-function IRCClient_numeric351() {
-	this.numeric(351, VERSION + " " + servername + " :" + VERSION_STR);
-}
-
-function IRCClient_numeric352(user,show_ips_only,chan) {
-	var who_mode="";
-	var disp;
-	var disphost;
-
-	if (!user)
-		return 0;
-
-	if (!chan)
-		disp = "*";
-	else
-		disp = chan.nam;
-
-	if (user.away)
-		who_mode += "G";
-	else
-		who_mode += "H";
-	if (chan) {
-		if (chan.modelist[CHANMODE_OP][user.id])
-			who_mode += "@";
-		else if (chan.modelist[CHANMODE_VOICE][user.id])
-			who_mode += "+";
-	}
-	if (user.mode&USERMODE_OPER)
-		who_mode += "*";
-
-	if (show_ips_only)
-		disphost = user.ip;
-	else
-		disphost = user.hostname;
-
-	this.numeric(352, disp + " " + user.uprefix + " " + disphost + " "
-		+ user.servername + " " + user.nick + " " + who_mode
-		+ " :" + user.hops + " " + user.realname);
-	return 1;
-}
-
-function IRCClient_numeric353(chan, str) {
-	// = public @ secret * everything else
-	if (chan.mode&CHANMODE_SECRET)
-		var ctype_str = "@";
-	else
-		var ctype_str = "=";
-	this.numeric("353", ctype_str + " " + chan.nam + " :" + str);
-}
-
-function IRCClient_numeric382(str) {
-	this.numeric(382, "ircd.conf :" + str);
-}
-
-function IRCClient_numeric391() {
-	this.numeric(391, servername + " :"
-		+ strftime("%A %B %d %Y -- %H:%M %z",time()));
-}
-
-function IRCClient_numeric401(str) {
-	this.numeric("401", str + " :No such nick/channel.");
-}
-
-function IRCClient_numeric402(str) {
-	this.numeric(402, str + " :No such server.");
-}
-
-function IRCClient_numeric403(str) {
-	this.numeric("403", str
-		+ " :No such channel or invalid channel designation.");
-}
-
-function IRCClient_numeric411(str) {
-	this.numeric("411", ":No recipient given. (" + str + ")");
-}
-
-function IRCClient_numeric412() {
-	this.numeric(412, " :No text to send.");
-}
-
-function IRCClient_numeric440(str) {
-	this.numeric(440, str + " :Services is currently down, sorry.");
-}
-
-function IRCClient_numeric441(str) {
-	this.numeric("441", str + " :They aren't on that channel.");
-}
-
-function IRCClient_numeric442(str) {
-	this.numeric("442", str + " :You're not on that channel.");
-}
-
-function IRCClient_numeric445() {
-	this.numeric(445, ":SUMMON has been disabled.");
-}
-
-function IRCClient_numeric446() {
-	this.numeric(446, ":USERS has been disabled.");
-}
-
-function IRCClient_numeric451() {
-	this.numeric("451", ":You have not registered.");
-}
-
-function IRCClient_numeric461(cmd) {
-	this.numeric("461", cmd + " :Not enough parameters.");
-}
-
-function IRCClient_numeric462() {
-	this.numeric("462", ":You may not re-register.");
-}
-
-function IRCClient_numeric481() {
-	this.numeric("481", ":Permission Denied.  "
-		+ "You do not have the correct IRC operator privileges.");
-}
-
-function IRCClient_numeric482(tmp_chan_nam) {
-	this.numeric("482", tmp_chan_nam + " :You're not a channel operator.");
-}
-
-//////////////////// Multi-numeric Display Functions ////////////////////
-
-function IRCClient_lusers() {
-	this.numeric(251, ":There are " + num_noninvis_users() + " users and "
-		+ num_invis_users() + " invisible on " + true_array_len(Servers)
-		+ " servers.");
-	this.numeric(252, num_opers() + " :IRC operators online.");
-	var unknown_connections = true_array_len(Unregistered);
-	if (unknown_connections)
-		this.numeric(253, unknown_connections + " :unknown connection(s).");
-	this.numeric(254, true_array_len(Channels) + " :channels formed.");
-	this.numeric(255, ":I have " + true_array_len(Local_Users)
-		+ " clients and " + true_array_len(Local_Servers) + " servers.");
-	this.numeric(250, ":Highest connection count: " + hcc_total + " ("
-		+ hcc_users + " clients.)");
-	this.server_notice(hcc_counter + " clients have connected since "
-		+ strftime("%a %b %d %H:%M:%S %Y %Z",server_uptime));
-}
-
-function num_noninvis_users() {
-	var counter = 0;
-	for(myuser in Users) {
-		if (!(Users[myuser].mode&USERMODE_INVISIBLE))
-			counter++;
-	}
-	return counter;
-}
-
-function num_invis_users() {
-	var counter = 0;
-	for(myuser in Users) {
-		if (Users[myuser].mode&USERMODE_INVISIBLE)
-			counter++;
-	}
-	return counter;
-}
-
-function num_opers() {
-	var counter = 0;
-	for(myuser in Users) {
-		if (Users[myuser].mode&USERMODE_OPER)
-			counter++;
-	}
-	return counter;
-}
-
-function IRCClient_motd() {
-	var motd_file = new File(system.text_dir + "ircmotd.txt");
-	if (motd_file.exists==false) {
-		this.numeric(422, ":MOTD file missing: " + motd_file.name);
-	} else if (motd_file.open("r")==false) {
-		this.numeric(424, ":MOTD error " + errno + " opening: "
-			+ motd_file.name);
-	} else {
-		this.numeric(375, ":- " + servername + " Message of the Day -");
-		this.numeric(372, ":- " + strftime("%m/%d/%Y %H:%M",motd_file.date));
-		while (!motd_file.eof) {
-			motd_line = motd_file.readln();
-			if (motd_line != null)
-				this.numeric(372, ":- " + motd_line);
-		}
-		motd_file.close();
-	}
-	this.numeric(376, ":End of /MOTD command.");
-}
-
-function IRCClient_names(chan) {
-	var Channel_user;
-	var numnicks=0;
-	var tmp="";
-	for(thisChannel_user in chan.users) {
-		Channel_user=chan.users[thisChannel_user];
-		if (!(Channel_user.mode&USERMODE_INVISIBLE) ||
-		     (this.channels[chan.nam.toUpperCase()]) ) {
-			if (numnicks)
-				tmp += " ";
-			if (chan.modelist[CHANMODE_OP][Channel_user.id])
-				tmp += "@";
-			else if (chan.modelist[CHANMODE_VOICE][Channel_user.id])
-				tmp += "+";
-			tmp += Channel_user.nick;
-			numnicks++;
-			if (numnicks >= 59) {
-				// dump what we've got, it's too much.
-				this.numeric353(chan, tmp);
-				numnicks=0;
-				tmp="";
-			}
-		}
-	}
-	if(numnicks)
-		this.numeric353(chan, tmp);
-}
-
-// Traverse each channel the user is on and see if target is on any of the
-// same channels.
-function IRCClient_onchanwith(target) {
-	for (c in this.channels) {
-		for (i in target.channels) {
-			if (c == i)
-				return 1; // success
-		}
-	}
-	return 0; // failure.
-}
-
-//////////////////// Auxillary Functions ////////////////////
-
-function IRCClient_bcast_to_uchans_unique(str) {
-	var already_bcast = new Object;
-	for(thisChannel in this.channels) {
-		var userchannel=this.channels[thisChannel];
-		for (j in userchannel.users) {
-			var usr = userchannel.users[j];
-			if (!already_bcast[usr.nick] && (usr.id != this.id) && usr.local) {
-				usr.originatorout(str,this);
-				already_bcast[usr.nick] = true;
-			}
-		}
-	}
-}
-
-function IRCClient_bcast_to_list(chan, str, bounce, list_bit) {
-	for (thisUser in chan.users) {
-		var aUser = chan.users[thisUser];
-		if (aUser && ( aUser.id != this.id || (bounce) ) &&
-		    chan.modelist[list_bit][aUser.id])
-			aUser.originatorout(str,this);
-	}
-}
-
-function IRCClient_bcast_to_channel(chan, str, bounce) {
-	for(thisUser in chan.users) {
-		var aUser=chan.users[thisUser];
-		if ( ( aUser.id != this.id || (bounce) ) &&
-		     aUser.local )
-			aUser.originatorout(str,this);
-	}
-}
-
-function IRCClient_bcast_to_channel_servers(chan, str) {
-	var sent_to_servers = new Object;
-	for(thisUser in chan.users) {
-		var aUser=chan.users[thisUser];
-		if (!aUser.local && (this.parent != aUser.parent) &&
-		    !sent_to_servers[aUser.parent.toLowerCase()]) {
-			aUser.originatorout(str,this);
-			sent_to_servers[aUser.parent.toLowerCase()] = true;
-		}
-	}
-}
-
-function IRCClient_check_nickname(newnick,squelch) {
-	var qline_nick;
-	var checknick;
-	var regexp;
-
-	newnick = newnick.slice(0,max_nicklen);
-	// If you're trying to NICK to yourself, drop silently.
-	if(newnick == this.nick)
-		return -1;
-	// First, check for valid nick against irclib.
-	if(IRC_check_nick(newnick)) {
-		if (!squelch)
-			this.numeric("432", newnick + " :Foobar'd Nickname.");
-		return 0;
-	}
-	// Second, check for existing nickname.
-	checknick = Users[newnick.toUpperCase()];
-	if(checknick && (checknick.nick != this.nick) ) {
-		if (!squelch)
-			this.numeric("433", newnick + " :Nickname is already in use.");
-		return 0;
-	}
-	// Third, match against Q:Lines
-	for (ql in QLines) {
-		if(wildmatch(newnick, QLines[ql].nick)) {
-			if (!squelch)
-				this.numeric(432, newnick + " :" + QLines[ql].reason);
-			return 0;
-		}
-	}
-	return 1; // if we made it this far, we're good!
-}
-
-function IRCClient_do_whois(wi) {
-	if (!wi)
-		return 0;
-	this.numeric(311, wi.nick + " " + wi.uprefix + " " + wi.hostname + " * :"
-		+ wi.realname);
-	var userchans="";
-	for (i in wi.channels) {
-		var chan = wi.channels[i];
-		if (!(chan.mode&CHANMODE_SECRET||chan.mode&CHANMODE_PRIVATE) ||
-		     this.channels[chan.nam.toUpperCase()] || this.mode&USERMODE_OPER) {
-			if (userchans)
-				userchans += " ";
-			if (chan.modelist[CHANMODE_OP][wi.id])
-				userchans += "@";
-			else if (chan.modelist[CHANMODE_VOICE][wi.id])
-				userchans += "+";
-			userchans += chan.nam;
-		}
-	}
-	if (userchans)
-		this.numeric(319, wi.nick + " :" + userchans);
-	if (wi.local)
-		this.numeric(312, wi.nick + " " + servername + " :" + serverdesc);
-	else {
-		wi_server = searchbyserver(wi.servername);
-		this.numeric(312, wi.nick + " " + wi_server.nick
-		+ " :" + wi_server.info);
-	}
-	if (wi.mode&USERMODE_OPER)
-		this.numeric(313, wi.nick + " :is an IRC operator.");
-	if (wi.away)
-		this.numeric(301, wi.nick + " :" + wi.away);
-	if (wi.local)
-		this.numeric(317, wi.nick + " " + (time() - wi.talkidle) + " "
-		+ wi.connecttime + " :seconds idle, signon time");
-}
-
-function IRCClient_services_msg(svcnick,send_str) {
-	var service_server;
-
-	if (!send_str) {
-		this.numeric412();
-		return 0;
-	}
-	// First, make sure the nick exists.
-	var usr = Users[svcnick.toUpperCase()];
-	if (!usr) {
-		this.numeric440(svcnick);
-		return 0;
-	}
-	service_server = searchbyserver(usr.servername);
-	if (!service_server || !service_server.uline) {
-		this.numeric440(svcnick);
-		return 0;
-	}
-	this.do_msg(svcnick,"PRIVMSG",send_str);
-}
-
-function IRCClient_global(target,type_str,send_str) {
-	if (!target.match("[.]")) {
-		this.numeric(413,target + " :No top-level domain specified.");
-		return 0;
-	}
-	var domain_part = target.split('.');
-	if (domain_part[domain_part.length - 1].match("[?*]")) {
-		this.numeric(414,target + " :Wildcard found in top-level domain.");
-		return 0;
-	}
-	var global_mask = target.slice(1);
-	var global_str = type_str + " " + target + " :" + send_str;
-	for(globClient in Local_Users) {
-		var Client = Local_Users[globClient];
-		if (target[0] == "#")
-			var global_match = Client.hostname;
-		else // assume $
-			var global_match = Client.servername;
-		if (wildmatch(global_match,global_mask))
-			Client.originatorout(global_str,this);
-	}
-	global_str = ":" + this.nick + " " + global_str;
-	if(this.local && this.parent) /* Incoming from a local server */
-		Servers[this.parent.toLowerCase()].bcast_to_servers_raw(global_str);
-	else if (this.flags&OLINE_CAN_GGNOTICE) /* From a local oper */
-		server_bcast_to_servers(global_str);
-	return 1;
-}
-
-function IRCClient_globops(str) {
-	var globops_bits = 0;
-	globops_bits |= USERMODE_OPER;
-	globops_bits |= USERMODE_GLOBOPS;
-	umode_notice(globops_bits,"Global","from " + this.nick +": " + str);
-	if (this.parent)
-		Servers[this.parent.toLowerCase()].bcast_to_servers_raw(":" + this.nick
-			+ " GLOBOPS :" + str);
-	else
-		server_bcast_to_servers(":" + this.nick + " GLOBOPS :" + str);
-}
-
-function IRCClient_do_msg(target,type_str,send_str) {
-	if ( (target[0] == "$") && (this.mode&USERMODE_OPER) &&
-	     ( (this.flags&OLINE_CAN_LGNOTICE) || !this.local)
-	   )
-		return this.global(target,type_str,send_str);
-
-	var send_to_list = -1;
-	if (target[0] == "@" && ( (target[1] == "#") || target[1] == "&") ) {
-		send_to_list = CHANMODE_OP;
-		target = target.slice(1);
-	} else if (target[0]=="+" && ((target[1] == "#")|| target[1] == "&")) {
-		send_to_list = CHANMODE_VOICE;
-		target = target.slice(1);
-	}
-		
-	if ((target[0] == "#") || (target[0] == "&")) {
-		var chan = Channels[target.toUpperCase()];
-		if (!chan) {
-			// check to see if it's a #*hostmask* oper message
-			if ( (target[0] == "#") && (this.mode&USERMODE_OPER) &&
-			     ( (this.flags&OLINE_CAN_LGNOTICE) || !this.local )
-			   ) {
-				return this.global(target,type_str,send_str);
-			} else {
-				this.numeric401(target);
-				return 0;
-			}
-		}
-		if ((chan.mode&CHANMODE_NOOUTSIDE)
-			&& !this.channels[chan.nam.toUpperCase()]) {
-			this.numeric(404, chan.nam + " :Cannot send to channel "
-				+ "(+n: no outside messages)");
-			return 0;
-		}
-		if ( (chan.mode&CHANMODE_MODERATED) &&
-		     !chan.modelist[CHANMODE_VOICE][this.id] &&
-		     !chan.modelist[CHANMODE_OP][this.id] ) {
-			this.numeric(404, chan.nam + " :Cannot send to channel "
-				+ "(+m: moderated)");
-			return 0;
-		}
-		if (chan.isbanned(this.nuh) &&
-		   !chan.modelist[CHANMODE_VOICE][this.id] &&
-		   !chan.modelist[CHANMODE_OP][this.id] ) {
-			this.numeric(404, chan.nam + " :Cannot send to channel "
-				+ "(+b: you're banned!)");
-			return 0;
-		}
-		if(send_to_list == -1) {
-			var str = type_str +" "+ chan.nam +" :"+ send_str;
-			this.bcast_to_channel(chan, str, false);
-			this.bcast_to_channel_servers(chan, str);
-		} else {
-			var prefix_chr;
-			if (send_to_list == CHANMODE_OP)
-				prefix_chr="@";
-			else if (send_to_list == CHANMODE_VOICE)
-				prefix_chr="+";
-			var str = type_str +" " + prefix_chr + chan.nam + " :"+ send_str;
-			this.bcast_to_list(chan, str, false, send_to_list);
-			this.bcast_to_channel_servers(chan, str);
-		}
-	} else {
-		if (target.match("[@]")) {
-			var msg_arg = target.split('@');
-			var real_target = msg_arg[0];
-			var target_server = searchbyserver(msg_arg[1]);
-			if (!target_server) {
-				this.numeric401(target);
-				return 0;
-			}
-			if (target_server == -1) {
-				this.numeric(407, target
-					+ " :Duplicate recipients, no message delivered.");
-				return 0;
-			}
-			target = msg_arg[0] + "@" + msg_arg[1];
-		} else {
-			var real_target = target;
-		}
-		var target_socket = Users[real_target.toUpperCase()];
-		if (target_socket) {
-			if (target_server &&
-			    (target_server.parent != target_socket.parent)) {
-				this.numeric401(target);
-				return 0;
-			}
-			if (target_server && 
-			    (target_server.id == target_socket.parent) )
-				target = real_target;
-			if (target_socket.issilenced(this.nuh))
-				return 0;	/* On SILENCE list.  Silently ignore. */
-			var str = type_str + " " + target + " :" + send_str;
-			target_socket.originatorout(str,this);
-			if (target_socket.away && (type_str == "PRIVMSG") &&
-			    !this.server && target_socket.local)
-				this.numeric(301, target_socket.nick + " :"
-					+ target_socket.away);
-		} else {
-			this.numeric401(target);
-			return 0;
-		}
-	}
-	return 1;
-}
-
-function IRCClient_do_admin() {
-	umode_notice(USERMODE_SPY,"ADMIN requested by " + this.nick + " (" +
-		this.uprefix + "@" + this.hostname + ") [" + this.servername +
-		"]");
-	if (Admin1 && Admin2 && Admin3) {
-		this.numeric(256, ":Administrative info about " + servername);
-		this.numeric(257, ":" + Admin1);
-		this.numeric(258, ":" + Admin2);
-		this.numeric(259, ":" + Admin3);
-	} else {
-		this.numeric(423, servername
-			+ " :No administrative information available.");
-	}
-}
-
-function IRCClient_do_info() {
-	umode_notice(USERMODE_SPY,"Spy","INFO requested by " + this.nick +
-		" (" + this.uprefix + "@" + this.hostname + ") [" +
-		this.servername + "]");
-	this.numeric(371, ":--=-=-=-=-=-=-=-=-=*[ The Synchronet IRCd v1.3a ]*=-=-=-=-=-=-=-=-=--");
-	this.numeric(371, ":  IRCd Copyright 2003-2009 by Randolph E. Sommerfeld <cyan@rrx.ca>");
-	this.numeric(371, ":" + system.version_notice + " " + system.copyright + ".");
-	this.numeric(371, ":--=-=-=-=-=-=-=-=-( A big thanks to the following )-=-=-=-=-=-=-=-=--");
-	this.numeric(371, ":DigitalMan (Rob Swindell): Resident coder god, various hacking all");
-	this.numeric(371, ":   around the IRCd, countless helpful suggestions and tips, and");
-	this.numeric(371, ":   tons of additions to the Synchronet JS API that made this possible.");
-	this.numeric(371, ":Deuce (Stephen Hurd): Resident Perl guru and ex-Saskatchewan zealot.");
-	this.numeric(371, ":   Originally converted the IRCd to be object-oriented, various small");
-	this.numeric(371, ":   hacks, and lots of guidance.");
-	this.numeric(371, ":Thanks to the DALnet Bahamut team for their help from time to time.");
-	this.numeric(371, ":Greets to: Arrak, ElvishMerchant, Foobar, Grimp, Kufat,");
-	this.numeric(371, ":   Psyko, Samael, Shaun, Torke, and all the #square oldbies.");
-	this.numeric(371, ":--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--");
-	this.numeric(371, ":Synchronet " + system.full_version);
-	this.numeric(371, ":Compiled with " + system.compiled_with + " at " + system.compiled_when);
-	this.numeric(371, ":Running on " + system.os_version);
-	this.numeric(371, ":Utilizing socket library: " + system.socket_lib);
-	this.numeric(371, ":Javascript library: " + system.js_version);
-	this.numeric(371, ":This BBS has been up since " + system.timestr(system.uptime));
-	this.numeric(371, ": -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -");
-	if (server.version_detail!=undefined) {
-		this.numeric(371, ":This IRCd was executed via:");
-		this.numeric(371, ":" + server.version_detail);
-	}
-	this.numeric(371, ":IRCd CVS revisions:")
-	this.numeric(371, ":Main(" + MAIN_REVISION + ") User(" + USER_REVISION + ") Channel(" + CHANNEL_REVISION + ") Server(" + SERVER_REVISION + ") Unreg(" + UNREG_REVISION + ")");
-	this.numeric(371, ":IRClib Version: " + IRCLIB_VERSION);
-	this.numeric(371, ":--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--");
-	this.numeric(371, ":This program is distributed under the terms of the GNU General Public");
-	this.numeric(371, ":License, version 2.  http://www.gnu.org/licenses/gpl.txt");
-	this.numeric(371, ":--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--");
-	this.numeric(374, ":End of /INFO list.");
-}
-
-function IRCClient_do_stats(statschar) {
-	switch(statschar[0]) {
-		case "C":
-		case "c":
-			var cline_port;
-			for (cline in CLines) {
-				if(CLines[cline].port)
-					cline_port = CLines[cline].port;
-				else
-					cline_port = "*";
-				this.numeric(213,"C " + CLines[cline].host + " * "
-					+ CLines[cline].servername + " " + cline_port + " "
-					+ CLines[cline].ircclass);
-				if (NLines[cline])
-					this.numeric(214,"N " + NLines[cline].host + " * "
-						+ NLines[cline].servername + " " + NLines[cline].flags
-						+ " " + NLines[cline].ircclass);
-			}
-			break;  
-		case "H":
-		case "h":
-			for (hl in HLines) {
-				this.numeric(244,"H " + HLines[hl].allowedmask + " * "
-					+ HLines[hl].servername);
-			}
-			break;
-		case "I":
-		case "i":
-			var my_port;
-			for (iline in ILines) {
-				if (!ILines[iline].port)
-					my_port = "*";
-				else
-					my_port = ILines[iline].port;
-				this.numeric(215,"I " + ILines[iline].ipmask + " * "
-					+ ILines[iline].hostmask + " " + my_port + " "
-					+ ILines[iline].ircclass);
-			}
-			break;
-		case "K":
-		case "k":
-			for (kline in KLines) {
-				if (KLines[kline].hostmask) {
-					this.numeric(216, KLines[kline].type + " "
-						+ KLines[kline].hostmask + " * * :"
-						+ KLines[kline].reason);
-				}
-			}
-			break;
-		case "L": /* FIXME */
-			this.numeric(241,"L <hostmask> * <servername> <maxdepth>");
-			break;
-		case "l": /* FIXME */
-			this.numeric(211,"<linkname> <sendq> <sentmessages> <sentbytes> <receivedmessages> <receivedbytes> <timeopen>");
-			break;
-		case "M":
-		case "m":
-			for (c in Profile) {
-				var sm = Profile[c];
-				this.numeric(212, c + " " + sm.ticks + " " + sm.executions);
-			}
-			break;          
-		case "O":       
-		case "o":
-			for (oline in OLines) {
-				this.numeric(243, "O " + OLines[oline].hostmask + " * "
-					+ OLines[oline].nick);
-			}
-			break;
-		case "U":
-			for (uline in ULines) {
-				this.numeric(246, "U " + ULines[uline] + " * * 0 -1");                  
-			}       
-			break;
-		case "u":
-			var this_uptime=time() - server_uptime;
-			var updays=Math.floor(this_uptime / 86400);
-			if (updays)
-				this_uptime %= 86400;
-			var uphours=Math.floor(this_uptime/(60*60));
-			var upmins=(Math.floor(this_uptime/60))%60;
-			var upsec=this_uptime%60;
-			var str = format("Server Up %u days, %u:%02u:%02u",
-				updays,uphours,upmins,upsec);
-			this.numeric(242,":" + str);
-			break;
-		case "Y":
-		case "y":
-			var yl;
-			for (thisYL in YLines) {
-				yl = YLines[thisYL];
-				this.numeric(218,"Y " + thisYL + " " + yl.pingfreq + " "
-					+ yl.connfreq + " " + yl.maxlinks + " " + yl.sendq);
-			}
-			break;
-		default:
-			break;
-	}
-	this.numeric(219, statschar[0] + " :End of /STATS Command.");
-}
-
-function IRCClient_do_users() {
-	var usersshown;
-	var u;
-
-	this.numeric(392,':UserID                    Terminal  Host');
-	usersshown=0;
-	for(node in system.node_list) {
-		if(system.node_list[node].status == NODE_INUSE) {
-			u=new User(system.node_list[node].useron);
-			this.numeric(393,format(':%-25s %-9s %-30s',u.alias,'Node'+node,
-				u.host_name));
-			usersshown++;
-		}
-	}
-	if(usersshown) {
-		this.numeric(394,':End of users');
-	} else {
-		this.numeric(395,':Nobody logged in');
-	}
-}
-
-function IRCClient_do_summon(summon_user) {
-	var usernum;
-	var isonline;
-
-	// Check if exists.
-	usernum = system.matchuser(summon_user);
-	if(!usernum)
-		this.numeric(444,":No such user.");
-	else {
-		// Check if logged in
-		isonline = 0;
-		for(node in system.node_list) {
-			if( (system.node_list[node].status == NODE_INUSE)
-				&& (system.node_list[node].useron == usernum) ) {
-				isonline = 1;
-				break;
-			}
-		}
-		if(!isonline) {
-			this.numeric(444,":User not logged in.");
-		} else {
-			system.put_telegram(usernum, "" + this.nick
-				+ " is summoning you to IRC chat.\r\n");
-			this.numeric(342, summon_user + " :Summoning user to IRC");
-		}
-	}
-}
-
-function IRCClient_do_links(mask) {
-	if (!mask)
-		mask = "*";
-	umode_notice(USERMODE_SPY,"Spy","LINKS " + mask + " requested by " +
-		this.nick + " (" + this.uprefix + "@" + this.hostname + ") [" +
-		this.servername + "]");
-	for(thisServer in Servers) {
-		var Server=Servers[thisServer];
-		if (wildmatch(Server.nick,mask)) {
-			this.numeric(364, Server.nick + " " + Server.linkparent + " :"
-				+ Server.hops + " " + Server.info);
-		}
-	}
-	if (wildmatch(servername,mask))
-		this.numeric(364, servername + " " + servername + " :0 " + serverdesc);
-	this.numeric(365, mask + " :End of /LINKS list.");
-}
-
-// Don't hunt for servers based on nicknames, as TRACE is more explicit.
-function IRCClient_do_trace(target) {
-	var server = searchbyserver(target);
-
-	if (server == -1) { // we hunted ourselves
-		// FIXME: What do these numbers mean? O_o?
-		this.numeric206("30","1","0",servername);
-		this.trace_all_opers();
-	} else if (server) {
-		server.rawout(":" + this.nick + " TRACE " + target);
-		this.numeric200(target,server.nick);
-		return 0;
-	} else {
-		// Okay, we've probably got a user.
-		var nick = Users[target.toUpperCase()];
-		if (!nick) {
-			this.numeric402(target);
-			return 0;
-		} else if (nick.local) {
-			if (nick.mode&USERMODE_OPER)
-				this.numeric204(nick);
-			else
-				this.numeric205(nick);
-		} else {
-			nick.rawout(":" + this.nick + " TRACE " + target);
-			this.numeric200(target,Servers[nick.parent.toLowerCase()].nick);
-		}
-	}
-	this.numeric(262, target + " :End of /TRACE.");
-}
-
-function IRCClient_trace_all_opers() {
-	for(thisoper in Local_Users) {
-		var oper=Local_Users[thisoper];
-		if (oper.mode&USERMODE_OPER)
-			this.numeric204(oper);
-	}
-}
-
-function IRCClient_do_connect(con_server,con_port) {
-	var con_cline = "";
-	for (ccl in CLines) {
-		if (wildmatch(CLines[ccl].servername,con_server) ||
-		    wildmatch(CLines[ccl].host,con_server) ) {
-			con_cline = CLines[ccl];
-			break;
-		}
-	}
-	if (!con_cline) {
-		this.numeric402(con_server);
-		return 0;
-	}
-	if (!con_port && con_cline.port)
-		con_port = con_cline.port;
-	if (!con_port && !con_cline.port)
-		con_port = String(default_port);
-	if (!con_port.match(/^[0-9]+$/)) {
-		this.server_notice("Invalid port: " + con_port);
-		return 0;
-	}
-	var msg = " CONNECT " + con_cline.servername + " " + con_port +
-		" from " + this.nick + "[" + this.uprefix + "@" +
-		this.hostname + "]";
-	var con_type = "Local";
-	if (this.parent) {
-		con_type = "Remote";
-		server_bcast_to_servers("GNOTICE :Remote" + msg);
-	}
-	umode_notice(USERMODE_ROUTING,"Routing","from "+servername+": " + 
-		con_type + msg);
-	connect_to_server(con_cline,con_port);
-	return 1;
-}
-
-function IRCClient_do_basic_who(whomask) {
-	var eow = "*";
-
-	var regexp = "^[0]{1,}$";
-	if (whomask.match(regexp))
-		whomask = "*";
-
-	if ((whomask[0] == "#") || (whomask[0] == "&")) {
-		var chan = Channels[whomask.toUpperCase()];
-		if (chan && ( ( !(chan.mode&CHANMODE_SECRET) &&
-		    !(chan.mode&CHANMODE_PRIVATE) ) ||
-		    this.channels[chan.nam.toUpperCase()] || (this.mode&USERMODE_OPER))
-		   ) {
-			for(i in chan.users) {
-				var usr = chan.users[i];
-				if (!(usr.mode&USERMODE_INVISIBLE) ||
-				    (this.mode&USERMODE_OPER) ||
-				    this.onchanwith(usr) ) {
-					var chkwho = this.numeric352(usr,false,chan);
-					if (!chkwho) {
-						umode_notice(USERMODE_OPER,"Notice",
-							"WHO returned 0 for user: " + usr.nick + " (A)");
-					}
-				}
-			}
-			eow = chan.nam;
-		}
-	} else {
-		for (i in Users) {
-			var usr = Users[i];
-			if (usr.match_who_mask(whomask) &&
-			    (!(usr.mode&USERMODE_INVISIBLE) ||
-			     (this.mode&USERMODE_OPER) ||
-			     this.onchanwith(usr) ) ) {
-				var chkwho = this.numeric352(usr);
-				if (!chkwho) {
-					umode_notice(USERMODE_OPER,"Notice",
-						"WHO returned 0 for user: " + usr.nick + " (B)");
-				}
-			}
-		}
-		eow = whomask;
-	}
-	this.numeric(315, eow + " :End of /WHO list. (Basic)");
-}
-
-function IRCClient_do_complex_who(cmd) {
-	var who = new Who();
-	var tmp;
-	var eow = "*";
-	var add = true;	// assume the user is doing + by default
-	var arg = 1;
-	var whomask = "";
-	var chan;
-
-	// RFC1459 Compatibility.  "WHO <mask> o"  Only do it if we find a
-	// wildcard, otherwise assume we're doing a complex WHO with 'modes'
-	if (cmd[2] && ( (cmd[1].match(/[*]/)) || cmd[1].match(/[?]/)) ||
-	    (cmd[1].match(/[0]/)) && !cmd[3] && (cmd[2].toLowerCase() == "o")) {
-		tmp = cmd[1];
-		cmd[1] = cmd[2];
-		cmd[2] = tmp;
-	}
-
-	for (myflag in cmd[1]) {
-		switch (cmd[1][myflag]) {
-			case "+":
-				if (!add)
-					add = true;
-				break;
-			case "-":
-				if (add)
-					add = false;
-				break;
-			case "a":
-				who.tweak_mode(WHO_AWAY,add);
-				break;
-			case "c":
-				arg++;
-				if (cmd[arg]) {
-					who.tweak_mode(WHO_CHANNEL,add);
-					who.Channel = cmd[arg];
-				}
-				break;
-			case "g":
-				arg++;
-				if (cmd[arg]) {
-					who.tweak_mode(WHO_REALNAME,add);
-					who.RealName = cmd[arg];
-				}
-				break;
-			case "h":
-				arg++;
-				if (cmd[arg]) {
-					who.tweak_mode(WHO_HOST,add);
-					who.Host = cmd[arg];
-				}
-				break;
-			case "i":
-				arg++;
-				if (cmd[arg]) {
-					who.tweak_mode(WHO_IP,add);
-					who.IP = cmd[arg];
-				}
-				break;
-			case "l":
-				arg++;
-				if (cmd[arg]) {
-					who.tweak_mode(WHO_CLASS,add);
-					who.Class = parseInt(cmd[arg]);
-				}
-			case "m": // we never set -m
-				arg++;
-				if (cmd[arg]) {
-					who.tweak_mode(WHO_UMODE,true);
-					if (!add) {
-						var tmp_umode = "";
-						if ((cmd[arg][0] != "+") ||
-						    (cmd[arg][0] != "-") )
-							tmp_umode += "+";
-						tmp_umode += cmd[arg].replace(/[-]/g," ");
-						tmp_umode = tmp_umode.replace(/[+]/g,"-");
-						who.UMode = tmp_umode.replace(/[ ]/g,"+");
-					} else {
-						who.UMode = cmd[arg];
-					}
-				}
-				break;
-			case "n":
-				arg++;
-				if (cmd[arg]) {
-					who.Nick = cmd[arg];
-					who.tweak_mode(WHO_NICK,add);
-				}
-				break;
-			case "o":
-				who.tweak_mode(WHO_OPER,add);
-				break;
-			case "s":
-				arg++;
-				if (cmd[arg]) {
-					who.Server = cmd[arg];
-					who.tweak_mode(WHO_SERVER,add);
-				}
-				break;
-			case "t":
-				arg++;
-				if (cmd[arg]) {
-					who.Time = parseInt(cmd[arg]);
-					who.tweak_mode(WHO_TIME,add);
-				}
-				break;
-			case "u":
-				arg++;
-				if (cmd[arg]) {
-					who.User = cmd[arg];
-					who.tweak_mode(WHO_USER,add);
-				}
-				break;
-			case "C":
-				who.tweak_mode(WHO_FIRST_CHANNEL,add);
-				break;
-			case "M":
-				who.tweak_mode(WHO_MEMBER_CHANNEL,add);
-				break;
-			case "I":
-				who.tweak_mode(WHO_SHOW_IPS_ONLY,add);
-				break;
-			default:
-				break;
-		}
-	}
-
-	// Check to see if the user passed a generic mask to us for processing.
-	arg++;
-	if (cmd[arg])
-		whomask = cmd[arg];
-
-	var regexp = "^[0]{1,}$";
-	if (whomask.match(regexp))
-		whomask = "*";
-
-	// allow +c/-c to override.
-	if (!who.Channel && ((whomask[0] == "#") || (whomask[0] == "&")))
-		who.Channel = whomask;
-
-	// Strip off any @ or + in front of a channel and set the flags.
-	var sf_op = false;
-	var sf_voice = false;
-	var sf_done = false;
-	var tmp_wc = who.Channel;
-	for (cc in tmp_wc) {
-		switch(tmp_wc[cc]) {
-			case "@":
-				sf_op = true;
-				who.Channel = who.Channel.slice(1);
-				break;
-			case "+":
-				sf_voice = true;
-				who.Channel = who.Channel.slice(1);
-				break;
-			default: // assume we're done
-				sf_done = true;
-				break;
-		}
-		if (sf_done)
-			break;
-	}
-	delete tmp_wc; // don't need this anymore.
-
-	// Now we traverse everything and apply the criteria the user passed.
-	var who_count = 0;
-	for (who_client in Users) {
-		var wc = Users[who_client];
-		var flag_M = this.onchanwith(wc);
-
-		// Don't even bother if the target is +i and the
-		// user isn't an oper or on a channel with the target.
-		if ( (wc.mode&USERMODE_INVISIBLE) &&
-		     !(this.mode&USERMODE_OPER) &&
-		     !flag_M)
-			continue;
-
-		if ((who.add_flags&WHO_AWAY) && !wc.away)
-			continue;
-		else if ((who.del_flags&WHO_AWAY) && wc.away)
-			continue;
-		if (who.add_flags&WHO_CHANNEL) {
-			if (!wc.channels[who.Channel.toUpperCase()])
-				continue;
-			if (sf_op && Channels[who.Channel.toUpperCase()]&&
-			    !Channels[who.Channel.toUpperCase()].modelist
-			    [CHANMODE_OP][wc.id])
-				continue;
-			if(sf_voice&&Channels[who.Channel.toUpperCase()]&&
-			    !Channels[who.Channel.toUpperCase()].modelist
-			    [CHANMODE_VOICE][wc.id])
-				continue;
-		} else if (who.del_flags&WHO_CHANNEL) {	
-			if (wc.channels[who.Channel.toUpperCase()])
-				continue;
-			if (sf_op && Channels[who.Channel.toUpperCase()]&&
-			    Channels[who.Channel.toUpperCase()].modelist
-			    [CHANMODE_OP][wc.id])
-				continue;
-			if(sf_voice&&Channels[who.Channel.toUpperCase()]&&
-			    Channels[who.Channel.toUpperCase()].modelist
-			    [CHANMODE_VOICE][wc.id])
-				continue;
-		}
-		if ((who.add_flags&WHO_REALNAME) &&
-		    !wildmatch(wc.realname,who.RealName))
-			continue;
-		else if ((who.del_flags&WHO_REALNAME) &&
-		    wildmatch(wc.realname,who.RealName))
-			continue;
-		if ((who.add_flags&WHO_HOST) &&
-		    !wildmatch(wc.hostname,who.Host))
-			continue;
-		else if ((who.del_flags&WHO_HOST) &&
-		    wildmatch(wc.hostname,who.Host))
-			continue;
-		if ((who.add_flags&WHO_IP) &&
-		    !wildmatch(wc.ip,who.IP))
-			continue;
-		else if ((who.del_flags&WHO_IP) &&
-		    wildmatch(wc.ip,who.IP))
-			continue;
-		if (who.add_flags&WHO_UMODE) { // no -m
-			var sic = false;
-			var madd = true;
-			for (mm in who.UMode) {
-				switch(who.UMode[mm]) {
-					case "+":
-						if (!madd)
-							madd = true;
-						break;
-					case "-":
-						if (madd)
-							madd = false;
-						break;
-					case "o":
-					case "i":
-					case "w":
-					case "b":
-					case "g":
-					case "s":
-					case "c":
-					case "r":
-					case "k":
-					case "f":
-					case "y":
-					case "d":
-					case "n":
-					case "h":
-					case "F":
-						if (
-						   (!madd && (wc.mode&
-						   USERMODE_CHAR
-							[who.UMode[mm]])
-						   )
-						||
-						   (madd && !(wc.mode&
-						   USERMODE_CHAR
-							[who.UMode[mm]])
-						   ) )
-							sic = true;
-						break;
-					default:
-						break;
-				}
-				if (sic)
-					break;
-			}
-			if (sic)
-				continue;
-		}
-		if ((who.add_flags&WHO_NICK) &&
-		    !wildmatch(wc.nick,who.Nick))
-			continue;
-		else if ((who.del_flags&WHO_NICK) &&
-		    wildmatch(wc.nick,who.Nick))
-			continue;
-		if ((who.add_flags&WHO_OPER) &&
-		    !(wc.mode&USERMODE_OPER))
-			continue;
-		else if ((who.del_flags&WHO_OPER) &&
-		    (wc.mode&USERMODE_OPER))
-			continue;
-		if ((who.add_flags&WHO_SERVER) &&
-		    !wildmatch(wc.servername,who.Server))
-			continue;
-		else if ((who.del_flags&WHO_SERVER) &&
-		    wildmatch(wc.servername,who.Server))
-			continue;
-		if ((who.add_flags&WHO_USER) &&
-		    !wildmatch(wc.uprefix,who.User))
-			continue;
-		else if ((who.del_flags&WHO_USER) &&
-		    wildmatch(wc.uprefix,who.User))
-			continue;
-		if ((who.add_flags&WHO_MEMBER_CHANNEL) && !flag_M)
-			continue;
-		else if ((who.del_flags&WHO_MEMBER_CHANNEL) && flag_M)
-			continue;
-		if ((who.add_flags&WHO_TIME) &&
-		    ((time() - wc.connecttime) < who.Time) )
-			continue;
-		else if ((who.del_flags&WHO_TIME) &&
-		    ((time() - wc.connecttime) > who.Time) )
-			continue;
-		if ((who.add_flags&WHO_CLASS) &&
-		    (wc.ircclass != who.Class))
-			continue;
-		else if ((who.del_flags&WHO_CLASS) &&
-		    (wc.ircclass == who.Class))
-			continue;
-
-		if (whomask && !wc.match_who_mask(whomask))
-			continue;       
-
-		chan = "";
-		if ((who.add_flags&WHO_FIRST_CHANNEL) && !who.Channel) {
-			for (x in wc.channels) {
-				if (!(Channels[x].mode&CHANMODE_SECRET
-						|| Channels[x].mode&CHANMODE_PRIVATE)
-					|| this.channels[x] || this.mode&USERMODE_OPER)
-				{
-					chan = Channels[x].nam;
-					break;
-				}
-			}
-		} else if (who.Channel) {
-			chan = who.Channel;
-		}
-
-		var show_ips_only;
-		if (who.add_flags&WHO_SHOW_IPS_ONLY)
-			show_ips_only = true;
-		else
-			show_ips_only = false;
-
-		// If we made it this far, we're good.
-		if (chan && Channels[chan.toUpperCase()]) {
-			var chkwho = this.numeric352(wc,show_ips_only,
-				Channels[chan.toUpperCase()]);
-			if (!chkwho) {
-				umode_notice(USERMODE_OPER,"Notice",
-					"WHO returned 0 for user: " + wc.nick + " (C)");
-			}
-		} else {
-			var chkwho = this.numeric352(wc,show_ips_only);
-			if (!chkwho) {
-				umode_notice(USERMODE_OPER,"Notice",
-					"WHO returned 0 for user: " + wc.nick + " (D)");
-			}
-		}
-		who_count++;
-
-		if (!(this.mode&USERMODE_OPER) && (who_count >= max_who))
-			break;
-	}
-
-	if (who.Channel)
-		eow = who.Channel;
-	else if (cmd[2])
-		eow = cmd[2];
-
-	this.numeric(315, eow + " :End of /WHO list. (Complex)");
-}
-
-// Object which stores WHO bits and arguments.
-function Who() {
-	this.add_flags = 0;
-	this.del_flags = 0;
-	this.tweak_mode = Who_tweak_mode;
-	this.Channel = "";
-	this.RealName = "";
-	this.Host = "";
-	this.IP = "";
-	this.UMode = "";
-	this.Nick = "";
-	this.Server = "";
-	this.User = "";
-	this.Time = 0;
-	this.Class = 0;
-}
-
-function Who_tweak_mode(bit,add) {
-	if (add) {
-		this.add_flags |= bit;
-		this.del_flags &= ~bit;
-	} else {
-		this.add_flags &= ~bit;
-		this.del_flags |= bit;
-	}
-}
-
-// Take a generic mask in and try to figure out if we're matching a channel,
-// mask, nick, or something else.  Return 1 if the user sent to us matches it
-// in some fashion.
-function IRCClient_match_who_mask(mask) {
-	if ((mask[0] == "#") || (mask[0] == "&")) { // channel
-		if (Channels[mask.toUpperCase()])
-			return 1;
-		else
-			return 0; // channel doesn't exist.
-	} else if (mask.match(/[!]/)) { // nick!user@host
-		if ( wildmatch(this.nick,mask.split("!")[0])
-		     && wildmatch(this.uprefix,
-				mask.slice(mask.indexOf("!")+1).split("@")[0])
-		     && wildmatch(this.hostname,mask.slice(mask.indexOf("@")+1)) )
-			return 1;
-	} else if (mask.match(/[@]/)) { // user@host
-		if ( wildmatch(this.uprefix,mask.split("@")[0]) &&
-		     wildmatch(this.hostname,mask.split("@")[1]) )
-			return 1;
-	} else if (mask.match(/[.]/)) { // host only
-		if ( wildmatch(this.hostname,mask) )
-			return 1;
-	} else { // must be a nick?
-		if ( wildmatch(this.nick,mask) )
-			return 1;
-	}
-	return 0;
-}
-
-function IRCClient_do_who_usage() {
-	this.numeric(334,":/WHO [+|-][acghilmnostuCIM] <args> <mask>");
-	this.numeric(334,":The modes as above work exactly like channel modes.");
-	this.numeric(334,":<mask> may be '*' or in nick!user@host notation.");
-	this.numeric(334,":i.e. '/WHO +a *.ca' would match all away users from *.ca");
-	this.numeric(334,":No args/mask matches to everything by default.");
-	this.numeric(334,":a       : User is away.");
-	this.numeric(334,":c <chan>: User is on <@+><#channel>, no wildcards. Can check +o/+v.");
-	this.numeric(334,":g <rnam>: Check against realname field, wildcards allowed.");
-	this.numeric(334,":h <host>: Check user's hostname, wildcards allowed.");
-	this.numeric(334,":i <ip>  : Check against IP address, wildcards allowed.");
-	this.numeric(334,":l <clas>: User is a member of <clas> irc class as defined on a Y:Line.");
-	this.numeric(334,":m <umde>: User has <umodes> set, -+ allowed.");
-	this.numeric(334,":n <nick>: User's nickname matches <nick>, wildcards allowed.");
-	this.numeric(334,":o       : User is an IRC Operator.");
-	this.numeric(334,":s <srvr>: User is on <server>, wildcards allowed.");
-	this.numeric(334,":t <time>: User has been on for more than (+) or less than (-) <time> secs.");
-	this.numeric(334,":u <user>: User's username field matches, wildcards allowed.");
-	this.numeric(334,":C       : Only display first channel that the user matches.");
-	this.numeric(334,":I       : Only return IP addresses (as opposed to hostnames.)");
-	this.numeric(334,":M       : Only check against channels you're a member of.");
-	this.numeric(315,"? :End of /WHO list. (Usage)");
-}
-
-function IRCClient_do_basic_list(mask) {
-	this.numeric321();
-	// Only allow commas if we're not doing wildcards, otherwise strip
-	// off anything past the first comma and pass to the generic parser
-	// to see if it can make heads or tails out of it.
-	if (mask.match(/[,]/)) {
-		if (mask.match(/[*?]/)) {
-			mask = mask.slice(0,mask.indexOf(","))
-		} else { // parse it out, but junk anything that's not a chan
-			var my_split = mask.split(",");
-			for (myChan in my_split) {
-				if (Channels[my_split[myChan].toUpperCase()])
-					this.numeric322(Channels[my_split[myChan].toUpperCase()]);
-			}
-			// our adventure ends here.
-			this.numeric(323, ":End of /LIST. (Basic: Comma-list)");
-			return;
-		}
-	}
-	for (chan in Channels) {
-		if (Channels[chan] && Channels[chan].match_list_mask(mask))
-			this.numeric322(Channels[chan]);
-	}
-	this.numeric(323, ":End of /LIST. (Basic)");
-	return;
-}
-
-// So, the user wants to go the hard way...
-function IRCClient_do_complex_list(cmd) {
-	var add = true;
-	var arg = 1;
-	var list = new List();
-	var listmask;
-	var listmask_items;
-
-	this.numeric321();
-
-	for (lc in cmd[1]) {
-		switch(cmd[1][lc]) {
-			case "+":
-				if (!add)
-					add = true;
-				break;
-			case "-":
-				if (add)
-					add = false;
-				break;
-			case "a":
-				arg++;
-				if (cmd[arg]) {
-					list.tweak_mode(LIST_CHANMASK,add);
-					list.Mask = cmd[arg];
-				}
-				break;
-			case "c":
-				arg++;
-				if (cmd[arg]) {
-					list.tweak_mode(LIST_CREATED,add);
-					list.Created = parseInt(cmd[arg])*60;
-				}
-				break;
-			case "m": // we never set -m, inverse.
-				arg++;
-				if (cmd[arg]) {
-					list.tweak_mode(LIST_MODES,true);
-					if (!add) {
-						var tmp_mode = "";
-						if((cmd[arg][0] != "+") ||
-						   (cmd[arg][0] != "-") )
-							tmp_mode += "+";
-						tmp_mode += cmd[arg].replace(/[-]/g," ");
-						tmp_mode = tmp_mode.replace(/[+]/g,"-");
-						list.Modes = tmp_mode.replace(/[ ]/g,"+");
-					} else {
-						list.Modes = cmd[arg];
-					}
-				}
-				break;
-			case "o":
-				arg++;
-				if (cmd[arg]) {
-					list.tweak_mode(LIST_TOPIC,add);
-					list.Topic = cmd[arg];
-				}
-				break;
-			case "p":
-				arg++;
-				if (cmd[arg]) {
-					list.tweak_mode(LIST_PEOPLE,add);
-					list.People = parseInt(cmd[arg]);
-				}
-				break;
-			case "t":
-				arg++;
-				if (cmd[arg]) {
-					list.tweak_mode(LIST_TOPICAGE,add);
-					list.TopicTime = parseInt(cmd[arg])*60;
-				}
-				break;
-			case "M":
-				list.tweak_mode(LIST_DISPLAY_CHAN_MODES,add);
-				break;
-			default:
-				break;
-		}
-	}
-
-	// Generic mask atop all this crap?
-	arg++;
-	if (cmd[arg])
-		listmask = cmd[arg];
-
-	// Here we go...
-	for (aChan in Channels) {
-		// Is the user allowed to see this channel, for starters?
-		if (!(Channels[aChan].mode&CHANMODE_SECRET) ||
-		     (this.mode&USERMODE_OPER) || this.channels[aChan]) {
-			
-			if ((list.add_flags&LIST_CHANMASK) &&
-			    !wildmatch(aChan,list.Mask.toUpperCase()))
-				continue;
-			else if ((list.del_flags&LIST_CHANMASK) &&
-			    wildmatch(aChan,list.Mask.toUpperCase()))
-				continue;
-			if ((list.add_flags&LIST_CREATED) &&
-			   (Channels[aChan].created < (time() - list.Created)))
-				continue;
-			else if ((list.del_flags&LIST_CREATED) &&
-			   (Channels[aChan].created > (time() - list.Created)))
-				continue;
-			if ((list.add_flags&LIST_TOPIC) &&
-			   (!wildmatch(Channels[aChan].topic,list.Topic)))
-				continue;
-			else if ((list.del_flags&LIST_TOPIC) &&
-			   (wildmatch(Channels[aChan].topic,list.Topic)))
-				continue;
-			if ((list.add_flags&LIST_PEOPLE) &&
-			    (true_array_len(Channels[aChan].users) < list.People) )
-				continue;
-			else if ((list.del_flags&LIST_PEOPLE) &&
-			    (true_array_len(Channels[aChan].users) >= list.People) )
-				continue;
-			if ((list.add_flags&LIST_TOPICAGE) && list.TopicTime &&
-			  (Channels[aChan].topictime > (time()-list.TopicTime)))
-				continue;
-			else if((list.del_flags&LIST_TOPICAGE)&&list.TopicTime&&
-			  (Channels[aChan].topictime < (time()-list.TopicTime)))
-				continue;
-			if (list.add_flags&LIST_MODES) { // there's no -m
-				var sic = false;
-				var madd = true;
-				var c = Channels[aChan];
-				for (mm in list.Modes) {
-					switch(list.Modes[mm]) {
-						case "+":
-							if (!madd)
-								madd = true;
-							break;
-						case "-":
-							if (madd)
-								madd = false;
-							break;
-						case "i":
-							if (
-							   (!madd && (c.mode&
-							   CHANMODE_INVITE))
-							||
-							   (madd && !(c.mode&
-							   CHANMODE_INVITE))
-							   )
-								sic = true;
-							break;
-						case "k":
-							if (
-							   (!madd && (c.mode&
-							   CHANMODE_KEY))
-							||
-							   (madd && !(c.mode&
-							   CHANMODE_KEY))
-							   )
-								sic = true;
-							break;
-						case "l":
-							if (
-							   (!madd && (c.mode&
-							   CHANMODE_LIMIT))
-							||
-							   (madd && !(c.mode&
-							   CHANMODE_LIMIT))
-							   )
-								sic = true;
-							break;
-						case "m":
-							if (
-							   (!madd && (c.mode&
-							   CHANMODE_MODERATED))
-							||
-							   (madd && !(c.mode&
-							   CHANMODE_MODERATED))
-							   )
-								sic = true;
-							break;
-						case "n":
-							if (
-							   (!madd && (c.mode&
-							   CHANMODE_NOOUTSIDE))
-							||
-							   (madd && !(c.mode&
-							   CHANMODE_NOOUTSIDE))
-							   )
-								sic = true;
-							break;
-						case "p":
-							if (
-							   (!madd && (c.mode&
-							   CHANMODE_PRIVATE))
-							||
-							   (madd && !(c.mode&
-							   CHANMODE_PRIVATE))
-							   )
-								sic = true;
-							break;
-						case "s":
-							if (
-							   (!madd && (c.mode&
-							   CHANMODE_SECRET))
-							||
-							   (madd && !(c.mode&
-							   CHANMODE_SECRET))
-							   )
-								sic = true;
-							break;
-						case "t":
-							if (
-							   (!madd && (c.mode&
-							   CHANMODE_TOPIC))
-							||
-							   (madd && !(c.mode&
-							   CHANMODE_TOPIC))
-							   )
-								sic = true;
-							break;
-						default:
-							break;
-					}
-					if (sic)
-						break;
-				}
-				if (sic)
-					continue;
-			}
-
-			if (listmask)
-				listmask_items = listmask.split(",");
-			var l_match = false; // assume we match nothing.
-			if (listmask_items) {
-				for (l in listmask_items) {
-					if (Channels[aChan].match_list_mask
-					   (listmask_items[l])) {
-						l_match = true;
-						break;
-					}
-				}
-				if (!l_match)
-					continue;
-			}
-
-			// We made it.
-			if (list.add_flags&LIST_DISPLAY_CHAN_MODES)
-				this.numeric322(Channels[aChan],true);
-			else
-				this.numeric322(Channels[aChan]);
-		}
-	}
-
-	this.numeric(323, ":End of /LIST. (Complex)");
-}
-		
-// Object which stores LIST bits and arguments.
-function List() {
-	this.add_flags = 0;
-	this.del_flags = 0;
-	this.tweak_mode = List_tweak_mode;
-	this.People = 0;
-	this.Created = 0;
-	this.TopicTime = 0;
-	this.Mask = "";
-	this.Modes = "";
-	this.Topic = "";
-}       
-
-function List_tweak_mode(bit,add) {
-	if (add) {
-		this.add_flags |= bit;
-		this.del_flags &= ~bit;
-	} else {
-		this.add_flags &= ~bit;
-		this.del_flags |= bit;
-	}
-}
-
-function IRCClient_do_list_usage() {
-	this.numeric(334,":/LIST [+|-][acmoptM] <args> <mask|channel{,channel}>");
-	this.numeric(334,":The modes as above work exactly like channel modes.");
-	this.numeric(334,":<mask> may be just like Bahamut notation.");
-	this.numeric(334,":(Bahamut Notation = >num,<num,C>num,C<num,T>num,T<num,*mask*,!*mask*)");
-	this.numeric(334,":i.e. '/LIST +p 50 #*irc*' lists chans w/ irc in the name and 50+ users.");
-	this.numeric(334,":No args/mask matches to everything by default.");
-	this.numeric(334,":a <mask>: List channels whose names match the mask.  Wildcards allowed.");
-	this.numeric(334,":c <time>: Chans created less than (-) or more than (+) <time> mins ago.");
-	this.numeric(334,":m <mods>: Channel has <modes> set.  -+ allowed.");
-	this.numeric(334,":o <topc>: Match against channel's <topic>, wildcards allowed.");
-	this.numeric(334,":p <num> : Chans with more or equal to (+) members, or (-) less than.");
-	this.numeric(334,":t <time>: Only channels whose topics were created <time> mins ago.");
-	this.numeric(334,":M       : Show channel's mode in front of the list topic.");
-	// No "end of" numeric for this.
-}
-
-// does 'this' (channel) match the 'mask' passed to us?  Use 'complex'
-// Bahamut parsing to determine that.
-function Channel_match_list_mask(mask) {
-	if (mask[0] == ">") { // Chan has more than X people?
-		if (true_array_len(this.users) < parseInt(mask.slice(1)))
-			return 0;
-	} else if (mask[0] == "<") { // Chan has less than X people?
-		if (true_array_len(this.users) >= parseInt(mask.slice(1)))
-			return 0;
-	} else if (mask[0].toUpperCase() == "C") { //created X mins ago?
-		if ((mask[1] == ">") && (this.created <
-		    (time() - (parseInt(mask.slice(2)) * 60)) ) )
-			return 0;
-		else if ((mask[1] == "<") && (this.created >
-		         (time() - (parseInt(mask.slice(2)) * 60)) ) )
-			return 0;
-	} else if (mask[0].toUpperCase() == "T") { //topics older than X mins?
-		if ((mask[1] == ">") && (this.topictime <
-		    (time() - (parseInt(mask.slice(2)) * 60)) ) )
-			return 0;
-		else if ((mask[1] == "<") && (this.topictime >
-		         (time() - (parseInt(mask.slice(2)) * 60)) ) )
-			return 0;
-	} else if (mask[0] == "!") { // doesn't match mask X
-		if (wildmatch(this.nam,mask.slice(1).toUpperCase()))
-			return 0;
-	} else { // if all else fails, we're matching a generic channel mask.
-		if (!wildmatch(this.nam,mask))
-			return 0;
-	}
-	return 1; // if we made it here, we matched something.
-}
-
-function IRCClient_get_usermode(bcast_modes) {
-	var tmp_mode = "+";
-	for (ch in USERMODE_CHAR) {
-		if ((!bcast_modes || (bcast_modes && USERMODE_BCAST[ch])) &&
-		    this.mode&USERMODE_CHAR[ch])
-			tmp_mode += ch;
-	}
-	return tmp_mode;
-}
-
-function UMode_tweak_mode(bit,add) {
-	if (add) {
-		this.add_flags |= bit;
-		this.del_flags &= ~bit;
-	} else {
-		this.add_flags &= ~bit;
-		this.del_flags |= bit;
-	}
-}
-
-function UMode() {
-	this.add_flags = 0;
-	this.del_flags = 0;
-	this.tweak_mode = UMode_tweak_mode;
-}
-
-function IRCClient_setusermode(modestr) {
-	if (!modestr)
-		return 0;
-	var add=true;
-	var unknown_mode=false;
-	var umode = new UMode();
-	for (modechar in modestr) {
-		switch (modestr[modechar]) {
-			case "+":
-				if (!add)
-					add=true;
-				break;
-			case "-":
-				if (add)
-					add=false;
-				break;
-			case "i":
-			case "w":
-			case "s":
-			case "k":
-			case "g":
-				umode.tweak_mode(USERMODE_CHAR
-					[modestr[modechar]],add);
-				break;
-			case "b":
-			case "r":
-			case "f":
-			case "y":
-			case "d":
-			case "n":
-				if (this.mode&USERMODE_OPER)
-					umode.tweak_mode(USERMODE_CHAR
-						[modestr[modechar]],add);
-				break;
-			case "o":
-				// Allow +o only by servers or non-local users.
-				if (add && this.parent &&
-				    Servers[this.parent.toLowerCase()].hub)
-					umode.tweak_mode(USERMODE_OPER,true);
-				else if (!add)
-					umode.tweak_mode(USERMODE_OPER,false);
-				break;
-			case "c":
-				if ((this.mode&USERMODE_OPER) &&
-				    (this.flags&OLINE_CAN_UMODEC))
-					umode.tweak_mode(USERMODE_CLIENT,add);
-				break;
-			case "A":
-				if ( ((this.mode&USERMODE_OPER) && (this.flags&OLINE_IS_ADMIN))
-					|| (this.parent && Servers[this.parent.toLowerCase()].hub) )
-					umode.tweak_mode(USERMODE_ADMIN,add);
-				break;
-			default:
-				if (!unknown_mode && !this.parent) {
-					this.numeric(501, ":Unknown MODE flag");
-					unknown_mode=true;
-				}
-				break;
-		}
-	}
-	var addmodes = "";
-	var delmodes = "";
-	var bcast_addmodes = "";
-	var bcast_delmodes = "";
-	for (mym in USERMODE_CHAR) {
-		if ((umode.add_flags&USERMODE_CHAR[mym]) &&
-		    !(this.mode&USERMODE_CHAR[mym])) {
-			addmodes += mym;
-			if (USERMODE_BCAST[mym])
-				bcast_addmodes += mym;
-			this.mode |= USERMODE_CHAR[mym];
-		} else if ((umode.del_flags&USERMODE_CHAR[mym]) &&
-		    (this.mode&USERMODE_CHAR[mym])) {
-			delmodes += mym;
-			if (USERMODE_BCAST[mym])
-				bcast_delmodes += mym;
-			this.mode &= ~USERMODE_CHAR[mym];
-		}
-	}
-	if (!addmodes && !delmodes)
-		return 0;
-	var final_modestr = "";
-	var bcast_modestr = "";
-	if (addmodes)
-		final_modestr += "+" + addmodes;
-	if (delmodes)
-		final_modestr += "-" + delmodes;
-	if (bcast_addmodes)
-		bcast_modestr += "+" + bcast_addmodes;
-	if (bcast_delmodes)
-		bcast_modestr += "-" + bcast_delmodes;
-	if (this.local && !this.server) {
-		this.originatorout("MODE "+this.nick+" "+final_modestr,this);
-		if (bcast_addmodes || bcast_delmodes)
-			this.bcast_to_servers("MODE "+this.nick+" "+bcast_modestr);
-	}
-	return bcast_modestr;
-}
-
-function IRCClient_check_timeout() {
-	if (!this.pinged && ((time() - this.idletime) >
-	    YLines[this.ircclass].pingfreq)) {
-		this.pinged = time();
-		this.rawout("PING :" + servername);
-	} else if (this.pinged && ((time() - this.pinged) >
-	    YLines[this.ircclass].pingfreq)) {
-		this.quit("Ping Timeout");
-		return 1; // ping timeout
-	}
-	return 0; // no ping timeout
-}
-
-function IRCClient_check_queues() {
-	var cmd;
-
-	this.sendq.send(this.socket);
-	while (this.recvq.queue.length) {
-		cmd = this.recvq.del();
-		Global_CommandLine = cmd;
-		this.work(cmd);
-		if (this.replaced_with !== undefined) {
-			this.replaced_with.check_queues();
-			break;
-		}
-	}
-}
-
-function IRCClient_finalize_server_connect(states,sendps) {
-	var statestr = " :" + states;
-	/* DreamForge-based servers do not support passing state information
-	   along on the PASS message */
-	if (this.type&DREAMFORGE)
-		statestr = "";
-	hcc_counter++;
-	gnotice("Link with " + this.nick + "[unknown@" + this.hostname +
-		"] established, states: " + states);
-	if (server.client_update != undefined)
-		server.client_update(this.socket, this.nick, this.hostname);
-	if (sendps) {
-		for (cl in CLines) {
-			if(wildmatch(this.nick,CLines[cl].servername)) {
-				this.rawout("PASS " + CLines[cl].password + statestr);
-				break;
-			}
-		}
-		if (this.type&BAHAMUT)
-			this.rawout("CAPAB " + server_capab);
-		this.rawout("SERVER " + servername + " 1 :" + serverdesc);
-	}
-	this.bcast_to_servers_raw(":" + servername + " SERVER " + this.nick
-		+ " 2 :" + this.info);
-	this.synchronize();
-}
-
-function CLine(host,password,servername,port,ircclass) {
-	this.host = host;
-	this.password = password;
-	this.servername = servername;
-	this.port = port;
-	this.ircclass = ircclass;
-	this.lastconnect = 0;
-}
-
-function HLine(allowedmask,servername) {
-	this.allowedmask = allowedmask;
-	this.servername = servername;
-}
-
-function ILine(ipmask,password,hostmask,port,ircclass) {
-	this.ipmask = ipmask;
-	this.password = password;
-	this.hostmask = hostmask;
-	this.port = port;
-	this.ircclass = ircclass;
-}
-
-function KLine(hostmask,reason,type) {
-	this.hostmask = hostmask;
-	this.reason = reason;
-	this.type = type;
-}
-
-function NLine(host,password,servername,flags,ircclass) {
-	this.host = host;
-	this.password = password;
-	this.servername = servername;
-	this.flags = flags;
-	this.ircclass = ircclass;
-}
-
-function OLine(hostmask,password,nick,flags,ircclass) {
-	this.hostmask = hostmask;
-	this.password = password;
-	this.nick = nick;
-	this.flags = flags;
-	this.ircclass = ircclass;
-}
-
-function QLine(nick,reason) {
-	this.nick = nick;
-	this.reason = reason;
-}
-
-function YLine(pingfreq,connfreq,maxlinks,sendq) {
-	this.pingfreq = pingfreq;
-	this.connfreq = connfreq;
-	this.maxlinks = maxlinks;
-	this.sendq = sendq;
-	this.active = 0;
-}
-
-function ZLine(ipmask,reason) {
-	this.ipmask = ipmask;
-	this.reason = reason;
-}
-
-function WhoWasObj(nick,uprefix,host,realname,server,serverdesc) {
-	this.nick = nick;
-	this.uprefix = uprefix;
-	this.host = host;
-	this.realname = realname;
-	this.server = server;
-	this.serverdesc = serverdesc;
-}
-
-function NickBuf(oldnick,newnick) {
-	this.oldnick = oldnick;
-	this.newnick = newnick;
-}
-
-// used for tracking true SJOIN nicks.
-function SJOIN_Nick(nick,isop,isvoice) {
-	this.nick = nick;
-	this.isop = isop;
-	this.isvoice = isvoice;
-}
-
-// Track IRC socket queues
-function IRC_Queue() {
-	this.queue = new Array;
-	this._recv_bytes = '';
-	this._send_bytes = '';
-	this.add = Queue_Add;
-	this.del = Queue_Del;
-	this.prepend = Queue_Prepend;
-	this.recv = Queue_Recv;
-	this.send = Queue_Send;
-}
-
-/* /STATS M, for profiling. */
-function StatsM() {
-	this.ticks = 0;
-	this.executions = 0;
-}
-
diff --git a/exec/load/ircd_channel.js b/exec/load/ircd/channel.js
similarity index 84%
rename from exec/load/ircd_channel.js
rename to exec/load/ircd/channel.js
index f27299c19d..9be6b6e07f 100644
--- a/exec/load/ircd_channel.js
+++ b/exec/load/ircd/channel.js
@@ -1,27 +1,23 @@
-// $Id: ircd_channel.js,v 1.34 2019/08/06 20:44:39 deuce Exp $
-//
-// ircd_channel.js                
-//
-// This program is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation; either version 2 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details:
-// http://www.gnu.org/licenses/gpl.txt
-//
-// Synchronet IRC Daemon as per RFC 1459, link compatible with Bahamut 1.4
-//
-// Copyright 2003-2009 Randolph Erwin Sommerfeld <sysop@rrx.ca>
-//
-// ** Everything related to channels and their operation.
-//
-
-////////// Constants / Defines //////////
-const CHANNEL_REVISION = "$Revision: 1.34 $".split(' ')[1];
+/*
+
+ ircd/channel.js
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details:
+ https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+
+ Everything related to channels in the IRCd and their operation.
+
+ Copyright 2003-2021 Randy Sommerfeld <cyan@synchro.net>
+
+*/
 
 const CHANMODE_NONE		=(1<<0); // NONE
 const CHANMODE_BAN		=(1<<1); // b
@@ -87,8 +83,11 @@ function Channel(nam) {
 ////////// Functions //////////
 
 function ChanMode_tweaktmpmode(tmp_bit,add) {
-	if (!this.chan.modelist[CHANMODE_OP][this.user.id] && 
-	    !this.user.server && !this.user.uline && !(this.mode&USERMODE_ADMIN)) {
+	if (   !this.chan.modelist[CHANMODE_OP][this.user.id]
+		&& !this.user.server
+		&& !this.user.uline
+		&& !(this.mode&USERMODE_ADMIN)
+	) {
 		this.user.numeric482(this.chan.nam);
 		return 0;
 	}
@@ -102,15 +101,17 @@ function ChanMode_tweaktmpmode(tmp_bit,add) {
 }
 
 function ChanMode_tweaktmpmodelist(tmp_bit,add,arg) {
-	if (!this.chan.modelist[CHANMODE_OP][this.user.id] &&
-	    !this.user.server && !this.user.uline && !(this.mode&USERMODE_ADMIN)) {
+	if (   !this.chan.modelist[CHANMODE_OP][this.user.id]
+		&& !this.user.server
+		&& !this.user.uline
+		&& !(this.mode&USERMODE_ADMIN)
+	) {
 		this.user.numeric482(this.chan.nam);
 		return 0;
 	}
 	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())
+		if (this.tmplist[tmp_bit][add][lstitem].toUpperCase() == arg.toUpperCase())
 			return 0;
 	}
 	// It doesn't exist on our mode, push it in.
@@ -122,8 +123,7 @@ function ChanMode_tweaktmpmodelist(tmp_bit,add,arg) {
 	else
 		oadd = true;
 	for (x in this.tmplist[tmp_bit][oadd]) {
-		if (this.tmplist[tmp_bit][oadd][x].toUpperCase() ==
-		    arg.toUpperCase()) {
+		if (this.tmplist[tmp_bit][oadd][x].toUpperCase() == arg.toUpperCase()) {
 			delete this.tmplist[tmp_bit][oadd][x];
 			return 0;
 		}
@@ -137,13 +137,14 @@ function ChanMode_affect_mode_list(list_bit) {
 			tmp_nick = Users[this.tmplist[list_bit][add][z].toUpperCase()];
 			if (!tmp_nick)
 				tmp_nick = search_nickbuf(this.tmplist[list_bit][add][z]);
-			if (tmp_nick && (add=="true") &&
-			    !this.chan.modelist[list_bit][tmp_nick.id]) {
+			if (tmp_nick && (add=="true") && !this.chan.modelist[list_bit][tmp_nick.id]) {
 				this.addmodes += MODE[list_bit].modechar;
 				this.addmodeargs += " " + tmp_nick.nick;
 				this.chan.modelist[list_bit][tmp_nick.id] = tmp_nick;
-			} else if (tmp_nick && (add=="false") &&
-			    this.chan.modelist[list_bit][tmp_nick.id]) {
+			} else if (tmp_nick
+						&& (add=="false")
+						&& this.chan.modelist[list_bit][tmp_nick.id]
+			) {
 				this.delmodes += MODE[list_bit].modechar;
 				this.delmodeargs += " " + tmp_nick.nick;
 				delete this.chan.modelist[list_bit][tmp_nick.id];
@@ -382,14 +383,24 @@ 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) && (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])) ) ) {
+			if (   (cm&CHANMODE_KEY)
+				&& (cmode.addbits&CHANMODE_KEY)
+				&& cmode.state_arg[cm]
+				&& chan.arg[cm]
+				&& !this.server
+				&& !this.parent
+				&& !bounce_modes
+			) {
+				this.numeric(467, format("%s :Channel key already set.", chan.nam));
+			} 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) {
@@ -413,7 +424,13 @@ function IRCClient_set_chanmode(chan,modeline,bounce_modes) {
 	// we simply display a list of bans on the channel.
 	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(367, format(
+				"%s %s %s %s",
+				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.");
 	}
@@ -422,8 +439,10 @@ function IRCClient_set_chanmode(chan,modeline,bounce_modes) {
 	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) {
+		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)) {
@@ -437,8 +456,9 @@ function IRCClient_set_chanmode(chan,modeline,bounce_modes) {
 
 	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()) {
+			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];
@@ -559,8 +579,9 @@ function IRCClient_do_join(chan_name,join_key) {
 					+ " :Cannot join channel (+i: invite only)");
 				return 0;
 			}
-			if ((chan.mode&CHANMODE_LIMIT) && (true_array_len(chan.users)
-			     	>= chan.arg[CHANMODE_LIMIT]) ) {
+			if (   (chan.mode&CHANMODE_LIMIT)
+				&& (true_array_len(chan.users) >= chan.arg[CHANMODE_LIMIT])
+			) {
 				this.numeric("471", chan.nam
 					+ " :Cannot join channel (+l: channel is full)");
 				return 0;
@@ -592,10 +613,13 @@ function IRCClient_do_join(chan_name,join_key) {
 			}
 		}
 		if (chan_name[0] != "&") {
-			this.bcast_to_servers_raw(":" + this.nick + " SJOIN "
-				+ chan.created + " " + chan.nam,BAHAMUT);
-			this.bcast_to_servers_raw(":" + this.nick + " JOIN "
-				+ chan.nam + " " + chan.created,DREAMFORGE);
+			this.bcast_to_servers_raw(
+				format(":%s SJOIN %s %s",
+					this.nick,
+					chan.created,
+					chan.nam
+				)
+			);
 		}
 	} else {
 		// create a new channel
@@ -610,16 +634,16 @@ function IRCClient_do_join(chan_name,join_key) {
 			chan.modelist[CHANMODE_OP][this.id] = this;
 		}
 		if (chan_name[0] != "&") {
-			this.bcast_to_servers_raw(":" + servername + " SJOIN "
-				+ chan.created + " " + chan.nam + " "
-				+ chan.chanmode() + " :" + create_op + this.nick,BAHAMUT);
-			this.bcast_to_servers_raw(":" + this.nick + " JOIN "
-				+ chan.nam,DREAMFORGE);
-			if (create_op) {
-				server_bcast_to_servers(":" + servername + " MODE "
-					+ chan.nam + " +o " + this.nick + " "
-					+ chan.created,DREAMFORGE);
-			}
+			this.bcast_to_servers_raw(
+				format(":%s SJOIN %s %s %s :%s%s",
+					servername,
+					chan.created,
+					chan.nam,
+					chan.chanmode(),
+					create_op,
+					this.nick
+				)
+			);
 		}
 	}
 	if (this.invited.toUpperCase() == chan.nam.toUpperCase())
diff --git a/exec/load/ircd/config.js b/exec/load/ircd/config.js
new file mode 100644
index 0000000000..0eb148848e
--- /dev/null
+++ b/exec/load/ircd/config.js
@@ -0,0 +1,419 @@
+/*
+
+ ircd/config.js
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details:
+ https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+
+ Anything that handles manipulating the IRCd configuration.
+
+ Copyright 2003-2021 Randy Sommerfeld <cyan@synchro.net>
+
+*/
+
+function parse_nline_flags(flags) {
+	var nline_flags = 0;
+	for(thisflag in flags) {
+		switch(flags[thisflag]) {
+			case "q":
+				nline_flags |= NLINE_CHECK_QWKPASSWD;
+				break;
+			case "w":
+				nline_flags |= NLINE_IS_QWKMASTER;
+				break;
+			case "k":
+				nline_flags |= NLINE_CHECK_WITH_QWKMASTER;
+				break;
+			default:
+				log(LOG_WARNING,"!WARNING Unknown N:Line flag '"
+					+ flags[thisflag] + "' in config.");
+				break;
+		}
+	}
+	return nline_flags;
+}
+
+function parse_oline_flags(flags) {
+	var oline_flags = 0;
+	for(thisflag in flags) {
+		switch(flags[thisflag]) {
+			case "r":
+				oline_flags |= OLINE_CAN_REHASH;
+				break;
+			case "R":
+				oline_flags |= OLINE_CAN_RESTART;
+				break;
+			case "D":
+				oline_flags |= OLINE_CAN_DIE;
+				break;
+			case "g":
+				oline_flags |= OLINE_CAN_GLOBOPS;
+				break;
+			case "w":
+				oline_flags |= OLINE_CAN_WALLOPS;
+				break;
+			case "l":
+				oline_flags |= OLINE_CAN_LOCOPS;
+				break;
+			case "c":
+				oline_flags |= OLINE_CAN_LSQUITCON;
+				break;
+			case "C":
+				oline_flags |= OLINE_CAN_GSQUITCON;
+				break;
+			case "k":
+				oline_flags |= OLINE_CAN_LKILL;
+				break;
+			case "K":
+				oline_flags |= OLINE_CAN_GKILL;
+				break;
+			case "b":
+				oline_flags |= OLINE_CAN_KLINE;
+				break;
+			case "B":
+				oline_flags |= OLINE_CAN_UNKLINE;
+				break;
+			case "n":
+				oline_flags |= OLINE_CAN_LGNOTICE;
+				break;
+			case "N":
+				oline_flags |= OLINE_CAN_GGNOTICE;
+				break;
+			case "u":
+				oline_flags |= OLINE_CAN_UMODEC;
+				break;
+			case "A":
+				oline_flags |= OLINE_IS_ADMIN;
+				break;
+			case "a":
+			case "f":
+			case "F":
+				break; // All reserved for future use.
+			case "s":
+				oline_flags |= OLINE_CAN_CHATOPS;
+				break;
+			case "S":
+				oline_flags |= OLINE_CHECK_SYSPASSWD;
+				break;
+			case "x":
+			case "X":
+				oline_flags |= OLINE_CAN_DEBUG;
+				break;
+			case "O":
+				oline_flags |= OLINE_IS_GOPER;
+				oline_flags |= OLINE_CAN_GSQUITCON;
+				oline_flags |= OLINE_CAN_GKILL;
+				oline_flags |= OLINE_CAN_GGNOTICE;
+				oline_flags |= OLINE_CAN_CHATOPS;
+			case "o":
+				oline_flags |= OLINE_CAN_REHASH;
+				oline_flags |= OLINE_CAN_GLOBOPS;
+				oline_flags |= OLINE_CAN_WALLOPS;
+				oline_flags |= OLINE_CAN_LOCOPS;
+				oline_flags |= OLINE_CAN_LSQUITCON;
+				oline_flags |= OLINE_CAN_LKILL;
+				oline_flags |= OLINE_CAN_KLINE;
+				oline_flags |= OLINE_CAN_UNKLINE;
+				oline_flags |= OLINE_CAN_LGNOTICE;
+				oline_flags |= OLINE_CAN_UMODEC;
+				break;
+			default:
+				log(LOG_WARNING,"!WARNING Unknown O:Line flag '"
+					+ flags[thisflag] + "' in config.");
+				break;
+		}
+	}
+	return oline_flags;
+}
+
+function read_config_file() {
+	/* All of these variables are global. */
+	Admin1 = "";
+	Admin2 = "";
+	Admin3 = "";
+	CLines = new Array;
+	HLines = new Array;
+	ILines = new Array;
+	KLines = new Array;
+	NLines = new Array;
+	OLines = new Array;
+	PLines = new Array;
+	QLines = new Array;
+	ULines = new Array;
+	diepass = "";
+	restartpass = "";
+	YLines = new Array;
+	ZLines = new Array;
+	/* End of global variables */
+	var fname="";
+	if (config_filename && config_filename.length) {
+		if(config_filename.indexOf('/')>=0 || config_filename.indexOf('\\')>=0)
+			fname=config_filename;
+		else
+			fname=system.ctrl_dir + config_filename;
+	} else {
+		fname=system.ctrl_dir + "ircd." + system.local_host_name + ".conf";
+		if(!file_exists(fname))
+			fname=system.ctrl_dir + "ircd." + system.host_name + ".conf";
+		if(!file_exists(fname))
+			fname=system.ctrl_dir + "ircd.conf";
+	}
+	log(LOG_INFO,"Trying to read configuration from: " + fname);
+	var file_handle = new File(fname);
+	if (file_handle.open("r")) {
+		if (fname.substr(fname.length-3,3) == "ini")
+			read_ini_config(file_handle);
+		else
+			read_conf_config(file_handle);
+		file_handle.close();
+	} else {
+		log("Couldn't open configuration file! Proceeding with defaults.");
+	}
+
+	time_config_read = time();
+	scan_for_klined_clients();
+
+	YLines[0] = new YLine(120,600,1,5050000); // default irc class
+}
+
+function ini_sections() {
+	this.IRCdInfo = ini_IRCdInfo;
+	this.Port = ini_Port;
+	this.ConnectClass = ini_ConnectClass;
+	this.Allow = ini_Allow;
+	this.Operator = ini_Operator;
+	this.Services = ini_Services;
+	this.Ban = ini_Ban;
+	this.Restrict = ini_Restrict;
+	this.Hub = ini_Hub;
+}
+
+function ini_IRCdInfo(arg, ini) {
+	log("ircdinfo " + ini.Hostname);
+	servername=ini.Hostname;
+	serverdesc=ini.Info;
+	Admin1=ini.Admin1;
+	Admin2=ini.Admin2;
+	Admin3=ini.Admin3;
+}
+
+/* Former M:Line */
+function ini_Port(arg, ini) {
+	mline_port = arg;
+}
+
+/* Former Y:Line */
+function ini_ConnectClass(arg, ini) {
+	YLines[arg] = new YLine(
+		parseInt(ini.PingFrequency),
+		parseInt(ini.ConnectFrequency),
+		parseInt(ini.Maximum),
+		parseInt(ini.SendQ)
+	);
+}
+
+/* Former I:Line */
+function ini_Allow(arg, ini) {
+	ILines[arg] = new ILine(
+		ini.Mask,
+		null, /* password */
+		ini.Mask, /* hostmask */
+		null, /* port */
+		0 /* irc class */
+	);
+}
+
+/* Former O:Line */
+function ini_Operator(arg, ini) {
+}
+
+/* Former U:Line */
+function ini_Services(arg, ini) {
+}
+
+/* Former K:Line & Z:Line */
+function ini_Ban(arg, ini) {
+}
+
+function ini_Restrict(arg, ini) {
+}
+
+/* Former H:Line */
+function ini_Hub(arg, ini) {
+	HLines.push(new HLine(
+		"*", /* servermask permitted */
+		arg /* servername */
+	));
+}
+
+function read_ini_config(conf) {
+	var ini = conf.iniGetAllObjects();
+	var split;
+	var section_name;
+	var section_arg;
+	var Sections = new ini_sections();
+
+	for each(var i in ini) {
+		split = i.name.split(":");
+		section_name = split[0];
+		section_arg = split[1];
+		if (typeof Sections[section_name] === 'function')
+			Sections[section_name](section_arg, i);
+	}
+}
+
+function read_conf_config(conf) {
+	function fancy_split(line) {
+		var ret = [];
+		var i;
+		var s = 0;
+		var inb = false;
+		var str;
+
+		for (i = 0; i < line.length; i++) {
+			if (line[i] == ':') {
+				if (inb)
+					continue;
+				if (i > 0 && line[i-1] == ']')
+					str = line.slice(s, i-1);
+				else
+					str = line.slice(s, i);
+				ret.push(str);
+				s = i + 1;
+			}
+			else if (!inb && line[i] == '[' && s == i) {
+				inb = true;
+				s = i + 1;
+			}
+			else if (line[i] == ']' && (i+1 == line.length || line[i+1] == ':')) {
+				inb = false;
+			}
+		}
+		if (s < line.length) {
+			if (i > 0 && line[i-1] == ']')
+				str = line.slice(s, i-1);
+			else
+				str = line.slice(s, i);
+			ret.push(str);
+		}
+
+		return ret;
+	}
+
+	while (!conf.eof) {
+		var conf_line = conf.readln();
+		if ((conf_line != null) && conf_line.match("[:]")) {
+			var arg = fancy_split(conf_line);
+			for(argument in arg) {
+				arg[argument]=arg[argument].replace(
+					/SYSTEM_HOST_NAME/g,system.host_name);
+				arg[argument]=arg[argument].replace(
+					/SYSTEM_NAME/g,system.name);
+				if (typeof system.qwk_id !== "undefined") {
+					arg[argument]=arg[argument].replace(
+						/SYSTEM_QWKID/g,system.qwk_id.toLowerCase()
+					);
+				}
+				arg[argument]=arg[argument].replace(
+					/VERSION_NOTICE/g,system.version_notice);
+			}
+		switch (conf_line[0].toUpperCase()) {
+				case "A":
+					if (!arg[3])
+						break;
+					Admin1 = arg[1];
+					Admin2 = arg[2];
+					Admin3 = arg[3];
+					break;
+				case "C":
+					if (!arg[5])
+						break;
+					CLines.push(new CLine(arg[1],arg[2],arg[3],arg[4],
+						parseInt(arg[5]) ));
+					break;
+				case "H":
+					if (!arg[3])
+						break;
+					HLines.push(new HLine(arg[1],arg[3]));
+					break;
+				case "I":
+					if (!arg[5])
+						break;
+					ILines.push(new ILine(arg[1],arg[2],arg[3],arg[4],
+						arg[5]));
+					break;
+				case "K":
+					if (!arg[2])
+						break;
+					var kline_mask = create_ban_mask(arg[1],true);
+					if (!kline_mask) {
+						log(LOG_WARNING,"!WARNING Invalid K:Line ("
+							+ arg[1] + ")");
+						break;
+					}
+					KLines.push(new KLine(kline_mask,arg[2],"K"));
+					break;
+				case "M":
+					if (!arg[3])
+						break;
+					servername = arg[1];
+					serverdesc = arg[3];
+					mline_port = parseInt(arg[4]);
+					break;
+				case "N":
+					if (!arg[5])
+						break;
+					NLines.push(new NLine(arg[1],arg[2],arg[3],
+						parse_nline_flags(arg[4]),arg[5]) );
+					break;
+				case "O":
+					if (!arg[5])
+						break;
+					OLines.push(new OLine(arg[1],arg[2],arg[3],
+						parse_oline_flags(arg[4]),parseInt(arg[5]) ));
+					break;
+				case "P":
+					PLines.push(parseInt(arg[4]));
+					break;
+				case "Q":
+					if (!arg[3])
+						break;
+					QLines.push(new QLine(arg[3],arg[2]));
+					break;
+				case "U":
+					if (!arg[1])
+						break;
+					ULines.push(arg[1]);
+					break;
+				case "X":
+					diepass = arg[1];
+					restartpass = arg[2];
+					break;
+				case "Y":
+					if (!arg[5])
+						break;
+					YLines[parseInt(arg[1])] = new YLine(parseInt(arg[2]),
+						parseInt(arg[3]),parseInt(arg[4]),parseInt(arg[5]));
+					break;
+				case "Z":
+					if (!arg[2])
+						break;
+					ZLines.push(new ZLine(arg[1],arg[2]));
+					break;
+				case "#":
+				case ";":
+				default:
+					break;
+			}
+		}
+	}
+}
+
diff --git a/exec/load/ircd/core.js b/exec/load/ircd/core.js
new file mode 100644
index 0000000000..e7fc71f9ea
--- /dev/null
+++ b/exec/load/ircd/core.js
@@ -0,0 +1,2521 @@
+/*
+
+ ircd/core.js
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details:
+ https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+
+ Core IRCd functions.
+
+ Copyright 2003-2021 Randy Sommerfeld <cyan@synchro.net>
+
+*/
+
+/* There's no way to get the length of an associative array in JS, so here we are. */
+function true_array_len(my_array) {
+	var counter = 0;
+	for (i in my_array) {
+		counter++;
+	}
+	return counter;
+}
+
+function terminate_everything(terminate_reason, error) {
+	log(error ? LOG_ERR : LOG_NOTICE, "Terminating: " + terminate_reason);
+	for(thisClient in Local_Sockets_Map) {
+		var Client = Local_Sockets_Map[thisClient];
+		Client.rawout("ERROR :" + terminate_reason);
+		Client.socket.close();
+	}
+	exit(error);
+}
+
+function search_server_only(server_name) {
+	if (!server_name)
+		return 0;
+	for(thisServer in Servers) {
+		var Server=Servers[thisServer];
+		if (wildmatch(Server.nick,server_name))
+			return Server;
+	}
+	if (wildmatch(servername,server_name))
+		return -1; // the server passed to us is our own.
+	// No success.
+	return 0;
+}
+
+function searchbyserver(servnick) {
+	if (!servnick)
+		return 0;
+	var server_try = search_server_only(servnick);
+	if (server_try) {
+		return server_try;
+	} else {
+		for(thisNick in Users) {
+			var Nick=Users[thisNick];
+			if (wildmatch(Nick.nick,servnick))
+				return search_server_only(Nick.servername);
+		}
+	}
+	return 0; // looks like we failed after all that hard work :(
+}
+
+// Only allow letters, numbers and underscore in username to a maximum of
+// 9 characters for 'anonymous' users (i.e. not using PASS to authenticate.)
+// hostile characters like !,@,: etc would be bad here :)
+function parse_username(str) {
+	str = str.replace(/[^\w]/g,"").toLowerCase();
+	if (!str)
+		str = "user"; // nothing? we'll give you something boring.
+	return str.slice(0,9);
+}
+
+function umode_notice(bit,ntype,nmessage) {
+	log(ntype + ": " + nmessage);
+	for (thisuser in Local_Users) {
+		var user = Local_Users[thisuser];
+		if (user.mode && ((user.mode&bit)==bit))
+			user.rawout(":" + servername + " NOTICE " + user.nick
+				+ " :*** " + ntype + " -- " + nmessage);
+	}
+
+}
+
+function create_ban_mask(str,kline) {
+	var tmp_banstr = new Array;
+	tmp_banstr[0] = "";
+	tmp_banstr[1] = "";
+	tmp_banstr[2] = "";
+	var bchar_counter = 0;
+	var part_counter = 0; // BAN: 0!1@2 KLINE: 0@1
+	var regexp="[A-Za-z\{\}\`\^\_\|\\]\\[\\\\0-9\-.*?\~:]";
+	var finalstr;
+	for (bchar in str) {
+		if (str[bchar].match(regexp)) {
+			tmp_banstr[part_counter] += str[bchar];
+			bchar_counter++;
+		} else if ((str[bchar] == "!") && (part_counter == 0) &&
+			   !kline) {
+			part_counter = 1;
+			bchar_counter = 0;
+		} else if ((str[bchar] == "@") && (part_counter == 1) &&
+			   !kline) {
+			part_counter = 2;
+			bchar_counter = 0;
+		} else if ((str[bchar] == "@") && (part_counter == 0)) {
+			if (kline) {
+				part_counter = 1;
+			} else {
+				tmp_banstr[1] = tmp_banstr[0];
+				tmp_banstr[0] = "*";
+				part_counter = 2;
+			}
+			bchar_counter = 0;
+		}
+	}
+	if (!tmp_banstr[0] && !tmp_banstr[1] && !tmp_banstr[2])
+		return 0;
+	if (tmp_banstr[0].match(/[.]/) && !tmp_banstr[1] && !tmp_banstr[2]) {
+		if (kline)
+			tmp_banstr[1] = tmp_banstr[0];
+		else
+			tmp_banstr[2] = tmp_banstr[0];
+		tmp_banstr[0] = "";
+	}
+	if (!tmp_banstr[0])
+		tmp_banstr[0] = "*";
+	if (!tmp_banstr[1])
+		tmp_banstr[1] = "*";
+	if (!tmp_banstr[2] && !kline)
+		tmp_banstr[2] = "*";
+	if (kline)
+		finalstr = tmp_banstr[0].slice(0,10) + "@" + tmp_banstr[1].slice(0,80);
+	else
+		finalstr = format("%s!%s@%s",
+			tmp_banstr[0].slice(0,max_nicklen),
+			tmp_banstr[1].slice(0,10),
+			tmp_banstr[2].slice(0,80)
+		);
+		while (finalstr.match(/[*][*]/)) {
+			finalstr=finalstr.replace(/[*][*]/g,"*");
+		}
+	return finalstr;
+}
+
+function isklined(kl_str) {
+	for(the_kl in KLines) {
+		if (   KLines[the_kl].hostmask
+			&& wildmatch(kl_str,KLines[the_kl].hostmask)
+		) {
+			return KLines[the_kl];
+		}
+	}
+	return 0;
+}
+
+function iszlined(zl_ip) {
+	for(the_zl in ZLines) {
+		if (   ZLines[the_zl].ipmask
+			&& wildmatch(zl_ip,ZLines[the_zl].ipmask)
+		) {
+			return 1;
+		}
+	}
+	return 0;
+}
+
+function scan_for_klined_clients() {
+	for(thisUser in Local_Users) {
+		var theuser=Local_Users[thisUser];
+		var kline=isklined(theuser.uprefix + "@" + theuser.hostname);
+		if (kline)
+			theuser.quit("User has been K:Lined (" + kline.reason + ")");
+		if (iszlined(theuser.ip))
+			theuser.quit("User has been Z:Lined");
+	}
+}
+
+function remove_kline(kl_hm) {
+	for(the_kl in KLines) {
+		if (   KLines[the_kl].hostmask
+			&& wildmatch(kl_hm,KLines[the_kl].hostmask)
+		) {
+			KLines[the_kl].hostmask = "";
+			KLines[the_kl].reason = "";
+			KLines[the_kl].type = "";
+			return 1;
+		}
+	}
+	return 0; // failure.
+}
+
+function connect_to_server(this_cline,the_port) {
+	var connect_sock;
+	var new_id;
+
+	if (!the_port && this_cline.port)
+		the_port = this_cline.port;
+	else if (!the_port)
+		the_port = default_port; // try a safe default.
+	if (js.global.ConnectedSocket != undefined) {
+		try {
+			connect_sock = new ConnectedSocket(this_cline.host, the_port, {timeout:ob_sock_timeout, bindaddrs:server.interface_ip_addr_list});
+		}
+		catch(e) {
+			connect_sock = new Socket();
+		}
+	}
+	else {
+		connect_sock = new Socket();
+		connect_sock.bind(0,server.interface_ip_address);
+		connect_sock.connect(this_cline.host,the_port,ob_sock_timeout);
+	}
+
+	var sendts = true; /* Assume Bahamut */
+
+	for (nl in NLines) {
+		var mynl = NLines[nl];
+	}
+
+	if (connect_sock.is_connected) {
+		umode_notice(USERMODE_ROUTING,"Routing",
+			"Connected!  Sending info...");
+		var sendstr = "PASS " + this_cline.password;
+		if (sendts)
+			sendstr += " :TS";
+		connect_sock.send(sendstr + "\r\n");
+		connect_sock.send("CAPAB " + Server_CAPAB + "\r\n");
+		connect_sock.send("SERVER " + servername + " 1 :" + serverdesc +"\r\n");
+		new_id = "id" + next_client_id;
+		next_client_id++;
+		Unregistered[new_id]=new Unregistered_Client(new_id,connect_sock);
+		Unregistered[new_id].sendps = false; // Don't do P/S pair again
+		Unregistered[new_id].outgoing = true; /* Outgoing Connection */
+		Unregistered[new_id].ircclass = this_cline.ircclass;
+		YLines[this_cline.ircclass].active++;
+		log(LOG_DEBUG, "Class "+this_cline.ircclass+" up to "+YLines[this_cline.ircclass].active+" active out of "+YLines[this_cline.ircclass].maxlinks);
+	}
+	else {
+		umode_notice(USERMODE_ROUTING,"Routing",
+			"Failed to connect to " +
+			this_cline.servername + " ("+this_cline.host+")");
+		connect_sock.close();
+	}
+	this_cline.lastconnect = time();
+}
+
+function wallopers(str) {
+	for(thisoper in Local_Users) {
+		var oper=Local_Users[thisoper];
+		if (oper.mode&USERMODE_OPER)
+			oper.rawout(str);
+	}
+}
+
+function push_nickbuf(oldnick,newnick) {
+	NickHistory.unshift(new NickBuf(oldnick,newnick));
+	if(NickHistory.length >= NickHistorySize)
+		NickHistory.pop();
+}
+
+function search_nickbuf(bufnick) {
+	for (nb=NickHistory.length-1;nb>-1;nb--) {
+		if (bufnick.toUpperCase() == NickHistory[nb].oldnick.toUpperCase()) {
+			if (!Users[NickHistory[nb].newnick.toUpperCase()])
+				bufnick = NickHistory[nb].newnick;
+			else
+				return Users[NickHistory[nb].newnick.toUpperCase()];
+		}
+	}
+	return 0;
+}
+
+function create_new_socket(port) {
+	var newsock;
+
+	log(LOG_DEBUG,"Creating new socket object on port " + port);
+	if (js.global.ListeningSocket != undefined) {
+		try {
+			newsock = new ListeningSocket(server.interface_ip_addr_list, port, "IRCd");
+			log(format("IRC server socket bound to TCP port %d", port));
+		}
+		catch(e) {
+			log(LOG_ERR,"!Error " + e + " creating listening socket on port "
+				+ port);
+			return 0;
+		}
+	} else {
+		newsock = new Socket();
+		if(!newsock.bind(port,server.interface_ip_address)) {
+			log(LOG_ERR,"!Error " + newsock.error + " binding socket to TCP port "
+				+ port);
+			return 0;
+		}
+		log(format("%04u ",newsock.descriptor)
+			+ "IRC server socket bound to TCP port " + port);
+		if(!newsock.listen(5 /* backlog */)) {
+			log(LOG_ERR,"!Error " + newsock.error
+				+ " setting up socket for listening");
+			return 0;
+		}
+	}
+	return newsock;
+}
+
+function check_qwk_passwd(qwkid,password) {
+	if (!password || !qwkid)
+		return 0;
+	qwkid = qwkid.toUpperCase();
+	var usernum = system.matchuser(qwkid);
+	var bbsuser = new User(usernum);
+	if (   (password.toUpperCase() == bbsuser.security.password.toUpperCase())
+		&& (bbsuser.security.restrictions&UFLAG_Q)
+	) {
+		return 1;
+	}
+	return 0;
+}
+function IRCClient_netsplit(ns_reason) {
+	if (!ns_reason)
+		ns_reason = "net.split.net.split net.split.net.split";
+	for (sqclient in Users) {
+		if (Users[sqclient] && (Users[sqclient].servername == this.nick)) {
+			Users[sqclient].quit(ns_reason,true,true);
+		}
+	}
+	for (sqserver in Servers) {
+		if (Servers[sqserver] && (Servers[sqserver].linkparent == this.nick)) {
+			Servers[sqserver].quit(ns_reason,true,true);
+		}
+	}
+}
+
+function IRCClient_RMChan(rmchan_obj) {
+	if (!rmchan_obj)
+		return 0;
+	if (rmchan_obj.users[this.id])
+		delete rmchan_obj.users[this.id];
+	if (this.channels[rmchan_obj.nam.toUpperCase()])
+		delete this.channels[rmchan_obj.nam.toUpperCase()];
+	delete rmchan_obj.modelist[CHANMODE_OP][this.id];
+	delete rmchan_obj.modelist[CHANMODE_VOICE][this.id];
+	if (!true_array_len(rmchan_obj.users))
+		delete Channels[rmchan_obj.nam.toUpperCase()];
+}
+
+//////////////////// Output Helper Functions ////////////////////
+function rawout(str) {
+	var sendconn;
+	var str_end;
+	var str_beg;
+
+	if (debug)
+		log(format("[RAW->%s]: %s",this.nick,str));
+
+	if (this.local) {
+		sendconn = this;
+	} else if (!this.local) {
+		if ((str[0] == ":") && str[0].match(["!"])) {
+			str_end = str.slice(str.indexOf(" ")+1);
+			str_beg = str.slice(0,str.indexOf("!"));
+			str = str_beg + " " + str_end;
+		}
+		sendconn = Servers[this.parent.toLowerCase()];
+	} else {
+		log(LOG_ERR,"!ERROR: No connection to send to?");
+		return 0;
+	}
+
+	sendconn.sendq.add(str);
+}
+
+function originatorout(str,origin) {
+	var send_data;
+	var sendconn;
+
+	if (debug)
+		log(format("[%s->%s]: %s",origin.nick,this.nick,str));
+
+	sendconn = this;
+	if(this.local && !this.server) {
+		if (origin.server)
+			send_data = ":" + origin.nick + " " + str;
+		else
+			send_data = ":" + origin.nuh + " " + str;
+	} else if (this.server) {
+		send_data = ":" + origin.nick + " " + str;
+	} else if (!this.local) {
+		sendconn = Servers[this.parent.toLowerCase()];
+		send_data = ":" + origin.nick + " " + str;
+	} else {
+		log(LOG_ERR,"!ERROR: No connection to send to?");
+		return 0;
+	}
+
+	sendconn.sendq.add(send_data);
+}
+
+function ircout(str) {
+	var send_data;
+	var sendconn;
+
+	if (debug)
+		log(format("[%s->%s]: %s",servername,this.nick,str));
+
+	if(this.local) {
+		sendconn = this;
+	} else if (this.parent) {
+		sendconn = Servers[this.parent.toLowerCase()];
+	} else {
+		log(LOG_ERR,"!ERROR: No socket to send to?");
+		return 0;
+	}
+
+	send_data = ":" + servername + " " + str;
+	sendconn.sendq.add(send_data);
+}
+
+function Queue_Recv(sock) {
+	var pos;
+	var cmd;
+	var str;
+
+	str = sock.recv(65536,0);
+	if (str !== null && str.length > 0) {
+		this._recv_bytes += str;
+		while ((pos = this._recv_bytes.search('\n')) != -1) {
+			cmd = this._recv_bytes.substr(0, pos);
+			this._recv_bytes = this._recv_bytes.substr(pos+1);
+			if (cmd[cmd.length-1] == '\r')
+				cmd = cmd.substr(0, cmd.length - 1);
+			this.add(cmd);
+		}
+	}
+}
+
+function Queue_Send(sock) {
+	var sent;
+
+	if (this.queue.length) {
+		this._send_bytes += this.queue.join('\r\n')+'\r\n';
+		this.queue = [];
+	}
+	if (this._send_bytes.length) {
+		sent = sock.send(this._send_bytes);
+		if (sent > 0)
+			this._send_bytes = this._send_bytes.substr(sent);
+	}
+}
+
+function Queue_Prepend(str) {
+	this.queue.unshift(str);
+}
+
+function Queue_Add(str) {
+	this.queue.push(str);
+}
+
+function Queue_Del() {
+	return this.queue.shift();
+}
+
+function IRCClient_server_notice(str) {
+	this.ircout("NOTICE " + this.nick + " :" + str);
+}
+
+function IRCClient_numeric(num, str) {
+	this.ircout(num + " " + this.nick + " " + str);
+}
+
+//////////////////// Numeric Functions ////////////////////
+function IRCClient_numeric200(dest,next) {
+	this.numeric(200, "Link " + VERSION + " " + dest + " " + next);
+}
+
+function IRCClient_numeric201(ircclass,server) {
+	this.numeric(201, "Try. " + ircclass + " " + server);
+}
+
+function IRCClient_numeric202(ircclass,server) {
+	this.numeric(202, "H.S. " + ircclass + " " + server);
+}
+
+function IRCClient_numeric203(ircclass,ip) {
+	this.numeric(203, "???? " + ircclass + " [" + ip + "]");
+}
+
+function IRCClient_numeric204(nick) {
+	this.numeric(204, "Oper " + nick.ircclass + " " + nick.nick);
+}
+
+function IRCClient_numeric205(nick) {
+	this.numeric(205, "User " + nick.ircclass + " " + nick.nick);
+}
+
+function IRCClient_numeric206(ircclass,sint,cint,server) {
+	this.numeric(206, "Serv " + ircclass + " " + sint + "S " + cint
+		+ "C *!*@" + server);
+}
+
+function IRCClient_numeric208(type,clientname) {
+	this.numeric(208, type + " 0 " + clientname);
+}
+
+function IRCClient_numeric261(file) {
+	this.numeric(261, "File " + file + " " + debug);
+}
+
+function IRCClient_numeric321() {
+	this.numeric("321", "Channel :Users  Name");
+}
+
+function IRCClient_numeric322(chan,show_modes) {
+	var channel_name;
+	var disp_topic = "";
+	var is_onchan = this.channels[chan.nam.toUpperCase()];
+
+	if (show_modes) {
+		var chanm = chan.chanmode()
+		disp_topic += "[" + chanm.slice(0,chanm.length-1) + "]"
+	}
+
+	if ((chan.mode&CHANMODE_PRIVATE) && !(this.mode&USERMODE_OPER) &&
+	    !is_onchan ) {
+		channel_name = "*";
+	} else {
+		channel_name = chan.nam;
+		if (disp_topic)
+			disp_topic += " ";
+		disp_topic += chan.topic;
+	}
+	if (!(chan.mode&CHANMODE_SECRET) || (this.mode&USERMODE_OPER) ||
+	    is_onchan )
+		this.numeric(322, channel_name + " " + true_array_len(chan.users)
+			+ " :" + disp_topic);
+}
+
+function IRCClient_numeric331(chan) {
+	this.numeric(331, chan.nam + " :No topic is set.");
+}
+
+function IRCClient_numeric332(chan) {
+	this.numeric(332, chan.nam + " :" + chan.topic);
+}
+
+function IRCClient_numeric333(chan) {
+	this.numeric(333, chan.nam + " " + chan.topicchangedby + " "
+		+ chan.topictime);
+}
+
+function IRCClient_numeric351() {
+	this.numeric(351, VERSION + " " + servername + " :" + VERSION_STR);
+}
+
+function IRCClient_numeric352(user,show_ips_only,chan) {
+	var who_mode="";
+	var disp;
+	var disphost;
+
+	if (!user)
+		return 0;
+
+	if (!chan)
+		disp = "*";
+	else
+		disp = chan.nam;
+
+	if (user.away)
+		who_mode += "G";
+	else
+		who_mode += "H";
+	if (chan) {
+		if (chan.modelist[CHANMODE_OP][user.id])
+			who_mode += "@";
+		else if (chan.modelist[CHANMODE_VOICE][user.id])
+			who_mode += "+";
+	}
+	if (user.mode&USERMODE_OPER)
+		who_mode += "*";
+
+	if (show_ips_only)
+		disphost = user.ip;
+	else
+		disphost = user.hostname;
+
+	this.numeric(352, disp + " " + user.uprefix + " " + disphost + " "
+		+ user.servername + " " + user.nick + " " + who_mode
+		+ " :" + user.hops + " " + user.realname);
+	return 1;
+}
+
+function IRCClient_numeric353(chan, str) {
+	// = public @ secret * everything else
+	if (chan.mode&CHANMODE_SECRET)
+		var ctype_str = "@";
+	else
+		var ctype_str = "=";
+	this.numeric("353", ctype_str + " " + chan.nam + " :" + str);
+}
+
+function IRCClient_numeric382(str) {
+	this.numeric(382, "ircd.conf :" + str);
+}
+
+function IRCClient_numeric391() {
+	this.numeric(391, servername + " :"
+		+ strftime("%A %B %d %Y -- %H:%M %z",time()));
+}
+
+function IRCClient_numeric401(str) {
+	this.numeric("401", str + " :No such nick/channel.");
+}
+
+function IRCClient_numeric402(str) {
+	this.numeric(402, str + " :No such server.");
+}
+
+function IRCClient_numeric403(str) {
+	this.numeric("403", str
+		+ " :No such channel or invalid channel designation.");
+}
+
+function IRCClient_numeric411(str) {
+	this.numeric("411", ":No recipient given. (" + str + ")");
+}
+
+function IRCClient_numeric412() {
+	this.numeric(412, " :No text to send.");
+}
+
+function IRCClient_numeric440(str) {
+	this.numeric(440, str + " :Services is currently down, sorry.");
+}
+
+function IRCClient_numeric441(str) {
+	this.numeric("441", str + " :They aren't on that channel.");
+}
+
+function IRCClient_numeric442(str) {
+	this.numeric("442", str + " :You're not on that channel.");
+}
+
+function IRCClient_numeric445() {
+	this.numeric(445, ":SUMMON has been disabled.");
+}
+
+function IRCClient_numeric446() {
+	this.numeric(446, ":USERS has been disabled.");
+}
+
+function IRCClient_numeric451() {
+	this.numeric("451", ":You have not registered.");
+}
+
+function IRCClient_numeric461(cmd) {
+	this.numeric("461", cmd + " :Not enough parameters.");
+}
+
+function IRCClient_numeric462() {
+	this.numeric("462", ":You may not re-register.");
+}
+
+function IRCClient_numeric481() {
+	this.numeric("481", ":Permission Denied.  "
+		+ "You do not have the correct IRC operator privileges.");
+}
+
+function IRCClient_numeric482(tmp_chan_nam) {
+	this.numeric("482", tmp_chan_nam + " :You're not a channel operator.");
+}
+
+//////////////////// Multi-numeric Display Functions ////////////////////
+
+function IRCClient_lusers() {
+	this.numeric(251,format(
+		":There are %d users and %d invisible on %d servers.",
+		num_noninvis_users(),
+		num_invis_users(),
+		true_array_len(Servers)
+	));
+	this.numeric(252, num_opers() + " :IRC operators online.");
+	var unknown_connections = true_array_len(Unregistered);
+	if (unknown_connections)
+		this.numeric(253, unknown_connections + " :unknown connection(s).");
+	this.numeric(254, true_array_len(Channels) + " :channels formed.");
+	this.numeric(255, ":I have " + true_array_len(Local_Users)
+		+ " clients and " + true_array_len(Local_Servers) + " servers.");
+	this.numeric(250, ":Highest connection count: " + hcc_total + " ("
+		+ hcc_users + " clients.)");
+	this.server_notice(hcc_counter + " clients have connected since "
+		+ strftime("%a %b %d %H:%M:%S %Y %Z",server_uptime));
+}
+
+function num_noninvis_users() {
+	var counter = 0;
+	for(myuser in Users) {
+		if (!(Users[myuser].mode&USERMODE_INVISIBLE))
+			counter++;
+	}
+	return counter;
+}
+
+function num_invis_users() {
+	var counter = 0;
+	for(myuser in Users) {
+		if (Users[myuser].mode&USERMODE_INVISIBLE)
+			counter++;
+	}
+	return counter;
+}
+
+function num_opers() {
+	var counter = 0;
+	for(myuser in Users) {
+		if (Users[myuser].mode&USERMODE_OPER)
+			counter++;
+	}
+	return counter;
+}
+
+function IRCClient_motd() {
+	var motd_file = new File(system.text_dir + "ircmotd.txt");
+	if (motd_file.exists==false) {
+		this.numeric(422, ":MOTD file missing: " + motd_file.name);
+	} else if (motd_file.open("r")==false) {
+		this.numeric(424, ":MOTD error " + errno + " opening: "
+			+ motd_file.name);
+	} else {
+		this.numeric(375, ":- " + servername + " Message of the Day -");
+		this.numeric(372, ":- " + strftime("%m/%d/%Y %H:%M",motd_file.date));
+		while (!motd_file.eof) {
+			motd_line = motd_file.readln();
+			if (motd_line != null)
+				this.numeric(372, ":- " + motd_line);
+		}
+		motd_file.close();
+	}
+	this.numeric(376, ":End of /MOTD command.");
+}
+
+function IRCClient_names(chan) {
+	var Channel_user;
+	var numnicks=0;
+	var tmp="";
+	for(thisChannel_user in chan.users) {
+		Channel_user=chan.users[thisChannel_user];
+		if (!(Channel_user.mode&USERMODE_INVISIBLE) ||
+		     (this.channels[chan.nam.toUpperCase()]) ) {
+			if (numnicks)
+				tmp += " ";
+			if (chan.modelist[CHANMODE_OP][Channel_user.id])
+				tmp += "@";
+			else if (chan.modelist[CHANMODE_VOICE][Channel_user.id])
+				tmp += "+";
+			tmp += Channel_user.nick;
+			numnicks++;
+			if (numnicks >= 59) {
+				// dump what we've got, it's too much.
+				this.numeric353(chan, tmp);
+				numnicks=0;
+				tmp="";
+			}
+		}
+	}
+	if(numnicks)
+		this.numeric353(chan, tmp);
+}
+
+// Traverse each channel the user is on and see if target is on any of the
+// same channels.
+function IRCClient_onchanwith(target) {
+	for (c in this.channels) {
+		for (i in target.channels) {
+			if (c == i)
+				return 1; // success
+		}
+	}
+	return 0; // failure.
+}
+
+//////////////////// Auxillary Functions ////////////////////
+
+function IRCClient_bcast_to_uchans_unique(str) {
+	var already_bcast = new Object;
+	for(thisChannel in this.channels) {
+		var userchannel=this.channels[thisChannel];
+		for (j in userchannel.users) {
+			var usr = userchannel.users[j];
+			if (!already_bcast[usr.nick] && (usr.id != this.id) && usr.local) {
+				usr.originatorout(str,this);
+				already_bcast[usr.nick] = true;
+			}
+		}
+	}
+}
+
+function IRCClient_bcast_to_list(chan, str, bounce, list_bit) {
+	for (thisUser in chan.users) {
+		var aUser = chan.users[thisUser];
+		if (aUser && ( aUser.id != this.id || (bounce) ) &&
+		    chan.modelist[list_bit][aUser.id])
+			aUser.originatorout(str,this);
+	}
+}
+
+function IRCClient_bcast_to_channel(chan, str, bounce) {
+	for(thisUser in chan.users) {
+		var aUser=chan.users[thisUser];
+		if (   ( aUser.id != this.id || (bounce) )
+			&& aUser.local
+		) {
+			aUser.originatorout(str,this);
+		}
+	}
+}
+
+function IRCClient_bcast_to_channel_servers(chan, str) {
+	var sent_to_servers = new Object;
+	for(thisUser in chan.users) {
+		var aUser=chan.users[thisUser];
+		if (   !aUser.local
+			&& (this.parent != aUser.parent)
+			&& !sent_to_servers[aUser.parent.toLowerCase()]
+		) {
+			aUser.originatorout(str,this);
+			sent_to_servers[aUser.parent.toLowerCase()] = true;
+		}
+	}
+}
+
+function IRCClient_check_nickname(newnick,squelch) {
+	var qline_nick;
+	var checknick;
+	var regexp;
+
+	newnick = newnick.slice(0,max_nicklen);
+	// If you're trying to NICK to yourself, drop silently.
+	if(newnick == this.nick)
+		return -1;
+	// First, check for valid nick against irclib.
+	if(IRC_check_nick(newnick)) {
+		if (!squelch)
+			this.numeric("432", newnick + " :Foobar'd Nickname.");
+		return 0;
+	}
+	// Second, check for existing nickname.
+	checknick = Users[newnick.toUpperCase()];
+	if(checknick && (checknick.nick != this.nick) ) {
+		if (!squelch)
+			this.numeric("433", newnick + " :Nickname is already in use.");
+		return 0;
+	}
+	// Third, match against Q:Lines
+	for (ql in QLines) {
+		if(wildmatch(newnick, QLines[ql].nick)) {
+			if (!squelch)
+				this.numeric(432, newnick + " :" + QLines[ql].reason);
+			return 0;
+		}
+	}
+	return 1; // if we made it this far, we're good!
+}
+
+function IRCClient_do_whois(wi) {
+	if (!wi)
+		return 0;
+	this.numeric(311, wi.nick + " " + wi.uprefix + " " + wi.hostname + " * :"
+		+ wi.realname);
+	var userchans="";
+	for (i in wi.channels) {
+		var chan = wi.channels[i];
+		if (!(chan.mode&CHANMODE_SECRET||chan.mode&CHANMODE_PRIVATE) ||
+		     this.channels[chan.nam.toUpperCase()] || this.mode&USERMODE_OPER) {
+			if (userchans)
+				userchans += " ";
+			if (chan.modelist[CHANMODE_OP][wi.id])
+				userchans += "@";
+			else if (chan.modelist[CHANMODE_VOICE][wi.id])
+				userchans += "+";
+			userchans += chan.nam;
+		}
+	}
+	if (userchans)
+		this.numeric(319, wi.nick + " :" + userchans);
+	if (wi.local)
+		this.numeric(312, wi.nick + " " + servername + " :" + serverdesc);
+	else {
+		wi_server = searchbyserver(wi.servername);
+		this.numeric(312, wi.nick + " " + wi_server.nick
+		+ " :" + wi_server.info);
+	}
+	if (wi.mode&USERMODE_OPER)
+		this.numeric(313, wi.nick + " :is an IRC operator.");
+	if (wi.away)
+		this.numeric(301, wi.nick + " :" + wi.away);
+	if (wi.local)
+		this.numeric(317, wi.nick + " " + (time() - wi.talkidle) + " "
+		+ wi.connecttime + " :seconds idle, signon time");
+}
+
+function IRCClient_services_msg(svcnick,send_str) {
+	var service_server;
+
+	if (!send_str) {
+		this.numeric412();
+		return 0;
+	}
+	// First, make sure the nick exists.
+	var usr = Users[svcnick.toUpperCase()];
+	if (!usr) {
+		this.numeric440(svcnick);
+		return 0;
+	}
+	service_server = searchbyserver(usr.servername);
+	if (!service_server || !service_server.uline) {
+		this.numeric440(svcnick);
+		return 0;
+	}
+	this.do_msg(svcnick,"PRIVMSG",send_str);
+}
+
+function IRCClient_global(target,type_str,send_str) {
+	if (!target.match("[.]")) {
+		this.numeric(413,target + " :No top-level domain specified.");
+		return 0;
+	}
+	var domain_part = target.split('.');
+	if (domain_part[domain_part.length - 1].match("[?*]")) {
+		this.numeric(414,target + " :Wildcard found in top-level domain.");
+		return 0;
+	}
+	var global_mask = target.slice(1);
+	var global_str = type_str + " " + target + " :" + send_str;
+	for(globClient in Local_Users) {
+		var Client = Local_Users[globClient];
+		if (target[0] == "#")
+			var global_match = Client.hostname;
+		else // assume $
+			var global_match = Client.servername;
+		if (wildmatch(global_match,global_mask))
+			Client.originatorout(global_str,this);
+	}
+	global_str = ":" + this.nick + " " + global_str;
+	if(this.local && this.parent) /* Incoming from a local server */
+		Servers[this.parent.toLowerCase()].bcast_to_servers_raw(global_str);
+	else if (this.flags&OLINE_CAN_GGNOTICE) /* From a local oper */
+		server_bcast_to_servers(global_str);
+	return 1;
+}
+
+function IRCClient_globops(str) {
+	var globops_bits = 0;
+	globops_bits |= USERMODE_OPER;
+	globops_bits |= USERMODE_GLOBOPS;
+	umode_notice(globops_bits,"Global","from " + this.nick +": " + str);
+	if (this.parent)
+		Servers[this.parent.toLowerCase()].bcast_to_servers_raw(":" + this.nick
+			+ " GLOBOPS :" + str);
+	else
+		server_bcast_to_servers(":" + this.nick + " GLOBOPS :" + str);
+}
+
+function IRCClient_do_msg(target,type_str,send_str) {
+	if (   (target[0] == "$")
+		&& (this.mode&USERMODE_OPER)
+		&& ((this.flags&OLINE_CAN_LGNOTICE) || !this.local)
+	) {
+		return this.global(target,type_str,send_str);
+	}
+
+	var send_to_list = -1;
+	if (target[0] == "@" && ( (target[1] == "#") || target[1] == "&") ) {
+		send_to_list = CHANMODE_OP;
+		target = target.slice(1);
+	} else if (target[0]=="+" && ((target[1] == "#")|| target[1] == "&")) {
+		send_to_list = CHANMODE_VOICE;
+		target = target.slice(1);
+	}
+
+	if ((target[0] == "#") || (target[0] == "&")) {
+		var chan = Channels[target.toUpperCase()];
+		if (!chan) {
+			// check to see if it's a #*hostmask* oper message
+			if ( (target[0] == "#") && (this.mode&USERMODE_OPER) &&
+			     ( (this.flags&OLINE_CAN_LGNOTICE) || !this.local )
+			   ) {
+				return this.global(target,type_str,send_str);
+			} else {
+				this.numeric401(target);
+				return 0;
+			}
+		}
+		if ((chan.mode&CHANMODE_NOOUTSIDE)
+			&& !this.channels[chan.nam.toUpperCase()]) {
+			this.numeric(404, chan.nam + " :Cannot send to channel "
+				+ "(+n: no outside messages)");
+			return 0;
+		}
+		if (   (chan.mode&CHANMODE_MODERATED)
+			&& !chan.modelist[CHANMODE_VOICE][this.id]
+			&& !chan.modelist[CHANMODE_OP][this.id]
+		) {
+			this.numeric(404,
+				format("%s :Can't send to channel (+m: moderated)"),
+				chan.nam
+			);
+			return 0;
+		}
+		if (   chan.isbanned(this.nuh)
+			&& !chan.modelist[CHANMODE_VOICE][this.id]
+			&& !chan.modelist[CHANMODE_OP][this.id]
+		) {
+			this.numeric(404,
+				format("%s :Can't send to channel (+b: you're banned!)"),
+				chan.nam
+			);
+			return 0;
+		}
+		if(send_to_list == -1) {
+			var str = type_str +" "+ chan.nam +" :"+ send_str;
+			this.bcast_to_channel(chan, str, false);
+			this.bcast_to_channel_servers(chan, str);
+		} else {
+			var prefix_chr;
+			if (send_to_list == CHANMODE_OP)
+				prefix_chr="@";
+			else if (send_to_list == CHANMODE_VOICE)
+				prefix_chr="+";
+			var str = type_str +" " + prefix_chr + chan.nam + " :"+ send_str;
+			this.bcast_to_list(chan, str, false, send_to_list);
+			this.bcast_to_channel_servers(chan, str);
+		}
+	} else {
+		if (target.match("[@]")) {
+			var msg_arg = target.split('@');
+			var real_target = msg_arg[0];
+			var target_server = searchbyserver(msg_arg[1]);
+			if (!target_server) {
+				this.numeric401(target);
+				return 0;
+			}
+			if (target_server == -1) {
+				this.numeric(407, target
+					+ " :Duplicate recipients, no message delivered.");
+				return 0;
+			}
+			target = msg_arg[0] + "@" + msg_arg[1];
+		} else {
+			var real_target = target;
+		}
+		var target_socket = Users[real_target.toUpperCase()];
+		if (target_socket) {
+			if (target_server && (target_server.parent != target_socket.parent)) {
+				this.numeric401(target);
+				return 0;
+			}
+			if (target_server && (target_server.id == target_socket.parent) ) {
+				target = real_target;
+			}
+			if (target_socket.issilenced(this.nuh))
+				return 0;	/* On SILENCE list.  Silently ignore. */
+			var str = type_str + " " + target + " :" + send_str;
+			target_socket.originatorout(str,this);
+			if (   target_socket.away
+				&& (type_str == "PRIVMSG")
+				&& !this.server
+				&& target_socket.local
+			) {
+				this.numeric(301, format("%s :%s",
+					target_socket.nick,
+					target_socket.away
+				));
+			}
+		} else {
+			this.numeric401(target);
+			return 0;
+		}
+	}
+	return 1;
+}
+
+function IRCClient_do_admin() {
+	umode_notice(USERMODE_STATS_LINKS,"StatsLinks",format(
+		"ADMIN requested by %s (%s@%s) [%s]",
+		this.nick,
+		this.uprefix,
+		this.hostname,
+		this.servername
+	));
+	if (Admin1 && Admin2 && Admin3) {
+		this.numeric(256, ":Administrative info about " + servername);
+		this.numeric(257, ":" + Admin1);
+		this.numeric(258, ":" + Admin2);
+		this.numeric(259, ":" + Admin3);
+	} else {
+		this.numeric(423, servername
+			+ " :No administrative information available.");
+	}
+}
+
+function IRCClient_do_info() {
+	umode_notice(USERMODE_STATS_LINKS,"StatsLinks",format(
+		"INFO requested by %s (%s@%s) [%s]",
+		this.nick,
+		this.uprefix,
+		this.hostname,
+		this.servername
+	));
+	this.numeric(371, ":--=-=-=-=-=-=-=-=-=*[ The Synchronet IRCd v1.9a ]*=-=-=-=-=-=-=-=-=--");
+	this.numeric(371, ":   IRCd Copyright 2003-2021 by Randy Sommerfeld <cyan@synchro.net>");
+	this.numeric(371, ":" + system.version_notice + " " + system.copyright + ".");
+	this.numeric(371, ":--=-=-=-=-=-=-=-=-( A big thanks to the following )-=-=-=-=-=-=-=-=--");
+	this.numeric(371, ":DigitalMan (Rob Swindell): Resident coder god, various hacking all");
+	this.numeric(371, ":   around the IRCd, countless helpful suggestions and tips, and");
+	this.numeric(371, ":   tons of additions to the Synchronet JS API that made this possible.");
+	this.numeric(371, ":Deuce (Stephen Hurd): Resident Perl guru and ex-Saskatchewan zealot.");
+	this.numeric(371, ":   Originally converted the IRCd to be object-oriented, various small");
+	this.numeric(371, ":   hacks, and lots of guidance.");
+	this.numeric(371, ":Thanks to the DALnet Bahamut team for their help from time to time.");
+	this.numeric(371, ":Greets to: Arrak, ElvishMerchant, Foobar, Grimp, Kufat,");
+	this.numeric(371, ":   Psyko, Samael, Shaun, Torke, and all the #square oldbies.");
+	this.numeric(371, ":--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--");
+	this.numeric(371, ":Synchronet " + system.full_version);
+	this.numeric(371, ":Compiled with " + system.compiled_with + " at " + system.compiled_when);
+	this.numeric(371, ":Running on " + system.os_version);
+	this.numeric(371, ":Utilizing socket library: " + system.socket_lib);
+	this.numeric(371, ":Javascript library: " + system.js_version);
+	this.numeric(371, ":This BBS has been up since " + system.timestr(system.uptime));
+	this.numeric(371, ": -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -");
+	if (server.version_detail!=undefined) {
+		this.numeric(371, ":This IRCd was executed via:");
+		this.numeric(371, ":" + server.version_detail);
+	}
+	this.numeric(371, ":IRCd git commit hashes:")
+	this.numeric(371, ":XXX FIXME XXX");
+	this.numeric(371, ":IRClib Version: " + IRCLIB_VERSION);
+	this.numeric(371, ":--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--");
+	this.numeric(371, ":This program is distributed under the terms of the GNU General Public");
+	this.numeric(371, ":License, version 2. https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt");
+	this.numeric(371, ":--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--");
+	this.numeric(374, ":End of /INFO list.");
+}
+
+function IRCClient_do_stats(statschar) {
+	switch(statschar[0]) {
+		case "C":
+		case "c":
+			var cline_port;
+			for (cline in CLines) {
+				if(CLines[cline].port)
+					cline_port = CLines[cline].port;
+				else
+					cline_port = "*";
+				this.numeric(213,"C " + CLines[cline].host + " * "
+					+ CLines[cline].servername + " " + cline_port + " "
+					+ CLines[cline].ircclass);
+				if (NLines[cline])
+					this.numeric(214,"N " + NLines[cline].host + " * "
+						+ NLines[cline].servername + " " + NLines[cline].flags
+						+ " " + NLines[cline].ircclass);
+			}
+			break;  
+		case "H":
+		case "h":
+			for (hl in HLines) {
+				this.numeric(244,"H " + HLines[hl].allowedmask + " * "
+					+ HLines[hl].servername);
+			}
+			break;
+		case "I":
+		case "i":
+			var my_port;
+			for (iline in ILines) {
+				if (!ILines[iline].port)
+					my_port = "*";
+				else
+					my_port = ILines[iline].port;
+				this.numeric(215,"I " + ILines[iline].ipmask + " * "
+					+ ILines[iline].hostmask + " " + my_port + " "
+					+ ILines[iline].ircclass);
+			}
+			break;
+		case "K":
+		case "k":
+			for (kline in KLines) {
+				if (KLines[kline].hostmask) {
+					this.numeric(216, KLines[kline].type + " "
+						+ KLines[kline].hostmask + " * * :"
+						+ KLines[kline].reason);
+				}
+			}
+			break;
+		case "L": /* FIXME */
+			this.numeric(241,"L <hostmask> * <servername> <maxdepth>");
+			break;
+		case "l": /* FIXME */
+			this.numeric(211,"<linkname> <sendq> <sentmessages> <sentbytes> <receivedmessages> <receivedbytes> <timeopen>");
+			break;
+		case "M":
+		case "m":
+			for (c in Profile) {
+				var sm = Profile[c];
+				this.numeric(212, c + " " + sm.ticks + " " + sm.executions);
+			}
+			break;
+		case "O":
+		case "o":
+			for (oline in OLines) {
+				this.numeric(243, "O " + OLines[oline].hostmask + " * "
+					+ OLines[oline].nick);
+			}
+			break;
+		case "U":
+			for (uline in ULines) {
+				this.numeric(246, "U " + ULines[uline] + " * * 0 -1");
+			}
+			break;
+		case "u":
+			var this_uptime=time() - server_uptime;
+			var updays=Math.floor(this_uptime / 86400);
+			if (updays)
+				this_uptime %= 86400;
+			var uphours=Math.floor(this_uptime/(60*60));
+			var upmins=(Math.floor(this_uptime/60))%60;
+			var upsec=this_uptime%60;
+			var str = format("Server Up %u days, %u:%02u:%02u",
+				updays,uphours,upmins,upsec);
+			this.numeric(242,":" + str);
+			break;
+		case "Y":
+		case "y":
+			var yl;
+			for (thisYL in YLines) {
+				yl = YLines[thisYL];
+				this.numeric(218,"Y " + thisYL + " " + yl.pingfreq + " "
+					+ yl.connfreq + " " + yl.maxlinks + " " + yl.sendq);
+			}
+			break;
+		default:
+			break;
+	}
+	this.numeric(219, statschar[0] + " :End of /STATS Command.");
+}
+
+function IRCClient_do_users() {
+	var usersshown;
+	var u;
+
+	this.numeric(392,':UserID                    Terminal  Host');
+	usersshown=0;
+	for(node in system.node_list) {
+		if(system.node_list[node].status == NODE_INUSE) {
+			u=new User(system.node_list[node].useron);
+			this.numeric(393,format(':%-25s %-9s %-30s',u.alias,'Node'+node,
+				u.host_name));
+			usersshown++;
+		}
+	}
+	if(usersshown) {
+		this.numeric(394,':End of users');
+	} else {
+		this.numeric(395,':Nobody logged in');
+	}
+}
+
+function IRCClient_do_summon(summon_user) {
+	var usernum;
+	var isonline;
+
+	// Check if exists.
+	usernum = system.matchuser(summon_user);
+	if(!usernum)
+		this.numeric(444,":No such user.");
+	else {
+		// Check if logged in
+		isonline = 0;
+		for(node in system.node_list) {
+			if( (system.node_list[node].status == NODE_INUSE)
+				&& (system.node_list[node].useron == usernum) ) {
+				isonline = 1;
+				break;
+			}
+		}
+		if(!isonline) {
+			this.numeric(444,":User not logged in.");
+		} else {
+			system.put_telegram(usernum, "" + this.nick
+				+ " is summoning you to IRC chat.\r\n");
+			this.numeric(342, summon_user + " :Summoning user to IRC");
+		}
+	}
+}
+
+function IRCClient_do_links(mask) {
+	if (!mask)
+		mask = "*";
+	umode_notice(USERMODE_STATS_LINKS,"StatsLinks",format(
+		"LINKS %s requested by %s (%s@%s) [%s]",
+		mask,
+		this.nick,
+		this.uprefix,
+		this.hostname,
+		this.servername
+	));
+	for(thisServer in Servers) {
+		var Server=Servers[thisServer];
+		if (wildmatch(Server.nick,mask)) {
+			this.numeric(364, Server.nick + " " + Server.linkparent + " :"
+				+ Server.hops + " " + Server.info);
+		}
+	}
+	if (wildmatch(servername,mask))
+		this.numeric(364, servername + " " + servername + " :0 " + serverdesc);
+	this.numeric(365, mask + " :End of /LINKS list.");
+}
+
+// Don't hunt for servers based on nicknames, as TRACE is more explicit.
+function IRCClient_do_trace(target) {
+	var server = searchbyserver(target);
+
+	if (server == -1) { // we hunted ourselves
+		// FIXME: What do these numbers mean? O_o?
+		this.numeric206("30","1","0",servername);
+		this.trace_all_opers();
+	} else if (server) {
+		server.rawout(":" + this.nick + " TRACE " + target);
+		this.numeric200(target,server.nick);
+		return 0;
+	} else {
+		// Okay, we've probably got a user.
+		var nick = Users[target.toUpperCase()];
+		if (!nick) {
+			this.numeric402(target);
+			return 0;
+		} else if (nick.local) {
+			if (nick.mode&USERMODE_OPER)
+				this.numeric204(nick);
+			else
+				this.numeric205(nick);
+		} else {
+			nick.rawout(":" + this.nick + " TRACE " + target);
+			this.numeric200(target,Servers[nick.parent.toLowerCase()].nick);
+		}
+	}
+	this.numeric(262, target + " :End of /TRACE.");
+}
+
+function IRCClient_trace_all_opers() {
+	for(thisoper in Local_Users) {
+		var oper=Local_Users[thisoper];
+		if (oper.mode&USERMODE_OPER)
+			this.numeric204(oper);
+	}
+}
+
+function IRCClient_do_connect(con_server,con_port) {
+	var con_cline = "";
+	for (ccl in CLines) {
+		if (wildmatch(CLines[ccl].servername,con_server) ||
+		    wildmatch(CLines[ccl].host,con_server) ) {
+			con_cline = CLines[ccl];
+			break;
+		}
+	}
+	if (!con_cline) {
+		this.numeric402(con_server);
+		return 0;
+	}
+	if (!con_port && con_cline.port)
+		con_port = con_cline.port;
+	if (!con_port && !con_cline.port)
+		con_port = String(default_port);
+	if (!con_port.match(/^[0-9]+$/)) {
+		this.server_notice("Invalid port: " + con_port);
+		return 0;
+	}
+	var msg = " CONNECT " + con_cline.servername + " " + con_port +
+		" from " + this.nick + "[" + this.uprefix + "@" +
+		this.hostname + "]";
+	var con_type = "Local";
+	if (this.parent) {
+		con_type = "Remote";
+		server_bcast_to_servers("GNOTICE :Remote" + msg);
+	}
+	umode_notice(USERMODE_ROUTING,"Routing","from "+servername+": " + 
+		con_type + msg);
+	connect_to_server(con_cline,con_port);
+	return 1;
+}
+
+function IRCClient_do_basic_who(whomask) {
+	var eow = "*";
+
+	var regexp = "^[0]{1,}$";
+	if (whomask.match(regexp))
+		whomask = "*";
+
+	if ((whomask[0] == "#") || (whomask[0] == "&")) {
+		var chan = Channels[whomask.toUpperCase()];
+		if (   chan
+			&& (
+				   (!(chan.mode&CHANMODE_SECRET) && !(chan.mode&CHANMODE_PRIVATE))
+				|| this.channels[chan.nam.toUpperCase()]
+				|| (this.mode&USERMODE_OPER)
+			)
+		) {
+			for(i in chan.users) {
+				var usr = chan.users[i];
+				if (   !(usr.mode&USERMODE_INVISIBLE)
+					|| (this.mode&USERMODE_OPER)
+					|| this.onchanwith(usr)
+				) {
+					var chkwho = this.numeric352(usr,false,chan);
+					if (!chkwho) {
+						umode_notice(USERMODE_OPER,"Notice",format(
+							"WHO returned 0 for user: %s (A)",
+							usr.nick
+						));
+					}
+				}
+			}
+			eow = chan.nam;
+		}
+	} else {
+		for (i in Users) {
+			var usr = Users[i];
+			if (   usr.match_who_mask(whomask)
+				&& (
+					   !(usr.mode&USERMODE_INVISIBLE)
+					|| (this.mode&USERMODE_OPER)
+					|| this.onchanwith(usr)
+				)
+			) {
+				var chkwho = this.numeric352(usr);
+				if (!chkwho) {
+					umode_notice(USERMODE_OPER,"Notice",format(
+						"WHO returned 0 for user: %s (B)",
+						usr.nick
+					));
+				}
+			}
+		}
+		eow = whomask;
+	}
+	this.numeric(315, eow + " :End of /WHO list. (Basic)");
+}
+
+function IRCClient_do_complex_who(cmd) {
+	var who = new Who();
+	var tmp;
+	var eow = "*";
+	var add = true;	/* We assume + so things like 'MODE #chan o cyan' work */
+	var arg = 1;
+	var whomask = "";
+	var chan;
+
+	// RFC1459 Compatibility.  "WHO <mask> o"  Only do it if we find a
+	// wildcard, otherwise assume we're doing a complex WHO with 'modes'
+	if (  cmd[2]
+		&& (
+			   cmd[1].match(/[*]/)
+			|| cmd[1].match(/[?]/)
+		)
+		|| cmd[1].match(/[0]/)
+		&& !cmd[3]
+		&& (cmd[2].toLowerCase() == "o")
+	) {
+		tmp = cmd[1];
+		cmd[1] = cmd[2];
+		cmd[2] = tmp;
+	}
+
+	for (myflag in cmd[1]) {
+		switch (cmd[1][myflag]) {
+			case "+":
+				if (!add)
+					add = true;
+				break;
+			case "-":
+				if (add)
+					add = false;
+				break;
+			case "a":
+				who.tweak_mode(WHO_AWAY,add);
+				break;
+			case "c":
+				arg++;
+				if (cmd[arg]) {
+					who.tweak_mode(WHO_CHANNEL,add);
+					who.Channel = cmd[arg];
+				}
+				break;
+			case "g":
+				arg++;
+				if (cmd[arg]) {
+					who.tweak_mode(WHO_REALNAME,add);
+					who.RealName = cmd[arg];
+				}
+				break;
+			case "h":
+				arg++;
+				if (cmd[arg]) {
+					who.tweak_mode(WHO_HOST,add);
+					who.Host = cmd[arg];
+				}
+				break;
+			case "i":
+				arg++;
+				if (cmd[arg]) {
+					who.tweak_mode(WHO_IP,add);
+					who.IP = cmd[arg];
+				}
+				break;
+			case "l":
+				arg++;
+				if (cmd[arg]) {
+					who.tweak_mode(WHO_CLASS,add);
+					who.Class = parseInt(cmd[arg]);
+				}
+			case "m": // we never set -m
+				arg++;
+				if (cmd[arg]) {
+					who.tweak_mode(WHO_UMODE,true);
+					if (!add) {
+						var tmp_umode = "";
+						if (   (cmd[arg][0] != "+")
+							|| (cmd[arg][0] != "-")
+						) {
+							tmp_umode += "+";
+						}
+						tmp_umode += cmd[arg].replace(/[-]/g," ");
+						tmp_umode = tmp_umode.replace(/[+]/g,"-");
+						who.UMode = tmp_umode.replace(/[ ]/g,"+");
+					} else {
+						who.UMode = cmd[arg];
+					}
+				}
+				break;
+			case "n":
+				arg++;
+				if (cmd[arg]) {
+					who.Nick = cmd[arg];
+					who.tweak_mode(WHO_NICK,add);
+				}
+				break;
+			case "o":
+				who.tweak_mode(WHO_OPER,add);
+				break;
+			case "s":
+				arg++;
+				if (cmd[arg]) {
+					who.Server = cmd[arg];
+					who.tweak_mode(WHO_SERVER,add);
+				}
+				break;
+			case "t":
+				arg++;
+				if (cmd[arg]) {
+					who.Time = parseInt(cmd[arg]);
+					who.tweak_mode(WHO_TIME,add);
+				}
+				break;
+			case "u":
+				arg++;
+				if (cmd[arg]) {
+					who.User = cmd[arg];
+					who.tweak_mode(WHO_USER,add);
+				}
+				break;
+			case "C":
+				who.tweak_mode(WHO_FIRST_CHANNEL,add);
+				break;
+			case "M":
+				who.tweak_mode(WHO_MEMBER_CHANNEL,add);
+				break;
+			case "I":
+				who.tweak_mode(WHO_SHOW_IPS_ONLY,add);
+				break;
+			default:
+				break;
+		}
+	}
+
+	// Check to see if the user passed a generic mask to us for processing.
+	arg++;
+	if (cmd[arg])
+		whomask = cmd[arg];
+
+	var regexp = "^[0]{1,}$";
+	if (whomask.match(regexp))
+		whomask = "*";
+
+	// allow +c/-c to override.
+	if (!who.Channel && ((whomask[0] == "#") || (whomask[0] == "&")))
+		who.Channel = whomask;
+
+	// Strip off any @ or + in front of a channel and set the flags.
+	var sf_op = false;
+	var sf_voice = false;
+	var sf_done = false;
+	var tmp_wc = who.Channel;
+	for (cc in tmp_wc) {
+		switch(tmp_wc[cc]) {
+			case "@":
+				sf_op = true;
+				who.Channel = who.Channel.slice(1);
+				break;
+			case "+":
+				sf_voice = true;
+				who.Channel = who.Channel.slice(1);
+				break;
+			default: // assume we're done
+				sf_done = true;
+				break;
+		}
+		if (sf_done)
+			break;
+	}
+	delete tmp_wc; // don't need this anymore.
+
+	// Now we traverse everything and apply the criteria the user passed.
+	var who_count = 0;
+	for (who_client in Users) {
+		var wc = Users[who_client];
+		var flag_M = this.onchanwith(wc);
+
+		if (   (wc.mode&USERMODE_INVISIBLE)
+			&& !(this.mode&USERMODE_OPER)
+			&& !flag_M
+		) {
+			continue;
+		}
+
+		if ((who.add_flags&WHO_AWAY) && !wc.away)
+			continue;
+		else if ((who.del_flags&WHO_AWAY) && wc.away)
+			continue;
+		if (who.add_flags&WHO_CHANNEL) {
+			if (!wc.channels[who.Channel.toUpperCase()])
+				continue;
+			if (   sf_op
+				&& Channels[who.Channel.toUpperCase()]
+				&& !Channels[who.Channel.toUpperCase()].modelist[CHANMODE_OP][wc.id]
+			) {
+				continue;
+			}
+			if (   sf_voice
+				&& Channels[who.Channel.toUpperCase()]
+				&& !Channels[who.Channel.toUpperCase()].modelist[CHANMODE_VOICE][wc.id]
+			) {
+				continue;
+			}
+		} else if (who.del_flags&WHO_CHANNEL) {
+			if (wc.channels[who.Channel.toUpperCase()])
+				continue;
+			if (   sf_op
+				&& Channels[who.Channel.toUpperCase()]
+				&& Channels[who.Channel.toUpperCase()].modelist[CHANMODE_OP][wc.id]
+			) {
+				continue;
+			}
+			if (   sf_voice
+				&& Channels[who.Channel.toUpperCase()]
+				&& Channels[who.Channel.toUpperCase()].modelist[CHANMODE_VOICE][wc.id]
+			) {
+				continue;
+			}
+		}
+		if ((who.add_flags&WHO_REALNAME) && !wildmatch(wc.realname,who.RealName)) {
+			continue;
+		} else if ((who.del_flags&WHO_REALNAME) && wildmatch(wc.realname,who.RealName)) {
+			continue;
+		}
+		if ((who.add_flags&WHO_HOST) && !wildmatch(wc.hostname,who.Host)) {
+			continue;
+		} else if ((who.del_flags&WHO_HOST) && wildmatch(wc.hostname,who.Host)) {
+			continue;
+		}
+		if ((who.add_flags&WHO_IP) && !wildmatch(wc.ip,who.IP)) {
+			continue;
+		} else if ((who.del_flags&WHO_IP) && wildmatch(wc.ip,who.IP)) {
+			continue;
+		}
+		if (who.add_flags&WHO_UMODE) { // no -m
+			var sic = false;
+			var madd = true;
+			for (mm in who.UMode) {
+				switch(who.UMode[mm]) {
+					case "+":
+						if (!madd)
+							madd = true;
+						break;
+					case "-":
+						if (madd)
+							madd = false;
+						break;
+					case "o":
+					case "i":
+					case "w":
+					case "b":
+					case "g":
+					case "s":
+					case "c":
+					case "r":
+					case "k":
+					case "f":
+					case "y":
+					case "d":
+					case "n":
+					case "h":
+					case "F":
+						if (
+						   (!madd && (wc.mode&
+						   USERMODE_CHAR
+							[who.UMode[mm]])
+						   )
+						||
+						   (madd && !(wc.mode&
+						   USERMODE_CHAR
+							[who.UMode[mm]])
+						   ) )
+							sic = true;
+						break;
+					default:
+						break;
+				}
+				if (sic)
+					break;
+			}
+			if (sic)
+				continue;
+		}
+		if ((who.add_flags&WHO_NICK) && !wildmatch(wc.nick,who.Nick))
+			continue;
+		else if ((who.del_flags&WHO_NICK) && wildmatch(wc.nick,who.Nick))
+			continue;
+		if ((who.add_flags&WHO_OPER) && !(wc.mode&USERMODE_OPER))
+			continue;
+		else if ((who.del_flags&WHO_OPER) && (wc.mode&USERMODE_OPER))
+			continue;
+		if ((who.add_flags&WHO_SERVER) && !wildmatch(wc.servername,who.Server))
+			continue;
+		else if ((who.del_flags&WHO_SERVER) && wildmatch(wc.servername,who.Server))
+			continue;
+		if ((who.add_flags&WHO_USER) && !wildmatch(wc.uprefix,who.User))
+			continue;
+		else if ((who.del_flags&WHO_USER) && wildmatch(wc.uprefix,who.User))
+			continue;
+		if ((who.add_flags&WHO_MEMBER_CHANNEL) && !flag_M)
+			continue;
+		else if ((who.del_flags&WHO_MEMBER_CHANNEL) && flag_M)
+			continue;
+		if ((who.add_flags&WHO_TIME) && ((time() - wc.connecttime) < who.Time))
+			continue;
+		else if ((who.del_flags&WHO_TIME) && ((time() - wc.connecttime) > who.Time))
+			continue;
+		if ((who.add_flags&WHO_CLASS) && (wc.ircclass != who.Class))
+			continue;
+		else if ((who.del_flags&WHO_CLASS) && (wc.ircclass == who.Class))
+			continue;
+
+		if (whomask && !wc.match_who_mask(whomask))
+			continue;
+
+		chan = "";
+		if ((who.add_flags&WHO_FIRST_CHANNEL) && !who.Channel) {
+			for (x in wc.channels) {
+				if (!(Channels[x].mode&CHANMODE_SECRET
+						|| Channels[x].mode&CHANMODE_PRIVATE)
+					|| this.channels[x] || this.mode&USERMODE_OPER)
+				{
+					chan = Channels[x].nam;
+					break;
+				}
+			}
+		} else if (who.Channel) {
+			chan = who.Channel;
+		}
+
+		var show_ips_only;
+		if (who.add_flags&WHO_SHOW_IPS_ONLY)
+			show_ips_only = true;
+		else
+			show_ips_only = false;
+
+		// If we made it this far, we're good.
+		if (chan && Channels[chan.toUpperCase()]) {
+			var chkwho = this.numeric352(wc,show_ips_only,
+				Channels[chan.toUpperCase()]);
+			if (!chkwho) {
+				umode_notice(USERMODE_OPER,"Notice",
+					"WHO returned 0 for user: " + wc.nick + " (C)");
+			}
+		} else {
+			var chkwho = this.numeric352(wc,show_ips_only);
+			if (!chkwho) {
+				umode_notice(USERMODE_OPER,"Notice",
+					"WHO returned 0 for user: " + wc.nick + " (D)");
+			}
+		}
+		who_count++;
+
+		if (!(this.mode&USERMODE_OPER) && (who_count >= max_who))
+			break;
+	}
+
+	if (who.Channel)
+		eow = who.Channel;
+	else if (cmd[2])
+		eow = cmd[2];
+
+	this.numeric(315, eow + " :End of /WHO list. (Complex)");
+}
+
+// Object which stores WHO bits and arguments.
+function Who() {
+	this.add_flags = 0;
+	this.del_flags = 0;
+	this.tweak_mode = Who_tweak_mode;
+	this.Channel = "";
+	this.RealName = "";
+	this.Host = "";
+	this.IP = "";
+	this.UMode = "";
+	this.Nick = "";
+	this.Server = "";
+	this.User = "";
+	this.Time = 0;
+	this.Class = 0;
+}
+
+function Who_tweak_mode(bit,add) {
+	if (add) {
+		this.add_flags |= bit;
+		this.del_flags &= ~bit;
+	} else {
+		this.add_flags &= ~bit;
+		this.del_flags |= bit;
+	}
+}
+
+// Take a generic mask in and try to figure out if we're matching a channel,
+// mask, nick, or something else.  Return 1 if the user sent to us matches it
+// in some fashion.
+function IRCClient_match_who_mask(mask) {
+	if ((mask[0] == "#") || (mask[0] == "&")) { // channel
+		if (Channels[mask.toUpperCase()])
+			return 1;
+		else
+			return 0; // channel doesn't exist.
+	} else if (mask.match(/[!]/)) { // nick!user@host
+		if (   wildmatch(this.nick,mask.split("!")[0])
+			&& wildmatch(this.uprefix,mask.slice(mask.indexOf("!")+1).split("@")[0])
+			&& wildmatch(this.hostname,mask.slice(mask.indexOf("@")+1))
+		) {
+			return 1;
+		}
+	} else if (mask.match(/[@]/)) { // user@host
+		if (   wildmatch(this.uprefix,mask.split("@")[0])
+			&& wildmatch(this.hostname,mask.split("@")[1])
+		) {
+			return 1;
+		}
+	} else if (mask.match(/[.]/)) { // host only
+		if ( wildmatch(this.hostname,mask) )
+			return 1;
+	} else { // must be a nick?
+		if ( wildmatch(this.nick,mask) )
+			return 1;
+	}
+	return 0;
+}
+
+function IRCClient_do_who_usage() {
+	this.numeric(334,":/WHO [+|-][acghilmnostuCIM] <args> <mask>");
+	this.numeric(334,":The modes as above work exactly like channel modes.");
+	this.numeric(334,":<mask> may be '*' or in nick!user@host notation.");
+	this.numeric(334,":i.e. '/WHO +a *.ca' would match all away users from *.ca");
+	this.numeric(334,":No args/mask matches to everything by default.");
+	this.numeric(334,":a       : User is away.");
+	this.numeric(334,":c <chan>: User is on <@+><#channel>, no wildcards. Can check +o/+v.");
+	this.numeric(334,":g <rnam>: Check against realname field, wildcards allowed.");
+	this.numeric(334,":h <host>: Check user's hostname, wildcards allowed.");
+	this.numeric(334,":i <ip>  : Check against IP address, wildcards allowed.");
+	this.numeric(334,":l <clas>: User is a member of <clas> irc class as defined on a Y:Line.");
+	this.numeric(334,":m <umde>: User has <umodes> set, -+ allowed.");
+	this.numeric(334,":n <nick>: User's nickname matches <nick>, wildcards allowed.");
+	this.numeric(334,":o       : User is an IRC Operator.");
+	this.numeric(334,":s <srvr>: User is on <server>, wildcards allowed.");
+	this.numeric(334,":t <time>: User has been on for more than (+) or less than (-) <time> secs.");
+	this.numeric(334,":u <user>: User's username field matches, wildcards allowed.");
+	this.numeric(334,":C       : Only display first channel that the user matches.");
+	this.numeric(334,":I       : Only return IP addresses (as opposed to hostnames.)");
+	this.numeric(334,":M       : Only check against channels you're a member of.");
+	this.numeric(315,"? :End of /WHO list. (Usage)");
+}
+
+function IRCClient_do_basic_list(mask) {
+	this.numeric321();
+	// Only allow commas if we're not doing wildcards, otherwise strip
+	// off anything past the first comma and pass to the generic parser
+	// to see if it can make heads or tails out of it.
+	if (mask.match(/[,]/)) {
+		if (mask.match(/[*?]/)) {
+			mask = mask.slice(0,mask.indexOf(","))
+		} else { // parse it out, but junk anything that's not a chan
+			var my_split = mask.split(",");
+			for (myChan in my_split) {
+				if (Channels[my_split[myChan].toUpperCase()])
+					this.numeric322(Channels[my_split[myChan].toUpperCase()]);
+			}
+			// our adventure ends here.
+			this.numeric(323, ":End of /LIST. (Basic: Comma-list)");
+			return;
+		}
+	}
+	for (chan in Channels) {
+		if (Channels[chan] && Channels[chan].match_list_mask(mask))
+			this.numeric322(Channels[chan]);
+	}
+	this.numeric(323, ":End of /LIST. (Basic)");
+	return;
+}
+
+// So, the user wants to go the hard way...
+function IRCClient_do_complex_list(cmd) {
+	var add = true;
+	var arg = 1;
+	var list = new List();
+	var listmask;
+	var listmask_items;
+
+	this.numeric321();
+
+	for (lc in cmd[1]) {
+		switch(cmd[1][lc]) {
+			case "+":
+				if (!add)
+					add = true;
+				break;
+			case "-":
+				if (add)
+					add = false;
+				break;
+			case "a":
+				arg++;
+				if (cmd[arg]) {
+					list.tweak_mode(LIST_CHANMASK,add);
+					list.Mask = cmd[arg];
+				}
+				break;
+			case "c":
+				arg++;
+				if (cmd[arg]) {
+					list.tweak_mode(LIST_CREATED,add);
+					list.Created = parseInt(cmd[arg])*60;
+				}
+				break;
+			case "m": // we never set -m, inverse.
+				arg++;
+				if (cmd[arg]) {
+					list.tweak_mode(LIST_MODES,true);
+					if (!add) {
+						var tmp_mode = "";
+						if((cmd[arg][0] != "+") ||
+						   (cmd[arg][0] != "-") )
+							tmp_mode += "+";
+						tmp_mode += cmd[arg].replace(/[-]/g," ");
+						tmp_mode = tmp_mode.replace(/[+]/g,"-");
+						list.Modes = tmp_mode.replace(/[ ]/g,"+");
+					} else {
+						list.Modes = cmd[arg];
+					}
+				}
+				break;
+			case "o":
+				arg++;
+				if (cmd[arg]) {
+					list.tweak_mode(LIST_TOPIC,add);
+					list.Topic = cmd[arg];
+				}
+				break;
+			case "p":
+				arg++;
+				if (cmd[arg]) {
+					list.tweak_mode(LIST_PEOPLE,add);
+					list.People = parseInt(cmd[arg]);
+				}
+				break;
+			case "t":
+				arg++;
+				if (cmd[arg]) {
+					list.tweak_mode(LIST_TOPICAGE,add);
+					list.TopicTime = parseInt(cmd[arg])*60;
+				}
+				break;
+			case "M":
+				list.tweak_mode(LIST_DISPLAY_CHAN_MODES,add);
+				break;
+			default:
+				break;
+		}
+	}
+
+	// Generic mask atop all this crap?
+	arg++;
+	if (cmd[arg])
+		listmask = cmd[arg];
+
+	// Here we go...
+	for (aChan in Channels) {
+		// Is the user allowed to see this channel, for starters?
+		if (!(Channels[aChan].mode&CHANMODE_SECRET) ||
+		     (this.mode&USERMODE_OPER) || this.channels[aChan]) {
+			
+			if ((list.add_flags&LIST_CHANMASK) &&
+			    !wildmatch(aChan,list.Mask.toUpperCase()))
+				continue;
+			else if ((list.del_flags&LIST_CHANMASK) &&
+			    wildmatch(aChan,list.Mask.toUpperCase()))
+				continue;
+			if ((list.add_flags&LIST_CREATED) &&
+			   (Channels[aChan].created < (time() - list.Created)))
+				continue;
+			else if ((list.del_flags&LIST_CREATED) &&
+			   (Channels[aChan].created > (time() - list.Created)))
+				continue;
+			if ((list.add_flags&LIST_TOPIC) &&
+			   (!wildmatch(Channels[aChan].topic,list.Topic)))
+				continue;
+			else if ((list.del_flags&LIST_TOPIC) &&
+			   (wildmatch(Channels[aChan].topic,list.Topic)))
+				continue;
+			if ((list.add_flags&LIST_PEOPLE) &&
+			    (true_array_len(Channels[aChan].users) < list.People) )
+				continue;
+			else if ((list.del_flags&LIST_PEOPLE) &&
+			    (true_array_len(Channels[aChan].users) >= list.People) )
+				continue;
+			if ((list.add_flags&LIST_TOPICAGE) && list.TopicTime &&
+			  (Channels[aChan].topictime > (time()-list.TopicTime)))
+				continue;
+			else if((list.del_flags&LIST_TOPICAGE)&&list.TopicTime&&
+			  (Channels[aChan].topictime < (time()-list.TopicTime)))
+				continue;
+			if (list.add_flags&LIST_MODES) { // there's no -m
+				var sic = false;
+				var madd = true;
+				var c = Channels[aChan];
+				for (mm in list.Modes) {
+					switch(list.Modes[mm]) {
+						case "+":
+							if (!madd)
+								madd = true;
+							break;
+						case "-":
+							if (madd)
+								madd = false;
+							break;
+						case "i":
+							if (   (!madd && (c.mode&CHANMODE_INVITE))
+								|| (madd && !(c.mode&CHANMODE_INVITE))
+							) {
+								sic = true;
+							}
+							break;
+						case "k":
+							if (   (!madd && (c.mode&CHANMODE_KEY))
+								|| (madd && !(c.mode&CHANMODE_KEY))
+							) {
+								sic = true;
+							}
+							break;
+						case "l":
+							if (   (!madd && (c.mode&CHANMODE_LIMIT))
+								|| (madd && !(c.mode&CHANMODE_LIMIT))
+							) {
+								sic = true;
+							}
+							break;
+						case "m":
+							if (   (!madd && (c.mode&CHANMODE_MODERATED))
+								|| (madd && !(c.mode&CHANMODE_MODERATED))
+							) {
+								sic = true;
+							}
+							break;
+						case "n":
+							if (   (!madd && (c.mode&CHANMODE_NOOUTSIDE))
+								|| (madd && !(c.mode&CHANMODE_NOOUTSIDE))
+							) {
+								sic = true;
+							}
+							break;
+						case "p":
+							if (   (!madd && (c.mode&CHANMODE_PRIVATE))
+								|| (madd && !(c.mode&CHANMODE_PRIVATE))
+							) {
+								sic = true;
+							}
+							break;
+						case "s":
+							if (   (!madd && (c.mode&CHANMODE_SECRET))
+								|| (madd && !(c.mode&CHANMODE_SECRET))
+							) {
+								sic = true;
+							}
+							break;
+						case "t":
+							if (   (!madd && (c.mode&CHANMODE_TOPIC))
+								|| (madd && !(c.mode&CHANMODE_TOPIC))
+							) {
+								sic = true;
+							}
+							break;
+						default:
+							break;
+					}
+					if (sic)
+						break;
+				}
+				if (sic)
+					continue;
+			}
+
+			if (listmask)
+				listmask_items = listmask.split(",");
+			var l_match = false; // assume we match nothing.
+			if (listmask_items) {
+				for (l in listmask_items) {
+					if (Channels[aChan].match_list_mask
+					   (listmask_items[l])) {
+						l_match = true;
+						break;
+					}
+				}
+				if (!l_match)
+					continue;
+			}
+
+			// We made it.
+			if (list.add_flags&LIST_DISPLAY_CHAN_MODES)
+				this.numeric322(Channels[aChan],true);
+			else
+				this.numeric322(Channels[aChan]);
+		}
+	}
+
+	this.numeric(323, ":End of /LIST. (Complex)");
+}
+		
+// Object which stores LIST bits and arguments.
+function List() {
+	this.add_flags = 0;
+	this.del_flags = 0;
+	this.tweak_mode = List_tweak_mode;
+	this.People = 0;
+	this.Created = 0;
+	this.TopicTime = 0;
+	this.Mask = "";
+	this.Modes = "";
+	this.Topic = "";
+}
+
+function List_tweak_mode(bit,add) {
+	if (add) {
+		this.add_flags |= bit;
+		this.del_flags &= ~bit;
+	} else {
+		this.add_flags &= ~bit;
+		this.del_flags |= bit;
+	}
+}
+
+function IRCClient_do_list_usage() {
+	this.numeric(334,":/LIST [+|-][acmoptM] <args> <mask|channel{,channel}>");
+	this.numeric(334,":The modes as above work exactly like channel modes.");
+	this.numeric(334,":<mask> may be just like Bahamut notation.");
+	this.numeric(334,":(Bahamut Notation = >num,<num,C>num,C<num,T>num,T<num,*mask*,!*mask*)");
+	this.numeric(334,":i.e. '/LIST +p 50 #*irc*' lists chans w/ irc in the name and 50+ users.");
+	this.numeric(334,":No args/mask matches to everything by default.");
+	this.numeric(334,":a <mask>: List channels whose names match the mask.  Wildcards allowed.");
+	this.numeric(334,":c <time>: Chans created less than (-) or more than (+) <time> mins ago.");
+	this.numeric(334,":m <mods>: Channel has <modes> set.  -+ allowed.");
+	this.numeric(334,":o <topc>: Match against channel's <topic>, wildcards allowed.");
+	this.numeric(334,":p <num> : Chans with more or equal to (+) members, or (-) less than.");
+	this.numeric(334,":t <time>: Only channels whose topics were created <time> mins ago.");
+	this.numeric(334,":M       : Show channel's mode in front of the list topic.");
+	// No "end of" numeric for this.
+}
+
+// does 'this' (channel) match the 'mask' passed to us?  Use 'complex'
+// Bahamut parsing to determine that.
+function Channel_match_list_mask(mask) {
+	if (mask[0] == ">") { // Chan has more than X people?
+		if (true_array_len(this.users) < parseInt(mask.slice(1)))
+			return 0;
+	} else if (mask[0] == "<") { // Chan has less than X people?
+		if (true_array_len(this.users) >= parseInt(mask.slice(1)))
+			return 0;
+	} else if (mask[0].toUpperCase() == "C") { //created X mins ago?
+		if (   (mask[1] == ">")
+			&& (this.created < (time() - (parseInt(mask.slice(2)) * 60)))
+		) {
+			return 0;
+		} else if (   (mask[1] == "<")
+					&& (this.created > (time() - (parseInt(mask.slice(2)) * 60)))
+		) {
+			return 0;
+		}
+	} else if (mask[0].toUpperCase() == "T") { //topics older than X mins?
+		if (   (mask[1] == ">")
+			&& (this.topictime < (time() - (parseInt(mask.slice(2)) * 60)) )
+		) {
+			return 0;
+		} else if (   (mask[1] == "<")
+					&& (this.topictime > (time() - (parseInt(mask.slice(2)) * 60)))
+		) {
+			return 0;
+		}
+	} else if (mask[0] == "!") { // doesn't match mask X
+		if (wildmatch(this.nam,mask.slice(1).toUpperCase()))
+			return 0;
+	} else { // if all else fails, we're matching a generic channel mask.
+		if (!wildmatch(this.nam,mask))
+			return 0;
+	}
+	return 1; // if we made it here, we matched something.
+}
+
+function IRCClient_get_usermode(bcast_modes) {
+	var tmp_mode = "+";
+	for (ch in USERMODE_CHAR) {
+		if (   (!bcast_modes || (bcast_modes && USERMODE_BCAST[ch]))
+			&& this.mode&USERMODE_CHAR[ch]
+		) {
+			tmp_mode += ch;
+		}
+	}
+	return tmp_mode;
+}
+
+function UMode_tweak_mode(bit,add) {
+	if (add) {
+		this.add_flags |= bit;
+		this.del_flags &= ~bit;
+	} else {
+		this.add_flags &= ~bit;
+		this.del_flags |= bit;
+	}
+}
+
+function UMode() {
+	this.add_flags = 0;
+	this.del_flags = 0;
+	this.tweak_mode = UMode_tweak_mode;
+}
+
+function IRCClient_setusermode(modestr) {
+	if (!modestr)
+		return 0;
+	var add=true;
+	var unknown_mode=false;
+	var umode = new UMode();
+	for (modechar in modestr) {
+		switch (modestr[modechar]) {
+			case "+":
+				if (!add)
+					add=true;
+				break;
+			case "-":
+				if (add)
+					add=false;
+				break;
+			case "i":
+			case "w":
+			case "s":
+			case "k":
+			case "g":
+				umode.tweak_mode(USERMODE_CHAR
+					[modestr[modechar]],add);
+				break;
+			case "b":
+			case "r":
+			case "f":
+			case "y":
+			case "d":
+			case "n":
+				if (this.mode&USERMODE_OPER)
+					umode.tweak_mode(USERMODE_CHAR
+						[modestr[modechar]],add);
+				break;
+			case "o":
+				// Allow +o only by servers or non-local users.
+				if (add && this.parent &&
+				    Servers[this.parent.toLowerCase()].hub)
+					umode.tweak_mode(USERMODE_OPER,true);
+				else if (!add)
+					umode.tweak_mode(USERMODE_OPER,false);
+				break;
+			case "c":
+				if ((this.mode&USERMODE_OPER) &&
+				    (this.flags&OLINE_CAN_UMODEC))
+					umode.tweak_mode(USERMODE_CLIENT,add);
+				break;
+			case "A":
+				if ( ((this.mode&USERMODE_OPER) && (this.flags&OLINE_IS_ADMIN))
+					|| (this.parent && Servers[this.parent.toLowerCase()].hub) )
+					umode.tweak_mode(USERMODE_ADMIN,add);
+				break;
+			default:
+				if (!unknown_mode && !this.parent) {
+					this.numeric(501, ":Unknown MODE flag");
+					unknown_mode=true;
+				}
+				break;
+		}
+	}
+	var addmodes = "";
+	var delmodes = "";
+	var bcast_addmodes = "";
+	var bcast_delmodes = "";
+	for (mym in USERMODE_CHAR) {
+		if (   (umode.add_flags&USERMODE_CHAR[mym])
+			&& !(this.mode&USERMODE_CHAR[mym])
+		) {
+			addmodes += mym;
+			if (USERMODE_BCAST[mym])
+				bcast_addmodes += mym;
+			this.mode |= USERMODE_CHAR[mym];
+		} else if (   (umode.del_flags&USERMODE_CHAR[mym])
+					&& (this.mode&USERMODE_CHAR[mym])
+		) {
+			delmodes += mym;
+			if (USERMODE_BCAST[mym])
+				bcast_delmodes += mym;
+			this.mode &= ~USERMODE_CHAR[mym];
+		}
+	}
+	if (!addmodes && !delmodes)
+		return 0;
+	var final_modestr = "";
+	var bcast_modestr = "";
+	if (addmodes)
+		final_modestr += "+" + addmodes;
+	if (delmodes)
+		final_modestr += "-" + delmodes;
+	if (bcast_addmodes)
+		bcast_modestr += "+" + bcast_addmodes;
+	if (bcast_delmodes)
+		bcast_modestr += "-" + bcast_delmodes;
+	if (this.local && !this.server) {
+		this.originatorout("MODE "+this.nick+" "+final_modestr,this);
+		if (bcast_addmodes || bcast_delmodes)
+			this.bcast_to_servers("MODE "+this.nick+" "+bcast_modestr);
+	}
+	return bcast_modestr;
+}
+
+function IRCClient_check_timeout() {
+	if (   !this.pinged
+		&& ((time() - this.idletime) > YLines[this.ircclass].pingfreq)
+	) {
+		this.pinged = time();
+		this.rawout("PING :" + servername);
+	} else if (   this.pinged
+				&& ((time() - this.pinged) > YLines[this.ircclass].pingfreq)
+	) {
+		this.quit("Ping Timeout");
+		return 1;
+	}
+	return 0;
+}
+
+function IRCClient_check_queues() {
+	var cmd;
+
+	this.sendq.send(this.socket);
+	while (this.recvq.queue.length) {
+		cmd = this.recvq.del();
+		this.work(cmd);
+		if (this.replaced_with !== undefined) {
+			this.replaced_with.check_queues();
+			break;
+		}
+	}
+}
+
+function IRCClient_finalize_server_connect(states,sendps) {
+	hcc_counter++;
+	gnotice("Link with " + this.nick + "[unknown@" + this.hostname +
+		"] established, states: " + states);
+	if (server.client_update != undefined)
+		server.client_update(this.socket, this.nick, this.hostname);
+	if (sendps) {
+		for (cl in CLines) {
+			if(wildmatch(this.nick,CLines[cl].servername)) {
+				this.rawout(format(
+					"PASS %s :%s",
+					CLines[cl].password,
+					states
+				));
+				break;
+			}
+		}
+		this.rawout("CAPAB " + Server_CAPAB);
+		this.rawout("SERVER " + servername + " 1 :" + serverdesc);
+	}
+	this.bcast_to_servers_raw(":" + servername + " SERVER " + this.nick
+		+ " 2 :" + this.info);
+	this.synchronize();
+}
+
+function CLine(host,password,servername,port,ircclass) {
+	this.host = host;
+	this.password = password;
+	this.servername = servername;
+	this.port = port;
+	this.ircclass = ircclass;
+	this.lastconnect = 0;
+}
+
+function HLine(allowedmask,servername) {
+	this.allowedmask = allowedmask;
+	this.servername = servername;
+}
+
+function ILine(ipmask,password,hostmask,port,ircclass) {
+	this.ipmask = ipmask;
+	this.password = password;
+	this.hostmask = hostmask;
+	this.port = port;
+	this.ircclass = ircclass;
+}
+
+function KLine(hostmask,reason,type) {
+	this.hostmask = hostmask;
+	this.reason = reason;
+	this.type = type;
+}
+
+function NLine(host,password,servername,flags,ircclass) {
+	this.host = host;
+	this.password = password;
+	this.servername = servername;
+	this.flags = flags;
+	this.ircclass = ircclass;
+}
+
+function OLine(hostmask,password,nick,flags,ircclass) {
+	this.hostmask = hostmask;
+	this.password = password;
+	this.nick = nick;
+	this.flags = flags;
+	this.ircclass = ircclass;
+}
+
+function QLine(nick,reason) {
+	this.nick = nick;
+	this.reason = reason;
+}
+
+function YLine(pingfreq,connfreq,maxlinks,sendq) {
+	this.pingfreq = pingfreq;
+	this.connfreq = connfreq;
+	this.maxlinks = maxlinks;
+	this.sendq = sendq;
+	this.active = 0;
+}
+
+function ZLine(ipmask,reason) {
+	this.ipmask = ipmask;
+	this.reason = reason;
+}
+
+function WhoWasObj(nick,uprefix,host,realname,server,serverdesc) {
+	this.nick = nick;
+	this.uprefix = uprefix;
+	this.host = host;
+	this.realname = realname;
+	this.server = server;
+	this.serverdesc = serverdesc;
+}
+
+function NickBuf(oldnick,newnick) {
+	this.oldnick = oldnick;
+	this.newnick = newnick;
+}
+
+// used for tracking true SJOIN nicks.
+function SJOIN_Nick(nick,isop,isvoice) {
+	this.nick = nick;
+	this.isop = isop;
+	this.isvoice = isvoice;
+}
+
+// Track IRC socket queues
+function IRC_Queue() {
+	this.queue = new Array;
+	this._recv_bytes = '';
+	this._send_bytes = '';
+	this.add = Queue_Add;
+	this.del = Queue_Del;
+	this.prepend = Queue_Prepend;
+	this.recv = Queue_Recv;
+	this.send = Queue_Send;
+}
+
+/* /STATS M, for profiling. */
+function StatsM() {
+	this.ticks = 0;
+	this.executions = 0;
+}
+
diff --git a/exec/load/ircd_server.js b/exec/load/ircd/server.js
similarity index 82%
rename from exec/load/ircd_server.js
rename to exec/load/ircd/server.js
index 6bc40ab9e7..f37b75e291 100644
--- a/exec/load/ircd_server.js
+++ b/exec/load/ircd/server.js
@@ -1,33 +1,28 @@
-// $Id: ircd_server.js,v 1.60 2020/04/03 22:21:51 deuce Exp $
-//
-// ircd_channel.js                
-//
-// This program is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation; either version 2 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details:
-// http://www.gnu.org/licenses/gpl.txt
-//
-// Synchronet IRC Daemon as per RFC 1459, link compatible with Bahamut 1.4
-//
-// Copyright 2003-2009 Randolph Erwin Sommerfeld <sysop@rrx.ca>
-//
-// ** Server to server operation is governed here.
-//
-
-////////// Constants / Defines //////////
-const SERVER_REVISION = "$Revision: 1.60 $".split(' ')[1];
+/*
+
+ ircd/server.js
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details:
+ https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+
+ IRCd inter-server communication.
+
+ Copyright 2003-2021 Randy Sommerfeld <cyan@synchro.net>
+
+*/
 
 // Various N:Line permission bits
 const NLINE_CHECK_QWKPASSWD		=(1<<0);	// q
 const NLINE_IS_QWKMASTER		=(1<<1);	// w
 const NLINE_CHECK_WITH_QWKMASTER	=(1<<2);	// k
-const NLINE_IS_DREAMFORGE		=(1<<3);	// d
 
 ////////// Objects //////////
 function IRC_Server() {
@@ -53,7 +48,6 @@ function IRC_Server() {
 	this.outgoing = false;
 	// Variables (consts, really) that point to various state information
 	this.socket = "";
-	this.type = BAHAMUT; /* Assume bahamut by default */
 	////////// FUNCTIONS
 	// Functions we use to control clients (specific)
 	this.quit = Server_Quit;
@@ -380,16 +374,6 @@ function Server_Work(cmdline) {
 					chan.created = cmd2_int;
 				}
 				cmd.shift();
-			} else if ( (cmdend_int == cmd[cmdend])
-					&& (this.type == DREAMFORGE) ) {
-				/* DreamForge style TS */
-				if (cmdend_int > chan.created) {
-					break;
-				} else if ((cmdend_int < chan.created) && ThisOrigin.server) {
-					bounce_modes = true;
-					chan.created = cmdend_int;
-				}
-				cmd.pop(); /* Strip the TS. */
 			}
 			cmd.shift();
 			cmd.shift();
@@ -409,11 +393,13 @@ function Server_Work(cmdline) {
 		if (cmd[1][0] == ":")
 			cmd[1] = cmd[1].slice(1);
 		if (wildmatch(servername, cmd[1])) {
-			umode_notice(USERMODE_SPY,"Spy",
-				"MOTD requested by " + ThisOrigin.nick+
-				" (" + ThisOrigin.uprefix + "@" +
-				ThisOrigin.hostname + ") [" +
-				ThisOrigin.servername + "]");
+			umode_notice(USERMODE_STATS_LINKS,"StatsLinks",format(
+				"MOTD requested by %s (%s@%s) [%s]",
+				ThisOrigin.nick,
+				ThisOrigin.uprefix,
+				ThisOrigin.hostname,
+				ThisOrigin.servername
+			));
 			ThisOrigin.motd();
 		} else {
 			var dest_server = searchbyserver(cmd[1]);
@@ -432,8 +418,9 @@ function Server_Work(cmdline) {
 					gnotice("Server " + this.nick + " trying to introduce nick " + collide.nick
 						+ " twice?! Ignoring.");
 					break;
-				} else if ((parseInt(collide.created) >
-				    parseInt(cmd[3])) && this.hub) {
+				} else if (   (parseInt(collide.created) > parseInt(cmd[3]))
+							&& this.hub
+				) {
 					// Nuke our side of things, allow this newly
 					// introduced nick to overrule.
 					collide.numeric(436, collide.nick + " :Nickname Collision KILL.");
@@ -454,8 +441,6 @@ function Server_Work(cmdline) {
 				}
 			}
 			var uprefixptr = 5; /* Bahamut */
-			if (this.type == DREAMFORGE)
-				uprefixptr = 4;
 			if (!this.hub) {
 				if(!this.check_nickname(cmd[1],true)) {
 					gnotice("Server " + this.nick + " trying to introduce invalid nickname: " + cmd[1]
@@ -479,36 +464,22 @@ function Server_Work(cmdline) {
 					break;
 				}
 			}
-			if (cmd[uprefixptr+1][0] == ".")	/* CR "hidden mask" fix. */
-				cmd[uprefixptr+1] = cmd[uprefixptr+1].slice(1);
 			var new_id = "id" + next_client_id;
 			next_client_id++;
 			Users[cmd[1].toUpperCase()] = new IRC_User(new_id);
 			var NewNick = Users[cmd[1].toUpperCase()];
 			NewNick.local = false; // not local. duh.
 			NewNick.nick = cmd[1];
-			/* What the hell.  CR reverses these at random. */
-			if (parseInt(cmd[2]) > 100) {
-				NewNick.created = cmd[2];
-				NewNick.hops = cmd[3];
-			} else {
-				NewNick.hops = cmd[2];
-				NewNick.created = cmd[3];
-			}
+			NewNick.hops = cmd[2];
+			NewNick.created = cmd[3];
 			NewNick.uprefix = cmd[uprefixptr];
 			NewNick.hostname = cmd[uprefixptr+1];
 			NewNick.servername = cmd[uprefixptr+2];
 			var rnptr = 10; /* Bahamut */
-			if (this.type == DREAMFORGE)
-				rnptr = 8;
 			NewNick.realname = IRC_string(cmdline,rnptr);
 			NewNick.parent = this.nick;
-			if (this.type == DREAMFORGE)
-				NewNick.ip = 0;
-			else /* Bahamut */
-				NewNick.ip = int_to_ip(cmd[9]);
-			if (this.type == BAHAMUT)
-				NewNick.setusermode(cmd[4]);
+			NewNick.ip = int_to_ip(cmd[9]);
+			NewNick.setusermode(cmd[4]);
 			for (u in ULines) {
 				if (ULines[u] == cmd[uprefixptr+2]) {
 					NewNick.uline = true;
@@ -516,26 +487,28 @@ function Server_Work(cmdline) {
 				}
 			}
 			var true_hops = parseInt(NewNick.hops)+1;
-			this.bcast_to_servers_raw("NICK "
-				+ NewNick.nick + " "
-				+ true_hops + " "
-				+ NewNick.created + " "
-				+ NewNick.get_usermode(true) + " "
-				+ NewNick.uprefix + " "
-				+ NewNick.hostname + " "
-				+ NewNick.servername + " "
-				+ "0 "
-				+ ip_to_int(NewNick.ip)
-				+ " :" + NewNick.realname,BAHAMUT);
-			this.bcast_to_servers_raw("NICK "
-				+ NewNick.nick + " "
-				+ true_hops + " "
-				+ NewNick.created + " "
-				+ NewNick.uprefix + " "
-				+ NewNick.hostname + " "
-				+ NewNick.servername + " "
-				+ "0 "
-				+ ":" + NewNick.realname,DREAMFORGE);
+			this.bcast_to_servers_raw(
+				format("NICK %s %s %s %s %s %s %s 0 %s :%s",
+					NewNick.nick,
+					true_hops,
+					NewNick.created,
+					NewNick.get_usermode(true),
+					NewNick.uprefix,
+					NewNick.hostname,
+					NewNick.servername,
+					ip_to_int(NewNick.ip),
+					NewNick.realname
+				)
+			);
+			umode_notice(USERMODE_DEBUG,"RemoteClient",format(
+				"NICK %s %s@%s %s %s :%s",
+				NewNick.nick,
+				NewNick.uprefix,
+				NewNick.hostname,
+				NewNick.servername,
+				NewNick.ip,
+				NewNick.realname
+			));
 		} else { // we're a user changing our nick.
 			var ctuc = cmd[1].toUpperCase();
 			if ((Users[ctuc])&&Users[ctuc].nick.toUpperCase() !=
@@ -606,14 +579,14 @@ function Server_Work(cmdline) {
 			var dest_server = searchbyserver(cmd[4]);
 			if (!dest_server) {
 				break;
-			} else if ((dest_server == -1) &&
-			    (this.flags&NLINE_IS_QWKMASTER)) {
+			} else if (   (dest_server == -1)
+						&& (this.flags&NLINE_IS_QWKMASTER)
+			) {
 				var qwkid = cmd[2].toLowerCase();
 				var hunt = qwkid + ".synchro.net";
 				var my_server = 0;
 				for (ur in Unregistered) {
-					if (Unregistered[ur].nick ==
-					    hunt) {
+					if (Unregistered[ur].nick == hunt) {
 						my_server = Unregistered[ur];
 						break;
 					}
@@ -765,8 +738,9 @@ function Server_Work(cmdline) {
 				var my_modechar = cmd[3][tmpmc];
 				if (my_modechar == "+")
 					continue;
-				if ((my_modechar == "k") ||
-				    (my_modechar == "l")) {
+				if (   (my_modechar == "k")
+					|| (my_modechar == "l")
+				) {
 					tmp_modeargs++;
 					incoming_modes[my_modechar] = cmd[tmp_modeargs];
 				} else {
@@ -788,7 +762,7 @@ function Server_Work(cmdline) {
 			/* The following corrects a bug in Bahamut.. */
 			if ((cmd[4] == "") && cmd[5])
 				tmp_modeargs++;
-			
+
 			tmp_modeargs++; /* Jump to start of string */
 			chan_members = IRC_string(cmdline,tmp_modeargs).split(' ');
 
@@ -798,7 +772,7 @@ function Server_Work(cmdline) {
 				break;
 			}
 
-			cm_array = new Array; /* True array */
+			cm_array = [];
 
 			for (cm in chan_members) {
 				var isop = false;
@@ -860,8 +834,6 @@ function Server_Work(cmdline) {
 				chan.users[member_obj.id] = member_obj;
 				var joinstr = "JOIN " + chan.nam;
 				member_obj.bcast_to_channel(chan, joinstr, false);
-				member_obj.bcast_to_servers_raw(":" + member_obj.nick
-					+ " " + joinstr, DREAMFORGE);
 				if (chan.created >= parseInt(cmd[1])) {
 					if (is_op) {
 						chan.modelist[CHANMODE_OP][member_obj.id]=member_obj.id;
@@ -874,7 +846,6 @@ function Server_Work(cmdline) {
 						var mode1str = "MODE " + chan.nam + " "
 							+ push_sync_modes + push_sync_args;
 						this.bcast_to_channel(chan,mode1str);
-						this.bcast_to_servers(mode1str,DREAMFORGE);
 						push_sync_modes = "+";
 						push_sync_args = "";
 						num_sync_modes = 0;
@@ -890,7 +861,6 @@ function Server_Work(cmdline) {
 						var mode2str = "MODE " + chan.nam + " "
 							+ push_sync_modes + push_sync_args;
 						this.bcast_to_channel(chan,mode2str);
-						this.bcast_to_servers(mode2str,DREAMFORGE);
 						push_sync_modes = "+";
 						push_sync_args = "";
 						num_sync_modes = 0;
@@ -902,19 +872,21 @@ function Server_Work(cmdline) {
 				var mode3str = "MODE " + chan.nam + " "
 					+ push_sync_modes + push_sync_args;
 				this.bcast_to_channel(chan, mode3str);
-				this.bcast_to_servers(mode3str,DREAMFORGE);
 			}
 
 			// Synchronize the TS to what we received.
 			if (chan.created > parseInt(cmd[1]))
 				chan.created = parseInt(cmd[1]);
 
-			this.bcast_to_servers_raw(":" + ThisOrigin.nick + " "
-				+ "SJOIN "
-				+ chan.created + " "
-				+ chan.nam + " "
-				+ chan.chanmode(true) + " "
-				+ ":" + new_chan_members,BAHAMUT)
+			this.bcast_to_servers_raw(
+				format(":%s SJOIN %s %s %s :%s",
+					ThisOrigin.nick,
+					chan.created,
+					chan.nam,
+					chan.chanmode(true),
+					new_chan_members
+				)
+			);
 		} else {
 			if (ThisOrigin.server) {
 				umode_notice(USERMODE_OPER,"Notice", "Server " + ThisOrigin.nick
@@ -926,8 +898,13 @@ function Server_Work(cmdline) {
 			ThisOrigin.channels[chan.nam.toUpperCase()] = chan;
 			chan.users[ThisOrigin.id] = ThisOrigin;
 			ThisOrigin.bcast_to_channel(chan, "JOIN " + chan.nam, false);
-			this.bcast_to_servers_raw(":" + ThisOrigin.nick + " SJOIN " + chan.created +" "+ chan.nam,BAHAMUT);
-			this.bcast_to_servers_raw(":" + ThisOrigin.nick + " JOIN " + chan.nam,DREAMFORGE);
+			this.bcast_to_servers_raw(
+				format(":%s SJOIN %s %s",
+					ThisOrigin.nick,
+					chan.created,
+					chan.nam
+				)
+			);
 		}
 		break;
 	case "SQUIT":
@@ -1111,17 +1088,17 @@ function Server_Work(cmdline) {
 
 		var akill_reason = IRC_string(cmdline,8);
 
-		var snd_prefix = ":" + ThisOrigin.nick + " ";
-
 		/* Propagate this to the network */
-		this.bcast_to_servers_raw(snd_prefix + cmdline,DREAMFORGE);
-		this.bcast_to_servers_raw(snd_prefix + "AKILL "
-			+ cmd[4] + " "				/* host */
-			+ cmd[3] + " "				/* user */
-			+ (cmd[7]-cmd[6]) + " "		/* length */
-			+ cmd[5] + " "				/* akiller */
-			+ ":" + akill_reason		/* reason */
-		,BAHAMUT);
+		this.bcast_to_servers_raw(
+			format(":%s AKILL %s %s %s %s :%s",
+				ThisOrigin.nick,
+				cmd[4],				/* host */
+				cmd[3],				/* user */
+				(cmd[7]-cmd[6]),	/* length */
+				cmd[5],				/* akiller */
+				akill_reason		/* reason */
+			)
+		);
 
 		var this_uh = cmd[3] + "@" + cmd[4];
 		if (isklined(this_uh))
@@ -1134,10 +1111,6 @@ function Server_Work(cmdline) {
 	case "CAPAB":
 	case "BURST":
 	case "SVSMODE":
-	case "NETINFO": /* Dreamforge/Unreal/CR */
-	case "SMO": /* Dreamforge/Unreal/CR -- Send Usermode */
-	case "EOS": /* Dreamforge/Unreal/CR -- End Of Synch */
-	case "TUNL": /* Dreamforge/Unreal/CR */
 	case "SETHOST": /* We do not honour SETHOST. */
 		break; // Silently ignore for now.
 	default:
@@ -1164,18 +1137,16 @@ function server_bcast_to_servers(str,type) {
 	}
 }
 
-function IRCClient_bcast_to_servers(str,type) {
+function IRCClient_bcast_to_servers(str) {
 	for(thisClient in Local_Servers) {
-		var srv = Local_Servers[thisClient];
-		if ( (srv.nick != this.parent) && (!type || (srv.type == type)))
+		if (Local_Servers[thisClient].nick != this.parent)
 			Local_Servers[thisClient].originatorout(str,this);
 	}
 }
 
-function IRCClient_bcast_to_servers_raw(str,type) {
+function IRCClient_bcast_to_servers_raw(str) {
 	for(thisClient in Local_Servers) {
-		var srv = Local_Servers[thisClient];
-		if ( (srv.nick != this.parent) && (!type || (srv.type == type)))
+		if (Local_Servers[thisClient].nick != this.parent)
 			Local_Servers[thisClient].rawout(str);
 	}
 }
@@ -1186,8 +1157,6 @@ function Server_Quit(str,suppress_bcast,is_netsplit,origin) {
 
 	if (is_netsplit) {
 		this.netsplit(str);
-		/* Fix for DreamForge's lack of NOQUIT */
-		this.bcast_to_servers_raw("SQUIT " + this.nick + " :" + str,DREAMFORGE);
 	} else if (this.local) {
 		this.netsplit(servername + " " + this.nick);
 		if (!suppress_bcast)
@@ -1267,22 +1236,26 @@ function IRCClient_server_info(sni_server) {
 }
 
 function IRCClient_server_nick_info(sni_client) {
-	var actual_hops = parseInt(sni_client.hops) + 1;
-	var sendstr = "NICK " + sni_client.nick + " " + actual_hops + " " + sni_client.created + " ";
-	if (this.type == BAHAMUT)
-		sendstr += sni_client.get_usermode(true) + " ";
-	sendstr += sni_client.uprefix + " " + sni_client.hostname + " " + sni_client.servername + " 0 ";
-	if (this.type == BAHAMUT)
-		sendstr += ip_to_int(sni_client.ip) + " ";
-	sendstr += ":" + sni_client.realname;
-
-	this.rawout(sendstr);
-
-	if (this.type == DREAMFORGE)
-		this.rawout(":" + sni_client.nick + " MODE " + sni_client.nick + " :" + sni_client.get_usermode(true));
-
-	if (sni_client.away)
-		this.rawout(":" + sni_client.nick + " AWAY :" + sni_client.away);
+	this.rawout(
+		format("NICK %s %s %s %s %s %s %s 0 %s :%s",
+			sni_client.nick,
+			parseInt(sni_client.hops) + 1,
+			sni_client.created,
+			sni_client.get_usermode(true),
+			sni_client.uprefix,
+			sni_client.hostname,
+			sni_client.servername,
+			ip_to_int(sni_client.ip),
+			sni_client.realname
+		)
+	);
+
+	if (sni_client.away) {
+		this.rawout(format(":%s AWAY :%s",
+			sni_client.nick,
+			sni_client.away
+		));
+	}
 }
 
 function IRCClient_reintroduce_nick(nick) {
@@ -1290,55 +1263,40 @@ function IRCClient_reintroduce_nick(nick) {
 
 	for (uchan in nick.channels) {
 		var chan = nick.channels[uchan];
-		if (this.type == DREAMFORGE) {
-			this.rawout(":" + nick.nick + " JOIN " + chan.nam);
-			if (chan.modelist[CHANMODE_OP][nick.id])
-				this.ircout("MODE " + chan.nam + " +o " + nick.nick + " " + chan.created);
-			if (chan.modelist[CHANMODE_VOICE][nick.id])
-				this.ircout("MODE " + chan.nam + " +v " + nick.nick + " " + chan.created);
-		} else { /* Bahamut */
-			var cmodes = "";
-			if (chan.modelist[CHANMODE_OP][nick.id])
-				cmodes += "@";
-			if (chan.modelist[CHANMODE_VOICE][nick.id])
-				cmodes += "+";
-			this.rawout("SJOIN " + chan.created + " " + chan.nam + " " + chan.chanmode(true)
-				+ " :" + cmodes + nick.nick);
+		var cmodes = "";
+		if (chan.modelist[CHANMODE_OP][nick.id])
+			cmodes += "@";
+		if (chan.modelist[CHANMODE_VOICE][nick.id])
+			cmodes += "+";
+		this.rawout(
+			format("SJOIN %s %s %s :%s%s",
+				chan.created,
+				chan.nam,
+				chan.chanmode(true),
+				cmodes,
+				nick.nick
+			)
+		);
+		if (chan.topic) {
+			this.rawout(
+				format("TOPIC %s %s %s :%s",
+					chan.nam,
+					chan.topicchangedby,
+					chan.topictime,
+					chan.topic
+				)
+			);
 		}
-		if (chan.topic)
-			this.rawout("TOPIC " + chan.nam + " " + chan.topicchangedby + " " + chan.topictime
-				+ " :" + chan.topic);
 	}
 }
 
 function IRCClient_server_chan_info(sni_chan) {
-	if (this.type == DREAMFORGE) {
-		var df_chan_occs = sni_chan.occupants().split(' ');
-		for (dfocc in df_chan_occs) {
-			var cmember = df_chan_occs[dfocc];
-			var mem_is_op = false;
-			var mem_is_voice = false;
-			if (cmember[0] == "@") {
-				cmember = cmember.slice(1);
-				mem_is_op = true;
-			}
-			if (cmember[0] == "+") {
-				cmember = cmember.slice(1);
-				mem_is_voice = true;
-			}
-			this.rawout(":" + cmember + " JOIN " + sni_chan.nam);
-			if (mem_is_op)
-				this.ircout("MODE " + sni_chan.nam + " +o " + cmember + " "
-					+ sni_chan.created);
-			if (mem_is_voice)
-				this.ircout("MODE " + sni_chan.nam + " +v " + cmember + " "
-					+ sni_chan.created);
-		}
-		this.ircout("MODE " + sni_chan.nam + " " + sni_chan.chanmode(true) + " " + sni_chan.created);
-	} else { /* Bahamut */
-		this.rawout("SJOIN " + sni_chan.created + " " + sni_chan.nam + " " + sni_chan.chanmode(true)
-			+ " :" + sni_chan.occupants())
-	}
+	this.rawout(format("SJOIN %s %s %s :%s",
+			sni_chan.created,
+			sni_chan.nam,
+			sni_chan.chanmode(true),
+			sni_chan.occupants()
+	));
 	var modecounter=0;
 	var modestr="+";
 	var modeargs="";
@@ -1349,20 +1307,22 @@ function IRCClient_server_chan_info(sni_chan) {
 			modeargs += " ";
 		modeargs += sni_chan.modelist[CHANMODE_BAN][aBan];
 		if (modecounter >= max_modes) {
-			var outstr = "MODE " + sni_chan.nam + " " + modestr + " " + modeargs;
-			if (this.type == DREAMFORGE)
-				outstr += " " + sni_chan.created;
-			this.ircout(outstr);
+			this.ircout(format("MODE %s %s %s",
+				sni_chan.nam,
+				modestr,
+				modeargs
+			));
 			modecounter=0;
 			modestr="+";
 			modeargs="";
 		}
 	}
 	if (modeargs) {
-		var outstr = "MODE " + sni_chan.nam + " " + modestr + " " + modeargs;
-		if (this.type == DREAMFORGE)
-			outstr += " " + sni_chan.created;
-		this.ircout(outstr);
+		this.ircout(format("MODE %s %s %s",
+			sni_chan.nam,
+			modestr,
+			modeargs
+		));
 	}
 }
 
diff --git a/exec/load/ircd_unreg.js b/exec/load/ircd/unregistered.js
similarity index 80%
rename from exec/load/ircd_unreg.js
rename to exec/load/ircd/unregistered.js
index 64bf0eb419..538a295b6c 100644
--- a/exec/load/ircd_unreg.js
+++ b/exec/load/ircd/unregistered.js
@@ -1,34 +1,31 @@
-// $Id: ircd_unreg.js,v 1.53 2020/04/04 03:34:03 deuce Exp $
-//
-// ircd_unreg.js
-//
-// This program is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation; either version 2 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details:
-// http://www.gnu.org/licenses/gpl.txt
-//
-// Synchronet IRC Daemon as per RFC 1459, link compatible with Bahamut 1.4
-//
-// Copyright 2003-2010 Randolph Erwin Sommerfeld <sysop@rrx.ca>
-//
-// ** Handle unregistered clients.
-//
+/*
 
-const UNREG_REVISION = "$Revision: 1.53 $".split(' ')[1];
+ ircd/unregistered.js
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details:
+ https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+
+ How unregistered clients are handled in the IRCd.
+
+ Copyright 2003-2021 Randy Sommerfeld <cyan@synchro.net>
+
+*/
 
 ////////// Objects //////////
 function Unregistered_Client(id,socket) {
 	////////// VARIABLES
 	// Bools/Flags that change depending on connection state.
-	this.pinged = false;	   // Sent PING?
-	this.local = true;	   // FIXME: this is redundant.
-	this.criteria_met = false; // Have we met registration criteria?
+	this.pinged = false;		// Sent PING?
+	this.local = true;			// FIXME: this is redundant.
+	this.criteria_met = false;	// Have we met registration criteria?
 	// Variables containing user/server information as we receive it.
 	this.id = id;
 	this.nick = "*";
@@ -48,7 +45,6 @@ function Unregistered_Client(id,socket) {
 	////////// FUNCTIONS
 	// Functions we use to control clients (specific)
 	this.work = Unregistered_Commands;
-//	this.JSON_Unregistered_Commands = JSON_Unregistered_Commands;
 	this.IRC_Unregistered_Commands = IRC_Unregistered_Commands;
 	this.quit = Unregistered_Quit;
 	this.check_timeout = IRCClient_check_timeout;
@@ -73,13 +69,17 @@ function Unregistered_Client(id,socket) {
 	Local_Sockets[id] = socket.descriptor;
 	Local_Sockets_Map[id] = this;
 	rebuild_socksel_array = true;
-	log(format("%04u",socket.descriptor)
-		+ " Accepted new connection: " + this.ip
-		+ " port " + socket.remote_port);
-	if ((this.ip.slice(0,4) == "127.") ||
-	    (this.ip.slice(0,3) == "10.") ||
-	    (this.ip.slice(0,8) == "192.168.") ||
-	    (this.ip.slice(0,7) == "172.16." )) {
+	log(format("%04u Accepted new connection: %s port %s",
+		socket.descriptor,
+		this.ip,
+		socket.remote_port
+	));
+	if (   (this.ip.slice(0,4) == "127.")
+		|| (this.ip.slice(0,3) == "10.")
+		|| (this.ip.slice(0,8) == "192.168.")
+		|| (this.ip.slice(0,7) == "100.64.")
+		|| (this.ip.slice(0,7) == "172.16." )
+	) {
 		this.hostname = servername;
 		this.pending_resolve_time = false;
 	} else {
@@ -99,7 +99,7 @@ function Unregistered_Commands(cmdline) {
 		cmdline = cmdline.slice(1);
 
 	if (debug)
-		log(LOG_DEBUG,"[UNREG]: " + cmdline);
+		log("[UNREG]: " + cmdline);
 
 	this.IRC_Unregistered_Commands(cmdline);
 }
@@ -128,7 +128,7 @@ function IRC_Unregistered_Commands(cmdline) {
 		return 0;
 
 	var legal_command = true; /* For tracking STATS M */
-	
+
 	switch(command) {
 		case "PING":
 			if (!cmd[1]) {
@@ -137,6 +137,7 @@ function IRC_Unregistered_Commands(cmdline) {
 			}
 			this.rawout("PONG " + servername + " :" + IRC_string(cmdline,1));
 			break;
+		case "CAP":
 		case "CAPAB":
 			break; // Silently ignore, for now.
 		case "NICK":
@@ -166,23 +167,25 @@ function IRC_Unregistered_Commands(cmdline) {
 				break;
 			}
 			if (Servers[cmd[1].toLowerCase()]) {
-	     			if (parseInt(cmd[2]) < 2)
+				if (parseInt(cmd[2]) < 2)
 					this.quit("Server already exists.");
 				return 0;
 			}
 			var this_nline = 0;
 			var qwk_slave = false;
 			var qwkid = cmd[1].slice(0,cmd[1].indexOf(".")).toUpperCase();
-     			if (parseInt(cmd[2]) < 2) {
+			if (parseInt(cmd[2]) < 2) {
 				for (nl in NLines) {
-					if ((NLines[nl].flags&NLINE_CHECK_QWKPASSWD) &&
-					    wildmatch(cmd[1],NLines[nl].servername)) {
+					if (   (NLines[nl].flags&NLINE_CHECK_QWKPASSWD)
+						&& wildmatch(cmd[1],NLines[nl].servername)
+					) {
 						if (check_qwk_passwd(qwkid,this.password)) {
 							this_nline = NLines[nl];
 							break;
 						}
-					} else if ((NLines[nl].flags&NLINE_CHECK_WITH_QWKMASTER) &&
-						   wildmatch(cmd[1],NLines[nl].servername)) {
+					} else if (   (NLines[nl].flags&NLINE_CHECK_WITH_QWKMASTER)
+								&& wildmatch(cmd[1],NLines[nl].servername)
+					) {
 							for (qwkm_nl in NLines) {
 								if (NLines[qwkm_nl].flags&NLINE_IS_QWKMASTER) {
 									var qwk_master = searchbyserver(NLines[qwkm_nl].servername);
@@ -190,24 +193,33 @@ function IRC_Unregistered_Commands(cmdline) {
 										this.quit("No QWK master available for authorization.");
 										return 0;
 									} else {
-										qwk_master.rawout(":" + servername + " PASS " + this.password + " :" + qwkid + " QWK");
+										qwk_master.rawout(format(
+											":%s PASS %s :%s QWK",
+											servername,
+											this.password,
+											qwkid
+										));
 										qwk_slave = true;
 									}
 								}
 							}
-					} else if ((NLines[nl].password == this.password) &&
-						   (wildmatch(cmd[1],NLines[nl].servername))
-						  ) {
-							this_nline = NLines[nl];
-							break;
+					} else if (   (NLines[nl].password == this.password)
+								&& (wildmatch(cmd[1],NLines[nl].servername))
+					) {
+						this_nline = NLines[nl];
+						break;
 					}
 				}
 			}
-			if ( (!this_nline ||
-			      ( (this_nline.password == "*") && !this.outgoing
-				&& !(this_nline.flags&NLINE_CHECK_QWKPASSWD) )
-			     ) && !qwk_slave) {
-	     			if (parseInt(cmd[2]) < 2)
+			if (   !qwk_slave
+				&& (   !this_nline
+					|| (   (this_nline.password == "*")
+						&& !this.outgoing
+						&& !(this_nline.flags&NLINE_CHECK_QWKPASSWD)
+					)
+				)
+			) {
+				if (parseInt(cmd[2]) < 2)
 					this.quit("UR Server not configured.");
 				return 0;
 			}
@@ -247,8 +259,6 @@ function IRC_Unregistered_Commands(cmdline) {
 					}
 				}
 			}
-			if (this_nline.flags&NLINE_IS_DREAMFORGE)
-				new_server.type = DREAMFORGE;
 			new_server.finalize_server_connect("TS",this.sendps);
 			this.replaced_with = new_server;
 			break;
@@ -357,13 +367,9 @@ function Unregistered_Welcome() {
 	var my_iline;
 	// FIXME: We don't compare connecting port.
 	for(thisILine in ILines) {
-		if ((wildmatch(this.uprefix + "@" +
-		    this.ip,
-		    ILines[thisILine].ipmask)) &&
-		    (wildmatch(this.uprefix + "@" +
-		    this.hostname,
-		    ILines[thisILine].hostmask))
-		   ) {
+		if (   (wildmatch(this.uprefix + "@" + this.ip, ILines[thisILine].ipmask))
+			&& (wildmatch(this.uprefix + "@" + this.hostname, ILines[thisILine].hostmask))
+		) {
 			my_iline = ILines[thisILine];
 			break;
 		}
@@ -425,9 +431,18 @@ function Unregistered_Welcome() {
 	if (server.client_update != undefined)
 		server.client_update(this.socket, this.nick, this.hostname);
 	var nickstr = "NICK " + this.nick + " 1 " + new_user.created + " ";
-	server_bcast_to_servers(nickstr + "+ " + this.uprefix + " " + this.hostname + " " + servername + " 0 " + ip_to_int(new_user.ip) + " :" + this.realname,BAHAMUT);
-	server_bcast_to_servers(nickstr + this.uprefix + " " + this.hostname + " " + servername + " 0 " + " :" + this.realname,DREAMFORGE);
-	// we're no longer unregistered.
+	server_bcast_to_servers(
+		format("NICK %s 1 %s + %s %s %s 0 %s :%s",
+			this.nick,
+			new_user.created,
+			this.uprefix,
+			this.hostname,
+			servername,
+			ip_to_int(new_user.ip),
+			this.realname
+		)
+	);
+	/* we're no longer unregistered. */
 	this.replaced_with = new_user;
 	delete Unregistered[this.id];
 	delete this;
diff --git a/exec/load/ircd_user.js b/exec/load/ircd/user.js
similarity index 93%
rename from exec/load/ircd_user.js
rename to exec/load/ircd/user.js
index 2681ee3353..619eabc4d5 100644
--- a/exec/load/ircd_user.js
+++ b/exec/load/ircd/user.js
@@ -1,27 +1,23 @@
-// $Id: ircd_user.js,v 1.53 2020/04/03 23:27:54 deuce Exp $
-//
-// ircd_unreg.js
-//
-// This program is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation; either version 2 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details:
-// http://www.gnu.org/licenses/gpl.txt
-//
-// Synchronet IRC Daemon as per RFC 1459, link compatible with Bahamut 1.4
-//
-// Copyright 2003-2009 Randolph Erwin Sommerfeld <sysop@rrx.ca>
-//
-// ** Handle registered clients.
-//
+/*
 
-////////// Constants / Defines //////////
-const USER_REVISION = "$Revision: 1.53 $".split(' ')[1];
+ ircd/user.js
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details:
+ https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+
+ Handling of regular user-to-server communication in the IRCd.
+
+ Copyright 2003-2021 Randy Sommerfeld <cyan@synchro.net>
+
+*/
 
 const USERMODE_NONE			=(1<<0); // NONE
 const USERMODE_OPER			=(1<<1); // o
@@ -34,7 +30,7 @@ const USERMODE_CLIENT		=(1<<7); // c
 const USERMODE_REJECTED		=(1<<8); // r
 const USERMODE_KILL			=(1<<9); // k
 const USERMODE_FLOOD		=(1<<10); // f
-const USERMODE_SPY			=(1<<11); // y
+const USERMODE_STATS_LINKS	=(1<<11); // y
 const USERMODE_DEBUG		=(1<<12); // d
 const USERMODE_ROUTING		=(1<<13); // n
 const USERMODE_HELP			=(1<<14); // h
@@ -52,7 +48,7 @@ USERMODE_CHAR["c"] = USERMODE_CLIENT;
 USERMODE_CHAR["r"] = USERMODE_REJECTED;
 USERMODE_CHAR["k"] = USERMODE_KILL;
 USERMODE_CHAR["f"] = USERMODE_FLOOD;
-USERMODE_CHAR["y"] = USERMODE_SPY;
+USERMODE_CHAR["y"] = USERMODE_STATS_LINKS;
 USERMODE_CHAR["d"] = USERMODE_DEBUG;
 USERMODE_CHAR["n"] = USERMODE_ROUTING;
 USERMODE_CHAR["h"] = USERMODE_HELP;
@@ -67,7 +63,7 @@ USERMODE_BCAST["h"] = true;
 USERMODE_BCAST["A"] = true;
 
 // FIXME: Services modes are broadcast but not displayed to the user.
-USERMODE_SERVICES = new Object;
+USERMODE_SERVICES = {};
 
 // Various permissions that can be set on an O:Line
 const OLINE_CAN_REHASH		=(1<<0);	// r
@@ -311,7 +307,7 @@ function User_Work(cmdline) {
 			this.numeric(409,":No origin specified.");
 			break;
 		}
-    if (cmd[1][0] == ":") cmd[1] = cmd[1].slice(1);
+	if (cmd[1][0] == ":") cmd[1] = cmd[1].slice(1);
 		if (cmd[2]) {
 			if (cmd[2][0] == ":")
 				cmd[2] = cmd[2].slice(1);
@@ -606,8 +602,7 @@ function User_Work(cmdline) {
 		this.do_connect(cmd[1],cmd[2]);
 		break;
 	case "DEBUG":
-		if (!((this.mode&USERMODE_OPER) &&
-		      (this.flags&OLINE_CAN_DEBUG))) {
+		if (!((this.mode&USERMODE_OPER) && (this.flags&OLINE_CAN_DEBUG))) {
 			this.numeric481();
 			break;
 		}
@@ -669,8 +664,7 @@ function User_Work(cmdline) {
 		}
 		break;
 	case "DIE":
-		if (!((this.mode&USERMODE_OPER) &&
-		      (this.flags&OLINE_CAN_DIE))) {
+		if (!((this.mode&USERMODE_OPER) && (this.flags&OLINE_CAN_DIE))) {
 			this.numeric481();
 			break;
 		}
@@ -687,8 +681,7 @@ function User_Work(cmdline) {
 	case "ERROR":
 		break; // silently ignore
 	case "GLOBOPS":
-		if (!((this.mode&USERMODE_OPER) &&
-		      (this.flags&OLINE_CAN_GLOBOPS))) {
+		if (!((this.mode&USERMODE_OPER) && (this.flags&OLINE_CAN_GLOBOPS))) {
 			this.numeric481();
 			break;
 		}
@@ -762,8 +755,7 @@ function User_Work(cmdline) {
 		this.numeric("303", isonstr);
 		break;
 	case "KILL":
-		if (!(this.mode&USERMODE_OPER) ||
-		    !(this.flags&OLINE_CAN_LKILL)) {
+		if (!(this.mode&USERMODE_OPER) || !(this.flags&OLINE_CAN_LKILL)) {
 			this.numeric481();
 			break;
 		}
@@ -936,8 +928,7 @@ function User_Work(cmdline) {
 		this.numeric(272, ":End of SILENCE List.");
 		break;
 	case "LOCOPS":
-		if (!((this.mode&USERMODE_OPER) &&
-		      (this.flags&OLINE_CAN_LOCOPS))) {
+		if (!((this.mode&USERMODE_OPER) && (this.flags&OLINE_CAN_LOCOPS))) {
 			this.numeric481();
 			break;
 		}
@@ -962,9 +953,13 @@ function User_Work(cmdline) {
 				break;
 			}
 		}
-		umode_notice(USERMODE_SPY,"Spy","MOTD requested by " +
-			this.nick + " (" + this.uprefix + "@" +
-			this.hostname + ") [" + this.servername + "]");
+		umode_notice(USERMODE_STATS_LINKS,"StatsLinks",format(
+			"MOTD requested by %s (%s@%s) [%s]",
+			this.nick,
+			this.uprefix,
+			this.hostname,
+			this.servername
+		));
 		this.motd();
 		break;
 	case "NAMES":
@@ -1067,19 +1062,18 @@ function User_Work(cmdline) {
 		}
 		var oper_success = false;
 		for (ol in OLines) {
-			if( (cmd[1].toUpperCase() ==
-			     OLines[ol].nick.toUpperCase())
-			   &&
-			    (wildmatch(this.uprefix + "@" +
-			     this.hostname,OLines[ol].hostmask)
-			    )
-			   &&
-			      (((cmd[2] == OLines[ol].password) &&
-			      !(OLines[ol].flags&OLINE_CHECK_SYSPASSWD)
-			     ) || (
-			      (OLines[ol].flags&OLINE_CHECK_SYSPASSWD)
-			      && system.check_syspass(cmd[2])
-			  ) ) ) {
+			if (  (cmd[1].toUpperCase() == OLines[ol].nick.toUpperCase())
+				&& (wildmatch(this.uprefix + "@" + this.hostname,OLines[ol].hostmask))
+				&& (
+						(  (cmd[2] == OLines[ol].password)
+							&& !(OLines[ol].flags&OLINE_CHECK_SYSPASSWD)
+						)
+					|| (
+						(OLines[ol].flags&OLINE_CHECK_SYSPASSWD)
+						&& system.check_syspass(cmd[2])
+					)
+				)
+			) {
 				oper_success=true;
 				this.ircclass = OLines[ol].ircclass;
 				this.flags = OLines[ol].flags;
@@ -1126,8 +1120,7 @@ function User_Work(cmdline) {
 		this.quit(IRC_string(cmdline,1));
 		break;
 	case "REHASH":
-		if (!((this.mode&USERMODE_OPER) &&
-		      (this.flags&OLINE_CAN_REHASH))) {
+		if (!((this.mode&USERMODE_OPER) && (this.flags&OLINE_CAN_REHASH))) {
 			this.numeric481();
 			break;
 		}
@@ -1138,10 +1131,10 @@ function User_Work(cmdline) {
 				umode_notice(USERMODE_SERVER,"Notice",this.nick
 					+ " is clearing temp klines while whistling innocently");
 				for (kl in KLines) {
-					if(KLines[kl].type ==
-					   "k")
-							delete KLines[kl];
+					if(KLines[kl].type == "k") {
+						delete KLines[kl];
 					}
+				}
 				break;
 			case "GC":
 				if (js.gc!=undefined) {
@@ -1172,8 +1165,7 @@ function User_Work(cmdline) {
 		}
 		break;
 	case "RESTART":
-		if (!((this.mode&USERMODE_OPER) &&
-		      (this.flags&OLINE_CAN_RESTART))) {
+		if (!((this.mode&USERMODE_OPER) && (this.flags&OLINE_CAN_RESTART))) {
 			this.numeric481();
 			break;
 		}
@@ -1189,8 +1181,7 @@ function User_Work(cmdline) {
 		terminate_everything(rs_str);
 		break;
 	case "SQUIT":
-		if (!((this.mode&USERMODE_OPER) &&
-		      (this.flags&OLINE_CAN_LSQUITCON))) {
+		if (!((this.mode&USERMODE_OPER) && (this.flags&OLINE_CAN_LSQUITCON))) {
 			this.numeric481();
 			break;
 		}
@@ -1358,16 +1349,14 @@ function User_Work(cmdline) {
 				break;
 			}
 			if (dest_server != -1) {
-				dest_server.rawout(":" + this.nick + " VERSION :"
-					+ dest_server.nick);
+				dest_server.rawout(":" + this.nick + " VERSION :" + dest_server.nick);
 				break;
 			}
 		}
 		this.numeric351();
 		break;
 	case "WALLOPS":
-		if (!((this.mode&USERMODE_OPER) &&
-		      (this.flags&OLINE_CAN_WALLOPS))) {
+		if (!((this.mode&USERMODE_OPER) && (this.flags&OLINE_CAN_WALLOPS))) {
 			this.numeric481();
 			break;
 		}
@@ -1538,8 +1527,6 @@ function User_Quit(str,suppress_bcast,is_netsplit,origin) {
 
 	if (!suppress_bcast)
 		this.bcast_to_servers(tmp);
-	else if (is_netsplit)
-		this.bcast_to_servers(tmp,DREAMFORGE); /* DF doesn't have NOQUIT */
 
 	if (this.local) {
 		if(server.client_remove!=undefined)
-- 
GitLab