Commits (2)
......@@ -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. */
......@@ -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);
}
}
......@@ -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])
......
......@@ -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",