- Copy cwmp-1-0..4.ts source classes from cwmp-connector (papaye) into app/protocol/ as local files (ts-nocheck, external dependency) - Add soap_envelope.ts: SoapEnvelope, SoapEnvelopeHeader, CwmpID, NAMESPACES - Add tr069_types.ts: re-exports all CWMP classes + CwmpMessage discriminated union + typed task payload interfaces (CWMPTaskType, CWMPTaskPayload, etc.) - Rewrite cwmp_message_builder.ts to use class-based builders instead of raw XML template strings; add transferCompleteResponse() and soapFault() - soap.ts: extractCwmpMessage returns CwmpMessage union, adds runtime allowlist guard against unknown CWMP method names from CPE - cwmp_middleware.ts: envelope typed SoapEnvelope|null, message CwmpMessage|null - cwmp_controller.ts: handleInform(informData: Inform), remove any, migrate TransferComplete and fault to new builder path, drop buildSoapEnvelope import - task_queue.ts: CWMPTask.type → CWMPTaskType literal union, payload → CWMPTaskPayload, result → CWMPTaskResultData - device_action_executor.ts: GPV/GPN/GPA responses typed, no more any - main_state_machine.ts: DeviceContext.message.data → CWMPTaskResultData, organizeParameters receives ParameterInfoStruct[] directly - device.ts: tmpParamValues typed ParameterValueStruct[], organizeParameters signature updated, updateFromValues typed - device_id.ts: fromInform typed with DeviceIdStruct - soap_envelope.ts: remove unconditional <cwmp:noMoreRequests> header (CPE→ACS only per TR-069 §3.7.1.4)
196 lines
6.1 KiB
TypeScript
196 lines
6.1 KiB
TypeScript
import { randomBytes } from 'node:crypto'
|
|
import {
|
|
Cwmp_1_0,
|
|
Download,
|
|
FactoryReset,
|
|
FaultCodeType,
|
|
FaultDetail,
|
|
FaultStruct,
|
|
Fault,
|
|
GetParameterAttributes,
|
|
GetParameterNames,
|
|
GetParameterValues,
|
|
InformResponse,
|
|
ParameterValueList,
|
|
Reboot,
|
|
ScheduleInform,
|
|
SetParameterAttributes,
|
|
SetParameterAttributesList,
|
|
SetParameterAttributesStruct,
|
|
AccessList,
|
|
SetParameterValues,
|
|
TransferCompleteResponse,
|
|
Upload,
|
|
} from '../cwmp-1-0.js'
|
|
import {
|
|
CwmpID,
|
|
NAMESPACES,
|
|
SoapEnvelope,
|
|
SoapEnvelopeAttribute,
|
|
SoapEnvelopeHeader,
|
|
} from '../soap_envelope.js'
|
|
import type { DownloadTaskPayload, UploadTaskPayload, SetParameterValuesTaskPayload, SetParameterAttributesTaskPayload } from '../tr069_types.js'
|
|
|
|
function generateCwmpID(): string {
|
|
return randomBytes(4).toString('hex')
|
|
}
|
|
|
|
function makeEnvelope(cwmpId?: string): SoapEnvelope {
|
|
const enve = new SoapEnvelope()
|
|
enve.$ = NAMESPACES['1.0'] as SoapEnvelopeAttribute
|
|
enve.Header = new SoapEnvelopeHeader()
|
|
enve.Header.ID = new CwmpID({
|
|
$: { MustUnderstand: '1' },
|
|
_: cwmpId || generateCwmpID(),
|
|
})
|
|
enve.Body = new Cwmp_1_0()
|
|
return enve
|
|
}
|
|
|
|
export class CWMPMessageBuilder {
|
|
static informResponse(cwmpId?: string): string {
|
|
const enve = makeEnvelope(cwmpId)
|
|
enve.Body.InformResponse = new InformResponse({ MaxEnvelopes: 1 })
|
|
return enve.getXml()
|
|
}
|
|
|
|
static getParameterNames(cwmpId: string | undefined, parameterPath: string, nextLevel: boolean = true): string {
|
|
const enve = makeEnvelope(cwmpId)
|
|
enve.Body.GetParameterNames = new GetParameterNames({
|
|
ParameterPath: parameterPath,
|
|
NextLevel: nextLevel,
|
|
})
|
|
return enve.getXml()
|
|
}
|
|
|
|
static getParameterValues(cwmpId: string | undefined, parameterNames: string[]): string {
|
|
const enve = makeEnvelope(cwmpId)
|
|
enve.Body.GetParameterValues = new GetParameterValues({ ParameterNames: parameterNames })
|
|
return enve.getXml()
|
|
}
|
|
|
|
static setParameterValues(
|
|
cwmpId: string | undefined,
|
|
parameters: Array<{ name: string; value: string; type: string }>
|
|
): string {
|
|
const pvStructs = parameters.map((p) => ({
|
|
Name: p.name,
|
|
Value: { _: p.value, $: { Type: p.type } },
|
|
}))
|
|
const pvList = new ParameterValueList({
|
|
$: { ArrayType: `cwmp:ParameterValueStruct[${pvStructs.length}]` },
|
|
ParameterValueStruct: pvStructs,
|
|
})
|
|
const enve = makeEnvelope(cwmpId)
|
|
enve.Body.SetParameterValues = new SetParameterValues({
|
|
ParameterValueList: pvList,
|
|
ParameterKey: 'papaye-v2',
|
|
})
|
|
return enve.getXml()
|
|
}
|
|
|
|
static setParameterValuesFromPayload(cwmpId: string | undefined, payload: SetParameterValuesTaskPayload): string {
|
|
const params = (payload.parameterValueList || []).map((p) => ({
|
|
name: p.Name,
|
|
value: String(p.Value?._ ?? ''),
|
|
type: p.Value?.$?.Type || 'xsd:string',
|
|
}))
|
|
return CWMPMessageBuilder.setParameterValues(cwmpId, params)
|
|
}
|
|
|
|
static download(cwmpId: string | undefined, params: DownloadTaskPayload): string {
|
|
const enve = makeEnvelope(cwmpId)
|
|
enve.Body.Download = new Download({
|
|
CommandKey: params.commandKey || '',
|
|
FileType: params.fileType || '1 Firmware Upgrade Image',
|
|
URL: params.url,
|
|
Username: params.username || '',
|
|
Password: params.password || '',
|
|
FileSize: params.fileSize || 0,
|
|
TargetFileName: params.targetFileName || '',
|
|
DelaySeconds: params.delaySeconds || 0,
|
|
SuccessURL: params.successUrl || '',
|
|
FailureURL: params.failureUrl || '',
|
|
})
|
|
return enve.getXml()
|
|
}
|
|
|
|
static upload(cwmpId: string | undefined, params: UploadTaskPayload): string {
|
|
const enve = makeEnvelope(cwmpId)
|
|
enve.Body.Upload = new Upload({
|
|
CommandKey: params.commandKey || '',
|
|
FileType: params.fileType || '3 Vendor Configuration File',
|
|
URL: params.url,
|
|
Username: params.username || '',
|
|
Password: params.password || '',
|
|
DelaySeconds: params.delaySeconds || 0,
|
|
})
|
|
return enve.getXml()
|
|
}
|
|
|
|
static reboot(cwmpId?: string, commandKey?: string): string {
|
|
const enve = makeEnvelope(cwmpId)
|
|
enve.Body.Reboot = new Reboot({ CommandKey: commandKey || '' })
|
|
return enve.getXml()
|
|
}
|
|
|
|
static factoryReset(cwmpId?: string): string {
|
|
const enve = makeEnvelope(cwmpId)
|
|
enve.Body.FactoryReset = new FactoryReset({})
|
|
return enve.getXml()
|
|
}
|
|
|
|
static scheduleInform(cwmpId: string | undefined, delaySeconds: number, commandKey?: string): string {
|
|
const enve = makeEnvelope(cwmpId)
|
|
enve.Body.ScheduleInform = new ScheduleInform({
|
|
DelaySeconds: delaySeconds,
|
|
CommandKey: commandKey || '',
|
|
})
|
|
return enve.getXml()
|
|
}
|
|
|
|
static getParameterAttributes(cwmpId: string | undefined, parameterNames: string[]): string {
|
|
const enve = makeEnvelope(cwmpId)
|
|
enve.Body.GetParameterAttributes = new GetParameterAttributes({ ParameterNames: parameterNames })
|
|
return enve.getXml()
|
|
}
|
|
|
|
static setParameterAttributes(
|
|
cwmpId: string | undefined,
|
|
attributes: SetParameterAttributesTaskPayload[]
|
|
): string {
|
|
const structs = attributes.map(
|
|
(a) =>
|
|
new SetParameterAttributesStruct({
|
|
Name: a.name,
|
|
NotificationChange: a.notificationChange,
|
|
Notification: a.notification,
|
|
AccessListChange: a.accessListChange,
|
|
AccessList: new AccessList({ string: a.accessList }),
|
|
})
|
|
)
|
|
const list = new SetParameterAttributesList({ SetParameterAttributesStruct: structs })
|
|
const enve = makeEnvelope(cwmpId)
|
|
enve.Body.SetParameterAttributes = new SetParameterAttributes({ ParameterList: list })
|
|
return enve.getXml()
|
|
}
|
|
|
|
static transferCompleteResponse(cwmpId?: string): string {
|
|
const enve = makeEnvelope(cwmpId)
|
|
enve.Body.TransferCompleteResponse = new TransferCompleteResponse({})
|
|
return enve.getXml()
|
|
}
|
|
|
|
static soapFault(faultCode: FaultCodeType, reason: string): string {
|
|
const fault = new Fault()
|
|
fault.Faultcode = 'Server'
|
|
fault.Faultstring = reason
|
|
fault.Detail = new FaultDetail({ Fault: new FaultStruct({ FaultCode: faultCode, FaultString: reason }) })
|
|
return fault.getXml()
|
|
}
|
|
|
|
static emptyResponse(): string {
|
|
return ''
|
|
}
|
|
}
|