pst-extractor 1.9.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.
- package/dist/ColumnDescriptor.class.d.ts +26 -26
- package/dist/ColumnDescriptor.class.js +51 -51
- package/dist/DescriptorIndexNode.class.d.ts +25 -26
- package/dist/DescriptorIndexNode.class.js +53 -53
- package/dist/LZFu.class.d.ts +11 -12
- package/dist/LZFu.class.js +95 -95
- package/dist/NodeInfo.class.d.ts +33 -33
- package/dist/NodeInfo.class.js +52 -52
- package/dist/NodeMap.class.d.ts +35 -35
- package/dist/NodeMap.class.js +86 -86
- package/dist/OffsetIndexItem.class.d.ts +23 -24
- package/dist/OffsetIndexItem.class.js +45 -45
- package/dist/OutlookProperties.d.ts +275 -275
- package/dist/OutlookProperties.js +281 -281
- package/dist/PSTActivity.class.d.ts +103 -103
- package/dist/PSTActivity.class.js +144 -144
- package/dist/PSTAppointment.class.d.ts +270 -271
- package/dist/PSTAppointment.class.js +376 -376
- package/dist/PSTAttachment.class.d.ts +172 -172
- package/dist/PSTAttachment.class.js +317 -317
- package/dist/PSTContact.class.d.ts +884 -884
- package/dist/PSTContact.class.js +1227 -1227
- package/dist/PSTDescriptorItem.class.d.ts +45 -46
- package/dist/PSTDescriptorItem.class.js +99 -99
- package/dist/PSTFile.class.d.ts +215 -216
- package/dist/PSTFile.class.js +818 -818
- package/dist/PSTFolder.class.d.ts +129 -129
- package/dist/PSTFolder.class.js +318 -310
- package/dist/PSTMessage.class.d.ts +788 -789
- package/dist/PSTMessage.class.js +1321 -1321
- package/dist/PSTMessageStore.class.d.ts +13 -13
- package/dist/PSTMessageStore.class.js +17 -17
- package/dist/PSTNodeInputStream.class.d.ts +122 -123
- package/dist/PSTNodeInputStream.class.js +514 -514
- package/dist/PSTObject.class.d.ts +133 -134
- package/dist/PSTObject.class.js +326 -326
- package/dist/PSTRecipient.class.d.ts +65 -65
- package/dist/PSTRecipient.class.js +103 -103
- package/dist/PSTTable.class.d.ts +52 -52
- package/dist/PSTTable.class.js +175 -175
- package/dist/PSTTable7C.class.d.ts +45 -45
- package/dist/PSTTable7C.class.js +281 -281
- package/dist/PSTTableBC.class.d.ts +31 -31
- package/dist/PSTTableBC.class.js +111 -111
- package/dist/PSTTableItem.class.d.ts +47 -48
- package/dist/PSTTableItem.class.js +124 -124
- package/dist/PSTTask.class.d.ts +146 -146
- package/dist/PSTTask.class.js +205 -205
- package/dist/PSTUtil.class.d.ts +134 -135
- package/dist/PSTUtil.class.js +795 -795
- package/dist/RecurrencePattern.class.d.ts +49 -50
- package/dist/RecurrencePattern.class.js +120 -120
- package/dist/index.d.ts +6 -6
- package/dist/index.js +15 -15
- package/example/package.json +6 -6
- package/example/yarn.lock +95 -44
- package/junit.xml +68 -68
- package/package.json +26 -26
- package/readme.md +1 -3
package/dist/PSTFile.class.js
CHANGED
|
@@ -1,818 +1,818 @@
|
|
|
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.PSTFile = void 0;
|
|
30
|
-
const fs = __importStar(require("fs"));
|
|
31
|
-
const long_1 = __importDefault(require("long"));
|
|
32
|
-
const DescriptorIndexNode_class_1 = require("./DescriptorIndexNode.class");
|
|
33
|
-
const OffsetIndexItem_class_1 = require("./OffsetIndexItem.class");
|
|
34
|
-
const PSTDescriptorItem_class_1 = require("./PSTDescriptorItem.class");
|
|
35
|
-
const PSTFolder_class_1 = require("./PSTFolder.class");
|
|
36
|
-
const PSTMessageStore_class_1 = require("./PSTMessageStore.class");
|
|
37
|
-
const PSTNodeInputStream_class_1 = require("./PSTNodeInputStream.class");
|
|
38
|
-
const PSTTableBC_class_1 = require("./PSTTableBC.class");
|
|
39
|
-
const PSTUtil_class_1 = require("./PSTUtil.class");
|
|
40
|
-
const NodeMap_class_1 = require("./NodeMap.class");
|
|
41
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
42
|
-
const uuidparse = require('uuid-parse');
|
|
43
|
-
class PSTFile {
|
|
44
|
-
|
|
45
|
-
this.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
['
|
|
56
|
-
['
|
|
57
|
-
['
|
|
58
|
-
['
|
|
59
|
-
['
|
|
60
|
-
['
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
//
|
|
72
|
-
this.
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
buffer[
|
|
99
|
-
buffer[
|
|
100
|
-
throw new Error('PSTFile::open
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
/**
|
|
126
|
-
* Close the file.
|
|
127
|
-
* @memberof PSTFile
|
|
128
|
-
*/
|
|
129
|
-
close() {
|
|
130
|
-
if (this.pstFD > 0) {
|
|
131
|
-
fs.closeSync(this.pstFD);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* Process name to ID map.
|
|
136
|
-
* @private
|
|
137
|
-
* @memberof PSTFile
|
|
138
|
-
*/
|
|
139
|
-
processNameToIDMap() {
|
|
140
|
-
const nameToIdMapDescriptorNode = this.getDescriptorIndexNode(long_1.default.fromNumber(97));
|
|
141
|
-
// get the descriptors if we have them
|
|
142
|
-
let localDescriptorItems = null;
|
|
143
|
-
if (nameToIdMapDescriptorNode.localDescriptorsOffsetIndexIdentifier.toNumber() !=
|
|
144
|
-
0) {
|
|
145
|
-
localDescriptorItems = this.getPSTDescriptorItems(nameToIdMapDescriptorNode.localDescriptorsOffsetIndexIdentifier);
|
|
146
|
-
}
|
|
147
|
-
// process the map
|
|
148
|
-
const off = this.getOffsetIndexNode(nameToIdMapDescriptorNode.dataOffsetIndexIdentifier);
|
|
149
|
-
const nodein = new PSTNodeInputStream_class_1.PSTNodeInputStream(this, off);
|
|
150
|
-
const bcTable = new PSTTableBC_class_1.PSTTableBC(nodein);
|
|
151
|
-
const tableItems = bcTable.getItems();
|
|
152
|
-
// Get the guids
|
|
153
|
-
const guidEntry = tableItems.get(2);
|
|
154
|
-
if (!guidEntry) {
|
|
155
|
-
throw new Error('PSTFile::processNameToIDMap guidEntry is null');
|
|
156
|
-
}
|
|
157
|
-
const guids = this.getData(guidEntry, localDescriptorItems);
|
|
158
|
-
const nGuids = Math.trunc(guids.length / 16);
|
|
159
|
-
const guidIndexes = [];
|
|
160
|
-
let offset = 0;
|
|
161
|
-
for (let i = 0; i < nGuids; ++i) {
|
|
162
|
-
let leftQuad = PSTUtil_class_1.PSTUtil.convertLittleEndianBytesToLong(guids, offset, offset + 4);
|
|
163
|
-
leftQuad = leftQuad.shiftLeft(32);
|
|
164
|
-
let midQuad = PSTUtil_class_1.PSTUtil.convertLittleEndianBytesToLong(guids, offset + 4, offset + 6);
|
|
165
|
-
midQuad = midQuad.shiftLeft(16);
|
|
166
|
-
const rightQuad = PSTUtil_class_1.PSTUtil.convertLittleEndianBytesToLong(guids, offset + 6, offset + 8);
|
|
167
|
-
const mostSigBits = leftQuad.or(midQuad).or(rightQuad);
|
|
168
|
-
const leastSigBits = PSTUtil_class_1.PSTUtil.convertBigEndianBytesToLong(guids, offset + 8, offset + 16);
|
|
169
|
-
// weird that need to cast below to any to get tsc error to go away - why?
|
|
170
|
-
// see https://github.com/Microsoft/TypeScript/issues/6436
|
|
171
|
-
const mostBuffer = mostSigBits.toBytes();
|
|
172
|
-
const leastBuffer = leastSigBits.toBytes();
|
|
173
|
-
const arrUID = mostBuffer.concat(leastBuffer);
|
|
174
|
-
const strUID = uuidparse.unparse(arrUID).toUpperCase();
|
|
175
|
-
const guid = this.guidMap.get(strUID);
|
|
176
|
-
if (guid) {
|
|
177
|
-
guidIndexes[i] = guid;
|
|
178
|
-
}
|
|
179
|
-
else {
|
|
180
|
-
guidIndexes[i] = -1; // We don't know this guid
|
|
181
|
-
}
|
|
182
|
-
// console.log('PSTFile:: processNameToIdMap idx: ' + i + ', ' + strUID + ', ' + guidIndexes[i]);
|
|
183
|
-
offset += 16;
|
|
184
|
-
}
|
|
185
|
-
// if we have a reference to an internal descriptor
|
|
186
|
-
const mapEntries = tableItems.get(3);
|
|
187
|
-
if (!mapEntries) {
|
|
188
|
-
throw new Error('PSTFile::processNameToIDMap mapEntries is null');
|
|
189
|
-
}
|
|
190
|
-
const nameToIdByte = this.getData(mapEntries, localDescriptorItems);
|
|
191
|
-
const stringMapEntries = tableItems.get(4);
|
|
192
|
-
if (!stringMapEntries) {
|
|
193
|
-
throw new Error('PSTFile::processNameToIDMap stringMapEntries is null');
|
|
194
|
-
}
|
|
195
|
-
const stringNameToIdByte = this.getData(stringMapEntries, localDescriptorItems);
|
|
196
|
-
// process the entries
|
|
197
|
-
for (let x = 0; x + 8 < nameToIdByte.length; x += 8) {
|
|
198
|
-
const key = PSTUtil_class_1.PSTUtil.convertLittleEndianBytesToLong(nameToIdByte, x, x + 4).toNumber();
|
|
199
|
-
let guid = PSTUtil_class_1.PSTUtil.convertLittleEndianBytesToLong(nameToIdByte, x + 4, x + 6).toNumber();
|
|
200
|
-
let propId = PSTUtil_class_1.PSTUtil.convertLittleEndianBytesToLong(nameToIdByte, x + 6, x + 8).toNumber();
|
|
201
|
-
if ((guid & 0x0001) == 0) {
|
|
202
|
-
// identifier is numeric
|
|
203
|
-
propId += 0x8000;
|
|
204
|
-
guid >>= 1;
|
|
205
|
-
let guidIndex;
|
|
206
|
-
if (guid == 1) {
|
|
207
|
-
guidIndex = PSTFile.PS_MAPI;
|
|
208
|
-
}
|
|
209
|
-
else if (guid == 2) {
|
|
210
|
-
guidIndex = PSTFile.PS_PUBLIC_STRINGS;
|
|
211
|
-
}
|
|
212
|
-
else {
|
|
213
|
-
guidIndex = guidIndexes[guid - 3];
|
|
214
|
-
}
|
|
215
|
-
PSTFile.nodeMap.setId(key, propId, guidIndex);
|
|
216
|
-
}
|
|
217
|
-
else {
|
|
218
|
-
// identifier is a string
|
|
219
|
-
// key is byte offset into the String stream in which the string name of the property is stored.
|
|
220
|
-
const len = PSTUtil_class_1.PSTUtil.convertLittleEndianBytesToLong(stringNameToIdByte, key, key + 4).toNumber();
|
|
221
|
-
const keyByteValue = Buffer.alloc(len);
|
|
222
|
-
PSTUtil_class_1.PSTUtil.arraycopy(stringNameToIdByte, key + 4, keyByteValue, 0, keyByteValue.length);
|
|
223
|
-
propId += 0x8000;
|
|
224
|
-
PSTFile.nodeMap.setId(keyByteValue.toString('utf16le').replace(/\0/g, ''), propId);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
/**
|
|
229
|
-
* Get data from a descriptor and store in buffer.
|
|
230
|
-
* @private
|
|
231
|
-
* @param {PSTTableItem} item
|
|
232
|
-
* @param {Map<number, PSTDescriptorItem>} localDescriptorItems
|
|
233
|
-
* @returns {Buffer}
|
|
234
|
-
* @memberof PSTFile
|
|
235
|
-
*/
|
|
236
|
-
getData(item, localDescriptorItems) {
|
|
237
|
-
if (item.data.length != 0) {
|
|
238
|
-
return item.data;
|
|
239
|
-
}
|
|
240
|
-
if (localDescriptorItems == null) {
|
|
241
|
-
throw new Error('PSTFile::getData External reference but no localDescriptorItems in PSTFile.getData()');
|
|
242
|
-
}
|
|
243
|
-
if (item.entryValueType != 0x0102) {
|
|
244
|
-
throw new Error('PSTFile::getData Attempting to get non-binary data in PSTFile.getData()');
|
|
245
|
-
}
|
|
246
|
-
const mapDescriptorItem = localDescriptorItems.get(item.entryValueReference);
|
|
247
|
-
if (mapDescriptorItem == null) {
|
|
248
|
-
throw new Error('PSTFile::getData Descriptor not found: ' + item.entryValueReference);
|
|
249
|
-
}
|
|
250
|
-
return mapDescriptorItem.getData();
|
|
251
|
-
}
|
|
252
|
-
/**
|
|
253
|
-
* Get name to ID map item.
|
|
254
|
-
* @param {number} key
|
|
255
|
-
* @param {number} idx
|
|
256
|
-
* @returns {number}
|
|
257
|
-
* @memberof PSTFile
|
|
258
|
-
*/
|
|
259
|
-
getNameToIdMapItem(key, idx) {
|
|
260
|
-
return PSTFile.nodeMap.getId(key, idx);
|
|
261
|
-
}
|
|
262
|
-
/**
|
|
263
|
-
* Get public string to id map item.
|
|
264
|
-
* @static
|
|
265
|
-
* @param {string} key
|
|
266
|
-
* @returns {number}
|
|
267
|
-
* @memberof PSTFile
|
|
268
|
-
*/
|
|
269
|
-
static getPublicStringToIdMapItem(key) {
|
|
270
|
-
return PSTFile.nodeMap.getId(key);
|
|
271
|
-
}
|
|
272
|
-
/**
|
|
273
|
-
* Get property name from id.
|
|
274
|
-
* @static
|
|
275
|
-
* @param {number} propertyId
|
|
276
|
-
* @param {boolean} bNamed
|
|
277
|
-
* @returns {string}
|
|
278
|
-
* @memberof PSTFile
|
|
279
|
-
*/
|
|
280
|
-
static getPropertyName(propertyId, bNamed) {
|
|
281
|
-
return PSTUtil_class_1.PSTUtil.propertyName.get(propertyId);
|
|
282
|
-
}
|
|
283
|
-
/**
|
|
284
|
-
* Get name to id map key.
|
|
285
|
-
* @static
|
|
286
|
-
* @param {number} propId
|
|
287
|
-
* @returns {long}
|
|
288
|
-
* @memberof PSTFile
|
|
289
|
-
*/
|
|
290
|
-
static getNameToIdMapKey(propId) {
|
|
291
|
-
return PSTFile.nodeMap.getNumericName(propId);
|
|
292
|
-
}
|
|
293
|
-
/**
|
|
294
|
-
* Get the message store of the PST file. Note that this doesn't really
|
|
295
|
-
* have much information, better to look under the root folder.
|
|
296
|
-
* @returns {PSTMessageStore}
|
|
297
|
-
* @memberof PSTFile
|
|
298
|
-
*/
|
|
299
|
-
getMessageStore() {
|
|
300
|
-
const messageStoreDescriptor = this.getDescriptorIndexNode(long_1.default.fromNumber(PSTFile.MESSAGE_STORE_DESCRIPTOR_IDENTIFIER));
|
|
301
|
-
return new PSTMessageStore_class_1.PSTMessageStore(this, messageStoreDescriptor);
|
|
302
|
-
}
|
|
303
|
-
/**
|
|
304
|
-
* Get the root folder for the PST file
|
|
305
|
-
* @returns {PSTFolder}
|
|
306
|
-
* @memberof PSTFile
|
|
307
|
-
*/
|
|
308
|
-
getRootFolder() {
|
|
309
|
-
const rootFolderDescriptor = this.getDescriptorIndexNode(long_1.default.fromValue(PSTFile.ROOT_FOLDER_DESCRIPTOR_IDENTIFIER));
|
|
310
|
-
const output = new PSTFolder_class_1.PSTFolder(this, rootFolderDescriptor);
|
|
311
|
-
return output;
|
|
312
|
-
}
|
|
313
|
-
/**
|
|
314
|
-
* Read a leaf in the b-tree.
|
|
315
|
-
* @param {long} bid
|
|
316
|
-
* @returns {PSTNodeInputStream}
|
|
317
|
-
* @memberof PSTFile
|
|
318
|
-
*/
|
|
319
|
-
readLeaf(bid) {
|
|
320
|
-
// get the index node for the descriptor index
|
|
321
|
-
const offsetItem = this.getOffsetIndexNode(bid);
|
|
322
|
-
return new PSTNodeInputStream_class_1.PSTNodeInputStream(this, offsetItem);
|
|
323
|
-
}
|
|
324
|
-
/**
|
|
325
|
-
* Read the size of the specified leaf.
|
|
326
|
-
* @param {long} bid
|
|
327
|
-
* @returns {number}
|
|
328
|
-
* @memberof PSTFile
|
|
329
|
-
*/
|
|
330
|
-
getLeafSize(bid) {
|
|
331
|
-
const offsetItem = this.getOffsetIndexNode(bid);
|
|
332
|
-
// Internal block?
|
|
333
|
-
if ((offsetItem.indexIdentifier.toNumber() & 0x02) == 0) {
|
|
334
|
-
// No, return the raw size
|
|
335
|
-
return offsetItem.size;
|
|
336
|
-
}
|
|
337
|
-
// we only need the first 8 bytes
|
|
338
|
-
const data = Buffer.alloc(8);
|
|
339
|
-
this.seek(offsetItem.fileOffset);
|
|
340
|
-
this.readCompletely(data);
|
|
341
|
-
// we are an array, get the sum of the sizes...
|
|
342
|
-
return PSTUtil_class_1.PSTUtil.convertLittleEndianBytesToLong(data, 4, 8).toNumber();
|
|
343
|
-
}
|
|
344
|
-
/**
|
|
345
|
-
* Get file offset, which is sorted in 8 little endian bytes
|
|
346
|
-
* @private
|
|
347
|
-
* @param {long} startOffset
|
|
348
|
-
* @returns {long}
|
|
349
|
-
* @memberof PSTFile
|
|
350
|
-
*/
|
|
351
|
-
extractLEFileOffset(startOffset) {
|
|
352
|
-
let offset = long_1.default.ZERO;
|
|
353
|
-
if (this._pstFileType == PSTFile.PST_TYPE_ANSI) {
|
|
354
|
-
this.seek(startOffset);
|
|
355
|
-
const buffer = Buffer.alloc(4);
|
|
356
|
-
this.readCompletely(buffer);
|
|
357
|
-
offset = offset.or(buffer[3] & 0xff);
|
|
358
|
-
offset = offset.shiftLeft(8);
|
|
359
|
-
offset = offset.or(buffer[2] & 0xff);
|
|
360
|
-
offset = offset.shiftLeft(8);
|
|
361
|
-
offset = offset.or(buffer[1] & 0xff);
|
|
362
|
-
offset = offset.shiftLeft(8);
|
|
363
|
-
offset = offset.or(buffer[0] & 0xff);
|
|
364
|
-
}
|
|
365
|
-
else {
|
|
366
|
-
this.seek(startOffset);
|
|
367
|
-
const buffer = Buffer.alloc(8);
|
|
368
|
-
this.readCompletely(buffer);
|
|
369
|
-
offset = offset.or(buffer[7] & 0xff);
|
|
370
|
-
let tmpLongValue;
|
|
371
|
-
for (let x = 6; x >= 0; x--) {
|
|
372
|
-
offset = offset.shiftLeft(8);
|
|
373
|
-
tmpLongValue = buffer[x] & 0xff;
|
|
374
|
-
offset = offset.or(tmpLongValue);
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
return offset;
|
|
378
|
-
}
|
|
379
|
-
/**
|
|
380
|
-
* Navigate PST B-tree and find a specific item.
|
|
381
|
-
* @private
|
|
382
|
-
* @param {long} index
|
|
383
|
-
* @param {boolean} descTree
|
|
384
|
-
* @returns {Buffer}
|
|
385
|
-
* @memberof PSTFile
|
|
386
|
-
*/
|
|
387
|
-
findBtreeItem(index, descTree) {
|
|
388
|
-
let btreeStartOffset;
|
|
389
|
-
let fileTypeAdjustment;
|
|
390
|
-
// first find the starting point for the offset index
|
|
391
|
-
if (this._pstFileType === PSTFile.PST_TYPE_ANSI) {
|
|
392
|
-
btreeStartOffset = this.extractLEFileOffset(long_1.default.fromValue(196));
|
|
393
|
-
if (descTree) {
|
|
394
|
-
btreeStartOffset = this.extractLEFileOffset(long_1.default.fromValue(188));
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
else {
|
|
398
|
-
btreeStartOffset = this.extractLEFileOffset(long_1.default.fromValue(240));
|
|
399
|
-
if (descTree) {
|
|
400
|
-
btreeStartOffset = this.extractLEFileOffset(long_1.default.fromValue(224));
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
// okay, what we want to do is navigate the tree until you reach the bottom....
|
|
404
|
-
// try and read the index b-tree
|
|
405
|
-
let buffer = Buffer.alloc(2);
|
|
406
|
-
if (this._pstFileType === PSTFile.PST_TYPE_ANSI) {
|
|
407
|
-
fileTypeAdjustment = 500;
|
|
408
|
-
}
|
|
409
|
-
else if (this._pstFileType === PSTFile.PST_TYPE_2013_UNICODE) {
|
|
410
|
-
fileTypeAdjustment = 0x1000 - 24;
|
|
411
|
-
}
|
|
412
|
-
else {
|
|
413
|
-
fileTypeAdjustment = 496;
|
|
414
|
-
}
|
|
415
|
-
this.seek(btreeStartOffset.add(fileTypeAdjustment));
|
|
416
|
-
this.readCompletely(buffer);
|
|
417
|
-
const b2 = Buffer.alloc(2);
|
|
418
|
-
b2[0] = 0xff80;
|
|
419
|
-
b2[1] = 0xff81;
|
|
420
|
-
// ensure apples to apples comparison
|
|
421
|
-
while ((buffer[0] === b2[0] && buffer[1] === b2[0] && !descTree) ||
|
|
422
|
-
(buffer[0] === b2[1] && buffer[1] === b2[1] && descTree)) {
|
|
423
|
-
// get the rest of the data
|
|
424
|
-
let len;
|
|
425
|
-
if (this._pstFileType == PSTFile.PST_TYPE_ANSI) {
|
|
426
|
-
len = 496;
|
|
427
|
-
}
|
|
428
|
-
else if (this._pstFileType == PSTFile.PST_TYPE_2013_UNICODE) {
|
|
429
|
-
len = 4056;
|
|
430
|
-
}
|
|
431
|
-
else {
|
|
432
|
-
len = 488;
|
|
433
|
-
}
|
|
434
|
-
const branchNodeItems = Buffer.alloc(len);
|
|
435
|
-
this.seek(btreeStartOffset);
|
|
436
|
-
this.readCompletely(branchNodeItems);
|
|
437
|
-
// console.log('PSTFile::findBtreeItem btreeStartOffset = ' + btreeStartOffset);
|
|
438
|
-
let numberOfItems = 0;
|
|
439
|
-
if (this._pstFileType === PSTFile.PST_TYPE_2013_UNICODE) {
|
|
440
|
-
const numberOfItemsBytes = Buffer.alloc(2);
|
|
441
|
-
this.readCompletely(numberOfItemsBytes);
|
|
442
|
-
numberOfItems = PSTUtil_class_1.PSTUtil.convertLittleEndianBytesToLong(numberOfItemsBytes).toNumber();
|
|
443
|
-
this.readCompletely(numberOfItemsBytes);
|
|
444
|
-
}
|
|
445
|
-
else {
|
|
446
|
-
numberOfItems = this.read();
|
|
447
|
-
this.read(); // maxNumberOfItems
|
|
448
|
-
}
|
|
449
|
-
const itemSize = this.read(); // itemSize
|
|
450
|
-
const levelsToLeaf = this.read();
|
|
451
|
-
if (levelsToLeaf > 0) {
|
|
452
|
-
let found = false;
|
|
453
|
-
for (let x = 0; x < numberOfItems; x++) {
|
|
454
|
-
if (this._pstFileType == PSTFile.PST_TYPE_ANSI) {
|
|
455
|
-
const indexIdOfFirstChildNode = this.extractLEFileOffset(btreeStartOffset.add(x * 12));
|
|
456
|
-
if (indexIdOfFirstChildNode > index) {
|
|
457
|
-
// get the address for the child first node in this group
|
|
458
|
-
btreeStartOffset = this.extractLEFileOffset(btreeStartOffset.add((x - 1) * 12 + 8));
|
|
459
|
-
this.seek(btreeStartOffset.add(500));
|
|
460
|
-
this.readCompletely(buffer);
|
|
461
|
-
found = true;
|
|
462
|
-
break;
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
else {
|
|
466
|
-
const indexIdOfFirstChildNode = this.extractLEFileOffset(btreeStartOffset.add(x * 24));
|
|
467
|
-
if (indexIdOfFirstChildNode.greaterThan(index)) {
|
|
468
|
-
// get the address for the child first node in this group
|
|
469
|
-
btreeStartOffset = this.extractLEFileOffset(btreeStartOffset.add((x - 1) * 24 + 16));
|
|
470
|
-
this.seek(btreeStartOffset.add(fileTypeAdjustment));
|
|
471
|
-
this.readCompletely(buffer);
|
|
472
|
-
found = true;
|
|
473
|
-
break;
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
if (!found) {
|
|
478
|
-
// it must be in the very last branch...
|
|
479
|
-
if (this._pstFileType == PSTFile.PST_TYPE_ANSI) {
|
|
480
|
-
btreeStartOffset = this.extractLEFileOffset(btreeStartOffset.add((numberOfItems - 1) * 12 + 8));
|
|
481
|
-
this.seek(btreeStartOffset.add(500));
|
|
482
|
-
this.readCompletely(buffer);
|
|
483
|
-
}
|
|
484
|
-
else {
|
|
485
|
-
btreeStartOffset = this.extractLEFileOffset(btreeStartOffset.add((numberOfItems - 1) * 24 + 16));
|
|
486
|
-
this.seek(btreeStartOffset.add(fileTypeAdjustment));
|
|
487
|
-
this.readCompletely(buffer);
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
else {
|
|
492
|
-
// we are at the bottom of the tree...
|
|
493
|
-
// we want to get our file offset!
|
|
494
|
-
for (let x = 0; x < numberOfItems; x++) {
|
|
495
|
-
if (this._pstFileType == PSTFile.PST_TYPE_ANSI) {
|
|
496
|
-
if (descTree) {
|
|
497
|
-
// The 32-bit descriptor index b-tree leaf node item
|
|
498
|
-
buffer = Buffer.alloc(4);
|
|
499
|
-
this.seek(btreeStartOffset.add(x * 16));
|
|
500
|
-
this.readCompletely(buffer);
|
|
501
|
-
if (PSTUtil_class_1.PSTUtil.convertLittleEndianBytesToLong(buffer).equals(index)) {
|
|
502
|
-
// give me the offset index please!
|
|
503
|
-
buffer = Buffer.alloc(16);
|
|
504
|
-
this.seek(btreeStartOffset.add(x * 16));
|
|
505
|
-
this.readCompletely(buffer);
|
|
506
|
-
return buffer;
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
else {
|
|
510
|
-
// The 32-bit (file) offset index item
|
|
511
|
-
const indexIdOfFirstChildNode = this.extractLEFileOffset(btreeStartOffset.add(x * 12));
|
|
512
|
-
if (indexIdOfFirstChildNode.equals(index)) {
|
|
513
|
-
// we found it!
|
|
514
|
-
buffer = Buffer.alloc(12);
|
|
515
|
-
this.seek(btreeStartOffset.add(x * 12));
|
|
516
|
-
this.readCompletely(buffer);
|
|
517
|
-
// console.log('PSTFile::findBtreeItem ' + index.toString() + ' found!');
|
|
518
|
-
return buffer;
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
else {
|
|
523
|
-
if (descTree) {
|
|
524
|
-
// The 64-bit descriptor index b-tree leaf node item
|
|
525
|
-
buffer = Buffer.alloc(4);
|
|
526
|
-
this.seek(btreeStartOffset.add(x * 32));
|
|
527
|
-
this.readCompletely(buffer);
|
|
528
|
-
if (PSTUtil_class_1.PSTUtil.convertLittleEndianBytesToLong(buffer).equals(index)) {
|
|
529
|
-
// give me the offset index please!
|
|
530
|
-
buffer = Buffer.alloc(32);
|
|
531
|
-
this.seek(btreeStartOffset.add(x * 32));
|
|
532
|
-
this.readCompletely(buffer);
|
|
533
|
-
// console.log('PSTFile::findBtreeItem ' + index.toString() + ' found!');
|
|
534
|
-
return buffer;
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
else {
|
|
538
|
-
// The 64-bit (file) offset index item
|
|
539
|
-
const indexIdOfFirstChildNode = this.extractLEFileOffset(btreeStartOffset.add(x * 24));
|
|
540
|
-
if (indexIdOfFirstChildNode.equals(index)) {
|
|
541
|
-
// we found it
|
|
542
|
-
buffer = Buffer.alloc(24);
|
|
543
|
-
this.seek(btreeStartOffset.add(x * 24));
|
|
544
|
-
this.readCompletely(buffer);
|
|
545
|
-
// console.log('PSTFile::findBtreeItem ' + index.toString() + ' found!');
|
|
546
|
-
return buffer;
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
throw new Error('PSTFile::findBtreeItem Unable to find ' +
|
|
552
|
-
index +
|
|
553
|
-
' is desc: ' +
|
|
554
|
-
descTree);
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
throw new Error('PSTFile::findBtreeItem Unable to find node: ' +
|
|
558
|
-
index +
|
|
559
|
-
' is desc: ' +
|
|
560
|
-
descTree);
|
|
561
|
-
}
|
|
562
|
-
/**
|
|
563
|
-
* Get a descriptor index node in the b-tree
|
|
564
|
-
* @param {long} id
|
|
565
|
-
* @returns {DescriptorIndexNode}
|
|
566
|
-
* @memberof PSTFile
|
|
567
|
-
*/
|
|
568
|
-
getDescriptorIndexNode(id) {
|
|
569
|
-
// console.log('PSTFile::getDescriptorIndexNode ' + id.toString())
|
|
570
|
-
return new DescriptorIndexNode_class_1.DescriptorIndexNode(this.findBtreeItem(id, true), this._pstFileType);
|
|
571
|
-
}
|
|
572
|
-
/**
|
|
573
|
-
* Get an offset index node in the b-tree
|
|
574
|
-
* @param {long} id
|
|
575
|
-
* @returns {OffsetIndexItem}
|
|
576
|
-
* @memberof PSTFile
|
|
577
|
-
*/
|
|
578
|
-
getOffsetIndexNode(id) {
|
|
579
|
-
return new OffsetIndexItem_class_1.OffsetIndexItem(this.findBtreeItem(id, false), this._pstFileType);
|
|
580
|
-
}
|
|
581
|
-
getPSTDescriptorItems(arg) {
|
|
582
|
-
let inputStream = arg;
|
|
583
|
-
if (typeof arg === 'object' && arg.hasOwnProperty('low')) {
|
|
584
|
-
inputStream = this.readLeaf(arg);
|
|
585
|
-
}
|
|
586
|
-
// make sure the signature is correct
|
|
587
|
-
inputStream.seek(long_1.default.ZERO);
|
|
588
|
-
const sig = inputStream.read();
|
|
589
|
-
if (sig != 0x2) {
|
|
590
|
-
throw new Error('PSTFile::getPSTDescriptorItems Unable to process descriptor node, bad signature: ' +
|
|
591
|
-
sig);
|
|
592
|
-
}
|
|
593
|
-
const output = new Map();
|
|
594
|
-
const numberOfItems = inputStream.seekAndReadLong(long_1.default.fromValue(2), 2);
|
|
595
|
-
let offset;
|
|
596
|
-
if (this._pstFileType === PSTFile.PST_TYPE_ANSI) {
|
|
597
|
-
offset = 4;
|
|
598
|
-
}
|
|
599
|
-
else {
|
|
600
|
-
offset = 8;
|
|
601
|
-
}
|
|
602
|
-
const data = Buffer.alloc(inputStream.length.toNumber());
|
|
603
|
-
inputStream.seek(long_1.default.ZERO);
|
|
604
|
-
inputStream.readCompletely(data);
|
|
605
|
-
for (let x = 0; x < numberOfItems.toNumber(); x++) {
|
|
606
|
-
const item = new PSTDescriptorItem_class_1.PSTDescriptorItem(data, offset, this);
|
|
607
|
-
output.set(item.descriptorIdentifier, item);
|
|
608
|
-
if (this._pstFileType === PSTFile.PST_TYPE_ANSI) {
|
|
609
|
-
offset += 12;
|
|
610
|
-
}
|
|
611
|
-
else {
|
|
612
|
-
offset += 24;
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
return output;
|
|
616
|
-
}
|
|
617
|
-
/**
|
|
618
|
-
* Build the children descriptor tree, used as a fallback when the nodes
|
|
619
|
-
* that list file contents are broken.
|
|
620
|
-
* @returns
|
|
621
|
-
* @memberof PSTFile
|
|
622
|
-
*/
|
|
623
|
-
getChildDescriptorTree() {
|
|
624
|
-
if (!this.childrenDescriptorTree) {
|
|
625
|
-
let btreeStartOffset = long_1.default.ZERO;
|
|
626
|
-
if (this._pstFileType === PSTFile.PST_TYPE_ANSI) {
|
|
627
|
-
btreeStartOffset = this.extractLEFileOffset(long_1.default.fromValue(188));
|
|
628
|
-
}
|
|
629
|
-
else {
|
|
630
|
-
btreeStartOffset = this.extractLEFileOffset(long_1.default.fromValue(224));
|
|
631
|
-
}
|
|
632
|
-
this.childrenDescriptorTree = new Map();
|
|
633
|
-
this.processDescriptorBTree(btreeStartOffset);
|
|
634
|
-
}
|
|
635
|
-
return this.childrenDescriptorTree;
|
|
636
|
-
}
|
|
637
|
-
/**
|
|
638
|
-
* Recursively walk PST descriptor tree and create internal version.
|
|
639
|
-
* @private
|
|
640
|
-
* @param {long} btreeStartOffset
|
|
641
|
-
* @memberof PSTFile
|
|
642
|
-
*/
|
|
643
|
-
processDescriptorBTree(btreeStartOffset) {
|
|
644
|
-
let fileTypeAdjustment;
|
|
645
|
-
let temp = Buffer.alloc(2);
|
|
646
|
-
if (this._pstFileType === PSTFile.PST_TYPE_ANSI) {
|
|
647
|
-
fileTypeAdjustment = 500;
|
|
648
|
-
}
|
|
649
|
-
else if (this._pstFileType === PSTFile.PST_TYPE_2013_UNICODE) {
|
|
650
|
-
fileTypeAdjustment = 0x1000 - 24;
|
|
651
|
-
}
|
|
652
|
-
else {
|
|
653
|
-
fileTypeAdjustment = 496;
|
|
654
|
-
}
|
|
655
|
-
this.seek(btreeStartOffset.add(fileTypeAdjustment));
|
|
656
|
-
this.readCompletely(temp);
|
|
657
|
-
if (temp[0] == 129 && temp[1] == 129) {
|
|
658
|
-
if (this._pstFileType === PSTFile.PST_TYPE_ANSI) {
|
|
659
|
-
this.seek(btreeStartOffset.add(496));
|
|
660
|
-
}
|
|
661
|
-
else if (this._pstFileType === PSTFile.PST_TYPE_2013_UNICODE) {
|
|
662
|
-
this.seek(btreeStartOffset.add(4056));
|
|
663
|
-
}
|
|
664
|
-
else {
|
|
665
|
-
this.seek(btreeStartOffset.add(488));
|
|
666
|
-
}
|
|
667
|
-
let numberOfItems = 0;
|
|
668
|
-
if (this._pstFileType === PSTFile.PST_TYPE_2013_UNICODE) {
|
|
669
|
-
const numberOfItemsBytes = Buffer.alloc(2);
|
|
670
|
-
this.readCompletely(numberOfItemsBytes);
|
|
671
|
-
numberOfItems = PSTUtil_class_1.PSTUtil.convertLittleEndianBytesToLong(numberOfItemsBytes).toNumber();
|
|
672
|
-
this.readCompletely(numberOfItemsBytes);
|
|
673
|
-
const maxNumberOfItems = PSTUtil_class_1.PSTUtil.convertLittleEndianBytesToLong(numberOfItemsBytes).toNumber();
|
|
674
|
-
}
|
|
675
|
-
else {
|
|
676
|
-
numberOfItems = this.read();
|
|
677
|
-
this.read(); // maxNumberOfItems
|
|
678
|
-
}
|
|
679
|
-
this.read(); // itemSize
|
|
680
|
-
const levelsToLeaf = this.read();
|
|
681
|
-
if (levelsToLeaf > 0) {
|
|
682
|
-
for (let x = 0; x < numberOfItems; x++) {
|
|
683
|
-
if (this._pstFileType === PSTFile.PST_TYPE_ANSI) {
|
|
684
|
-
const branchNodeItemStartIndex = btreeStartOffset.add(12 * x);
|
|
685
|
-
const nextLevelStartsAt = this.extractLEFileOffset(branchNodeItemStartIndex.add(8));
|
|
686
|
-
this.processDescriptorBTree(nextLevelStartsAt);
|
|
687
|
-
}
|
|
688
|
-
else {
|
|
689
|
-
const branchNodeItemStartIndex = btreeStartOffset.add(24 * x);
|
|
690
|
-
const nextLevelStartsAt = this.extractLEFileOffset(branchNodeItemStartIndex.add(16));
|
|
691
|
-
this.processDescriptorBTree(nextLevelStartsAt);
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
else {
|
|
696
|
-
for (let x = 0; x < numberOfItems; x++) {
|
|
697
|
-
if (this._pstFileType === PSTFile.PST_TYPE_ANSI) {
|
|
698
|
-
this.seek(btreeStartOffset.add(x * 16));
|
|
699
|
-
temp = Buffer.alloc(16);
|
|
700
|
-
this.readCompletely(temp);
|
|
701
|
-
}
|
|
702
|
-
else {
|
|
703
|
-
this.seek(btreeStartOffset.add(x * 32));
|
|
704
|
-
temp = Buffer.alloc(32);
|
|
705
|
-
this.readCompletely(temp);
|
|
706
|
-
}
|
|
707
|
-
const tempNode = new DescriptorIndexNode_class_1.DescriptorIndexNode(temp, this._pstFileType);
|
|
708
|
-
// we don't want to be children of ourselves...
|
|
709
|
-
if (tempNode.parentDescriptorIndexIdentifier ==
|
|
710
|
-
tempNode.descriptorIdentifier) {
|
|
711
|
-
// skip!
|
|
712
|
-
}
|
|
713
|
-
else if (this.childrenDescriptorTree.has(tempNode.parentDescriptorIndexIdentifier)) {
|
|
714
|
-
// add this entry to the existing list of children
|
|
715
|
-
const children = this.childrenDescriptorTree.get(tempNode.parentDescriptorIndexIdentifier);
|
|
716
|
-
if (!children) {
|
|
717
|
-
throw new Error('PSTFile::processDescriptorBTree children is null');
|
|
718
|
-
}
|
|
719
|
-
children.push(tempNode);
|
|
720
|
-
}
|
|
721
|
-
else {
|
|
722
|
-
// create a new entry and add this one to that
|
|
723
|
-
const children = [];
|
|
724
|
-
children.push(tempNode);
|
|
725
|
-
this.childrenDescriptorTree.set(tempNode.parentDescriptorIndexIdentifier, children);
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
}
|
|
730
|
-
else {
|
|
731
|
-
throw new Error('PSTFile::processDescriptorBTree Unable to read descriptor node, is not a descriptor');
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
/*
|
|
735
|
-
Basic file functions.
|
|
736
|
-
*/
|
|
737
|
-
/**
|
|
738
|
-
* Read a single byte from the PST file.
|
|
739
|
-
* @param {number} [position]
|
|
740
|
-
* @returns {number}
|
|
741
|
-
* @memberof PSTFile
|
|
742
|
-
*/
|
|
743
|
-
read(position) {
|
|
744
|
-
const pos = position ? position : this.position;
|
|
745
|
-
const buffer = Buffer.alloc(1);
|
|
746
|
-
const bytesRead = this.readSync(buffer, buffer.length, pos);
|
|
747
|
-
this.position = position ? position + bytesRead : this.position + bytesRead;
|
|
748
|
-
return buffer[0];
|
|
749
|
-
}
|
|
750
|
-
/**
|
|
751
|
-
* Read a complete section from the file, storing in the supplied buffer.
|
|
752
|
-
* @param {Buffer} buffer
|
|
753
|
-
* @param {number} [position]
|
|
754
|
-
* @returns
|
|
755
|
-
* @memberof PSTFile
|
|
756
|
-
*/
|
|
757
|
-
readCompletely(buffer, position) {
|
|
758
|
-
const pos = position ? position : this.position;
|
|
759
|
-
const bytesRead = this.readSync(buffer, buffer.length, pos);
|
|
760
|
-
this.position = position ? position + bytesRead : this.position + bytesRead;
|
|
761
|
-
}
|
|
762
|
-
/**
|
|
763
|
-
* Read from either file system, or in memory buffer.
|
|
764
|
-
* @param {Buffer} buffer
|
|
765
|
-
* @param {number} length
|
|
766
|
-
* @param {number} position
|
|
767
|
-
* @returns {number} of bytes read
|
|
768
|
-
* @memberof PSTFile
|
|
769
|
-
*/
|
|
770
|
-
readSync(buffer, length, position) {
|
|
771
|
-
if (this.pstFD > 0) {
|
|
772
|
-
// read from file system
|
|
773
|
-
return fs.readSync(this.pstFD, buffer, 0, length, position);
|
|
774
|
-
}
|
|
775
|
-
else {
|
|
776
|
-
// copy from in-memory buffer
|
|
777
|
-
this.pstBuffer.copy(buffer, 0, position, position + length);
|
|
778
|
-
return length;
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
/**
|
|
782
|
-
* Seek to a specific position in PST file.
|
|
783
|
-
* @param {long} index
|
|
784
|
-
* @memberof PSTFile
|
|
785
|
-
*/
|
|
786
|
-
seek(index) {
|
|
787
|
-
this.position = index.toNumber();
|
|
788
|
-
}
|
|
789
|
-
/**
|
|
790
|
-
* JSON stringify the object properties.
|
|
791
|
-
* @returns {string}
|
|
792
|
-
* @memberof PSTFile
|
|
793
|
-
*/
|
|
794
|
-
toJSON() {
|
|
795
|
-
return this;
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
exports.PSTFile = PSTFile;
|
|
799
|
-
PSTFile.ENCRYPTION_TYPE_NONE = 0;
|
|
800
|
-
PSTFile.ENCRYPTION_TYPE_COMPRESSIBLE = 1;
|
|
801
|
-
PSTFile.MESSAGE_STORE_DESCRIPTOR_IDENTIFIER = 33;
|
|
802
|
-
PSTFile.ROOT_FOLDER_DESCRIPTOR_IDENTIFIER = 290;
|
|
803
|
-
PSTFile.PST_TYPE_ANSI = 14;
|
|
804
|
-
PSTFile.PST_TYPE_ANSI_2 = 15;
|
|
805
|
-
PSTFile.PST_TYPE_UNICODE = 23;
|
|
806
|
-
PSTFile.PST_TYPE_2013_UNICODE = 36;
|
|
807
|
-
PSTFile.PS_PUBLIC_STRINGS = 0;
|
|
808
|
-
PSTFile.PS_INTERNET_HEADERS = 3;
|
|
809
|
-
PSTFile.PSETID_Messaging = 7;
|
|
810
|
-
PSTFile.PSETID_Note = 8;
|
|
811
|
-
PSTFile.PSETID_PostRss = 9;
|
|
812
|
-
PSTFile.PSETID_Task = 10;
|
|
813
|
-
PSTFile.PSETID_UnifiedMessaging = 11;
|
|
814
|
-
PSTFile.PS_MAPI = 12;
|
|
815
|
-
PSTFile.PSETID_AirSync = 13;
|
|
816
|
-
PSTFile.PSETID_Sharing = 14;
|
|
817
|
-
// node tree maps
|
|
818
|
-
PSTFile.nodeMap = new NodeMap_class_1.NodeMap();
|
|
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.PSTFile = void 0;
|
|
30
|
+
const fs = __importStar(require("fs"));
|
|
31
|
+
const long_1 = __importDefault(require("long"));
|
|
32
|
+
const DescriptorIndexNode_class_1 = require("./DescriptorIndexNode.class");
|
|
33
|
+
const OffsetIndexItem_class_1 = require("./OffsetIndexItem.class");
|
|
34
|
+
const PSTDescriptorItem_class_1 = require("./PSTDescriptorItem.class");
|
|
35
|
+
const PSTFolder_class_1 = require("./PSTFolder.class");
|
|
36
|
+
const PSTMessageStore_class_1 = require("./PSTMessageStore.class");
|
|
37
|
+
const PSTNodeInputStream_class_1 = require("./PSTNodeInputStream.class");
|
|
38
|
+
const PSTTableBC_class_1 = require("./PSTTableBC.class");
|
|
39
|
+
const PSTUtil_class_1 = require("./PSTUtil.class");
|
|
40
|
+
const NodeMap_class_1 = require("./NodeMap.class");
|
|
41
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
42
|
+
const uuidparse = require('uuid-parse');
|
|
43
|
+
class PSTFile {
|
|
44
|
+
get encryptionType() {
|
|
45
|
+
return this._encryptionType;
|
|
46
|
+
}
|
|
47
|
+
get pstFileType() {
|
|
48
|
+
return this._pstFileType;
|
|
49
|
+
}
|
|
50
|
+
get pstFilename() {
|
|
51
|
+
return this._pstFilename;
|
|
52
|
+
}
|
|
53
|
+
constructor(arg) {
|
|
54
|
+
this.guidMap = new Map([
|
|
55
|
+
['00020329-0000-0000-C000-000000000046', 0],
|
|
56
|
+
['00062008-0000-0000-C000-000000000046', 1],
|
|
57
|
+
['00062004-0000-0000-C000-000000000046', 2],
|
|
58
|
+
['00020386-0000-0000-C000-000000000046', 3],
|
|
59
|
+
['00062002-0000-0000-C000-000000000046', 4],
|
|
60
|
+
['6ED8DA90-450B-101B-98DA-00AA003F1305', 5],
|
|
61
|
+
['0006200A-0000-0000-C000-000000000046', 6],
|
|
62
|
+
['41F28F13-83F4-4114-A584-EEDB5A6B0BFF', 7],
|
|
63
|
+
['0006200E-0000-0000-C000-000000000046', 8],
|
|
64
|
+
['00062041-0000-0000-C000-000000000046', 9],
|
|
65
|
+
['00062003-0000-0000-C000-000000000046', 10],
|
|
66
|
+
['4442858E-A9E3-4E80-B900-317A210CC15B', 11],
|
|
67
|
+
['00020328-0000-0000-C000-000000000046', 12],
|
|
68
|
+
['71035549-0739-4DCB-9163-00F0580DBBDF', 13],
|
|
69
|
+
['00062040-0000-0000-C000-000000000046', 14],
|
|
70
|
+
]);
|
|
71
|
+
// the type of encryption the files uses
|
|
72
|
+
this._encryptionType = 0;
|
|
73
|
+
// type of file (e.g. ANSI)
|
|
74
|
+
this._pstFileType = 0;
|
|
75
|
+
this._pstFilename = '';
|
|
76
|
+
// b-tree
|
|
77
|
+
this.childrenDescriptorTree = null;
|
|
78
|
+
// in-memory file buffer (instead of filesystem)
|
|
79
|
+
this.pstBuffer = Buffer.alloc(0);
|
|
80
|
+
// position in file
|
|
81
|
+
this.position = 0;
|
|
82
|
+
if (arg instanceof Buffer) {
|
|
83
|
+
// use an in-memory buffer of PST
|
|
84
|
+
this.pstBuffer = arg;
|
|
85
|
+
this.pstFD = -1;
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
// use PST in filesystem
|
|
89
|
+
this._pstFilename = arg;
|
|
90
|
+
this.pstFD = fs.openSync(this._pstFilename, 'r');
|
|
91
|
+
}
|
|
92
|
+
// confirm first 4 bytes are !BDN
|
|
93
|
+
const buffer = Buffer.alloc(514);
|
|
94
|
+
this.readSync(buffer, 514, 0);
|
|
95
|
+
const key = '!BDN';
|
|
96
|
+
if (buffer[0] != key.charCodeAt(0) ||
|
|
97
|
+
buffer[1] != key.charCodeAt(1) ||
|
|
98
|
+
buffer[2] != key.charCodeAt(2) ||
|
|
99
|
+
buffer[3] != key.charCodeAt(3)) {
|
|
100
|
+
throw new Error('PSTFile::open Invalid file header (expected: "!BDN"): ' + buffer);
|
|
101
|
+
}
|
|
102
|
+
// make sure we are using a supported version of a PST...
|
|
103
|
+
if (buffer[10] === PSTFile.PST_TYPE_ANSI_2) {
|
|
104
|
+
buffer[10] = PSTFile.PST_TYPE_ANSI;
|
|
105
|
+
}
|
|
106
|
+
if (buffer[10] !== PSTFile.PST_TYPE_ANSI &&
|
|
107
|
+
buffer[10] !== PSTFile.PST_TYPE_UNICODE &&
|
|
108
|
+
buffer[10] !== PSTFile.PST_TYPE_2013_UNICODE) {
|
|
109
|
+
throw new Error('PSTFile::open Unrecognised PST File version: ' + buffer[10]);
|
|
110
|
+
}
|
|
111
|
+
this._pstFileType = buffer[10];
|
|
112
|
+
// make sure no encryption
|
|
113
|
+
if (this._pstFileType === PSTFile.PST_TYPE_ANSI) {
|
|
114
|
+
this._encryptionType = buffer[461];
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
this._encryptionType = buffer[513];
|
|
118
|
+
}
|
|
119
|
+
if (this._encryptionType === 0x02) {
|
|
120
|
+
throw new Error('PSTFile::open PST is encrypted');
|
|
121
|
+
}
|
|
122
|
+
// build out name to id map
|
|
123
|
+
this.processNameToIDMap();
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Close the file.
|
|
127
|
+
* @memberof PSTFile
|
|
128
|
+
*/
|
|
129
|
+
close() {
|
|
130
|
+
if (this.pstFD > 0) {
|
|
131
|
+
fs.closeSync(this.pstFD);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Process name to ID map.
|
|
136
|
+
* @private
|
|
137
|
+
* @memberof PSTFile
|
|
138
|
+
*/
|
|
139
|
+
processNameToIDMap() {
|
|
140
|
+
const nameToIdMapDescriptorNode = this.getDescriptorIndexNode(long_1.default.fromNumber(97));
|
|
141
|
+
// get the descriptors if we have them
|
|
142
|
+
let localDescriptorItems = null;
|
|
143
|
+
if (nameToIdMapDescriptorNode.localDescriptorsOffsetIndexIdentifier.toNumber() !=
|
|
144
|
+
0) {
|
|
145
|
+
localDescriptorItems = this.getPSTDescriptorItems(nameToIdMapDescriptorNode.localDescriptorsOffsetIndexIdentifier);
|
|
146
|
+
}
|
|
147
|
+
// process the map
|
|
148
|
+
const off = this.getOffsetIndexNode(nameToIdMapDescriptorNode.dataOffsetIndexIdentifier);
|
|
149
|
+
const nodein = new PSTNodeInputStream_class_1.PSTNodeInputStream(this, off);
|
|
150
|
+
const bcTable = new PSTTableBC_class_1.PSTTableBC(nodein);
|
|
151
|
+
const tableItems = bcTable.getItems();
|
|
152
|
+
// Get the guids
|
|
153
|
+
const guidEntry = tableItems.get(2);
|
|
154
|
+
if (!guidEntry) {
|
|
155
|
+
throw new Error('PSTFile::processNameToIDMap guidEntry is null');
|
|
156
|
+
}
|
|
157
|
+
const guids = this.getData(guidEntry, localDescriptorItems);
|
|
158
|
+
const nGuids = Math.trunc(guids.length / 16);
|
|
159
|
+
const guidIndexes = [];
|
|
160
|
+
let offset = 0;
|
|
161
|
+
for (let i = 0; i < nGuids; ++i) {
|
|
162
|
+
let leftQuad = PSTUtil_class_1.PSTUtil.convertLittleEndianBytesToLong(guids, offset, offset + 4);
|
|
163
|
+
leftQuad = leftQuad.shiftLeft(32);
|
|
164
|
+
let midQuad = PSTUtil_class_1.PSTUtil.convertLittleEndianBytesToLong(guids, offset + 4, offset + 6);
|
|
165
|
+
midQuad = midQuad.shiftLeft(16);
|
|
166
|
+
const rightQuad = PSTUtil_class_1.PSTUtil.convertLittleEndianBytesToLong(guids, offset + 6, offset + 8);
|
|
167
|
+
const mostSigBits = leftQuad.or(midQuad).or(rightQuad);
|
|
168
|
+
const leastSigBits = PSTUtil_class_1.PSTUtil.convertBigEndianBytesToLong(guids, offset + 8, offset + 16);
|
|
169
|
+
// weird that need to cast below to any to get tsc error to go away - why?
|
|
170
|
+
// see https://github.com/Microsoft/TypeScript/issues/6436
|
|
171
|
+
const mostBuffer = mostSigBits.toBytes();
|
|
172
|
+
const leastBuffer = leastSigBits.toBytes();
|
|
173
|
+
const arrUID = mostBuffer.concat(leastBuffer);
|
|
174
|
+
const strUID = uuidparse.unparse(arrUID).toUpperCase();
|
|
175
|
+
const guid = this.guidMap.get(strUID);
|
|
176
|
+
if (guid) {
|
|
177
|
+
guidIndexes[i] = guid;
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
guidIndexes[i] = -1; // We don't know this guid
|
|
181
|
+
}
|
|
182
|
+
// console.log('PSTFile:: processNameToIdMap idx: ' + i + ', ' + strUID + ', ' + guidIndexes[i]);
|
|
183
|
+
offset += 16;
|
|
184
|
+
}
|
|
185
|
+
// if we have a reference to an internal descriptor
|
|
186
|
+
const mapEntries = tableItems.get(3);
|
|
187
|
+
if (!mapEntries) {
|
|
188
|
+
throw new Error('PSTFile::processNameToIDMap mapEntries is null');
|
|
189
|
+
}
|
|
190
|
+
const nameToIdByte = this.getData(mapEntries, localDescriptorItems);
|
|
191
|
+
const stringMapEntries = tableItems.get(4);
|
|
192
|
+
if (!stringMapEntries) {
|
|
193
|
+
throw new Error('PSTFile::processNameToIDMap stringMapEntries is null');
|
|
194
|
+
}
|
|
195
|
+
const stringNameToIdByte = this.getData(stringMapEntries, localDescriptorItems);
|
|
196
|
+
// process the entries
|
|
197
|
+
for (let x = 0; x + 8 < nameToIdByte.length; x += 8) {
|
|
198
|
+
const key = PSTUtil_class_1.PSTUtil.convertLittleEndianBytesToLong(nameToIdByte, x, x + 4).toNumber();
|
|
199
|
+
let guid = PSTUtil_class_1.PSTUtil.convertLittleEndianBytesToLong(nameToIdByte, x + 4, x + 6).toNumber();
|
|
200
|
+
let propId = PSTUtil_class_1.PSTUtil.convertLittleEndianBytesToLong(nameToIdByte, x + 6, x + 8).toNumber();
|
|
201
|
+
if ((guid & 0x0001) == 0) {
|
|
202
|
+
// identifier is numeric
|
|
203
|
+
propId += 0x8000;
|
|
204
|
+
guid >>= 1;
|
|
205
|
+
let guidIndex;
|
|
206
|
+
if (guid == 1) {
|
|
207
|
+
guidIndex = PSTFile.PS_MAPI;
|
|
208
|
+
}
|
|
209
|
+
else if (guid == 2) {
|
|
210
|
+
guidIndex = PSTFile.PS_PUBLIC_STRINGS;
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
guidIndex = guidIndexes[guid - 3];
|
|
214
|
+
}
|
|
215
|
+
PSTFile.nodeMap.setId(key, propId, guidIndex);
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
// identifier is a string
|
|
219
|
+
// key is byte offset into the String stream in which the string name of the property is stored.
|
|
220
|
+
const len = PSTUtil_class_1.PSTUtil.convertLittleEndianBytesToLong(stringNameToIdByte, key, key + 4).toNumber();
|
|
221
|
+
const keyByteValue = Buffer.alloc(len);
|
|
222
|
+
PSTUtil_class_1.PSTUtil.arraycopy(stringNameToIdByte, key + 4, keyByteValue, 0, keyByteValue.length);
|
|
223
|
+
propId += 0x8000;
|
|
224
|
+
PSTFile.nodeMap.setId(keyByteValue.toString('utf16le').replace(/\0/g, ''), propId);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Get data from a descriptor and store in buffer.
|
|
230
|
+
* @private
|
|
231
|
+
* @param {PSTTableItem} item
|
|
232
|
+
* @param {Map<number, PSTDescriptorItem>} localDescriptorItems
|
|
233
|
+
* @returns {Buffer}
|
|
234
|
+
* @memberof PSTFile
|
|
235
|
+
*/
|
|
236
|
+
getData(item, localDescriptorItems) {
|
|
237
|
+
if (item.data.length != 0) {
|
|
238
|
+
return item.data;
|
|
239
|
+
}
|
|
240
|
+
if (localDescriptorItems == null) {
|
|
241
|
+
throw new Error('PSTFile::getData External reference but no localDescriptorItems in PSTFile.getData()');
|
|
242
|
+
}
|
|
243
|
+
if (item.entryValueType != 0x0102) {
|
|
244
|
+
throw new Error('PSTFile::getData Attempting to get non-binary data in PSTFile.getData()');
|
|
245
|
+
}
|
|
246
|
+
const mapDescriptorItem = localDescriptorItems.get(item.entryValueReference);
|
|
247
|
+
if (mapDescriptorItem == null) {
|
|
248
|
+
throw new Error('PSTFile::getData Descriptor not found: ' + item.entryValueReference);
|
|
249
|
+
}
|
|
250
|
+
return mapDescriptorItem.getData();
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Get name to ID map item.
|
|
254
|
+
* @param {number} key
|
|
255
|
+
* @param {number} idx
|
|
256
|
+
* @returns {number}
|
|
257
|
+
* @memberof PSTFile
|
|
258
|
+
*/
|
|
259
|
+
getNameToIdMapItem(key, idx) {
|
|
260
|
+
return PSTFile.nodeMap.getId(key, idx);
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Get public string to id map item.
|
|
264
|
+
* @static
|
|
265
|
+
* @param {string} key
|
|
266
|
+
* @returns {number}
|
|
267
|
+
* @memberof PSTFile
|
|
268
|
+
*/
|
|
269
|
+
static getPublicStringToIdMapItem(key) {
|
|
270
|
+
return PSTFile.nodeMap.getId(key);
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Get property name from id.
|
|
274
|
+
* @static
|
|
275
|
+
* @param {number} propertyId
|
|
276
|
+
* @param {boolean} bNamed
|
|
277
|
+
* @returns {string}
|
|
278
|
+
* @memberof PSTFile
|
|
279
|
+
*/
|
|
280
|
+
static getPropertyName(propertyId, bNamed) {
|
|
281
|
+
return PSTUtil_class_1.PSTUtil.propertyName.get(propertyId);
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Get name to id map key.
|
|
285
|
+
* @static
|
|
286
|
+
* @param {number} propId
|
|
287
|
+
* @returns {long}
|
|
288
|
+
* @memberof PSTFile
|
|
289
|
+
*/
|
|
290
|
+
static getNameToIdMapKey(propId) {
|
|
291
|
+
return PSTFile.nodeMap.getNumericName(propId);
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Get the message store of the PST file. Note that this doesn't really
|
|
295
|
+
* have much information, better to look under the root folder.
|
|
296
|
+
* @returns {PSTMessageStore}
|
|
297
|
+
* @memberof PSTFile
|
|
298
|
+
*/
|
|
299
|
+
getMessageStore() {
|
|
300
|
+
const messageStoreDescriptor = this.getDescriptorIndexNode(long_1.default.fromNumber(PSTFile.MESSAGE_STORE_DESCRIPTOR_IDENTIFIER));
|
|
301
|
+
return new PSTMessageStore_class_1.PSTMessageStore(this, messageStoreDescriptor);
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Get the root folder for the PST file
|
|
305
|
+
* @returns {PSTFolder}
|
|
306
|
+
* @memberof PSTFile
|
|
307
|
+
*/
|
|
308
|
+
getRootFolder() {
|
|
309
|
+
const rootFolderDescriptor = this.getDescriptorIndexNode(long_1.default.fromValue(PSTFile.ROOT_FOLDER_DESCRIPTOR_IDENTIFIER));
|
|
310
|
+
const output = new PSTFolder_class_1.PSTFolder(this, rootFolderDescriptor);
|
|
311
|
+
return output;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Read a leaf in the b-tree.
|
|
315
|
+
* @param {long} bid
|
|
316
|
+
* @returns {PSTNodeInputStream}
|
|
317
|
+
* @memberof PSTFile
|
|
318
|
+
*/
|
|
319
|
+
readLeaf(bid) {
|
|
320
|
+
// get the index node for the descriptor index
|
|
321
|
+
const offsetItem = this.getOffsetIndexNode(bid);
|
|
322
|
+
return new PSTNodeInputStream_class_1.PSTNodeInputStream(this, offsetItem);
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Read the size of the specified leaf.
|
|
326
|
+
* @param {long} bid
|
|
327
|
+
* @returns {number}
|
|
328
|
+
* @memberof PSTFile
|
|
329
|
+
*/
|
|
330
|
+
getLeafSize(bid) {
|
|
331
|
+
const offsetItem = this.getOffsetIndexNode(bid);
|
|
332
|
+
// Internal block?
|
|
333
|
+
if ((offsetItem.indexIdentifier.toNumber() & 0x02) == 0) {
|
|
334
|
+
// No, return the raw size
|
|
335
|
+
return offsetItem.size;
|
|
336
|
+
}
|
|
337
|
+
// we only need the first 8 bytes
|
|
338
|
+
const data = Buffer.alloc(8);
|
|
339
|
+
this.seek(offsetItem.fileOffset);
|
|
340
|
+
this.readCompletely(data);
|
|
341
|
+
// we are an array, get the sum of the sizes...
|
|
342
|
+
return PSTUtil_class_1.PSTUtil.convertLittleEndianBytesToLong(data, 4, 8).toNumber();
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Get file offset, which is sorted in 8 little endian bytes
|
|
346
|
+
* @private
|
|
347
|
+
* @param {long} startOffset
|
|
348
|
+
* @returns {long}
|
|
349
|
+
* @memberof PSTFile
|
|
350
|
+
*/
|
|
351
|
+
extractLEFileOffset(startOffset) {
|
|
352
|
+
let offset = long_1.default.ZERO;
|
|
353
|
+
if (this._pstFileType == PSTFile.PST_TYPE_ANSI) {
|
|
354
|
+
this.seek(startOffset);
|
|
355
|
+
const buffer = Buffer.alloc(4);
|
|
356
|
+
this.readCompletely(buffer);
|
|
357
|
+
offset = offset.or(buffer[3] & 0xff);
|
|
358
|
+
offset = offset.shiftLeft(8);
|
|
359
|
+
offset = offset.or(buffer[2] & 0xff);
|
|
360
|
+
offset = offset.shiftLeft(8);
|
|
361
|
+
offset = offset.or(buffer[1] & 0xff);
|
|
362
|
+
offset = offset.shiftLeft(8);
|
|
363
|
+
offset = offset.or(buffer[0] & 0xff);
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
this.seek(startOffset);
|
|
367
|
+
const buffer = Buffer.alloc(8);
|
|
368
|
+
this.readCompletely(buffer);
|
|
369
|
+
offset = offset.or(buffer[7] & 0xff);
|
|
370
|
+
let tmpLongValue;
|
|
371
|
+
for (let x = 6; x >= 0; x--) {
|
|
372
|
+
offset = offset.shiftLeft(8);
|
|
373
|
+
tmpLongValue = buffer[x] & 0xff;
|
|
374
|
+
offset = offset.or(tmpLongValue);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
return offset;
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Navigate PST B-tree and find a specific item.
|
|
381
|
+
* @private
|
|
382
|
+
* @param {long} index
|
|
383
|
+
* @param {boolean} descTree
|
|
384
|
+
* @returns {Buffer}
|
|
385
|
+
* @memberof PSTFile
|
|
386
|
+
*/
|
|
387
|
+
findBtreeItem(index, descTree) {
|
|
388
|
+
let btreeStartOffset;
|
|
389
|
+
let fileTypeAdjustment;
|
|
390
|
+
// first find the starting point for the offset index
|
|
391
|
+
if (this._pstFileType === PSTFile.PST_TYPE_ANSI) {
|
|
392
|
+
btreeStartOffset = this.extractLEFileOffset(long_1.default.fromValue(196));
|
|
393
|
+
if (descTree) {
|
|
394
|
+
btreeStartOffset = this.extractLEFileOffset(long_1.default.fromValue(188));
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
btreeStartOffset = this.extractLEFileOffset(long_1.default.fromValue(240));
|
|
399
|
+
if (descTree) {
|
|
400
|
+
btreeStartOffset = this.extractLEFileOffset(long_1.default.fromValue(224));
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
// okay, what we want to do is navigate the tree until you reach the bottom....
|
|
404
|
+
// try and read the index b-tree
|
|
405
|
+
let buffer = Buffer.alloc(2);
|
|
406
|
+
if (this._pstFileType === PSTFile.PST_TYPE_ANSI) {
|
|
407
|
+
fileTypeAdjustment = 500;
|
|
408
|
+
}
|
|
409
|
+
else if (this._pstFileType === PSTFile.PST_TYPE_2013_UNICODE) {
|
|
410
|
+
fileTypeAdjustment = 0x1000 - 24;
|
|
411
|
+
}
|
|
412
|
+
else {
|
|
413
|
+
fileTypeAdjustment = 496;
|
|
414
|
+
}
|
|
415
|
+
this.seek(btreeStartOffset.add(fileTypeAdjustment));
|
|
416
|
+
this.readCompletely(buffer);
|
|
417
|
+
const b2 = Buffer.alloc(2);
|
|
418
|
+
b2[0] = 0xff80;
|
|
419
|
+
b2[1] = 0xff81;
|
|
420
|
+
// ensure apples to apples comparison
|
|
421
|
+
while ((buffer[0] === b2[0] && buffer[1] === b2[0] && !descTree) ||
|
|
422
|
+
(buffer[0] === b2[1] && buffer[1] === b2[1] && descTree)) {
|
|
423
|
+
// get the rest of the data
|
|
424
|
+
let len;
|
|
425
|
+
if (this._pstFileType == PSTFile.PST_TYPE_ANSI) {
|
|
426
|
+
len = 496;
|
|
427
|
+
}
|
|
428
|
+
else if (this._pstFileType == PSTFile.PST_TYPE_2013_UNICODE) {
|
|
429
|
+
len = 4056;
|
|
430
|
+
}
|
|
431
|
+
else {
|
|
432
|
+
len = 488;
|
|
433
|
+
}
|
|
434
|
+
const branchNodeItems = Buffer.alloc(len);
|
|
435
|
+
this.seek(btreeStartOffset);
|
|
436
|
+
this.readCompletely(branchNodeItems);
|
|
437
|
+
// console.log('PSTFile::findBtreeItem btreeStartOffset = ' + btreeStartOffset);
|
|
438
|
+
let numberOfItems = 0;
|
|
439
|
+
if (this._pstFileType === PSTFile.PST_TYPE_2013_UNICODE) {
|
|
440
|
+
const numberOfItemsBytes = Buffer.alloc(2);
|
|
441
|
+
this.readCompletely(numberOfItemsBytes);
|
|
442
|
+
numberOfItems = PSTUtil_class_1.PSTUtil.convertLittleEndianBytesToLong(numberOfItemsBytes).toNumber();
|
|
443
|
+
this.readCompletely(numberOfItemsBytes);
|
|
444
|
+
}
|
|
445
|
+
else {
|
|
446
|
+
numberOfItems = this.read();
|
|
447
|
+
this.read(); // maxNumberOfItems
|
|
448
|
+
}
|
|
449
|
+
const itemSize = this.read(); // itemSize
|
|
450
|
+
const levelsToLeaf = this.read();
|
|
451
|
+
if (levelsToLeaf > 0) {
|
|
452
|
+
let found = false;
|
|
453
|
+
for (let x = 0; x < numberOfItems; x++) {
|
|
454
|
+
if (this._pstFileType == PSTFile.PST_TYPE_ANSI) {
|
|
455
|
+
const indexIdOfFirstChildNode = this.extractLEFileOffset(btreeStartOffset.add(x * 12));
|
|
456
|
+
if (indexIdOfFirstChildNode > index) {
|
|
457
|
+
// get the address for the child first node in this group
|
|
458
|
+
btreeStartOffset = this.extractLEFileOffset(btreeStartOffset.add((x - 1) * 12 + 8));
|
|
459
|
+
this.seek(btreeStartOffset.add(500));
|
|
460
|
+
this.readCompletely(buffer);
|
|
461
|
+
found = true;
|
|
462
|
+
break;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
else {
|
|
466
|
+
const indexIdOfFirstChildNode = this.extractLEFileOffset(btreeStartOffset.add(x * 24));
|
|
467
|
+
if (indexIdOfFirstChildNode.greaterThan(index)) {
|
|
468
|
+
// get the address for the child first node in this group
|
|
469
|
+
btreeStartOffset = this.extractLEFileOffset(btreeStartOffset.add((x - 1) * 24 + 16));
|
|
470
|
+
this.seek(btreeStartOffset.add(fileTypeAdjustment));
|
|
471
|
+
this.readCompletely(buffer);
|
|
472
|
+
found = true;
|
|
473
|
+
break;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
if (!found) {
|
|
478
|
+
// it must be in the very last branch...
|
|
479
|
+
if (this._pstFileType == PSTFile.PST_TYPE_ANSI) {
|
|
480
|
+
btreeStartOffset = this.extractLEFileOffset(btreeStartOffset.add((numberOfItems - 1) * 12 + 8));
|
|
481
|
+
this.seek(btreeStartOffset.add(500));
|
|
482
|
+
this.readCompletely(buffer);
|
|
483
|
+
}
|
|
484
|
+
else {
|
|
485
|
+
btreeStartOffset = this.extractLEFileOffset(btreeStartOffset.add((numberOfItems - 1) * 24 + 16));
|
|
486
|
+
this.seek(btreeStartOffset.add(fileTypeAdjustment));
|
|
487
|
+
this.readCompletely(buffer);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
else {
|
|
492
|
+
// we are at the bottom of the tree...
|
|
493
|
+
// we want to get our file offset!
|
|
494
|
+
for (let x = 0; x < numberOfItems; x++) {
|
|
495
|
+
if (this._pstFileType == PSTFile.PST_TYPE_ANSI) {
|
|
496
|
+
if (descTree) {
|
|
497
|
+
// The 32-bit descriptor index b-tree leaf node item
|
|
498
|
+
buffer = Buffer.alloc(4);
|
|
499
|
+
this.seek(btreeStartOffset.add(x * 16));
|
|
500
|
+
this.readCompletely(buffer);
|
|
501
|
+
if (PSTUtil_class_1.PSTUtil.convertLittleEndianBytesToLong(buffer).equals(index)) {
|
|
502
|
+
// give me the offset index please!
|
|
503
|
+
buffer = Buffer.alloc(16);
|
|
504
|
+
this.seek(btreeStartOffset.add(x * 16));
|
|
505
|
+
this.readCompletely(buffer);
|
|
506
|
+
return buffer;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
else {
|
|
510
|
+
// The 32-bit (file) offset index item
|
|
511
|
+
const indexIdOfFirstChildNode = this.extractLEFileOffset(btreeStartOffset.add(x * 12));
|
|
512
|
+
if (indexIdOfFirstChildNode.equals(index)) {
|
|
513
|
+
// we found it!
|
|
514
|
+
buffer = Buffer.alloc(12);
|
|
515
|
+
this.seek(btreeStartOffset.add(x * 12));
|
|
516
|
+
this.readCompletely(buffer);
|
|
517
|
+
// console.log('PSTFile::findBtreeItem ' + index.toString() + ' found!');
|
|
518
|
+
return buffer;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
else {
|
|
523
|
+
if (descTree) {
|
|
524
|
+
// The 64-bit descriptor index b-tree leaf node item
|
|
525
|
+
buffer = Buffer.alloc(4);
|
|
526
|
+
this.seek(btreeStartOffset.add(x * 32));
|
|
527
|
+
this.readCompletely(buffer);
|
|
528
|
+
if (PSTUtil_class_1.PSTUtil.convertLittleEndianBytesToLong(buffer).equals(index)) {
|
|
529
|
+
// give me the offset index please!
|
|
530
|
+
buffer = Buffer.alloc(32);
|
|
531
|
+
this.seek(btreeStartOffset.add(x * 32));
|
|
532
|
+
this.readCompletely(buffer);
|
|
533
|
+
// console.log('PSTFile::findBtreeItem ' + index.toString() + ' found!');
|
|
534
|
+
return buffer;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
else {
|
|
538
|
+
// The 64-bit (file) offset index item
|
|
539
|
+
const indexIdOfFirstChildNode = this.extractLEFileOffset(btreeStartOffset.add(x * 24));
|
|
540
|
+
if (indexIdOfFirstChildNode.equals(index)) {
|
|
541
|
+
// we found it
|
|
542
|
+
buffer = Buffer.alloc(24);
|
|
543
|
+
this.seek(btreeStartOffset.add(x * 24));
|
|
544
|
+
this.readCompletely(buffer);
|
|
545
|
+
// console.log('PSTFile::findBtreeItem ' + index.toString() + ' found!');
|
|
546
|
+
return buffer;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
throw new Error('PSTFile::findBtreeItem Unable to find ' +
|
|
552
|
+
index +
|
|
553
|
+
' is desc: ' +
|
|
554
|
+
descTree);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
throw new Error('PSTFile::findBtreeItem Unable to find node: ' +
|
|
558
|
+
index +
|
|
559
|
+
' is desc: ' +
|
|
560
|
+
descTree);
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Get a descriptor index node in the b-tree
|
|
564
|
+
* @param {long} id
|
|
565
|
+
* @returns {DescriptorIndexNode}
|
|
566
|
+
* @memberof PSTFile
|
|
567
|
+
*/
|
|
568
|
+
getDescriptorIndexNode(id) {
|
|
569
|
+
// console.log('PSTFile::getDescriptorIndexNode ' + id.toString())
|
|
570
|
+
return new DescriptorIndexNode_class_1.DescriptorIndexNode(this.findBtreeItem(id, true), this._pstFileType);
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* Get an offset index node in the b-tree
|
|
574
|
+
* @param {long} id
|
|
575
|
+
* @returns {OffsetIndexItem}
|
|
576
|
+
* @memberof PSTFile
|
|
577
|
+
*/
|
|
578
|
+
getOffsetIndexNode(id) {
|
|
579
|
+
return new OffsetIndexItem_class_1.OffsetIndexItem(this.findBtreeItem(id, false), this._pstFileType);
|
|
580
|
+
}
|
|
581
|
+
getPSTDescriptorItems(arg) {
|
|
582
|
+
let inputStream = arg;
|
|
583
|
+
if (typeof arg === 'object' && arg.hasOwnProperty('low')) {
|
|
584
|
+
inputStream = this.readLeaf(arg);
|
|
585
|
+
}
|
|
586
|
+
// make sure the signature is correct
|
|
587
|
+
inputStream.seek(long_1.default.ZERO);
|
|
588
|
+
const sig = inputStream.read();
|
|
589
|
+
if (sig != 0x2) {
|
|
590
|
+
throw new Error('PSTFile::getPSTDescriptorItems Unable to process descriptor node, bad signature: ' +
|
|
591
|
+
sig);
|
|
592
|
+
}
|
|
593
|
+
const output = new Map();
|
|
594
|
+
const numberOfItems = inputStream.seekAndReadLong(long_1.default.fromValue(2), 2);
|
|
595
|
+
let offset;
|
|
596
|
+
if (this._pstFileType === PSTFile.PST_TYPE_ANSI) {
|
|
597
|
+
offset = 4;
|
|
598
|
+
}
|
|
599
|
+
else {
|
|
600
|
+
offset = 8;
|
|
601
|
+
}
|
|
602
|
+
const data = Buffer.alloc(inputStream.length.toNumber());
|
|
603
|
+
inputStream.seek(long_1.default.ZERO);
|
|
604
|
+
inputStream.readCompletely(data);
|
|
605
|
+
for (let x = 0; x < numberOfItems.toNumber(); x++) {
|
|
606
|
+
const item = new PSTDescriptorItem_class_1.PSTDescriptorItem(data, offset, this);
|
|
607
|
+
output.set(item.descriptorIdentifier, item);
|
|
608
|
+
if (this._pstFileType === PSTFile.PST_TYPE_ANSI) {
|
|
609
|
+
offset += 12;
|
|
610
|
+
}
|
|
611
|
+
else {
|
|
612
|
+
offset += 24;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
return output;
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* Build the children descriptor tree, used as a fallback when the nodes
|
|
619
|
+
* that list file contents are broken.
|
|
620
|
+
* @returns
|
|
621
|
+
* @memberof PSTFile
|
|
622
|
+
*/
|
|
623
|
+
getChildDescriptorTree() {
|
|
624
|
+
if (!this.childrenDescriptorTree) {
|
|
625
|
+
let btreeStartOffset = long_1.default.ZERO;
|
|
626
|
+
if (this._pstFileType === PSTFile.PST_TYPE_ANSI) {
|
|
627
|
+
btreeStartOffset = this.extractLEFileOffset(long_1.default.fromValue(188));
|
|
628
|
+
}
|
|
629
|
+
else {
|
|
630
|
+
btreeStartOffset = this.extractLEFileOffset(long_1.default.fromValue(224));
|
|
631
|
+
}
|
|
632
|
+
this.childrenDescriptorTree = new Map();
|
|
633
|
+
this.processDescriptorBTree(btreeStartOffset);
|
|
634
|
+
}
|
|
635
|
+
return this.childrenDescriptorTree;
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Recursively walk PST descriptor tree and create internal version.
|
|
639
|
+
* @private
|
|
640
|
+
* @param {long} btreeStartOffset
|
|
641
|
+
* @memberof PSTFile
|
|
642
|
+
*/
|
|
643
|
+
processDescriptorBTree(btreeStartOffset) {
|
|
644
|
+
let fileTypeAdjustment;
|
|
645
|
+
let temp = Buffer.alloc(2);
|
|
646
|
+
if (this._pstFileType === PSTFile.PST_TYPE_ANSI) {
|
|
647
|
+
fileTypeAdjustment = 500;
|
|
648
|
+
}
|
|
649
|
+
else if (this._pstFileType === PSTFile.PST_TYPE_2013_UNICODE) {
|
|
650
|
+
fileTypeAdjustment = 0x1000 - 24;
|
|
651
|
+
}
|
|
652
|
+
else {
|
|
653
|
+
fileTypeAdjustment = 496;
|
|
654
|
+
}
|
|
655
|
+
this.seek(btreeStartOffset.add(fileTypeAdjustment));
|
|
656
|
+
this.readCompletely(temp);
|
|
657
|
+
if (temp[0] == 129 && temp[1] == 129) {
|
|
658
|
+
if (this._pstFileType === PSTFile.PST_TYPE_ANSI) {
|
|
659
|
+
this.seek(btreeStartOffset.add(496));
|
|
660
|
+
}
|
|
661
|
+
else if (this._pstFileType === PSTFile.PST_TYPE_2013_UNICODE) {
|
|
662
|
+
this.seek(btreeStartOffset.add(4056));
|
|
663
|
+
}
|
|
664
|
+
else {
|
|
665
|
+
this.seek(btreeStartOffset.add(488));
|
|
666
|
+
}
|
|
667
|
+
let numberOfItems = 0;
|
|
668
|
+
if (this._pstFileType === PSTFile.PST_TYPE_2013_UNICODE) {
|
|
669
|
+
const numberOfItemsBytes = Buffer.alloc(2);
|
|
670
|
+
this.readCompletely(numberOfItemsBytes);
|
|
671
|
+
numberOfItems = PSTUtil_class_1.PSTUtil.convertLittleEndianBytesToLong(numberOfItemsBytes).toNumber();
|
|
672
|
+
this.readCompletely(numberOfItemsBytes);
|
|
673
|
+
const maxNumberOfItems = PSTUtil_class_1.PSTUtil.convertLittleEndianBytesToLong(numberOfItemsBytes).toNumber();
|
|
674
|
+
}
|
|
675
|
+
else {
|
|
676
|
+
numberOfItems = this.read();
|
|
677
|
+
this.read(); // maxNumberOfItems
|
|
678
|
+
}
|
|
679
|
+
this.read(); // itemSize
|
|
680
|
+
const levelsToLeaf = this.read();
|
|
681
|
+
if (levelsToLeaf > 0) {
|
|
682
|
+
for (let x = 0; x < numberOfItems; x++) {
|
|
683
|
+
if (this._pstFileType === PSTFile.PST_TYPE_ANSI) {
|
|
684
|
+
const branchNodeItemStartIndex = btreeStartOffset.add(12 * x);
|
|
685
|
+
const nextLevelStartsAt = this.extractLEFileOffset(branchNodeItemStartIndex.add(8));
|
|
686
|
+
this.processDescriptorBTree(nextLevelStartsAt);
|
|
687
|
+
}
|
|
688
|
+
else {
|
|
689
|
+
const branchNodeItemStartIndex = btreeStartOffset.add(24 * x);
|
|
690
|
+
const nextLevelStartsAt = this.extractLEFileOffset(branchNodeItemStartIndex.add(16));
|
|
691
|
+
this.processDescriptorBTree(nextLevelStartsAt);
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
else {
|
|
696
|
+
for (let x = 0; x < numberOfItems; x++) {
|
|
697
|
+
if (this._pstFileType === PSTFile.PST_TYPE_ANSI) {
|
|
698
|
+
this.seek(btreeStartOffset.add(x * 16));
|
|
699
|
+
temp = Buffer.alloc(16);
|
|
700
|
+
this.readCompletely(temp);
|
|
701
|
+
}
|
|
702
|
+
else {
|
|
703
|
+
this.seek(btreeStartOffset.add(x * 32));
|
|
704
|
+
temp = Buffer.alloc(32);
|
|
705
|
+
this.readCompletely(temp);
|
|
706
|
+
}
|
|
707
|
+
const tempNode = new DescriptorIndexNode_class_1.DescriptorIndexNode(temp, this._pstFileType);
|
|
708
|
+
// we don't want to be children of ourselves...
|
|
709
|
+
if (tempNode.parentDescriptorIndexIdentifier ==
|
|
710
|
+
tempNode.descriptorIdentifier) {
|
|
711
|
+
// skip!
|
|
712
|
+
}
|
|
713
|
+
else if (this.childrenDescriptorTree.has(tempNode.parentDescriptorIndexIdentifier)) {
|
|
714
|
+
// add this entry to the existing list of children
|
|
715
|
+
const children = this.childrenDescriptorTree.get(tempNode.parentDescriptorIndexIdentifier);
|
|
716
|
+
if (!children) {
|
|
717
|
+
throw new Error('PSTFile::processDescriptorBTree children is null');
|
|
718
|
+
}
|
|
719
|
+
children.push(tempNode);
|
|
720
|
+
}
|
|
721
|
+
else {
|
|
722
|
+
// create a new entry and add this one to that
|
|
723
|
+
const children = [];
|
|
724
|
+
children.push(tempNode);
|
|
725
|
+
this.childrenDescriptorTree.set(tempNode.parentDescriptorIndexIdentifier, children);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
else {
|
|
731
|
+
throw new Error('PSTFile::processDescriptorBTree Unable to read descriptor node, is not a descriptor');
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
/*
|
|
735
|
+
Basic file functions.
|
|
736
|
+
*/
|
|
737
|
+
/**
|
|
738
|
+
* Read a single byte from the PST file.
|
|
739
|
+
* @param {number} [position]
|
|
740
|
+
* @returns {number}
|
|
741
|
+
* @memberof PSTFile
|
|
742
|
+
*/
|
|
743
|
+
read(position) {
|
|
744
|
+
const pos = position ? position : this.position;
|
|
745
|
+
const buffer = Buffer.alloc(1);
|
|
746
|
+
const bytesRead = this.readSync(buffer, buffer.length, pos);
|
|
747
|
+
this.position = position ? position + bytesRead : this.position + bytesRead;
|
|
748
|
+
return buffer[0];
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Read a complete section from the file, storing in the supplied buffer.
|
|
752
|
+
* @param {Buffer} buffer
|
|
753
|
+
* @param {number} [position]
|
|
754
|
+
* @returns
|
|
755
|
+
* @memberof PSTFile
|
|
756
|
+
*/
|
|
757
|
+
readCompletely(buffer, position) {
|
|
758
|
+
const pos = position ? position : this.position;
|
|
759
|
+
const bytesRead = this.readSync(buffer, buffer.length, pos);
|
|
760
|
+
this.position = position ? position + bytesRead : this.position + bytesRead;
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* Read from either file system, or in memory buffer.
|
|
764
|
+
* @param {Buffer} buffer
|
|
765
|
+
* @param {number} length
|
|
766
|
+
* @param {number} position
|
|
767
|
+
* @returns {number} of bytes read
|
|
768
|
+
* @memberof PSTFile
|
|
769
|
+
*/
|
|
770
|
+
readSync(buffer, length, position) {
|
|
771
|
+
if (this.pstFD > 0) {
|
|
772
|
+
// read from file system
|
|
773
|
+
return fs.readSync(this.pstFD, buffer, 0, length, position);
|
|
774
|
+
}
|
|
775
|
+
else {
|
|
776
|
+
// copy from in-memory buffer
|
|
777
|
+
this.pstBuffer.copy(buffer, 0, position, position + length);
|
|
778
|
+
return length;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
/**
|
|
782
|
+
* Seek to a specific position in PST file.
|
|
783
|
+
* @param {long} index
|
|
784
|
+
* @memberof PSTFile
|
|
785
|
+
*/
|
|
786
|
+
seek(index) {
|
|
787
|
+
this.position = index.toNumber();
|
|
788
|
+
}
|
|
789
|
+
/**
|
|
790
|
+
* JSON stringify the object properties.
|
|
791
|
+
* @returns {string}
|
|
792
|
+
* @memberof PSTFile
|
|
793
|
+
*/
|
|
794
|
+
toJSON() {
|
|
795
|
+
return this;
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
exports.PSTFile = PSTFile;
|
|
799
|
+
PSTFile.ENCRYPTION_TYPE_NONE = 0;
|
|
800
|
+
PSTFile.ENCRYPTION_TYPE_COMPRESSIBLE = 1;
|
|
801
|
+
PSTFile.MESSAGE_STORE_DESCRIPTOR_IDENTIFIER = 33;
|
|
802
|
+
PSTFile.ROOT_FOLDER_DESCRIPTOR_IDENTIFIER = 290;
|
|
803
|
+
PSTFile.PST_TYPE_ANSI = 14;
|
|
804
|
+
PSTFile.PST_TYPE_ANSI_2 = 15;
|
|
805
|
+
PSTFile.PST_TYPE_UNICODE = 23;
|
|
806
|
+
PSTFile.PST_TYPE_2013_UNICODE = 36;
|
|
807
|
+
PSTFile.PS_PUBLIC_STRINGS = 0;
|
|
808
|
+
PSTFile.PS_INTERNET_HEADERS = 3;
|
|
809
|
+
PSTFile.PSETID_Messaging = 7;
|
|
810
|
+
PSTFile.PSETID_Note = 8;
|
|
811
|
+
PSTFile.PSETID_PostRss = 9;
|
|
812
|
+
PSTFile.PSETID_Task = 10;
|
|
813
|
+
PSTFile.PSETID_UnifiedMessaging = 11;
|
|
814
|
+
PSTFile.PS_MAPI = 12;
|
|
815
|
+
PSTFile.PSETID_AirSync = 13;
|
|
816
|
+
PSTFile.PSETID_Sharing = 14;
|
|
817
|
+
// node tree maps
|
|
818
|
+
PSTFile.nodeMap = new NodeMap_class_1.NodeMap();
|