1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 | (function (root) {
|
12 | "use strict";
|
13 |
|
14 | var JSZip = root.JSZip;
|
15 |
|
16 | var MAX_VALUE_16BITS = 65535;
|
17 | var MAX_VALUE_32BITS = -1;
|
18 |
|
19 | |
20 |
|
21 |
|
22 |
|
23 |
|
24 | var pretty = function (str) {
|
25 | var res = '', code, i;
|
26 | for (i = 0; i < (str||"").length; i++) {
|
27 | code = str.charCodeAt(i);
|
28 | res += '\\x' + (code < 16 ? "0" : "") + code.toString(16).toUpperCase();
|
29 | }
|
30 | return res;
|
31 | };
|
32 |
|
33 | |
34 |
|
35 |
|
36 |
|
37 |
|
38 | var findCompression = function (compressionMethod) {
|
39 | for (var method in JSZip.compressions) {
|
40 | if( !JSZip.compressions.hasOwnProperty(method) ) { continue; }
|
41 | if (JSZip.compressions[method].magic === compressionMethod) {
|
42 | return JSZip.compressions[method];
|
43 | }
|
44 | }
|
45 | return null;
|
46 | };
|
47 |
|
48 |
|
49 | |
50 |
|
51 |
|
52 |
|
53 |
|
54 |
|
55 |
|
56 | function DataReader(data) {
|
57 | this.data = null;
|
58 | this.length = 0;
|
59 | this.index = 0;
|
60 | }
|
61 | DataReader.prototype = {
|
62 | |
63 |
|
64 |
|
65 |
|
66 |
|
67 | checkOffset : function (offset) {
|
68 | this.checkIndex(this.index + offset);
|
69 | },
|
70 | |
71 |
|
72 |
|
73 |
|
74 |
|
75 | checkIndex : function (newIndex) {
|
76 | if (this.length < newIndex || newIndex < 0) {
|
77 | throw new Error("End of data reached (data length = " +
|
78 | this.length + ", asked index = " +
|
79 | (newIndex) + "). Corrupted zip ?");
|
80 | }
|
81 | },
|
82 | |
83 |
|
84 |
|
85 |
|
86 |
|
87 | setIndex : function (newIndex) {
|
88 | this.checkIndex(newIndex);
|
89 | this.index = newIndex;
|
90 | },
|
91 | |
92 |
|
93 |
|
94 |
|
95 |
|
96 | skip : function (n) {
|
97 | this.setIndex(this.index + n);
|
98 | },
|
99 | |
100 |
|
101 |
|
102 |
|
103 |
|
104 | byteAt : function(i) {
|
105 |
|
106 | },
|
107 | |
108 |
|
109 |
|
110 |
|
111 |
|
112 | readInt : function (size) {
|
113 | var result = 0, i;
|
114 | this.checkOffset(size);
|
115 | for(i = this.index + size - 1; i >= this.index; i--) {
|
116 | result = (result << 8) + this.byteAt(i);
|
117 | }
|
118 | this.index += size;
|
119 | return result;
|
120 | },
|
121 | |
122 |
|
123 |
|
124 |
|
125 |
|
126 | readString : function (size) {
|
127 | return JSZip.utils.transformTo("string", this.readData(size));
|
128 | },
|
129 | |
130 |
|
131 |
|
132 |
|
133 |
|
134 | readData : function (size) {
|
135 |
|
136 | },
|
137 | |
138 |
|
139 |
|
140 |
|
141 |
|
142 | lastIndexOfSignature : function (sig) {
|
143 |
|
144 | },
|
145 | |
146 |
|
147 |
|
148 |
|
149 | readDate : function () {
|
150 | var dostime = this.readInt(4);
|
151 | return new Date(
|
152 | ((dostime >> 25) & 0x7f) + 1980,
|
153 | ((dostime >> 21) & 0x0f) - 1,
|
154 | (dostime >> 16) & 0x1f,
|
155 | (dostime >> 11) & 0x1f,
|
156 | (dostime >> 5) & 0x3f,
|
157 | (dostime & 0x1f) << 1);
|
158 | }
|
159 | };
|
160 |
|
161 |
|
162 | |
163 |
|
164 |
|
165 |
|
166 |
|
167 | function StringReader(data, optimizedBinaryString) {
|
168 | this.data = data;
|
169 | if (!optimizedBinaryString) {
|
170 | this.data = JSZip.utils.string2binary(this.data);
|
171 | }
|
172 | this.length = this.data.length;
|
173 | this.index = 0;
|
174 | }
|
175 | StringReader.prototype = new DataReader();
|
176 | |
177 |
|
178 |
|
179 | StringReader.prototype.byteAt = function(i) {
|
180 | return this.data.charCodeAt(i);
|
181 | };
|
182 | |
183 |
|
184 |
|
185 | StringReader.prototype.lastIndexOfSignature = function (sig) {
|
186 | return this.data.lastIndexOf(sig);
|
187 | };
|
188 | |
189 |
|
190 |
|
191 | StringReader.prototype.readData = function (size) {
|
192 | this.checkOffset(size);
|
193 |
|
194 | var result = this.data.slice(this.index, this.index + size);
|
195 | this.index += size;
|
196 | return result;
|
197 | };
|
198 |
|
199 |
|
200 | |
201 |
|
202 |
|
203 |
|
204 |
|
205 | function Uint8ArrayReader(data) {
|
206 | if (data) {
|
207 | this.data = data;
|
208 | this.length = this.data.length;
|
209 | this.index = 0;
|
210 | }
|
211 | }
|
212 | Uint8ArrayReader.prototype = new DataReader();
|
213 | |
214 |
|
215 |
|
216 | Uint8ArrayReader.prototype.byteAt = function(i) {
|
217 | return this.data[i];
|
218 | };
|
219 | |
220 |
|
221 |
|
222 | Uint8ArrayReader.prototype.lastIndexOfSignature = function (sig) {
|
223 | var sig0 = sig.charCodeAt(0),
|
224 | sig1 = sig.charCodeAt(1),
|
225 | sig2 = sig.charCodeAt(2),
|
226 | sig3 = sig.charCodeAt(3);
|
227 | for(var i = this.length - 4;i >= 0;--i) {
|
228 | if (this.data[i] === sig0 && this.data[i+1] === sig1 && this.data[i+2] === sig2 && this.data[i+3] === sig3) {
|
229 | return i;
|
230 | }
|
231 | }
|
232 |
|
233 | return -1;
|
234 | };
|
235 | |
236 |
|
237 |
|
238 | Uint8ArrayReader.prototype.readData = function (size) {
|
239 | this.checkOffset(size);
|
240 | var result = this.data.subarray(this.index, this.index + size);
|
241 | this.index += size;
|
242 | return result;
|
243 | };
|
244 |
|
245 | |
246 |
|
247 |
|
248 |
|
249 |
|
250 | function NodeBufferReader(data) {
|
251 | this.data = data;
|
252 | this.length = this.data.length;
|
253 | this.index = 0;
|
254 | }
|
255 | NodeBufferReader.prototype = new Uint8ArrayReader();
|
256 |
|
257 | |
258 |
|
259 |
|
260 | NodeBufferReader.prototype.readData = function (size) {
|
261 | this.checkOffset(size);
|
262 | var result = this.data.slice(this.index, this.index + size);
|
263 | this.index += size;
|
264 | return result;
|
265 | };
|
266 |
|
267 |
|
268 |
|
269 | |
270 |
|
271 |
|
272 |
|
273 |
|
274 |
|
275 | function ZipEntry(options, loadOptions) {
|
276 | this.options = options;
|
277 | this.loadOptions = loadOptions;
|
278 | }
|
279 | ZipEntry.prototype = {
|
280 | |
281 |
|
282 |
|
283 |
|
284 | isEncrypted : function () {
|
285 |
|
286 | return (this.bitFlag & 0x0001) === 0x0001;
|
287 | },
|
288 | |
289 |
|
290 |
|
291 |
|
292 | useUTF8 : function () {
|
293 |
|
294 | return (this.bitFlag & 0x0800) === 0x0800;
|
295 | },
|
296 | |
297 |
|
298 |
|
299 |
|
300 |
|
301 |
|
302 |
|
303 | prepareCompressedContent : function (reader, from, length) {
|
304 | return function () {
|
305 | var previousIndex = reader.index;
|
306 | reader.setIndex(from);
|
307 | var compressedFileData = reader.readData(length);
|
308 | reader.setIndex(previousIndex);
|
309 |
|
310 | return compressedFileData;
|
311 | };
|
312 | },
|
313 | |
314 |
|
315 |
|
316 |
|
317 |
|
318 |
|
319 |
|
320 |
|
321 |
|
322 | prepareContent : function (reader, from, length, compression, uncompressedSize) {
|
323 | return function () {
|
324 |
|
325 | var compressedFileData = JSZip.utils.transformTo(compression.uncompressInputType, this.getCompressedContent());
|
326 | var uncompressedFileData = compression.uncompress(compressedFileData);
|
327 |
|
328 | if (uncompressedFileData.length !== uncompressedSize) {
|
329 | throw new Error("Bug : uncompressed data size mismatch");
|
330 | }
|
331 |
|
332 | return uncompressedFileData;
|
333 | };
|
334 | },
|
335 | |
336 |
|
337 |
|
338 |
|
339 | readLocalPart : function(reader) {
|
340 | var compression, localExtraFieldsLength;
|
341 |
|
342 |
|
343 |
|
344 |
|
345 |
|
346 |
|
347 | reader.skip(22);
|
348 |
|
349 |
|
350 |
|
351 |
|
352 |
|
353 |
|
354 |
|
355 |
|
356 |
|
357 |
|
358 |
|
359 | this.fileNameLength = reader.readInt(2);
|
360 | localExtraFieldsLength = reader.readInt(2);
|
361 | this.fileName = reader.readString(this.fileNameLength);
|
362 | reader.skip(localExtraFieldsLength);
|
363 |
|
364 | if (this.compressedSize == -1 || this.uncompressedSize == -1) {
|
365 | throw new Error("Bug or corrupted zip : didn't get enough informations from the central directory " +
|
366 | "(compressedSize == -1 || uncompressedSize == -1)");
|
367 | }
|
368 |
|
369 | compression = findCompression(this.compressionMethod);
|
370 | if (compression === null) {
|
371 | throw new Error("Corrupted zip : compression " + pretty(this.compressionMethod) +
|
372 | " unknown (inner file : " + this.fileName + ")");
|
373 | }
|
374 | this.decompressed = new JSZip.CompressedObject();
|
375 | this.decompressed.compressedSize = this.compressedSize;
|
376 | this.decompressed.uncompressedSize = this.uncompressedSize;
|
377 | this.decompressed.crc32 = this.crc32;
|
378 | this.decompressed.compressionMethod = this.compressionMethod;
|
379 | this.decompressed.getCompressedContent = this.prepareCompressedContent(reader, reader.index, this.compressedSize, compression);
|
380 | this.decompressed.getContent = this.prepareContent(reader, reader.index, this.compressedSize, compression, this.uncompressedSize);
|
381 |
|
382 |
|
383 | if (this.loadOptions.checkCRC32) {
|
384 | this.decompressed = JSZip.utils.transformTo("string", this.decompressed.getContent());
|
385 | if (JSZip.prototype.crc32(this.decompressed) !== this.crc32) {
|
386 | throw new Error("Corrupted zip : CRC32 mismatch");
|
387 | }
|
388 | }
|
389 | },
|
390 |
|
391 | |
392 |
|
393 |
|
394 |
|
395 | readCentralPart : function(reader) {
|
396 | this.versionMadeBy = reader.readString(2);
|
397 | this.versionNeeded = reader.readInt(2);
|
398 | this.bitFlag = reader.readInt(2);
|
399 | this.compressionMethod = reader.readString(2);
|
400 | this.date = reader.readDate();
|
401 | this.crc32 = reader.readInt(4);
|
402 | this.compressedSize = reader.readInt(4);
|
403 | this.uncompressedSize = reader.readInt(4);
|
404 | this.fileNameLength = reader.readInt(2);
|
405 | this.extraFieldsLength = reader.readInt(2);
|
406 | this.fileCommentLength = reader.readInt(2);
|
407 | this.diskNumberStart = reader.readInt(2);
|
408 | this.internalFileAttributes = reader.readInt(2);
|
409 | this.externalFileAttributes = reader.readInt(4);
|
410 | this.localHeaderOffset = reader.readInt(4);
|
411 |
|
412 | if (this.isEncrypted()) {
|
413 | throw new Error("Encrypted zip are not supported");
|
414 | }
|
415 |
|
416 | this.fileName = reader.readString(this.fileNameLength);
|
417 | this.readExtraFields(reader);
|
418 | this.parseZIP64ExtraField(reader);
|
419 | this.fileComment = reader.readString(this.fileCommentLength);
|
420 |
|
421 |
|
422 | this.dir = this.externalFileAttributes & 0x00000010 ? true : false;
|
423 | },
|
424 | |
425 |
|
426 |
|
427 |
|
428 | parseZIP64ExtraField : function(reader) {
|
429 |
|
430 | if(!this.extraFields[0x0001]) {
|
431 | return;
|
432 | }
|
433 |
|
434 |
|
435 | var extraReader = new StringReader(this.extraFields[0x0001].value);
|
436 |
|
437 |
|
438 |
|
439 | if(this.uncompressedSize === MAX_VALUE_32BITS) {
|
440 | this.uncompressedSize = extraReader.readInt(8);
|
441 | }
|
442 | if(this.compressedSize === MAX_VALUE_32BITS) {
|
443 | this.compressedSize = extraReader.readInt(8);
|
444 | }
|
445 | if(this.localHeaderOffset === MAX_VALUE_32BITS) {
|
446 | this.localHeaderOffset = extraReader.readInt(8);
|
447 | }
|
448 | if(this.diskNumberStart === MAX_VALUE_32BITS) {
|
449 | this.diskNumberStart = extraReader.readInt(4);
|
450 | }
|
451 | },
|
452 | |
453 |
|
454 |
|
455 |
|
456 | readExtraFields : function(reader) {
|
457 | var start = reader.index,
|
458 | extraFieldId,
|
459 | extraFieldLength,
|
460 | extraFieldValue;
|
461 |
|
462 | this.extraFields = this.extraFields || {};
|
463 |
|
464 | while (reader.index < start + this.extraFieldsLength) {
|
465 | extraFieldId = reader.readInt(2);
|
466 | extraFieldLength = reader.readInt(2);
|
467 | extraFieldValue = reader.readString(extraFieldLength);
|
468 |
|
469 | this.extraFields[extraFieldId] = {
|
470 | id: extraFieldId,
|
471 | length: extraFieldLength,
|
472 | value: extraFieldValue
|
473 | };
|
474 | }
|
475 | },
|
476 | |
477 |
|
478 |
|
479 | handleUTF8 : function() {
|
480 | if (this.useUTF8()) {
|
481 | this.fileName = JSZip.prototype.utf8decode(this.fileName);
|
482 | this.fileComment = JSZip.prototype.utf8decode(this.fileComment);
|
483 | }
|
484 | }
|
485 | };
|
486 |
|
487 |
|
488 |
|
489 | |
490 |
|
491 |
|
492 |
|
493 |
|
494 |
|
495 | function ZipEntries(data, loadOptions) {
|
496 | this.files = [];
|
497 | this.loadOptions = loadOptions;
|
498 | if (data) {
|
499 | this.load(data);
|
500 | }
|
501 | }
|
502 | ZipEntries.prototype = {
|
503 | |
504 |
|
505 |
|
506 |
|
507 |
|
508 | checkSignature : function(expectedSignature) {
|
509 | var signature = this.reader.readString(4);
|
510 | if (signature !== expectedSignature) {
|
511 | throw new Error("Corrupted zip or bug : unexpected signature " +
|
512 | "(" + pretty(signature) + ", expected " + pretty(expectedSignature) + ")");
|
513 | }
|
514 | },
|
515 | |
516 |
|
517 |
|
518 | readBlockEndOfCentral : function () {
|
519 | this.diskNumber = this.reader.readInt(2);
|
520 | this.diskWithCentralDirStart = this.reader.readInt(2);
|
521 | this.centralDirRecordsOnThisDisk = this.reader.readInt(2);
|
522 | this.centralDirRecords = this.reader.readInt(2);
|
523 | this.centralDirSize = this.reader.readInt(4);
|
524 | this.centralDirOffset = this.reader.readInt(4);
|
525 |
|
526 | this.zipCommentLength = this.reader.readInt(2);
|
527 | this.zipComment = this.reader.readString(this.zipCommentLength);
|
528 | },
|
529 | |
530 |
|
531 |
|
532 |
|
533 |
|
534 |
|
535 | readBlockZip64EndOfCentral : function () {
|
536 | this.zip64EndOfCentralSize = this.reader.readInt(8);
|
537 | this.versionMadeBy = this.reader.readString(2);
|
538 | this.versionNeeded = this.reader.readInt(2);
|
539 | this.diskNumber = this.reader.readInt(4);
|
540 | this.diskWithCentralDirStart = this.reader.readInt(4);
|
541 | this.centralDirRecordsOnThisDisk = this.reader.readInt(8);
|
542 | this.centralDirRecords = this.reader.readInt(8);
|
543 | this.centralDirSize = this.reader.readInt(8);
|
544 | this.centralDirOffset = this.reader.readInt(8);
|
545 |
|
546 | this.zip64ExtensibleData = {};
|
547 | var extraDataSize = this.zip64EndOfCentralSize - 44,
|
548 | index = 0,
|
549 | extraFieldId,
|
550 | extraFieldLength,
|
551 | extraFieldValue;
|
552 | while(index < extraDataSize) {
|
553 | extraFieldId = this.reader.readInt(2);
|
554 | extraFieldLength = this.reader.readInt(4);
|
555 | extraFieldValue = this.reader.readString(extraFieldLength);
|
556 | this.zip64ExtensibleData[extraFieldId] = {
|
557 | id: extraFieldId,
|
558 | length: extraFieldLength,
|
559 | value: extraFieldValue
|
560 | };
|
561 | }
|
562 | },
|
563 | |
564 |
|
565 |
|
566 | readBlockZip64EndOfCentralLocator : function () {
|
567 | this.diskWithZip64CentralDirStart = this.reader.readInt(4);
|
568 | this.relativeOffsetEndOfZip64CentralDir = this.reader.readInt(8);
|
569 | this.disksCount = this.reader.readInt(4);
|
570 | if (this.disksCount > 1) {
|
571 | throw new Error("Multi-volumes zip are not supported");
|
572 | }
|
573 | },
|
574 | |
575 |
|
576 |
|
577 | readLocalFiles : function() {
|
578 | var i, file;
|
579 | for(i = 0; i < this.files.length; i++) {
|
580 | file = this.files[i];
|
581 | this.reader.setIndex(file.localHeaderOffset);
|
582 | this.checkSignature(JSZip.signature.LOCAL_FILE_HEADER);
|
583 | file.readLocalPart(this.reader);
|
584 | file.handleUTF8();
|
585 | }
|
586 | },
|
587 | |
588 |
|
589 |
|
590 | readCentralDir : function() {
|
591 | var file;
|
592 |
|
593 | this.reader.setIndex(this.centralDirOffset);
|
594 | while(this.reader.readString(4) === JSZip.signature.CENTRAL_FILE_HEADER) {
|
595 | file = new ZipEntry({
|
596 | zip64: this.zip64
|
597 | }, this.loadOptions);
|
598 | file.readCentralPart(this.reader);
|
599 | this.files.push(file);
|
600 | }
|
601 | },
|
602 | |
603 |
|
604 |
|
605 | readEndOfCentral : function() {
|
606 | var offset = this.reader.lastIndexOfSignature(JSZip.signature.CENTRAL_DIRECTORY_END);
|
607 | if (offset === -1) {
|
608 | throw new Error("Corrupted zip : can't find end of central directory");
|
609 | }
|
610 | this.reader.setIndex(offset);
|
611 | this.checkSignature(JSZip.signature.CENTRAL_DIRECTORY_END);
|
612 | this.readBlockEndOfCentral();
|
613 |
|
614 |
|
615 | |
616 |
|
617 |
|
618 |
|
619 |
|
620 |
|
621 |
|
622 |
|
623 |
|
624 |
|
625 | if (this.diskNumber === MAX_VALUE_16BITS ||
|
626 | this.diskWithCentralDirStart === MAX_VALUE_16BITS ||
|
627 | this.centralDirRecordsOnThisDisk === MAX_VALUE_16BITS ||
|
628 | this.centralDirRecords === MAX_VALUE_16BITS ||
|
629 | this.centralDirSize === MAX_VALUE_32BITS ||
|
630 | this.centralDirOffset === MAX_VALUE_32BITS
|
631 | ) {
|
632 | this.zip64 = true;
|
633 |
|
634 | |
635 |
|
636 |
|
637 |
|
638 |
|
639 |
|
640 |
|
641 |
|
642 |
|
643 |
|
644 | offset = this.reader.lastIndexOfSignature(JSZip.signature.ZIP64_CENTRAL_DIRECTORY_LOCATOR);
|
645 | if (offset === -1) {
|
646 | throw new Error("Corrupted zip : can't find the ZIP64 end of central directory locator");
|
647 | }
|
648 | this.reader.setIndex(offset);
|
649 | this.checkSignature(JSZip.signature.ZIP64_CENTRAL_DIRECTORY_LOCATOR);
|
650 | this.readBlockZip64EndOfCentralLocator();
|
651 |
|
652 |
|
653 | this.reader.setIndex(this.relativeOffsetEndOfZip64CentralDir);
|
654 | this.checkSignature(JSZip.signature.ZIP64_CENTRAL_DIRECTORY_END);
|
655 | this.readBlockZip64EndOfCentral();
|
656 | }
|
657 | },
|
658 | prepareReader : function (data) {
|
659 | var type = JSZip.utils.getTypeOf(data);
|
660 | if (type === "string" && !JSZip.support.uint8array) {
|
661 | this.reader = new StringReader(data, this.loadOptions.optimizedBinaryString);
|
662 | } else if (type === "nodebuffer") {
|
663 | this.reader = new NodeBufferReader(data);
|
664 | } else {
|
665 | this.reader = new Uint8ArrayReader(JSZip.utils.transformTo("uint8array", data));
|
666 | }
|
667 | },
|
668 | |
669 |
|
670 |
|
671 |
|
672 | load : function(data) {
|
673 | this.prepareReader(data);
|
674 | this.readEndOfCentral();
|
675 | this.readCentralDir();
|
676 | this.readLocalFiles();
|
677 | }
|
678 | };
|
679 |
|
680 |
|
681 | |
682 |
|
683 |
|
684 |
|
685 |
|
686 |
|
687 |
|
688 | JSZip.prototype.load = function(data, options) {
|
689 | var files, zipEntries, i, input;
|
690 | options = options || {};
|
691 | if(options.base64) {
|
692 | data = JSZip.base64.decode(data);
|
693 | }
|
694 |
|
695 | zipEntries = new ZipEntries(data, options);
|
696 | files = zipEntries.files;
|
697 | for (i = 0; i < files.length; i++) {
|
698 | input = files[i];
|
699 | this.file(input.fileName, input.decompressed, {
|
700 | binary:true,
|
701 | optimizedBinaryString:true,
|
702 | date:input.date,
|
703 | dir:input.dir
|
704 | });
|
705 | }
|
706 |
|
707 | return this;
|
708 | };
|
709 |
|
710 | }(this));
|
711 |
|
712 |
|