diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj index 0092d93..8b97a68 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj @@ -286,7 +286,9 @@ + + @@ -296,7 +298,9 @@ + + diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/GAB/GABHandler.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/GAB/GABHandler.cs index bb3ad52..6fa50f5 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/GAB/GABHandler.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/GAB/GABHandler.cs @@ -336,7 +336,7 @@ namespace Acacia.Features.GAB // Scan a few of the newest items, in case there is some junk in the ZPush folder // TODO: this shouldn't happen in production. int i = 0; - foreach(IItem item in Folder.ItemsSorted("LastModificationTime", true)) + foreach(IItem item in Folder.Items.Sort("LastModificationTime", true)) { using (item) { diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IFolder.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IFolder.cs index 54cdbd5..328df5b 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IFolder.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IFolder.cs @@ -35,9 +35,7 @@ namespace Acacia.Stubs bool ShowAsOutlookAB { get; set; } - IEnumerable Items { get; } - - IEnumerable ItemsSorted(string field, bool descending); + IItems Items { get; } IItem GetItemById(string id); @@ -58,7 +56,11 @@ namespace Acacia.Stubs IEnumerable GetSubFolders() where FolderType : IFolder; - IEnumerable GetSubFolders(); + + IFolders SubFolders + { + get; + } FolderType GetSubFolder(string name) where FolderType : IFolder; diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IFolders.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IFolders.cs new file mode 100644 index 0000000..27fb82b --- /dev/null +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IFolders.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Acacia.Stubs +{ + public delegate void IFolders_FolderEventHandler(IFolder folder); + public delegate void IFolders_EventHandler(); + + public interface IFolders_Events : IDisposable + { + event IFolders_FolderEventHandler FolderAdd; + event IFolders_FolderEventHandler FolderChange; + event IFolders_EventHandler FolderRemove; + } + + public interface IFolders : IEnumerable + { + #region Events + + + /// + /// Returns an events subscribption object. + /// + /// The events. The caller is responsible for disposing + IFolders_Events GetEvents(); + + #endregion + } +} diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IItems.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IItems.cs new file mode 100644 index 0000000..9773f34 --- /dev/null +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IItems.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Acacia.Stubs +{ + public delegate void IItems_ItemEventHandler(IItem item); + + + public interface IItems_Events : IDisposable + { + event IItems_ItemEventHandler ItemAdd; + event IItems_ItemEventHandler ItemChange; + } + + public interface IItems : IEnumerable + { + /// + /// Sorts the items. + /// + /// + /// + /// The current collection, which will be sorted + IItems Sort(string field, bool descending); + + /// + /// Returns an events subscribption object. + /// + /// The events. The caller is responsible for disposing + IItems_Events GetEvents(); + } +} diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/ComWrapper.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/ComWrapper.cs index 9aac7ef..b8d4930 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/ComWrapper.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/ComWrapper.cs @@ -27,7 +27,7 @@ namespace Acacia.Stubs.OutlookWrappers { abstract class ComWrapper : DisposableWrapper, IComWrapper { - protected ItemType _item { get; private set; } + protected readonly ItemType _item; /// /// Creates a wrapper. @@ -49,7 +49,6 @@ namespace Acacia.Stubs.OutlookWrappers if (MustRelease) { ComRelease.Release(_item); - _item = default(ItemType); } } diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/FolderWrapper.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/FolderWrapper.cs index f0bf96b..a8c934f 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/FolderWrapper.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/FolderWrapper.cs @@ -34,6 +34,13 @@ namespace Acacia.Stubs.OutlookWrappers { } + protected override void DoRelease() + { + base.DoRelease(); + } + + internal NSOutlook.Folder RawItem { get { return _item; } } + protected override NSOutlook.PropertyAccessor GetPropertyAccessor() { return _item.PropertyAccessor; @@ -125,118 +132,15 @@ namespace Acacia.Stubs.OutlookWrappers public ItemType ItemType { get { return (ItemType)(int)_item.DefaultItemType; } } - #region Enumeration - public class ItemsEnumerator : ComWrapper, IEnumerator - where ItemType : IItem - { - private IEnumerator _enum; - private ItemType _last; - - public ItemsEnumerator(NSOutlook.Folder folder, string field, bool descending) : base(folder.Items) - { - // TODO: can _items be released here already? - if (field != null) - { - this._item.Sort("[" + field + "]", descending); - } - this._enum = _item.GetEnumerator(); - } - - protected override void DoRelease() - { - CleanLast(); - if (_enum != null) - { - if (_enum is IDisposable) - ((IDisposable)_enum).Dispose(); - ComRelease.Release(_enum); - _enum = null; - } - base.DoRelease(); - } - - public ItemType Current - { - get - { - CleanLast(); - _last = Mapping.Wrap(_enum.Current); - return _last; - } - } - - object IEnumerator.Current - { - get - { - return Current; - } - } - - private void CleanLast() - { - if (_last != null) - { - _last.Dispose(); - _last = default(ItemType); - } - } - - public bool MoveNext() - { - CleanLast(); - return _enum.MoveNext(); - } - - public void Reset() - { - CleanLast(); - _enum.Reset(); - } - } - - public class ItemsEnumerable : IEnumerable - where ItemType : IItem - { - // Managed by the caller, not released here - private readonly NSOutlook.Folder _folder; - private readonly string _field; - private readonly bool _descending; - - public ItemsEnumerable(NSOutlook.Folder folder, string field, bool descending) - { - this._folder = folder; - this._field = field; - this._descending = descending; - } - - public IEnumerator GetEnumerator() - { - return new ItemsEnumerator(_folder, _field, _descending); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } - - public IEnumerable Items + public IItems Items { get { - return new ItemsEnumerable(_item, null, false); + return new ItemsWrapper(this); } } - public IEnumerable ItemsSorted(string field, bool descending) - { - return new ItemsEnumerable(_item, field, descending); - } - - #endregion - public IItem GetItemById(string entryId) { try @@ -289,13 +193,16 @@ namespace Acacia.Stubs.OutlookWrappers // Don't release the items, the wrapper manages them foreach (NSOutlook.Folder folder in _item.Folders.RawEnum(false)) { - yield return WrapFolder(folder); + yield return folder.Wrap(); }; } - public IEnumerable GetSubFolders() + public IFolders SubFolders { - return GetSubFolders(); + get + { + return new FoldersWrapper(this); + } } public FolderType GetSubFolder(string name) @@ -319,7 +226,7 @@ namespace Acacia.Stubs.OutlookWrappers } if (sub == null) return default(FolderType); - return WrapFolder(sub); + return sub.Wrap(); } public FolderType CreateFolder(string name) @@ -330,37 +237,19 @@ namespace Acacia.Stubs.OutlookWrappers NSOutlook.Folders folders = com.Add(_item.Folders); if (typeof(FolderType) == typeof(IFolder)) { - return WrapFolder(folders.Add(name)); + return folders.Add(name).Wrap(); } else if (typeof(FolderType) == typeof(IAddressBook)) { NSOutlook.MAPIFolder newFolder = folders.Add(name, NSOutlook.OlDefaultFolders.olFolderContacts); newFolder.ShowAsOutlookAB = true; - return WrapFolder(newFolder); + return newFolder.Wrap(); } else throw new NotSupportedException(); } } - private FolderType WrapFolder(NSOutlook.MAPIFolder folder) - where FolderType : IFolder - { - if (typeof(FolderType) == typeof(IFolder)) - { - return (FolderType)(IFolder)new FolderWrapper(folder); - } - else if (typeof(FolderType) == typeof(IAddressBook)) - { - return (FolderType)(IFolder)new AddressBookWrapper(folder); - } - else - { - ComRelease.Release(folder); - throw new NotSupportedException(); - } - } - #endregion public IStorageItem GetStorageItem(string name) diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/FoldersWrapper.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/FoldersWrapper.cs new file mode 100644 index 0000000..cfe3031 --- /dev/null +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/FoldersWrapper.cs @@ -0,0 +1,199 @@ +using Acacia.Utils; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using NSOutlook = Microsoft.Office.Interop.Outlook; + +namespace Acacia.Stubs.OutlookWrappers +{ + class FoldersWrapper : IFolders + { + // Managed by the caller, not released here + private readonly FolderWrapper _folder; + + public FoldersWrapper(FolderWrapper folder) + { + this._folder = folder; + } + + public IEnumerator GetEnumerator() + { + // Don't release the items, the wrapper manages them + foreach (NSOutlook.Folder folder in _folder.RawItem.Folders.RawEnum(false)) + { + yield return folder.Wrap(); + }; + } + + IEnumerator IEnumerable.GetEnumerator() + { + // Don't release the items, the wrapper manages them + foreach (NSOutlook.Folder folder in _folder.RawItem.Folders.RawEnum(false)) + { + yield return folder.Wrap(); + }; + } + + #region Events + + private class EventsWrapper : ComWrapper, IFolders_Events + { + public EventsWrapper(NSOutlook.Folders item) : base(item) + { + } + + #region FolderAdd + + private IFolders_FolderEventHandler _folderAdd; + public event IFolders_FolderEventHandler FolderAdd + { + add + { + if (_folderAdd == null) + HookFolderAdd(true); + _folderAdd += value; + } + remove + { + _folderAdd -= value; + if (_folderAdd == null) + HookFolderAdd(false); + } + } + + private void HookFolderAdd(bool hook) + { + if (hook) + _item.FolderAdd += HandleFolderAdd; + else + _item.FolderAdd -= HandleFolderAdd; + } + + private void HandleFolderAdd(NSOutlook.MAPIFolder folder) + { + try + { + if (_folderAdd != null) + { + using (IFolder folderWrapped = Mapping.Wrap(folder, false)) + { + if (folderWrapped != null) + { + _folderAdd(folderWrapped); + } + } + } + } + catch (System.Exception e) + { + Logger.Instance.Error(this, "Exception in HandleFolderAdd: {0}", e); + } + } + + #endregion + + #region FolderChange + + private IFolders_FolderEventHandler _folderChange; + public event IFolders_FolderEventHandler FolderChange + { + add + { + if (_folderChange == null) + HookFolderChange(true); + _folderChange += value; + } + remove + { + _folderChange -= value; + if (_folderChange == null) + HookFolderChange(false); + } + } + + private void HookFolderChange(bool hook) + { + if (hook) + _item.FolderChange += HandleFolderChange; + else + _item.FolderChange -= HandleFolderChange; + } + + private void HandleFolderChange(NSOutlook.MAPIFolder folder) + { + try + { + if (_folderChange != null) + { + using (IFolder folderWrapped = Mapping.Wrap(folder, false)) + { + if (folderWrapped != null) + { + _folderChange(folderWrapped); + } + } + } + } + catch (System.Exception e) + { + Logger.Instance.Error(this, "Exception in HandleFolderChange: {0}", e); + } + } + + #endregion + + #region FolderRemove + + private IFolders_EventHandler _folderRemove; + public event IFolders_EventHandler FolderRemove + { + add + { + if (_folderRemove == null) + HookFolderRemove(true); + _folderRemove += value; + } + remove + { + _folderRemove -= value; + if (_folderRemove == null) + HookFolderRemove(false); + } + } + + private void HookFolderRemove(bool hook) + { + if (hook) + _item.FolderRemove += HandleFolderRemove; + else + _item.FolderRemove -= HandleFolderRemove; + } + + private void HandleFolderRemove() + { + try + { + if (_folderRemove != null) + { + _folderRemove(); + } + } + catch (System.Exception e) + { + Logger.Instance.Error(this, "Exception in HandleFolderRemove: {0}", e); + } + } + + #endregion + } + + public IFolders_Events GetEvents() + { + return new EventsWrapper(_folder.RawItem.Folders); + } + #endregion + } +} diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/ItemsWrapper.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/ItemsWrapper.cs new file mode 100644 index 0000000..c088e84 --- /dev/null +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/OutlookWrappers/ItemsWrapper.cs @@ -0,0 +1,235 @@ +using Acacia.Utils; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using NSOutlook = Microsoft.Office.Interop.Outlook; + +namespace Acacia.Stubs.OutlookWrappers +{ + class ItemsWrapper : IItems + { + // Managed by the caller, not released here + private readonly FolderWrapper _folder; + private string _field; + private bool _descending; + + public ItemsWrapper(FolderWrapper folder) + { + this._folder = folder; + } + + public IItems Sort(string field, bool descending) + { + this._field = field; + this._descending = descending; + return this; + } + + private NSOutlook.Items GetItems() + { + return _folder.RawItem.Items; + } + + public IEnumerator GetEnumerator() + { + return new ItemsEnumerator(this, _field, _descending); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #region Enumeration + + public class ItemsEnumerator : ComWrapper, IEnumerator + where ItemType : IItem + { + private IEnumerator _enum; + private ItemType _last; + + public ItemsEnumerator(ItemsWrapper items, string field, bool descending) : base(items.GetItems()) + { + // TODO: can _items be released here already? + if (field != null) + { + this._item.Sort("[" + field + "]", descending); + } + this._enum = _item.GetEnumerator(); + } + + protected override void DoRelease() + { + CleanLast(); + if (_enum != null) + { + if (_enum is IDisposable) + ((IDisposable)_enum).Dispose(); + ComRelease.Release(_enum); + _enum = null; + } + base.DoRelease(); + } + + public ItemType Current + { + get + { + CleanLast(); + _last = Mapping.Wrap(_enum.Current); + return _last; + } + } + + object IEnumerator.Current + { + get + { + return Current; + } + } + + private void CleanLast() + { + if (_last != null) + { + _last.Dispose(); + _last = default(ItemType); + } + } + + public bool MoveNext() + { + CleanLast(); + return _enum.MoveNext(); + } + + public void Reset() + { + CleanLast(); + _enum.Reset(); + } + } + + #endregion + + #region Events + + private class EventsWrapper : ComWrapper, IItems_Events + { + public EventsWrapper(NSOutlook.Items item) : base(item) + { + } + + #region ItemAdd + + private IItems_ItemEventHandler _itemAdd; + public event IItems_ItemEventHandler ItemAdd + { + add + { + if (_itemAdd == null) + HookItemAdd(true); + _itemAdd += value; + } + remove + { + _itemAdd -= value; + if (_itemAdd == null) + HookItemAdd(false); + } + } + + private void HookItemAdd(bool hook) + { + if (hook) + _item.ItemAdd += HandleItemAdd; + else + _item.ItemAdd -= HandleItemAdd; + } + + private void HandleItemAdd(object objItem) + { + try + { + if (_itemAdd != null) + { + using (IItem item = Mapping.Wrap(objItem, false)) + { + if (item != null) + { + _itemAdd(item); + } + } + } + } + catch (System.Exception e) + { + Logger.Instance.Error(this, "Exception in HandleItemAdd: {0}", e); + } + } + + #endregion + + #region ItemChange + + private IItems_ItemEventHandler _itemChange; + public event IItems_ItemEventHandler ItemChange + { + add + { + if (_itemChange == null) + HookItemChange(true); + _itemChange += value; + } + remove + { + _itemChange -= value; + if (_itemChange == null) + HookItemChange(false); + } + } + + private void HookItemChange(bool hook) + { + if (hook) + _item.ItemChange += HandleItemChange; + else + _item.ItemChange -= HandleItemChange; + } + + private void HandleItemChange(object objItem) + { + try + { + if (_itemChange != null) + { + using (IItem item = Mapping.Wrap(objItem, false)) + { + if (item != null) + { + _itemChange(item); + } + } + } + } + catch (System.Exception e) + { + Logger.Instance.Error(this, "Exception in HandleItemChange: {0}", e); + } + } + + #endregion + } + + public IItems_Events GetEvents() + { + return new EventsWrapper(GetItems()); + } + + #endregion + } +} diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/Wrappers.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/Wrappers.cs index a30c9a7..d163089 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/Wrappers.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/Wrappers.cs @@ -1,4 +1,5 @@ using Acacia.Stubs.OutlookWrappers; +using Acacia.Utils; using System; using System.Collections.Generic; using System.Linq; @@ -15,6 +16,25 @@ namespace Acacia.Stubs return Mapping.WrapOrDefault(obj); } + + public static FolderType Wrap(this NSOutlook.MAPIFolder folder) + where FolderType : IFolder + { + if (typeof(FolderType) == typeof(IFolder)) + { + return (FolderType)(IFolder)new FolderWrapper(folder); + } + else if (typeof(FolderType) == typeof(IAddressBook)) + { + return (FolderType)(IFolder)new AddressBookWrapper(folder); + } + else + { + ComRelease.Release(folder); + throw new NotSupportedException(); + } + } + public static WrapType Wrap(this object o, bool mustRelease = true) where WrapType : IBase { diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Utils/Util.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Utils/Util.cs index dbef980..62453e7 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Utils/Util.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Utils/Util.cs @@ -62,7 +62,6 @@ namespace Acacia.Utils ComRelease.Release(source); } - // TODO: check this public static IEnumerable RawEnum(this IEnumerable source, bool releaseItems = true) { foreach (object item in source) diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/ZPushFolder.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/ZPushFolder.cs index c5941c4..9f6fad2 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/ZPushFolder.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/ZPushFolder.cs @@ -27,10 +27,12 @@ namespace Acacia.ZPush { public class ZPushFolder : DisposableWrapper { - private IFolder _folder; - private ZPushFolder _parent; + private readonly IFolder _folder; + private readonly IItems_Events _items; + private readonly IFolders_Events _subFolders; + private readonly ZPushFolder _parent; private readonly ZPushWatcher _watcher; - private List _itemsWatchers = new List(); + private readonly List _itemsWatchers = new List(); /// /// Children folders indexed by EntryID @@ -50,6 +52,9 @@ namespace Acacia.ZPush this._parent = parent; this._watcher = watcher; this._folder = folder; + // We need to keep links to these objects to keep getting events. + this._items = folder.Items.GetEvents(); + this._subFolders = folder.SubFolders.GetEvents(); folder.ZPush = this; } @@ -57,6 +62,13 @@ namespace Acacia.ZPush { Cleanup(); _folder.Dispose(); + _items.Dispose(); + _subFolders.Dispose(); + } + + public override string ToString() + { + return Name; } public IFolder Folder { get { return _folder; } } @@ -71,7 +83,7 @@ namespace Acacia.ZPush _watcher.OnFolderDiscovered(this); // Recurse the children - foreach (IFolder subfolder in _folder.GetSubFolders()) + foreach (IFolder subfolder in _folder.SubFolders) { Tasks.Task(null, "WatchChild", () => WatchChild(subfolder)); } @@ -94,10 +106,10 @@ namespace Acacia.ZPush } } + #region Event handlers + private void HookEvents(bool register) { - // TODO - /* if (register) { // Item events @@ -119,9 +131,119 @@ namespace Acacia.ZPush _subFolders.FolderAdd -= SubFolders_FolderAdd; _subFolders.FolderRemove -= SubFolders_FolderRemove; _subFolders.FolderChange -= SubFolders_FolderChange; - }*/ + } } + #region Event handlers + + private void SubFolders_FolderAdd(IFolder folder) + { + try + { + Logger.Instance.Debug(this, "Folder added in {0}: {1}", Name, folder.Name); + WatchChild(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}", 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. + // TODO: move this logic into IFolders? + HashSet remaining = new HashSet(); + foreach (IFolder child in _folder.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> remove = new List>(); + 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}", 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(IFolder folder) + { + try + { + Logger.Instance.Debug(this, "Folder changed in {0}: {1}", 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}", Name, folder.Name, folder.EntryID, folder.StoreDisplayName); + WatchChild(folder); + } + } + catch (System.Exception e) { Logger.Instance.Error(this, "Exception in SubFolders_FolderChange: {0}: {1}", Name, e); } + } + + private void Items_ItemAdd(IItem item) + { + try + { + 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(IItem item) + { + try + { + 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 + + #endregion + private void Cleanup() { Logger.Instance.Trace(this, "Unwatching folder: {0}", _folder.Name); diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/ZPushLocalStore.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/ZPushLocalStore.cs index 9330eb5..268ca1f 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/ZPushLocalStore.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/ZPushLocalStore.cs @@ -135,7 +135,7 @@ namespace Acacia.ZPush // Hide the folders that are not custom folders using (IFolder root = store.GetRootFolder()) { - foreach(IFolder sub in root.GetSubFolders()) + foreach(IFolder sub in root.SubFolders) { using (sub) {