├── .gitattributes ├── .gitignore ├── PSTFileFormat.sln ├── PSTFileFormat ├── ListsTablesAndProperties │ ├── BTreeOnHeap │ │ ├── BTreeOnHeap.cs │ │ ├── BTreeOnHeapDataRecord.cs │ │ ├── BTreeOnHeapHeader.cs │ │ ├── BTreeOnHeapIndex.cs │ │ ├── BTreeOnHeapIndexRecord.cs │ │ └── BTreeOnHeapLeaf.cs │ ├── Enums │ │ ├── OnHeapTypeName.cs │ │ ├── PropertyID.cs │ │ └── PropertyTypeName.cs │ ├── Exceptions │ │ └── MissingSubnodeException.cs │ ├── HeapID.cs │ ├── HeapOnNode │ │ ├── BlockData │ │ │ ├── HeapOnNodeBitmapBlockData.cs │ │ │ ├── HeapOnNodeBlockData.cs │ │ │ ├── HeapOnNodeFirstBlockData.cs │ │ │ └── HeapOnNodePageBlockData.cs │ │ ├── Header │ │ │ ├── HeapOnNodeBitmapHeader.cs │ │ │ ├── HeapOnNodeHeader.cs │ │ │ └── HeapOnNodePageHeader.cs │ │ ├── HeapOnNode.cs │ │ ├── HeapOnNodeHelper.cs │ │ └── HeapOnNodePageMap.cs │ ├── HeapOrNodeID.cs │ ├── NodeStorageHelper.cs │ ├── PropertyContext │ │ ├── NamedPropertyContext.cs │ │ ├── PropertyContext.cs │ │ ├── PropertyContextHelper.cs │ │ ├── PropertyContextRecord.cs │ │ └── PtypObjectRecord.cs │ └── Tables │ │ ├── NamedTableContext.cs │ │ ├── TableColumnDescriptor.cs │ │ ├── TableContext.cs │ │ ├── TableContextHelper.cs │ │ ├── TableContextInfo.cs │ │ └── TableContextRowID.cs ├── Messaging │ ├── Attachments │ │ ├── AttachmentObject.cs │ │ └── AttachmentTable.cs │ ├── Enums │ │ ├── AttachMethod.cs │ │ ├── AttachmentFlags.cs │ │ ├── BusyStatus.cs │ │ ├── FolderItemTypeName.cs │ │ ├── IconIndex.cs │ │ ├── Importance.cs │ │ ├── MeetingType.cs │ │ ├── MessageFlags.cs │ │ ├── MessagePriority.cs │ │ ├── MessageSensitivity.cs │ │ ├── ObjectType.cs │ │ ├── PropertyLongID.cs │ │ ├── PropertySetGuid.cs │ │ ├── RecipientFlags.cs │ │ ├── RecipientType.cs │ │ ├── SideEffectsFlags.cs │ │ └── TaskMode.cs │ ├── Exceptions │ │ ├── InvalidPropertyException.cs │ │ └── InvalidRecurrencePatternException.cs │ ├── Folders │ │ ├── CalendarFolder.cs │ │ ├── MailFolder.cs │ │ └── PSTFolder.cs │ ├── HierarchyTable.cs │ ├── Messages │ │ ├── Appointment.cs │ │ ├── ConversationIndexHeader.cs │ │ ├── MessageObject.Properties.cs │ │ ├── MessageObject.cs │ │ ├── ModifiedAppointmentInstance.AppointmentProperties.cs │ │ ├── ModifiedAppointmentInstance.MessageProperties.cs │ │ ├── ModifiedAppointmentInstance.cs │ │ ├── Note.cs │ │ ├── RecurrencePatternStructure │ │ │ ├── AppointmentRecurrencePatternStructure.cs │ │ │ ├── CalendarHelper.cs │ │ │ ├── DailyRecurrencePatternStructure.cs │ │ │ ├── DateTimeHelper.cs │ │ │ ├── Enums │ │ │ │ ├── DayOccurenceNumber.cs │ │ │ │ ├── DaysOfWeekFlags.cs │ │ │ │ ├── OutlookDayOfWeek.cs │ │ │ │ ├── RecurrenceEnums.cs │ │ │ │ └── RecurrenceType.cs │ │ │ ├── ExceptionInfoStructure.cs │ │ │ ├── MonthlyRecurrencePatternStructure.cs │ │ │ ├── RecurrenceTypeHelper.cs │ │ │ ├── WeeklyRecurrencePatternStructure.cs │ │ │ └── YearlyRecurrencePatternStructure.cs │ │ ├── RecurringAppointment.cs │ │ ├── SingleAppointment.cs │ │ ├── TimeZoneDefinitionStructure │ │ │ ├── Enums │ │ │ │ └── TimeZoneRuleFlags.cs │ │ │ ├── TimeZoneDefinitionRecurStructure.cs │ │ │ └── TimeZoneRuleStructure.cs │ │ └── TimeZoneStructure │ │ │ ├── AdjustmentRuleHelper.cs │ │ │ └── TimeZoneStructure.cs │ ├── NamedProperties │ │ ├── NameID.cs │ │ ├── PropertyName.cs │ │ ├── PropertyNameToIDMap.cs │ │ └── PropertyNames.cs │ ├── Recipients │ │ ├── MessageRecipient.cs │ │ ├── RecipientEntryID.cs │ │ └── RecipientsTable.cs │ └── Search │ │ ├── Enums │ │ ├── SearchUpdateDescriptorFlags.cs │ │ └── SearchUpdateDescriptorType.cs │ │ ├── SearchDomainObject.cs │ │ ├── SearchManagementQueue.cs │ │ ├── SearchUpdateDescriptor.cs │ │ └── SearchUpdateDescriptorData │ │ ├── SearchUpdateDescriptorData.cs │ │ ├── SearchUpdateDescriptorFolderAdded.cs │ │ ├── SearchUpdateDescriptorFolderModified.cs │ │ └── SearchUpdateDescriptorMessageAdded.cs ├── NodeDatabse │ ├── AllocationHelper.cs │ ├── BTree │ │ ├── BTree.cs │ │ ├── BTreeIndexEntry.cs │ │ ├── BTreeIndexPage.cs │ │ ├── BTreePage.cs │ │ ├── BlockBTree.cs │ │ ├── BlockBTreeEntry.cs │ │ ├── BlockBTreeLeafPage.cs │ │ ├── BufferedBTreePageStore.cs │ │ ├── NodeBTree.cs │ │ ├── NodeBTreeEntry.cs │ │ └── NodeBTreeLeafPage.cs │ ├── Block │ │ ├── Block.cs │ │ ├── BlockID.cs │ │ ├── BlockRef.cs │ │ ├── BlockTrailer.cs │ │ └── BufferedBlockStore.cs │ ├── DataTree │ │ ├── DataBlock.cs │ │ ├── DataTree.cs │ │ ├── XBlock.cs │ │ └── XXBlock.cs │ ├── Enums │ │ ├── BlockType.cs │ │ ├── InternalNodeName.cs │ │ ├── NodeTypeName.cs │ │ ├── PageTypeName.cs │ │ ├── WriterCompatibilityMode.cs │ │ └── bCryptMethodName.cs │ ├── Exceptions │ │ ├── InvalidAllocationMapException.cs │ │ ├── InvalidBlockIDException.cs │ │ └── InvalidChecksumException.cs │ ├── Node.cs │ ├── NodeID.cs │ ├── PSTCRCCalculation.cs │ ├── PSTEncryptionUtils.cs │ ├── PSTHeader.cs │ ├── PSTNode.cs │ ├── Pages │ │ ├── AllocationMapPage.cs │ │ ├── DensityListPage.cs │ │ ├── DensityListPageEntry.cs │ │ ├── FPMapPage.cs │ │ ├── FreeMapPage.cs │ │ ├── PMapPage.cs │ │ ├── Page.cs │ │ └── PageTrailer.cs │ ├── RootStructure.cs │ ├── Subnode.cs │ └── SubnodeBTree │ │ ├── SubnodeBTree.cs │ │ ├── SubnodeIntermediateBlock.cs │ │ ├── SubnodeIntermediateEntry.cs │ │ ├── SubnodeLeafBlock.cs │ │ └── SubnodeLeafEntry.cs ├── PSTFile.cs ├── PSTFileFormat.csproj ├── Properties │ └── AssemblyInfo.cs └── Utils │ ├── AdjustmentRuleUtils.Win32.cs │ ├── AdjustmentRuleUtils.cs │ ├── RegistryTimeZoneUtils.Win32.cs │ ├── StringHelper.cs │ ├── TimeZoneInfoUtils.Win32.cs │ └── TimeZoneInfoUtils.cs ├── Readme.md └── Utilities ├── ByteUtils ├── BigEndianReader.cs ├── BigEndianWriter.cs ├── ByteReader.cs ├── ByteUtils.cs ├── ByteWriter.cs ├── LittleEndianReader.cs └── LittleEndianWriter.cs ├── Comparers └── ReverseComparer.cs ├── Conversion ├── BigEndianConverter.cs ├── Conversion.SimpleTypes.cs └── LittleEndianConverter.cs ├── DateTime └── DateTimeUtils.cs ├── Generics ├── BlockingQueue.cs ├── KeyValuePairList.cs ├── ListUtils.cs ├── Map.cs └── SortedList.cs ├── Properties └── AssemblyInfo.cs ├── Strings └── QuotedStringUtils.cs ├── Threading ├── CountdownLatch.cs └── Parallel.cs ├── TimeZoneInformation ├── RegistryTimeZoneInformation.cs └── SystemTime.cs └── Utilities.csproj /.gitattributes: -------------------------------------------------------------------------------- 1 | # Unsetting the text attribute on a path tells Git not to attempt any end-of-line conversion upon checkin or checkout 2 | * -text 3 | 4 | # Custom for Visual Studio 5 | *.cs -text diff=csharp 6 | *.csproj -text merge=union 7 | *.sln -text merge=union eol=crlf 8 | -------------------------------------------------------------------------------- /PSTFileFormat.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 9.00 3 | # Visual Studio 2005 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PSTFileFormat", "PSTFileFormat\PSTFileFormat.csproj", "{C5A7971B-86B5-44A7-B91B-5C276C891E9B}" 5 | EndProject 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Utilities", "Utilities\Utilities.csproj", "{AECB4E91-32AE-4AD1-AE1D-8FDBE33B224D}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {C5A7971B-86B5-44A7-B91B-5C276C891E9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {C5A7971B-86B5-44A7-B91B-5C276C891E9B}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {C5A7971B-86B5-44A7-B91B-5C276C891E9B}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {C5A7971B-86B5-44A7-B91B-5C276C891E9B}.Release|Any CPU.Build.0 = Release|Any CPU 18 | {AECB4E91-32AE-4AD1-AE1D-8FDBE33B224D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {AECB4E91-32AE-4AD1-AE1D-8FDBE33B224D}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {AECB4E91-32AE-4AD1-AE1D-8FDBE33B224D}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {AECB4E91-32AE-4AD1-AE1D-8FDBE33B224D}.Release|Any CPU.Build.0 = Release|Any CPU 22 | EndGlobalSection 23 | GlobalSection(SolutionProperties) = preSolution 24 | HideSolutionNode = FALSE 25 | EndGlobalSection 26 | EndGlobal 27 | -------------------------------------------------------------------------------- /PSTFileFormat/ListsTablesAndProperties/BTreeOnHeap/BTreeOnHeapDataRecord.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | 13 | namespace PSTFileFormat 14 | { 15 | public abstract class BTreeOnHeapDataRecord 16 | { 17 | public abstract int CompareTo(byte[] key); 18 | 19 | public abstract int CompareTo(BTreeOnHeapDataRecord record); 20 | 21 | public abstract void WriteBytes(byte[] buffer, int offset); 22 | 23 | public bool IsKeyEquals(byte[] key) 24 | { 25 | return (CompareTo(key) == 0); 26 | } 27 | 28 | public abstract byte[] Key 29 | { 30 | get; 31 | } 32 | 33 | public abstract int KeyLength 34 | { 35 | get; 36 | } 37 | 38 | public abstract int DataLength 39 | { 40 | get; 41 | } 42 | 43 | public int RecordLength 44 | { 45 | get 46 | { 47 | return KeyLength + DataLength; 48 | } 49 | } 50 | 51 | public static T CreateInstance(byte[] leafBytes, int offset) where T : BTreeOnHeapDataRecord 52 | { 53 | // this will call one of the following: 54 | // PropertyContextRecord(byte[] buffer, int offset) 55 | // TableContextRowID(byte[] buffer, int offset) 56 | T record = (T)Activator.CreateInstance(typeof(T), leafBytes, offset); 57 | return record; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /PSTFileFormat/ListsTablesAndProperties/BTreeOnHeap/BTreeOnHeapHeader.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public class BTreeOnHeapHeader // BTHHEADER 17 | { 18 | public int Length = 8; 19 | 20 | public OnHeapTypeName bType; 21 | public byte cbKey; // Size of the BTree Key value, in bytes 22 | public byte cbEnt; // Size of the data value, in bytes 23 | public byte bIdxLevels; // Index depth, 0 means the BTH root is a leaf (with data records) or an empty BTH. 24 | public HeapID hidRoot; // This is the HID that points to the BTH root for this BTHHEADER. 25 | 26 | public BTreeOnHeapHeader() 27 | { 28 | bType = OnHeapTypeName.bTypeBTH; 29 | hidRoot = HeapID.EmptyHeapID; // Set to 0 if the BTH is empty 30 | } 31 | 32 | public BTreeOnHeapHeader(byte[] buffer) : this(buffer, 0) 33 | { } 34 | 35 | public BTreeOnHeapHeader(byte[] buffer, int offset) 36 | { 37 | bType = (OnHeapTypeName)ByteReader.ReadByte(buffer, offset + 0); 38 | cbKey = ByteReader.ReadByte(buffer, offset + 1); 39 | cbEnt = ByteReader.ReadByte(buffer, offset + 2); 40 | bIdxLevels = ByteReader.ReadByte(buffer, offset + 3); 41 | hidRoot = new HeapID(buffer, offset + 4); 42 | } 43 | 44 | public byte[] GetBytes() 45 | { 46 | byte[] buffer = new byte[Length]; 47 | ByteWriter.WriteByte(buffer, 0, (byte)bType); 48 | ByteWriter.WriteByte(buffer, 1, cbKey); 49 | ByteWriter.WriteByte(buffer, 2, cbEnt); 50 | ByteWriter.WriteByte(buffer, 3, bIdxLevels); 51 | LittleEndianWriter.WriteUInt32(buffer, 4, hidRoot.Value); 52 | return buffer; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /PSTFileFormat/ListsTablesAndProperties/BTreeOnHeap/BTreeOnHeapIndexRecord.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public class BTreeOnHeapIndexRecord 17 | { 18 | public byte[] key; 19 | public HeapID hidNextLevel; 20 | 21 | // This field helps us during updates: 22 | public BTreeOnHeapIndex Index; 23 | 24 | public BTreeOnHeapIndexRecord() 25 | { 26 | } 27 | 28 | public BTreeOnHeapIndexRecord(byte[] buffer, int offset, int keyLength) 29 | { 30 | key = ByteReader.ReadBytes(buffer, offset, keyLength); 31 | hidNextLevel = new HeapID(buffer, offset + keyLength); 32 | } 33 | 34 | public void WriteBytes(byte[] buffer, int offset) 35 | { 36 | ByteWriter.WriteBytes(buffer, offset, key); 37 | LittleEndianWriter.WriteUInt32(buffer, offset + key.Length, hidNextLevel.Value); 38 | } 39 | 40 | public override bool Equals(object obj) 41 | { 42 | if (obj is BTreeOnHeapIndexRecord) 43 | { 44 | return (CompareTo((BTreeOnHeapIndexRecord)obj) == 0); 45 | } 46 | return false; 47 | } 48 | 49 | public int CompareTo(BTreeOnHeapIndexRecord record) 50 | { 51 | return CompareTo(record.key); 52 | } 53 | 54 | public int CompareTo(byte[] keyToCompare) 55 | { 56 | if (key.Length == 2 && keyToCompare.Length == 2) 57 | { 58 | return LittleEndianConverter.ToUInt16(key, 0).CompareTo(LittleEndianConverter.ToUInt16(keyToCompare, 0)); 59 | } 60 | else if (key.Length == 4 && keyToCompare.Length == 4) 61 | { 62 | return LittleEndianConverter.ToUInt32(key, 0).CompareTo(LittleEndianConverter.ToUInt32(keyToCompare, 0)); 63 | } 64 | else if (key.Length == 8 && keyToCompare.Length == 8) 65 | { 66 | return LittleEndianConverter.ToUInt64(key, 0).CompareTo(LittleEndianConverter.ToUInt64(keyToCompare, 0)); 67 | } 68 | else 69 | { 70 | // key.Length could be 16 71 | throw new NotImplementedException("Unsupported key length"); 72 | } 73 | } 74 | 75 | public override int GetHashCode() 76 | { 77 | return key.GetHashCode(); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /PSTFileFormat/ListsTablesAndProperties/Enums/OnHeapTypeName.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace PSTFileFormat 3 | { 4 | // This value describes the higher-level structure that is implemented on top of the Heap-on-Node. 5 | public enum OnHeapTypeName : byte // bType 6 | { 7 | bTypeTC = 0x7C, 8 | bTypeBTH = 0xB5, 9 | bTypePC = 0xBC, 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /PSTFileFormat/ListsTablesAndProperties/Enums/PropertyTypeName.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace PSTFileFormat 3 | { 4 | public enum PropertyTypeName 5 | { 6 | PtypInteger16 = 0x0002, 7 | PtypInteger32 = 0x0003, 8 | PtypFloating32 = 0x0004, 9 | PtypFloating64 = 0x0005, 10 | PtypErrorCode = 0x000A, // 4 bytes 11 | PtypBoolean = 0x000B, 12 | PtypObject = 0x000D, 13 | PtypInteger64 = 0x0014, 14 | PtypString8 = 0x001E, 15 | PtypString = 0x001F, 16 | PtypTime = 0x0040, 17 | PtypGuid = 0x0048, 18 | PtypBinary = 0x0102, 19 | PtypMultiString = 0x101F, 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /PSTFileFormat/ListsTablesAndProperties/Exceptions/MissingSubnodeException.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | 13 | namespace PSTFileFormat 14 | { 15 | public class MissingSubnodeException : Exception 16 | { 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /PSTFileFormat/ListsTablesAndProperties/HeapID.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public class HeapID // HID - 4 bytes 17 | { 18 | public const int MaximumHidIndex = 2047; // hidIndex is represented by 11 bytes 19 | public static readonly HeapID EmptyHeapID = new HeapID(0, 0); // The hid is set to zero if the item is empty 20 | 21 | public const int Length = 4; 22 | // An HID is a 4-byte value that identifies an item allocated from the heap. 23 | // hidType & hidIndex & hidBlockIndex together comprise the unique HID 24 | 25 | private uint m_heapID; 26 | 27 | public HeapID(uint heapID) 28 | { 29 | m_heapID = heapID; 30 | } 31 | 32 | public HeapID(byte[] buffer) : this(buffer, 0) 33 | { 34 | 35 | } 36 | 37 | public HeapID(byte[] buffer, int offset) 38 | { 39 | m_heapID = LittleEndianConverter.ToUInt32(buffer, offset); 40 | } 41 | 42 | public HeapID(ushort hidBlockIndex, ushort hidIndex) 43 | { 44 | if (hidIndex > MaximumHidIndex) 45 | { 46 | throw new ArgumentException("hidIndex is out of range, cannot allocate additional items"); 47 | } 48 | m_heapID = ((byte)NodeTypeName.NID_TYPE_HID & 0x1F); 49 | m_heapID |= (uint)(hidIndex & 0x7FF) << 5; 50 | m_heapID |= (uint)(hidBlockIndex << 16); 51 | } 52 | 53 | public NodeTypeName hidType 54 | { 55 | get 56 | { 57 | return (NodeTypeName)(m_heapID & 0x1F); 58 | } 59 | } 60 | 61 | /// 62 | /// 1-based index value that identifies an item allocated from the heap node 63 | /// 64 | public ushort hidIndex 65 | { 66 | get 67 | { 68 | return (ushort)((m_heapID >> 5) & 0x7FF); 69 | } 70 | } 71 | 72 | /// 73 | /// indicates the zero-based index of the data block in which this heap item resides. 74 | /// 75 | public ushort hidBlockIndex 76 | { 77 | get 78 | { 79 | return (ushort)(m_heapID >> 16); 80 | } 81 | } 82 | 83 | public uint Value 84 | { 85 | get 86 | { 87 | return m_heapID; 88 | } 89 | } 90 | 91 | // The hid is set to zero if the item is empty 92 | public bool IsEmpty 93 | { 94 | get 95 | { 96 | return (m_heapID == 0); 97 | } 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /PSTFileFormat/ListsTablesAndProperties/HeapOnNode/BlockData/HeapOnNodeBitmapBlockData.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public class HeapOnNodeBitmapBlockData : HeapOnNodeBlockData 17 | { 18 | public HeapOnNodeBitmapHeader BitmapHeader; 19 | 20 | public HeapOnNodeBitmapBlockData() 21 | { 22 | BitmapHeader = new HeapOnNodeBitmapHeader(); 23 | } 24 | 25 | public HeapOnNodeBitmapBlockData(byte[] buffer) 26 | { 27 | BitmapHeader = new HeapOnNodeBitmapHeader(buffer); 28 | PopulateHeapItems(buffer, BitmapHeader.ibHnpm); 29 | } 30 | 31 | public override void WriteHeader(byte[] buffer, int offset) 32 | { 33 | BitmapHeader.WriteBytes(buffer, offset); 34 | } 35 | 36 | public override int HeaderLength 37 | { 38 | get 39 | { 40 | return HeapOnNodeBitmapHeader.Length; 41 | } 42 | } 43 | 44 | public override ushort ibHnpm 45 | { 46 | get 47 | { 48 | return BitmapHeader.ibHnpm; 49 | } 50 | set 51 | { 52 | BitmapHeader.ibHnpm = value; 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /PSTFileFormat/ListsTablesAndProperties/HeapOnNode/BlockData/HeapOnNodeBlockData.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public abstract class HeapOnNodeBlockData // Refers to the decoded data within the data block 17 | { 18 | private HeapOnNodePageMap m_pageMap; 19 | public List HeapItems = new List(); 20 | 21 | public abstract void WriteHeader(byte[] buffer, int offset); 22 | 23 | public void PopulateHeapItems(byte[] buffer, ushort ibHnpm) 24 | { 25 | m_pageMap = new HeapOnNodePageMap(buffer, ibHnpm); 26 | 27 | int itemCount = m_pageMap.ItemCount; 28 | for (int itemIndex = 0; itemIndex < itemCount; itemIndex++) 29 | { 30 | int itemOffset = m_pageMap.GetHeapItemStartOffset(itemIndex); 31 | int itemSize = m_pageMap.GetHeapItemSize(itemIndex); 32 | byte[] itemBytes = new byte[itemSize]; 33 | Array.Copy(buffer, itemOffset, itemBytes, 0, itemSize); 34 | HeapItems.Add(itemBytes); 35 | } 36 | } 37 | 38 | public byte[] GetBytes() 39 | { 40 | int headerLength = this.HeaderLength; 41 | int dataLength = this.DataLength; 42 | // http://social.msdn.microsoft.com/Forums/en-US/os_binaryfile/thread/a5f9c653-40f5-4638-85d3-00c54607d984/ 43 | // Padding to align to 2 byte boundary must be appended 44 | int paddingLength = (int)Math.Ceiling((double)(headerLength + dataLength) / 2) * 2 - (headerLength + dataLength); 45 | 46 | int firstItemOffset = headerLength; 47 | this.ibHnpm = (ushort)(headerLength + dataLength + paddingLength); 48 | int length = headerLength + dataLength + paddingLength + HeapOnNodePageMap.CalculateRecordLength(this.HeapItems.Count); 49 | 50 | byte[] buffer = new byte[length]; 51 | WriteHeader(buffer, 0); 52 | WriteHeapItems(buffer, firstItemOffset); 53 | HeapOnNodePageMap.WriteBytes(buffer, ibHnpm, firstItemOffset, HeapItems); 54 | return buffer; 55 | } 56 | 57 | public void WriteHeapItems(byte[] buffer, int offset) 58 | { 59 | foreach(byte[] itemBytes in HeapItems) 60 | { 61 | ByteWriter.WriteBytes(buffer, offset, itemBytes); 62 | offset += itemBytes.Length; 63 | } 64 | } 65 | 66 | public abstract int HeaderLength 67 | { 68 | get; 69 | } 70 | 71 | public abstract ushort ibHnpm 72 | { 73 | get; 74 | set; 75 | } 76 | 77 | public int DataLength 78 | { 79 | get 80 | { 81 | int result = 0; 82 | for (int index = 0; index < HeapItems.Count; index++) 83 | { 84 | result += HeapItems[index].Length; 85 | } 86 | return result; 87 | } 88 | } 89 | 90 | public int AvailableSpace 91 | { 92 | get 93 | { 94 | return DataBlock.MaximumDataLength - this.HeaderLength - HeapOnNodePageMap.CalculateRecordLength(HeapItems.Count) - this.DataLength; 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /PSTFileFormat/ListsTablesAndProperties/HeapOnNode/BlockData/HeapOnNodeFirstBlockData.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public class HeapOnNodeFirstBlockData : HeapOnNodeBlockData 17 | { 18 | public HeapOnNodeHeader HeapHeader; 19 | 20 | /// 21 | /// Create new first block 22 | /// 23 | public HeapOnNodeFirstBlockData() 24 | { 25 | HeapHeader = new HeapOnNodeHeader(); 26 | } 27 | 28 | public HeapOnNodeFirstBlockData(byte[] buffer) 29 | { 30 | HeapHeader = new HeapOnNodeHeader(buffer, 0); 31 | PopulateHeapItems(buffer, HeapHeader.ibHnpm); 32 | } 33 | 34 | public override void WriteHeader(byte[] buffer, int offset) 35 | { 36 | HeapHeader.WriteBytes(buffer, offset); 37 | } 38 | 39 | public override int HeaderLength 40 | { 41 | get 42 | { 43 | return HeapOnNodeHeader.Length; 44 | } 45 | } 46 | 47 | public override ushort ibHnpm 48 | { 49 | get 50 | { 51 | return HeapHeader.ibHnpm; 52 | } 53 | set 54 | { 55 | HeapHeader.ibHnpm = value; 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /PSTFileFormat/ListsTablesAndProperties/HeapOnNode/BlockData/HeapOnNodePageBlockData.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public class HeapOnNodePageBlockData : HeapOnNodeBlockData 17 | { 18 | public HeapOnNodePageHeader PageHeader; 19 | 20 | public HeapOnNodePageBlockData() 21 | { 22 | PageHeader = new HeapOnNodePageHeader(); 23 | } 24 | 25 | public HeapOnNodePageBlockData(byte[] buffer) 26 | { 27 | PageHeader = new HeapOnNodePageHeader(buffer); 28 | PopulateHeapItems(buffer, PageHeader.ibHnpm); 29 | } 30 | 31 | public override void WriteHeader(byte[] buffer, int offset) 32 | { 33 | PageHeader.WriteBytes(buffer, offset); 34 | } 35 | 36 | public override int HeaderLength 37 | { 38 | get 39 | { 40 | return HeapOnNodePageHeader.Length; 41 | } 42 | } 43 | 44 | public override ushort ibHnpm 45 | { 46 | get 47 | { 48 | return PageHeader.ibHnpm; 49 | } 50 | set 51 | { 52 | PageHeader.ibHnpm = value; 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /PSTFileFormat/ListsTablesAndProperties/HeapOnNode/Header/HeapOnNodeBitmapHeader.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public class HeapOnNodeBitmapHeader // HNBITMAPHDR 17 | { 18 | public const int Length = 66; 19 | 20 | public ushort ibHnpm; 21 | public byte[] rgbFillLevel = new byte[128]; // 64 bytes, 128 entries 22 | 23 | public HeapOnNodeBitmapHeader() 24 | { 25 | } 26 | 27 | public HeapOnNodeBitmapHeader(byte[] buffer) : this(buffer, 0) 28 | { 29 | } 30 | 31 | public HeapOnNodeBitmapHeader(byte[] buffer, int offset) 32 | { 33 | ibHnpm = LittleEndianConverter.ToUInt16(buffer, offset + 0); 34 | rgbFillLevel = HeapOnNodeHelper.ReadFillLevelMap(buffer, offset + 2, 128); 35 | } 36 | 37 | public void WriteBytes(byte[] buffer, int offset) 38 | { 39 | LittleEndianWriter.WriteUInt16(buffer, offset + 0, ibHnpm); 40 | HeapOnNodeHelper.WriteFillLevelMap(buffer, offset + 2, rgbFillLevel); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /PSTFileFormat/ListsTablesAndProperties/HeapOnNode/Header/HeapOnNodeHeader.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public class HeapOnNodeHeader // HNHDR, appear at the first data block 17 | { 18 | public const byte HeapOnNodeBlockSignature = 0xEC; 19 | 20 | public const int Length = 12; 21 | 22 | public ushort ibHnpm; // The byte offset to the HN page Map record 23 | public byte bSig; 24 | public OnHeapTypeName bClientSig; 25 | public HeapID hidUserRoot; 26 | public byte[] rgbFillLevel = new byte[8]; // 4 bytes, 8 entries 27 | 28 | public HeapOnNodeHeader() 29 | { 30 | bSig = HeapOnNodeBlockSignature; // heap signature 31 | hidUserRoot = HeapID.EmptyHeapID; 32 | } 33 | 34 | public HeapOnNodeHeader(byte[] buffer) : this(buffer, 0) 35 | { } 36 | 37 | public HeapOnNodeHeader(byte[] buffer, int offset) 38 | { 39 | ibHnpm = LittleEndianConverter.ToUInt16(buffer, offset + 0); 40 | bSig = ByteReader.ReadByte(buffer, offset + 2); 41 | bClientSig = (OnHeapTypeName)ByteReader.ReadByte(buffer, offset + 3); 42 | hidUserRoot = new HeapID(buffer, offset + 4); 43 | rgbFillLevel = HeapOnNodeHelper.ReadFillLevelMap(buffer, offset + 8, 8); 44 | } 45 | 46 | public void WriteBytes(byte[] buffer, int offset) 47 | { 48 | LittleEndianWriter.WriteUInt16(buffer, offset + 0, ibHnpm); 49 | ByteWriter.WriteByte(buffer, offset + 2, bSig); 50 | ByteWriter.WriteByte(buffer, offset + 3, (byte)bClientSig); 51 | LittleEndianWriter.WriteUInt32(buffer, offset + 4, hidUserRoot.Value); 52 | HeapOnNodeHelper.WriteFillLevelMap(buffer, offset + 8, rgbFillLevel); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /PSTFileFormat/ListsTablesAndProperties/HeapOnNode/Header/HeapOnNodePageHeader.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public class HeapOnNodePageHeader // HNPAGEHDR 17 | { 18 | public const int Length = 2; 19 | 20 | public ushort ibHnpm; 21 | 22 | public HeapOnNodePageHeader() 23 | { 24 | } 25 | 26 | public HeapOnNodePageHeader(byte[] buffer) : this(buffer, 0) 27 | { 28 | } 29 | 30 | public HeapOnNodePageHeader(byte[] buffer, int offset) 31 | { 32 | ibHnpm = LittleEndianConverter.ToUInt16(buffer, offset + 0); 33 | } 34 | 35 | public void WriteBytes(byte[] buffer, int offset) 36 | { 37 | LittleEndianWriter.WriteUInt16(buffer, offset + 0, ibHnpm); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /PSTFileFormat/ListsTablesAndProperties/HeapOnNode/HeapOnNodeHelper.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public class HeapOnNodeHelper 17 | { 18 | public static byte[] ReadFillLevelMap(byte[] buffer, int offset, int numberOfEntries) 19 | { 20 | byte[] fillLevelMap = new byte[numberOfEntries]; 21 | int bytesToRead = numberOfEntries / 2; 22 | for (int index = 0; index < bytesToRead; index++) 23 | { 24 | byte entry1 = (byte)(buffer[offset] & 0xF); 25 | byte entry2 = (byte)(buffer[offset] >> 4); 26 | fillLevelMap[index * 2] = entry1; 27 | fillLevelMap[index * 2 + 1] = entry2; 28 | offset += 1; 29 | } 30 | return fillLevelMap; 31 | } 32 | 33 | public static void WriteFillLevelMap(byte[] buffer, int offset, byte[] fillLevelMap) 34 | { 35 | int bytesToWrite = fillLevelMap.Length / 2; 36 | for (int index = 0; index < bytesToWrite; index++) 37 | { 38 | byte fillLevelByte = (byte)(fillLevelMap[index * 2] & 0xF); 39 | fillLevelByte |= (byte)(fillLevelMap[index * 2 + 1] << 4); 40 | ByteWriter.WriteByte(buffer, offset, fillLevelByte); 41 | offset += 1; 42 | } 43 | } 44 | 45 | public static byte GetBlockFillLevel(HeapOnNodeBlockData blockData) 46 | { 47 | int availableSpace = blockData.AvailableSpace; 48 | if (availableSpace >= 3584) 49 | { 50 | return 0x00; 51 | } 52 | else if (availableSpace >= 2560) 53 | { 54 | return 0x01; 55 | } 56 | else if (availableSpace >= 2048) 57 | { 58 | return 0x02; 59 | } 60 | else if (availableSpace >= 1792) 61 | { 62 | return 0x03; 63 | } 64 | else if (availableSpace >= 1536) 65 | { 66 | return 0x04; 67 | } 68 | else if (availableSpace >= 1280) 69 | { 70 | return 0x05; 71 | } 72 | else if (availableSpace >= 1024) 73 | { 74 | return 0x06; 75 | } 76 | else if (availableSpace >= 768) 77 | { 78 | return 0x07; 79 | } 80 | else if (availableSpace >= 512) 81 | { 82 | return 0x08; 83 | } 84 | else if (availableSpace >= 256) 85 | { 86 | return 0x09; 87 | } 88 | else if (availableSpace >= 128) 89 | { 90 | return 0x0A; 91 | } 92 | else if (availableSpace >= 64) 93 | { 94 | return 0x0B; 95 | } 96 | else if (availableSpace >= 32) 97 | { 98 | return 0x0C; 99 | } 100 | else if (availableSpace >= 16) 101 | { 102 | return 0x0D; 103 | } 104 | else if (availableSpace >= 8) 105 | { 106 | return 0x0E; 107 | } 108 | else 109 | { 110 | return 0x0F; 111 | } 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /PSTFileFormat/ListsTablesAndProperties/HeapOrNodeID.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public class HeapOrNodeID // HNID 17 | { 18 | private HeapID m_heapID; 19 | private NodeID m_nodeID; 20 | 21 | public HeapOrNodeID(HeapID heapID) 22 | { 23 | m_heapID = heapID; 24 | } 25 | 26 | public HeapOrNodeID(NodeID nodeID) 27 | { 28 | m_nodeID = nodeID; 29 | } 30 | 31 | public HeapOrNodeID(byte[] buffer) : this(buffer, 0) 32 | { 33 | 34 | } 35 | 36 | public HeapOrNodeID(byte[] buffer, int offset) 37 | { 38 | HeapID tempHID = new HeapID(buffer, offset); 39 | if (tempHID.hidType == NodeTypeName.NID_TYPE_HID) 40 | { 41 | m_heapID = tempHID; 42 | } 43 | else 44 | { 45 | m_nodeID = new NodeID(buffer, offset); 46 | } 47 | } 48 | 49 | public bool IsHeapID 50 | { 51 | get 52 | { 53 | return (m_heapID != null); 54 | } 55 | } 56 | 57 | public bool IsEmpty 58 | { 59 | get 60 | { 61 | // Note if the uint value in the buffer is 0, then IsHeapID == true 62 | return (IsHeapID && m_heapID.Value == 0); 63 | } 64 | } 65 | 66 | /*public bool IsNodeID 67 | { 68 | get 69 | { 70 | return (m_nodeID != null); 71 | } 72 | }*/ 73 | 74 | public HeapID HeapID 75 | { 76 | get 77 | { 78 | return m_heapID; 79 | } 80 | } 81 | 82 | public NodeID NodeID 83 | { 84 | get 85 | { 86 | return m_nodeID; 87 | } 88 | } 89 | 90 | public uint Value 91 | { 92 | get 93 | { 94 | if (IsHeapID) 95 | { 96 | return m_heapID.Value; 97 | } 98 | else 99 | { 100 | return m_nodeID.Value; 101 | } 102 | } 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /PSTFileFormat/ListsTablesAndProperties/PropertyContext/PropertyContextHelper.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public class PropertyContextHelper 17 | { 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /PSTFileFormat/ListsTablesAndProperties/PropertyContext/PtypObjectRecord.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | // http://msdn.microsoft.com/en-us/library/gg491783%28v=office.12%29.aspx 17 | public class PtypObjectRecord 18 | { 19 | public NodeID Nid; 20 | public uint ulSize; 21 | 22 | public PtypObjectRecord(NodeID nodeID, uint size) 23 | { 24 | Nid = nodeID; 25 | ulSize = size; 26 | } 27 | 28 | public PtypObjectRecord(byte[] buffer) 29 | { 30 | Nid = new NodeID(buffer, 0x00); 31 | ulSize = LittleEndianConverter.ToUInt32(buffer, 0x04); 32 | } 33 | 34 | public byte[] GetBytes() 35 | { 36 | byte[] buffer = new byte[8]; 37 | LittleEndianWriter.WriteUInt32(buffer, 0, Nid.Value); 38 | LittleEndianWriter.WriteUInt32(buffer, 4, ulSize); 39 | return buffer; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /PSTFileFormat/ListsTablesAndProperties/Tables/TableContextRowID.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public class TableContextRowID : BTreeOnHeapDataRecord // TCROWID (BTH leaf record) 17 | { 18 | public const int RecordKeyLength = 4; 19 | public const int RecordDataLength = 4; 20 | public const int Length = 8; 21 | 22 | public uint dwRowID; 23 | public uint dwRowIndex; 24 | 25 | public TableContextRowID() 26 | { 27 | 28 | } 29 | 30 | public TableContextRowID(uint rowID, uint rowIndex) 31 | { 32 | dwRowID = rowID; 33 | dwRowIndex = rowIndex; 34 | } 35 | 36 | public TableContextRowID(byte[] buffer, int offset) 37 | { 38 | dwRowID = LittleEndianConverter.ToUInt32(buffer, offset + 0); 39 | dwRowIndex = LittleEndianConverter.ToUInt32(buffer, offset + 4); 40 | } 41 | 42 | public override void WriteBytes(byte[] buffer, int offset) 43 | { 44 | LittleEndianWriter.WriteUInt32(buffer, offset + 0, dwRowID); 45 | LittleEndianWriter.WriteUInt32(buffer, offset + 4, dwRowIndex); 46 | } 47 | 48 | public override byte[] Key 49 | { 50 | get 51 | { 52 | return LittleEndianConverter.GetBytes(dwRowID); 53 | } 54 | } 55 | 56 | public override int KeyLength 57 | { 58 | get 59 | { 60 | return RecordKeyLength; 61 | } 62 | } 63 | 64 | public override int DataLength 65 | { 66 | get 67 | { 68 | return RecordDataLength; 69 | } 70 | } 71 | 72 | public override int CompareTo(byte[] key) 73 | { 74 | if (key.Length == KeyLength) 75 | { 76 | return dwRowID.CompareTo(LittleEndianConverter.ToUInt32(key, 0)); 77 | } 78 | return -1; 79 | } 80 | 81 | public override int CompareTo(BTreeOnHeapDataRecord record) 82 | { 83 | if (record is TableContextRowID) 84 | { 85 | return dwRowID.CompareTo(((TableContextRowID)record).dwRowID); 86 | } 87 | return -1; 88 | } 89 | 90 | public override bool Equals(object obj) 91 | { 92 | if (obj is TableContextRowID) 93 | { 94 | return ((TableContextRowID)obj).dwRowID == dwRowID; 95 | } 96 | return false; 97 | } 98 | 99 | public override int GetHashCode() 100 | { 101 | return dwRowID.GetHashCode(); 102 | } 103 | 104 | public static int CompareByRowIndex(TableContextRowID a, TableContextRowID b) 105 | { 106 | return a.dwRowIndex.CompareTo(b.dwRowIndex); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Enums/AttachMethod.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace PSTFileFormat 3 | { 4 | public enum AttachMethod : uint 5 | { 6 | None = 0, 7 | ByValue = 1, 8 | ByReference = 2, 9 | ByReferenceOnly = 4, 10 | EmbeddedMessage = 5, 11 | Storage = 6, 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Enums/AttachmentFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PSTFileFormat 4 | { 5 | [Flags] 6 | public enum AttachmentFlags : uint 7 | { 8 | afException = 0x02, 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Enums/BusyStatus.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace PSTFileFormat 3 | { 4 | public enum BusyStatus : uint 5 | { 6 | Avaiable = 0x00, 7 | Tentative = 0x01, 8 | Busy = 0x02, 9 | OutOfOffice = 0x03, 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Enums/FolderItemTypeName.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace PSTFileFormat 3 | { 4 | public enum FolderItemTypeName 5 | { 6 | Unspecified, 7 | Appointment, 8 | Contact, 9 | Journal, 10 | Note, // Mail Messages and notes 11 | StickyNote, 12 | Task, 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Enums/IconIndex.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace PSTFileFormat 3 | { 4 | // http://msdn.microsoft.com/en-us/library/cc815472.aspx 5 | public enum IconIndex 6 | { 7 | NewMail = -1, 8 | SingleInstanceAppointment = 0x00000400, 9 | RecurringAppointment = 0x00000401, 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Enums/Importance.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace PSTFileFormat 3 | { 4 | public enum MessageImportance : uint 5 | { 6 | Low = 0x00, 7 | Normal = 0x01, 8 | High = 0x02, 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Enums/MeetingType.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace PSTFileFormat 3 | { 4 | public enum MeetingType 5 | { 6 | WindowsNetmeeting = 0x00000000, 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Enums/MessageFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace PSTFileFormat 6 | { 7 | [Flags] 8 | public enum MessageFlags : int 9 | { 10 | MSGFLAG_READ = 0x01, 11 | MSGFLAG_UNMODIFIED = 0x02, 12 | MSGFLAG_SUBMIT = 0x04, 13 | MSGFLAG_UNSENT = 0x08, 14 | MSGFLAG_HASATTACH = 0x10, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Enums/MessagePriority.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace PSTFileFormat 3 | { 4 | public enum MessagePriority : uint 5 | { 6 | Normal = 0x00000000, 7 | Urgent = 0x00000001, 8 | NotUrgent = 0xFFFFFFFF 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Enums/MessageSensitivity.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace PSTFileFormat 3 | { 4 | public enum MessageSensitivity 5 | { 6 | None = 0, // SENSITIVITY_NONE 7 | Personal = 1, // SENSITIVITY_PERSONAL 8 | Private = 2, // SENSITIVITY_PRIVATE 9 | CompanyConfidential = 3, // SENSITIVITY_COMPANY_CONFIDENTIAL 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Enums/ObjectType.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace PSTFileFormat 3 | { 4 | public enum ObjectType : uint 5 | { 6 | MailUser = 0x0006, 7 | AttachmentObject = 0x0007, 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Enums/PropertySetGuid.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PSTFileFormat 4 | { 5 | public class PropertySetGuid 6 | { 7 | // http://msdn.microsoft.com/en-us/library/ee219487%28v=exchg.80%29.aspx 8 | public static readonly Guid PS_MAPI = new Guid("{00020328-0000-0000-C000-000000000046}"); 9 | public static readonly Guid PS_PUBLIC_STRINGS = new Guid("{00020329-0000-0000-C000-000000000046}"); 10 | 11 | public static readonly Guid PS_INTERNET_HEADERS = new Guid("{00020386-0000-0000-C000-000000000046}"); 12 | public static readonly Guid PSETID_Common = new Guid("{00062008-0000-0000-C000-000000000046}"); 13 | public static readonly Guid PSETID_Appointment = new Guid("{00062002-0000-0000-C000-000000000046}"); 14 | public static readonly Guid PSETID_Meeting = new Guid("{6ED8DA90-450B-101B-98DA-00AA003F1305}"); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Enums/RecipientFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PSTFileFormat 4 | { 5 | [Flags] 6 | public enum RecipientFlags 7 | { 8 | SendableAttendee = 0x0001, 9 | MeetingOrganizer = 0x0002, 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Enums/RecipientType.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace PSTFileFormat 3 | { 4 | // http://msdn.microsoft.com/en-us/library/ee200746%28v=exchg.80%29.aspx 5 | public enum RecipientType : uint 6 | { 7 | To = 0x0001, // Required Atendee 8 | Cc = 0x0002, // Optional Atendee 9 | Bcc = 0x0003, // Resource (Room or Equipment) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Enums/SideEffectsFlags.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace PSTFileFormat 3 | { 4 | public enum SideEffectsFlags 5 | { 6 | seOpenToDelete = 0x00000001, 7 | seNoFrame = 0x00000008, 8 | seCoerceToInbox = 0x00000010, 9 | seOpenToCopy = 0x00000020, 10 | seOpenToMove = 0x00000040, 11 | seOpenForCtxMenu = 0x00000100, 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Enums/TaskMode.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace PSTFileFormat 3 | { 4 | public enum TaskMode 5 | { 6 | NotAssigned = 0x00, 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Exceptions/InvalidPropertyException.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | 13 | namespace PSTFileFormat 14 | { 15 | public class InvalidPropertyException : Exception 16 | { 17 | public InvalidPropertyException(string message) : base(message) 18 | { 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Exceptions/InvalidRecurrencePatternException.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | 13 | namespace PSTFileFormat 14 | { 15 | public class InvalidRecurrencePatternException : InvalidPropertyException 16 | { 17 | public InvalidRecurrencePatternException(string message) : base(message) 18 | { 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Folders/MailFolder.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2017 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public class MailFolder : PSTFolder 17 | { 18 | public MailFolder(PSTNode node) : base(node) 19 | { 20 | } 21 | 22 | public Note GetNote(int index) 23 | { 24 | TableContext tc = GetContentsTable(); 25 | if (tc != null) 26 | { 27 | if (index < tc.RowCount) 28 | { 29 | // dwRowID is the MessageID 30 | NodeID nodeID = new NodeID(tc.GetRowID(index)); 31 | Note note = Note.GetNote(this.File, nodeID); 32 | return note; 33 | } 34 | } 35 | return null; 36 | } 37 | 38 | public override void AddContentTableColumns(NamedTableContext contentsTable) 39 | { 40 | base.AddContentTableColumns(contentsTable); 41 | 42 | contentsTable.AddPropertyColumnIfNotExist(PropertyNames.PidLidSideEffects, PropertyTypeName.PtypInteger32); 43 | contentsTable.AddPropertyColumnIfNotExist(PropertyNames.PidLidHeaderItem, PropertyTypeName.PtypInteger32); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/HierarchyTable.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | 13 | namespace PSTFileFormat 14 | { 15 | public class HierarchyTable : TableContext 16 | { 17 | public HierarchyTable(HeapOnNode heap, SubnodeBTree subnodeBTree) : base(heap, subnodeBTree) 18 | { 19 | 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Messages/ConversationIndexHeader.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | // http://msdn.microsoft.com/en-us/library/ee202481%28v=exchg.80%29.aspx 17 | public class ConversationIndexHeader 18 | { 19 | public DateTime FileTime; 20 | public Guid Guid; 21 | 22 | public ConversationIndexHeader(Guid guid) 23 | { 24 | FileTime = DateTime.Now; 25 | Guid = guid; 26 | } 27 | 28 | public ConversationIndexHeader(byte[] buffer) 29 | { 30 | if (buffer[0] != 0x01) 31 | { 32 | throw new InvalidPropertyException("Invalid Conversation Index Header"); 33 | } 34 | 35 | // we want the most significant byte from current DateTime 36 | byte[] temp = BigEndianConverter.GetBytes(DateTime.Now.ToFileTimeUtc()); 37 | temp[6] = 0; 38 | temp[7] = 0; 39 | Array.Copy(buffer, 1, temp, 1, 5); 40 | FileTime = DateTime.FromFileTimeUtc(BigEndianConverter.ToInt64(temp, 0)); 41 | Guid = LittleEndianConverter.ToGuid(buffer, 6); 42 | } 43 | 44 | public byte[] GetBytes() 45 | { 46 | byte[] buffer = new byte[22]; 47 | // the first byte of filetime should be 1 48 | byte[] temp = BigEndianConverter.GetBytes(FileTime.ToFileTimeUtc()); 49 | Array.Copy(temp, 0, buffer, 0, 6); 50 | LittleEndianWriter.WriteGuidBytes(buffer, 6, Guid); 51 | return buffer; 52 | } 53 | 54 | public static ConversationIndexHeader GenerateNewConversationIndex() 55 | { 56 | return new ConversationIndexHeader(Guid.NewGuid()); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Messages/ModifiedAppointmentInstance.MessageProperties.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public partial class ModifiedAppointmentInstance 17 | { 18 | public string MessageClass 19 | { 20 | get 21 | { 22 | return PC.GetStringProperty(PropertyID.PidTagMessageClass); 23 | } 24 | set 25 | { 26 | PC.SetStringProperty(PropertyID.PidTagMessageClass, value); 27 | } 28 | } 29 | 30 | /// 31 | /// Maximum length: 255 characters 32 | /// 33 | public string Subject 34 | { 35 | get 36 | { 37 | return this.PC.GetStringProperty(PropertyID.PidTagSubject); 38 | } 39 | set 40 | { 41 | PC.SetStringProperty(PropertyID.PidTagSubject, value); 42 | PC.SetStringProperty(PropertyID.PidTagConversationTopic, value); 43 | } 44 | } 45 | 46 | public string Body 47 | { 48 | get 49 | { 50 | return this.PC.GetStringProperty(PropertyID.PidTagBody); 51 | } 52 | set 53 | { 54 | PC.SetStringProperty(PropertyID.PidTagBody, value); 55 | } 56 | } 57 | 58 | public bool AlternateRecipientAllowed 59 | { 60 | set 61 | { 62 | PC.SetBooleanProperty(PropertyID.PidTagAlternateRecipientAllowed, value); 63 | } 64 | } 65 | 66 | public MessageFlags MessageFlags 67 | { 68 | get 69 | { 70 | return (MessageFlags)PC.GetInt32Property(PropertyID.PidTagMessageFlags); 71 | } 72 | set 73 | { 74 | PC.SetInt32Property(PropertyID.PidTagMessageFlags, (int)value); 75 | } 76 | } 77 | 78 | public MessageImportance Importance 79 | { 80 | set 81 | { 82 | PC.SetInt32Property(PropertyID.PidTagImportance, (int)value); 83 | } 84 | } 85 | 86 | public MessagePriority Priority 87 | { 88 | set 89 | { 90 | PC.SetInt32Property(PropertyID.PidTagPriority, (int)value); 91 | } 92 | } 93 | 94 | public MessageSensitivity Sensitivity 95 | { 96 | set 97 | { 98 | PC.SetInt32Property(PropertyID.PidTagSensitivity, (int)value); 99 | } 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Messages/Note.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2017 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | /// 17 | /// Mail Message 18 | /// 19 | public class Note : MessageObject 20 | { 21 | protected Note(PSTNode node) : base(node) 22 | { 23 | } 24 | 25 | public override void SaveChanges() 26 | { 27 | base.SaveChanges(); 28 | } 29 | 30 | public static Note GetNote(PSTFile file, NodeID nodeID) 31 | { 32 | PSTNode node = file.GetNode(nodeID); 33 | NamedPropertyContext pc = node.PC; 34 | if (pc != null) 35 | { 36 | return new Note(node); 37 | } 38 | else 39 | { 40 | return null; 41 | } 42 | } 43 | 44 | public static Note CreateNewNote(PSTFile file, NodeID parentNodeID) 45 | { 46 | return CreateNewNote(file, parentNodeID, Guid.NewGuid()); 47 | } 48 | 49 | public static Note CreateNewNote(PSTFile file, NodeID parentNodeID, Guid searchKey) 50 | { 51 | MessageObject message = CreateNewMessage(file, FolderItemTypeName.Note, parentNodeID, searchKey); 52 | Note note = new Note(message); 53 | note.MessageFlags = MessageFlags.MSGFLAG_READ; 54 | note.InternetCodepage = 1255; 55 | note.MessageDeliveryTime = DateTime.UtcNow; 56 | note.ClientSubmitTime = DateTime.UtcNow; 57 | note.SideEffects = SideEffectsFlags.seOpenForCtxMenu | SideEffectsFlags.seOpenToMove | SideEffectsFlags.seOpenToCopy | SideEffectsFlags.seCoerceToInbox | SideEffectsFlags.seOpenToDelete; 58 | note.Importance = MessageImportance.Normal; 59 | note.Priority = MessagePriority.Normal; 60 | 61 | note.IconIndex = IconIndex.NewMail; 62 | return note; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Messages/RecurrencePatternStructure/CalendarHelper.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public class CalendarHelper 17 | { 18 | public static int CalculateNumberOfOccurences(DateTime startDate, DateTime lastInstanceStartDate, RecurrenceType recurrenceType, int period, int day) 19 | { 20 | startDate = DateTimeUtils.GetDayStart(startDate); 21 | lastInstanceStartDate = DateTimeUtils.GetDayStart(lastInstanceStartDate); 22 | 23 | if (recurrenceType == RecurrenceType.EveryNDays) 24 | { 25 | TimeSpan ts = lastInstanceStartDate - startDate; 26 | return ((int)ts.TotalDays) / period + 1; 27 | } 28 | else if (recurrenceType == RecurrenceType.EveryWeekday) 29 | { 30 | DaysOfWeekFlags weekdays = DateTimeHelper.Weekdays; 31 | return CalculateNumberOfOccurencesInWeek(startDate, lastInstanceStartDate, period, weekdays); 32 | } 33 | else if (recurrenceType == RecurrenceType.EveryNWeeks) 34 | { 35 | return CalculateNumberOfOccurencesInWeek(startDate, lastInstanceStartDate, period, (DaysOfWeekFlags)day); 36 | } 37 | else if (recurrenceType == RecurrenceType.EveryNMonths || 38 | recurrenceType == RecurrenceType.EveryNthDayOfEveryNMonths) 39 | { 40 | int numberOfMonths = DateTimeHelper.GetMonthSpan(startDate, lastInstanceStartDate); 41 | 42 | return numberOfMonths / period + 1; // extra day 43 | } 44 | else 45 | { 46 | int numberOfYears = lastInstanceStartDate.Year - startDate.Year; 47 | return numberOfYears / period + 1; 48 | } 49 | } 50 | 51 | public static int CalculateNumberOfOccurencesInWeek(DateTime startDate, DateTime lastInstanceStartDate, int period, DaysOfWeekFlags daysOfWeek) 52 | { 53 | TimeSpan ts = lastInstanceStartDate - startDate; 54 | int totalDays = (int)ts.TotalDays + 1; 55 | int daysOfWeekCount = GetSetBitCount((int)daysOfWeek); 56 | int numberOfWeeks = (totalDays / 7); 57 | int extraDays = totalDays - numberOfWeeks * 7; 58 | int extraOccurences = 0; 59 | 60 | DateTime date = DateTimeUtils.GetDayStart(startDate).AddDays(numberOfWeeks * 7); 61 | while (date <= lastInstanceStartDate) 62 | { 63 | if ((DateTimeHelper.GetDayOfWeek(date) & daysOfWeek) > 0) 64 | { 65 | extraOccurences++; 66 | } 67 | date = date.AddDays(1); 68 | } 69 | return numberOfWeeks * daysOfWeekCount / period + extraOccurences; 70 | } 71 | 72 | public static int GetSetBitCount(long lValue) 73 | { 74 | int iCount = 0; 75 | 76 | //Loop the value while there are still bits 77 | while (lValue != 0) 78 | { 79 | //Remove the end bit 80 | lValue = lValue & (lValue - 1); 81 | 82 | //Increment the count 83 | iCount++; 84 | } 85 | 86 | //Return the count 87 | return iCount; 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Messages/RecurrencePatternStructure/DailyRecurrencePatternStructure.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.IO; 12 | using System.Text; 13 | using Utilities; 14 | 15 | namespace PSTFileFormat 16 | { 17 | public enum DailyPatternType : ushort 18 | { 19 | EveryDay = 0, 20 | EveryWeekday = 1, 21 | } 22 | 23 | public class DailyRecurrencePatternStructure : AppointmentRecurrencePatternStructure 24 | { 25 | //public DaysOfWeekFlags DaysOfWeek; // when DailyPattern == EveryWeekday 26 | 27 | public DailyRecurrencePatternStructure() 28 | { 29 | RecurFrequency = RecurrenceFrequency.Daily; 30 | } 31 | 32 | public DailyRecurrencePatternStructure(byte[] buffer) : base(buffer) 33 | {} 34 | 35 | public override void ReadPatternTypeSpecific(byte[] buffer, ref int offset) 36 | { 37 | // we start reading from offset 22 38 | if (PatternType == PatternType.Day) 39 | { 40 | } 41 | else if (PatternType == PatternType.Week) // EveryWeekday 42 | { 43 | DaysOfWeekFlags DaysOfWeek = (DaysOfWeekFlags)LittleEndianReader.ReadUInt32(buffer, ref offset); 44 | if (DaysOfWeek != DateTimeHelper.Weekdays) 45 | { 46 | throw new InvalidRecurrencePatternException("Invalid DaysOfWeek for Daily Recurrence Pattern"); 47 | } 48 | } 49 | else 50 | { 51 | throw new InvalidRecurrencePatternException("Invalid Pattern Type"); 52 | } 53 | } 54 | 55 | public override void WritePatternTypeSpecific(Stream stream) 56 | { 57 | if (PatternType == PatternType.Day) 58 | { 59 | } 60 | else if (PatternType == PatternType.Week) // EveryWeekday 61 | { 62 | LittleEndianWriter.WriteUInt32(stream, (uint)DateTimeHelper.Weekdays); 63 | } 64 | else 65 | { 66 | throw new InvalidRecurrencePatternException("Invalid Pattern Type"); 67 | } 68 | } 69 | 70 | [Obsolete] 71 | public int PeriodInDays 72 | { 73 | get 74 | { 75 | if (PatternType == PatternType.Day) 76 | { 77 | return (int)(Period / 1440); 78 | } 79 | else 80 | { 81 | return 7; 82 | } 83 | } 84 | set 85 | { 86 | if (PatternType == PatternType.Day) 87 | { 88 | Period = (uint)value * 1440; 89 | } 90 | else 91 | { 92 | Period = (uint)value / 7; 93 | } 94 | } 95 | } 96 | 97 | [Obsolete] 98 | public int DaysToSkip 99 | { 100 | get 101 | { 102 | return (int)(FirstDateTime / 1440); 103 | } 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Messages/RecurrencePatternStructure/DateTimeHelper.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.IO; 12 | using System.Text; 13 | using Utilities; 14 | 15 | namespace PSTFileFormat 16 | { 17 | public class DateTimeHelper 18 | { 19 | public const DaysOfWeekFlags Weekdays = DaysOfWeekFlags.Monday | DaysOfWeekFlags.Tuesday | DaysOfWeekFlags.Wednesday | DaysOfWeekFlags.Thursday | DaysOfWeekFlags.Friday; 20 | 21 | public static DateTime ReadDateTimeFromMinutes(byte[] buffer, ref int offset) 22 | { 23 | uint minutesSince1601 = LittleEndianReader.ReadUInt32(buffer, ref offset); 24 | return GetDateTime(minutesSince1601, DateTimeKind.Unspecified); 25 | } 26 | 27 | public static DateTime ToDateTimeFromMinutes(byte[] buffer, int offset) 28 | { 29 | uint minutesSince1601 = LittleEndianConverter.ToUInt32(buffer, offset); 30 | return GetDateTime(minutesSince1601, DateTimeKind.Unspecified); 31 | } 32 | 33 | public static DateTime GetDateTime(uint minutesSince1601, DateTimeKind kind) 34 | { 35 | long fileTimeUtc = (long)minutesSince1601 * 60 * 10000000; 36 | DateTime result = DateTime.FromFileTimeUtc(fileTimeUtc); 37 | result = DateTime.SpecifyKind(result, kind); // We read as UTC to avoid conversion 38 | return result; 39 | } 40 | 41 | public static void WriteDateTimeInMinutes(Stream stream, DateTime dt) 42 | { 43 | dt = DateTime.SpecifyKind(dt, DateTimeKind.Utc); // We write as UTC to avoid conversion 44 | uint minutesSince1601 = GetMinutesSince1601(dt); 45 | LittleEndianWriter.WriteUInt32(stream, minutesSince1601); 46 | } 47 | 48 | public static void WriteDateTimeInMinutes(byte[] buffer, ref int offset, DateTime dt) 49 | { 50 | dt = DateTime.SpecifyKind(dt, DateTimeKind.Utc); // We write as UTC to avoid conversion 51 | uint minutesSince1601 = GetMinutesSince1601(dt); 52 | LittleEndianWriter.WriteUInt32(buffer, ref offset, minutesSince1601); 53 | } 54 | 55 | public static uint GetMinutesSince1601(DateTime dt) 56 | { 57 | uint minutesSince1601 = (uint)(dt.ToFileTimeUtc() / (60 * 10000000)); 58 | return minutesSince1601; 59 | } 60 | 61 | /// 62 | /// Calculate the Months needed to add to startDate in order for it to be in the same year and month as endDate 63 | /// 64 | public static int GetMonthSpan(DateTime startDate, DateTime endDate) 65 | { 66 | int result = 0; 67 | 68 | result = (endDate.Year - startDate.Year) * 12; 69 | result += endDate.Month - startDate.Month; 70 | 71 | return result; 72 | } 73 | 74 | public static DaysOfWeekFlags GetDayOfWeek(DateTime dt) 75 | { 76 | switch (dt.DayOfWeek) 77 | { 78 | case DayOfWeek.Sunday: 79 | return DaysOfWeekFlags.Sunday; 80 | case DayOfWeek.Monday: 81 | return DaysOfWeekFlags.Monday; 82 | case DayOfWeek.Tuesday: 83 | return DaysOfWeekFlags.Tuesday; 84 | case DayOfWeek.Wednesday: 85 | return DaysOfWeekFlags.Wednesday; 86 | case DayOfWeek.Thursday: 87 | return DaysOfWeekFlags.Thursday; 88 | case DayOfWeek.Friday: 89 | return DaysOfWeekFlags.Friday; 90 | default: 91 | case DayOfWeek.Saturday: 92 | return DaysOfWeekFlags.Saturday; 93 | } 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Messages/RecurrencePatternStructure/Enums/DayOccurenceNumber.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace PSTFileFormat 3 | { 4 | public enum DayOccurenceNumber : ushort 5 | { 6 | NotApplicable = 0, // We need this for serialization 7 | First = 1, 8 | Second = 2, 9 | Third = 3, 10 | Fourth = 4, 11 | Last = 5, 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Messages/RecurrencePatternStructure/Enums/DaysOfWeekFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PSTFileFormat 4 | { 5 | [Flags] 6 | public enum DaysOfWeekFlags : uint 7 | { 8 | Sunday = 0x01, 9 | Monday = 0x02, 10 | Tuesday = 0x04, 11 | Wednesday = 0x08, 12 | Thursday = 0x10, 13 | Friday = 0x20, 14 | Saturday = 0x40, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Messages/RecurrencePatternStructure/Enums/OutlookDayOfWeek.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace PSTFileFormat 3 | { 4 | public enum OutlookDayOfWeek : uint 5 | { 6 | Sunday = 0x01, 7 | Monday = 0x02, 8 | Tuesday = 0x04, 9 | Wednesday = 0x08, 10 | Thursday = 0x10, 11 | Friday = 0x20, 12 | Saturday = 0x40, 13 | Weekday = 0x3E, // e.g. the fourth weekday 14 | WeekendDay = 0x41, // e.g. the fourth weekend day 15 | Day = 0x7F, // e.g. the fourth day 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Messages/RecurrencePatternStructure/Enums/RecurrenceEnums.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PSTFileFormat 4 | { 5 | public enum RecurrenceFrequency : ushort 6 | { 7 | Daily = 0x200A, 8 | Weekly = 0x200B, 9 | Monthly = 0x200C, 10 | Yearly = 0x200D, 11 | } 12 | 13 | public enum PatternType : ushort 14 | { 15 | Day = 0x0000, 16 | Week = 0x0001, 17 | Month = 0x0002, 18 | MonthNth = 0x03, // i.e. the fourth monday of every month / the fourth monday of may 19 | } 20 | 21 | public enum RecurrenceEndType : uint 22 | { 23 | EndAfterDate = 0x00002021, 24 | EndAfterNOccurrences = 0x00002022, 25 | NeverEnd = 0x00002023, 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Messages/RecurrencePatternStructure/Enums/RecurrenceType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PSTFileFormat 4 | { 5 | /// 6 | /// This enum represents the combinations of RecurrenceFrequency and PatternType 7 | /// 8 | public enum RecurrenceType : ushort 9 | { 10 | None = 0, 11 | EveryNDays = 1, 12 | EveryWeekday = 2, 13 | EveryNWeeks = 3, 14 | EveryNMonths = 4, 15 | EveryNthDayOfEveryNMonths = 5, 16 | EveryNYears = 6, // Outlook 2007 GUI is no longer limited to 'every year' after applying KB950219 or SP2 17 | EveryNthDayOfEveryNYears = 7, // Outlook 2007 GUI is no longer limited to 'every year' after applying KB950219 or SP2 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Messages/RecurrencePatternStructure/MonthlyRecurrencePatternStructure.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.IO; 12 | using System.Text; 13 | using Utilities; 14 | 15 | namespace PSTFileFormat 16 | { 17 | public class MonthlyRecurrencePatternStructure : AppointmentRecurrencePatternStructure 18 | { 19 | public uint DayOfMonth; 20 | public OutlookDayOfWeek DayOfWeek; 21 | public DayOccurenceNumber DayOccurenceNumber; 22 | 23 | public MonthlyRecurrencePatternStructure() 24 | { 25 | RecurFrequency = RecurrenceFrequency.Monthly; 26 | } 27 | 28 | public MonthlyRecurrencePatternStructure(byte[] buffer) : base(buffer) 29 | { 30 | } 31 | 32 | public override void ReadPatternTypeSpecific(byte[] buffer, ref int offset) 33 | { 34 | // we start reading from offset 22 35 | if (PatternType == PatternType.Month) // i.e. the 23th of every month 36 | { 37 | DayOfMonth = LittleEndianReader.ReadUInt32(buffer, ref offset); 38 | } 39 | else if (PatternType == PatternType.MonthNth) // i.e. the fourth monday of every month 40 | { 41 | DayOfWeek = (OutlookDayOfWeek)LittleEndianReader.ReadUInt32(buffer, ref offset); 42 | DayOccurenceNumber = (DayOccurenceNumber)LittleEndianReader.ReadUInt32(buffer, ref offset); 43 | } 44 | else 45 | { 46 | throw new InvalidRecurrencePatternException("Invalid Pattern Type"); 47 | } 48 | } 49 | 50 | public override void WritePatternTypeSpecific(Stream stream) 51 | { 52 | if (PatternType == PatternType.Month) // i.e. the 23th of every month 53 | { 54 | LittleEndianWriter.WriteUInt32(stream, DayOfMonth); 55 | } 56 | else if (PatternType == PatternType.MonthNth) // i.e. the fourth monday of every month 57 | { 58 | LittleEndianWriter.WriteUInt32(stream, (uint)DayOfWeek); 59 | LittleEndianWriter.WriteUInt32(stream, (uint)DayOccurenceNumber); 60 | } 61 | else 62 | { 63 | throw new InvalidRecurrencePatternException("Invalid Pattern Type"); 64 | } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Messages/RecurrencePatternStructure/WeeklyRecurrencePatternStructure.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.IO; 12 | using System.Text; 13 | using Utilities; 14 | 15 | namespace PSTFileFormat 16 | { 17 | public class WeeklyRecurrencePatternStructure : AppointmentRecurrencePatternStructure 18 | { 19 | public DaysOfWeekFlags DaysOfWeek; 20 | 21 | public WeeklyRecurrencePatternStructure() 22 | { 23 | RecurFrequency = RecurrenceFrequency.Weekly; 24 | } 25 | 26 | public WeeklyRecurrencePatternStructure(byte[] buffer) : base(buffer) 27 | { 28 | } 29 | 30 | public override void ReadPatternTypeSpecific(byte[] buffer, ref int offset) 31 | { 32 | // we start reading from offset 22 33 | if (PatternType == PatternType.Week) // specific days in week 34 | { 35 | DaysOfWeek = (DaysOfWeekFlags)LittleEndianReader.ReadUInt32(buffer, ref offset); 36 | } 37 | else 38 | { 39 | throw new InvalidRecurrencePatternException("Invalid Pattern Type"); 40 | } 41 | } 42 | 43 | public override void WritePatternTypeSpecific(Stream stream) 44 | { 45 | if (PatternType == PatternType.Week) // specific days in week 46 | { 47 | LittleEndianWriter.WriteUInt32(stream, (uint)DaysOfWeek); 48 | } 49 | else 50 | { 51 | throw new InvalidRecurrencePatternException("Invalid Pattern Type"); 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Messages/RecurrencePatternStructure/YearlyRecurrencePatternStructure.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.IO; 12 | using System.Text; 13 | using Utilities; 14 | 15 | namespace PSTFileFormat 16 | { 17 | public class YearlyRecurrencePatternStructure : AppointmentRecurrencePatternStructure 18 | { 19 | public uint DayOfMonth; 20 | public OutlookDayOfWeek DayOfWeek; 21 | public DayOccurenceNumber DayOccurenceNumber; 22 | 23 | public YearlyRecurrencePatternStructure() 24 | { 25 | RecurFrequency = RecurrenceFrequency.Yearly; 26 | } 27 | 28 | public YearlyRecurrencePatternStructure(byte[] buffer) : base(buffer) 29 | { 30 | } 31 | 32 | public override void ReadPatternTypeSpecific(byte[] buffer, ref int offset) 33 | { 34 | // we start reading from offset 22 35 | if (PatternType == PatternType.Month) // i.e. the 23rd of may 36 | { 37 | DayOfMonth = LittleEndianReader.ReadUInt32(buffer, ref offset); 38 | } 39 | else if (PatternType == PatternType.MonthNth) // i.e. the fourth monday of may 40 | { 41 | DayOfWeek = (OutlookDayOfWeek)LittleEndianReader.ReadUInt32(buffer, ref offset); 42 | DayOccurenceNumber = (DayOccurenceNumber)LittleEndianReader.ReadUInt32(buffer, ref offset); 43 | } 44 | else 45 | { 46 | throw new InvalidRecurrencePatternException("Invalid Pattern Type"); 47 | } 48 | } 49 | 50 | public override void WritePatternTypeSpecific(Stream stream) 51 | { 52 | if (PatternType == PatternType.Month) // i.e. the 23rd of may 53 | { 54 | LittleEndianWriter.WriteUInt32(stream, DayOfMonth); 55 | } 56 | else if (PatternType == PatternType.MonthNth) // i.e. the fourth monday of may 57 | { 58 | LittleEndianWriter.WriteUInt32(stream, (uint)DayOfWeek); 59 | LittleEndianWriter.WriteUInt32(stream, (uint)DayOccurenceNumber); 60 | } 61 | else 62 | { 63 | throw new InvalidRecurrencePatternException("Invalid Pattern Type"); 64 | } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Messages/TimeZoneDefinitionStructure/Enums/TimeZoneRuleFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PSTFileFormat 4 | { 5 | [Flags] 6 | public enum TimeZoneRuleFlags 7 | { 8 | RecurringTimeZoneRule = 0x01, // TZRULE_FLAG_RECUR_CURRENT_TZREG, This flag specifies that this rule (4) is associated with a recurring series 9 | EffectiveTimeZoneRule = 0x02, // TZRULE_FLAG_EFFECTIVE_TZREG 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Messages/TimeZoneStructure/AdjustmentRuleHelper.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public class AdjustmentRuleHelper 17 | { 18 | public static SystemTime GetStandardDate(TimeZoneInfo.AdjustmentRule rule) 19 | { 20 | return FromTransitionTime(rule.DateStart.Year, rule.DateEnd.Year, rule.DaylightTransitionEnd); 21 | } 22 | 23 | public static SystemTime GetDaylightDate(TimeZoneInfo.AdjustmentRule rule) 24 | { 25 | return FromTransitionTime(rule.DateStart.Year, rule.DateEnd.Year, rule.DaylightTransitionStart); 26 | } 27 | 28 | // Details about the conversion process: 29 | // http://msdn.microsoft.com/en-us/library/windows/desktop/ms725481%28v=vs.85%29.aspx 30 | public static SystemTime FromTransitionTime(int startYear, int endYear, TimeZoneInfo.TransitionTime transitionTime) 31 | { 32 | SystemTime result = new SystemTime(); 33 | if (transitionTime.IsFixedDateRule) 34 | { 35 | if (startYear == endYear) 36 | { 37 | // If the wYear member is not zero, the transition date is absolute; it will only occur one time. 38 | int year = startYear; 39 | int month = transitionTime.Month; 40 | int day = transitionTime.Day; 41 | // this will calculate DayOfWeek 42 | TimeSpan timeOfDay = transitionTime.TimeOfDay.TimeOfDay; 43 | result.DateTime = new DateTime(year, month, day).Add(timeOfDay); 44 | // When rule is fixed-date, wDayOfWeek (which is redundant) is unused and set to 0 45 | // http://social.msdn.microsoft.com/Forums/en-US/os_binaryfile/thread/d5ebf7d3-f6a9-429d-8f27-7ec2bdec440f 46 | result.wDayOfWeek = 0; 47 | } 48 | else 49 | { 50 | // The SystemTime structure (and registry) do not support this 51 | throw new Exception("Cannot create transition time with absolute date that spans multiple years"); 52 | } 53 | } 54 | else 55 | { 56 | // If the wYear member is zero, it is a relative date that occurs yearly. 57 | result.wYear = 0; // Note: TimeZoneRuleStructure have an additional wYear parameter besides the one in stStandardDate / stDaylightDate 58 | result.wMonth = (ushort)transitionTime.Month; 59 | // the wDay member is set to indicate the occurrence of the day of the week within the month 60 | result.wDay = (ushort)transitionTime.Week; 61 | result.wDayOfWeek = (ushort)transitionTime.DayOfWeek; 62 | result.TimeOfDay = transitionTime.TimeOfDay.TimeOfDay; 63 | } 64 | return result; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/NamedProperties/NameID.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public class NameID 17 | { 18 | public const int Length = 8; 19 | 20 | public uint dwPropertyID; 21 | public bool IdentifierType; // a.k.a. N 22 | public ushort wGuid; // Index hint 23 | public ushort wPropIdx; // The Property ID of this named property is calculated by adding 0x8000 to wPropIndex 24 | 25 | public NameID(PropertyLongID propertyLongID, ushort propertySetGuidIndexHint, ushort propertyIndex) 26 | { 27 | dwPropertyID = (uint)propertyLongID; 28 | IdentifierType = false; 29 | wGuid = propertySetGuidIndexHint; 30 | wPropIdx = propertyIndex; 31 | } 32 | 33 | public NameID(byte[] buffer, int offset) 34 | { 35 | dwPropertyID = LittleEndianConverter.ToUInt32(buffer, offset + 0); 36 | ushort temp = LittleEndianConverter.ToUInt16(buffer, offset + 4); 37 | IdentifierType = (temp & 0x01) > 0; 38 | wGuid = (ushort)(temp >> 1); 39 | wPropIdx = LittleEndianConverter.ToUInt16(buffer, offset + 6); 40 | } 41 | 42 | public void WriteBytes(byte[] buffer, int offset) 43 | { 44 | LittleEndianWriter.WriteUInt32(buffer, offset + 0, dwPropertyID); 45 | ushort temp = (ushort)(wGuid << 1); 46 | if (IdentifierType) 47 | { 48 | temp |= 0x01; 49 | } 50 | LittleEndianWriter.WriteUInt16(buffer, offset + 4, temp); 51 | LittleEndianWriter.WriteUInt16(buffer, offset + 6, wPropIdx); 52 | } 53 | 54 | public override string ToString() 55 | { 56 | return String.Format("dwPropertyID: {0}, IdentifierType: {1}, wGuid: {2}, wPropIdx: {3}", dwPropertyID, IdentifierType, wGuid, wPropIdx); 57 | } 58 | 59 | public ushort PropertyShortID 60 | { 61 | get 62 | { 63 | return (ushort)(0x8000 + wPropIdx); 64 | } 65 | } 66 | 67 | public bool IsStringIdentifier 68 | { 69 | get 70 | { 71 | // false - the named property identifier is a 16-bit numerical value 72 | // true - the named property identifier is a string 73 | return (IdentifierType == true); 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/NamedProperties/PropertyName.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | 13 | namespace PSTFileFormat 14 | { 15 | public class PropertyName 16 | { 17 | public PropertyLongID PropertyLongID; 18 | public Guid PropertySetGuid; 19 | 20 | public PropertyName(PropertyLongID propertyLongID, Guid propertySetGuid) 21 | { 22 | PropertyLongID = propertyLongID; 23 | PropertySetGuid = propertySetGuid; 24 | } 25 | 26 | public override bool Equals(object obj) 27 | { 28 | if (obj is PropertyName) 29 | { 30 | return (PropertyLongID == ((PropertyName)obj).PropertyLongID && 31 | PropertySetGuid == ((PropertyName)obj).PropertySetGuid); 32 | } 33 | return false; 34 | } 35 | 36 | public override int GetHashCode() 37 | { 38 | return PropertyLongID.GetHashCode(); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Recipients/MessageRecipient.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | 13 | namespace PSTFileFormat 14 | { 15 | public class MessageRecipient 16 | { 17 | public string DisplayName; 18 | public string EmailAddress; 19 | public bool IsOrganizer; 20 | 21 | public MessageRecipient() 22 | { 23 | } 24 | 25 | public MessageRecipient(string displayName, string emailAddress, bool isOrganizer) 26 | { 27 | DisplayName = displayName; 28 | EmailAddress = emailAddress; 29 | IsOrganizer = isOrganizer; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Recipients/RecipientsTable.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2019 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public class RecipientsTable : TableContext 17 | { 18 | public RecipientsTable(HeapOnNode heap, SubnodeBTree subnodeBTree) : base(heap, subnodeBTree) 19 | { 20 | } 21 | 22 | public MessageRecipient GetRecipient(int rowIndex) 23 | { 24 | MessageRecipient result = new MessageRecipient(); 25 | result.DisplayName = GetStringProperty(rowIndex, PropertyID.PidTagDisplayName); 26 | result.EmailAddress = GetStringProperty(rowIndex, PropertyID.PidTagEmailAddress); 27 | if (ContainsPropertyColumn(PropertyID.PidTagRecipientFlags, PropertyTypeName.PtypInteger32)) 28 | { 29 | RecipientFlags recipientFlags = (RecipientFlags)GetInt32Property(rowIndex, PropertyID.PidTagRecipientFlags); 30 | result.IsOrganizer = ((recipientFlags & RecipientFlags.MeetingOrganizer) > 0); 31 | } 32 | return result; 33 | } 34 | 35 | public void AddRecipient(PSTFile file, MessageRecipient recipient) 36 | { 37 | AddRecipient(file, recipient.DisplayName, recipient.EmailAddress, recipient.IsOrganizer); 38 | } 39 | 40 | public void AddRecipient(PSTFile file, string displayName, string emailAddress, bool isOrganizer) 41 | { 42 | // http://social.msdn.microsoft.com/Forums/en-US/os_binaryfile/thread/a5f9c653-40f5-4638-85d3-00c54607d984/ 43 | // dwRowID must be > 0: 44 | uint rowID = (uint)RowCount + 1; // good enough for now 45 | int rowIndex = AddRow(rowID); 46 | SetStringProperty(rowIndex, PropertyID.PidTagDisplayName, displayName); 47 | SetStringProperty(rowIndex, PropertyID.PidTagAddressType, "SMTP"); 48 | SetStringProperty(rowIndex, PropertyID.PidTagEmailAddress, emailAddress); 49 | SetBytesProperty(rowIndex, PropertyID.PidTagSearchKey, LittleEndianConverter.GetBytes(Guid.NewGuid())); 50 | SetInt32Property(rowIndex, PropertyID.PidTagRecipientType, (int)RecipientType.To); 51 | SetInt32Property(rowIndex, PropertyID.PidTagObjectType, (int)ObjectType.MailUser); 52 | SetInt32Property(rowIndex, PropertyID.PidTagDisplayType, 0); 53 | 54 | SetStringProperty(rowIndex, PropertyID.PidTagRecipientDisplayName, displayName); 55 | 56 | int recipientFlags = (int)RecipientFlags.SendableAttendee; 57 | if (isOrganizer) 58 | { 59 | recipientFlags |= (int)RecipientFlags.MeetingOrganizer; 60 | } 61 | SetInt32Property(rowIndex, PropertyID.PidTagRecipientFlags, recipientFlags); 62 | SetInt32Property(rowIndex, PropertyID.PidTagRecipientTrackStatus, 0); 63 | 64 | byte[] recipientEntryID = RecipientEntryID.GetEntryID(displayName, emailAddress).GetBytes(); 65 | SetBytesProperty(rowIndex, PropertyID.PidTagEntryId, recipientEntryID); 66 | SetBytesProperty(rowIndex, PropertyID.PidTagRecipientEntryId, recipientEntryID); 67 | 68 | SetInt32Property(rowIndex, PropertyID.PidTagLtpRowId, (int)rowID); 69 | SetInt32Property(rowIndex, PropertyID.PidTagLtpRowVer, (int)file.Header.AllocateNextUniqueID()); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Search/Enums/SearchUpdateDescriptorFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PSTFileFormat 4 | { 5 | [Flags] 6 | public enum SearchUpdateDescriptorFlags : ushort 7 | { 8 | SUDF_PRIORITY_LOW = 0x0001, 9 | SUDF_PRIORITY_HIGH = 0x0002, 10 | SUDF_SEARCH_RESTART = 0x0004, 11 | SUDF_NAME_CHANGED = 0x0008, 12 | SUDF_MOVE_OUT_TO_IN = 0x0010, 13 | SUDF_MOVE_IN_TO_IN = 0x0020, 14 | SUDF_MOVE_IN_TO_OUT = 0x0040, 15 | SUDF_MOVE_OUT_TO_OUT = 0x0080, 16 | SUDF_SPAM_CHECK_SERVER = 0x0100, 17 | SUDF_SET_DEL_NAME = 0x0200, 18 | SUDF_SRCH_DONE = 0x0400, 19 | SUDF_DOMAIN_CHECKED = 0x8000, 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Search/Enums/SearchUpdateDescriptorType.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace PSTFileFormat 3 | { 4 | public enum SearchUpdateDescriptorType : ushort 5 | { 6 | SUDT_NULL = 0x00, 7 | SUDT_MSG_ADD = 0x01, 8 | SUDT_MSG_MOD = 0x02, 9 | SUDT_MSG_DEL = 0x03, 10 | SUDT_MSG_MOV = 0x04, 11 | SUDT_FLD_ADD = 0x05, 12 | SUDT_FLD_MOD = 0x06, 13 | SUDT_FLD_DEL = 0x07, 14 | SUDT_FLD_MOV = 0x08, 15 | SUDT_SRCH_ADD = 0x09, 16 | SUDT_SRCH_MOD = 0x0A, 17 | SUDT_SRCH_DEL = 0x0B, 18 | SUDT_MSG_ROW_MOD = 0x0C, 19 | SUDT_MSG_SPAM = 0x0D, 20 | SUDT_IDX_MSG_DEL = 0x0E, 21 | SUDT_MSG_IDX = 0x0F, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Search/SearchDomainObject.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public class SearchDomainObject 17 | { 18 | PSTFile m_file; 19 | byte[] m_data; 20 | 21 | public SearchDomainObject(PSTFile file) 22 | { 23 | m_file = file; 24 | } 25 | 26 | public bool ContainsNode(NodeID nodeID) 27 | { 28 | if (m_data == null) 29 | { 30 | PSTNode node = m_file.GetNode((uint)InternalNodeName.NID_SEARCH_DOMAIN_OBJECT); 31 | if (node.DataTree == null) 32 | { 33 | m_data = new byte[0]; 34 | } 35 | else 36 | { 37 | m_data = node.DataTree.GetData(); 38 | } 39 | } 40 | 41 | int nodeCount = m_data.Length / 4; 42 | for (int index = 0; index < nodeCount; index++) 43 | { 44 | uint currentNodeID = LittleEndianConverter.ToUInt32(m_data, index * 4); 45 | if (currentNodeID == nodeID.Value) 46 | { 47 | return true; 48 | } 49 | } 50 | return false; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Search/SearchUpdateDescriptor.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public class SearchUpdateDescriptor 17 | { 18 | public const int Length = 20; 19 | 20 | public SearchUpdateDescriptorFlags wFlags; 21 | public SearchUpdateDescriptorType wSUDType; 22 | public SearchUpdateDescriptorData SUDData; 23 | 24 | public SearchUpdateDescriptor(SearchUpdateDescriptorFlags flags, SearchUpdateDescriptorType type, SearchUpdateDescriptorData data) 25 | { 26 | wFlags = flags; 27 | wSUDType = type; 28 | SUDData = data; 29 | } 30 | 31 | public SearchUpdateDescriptor(byte[] buffer, int offset) 32 | { 33 | wFlags = (SearchUpdateDescriptorFlags)LittleEndianConverter.ToUInt16(buffer, offset + 0); 34 | wSUDType = (SearchUpdateDescriptorType)LittleEndianConverter.ToUInt16(buffer, offset + 2); 35 | switch (wSUDType) 36 | { 37 | case SearchUpdateDescriptorType.SUDT_FLD_ADD: 38 | case SearchUpdateDescriptorType.SUDT_FLD_MOV: 39 | SUDData = new SearchUpdateDescriptorFolderAdded(buffer, offset + 4); 40 | break; 41 | case SearchUpdateDescriptorType.SUDT_FLD_MOD: 42 | case SearchUpdateDescriptorType.SUDT_FLD_DEL: 43 | SUDData = new SearchUpdateDescriptorFolderModified(buffer, offset + 4); 44 | break; 45 | case SearchUpdateDescriptorType.SUDT_MSG_ADD: 46 | case SearchUpdateDescriptorType.SUDT_MSG_MOD: 47 | case SearchUpdateDescriptorType.SUDT_MSG_DEL: 48 | SUDData = new SearchUpdateDescriptorMessageAdded(buffer, offset + 4); 49 | break; 50 | default: 51 | throw new NotImplementedException("Unsupported SUD type"); 52 | } 53 | } 54 | 55 | public byte[] GetBytes() 56 | { 57 | byte[] buffer = new byte[Length]; 58 | LittleEndianWriter.WriteUInt16(buffer, 0, (ushort)wFlags); 59 | LittleEndianWriter.WriteUInt16(buffer, 2, (ushort)wSUDType); 60 | SUDData.WriteBytes(buffer, 4); 61 | 62 | return buffer; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Search/SearchUpdateDescriptorData/SearchUpdateDescriptorData.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public abstract class SearchUpdateDescriptorData 17 | { 18 | public const int Length = 16; 19 | 20 | public abstract void WriteBytes(byte[] buffer, int offset); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Search/SearchUpdateDescriptorData/SearchUpdateDescriptorFolderAdded.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | // SUD_FLD_ADD / SUD_FLD_MOV 17 | public class SearchUpdateDescriptorFolderAdded : SearchUpdateDescriptorData 18 | { 19 | public NodeID nidParent; 20 | public NodeID nidFld; // NID of the Folder object that was added or moved. 21 | 22 | public SearchUpdateDescriptorFolderAdded(NodeID parentNodeID, NodeID folderNodeID) 23 | { 24 | nidParent = parentNodeID; 25 | nidFld = folderNodeID; 26 | } 27 | 28 | public SearchUpdateDescriptorFolderAdded(byte[] buffer, int offset) 29 | { 30 | nidParent = new NodeID(buffer, offset + 0); 31 | nidFld = new NodeID(buffer, offset + 4); 32 | } 33 | 34 | public override void WriteBytes(byte[] buffer, int offset) 35 | { 36 | LittleEndianWriter.WriteUInt32(buffer, offset + 0, nidParent.Value); 37 | LittleEndianWriter.WriteUInt32(buffer, offset + 4, nidFld.Value); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Search/SearchUpdateDescriptorData/SearchUpdateDescriptorFolderModified.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | // SUD_FLD_MOD / SUD_FLD_DEL 17 | public class SearchUpdateDescriptorFolderModified : SearchUpdateDescriptorData 18 | { 19 | public NodeID nidFld; 20 | 21 | public SearchUpdateDescriptorFolderModified(NodeID folderNodeID) 22 | { 23 | nidFld = folderNodeID; 24 | } 25 | 26 | public SearchUpdateDescriptorFolderModified(byte[] buffer, int offset) 27 | { 28 | nidFld = new NodeID(buffer, offset + 0); 29 | } 30 | 31 | public override void WriteBytes(byte[] buffer, int offset) 32 | { 33 | LittleEndianWriter.WriteUInt32(buffer, offset + 0, nidFld.Value); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /PSTFileFormat/Messaging/Search/SearchUpdateDescriptorData/SearchUpdateDescriptorMessageAdded.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | // SUD_MSG_ADD / SUD_MSG_MOD / SUD_MSG_DEL Structure 17 | public class SearchUpdateDescriptorMessageAdded : SearchUpdateDescriptorData 18 | { 19 | public NodeID nidParent; // NID of the parent Folder object into which the Message object is added, modified, or deleted. 20 | public NodeID nidMsg; // NID of the Message object that was added, modified, or deleted. 21 | 22 | public SearchUpdateDescriptorMessageAdded(NodeID folderNodeID, NodeID messageNodeID) 23 | { 24 | nidParent = folderNodeID; 25 | nidMsg = messageNodeID; 26 | } 27 | 28 | public SearchUpdateDescriptorMessageAdded(byte[] buffer, int offset) 29 | { 30 | nidParent = new NodeID(buffer, offset + 0); 31 | nidMsg = new NodeID(buffer, offset + 4); 32 | } 33 | 34 | public override void WriteBytes(byte[] buffer, int offset) 35 | { 36 | LittleEndianWriter.WriteUInt32(buffer, offset + 0, nidParent.Value); 37 | LittleEndianWriter.WriteUInt32(buffer, offset + 4, nidMsg.Value); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /PSTFileFormat/NodeDatabse/BTree/BTreeIndexEntry.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public class BTreeIndexEntry // BTENTRY (Intermediate Entries) 17 | { 18 | public const int Length = 24; 19 | 20 | public ulong btkey; // All the entries in the child BTPAGE referenced by BREF have key values greater than or equal to this key value. 21 | public BlockRef BREF; 22 | 23 | public BTreeIndexEntry() 24 | { 25 | BREF = new BlockRef(); 26 | } 27 | 28 | public BTreeIndexEntry(byte[] buffer, int offset) 29 | { 30 | btkey = LittleEndianConverter.ToUInt64(buffer, offset + 0); 31 | BREF = new BlockRef(buffer, offset + 8); 32 | } 33 | 34 | public void WriteBytes(byte[] buffer, int offset) 35 | { 36 | LittleEndianWriter.WriteUInt64(buffer, offset + 0, btkey); 37 | BREF.WriteBytes(buffer, offset + 8); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /PSTFileFormat/NodeDatabse/BTree/BlockBTreeEntry.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public class BlockBTreeEntry // BBTENTRY (Leaf BBT Entry) 17 | { 18 | public BlockRef BREF; 19 | public ushort cb; // The count of bytes of the raw data contained in the block (excluding the block trailer and alignment padding) 20 | public ushort cRef; // Reference count: indicating the count of references to this block. 21 | //public uint dwPadding 22 | 23 | public BlockBTreeEntry() 24 | { 25 | BREF = new BlockRef(); 26 | } 27 | 28 | public BlockBTreeEntry(byte[] buffer, int offset) 29 | { 30 | BREF = new BlockRef(buffer, offset + 0); 31 | cb = LittleEndianConverter.ToUInt16(buffer, offset + 16); 32 | cRef = LittleEndianConverter.ToUInt16(buffer, offset + 18); 33 | } 34 | 35 | public byte[] GetBytes() 36 | { 37 | byte[] buffer = new byte[24]; 38 | WriteBytes(buffer, 0); 39 | return buffer; 40 | } 41 | 42 | public void WriteBytes(byte[] buffer, int offset) 43 | { 44 | BREF.WriteBytes(buffer, offset + 0); 45 | LittleEndianWriter.WriteUInt16(buffer, offset + 16, cb); 46 | LittleEndianWriter.WriteUInt16(buffer, offset + 18, cRef); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /PSTFileFormat/NodeDatabse/BTree/NodeBTreeEntry.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public class NodeBTreeEntry // NBTreeEntry (Leaf NBT Entry) 17 | { 18 | // to stay consistent with the size of the btkey member in BTENTRY, 19 | // the 4-byte NID is extended to its 8-byte equivalent 20 | public NodeID nid; 21 | // padding of 4 bytes (TODO: Verify) 22 | public BlockID bidData; // The BID of the data block for this node 23 | public BlockID bidSub; // The BID of the subnode block for this node. If this value is zero, a subnode block does not exist 24 | public NodeID nidParent; 25 | // public uint dwPadding; 26 | 27 | public NodeBTreeEntry() 28 | { 29 | } 30 | 31 | public NodeBTreeEntry(byte[] buffer, int offset) 32 | { 33 | nid = new NodeID(buffer, offset + 0); 34 | bidData = new BlockID(buffer, offset + 8); 35 | bidSub = new BlockID(buffer, offset + 16); 36 | nidParent = new NodeID(buffer, offset + 24); 37 | // 4 bytes of padding (Outlook does not always set these bytes to 0) 38 | } 39 | 40 | public byte[] GetBytes() 41 | { 42 | byte[] buffer = new byte[32]; 43 | return buffer; 44 | } 45 | 46 | public void WriteBytes(byte[] buffer, int offset) 47 | { 48 | LittleEndianWriter.WriteUInt32(buffer, offset + 0, nid.Value); 49 | LittleEndianWriter.WriteUInt64(buffer, offset + 8, bidData.Value); 50 | LittleEndianWriter.WriteUInt64(buffer, offset + 16, bidSub.Value); 51 | LittleEndianWriter.WriteUInt32(buffer, offset + 24, nidParent.Value); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /PSTFileFormat/NodeDatabse/Block/BlockID.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public class BlockID // BID 17 | { 18 | public const long MaximumBidIndex = 0xFFFFFFFFFFFFFFF; 19 | public const int Length = 8; 20 | 21 | // Reserved & Internel & bidIndex together comprise the unique BlockID 22 | private ulong m_blockID; 23 | 24 | public BlockID(ulong blockID) 25 | { 26 | m_blockID = blockID; 27 | } 28 | 29 | public BlockID(bool isInternal, ulong bidIndex) 30 | { 31 | this.Internal = isInternal; 32 | this.bidIndex = bidIndex; 33 | } 34 | 35 | public BlockID(byte[] buffer, int offset) 36 | { 37 | m_blockID = LittleEndianConverter.ToUInt64(buffer, offset + 0); 38 | } 39 | 40 | public void WriteBytes(byte[] buffer, int offset) 41 | { 42 | LittleEndianWriter.WriteUInt64(buffer, offset + 0, m_blockID); 43 | } 44 | 45 | public ulong Value 46 | { 47 | get 48 | { 49 | return m_blockID; 50 | } 51 | } 52 | 53 | public ulong LookupValue 54 | { 55 | get 56 | { 57 | // Readers MUST ignore the reserved bit and treat it as zero before looking up the BID from the BBT 58 | return m_blockID & 0xFFFFFFFFFFFFFFFEU; 59 | } 60 | } 61 | 62 | 63 | // first bit is 'reserved' 64 | // Office Outlook 2003, Office Outlook 2007, and Outlook 2010 use the reserved bit for implementation-specific data 65 | public bool Reserved 66 | { 67 | get 68 | { 69 | return (m_blockID & 0x01) != 0; 70 | } 71 | } 72 | 73 | // second bit is 'internal' 74 | public bool Internal 75 | { 76 | get 77 | { 78 | return (m_blockID & 0x02) != 0; 79 | } 80 | set 81 | { 82 | if (value) 83 | { 84 | m_blockID |= 0x02; 85 | } 86 | else 87 | { 88 | m_blockID &= 0xFFFFFFFD; 89 | } 90 | 91 | } 92 | } 93 | 94 | public ulong bidIndex 95 | { 96 | get 97 | { 98 | return m_blockID >> 2; 99 | } 100 | set 101 | { 102 | m_blockID &= 0x03; 103 | m_blockID |= (value << 2); 104 | } 105 | } 106 | 107 | public BlockID Clone() 108 | { 109 | return new BlockID(m_blockID); 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /PSTFileFormat/NodeDatabse/Block/BlockRef.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public class BlockRef // BREF 17 | { 18 | public const int Length = 16; 19 | 20 | public BlockID bid; 21 | public ulong ib; // byte offset from beginning of the file 22 | 23 | public BlockRef() 24 | { 25 | } 26 | 27 | public BlockRef(byte[] buffer, int offset) 28 | { 29 | bid = new BlockID(buffer, offset + 0); 30 | ib = LittleEndianConverter.ToUInt64(buffer, offset + 8); 31 | } 32 | 33 | public void WriteBytes(byte[] buffer, int offset) 34 | { 35 | bid.WriteBytes(buffer, offset + 0); 36 | LittleEndianWriter.WriteUInt64(buffer, offset + 8, ib); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /PSTFileFormat/NodeDatabse/Block/BlockTrailer.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public class BlockTrailer 17 | { 18 | public const int Length = 16; 19 | 20 | public ushort cb; 21 | public ushort wSig; 22 | public uint dwCRC; 23 | public BlockID bid; 24 | 25 | public BlockTrailer() 26 | { 27 | } 28 | 29 | public BlockTrailer(byte[] buffer, int offset) 30 | { 31 | cb = LittleEndianConverter.ToUInt16(buffer, offset + 0); 32 | wSig = LittleEndianConverter.ToUInt16(buffer, offset + 2); 33 | dwCRC = LittleEndianConverter.ToUInt32(buffer, offset + 4); 34 | bid = new BlockID(buffer, offset + 8); 35 | } 36 | 37 | public void WriteBytes(byte[] buffer, int dataLength, int offset, ulong fileOffset) 38 | { 39 | wSig = ComputeSignature(fileOffset, bid.Value); 40 | // CRC is only calculated on the raw data (i.e. excluding BlockTrailer and padding) 41 | dwCRC = PSTCRCCalculation.ComputeCRC(buffer, dataLength); 42 | 43 | LittleEndianWriter.WriteUInt16(buffer, offset + 0, cb); 44 | LittleEndianWriter.WriteUInt16(buffer, offset + 2, wSig); 45 | LittleEndianWriter.WriteUInt32(buffer, offset + 4, dwCRC); 46 | LittleEndianWriter.WriteUInt64(buffer, offset + 8, bid.Value); 47 | } 48 | 49 | public static BlockTrailer ReadFromEndOfBuffer(byte[] buffer) 50 | { 51 | return new BlockTrailer(buffer, buffer.Length - 16); 52 | } 53 | 54 | public static ushort ComputeSignature(ulong ib, ulong bid) 55 | { 56 | ib ^= bid; 57 | return ((ushort)((ushort)(ib >> 16) ^ (ushort)(ib))); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /PSTFileFormat/NodeDatabse/DataTree/DataBlock.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.IO; 12 | using System.Text; 13 | using Utilities; 14 | 15 | namespace PSTFileFormat 16 | { 17 | public class DataBlock : Block 18 | { 19 | public const int MaximumDataLength = 8176; // Block.MaximumLength - BlockTrailer.Length; 20 | 21 | private bCryptMethodName m_bCryptMethod; 22 | public byte[] Data = new byte[0]; 23 | 24 | public DataBlock(bCryptMethodName bCryptMethod) 25 | { 26 | m_bCryptMethod = bCryptMethod; 27 | } 28 | 29 | public DataBlock(byte[] buffer, bCryptMethodName bCryptMethod) : base(buffer) 30 | { 31 | m_bCryptMethod = bCryptMethod; 32 | Data = new byte[BlockTrailer.cb]; 33 | Array.Copy(buffer, Data, BlockTrailer.cb); 34 | 35 | // DataBlock's data may be decoded 36 | Data = GetDecodedData(); 37 | } 38 | 39 | public override void WriteDataBytes(byte[] buffer, ref int offset) 40 | { 41 | byte[] data = GetEncodedData(); 42 | ByteWriter.WriteBytes(buffer, offset, data); 43 | offset += data.Length; 44 | } 45 | 46 | public byte[] GetDecodedData() 47 | { 48 | byte[] result = new byte[Data.Length]; 49 | Array.Copy(Data, result, Data.Length); 50 | if (m_bCryptMethod == bCryptMethodName.NDB_CRYPT_PERMUTE) 51 | { 52 | PSTEncryptionUtils.CryptPermute(result, result.Length, false); 53 | } 54 | else if (m_bCryptMethod == bCryptMethodName.NDB_CRYPT_CYCLIC) 55 | { 56 | // The block trailer was supposed to be read at this stage. 57 | // [MS-PST]: the value to use for dwKey is the lower DWORD of the BID 58 | // associated with this data block. 59 | uint key = (uint)(BlockID.Value & 0xFFFFFFFF); 60 | PSTEncryptionUtils.CryptCyclic(result, result.Length, key); 61 | } 62 | return result; 63 | } 64 | 65 | public byte[] GetEncodedData() 66 | { 67 | byte[] result = new byte[Data.Length]; 68 | Array.Copy(Data, result, Data.Length); 69 | if (m_bCryptMethod == bCryptMethodName.NDB_CRYPT_PERMUTE) 70 | { 71 | PSTEncryptionUtils.CryptPermute(result, result.Length, true); 72 | } 73 | else if (m_bCryptMethod == bCryptMethodName.NDB_CRYPT_CYCLIC) 74 | { 75 | // [MS-PST]: the value to use for dwKey is the lower DWORD of the BID 76 | // associated with this data block. 77 | uint key = (uint)(BlockID.Value & 0xFFFFFFFF); 78 | PSTEncryptionUtils.CryptCyclic(result, result.Length, key); 79 | } 80 | return result; 81 | } 82 | 83 | public override Block Clone() 84 | { 85 | DataBlock result = (DataBlock)MemberwiseClone(); 86 | result.Data = new byte[Data.Length]; 87 | Array.Copy(Data, result.Data, Data.Length); 88 | return result; 89 | } 90 | 91 | // Raw data contained in the block (excluding trailer and alignment padding) 92 | public override int DataLength 93 | { 94 | get 95 | { 96 | return Data.Length; 97 | } 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /PSTFileFormat/NodeDatabse/DataTree/XBlock.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public class XBlock : Block 17 | { 18 | public const int MaximumNumberOfDataBlocks = 1021; // (8192 - 16 - 8) / 8 19 | public BlockType btype; 20 | public byte cLevel; 21 | //private ushort cEnt; 22 | public uint lcbTotal; // Total bytes of all the external data 23 | public List rgbid = new List(); 24 | 25 | public XBlock() 26 | { 27 | btype = BlockType.XBlock; 28 | cLevel = 0x01; // 0x01 for XBlock 29 | } 30 | 31 | public XBlock(byte[] buffer) : base(buffer) 32 | { 33 | btype = (BlockType)buffer[0]; 34 | cLevel = buffer[1]; 35 | ushort cEnt = LittleEndianConverter.ToUInt16(buffer, 2); 36 | lcbTotal = LittleEndianConverter.ToUInt32(buffer, 4); 37 | int position = 8; 38 | for (int index = 0; index < cEnt; index++) 39 | { 40 | BlockID bid = new BlockID(buffer, position); 41 | rgbid.Add(bid); 42 | position += 8; 43 | } 44 | } 45 | 46 | public override void WriteDataBytes(byte[] buffer, ref int offset) 47 | { 48 | ByteWriter.WriteByte(buffer, offset + 0, (byte)btype); 49 | ByteWriter.WriteByte(buffer, offset + 1, (byte)cLevel); 50 | LittleEndianWriter.WriteInt32(buffer, offset + 2, rgbid.Count); 51 | LittleEndianWriter.WriteUInt32(buffer, offset + 4, lcbTotal); 52 | offset = 8; 53 | for (int index = 0; index < rgbid.Count; index++) 54 | { 55 | LittleEndianWriter.WriteUInt64(buffer, offset, rgbid[index].Value); 56 | offset += 8; 57 | } 58 | } 59 | 60 | public override Block Clone() 61 | { 62 | XBlock result = (XBlock)MemberwiseClone(); 63 | result.rgbid = new List(); 64 | foreach (BlockID blockID in rgbid) 65 | { 66 | result.rgbid.Add(blockID.Clone()); 67 | } 68 | return result; 69 | } 70 | 71 | // Raw data contained in the block (excluding trailer and alignment padding) 72 | public override int DataLength 73 | { 74 | get 75 | { 76 | return 8 + rgbid.Count * 8; 77 | } 78 | } 79 | 80 | public int NumberOfDataBlocks 81 | { 82 | get 83 | { 84 | return rgbid.Count; 85 | } 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /PSTFileFormat/NodeDatabse/DataTree/XXBlock.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public class XXBlock : Block 17 | { 18 | public const int MaximumNumberOfXBlocks = 1021; // (8192 - 16 - 8) / 8 19 | public const int MaximumNumberOfDataBlocks = 1042441; // 1021 * 1021 20 | 21 | public BlockType btype; 22 | public byte cLevel; 23 | //private ushort cEnt; 24 | public uint lcbTotal; // Total bytes of all the external data 25 | public List rgbid = new List(); 26 | 27 | public XXBlock() 28 | { 29 | btype = BlockType.XXBlock; 30 | cLevel = 0x02; // 0x01 for XBlock 31 | } 32 | 33 | public XXBlock(byte[] buffer) : base(buffer) 34 | { 35 | btype = (BlockType)buffer[0]; 36 | cLevel = buffer[1]; 37 | ushort cEnt = LittleEndianConverter.ToUInt16(buffer, 2); 38 | lcbTotal = LittleEndianConverter.ToUInt32(buffer, 4); 39 | int position = 8; 40 | for (int index = 0; index < cEnt; index++) 41 | { 42 | BlockID bid = new BlockID(buffer, position); 43 | rgbid.Add(bid); 44 | position += 8; 45 | } 46 | } 47 | 48 | public override void WriteDataBytes(byte[] buffer, ref int offset) 49 | { 50 | ByteWriter.WriteByte(buffer, offset + 0, (byte)btype); 51 | ByteWriter.WriteByte(buffer, offset + 1, (byte)cLevel); 52 | LittleEndianWriter.WriteInt32(buffer, offset + 2, rgbid.Count); 53 | LittleEndianWriter.WriteUInt32(buffer, offset + 4, lcbTotal); 54 | offset = 8; 55 | for (int index = 0; index < rgbid.Count; index++) 56 | { 57 | LittleEndianWriter.WriteUInt64(buffer, offset, rgbid[index].Value); 58 | offset += 8; 59 | } 60 | } 61 | 62 | public override Block Clone() 63 | { 64 | XXBlock result = (XXBlock)MemberwiseClone(); 65 | result.rgbid = new List(); 66 | foreach (BlockID blockID in rgbid) 67 | { 68 | result.rgbid.Add(blockID.Clone()); 69 | } 70 | return result; 71 | } 72 | 73 | // Raw data contained in the block (excluding trailer and alignment padding) 74 | public override int DataLength 75 | { 76 | get 77 | { 78 | return 8 + rgbid.Count * 8; 79 | } 80 | } 81 | 82 | public int NumberOfXBlocks 83 | { 84 | get 85 | { 86 | return rgbid.Count; 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /PSTFileFormat/NodeDatabse/Enums/BlockType.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace PSTFileFormat 3 | { 4 | public enum BlockType : byte // btype 5 | { 6 | XBlock = 0x01, // XBlock and XXBlock has the same btype and different cLevel 7 | XXBlock = 0x01, // XBlock and XXBlock has the same btype and different cLevel 8 | 9 | SLBLOCK = 0x02, // SLBLOCK and SIBLOCK has the same btype and different cLevel 10 | SIBLOCK = 0x02, // SLBLOCK and SIBLOCK has the same btype and different cLevel 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /PSTFileFormat/NodeDatabse/Enums/InternalNodeName.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace PSTFileFormat 3 | { 4 | public enum InternalNodeName : uint // list of Special Internal NID values 5 | { 6 | NID_MESSAGE_STORE = 0x0021, 7 | NID_NAME_TO_ID_MAP = 0x0061, 8 | NID_NORMAL_FOLDER_TEMPLATE = 0x00A1, 9 | NID_SEARCH_FOLDER_TEMPLATE = 0x00C1, 10 | NID_ROOT_FOLDER = 0x0122, 11 | NID_SEARCH_MANAGEMENT_QUEUE = 0x01E1, 12 | //NID_SEARCH_ACTIVITY_LIST = 0x0201, // a.k.a. SAL 13 | NID_SEARCH_DOMAIN_OBJECT = 0x0261, 14 | NID_HIERARCHY_TABLE_TEMPLATE = 0x060D, 15 | NID_CONTENTS_TABLE_TEMPLATE = 0x060E, 16 | NID_ASSOC_CONTENTS_TABLE_TEMPLATE = 0x060F, 17 | NID_SEARCH_CONTENTS_TABLE_TEMPLATE = 0x0610, 18 | NID_ATTACHMENT_TABLE = 0x0671, 19 | NID_RECIPIENT_TABLE = 0x0692, 20 | // NID_SEARCH_ROOT_SEARCH_FOLDER = 0x0723, // GUST PC? 21 | // NID_SEARCH_UPDATE_QUEUE = 0x0726, 22 | // NID_SEARCH_CRITERIA_OBJECT = 0x0727, 23 | // NID_SEARCH_ROOT_SEARCH_CONTENT_TABLE = 0x0730, // GUST TC? 24 | NID_TOP_OF_PERSONAL_FOLDERS = 0x8022, 25 | //NID_TOP_OF_PERSONAL_FOLDERS_HIERARCHY_TABLE = 0x802D, 26 | //NID_SEARCH_FOLDER_HIERARCHY_TABLE = 0x804D, 27 | 28 | // OST file 29 | OST_NID_ROOT_PUBLIC = 0x2002, // child of NID_ROOT_FOLDER 30 | OST_NID_ROOT_MAILBOX = 0x20A2, // child of NID_ROOT_FOLDER 31 | OST_NID_TOP_OF_PERSONAL_FOLDERS = 0x2142, // child of OST_NID_ROOT_MAILBOX 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /PSTFileFormat/NodeDatabse/Enums/NodeTypeName.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace PSTFileFormat 3 | { 4 | public enum NodeTypeName : byte // NIDType 5 | { 6 | NID_TYPE_HID = 0x00, 7 | NID_TYPE_INTERNAL = 0x01, 8 | NID_TYPE_NORMAL_FOLDER = 0x02, 9 | NID_TYPE_SEARCH_FOLDER = 0x03, 10 | NID_TYPE_NORMAL_MESSAGE = 0x04, 11 | NID_TYPE_ATTACHMENT = 0x05, 12 | NID_TYPE_SEARCH_UPDATE_QUEUE = 0x06, 13 | NID_TYPE_SEARCH_CRITERIA_OBJECT = 0x07, 14 | NID_TYPE_ASSOC_MESSAGE = 0x08, 15 | NID_TYPE_CONTENTS_TABLE_INDEX = 0x0A, 16 | NID_TYPE_RECEIVE_FOLDER_TABLE = 0x0B, 17 | NID_TYPE_OUTGOING_QUEUE_TABLE = 0x0C, 18 | NID_TYPE_HIERARCHY_TABLE = 0x0D, 19 | NID_TYPE_CONTENTS_TABLE = 0x0E, 20 | NID_TYPE_ASSOC_CONTENTS_TABLE = 0x0F, 21 | NID_TYPE_SEARCH_CONTENTS_TABLE = 0x10, 22 | NID_TYPE_ATTACHMENT_TABLE = 0x11, 23 | NID_TYPE_RECIPIENT_TABLE = 0x12, 24 | NID_TYPE_SEARCH_TABLE_INDEX = 0x13, 25 | NID_TYPE_LTP = 0x1F, 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /PSTFileFormat/NodeDatabse/Enums/PageTypeName.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace PSTFileFormat 3 | { 4 | public enum PageTypeName : byte 5 | { 6 | ptypeBBT = 0x80, // Block BTree page. 7 | ptypeNBT = 0x81, // Node BTree page 8 | ptypeFMap = 0x82, // Free Map page 9 | ptypePMap = 0x83, // Allocation Page Map page 10 | ptypeAMap = 0x84, // Allocation Map page 11 | ptypeFPMap = 0x85, // Free Page Map page 12 | ptypeDL = 0x86, // Density List page 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /PSTFileFormat/NodeDatabse/Enums/WriterCompatibilityMode.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace PSTFileFormat 3 | { 4 | public enum WriterCompatibilityMode 5 | { 6 | Outlook2003RTM, 7 | Outlook2003SP3, // Will write TimeZoneDefinitionStartDisplay / EndDisplay 8 | Outlook2007RTM, // Do not use DList 9 | Outlook2007SP2, // Use DList 10 | Outlook2010RTM, 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /PSTFileFormat/NodeDatabse/Enums/bCryptMethodName.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace PSTFileFormat 3 | { 4 | public enum bCryptMethodName : byte 5 | { 6 | NDB_CRYPT_NONE = 0x00, // No Encryption 7 | NDB_CRYPT_PERMUTE = 0x01, // Compressible Encryption 8 | NDB_CRYPT_CYCLIC = 0x02, // HighEncryption 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /PSTFileFormat/NodeDatabse/Exceptions/InvalidAllocationMapException.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | 13 | namespace PSTFileFormat 14 | { 15 | public class InvalidAllocationMapException : Exception 16 | { 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /PSTFileFormat/NodeDatabse/Exceptions/InvalidBlockIDException.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | 13 | namespace PSTFileFormat 14 | { 15 | public class InvalidBlockIDException : Exception 16 | { 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /PSTFileFormat/NodeDatabse/Exceptions/InvalidChecksumException.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | 13 | namespace PSTFileFormat 14 | { 15 | public class InvalidChecksumException : Exception 16 | { 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /PSTFileFormat/NodeDatabse/NodeID.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public class NodeID // NID - 4 bytes 17 | { 18 | public const int MaximumNidIndex = 0x7FFFFFF; 19 | // nidType & nidIndex together comprise the unique NodeID 20 | 21 | // Note: 22 | // The documentation is not clear about the order of nidType and nidIndex, 23 | // it's bit reversed in [MS-PST] 24 | 25 | private uint m_nodeID; 26 | 27 | public NodeID(byte[] buffer, int offset) 28 | { 29 | m_nodeID = LittleEndianConverter.ToUInt32(buffer, offset + 0); 30 | } 31 | 32 | public NodeID(uint nid) 33 | { 34 | m_nodeID = nid; 35 | } 36 | 37 | public NodeID(NodeTypeName nidType, uint nidIndex) 38 | { 39 | m_nodeID = (byte)((byte)nidType & 0x1F); 40 | m_nodeID |= (nidIndex << 5); 41 | } 42 | 43 | public NodeTypeName nidType 44 | { 45 | get 46 | { 47 | return (NodeTypeName)(m_nodeID & 0x1F); 48 | } 49 | } 50 | 51 | public NodeID Clone() 52 | { 53 | return new NodeID(m_nodeID); 54 | } 55 | 56 | public uint nidIndex 57 | { 58 | get 59 | { 60 | return (m_nodeID >> 5); 61 | } 62 | } 63 | 64 | public uint Value 65 | { 66 | get 67 | { 68 | return m_nodeID; 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /PSTFileFormat/NodeDatabse/PSTNode.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | 13 | namespace PSTFileFormat 14 | { 15 | // "External" (as in not internal) node, with a unique NID within the Node database 16 | public class PSTNode : Node 17 | { 18 | private NodeID m_nodeID; 19 | 20 | public PSTNode(PSTFile file, NodeID nodeID, DataTree dataTree, SubnodeBTree subnodeBTree) : base(file, dataTree, subnodeBTree) 21 | { 22 | m_nodeID = nodeID; 23 | } 24 | 25 | public PSTNode(PSTNode node) : base(node.File, node.DataTree, node.SubnodeBTree) 26 | { 27 | m_nodeID = node.NodeID; 28 | } 29 | 30 | /// 31 | /// The Node BTree will be updated to point to the new data tree and subnode BTree 32 | /// 33 | public override void SaveChanges() 34 | { 35 | base.SaveChanges(); 36 | 37 | NodeBTreeEntry entry = File.FindNodeEntryByNodeID(m_nodeID.Value); 38 | ulong dataTreeRootBlockID = 0; 39 | if (DataTree != null && DataTree.RootBlock != null) 40 | { 41 | dataTreeRootBlockID = DataTree.RootBlock.BlockID.Value; 42 | } 43 | 44 | ulong subnodeBTreeRootBlockID = 0; 45 | if (SubnodeBTree != null && SubnodeBTree.RootBlock != null) 46 | { 47 | subnodeBTreeRootBlockID = SubnodeBTree.RootBlock.BlockID.Value; 48 | } 49 | 50 | if (entry.bidData.Value != dataTreeRootBlockID || 51 | entry.bidSub.Value != subnodeBTreeRootBlockID) 52 | { 53 | File.NodeBTree.UpdateNodeEntry(m_nodeID, DataTree, SubnodeBTree); 54 | } 55 | } 56 | 57 | public override void Delete() 58 | { 59 | base.Delete(); 60 | File.NodeBTree.DeleteNodeEntry(this.NodeID); 61 | } 62 | 63 | public NodeID NodeID 64 | { 65 | get 66 | { 67 | return m_nodeID; 68 | } 69 | } 70 | 71 | public NodeID ParentNodeID 72 | { 73 | get 74 | { 75 | NodeBTreeEntry entry = File.NodeBTree.FindNodeEntryByNodeID(m_nodeID.Value); 76 | if (entry != null) 77 | { 78 | return entry.nidParent; 79 | } 80 | else 81 | { 82 | return null; 83 | } 84 | } 85 | } 86 | 87 | public static PSTNode GetPSTNode(PSTFile file, NodeID nodeID) 88 | { 89 | NodeBTreeEntry entry = file.FindNodeEntryByNodeID(nodeID.Value); 90 | if (entry != null) 91 | { 92 | DataTree dataTree = null; 93 | if (entry.bidData.Value != 0) 94 | { 95 | Block rootDataBlock = file.FindBlockByBlockID(entry.bidData); 96 | dataTree = new DataTree(file, rootDataBlock); 97 | } 98 | 99 | SubnodeBTree subnodeBTree = null; 100 | if (entry.bidSub.Value != 0) 101 | { 102 | Block rootSubnodeBlock = file.FindBlockByBlockID(entry.bidSub); 103 | subnodeBTree = new SubnodeBTree(file, rootSubnodeBlock); 104 | } 105 | return new PSTNode(file, nodeID, dataTree, subnodeBTree); 106 | } 107 | else 108 | { 109 | return null; 110 | } 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /PSTFileFormat/NodeDatabse/Pages/DensityListPage.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public class DensityListPage : Page // DLISTPAGE 17 | { 18 | public const int FirstPageOffset = 0x4200; 19 | 20 | public const int MaxNumberOfEntries = 119; // 476 / 4 21 | 22 | public const byte DFL_BACKFILL_COMPLETE = 0x01; 23 | 24 | public byte bFlags; 25 | //public byte cEntDList; 26 | // 2 bytes padding 27 | // If DFL_BACKFILL_COMPLETE is set in bFlags, then ulCurrentPage indicates the AMap page index that is used 28 | // in the next allocation. If DFL_BACKFILL_COMPLETE is not set in bFlags, then ulCurrentPage indicates 29 | // the AMap page index that is attempted for backfilling in the next allocation. 30 | uint ulCurrentPage; 31 | List rgDListPageEnt = new List(); 32 | 33 | public DensityListPage(byte[] buffer) : base(buffer) 34 | { 35 | bFlags = buffer[0]; 36 | byte cEntDList = buffer[1]; 37 | ulCurrentPage = LittleEndianConverter.ToUInt32(buffer, 4); 38 | int offset = 8; 39 | for (int index = 0; index < cEntDList; index++) 40 | { 41 | DensityListPageEntry entry = new DensityListPageEntry(buffer, offset); 42 | rgDListPageEnt.Add(entry); 43 | offset += DensityListPageEntry.Length; 44 | } 45 | } 46 | 47 | public override byte[] GetBytes(ulong fileOffset) 48 | { 49 | if (rgDListPageEnt.Count > MaxNumberOfEntries) 50 | { 51 | throw new Exception("Density list contains too many entries"); 52 | } 53 | 54 | byte[] buffer = new byte[Length]; 55 | buffer[0] = bFlags; 56 | buffer[1] = (byte)rgDListPageEnt.Count; 57 | LittleEndianWriter.WriteUInt32(buffer, 4, ulCurrentPage); 58 | 59 | int offset = 8; 60 | foreach (DensityListPageEntry entry in rgDListPageEnt) 61 | { 62 | entry.WriteBytes(buffer, offset); 63 | offset += DensityListPageEntry.Length; 64 | } 65 | pageTrailer.dwCRC = PSTCRCCalculation.ComputeCRC(buffer, Length - PageTrailer.Length); 66 | pageTrailer.WriteToPage(buffer, fileOffset); 67 | 68 | return buffer; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /PSTFileFormat/NodeDatabse/Pages/DensityListPageEntry.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public class DensityListPageEntry // DLISTPAGEENT 17 | { 18 | public const int Length = 4; 19 | 20 | public uint dwPageNum; // 20 bits 21 | public ushort dwFreeSlots; // 12 bits 22 | 23 | public DensityListPageEntry(byte[] buffer, int offset) 24 | { 25 | uint temp = LittleEndianConverter.ToUInt32(buffer, offset); 26 | dwPageNum = temp & 0xFFFFF; 27 | dwFreeSlots = (ushort)(temp >> 20); 28 | } 29 | 30 | public void WriteBytes(byte[] buffer, int offset) 31 | { 32 | uint temp = dwPageNum & 0xFFFFF; 33 | temp |= ((dwFreeSlots & 0x3FFU) << 20); 34 | LittleEndianWriter.WriteUInt32(buffer, offset, temp); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /PSTFileFormat/NodeDatabse/Pages/FPMapPage.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public class FPMapPage : Page // FPMAPPAGE 17 | { 18 | public const int FirstPageOffset = 0x7C004A00; // offset of the first FMAPPAGE within the PST file (0x4A00 + 253952 * 64 * 128) 19 | public const long MapppedLength = 8061452288; // the number of bytes mapped by an FMap (496 * 253952 * 64) 20 | 21 | public byte[] rgbFPMapBits = new byte[496]; 22 | 23 | public FPMapPage() 24 | { 25 | pageTrailer.ptype = PageTypeName.ptypeFPMap; 26 | pageTrailer.wSig = 0x00; // zero for FPMap 27 | } 28 | 29 | public FPMapPage(byte[] buffer) : base(buffer) 30 | { 31 | Array.Copy(buffer, 0, rgbFPMapBits, 0, rgbFPMapBits.Length); 32 | } 33 | 34 | /// Irrelevant for AMap 35 | public override byte[] GetBytes(ulong fileOffset) 36 | { 37 | byte[] buffer = new byte[Length]; 38 | Array.Copy(rgbFPMapBits, 0, buffer, 0, rgbFPMapBits.Length); 39 | pageTrailer.WriteToPage(buffer, fileOffset); 40 | 41 | return buffer; 42 | } 43 | 44 | /// -1 for header 45 | public static int GetFPMapPageIndex(int aMapPageIndex) 46 | { 47 | if (aMapPageIndex < 128 * 64) 48 | { 49 | return -1; 50 | } 51 | else 52 | { 53 | return (aMapPageIndex - 128) / 496; 54 | } 55 | } 56 | 57 | public static int GetFPMapEntryIndex(int aMapPageIndex) 58 | { 59 | if (aMapPageIndex < 128 * 64) 60 | { 61 | return aMapPageIndex; 62 | } 63 | else 64 | { 65 | return (aMapPageIndex - 128 * 64) % (496 * 64); 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /PSTFileFormat/NodeDatabse/Pages/FreeMapPage.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.IO; 12 | using System.Text; 13 | using Utilities; 14 | 15 | namespace PSTFileFormat 16 | { 17 | public class FreeMapPage : Page // FMAPPAGE 18 | { 19 | public const int FirstPageOffset = 0x1F04800; // offset of the first FMAPPAGE within the PST file (0x4800 + 253952 * 128) 20 | public const int MapppedLength = 125960192; // the number of bytes mapped by an FMap (496 * 253952) 21 | 22 | public byte[] rgbFMapBits = new byte[496]; 23 | 24 | public FreeMapPage() 25 | { 26 | pageTrailer.ptype = PageTypeName.ptypeFMap; 27 | pageTrailer.wSig = 0x00; // zero for FMap 28 | } 29 | 30 | public FreeMapPage(byte[] buffer) : base(buffer) 31 | { 32 | Array.Copy(buffer, 0, rgbFMapBits, 0, rgbFMapBits.Length); 33 | } 34 | 35 | /// Irrelevant for AMap 36 | public override byte[] GetBytes(ulong fileOffset) 37 | { 38 | byte[] buffer = new byte[Length]; 39 | Array.Copy(rgbFMapBits, 0, buffer, 0, rgbFMapBits.Length); 40 | pageTrailer.WriteToPage(buffer, fileOffset); 41 | 42 | return buffer; 43 | } 44 | 45 | /// -1 for header 46 | public static int GetFreeMapPageIndex(int aMapPageIndex) 47 | { 48 | if (aMapPageIndex < 128) 49 | { 50 | return -1; 51 | } 52 | else 53 | { 54 | return (aMapPageIndex - 128) / 496; 55 | } 56 | } 57 | 58 | public static int GetFreeMapEntryIndex(int aMapPageIndex) 59 | { 60 | if (aMapPageIndex < 128) 61 | { 62 | return aMapPageIndex; 63 | } 64 | else 65 | { 66 | return (aMapPageIndex - 128) % 496; 67 | } 68 | } 69 | 70 | public void WriteFreeMapPage(PSTFile file, int fMapPageIndex) 71 | { 72 | long offset = FirstPageOffset + (long)MapppedLength * fMapPageIndex; 73 | WriteToStream(file.BaseStream, offset); 74 | } 75 | 76 | public static FreeMapPage ReadFreeMapPage(PSTFile file, int fMapPageIndex) 77 | { 78 | long offset = FirstPageOffset + (long)MapppedLength * fMapPageIndex; 79 | file.BaseStream.Seek(offset, SeekOrigin.Begin); 80 | byte[] buffer = new byte[Length]; 81 | file.BaseStream.Read(buffer, 0, Length); 82 | 83 | return new FreeMapPage(buffer); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /PSTFileFormat/NodeDatabse/Pages/PMapPage.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.IO; 12 | using System.Text; 13 | using Utilities; 14 | 15 | namespace PSTFileFormat 16 | { 17 | public class PMapPage : Page //PMAPPAGE 18 | { 19 | public const int FirstPageOffset = 0x4600; // offset of the first PMAPPAGE within the PST file 20 | public const int MapppedLength = 2031616; // the number of bytes mapped by a PMap (496 * 8 * 512) 21 | 22 | public byte[] rgbPMapBits = new byte[496]; 23 | 24 | public PMapPage() 25 | { 26 | pageTrailer.ptype = PageTypeName.ptypePMap; 27 | pageTrailer.wSig = 0x00; // zero for PMap 28 | } 29 | 30 | public PMapPage(byte[] buffer) : base(buffer) 31 | { 32 | Array.Copy(buffer, 0, rgbPMapBits, 0, rgbPMapBits.Length); 33 | } 34 | 35 | /// Irrelevant for AMap 36 | public override byte[] GetBytes(ulong fileOffset) 37 | { 38 | byte[] buffer = new byte[Length]; 39 | Array.Copy(rgbPMapBits, 0, buffer, 0, rgbPMapBits.Length); 40 | pageTrailer.WriteToPage(buffer, fileOffset); 41 | 42 | return buffer; 43 | } 44 | 45 | public static PMapPage GetFilledPMapPage() 46 | { 47 | PMapPage page = new PMapPage(); 48 | for (int index = 0; index < page.rgbPMapBits.Length; index++) 49 | { 50 | page.rgbPMapBits[index] = 0xFF; 51 | } 52 | return page; 53 | } 54 | 55 | public static PMapPage ReadPMapPage(PSTFile file, int pMapPageIndex) 56 | { 57 | // The first AMap of a PST file is always located at absolute file offset 0x4600, and subsequent PMaps appear at intervals of 2,031,616 bytes thereafter 58 | long offset = FirstPageOffset + (long)MapppedLength * pMapPageIndex; 59 | file.BaseStream.Seek(offset, SeekOrigin.Begin); 60 | byte[] buffer = new byte[Length]; 61 | file.BaseStream.Read(buffer, 0, Length); 62 | 63 | return new PMapPage(buffer); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /PSTFileFormat/NodeDatabse/Pages/Page.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.IO; 12 | using System.Text; 13 | using Utilities; 14 | 15 | namespace PSTFileFormat 16 | { 17 | public abstract class Page 18 | { 19 | public const int Length = 512; 20 | public PageTrailer pageTrailer; 21 | 22 | public Page() 23 | { 24 | pageTrailer = new PageTrailer(); 25 | } 26 | 27 | public Page(byte[] buffer) 28 | { 29 | pageTrailer = PageTrailer.ReadFromPage(buffer); 30 | } 31 | 32 | public abstract byte[] GetBytes(ulong fileOffset); 33 | 34 | public BlockID BlockID 35 | { 36 | get 37 | { 38 | return pageTrailer.bid; 39 | } 40 | set 41 | { 42 | pageTrailer.bid = value; 43 | } 44 | } 45 | 46 | public void WriteToStream(Stream stream, long offset) 47 | { 48 | stream.Seek(offset, SeekOrigin.Begin); 49 | stream.Write(GetBytes((ulong)offset), 0, Length); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /PSTFileFormat/NodeDatabse/Pages/PageTrailer.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public class PageTrailer // PAGETRAILER 17 | { 18 | public const int Length = 16; 19 | public const int OffsetFromPageStart = 496; 20 | 21 | public PageTypeName ptype; 22 | //public PageTypeName ptypeRepeat; 23 | public ushort wSig; 24 | public uint dwCRC; 25 | public BlockID bid; 26 | 27 | public PageTrailer() 28 | { 29 | } 30 | 31 | public PageTrailer(byte[] buffer, int offset) 32 | { 33 | ptype = (PageTypeName)buffer[offset + 0]; 34 | PageTypeName ptypeRepeat = (PageTypeName)buffer[offset + 1]; 35 | wSig = LittleEndianConverter.ToUInt16(buffer, offset + 2); 36 | dwCRC = LittleEndianConverter.ToUInt32(buffer, offset + 4); 37 | bid = new BlockID(buffer, offset + 8); 38 | } 39 | 40 | public void WriteToPage(byte[] buffer, ulong fileOffset) 41 | { 42 | if (ptype == PageTypeName.ptypeBBT || 43 | ptype == PageTypeName.ptypeNBT || 44 | ptype == PageTypeName.ptypeDL) 45 | { 46 | wSig = BlockTrailer.ComputeSignature(fileOffset, bid.Value); 47 | } 48 | else 49 | { 50 | wSig = 0; 51 | // AMap, PMap, FMap, and FPMap pages have a special convention where their BID is assigned the same value as their IB 52 | bid = new BlockID(fileOffset); 53 | } 54 | 55 | 56 | dwCRC = PSTCRCCalculation.ComputeCRC(buffer, OffsetFromPageStart); 57 | 58 | int offset = OffsetFromPageStart; 59 | buffer[offset + 0] = (byte)ptype; 60 | buffer[offset + 1] = (byte)ptype; 61 | LittleEndianWriter.WriteUInt16(buffer, offset + 2, wSig); 62 | LittleEndianWriter.WriteUInt32(buffer, offset + 4, dwCRC); 63 | LittleEndianWriter.WriteUInt64(buffer, offset + 8, bid.Value); 64 | } 65 | 66 | public static PageTrailer ReadFromPage(byte[] buffer) 67 | { 68 | return new PageTrailer(buffer, OffsetFromPageStart); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /PSTFileFormat/NodeDatabse/RootStructure.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public class RootStructure 17 | { 18 | public const int Length = 72; 19 | 20 | public const byte INVALID_AMAP = 0x00; 21 | public const byte VALID_AMAP1 = 0x01; // Outlook 2003 22 | public const byte VALID_AMAP2 = 0x02; // Outlook 2007+ 23 | 24 | public ulong ibFileEOF; 25 | public ulong ibAMapLast; 26 | public ulong cbAMapFree; 27 | public ulong cbPMapFree; 28 | public BlockRef BREFNBT; // Node BTree 29 | public BlockRef BREFBBT; // Block BTree 30 | public byte fAMapValid; 31 | // Outlook 2003-2010 use these value for implementation-specific data. 32 | // Modification of these values can result in failure to read the PST file by Outlook 33 | public byte bReserved; 34 | public ushort wReserved; 35 | 36 | public RootStructure(byte[] buffer, int offset) 37 | { 38 | ibFileEOF = LittleEndianConverter.ToUInt64(buffer, offset + 4); 39 | ibAMapLast = LittleEndianConverter.ToUInt64(buffer, offset + 12); 40 | cbAMapFree = LittleEndianConverter.ToUInt64(buffer, offset + 20); 41 | cbPMapFree = LittleEndianConverter.ToUInt64(buffer, offset + 28); 42 | BREFNBT = new BlockRef(buffer, offset + 36); 43 | BREFBBT = new BlockRef(buffer, offset + 52); 44 | fAMapValid = ByteReader.ReadByte(buffer, offset + 68); 45 | bReserved = ByteReader.ReadByte(buffer, offset + 69); 46 | wReserved = LittleEndianConverter.ToUInt16(buffer, offset + 70); 47 | } 48 | 49 | public void WriteBytes(byte[] buffer, int offset, WriterCompatibilityMode writerCompatibilityMode) 50 | { 51 | if (fAMapValid == VALID_AMAP1 && writerCompatibilityMode >= WriterCompatibilityMode.Outlook2007RTM) 52 | { 53 | fAMapValid = VALID_AMAP2; 54 | } 55 | 56 | LittleEndianWriter.WriteUInt64(buffer, offset + 4, ibFileEOF); 57 | LittleEndianWriter.WriteUInt64(buffer, offset + 12, ibAMapLast); 58 | LittleEndianWriter.WriteUInt64(buffer, offset + 20, cbAMapFree); 59 | LittleEndianWriter.WriteUInt64(buffer, offset + 28, cbPMapFree); 60 | BREFNBT.WriteBytes(buffer, offset + 36); 61 | BREFBBT.WriteBytes(buffer, offset + 52); 62 | ByteWriter.WriteByte(buffer, offset + 68, fAMapValid); 63 | ByteWriter.WriteByte(buffer, offset + 69, bReserved); 64 | LittleEndianWriter.WriteUInt16(buffer, offset + 70, wReserved); 65 | } 66 | 67 | public int NumberOfAllocationMapPages 68 | { 69 | get 70 | { 71 | return (int)((ibAMapLast - AllocationMapPage.FirstPageOffset) / AllocationMapPage.MapppedLength) + 1; 72 | } 73 | } 74 | 75 | public bool IsAllocationMapValid 76 | { 77 | get 78 | { 79 | return (fAMapValid == VALID_AMAP1 || fAMapValid == VALID_AMAP2); 80 | } 81 | set 82 | { 83 | if (value) 84 | { 85 | // We set it to VALID_AMAP2 during WriteBytes() if necessary 86 | fAMapValid = VALID_AMAP1; 87 | } 88 | else 89 | { 90 | fAMapValid = INVALID_AMAP; 91 | } 92 | } 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /PSTFileFormat/NodeDatabse/Subnode.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public class Subnode : Node 17 | { 18 | private NodeID m_subnodeID; 19 | 20 | public Subnode(PSTFile file, NodeID subnodeID, DataTree dataTree, SubnodeBTree subnodeBTree) 21 | : base(file, dataTree, subnodeBTree) 22 | { 23 | m_subnodeID = subnodeID; 24 | } 25 | 26 | public NodeID SubnodeID 27 | { 28 | get 29 | { 30 | return m_subnodeID; 31 | } 32 | } 33 | 34 | public void SaveChanges(SubnodeBTree parentSubnodeBTree) 35 | { 36 | SaveChanges(); 37 | parentSubnodeBTree.UpdateSubnodeEntry(m_subnodeID, this.DataTree, this.SubnodeBTree); 38 | } 39 | 40 | /// 41 | /// The entry will be removed from the parent subnode-BTree 42 | /// 43 | public void Delete(SubnodeBTree parentSubnodeBTree) 44 | { 45 | Delete(); 46 | parentSubnodeBTree.DeleteSubnodeEntry(m_subnodeID); 47 | } 48 | 49 | public static Subnode GetSubnode(PSTFile file, SubnodeLeafEntry entry) 50 | { 51 | DataTree dataTree = null; 52 | if (entry.bidData.Value != 0) 53 | { 54 | Block rootDataBlock = file.FindBlockByBlockID(entry.bidData); 55 | if (rootDataBlock == null) 56 | { 57 | throw new Exception("Cannot get subnode: missing data tree root block"); 58 | } 59 | dataTree = new DataTree(file, rootDataBlock); 60 | } 61 | 62 | SubnodeBTree subnodeBTree = null; 63 | if (entry.bidSub.Value != 0) 64 | { 65 | Block rootSubnodeBlock = file.FindBlockByBlockID(entry.bidSub); 66 | if (rootSubnodeBlock == null) 67 | { 68 | throw new Exception("Missing Subnode BTree Root Block"); 69 | } 70 | subnodeBTree = new SubnodeBTree(file, rootSubnodeBlock); 71 | } 72 | return new Subnode(file, entry.nid, dataTree, subnodeBTree); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /PSTFileFormat/NodeDatabse/SubnodeBTree/SubnodeIntermediateEntry.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public class SubnodeIntermediateEntry // SIENTRY 17 | { 18 | public const int Length = 16; 19 | 20 | public NodeID nid; // The key NID value to the next-level child block 21 | public BlockID bid; // The BID of the SLBLOCK 22 | 23 | public SubnodeIntermediateEntry() 24 | { 25 | } 26 | 27 | public SubnodeIntermediateEntry(NodeID nodeID, BlockID blockID) 28 | { 29 | nid = nodeID; 30 | bid = blockID; 31 | } 32 | 33 | public SubnodeIntermediateEntry(byte[] buffer, int offset) 34 | { 35 | // the 4-byte NID is extended to its 8-byte equivalent 36 | nid = new NodeID(buffer, offset); 37 | bid = new BlockID(buffer, offset + 8); 38 | } 39 | 40 | public void WriteBytes(byte[] buffer, int offset) 41 | { 42 | // the 4-byte NID is extended to its 8-byte equivalent 43 | LittleEndianWriter.WriteUInt32(buffer, offset + 0, nid.Value); 44 | LittleEndianWriter.WriteUInt64(buffer, offset + 8, bid.Value); 45 | } 46 | 47 | public SubnodeIntermediateEntry Clone() 48 | { 49 | return new SubnodeIntermediateEntry(nid.Clone(), bid.Clone()); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /PSTFileFormat/NodeDatabse/SubnodeBTree/SubnodeLeafEntry.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Utilities; 13 | 14 | namespace PSTFileFormat 15 | { 16 | public class SubnodeLeafEntry // SLENTRY 17 | { 18 | public const int Length = 24; 19 | 20 | public NodeID nid; 21 | public BlockID bidData; 22 | public BlockID bidSub; 23 | 24 | public SubnodeLeafEntry() 25 | { 26 | } 27 | 28 | public SubnodeLeafEntry(byte[] buffer, int offset) 29 | { 30 | // the 4-byte NID is extended to its 8-byte equivalent 31 | nid = new NodeID(buffer, offset + 0); 32 | bidData = new BlockID(buffer, offset + 8); 33 | bidSub = new BlockID(buffer, offset + 16); 34 | } 35 | 36 | public void WriteBytes(byte[] buffer, int offset) 37 | { 38 | // the 4-byte NID is extended to its 8-byte equivalent 39 | LittleEndianWriter.WriteUInt32(buffer, offset + 0, nid.Value); 40 | LittleEndianWriter.WriteUInt64(buffer, offset + 8, bidData.Value); 41 | LittleEndianWriter.WriteUInt64(buffer, offset + 16, bidSub.Value); 42 | } 43 | 44 | public SubnodeLeafEntry Clone() 45 | { 46 | SubnodeLeafEntry result = new SubnodeLeafEntry(); 47 | result.nid = nid.Clone(); 48 | result.bidData = bidData.Clone(); 49 | result.bidSub = bidSub.Clone(); 50 | return result; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /PSTFileFormat/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("PSTFileFormat")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("ROM Knowledgeware")] 12 | [assembly: AssemblyProduct("PSTFileFormat")] 13 | [assembly: AssemblyCopyright("Copyright © ROM Knowledgeware 2012-2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("06c10df0-2223-471e-9109-97001bd32890")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Revision and Build Numbers 33 | // by using the '*' as shown below: 34 | [assembly: AssemblyVersion("1.3.1.0")] 35 | [assembly: AssemblyFileVersion("1.3.1.0")] 36 | -------------------------------------------------------------------------------- /PSTFileFormat/Utils/AdjustmentRuleUtils.Win32.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Globalization; 12 | using System.Text; 13 | 14 | namespace Utilities 15 | { 16 | public partial class AdjustmentRuleUtils 17 | { 18 | /// 19 | /// This will return the static timezone adjustment rule the the OS uses. 20 | /// We assume that the static rule is used by one of the dynamic adjustment rules. 21 | /// 22 | public static TimeZoneInfo.AdjustmentRule FindSystemStaticAdjustmentRule(string keyName) 23 | { 24 | RegistryTimeZoneInformation information = RegistryTimeZoneUtils.GetStaticTimeZoneInformation(keyName); 25 | if (information == null) 26 | { 27 | throw new TimeZoneNotFoundException(); 28 | } 29 | else 30 | { 31 | return GetStaticAdjustmentRule(information); 32 | } 33 | } 34 | 35 | public static TimeZoneInfo.AdjustmentRule GetStaticAdjustmentRule(RegistryTimeZoneInformation information) 36 | { 37 | if (RegistryTimeZoneUtils.IsDaylightSavingsEnabled()) 38 | { 39 | return CreateStaticAdjustmentRule(information.Bias, information.StandardBias, information.DaylightBias, information.StandardDate, information.DaylightDate); 40 | } 41 | else 42 | { 43 | // We cant have an AdjustmentRule if there is no DST 44 | return null; 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /PSTFileFormat/Utils/RegistryTimeZoneUtils.Win32.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Microsoft.Win32; 13 | 14 | namespace Utilities 15 | { 16 | public partial class RegistryTimeZoneUtils 17 | { 18 | /// 19 | /// For dynamic DST use TimeZoneInfo 20 | /// 21 | /// /// null if the key does not contain timezone information 22 | public static RegistryTimeZoneInformation GetStaticTimeZoneInformation(string keyName) 23 | { 24 | RegistryKey timeZonesKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones"); 25 | RegistryKey timeZoneKey = timeZonesKey.OpenSubKey(keyName); 26 | if (timeZoneKey == null) 27 | { 28 | return null; 29 | } 30 | 31 | byte[] tzi = (byte[])timeZoneKey.GetValue("TZI"); 32 | if (tzi == null) 33 | { 34 | return null; 35 | } 36 | 37 | return new RegistryTimeZoneInformation(tzi); 38 | } 39 | 40 | public static string GetDisplayName(string keyName, out string standardDisplayName, out string daylightDisplayName) 41 | { 42 | RegistryKey timeZonesKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones"); 43 | RegistryKey timeZoneKey = timeZonesKey.OpenSubKey(keyName); 44 | if (timeZoneKey == null) 45 | { 46 | standardDisplayName = null; 47 | daylightDisplayName = null; 48 | return null; 49 | } 50 | 51 | string displayName = (string)timeZoneKey.GetValue("Display"); 52 | standardDisplayName = (string)timeZoneKey.GetValue("Std"); 53 | daylightDisplayName = (string)timeZoneKey.GetValue("Dlt"); 54 | 55 | return displayName; 56 | } 57 | 58 | public static bool IsDaylightSavingsEnabled() 59 | { 60 | RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\TimeZoneInformation"); 61 | int value = (int)key.GetValue("DisableAutoDaylightTimeSet", 0); 62 | return (value == 0); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /PSTFileFormat/Utils/StringHelper.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | 13 | namespace Utilities 14 | { 15 | public class StringHelper 16 | { 17 | public static string GetByteArrayString(byte[] array) 18 | { 19 | StringBuilder builder = new StringBuilder(); 20 | foreach (byte b in array) 21 | { 22 | builder.Append(b.ToString("X2")); // 2 digit hex 23 | builder.Append(" "); 24 | } 25 | return builder.ToString(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /PSTFileFormat/Utils/TimeZoneInfoUtils.Win32.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | 13 | namespace Utilities 14 | { 15 | public partial class TimeZoneInfoUtils 16 | { 17 | public static TimeZoneInfo GetSystemStaticTimeZone(string keyName) 18 | { 19 | RegistryTimeZoneInformation information = RegistryTimeZoneUtils.GetStaticTimeZoneInformation(keyName); 20 | TimeZoneInfo.AdjustmentRule rule = AdjustmentRuleUtils.GetStaticAdjustmentRule(information); 21 | string displayName; 22 | string standardDisplayName; 23 | string daylightDisplayName; 24 | displayName = RegistryTimeZoneUtils.GetDisplayName(keyName, out standardDisplayName, out daylightDisplayName); 25 | 26 | TimeSpan baseUtcOffset = new TimeSpan(0, -(information.Bias + information.StandardBias), 0); 27 | if (rule == null) 28 | { 29 | // null will only be returned if there is no daylight saving 30 | return TimeZoneInfo.CreateCustomTimeZone(keyName, baseUtcOffset, displayName, standardDisplayName); 31 | } 32 | else 33 | { 34 | return TimeZoneInfo.CreateCustomTimeZone(keyName, baseUtcOffset, displayName, standardDisplayName, daylightDisplayName, new TimeZoneInfo.AdjustmentRule[] { rule }); 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /PSTFileFormat/Utils/TimeZoneInfoUtils.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012-2016 ROM Knowledgeware. All rights reserved. 2 | * 3 | * You can redistribute this program and/or modify it under the terms of 4 | * the GNU Lesser Public License as published by the Free Software Foundation, 5 | * either version 3 of the License, or (at your option) any later version. 6 | * 7 | * Maintainer: Tal Aloni 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Text; 12 | using Microsoft.Win32; 13 | 14 | namespace Utilities 15 | { 16 | public partial class TimeZoneInfoUtils 17 | { 18 | public static bool AreEquivalent(TimeZoneInfo a, TimeZoneInfo b) 19 | { 20 | return (a.BaseUtcOffset == b.BaseUtcOffset && a.HasSameRules(b)); 21 | } 22 | 23 | public static DateTime SafeConvertToUtc(DateTime dateTime, TimeZoneInfo sourceTimeZone) 24 | { 25 | // time can be invalid when setting the clock one hour forward 26 | if (sourceTimeZone.IsInvalidTime(dateTime)) 27 | { 28 | TimeZoneInfo.AdjustmentRule rule = AdjustmentRuleUtils.GetAdjustmentRuleForTime(sourceTimeZone.GetAdjustmentRules(), dateTime.Date); 29 | // rule cannot be null for invalid time 30 | return TimeZoneInfo.ConvertTimeToUtc(dateTime.Add(rule.DaylightDelta), sourceTimeZone); 31 | } 32 | else 33 | { 34 | return TimeZoneInfo.ConvertTimeToUtc(dateTime, sourceTimeZone); 35 | } 36 | } 37 | 38 | public static DateTime AdvancedConvertToUtc(DateTime dateTime, TimeZoneInfo sourceTimeZone, bool assumeDaylightTimeForAmbiguousTime) 39 | { 40 | DateTime result = SafeConvertToUtc(dateTime, sourceTimeZone); 41 | 42 | // time can be ambiguous when setting the clock one hour backward 43 | if (sourceTimeZone.IsAmbiguousTime(dateTime) && assumeDaylightTimeForAmbiguousTime) 44 | { 45 | TimeZoneInfo.AdjustmentRule rule = AdjustmentRuleUtils.GetAdjustmentRuleForTime(sourceTimeZone.GetAdjustmentRules(), dateTime.Date); 46 | // ConvertTimeToUtc will assume standard time, we want it to assume daylight time 47 | // rule cannot be null for ambiguous time 48 | result = result.Add(-rule.DaylightDelta); 49 | } 50 | 51 | return result; 52 | } 53 | 54 | public static DateTime SetValidTimeOfDay(DateTime dateTime, TimeSpan timeOfDay, TimeZoneInfo timezone) 55 | { 56 | dateTime = DateTimeUtils.SetTimeOfDay(dateTime, timeOfDay); 57 | // time can be invalid when setting the clock one hour forward 58 | if (timezone.IsInvalidTime(dateTime)) 59 | { 60 | TimeZoneInfo.AdjustmentRule rule = AdjustmentRuleUtils.GetAdjustmentRuleForTime(timezone.GetAdjustmentRules(), dateTime.Date); 61 | // rule cannot be null for invalid time 62 | dateTime = dateTime.Add(rule.DaylightDelta); 63 | } 64 | 65 | dateTime = DateTime.SpecifyKind(dateTime, DateTimeKind.Unspecified); 66 | return dateTime; 67 | } 68 | 69 | public static TimeZoneInfo FindSystemTimeZoneByDisplayName(string displayName) 70 | { 71 | foreach (TimeZoneInfo timezone in TimeZoneInfo.GetSystemTimeZones()) 72 | { 73 | if (timezone.DisplayName == displayName) 74 | { 75 | return timezone; 76 | } 77 | } 78 | return null; 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | PSTFileFormat is an open source library written in C# designed to read and write to Outlook PST files. 2 | 3 | Usage Examples: 4 | =============== 5 | List all folders in a PST file: 6 | 7 | public void ListAllFolders(PSTFile file) 8 | { 9 | tvFolders.Nodes.Clear(); 10 | PSTFolder rootFolder = file.TopOfPersonalFolders; 11 | TreeNode rootNode = new TreeNode(rootFolder.DisplayName); 12 | rootNode.Name = rootFolder.NodeID.Value.ToString(); 13 | tvFolders.Nodes.Add(rootNode); 14 | AddSubFolders(rootNode, rootFolder); 15 | } 16 | 17 | public void AddSubFolders(TreeNode node, PSTFolder folder) 18 | { 19 | List childFolders = folder.GetChildFolders(); 20 | foreach(PSTFolder childFolder in childFolders) 21 | { 22 | TreeNode childNode = new TreeNode(childFolder.DisplayName + " (" + childFolder.ItemType.ToString() + ")" ); 23 | childNode.Name = childFolder.NodeID.Value.ToString(); 24 | node.Nodes.Add(childNode); 25 | AddSubFolders(childNode, childFolder); 26 | } 27 | } 28 | 29 | Technical References: 30 | ===================== 31 | 1. https://msdn.microsoft.com/en-us/library/ff385210(v=office.12).aspx 32 | 33 | 2. https://blogs.msdn.microsoft.com/openspecification/2010/11/30/ms-pst-how-to-navigate-the-node-btree/ 34 | 35 | 3. https://blogs.msdn.microsoft.com/openspecification/2011/02/11/ms-pst-the-relationship-between-nodes-and-blocks/ 36 | 37 | License: 38 | ======== 39 | PSTFileFormat is licensed under the GNU Lesser Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 40 | -------------------------------------------------------------------------------- /Utilities/ByteUtils/BigEndianReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | 5 | namespace Utilities 6 | { 7 | public class BigEndianReader 8 | { 9 | public static short ReadInt16(byte[] buffer, ref int offset) 10 | { 11 | offset += 2; 12 | return BigEndianConverter.ToInt16(buffer, offset - 2); 13 | } 14 | 15 | public static ushort ReadUInt16(byte[] buffer, ref int offset) 16 | { 17 | offset += 2; 18 | return BigEndianConverter.ToUInt16(buffer, offset - 2); 19 | } 20 | 21 | public static int ReadInt32(byte[] buffer, ref int offset) 22 | { 23 | offset += 4; 24 | return BigEndianConverter.ToInt32(buffer, offset - 4); 25 | } 26 | 27 | public static uint ReadUInt32(byte[] buffer, ref int offset) 28 | { 29 | offset += 4; 30 | return BigEndianConverter.ToUInt32(buffer, offset - 4); 31 | } 32 | 33 | public static long ReadInt64(byte[] buffer, ref int offset) 34 | { 35 | offset += 8; 36 | return BigEndianConverter.ToInt64(buffer, offset - 8); 37 | } 38 | 39 | public static ulong ReadUInt64(byte[] buffer, ref int offset) 40 | { 41 | offset += 8; 42 | return BigEndianConverter.ToUInt64(buffer, offset - 8); 43 | } 44 | 45 | public static Guid ReadGuidBytes(byte[] buffer, ref int offset) 46 | { 47 | offset += 16; 48 | return BigEndianConverter.ToGuid(buffer, offset - 16); 49 | } 50 | 51 | public static short ReadInt16(Stream stream) 52 | { 53 | byte[] buffer = new byte[2]; 54 | stream.Read(buffer, 0, 2); 55 | return BigEndianConverter.ToInt16(buffer, 0); 56 | } 57 | 58 | public static ushort ReadUInt16(Stream stream) 59 | { 60 | byte[] buffer = new byte[2]; 61 | stream.Read(buffer, 0, 2); 62 | return BigEndianConverter.ToUInt16(buffer, 0); 63 | } 64 | 65 | public static int ReadInt32(Stream stream) 66 | { 67 | byte[] buffer = new byte[4]; 68 | stream.Read(buffer, 0, 4); 69 | return BigEndianConverter.ToInt32(buffer, 0); 70 | } 71 | 72 | public static uint ReadUInt32(Stream stream) 73 | { 74 | byte[] buffer = new byte[4]; 75 | stream.Read(buffer, 0, 4); 76 | return BigEndianConverter.ToUInt32(buffer, 0); 77 | } 78 | 79 | public static long ReadInt64(Stream stream) 80 | { 81 | byte[] buffer = new byte[8]; 82 | stream.Read(buffer, 0, 8); 83 | return BigEndianConverter.ToInt64(buffer, 0); 84 | } 85 | 86 | public static ulong ReadUInt64(Stream stream) 87 | { 88 | byte[] buffer = new byte[8]; 89 | stream.Read(buffer, 0, 8); 90 | return BigEndianConverter.ToUInt64(buffer, 0); 91 | } 92 | 93 | public static Guid ReadGuidBytes(Stream stream) 94 | { 95 | byte[] buffer = new byte[16]; 96 | stream.Read(buffer, 0, 16); 97 | return BigEndianConverter.ToGuid(buffer, 0); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Utilities/ByteUtils/ByteUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | 5 | namespace Utilities 6 | { 7 | public class ByteUtils 8 | { 9 | public static byte[] Concatenate(byte[] a, byte[] b) 10 | { 11 | byte[] result = new byte[a.Length + b.Length]; 12 | Array.Copy(a, 0, result, 0, a.Length); 13 | Array.Copy(b, 0, result, a.Length, b.Length); 14 | return result; 15 | } 16 | 17 | public static bool AreByteArraysEqual(byte[] array1, byte[] array2) 18 | { 19 | if (array1.Length != array2.Length) 20 | { 21 | return false; 22 | } 23 | 24 | for (int index = 0; index < array1.Length; index++) 25 | { 26 | if (array1[index] != array2[index]) 27 | { 28 | return false; 29 | } 30 | } 31 | 32 | return true; 33 | } 34 | 35 | public static byte[] XOR(byte[] array1, byte[] array2) 36 | { 37 | if (array1.Length == array2.Length) 38 | { 39 | return XOR(array1, 0, array2, 0, array1.Length); 40 | } 41 | else 42 | { 43 | throw new ArgumentException("Arrays must be of equal length"); 44 | } 45 | } 46 | 47 | public static byte[] XOR(byte[] array1, int offset1, byte[] array2, int offset2, int length) 48 | { 49 | if (offset1 + length <= array1.Length && offset2 + length <= array2.Length) 50 | { 51 | byte[] result = new byte[length]; 52 | for (int index = 0; index < length; index++) 53 | { 54 | result[index] = (byte)(array1[offset1 + index] ^ array2[offset2 + index]); 55 | } 56 | return result; 57 | } 58 | else 59 | { 60 | throw new ArgumentOutOfRangeException(); 61 | } 62 | } 63 | 64 | public static long CopyStream(Stream input, Stream output) 65 | { 66 | // input may not support seeking, so don't use input.Position 67 | return CopyStream(input, output, Int64.MaxValue); 68 | } 69 | 70 | public static long CopyStream(Stream input, Stream output, long count) 71 | { 72 | const int MaxBufferSize = 1048576; // 1 MB 73 | int bufferSize = (int)Math.Min(MaxBufferSize, count); 74 | byte[] buffer = new byte[bufferSize]; 75 | long totalBytesRead = 0; 76 | while (totalBytesRead < count) 77 | { 78 | int numberOfBytesToRead = (int)Math.Min(bufferSize, count - totalBytesRead); 79 | int bytesRead = input.Read(buffer, 0, numberOfBytesToRead); 80 | totalBytesRead += bytesRead; 81 | output.Write(buffer, 0, bytesRead); 82 | if (bytesRead == 0) // no more bytes to read from input stream 83 | { 84 | return totalBytesRead; 85 | } 86 | } 87 | return totalBytesRead; 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Utilities/ByteUtils/LittleEndianReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | 5 | namespace Utilities 6 | { 7 | public class LittleEndianReader 8 | { 9 | public static short ReadInt16(byte[] buffer, ref int offset) 10 | { 11 | offset += 2; 12 | return LittleEndianConverter.ToInt16(buffer, offset - 2); 13 | } 14 | 15 | public static ushort ReadUInt16(byte[] buffer, ref int offset) 16 | { 17 | offset += 2; 18 | return LittleEndianConverter.ToUInt16(buffer, offset - 2); 19 | } 20 | 21 | public static int ReadInt32(byte[] buffer, ref int offset) 22 | { 23 | offset += 4; 24 | return LittleEndianConverter.ToInt32(buffer, offset - 4); 25 | } 26 | 27 | public static uint ReadUInt32(byte[] buffer, ref int offset) 28 | { 29 | offset += 4; 30 | return LittleEndianConverter.ToUInt32(buffer, offset - 4); 31 | } 32 | 33 | public static long ReadInt64(byte[] buffer, ref int offset) 34 | { 35 | offset += 8; 36 | return LittleEndianConverter.ToInt64(buffer, offset - 8); 37 | } 38 | 39 | public static ulong ReadUInt64(byte[] buffer, ref int offset) 40 | { 41 | offset += 8; 42 | return LittleEndianConverter.ToUInt64(buffer, offset - 8); 43 | } 44 | 45 | public static Guid ReadGuid(byte[] buffer, ref int offset) 46 | { 47 | offset += 16; 48 | return LittleEndianConverter.ToGuid(buffer, offset - 16); 49 | } 50 | 51 | public static short ReadInt16(Stream stream) 52 | { 53 | byte[] buffer = new byte[2]; 54 | stream.Read(buffer, 0, 2); 55 | return LittleEndianConverter.ToInt16(buffer, 0); 56 | } 57 | 58 | public static ushort ReadUInt16(Stream stream) 59 | { 60 | byte[] buffer = new byte[2]; 61 | stream.Read(buffer, 0, 2); 62 | return LittleEndianConverter.ToUInt16(buffer, 0); 63 | } 64 | 65 | public static int ReadInt32(Stream stream) 66 | { 67 | byte[] buffer = new byte[4]; 68 | stream.Read(buffer, 0, 4); 69 | return LittleEndianConverter.ToInt32(buffer, 0); 70 | } 71 | 72 | public static uint ReadUInt32(Stream stream) 73 | { 74 | byte[] buffer = new byte[4]; 75 | stream.Read(buffer, 0, 4); 76 | return LittleEndianConverter.ToUInt32(buffer, 0); 77 | } 78 | 79 | public static long ReadInt64(Stream stream) 80 | { 81 | byte[] buffer = new byte[8]; 82 | stream.Read(buffer, 0, 8); 83 | return LittleEndianConverter.ToInt64(buffer, 0); 84 | } 85 | 86 | public static ulong ReadUInt64(Stream stream) 87 | { 88 | byte[] buffer = new byte[8]; 89 | stream.Read(buffer, 0, 8); 90 | return LittleEndianConverter.ToUInt64(buffer, 0); 91 | } 92 | 93 | public static Guid ReadGuidBytes(Stream stream) 94 | { 95 | byte[] buffer = new byte[16]; 96 | stream.Read(buffer, 0, 16); 97 | return LittleEndianConverter.ToGuid(buffer, 0); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Utilities/ByteUtils/LittleEndianWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | 5 | namespace Utilities 6 | { 7 | public class LittleEndianWriter 8 | { 9 | public static void WriteUInt16(byte[] buffer, int offset, ushort value) 10 | { 11 | byte[] bytes = LittleEndianConverter.GetBytes(value); 12 | Array.Copy(bytes, 0, buffer, offset, bytes.Length); 13 | } 14 | 15 | public static void WriteUInt16(byte[] buffer, ref int offset, ushort value) 16 | { 17 | WriteUInt16(buffer, offset, value); 18 | offset += 2; 19 | } 20 | 21 | public static void WriteInt16(byte[] buffer, int offset, short value) 22 | { 23 | byte[] bytes = LittleEndianConverter.GetBytes(value); 24 | Array.Copy(bytes, 0, buffer, offset, bytes.Length); 25 | } 26 | 27 | public static void WriteInt16(byte[] buffer, ref int offset, short value) 28 | { 29 | WriteInt16(buffer, offset, value); 30 | offset += 2; 31 | } 32 | 33 | public static void WriteUInt32(byte[] buffer, int offset, uint value) 34 | { 35 | byte[] bytes = LittleEndianConverter.GetBytes(value); 36 | Array.Copy(bytes, 0, buffer, offset, bytes.Length); 37 | } 38 | 39 | public static void WriteUInt32(byte[] buffer, ref int offset, uint value) 40 | { 41 | WriteUInt32(buffer, offset, value); 42 | offset += 4; 43 | } 44 | 45 | public static void WriteInt32(byte[] buffer, int offset, int value) 46 | { 47 | byte[] bytes = LittleEndianConverter.GetBytes(value); 48 | Array.Copy(bytes, 0, buffer, offset, bytes.Length); 49 | } 50 | 51 | public static void WriteInt32(byte[] buffer, ref int offset, int value) 52 | { 53 | WriteInt32(buffer, offset, value); 54 | offset += 4; 55 | } 56 | 57 | public static void WriteUInt64(byte[] buffer, int offset, ulong value) 58 | { 59 | byte[] bytes = LittleEndianConverter.GetBytes(value); 60 | Array.Copy(bytes, 0, buffer, offset, bytes.Length); 61 | } 62 | 63 | public static void WriteUInt64(byte[] buffer, ref int offset, ulong value) 64 | { 65 | WriteUInt64(buffer, offset, value); 66 | offset += 8; 67 | } 68 | 69 | public static void WriteInt64(byte[] buffer, int offset, long value) 70 | { 71 | byte[] bytes = LittleEndianConverter.GetBytes(value); 72 | Array.Copy(bytes, 0, buffer, offset, bytes.Length); 73 | } 74 | 75 | public static void WriteInt64(byte[] buffer, ref int offset, long value) 76 | { 77 | WriteInt64(buffer, offset, value); 78 | offset += 8; 79 | } 80 | 81 | public static void WriteGuidBytes(byte[] buffer, int offset, Guid value) 82 | { 83 | byte[] bytes = LittleEndianConverter.GetBytes(value); 84 | Array.Copy(bytes, 0, buffer, offset, bytes.Length); 85 | } 86 | 87 | public static void WriteGuidBytes(byte[] buffer, ref int offset, Guid value) 88 | { 89 | WriteGuidBytes(buffer, offset, value); 90 | offset += 16; 91 | } 92 | 93 | public static void WriteUInt16(Stream stream, ushort value) 94 | { 95 | byte[] bytes = LittleEndianConverter.GetBytes(value); 96 | stream.Write(bytes, 0, bytes.Length); 97 | } 98 | 99 | public static void WriteInt32(Stream stream, int value) 100 | { 101 | byte[] bytes = LittleEndianConverter.GetBytes(value); 102 | stream.Write(bytes, 0, bytes.Length); 103 | } 104 | 105 | public static void WriteUInt32(Stream stream, uint value) 106 | { 107 | byte[] bytes = LittleEndianConverter.GetBytes(value); 108 | stream.Write(bytes, 0, bytes.Length); 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /Utilities/Comparers/ReverseComparer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Utilities 6 | { 7 | public class ReverseComparer : IComparer 8 | { 9 | private IComparer m_comparer; 10 | 11 | public ReverseComparer(IComparer comparer) 12 | { 13 | m_comparer = comparer; 14 | } 15 | 16 | public int Compare(T x, T y) 17 | { 18 | return m_comparer.Compare(y, x); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Utilities/Generics/BlockingQueue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | using System.Text; 5 | 6 | namespace Utilities 7 | { 8 | public class BlockingQueue 9 | { 10 | private Queue m_queue = new Queue(); 11 | private int m_count = 0; 12 | private bool m_stopping; 13 | 14 | public void Enqueue(T item) 15 | { 16 | lock (m_queue) 17 | { 18 | m_queue.Enqueue(item); 19 | m_count++; 20 | if (m_queue.Count == 1) 21 | { 22 | Monitor.Pulse(m_queue); 23 | } 24 | } 25 | } 26 | 27 | public void Enqueue(List items) 28 | { 29 | if (items.Count == 0) 30 | { 31 | return; 32 | } 33 | lock (m_queue) 34 | { 35 | foreach (T item in items) 36 | { 37 | m_queue.Enqueue(item); 38 | m_count++; 39 | } 40 | if (m_queue.Count == items.Count) 41 | { 42 | Monitor.Pulse(m_queue); 43 | } 44 | } 45 | } 46 | 47 | /// Will return false if the BlockingQueue is stopped 48 | public bool TryDequeue(out T item) 49 | { 50 | lock (m_queue) 51 | { 52 | while (m_queue.Count == 0) 53 | { 54 | Monitor.Wait(m_queue); 55 | if (m_stopping) 56 | { 57 | item = default(T); 58 | return false; 59 | } 60 | } 61 | 62 | item = m_queue.Dequeue(); 63 | m_count--; 64 | return true; 65 | } 66 | } 67 | 68 | public void Stop() 69 | { 70 | lock (m_queue) 71 | { 72 | m_stopping = true; 73 | Monitor.PulseAll(m_queue); 74 | } 75 | } 76 | 77 | public int Count 78 | { 79 | get 80 | { 81 | return m_count; 82 | } 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Utilities/Generics/KeyValuePairList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | 5 | namespace Utilities 6 | { 7 | public partial class KeyValuePairList : List> 8 | { 9 | public bool ContainsKey(TKey key) 10 | { 11 | return (this.IndexOfKey(key) != -1); 12 | } 13 | 14 | public int IndexOfKey(TKey key) 15 | { 16 | for (int index = 0; index < this.Count; index++) 17 | { 18 | if (this[index].Key.Equals(key)) 19 | { 20 | return index; 21 | } 22 | } 23 | 24 | return -1; 25 | } 26 | 27 | public TValue ValueOf(TKey key) 28 | { 29 | for (int index = 0; index < this.Count; index++) 30 | { 31 | if (this[index].Key.Equals(key)) 32 | { 33 | return this[index].Value; 34 | } 35 | } 36 | 37 | return default(TValue); 38 | } 39 | 40 | public void Add(TKey key, TValue value) 41 | { 42 | this.Add(new KeyValuePair(key, value)); 43 | } 44 | 45 | public List Keys 46 | { 47 | get 48 | { 49 | List result = new List(); 50 | foreach (KeyValuePair entity in this) 51 | { 52 | result.Add(entity.Key); 53 | } 54 | return result; 55 | } 56 | } 57 | 58 | public List Values 59 | { 60 | get 61 | { 62 | List result = new List(); 63 | foreach (KeyValuePair entity in this) 64 | { 65 | result.Add(entity.Value); 66 | } 67 | return result; 68 | } 69 | } 70 | 71 | new public void Sort() 72 | { 73 | this.Sort(Comparer.Default); 74 | } 75 | 76 | public void Sort(ListSortDirection sortDirection) 77 | { 78 | Sort(Comparer.Default, sortDirection); 79 | } 80 | 81 | public void Sort(IComparer comparer, ListSortDirection sortDirection) 82 | { 83 | if (sortDirection == ListSortDirection.Ascending) 84 | { 85 | Sort(comparer); 86 | } 87 | else 88 | { 89 | Sort(new ReverseComparer(comparer)); 90 | } 91 | } 92 | 93 | public void Sort(IComparer comparer) 94 | { 95 | this.Sort(delegate(KeyValuePair a, KeyValuePair b) 96 | { 97 | return comparer.Compare(a.Key, b.Key); 98 | }); 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /Utilities/Generics/ListUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Utilities 7 | { 8 | public class ListUtils 9 | { 10 | public static List GetRemovedRange(List source, List items) 11 | { 12 | List result = new List(source); 13 | foreach (T item in items) 14 | { 15 | result.Remove(item); 16 | } 17 | return result; 18 | } 19 | 20 | public static List FromArrayList(ArrayList arrayList) 21 | { 22 | List result = new List(); 23 | foreach(T entity in arrayList) 24 | { 25 | result.Add(entity); 26 | } 27 | return result; 28 | } 29 | 30 | public static List GetSorted(List source) 31 | { 32 | List result = new List(source); 33 | result.Sort(); 34 | return result; 35 | } 36 | 37 | public static List GetDistinct(List source) 38 | { 39 | List result = new List(); 40 | foreach (T entry in source) 41 | { 42 | if (!result.Contains(entry)) 43 | { 44 | result.Add(entry); 45 | } 46 | } 47 | return result; 48 | } 49 | 50 | public static List ToList(IEnumerable collection) 51 | { 52 | List result = new List(); 53 | foreach (T entry in collection) 54 | { 55 | result.Add(entry); 56 | } 57 | return result; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Utilities/Generics/Map.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Utilities 6 | { 7 | /// 8 | /// Based on: 9 | /// http://stackoverflow.com/questions/10966331/two-way-bidirectional-dictionary-in-c 10 | /// 11 | public class Map 12 | { 13 | private Dictionary m_forward = new Dictionary(); 14 | private Dictionary m_reverse = new Dictionary(); 15 | 16 | public Map() 17 | { 18 | m_forward = new Dictionary(); 19 | m_reverse = new Dictionary(); 20 | } 21 | 22 | public void Add(T1 key, T2 value) 23 | { 24 | m_forward.Add(key, value); 25 | m_reverse.Add(value, key); 26 | } 27 | 28 | public bool ContainsKey(T1 key) 29 | { 30 | return m_forward.ContainsKey(key); 31 | } 32 | 33 | public bool ContainsValue(T2 value) 34 | { 35 | return m_reverse.ContainsKey(value); 36 | } 37 | 38 | public T2 this[T1 key] 39 | { 40 | get 41 | { 42 | return m_forward[key]; 43 | } 44 | } 45 | 46 | public T1 GetKey(T2 value) 47 | { 48 | return m_reverse[value]; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Utilities/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Utilities")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("ROM Knowledgeware")] 12 | [assembly: AssemblyProduct("Utilities")] 13 | [assembly: AssemblyCopyright("Copyright © ROM Knowledgeware 2006-2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("8fd27e1d-f86b-47c5-8e46-7cd44949174e")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Revision and Build Numbers 33 | // by using the '*' as shown below: 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /Utilities/Threading/CountdownLatch.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | 4 | namespace Utilities 5 | { 6 | public class CountdownLatch 7 | { 8 | private int m_count; 9 | private EventWaitHandle m_waitHandle = new EventWaitHandle(true, EventResetMode.ManualReset); 10 | 11 | public CountdownLatch() 12 | { 13 | } 14 | 15 | public void Increment() 16 | { 17 | int count = Interlocked.Increment(ref m_count); 18 | if (count == 1) 19 | { 20 | m_waitHandle.Reset(); 21 | } 22 | } 23 | 24 | public void Add(int value) 25 | { 26 | int count = Interlocked.Add(ref m_count, value); 27 | if (count == value) 28 | { 29 | m_waitHandle.Reset(); 30 | } 31 | } 32 | 33 | public void Decrement() 34 | { 35 | int count = Interlocked.Decrement(ref m_count); 36 | if (m_count == 0) 37 | { 38 | m_waitHandle.Set(); 39 | } 40 | else if (count < 0) 41 | { 42 | throw new InvalidOperationException("Count must be greater than or equal to 0"); 43 | } 44 | } 45 | 46 | public void WaitUntilZero() 47 | { 48 | m_waitHandle.WaitOne(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Utilities/Threading/Parallel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using System.Threading; 6 | 7 | namespace Utilities 8 | { 9 | public delegate void ForDelegate(int i); 10 | public delegate void DelegateProcess(); 11 | 12 | // Based on: 13 | // http://coding-time.blogspot.pt/2008/03/implement-your-own-parallelfor-in-c.html 14 | // C# 2.0 adaptation based on: 15 | // http://dotnetgalactics.wordpress.com/2009/11/19/how-to-provide-a-parallel-for-loop-in-c2-0-2/ 16 | public class Parallel 17 | { 18 | /// 19 | /// Parallel for loop. Invokes given action, passing arguments 20 | /// fromInclusive - toExclusive on multiple threads. 21 | /// Returns when loop finished. 22 | /// 23 | public static void For(int fromInclusive, int toExclusive, ForDelegate forDelegate) 24 | { 25 | int chunkSize = 4; 26 | For(fromInclusive, toExclusive, chunkSize, forDelegate); 27 | } 28 | 29 | /// 30 | /// Parallel for loop. Invokes given action, passing arguments 31 | /// fromInclusive - toExclusive on multiple threads. 32 | /// Returns when loop finished. 33 | /// 34 | /// 35 | /// chunkSize = 1 makes items to be processed in order. 36 | /// Bigger chunk size should reduce lock waiting time and thus 37 | /// increase paralelism. 38 | /// 39 | public static void For(int fromInclusive, int toExclusive, int chunkSize, ForDelegate forDelegate) 40 | { 41 | int threadCount = Environment.ProcessorCount; 42 | For(fromInclusive, toExclusive, chunkSize, threadCount, forDelegate); 43 | } 44 | 45 | /// 46 | /// Parallel for loop. Invokes given action, passing arguments 47 | /// fromInclusive - toExclusive on multiple threads. 48 | /// Returns when loop finished. 49 | /// 50 | /// 51 | /// chunkSize = 1 makes items to be processed in order. 52 | /// Bigger chunk size should reduce lock waiting time and thus 53 | /// increase paralelism. 54 | /// 55 | /// number of process() threads 56 | public static void For(int fromInclusive, int toExclusive, int chunkSize, int threadCount, ForDelegate forDelegate) 57 | { 58 | int index = fromInclusive - chunkSize; 59 | // locker object shared by all the process() delegates 60 | object locker = new object(); 61 | 62 | // processing function 63 | // takes next chunk and processes it using action 64 | DelegateProcess process = delegate() 65 | { 66 | while (true) 67 | { 68 | int chunkStart = 0; 69 | lock (locker) 70 | { 71 | // take next chunk 72 | index += chunkSize; 73 | chunkStart = index; 74 | } 75 | // process the chunk 76 | // (another thread is processing another chunk 77 | // so the real order of items will be out-of-order) 78 | for (int i = chunkStart; i < chunkStart + chunkSize; i++) 79 | { 80 | if (i >= toExclusive) return; 81 | forDelegate(i); 82 | } 83 | } 84 | }; 85 | 86 | // launch process() threads 87 | IAsyncResult[] asyncResults = new IAsyncResult[threadCount]; 88 | for (int i = 0; i < threadCount; ++i) 89 | { 90 | asyncResults[i] = process.BeginInvoke(null, null); 91 | } 92 | // wait for all threads to complete 93 | for (int i = 0; i < threadCount; ++i) 94 | { 95 | process.EndInvoke(asyncResults[i]); 96 | } 97 | } 98 | } 99 | } -------------------------------------------------------------------------------- /Utilities/TimeZoneInformation/RegistryTimeZoneInformation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Utilities 6 | { 7 | public class RegistryTimeZoneInformation // _REG_TZI_FORMAT 8 | { 9 | public const int Length = 44; 10 | 11 | public int Bias; 12 | public int StandardBias; 13 | public int DaylightBias; 14 | public SystemTime StandardDate; 15 | public SystemTime DaylightDate; 16 | 17 | public RegistryTimeZoneInformation(byte[] bytes) 18 | { 19 | if ((bytes == null) || (bytes.Length != 0x2c)) 20 | { 21 | throw new ArgumentException("Invalid REG_TZI_FORMAT"); 22 | } 23 | this.Bias = LittleEndianConverter.ToInt32(bytes, 0); 24 | this.StandardBias = LittleEndianConverter.ToInt32(bytes, 4); 25 | this.DaylightBias = LittleEndianConverter.ToInt32(bytes, 8); 26 | this.StandardDate = new SystemTime(bytes, 12); 27 | this.DaylightDate = new SystemTime(bytes, 28); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Utilities/Utilities.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Debug 4 | AnyCPU 5 | 8.0.50727 6 | 2.0 7 | {AECB4E91-32AE-4AD1-AE1D-8FDBE33B224D} 8 | Library 9 | Properties 10 | Utilities 11 | Utilities 12 | 13 | 14 | true 15 | full 16 | false 17 | bin\Debug\ 18 | DEBUG;TRACE 19 | prompt 20 | 4 21 | 22 | 23 | pdbonly 24 | true 25 | bin\Release\ 26 | TRACE 27 | prompt 28 | 4 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 66 | --------------------------------------------------------------------------------