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.

147 lines
5.6 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
  70. let message = tokenized[2]
  71. if (typeof tokenized[1] === 'string' && tokenized[1].charAt(0) === "#") {
  72. target = channellist[tokenized[1]]
  73. } else {
  74. let targetUser = userlist[tokenized[1]]
  75. if (targetUser && targetUser.registered) {
  76. target = targetUser
  77. }
  78. }
  79. if (target) {
  80. target.sendMsg(user, message)
  81. } else {
  82. user.sendRaw(`:irc.example.com ${ERR_NOSUCHNICK} ${target} no such nick/channel`)
  83. }
  84. break;
  85. case "JOIN":
  86. if (!(tokenized[1].charAt(0) === "#")) {
  87. socket.write(`:irc.example.com ${ERR_CANNOTSENDTOCHAN}: invalid channel name`)
  88. break;
  89. }
  90. let channelname = tokenized[1]
  91. if (Object.keys(channellist).includes(channelname)) {
  92. channellist[channelname].join(user)
  93. } else {
  94. let channel = new Channel(channelname)
  95. channel.join(user)
  96. channellist[channelname] = channel
  97. }
  98. break;
  99. case "QUIT":
  100. server.closeConnection(user.nickname)
  101. break;
  102. case "PASS":
  103. if (tokenized[1] === config.password) {
  104. user.authenticated = true
  105. } else {
  106. user.closeConnection()
  107. }
  108. break;
  109. default:
  110. console.error(`Unknown command: ${command}`);
  111. }
  112. });
  113. }).on('error', (err) => {
  114. console.error(err);
  115. })
  116. }).on('error', (err) => {
  117. console.error(err);
  118. throw err;
  119. });
  120. server.getUserlist = function () {
  121. return userlist
  122. }
  123. server.closeConnection = function (user_nickname) {
  124. let user = userlist[user_nickname]
  125. user.closeConnection()
  126. delete userlist[user_nickname];
  127. }
  128. return server
  129. }
  130. module.exports = server