Implement SOAP processor using our new XML parser
This commit is contained in:
1 parent
bec74a4995
commit
f4c9321d4d
4 files changed
+1002
-253
No files matched your search
@@ -84,6 +84,7 @@ const options = {
|
|||||||
XML_FORMAT: { type: "bool", default: false },
|
XML_FORMAT: { type: "bool", default: false },
|
||||||
XML_NO_DECL: { type: "bool", default: false },
|
XML_NO_DECL: { type: "bool", default: false },
|
||||||
XML_NO_EMPTY: { type: "bool", default: false },
|
XML_NO_EMPTY: { type: "bool", default: false },
|
||||||
|
XML_LIBXMLJS: { type: "bool", default: false },
|
||||||
|
|
||||||
// Should probably never be changed
|
// Should probably never be changed
|
||||||
DEVICE_ONLINE_THRESHOLD: { type: "int", default: 4000 }
|
DEVICE_ONLINE_THRESHOLD: { type: "int", default: 4000 }
|
||||||
|
|||||||
+27
-5
@@ -24,7 +24,8 @@ import { Socket } from "net";
|
|||||||
import * as auth from "./auth";
|
import * as auth from "./auth";
|
||||||
import * as config from "./config";
|
import * as config from "./config";
|
||||||
import * as common from "./common";
|
import * as common from "./common";
|
||||||
import * as soap from "./soap";
|
import * as soapCustom from "./soap";
|
||||||
|
import * as soapLibxmljs from "./soap-libxmljs";
|
||||||
import * as session from "./session";
|
import * as session from "./session";
|
||||||
import { evaluateAsync, evaluate, extractParams } from "./common/expression";
|
import { evaluateAsync, evaluate, extractParams } from "./common/expression";
|
||||||
import * as device from "./device";
|
import * as device from "./device";
|
||||||
@@ -50,7 +51,13 @@ import { IncomingMessage, ServerResponse } from "http";
|
|||||||
import { Readable } from "stream";
|
import { Readable } from "stream";
|
||||||
import { promisify } from "util";
|
import { promisify } from "util";
|
||||||
import { TLSSocket } from "tls";
|
import { TLSSocket } from "tls";
|
||||||
|
import { dependencies as DEPENDENCIES } from "../package.json";
|
||||||
|
import { parseXmlDeclaration } from "./xml-parser";
|
||||||
|
|
||||||
|
const soap =
|
||||||
|
DEPENDENCIES["libxmljs"] && config.get("XML_LIBXMLJS")
|
||||||
|
? soapLibxmljs
|
||||||
|
: soapCustom;
|
||||||
const gzipPromisified = promisify(zlib.gzip);
|
const gzipPromisified = promisify(zlib.gzip);
|
||||||
const deflatePromisified = promisify(zlib.deflate);
|
const deflatePromisified = promisify(zlib.deflate);
|
||||||
|
|
||||||
@@ -70,7 +77,7 @@ const stats = {
|
|||||||
|
|
||||||
async function authenticate(
|
async function authenticate(
|
||||||
sessionContext: SessionContext,
|
sessionContext: SessionContext,
|
||||||
body: string | Buffer
|
body: string
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
const authExpression: Expression = localCache.getConfigExpression(
|
const authExpression: Expression = localCache.getConfigExpression(
|
||||||
sessionContext.cacheSnapshot,
|
sessionContext.cacheSnapshot,
|
||||||
@@ -1220,11 +1227,26 @@ async function listenerAsync(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let charset;
|
||||||
|
if (httpRequest.headers["content-type"]) {
|
||||||
|
const m = httpRequest.headers["content-type"].match(
|
||||||
|
/charset=['"]?([^'"\s]+)/i
|
||||||
|
);
|
||||||
|
if (m) charset = m[1].toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!charset) {
|
||||||
|
const e = parseXmlDeclaration(body).find(s => s.localName === "encoding");
|
||||||
|
charset = e ? e.value : "utf8";
|
||||||
|
}
|
||||||
|
|
||||||
|
const bodyStr = body.toString(charset);
|
||||||
|
|
||||||
const parseWarnings = [];
|
const parseWarnings = [];
|
||||||
let rpc;
|
let rpc;
|
||||||
try {
|
try {
|
||||||
rpc = soap.request(
|
rpc = soap.request(
|
||||||
body,
|
bodyStr,
|
||||||
sessionContext ? sessionContext.cwmpVersion : null,
|
sessionContext ? sessionContext.cwmpVersion : null,
|
||||||
parseWarnings
|
parseWarnings
|
||||||
);
|
);
|
||||||
@@ -1246,7 +1268,7 @@ async function listenerAsync(
|
|||||||
// Authenticate in case of new connection or unauthenticated session
|
// Authenticate in case of new connection or unauthenticated session
|
||||||
const authenticated =
|
const authenticated =
|
||||||
sessionContext.state === 0 || newConnection
|
sessionContext.state === 0 || newConnection
|
||||||
? await authenticate(sessionContext, body)
|
? await authenticate(sessionContext, bodyStr)
|
||||||
: true;
|
: true;
|
||||||
|
|
||||||
if (!authenticated) return responseUnauthorized(sessionContext);
|
if (!authenticated) return responseUnauthorized(sessionContext);
|
||||||
@@ -1328,7 +1350,7 @@ async function listenerAsync(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const authenticated = await authenticate(_sessionContext, body);
|
const authenticated = await authenticate(_sessionContext, bodyStr);
|
||||||
if (!authenticated) return responseUnauthorized(_sessionContext);
|
if (!authenticated) return responseUnauthorized(_sessionContext);
|
||||||
|
|
||||||
parsedRpc(_sessionContext, rpc, parseWarnings);
|
parsedRpc(_sessionContext, rpc, parseWarnings);
|
||||||
|
|||||||
@@ -0,0 +1,747 @@
|
|||||||
|
/**
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { parseXml, Document, Element, ParserOptions } from "libxmljs";
|
||||||
|
import * as config from "./config";
|
||||||
|
import { version as VERSION } from "../package.json";
|
||||||
|
import {
|
||||||
|
CpeGetResponse,
|
||||||
|
CpeSetResponse,
|
||||||
|
InformRequest,
|
||||||
|
AcsResponse,
|
||||||
|
CpeRequest,
|
||||||
|
FaultStruct,
|
||||||
|
SpvFault,
|
||||||
|
CpeFault,
|
||||||
|
SoapMessage,
|
||||||
|
TransferCompleteRequest
|
||||||
|
} from "./types";
|
||||||
|
|
||||||
|
const SERVER_NAME = `GenieACS/${VERSION}`;
|
||||||
|
|
||||||
|
const NAMESPACES = {
|
||||||
|
"1.0": {
|
||||||
|
"soap-enc": "http://schemas.xmlsoap.org/soap/encoding/",
|
||||||
|
"soap-env": "http://schemas.xmlsoap.org/soap/envelope/",
|
||||||
|
xsd: "http://www.w3.org/2001/XMLSchema",
|
||||||
|
xsi: "http://www.w3.org/2001/XMLSchema-instance",
|
||||||
|
cwmp: "urn:dslforum-org:cwmp-1-0"
|
||||||
|
},
|
||||||
|
"1.1": {
|
||||||
|
"soap-enc": "http://schemas.xmlsoap.org/soap/encoding/",
|
||||||
|
"soap-env": "http://schemas.xmlsoap.org/soap/envelope/",
|
||||||
|
xsd: "http://www.w3.org/2001/XMLSchema",
|
||||||
|
xsi: "http://www.w3.org/2001/XMLSchema-instance",
|
||||||
|
cwmp: "urn:dslforum-org:cwmp-1-1"
|
||||||
|
},
|
||||||
|
"1.2": {
|
||||||
|
"soap-enc": "http://schemas.xmlsoap.org/soap/encoding/",
|
||||||
|
"soap-env": "http://schemas.xmlsoap.org/soap/envelope/",
|
||||||
|
xsd: "http://www.w3.org/2001/XMLSchema",
|
||||||
|
xsi: "http://www.w3.org/2001/XMLSchema-instance",
|
||||||
|
cwmp: "urn:dslforum-org:cwmp-1-2"
|
||||||
|
},
|
||||||
|
"1.3": {
|
||||||
|
"soap-enc": "http://schemas.xmlsoap.org/soap/encoding/",
|
||||||
|
"soap-env": "http://schemas.xmlsoap.org/soap/envelope/",
|
||||||
|
xsd: "http://www.w3.org/2001/XMLSchema",
|
||||||
|
xsi: "http://www.w3.org/2001/XMLSchema-instance",
|
||||||
|
cwmp: "urn:dslforum-org:cwmp-1-2"
|
||||||
|
},
|
||||||
|
"1.4": {
|
||||||
|
"soap-enc": "http://schemas.xmlsoap.org/soap/encoding/",
|
||||||
|
"soap-env": "http://schemas.xmlsoap.org/soap/envelope/",
|
||||||
|
xsd: "http://www.w3.org/2001/XMLSchema",
|
||||||
|
xsi: "http://www.w3.org/2001/XMLSchema-instance",
|
||||||
|
cwmp: "urn:dslforum-org:cwmp-1-3"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const LIBXMLJS_PARSE_OPTIONS: ParserOptions = {
|
||||||
|
nocdata: true,
|
||||||
|
recover: !!config.get("XML_RECOVER"),
|
||||||
|
/* eslint-disable-next-line @typescript-eslint/camelcase */
|
||||||
|
ignore_enc: !!config.get("XML_IGNORE_ENC")
|
||||||
|
};
|
||||||
|
|
||||||
|
interface WriterOptions {
|
||||||
|
declaration?: boolean;
|
||||||
|
format?: boolean;
|
||||||
|
selfCloseEmpty?: boolean;
|
||||||
|
whitespace?: boolean;
|
||||||
|
type?: "XML" | "HTML" | "XHTML";
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "libxmljs" {
|
||||||
|
interface Document {
|
||||||
|
toString(formatting: boolean | WriterOptions): string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const LIBXMLJS_SAVE_OPTIONS: WriterOptions = {
|
||||||
|
format: !!config.get("XML_FORMAT"),
|
||||||
|
declaration: !config.get("XML_NO_DECL"),
|
||||||
|
selfCloseEmpty: !config.get("XML_NO_EMPTY")
|
||||||
|
};
|
||||||
|
|
||||||
|
let warnings;
|
||||||
|
|
||||||
|
function parseBool(v): boolean {
|
||||||
|
v = "" + v;
|
||||||
|
if (v === "true" || v === "TRUE" || v === "True" || v === "1") return true;
|
||||||
|
else if (v === "false" || v === "FALSE" || v === "False" || v === "0")
|
||||||
|
return false;
|
||||||
|
else return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function event(xml): string[] {
|
||||||
|
return xml
|
||||||
|
.childNodes()
|
||||||
|
.filter(n => n.name() === "EventStruct")
|
||||||
|
.map(c =>
|
||||||
|
c
|
||||||
|
.childNodes()
|
||||||
|
.find(n => n.name() === "EventCode")
|
||||||
|
.text()
|
||||||
|
.trim()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parameterInfoList(xml): [string, boolean][] {
|
||||||
|
return xml
|
||||||
|
.childNodes()
|
||||||
|
.filter(e => e.name() === "ParameterInfoStruct")
|
||||||
|
.map(e => {
|
||||||
|
let param, value;
|
||||||
|
for (const c of e.childNodes()) {
|
||||||
|
switch (c.name()) {
|
||||||
|
case "Name":
|
||||||
|
param = c.text();
|
||||||
|
break;
|
||||||
|
case "Writable":
|
||||||
|
value = c.text();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let parsed = parseBool(value);
|
||||||
|
|
||||||
|
if (parsed === null) {
|
||||||
|
warnings.push({
|
||||||
|
message: "Invalid writable attribute",
|
||||||
|
parameter: param
|
||||||
|
});
|
||||||
|
parsed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [param, parsed];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function parameterValueList(
|
||||||
|
xml
|
||||||
|
): [string, string | number | boolean, string][] {
|
||||||
|
return xml
|
||||||
|
.childNodes()
|
||||||
|
.filter(e => e.name() === "ParameterValueStruct")
|
||||||
|
.map(e => {
|
||||||
|
let valueElement, param;
|
||||||
|
for (const c of e.childNodes()) {
|
||||||
|
switch (c.name()) {
|
||||||
|
case "Name":
|
||||||
|
param = c.text();
|
||||||
|
break;
|
||||||
|
case "Value":
|
||||||
|
valueElement = c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const valueType = valueElement.attr("type").value();
|
||||||
|
const value = valueElement.text();
|
||||||
|
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 [param, parsed, valueType];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function GetParameterNames(xml, methodRequest): void {
|
||||||
|
const el = xml.node("cwmp:GetParameterNames");
|
||||||
|
el.node("ParameterPath").text(methodRequest.parameterPath);
|
||||||
|
el.node("NextLevel").text(+methodRequest.nextLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
function GetParameterNamesResponse(xml): CpeGetResponse {
|
||||||
|
return {
|
||||||
|
name: "GetParameterNamesResponse",
|
||||||
|
parameterList: parameterInfoList(
|
||||||
|
xml.childNodes().find(n => n.name() === "ParameterList")
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function GetParameterValues(xml, methodRequest): void {
|
||||||
|
const el = xml.node("cwmp:GetParameterValues").node("ParameterNames");
|
||||||
|
el.attr({
|
||||||
|
"soap-enc:arrayType": `xsd:string[${methodRequest.parameterNames.length}]`
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const p of methodRequest.parameterNames) el.node("string").text(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
function GetParameterValuesResponse(xml): CpeGetResponse {
|
||||||
|
return {
|
||||||
|
name: "GetParameterValuesResponse",
|
||||||
|
parameterList: parameterValueList(
|
||||||
|
xml.childNodes().find(n => n.name() === "ParameterList")
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function SetParameterValues(xml, methodRequest): void {
|
||||||
|
const el = xml.node("cwmp:SetParameterValues");
|
||||||
|
const paramList = el.node("ParameterList");
|
||||||
|
paramList.attr({
|
||||||
|
"soap-enc:arrayType": `cwmp:ParameterValueStruct[${
|
||||||
|
methodRequest.parameterList.length
|
||||||
|
}]`
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const p of methodRequest.parameterList) {
|
||||||
|
const pvs = paramList.node("ParameterValueStruct");
|
||||||
|
pvs.node("Name").text(p[0]);
|
||||||
|
let val = p[1];
|
||||||
|
if (p[2] === "xsd:dateTime" && typeof val === "number") {
|
||||||
|
val = new Date(val).toISOString();
|
||||||
|
if (methodRequest.DATETIME_MILLISECONDS === false)
|
||||||
|
val = val.replace(".000", "");
|
||||||
|
}
|
||||||
|
if (p[2] === "xsd:boolean" && typeof val === "boolean")
|
||||||
|
if (methodRequest.BOOLEAN_LITERAL === false) val = +val;
|
||||||
|
|
||||||
|
pvs
|
||||||
|
.node("Value")
|
||||||
|
.attr({ "xsi:type": p[2] })
|
||||||
|
.text(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
el.node("ParameterKey").text(methodRequest.parameterKey || "");
|
||||||
|
}
|
||||||
|
|
||||||
|
function SetParameterValuesResponse(xml): CpeSetResponse {
|
||||||
|
return {
|
||||||
|
name: "SetParameterValuesResponse",
|
||||||
|
status: parseInt(
|
||||||
|
xml
|
||||||
|
.childNodes()
|
||||||
|
.find(n => n.name() === "Status")
|
||||||
|
.text()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function AddObject(xml, methodRequest): void {
|
||||||
|
const el = xml.node("cwmp:AddObject");
|
||||||
|
el.node("ObjectName").text(methodRequest.objectName);
|
||||||
|
el.node("ParameterKey").text(methodRequest.parameterKey || "");
|
||||||
|
}
|
||||||
|
|
||||||
|
function AddObjectResponse(xml): CpeSetResponse {
|
||||||
|
let instanceNumber, status;
|
||||||
|
for (const c of xml.childNodes()) {
|
||||||
|
switch (c.name()) {
|
||||||
|
case "InstanceNumber":
|
||||||
|
instanceNumber = parseInt(c.text());
|
||||||
|
break;
|
||||||
|
case "Status":
|
||||||
|
status = parseInt(c.text());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: "AddObjectResponse",
|
||||||
|
instanceNumber: instanceNumber,
|
||||||
|
status: status
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function DeleteObject(xml, methodRequest): void {
|
||||||
|
const el = xml.node("cwmp:DeleteObject");
|
||||||
|
el.node("ObjectName").text(methodRequest.objectName);
|
||||||
|
el.node("ParameterKey").text(methodRequest.parameterKey || "");
|
||||||
|
}
|
||||||
|
|
||||||
|
function DeleteObjectResponse(xml): CpeSetResponse {
|
||||||
|
return {
|
||||||
|
name: "DeleteObjectResponse",
|
||||||
|
status: parseInt(
|
||||||
|
xml
|
||||||
|
.childNodes()
|
||||||
|
.find(n => n.name() === "Status")
|
||||||
|
.text()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function Reboot(xml, methodRequest): void {
|
||||||
|
const el = xml.node("cwmp:Reboot");
|
||||||
|
el.node("CommandKey").text(methodRequest.commandKey || "");
|
||||||
|
}
|
||||||
|
|
||||||
|
function RebootResponse(): CpeSetResponse {
|
||||||
|
return {
|
||||||
|
name: "RebootResponse"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function FactoryReset(xml): void {
|
||||||
|
xml.node("cwmp:FactoryReset");
|
||||||
|
}
|
||||||
|
|
||||||
|
function FactoryResetResponse(): CpeSetResponse {
|
||||||
|
return {
|
||||||
|
name: "FactoryResetResponse"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function Download(xml, methodRequest): void {
|
||||||
|
const el = xml.node("cwmp:Download");
|
||||||
|
el.node("CommandKey").text(methodRequest.commandKey || "");
|
||||||
|
el.node("FileType").text(methodRequest.fileType);
|
||||||
|
el.node("URL").text(methodRequest.url);
|
||||||
|
el.node("Username").text(methodRequest.username || "");
|
||||||
|
el.node("Password").text(methodRequest.password || "");
|
||||||
|
el.node("FileSize").text(methodRequest.fileSize || "0");
|
||||||
|
el.node("TargetFileName").text(methodRequest.targetFileName || "");
|
||||||
|
el.node("DelaySeconds").text(methodRequest.delaySeconds || "0");
|
||||||
|
el.node("SuccessURL").text(methodRequest.successUrl || "");
|
||||||
|
el.node("FailureURL").text(methodRequest.failureUrl || "");
|
||||||
|
}
|
||||||
|
|
||||||
|
function DownloadResponse(xml): CpeSetResponse {
|
||||||
|
let status, startTime, completeTime;
|
||||||
|
for (const c of xml.childNodes()) {
|
||||||
|
switch (c.name()) {
|
||||||
|
case "Status":
|
||||||
|
status = parseInt(c.text());
|
||||||
|
break;
|
||||||
|
case "StartTime":
|
||||||
|
startTime = Date.parse(c.text());
|
||||||
|
break;
|
||||||
|
case "CompleteTime":
|
||||||
|
completeTime = Date.parse(c.text());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: "DownloadResponse",
|
||||||
|
status: status,
|
||||||
|
startTime: startTime,
|
||||||
|
completeTime: completeTime
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function Inform(xml): InformRequest {
|
||||||
|
let retryCount, evnt, parameterList;
|
||||||
|
const deviceId = {
|
||||||
|
Manufacturer: null,
|
||||||
|
OUI: null,
|
||||||
|
ProductClass: null,
|
||||||
|
SerialNumber: null
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const c of xml.childNodes()) {
|
||||||
|
switch (c.name()) {
|
||||||
|
case "ParameterList":
|
||||||
|
parameterList = parameterValueList(c);
|
||||||
|
break;
|
||||||
|
case "DeviceId":
|
||||||
|
for (const cc of c.childNodes()) {
|
||||||
|
const n = cc.name();
|
||||||
|
if (n in deviceId) deviceId[n] = cc.text();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "Event":
|
||||||
|
evnt = event(c);
|
||||||
|
break;
|
||||||
|
case "RetryCount":
|
||||||
|
retryCount = parseInt(c.text());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: "Inform",
|
||||||
|
parameterList: parameterList,
|
||||||
|
deviceId: deviceId,
|
||||||
|
event: evnt,
|
||||||
|
retryCount: retryCount
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function InformResponse(xml): void {
|
||||||
|
xml
|
||||||
|
.node("cwmp:InformResponse")
|
||||||
|
.node("MaxEnvelopes")
|
||||||
|
.text(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function GetRPCMethods(): AcsResponse {
|
||||||
|
return { name: "GetRPCMethods" };
|
||||||
|
}
|
||||||
|
|
||||||
|
function GetRPCMethodsResponse(xml, methodResponse): void {
|
||||||
|
const el = xml.node("cwmp:GetRPCMethodsResponse").node("MethodList");
|
||||||
|
el.attr({
|
||||||
|
"soap-enc:arrayType": `xsd:string[${methodResponse.methodList.length}]`
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const m of methodResponse.methodList) el.node("string").text(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
function TransferComplete(xml): TransferCompleteRequest {
|
||||||
|
let commandKey, _faultStruct, startTime, completeTime;
|
||||||
|
for (const c of xml.childNodes()) {
|
||||||
|
switch (c.name()) {
|
||||||
|
case "CommandKey":
|
||||||
|
commandKey = c.text();
|
||||||
|
break;
|
||||||
|
case "FaultStruct":
|
||||||
|
_faultStruct = faultStruct(c);
|
||||||
|
break;
|
||||||
|
case "StartTime":
|
||||||
|
startTime = Date.parse(c.text());
|
||||||
|
break;
|
||||||
|
case "CompleteTime":
|
||||||
|
completeTime = Date.parse(c.text());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: "TransferComplete",
|
||||||
|
commandKey: commandKey,
|
||||||
|
faultStruct: _faultStruct,
|
||||||
|
startTime: startTime,
|
||||||
|
completeTime: completeTime
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function TransferCompleteResponse(xml): void {
|
||||||
|
xml.node("cwmp:TransferCompleteResponse").text("");
|
||||||
|
}
|
||||||
|
|
||||||
|
function RequestDownload(xml): CpeRequest {
|
||||||
|
return {
|
||||||
|
name: "RequestDownload",
|
||||||
|
fileType: xml
|
||||||
|
.childNodes()
|
||||||
|
.find(n => n.name() === "FileType")
|
||||||
|
.text()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function RequestDownloadResponse(xml): void {
|
||||||
|
xml.node("cwmp:RequestDownloadResponse").text("");
|
||||||
|
}
|
||||||
|
|
||||||
|
function faultStruct(xml): FaultStruct {
|
||||||
|
let faultCode, faultString, setParameterValuesFault: SpvFault[], pn, fc, fs;
|
||||||
|
for (const c of xml.childNodes()) {
|
||||||
|
switch (c.name()) {
|
||||||
|
case "FaultCode":
|
||||||
|
faultCode = c.text();
|
||||||
|
break;
|
||||||
|
case "FaultString":
|
||||||
|
faultString = c.text();
|
||||||
|
break;
|
||||||
|
case "SetParameterValuesFault":
|
||||||
|
setParameterValuesFault = setParameterValuesFault || [];
|
||||||
|
for (const cc of c.childNodes()) {
|
||||||
|
switch (cc.name()) {
|
||||||
|
case "ParameterName":
|
||||||
|
pn = cc.text();
|
||||||
|
break;
|
||||||
|
case "FaultCode":
|
||||||
|
fc = cc.text();
|
||||||
|
break;
|
||||||
|
case "FaultString":
|
||||||
|
fs = cc.text();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setParameterValuesFault.push({
|
||||||
|
parameterName: pn,
|
||||||
|
faultCode: fc,
|
||||||
|
faultString: fs
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { faultCode, faultString, setParameterValuesFault };
|
||||||
|
}
|
||||||
|
|
||||||
|
function fault(xml): CpeFault {
|
||||||
|
let faultCode, faultString, detail;
|
||||||
|
for (const c of xml.childNodes()) {
|
||||||
|
switch (c.name()) {
|
||||||
|
case "faultcode":
|
||||||
|
faultCode = c.text();
|
||||||
|
break;
|
||||||
|
case "faultstring":
|
||||||
|
faultString = c.text();
|
||||||
|
break;
|
||||||
|
case "detail":
|
||||||
|
detail = faultStruct(c.childNodes().find(n => n.name() === "Fault"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { faultCode, faultString, detail };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function request(data, cwmpVersion, warn): SoapMessage {
|
||||||
|
warnings = warn;
|
||||||
|
|
||||||
|
const rpc = {
|
||||||
|
id: null,
|
||||||
|
cwmpVersion: cwmpVersion,
|
||||||
|
sessionTimeout: null,
|
||||||
|
cpeRequest: null,
|
||||||
|
cpeFault: null,
|
||||||
|
cpeResponse: null
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!data.length) return rpc;
|
||||||
|
|
||||||
|
const xml = parseXml(data, LIBXMLJS_PARSE_OPTIONS);
|
||||||
|
|
||||||
|
let headerElement, bodyElement;
|
||||||
|
|
||||||
|
for (const c of xml.childNodes()) {
|
||||||
|
switch (c.name()) {
|
||||||
|
case "Header":
|
||||||
|
headerElement = c;
|
||||||
|
break;
|
||||||
|
case "Body":
|
||||||
|
bodyElement = c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (headerElement) {
|
||||||
|
for (const c of headerElement.childNodes()) {
|
||||||
|
switch (c.name()) {
|
||||||
|
case "ID":
|
||||||
|
rpc.id = c.text();
|
||||||
|
break;
|
||||||
|
case "sessionTimeout":
|
||||||
|
rpc.sessionTimeout = parseInt(c.text());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const methodElement of bodyElement.childNodes()) {
|
||||||
|
const methodName = methodElement.name();
|
||||||
|
if (methodName === "text") continue;
|
||||||
|
|
||||||
|
if (!rpc.cwmpVersion && methodName !== "Fault") {
|
||||||
|
switch (methodElement.namespace().href()) {
|
||||||
|
case "urn:dslforum-org:cwmp-1-0":
|
||||||
|
rpc.cwmpVersion = "1.0";
|
||||||
|
break;
|
||||||
|
case "urn:dslforum-org:cwmp-1-1":
|
||||||
|
rpc.cwmpVersion = "1.1";
|
||||||
|
break;
|
||||||
|
case "urn:dslforum-org:cwmp-1-2":
|
||||||
|
if (rpc.sessionTimeout) rpc.cwmpVersion = "1.3";
|
||||||
|
else rpc.cwmpVersion = "1.2";
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "urn:dslforum-org:cwmp-1-3":
|
||||||
|
rpc.cwmpVersion = "1.4";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error("Unrecognized CWMP version");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (methodName) {
|
||||||
|
case "Inform":
|
||||||
|
rpc.cpeRequest = Inform(methodElement);
|
||||||
|
break;
|
||||||
|
case "GetRPCMethods":
|
||||||
|
rpc.cpeRequest = GetRPCMethods();
|
||||||
|
break;
|
||||||
|
case "TransferComplete":
|
||||||
|
rpc.cpeRequest = TransferComplete(methodElement);
|
||||||
|
break;
|
||||||
|
case "RequestDownload":
|
||||||
|
rpc.cpeRequest = RequestDownload(methodElement);
|
||||||
|
break;
|
||||||
|
case "GetParameterNamesResponse":
|
||||||
|
rpc.cpeResponse = GetParameterNamesResponse(methodElement);
|
||||||
|
break;
|
||||||
|
case "GetParameterValuesResponse":
|
||||||
|
rpc.cpeResponse = GetParameterValuesResponse(methodElement);
|
||||||
|
break;
|
||||||
|
case "SetParameterValuesResponse":
|
||||||
|
rpc.cpeResponse = SetParameterValuesResponse(methodElement);
|
||||||
|
break;
|
||||||
|
case "AddObjectResponse":
|
||||||
|
rpc.cpeResponse = AddObjectResponse(methodElement);
|
||||||
|
break;
|
||||||
|
case "DeleteObjectResponse":
|
||||||
|
rpc.cpeResponse = DeleteObjectResponse(methodElement);
|
||||||
|
break;
|
||||||
|
case "RebootResponse":
|
||||||
|
rpc.cpeResponse = RebootResponse();
|
||||||
|
break;
|
||||||
|
case "FactoryResetResponse":
|
||||||
|
rpc.cpeResponse = FactoryResetResponse();
|
||||||
|
break;
|
||||||
|
case "DownloadResponse":
|
||||||
|
rpc.cpeResponse = DownloadResponse(methodElement);
|
||||||
|
break;
|
||||||
|
case "Fault":
|
||||||
|
rpc.cpeFault = fault(methodElement);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error(`8000 Method not supported ${methodName}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rpc;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createSoapEnv(cwmpVersion: string): Element {
|
||||||
|
const xml = new Document();
|
||||||
|
const env = xml.node("soap-env:Envelope");
|
||||||
|
|
||||||
|
for (const prefix of Object.keys(NAMESPACES[cwmpVersion]))
|
||||||
|
env.defineNamespace(prefix, NAMESPACES[cwmpVersion][prefix]);
|
||||||
|
|
||||||
|
return env;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function response(rpc): { code: number; headers: {}; data: string } {
|
||||||
|
const headers = {
|
||||||
|
Server: SERVER_NAME,
|
||||||
|
SOAPServer: SERVER_NAME
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!rpc) return { code: 204, headers: headers, data: "" };
|
||||||
|
|
||||||
|
const env = createSoapEnv(rpc.cwmpVersion);
|
||||||
|
const header = env.node("soap-env:Header");
|
||||||
|
header
|
||||||
|
.node("cwmp:ID")
|
||||||
|
.attr({
|
||||||
|
"soap-env:mustUnderstand": "1"
|
||||||
|
})
|
||||||
|
.text(rpc.id);
|
||||||
|
|
||||||
|
const body = env.node("soap-env:Body");
|
||||||
|
|
||||||
|
if (rpc.acsResponse) {
|
||||||
|
switch (rpc.acsResponse.name) {
|
||||||
|
case "InformResponse":
|
||||||
|
InformResponse(body);
|
||||||
|
break;
|
||||||
|
case "GetRPCMethodsResponse":
|
||||||
|
GetRPCMethodsResponse(body, rpc.acsResponse);
|
||||||
|
break;
|
||||||
|
case "TransferCompleteResponse":
|
||||||
|
TransferCompleteResponse(body);
|
||||||
|
break;
|
||||||
|
case "RequestDownloadResponse":
|
||||||
|
RequestDownloadResponse(body);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error(`Unknown method response type ${rpc.acsResponse.name}`);
|
||||||
|
}
|
||||||
|
} else if (rpc.acsRequest) {
|
||||||
|
switch (rpc.acsRequest.name) {
|
||||||
|
case "GetParameterNames":
|
||||||
|
GetParameterNames(body, rpc.acsRequest);
|
||||||
|
break;
|
||||||
|
case "GetParameterValues":
|
||||||
|
GetParameterValues(body, rpc.acsRequest);
|
||||||
|
break;
|
||||||
|
case "SetParameterValues":
|
||||||
|
SetParameterValues(body, rpc.acsRequest);
|
||||||
|
break;
|
||||||
|
case "AddObject":
|
||||||
|
AddObject(body, rpc.acsRequest);
|
||||||
|
break;
|
||||||
|
case "DeleteObject":
|
||||||
|
DeleteObject(body, rpc.acsRequest);
|
||||||
|
break;
|
||||||
|
case "Reboot":
|
||||||
|
Reboot(body, rpc.acsRequest);
|
||||||
|
break;
|
||||||
|
case "FactoryReset":
|
||||||
|
FactoryReset(body);
|
||||||
|
break;
|
||||||
|
case "Download":
|
||||||
|
Download(body, rpc.acsRequest);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error(`Unknown method request ${rpc.acsRequest.name}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
headers["Content-Type"] = 'text/xml; charset="utf-8"';
|
||||||
|
return {
|
||||||
|
code: 200,
|
||||||
|
headers: headers,
|
||||||
|
data: env.doc().toString(LIBXMLJS_SAVE_OPTIONS)
|
||||||
|
};
|
||||||
|
}
|
||||||
+227
-248
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright 2013-2018 Zaid Abdulla
|
* Copyright 2013-2019 Zaid Abdulla
|
||||||
*
|
*
|
||||||
* This file is part of GenieACS.
|
* This file is part of GenieACS.
|
||||||
*
|
*
|
||||||
@@ -17,8 +17,14 @@
|
|||||||
* along with GenieACS. If not, see <http://www.gnu.org/licenses/>.
|
* along with GenieACS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { parseXml, Document, Element, ParserOptions } from "libxmljs";
|
import {
|
||||||
import * as config from "./config";
|
parseXml,
|
||||||
|
Element,
|
||||||
|
parseAttrs,
|
||||||
|
encodeEntities,
|
||||||
|
decodeEntities
|
||||||
|
} from "./xml-parser";
|
||||||
|
import memoize from "./common/memoize";
|
||||||
import { version as VERSION } from "../package.json";
|
import { version as VERSION } from "../package.json";
|
||||||
import {
|
import {
|
||||||
CpeGetResponse,
|
CpeGetResponse,
|
||||||
@@ -73,35 +79,10 @@ const NAMESPACES = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const LIBXMLJS_PARSE_OPTIONS: ParserOptions = {
|
|
||||||
nocdata: true,
|
|
||||||
recover: !!config.get("XML_RECOVER"),
|
|
||||||
/* eslint-disable-next-line @typescript-eslint/camelcase */
|
|
||||||
ignore_enc: !!config.get("XML_IGNORE_ENC")
|
|
||||||
};
|
|
||||||
|
|
||||||
interface WriterOptions {
|
|
||||||
declaration?: boolean;
|
|
||||||
format?: boolean;
|
|
||||||
selfCloseEmpty?: boolean;
|
|
||||||
whitespace?: boolean;
|
|
||||||
type?: "XML" | "HTML" | "XHTML";
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module "libxmljs" {
|
|
||||||
interface Document {
|
|
||||||
toString(formatting: boolean | WriterOptions): string;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const LIBXMLJS_SAVE_OPTIONS: WriterOptions = {
|
|
||||||
format: !!config.get("XML_FORMAT"),
|
|
||||||
declaration: !config.get("XML_NO_DECL"),
|
|
||||||
selfCloseEmpty: !config.get("XML_NO_EMPTY")
|
|
||||||
};
|
|
||||||
|
|
||||||
let warnings;
|
let warnings;
|
||||||
|
|
||||||
|
const memoizedParseAttrs = memoize(parseAttrs);
|
||||||
|
|
||||||
function parseBool(v): boolean {
|
function parseBool(v): boolean {
|
||||||
v = "" + v;
|
v = "" + v;
|
||||||
if (v === "true" || v === "TRUE" || v === "True" || v === "1") return true;
|
if (v === "true" || v === "TRUE" || v === "True" || v === "1") return true;
|
||||||
@@ -110,37 +91,29 @@ function parseBool(v): boolean {
|
|||||||
else return null;
|
else return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function event(xml): string[] {
|
function event(xml: Element): string[] {
|
||||||
return xml
|
return xml.children
|
||||||
.childNodes()
|
.filter(n => n.localName === "EventStruct")
|
||||||
.filter(n => n.name() === "EventStruct")
|
.map(c => c.children.find(n => n.localName === "EventCode").text.trim());
|
||||||
.map(c =>
|
|
||||||
c
|
|
||||||
.childNodes()
|
|
||||||
.find(n => n.name() === "EventCode")
|
|
||||||
.text()
|
|
||||||
.trim()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function parameterInfoList(xml): [string, boolean][] {
|
function parameterInfoList(xml: Element): [string, boolean][] {
|
||||||
return xml
|
return xml.children
|
||||||
.childNodes()
|
.filter(e => e.localName === "ParameterInfoStruct")
|
||||||
.filter(e => e.name() === "ParameterInfoStruct")
|
.map<[string, boolean]>(e => {
|
||||||
.map(e => {
|
let param: string, value: string;
|
||||||
let param, value;
|
for (const c of e.children) {
|
||||||
for (const c of e.childNodes()) {
|
switch (c.localName) {
|
||||||
switch (c.name()) {
|
|
||||||
case "Name":
|
case "Name":
|
||||||
param = c.text();
|
param = c.text;
|
||||||
break;
|
break;
|
||||||
case "Writable":
|
case "Writable":
|
||||||
value = c.text();
|
value = c.text;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let parsed = parseBool(value);
|
let parsed: boolean = parseBool(value);
|
||||||
|
|
||||||
if (parsed === null) {
|
if (parsed === null) {
|
||||||
warnings.push({
|
warnings.push({
|
||||||
@@ -154,18 +127,23 @@ function parameterInfoList(xml): [string, boolean][] {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getValueType = memoize(str =>
|
||||||
|
parseAttrs(str)
|
||||||
|
.find(s => s.localName === "type")
|
||||||
|
.value.trim()
|
||||||
|
);
|
||||||
|
|
||||||
function parameterValueList(
|
function parameterValueList(
|
||||||
xml
|
xml: Element
|
||||||
): [string, string | number | boolean, string][] {
|
): [string, string | number | boolean, string][] {
|
||||||
return xml
|
return xml.children
|
||||||
.childNodes()
|
.filter(e => e.localName === "ParameterValueStruct")
|
||||||
.filter(e => e.name() === "ParameterValueStruct")
|
.map<[string, string | number | boolean, string]>(e => {
|
||||||
.map(e => {
|
let valueElement: Element, param: string;
|
||||||
let valueElement, param;
|
for (const c of e.children) {
|
||||||
for (const c of e.childNodes()) {
|
switch (c.localName) {
|
||||||
switch (c.name()) {
|
|
||||||
case "Name":
|
case "Name":
|
||||||
param = c.text();
|
param = c.text;
|
||||||
break;
|
break;
|
||||||
case "Value":
|
case "Value":
|
||||||
valueElement = c;
|
valueElement = c;
|
||||||
@@ -173,9 +151,10 @@ function parameterValueList(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const valueType = valueElement.attr("type").value();
|
const valueType = getValueType(valueElement.attrs);
|
||||||
const value = valueElement.text();
|
|
||||||
let parsed = value;
|
const value = decodeEntities(valueElement.text);
|
||||||
|
let parsed: string | number | boolean = value;
|
||||||
if (valueType === "xsd:boolean") {
|
if (valueType === "xsd:boolean") {
|
||||||
parsed = parseBool(value);
|
parsed = parseBool(value);
|
||||||
if (parsed === null) {
|
if (parsed === null) {
|
||||||
@@ -209,51 +188,40 @@ function parameterValueList(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function GetParameterNames(xml, methodRequest): void {
|
function GetParameterNames(methodRequest): string {
|
||||||
const el = xml.node("cwmp:GetParameterNames");
|
return `<cwmp:GetParameterNames><ParameterPath>${
|
||||||
el.node("ParameterPath").text(methodRequest.parameterPath);
|
methodRequest.parameterPath
|
||||||
el.node("NextLevel").text(+methodRequest.nextLevel);
|
}</ParameterPath><NextLevel>${+methodRequest.nextLevel}</NextLevel></cwmp:GetParameterNames>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function GetParameterNamesResponse(xml): CpeGetResponse {
|
function GetParameterNamesResponse(xml): CpeGetResponse {
|
||||||
return {
|
return {
|
||||||
name: "GetParameterNamesResponse",
|
name: "GetParameterNamesResponse",
|
||||||
parameterList: parameterInfoList(
|
parameterList: parameterInfoList(
|
||||||
xml.childNodes().find(n => n.name() === "ParameterList")
|
xml.children.find(n => n.localName === "ParameterList")
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function GetParameterValues(xml, methodRequest): void {
|
function GetParameterValues(methodRequest): string {
|
||||||
const el = xml.node("cwmp:GetParameterValues").node("ParameterNames");
|
return `<cwmp:GetParameterValues><ParameterNames soap-enc:arrayType="xsd:string[${
|
||||||
el.attr({
|
methodRequest.parameterNames.length
|
||||||
"soap-enc:arrayType": `xsd:string[${methodRequest.parameterNames.length}]`
|
}]">${methodRequest.parameterNames
|
||||||
});
|
.map(p => `<string>${p}</string>`)
|
||||||
|
.join("")}</ParameterNames></cwmp:GetParameterValues>`;
|
||||||
for (const p of methodRequest.parameterNames) el.node("string").text(p);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function GetParameterValuesResponse(xml): CpeGetResponse {
|
function GetParameterValuesResponse(xml: Element): CpeGetResponse {
|
||||||
return {
|
return {
|
||||||
name: "GetParameterValuesResponse",
|
name: "GetParameterValuesResponse",
|
||||||
parameterList: parameterValueList(
|
parameterList: parameterValueList(
|
||||||
xml.childNodes().find(n => n.name() === "ParameterList")
|
xml.children.find(n => n.localName === "ParameterList")
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function SetParameterValues(xml, methodRequest): void {
|
function SetParameterValues(methodRequest): string {
|
||||||
const el = xml.node("cwmp:SetParameterValues");
|
const params = methodRequest.parameterList.map(p => {
|
||||||
const paramList = el.node("ParameterList");
|
|
||||||
paramList.attr({
|
|
||||||
"soap-enc:arrayType": `cwmp:ParameterValueStruct[${
|
|
||||||
methodRequest.parameterList.length
|
|
||||||
}]`
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const p of methodRequest.parameterList) {
|
|
||||||
const pvs = paramList.node("ParameterValueStruct");
|
|
||||||
pvs.node("Name").text(p[0]);
|
|
||||||
let val = p[1];
|
let val = p[1];
|
||||||
if (p[2] === "xsd:dateTime" && typeof val === "number") {
|
if (p[2] === "xsd:dateTime" && typeof val === "number") {
|
||||||
val = new Date(val).toISOString();
|
val = new Date(val).toISOString();
|
||||||
@@ -262,43 +230,42 @@ function SetParameterValues(xml, methodRequest): void {
|
|||||||
}
|
}
|
||||||
if (p[2] === "xsd:boolean" && typeof val === "boolean")
|
if (p[2] === "xsd:boolean" && typeof val === "boolean")
|
||||||
if (methodRequest.BOOLEAN_LITERAL === false) val = +val;
|
if (methodRequest.BOOLEAN_LITERAL === false) val = +val;
|
||||||
|
`<ParameterValueStruct><Name>${p[0]}</Name><Value xsi:type="${
|
||||||
|
p[2]
|
||||||
|
}">${encodeEntities("" + val)}</Value></ParameterValueStruct>`;
|
||||||
|
});
|
||||||
|
|
||||||
pvs
|
return `<cwmp:SetParameterValues><ParameterList soap-enc:arrayType="cwmp:ParameterValueStruct[${
|
||||||
.node("Value")
|
methodRequest.parameterList.length
|
||||||
.attr({ "xsi:type": p[2] })
|
}]">${params.join(
|
||||||
.text(val);
|
""
|
||||||
}
|
)}</ParameterList><ParameterKey>${methodRequest.parameterKey ||
|
||||||
|
""}</ParameterKey></cwmp:SetParameterValues>`;
|
||||||
el.node("ParameterKey").text(methodRequest.parameterKey || "");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function SetParameterValuesResponse(xml): CpeSetResponse {
|
function SetParameterValuesResponse(xml: Element): CpeSetResponse {
|
||||||
return {
|
return {
|
||||||
name: "SetParameterValuesResponse",
|
name: "SetParameterValuesResponse",
|
||||||
status: parseInt(
|
status: parseInt(xml.children.find(n => n.localName === "Status").text)
|
||||||
xml
|
|
||||||
.childNodes()
|
|
||||||
.find(n => n.name() === "Status")
|
|
||||||
.text()
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function AddObject(xml, methodRequest): void {
|
function AddObject(methodRequest): string {
|
||||||
const el = xml.node("cwmp:AddObject");
|
return `<cwmp:AddObject><ObjectName>${
|
||||||
el.node("ObjectName").text(methodRequest.objectName);
|
methodRequest.objectName
|
||||||
el.node("ParameterKey").text(methodRequest.parameterKey || "");
|
}</ObjectName><ParameterKey>${methodRequest.parameterKey ||
|
||||||
|
""}</ParameterKey></cwmp:AddObject>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function AddObjectResponse(xml): CpeSetResponse {
|
function AddObjectResponse(xml: Element): CpeSetResponse {
|
||||||
let instanceNumber, status;
|
let instanceNumber, status;
|
||||||
for (const c of xml.childNodes()) {
|
for (const c of xml.children) {
|
||||||
switch (c.name()) {
|
switch (c.localName) {
|
||||||
case "InstanceNumber":
|
case "InstanceNumber":
|
||||||
instanceNumber = parseInt(c.text());
|
instanceNumber = parseInt(c.text);
|
||||||
break;
|
break;
|
||||||
case "Status":
|
case "Status":
|
||||||
status = parseInt(c.text());
|
status = parseInt(c.text);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -310,27 +277,23 @@ function AddObjectResponse(xml): CpeSetResponse {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function DeleteObject(xml, methodRequest): void {
|
function DeleteObject(methodRequest): string {
|
||||||
const el = xml.node("cwmp:DeleteObject");
|
return `<cwmp:DeleteObject><ObjectName>${
|
||||||
el.node("ObjectName").text(methodRequest.objectName);
|
methodRequest.objectName
|
||||||
el.node("ParameterKey").text(methodRequest.parameterKey || "");
|
}</ObjectName><ParameterKey>${methodRequest.parameterKey ||
|
||||||
|
""}</ParameterKey></cwmp:DeleteObject>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function DeleteObjectResponse(xml): CpeSetResponse {
|
function DeleteObjectResponse(xml: Element): CpeSetResponse {
|
||||||
return {
|
return {
|
||||||
name: "DeleteObjectResponse",
|
name: "DeleteObjectResponse",
|
||||||
status: parseInt(
|
status: parseInt(xml.children.find(n => n.localName === "Status").text)
|
||||||
xml
|
|
||||||
.childNodes()
|
|
||||||
.find(n => n.name() === "Status")
|
|
||||||
.text()
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function Reboot(xml, methodRequest): void {
|
function Reboot(methodRequest): string {
|
||||||
const el = xml.node("cwmp:Reboot");
|
return `<cwmp:Reboot><CommandKey>${methodRequest.commandKey ||
|
||||||
el.node("CommandKey").text(methodRequest.commandKey || "");
|
""}</CommandKey></cwmp:Reboot>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function RebootResponse(): CpeSetResponse {
|
function RebootResponse(): CpeSetResponse {
|
||||||
@@ -339,8 +302,8 @@ function RebootResponse(): CpeSetResponse {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function FactoryReset(xml): void {
|
function FactoryReset(): string {
|
||||||
xml.node("cwmp:FactoryReset");
|
return "<cwmp:FactoryReset></cwmp:FactoryReset>";
|
||||||
}
|
}
|
||||||
|
|
||||||
function FactoryResetResponse(): CpeSetResponse {
|
function FactoryResetResponse(): CpeSetResponse {
|
||||||
@@ -349,32 +312,37 @@ function FactoryResetResponse(): CpeSetResponse {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function Download(xml, methodRequest): void {
|
function Download(methodRequest): string {
|
||||||
const el = xml.node("cwmp:Download");
|
return `<cwmp:Download><CommandKey>${methodRequest.commandKey ||
|
||||||
el.node("CommandKey").text(methodRequest.commandKey || "");
|
""}</CommandKey><FileType>${methodRequest.fileType}</FileType><URL>${
|
||||||
el.node("FileType").text(methodRequest.fileType);
|
methodRequest.url
|
||||||
el.node("URL").text(methodRequest.url);
|
}</URL><Username>${encodeEntities(
|
||||||
el.node("Username").text(methodRequest.username || "");
|
methodRequest.username || ""
|
||||||
el.node("Password").text(methodRequest.password || "");
|
)}</Username><Password>${encodeEntities(
|
||||||
el.node("FileSize").text(methodRequest.fileSize || "0");
|
methodRequest.password || ""
|
||||||
el.node("TargetFileName").text(methodRequest.targetFileName || "");
|
)}</Password><FileSize>${methodRequest.fileSize ||
|
||||||
el.node("DelaySeconds").text(methodRequest.delaySeconds || "0");
|
"0"}</FileSize><TargetFileName>${encodeEntities(
|
||||||
el.node("SuccessURL").text(methodRequest.successUrl || "");
|
methodRequest.targetFileName || ""
|
||||||
el.node("FailureURL").text(methodRequest.failureUrl || "");
|
)}</TargetFileName><DelaySeconds>${methodRequest.delaySeconds ||
|
||||||
|
"0"}</DelaySeconds><SuccessURL>${encodeEntities(
|
||||||
|
methodRequest.successUrl || ""
|
||||||
|
)}</SuccessURL><FailureURL>${encodeEntities(
|
||||||
|
methodRequest.failureUrl || ""
|
||||||
|
)}</FailureURL></cwmp:Download>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function DownloadResponse(xml): CpeSetResponse {
|
function DownloadResponse(xml: Element): CpeSetResponse {
|
||||||
let status, startTime, completeTime;
|
let status, startTime, completeTime;
|
||||||
for (const c of xml.childNodes()) {
|
for (const c of xml.children) {
|
||||||
switch (c.name()) {
|
switch (c.localName) {
|
||||||
case "Status":
|
case "Status":
|
||||||
status = parseInt(c.text());
|
status = parseInt(c.text);
|
||||||
break;
|
break;
|
||||||
case "StartTime":
|
case "StartTime":
|
||||||
startTime = Date.parse(c.text());
|
startTime = Date.parse(c.text);
|
||||||
break;
|
break;
|
||||||
case "CompleteTime":
|
case "CompleteTime":
|
||||||
completeTime = Date.parse(c.text());
|
completeTime = Date.parse(c.text);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -387,7 +355,7 @@ function DownloadResponse(xml): CpeSetResponse {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function Inform(xml): InformRequest {
|
function Inform(xml: Element): InformRequest {
|
||||||
let retryCount, evnt, parameterList;
|
let retryCount, evnt, parameterList;
|
||||||
const deviceId = {
|
const deviceId = {
|
||||||
Manufacturer: null,
|
Manufacturer: null,
|
||||||
@@ -396,22 +364,22 @@ function Inform(xml): InformRequest {
|
|||||||
SerialNumber: null
|
SerialNumber: null
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const c of xml.childNodes()) {
|
for (const c of xml.children) {
|
||||||
switch (c.name()) {
|
switch (c.localName) {
|
||||||
case "ParameterList":
|
case "ParameterList":
|
||||||
parameterList = parameterValueList(c);
|
parameterList = parameterValueList(c);
|
||||||
break;
|
break;
|
||||||
case "DeviceId":
|
case "DeviceId":
|
||||||
for (const cc of c.childNodes()) {
|
for (const cc of c.children) {
|
||||||
const n = cc.name();
|
const n = cc.localName;
|
||||||
if (n in deviceId) deviceId[n] = cc.text();
|
if (n in deviceId) deviceId[n] = decodeEntities(cc.text);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "Event":
|
case "Event":
|
||||||
evnt = event(c);
|
evnt = event(c);
|
||||||
break;
|
break;
|
||||||
case "RetryCount":
|
case "RetryCount":
|
||||||
retryCount = parseInt(c.text());
|
retryCount = parseInt(c.text);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -425,41 +393,37 @@ function Inform(xml): InformRequest {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function InformResponse(xml): void {
|
function InformResponse(): string {
|
||||||
xml
|
return "<cwmp:InformResponse><MaxEnvelopes>1</MaxEnvelopes></cwmp:InformResponse>";
|
||||||
.node("cwmp:InformResponse")
|
|
||||||
.node("MaxEnvelopes")
|
|
||||||
.text(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function GetRPCMethods(): AcsResponse {
|
function GetRPCMethods(): AcsResponse {
|
||||||
return { name: "GetRPCMethods" };
|
return { name: "GetRPCMethods" };
|
||||||
}
|
}
|
||||||
|
|
||||||
function GetRPCMethodsResponse(xml, methodResponse): void {
|
function GetRPCMethodsResponse(methodResponse): string {
|
||||||
const el = xml.node("cwmp:GetRPCMethodsResponse").node("MethodList");
|
return `<cwmp:GetRPCMethodsResponse><MethodList soap-enc:arrayType="xsd:string[${
|
||||||
el.attr({
|
methodResponse.methodList.length
|
||||||
"soap-enc:arrayType": `xsd:string[${methodResponse.methodList.length}]`
|
}]">${methodResponse.methodList
|
||||||
});
|
.map(m => `<string>${m}</string>`)
|
||||||
|
.join("")}</MethodList></cwmp:GetRPCMethodsResponse>`;
|
||||||
for (const m of methodResponse.methodList) el.node("string").text(m);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function TransferComplete(xml): TransferCompleteRequest {
|
function TransferComplete(xml: Element): TransferCompleteRequest {
|
||||||
let commandKey, _faultStruct, startTime, completeTime;
|
let commandKey, _faultStruct, startTime, completeTime;
|
||||||
for (const c of xml.childNodes()) {
|
for (const c of xml.children) {
|
||||||
switch (c.name()) {
|
switch (c.localName) {
|
||||||
case "CommandKey":
|
case "CommandKey":
|
||||||
commandKey = c.text();
|
commandKey = c.text;
|
||||||
break;
|
break;
|
||||||
case "FaultStruct":
|
case "FaultStruct":
|
||||||
_faultStruct = faultStruct(c);
|
_faultStruct = faultStruct(c);
|
||||||
break;
|
break;
|
||||||
case "StartTime":
|
case "StartTime":
|
||||||
startTime = Date.parse(c.text());
|
startTime = Date.parse(c.text);
|
||||||
break;
|
break;
|
||||||
case "CompleteTime":
|
case "CompleteTime":
|
||||||
completeTime = Date.parse(c.text());
|
completeTime = Date.parse(c.text);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -473,46 +437,43 @@ function TransferComplete(xml): TransferCompleteRequest {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function TransferCompleteResponse(xml): void {
|
function TransferCompleteResponse(): string {
|
||||||
xml.node("cwmp:TransferCompleteResponse").text("");
|
return "<cwmp:TransferCompleteResponse></cwmp:TransferCompleteResponse>";
|
||||||
}
|
}
|
||||||
|
|
||||||
function RequestDownload(xml): CpeRequest {
|
function RequestDownload(xml: Element): CpeRequest {
|
||||||
return {
|
return {
|
||||||
name: "RequestDownload",
|
name: "RequestDownload",
|
||||||
fileType: xml
|
fileType: xml.children.find(n => n.localName === "FileType").text
|
||||||
.childNodes()
|
|
||||||
.find(n => n.name() === "FileType")
|
|
||||||
.text()
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function RequestDownloadResponse(xml): void {
|
function RequestDownloadResponse(): string {
|
||||||
xml.node("cwmp:RequestDownloadResponse").text("");
|
return "<cwmp:RequestDownloadResponse></cwmp:RequestDownloadResponse>";
|
||||||
}
|
}
|
||||||
|
|
||||||
function faultStruct(xml): FaultStruct {
|
function faultStruct(xml: Element): FaultStruct {
|
||||||
let faultCode, faultString, setParameterValuesFault: SpvFault[], pn, fc, fs;
|
let faultCode, faultString, setParameterValuesFault: SpvFault[], pn, fc, fs;
|
||||||
for (const c of xml.childNodes()) {
|
for (const c of xml.children) {
|
||||||
switch (c.name()) {
|
switch (c.localName) {
|
||||||
case "FaultCode":
|
case "FaultCode":
|
||||||
faultCode = c.text();
|
faultCode = c.text;
|
||||||
break;
|
break;
|
||||||
case "FaultString":
|
case "FaultString":
|
||||||
faultString = c.text();
|
faultString = decodeEntities(c.text);
|
||||||
break;
|
break;
|
||||||
case "SetParameterValuesFault":
|
case "SetParameterValuesFault":
|
||||||
setParameterValuesFault = setParameterValuesFault || [];
|
setParameterValuesFault = setParameterValuesFault || [];
|
||||||
for (const cc of c.childNodes()) {
|
for (const cc of c.children) {
|
||||||
switch (cc.name()) {
|
switch (cc.localName) {
|
||||||
case "ParameterName":
|
case "ParameterName":
|
||||||
pn = cc.text();
|
pn = cc.text;
|
||||||
break;
|
break;
|
||||||
case "FaultCode":
|
case "FaultCode":
|
||||||
fc = cc.text();
|
fc = cc.text;
|
||||||
break;
|
break;
|
||||||
case "FaultString":
|
case "FaultString":
|
||||||
fs = cc.text();
|
fs = decodeEntities(cc.text);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -527,18 +488,18 @@ function faultStruct(xml): FaultStruct {
|
|||||||
return { faultCode, faultString, setParameterValuesFault };
|
return { faultCode, faultString, setParameterValuesFault };
|
||||||
}
|
}
|
||||||
|
|
||||||
function fault(xml): CpeFault {
|
function fault(xml: Element): CpeFault {
|
||||||
let faultCode, faultString, detail;
|
let faultCode, faultString, detail;
|
||||||
for (const c of xml.childNodes()) {
|
for (const c of xml.children) {
|
||||||
switch (c.name()) {
|
switch (c.localName) {
|
||||||
case "faultcode":
|
case "faultcode":
|
||||||
faultCode = c.text();
|
faultCode = c.text;
|
||||||
break;
|
break;
|
||||||
case "faultstring":
|
case "faultstring":
|
||||||
faultString = c.text();
|
faultString = decodeEntities(c.text);
|
||||||
break;
|
break;
|
||||||
case "detail":
|
case "detail":
|
||||||
detail = faultStruct(c.childNodes().find(n => n.name() === "Fault"));
|
detail = faultStruct(c.children.find(n => n.localName === "Fault"));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -546,7 +507,7 @@ function fault(xml): CpeFault {
|
|||||||
return { faultCode, faultString, detail };
|
return { faultCode, faultString, detail };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function request(data, cwmpVersion, warn): SoapMessage {
|
export function request(body: string, cwmpVersion, warn): SoapMessage {
|
||||||
warnings = warn;
|
warnings = warn;
|
||||||
|
|
||||||
const rpc = {
|
const rpc = {
|
||||||
@@ -558,14 +519,18 @@ export function request(data, cwmpVersion, warn): SoapMessage {
|
|||||||
cpeResponse: null
|
cpeResponse: null
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!data.length) return rpc;
|
if (!body.length) return rpc;
|
||||||
|
|
||||||
const xml = parseXml(data, LIBXMLJS_PARSE_OPTIONS);
|
const xml = parseXml(body);
|
||||||
|
|
||||||
let headerElement, bodyElement;
|
if (!xml.children.length) return rpc;
|
||||||
|
|
||||||
for (const c of xml.childNodes()) {
|
const envelope = xml.children[0];
|
||||||
switch (c.name()) {
|
|
||||||
|
let headerElement: Element, bodyElement: Element;
|
||||||
|
|
||||||
|
for (const c of envelope.children) {
|
||||||
|
switch (c.localName) {
|
||||||
case "Header":
|
case "Header":
|
||||||
headerElement = c;
|
headerElement = c;
|
||||||
break;
|
break;
|
||||||
@@ -576,24 +541,37 @@ export function request(data, cwmpVersion, warn): SoapMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (headerElement) {
|
if (headerElement) {
|
||||||
for (const c of headerElement.childNodes()) {
|
for (const c of headerElement.children) {
|
||||||
switch (c.name()) {
|
switch (c.localName) {
|
||||||
case "ID":
|
case "ID":
|
||||||
rpc.id = c.text();
|
rpc.id = decodeEntities(c.text);
|
||||||
break;
|
break;
|
||||||
case "sessionTimeout":
|
case "sessionTimeout":
|
||||||
rpc.sessionTimeout = parseInt(c.text());
|
rpc.sessionTimeout = parseInt(c.text);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const methodElement of bodyElement.childNodes()) {
|
const methodElement = bodyElement.children[0];
|
||||||
const methodName = methodElement.name();
|
|
||||||
if (methodName === "text") continue;
|
|
||||||
|
|
||||||
if (!rpc.cwmpVersion && methodName !== "Fault") {
|
if (!rpc.cwmpVersion && methodElement.localName !== "Fault") {
|
||||||
switch (methodElement.namespace().href()) {
|
let namespace, namespaceHref;
|
||||||
|
for (const e of [methodElement, bodyElement, envelope]) {
|
||||||
|
namespace = namespace || e.namespace;
|
||||||
|
if (e.attrs) {
|
||||||
|
const attrs = memoizedParseAttrs(e.attrs);
|
||||||
|
const attr = namespace
|
||||||
|
? attrs.find(
|
||||||
|
s => s.namespace === "xmlns" && s.localName === namespace
|
||||||
|
)
|
||||||
|
: attrs.find(s => s.name === "xmlns");
|
||||||
|
|
||||||
|
if (attr) namespaceHref = attr.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (namespaceHref) {
|
||||||
case "urn:dslforum-org:cwmp-1-0":
|
case "urn:dslforum-org:cwmp-1-0":
|
||||||
rpc.cwmpVersion = "1.0";
|
rpc.cwmpVersion = "1.0";
|
||||||
break;
|
break;
|
||||||
@@ -613,7 +591,7 @@ export function request(data, cwmpVersion, warn): SoapMessage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (methodName) {
|
switch (methodElement.localName) {
|
||||||
case "Inform":
|
case "Inform":
|
||||||
rpc.cpeRequest = Inform(methodElement);
|
rpc.cpeRequest = Inform(methodElement);
|
||||||
break;
|
break;
|
||||||
@@ -654,22 +632,29 @@ export function request(data, cwmpVersion, warn): SoapMessage {
|
|||||||
rpc.cpeFault = fault(methodElement);
|
rpc.cpeFault = fault(methodElement);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error(`8000 Method not supported ${methodName}`);
|
throw new Error(`8000 Method not supported ${methodElement.localName}`);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return rpc;
|
return rpc;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createSoapEnv(cwmpVersion: string): Element {
|
const namespacesAttrs = {
|
||||||
const xml = new Document();
|
"1.0": Object.entries(NAMESPACES["1.0"])
|
||||||
const env = xml.node("soap-env:Envelope");
|
.map(([k, v]) => `xmlns:${k}="${v}"`)
|
||||||
|
.join(" "),
|
||||||
for (const prefix of Object.keys(NAMESPACES[cwmpVersion]))
|
"1.1": Object.entries(NAMESPACES["1.1"])
|
||||||
env.defineNamespace(prefix, NAMESPACES[cwmpVersion][prefix]);
|
.map(([k, v]) => `xmlns:${k}="${v}"`)
|
||||||
|
.join(" "),
|
||||||
return env;
|
"1.2": Object.entries(NAMESPACES["1.2"])
|
||||||
}
|
.map(([k, v]) => `xmlns:${k}="${v}"`)
|
||||||
|
.join(" "),
|
||||||
|
"1.3": Object.entries(NAMESPACES["1.3"])
|
||||||
|
.map(([k, v]) => `xmlns:${k}="${v}"`)
|
||||||
|
.join(" "),
|
||||||
|
"1.4": Object.entries(NAMESPACES["1.4"])
|
||||||
|
.map(([k, v]) => `xmlns:${k}="${v}"`)
|
||||||
|
.join(" ")
|
||||||
|
};
|
||||||
|
|
||||||
export function response(rpc): { code: number; headers: {}; data: string } {
|
export function response(rpc): { code: number; headers: {}; data: string } {
|
||||||
const headers = {
|
const headers = {
|
||||||
@@ -679,30 +664,20 @@ export function response(rpc): { code: number; headers: {}; data: string } {
|
|||||||
|
|
||||||
if (!rpc) return { code: 204, headers: headers, data: "" };
|
if (!rpc) return { code: 204, headers: headers, data: "" };
|
||||||
|
|
||||||
const env = createSoapEnv(rpc.cwmpVersion);
|
let body;
|
||||||
const header = env.node("soap-env:Header");
|
|
||||||
header
|
|
||||||
.node("cwmp:ID")
|
|
||||||
.attr({
|
|
||||||
"soap-env:mustUnderstand": "1"
|
|
||||||
})
|
|
||||||
.text(rpc.id);
|
|
||||||
|
|
||||||
const body = env.node("soap-env:Body");
|
|
||||||
|
|
||||||
if (rpc.acsResponse) {
|
if (rpc.acsResponse) {
|
||||||
switch (rpc.acsResponse.name) {
|
switch (rpc.acsResponse.name) {
|
||||||
case "InformResponse":
|
case "InformResponse":
|
||||||
InformResponse(body);
|
body = InformResponse();
|
||||||
break;
|
break;
|
||||||
case "GetRPCMethodsResponse":
|
case "GetRPCMethodsResponse":
|
||||||
GetRPCMethodsResponse(body, rpc.acsResponse);
|
body = GetRPCMethodsResponse(rpc.acsResponse);
|
||||||
break;
|
break;
|
||||||
case "TransferCompleteResponse":
|
case "TransferCompleteResponse":
|
||||||
TransferCompleteResponse(body);
|
body = TransferCompleteResponse();
|
||||||
break;
|
break;
|
||||||
case "RequestDownloadResponse":
|
case "RequestDownloadResponse":
|
||||||
RequestDownloadResponse(body);
|
body = RequestDownloadResponse();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown method response type ${rpc.acsResponse.name}`);
|
throw new Error(`Unknown method response type ${rpc.acsResponse.name}`);
|
||||||
@@ -710,28 +685,28 @@ export function response(rpc): { code: number; headers: {}; data: string } {
|
|||||||
} else if (rpc.acsRequest) {
|
} else if (rpc.acsRequest) {
|
||||||
switch (rpc.acsRequest.name) {
|
switch (rpc.acsRequest.name) {
|
||||||
case "GetParameterNames":
|
case "GetParameterNames":
|
||||||
GetParameterNames(body, rpc.acsRequest);
|
body = GetParameterNames(rpc.acsRequest);
|
||||||
break;
|
break;
|
||||||
case "GetParameterValues":
|
case "GetParameterValues":
|
||||||
GetParameterValues(body, rpc.acsRequest);
|
body = GetParameterValues(rpc.acsRequest);
|
||||||
break;
|
break;
|
||||||
case "SetParameterValues":
|
case "SetParameterValues":
|
||||||
SetParameterValues(body, rpc.acsRequest);
|
body = SetParameterValues(rpc.acsRequest);
|
||||||
break;
|
break;
|
||||||
case "AddObject":
|
case "AddObject":
|
||||||
AddObject(body, rpc.acsRequest);
|
body = AddObject(rpc.acsRequest);
|
||||||
break;
|
break;
|
||||||
case "DeleteObject":
|
case "DeleteObject":
|
||||||
DeleteObject(body, rpc.acsRequest);
|
body = DeleteObject(rpc.acsRequest);
|
||||||
break;
|
break;
|
||||||
case "Reboot":
|
case "Reboot":
|
||||||
Reboot(body, rpc.acsRequest);
|
body = Reboot(rpc.acsRequest);
|
||||||
break;
|
break;
|
||||||
case "FactoryReset":
|
case "FactoryReset":
|
||||||
FactoryReset(body);
|
body = FactoryReset();
|
||||||
break;
|
break;
|
||||||
case "Download":
|
case "Download":
|
||||||
Download(body, rpc.acsRequest);
|
body = Download(rpc.acsRequest);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown method request ${rpc.acsRequest.name}`);
|
throw new Error(`Unknown method request ${rpc.acsRequest.name}`);
|
||||||
@@ -742,6 +717,10 @@ export function response(rpc): { code: number; headers: {}; data: string } {
|
|||||||
return {
|
return {
|
||||||
code: 200,
|
code: 200,
|
||||||
headers: headers,
|
headers: headers,
|
||||||
data: env.doc().toString(LIBXMLJS_SAVE_OPTIONS)
|
data: `<?xml version="1.0" encoding="UTF-8"?>\n<soap-env:Envelope ${
|
||||||
|
namespacesAttrs[rpc.cwmpVersion]
|
||||||
|
}><soap-env:Header><cwmp:ID soap-env:mustUnderstand="1">${
|
||||||
|
rpc.id
|
||||||
|
}</cwmp:ID></soap-env:Header><soap-env:Body>${body}</soap-env:Body></soap-env:Envelope>`
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user