diff --git a/exec/ircd.js b/exec/ircd.js index 2d834646da9081df8a2f0eb31f48d4471d7bb381..29d1f1bbd040773fb470b951aed68dd9e6ee32b4 100644 --- a/exec/ircd.js +++ b/exec/ircd.js @@ -26,6 +26,7 @@ load("sbbsdefs.js"); load("sockdefs.js"); load("nodedefs.js"); load("irclib.js"); +load("dns.js"); /* Libraries specific to the IRCd */ load("ircd/core.js"); @@ -35,293 +36,122 @@ load("ircd/channel.js"); load("ircd/server.js"); load("ircd/config.js"); -/* Global Constants */ - -const VERSION = "SynchronetIRCd-1.9a"; +/*** Global Constants - Always in ALL_UPPERCASE ***/ +const VERSION = "SynchronetIRCd-1.9b"; const VERSION_STR = format( "Synchronet %s%s-%s%s (IRCd by Randy Sommerfeld)", system.version, system.revision, system.platform, system.beta_version ); - -/* 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 -// is important because connecing is a BLOCKING operation, so your IRC *will* -// freeze for the amount of time it takes to connect. -const ob_sock_timeout = 3; - -// Should we enable the USERS and SUMMON commands? These allow IRC users to -// view users on the local BBS and summon them to IRC via a Synchronet telegram -// message respectively. Some people might be running the ircd standalone, or -// otherwise don't want anonymous IRC users to have access to these commands. -// We enable this by default because there's typically nothing wrong with -// seeing who's on an arbitrary BBS or summoning them to IRC. -const enable_users_summon = true; - -// 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 -// cause your network to desynchronize (and possibly crash the IRCD) -// Remember, this is Synchronet, not Desynchronet ;) -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 = 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 - -const server_uptime = time(); +/* This will be replaced with a dynamic CAPAB system */ +const SERVER_CAPAB = "TS3 NOQUIT SSJOIN BURST UNCONNECT NICKIP TSMODE"; +/* This will be in the configuration for 2.0 */ +const SUMMON = true; + +/* Need to detect when a server doesn't line up with these on the network. */ +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 = 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 */ +const MAX_WHOWAS = 1000; /* Size of the WHOWAS buffer */ +const MAX_NICKHISTORY = 1000; /* Size of the nick change history buffer */ +const MAX_CLIENT_RECVQ = 2560;/* Maximum size of unregistered & user recvq */ +const MAX_AWAYLEN = 80; /* Maximum away message length */ +const MAX_USERHOST = 6; /* Maximum arguments to USERHOST command */ +const MAX_REALNAME = 50; /* Maximum length of users real name field */ + +const SERVER_UPTIME = system.timer; +const SERVER_UPTIME_STRF = strftime("%a %b %d %Y at %H:%M:%S %Z",time()); + +/*** Global Objects, Arrays and Variables - Always in Mixed_Case ***/ + +/* Global Objects */ +var DNS_Resolver = new DNS(); + +/* Every object (unregistered, server, user) is tagged with a unique ID */ +var Assigned_IDs = {}; /* Key: Numeric ID */ + +var Unregistered = {}; /* Key: Numeric ID */ +var Users = {}; /* Key: .toUpperCase() nick */ +var Servers = {}; /* Key: .toLowerCase() nick */ +var Channels = {}; /* Key: .toUpperCase() channel name, including prefix */ + +var Local_Users = {}; +var Local_Servers = {}; + +var WhoWas = {}; /* Stores uppercase nicks */ +var WhoWasMap = []; /* An array pointing to WhoWas object entries */ + +var NickHistory = []; /* Nick change tracking */ + +var Profile = {}; /* CPU profiling */ /* Global Variables */ +var Default_Port = 6667; -// 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; - -/* This was previously on its own in the functions - Maybe there was a reason why? */ -var time_config_read; - -/* Primary arrays */ -var Unregistered = new Object; -var Users = new Object; -var Servers = new Object; -var Channels = new Object; - -var Local_Sockets = new Object; -var Local_Sockets_Map = new Object; - -var Selectable_Sockets = new Object; -var Selectable_Sockets_Map = new Object; - -/* Highest Connection Count tracking */ -var hcc_total = 0; -var hcc_users = 0; -var hcc_counter = 0; - -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 */ - -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. */ -var Profile = new Object; - -/* 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. -var Local_Users = new Object; -var Local_Servers = new Object; - -var rebuild_socksel_array = true; - -var network_debug = false; - -var last_recvq_check = 0; - -var servername = "server.invalid"; -var serverdesc = "No description provided."; - -log(VERSION + " started."); - -// Parse command-line arguments. -var config_filename=""; -var cmdline_port; -var cmdarg; -for (cmdarg=0;cmdarg<argc;cmdarg++) { - switch(argv[cmdarg].toLowerCase()) { - case "-f": - config_filename = argv[++cmdarg]; - break; - case "-p": - cmdline_port = parseInt(argv[++cmdarg]); - break; - case "-d": - debug=true; - break; - case "-a": - cmdline_addr = argv[++cmdarg].split(','); - break; - } -} +var Time_Config_Read; /* Stores time() of when the config was last read */ -/* Temporary hack to make JSexec testing code not complain */ -var mline_port; +/* Will this server try to enforce good network behaviour? */ +/* Setting to "true" results in bouncing bad modes, KILLing bogus NICKs, etc. */ +var Enforcement = true; -read_config_file(); +/* Highest Connection Count ("HCC") tracking */ +var HCC_Total = 0; +var HCC_Users = 0; +var HCC_Counter = 0; -/* This tests if we're running from JSexec or not */ -if(this.server==undefined) { - if (!jsexec_revision_detail) - var jsexec_revision_detail = "JSexec"; +var ServerName; +var ServerDesc = ""; - if (cmdline_port) - default_port = cmdline_port; - else if (typeof mline_port !== undefined) - default_port = mline_port; +/* Runtime configuration */ +var Config_Filename = ""; - var server = { - socket: false, - terminated: false, - version_detail: jsexec_revision_detail, - interface_ip_addr_list: ["0.0.0.0","::"] - }; +var Restart_Password; +var Die_Password; - server.socket = create_new_socket(default_port) +var Admin1; +var Admin2; +var Admin3; - if (!server.socket) - exit(); -} +var CLines = []; /* Server [C]onnect lines */ +var NLines = []; /* Server inbound connect lines */ +var HLines = []; /* Hubs */ +var ILines = []; /* IRC Classes */ +var KLines = []; /* user@hostname based bans */ +var OLines = []; /* IRC Operators */ +var PLines = []; /* Ports for the IRCd to listen to */ +var QLines = []; /* [Q]uarantined (reserved) nicknames */ +var ULines = []; /* Servers allowed to send unchecked MODE amongst other things */ +var YLines = []; /* Defines what user & server objects get what settings */ +var ZLines = []; /* IP based bans */ -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 -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) { - var new_pl_sock = create_new_socket(PLines[pl]); - if (new_pl_sock) { - new_pl_sock.nonblocking = true; - new_pl_sock.debug = false; - open_plines.push(new_pl_sock); - } -} +/** Begin executing code **/ -js.branch_limit=0; // we're not an infinite loop. -js.auto_terminate=false; // we handle our own termination requests - -/*** 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. */ - for (pl in open_plines) { - if (open_plines[pl].poll()) { - var client_sock=open_plines[pl].accept(); - log(LOG_DEBUG,"Accepting new connection on port " - + client_sock.local_port); - if(client_sock) { - client_sock.nonblocking = true; - switch(client_sock.local_port) { - case 994: - case 6697: - client_sock.ssl_server=1; - } - if (!client_sock.remote_ip_address) { - log(LOG_DEBUG,"Socket has no IP address. Closing."); - client_sock.close(); - } else if (iszlined(client_sock.remote_ip_address)) { - 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; - next_client_id++; - if(server.client_add != undefined) - server.client_add(client_sock); - if(server.clients != undefined) - log(LOG_DEBUG,format("%d clients", server.clients)); - Unregistered[new_id] = new Unregistered_Client(new_id, - client_sock); - } - } else - log(LOG_DEBUG,"!ERROR " + open_plines[pl].error - + " accepting connection"); - } - } +log(LOG_NOTICE, VERSION + " started."); - // Check for pending DNS hostname resolutions. - for(this_unreg in Unregistered) { - if (Unregistered[this_unreg] && - Unregistered[this_unreg].pending_resolve_time) - Unregistered[this_unreg].resolve_check(); - } +/* If we're running from JSexec we don't have a global server object, so fake one. */ +if (server === undefined) { + /* Define the global here so Startup() can manipulate it. */ + var server = "JSexec"; +} - // Only rebuild our selectable sockets if required. - if (rebuild_socksel_array) { - Selectable_Sockets = new Array; - Selectable_Sockets_Map = new Array; - for (i in Local_Sockets) { - Selectable_Sockets.push(Local_Sockets[i]); - Selectable_Sockets_Map.push(Local_Sockets_Map[i]); - } - rebuild_socksel_array = false; - } +Startup(); - /* Check for ping timeouts and process queues. */ - /* FIXME/TODO: These need to be changed to a mapping system ASAP. */ - 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(); - } - } +js.do_callbacks = true; - // do some work. - if (Selectable_Sockets.length) { - var readme = socket_select(Selectable_Sockets, 1 /*secs*/); - try { - for(thisPolled in readme) { - if (Selectable_Sockets_Map[readme[thisPolled]]) { - var conn = Selectable_Sockets_Map[readme[thisPolled]]; - if (!conn.socket.is_connected) { - conn.quit("Connection reset by peer."); - continue; - } - conn.recvq.recv(conn.socket); - } - } - } catch(e) { - 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); - } - - // Scan C:Lines for servers to connect to automatically. - 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", - format("Auto-connecting to %s (%s)", - CLines[thisCL].servername, - CLines[thisCL].host - ) - ); - connect_to_server(CLines[thisCL]); - } +function config_rehash_semaphore_check() { + if(file_date(system.ctrl_dir + "ircd.rehash") > Time_Config_Read) { + Read_Config_File(); } } +js.setInterval(config_rehash_semaphore_check, 1000 /* milliseconds */); + +Open_PLines(); -/* We've exited the main loop, so terminate everything. */ -terminate_everything("Terminated."); +/* We exit here and pass everything to the callback engine. */ +/* Deuce says I can't use exit(). So just pretend it's here instead. */ diff --git a/exec/load/ircd/channel.js b/exec/load/ircd/channel.js index 9be6b6e07f5ee586f640e9959844b084510f2db7..c4a7c3595d9706f3a287c8fbc7d2e8c45f560d4b 100644 --- a/exec/load/ircd/channel.js +++ b/exec/load/ircd/channel.js @@ -19,69 +19,60 @@ */ -const CHANMODE_NONE =(1<<0); // NONE -const CHANMODE_BAN =(1<<1); // b -const CHANMODE_INVITE =(1<<2); // i -const CHANMODE_KEY =(1<<3); // k -const CHANMODE_LIMIT =(1<<4); // l -const CHANMODE_MODERATED=(1<<5); // m -const CHANMODE_NOOUTSIDE=(1<<6); // n -const CHANMODE_OP =(1<<7); // o -const CHANMODE_PRIVATE =(1<<8); // p -const CHANMODE_SECRET =(1<<9); // s -const CHANMODE_TOPIC =(1<<10); // t -const CHANMODE_VOICE =(1<<11); // v - -/* These are used in the mode crunching section to figure out what character - to display in the crunched MODE line. */ -function Mode(modechar,args,state,list,isnick) { - this.modechar = modechar; /* The mode's character */ - this.args = args; /* Does this mode take only a single arg? */ - this.state = state; /* Stateful? (changes channel behaviour) */ - this.list = list; /* Does this mode accept a list? */ - this.isnick = isnick; /* Is nick (true) or a n!u@h mask (false) */ -} +/* Channel Modes */ +const CHANMODE_NONE =(1<<0); +const CHANMODE_BAN =(1<<1); /* +b */ +const CHANMODE_INVITE =(1<<2); /* +i */ +const CHANMODE_KEY =(1<<3); /* +k */ +const CHANMODE_LIMIT =(1<<4); /* +l */ +const CHANMODE_MODERATED=(1<<5); /* +m */ +const CHANMODE_NOOUTSIDE=(1<<6); /* +n */ +const CHANMODE_OP =(1<<7); /* +o */ +const CHANMODE_PRIVATE =(1<<8); /* +p */ +const CHANMODE_SECRET =(1<<9); /* +s */ +const CHANMODE_TOPIC =(1<<10); /* +t */ +const CHANMODE_VOICE =(1<<11); /* +v */ + +const MODE = {}; +MODE[CHANMODE_BAN] = new IRC_Channel_Mode("b",true,false,true,false); +MODE[CHANMODE_INVITE] = new IRC_Channel_Mode("i",false,true,false,false); +MODE[CHANMODE_KEY] = new IRC_Channel_Mode("k",true,true,false,false); +MODE[CHANMODE_LIMIT] = new IRC_Channel_Mode("l",true,true,false,false); +MODE[CHANMODE_MODERATED]= new IRC_Channel_Mode("m",false,true,false,false); +MODE[CHANMODE_NOOUTSIDE]= new IRC_Channel_Mode("n",false,true,false,false); +MODE[CHANMODE_OP] = new IRC_Channel_Mode("o",true,false,true,true); +MODE[CHANMODE_PRIVATE] = new IRC_Channel_Mode("p",false,true,false,false); +MODE[CHANMODE_SECRET] = new IRC_Channel_Mode("s",false,true,false,false); +MODE[CHANMODE_TOPIC] = new IRC_Channel_Mode("t",false,true,false,false); +MODE[CHANMODE_VOICE] = new IRC_Channel_Mode("v",true,false,true,true); + +/* Object Prototypes */ -MODE = new Object; -MODE[CHANMODE_BAN] = new Mode("b",true,false,true,false); -MODE[CHANMODE_INVITE] = new Mode("i",false,true,false,false); -MODE[CHANMODE_KEY] = new Mode("k",true,true,false,false); -MODE[CHANMODE_LIMIT] = new Mode("l",true,true,false,false); -MODE[CHANMODE_MODERATED]= new Mode("m",false,true,false,false); -MODE[CHANMODE_NOOUTSIDE]= new Mode("n",false,true,false,false); -MODE[CHANMODE_OP] = new Mode("o",true,false,true,true); -MODE[CHANMODE_PRIVATE] = new Mode("p",false,true,false,false); -MODE[CHANMODE_SECRET] = new Mode("s",false,true,false,false); -MODE[CHANMODE_TOPIC] = new Mode("t",false,true,false,false); -MODE[CHANMODE_VOICE] = new Mode("v",true,false,true,true); - -////////// Objects ////////// function Channel(nam) { - this.nam=nam; - this.mode=CHANMODE_NONE; - this.topic=""; - this.topictime=0; - this.topicchangedby=""; - this.arg = new Object; + this.nam = nam; + this.mode = CHANMODE_NONE; + this.topic = ""; + this.topictime = 0; + this.topicchangedby = ""; + this.arg = {}; this.arg[CHANMODE_LIMIT] = 0; this.arg[CHANMODE_KEY] = ""; - this.users=new Object; - this.modelist=new Object; - this.modelist[CHANMODE_OP]=new Object; - this.modelist[CHANMODE_VOICE]=new Object; - this.modelist[CHANMODE_BAN]=new Array; /* True Array */ - this.bantime=new Object; - this.bancreator=new Object; - this.created=time(); - this.chanmode=Channel_chanmode; - this.isbanned=Channel_isbanned; - this.count_modelist=Channel_count_modelist; - this.occupants=Channel_occupants; - this.match_list_mask=Channel_match_list_mask; + this.users = {}; + this.modelist = {}; + this.modelist[CHANMODE_OP] = {}; + this.modelist[CHANMODE_VOICE] = {}; + this.modelist[CHANMODE_BAN] = []; + this.bantime = {}; + this.bancreator = {}; + this.created = time(); + /* Functions */ + this.chanmode = Channel_chanmode; + this.isbanned = Channel_isbanned; + this.count_modelist = Channel_count_modelist; + this.occupants = Channel_Occupants; + this.match_list_mask = Channel_match_list_mask; } -////////// Functions ////////// - function ChanMode_tweaktmpmode(tmp_bit,add) { if ( !this.chan.modelist[CHANMODE_OP][this.user.id] && !this.user.server @@ -100,7 +91,9 @@ function ChanMode_tweaktmpmode(tmp_bit,add) { } } -function ChanMode_tweaktmpmodelist(tmp_bit,add,arg) { +function ChanMode_tweaktmpmodelist(bit,add,arg) { + var i; + if ( !this.chan.modelist[CHANMODE_OP][this.user.id] && !this.user.server && !this.user.uline @@ -109,29 +102,24 @@ function ChanMode_tweaktmpmodelist(tmp_bit,add,arg) { 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()) + for (i in this.tmplist[bit][add]) { + /* Is this argument in our list for this mode already? */ + if (this.tmplist[bit][add][i].toUpperCase() == arg.toUpperCase()) return 0; } - // It doesn't exist on our mode, push it in. - this.tmplist[tmp_bit][add].push(arg); - // Check for it against the other mode, and maybe nuke it. - var oadd; - if (add) - oadd = false; - else - oadd = true; - for (x in this.tmplist[tmp_bit][oadd]) { - if (this.tmplist[tmp_bit][oadd][x].toUpperCase() == arg.toUpperCase()) { - delete this.tmplist[tmp_bit][oadd][x]; + /* It doesn't exist on our mode, push it in. */ + this.tmplist[bit][add].push(arg); + /* Check for it against the other mode, and maybe nuke it. */ + for (i in this.tmplist[bit][add ? false : true]) { + if (this.tmplist[bit][add ? false : true][i].toUpperCase() == arg.toUpperCase()) { + delete this.tmplist[bit][add ? false : true][i]; return 0; } } } function ChanMode_affect_mode_list(list_bit) { - var tmp_nick; + var tmp_nick, add, z; for (add in this.tmplist[list_bit]) { for (z in this.tmplist[list_bit][add]) { tmp_nick = Users[this.tmplist[list_bit][add][z].toUpperCase()]; @@ -156,12 +144,15 @@ function ChanMode_affect_mode_list(list_bit) { } function Channel_count_modelist(list_bit) { - var tmp_counter=0; - for (tmp_count in this.modelist[list_bit]) { - if (this.modelist[list_bit][tmp_count]) - tmp_counter++; + var i; + var ret = 0; + + for (i in this.modelist[list_bit]) { + if (this.modelist[list_bit][i]) + ret++; } - return tmp_counter; + + return ret; } function Channel_chanmode(show_args) { @@ -194,32 +185,37 @@ function Channel_chanmode(show_args) { return tmp_mode + tmp_extras; } -function Channel_isbanned(banned_nuh) { - for (this_ban in this.modelist[CHANMODE_BAN]) { - if(wildmatch(banned_nuh,this.modelist[CHANMODE_BAN][this_ban])) +function Channel_isbanned(nuh) { + var i; + + for (i in this.modelist[CHANMODE_BAN]) { + if(wildmatch(nuh,this.modelist[CHANMODE_BAN][i])) return 1; } return 0; } -function Channel_occupants() { - var chan_occupants=""; - for(thisChannel_user in this.users) { - var Channel_user=this.users[thisChannel_user]; - if (Channel_user.nick) { - if (chan_occupants) - chan_occupants += " "; - if (this.modelist[CHANMODE_OP][Channel_user.id]) - chan_occupants += "@"; - if (this.modelist[CHANMODE_VOICE][Channel_user.id]) - chan_occupants += "+"; - chan_occupants += Channel_user.nick; +function Channel_Occupants() { + var i; + var user; + var ret = ""; + + for (i in this.users) { + user = this.users[i]; + if (user.nick) { + if (ret) + ret += " "; + if (this.modelist[CHANMODE_OP][user.id]) + ret += "@"; + if (this.modelist[CHANMODE_VOICE][user.id]) + ret += "+"; + ret += user.nick; } } - return chan_occupants; + + return ret; } -// Yay, version 3.0 of this.set_chanmode(), eradicates any global variables. function ChanMode(chan,user) { this.tmplist = new Object; this.tmplist[CHANMODE_OP] = new Object; @@ -249,6 +245,8 @@ function ChanMode(chan,user) { } function IRCClient_set_chanmode(chan,modeline,bounce_modes) { + var i, j; + if (!chan || !modeline) return; @@ -261,9 +259,9 @@ function IRCClient_set_chanmode(chan,modeline,bounce_modes) { var mode_counter=0; var mode_args_counter=1; // start counting at args, not the modestring - for (modechar in cm_args[0]) { + for (i in cm_args[0]) { mode_counter++; - switch (cm_args[0][modechar]) { + switch (cm_args[0][i]) { case "+": if (!add) add=true; @@ -345,11 +343,11 @@ function IRCClient_set_chanmode(chan,modeline,bounce_modes) { break; default: if ((!this.parent) && (!this.server)) - this.numeric(472, cm_args[0][modechar] + " :is unknown mode char to me."); + this.numeric(472, cm_args[0][i] + " :is unknown mode char to me."); mode_counter--; break; } - if (mode_counter == max_modes) + if (mode_counter == MAX_MODES) break; } @@ -358,22 +356,22 @@ function IRCClient_set_chanmode(chan,modeline,bounce_modes) { // function. Or, clear any ops, voiced members, or bans on the 'bad' // side of the network sync. if (bounce_modes) { - for (cm in MODE) { - if (MODE[cm].state && (chan.mode&cm) && !(cmode.addbits&cm)) { - cmode.delbits |= cm; - } else if (MODE[cm].list && MODE[cm].isnick) { - for (member in chan.modelist[cm]) { - cmode.delmodes += MODE[cm].modechar; - cmode.delmodeargs += " " + chan.modelist[cm][member].nick; - delete chan.modelist[cm][member]; + for (i in MODE) { + if (MODE[i].state && (chan.mode&i) && !(cmode.addbits&i)) { + cmode.delbits |= i; + } else if (MODE[i].list && MODE[i].isnick) { + for (j in chan.modelist[i]) { + cmode.delmodes += MODE[i].modechar; + cmode.delmodeargs += " " + chan.modelist[i][j].nick; + delete chan.modelist[i][j]; } - } else if (MODE[cm].list && !MODE[cm].isnick) { - for (ban in chan.modelist[cm]) { - cmode.delmodes += MODE[cm].modechar; - cmode.delmodeargs += " " + chan.modelist[cm][ban]; - delete chan.modelist[cm][ban]; - delete chan.bantime[ban]; - delete chan.bancreator[ban]; + } else if (MODE[i].list && !MODE[i].isnick) { + for (j in chan.modelist[i]) { + cmode.delmodes += MODE[i].modechar; + cmode.delmodeargs += " " + chan.modelist[i][j]; + delete chan.modelist[i][j]; + delete chan.bantime[j]; + delete chan.bancreator[j]; } } } @@ -381,40 +379,40 @@ function IRCClient_set_chanmode(chan,modeline,bounce_modes) { // Now we run through all the mode toggles and construct our lists for // later display. We also play with the channel bit switches here. - for (cm in MODE) { - if (MODE[cm].state) { - if ( (cm&CHANMODE_KEY) + for (i in MODE) { + if (MODE[i].state) { + if ( (i&CHANMODE_KEY) && (cmode.addbits&CHANMODE_KEY) - && cmode.state_arg[cm] - && chan.arg[cm] + && cmode.state_arg[i] + && chan.arg[i] && !this.server && !this.parent && !bounce_modes ) { this.numeric(467, format("%s :Channel key already set.", chan.nam)); - } else if ( (cmode.addbits&cm) + } else if ( (cmode.addbits&i) && ( - !(chan.mode&cm) + !(chan.mode&i) || ( - (cm==CHANMODE_LIMIT) + (i==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) { + cmode.addmodes += MODE[i].modechar; + chan.mode |= i; + if (MODE[i].args && MODE[i].state) { cmode.addmodeargs += " " + - cmode.state_arg[cm]; - chan.arg[cm] = cmode.state_arg[cm]; + cmode.state_arg[i]; + chan.arg[i] = cmode.state_arg[i]; } - } else if ((cmode.delbits&cm) && (chan.mode&cm)) { - cmode.delmodes += MODE[cm].modechar; - chan.mode &= ~cm; - if (MODE[cm].args && MODE[cm].state) { + } else if ((cmode.delbits&i) && (chan.mode&i)) { + cmode.delmodes += MODE[i].modechar; + chan.mode &= ~i; + if (MODE[i].args && MODE[i].state) { cmode.delmodeargs += " " + - cmode.state_arg[cm]; - chan.arg[cm] = ""; + cmode.state_arg[i]; + chan.arg[i] = ""; } } } @@ -436,10 +434,10 @@ function IRCClient_set_chanmode(chan,modeline,bounce_modes) { } // Bans are a specialized case, sigh. - for (z in cmode.tmplist[CHANMODE_BAN][true]) { // +b + for (i 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) + cmode.tmplist[CHANMODE_BAN][add][i]); + if ( (chan.count_modelist(CHANMODE_BAN) >= MAX_BANS) && !this.server && !this.parent ) { @@ -454,14 +452,14 @@ function IRCClient_set_chanmode(chan,modeline,bounce_modes) { } } - for (z in cmode.tmplist[CHANMODE_BAN][false]) { // -b + for (i in cmode.tmplist[CHANMODE_BAN][false]) { // -b for (ban in chan.modelist[CHANMODE_BAN]) { - if ( cmode.tmplist[CHANMODE_BAN][false][z].toUpperCase() + if ( cmode.tmplist[CHANMODE_BAN][false][i].toUpperCase() == chan.modelist[CHANMODE_BAN][ban].toUpperCase() ) { cmode.delmodes += "b"; cmode.delmodeargs += " " + - cmode.tmplist[CHANMODE_BAN][false][z]; + cmode.tmplist[CHANMODE_BAN][false][i]; delete chan.modelist[CHANMODE_BAN][ban]; delete chan.bantime[ban]; delete chan.bancreator[ban]; @@ -492,8 +490,8 @@ function IRCClient_set_chanmode(chan,modeline,bounce_modes) { var mode_counter = 0; var mode_output = ""; var f_mode_args = ""; - for (marg in final_args[0]) { - switch (final_args[0][marg]) { + for (i in final_args[0]) { + switch (final_args[0][i]) { case "+": mode_output += "+"; add = true; @@ -505,7 +503,7 @@ function IRCClient_set_chanmode(chan,modeline,bounce_modes) { case "l": if (!add) { mode_counter++; - mode_output += final_args[0][marg]; + mode_output += final_args[0][i]; break; } case "b": // only modes with arguments @@ -517,10 +515,10 @@ function IRCClient_set_chanmode(chan,modeline,bounce_modes) { f_mode_args += " " + final_args[arg_counter]; default: mode_counter++; - mode_output += final_args[0][marg]; + mode_output += final_args[0][i]; break; } - if (mode_counter >= max_modes) { + if (mode_counter >= MAX_MODES) { var str = "MODE " + chan.nam + " " + mode_output + f_mode_args; if (!this.server) this.bcast_to_channel(chan, str, true); @@ -551,7 +549,10 @@ function IRCClient_set_chanmode(chan,modeline,bounce_modes) { } function IRCClient_do_join(chan_name,join_key) { - chan_name = chan_name.slice(0,max_chanlen) + chan_name = chan_name.slice(0,MAX_CHANLEN) + + if (!join_key) + join_key = ""; var uc_chan_name = chan_name.toUpperCase(); @@ -567,7 +568,7 @@ function IRCClient_do_join(chan_name,join_key) { } if (this.channels[uc_chan_name]) return 0; - if ((true_array_len(this.channels) >= max_user_chans) && this.local) { + if ((true_array_len(this.channels) >= MAX_USER_CHANS) && this.local) { this.numeric("405", chan_name + " :You have joined too many channels."); return 0; } @@ -636,7 +637,7 @@ function IRCClient_do_join(chan_name,join_key) { if (chan_name[0] != "&") { this.bcast_to_servers_raw( format(":%s SJOIN %s %s %s :%s%s", - servername, + ServerName, chan.created, chan.nam, chan.chanmode(), @@ -684,11 +685,9 @@ function IRCClient_do_part(chan_name) { } function IRCClient_part_all() { - var partingChannel; + var i; - for(thisChannel in this.channels) { - partingChannel=this.channels[thisChannel]; - this.do_part(partingChannel.nam); + for (i in this.channels) { + this.do_part(this.channels[i].nam); } } - diff --git a/exec/load/ircd/config.js b/exec/load/ircd/config.js index 0eb148848e67690aa918091450a72b0c299fcf24..9f3abcb3a82825fd06ab043a8bcfc0aa7f377fca 100644 --- a/exec/load/ircd/config.js +++ b/exec/load/ircd/config.js @@ -20,9 +20,11 @@ */ function parse_nline_flags(flags) { + var i; var nline_flags = 0; - for(thisflag in flags) { - switch(flags[thisflag]) { + + for (i in flags) { + switch(flags[i]) { case "q": nline_flags |= NLINE_CHECK_QWKPASSWD; break; @@ -33,8 +35,10 @@ function parse_nline_flags(flags) { nline_flags |= NLINE_CHECK_WITH_QWKMASTER; break; default: - log(LOG_WARNING,"!WARNING Unknown N:Line flag '" - + flags[thisflag] + "' in config."); + log(LOG_WARNING,format( + "!WARNING Unknown N:Line flag '%s' in config.", + flags[i] + )); break; } } @@ -42,9 +46,11 @@ function parse_nline_flags(flags) { } function parse_oline_flags(flags) { + var i; var oline_flags = 0; - for(thisflag in flags) { - switch(flags[thisflag]) { + + for (i in flags) { + switch(flags[i]) { case "r": oline_flags |= OLINE_CAN_REHASH; break; @@ -105,7 +111,7 @@ function parse_oline_flags(flags) { break; case "x": case "X": - oline_flags |= OLINE_CAN_DEBUG; + oline_flags |= OLINE_CAN_EVAL; break; case "O": oline_flags |= OLINE_IS_GOPER; @@ -126,39 +132,50 @@ function parse_oline_flags(flags) { oline_flags |= OLINE_CAN_UMODEC; break; default: - log(LOG_WARNING,"!WARNING Unknown O:Line flag '" - + flags[thisflag] + "' in config."); + log(LOG_WARNING,format( + "!WARNING Unknown O:Line flag '%s' in config.", + flags[i] + )); break; } } return oline_flags; } -function read_config_file() { +function Read_Config_File() { + var i; + /* 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; + if (typeof CLines !== 'undefined') { + for (i in CLines) { + if (CLines[i].next_connect) + js.clearTimeout(CLines[i].next_connect); + } + } + CLines = []; + HLines = []; + ILines = []; + KLines = []; + NLines = []; + OLines = []; + PLines = []; + QLines = []; + ULines = []; + YLines = []; + ZLines = []; + Die_Password = ""; + Restart_Password = ""; /* End of global variables */ + var fname=""; - if (config_filename && config_filename.length) { - if(config_filename.indexOf('/')>=0 || config_filename.indexOf('\\')>=0) - fname=config_filename; + 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; + fname=system.ctrl_dir + Config_Filename; } else { fname=system.ctrl_dir + "ircd." + system.local_host_name + ".conf"; if(!file_exists(fname)) @@ -175,13 +192,13 @@ function read_config_file() { read_conf_config(file_handle); file_handle.close(); } else { - log("Couldn't open configuration file! Proceeding with defaults."); + log(LOG_NOTICE, "Couldn't open configuration file! Proceeding with defaults."); } - time_config_read = time(); - scan_for_klined_clients(); + Time_Config_Read = time(); + Scan_For_Banned_Clients(); - YLines[0] = new YLine(120,600,1,5050000); // default irc class + YLines[0] = new YLine(120,600,1,5050000); /* Default IRC class */ } function ini_sections() { @@ -197,9 +214,8 @@ function ini_sections() { } function ini_IRCdInfo(arg, ini) { - log("ircdinfo " + ini.Hostname); - servername=ini.Hostname; - serverdesc=ini.Info; + ServerName=ini.Hostname; + ServerDesc=ini.Info; Admin1=ini.Admin1; Admin2=ini.Admin2; Admin3=ini.Admin3; @@ -207,7 +223,7 @@ function ini_IRCdInfo(arg, ini) { /* Former M:Line */ function ini_Port(arg, ini) { - mline_port = arg; + Default_Port = arg; } /* Former Y:Line */ @@ -256,21 +272,19 @@ function ini_Hub(arg, ini) { function read_ini_config(conf) { var ini = conf.iniGetAllObjects(); - var split; - var section_name; - var section_arg; var Sections = new ini_sections(); + var i, s; - 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); + for (var i in ini) { + s = i.name.split(":"); + if (typeof Sections[s[0]] === 'function') + Sections[s[0]](s[1], i); } } function read_conf_config(conf) { + var conf_line, arg, i; + function fancy_split(line) { var ret = []; var i; @@ -309,21 +323,15 @@ function read_conf_config(conf) { } while (!conf.eof) { - var conf_line = conf.readln(); + 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); + arg = fancy_split(conf_line); + for (i in arg) { + arg[i]=arg[i].replace(/SYSTEM_HOST_NAME/g,system.host_name); + arg[i]=arg[i].replace(/SYSTEM_NAME/g,system.name); + arg[i]=arg[i].replace(/VERSION_NOTICE/g,system.version_notice); + if (typeof system.qwk_id !== 'undefined') + arg[i]=arg[i].replace(/SYSTEM_QWKID/g,system.qwk_id.toLowerCase()); } switch (conf_line[0].toUpperCase()) { case "A": @@ -336,8 +344,7 @@ function read_conf_config(conf) { case "C": if (!arg[5]) break; - CLines.push(new CLine(arg[1],arg[2],arg[3],arg[4], - parseInt(arg[5]) )); + CLines.push(new CLine(arg[1],arg[2],arg[3],arg[4],parseInt(arg[5]))); break; case "H": if (!arg[3]) @@ -355,8 +362,7 @@ function read_conf_config(conf) { break; var kline_mask = create_ban_mask(arg[1],true); if (!kline_mask) { - log(LOG_WARNING,"!WARNING Invalid K:Line (" - + arg[1] + ")"); + log(LOG_WARNING,"!WARNING Invalid K:Line (" + arg[1] + ")"); break; } KLines.push(new KLine(kline_mask,arg[2],"K")); @@ -364,9 +370,9 @@ function read_conf_config(conf) { case "M": if (!arg[3]) break; - servername = arg[1]; - serverdesc = arg[3]; - mline_port = parseInt(arg[4]); + ServerName = arg[1]; + ServerDesc = arg[3]; + Default_Port = parseInt(arg[4]); break; case "N": if (!arg[5]) @@ -394,8 +400,8 @@ function read_conf_config(conf) { ULines.push(arg[1]); break; case "X": - diepass = arg[1]; - restartpass = arg[2]; + Die_Password = arg[1]; + Restart_Password = arg[2]; break; case "Y": if (!arg[5]) diff --git a/exec/load/ircd/core.js b/exec/load/ircd/core.js index e7fc71f9eacb22cd0d4c4b7665be0359cdfda203..f7daa95c3c541a0e7ee79382f7d864dfa1c49e86 100644 --- a/exec/load/ircd/core.js +++ b/exec/load/ircd/core.js @@ -19,8 +19,8 @@ */ -/* 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 i; var counter = 0; for (i in my_array) { counter++; @@ -28,49 +28,67 @@ function true_array_len(my_array) { 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(); +function terminate_everything(str, error) { + var i; + var sendstr; + + sendstr = format("ERROR :%s\r\n", str); + + log(error ? LOG_ERR : LOG_NOTICE, "Terminating: " + str); + + for (i in Unregistered) { + Unregistered[i].socket.send(sendstr); + Unregistered[i].socket.close(); + } + for (i in Local_Servers) { + Local_Servers[i].socket.send(sendstr); + Local_Servers[i].socket.close(); } + for (i in Local_Users) { + Local_Users[i].socket.send(sendstr); + Local_Users[i].socket.close(); + } + + js.do_callbacks = false; exit(error); } function search_server_only(server_name) { + var i; + 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; + + for (i in Servers) { + if (wildmatch(Servers[i].nick,server_name)) + return Servers[i]; + } + if (wildmatch(ServerName,server_name)) + return -1; /* the server passed to us is our own. */ + return 0; /* Failure */ } function searchbyserver(servnick) { + var i; + 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 false; + + i = search_server_only(servnick); + if (i) + return i; + + for (i in Users) { + if (wildmatch(Users[i].nick,servnick)) + return search_server_only(Users[i].servername); } - return 0; // looks like we failed after all that hard work :( + + return false; } -// 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 :) +/* 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) @@ -79,12 +97,20 @@ function parse_username(str) { } 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); + var i, user; + + log(LOG_INFO, format("%s: %s", ntype, nmessage)); + + for (i in Local_Users) { + user = Local_Users[i]; + if (user.mode && ((user.mode&bit)==bit)) { + user.rawout(format(":%s NOTICE %s :*** %s -- %s", + ServerName, + user.nick, + ntype, + nmessage + )); + } } } @@ -94,23 +120,24 @@ function create_ban_mask(str,kline) { tmp_banstr[0] = ""; tmp_banstr[1] = ""; tmp_banstr[2] = ""; + var i; 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]; + for (i in str) { + if (str[i].match(regexp)) { + tmp_banstr[part_counter] += str[i]; bchar_counter++; - } else if ((str[bchar] == "!") && (part_counter == 0) && + } else if ((str[i] == "!") && (part_counter == 0) && !kline) { part_counter = 1; bchar_counter = 0; - } else if ((str[bchar] == "@") && (part_counter == 1) && + } else if ((str[i] == "@") && (part_counter == 1) && !kline) { part_counter = 2; bchar_counter = 0; - } else if ((str[bchar] == "@") && (part_counter == 0)) { + } else if ((str[i] == "@") && (part_counter == 0)) { if (kline) { part_counter = 1; } else { @@ -140,7 +167,7 @@ function create_ban_mask(str,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[0].slice(0,MAX_NICKLEN), tmp_banstr[1].slice(0,10), tmp_banstr[2].slice(0,80) ); @@ -151,122 +178,132 @@ function create_ban_mask(str,kline) { } 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]; + var i; + + for (i in KLines) { + if (KLines[i].hostmask && wildmatch(kl_str,KLines[i].hostmask)) { + return KLines[i]; } } return 0; } -function iszlined(zl_ip) { - for(the_zl in ZLines) { - if ( ZLines[the_zl].ipmask - && wildmatch(zl_ip,ZLines[the_zl].ipmask) - ) { +function IP_Banned(ip) { + var i; + + for (i in ZLines) { + if (ZLines[i].ipmask && wildmatch(ip,ZLines[i].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 Scan_For_Banned_Clients() { + var i, user; + + for (i in Local_Users) { + user = Local_Users[i]; + if ( isklined(user.uprefix + "@" + user.hostname) + || IP_Banned(user.ip) + ) { + theuser.quit("User has been banned"); + } } } 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 = ""; + var i; + + for (i in KLines) { + if (KLines[i].hostmask && wildmatch(kl_hm,KLines[i].hostmask)) { + delete KLines[i]; return 1; } } - return 0; // failure. + return 0; } -function connect_to_server(this_cline,the_port) { - var connect_sock; - var new_id; +/* this = cline */ +function connect_to_server(port) { + var sock = new Socket(); - 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); - } + sock.array_buffer = false; /* JS78, we want strings */ + sock.cline = this; + sock.outbound = true; - var sendts = true; /* Assume Bahamut */ + if (!port && this.port) + port = this.port; + else if (!port) + port = Default_Port; - for (nl in NLines) { - var mynl = NLines[nl]; - } + umode_notice(USERMODE_ROUTING,"Routing",format( + "Auto-connecting to %s (%s) on port %d", + this.servername, + this.host, + port + )); - 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(); + sock.connect(this.host, port, handle_outbound_server_connect); + + if (this.next_connect) + delete this.next_connect; +} + +/* socket object with .cline attached */ +function handle_outbound_server_connect() { + var id; + + if (this.is_connected) { + umode_notice(USERMODE_ROUTING,"Routing","Connected! Sending info..."); + this.send(format("PASS %s :TS\r\n", this.cline.password)); + this.send("CAPAB " + SERVER_CAPAB + "\r\n"); + this.send("SERVER " + ServerName + " 1 :" + ServerDesc +"\r\n"); + id = Generate_ID(); + Unregistered[id] = new Unregistered_Client(id,this); + Unregistered[id].server = true; /* Avoid recvq limitation */ + Unregistered[id].ircclass = this.cline.ircclass; + YLines[this.cline.ircclass].active++; + log(LOG_DEBUG,format("Class %s up to %d active out of %d", + this.cline.ircclass, + YLines[this.cline.ircclass].active, + YLines[this.cline.ircclass].maxlinks + )); + this.callback_id = this.on("read", Socket_Recv); + } else { + umode_notice(USERMODE_ROUTING,"Routing",format( + "Failed to connect to %s (%s). Trying again in %d seconds.", + this.cline.servername, + this.cline.host, + YLines[this.cline.ircclass].connfreq + )); + this.close(); + this.cline.next_connect = js.setTimeout( + connect_to_server, + YLines[this.cline.ircclass].connfreq * 1000, + this.cline + ); } - this_cline.lastconnect = time(); + this.cline.lastconnect = system.timer; } -function wallopers(str) { - for(thisoper in Local_Users) { - var oper=Local_Users[thisoper]; - if (oper.mode&USERMODE_OPER) - oper.rawout(str); +function Write_All_Opers(str) { + var i; + + for (i in Local_Users) { + if (Local_Users[i].mode&USERMODE_OPER) + Local_Users[i].rawout(str); } } function push_nickbuf(oldnick,newnick) { NickHistory.unshift(new NickBuf(oldnick,newnick)); - if(NickHistory.length >= NickHistorySize) + if(NickHistory.length >= MAX_NICKHISTORY) NickHistory.pop(); } function search_nickbuf(bufnick) { + var nb; for (nb=NickHistory.length-1;nb>-1;nb--) { if (bufnick.toUpperCase() == NickHistory[nb].oldnick.toUpperCase()) { if (!Users[NickHistory[nb].newnick.toUpperCase()]) @@ -285,11 +322,10 @@ function create_new_socket(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)); + log(LOG_NOTICE, format("IRC server socket bound to TCP port %d", port)); } catch(e) { - log(LOG_ERR,"!Error " + e + " creating listening socket on port " - + port); + log(LOG_ERR,"!Error " + e + " creating listening socket on port " + port); return 0; } } else { @@ -299,41 +335,51 @@ function create_new_socket(port) { + port); return 0; } - log(format("%04u ",newsock.descriptor) - + "IRC server socket bound to TCP port " + port); + log(LOG_NOTICE, format( + "%04u IRC server socket bound to TCP port %d", + newsock.descriptor, + port + )); if(!newsock.listen(5 /* backlog */)) { - log(LOG_ERR,"!Error " + newsock.error - + " setting up socket for listening"); + log(LOG_ERR,"!Error " + newsock.error + " setting up socket for listening"); return 0; } } return newsock; } -function check_qwk_passwd(qwkid,password) { +function Check_QWK_Password(qwkid,password) { + var u; + if (!password || !qwkid) - return 0; + return false; 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) + u = new User(system.matchuser(qwkid)); + if (!u) + return false; + if ( (password.toUpperCase() == u.security.password.toUpperCase()) + && (u.security.restrictions&UFLAG_Q) ) { - return 1; + return true; } - return 0; + return false; } + function IRCClient_netsplit(ns_reason) { + var i; + 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 (i in Users) { + if (Users[i] && (Users[i].servername == this.nick)) { + Users[i].quit(ns_reason,true,true); } } - for (sqserver in Servers) { - if (Servers[sqserver] && (Servers[sqserver].linkparent == this.nick)) { - Servers[sqserver].quit(ns_reason,true,true); + + for (i in Servers) { + if (Servers[i] && (Servers[i].linkparent == this.nick)) { + Servers[i].quit(ns_reason,true,true); } } } @@ -351,14 +397,21 @@ function IRCClient_RMChan(rmchan_obj) { delete Channels[rmchan_obj.nam.toUpperCase()]; } -//////////////////// Output Helper Functions //////////////////// +function Generate_ID() { + var i; + for (i = 0; Assigned_IDs[i]; i++) { + /* do nothing */ + } + Assigned_IDs[i] = true; + return i; +} + function rawout(str) { var sendconn; var str_end; var str_beg; - if (debug) - log(format("[RAW->%s]: %s",this.nick,str)); + log(LOG_DEBUG, format("[RAW->%s]: %s",this.nick,str)); if (this.local) { sendconn = this; @@ -374,6 +427,9 @@ function rawout(str) { return 0; } + if (sendconn.sendq.empty) { + js.setImmediate(Process_Sendq, sendconn.sendq); + } sendconn.sendq.add(str); } @@ -381,8 +437,7 @@ function originatorout(str,origin) { var send_data; var sendconn; - if (debug) - log(format("[%s->%s]: %s",origin.nick,this.nick,str)); + log(LOG_DEBUG,format("[%s->%s]: %s",origin.nick,this.nick,str)); sendconn = this; if(this.local && !this.server) { @@ -400,6 +455,9 @@ function originatorout(str,origin) { return 0; } + if (sendconn.sendq.empty) { + js.setImmediate(Process_Sendq, sendconn.sendq); + } sendconn.sendq.add(send_data); } @@ -407,8 +465,7 @@ function ircout(str) { var send_data; var sendconn; - if (debug) - log(format("[%s->%s]: %s",servername,this.nick,str)); + log(LOG_DEBUG,format("[%s->%s]: %s",ServerName,this.nick,str)); if(this.local) { sendconn = this; @@ -419,10 +476,23 @@ function ircout(str) { return 0; } - send_data = ":" + servername + " " + str; + send_data = ":" + ServerName + " " + str; + + if (sendconn.sendq.empty) { + js.setImmediate(Process_Sendq, sendconn.sendq); + } sendconn.sendq.add(send_data); } +/* this = socket object passed from sock.on() */ +function Socket_Recv() { + if (!this.is_connected) { + this.irc.quit("Connection reset by peer."); + return 1; + } + this.irc.recvq.recv(this); +} + function Queue_Recv(sock) { var pos; var cmd; @@ -438,6 +508,70 @@ function Queue_Recv(sock) { cmd = cmd.substr(0, cmd.length - 1); this.add(cmd); } + if (sock.irc.server) { + if (sock.irc.recvq.queue.length > 0) + js.setImmediate(Unthrottled_Recvq, sock.irc.recvq); + return 1; + } + if (sock.irc.recvq.size > MAX_CLIENT_RECVQ) { + sock.irc.quit(format("Excess Flood (%d bytes)",sock.irc.recvq.size)); + return 1; + } + if (sock.irc.recvq.queue.length > 0) { + js.setImmediate(Process_Recvq, sock.irc.recvq); + } + } + return 0; +} + +function Unthrottled_Recvq() { + if (this.queue.length == 0) + return 1; + this.irc.work(this.queue.shift()); + if (this.queue.length > 0) { + js.setImmediate(Unthrottled_Recvq, this); + } + return 0; +} + +/* this = IRC_Queue object */ +function Recvq_Unthrottle() { + this.throttled = false; + this.num_sent = 3; + this.time_marker = system.timer; + js.setImmediate(Process_Recvq, this); +} + +/* this = IRC_Queue object */ +function Process_Recvq() { + if (this.queue.length == 0) + return 1; + var timediff = system.timer - this.time_marker; + if (timediff >= 2) { + this.num_sent = 0; + this.time_marker = system.timer; + } + if (this.throttled) { + if (timediff < 2) + return 1; + } else if ((timediff <= 2) && (this.num_sent >= 4)) { + this.throttled = true; + this.time_marker = system.timer; + js.setTimeout(Recvq_Unthrottle, 2000, this); + return 1; + } + this.irc.work(this.queue.shift()); + this.num_sent++; + if (this.queue.length > 0) { + js.setImmediate(Process_Recvq, this); + } + return 0; +} + +/* this = IRC_Queue object */ +function Process_Sendq() { + if (!this.empty) { + this.send(this.socket); } } @@ -450,8 +584,12 @@ function Queue_Send(sock) { } if (this._send_bytes.length) { sent = sock.send(this._send_bytes); - if (sent > 0) + if (sent > 0) { this._send_bytes = this._send_bytes.substr(sent); + if (this._send_bytes.length > 0) { + js.setImmediate(Process_Sendq, this); + } + } } } @@ -475,7 +613,6 @@ 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); } @@ -509,8 +646,8 @@ function IRCClient_numeric208(type,clientname) { this.numeric(208, type + " 0 " + clientname); } -function IRCClient_numeric261(file) { - this.numeric(261, "File " + file + " " + debug); +function IRCClient_numeric261(file) { /* not currently used */ + this.numeric(261, "File " + file + " debug"); } function IRCClient_numeric321() { @@ -556,21 +693,15 @@ function IRCClient_numeric333(chan) { } function IRCClient_numeric351() { - this.numeric(351, VERSION + " " + servername + " :" + VERSION_STR); + this.numeric(351, VERSION + " " + ServerName + " :" + VERSION_STR); } -function IRCClient_numeric352(user,show_ips_only,chan) { +function IRCClient_numeric352(user,who_options,chan) { var who_mode=""; - var disp; - var disphost; + var gecos; if (!user) - return 0; - - if (!chan) - disp = "*"; - else - disp = chan.nam; + throw "WHO Numeric 352 was called without a user."; if (user.away) who_mode += "G"; @@ -585,24 +716,36 @@ function IRCClient_numeric352(user,show_ips_only,chan) { 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); + gecos = user.realname; + if (who_options&WHO_SHOW_PARENT) + gecos = user.parent; + if (who_options&WHO_SHOW_ID) + gecos = user.id; + if (who_options&WHO_SHOW_CALLBACKID) + gecos = user.local ? user.socket.callback_id : "Not a local user."; + + this.numeric(352, format( + "%s %s %s %s %s %s :%s %s", + chan ? chan.nam : "*", + user.uprefix, + (who_options&WHO_SHOW_IPS_ONLY) ? user.ip : user.hostname, + user.servername, + user.nick, + who_mode, + user.hops, + gecos + )); 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); + /* = public @ secret * everything else */ + this.numeric(353, format( + "%s %s :%s", + (chan.mode&CHANMODE_SECRET) ? "@" : "=", + chan.nam, + str + )); } function IRCClient_numeric382(str) { @@ -610,7 +753,7 @@ function IRCClient_numeric382(str) { } function IRCClient_numeric391() { - this.numeric(391, servername + " :" + this.numeric(391, ServerName + " :" + strftime("%A %B %d %Y -- %H:%M %z",time())); } @@ -676,14 +819,12 @@ 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) + true_array_len(Servers) + 1 /* count ourselves */ )); this.numeric(252, num_opers() + " :IRC operators online."); var unknown_connections = true_array_len(Unregistered); @@ -692,53 +833,72 @@ function IRCClient_lusers() { 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)); + this.numeric(250, ":Highest connection count: " + HCC_Total + " (" + + HCC_Users + " clients.)"); + this.server_notice(format( + "%d clients have connected since %s", + HCC_Counter, + SERVER_UPTIME_STRF + )); } function num_noninvis_users() { + var i; var counter = 0; - for(myuser in Users) { - if (!(Users[myuser].mode&USERMODE_INVISIBLE)) + + for (i in Users) { + if (!(Users[i].mode&USERMODE_INVISIBLE)) counter++; } return counter; } function num_invis_users() { + var i; var counter = 0; - for(myuser in Users) { - if (Users[myuser].mode&USERMODE_INVISIBLE) + + for (i in Users) { + if (Users[i].mode&USERMODE_INVISIBLE) counter++; } return counter; } function num_opers() { + var i; var counter = 0; - for(myuser in Users) { - if (Users[myuser].mode&USERMODE_OPER) + + for (i in Users) { + if (Users[i].mode&USERMODE_OPER) counter++; } return counter; } function IRCClient_motd() { + var motd_line; var motd_file = new File(system.text_dir + "ircmotd.txt"); if (motd_file.exists==false) { - this.numeric(422, ":MOTD file missing: " + motd_file.name); + this.numeric(422, ":MOTD file missing."); + umode_notice(USERMODE_OPER,"Notice",format( + "MOTD file missing: %s",motd_file.name + )); } else if (motd_file.open("r")==false) { - this.numeric(424, ":MOTD error " + errno + " opening: " - + motd_file.name); + this.numeric(424, ":Error opening MOTD file."); + umode_notice(USERMODE_OPER,"Notice",format( + "MOTD error %d opening: %s", errno, motd_file.name + )); } else { - this.numeric(375, ":- " + servername + " Message of the Day -"); + motd_file.codepage = 0; + 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) + if (motd_line != null) { + if (typeof codepage_encode == 'function') + motd_line = codepage_encode(false, 0, motd_line); this.numeric(372, ":- " + motd_line); + } } motd_file.close(); } @@ -749,8 +909,9 @@ function IRCClient_names(chan) { var Channel_user; var numnicks=0; var tmp=""; - for(thisChannel_user in chan.users) { - Channel_user=chan.users[thisChannel_user]; + var i; + for (i in chan.users) { + Channel_user=chan.users[i]; if (!(Channel_user.mode&USERMODE_INVISIBLE) || (this.channels[chan.nam.toUpperCase()]) ) { if (numnicks) @@ -773,9 +934,11 @@ function IRCClient_names(chan) { this.numeric353(chan, tmp); } -// Traverse each channel the user is on and see if target is on any of the -// same channels. +/* Traverse each channel the user is on and see if target is on any of the + same channels. */ function IRCClient_onchanwith(target) { + var c, i; + for (c in this.channels) { for (i in target.channels) { if (c == i) @@ -785,52 +948,60 @@ function IRCClient_onchanwith(target) { 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; + var i, j; + var already_bcast = {}; + + for (i in this.channels) { + for (j in this.channels[i].users) { + if ( !already_bcast[this.channels[i].users[j].nick] + && (this.channels[i].users[j].id != this.id) + && this.channels[i].users[j].local + ) { + this.channels[i].users[j].originatorout(str,this); + already_bcast[this.channels[i].users[j].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); + var i, user; + + for (i in chan.users) { + var user = chan.users[i]; + if ( user + && (user.id != this.id || (bounce)) + && chan.modelist[list_bit][user.id] + ) { + user.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); + var i, user; + + for (i in chan.users) { + var user = chan.users[i]; + if ((user.id != this.id || bounce) && user.local) { + user.originatorout(str,this); } } } function IRCClient_bcast_to_channel_servers(chan, str) { + var i, user; + 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()] + for (i in chan.users) { + user = chan.users[i]; + if ( !user.local + && (this.parent != user.parent) + && !sent_to_servers[user.parent.toLowerCase()] ) { - aUser.originatorout(str,this); - sent_to_servers[aUser.parent.toLowerCase()] = true; + user.originatorout(str,this); + sent_to_servers[user.parent.toLowerCase()] = true; } } } @@ -839,45 +1010,57 @@ function IRCClient_check_nickname(newnick,squelch) { var qline_nick; var checknick; var regexp; + var i; - newnick = newnick.slice(0,max_nicklen); - // If you're trying to NICK to yourself, drop silently. + 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. + /* 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. + /* 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)) { + /* Third, match against Q:Lines */ + for (i in QLines) { + if(wildmatch(newnick, QLines[i].nick)) { if (!squelch) - this.numeric(432, newnick + " :" + QLines[ql].reason); + this.numeric(432, newnick + " :" + QLines[i].reason); return 0; } } - return 1; // if we made it this far, we're good! + return 1; /* Success */ } function IRCClient_do_whois(wi) { + var i; + var userchans = ""; + var chan; + if (!wi) - return 0; - this.numeric(311, wi.nick + " " + wi.uprefix + " " + wi.hostname + " * :" - + wi.realname); - var userchans=""; + throw "do_whois() called without an argument."; + if (!wi.nick) + throw "do_whois() called without a valid nick."; + + this.numeric(311, format( + "%s %s %s * :%s", + wi.nick, wi.uprefix, wi.hostname, wi.realname + )); + 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 ( this.channels[chan.nam.toUpperCase()] + || !(chan.mode&CHANMODE_SECRET || chan.mode&CHANMODE_PRIVATE) + || this.mode&USERMODE_OPER + ) { if (userchans) userchans += " "; if (chan.modelist[CHANMODE_OP][wi.id]) @@ -887,22 +1070,49 @@ function IRCClient_do_whois(wi) { 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 (userchans) { + this.numeric(319, format( + "%s :%s", + wi.nick, userchans + )); + } + if (wi.local) { + this.numeric(312, format( + "%s %s :%s", + wi.nick, ServerName, ServerDesc + )); + } else { + i = searchbyserver(wi.servername); + this.numeric(312, format( + "%s %s :%s", + wi.nick, i.nick, i.info + )); + } + if (wi.uline) { + this.numeric(313, format( + "%s :is a Network Service.", + wi.nick + )); + } else if (wi.mode&USERMODE_OPER) { + this.numeric(313, format( + "%s :is an IRC Operator.", + wi.nick + )); + } + if (wi.away) { + this.numeric(301, format( + "%s :%s", + wi.nick, wi.away + )); + } + if (wi.local) { + this.numeric(317, format( + "%s %s %s :seconds idle, signon time", + wi.nick, + time() - wi.talkidle, + wi.connecttime + )); } - 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) { @@ -912,7 +1122,6 @@ function IRCClient_services_msg(svcnick,send_str) { this.numeric412(); return 0; } - // First, make sure the nick exists. var usr = Users[svcnick.toUpperCase()]; if (!usr) { this.numeric440(svcnick); @@ -927,6 +1136,8 @@ function IRCClient_services_msg(svcnick,send_str) { } function IRCClient_global(target,type_str,send_str) { + var i; + if (!target.match("[.]")) { this.numeric(413,target + " :No top-level domain specified."); return 0; @@ -938,14 +1149,13 @@ function IRCClient_global(target,type_str,send_str) { } var global_mask = target.slice(1); var global_str = type_str + " " + target + " :" + send_str; - for(globClient in Local_Users) { - var Client = Local_Users[globClient]; + for (i in Local_Users) { if (target[0] == "#") - var global_match = Client.hostname; - else // assume $ - var global_match = Client.servername; + var global_match = Local_Users[i].hostname; + else /* assume $ */ + var global_match = Local_Users[i].servername; if (wildmatch(global_match,global_mask)) - Client.originatorout(global_str,this); + Local_Users[i].originatorout(global_str,this); } global_str = ":" + this.nick + " " + global_str; if(this.local && this.parent) /* Incoming from a local server */ @@ -959,12 +1169,24 @@ 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); + umode_notice(globops_bits,"Global",format( + "from %s: %s", + this.nick, + str + )); + if (this.parent) { + Servers[this.parent.toLowerCase()].bcast_to_servers_raw(format( + ":%s GLOBOPS :%s", + this.nick, + str + )); + } else { + server_bcast_to_servers(format( + ":%s GLOBOPS :%s", + this.nick, + str + )); + } } function IRCClient_do_msg(target,type_str,send_str) { @@ -987,7 +1209,7 @@ function IRCClient_do_msg(target,type_str,send_str) { if ((target[0] == "#") || (target[0] == "&")) { var chan = Channels[target.toUpperCase()]; if (!chan) { - // check to see if it's a #*hostmask* oper message + /* check to see if it's a #*hostmask* oper message */ if ( (target[0] == "#") && (this.mode&USERMODE_OPER) && ( (this.flags&OLINE_CAN_LGNOTICE) || !this.local ) ) { @@ -1095,12 +1317,12 @@ function IRCClient_do_admin() { this.servername )); if (Admin1 && Admin2 && Admin3) { - this.numeric(256, ":Administrative info about " + servername); + this.numeric(256, ":Administrative info about " + ServerName); this.numeric(257, ":" + Admin1); this.numeric(258, ":" + Admin2); this.numeric(259, ":" + Admin3); } else { - this.numeric(423, servername + this.numeric(423, ServerName + " :No administrative information available."); } } @@ -1113,87 +1335,97 @@ function IRCClient_do_info() { this.hostname, this.servername )); - this.numeric(371, ":--=-=-=-=-=-=-=-=-=*[ The Synchronet IRCd v1.9a ]*=-=-=-=-=-=-=-=-=--"); + this.numeric(371, ":--=-=-=-=-=-=-=-=-=-=*[ The Synchronet IRCd v1.9b ]*=-=-=-=-=-=-=-=-=-=--"); 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, ":--=-=-=-=-=-=-=-=-=-( 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, ":Thanks to the DALnet Bahamut team for their help over the years."); + this.numeric(371, ":Greets to: Arrak, ElvishMerchant, Foobar, Grimp, Kufat, Psyko,"); + this.numeric(371, ": Samael, TheStoryTillNow, Torke, and all the #square oldbies."); + this.numeric(371, ":In memory of Palidor (Alex Kimbel) March 17, 1984 - December 12, 2012."); + 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, ":- - - - - - - - - - - - - - - - - - - - - - - - -"); + 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.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(371, ":--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--"); this.numeric(374, ":End of /INFO list."); } function IRCClient_do_stats(statschar) { + var i; + 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); + for (i in CLines) { + this.numeric(213,format( + "C %s * %s %s %s", + CLines[i].host, + CLines[i].servername, + CLines[i].port ? CLines[i].port : "*", + CLines[i].ircclass + )); + if (NLines[i]) { + this.numeric(214,format( + "N %s * %s %s %s", + NLines[i].host, + NLines[i].servername, + NLines[i].flags, + NLines[i].ircclass + )); + } } break; case "H": case "h": - for (hl in HLines) { - this.numeric(244,"H " + HLines[hl].allowedmask + " * " - + HLines[hl].servername); + for (i in HLines) { + this.numeric(244,format( + "H %s * %s", + HLines[i].allowedmask, + HLines[i].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); + for (i in ILines) { + this.numeric(215,format( + "I %s * %s %s %s", + ILines[i].ipmask, + ILines[i].hostmask, + ILines[i].port ? ILines[i].port : "*", + ILines[i].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); + for (i in KLines) { + if (KLines[i].hostmask) { + this.numeric(216, format( + "%s %s * * :%s", + KLines[i].type, + KLines[i].hostmask, + KLines[i].reason + )); } } break; @@ -1205,42 +1437,47 @@ function IRCClient_do_stats(statschar) { break; case "M": case "m": - for (c in Profile) { - var sm = Profile[c]; - this.numeric(212, c + " " + sm.ticks + " " + sm.executions); + for (i in Profile) { + this.numeric(212, format( + "%s %s %s", + Profile[i], + Profile[i].ticks, + Profile[i].executions + )); } break; case "O": case "o": - for (oline in OLines) { - this.numeric(243, "O " + OLines[oline].hostmask + " * " - + OLines[oline].nick); + for (i in OLines) { + this.numeric(243, format( + "O %s * %s", + OLines[i].hostmask, + OLines[i].nick + )); } break; case "U": - for (uline in ULines) { - this.numeric(246, "U " + ULines[uline] + " * * 0 -1"); + for (i in ULines) { + this.numeric(246, format( + "U %s * * 0 -1", + ULines[i] + )); } 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); + this.numeric(242,format(":%s", Uptime_String())); 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); + for (i in YLines) { + this.numeric(218,format( + "Y %s %s %s %s %s", + i, + YLines[i].pingfreq, + YLines[i].connfreq, + YLines[i].maxlinks, + YLines[i].sendq + )); } break; default: @@ -1250,14 +1487,14 @@ function IRCClient_do_stats(statschar) { } function IRCClient_do_users() { - var usersshown; - var u; + var i, u; + var usersshown = false; 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); + for (i in system.node_list) { + if(system.node_list[i].status == NODE_INUSE) { + u=new User(system.node_list[i].useron); this.numeric(393,format(':%-25s %-9s %-30s',u.alias,'Node'+node, u.host_name)); usersshown++; @@ -1271,20 +1508,17 @@ function IRCClient_do_users() { } function IRCClient_do_summon(summon_user) { - var usernum; - var isonline; + var usernum, isonline, i; - // Check if exists. usernum = system.matchuser(summon_user); - if(!usernum) + if(!usernum) { this.numeric(444,":No such user."); - else { - // Check if logged in + } else { isonline = 0; - for(node in system.node_list) { - if( (system.node_list[node].status == NODE_INUSE) - && (system.node_list[node].useron == usernum) ) { - isonline = 1; + for (i in system.node_list) { + if( (system.node_list[i].status == NODE_INUSE) + && (system.node_list[i].useron == usernum) ) { + isonline = true; break; } } @@ -1299,6 +1533,8 @@ function IRCClient_do_summon(summon_user) { } function IRCClient_do_links(mask) { + var i; + if (!mask) mask = "*"; umode_notice(USERMODE_STATS_LINKS,"StatsLinks",format( @@ -1309,32 +1545,34 @@ function IRCClient_do_links(mask) { 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); + for (i in Servers) { + if (wildmatch(Servers[i].nick,mask)) { + this.numeric(364, format( + "%s %s :%s %s", + Servers[i].nick, + Servers[i].linkparent, + Servers[i].hops, + Servers[i].info + )); } } - if (wildmatch(servername,mask)) - this.numeric(364, servername + " " + servername + " :0 " + serverdesc); + 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. +/* 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); + if (server == -1) { /* we hunted ourselves */ 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. + /* Okay, we've probably got a user. */ var nick = Users[target.toUpperCase()]; if (!nick) { this.numeric402(target); @@ -1353,19 +1591,21 @@ function IRCClient_do_trace(target) { } function IRCClient_trace_all_opers() { - for(thisoper in Local_Users) { - var oper=Local_Users[thisoper]; - if (oper.mode&USERMODE_OPER) - this.numeric204(oper); + var i; + for (i in Local_Users) { + if (Local_Users[i].mode&USERMODE_OPER) + this.numeric204(Local_Users[i]); } } function IRCClient_do_connect(con_server,con_port) { + var i; var con_cline = ""; - for (ccl in CLines) { - if (wildmatch(CLines[ccl].servername,con_server) || - wildmatch(CLines[ccl].host,con_server) ) { - con_cline = CLines[ccl]; + + for (i in CLines) { + if (wildmatch(CLines[i].servername,con_server) || + wildmatch(CLines[i].host,con_server) ) { + con_cline = CLines[i]; break; } } @@ -1376,7 +1616,7 @@ function IRCClient_do_connect(con_server,con_port) { if (!con_port && con_cline.port) con_port = con_cline.port; if (!con_port && !con_cline.port) - con_port = String(default_port); + con_port = String(Default_Port); if (!con_port.match(/^[0-9]+$/)) { this.server_notice("Invalid port: " + con_port); return 0; @@ -1389,13 +1629,18 @@ function IRCClient_do_connect(con_server,con_port) { con_type = "Remote"; server_bcast_to_servers("GNOTICE :Remote" + msg); } - umode_notice(USERMODE_ROUTING,"Routing","from "+servername+": " + + umode_notice(USERMODE_ROUTING,"Routing","from "+ServerName+": " + con_type + msg); - connect_to_server(con_cline,con_port); + con_cline.next_connect = js.setTimeout( + connect_to_server, + YLines[con_cline.ircclass].connfreq * 1000, + con_cline + ); return 1; } function IRCClient_do_basic_who(whomask) { + var i; var eow = "*"; var regexp = "^[0]{1,}$"; @@ -1411,13 +1656,13 @@ function IRCClient_do_basic_who(whomask) { || (this.mode&USERMODE_OPER) ) ) { - for(i in chan.users) { + 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); + var chkwho = this.numeric352(usr,0 /* who_options */,chan); if (!chkwho) { umode_notice(USERMODE_OPER,"Notice",format( "WHO returned 0 for user: %s (A)", @@ -1453,16 +1698,15 @@ function IRCClient_do_basic_who(whomask) { } function IRCClient_do_complex_who(cmd) { + var tmp, chan, i, x; 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' + /* 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(/[*]/) @@ -1477,8 +1721,8 @@ function IRCClient_do_complex_who(cmd) { cmd[2] = tmp; } - for (myflag in cmd[1]) { - switch (cmd[1][myflag]) { + for (i in cmd[1]) { + switch (cmd[1][i]) { case "+": if (!add) add = true; @@ -1524,7 +1768,7 @@ function IRCClient_do_complex_who(cmd) { who.tweak_mode(WHO_CLASS,add); who.Class = parseInt(cmd[arg]); } - case "m": // we never set -m + case "m": /* we never set -m */ arg++; if (cmd[arg]) { who.tweak_mode(WHO_UMODE,true); @@ -1583,12 +1827,24 @@ function IRCClient_do_complex_who(cmd) { case "I": who.tweak_mode(WHO_SHOW_IPS_ONLY,add); break; + case "X": + if (this.mode&USERMODE_OPER) + who.tweak_mode(WHO_SHOW_PARENT,add); + break; + case "Y": + if (this.mode&USERMODE_OPER) + who.tweak_mode(WHO_SHOW_ID,add); + break; + case "Z": + if (this.mode&USERMODE_OPER) + who.tweak_mode(WHO_SHOW_CALLBACKID,add); + break; default: break; } } - // Check to see if the user passed a generic mask to us for processing. + /* Check to see if the user passed a generic mask to us for processing. */ arg++; if (cmd[arg]) whomask = cmd[arg]; @@ -1597,11 +1853,11 @@ function IRCClient_do_complex_who(cmd) { if (whomask.match(regexp)) whomask = "*"; - // allow +c/-c to override. + /* 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. + /* 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; @@ -1623,12 +1879,11 @@ function IRCClient_do_complex_who(cmd) { if (sf_done) break; } - delete tmp_wc; // don't need this anymore. - // Now we traverse everything and apply the criteria the user passed. + /* 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]; + for (i in Users) { + var wc = Users[i]; var flag_M = this.onchanwith(wc); if ( (wc.mode&USERMODE_INVISIBLE) @@ -1688,7 +1943,7 @@ function IRCClient_do_complex_who(cmd) { } else if ((who.del_flags&WHO_IP) && wildmatch(wc.ip,who.IP)) { continue; } - if (who.add_flags&WHO_UMODE) { // no -m + if (who.add_flags&WHO_UMODE) { /* no -m */ var sic = false; var madd = true; for (mm in who.UMode) { @@ -1784,22 +2039,15 @@ function IRCClient_do_complex_who(cmd) { 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 we made it this far, we're good. */ if (chan && Channels[chan.toUpperCase()]) { - var chkwho = this.numeric352(wc,show_ips_only, - Channels[chan.toUpperCase()]); + var chkwho = this.numeric352(wc, who.add_flags, 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); + var chkwho = this.numeric352(wc, who.add_flags); if (!chkwho) { umode_notice(USERMODE_OPER,"Notice", "WHO returned 0 for user: " + wc.nick + " (D)"); @@ -1807,7 +2055,7 @@ function IRCClient_do_complex_who(cmd) { } who_count++; - if (!(this.mode&USERMODE_OPER) && (who_count >= max_who)) + if (!(this.mode&USERMODE_OPER) && (who_count >= MAX_WHO)) break; } @@ -1819,7 +2067,7 @@ function IRCClient_do_complex_who(cmd) { this.numeric(315, eow + " :End of /WHO list. (Complex)"); } -// Object which stores WHO bits and arguments. +/* Object prototype for storing WHO bits and arguments. */ function Who() { this.add_flags = 0; this.del_flags = 0; @@ -1846,9 +2094,9 @@ function Who_tweak_mode(bit,add) { } } -// 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. +/* 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()]) @@ -1899,48 +2147,55 @@ function IRCClient_do_who_usage() { 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."); + if (this.mode&USERMODE_OPER) { + this.numeric(334,":X : *OPER ONLY* Show 'parent' object in realname field."); + this.numeric(334,":Y : *OPER ONLY* Show object 'id' in realname field."); + this.numeric(334,":Z : *OPER ONLY* Show 'callback id' in realname field."); + } this.numeric(315,"? :End of /WHO list. (Usage)"); } function IRCClient_do_basic_list(mask) { + var i; + 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. + /* 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 + } 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]); + for (i in Channels) { + if (Channels[i] && Channels[i].match_list_mask(mask)) + this.numeric322(Channels[i]); } this.numeric(323, ":End of /LIST. (Basic)"); return; } -// So, the user wants to go the hard way... +/* So, the user wants to go the hard way... */ function IRCClient_do_complex_list(cmd) { + var i, l; var add = true; - var arg = 1; + var arg = 0; var list = new List(); var listmask; var listmask_items; this.numeric321(); - for (lc in cmd[1]) { - switch(cmd[1][lc]) { + for (i in cmd[0]) { + switch(cmd[0][i]) { case "+": if (!add) add = true; @@ -1963,7 +2218,7 @@ function IRCClient_do_complex_list(cmd) { list.Created = parseInt(cmd[arg])*60; } break; - case "m": // we never set -m, inverse. + case "m": /* we never set -m, inverse. */ arg++; if (cmd[arg]) { list.tweak_mode(LIST_MODES,true); @@ -2009,51 +2264,51 @@ function IRCClient_do_complex_list(cmd) { } } - // Generic mask atop all this crap? + /* 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]) { + for (i in Channels) { + if ( !(Channels[i].mode&CHANMODE_SECRET) + || (this.mode&USERMODE_OPER) + || this.channels[i] + ) { if ((list.add_flags&LIST_CHANMASK) && - !wildmatch(aChan,list.Mask.toUpperCase())) + !wildmatch(i,list.Mask.toUpperCase())) continue; else if ((list.del_flags&LIST_CHANMASK) && - wildmatch(aChan,list.Mask.toUpperCase())) + wildmatch(i,list.Mask.toUpperCase())) continue; if ((list.add_flags&LIST_CREATED) && - (Channels[aChan].created < (time() - list.Created))) + (Channels[i].created < (time() - list.Created))) continue; else if ((list.del_flags&LIST_CREATED) && - (Channels[aChan].created > (time() - list.Created))) + (Channels[i].created > (time() - list.Created))) continue; if ((list.add_flags&LIST_TOPIC) && - (!wildmatch(Channels[aChan].topic,list.Topic))) + (!wildmatch(Channels[i].topic,list.Topic))) continue; else if ((list.del_flags&LIST_TOPIC) && - (wildmatch(Channels[aChan].topic,list.Topic))) + (wildmatch(Channels[i].topic,list.Topic))) continue; if ((list.add_flags&LIST_PEOPLE) && - (true_array_len(Channels[aChan].users) < list.People) ) + (true_array_len(Channels[i].users) < list.People) ) continue; else if ((list.del_flags&LIST_PEOPLE) && - (true_array_len(Channels[aChan].users) >= list.People) ) + (true_array_len(Channels[i].users) >= list.People) ) continue; if ((list.add_flags&LIST_TOPICAGE) && list.TopicTime && - (Channels[aChan].topictime > (time()-list.TopicTime))) + (Channels[i].topictime > (time()-list.TopicTime))) continue; else if((list.del_flags&LIST_TOPICAGE)&&list.TopicTime&& - (Channels[aChan].topictime < (time()-list.TopicTime))) + (Channels[i].topictime < (time()-list.TopicTime))) continue; - if (list.add_flags&LIST_MODES) { // there's no -m + if (list.add_flags&LIST_MODES) { /* there's no -m */ var sic = false; var madd = true; - var c = Channels[aChan]; + var c = Channels[i]; for (mm in list.Modes) { switch(list.Modes[mm]) { case "+": @@ -2132,10 +2387,10 @@ function IRCClient_do_complex_list(cmd) { if (listmask) listmask_items = listmask.split(","); - var l_match = false; // assume we match nothing. + var l_match = false; /* assume we match nothing. */ if (listmask_items) { for (l in listmask_items) { - if (Channels[aChan].match_list_mask + if (Channels[i].match_list_mask (listmask_items[l])) { l_match = true; break; @@ -2145,18 +2400,17 @@ function IRCClient_do_complex_list(cmd) { continue; } - // We made it. if (list.add_flags&LIST_DISPLAY_CHAN_MODES) - this.numeric322(Channels[aChan],true); + this.numeric322(Channels[i],true); else - this.numeric322(Channels[aChan]); + this.numeric322(Channels[i]); } } this.numeric(323, ":End of /LIST. (Complex)"); } -// Object which stores LIST bits and arguments. +/* Object prototype for storing LIST bits and arguments. */ function List() { this.add_flags = 0; this.del_flags = 0; @@ -2193,19 +2447,19 @@ function IRCClient_do_list_usage() { 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. + /* No "end of" numeric for this. */ } -// does 'this' (channel) match the 'mask' passed to us? Use 'complex' -// Bahamut parsing to determine that. +/* does 'this' (channel) match the 'mask' passed to us? Use 'complex' + Bahamut-style parsing to determine that. */ function Channel_match_list_mask(mask) { - if (mask[0] == ">") { // Chan has more than X people? + 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? + } 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? + } else if (mask[0].toUpperCase() == "C") { /* created X mins ago? */ if ( (mask[1] == ">") && (this.created < (time() - (parseInt(mask.slice(2)) * 60))) ) { @@ -2215,7 +2469,7 @@ function Channel_match_list_mask(mask) { ) { return 0; } - } else if (mask[0].toUpperCase() == "T") { //topics older than X mins? + } else if (mask[0].toUpperCase() == "T") { /* topics older than X mins? */ if ( (mask[1] == ">") && (this.topictime < (time() - (parseInt(mask.slice(2)) * 60)) ) ) { @@ -2225,23 +2479,25 @@ function Channel_match_list_mask(mask) { ) { return 0; } - } else if (mask[0] == "!") { // doesn't match mask X + } 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. + } 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. + return 1; /* if we made it here, we matched something. */ } function IRCClient_get_usermode(bcast_modes) { + var i; var tmp_mode = "+"; - for (ch in USERMODE_CHAR) { - if ( (!bcast_modes || (bcast_modes && USERMODE_BCAST[ch])) - && this.mode&USERMODE_CHAR[ch] + + for (i in USERMODE_CHAR) { + if ( (!bcast_modes || (bcast_modes && USERMODE_BCAST[i])) + && this.mode&USERMODE_CHAR[i] ) { - tmp_mode += ch; + tmp_mode += i; } } return tmp_mode; @@ -2264,13 +2520,15 @@ function UMode() { } function IRCClient_setusermode(modestr) { + var i; + if (!modestr) return 0; var add=true; var unknown_mode=false; var umode = new UMode(); - for (modechar in modestr) { - switch (modestr[modechar]) { + for (i in modestr) { + switch (modestr[i]) { case "+": if (!add) add=true; @@ -2285,7 +2543,7 @@ function IRCClient_setusermode(modestr) { case "k": case "g": umode.tweak_mode(USERMODE_CHAR - [modestr[modechar]],add); + [modestr[i]],add); break; case "b": case "r": @@ -2295,10 +2553,10 @@ function IRCClient_setusermode(modestr) { case "n": if (this.mode&USERMODE_OPER) umode.tweak_mode(USERMODE_CHAR - [modestr[modechar]],add); + [modestr[i]],add); break; case "o": - // Allow +o only by servers or non-local users. + /* Allow +o only by servers or non-local users. */ if (add && this.parent && Servers[this.parent.toLowerCase()].hub) umode.tweak_mode(USERMODE_OPER,true); @@ -2327,21 +2585,21 @@ function IRCClient_setusermode(modestr) { 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]) + for (i in USERMODE_CHAR) { + if ( (umode.add_flags&USERMODE_CHAR[i]) + && !(this.mode&USERMODE_CHAR[i]) ) { - 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]) + addmodes += i; + if (USERMODE_BCAST[i]) + bcast_addmodes += i; + this.mode |= USERMODE_CHAR[i]; + } else if ( (umode.del_flags&USERMODE_CHAR[i]) + && (this.mode&USERMODE_CHAR[i]) ) { - delmodes += mym; - if (USERMODE_BCAST[mym]) - bcast_delmodes += mym; - this.mode &= ~USERMODE_CHAR[mym]; + delmodes += i; + if (USERMODE_BCAST[i]) + bcast_delmodes += i; + this.mode &= ~USERMODE_CHAR[i]; } } if (!addmodes && !delmodes) @@ -2366,58 +2624,196 @@ function IRCClient_setusermode(modestr) { function IRCClient_check_timeout() { if ( !this.pinged - && ((time() - this.idletime) > YLines[this.ircclass].pingfreq) + && ((system.timer - this.idletime) > YLines[this.ircclass].pingfreq) ) { - this.pinged = time(); - this.rawout("PING :" + servername); + this.pinged = system.timer; + this.rawout("PING :" + ServerName); } else if ( this.pinged - && ((time() - this.pinged) > YLines[this.ircclass].pingfreq) + && ((system.timer - this.pinged) > YLines[this.ircclass].pingfreq) ) { - this.quit("Ping Timeout"); + this.quit(format("Ping Timeout (%d seconds)", YLines[this.ircclass].pingfreq)); return 1; } return 0; } -function IRCClient_check_queues() { - var cmd; +function IRCClient_finalize_server_connect(states) { + var i; - 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) + HCC_Counter++; + gnotice(format("Link with %s [unknown@%s] established, states: %s", + this.nick, + this.hostname, + 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)) { + if (!this.socket.outbound) { + for (i in CLines) { + if(wildmatch(this.nick,CLines[i].servername)) { this.rawout(format( "PASS %s :%s", - CLines[cl].password, + CLines[i].password, states )); break; } } - this.rawout("CAPAB " + Server_CAPAB); - this.rawout("SERVER " + servername + " 1 :" + serverdesc); + this.rawout("CAPAB " + SERVER_CAPAB); + this.rawout("SERVER " + ServerName + " 1 :" + ServerDesc); } - this.bcast_to_servers_raw(":" + servername + " SERVER " + this.nick - + " 2 :" + this.info); + this.bcast_to_servers_raw(format( + ":%s SERVER %s 2 :%s", + ServerName, + this.nick, + this.info + )); this.synchronize(); } +function accept_new_socket() { + var unreg_obj; + var id; + var sock; + + sock = this.accept(); + + sock.array_buffer = false; /* JS78, we want strings */ + + if (!sock) { + log(LOG_DEBUG,"!ERROR " + sock.error + " accepting connection"); + return 1; + } + + log(LOG_DEBUG,"Accepting new connection on port " + sock.local_port); + + switch (sock.local_port) { + case 994: + case 6697: + try { + sock.ssl_server = 1; + } catch(e) { + log(LOG_INFO,"!ERROR Couldn't enable SSL on new connection. Closing."); + sock.close(); + return 1; + } + } + + if (!sock.remote_ip_address) { + log(LOG_DEBUG,"!ERROR Socket has no IP address. Closing."); + sock.close(); + return 1; + } + + if (IP_Banned(sock.remote_ip_address)) { + sock.send(format( + ":%s 465 * :You've been banned from this server.\r\n", + ServerName + )); + sock.close(); + return 1; + } + + if (server.client_add !== undefined) { + server.client_add(sock); + } + + if (server.clients !== undefined) { + log(LOG_DEBUG,format("%d clients", server.clients)); + } + + sock.callback_id = sock.on("read", Socket_Recv); + + id = Generate_ID(); + unreg_obj = new Unregistered_Client(id, sock); + + Unregistered[id] = unreg_obj; + + return 0; +} + +function Startup() { + /* Parse command-line arguments. */ + var cmdline_port; + var cmdline_addr; + var cmdarg; + + for (cmdarg=0;cmdarg<argc;cmdarg++) { + switch(argv[cmdarg].toLowerCase()) { + case "-f": + Config_Filename = argv[++cmdarg]; + break; + case "-p": + cmdline_port = parseInt(argv[++cmdarg]); + break; + case "-a": + cmdline_addr = argv[++cmdarg].split(','); + break; + } + } + + Read_Config_File(); + + /* This tests if we're running from JSexec or not */ + if (server == "JSexec") { + + if (cmdline_port) + Default_Port = cmdline_port; + + server = { + socket: false, + terminated: false, + interface_ip_addr_list: (cmdline_addr || ["0.0.0.0","::"]) + }; + + if (typeof jsexec_revision_detail !== 'undefined') + server.version_detail = jsexec_revision_detail; + else if (typeof jsdoor_revision_detail !== 'undefined') + server.version_detail = jsdoor_revision_detail; + + server.socket = create_new_socket(Default_Port) + + if (!server.socket) + exit(); + } + + server.socket.nonblocking = true; + server.socket.debug = false; + server.socket.callback_id = server.socket.on("read", accept_new_socket); +} + +function Open_PLines() { + var i, sock; + + for (i in PLines) { + sock = create_new_socket(PLines[i]); + if (sock) { + sock.nonblocking = true; + sock.debug = false; + sock.on("read", accept_new_socket); + } + } +} + +function Uptime_String() { + var uptime, days, hours, mins, secs; + + uptime = system.timer - SERVER_UPTIME; + days = Math.floor(uptime / 86400); + if (days) + uptime %= 86400; + hours = Math.floor(uptime/(60*60)); + mins = (Math.floor(uptime/60))%60; + secs = uptime%60; + + return format( + "Server Up %u days, %u:%02u:%02u", + days, hours, mins, secs + ); +} + +/** Global object prototypes **/ + function CLine(host,password,servername,port,ircclass) { this.host = host; this.password = password; @@ -2425,6 +2821,13 @@ function CLine(host,password,servername,port,ircclass) { this.port = port; this.ircclass = ircclass; this.lastconnect = 0; + if ( YLines[ircclass] !== undefined + && Servers[servername.toLowerCase()] === undefined + && YLines[ircclass].connfreq > 0 + && YLines[ircclass].maxlinks > YLines[ircclass].active + ) { + js.setImmediate(connect_to_server, this); + } } function HLine(allowedmask,servername) { @@ -2494,16 +2897,29 @@ function NickBuf(oldnick,newnick) { this.newnick = newnick; } -// used for tracking true SJOIN nicks. -function SJOIN_Nick(nick,isop,isvoice) { - this.nick = nick; - this.isop = isop; - this.isvoice = isvoice; +/* An "SJOIN-style nick" can be in the form of: + Nick, @Nick, @+Nick, +Nick. */ +function SJOIN_Nick(str) { + if (!str) + return 0; + + this.isop = false; + this.isvoice = false; + if (str[0] == "@") { + this.isop = true; + str = str.slice(1); + } + if (str[0] == "+") { + this.isvoice = true; + str = str.slice(1); + } + this.nick = str; } -// Track IRC socket queues -function IRC_Queue() { - this.queue = new Array; +function IRC_Queue(irc) { + this.irc = irc; + this.socket = irc.socket; + this.queue = []; this._recv_bytes = ''; this._send_bytes = ''; this.add = Queue_Add; @@ -2511,6 +2927,36 @@ function IRC_Queue() { this.prepend = Queue_Prepend; this.recv = Queue_Recv; this.send = Queue_Send; + this.limit = 1000000; /* 1MB by default is plenty */ + this.time_marker = 0; /* system.timer */ + this.num_sent = 0; + this.throttled = false; + this.purge = function() { + this.queue = []; + this._recv_bytes = ''; + this._send_bytes = ''; + } + this.__defineGetter__("size", function() { + var ret = 0; + var i; + + for (i in this.queue) { + ret += this.queue[i].length; + } + + ret += this._recv_bytes.length + this._send_bytes.length; + + return ret; + }); + this.__defineGetter__("empty", function() { + if (this.queue.length > 0) + return false; + if (this._recv_bytes.length > 0) + return false; + if (this._send_bytes.length > 0) + return false; + return true; + }); } /* /STATS M, for profiling. */ diff --git a/exec/load/ircd/server.js b/exec/load/ircd/server.js index f37b75e29199ae5da9c558baecac4144a08c68e0..08f0b6a3062acf957f03f8c89cf9c84d393b0ac6 100644 --- a/exec/load/ircd/server.js +++ b/exec/load/ircd/server.js @@ -37,15 +37,13 @@ function IRC_Server() { this.flags = 0; this.hops = 0; this.hostname = ""; - this.id = 0; this.ip = ""; this.ircclass = 0; - this.linkparent=""; + this.linkparent = ""; this.nick = ""; this.parent = 0; this.info = ""; - this.idletime = time(); - this.outgoing = false; + this.idletime = system.timer; // Variables (consts, really) that point to various state information this.socket = ""; ////////// FUNCTIONS @@ -57,8 +55,8 @@ function IRC_Server() { this.ircout=ircout; this.originatorout=originatorout; this.rawout=rawout; - this.sendq = new IRC_Queue(); - this.recvq = new IRC_Queue(); + this.sendq = new IRC_Queue(this); + this.recvq = new IRC_Queue(this); // IRC protocol sending functions this.bcast_to_channel=IRCClient_bcast_to_channel; this.bcast_to_servers=IRCClient_bcast_to_servers; @@ -74,9 +72,9 @@ function IRC_Server() { this.finalize_server_connect=IRCClient_finalize_server_connect; // Global Functions this.check_timeout=IRCClient_check_timeout; - this.check_queues=IRCClient_check_queues; this.set_chanmode=IRCClient_set_chanmode; this.check_nickname=IRCClient_check_nickname; + this.do_msg=IRCClient_do_msg; // Output helper functions (shared) } @@ -84,504 +82,637 @@ function IRC_Server() { function Server_Work(cmdline) { var clockticks = system.timer; - var cmd; - var command; + var cmd, p; + var tmp, i, j, k, n; /* Temp vars used during command processing */ + var origin; - if (debug) - log(format("[%s<-%s]: %s",servername,this.nick,cmdline)); + log(LOG_DEBUG,format("[%s<-%s]: %s",ServerName,this.nick,cmdline)); - cmd = cmdline.split(" "); + cmd = IRC_parse(cmdline); - if (cmdline[0] == ":") { - // Silently ignore NULL originator commands. - if (!cmd[1]) - return 0; - origin = cmd[0].slice(1); - command = cmd[1].toUpperCase(); - cmdline = cmdline.slice(cmdline.indexOf(" ")+1); - // resplit cmd[] - cmd = cmdline.split(" "); - } else { - command = cmd[0].toUpperCase(); - origin = this.nick; + if (!cmd.source.name) { + cmd.source.name = this.nick; + cmd.source.is_server = true; } - var killtype; - var ThisOrigin; - if (origin.match(/[.]/)) { - ThisOrigin = Servers[origin.toLowerCase()]; - killtype = "SQUIT"; - } else { - ThisOrigin = Users[origin.toUpperCase()]; - killtype = "KILL"; - } - if (!ThisOrigin) { - umode_notice(USERMODE_OPER,"Notice","Server " + this.nick + - " trying to pass message for non-existent origin: " + - origin); - this.rawout(killtype + " " + origin + " :" + servername + " (" + origin + "(?) <- " + this.nick + ")"); + if (cmd.source.is_server) + origin = Servers[cmd.source.name.toLowerCase()]; + else + origin = Users[cmd.source.name.toUpperCase()]; + + if (!origin) { + umode_notice(USERMODE_OPER,"Notice",format( + "Server %s trying to pass message for non-existent origin: %s", + this.nick, + cmd.source.name + )); + if (Enforcement) { + this.rawout(format("%s %s :%s (%s(?) <- %s)", + cmd.source.is_server ? "SQUIT" : "KILL", + cmd.source.name, + ServerName, + cmd.source.name, + this.nick + )); + } else { + this.quit(format( + "Server %s trying to pass message for non-existent origin: %s", + this.nick, + cmd.source.name + )); + } return 0; } - this.idletime = time(); + this.idletime = system.timer; + + p = cmd.params; - if (command.match(/^[0-9]+/)) { // passing on a numeric to the client - if (!cmd[1]) - return 0; // uh...? - var destination = Users[cmd[1].toUpperCase()]; - if (!destination) + if (cmd.verb.match(/^[0-9]+/)) { /* Passing on a numeric to a client */ + if (!p[0]) return 0; - destination.rawout(":" + ThisOrigin.nick + " " + cmdline); + var tmp = Users[p[0].toUpperCase()]; + if (!tmp) + return 0; + tmp.rawout(cmdline); /* We don't process numerics directly, pass on */ return 1; } - var legal_command = true; /* For tracking STATS M */ - - switch(command) { - // PING at the top thanks to RFC1459 - case "PING": - if (!cmd[1]) + switch(cmd.verb) { + case "PING": /* RFC1459 says to respond to these first */ + if (!p[0]) break; - if (cmd[2] && (cmd[2][0] == ":")) - cmd[2] = cmd[2].slice(1); - var tmp_server; - if (cmd[2]) - tmp_server = searchbyserver(cmd[2]); - if (tmp_server && (tmp_server != -1) && (tmp_server.id != ThisOrigin.id)) { - tmp_server.rawout(":" + ThisOrigin.nick + " PING " + ThisOrigin.nick + " :" + tmp_server.nick); + if (p[1]) + tmp = searchbyserver(p[1]); + if (tmp && tmp != -1 && tmp.id != origin.id) { + tmp.rawout(format( + ":%s PING %s :%s", + origin.nick, + origin.nick, + tmp.nick + )); break; } - if (cmd[1][0] == ":") - cmd[1] = cmd[1].slice(1); - if (!cmd[2]) - this.ircout("PONG " + servername + " :" + cmd[1]); - else - this.ircout("PONG " + cmd[2] + " :" + cmd[1]); + if (!p[1]) { + this.ircout(format( + "PONG %s :%s", + ServerName, + p[0] + )) + break; + } + this.ircout(format( + "PONG %s :%s", + p[1], + p[0] + )); break; case "ADMIN": - if (!cmd[1] || ThisOrigin.server) + if (!p[0] || origin.server) + break; + if (wildmatch(ServerName,p[0])) { + origin.do_admin(); break; - if (cmd[1][0] == ":") - cmd[1] = cmd[1].slice(1); - if (wildmatch(servername,cmd[1])) { - ThisOrigin.do_admin(); - } else { - var dest_server = searchbyserver(cmd[1]); - if (!dest_server) - break; - dest_server.rawout(":" + ThisOrigin.nick + " ADMIN :" + dest_server.nick); } + tmp = searchbyserver(p[0]); + if (!tmp) + break; + tmp.rawout(format( + ":%s ADMIN :%s", + origin.nick, + tmp.nick + )); break; case "AKILL": - var this_uh; - - if (!cmd[6]) + if (!p[5]) break; - if (!ThisOrigin.uline) { - umode_notice(USERMODE_OPER,"Notice","Non-U:Lined server " - + ThisOrigin.nick + " trying to utilize AKILL."); + + if (!origin.uline) { + umode_notice(USERMODE_OPER,"Notice",format( + "Non-U:Lined server %s trying to utilize AKILL.", + origin.nick + )); break; } - this.bcast_to_servers_raw(":" + ThisOrigin.nick + " " + cmdline); + this.bcast_to_servers_raw(format( + ":%s %s", + origin.nick, + p.join(" ") + )); - this_uh = cmd[2] + "@" + cmd[1]; - if (isklined(this_uh)) + k = p[1] + "@" + p[0]; + if (isklined(k)) break; - KLines.push(new KLine(this_uh,IRC_string(cmdline,6),"A")); - scan_for_klined_clients(); + KLines.push(new KLine(k,p[5],"A")); + Scan_For_Banned_Clients(); break; case "AWAY": - if (ThisOrigin.server) + if (origin.server) break; - var send_away = "AWAY"; - var my_ircstr = IRC_string(cmdline,1); - if (!my_ircstr) { - ThisOrigin.away = ""; - } else { - ThisOrigin.away = my_ircstr; - send_away += " :" + my_ircstr; - } - this.bcast_to_servers_raw(":" + ThisOrigin.nick + " " + send_away); + origin.away = p[0] ? p[0] : ""; + this.bcast_to_servers_raw(format( + ":%s AWAY%s", + origin.nick, + p[0] ? format(" :%s", p[0]) : "" + )); break; case "CHATOPS": - if (!cmd[1]) + if (!p[0]) break; - var my_ircstr = IRC_string(cmdline,1); - umode_notice(USERMODE_CHATOPS,"ChatOps","from " + - ThisOrigin.nick + ": " + my_ircstr); - this.bcast_to_servers_raw(":" + ThisOrigin.nick + " " + - "CHATOPS :" + my_ircstr); + umode_notice(USERMODE_CHATOPS,"ChatOps",format( + "from %s: %s", + origin.nick, + p[0] + )); + this.bcast_to_servers_raw(format( + ":%s CHATOPS :%s", + origin.nick, + p[0] + )); break; case "CONNECT": - if (!cmd[3] || !this.hub || ThisOrigin.server) + if (!p[2] || !this.hub || origin.server) + break; + if (wildmatch(ServerName, p[2])) { + origin.do_connect(p[0],p[1]); break; - if (wildmatch(servername, cmd[3])) { - ThisOrigin.do_connect(cmd[1],cmd[2]); - } else { - var dest_server = searchbyserver(cmd[3]); - if (!dest_server) - break; - dest_server.rawout(":" + ThisOrigin.nick + " CONNECT " + cmd[1] + " " + cmd[2] + " " - + dest_server.nick); } + tmp = searchbyserver(p[2]); + if (!tmp) + break; + tmp.rawout(format( + ":%s CONNECT %s %s %s", + origin.nick, + p[0], + p[1], + tmp.nick + )); break; case "GLOBOPS": - if (!cmd[1]) + if (!p[0]) break; - var my_ircstr = IRC_string(cmdline,1); - ThisOrigin.globops(my_ircstr); + origin.globops(p[0]); break; case "GNOTICE": - if (!cmd[1]) + if (!p[0]) break; - var my_ircstr = IRC_string(cmdline,1); - umode_notice(USERMODE_ROUTING,"Routing","from " + ThisOrigin.nick + ": " + my_ircstr); - this.bcast_to_servers_raw(":" + ThisOrigin.nick + " " + "GNOTICE :" + my_ircstr); + umode_notice(USERMODE_ROUTING,"Routing",format( + "from %s: %s", + origin.nick, + p[0] + )); + this.bcast_to_servers_raw(format( + ":%s GNOTICE :%s", + origin.nick, + p[0] + )); break; case "ERROR": - var my_ircstr = IRC_string(cmdline,1); - gnotice("ERROR from " + this.nick + " [(+)0@" + this.hostname + "] -- " + my_ircstr); - ThisOrigin.quit(my_ircstr); + if (!p[0]) + p[0] = "No error message received."; + gnotice(format( + "ERROR from %s [(+)0@%s] -- %s", + this.nick, + this.hostname, + p[0] + )); + ThisOrigin.quit(p[0]); break; case "INFO": - if (!cmd[1] || ThisOrigin.server) + if (!p[0] || origin.server) + break; + if (wildmatch(ServerName, p[0])) { + origin.do_info(); break; - if (cmd[1][0] == ":") - cmd[1] = cmd[1].slice(1); - if (wildmatch(servername, cmd[1])) { - ThisOrigin.do_info(); - } else { - var dest_server = searchbyserver(cmd[1]); - if (!dest_server) - break; - dest_server.rawout(":" + ThisOrigin.nick + " INFO :" + dest_server.nick); } + tmp = searchbyserver(p[0]); + if (!tmp) + break; + tmp.rawout(format( + ":%s INFO :%s", + origin.nick, + tmp.nick + )); break; case "INVITE": - if (!cmd[2] || ThisOrigin.server) + if (!p[1] || origin.server) break; - if (cmd[2][0] == ":") - cmd[2] = cmd[2].slice(1); - var chan = Channels[cmd[2].toUpperCase()]; - if (!chan) + tmp = Channels[cmd[2].toUpperCase()]; + if (!tmp) break; - if (!chan.modelist[CHANMODE_OP][ThisOrigin.id]) + if (!tmp.modelist[CHANMODE_OP][origin.id]) break; - var nick = Users[cmd[1].toUpperCase()]; - if (!nick) + j = Users[p[0].toUpperCase()]; + if (!j) break; - if (!nick.channels[chan.nam.toUpperCase()]) + if (!j.channels[tmp.nam.toUpperCase()]) break; - nick.originatorout("INVITE " + nick.nick + " :" + chan.nam,ThisOrigin); - nick.invited = chan.nam.toUpperCase(); + j.originatorout(format( + "INVITE %s :%s", + j.nick, + tmp.nam + ),origin); + j.invited = tmp.nam.toUpperCase(); break; case "JOIN": - if (!cmd[1] || ThisOrigin.server) + if (!p[0] || origin.server) break; - if (cmd[1][0] == ":") - cmd[1]=cmd[1].slice(1); - var the_channels = cmd[1].split(","); - for (jchan in the_channels) { - if (the_channels[jchan][0] != "#") + k = p[0].split(","); + for (i in k) { + if (k[i][0] != "#") continue; - ThisOrigin.do_join(the_channels[jchan].slice(0,max_chanlen),""); + origin.do_join(k[i].slice(0,MAX_CHANLEN),""); } break; case "KICK": - var chanid; - var nickid; - var str; - var kick_reason; - - if (!cmd[2]) + if (!p[1]) break; - chanid = Channels[cmd[1].toUpperCase()]; - if (!chanid) + tmp = Channels[p[0].toUpperCase()]; + if (!tmp) break; - nickid = Users[cmd[2].toUpperCase()]; - if (!nickid) - nickid = search_nickbuf(cmd[2]); - if (!nickid) + j = Users[p[1].toUpperCase()]; + if (!j) + j = search_nickbuf(p[1]); + if (!j) break; - if (!nickid.channels[chanid.nam.toUpperCase()]) + if (!j.channels[tmp.nam.toUpperCase()]) break; - if (cmd[3]) - kick_reason = IRC_string(cmdline,3).slice(0,max_kicklen); +/* if (p[2]) + kick_reason = IRC_string(cmdline,3).slice(0,MAX_KICKLEN); else - kick_reason = ThisOrigin.nick; - str = "KICK " + chanid.nam + " " + nickid.nick + " :" + kick_reason; - ThisOrigin.bcast_to_channel(chanid, str, false); - this.bcast_to_servers_raw(":" + ThisOrigin.nick + " " + str); - nickid.rmchan(chanid); + kick_reason = ThisOrigin.nick; */ + origin.bcast_to_channel(tmp, format( + "KICK %s %s :%s", + tmp.nam, + j.nick, + p[2] ? p[2] : j.nick + ),false /* bounceback */); + this.bcast_to_servers_raw(format( + ":%s KICK %s %s :%s", + origin.nick, + tmp.nam, + j.nick, + p[2] ? p[2] : j.nick + )); + j.rmchan(tmp); break; case "KILL": - if (!cmd[1] || !cmd[2]) - break; - if (cmd[1].match(/[.]/)) - break; - if (cmd[2] == ":") - break; - var reason = IRC_string(cmdline,2); - var kills = cmd[1].split(","); - for(kill in kills) { - var target = Users[kills[kill].toUpperCase()]; - if (!target) - target = search_nickbuf(kills[kill]); - if (target && (this.hub || (target.parent == this.nick)) ) { - umode_notice(USERMODE_KILL,"Notice","Received KILL message for " + target.nuh - + ". From " + ThisOrigin.nick + " Path: " + target.nick + "!Synchronet!" - + ThisOrigin.nick + " (" + reason + ")"); - this.bcast_to_servers_raw(":" + ThisOrigin.nick + " KILL " + target.nick + " :" + reason); - target.quit("KILLED by " + ThisOrigin.nick + " (" + reason + ")",true); - } else if (target && !this.hub) { - umode_notice(USERMODE_OPER,"Notice","Non-Hub server " + this.nick + " trying to KILL " - + target.nick); - this.reintroduce_nick(target); + if (!p[1]) + break; + if (p[0].match(/[.]/)) + break; + /* var reason = p[1] */ + k = p[0].split(","); + for (i in k) { + tmp = Users[k[i].toUpperCase()]; + if (!tmp) + tmp = search_nickbuf(k[i]); + if (!tmp) + continue; + if (!Enforcement || this.hub || (tmp.parent == this.nick)) { + umode_notice(USERMODE_KILL,"Notice",format( + "Received KILL message for %s. From %s Path: %s!Synchronet!%s (%s)", + tmp.nuh, + origin.nick, + tmp.nick, + origin.nick, + p[1] + )); + this.bcast_to_servers_raw(format( + ":%s KILL %s :%s", + origin.nick, + tmp.nick, + p[1] + )); + tmp.quit(format( + "KILLED by %s (%s)", + origin.nick, + p[1] + ),true /* suppress_bcast */); + continue; + } + if (!this.hub && Enforcement) { + umode_notice(USERMODE_OPER,"Notice",format( + "Non-Hub server %s trying to KILL %s", + this.nick, + tmp.nick + )); + this.reintroduce_nick(tmp); } } break; case "LINKS": - if (!cmd[1] || !cmd[2] || ThisOrigin.server) + if (!p[1] || origin.server) + break; + if (match_irc_mask(ServerName, p[0])) { + origin.do_links(p[1]); break; - if (match_irc_mask(servername, cmd[1])) { - ThisOrigin.do_links(cmd[2]); - } else { - var dest_server = searchbyserver(cmd[1]); - if (!dest_server) - break; - dest_server.rawout(":" + ThisOrigin.nick + " LINKS " + dest_server.nick + " " + cmd[2]); } + tmp = searchbyserver(p[0]); + if (!tmp) + break; + tmp.rawout(format( + ":%s LINKS %s %s", + origin.nick, + tmp.nick, + p[1] + )); break; case "MODE": - if (!cmd[1] || !cmd[2]) + if (!p[1]) break; - if (cmd[1][0] == "#") { - var chan = Channels[cmd[1].toUpperCase()]; - if (!chan) - break; - var modeline; - var cmdend = cmd.length - 1; - var cmd2_int = parseInt(cmd[2]); - var cmdend_int = parseInt(cmd[cmdend]); - var bounce_modes = false; - /* Detect if this is a TSMODE. If so, handle. */ - if (cmd2_int == cmd[2]) { - if (cmd2_int > chan.created) { - break; - } else if ((cmd2_int < chan.created) && ThisOrigin.server) { - bounce_modes = true; - chan.created = cmd2_int; - } - cmd.shift(); - } - cmd.shift(); - cmd.shift(); - var modeline = cmd.join(" "); - ThisOrigin.set_chanmode(chan,modeline,bounce_modes); - } else { // assume it's for a user - var my_modes = ThisOrigin.setusermode(IRC_string(cmd[2],0)); - if (my_modes) { - this.bcast_to_servers_raw(":" + ThisOrigin.nick - + " MODE " + ThisOrigin.nick + " " + my_modes); + if (p[0][0] != "#") { /* Setting a user mode */ + tmp = origin.setusermode(p[1]); + if (tmp) { + this.bcast_to_servers_raw(format( + ":%s MODE %s %s", + origin.nick, + origin.nick, + tmp + )); } + break; } + /* Setting a channel mode */ + tmp = Channels[p[0].toUpperCase()]; + if (!tmp) + break; + j = false; /* bounce our side? */ + if (parseInt(p[1]) > tmp.created) /* TS violation TODO: bounce their side */ + break; + if (parseInt(p[1]) < tmp.created && origin.server) + j = true; + p.shift(); + origin.set_chanmode(tmp,p.join(" "),j); break; case "MOTD": - if (!cmd[1] || ThisOrigin.server) + if (!p[0] || origin.server) break; - if (cmd[1][0] == ":") - cmd[1] = cmd[1].slice(1); - if (wildmatch(servername, cmd[1])) { + if (wildmatch(ServerName, p[0])) { umode_notice(USERMODE_STATS_LINKS,"StatsLinks",format( "MOTD requested by %s (%s@%s) [%s]", - ThisOrigin.nick, - ThisOrigin.uprefix, - ThisOrigin.hostname, - ThisOrigin.servername + origin.nick, + origin.uprefix, + origin.hostname, + origin.servername )); - ThisOrigin.motd(); - } else { - var dest_server = searchbyserver(cmd[1]); - if (!dest_server) - break; - dest_server.rawout(":" + ThisOrigin.nick + " MOTD :" + dest_server.nick); + origin.motd(); + break; } + tmp = searchbyserver(p[0]); + if (!tmp) + break; + tmp.rawout(format( + ":%s MOTD :%s", + origin.nick, + tmp.nick + )); break; case "NICK": - if (!cmd[2]) - break; - if (ThisOrigin.server && cmd[8]) { - var collide = Users[cmd[1].toUpperCase()]; - if (collide) { - if (collide.parent == this.nick) { - gnotice("Server " + this.nick + " trying to introduce nick " + collide.nick - + " twice?! Ignoring."); + if (!p[1]) + break; + if (origin.server && p[9]) { /* New nick being introduced */ + tmp = Users[p[0].toUpperCase()]; + if (tmp) { /* Nickname collision */ + if (tmp.parent == this.nick) { + gnotice(format( + "Server %s trying to introduce nick %s twice?! Ignoring.", + this.nick, + tmp.nick + )); break; - } 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."); - this.bcast_to_servers("KILL " + collide.nick + " :Nickname Collision."); - collide.quit("Nickname Collision",true); - } else if (!this.hub) { - gnotice("Server " + this.nick + " trying to collide nick " + collide.nick - + " forwards, reversing."); - // Don't collide our side of things from a leaf - this.ircout("KILL " + cmd[1] + " :Inverse Nickname Collision."); - // Reintroduce our nick, because the remote end - // probably killed it already on our behalf. - this.reintroduce_nick(collide); + } + if (tmp.created > parseInt(p[2]) && (this.hub || !Enforcement)) { + /* We're on the wrong side. */ + tmp.numeric(436, tmp.nick + " :Nickname Collision KILL."); + this.bcast_to_servers(format( + "KILL %s :Nickname Collision.", + tmp.nick + )); + tmp.quit("Nickname Collision",true); break; - } else if (this.hub) { - // Other side should nuke on our behalf. + } + if (!this.hub && Enforcement) { + gnotice(format( + "Server %s trying to collide nick %s forwards. Reversing.", + this.nick, + tmp.nick + )); + this.ircout(format( + "KILL %s :Inverse Nickname Collision.", + p[0] + )); + this.reintroduce_nick(tmp); + break; + } + if (this.hub) { + /* Our TS greater than what was passed to us. */ + /* Other side should nuke on our behalf. */ break; } } - var uprefixptr = 5; /* Bahamut */ if (!this.hub) { - if(!this.check_nickname(cmd[1],true)) { - gnotice("Server " + this.nick + " trying to introduce invalid nickname: " + cmd[1] - + ", killed."); - this.ircout("KILL " + cmd[1] + " :Bogus Nickname."); + if (!this.check_nickname(p[0],true)) { + gnotice(format( + "Server %s trying to introduce invalid nickname: %s", + this.nick, + p[0] + )); + if (Enforcement) { + this.ircout(format( + "KILL %s :Bogus Nickname.", + p[0] + )); + } else { + this.quit(format( + "Server %s trying to introduce invalid nickname: %s", + this.nick, cmd[1] + )) + } break; } - cmd[2] = 1; // Force hops to 1. - cmd[3] = time(); // Force TS on nick. - cmd[7] = this.nick // Force server name - } else { // if we're a hub - var test_server = searchbyserver(cmd[uprefixptr+2]); - if (!test_server || (this.nick != - test_server.parent)) { - if (debug && test_server) - log(LOG_DEBUG,"this.nick: " + this.nick + " test_server.parent: " + test_server.parent); - umode_notice(USERMODE_OPER,"Notice","Server " + this.nick - + " trying to introduce nick from server not behind it: " - + cmd[1] + "@" + cmd[uprefixptr+2]); - this.ircout("KILL " + cmd[1] + " :Invalid Origin."); + /* Don't trust what a leaf tells us */ + p[1] = 1; + p[2] = time(); + p[6] = this.nick; + } else { /* Hub (trusted) */ + tmp = searchbyserver(p[6]); + if (!tmp || (this.nick != tmp.parent)) { + umode_notice(USERMODE_OPER,"Notice",format( + "Server %s trying illegal NICK %s@%s (parent: %s)", + origin.nick, + p[0], + p[6], + tmp ? tmp.parent : "none" + )); + if (Enforcement) { + this.ircout(format( + "KILL %s :Invalid Origin.", + p[0] + )); + } else { + this.quit(format( + "Server %s trying illegal NICK %s@%s (parent: %s)", + origin.nick, + p[0], + p[6], + tmp ? tmp.parent : "none" + )); + } break; } } - 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]; - 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 */ - NewNick.realname = IRC_string(cmdline,rnptr); - NewNick.parent = this.nick; - NewNick.ip = int_to_ip(cmd[9]); - NewNick.setusermode(cmd[4]); - for (u in ULines) { - if (ULines[u] == cmd[uprefixptr+2]) { - NewNick.uline = true; + Users[p[0].toUpperCase()] = new IRC_User(Generate_ID()); + j = Users[p[0].toUpperCase()]; + j.local = false; + j.nick = p[0]; + j.hops = parseInt(p[1]); + j.created = parseInt(p[2]); + j.uprefix = p[4]; + j.hostname = p[5]; + j.servername = p[6]; + j.realname = p[9]; + j.parent = this.nick; + j.ip = int_to_ip(p[8]); + j.setusermode(p[3]); + for (i in ULines) { + if (ULines[i] == p[6]) { + j.uline = true; break; } } - var true_hops = parseInt(NewNick.hops)+1; 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 + j.nick, + j.hops + 1, + j.created, + j.get_usermode(true), + j.uprefix, + j.hostname, + j.servername, + ip_to_int(j.ip), + j.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 + j.nick, + j.uprefix, + j.hostname, + j.servername, + j.ip, + j.realname, + j.id )); - } else { // we're a user changing our nick. - var ctuc = cmd[1].toUpperCase(); - if ((Users[ctuc])&&Users[ctuc].nick.toUpperCase() != - ThisOrigin.nick.toUpperCase()) { - gnotice("Server " + this.nick + " trying to collide nick via NICK changeover: " - + ThisOrigin.nick + " -> " + cmd[1]); - server_bcast_to_servers("KILL " + ThisOrigin.nick + " :Bogus nickname changeover."); - ThisOrigin.quit("Bogus nickname changeover."); - this.ircout("KILL " + cmd[1] + " :Bogus nickname changeover."); - this.reintroduce_nick(Users[ctuc]); + break; + } else { /* A user changing their nick */ + tmp = Users[p[0].toUpperCase()]; + if (tmp && tmp.nick.toUpperCase() != origin.nick.toUpperCase()) { + gnotice(format( + "Server %s trying to collide via NICK changeover: %s -> %s", + this.nick, + origin.nick, + p[0] + )); + if (Enforcement) { + server_bcast_to_servers(format( + "KILL %s :Bogus nickname changeover.", + origin.nick + )); + origin.quit("Bogus nickname changeover."); + this.ircout(format( + "KILL %s :Bogus nickname changeover.", + p[0] + )); + this.reintroduce_nick(tmp); + } else { + this.quit(format( + "Server %s trying to collide via NICK changeover: %s -> %s", + this.nick, + origin.nick, + p[0] + )); + } break; } - if (ThisOrigin.check_nickname(cmd[1]) < 1) { - gnotice("Server " + this.nick + " trying to change to bogus nick: " - + ThisOrigin.nick + " -> " + cmd[1]); - this.bcast_to_servers_raw("KILL " + ThisOrigin.nick + " :Bogus nickname switch detected."); - this.ircout("KILL " + cmd[1] + " :Bogus nickname switch detected."); - ThisOrigin.quit("Bogus nickname switch detected.",true); + if (origin.check_nickname(p[0]) < 1) { + gnotice(format( + "Server %s trying to change to bogus nick: %s -> %s", + this.nick, + origin.nick, + p[0] + )); + if (Enforcement) { + this.bcast_to_servers_raw(format( + "KILL %s :Bogus nickname switch detected.", + origin.nick + )); + this.ircout(format( + "KILL %s :Bogus nickname switch detected.", + p[0] + )); + origin.quit("Bogus nickname switch detected.",true); + } else { + this.quit(format( + "Server %s trying to change to bogus nick: %s -> %s", + this.nick, + origin.nick, + p[0] + )); + } break; } if (this.hub) - ThisOrigin.created = parseInt(IRC_string(cmd[2],0)); + origin.created = parseInt(p[1]); else - ThisOrigin.created = time(); - ThisOrigin.bcast_to_uchans_unique("NICK " + cmd[1]); - this.bcast_to_servers_raw(":" + ThisOrigin.nick + " NICK " + cmd[1] + " :" + ThisOrigin.created); - if (ctuc != ThisOrigin.nick.toUpperCase()) { - push_nickbuf(ThisOrigin.nick,cmd[1]); - Users[cmd[1].toUpperCase()] = ThisOrigin; - delete Users[ThisOrigin.nick.toUpperCase()]; + origin.created = time(); + origin.bcast_to_uchans_unique(format( + "NICK %s", + p[0] + )); + this.bcast_to_servers_raw(format( + ":%s NICK %s :%s", + origin.nick, + p[0], + origin.created + )); + if (p[0].toUpperCase() != origin.nick.toUpperCase()) { + push_nickbuf(origin.nick,p[0]); + Users[p[0].toUpperCase()] = origin; + delete Users[origin.nick.toUpperCase()]; } - ThisOrigin.nick = cmd[1]; + origin.nick = p[0]; } break; case "NOTICE": - // FIXME: servers should be able to send notices. - if (!cmd[1] || ThisOrigin.server) - break; - if (this.nick != ThisOrigin.parent) - break; /* Fix for CR bouncing back msgs. */ - var my_ircstr = IRC_string(cmdline,2); - if (!cmd[2] || !my_ircstr) - break; - var targets = cmd[1].split(","); - for (nt in targets) { - if (targets[nt][0] != "&") - ThisOrigin.do_msg(targets[nt],"NOTICE",my_ircstr); + if (!p[1]) + break; + tmp = p[0].split(","); + for (i in tmp) { + if (tmp[i][0] != "&" && tmp[i][0] != "*") + origin.do_msg(tmp[i],"NOTICE",p[1]); } break; case "PART": - if (!cmd[1] || ThisOrigin.server) + if (!p[0] || origin.server) break; - var the_channels = cmd[1].split(","); - for(pchan in the_channels) { - ThisOrigin.do_part(the_channels[pchan]); + tmp = p[0].split(","); + for (i in tmp) { + origin.do_part(tmp[i]); } break; case "PASS": - var result; - - if (!this.hub || !cmd[3]) + break; /* XXX FIXME XXX */ + if (!this.hub || !p[2]) break; - if (cmd[3] != "QWK") + if (p[2] != "QWK") break; - if (cmd[2][0] == ":") - cmd[2] = cmd[2].slice(1); - if (cmd[4]) { // pass the message on to target. - var dest_server = searchbyserver(cmd[4]); - if (!dest_server) { + if (p[3]) { /* pass the message on to target. */ + tmp = searchbyserver(p[3]); + if (!tmp) break; - } else if ( (dest_server == -1) - && (this.flags&NLINE_IS_QWKMASTER) - ) { + if (tmp == -1 && this.flags&NLINE_IS_QWKMASTER) { var qwkid = cmd[2].toLowerCase(); var hunt = qwkid + ".synchro.net"; var my_server = 0; @@ -599,12 +730,12 @@ function Server_Work(cmdline) { } Servers[my_server.nick.toLowerCase()] = new IRC_Server(); var ns = Servers[my_server.id]; - ns.id = my_server.id; + ns.id = my_server.nick.toLowerCase(); ns.nick = my_server.nick; ns.info = my_server.realname; ns.socket = my_server.socket; delete Unregistered[my_server.id]; - ns.finalize_server_connect("QWK",true); + ns.finalize_server_connect("QWK"); break; } else if (dest_server) { if (dest_server == -1) @@ -624,530 +755,484 @@ function Server_Work(cmdline) { } } } - // If we got here, we must be the qwk master. Process. - if (check_qwk_passwd(cmd[2],cmd[1])) + /* If we got here, we must be the qwk master. Process. */ + if (Check_QWK_Password(p[1],p[0])) result = "OK"; else result = "VOID"; - this.rawout(":" + servername + " PASS " + result + " :" + cmd[2] + " QWK " + ThisOrigin.nick); + this.rawout(":" + ServerName + " PASS " + result + " :" + cmd[2] + " QWK " + ThisOrigin.nick); break; case "PONG": - if (cmd[2]) { - if (cmd[2][0] == ":") - cmd[2] = cmd[2].slice(1); - var my_server = searchbyserver(cmd[2]); - if (!my_server) { + if (p[1]) { + tmp = searchbyserver(p[1]); + if (!tmp) break; - } else if (my_server == -1) { - var my_nick = Users[cmd[2].toUpperCase()]; - if (my_nick) - my_nick.rawout(":" + ThisOrigin.nick + " PONG " + cmd[1] + " :" + my_nick.nick); - else - this.pinged = false; - break; - } else if (my_server) { - my_server.rawout(":" + ThisOrigin.nick + " PONG " + cmd[1] + " :" + cmd[2]); + if (tmp == -1) { + j = Users[p[1].toUpperCase()]; + if (j) { + j.rawout(format( + ":%s PONG %s :%s", + origin.nick, + p[0], + j.nick + )); + } + } else if (tmp) { + tmp.rawout(format( + ":%s PONG %s :%s", + origin.nick, + p[0], + p[1] + )); break; } } this.pinged = false; break; case "PRIVMSG": - if (!cmd[1] || ThisOrigin.server) - break; - if (this.nick != ThisOrigin.parent) - break; /* Fix for CR bouncing back msgs. */ - var my_ircstr = IRC_string(cmdline,2); - if (!cmd[2] || !my_ircstr) + if (!p[1] || origin.server) break; - var targets = cmd[1].split(","); - for (pm in targets) { - if (targets[pm][0] != "&") - ThisOrigin.do_msg(targets[pm],"PRIVMSG",my_ircstr); + tmp = p[0].split(","); + for (i in tmp) { + if (tmp[i][0] != "&") + origin.do_msg(tmp[i],"PRIVMSG",p[1]); } break; case "QUIT": - ThisOrigin.quit(IRC_string(cmdline,1)); + if (!p[0]) + p[0] = origin.nick; + origin.quit(p[0]); break; case "SERVER": - if (!cmd[3]) + if (!p[2]) break; - // FIXME: when on Earth does this happen? :P? - var hops = parseInt(cmd[2]); - if ((hops == 1) && !this.info) { - umode_notice(USERMODE_OPER,"Notice", "Server " + cmd[1] + " updating info after handshake???"); - this.nick = cmd[1]; + if (p[1] == 1 && !this.info) { + umode_notice(USERMODE_OPER,"Notice",format( + "Server %s updating info after handshake???", + p[0] + )); + this.nick = p[0]; this.hops = 1; - this.info = IRC_string(cmdline,3); - this.linkparent = servername; + this.info = p[2]; + this.linkparent = ServerName; this.parent = this.nick; - var newsrv = this; - } else if (hops > 1) { + } else if (p[1] > 1) { if (this.hub) { - if (searchbyserver(cmd[1])) { - this.quit("Server " + cmd[1] + " already exists."); - return 0; + if (searchbyserver(p[0])) { + umode_notice(USERMODE_OPER,"Notice",format( + "Server %s trying to introduce %s but it already exists.", + this.nick, + p[0] + )); + if (Enforcement) { + this.rawout(format( + ":%s SQUIT %s :Server already exists.", + ServerName, + p[0] + )); + } else { + this.quit(format( + "Server %s already exists.", + p[0] + )); + } + break; } - var lcserver = cmd[1].toLowerCase(); - var new_id = "id" + next_client_id; - next_client_id++; - Servers[lcserver] = new IRC_Server(); - var newsrv = Servers[lcserver]; - newsrv.hops = cmd[2]; - newsrv.nick = cmd[1]; - newsrv.info = IRC_string(cmdline,3); - newsrv.parent = this.nick; - newsrv.linkparent = ThisOrigin.nick; - newsrv.local = false; - for (u in ULines) { - if (ULines[u] == cmd[1]) { - newsrv.uline = true; + Servers[p[0].toLowerCase()] = new IRC_Server(); + tmp = Servers[p[0].toLowerCase()]; + tmp.id = p[0].toLowerCase(); + tmp.hops = parseInt(p[1]); + tmp.nick = p[0]; + tmp.info = p[2]; + tmp.parent = this.nick; + tmp.linkparent = origin.nick; + tmp.local = false; + for (i in ULines) { + if (ULines[i] == p[0]) { + tmp.uline = true; break; } } + this.bcast_to_servers_raw(format( + ":%s SERVER %s %d :%s", + tmp.linkparent, + tmp.nick, + tmp.hops + 1, + tmp.info + )); } else { - umode_notice(USERMODE_ROUTING,"Routing","from " + servername - + ": Non-Hub link " + this.nick + " introduced " + cmd[1] + "(*)."); - this.quit("Too many servers. You have no H:Line to introduce " + cmd[1] + ".",true); - return 0; + umode_notice(USERMODE_ROUTING,"Routing",format( + "from %s: Non-Hub link %s introduced %s(*).", + ServerName, + this.nick, + p[0] + )); + this.quit(format( + "Too many servers. You have no H:Line to introduce %s.", + p[0] + ),true /* suppress_bcast */); + break; } } else { - umode_notice(USERMODE_OPER,"Notice","Refusing to comply with supposedly bogus SERVER command from " - + this.nick + ": " + cmdline); + umode_notice(USERMODE_OPER,"Notice",format( + "Refusing to comply with supposedly bogus SERVER command from %s: %s", + this.nick, + p.join(" ") + )); break; } - this.bcast_to_servers_raw(":" + newsrv.linkparent + " SERVER " + newsrv.nick + " " - + (parseInt(newsrv.hops)+1) + " :" + newsrv.info); break; case "SJOIN": - if (!cmd[2]) + if (!p[1] || p[1][0] != "#") break; - if (cmd[2][0] != "#") - break; - - var chan_members; - var cm_array; - var mode_args; - if (cmd[3]) { - var incoming_modes = new Object; - var tmp_modeargs = 3; - - /* Parse the modes and break them down */ - for (tmpmc in cmd[3]) { - var my_modechar = cmd[3][tmpmc]; - if (my_modechar == "+") - continue; - if ( (my_modechar == "k") - || (my_modechar == "l") - ) { - tmp_modeargs++; - incoming_modes[my_modechar] = cmd[tmp_modeargs]; - } else { - incoming_modes[my_modechar] = true; - } - } - - /* Reconstruct our modes into a string now */ - var mode_args_chars = "+"; - var mode_args_args = ""; - for (my_mc in incoming_modes) { - mode_args_chars += my_mc; - if ((my_mc == "k") || (my_mc == "l")) { - mode_args_args += " " + incoming_modes[my_mc]; - } - } - mode_args = mode_args_chars + mode_args_args; + tmp = Channels[p[1].toUpperCase()]; + if (!tmp) { + Channels[p[1].toUpperCase()] = new Channel(p[1].toUpperCase()); + tmp = Channels[p[1].toUpperCase()]; + tmp.nam = p[1]; + tmp.created = parseInt(p[0]); + } - /* The following corrects a bug in Bahamut.. */ - if ((cmd[4] == "") && cmd[5]) - tmp_modeargs++; + if (p[2]) { + this.set_chanmode( + tmp, /* Channel object */ + p.splice(2,p.length-4).join(" "), /* Channel mode */ + (tmp.created == parseInt(p[0])) ? false : true /* TS */ + ); - tmp_modeargs++; /* Jump to start of string */ - chan_members = IRC_string(cmdline,tmp_modeargs).split(' '); + j = p[p.length-1].split(" "); /* Channel members */ - if (chan_members == "") { - umode_notice(USERMODE_OPER,"Notice","Server " + this.nick + " trying to SJOIN empty channel " - + cmd[2] + " before processing."); + if (!j[0]) { + umode_notice(USERMODE_OPER,"Notice",format( + "Server %s trying to SJOIN empty channel %s before processing.", + this.nick, + p[1] + )); break; } - cm_array = []; - - for (cm in chan_members) { - var isop = false; - var isvoice = false; - if (chan_members[cm][0] == "@") { - isop = true; - chan_members[cm] = chan_members[cm].slice(1); - } - if (chan_members[cm][0] == "+") { - isvoice = true; - chan_members[cm] = chan_members[cm].slice(1); - } - var tmp_nick = Users[chan_members[cm].toUpperCase()]; - if (!tmp_nick) + for (i in j) { + k = new SJOIN_Nick(j[i]); + n = Users[k.nick.toUpperCase()]; + if (!n) continue; - cm_array.push(new SJOIN_Nick(tmp_nick,isop,isvoice)); - } - - if (cm_array.length < 1) { - umode_notice(USERMODE_OPER,"Notice","Server " + this.nick - + " trying to SJOIN empty channel " - + cmd[2] + " post processing."); - break; - } - - } - var cn_tuc = cmd[2].toUpperCase(); - var chan = Channels[cn_tuc]; - if (!chan) { - Channels[cn_tuc]=new Channel(cn_tuc); - chan = Channels[cn_tuc]; - chan.nam = cmd[2]; - chan.created = parseInt(cmd[1]); - } + if (!n.channels[tmp.nam.toUpperCase()]) { + tmp.users[n.id] = n; + n.channels[tmp.nam.toUpperCase()] = tmp; + n.bcast_to_channel(tmp, format( + "JOIN %s", + tmp.nam + ), false /*bcast*/); + } - if (cmd[3]) { - var bounce_modes = true; - if (!ThisOrigin.local || (chan.created == parseInt(cmd[1]))) - bounce_modes = false; - if (chan.created >= parseInt(cmd[1])) - this.set_chanmode(chan, mode_args, bounce_modes); - - var num_sync_modes = 0; - var push_sync_modes = "+"; - var push_sync_args = ""; - var new_chan_members = ""; - for (member in cm_array) { - if (new_chan_members) - new_chan_members += " "; - - var member_obj = cm_array[member].nick; - var is_voice = cm_array[member].isvoice; - var is_op = cm_array[member].isop; - - if (member_obj.channels[chan.nam.toUpperCase()]) - continue; - member_obj.channels[chan.nam.toUpperCase()] = chan; - chan.users[member_obj.id] = member_obj; - var joinstr = "JOIN " + chan.nam; - member_obj.bcast_to_channel(chan, joinstr, false); - if (chan.created >= parseInt(cmd[1])) { - if (is_op) { - chan.modelist[CHANMODE_OP][member_obj.id]=member_obj.id; - push_sync_modes += "o"; - push_sync_args += " " + member_obj.nick; - num_sync_modes++; - new_chan_members += "@"; - } - if (num_sync_modes >= max_modes) { - var mode1str = "MODE " + chan.nam + " " - + push_sync_modes + push_sync_args; - this.bcast_to_channel(chan,mode1str); - push_sync_modes = "+"; - push_sync_args = ""; - num_sync_modes = 0; - } - if (is_voice) { - chan.modelist[CHANMODE_VOICE][member_obj.id]=member_obj; - push_sync_modes += "v"; - push_sync_args += " " + member_obj.nick; - num_sync_modes++; - new_chan_members += "+"; - } - if (num_sync_modes >= max_modes) { - var mode2str = "MODE " + chan.nam + " " - + push_sync_modes + push_sync_args; - this.bcast_to_channel(chan,mode2str); - push_sync_modes = "+"; - push_sync_args = ""; - num_sync_modes = 0; + if (tmp.created >= parseInt(p[0])) { /* We have TS superiority */ + if (k.isop) + tmp.modelist[CHANMODE_OP][n.id] = n.id; + if (k.isvoice) + tmp.modelist[CHANMODE_VOICE][n.id] = n.id; + if (k.isop || k.isvoice) { + origin.bcast_to_channel(tmp, format( + "MODE %s +%s%s %s", + tmp.nam, + k.isop ? "o" : "", + k.isvoice ? "v" : "", + n.nick + ), false /*bcast*/); } } - new_chan_members += member_obj.nick; - } - if (num_sync_modes) { - var mode3str = "MODE " + chan.nam + " " - + push_sync_modes + push_sync_args; - this.bcast_to_channel(chan, mode3str); } - // Synchronize the TS to what we received. - if (chan.created > parseInt(cmd[1])) - chan.created = parseInt(cmd[1]); + if (tmp.created > parseInt(p[0])) + tmp.created = parseInt(p[0]); this.bcast_to_servers_raw( format(":%s SJOIN %s %s %s :%s", - ThisOrigin.nick, - chan.created, - chan.nam, - chan.chanmode(true), - new_chan_members + origin.nick, + tmp.created, + tmp.nam, + tmp.chanmode(true /* pass args */), + p[p.length-1] ) ); - } else { - if (ThisOrigin.server) { - umode_notice(USERMODE_OPER,"Notice", "Server " + ThisOrigin.nick - + " trying to SJOIN itself to a channel?!"); + } else { /* User single SJOIN */ + if (origin.server) { + umode_notice(USERMODE_OPER,"Notice",format( + "Server %s trying to SJOIN itself to a channel?!", + origin.nick + )); break; } - if (ThisOrigin.channels[chan.nam.toUpperCase()]) + if (origin.channels[tmp.nam.toUpperCase()]) break; - ThisOrigin.channels[chan.nam.toUpperCase()] = chan; - chan.users[ThisOrigin.id] = ThisOrigin; - ThisOrigin.bcast_to_channel(chan, "JOIN " + chan.nam, false); + origin.channels[tmp.nam.toUpperCase()] = tmp; + tmp.users[origin.id] = origin; + origin.bcast_to_channel(tmp, format( + "JOIN %s", + tmp.nam + ), false /*bcast*/); this.bcast_to_servers_raw( format(":%s SJOIN %s %s", - ThisOrigin.nick, - chan.created, - chan.nam + origin.nick, + tmp.created, + tmp.nam ) ); } break; case "SQUIT": - var sq_server; - var reason; - - if (!cmd[1] || !this.hub) - sq_server = this; + if (!p[0] || !this.hub) + tmp = this; else - sq_server = searchbyserver(cmd[1]); - if (!sq_server) - break; - reason = IRC_string(cmdline,2); - if (!reason || !cmd[2]) - reason = ThisOrigin.nick; - if (sq_server == -1) { - this.bcast_to_servers_raw("SQUIT " + this.nick + " :Forwards SQUIT."); + tmp = searchbyserver(p[0]); + if (!tmp) + break; + if (!p[1]) /* Reason */ + p[1] = origin.nick; + if (tmp == -1) { + this.bcast_to_servers_raw(format( + "SQUIT %s :Forwards SQUIT.", + this.nick + )); this.quit("Forwards SQUIT.",true); break; } - // message from our uplink telling us a server is gone - if (this.nick == sq_server.parent) { - sq_server.quit(reason,false,false,ThisOrigin); + /* message from our uplink telling us a server is gone */ + if (this.nick == tmp.parent) { + tmp.quit(p[1],false,false,origin); break; } - // oper or server going for squit of a server - if (!sq_server.local) { - sq_server.rawout(":" + ThisOrigin.nick + " SQUIT " + sq_server.nick + " :" + reason); + /* oper or server going for squit of a server */ + if (!tmp.local) { + tmp.rawout(format( + ":%s SQUIT %s :%s", + origin.nick, + tmp.nick, + p[1] + )); break; } - var msg = "Received SQUIT " + cmd[1] + " from " + - ThisOrigin.nick + "(" + reason + ")"; - server_bcast_to_servers("GNOTICE :" + msg); - umode_notice(USERMODE_ROUTING,"Routing","from " + - servername + ": " + msg); - sq_server.quit(reason); + server_bcast_to_servers(format( + "GNOTICE :Received SQUIT %s from %s (%s)", + p[0], + origin.nick, + p[1] + )); + umode_notice(USERMODE_ROUTING,"Routing",format( + "from %s: Received SQUIT %s from %s (%s)", + ServerName, + p[0], + origin.nick, + p[1] + )); + tmp.quit(p[1]); break; case "STATS": - if (!cmd[2] || ThisOrigin.server) + if (!p[1] || origin.server) + break; + if (wildmatch(ServerName, p[1])) { + origin.do_stats(p[0][0]); break; - if (cmd[2][0] == ":") - cmd[2] = cmd[2].slice(1); - if (wildmatch(servername, cmd[2])) { - ThisOrigin.do_stats(cmd[1][0]); - } else { - var dest_server = searchbyserver(cmd[2]); - if (!dest_server) - break; - dest_server.rawout(":" + ThisOrigin.nick + " STATS " + cmd[1][0] + " :" + dest_server.nick); } + tmp = searchbyserver(p[1]); + if (!tmp) + break; + tmp.rawout(format( + ":%s STATS %s :%s", + origin.nick, + p[0][0], + tmp.nick + )); break; case "SUMMON": - if (!cmd[2] || ThisOrigin.server) + if (!p[1] || origin.server) + break; + if (wildmatch(ServerName, p[1])) { + if (SUMMON) + origin.do_summon(p[0]); + else + origin.numeric445(); break; - if (cmd[2][0] == ":") - cmd[2] = cmd[2].slice(1); - if (wildmatch(servername, cmd[2])) { - if (enable_users_summon) { - ThisOrigin.do_summon(cmd[1]); - } else { - ThisOrigin.numeric445(); - break; - } - } else { - var dest_server = searchbyserver(cmd[1]); - if (!dest_server) - break; - dest_server.rawout(":" + ThisOrigin.nick + " SUMMON " + cmd[1] + " :" + dest_server.nick); } + tmp = searchbyserver(p[1]); + if (!tmp) + break; + tmp.rawout(format( + ":%s SUMMON %s :%s", + origin.nick, + p[1], + tmp.nick + )); break; case "TIME": - if (!cmd[1] || ThisOrigin.server) + if (!p[0] || origin.server) + break; + if (wildmatch(ServerName, p[0])) { + origin.numeric391(); break; - if (cmd[1][0] == ":") - cmd[1] = cmd[1].slice(1); - if (wildmatch(servername, cmd[1])) { - ThisOrigin.numeric391(); - } else { - var dest_server = searchbyserver(cmd[1]); - if (!dest_server) - break; - dest_server.rawout(":" + ThisOrigin.nick + " TIME :" + dest_server.nick); } + tmp = searchbyserver(p[0]); + if (!tmp) + break; + tmp.rawout(format( + ":%s TIME :%s", + origin.nick, + tmp.nick + )); break; case "TOPIC": - if (!cmd[4]) + if (!p[3]) break; - var chan = Channels[cmd[1].toUpperCase()]; - if (!chan) + tmp = Channels[p[0].toUpperCase()]; + if (!tmp) break; - var the_topic = IRC_string(cmdline,4); - if (the_topic == chan.topic) + if (p[3] == tmp.topic) break; - chan.topic = the_topic; - if (this.hub) - chan.topictime = cmd[3]; - else - chan.topictime = time(); - chan.topicchangedby = cmd[2]; - var str = "TOPIC " + chan.nam + " :" + chan.topic; - ThisOrigin.bcast_to_channel(chan,str,false); - this.bcast_to_servers_raw(":" + ThisOrigin.nick + " TOPIC " + chan.nam + " " + cmd[2] + " " - + chan.topictime + " :" + chan.topic); + tmp.topictime = this.hub ? p[2] : time(); + tmp.topic = p[3]; + tmp.topicchangedby = p[1]; + origin.bcast_to_channel(tmp, format( + "TOPIC %s :%s", + tmp.nam, + tmp.topic + ),false /*bcast*/); + this.bcast_to_servers_raw(format( + ":%s TOPIC %s %s %d :%s", + origin.nick, + tmp.nam, + p[1], + tmp.topictime, + tmp.topic + )); break; case "TRACE": - if (!cmd[1] || ThisOrigin.server) + if (!p[0] || origin.server) break; - ThisOrigin.do_trace(cmd[1]); + origin.do_trace(p[0]); break; case "USERS": - if (!cmd[1] || ThisOrigin.server) + if (!p[0] || origin.server) + break; + tmp = searchbyserver(p[0]); + if (!tmp) break; - if (cmd[1][0] == ":") - cmd[1] = cmd[1].slice(1); - if (searchbyserver(cmd[1]) == -1) { + if (tmp == -1) { ThisOrigin.numeric351(); - } else { - // psst, pass it on - var dest_server = searchbyserver(cmd[1]); - if (!dest_server) - break; - dest_server.rawout(":" + ThisOrigin.nick + " USERS :" + dest_server.nick); + break; } + tmp.rawout(format( + ":%s USERS :%s", + origin.nick, + tmp.nick + )); break; case "VERSION": - if (!cmd[1] || ThisOrigin.server) + if (!p[0] || origin.server) + break; + tmp = searchbyserver(p[0]); + if (!tmp) + break; + if (tmp == -1) { + origin.numeric351(); break; - if (cmd[1][0] == ":") - cmd[1] = cmd[1].slice(1); - if (searchbyserver(cmd[1]) == -1) { - // it's for us, return the message - ThisOrigin.numeric351(); - } else { - // psst, pass it on - var dest_server = searchbyserver(cmd[1]); - if (!dest_server) - break; // someone messed up. - dest_server.rawout(":" + ThisOrigin.nick + " VERSION :" + dest_server.nick); } + tmp.rawout(format( + ":%s VERSION :%s", + origin.nick, + tmp.nick + )); break; case "WALLOPS": - if (!cmd[1]) + if (!p[0]) break; - var str = ":" + ThisOrigin.nick + " WALLOPS :" + - IRC_string(cmdline,1); - wallopers(str); - this.bcast_to_servers_raw(str); + Write_All_Opers(format( + ":%s WALLOPS :%s", + origin.nick, + p[0] + )); + this.bcast_to_servers_raw(format( + ":%s WALLOPS :%s", + origin.nick, + p[0] + )); break; case "WHOIS": - if (!cmd[2] || ThisOrigin.server) - break; - if (searchbyserver(cmd[1]) == -1) { - var wi_nicks = IRC_string(cmd[2],0).split(","); - for (wi_nick in wi_nicks) { - var wi = Users[wi_nicks[wi_nick].toUpperCase()]; - if (wi) - ThisOrigin.do_whois(wi); - else - ThisOrigin.numeric401(wi_nicks[wi_nick]); - } - ThisOrigin.numeric(318, wi_nicks[0]+" :End of /WHOIS list."); - } else { - var dest_server = searchbyserver(cmd[1]); - if (!dest_server) - break; - dest_server.rawout(":" + ThisOrigin.nick + " WHOIS " - + dest_server.nick + " :" + IRC_string(cmd[2],0)); - } - break; - case "TKL": /* DreamForge/Unreal style global network line modification */ - if (!ThisOrigin.uline) { - umode_notice(USERMODE_OPER,"Notice","Non-U:Lined server " - + ThisOrigin.nick + " trying to utilize TKL."); + if (!p[1] || origin.server) break; - } - if (cmd[2] != "G") /* We don't support anything other than G:Lines. */ - break; - var tkl_add = false; - if (cmd[1] == "+") - tkl_add = true; - - var akill_reason = IRC_string(cmdline,8); - - /* Propagate this to the network */ - 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)) + tmp = searchbyserver(p[0]); + if (!tmp) break; - KLines.push(new KLine(this_uh,akill_reason,"A")); - scan_for_klined_clients(); - break; - case "SAMODE": /* :source o #channel modechange modeparams */ + if (tmp == -1) { + k = p[1].split(","); + for (i in k) { + n = Users[k[i].toUpperCase()]; + if (n) { + origin.do_whois(n); + continue; + } + origin.numeric401(k[i]); + } + origin.numeric(318, format( + "%s :End of /WHOIS list.", + k[0] + )); + break; + } + tmp.rawout(format( + ":%s WHOIS %s :%s", + origin.nick, + tmp.nick, + p[1] + )); break; case "CAPAB": case "BURST": - case "SVSMODE": - case "SETHOST": /* We do not honour SETHOST. */ - break; // Silently ignore for now. + return 0; /* Silently ignore these commands */ default: - umode_notice(USERMODE_OPER,"Notice","Server " + ThisOrigin.nick + " sent unrecognized command: " - + cmdline); + umode_notice(USERMODE_OPER,"Notice",format( + "Server %s sent unrecognized command: %s %s", + origin.nick, + cmd.verb, + p.join(" ") + )); return 0; } /* This part only executed if the command was legal. */ - if (!Profile[command]) - Profile[command] = new StatsM; - Profile[command].executions++; + if (!Profile[cmd.verb]) + Profile[cmd.verb] = new StatsM; + Profile[cmd.verb].executions++; Profile.ticks += system.timer - clockticks; } ////////// Functions ////////// function server_bcast_to_servers(str,type) { - for(thisClient in Local_Servers) { - var srv = Local_Servers[thisClient]; - if (!type || (srv.type == type)) - srv.rawout(str); + var i; + + for (i in Local_Servers) { + if (!type || (Local_Servers[i].type == type)) + Local_Servers[i].rawout(str); } } function IRCClient_bcast_to_servers(str) { - for(thisClient in Local_Servers) { - if (Local_Servers[thisClient].nick != this.parent) - Local_Servers[thisClient].originatorout(str,this); + var i; + + for (i in Local_Servers) { + if (Local_Servers[i].nick != this.parent) + Local_Servers[i].originatorout(str,this); } } function IRCClient_bcast_to_servers_raw(str) { - for(thisClient in Local_Servers) { - if (Local_Servers[thisClient].nick != this.parent) - Local_Servers[thisClient].rawout(str); + var i; + + for (i in Local_Servers) { + if (Local_Servers[i].nick != this.parent) + Local_Servers[i].rawout(str); } } @@ -1158,7 +1243,7 @@ function Server_Quit(str,suppress_bcast,is_netsplit,origin) { if (is_netsplit) { this.netsplit(str); } else if (this.local) { - this.netsplit(servername + " " + this.nick); + this.netsplit(ServerName + " " + this.nick); if (!suppress_bcast) this.bcast_to_servers_raw("SQUIT " + this.nick + " :" + str); } else if (origin) { @@ -1174,65 +1259,111 @@ function Server_Quit(str,suppress_bcast,is_netsplit,origin) { this.netsplit(); } + if (this.socket.outbound) { + if (YLines[this.ircclass].active > 0) { + YLines[this.ircclass].active--; + log(LOG_DEBUG, format("Class %s down to %d active out of %d", + this.ircclass, + YLines[this.ircclass].active, + YLines[this.ircclass].maxlinks + )); + } else { + log(LOG_ERR, format("Class %d YLine going negative", this.ircclass)); + } + } + if (this.local) { - if (server.client_remove!=undefined) + this.recvq.purge(); + this.sendq.purge(); + + if (server.client_remove !== undefined) server.client_remove(this.socket); - // FIXME: wrong phrasing below gnotice("Closing Link: " + this.nick + " (" + str + ")"); - this.rawout("ERROR :Closing Link: [" + this.uprefix + "@" + this.hostname + "] (" + str + ")"); - if (this.socket!=undefined) + if (this.socket !== undefined) { + if (this.socket.is_connected) { + this.socket.send(format( + "ERROR :Closing Link: [%s@%s] (%s)", + this.uprefix, + this.hostname, + str + )); + } + log(LOG_NOTICE,format( + "Connection with server %s was closed. (%s)", + this.nick, + str + )); + if (this.socket.callback_id !== undefined) { + this.socket.clearOn("read", this.socket.callback_id); + delete this.socket.callback_id; + } this.socket.close(); - delete Local_Sockets[this.id]; - } - if (this.outgoing) { - if (YLines[this.ircclass].active > 0) { - YLines[this.ircclass].active--; - log(LOG_DEBUG, "Class "+this.ircclass+" down to "+YLines[this.ircclass].active+" active out of "+YLines[this.ircclass].maxlinks); + delete this.socket; } - else - log(LOG_ERROR, format("Class %d YLine going negative", this.ircclass)); + + js.clearInterval(this.pinginterval); } - delete Local_Sockets[this.id]; - delete Local_Sockets_Map[this.id]; - delete Local_Servers[this.id]; + + delete Local_Servers[this.nick.toLowerCase()]; delete Servers[this.nick.toLowerCase()]; delete this; - rebuild_socksel_array = true; } function IRCClient_synchronize() { + var i; + + log(LOG_NOTICE,format( + "Connection established with server %s", + this.nick + )); + this.rawout("BURST"); // warn of impending synchronization - for (my_server in Servers) { - if (Servers[my_server].id != this.id) - this.server_info(Servers[my_server]); + for (i in Servers) { + if (Servers[i].id != this.id) + this.server_info(Servers[i]); } - for (my_client in Users) { - this.server_nick_info(Users[my_client]); + for (i in Users) { + this.server_nick_info(Users[i]); } - for (my_channel in Channels) { - if (my_channel[0] == "#") - this.server_chan_info(Channels[my_channel]); + for (i in Channels) { + if (i[0] == "#") + this.server_chan_info(Channels[i]); } - gnotice(this.nick + " has processed user/channel burst, "+ - "sending topic burst."); - for (my_channel in Channels) { - if ((my_channel[0] == "#") && Channels[my_channel].topic) { - var chan = Channels[my_channel]; - this.rawout("TOPIC " + chan.nam + " " + chan.topicchangedby + " " + chan.topictime - + " :" + chan.topic); + + gnotice(format( + "%s has processed user/channel burst, sending topic burst.", + this.nick + )); + + for (i in Channels) { + if ((i[0] == "#") && Channels[i].topic) { + this.rawout(format( + "TOPIC %s %s %s :%s", + Channels[i].nam, + Channels[i].topicchangedby, + Channels[i].topictime, + Channels[i].topic + )); } } - this.rawout("BURST 0"); // burst completed. - gnotice(this.nick + " has processed topic burst " + - "(synched to network data)."); + this.rawout("BURST 0"); /* burst completed. */ + + gnotice(format( + "%s has processed topic burst (synched to network data).", + this.nick + )); } function IRCClient_server_info(sni_server) { - var realhops = parseInt(sni_server.hops)+1; - this.rawout(":" + sni_server.linkparent + " SERVER " + sni_server.nick + " " + realhops - + " :" + sni_server.info); + this.rawout(format( + ":%s SERVER %s %s :%s", + sni_server.linkparent, + sni_server.nick, + parseInt(sni_server.hops)+1, + sni_server.info + )); } function IRCClient_server_nick_info(sni_client) { @@ -1259,6 +1390,11 @@ function IRCClient_server_nick_info(sni_client) { } function IRCClient_reintroduce_nick(nick) { + if (Enforcement) { + log(LOG_DEBUG, "Trying to reintroduce nick while Enforcement mode is off."); + return 0; + } + this.server_nick_info(nick); for (uchan in nick.channels) { @@ -1291,6 +1427,8 @@ function IRCClient_reintroduce_nick(nick) { } function IRCClient_server_chan_info(sni_chan) { + var i; + this.rawout(format("SJOIN %s %s %s :%s", sni_chan.created, sni_chan.nam, @@ -1300,13 +1438,13 @@ function IRCClient_server_chan_info(sni_chan) { var modecounter=0; var modestr="+"; var modeargs=""; - for (aBan in sni_chan.modelist[CHANMODE_BAN]) { + for (i in sni_chan.modelist[CHANMODE_BAN]) { modecounter++; modestr += "b"; if (modeargs) modeargs += " "; - modeargs += sni_chan.modelist[CHANMODE_BAN][aBan]; - if (modecounter >= max_modes) { + modeargs += sni_chan.modelist[CHANMODE_BAN][i]; + if (modecounter >= MAX_MODES) { this.ircout(format("MODE %s %s %s", sni_chan.nam, modestr, @@ -1327,6 +1465,6 @@ function IRCClient_server_chan_info(sni_chan) { } function gnotice(str) { - umode_notice(USERMODE_ROUTING,"Routing","from " + servername + ": " + str); + umode_notice(USERMODE_ROUTING,"Routing","from " + ServerName + ": " + str); server_bcast_to_servers("GNOTICE :" + str); } diff --git a/exec/load/ircd/unregistered.js b/exec/load/ircd/unregistered.js index 538a295b6c44af9c25b94b89dcb862f6d444585c..dac7b78c597a0a3190628acf6b5fd1214937e1fa 100644 --- a/exec/load/ircd/unregistered.js +++ b/exec/load/ircd/unregistered.js @@ -23,53 +23,55 @@ function Unregistered_Client(id,socket) { ////////// VARIABLES // Bools/Flags that change depending on connection state. - this.pinged = false; // Sent PING? + this.pinged = false; 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 = "*"; this.realname = ""; this.uprefix = ""; - this.hostname = ""; this.password = ""; this.ircclass = 0; - this.idletime = time(); + this.idletime = system.timer; this.ip = socket.remote_ip_address; + /* Render IPv4 in IPv6 addresses as IPv4 */ + if (this.ip.toUpperCase().slice(0,7) == "::FFFF:") { + this.ip = this.ip.slice(7); + } + this.hostname = this.ip; this.pending_resolve = false; - this.pending_resolve_time = time(); - this.sendps = true; // Send the PASS/SERVER pair by default. - this.outgoing = false; /* We're an incoming connection by default */ + this.dns_pending = time(); // Variables (consts, really) that point to various state information this.socket = socket; + this.socket.irc = this; ////////// FUNCTIONS - // Functions we use to control clients (specific) this.work = Unregistered_Commands; - this.IRC_Unregistered_Commands = IRC_Unregistered_Commands; this.quit = Unregistered_Quit; this.check_timeout = IRCClient_check_timeout; - this.check_queues = IRCClient_check_queues; - this.resolve_check = Unregistered_Resolve_Check; this.welcome = Unregistered_Welcome; - // Output helper functions (shared) + this.QWK_Master_Authentication = QWK_Master_Authentication; this.rawout = rawout; this.originatorout = originatorout; this.ircout = ircout; - this.sendq = new IRC_Queue(); - this.recvq = new IRC_Queue(); + this.sendq = new IRC_Queue(this); + this.recvq = new IRC_Queue(this); this.server_notice = IRCClient_server_notice; this.check_nickname = IRCClient_check_nickname; - // Numerics (shared) + this.Unregistered_Check_User_Registration = Unregistered_Check_User_Registration; this.numeric = IRCClient_numeric; this.numeric451 = IRCClient_numeric451; this.numeric461 = IRCClient_numeric461; this.numeric462 = IRCClient_numeric462; - ////////// EXECUTION - // We're a local socket that must be polled. - Local_Sockets[id] = socket.descriptor; - Local_Sockets_Map[id] = this; - rebuild_socksel_array = true; - log(format("%04u Accepted new connection: %s port %s", + this.pinginterval = js.setInterval( + IRCClient_check_timeout, + YLines[0].pingfreq * 1000, + this + ); + if (this.socket.outbound) { + this.dns_pending = false; + return 0; + } + log(LOG_NOTICE, format("%04u Accepted new connection: %s port %s", socket.descriptor, this.ip, socket.remote_port @@ -79,219 +81,184 @@ function Unregistered_Client(id,socket) { || (this.ip.slice(0,8) == "192.168.") || (this.ip.slice(0,7) == "100.64.") || (this.ip.slice(0,7) == "172.16." ) + || (this.ip.slice(0,2) == "::") + || (this.ip.slice(0,6).toUpperCase() == "FE80::") ) { - this.hostname = servername; - this.pending_resolve_time = false; + this.hostname = ServerName; + this.dns_pending = false; } else { - this.pending_resolve = load(true,"dnshelper.js",this.ip); + this.reverse_resolver = function(resp) { + if (!this.dns_pending) { + log(LOG_DEBUG,format("[UNREG] WARNING: Received extraneous RDNS reply.")); + return false; + } + log(LOG_DEBUG,format("[UNREG] Received RDNS reply: %s", resp[0])); + if (resp[0] === undefined) { + /* Fall through */ + } else if (resp[0].search(/[.]/) == -1 || resp[0].search(/[.]local$/i) > -1) { + this.hostname = ServerName; + } else { + this.hostname = resp[0]; + log(LOG_DEBUG,format("[UNREG] Resolving hostname: %s", resp[0])); + DNS_Resolver.resolve(resp[0], this.forward_resolver, this); + return true; + } + this.dns_pending = false; + this.Unregistered_Check_User_Registration(); + return false; + } + this.forward_resolver = function(resp) { + if (!this.dns_pending) { + log(LOG_DEBUG,format("[UNREG] WARNING: Received extraneous DNS reply.")); + return false; + } + log(LOG_DEBUG,format("[UNREG] Received DNS reply: %s", resp[0])); + if ((resp[0] === undefined) || (resp[0] != this.ip)) { + this.hostname = this.ip; + } + this.dns_pending = false; + this.Unregistered_Check_User_Registration(); + return true; + } + log(LOG_DEBUG,format("[UNREG] Resolving IP: %s", this.ip)); + DNS_Resolver.reverse(this.ip, this.reverse_resolver, this); } - this.server_notice("*** " + VERSION + " (" + serverdesc + ") Ready. " + id); + this.server_notice(format("*** %s (%s) Ready.", + VERSION, + ServerDesc + )); } -////////// Command Parsers ////////// - function Unregistered_Commands(cmdline) { - // Only accept up to 512 bytes from unregistered clients. - cmdline = cmdline.slice(0,512); - - // Kludge for broken clients. - if ((cmdline[0] == "\r") || (cmdline[0] == "\n")) - cmdline = cmdline.slice(1); + cmdline = cmdline.slice(0,512); /* 512 bytes per RFC1459 */ - if (debug) - log("[UNREG]: " + cmdline); + log(LOG_DEBUG,"[UNREG]: " + cmdline); - this.IRC_Unregistered_Commands(cmdline); -} - -function IRC_Unregistered_Commands(cmdline) { var clockticks = system.timer; - var cmd; - var command; + var i, cmd, p; - cmd = cmdline.split(" "); - if (cmdline[0] == ":") { - // Silently ignore NULL originator commands. - if (!cmd[1]) - return 0; - // We allow non-matching :<origin> commands through FOR NOW, - // since some *broken* IRC clients use this in the unreg stage. - command = cmd[1].toUpperCase(); - cmdline = cmdline.slice(cmdline.indexOf(" ")+1); - cmd = cmdline.split(" "); - } else { - command = cmd[0].toUpperCase(); - } + cmd = IRC_parse(cmdline); + + if (!cmd.verb) + return 0; - // we ignore all numerics from unregistered clients. - if (command.match(/^[0-9]+/)) + if (cmd.verb.match(/^[0-9]+/)) return 0; - var legal_command = true; /* For tracking STATS M */ + p = cmd.params; - switch(command) { + switch(cmd.verb) { case "PING": - if (!cmd[1]) { + if (!p[0]) { this.numeric(409,":No origin specified."); break; } - this.rawout("PONG " + servername + " :" + IRC_string(cmdline,1)); + this.rawout("PONG " + ServerName + " :" + p[0]); break; case "CAP": case "CAPAB": - break; // Silently ignore, for now. + break; /* Silently ignore, for now. */ case "NICK": - if (!cmd[1]) { + if (!p[0]) { this.numeric(431, ":No nickname given."); break; } - var the_nick = IRC_string(cmd[1],0).slice(0,max_nicklen); - if (this.check_nickname(the_nick)) - this.nick = the_nick; + p[0] = p[0].slice(0,MAX_NICKLEN); + if (this.check_nickname(p[0])) + this.nick = p[0]; + this.Unregistered_Check_User_Registration(); break; case "PASS": - if (!cmd[1] || this.password) + if (!p[0] || this.password) break; - this.password = IRC_string(cmd[1],0); + this.password = p[0]; break; case "PONG": this.pinged = false; break; case "SERVER": - if ((this.nick != "*") || this.criteria_met) { + if (this.nick != "*") { this.numeric462(); break; } - if (!cmd[3]) { + if (!p[2]) { this.numeric461("SERVER"); break; } - if (Servers[cmd[1].toLowerCase()]) { - if (parseInt(cmd[2]) < 2) + if (Servers[p[0].toLowerCase()]) { + if (parseInt(p[1]) < 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) { - for (nl in NLines) { - 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) + if (parseInt(p[1]) < 2) { + for (i in NLines) { + if ( (NLines[i].flags&NLINE_CHECK_QWKPASSWD) + && wildmatch(p[0],NLines[i].servername) + && Check_QWK_Password( + p[0].slice(0,p[0].indexOf(".")).toUpperCase(), + this.password + ) ) { - for (qwkm_nl in NLines) { - if (NLines[qwkm_nl].flags&NLINE_IS_QWKMASTER) { - var qwk_master = searchbyserver(NLines[qwkm_nl].servername); - if (!qwk_master) { - this.quit("No QWK master available for authorization."); - return 0; - } else { - 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; + Register_Unregistered_Local_Server(this, p, NLines[i]); + return true; } - } - } - 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; - } - // Take care of registration right now. - Servers[cmd[1].toLowerCase()] = new IRC_Server(); - var new_server = Servers[cmd[1].toLowerCase()]; - Local_Servers[this.id] = new_server; - Local_Sockets_Map[this.id] = new_server; - delete Unregistered[this.id]; - rebuild_socksel_array = true; - new_server.socket = this.socket; - new_server.hops = cmd[2]; - new_server.info = IRC_string(cmdline,3); - new_server.parent = cmd[1]; - new_server.linkparent = servername; - new_server.id = this.id; - new_server.flags = this_nline.flags; - new_server.nick = cmd[1]; - new_server.hostname = this.hostname; - new_server.recvq = this.recvq; - new_server.sendq = this.sendq; - new_server.ircclass = this.ircclass; - new_server.outgoing = this.outgoing; - if (!qwk_slave) { // qwk slaves should never be hubs. - for (hl in HLines) { - if (HLines[hl].servername.toLowerCase() - == cmd[1].toLowerCase()) { - new_server.hub = true; - break; + if ( (NLines[i].flags&NLINE_CHECK_WITH_QWKMASTER) + && wildmatch(p[0],NLines[i].servername) + && this.QWK_Master_Authentication( + p[0].slice(0,p[0].indexOf(".")).toUpperCase() + ) + ) { + this.quit("QWK relaying not yet implemented."); + return true; } - } - // nor should they be ulined. - for (u in ULines) { - if (ULines[u] == cmd[1]) { - new_server.uline = true; - break; + if ( (NLines[i].password == this.password) + && (wildmatch(p[0],NLines[i].servername)) + ) { + Register_Unregistered_Local_Server(this, p, NLines[i]); + return true; } } } - new_server.finalize_server_connect("TS",this.sendps); - this.replaced_with = new_server; + this.quit("No matching server configuration found."); break; case "USER": if (this.uprefix) break; - if (!cmd[4]) { + if (!p[3]) { this.numeric461("USER"); break; } - this.realname = IRC_string(cmdline,4).slice(0,50); - this.uprefix = parse_username(cmd[1]); + this.realname = p[3].slice(0,MAX_REALNAME); + this.uprefix = parse_username(p[0]); + this.Unregistered_Check_User_Registration(); break; case "QUIT": this.quit(); return 0; case "NOTICE": - break; // drop silently + break; /* Drop silently */ default: this.numeric451(); return 0; } /* This part only executed if the command was legal. */ + if (!Profile[cmd.verb]) + Profile[cmd.verb] = new StatsM; + Profile[cmd.verb].executions++; + Profile[cmd.verb].ticks += system.timer - clockticks; +} + +function Unregistered_Check_User_Registration() { + var usernum, bbsuser; - if (!this.criteria_met && this.uprefix && (this.nick != "*") ) { - var usernum; - if (this.password) { + if (!this.dns_pending && !this.socket.outbound && this.uprefix && this.nick != "*") { + if (this.password && (system.matchuser !== undefined)) { usernum = system.matchuser(this.uprefix); if (!usernum) usernum = system.matchuser(this.nick); if (usernum) { - var bbsuser = new User(usernum); + bbsuser = new User(usernum); if (this.password.toUpperCase() == bbsuser.security.password) { this.uprefix = parse_username(bbsuser.handle); bbsuser.connection = "IRC"; @@ -301,76 +268,59 @@ function IRC_Unregistered_Commands(cmdline) { } if (!usernum) this.uprefix = "~" + this.uprefix; - this.criteria_met = true; - if (this.hostname && !this.pending_resolve_time) + if (this.hostname) this.welcome(); } - - if (!Profile[command]) - Profile[command] = new StatsM; - Profile[command].executions++; - Profile[command].ticks += system.timer - clockticks; - } -////////// Functions ////////// - function Unregistered_Quit(msg) { - if (msg) - this.rawout("ERROR :" + msg); - if (server.client_remove!=undefined) + this.recvq.purge(); + this.sendq.purge(); + if (msg && this.socket.is_connected) + this.socket.send(format("ERROR :%s\r\n", msg)); + if (server.client_remove !== undefined) server.client_remove(this.socket); if(server.clients != undefined) log(LOG_DEBUG,format("%d clients", server.clients)); else - log(LOG_INFO, "Unregistered_Quit(\""+msg+"\")"); - this.socket.close(); - if (this.outgoing) { + log(LOG_INFO, format('[UNREG] QUIT ("%s")', msg)); + if (this.socket.outbound) { if (YLines[this.ircclass].active > 0) { YLines[this.ircclass].active--; - log(LOG_DEBUG, "Class "+this.ircclass+" down to "+YLines[this.ircclass].active+" active out of "+YLines[this.ircclass].maxlinks); + log(LOG_DEBUG, format("Class %d down to %d active out of %d", + this.ircclass, + YLines[this.ircclass].active, + YLines[this.ircclass].maxlinks + )); + } else { + log(LOG_ERR, format("Class %d YLine going negative", this.ircclass)); } - else - log(LOG_ERROR, format("Class %d YLine going negative", this.ircclass)); } - delete Local_Sockets[this.id]; - delete Local_Sockets_Map[this.id]; + this.socket.clearOn("read", this.socket.callback_id); + this.socket.close(); + log(LOG_NOTICE,format( + "%04u Connection closed.", + this.socket.descriptor + )); + js.clearInterval(this.pinginterval); + delete Assigned_IDs[this.id]; delete Unregistered[this.id]; - delete this; - rebuild_socksel_array = true; -} - -function Unregistered_Resolve_Check() { - var my_resolved = this.pending_resolve.read(); - if (my_resolved) { - if (my_resolved.search(/[.]/)) - this.hostname = my_resolved; - else - this.hostname = servername; - this.pending_resolve_time = false; - } else if ( (time() - this.pending_resolve_time) > 5) { - this.hostname = this.ip; - this.pending_resolve_time = false; - } - if (this.criteria_met && !this.pending_resolve_time) - this.welcome(); - return 0; } function Unregistered_Welcome() { + var i, my_iline; + if (isklined(this.uprefix + "@" + this.hostname)) { this.numeric(465, ":You've been K:Lined from this server."); this.quit("You've been K:Lined from this server."); return 0; } - // Check for a valid I:Line. - 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)) + /* FIXME: We don't compare connecting port. */ + for (i in ILines) { + if ( (wildmatch(this.uprefix + "@" + this.ip, ILines[i].ipmask)) + && (wildmatch(this.uprefix + "@" + this.hostname, ILines[i].hostmask)) ) { - my_iline = ILines[thisILine]; + my_iline = ILines[i]; break; } } @@ -384,18 +334,11 @@ function Unregistered_Welcome() { this.quit("Denied."); return 0; } - // This user is good to go, add his connection to the total. - if ( (true_array_len(Local_Users) + true_array_len(Local_Servers)) > hcc_total) - hcc_total = true_array_len(Local_Users) + true_array_len(Local_Servers); - if (true_array_len(Local_Users) > hcc_users) - hcc_users = true_array_len(Local_Users); - // Amazing. We meet the registration criteria. Users[this.nick.toUpperCase()] = new IRC_User(this.id); var new_user = Users[this.nick.toUpperCase()]; - Local_Sockets_Map[this.id] = new_user; Local_Users[this.id] = new_user; - rebuild_socksel_array = true; new_user.socket = this.socket; + new_user.socket.irc = new_user; new_user.nick = this.nick; new_user.uprefix = this.uprefix; new_user.hostname = this.hostname; @@ -405,45 +348,143 @@ function Unregistered_Welcome() { new_user.ircclass = my_iline.ircclass; new_user.sendq = this.sendq; new_user.recvq = this.recvq; - // Shouldn't be a thing... - new_user.outgoing = this.outgoing; - hcc_counter++; + new_user.sendq.irc = new_user; + new_user.recvq.irc = new_user; + /* Remove the unregistered ping interval and install a new on based on Y:Line */ + js.clearInterval(this.pinginterval); + new_user.pinginterval = js.setInterval( + IRCClient_check_timeout, + YLines[my_iline.ircclass].pingfreq * 1000, + new_user + ); + HCC_Counter++; + if ( (true_array_len(Local_Users) + true_array_len(Local_Servers)) > HCC_Total) + HCC_Total = true_array_len(Local_Users) + true_array_len(Local_Servers); + if (true_array_len(Local_Users) > HCC_Users) + HCC_Users = true_array_len(Local_Users); this.numeric("001", ":Welcome to the Synchronet IRC Service, " + new_user.nuh); - this.numeric("002", ":Your host is " + servername + ", running version " + VERSION); - this.numeric("003", ":This server was created " + strftime("%a %b %d %Y at %H:%M:%S %Z",server_uptime)); - this.numeric("004", servername + " " + VERSION + " oiwbgscrkfydnhF biklmnopstv"); - this.numeric("005", "NETWORK=Synchronet MAXBANS=" + max_bans + " " - + "MAXCHANNELS=" + max_user_chans + " CHANNELLEN=" + max_chanlen + " " - + "KICKLEN=" + max_kicklen + " NICKLEN=" + max_nicklen + " " - + "TOPICLEN=" + max_topiclen + " MODES=" + max_modes + " " - + "CHANTYPES=#& CHANLIMIT=#:" + max_user_chans + " PREFIX=(ov)@+ " - + "STATUSMSG=@+ :are available on this server."); - this.numeric("005", "CASEMAPPING=ascii SILENCE=" + max_silence + " " - + "ELIST=cmnt CHANMODES=b,k,l,imnpst " - + "MAXLIST=b:" + max_bans + " " - + "TARGMAX=JOIN:,KICK:,KILL:,NOTICE:,PART:,PRIVMSG:,WHOIS:,WHOWAS: " - + ":are available on this server."); + this.numeric("002", ":Your host is " + ServerName + ", running version " + VERSION); + this.numeric("003", format(":This server was created %s", SERVER_UPTIME_STRF)); + this.numeric("004", ServerName + " " + VERSION + " oiwbgscrkfydnhF biklmnopstv"); + /* This needs to be rewritten so the server capabilities are rendered dynamically */ + this.numeric("005","NETWORK=Synchronet" + + " MAXBANS=" + MAX_BANS + + " MAXCHANNELS=" + MAX_USER_CHANS + + " CHANNELLEN=" + MAX_CHANLEN + + " KICKLEN=" + MAX_KICKLEN + + " NICKLEN=" + MAX_NICKLEN + + " TOPICLEN=" + MAX_TOPICLEN + + " MODES=" + MAX_MODES + + " CHANTYPES=#&" + + " CHANLIMIT=#&:" + MAX_USER_CHANS + + " PREFIX=(ov)@+" + + " STATUSMSG=@+" + + " :are available on this server."); + this.numeric("005","CASEMAPPING=ascii" + + " SILENCE=" + MAX_SILENCE + + " ELIST=cmnt" + + " CHANMODES=b,k,l,imnpst" + + " MAXLIST=b:" + MAX_BANS + + " TARGMAX=JOIN:,KICK:,KILL:,NOTICE:,PART:,PRIVMSG:,WHOIS:,WHOWAS: " /* FIXME */ + + " :are available on this server."); + this.numeric("005","AWAYLEN=" + MAX_AWAYLEN + + " :are available on this server."); new_user.lusers(); new_user.motd(); - umode_notice(USERMODE_CLIENT,"Client","Client connecting: " + - this.nick + " (" + this.uprefix + "@" + this.hostname + - ") [" + this.ip + "] {" + hcc_counter + "}"); + umode_notice(USERMODE_CLIENT,"Client",format( + "Client connecting: %s (%s@%s) [%s] {%d}", + this.nick, + this.uprefix, + this.hostname, + this.ip, + HCC_Counter + )); 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( - format("NICK %s 1 %s + %s %s %s 0 %s :%s", + 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, + ServerName, ip_to_int(new_user.ip), this.realname - ) - ); + )); + log(LOG_NOTICE, format( + "New IRC user: %s (%s@%s)", + this.nick, + this.uprefix, + this.hostname + )); /* we're no longer unregistered. */ - this.replaced_with = new_user; delete Unregistered[this.id]; - delete this; +} + +function QWK_Master_Authentication(qwkid) { + var i, s; + + for (i in NLines) { + if (NLines[i].flags&NLINE_IS_QWKMASTER) { + s = searchbyserver(NLines[i].servername); + if (!s) { + this.quit("No QWK master available for authentication."); + return false; + } + s.rawout(format( + ":%s PASS %s :%s QWK", + ServerName, + NLines[i].password, + qwkid + )); + return true; + } + } + return false; +} + +function Register_Unregistered_Local_Server(unreg, p, nline) { + var i, s; + + Servers[p[0].toLowerCase()] = new IRC_Server(); + s = Servers[p[0].toLowerCase()]; + Local_Servers[p[0].toLowerCase()] = s; + s.socket = unreg.socket; + s.socket.irc = s; + s.hops = p[1]; + s.info = p[2]; + s.parent = p[0]; + s.linkparent = ServerName; + s.id = p[0].toLowerCase(); + s.flags = nline.flags; + s.nick = p[0]; + s.hostname = unreg.hostname; + s.recvq = unreg.recvq; + s.sendq = unreg.sendq; + s.recvq.irc = s; + s.sendq.irc = s; + s.ircclass = unreg.ircclass; + + for (i in HLines) { + if (HLines[i].servername.toLowerCase() == p[0].toLowerCase()) { + s.hub = true; + break; + } + } + for (i in ULines) { + if (ULines[i] == p[0]) { + s.uline = true; + break; + } + } + + /* Remove the unregistered ping interval and install a new on based on Y:Line */ + js.clearInterval(unreg.pinginterval); + s.pinginterval = js.setInterval( + IRCClient_check_timeout, + YLines[unreg.ircclass].pingfreq * 1000, + s + ); + s.finalize_server_connect("TS"); + delete Unregistered[unreg.id]; + delete Assigned_IDs[unreg.id]; } diff --git a/exec/load/ircd/user.js b/exec/load/ircd/user.js index 619eabc4d51e99c2d96dc5f2c6c2b89c73a35752..c0e92283d0c93a6740d3ff3cfbc1b0942ea8e3a1 100644 --- a/exec/load/ircd/user.js +++ b/exec/load/ircd/user.js @@ -19,25 +19,25 @@ */ -const USERMODE_NONE =(1<<0); // NONE -const USERMODE_OPER =(1<<1); // o -const USERMODE_INVISIBLE =(1<<2); // i -const USERMODE_WALLOPS =(1<<3); // w -const USERMODE_CHATOPS =(1<<4); // b -const USERMODE_GLOBOPS =(1<<5); // g -const USERMODE_SERVER =(1<<6); // s -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_STATS_LINKS =(1<<11); // y -const USERMODE_DEBUG =(1<<12); // d -const USERMODE_ROUTING =(1<<13); // n -const USERMODE_HELP =(1<<14); // h -const USERMODE_NOTHROTTLE =(1<<15); // F -const USERMODE_ADMIN =(1<<16); // A +const USERMODE_NONE =(1<<0); /* NONE */ +const USERMODE_OPER =(1<<1); /* o */ +const USERMODE_INVISIBLE =(1<<2); /* i */ +const USERMODE_WALLOPS =(1<<3); /* w */ +const USERMODE_CHATOPS =(1<<4); /* b */ +const USERMODE_GLOBOPS =(1<<5); /* g */ +const USERMODE_SERVER =(1<<6); /* s */ +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_STATS_LINKS =(1<<11); /* y */ +const USERMODE_DEBUG =(1<<12); /* d */ +const USERMODE_ROUTING =(1<<13); /* n */ +const USERMODE_HELP =(1<<14); /* h */ +const USERMODE_NOTHROTTLE =(1<<15); /* F */ +const USERMODE_ADMIN =(1<<16); /* A */ -USERMODE_CHAR = new Object; +const USERMODE_CHAR = {}; USERMODE_CHAR["o"] = USERMODE_OPER; USERMODE_CHAR["i"] = USERMODE_INVISIBLE; USERMODE_CHAR["w"] = USERMODE_WALLOPS; @@ -55,110 +55,102 @@ USERMODE_CHAR["h"] = USERMODE_HELP; USERMODE_CHAR["F"] = USERMODE_NOTHROTTLE; USERMODE_CHAR["A"] = USERMODE_ADMIN; -// Most umodes aren't propagated across the network. Define the ones that are. -USERMODE_BCAST = new Object; +/* Most umodes aren't propagated across the network. Define the ones that are. */ +const USERMODE_BCAST = {}; USERMODE_BCAST["o"] = true; USERMODE_BCAST["i"] = true; USERMODE_BCAST["h"] = true; USERMODE_BCAST["A"] = true; -// FIXME: Services modes are broadcast but not displayed to the user. -USERMODE_SERVICES = {}; +/* Services modes are broadcast but not displayed to the user. */ +const USERMODE_SERVICES = {}; -// Various permissions that can be set on an O:Line -const OLINE_CAN_REHASH =(1<<0); // r -const OLINE_CAN_RESTART =(1<<1); // R -const OLINE_CAN_DIE =(1<<2); // D -const OLINE_CAN_GLOBOPS =(1<<3); // g -const OLINE_CAN_WALLOPS =(1<<4); // w -const OLINE_CAN_LOCOPS =(1<<5); // l -const OLINE_CAN_LSQUITCON =(1<<6); // c -const OLINE_CAN_GSQUITCON =(1<<7); // C -const OLINE_CAN_LKILL =(1<<8); // k -const OLINE_CAN_GKILL =(1<<9); // K -const OLINE_CAN_KLINE =(1<<10); // b -const OLINE_CAN_UNKLINE =(1<<11); // B -const OLINE_CAN_LGNOTICE =(1<<12); // n -const OLINE_CAN_GGNOTICE =(1<<13); // N -const OLINE_IS_ADMIN =(1<<14); // A -// Synchronet IRCd doesn't have umode +a RESERVED -const OLINE_CAN_UMODEC =(1<<16); // c -const OLINE_CAN_CHATOPS =(1<<19); // s -const OLINE_CHECK_SYSPASSWD =(1<<20); // S -const OLINE_CAN_DEBUG =(1<<21); // x -const OLINE_IS_GOPER =(1<<22); // "big O" +/* Various permissions that can be set on an O:Line */ +const OLINE_CAN_REHASH =(1<<0); /* r */ +const OLINE_CAN_RESTART =(1<<1); /* R */ +const OLINE_CAN_DIE =(1<<2); /* D */ +const OLINE_CAN_GLOBOPS =(1<<3); /* g */ +const OLINE_CAN_WALLOPS =(1<<4); /* w */ +const OLINE_CAN_LOCOPS =(1<<5); /* l */ +const OLINE_CAN_LSQUITCON =(1<<6); /* c */ +const OLINE_CAN_GSQUITCON =(1<<7); /* C */ +const OLINE_CAN_LKILL =(1<<8); /* k */ +const OLINE_CAN_GKILL =(1<<9); /* K */ +const OLINE_CAN_KLINE =(1<<10); /* b */ +const OLINE_CAN_UNKLINE =(1<<11); /* B */ +const OLINE_CAN_LGNOTICE =(1<<12); /* n */ +const OLINE_CAN_GGNOTICE =(1<<13); /* N */ +const OLINE_IS_ADMIN =(1<<14); /* A */ +/* Synchronet IRCd doesn't have umode +a RESERVED */ +const OLINE_CAN_UMODEC =(1<<16); /* c */ +const OLINE_CAN_CHATOPS =(1<<19); /* s */ +const OLINE_CHECK_SYSPASSWD =(1<<20); /* S */ +const OLINE_CAN_EVAL =(1<<21); /* x */ +const OLINE_IS_GOPER =(1<<22); /* "big O" */ -// Bits used for walking the complex WHO flags. -const WHO_AWAY =(1<<0); // a -const WHO_CHANNEL =(1<<1); // c -const WHO_REALNAME =(1<<2); // g -const WHO_HOST =(1<<3); // h -const WHO_IP =(1<<4); // i -const WHO_CLASS =(1<<5); // l -const WHO_UMODE =(1<<6); // m -const WHO_NICK =(1<<7); // n -const WHO_OPER =(1<<8); // o -const WHO_SERVER =(1<<9); // s -const WHO_TIME =(1<<10); // t -const WHO_USER =(1<<11); // u -const WHO_FIRST_CHANNEL =(1<<12); // C -const WHO_MEMBER_CHANNEL =(1<<13); // M -const WHO_SHOW_IPS_ONLY =(1<<14); // I +/* Bits used for walking the complex WHO flags. */ +const WHO_AWAY =(1<<0); /* a */ +const WHO_CHANNEL =(1<<1); /* c */ +const WHO_REALNAME =(1<<2); /* g */ +const WHO_HOST =(1<<3); /* h */ +const WHO_IP =(1<<4); /* i */ +const WHO_CLASS =(1<<5); /* l */ +const WHO_UMODE =(1<<6); /* m */ +const WHO_NICK =(1<<7); /* n */ +const WHO_OPER =(1<<8); /* o */ +const WHO_SERVER =(1<<9); /* s */ +const WHO_TIME =(1<<10); /* t */ +const WHO_USER =(1<<11); /* u */ +const WHO_FIRST_CHANNEL =(1<<12); /* C */ +const WHO_MEMBER_CHANNEL =(1<<13); /* M */ +const WHO_SHOW_IPS_ONLY =(1<<14); /* I */ +const WHO_SHOW_PARENT =(1<<15); /* X */ +const WHO_SHOW_ID =(1<<16); /* Y */ +const WHO_SHOW_CALLBACKID =(1<<17); /* Z */ -// Bits used for walking complex LIST flags. -const LIST_CHANMASK =(1<<0); // a -const LIST_CREATED =(1<<1); // c -const LIST_MODES =(1<<2); // m -const LIST_TOPIC =(1<<3); // o -const LIST_PEOPLE =(1<<4); // p -const LIST_TOPICAGE =(1<<5); // t -const LIST_DISPLAY_CHAN_MODES =(1<<6); // M +/* Bits used for walking complex LIST flags. */ +const LIST_CHANMASK =(1<<0); /* a */ +const LIST_CREATED =(1<<1); /* c */ +const LIST_MODES =(1<<2); /* m */ +const LIST_TOPIC =(1<<3); /* o */ +const LIST_PEOPLE =(1<<4); /* p */ +const LIST_TOPICAGE =(1<<5); /* t */ +const LIST_DISPLAY_CHAN_MODES =(1<<6); /* M */ -////////// Objects ////////// function IRC_User(id) { - ////////// VARIABLES - // Bools/Flags that change depending on connection state. - this.flagged_for_quit = false; // QUIT later? - this.local = true; // are we a local socket? - this.pinged = false; // sent PING? - this.server = false; // No, we're not a server. - this.uline = false; // Are we services? - // Variables containing user/server information as we receive it. + this.local = true; /* are we a local socket? */ + this.pinged = false; /* sent PING? */ + this.server = false; /* No, we're not a server. */ + this.uline = false; /* Are we services? */ this.away = ""; - this.channels = new Object; + this.channels = {}; this.connecttime = time(); this.created = 0; this.flags = 0; this.hops = 0; this.hostname = ""; - this.idletime = time(); + this.idletime = system.timer; this.invited = ""; this.ircclass = 0; this.mode = 0; this.nick = ""; this.parent = 0; this.realname = ""; - this.servername = servername; - this.silence = new Array; /* A true array. */ + this.servername = ServerName; + this.silence = []; this.talkidle = time(); this.uprefix = ""; this.id = id; - this.throttle_count = 0; /* Number of commands executed within 2 secs */ - this.outgoing = false; - // Variables (consts, really) that point to various state information this.socket = ""; - ////////// FUNCTIONS - // Functions we use to control clients (specific) + /* Functions */ this.issilenced = User_IsSilenced; this.quit = User_Quit; this.work = User_Work; - // Socket functions this.ircout=ircout; this.originatorout=originatorout; this.rawout=rawout; - this.sendq = new IRC_Queue(); - this.recvq = new IRC_Queue(); - // Output helper functions (shared) + this.sendq = new IRC_Queue(this); + this.recvq = new IRC_Queue(this); this.bcast_to_channel=IRCClient_bcast_to_channel; this.bcast_to_channel_servers=IRCClient_bcast_to_channel_servers; this.bcast_to_list=IRCClient_bcast_to_list; @@ -186,26 +178,25 @@ function IRC_User(id) { this.server_notice=IRCClient_server_notice; this.services_msg=IRCClient_services_msg; this.trace_all_opers=IRCClient_trace_all_opers; - // WHO + /* WHO */ this.do_basic_who=IRCClient_do_basic_who; this.do_complex_who=IRCClient_do_complex_who; this.do_who_usage=IRCClient_do_who_usage; this.match_who_mask=IRCClient_match_who_mask; - // LIST + /* LIST */ this.do_basic_list=IRCClient_do_basic_list; this.do_complex_list=IRCClient_do_complex_list; this.do_list_usage=IRCClient_do_list_usage; - // Global functions + /* Global functions */ this.check_nickname=IRCClient_check_nickname; this.check_timeout=IRCClient_check_timeout; - this.check_queues=IRCClient_check_queues; this.get_usermode=IRCClient_get_usermode; this.netsplit=IRCClient_netsplit; this.onchanwith=IRCClient_onchanwith; this.rmchan=IRCClient_RMChan; this.setusermode=IRCClient_setusermode; this.set_chanmode=IRCClient_set_chanmode; - // Numerics + /* Numerics */ this.numeric=IRCClient_numeric; this.numeric200=IRCClient_numeric200; this.numeric201=IRCClient_numeric201; @@ -241,157 +232,127 @@ function IRC_User(id) { this.numeric462=IRCClient_numeric462; this.numeric481=IRCClient_numeric481; this.numeric482=IRCClient_numeric482; - // Getters + /* Getters */ this.__defineGetter__("nuh", function() { return(this.nick + "!" + this.uprefix + "@" + this.hostname); }); } -////////// Command Parser ////////// function User_Work(cmdline) { var clockticks = system.timer; - var cmd; - var command; + var cmd, p; + var tmp, i, j, k; /* Temp vars used during command processing */ - /* If a user sends 5 commands within 2 seconds, add it to the recvq and - don't bother processing anything else the user sends until at least - 2 seconds from now. */ - if ( (time() - this.idletime) <= 2) { - if (this.throttle_count >= 4) { - this.recvq.prepend(cmdline); - return 0; - } - this.throttle_count++; - } else { - this.throttle_count = 0; - } + this.idletime = system.timer; - this.idletime = time(); + cmdline = cmdline.slice(0,512); /* 512 bytes per RFC1459 */ - // Only accept up to 512 bytes from clients as per RFC1459. - cmdline = cmdline.slice(0,512); - // Kludge for broken clients. - if ((cmdline[0] == "\r") || (cmdline[0] == "\n")) - cmdline = cmdline.slice(1); - if (debug) - log(format("[%s<-%s]: %s",servername,this.nick,cmdline)); - cmd = cmdline.split(" "); - if (cmdline[0] == ":") { - // Silently ignore NULL originator commands. - if (!cmd[1]) - return 0; - // if :<originator> doesn't match nick of originating - // socket, drop silently per RFC. - if (cmd[0].slice(1).toUpperCase() != this.nick.toUpperCase()) - return 0; - command = cmd[1].toUpperCase(); - cmdline = cmdline.slice(cmdline.indexOf(" ")+1); - // resplit cmd[] - cmd = cmdline.split(" "); - } else { - command = cmd[0].toUpperCase(); - } + log(LOG_DEBUG,format("[%s<-%s]: %s",ServerName,this.nick,cmdline)); + + cmd = IRC_parse(cmdline); + + if (!cmd.verb) + return 0; + + if (cmd.source.nick && cmd.source.nick.toUpperCase() != this.nick.toUpperCase()) + return 0; - // Ignore possible numerics from clients. - if (command.match(/^[0-9]+/)) + if (cmd.verb.match(/^[0-9]+/)) return 0; - var legal_command = true; /* For tracking STATS M */ + p = cmd.params; - switch (command) { - // RFC1459 states that we must reply to a PING as fast as - // possible, which is why this is on top. + switch (cmd.verb) { + /* Prioritize PING as per RFC1459 */ case "PING": - if (!cmd[1]) { + if (!p[0]) { this.numeric(409,":No origin specified."); break; } - if (cmd[1][0] == ":") cmd[1] = cmd[1].slice(1); - if (cmd[2]) { - if (cmd[2][0] == ":") - cmd[2] = cmd[2].slice(1); - var dest_server = searchbyserver(cmd[2]); - if (!dest_server) { - this.numeric402(cmd[2]); + if (p[1]) { + tmp = searchbyserver(p[1]); + if (!tmp) { + this.numeric402(p[1]); break; } - if (dest_server != -1) { - dest_server.rawout(":" + this.nick + " PING " + this.nick - + " :" + cmd[2]); + if (tmp != -1) { + tmp.rawout(format( + ":%s PING %s :%s", + this.nick, + this.nick, + p[1] + )); break; } } - this.ircout("PONG " + servername + " :" + cmd[1]); + this.ircout(format("PONG %s :%s", + ServerName, + p[0] + )); break; case "PRIVMSG": - if (!cmd[1]) { + if (!p[0]) { this.numeric411("PRIVMSG"); break; } - var my_ircstr = IRC_string(cmdline,2); - if (!cmd[2] || my_ircstr == "") { + if (!p[1]) { this.numeric412(); break; } - var targets = cmd[1].split(','); - for (pm in targets) { - this.do_msg(targets[pm],"PRIVMSG",my_ircstr); + tmp = p[0].split(','); + for (i in tmp) { + this.do_msg(tmp[i],"PRIVMSG",p[1]); } this.talkidle = time(); break; case "MODE": - var chan; - - if (!cmd[1]) - break; // silently ignore - if (!cmd[2]) { - // information only - if ((cmd[1][0] == "#") || (cmd[1][0] == "&")) { - chan = Channels[cmd[1].toUpperCase()]; - if (chan) { - var fullmodes = false; - if (this.channels[chan.nam.toUpperCase()]) - fullmodes = true; - this.numeric(324, chan.nam +" "+ chan.chanmode(fullmodes)); - this.numeric(329, chan.nam +" "+ chan.created); - break; - } else { - this.numeric401(cmd[1]); + if (!p[0]) + break; + if (!p[1]) { /* User requesting info only */ + if ((p[0][0] == "#") || (p[0][0] == "&")) { + tmp = Channels[p[0].toUpperCase()]; + if (!tmp) { + this.numeric401(p[0]); break; } - } else { - // getting my umode - if (cmd[1].toUpperCase() == this.nick.toUpperCase()) + this.numeric(324, format("%s %s", + tmp.nam, + tmp.chanmode(this.channels[p[0].toUpperCase()] ? true : false) + )); + this.numeric(329, format("%s %s", + tmp.nam, + tmp.created + )); + break; + } else { /* Requesting a usermode */ + if (p[0].toUpperCase() == this.nick.toUpperCase()) this.numeric(221, this.get_usermode()); - else if (Users[cmd[1].toUpperCase()]) + else if (Users[p[0].toUpperCase()]) this.numeric(502, ":Can't view mode for other users."); else - this.numeric401(cmd[1]); + this.numeric401(p[0]); break; } - } else { - if ((cmd[1][0] == "#") || (cmd[1][0] == "&")) { - chan = Channels[cmd[1].toUpperCase()]; - if (!chan) { - this.numeric403(cmd[1]); + } else { /* Mode flags were passed */ + if ((p[0][0] == "#") || (p[0][0] == "&")) { + tmp = Channels[p[0].toUpperCase()]; + if (!tmp) { + this.numeric403(p[0]); break; } - cmd.shift(); - cmd.shift(); - var modeline = cmd.join(" "); - this.set_chanmode(chan,modeline,false); - } else if (cmd[1].toUpperCase() == this.nick.toUpperCase()) { - this.setusermode(cmd[2]); + p.shift(); + this.set_chanmode(tmp, p.join(" "), false /* bounce? */); + } else if (p[0].toUpperCase() == this.nick.toUpperCase()) { + this.setusermode(p[1]); } else { this.numeric(502, ":Can't change mode for other users."); } } break; case "AWAY": - var my_ircstr = IRC_string(cmdline,1).slice(0,80); - if (cmd[1] && (my_ircstr != "") ) { - this.away=my_ircstr; + if (p[0] && p[0] != "") { + this.away = p[0].slice(0, MAX_AWAYLEN); this.numeric(306, ":You have been marked as being away."); this.bcast_to_servers("AWAY :" + this.away); } else { @@ -401,266 +362,246 @@ function User_Work(cmdline) { } break; case "JOIN": - if (!cmd[1]) { + if (!p[0]) { this.numeric461("JOIN"); break; } - if (cmd[1][0] == ":") - cmd[1]=cmd[1].slice(1); - var the_channels = cmd[1].split(","); - var the_keys = ""; - if (cmd[2]) - the_keys = cmd[2].split(","); - var key_counter = 0; - for(jchan in the_channels) { - var chanstr = the_channels[jchan]; - if(chanstr[0] == "0") { + k = []; /* channel keys */ + if (p[1]) + k = p[1].split(","); + + j = p[0].split(","); + for (i in j) { + if (j[i] == "0") { this.part_all(); + break; + } + if ( Channels[j[i].toUpperCase()] + && k[0] + && (Channels[j[i].toUpperCase()].mode&CHANMODE_KEY) + ) { + this.do_join(j[i], k[0]); + k.shift(); } else { - var chan = Channels[chanstr.toUpperCase()]; - if (chan && the_keys[key_counter] && (chan.mode&CHANMODE_KEY)){ - this.do_join(chanstr.slice(0,max_chanlen) - ,the_keys[key_counter]) - key_counter++; - } else { - this.do_join(chanstr.slice(0,max_chanlen),""); - } + this.do_join(j[i].slice(0, MAX_CHANLEN)); } } break; case "PART": - var the_channels; - - if (!cmd[1]) { + if (!p[0]) { this.numeric461("PART"); break; } - the_channels = cmd[1].split(","); - for(pchan in the_channels) { - this.do_part(the_channels[pchan]); + tmp = p[0].split(","); + for (i in tmp) { + this.do_part(tmp[i]); } break; case "KICK": - if (!cmd[2]) { + if (!p[1]) { this.numeric461("KICK"); break; } - var chan = Channels[cmd[1].toUpperCase()]; - if (!chan) { - this.numeric403(cmd[1]); + tmp = Channels[p[0].toUpperCase()]; + if (!tmp) { + this.numeric403(p[0]); break; } - if (!chan.modelist[CHANMODE_OP][this.id]) { - this.numeric482(chan.nam); + if (!tmp.modelist[CHANMODE_OP][this.id]) { + this.numeric482(tmp.nam); break; } - var nick = Users[cmd[2].toUpperCase()]; - if (!nick) { - nick = search_nickbuf(cmd[2]); - if (!nick) { - this.numeric401(cmd[2]); + var j = Users[p[1].toUpperCase()]; /* target */ + if (!j) { + j = search_nickbuf(p[1]); + if (!j) { + this.numeric401(p[1]); break; } } - if (!nick.channels[chan.nam.toUpperCase()]) { - this.numeric(441, nick.nick + " " + chan.nam - + " :They aren't on that channel!"); - break; - } - var kick_reason; - var my_ircstr = IRC_string(cmdline,3); - if (my_ircstr) - kick_reason = my_ircstr; - else - kick_reason = this.nick; - var str = "KICK " + chan.nam + " " + nick.nick + " :" + kick_reason; - this.bcast_to_channel(chan, str, true); - this.bcast_to_servers(str); - nick.rmchan(chan); + if (!j.channels[tmp.nam.toUpperCase()]) { + this.numeric(441, format( + "%s %s :They aren't on that channel!", + j.nick, + tmp.nam + )); + break; + } + + i = format("KICK %s %s :%s", + tmp.nam, + j.nick, + p[2] ? p[2] : j.nick + ); + + this.bcast_to_channel(tmp, i, true); + this.bcast_to_servers(i); + j.rmchan(tmp); break; case "TOPIC": - if (!cmd[1]) { + if (!p[0]) { this.numeric461("TOPIC"); break; } - var chan = Channels[cmd[1].toUpperCase()]; - if (!chan) { - this.numeric403(cmd[1]); + var tmp = Channels[p[0].toUpperCase()]; + if (!tmp) { + this.numeric403(p[0]); break; } - if (!this.channels[chan.nam.toUpperCase()]) { - this.numeric442(chan.nam); + if (!this.channels[tmp.nam.toUpperCase()]) { + this.numeric442(tmp.nam); break; } - if (cmd[2]) { - if (!(chan.mode&CHANMODE_TOPIC) - || chan.modelist[CHANMODE_OP][this.id]) { - var tmp_topic = IRC_string(cmdline,2).slice(0,max_topiclen); - if (tmp_topic == chan.topic) + if (p[1]) { /* Setting a topic */ + if (!(tmp.mode&CHANMODE_TOPIC) || tmp.modelist[CHANMODE_OP][this.id]) { + j = p[1].slice(0, MAX_TOPICLEN); + if (j == tmp.topic) break; - chan.topic = tmp_topic; - chan.topictime = time(); - chan.topicchangedby = this.nick; - var str = "TOPIC " + chan.nam + " :" + chan.topic; - this.bcast_to_channel(chan, str, true); - this.bcast_to_servers("TOPIC " + chan.nam + " " + this.nick - + " " + chan.topictime + " :" + chan.topic); + tmp.topic = j; + tmp.topictime = time(); + tmp.topicchangedby = this.nick; + this.bcast_to_channel( + tmp, + format("TOPIC %s :%s", tmp.nam, tmp.topic), + true /* bounceback */ + ); + this.bcast_to_servers(format( + "TOPIC %s %s %s :%s", + tmp.nam, + this.nick, + tmp.topictime, + tmp.topic + )); } else { - this.numeric482(chan.nam); + this.numeric482(tmp.nam); } - } else { // we're just looking at one - if (chan.topic) { - this.numeric332(chan); - this.numeric333(chan); + } else { /* Looking at a topic */ + if (tmp.topic) { + this.numeric332(tmp); + this.numeric333(tmp); } else { - this.numeric331(chan); + this.numeric331(tmp); } } break; case "WHOIS": - if (!cmd[1]) { + if (!p[0]) { this.numeric(431, ":No nickname given."); break; } - if (cmd[2]) { - var dest_server = searchbyserver(cmd[1]); - if (!dest_server) { - this.numeric402(cmd[1]); + if (p[1]) { /* Asking remote server */ + tmp = searchbyserver(p[0]); + if (!tmp) { + this.numeric402(p[0]); break; } - if (dest_server != -1) { - dest_server.rawout(":" + this.nick + " WHOIS " - + dest_server.nick + " :" + IRC_string(cmdline,1)); + if (tmp != -1) { + tmp.rawout(format( + ":%s WHOIS %s :%s", + this.nick, + tmp.nick, + p[0] + )); break; - } else { - cmd[1] = cmd[2]; } } - var wi_nicks = IRC_string(cmd[1],0).split(","); - for (wi_nick in wi_nicks) { - var wi = Users[wi_nicks[wi_nick].toUpperCase()]; - if (wi) - this.do_whois(wi); + tmp = p[0].split(","); + for (i in tmp) { + if (Users[tmp[i].toUpperCase()]) + this.do_whois(Users[tmp[i].toUpperCase()]); else - this.numeric401(wi_nicks[wi_nick]); + this.numeric401(tmp[i]); } - this.numeric(318, wi_nicks[0]+" :End of /WHOIS list."); + this.numeric(318, tmp[0]+" :End of /WHOIS list."); break; case "ADMIN": - if (cmd[1]) { - if (cmd[1][0] == ":") - cmd[1] = cmd[1].slice(1); - var dest_server = searchbyserver(cmd[1]); - if (!dest_server) { - this.numeric402(cmd[1]); + if (p[0]) { + tmp = searchbyserver(p[0]); + if (!tmp) { + this.numeric402(p[0]); break; } - if (dest_server != -1) { - dest_server.rawout(":" + this.nick + " ADMIN :" - + dest_server.nick); + if (tmp != -1) { + tmp.rawout(format( + ":%s ADMIN :%s", + this.nick, + tmp.nick + )); break; } } this.do_admin(); break; case "CHATOPS": - if (!((this.mode&USERMODE_OPER) && - (this.flags&OLINE_CAN_CHATOPS))) { + if (!((this.mode&USERMODE_OPER) && (this.flags&OLINE_CAN_CHATOPS))) { this.numeric481(); break; } - var my_ircstr = IRC_string(cmdline,1); - umode_notice(USERMODE_CHATOPS,"ChatOps","from " + this.nick + ": " - + my_ircstr); - server_bcast_to_servers(":" + this.nick + " CHATOPS :" + my_ircstr); + if (!p[0]) + break; + umode_notice(USERMODE_CHATOPS,"ChatOps",format( + "from %s: %s", + this.nick, + p[0] + )); + server_bcast_to_servers(format( + ":%s CHATOPS :%s", + this.nick, + p[0] + )); break; case "CONNECT": - if (!((this.mode&USERMODE_OPER) && - (this.flags&OLINE_CAN_LSQUITCON) )) { + if (!((this.mode&USERMODE_OPER) && (this.flags&OLINE_CAN_LSQUITCON) )) { this.numeric481(); break; } - if (!cmd[1]) { + if (!p[0]) { this.numeric461("CONNECT"); break; } - if (cmd[3]) { - var dest_server = searchbyserver(cmd[3]); - if (!dest_server) { - this.numeric402(cmd[3]); + if (p[2]) { /* Asking remote server to CONNECT */ + tmp = searchbyserver(p[2]); + if (!tmp) { + this.numeric402(p[2]); break; - } else if (dest_server != -1) { + } + if (tmp != -1) { if (!(this.flags&OLINE_CAN_GSQUITCON)) { this.numeric481(); break; } - dest_server.rawout(":" + this.nick + " CONNECT " + cmd[1] - + " " + cmd[2] + " " + dest_server.nick); + tmp.rawout(format( + ":%s CONNECT %s %s %s", + this.nick, + p[0], + p[1], + tmp.nick + )); break; } } - this.do_connect(cmd[1],cmd[2]); + this.do_connect(p[0],p[1]); break; - case "DEBUG": - if (!((this.mode&USERMODE_OPER) && (this.flags&OLINE_CAN_DEBUG))) { + case "EVAL": + if (!(this.mode&USERMODE_OPER) || !(this.flags&OLINE_CAN_EVAL)) { this.numeric481(); break; } - if (!cmd[1]) { - this.server_notice("Usage:"); - this.server_notice(" DEBUG D Toggle DEBUG mode on/off"); - this.server_notice(" DEBUG E <exp> Eval Javascript expression"); - this.server_notice(" DEBUG Y <val> Set yield frequency to val"); - this.server_notice(" DEBUG U Dump users stored in mem"); - this.server_notice(" DEBUG C Dump channels stored in mem"); + if (!p[0]) { + this.server_notice("No expression provided to evaluate."); break; } - switch (cmd[1][0].toUpperCase()) { - case "C": - for (mychan in Channels) { - this.server_notice(Channels[mychan].nam + "," - + Channels[mychan].mode + "," + Channels[mychan].users); - } - break; - case "D": - if (debug) { - debug=false; - umode_notice(USERMODE_OPER,"Notice","Debug mode disabled by " - + this.nuh); - } else { - debug=true; - umode_notice(USERMODE_OPER,"Notice","Debug mode enabled by " - + this.nuh); - } - break; - case "E": - cmd.shift(); - cmd.shift(); - var exp = cmd.join(" "); - umode_notice(USERMODE_DEBUG,"Debug","Oper " + this.nick - + " is using EVAL: " + exp); - try { - this.server_notice("Result: " + eval(exp)); - } catch(e) { - this.server_notice("!" + e); - } - break; - case "U": - for (myuser in Users) { - var usr = Users[myuser]; - this.server_notice(usr.nick + "," + usr.local + "," - + usr.parent + "," + usr.id); - } - break; - case "Y": - if (cmd[2]) { - umode_notice(USERMODE_DEBUG,"Debug", - "branch.yield_freq set to " + cmd[2] + " by " + this.nuh); - branch.yield_freq = parseInt(cmd[2]); - } - break; - default: - this.server_notice("Unknown DEBUG flag."); + p.shift(); + tmp = p.join(" "); + umode_notice(USERMODE_DEBUG,"Debug",format( + "Oper %s is using EVAL: %s", + this.nick, + tmp + )); + try { + this.server_notice("Result: " + eval(tmp)); + } catch(e) { + this.server_notice("!" + e); } break; case "DIE": @@ -668,136 +609,147 @@ function User_Work(cmdline) { this.numeric481(); break; } - if (diepass && !cmd[1]) { + if (Die_Password && !p[0]) { this.numeric461("DIE"); break; - } else if (diepass && (cmd[1] != diepass)) { + } else if (Die_Password && (p[0] != Die_Password)) { this.server_notice("Invalid DIE password."); break; } log(LOG_ERR,"!ERROR! Shutting down the ircd as per " + this.nuh); - js.terminated = true; + js.do_callbacks = false; break; case "ERROR": - break; // silently ignore + break; /* ERROR is silently ignored for users */ case "GLOBOPS": if (!((this.mode&USERMODE_OPER) && (this.flags&OLINE_CAN_GLOBOPS))) { this.numeric481(); break; } - if (!cmd[1]) { + if (!p[0]) { this.numeric461("GLOBOPS"); break; } - this.globops(IRC_string(cmdline,1)); + this.globops(p[0]); break; case "INFO": - if (cmd[1]) { - if (cmd[1][0] == ":") - cmd[1] = cmd[1].slice(1); - var dest_server = searchbyserver(cmd[1]); - if (!dest_server) { + if (p[0]) { + tmp = searchbyserver(p[0]); + if (!tmp) { this.numeric402(cmd[1]); break; } - if (dest_server != -1) { - dest_server.rawout(":" + this.nick + " INFO :" - + dest_server.nick); + if (tmp != -1) { + tmp.rawout(format( + ":%s INFO :%s", + this.nick, + tmp.nick + )); break; } } this.do_info(); break; case "INVITE": - if (!cmd[2]) { + if (!p[1]) { this.numeric461("INVITE"); break; } - var chanid = Channels[cmd[2].toUpperCase()]; - if (!chanid) { - this.numeric403(cmd[2]); + var tmp = Channels[p[1].toUpperCase()]; + if (!tmp) { + this.numeric403(p[1]); break; } - if (!chanid.modelist[CHANMODE_OP][this.id]) { - this.numeric482(chanid.nam); + if (!tmp.modelist[CHANMODE_OP][this.id]) { + this.numeric482(tmp.nam); break; } - var nickid = Users[cmd[1].toUpperCase()]; - if (!nickid) { - this.numeric401(cmd[1]); + j = Users[p[0].toUpperCase()]; + if (!j) { + this.numeric401(p[0]); break; } - if (nickid.channels[cmd[2].toUpperCase()]) { - this.numeric(443, nickid.nick + " " + chanid.nam - + " :is already on channel."); + if (j.channels[p[1].toUpperCase()]) { + this.numeric(443, format( + "%s %s :is already on channel.", + j.nick, + tmp.nam + )); break; } - this.numeric("341", nickid.nick + " " + chanid.nam); - nickid.originatorout("INVITE " + nickid.nick + " :" + chanid.nam,this); - nickid.invited=chanid.nam.toUpperCase(); + this.numeric("341", j.nick + " " + tmp.nam); + j.originatorout("INVITE " + j.nick + " :" + tmp.nam,this); + j.invited = tmp.nam.toUpperCase(); break; case "ISON": - if (!cmd[1]) - break; // drop silently - if (cmd[1][0] == ":") - cmd[1] = cmd[1].slice(1); - var isonstr = ":"; - var ison_nick_id; - cmd.shift(); // get rid of command - for(ison in cmd) { - ison_nick_id = Users[cmd[ison].toUpperCase()]; - if (ison_nick_id) { - if (isonstr != ":") - isonstr += " "; - isonstr += ison_nick_id.nick; + if (!p[0]) + break; /* Drop silently */ + tmp = ""; + for (i in p) { + j = Users[p[i].toUpperCase()]; + if (j) { + if (!tmp.length) + tmp = ":"; + else + tmp += " "; + tmp += j.nick; } } - this.numeric("303", isonstr); + this.numeric("303", tmp); break; case "KILL": if (!(this.mode&USERMODE_OPER) || !(this.flags&OLINE_CAN_LKILL)) { this.numeric481(); break; } - if (!cmd[2]) { + if (!p[1]) { this.numeric461("KILL"); break; } - if (cmd[1].match(/[.]+/)) { + if (p[0].match(/[.]/)) { this.numeric(483, ":You can't kill a server!"); break; } - if (cmd[2] == ":") { + if (!p[1]) { this.numeric(461, "KILL :You MUST specify a reason for /KILL."); break; } - var reason = IRC_string(cmdline,2); - var kills = cmd[1].split(","); - var target; - for(kill in kills) { - target = Users[kills[kill].toUpperCase()]; - if (!target) - target = search_nickbuf(kills[kill]); - if (!target) { - this.numeric401(kills[kill]); + tmp = p[0].split(","); + for (i in tmp) { + k = Users[tmp[i].toUpperCase()]; + if (!k) + k = search_nickbuf(tmp[i]); + if (!k) + this.numeric401(tmp[i]); continue; - } - if (target.local) { - target.quit("Local kill by " + this.nick + " (" + reason + ")"); + if (k.local) { + k.quit(format( + "Local kill by %s (%s)", + this.nick, + p[1] + )); } else { if (!(this.flags&OLINE_CAN_GKILL)) { this.numeric481(); continue; } - var trg_srv = searchbyserver(target.servername); - if (trg_srv && trg_srv.uline) { + j = searchbyserver(k.servername); + if (j && j.uline) { this.numeric(483, ":You may not KILL clients on a U:Lined server."); continue; } - server_bcast_to_servers(":" + this.nick + " KILL " - + target.nick + " :" + reason); - target.quit("Killed (" + this.nick + " (" + reason + "))",true); + server_bcast_to_servers(format( + ":%s KILL %s :%s", + this.nick, + k.nick, + p[1] + )); + target.quit(format( + "Killed (%s (%s))", + this.nick, + p[1] + ), true /* Suppress broadcast */ ); } } break; @@ -807,24 +759,26 @@ function User_Work(cmdline) { this.numeric481(); break; } - if (!cmd[2]) { + if (!p[1]) { this.numeric461("KLINE"); break; } - var kline_mask = create_ban_mask(cmd[1],true); - if (!kline_mask) { + k = create_ban_mask(p[0],true); + if (!k) { this.server_notice("Invalid K:Line mask."); break; } - if (isklined(kline_mask)) { + if (isklined(k)) { this.server_notice("K:Line already exists!"); break; } - KLines.push(new KLine(kline_mask,IRC_string(cmdline,2),"k")); - umode_notice(USERMODE_OPER,"Notice", this.nick - + " added temporary 99 min. k-line for [" - + kline_mask + "] [0]"); - scan_for_klined_clients(); + KLines.push(new KLine(k,p[1],"k")); + umode_notice(USERMODE_OPER,"Notice",format( + "%s added k-line for [%s]", + this.nick, + k + )); + Scan_For_Banned_Clients(); break; case "UNKLINE": if (!((this.mode&USERMODE_OPER) && @@ -832,97 +786,112 @@ function User_Work(cmdline) { this.numeric481(); break; } - if (!cmd[1]) { + if (!p[0]) { this.numeric461("UNKLINE"); break; } - var kline_mask = create_ban_mask(cmd[1],true); - if (!kline_mask) { + k = create_ban_mask(p[0],true); + if (!k) { this.server_notice("Invalid K:Line mask."); break; } - if (!isklined(kline_mask)) { + if (!isklined(k)) { this.server_notice("No such K:Line."); break; } - remove_kline(kline_mask); - umode_notice(USERMODE_OPER,"Notice", this.nick + - " has removed the K-Line for: [" + kline_mask + "] (1 matches)"); + remove_kline(k); + umode_notice(USERMODE_OPER,"Notice",format( + "%s has removed the K-Line for: [%s]", + this.nick, + k + )); break; case "LINKS": - if (!cmd[1]) { // * + if (!p[0]) { this.do_links(); break; - } else if (cmd[2]) { // <remote-server> <mask> - var dest_server = searchbyserver(cmd[1]); - if (!dest_server) { - this.numeric402(cmd[1]); + } else if (p[1]) { + tmp = searchbyserver(p[0]); + if (!tmp) { + this.numeric402(p[0]); break; } - if (dest_server != -1) { - dest_server.rawout(":" + this.nick + " LINKS " - + dest_server.nick + " " + cmd[2]); + if (tmp != -1) { + tmp.rawout(format( + ":%s LINKS %s %s", + this.nick, + tmp.nick, + p[1] + )); break; } - } else if (cmd[1]) { // <mask> - this.do_links(cmd[1]); - break; } + this.do_links(p[0]); break; case "LIST": - if (!cmd[1]) { + if (!p[0]) { this.do_basic_list("*"); break; } - if (cmd[1] == "?") { + if (p[0] == "?" || p[0].toUpperCase() == "HELP") { this.do_list_usage(); break; } - if (!cmd[2] && (cmd[1][0]!="+") && (cmd[1][0]!="-")) { - this.do_basic_list(cmd[1]); + if (!p[1] && (p[0][0]!="+") && (p[0][0]!="-")) { + this.do_basic_list(p[0]); break; } - this.do_complex_list(cmd); + this.do_complex_list(p); break; case "SILENCE": - var sil_dest = this.nick; - if (!cmd[1]) { /* Just display our SILENCE list */ - for (sil in this.silence) { - this.numeric(271, this.nick + " " + this.silence[sil]); + if (!p[0]) { /* Just display our SILENCE list */ + for (i in this.silence) { + this.numeric(271, format( + "%s %s", + this.nick, + this.silence[i] + )); } - } else if (Users[cmd[1].toUpperCase()]) { - /* Looking at another user's SILENCE list isn't supported - at this time. */ - sil_dest = cmd[1]; - } else { /* Adding or removing an entry to our SILENCE list */ - var target = cmd[1]; - var del_sil = false; - if (target[0] == "+") - target = target.slice(1); - if (cmd[1][0] == "-") { - del_sil = true; - target = target.slice(1); + } else if (Users[p[0].toUpperCase()]) { + if (!this.mode&USERMODE_OPER) { + this.numeric481(); + break; + } + for (i in Users[p[0].toUpperCase()].silence) { + this.numeric(271, format( + "%s %s", + Users[p[0].toUpperCase()].nick, + Users[p[0].toUpperCase()].silence[i] + )); } - var sil_mask = create_ban_mask(target); - var sil_mchar; - if (!del_sil) { /* Adding an entry */ - if (this.issilenced(sil_mask)) - break; /* Exit silently if already on SILENCE list */ - if (this.silence.length >= max_silence) { - /* We only allow 10 entries in the user's SILENCE list. */ - this.numeric(511, this.nick + " " + target + " :" - + "Your SILENCE list is full."); + } else { /* Adding or removing an entry to our SILENCE list */ + if (p[0][0] == "-") { + p[0] = p[0].slice(1); + tmp = create_ban_mask(p[0]); + j = this.issilenced(tmp); + if (j) { + delete this.silence[j]; + this.originatorout(format( + "SILENCE -%s", + tmp + )); + } + } else { + if (p[0][0] == "+") + p[0] = p[0].slice(1); + tmp = create_ban_mask(p[0]); + if (this.issilenced(tmp)) + break; /* Exit silently if already on SILENCE list */ + if (this.silence.length >= MAX_SILENCE) { + this.numeric(511, format( + "%s %s :Your SILENCE list is full.", + this.nick, + p[0] + )); break; } - this.silence.push(sil_mask); - sil_mchar = "+"; - } else { /* Deleting an entry */ - var sil_entry = this.issilenced(sil_mask); - delete this.silence[sil_entry]; - sil_mchar = "-"; + this.silence.push(tmp); } - var sil_str = "SILENCE " + sil_mchar + sil_mask; - this.originatorout(sil_str, this); break; } this.numeric(272, ":End of SILENCE List."); @@ -932,24 +901,28 @@ function User_Work(cmdline) { this.numeric481(); break; } - umode_notice(USERMODE_OPER,"LocOps","from " + - this.nick + ": " + IRC_string(cmdline,1)); + umode_notice(USERMODE_OPER,"LocOps",format( + "from %s :%s", + this.nick, + p[0] + )); break; case "LUSERS": this.lusers(); break; case "MOTD": - if (cmd[1]) { - if (cmd[1][0] == ":") - cmd[1] = cmd[1].slice(1); - var dest_server = searchbyserver(cmd[1]); - if (!dest_server) { - this.numeric402(cmd[1]); + if (p[0]) { + tmp = searchbyserver(p[0]); + if (!tmp) { + this.numeric402(p[0]); break; } - if (dest_server != -1) { - dest_server.rawout(":" + this.nick + " MOTD :" - + dest_server.nick); + if (tmp != -1) { + tmp.rawout(format( + ":%s MOTD :%s", + this.nick, + tmp.nick + )); break; } } @@ -963,96 +936,92 @@ function User_Work(cmdline) { this.motd(); break; case "NAMES": - var numnicks; - var tmp; - var chan; - var Client; - - if (!cmd[1]) { - for(tc in Channels) { - chan = Channels[tc]; - if (!(tc.mode&CHANMODE_SECRET) && - !(tc.mode&CHANMODE_PRIVATE)) - this.names(chan); + if (!p[0]) { + for (i in Channels) { + if (this.channels[Channels[i].nam.toUpperCase()] + || ( !(Channels[i].mode&CHANMODE_SECRET) + && !(Channels[i].mode&CHANMODE_PRIVATE) + ) + ) { + this.names(Channels[i]); + } } - numnicks = 0; tmp = ""; - for (thisClient in Users) { - Client = Users[thisClient]; - if (!true_array_len(Client.channels) && - !(Client.mode&USERMODE_INVISIBLE)) { - if (numnicks) + j = 0; + for (i in Users) { + if ( !true_array_len(Users[i].channels) + && !(Users[i].mode&USERMODE_INVISIBLE) + ) { + if (tmp) tmp += " "; - tmp += Client.nick; - numnicks++; - if (numnicks >= 59) { + tmp += Users[i].nick; + j++; + if (j >= 59) { this.numeric(353,"* * :"+tmp); - numnicks = 0; + j = 0; tmp = ""; } } } - if (numnicks) + if (j) this.numeric(353,"* * :"+tmp); this.numeric(366, "* :End of /NAMES list."); } else { - var chans = cmd[1].split(','); - for (nc in chans) { - if ((chans[nc][0] == "#") || - (chans[nc][0] == "&")) { - chan = Channels[chans[nc].toUpperCase()]; - if (chan) - this.names(chan); + tmp = p[0].split(","); + for (i in tmp) { + if ( (tmp[i][0] == "#") + || (tmp[i][0] == "&") + ) { + if (Channels[tmp[i].toUpperCase()]) + this.names(Channels[tmp[i].toUpperCase()]); else continue; } else { continue; } } - this.numeric(366, chans[nc] + " :End of /NAMES list."); + this.numeric(366, tmp[i] + " :End of /NAMES list."); } break; case "NICK": - var the_nick; - - if (!cmd[1]) { + if (!p[0]) { this.numeric(431, ":No nickname given."); break; } - the_nick = IRC_string(cmd[1],0).slice(0,max_nicklen); - if(this.check_nickname(the_nick) > 0) { - var str="NICK " + the_nick; - this.bcast_to_uchans_unique(str); - this.originatorout(str,this); - if (the_nick.toUpperCase() != this.nick.toUpperCase()) { + p[0] = p[0].slice(0,MAX_NICKLEN); + if(this.check_nickname(p[0]) > 0) { + this.bcast_to_uchans_unique("NICK " + p[0]); + this.originatorout("NICK " + p[0],this); + if (p[0].toUpperCase() != this.nick.toUpperCase()) { this.created = time(); - push_nickbuf(this.nick,the_nick); - // move our Users entry over. - Users[the_nick.toUpperCase()] = this; + push_nickbuf(this.nick,p[0]); + Users[p[0].toUpperCase()] = this; delete Users[this.nick.toUpperCase()]; } - // finalize - this.bcast_to_servers(str + " :" + this.created); - this.nick = the_nick; + this.bcast_to_servers(format( + "NICK %s :%s", + p[0], + this.created + )); + this.nick = p[0]; } break; case "NOTICE": - if (!cmd[1]) { + if (!p[0]) { this.numeric411("NOTICE"); break; } - var my_ircstr = IRC_string(cmdline,2); - if (!cmd[2] || !my_ircstr) { + if (!p[1]) { this.numeric412(); break; } - var targets = cmd[1].split(','); - for (nt in targets) { - this.do_msg(targets[nt],"NOTICE",my_ircstr); + tmp = p[0].split(","); + for (i in tmp) { + this.do_msg(tmp[i],"NOTICE",p[1]); } break; case "OPER": - if (!cmd[2]) { + if (!p[1]) { this.numeric461(command); break; } @@ -1060,97 +1029,115 @@ function User_Work(cmdline) { this.server_notice("You're already an IRC operator."); break; } - var oper_success = false; - for (ol in OLines) { - if ( (cmd[1].toUpperCase() == OLines[ol].nick.toUpperCase()) - && (wildmatch(this.uprefix + "@" + this.hostname,OLines[ol].hostmask)) + for (i in OLines) { + if ( (p[0].toUpperCase() == OLines[i].nick.toUpperCase()) + && (wildmatch(this.uprefix + "@" + this.hostname,OLines[i].hostmask)) && ( - ( (cmd[2] == OLines[ol].password) - && !(OLines[ol].flags&OLINE_CHECK_SYSPASSWD) + ( (p[1] == OLines[i].password) + && !(OLines[i].flags&OLINE_CHECK_SYSPASSWD) ) || ( - (OLines[ol].flags&OLINE_CHECK_SYSPASSWD) - && system.check_syspass(cmd[2]) + (OLines[i].flags&OLINE_CHECK_SYSPASSWD) + && system.check_syspass(p[1]) ) ) ) { - oper_success=true; - this.ircclass = OLines[ol].ircclass; - this.flags = OLines[ol].flags; + this.ircclass = OLines[i].ircclass; + this.flags = OLines[i].flags; + this.numeric(381, ":You are now an IRC operator."); + this.mode |= USERMODE_OPER; + this.rawout(format( + ":%s MODE %s +o", + this.nick, + this.nick + )); + umode_notice(USERMODE_SERVER,"Notice",format( + "%s (%s@%s) [%s] is now operator (O)", + this.nick, + this.uprefix, + this.hostname, + this.ip + )); + if (OLines[i].flags&OLINE_IS_GOPER) + this.bcast_to_servers("MODE "+ this.nick +" +o"); break; } } - if (!oper_success) { - this.numeric(491, ":No O:Lines for your host. Attempt logged."); - umode_notice(USERMODE_OPER,"Notice","Failed OPER attempt by " - + this.nick + " (" + this.uprefix + "@" + this.hostname + ")"); - break; - } - // otherwise we succeeded. - this.numeric(381, ":You are now an IRC operator."); - this.mode|=USERMODE_OPER; - this.rawout(":" + this.nick + " MODE " + this.nick + " +o"); - umode_notice(USERMODE_SERVER,"Notice", - this.nick + " (" + this.uprefix + - "@" + this.hostname + ") " + - "is now operator (O)"); - if (OLines[ol].flags&OLINE_IS_GOPER) - this.bcast_to_servers("MODE "+ this.nick +" +o"); + if (this.mode&USERMODE_OPER) + break; + this.numeric(491, ":No O:Lines for your host. Attempt logged."); + umode_notice(USERMODE_OPER,"Notice",format( + "Failed OPER attempt by %s (%s@%s) [%s]", + this.nick, + this.uprefix, + this.hostname, + this.ip + )); break; case "PASS": case "USER": this.numeric462(); break; case "PONG": - if (cmd[2]) { - var dest_server = searchbyserver(cmd[2]); - if (!dest_server) { - this.numeric402(cmd[2]); + if (p[1]) { + tmp = searchbyserver(p[1]); + if (!tmp) { + this.numeric402(p[1]); break; } - if (dest_server != -1) { - dest_server.rawout(":" + this.nick + " PONG " + cmd[1] + " " - + dest_server.nick); + if (tmp != -1) { + tmp.rawout(format( + ":%s PONG %s %s", + this.nick, + p[0], + tmp.nick + )); break; } } this.pinged = false; break; case "QUIT": - this.quit(IRC_string(cmdline,1)); + this.quit(p[0]); break; case "REHASH": if (!((this.mode&USERMODE_OPER) && (this.flags&OLINE_CAN_REHASH))) { this.numeric481(); break; } - if (cmd[1]) { - switch(cmd[1].toUpperCase()) { + if (p[0]) { + switch(p[0].toUpperCase()) { case "TKLINES": this.numeric382("temp klines"); - 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]; + umode_notice(USERMODE_SERVER,"Notice",format( + "%s is clearing temp klines while whistling innocently", + this.nick + )); + for (i in KLines) { + if(KLines[i].type == "k") { + delete KLines[i]; } } break; case "GC": if (js.gc!=undefined) { this.numeric382("garbage collecting"); - umode_notice(USERMODE_SERVER,"Notice",this.nick - + " is garbage collecting while whistling innocently"); + umode_notice(USERMODE_SERVER,"Notice",format( + "%s is garbage collecting while whistling innocently", + this.nick + )); js.gc(); } break; case "AKILLS": this.numeric382("akills"); - umode_notice(USERMODE_SERVER,"Notice",this.nick - + " is rehashing akills"); - for (kl in KLines) { - if(KLines[kl].type == "A") - delete KLines[kl]; + umode_notice(USERMODE_SERVER,"Notice",format( + "%s is rehashing akills while whistling innocently", + this.nick + )); + for (i in KLines) { + if(KLines[i].type == "A") + delete KLines[i]; } break; default: @@ -1158,10 +1145,11 @@ function User_Work(cmdline) { } } else { this.numeric382("Rehashing."); - umode_notice(USERMODE_SERVER,"Notice",this.nick - + " is rehashing Server config file while " - + "whistling innocently"); - read_config_file(); + umode_notice(USERMODE_SERVER,"Notice",format( + "%s is rehashing Server config file while whistling innocently", + this.nick + )); + Read_Config_File(); } break; case "RESTART": @@ -1169,187 +1157,195 @@ function User_Work(cmdline) { this.numeric481(); break; } - if (restartpass && !cmd[1]) { + if (Restart_Password && !p[0]) { this.numeric461("RESTART"); break; - } else if (restartpass && (cmd[1] != restartpass)) { + } else if (Restart_Password && (p[0] != Restart_Password)) { this.server_notice("Invalid RESTART password."); break; } - var rs_str = "Aieeeee!!! Restarting server..."; - umode_notice(USERMODE_SERVER,"Notice",rs_str); - terminate_everything(rs_str); + umode_notice(USERMODE_SERVER,"Notice",format( + "Aieeeee!!! Restarting server..." + )); + terminate_everything("Aieeeee!!! Restarting server..."); break; case "SQUIT": if (!((this.mode&USERMODE_OPER) && (this.flags&OLINE_CAN_LSQUITCON))) { this.numeric481(); break; } - if(!cmd[1]) + if(!p[0]) break; - var sq_server = searchbyserver(cmd[1]); - if(!sq_server) { - this.numeric402(cmd[1]); + tmp = searchbyserver(p[0]); + if(!tmp) { + this.numeric402(p[0]); break; } - var reason = IRC_string(cmdline,2); - if (!reason) - reason = this.nick; - if (sq_server == -1) { - this.quit(reason); + if (!p[1]) + p[1] = this.nick; + if (tmp == -1) { + this.quit(p[1]); break; } - if (!sq_server.local) { + if (!tmp.local) { if (!(this.flags&OLINE_CAN_GSQUITCON)) { this.numeric481(); break; } - sq_server.rawout(":" + this.nick + " SQUIT " + sq_server.nick - + " :" + reason); - break; - } - umode_notice(USERMODE_ROUTING,"Routing","from " + - servername + ": Received SQUIT " + cmd[1] + - " from " + this.nick + "[" + this.uprefix + - "@" + this.hostname + "] (" + reason + ")"); - sq_server.quit(reason); + tmp.rawout(format( + ":%s SQUIT %s :%s", + this.nick, + tmp.nick, + p[1] + )); + break; + } + umode_notice(USERMODE_ROUTING,"Routing",format( + "from %s: Received SQUIT %s from %s [%s@%s] (%s)", + ServerName, + p[0], + this.nick, + this.uprefix, + this.hostname, + p[1] + )); + tmp.quit(p[1]); break; case "STATS": - if(!cmd[1]) - break; - if (cmd[2]) { - if (cmd[2][0] == ":") - cmd[2] = cmd[2].slice(1); - var dest_server = searchbyserver(cmd[2]); - if (!dest_server) { - this.numeric402(cmd[2]); + if(!p[0]) + break; + if (p[1]) { + tmp = searchbyserver(p[1]); + if (!tmp) { + this.numeric402(p[1]); break; } - if (dest_server != -1) { - dest_server.rawout(":" + this.nick + " STATS " + cmd[1][0] - + " :" + dest_server.nick); + if (tmp != -1) { + tmp.rawout(format( + ":%s STATS %s :%s", + this.nick, + p[0][0], + tmp.nick + )); break; } } - this.do_stats(cmd[1][0]); + this.do_stats(p[0][0]); break; case "SUMMON": - if(!cmd[1]) { + if(!p[0]) { this.numeric411("SUMMON"); break; } - if (cmd[2]) { - if (cmd[2][0] == ":") - cmd[2] = cmd[2].slice(1); - var dest_server = searchbyserver(cmd[1]); - if (!dest_server) { - this.numeric402(cmd[2]); + if (p[1]) { + tmp = searchbyserver(p[0]); + if (!tmp) { + this.numeric402(p[1]); break; } - if (dest_server != -1) { - dest_server.rawout(":" + this.nick + " SUMMON " + cmd[1] - + " :" + dest_server.nick); + if (tmp != -1) { + tmp.rawout(format( + ":%s SUMMON %s :%s", + this.nick, + p[0], + tmp.nick + )); break; } } - if(!enable_users_summon) { + if (!SUMMON) { this.numeric445(); break; } - this.do_summon(cmd[1]); + this.do_summon(p[0]); break; case "TIME": - if (cmd[1]) { - if (cmd[1][0] == ":") - cmd[1] = cmd[1].slice(1); - var dest_server = searchbyserver(cmd[1]); - if (!dest_server) { - this.numeric402(cmd[1]); + if (p[0]) { + tmp = searchbyserver(p[0]); + if (!tmp) { + this.numeric402(p[0]); break; } - if (dest_server != -1) { - dest_server.rawout(":" + this.nick + " TIME :" - + dest_server.nick); + if (tmp != -1) { + tmp.rawout(format( + ":%s TIME :%s", + this.nick, + tmp.nick + )); break; } } this.numeric391(); break; case "TRACE": - if (cmd[1]) { - this.do_trace(cmd[1]); - } else { // no args? pass our servername as the target - this.do_trace(servername); - } + this.do_trace(p[0] ? p[0] : ServerName); break; case "USERS": - if (cmd[1]) { - if (cmd[1][0] == ":") - cmd[1] = cmd[1].slice(1); - var dest_server = searchbyserver(cmd[1]); - if (!dest_server) { - this.numeric402(cmd[1]); + if (p[0]) { + tmp = searchbyserver(p[0]); + if (!tmp) { + this.numeric402(p[0]); break; } - if (dest_server != -1) { - dest_server.rawout(":" + this.nick + " USERS :" - + dest_server.nick); + if (tmp != -1) { + tmp.rawout(format( + ":%s USERS :%s", + this.nick, + tmp.nick + )); break; } } - if (!enable_users_summon) { + if (!SUMMON) { this.numeric446(); break; } this.do_users(); break; case "USERHOST": - var uhnick; - var uh; - var uhstr = ""; - var uh_argcount; - - if (!cmd[1]) { + if (!p[0]) { this.numeric461("USERHOST"); break; } - if (cmd.length > 6) - uh_argcount = 6; - else - uh_argcount = cmd.length; + j = p.length; + if (j > MAX_USERHOST) + j = MAX_USERHOST; - for (uh=1 ; uh < uh_argcount ; uh++) { - uhnick = Users[cmd[uh].toUpperCase()]; - if (uhnick) { - if (uhstr) - uhstr += " "; - uhstr += uhnick.nick; - if (uhnick.mode&USERMODE_OPER) - uhstr += "*"; - uhstr += "="; - if (uhnick.away) - uhstr += "-"; + k = ""; + for (i = 0; i < j; i++) { + tmp = Users[p[i].toUpperCase()]; + if (tmp) { + if (k) + k += " "; + k += tmp.nick; + if (tmp.mode&USERMODE_OPER) + k += "*"; + k += "="; + if (tmp.away) + k += "-"; else - uhstr += "+"; - uhstr += uhnick.uprefix; - uhstr += "@"; - uhstr += uhnick.hostname; + k += "+"; + k += tmp.uprefix; + k += "@"; + k += tmp.hostname; } } - this.numeric(302, ":" + uhstr); + this.numeric(302, ":" + k); break; case "VERSION": - if (cmd[1]) { - if (cmd[1][0] == ":") - cmd[1] = cmd[1].slice(1); - var dest_server = searchbyserver(cmd[1]); - if (!dest_server) { - this.numeric402(cmd[1]); + if (p[0]) { + tmp = searchbyserver(p[0]); + if (!tmp) { + this.numeric402(p[0]); break; } - if (dest_server != -1) { - dest_server.rawout(":" + this.nick + " VERSION :" + dest_server.nick); + if (tmp != -1) { + tmp.rawout(format( + ":%s VERSION :%s", + this.nick, + tmp.nick + )); break; } } @@ -1360,151 +1356,152 @@ function User_Work(cmdline) { this.numeric481(); break; } - if (!cmd[1]) { + if (!p[0]) { this.numeric461("WALLOPS"); break; } - var my_ircstr = IRC_string(cmdline,1); - wallopers(":" + this.nuh + " WALLOPS :" + my_ircstr); - server_bcast_to_servers(":" + this.nick + " WALLOPS :" + my_ircstr); + Write_All_Opers(format( + ":%s WALLOPS :%s", + this.nuh, + p[0] + )); + server_bcast_to_servers(format( + ":%s WALLOPS :%s", + this.nick, + p[0] + )); break; case "WHO": - if (!cmd[1]) { + if (!p[0] || p[0] == "?" || p[0] == "HELP") { this.do_who_usage(); break; } - if (cmd[1] == "?") { - this.do_who_usage(); + if (p[1] || p[0][0] == "-" || p[0][0] == "+") { + p.unshift(""); /* FIXME: Hack to force p[0]=cmd[1] in do_complex_who() */ + this.do_complex_who(p); break; } - if (cmd[2] || (cmd[1][0] == "-")||(cmd[1][0] == "+")) - this.do_complex_who(cmd); - else - this.do_basic_who(cmd[1]); + this.do_basic_who(p[0]); break; case "WHOWAS": - if (!cmd[1]) { + if (!p[0]) { this.numeric(431, ":No nickname given."); break; } - var ww = WhoWas[cmd[1].toUpperCase()]; - var firstnick; - if (ww) { - for (e in ww) { - this.numeric(314,ww[e].nick + " " + ww[e].uprefix + " " - + ww[e].host + " * :" + ww[e].realname); - this.numeric(312,ww[e].nick + " " + ww[e].server + " :" - + ww[e].serverdesc); - if(!firstnick) - firstnick = ww[e].nick; + tmp = WhoWas[p[0].toUpperCase()]; + j = ""; + if (tmp) { + for (i in tmp) { + this.numeric(314,format( + "%s %s %s * :%s", + tmp[i].nick, + tmp[i].uprefix, + tmp[i].host, + tmp[i].realname + )); + this.numeric(312,format( + "%s %s :%s", + tmp[i].nick, + tmp[i].server, + tmp[i].serverdesc + )); + if (!j) + j = tmp[i].nick; } } - if (!firstnick) { - this.numeric(406,cmd[1] + " :There was no such nickname."); - firstnick = cmd[1]; + if (!j) { + this.numeric(406,p[0] + " :There was no such nickname."); + j = p[0]; } - this.numeric(369,firstnick+" :End of /WHOWAS command."); + this.numeric(369,j+" :End of /WHOWAS command."); break; - // Services Helper Commands... case "CS": case "CHANSERV": - if (!cmd[1]) { + if (!p[0]) { this.numeric412(); break; } - var str = cmdline.slice(cmdline.indexOf(" ")+1); - if (str[0] == ":") - str = str.slice(1); - this.services_msg("ChanServ",str); + if (p[1]) + p[0] = p.join(" "); + this.services_msg("ChanServ",p[0]); break; case "NS": case "NICKSERV": - if (!cmd[1]) { + if (!p[0]) { this.numeric412(); break; } - var str = cmdline.slice(cmdline.indexOf(" ")+1); - if (str[0] == ":") - str = str.slice(1); - this.services_msg("NickServ",str); + if (p[1]) + p[0] = p.join(" "); + this.services_msg("NickServ",p[0]); break; case "MS": case "MEMOSERV": - if (!cmd[1]) { + if (!p[0]) { this.numeric412(); break; } - var str = cmdline.slice(cmdline.indexOf(" ")+1); - if (str[0] == ":") - str = str.slice(1); - this.services_msg("MemoServ",str); + if (p[1]) + p[0] = p.join(" "); + this.services_msg("MemoServ",p[0]); break; case "OS": case "OPERSERV": - if (!cmd[1]) { + if (!p[0]) { this.numeric412(); break; } - var str = cmdline.slice(cmdline.indexOf(" ")+1); - if (str[0] == ":") - str = str.slice(1); - this.services_msg("OperServ",str); + if (p[1]) + p[0] = p.join(" "); + this.services_msg("OperServ",p[0]); break; case "HELP": case "HS": case "HELPSERV": - var str; - if (!cmd[1]) { - str = "HELP"; - } else { - str = cmdline.slice(cmdline.indexOf(" ")+1); - if (str[0] == ":") - str = str.slice(1); + if (!p[0]) { + p[0] = "HELP"; } - this.services_msg("HelpServ",str); + if (p[1]) + p[0] = p.join(" "); + this.services_msg("HelpServ",p[0]); break; case "IDENTIFY": - if (!cmd[1]) { + if (!p[0]) { this.numeric412(); break; } - var str = cmdline.slice(cmdline.indexOf(" ")+1); - if (str[0] == ":") - str = str.slice(1); - var services_target; - if (cmd[1][0] == "#") - services_target = "ChanServ"; - else - services_target = "NickServ"; - this.services_msg(services_target,"IDENTIFY " + str); + if (p[1]) + p[0] = p.join(" "); + this.services_msg(p[0][0] == "#" ? "ChanServ" : "NickServ","IDENTIFY " + p[0]); break; default: - this.numeric("421", command + " :Unknown command."); + this.numeric("421", cmd.verb + " :Unknown command."); return 0; } /* This part only executed if the command was legal. */ - if (!Profile[command]) - Profile[command] = new StatsM; - Profile[command].executions++; - Profile[command].ticks += system.timer - clockticks; + if (!Profile[cmd.verb]) + Profile[cmd.verb] = new StatsM; + Profile[cmd.verb].executions++; + Profile[cmd.verb].ticks += system.timer - clockticks; } function User_Quit(str,suppress_bcast,is_netsplit,origin) { + var i; + if (!str) str = this.nick; - var ww_serverdesc; + var ww_serverdesc = ServerDesc; var tmp = "QUIT :" + str; this.bcast_to_uchans_unique(tmp); - for(thisChannel in this.channels) { - this.rmchan(this.channels[thisChannel]); + for (i in this.channels) { + this.rmchan(this.channels[i]); } - var ww_serverdesc = serverdesc; if (this.parent) ww_serverdesc = Servers[this.servername.toLowerCase()].info; @@ -1517,7 +1514,7 @@ function User_Quit(str,suppress_bcast,is_netsplit,origin) { this.realname, this.servername, ww_serverdesc)) - 1; WhoWasMap.unshift(ww[ptr]); - if (WhoWasMap.length > WhoWas_Buffer) { + if (WhoWasMap.length > MAX_WHOWAS) { var ww_pop = WhoWasMap.pop(); var ww_obj = WhoWas[ww_pop.nick.toUpperCase()]; ww_obj.pop(); @@ -1529,38 +1526,50 @@ function User_Quit(str,suppress_bcast,is_netsplit,origin) { this.bcast_to_servers(tmp); if (this.local) { - if(server.client_remove!=undefined) + /* We purge the queue and send immediately */ + this.recvq.purge(); + this.sendq.purge(); + if (this.socket.is_connected) { + this.socket.send(format("ERROR :Closing Link: [%s@%s] (%s)\r\n", + this.uprefix, + this.hostname, + str + )); + } + umode_notice(USERMODE_CLIENT,"Client",format( + "Client exiting: %s (%s@%s) [%s] [%s]", + this.nick, + this.uprefix, + this.hostname, + str, + this.ip + )); + log(LOG_NOTICE,format( + "IRC user quitting: %s (%s@%s)", + this.nick, + this.uprefix, + this.hostname + )); + if (server.client_remove !== undefined) server.client_remove(this.socket); - this.rawout("ERROR :Closing Link: [" + this.uprefix + "@" - + this.hostname + "] (" + str + ")"); - umode_notice(USERMODE_CLIENT,"Client","Client exiting: " + this.nick - + " (" + this.uprefix + "@" + this.hostname + ") [" + str + "] [" - + this.ip + "]"); - if (this.socket!=undefined) + if (this.socket !== undefined) { + this.socket.clearOn("read", this.socket.callback_id); this.socket.close(); - } - if (this.outgoing) { - log(LOG_ERROR, "Outgoing USER connection detected!"); - if (YLines[this.ircclass].active > 0) { - YLines[this.ircclass].active--; - log(LOG_DEBUG, "Class "+this.ircclass+" down to "+YLines[this.ircclass].active+" active out of "+YLines[this.ircclass].maxlinks); - } - else - log(LOG_ERROR, format("Class %d YLine going negative", this.ircclass)); + } + js.clearInterval(this.pinginterval); } - delete Local_Sockets[this.id]; - delete Local_Sockets_Map[this.id]; + delete Assigned_IDs[this.id]; delete Local_Users[this.id]; delete Users[this.nick.toUpperCase()]; - delete this; - rebuild_socksel_array = true; } function User_IsSilenced(sil_mask) { - for (sil in this.silence) { - if (wildmatch(sil_mask,this.silence[sil])) - return sil; + var i; + + for (i in this.silence) { + if (wildmatch(sil_mask,this.silence[i])) + return i; } - return 0; /* Not Silenced. */ + return false; /* Not Silenced. */ } diff --git a/exec/load/irclib.js b/exec/load/irclib.js index 69b6b6e5407d8be7eaa4f2bea01853b1e43493e8..ef6f9238e7e14a59338d090a2620d8f6d6902e32 100644 --- a/exec/load/irclib.js +++ b/exec/load/irclib.js @@ -1,30 +1,148 @@ -// $Id: irclib.js,v 1.23 2019/08/06 13:38:11 deuce Exp $ -// -// irclib.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 -// -// A library of useful IRC functions and objects that can be used to assist -// in the creation of IRC clients, bots, servers, or custom shells. -// -// If you use this to create something neat, let me know about it! :) -// Either email, or find me on #synchronet, irc.synchro.net, nick 'Cyan' -// -// Copyright 2003-2006 Randolph Erwin Sommerfeld <sysop@rrx.ca> -// - -const IRCLIB_REVISION = "$Revision: 1.23 $".split(' ')[1]; +/* + irclib.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 + + A library of useful IRC functions and objects that can be used to assist + in the creation of IRC clients, bots, servers, or custom shells. + + If you use this to create something neat, let me know about it! :) + Either email, or find me on #synchronet, irc.synchro.net, nick 'Cyan' + + Copyright 2003-2021 Randy Sommerfeld <cyan@synchro.net> +*/ + +const IRCLIB_REVISION = "1.24"; const IRCLIB_VERSION = "irclib.js-" + IRCLIB_REVISION; +/* + This is the "grand parser" that you should be using to handle all + incoming messages, whether from a client or server. + Returns an object "ret" as defined below. + Note it is possible for nothing to be defined in 'source', in + which case you should assign the source to the socket name. i.e.: + if (!source.name) + source.name = this.socket.name; +*/ +function IRC_parse(str) { + var w, e; + + var ret = { + tags: {}, + source: { + name: "", + user: "", + host: "", + is_server: false + }, + verb: "", + params: [] + }; + + for (w = 0; str.length; w++) { + e = str.indexOf(" "); + if (e == -1) + e = str.length; + if (!ret.verb && w < 2) { + switch(str[0]) { + case "@": + ret.tags = IRC_parse_tags(str.substr(0, e)); + str = str.slice(e+1); + continue; + case ":": + ret.source = IRC_parse_source(str.substr(0, e)); + str = str.slice(e+1); + continue; + } + } + if (!ret.verb) { + ret.verb = str.substr(0, e).toUpperCase(); + str = str.slice(e+1); + continue; + } + if (str[0] == ":") { + ret.params.push(str.slice(1)); + break; + } + ret.params.push(str.substr(0, e)); + str = str.slice(e+1); + } + + return ret; +} + +/* + IRCv3 style tag parsing + Returns an array of tags, with tag name in the key +*/ +function IRC_parse_tags(str) { + var ret = {}; + var e, q; + + if (!str || str[0] != "@") + return ret; + + str = str.slice(1); + + while (str.length) { + e = str.indexOf(";"); + if (!e || e == -1) + e = str.length; + q = str.indexOf("="); + if (!q || q > e) { + ret[str.substr(0, e)] = true; + str = str.slice(e+1); + continue; + } + ret[str.substr(0, q)] = str.substr(q+1, e); + str = str.slice(e+1); + } + + return ret; +} + +/* + Returns an object "ret" based on where it's from. + It's possible this can be empty. +*/ +function IRC_parse_source(str) { + var nuh; + var ret = { + name: "", + user: "", + host: "", + is_server: false + }; + + if (!str || str[0] != ":") + return ret; + + str = str.slice(1); + + if (str.match(/!/)) { + nuh = IRC_split_nuh(str); + ret.name = nuh[0]; + ret.user = nuh[1]; + ret.host = nuh[2]; + } else if (str.match(/[.]/)) { + ret.name = str; + ret.is_server = true; + } else { + ret.name = str; + } + + return ret; +} + // Connect to a server as a client. // hostname Hostname to connect to // nick Desired nickname @@ -254,14 +372,14 @@ function IRC_split_nuh(str) { /* Convert a dotted-quad IP address to an integer, i.e. for use in CTCP */ function ip_to_int(ip) { + var quads; + if (!ip) return 0; - var quads = ip.split("."); - var addr = (quads[0]&0xff)<<24; - addr|=(quads[1]&0xff)<<16; - addr|=(quads[2]&0xff)<<8; - addr|=(quads[3]&0xff); - return addr; + + quads = ip.split("."); + + return quads[0] * 0x1000000 + quads[1] * 0x10000 + quads[2] * 0x100 + quads[3]; } /* Convert an integer to an IP address, i.e. for receiving CTCP's */ @@ -271,7 +389,7 @@ function int_to_ip(ip) { ,(ip>>16)&0xff ,(ip>>8)&0xff ,ip&0xff - )); + )); } /* A handy object for keeping track of nicks and their properties */ @@ -295,3 +413,11 @@ function IRC_Server(name) { this.info = "Unknown Server"; } +/* Object prototype for defining channel mode behaviour */ +function IRC_Channel_Mode(modechar,args,state,list,isnick) { + this.modechar = modechar; /* The mode's character */ + this.args = args; /* Does this mode take only a single arg? */ + this.state = state; /* Stateful? (changes channel behaviour) */ + this.list = list; /* Does this mode accept a list? */ + this.isnick = isnick; /* Is nick (true) or a n!u@h mask (false) */ +} diff --git a/text/ircmotd.txt b/text/ircmotd.txt index 13ca81a3ce888325879afa5380461651033bad99..e4ed748ea71e2790af78bccfd95276a8b3b4f48e 100644 --- a/text/ircmotd.txt +++ b/text/ircmotd.txt @@ -1,6 +1,8 @@ - -Welcome to the Synchronet IRC daemon! -This is an example MOTD (Message of the Day) that is displayed -when users first connect, and should be changed to something -more original. :) - + .|'''.| '|| . + ||.. ' .... ..... ... .... || .. ... .. ... .. ... .... .||. + ''|||. '|. | || || .| '' ||' || ||' ''.| '|. || || .|...|| || +. '|| '|.| || || || || || || || || || || || || +|'....|' '| .||. ||. '|...'.||. ||..||. '|..|' .||. ||. '|...' '|.' + .. | + '' + Welcome to the Synchronet IRC network! Type '/JOIN #synchronet' to chat.