88using System . Text ;
99using System . Text . RegularExpressions ;
1010using DocumentServices . Modules . Readers . MsgReader . Header ;
11+ using DocumentServices . Modules . Readers . MsgReader . Rtf ;
1112using FILETIME = System . Runtime . InteropServices . ComTypes . FILETIME ;
1213using 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 :
0 commit comments