irc.js 9.8 KB

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