unrealircd.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. module.exports.connected = connected;
  2. module.exports.processMessage = processMessage;
  3. module.exports.processSender = processSender;
  4. module.exports.setHandlers = setHandlers;
  5. module.exports.setSettings = setSettings;
  6. var handlers = {
  7. sendHandler: null,
  8. findServer: null,
  9. findUser: null,
  10. killUser: null,
  11. newServer: null,
  12. newUser: null,
  13. parseUmodes: null,
  14. getChannel: null,
  15. quitUser: null
  16. };
  17. var settings = {
  18. ID: null,
  19. password: null,
  20. name: null,
  21. description: null,
  22. version: null,
  23. me: null,
  24. maxUsers: null
  25. };
  26. var prefixes = {
  27. '~': 'q',
  28. '&': 'a',
  29. '@': 'o',
  30. '%': 'h',
  31. '+': 'v'
  32. };
  33. function parseModePrefix(text){
  34. var modes = [];
  35. for(var i=0; i<text.length; i++){
  36. var c = text.charAt(i);
  37. if(c in prefixes){
  38. modes[prefixes[c]] = true;
  39. } else break;
  40. }
  41. var uid = text.substring(i);
  42. var user = handlers.findUser(uid);
  43. if(!user){
  44. handlers.killUser(uid);
  45. return null;
  46. }
  47. return { 'user': user, 'modes': modes };
  48. }
  49. function connected(){
  50. ircSend(null, settings.ID, 'PASS', [settings.password]);
  51. ircSend(null, settings.ID, 'PROTOCTL', ['NICKv2', 'VHP', 'UMODE2', 'NICKIP', 'SJOIN', 'SJOIN2', 'SJ3', 'NOQUIT', 'TKLEXT', 'MLOCK', 'SID']);
  52. ircSend(null, settings.ID, 'PROTOCTL', ['EAUTH=' + settings.name + ',,,' + settings.version]);
  53. ircSend(null, settings.ID, 'PROTOCTL', ['SID=' + settings.ID]);
  54. ircSend(null, settings.ID, 'SERVER', [settings.name, '1', settings.description]);
  55. }
  56. function processSender(sender){
  57. if(sender.nick){
  58. if(server = handlers.findServer(sender.nick)){
  59. sender.nick = server.name;
  60. sender.server = server;
  61. } else if(user = handlers.findUser(sender.nick)){
  62. sender.nick = user.name
  63. sender.ident = user.ident;
  64. sender.host = user.host;
  65. sender.user = user;
  66. } else {
  67. handlers.killUser(sender.nick);
  68. return false;
  69. }
  70. }
  71. return sender;
  72. }
  73. function processMessage(msg){
  74. if(!msg) return;
  75. if(msg.command in cmdBinds){
  76. cmdBinds[msg.command](msg);
  77. } else {
  78. console.log('Unhandled cmd: '+msg.command);
  79. console.log(msg);
  80. }
  81. }
  82. function setHandlers(newHandlers){
  83. handlers = newHandlers;
  84. }
  85. function setSettings(newSettings){
  86. settings = newSettings;
  87. }
  88. function ircSend(tags, from, cmd, args){
  89. handlers.send(tags, from, cmd, args);
  90. }
  91. var cmdBinds = {
  92. 'PROTOCTL': function(msg){
  93. //TODO parse
  94. console.log(msg);
  95. },
  96. 'SERVER': function(msg){
  97. var expr = /^([^-]+)-([^-]+)-([^ ]+) (.*)$/;
  98. var match = expr.exec(msg.args[2]);
  99. if(match){
  100. handlers.newServer(msg.args[0], match[3], match[4], msg.args[1], settings.me);
  101. } else {
  102. throw 'Unknown SERVER message';
  103. }
  104. },
  105. 'MD': function(msg){
  106. switch(msg.args[0]){
  107. case 'client':
  108. var user = handlers.findUser(msg.args[1]);
  109. if(!user) return;
  110. switch(msg.args[2]){
  111. case 'certfp':
  112. if(!msg.args[3] || msg.args[3].length == 0) break;
  113. user.setSecure(true);
  114. user.setFingerprint(msg.args[3]);
  115. break;
  116. default: break;
  117. }
  118. break;
  119. default: break;
  120. }
  121. },
  122. 'SMOD': function(msg){ // ignore
  123. },
  124. 'EOS': function(msg){ // ?
  125. },
  126. 'SINFO': function(msg){
  127. console.log(msg);
  128. },
  129. 'SID': function(msg){
  130. handlers.newServer(msg.args[0], msg.args[2], msg.args[3], msg.args[1], msg.sender);
  131. },
  132. 'UID': function(msg){
  133. var nick = msg.args[0];
  134. var distance = msg.args[1];
  135. var TS = msg.args[2];
  136. var ident = msg.args[3];
  137. var host = msg.args[4];
  138. var uid = msg.args[5];
  139. var account = msg.args[6];
  140. var umodes = msg.args[7];
  141. var vhost = msg.args[8];
  142. var cloakedHost = msg.args[9];
  143. var ip = msg.args[10];
  144. var realname = msg.args[11];
  145. if(ip != '*'){
  146. var ipBinary = Buffer.from(ip, 'base64');
  147. console.log(ipBinary);
  148. if(ipBinary.byteLength == 4){ // IPv4
  149. ip = ipBinary[0].toString(10) + '.' + ipBinary[1].toString(10) + '.' + ipBinary[2].toString(10) + '.' + ipBinary[3].toString(10);
  150. } else if(ipBinary.byteLength == 16){ // IPv6
  151. ip = '';
  152. for(var i=0; i<8; i++){
  153. if(i > 0) ip += ':';
  154. ip += ipBinary[i*2].toString(16).padStart(2, '0') + ipBinary[i*2+1].toString(16).padStart(2, '0');
  155. }
  156. } else {
  157. throw 'unknown IP format';
  158. }
  159. } else {
  160. ip = null;
  161. }
  162. if(vhost == '*'){
  163. vhost = null;
  164. }
  165. if(cloakedHost == '*'){
  166. cloakedHost = null;
  167. }
  168. if(account == '0'){
  169. account = null;
  170. }
  171. handlers.newUser(nick, distance, TS, ident, host, uid, account, handlers.parseUmodes(umodes), vhost, cloakedHost, ip, realname, msg.sender.server);
  172. },
  173. 'SJOIN': function(msg){
  174. var channel = handlers.getChannel(msg.args[1]);
  175. channel.setTS(msg.args[0]);
  176. var modeArgs = [];
  177. for(var i=3; i<msg.args.length - 1; i++){
  178. modeArgs.push(msg.args[i]);
  179. }
  180. channel.addModes(msg.args[2], modeArgs);
  181. var members = msg.text.split(' ');
  182. for(var i=0; i<members.length; i++){
  183. var member = members[i];
  184. var c = member.charAt(0);
  185. if(c == '&'){ // +b
  186. channel.addBan(member.substring(1));
  187. } else if(c == '"'){ // +e
  188. channel.addExcept(member.substring(1));
  189. } else if(c == '\''){ // +I
  190. channel.addInvex(member.substring(1));
  191. } else {
  192. var udata = parseModePrefix(member);
  193. if(!udata) return;
  194. console.log(udata.user.name + ' joined ' + msg.args[1]);
  195. channel.joinUser(udata.user);
  196. channel.setStatusModes(udata.user, udata.modes);
  197. }
  198. }
  199. console.log(channel);
  200. },
  201. 'SWHOIS': function(msg){
  202. //TODO
  203. },
  204. 'MODE': function(msg){
  205. //TODO
  206. },
  207. 'TKL': function(msg){
  208. //TODO
  209. },
  210. 'METADATA': function(msg){
  211. //TODO
  212. },
  213. 'NETINFO': function(msg){
  214. ircSend(null, null, [settings.maxUsers, Math.floor(new Date() / 1000), msg.args[2], msg.args[3], "0", "0", "0", msg.args[7]]);
  215. },
  216. 'SASL': function(msg){
  217. //TODO
  218. },
  219. 'PING': function(msg){
  220. msg.reply(null, 'PONG', msg.args);
  221. },
  222. 'QUIT': function(msg){
  223. handlers.quitUser(msg.sender.user);
  224. },
  225. 'PART': function(msg){
  226. var channel = handlers.getChannel(msg.args[0]);
  227. channel.removeUser(msg.sender.user);
  228. },
  229. 'CHGHOST': function(msg){
  230. var user = handlers.findUser(msg.args[0]);
  231. if(!user) return;
  232. user.changeVHost(msg.args[1]);
  233. },
  234. 'CHGIDENT': function(msg){
  235. var user = handlers.findUser(msg.args[0]);
  236. if(!user) return;
  237. user.changeVIdent(msg.args[1]);
  238. },
  239. 'CHGNAME': function(msg){
  240. var user = handlers.findUser(msg.args[0]);
  241. if(!user) return;
  242. user.setRealname(msg.args[1]);
  243. },
  244. 'SETNAME': function(msg){
  245. msg.sender.user.setRealname(msg.args[0]);
  246. },
  247. 'SETHOST': function(msg){
  248. //TODO
  249. },
  250. 'SETIDENT': function(msg){
  251. //TODO
  252. },
  253. 'NICK': function(msg){
  254. //TODO
  255. },
  256. 'SDESC': function(msg){
  257. //TODO
  258. },
  259. 'TOPIC': function(msg){
  260. //TODO
  261. },
  262. };