UNPKG

5.98 kBJavaScriptView Raw
1'use strict';
2
3module.exports = Parse.create = Parse;
4
5var PullStream = require('pullstream');
6var Stream = require('stream').Stream;
7var inherits = require('util').inherits;
8var zlib = require('zlib');
9var binary = require('binary');
10var Entry = require('./entry');
11
12inherits(Parse, Stream);
13
14function Parse() {
15 var self = this;
16 if (!(this instanceof Parse)) {
17 return new Parse();
18 }
19
20 Stream.apply(this);
21
22 this.writable = true;
23 this.readable = true;
24 this._pullStream = new PullStream();
25
26 this._pullStream.on("error", function (e) {
27 self.emit('error', e);
28 });
29
30 /*this._pullStream.on("end", function () {
31 self.emit('end');
32 });*/
33
34 this._readRecord();
35}
36
37Parse.prototype._readRecord = function () {
38 var self = this;
39 this._pullStream.pull(4, function (err, data) {
40 if (err) {
41 return self.emit('error', err);
42 }
43
44 if (data.length === 0) {
45 return;
46 }
47
48 var signature = data.readUInt32LE(0);
49 if (signature === 0x04034b50) {
50 self._readFile();
51 } else if (signature === 0x02014b50) {
52 self._readCentralDirectoryFileHeader();
53 } else if (signature === 0x06054b50) {
54 self._readEndOfCentralDirectoryRecord();
55 } else {
56 err = new Error('invalid signature: 0x' + signature.toString(16) + ' (at position: 0x' + data.posInStream.toString(16) + ')');
57 self.emit('error', err);
58 }
59 });
60};
61
62Parse.prototype._readFile = function () {
63 var self = this;
64 this._pullStream.pull(26, function (err, data) {
65 if (err) {
66 return self.emit('error', err);
67 }
68
69 var vars = binary.parse(data)
70 .word16lu('versionsNeededToExtract')
71 .word16lu('flags')
72 .word16lu('compressionMethod')
73 .word16lu('lastModifiedTime')
74 .word16lu('lastModifiedDate')
75 .word32lu('crc32')
76 .word32lu('compressedSize')
77 .word32lu('uncompressedSize')
78 .word16lu('fileNameLength')
79 .word16lu('extraFieldLength')
80 .vars;
81
82 return self._pullStream.pull(vars.fileNameLength, function (err, fileName) {
83 if (err) {
84 return self.emit('error', err);
85 }
86 fileName = fileName.toString('utf8');
87 var entry = new Entry(self._pullStream);
88 entry.path = fileName;
89 entry.props.path = fileName;
90 entry.type = (vars.compressedSize === 0 && /[\/\\]$/.test(fileName)) ? 'Directory' : 'File';
91 entry.size = vars.uncompressedSize;
92
93 self.emit('entry', entry);
94
95 self._pullStream.pull(vars.extraFieldLength, function (err, extraField) {
96 if (err) {
97 return self.emit('error', err);
98 }
99 if (vars.compressionMethod === 0) {
100 self._pullStream.pull(vars.compressedSize, function (err, compressedData) {
101 if (err) {
102 return self.emit('error', err);
103 }
104
105 entry.emit('data', compressedData);
106 entry.emit('end');
107
108 return self._readRecord();
109 });
110 } else {
111 var inflater = zlib.createInflateRaw();
112 inflater.on('error', function (err) {
113 self.emit('error', err);
114 });
115 inflater.on('end', function () {
116 entry.emit('end');
117 self._readRecord();
118 });
119 inflater.on('data', function (uncompressedData) {
120 entry.emit('data', uncompressedData);
121 });
122 self._pullStream.pipe(vars.compressedSize, inflater);
123 }
124 });
125 });
126 });
127};
128
129Parse.prototype._readCentralDirectoryFileHeader = function () {
130 var self = this;
131 this._pullStream.pull(42, function (err, data) {
132 if (err) {
133 return self.emit('error', err);
134 }
135
136 var vars = binary.parse(data)
137 .word16lu('versionMadeBy')
138 .word16lu('versionsNeededToExtract')
139 .word16lu('flags')
140 .word16lu('compressionMethod')
141 .word16lu('lastModifiedTime')
142 .word16lu('lastModifiedDate')
143 .word32lu('crc32')
144 .word32lu('compressedSize')
145 .word32lu('uncompressedSize')
146 .word16lu('fileNameLength')
147 .word16lu('extraFieldLength')
148 .word16lu('fileCommentLength')
149 .word16lu('diskNumber')
150 .word16lu('internalFileAttributes')
151 .word32lu('externalFileAttributes')
152 .word32lu('offsetToLocalFileHeader')
153 .vars;
154
155 return self._pullStream.pull(vars.fileNameLength, function (err, fileName) {
156 if (err) {
157 return self.emit('error', err);
158 }
159 fileName = fileName.toString('utf8');
160
161 self._pullStream.pull(vars.extraFieldLength, function (err, extraField) {
162 if (err) {
163 return self.emit('error', err);
164 }
165 self._pullStream.pull(vars.fileCommentLength, function (err, fileComment) {
166 if (err) {
167 return self.emit('error', err);
168 }
169 return self._readRecord();
170 });
171 });
172 });
173 });
174};
175
176Parse.prototype._readEndOfCentralDirectoryRecord = function () {
177 var self = this;
178 this._pullStream.pull(18, function (err, data) {
179 if (err) {
180 return self.emit('error', err);
181 }
182
183 var vars = binary.parse(data)
184 .word16lu('diskNumber')
185 .word16lu('diskStart')
186 .word16lu('numberOfRecordsOnDisk')
187 .word16lu('numberOfRecords')
188 .word32lu('sizeOfCentralDirectory')
189 .word32lu('offsetToStartOfCentralDirectory')
190 .word16lu('commentLength')
191 .vars;
192
193 return self._pullStream.pull(vars.commentLength, function (err, comment) {
194 if (err) {
195 return self.emit('error', err);
196 }
197 comment = comment.toString('utf8');
198 self.emit('end');
199 return self.emit('close');
200 });
201 });
202};
203
204Parse.prototype.write = function (data) {
205 this._pullStream.write(data);
206};
207
208Parse.prototype.end = function (data) {
209 this._pullStream.end(data);
210};
211
212Parse.prototype.pipe = function (dest, opts) {
213 var self = this;
214 if (typeof dest.add === "function") {
215 self.on("entry", function (entry) {
216 dest.add(entry);
217 })
218 }
219 return Stream.prototype.pipe.apply(this, arguments);
220};