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.

138 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)
  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. if (Object.keys(channellist).includes(channelname)) {
  83. channellist[channelname].join(user)
  84. } else {
  85. let channel = new Channel(channelname)
  86. channel.join(user)
  87. channellist[channelname] = channel
  88. }
  89. break;
  90. case "QUIT":
  91. server.closeConnection(user.nickname)
  92. break;
  93. case "PASS":
  94. if (tokenized[1] === config.password) {
  95. user.authenticated = true
  96. } else {
  97. user.closeConnection()
  98. }
  99. break;
  100. default:
  101. console.error(`Unknown command: ${command}`);
  102. }
  103. });
  104. }).on('error', (err) => {
  105. console.error(err);
  106. })
  107. }).on('error', (err) => {
  108. console.error(err);
  109. throw err;
  110. });
  111. server.getUserlist = function () {
  112. return userlist
  113. }
  114. server.closeConnection = function (user_nickname) {
  115. let user = userlist[user_nickname]
  116. user.closeConnection()
  117. delete userlist[user_nickname];
  118. }
  119. return server
  120. }
  121. module.exports = server