218 lines
5.8 KiB
JavaScript
218 lines
5.8 KiB
JavaScript
/**
|
|
* Copyright 2013-2018 Zaid Abdulla
|
|
*
|
|
* This file is part of GenieACS.
|
|
*
|
|
* GenieACS is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as
|
|
* published by the Free Software Foundation, either version 3 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* GenieACS is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with GenieACS. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
"use strict";
|
|
|
|
const UNDEFINED_TYPE = "[object Undefined]";
|
|
const NULL_TYPE = "[object Null]";
|
|
const BOOLEAN_TYPE = "[object Boolean]";
|
|
const NUMBER_TYPE = "[object Number]";
|
|
const STRING_TYPE = "[object String]";
|
|
const ARRAY_TYPE = "[object Array]";
|
|
const OBJECT_TYPE = "[object Object]";
|
|
const REGEXP_TYPE = "[object RegExp]";
|
|
const DATE_TYPE = "[object Date]";
|
|
|
|
const typeOf = (obj) => Object.prototype.toString.call(obj);
|
|
|
|
function generateDeviceId(deviceIdStruct) {
|
|
// Escapes everything except alphanumerics and underscore
|
|
function esc(str) {
|
|
return str.replace(/[^A-Za-z0-9_]/g, (chr) => {
|
|
const buf = new Buffer(chr, "utf8");
|
|
let rep = "";
|
|
for (let b of buf)
|
|
rep += "%" + (b.toString(16).toUpperCase());
|
|
return rep;
|
|
});
|
|
}
|
|
|
|
// Guaranteeing globally unique id as defined in TR-069
|
|
if (deviceIdStruct["ProductClass"])
|
|
return (esc(deviceIdStruct["OUI"])) + "-" + (esc(deviceIdStruct["ProductClass"])) + "-" + (esc(deviceIdStruct["SerialNumber"]));
|
|
return (esc(deviceIdStruct["OUI"])) + "-" + (esc(deviceIdStruct["SerialNumber"]));
|
|
}
|
|
|
|
function parseAlias(pattern, start, res) {
|
|
const aliases = [];
|
|
let i = start;
|
|
while (i < pattern.length && pattern[i] !== "]") {
|
|
const alias = [];
|
|
let j = i = parsePath(pattern, i, alias) + 1;
|
|
while (pattern[j] !== "]" && pattern[j] !== ",") {
|
|
if (pattern[j] === "\"" && i === j) {
|
|
++j;
|
|
while (pattern[j] !== "\"" || pattern[j - 1] === "\\") {
|
|
if (++j >= pattern.length)
|
|
throw new Error("Invalid alias expression");
|
|
}
|
|
}
|
|
if (++j >= pattern.length)
|
|
throw new Error("Invalid alias expression");
|
|
}
|
|
|
|
let value = pattern.slice(i, j).trim();
|
|
i = j;
|
|
if (value[0] === "\"") {
|
|
try {
|
|
value = JSON.parse(value);
|
|
} catch (error) {
|
|
throw new Error("Invalid alias expression");
|
|
}
|
|
}
|
|
|
|
alias.push(value);
|
|
aliases.push(alias);
|
|
if (pattern[i] === ",")
|
|
++i;
|
|
}
|
|
|
|
// Sort to ensure identical expressions have idential string representation
|
|
function srt(a, b) {
|
|
const jMax = Math.min(a.length, b.length);
|
|
for (let j = 0; j < jMax; j += 2) {
|
|
const kMax = Math.min(a[j].length, b[j].length);
|
|
for (let k = 0; k < kMax; ++ k) {
|
|
if (Array.isArray(a[j][k])) {
|
|
if (Array.isArray(b[j][k]))
|
|
return srt(a[j][k], b[j][k]);
|
|
else if (b[j][k] == null)
|
|
return -1;
|
|
else
|
|
return 1;
|
|
} else if (a[j][k] == null) {
|
|
if (b[j][k] == null)
|
|
return 0;
|
|
else
|
|
return 1;
|
|
} else if ((b[j][k] == null) || Array.isArray(b[j][k])) {
|
|
return -1;
|
|
} else if (a[j][k] > b[j][k]) {
|
|
return 1;
|
|
} else if (a[j][k] < b[j][k]) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (a[j].length > b[j].length)
|
|
return -1;
|
|
else if (a[j].length < b[j].length)
|
|
return 1;
|
|
|
|
if (a[j + 1] > b[j + 1])
|
|
return -1;
|
|
else if (a[j + 1] < b[j + 1])
|
|
return 1;
|
|
}
|
|
|
|
if (a.length > b.length)
|
|
return -1;
|
|
else if (a.length < b.length)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
aliases.sort(srt);
|
|
res.push([].concat.apply([], aliases));
|
|
return i;
|
|
}
|
|
|
|
function parsePath(pattern, start, res) {
|
|
const path = [];
|
|
path.wildcard = 0;
|
|
path.alias = 0;
|
|
let i = start || 0;
|
|
|
|
// Colon separator is needed for parseAlias
|
|
if (i < pattern.length && pattern[i] !== ":") {
|
|
while (true) {
|
|
if (pattern[i] === "[") {
|
|
path.alias |= 1 << path.length;
|
|
i = parseAlias(pattern, i + 1, path) + 1;
|
|
} else {
|
|
let j = i;
|
|
while (i < pattern.length && pattern[i] !== ":" && pattern[i] !== ".")
|
|
++i;
|
|
|
|
const n = pattern.slice(j, i);
|
|
if (n === "*")
|
|
path.wildcard |= 1 << path.length;
|
|
path.push(n);
|
|
}
|
|
|
|
if (i >= pattern.length || pattern[i] === ":")
|
|
break;
|
|
else if (pattern[i] !== ".")
|
|
throw new Error("Invalid alias expression");
|
|
|
|
++i;
|
|
}
|
|
}
|
|
|
|
Object.freeze(path);
|
|
|
|
if (res == null)
|
|
return path;
|
|
|
|
res.push(path);
|
|
return i;
|
|
}
|
|
|
|
function addPathMeta(path) {
|
|
if (path.alias != null && path.wildcard != null)
|
|
return path;
|
|
|
|
path.alias = 0;
|
|
path.wildcard = 0;
|
|
|
|
for (let [i, p] of path.entries()) {
|
|
if (Array.isArray(p)) {
|
|
path.alias |= 1 << i;
|
|
for (let j = 0; j < p.length; j += 2)
|
|
addPathMeta(p[j]);
|
|
} else if (p === "*") {
|
|
path.wildcard |= 1 << i;
|
|
}
|
|
}
|
|
|
|
Object.freeze(path);
|
|
return path;
|
|
}
|
|
|
|
// Source: http://stackoverflow.com/a/6969486
|
|
function escapeRegExp(str) {
|
|
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
|
|
}
|
|
|
|
exports.UNDEFINED_TYPE = UNDEFINED_TYPE;
|
|
exports.NULL_TYPE = NULL_TYPE;
|
|
exports.BOOLEAN_TYPE = BOOLEAN_TYPE;
|
|
exports.NUMBER_TYPE = NUMBER_TYPE;
|
|
exports.STRING_TYPE = STRING_TYPE;
|
|
exports.ARRAY_TYPE = ARRAY_TYPE;
|
|
exports.OBJECT_TYPE = OBJECT_TYPE;
|
|
exports.DATE_TYPE = DATE_TYPE;
|
|
exports.REGEXP_TYPE = REGEXP_TYPE;
|
|
|
|
exports.typeOf = typeOf;
|
|
exports.generateDeviceId = generateDeviceId;
|
|
exports.parsePath = parsePath;
|
|
exports.addPathMeta = addPathMeta;
|
|
exports.escapeRegExp = escapeRegExp;
|