@kenjiuno/msgreader
Advanced tools
Comparing version 1.6.6 to 1.7.0
@@ -170,2 +170,11 @@ const program = require('commander'); | ||
program | ||
.command('dummy2') | ||
.action(() => { | ||
const msgFileBuffer = fs.readFileSync('test/voteItems.msg'); | ||
const testMsg = new MsgReader(msgFileBuffer); | ||
const testMsgInfo = testMsg.getFileData(); | ||
console.log(testMsgInfo); | ||
}); | ||
program | ||
.parse(process.argv); |
@@ -61,2 +61,3 @@ declare const _default: { | ||
'7ffe': string; | ||
'0c15': string; | ||
'3001': string; | ||
@@ -67,2 +68,12 @@ '3002': string; | ||
}; | ||
PIDLID_MAPPING: { | ||
"00062008-0000-0000-c000-000000000046": { | ||
34080: { | ||
id: string; | ||
}; | ||
34084: { | ||
id: string; | ||
}; | ||
}; | ||
}; | ||
CLASS_MAPPING: { | ||
@@ -75,2 +86,4 @@ ATTACHMENT_DATA: string; | ||
'0102': string; | ||
'0003': string; | ||
'000b': string; | ||
}; | ||
@@ -77,0 +90,0 @@ DIR_TYPE: { |
@@ -76,2 +76,3 @@ "use strict"; | ||
// recipient specific | ||
'0c15': 'recipType', | ||
'3001': 'name', | ||
@@ -82,2 +83,12 @@ '3002': 'addressType', | ||
}, | ||
PIDLID_MAPPING: { | ||
"00062008-0000-0000-c000-000000000046": { | ||
0x00008520: { | ||
id: "PidLidVerbStream" | ||
}, | ||
0x00008524: { | ||
id: "PidLidVerbResponse" | ||
} | ||
} | ||
}, | ||
CLASS_MAPPING: { | ||
@@ -89,3 +100,5 @@ ATTACHMENT_DATA: '3701' | ||
'001f': 'unicode', | ||
'0102': 'binary' | ||
'0102': 'binary', | ||
'0003': 'integer', | ||
'000b': 'boolean' | ||
}, | ||
@@ -92,0 +105,0 @@ DIR_TYPE: { |
@@ -371,2 +371,11 @@ export interface ParserConfig { | ||
} | ||
declare enum KeyType { | ||
root = 0, | ||
toSub = 1 | ||
} | ||
interface FieldValuePair { | ||
key: string; | ||
keyType: KeyType; | ||
value: string | Uint8Array; | ||
} | ||
/** | ||
@@ -380,5 +389,10 @@ * The core implementation of MsgReader | ||
private innerMsgBurners; | ||
/** | ||
* pidTag: 0x8000 ~ 0xFFFF | ||
*/ | ||
private privatePidToKeyed; | ||
constructor(arrayBuffer: ArrayBuffer | DataView); | ||
private getFieldValue; | ||
private decodeField; | ||
private fieldsDataDocument; | ||
setDecodedFieldTo(fields: FieldsData, pair: FieldValuePair): void; | ||
private getFieldType; | ||
@@ -391,2 +405,3 @@ private fieldsDataDirInner; | ||
private fieldsDataDir; | ||
private fieldsNameIdDir; | ||
/** | ||
@@ -411,1 +426,2 @@ * extract real fields | ||
} | ||
export {}; |
@@ -18,2 +18,6 @@ "use strict"; | ||
var _EntryStreamParser = require("./EntryStreamParser"); | ||
var _VerbStreamParser = require("./VerbStreamParser"); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } | ||
@@ -53,2 +57,9 @@ | ||
var KeyType; | ||
(function (KeyType) { | ||
KeyType[KeyType["root"] = 0] = "root"; | ||
KeyType[KeyType["toSub"] = 1] = "toSub"; | ||
})(KeyType || (KeyType = {})); | ||
function fileTimeToUnixEpoch(time) { | ||
@@ -63,2 +74,5 @@ return (time - 116444736000000000) / 10000; | ||
var MsgReader = /*#__PURE__*/function () { | ||
/** | ||
* pidTag: 0x8000 ~ 0xFFFF | ||
*/ | ||
function MsgReader(arrayBuffer) { | ||
@@ -75,2 +89,4 @@ _classCallCheck(this, MsgReader); | ||
_defineProperty(this, "privatePidToKeyed", void 0); | ||
this.reader = new _Reader.Reader(arrayBuffer); | ||
@@ -80,15 +96,79 @@ } | ||
_createClass(MsgReader, [{ | ||
key: "getFieldValue", | ||
value: function getFieldValue(fieldProperty, type) { | ||
var array = fieldProperty.provider(); | ||
key: "decodeField", | ||
value: function decodeField(fieldClass, fieldType, provider, insideProps) { | ||
var array = provider(); | ||
var ds = new _DataStream["default"](array, 0, _DataStream["default"].LITTLE_ENDIAN); | ||
var decodeAs = _const["default"].MSG.FIELD.TYPE_MAPPING[type]; | ||
var key = _const["default"].MSG.FIELD.NAME_MAPPING[fieldClass]; | ||
var keyType = KeyType.root; | ||
var classValue = parseInt("0x".concat(fieldClass)); | ||
if (0) {} else if (decodeAs === "string") { | ||
return ds.readString(fieldProperty.length); | ||
if (classValue >= 0x8000) { | ||
var keyed = this.privatePidToKeyed[classValue]; | ||
if (keyed) { | ||
if (keyed.useName) { | ||
key = keyed.name; | ||
keyType = KeyType.toSub; | ||
} else { | ||
var lidDict = _const["default"].MSG.FIELD.PIDLID_MAPPING[keyed.propertySet]; | ||
if (lidDict !== undefined) { | ||
var prop = lidDict[keyed.propertyLid]; | ||
if (prop !== undefined) { | ||
key = prop.id; | ||
keyType = KeyType.toSub; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
var value = array; | ||
var skip = false; | ||
var decodeAs = _const["default"].MSG.FIELD.TYPE_MAPPING[fieldType]; | ||
if (0) {} else if (key === "PidLidVerbStream") { | ||
value = (0, _VerbStreamParser.parse)(ds); | ||
} else if (decodeAs === "string") { | ||
value = ds.readString(array.length); | ||
skip = insideProps; | ||
} else if (decodeAs === "unicode") { | ||
return ds.readUCS2String(fieldProperty.length / 2); | ||
value = ds.readUCS2String(array.length / 2); | ||
skip = insideProps; | ||
} else if (decodeAs === "integer") { | ||
value = ds.readUint32(); | ||
} else if (decodeAs === "boolean") { | ||
value = ds.readUint16() ? true : false; | ||
} | ||
return array; | ||
if (skip) { | ||
key = undefined; | ||
} | ||
if (0) {} else if (key === "PidLidVerbResponse") { | ||
key = "votingResponse"; | ||
keyType = KeyType.root; | ||
} else if (key === "PidLidVerbStream") { | ||
key = "votingOptions"; | ||
keyType = KeyType.root; | ||
} else if (key === "recipType") { | ||
var MAPI_TO = 1; | ||
var MAPI_CC = 2; | ||
var MAPI_BCC = 3; | ||
if (0) {} else if (value === MAPI_TO) { | ||
value = "to"; | ||
} else if (value === MAPI_CC) { | ||
value = "cc"; | ||
} else if (value === MAPI_BCC) { | ||
value = "bcc"; | ||
} | ||
} | ||
return { | ||
key: key, | ||
keyType: keyType, | ||
value: value | ||
}; | ||
} | ||
@@ -102,8 +182,3 @@ }, { | ||
parserConfig.propertyObserver && parserConfig.propertyObserver(fields, parseInt(value.substring(0, 8), 16), documentProperty.provider()); | ||
var fieldName = _const["default"].MSG.FIELD.NAME_MAPPING[fieldClass]; | ||
if (fieldName) { | ||
fields[fieldName] = this.getFieldValue(documentProperty, fieldType); | ||
} | ||
if (fieldClass == _const["default"].MSG.FIELD.CLASS_MAPPING.ATTACHMENT_DATA) { | ||
@@ -113,5 +188,20 @@ // attachment specific info | ||
fields.contentLength = documentProperty.length; | ||
} else { | ||
this.setDecodedFieldTo(fields, this.decodeField(fieldClass, fieldType, documentProperty.provider, false)); | ||
} | ||
} | ||
}, { | ||
key: "setDecodedFieldTo", | ||
value: function setDecodedFieldTo(fields, pair) { | ||
var key = pair.key, | ||
keyType = pair.keyType, | ||
value = pair.value; | ||
if (key !== undefined) { | ||
if (keyType === KeyType.root) { | ||
fields[key] = value; | ||
} | ||
} | ||
} | ||
}, { | ||
key: "getFieldType", | ||
@@ -143,3 +233,3 @@ value: function getFieldType(fieldProperty) { | ||
// unknown, read | ||
this.fieldsDataDir(parserConfig, dirProperty, rootFolder, fields, "nameid"); | ||
this.fieldsNameIdDir(parserConfig, dirProperty, rootFolder, fields); | ||
} else { | ||
@@ -224,3 +314,3 @@ // other dir | ||
var sources = rootFolder.subFolders().filter(function (it) { | ||
return it.name === "__nameid_version1.0"; | ||
return it.name === _const["default"].MSG.FIELD.PREFIX.NAMEID; | ||
}); | ||
@@ -276,2 +366,4 @@ | ||
value: function fieldsRecipAndAttachmentProperties(parserConfig, documentProperty, fields) { | ||
var _this2 = this; | ||
var propertiesBinary = documentProperty.provider(); | ||
@@ -281,46 +373,17 @@ var propertiesDs = new _DataStream["default"](propertiesBinary, 8, _DataStream["default"].LITTLE_ENDIAN); // See: [MS-OXMSG]: Outlook Item (.msg) File Format, 2.4 Property Stream | ||
var PtypInteger32 = 0x0003; | ||
var PtypBoolean = 0x000B; | ||
var PidTagRecipientType = 0x0C150003; | ||
while (!propertiesDs.isEof()) { | ||
var _loop = function _loop() { | ||
var propertyTag = propertiesDs.readUint32(); | ||
var flags = propertiesDs.readUint32(); | ||
var fieldClass = (0, _utils.toHexStr)(propertyTag >> 16 & 65535, 4); | ||
var fieldType = (0, _utils.toHexStr)(propertyTag & 65535, 4); | ||
var ptyp = propertyTag & 0xFFFF; | ||
var value = undefined; | ||
var rawDataPos = propertiesDs.position; | ||
var fieldClass = (0, _utils.toHex2)(propertyTag >> 16 & 65535); | ||
var fieldType = (0, _utils.toHex2)(propertyTag & 65535); | ||
var raw = propertiesDs.readUint8Array(8); | ||
parserConfig.propertyObserver(fields, propertyTag, raw); | ||
if (ptyp === PtypInteger32) { | ||
value = propertiesDs.readUint32(); | ||
} else if (ptyp === PtypBoolean) { | ||
value = propertiesDs.readUint16() ? true : false; | ||
} | ||
_this2.setDecodedFieldTo(fields, _this2.decodeField(fieldClass, fieldType, function () { | ||
return raw; | ||
}, true)); | ||
}; | ||
propertiesDs.seek(rawDataPos); | ||
var _raw = propertiesDs.readUint8Array(8); | ||
parserConfig.propertyObserver(fields, propertyTag, _raw); | ||
if (propertyTag === PidTagRecipientType) { | ||
var MAPI_TO = 1; | ||
var MAPI_CC = 2; | ||
var MAPI_BCC = 3; | ||
if (value === MAPI_TO) { | ||
fields["recipType"] = "to"; | ||
} else if (value === MAPI_CC) { | ||
fields["recipType"] = "cc"; | ||
} else if (value === MAPI_BCC) { | ||
fields["recipType"] = "bcc"; | ||
} | ||
} else { | ||
var fieldName = _const["default"].MSG.FIELD.NAME_MAPPING[fieldClass]; //console.info("@", fieldClass, propertyTag, fieldName, value); | ||
if (fieldName && value !== undefined) { | ||
fields[fieldName] = value; | ||
} | ||
} | ||
while (!propertiesDs.isEof()) { | ||
_loop(); | ||
} | ||
@@ -368,7 +431,2 @@ } | ||
value: function fieldsDataDir(parserConfig, dirProperty, rootFolder, fields, subClass) { | ||
if (subClass === "nameid") { | ||
// skip normal processing | ||
return; | ||
} | ||
var _iterator4 = _createForOfIteratorHelper(dirProperty.subFolders()), | ||
@@ -411,2 +469,71 @@ _step4; | ||
} | ||
}, { | ||
key: "fieldsNameIdDir", | ||
value: function fieldsNameIdDir(parserConfig, dirProperty, rootFolder, fields) { | ||
var guidTable = undefined; | ||
var stringTable = undefined; | ||
var entryTable = undefined; | ||
var _iterator6 = _createForOfIteratorHelper(dirProperty.fileNameSets()), | ||
_step6; | ||
try { | ||
for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) { | ||
var fileSet = _step6.value; | ||
if (0) {} else if (fileSet.name.indexOf(_const["default"].MSG.FIELD.PREFIX.DOCUMENT) == 0) { | ||
var value = fileSet.name.substring(12).toLowerCase(); | ||
var fieldClass = value.substring(0, 4); | ||
var fieldType = value.substring(4, 8); | ||
if (0) {} else if (fieldClass === "0002" && fieldType === "0102") { | ||
guidTable = fileSet.provider(); | ||
} else if (fieldClass === "0003" && fieldType === "0102") { | ||
entryTable = fileSet.provider(); | ||
} else if (fieldClass === "0004" && fieldType === "0102") { | ||
stringTable = fileSet.provider(); | ||
} | ||
} | ||
} //console.log("%", guidTable, stringTable, entryTable); | ||
} catch (err) { | ||
_iterator6.e(err); | ||
} finally { | ||
_iterator6.f(); | ||
} | ||
if (guidTable !== undefined && stringTable !== undefined && entryTable !== undefined) { | ||
var entries = (0, _EntryStreamParser.parse)(entryTable); | ||
var stringReader = new _DataStream["default"](stringTable, 0, _DataStream["default"].LITTLE_ENDIAN); | ||
var _iterator7 = _createForOfIteratorHelper(entries), | ||
_step7; | ||
try { | ||
for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) { | ||
var entry = _step7.value; | ||
if (entry.isStringProperty) { | ||
stringReader.seek(entry.key); | ||
var numTextBytes = stringReader.readUint32(); | ||
this.privatePidToKeyed[0x8000 | entry.propertyIndex] = { | ||
useName: true, | ||
name: stringReader.readUCS2String(numTextBytes / 2) | ||
}; | ||
} else { | ||
this.privatePidToKeyed[0x8000 | entry.propertyIndex] = { | ||
useName: false, | ||
propertySet: entry.guidIndex === 1 ? "00020328-00000-0000-C000-00000000046" : entry.guidIndex === 2 ? "00020329-00000-0000-C000-00000000046" : (0, _utils.msftUuidStringify)(guidTable, 16 * (entry.guidIndex - 3)), | ||
propertyLid: entry.key | ||
}; | ||
} | ||
} //console.log("@", this.privatePidToKeyed); | ||
} catch (err) { | ||
_iterator7.e(err); | ||
} finally { | ||
_iterator7.f(); | ||
} | ||
} | ||
} | ||
/** | ||
@@ -451,2 +578,3 @@ * extract real fields | ||
this.innerMsgBurners = {}; | ||
this.privatePidToKeyed = {}; | ||
this.fieldsData = this.parseMsgData({ | ||
@@ -453,0 +581,0 @@ propertyObserver: ((_this$parserConfig = this.parserConfig) === null || _this$parserConfig === void 0 ? void 0 : _this$parserConfig.propertyObserver) || function () {} |
@@ -13,1 +13,30 @@ /** | ||
export declare function toHexStr(value: number, padding: number): string; | ||
/** | ||
* byte to hex string | ||
* | ||
* @internal | ||
*/ | ||
export declare function toHex1(value: number): string; | ||
/** | ||
* little uint16 to hex string | ||
* | ||
* @internal | ||
*/ | ||
export declare function toHex2(value: number): string; | ||
/** | ||
* little uint32 to hex string | ||
* | ||
* @internal | ||
*/ | ||
export declare function toHex4(value: number): string; | ||
/** | ||
* Variant 2 UUIDs, historically used in Microsoft's COM/OLE libraries, | ||
* use a mixed-endian format, whereby the first three components of the UUID are little-endian, | ||
* and the last two are big-endian. | ||
* For example, `00112233-4455-6677-8899-aabbccddeeff` is encoded as the bytes | ||
* `33 22 11 00 55 44 77 66 88 99 aa bb cc dd ee ff`. | ||
* | ||
* @see https://en.wikipedia.org/wiki/Universally_unique_identifier | ||
* @internal | ||
*/ | ||
export declare function msftUuidStringify(array: ArrayLike<number>, offset: number): string; |
@@ -9,2 +9,6 @@ "use strict"; | ||
exports.toHexStr = toHexStr; | ||
exports.toHex1 = toHex1; | ||
exports.toHex2 = toHex2; | ||
exports.toHex4 = toHex4; | ||
exports.msftUuidStringify = msftUuidStringify; | ||
@@ -59,2 +63,48 @@ /** | ||
return text; | ||
} | ||
var hex = "0123456789abcdef"; | ||
/** | ||
* byte to hex string | ||
* | ||
* @internal | ||
*/ | ||
function toHex1(value) { | ||
return hex[value >> 4 & 15] + hex[value & 15]; | ||
} | ||
/** | ||
* little uint16 to hex string | ||
* | ||
* @internal | ||
*/ | ||
function toHex2(value) { | ||
return hex[value >> 12 & 15] + hex[value >> 8 & 15] + hex[value >> 4 & 15] + hex[value & 15]; | ||
} | ||
/** | ||
* little uint32 to hex string | ||
* | ||
* @internal | ||
*/ | ||
function toHex4(value) { | ||
return hex[value >> 28 & 15] + hex[value >> 24 & 15] + hex[value >> 20 & 15] + hex[value >> 16 & 15] + hex[value >> 12 & 15] + hex[value >> 8 & 15] + hex[value >> 4 & 15] + hex[value & 15]; | ||
} | ||
/** | ||
* Variant 2 UUIDs, historically used in Microsoft's COM/OLE libraries, | ||
* use a mixed-endian format, whereby the first three components of the UUID are little-endian, | ||
* and the last two are big-endian. | ||
* For example, `00112233-4455-6677-8899-aabbccddeeff` is encoded as the bytes | ||
* `33 22 11 00 55 44 77 66 88 99 aa bb cc dd ee ff`. | ||
* | ||
* @see https://en.wikipedia.org/wiki/Universally_unique_identifier | ||
* @internal | ||
*/ | ||
function msftUuidStringify(array, offset) { | ||
return "" + toHex1(array[offset + 3]) + toHex1(array[offset + 2]) + toHex1(array[offset + 1]) + toHex1(array[offset + 0]) + "-" + toHex1(array[offset + 5]) + toHex1(array[offset + 4]) + "-" + toHex1(array[offset + 7]) + toHex1(array[offset + 6]) + "-" + toHex1(array[offset + 8]) + toHex1(array[offset + 9]) + "-" + toHex1(array[offset + 10]) + toHex1(array[offset + 11]) + toHex1(array[offset + 12]) + toHex1(array[offset + 13]) + toHex1(array[offset + 14]) + toHex1(array[offset + 15]); | ||
} |
{ | ||
"name": "@kenjiuno/msgreader", | ||
"version": "1.6.6", | ||
"version": "1.7.0", | ||
"description": "", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
235706
24
7056