irc.js 8.9 KB

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