@lordbex/thelounge 4.4.4-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 +2 -2
- package/dist/defaults/config.js +31 -2
- package/dist/package.json +93 -91
- package/dist/server/client.js +188 -194
- package/dist/server/clientManager.js +65 -63
- 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 +88 -86
- 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 +75 -96
- 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 +7 -14
- 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 +93 -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/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
|
@@ -1,16 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const user_1 = __importDefault(require("./user"));
|
|
10
|
-
const storage_1 = __importDefault(require("../plugins/storage"));
|
|
11
|
-
const prefix_1 = __importDefault(require("./prefix"));
|
|
12
|
-
const msg_1 = require("../../shared/types/msg");
|
|
13
|
-
const chan_1 = require("../../shared/types/chan");
|
|
1
|
+
import _ from "lodash";
|
|
2
|
+
import log from "../log.js";
|
|
3
|
+
import Config from "../config.js";
|
|
4
|
+
import User from "./user.js";
|
|
5
|
+
import storage from "../plugins/storage.js";
|
|
6
|
+
import Prefix from "./prefix.js";
|
|
7
|
+
import { MessageType } from "../../shared/types/msg.js";
|
|
8
|
+
import { ChanType, ChanState } from "../../shared/types/chan.js";
|
|
14
9
|
class Chan {
|
|
15
10
|
// TODO: don't force existence, figure out how to make TS infer it.
|
|
16
11
|
id;
|
|
@@ -25,6 +20,7 @@ class Chan {
|
|
|
25
20
|
muted;
|
|
26
21
|
type;
|
|
27
22
|
state;
|
|
23
|
+
pinned;
|
|
28
24
|
// mIRC FiSH Blowfish key, stored server-side only
|
|
29
25
|
blowfishKey;
|
|
30
26
|
userAway;
|
|
@@ -32,20 +28,22 @@ class Chan {
|
|
|
32
28
|
data;
|
|
33
29
|
closed;
|
|
34
30
|
num_users;
|
|
31
|
+
groups;
|
|
35
32
|
constructor(attr) {
|
|
36
|
-
|
|
33
|
+
_.defaults(this, attr, {
|
|
37
34
|
id: 0,
|
|
38
35
|
messages: [],
|
|
39
36
|
name: "",
|
|
40
37
|
key: "",
|
|
41
38
|
topic: "",
|
|
42
|
-
type:
|
|
43
|
-
state:
|
|
39
|
+
type: ChanType.CHANNEL,
|
|
40
|
+
state: ChanState.PARTED,
|
|
44
41
|
firstUnread: 0,
|
|
45
42
|
unread: 0,
|
|
46
43
|
highlight: 0,
|
|
47
44
|
users: new Map(),
|
|
48
45
|
muted: false,
|
|
46
|
+
pinned: false,
|
|
49
47
|
});
|
|
50
48
|
}
|
|
51
49
|
destroy() {
|
|
@@ -55,7 +53,7 @@ class Chan {
|
|
|
55
53
|
const chanId = this.id;
|
|
56
54
|
msg.id = client.idMsg++;
|
|
57
55
|
// If this channel is open in any of the clients, do not increase unread counter
|
|
58
|
-
const isOpen =
|
|
56
|
+
const isOpen = _.find(client.attachedClients, { openChannel: chanId }) !== undefined;
|
|
59
57
|
if (msg.self) {
|
|
60
58
|
// reset counters/markers when receiving self-/echo-message
|
|
61
59
|
this.unread = 0;
|
|
@@ -76,7 +74,7 @@ class Chan {
|
|
|
76
74
|
client.emit("msg", { chan: chanId, msg, unread: this.unread, highlight: this.highlight });
|
|
77
75
|
// Never store messages in public mode as the session
|
|
78
76
|
// is completely destroyed when the page gets closed
|
|
79
|
-
if (
|
|
77
|
+
if (Config.values.public) {
|
|
80
78
|
return;
|
|
81
79
|
}
|
|
82
80
|
// showInActive is only processed on "msg", don't need it on page reload
|
|
@@ -84,24 +82,24 @@ class Chan {
|
|
|
84
82
|
delete msg.showInActive;
|
|
85
83
|
}
|
|
86
84
|
this.writeUserLog(client, msg);
|
|
87
|
-
if (
|
|
88
|
-
const deleted = this.messages.splice(0, this.messages.length -
|
|
85
|
+
if (Config.values.maxHistory >= 0 && this.messages.length > Config.values.maxHistory) {
|
|
86
|
+
const deleted = this.messages.splice(0, this.messages.length - Config.values.maxHistory);
|
|
89
87
|
// If maxHistory is 0, image would be dereferenced before client had a chance to retrieve it,
|
|
90
88
|
// so for now, just don't implement dereferencing for this edge case.
|
|
91
|
-
if (
|
|
89
|
+
if (Config.values.maxHistory > 0) {
|
|
92
90
|
this.dereferencePreviews(deleted);
|
|
93
91
|
}
|
|
94
92
|
}
|
|
95
93
|
}
|
|
96
94
|
dereferencePreviews(messages) {
|
|
97
|
-
if (!
|
|
95
|
+
if (!Config.values.prefetch || !Config.values.prefetchStorage) {
|
|
98
96
|
return;
|
|
99
97
|
}
|
|
100
98
|
messages.forEach((message) => {
|
|
101
99
|
if (message.previews) {
|
|
102
100
|
message.previews.forEach((preview) => {
|
|
103
101
|
if (preview.thumb) {
|
|
104
|
-
|
|
102
|
+
storage.dereference(preview.thumb);
|
|
105
103
|
preview.thumb = "";
|
|
106
104
|
}
|
|
107
105
|
});
|
|
@@ -132,7 +130,7 @@ class Chan {
|
|
|
132
130
|
return this.users.get(nick.toLowerCase());
|
|
133
131
|
}
|
|
134
132
|
getUser(nick) {
|
|
135
|
-
return this.findUser(nick) || new
|
|
133
|
+
return this.findUser(nick) || new User({ nick }, new Prefix([]));
|
|
136
134
|
}
|
|
137
135
|
setUser(user) {
|
|
138
136
|
this.users.set(user.nick.toLowerCase(), user);
|
|
@@ -176,10 +174,12 @@ class Chan {
|
|
|
176
174
|
muted: this.muted,
|
|
177
175
|
type: this.type,
|
|
178
176
|
state: this.state,
|
|
177
|
+
pinned: this.pinned,
|
|
179
178
|
special: this.special,
|
|
180
179
|
data: this.data,
|
|
181
180
|
closed: this.closed,
|
|
182
181
|
num_users: this.num_users,
|
|
182
|
+
groups: this.groups,
|
|
183
183
|
};
|
|
184
184
|
// TODO: funny array mutation below might need to be reproduced
|
|
185
185
|
// static optionalProperties = ["userAway", "special", "data", "closed", "num_users"];
|
|
@@ -196,13 +196,12 @@ class Chan {
|
|
|
196
196
|
if (client.messageStorage.length === 0) {
|
|
197
197
|
return;
|
|
198
198
|
}
|
|
199
|
-
const targetChannel = this;
|
|
200
199
|
// Is this particular message or channel loggable
|
|
201
200
|
if (!msg.isLoggable() || !this.isLoggable()) {
|
|
202
201
|
// Because notices are nasty and can be shown in active channel on the client
|
|
203
202
|
// if there is no open query, we want to always log notices in the sender's name
|
|
204
|
-
if (msg.type ===
|
|
205
|
-
|
|
203
|
+
if (msg.type === MessageType.NOTICE && msg.showInActive) {
|
|
204
|
+
this.name = msg.from.nick || ""; // TODO: check if || works
|
|
206
205
|
}
|
|
207
206
|
else {
|
|
208
207
|
return;
|
|
@@ -214,7 +213,7 @@ class Chan {
|
|
|
214
213
|
return;
|
|
215
214
|
}
|
|
216
215
|
for (const messageStorage of client.messageStorage) {
|
|
217
|
-
messageStorage.index(target.network,
|
|
216
|
+
messageStorage.index(target.network, this, msg).catch((e) => log.error(e));
|
|
218
217
|
}
|
|
219
218
|
}
|
|
220
219
|
loadMessages(client, network) {
|
|
@@ -223,7 +222,7 @@ class Chan {
|
|
|
223
222
|
}
|
|
224
223
|
if (!network.irc) {
|
|
225
224
|
// Network created, but misconfigured
|
|
226
|
-
|
|
225
|
+
log.warn(`Failed to load messages for ${client.name}, network ${network.name} is not initialized.`);
|
|
227
226
|
return;
|
|
228
227
|
}
|
|
229
228
|
if (!client.messageProvider) {
|
|
@@ -247,9 +246,13 @@ class Chan {
|
|
|
247
246
|
if (!this.firstUnread) {
|
|
248
247
|
this.firstUnread = messages[messages.length - 1].id;
|
|
249
248
|
}
|
|
249
|
+
const enhancedSearch = Boolean(client.config.clientSettings.searchEnabled &&
|
|
250
|
+
client.config.clientSettings.enableEnhancedSearch);
|
|
251
|
+
// if enhancedSearchEnabled = true send all loaded messages to the client
|
|
252
|
+
// otherwise only send 100
|
|
250
253
|
client.emit("more", {
|
|
251
254
|
chan: this.id,
|
|
252
|
-
messages: messages.slice(-100),
|
|
255
|
+
messages: enhancedSearch ? messages : messages.slice(-100),
|
|
253
256
|
totalMessages: messages.length,
|
|
254
257
|
});
|
|
255
258
|
if (network.irc.network.cap.isEnabled("znc.in/playback")) {
|
|
@@ -257,10 +260,10 @@ class Chan {
|
|
|
257
260
|
requestZncPlayback(this, network, from);
|
|
258
261
|
}
|
|
259
262
|
})
|
|
260
|
-
.catch((err) =>
|
|
263
|
+
.catch((err) => log.error(`Failed to load messages for ${client.name}: ${err.toString()}`));
|
|
261
264
|
}
|
|
262
265
|
isLoggable() {
|
|
263
|
-
return this.type ===
|
|
266
|
+
return this.type === ChanType.CHANNEL || this.type === ChanType.QUERY;
|
|
264
267
|
}
|
|
265
268
|
setMuteStatus(muted) {
|
|
266
269
|
this.muted = !!muted;
|
|
@@ -272,4 +275,4 @@ function requestZncPlayback(channel, network, from) {
|
|
|
272
275
|
}
|
|
273
276
|
network.irc.raw("ZNC", "*playback", "PLAY", channel.name, from.toString());
|
|
274
277
|
}
|
|
275
|
-
|
|
278
|
+
export default Chan;
|
|
@@ -1,10 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const lodash_1 = __importDefault(require("lodash"));
|
|
7
|
-
const msg_1 = require("../../shared/types/msg");
|
|
1
|
+
import _ from "lodash";
|
|
2
|
+
import { MessageType, } from "../../shared/types/msg.js";
|
|
8
3
|
class Msg {
|
|
9
4
|
from;
|
|
10
5
|
id;
|
|
@@ -39,6 +34,7 @@ class Msg {
|
|
|
39
34
|
users;
|
|
40
35
|
statusmsgGroup;
|
|
41
36
|
params;
|
|
37
|
+
massEventSummary;
|
|
42
38
|
constructor(attr) {
|
|
43
39
|
// Some properties need to be copied in the Msg object instead of referenced
|
|
44
40
|
if (attr) {
|
|
@@ -51,12 +47,12 @@ class Msg {
|
|
|
51
47
|
}
|
|
52
48
|
});
|
|
53
49
|
}
|
|
54
|
-
|
|
50
|
+
_.defaults(this, attr, {
|
|
55
51
|
from: {},
|
|
56
52
|
id: 0,
|
|
57
53
|
previews: [],
|
|
58
54
|
text: "",
|
|
59
|
-
type:
|
|
55
|
+
type: MessageType.MESSAGE,
|
|
60
56
|
self: false,
|
|
61
57
|
});
|
|
62
58
|
if (this.time) {
|
|
@@ -70,23 +66,23 @@ class Msg {
|
|
|
70
66
|
return this.previews.find((preview) => preview.link === link);
|
|
71
67
|
}
|
|
72
68
|
isLoggable() {
|
|
73
|
-
if (this.type ===
|
|
69
|
+
if (this.type === MessageType.TOPIC) {
|
|
74
70
|
// Do not log topic that is sent on channel join
|
|
75
71
|
return !!this.from.nick;
|
|
76
72
|
}
|
|
77
73
|
switch (this.type) {
|
|
78
|
-
case
|
|
79
|
-
case
|
|
80
|
-
case
|
|
81
|
-
case
|
|
82
|
-
case
|
|
83
|
-
case
|
|
84
|
-
case
|
|
85
|
-
case
|
|
74
|
+
case MessageType.MONOSPACE_BLOCK:
|
|
75
|
+
case MessageType.ERROR:
|
|
76
|
+
case MessageType.TOPIC_SET_BY:
|
|
77
|
+
case MessageType.MODE_CHANNEL:
|
|
78
|
+
case MessageType.MODE_USER:
|
|
79
|
+
case MessageType.RAW:
|
|
80
|
+
case MessageType.WHOIS:
|
|
81
|
+
case MessageType.PLUGIN:
|
|
86
82
|
return false;
|
|
87
83
|
default:
|
|
88
84
|
return true;
|
|
89
85
|
}
|
|
90
86
|
}
|
|
91
87
|
}
|
|
92
|
-
|
|
88
|
+
export default Msg;
|
|
@@ -1,20 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const config_1 = __importDefault(require("../config"));
|
|
14
|
-
const sts_1 = __importDefault(require("../plugins/sts"));
|
|
15
|
-
const clientCertificate_1 = __importDefault(require("../plugins/clientCertificate"));
|
|
16
|
-
const msg_2 = require("../../shared/types/msg");
|
|
17
|
-
const chan_2 = require("../../shared/types/chan");
|
|
1
|
+
import _ from "lodash";
|
|
2
|
+
import { v4 as uuidv4 } from "uuid";
|
|
3
|
+
import IrcFramework from "irc-framework";
|
|
4
|
+
import Chan from "./chan.js";
|
|
5
|
+
import Msg from "./msg.js";
|
|
6
|
+
import Prefix from "./prefix.js";
|
|
7
|
+
import Helper from "../helper.js";
|
|
8
|
+
import Config from "../config.js";
|
|
9
|
+
import STSPolicies from "../plugins/sts.js";
|
|
10
|
+
import ClientCertificate from "../plugins/clientCertificate.js";
|
|
11
|
+
import { MessageType } from "../../shared/types/msg.js";
|
|
12
|
+
import { ChanType } from "../../shared/types/chan.js";
|
|
18
13
|
class Network {
|
|
19
14
|
nick;
|
|
20
15
|
name;
|
|
@@ -52,7 +47,7 @@ class Network {
|
|
|
52
47
|
// TODO: this is only available on export
|
|
53
48
|
hasSTSPolicy;
|
|
54
49
|
constructor(attr) {
|
|
55
|
-
|
|
50
|
+
_.defaults(this, attr, {
|
|
56
51
|
name: "",
|
|
57
52
|
nick: "",
|
|
58
53
|
host: "",
|
|
@@ -73,7 +68,7 @@ class Network {
|
|
|
73
68
|
irc: null,
|
|
74
69
|
serverOptions: {
|
|
75
70
|
CHANTYPES: ["#", "&"],
|
|
76
|
-
PREFIX: new
|
|
71
|
+
PREFIX: new Prefix([
|
|
77
72
|
{ symbol: "!", mode: "Y" },
|
|
78
73
|
{ symbol: "@", mode: "o" },
|
|
79
74
|
{ symbol: "%", mode: "h" },
|
|
@@ -94,18 +89,18 @@ class Network {
|
|
|
94
89
|
keepNick: null,
|
|
95
90
|
});
|
|
96
91
|
if (!this.uuid) {
|
|
97
|
-
this.uuid = (
|
|
92
|
+
this.uuid = uuidv4();
|
|
98
93
|
}
|
|
99
94
|
if (!this.name) {
|
|
100
95
|
this.name = this.host;
|
|
101
96
|
}
|
|
102
|
-
this.channels.unshift(new
|
|
97
|
+
this.channels.unshift(new Chan({
|
|
103
98
|
name: this.name,
|
|
104
|
-
type:
|
|
99
|
+
type: ChanType.LOBBY,
|
|
105
100
|
// The lobby only starts as muted if every channel (unless it's special) is muted.
|
|
106
101
|
// This is A) easier to implement and B) stops some confusion on startup.
|
|
107
102
|
muted: this.channels.length >= 1 &&
|
|
108
|
-
this.channels.every((chan) => chan.muted || chan.type ===
|
|
103
|
+
this.channels.every((chan) => chan.muted || chan.type === ChanType.SPECIAL),
|
|
109
104
|
}));
|
|
110
105
|
// Ensure FiSH keys are applied to any pre-existing channels loaded from disk
|
|
111
106
|
this.applyBlowKeysToChannels();
|
|
@@ -115,7 +110,7 @@ class Network {
|
|
|
115
110
|
const cleanNick = (str) => str.replace(/[\x00\s:!@]/g, "_").substring(0, 100);
|
|
116
111
|
// Remove new lines and limit length
|
|
117
112
|
const cleanString = (str) => str.replace(/[\x00\r\n]/g, "").substring(0, 300);
|
|
118
|
-
this.setNick(cleanNick(String(this.nick ||
|
|
113
|
+
this.setNick(cleanNick(String(this.nick || Config.getDefaultNick())));
|
|
119
114
|
if (!this.username) {
|
|
120
115
|
// If username is empty, make one from the provided nick
|
|
121
116
|
this.username = this.nick.replace(/[^a-zA-Z0-9]/g, "");
|
|
@@ -134,8 +129,8 @@ class Network {
|
|
|
134
129
|
this.proxyPassword = cleanString(this.proxyPassword);
|
|
135
130
|
this.proxyEnabled = !!this.proxyEnabled;
|
|
136
131
|
const error = function (network, text) {
|
|
137
|
-
network.getLobby().pushMessage(client, new
|
|
138
|
-
type:
|
|
132
|
+
network.getLobby().pushMessage(client, new Msg({
|
|
133
|
+
type: MessageType.ERROR,
|
|
139
134
|
text: text,
|
|
140
135
|
}), true);
|
|
141
136
|
};
|
|
@@ -145,30 +140,30 @@ class Network {
|
|
|
145
140
|
if (!["", "plain", "external"].includes(this.sasl)) {
|
|
146
141
|
this.sasl = "";
|
|
147
142
|
}
|
|
148
|
-
if (
|
|
143
|
+
if (Config.values.lockNetwork) {
|
|
149
144
|
// This check is needed to prevent invalid user configurations
|
|
150
|
-
if (!
|
|
145
|
+
if (!Config.values.public &&
|
|
151
146
|
this.host &&
|
|
152
147
|
this.host.length > 0 &&
|
|
153
|
-
this.host !==
|
|
148
|
+
this.host !== Config.values.defaults.host) {
|
|
154
149
|
error(this, `The hostname you specified (${this.host}) is not allowed.`);
|
|
155
150
|
return false;
|
|
156
151
|
}
|
|
157
|
-
if (
|
|
158
|
-
this.name =
|
|
152
|
+
if (Config.values.public) {
|
|
153
|
+
this.name = Config.values.defaults.name;
|
|
159
154
|
// Sync lobby channel name
|
|
160
|
-
this.getLobby().name =
|
|
155
|
+
this.getLobby().name = Config.values.defaults.name;
|
|
161
156
|
}
|
|
162
|
-
this.host =
|
|
163
|
-
this.port =
|
|
164
|
-
this.tls =
|
|
165
|
-
this.rejectUnauthorized =
|
|
157
|
+
this.host = Config.values.defaults.host;
|
|
158
|
+
this.port = Config.values.defaults.port;
|
|
159
|
+
this.tls = Config.values.defaults.tls;
|
|
160
|
+
this.rejectUnauthorized = Config.values.defaults.rejectUnauthorized;
|
|
166
161
|
}
|
|
167
162
|
if (this.host.length === 0) {
|
|
168
163
|
error(this, "You must specify a hostname to connect.");
|
|
169
164
|
return false;
|
|
170
165
|
}
|
|
171
|
-
const stsPolicy =
|
|
166
|
+
const stsPolicy = STSPolicies.get(this.host);
|
|
172
167
|
if (stsPolicy && !this.tls) {
|
|
173
168
|
error(this, `${this.host} has an active strict transport security policy, will connect to port ${stsPolicy.port} over a secure connection.`);
|
|
174
169
|
this.port = stsPolicy.port;
|
|
@@ -178,9 +173,9 @@ class Network {
|
|
|
178
173
|
return true;
|
|
179
174
|
}
|
|
180
175
|
createIrcFramework(client) {
|
|
181
|
-
this.irc = new
|
|
176
|
+
this.irc = new IrcFramework.Client({
|
|
182
177
|
version: false, // We handle it ourselves
|
|
183
|
-
outgoing_addr:
|
|
178
|
+
outgoing_addr: Config.values.bind,
|
|
184
179
|
enable_chghost: true,
|
|
185
180
|
enable_echomessage: true,
|
|
186
181
|
enable_setname: true,
|
|
@@ -191,18 +186,21 @@ class Network {
|
|
|
191
186
|
// TODO: this type should be set after setIrcFrameworkOptions
|
|
192
187
|
});
|
|
193
188
|
this.setIrcFrameworkOptions(client);
|
|
194
|
-
|
|
189
|
+
// Request custom capabilities
|
|
190
|
+
const customCaps = [
|
|
195
191
|
"znc.in/self-message", // Legacy echo-message for ZNC
|
|
196
192
|
"znc.in/playback", // See http://wiki.znc.in/Playback
|
|
197
|
-
|
|
193
|
+
"seedpool/enhanced", // THC enhanced client features
|
|
194
|
+
];
|
|
195
|
+
this.irc.requestCap(customCaps);
|
|
198
196
|
}
|
|
199
197
|
setIrcFrameworkOptions(client) {
|
|
200
198
|
this.irc.options.host = this.host;
|
|
201
199
|
this.irc.options.port = this.port;
|
|
202
200
|
this.irc.options.password = this.password;
|
|
203
201
|
this.irc.options.nick = this.nick;
|
|
204
|
-
this.irc.options.username =
|
|
205
|
-
?
|
|
202
|
+
this.irc.options.username = Config.values.useHexIp
|
|
203
|
+
? Helper.ip2hex(client.config.browser.ip)
|
|
206
204
|
: this.username;
|
|
207
205
|
this.irc.options.gecos = this.realname;
|
|
208
206
|
this.irc.options.tls = this.tls;
|
|
@@ -230,7 +228,7 @@ class Network {
|
|
|
230
228
|
else if (this.sasl === "external") {
|
|
231
229
|
this.irc.options.sasl_mechanism = "EXTERNAL";
|
|
232
230
|
this.irc.options.account = {};
|
|
233
|
-
this.irc.options.client_certificate =
|
|
231
|
+
this.irc.options.client_certificate = ClientCertificate.get(this.uuid);
|
|
234
232
|
}
|
|
235
233
|
else if (this.sasl === "plain") {
|
|
236
234
|
delete this.irc.options.sasl_mechanism;
|
|
@@ -250,19 +248,19 @@ class Network {
|
|
|
250
248
|
}
|
|
251
249
|
applyBlowKeysToChannels() {
|
|
252
250
|
for (const c of this.channels) {
|
|
253
|
-
if (c.type !==
|
|
251
|
+
if (c.type !== ChanType.CHANNEL && c.type !== ChanType.QUERY) {
|
|
254
252
|
continue;
|
|
255
253
|
}
|
|
256
254
|
c.blowfishKey = this.resolveBlowKeyFor(c.name);
|
|
257
255
|
}
|
|
258
256
|
}
|
|
259
257
|
createWebIrc(client) {
|
|
260
|
-
if (!
|
|
261
|
-
!Object.prototype.hasOwnProperty.call(
|
|
258
|
+
if (!Config.values.webirc ||
|
|
259
|
+
!Object.prototype.hasOwnProperty.call(Config.values.webirc, this.host)) {
|
|
262
260
|
return null;
|
|
263
261
|
}
|
|
264
262
|
const webircObject = {
|
|
265
|
-
password:
|
|
263
|
+
password: Config.values.webirc[this.host],
|
|
266
264
|
username: "thelounge",
|
|
267
265
|
address: client.config.browser?.ip,
|
|
268
266
|
hostname: client.config.browser?.hostname,
|
|
@@ -274,9 +272,9 @@ class Network {
|
|
|
274
272
|
secure: true,
|
|
275
273
|
};
|
|
276
274
|
}
|
|
277
|
-
if (typeof
|
|
275
|
+
if (typeof Config.values.webirc[this.host] === "function") {
|
|
278
276
|
webircObject.password = null;
|
|
279
|
-
return
|
|
277
|
+
return Config.values.webirc[this.host](webircObject, this);
|
|
280
278
|
}
|
|
281
279
|
return webircObject;
|
|
282
280
|
}
|
|
@@ -285,32 +283,35 @@ class Network {
|
|
|
285
283
|
const oldNick = this.nick;
|
|
286
284
|
const oldRealname = this.realname;
|
|
287
285
|
this.keepNick = null;
|
|
288
|
-
this.nick = args.nick;
|
|
289
|
-
this.host = String(args.host
|
|
290
|
-
this.name = String(args.name
|
|
291
|
-
this.port = parseInt(args.port, 10);
|
|
286
|
+
this.nick = String(args.nick ?? "");
|
|
287
|
+
this.host = String(args.host ?? "");
|
|
288
|
+
this.name = String(args.name ?? "") || this.host;
|
|
289
|
+
this.port = parseInt(String(args.port ?? 6667), 10);
|
|
292
290
|
this.tls = !!args.tls;
|
|
293
291
|
this.rejectUnauthorized = !!args.rejectUnauthorized;
|
|
294
|
-
this.password = String(args.password
|
|
295
|
-
this.username = String(args.username
|
|
296
|
-
this.realname = String(args.realname
|
|
297
|
-
this.leaveMessage = String(args.leaveMessage
|
|
298
|
-
this.sasl = String(args.sasl
|
|
299
|
-
this.saslAccount = String(args.saslAccount
|
|
300
|
-
this.saslPassword = String(args.saslPassword
|
|
301
|
-
this.proxyHost = String(args.proxyHost
|
|
302
|
-
this.proxyPort = parseInt(args.proxyPort, 10);
|
|
303
|
-
this.proxyUsername = String(args.proxyUsername
|
|
304
|
-
this.proxyPassword = String(args.proxyPassword
|
|
292
|
+
this.password = String(args.password ?? "");
|
|
293
|
+
this.username = String(args.username ?? "");
|
|
294
|
+
this.realname = String(args.realname ?? "");
|
|
295
|
+
this.leaveMessage = String(args.leaveMessage ?? "");
|
|
296
|
+
this.sasl = String(args.sasl ?? "");
|
|
297
|
+
this.saslAccount = String(args.saslAccount ?? "");
|
|
298
|
+
this.saslPassword = String(args.saslPassword ?? "");
|
|
299
|
+
this.proxyHost = String(args.proxyHost ?? "");
|
|
300
|
+
this.proxyPort = parseInt(String(args.proxyPort ?? 1080), 10);
|
|
301
|
+
this.proxyUsername = String(args.proxyUsername ?? "");
|
|
302
|
+
this.proxyPassword = String(args.proxyPassword ?? "");
|
|
305
303
|
this.proxyEnabled = !!args.proxyEnabled;
|
|
306
304
|
// Split commands into an array
|
|
307
|
-
|
|
305
|
+
const commandsStr = Array.isArray(args.commands)
|
|
306
|
+
? args.commands.join("\n")
|
|
307
|
+
: String(args.commands ?? "");
|
|
308
|
+
this.commands = commandsStr
|
|
308
309
|
.replace(/\r\n|\r|\n/g, "\n")
|
|
309
310
|
.split("\n")
|
|
310
311
|
.filter((command) => command.length > 0);
|
|
311
312
|
// FiSH: read global key and per-target keys (only update when provided)
|
|
312
313
|
if (Object.prototype.hasOwnProperty.call(args, "fishGlobalKey")) {
|
|
313
|
-
this.fishGlobalKey =
|
|
314
|
+
this.fishGlobalKey = Helper.toTrimmedString(args.fishGlobalKey || "");
|
|
314
315
|
}
|
|
315
316
|
// FiSH: read per-target keys (only update when provided)
|
|
316
317
|
if (Object.prototype.hasOwnProperty.call(args, "fishKeys")) {
|
|
@@ -318,8 +319,8 @@ class Network {
|
|
|
318
319
|
const map = {};
|
|
319
320
|
if (value && typeof value === "object") {
|
|
320
321
|
for (const [rawName, rawKey] of Object.entries(value)) {
|
|
321
|
-
const name =
|
|
322
|
-
const key =
|
|
322
|
+
const name = Helper.toTrimmedString(rawName).toLowerCase();
|
|
323
|
+
const key = Helper.toTrimmedString(rawKey);
|
|
323
324
|
if (name && key) {
|
|
324
325
|
map[name] = key;
|
|
325
326
|
}
|
|
@@ -380,7 +381,7 @@ class Network {
|
|
|
380
381
|
// Do not match characters and numbers (unless IRC color)
|
|
381
382
|
"(?:^|[^a-z0-9]|\x03[0-9]{1,2})" +
|
|
382
383
|
// Escape nickname, as it may contain regex stuff
|
|
383
|
-
|
|
384
|
+
_.escapeRegExp(nick) +
|
|
384
385
|
// Do not match characters and numbers
|
|
385
386
|
"(?:[^a-z0-9]|$)",
|
|
386
387
|
// Case insensitive search
|
|
@@ -410,7 +411,7 @@ class Network {
|
|
|
410
411
|
if (this.irc && this.irc.connection && this.irc.connection.transport) {
|
|
411
412
|
const transport = this.irc.connection.transport;
|
|
412
413
|
if (transport.socket) {
|
|
413
|
-
const isLocalhost =
|
|
414
|
+
const isLocalhost = ["127.0.0.1", "::1"].includes(transport.socket.remoteAddress);
|
|
414
415
|
const isAuthorized = transport.socket.encrypted && transport.socket.authorized;
|
|
415
416
|
status.connected = transport.isConnected();
|
|
416
417
|
status.secure = isAuthorized || isLocalhost;
|
|
@@ -420,12 +421,12 @@ class Network {
|
|
|
420
421
|
}
|
|
421
422
|
addChannel(newChan) {
|
|
422
423
|
// Assign FiSH key based on network configuration when adding
|
|
423
|
-
if (newChan && (newChan.type ===
|
|
424
|
+
if (newChan && (newChan.type === ChanType.CHANNEL || newChan.type === ChanType.QUERY)) {
|
|
424
425
|
newChan.blowfishKey = this.resolveBlowKeyFor(newChan.name);
|
|
425
426
|
}
|
|
426
427
|
let index = this.channels.length; // Default to putting as the last item in the array
|
|
427
428
|
// Don't sort special channels in amongst channels/users.
|
|
428
|
-
if (newChan.type ===
|
|
429
|
+
if (newChan.type === ChanType.CHANNEL || newChan.type === ChanType.QUERY) {
|
|
429
430
|
// We start at 1 so we don't test against the lobby
|
|
430
431
|
for (let i = 1; i < this.channels.length; i++) {
|
|
431
432
|
const compareChan = this.channels[i];
|
|
@@ -433,7 +434,7 @@ class Network {
|
|
|
433
434
|
if (newChan.name.localeCompare(compareChan.name, undefined, {
|
|
434
435
|
sensitivity: "base",
|
|
435
436
|
}) <= 0 ||
|
|
436
|
-
(compareChan.type !==
|
|
437
|
+
(compareChan.type !== ChanType.CHANNEL && compareChan.type !== ChanType.QUERY)) {
|
|
437
438
|
index = i;
|
|
438
439
|
break;
|
|
439
440
|
}
|
|
@@ -447,8 +448,8 @@ class Network {
|
|
|
447
448
|
return;
|
|
448
449
|
}
|
|
449
450
|
// https://ircv3.net/specs/extensions/sts#rescheduling-expiry-on-disconnect
|
|
450
|
-
|
|
451
|
-
this.irc.quit(quitMessage || this.leaveMessage ||
|
|
451
|
+
STSPolicies.refreshExpiration(this.host);
|
|
452
|
+
this.irc.quit(quitMessage || this.leaveMessage || Config.values.leaveMessage);
|
|
452
453
|
}
|
|
453
454
|
exportForEdit() {
|
|
454
455
|
const fieldsToReturn = [
|
|
@@ -469,21 +470,21 @@ class Network {
|
|
|
469
470
|
"proxyUsername",
|
|
470
471
|
"proxyPassword",
|
|
471
472
|
];
|
|
472
|
-
if (!
|
|
473
|
+
if (!Config.values.lockNetwork) {
|
|
473
474
|
fieldsToReturn.push("host");
|
|
474
475
|
fieldsToReturn.push("port");
|
|
475
476
|
fieldsToReturn.push("tls");
|
|
476
477
|
fieldsToReturn.push("rejectUnauthorized");
|
|
477
478
|
}
|
|
478
|
-
const data =
|
|
479
|
-
data.hasSTSPolicy = !!
|
|
479
|
+
const data = _.pick(this, fieldsToReturn);
|
|
480
|
+
data.hasSTSPolicy = !!STSPolicies.get(this.host);
|
|
480
481
|
// Include FiSH fields for editing UI
|
|
481
482
|
data.fishGlobalKey = this.fishGlobalKey || "";
|
|
482
483
|
data.fishKeys = { ...(this.fishKeys || {}) };
|
|
483
484
|
return data;
|
|
484
485
|
}
|
|
485
486
|
export() {
|
|
486
|
-
const network =
|
|
487
|
+
const network = _.pick(this, [
|
|
487
488
|
"uuid",
|
|
488
489
|
"awayMessage",
|
|
489
490
|
"nick",
|
|
@@ -513,24 +514,25 @@ class Network {
|
|
|
513
514
|
]);
|
|
514
515
|
network.channels = this.channels
|
|
515
516
|
.filter(function (channel) {
|
|
516
|
-
return channel.type ===
|
|
517
|
+
return channel.type === ChanType.CHANNEL || channel.type === ChanType.QUERY;
|
|
517
518
|
})
|
|
518
519
|
.map(function (chan) {
|
|
519
520
|
const keys = ["name", "muted"];
|
|
520
|
-
if (chan.type ===
|
|
521
|
+
if (chan.type === ChanType.CHANNEL) {
|
|
521
522
|
keys.push("key");
|
|
522
523
|
}
|
|
523
|
-
else if (chan.type ===
|
|
524
|
+
else if (chan.type === ChanType.QUERY) {
|
|
524
525
|
keys.push("type");
|
|
526
|
+
keys.push("pinned");
|
|
525
527
|
}
|
|
526
|
-
return
|
|
528
|
+
return _.pick(chan, keys);
|
|
527
529
|
// Override the type because we're omitting ID
|
|
528
530
|
});
|
|
529
531
|
return network;
|
|
530
532
|
}
|
|
531
533
|
getChannel(name) {
|
|
532
534
|
name = name.toLowerCase();
|
|
533
|
-
return
|
|
535
|
+
return _.find(this.channels, function (that, i) {
|
|
534
536
|
// Skip network lobby (it's always unshifted into first position)
|
|
535
537
|
return i > 0 && that.name.toLowerCase() === name;
|
|
536
538
|
});
|
|
@@ -539,4 +541,4 @@ class Network {
|
|
|
539
541
|
return this.channels[0];
|
|
540
542
|
}
|
|
541
543
|
}
|
|
542
|
-
|
|
544
|
+
export default Network;
|