/// <summary> /// Gets the <see cref="TableEntry"/> with the specified property tag. /// </summary> /// <param name="propertyTag">The property tag.</param> /// <returns>TableEntry.</returns> public TableEntry this[PropertyTag propertyTag] { get { for (int i = 0; i < this.Count; i++) { if (this[i].PropertyTag.Tag == propertyTag.Tag) { return(this[i]); } } return(null); } }
private void Parse(PstFileReader reader, byte[] tableBuffer, LocalDescriptorList localDescriptorList, DataStructure tableDataNode, bool isRecipientsTable) { ushort indexOffset = BitConverter.ToUInt16(tableBuffer, 0); ushort tableType = BitConverter.ToUInt16(tableBuffer, 2); uint tableValueReference = BitConverter.ToUInt32(tableBuffer, 4); TableIndex index = new TableIndex(tableBuffer, indexOffset, reader.PstFile.Is64Bit); TableIndexItem tableHeader7CIndexItem = index.GetItem(tableValueReference); if (tableHeader7CIndexItem == null) { return; } byte[] tableHeader7CBuffer = new byte[tableHeader7CIndexItem.EndOffset - tableHeader7CIndexItem.StartOffset]; if (tableHeader7CBuffer.Length > tableBuffer.Length - tableHeader7CIndexItem.StartOffset) { return; } System.Array.Copy(tableBuffer, tableHeader7CIndexItem.StartOffset, tableHeader7CBuffer, 0, tableHeader7CBuffer.Length); TableHeader7C tableHeader7C = new TableHeader7C(tableHeader7CBuffer); if (tableHeader7C.Type != 0x7C) { return; } TableIndexItem tableHeaderB5IndexItem = index.GetItem(tableHeader7C.B5HeaderReference); byte[] tableHeaderB5Buffer = new byte[8]; System.Array.Copy(tableBuffer, tableHeaderB5IndexItem.StartOffset, tableHeaderB5Buffer, 0, 8); TableHeaderB5 tableHeaderB5 = new TableHeaderB5(tableHeaderB5Buffer); BTreeOnHeapHeader bTreeOnHeapHeader = new BTreeOnHeapHeader(tableHeaderB5Buffer); if (tableHeaderB5.Type != 0xB5) { return; } if (tableHeader7C.ValueEntriesIndexReference == 0) { return; } byte[] entryValueBuffer = null; bool useB5Header = false; if (bTreeOnHeapHeader.BIdxLevels == 2) { //Not implemented return; } else if (bTreeOnHeapHeader.BIdxLevels == 1) { byte[] level1EntryValueBuffer = null; if (bTreeOnHeapHeader.HidRoot.Index > 0 && bTreeOnHeapHeader.HidRoot.BlockIndex == 0) { TableIndexItem entryIndexItem = index.Items.ContainsKey((int)bTreeOnHeapHeader.HidRoot.Index) ? index.Items[(int)bTreeOnHeapHeader.HidRoot.Index] : null; if (entryIndexItem != null) { level1EntryValueBuffer = new byte[entryIndexItem.EndOffset - entryIndexItem.StartOffset]; System.Array.Copy(tableBuffer, entryIndexItem.StartOffset, level1EntryValueBuffer, 0, level1EntryValueBuffer.Length); } } else if (bTreeOnHeapHeader.HidRoot.Index > 0 && bTreeOnHeapHeader.HidRoot.BlockIndex > 0) { if (index.SecondItems.Length >= bTreeOnHeapHeader.HidRoot.BlockIndex) { IDictionary <int, TableIndexItem> secondIndexTable = index.SecondItems[bTreeOnHeapHeader.HidRoot.BlockIndex - 1]; if (secondIndexTable != null) { TableIndexItem entryIndexItem = secondIndexTable.ContainsKey((int)bTreeOnHeapHeader.HidRoot.Index) ? secondIndexTable[(int)bTreeOnHeapHeader.HidRoot.Index] : null; if (entryIndexItem != null) { level1EntryValueBuffer = new byte[entryIndexItem.EndOffset - entryIndexItem.StartOffset]; System.Array.Copy(tableBuffer, entryIndexItem.StartOffset, level1EntryValueBuffer, 0, level1EntryValueBuffer.Length); } } } } MemoryStream entryValueBufferMemoryStream = new MemoryStream(); for (int i = 0; i < level1EntryValueBuffer.Length; i += 8) { byte[] heapIdBuffer = new byte[4]; System.Array.Copy(level1EntryValueBuffer, i + 4, heapIdBuffer, 0, 4); HeapId heapId = new HeapId(heapIdBuffer); if (heapId.Index > 0 && heapId.BlockIndex == 0) { TableIndexItem tempEntryIndexItem = index.Items.ContainsKey((int)heapId.Index) ? index.Items[(int)heapId.Index] : null; if (tempEntryIndexItem != null) { byte[] tempLevel1EntryValueBuffer = new byte[tempEntryIndexItem.EndOffset - tempEntryIndexItem.StartOffset]; System.Array.Copy(tableBuffer, tempEntryIndexItem.StartOffset, tempLevel1EntryValueBuffer, 0, tempLevel1EntryValueBuffer.Length); entryValueBufferMemoryStream.Write(tempLevel1EntryValueBuffer, 0, tempLevel1EntryValueBuffer.Length); } } else if (heapId.Index > 0 && heapId.BlockIndex > 0) { if (index.SecondItems.Length >= heapId.BlockIndex) { IDictionary <int, TableIndexItem> secondIndexTable = index.SecondItems[heapId.BlockIndex - 1]; if (secondIndexTable != null) { TableIndexItem tempEntryIndexItem = secondIndexTable.ContainsKey((int)heapId.Index) ? secondIndexTable[(int)heapId.Index] : null; if (tempEntryIndexItem != null) { byte[] tempLevel1EntryValueBuffer = new byte[tempEntryIndexItem.EndOffset - tempEntryIndexItem.StartOffset]; System.Array.Copy(tableBuffer, tempEntryIndexItem.StartOffset, tempLevel1EntryValueBuffer, 0, tempLevel1EntryValueBuffer.Length); entryValueBufferMemoryStream.Write(tempLevel1EntryValueBuffer, 0, tempLevel1EntryValueBuffer.Length); } } } } } entryValueBuffer = entryValueBufferMemoryStream.ToArray(); useB5Header = true; } else if (localDescriptorList != null) { LocalDescriptorListElement element = localDescriptorList.Elements.ContainsKey(tableHeader7C.ValueEntriesIndexReference) ? localDescriptorList.Elements[tableHeader7C.ValueEntriesIndexReference] : null; if (element == null && tableHeader7C.ValueEntriesIndexReference != 63 && !Util.IsInternalReference(tableHeader7C.ValueEntriesIndexReference)) { uint elementId = isRecipientsTable ? 1682u : 1649u; element = localDescriptorList.Elements.ContainsKey(elementId) ? localDescriptorList.Elements[elementId] : null; if (element != null && element.SubList != null) { element = element.SubList.Elements.ContainsKey(tableHeader7C.ValueEntriesIndexReference) ? element.SubList.Elements[tableHeader7C.ValueEntriesIndexReference] : null; } } if (element != null) { DataStructure dataNode = reader.PstFile.DataIndexTree.GetDataStructure(element.DataStructureId); if (dataNode != null) { entryValueBuffer = Util.GetBuffer(reader, dataNode); } } else { if (tableHeader7C.ValueEntriesIndexReference == 63) { TableIndexItem entryValueIndexItem = index.GetItem(tableHeaderB5.ValueEntriesIndexReference); if (entryValueIndexItem != null && tableBuffer.Length >= entryValueIndexItem.EndOffset) { entryValueBuffer = new byte[entryValueIndexItem.EndOffset - entryValueIndexItem.StartOffset]; System.Array.Copy(tableBuffer, entryValueIndexItem.StartOffset, entryValueBuffer, 0, entryValueBuffer.Length); } useB5Header = true; } else { TableIndexItem entryValueIndexItem = index.GetItem(tableHeader7C.ValueEntriesIndexReference); if (entryValueIndexItem != null && tableBuffer.Length >= entryValueIndexItem.EndOffset) { entryValueBuffer = new byte[entryValueIndexItem.EndOffset - entryValueIndexItem.StartOffset]; System.Array.Copy(tableBuffer, entryValueIndexItem.StartOffset, entryValueBuffer, 0, entryValueBuffer.Length); } } } } else { if (tableHeader7C.ValueEntriesIndexReference == 63) { TableIndexItem entryValueIndexItem = index.GetItem(tableHeaderB5.ValueEntriesIndexReference); if (entryValueIndexItem != null && tableBuffer.Length >= entryValueIndexItem.EndOffset) { entryValueBuffer = new byte[entryValueIndexItem.EndOffset - entryValueIndexItem.StartOffset]; System.Array.Copy(tableBuffer, entryValueIndexItem.StartOffset, entryValueBuffer, 0, entryValueBuffer.Length); } useB5Header = true; } else { TableIndexItem entryValueIndexItem = index.GetItem(tableHeader7C.ValueEntriesIndexReference); if (entryValueIndexItem != null && tableBuffer.Length >= entryValueIndexItem.EndOffset) { entryValueBuffer = new byte[entryValueIndexItem.EndOffset - entryValueIndexItem.StartOffset]; System.Array.Copy(tableBuffer, entryValueIndexItem.StartOffset, entryValueBuffer, 0, entryValueBuffer.Length); } } } if (entryValueBuffer == null) { return; } int entriesArraySize = entryValueBuffer.Length / tableHeader7C.ValueArraySize; if (useB5Header) { entriesArraySize = entryValueBuffer.Length / (tableHeaderB5.EntryIdSize + tableHeaderB5.EntryValueSize); } EntriesArray = new TableEntryList[entriesArraySize]; for (int m = 0; m < entriesArraySize; m++) { EntriesArray[m] = new TableEntryList(); for (int i = 0; i < tableHeader7C.EntryDefinitions.Count; i++) { Table7CEntryDefinition entryDefinition = tableHeader7C.EntryDefinitions[i]; TableEntry entry = new TableEntry(reader.PstFile.Encoding); entry.PropertyTag = new PropertyTag(entryDefinition.EntryId, entryDefinition.EntryValueType); int offset = m * tableHeader7C.ValueArraySize + entryDefinition.ValueArrayEntryOffset; if (useB5Header) { offset = m * (tableHeaderB5.EntryIdSize + tableHeaderB5.EntryValueSize); } if (entryValueBuffer.Length < offset) { if (isRecipientsTable) { Console.Error.WriteLine("\rWarning: Bad entry Recipients Property Table..skipping entry"); } else { Console.Error.WriteLine("\rWarning: Bad entry in Table 7c..skipping entry"); } } else if (entry.PropertyTag.Type == PropertyType.Short || entry.PropertyTag.Type == PropertyType.Boolean) { entry.ValueBuffer = new byte[2]; System.Array.Copy(entryValueBuffer, offset, entry.ValueBuffer, 0, 2); } else if (entry.PropertyTag.Type == PropertyType.Integer || entry.PropertyTag.Type == PropertyType.Float) { entry.ValueBuffer = new byte[4]; System.Array.Copy(entryValueBuffer, offset, entry.ValueBuffer, 0, 4); } else if (!useB5Header && (entry.PropertyTag.Type == PropertyType.Long || entry.PropertyTag.Type == PropertyType.Double || entry.PropertyTag.Type == PropertyType.Currency)) { entry.ValueBuffer = new byte[8]; System.Array.Copy(entryValueBuffer, offset, entry.ValueBuffer, 0, 8); } else if (entry.PropertyTag.Type == PropertyType.Long || entry.PropertyTag.Type == PropertyType.Double || entry.PropertyTag.Type == PropertyType.Currency || entry.PropertyTag.Type == PropertyType.ShortArray || entry.PropertyTag.Type == PropertyType.IntegerArray || entry.PropertyTag.Type == PropertyType.FloatArray || entry.PropertyTag.Type == PropertyType.LongArray || entry.PropertyTag.Type == PropertyType.DoubleArray || entry.PropertyTag.Type == PropertyType.CurrencyArray || entry.PropertyTag.Type == PropertyType.String || entry.PropertyTag.Type == PropertyType.String8 || entry.PropertyTag.Type == PropertyType.StringArray || entry.PropertyTag.Type == PropertyType.String8Array || entry.PropertyTag.Type == PropertyType.Binary || entry.PropertyTag.Type == PropertyType.BinaryArray || entry.PropertyTag.Type == PropertyType.Object || entry.PropertyTag.Type == PropertyType.Guid || entry.PropertyTag.Type == PropertyType.GuidArray || entry.PropertyTag.Type == PropertyType.ApplicationTime || entry.PropertyTag.Type == PropertyType.SystemTime) { uint valueReference = 0; if (useB5Header && tableHeaderB5.EntryValueSize == 2) { valueReference = BitConverter.ToUInt16(entryValueBuffer, offset); } else { valueReference = BitConverter.ToUInt32(entryValueBuffer, offset); } if (valueReference == 0) { continue; } if (Util.IsInternalReference(valueReference)) { TableIndexItem indexItem = index.GetItem(valueReference); if (indexItem != null) { if (indexItem.EndOffset > indexItem.StartOffset) { entry.ValueBuffer = new byte[indexItem.EndOffset - indexItem.StartOffset]; if (tableBuffer.Length >= indexItem.StartOffset + entry.ValueBuffer.Length) { System.Array.Copy(tableBuffer, indexItem.StartOffset, entry.ValueBuffer, 0, entry.ValueBuffer.Length); } } } } else { if (localDescriptorList != null) { LocalDescriptorListElement element = localDescriptorList.Elements.ContainsKey(valueReference) ? localDescriptorList.Elements[valueReference] : null; if (element == null) { LocalDescriptorListElement recipientElement = localDescriptorList.Elements.ContainsKey((uint)1682) ? localDescriptorList.Elements[(uint)1682] : null; if (recipientElement != null && recipientElement.SubList != null) { element = recipientElement.SubList.Elements.ContainsKey(valueReference) ? recipientElement.SubList.Elements[valueReference] : null; } } if (element != null) { DataStructure dataNode = reader.PstFile.DataIndexTree.GetDataStructure(element.DataStructureId); if (dataNode != null) { entry.ValueBuffer = Util.GetBuffer(reader, dataNode); } } } } } EntriesArray[m].Add(entry); } } //Try to recover recipients table if email address is null in all entries int emptyEmailAddressCount = 0; for (int k = 0; k < EntriesArray.Length; k++) { if (EntriesArray[k][MapiPropertyTag.PR_EMAIL_ADDRESS] != null && EntriesArray[k][MapiPropertyTag.PR_EMAIL_ADDRESS].ValueBuffer == null) { emptyEmailAddressCount++; } else if (EntriesArray[k][MapiPropertyTag.PR_EMAIL_ADDRESS] == null && EntriesArray[k][MapiPropertyTag.PR_RECIPIENT_TYPE] != null) { emptyEmailAddressCount++; } } if (emptyEmailAddressCount > (int)(EntriesArray.Length / 2)) { TableInfo tableInfo = new TableInfo(tableHeader7CBuffer); IDictionary <uint, byte[]> rowTable = new Dictionary <uint, byte[]>(); int rowLength = tableInfo.EndOffsetCellExistenceBlock; IList <TableRowId> rowIds = new List <TableRowId>(); for (int i = 0; i < entryValueBuffer.Length; i += entryValueBuffer.Length / entriesArraySize) { byte[] rowIdBuffer = new byte[entryValueBuffer.Length / entriesArraySize]; if (entryValueBuffer.Length >= i + rowIdBuffer.Length) { System.Array.Copy(entryValueBuffer, i, rowIdBuffer, 0, rowIdBuffer.Length); TableRowId rowId = new TableRowId(rowIdBuffer); rowIds.Add(rowId); } } if (localDescriptorList != null) { LocalDescriptorListElement element = localDescriptorList.Elements.ContainsKey((uint)0x692) ? localDescriptorList.Elements[(uint)0x692] : null; if (element != null) { if (element.SubList == null || !element.SubList.Elements.ContainsKey((uint)63) || element.SubList.Elements[(uint)63] == null) { return; } LocalDescriptorListElement subElement = element.SubList.Elements[(uint)63]; DataStructure dataNode = reader.PstFile.DataIndexTree.GetDataStructure(subElement.DataStructureId); if (dataNode != null) { byte[] rowMatrix = Util.GetBuffer(reader, dataNode); int tableSize = reader.PstFile.Is64Bit ? 8176 : 8180; int tableCount = 1; for (int r = 0; r <= rowMatrix.Length - rowLength; r += rowLength) { if (((tableSize * tableCount) - r) < rowLength) { r = tableSize * tableCount++; } byte[] rowBuffer = new byte[rowLength]; System.Array.Copy(rowMatrix, r, rowBuffer, 0, rowBuffer.Length); uint dwRowId = BitConverter.ToUInt32(rowBuffer, 0); if (!rowTable.ContainsKey(dwRowId)) { rowTable.Add(dwRowId, rowBuffer); } else { //Console.WriteLine("Warning: existing row key:" + dwRowId); } } int cellExistenceBlockLength = tableInfo.EndOffsetCellExistenceBlock - tableInfo.EndOffset1ByteDataValue; EntriesArray = new TableEntryList[rowIds.Count]; for (int i = 0; i < rowIds.Count; i++) { EntriesArray[i] = new TableEntryList(); byte[] rowBuffer = rowTable.ContainsKey(rowIds[i].Id) ? rowTable[rowIds[i].Id] : null; if (rowBuffer != null) { for (int c = 0; c < tableInfo.Columns.Count; c++) { TableColumnDescription columnDescription = tableInfo.Columns[c]; byte[] cellExistenceBlock = new byte[cellExistenceBlockLength]; System.Array.Copy(rowBuffer, rowBuffer.Length - cellExistenceBlockLength, cellExistenceBlock, 0, cellExistenceBlock.Length); int cellExistenceBlockValue = cellExistenceBlock[columnDescription.IBit / 8] & (1 << (7 - (columnDescription.IBit % 8))); if (cellExistenceBlockValue != 0) //true { PropertyTag propertyTag = new PropertyTag(columnDescription.Tag); TableEntry entry = new TableEntry(reader.PstFile.Encoding); byte[] valueBuffer = new byte[columnDescription.CountBytes]; System.Array.Copy(rowBuffer, columnDescription.Offset, valueBuffer, 0, valueBuffer.Length); if (propertyTag.Type == PropertyType.Boolean) { if (valueBuffer[0] != 0) //true { valueBuffer = BitConverter.GetBytes((short)1); } else { valueBuffer = BitConverter.GetBytes((short)0); } } else if (propertyTag.Type == PropertyType.Long || propertyTag.Type == PropertyType.Double || propertyTag.Type == PropertyType.Currency || propertyTag.Type == PropertyType.ApplicationTime || propertyTag.Type == PropertyType.SystemTime || propertyTag.Type == PropertyType.Guid) { HeapId heapId = new HeapId(valueBuffer); TableIndexItem indexItem = index.Items.ContainsKey((int)heapId.Index) ? index.Items[(int)heapId.Index] : null; if (indexItem != null) { valueBuffer = new byte[indexItem.EndOffset - indexItem.StartOffset]; System.Array.Copy(tableBuffer, indexItem.StartOffset, valueBuffer, 0, valueBuffer.Length); } } else if (propertyTag.Type == PropertyType.String || propertyTag.Type == PropertyType.String8 || propertyTag.Type == PropertyType.Binary || propertyTag.Type == PropertyType.Object || propertyTag.Type == PropertyType.ShortArray || propertyTag.Type == PropertyType.IntegerArray || propertyTag.Type == PropertyType.FloatArray || propertyTag.Type == PropertyType.LongArray || propertyTag.Type == PropertyType.DoubleArray || propertyTag.Type == PropertyType.CurrencyArray || propertyTag.Type == PropertyType.StringArray || propertyTag.Type == PropertyType.String8Array || propertyTag.Type == PropertyType.BinaryArray || propertyTag.Type == PropertyType.GuidArray) { HeapId heapId = new HeapId(valueBuffer); if (heapId.Index > 0 && heapId.BlockIndex == 0) { TableIndexItem indexItem = index.Items.ContainsKey((int)heapId.Index) ? index.Items[(int)heapId.Index] : null; if (indexItem != null) { valueBuffer = new byte[indexItem.EndOffset - indexItem.StartOffset]; System.Array.Copy(tableBuffer, indexItem.StartOffset, valueBuffer, 0, valueBuffer.Length); } } else if (heapId.Index > 0 && heapId.BlockIndex > 0) { if (index.SecondItems.Length >= heapId.BlockIndex) { IDictionary <int, TableIndexItem> secondIndexTable = index.SecondItems[heapId.BlockIndex - 1]; if (secondIndexTable != null) { TableIndexItem indexItem = secondIndexTable.ContainsKey((int)heapId.Index) ? secondIndexTable[(int)heapId.Index] : null; if (indexItem != null) { valueBuffer = new byte[indexItem.EndOffset - indexItem.StartOffset]; System.Array.Copy(tableBuffer, indexItem.StartOffset, valueBuffer, 0, valueBuffer.Length); } } } } } entry.PropertyTag = propertyTag; entry.ValueBuffer = valueBuffer; EntriesArray[i].Add(entry); } } } } } } } } }