irc.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. var net = require('net');
  2. var IRCserver = require('./server');
  3. var IRCuser = require('./user');
  4. var IRCchannel = require('./channel');
  5. var uplink = null;
  6. var connection = null;
  7. var recvData = '';
  8. var users = [];
  9. var servers = [];
  10. var channels = [];
  11. var me = null;
  12. function findServer(s){
  13. for(var i=0; i<servers.length; i++){
  14. srv = servers[i];
  15. if(srv.sid == s || srv.name == s) return srv;
  16. }
  17. return null;
  18. }
  19. function findUser(u){
  20. for(var i=0; i<users.length; i++){
  21. usr = users[i];
  22. if(usr.uid == u || usr.name == u) return usr;
  23. }
  24. return null;
  25. }
  26. function removeServer(server){
  27. var length = servers.length;
  28. var done = false;
  29. do {
  30. for(var i=0; i<length; i++){
  31. if(servers.length != length) break;
  32. if(servers[i].uplink == server){
  33. removeServer(servers[i]); // remove connected servers recursively
  34. }
  35. }
  36. if(i == length) done = true;
  37. } while(!done);
  38. for(var i=users.length-1; i>=0; i--){
  39. if(users[i].uplink == server){
  40. quitUser(users[i]); // remove users connected to this server
  41. }
  42. }
  43. for(var i=servers.length-1; i>=0; i--){ // finally, remove the given server
  44. if(servers[i] == server){
  45. console.log('Removing server '+servers[i].name);
  46. servers.splice(i, 1);
  47. delete server;
  48. break;
  49. }
  50. }
  51. }
  52. function getChannel(name){
  53. var lowerName = name.toLowerCase();
  54. var channel = null;
  55. for(var i=0; i<channels.length; i++){
  56. if(channels[i].name == lowerName){
  57. channel = channels[i];
  58. break;
  59. }
  60. }
  61. if(!channel){
  62. console.log('Creating channel '+name);
  63. channel = new IRCchannel;
  64. channel.name = name;
  65. channels.push(channel);
  66. }
  67. return channel;
  68. }
  69. function killUser(user){
  70. console.log('KILL');
  71. console.log(user);
  72. // TODO
  73. }
  74. 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
  75. var user;
  76. if((user = findUser(uid)) || (user = findUser(nick))){
  77. console.log(user);
  78. throw 'User already exists';
  79. }
  80. console.log('Introducing user '+nick);
  81. user = new IRCuser;
  82. user.introduce(nick, distance, TS, ident, host, uid, account, umodes, vhost, cloakedHost, ip, realname, uplink);
  83. users.push(user);
  84. }
  85. function newServer(name, sid, desc, distance, uplink){
  86. if(findServer(sid) || findServer(name)){
  87. throw 'Server already exists';
  88. }
  89. console.log('Introducing server '+name+' (SID='+sid+', uplink="'+uplink.name+'")');
  90. server = new IRCserver;
  91. server.introduce(name, sid, desc, distance, uplink);
  92. servers.push(server);
  93. }
  94. function parseUmodes(text){
  95. return null;
  96. // TODO
  97. }
  98. function ircSendData(tags, from, cmd, args){
  99. var data = makeTagsString(tags);
  100. if(from){
  101. data += ':' + from + ' ';
  102. }
  103. data += cmd;
  104. if(args) for(var i=0; i<args.length; i++){
  105. var arg = args[i];
  106. if(arg.indexOf(' ') >= 0){ // valid for last arg only
  107. arg = ':' + arg;
  108. }
  109. data += ' ' + arg;
  110. }
  111. console.log('<<< '+data);
  112. data += "\r\n";
  113. connection.write(data);
  114. }
  115. function ircDataReceived(data){
  116. data = data.toString('utf8');
  117. recvData += data;
  118. var buffer = '';
  119. for(var i=0; i<recvData.length; i++){
  120. var c = recvData.charAt(i);
  121. switch(c){
  122. default: buffer += c; break;
  123. case '\r': case '\n':
  124. if(buffer.length > 0)
  125. ircMessage(buffer);
  126. buffer = '';
  127. break;
  128. }
  129. }
  130. recvData = buffer;
  131. }
  132. function makeTagsString(tags){
  133. return '';
  134. }
  135. function ircMessage(data){
  136. var msg = irc.parseLine(data);
  137. uplink.processMessage(msg);
  138. }
  139. function ircConnectionClosed(e){
  140. console.log('Closed: '+e);
  141. process.exit(1);
  142. }
  143. function processReplyTags(inputTags, newTags){
  144. return newTags;
  145. // TODO
  146. }
  147. function quitUser(user){
  148. console.log('Removing user '+user.name);
  149. for(var i=0; i<channels.length; i++){
  150. channels[i].removeUser(user);
  151. }
  152. for(var i=0; i<users.length; i++){
  153. if(users[i] == user){
  154. users.splice(i, 1);
  155. delete user;
  156. break;
  157. }
  158. }
  159. }
  160. function removeEmptyChannels(){
  161. for(var i=channels.length - 1; i >= 0; i--){
  162. if(channels[i].users.length == 0){
  163. storeChannelData(channels[i]);
  164. console.log('Removing channel '+channels[i].name);
  165. var channel = channels[i];
  166. channels.splice(i, 1);
  167. delete channel;
  168. }
  169. }
  170. }
  171. function storeChannelData(channel){
  172. // TODO maybe move a level up
  173. }
  174. setInterval(removeEmptyChannels, 1000);
  175. var irc = {
  176. 'setConnection': function(host, port, protocol){
  177. uplink = require('./protocol/' + protocol);
  178. me = new IRCserver;
  179. me.introduce('serwisy.pirc.pl', '11K', 'Serwisy', 0, null);
  180. servers.push(me);
  181. uplink.setHandlers({
  182. send: ircSendData,
  183. findServer: findServer,
  184. findUser: findUser,
  185. killUser: killUser,
  186. newServer: newServer,
  187. newUser: newUser,
  188. parseUmodes: parseUmodes,
  189. getChannel: getChannel,
  190. quitUser: quitUser,
  191. removeServer: removeServer
  192. });
  193. uplink.setSettings({
  194. ID: me.sid,
  195. password: 'myservicespassword',
  196. name: me.name,
  197. description: me.description,
  198. version: 'k4be-services',
  199. me: me,
  200. maxUsers: 0
  201. });
  202. connection = new net.Socket();
  203. connection.connect(port, host, uplink.connected.bind(this));
  204. connection.on('data', ircDataReceived);
  205. connection.on('close', ircConnectionClosed);
  206. },
  207. 'messagedata': function() {
  208. this.text = '';
  209. this.args = [];
  210. this.tags = [];
  211. this.command = '';
  212. this.sender = {
  213. 'nick': '',
  214. 'ident': '',
  215. 'host': '',
  216. 'server': false,
  217. 'user': false
  218. };
  219. this.time = new Date();
  220. this.reply = function(tags, cmd, args){
  221. var outTags = processReplyTags(this.tags, tags);
  222. ircSendData(outTags, this.sender.nick, cmd, args);
  223. }
  224. this.originalString = '';
  225. },
  226. 'parseTags': function(tagsLine){
  227. var tags = [];
  228. var tagState = 'keyName';
  229. var keyValue;
  230. var keyName = '';
  231. for(var i = 0; i < tagsLine.length; i++){
  232. var cchar = tagsLine.charAt(i);
  233. switch(tagState){
  234. case 'keyName':
  235. switch(cchar){
  236. case '=':
  237. tagState = 'keyValue';
  238. keyValue = '';
  239. break;
  240. case ';':
  241. tags[keyName] = '';
  242. keyName = ''; // staying in tagStateKeyName
  243. break;
  244. default: keyName += cchar; break;
  245. }
  246. break;
  247. case 'keyValue':
  248. switch(cchar){
  249. case '\\': tagState = 'keyValueEscape'; break;
  250. case ';':
  251. tags[keyName] = keyValue;
  252. keyName = '';
  253. tagState = 'keyName';
  254. break;
  255. default: keyValue += cchar; break;
  256. }
  257. break;
  258. case 'keyValueEscape':
  259. switch(cchar){
  260. case ':': keyValue += ';'; break;
  261. case 's': keyValue += ' '; break;
  262. case 'r': keyValue += '\r'; break;
  263. case 'n': keyValue += '\n'; break;
  264. default: keyValue += cchar; break;
  265. }
  266. tagState = 'keyValue';
  267. break;
  268. }
  269. }
  270. if(keyName.length > 0) tags[keyName] = keyValue; // flush last tag
  271. return tags;
  272. },
  273. 'parseLine': function(line){
  274. var ircmsg = new irc.messagedata();
  275. var line = line.trim();
  276. line.replace(/^\s+|\s+$/gm,'');
  277. if(line == ''){
  278. return;
  279. }
  280. ircmsg.originalString = line;
  281. var msglen = line.length;
  282. var pstate = 'start';
  283. var currArg = '';
  284. var tags = '';
  285. var haveText = false;
  286. console.log('>>> ' + line);
  287. for(var i = 0; i < msglen; i++){
  288. var cchar = line.charAt(i);
  289. switch(pstate){
  290. case 'start':
  291. switch(cchar){
  292. case '@': pstate = 'tags'; break;
  293. case ':': pstate = 'senderNick'; break;
  294. default:
  295. pstate = 'command';
  296. ircmsg.command += cchar;
  297. break;
  298. }
  299. break;
  300. case 'tags':
  301. switch(cchar){
  302. case ' ':
  303. pstate = 'start';
  304. ircmsg.tags = irc.parseTags(tags);
  305. break;
  306. default: tags += cchar; break;
  307. }
  308. break;
  309. case 'senderNick':
  310. switch(cchar){
  311. case '!': pstate = 'senderUser'; break;
  312. case '@': pstate = 'senderHost'; break;
  313. case ' ': pstate = 'command'; break;
  314. default: ircmsg.sender.nick += cchar; break;
  315. }
  316. break;
  317. case 'senderUser':
  318. switch(cchar){
  319. case '@': pstate = 'senderHost'; break;
  320. case ' ': pstate = 'command'; break;
  321. default: ircmsg.sender.ident += cchar; break;
  322. }
  323. break;
  324. case 'senderHost':
  325. switch(cchar){
  326. case ' ': pstate = 'command'; break;
  327. default: ircmsg.sender.host += cchar; break;
  328. }
  329. break;
  330. case 'command':
  331. switch(cchar){
  332. case ' ': pstate = 'args'; break;
  333. default: ircmsg.command += cchar; break;
  334. }
  335. break;
  336. case 'args':
  337. switch(cchar){
  338. case ' ':
  339. if(currArg != ''){
  340. ircmsg.args.push(currArg);
  341. }
  342. currArg = '';
  343. break;
  344. case ':':
  345. if(prevChar == ' '){
  346. pstate = 'message';
  347. haveText = true;
  348. } else {
  349. currArg += cchar;
  350. }
  351. break;
  352. default: currArg += cchar; break;
  353. }
  354. break;
  355. case 'message':
  356. ircmsg.text += cchar;
  357. break;
  358. }
  359. var prevChar = cchar;
  360. }
  361. if(pstate == 'args'){
  362. ircmsg.args.push(currArg);
  363. }
  364. ircmsg.sender = uplink.processSender(ircmsg.sender);
  365. if(!ircmsg.sender){
  366. return false;
  367. }
  368. if(!ircmsg.sender.server && !ircmsg.sender.user){
  369. if(ircmsg.sender.ident == '' && ircmsg.sender.host == '' && ircmsg.sender.nick.indexOf('.')!=-1){
  370. ircmsg.sender.server = true;
  371. } else {
  372. ircmsg.sender.user = true;
  373. }
  374. }
  375. if(!haveText){
  376. ircmsg.text = ircmsg.args[ircmsg.args.length-1]; // handling last argument as text if : is missing
  377. } else {
  378. ircmsg.args.push(ircmsg.text); // handling text as a last argument as required by the protocol
  379. }
  380. // add u@h
  381. /* if(ircmsg.sender.user){
  382. if(ircmsg.sender.ident) users.getUser(ircmsg.sender.nick).setIdent(ircmsg.sender.ident);
  383. if(ircmsg.sender.host) users.getUser(ircmsg.sender.nick).setHost(ircmsg.sender.host);
  384. }*/
  385. // process known tags
  386. /* if('time' in ircmsg.tags){
  387. ircmsg.time = parseISOString(ircmsg.tags['time']);
  388. }
  389. if('account' in ircmsg.tags){
  390. users.getUser(ircmsg.sender.nick).setAccount(ircmsg.tags['account']);
  391. }*/
  392. // console.log(ircmsg);
  393. return ircmsg;
  394. }
  395. };
  396. module.exports = irc;