@lordbex/thelounge 4.4.3-blowfish
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/.thelounge_home +1 -0
- package/LICENSE +22 -0
- package/README.md +95 -0
- package/client/index.html.tpl +69 -0
- package/dist/defaults/config.js +465 -0
- package/dist/package.json +174 -0
- package/dist/server/client.js +678 -0
- package/dist/server/clientManager.js +220 -0
- package/dist/server/command-line/index.js +85 -0
- package/dist/server/command-line/install.js +123 -0
- package/dist/server/command-line/outdated.js +30 -0
- package/dist/server/command-line/start.js +34 -0
- package/dist/server/command-line/storage.js +103 -0
- package/dist/server/command-line/uninstall.js +40 -0
- package/dist/server/command-line/upgrade.js +64 -0
- package/dist/server/command-line/users/add.js +67 -0
- package/dist/server/command-line/users/edit.js +39 -0
- package/dist/server/command-line/users/index.js +17 -0
- package/dist/server/command-line/users/list.js +53 -0
- package/dist/server/command-line/users/remove.js +37 -0
- package/dist/server/command-line/users/reset.js +64 -0
- package/dist/server/command-line/utils.js +177 -0
- package/dist/server/config.js +138 -0
- package/dist/server/helper.js +161 -0
- package/dist/server/identification.js +139 -0
- package/dist/server/index.js +3 -0
- package/dist/server/log.js +35 -0
- package/dist/server/models/chan.js +275 -0
- package/dist/server/models/msg.js +92 -0
- package/dist/server/models/network.js +546 -0
- package/dist/server/models/prefix.js +31 -0
- package/dist/server/models/user.js +42 -0
- package/dist/server/plugins/auth/ldap.js +188 -0
- package/dist/server/plugins/auth/local.js +41 -0
- package/dist/server/plugins/auth.js +70 -0
- package/dist/server/plugins/changelog.js +103 -0
- package/dist/server/plugins/clientCertificate.js +115 -0
- package/dist/server/plugins/dev-server.js +33 -0
- package/dist/server/plugins/inputs/action.js +54 -0
- package/dist/server/plugins/inputs/away.js +20 -0
- package/dist/server/plugins/inputs/ban.js +45 -0
- package/dist/server/plugins/inputs/blow.js +44 -0
- package/dist/server/plugins/inputs/connect.js +41 -0
- package/dist/server/plugins/inputs/ctcp.js +29 -0
- package/dist/server/plugins/inputs/disconnect.js +15 -0
- package/dist/server/plugins/inputs/ignore.js +74 -0
- package/dist/server/plugins/inputs/ignorelist.js +50 -0
- package/dist/server/plugins/inputs/index.js +105 -0
- package/dist/server/plugins/inputs/invite.js +31 -0
- package/dist/server/plugins/inputs/kick.js +26 -0
- package/dist/server/plugins/inputs/kill.js +13 -0
- package/dist/server/plugins/inputs/list.js +12 -0
- package/dist/server/plugins/inputs/mode.js +55 -0
- package/dist/server/plugins/inputs/msg.js +106 -0
- package/dist/server/plugins/inputs/mute.js +56 -0
- package/dist/server/plugins/inputs/nick.js +55 -0
- package/dist/server/plugins/inputs/notice.js +42 -0
- package/dist/server/plugins/inputs/part.js +46 -0
- package/dist/server/plugins/inputs/quit.js +27 -0
- package/dist/server/plugins/inputs/raw.js +13 -0
- package/dist/server/plugins/inputs/rejoin.js +25 -0
- package/dist/server/plugins/inputs/topic.js +24 -0
- package/dist/server/plugins/inputs/whois.js +19 -0
- package/dist/server/plugins/irc-events/away.js +59 -0
- package/dist/server/plugins/irc-events/cap.js +62 -0
- package/dist/server/plugins/irc-events/chghost.js +29 -0
- package/dist/server/plugins/irc-events/connection.js +152 -0
- package/dist/server/plugins/irc-events/ctcp.js +72 -0
- package/dist/server/plugins/irc-events/error.js +80 -0
- package/dist/server/plugins/irc-events/help.js +21 -0
- package/dist/server/plugins/irc-events/info.js +21 -0
- package/dist/server/plugins/irc-events/invite.js +27 -0
- package/dist/server/plugins/irc-events/join.js +53 -0
- package/dist/server/plugins/irc-events/kick.js +39 -0
- package/dist/server/plugins/irc-events/link.js +442 -0
- package/dist/server/plugins/irc-events/list.js +47 -0
- package/dist/server/plugins/irc-events/message.js +187 -0
- package/dist/server/plugins/irc-events/mode.js +124 -0
- package/dist/server/plugins/irc-events/modelist.js +67 -0
- package/dist/server/plugins/irc-events/motd.js +29 -0
- package/dist/server/plugins/irc-events/names.js +21 -0
- package/dist/server/plugins/irc-events/nick.js +45 -0
- package/dist/server/plugins/irc-events/part.js +35 -0
- package/dist/server/plugins/irc-events/quit.js +32 -0
- package/dist/server/plugins/irc-events/sasl.js +26 -0
- package/dist/server/plugins/irc-events/topic.js +42 -0
- package/dist/server/plugins/irc-events/unhandled.js +31 -0
- package/dist/server/plugins/irc-events/welcome.js +22 -0
- package/dist/server/plugins/irc-events/whois.js +57 -0
- package/dist/server/plugins/messageStorage/sqlite.js +454 -0
- package/dist/server/plugins/messageStorage/text.js +124 -0
- package/dist/server/plugins/packages/index.js +200 -0
- package/dist/server/plugins/packages/publicClient.js +66 -0
- package/dist/server/plugins/packages/themes.js +61 -0
- package/dist/server/plugins/storage.js +88 -0
- package/dist/server/plugins/sts.js +85 -0
- package/dist/server/plugins/uploader.js +267 -0
- package/dist/server/plugins/webpush.js +99 -0
- package/dist/server/server.js +857 -0
- package/dist/server/storageCleaner.js +131 -0
- package/dist/server/utils/fish.js +432 -0
- package/dist/shared/irc.js +19 -0
- package/dist/shared/linkify.js +81 -0
- package/dist/shared/types/chan.js +22 -0
- package/dist/shared/types/changelog.js +2 -0
- package/dist/shared/types/config.js +2 -0
- package/dist/shared/types/mention.js +2 -0
- package/dist/shared/types/msg.js +34 -0
- package/dist/shared/types/network.js +2 -0
- package/dist/shared/types/storage.js +2 -0
- package/dist/shared/types/user.js +2 -0
- package/dist/webpack.config.js +224 -0
- package/index.js +38 -0
- package/package.json +174 -0
- package/public/audio/pop.wav +0 -0
- package/public/css/style.css +12 -0
- package/public/css/style.css.map +1 -0
- package/public/favicon.ico +0 -0
- package/public/fonts/fa-solid-900.woff +0 -0
- package/public/fonts/fa-solid-900.woff2 +0 -0
- package/public/img/favicon-alerted.ico +0 -0
- package/public/img/icon-alerted-black-transparent-bg-72x72px.png +0 -0
- package/public/img/icon-alerted-grey-bg-192x192px.png +0 -0
- package/public/img/icon-black-transparent-bg.svg +1 -0
- package/public/img/logo-grey-bg-120x120px.png +0 -0
- package/public/img/logo-grey-bg-152x152px.png +0 -0
- package/public/img/logo-grey-bg-167x167px.png +0 -0
- package/public/img/logo-grey-bg-180x180px.png +0 -0
- package/public/img/logo-grey-bg-192x192px.png +0 -0
- package/public/img/logo-grey-bg-512x512px.png +0 -0
- package/public/img/logo-grey-bg.svg +1 -0
- package/public/img/logo-horizontal-transparent-bg-inverted.svg +1 -0
- package/public/img/logo-horizontal-transparent-bg.svg +1 -0
- package/public/img/logo-transparent-bg-inverted.svg +1 -0
- package/public/img/logo-transparent-bg.svg +1 -0
- package/public/img/logo-vertical-transparent-bg-inverted.svg +1 -0
- package/public/img/logo-vertical-transparent-bg.svg +1 -0
- package/public/js/bundle.js +2 -0
- package/public/js/bundle.js.map +1 -0
- package/public/js/bundle.vendor.js +3 -0
- package/public/js/bundle.vendor.js.LICENSE.txt +18 -0
- package/public/js/bundle.vendor.js.map +1 -0
- package/public/js/loading-error-handlers.js +1 -0
- package/public/robots.txt +2 -0
- package/public/service-worker.js +1 -0
- package/public/thelounge.webmanifest +53 -0
- package/public/themes/default.css +35 -0
- package/public/themes/morning.css +183 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
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");
|
|
8
|
+
class Msg {
|
|
9
|
+
from;
|
|
10
|
+
id;
|
|
11
|
+
previews;
|
|
12
|
+
text;
|
|
13
|
+
type;
|
|
14
|
+
self;
|
|
15
|
+
time;
|
|
16
|
+
hostmask;
|
|
17
|
+
target;
|
|
18
|
+
// TODO: new_nick is only on MessageType.NICK,
|
|
19
|
+
// we should probably make Msgs that extend this class and use those
|
|
20
|
+
// throughout. I'll leave any similar fields below.
|
|
21
|
+
new_nick;
|
|
22
|
+
highlight;
|
|
23
|
+
showInActive;
|
|
24
|
+
new_ident;
|
|
25
|
+
new_host;
|
|
26
|
+
ctcpMessage;
|
|
27
|
+
command;
|
|
28
|
+
invitedYou;
|
|
29
|
+
gecos;
|
|
30
|
+
account;
|
|
31
|
+
// these are all just for error:
|
|
32
|
+
error;
|
|
33
|
+
nick;
|
|
34
|
+
channel;
|
|
35
|
+
reason;
|
|
36
|
+
raw_modes;
|
|
37
|
+
when;
|
|
38
|
+
whois;
|
|
39
|
+
users;
|
|
40
|
+
statusmsgGroup;
|
|
41
|
+
params;
|
|
42
|
+
constructor(attr) {
|
|
43
|
+
// Some properties need to be copied in the Msg object instead of referenced
|
|
44
|
+
if (attr) {
|
|
45
|
+
["from", "target"].forEach((prop) => {
|
|
46
|
+
if (attr[prop]) {
|
|
47
|
+
this[prop] = {
|
|
48
|
+
mode: attr[prop].mode,
|
|
49
|
+
nick: attr[prop].nick,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
lodash_1.default.defaults(this, attr, {
|
|
55
|
+
from: {},
|
|
56
|
+
id: 0,
|
|
57
|
+
previews: [],
|
|
58
|
+
text: "",
|
|
59
|
+
type: msg_1.MessageType.MESSAGE,
|
|
60
|
+
self: false,
|
|
61
|
+
});
|
|
62
|
+
if (this.time) {
|
|
63
|
+
this.time = new Date(this.time);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
this.time = new Date();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
findPreview(link) {
|
|
70
|
+
return this.previews.find((preview) => preview.link === link);
|
|
71
|
+
}
|
|
72
|
+
isLoggable() {
|
|
73
|
+
if (this.type === msg_1.MessageType.TOPIC) {
|
|
74
|
+
// Do not log topic that is sent on channel join
|
|
75
|
+
return !!this.from.nick;
|
|
76
|
+
}
|
|
77
|
+
switch (this.type) {
|
|
78
|
+
case msg_1.MessageType.MONOSPACE_BLOCK:
|
|
79
|
+
case msg_1.MessageType.ERROR:
|
|
80
|
+
case msg_1.MessageType.TOPIC_SET_BY:
|
|
81
|
+
case msg_1.MessageType.MODE_CHANNEL:
|
|
82
|
+
case msg_1.MessageType.MODE_USER:
|
|
83
|
+
case msg_1.MessageType.RAW:
|
|
84
|
+
case msg_1.MessageType.WHOIS:
|
|
85
|
+
case msg_1.MessageType.PLUGIN:
|
|
86
|
+
return false;
|
|
87
|
+
default:
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
exports.default = Msg;
|
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
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 uuid_1 = require("uuid");
|
|
8
|
+
const irc_framework_1 = __importDefault(require("irc-framework"));
|
|
9
|
+
const chan_1 = __importDefault(require("./chan"));
|
|
10
|
+
const msg_1 = __importDefault(require("./msg"));
|
|
11
|
+
const prefix_1 = __importDefault(require("./prefix"));
|
|
12
|
+
const helper_1 = __importDefault(require("../helper"));
|
|
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");
|
|
18
|
+
class Network {
|
|
19
|
+
nick;
|
|
20
|
+
name;
|
|
21
|
+
host;
|
|
22
|
+
port;
|
|
23
|
+
tls;
|
|
24
|
+
userDisconnected;
|
|
25
|
+
rejectUnauthorized;
|
|
26
|
+
password;
|
|
27
|
+
awayMessage;
|
|
28
|
+
commands;
|
|
29
|
+
username;
|
|
30
|
+
realname;
|
|
31
|
+
leaveMessage;
|
|
32
|
+
sasl;
|
|
33
|
+
saslAccount;
|
|
34
|
+
saslPassword;
|
|
35
|
+
channels;
|
|
36
|
+
uuid;
|
|
37
|
+
proxyHost;
|
|
38
|
+
proxyPort;
|
|
39
|
+
proxyUsername;
|
|
40
|
+
proxyPassword;
|
|
41
|
+
proxyEnabled;
|
|
42
|
+
highlightRegex;
|
|
43
|
+
// FiSH Blowfish settings persisted per-network
|
|
44
|
+
fishGlobalKey;
|
|
45
|
+
fishKeys;
|
|
46
|
+
irc;
|
|
47
|
+
chanCache;
|
|
48
|
+
ignoreList;
|
|
49
|
+
keepNick;
|
|
50
|
+
status;
|
|
51
|
+
serverOptions;
|
|
52
|
+
// TODO: this is only available on export
|
|
53
|
+
hasSTSPolicy;
|
|
54
|
+
constructor(attr) {
|
|
55
|
+
lodash_1.default.defaults(this, attr, {
|
|
56
|
+
name: "",
|
|
57
|
+
nick: "",
|
|
58
|
+
host: "",
|
|
59
|
+
port: 6667,
|
|
60
|
+
tls: false,
|
|
61
|
+
userDisconnected: false,
|
|
62
|
+
rejectUnauthorized: false,
|
|
63
|
+
password: "",
|
|
64
|
+
awayMessage: "",
|
|
65
|
+
commands: [],
|
|
66
|
+
username: "",
|
|
67
|
+
realname: "",
|
|
68
|
+
leaveMessage: "",
|
|
69
|
+
sasl: "",
|
|
70
|
+
saslAccount: "",
|
|
71
|
+
saslPassword: "",
|
|
72
|
+
channels: [],
|
|
73
|
+
irc: null,
|
|
74
|
+
serverOptions: {
|
|
75
|
+
CHANTYPES: ["#", "&"],
|
|
76
|
+
PREFIX: new prefix_1.default([
|
|
77
|
+
{ symbol: "!", mode: "Y" },
|
|
78
|
+
{ symbol: "@", mode: "o" },
|
|
79
|
+
{ symbol: "%", mode: "h" },
|
|
80
|
+
{ symbol: "+", mode: "v" },
|
|
81
|
+
]),
|
|
82
|
+
NETWORK: "",
|
|
83
|
+
},
|
|
84
|
+
proxyHost: "",
|
|
85
|
+
proxyPort: 1080,
|
|
86
|
+
proxyUsername: "",
|
|
87
|
+
proxyPassword: "",
|
|
88
|
+
proxyEnabled: false,
|
|
89
|
+
// FiSH defaults
|
|
90
|
+
fishGlobalKey: "",
|
|
91
|
+
fishKeys: {},
|
|
92
|
+
chanCache: [],
|
|
93
|
+
ignoreList: [],
|
|
94
|
+
keepNick: null,
|
|
95
|
+
});
|
|
96
|
+
if (!this.uuid) {
|
|
97
|
+
this.uuid = (0, uuid_1.v4)();
|
|
98
|
+
}
|
|
99
|
+
if (!this.name) {
|
|
100
|
+
this.name = this.host;
|
|
101
|
+
}
|
|
102
|
+
this.channels.unshift(new chan_1.default({
|
|
103
|
+
name: this.name,
|
|
104
|
+
type: chan_2.ChanType.LOBBY,
|
|
105
|
+
// The lobby only starts as muted if every channel (unless it's special) is muted.
|
|
106
|
+
// This is A) easier to implement and B) stops some confusion on startup.
|
|
107
|
+
muted: this.channels.length >= 1 &&
|
|
108
|
+
this.channels.every((chan) => chan.muted || chan.type === chan_2.ChanType.SPECIAL),
|
|
109
|
+
}));
|
|
110
|
+
// Ensure FiSH keys are applied to any pre-existing channels loaded from disk
|
|
111
|
+
this.applyBlowKeysToChannels();
|
|
112
|
+
}
|
|
113
|
+
validate(client) {
|
|
114
|
+
// Remove !, :, @ and whitespace characters from nicknames and usernames
|
|
115
|
+
const cleanNick = (str) => str.replace(/[\x00\s:!@]/g, "_").substring(0, 100);
|
|
116
|
+
// Remove new lines and limit length
|
|
117
|
+
const cleanString = (str) => str.replace(/[\x00\r\n]/g, "").substring(0, 300);
|
|
118
|
+
this.setNick(cleanNick(String(this.nick || config_1.default.getDefaultNick())));
|
|
119
|
+
if (!this.username) {
|
|
120
|
+
// If username is empty, make one from the provided nick
|
|
121
|
+
this.username = this.nick.replace(/[^a-zA-Z0-9]/g, "");
|
|
122
|
+
}
|
|
123
|
+
this.username = cleanString(this.username) || "thelounge";
|
|
124
|
+
this.realname = cleanString(this.realname) || this.nick;
|
|
125
|
+
this.leaveMessage = cleanString(this.leaveMessage);
|
|
126
|
+
this.password = cleanString(this.password);
|
|
127
|
+
this.host = cleanString(this.host).toLowerCase();
|
|
128
|
+
this.name = cleanString(this.name);
|
|
129
|
+
this.saslAccount = cleanString(this.saslAccount);
|
|
130
|
+
this.saslPassword = cleanString(this.saslPassword);
|
|
131
|
+
this.proxyHost = cleanString(this.proxyHost);
|
|
132
|
+
this.proxyPort = this.proxyPort || 1080;
|
|
133
|
+
this.proxyUsername = cleanString(this.proxyUsername);
|
|
134
|
+
this.proxyPassword = cleanString(this.proxyPassword);
|
|
135
|
+
this.proxyEnabled = !!this.proxyEnabled;
|
|
136
|
+
const error = function (network, text) {
|
|
137
|
+
network.getLobby().pushMessage(client, new msg_1.default({
|
|
138
|
+
type: msg_2.MessageType.ERROR,
|
|
139
|
+
text: text,
|
|
140
|
+
}), true);
|
|
141
|
+
};
|
|
142
|
+
if (!this.port) {
|
|
143
|
+
this.port = this.tls ? 6697 : 6667;
|
|
144
|
+
}
|
|
145
|
+
if (!["", "plain", "external"].includes(this.sasl)) {
|
|
146
|
+
this.sasl = "";
|
|
147
|
+
}
|
|
148
|
+
if (config_1.default.values.lockNetwork) {
|
|
149
|
+
// This check is needed to prevent invalid user configurations
|
|
150
|
+
if (!config_1.default.values.public &&
|
|
151
|
+
this.host &&
|
|
152
|
+
this.host.length > 0 &&
|
|
153
|
+
this.host !== config_1.default.values.defaults.host) {
|
|
154
|
+
error(this, `The hostname you specified (${this.host}) is not allowed.`);
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
if (config_1.default.values.public) {
|
|
158
|
+
this.name = config_1.default.values.defaults.name;
|
|
159
|
+
// Sync lobby channel name
|
|
160
|
+
this.getLobby().name = config_1.default.values.defaults.name;
|
|
161
|
+
}
|
|
162
|
+
this.host = config_1.default.values.defaults.host;
|
|
163
|
+
this.port = config_1.default.values.defaults.port;
|
|
164
|
+
this.tls = config_1.default.values.defaults.tls;
|
|
165
|
+
this.rejectUnauthorized = config_1.default.values.defaults.rejectUnauthorized;
|
|
166
|
+
}
|
|
167
|
+
if (this.host.length === 0) {
|
|
168
|
+
error(this, "You must specify a hostname to connect.");
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
const stsPolicy = sts_1.default.get(this.host);
|
|
172
|
+
if (stsPolicy && !this.tls) {
|
|
173
|
+
error(this, `${this.host} has an active strict transport security policy, will connect to port ${stsPolicy.port} over a secure connection.`);
|
|
174
|
+
this.port = stsPolicy.port;
|
|
175
|
+
this.tls = true;
|
|
176
|
+
this.rejectUnauthorized = true;
|
|
177
|
+
}
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
createIrcFramework(client) {
|
|
181
|
+
this.irc = new irc_framework_1.default.Client({
|
|
182
|
+
version: false, // We handle it ourselves
|
|
183
|
+
outgoing_addr: config_1.default.values.bind,
|
|
184
|
+
enable_chghost: true,
|
|
185
|
+
enable_echomessage: true,
|
|
186
|
+
enable_setname: true,
|
|
187
|
+
auto_reconnect: true,
|
|
188
|
+
// Exponential backoff maxes out at 300 seconds after 9 reconnects,
|
|
189
|
+
// it will keep trying for well over an hour (plus the timeouts)
|
|
190
|
+
auto_reconnect_max_retries: 30,
|
|
191
|
+
// TODO: this type should be set after setIrcFrameworkOptions
|
|
192
|
+
});
|
|
193
|
+
this.setIrcFrameworkOptions(client);
|
|
194
|
+
this.irc.requestCap([
|
|
195
|
+
"znc.in/self-message", // Legacy echo-message for ZNC
|
|
196
|
+
"znc.in/playback", // See http://wiki.znc.in/Playback
|
|
197
|
+
]);
|
|
198
|
+
}
|
|
199
|
+
setIrcFrameworkOptions(client) {
|
|
200
|
+
this.irc.options.host = this.host;
|
|
201
|
+
this.irc.options.port = this.port;
|
|
202
|
+
this.irc.options.password = this.password;
|
|
203
|
+
this.irc.options.nick = this.nick;
|
|
204
|
+
this.irc.options.username = config_1.default.values.useHexIp
|
|
205
|
+
? helper_1.default.ip2hex(client.config.browser.ip)
|
|
206
|
+
: this.username;
|
|
207
|
+
this.irc.options.gecos = this.realname;
|
|
208
|
+
this.irc.options.tls = this.tls;
|
|
209
|
+
this.irc.options.rejectUnauthorized = this.rejectUnauthorized;
|
|
210
|
+
this.irc.options.webirc = this.createWebIrc(client);
|
|
211
|
+
this.irc.options.client_certificate = null;
|
|
212
|
+
if (this.proxyEnabled) {
|
|
213
|
+
this.irc.options.socks = {
|
|
214
|
+
host: this.proxyHost,
|
|
215
|
+
port: this.proxyPort,
|
|
216
|
+
user: this.proxyUsername,
|
|
217
|
+
pass: this.proxyPassword,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
delete this.irc.options.socks;
|
|
222
|
+
}
|
|
223
|
+
if (!this.sasl) {
|
|
224
|
+
delete this.irc.options.sasl_mechanism;
|
|
225
|
+
delete this.irc.options.account;
|
|
226
|
+
}
|
|
227
|
+
else if (this.sasl === "external") {
|
|
228
|
+
this.irc.options.sasl_mechanism = "EXTERNAL";
|
|
229
|
+
this.irc.options.account = {};
|
|
230
|
+
this.irc.options.client_certificate = clientCertificate_1.default.get(this.uuid);
|
|
231
|
+
}
|
|
232
|
+
else if (this.sasl === "plain") {
|
|
233
|
+
delete this.irc.options.sasl_mechanism;
|
|
234
|
+
this.irc.options.account = {
|
|
235
|
+
account: this.saslAccount,
|
|
236
|
+
password: this.saslPassword,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
resolveBlowKeyFor(name) {
|
|
241
|
+
if (!name) {
|
|
242
|
+
return this.fishGlobalKey || undefined;
|
|
243
|
+
}
|
|
244
|
+
const keyMap = this.fishKeys || {};
|
|
245
|
+
const lower = name.toLowerCase();
|
|
246
|
+
return keyMap[lower] || this.fishGlobalKey || undefined;
|
|
247
|
+
}
|
|
248
|
+
applyBlowKeysToChannels() {
|
|
249
|
+
for (const c of this.channels) {
|
|
250
|
+
if (c.type !== chan_2.ChanType.CHANNEL && c.type !== chan_2.ChanType.QUERY) {
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
c.blowfishKey = this.resolveBlowKeyFor(c.name);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
createWebIrc(client) {
|
|
257
|
+
if (!config_1.default.values.webirc ||
|
|
258
|
+
!Object.prototype.hasOwnProperty.call(config_1.default.values.webirc, this.host)) {
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
const webircObject = {
|
|
262
|
+
password: config_1.default.values.webirc[this.host],
|
|
263
|
+
username: "thelounge",
|
|
264
|
+
address: client.config.browser?.ip,
|
|
265
|
+
hostname: client.config.browser?.hostname,
|
|
266
|
+
options: {},
|
|
267
|
+
};
|
|
268
|
+
// https://ircv3.net/specs/extensions/webirc#options
|
|
269
|
+
if (client.config.browser?.isSecure) {
|
|
270
|
+
webircObject.options = {
|
|
271
|
+
secure: true,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
if (typeof config_1.default.values.webirc[this.host] === "function") {
|
|
275
|
+
webircObject.password = null;
|
|
276
|
+
return config_1.default.values.webirc[this.host](webircObject, this);
|
|
277
|
+
}
|
|
278
|
+
return webircObject;
|
|
279
|
+
}
|
|
280
|
+
edit(client, args) {
|
|
281
|
+
const oldNetworkName = this.name;
|
|
282
|
+
const oldNick = this.nick;
|
|
283
|
+
const oldRealname = this.realname;
|
|
284
|
+
this.keepNick = null;
|
|
285
|
+
this.nick = args.nick;
|
|
286
|
+
this.host = String(args.host || "");
|
|
287
|
+
this.name = String(args.name || "") || this.host;
|
|
288
|
+
this.port = parseInt(args.port, 10);
|
|
289
|
+
this.tls = !!args.tls;
|
|
290
|
+
this.rejectUnauthorized = !!args.rejectUnauthorized;
|
|
291
|
+
this.password = String(args.password || "");
|
|
292
|
+
this.username = String(args.username || "");
|
|
293
|
+
this.realname = String(args.realname || "");
|
|
294
|
+
this.leaveMessage = String(args.leaveMessage || "");
|
|
295
|
+
this.sasl = String(args.sasl || "");
|
|
296
|
+
this.saslAccount = String(args.saslAccount || "");
|
|
297
|
+
this.saslPassword = String(args.saslPassword || "");
|
|
298
|
+
this.proxyHost = String(args.proxyHost || "");
|
|
299
|
+
this.proxyPort = parseInt(args.proxyPort, 10);
|
|
300
|
+
this.proxyUsername = String(args.proxyUsername || "");
|
|
301
|
+
this.proxyPassword = String(args.proxyPassword || "");
|
|
302
|
+
this.proxyEnabled = !!args.proxyEnabled;
|
|
303
|
+
// Split commands into an array
|
|
304
|
+
this.commands = String(args.commands || "")
|
|
305
|
+
.replace(/\r\n|\r|\n/g, "\n")
|
|
306
|
+
.split("\n")
|
|
307
|
+
.filter((command) => command.length > 0);
|
|
308
|
+
// FiSH: read global key and per-target keys (only update when provided)
|
|
309
|
+
if (Object.prototype.hasOwnProperty.call(args, "fishGlobalKey")) {
|
|
310
|
+
this.fishGlobalKey = String(args.fishGlobalKey || "").trim();
|
|
311
|
+
}
|
|
312
|
+
if (Object.prototype.hasOwnProperty.call(args, "fishKeysText")) {
|
|
313
|
+
const fishKeysText = String(args.fishKeysText || "").replace(/\r\n|\r|\n/g, "\n");
|
|
314
|
+
const map = {};
|
|
315
|
+
fishKeysText.split("\n").forEach((line) => {
|
|
316
|
+
const trimmed = line.trim();
|
|
317
|
+
if (!trimmed) {
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
const spaceIdx = trimmed.indexOf(" ");
|
|
321
|
+
if (spaceIdx === -1) {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
const name = trimmed.substring(0, spaceIdx).toLowerCase();
|
|
325
|
+
const key = trimmed.substring(spaceIdx + 1).trim();
|
|
326
|
+
if (!name || !key) {
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
map[name] = key;
|
|
330
|
+
});
|
|
331
|
+
this.fishKeys = map;
|
|
332
|
+
}
|
|
333
|
+
// Sync lobby channel name
|
|
334
|
+
this.getLobby().name = this.name;
|
|
335
|
+
if (this.name !== oldNetworkName) {
|
|
336
|
+
// Send updated network name to all connected clients
|
|
337
|
+
client.emit("network:name", {
|
|
338
|
+
uuid: this.uuid,
|
|
339
|
+
name: this.name,
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
if (!this.validate(client)) {
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
// Apply FiSH keys to existing channels after edit
|
|
346
|
+
this.applyBlowKeysToChannels();
|
|
347
|
+
if (this.irc) {
|
|
348
|
+
if (this.nick !== oldNick) {
|
|
349
|
+
if (this.irc.connected) {
|
|
350
|
+
// Send new nick straight away
|
|
351
|
+
this.irc.changeNick(this.nick);
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
this.irc.user.nick = this.nick;
|
|
355
|
+
// Update UI nick straight away if IRC is not connected
|
|
356
|
+
client.emit("nick", {
|
|
357
|
+
network: this.uuid,
|
|
358
|
+
nick: this.nick,
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
if (this.irc.connected &&
|
|
363
|
+
this.realname !== oldRealname &&
|
|
364
|
+
this.irc.network.cap.isEnabled("setname")) {
|
|
365
|
+
this.irc.raw("SETNAME", this.realname);
|
|
366
|
+
}
|
|
367
|
+
this.setIrcFrameworkOptions(client);
|
|
368
|
+
if (this.irc.options?.username) {
|
|
369
|
+
this.irc.user.username = this.irc.options.username;
|
|
370
|
+
}
|
|
371
|
+
if (this.irc.options?.gecos) {
|
|
372
|
+
this.irc.user.gecos = this.irc.options.gecos;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
client.save();
|
|
376
|
+
}
|
|
377
|
+
destroy() {
|
|
378
|
+
this.channels.forEach((channel) => channel.destroy());
|
|
379
|
+
}
|
|
380
|
+
setNick(nick) {
|
|
381
|
+
this.nick = nick;
|
|
382
|
+
this.highlightRegex = new RegExp(
|
|
383
|
+
// Do not match characters and numbers (unless IRC color)
|
|
384
|
+
"(?:^|[^a-z0-9]|\x03[0-9]{1,2})" +
|
|
385
|
+
// Escape nickname, as it may contain regex stuff
|
|
386
|
+
lodash_1.default.escapeRegExp(nick) +
|
|
387
|
+
// Do not match characters and numbers
|
|
388
|
+
"(?:[^a-z0-9]|$)",
|
|
389
|
+
// Case insensitive search
|
|
390
|
+
"i");
|
|
391
|
+
if (this.keepNick === nick) {
|
|
392
|
+
this.keepNick = null;
|
|
393
|
+
}
|
|
394
|
+
if (this.irc?.options) {
|
|
395
|
+
this.irc.options.nick = nick;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
getFilteredClone(lastActiveChannel, lastMessage) {
|
|
399
|
+
return {
|
|
400
|
+
uuid: this.uuid,
|
|
401
|
+
name: this.name,
|
|
402
|
+
nick: this.nick,
|
|
403
|
+
serverOptions: this.serverOptions,
|
|
404
|
+
status: this.getNetworkStatus(),
|
|
405
|
+
channels: this.channels.map((channel) => channel.getFilteredClone(lastActiveChannel, lastMessage)),
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
getNetworkStatus() {
|
|
409
|
+
const status = {
|
|
410
|
+
connected: false,
|
|
411
|
+
secure: false,
|
|
412
|
+
};
|
|
413
|
+
if (this.irc && this.irc.connection && this.irc.connection.transport) {
|
|
414
|
+
const transport = this.irc.connection.transport;
|
|
415
|
+
if (transport.socket) {
|
|
416
|
+
const isLocalhost = transport.socket.remoteAddress === "127.0.0.1";
|
|
417
|
+
const isAuthorized = transport.socket.encrypted && transport.socket.authorized;
|
|
418
|
+
status.connected = transport.isConnected();
|
|
419
|
+
status.secure = isAuthorized || isLocalhost;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
return status;
|
|
423
|
+
}
|
|
424
|
+
addChannel(newChan) {
|
|
425
|
+
// Assign FiSH key based on network configuration when adding
|
|
426
|
+
if (newChan && (newChan.type === chan_2.ChanType.CHANNEL || newChan.type === chan_2.ChanType.QUERY)) {
|
|
427
|
+
newChan.blowfishKey = this.resolveBlowKeyFor(newChan.name);
|
|
428
|
+
}
|
|
429
|
+
let index = this.channels.length; // Default to putting as the last item in the array
|
|
430
|
+
// Don't sort special channels in amongst channels/users.
|
|
431
|
+
if (newChan.type === chan_2.ChanType.CHANNEL || newChan.type === chan_2.ChanType.QUERY) {
|
|
432
|
+
// We start at 1 so we don't test against the lobby
|
|
433
|
+
for (let i = 1; i < this.channels.length; i++) {
|
|
434
|
+
const compareChan = this.channels[i];
|
|
435
|
+
// Negative if the new chan is alphabetically before the next chan in the list, positive if after
|
|
436
|
+
if (newChan.name.localeCompare(compareChan.name, undefined, {
|
|
437
|
+
sensitivity: "base",
|
|
438
|
+
}) <= 0 ||
|
|
439
|
+
(compareChan.type !== chan_2.ChanType.CHANNEL && compareChan.type !== chan_2.ChanType.QUERY)) {
|
|
440
|
+
index = i;
|
|
441
|
+
break;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
this.channels.splice(index, 0, newChan);
|
|
446
|
+
return index;
|
|
447
|
+
}
|
|
448
|
+
quit(quitMessage) {
|
|
449
|
+
if (!this.irc) {
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
// https://ircv3.net/specs/extensions/sts#rescheduling-expiry-on-disconnect
|
|
453
|
+
sts_1.default.refreshExpiration(this.host);
|
|
454
|
+
this.irc.quit(quitMessage || this.leaveMessage || config_1.default.values.leaveMessage);
|
|
455
|
+
}
|
|
456
|
+
exportForEdit() {
|
|
457
|
+
const fieldsToReturn = [
|
|
458
|
+
"uuid",
|
|
459
|
+
"name",
|
|
460
|
+
"nick",
|
|
461
|
+
"password",
|
|
462
|
+
"username",
|
|
463
|
+
"realname",
|
|
464
|
+
"leaveMessage",
|
|
465
|
+
"sasl",
|
|
466
|
+
"saslAccount",
|
|
467
|
+
"saslPassword",
|
|
468
|
+
"commands",
|
|
469
|
+
"proxyEnabled",
|
|
470
|
+
"proxyHost",
|
|
471
|
+
"proxyPort",
|
|
472
|
+
"proxyUsername",
|
|
473
|
+
"proxyPassword",
|
|
474
|
+
];
|
|
475
|
+
if (!config_1.default.values.lockNetwork) {
|
|
476
|
+
fieldsToReturn.push("host");
|
|
477
|
+
fieldsToReturn.push("port");
|
|
478
|
+
fieldsToReturn.push("tls");
|
|
479
|
+
fieldsToReturn.push("rejectUnauthorized");
|
|
480
|
+
}
|
|
481
|
+
const data = lodash_1.default.pick(this, fieldsToReturn);
|
|
482
|
+
data.hasSTSPolicy = !!sts_1.default.get(this.host);
|
|
483
|
+
// Include FiSH fields for editing UI
|
|
484
|
+
data.fishGlobalKey = this.fishGlobalKey || "";
|
|
485
|
+
const lines = Object.entries(this.fishKeys || {}).map(([name, key]) => `${name} ${key}`);
|
|
486
|
+
data.fishKeysText = lines.join("\n");
|
|
487
|
+
return data;
|
|
488
|
+
}
|
|
489
|
+
export() {
|
|
490
|
+
const network = lodash_1.default.pick(this, [
|
|
491
|
+
"uuid",
|
|
492
|
+
"awayMessage",
|
|
493
|
+
"nick",
|
|
494
|
+
"name",
|
|
495
|
+
"host",
|
|
496
|
+
"port",
|
|
497
|
+
"tls",
|
|
498
|
+
"userDisconnected",
|
|
499
|
+
"rejectUnauthorized",
|
|
500
|
+
"password",
|
|
501
|
+
"username",
|
|
502
|
+
"realname",
|
|
503
|
+
"leaveMessage",
|
|
504
|
+
"sasl",
|
|
505
|
+
"saslAccount",
|
|
506
|
+
"saslPassword",
|
|
507
|
+
"commands",
|
|
508
|
+
"ignoreList",
|
|
509
|
+
"proxyHost",
|
|
510
|
+
"proxyPort",
|
|
511
|
+
"proxyUsername",
|
|
512
|
+
"proxyEnabled",
|
|
513
|
+
"proxyPassword",
|
|
514
|
+
// FiSH persistence
|
|
515
|
+
"fishGlobalKey",
|
|
516
|
+
"fishKeys",
|
|
517
|
+
]);
|
|
518
|
+
network.channels = this.channels
|
|
519
|
+
.filter(function (channel) {
|
|
520
|
+
return channel.type === chan_2.ChanType.CHANNEL || channel.type === chan_2.ChanType.QUERY;
|
|
521
|
+
})
|
|
522
|
+
.map(function (chan) {
|
|
523
|
+
const keys = ["name", "muted"];
|
|
524
|
+
if (chan.type === chan_2.ChanType.CHANNEL) {
|
|
525
|
+
keys.push("key");
|
|
526
|
+
}
|
|
527
|
+
else if (chan.type === chan_2.ChanType.QUERY) {
|
|
528
|
+
keys.push("type");
|
|
529
|
+
}
|
|
530
|
+
return lodash_1.default.pick(chan, keys);
|
|
531
|
+
// Override the type because we're omitting ID
|
|
532
|
+
});
|
|
533
|
+
return network;
|
|
534
|
+
}
|
|
535
|
+
getChannel(name) {
|
|
536
|
+
name = name.toLowerCase();
|
|
537
|
+
return lodash_1.default.find(this.channels, function (that, i) {
|
|
538
|
+
// Skip network lobby (it's always unshifted into first position)
|
|
539
|
+
return i > 0 && that.name.toLowerCase() === name;
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
getLobby() {
|
|
543
|
+
return this.channels[0];
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
exports.default = Network;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
class Prefix {
|
|
4
|
+
prefix;
|
|
5
|
+
modeToSymbol;
|
|
6
|
+
symbols;
|
|
7
|
+
constructor(prefix) {
|
|
8
|
+
this.prefix = prefix || []; // [{symbol: "@", mode: "o"}, ... ]
|
|
9
|
+
this.modeToSymbol = {};
|
|
10
|
+
this.symbols = [];
|
|
11
|
+
this._update_internals();
|
|
12
|
+
}
|
|
13
|
+
_update_internals() {
|
|
14
|
+
// clean out the old cruft
|
|
15
|
+
this.modeToSymbol = {};
|
|
16
|
+
this.symbols = [];
|
|
17
|
+
const that = this;
|
|
18
|
+
this.prefix.forEach(function (p) {
|
|
19
|
+
that.modeToSymbol[p.mode] = p.symbol;
|
|
20
|
+
that.symbols.push(p.symbol);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
update(prefix) {
|
|
24
|
+
this.prefix = prefix || [];
|
|
25
|
+
this._update_internals();
|
|
26
|
+
}
|
|
27
|
+
forEach(f) {
|
|
28
|
+
return this.prefix.forEach(f);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
exports.default = Prefix;
|