@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.
Files changed (148) hide show
  1. package/.thelounge_home +1 -0
  2. package/LICENSE +22 -0
  3. package/README.md +95 -0
  4. package/client/index.html.tpl +69 -0
  5. package/dist/defaults/config.js +465 -0
  6. package/dist/package.json +174 -0
  7. package/dist/server/client.js +678 -0
  8. package/dist/server/clientManager.js +220 -0
  9. package/dist/server/command-line/index.js +85 -0
  10. package/dist/server/command-line/install.js +123 -0
  11. package/dist/server/command-line/outdated.js +30 -0
  12. package/dist/server/command-line/start.js +34 -0
  13. package/dist/server/command-line/storage.js +103 -0
  14. package/dist/server/command-line/uninstall.js +40 -0
  15. package/dist/server/command-line/upgrade.js +64 -0
  16. package/dist/server/command-line/users/add.js +67 -0
  17. package/dist/server/command-line/users/edit.js +39 -0
  18. package/dist/server/command-line/users/index.js +17 -0
  19. package/dist/server/command-line/users/list.js +53 -0
  20. package/dist/server/command-line/users/remove.js +37 -0
  21. package/dist/server/command-line/users/reset.js +64 -0
  22. package/dist/server/command-line/utils.js +177 -0
  23. package/dist/server/config.js +138 -0
  24. package/dist/server/helper.js +161 -0
  25. package/dist/server/identification.js +139 -0
  26. package/dist/server/index.js +3 -0
  27. package/dist/server/log.js +35 -0
  28. package/dist/server/models/chan.js +275 -0
  29. package/dist/server/models/msg.js +92 -0
  30. package/dist/server/models/network.js +546 -0
  31. package/dist/server/models/prefix.js +31 -0
  32. package/dist/server/models/user.js +42 -0
  33. package/dist/server/plugins/auth/ldap.js +188 -0
  34. package/dist/server/plugins/auth/local.js +41 -0
  35. package/dist/server/plugins/auth.js +70 -0
  36. package/dist/server/plugins/changelog.js +103 -0
  37. package/dist/server/plugins/clientCertificate.js +115 -0
  38. package/dist/server/plugins/dev-server.js +33 -0
  39. package/dist/server/plugins/inputs/action.js +54 -0
  40. package/dist/server/plugins/inputs/away.js +20 -0
  41. package/dist/server/plugins/inputs/ban.js +45 -0
  42. package/dist/server/plugins/inputs/blow.js +44 -0
  43. package/dist/server/plugins/inputs/connect.js +41 -0
  44. package/dist/server/plugins/inputs/ctcp.js +29 -0
  45. package/dist/server/plugins/inputs/disconnect.js +15 -0
  46. package/dist/server/plugins/inputs/ignore.js +74 -0
  47. package/dist/server/plugins/inputs/ignorelist.js +50 -0
  48. package/dist/server/plugins/inputs/index.js +105 -0
  49. package/dist/server/plugins/inputs/invite.js +31 -0
  50. package/dist/server/plugins/inputs/kick.js +26 -0
  51. package/dist/server/plugins/inputs/kill.js +13 -0
  52. package/dist/server/plugins/inputs/list.js +12 -0
  53. package/dist/server/plugins/inputs/mode.js +55 -0
  54. package/dist/server/plugins/inputs/msg.js +106 -0
  55. package/dist/server/plugins/inputs/mute.js +56 -0
  56. package/dist/server/plugins/inputs/nick.js +55 -0
  57. package/dist/server/plugins/inputs/notice.js +42 -0
  58. package/dist/server/plugins/inputs/part.js +46 -0
  59. package/dist/server/plugins/inputs/quit.js +27 -0
  60. package/dist/server/plugins/inputs/raw.js +13 -0
  61. package/dist/server/plugins/inputs/rejoin.js +25 -0
  62. package/dist/server/plugins/inputs/topic.js +24 -0
  63. package/dist/server/plugins/inputs/whois.js +19 -0
  64. package/dist/server/plugins/irc-events/away.js +59 -0
  65. package/dist/server/plugins/irc-events/cap.js +62 -0
  66. package/dist/server/plugins/irc-events/chghost.js +29 -0
  67. package/dist/server/plugins/irc-events/connection.js +152 -0
  68. package/dist/server/plugins/irc-events/ctcp.js +72 -0
  69. package/dist/server/plugins/irc-events/error.js +80 -0
  70. package/dist/server/plugins/irc-events/help.js +21 -0
  71. package/dist/server/plugins/irc-events/info.js +21 -0
  72. package/dist/server/plugins/irc-events/invite.js +27 -0
  73. package/dist/server/plugins/irc-events/join.js +53 -0
  74. package/dist/server/plugins/irc-events/kick.js +39 -0
  75. package/dist/server/plugins/irc-events/link.js +442 -0
  76. package/dist/server/plugins/irc-events/list.js +47 -0
  77. package/dist/server/plugins/irc-events/message.js +187 -0
  78. package/dist/server/plugins/irc-events/mode.js +124 -0
  79. package/dist/server/plugins/irc-events/modelist.js +67 -0
  80. package/dist/server/plugins/irc-events/motd.js +29 -0
  81. package/dist/server/plugins/irc-events/names.js +21 -0
  82. package/dist/server/plugins/irc-events/nick.js +45 -0
  83. package/dist/server/plugins/irc-events/part.js +35 -0
  84. package/dist/server/plugins/irc-events/quit.js +32 -0
  85. package/dist/server/plugins/irc-events/sasl.js +26 -0
  86. package/dist/server/plugins/irc-events/topic.js +42 -0
  87. package/dist/server/plugins/irc-events/unhandled.js +31 -0
  88. package/dist/server/plugins/irc-events/welcome.js +22 -0
  89. package/dist/server/plugins/irc-events/whois.js +57 -0
  90. package/dist/server/plugins/messageStorage/sqlite.js +454 -0
  91. package/dist/server/plugins/messageStorage/text.js +124 -0
  92. package/dist/server/plugins/packages/index.js +200 -0
  93. package/dist/server/plugins/packages/publicClient.js +66 -0
  94. package/dist/server/plugins/packages/themes.js +61 -0
  95. package/dist/server/plugins/storage.js +88 -0
  96. package/dist/server/plugins/sts.js +85 -0
  97. package/dist/server/plugins/uploader.js +267 -0
  98. package/dist/server/plugins/webpush.js +99 -0
  99. package/dist/server/server.js +857 -0
  100. package/dist/server/storageCleaner.js +131 -0
  101. package/dist/server/utils/fish.js +432 -0
  102. package/dist/shared/irc.js +19 -0
  103. package/dist/shared/linkify.js +81 -0
  104. package/dist/shared/types/chan.js +22 -0
  105. package/dist/shared/types/changelog.js +2 -0
  106. package/dist/shared/types/config.js +2 -0
  107. package/dist/shared/types/mention.js +2 -0
  108. package/dist/shared/types/msg.js +34 -0
  109. package/dist/shared/types/network.js +2 -0
  110. package/dist/shared/types/storage.js +2 -0
  111. package/dist/shared/types/user.js +2 -0
  112. package/dist/webpack.config.js +224 -0
  113. package/index.js +38 -0
  114. package/package.json +174 -0
  115. package/public/audio/pop.wav +0 -0
  116. package/public/css/style.css +12 -0
  117. package/public/css/style.css.map +1 -0
  118. package/public/favicon.ico +0 -0
  119. package/public/fonts/fa-solid-900.woff +0 -0
  120. package/public/fonts/fa-solid-900.woff2 +0 -0
  121. package/public/img/favicon-alerted.ico +0 -0
  122. package/public/img/icon-alerted-black-transparent-bg-72x72px.png +0 -0
  123. package/public/img/icon-alerted-grey-bg-192x192px.png +0 -0
  124. package/public/img/icon-black-transparent-bg.svg +1 -0
  125. package/public/img/logo-grey-bg-120x120px.png +0 -0
  126. package/public/img/logo-grey-bg-152x152px.png +0 -0
  127. package/public/img/logo-grey-bg-167x167px.png +0 -0
  128. package/public/img/logo-grey-bg-180x180px.png +0 -0
  129. package/public/img/logo-grey-bg-192x192px.png +0 -0
  130. package/public/img/logo-grey-bg-512x512px.png +0 -0
  131. package/public/img/logo-grey-bg.svg +1 -0
  132. package/public/img/logo-horizontal-transparent-bg-inverted.svg +1 -0
  133. package/public/img/logo-horizontal-transparent-bg.svg +1 -0
  134. package/public/img/logo-transparent-bg-inverted.svg +1 -0
  135. package/public/img/logo-transparent-bg.svg +1 -0
  136. package/public/img/logo-vertical-transparent-bg-inverted.svg +1 -0
  137. package/public/img/logo-vertical-transparent-bg.svg +1 -0
  138. package/public/js/bundle.js +2 -0
  139. package/public/js/bundle.js.map +1 -0
  140. package/public/js/bundle.vendor.js +3 -0
  141. package/public/js/bundle.vendor.js.LICENSE.txt +18 -0
  142. package/public/js/bundle.vendor.js.map +1 -0
  143. package/public/js/loading-error-handlers.js +1 -0
  144. package/public/robots.txt +2 -0
  145. package/public/service-worker.js +1 -0
  146. package/public/thelounge.webmanifest +53 -0
  147. package/public/themes/default.css +35 -0
  148. 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;