/// <summary> /// Populates the value of variable length properties /// </summary> private void PopulateVariableLengthProperties(PropertyStream ps, string storage) { var variableLengthReader = new VariableLengthStreamReader(_compoundFile); foreach (var item in ps.Data) { if (!TypeMapper.IsFixedLength(item.PropertyTag.Type)) { object obj = variableLengthReader.GetVariableLengthProperty(item, storage); (item as VariableLengthPropertyEntry).VariableLengthData = obj; } } }
/// <summary> /// This method returns the stream names of named properties /// </summary> /// <returns></returns> private List<PropertyEntry> GetNamedProperties(PropertyStream ps) { List<PropertyEntry> namedProperties = new List<PropertyEntry>(); foreach (var item in ps.Data) { if (item.PropertyTag.ID >= 0x8000 && item.PropertyTag.ID <= 0x8FFF) { namedProperties.Add(item); } } return namedProperties; }
/// <summary> /// The class will read and parse a property stream. /// See https://msdn.microsoft.com/en-us/library/ee203894(v=exchg.80).aspx for the structure. /// </summary> /// <param name="storage">Which storage to read from. If null, read from the top-level storage</param> /// <returns>A populated Property Stream</returns> internal PropertyStream ReadPropertyStream(string storage = null) { PropertyStream ps = new PropertyStream(); ps.Header = new Headers.TopLevelHeader(); CFStream propStream; byte[] data; if (storage == null) // we want the top-level property stream { propStream = _compoundFile.RootStorage.GetStream(PropertyStream.STREAM_NAME); data = propStream.GetData(); ps.Header.NextRecipientId = BitConverter.ToInt32(data, 8); ps.Header.NextAttachmentId = BitConverter.ToInt32(data, 12); ps.Header.RecipientCount = BitConverter.ToInt32(data, 16); ps.Header.AttachmentCount = BitConverter.ToInt32(data, 20); ps.NumberOfProperties = (data.Length - Headers.TopLevelHeader.HEADER_SIZE_BYTES) / PropertyEntry.SIZE_BYTES; ps.Data = new List<PropertyEntry>(); for (int i = 0; i < ps.NumberOfProperties; i++) { //start reading at 32 ps.Data.Add(ReadPropertyEntry(data.Skip(Headers.TopLevelHeader.HEADER_SIZE_BYTES).Skip(i * 16).Take(16).ToArray())); } } else //we want the attachment/recipient property stream { propStream = _compoundFile.RootStorage.GetStorage(storage).GetStream(PropertyStream.STREAM_NAME); data = propStream.GetData(); ps.NumberOfProperties = (data.Length - Headers.BaseHeader.HEADER_SIZE_BYTES) / PropertyEntry.SIZE_BYTES; ps.Data = new List<PropertyEntry>(); for (int i = 0; i < ps.NumberOfProperties; i++) { //start reading at 8 ps.Data.Add(ReadPropertyEntry(data.Skip(Headers.BaseHeader.HEADER_SIZE_BYTES).Skip(i * 16).Take(16).ToArray())); } } PopulateVariableLengthProperties(ps, storage); return ps; }
/// <summary> /// This will get and parse named properties /// </summary> /// <param name="ps">A populated property stream</param> /// <returns>A list of NamedProperty objects</returns> private List<NamedProperty> ParseNamedProperties(PropertyStream ps) { NamedPropertyMapper mapper = new NamedPropertyMapper(_namedPropertyParser); List<PropertyEntry> namedPropertyEntries = GetNamedProperties(ps); List<NamedProperty> namedProperties = new List<NamedProperty>(); foreach (var property in namedPropertyEntries) { namedProperties.Add(mapper.MapProperty(property)); } return namedProperties; }