pst-extractor 1.8.0 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/ColumnDescriptor.class.d.ts +26 -26
  2. package/dist/ColumnDescriptor.class.js +51 -48
  3. package/dist/DescriptorIndexNode.class.d.ts +25 -26
  4. package/dist/DescriptorIndexNode.class.js +53 -53
  5. package/dist/LZFu.class.d.ts +11 -12
  6. package/dist/LZFu.class.js +95 -95
  7. package/dist/NodeInfo.class.d.ts +33 -33
  8. package/dist/NodeInfo.class.js +52 -52
  9. package/dist/NodeMap.class.d.ts +35 -35
  10. package/dist/NodeMap.class.js +86 -83
  11. package/dist/OffsetIndexItem.class.d.ts +23 -24
  12. package/dist/OffsetIndexItem.class.js +45 -45
  13. package/dist/OutlookProperties.d.ts +275 -275
  14. package/dist/OutlookProperties.js +281 -281
  15. package/dist/PSTActivity.class.d.ts +103 -103
  16. package/dist/PSTActivity.class.js +144 -144
  17. package/dist/PSTAppointment.class.d.ts +270 -271
  18. package/dist/PSTAppointment.class.js +376 -376
  19. package/dist/PSTAttachment.class.d.ts +172 -172
  20. package/dist/PSTAttachment.class.js +317 -314
  21. package/dist/PSTContact.class.d.ts +884 -884
  22. package/dist/PSTContact.class.js +1227 -1227
  23. package/dist/PSTDescriptorItem.class.d.ts +45 -46
  24. package/dist/PSTDescriptorItem.class.js +99 -96
  25. package/dist/PSTFile.class.d.ts +215 -216
  26. package/dist/PSTFile.class.js +818 -792
  27. package/dist/PSTFolder.class.d.ts +129 -129
  28. package/dist/PSTFolder.class.js +318 -307
  29. package/dist/PSTMessage.class.d.ts +788 -789
  30. package/dist/PSTMessage.class.js +1321 -1318
  31. package/dist/PSTMessageStore.class.d.ts +13 -13
  32. package/dist/PSTMessageStore.class.js +17 -17
  33. package/dist/PSTNodeInputStream.class.d.ts +122 -123
  34. package/dist/PSTNodeInputStream.class.js +514 -488
  35. package/dist/PSTObject.class.d.ts +133 -134
  36. package/dist/PSTObject.class.js +326 -323
  37. package/dist/PSTRecipient.class.d.ts +65 -65
  38. package/dist/PSTRecipient.class.js +103 -103
  39. package/dist/PSTTable.class.d.ts +52 -52
  40. package/dist/PSTTable.class.js +175 -172
  41. package/dist/PSTTable7C.class.d.ts +45 -45
  42. package/dist/PSTTable7C.class.js +281 -278
  43. package/dist/PSTTableBC.class.d.ts +31 -31
  44. package/dist/PSTTableBC.class.js +111 -108
  45. package/dist/PSTTableItem.class.d.ts +47 -48
  46. package/dist/PSTTableItem.class.js +124 -121
  47. package/dist/PSTTask.class.d.ts +146 -146
  48. package/dist/PSTTask.class.js +205 -205
  49. package/dist/PSTUtil.class.d.ts +134 -135
  50. package/dist/PSTUtil.class.js +795 -790
  51. package/dist/RecurrencePattern.class.d.ts +49 -50
  52. package/dist/RecurrencePattern.class.js +120 -120
  53. package/dist/index.d.ts +6 -6
  54. package/dist/index.js +15 -15
  55. package/example/package.json +7 -7
  56. package/example/test-min.ts +31 -12
  57. package/example/{test-mem.ts → test.ts} +38 -30
  58. package/example/testdata/output.txt +278 -0
  59. package/example/testdata/outputBody.txt +3404 -0
  60. package/example/yarn.lock +112 -50
  61. package/junit.xml +36 -36
  62. package/package.json +28 -27
  63. package/readme.md +1 -3
  64. package/example/test-max.ts +0 -251
@@ -1,488 +1,514 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PSTNodeInputStream = void 0;
4
- /* eslint-disable @typescript-eslint/no-explicit-any */
5
- const long = require("long");
6
- const zlib = require("zlib");
7
- const PSTDescriptorItem_class_1 = require("./PSTDescriptorItem.class");
8
- const PSTUtil_class_1 = require("./PSTUtil.class");
9
- const OffsetIndexItem_class_1 = require("./OffsetIndexItem.class");
10
- const PSTFile_class_1 = require("./PSTFile.class");
11
- class PSTNodeInputStream {
12
- constructor(pstFile, arg, encrypted) {
13
- this.skipPoints = [];
14
- this.indexItems = [];
15
- this.currentBlock = 0;
16
- this.allData = null;
17
- this.isZlib = false;
18
- this._currentLocation = long.ZERO;
19
- this._length = long.ZERO;
20
- this._encrypted = false;
21
- this.totalLoopCount = 0;
22
- this._pstFile = pstFile;
23
- if (arg instanceof OffsetIndexItem_class_1.OffsetIndexItem) {
24
- this._encrypted =
25
- pstFile.encryptionType == PSTFile_class_1.PSTFile.ENCRYPTION_TYPE_COMPRESSIBLE;
26
- this.loadFromOffsetItem(arg);
27
- }
28
- else if (arg instanceof PSTDescriptorItem_class_1.PSTDescriptorItem) {
29
- this._encrypted =
30
- pstFile.encryptionType == PSTFile_class_1.PSTFile.ENCRYPTION_TYPE_COMPRESSIBLE;
31
- // we want to get the first block of data and see what we are dealing with
32
- this.loadFromOffsetItem(pstFile.getOffsetIndexNode(long.fromNumber(arg.offsetIndexIdentifier)));
33
- }
34
- else if (arg instanceof Buffer) {
35
- this.allData = arg;
36
- this._length = long.fromNumber(this.allData.length);
37
- if (encrypted != undefined) {
38
- this._encrypted = encrypted;
39
- }
40
- else {
41
- this._encrypted =
42
- pstFile.encryptionType == PSTFile_class_1.PSTFile.ENCRYPTION_TYPE_COMPRESSIBLE;
43
- }
44
- }
45
- this.currentBlock = 0;
46
- this.currentLocation = long.ZERO;
47
- this.detectZlib();
48
- }
49
- get currentLocation() {
50
- return this._currentLocation;
51
- }
52
- set currentLocation(loc) {
53
- this._currentLocation = loc;
54
- }
55
- get pstFile() {
56
- return this._pstFile;
57
- }
58
- get length() {
59
- return this._length;
60
- }
61
- get encrypted() {
62
- return this._encrypted;
63
- }
64
- /**
65
- * Checks if the node is ZL compressed and unzips if so.
66
- * @private
67
- * @returns
68
- * @memberof PSTNodeInputStream
69
- */
70
- detectZlib() {
71
- // not really sure how this is meant to work, kind of going by feel here.
72
- if (this.length.lt(4)) {
73
- return;
74
- }
75
- try {
76
- if (this.read() === 0x78 && this.read() === 0x9c) {
77
- let multiStreams = false;
78
- if (this.indexItems.length > 1) {
79
- const i = this.indexItems[1];
80
- this.pstFile.seek(i.fileOffset);
81
- multiStreams =
82
- this.pstFile.read() === 0x78 && this.pstFile.read() === 0x9c;
83
- }
84
- // we are a compressed block, decompress the whole thing into a buffer
85
- // and replace our contents with that. firstly, if we have blocks, use that as the length
86
- let outputStream = null;
87
- if (multiStreams) {
88
- // debugger
89
- // console.log('list of all index items')
90
- // for (let i of this.indexItems) {
91
- // console.log(i.toJSON());
92
- // }
93
- // console.log('----------------------')
94
- for (const i of this.indexItems) {
95
- const inData = Buffer.alloc(i.size);
96
- this.pstFile.seek(i.fileOffset);
97
- this.pstFile.readCompletely(inData);
98
- const buf = zlib.unzipSync(inData);
99
- outputStream = outputStream ? Buffer.concat([outputStream, buf]) : buf;
100
- }
101
- this.indexItems = [];
102
- this.skipPoints = [];
103
- }
104
- else {
105
- let compressedLength = this.length.toNumber();
106
- if (this.indexItems.length > 0) {
107
- compressedLength = 0;
108
- for (const i of this.indexItems) {
109
- compressedLength += i.size;
110
- }
111
- }
112
- const inData = Buffer.alloc(compressedLength);
113
- this.seek(long.ZERO);
114
- this.readCompletely(inData);
115
- outputStream = zlib.unzipSync(inData);
116
- }
117
- this.allData = outputStream;
118
- this.currentLocation = long.ZERO;
119
- this.currentBlock = 0;
120
- this._length = this.allData
121
- ? long.fromNumber(this.allData.length)
122
- : long.ZERO;
123
- }
124
- this.seek(long.ZERO);
125
- }
126
- catch (err) {
127
- console.error('PSTNodeInputStream::detectZlib Unable to decompress reportedly compressed block\n' +
128
- err);
129
- throw err;
130
- }
131
- }
132
- /**
133
- * Load data from offset in file.
134
- * @private
135
- * @param {OffsetIndexItem} offsetItem
136
- * @returns
137
- * @memberof PSTNodeInputStream
138
- */
139
- loadFromOffsetItem(offsetItem) {
140
- let bInternal = (offsetItem.indexIdentifier.toNumber() & 0x02) != 0;
141
- const data = Buffer.alloc(offsetItem.size);
142
- this.pstFile.seek(offsetItem.fileOffset);
143
- this.pstFile.readCompletely(data);
144
- if (bInternal) {
145
- // All internal blocks are at least 8 bytes long...
146
- if (offsetItem.size < 8) {
147
- throw new Error('PSTNodeInputStream::loadFromOffsetItem Invalid internal block size');
148
- }
149
- if (data[0] == 0x1) {
150
- bInternal = false;
151
- // we are a xblock, or xxblock
152
- this._length = PSTUtil_class_1.PSTUtil.convertLittleEndianBytesToLong(data, 4, 8);
153
- // go through all of the blocks and create skip points.
154
- this.getBlockSkipPoints(data);
155
- return;
156
- }
157
- }
158
- // (Internal blocks aren't compressed)
159
- if (bInternal) {
160
- this._encrypted = false;
161
- }
162
- this.allData = data;
163
- this._length = long.fromValue(this.allData.length);
164
- }
165
- /**
166
- * Get block skip points in file.
167
- * @private
168
- * @param {Buffer} data
169
- * @memberof PSTNodeInputStream
170
- */
171
- getBlockSkipPoints(data) {
172
- if (data[0] != 0x1) {
173
- throw new Error('PSTNodeInputStream::loadFromOffsetItem Unable to process XBlock, incorrect identifier');
174
- }
175
- const numberOfEntries = PSTUtil_class_1.PSTUtil.convertLittleEndianBytesToLong(data, 2, 4).toNumber();
176
- let arraySize = 8;
177
- if (this.pstFile.pstFileType == PSTFile_class_1.PSTFile.PST_TYPE_ANSI) {
178
- arraySize = 4;
179
- }
180
- if (data[1] == 0x2) {
181
- // XXBlock
182
- let offset = 8;
183
- for (let x = 0; x < numberOfEntries; x++) {
184
- let bid = PSTUtil_class_1.PSTUtil.convertLittleEndianBytesToLong(data, offset, offset + arraySize);
185
- bid = bid.and(0xfffffffe);
186
- // get the details in this block and
187
- const offsetItem = this.pstFile.getOffsetIndexNode(bid);
188
- const blockData = Buffer.alloc(offsetItem.size);
189
- this.pstFile.seek(offsetItem.fileOffset);
190
- this.pstFile.readCompletely(blockData);
191
- // recurse
192
- this.getBlockSkipPoints(blockData);
193
- offset += arraySize;
194
- }
195
- }
196
- else if (data[1] == 0x1) {
197
- // normal XBlock
198
- let offset = 8;
199
- for (let x = 0; x < numberOfEntries; x++) {
200
- let bid = PSTUtil_class_1.PSTUtil.convertLittleEndianBytesToLong(data, offset, offset + arraySize);
201
- bid = bid.and(0xfffffffe);
202
- // get the details in this block and add it to the list
203
- const offsetItem = this.pstFile.getOffsetIndexNode(bid);
204
- this.indexItems.push(offsetItem);
205
- this.skipPoints.push(long.fromValue(this.currentLocation));
206
- this.currentLocation = this.currentLocation.add(offsetItem.size);
207
- offset += arraySize;
208
- }
209
- }
210
- }
211
- /**
212
- * Read from the stream.
213
- * @param {Buffer} [output]
214
- * @returns
215
- * @memberof PSTNodeInputStream
216
- */
217
- read(output) {
218
- if (!output) {
219
- return this.readSingleByte();
220
- }
221
- else {
222
- return this.readBlock(output);
223
- }
224
- }
225
- /**
226
- * Read a single byte from the input stream.
227
- * @returns {number}
228
- * @memberof PSTNodeInputStream
229
- */
230
- readSingleByte() {
231
- // first deal with items < 8K and we have all the data already
232
- if (this.allData != null) {
233
- if (this.currentLocation == this.length) {
234
- // EOF
235
- return -1;
236
- }
237
- let value = this.allData[this.currentLocation.toNumber()] & 0xff;
238
- this.currentLocation = this.currentLocation.add(1);
239
- if (this.encrypted) {
240
- value = PSTUtil_class_1.PSTUtil.compEnc[value];
241
- }
242
- return value;
243
- }
244
- let item = this.indexItems[this.currentBlock];
245
- let skipPoint = this.skipPoints[this.currentBlock];
246
- if (this.currentLocation.add(1).greaterThan(skipPoint.add(item.size))) {
247
- // got to move to the next block
248
- this.currentBlock++;
249
- if (this.currentBlock >= this.indexItems.length) {
250
- return -1;
251
- }
252
- item = this.indexItems[this.currentBlock];
253
- skipPoint = this.skipPoints[this.currentBlock];
254
- }
255
- // get the next byte.
256
- const pos = item.fileOffset.add(this.currentLocation).subtract(skipPoint);
257
- this.pstFile.seek(pos);
258
- let output = this.pstFile.read();
259
- if (output < 0) {
260
- return -1;
261
- }
262
- if (this.encrypted) {
263
- output = PSTUtil_class_1.PSTUtil.compEnc[output];
264
- }
265
- this.currentLocation = this.currentLocation.add(1);
266
- return output;
267
- }
268
- /**
269
- * Read a block from the input stream, ensuring buffer is completely filled.
270
- * Recommended block size = 8176 (size used internally by PSTs)
271
- * @param {Buffer} target
272
- * @memberof PSTNodeInputStream
273
- */
274
- readCompletely(target) {
275
- let offset = 0;
276
- let numRead = 0;
277
- while (offset < target.length) {
278
- numRead = this.readFromOffset(target, offset, target.length - offset);
279
- if (numRead === -1) {
280
- throw new Error('PSTNodeInputStream::readCompletely unexpected EOF encountered attempting to read from PSTInputStream');
281
- }
282
- offset += numRead;
283
- }
284
- }
285
- /**
286
- * Read a block from the input stream.
287
- * Recommended block size = 8176 (size used internally by PSTs)
288
- * @param {Buffer} output
289
- * @returns {number}
290
- * @memberof PSTNodeInputStream
291
- */
292
- readBlock(output) {
293
- // this method is implemented in an attempt to make things a bit faster
294
- // than the byte-by-byte read() crap above.
295
- // it's tricky 'cause we have to copy blocks from a few different areas.
296
- if (this.currentLocation == this.length) {
297
- // EOF
298
- return -1;
299
- }
300
- // first deal with the small stuff
301
- if (this.allData != null) {
302
- const bytesRemaining = this.length
303
- .subtract(this.currentLocation)
304
- .toNumber();
305
- if (output.length >= bytesRemaining) {
306
- PSTUtil_class_1.PSTUtil.arraycopy(this.allData, this.currentLocation.toNumber(), output, 0, bytesRemaining);
307
- if (this.encrypted) {
308
- PSTUtil_class_1.PSTUtil.decode(output);
309
- }
310
- this.currentLocation = this.currentLocation.add(bytesRemaining); // should be = to this.length
311
- return bytesRemaining;
312
- }
313
- else {
314
- PSTUtil_class_1.PSTUtil.arraycopy(this.allData, this.currentLocation.toNumber(), output, 0, output.length);
315
- if (this.encrypted) {
316
- PSTUtil_class_1.PSTUtil.decode(output);
317
- }
318
- this.currentLocation = this.currentLocation.add(output.length);
319
- return output.length;
320
- }
321
- }
322
- let filled = false;
323
- let totalBytesFilled = 0;
324
- // while we still need to fill the array
325
- while (!filled) {
326
- // fill up the output from where we are
327
- // get the current block, either to the end, or until the length of
328
- // the output
329
- const offset = this.indexItems[this.currentBlock];
330
- const skipPoint = this.skipPoints[this.currentBlock];
331
- const currentPosInBlock = this.currentLocation
332
- .subtract(skipPoint)
333
- .toNumber();
334
- this.pstFile.seek(offset.fileOffset.add(currentPosInBlock));
335
- const nextSkipPoint = skipPoint.add(offset.size);
336
- let bytesRemaining = output.length - totalBytesFilled;
337
- // if the total bytes remaining if going to take us past our size
338
- if (bytesRemaining > this.length.subtract(this.currentLocation).toNumber()) {
339
- // we only have so much to give
340
- bytesRemaining = this.length.subtract(this.currentLocation).toNumber();
341
- }
342
- if (nextSkipPoint.greaterThanOrEqual(this.currentLocation.add(bytesRemaining))) {
343
- // we can fill the output with the rest of our current block!
344
- const chunk = Buffer.alloc(bytesRemaining);
345
- this.pstFile.readCompletely(chunk);
346
- PSTUtil_class_1.PSTUtil.arraycopy(chunk, 0, output, totalBytesFilled, bytesRemaining);
347
- totalBytesFilled += bytesRemaining;
348
- // we are done!
349
- filled = true;
350
- this.currentLocation = this.currentLocation.add(bytesRemaining);
351
- }
352
- else {
353
- // we need to read out a whole chunk and keep going
354
- const bytesToRead = offset.size - currentPosInBlock;
355
- const chunk = Buffer.alloc(bytesToRead);
356
- this.pstFile.readCompletely(chunk);
357
- PSTUtil_class_1.PSTUtil.arraycopy(chunk, 0, output, totalBytesFilled, bytesToRead);
358
- totalBytesFilled += bytesToRead;
359
- this.currentBlock++;
360
- this.currentLocation = this.currentLocation.add(bytesToRead);
361
- }
362
- this.totalLoopCount++;
363
- }
364
- // decode the array if required
365
- if (this.encrypted) {
366
- PSTUtil_class_1.PSTUtil.decode(output);
367
- }
368
- // fill up our chunk
369
- // move to the next chunk
370
- return totalBytesFilled;
371
- }
372
- /**
373
- * Read from the offset.
374
- * @param {Buffer} output
375
- * @param {number} offset
376
- * @param {number} length
377
- * @returns {number}
378
- * @memberof PSTNodeInputStream
379
- */
380
- readFromOffset(output, offset, length) {
381
- if (this.currentLocation == this.length) {
382
- // EOF
383
- return -1;
384
- }
385
- if (output.length < length) {
386
- length = output.length;
387
- }
388
- const buf = Buffer.alloc(length);
389
- const lengthRead = this.readBlock(buf);
390
- PSTUtil_class_1.PSTUtil.arraycopy(buf, 0, output, offset, lengthRead);
391
- return lengthRead;
392
- }
393
- /**
394
- * Reset the file pointer (internally).
395
- * @memberof PSTNodeInputStream
396
- */
397
- reset() {
398
- this.currentBlock = 0;
399
- this._currentLocation = long.ZERO;
400
- }
401
- /**
402
- * Get the offsets (block positions) used in the array
403
- * @returns {long[]}
404
- * @memberof PSTNodeInputStream
405
- */
406
- getBlockOffsets() {
407
- const output = [];
408
- if (this.skipPoints.length === 0) {
409
- const len = long.fromValue(this.length);
410
- output.push(len);
411
- }
412
- else {
413
- for (let x = 0; x < this.skipPoints.length; x++) {
414
- const size = long.fromValue(this.indexItems[x].size);
415
- output.push(this.skipPoints[x].add(size));
416
- }
417
- }
418
- return output;
419
- }
420
- /**
421
- * Seek within item.
422
- * @param {long} location
423
- * @returns
424
- * @memberof PSTNodeInputStream
425
- */
426
- seek(location) {
427
- // not past the end!
428
- if (location.greaterThan(this.length)) {
429
- throw new Error('PSTNodeInputStream::seek Attempt to seek past end of item! size = ' +
430
- this.length +
431
- ', seeking to:' +
432
- location);
433
- }
434
- // are we already there?
435
- if (this.currentLocation.equals(location)) {
436
- return;
437
- }
438
- // get us to the right block
439
- let skipPoint = long.ZERO;
440
- this.currentBlock = 0;
441
- if (this.allData == null) {
442
- skipPoint = this.skipPoints[this.currentBlock + 1];
443
- while (location.greaterThanOrEqual(skipPoint)) {
444
- this.currentBlock++;
445
- // is this the last block?
446
- if (this.currentBlock == this.skipPoints.length - 1) {
447
- // that's all folks
448
- break;
449
- }
450
- else {
451
- skipPoint = this.skipPoints[this.currentBlock + 1];
452
- }
453
- }
454
- }
455
- // now move us to the right position in there
456
- this.currentLocation = location;
457
- if (this.allData == null) {
458
- const blockStart = this.indexItems[this.currentBlock].fileOffset;
459
- const newFilePos = blockStart.add(location).subtract(skipPoint);
460
- this.pstFile.seek(newFilePos);
461
- }
462
- }
463
- /**
464
- * Seek within stream and read a long.
465
- * @param {long} location
466
- * @param {number} bytes
467
- * @returns {long}
468
- * @memberof PSTNodeInputStream
469
- */
470
- seekAndReadLong(location, bytes) {
471
- this.seek(location);
472
- const buffer = Buffer.alloc(bytes);
473
- this.readCompletely(buffer);
474
- return PSTUtil_class_1.PSTUtil.convertLittleEndianBytesToLong(buffer);
475
- }
476
- /**
477
- * JSON the object, large buffers excluded.
478
- * @returns {*}
479
- * @memberof PSTNodeInputStream
480
- */
481
- toJSON() {
482
- return {
483
- currentBlock: this.currentBlock,
484
- isZlib: this.isZlib,
485
- };
486
- }
487
- }
488
- exports.PSTNodeInputStream = PSTNodeInputStream;
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.PSTNodeInputStream = void 0;
30
+ /* eslint-disable @typescript-eslint/no-explicit-any */
31
+ const long_1 = __importDefault(require("long"));
32
+ const zlib = __importStar(require("zlib"));
33
+ const PSTDescriptorItem_class_1 = require("./PSTDescriptorItem.class");
34
+ const PSTUtil_class_1 = require("./PSTUtil.class");
35
+ const OffsetIndexItem_class_1 = require("./OffsetIndexItem.class");
36
+ const PSTFile_class_1 = require("./PSTFile.class");
37
+ class PSTNodeInputStream {
38
+ get currentLocation() {
39
+ return this._currentLocation;
40
+ }
41
+ set currentLocation(loc) {
42
+ this._currentLocation = loc;
43
+ }
44
+ get pstFile() {
45
+ return this._pstFile;
46
+ }
47
+ get length() {
48
+ return this._length;
49
+ }
50
+ get encrypted() {
51
+ return this._encrypted;
52
+ }
53
+ constructor(pstFile, arg, encrypted) {
54
+ this.skipPoints = [];
55
+ this.indexItems = [];
56
+ this.currentBlock = 0;
57
+ this.allData = null;
58
+ this.isZlib = false;
59
+ this._currentLocation = long_1.default.ZERO;
60
+ this._length = long_1.default.ZERO;
61
+ this._encrypted = false;
62
+ this.totalLoopCount = 0;
63
+ this._pstFile = pstFile;
64
+ if (arg instanceof OffsetIndexItem_class_1.OffsetIndexItem) {
65
+ this._encrypted =
66
+ pstFile.encryptionType == PSTFile_class_1.PSTFile.ENCRYPTION_TYPE_COMPRESSIBLE;
67
+ this.loadFromOffsetItem(arg);
68
+ }
69
+ else if (arg instanceof PSTDescriptorItem_class_1.PSTDescriptorItem) {
70
+ this._encrypted =
71
+ pstFile.encryptionType == PSTFile_class_1.PSTFile.ENCRYPTION_TYPE_COMPRESSIBLE;
72
+ // we want to get the first block of data and see what we are dealing with
73
+ this.loadFromOffsetItem(pstFile.getOffsetIndexNode(long_1.default.fromNumber(arg.offsetIndexIdentifier)));
74
+ }
75
+ else if (arg instanceof Buffer) {
76
+ this.allData = arg;
77
+ this._length = long_1.default.fromNumber(this.allData.length);
78
+ if (encrypted != undefined) {
79
+ this._encrypted = encrypted;
80
+ }
81
+ else {
82
+ this._encrypted =
83
+ pstFile.encryptionType == PSTFile_class_1.PSTFile.ENCRYPTION_TYPE_COMPRESSIBLE;
84
+ }
85
+ }
86
+ this.currentBlock = 0;
87
+ this.currentLocation = long_1.default.ZERO;
88
+ this.detectZlib();
89
+ }
90
+ /**
91
+ * Checks if the node is ZL compressed and unzips if so.
92
+ * @private
93
+ * @returns
94
+ * @memberof PSTNodeInputStream
95
+ */
96
+ detectZlib() {
97
+ // not really sure how this is meant to work, kind of going by feel here.
98
+ if (this.length.lt(4)) {
99
+ return;
100
+ }
101
+ try {
102
+ if (this.read() === 0x78 && this.read() === 0x9c) {
103
+ let multiStreams = false;
104
+ if (this.indexItems.length > 1) {
105
+ const i = this.indexItems[1];
106
+ this.pstFile.seek(i.fileOffset);
107
+ multiStreams =
108
+ this.pstFile.read() === 0x78 && this.pstFile.read() === 0x9c;
109
+ }
110
+ // we are a compressed block, decompress the whole thing into a buffer
111
+ // and replace our contents with that. firstly, if we have blocks, use that as the length
112
+ let outputStream = null;
113
+ if (multiStreams) {
114
+ // debugger
115
+ // console.log('list of all index items')
116
+ // for (let i of this.indexItems) {
117
+ // console.log(i.toJSON());
118
+ // }
119
+ // console.log('----------------------')
120
+ for (const i of this.indexItems) {
121
+ const inData = Buffer.alloc(i.size);
122
+ this.pstFile.seek(i.fileOffset);
123
+ this.pstFile.readCompletely(inData);
124
+ const buf = zlib.unzipSync(inData);
125
+ outputStream = outputStream ? Buffer.concat([outputStream, buf]) : buf;
126
+ }
127
+ this.indexItems = [];
128
+ this.skipPoints = [];
129
+ }
130
+ else {
131
+ let compressedLength = this.length.toNumber();
132
+ if (this.indexItems.length > 0) {
133
+ compressedLength = 0;
134
+ for (const i of this.indexItems) {
135
+ compressedLength += i.size;
136
+ }
137
+ }
138
+ const inData = Buffer.alloc(compressedLength);
139
+ this.seek(long_1.default.ZERO);
140
+ this.readCompletely(inData);
141
+ outputStream = zlib.unzipSync(inData);
142
+ }
143
+ this.allData = outputStream;
144
+ this.currentLocation = long_1.default.ZERO;
145
+ this.currentBlock = 0;
146
+ this._length = this.allData
147
+ ? long_1.default.fromNumber(this.allData.length)
148
+ : long_1.default.ZERO;
149
+ }
150
+ this.seek(long_1.default.ZERO);
151
+ }
152
+ catch (err) {
153
+ console.error('PSTNodeInputStream::detectZlib Unable to decompress reportedly compressed block\n' +
154
+ err);
155
+ throw err;
156
+ }
157
+ }
158
+ /**
159
+ * Load data from offset in file.
160
+ * @private
161
+ * @param {OffsetIndexItem} offsetItem
162
+ * @returns
163
+ * @memberof PSTNodeInputStream
164
+ */
165
+ loadFromOffsetItem(offsetItem) {
166
+ let bInternal = (offsetItem.indexIdentifier.toNumber() & 0x02) != 0;
167
+ const data = Buffer.alloc(offsetItem.size);
168
+ this.pstFile.seek(offsetItem.fileOffset);
169
+ this.pstFile.readCompletely(data);
170
+ if (bInternal) {
171
+ // All internal blocks are at least 8 bytes long...
172
+ if (offsetItem.size < 8) {
173
+ throw new Error('PSTNodeInputStream::loadFromOffsetItem Invalid internal block size');
174
+ }
175
+ if (data[0] == 0x1) {
176
+ bInternal = false;
177
+ // we are a xblock, or xxblock
178
+ this._length = PSTUtil_class_1.PSTUtil.convertLittleEndianBytesToLong(data, 4, 8);
179
+ // go through all of the blocks and create skip points.
180
+ this.getBlockSkipPoints(data);
181
+ return;
182
+ }
183
+ }
184
+ // (Internal blocks aren't compressed)
185
+ if (bInternal) {
186
+ this._encrypted = false;
187
+ }
188
+ this.allData = data;
189
+ this._length = long_1.default.fromValue(this.allData.length);
190
+ }
191
+ /**
192
+ * Get block skip points in file.
193
+ * @private
194
+ * @param {Buffer} data
195
+ * @memberof PSTNodeInputStream
196
+ */
197
+ getBlockSkipPoints(data) {
198
+ if (data[0] != 0x1) {
199
+ throw new Error('PSTNodeInputStream::loadFromOffsetItem Unable to process XBlock, incorrect identifier');
200
+ }
201
+ const numberOfEntries = PSTUtil_class_1.PSTUtil.convertLittleEndianBytesToLong(data, 2, 4).toNumber();
202
+ let arraySize = 8;
203
+ if (this.pstFile.pstFileType == PSTFile_class_1.PSTFile.PST_TYPE_ANSI) {
204
+ arraySize = 4;
205
+ }
206
+ if (data[1] == 0x2) {
207
+ // XXBlock
208
+ let offset = 8;
209
+ for (let x = 0; x < numberOfEntries; x++) {
210
+ let bid = PSTUtil_class_1.PSTUtil.convertLittleEndianBytesToLong(data, offset, offset + arraySize);
211
+ bid = bid.and(0xfffffffe);
212
+ // get the details in this block and
213
+ const offsetItem = this.pstFile.getOffsetIndexNode(bid);
214
+ const blockData = Buffer.alloc(offsetItem.size);
215
+ this.pstFile.seek(offsetItem.fileOffset);
216
+ this.pstFile.readCompletely(blockData);
217
+ // recurse
218
+ this.getBlockSkipPoints(blockData);
219
+ offset += arraySize;
220
+ }
221
+ }
222
+ else if (data[1] == 0x1) {
223
+ // normal XBlock
224
+ let offset = 8;
225
+ for (let x = 0; x < numberOfEntries; x++) {
226
+ let bid = PSTUtil_class_1.PSTUtil.convertLittleEndianBytesToLong(data, offset, offset + arraySize);
227
+ bid = bid.and(0xfffffffe);
228
+ // get the details in this block and add it to the list
229
+ const offsetItem = this.pstFile.getOffsetIndexNode(bid);
230
+ this.indexItems.push(offsetItem);
231
+ this.skipPoints.push(long_1.default.fromValue(this.currentLocation));
232
+ this.currentLocation = this.currentLocation.add(offsetItem.size);
233
+ offset += arraySize;
234
+ }
235
+ }
236
+ }
237
+ /**
238
+ * Read from the stream.
239
+ * @param {Buffer} [output]
240
+ * @returns
241
+ * @memberof PSTNodeInputStream
242
+ */
243
+ read(output) {
244
+ if (!output) {
245
+ return this.readSingleByte();
246
+ }
247
+ else {
248
+ return this.readBlock(output);
249
+ }
250
+ }
251
+ /**
252
+ * Read a single byte from the input stream.
253
+ * @returns {number}
254
+ * @memberof PSTNodeInputStream
255
+ */
256
+ readSingleByte() {
257
+ // first deal with items < 8K and we have all the data already
258
+ if (this.allData != null) {
259
+ if (this.currentLocation == this.length) {
260
+ // EOF
261
+ return -1;
262
+ }
263
+ let value = this.allData[this.currentLocation.toNumber()] & 0xff;
264
+ this.currentLocation = this.currentLocation.add(1);
265
+ if (this.encrypted) {
266
+ value = PSTUtil_class_1.PSTUtil.compEnc[value];
267
+ }
268
+ return value;
269
+ }
270
+ let item = this.indexItems[this.currentBlock];
271
+ let skipPoint = this.skipPoints[this.currentBlock];
272
+ if (this.currentLocation.add(1).greaterThan(skipPoint.add(item.size))) {
273
+ // got to move to the next block
274
+ this.currentBlock++;
275
+ if (this.currentBlock >= this.indexItems.length) {
276
+ return -1;
277
+ }
278
+ item = this.indexItems[this.currentBlock];
279
+ skipPoint = this.skipPoints[this.currentBlock];
280
+ }
281
+ // get the next byte.
282
+ const pos = item.fileOffset.add(this.currentLocation).subtract(skipPoint);
283
+ this.pstFile.seek(pos);
284
+ let output = this.pstFile.read();
285
+ if (output < 0) {
286
+ return -1;
287
+ }
288
+ if (this.encrypted) {
289
+ output = PSTUtil_class_1.PSTUtil.compEnc[output];
290
+ }
291
+ this.currentLocation = this.currentLocation.add(1);
292
+ return output;
293
+ }
294
+ /**
295
+ * Read a block from the input stream, ensuring buffer is completely filled.
296
+ * Recommended block size = 8176 (size used internally by PSTs)
297
+ * @param {Buffer} target
298
+ * @memberof PSTNodeInputStream
299
+ */
300
+ readCompletely(target) {
301
+ let offset = 0;
302
+ let numRead = 0;
303
+ while (offset < target.length) {
304
+ numRead = this.readFromOffset(target, offset, target.length - offset);
305
+ if (numRead === -1) {
306
+ throw new Error('PSTNodeInputStream::readCompletely unexpected EOF encountered attempting to read from PSTInputStream');
307
+ }
308
+ offset += numRead;
309
+ }
310
+ }
311
+ /**
312
+ * Read a block from the input stream.
313
+ * Recommended block size = 8176 (size used internally by PSTs)
314
+ * @param {Buffer} output
315
+ * @returns {number}
316
+ * @memberof PSTNodeInputStream
317
+ */
318
+ readBlock(output) {
319
+ // this method is implemented in an attempt to make things a bit faster
320
+ // than the byte-by-byte read() crap above.
321
+ // it's tricky 'cause we have to copy blocks from a few different areas.
322
+ if (this.currentLocation == this.length) {
323
+ // EOF
324
+ return -1;
325
+ }
326
+ // first deal with the small stuff
327
+ if (this.allData != null) {
328
+ const bytesRemaining = this.length
329
+ .subtract(this.currentLocation)
330
+ .toNumber();
331
+ if (output.length >= bytesRemaining) {
332
+ PSTUtil_class_1.PSTUtil.arraycopy(this.allData, this.currentLocation.toNumber(), output, 0, bytesRemaining);
333
+ if (this.encrypted) {
334
+ PSTUtil_class_1.PSTUtil.decode(output);
335
+ }
336
+ this.currentLocation = this.currentLocation.add(bytesRemaining); // should be = to this.length
337
+ return bytesRemaining;
338
+ }
339
+ else {
340
+ PSTUtil_class_1.PSTUtil.arraycopy(this.allData, this.currentLocation.toNumber(), output, 0, output.length);
341
+ if (this.encrypted) {
342
+ PSTUtil_class_1.PSTUtil.decode(output);
343
+ }
344
+ this.currentLocation = this.currentLocation.add(output.length);
345
+ return output.length;
346
+ }
347
+ }
348
+ let filled = false;
349
+ let totalBytesFilled = 0;
350
+ // while we still need to fill the array
351
+ while (!filled) {
352
+ // fill up the output from where we are
353
+ // get the current block, either to the end, or until the length of
354
+ // the output
355
+ const offset = this.indexItems[this.currentBlock];
356
+ const skipPoint = this.skipPoints[this.currentBlock];
357
+ const currentPosInBlock = this.currentLocation
358
+ .subtract(skipPoint)
359
+ .toNumber();
360
+ this.pstFile.seek(offset.fileOffset.add(currentPosInBlock));
361
+ const nextSkipPoint = skipPoint.add(offset.size);
362
+ let bytesRemaining = output.length - totalBytesFilled;
363
+ // if the total bytes remaining if going to take us past our size
364
+ if (bytesRemaining > this.length.subtract(this.currentLocation).toNumber()) {
365
+ // we only have so much to give
366
+ bytesRemaining = this.length.subtract(this.currentLocation).toNumber();
367
+ }
368
+ if (nextSkipPoint.greaterThanOrEqual(this.currentLocation.add(bytesRemaining))) {
369
+ // we can fill the output with the rest of our current block!
370
+ const chunk = Buffer.alloc(bytesRemaining);
371
+ this.pstFile.readCompletely(chunk);
372
+ PSTUtil_class_1.PSTUtil.arraycopy(chunk, 0, output, totalBytesFilled, bytesRemaining);
373
+ totalBytesFilled += bytesRemaining;
374
+ // we are done!
375
+ filled = true;
376
+ this.currentLocation = this.currentLocation.add(bytesRemaining);
377
+ }
378
+ else {
379
+ // we need to read out a whole chunk and keep going
380
+ const bytesToRead = offset.size - currentPosInBlock;
381
+ const chunk = Buffer.alloc(bytesToRead);
382
+ this.pstFile.readCompletely(chunk);
383
+ PSTUtil_class_1.PSTUtil.arraycopy(chunk, 0, output, totalBytesFilled, bytesToRead);
384
+ totalBytesFilled += bytesToRead;
385
+ this.currentBlock++;
386
+ this.currentLocation = this.currentLocation.add(bytesToRead);
387
+ }
388
+ this.totalLoopCount++;
389
+ }
390
+ // decode the array if required
391
+ if (this.encrypted) {
392
+ PSTUtil_class_1.PSTUtil.decode(output);
393
+ }
394
+ // fill up our chunk
395
+ // move to the next chunk
396
+ return totalBytesFilled;
397
+ }
398
+ /**
399
+ * Read from the offset.
400
+ * @param {Buffer} output
401
+ * @param {number} offset
402
+ * @param {number} length
403
+ * @returns {number}
404
+ * @memberof PSTNodeInputStream
405
+ */
406
+ readFromOffset(output, offset, length) {
407
+ if (this.currentLocation == this.length) {
408
+ // EOF
409
+ return -1;
410
+ }
411
+ if (output.length < length) {
412
+ length = output.length;
413
+ }
414
+ const buf = Buffer.alloc(length);
415
+ const lengthRead = this.readBlock(buf);
416
+ PSTUtil_class_1.PSTUtil.arraycopy(buf, 0, output, offset, lengthRead);
417
+ return lengthRead;
418
+ }
419
+ /**
420
+ * Reset the file pointer (internally).
421
+ * @memberof PSTNodeInputStream
422
+ */
423
+ reset() {
424
+ this.currentBlock = 0;
425
+ this._currentLocation = long_1.default.ZERO;
426
+ }
427
+ /**
428
+ * Get the offsets (block positions) used in the array
429
+ * @returns {long[]}
430
+ * @memberof PSTNodeInputStream
431
+ */
432
+ getBlockOffsets() {
433
+ const output = [];
434
+ if (this.skipPoints.length === 0) {
435
+ const len = long_1.default.fromValue(this.length);
436
+ output.push(len);
437
+ }
438
+ else {
439
+ for (let x = 0; x < this.skipPoints.length; x++) {
440
+ const size = long_1.default.fromValue(this.indexItems[x].size);
441
+ output.push(this.skipPoints[x].add(size));
442
+ }
443
+ }
444
+ return output;
445
+ }
446
+ /**
447
+ * Seek within item.
448
+ * @param {long} location
449
+ * @returns
450
+ * @memberof PSTNodeInputStream
451
+ */
452
+ seek(location) {
453
+ // not past the end!
454
+ if (location.greaterThan(this.length)) {
455
+ throw new Error('PSTNodeInputStream::seek Attempt to seek past end of item! size = ' +
456
+ this.length +
457
+ ', seeking to:' +
458
+ location);
459
+ }
460
+ // are we already there?
461
+ if (this.currentLocation.equals(location)) {
462
+ return;
463
+ }
464
+ // get us to the right block
465
+ let skipPoint = long_1.default.ZERO;
466
+ this.currentBlock = 0;
467
+ if (this.allData == null) {
468
+ skipPoint = this.skipPoints[this.currentBlock + 1];
469
+ while (location.greaterThanOrEqual(skipPoint)) {
470
+ this.currentBlock++;
471
+ // is this the last block?
472
+ if (this.currentBlock == this.skipPoints.length - 1) {
473
+ // that's all folks
474
+ break;
475
+ }
476
+ else {
477
+ skipPoint = this.skipPoints[this.currentBlock + 1];
478
+ }
479
+ }
480
+ }
481
+ // now move us to the right position in there
482
+ this.currentLocation = location;
483
+ if (this.allData == null) {
484
+ const blockStart = this.indexItems[this.currentBlock].fileOffset;
485
+ const newFilePos = blockStart.add(location).subtract(skipPoint);
486
+ this.pstFile.seek(newFilePos);
487
+ }
488
+ }
489
+ /**
490
+ * Seek within stream and read a long.
491
+ * @param {long} location
492
+ * @param {number} bytes
493
+ * @returns {long}
494
+ * @memberof PSTNodeInputStream
495
+ */
496
+ seekAndReadLong(location, bytes) {
497
+ this.seek(location);
498
+ const buffer = Buffer.alloc(bytes);
499
+ this.readCompletely(buffer);
500
+ return PSTUtil_class_1.PSTUtil.convertLittleEndianBytesToLong(buffer);
501
+ }
502
+ /**
503
+ * JSON the object, large buffers excluded.
504
+ * @returns {*}
505
+ * @memberof PSTNodeInputStream
506
+ */
507
+ toJSON() {
508
+ return {
509
+ currentBlock: this.currentBlock,
510
+ isZlib: this.isZlib,
511
+ };
512
+ }
513
+ }
514
+ exports.PSTNodeInputStream = PSTNodeInputStream;