@lordbex/thelounge 4.4.3-blowfish → 4.5.0-blowfish-pre
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -7
- package/dist/defaults/config.js +31 -2
- package/dist/package.json +94 -91
- package/dist/server/client.js +188 -194
- package/dist/server/clientManager.js +75 -72
- package/dist/server/command-line/index.js +44 -43
- package/dist/server/command-line/install.js +37 -70
- package/dist/server/command-line/outdated.js +12 -17
- package/dist/server/command-line/start.js +25 -26
- package/dist/server/command-line/storage.js +26 -31
- package/dist/server/command-line/uninstall.js +16 -23
- package/dist/server/command-line/upgrade.js +20 -26
- package/dist/server/command-line/users/add.js +33 -40
- package/dist/server/command-line/users/edit.js +18 -24
- package/dist/server/command-line/users/index.js +12 -16
- package/dist/server/command-line/users/list.js +11 -39
- package/dist/server/command-line/users/remove.js +16 -22
- package/dist/server/command-line/users/reset.js +34 -35
- package/dist/server/command-line/utils.js +231 -87
- package/dist/server/config.js +61 -52
- package/dist/server/helper.js +29 -28
- package/dist/server/identification.js +39 -34
- package/dist/server/index.js +1 -3
- package/dist/server/log.js +19 -16
- package/dist/server/models/chan.js +36 -33
- package/dist/server/models/msg.js +15 -19
- package/dist/server/models/network.js +102 -104
- package/dist/server/models/prefix.js +4 -7
- package/dist/server/models/user.js +5 -10
- package/dist/server/path-helper.js +8 -0
- package/dist/server/plugins/auth/ldap.js +177 -112
- package/dist/server/plugins/auth/local.js +10 -15
- package/dist/server/plugins/auth.js +6 -35
- package/dist/server/plugins/changelog.js +30 -27
- package/dist/server/plugins/clientCertificate.js +33 -37
- package/dist/server/plugins/dev-server.js +15 -21
- package/dist/server/plugins/inputs/action.js +9 -14
- package/dist/server/plugins/inputs/away.js +1 -3
- package/dist/server/plugins/inputs/ban.js +9 -14
- package/dist/server/plugins/inputs/blow.js +9 -14
- package/dist/server/plugins/inputs/connect.js +5 -10
- package/dist/server/plugins/inputs/ctcp.js +7 -12
- package/dist/server/plugins/inputs/disconnect.js +1 -3
- package/dist/server/plugins/inputs/ignore.js +23 -29
- package/dist/server/plugins/inputs/ignorelist.js +12 -18
- package/dist/server/plugins/inputs/index.js +8 -34
- package/dist/server/plugins/inputs/invite.js +7 -12
- package/dist/server/plugins/inputs/kick.js +7 -12
- package/dist/server/plugins/inputs/kill.js +1 -3
- package/dist/server/plugins/inputs/list.js +1 -3
- package/dist/server/plugins/inputs/mode.js +10 -15
- package/dist/server/plugins/inputs/msg.js +13 -18
- package/dist/server/plugins/inputs/mute.js +9 -15
- package/dist/server/plugins/inputs/nick.js +9 -14
- package/dist/server/plugins/inputs/notice.js +5 -7
- package/dist/server/plugins/inputs/part.js +11 -16
- package/dist/server/plugins/inputs/quit.js +7 -13
- package/dist/server/plugins/inputs/rainbow.js +55 -0
- package/dist/server/plugins/inputs/raw.js +1 -3
- package/dist/server/plugins/inputs/rejoin.js +7 -12
- package/dist/server/plugins/inputs/topic.js +7 -12
- package/dist/server/plugins/inputs/whois.js +1 -3
- package/dist/server/plugins/irc-events/away.js +14 -20
- package/dist/server/plugins/irc-events/cap.js +16 -22
- package/dist/server/plugins/irc-events/chghost.js +14 -13
- package/dist/server/plugins/irc-events/connection.js +61 -63
- package/dist/server/plugins/irc-events/ctcp.js +22 -28
- package/dist/server/plugins/irc-events/error.js +20 -26
- package/dist/server/plugins/irc-events/help.js +7 -13
- package/dist/server/plugins/irc-events/info.js +7 -13
- package/dist/server/plugins/irc-events/invite.js +7 -13
- package/dist/server/plugins/irc-events/join.js +30 -27
- package/dist/server/plugins/irc-events/kick.js +21 -17
- package/dist/server/plugins/irc-events/link.js +122 -109
- package/dist/server/plugins/irc-events/list.js +23 -26
- package/dist/server/plugins/irc-events/message.js +46 -52
- package/dist/server/plugins/irc-events/mode.js +66 -63
- package/dist/server/plugins/irc-events/modelist.js +29 -35
- package/dist/server/plugins/irc-events/motd.js +10 -16
- package/dist/server/plugins/irc-events/names.js +3 -6
- package/dist/server/plugins/irc-events/nick.js +26 -23
- package/dist/server/plugins/irc-events/part.js +19 -15
- package/dist/server/plugins/irc-events/quit.js +17 -14
- package/dist/server/plugins/irc-events/sasl.js +9 -15
- package/dist/server/plugins/irc-events/spgroups.js +38 -0
- package/dist/server/plugins/irc-events/spjoin.js +52 -0
- package/dist/server/plugins/irc-events/topic.js +12 -18
- package/dist/server/plugins/irc-events/unhandled.js +12 -12
- package/dist/server/plugins/irc-events/welcome.js +7 -13
- package/dist/server/plugins/irc-events/whois.js +20 -24
- package/dist/server/plugins/massEventAggregator.js +214 -0
- package/dist/server/plugins/messageStorage/sqlite.js +322 -141
- package/dist/server/plugins/messageStorage/text.js +21 -26
- package/dist/server/plugins/packages/index.js +105 -74
- package/dist/server/plugins/packages/publicClient.js +7 -16
- package/dist/server/plugins/packages/themes.js +11 -16
- package/dist/server/plugins/storage.js +28 -33
- package/dist/server/plugins/sts.js +12 -17
- package/dist/server/plugins/uploader.js +40 -43
- package/dist/server/plugins/webpush.js +23 -51
- package/dist/server/server.js +318 -271
- package/dist/server/storageCleaner.js +29 -37
- package/dist/server/utils/fish.js +349 -389
- package/dist/shared/irc.js +3 -6
- package/dist/shared/linkify.js +7 -14
- package/dist/shared/types/chan.js +6 -9
- package/dist/shared/types/changelog.js +1 -2
- package/dist/shared/types/config.js +1 -2
- package/dist/shared/types/mention.js +1 -2
- package/dist/shared/types/msg.js +3 -5
- package/dist/shared/types/network.js +1 -2
- package/dist/shared/types/storage.js +1 -2
- package/dist/shared/types/user.js +1 -2
- package/index.js +14 -10
- package/package.json +94 -91
- package/public/css/style.css +9 -6
- package/public/css/style.css.map +1 -1
- package/public/fonts/font-awesome/fa-brands-400.ttf +0 -0
- package/public/fonts/font-awesome/fa-brands-400.woff2 +0 -0
- package/public/fonts/font-awesome/fa-duotone-900.ttf +0 -0
- package/public/fonts/font-awesome/fa-duotone-900.woff2 +0 -0
- package/public/fonts/font-awesome/fa-light-300.ttf +0 -0
- package/public/fonts/font-awesome/fa-light-300.woff2 +0 -0
- package/public/fonts/font-awesome/fa-regular-400.ttf +0 -0
- package/public/fonts/font-awesome/fa-regular-400.woff2 +0 -0
- package/public/fonts/font-awesome/fa-solid-900.ttf +0 -0
- package/public/fonts/font-awesome/fa-solid-900.woff2 +0 -0
- package/public/fonts/font-awesome/fa-thin-100.ttf +0 -0
- package/public/fonts/font-awesome/fa-thin-100.woff2 +0 -0
- package/public/fonts/font-awesome/fa-v4compatibility.ttf +0 -0
- package/public/fonts/font-awesome/fa-v4compatibility.woff2 +0 -0
- package/public/js/bundle.js +1 -1
- package/public/js/bundle.js.map +1 -1
- package/public/js/bundle.vendor.js +1 -1
- package/public/js/bundle.vendor.js.LICENSE.txt +24 -6
- package/public/js/bundle.vendor.js.map +1 -1
- package/public/js/loading-error-handlers.js +1 -1
- package/public/service-worker.js +1 -1
- package/public/themes/default.css +1 -1
- package/public/themes/morning.css +1 -1
- package/dist/webpack.config.js +0 -224
package/dist/server/server.js
CHANGED
|
@@ -1,71 +1,43 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
};
|
|
25
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
-
};
|
|
28
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
-
const lodash_1 = __importDefault(require("lodash"));
|
|
30
|
-
const ws_1 = require("ws");
|
|
31
|
-
const express_1 = __importDefault(require("express"));
|
|
32
|
-
const fs_1 = __importDefault(require("fs"));
|
|
33
|
-
const path_1 = __importDefault(require("path"));
|
|
34
|
-
const socket_io_1 = require("socket.io");
|
|
35
|
-
const dns_1 = __importDefault(require("dns"));
|
|
36
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
37
|
-
const net_1 = __importDefault(require("net"));
|
|
38
|
-
const log_1 = __importDefault(require("./log"));
|
|
39
|
-
const client_1 = __importDefault(require("./client"));
|
|
40
|
-
const clientManager_1 = __importDefault(require("./clientManager"));
|
|
41
|
-
const uploader_1 = __importDefault(require("./plugins/uploader"));
|
|
42
|
-
const helper_1 = __importDefault(require("./helper"));
|
|
43
|
-
const config_1 = __importDefault(require("./config"));
|
|
44
|
-
const identification_1 = __importDefault(require("./identification"));
|
|
45
|
-
const changelog_1 = __importDefault(require("./plugins/changelog"));
|
|
46
|
-
const inputs_1 = __importDefault(require("./plugins/inputs"));
|
|
47
|
-
const auth_1 = __importDefault(require("./plugins/auth"));
|
|
48
|
-
const themes_1 = __importDefault(require("./plugins/packages/themes"));
|
|
49
|
-
themes_1.default.loadLocalThemes();
|
|
50
|
-
const index_1 = __importDefault(require("./plugins/packages/index"));
|
|
51
|
-
const utils_1 = __importDefault(require("./command-line/utils"));
|
|
52
|
-
const chan_1 = require("../shared/types/chan");
|
|
1
|
+
import _ from "lodash";
|
|
2
|
+
import { WebSocketServer as wsServer } from "ws";
|
|
3
|
+
import express from "express";
|
|
4
|
+
import fs from "fs";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import { Server as ioServer } from "socket.io";
|
|
7
|
+
import dns from "dns";
|
|
8
|
+
import colors from "chalk";
|
|
9
|
+
import net from "net";
|
|
10
|
+
import log from "./log.js";
|
|
11
|
+
import Client from "./client.js";
|
|
12
|
+
import ClientManager from "./clientManager.js";
|
|
13
|
+
import Uploader from "./plugins/uploader.js";
|
|
14
|
+
import Helper from "./helper.js";
|
|
15
|
+
import Config from "./config.js";
|
|
16
|
+
import Identification from "./identification.js";
|
|
17
|
+
import changelog from "./plugins/changelog.js";
|
|
18
|
+
import inputs from "./plugins/inputs/index.js";
|
|
19
|
+
import Auth from "./plugins/auth.js";
|
|
20
|
+
import themes from "./plugins/packages/themes.js";
|
|
21
|
+
themes.loadLocalThemes();
|
|
22
|
+
import packages from "./plugins/packages/index.js";
|
|
23
|
+
import Utils from "./command-line/utils.js";
|
|
24
|
+
import { ChanType } from "../shared/types/chan.js";
|
|
53
25
|
// A random number that will force clients to reload the page if it differs
|
|
54
26
|
const serverHash = Math.floor(Date.now() * Math.random());
|
|
55
27
|
let manager = null;
|
|
56
|
-
async function
|
|
28
|
+
export default async function (options = {
|
|
57
29
|
dev: false,
|
|
58
30
|
}) {
|
|
59
|
-
|
|
60
|
-
(Node.js ${
|
|
61
|
-
|
|
31
|
+
log.info(`The Lounge ${colors.green(Helper.getVersion())} \
|
|
32
|
+
(Node.js ${colors.green(process.versions.node)} on ${colors.green(process.platform)} ${process.arch})`);
|
|
33
|
+
log.info(`Configuration file: ${colors.green(Config.getConfigPath())}`);
|
|
62
34
|
const staticOptions = {
|
|
63
35
|
redirect: false,
|
|
64
36
|
maxAge: 86400 * 1000,
|
|
65
37
|
};
|
|
66
|
-
const app = (
|
|
38
|
+
const app = express();
|
|
67
39
|
if (options.dev) {
|
|
68
|
-
(await
|
|
40
|
+
(await import("./plugins/dev-server.js")).default(app);
|
|
69
41
|
}
|
|
70
42
|
app.set("env", "production")
|
|
71
43
|
.disable("x-powered-by")
|
|
@@ -75,10 +47,10 @@ async function default_1(options = {
|
|
|
75
47
|
.get("/service-worker.js", forceNoCacheRequest)
|
|
76
48
|
.get("/js/bundle.js.map", forceNoCacheRequest)
|
|
77
49
|
.get("/css/style.css.map", forceNoCacheRequest)
|
|
78
|
-
.use(
|
|
79
|
-
.use("/storage/",
|
|
80
|
-
if (
|
|
81
|
-
|
|
50
|
+
.use(express.static(Utils.getFileFromRelativeToRoot("public"), staticOptions))
|
|
51
|
+
.use("/storage/", express.static(Config.getStoragePath(), staticOptions));
|
|
52
|
+
if (Config.values.fileUpload.enable) {
|
|
53
|
+
Uploader.router(app);
|
|
82
54
|
}
|
|
83
55
|
// This route serves *installed themes only*. Local themes are served directly
|
|
84
56
|
// from the `public/themes/` folder as static assets, without entering this
|
|
@@ -86,7 +58,7 @@ async function default_1(options = {
|
|
|
86
58
|
// local themes will not get those changes.
|
|
87
59
|
app.get("/themes/:theme.css", (req, res) => {
|
|
88
60
|
const themeName = encodeURIComponent(req.params.theme);
|
|
89
|
-
const theme =
|
|
61
|
+
const theme = themes.getByName(themeName);
|
|
90
62
|
if (theme === undefined || theme.filename === undefined) {
|
|
91
63
|
return res.status(404).send("Not found");
|
|
92
64
|
}
|
|
@@ -95,161 +67,196 @@ async function default_1(options = {
|
|
|
95
67
|
app.get("/packages/:package/:filename", (req, res) => {
|
|
96
68
|
const packageName = req.params.package;
|
|
97
69
|
const fileName = req.params.filename;
|
|
98
|
-
const packageFile =
|
|
99
|
-
if (!packageFile || !
|
|
70
|
+
const packageFile = packages.getPackage(packageName);
|
|
71
|
+
if (!packageFile || !packages.getFiles().includes(`${packageName}/${fileName}`)) {
|
|
100
72
|
return res.status(404).send("Not found");
|
|
101
73
|
}
|
|
102
|
-
const packagePath =
|
|
103
|
-
return res.sendFile(
|
|
74
|
+
const packagePath = Config.getPackageModulePath(packageName);
|
|
75
|
+
return res.sendFile(path.join(packagePath, fileName));
|
|
104
76
|
});
|
|
105
|
-
if (
|
|
106
|
-
|
|
77
|
+
if (Config.values.public && (Config.values.ldap || {}).enable) {
|
|
78
|
+
log.warn("Server is public and set to use LDAP. Set to private mode if trying to use LDAP authentication.");
|
|
107
79
|
}
|
|
108
80
|
let server;
|
|
109
|
-
if (!
|
|
110
|
-
const createServer = (await
|
|
81
|
+
if (!Config.values.https.enable) {
|
|
82
|
+
const createServer = (await import("http")).createServer;
|
|
111
83
|
server = createServer(app);
|
|
112
84
|
}
|
|
113
85
|
else {
|
|
114
|
-
const keyPath =
|
|
115
|
-
const certPath =
|
|
116
|
-
const caPath =
|
|
117
|
-
if (!keyPath.length || !
|
|
118
|
-
|
|
86
|
+
const keyPath = Helper.expandHome(Config.values.https.key);
|
|
87
|
+
const certPath = Helper.expandHome(Config.values.https.certificate);
|
|
88
|
+
const caPath = Helper.expandHome(Config.values.https.ca);
|
|
89
|
+
if (!keyPath.length || !fs.existsSync(keyPath)) {
|
|
90
|
+
log.error("Path to SSL key is invalid. Stopping server...");
|
|
119
91
|
process.exit(1);
|
|
120
92
|
}
|
|
121
|
-
if (!certPath.length || !
|
|
122
|
-
|
|
93
|
+
if (!certPath.length || !fs.existsSync(certPath)) {
|
|
94
|
+
log.error("Path to SSL certificate is invalid. Stopping server...");
|
|
123
95
|
process.exit(1);
|
|
124
96
|
}
|
|
125
|
-
if (caPath.length && !
|
|
126
|
-
|
|
97
|
+
if (caPath.length && !fs.existsSync(caPath)) {
|
|
98
|
+
log.error("Path to SSL ca bundle is invalid. Stopping server...");
|
|
127
99
|
process.exit(1);
|
|
128
100
|
}
|
|
129
|
-
const createServer = (await
|
|
101
|
+
const createServer = (await import("https")).createServer;
|
|
130
102
|
server = createServer({
|
|
131
|
-
key:
|
|
132
|
-
cert:
|
|
133
|
-
ca: caPath ?
|
|
103
|
+
key: fs.readFileSync(keyPath),
|
|
104
|
+
cert: fs.readFileSync(certPath),
|
|
105
|
+
ca: caPath ? fs.readFileSync(caPath) : undefined,
|
|
134
106
|
}, app);
|
|
135
107
|
}
|
|
136
108
|
let listenParams;
|
|
137
|
-
if (typeof
|
|
138
|
-
listenParams =
|
|
109
|
+
if (typeof Config.values.host === "string" && Config.values.host.startsWith("unix:")) {
|
|
110
|
+
listenParams = Config.values.host.replace(/^unix:/, "");
|
|
139
111
|
}
|
|
140
112
|
else {
|
|
141
113
|
listenParams = {
|
|
142
|
-
port:
|
|
143
|
-
host:
|
|
114
|
+
port: Config.values.port,
|
|
115
|
+
host: Config.values.host,
|
|
144
116
|
};
|
|
145
117
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const address = server?.address();
|
|
155
|
-
if (address && typeof address !== "string") {
|
|
156
|
-
// TODO: Node may revert the Node 18 family string --> number change
|
|
157
|
-
// @ts-expect-error This condition will always return 'false' since the types 'string' and 'number' have no overlap.
|
|
158
|
-
if (address.family === "IPv6" || address.family === 6) {
|
|
159
|
-
address.address = "[" + address.address + "]";
|
|
118
|
+
server.on("error", (err) => log.error(`${err}`));
|
|
119
|
+
let sockets = null;
|
|
120
|
+
let identd = null;
|
|
121
|
+
return new Promise((resolve) => {
|
|
122
|
+
server.listen(listenParams, () => {
|
|
123
|
+
void (async () => {
|
|
124
|
+
if (typeof listenParams === "string") {
|
|
125
|
+
log.info("Available on socket " + colors.green(listenParams));
|
|
160
126
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
transports: config_1.default.values.transports,
|
|
176
|
-
pingTimeout: 60000,
|
|
177
|
-
});
|
|
178
|
-
sockets.on("connect", (socket) => {
|
|
179
|
-
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
180
|
-
socket.on("error", (err) => log_1.default.error(`io socket error: ${err}`));
|
|
181
|
-
if (config_1.default.values.public) {
|
|
182
|
-
performAuthentication.call(socket, {});
|
|
183
|
-
}
|
|
184
|
-
else {
|
|
185
|
-
socket.on("auth:perform", performAuthentication);
|
|
186
|
-
socket.emit("auth:start", serverHash);
|
|
187
|
-
}
|
|
188
|
-
});
|
|
189
|
-
manager = new clientManager_1.default();
|
|
190
|
-
index_1.default.loadPackages();
|
|
191
|
-
const defaultTheme = themes_1.default.getByName(config_1.default.values.theme);
|
|
192
|
-
if (defaultTheme === undefined) {
|
|
193
|
-
log_1.default.warn(`The specified default theme "${chalk_1.default.red(config_1.default.values.theme)}" does not exist, verify your config.`);
|
|
194
|
-
config_1.default.values.theme = "default";
|
|
195
|
-
}
|
|
196
|
-
else if (defaultTheme.themeColor) {
|
|
197
|
-
config_1.default.values.themeColor = defaultTheme.themeColor;
|
|
198
|
-
}
|
|
199
|
-
new identification_1.default((identHandler, err) => {
|
|
200
|
-
if (err) {
|
|
201
|
-
log_1.default.error(`Could not start identd server, ${err.message}`);
|
|
202
|
-
process.exit(1);
|
|
203
|
-
}
|
|
204
|
-
else if (!manager) {
|
|
205
|
-
log_1.default.error("Could not start identd server, ClientManager is undefined");
|
|
206
|
-
process.exit(1);
|
|
207
|
-
}
|
|
208
|
-
manager.init(identHandler, sockets);
|
|
209
|
-
});
|
|
210
|
-
// Handle ctrl+c and kill gracefully
|
|
211
|
-
let suicideTimeout = null;
|
|
212
|
-
const exitGracefully = async function () {
|
|
213
|
-
if (suicideTimeout !== null) {
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
log_1.default.info("Exiting...");
|
|
217
|
-
// Close all client and IRC connections
|
|
218
|
-
if (manager) {
|
|
219
|
-
manager.clients.forEach((client) => client.quit());
|
|
220
|
-
}
|
|
221
|
-
if (config_1.default.values.prefetchStorage) {
|
|
222
|
-
log_1.default.info("Clearing prefetch storage folder, this might take a while...");
|
|
223
|
-
(await Promise.resolve().then(() => __importStar(require("./plugins/storage")))).default.emptyDir();
|
|
224
|
-
}
|
|
225
|
-
// Forcefully exit after 3 seconds
|
|
226
|
-
suicideTimeout = setTimeout(() => process.exit(1), 3000);
|
|
227
|
-
// Close http server
|
|
228
|
-
server?.close(() => {
|
|
229
|
-
if (suicideTimeout !== null) {
|
|
230
|
-
clearTimeout(suicideTimeout);
|
|
127
|
+
else {
|
|
128
|
+
const protocol = Config.values.https.enable ? "https" : "http";
|
|
129
|
+
const address = server?.address();
|
|
130
|
+
if (address && typeof address !== "string") {
|
|
131
|
+
// Node.js changed address.family from number (4, 6) to string ("IPv4", "IPv6")
|
|
132
|
+
// in v18, but may revert this change. Support both formats for compatibility.
|
|
133
|
+
const family = address.family;
|
|
134
|
+
if (family === "IPv6" || family === 6) {
|
|
135
|
+
address.address = "[" + address.address + "]";
|
|
136
|
+
}
|
|
137
|
+
log.info("Available at " +
|
|
138
|
+
colors.green(`${protocol}://${address.address}:${address.port}/`) +
|
|
139
|
+
` in ${colors.bold(Config.values.public ? "public" : "private")} mode`);
|
|
140
|
+
}
|
|
231
141
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
142
|
+
// This should never happen
|
|
143
|
+
if (!server) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
sockets = new ioServer(server, {
|
|
147
|
+
wsEngine: wsServer,
|
|
148
|
+
cookie: false,
|
|
149
|
+
serveClient: false,
|
|
150
|
+
transports: Config.values.transports,
|
|
151
|
+
pingTimeout: 60000,
|
|
152
|
+
});
|
|
153
|
+
sockets.on("connect", (socket) => {
|
|
154
|
+
socket.on("error", (err) => log.error(`io socket error: ${err}`));
|
|
155
|
+
if (Config.values.public) {
|
|
156
|
+
performAuthentication.call(socket, {});
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
socket.on("auth:perform", performAuthentication);
|
|
160
|
+
socket.emit("auth:start", serverHash);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
manager = new ClientManager();
|
|
164
|
+
await packages.loadPackages();
|
|
165
|
+
const defaultTheme = themes.getByName(Config.values.theme);
|
|
166
|
+
if (defaultTheme === undefined) {
|
|
167
|
+
log.warn(`The specified default theme "${colors.red(Config.values.theme)}" does not exist, verify your config.`);
|
|
168
|
+
Config.values.theme = "default";
|
|
169
|
+
}
|
|
170
|
+
else if (defaultTheme.themeColor) {
|
|
171
|
+
Config.values.themeColor = defaultTheme.themeColor;
|
|
172
|
+
}
|
|
173
|
+
new Identification((identHandler, err) => {
|
|
174
|
+
if (err) {
|
|
175
|
+
log.error(`Could not start identd server, ${err.message}`);
|
|
176
|
+
process.exit(1);
|
|
177
|
+
}
|
|
178
|
+
else if (!manager) {
|
|
179
|
+
log.error("Could not start identd server, ClientManager is undefined");
|
|
180
|
+
process.exit(1);
|
|
181
|
+
}
|
|
182
|
+
identd = identHandler;
|
|
183
|
+
manager.init(identHandler, sockets);
|
|
184
|
+
});
|
|
185
|
+
// Handle ctrl+c and kill gracefully
|
|
186
|
+
let suicideTimeout = null;
|
|
187
|
+
const exitGracefully = function () {
|
|
188
|
+
if (suicideTimeout !== null) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
log.info("Exiting...");
|
|
192
|
+
// Close all client and IRC connections
|
|
193
|
+
if (manager) {
|
|
194
|
+
manager.clients.forEach((client) => client.quit());
|
|
195
|
+
}
|
|
196
|
+
if (Config.values.prefetchStorage) {
|
|
197
|
+
log.info("Clearing prefetch storage folder, this might take a while...");
|
|
198
|
+
import("./plugins/storage.js")
|
|
199
|
+
.then(({ default: storage }) => {
|
|
200
|
+
storage.emptyDir();
|
|
201
|
+
})
|
|
202
|
+
.catch((err) => {
|
|
203
|
+
log.error(`Could not clear storage folder, ${err.message}`);
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
// Forcefully exit after 3 seconds
|
|
207
|
+
suicideTimeout = setTimeout(() => process.exit(1), 3000);
|
|
208
|
+
// Close http server
|
|
209
|
+
server?.close(() => {
|
|
210
|
+
if (suicideTimeout !== null) {
|
|
211
|
+
clearTimeout(suicideTimeout);
|
|
212
|
+
}
|
|
213
|
+
process.exit(0);
|
|
214
|
+
});
|
|
215
|
+
};
|
|
216
|
+
process.on("SIGINT", exitGracefully);
|
|
217
|
+
process.on("SIGTERM", exitGracefully);
|
|
218
|
+
// Clear storage folder after server starts successfully
|
|
219
|
+
if (Config.values.prefetchStorage) {
|
|
220
|
+
import("./plugins/storage.js")
|
|
221
|
+
.then(({ default: storage }) => {
|
|
222
|
+
storage.emptyDir();
|
|
223
|
+
})
|
|
224
|
+
.catch((err) => {
|
|
225
|
+
log.error(`Could not clear storage folder, ${err.message}`);
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
changelog.checkForUpdates(manager);
|
|
229
|
+
// Create stop method that properly closes everything
|
|
230
|
+
const stop = (callback) => {
|
|
231
|
+
// Stop changelog update checks to clear the 24-hour timeout
|
|
232
|
+
changelog.stopUpdateChecks();
|
|
233
|
+
// Stop file watchers
|
|
234
|
+
packages.stopWatching();
|
|
235
|
+
if (manager) {
|
|
236
|
+
manager.stopAutoloadUsers();
|
|
237
|
+
}
|
|
238
|
+
// Remove signal handlers to prevent double-close on SIGTERM/SIGINT
|
|
239
|
+
process.removeListener("SIGINT", exitGracefully);
|
|
240
|
+
process.removeListener("SIGTERM", exitGracefully);
|
|
241
|
+
// Close identd first, then HTTP server (which will auto-close Socket.IO)
|
|
242
|
+
if (identd) {
|
|
243
|
+
identd.close(() => {
|
|
244
|
+
server.close(callback);
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
server.close(callback);
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
resolve({
|
|
252
|
+
httpServer: server,
|
|
253
|
+
io: sockets,
|
|
254
|
+
stop,
|
|
255
|
+
});
|
|
256
|
+
})();
|
|
257
|
+
});
|
|
249
258
|
});
|
|
250
|
-
return server;
|
|
251
259
|
}
|
|
252
|
-
exports.default = default_1;
|
|
253
260
|
function getClientLanguage(socket) {
|
|
254
261
|
const acceptLanguage = socket.handshake.headers["accept-language"];
|
|
255
262
|
if (typeof acceptLanguage === "string" && /^[\x00-\x7F]{1,50}$/.test(acceptLanguage)) {
|
|
@@ -260,11 +267,11 @@ function getClientLanguage(socket) {
|
|
|
260
267
|
}
|
|
261
268
|
function getClientIp(socket) {
|
|
262
269
|
let ip = socket.handshake.address || "127.0.0.1";
|
|
263
|
-
if (
|
|
270
|
+
if (Config.values.reverseProxy) {
|
|
264
271
|
const forwarded = String(socket.handshake.headers["x-forwarded-for"])
|
|
265
272
|
.split(/\s*,\s*/)
|
|
266
273
|
.filter(Boolean);
|
|
267
|
-
if (forwarded.length &&
|
|
274
|
+
if (forwarded.length && net.isIP(forwarded[0])) {
|
|
268
275
|
ip = forwarded[0];
|
|
269
276
|
}
|
|
270
277
|
}
|
|
@@ -272,7 +279,7 @@ function getClientIp(socket) {
|
|
|
272
279
|
}
|
|
273
280
|
function getClientSecure(socket) {
|
|
274
281
|
let secure = socket.handshake.secure;
|
|
275
|
-
if (
|
|
282
|
+
if (Config.values.reverseProxy && socket.handshake.headers["x-forwarded-proto"] === "https") {
|
|
276
283
|
secure = true;
|
|
277
284
|
}
|
|
278
285
|
return secure;
|
|
@@ -297,7 +304,7 @@ function addSecurityHeaders(_req, res, next) {
|
|
|
297
304
|
// If prefetch is enabled, but storage is not, we have to allow mixed content
|
|
298
305
|
// - https://user-images.githubusercontent.com is where we currently push our changelog screenshots
|
|
299
306
|
// - data: is required for the HTML5 video player
|
|
300
|
-
if (
|
|
307
|
+
if (Config.values.prefetchStorage || !Config.values.prefetch) {
|
|
301
308
|
policies.push("img-src 'self' data: https://user-images.githubusercontent.com");
|
|
302
309
|
policies.unshift("block-all-mixed-content");
|
|
303
310
|
}
|
|
@@ -316,17 +323,17 @@ function forceNoCacheRequest(_req, res, next) {
|
|
|
316
323
|
}
|
|
317
324
|
function indexRequest(_req, res) {
|
|
318
325
|
res.setHeader("Content-Type", "text/html");
|
|
319
|
-
|
|
326
|
+
fs.readFile(Utils.getFileFromRelativeToRoot("client/index.html.tpl"), "utf-8", (err, file) => {
|
|
320
327
|
if (err) {
|
|
321
|
-
|
|
328
|
+
log.error(`failed to server index request: ${err.name}, ${err.message}`);
|
|
322
329
|
res.sendStatus(500);
|
|
323
330
|
return;
|
|
324
331
|
}
|
|
325
332
|
const config = {
|
|
326
333
|
...getServerConfiguration(),
|
|
327
|
-
...{ cacheBust:
|
|
334
|
+
...{ cacheBust: Helper.getVersionCacheBust() },
|
|
328
335
|
};
|
|
329
|
-
res.send(
|
|
336
|
+
res.send(_.template(file)(config));
|
|
330
337
|
});
|
|
331
338
|
}
|
|
332
339
|
function initializeClient(socket, client, token, lastMessage, openChannel) {
|
|
@@ -344,27 +351,43 @@ function initializeClient(socket, client, token, lastMessage, openChannel) {
|
|
|
344
351
|
else {
|
|
345
352
|
openChannel = client.lastActiveChannel;
|
|
346
353
|
}
|
|
347
|
-
|
|
348
|
-
|
|
354
|
+
// If no valid active channel, default to the first channel of the first network
|
|
355
|
+
if (!openChannel || openChannel < 0) {
|
|
356
|
+
const firstNetwork = client.networks[0];
|
|
357
|
+
if (firstNetwork && firstNetwork.channels.length > 0) {
|
|
358
|
+
openChannel = firstNetwork.channels[0].id;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
if (Config.values.fileUpload.enable) {
|
|
362
|
+
new Uploader(socket);
|
|
349
363
|
}
|
|
350
364
|
socket.on("disconnect", function () {
|
|
351
365
|
process.nextTick(() => client.clientDetach(socket.id));
|
|
352
366
|
});
|
|
353
367
|
socket.on("input", (data) => {
|
|
354
|
-
if (
|
|
368
|
+
if (_.isPlainObject(data)) {
|
|
355
369
|
client.input(data);
|
|
356
370
|
}
|
|
357
371
|
});
|
|
358
372
|
socket.on("more", (data) => {
|
|
359
|
-
if (
|
|
373
|
+
if (_.isPlainObject(data)) {
|
|
360
374
|
const history = client.more(data);
|
|
361
375
|
if (history !== null) {
|
|
362
376
|
socket.emit("more", history);
|
|
363
377
|
}
|
|
364
378
|
}
|
|
365
379
|
});
|
|
380
|
+
// Fetch messages around a specific time (for jumping to search results)
|
|
381
|
+
socket.on("messages:around", async (data) => {
|
|
382
|
+
if (_.isPlainObject(data)) {
|
|
383
|
+
const result = await client.getMessagesAround(data);
|
|
384
|
+
if (result !== null) {
|
|
385
|
+
socket.emit("messages:around", result);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
});
|
|
366
389
|
socket.on("network:new", (data) => {
|
|
367
|
-
if (
|
|
390
|
+
if (_.isPlainObject(data)) {
|
|
368
391
|
// prevent people from overriding webirc settings
|
|
369
392
|
data.uuid = null;
|
|
370
393
|
data.commands = null;
|
|
@@ -376,30 +399,30 @@ function initializeClient(socket, client, token, lastMessage, openChannel) {
|
|
|
376
399
|
if (typeof data !== "string") {
|
|
377
400
|
return;
|
|
378
401
|
}
|
|
379
|
-
const network =
|
|
402
|
+
const network = _.find(client.networks, { uuid: data });
|
|
380
403
|
if (!network) {
|
|
381
404
|
return;
|
|
382
405
|
}
|
|
383
406
|
socket.emit("network:info", network.exportForEdit());
|
|
384
407
|
});
|
|
385
408
|
socket.on("network:edit", (data) => {
|
|
386
|
-
if (!
|
|
409
|
+
if (!_.isPlainObject(data)) {
|
|
387
410
|
return;
|
|
388
411
|
}
|
|
389
|
-
const network =
|
|
412
|
+
const network = _.find(client.networks, { uuid: data.uuid });
|
|
390
413
|
if (!network) {
|
|
391
414
|
return;
|
|
392
415
|
}
|
|
393
416
|
network.edit(client, data);
|
|
394
417
|
});
|
|
395
418
|
socket.on("history:clear", (data) => {
|
|
396
|
-
if (
|
|
419
|
+
if (_.isPlainObject(data)) {
|
|
397
420
|
client.clearHistory(data);
|
|
398
421
|
}
|
|
399
422
|
});
|
|
400
|
-
if (!
|
|
423
|
+
if (!Config.values.public && !Config.values.ldap.enable) {
|
|
401
424
|
socket.on("change-password", (data) => {
|
|
402
|
-
if (
|
|
425
|
+
if (_.isPlainObject(data)) {
|
|
403
426
|
const old = data.old_password;
|
|
404
427
|
const p1 = data.new_password;
|
|
405
428
|
const p2 = data.verify_password;
|
|
@@ -410,7 +433,7 @@ function initializeClient(socket, client, token, lastMessage, openChannel) {
|
|
|
410
433
|
});
|
|
411
434
|
return;
|
|
412
435
|
}
|
|
413
|
-
|
|
436
|
+
Helper.password
|
|
414
437
|
.compare(old || "", client.config.password)
|
|
415
438
|
.then((matching) => {
|
|
416
439
|
if (!matching) {
|
|
@@ -420,7 +443,7 @@ function initializeClient(socket, client, token, lastMessage, openChannel) {
|
|
|
420
443
|
});
|
|
421
444
|
return;
|
|
422
445
|
}
|
|
423
|
-
const hash =
|
|
446
|
+
const hash = Helper.password.hash(p1);
|
|
424
447
|
client.setPassword(hash, (success) => {
|
|
425
448
|
socket.emit("change-password", {
|
|
426
449
|
success: success,
|
|
@@ -429,7 +452,7 @@ function initializeClient(socket, client, token, lastMessage, openChannel) {
|
|
|
429
452
|
});
|
|
430
453
|
})
|
|
431
454
|
.catch((error) => {
|
|
432
|
-
|
|
455
|
+
log.error(`Error while checking users password. Error: ${error.message}`);
|
|
433
456
|
});
|
|
434
457
|
}
|
|
435
458
|
});
|
|
@@ -438,7 +461,7 @@ function initializeClient(socket, client, token, lastMessage, openChannel) {
|
|
|
438
461
|
client.open(socket.id, data);
|
|
439
462
|
});
|
|
440
463
|
socket.on("sort:networks", (data) => {
|
|
441
|
-
if (!
|
|
464
|
+
if (!_.isPlainObject(data)) {
|
|
442
465
|
return;
|
|
443
466
|
}
|
|
444
467
|
if (!Array.isArray(data.order)) {
|
|
@@ -447,7 +470,7 @@ function initializeClient(socket, client, token, lastMessage, openChannel) {
|
|
|
447
470
|
client.sortNetworks(data.order);
|
|
448
471
|
});
|
|
449
472
|
socket.on("sort:channels", (data) => {
|
|
450
|
-
if (!
|
|
473
|
+
if (!_.isPlainObject(data)) {
|
|
451
474
|
return;
|
|
452
475
|
}
|
|
453
476
|
if (!Array.isArray(data.order) || typeof data.network !== "string") {
|
|
@@ -456,25 +479,25 @@ function initializeClient(socket, client, token, lastMessage, openChannel) {
|
|
|
456
479
|
client.sortChannels(data.network, data.order);
|
|
457
480
|
});
|
|
458
481
|
socket.on("names", (data) => {
|
|
459
|
-
if (
|
|
482
|
+
if (_.isPlainObject(data)) {
|
|
460
483
|
client.names(data);
|
|
461
484
|
}
|
|
462
485
|
});
|
|
463
486
|
socket.on("changelog", () => {
|
|
464
|
-
Promise.all([
|
|
487
|
+
Promise.all([changelog.fetch(), packages.outdated()])
|
|
465
488
|
.then(([changelogData, packageUpdate]) => {
|
|
466
489
|
changelogData.packages = packageUpdate;
|
|
467
490
|
socket.emit("changelog", changelogData);
|
|
468
491
|
})
|
|
469
492
|
.catch((error) => {
|
|
470
|
-
|
|
493
|
+
log.error(`Error while fetching changelog. Error: ${error.message}`);
|
|
471
494
|
});
|
|
472
495
|
});
|
|
473
496
|
// In public mode only one client can be connected,
|
|
474
497
|
// so there's no need to handle msg:preview:toggle
|
|
475
|
-
if (!
|
|
498
|
+
if (!Config.values.public) {
|
|
476
499
|
socket.on("msg:preview:toggle", (data) => {
|
|
477
|
-
if (
|
|
500
|
+
if (_.isPlainObject(data)) {
|
|
478
501
|
return;
|
|
479
502
|
}
|
|
480
503
|
const networkAndChan = client.find(data.target);
|
|
@@ -516,7 +539,7 @@ function initializeClient(socket, client, token, lastMessage, openChannel) {
|
|
|
516
539
|
socket.on("mentions:dismiss_all", () => {
|
|
517
540
|
client.mentions = [];
|
|
518
541
|
});
|
|
519
|
-
if (!
|
|
542
|
+
if (!Config.values.public) {
|
|
520
543
|
socket.on("push:register", (subscription) => {
|
|
521
544
|
if (!Object.prototype.hasOwnProperty.call(client.config.sessions, token)) {
|
|
522
545
|
return;
|
|
@@ -535,10 +558,10 @@ function initializeClient(socket, client, token, lastMessage, openChannel) {
|
|
|
535
558
|
}
|
|
536
559
|
const sendSessionList = () => {
|
|
537
560
|
// TODO: this should use the ClientSession type currently in client
|
|
538
|
-
const sessions =
|
|
561
|
+
const sessions = _.map(client.config.sessions, (session, sessionToken) => {
|
|
539
562
|
return {
|
|
540
563
|
current: sessionToken === token,
|
|
541
|
-
active:
|
|
564
|
+
active: _.reduce(client.attachedClients, (count, attachedClient) => count + (attachedClient.token === sessionToken ? 1 : 0), 0),
|
|
542
565
|
lastUse: session.lastUse,
|
|
543
566
|
ip: session.ip,
|
|
544
567
|
agent: session.agent,
|
|
@@ -548,9 +571,9 @@ function initializeClient(socket, client, token, lastMessage, openChannel) {
|
|
|
548
571
|
socket.emit("sessions:list", sessions);
|
|
549
572
|
};
|
|
550
573
|
socket.on("sessions:get", sendSessionList);
|
|
551
|
-
if (!
|
|
574
|
+
if (!Config.values.public) {
|
|
552
575
|
socket.on("setting:set", (newSetting) => {
|
|
553
|
-
if (!
|
|
576
|
+
if (!_.isPlainObject(newSetting)) {
|
|
554
577
|
return;
|
|
555
578
|
}
|
|
556
579
|
if (typeof newSetting.value === "object" ||
|
|
@@ -597,15 +620,15 @@ function initializeClient(socket, client, token, lastMessage, openChannel) {
|
|
|
597
620
|
}
|
|
598
621
|
const { chan, network } = networkAndChan;
|
|
599
622
|
// If the user mutes the lobby, we mute the entire network.
|
|
600
|
-
if (chan.type ===
|
|
623
|
+
if (chan.type === ChanType.LOBBY) {
|
|
601
624
|
for (const channel of network.channels) {
|
|
602
|
-
if (channel.type !==
|
|
625
|
+
if (channel.type !== ChanType.SPECIAL) {
|
|
603
626
|
channel.setMuteStatus(setMutedTo);
|
|
604
627
|
}
|
|
605
628
|
}
|
|
606
629
|
}
|
|
607
630
|
else {
|
|
608
|
-
if (chan.type !==
|
|
631
|
+
if (chan.type !== ChanType.SPECIAL) {
|
|
609
632
|
chan.setMuteStatus(setMutedTo);
|
|
610
633
|
}
|
|
611
634
|
}
|
|
@@ -617,6 +640,25 @@ function initializeClient(socket, client, token, lastMessage, openChannel) {
|
|
|
617
640
|
}
|
|
618
641
|
client.save();
|
|
619
642
|
});
|
|
643
|
+
socket.on("pin:change", ({ target, setPinnedTo }) => {
|
|
644
|
+
const networkAndChan = client.find(target);
|
|
645
|
+
if (!networkAndChan) {
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
const { chan } = networkAndChan;
|
|
649
|
+
// Only allow pinning queries
|
|
650
|
+
if (chan.type !== ChanType.QUERY) {
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
chan.pinned = setPinnedTo;
|
|
654
|
+
for (const attachedClient of Object.keys(client.attachedClients)) {
|
|
655
|
+
manager.sockets.in(attachedClient).emit("pin:changed", {
|
|
656
|
+
target,
|
|
657
|
+
status: setPinnedTo,
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
client.save();
|
|
661
|
+
});
|
|
620
662
|
}
|
|
621
663
|
socket.on("sign-out", (tokenToSignOut) => {
|
|
622
664
|
// If no token provided, sign same client out
|
|
@@ -628,7 +670,7 @@ function initializeClient(socket, client, token, lastMessage, openChannel) {
|
|
|
628
670
|
}
|
|
629
671
|
delete client.config.sessions[tokenToSignOut];
|
|
630
672
|
client.save();
|
|
631
|
-
|
|
673
|
+
_.map(client.attachedClients, (attachedClient, socketId) => {
|
|
632
674
|
if (attachedClient.token !== tokenToSignOut) {
|
|
633
675
|
return;
|
|
634
676
|
}
|
|
@@ -649,9 +691,9 @@ function initializeClient(socket, client, token, lastMessage, openChannel) {
|
|
|
649
691
|
networks: client.networks.map((network) => network.getFilteredClone(openChannel, lastMessage)),
|
|
650
692
|
token: tokenToSend,
|
|
651
693
|
});
|
|
652
|
-
socket.emit("commands",
|
|
694
|
+
socket.emit("commands", inputs.getCommands());
|
|
653
695
|
};
|
|
654
|
-
if (
|
|
696
|
+
if (Config.values.public) {
|
|
655
697
|
sendInitEvent();
|
|
656
698
|
}
|
|
657
699
|
else if (!token) {
|
|
@@ -669,59 +711,58 @@ function initializeClient(socket, client, token, lastMessage, openChannel) {
|
|
|
669
711
|
}
|
|
670
712
|
function getClientConfiguration() {
|
|
671
713
|
const common = {
|
|
672
|
-
fileUpload:
|
|
673
|
-
ldapEnabled:
|
|
674
|
-
isUpdateAvailable:
|
|
714
|
+
fileUpload: Config.values.fileUpload.enable,
|
|
715
|
+
ldapEnabled: Config.values.ldap.enable,
|
|
716
|
+
isUpdateAvailable: changelog.isUpdateAvailable,
|
|
675
717
|
applicationServerKey: manager.webPush.vapidKeys.publicKey,
|
|
676
|
-
version:
|
|
677
|
-
gitCommit:
|
|
678
|
-
themes:
|
|
679
|
-
defaultTheme:
|
|
680
|
-
public:
|
|
681
|
-
useHexIp:
|
|
682
|
-
prefetch:
|
|
683
|
-
fileUploadMaxFileSize:
|
|
718
|
+
version: Helper.getVersionNumber(),
|
|
719
|
+
gitCommit: Helper.getGitCommit(),
|
|
720
|
+
themes: themes.getAll(),
|
|
721
|
+
defaultTheme: Config.values.theme,
|
|
722
|
+
public: Config.values.public,
|
|
723
|
+
useHexIp: Config.values.useHexIp,
|
|
724
|
+
prefetch: Config.values.prefetch,
|
|
725
|
+
fileUploadMaxFileSize: Uploader ? Uploader.getMaxFileSize() : undefined, // TODO can't be undefined?
|
|
684
726
|
};
|
|
685
727
|
const defaultsOverride = {
|
|
686
|
-
nick:
|
|
728
|
+
nick: Config.getDefaultNick(), // expand the number part
|
|
687
729
|
// TODO: this doesn't seem right, if the client needs this as a buffer
|
|
688
730
|
// the client ought to add it on its own
|
|
689
731
|
sasl: "",
|
|
690
732
|
saslAccount: "",
|
|
691
733
|
saslPassword: "",
|
|
692
734
|
};
|
|
693
|
-
if (!
|
|
735
|
+
if (!Config.values.lockNetwork) {
|
|
694
736
|
const defaults = {
|
|
695
|
-
...
|
|
737
|
+
..._.clone(Config.values.defaults),
|
|
696
738
|
...defaultsOverride,
|
|
697
739
|
};
|
|
698
740
|
const result = {
|
|
699
741
|
...common,
|
|
700
742
|
defaults: defaults,
|
|
701
|
-
lockNetwork:
|
|
743
|
+
lockNetwork: Config.values.lockNetwork,
|
|
702
744
|
};
|
|
703
745
|
return result;
|
|
704
746
|
}
|
|
705
747
|
// Only send defaults that are visible on the client
|
|
706
748
|
const defaults = {
|
|
707
|
-
...
|
|
749
|
+
..._.omit(Config.values.defaults, ["host", "name", "port", "tls", "rejectUnauthorized"]),
|
|
708
750
|
...defaultsOverride,
|
|
709
751
|
};
|
|
710
752
|
const result = {
|
|
711
753
|
...common,
|
|
712
|
-
lockNetwork:
|
|
754
|
+
lockNetwork: Config.values.lockNetwork,
|
|
713
755
|
defaults: defaults,
|
|
714
756
|
};
|
|
715
757
|
return result;
|
|
716
758
|
}
|
|
717
759
|
function getServerConfiguration() {
|
|
718
|
-
return { ...
|
|
760
|
+
return { ...Config.values, ...{ stylesheets: packages.getStylesheets() } };
|
|
719
761
|
}
|
|
720
762
|
function performAuthentication(data) {
|
|
721
|
-
if (!
|
|
763
|
+
if (!_.isPlainObject(data)) {
|
|
722
764
|
return;
|
|
723
765
|
}
|
|
724
|
-
const socket = this;
|
|
725
766
|
let client;
|
|
726
767
|
let token;
|
|
727
768
|
const finalInit = () => {
|
|
@@ -739,7 +780,7 @@ function performAuthentication(data) {
|
|
|
739
780
|
if (!client) {
|
|
740
781
|
throw new Error("finalInit called with undefined client, this is a bug");
|
|
741
782
|
}
|
|
742
|
-
initializeClient(
|
|
783
|
+
initializeClient(this, client, token, lastMessage, openChannel);
|
|
743
784
|
};
|
|
744
785
|
const initClient = () => {
|
|
745
786
|
if (!client) {
|
|
@@ -748,17 +789,17 @@ function performAuthentication(data) {
|
|
|
748
789
|
// Configuration does not change during runtime of TL,
|
|
749
790
|
// and the client listens to this event only once
|
|
750
791
|
if (data && (!("hasConfig" in data) || !data.hasConfig)) {
|
|
751
|
-
|
|
752
|
-
|
|
792
|
+
this.emit("configuration", getClientConfiguration());
|
|
793
|
+
this.emit("push:issubscribed", token && client.config.sessions[token].pushSubscription ? true : false);
|
|
753
794
|
}
|
|
754
|
-
const clientIP = getClientIp(
|
|
795
|
+
const clientIP = getClientIp(this);
|
|
755
796
|
client.config.browser = {
|
|
756
797
|
ip: clientIP,
|
|
757
|
-
isSecure: getClientSecure(
|
|
758
|
-
language: getClientLanguage(
|
|
798
|
+
isSecure: getClientSecure(this),
|
|
799
|
+
language: getClientLanguage(this),
|
|
759
800
|
};
|
|
760
801
|
// If webirc is enabled perform reverse dns lookup
|
|
761
|
-
if (
|
|
802
|
+
if (Config.values.webirc === null) {
|
|
762
803
|
return finalInit();
|
|
763
804
|
}
|
|
764
805
|
const cb_client = client; // ensure that TS figures out that client can't be nil
|
|
@@ -767,13 +808,13 @@ function performAuthentication(data) {
|
|
|
767
808
|
finalInit();
|
|
768
809
|
});
|
|
769
810
|
};
|
|
770
|
-
if (
|
|
771
|
-
client = new
|
|
811
|
+
if (Config.values.public) {
|
|
812
|
+
client = new Client(manager);
|
|
772
813
|
client.connect();
|
|
773
814
|
manager.clients.push(client);
|
|
774
815
|
const cb_client = client; // ensure TS can see we never have a nil client
|
|
775
|
-
|
|
776
|
-
manager.clients =
|
|
816
|
+
this.on("disconnect", () => {
|
|
817
|
+
manager.clients = _.without(manager.clients, cb_client);
|
|
777
818
|
cb_client.quit();
|
|
778
819
|
});
|
|
779
820
|
initClient();
|
|
@@ -786,12 +827,12 @@ function performAuthentication(data) {
|
|
|
786
827
|
// Authorization failed
|
|
787
828
|
if (!success) {
|
|
788
829
|
if (!client) {
|
|
789
|
-
|
|
830
|
+
log.warn(`Authentication for non existing user attempted from ${colors.bold(getClientIp(this))}`);
|
|
790
831
|
}
|
|
791
832
|
else {
|
|
792
|
-
|
|
833
|
+
log.warn(`Authentication failed for user ${colors.bold(data.user)} from ${colors.bold(getClientIp(this))}`);
|
|
793
834
|
}
|
|
794
|
-
|
|
835
|
+
this.emit("auth:failed");
|
|
795
836
|
return;
|
|
796
837
|
}
|
|
797
838
|
// If authorization succeeded but there is no loaded user,
|
|
@@ -815,13 +856,19 @@ function performAuthentication(data) {
|
|
|
815
856
|
}
|
|
816
857
|
}
|
|
817
858
|
if (!("user" in data && "password" in data)) {
|
|
818
|
-
|
|
859
|
+
log.warn("performAuthentication: callback data has no user or no password");
|
|
860
|
+
authCallback(false);
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
if (!manager) {
|
|
864
|
+
log.error("performAuthentication: ClientManager not initialized");
|
|
819
865
|
authCallback(false);
|
|
820
866
|
return;
|
|
821
867
|
}
|
|
822
|
-
|
|
868
|
+
const clientManager = manager;
|
|
869
|
+
void Auth.initialize().then(() => {
|
|
823
870
|
// Perform password checking
|
|
824
|
-
|
|
871
|
+
Auth.auth(clientManager, client, data.user, data.password, authCallback);
|
|
825
872
|
});
|
|
826
873
|
}
|
|
827
874
|
function reverseDnsLookup(ip, callback) {
|
|
@@ -829,11 +876,11 @@ function reverseDnsLookup(ip, callback) {
|
|
|
829
876
|
// returning SERVFAIL it seems: https://github.com/thelounge/thelounge/issues/4768
|
|
830
877
|
// so we manually resolve with the ip as a fallback in case something fails
|
|
831
878
|
try {
|
|
832
|
-
|
|
879
|
+
dns.reverse(ip, (reverseErr, hostnames) => {
|
|
833
880
|
if (reverseErr || hostnames.length < 1) {
|
|
834
881
|
return callback(ip);
|
|
835
882
|
}
|
|
836
|
-
|
|
883
|
+
dns.resolve(hostnames[0], net.isIP(ip) === 6 ? "AAAA" : "A", (resolveErr, resolvedIps) => {
|
|
837
884
|
// TODO: investigate SoaRecord class
|
|
838
885
|
if (!Array.isArray(resolvedIps)) {
|
|
839
886
|
return callback(ip);
|
|
@@ -851,7 +898,7 @@ function reverseDnsLookup(ip, callback) {
|
|
|
851
898
|
});
|
|
852
899
|
}
|
|
853
900
|
catch (err) {
|
|
854
|
-
|
|
901
|
+
log.error(`failed to resolve rDNS for ${ip}, using ip instead`, String(err));
|
|
855
902
|
setImmediate(callback, ip); // makes sure we always behave asynchronously
|
|
856
903
|
}
|
|
857
904
|
}
|