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