mirror of
https://github.com/Kopano-dev/kopano-ol-extension.git
synced 2023-10-10 13:37:40 +02:00
Merge branch 'master' of https://stash.kopano.io/scm/koe/kopano_ol_extension_source
This commit is contained in:
commit
dd837caba5
@ -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;
|
|
||||||
imapi.SetSearchCriteria(&restrict, null, SearchCriteriaFlags.NONE);
|
|
||||||
}
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user