From 82804f66570330b5a5ca87443c3c8a7c268946f3 Mon Sep 17 00:00:00 2001 From: C-3PO Date: Sat, 23 Jun 2018 21:33:35 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Parse=20manifest.xml=20into=20JSON?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ssn/getPatchmanifest.ts | 12 +++++++++- src/ssn/parsePatchmanifest.ts | 20 +++++++++++++++++ src/ssn/verifyPatchmanifest.ts | 40 +++++++++++++++++----------------- 3 files changed, 51 insertions(+), 21 deletions(-) create mode 100644 src/ssn/parsePatchmanifest.ts diff --git a/src/ssn/getPatchmanifest.ts b/src/ssn/getPatchmanifest.ts index 50ca5f2..f338a86 100644 --- a/src/ssn/getPatchmanifest.ts +++ b/src/ssn/getPatchmanifest.ts @@ -2,6 +2,7 @@ import { TextDecoder } from 'util'; import * as xmlJs from 'xml-js'; import getUrlContents from '../cdn/getUrlContents'; import { Product } from '../interfaces/ISettings'; +import parsePatchmanifest from '../ssn/parsePatchmanifest'; import verifyPatchmanifest from '../ssn/verifyPatchmanifest'; import verifyProductName from '../ssn/verifyProductName'; import extractFile from './extractFile'; @@ -21,6 +22,7 @@ export default async function getPatchmanifest(product: Product): Promise { //Parse .patchmanifest file const fileEntries = readSsnFile(ssnFile); + //Verify .patchmanifest file if (fileEntries.length !== 1) { throw new Error(`Expected .patchmanifest to contain 1 file but it had "${fileEntries.length}" files.`); } @@ -32,10 +34,18 @@ export default async function getPatchmanifest(product: Product): Promise { //Extract manifest.xml file const patchmanifestFile = await extractFile(firstFile, [new DataView(ssnFile)]); + + //Convert ArrayBuffer to string const patchmanifestXml = Decoder.decode(patchmanifestFile); + //convert XML to JSON-converted XML const patchManifestJson = xmlJs.xml2js(patchmanifestXml) as xmlJs.Element; + + //verify that XML file contains all required elements and attributes verifyPatchmanifest(patchManifestJson, product); - return patchManifestJson; + //convert JSON-converted XML to an easier to read JSON + const patchManifestSimple = parsePatchmanifest(patchManifestJson); + + return patchManifestSimple; } diff --git a/src/ssn/parsePatchmanifest.ts b/src/ssn/parsePatchmanifest.ts new file mode 100644 index 0000000..9c93d87 --- /dev/null +++ b/src/ssn/parsePatchmanifest.ts @@ -0,0 +1,20 @@ + +/** Receives a JSON-converted version of the manifest.xml file, and returns an easier to read JSON file */ +export default function parsePatchManifest(manifestFile: any): any { + const out: any = {}; + + const PatchManifest = manifestFile.elements[0]; + + //289 + const RequiredRelease = PatchManifest.elements[2]; + out.current = Number(RequiredRelease.elements[0].text); + + //053678f8057e52896a8145dca5c188ab3f24fa55f + const Releases = PatchManifest.elements[7]; + out.releases = Releases.elements.map((Release: any) => ({ id: Release.elements[0].elements[0].text, sha1: Release.elements[1].elements[0].text })); + + const ReleaseUpdatePaths = PatchManifest.elements[8]; + out.paths = ReleaseUpdatePaths.elements.map((ReleaseUpdatePath: any) => ({ from: ReleaseUpdatePath.elements[0].elements[0].text, to: ReleaseUpdatePath.elements[1].elements[0].text })); + + return out; +} diff --git a/src/ssn/verifyPatchmanifest.ts b/src/ssn/verifyPatchmanifest.ts index 73478c6..14ae503 100644 --- a/src/ssn/verifyPatchmanifest.ts +++ b/src/ssn/verifyPatchmanifest.ts @@ -1,45 +1,45 @@ import * as xmlJs from 'xml-js'; import { Product } from '../interfaces/ISettings'; -/** Receives a JSON-converted version of the manifest.xml file */ -export default function verifyPatchmanifest(obj: xmlJs.Element, product: Product): any { +/** Receives a JSON-converted version of the manifest.xml file, and verifies that all required elements and attributes are present, and nothing more */ +export default function verifyPatchmanifest(manifestFile: xmlJs.Element, product: Product): any { // - if (obj.declaration === undefined || obj.declaration.attributes === undefined || Object.keys(obj.declaration.attributes).length !== 2 || obj.declaration.attributes.version !== '1.0' || obj.declaration.attributes.encoding !== 'utf-8') { + if (manifestFile.declaration === undefined || manifestFile.declaration.attributes === undefined || Object.keys(manifestFile.declaration.attributes).length !== 2 || manifestFile.declaration.attributes.version !== '1.0' || manifestFile.declaration.attributes.encoding !== 'utf-8') { throw new Error('Expected declration with version 1.0 and utf-8 encoding.'); } // - if (obj.elements === undefined || obj.elements.length !== 1) { + if (manifestFile.elements === undefined || manifestFile.elements.length !== 1) { throw new Error('Expected one root element.'); } - const root = obj.elements[0]; - if (root.type !== 'element' || root.name !== 'PatchManifest') { - throw new Error(`Expected root element to be called PatchManifest but it was "${root.name}".`); + const PatchManifest = manifestFile.elements[0]; + if (PatchManifest.type !== 'element' || PatchManifest.name !== 'PatchManifest') { + throw new Error(`Expected root element to be called PatchManifest but it was "${PatchManifest.name}".`); } - if (root.attributes === undefined || Object.keys(root.attributes).length !== 2 || root.attributes['xmlns:xsi'] !== 'http://www.w3.org/2001/XMLSchema-instance' || root.attributes['xmlns:xsd'] !== 'http://www.w3.org/2001/XMLSchema') { + if (PatchManifest.attributes === undefined || Object.keys(PatchManifest.attributes).length !== 2 || PatchManifest.attributes['xmlns:xsi'] !== 'http://www.w3.org/2001/XMLSchema-instance' || PatchManifest.attributes['xmlns:xsd'] !== 'http://www.w3.org/2001/XMLSchema') { throw new Error(`Expected root element to have attributes xmlns:xsi and xmlns:xsd.`); } - if (root.elements === undefined) { + if (PatchManifest.elements === undefined) { throw new Error(`Expected child elements under PatchManifest but there were none.`); } - if (root.elements.length !== 9) { - throw new Error(`Expected 9 child elements under PatchManifest but there were ${root.elements.length}.`); + if (PatchManifest.elements.length !== 9) { + throw new Error(`Expected 9 child elements under PatchManifest but there were ${PatchManifest.elements.length}.`); } // - const Dependencies = root.elements[0]; + const Dependencies = PatchManifest.elements[0]; if (Dependencies.type !== 'element' || Dependencies.name !== 'Dependencies' || Dependencies.attributes !== undefined || Dependencies.elements !== undefined) { throw new Error('Expected Dependencies element with no child elements and no attributes.'); } //assets_swtor_de_de - const Name = root.elements[1]; + const Name = PatchManifest.elements[1]; if (Name.type !== 'element' || Name.name !== 'Name' || Name.attributes !== undefined || Name.elements === undefined || Name.elements.length !== 1 || Name.elements[0].type !== 'text' || Name.elements[0].text !== product) { throw new Error('Expected Name element with one child element equal to product and no attributes.'); } //289 - const RequiredRelease = root.elements[2]; + const RequiredRelease = PatchManifest.elements[2]; if (RequiredRelease.type !== 'element' || RequiredRelease.name !== 'RequiredRelease' || RequiredRelease.attributes !== undefined || RequiredRelease.elements === undefined) { throw new Error('Expected RequiredRelease element.'); } @@ -48,7 +48,7 @@ export default function verifyPatchmanifest(obj: xmlJs.Element, product: Product } //-1 - const UpcomingRelease = root.elements[3]; + const UpcomingRelease = PatchManifest.elements[3]; if (UpcomingRelease.type !== 'element' || UpcomingRelease.name !== 'UpcomingRelease' || UpcomingRelease.attributes !== undefined || UpcomingRelease.elements === undefined) { throw new Error('Expected UpcomingRelease element.'); } @@ -57,7 +57,7 @@ export default function verifyPatchmanifest(obj: xmlJs.Element, product: Product } //{ModulePath} - const TargetDirectory = root.elements[4]; + const TargetDirectory = PatchManifest.elements[4]; if (TargetDirectory.type !== 'element' || TargetDirectory.name !== 'TargetDirectory' || TargetDirectory.attributes !== undefined || TargetDirectory.elements === undefined) { throw new Error('Expected TargetDirectory element.'); } @@ -66,7 +66,7 @@ export default function verifyPatchmanifest(obj: xmlJs.Element, product: Product } //false - const RequiresElevation = root.elements[5]; + const RequiresElevation = PatchManifest.elements[5]; if (RequiresElevation.type !== 'element' || RequiresElevation.name !== 'RequiresElevation' || RequiresElevation.attributes !== undefined || RequiresElevation.elements === undefined) { throw new Error('Expected RequiresElevation element.'); } @@ -75,7 +75,7 @@ export default function verifyPatchmanifest(obj: xmlJs.Element, product: Product } //false - const Maintenance = root.elements[6]; + const Maintenance = PatchManifest.elements[6]; if (Maintenance.type !== 'element' || Maintenance.name !== 'Maintenance' || Maintenance.attributes !== undefined || Maintenance.elements === undefined) { throw new Error('Expected Maintenance element.'); } @@ -84,7 +84,7 @@ export default function verifyPatchmanifest(obj: xmlJs.Element, product: Product } // - const Releases = root.elements[7]; + const Releases = PatchManifest.elements[7]; if (Releases.type !== 'element' || Releases.name !== 'Releases' || Releases.attributes !== undefined || Releases.elements === undefined) { throw new Error('Expected Releases element.'); } @@ -112,7 +112,7 @@ export default function verifyPatchmanifest(obj: xmlJs.Element, product: Product } // - const ReleaseUpdatePaths = root.elements[8]; + const ReleaseUpdatePaths = PatchManifest.elements[8]; if (ReleaseUpdatePaths.type !== 'element' || ReleaseUpdatePaths.name !== 'ReleaseUpdatePaths' || ReleaseUpdatePaths.attributes !== undefined || ReleaseUpdatePaths.elements === undefined) { throw new Error('Expected ReleaseUpdatePaths element.'); }