snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s

This commit is contained in:
upstream-mirror
2026-09-16 01:21:02 +05:00
commit 30810eecd7
10161 changed files with 987110 additions and 0 deletions

View File

@@ -0,0 +1,519 @@
/**
* This file is part of https://github.com/martynsmith/node-irc
* by https://github.com/martynsmith
*/
module.exports = {
'001': {
name: 'rpl_welcome',
type: 'reply',
},
'002': {
name: 'rpl_yourhost',
type: 'reply',
},
'003': {
name: 'rpl_created',
type: 'reply',
},
'004': {
name: 'rpl_myinfo',
type: 'reply',
},
'005': {
name: 'rpl_isupport',
type: 'reply',
},
'200': {
name: 'rpl_tracelink',
type: 'reply',
},
'201': {
name: 'rpl_traceconnecting',
type: 'reply',
},
'202': {
name: 'rpl_tracehandshake',
type: 'reply',
},
'203': {
name: 'rpl_traceunknown',
type: 'reply',
},
'204': {
name: 'rpl_traceoperator',
type: 'reply',
},
'205': {
name: 'rpl_traceuser',
type: 'reply',
},
'206': {
name: 'rpl_traceserver',
type: 'reply',
},
'208': {
name: 'rpl_tracenewtype',
type: 'reply',
},
'211': {
name: 'rpl_statslinkinfo',
type: 'reply',
},
'212': {
name: 'rpl_statscommands',
type: 'reply',
},
'213': {
name: 'rpl_statscline',
type: 'reply',
},
'214': {
name: 'rpl_statsnline',
type: 'reply',
},
'215': {
name: 'rpl_statsiline',
type: 'reply',
},
'216': {
name: 'rpl_statskline',
type: 'reply',
},
'218': {
name: 'rpl_statsyline',
type: 'reply',
},
'219': {
name: 'rpl_endofstats',
type: 'reply',
},
'221': {
name: 'rpl_umodeis',
type: 'reply',
},
'241': {
name: 'rpl_statslline',
type: 'reply',
},
'242': {
name: 'rpl_statsuptime',
type: 'reply',
},
'243': {
name: 'rpl_statsoline',
type: 'reply',
},
'244': {
name: 'rpl_statshline',
type: 'reply',
},
'250': {
name: 'rpl_statsconn',
type: 'reply',
},
'251': {
name: 'rpl_luserclient',
type: 'reply',
},
'252': {
name: 'rpl_luserop',
type: 'reply',
},
'253': {
name: 'rpl_luserunknown',
type: 'reply',
},
'254': {
name: 'rpl_luserchannels',
type: 'reply',
},
'255': {
name: 'rpl_luserme',
type: 'reply',
},
'256': {
name: 'rpl_adminme',
type: 'reply',
},
'257': {
name: 'rpl_adminloc1',
type: 'reply',
},
'258': {
name: 'rpl_adminloc2',
type: 'reply',
},
'259': {
name: 'rpl_adminemail',
type: 'reply',
},
'261': {
name: 'rpl_tracelog',
type: 'reply',
},
'265': {
name: 'rpl_localusers',
type: 'reply',
},
'266': {
name: 'rpl_globalusers',
type: 'reply',
},
'300': {
name: 'rpl_none',
type: 'reply',
},
'301': {
name: 'rpl_away',
type: 'reply',
},
'302': {
name: 'rpl_userhost',
type: 'reply',
},
'303': {
name: 'rpl_ison',
type: 'reply',
},
'305': {
name: 'rpl_unaway',
type: 'reply',
},
'306': {
name: 'rpl_nowaway',
type: 'reply',
},
'311': {
name: 'rpl_whoisuser',
type: 'reply',
},
'312': {
name: 'rpl_whoisserver',
type: 'reply',
},
'313': {
name: 'rpl_whoisoperator',
type: 'reply',
},
'314': {
name: 'rpl_whowasuser',
type: 'reply',
},
'315': {
name: 'rpl_endofwho',
type: 'reply',
},
'317': {
name: 'rpl_whoisidle',
type: 'reply',
},
'318': {
name: 'rpl_endofwhois',
type: 'reply',
},
'319': {
name: 'rpl_whoischannels',
type: 'reply',
},
'321': {
name: 'rpl_liststart',
type: 'reply',
},
'322': {
name: 'rpl_list',
type: 'reply',
},
'323': {
name: 'rpl_listend',
type: 'reply',
},
'324': {
name: 'rpl_channelmodeis',
type: 'reply',
},
'329': {
name: 'rpl_creationtime',
type: 'reply',
},
'331': {
name: 'rpl_notopic',
type: 'reply',
},
'332': {
name: 'rpl_topic',
type: 'reply',
},
'333': {
name: 'rpl_topicwhotime',
type: 'reply',
},
'341': {
name: 'rpl_inviting',
type: 'reply',
},
'342': {
name: 'rpl_summoning',
type: 'reply',
},
'351': {
name: 'rpl_version',
type: 'reply',
},
'352': {
name: 'rpl_whoreply',
type: 'reply',
},
'353': {
name: 'rpl_namreply',
type: 'reply',
},
'364': {
name: 'rpl_links',
type: 'reply',
},
'365': {
name: 'rpl_endoflinks',
type: 'reply',
},
'366': {
name: 'rpl_endofnames',
type: 'reply',
},
'367': {
name: 'rpl_banlist',
type: 'reply',
},
'368': {
name: 'rpl_endofbanlist',
type: 'reply',
},
'369': {
name: 'rpl_endofwhowas',
type: 'reply',
},
'371': {
name: 'rpl_info',
type: 'reply',
},
'372': {
name: 'rpl_motd',
type: 'reply',
},
'374': {
name: 'rpl_endofinfo',
type: 'reply',
},
'375': {
name: 'rpl_motdstart',
type: 'reply',
},
'376': {
name: 'rpl_endofmotd',
type: 'reply',
},
'381': {
name: 'rpl_youreoper',
type: 'reply',
},
'382': {
name: 'rpl_rehashing',
type: 'reply',
},
'391': {
name: 'rpl_time',
type: 'reply',
},
'392': {
name: 'rpl_usersstart',
type: 'reply',
},
'393': {
name: 'rpl_users',
type: 'reply',
},
'394': {
name: 'rpl_endofusers',
type: 'reply',
},
'395': {
name: 'rpl_nousers',
type: 'reply',
},
'401': {
name: 'err_nosuchnick',
type: 'error',
},
'402': {
name: 'err_nosuchserver',
type: 'error',
},
'403': {
name: 'err_nosuchchannel',
type: 'error',
},
'404': {
name: 'err_cannotsendtochan',
type: 'error',
},
'405': {
name: 'err_toomanychannels',
type: 'error',
},
'406': {
name: 'err_wasnosuchnick',
type: 'error',
},
'407': {
name: 'err_toomanytargets',
type: 'error',
},
'409': {
name: 'err_noorigin',
type: 'error',
},
'411': {
name: 'err_norecipient',
type: 'error',
},
'412': {
name: 'err_notexttosend',
type: 'error',
},
'413': {
name: 'err_notoplevel',
type: 'error',
},
'414': {
name: 'err_wildtoplevel',
type: 'error',
},
'421': {
name: 'err_unknowncommand',
type: 'error',
},
'422': {
name: 'err_nomotd',
type: 'error',
},
'423': {
name: 'err_noadmininfo',
type: 'error',
},
'424': {
name: 'err_fileerror',
type: 'error',
},
'431': {
name: 'err_nonicknamegiven',
type: 'error',
},
'432': {
name: 'err_erroneusnickname',
type: 'error',
},
'433': {
name: 'err_nicknameinuse',
type: 'error',
},
'436': {
name: 'err_nickcollision',
type: 'error',
},
'441': {
name: 'err_usernotinchannel',
type: 'error',
},
'442': {
name: 'err_notonchannel',
type: 'error',
},
'443': {
name: 'err_useronchannel',
type: 'error',
},
'444': {
name: 'err_nologin',
type: 'error',
},
'445': {
name: 'err_summondisabled',
type: 'error',
},
'446': {
name: 'err_usersdisabled',
type: 'error',
},
'451': {
name: 'err_notregistered',
type: 'error',
},
'461': {
name: 'err_needmoreparams',
type: 'error',
},
'462': {
name: 'err_alreadyregistred',
type: 'error',
},
'463': {
name: 'err_nopermforhost',
type: 'error',
},
'464': {
name: 'err_passwdmismatch',
type: 'error',
},
'465': {
name: 'err_yourebannedcreep',
type: 'error',
},
'467': {
name: 'err_keyset',
type: 'error',
},
'471': {
name: 'err_channelisfull',
type: 'error',
},
'472': {
name: 'err_unknownmode',
type: 'error',
},
'473': {
name: 'err_inviteonlychan',
type: 'error',
},
'474': {
name: 'err_bannedfromchan',
type: 'error',
},
'475': {
name: 'err_badchannelkey',
type: 'error',
},
'481': {
name: 'err_noprivileges',
type: 'error',
},
'482': {
name: 'err_chanoprivsneeded',
type: 'error',
},
'483': {
name: 'err_cantkillserver',
type: 'error',
},
'491': {
name: 'err_nooperhost',
type: 'error',
},
'501': {
name: 'err_umodeunknownflag',
type: 'error',
},
'502': {
name: 'err_usersdontmatch',
type: 'error',
},
};

View File

@@ -0,0 +1,195 @@
import { EventEmitter } from 'node:events';
import net from 'node:net';
import util from 'node:util';
import { Logger } from '@rocket.chat/logger';
import localCommandHandlers from './localCommandHandlers';
import parseMessage from './parseMessage';
import peerCommandHandlers from './peerCommandHandlers';
const logger = new Logger('IRC Server');
class RFC2813 {
constructor(config) {
this.config = config;
// Hold registered state
this.registerSteps = [];
this.isRegistered = false;
// Hold peer server information
this.serverPrefix = null;
// Hold the buffer while receiving
this.receiveBuffer = Buffer.from('');
}
/**
* Setup socket
*/
setupSocket() {
// Setup socket
this.socket = new net.Socket();
this.socket.setNoDelay();
this.socket.setEncoding('utf-8');
this.socket.setKeepAlive(true);
this.socket.setTimeout(90000);
this.socket.on('data', this.onReceiveFromPeer.bind(this));
this.socket.on('connect', this.onConnect.bind(this));
this.socket.on('error', (err) => logger.error({ msg: 'Socket error', err }));
this.socket.on('timeout', () => this.log({ msg: 'Timeout' }));
this.socket.on('close', () => this.log({ msg: 'Connection Closed' }));
// Setup local
this.on('onReceiveFromLocal', this.onReceiveFromLocal.bind(this));
}
/**
* Log helper
*/
log(message) {
// TODO logger: debug?
if (typeof message === 'string') {
logger.info({ msg: message });
return;
}
logger.info(message);
}
/**
* Connect
*/
register() {
this.log({
msg: 'Connecting to IRC server',
host: this.config.server.host,
port: this.config.server.port,
});
if (!this.socket) {
this.setupSocket();
}
this.socket.connect(this.config.server.port, this.config.server.host);
}
/**
* Disconnect
*/
disconnect() {
this.log('Disconnecting from server.');
if (this.socket) {
this.socket.destroy();
this.socket = undefined;
}
this.isRegistered = false;
this.registerSteps = [];
}
/**
* Setup the server connection
*/
onConnect() {
this.log('Connected! Registering as server...');
this.write({
command: 'PASS',
parameters: [this.config.passwords.local, '0210', 'ngircd'],
});
this.write({
command: 'SERVER',
parameters: [this.config.server.name],
trailer: this.config.server.description,
});
}
/**
* Sends a command message through the socket
*/
write(command) {
let buffer = command.prefix ? `:${command.prefix} ` : '';
buffer += command.command;
if (command.parameters && command.parameters.length > 0) {
buffer += ` ${command.parameters.join(' ')}`;
}
if (command.trailer) {
buffer += ` :${command.trailer}`;
}
this.log({ msg: 'Sending Command', buffer });
return this.socket.write(`${buffer}\r\n`);
}
/**
*
*
* Peer message handling
*
*
*/
onReceiveFromPeer(chunk) {
if (typeof chunk === 'string') {
this.receiveBuffer += chunk;
} else {
this.receiveBuffer = Buffer.concat([this.receiveBuffer, chunk]);
}
const lines = this.receiveBuffer.toString().split(/\r\n|\r|\n|\u0007/); // eslint-disable-line no-control-regex
// If the buffer does not end with \r\n, more chunks are coming
if (lines.pop()) {
return;
}
// Reset the buffer
this.receiveBuffer = Buffer.from('');
lines.forEach((line) => {
if (line.length && !line.startsWith('a')) {
const parsedMessage = parseMessage(line);
if (peerCommandHandlers[parsedMessage.command]) {
this.log({ msg: 'Handling peer message', line });
const command = peerCommandHandlers[parsedMessage.command].call(this, parsedMessage);
if (command) {
this.log({ msg: 'Emitting peer command to local', command });
this.emit('peerCommand', command);
}
} else {
this.log({ msg: 'Unhandled peer message', parsedMessage });
}
}
});
}
/**
*
*
* Local message handling
*
*
*/
onReceiveFromLocal(command, parameters) {
if (localCommandHandlers[command]) {
this.log({ msg: 'Handling local command', command });
localCommandHandlers[command].call(this, parameters, this);
} else {
this.log({ msg: 'Unhandled local command', command });
}
}
}
util.inherits(RFC2813, EventEmitter);
export default RFC2813;

View File

@@ -0,0 +1,111 @@
function registerUser(parameters) {
const {
name,
profile: {
irc: { nick, username },
},
} = parameters;
this.write({
prefix: this.config.server.name,
command: 'NICK',
parameters: [nick, 1, username, 'irc.rocket.chat', 1, '+i'],
trailer: name,
});
}
function joinChannel(parameters) {
const {
room: { name: roomName },
user: {
profile: {
irc: { nick },
},
},
} = parameters;
this.write({
prefix: this.config.server.name,
command: 'NJOIN',
parameters: [`#${roomName}`],
trailer: nick,
});
}
function joinedChannel(parameters, handler) {
const roomName = parameters.room?.name;
const nick = parameters.user?.profile?.irc?.nick;
if (!roomName) {
handler.log('Skipping room with no name.');
return;
}
if (!nick) {
handler.log('Skipping user with no irc nick.');
return;
}
this.write({
prefix: nick,
command: 'JOIN',
parameters: [`#${roomName}`],
});
}
function leftChannel(parameters) {
const {
room: { name: roomName },
user: {
profile: {
irc: { nick },
},
},
} = parameters;
this.write({
prefix: nick,
command: 'PART',
parameters: [`#${roomName}`],
});
}
function sentMessage(parameters) {
const {
user: {
profile: {
irc: { nick },
},
},
to,
message,
} = parameters;
// eslint-disable-next-line no-control-regex
const lines = message.toString().split(/\r\n|\r|\n|\u0007/);
for (const line of lines) {
this.write({
prefix: nick,
command: 'PRIVMSG',
parameters: [to],
trailer: line,
});
}
}
function disconnected(parameters) {
const {
user: {
profile: {
irc: { nick },
},
},
} = parameters;
this.write({
prefix: nick,
command: 'QUIT',
});
}
export default { registerUser, joinChannel, joinedChannel, leftChannel, sentMessage, disconnected };

View File

@@ -0,0 +1,69 @@
/**
* This file is part of https://github.com/martynsmith/node-irc
* by https://github.com/martynsmith
*/
const replyFor = require('./codes');
/**
* parseMessage(line, stripColors)
*
* takes a raw "line" from the IRC server and turns it into an object with
* useful keys
* @param {String} line Raw message from IRC server.
* @return {Object} A parsed message object.
*/
module.exports = function parseMessage(line) {
const message = {};
let match;
// Parse prefix
match = line.match(/^:([^ ]+) +/);
if (match) {
message.prefix = match[1];
line = line.replace(/^:[^ ]+ +/, '');
match = message.prefix.match(/^([_a-zA-Z0-9\~\[\]\\`^{}|-]*)(!([^@]+)@(.*))?$/);
if (match) {
message.nick = match[1];
message.user = match[3];
message.host = match[4];
} else {
message.server = message.prefix;
}
}
// Parse command
match = line.match(/^([^ ]+) */);
message.command = match[1];
message.rawCommand = match[1];
message.commandType = 'normal';
line = line.replace(/^[^ ]+ +/, '');
if (replyFor[message.rawCommand]) {
message.command = replyFor[message.rawCommand].name;
message.commandType = replyFor[message.rawCommand].type;
}
message.args = [];
let middle;
let trailing;
// Parse parameters
if (line.search(/^:|\s+:/) !== -1) {
match = line.match(/(.*?)(?:^:|\s+:)(.*)/);
middle = match[1].trimRight();
trailing = match[2];
} else {
middle = line;
}
if (middle.length) {
message.args = middle.split(/ +/);
}
if (typeof trailing !== 'undefined' && trailing.length) {
message.args.push(trailing);
}
return message;
};

View File

@@ -0,0 +1,113 @@
function PASS() {
this.log('Received PASS command, continue registering...');
this.registerSteps.push('PASS');
}
function SERVER(parsedMessage) {
this.log('Received SERVER command, waiting for first PING...');
this.serverPrefix = parsedMessage.prefix;
this.registerSteps.push('SERVER');
}
function PING() {
if (!this.isRegistered && this.registerSteps.length === 2) {
this.log('Received first PING command, server is registered!');
this.isRegistered = true;
this.emit('registered');
}
this.write({
prefix: this.config.server.name,
command: 'PONG',
parameters: [this.config.server.name],
});
}
function NICK(parsedMessage) {
let command;
// Check if the message comes from the server,
// which means it is a new user
if (parsedMessage.prefix === this.serverPrefix) {
command = {
identifier: 'userRegistered',
args: {
nick: parsedMessage.args[0],
username: parsedMessage.args[2],
host: parsedMessage.args[3],
name: parsedMessage.args[6],
},
};
} else {
// Otherwise, it is a nick change
command = {
identifier: 'nickChanged',
args: {
nick: parsedMessage.nick,
newNick: parsedMessage.args[0],
},
};
}
return command;
}
function JOIN(parsedMessage) {
const command = {
identifier: 'joinedChannel',
args: {
roomName: parsedMessage.args[0].substring(1),
nick: parsedMessage.prefix,
},
};
return command;
}
function PART(parsedMessage) {
const command = {
identifier: 'leftChannel',
args: {
roomName: parsedMessage.args[0].substring(1),
nick: parsedMessage.prefix,
},
};
return command;
}
function PRIVMSG(parsedMessage) {
const command = {
identifier: 'sentMessage',
args: {
nick: parsedMessage.prefix,
message: parsedMessage.args[1],
},
};
if (parsedMessage.args[0][0] === '#') {
command.args.roomName = parsedMessage.args[0].substring(1);
} else {
command.args.recipientNick = parsedMessage.args[0];
}
return command;
}
function QUIT(parsedMessage) {
const command = {
identifier: 'disconnected',
args: {
nick: parsedMessage.prefix,
},
};
return command;
}
export default { PASS, SERVER, PING, NICK, JOIN, PART, PRIVMSG, QUIT };