| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532 | var net = require('net');var IRCserver = require('./server');var IRCuser = require('./user');var IRCchannel = require('./channel');var connection = null;var recvData = '';var users = [];var servers = [];var channels = [];var me = null;var events = {};function addInternalEvents(){	events.netSynced.unshift(function(){		for(var i=0; i<users.length; i++){			var user = users[i];			if(user.uplink == me){				irc.uplink.introduceUser(user);			}		}		for(var i=0; i<channels.length; i++){			irc.uplink.syncChannel(channels[i]);		}	});	events.newTagMessage.unshift(function(inputTags, newTags, msg){		if(!newTags){			var newTags = {};		}		if('label' in inputTags) newTags['label'] = inputTags['label']; // labeled-response		newTags['time'] = new Date().toISOString(); // server-time		newTags['msgid'] = randomID('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz', 22);		return [inputTags, newTags, msg];	});}function randomID(characters, length){	var result = '';	var charactersLength = characters.length;	for(var i=0; i<length; i++ ) {		result += characters.charAt(Math.floor(Math.random() * charactersLength));	}	return result;}function findServer(s){	for(var i=0; i<servers.length; i++){		srv = servers[i];		if(srv.sid == s || srv.name == s) return srv;	}	return null;}function findUser(u){	for(var i=0; i<users.length; i++){		usr = users[i];		if(usr.uid == u || usr.name == u) return usr;	}	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;	do {		for(var i=0; i<length; i++){			if(servers.length != length) break;			if(servers[i].uplink == server){				removeServer(servers[i]); // remove connected servers recursively			}		}		if(i == length) done = true;	} while(!done);	for(var i=users.length-1; i>=0; i--){		if(users[i].uplink == server){			quitUser(users[i]); // remove users connected to this server		}	}	for(var i=servers.length-1; i>=0; i--){ // finally, remove the given server		if(servers[i] == server){			console.log('Removing server '+servers[i].name);			events.doEvent('serverDelete', server);			servers.splice(i, 1);			delete server;			break;		}	}}function getChannel(name){	var lowerName = name.toLowerCase();	var channel = null;	for(var i=0; i<channels.length; i++){		if(channels[i].name == lowerName){			channel = channels[i];			break;		}	}	if(!channel){		console.log('Creating channel '+name);		channel = new IRCchannel;		channel.setEvents(events);		channel.name = name;		channels.push(channel);		events.doEvent('channelCreate', channel);	}	return channel;}function killUser(user){	console.log('KILL');	console.log(user);// TODO}function newUser(nick, distance, TS, ident, host, uid, account, umodes, vhost, cloakedHost, ip, realname, uplink){ // nick, distance, TS, ident, host, uid, account, umodes, vhost, cloakedHost, ip, realname, uplink	var user;	if((user = findUser(uid)) || (user = findUser(nick))){		console.log(user);		throw 'User already exists';	}	console.log('Introducing user '+nick);	user = new IRCuser;	user.setEvents(events);	user.introduce(nick, distance, TS, ident, host, uid, account, umodes, vhost, cloakedHost, ip, realname, uplink);	users.push(user);	events.doEvent('userCreate', user);	return user;}function newServer(name, sid, desc, distance, uplink){	if(findServer(sid) || findServer(name)){		throw 'Server already exists';	}	console.log('Introducing server '+name+' (SID='+sid+', uplink="'+uplink.name+'")');	server = new IRCserver;	server.setEvents(events);	server.introduce(name, sid, desc, distance, uplink);	servers.push(server);	events.doEvent('serverCreate', server);	return server;}function ircSendData(tags, from, cmd, args){	var data = makeTagsString(tags);	if(from){		data += ':' + from + ' ';	}	data += cmd;	if(args) for(var i=0; i<args.length; i++){		var arg = args[i];		if(arg.indexOf(' ') >= 0){ // valid for last arg only			arg = ':' + arg;		}		data += ' ' + arg;	}	console.log('<<< '+data);	data += "\r\n";	connection.write(data);}function ircDataReceived(data){	data = data.toString('utf8');	recvData += data;	var buffer = '';	for(var i=0; i<recvData.length; i++){		var c = recvData.charAt(i);		switch(c){			default: buffer += c; break;			case '\r': case '\n':				if(buffer.length > 0)					ircMessage(buffer);				buffer = '';				break;		}					}	recvData = buffer;}function makeTagsString(tags){	if(!irc.uplink.supportsTags) return '';	if(!tags) return '';	var tagText = '';	for(name in tags){		var value = tags[name].replace(/;/g, '\\:').replace(/ /g, '\\s').replace(/\\/g, '\\\\').replace(/\n/g, '\\n').replace(/\r/g, '\\r');		if(tagText.length == 0){			tagText += '@';		} else {			tagText += ';';		}		tagText += name;		if(value.length > 0){			tagText += '=' + value;		}	}	if(tagText.length > 0){		tagText += ' ';	}	return tagText;}function ircMessage(data){	var msg = irc.parseLine(data);	irc.uplink.processMessage(msg);}function ircConnectionClosed(e){	events.doEvent('uplinkLost', me);	console.log('Closed: '+e);	process.exit(1);}function processReplyTags(inputTags, newTags, msg){	newTags = events.doEvent('newTagMessage', inputTags, newTags, msg);	return newTags[1];}function newTags(newTags){	return events.doEvent('newTagMessage', {}, newTags, null)[1];}function quitUser(user){	if(user.uplink == me){ // out bot was killed?		console.log('Tried to kill ' + user.name + '!');		irc.uplink.introduceUser(user); // re-introduce		for(var i=0; i<user.channels.length; i++){			irc.uplink.joinChannel(user, user.channels[i]);		}		return;	}	console.log('Removing user '+user.name);	for(var i=0; i<channels.length; i++){		channels[i].removeUser(user);	}	for(var i=0; i<users.length; i++){		if(users[i] == user){			users.splice(i, 1);			events.doEvent('userDelete', user);			delete user;			break;		}	}}function removeEmptyChannels(){	for(var i=channels.length - 1; i >= 0; i--){		if(channels[i].users.length == 0){			storeChannelData(channels[i]);			console.log('Removing channel '+channels[i].name);			var channel = channels[i];			channels.splice(i, 1);			delete channel;		}	}}function storeChannelData(channel){// TODO maybe move a level up}setInterval(removeEmptyChannels, 1000);var irc = {	'users': users,	'channels': channels,	'servers': servers,	'getChannel': getChannel,	'uplink': null,	'setConnection': function(host, port, protocol){		irc.uplink = require('./protocol/' + protocol);		me = new IRCserver;		me.setEvents(events);		me.introduce('serwisy.pirc.pl', '11K', 'Serwisy', 0, null);		servers.push(me);		irc.uplink.setHandlers({			send: ircSendData,			findServer: findServer,			findUser: findUser,			killUser: killUser,			newServer: newServer,			newUser: newUser,			getChannel: getChannel,			quitUser: quitUser,			removeServer: removeServer,			findChannel: findChannel,			randomID: randomID,			newTags: newTags		});		irc.uplink.setSettings({			ID: me.sid,			password: 'myservicespassword',			name: me.name,			description: me.description,			version: 'k4be-services',			me: me,			maxUsers: 0		});		addInternalEvents();		irc.uplink.setEvents(events);		connection = new net.Socket();		connection.connect(port, host, irc.uplink.connected.bind(this));		connection.on('data', ircDataReceived);		connection.on('close', ircConnectionClosed);	},	'setEvents': function(newEvents){		events = newEvents;	},	'makeUser': function(nick, ident, host, realname){		var user = new IRCuser;		user.setEvents(events);		var uid = irc.uplink.makeUid();		var umodes = { 'SERVICEUSER': true, 'BOT': true, 'OPER': true }; // TODO make it configurable		user.introduce(nick, 0, Math.floor(new Date() / 1000).toString(10), ident, host, uid, null, umodes, null, null, null, realname, me);		users.push(user);		return user;	},	'messagedata': function() {		this.text = '';		this.args = [];		this.tags = [];		this.command = '';		this.sender = {			'nick': '',			'ident': '',			'host': '',			'server': false,			'user': false		};		this.time = new Date();		this.reply = function(tags, cmd, args){			var outTags = processReplyTags(this.tags, tags, this);			ircSendData(outTags, this.sender.nick, cmd, args);		}		this.originalString = '';	},	'parseTags': function(tagsLine){		var tags = [];		var tagState = 'keyName';		var keyValue;		var keyName = '';		for(var i = 0; i < tagsLine.length; i++){			var cchar = tagsLine.charAt(i);			switch(tagState){				case 'keyName':					switch(cchar){						case '=':							tagState = 'keyValue';							keyValue = '';							break;						case ';':							tags[keyName] = '';							keyName = ''; // staying in tagStateKeyName							break;						default: keyName += cchar; break;					}					break;				case 'keyValue':					switch(cchar){						case '\\': tagState = 'keyValueEscape'; break;						case ';':							tags[keyName] = keyValue;							keyName = '';							tagState = 'keyName';							break;						default: keyValue += cchar; break;					}					break;				case 'keyValueEscape':					switch(cchar){						case ':': keyValue += ';'; break;						case 's': keyValue += ' '; break;						case 'r': keyValue += '\r'; break;						case 'n': keyValue += '\n'; break;						default: keyValue += cchar; break;					}					tagState = 'keyValue';					break;			}		}		if(keyName.length > 0) tags[keyName] = keyValue; // flush last tag		return tags;	},	'parseLine': function(line){		var ircmsg = new irc.messagedata();		var line = line.trim();		line.replace(/^\s+|\s+$/gm,'');				if(line == ''){			return;		}				ircmsg.originalString = line;				var msglen = line.length;			var pstate = 'start';		var currArg = '';		var tags = '';		var haveText = false;				console.log('>>> ' + line);		for(var i = 0; i < msglen; i++){			var cchar = line.charAt(i);			switch(pstate){				case 'start':					switch(cchar){						case '@': pstate = 'tags'; break;						case ':': pstate = 'senderNick'; break;						default:							pstate = 'command';							ircmsg.command += cchar;							break;					}					break;				case 'tags':					switch(cchar){						case ' ':							pstate = 'start';							ircmsg.tags = irc.parseTags(tags);							break;						default: tags += cchar; break;					}					break;				case 'senderNick':					switch(cchar){						case '!': pstate = 'senderUser'; break;						case '@': pstate = 'senderHost'; break;						case ' ': pstate = 'command'; break;						default: ircmsg.sender.nick += cchar; break;					}					break;				case 'senderUser':					switch(cchar){						case '@': pstate = 'senderHost'; break;						case ' ': pstate = 'command'; break;						default: ircmsg.sender.ident += cchar; break;					}					break;				case 'senderHost':					switch(cchar){						case ' ': pstate = 'command'; break;						default: ircmsg.sender.host += cchar; break;					}					break;				case 'command':					switch(cchar){						case ' ': pstate = 'args'; break;						default: ircmsg.command += cchar; break;					}					break;				case 'args':					switch(cchar){						case ' ':							if(currArg != ''){								ircmsg.args.push(currArg);							}							currArg = '';							break;						case ':':							if(prevChar == ' '){								pstate = 'message';								haveText = true;							} else {								currArg += cchar;							}							break;						default: currArg += cchar; break;					}					break;				case 'message':					ircmsg.text += cchar;					break;			}			var prevChar = cchar;		}		if(pstate == 'args'){			ircmsg.args.push(currArg);		}				ircmsg.sender = irc.uplink.processSender(ircmsg.sender);		if(!ircmsg.sender){			return false;		}				if(!ircmsg.sender.server && !ircmsg.sender.user){			if(ircmsg.sender.ident == '' && ircmsg.sender.host == '' && ircmsg.sender.nick.indexOf('.')!=-1){				ircmsg.sender.server = true;			} else {				ircmsg.sender.user = true;			}		}		if(!haveText){			ircmsg.text = ircmsg.args[ircmsg.args.length-1]; // handling last argument as text if : is missing		} else {			ircmsg.args.push(ircmsg.text); // handling text as a last argument as required by the protocol		}// add u@h/*		if(ircmsg.sender.user){			if(ircmsg.sender.ident) users.getUser(ircmsg.sender.nick).setIdent(ircmsg.sender.ident);			if(ircmsg.sender.host) users.getUser(ircmsg.sender.nick).setHost(ircmsg.sender.host);		}*/// process known tags/*		if('time' in ircmsg.tags){			ircmsg.time = parseISOString(ircmsg.tags['time']);		}		if('account' in ircmsg.tags){			users.getUser(ircmsg.sender.nick).setAccount(ircmsg.tags['account']);		}*///		console.log(ircmsg);		return ircmsg;	}};module.exports = irc;
 |