You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

135 lines
4.9 KiB

  1. const net = require("net")
  2. const User = require("../src/user.js")
  3. const Channel = require("../src/channel.js")
  4. const RPL_WELCOME = '001'
  5. const ERR_NOSUCHNICK = '401'
  6. const ERR_NOSUCHCHANNEL = '403'
  7. const ERR_CANNOTSENDTOCHAN = '404'
  8. const ERR_UNKNOWNCOMMAND = '421'
  9. const ERR_ERRONEUSNICKNAME = '432'
  10. const ERR_NONICKNAMEGIVEN = '431'
  11. const ERR_NICKNAMEINUSE = '433'
  12. const ERR_NEEDMOREPARAMS = '461'
  13. let server = {}
  14. server.create = function create(config = {}) {
  15. let password
  16. if (config.password) {
  17. password = config.password
  18. }
  19. let userlist = {}
  20. let channellist = {}
  21. let server = net.createServer((socket) => {
  22. const user = new User(socket, !config.password)
  23. socket.on('data', function (data) {
  24. data.toString("ascii").split("\r\n").forEach(function (commandline) {
  25. if (commandline == "") {
  26. return
  27. }
  28. let splitted = commandline.split(" ")
  29. let tokenized = []
  30. let lastParam = -1
  31. for (let i = 0; i < splitted.length; i++) {
  32. if (lastParam > 0) {
  33. tokenized[lastParam] = tokenized[lastParam] + " " + splitted[i]
  34. } else if (splitted[i].charAt(0) == ":" && i > 0) {
  35. lastParam = i
  36. tokenized[lastParam] = splitted[i].slice(1)
  37. } else {
  38. tokenized[i] = splitted[i]
  39. }
  40. }
  41. let command = tokenized[0].toUpperCase()
  42. switch (command) {
  43. case "PING":
  44. if (tokenized[1] && tokenized[1] === "irc.example.com") {
  45. socket.write("PONG irc.example.com\r\n")
  46. }
  47. break;
  48. case "NICK":
  49. if (tokenized[1]) {
  50. let nickname = tokenized[1]
  51. // nick collision test
  52. if (!Object.keys(userlist).includes(nickname)) {
  53. user.setNickname(nickname)
  54. userlist[user.nickname] = user
  55. } else {
  56. socket.write(`:irc.example.com ${ERR_NICKNAMEINUSE} ${nickname} :Nickname is already in use`, 'ascii')
  57. }
  58. } else {
  59. socket.write(`:irc.example.com ${ERR_NONICKNAMEGIVEN} :No nickname given`, 'ascii')
  60. }
  61. break;
  62. case "USER":
  63. // ignoring servername and hostname, not useful these days
  64. user.register(tokenized[1], tokenized[4])
  65. let address = user.getAddress()
  66. socket.write(`:irc.example.com 001 ${user.nickname} :Welcome to the example IRC Project ${user.nickname}!~${user.username}@${address}\r\n`, "ascii")
  67. break;
  68. case "PRIVMSG":
  69. let target = userlist[tokenized[1]]
  70. let message = tokenized[2]
  71. if (target.registered === false) {
  72. socket.write(`:irc.example.com ${ERR_NOSUCHNICK} ${target} no such nick/channel`)
  73. } else {
  74. target.sendMsg(user, message)
  75. }
  76. break;
  77. case "JOIN":
  78. let channelname = tokenized[1]
  79. if (Object.keys(channellist).includes(channelname)) {
  80. channellist[channelname].join(user)
  81. } else {
  82. let channel = new Channel(channelname)
  83. channel.join(user)
  84. channellist[channelname] = channel
  85. }
  86. break;
  87. case "QUIT":
  88. server.closeConnection(user.nickname)
  89. break;
  90. case "PASS":
  91. if (tokenized[1] === config.password) {
  92. user.authenticated = true
  93. } else {
  94. user.closeConnection()
  95. }
  96. break;
  97. default:
  98. console.error(`Unknown command: ${command}`);
  99. }
  100. });
  101. }).on('error', (err) => {
  102. console.error(err);
  103. })
  104. }).on('error', (err) => {
  105. console.error(err);
  106. throw err;
  107. });
  108. server.getUserlist = function () {
  109. return userlist
  110. }
  111. server.closeConnection = function (user_nickname) {
  112. let user = userlist[user_nickname]
  113. user.closeConnection()
  114. delete userlist[user_nickname];
  115. }
  116. return server
  117. }
  118. module.exports = server