mirror of
				https://github.com/Kopano-dev/kopano-ol-extension.git
				synced 2023-10-10 11:37:40 +00:00 
			
		
		
		
	Merge branch 'master' of https://stash.kopano.io/scm/koe/kopano_ol_extension_source
This commit is contained in:
		| @@ -310,13 +310,15 @@ | |||||||
|     <Compile Include="Logging.cs" /> |     <Compile Include="Logging.cs" /> | ||||||
|     <Compile Include="Native\IOleWindow.cs" /> |     <Compile Include="Native\IOleWindow.cs" /> | ||||||
|     <Compile Include="Native\IOlkAccount.cs" /> |     <Compile Include="Native\IOlkAccount.cs" /> | ||||||
|  |     <Compile Include="Native\Kernel32.cs" /> | ||||||
|     <Compile Include="Native\MAPI\Binary.cs" /> |     <Compile Include="Native\MAPI\Binary.cs" /> | ||||||
|     <Compile Include="Native\MAPI\IMAPIContainer.cs" /> |     <Compile Include="Native\MAPI\IMAPIContainer.cs" /> | ||||||
|     <Compile Include="Native\MAPI\IMAPIFolder.cs" /> |     <Compile Include="Native\MAPI\IMAPIFolder.cs" /> | ||||||
|     <Compile Include="Native\MAPI\IMAPIProp.cs" /> |     <Compile Include="Native\MAPI\IMAPIProp.cs" /> | ||||||
|  |     <Compile Include="Native\MAPI\MAPI.cs" /> | ||||||
|     <Compile Include="Native\MAPI\Property.cs" /> |     <Compile Include="Native\MAPI\Property.cs" /> | ||||||
|     <Compile Include="Native\MAPI\Restriction.cs" /> |     <Compile Include="Native\MAPI\Restriction.cs" /> | ||||||
|     <Compile Include="Native\NativeEncoder.cs" /> |     <Compile Include="Native\MAPI\RestrictionEncoder.cs" /> | ||||||
|     <Compile Include="OutlookConstants.cs" /> |     <Compile Include="OutlookConstants.cs" /> | ||||||
|     <Compile Include="SearchQuery.cs" /> |     <Compile Include="SearchQuery.cs" /> | ||||||
|     <Compile Include="Stubs\Enums.cs" /> |     <Compile Include="Stubs\Enums.cs" /> | ||||||
|   | |||||||
| @@ -46,6 +46,15 @@ namespace Acacia.Features.SharedFolders | |||||||
|         } |         } | ||||||
|         private static readonly BoolOption OPTION_REMINDERS = new BoolOption("Reminders", true); |         private static readonly BoolOption OPTION_REMINDERS = new BoolOption("Reminders", true); | ||||||
|  |  | ||||||
|  |         [AcaciaOption("If disabled, the reminders queyr will be explicitly stopped and started when update the query. " + | ||||||
|  |                       "This causes more effort to search again, but might prevent issues.")] | ||||||
|  |         public bool RemindersKeepRunning | ||||||
|  |         { | ||||||
|  |             get { return GetOption(OPTION_REMINDERS_KEEP_RUNNING); } | ||||||
|  |             set { SetOption(OPTION_REMINDERS_KEEP_RUNNING, value); } | ||||||
|  |         } | ||||||
|  |         private static readonly BoolOption OPTION_REMINDERS_KEEP_RUNNING = new BoolOption("RemindersKeepRunning", true); | ||||||
|  |  | ||||||
|         #endregion |         #endregion | ||||||
|  |  | ||||||
|         public override void Startup() |         public override void Startup() | ||||||
|   | |||||||
| @@ -14,15 +14,16 @@ namespace Acacia.Features.SharedFolders | |||||||
|     { |     { | ||||||
|         private static readonly SearchQuery.PropertyIdentifier PROP_FOLDER = new SearchQuery.PropertyIdentifier(PropTag.FromInt(0x6B20001F)); |         private static readonly SearchQuery.PropertyIdentifier PROP_FOLDER = new SearchQuery.PropertyIdentifier(PropTag.FromInt(0x6B20001F)); | ||||||
|  |  | ||||||
|         private readonly LogContext _context; |         private readonly FeatureSharedFolders _feature; | ||||||
|         private readonly IFolder _folder; |         private readonly IFolder _folder; | ||||||
|         private SearchQuery _queryRoot; |         private SearchQuery _queryRoot; | ||||||
|         private SearchQuery.Or _queryCustom; |         private SearchQuery.Or _queryCustom; | ||||||
|  |         private bool _queryCustomModified; | ||||||
|  |  | ||||||
|         public RemindersQuery(LogContext context, IStore store) |         public RemindersQuery(FeatureSharedFolders feature, IStore store) | ||||||
|         { |         { | ||||||
|             this._context = context; |             this._feature = feature; | ||||||
|             _folder = store.GetSpecialFolder(SpecialFolder.Reminders); |             this._folder = store.GetSpecialFolder(SpecialFolder.Reminders); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public bool Open() |         public bool Open() | ||||||
| @@ -31,10 +32,10 @@ namespace Acacia.Features.SharedFolders | |||||||
|                 return true; |                 return true; | ||||||
|             try |             try | ||||||
|             { |             { | ||||||
|                 _queryRoot = _folder.SearchCriteria; |                 _queryRoot = FolderQuery; | ||||||
|                 if (!(_queryRoot is SearchQuery.And)) |                 if (!(_queryRoot is SearchQuery.And)) | ||||||
|                     return false; |                     return false; | ||||||
|                 Logger.Instance.Trace(this, "Current query1: {0}", _queryRoot.ToString()); |                 Logger.Instance.Debug(this, "Current query:\n{0}", _queryRoot.ToString()); | ||||||
|  |  | ||||||
|                 SearchQuery.And root = (SearchQuery.And)_queryRoot; |                 SearchQuery.And root = (SearchQuery.And)_queryRoot; | ||||||
|                 // TODO: more strict checking of query |                 // TODO: more strict checking of query | ||||||
| @@ -50,7 +51,6 @@ namespace Acacia.Features.SharedFolders | |||||||
|  |  | ||||||
|                 // We have the root, but not the custom query. Create it. |                 // We have the root, but not the custom query. Create it. | ||||||
|                 Logger.Instance.Debug(this, "Creating custom query"); |                 Logger.Instance.Debug(this, "Creating custom query"); | ||||||
|                 Logger.Instance.Trace(this, "Current query: {0}", root.ToString()); |  | ||||||
|                 _queryCustom = new SearchQuery.Or(); |                 _queryCustom = new SearchQuery.Or(); | ||||||
|  |  | ||||||
|                 // Add the prefix exclusion for shared folders |                 // Add the prefix exclusion for shared folders | ||||||
| @@ -63,11 +63,10 @@ namespace Acacia.Features.SharedFolders | |||||||
|                 ); |                 ); | ||||||
|  |  | ||||||
|                 root.Operands.Add(_queryCustom); |                 root.Operands.Add(_queryCustom); | ||||||
|                 Logger.Instance.Trace(this, "Modified query: {0}", root.ToString()); |                 Logger.Instance.Debug(this, "Modified query:\n{0}", root.ToString()); | ||||||
|                 // Store it |                 // Store it | ||||||
|                 // TODO: could store it on change only |                 FolderQuery = root; | ||||||
|                 _folder.SearchCriteria = root; |                 Logger.Instance.Debug(this, "Modified query readback:\n{0}", FolderQuery); | ||||||
|                 Logger.Instance.Trace(this, "Modified query2: {0}", _folder.SearchCriteria.ToString()); |  | ||||||
|             } |             } | ||||||
|             catch (Exception e) |             catch (Exception e) | ||||||
|             { |             { | ||||||
| @@ -80,7 +79,7 @@ namespace Acacia.Features.SharedFolders | |||||||
|         { |         { | ||||||
|             get |             get | ||||||
|             { |             { | ||||||
|                 return _context.LogContextId; |                 return _feature.LogContextId; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -91,7 +90,29 @@ namespace Acacia.Features.SharedFolders | |||||||
|  |  | ||||||
|         public void Commit() |         public void Commit() | ||||||
|         { |         { | ||||||
|             _folder.SearchCriteria = _queryRoot; |             if (_queryCustomModified) | ||||||
|  |             { | ||||||
|  |                 FolderQuery = _queryRoot; | ||||||
|  |                 _queryCustomModified = false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private SearchQuery FolderQuery | ||||||
|  |         { | ||||||
|  |             get | ||||||
|  |             { | ||||||
|  |                 return _folder.SearchCriteria; | ||||||
|  |             } | ||||||
|  |             set | ||||||
|  |             { | ||||||
|  |                 if (!_feature.RemindersKeepRunning) | ||||||
|  |                     _folder.SearchRunning = false; | ||||||
|  |  | ||||||
|  |                 _folder.SearchCriteria = value; | ||||||
|  |  | ||||||
|  |                 if (!_feature.RemindersKeepRunning) | ||||||
|  |                     _folder.SearchRunning = true; | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public void UpdateReminders(SyncId folderId, bool wantReminders) |         public void UpdateReminders(SyncId folderId, bool wantReminders) | ||||||
| @@ -125,6 +146,7 @@ namespace Acacia.Features.SharedFolders | |||||||
|                 _queryCustom.Operands.Add(new SearchQuery.PropertyContent( |                 _queryCustom.Operands.Add(new SearchQuery.PropertyContent( | ||||||
|                     PROP_FOLDER, SearchQuery.ContentMatchOperation.Prefix, SearchQuery.ContentMatchModifiers.None, prefix |                     PROP_FOLDER, SearchQuery.ContentMatchOperation.Prefix, SearchQuery.ContentMatchModifiers.None, prefix | ||||||
|                 )); |                 )); | ||||||
|  |                 _queryCustomModified = true; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -154,6 +176,7 @@ namespace Acacia.Features.SharedFolders | |||||||
|  |  | ||||||
|                     Logger.Instance.Trace(this, "Unwanted prefix at {0}: {1}", i, prefix); |                     Logger.Instance.Trace(this, "Unwanted prefix at {0}: {1}", i, prefix); | ||||||
|                     _queryCustom.Operands.RemoveAt(i); |                     _queryCustom.Operands.RemoveAt(i); | ||||||
|  |                     _queryCustomModified = true; | ||||||
|                 } |                 } | ||||||
|                 else ++i; |                 else ++i; | ||||||
|             } |             } | ||||||
|   | |||||||
							
								
								
									
										29
									
								
								src/AcaciaZPushPlugin/AcaciaZPushPlugin/Native/Kernel32.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/AcaciaZPushPlugin/AcaciaZPushPlugin/Native/Kernel32.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | |||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Linq; | ||||||
|  | using System.Runtime.InteropServices; | ||||||
|  | using System.Text; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  |  | ||||||
|  | namespace Acacia.Native | ||||||
|  | { | ||||||
|  |     public static class Kernel32 | ||||||
|  |     { | ||||||
|  |         [DllImport("kernel32.dll", SetLastError = false)] | ||||||
|  |         public static extern void CopyMemory(IntPtr dest, IntPtr src, uint count); | ||||||
|  |  | ||||||
|  |         // ZeroMemory is actually a macro | ||||||
|  |         public static void ZeroMemory(IntPtr ptr, int length) | ||||||
|  |         { | ||||||
|  |             for (int i = 0; i < length / 8; i += 8) | ||||||
|  |             { | ||||||
|  |                 Marshal.WriteInt64(ptr, i, 0x00); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             for (int i = length % 8; i < -1; i--) | ||||||
|  |             { | ||||||
|  |                 Marshal.WriteByte(ptr, length - i, 0x00); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -24,35 +24,40 @@ using System.Threading.Tasks; | |||||||
|  |  | ||||||
| namespace Acacia.Native.MAPI | namespace Acacia.Native.MAPI | ||||||
| { | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Simple wrapper type to allow string encoding to work. Returned from SBinary.Unmarshall; this is needed | ||||||
|  |     /// as a copy of the data ptr there must be managed. | ||||||
|  |     /// </summary> | ||||||
|  |     public struct SBinaryWrapper | ||||||
|  |     { | ||||||
|  |         public readonly byte[] Data; | ||||||
|  |  | ||||||
|  |         public SBinaryWrapper(byte[] bytes) | ||||||
|  |         { | ||||||
|  |             this.Data = bytes; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public override string ToString() | ||||||
|  |         { | ||||||
|  |             return "cb: " + Data.Length.ToString() + " lpb: " + StringUtil.BytesToHex(Data); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     unsafe public struct SBinary |     unsafe public struct SBinary | ||||||
|     { |     { | ||||||
|         public uint cb; |         public uint cb; | ||||||
|         public byte* ptr; |         public byte* ptr; | ||||||
|  |  | ||||||
|         public byte[] Unmarshal() |         public SBinaryWrapper Unmarshal() | ||||||
|         { |         { | ||||||
|             byte[] result = new byte[cb]; |             byte[] result = new byte[cb]; | ||||||
|             System.Runtime.InteropServices.Marshal.Copy((IntPtr)ptr, result, 0, result.Length); |             Marshal.Copy((IntPtr)ptr, result, 0, result.Length); | ||||||
|             return result; |             return new SBinaryWrapper(result); | ||||||
|         } |  | ||||||
|  |  | ||||||
|         /// <summary> |  | ||||||
|         /// Returns an instance with the data allocated in the enocder. |  | ||||||
|         /// </summary> |  | ||||||
|         public SBinary Marshal(NativeEncoder encoder) |  | ||||||
|         { |  | ||||||
|             return new SBinary() |  | ||||||
|             { |  | ||||||
|                 cb = cb, |  | ||||||
|                 ptr = (byte*)encoder.Allocate(Unmarshal()) |  | ||||||
|             }; |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public override string ToString() |         public override string ToString() | ||||||
|         { |         { | ||||||
|             byte[] b = Unmarshal(); |             return Unmarshal().ToString(); | ||||||
|             return b.Length.ToString() + ":" + StringUtil.BytesToHex(b); |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -60,15 +65,5 @@ namespace Acacia.Native.MAPI | |||||||
|     { |     { | ||||||
|         public uint count; |         public uint count; | ||||||
|         public SBinary* ptr; |         public SBinary* ptr; | ||||||
|  |  | ||||||
|         public byte[][] Unmarshal() |  | ||||||
|         { |  | ||||||
|             byte[][] result = new byte[count][]; |  | ||||||
|             for (uint i = 0; i < count; ++i) |  | ||||||
|             { |  | ||||||
|                 result[i] = ptr[i].Unmarshal(); |  | ||||||
|             } |  | ||||||
|             return result; |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										21
									
								
								src/AcaciaZPushPlugin/AcaciaZPushPlugin/Native/MAPI/MAPI.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/AcaciaZPushPlugin/AcaciaZPushPlugin/Native/MAPI/MAPI.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Linq; | ||||||
|  | using System.Runtime.InteropServices; | ||||||
|  | using System.Text; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  |  | ||||||
|  | namespace Acacia.Native.MAPI | ||||||
|  | { | ||||||
|  |     public static class MAPI | ||||||
|  |     { | ||||||
|  |         [DllImport("MAPI32.DLL", CharSet = CharSet.Ansi)] | ||||||
|  |         public static extern IntPtr MAPIAllocateBuffer(uint cbSize, ref IntPtr lppBuffer); | ||||||
|  |  | ||||||
|  |         [DllImport("MAPI32.DLL", CharSet = CharSet.Ansi)] | ||||||
|  |         public static extern IntPtr MAPIAllocateMore(uint cbSize, IntPtr lpObject, ref IntPtr lppBuffer); | ||||||
|  |  | ||||||
|  |         [DllImport("MAPI32.DLL", CharSet = CharSet.Ansi)] | ||||||
|  |         public static extern uint MAPIFreeBuffer(IntPtr lpBuffer); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -20,7 +20,6 @@ using System.Linq; | |||||||
| using System.Runtime.InteropServices; | using System.Runtime.InteropServices; | ||||||
| using System.Text; | using System.Text; | ||||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||||
| using static Acacia.Native.NativeEncoder; |  | ||||||
|  |  | ||||||
| namespace Acacia.Native.MAPI | namespace Acacia.Native.MAPI | ||||||
| { | { | ||||||
| @@ -48,7 +47,7 @@ namespace Acacia.Native.MAPI | |||||||
|  |  | ||||||
|         public override string ToString() |         public override string ToString() | ||||||
|         { |         { | ||||||
|             return "<" + prop.ToString("X4") + ":" + type + ">"; |             return string.Format("0x{0:X4}{1:X4} (PT_{2})", prop, (int)type, type); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public SearchQuery.PropertyIdentifier ToPropertyIdentifier() |         public SearchQuery.PropertyIdentifier ToPropertyIdentifier() | ||||||
| @@ -144,33 +143,42 @@ namespace Acacia.Native.MAPI | |||||||
|                 case PropType.UNICODE: |                 case PropType.UNICODE: | ||||||
|                     return new string(data.lpszW); |                     return new string(data.lpszW); | ||||||
|                 case PropType.BINARY: |                 case PropType.BINARY: | ||||||
|                     return data.bin; |                     return data.bin.Unmarshal(); | ||||||
|             } |             } | ||||||
|             throw new NotImplementedException(); |             throw new NotImplementedException(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         unsafe public static IntPtr MarshalFromObject(NativeEncoder encoder, PropTag prop, object value) |         unsafe public static IntPtr MarshalFromObject(RestrictionEncoder encoder, PropTag prop, object value) | ||||||
|         { |         { | ||||||
|             PropValue obj = new PropValue(); |             IntPtr ptr; | ||||||
|             obj.header.ulPropTag = prop; |             Data data = new Data(); | ||||||
|  |  | ||||||
|             switch (prop.type) |             switch (prop.type) | ||||||
|             { |             { | ||||||
|                 case PropType.BOOLEAN: |                 case PropType.BOOLEAN: | ||||||
|                     obj.data.b = (bool)value; |                     data.b = (bool)value; | ||||||
|                     return encoder.Allocate(obj.header, obj.data.b); |                     ptr = encoder.AllocateWithExtra<Header>(8, data.b); | ||||||
|  |                     break; | ||||||
|                 case PropType.STRING8: |                 case PropType.STRING8: | ||||||
|                     IntPtr ptrA = encoder.Allocate(Encoding.ASCII.GetBytes((string)value), new byte[] { 0 }); |                     IntPtr lpszA = encoder.Allocate(Encoding.ASCII.GetBytes((string)value), 1); | ||||||
|                     return encoder.Allocate(obj.header, 8, ptrA); |                     ptr = encoder.AllocateWithExtra<Header>(8, lpszA); | ||||||
|  |                     break; | ||||||
|                 case PropType.UNICODE: |                 case PropType.UNICODE: | ||||||
|                     IntPtr ptrW = encoder.Allocate(Encoding.Unicode.GetBytes((string)value), new byte[] { 0, 0 }); |                     IntPtr lpszW = encoder.Allocate(Encoding.Unicode.GetBytes((string)value), 2); | ||||||
|                     return encoder.Allocate(obj.header, 8, ptrW); |                     ptr = encoder.AllocateWithExtra<Header>(8, lpszW); | ||||||
|  |                     break; | ||||||
|                 case PropType.BINARY: |                 case PropType.BINARY: | ||||||
|                     obj.data.bin = ((SBinary)value).Marshal(encoder); |                     data.bin.cb = (uint)((SBinaryWrapper)value).Data.Length; | ||||||
|                     return encoder.Allocate(obj.header, 8, obj.data.bin); |                     data.bin.ptr = (byte*)encoder.Allocate(((SBinaryWrapper)value).Data); | ||||||
|  |                     ptr = encoder.AllocateWithExtra<Header>(8, data.bin); | ||||||
|  |                     break; | ||||||
|                 default: |                 default: | ||||||
|                     throw new NotImplementedException(); |                     throw new NotImplementedException(); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  |             // Initialise the header | ||||||
|  |             PropValue* obj = (PropValue*)ptr; | ||||||
|  |             obj->header.ulPropTag = prop; | ||||||
|  |             return ptr; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -39,20 +39,42 @@ namespace Acacia.Native.MAPI | |||||||
|         public PropTag ulPropTag; |         public PropTag ulPropTag; | ||||||
|         public PropValue* prop; |         public PropValue* prop; | ||||||
|  |  | ||||||
|  |         private static readonly string[] RELOP_NAMES = | ||||||
|  |         { | ||||||
|  |             "RELOP_LT", "RELOP_LE", "RELOP_GT", "RELOP_GE", "RELOP_EQ", "RELOP_NE", "RELOP_RE" | ||||||
|  |         }; | ||||||
|  |  | ||||||
|         public string ToString(int depth) |         public string ToString(int depth) | ||||||
|         { |         { | ||||||
|             string indent = new string(' ', depth); |             string s = string.Format( | ||||||
|             string s = indent + relop + ":" + ulPropTag.ToString(); |                 "{0}lpRes->res.resProperty.relop = {2} = 0x{1:X8}\n" + | ||||||
|             s += ":" + prop->ToString(); |                 "{0}lpRes->res.resProperty.ulPropTag = {3}\n", | ||||||
|             s += "\n"; |                 SRestriction.Indent(depth), | ||||||
|  |                 (int)relop, | ||||||
|  |                 RELOP_NAMES[(int)relop], | ||||||
|  |                 ulPropTag | ||||||
|  |             ); | ||||||
|  |  | ||||||
|  |             s += string.Format("{0}lpRes->res.resProperty.lpProp->ulPropTag = {1}\n", | ||||||
|  |                 SRestriction.Indent(depth), | ||||||
|  |                 ulPropTag | ||||||
|  |             ); | ||||||
|  |  | ||||||
|  |             s += string.Format("{0}lpRes->res.resProperty.lpProp->Value = {1}\n", | ||||||
|  |                 SRestriction.Indent(depth), | ||||||
|  |                 *prop | ||||||
|  |             ); | ||||||
|  |  | ||||||
|             return s; |             return s; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public SearchQuery ToSearchQuery() |         public SearchQuery ToSearchQuery() | ||||||
|         { |         { | ||||||
|  |             object value = prop->ToObject(); | ||||||
|             return new SearchQuery.PropertyCompare(ulPropTag.ToPropertyIdentifier(), |             return new SearchQuery.PropertyCompare(ulPropTag.ToPropertyIdentifier(), | ||||||
|                 (SearchQuery.ComparisonOperation)(int)relop, |                 (SearchQuery.ComparisonOperation)(int)relop, | ||||||
|                 prop->ToObject()); |                 value | ||||||
|  |             ); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -76,10 +98,24 @@ namespace Acacia.Native.MAPI | |||||||
|  |  | ||||||
|         public string ToString(int depth) |         public string ToString(int depth) | ||||||
|         { |         { | ||||||
|             string indent = new string(' ', depth); |             string s = string.Format( | ||||||
|             string s = indent + ulFuzzyLevel + ":" + ulPropTag.ToString(); |                 "{0}lpRes->res.resContent.ulFuzzyLevel = FL_{2} = 0x{1:X8}\n" + | ||||||
|             s += ":" + prop->ToString(); |                 "{0}lpRes->res.resContent.ulPropTag = {3}\n", | ||||||
|             s += "\n"; |                 SRestriction.Indent(depth), | ||||||
|  |                 (int)ulFuzzyLevel, | ||||||
|  |                 ulFuzzyLevel, | ||||||
|  |                 ulPropTag | ||||||
|  |             ); | ||||||
|  |  | ||||||
|  |             s += string.Format("{0}lpRes->res.resContent.lpProp->ulPropTag = {1}\n", | ||||||
|  |                 SRestriction.Indent(depth), | ||||||
|  |                 ulPropTag | ||||||
|  |             ); | ||||||
|  |  | ||||||
|  |             s += string.Format("{0}lpRes->res.resContent.lpProp->Value = {1}\n", | ||||||
|  |                 SRestriction.Indent(depth), | ||||||
|  |                 *prop | ||||||
|  |             ); | ||||||
|             return s; |             return s; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -120,19 +156,20 @@ namespace Acacia.Native.MAPI | |||||||
|         public uint cb; |         public uint cb; | ||||||
|         public SRestriction* ptr; |         public SRestriction* ptr; | ||||||
|  |  | ||||||
|         public string ToString(int depth) |         public string ToString(string name, int depth) | ||||||
|         { |         { | ||||||
|             string s = ""; |             string s = string.Format("{0}lpRes->res.res{1}.cRes = 0x{2:X8}\n", SRestriction.Indent(depth), name, cb); | ||||||
|             for (uint i = 0; i < cb; ++i) |             for (uint i = 0; i < cb; ++i) | ||||||
|             { |             { | ||||||
|                 s += ptr[i].ToString(depth); |                 s += string.Format("{0}lpRes->res.res{1}.lpRes[0x{2:X8}]\n", SRestriction.Indent(depth), name, i); | ||||||
|  |                 s += ptr[i].ToString(depth + 1); | ||||||
|             } |             } | ||||||
|             return s; |             return s; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public SearchQuery ToSearchQuery(bool and) |         public SearchQuery ToSearchQuery(bool and) | ||||||
|         { |         { | ||||||
|             SearchQuery.MultiOperator oper = and ? (SearchQuery.MultiOperator)new SearchQuery.And() : new SearchQuery.Or(); ; |             SearchQuery.MultiOperator oper = and ? (SearchQuery.MultiOperator)new SearchQuery.And() : new SearchQuery.Or(); | ||||||
|             for (uint i = 0; i < cb; ++i) |             for (uint i = 0; i < cb; ++i) | ||||||
|             { |             { | ||||||
|                 oper.Add(ptr[i].ToSearchQuery()); |                 oper.Add(ptr[i].ToSearchQuery()); | ||||||
| @@ -148,7 +185,10 @@ namespace Acacia.Native.MAPI | |||||||
|  |  | ||||||
|         public string ToString(int depth) |         public string ToString(int depth) | ||||||
|         { |         { | ||||||
|             return ptr->ToString(depth); |             string s = string.Format("{0}lpRes->res.resNot.ulReserved = 0x{1:X8}\n", SRestriction.Indent(depth), dwReserved); | ||||||
|  |             s += string.Format("{0}lpRes->res.resNot.lpRes\n", SRestriction.Indent(depth)); | ||||||
|  |             s += ptr->ToString(depth + 1); | ||||||
|  |             return s; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public SearchQuery ToSearchQuery() |         public SearchQuery ToSearchQuery() | ||||||
| @@ -175,8 +215,10 @@ namespace Acacia.Native.MAPI | |||||||
|         } |         } | ||||||
|         public string ToString(int depth) |         public string ToString(int depth) | ||||||
|         { |         { | ||||||
|             string indent = new string(' ', depth); |             string s = string.Format("{0}lpRes->res.resBitMask.relBMR = BMR_{1} = 0x{2:X8}\n", SRestriction.Indent(depth), bmr, (int)bmr); | ||||||
|             return indent + ToString() + "\n"; |             s += string.Format("{0}lpRes->res.resBitMask.ulMask = 0x{1:X8}\n", SRestriction.Indent(depth), mask); | ||||||
|  |             s += string.Format("{0}lpRes->res.resBitMask.ulPropTag = 0x{1:X8}\n", SRestriction.Indent(depth), prop); | ||||||
|  |             return s; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public SearchQuery ToSearchQuery() |         public SearchQuery ToSearchQuery() | ||||||
| @@ -197,8 +239,10 @@ namespace Acacia.Native.MAPI | |||||||
|         } |         } | ||||||
|         public string ToString(int depth) |         public string ToString(int depth) | ||||||
|         { |         { | ||||||
|             string indent = new string(' ', depth); |             string s = string.Format("{0}lpRes->res.resExist.ulPropTag = 0x{1:X8}\n", SRestriction.Indent(depth), prop); | ||||||
|             return indent + prop.ToString() + "\n"; |             s += string.Format("{0}lpRes->res.resExist.ulReserved1 = 0x{1:X8}\n", SRestriction.Indent(depth), dwReserved1); | ||||||
|  |             s += string.Format("{0}lpRes->res.resExist.ulReserved2 = 0x{1:X8}\n", SRestriction.Indent(depth), dwReserved2); | ||||||
|  |             return s; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public SearchQuery ToSearchQuery() |         public SearchQuery ToSearchQuery() | ||||||
| @@ -281,30 +325,34 @@ namespace Acacia.Native.MAPI | |||||||
|             return ToString(0); |             return ToString(0); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         internal static string Indent(int depth) | ||||||
|  |         { | ||||||
|  |             return new string('\t', depth); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         public string ToString(int depth) |         public string ToString(int depth) | ||||||
|         { |         { | ||||||
|             string indent = new string(' ', depth); |             string s = Indent(depth) + string.Format("lpRes->rt = 0x{0:X} = RES_{1}\n", (int)header.rt, header.rt); | ||||||
|             string s = indent + header.rt.ToString() + "\n" + indent + "{\n"; |  | ||||||
|             switch (header.rt) |             switch (header.rt) | ||||||
|             { |             { | ||||||
|                 case RestrictionType.AND: |                 case RestrictionType.AND: | ||||||
|                 case RestrictionType.OR: |                 case RestrictionType.OR: | ||||||
|                     s += data.sub.ToString(depth + 1); |                     s += data.sub.ToString(header.rt.ToString().ToTitle(), depth); | ||||||
|                     break; |                     break; | ||||||
|                 case RestrictionType.NOT: |                 case RestrictionType.NOT: | ||||||
|                     s += data.not.ToString(depth + 1); |                     s += data.not.ToString(depth); | ||||||
|                     break; |                     break; | ||||||
|                 case RestrictionType.CONTENT: |                 case RestrictionType.CONTENT: | ||||||
|                     s += data.content.ToString(depth + 1); |                     s += data.content.ToString(depth); | ||||||
|                     break; |                     break; | ||||||
|                 case RestrictionType.PROPERTY: |                 case RestrictionType.PROPERTY: | ||||||
|                     s += data.prop.ToString(depth + 1); |                     s += data.prop.ToString(depth); | ||||||
|                     break; |                     break; | ||||||
|                 case RestrictionType.BITMASK: |                 case RestrictionType.BITMASK: | ||||||
|                     s += data.bitMask.ToString(depth + 1); |                     s += data.bitMask.ToString(depth); | ||||||
|                     break; |                     break; | ||||||
|                 case RestrictionType.EXIST: |                 case RestrictionType.EXIST: | ||||||
|                     s += data.exist.ToString(depth + 1); |                     s += data.exist.ToString(depth); | ||||||
|                     break; |                     break; | ||||||
|  |  | ||||||
|                     /* TODO        COMPAREPROPS, |                     /* TODO        COMPAREPROPS, | ||||||
| @@ -316,158 +364,7 @@ namespace Acacia.Native.MAPI | |||||||
|                             ANNOTATION*/ |                             ANNOTATION*/ | ||||||
|  |  | ||||||
|             } |             } | ||||||
|             s += indent + "}\n"; |  | ||||||
|             return s; |             return s; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// <summary> |  | ||||||
|     /// Encodes a search as an SRestriction. Note that as memory needs to be managed for the miscellaneous structures, |  | ||||||
|     /// the SRestriction is only valid until RestrictionEncoder is disposed. |  | ||||||
|     /// </summary> |  | ||||||
|     unsafe public class RestrictionEncoder : NativeEncoder, ISearchEncoder |  | ||||||
|     { |  | ||||||
|         private class EncodingStack |  | ||||||
|         { |  | ||||||
|             public SRestriction[] array; |  | ||||||
|             public int index; |  | ||||||
|             public SRestriction* ptr; |  | ||||||
|  |  | ||||||
|             public EncodingStack(int count, Allocation<SRestriction[]> alloc) |  | ||||||
|             { |  | ||||||
|                 array = alloc.Object; |  | ||||||
|                 index = 0; |  | ||||||
|                 ptr = (SRestriction*)alloc.Pointer; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         private readonly Stack<EncodingStack> _current = new Stack<EncodingStack>(); |  | ||||||
|         private readonly EncodingStack _root; |  | ||||||
|  |  | ||||||
|         public RestrictionEncoder() |  | ||||||
|         { |  | ||||||
|             // Create an object for the root element |  | ||||||
|             _root = Begin(1); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         protected override void DoRelease() |  | ||||||
|         { |  | ||||||
|             base.DoRelease(); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         public SRestriction Restriction |  | ||||||
|         { |  | ||||||
|             get { return _root.array[0]; } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         private SRestriction* Current |  | ||||||
|         { |  | ||||||
|             get |  | ||||||
|             { |  | ||||||
|                 EncodingStack top = _current.Peek(); |  | ||||||
|                 return top.ptr + top.index; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         public void Encode(SearchQuery.PropertyExists part) |  | ||||||
|         { |  | ||||||
|             Current->header.rt = RestrictionType.EXIST; |  | ||||||
|             Current->data.exist.prop = part.Property.Tag; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         public void Encode(SearchQuery.Or part) |  | ||||||
|         { |  | ||||||
|             Current->header.rt = RestrictionType.OR; |  | ||||||
|             Current->data.sub.cb = (uint)part.Operands.Count; |  | ||||||
|             Current->data.sub.ptr = EncodePointer(part.Operands); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         public void Encode(SearchQuery.PropertyIdentifier part) |  | ||||||
|         { |  | ||||||
|             // This should be unreachable |  | ||||||
|             throw new InvalidProgramException(); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         public void Encode(SearchQuery.Not part) |  | ||||||
|         { |  | ||||||
|             Current->header.rt = RestrictionType.NOT; |  | ||||||
|             Current->data.not.ptr = EncodePointer(new[] { part.Operand }); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         public void Encode(SearchQuery.And part) |  | ||||||
|         { |  | ||||||
|             Current->header.rt = RestrictionType.AND; |  | ||||||
|             Current->data.sub.cb = (uint)part.Operands.Count; |  | ||||||
|             Current->data.sub.ptr = EncodePointer(part.Operands); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         private SRestriction* EncodePointer(IEnumerable<SearchQuery> operands) |  | ||||||
|         { |  | ||||||
|             EncodingStack alloc = Begin(operands.Count()); |  | ||||||
|             try |  | ||||||
|             { |  | ||||||
|                 foreach (SearchQuery operand in operands) |  | ||||||
|                 { |  | ||||||
|                     operand.Encode(this); |  | ||||||
|                     ++alloc.index; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             finally |  | ||||||
|             { |  | ||||||
|                 End(); |  | ||||||
|             } |  | ||||||
|             return alloc.ptr; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         private EncodingStack Begin(int count) |  | ||||||
|         { |  | ||||||
|             // Allocate and push the array |  | ||||||
|             EncodingStack alloc = new EncodingStack(count, Allocate(new SRestriction[count])); |  | ||||||
|             _current.Push(alloc); |  | ||||||
|  |  | ||||||
|             return alloc; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         private void End() |  | ||||||
|         { |  | ||||||
|             _current.Pop(); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         public void Encode(SearchQuery.PropertyContent part) |  | ||||||
|         { |  | ||||||
|             Current->header.rt = RestrictionType.CONTENT; |  | ||||||
|             Current->data.content.ulFuzzyLevel = ContentRestriction.FuzzyLevelFromSearchQuery(part); |  | ||||||
|             Current->data.content.ulPropTag = part.Property.Tag; |  | ||||||
|             Current->data.content.prop = (PropValue*)PropValue.MarshalFromObject(this, part.Property.Tag, part.Content); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         public void Encode(SearchQuery.PropertyCompare part) |  | ||||||
|         { |  | ||||||
|             Current->header.rt = RestrictionType.PROPERTY; |  | ||||||
|             Current->data.prop.relop = (SearchOperation)part.Operation; |  | ||||||
|             Current->data.prop.ulPropTag = part.Property.Tag; |  | ||||||
|             Current->data.prop.prop = (PropValue*)PropValue.MarshalFromObject(this, part.Property.Tag, part.Value); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         public void Encode(SearchQuery.PropertyBitMask part) |  | ||||||
|         { |  | ||||||
|             Current->header.rt = RestrictionType.BITMASK; |  | ||||||
|             Current->data.bitMask.bmr = (BMR)(int)part.Operation; |  | ||||||
|             Current->data.bitMask.prop = part.Property.Tag; |  | ||||||
|             Current->data.bitMask.mask = part.Mask; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public static class RestrictionExensions |  | ||||||
|     { |  | ||||||
|         /// <summary> |  | ||||||
|         /// Encodes the search as an SRestriction. |  | ||||||
|         /// </summary> |  | ||||||
|         /// <returns>The encoder containing the restriction. The caller is responsible for disposing.</returns> |  | ||||||
|         public static RestrictionEncoder ToRestriction(this SearchQuery search) |  | ||||||
|         { |  | ||||||
|             RestrictionEncoder encoder = new RestrictionEncoder(); |  | ||||||
|             search.Encode(encoder); |  | ||||||
|             return encoder; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,249 @@ | |||||||
|  | using Acacia.Stubs; | ||||||
|  | using Acacia.Utils; | ||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Linq; | ||||||
|  | using System.Runtime.InteropServices; | ||||||
|  | using System.Text; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  |  | ||||||
|  | namespace Acacia.Native.MAPI | ||||||
|  | { | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// Encodes a search as an SRestriction. Note that as memory needs to be managed for the miscellaneous structures, | ||||||
|  |     /// the SRestriction is only valid until RestrictionEncoder is disposed. | ||||||
|  |     /// </summary> | ||||||
|  |     unsafe public class RestrictionEncoder : DisposableWrapper, ISearchEncoder | ||||||
|  |     { | ||||||
|  |         private class Allocation | ||||||
|  |         { | ||||||
|  |             public readonly IntPtr ptr; | ||||||
|  |             private readonly int count; | ||||||
|  |             private int index; | ||||||
|  |  | ||||||
|  |             public SRestriction* Current | ||||||
|  |             { | ||||||
|  |                 get | ||||||
|  |                 { | ||||||
|  |                     if (index >= count) | ||||||
|  |                         throw new InvalidProgramException(); | ||||||
|  |                     return Pointer + index; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             public SRestriction* Pointer { get { return (SRestriction*)ptr; } } | ||||||
|  |  | ||||||
|  |             /// <summary> | ||||||
|  |             /// Constructor. Allocates the memory for the object. | ||||||
|  |             /// </summary> | ||||||
|  |             /// <param name="root">The root allocation, or null if this is the root. All allocations will be added to the root.</param> | ||||||
|  |             /// <param name="count">The number of SRestriction objects to allocate</param> | ||||||
|  |             public Allocation(RestrictionEncoder encoder, int count) | ||||||
|  |             { | ||||||
|  |                 this.count = count; | ||||||
|  |                 // Allocate the buffer | ||||||
|  |                 ptr = encoder.AllocateArray<SRestriction>(count); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             public void Next() | ||||||
|  |             { | ||||||
|  |                 ++index; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         private Allocation _root; | ||||||
|  |         private readonly Stack<Allocation> _stack = new Stack<Allocation>(); | ||||||
|  |  | ||||||
|  |         public RestrictionEncoder() | ||||||
|  |         { | ||||||
|  |             // Push the root entry | ||||||
|  |             _root = Push(1); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         protected override void DoRelease() | ||||||
|  |         { | ||||||
|  |             if (_root != null) | ||||||
|  |             { | ||||||
|  |                 uint res = MAPI.MAPIFreeBuffer(_root.ptr); | ||||||
|  |                 if (res != 0) | ||||||
|  |                 { | ||||||
|  |                     // TODO: log? | ||||||
|  |                 } | ||||||
|  |                 _root = null; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public SRestriction* Encoded | ||||||
|  |         { | ||||||
|  |             get { return _root.Pointer; } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public SRestriction* Current | ||||||
|  |         { | ||||||
|  |             get { return _stack.Peek().Current; } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private Allocation Push(int count = 1) | ||||||
|  |         { | ||||||
|  |             Allocation alloc = new Allocation(this, count); | ||||||
|  |             _stack.Push(alloc); | ||||||
|  |             return alloc; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private void Pop(Allocation expected) | ||||||
|  |         { | ||||||
|  |             Allocation alloc = _stack.Pop(); | ||||||
|  |             if (expected != alloc) | ||||||
|  |                 throw new InvalidProgramException(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public void Encode(SearchQuery.PropertyExists part) | ||||||
|  |         { | ||||||
|  |             Current->header.rt = RestrictionType.EXIST; | ||||||
|  |             Current->data.exist.prop = part.Property.Tag; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public void Encode(SearchQuery.Or part) | ||||||
|  |         { | ||||||
|  |             Current->header.rt = RestrictionType.OR; | ||||||
|  |             Current->data.sub.cb = (uint)part.Operands.Count; | ||||||
|  |             Current->data.sub.ptr = EncodePointer(part.Operands); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public void Encode(SearchQuery.And part) | ||||||
|  |         { | ||||||
|  |             Current->header.rt = RestrictionType.AND; | ||||||
|  |             Current->data.sub.cb = (uint)part.Operands.Count; | ||||||
|  |             Current->data.sub.ptr = EncodePointer(part.Operands); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public void Encode(SearchQuery.PropertyIdentifier part) | ||||||
|  |         { | ||||||
|  |             // This should be unreachable | ||||||
|  |             throw new InvalidProgramException(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public void Encode(SearchQuery.Not part) | ||||||
|  |         { | ||||||
|  |             Current->header.rt = RestrictionType.NOT; | ||||||
|  |             Current->data.not.ptr = EncodePointer(new[] { part.Operand }); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private SRestriction* EncodePointer(IEnumerable<SearchQuery> operands) | ||||||
|  |         { | ||||||
|  |             Allocation alloc = Push(operands.Count()); | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 foreach (SearchQuery operand in operands) | ||||||
|  |                 { | ||||||
|  |                     operand.Encode(this); | ||||||
|  |                     alloc.Next(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             finally | ||||||
|  |             { | ||||||
|  |                 Pop(alloc); | ||||||
|  |             } | ||||||
|  |             return alloc.Pointer; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public void Encode(SearchQuery.PropertyContent part) | ||||||
|  |         { | ||||||
|  |             Current->header.rt = RestrictionType.CONTENT; | ||||||
|  |             Current->data.content.ulFuzzyLevel = ContentRestriction.FuzzyLevelFromSearchQuery(part); | ||||||
|  |             Current->data.content.ulPropTag = part.Property.Tag; | ||||||
|  |             Current->data.content.prop = (PropValue*)PropValue.MarshalFromObject(this, part.Property.Tag, part.Content); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public void Encode(SearchQuery.PropertyCompare part) | ||||||
|  |         { | ||||||
|  |             Current->header.rt = RestrictionType.PROPERTY; | ||||||
|  |             Current->data.prop.relop = (SearchOperation)part.Operation; | ||||||
|  |             Current->data.prop.ulPropTag = part.Property.Tag; | ||||||
|  |             Current->data.prop.prop = (PropValue*)PropValue.MarshalFromObject(this, part.Property.Tag, part.Value); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public void Encode(SearchQuery.PropertyBitMask part) | ||||||
|  |         { | ||||||
|  |             Current->header.rt = RestrictionType.BITMASK; | ||||||
|  |             Current->data.bitMask.bmr = (BMR)(int)part.Operation; | ||||||
|  |             Current->data.bitMask.prop = part.Property.Tag; | ||||||
|  |             Current->data.bitMask.mask = part.Mask; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public IntPtr AllocateArray<StructType>(int count) | ||||||
|  |         { | ||||||
|  |             // Try to just determine the size based on the type. If that fails, determine the size of a default object | ||||||
|  |             int structSize = Marshal.SizeOf(typeof(StructType)); | ||||||
|  |             return AllocateRaw(structSize * count); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public IntPtr AllocateWithExtra<PrimaryType>(int alignExtra, object extra) | ||||||
|  |         { | ||||||
|  |             // Determine the size | ||||||
|  |             int size = Marshal.SizeOf<PrimaryType>(); | ||||||
|  |             size = Util.Align(size, alignExtra); | ||||||
|  |  | ||||||
|  |             int extraOffset = size; | ||||||
|  |             size += Marshal.SizeOf(extra); | ||||||
|  |  | ||||||
|  |             // Allocate | ||||||
|  |             IntPtr ptr = AllocateRaw(size); | ||||||
|  |  | ||||||
|  |             // Copy the extra structure | ||||||
|  |             Marshal.StructureToPtr(extra, ptr + extraOffset, false); | ||||||
|  |             return ptr; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private IntPtr AllocateRaw(int size) | ||||||
|  |         {  | ||||||
|  |             IntPtr res; | ||||||
|  |             IntPtr ptr = IntPtr.Zero; | ||||||
|  |             if (_root == null) | ||||||
|  |             { | ||||||
|  |                 res = MAPI.MAPIAllocateBuffer((uint)size, ref ptr); | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 res = MAPI.MAPIAllocateMore((uint)size, _root.ptr, ref ptr); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (res != IntPtr.Zero) | ||||||
|  |                 throw new InvalidOperationException("MAPI Allocation failed: " + res); | ||||||
|  |  | ||||||
|  |             // Zero it out to prevent issues | ||||||
|  |             Kernel32.ZeroMemory(ptr, size); | ||||||
|  |  | ||||||
|  |             return ptr; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Allocates a copy of the byte array. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="bytes">The byte array</param> | ||||||
|  |         /// <param name="zeros">Number of additional zero bytes, for padding</param> | ||||||
|  |         public IntPtr Allocate(byte[] bytes, int zeros = 0) | ||||||
|  |         { | ||||||
|  |             IntPtr ptr = AllocateRaw(bytes.Length + zeros); | ||||||
|  |             Marshal.Copy(bytes, 0, ptr, bytes.Length); | ||||||
|  |             for (int i = 0; i < zeros; ++i) | ||||||
|  |             { | ||||||
|  |                 ((byte*)ptr)[bytes.Length + i] = 0; | ||||||
|  |             } | ||||||
|  |             return ptr; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static class RestrictionExensions | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// Encodes the search as an SRestriction. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <returns>The encoder containing the restriction. The caller is responsible for disposing.</returns> | ||||||
|  |         public static RestrictionEncoder ToRestriction(this SearchQuery search) | ||||||
|  |         { | ||||||
|  |             RestrictionEncoder encoder = new RestrictionEncoder(); | ||||||
|  |             search.Encode(encoder); | ||||||
|  |             return encoder; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,175 +0,0 @@ | |||||||
| using Acacia.Utils; |  | ||||||
| using System; |  | ||||||
| using System.Collections.Generic; |  | ||||||
| using System.Linq; |  | ||||||
| using System.Runtime.InteropServices; |  | ||||||
| using System.Text; |  | ||||||
| using System.Threading.Tasks; |  | ||||||
| using Acacia.Native.MAPI; |  | ||||||
|  |  | ||||||
| namespace Acacia.Native |  | ||||||
| { |  | ||||||
|     /// <summary> |  | ||||||
|     /// Helper for encoding objects into native structures. |  | ||||||
|     /// </summary> |  | ||||||
|     abstract public class NativeEncoder : DisposableWrapper |  | ||||||
|     { |  | ||||||
|         protected class AllocationBase : IDisposable |  | ||||||
|         { |  | ||||||
|             protected readonly object _obj; |  | ||||||
|             protected readonly GCHandle _handle; |  | ||||||
|             protected readonly IntPtr _ptr; |  | ||||||
|  |  | ||||||
|             public AllocationBase(Type type, int size) |  | ||||||
|             { |  | ||||||
|                 _ptr = Marshal.AllocHGlobal(size); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             public AllocationBase(object obj) |  | ||||||
|             { |  | ||||||
|                 this._obj = obj; |  | ||||||
|                 _handle = GCHandle.Alloc(obj, GCHandleType.Pinned); |  | ||||||
|                 _ptr = _handle.AddrOfPinnedObject(); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             public IntPtr Pointer { get { return _ptr; } } |  | ||||||
|              |  | ||||||
|             public void Dispose() |  | ||||||
|             { |  | ||||||
|                 if (_handle.IsAllocated) |  | ||||||
|                     _handle.Free(); |  | ||||||
|                 else |  | ||||||
|                     Marshal.FreeHGlobal(_ptr); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         unsafe protected class Allocation<ObjType> : AllocationBase |  | ||||||
|         { |  | ||||||
|             internal Allocation(int size) : base(typeof(ObjType), size) |  | ||||||
|             { |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             internal Allocation(ObjType obj) : base(obj) |  | ||||||
|             { |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             public ObjType Object |  | ||||||
|             { |  | ||||||
|                 get |  | ||||||
|                 { |  | ||||||
|                     return (ObjType)_obj; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         private readonly List<AllocationBase> _allocs = new List<AllocationBase>(); |  | ||||||
|  |  | ||||||
|         override protected void DoRelease() |  | ||||||
|         { |  | ||||||
|             foreach(AllocationBase alloc in _allocs) |  | ||||||
|                 alloc.Dispose(); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         protected AllocationBase Allocate(int size) |  | ||||||
|         { |  | ||||||
|             AllocationBase alloc = new AllocationBase(typeof(object), size); |  | ||||||
|             _allocs.Add(alloc); |  | ||||||
|             return alloc; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         protected Allocation<ObjType> Allocate<ObjType>(ObjType obj) |  | ||||||
|         { |  | ||||||
|             Allocation<ObjType> alloc = new Allocation<ObjType>(obj); |  | ||||||
|             _allocs.Add(alloc); |  | ||||||
|             return alloc; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         protected Allocation<ObjType> Allocate<ObjType>() |  | ||||||
|         { |  | ||||||
|             return Allocate<ObjType>(Activator.CreateInstance<ObjType>()); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // TODO: put in lib |  | ||||||
|         [DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)] |  | ||||||
|         private static extern void CopyMemory(IntPtr dest, IntPtr src, uint count); |  | ||||||
|  |  | ||||||
|         public IntPtr Allocate<ElementType>(ElementType[] obj, params ElementType[][] additional) |  | ||||||
|         { |  | ||||||
|             ElementType[][] all = new ElementType[][] { obj }.Concat(additional).ToArray(); |  | ||||||
|  |  | ||||||
|             int size = 0; |  | ||||||
|             int[] starts = new int[all.Length]; |  | ||||||
|             int[] sizes = new int[all.Length]; |  | ||||||
|             for (int i = 0; i < all.Length; ++i) |  | ||||||
|             { |  | ||||||
|                 starts[i] = size; |  | ||||||
|                 int thisSize = ((Array)all[i]).Length * Marshal.SizeOf<ElementType>(); |  | ||||||
|                 sizes[i] = thisSize; |  | ||||||
|                 size += thisSize; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             AllocationBase alloc = Allocate(size); |  | ||||||
|             IntPtr ptr = alloc.Pointer; |  | ||||||
|             for (int i = 0; i < all.Length; ++i) |  | ||||||
|             { |  | ||||||
|                 GCHandle handle = GCHandle.Alloc(all[i], GCHandleType.Pinned); |  | ||||||
|                 try |  | ||||||
|                 { |  | ||||||
|                     CopyMemory(ptr + starts[i], handle.AddrOfPinnedObject(), (uint)sizes[i]); |  | ||||||
|                 } |  | ||||||
|                 finally |  | ||||||
|                 { |  | ||||||
|                     handle.Free(); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             return alloc.Pointer; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         /// <summary> |  | ||||||
|         /// Returns a block of memory containing all specified objects sequentially. |  | ||||||
|         /// </summary> |  | ||||||
|         public IntPtr Allocate(object obj, params object[] additional) |  | ||||||
|         { |  | ||||||
|             object[] all = new object[] { obj }.Concat(additional).ToArray(); |  | ||||||
|  |  | ||||||
|             int size = 0; |  | ||||||
|             int[] starts = new int[all.Length]; |  | ||||||
|             object[] encode = new object[all.Length]; |  | ||||||
|             int used = 0; |  | ||||||
|             int align = -1; |  | ||||||
|             for (int i = 0; i < all.Length; ++i) |  | ||||||
|             { |  | ||||||
|                 if (all[i] is int) |  | ||||||
|                 { |  | ||||||
|                     align = (int)all[i]; |  | ||||||
|                     ++i; |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 starts[used] = Align(size, align); |  | ||||||
|                 int thisSize = Marshal.SizeOf(all[i]); |  | ||||||
|                 size = starts[used] + thisSize; |  | ||||||
|                 encode[used] = all[i]; |  | ||||||
|                 align = -1; |  | ||||||
|                 ++used; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             AllocationBase alloc = Allocate(size); |  | ||||||
|             IntPtr ptr = alloc.Pointer; |  | ||||||
|             for (int i = 0; i < used; ++i) |  | ||||||
|             { |  | ||||||
|                 Marshal.StructureToPtr(encode[i], ptr + starts[i], false); |  | ||||||
|             } |  | ||||||
|             return alloc.Pointer; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|         private int Align(int size, int align) |  | ||||||
|         { |  | ||||||
|             if (align < 0) |  | ||||||
|                 align = Marshal.SizeOf<IntPtr>(); |  | ||||||
|             int additional = (align - (size % align)) % align; |  | ||||||
|             return size + additional; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -121,6 +121,12 @@ namespace Acacia.Stubs | |||||||
|             set; |             set; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         bool SearchRunning | ||||||
|  |         { | ||||||
|  |             get; | ||||||
|  |             set; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         void Save(); |         void Save(); | ||||||
|  |  | ||||||
|         void SetCustomIcon(IPicture icon); |         void SetCustomIcon(IPicture icon); | ||||||
|   | |||||||
| @@ -392,7 +392,56 @@ namespace Acacia.Stubs.OutlookWrappers | |||||||
|             set; |             set; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         #region Search criteria | ||||||
|  |  | ||||||
|         unsafe public SearchQuery SearchCriteria |         unsafe public SearchQuery SearchCriteria | ||||||
|  |         { | ||||||
|  |             get | ||||||
|  |             { | ||||||
|  |                 IMAPIFolder imapi = _item.MAPIOBJECT as IMAPIFolder; | ||||||
|  |                 SBinaryArray* sb1 = null; | ||||||
|  |                 SRestriction* restrict = null; | ||||||
|  |                 try | ||||||
|  |                 { | ||||||
|  |                     SearchCriteriaState state; | ||||||
|  |                     imapi.GetSearchCriteria(0, &restrict, &sb1, out state); | ||||||
|  |                     Logger.Instance.Debug(this, "GetSearchCriteria: {0}: {1}\n{2}", Name, state,  | ||||||
|  |                                             restrict == null ? "<NODE>" : restrict->ToString()); | ||||||
|  |                     return restrict->ToSearchQuery(); | ||||||
|  |                 } | ||||||
|  |                 finally | ||||||
|  |                 { | ||||||
|  |                     MAPI.MAPIFreeBuffer((IntPtr)restrict); | ||||||
|  |                     MAPI.MAPIFreeBuffer((IntPtr)sb1); | ||||||
|  |                     ComRelease.Release(imapi); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             set | ||||||
|  |             { | ||||||
|  |                 IMAPIFolder imapi = _item.MAPIOBJECT as IMAPIFolder; | ||||||
|  |                 try | ||||||
|  |                 { | ||||||
|  |                     using (RestrictionEncoder res = value.ToRestriction()) | ||||||
|  |                     { | ||||||
|  |                         SRestriction* resEncoded = res.Encoded; | ||||||
|  |                         Logger.Instance.Debug(this, "SetSearchCriteria: {0}\n{1}", Name, resEncoded == null ? "<NODE>" : resEncoded->ToString()); | ||||||
|  |                         imapi.SetSearchCriteria(resEncoded, null, SearchCriteriaFlags.NONE); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 catch (Exception e) | ||||||
|  |                 { | ||||||
|  |                     Logger.Instance.Error(this, "Exception in SetSearchCriteria: {0}: {1}", Name, e); | ||||||
|  |                 } | ||||||
|  |                 finally | ||||||
|  |                 { | ||||||
|  |                     ComRelease.Release(imapi); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         unsafe public bool SearchRunning | ||||||
|         { |         { | ||||||
|             get |             get | ||||||
|             { |             { | ||||||
| @@ -400,10 +449,13 @@ namespace Acacia.Stubs.OutlookWrappers | |||||||
|                 try |                 try | ||||||
|                 { |                 { | ||||||
|                     SearchCriteriaState state; |                     SearchCriteriaState state; | ||||||
|                     SBinaryArray* sb1; |                     imapi.GetSearchCriteria(0, null, null, out state); | ||||||
|                     SRestriction* restrict; |                     return (state & SearchCriteriaState.SEARCH_RUNNING) != 0; | ||||||
|                     imapi.GetSearchCriteria(0, &restrict, &sb1, out state); |                 } | ||||||
|                     return restrict->ToSearchQuery(); |                 catch (Exception e) | ||||||
|  |                 { | ||||||
|  |                     Logger.Instance.Error(this, "Exception in GetSearchRunning: {0}: {1}", Name, e); | ||||||
|  |                     return true; | ||||||
|                 } |                 } | ||||||
|                 finally |                 finally | ||||||
|                 { |                 { | ||||||
| @@ -416,11 +468,11 @@ namespace Acacia.Stubs.OutlookWrappers | |||||||
|                 IMAPIFolder imapi = _item.MAPIOBJECT as IMAPIFolder; |                 IMAPIFolder imapi = _item.MAPIOBJECT as IMAPIFolder; | ||||||
|                 try |                 try | ||||||
|                 { |                 { | ||||||
|                     using (RestrictionEncoder res = value.ToRestriction()) |                     imapi.SetSearchCriteria(null, null, value ? SearchCriteriaFlags.RESTART_SEARCH : SearchCriteriaFlags.STOP_SEARCH); | ||||||
|                     { |                 } | ||||||
|                         SRestriction restrict = res.Restriction; |                 catch (Exception e) | ||||||
|                         imapi.SetSearchCriteria(&restrict, null, SearchCriteriaFlags.NONE); |                 { | ||||||
|                     } |                     Logger.Instance.Error(this, "Exception in SetSearchRunning: {0}: {1}", Name, e); | ||||||
|                 } |                 } | ||||||
|                 finally |                 finally | ||||||
|                 { |                 { | ||||||
| @@ -428,5 +480,7 @@ namespace Acacia.Stubs.OutlookWrappers | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         #endregion | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,4 +1,6 @@ | |||||||
| /// Copyright 2017 Kopano b.v. |  | ||||||
|  | using Acacia.Native; | ||||||
|  | /// Copyright 2017 Kopano b.v. | ||||||
| ///  | ///  | ||||||
| /// This program is free software: you can redistribute it and/or modify | /// This program is free software: you can redistribute it and/or modify | ||||||
| /// it under the terms of the GNU Affero General Public License, version 3, | /// it under the terms of the GNU Affero General Public License, version 3, | ||||||
| @@ -13,7 +15,6 @@ | |||||||
| /// along with this program.If not, see<http://www.gnu.org/licenses/>. | /// along with this program.If not, see<http://www.gnu.org/licenses/>. | ||||||
| ///  | ///  | ||||||
| /// Consult LICENSE file for details | /// Consult LICENSE file for details | ||||||
|  |  | ||||||
| using System; | using System; | ||||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||||
| using System.Drawing; | using System.Drawing; | ||||||
| @@ -27,9 +28,6 @@ namespace Acacia.Utils | |||||||
| { | { | ||||||
|     public static class ImageUtils |     public static class ImageUtils | ||||||
|     { |     { | ||||||
|         [DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)] |  | ||||||
|         private static extern void CopyMemory(IntPtr dest, IntPtr src, uint count); |  | ||||||
|  |  | ||||||
|         public static Bitmap GetBitmapFromHBitmap(IntPtr nativeHBitmap) |         public static Bitmap GetBitmapFromHBitmap(IntPtr nativeHBitmap) | ||||||
|         { |         { | ||||||
|  |  | ||||||
| @@ -49,7 +47,7 @@ namespace Acacia.Utils | |||||||
|                 { |                 { | ||||||
|                     IntPtr target = bmpData2.Scan0 + bmpData2.Stride * y; |                     IntPtr target = bmpData2.Scan0 + bmpData2.Stride * y; | ||||||
|                     IntPtr source = bmpData.Scan0 + bmpData.Stride * y; |                     IntPtr source = bmpData.Scan0 + bmpData.Stride * y; | ||||||
|                     CopyMemory(target, source, (uint)Math.Abs(bmpData2.Stride)); |                     Kernel32.CopyMemory(target, source, (uint)Math.Abs(bmpData2.Stride)); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             finally |             finally | ||||||
|   | |||||||
| @@ -49,6 +49,13 @@ namespace Acacia.Utils | |||||||
|             return _this; |             return _this; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         public static string ToTitle(this string _this) | ||||||
|  |         { | ||||||
|  |             if (string.IsNullOrWhiteSpace(_this)) | ||||||
|  |                 return _this; | ||||||
|  |             return _this.Substring(0, 1).ToUpper() + _this.Substring(1).ToLower(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |  | ||||||
|         #endregion |         #endregion | ||||||
|  |  | ||||||
|   | |||||||
| @@ -173,5 +173,11 @@ namespace Acacia.Utils | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         #endregion |         #endregion | ||||||
|  |  | ||||||
|  |         public static int Align(int size, int align) | ||||||
|  |         { | ||||||
|  |             int additional = (align - (size % align)) % align; | ||||||
|  |             return size + additional; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user