From 8f5c08e4a9990ed6ea27c6239eb06c410ef3ec97 Mon Sep 17 00:00:00 2001
From: Patrick Simpson
Date: Mon, 27 Feb 2017 19:04:41 +0100
Subject: [PATCH] [KOE-12] Finished encoding of search criteria for COM. Added
code to modify search query to exclude shared folders, still need to add code
to allow for selected folders.
---
.../AcaciaZPushPlugin.csproj | 2 +
.../SharedFolders/SharedCalendarReminders.cs | 80 ++++++++
.../Features/Signatures/FeatureSignatures.cs | 2 +-
.../AcaciaZPushPlugin/Native/MAPI/Binary.cs | 14 +-
.../AcaciaZPushPlugin/Native/MAPI/Property.cs | 139 ++++++++------
.../Native/MAPI/Restriction.cs | 178 +++++++++++++++---
.../AcaciaZPushPlugin/Native/NativeEncoder.cs | 118 ++++++++++--
.../AcaciaZPushPlugin/SearchQuery.cs | 126 +++++++++++--
.../Stubs/OutlookWrappers/FolderWrapper.cs | 16 +-
9 files changed, 554 insertions(+), 121 deletions(-)
create mode 100644 src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedCalendarReminders.cs
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj
index 69353fd..4f0feb6 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj
@@ -274,6 +274,7 @@
+
UserControl
@@ -291,6 +292,7 @@
+
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedCalendarReminders.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedCalendarReminders.cs
new file mode 100644
index 0000000..7db25c1
--- /dev/null
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SharedFolders/SharedCalendarReminders.cs
@@ -0,0 +1,80 @@
+using Acacia.Native.MAPI;
+using Acacia.Stubs;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Acacia.Features.SharedFolders
+{
+ public class SharedCalendarReminders : LogContext
+ {
+ private static readonly SearchQuery.PropertyIdentifier PROP_FOLDER = new SearchQuery.PropertyIdentifier(PropTag.FromInt(0x6B20001F));
+
+ private readonly LogContext _context;
+ public string LogContextId
+ {
+ get
+ {
+ return _context.LogContextId;
+ }
+ }
+
+ public SharedCalendarReminders(LogContext context)
+ {
+ this._context = context;
+ }
+
+ public void Initialise(IStore store)
+ {
+ using (IFolder reminders = store.GetSpecialFolder(SpecialFolder.Reminders))
+ {
+ SearchQuery.Or custom = FindCustomQuery(reminders, true);
+ }
+ }
+
+ private SearchQuery.Or FindCustomQuery(IFolder reminders, bool addIfNeeded)
+ {
+ SearchQuery query = reminders.SearchCriteria;
+ if (!(query is SearchQuery.And))
+ return null;
+ Logger.Instance.Trace(this, "Current query1: {0}", query.ToString());
+
+ SearchQuery.And root = (SearchQuery.And)query;
+ // TODO: more strict checking of query
+ if (root.Operands.Count == 3)
+ {
+ SearchQuery.Or custom = root.Operands.ElementAt(2) as SearchQuery.Or;
+ if (custom != null)
+ {
+ // TODO: check property test
+ return custom;
+ }
+ }
+
+ // We have the root, but not the custom query. Create it if needed.
+ if (addIfNeeded)
+ {
+ Logger.Instance.Debug(this, "Creating custom query");
+ Logger.Instance.Trace(this, "Current query: {0}", root.ToString());
+ SearchQuery.Or custom = new SearchQuery.Or();
+
+ // Add the prefix exclusion for shared folders
+ custom.Add(
+ new SearchQuery.Not(
+ new SearchQuery.PropertyContent(
+ PROP_FOLDER, SearchQuery.ContentMatchOperation.Prefix, SearchQuery.ContentMatchModifiers.None, "S"
+ )
+ )
+ );
+
+ root.Operands.Add(custom);
+ Logger.Instance.Trace(this, "Modified query: {0}", root.ToString());
+ reminders.SearchCriteria = root;
+ Logger.Instance.Trace(this, "Modified query2: {0}", reminders.SearchCriteria.ToString());
+ }
+ return null;
+ }
+ }
+}
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/Signatures/FeatureSignatures.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/Signatures/FeatureSignatures.cs
index e4e0cd0..5da2521 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/Signatures/FeatureSignatures.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/Signatures/FeatureSignatures.cs
@@ -122,7 +122,7 @@ namespace Acacia.Features.Signatures
/// The signature hash. If null, the hash will not be checked and a hard sync will be done.
private void SyncSignatures(ZPushAccount account, string serverSignatureHash)
{
- if (account == null || !account.Capabilities.Has("signatures"))
+ if (account?.Capabilities == null || !account.Capabilities.Has("signatures"))
return;
// Check hash if needed
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Native/MAPI/Binary.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Native/MAPI/Binary.cs
index 9cbf51e..ee8702b 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Native/MAPI/Binary.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Native/MAPI/Binary.cs
@@ -33,10 +33,22 @@ namespace Acacia.Native.MAPI
public byte[] Unmarshal()
{
byte[] result = new byte[cb];
- Marshal.Copy((IntPtr)ptr, result, 0, result.Length);
+ System.Runtime.InteropServices.Marshal.Copy((IntPtr)ptr, result, 0, result.Length);
return result;
}
+ ///
+ /// Returns an instance with the data allocated in the enocder.
+ ///
+ public SBinary Marshal(NativeEncoder encoder)
+ {
+ return new SBinary()
+ {
+ cb = cb,
+ ptr = (byte*)encoder.Allocate(Unmarshal())
+ };
+ }
+
public override string ToString()
{
byte[] b = Unmarshal();
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Native/MAPI/Property.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Native/MAPI/Property.cs
index ac60294..384c845 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Native/MAPI/Property.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Native/MAPI/Property.cs
@@ -20,6 +20,7 @@ using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
+using static Acacia.Native.NativeEncoder;
namespace Acacia.Native.MAPI
{
@@ -52,7 +53,7 @@ namespace Acacia.Native.MAPI
public SearchQuery.PropertyIdentifier ToPropertyIdentifier()
{
- return SearchQuery.PropertyIdentifier.FromTag(prop, (ushort)type);
+ return new SearchQuery.PropertyIdentifier(this);
}
public static PropTag FromInt(int v)
@@ -65,80 +66,110 @@ namespace Acacia.Native.MAPI
}
}
-
-
- [StructLayout(LayoutKind.Explicit)]
- unsafe public struct PropValue
+ // TODO: align is probably wrong for 32-bit
+ [StructLayout(LayoutKind.Sequential)]
+ public struct PropValue
{
- [FieldOffset(0)]
- public PropTag ulPropTag;
+ [StructLayout(LayoutKind.Sequential)]
+ public struct Header
+ {
+ public PropTag ulPropTag;
+ }
- [FieldOffset(4)]
- public uint dwAlignPad;
+ [StructLayout(LayoutKind.Explicit)]
+ unsafe public struct Data
+ {
+ // short int i; /* case PT_I2 */
+ // LONG l; /* case PT_LONG */
+ // ULONG ul; /* alias for PT_LONG */
+ // LPVOID lpv; /* alias for PT_PTR */
+ // float flt; /* case PT_R4 */
+ // double dbl; /* case PT_DOUBLE */
+ // unsigned short int b; /* case PT_BOOLEAN */
+ [FieldOffset(0), MarshalAs(UnmanagedType.U2)]
+ public bool b;
- // short int i; /* case PT_I2 */
- // LONG l; /* case PT_LONG */
- // ULONG ul; /* alias for PT_LONG */
- // LPVOID lpv; /* alias for PT_PTR */
- // float flt; /* case PT_R4 */
- // double dbl; /* case PT_DOUBLE */
- // unsigned short int b; /* case PT_BOOLEAN */
- [FieldOffset(8), MarshalAs(UnmanagedType.U2)]
- public bool b;
+ // CURRENCY cur; /* case PT_CURRENCY */
+ // double at; /* case PT_APPTIME */
+ // FILETIME ft; /* case PT_SYSTIME */
- // CURRENCY cur; /* case PT_CURRENCY */
- // double at; /* case PT_APPTIME */
- // FILETIME ft; /* case PT_SYSTIME */
+ // LPSTR lpszA; /* case PT_STRING8 */
+ [FieldOffset(0), MarshalAs(UnmanagedType.LPStr)]
+ public sbyte* lpszA;
- // LPSTR lpszA; /* case PT_STRING8 */
- [FieldOffset(8), MarshalAs(UnmanagedType.LPStr)]
- public sbyte* lpszA;
+ // SBinary bin; /* case PT_BINARY */
+ [FieldOffset(0)]
+ public SBinary bin;
- // SBinary bin; /* case PT_BINARY */
- [FieldOffset(8)]
- public SBinary bin;
+ // LPWSTR lpszW; /* case PT_UNICODE */
+ [FieldOffset(0), MarshalAs(UnmanagedType.LPWStr)]
+ public char* lpszW;
- // LPWSTR lpszW; /* case PT_UNICODE */
- [FieldOffset(8), MarshalAs(UnmanagedType.LPWStr)]
- public char* lpszW;
+ // LPGUID lpguid; /* case PT_CLSID */
+ // LARGE_INTEGER li; /* case PT_I8 */
+ // SShortArray MVi; /* case PT_MV_I2 */
+ // SLongArray MVl; /* case PT_MV_LONG */
+ // SRealArray MVflt; /* case PT_MV_R4 */
+ // SDoubleArray MVdbl; /* case PT_MV_DOUBLE */
+ // SCurrencyArray MVcur; /* case PT_MV_CURRENCY */
+ // SAppTimeArray MVat; /* case PT_MV_APPTIME */
+ // SDateTimeArray MVft; /* case PT_MV_SYSTIME */
+ // SBinaryArray MVbin; /* case PT_MV_BINARY */
+ // SLPSTRArray MVszA; /* case PT_MV_STRING8 */
+ // SWStringArray MVszW; /* case PT_MV_UNICODE */
- // LPGUID lpguid; /* case PT_CLSID */
- // LARGE_INTEGER li; /* case PT_I8 */
- // SShortArray MVi; /* case PT_MV_I2 */
- // SLongArray MVl; /* case PT_MV_LONG */
- // SRealArray MVflt; /* case PT_MV_R4 */
- // SDoubleArray MVdbl; /* case PT_MV_DOUBLE */
- // SCurrencyArray MVcur; /* case PT_MV_CURRENCY */
- // SAppTimeArray MVat; /* case PT_MV_APPTIME */
- // SDateTimeArray MVft; /* case PT_MV_SYSTIME */
- // SBinaryArray MVbin; /* case PT_MV_BINARY */
- // SLPSTRArray MVszA; /* case PT_MV_STRING8 */
- // SWStringArray MVszW; /* case PT_MV_UNICODE */
+ // SGuidArray MVguid; /* case PT_MV_CLSID */
+ // SLargeIntegerArray MVli; /* case PT_MV_I8 */
+ // SCODE err; /* case PT_ERROR */
+ // LONG x; /* case PT_NULL, PT_OBJECT (no usable value) */
+ }
- // SGuidArray MVguid; /* case PT_MV_CLSID */
- // SLargeIntegerArray MVli; /* case PT_MV_I8 */
- // SCODE err; /* case PT_ERROR */
- // LONG x; /* case PT_NULL, PT_OBJECT (no usable value) */
+ public Header header;
+ public Data data;
public override string ToString()
{
return ToObject()?.ToString() ?? "";
}
- public object ToObject()
+ unsafe public object ToObject()
{
- switch (ulPropTag.type)
+ switch (header.ulPropTag.type)
{
case PropType.BOOLEAN:
- return b;
+ return data.b;
case PropType.STRING8:
- return new string(lpszA);
+ return new string(data.lpszA);
+ case PropType.UNICODE:
+ return new string(data.lpszW);
case PropType.BINARY:
- return bin;
- //case PropType.UNICODE:
- // return lpszW.ToString();
+ return data.bin;
+ }
+ throw new NotImplementedException();
+ }
+
+ unsafe public static IntPtr MarshalFromObject(NativeEncoder encoder, PropTag prop, object value)
+ {
+ PropValue obj = new PropValue();
+ obj.header.ulPropTag = prop;
+
+ switch (prop.type)
+ {
+ case PropType.BOOLEAN:
+ obj.data.b = (bool)value;
+ return encoder.Allocate(obj.header, obj.data.b);
+ case PropType.STRING8:
+ IntPtr ptrA = encoder.Allocate(Encoding.ASCII.GetBytes((string)value), new byte[] { 0 });
+ return encoder.Allocate(obj.header, ptrA);
+ case PropType.UNICODE:
+ IntPtr ptrW = encoder.Allocate(Encoding.Unicode.GetBytes((string)value), new byte[] { 0, 0 });
+ return encoder.Allocate(obj.header, ptrW);
+ case PropType.BINARY:
+ obj.data.bin = ((SBinary)value).Marshal(encoder);
+ return encoder.Allocate(obj.header, obj.data.bin);
+ default:
+ throw new NotImplementedException();
}
- return null;
}
}
}
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Native/MAPI/Restriction.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Native/MAPI/Restriction.cs
index 6c48a3b..d17eabf 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Native/MAPI/Restriction.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Native/MAPI/Restriction.cs
@@ -1,5 +1,6 @@
using Acacia.Stubs;
+using Acacia.Utils;
/// Copyright 2017 Kopano b.v.
///
/// This program is free software: you can redistribute it and/or modify
@@ -69,14 +70,14 @@ namespace Acacia.Native.MAPI
unsafe public struct ContentRestriction
{
- public FuzzyLevel fuzzy;
+ public FuzzyLevel ulFuzzyLevel;
public PropTag ulPropTag;
public PropValue* prop;
public string ToString(int depth)
{
string indent = new string(' ', depth);
- string s = indent + fuzzy + ":" + ulPropTag.ToString();
+ string s = indent + ulFuzzyLevel + ":" + ulPropTag.ToString();
s += ":" + prop->ToString();
s += "\n";
return s;
@@ -85,9 +86,15 @@ namespace Acacia.Native.MAPI
public SearchQuery ToSearchQuery()
{
return new SearchQuery.PropertyContent(ulPropTag.ToPropertyIdentifier(),
- (uint)fuzzy, // TODO
+ (SearchQuery.ContentMatchOperation)((uint)ulFuzzyLevel & 0xF),
+ (SearchQuery.ContentMatchModifiers)(((uint)ulFuzzyLevel & 0xF0000) >> 16),
prop->ToObject());
}
+
+ public static FuzzyLevel FuzzyLevelFromSearchQuery(SearchQuery.PropertyContent search)
+ {
+ return (FuzzyLevel)((int)search.Operation | ((int)search.Modifiers << 16));
+ }
}
// TODO: merge with ISearch
@@ -174,7 +181,7 @@ namespace Acacia.Native.MAPI
public SearchQuery ToSearchQuery()
{
- return new SearchQuery.PropertyBitMask(prop.ToPropertyIdentifier(), bmr == BMR.EQZ, mask);
+ return new SearchQuery.PropertyBitMask(prop.ToPropertyIdentifier(), (SearchQuery.BitMaskOperation)(int)bmr, mask);
}
}
@@ -305,28 +312,153 @@ namespace Acacia.Native.MAPI
}
}
- /* Example search code
+ ///
+ /// 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.
+ ///
+ unsafe public class RestrictionEncoder : NativeEncoder, ISearchEncoder
{
- MAPIFolder folder = (MAPIFolder)account.Store.GetSpecialFolder(Microsoft.Office.Interop.Outlook.OlSpecialFolders.olSpecialFolderReminders);
- dynamic obj = folder.MAPIOBJECT;
- IMAPIFolder imapi = obj as IMAPIFolder;
+ private class EncodingStack
+ {
+ public SRestriction[] array;
+ public int index;
+ public SRestriction* ptr;
- //imapi.GetSearchCriteria(0, IntPtr.Zero, IntPtr.Zero, ref state);
- GetSearchCriteriaState state;
- //imapi.GetContentsTable(0, out p);
- SBinaryArray* sb1;
- SRestriction* restrict;
- imapi.GetSearchCriteria(0, &restrict, &sb1, out state);
- Logger.Instance.Warning(this, "SEARCH:\n{0}", restrict->ToString());
+ public EncodingStack(int count, Allocation alloc)
+ {
+ array = alloc.Object;
+ index = 0;
+ ptr = (SRestriction*)alloc.Pointer;
+ }
+ }
+ private readonly Stack _current = new Stack();
+ private readonly EncodingStack _root;
- restrict->rt = RestrictionType.AND;
- imapi.SetSearchCriteria(restrict, sb1, SetSearchCriteriaFlags.NONE);
+ public RestrictionEncoder()
+ {
+ // Create an object for the root element
+ _root = Begin(1);
+ }
+ protected override void DoRelease()
+ {
+ base.DoRelease();
+ }
- //SBinaryArray sb = Marshal.PtrToStructure(p2);
- //byte[][] ids = sb.Unmarshal();
- //Logger.Instance.Warning(this, "SEARCH: {0}", StringUtil.BytesToHex(ids[0]));
- //imapi.GetLastError(0, 0, out p2);
- //imapi.SaveChanges(SaveChangesFlags.FORCE_SAVE);
- } */
+ 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->rt = RestrictionType.EXIST;
+ Current->exist.prop = part.Property.Tag;
+ }
+
+ public void Encode(SearchQuery.Or part)
+ {
+ Current->rt = RestrictionType.OR;
+ Current->sub.cb = (uint)part.Operands.Count;
+ Current->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->rt = RestrictionType.NOT;
+ Current->not.ptr = EncodePointer(new[] { part.Operand });
+ }
+
+ public void Encode(SearchQuery.And part)
+ {
+ Current->rt = RestrictionType.AND;
+ Current->sub.cb = (uint)part.Operands.Count;
+ Current->sub.ptr = EncodePointer(part.Operands);
+ }
+
+ private SRestriction* EncodePointer(IEnumerable 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->rt = RestrictionType.CONTENT;
+ Current->content.ulFuzzyLevel = ContentRestriction.FuzzyLevelFromSearchQuery(part);
+ Current->content.ulPropTag = part.Property.Tag;
+ Current->content.prop = (PropValue*)PropValue.MarshalFromObject(this, part.Property.Tag, part.Content);
+ }
+
+ public void Encode(SearchQuery.PropertyCompare part)
+ {
+ Current->rt = RestrictionType.PROPERTY;
+ Current->prop.relop = (SearchOperation)part.Operation;
+ Current->prop.ulPropTag = part.Property.Tag;
+ Current->prop.prop = (PropValue*)PropValue.MarshalFromObject(this, part.Property.Tag, part.Value);
+ }
+
+ public void Encode(SearchQuery.PropertyBitMask part)
+ {
+ Current->rt = RestrictionType.BITMASK;
+ Current->bitMask.bmr = (BMR)(int)part.Operation;
+ Current->bitMask.prop = part.Property.Tag;
+ Current->bitMask.mask = part.Mask;
+ }
+ }
+
+ public static class RestrictionExensions
+ {
+ ///
+ /// Encodes the search as an SRestriction.
+ ///
+ /// The encoder containing the restriction. The caller is responsible for disposing.
+ public static RestrictionEncoder ToRestriction(this SearchQuery search)
+ {
+ RestrictionEncoder encoder = new RestrictionEncoder();
+ search.Encode(encoder);
+ return encoder;
+ }
+ }
}
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Native/NativeEncoder.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Native/NativeEncoder.cs
index d6e73cc..cf38e2a 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Native/NativeEncoder.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Native/NativeEncoder.cs
@@ -5,6 +5,7 @@ using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
+using Acacia.Native.MAPI;
namespace Acacia.Native
{
@@ -13,7 +14,7 @@ namespace Acacia.Native
///
abstract public class NativeEncoder : DisposableWrapper
{
- abstract protected class AllocationBase
+ protected class AllocationBase : IDisposable
{
protected readonly object _obj;
protected readonly GCHandle _handle;
@@ -31,15 +32,24 @@ namespace Acacia.Native
_ptr = _handle.AddrOfPinnedObject();
}
- // TODO: release
+ public IntPtr Pointer { get { return _ptr; } }
+
+ public void Dispose()
+ {
+ if (_handle.IsAllocated)
+ _handle.Free();
+ else
+ Marshal.FreeHGlobal(_ptr);
+ }
}
+
unsafe protected class Allocation : AllocationBase
{
- public Allocation(int size) : base(typeof(ObjType), size)
+ internal Allocation(int size) : base(typeof(ObjType), size)
{
}
- public Allocation(ObjType obj) : base(obj)
+ internal Allocation(ObjType obj) : base(obj)
{
}
@@ -50,26 +60,21 @@ namespace Acacia.Native
return (ObjType)_obj;
}
}
-
- public IntPtr Pointer
- {
- get
- {
- return _ptr;
- }
- }
}
private readonly List _allocs = new List();
- ///
- /// Allocates an object of the specified type. The allocation is managed by this encoder.
- ///
- /// If larger than 0, the size to allocate. Otherwise, the size of the object is used.
- /// The allocated object.
- protected Allocation Allocate(int size = -1)
+ override protected void DoRelease()
{
- throw new NotImplementedException();
+ 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 Allocate(ObjType obj)
@@ -78,5 +83,80 @@ namespace Acacia.Native
_allocs.Add(alloc);
return alloc;
}
+
+ protected Allocation Allocate()
+ {
+ return Allocate(Activator.CreateInstance());
+ }
+
+ // 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[] 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();
+ 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;
+ }
+
+ ///
+ /// Returns a block of memory containing all specified objects sequentially.
+ ///
+ 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];
+ for (int i = 0; i < all.Length; ++i)
+ {
+ starts[i] = Align(size);
+ int thisSize = Marshal.SizeOf(all[i]);
+ size = starts[i] + thisSize;
+ }
+
+ AllocationBase alloc = Allocate(size);
+ IntPtr ptr = alloc.Pointer;
+ for (int i = 0; i < all.Length; ++i)
+ {
+ Marshal.StructureToPtr(all[i], ptr + starts[i], false);
+ }
+ return alloc.Pointer;
+ }
+
+
+
+ private int Align(int size)
+ {
+ int align = Marshal.SizeOf();
+ int additional = (align - (size % align)) % align;
+ return size + additional;
+ }
}
}
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/SearchQuery.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/SearchQuery.cs
index c010631..45e9f5d 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/SearchQuery.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/SearchQuery.cs
@@ -32,6 +32,7 @@ namespace Acacia
void Encode(SearchQuery.And part);
void Encode(SearchQuery.Or part);
void Encode(SearchQuery.Not part);
+ void Encode(SearchQuery.PropertyIdentifier part);
}
public class ToStringEncoder : ISearchEncoder
@@ -46,21 +47,20 @@ namespace Acacia
public void Encode(SearchQuery.And part)
{
- EncodeMulti(part, "AND");
+ EncodeMulti("AND", part.Operands);
}
public void Encode(SearchQuery.Or part)
{
- EncodeMulti(part, "OR");
+ EncodeMulti("OR", part.Operands);
}
public void Encode(SearchQuery.Not part)
{
- _builder.Append("NOT ");
- part.Operand.Encode(this);
+ EncodeMulti("NOT", new[] { part.Operand });
}
- private void EncodeMulti(SearchQuery.MultiOperator part, string oper)
+ private void EncodeMulti(string oper, IEnumerable parts)
{
Indent();
_builder.Append(oper).Append("\n");
@@ -69,7 +69,7 @@ namespace Acacia
++_indent;
- foreach (SearchQuery operand in part.Operands)
+ foreach (SearchQuery operand in parts)
operand.Encode(this);
--_indent;
@@ -80,22 +80,59 @@ namespace Acacia
public void Encode(SearchQuery.PropertyBitMask part)
{
- _builder.Append("BITMASK:").Append(part.Property); // TODO: operator/value
+ Indent();
+ _builder.Append("BITMASK{");
+ part.Property.Encode(this);
+ _builder.Append(" ").Append(part.Operation).Append(" ");
+ _builder.Append(part.Mask.ToString("X8"));
+ _builder.Append("}\n");
}
+ private static readonly string[] COMPARISON_OPERATORS = {"<", "<=", ">", ">=", "==", "!=", "LIKE"};
+
public void Encode(SearchQuery.PropertyCompare part)
{
- _builder.Append("COMPARE:").Append(part.Property); // TODO: operator/value
+ Indent();
+ _builder.Append("COMPARE{");
+ part.Property.Encode(this);
+ _builder.Append(" ").Append(COMPARISON_OPERATORS[(int)part.Operation]).Append(" ");
+ _builder.Append(part.Value);
+ _builder.Append("}\n");
}
public void Encode(SearchQuery.PropertyContent part)
{
- _builder.Append("CONTENT:").Append(part.Property); // TODO: operator/value
+ Indent();
+ _builder.Append("CONTENT{");
+ part.Property.Encode(this);
+
+ List options = new List();
+ if (part.Operation != SearchQuery.ContentMatchOperation.Full)
+ options.Add(part.Operation.ToString());
+
+ if (part.Modifiers != SearchQuery.ContentMatchModifiers.None)
+ {
+ options.Add(part.Modifiers.ToString());
+ }
+
+ string optionsString = options.Count == 0 ? "" : ("(" + string.Join(",", options) + ")");
+
+ _builder.Append(" ==").Append(optionsString).Append(" ");
+ _builder.Append(part.Content);
+ _builder.Append("}\n");
}
public void Encode(SearchQuery.PropertyExists part)
{
- _builder.Append("EXISTS:").Append(part.Property);
+ Indent();
+ _builder.Append("EXISTS{");
+ part.Property.Encode(this);
+ _builder.Append("}\n");
+ }
+
+ public void Encode(SearchQuery.PropertyIdentifier part)
+ {
+ _builder.Append(part.Id);
}
public string GetValue()
@@ -133,7 +170,7 @@ namespace Acacia
_operands.Add(operand);
}
- public IEnumerable Operands
+ public ICollection Operands
{
get { return _operands; }
}
@@ -180,16 +217,23 @@ namespace Acacia
///
public class PropertyIdentifier
{
- private string _id;
+ public string Id { get; private set; }
+ public PropTag Tag { get; private set; }
- public PropertyIdentifier(string id)
+ public PropertyIdentifier(PropTag tag)
{
- this._id = id;
+ this.Tag = tag;
+ Id = string.Format("{0:X4}{1:X4}", tag.prop, (int)tag.type);
}
- public static PropertyIdentifier FromTag(ushort prop, ushort type)
+ public void Encode(ISearchEncoder encoder)
{
- return new PropertyIdentifier(string.Format("{0:4X}{1:4X}", prop, type));
+ encoder.Encode(this);
+ }
+
+ public override string ToString()
+ {
+ return Id;
}
}
@@ -247,11 +291,44 @@ namespace Acacia
}
}
+ public enum ContentMatchOperation
+ {
+ ///
+ /// Match full content
+ ///
+ Full,
+
+ ///
+ /// Match part of the content
+ ///
+ SubString,
+
+ ///
+ /// Match the start of the content
+ ///
+ Prefix
+ }
+
+ [Flags]
+ public enum ContentMatchModifiers
+ {
+ None = 0,
+ CaseInsensitive = 1,
+ IgnoreNonSpace = 2,
+ Loose = 4
+ }
+
public class PropertyContent : PropertyQuery
{
- public PropertyContent(PropertyIdentifier property, uint options, object content) : base(property)
+ public ContentMatchOperation Operation { get; set; }
+ public ContentMatchModifiers Modifiers { get; set; }
+ public object Content { get; set; }
+
+ public PropertyContent(PropertyIdentifier property, ContentMatchOperation operation, ContentMatchModifiers modifiers, object content) : base(property)
{
- // TODO
+ this.Operation = operation;
+ this.Modifiers = modifiers;
+ this.Content = content;
}
public override void Encode(ISearchEncoder encoder)
@@ -260,11 +337,20 @@ namespace Acacia
}
}
+ public enum BitMaskOperation
+ {
+ EQZ, NEZ
+ }
+
public class PropertyBitMask : PropertyQuery
{
- public PropertyBitMask(PropertyIdentifier property, bool wantZero, uint mask) : base(property)
+ public BitMaskOperation Operation { get; set; }
+ public uint Mask { get; set; }
+
+ public PropertyBitMask(PropertyIdentifier property, BitMaskOperation operation, uint mask) : base(property)
{
- // TODO
+ this.Operation = operation;
+ this.Mask = mask;
}
public override void Encode(ISearchEncoder encoder)
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/FolderWrapper.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/FolderWrapper.cs
index ff8499f..af92cbd 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/FolderWrapper.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/FolderWrapper.cs
@@ -403,7 +403,6 @@ namespace Acacia.Stubs.OutlookWrappers
SBinaryArray* sb1;
SRestriction* restrict;
imapi.GetSearchCriteria(0, &restrict, &sb1, out state);
- Logger.Instance.Warning(this, "SEARCH:\n{0}", restrict->ToString());
return restrict->ToSearchQuery();
}
finally
@@ -414,8 +413,19 @@ namespace Acacia.Stubs.OutlookWrappers
set
{
- // TODO
- throw new NotImplementedException();
+ IMAPIFolder imapi = _item.MAPIOBJECT as IMAPIFolder;
+ try
+ {
+ using (RestrictionEncoder res = value.ToRestriction())
+ {
+ SRestriction restrict = res.Restriction;
+ imapi.SetSearchCriteria(&restrict, null, SearchCriteriaFlags.NONE);
+ }
+ }
+ finally
+ {
+ ComRelease.Release(imapi);
+ }
}
}
}