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.

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