Synchronet now requires the libarchive development package (e.g. libarchive-dev on Debian-based Linux distros, libarchive.org for more info) to build successfully.

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])
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
// $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) */
}
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.