diff --git a/.gitignore b/.gitignore
index 3364eab..6451580 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,7 +2,6 @@
lib/*.map
lib/api-functions.js
lib/auth.js
-lib/common.js
lib/config.js
lib/cwmp.js
lib/db.js
diff --git a/lib/common.coffee b/lib/common.coffee
deleted file mode 100644
index 9362a94..0000000
--- a/lib/common.coffee
+++ /dev/null
@@ -1,229 +0,0 @@
-###
-# Copyright 2013-2017 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 .
-###
-
-buffer = require 'buffer'
-
-UNDEFINED_TYPE = '[object Undefined]'
-NULL_TYPE = '[object Null]'
-BOOLEAN_TYPE = '[object Boolean]'
-NUMBER_TYPE = '[object Number]'
-STRING_TYPE = '[object String]'
-ARRAY_TYPE = '[object Array]'
-OBJECT_TYPE = '[object Object]'
-REGEXP_TYPE = '[object RegExp]'
-DATE_TYPE = '[object Date]'
-
-
-typeOf = (obj) ->
- Object.prototype.toString.call(obj)
-
-
-exports.generateDeviceId = (deviceIdStruct) ->
- # Percent escaping function. Escapes everything except alphanumerics and underscore
- esc = (str) ->
- str.replace(/[^A-Za-z0-9_]/g, (chr) ->
- buf = new buffer.Buffer(chr, 'utf8')
- rep = ''
- rep += "%#{b.toString(16).toUpperCase()}" for b in buf
- 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'])}"
-
-
-exports.getParamValueFromPath = (obj, path) ->
- pp = path.split('.')
- ref = obj
- try
- for p in pp
- ref = ref[p]
- return ref
- catch err
- return undefined
-
-
-pathOverlap = (a, b, start) ->
- if a.length == b.length
- res = 3
- else if a.length > b.length
- res = 2
- else
- res = 1
-
- for i in [(start ? 0)...Math.min(a.length, b.length)] by 1
- if a[i]?
- if not b[i]?
- res &= 2
- else if a[i] != b[i]
- return 0
- else if b[i]?
- res &= 1
-
- return 0 if not res
-
- return res
-
-
-parseAlias = (pattern, start, res) ->
- aliases = []
- i = start
- while i < pattern.length and pattern[i] != ']'
- alias = []
- i = j = parsePath(pattern, i, alias) + 1
-
- while pattern[j] not in [']', ',']
- if pattern[j] == '"' and i == j
- ++ j
- while pattern[j] != '"' or pattern[j - 1] == '\\'
- if ++ j >= pattern.length
- throw new Error('Invalid alias expression')
- if ++ j >= pattern.length
- throw new Error('Invalid alias expression')
- value = pattern[i...j].trim()
- i = j
- if value[0] == '"'
- try
- value = JSON.parse(value)
- catch err
- throw new Error('Invalid alias expression')
- alias.push(value)
- aliases.push(alias)
- ++ i if pattern[i] == ','
-
- # Need to sort to ensure identical expressions have idential string representation
- srt = (a, b) ->
- for j in [0...Math.min(a.length, b.length)] by 2
- for k in [0...Math.min(a[j].length, b[j].length)] by 1
- if Array.isArray(a[j][k])
- if Array.isArray(b[j][k])
- return srt(a[j][k], b[j][k])
- else if not b[j][k]?
- return -1
- else
- return 1
- else if not a[j][k]?
- if not b[j][k]?
- return 0
- else
- return 1
- else if not b[j][k]? or 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
-
-
-parsePath = (pattern, start, res) ->
- path = []
- path.wildcard = 0
- path.alias = 0
- i = start ? 0
-
- # Colon separator is needed for parseAlias
- if i < pattern.length and pattern[i] != ':'
- while true
- if pattern[i] == '['
- path.alias |= 1 << path.length
- i = parseAlias(pattern, i + 1, path) + 1
- else
- j = i
- while i < pattern.length and pattern[i] != ':' and pattern[i] != '.'
- ++ i
- n = pattern.slice(j, i)
- path.wildcard |= 1 << path.length if n == '*'
- path.push(n)
-
- if i >= pattern.length or pattern[i] == ':'
- break
- else if pattern[i] != '.'
- throw new Error('Invalid alias expression')
-
- ++ i
-
- Object.freeze(path)
-
- if not res?
- return path
-
- res.push(path)
- return i
-
-
-addPathMeta = (path) ->
- return path if path.alias? or path.wildcard?
-
- path.alias = 0
- path.wildcard = 0
-
- for p, i in path
- if typeOf(p) is ARRAY_TYPE
- path.alias |= 1 << i
- for j in [0...p.length] by 2
- addPathMeta(p[j])
- else if p == '*'
- path.wildcard |= 1 << i
-
- Object.freeze(path)
- return path
-
-
-# Source: http://stackoverflow.com/a/6969486
-escapeRegExp = (str) ->
- return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&")
-
-
-exports.UNDEFINED_TYPE = UNDEFINED_TYPE
-exports.NULL_TYPE = NULL_TYPE
-exports.NUMBER_TYPE = NUMBER_TYPE
-exports.STRING_TYPE = STRING_TYPE
-exports.ARRAY_TYPE = ARRAY_TYPE
-exports.OBJECT_TYPE = OBJECT_TYPE
-exports.REGEXP_TYPE = REGEXP_TYPE
-
-exports.typeOf = typeOf
-exports.pathOverlap = pathOverlap
-exports.parsePath = parsePath
-exports.addPathMeta = addPathMeta
-exports.escapeRegExp = escapeRegExp
diff --git a/lib/common.js b/lib/common.js
new file mode 100644
index 0000000..05f9e96
--- /dev/null
+++ b/lib/common.js
@@ -0,0 +1,217 @@
+/**
+ * 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 .
+ */
+"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;