Cleaned up ZPushFolder, removed Folder subclass. It compiles, but will not run. Event handling code disabled for now.

This commit is contained in:
Patrick Simpson 2017-02-10 11:59:07 +01:00
parent 297efe023b
commit 5842e90203
29 changed files with 312 additions and 325 deletions

View File

@ -279,6 +279,7 @@
<Compile Include="Native\MAPI.cs" />
<Compile Include="Native\IOleWindow.cs" />
<Compile Include="OutlookConstants.cs" />
<Compile Include="Stubs\Enums.cs" />
<Compile Include="Stubs\IAddIn.cs" />
<Compile Include="Stubs\IAddressEntry.cs" />
<Compile Include="Stubs\ICommandBars.cs" />
@ -292,12 +293,14 @@
<Compile Include="Stubs\OutlookWrappers\ExplorerWrapper.cs" />
<Compile Include="Stubs\OutlookWrappers\OutlookItemWrapper.cs" />
<Compile Include="Stubs\OutlookWrappers\RecipientWrapper.cs" />
<Compile Include="Stubs\Wrappers.cs" />
<Compile Include="UI\Outlook\OutlookImageList.cs" />
<Compile Include="UI\Outlook\RibbonToggleButton.cs" />
<Compile Include="UI\Outlook\RibbonButton.cs" />
<Compile Include="UI\Outlook\CommandElement.cs" />
<Compile Include="UI\Outlook\MenuItem.cs" />
<Compile Include="UI\Outlook\Types.cs" />
<Compile Include="Utils\DisposableWrapper.cs" />
<Compile Include="Utils\ImageUtils.cs" />
<Compile Include="Utils\RegistryUtil.cs" />
<Compile Include="ZPush\API\SharedFolders\AvailableFolder.cs" />
@ -323,7 +326,6 @@
<Compile Include="ZPush\Connect\ZPushRequestEncoder.cs" />
<Compile Include="ZPush\Connect\Soap\SoapRequestEncoder.cs" />
<Compile Include="ZPush\Connect\Soap\SoapRequest.cs" />
<Compile Include="Stubs\ItemType.cs" />
<Compile Include="Stubs\OutlookWrappers\ComWrapper.cs" />
<Compile Include="UI\FeatureSettings.cs">
<SubType>UserControl</SubType>

View File

@ -251,7 +251,7 @@ namespace Acacia.Features.GAB
if (_processing == 0)
{
// Check parent folder is a GAB contacts folder
if (_gabFolders.Contains(item.ParentEntryId) && IsGABItem(item))
if (_gabFolders.Contains(item.ParentEntryID) && IsGABItem(item))
{
DoSuppressEvent(findInspector ? item : null, ref cancel);
}
@ -434,7 +434,7 @@ namespace Acacia.Features.GAB
gab.AttrHidden = false;
// Update admin
_gabFolders.Add(gab.EntryId);
_gabFolders.Add(gab.EntryID);
GABInfo gabInfo = GABInfo.Get(gab, domainName);
gabInfo.Store(gab);
@ -548,7 +548,7 @@ namespace Acacia.Features.GAB
GABInfo info = GetGABContactsFolderInfo(subfolder);
if (info != null && !_domains.Contains(info.Domain))
{
Logger.Instance.Info(this, "Unused GAB folder: {0} - {1}", subfolder.EntryId, subfolder.Name);
Logger.Instance.Info(this, "Unused GAB folder: {0} - {1}", subfolder.EntryID, subfolder.Name);
try
{
deletedSomething = true;

View File

@ -205,7 +205,7 @@ namespace Acacia.Features.GAB
// TODO: make type-checking iterator?
if (item is IZPushItem)
{
string entryId = item.EntryId;
string entryId = item.EntryID;
Logger.Instance.Trace(this, "Checking chunk: {0}", item.Subject);
if (_feature.ProcessItems2)
{
@ -613,7 +613,7 @@ namespace Acacia.Features.GAB
{
using (IItem item = FindItemById(memberId))
{
Logger.Instance.Debug(this, "Finding member {0} of {1}: {2}", memberId, id, item?.EntryId);
Logger.Instance.Debug(this, "Finding member {0} of {1}: {2}", memberId, id, item?.EntryID);
if (item != null)
AddGroupMember(group, item);
}
@ -670,7 +670,7 @@ namespace Acacia.Features.GAB
{
using (IItem groupItem = FindItemById(memberOf))
{
Logger.Instance.Debug(this, "Finding group {0} for {1}: {2}", memberOf, id, groupItem?.EntryId);
Logger.Instance.Debug(this, "Finding group {0} for {1}: {2}", memberOf, id, groupItem?.EntryID);
if (groupItem is IDistributionList)
{
AddGroupMember((IDistributionList)groupItem, item);

View File

@ -92,7 +92,7 @@ namespace Acacia.Features.Notes
{
// Only patch if on a ZPush server that supports notes. Store the folder as entryId, there have been some
// issues with the folder object being disposed in the past
string folderId = folder.EntryId;
string folderId = folder.EntryID;
ZPushAccount zpush = Watcher.Accounts.GetAccount(folder);
if (zpush != null)
{
@ -219,7 +219,7 @@ namespace Acacia.Features.Notes
{
if ((int)item.GetProperty(OutlookConstants.PR_ICON_INDEX) != 771)
{
Logger.Instance.Trace(this, "Patching item: {0}", item.EntryId);
Logger.Instance.Trace(this, "Patching item: {0}", item.EntryID);
// Patch standard properties
item.SetProperties(

View File

@ -106,9 +106,9 @@ namespace Acacia.Features.SecondaryContacts
Logger.Instance.Debug(this, "Patched secondary contacts folder: {0}", strippedName);
// Register and show a warning, if not already done.
// Note that patching may be done multiple times.
if (!_warnedFolders.Contains(folder.EntryId))
if (!_warnedFolders.Contains(folder.EntryID))
{
_warnedFolders.Add(folder.EntryId);
_warnedFolders.Add(folder.EntryID);
if (MessageBox.Show(StringUtil.GetResourceString("SecondaryContactsPatched_Body", strippedName),
StringUtil.GetResourceString("SecondaryContactsPatched_Title"),
@ -121,7 +121,7 @@ namespace Acacia.Features.SecondaryContacts
}
}
// If _warnedFolders does not contain the folder (and it's hidden), this means Outlook was restarted.
else if (!_warnedFolders.Contains(folder.EntryId))
else if (!_warnedFolders.Contains(folder.EntryID))
{
// Stage 2

View File

@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Acacia.Stubs
{
// Replacement for olItemType
public enum ItemType
{
MailItem = 0,
AppointmentItem = 1,
ContactItem = 2,
TaskItem = 3,
JournalItem = 4,
NoteItem = 5,
PostItem = 6,
DistributionListItem = 7,
MobileItemSMS = 11,
MobileItemMMS = 12
}
// Replacement for olDefaultFolders
public enum DefaultFolder
{
DeletedItems = 3,
Outbox = 4,
SentMail = 5,
Inbox = 6,
Calendar = 9,
Contacts = 10,
Journal = 11,
Notes = 12,
Tasks = 13,
Drafts = 16,
FoldersAllPublicFolders = 18,
Conflicts = 19,
SyncIssues = 20,
LocalFailures = 21,
ServerFailures = 22,
Junk = 23,
RssFeeds = 25,
ToDo = 28,
ManagedEmail = 29,
SuggestedContacts = 30
}
}

View File

@ -36,11 +36,16 @@ namespace Acacia.Stubs
#region Ids and hierarchy
string EntryId { get; }
string EntryID { get; }
IFolder Parent { get; }
string ParentEntryId { get; }
string ParentEntryID { get; }
/// <summary>
/// Returns the store. The owner is responsible for disposing.
/// TODO: make method to make disposing clear
/// </summary>
IStore Store { get; }
/// <summary>
/// Quick accessor to Store.Id, to prevent allocating a wrapper for it.
/// </summary>

View File

@ -41,6 +41,10 @@ namespace Acacia.Stubs
IItem GetItemById(string id);
string FullFolderPath { get; }
ItemType DefaultItemType { get; }
#endregion
#region Searching

View File

@ -29,6 +29,17 @@ namespace Acacia.Stubs
/// </summary>
/// <returns>The root folder. The caller is responsible for disposing.</returns>
IFolder GetRootFolder();
/// <summary>
/// Returns a default folder.
/// </summary>
/// <returns>The default folder. The caller is responsible for disposing.</returns>
IFolder GetDefaultFolder(DefaultFolder folder);
/// <summary>
/// Returns GetDefaultFolder.EntryID, for simplified memory manaement
/// </summary>
string GetDefaultFolderId(DefaultFolder folder);
IItem GetItemFromID(string id);
string DisplayName { get; }
string StoreID { get; }

View File

@ -1,38 +0,0 @@
/// 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;
namespace Acacia.Stubs
{
public enum ItemType
{
MailItem = 0,
AppointmentItem = 1,
ContactItem = 2,
TaskItem = 3,
JournalItem = 4,
NoteItem = 5,
PostItem = 6,
DistributionListItem = 7,
MobileItemSMS = 11,
MobileItemMMS = 12
}
}

View File

@ -228,7 +228,7 @@ namespace Acacia.Stubs.OutlookWrappers
// And fetch it and wrap
NSOutlook.Stores stores = com.Add(session.Stores);
return StoreWrapper.Wrap(stores[stores.Count]);
return Mapping.Wrap(stores[stores.Count]);
}
}
@ -242,7 +242,7 @@ namespace Acacia.Stubs.OutlookWrappers
NSOutlook.Stores stores = com.Add(session.Stores);
foreach (NSOutlook.Store store in stores)
{
yield return StoreWrapper.Wrap(store);
yield return Mapping.Wrap(store);
}
}
}

View File

@ -93,7 +93,7 @@ namespace Acacia.Stubs.OutlookWrappers
#region IBase implementation
public string EntryId { get { return _item.EntryID; } }
public string EntryID { get { return _item.EntryID; } }
public IFolder Parent
{
@ -104,7 +104,7 @@ namespace Acacia.Stubs.OutlookWrappers
}
}
public string ParentEntryId
public string ParentEntryID
{
get
{
@ -123,7 +123,7 @@ namespace Acacia.Stubs.OutlookWrappers
using (ComRelease com = new ComRelease())
{
NSOutlook.Folder parent = com.Add(_item.Parent);
return StoreWrapper.Wrap(parent?.Store);
return Mapping.Wrap(parent?.Store);
}
}
}

View File

@ -25,50 +25,7 @@ using System.Threading.Tasks;
namespace Acacia.Stubs.OutlookWrappers
{
abstract class RawComWrapper : IComWrapper
{
protected RawComWrapper()
{
Interlocked.Increment(ref Statistics.CreatedWrappers);
this._createdTrace = new System.Diagnostics.StackTrace();
MustRelease = true;
}
~RawComWrapper()
{
Interlocked.Increment(ref Statistics.DeletedWrappers);
if (!_isDisposed)
{
Logger.Instance.Warning(this, "Undisposed wrapper: {0}", _createdTrace);
// Dispose, but don't count auto disposals, so the stats show it.
DoRelease();
}
}
private bool _isDisposed;
private readonly System.Diagnostics.StackTrace _createdTrace;
virtual public void Dispose()
{
if (!_isDisposed)
{
Logger.Instance.TraceExtra(this, "Disposing wrapper: {0}", new System.Diagnostics.StackTrace());
_isDisposed = true;
Interlocked.Increment(ref Statistics.DisposedWrappers);
DoRelease();
}
}
public bool MustRelease
{
get;
set;
}
abstract protected void DoRelease();
}
abstract class ComWrapper<ItemType> : RawComWrapper
abstract class ComWrapper<ItemType> : DisposableWrapper, IComWrapper
{
protected ItemType _item { get; private set; }
@ -78,6 +35,13 @@ namespace Acacia.Stubs.OutlookWrappers
protected ComWrapper(ItemType item)
{
this._item = item;
MustRelease = true;
}
public bool MustRelease
{
get;
set;
}
override protected void DoRelease()

View File

@ -218,7 +218,7 @@ namespace Acacia.Stubs.OutlookWrappers
#region IBase implementation
public string EntryId { get { return _item.EntryID; } }
public string EntryID { get { return _item.EntryID; } }
public IFolder Parent
{
@ -229,7 +229,7 @@ namespace Acacia.Stubs.OutlookWrappers
}
}
public string ParentEntryId
public string ParentEntryID
{
get
{
@ -248,7 +248,7 @@ namespace Acacia.Stubs.OutlookWrappers
using (ComRelease com = new ComRelease())
{
NSOutlook.Folder parent = com.Add(_item.Parent);
return StoreWrapper.Wrap(parent?.Store);
return Mapping.Wrap(parent?.Store);
}
}
}

View File

@ -160,7 +160,7 @@ namespace Acacia.Stubs.OutlookWrappers
{
List<byte> id = new List<byte>();
id.AddRange(PREFIX_MEMBER_ID);
id.AddRange(StringUtil.HexToBytes(member.EntryId));
id.AddRange(StringUtil.HexToBytes(member.EntryID));
return id.ToArray();
}
@ -234,7 +234,7 @@ namespace Acacia.Stubs.OutlookWrappers
#region IBase implementation
public string EntryId { get { return _item.EntryID; } }
public string EntryID { get { return _item.EntryID; } }
public IFolder Parent
{
@ -245,7 +245,7 @@ namespace Acacia.Stubs.OutlookWrappers
}
}
public string ParentEntryId
public string ParentEntryID
{
get
{
@ -264,7 +264,7 @@ namespace Acacia.Stubs.OutlookWrappers
using (ComRelease com = new ComRelease())
{
NSOutlook.Folder parent = com.Add(_item.Parent);
return StoreWrapper.Wrap(parent?.Store);
return Mapping.Wrap(parent?.Store);
}
}
}

View File

@ -39,6 +39,8 @@ namespace Acacia.Stubs.OutlookWrappers
return _item.PropertyAccessor;
}
public string FullFolderPath { get { return _item.FullFolderPath; } }
public IFolder Parent
{
get
@ -48,7 +50,7 @@ namespace Acacia.Stubs.OutlookWrappers
}
}
public string ParentEntryId
public string ParentEntryID
{
get
{
@ -96,9 +98,10 @@ namespace Acacia.Stubs.OutlookWrappers
}
}
public string EntryId { get { return _item.EntryID; } }
public string EntryID { get { return _item.EntryID; } }
public IStore Store { get { return Mapping.Wrap(_item.Store); } }
public IStore Store { get { return StoreWrapper.Wrap(_item.Store); } }
public string StoreId
{
get
@ -460,5 +463,9 @@ namespace Acacia.Stubs.OutlookWrappers
#endregion
public ItemType DefaultItemType
{
get { return (ItemType)(int)_item.DefaultItemType; }
}
}
}

View File

@ -127,7 +127,7 @@ namespace Acacia.Stubs.OutlookWrappers
#region IBase implementation
public string EntryId { get { return _item.EntryID; } }
public string EntryID { get { return _item.EntryID; } }
public IFolder Parent
{
@ -138,7 +138,7 @@ namespace Acacia.Stubs.OutlookWrappers
}
}
public string ParentEntryId
public string ParentEntryID
{
get
{
@ -157,7 +157,7 @@ namespace Acacia.Stubs.OutlookWrappers
using (ComRelease com = new ComRelease())
{
NSOutlook.Folder parent = com.Add(_item.Parent);
return StoreWrapper.Wrap(parent?.Store);
return Mapping.Wrap(parent?.Store);
}
}
}

View File

@ -80,11 +80,23 @@ namespace Acacia.Stubs.OutlookWrappers
public static IRecipient Wrap(NSOutlook.Recipient r, bool mustRelease = true)
{
if (r == null)
return null;
RecipientWrapper wrapped = new RecipientWrapper(r);
wrapped.MustRelease = mustRelease;
return wrapped;
}
// TODO: extension methods for this
public static IStore Wrap(NSOutlook.Store obj, bool mustRelease = true)
{
if (obj == null)
return null;
StoreWrapper wrapped = new StoreWrapper(obj);
wrapped.MustRelease = mustRelease;
return wrapped;
}
// TODO: are these not the same now? Differ only on wrong type?
public static Type WrapOrDefault<Type>(object o, bool mustRelease = true)
where Type : IBase

View File

@ -74,7 +74,7 @@ namespace Acacia.Stubs.OutlookWrappers
#region IBase implementation
public string EntryId { get { return _item.EntryID; } }
public string EntryID { get { return _item.EntryID; } }
public IFolder Parent
{
@ -85,7 +85,7 @@ namespace Acacia.Stubs.OutlookWrappers
}
}
public string ParentEntryId
public string ParentEntryID
{
get
{
@ -104,7 +104,7 @@ namespace Acacia.Stubs.OutlookWrappers
using (ComRelease com = new ComRelease())
{
NSOutlook.Folder parent = com.Add(_item.Parent);
return StoreWrapper.Wrap(parent?.Store);
return Mapping.Wrap(parent?.Store);
}
}
}

View File

@ -72,7 +72,7 @@ namespace Acacia.Stubs.OutlookWrappers
#region IBase implementation
public string EntryId { get { return _item.EntryID; } }
public string EntryID { get { return _item.EntryID; } }
public IFolder Parent
{
@ -83,7 +83,7 @@ namespace Acacia.Stubs.OutlookWrappers
}
}
public string ParentEntryId
public string ParentEntryID
{
get
{
@ -102,7 +102,7 @@ namespace Acacia.Stubs.OutlookWrappers
using (ComRelease com = new ComRelease())
{
NSOutlook.Folder parent = com.Add(_item.Parent);
return StoreWrapper.Wrap(parent?.Store);
return Mapping.Wrap(parent?.Store);
}
}
}

View File

@ -26,12 +26,7 @@ namespace Acacia.Stubs.OutlookWrappers
{
class StoreWrapper : ComWrapper<NSOutlook.Store>, IStore
{
internal static IStore Wrap(NSOutlook.Store store)
{
return store == null ? null : new StoreWrapper(store);
}
private StoreWrapper(NSOutlook.Store store) : base(store)
internal StoreWrapper(NSOutlook.Store store) : base(store)
{
}
@ -41,6 +36,25 @@ namespace Acacia.Stubs.OutlookWrappers
return new FolderWrapper((NSOutlook.Folder)_item.GetRootFolder());
}
public IFolder GetDefaultFolder(DefaultFolder folder)
{
// FolderWrapper manages the returned Folder
return new FolderWrapper((NSOutlook.Folder)_item.GetDefaultFolder((NSOutlook.OlDefaultFolders)(int)folder));
}
public string GetDefaultFolderId(DefaultFolder folder)
{
NSOutlook.MAPIFolder mapiFolder = _item.GetDefaultFolder((NSOutlook.OlDefaultFolders)(int)folder);
try
{
return mapiFolder.EntryID;
}
finally
{
ComRelease.Release(mapiFolder);
}
}
public IItem GetItemFromID(string id)
{
using (ComRelease com = new ComRelease())
@ -79,6 +93,5 @@ namespace Acacia.Stubs.OutlookWrappers
}
}
}
}
}

View File

@ -72,7 +72,7 @@ namespace Acacia.Stubs.OutlookWrappers
#region IBase implementation
public string EntryId { get { return _item.EntryID; } }
public string EntryID { get { return _item.EntryID; } }
public IFolder Parent
{
@ -83,7 +83,7 @@ namespace Acacia.Stubs.OutlookWrappers
}
}
public string ParentEntryId
public string ParentEntryID
{
get
{
@ -102,7 +102,7 @@ namespace Acacia.Stubs.OutlookWrappers
using (ComRelease com = new ComRelease())
{
NSOutlook.Folder parent = com.Add(_item.Parent);
return StoreWrapper.Wrap(parent?.Store);
return Mapping.Wrap(parent?.Store);
}
}
}

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NSOutlook = Microsoft.Office.Interop.Outlook;
namespace Acacia.Stubs
{
public static class Wrappers
{
public static IFolder Wrap(this NSOutlook.MAPIFolder obj)
{
throw new NotImplementedException(); // TODO
}
}
}

View File

@ -0,0 +1,47 @@
using Acacia.Features.DebugSupport;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Acacia.Utils
{
abstract public class DisposableWrapper : IDisposable
{
protected DisposableWrapper()
{
Interlocked.Increment(ref Statistics.CreatedWrappers);
this._createdTrace = new System.Diagnostics.StackTrace();
}
~DisposableWrapper()
{
Interlocked.Increment(ref Statistics.DeletedWrappers);
if (!_isDisposed)
{
Logger.Instance.Warning(this, "Undisposed wrapper: {0}", _createdTrace);
// Dispose, but don't count auto disposals, so the stats show it.
DoRelease();
}
}
private bool _isDisposed;
private readonly System.Diagnostics.StackTrace _createdTrace;
virtual public void Dispose()
{
if (!_isDisposed)
{
Logger.Instance.TraceExtra(this, "Disposing wrapper: {0}", new System.Diagnostics.StackTrace());
_isDisposed = true;
Interlocked.Increment(ref Statistics.DisposedWrappers);
DoRelease();
}
}
abstract protected void DoRelease();
}
}

View File

@ -252,7 +252,7 @@ namespace Acacia.ZPush.Connect
}
}
private class Request : RawComWrapper
private class Request : DisposableWrapper
{
private const string ACTIVESYNC_URL = "https://{0}/Microsoft-Server-ActiveSync?DeviceId={1}&Cmd={2}&User={3}&DeviceType={4}";

View File

@ -15,6 +15,7 @@
/// Consult LICENSE file for details
using Acacia.Stubs;
using Acacia.Stubs.OutlookWrappers;
using Acacia.Utils;
using Acacia.ZPush.Connect;
using Microsoft.Win32;
@ -32,21 +33,20 @@ using NSOutlook = Microsoft.Office.Interop.Outlook;
namespace Acacia.ZPush
{
[TypeConverter(typeof(ExpandableObjectConverter))]
public class ZPushAccount : LogContext
public class ZPushAccount : DisposableWrapper, LogContext
{
#region Miscellaneous
private readonly string _regPath;
// TODO: this should probably be wrapped. Make ZPushAccount ComWrapper?
private readonly NSOutlook.Store _store;
private readonly IStore _store;
/// <summary>
/// Constructor.
/// </summary>
/// <param name="regPath">They registry key containing the account settings.</param>
/// <param name="store">The store this account represents.</param>
internal ZPushAccount(string regPath, NSOutlook.Store store)
/// <param name="store">The store this account represents. The new object takes ownership</param>
internal ZPushAccount(string regPath, IStore store)
{
this._regPath = regPath;
this._store = store;
@ -55,6 +55,11 @@ namespace Acacia.ZPush
SmtpAddress = RegistryUtil.GetValueString(_regPath, OutlookConstants.REG_VAL_EMAIL, null);
}
protected override void DoRelease()
{
_store.Dispose();
}
[Browsable(false)]
public string LogContextId
{
@ -83,8 +88,7 @@ namespace Acacia.ZPush
#region Properties
[Browsable(false)]
// TODO: remove this
public NSOutlook.Store Store
public IStore Store
{
get
{
@ -229,7 +233,7 @@ namespace Acacia.ZPush
public void LinkedGABFolder(IFolder folder)
{
GABFolderLinked = folder.EntryId;
GABFolderLinked = folder.EntryID;
}
internal void OnConfirmationResponse(ZPushConnection.Response response)

View File

@ -15,6 +15,7 @@
/// Consult LICENSE file for details
using Acacia.Stubs;
using Acacia.Stubs.OutlookWrappers;
using Acacia.Utils;
using Microsoft.Win32;
using System;
@ -238,36 +239,41 @@ namespace Acacia.ZPush
/// </summary>
private void StoreAdded(NSOutlook.Store s)
{
IStore store = null;
try
{
using (ComRelease com = new ComRelease())
// Accessing the store object causes random crashes, simply iterate to find new stores
Logger.Instance.Trace(this, "StoreAdded: {0}", s.StoreID);
foreach (NSOutlook.Store rawStore in _session.Stores.RawEnum(false))
{
Logger.Instance.Trace(this, "StoreAdded: {0}", s.StoreID);
foreach (NSOutlook.Store store in com.Add(com.Add(_app.Session).Stores))
if (!_accountsByStoreId.ContainsKey(rawStore.StoreID))
{
if (!_accountsByStoreId.ContainsKey(store.StoreID))
Logger.Instance.Trace(this, "New store: {0}", rawStore.DisplayName);
store = Mapping.Wrap(rawStore);
ZPushAccount zpush = TryCreateFromRegistry(store);
if (zpush == null)
{
Logger.Instance.Trace(this, "New store: {0}", store.DisplayName);
ZPushAccount zpush = TryCreateFromRegistry(store);
if (zpush == null)
{
// Add it to the cache so it is not evaluated again.
_accountsByStoreId.Add(store.StoreID, null);
Logger.Instance.Trace(this, "Not a ZPush store: {0}", store.DisplayName);
}
else
{
Logger.Instance.Trace(this, "New ZPush store: {0}: {1}", store.DisplayName, zpush);
_watcher.OnAccountDiscovered(zpush, false);
}
// Add it to the cache so it is not evaluated again.
_accountsByStoreId.Add(store.StoreID, null);
Logger.Instance.Trace(this, "Not a ZPush store: {0}", store.DisplayName);
store.Dispose();
}
else
{
Logger.Instance.Trace(this, "New ZPush store: {0}: {1}", store.DisplayName, zpush);
_watcher.OnAccountDiscovered(zpush, false);
// zpush has taken ownership
}
else ComRelease.Release(store);
}
else ComRelease.Release(rawStore);
}
}
catch(System.Exception e)
{
Logger.Instance.Error(this, "StoreAdded Exception: {0}", e);
if (store != null)
store.Dispose();
}
}
@ -287,6 +293,7 @@ namespace Acacia.ZPush
/// <param name="account">The account. The caller is responsible for releasing this.</param>
/// <returns>The associated ZPushAccount</returns>
/// <exception cref="Exception">If the registry key cannot be found</exception>
// TODO: check management of account
private ZPushAccount CreateFromRegistry(NSOutlook.Account account)
{
// TODO: check that caller releases account everywhere
@ -300,10 +307,10 @@ namespace Acacia.ZPush
string storeId = ZPushAccount.GetStoreId(baseKey.Name);
// Find the store
NSOutlook.Store store = _app.Session.GetStoreFromID(storeId);
NSOutlook.Store store = _session.GetStoreFromID(storeId);
// Done, create and register
ZPushAccount zpush = new ZPushAccount(baseKey.Name, store);
ZPushAccount zpush = new ZPushAccount(baseKey.Name, Mapping.Wrap(store));
Register(zpush);
return zpush;
}
@ -312,9 +319,9 @@ namespace Acacia.ZPush
/// <summary>
/// Creates the ZPushAccount for the store, from the registry.
/// </summary>
/// <param name="store">The store</param>
/// <param name="store">The store. Ownership is transferred to the ZPushAccount. If the account is not created, the store is NOT disposed</param>
/// <returns>The ZPushAccount, or null if no account is associated with the store</returns>
private ZPushAccount TryCreateFromRegistry(NSOutlook.Store store)
private ZPushAccount TryCreateFromRegistry(IStore store)
{
using (RegistryKey baseKey = FindRegistryKey(store))
{
@ -363,7 +370,7 @@ namespace Acacia.ZPush
/// Finds the registry key for the account associated with the store.
/// </summary>
/// <returns>The registry key, or null if it cannot be found</returns>
private RegistryKey FindRegistryKey(NSOutlook.Store store)
private RegistryKey FindRegistryKey(IStore store)
{
// Find the registry key by store id
using (RegistryKey key = OpenBaseKey())

View File

@ -22,15 +22,12 @@ using System.Linq;
using Acacia.Utils;
using System.Text;
using System.Threading.Tasks;
using NSOutlook = Microsoft.Office.Interop.Outlook;
namespace Acacia.ZPush
{
// TODO: make this contain Folder instead of inheriting, then FolderWrapper needn't be public
public class ZPushFolder : FolderWrapper
public class ZPushFolder : DisposableWrapper
{
private readonly NSOutlook.Items _items;
private readonly NSOutlook.Folders _subFolders;
private IFolder _folder;
private ZPushFolder _parent;
private readonly ZPushWatcher _watcher;
private List<ItemsWatcher> _itemsWatchers = new List<ItemsWatcher>();
@ -40,24 +37,29 @@ namespace Acacia.ZPush
/// </summary>
protected readonly Dictionary<string, ZPushFolder> _children = new Dictionary<string, ZPushFolder>();
internal ZPushFolder(ZPushWatcher watcher, NSOutlook.Folder folder)
internal ZPushFolder(ZPushWatcher watcher, IFolder folder)
:
this(watcher, null, folder)
{
Initialise();
}
private ZPushFolder(ZPushWatcher watcher, ZPushFolder parent, NSOutlook.Folder folder)
:
base(folder)
private ZPushFolder(ZPushWatcher watcher, ZPushFolder parent, IFolder folder)
{
Logger.Instance.Trace(this, "Watching folder: {1}: {0}", folder.EntryID, folder.Name);
this._parent = parent;
this._watcher = watcher;
this._items = folder.Items;
this._subFolders = folder.Folders;
this._folder = folder;
}
protected override void DoRelease()
{
_folder.Dispose();
}
public IFolder Folder { get; }
public string Name { get { return _folder.Name; } }
private void Initialise()
{
// Register the events
@ -67,20 +69,21 @@ namespace Acacia.ZPush
_watcher.OnFolderDiscovered(this);
// Recurse the children
foreach (NSOutlook.Folder subfolder in this._subFolders)
foreach (IFolder subfolder in _folder.GetSubFolders())
{
Tasks.Task(null, "WatchChild", () => WatchChild(subfolder));
}
}
public override void Dispose()
// TODO
/*public override void Dispose()
{
Logger.Instance.Trace(this, "Disposing folder: {0}", _item.Name);
Cleanup();
base.Dispose();
ComRelease.Release(_items);
ComRelease.Release(_subFolders);
}
}*/
internal ItemsWatcher ItemsWatcher()
{
@ -92,7 +95,7 @@ namespace Acacia.ZPush
public void ReportExistingItems<TypedItem>(TypedItemEventHandler<TypedItem> handler)
where TypedItem : IItem
{
foreach(IItem item in Items)
foreach(IItem item in _folder.Items)
{
if (item is TypedItem)
handler((TypedItem)item);
@ -101,6 +104,8 @@ namespace Acacia.ZPush
private void HookEvents(bool register)
{
// TODO
/*
if (register)
{
// Item events
@ -122,12 +127,12 @@ namespace Acacia.ZPush
_subFolders.FolderAdd -= SubFolders_FolderAdd;
_subFolders.FolderRemove -= SubFolders_FolderRemove;
_subFolders.FolderChange -= SubFolders_FolderChange;
}
}*/
}
private void Cleanup()
{
Logger.Instance.Trace(this, "Unwatching folder: {0}", _item.Name);
Logger.Instance.Trace(this, "Unwatching folder: {0}", _folder.Name);
// The events need to be unhooked explicitly, otherwise we get double notifications if a folder is moved
HookEvents(false);
foreach (ZPushFolder child in _children.Values)
@ -141,7 +146,7 @@ namespace Acacia.ZPush
/// Watches the child folder.
/// </summary>
/// <param name="child">The child folder. Ownership will be taken.</param>
private void WatchChild(NSOutlook.Folder child)
private void WatchChild(IFolder child)
{
if (!_children.ContainsKey(child.EntryID))
{
@ -165,124 +170,5 @@ namespace Acacia.ZPush
// Release the folder if not used
ComRelease.Release(child);
}
#region Event handlers
private void SubFolders_FolderAdd(NSOutlook.MAPIFolder folder)
{
try
{
Logger.Instance.Debug(this, "Folder added in {0}: {1}", this._item.Name, folder.Name);
WatchChild((NSOutlook.Folder)folder);
}
catch (System.Exception e) { Logger.Instance.Error(this, "Exception in SubFolders_FolderAdd: {0}: {1}", Name, e); }
}
private void SubFolders_FolderRemove()
{
try
{
Logger.Instance.Debug(this, "Folder removed from {0}", this._item.Name);
// Helpfully, Outlook doesn't tell us which folder was removed. Could use the BeforeFolderMove event instead,
// but that doesn't fire if a folder was removed on the server.
// Hence, fetch all the remaining folder ids, and remove any folder that no longer exists.
HashSet<string> remaining = new HashSet<string>();
foreach (NSOutlook.Folder child in _subFolders)
{
try
{
remaining.Add(child.EntryID);
}
catch (System.Exception e) { Logger.Instance.Warning(this, "Ignoring failed child: {0}", e); }
}
// Find the folders that need to be removed. There should be only one, but with Outlook we can never be sure,
// so compare all. We cannot modify the dictionary during iteration, so store entries to be removed in a
// temporary list
List<KeyValuePair<string, ZPushFolder>> remove = new List<KeyValuePair<string, ZPushFolder>>();
foreach (var entry in _children)
{
if (!remaining.Contains(entry.Key))
{
remove.Add(entry);
}
}
// Actually remove the folders
foreach (var entry in remove)
{
Logger.Instance.Debug(this, "Removing subfolder {0}, {1}", this._item.Name, entry.Key);
_children.Remove(entry.Key);
entry.Value.Cleanup();
}
}
catch (System.Exception e) { Logger.Instance.Error(this, "Exception in SubFolders_FolderRemove: {0}: {1}", Name, e); }
}
private void SubFolders_FolderChange(NSOutlook.MAPIFolder folder)
{
try
{
Logger.Instance.Debug(this, "Folder changed in {0}: {1}", this._item.Name, folder.Name);
ZPushFolder child;
if (_children.TryGetValue(folder.EntryID, out child))
{
_watcher.OnFolderChanged(child);
// TODO: release folder?
}
else
{
// On a clean profile, we sometimes get a change notification, but not an add notification
// Create it now
// This will send a discover notification if required, which is just as good as a change notification
Logger.Instance.Debug(this, "Folder change on unreported folder in {0}: {1}, {2}, {3}", this._item.Name, folder.Name, folder.EntryID, folder.Store.DisplayName);
WatchChild((NSOutlook.Folder)folder);
}
}
catch (System.Exception e) { Logger.Instance.Error(this, "Exception in SubFolders_FolderChange: {0}: {1}", Name, e); }
}
private void Items_ItemAdd(object oItem)
{
try
{
using (IItem item = Mapping.Wrap<IItem>(oItem))
{
if (item != null)
{
Logger.Instance.Trace(this, "New item {0}: {1}", Name, item.EntryId);
foreach (ItemsWatcher watcher in _itemsWatchers)
watcher.OnItemAdd(this, item);
}
}
}
catch(System.Exception e)
{
Logger.Instance.Trace(this, "ItemAdd exception: {0}: {1}", Name, e);
}
}
private void Items_ItemChange(object oItem)
{
try
{
using (IItem item = Mapping.Wrap<IItem>(oItem))
{
if (item != null)
{
Logger.Instance.Trace(this, "Changed item {0}", Name);
foreach (ItemsWatcher watcher in _itemsWatchers)
watcher.OnItemChange(this, item);
}
}
}
catch (System.Exception e)
{
Logger.Instance.Trace(this, "ItemChange exception: {0}: {1}", Name, e);
}
}
#endregion
}
}

View File

@ -37,6 +37,7 @@ namespace Acacia.ZPush
/// </summary>
public class ZPushWatcher
{
// TODO: remove
private readonly NSOutlook.Application _app;
public readonly ZPushAccounts Accounts;
public readonly ZPushSync Sync;
@ -277,7 +278,7 @@ namespace Acacia.ZPush
private void HandleFolderWatchers(ZPushAccount account)
{
// We need to keep the object alive to keep receiving events
_rootFolder = new ZPushFolder(this, (NSOutlook.Folder)account.Store.GetRootFolder());
_rootFolder = new ZPushFolder(this, account.Store.GetRootFolder());
}
public void WatchFolder(FolderRegistration folder, FolderEventHandler handler, FolderEventHandler changedHandler = null)
@ -299,7 +300,7 @@ namespace Acacia.ZPush
// Check existing folders for events
foreach(ZPushFolder existing in _allFolders)
{
if (folder.IsApplicable(existing))
if (folder.IsApplicable(existing.Folder))
{
DispatchFolderEvent(folder, watcher, existing, true);
}
@ -326,7 +327,7 @@ namespace Acacia.ZPush
// See if anybody is interested
foreach (KeyValuePair<FolderRegistration, FolderWatcher> entry in _folderWatchers)
{
if (entry.Key.IsApplicable(folder))
if (entry.Key.IsApplicable(folder.Folder))
{
DispatchFolderEvent(entry.Key, entry.Value, folder, isNew);
}
@ -337,17 +338,17 @@ namespace Acacia.ZPush
{
Logger.Instance.Debug(this, "Folder event: {0}, {1}, {2}", folder, reg, isNew);
if (isNew)
watcher.OnDiscovered(folder);
watcher.OnDiscovered(folder.Folder);
else
watcher.OnChanged(folder);
watcher.OnChanged(folder.Folder);
}
internal bool ShouldFolderBeWatched(ZPushFolder parent, NSOutlook.Folder child)
internal bool ShouldFolderBeWatched(ZPushFolder parent, IFolder child)
{
if (parent.IsAtDepth(0))
if (parent.Folder.IsAtDepth(0))
{
// Special mail folders cause issues, they are disallowed
if (child.DefaultItemType != NSOutlook.OlItemType.olMailItem)
if (child.DefaultItemType != ItemType.MailItem)
return true;
return !IsBlackListedMailFolder(child);
@ -355,30 +356,27 @@ namespace Acacia.ZPush
return true;
}
private static readonly NSOutlook.OlDefaultFolders[] BLACKLISTED_MAIL_FOLDERS =
private static readonly DefaultFolder[] BLACKLISTED_MAIL_FOLDERS =
{
NSOutlook.OlDefaultFolders.olFolderOutbox,
NSOutlook.OlDefaultFolders.olFolderDrafts,
NSOutlook.OlDefaultFolders.olFolderConflicts,
NSOutlook.OlDefaultFolders.olFolderSyncIssues,
NSOutlook.OlDefaultFolders.olFolderRssFeeds,
NSOutlook.OlDefaultFolders.olFolderManagedEmail
DefaultFolder.Outbox,
DefaultFolder.Drafts,
DefaultFolder.Conflicts,
DefaultFolder.SyncIssues,
DefaultFolder.RssFeeds,
DefaultFolder.ManagedEmail
};
private static bool IsBlackListedMailFolder(NSOutlook.Folder folder)
private static bool IsBlackListedMailFolder(IFolder folder)
{
string entryId = folder.EntryID;
using (ComRelease com = new ComRelease())
{
NSOutlook.Store store = com.Add(folder.Store);
foreach(NSOutlook.OlDefaultFolders defaultFolder in BLACKLISTED_MAIL_FOLDERS)
using (IStore store = folder.Store)
{
foreach(DefaultFolder defaultFolderId in BLACKLISTED_MAIL_FOLDERS)
{
try
{
if (entryId == com.Add(store.GetDefaultFolder(defaultFolder)).EntryID)
return true;
}
catch (System.Exception) { }
if (entryId == store.GetDefaultFolderId(defaultFolderId))
return true;
}
}
return false;