UNPKG

@shockpkg/ria-packager

Version:

Package for creating Adobe AIR packages

516 lines (465 loc) 11.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Packager = void 0; var _promises = require("node:fs/promises"); var _nodePath = require("node:path"); var _archiveFiles = require("@shockpkg/archive-files"); var _signature = require("./signature.js"); var _sha = require("./hasher/sha256.js"); /** * Options for adding resources. */ /** * Packager object. */ class Packager { /** * Make a debug build. */ debug = false; /** * Keystore object to use for signing. */ keystore = null; /** * Timestamp URL. */ timestampUrl = null; /** * Application descriptor file data. */ descriptorData = null; /** * Application descriptor file path. */ descriptorFile = null; /** * File and directory names to exclude when added a directory. */ excludes = [/^\./, /^ehthumbs\.db$/i, /^Thumbs\.db$/i]; /** * Set the nobrowse option on mounted disk images. */ nobrowse = false; /** * Output path. */ /** * Open flag. */ _isOpen = false; /** * Adding a resource flag. */ _isAddingResource = false; /** * Hasher object. */ /** * Signature object. */ _signature = null; /** * Packager constructor. * * @param path Output path. */ constructor(path) { this._hasher = this._createHasher(); this.path = path; } /** * Check if output open. * * @returns Returns true if open, else false. */ get isOpen() { return this._isOpen; } /** * Open with application descriptor XML data. */ async open() { if (this._isOpen) { throw new Error('Already open'); } const descriptorData = await this._getDescriptorData(); this._applicationInfoInit(descriptorData); await this._open(); this._isOpen = true; this._hasher.reset(); this._signature = null; if (this.signed) { this._signature = this._createSignature(); this._signature.timestampUrl = this.timestampUrl; const keystore = this._getKeystore(); this._signature.certificate = keystore.getCertificate(); this._signature.privateKey = keystore.getPrivateKey(); } await this._addMetaResourcesStart(descriptorData); } /** * Close output. */ async close() { if (!this._isOpen) { throw new Error('Not open'); } await this._addMetaResourcesEnd(); await this._close(); this._isOpen = false; this._applicationInfoClear(); this._hasher.reset(); this._signature = null; } /** * Run asyncronous function with automatic open and close. * * @param func Async function. * @returns Return value of the async function. */ async write(func) { await this.open(); let r; try { r = await func.call(this, this); } finally { await this.close(); } return r; } /** * Check if name is excluded file. * * @param name File name. * @returns Returns true if excluded, else false. */ isExcludedFile(name) { for (const exclude of this.excludes) { if (exclude.test(name)) { return true; } } return false; } /** * Add resource with file. * * @param source File path. * @param destination Packaged file relative destination. * @param options Resource options. */ async addResourceFile(source, destination = null, options = null) { const opts = options || {}; const dest = destination === null ? source : destination; const stat = await (0, _promises.lstat)(source); // Symlinks would only be allowed in a macOS native extension. // Throw an error like the official packager does. if (stat.isSymbolicLink()) { throw new Error(`Cannot add symlink: ${source}`); } // Throw if not a regular file. if (!stat.isFile()) { throw new Error(`Unsupported file type: ${source}`); } let { executable } = opts; if (executable !== true && executable !== false) { // eslint-disable-next-line no-bitwise executable = !!(stat.mode & 0b001000000); } const data = await (0, _promises.readFile)(source); await this.addResource(dest, data, { executable, mtime: opts.mtime || stat.mtime }); } /** * Add resource with directory. * Walks the directory looking for files to add, skips excluded file names. * * @param source Directory path. * @param destination Packaged directory relative destination. * @param options Resource options. */ async addResourceDirectory(source, destination = null, options = null) { const dest = destination === null ? source : destination; await (0, _archiveFiles.fsWalk)(source, async (path, stat) => { // If this name is excluded, skip without descending. if (this.isExcludedFile((0, _nodePath.basename)(path))) { return false; } // Ignore directories, but descend into them. // Only files are listed in the ZIP packages. // Created automatically for files in any other package. if (stat.isDirectory()) { return true; } // Anything else assume file. await this.addResourceFile((0, _nodePath.join)(source, path), (0, _nodePath.join)(dest, path), options); return true; }); } /** * Add resource with data. * * @param destination Packaged file relative destination. * @param data Resource data. * @param options Resource options. */ async addResource(destination, data, options = null) { if (!this._isOpen) { throw new Error('Not open'); } if (this._isAddingResource) { throw new Error('Resources must be added sequentially'); } this._isAddingResource = true; await this._addResource((0, _archiveFiles.pathNormalize)(destination), data, options || {}, true, true); this._isAddingResource = false; } /** * Create Hasher object. * * @returns Hasher object. */ _createHasher() { return new _sha.HasherSha256(); } /** * Create Signature object. * * @returns Hasher object. */ _createSignature() { return new _signature.Signature(); } /** * Path of the mimetype meta resource. * * @returns Resource path. */ get _metaResourceMimetypePath() { return 'mimetype'; } /** * Path of the application meta resource. * * @returns Resource path. */ get _metaResourceApplicationPath() { return 'META-INF/AIR/application.xml'; } /** * Path of the hash meta resource. * * @returns Resource path. */ get _metaResourceHashPath() { return 'META-INF/AIR/hash'; } /** * Path of the debug meta resource. * * @returns Resource path. */ get _metaResourceDebugPath() { return 'META-INF/AIR/debug'; } /** * Path of the signatures meta resource. * * @returns Resource path. */ get _metaResourceSignaturesPath() { return 'META-INF/signatures.xml'; } /** * Get application descriptor data or throw. * * @returns Application descriptor XML data. */ async _getDescriptorData() { const { descriptorData, descriptorFile } = this; if (descriptorData) { switch (typeof descriptorData) { case 'function': { const d = await descriptorData(); return typeof d === 'string' ? new TextEncoder().encode(d) : d; } case 'string': { return new TextEncoder().encode(descriptorData); } default: { return descriptorData; } } } if (descriptorFile !== null) { const d = await (0, _promises.readFile)(descriptorFile); return new Uint8Array(d.buffer, d.byteOffset, d.byteLength); } throw new Error('Missing application descriptor data'); } /** * Get encoded mimetype data. * * @returns Mimetype data. */ _getMimetypeData() { // The mimetype is UTF-8. return new TextEncoder().encode(this.mimetype); } /** * Get the keystore object. * * @returns Keystore object. */ _getKeystore() { const r = this.keystore; if (!r) { throw new Error('A keystore not set'); } return r; } /** * Add resource with data, with options controlling hashing and signing. * * @param destination Packaged file relative destination. * @param data Resource data. * @param options Resource options. * @param hashed This file is hashed. * @param signed This file is signed. */ async _addResource(destination, data, options, hashed, signed) { if (hashed) { this._hasher.update(data); } if (signed) { const signature = this._signature; if (signature) { signature.addFile(destination, data); } } await this._writeResource(destination, data, options); } /** * Add meta resources start. * * @param applicationData XML data. */ async _addMetaResourcesStart(applicationData) { await this._addMetaResourceMimetype(); await this._addMetaResourceApplication(applicationData); await this._addMetaResourceHash(); if (this.debug) { await this._addMetaResourceDebug(); } } /** * Add meta resources end. */ async _addMetaResourcesEnd() { if (this.signed) { await this._addMetaResourceSignatures(); } } /** * Add meta resource for the mimetype. */ async _addMetaResourceMimetype() { const path = this._metaResourceMimetypePath; const data = this._getMimetypeData(); await this._addResource(path, data, {}, true, true); } /** * Add meta resource for the application descriptor. * * @param applicationData The application descriptor data. */ async _addMetaResourceApplication(applicationData) { const path = this._metaResourceApplicationPath; await this._addResource(path, applicationData, {}, true, true); } /** * Add meta resource for the hash (needs updating on close). */ async _addMetaResourceHash() { const path = this._metaResourceHashPath; const data = new Uint8Array(this._hasher.bytes); await this._addResource(path, data, {}, false, false); } /** * Add meta resource for debug. */ async _addMetaResourceDebug() { const path = this._metaResourceDebugPath; const data = new Uint8Array(0); await this._addResource(path, data, {}, true, true); } /** * Add resource for signatures. */ async _addMetaResourceSignatures() { const path = this._metaResourceSignaturesPath; const signature = this._signature; if (!signature) { throw new Error('Internal error'); } signature.digest(); signature.sign(); if (signature.timestampUrl) { await signature.timestamp(); } const data = signature.encode(); await this._addResource(path, data, {}, false, false); } /** * Init application info from descriptor data. * * @param applicationData The application descriptor data. */ _applicationInfoInit(applicationData) { // Do nothing. } /** * Clear application info from descriptor data. */ _applicationInfoClear() { // Do nothing. } /** * Package mimetype. * * @returns Mimetype string. */ /** * Package signed. * * @returns Boolean for if package is signed or not. */ /** * Open implementation. */ /** * Close implementation. */ /** * Write resource with data implementation. * * @param destination Packaged file relative destination. * @param data Resource data. * @param options Resource options. */ } exports.Packager = Packager; //# sourceMappingURL=packager.js.map