Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
843d183045 | ||
|
|
1cf455540a | ||
|
|
29d6f5b9d0 | ||
|
|
f639435a09 | ||
|
|
9db29bb55d | ||
|
|
48f861a6a5 | ||
|
|
1a5fd65f4e | ||
|
|
475e3fffbe | ||
|
|
3a178a5744 | ||
|
|
ceeb213899 | ||
|
|
38db068c29 |
No files matched your search
@@ -1,5 +1,16 @@
|
||||
# Change Log
|
||||
|
||||
## 1.1.1 (2017-03-23)
|
||||
|
||||
- Avoid crashing when connection request credentials are missing.
|
||||
- Show a warning instead of crashing when failing to parse parameter values
|
||||
according to the expected value type.
|
||||
- Add missing "Registered" event.
|
||||
- Fix bug where in certain cases many more instances than declared are created.
|
||||
- Fix parameter discovery bug when declared path timestamp is 1 or is not set.
|
||||
- Fix preset precondition failing when testing against datetime parameters and
|
||||
certain other parameters like \_deviceId.\_ProductClass.
|
||||
|
||||
## 1.1.0 (2017-03-10)
|
||||
|
||||
- Provisions enable implementing dynamic device configuration or complex device
|
||||
|
||||
@@ -78,10 +78,10 @@ httpConReq = (url, username, password, timeout, callback) ->
|
||||
if res.statusCode == 401 and res.headers['www-authenticate']?
|
||||
authHeader = auth.parseAuthHeader(res.headers['www-authenticate'])
|
||||
if authHeader.method is 'Basic'
|
||||
options.headers = {'Authorization' : auth.basic(username, password)}
|
||||
options.headers = {'Authorization' : auth.basic(username or '', password or '')}
|
||||
else if authHeader.method is 'Digest'
|
||||
options.headers = {
|
||||
'Authorization' : auth.digest(username, password, options.path, 'GET', null, authHeader)
|
||||
'Authorization' : auth.digest(username or '', password or '', options.path, 'GET', null, authHeader)
|
||||
}
|
||||
|
||||
request = http.get(options, (res) ->
|
||||
|
||||
@@ -63,19 +63,6 @@ exports.getParamValueFromPath = (obj, path) ->
|
||||
return undefined
|
||||
|
||||
|
||||
exports.matchType = (src, dst) ->
|
||||
switch typeOf(src)
|
||||
when STRING_TYPE
|
||||
String(dst)
|
||||
when NUMBER_TYPE
|
||||
Number(dst)
|
||||
when BOOLEAN_TYPE
|
||||
v = String(dst).trim().toLowerCase()
|
||||
v == 'true' or v == 'on' or v == 'yes' or v == '1'
|
||||
else
|
||||
dst
|
||||
|
||||
|
||||
pathOverlap = (a, b, start) ->
|
||||
if a.length == b.length
|
||||
res = 3
|
||||
|
||||
+7
-1
@@ -603,9 +603,10 @@ listener = (httpRequest, httpResponse) ->
|
||||
|
||||
stream.on('end', () ->
|
||||
rpc = null
|
||||
parseWarnings = []
|
||||
getSession(httpRequest, f = (err, sessionContext) ->
|
||||
return throwError(err, httpResponse) if err
|
||||
rpc ?= soap.request(httpRequest, sessionContext?.cwmpVersion)
|
||||
rpc ?= soap.request(httpRequest, sessionContext?.cwmpVersion, parseWarnings)
|
||||
if not sessionContext?
|
||||
if rpc.cpeRequest?.name isnt 'Inform'
|
||||
httpResponse.writeHead(400)
|
||||
@@ -645,6 +646,11 @@ listener = (httpRequest, httpResponse) ->
|
||||
sessionContext.httpRequest = httpRequest
|
||||
sessionContext.httpResponse = httpResponse
|
||||
|
||||
for w in parseWarnings
|
||||
w.sessionContext = sessionContext
|
||||
w.rpc = rpc;
|
||||
logger.accessWarn(w)
|
||||
|
||||
if config.get('DEBUG', sessionContext.deviceId)
|
||||
dump = "# REQUEST #{new Date(Date.now())}\n" + JSON.stringify(httpRequest.headers) + "\n#{httpRequest.getBody()}\n\n"
|
||||
require('fs').appendFile("./debug/#{sessionContext.deviceId}.dump", dump, (err) ->
|
||||
|
||||
+2
-2
@@ -264,7 +264,7 @@ fetchDevice = (id, timestamp, patterns, callback) ->
|
||||
else
|
||||
for k, v of projTree
|
||||
p = path.concat(k)
|
||||
res.push([p, +(obj['_timestamp'] ? 1)])
|
||||
res.push([p, obj['_timestamp']]) if obj['_timestamp']
|
||||
loaded.push([p, ((1 << path.length) - 1) ^ ((1 << MAX_DEPTH) - 1)])
|
||||
|
||||
for k, v of device
|
||||
@@ -292,7 +292,7 @@ fetchDevice = (id, timestamp, patterns, callback) ->
|
||||
{object: [timestamp, 1], writable: [timestamp, 0]}])
|
||||
# Use current timestamp for registered event attribute timestamps
|
||||
res.push([['Events', 'Registered'], timestamp,
|
||||
{object: [timestamp, 0], writable: [timestamp, 0], value: [timestamp, [v, 'xsd:dateTime']]}])
|
||||
{object: [timestamp, 0], writable: [timestamp, 0], value: [timestamp, [+v, 'xsd:dateTime']]}])
|
||||
delete device[k]
|
||||
when '_id'
|
||||
res.push([['DeviceID'], timestamp,
|
||||
|
||||
+30
-1
@@ -244,10 +244,28 @@ test = (params, query) ->
|
||||
return true
|
||||
|
||||
|
||||
matchType = (src, dst) ->
|
||||
if typeof src is 'string'
|
||||
return String(dst)
|
||||
else if typeof src is 'number'
|
||||
if +dst == parseFloat(dst)
|
||||
return +dst
|
||||
else if not isNaN(Date.parse(dst))
|
||||
return Date.parse(dst)
|
||||
else if typeof src is 'boolean'
|
||||
v = String(dst).trim()
|
||||
if v is 'true' or v is 'TRUE' or v is 'True' or v is '1'
|
||||
return true
|
||||
else if v is 'false' or v is 'FALSE' or v is 'False' or v is '0'
|
||||
return false
|
||||
|
||||
return dst
|
||||
|
||||
|
||||
testFilter = (obj, filter) ->
|
||||
for k, v of filter
|
||||
[param, op] = k.split(/([^a-zA-Z0-9\-\_\.].*)/, 2)
|
||||
val = common.matchType(obj[param], v)
|
||||
val = matchType(obj[param], v)
|
||||
switch op
|
||||
when '=', undefined
|
||||
return false if obj[param] != val
|
||||
@@ -289,6 +307,17 @@ convertMongoQueryToFilters = (query, filters) ->
|
||||
v = v.replace(/[^a-zA-Z0-9\-]+/g, '_')
|
||||
filters["Tags.#{v}"] = true
|
||||
else
|
||||
switch k
|
||||
when '_id' then k = 'DeviceID.ID'
|
||||
when '_deviceId._Manufacturer' then k = 'DeviceID.Manufacturer'
|
||||
when '_deviceId._OUI' then k = 'DeviceID.OUI'
|
||||
when '_deviceId._ProductClass' then k = 'DeviceID.ProductClass'
|
||||
when '_deviceId._SerialNumber' then k = 'DeviceID.SerialNumber'
|
||||
when '_lastInform' then k = 'Events.Inform'
|
||||
when '_lastBootstrap' then k = 'Events.0_BOOTSTRAP'
|
||||
when '_lastBoot' then k = 'Events.1_BOOT'
|
||||
when '_registered' then k = 'Events.Registered'
|
||||
|
||||
if common.typeOf(v) is common.OBJECT_TYPE
|
||||
for kk, vv of v
|
||||
switch kk
|
||||
|
||||
@@ -149,6 +149,10 @@ inform = (sessionContext, rpcReq, callback) ->
|
||||
loadParameters(sessionContext, (err) ->
|
||||
return callback(err) if err
|
||||
|
||||
if sessionContext.new
|
||||
params.push([['Events', 'Registered'], timestamp,
|
||||
{object: [timestamp, 0], writable: [timestamp, 0], value: [timestamp, [timestamp, 'xsd:dateTime']]}])
|
||||
|
||||
sessionContext.deviceData.timestamps.revision = 1
|
||||
sessionContext.deviceData.attributes.revision = 1
|
||||
|
||||
@@ -454,6 +458,7 @@ runDeclarations = (sessionContext, declarations) ->
|
||||
|
||||
for declaration, i in declarations
|
||||
path = common.addPathMeta(declaration[0])
|
||||
unpacked = null
|
||||
|
||||
if (path.alias | path.wildcard) & 1 or path[0] == 'VirtualParameters'
|
||||
sessionContext.deviceData.paths.add(['VirtualParameters'])
|
||||
|
||||
+54
-12
@@ -85,6 +85,8 @@ if (XML_IGNORE_NAMESPACE) {
|
||||
};
|
||||
}
|
||||
|
||||
let warnings;
|
||||
|
||||
|
||||
function parseBool(v) {
|
||||
v = "" + v;
|
||||
@@ -93,7 +95,7 @@ function parseBool(v) {
|
||||
else if (v === "false" || v === "FALSE" || v === "False" || v === "0")
|
||||
return false;
|
||||
else
|
||||
throw new Error("Invalid boolean value");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -103,9 +105,21 @@ function event(xml) {
|
||||
|
||||
|
||||
function parameterInfoList(xml) {
|
||||
return xml.find("ParameterInfoStruct").map(
|
||||
e => [e.get("Name").text(), parseBool(e.get("Writable").text())]
|
||||
);
|
||||
return xml.find("ParameterInfoStruct").map(function(e) {
|
||||
let param = e.get("Name").text();
|
||||
let value = e.get("Writable").text();
|
||||
let parsed = parseBool(value);
|
||||
|
||||
if (parsed === null) {
|
||||
warnings.push({
|
||||
message: "Invalid writable attribute",
|
||||
parameter: param
|
||||
});
|
||||
parsed = false;
|
||||
}
|
||||
|
||||
return [param, parsed];
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -113,15 +127,41 @@ function parameterValueList(xml) {
|
||||
return xml.find("ParameterValueStruct").map(function(e) {
|
||||
const valueElement = e.get("Value");
|
||||
const valueType = valueElement.attr("type").value();
|
||||
let param = e.get("Name").text();
|
||||
let value = valueElement.text();
|
||||
if (valueType === "xsd:boolean")
|
||||
value = parseBool(value);
|
||||
else if (valueType === "xsd:int" || valueType === "xsd:unsignedInt")
|
||||
value = parseInt(value);
|
||||
else if (valueType === "xsd:dateTime")
|
||||
value = Date.parse(value);
|
||||
let parsed = value;
|
||||
if (valueType === "xsd:boolean") {
|
||||
parsed = parseBool(value);
|
||||
if (parsed === null) {
|
||||
warnings.push({
|
||||
message: "Invalid value attribute",
|
||||
parameter: param
|
||||
});
|
||||
parsed = value;
|
||||
}
|
||||
}
|
||||
else if (valueType === "xsd:int" || valueType === "xsd:unsignedInt") {
|
||||
parsed = parseInt(value);
|
||||
if (isNaN(parsed)) {
|
||||
warnings.push({
|
||||
message: "Invalid value attribute",
|
||||
parameter: param
|
||||
});
|
||||
parsed = value;
|
||||
}
|
||||
}
|
||||
else if (valueType === "xsd:dateTime") {
|
||||
parsed = Date.parse(value);
|
||||
if (isNaN(parsed)) {
|
||||
warnings.push({
|
||||
message: "Invalid value attribute",
|
||||
parameter: param
|
||||
});
|
||||
parsed = value;
|
||||
}
|
||||
}
|
||||
|
||||
return [e.get("Name").text(), value, valueType];
|
||||
return [param, parsed, valueType];
|
||||
});
|
||||
}
|
||||
|
||||
@@ -367,7 +407,9 @@ function fault(xml, cwmpVersion) {
|
||||
}
|
||||
|
||||
|
||||
function request(httpRequest, cwmpVersion) {
|
||||
function request(httpRequest, cwmpVersion, warn) {
|
||||
warnings = warn;
|
||||
|
||||
const rpc = {
|
||||
cwmpVersion: cwmpVersion
|
||||
};
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "genieacs",
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.1",
|
||||
"description": "A TR-069 Auto Configuration Server (ACS)",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
Reference in new issue
Block a user