1 | "use strict";
|
2 |
|
3 | Object.defineProperty(exports, "__esModule", {
|
4 | value: true
|
5 | });
|
6 | exports.Packager = void 0;
|
7 | var _promises = require("node:fs/promises");
|
8 | var _nodePath = require("node:path");
|
9 | var _archiveFiles = require("@shockpkg/archive-files");
|
10 | var _signature = require("./signature.js");
|
11 | var _sha = require("./hasher/sha256.js");
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 | class Packager {
|
20 | |
21 |
|
22 |
|
23 | debug = false;
|
24 |
|
25 | |
26 |
|
27 |
|
28 | keystore = null;
|
29 |
|
30 | |
31 |
|
32 |
|
33 | timestampUrl = null;
|
34 |
|
35 | |
36 |
|
37 |
|
38 | descriptorData = null;
|
39 |
|
40 | |
41 |
|
42 |
|
43 | descriptorFile = null;
|
44 |
|
45 | |
46 |
|
47 |
|
48 | excludes = [/^\./, /^ehthumbs\.db$/i, /^Thumbs\.db$/i];
|
49 |
|
50 | |
51 |
|
52 |
|
53 | nobrowse = false;
|
54 |
|
55 | |
56 |
|
57 |
|
58 |
|
59 | |
60 |
|
61 |
|
62 | _isOpen = false;
|
63 |
|
64 | |
65 |
|
66 |
|
67 | _isAddingResource = false;
|
68 |
|
69 | |
70 |
|
71 |
|
72 |
|
73 | |
74 |
|
75 |
|
76 | _signature = null;
|
77 |
|
78 | |
79 |
|
80 |
|
81 |
|
82 |
|
83 | constructor(path) {
|
84 | this._hasher = this._createHasher();
|
85 | this.path = path;
|
86 | }
|
87 |
|
88 | |
89 |
|
90 |
|
91 |
|
92 |
|
93 | get isOpen() {
|
94 | return this._isOpen;
|
95 | }
|
96 |
|
97 | |
98 |
|
99 |
|
100 | async open() {
|
101 | if (this._isOpen) {
|
102 | throw new Error('Already open');
|
103 | }
|
104 | const descriptorData = await this._getDescriptorData();
|
105 | this._applicationInfoInit(descriptorData);
|
106 | await this._open();
|
107 | this._isOpen = true;
|
108 | this._hasher.reset();
|
109 | this._signature = null;
|
110 | if (this.signed) {
|
111 | this._signature = this._createSignature();
|
112 | this._signature.timestampUrl = this.timestampUrl;
|
113 | const keystore = this._getKeystore();
|
114 | this._signature.certificate = keystore.getCertificate();
|
115 | this._signature.privateKey = keystore.getPrivateKey();
|
116 | }
|
117 | await this._addMetaResourcesStart(descriptorData);
|
118 | }
|
119 |
|
120 | |
121 |
|
122 |
|
123 | async close() {
|
124 | if (!this._isOpen) {
|
125 | throw new Error('Not open');
|
126 | }
|
127 | await this._addMetaResourcesEnd();
|
128 | await this._close();
|
129 | this._isOpen = false;
|
130 | this._applicationInfoClear();
|
131 | this._hasher.reset();
|
132 | this._signature = null;
|
133 | }
|
134 |
|
135 | |
136 |
|
137 |
|
138 |
|
139 |
|
140 |
|
141 | async write(func) {
|
142 | await this.open();
|
143 | let r;
|
144 | try {
|
145 | r = await func.call(this, this);
|
146 | } finally {
|
147 | await this.close();
|
148 | }
|
149 | return r;
|
150 | }
|
151 |
|
152 | |
153 |
|
154 |
|
155 |
|
156 |
|
157 |
|
158 | isExcludedFile(name) {
|
159 | for (const exclude of this.excludes) {
|
160 | if (exclude.test(name)) {
|
161 | return true;
|
162 | }
|
163 | }
|
164 | return false;
|
165 | }
|
166 |
|
167 | |
168 |
|
169 |
|
170 |
|
171 |
|
172 |
|
173 |
|
174 | async addResourceFile(source, destination = null, options = null) {
|
175 | const opts = options || {};
|
176 | const dest = destination === null ? source : destination;
|
177 | const stat = await (0, _promises.lstat)(source);
|
178 |
|
179 |
|
180 |
|
181 | if (stat.isSymbolicLink()) {
|
182 | throw new Error(`Cannot add symlink: ${source}`);
|
183 | }
|
184 |
|
185 |
|
186 | if (!stat.isFile()) {
|
187 | throw new Error(`Unsupported file type: ${source}`);
|
188 | }
|
189 | let {
|
190 | executable
|
191 | } = opts;
|
192 | if (executable !== true && executable !== false) {
|
193 |
|
194 | executable = !!(stat.mode & 0b001000000);
|
195 | }
|
196 | const data = await (0, _promises.readFile)(source);
|
197 | await this.addResource(dest, data, {
|
198 | executable,
|
199 | mtime: opts.mtime || stat.mtime
|
200 | });
|
201 | }
|
202 |
|
203 | |
204 |
|
205 |
|
206 |
|
207 |
|
208 |
|
209 |
|
210 |
|
211 | async addResourceDirectory(source, destination = null, options = null) {
|
212 | const dest = destination === null ? source : destination;
|
213 | await (0, _archiveFiles.fsWalk)(source, async (path, stat) => {
|
214 |
|
215 | if (this.isExcludedFile((0, _nodePath.basename)(path))) {
|
216 | return false;
|
217 | }
|
218 |
|
219 |
|
220 |
|
221 |
|
222 | if (stat.isDirectory()) {
|
223 | return true;
|
224 | }
|
225 |
|
226 |
|
227 | await this.addResourceFile((0, _nodePath.join)(source, path), (0, _nodePath.join)(dest, path), options);
|
228 | return true;
|
229 | });
|
230 | }
|
231 |
|
232 | |
233 |
|
234 |
|
235 |
|
236 |
|
237 |
|
238 |
|
239 | async addResource(destination, data, options = null) {
|
240 | if (!this._isOpen) {
|
241 | throw new Error('Not open');
|
242 | }
|
243 | if (this._isAddingResource) {
|
244 | throw new Error('Resources must be added sequentially');
|
245 | }
|
246 | this._isAddingResource = true;
|
247 | await this._addResource((0, _archiveFiles.pathNormalize)(destination), data, options || {}, true, true);
|
248 | this._isAddingResource = false;
|
249 | }
|
250 |
|
251 | |
252 |
|
253 |
|
254 |
|
255 |
|
256 | _createHasher() {
|
257 | return new _sha.HasherSha256();
|
258 | }
|
259 |
|
260 | |
261 |
|
262 |
|
263 |
|
264 |
|
265 | _createSignature() {
|
266 | return new _signature.Signature();
|
267 | }
|
268 |
|
269 | |
270 |
|
271 |
|
272 |
|
273 |
|
274 | get _metaResourceMimetypePath() {
|
275 | return 'mimetype';
|
276 | }
|
277 |
|
278 | |
279 |
|
280 |
|
281 |
|
282 |
|
283 | get _metaResourceApplicationPath() {
|
284 | return 'META-INF/AIR/application.xml';
|
285 | }
|
286 |
|
287 | |
288 |
|
289 |
|
290 |
|
291 |
|
292 | get _metaResourceHashPath() {
|
293 | return 'META-INF/AIR/hash';
|
294 | }
|
295 |
|
296 | |
297 |
|
298 |
|
299 |
|
300 |
|
301 | get _metaResourceDebugPath() {
|
302 | return 'META-INF/AIR/debug';
|
303 | }
|
304 |
|
305 | |
306 |
|
307 |
|
308 |
|
309 |
|
310 | get _metaResourceSignaturesPath() {
|
311 | return 'META-INF/signatures.xml';
|
312 | }
|
313 |
|
314 | |
315 |
|
316 |
|
317 |
|
318 |
|
319 | async _getDescriptorData() {
|
320 | const {
|
321 | descriptorData,
|
322 | descriptorFile
|
323 | } = this;
|
324 | if (descriptorData) {
|
325 | switch (typeof descriptorData) {
|
326 | case 'function':
|
327 | {
|
328 | const d = await descriptorData();
|
329 | return typeof d === 'string' ? new TextEncoder().encode(d) : d;
|
330 | }
|
331 | case 'string':
|
332 | {
|
333 | return new TextEncoder().encode(descriptorData);
|
334 | }
|
335 | default:
|
336 | {
|
337 | return descriptorData;
|
338 | }
|
339 | }
|
340 | }
|
341 | if (descriptorFile !== null) {
|
342 | const d = await (0, _promises.readFile)(descriptorFile);
|
343 | return new Uint8Array(d.buffer, d.byteOffset, d.byteLength);
|
344 | }
|
345 | throw new Error('Missing application descriptor data');
|
346 | }
|
347 |
|
348 | |
349 |
|
350 |
|
351 |
|
352 |
|
353 | _getMimetypeData() {
|
354 |
|
355 | return new TextEncoder().encode(this.mimetype);
|
356 | }
|
357 |
|
358 | |
359 |
|
360 |
|
361 |
|
362 |
|
363 | _getKeystore() {
|
364 | const r = this.keystore;
|
365 | if (!r) {
|
366 | throw new Error('A keystore not set');
|
367 | }
|
368 | return r;
|
369 | }
|
370 |
|
371 | |
372 |
|
373 |
|
374 |
|
375 |
|
376 |
|
377 |
|
378 |
|
379 |
|
380 | async _addResource(destination, data, options, hashed, signed) {
|
381 | if (hashed) {
|
382 | this._hasher.update(data);
|
383 | }
|
384 | if (signed) {
|
385 | const signature = this._signature;
|
386 | if (signature) {
|
387 | signature.addFile(destination, data);
|
388 | }
|
389 | }
|
390 | await this._writeResource(destination, data, options);
|
391 | }
|
392 |
|
393 | |
394 |
|
395 |
|
396 |
|
397 |
|
398 | async _addMetaResourcesStart(applicationData) {
|
399 | await this._addMetaResourceMimetype();
|
400 | await this._addMetaResourceApplication(applicationData);
|
401 | await this._addMetaResourceHash();
|
402 | if (this.debug) {
|
403 | await this._addMetaResourceDebug();
|
404 | }
|
405 | }
|
406 |
|
407 | |
408 |
|
409 |
|
410 | async _addMetaResourcesEnd() {
|
411 | if (this.signed) {
|
412 | await this._addMetaResourceSignatures();
|
413 | }
|
414 | }
|
415 |
|
416 | |
417 |
|
418 |
|
419 | async _addMetaResourceMimetype() {
|
420 | const path = this._metaResourceMimetypePath;
|
421 | const data = this._getMimetypeData();
|
422 | await this._addResource(path, data, {}, true, true);
|
423 | }
|
424 |
|
425 | |
426 |
|
427 |
|
428 |
|
429 |
|
430 | async _addMetaResourceApplication(applicationData) {
|
431 | const path = this._metaResourceApplicationPath;
|
432 | await this._addResource(path, applicationData, {}, true, true);
|
433 | }
|
434 |
|
435 | |
436 |
|
437 |
|
438 | async _addMetaResourceHash() {
|
439 | const path = this._metaResourceHashPath;
|
440 | const data = new Uint8Array(this._hasher.bytes);
|
441 | await this._addResource(path, data, {}, false, false);
|
442 | }
|
443 |
|
444 | |
445 |
|
446 |
|
447 | async _addMetaResourceDebug() {
|
448 | const path = this._metaResourceDebugPath;
|
449 | const data = new Uint8Array(0);
|
450 | await this._addResource(path, data, {}, true, true);
|
451 | }
|
452 |
|
453 | |
454 |
|
455 |
|
456 | async _addMetaResourceSignatures() {
|
457 | const path = this._metaResourceSignaturesPath;
|
458 | const signature = this._signature;
|
459 | if (!signature) {
|
460 | throw new Error('Internal error');
|
461 | }
|
462 | signature.digest();
|
463 | signature.sign();
|
464 | if (signature.timestampUrl) {
|
465 | await signature.timestamp();
|
466 | }
|
467 | const data = signature.encode();
|
468 | await this._addResource(path, data, {}, false, false);
|
469 | }
|
470 |
|
471 | |
472 |
|
473 |
|
474 |
|
475 |
|
476 | _applicationInfoInit(applicationData) {
|
477 |
|
478 | }
|
479 |
|
480 | |
481 |
|
482 |
|
483 | _applicationInfoClear() {
|
484 |
|
485 | }
|
486 |
|
487 | |
488 |
|
489 |
|
490 |
|
491 |
|
492 |
|
493 | |
494 |
|
495 |
|
496 |
|
497 |
|
498 |
|
499 | |
500 |
|
501 |
|
502 |
|
503 | |
504 |
|
505 |
|
506 |
|
507 | |
508 |
|
509 |
|
510 |
|
511 |
|
512 |
|
513 |
|
514 | }
|
515 | exports.Packager = Packager;
|
516 |
|
\ | No newline at end of file |