Skip to content

Commit e1cac64

Browse files
author
Sicos1977
committed
Implementing propertyname structure
1 parent ba81f33 commit e1cac64

3 files changed

Lines changed: 83 additions & 32 deletions

File tree

‎MsgReader/Outlook/MapiTags.cs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1132,6 +1132,7 @@ internal static class MapiTags
11321132
/// The stream with the name properties are always in stream "__substg1.0_00030102"
11331133
/// </summary>
11341134
public const string NameIdStorageMappingStream = "__substg1.0_00030102";
1135+
public const string NameIdStorageMappingStream2 = "__substg1.0_00040102";
11351136

11361137
/// <summary>
11371138
/// Stream properties begin for header or top

‎MsgReader/Outlook/Storage.cs‎

Lines changed: 80 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.Text;
99
using System.Text.RegularExpressions;
1010
using DocumentServices.Modules.Readers.MsgReader.Header;
11+
using DocumentServices.Modules.Readers.MsgReader.Rtf;
1112
using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;
1213
using STATSTG = System.Runtime.InteropServices.ComTypes.STATSTG;
1314

@@ -270,16 +271,30 @@ public static void RemoveItem(object track)
270271
/// </summary>
271272
private class MapiToOom : Storage
272273
{
273-
#region Properties
274+
#region Constructor
275+
/// <summary>
276+
/// Initializes a new instance of the <see cref="Storage.MapiToOom" /> class.
277+
/// </summary>
278+
/// <param name="message"> The message. </param>
279+
internal MapiToOom(Storage message) : base(message._storage)
280+
{
281+
GC.SuppressFinalize(message);
282+
_propHeaderSize = MapiTags.PropertiesStreamHeaderTop;
283+
}
284+
#endregion
285+
286+
#region GetMapping
274287
/// <summary>
275288
/// Gets the display name
276289
/// </summary>
277290
public Dictionary<string, string> GetMapping(IEnumerable<string> namedProperties)
278291
{
279292
var result = new Dictionary<string, string>();
280-
293+
281294
// Named properties are always in the __substg1.0_00030102 stream
282295
var nameStreamBytes = GetStreamBytes(MapiTags.NameIdStorageMappingStream);
296+
//var test = GetStreamBytes(MapiTags.NameIdStorageMappingStream2);
297+
//var test1 = BitConverter.ToString(test).Replace("-", string.Empty);
283298

284299
foreach (var namedProperty in namedProperties)
285300
{
@@ -290,29 +305,41 @@ public Dictionary<string, string> GetMapping(IEnumerable<string> namedProperties
290305
// multiply the outcome with 8
291306
var offset = (value - 32768) * 8;
292307

293-
// We need the first 2 bytes for the mapping, but because the nameStreamBytes is in little
294-
// endian we need to swap the first 2 bytes
295-
var propIdent = new[] { nameStreamBytes[offset + 1], nameStreamBytes[offset] };
296-
var propIdentString = BitConverter.ToString(propIdent).Replace("-", string.Empty);
308+
//var type = nameStreamBytes[offset + 4];
297309

298-
result.Add(propIdentString, namedProperty);
310+
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
311+
// Kind | Guid
312+
// | LID (optional)
313+
// | NameSize | Name (optional) (variable)
314+
315+
// Read the first byte to check what of kind we have
316+
// 0x00 - The property is identified by the LID field.
317+
// 0x01 - The property is identified by the Name field.
318+
// 0xFF - The property does not have an associated PropertyName field.
319+
// "01-85-00-00-08-00-14-00-60-85-00-00-08-00-15-00-41-82-00-00-06-00-16-00-43-82-00-00-06-00-17-00"
320+
var kind = BitConverter.ToInt64(nameStreamBytes, offset);
321+
322+
if (nameStreamBytes[offset] == 1) // 0x01
323+
{
324+
var test = BitConverter.ToString(nameStreamBytes, offset, 32);
325+
//http://msdn.microsoft.com/en-us/library/ee158295%28v=exchg.80%29.aspx
326+
}
327+
else
328+
{
329+
// We need the first 2 bytes for the mapping, but because the nameStreamBytes is in little
330+
// endian we need to swap the first 2 bytes
331+
var propIdent = new[] {nameStreamBytes[offset + 1], nameStreamBytes[offset]};
332+
var propIdentString = BitConverter.ToString(propIdent).Replace("-", string.Empty);
333+
var newValue = ushort.Parse(propIdentString, NumberStyles.HexNumber);
334+
if (newValue < 32768) continue;
335+
result.Add(propIdentString, namedProperty);
336+
}
337+
299338
}
300339

301340
return result;
302341
}
303342
#endregion
304-
305-
#region Constructor
306-
/// <summary>
307-
/// Initializes a new instance of the <see cref="Storage.MapiToOom" /> class.
308-
/// </summary>
309-
/// <param name="message"> The message. </param>
310-
internal MapiToOom(Storage message) : base(message._storage)
311-
{
312-
GC.SuppressFinalize(message);
313-
_propHeaderSize = MapiTags.PropertiesStreamHeaderTop;
314-
}
315-
#endregion
316343
}
317344
#endregion
318345

@@ -757,13 +784,9 @@ public string Location
757784
/// <summary>
758785
/// Returns the start time for the appointment
759786
/// </summary>
760-
public DateTime Start
787+
public DateTime? Start
761788
{
762-
get
763-
{
764-
var start = GetMapiPropertyDateTime(MapiTags.AppointmentStartWhole);
765-
return start != null ? ((DateTime) start).ToLocalTime() : DateTime.Now;
766-
}
789+
get { return GetMapiPropertyDateTime(MapiTags.AppointmentStartWhole); }
767790
}
768791

769792

@@ -1326,17 +1349,42 @@ protected override void LoadStorage(NativeMethods.IStorage storage)
13261349
if (name.StartsWith(MapiTags.SubStgVersion1))
13271350
{
13281351
// Get the property value
1329-
var property = name.Substring(12, 4);
1352+
var propIdentString = name.Substring(12, 4);
13301353

13311354
// Convert it to a short
1332-
var value = ushort.Parse(property, NumberStyles.HexNumber);
1355+
var value = ushort.Parse(propIdentString, NumberStyles.HexNumber);
13331356

13341357
// Check if the value is in the named property range (8000 to FFFE (Hex))
13351358
if (value >= 32768 && value <= 65534)
13361359
{
13371360
// If so then add it to perform mapping later on
1338-
if (!mappingValues.Contains(property))
1339-
mappingValues.Add(property);
1361+
if (!mappingValues.Contains(propIdentString))
1362+
mappingValues.Add(propIdentString);
1363+
}
1364+
}
1365+
}
1366+
1367+
// Check if there is also a properties stream and if so get all the named MAPI properties from it
1368+
if (_streamStatistics.ContainsKey(MapiTags.PropertiesStream))
1369+
{
1370+
// Get the raw bytes for the property stream
1371+
var propBytes = GetStreamBytes(MapiTags.PropertiesStream);
1372+
1373+
for (var i = _propHeaderSize; i < propBytes.Length; i = i + 16)
1374+
{
1375+
// Get property identifer located in 3nd and 4th bytes as a hexdecimal string
1376+
var propIdent = new[] { propBytes[i + 3], propBytes[i + 2] };
1377+
var propIdentString = BitConverter.ToString(propIdent).Replace("-", string.Empty);
1378+
1379+
// Convert it to a short
1380+
var value = ushort.Parse(propIdentString, NumberStyles.HexNumber);
1381+
1382+
// Check if the value is in the named property range (8000 to FFFE (Hex))
1383+
if (value >= 32768 && value <= 65534)
1384+
{
1385+
// If so then add it to perform mapping later on
1386+
if (!mappingValues.Contains(propIdentString))
1387+
mappingValues.Add(propIdentString);
13401388
}
13411389
}
13421390
}
@@ -1346,7 +1394,8 @@ protected override void LoadStorage(NativeMethods.IStorage storage)
13461394
{
13471395
// Get the Named Id Storage, we need this one to perform the mapping
13481396
var storageStat = _subStorageStatistics[MapiTags.NameIdStorage];
1349-
var subStorage = storage.OpenStorage(storageStat.pwcsName, IntPtr.Zero, NativeMethods.Stgm.Read | NativeMethods.Stgm.ShareExclusive, IntPtr.Zero, 0);
1397+
var subStorage = storage.OpenStorage(storageStat.pwcsName, IntPtr.Zero,
1398+
NativeMethods.Stgm.Read | NativeMethods.Stgm.ShareExclusive, IntPtr.Zero, 0);
13501399

13511400
// Load the subStorage into our mapping class that does all the mapping magic
13521401
var mapiToOom = new MapiToOom(new Storage(subStorage));
@@ -1816,7 +1865,7 @@ private object GetMapiPropertyFromStreamOrStorage(string propIdentifier)
18161865
return null;
18171866

18181867
// Depending on prop type use method to get property value
1819-
var containerName = "__substg1.0_" + propTag;
1868+
var containerName = MapiTags.SubStgVersion1 + "_" + propTag;
18201869
switch (propType)
18211870
{
18221871
case MapiTags.PT_UNSPECIFIED:

‎MsgReader/Reader.cs‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ public string[] ExtractToFolder(string inputFile, string outputFolder, bool hype
7373

7474
try
7575
{
76-
using (var message = new Storage.Message(File.Open(inputFile, FileMode.Open, FileAccess.Read)))
76+
using (var stream = File.Open(inputFile, FileMode.Open, FileAccess.Read))
77+
using (var message = new Storage.Message(stream))
7778
{
7879
switch (message.Type)
7980
{

0 commit comments

Comments
 (0)