2016-10-09 19:14:02 +00:00
|
|
|
"use strict";
|
|
|
|
|
2016-09-05 12:37:27 +00:00
|
|
|
const pkg = require("../package.json");
|
2018-01-11 11:33:36 +00:00
|
|
|
const _ = require("lodash");
|
|
|
|
const path = require("path");
|
|
|
|
const os = require("os");
|
|
|
|
const fs = require("fs");
|
|
|
|
const net = require("net");
|
|
|
|
const bcrypt = require("bcryptjs");
|
2019-03-08 10:29:49 +00:00
|
|
|
const crypto = require("crypto");
|
2014-10-04 23:22:23 +00:00
|
|
|
|
2017-12-07 03:54:54 +00:00
|
|
|
const Helper = {
|
|
|
|
expandHome,
|
|
|
|
getVersion,
|
2019-03-08 10:29:49 +00:00
|
|
|
getVersionCacheBust,
|
2020-08-21 11:26:35 +00:00
|
|
|
getVersionNumber,
|
2017-12-07 03:54:54 +00:00
|
|
|
getGitCommit,
|
|
|
|
ip2hex,
|
2018-03-11 18:17:57 +00:00
|
|
|
parseHostmask,
|
|
|
|
compareHostmask,
|
2021-11-02 20:42:17 +00:00
|
|
|
compareWithWildcard,
|
2016-10-21 19:00:43 +00:00
|
|
|
|
|
|
|
password: {
|
|
|
|
hash: passwordHash,
|
|
|
|
compare: passwordCompare,
|
|
|
|
requiresUpdate: passwordRequiresUpdate,
|
|
|
|
},
|
2014-09-13 12:23:17 +00:00
|
|
|
};
|
|
|
|
|
2016-05-09 16:19:16 +00:00
|
|
|
module.exports = Helper;
|
|
|
|
|
2016-09-05 12:37:27 +00:00
|
|
|
function getVersion() {
|
|
|
|
const gitCommit = getGitCommit();
|
2018-04-15 22:19:25 +00:00
|
|
|
const version = `v${pkg.version}`;
|
|
|
|
return gitCommit ? `source (${gitCommit} / ${version})` : version;
|
2016-09-05 12:37:27 +00:00
|
|
|
}
|
|
|
|
|
2020-08-21 11:26:35 +00:00
|
|
|
function getVersionNumber() {
|
|
|
|
return pkg.version;
|
|
|
|
}
|
|
|
|
|
2016-09-05 12:37:27 +00:00
|
|
|
let _gitCommit;
|
2018-02-20 07:28:04 +00:00
|
|
|
|
2016-09-05 12:37:27 +00:00
|
|
|
function getGitCommit() {
|
|
|
|
if (_gitCommit !== undefined) {
|
|
|
|
return _gitCommit;
|
|
|
|
}
|
2018-02-20 07:28:04 +00:00
|
|
|
|
2021-12-03 17:14:26 +00:00
|
|
|
if (!fs.existsSync(path.resolve(__dirname, "..", ".git"))) {
|
2018-04-16 14:08:30 +00:00
|
|
|
_gitCommit = null;
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2016-09-05 12:37:27 +00:00
|
|
|
try {
|
|
|
|
_gitCommit = require("child_process")
|
2018-04-16 13:35:17 +00:00
|
|
|
.execSync(
|
|
|
|
"git rev-parse --short HEAD", // Returns hash of current commit
|
|
|
|
{stdio: ["ignore", "pipe", "ignore"]}
|
|
|
|
)
|
2016-09-05 12:37:27 +00:00
|
|
|
.toString()
|
|
|
|
.trim();
|
|
|
|
return _gitCommit;
|
|
|
|
} catch (e) {
|
|
|
|
// Not a git repository or git is not installed
|
|
|
|
_gitCommit = null;
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-08 10:29:49 +00:00
|
|
|
function getVersionCacheBust() {
|
2020-03-21 20:55:36 +00:00
|
|
|
const hash = crypto.createHash("sha256").update(Helper.getVersion()).digest("hex");
|
2019-03-08 10:29:49 +00:00
|
|
|
|
|
|
|
return hash.substring(0, 10);
|
|
|
|
}
|
|
|
|
|
2016-11-19 18:32:47 +00:00
|
|
|
function ip2hex(address) {
|
|
|
|
// no ipv6 support
|
|
|
|
if (!net.isIPv4(address)) {
|
|
|
|
return "00000000";
|
|
|
|
}
|
|
|
|
|
2019-07-17 09:33:59 +00:00
|
|
|
return address
|
|
|
|
.split(".")
|
2020-03-21 20:55:36 +00:00
|
|
|
.map(function (octet) {
|
2019-07-17 09:33:59 +00:00
|
|
|
let hex = parseInt(octet, 10).toString(16);
|
2016-11-19 18:32:47 +00:00
|
|
|
|
2019-07-17 09:33:59 +00:00
|
|
|
if (hex.length === 1) {
|
|
|
|
hex = "0" + hex;
|
|
|
|
}
|
2016-11-19 18:32:47 +00:00
|
|
|
|
2019-07-17 09:33:59 +00:00
|
|
|
return hex;
|
|
|
|
})
|
|
|
|
.join("");
|
2016-11-19 18:32:47 +00:00
|
|
|
}
|
|
|
|
|
2017-12-08 04:33:43 +00:00
|
|
|
// Expand ~ into the current user home dir.
|
|
|
|
// This does *not* support `~other_user/tmp` => `/home/other_user/tmp`.
|
2016-05-08 06:21:31 +00:00
|
|
|
function expandHome(shortenedPath) {
|
2017-04-17 19:48:28 +00:00
|
|
|
if (!shortenedPath) {
|
|
|
|
return "";
|
|
|
|
}
|
2016-04-27 08:13:25 +00:00
|
|
|
|
2017-08-16 07:06:20 +00:00
|
|
|
const home = os.homedir().replace("$", "$$$$");
|
2016-05-08 06:21:31 +00:00
|
|
|
return path.resolve(shortenedPath.replace(/^~($|\/|\\)/, home + "$1"));
|
2016-04-27 08:13:25 +00:00
|
|
|
}
|
2016-10-21 19:00:43 +00:00
|
|
|
|
|
|
|
function passwordRequiresUpdate(password) {
|
|
|
|
return bcrypt.getRounds(password) !== 11;
|
|
|
|
}
|
|
|
|
|
|
|
|
function passwordHash(password) {
|
|
|
|
return bcrypt.hashSync(password, bcrypt.genSaltSync(11));
|
|
|
|
}
|
|
|
|
|
|
|
|
function passwordCompare(password, expected) {
|
2017-03-23 07:47:51 +00:00
|
|
|
return bcrypt.compare(password, expected);
|
2016-10-21 19:00:43 +00:00
|
|
|
}
|
2018-03-10 11:54:51 +00:00
|
|
|
|
2018-03-11 18:17:57 +00:00
|
|
|
function parseHostmask(hostmask) {
|
|
|
|
let nick = "";
|
|
|
|
let ident = "*";
|
|
|
|
let hostname = "*";
|
|
|
|
let parts = [];
|
|
|
|
|
|
|
|
// Parse hostname first, then parse the rest
|
|
|
|
parts = hostmask.split("@");
|
|
|
|
|
|
|
|
if (parts.length >= 2) {
|
|
|
|
hostname = parts[1] || "*";
|
|
|
|
hostmask = parts[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
hostname = hostname.toLowerCase();
|
|
|
|
|
|
|
|
parts = hostmask.split("!");
|
|
|
|
|
|
|
|
if (parts.length >= 2) {
|
|
|
|
ident = parts[1] || "*";
|
|
|
|
hostmask = parts[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
ident = ident.toLowerCase();
|
|
|
|
|
|
|
|
nick = hostmask.toLowerCase() || "*";
|
|
|
|
|
|
|
|
const result = {
|
|
|
|
nick: nick,
|
|
|
|
ident: ident,
|
|
|
|
hostname: hostname,
|
|
|
|
};
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
function compareHostmask(a, b) {
|
2019-07-17 09:33:59 +00:00
|
|
|
return (
|
2021-11-02 20:42:17 +00:00
|
|
|
compareWithWildcard(a.nick, b.nick) &&
|
|
|
|
compareWithWildcard(a.ident, b.ident) &&
|
|
|
|
compareWithWildcard(a.hostname, b.hostname)
|
2019-07-17 09:33:59 +00:00
|
|
|
);
|
2018-03-11 18:17:57 +00:00
|
|
|
}
|
2021-11-02 20:42:17 +00:00
|
|
|
|
|
|
|
function compareWithWildcard(a, b) {
|
|
|
|
// we allow '*' and '?' wildcards in our comparison.
|
|
|
|
// this is mostly aligned with https://modern.ircdocs.horse/#wildcard-expressions
|
|
|
|
// but we do not support the escaping. The ABNF does not seem to be clear as to
|
|
|
|
// how to escape the escape char '\', which is valid in a nick,
|
|
|
|
// whereas the wildcards tend not to be (as per RFC1459).
|
|
|
|
|
|
|
|
// The "*" wildcard is ".*" in regex, "?" is "."
|
|
|
|
// so we tokenize and join with the proper char back together,
|
|
|
|
// escaping any other regex modifier
|
|
|
|
const wildmany_split = a.split("*").map((sub) => {
|
|
|
|
const wildone_split = sub.split("?").map((p) => _.escapeRegExp(p));
|
|
|
|
return wildone_split.join(".");
|
|
|
|
});
|
|
|
|
const user_regex = wildmany_split.join(".*");
|
|
|
|
const re = new RegExp(`^${user_regex}$`, "i"); // case insensitive
|
|
|
|
return re.test(b);
|
|
|
|
}
|