123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229 |
- 'use strict';
- var binary = require('binary');
- var convertDateTime = function (dosDate, dosTime) {
- var year = ((dosDate >> 9) & 0x7F) + 1980;
- var month = (dosDate >> 5) & 0x0F;
- var day = dosDate & 0x1F;
- var hour = (dosTime >> 11);
- var minute = (dosTime >> 5) & 0x3F;
- var second = (dosTime & 0x1F) * 2;
- var result = new Date(year, month - 1, day, hour, minute, second, 0);
- return result;
- };
- var convertGeneralPurposeFlags = function (value) {
- var bits = [];
- for (var i = 0; i < 16; i++) {
- bits[i] = (value >> i) & 1;
- }
- return {
- encrypted: !!bits[0],
- compressionFlag1: !!bits[1],
- compressionFlag2: !!bits[2],
- useDataDescriptor: !!bits[3],
- enhancedDeflating: !!bits[4],
- compressedPatched: !!bits[5],
- strongEncryption: !!bits[6],
- utf8: !!bits[11],
- encryptedCD: !!bits[13]
- };
- };
- var parseExternalFileAttributes = function (externalAttributes, platform) {
- var types = {
- // In theory, any of these could be set. Realistically, though, it will
- // be regular, directory or symlink
- 1: 'NamedPipe',
- 2: 'Character',
- 4: 'Directory',
- 6: 'Block',
- 8: 'File',
- 10: 'SymbolicLink',
- 12: 'Socket'
- };
- switch (platform) {
- case 3: // Unix
- return {
- platform: 'Unix',
- type: types[(externalAttributes >> 28) & 0x0F],
- mode: (externalAttributes >> 16) & 0xFFF
- };
- // case 0: // MSDOS
- default:
- if (platform !== 0) {
- console.warn('Possibly unsupported ZIP platform type, ' + platform);
- }
- var attribs = {
- A: (externalAttributes >> 5) & 0x01,
- D: (externalAttributes >> 4) & 0x01,
- V: (externalAttributes >> 3) & 0x01,
- S: (externalAttributes >> 2) & 0x01,
- H: (externalAttributes >> 1) & 0x01,
- R: externalAttributes & 0x01
- };
- // With no better guidance we'll make the default permissions ugo+r
- var mode = parseInt('0444', 8);
- if (attribs.D) {
- mode |= parseInt('0111', 8); // Set the execute bit
- }
- if (!attribs.R) {
- mode |= parseInt('0222', 8); // Set the write bit
- }
- mode &= ~process.umask();
- return {
- platform: 'DOS',
- type: attribs.D ? 'Directory' : 'File',
- mode: mode
- };
- }
- };
- var readEndRecord = function (buffer) {
- var data = binary.parse(buffer)
- .word32lu('signature')
- .word16lu('diskNumber')
- .word16lu('directoryStartDisk')
- .word16lu('directoryEntryCountDisk')
- .word16lu('directoryEntryCount')
- .word32lu('directorySize')
- .word32lu('directoryOffset')
- .word16lu('commentLength')
- .buffer('comment', 'commentLength')
- .vars;
- data.comment = data.comment.toString();
- return data;
- };
- var directorySort = function (a, b) {
- return a.relativeOffsetOfLocalHeader - b.relativeOffsetOfLocalHeader;
- };
- var readDirectory = function (buffer) {
- var directory = [];
- var current;
- var index = 0;
- while (index < buffer.length) {
- current = binary.parse(buffer.slice(index, index + 46))
- .word32lu('signature')
- .word8lu('creatorSpecVersion')
- .word8lu('creatorPlatform')
- .word8lu('requiredSpecVersion')
- .word8lu('requiredPlatform')
- .word16lu('generalPurposeBitFlag')
- .word16lu('compressionMethod')
- .word16lu('lastModFileTime')
- .word16lu('lastModFileDate')
- .word32lu('crc32')
- .word32lu('compressedSize')
- .word32lu('uncompressedSize')
- .word16lu('fileNameLength')
- .word16lu('extraFieldLength')
- .word16lu('fileCommentLength')
- .word16lu('diskNumberStart')
- .word16lu('internalFileAttributes')
- .word32lu('externalFileAttributes')
- .word32lu('relativeOffsetOfLocalHeader')
- .vars;
- index += 46;
- current.generalPurposeFlags = convertGeneralPurposeFlags(current.generalPurposeBitFlag);
- current.fileAttributes = parseExternalFileAttributes(current.externalFileAttributes, current.creatorPlatform);
- current.modifiedTime = convertDateTime(current.lastModFileDate, current.lastModFileTime);
- current.fileName = current.extraField = current.fileComment = '';
- current.headerLength = 46 + current.fileNameLength + current.extraFieldLength + current.fileCommentLength;
- if (current.fileNameLength > 0) {
- current.fileName = buffer.slice(index, index + current.fileNameLength).toString();
- index += current.fileNameLength;
- }
- if (current.extraFieldLength > 0) {
- current.extraField = buffer.slice(index, index + current.extraFieldLength).toString();
- index += current.extraFieldLength;
- }
- if (current.fileCommentLength > 0) {
- current.fileComment = buffer.slice(index, index + current.fileCommentLength).toString();
- index += current.fileCommentLength;
- }
- if (current.fileAttributes.type !== 'Directory' && current.fileName.substr(-1) === '/') {
- // TODO: check that this is a reasonable check
- current.fileAttributes.type = 'Directory';
- }
- directory.push(current);
- }
- directory.sort(directorySort);
- return directory;
- };
- var readFileEntry = function (buffer) {
- var index = 0;
- var fileEntry = binary.parse(buffer.slice(index, 30))
- .word32lu('signature')
- .word16lu('versionNeededToExtract')
- .word16lu('generalPurposeBitFlag')
- .word16lu('compressionMethod')
- .word16lu('lastModFileTime')
- .word16lu('lastModFileDate')
- .word32lu('crc32')
- .word32lu('compressedSize')
- .word32lu('uncompressedSize')
- .word16lu('fileNameLength')
- .word16lu('extraFieldLength')
- .vars;
- index += 30;
- fileEntry.fileName = fileEntry.extraField = '';
- fileEntry.entryLength = 30 + fileEntry.fileNameLength + fileEntry.extraFieldLength;
- if (fileEntry.entryLength > structures.maxFileEntrySize) {
- throw new Error('File entry unexpectedly large: ' + fileEntry.entryLength + ' (max: ' + structures.maxFileEntrySize + ')');
- }
- if (fileEntry.fileNameLength > 0) {
- fileEntry.fileName = buffer.slice(index, index + fileEntry.fileNameLength).toString();
- index += fileEntry.fileNameLength;
- }
- if (fileEntry.extraFieldLength > 0) {
- fileEntry.extraField = buffer.slice(index, index + fileEntry.extraFieldLength).toString();
- index += fileEntry.extraFieldLength;
- }
- return fileEntry;
- };
- var structures = module.exports = {
- readEndRecord: readEndRecord,
- readDirectory: readDirectory,
- readFileEntry: readFileEntry,
- maxFileEntrySize: 4096
- };
|