Commits (2)
...@@ -26,6 +26,7 @@ load("sbbsdefs.js"); ...@@ -26,6 +26,7 @@ load("sbbsdefs.js");
load("sockdefs.js"); load("sockdefs.js");
load("nodedefs.js"); load("nodedefs.js");
load("irclib.js"); load("irclib.js");
load("dns.js");
/* Libraries specific to the IRCd */ /* Libraries specific to the IRCd */
load("ircd/core.js"); load("ircd/core.js");
...@@ -35,293 +36,122 @@ load("ircd/channel.js"); ...@@ -35,293 +36,122 @@ load("ircd/channel.js");
load("ircd/server.js"); load("ircd/server.js");
load("ircd/config.js"); load("ircd/config.js");
/* Global Constants */ /*** Global Constants - Always in ALL_UPPERCASE ***/
const VERSION = "SynchronetIRCd-1.9b";
const VERSION = "SynchronetIRCd-1.9a";
const VERSION_STR = format( const VERSION_STR = format(
"Synchronet %s%s-%s%s (IRCd by Randy Sommerfeld)", "Synchronet %s%s-%s%s (IRCd by Randy Sommerfeld)",
system.version, system.revision, system.version, system.revision,
system.platform, system.beta_version system.platform, system.beta_version
); );
/* This will be replaced with a dynamic CAPAB system */
/* This will be replaced with a dynamic CAPAB system later. */ const SERVER_CAPAB = "TS3 NOQUIT SSJOIN BURST UNCONNECT NICKIP TSMODE";
const Server_CAPAB = "TS3 NOQUIT SSJOIN BURST UNCONNECT NICKIP TSMODE"; /* This will be in the configuration for 2.0 */
const SUMMON = true;
// 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 /* Need to detect when a server doesn't line up with these on the network. */
// is important because connecing is a BLOCKING operation, so your IRC *will* const MAX_CHANLEN = 100; /* Maximum channel name length. */
// freeze for the amount of time it takes to connect. const MAX_NICKLEN = 30; /* Maximum nickname length. */
const ob_sock_timeout = 3; const MAX_MODES = 6; /* Maximum modes on single MODE command */
const MAX_USER_CHANS = 100; /* Maximum channels users can join */
// Should we enable the USERS and SUMMON commands? These allow IRC users to const MAX_BANS = 25; /* Maximum bans (+b) per channel */
// view users on the local BBS and summon them to IRC via a Synchronet telegram const MAX_TOPICLEN = 307; /* Maximum length of topic per channel */
// message respectively. Some people might be running the ircd standalone, or const MAX_KICKLEN = 307; /* Maximum length of kick reasons */
// otherwise don't want anonymous IRC users to have access to these commands. const MAX_WHO = 100; /* Maximum replies to WHO for non-oper users */
// We enable this by default because there's typically nothing wrong with const MAX_SILENCE = 10; /* Maximum entries on a user's SILENCE list */
// seeing who's on an arbitrary BBS or summoning them to IRC. const MAX_WHOWAS = 1000; /* Size of the WHOWAS buffer */
const enable_users_summon = true; const MAX_NICKHISTORY = 1000; /* Size of the nick change history buffer */
const MAX_CLIENT_RECVQ = 2560;/* Maximum size of unregistered & user recvq */
// EVERY server on the network MUST have the same values in ALL of these const MAX_AWAYLEN = 80; /* Maximum away message length */
// categories. If you change these, you WILL NOT be able to link to the const MAX_USERHOST = 6; /* Maximum arguments to USERHOST command */
// Synchronet IRC network. Linking servers with different values here WILL const MAX_REALNAME = 50; /* Maximum length of users real name field */
// cause your network to desynchronize (and possibly crash the IRCD)
// Remember, this is Synchronet, not Desynchronet ;) const SERVER_UPTIME = system.timer;
const max_chanlen = 100; // Maximum channel name length. const SERVER_UPTIME_STRF = strftime("%a %b %d %Y at %H:%M:%S %Z",time());
const max_nicklen = 30; // Maximum nickname length.
const max_modes = 6; // Maximum modes on single MODE command /*** Global Objects, Arrays and Variables - Always in Mixed_Case ***/
const max_user_chans = 100; // Maximum channels users can join
const max_bans = 25; // Maximum bans (+b) per channel /* Global Objects */
const max_topiclen = 307; // Maximum length of topic per channel var DNS_Resolver = new DNS();
const max_kicklen = 307; // Maximum length of kick reasons
const max_who = 100; // Maximum replies to WHO for non-oper users /* Every object (unregistered, server, user) is tagged with a unique ID */
const max_silence = 10; // Maximum entries on a user's SILENCE list var Assigned_IDs = {}; /* Key: Numeric ID */
const server_uptime = time(); 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 */ /* Global Variables */
var Default_Port = 6667;
// This will dump all I/O to and from the server to your Synchronet console. var Time_Config_Read; /* Stores time() of when the config was last read */
// 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;
}
}
/* Temporary hack to make JSexec testing code not complain */ /* Will this server try to enforce good network behaviour? */
var mline_port; /* 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 */ var ServerName;
if(this.server==undefined) { var ServerDesc = "";
if (!jsexec_revision_detail)
var jsexec_revision_detail = "JSexec";
if (cmdline_port) /* Runtime configuration */
default_port = cmdline_port; var Config_Filename = "";
else if (typeof mline_port !== undefined)
default_port = mline_port;
var server = { var Restart_Password;
socket: false, var Die_Password;
terminated: false,
version_detail: jsexec_revision_detail,
interface_ip_addr_list: ["0.0.0.0","::"]
};
server.socket = create_new_socket(default_port) var Admin1;
var Admin2;
var Admin3;
if (!server.socket) var CLines = []; /* Server [C]onnect lines */
exit(); 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! /** Begin executing code **/
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);
}
}
js.branch_limit=0; // we're not an infinite loop. log(LOG_NOTICE, VERSION + " started.");
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");
}
}
// Check for pending DNS hostname resolutions. /* If we're running from JSexec we don't have a global server object, so fake one. */
for(this_unreg in Unregistered) { if (server === undefined) {
if (Unregistered[this_unreg] && /* Define the global here so Startup() can manipulate it. */
Unregistered[this_unreg].pending_resolve_time) var server = "JSexec";
Unregistered[this_unreg].resolve_check(); }
}
// Only rebuild our selectable sockets if required. Startup();
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;
}
/* Check for ping timeouts and process queues. */ js.do_callbacks = true;
/* 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();
}
}
// do some work. function config_rehash_semaphore_check() {
if (Selectable_Sockets.length) { if(file_date(system.ctrl_dir + "ircd.rehash") > Time_Config_Read) {
var readme = socket_select(Selectable_Sockets, 1 /*secs*/); Read_Config_File();
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]);
}
} }
} }
js.setInterval(config_rehash_semaphore_check, 1000 /* milliseconds */);
Open_PLines();
/* We've exited the main loop, so terminate everything. */ /* We exit here and pass everything to the callback engine. */
terminate_everything("Terminated."); /* Deuce says I can't use exit(). So just pretend it's here instead. */
...@@ -19,69 +19,60 @@ ...@@ -19,69 +19,60 @@
*/ */
const CHANMODE_NONE =(1<<0); // NONE /* Channel Modes */
const CHANMODE_BAN =(1<<1); // b const CHANMODE_NONE =(1<<0);
const CHANMODE_INVITE =(1<<2); // i const CHANMODE_BAN =(1<<1); /* +b */
const CHANMODE_KEY =(1<<3); // k const CHANMODE_INVITE =(1<<2); /* +i */
const CHANMODE_LIMIT =(1<<4); // l const CHANMODE_KEY =(1<<3); /* +k */
const CHANMODE_MODERATED=(1<<5); // m const CHANMODE_LIMIT =(1<<4); /* +l */
const CHANMODE_NOOUTSIDE=(1<<6); // n const CHANMODE_MODERATED=(1<<5); /* +m */
const CHANMODE_OP =(1<<7); // o const CHANMODE_NOOUTSIDE=(1<<6); /* +n */
const CHANMODE_PRIVATE =(1<<8); // p const CHANMODE_OP =(1<<7); /* +o */
const CHANMODE_SECRET =(1<<9); // s const CHANMODE_PRIVATE =(1<<8); /* +p */
const CHANMODE_TOPIC =(1<<10); // t const CHANMODE_SECRET =(1<<9); /* +s */
const CHANMODE_VOICE =(1<<11); // v 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. */ const MODE = {};
function Mode(modechar,args,state,list,isnick) { MODE[CHANMODE_BAN] = new IRC_Channel_Mode("b",true,false,true,false);
this.modechar = modechar; /* The mode's character */ MODE[CHANMODE_INVITE] = new IRC_Channel_Mode("i",false,true,false,false);
this.args = args; /* Does this mode take only a single arg? */ MODE[CHANMODE_KEY] = new IRC_Channel_Mode("k",true,true,false,false);
this.state = state; /* Stateful? (changes channel behaviour) */ MODE[CHANMODE_LIMIT] = new IRC_Channel_Mode("l",true,true,false,false);
this.list = list; /* Does this mode accept a list? */ MODE[CHANMODE_MODERATED]= new IRC_Channel_Mode("m",false,true,false,false);
this.isnick = isnick; /* Is nick (true) or a n!u@h mask (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) { function Channel(nam) {
this.nam=nam; this.nam = nam;
this.mode=CHANMODE_NONE; this.mode = CHANMODE_NONE;
this.topic=""; this.topic = "";
this.topictime=0; this.topictime = 0;
this.topicchangedby=""; this.topicchangedby = "";
this.arg = new Object; this.arg = {};
this.arg[CHANMODE_LIMIT] = 0; this.arg[CHANMODE_LIMIT] = 0;
this.arg[CHANMODE_KEY] = ""; this.arg[CHANMODE_KEY] = "";
this.users=new Object; this.users = {};
this.modelist=new Object; this.modelist = {};
this.modelist[CHANMODE_OP]=new Object; this.modelist[CHANMODE_OP] = {};
this.modelist[CHANMODE_VOICE]=new Object; this.modelist[CHANMODE_VOICE] = {};
this.modelist[CHANMODE_BAN]=new Array; /* True Array */ this.modelist[CHANMODE_BAN] = [];
this.bantime=new Object; this.bantime = {};
this.bancreator=new Object; this.bancreator = {};
this.created=time(); this.created = time();
this.chanmode=Channel_chanmode; /* Functions */
this.isbanned=Channel_isbanned; this.chanmode = Channel_chanmode;
this.count_modelist=Channel_count_modelist; this.isbanned = Channel_isbanned;
this.occupants=Channel_occupants; this.count_modelist = Channel_count_modelist;
this.match_list_mask=Channel_match_list_mask; this.occupants = Channel_Occupants;
this.match_list_mask = Channel_match_list_mask;
} }
////////// Functions //////////
function ChanMode_tweaktmpmode(tmp_bit,add) { function ChanMode_tweaktmpmode(tmp_bit,add) {
if ( !this.chan.modelist[CHANMODE_OP][this.user.id] if ( !this.chan.modelist[CHANMODE_OP][this.user.id]
&& !this.user.server && !this.user.server
...@@ -100,7 +91,9 @@ function ChanMode_tweaktmpmode(tmp_bit,add) { ...@@ -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] if ( !this.chan.modelist[CHANMODE_OP][this.user.id]
&& !this.user.server && !this.user.server
&& !this.user.uline && !this.user.uline
...@@ -109,29 +102,24 @@ function ChanMode_tweaktmpmodelist(tmp_bit,add,arg) { ...@@ -109,29 +102,24 @@ function ChanMode_tweaktmpmodelist(tmp_bit,add,arg) {
this.user.numeric482(this.chan.nam); this.user.numeric482(this.chan.nam);
return 0; return 0;
} }
for (lstitem in this.tmplist[tmp_bit][add]) { for (i in this.tmplist[bit][add]) {
// Is this argument in our list for this mode already? /* Is this argument in our list for this mode already? */
if (this.tmplist[tmp_bit][add][lstitem].toUpperCase() == arg.toUpperCase()) if (this.tmplist[bit][add][i].toUpperCase() == arg.toUpperCase())
return 0; return 0;
} }
// It doesn't exist on our mode, push it in. /* It doesn't exist on our mode, push it in. */
this.tmplist[tmp_bit][add].push(arg); this.tmplist[bit][add].push(arg);
// Check for it against the other mode, and maybe nuke it. /* Check for it against the other mode, and maybe nuke it. */
var oadd; for (i in this.tmplist[bit][add ? false : true]) {
if (add) if (this.tmplist[bit][add ? false : true][i].toUpperCase() == arg.toUpperCase()) {
oadd = false; delete this.tmplist[bit][add ? false : true][i];
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];
return 0; return 0;
} }
} }
} }
function ChanMode_affect_mode_list(list_bit) { function ChanMode_affect_mode_list(list_bit) {
var tmp_nick; var tmp_nick, add, z;
for (add in this.tmplist[list_bit]) { for (add in this.tmplist[list_bit]) {
for (z in this.tmplist[list_bit][add]) { for (z in this.tmplist[list_bit][add]) {
tmp_nick = Users[this.tmplist[list_bit][add][z].toUpperCase()]; tmp_nick = Users[this.tmplist[list_bit][add][z].toUpperCase()];
...@@ -156,12 +144,15 @@ function ChanMode_affect_mode_list(list_bit) { ...@@ -156,12 +144,15 @@ function ChanMode_affect_mode_list(list_bit) {
} }
function Channel_count_modelist(list_bit) { function Channel_count_modelist(list_bit) {
var tmp_counter=0; var i;
for (tmp_count in this.modelist[list_bit]) { var ret = 0;
if (this.modelist[list_bit][tmp_count])
tmp_counter++; for (i in this.modelist[list_bit]) {
if (this.modelist[list_bit][i])
ret++;
} }
return tmp_counter;
return ret;
} }
function Channel_chanmode(show_args) { function Channel_chanmode(show_args) {
...@@ -194,32 +185,37 @@ function Channel_chanmode(show_args) { ...@@ -194,32 +185,37 @@ function Channel_chanmode(show_args) {
return tmp_mode + tmp_extras; return tmp_mode + tmp_extras;
} }
function Channel_isbanned(banned_nuh) { function Channel_isbanned(nuh) {
for (this_ban in this.modelist[CHANMODE_BAN]) { var i;
if(wildmatch(banned_nuh,this.modelist[CHANMODE_BAN][this_ban]))
for (i in this.modelist[CHANMODE_BAN]) {
if(wildmatch(nuh,this.modelist[CHANMODE_BAN][i]))
return 1; return 1;
} }
return 0; return 0;
} }
function Channel_occupants() { function Channel_Occupants() {
var chan_occupants=""; var i;
for(thisChannel_user in this.users) { var user;
var Channel_user=this.users[thisChannel_user]; var ret = "";
if (Channel_user.nick) {
if (chan_occupants) for (i in this.users) {
chan_occupants += " "; user = this.users[i];
if (this.modelist[CHANMODE_OP][Channel_user.id]) if (user.nick) {
chan_occupants += "@"; if (ret)
if (this.modelist[CHANMODE_VOICE][Channel_user.id]) ret += " ";
chan_occupants += "+"; if (this.modelist[CHANMODE_OP][user.id])
chan_occupants += Channel_user.nick; 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) { function ChanMode(chan,user) {
this.tmplist = new Object; this.tmplist = new Object;
this.tmplist[CHANMODE_OP] = new Object; this.tmplist[CHANMODE_OP] = new Object;
...@@ -249,6 +245,8 @@ function ChanMode(chan,user) { ...@@ -249,6 +245,8 @@ function ChanMode(chan,user) {
} }
function IRCClient_set_chanmode(chan,modeline,bounce_modes) { function IRCClient_set_chanmode(chan,modeline,bounce_modes) {
var i, j;
if (!chan || !modeline) if (!chan || !modeline)
return; return;
...@@ -261,9 +259,9 @@ function IRCClient_set_chanmode(chan,modeline,bounce_modes) { ...@@ -261,9 +259,9 @@ function IRCClient_set_chanmode(chan,modeline,bounce_modes) {
var mode_counter=0; var mode_counter=0;
var mode_args_counter=1; // start counting at args, not the modestring 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++; mode_counter++;
switch (cm_args[0][modechar]) { switch (cm_args[0][i]) {
case "+": case "+":
if (!add) if (!add)
add=true; add=true;
...@@ -345,11 +343,11 @@ function IRCClient_set_chanmode(chan,modeline,bounce_modes) { ...@@ -345,11 +343,11 @@ function IRCClient_set_chanmode(chan,modeline,bounce_modes) {
break; break;
default: default:
if ((!this.parent) && (!this.server)) 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--; mode_counter--;
break; break;
} }
if (mode_counter == max_modes) if (mode_counter == MAX_MODES)
break; break;
} }
...@@ -358,22 +356,22 @@ function IRCClient_set_chanmode(chan,modeline,bounce_modes) { ...@@ -358,22 +356,22 @@ function IRCClient_set_chanmode(chan,modeline,bounce_modes) {
// function. Or, clear any ops, voiced members, or bans on the 'bad' // function. Or, clear any ops, voiced members, or bans on the 'bad'
// side of the network sync. // side of the network sync.
if (bounce_modes) { if (bounce_modes) {
for (cm in MODE) { for (i in MODE) {
if (MODE[cm].state && (chan.mode&cm) && !(cmode.addbits&cm)) { if (MODE[i].state && (chan.mode&i) && !(cmode.addbits&i)) {
cmode.delbits |= cm; cmode.delbits |= i;
} else if (MODE[cm].list && MODE[cm].isnick) { } else if (MODE[i].list && MODE[i].isnick) {
for (member in chan.modelist[cm]) { for (j in chan.modelist[i]) {
cmode.delmodes += MODE[cm].modechar; cmode.delmodes += MODE[i].modechar;
cmode.delmodeargs += " " + chan.modelist[cm][member].nick; cmode.delmodeargs += " " + chan.modelist[i][j].nick;
delete chan.modelist[cm][member]; delete chan.modelist[i][j];
} }
} else if (MODE[cm].list && !MODE[cm].isnick) { } else if (MODE[i].list && !MODE[i].isnick) {
for (ban in chan.modelist[cm]) { for (j in chan.modelist[i]) {
cmode.delmodes += MODE[cm].modechar; cmode.delmodes += MODE[i].modechar;
cmode.delmodeargs += " " + chan.modelist[cm][ban]; cmode.delmodeargs += " " + chan.modelist[i][j];
delete chan.modelist[cm][ban]; delete chan.modelist[i][j];
delete chan.bantime[ban]; delete chan.bantime[j];
delete chan.bancreator[ban]; delete chan.bancreator[j];
} }
} }
} }
...@@ -381,40 +379,40 @@ function IRCClient_set_chanmode(chan,modeline,bounce_modes) { ...@@ -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 // Now we run through all the mode toggles and construct our lists for
// later display. We also play with the channel bit switches here. // later display. We also play with the channel bit switches here.
for (cm in MODE) { for (i in MODE) {
if (MODE[cm].state) { if (MODE[i].state) {
if ( (cm&CHANMODE_KEY) if ( (i&CHANMODE_KEY)
&& (cmode.addbits&CHANMODE_KEY) && (cmode.addbits&CHANMODE_KEY)
&& cmode.state_arg[cm] && cmode.state_arg[i]
&& chan.arg[cm] && chan.arg[i]
&& !this.server && !this.server
&& !this.parent && !this.parent
&& !bounce_modes && !bounce_modes
) { ) {
this.numeric(467, format("%s :Channel key already set.", chan.nam)); 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]) &&(chan.arg[CHANMODE_LIMIT]!=cmode.state_arg[CHANMODE_LIMIT])
) )
) )
) { ) {
cmode.addmodes += MODE[cm].modechar; cmode.addmodes += MODE[i].modechar;
chan.mode |= cm; chan.mode |= i;
if (MODE[cm].args && MODE[cm].state) { if (MODE[i].args && MODE[i].state) {
cmode.addmodeargs += " " + cmode.addmodeargs += " " +
cmode.state_arg[cm]; cmode.state_arg[i];
chan.arg[cm] = cmode.state_arg[cm]; chan.arg[i] = cmode.state_arg[i];
} }
} else if ((cmode.delbits&cm) && (chan.mode&cm)) { } else if ((cmode.delbits&i) && (chan.mode&i)) {
cmode.delmodes += MODE[cm].modechar; cmode.delmodes += MODE[i].modechar;
chan.mode &= ~cm; chan.mode &= ~i;
if (MODE[cm].args && MODE[cm].state) { if (MODE[i].args && MODE[i].state) {
cmode.delmodeargs += " " + cmode.delmodeargs += " " +
cmode.state_arg[cm]; cmode.state_arg[i];
chan.arg[cm] = ""; chan.arg[i] = "";
} }
} }
} }
...@@ -436,10 +434,10 @@ function IRCClient_set_chanmode(chan,modeline,bounce_modes) { ...@@ -436,10 +434,10 @@ function IRCClient_set_chanmode(chan,modeline,bounce_modes) {
} }
// Bans are a specialized case, sigh. // 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( var set_ban = create_ban_mask(
cmode.tmplist[CHANMODE_BAN][add][z]); cmode.tmplist[CHANMODE_BAN][add][i]);
if ( (chan.count_modelist(CHANMODE_BAN) >= max_bans) if ( (chan.count_modelist(CHANMODE_BAN) >= MAX_BANS)
&& !this.server && !this.server
&& !this.parent && !this.parent
) { ) {
...@@ -454,14 +452,14 @@ function IRCClient_set_chanmode(chan,modeline,bounce_modes) { ...@@ -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]) { 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() == chan.modelist[CHANMODE_BAN][ban].toUpperCase()
) { ) {
cmode.delmodes += "b"; cmode.delmodes += "b";
cmode.delmodeargs += " " + cmode.delmodeargs += " " +
cmode.tmplist[CHANMODE_BAN][false][z]; cmode.tmplist[CHANMODE_BAN][false][i];
delete chan.modelist[CHANMODE_BAN][ban]; delete chan.modelist[CHANMODE_BAN][ban];
delete chan.bantime[ban]; delete chan.bantime[ban];
delete chan.bancreator[ban]; delete chan.bancreator[ban];
...@@ -492,8 +490,8 @@ function IRCClient_set_chanmode(chan,modeline,bounce_modes) { ...@@ -492,8 +490,8 @@ function IRCClient_set_chanmode(chan,modeline,bounce_modes) {
var mode_counter = 0; var mode_counter = 0;
var mode_output = ""; var mode_output = "";
var f_mode_args = ""; var f_mode_args = "";
for (marg in final_args[0]) { for (i in final_args[0]) {
switch (final_args[0][marg]) { switch (final_args[0][i]) {
case "+": case "+":
mode_output += "+"; mode_output += "+";
add = true; add = true;
...@@ -505,7 +503,7 @@ function IRCClient_set_chanmode(chan,modeline,bounce_modes) { ...@@ -505,7 +503,7 @@ function IRCClient_set_chanmode(chan,modeline,bounce_modes) {
case "l": case "l":
if (!add) { if (!add) {
mode_counter++; mode_counter++;
mode_output += final_args[0][marg]; mode_output += final_args[0][i];
break; break;
} }
case "b": // only modes with arguments case "b": // only modes with arguments
...@@ -517,10 +515,10 @@ function IRCClient_set_chanmode(chan,modeline,bounce_modes) { ...@@ -517,10 +515,10 @@ function IRCClient_set_chanmode(chan,modeline,bounce_modes) {
f_mode_args += " " + final_args[arg_counter]; f_mode_args += " " + final_args[arg_counter];
default: default:
mode_counter++; mode_counter++;
mode_output += final_args[0][marg]; mode_output += final_args[0][i];
break; break;
} }
if (mode_counter >= max_modes) { if (mode_counter >= MAX_MODES) {
var str = "MODE " + chan.nam + " " + mode_output + f_mode_args; var str = "MODE " + chan.nam + " " + mode_output + f_mode_args;
if (!this.server) if (!this.server)
this.bcast_to_channel(chan, str, true); this.bcast_to_channel(chan, str, true);
...@@ -551,7 +549,10 @@ function IRCClient_set_chanmode(chan,modeline,bounce_modes) { ...@@ -551,7 +549,10 @@ function IRCClient_set_chanmode(chan,modeline,bounce_modes) {
} }
function IRCClient_do_join(chan_name,join_key) { 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(); var uc_chan_name = chan_name.toUpperCase();
...@@ -567,7 +568,7 @@ function IRCClient_do_join(chan_name,join_key) { ...@@ -567,7 +568,7 @@ function IRCClient_do_join(chan_name,join_key) {
} }
if (this.channels[uc_chan_name]) if (this.channels[uc_chan_name])
return 0; 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."); this.numeric("405", chan_name + " :You have joined too many channels.");
return 0; return 0;
} }
...@@ -636,7 +637,7 @@ function IRCClient_do_join(chan_name,join_key) { ...@@ -636,7 +637,7 @@ function IRCClient_do_join(chan_name,join_key) {
if (chan_name[0] != "&") { if (chan_name[0] != "&") {
this.bcast_to_servers_raw( this.bcast_to_servers_raw(
format(":%s SJOIN %s %s %s :%s%s", format(":%s SJOIN %s %s %s :%s%s",
servername, ServerName,
chan.created, chan.created,
chan.nam, chan.nam,
chan.chanmode(), chan.chanmode(),
...@@ -684,11 +685,9 @@ function IRCClient_do_part(chan_name) { ...@@ -684,11 +685,9 @@ function IRCClient_do_part(chan_name) {
} }
function IRCClient_part_all() { function IRCClient_part_all() {
var partingChannel; var i;
for(thisChannel in this.channels) { for (i in this.channels) {
partingChannel=this.channels[thisChannel]; this.do_part(this.channels[i].nam);
this.do_part(partingChannel.nam);
} }
} }
...@@ -20,9 +20,11 @@ ...@@ -20,9 +20,11 @@
*/ */
function parse_nline_flags(flags) { function parse_nline_flags(flags) {
var i;
var nline_flags = 0; var nline_flags = 0;
for(thisflag in flags) {
switch(flags[thisflag]) { for (i in flags) {
switch(flags[i]) {
case "q": case "q":
nline_flags |= NLINE_CHECK_QWKPASSWD; nline_flags |= NLINE_CHECK_QWKPASSWD;
break; break;
...@@ -33,8 +35,10 @@ function parse_nline_flags(flags) { ...@@ -33,8 +35,10 @@ function parse_nline_flags(flags) {
nline_flags |= NLINE_CHECK_WITH_QWKMASTER; nline_flags |= NLINE_CHECK_WITH_QWKMASTER;
break; break;
default: default:
log(LOG_WARNING,"!WARNING Unknown N:Line flag '" log(LOG_WARNING,format(
+ flags[thisflag] + "' in config."); "!WARNING Unknown N:Line flag '%s' in config.",
flags[i]
));
break; break;
} }
} }
...@@ -42,9 +46,11 @@ function parse_nline_flags(flags) { ...@@ -42,9 +46,11 @@ function parse_nline_flags(flags) {
} }
function parse_oline_flags(flags) { function parse_oline_flags(flags) {
var i;
var oline_flags = 0; var oline_flags = 0;
for(thisflag in flags) {
switch(flags[thisflag]) { for (i in flags) {
switch(flags[i]) {
case "r": case "r":
oline_flags |= OLINE_CAN_REHASH; oline_flags |= OLINE_CAN_REHASH;
break; break;
...@@ -105,7 +111,7 @@ function parse_oline_flags(flags) { ...@@ -105,7 +111,7 @@ function parse_oline_flags(flags) {
break; break;
case "x": case "x":
case "X": case "X":
oline_flags |= OLINE_CAN_DEBUG; oline_flags |= OLINE_CAN_EVAL;
break; break;
case "O": case "O":
oline_flags |= OLINE_IS_GOPER; oline_flags |= OLINE_IS_GOPER;
...@@ -126,39 +132,50 @@ function parse_oline_flags(flags) { ...@@ -126,39 +132,50 @@ function parse_oline_flags(flags) {
oline_flags |= OLINE_CAN_UMODEC; oline_flags |= OLINE_CAN_UMODEC;
break; break;
default: default:
log(LOG_WARNING,"!WARNING Unknown O:Line flag '" log(LOG_WARNING,format(
+ flags[thisflag] + "' in config."); "!WARNING Unknown O:Line flag '%s' in config.",
flags[i]
));
break; break;
} }
} }
return oline_flags; return oline_flags;
} }
function read_config_file() { function Read_Config_File() {
var i;
/* All of these variables are global. */ /* All of these variables are global. */
Admin1 = ""; Admin1 = "";
Admin2 = ""; Admin2 = "";
Admin3 = ""; Admin3 = "";
CLines = new Array; if (typeof CLines !== 'undefined') {
HLines = new Array; for (i in CLines) {
ILines = new Array; if (CLines[i].next_connect)
KLines = new Array; js.clearTimeout(CLines[i].next_connect);
NLines = new Array; }
OLines = new Array; }
PLines = new Array; CLines = [];
QLines = new Array; HLines = [];
ULines = new Array; ILines = [];
diepass = ""; KLines = [];
restartpass = ""; NLines = [];
YLines = new Array; OLines = [];
ZLines = new Array; PLines = [];
QLines = [];
ULines = [];
YLines = [];
ZLines = [];
Die_Password = "";
Restart_Password = "";
/* End of global variables */ /* End of global variables */
var fname=""; var fname="";
if (config_filename && config_filename.length) { if (Config_Filename && Config_Filename.length) {
if(config_filename.indexOf('/')>=0 || config_filename.indexOf('\\')>=0) if(Config_Filename.indexOf('/')>=0 || Config_Filename.indexOf('\\')>=0)
fname=config_filename; fname=Config_Filename;
else else
fname=system.ctrl_dir + config_filename; fname=system.ctrl_dir + Config_Filename;
} else { } else {
fname=system.ctrl_dir + "ircd." + system.local_host_name + ".conf"; fname=system.ctrl_dir + "ircd." + system.local_host_name + ".conf";
if(!file_exists(fname)) if(!file_exists(fname))
...@@ -175,13 +192,13 @@ function read_config_file() { ...@@ -175,13 +192,13 @@ function read_config_file() {
read_conf_config(file_handle); read_conf_config(file_handle);
file_handle.close(); file_handle.close();
} else { } 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(); Time_Config_Read = time();
scan_for_klined_clients(); 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() { function ini_sections() {
...@@ -197,9 +214,8 @@ function ini_sections() { ...@@ -197,9 +214,8 @@ function ini_sections() {
} }
function ini_IRCdInfo(arg, ini) { function ini_IRCdInfo(arg, ini) {
log("ircdinfo " + ini.Hostname); ServerName=ini.Hostname;
servername=ini.Hostname; ServerDesc=ini.Info;
serverdesc=ini.Info;
Admin1=ini.Admin1; Admin1=ini.Admin1;
Admin2=ini.Admin2; Admin2=ini.Admin2;
Admin3=ini.Admin3; Admin3=ini.Admin3;
...@@ -207,7 +223,7 @@ function ini_IRCdInfo(arg, ini) { ...@@ -207,7 +223,7 @@ function ini_IRCdInfo(arg, ini) {
/* Former M:Line */ /* Former M:Line */
function ini_Port(arg, ini) { function ini_Port(arg, ini) {
mline_port = arg; Default_Port = arg;
} }
/* Former Y:Line */ /* Former Y:Line */
...@@ -256,21 +272,19 @@ function ini_Hub(arg, ini) { ...@@ -256,21 +272,19 @@ function ini_Hub(arg, ini) {
function read_ini_config(conf) { function read_ini_config(conf) {
var ini = conf.iniGetAllObjects(); var ini = conf.iniGetAllObjects();
var split;
var section_name;
var section_arg;
var Sections = new ini_sections(); var Sections = new ini_sections();
var i, s;
for each(var i in ini) { for (var i in ini) {
split = i.name.split(":"); s = i.name.split(":");
section_name = split[0]; if (typeof Sections[s[0]] === 'function')
section_arg = split[1]; Sections[s[0]](s[1], i);
if (typeof Sections[section_name] === 'function')
Sections[section_name](section_arg, i);
} }
} }
function read_conf_config(conf) { function read_conf_config(conf) {
var conf_line, arg, i;
function fancy_split(line) { function fancy_split(line) {
var ret = []; var ret = [];
var i; var i;
...@@ -309,21 +323,15 @@ function read_conf_config(conf) { ...@@ -309,21 +323,15 @@ function read_conf_config(conf) {
} }
while (!conf.eof) { while (!conf.eof) {
var conf_line = conf.readln(); conf_line = conf.readln();
if ((conf_line != null) && conf_line.match("[:]")) { if ((conf_line != null) && conf_line.match("[:]")) {
var arg = fancy_split(conf_line); arg = fancy_split(conf_line);
for(argument in arg) { for (i in arg) {
arg[argument]=arg[argument].replace( arg[i]=arg[i].replace(/SYSTEM_HOST_NAME/g,system.host_name);
/SYSTEM_HOST_NAME/g,system.host_name); arg[i]=arg[i].replace(/SYSTEM_NAME/g,system.name);
arg[argument]=arg[argument].replace( arg[i]=arg[i].replace(/VERSION_NOTICE/g,system.version_notice);
/SYSTEM_NAME/g,system.name); if (typeof system.qwk_id !== 'undefined')
if (typeof system.qwk_id !== "undefined") { arg[i]=arg[i].replace(/SYSTEM_QWKID/g,system.qwk_id.toLowerCase());
arg[argument]=arg[argument].replace(
/SYSTEM_QWKID/g,system.qwk_id.toLowerCase()
);
}
arg[argument]=arg[argument].replace(
/VERSION_NOTICE/g,system.version_notice);
} }
switch (conf_line[0].toUpperCase()) { switch (conf_line[0].toUpperCase()) {
case "A": case "A":
...@@ -336,8 +344,7 @@ function read_conf_config(conf) { ...@@ -336,8 +344,7 @@ function read_conf_config(conf) {
case "C": case "C":
if (!arg[5]) if (!arg[5])
break; break;
CLines.push(new CLine(arg[1],arg[2],arg[3],arg[4], CLines.push(new CLine(arg[1],arg[2],arg[3],arg[4],parseInt(arg[5])));
parseInt(arg[5]) ));
break; break;
case "H": case "H":
if (!arg[3]) if (!arg[3])
...@@ -355,8 +362,7 @@ function read_conf_config(conf) { ...@@ -355,8 +362,7 @@ function read_conf_config(conf) {
break; break;
var kline_mask = create_ban_mask(arg[1],true); var kline_mask = create_ban_mask(arg[1],true);
if (!kline_mask) { if (!kline_mask) {
log(LOG_WARNING,"!WARNING Invalid K:Line (" log(LOG_WARNING,"!WARNING Invalid K:Line (" + arg[1] + ")");
+ arg[1] + ")");
break; break;
} }
KLines.push(new KLine(kline_mask,arg[2],"K")); KLines.push(new KLine(kline_mask,arg[2],"K"));
...@@ -364,9 +370,9 @@ function read_conf_config(conf) { ...@@ -364,9 +370,9 @@ function read_conf_config(conf) {
case "M": case "M":
if (!arg[3]) if (!arg[3])
break; break;
servername = arg[1]; ServerName = arg[1];
serverdesc = arg[3]; ServerDesc = arg[3];
mline_port = parseInt(arg[4]); Default_Port = parseInt(arg[4]);
break; break;
case "N": case "N":
if (!arg[5]) if (!arg[5])
...@@ -394,8 +400,8 @@ function read_conf_config(conf) { ...@@ -394,8 +400,8 @@ function read_conf_config(conf) {
ULines.push(arg[1]); ULines.push(arg[1]);
break; break;
case "X": case "X":
diepass = arg[1]; Die_Password = arg[1];
restartpass = arg[2]; Restart_Password = arg[2];
break; break;
case "Y": case "Y":
if (!arg[5]) 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
// irclib.js
// This program is free software; you can redistribute it and/or modify
// 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
// it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or
// the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
// but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details:
// GNU General Public License for more details: https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
// http://www.gnu.org/licenses/gpl.txt
// A library of useful IRC functions and objects that can be used to assist
// 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.
// in the creation of IRC clients, bots, servers, or custom shells.
// If you use this to create something neat, let me know about it! :)
// 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'
// Either email, or find me on #synchronet, irc.synchro.net, nick 'Cyan'
// Copyright 2003-2021 Randy Sommerfeld <cyan@synchro.net>
// Copyright 2003-2006 Randolph Erwin Sommerfeld <sysop@rrx.ca> */
//
const IRCLIB_REVISION = "1.24";
const IRCLIB_REVISION = "$Revision: 1.23 $".split(' ')[1];
const IRCLIB_VERSION = "irclib.js-" + IRCLIB_REVISION; 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. // Connect to a server as a client.
// hostname Hostname to connect to // hostname Hostname to connect to
// nick Desired nickname // nick Desired nickname
...@@ -254,14 +372,14 @@ function IRC_split_nuh(str) { ...@@ -254,14 +372,14 @@ function IRC_split_nuh(str) {
/* Convert a dotted-quad IP address to an integer, i.e. for use in CTCP */ /* Convert a dotted-quad IP address to an integer, i.e. for use in CTCP */
function ip_to_int(ip) { function ip_to_int(ip) {
var quads;
if (!ip) if (!ip)
return 0; return 0;
var quads = ip.split(".");
var addr = (quads[0]&0xff)<<24; quads = ip.split(".");
addr|=(quads[1]&0xff)<<16;
addr|=(quads[2]&0xff)<<8; return quads[0] * 0x1000000 + quads[1] * 0x10000 + quads[2] * 0x100 + quads[3];
addr|=(quads[3]&0xff);
return addr;
} }
/* Convert an integer to an IP address, i.e. for receiving CTCP's */ /* Convert an integer to an IP address, i.e. for receiving CTCP's */
...@@ -271,7 +389,7 @@ function int_to_ip(ip) { ...@@ -271,7 +389,7 @@ function int_to_ip(ip) {
,(ip>>16)&0xff ,(ip>>16)&0xff
,(ip>>8)&0xff ,(ip>>8)&0xff
,ip&0xff ,ip&0xff
)); ));
} }
/* A handy object for keeping track of nicks and their properties */ /* A handy object for keeping track of nicks and their properties */
...@@ -295,3 +413,11 @@ function IRC_Server(name) { ...@@ -295,3 +413,11 @@ function IRC_Server(name) {
this.info = "Unknown Server"; 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.