From 1c8f3541b0c66b09571546f75bc8b9f5c975640d Mon Sep 17 00:00:00 2001
From: cyan <>
Date: Sat, 6 Sep 2003 06:45:04 +0000
Subject: [PATCH] * Fixed various 3.10 compatibility bugs, although running
 ircd.js under 3.10   is strongly discouraged and should only be used for
 testing. * Don't 301 if a PRIVMSG to an AWAY user originated non-locally. *
 Support nick@server as valid targets for NOTICE and PRIVMSG. * Support
 CS,NS,MS,OS,CHANSERV,NICKSERV,MEMOSERV,OPERSERV, and IDENTIFY   commands,
 these are helper commands per Bahamut 1.4 used to send messages to   services
 only.  Thanks to Foobar for pointing this and the above item out. * Support
 oper sending of messages to #*mask.tld as per RFC. * Cleaned up the global
 message sending routine, added numeric 413,414 per RFC. * INVITE wasn't
 propagating across the network, and an invite wasn't expiring   once a user
 entered a channel after being invited like it should've. * Enhanced the DEBUG
 oper command to accept arguments depending on what debug   setting you want
 to tweak.  "DEBUG D" to toggle debug mode on/off, and   "DEBUG Y <val>" to
 set branch.yield_freq to <val>, suggested by DigitalMan.

---
 exec/ircd.js | 198 +++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 162 insertions(+), 36 deletions(-)

diff --git a/exec/ircd.js b/exec/ircd.js
index 398505e801..700cda9599 100644
--- a/exec/ircd.js
+++ b/exec/ircd.js
@@ -684,8 +684,9 @@ log(VERSION + " started.");
 ///// Main Loop /////
 while (!server.terminated) {
 
-	branch.limit=0; // we're not an infinite loop.
-	yield(); // don't peg the CPU
+	if(this.branch!=undefined)
+		branch.limit=0; // we're not an infinite loop.
+	mswait(1); // don't peg the CPU
 
 	if(server.terminated)
 		break;
@@ -758,6 +759,13 @@ function IRCClient(socket,new_id,local_client,do_newconn) {
 	this.ircnuh getter = function() {
 		return(this.nick + "!" + this.uprefix + "@" + this.hostname);
 	};
+	this.isulined getter = function() {
+		for (my_ul in ULines) {
+			if (this.nick.toUpperCase() == ULines[my_ul].toUpperCase())
+				return 1;
+		}
+		return 0;
+	};
 	this.hops=0;
 	this.server=false;
 	this.servername = servername;
@@ -777,6 +785,7 @@ function IRCClient(socket,new_id,local_client,do_newconn) {
 	this.numeric402=IRCClient_numeric402;
 	this.numeric403=IRCClient_numeric403;
 	this.numeric411=IRCClient_numeric411;
+	this.numeric440=IRCClient_numeric440;
 	this.numeric441=IRCClient_numeric441;
 	this.numeric442=IRCClient_numeric442;
 	this.numeric451=IRCClient_numeric451;
@@ -802,6 +811,8 @@ function IRCClient(socket,new_id,local_client,do_newconn) {
 	this.do_part=IRCClient_do_part;
 	this.do_whois=IRCClient_do_whois;
 	this.do_msg=IRCClient_do_msg;
+	this.global=IRCClient_global;
+	this.services_msg=IRCClient_services_msg;
 	this.part_all=IRCClient_part_all;
 	this.server_commands=IRCClient_server_commands;
 	this.unregistered_commands=IRCClient_unregistered_commands;
@@ -832,7 +843,7 @@ function IRCClient(socket,new_id,local_client,do_newconn) {
 				went_into_hostname = time();
 			if (resolve_hostnames)
 				possible_hostname = resolve_host(this.socket.remote_ip_address);
-			if (possible_hostname) {
+			if (resolve_hostnames && possible_hostname) {
 				this.hostname = possible_hostname;
 				if(server.client_update != undefined)
 					server.client_update(this.socket,this.nick, this.hostname);
@@ -1070,6 +1081,10 @@ function IRCClient_numeric411(str) {
 	this.numeric("411", ":No recipient given. (" + str + ")");
 }
 
+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.");
 }
@@ -1348,26 +1363,54 @@ function IRCClient_do_whois(wi) {
 		this.numeric(317, wi.nick + " " + (time() - wi.talkidle) + " " + wi.connecttime + " :seconds idle, signon time");
 }
 
-function IRCClient_do_msg(target,type_str,send_str) {
-	if ((target[0] == "$") && (this.mode&USERMODE_OPER)) {
-		var global_mask = target.slice(1);
-		var global_str = type_str + " " + target + " :" + send_str;
-		for(globClient in Clients) {
-			var Client = Clients[globClient];
-			if ((Client.conntype == TYPE_USER) &&
-			    match_irc_mask(Client.servername,global_mask) &&
-			    Client.local)
-				Client.originatorout(global_str,this);
-		}
-		global_str = ":" + this.nick + " " + global_str;
-		if(this.parent) {
-			var globServer = Clients[this.parent];
-			globServer.bcast_to_servers_raw(global_str);
-		} else {
-			server_bcast_to_servers(global_str);
-		}
-		return 1;
+function IRCClient_services_msg(svcnick,send_str) {
+	// First, make sure the nick exists.
+	var service_nickname = searchbynick(svcnick);
+	if (!service_nickname) {
+		this.numeric440(svcnick);
+		return 0;
 	}
+	var service_server = searchbyserver(service_nickname.servername);
+	if (!service_server || !service_server.isulined) {
+		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 Clients) {
+		var Client = Clients[globClient];
+		if (target[0] == "#")
+			var global_match = Client.hostname;
+		else // assume $
+			var global_match = Client.servername;
+		if ((Client.conntype == TYPE_USER) && Client.local &&
+		    match_irc_mask(global_match,global_mask) )
+			Client.originatorout(global_str,this);
+	}
+	global_str = ":" + this.nick + " " + global_str;
+	if(this.parent)
+		Clients[this.parent].bcast_to_servers_raw(global_str);
+	else
+		server_bcast_to_servers(global_str);
+	return 1;
+}
+
+function IRCClient_do_msg(target,type_str,send_str) {
+	if ((target[0] == "$") && (this.mode&USERMODE_OPER))
+		return this.global(target,type_str,send_str);
 
 	send_to_list = -1;
 	if (target[0] == "@" && ( (target[1] == "#") || target[1] == "&") ) {
@@ -1381,8 +1424,13 @@ function IRCClient_do_msg(target,type_str,send_str) {
 	if ((target[0] == "#") || (target[0] == "&")) {
 		chan = searchbychannel(target);
 		if (!chan) {
-			this.numeric401(target);
-			return 0;
+			// check to see if it's a #*hostmask* oper message
+			if ((target[0] == "#") && (this.mode&USERMODE_OPER)) {
+				return this.global(target,type_str,send_str);
+			} else {
+				this.numeric401(target);
+				return 0;
+			}
 		}
 		if ( (chan.mode&CHANMODE_NOOUTSIDE) &&
 		     !this.onchannel(chan.nam.toUpperCase())) {
@@ -1415,17 +1463,38 @@ function IRCClient_do_msg(target,type_str,send_str) {
 			this.bcast_to_channel_servers(chan.nam, str);
 		}
 	} else {
-		target_socket = searchbynick(target);
+		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;
+			}
+			target = msg_arg[0] + "@" + msg_arg[1];
+		} else {
+			var real_target = target;
+		}
+		target_socket = searchbynick(real_target);
 		if (target_socket) {
-			str = type_str +" " + target_socket.nick +" :"+ send_str;
+			if (!(target_server &&
+			    (target_server.nick.toUpperCase() ==
+			     target_socket.servername.toUpperCase()) 
+			   ) ) {
+				this.numeric401(target);
+				return 0;
+			}
+			str = type_str + " " + target + " :" + send_str;
 			target_socket.originatorout(str,this);
-			if (target_socket.away && (type_str == "PRIVMSG") )
+			if (target_socket.away && (type_str == "PRIVMSG") &&
+			    !this.server)
 				this.numeric(301, target_socket.nick + " :" + target_socket.away);
 		} else {
 			this.numeric401(target);
 			return 0;
 		}
 	}
+	return 1;
 }
 
 function IRCClient_do_join(chan_name,join_key) {
@@ -1502,7 +1571,7 @@ function IRCClient_do_join(chan_name,join_key) {
 		if (chan_name[0] != "&")
 			server_bcast_to_servers(":" + servername + " SJOIN " + Channels[chan].created + " " + Channels[chan].nam + " " + Channels[chan].chanmode() + " :@" + this.nick);
 	}
-	if (this.invited == Channels[chan].nam)
+	if (this.invited.toUpperCase() == Channels[chan].nam.toUpperCase())
 		this.invited = "";
 	this.channels.push(chan);
 	if (!this.parent) {
@@ -2162,14 +2231,32 @@ function IRCClient_registered_commands(command, cmdline) {
 				this.numeric481();
 				break;
 			}
-			if (debug) {
-				debug=false;
-				oper_notice("Notice","Debug mode disabled by " + this.ircnuh);
-				log("!NOTICE debug mode disabled by " + this.ircnuh);
-			} else {
-				debug=true;
-				oper_notice("Notice","Debug mode enabled by " + this.ircnuh);
-				log("!NOTICE debug mode enabled by " + this.ircnuh);
+			if (!cmd[1]) {
+				this.server_notice("Usage:");
+				this.server_notice("  DEBUG D       - Toggle DEBUG mode on/off");
+				this.server_notice("  DEBUG Y <val> - Set yield frequency to <val>");
+				break;
+			}
+			switch (cmd[1][0].toUpperCase()) {
+				case "D":
+					if (debug) {
+						debug=false;
+						oper_notice("Notice","Debug mode disabled by " + this.ircnuh);
+						log("!NOTICE debug mode disabled by " + this.ircnuh);
+					} else {
+						debug=true;
+						oper_notice("Notice","Debug mode enabled by " + this.ircnuh);
+						log("!NOTICE debug mode enabled by " + this.ircnuh);
+					}
+					break;
+				case "Y":
+					if (cmd[2]) {
+						oper_notice("Notice","branch.yield_freq set to " + cmd[2] + " by " + this.ircnuh);
+						branch.yield_freq = parseInt(cmd[2]);
+					}
+					break;
+				default:
+					break;
 			}
 			break;
 		case "INFO":
@@ -2977,6 +3064,29 @@ function IRCClient_registered_commands(command, cmdline) {
 			break;
 		case "ERROR":
 			break; // silently ignore.
+		case "CS":
+		case "CHANSERV":
+			this.services_msg("ChanServ",ircstring(cmdline));
+			break;
+		case "NS":
+		case "NICKSERV":
+			this.services_msg("NickServ",ircstring(cmdline));
+			break;
+		case "MS":
+		case "MEMOSERV":
+			this.services_msg("MemoServ",ircstring(cmdline));
+			break;
+		case "OS":
+		case "OPERSERV":
+			this.services_msg("OperServ",ircstring(cmdline));
+			break;
+		case "IDENTIFY":
+			if (cmd[1][0] == "#")
+				var services_target = "ChanServ";
+			else
+				var services_target = "NickServ";
+			this.services_msg(services_target,"IDENTIFY " + ircstring(cmdline));
+			break;
 		default:
 			this.numeric("421", command + " :Unknown command.");
 			break;
@@ -3023,6 +3133,22 @@ function IRCClient_server_commands(origin, command, cmdline) {
 			oper_notice("Notice", "ERROR :from " + ThisOrigin.nick + "[(+)0@" + this.hostname + "] -- " + ircstring(cmdline));
 			ThisOrigin.quit();
 			break;
+		case "INVITE":
+			if (!cmd[2])
+				break;
+			chanid = searchbychannel(cmd[2]);
+			if (!chanid)
+				break;
+			if (!chanid.ismode(this.id,CHANLIST_OP))
+				break;
+			nickid = searchbynick(cmd[1]);
+			if (!nickid)
+				break;
+			if (nickid.onchannel(chanid.nam.toUpperCase()))
+				break;
+			nickid.originatorout("INVITE " + nickid.nick + " :" + chanid.nam,this);
+			nickid.invited=chanid.nam.toUpperCase();
+			break;
 		case "KICK":
 			if (!cmd[2])
 				break;
-- 
GitLab