kopano-ol-extension/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/FolderWrapper.cs

528 lines
16 KiB
C#

/// Copyright 2016 Kopano b.v.
///
/// 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,
/// as published by the Free Software Foundation.
///
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
/// GNU Affero General Public License for more details.
///
/// You should have received a copy of the GNU Affero General Public License
/// along with this program.If not, see<http://www.gnu.org/licenses/>.
///
/// Consult LICENSE file for details
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections;
using Acacia.Utils;
using Acacia.ZPush;
using NSOutlook = Microsoft.Office.Interop.Outlook;
using Acacia.Native.MAPI;
using stdole;
namespace Acacia.Stubs.OutlookWrappers
{
public class FolderWrapper : OutlookWrapper<NSOutlook.Folder>, IFolder
{
public FolderWrapper(NSOutlook.MAPIFolder folder)
:
base((NSOutlook.Folder)folder)
{
}
protected override void DoRelease()
{
base.DoRelease();
}
protected NSOutlook.MAPIFolder CloneComObject()
{
using (ComRelease com = new ComRelease())
{
NSOutlook.Application app = com.Add(_item.Application);
NSOutlook.NameSpace session = com.Add(app.Session);
NSOutlook.MAPIFolder folder = session.GetFolderFromID(EntryID);
return folder;
}
}
virtual public IFolder Clone()
{
return new FolderWrapper(CloneComObject());
}
public void Save()
{
IMAPIFolder imapi = _item.MAPIOBJECT as IMAPIFolder;
try
{
imapi.SaveChanges(SaveChangesFlags.FORCE_SAVE);
}
finally
{
ComRelease.Release(imapi);
}
}
public NSOutlook.Folder RawItem { get { return _item; } }
protected override NSOutlook.PropertyAccessor GetPropertyAccessor()
{
return _item.PropertyAccessor;
}
public string FullFolderPath { get { return _item.FullFolderPath; } }
override protected IFolder ParentUnchecked
{
get
{
// The wrapper manages the returned folder
return Mapping.Wrap<IFolder>(_item.Parent as NSOutlook.Folder);
}
}
/// <summary>
/// Checks if the folder is at the specified depth. The root folder is at depth 0, its children at depth 1, etc.
/// This function exists because sometimes it's need to determine if a folder is at a specific depth; using this
/// function prevents creating lots of wrappers.
/// </summary>
public bool IsAtDepth(int depth)
{
using (ComRelease com = new ComRelease())
{
// The parent of the root item is a session, not null. Hence the explicit type checks.
// _item is managed by this wrapper and does not need to be released.
NSOutlook.Folder current = _item;
for (int i = 0; i < depth; ++i)
{
object parent = com.Add(current.Parent);
current = parent as NSOutlook.Folder;
if (current == null)
return false;
}
// Check if the remaining parent is a folder
object finalParent = com.Add(current.Parent);
return !(finalParent is NSOutlook.Folder);
}
}
public SyncId SyncId
{
get
{
string syncId = (string)GetProperty(OutlookConstants.PR_ZPUSH_SYNC_ID);
return syncId == null ? null : new SyncId(syncId);
}
}
public BackendId BackendId
{
get
{
string backendId = (string)GetProperty(OutlookConstants.PR_ZPUSH_BACKEND_ID);
return backendId == null ? null : new BackendId(backendId);
}
}
public override string LogKey
{
get
{
string s = EntryID;
try
{
s += ":" + SyncId;
}
catch (Exception) { }
try
{
s += ":" + Name;
}
catch (Exception) { }
return s;
}
}
override public string EntryID { get { return _item.EntryID; } }
override public IStore GetStore() { return Mapping.Wrap(_item.Store); }
public ItemType ItemType { get { return (ItemType)(int)_item.DefaultItemType; } }
public IItems Items
{
get
{
return new ItemsWrapper(this);
}
}
public IItem GetItemById(string entryId)
{
try
{
using (IStore store = GetStore())
{
return store.GetItemFromID(entryId);
}
}
catch(System.Exception)
{
return null;
}
}
public string Name
{
get { return _item.Name; }
set { _item.Name = value; }
}
public string Description
{
get { return _item.Description; }
set { _item.Description = value; }
}
public string DefaultMessageClass
{
get { return _item.DefaultMessageClass; }
}
public bool ShowAsOutlookAB
{
get { return _item.ShowAsOutlookAB; }
set { _item.ShowAsOutlookAB = value; }
}
public ISearch<ItemType> Search<ItemType>()
where ItemType: IItem
{
return new SearchWrapper<ItemType>(_item.Items);
}
#region Subfolders
public IEnumerable<FolderType> GetSubFolders<FolderType>()
where FolderType : IFolder
{
// Don't release the items, the wrapper manages them
foreach (NSOutlook.Folder folder in _item.Folders.ComEnum(false))
{
yield return folder.Wrap<FolderType>();
};
}
public IFolders SubFolders
{
get
{
return new FoldersWrapper(this);
}
}
public FolderType GetSubFolder<FolderType>(string name)
where FolderType : IFolder
{
// Fetching the folder by name throws an exception if not found, loop and find
// to prevent exceptions in the log.
// Don't release the items in RawEnum, they are release manually or handed to WrapFolders.
NSOutlook.Folder sub = null;
foreach(NSOutlook.Folder folder in _item.Folders.ComEnum(false))
{
if (folder.Name == name)
{
sub = folder;
break; // TODO: does this prevent the rest of the objects from getting released?
}
else
{
ComRelease.Release(folder);
}
}
if (sub == null)
return default(FolderType);
return sub.Wrap<FolderType>();
}
public FolderType CreateFolder<FolderType>(string name)
where FolderType : IFolder
{
using (ComRelease com = new ComRelease())
{
NSOutlook.Folders folders = com.Add(_item.Folders);
if (typeof(FolderType) == typeof(IFolder))
{
return folders.Add(name).Wrap<FolderType>();
}
else if (typeof(FolderType) == typeof(IAddressBook))
{
NSOutlook.MAPIFolder newFolder = folders.Add(name, NSOutlook.OlDefaultFolders.olFolderContacts);
newFolder.ShowAsOutlookAB = true;
return newFolder.Wrap<FolderType>();
}
else
throw new NotSupportedException();
}
}
#endregion
public IStorageItem GetStorageItem(string name)
{
NSOutlook.StorageItem item = _item.GetStorage(name, NSOutlook.OlStorageIdentifierType.olIdentifyBySubject);
if (item == null)
return null;
return new StorageItemWrapper(item);
}
#region Item creation
/// <summary>
/// Creates a new item
/// </summary>
public ItemType Create<ItemType>()
where ItemType : IItem
{
using (ComRelease com = new ComRelease())
{
NSOutlook.Items items = com.Add(_item.Items);
object item = items.Add(Mapping.OutlookItemType<ItemType>());
return Mapping.Wrap<ItemType>(item);
}
}
#endregion
override public void Delete()
{
_item.Delete();
}
#region Misc
public override string ToString()
{
return "Folder: " + _item.Name;
}
#endregion
#region Events
// Hook the BeforeItemMove event handler only if someone is actually listening on it
private IFolder_BeforeItemMove _beforeItemMove;
public event IFolder_BeforeItemMove BeforeItemMove
{
add
{
if (_beforeItemMove == null)
HookBeforeItemMove(true);
_beforeItemMove += value;
}
remove
{
_beforeItemMove -= value;
if (_beforeItemMove == null)
HookBeforeItemMove(false);
}
}
private void HookBeforeItemMove(bool hook)
{
if (hook)
_item.BeforeItemMove += HandleBeforeItemMove;
else
_item.BeforeItemMove -= HandleBeforeItemMove;
}
private void HandleBeforeItemMove(object item, NSOutlook.MAPIFolder target, ref bool cancel)
{
try
{
if (_beforeItemMove != null)
{
using (IItem itemWrapped = Mapping.Wrap<IItem>(item, false))
using (IFolder targetWrapped = Mapping.Wrap<IFolder>(target, false))
{
if (itemWrapped != null && targetWrapped != null)
{
_beforeItemMove(this, itemWrapped, targetWrapped, ref cancel);
}
}
}
}
catch(System.Exception e)
{
Logger.Instance.Error(this, "Exception in HandleBeforeItemMove: {0}", e);
}
}
private IFolder_BeforeFolderMove _beforeFolderMove;
public event IFolder_BeforeFolderMove BeforeFolderMove
{
add
{
if (_beforeFolderMove == null)
HookBeforeFolderMove(true);
_beforeFolderMove += value;
}
remove
{
_beforeFolderMove -= value;
if (_beforeFolderMove == null)
HookBeforeFolderMove(false);
}
}
private void HookBeforeFolderMove(bool hook)
{
if (hook)
_item.BeforeFolderMove += HandleBeforeFolderMove;
else
_item.BeforeFolderMove -= HandleBeforeFolderMove;
}
private void HandleBeforeFolderMove(NSOutlook.MAPIFolder target, ref bool cancel)
{
try
{
if (_beforeFolderMove != null)
{
using (IFolder targetWrapped = Mapping.Wrap<IFolder>(target, false))
{
if (targetWrapped != null)
{
_beforeFolderMove(this, targetWrapped, ref cancel);
}
}
}
}
catch (System.Exception e)
{
Logger.Instance.Error(this, "Exception in HandleBeforeItemMove: {0}", e);
}
}
public void SetCustomIcon(IPicture icon)
{
_item.SetCustomIcon(((PictureWrapper)icon).RawItem as StdPicture);
}
#endregion
public ItemType DefaultItemType
{
get { return (ItemType)(int)_item.DefaultItemType; }
}
public ZPushFolder ZPush
{
get;
set;
}
#region Search criteria
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.Trace(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.Trace(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
{
IMAPIFolder imapi = _item.MAPIOBJECT as IMAPIFolder;
try
{
SearchCriteriaState state;
imapi.GetSearchCriteria(0, null, null, out state);
return (state & SearchCriteriaState.SEARCH_RUNNING) != 0;
}
catch (Exception e)
{
Logger.Instance.Error(this, "Exception in GetSearchRunning: {0}: {1}", Name, e);
return true;
}
finally
{
ComRelease.Release(imapi);
}
}
set
{
IMAPIFolder imapi = _item.MAPIOBJECT as IMAPIFolder;
try
{
imapi.SetSearchCriteria(null, null, value ? SearchCriteriaFlags.RESTART_SEARCH : SearchCriteriaFlags.STOP_SEARCH);
}
catch (Exception e)
{
Logger.Instance.Error(this, "Exception in SetSearchRunning: {0}: {1}", Name, e);
}
finally
{
ComRelease.Release(imapi);
}
}
}
#endregion
}
}