Parcourir la source

Handle channel status modes and list modes.

k4be il y a 5 ans
Parent
commit
4c65ab12f5
4 fichiers modifiés avec 221 ajouts et 138 suppressions
  1. 37 27
      channel.js
  2. 38 29
      irc.js
  3. 146 74
      protocol/unrealircd.js
  4. 0 8
      server.js

+ 37 - 27
channel.js

@@ -2,47 +2,57 @@ var channel = function(){
 	this.name = null;
 	this.TS = null;
 	this.topic = null;
-	this.modes = {};
-	this.statusModes = {};
+	this.modes = {}; // indexed by mode name
+	this.statusModes = {}; // indexed by UID
+	this.listModes = {}; // indexed by mode name
 	this.users = [];
 
 	this.setTS = function(TS){
 		this.TS = TS;
 	};
 	
-	this.addModes = function(modes){
-		console.log(modes);
-		// TODO
+	this.changeModes = function(modes){
+		for(var mode in modes){
+			if(mode == 'status'){
+				for(var i=0; i<modes[mode].length; i++){
+					var statusMode = modes[mode][i];
+					this.statusModes[statusMode.user.uid][statusMode.name] = statusMode.status;
+					console.log((statusMode.status?'Setting':'Unsetting')+' '+statusMode.name+' for '+statusMode.user.name+' on '+this.name);
+				}
+			} else if(mode == 'list'){
+				for(var i=0; i<modes[mode].length; i++){
+					var listMode = modes[mode][i];
+					if(!(listMode.name in this.listModes)){
+						this.listModes[listMode.name] = [];
+					}
+					
+					if(listMode.status){ // add
+						if(this.listModes[listMode.name].indexOf(listMode.value) < 0){
+							this.listModes[listMode.name].push(listMode.value);
+						}
+					} else { // remove
+						for(var j=this.listModes[listMode.name].length-1; j >= 0; j--){
+							if(this.listModes[listMode.name][j] == listMode.value){
+								this.listModes[listMode.name].splice(j, 1);
+							}
+						}
+					}
+				}
+			} else {
+				this.modes[mode] = modes[mode];
+			}
+		}
 	};
 	
 	this.joinUser = function(user){
 		if(this.users.indexOf(user) >= 0) return;
 		this.users.push(user);
-	};
-	
-	this.setStatusModes = function(user, modes){
-		console.log(user);
-		console.log(modes);
-		this.statusModes[user.uid] = modes;
-		// TODO
-	};
-	
-	this.addBan = function(ban){
-		// TODO
-	};
-	
-	this.addExcept = function(except){
-		// TODO
-	};
-	
-	this.addInvex = function(invex){
-		// TODO
+		this.statusModes[user.uid] = {};
 	};
 	
 	this.removeUser = function(user){
-		var index = this.statusModes.indexOf(user);
-		if(index >= 0){
-			this.statusModes.splice(index, 1); // remove status modes for this user
+		if(user.uid in this.statusModes){
+			delete this.statusModes[user.uid]; // remove status modes for this user
 		}
 		for(var i=0; i<this.users.length; i++){
 			if(this.users[i] == user){

+ 38 - 29
irc.js

@@ -28,6 +28,14 @@ function findUser(u){
 	return null;
 }
 
+function findChannel(c){
+	for(var i=0; i<channels.length; i++){
+		usr = channels[i];
+		if(channels[i].name == c) return channels[i];
+	}
+	return null;
+}
+
 function removeServer(server){
 	var length = servers.length;
 	var done = false;
@@ -195,35 +203,36 @@ setInterval(removeEmptyChannels, 1000);
 
 var irc = {
 	'setConnection': function(host, port, protocol){
-	uplink = require('./protocol/' + protocol);
-	me = new IRCserver;
-	me.introduce('serwisy.pirc.pl', '11K', 'Serwisy', 0, null);
-	servers.push(me);
-	uplink.setHandlers({
-		send: ircSendData,
-		findServer: findServer,
-		findUser: findUser,
-		killUser: killUser,
-		newServer: newServer,
-		newUser: newUser,
-		parseUmodes: parseUmodes,
-		getChannel: getChannel,
-		quitUser: quitUser,
-		removeServer: removeServer
-	});
-	uplink.setSettings({
-		ID: me.sid,
-		password: 'myservicespassword',
-		name: me.name,
-		description: me.description,
-		version: 'k4be-services',
-		me: me,
-		maxUsers: 0
-	});
-	connection = new net.Socket();
-	connection.connect(port, host, uplink.connected.bind(this));
-	connection.on('data', ircDataReceived);
-	connection.on('close', ircConnectionClosed);
+		uplink = require('./protocol/' + protocol);
+		me = new IRCserver;
+		me.introduce('serwisy.pirc.pl', '11K', 'Serwisy', 0, null);
+		servers.push(me);
+		uplink.setHandlers({
+			send: ircSendData,
+			findServer: findServer,
+			findUser: findUser,
+			killUser: killUser,
+			newServer: newServer,
+			newUser: newUser,
+			parseUmodes: parseUmodes,
+			getChannel: getChannel,
+			quitUser: quitUser,
+			removeServer: removeServer,
+			findChannel: findChannel
+		});
+		uplink.setSettings({
+			ID: me.sid,
+			password: 'myservicespassword',
+			name: me.name,
+			description: me.description,
+			version: 'k4be-services',
+			me: me,
+			maxUsers: 0
+		});
+		connection = new net.Socket();
+		connection.connect(port, host, uplink.connected.bind(this));
+		connection.on('data', ircDataReceived);
+		connection.on('close', ircConnectionClosed);
 	},
 	'messagedata': function() {
 		this.text = '';

+ 146 - 74
protocol/unrealircd.js

@@ -1,8 +1,46 @@
-module.exports.connected = connected;
-module.exports.processMessage = processMessage;
-module.exports.processSender = processSender;
-module.exports.setHandlers = setHandlers;
-module.exports.setSettings = setSettings;
+var protocol = {
+	connected: function(){
+		ircSend(null, settings.ID, 'PASS', [settings.password]);
+		ircSend(null, settings.ID, 'PROTOCTL', ['NICKv2', 'VHP', 'UMODE2', 'NICKIP', 'SJOIN', 'SJOIN2', 'SJ3', 'NOQUIT', 'TKLEXT', 'MLOCK', 'SID', 'MTAGS']);
+		ircSend(null, settings.ID, 'PROTOCTL', ['EAUTH=' + settings.name + ',,,' + settings.version]);
+		ircSend(null, settings.ID, 'PROTOCTL', ['SID=' + settings.ID]);
+		ircSend(null, settings.ID, 'SERVER', [settings.name, '1', settings.description]);
+	},
+	processMessage: function(msg){
+		if(!msg) return;
+		if(msg.command in cmdBinds){
+			cmdBinds[msg.command](msg);
+		} else {
+			console.log('Unhandled cmd: '+msg.command);
+			console.log(msg);
+		}
+	},
+	processSender: function(sender){
+		if(sender.nick){
+			if(server = handlers.findServer(sender.nick)){
+				sender.nick = server.name;
+				sender.server = server;
+			} else if(user = handlers.findUser(sender.nick)){
+				sender.nick = user.name
+				sender.ident = user.ident;
+				sender.host = user.host;
+				sender.user = user;
+			} else {
+				handlers.killUser(sender.nick);
+				return false;
+			}
+		}
+		return sender;
+	},
+	setHandlers: function(newHandlers){
+		handlers = newHandlers;
+	},
+	setSettings: function(newSettings){
+		settings = newSettings;
+	}
+}
+
+module.exports = protocol;
 
 var handlers = {
 	sendHandler: null,
@@ -13,6 +51,7 @@ var handlers = {
 	newUser: null,
 	parseUmodes: null,
 	getChannel: null,
+	findChannel: null,
 	quitUser: null,
 	removeServer: null
 };
@@ -30,11 +69,20 @@ var settings = {
 var connection = {
 	'protoctl': {},
 	'prefixes': {},
+	'statusmodes': [],
 	'chmodes': [], // 0 - list modes, 1 - argument modes, 2 - argument modes (add only), 3 - standard modes
 	'umodes': {}
 };
 
 var chmodeMap = {
+	'b': { 'name': 'BAN', 'user': true, 'services': true },
+	'e': { 'name': 'EXCEPT', 'user': true, 'services': true },
+	'I': { 'name': 'INVEX', 'user': true, 'services': true },
+	'v': { 'name': 'VOICE', 'user': true, 'services': true },
+	'h': { 'name': 'HALFOP', 'user': true, 'services': true },
+	'o': { 'name': 'OP', 'user': true, 'services': true },
+	'a': { 'name': 'PROTECT', 'user': true, 'services': true },
+	'q': { 'name': 'OWNER', 'user': true, 'services': true },
 	'k': { 'name': 'KEY', 'user': true, 'services': true },
 	'L': { 'name': 'CHANNELLINK', 'user': true, 'services': true },
 	'f': { 'name': 'FLOODPROT', 'user': true, 'services': true },
@@ -70,6 +118,8 @@ function parseChannelModes(modes, args){
 	var plus = true;
 	var argIndex = 0;
 	var output = {};
+	output['status'] = [];
+	output['list'] = [];
 	for(var i=0; i<modes.length; i++){
 		var c = modes.charAt(i);
 		if(c in chmodeMap){
@@ -82,7 +132,7 @@ function parseChannelModes(modes, args){
 		} else if(c == '-'){
 			plus = false;
 		} else if(connection.chmodes[0].indexOf(c) >= 0){
-			throw 'Unexpected list mode';
+			output['list'].push({ 'name': name, 'value': args[argIndex++], 'status': plus });
 		} else if(connection.chmodes[1].indexOf(c) >= 0){
 			if(plus){
 				output[name] = args[argIndex++];
@@ -96,72 +146,62 @@ function parseChannelModes(modes, args){
 			} else {
 				output[name] = false;
 			}
+		} else if(connection.statusmodes.indexOf(c) >= 0){
+			var user = handlers.findUser(args[argIndex++]);
+			if(user){
+				console.log('Setting status mode for '+user.name);
+				output['status'].push({ 'name': name, 'user': user, 'status': plus });
+			} else {
+				throw 'User not found';
+			}
 		} else { // treat unknown chars as type 3
+			if(connection.chmodes[3].indexOf(c) < 0){
+				console.log('Unknown mode '+name);
+			}
 			output[name] = plus;
 		}
 	}
+	console.log(output);
 	return output;
 }
 
 function parseModePrefix(text){
-	var modes = [];
-	for(var i=0; i<text.length; i++){
-		var c = text.charAt(i);
-		if(c in connection.prefixes){
-			modes[connection.prefixes[c]] = true;
-		} else break;
-	}
-	var uid = text.substring(i);
-	var user = handlers.findUser(uid);
-	if(!user){
-		handlers.killUser(uid);
-		return null;
-	}
-	return { 'user': user, 'modes': modes };
-}
-
-function connected(){
-	ircSend(null, settings.ID, 'PASS', [settings.password]);
-	ircSend(null, settings.ID, 'PROTOCTL', ['NICKv2', 'VHP', 'UMODE2', 'NICKIP', 'SJOIN', 'SJOIN2', 'SJ3', 'NOQUIT', 'TKLEXT', 'MLOCK', 'SID', 'MTAGS']);
-	ircSend(null, settings.ID, 'PROTOCTL', ['EAUTH=' + settings.name + ',,,' + settings.version]);
-	ircSend(null, settings.ID, 'PROTOCTL', ['SID=' + settings.ID]);
-	ircSend(null, settings.ID, 'SERVER', [settings.name, '1', settings.description]);
-}
-
-function processSender(sender){
-	if(sender.nick){
-		if(server = handlers.findServer(sender.nick)){
-			sender.nick = server.name;
-			sender.server = server;
-		} else if(user = handlers.findUser(sender.nick)){
-			sender.nick = user.name
-			sender.ident = user.ident;
-			sender.host = user.host;
-			sender.user = user;
-		} else {
-			handlers.killUser(sender.nick);
-			return false;
+	var modes = {'status': [], 'list': [] };
+	var isUser = false;
+	var c = text.charAt(0);
+	if(c == '&'){ // +b
+		modes.list.push({ 'name': 'BAN', 'value': text.substring(1), 'status': true });
+	} else if(c == '"'){ // +e
+		modes.list.push({ 'name': 'EXCEPT', 'value': text.substring(1), 'status': true });
+	} else if(c == '\''){ // +I
+		modes.list.push({ 'name': 'INVEX', 'value': text.substring(1), 'status': true });
+	} else {
+		isUser = true;
+		for(var i=0; i<text.length; i++){
+			c = text.charAt(i);
+			if(c in connection.prefixes){
+				modes.status.push({ 'name': chmodeMap[connection.prefixes[c]].name, 'user': false /* setting it later */, 'status': true });
+				// can be more than 1
+			} else {
+				break;
+			}
 		}
 	}
-	return sender;
-}
 
-function processMessage(msg){
-	if(!msg) return;
-	if(msg.command in cmdBinds){
-		cmdBinds[msg.command](msg);
-	} else {
-		console.log('Unhandled cmd: '+msg.command);
-		console.log(msg);
+	var user = null;
+	if(isUser){
+		var uid = text.substring(i);
+		user = handlers.findUser(uid);
+		if(!user){
+			handlers.killUser(uid);
+			modes.status = [];
+		}
+		for(var i=0; i<modes.status.length; i++){
+			modes.status[i].user = user;
+		}
 	}
-}
 
-function setHandlers(newHandlers){
-	handlers = newHandlers;
-}
-
-function setSettings(newSettings){
-	settings = newSettings;
+	return { 'modes': modes, 'user': user };
 }
 
 function ircSend(tags, from, cmd, args){
@@ -182,6 +222,7 @@ var cmdBinds = {
 						var chars = data[1].split('');
 						for(var i=0; i<modes.length; i++){
 							connection.prefixes[chars[i]] = modes[i];
+							connection.statusmodes.push(modes[i]);
 						}
 						break;
 					case 'CHANMODES':
@@ -229,6 +270,7 @@ var cmdBinds = {
 	},
 	
 	'EOS': function(msg){ // ?
+		console.log(connection);
 	},
 	
 	'SINFO': function(msg){
@@ -283,28 +325,23 @@ var cmdBinds = {
 	'SJOIN': function(msg){
 		var channel = handlers.getChannel(msg.args[1]);
 		channel.setTS(msg.args[0]);
-		var modeArgs = [];
-		for(var i=3; i<msg.args.length - 1; i++){
-			modeArgs.push(msg.args[i]);
+		if(msg.args.length > 3){
+			var modeArgs = [];
+			for(var i=3; i<msg.args.length - 1; i++){
+				modeArgs.push(msg.args[i]);
+			}
+			channel.changeModes(parseChannelModes(msg.args[2], modeArgs));
 		}
-		channel.addModes(parseChannelModes(msg.args[2], modeArgs));
 		var members = msg.text.split(' ');
 		for(var i=0; i<members.length; i++){
 			var member = members[i];
 			var c = member.charAt(0);
-			if(c == '&'){ // +b
-				channel.addBan(member.substring(1));
-			} else if(c == '"'){ // +e
-				channel.addExcept(member.substring(1));
-			} else if(c == '\''){ // +I
-				channel.addInvex(member.substring(1));
-			} else {
-				var udata = parseModePrefix(member);
-				if(!udata) return;
+			var udata = parseModePrefix(member);
+			if(udata.user){
 				console.log(udata.user.name + ' joined ' + msg.args[1]);
 				channel.joinUser(udata.user);
-				channel.setStatusModes(udata.user, udata.modes);
 			}
+			channel.changeModes(udata.modes);
 		}
 	},
 	
@@ -318,8 +355,43 @@ var cmdBinds = {
 		console.log(msg);
 		//TODO
 	},
-	
+/*messagedata {
+  text: '1584206946',
+  args: [ '#qwer', '+nt', '1584206946' ],
+  tags: [
+    time: '2020-03-14T17:29:07.017Z',
+    msgid: '0AnsVOjKaaFGxoaANiChIo-Uqo0dqr9WjGPgUEgGzdr3w'
+  ],
+  command: 'MODE',
+  sender: {
+    nick: 'test3.pirc.pl',
+    ident: '',
+    host: '',
+    server: server {
+      name: 'test3.pirc.pl',
+      sid: '143',
+      description: 'serwer testowy!',
+      distance: '1',
+      uplink: [server],
+      introduce: [Function]
+    },
+    user: false
+  },
+  time: 2020-03-14T17:29:07.021Z,
+  reply: [Function],
+  originalString: '@time=2020-03-14T17:29:07.017Z;msgid=0AnsVOjKaaFGxoaANiChIo-Uqo0dqr9WjGPgUEgGzdr3w :143 MODE #qwer +nt  1584206946'
+}
+*/
 	'MODE': function(msg){
+		var channel = handlers.findChannel(msg.args[0]);
+		if(channel){
+			var modeArgs = [];
+			for(var i=2; i<msg.args.length; i++){
+				modeArgs.push(msg.args[i]);
+			}
+			var chmodes = parseChannelModes(msg.args[1], modeArgs);
+			channel.changeModes(chmodes);
+		}
 		console.log(msg);
 		//TODO
 	},

+ 0 - 8
server.js

@@ -1,11 +1,3 @@
-/*module.exports.introduce = introduce;
-module.exports.getSid = function(){ return sid; };
-module.exports.getName = function(){ return name; };
-module.exports.getDescription = function(){ return description; };
-module.exports.getDistance = function(){ return distance; };
-module.exports.getUplink = function(){ return uplink; };
-module.exports.isServer = true;
-module.exports.isUser = false;*/
 
 var server = function(){
 	this.name = null;