Cleaned up account checking

This commit is contained in:
Patrick Simpson 2017-02-14 14:46:24 +01:00
parent d4b03c2eb9
commit cb32513ed3
35 changed files with 763 additions and 621 deletions

View File

@ -280,6 +280,7 @@
<Compile Include="Native\IOleWindow.cs" />
<Compile Include="OutlookConstants.cs" />
<Compile Include="Stubs\Enums.cs" />
<Compile Include="Stubs\IAccount.cs" />
<Compile Include="Stubs\IAddIn.cs" />
<Compile Include="Stubs\IAddressEntry.cs" />
<Compile Include="Stubs\ICommandBars.cs" />
@ -288,7 +289,9 @@
<Compile Include="Stubs\IItemEvents.cs" />
<Compile Include="Stubs\IOutlookWindow.cs" />
<Compile Include="Stubs\IRecipient.cs" />
<Compile Include="Stubs\IStores.cs" />
<Compile Include="Stubs\ISyncObject.cs" />
<Compile Include="Stubs\OutlookWrappers\AccountWrapper.cs" />
<Compile Include="Stubs\OutlookWrappers\AddInWrapper.cs" />
<Compile Include="Stubs\OutlookWrappers\AddressEntryWrapper.cs" />
<Compile Include="Stubs\OutlookWrappers\CommandBarsWrapper.cs" />
@ -296,6 +299,7 @@
<Compile Include="Stubs\OutlookWrappers\ItemEventsWrapper.cs" />
<Compile Include="Stubs\OutlookWrappers\OutlookItemWrapper.cs" />
<Compile Include="Stubs\OutlookWrappers\RecipientWrapper.cs" />
<Compile Include="Stubs\OutlookWrappers\StoresWrapper.cs" />
<Compile Include="Stubs\OutlookWrappers\SyncObjectWrapper.cs" />
<Compile Include="Stubs\Wrappers.cs" />
<Compile Include="UI\Outlook\OutlookImageList.cs" />

View File

@ -103,7 +103,7 @@ namespace Acacia.Features.FreeBusy
set
{
RegistryUtil.SetConfigValue(Name, REG_DEFAULTACCOUNT, value == null ? "" : value.SmtpAddress, RegistryValueKind.String);
RegistryUtil.SetConfigValue(Name, REG_DEFAULTACCOUNT, value == null ? "" : value.Account.SmtpAddress, RegistryValueKind.String);
}
}

View File

@ -29,7 +29,6 @@ using System.ComponentModel;
using System.Windows.Forms;
using Acacia.UI;
using static Acacia.DebugOptions;
using Microsoft.Office.Interop.Outlook;
namespace Acacia.Features.GAB
{
@ -474,7 +473,7 @@ namespace Acacia.Features.GAB
private void AccountDiscovered(ZPushAccount zpush)
{
Logger.Instance.Info(this, "Account discovered: {0}", zpush.DisplayName);
_domains.Add(zpush.DomainName);
_domains.Add(zpush.Account.DomainName);
zpush.ConfirmedChanged += (z) =>
{
@ -606,7 +605,7 @@ namespace Acacia.Features.GAB
private void RegisterGABAccount(ZPushAccount account, IFolder folder)
{
// Determine the domain name
string domain = account.DomainName;
string domain = account.Account.DomainName;
// Could already be registered if there are multiple accounts on the same domain
GABHandler gab;
@ -637,7 +636,7 @@ namespace Acacia.Features.GAB
private void ZPushChannelAvailable(IFolder folder)
{
using (IStore store = folder.Store)
using (IStore store = folder.GetStore())
{
Logger.Instance.Debug(this, "Z-Push channel available: {0} on {1}", folder, store.DisplayName);

View File

@ -121,7 +121,7 @@ namespace Acacia.Features.GAB
{
get
{
using(IStore store = Folder.Store)
using(IStore store = Folder.GetStore())
return store.DisplayName;
}
}

View File

@ -232,7 +232,7 @@ namespace Acacia.Features.OutOfOffice
if (oof.State != ActiveSync.OOFState.Disabled)
{
if (MessageBox.Show(
string.Format(Properties.Resources.OOFStartup_Message, account.SmtpAddress),
string.Format(Properties.Resources.OOFStartup_Message, account.Account.SmtpAddress),
Properties.Resources.OOFStartup_Title,
MessageBoxButtons.YesNo,
MessageBoxIcon.Question

View File

@ -43,7 +43,7 @@ namespace Acacia.Features.OutOfOffice
InitializeComponent();
// Add the email address to the title
Text = string.Format(Text, account.SmtpAddress);
Text = string.Format(Text, account.Account.SmtpAddress);
// Set the time formats
timeFrom.CustomFormat = CultureInfo.CurrentCulture.DateTimeFormat.ShortTimePattern;

View File

@ -71,7 +71,7 @@ namespace Acacia.Features.SendAs
private void MailEvents_Respond(IMailItem mail, IMailItem response)
{
Logger.Instance.Trace(this, "Responding to mail, checking");
using (IStore store = mail.Store)
using (IStore store = mail.GetStore())
{
ZPushAccount zpush = Watcher.Accounts.GetAccount(store);
Logger.Instance.Trace(this, "Checking ZPush: {0}", zpush);
@ -114,13 +114,13 @@ namespace Acacia.Features.SendAs
private void MailEvents_ItemSend(IMailItem item, ref bool cancel)
{
using (IStore store = item.Store)
using (IStore store = item.GetStore())
{
ZPushAccount zpush = Watcher.Accounts.GetAccount(store);
if (zpush != null)
{
string address = item.SenderEmailAddress;
if (address != null && address != zpush.SmtpAddress)
if (address != null && address != zpush.Account.SmtpAddress)
{
Logger.Instance.Trace(this, "SendAs: {0}: {1}", address, item.SenderName);
item.SetProperty(Constants.ZPUSH_SEND_AS, address);

View File

@ -71,7 +71,7 @@ namespace Acacia.Features.SharedFolders
).Images;
// Add the email address to the title
Text = string.Format(Text, account.SmtpAddress);
Text = string.Format(Text, account.Account.SmtpAddress);
// Set up options
ShowOptions(new KTreeNode[0]);

View File

@ -100,15 +100,15 @@ namespace Acacia.Features.WebApp
// Perform a cached auto discover
try
{
Logger.Instance.Debug(this, "Starting kdiscover: {0}", account.DomainName);
Logger.Instance.Debug(this, "Starting kdiscover: {0}", account.Account.DomainName);
string url = PerformAutoDiscover(account);
Logger.Instance.Debug(this, "Finished kdiscover: {0}: {1}", account.DomainName, url);
Logger.Instance.Debug(this, "Finished kdiscover: {0}: {1}", account.Account.DomainName, url);
account.SetFeatureData(this, TXT_KDISCOVER, new URLCached(url));
return url;
}
catch (System.Exception e)
{
Logger.Instance.Warning(this, "Exception during kdiscover: {0}: {1}", account.DomainName, e);
Logger.Instance.Warning(this, "Exception during kdiscover: {0}: {1}", account.Account.DomainName, e);
account.SetFeatureData(this, TXT_KDISCOVER, null);
return null;
}
@ -117,7 +117,7 @@ namespace Acacia.Features.WebApp
private string PerformAutoDiscover(ZPushAccount account)
{
// Fetch the txt record
IList<string> txt = DnsUtil.GetTxtRecord(account.DomainName);
IList<string> txt = DnsUtil.GetTxtRecord(account.Account.DomainName);
if (txt == null)
return null;

View File

@ -45,4 +45,11 @@ namespace Acacia.Stubs
ManagedEmail = 29,
SuggestedContacts = 30
}
public enum AccountType
{
// TODO
EAS,
Other
}
}

View File

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security;
using System.Text;
using System.Threading.Tasks;
namespace Acacia.Stubs
{
public interface IAccount : IDisposable
{
AccountType AccountType { get; }
IStore Store { get; }
string DisplayName { get; }
string SmtpAddress { get; }
string UserName { get; }
string ServerURL { get; }
string DeviceId { get; }
SecureString Password { get; }
bool HasPassword { get; }
string StoreID { get; }
string DomainName { get; }
}
}

View File

@ -44,9 +44,9 @@ namespace Acacia.Stubs
// TODO: clean this up
/// <summary>
/// Sends and receives all accounts.
/// Sends and receives all accounts, or a specific account.
/// </summary>
void SendReceive();
void SendReceive(IAccount account = null);
/// <summary>
/// Restarts the application
@ -63,12 +63,13 @@ namespace Acacia.Stubs
IRecipient ResolveRecipient(string name);
IStore AddFileStore(string path);
/// <summary>
/// Returns the stores. The caller is responsible for disposing.
/// Returns the store manager. This is a shared object and must NOT be disposed.
/// </summary>
IEnumerable<IStore> Stores { get; }
IStores Stores
{
get;
}
#endregion
}

View File

@ -42,14 +42,13 @@ namespace Acacia.Stubs
/// <summary>
/// Returns the store. The owner is responsible for disposing.
/// TODO: make method to make disposing clear
/// </summary>
IStore Store { get; }
IStore GetStore();
/// <summary>
/// Quick accessor to Store.Id, to prevent allocating a wrapper for it.
/// </summary>
string StoreId { get; }
string StoreID { get; }
/// <summary>
/// Quick accessor to Store.DisplayName, to prevent allocating a wrapper for it.

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NSOutlook = Microsoft.Office.Interop.Outlook;
using NSOutlookDelegates = Microsoft.Office.Interop.Outlook;
namespace Acacia.Stubs
{
@ -12,32 +12,32 @@ namespace Acacia.Stubs
#region Event handlers
// TODO: custom delegates
event NSOutlook.ItemEvents_10_AfterWriteEventHandler AfterWrite;
event NSOutlook.ItemEvents_10_AttachmentAddEventHandler AttachmentAdd;
event NSOutlook.ItemEvents_10_AttachmentReadEventHandler AttachmentRead;
event NSOutlook.ItemEvents_10_AttachmentRemoveEventHandler AttachmentRemove;
event NSOutlook.ItemEvents_10_BeforeAttachmentAddEventHandler BeforeAttachmentAdd;
event NSOutlook.ItemEvents_10_BeforeAttachmentPreviewEventHandler BeforeAttachmentPreview;
event NSOutlook.ItemEvents_10_BeforeAttachmentReadEventHandler BeforeAttachmentRead;
event NSOutlook.ItemEvents_10_BeforeAttachmentSaveEventHandler BeforeAttachmentSave;
event NSOutlook.ItemEvents_10_BeforeAttachmentWriteToTempFileEventHandler BeforeAttachmentWriteToTempFile;
event NSOutlook.ItemEvents_10_BeforeAutoSaveEventHandler BeforeAutoSave;
event NSOutlook.ItemEvents_10_BeforeCheckNamesEventHandler BeforeCheckNames;
event NSOutlook.ItemEvents_10_BeforeDeleteEventHandler BeforeDelete;
event NSOutlook.ItemEvents_10_BeforeReadEventHandler BeforeRead;
event NSOutlook.ItemEvents_10_CloseEventHandler Close;
event NSOutlook.ItemEvents_10_CustomActionEventHandler CustomAction;
event NSOutlook.ItemEvents_10_CustomPropertyChangeEventHandler CustomPropertyChange;
event NSOutlook.ItemEvents_10_ForwardEventHandler Forward;
event NSOutlook.ItemEvents_10_OpenEventHandler Open;
event NSOutlook.ItemEvents_10_PropertyChangeEventHandler PropertyChange;
event NSOutlook.ItemEvents_10_ReadEventHandler Read;
event NSOutlook.ItemEvents_10_ReadCompleteEventHandler ReadComplete;
event NSOutlook.ItemEvents_10_ReplyEventHandler Reply;
event NSOutlook.ItemEvents_10_ReplyAllEventHandler ReplyAll;
event NSOutlook.ItemEvents_10_SendEventHandler Send;
event NSOutlook.ItemEvents_10_UnloadEventHandler Unload;
event NSOutlook.ItemEvents_10_WriteEventHandler Write;
event NSOutlookDelegates.ItemEvents_10_AfterWriteEventHandler AfterWrite;
event NSOutlookDelegates.ItemEvents_10_AttachmentAddEventHandler AttachmentAdd;
event NSOutlookDelegates.ItemEvents_10_AttachmentReadEventHandler AttachmentRead;
event NSOutlookDelegates.ItemEvents_10_AttachmentRemoveEventHandler AttachmentRemove;
event NSOutlookDelegates.ItemEvents_10_BeforeAttachmentAddEventHandler BeforeAttachmentAdd;
event NSOutlookDelegates.ItemEvents_10_BeforeAttachmentPreviewEventHandler BeforeAttachmentPreview;
event NSOutlookDelegates.ItemEvents_10_BeforeAttachmentReadEventHandler BeforeAttachmentRead;
event NSOutlookDelegates.ItemEvents_10_BeforeAttachmentSaveEventHandler BeforeAttachmentSave;
event NSOutlookDelegates.ItemEvents_10_BeforeAttachmentWriteToTempFileEventHandler BeforeAttachmentWriteToTempFile;
event NSOutlookDelegates.ItemEvents_10_BeforeAutoSaveEventHandler BeforeAutoSave;
event NSOutlookDelegates.ItemEvents_10_BeforeCheckNamesEventHandler BeforeCheckNames;
event NSOutlookDelegates.ItemEvents_10_BeforeDeleteEventHandler BeforeDelete;
event NSOutlookDelegates.ItemEvents_10_BeforeReadEventHandler BeforeRead;
event NSOutlookDelegates.ItemEvents_10_CloseEventHandler Close;
event NSOutlookDelegates.ItemEvents_10_CustomActionEventHandler CustomAction;
event NSOutlookDelegates.ItemEvents_10_CustomPropertyChangeEventHandler CustomPropertyChange;
event NSOutlookDelegates.ItemEvents_10_ForwardEventHandler Forward;
event NSOutlookDelegates.ItemEvents_10_OpenEventHandler Open;
event NSOutlookDelegates.ItemEvents_10_PropertyChangeEventHandler PropertyChange;
event NSOutlookDelegates.ItemEvents_10_ReadEventHandler Read;
event NSOutlookDelegates.ItemEvents_10_ReadCompleteEventHandler ReadComplete;
event NSOutlookDelegates.ItemEvents_10_ReplyEventHandler Reply;
event NSOutlookDelegates.ItemEvents_10_ReplyAllEventHandler ReplyAll;
event NSOutlookDelegates.ItemEvents_10_SendEventHandler Send;
event NSOutlookDelegates.ItemEvents_10_UnloadEventHandler Unload;
event NSOutlookDelegates.ItemEvents_10_WriteEventHandler Write;
#endregion
}

View File

@ -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 IStores_AccountDiscovered(IAccount account);
public delegate void IStores_AccountRemoved(IAccount account);
/// <summary>
/// Manages the stores and associated acounts.
/// </summary>
public interface IStores: IComWrapper, IEnumerable<IStore>
{
/// <summary>
/// Returns the accounts. The accounts are shared objects and must not be disposed.
/// </summary>
IEnumerable<IAccount> Accounts { get; }
event IStores_AccountDiscovered AccountDiscovered;
event IStores_AccountRemoved AccountRemoved;
/// <summary>
/// Adds a file store to the current collection. If the store is already in the collection, an exception is thrown.
/// </summary>
/// <param name="path">The path.</param>
/// <returns>The store. The caller is responsible for disposing.</returns>
IStore AddFileStore(string path);
}
}

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NSOutlook = Microsoft.Office.Interop.Outlook;
using NSOutlookDelegates = Microsoft.Office.Interop.Outlook;
namespace Acacia.Stubs
{
@ -25,10 +25,10 @@ namespace Acacia.Stubs
#region Events
// TODO: custom delegates
event NSOutlook.SyncObjectEvents_OnErrorEventHandler OnError;
event NSOutlook.SyncObjectEvents_ProgressEventHandler Progress;
event NSOutlook.SyncObjectEvents_SyncEndEventHandler SyncEnd;
event NSOutlook.SyncObjectEvents_SyncStartEventHandler SyncStart;
event NSOutlookDelegates.SyncObjectEvents_OnErrorEventHandler OnError;
event NSOutlookDelegates.SyncObjectEvents_ProgressEventHandler Progress;
event NSOutlookDelegates.SyncObjectEvents_SyncEndEventHandler SyncEnd;
event NSOutlookDelegates.SyncObjectEvents_SyncStartEventHandler SyncStart;
#endregion
}
}

View File

@ -0,0 +1,153 @@
using Acacia.Utils;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Security;
using System.Text;
using System.Threading.Tasks;
namespace Acacia.Stubs.OutlookWrappers
{
[TypeConverter(typeof(ExpandableObjectConverter))]
class AccountWrapper : DisposableWrapper, IAccount, LogContext
{
private readonly string _regPath;
private readonly IStore _store;
internal AccountWrapper(string regPath, IStore store)
{
this._regPath = regPath;
this._store = store;
// Cache the SmtpAddress, it is used as the key
SmtpAddress = RegistryUtil.GetValueString(_regPath, OutlookConstants.REG_VAL_EMAIL, null);
}
protected override void DoRelease()
{
_store.Dispose();
}
[Browsable(false)]
public string LogContextId
{
get
{
return "ZPushAccount(" + SmtpAddress + ")";
}
}
public override string ToString()
{
return SmtpAddress;
}
/// <summary>
/// Triggers an Outlook send/receive operation.
/// </summary>
public void SendReceive()
{
// TODO: ThisAddIn.Instance.SendReceive();
throw new NotImplementedException();
}
#region Properties
public AccountType AccountType
{
get
{
return (DeviceId == null) ? AccountType.Other : AccountType.EAS;
}
}
[Browsable(false)]
public IStore Store
{
get
{
return _store;
}
}
public string DisplayName
{
get
{
return RegistryUtil.GetValueString(_regPath, OutlookConstants.REG_VAL_DISPLAYNAME, null);
}
}
public string SmtpAddress
{
get;
private set;
}
public string UserName
{
get
{
return RegistryUtil.GetValueString(_regPath, OutlookConstants.REG_VAL_EAS_USERNAME, null);
}
}
public string ServerURL
{
get
{
return RegistryUtil.GetValueString(_regPath, OutlookConstants.REG_VAL_EAS_SERVER, null);
}
}
public string DeviceId
{
get
{
return RegistryUtil.GetValueString(_regPath, OutlookConstants.REG_VAL_EAS_DEVICEID, null);
}
}
[Browsable(false)]
public SecureString Password
{
get
{
byte[] encrypted = (byte[])Registry.GetValue(_regPath, OutlookConstants.REG_VAL_EAS_PASSWORD, null);
return PasswordEncryption.Decrypt(encrypted);
}
}
[Browsable(false)]
public bool HasPassword
{
get { return Registry.GetValue(_regPath, OutlookConstants.REG_VAL_EAS_PASSWORD, null) != null; }
}
public string StoreID
{
get { return GetStoreId(_regPath); }
}
public static string GetStoreId(string regPath)
{
return StringUtil.BytesToHex((byte[])Registry.GetValue(regPath, OutlookConstants.REG_VAL_EAS_STOREID, null));
}
public string DomainName
{
get
{
int index = SmtpAddress.IndexOf('@');
if (index < 0)
return SmtpAddress;
else
return SmtpAddress.Substring(index + 1);
}
}
#endregion
}
}

View File

@ -17,17 +17,29 @@ namespace Acacia.Stubs.OutlookWrappers
{
public class AddInWrapper : IAddIn
{
private readonly NSOutlook.Application _app;
private readonly ThisAddIn _thisAddIn;
private NSOutlook.Application _app;
private readonly StoresWrapper _stores;
public AddInWrapper(ThisAddIn thisAddIn)
{
this._thisAddIn = thisAddIn;
this._app = thisAddIn.Application;
NSOutlook.NameSpace session = _app.Session;
try
{
this._stores = new StoresWrapper(session.Stores);
}
finally
{
ComRelease.Release(session);
}
}
public void SendReceive()
public void SendReceive(IAccount account)
{
// TODO: send/receive specific account
NSOutlook.NameSpace session = _app.Session;
try
{
@ -39,6 +51,11 @@ namespace Acacia.Stubs.OutlookWrappers
}
}
public void Start()
{
_stores.Start();
}
public void Restart()
{
// Can not use the assembly location, as that is in the GAC
@ -224,35 +241,9 @@ namespace Acacia.Stubs.OutlookWrappers
}
}
public IStore AddFileStore(string path)
public IStores Stores
{
using (ComRelease com = new ComRelease())
{
NSOutlook.NameSpace session = com.Add(_app.Session);
// Add the store
session.AddStore(path);
// And fetch it and wrap
NSOutlook.Stores stores = com.Add(session.Stores);
return Mapping.Wrap(stores[stores.Count]);
}
}
public IEnumerable<IStore> Stores
{
get
{
using (ComRelease com = new ComRelease())
{
NSOutlook.NameSpace session = com.Add(_app.Session);
NSOutlook.Stores stores = com.Add(session.Stores);
foreach (NSOutlook.Store store in stores)
{
yield return Mapping.Wrap(store);
}
}
}
get { return _stores; }
}
}
}

View File

@ -116,19 +116,16 @@ namespace Acacia.Stubs.OutlookWrappers
}
}
public IStore Store
public IStore GetStore()
{
get
using (ComRelease com = new ComRelease())
{
using (ComRelease com = new ComRelease())
{
NSOutlook.Folder parent = com.Add(_item.Parent);
return Mapping.Wrap(parent?.Store);
}
NSOutlook.Folder parent = com.Add(_item.Parent);
return Mapping.Wrap(parent?.Store);
}
}
public string StoreId
public string StoreID
{
get
{

View File

@ -241,19 +241,16 @@ namespace Acacia.Stubs.OutlookWrappers
}
}
public IStore Store
public IStore GetStore()
{
get
using (ComRelease com = new ComRelease())
{
using (ComRelease com = new ComRelease())
{
NSOutlook.Folder parent = com.Add(_item.Parent);
return Mapping.Wrap(parent?.Store);
}
NSOutlook.Folder parent = com.Add(_item.Parent);
return Mapping.Wrap(parent?.Store);
}
}
public string StoreId
public string StoreID
{
get
{

View File

@ -257,19 +257,16 @@ namespace Acacia.Stubs.OutlookWrappers
}
}
public IStore Store
public IStore GetStore()
{
get
using (ComRelease com = new ComRelease())
{
using (ComRelease com = new ComRelease())
{
NSOutlook.Folder parent = com.Add(_item.Parent);
return Mapping.Wrap(parent?.Store);
}
NSOutlook.Folder parent = com.Add(_item.Parent);
return Mapping.Wrap(parent?.Store);
}
}
public string StoreId
public string StoreID
{
get
{

View File

@ -100,13 +100,13 @@ namespace Acacia.Stubs.OutlookWrappers
public string EntryID { get { return _item.EntryID; } }
public IStore Store { get { return Mapping.Wrap(_item.Store); } }
public IStore GetStore() { return Mapping.Wrap(_item.Store); }
public string StoreId
public string StoreID
{
get
{
using (IStore store = Store)
using (IStore store = GetStore())
{
return store.StoreID;
}
@ -116,7 +116,7 @@ namespace Acacia.Stubs.OutlookWrappers
{
get
{
using (IStore store = Store)
using (IStore store = GetStore())
{
return store.DisplayName;
}
@ -241,7 +241,7 @@ namespace Acacia.Stubs.OutlookWrappers
{
try
{
using (IStore store = Store)
using (IStore store = GetStore())
{
return store.GetItemFromID(entryId);
}

View File

@ -150,19 +150,16 @@ namespace Acacia.Stubs.OutlookWrappers
}
}
public IStore Store
public IStore GetStore()
{
get
using (ComRelease com = new ComRelease())
{
using (ComRelease com = new ComRelease())
{
NSOutlook.Folder parent = com.Add(_item.Parent);
return Mapping.Wrap(parent?.Store);
}
NSOutlook.Folder parent = com.Add(_item.Parent);
return Mapping.Wrap(parent?.Store);
}
}
public string StoreId
public string StoreID
{
get
{

View File

@ -97,19 +97,16 @@ namespace Acacia.Stubs.OutlookWrappers
}
}
public IStore Store
public IStore GetStore()
{
get
using (ComRelease com = new ComRelease())
{
using (ComRelease com = new ComRelease())
{
NSOutlook.Folder parent = com.Add(_item.Parent);
return Mapping.Wrap(parent?.Store);
}
NSOutlook.Folder parent = com.Add(_item.Parent);
return Mapping.Wrap(parent?.Store);
}
}
public string StoreId
public string StoreID
{
get
{

View File

@ -95,19 +95,16 @@ namespace Acacia.Stubs.OutlookWrappers
}
}
public IStore Store
public IStore GetStore()
{
get
using (ComRelease com = new ComRelease())
{
using (ComRelease com = new ComRelease())
{
NSOutlook.Folder parent = com.Add(_item.Parent);
return Mapping.Wrap(parent?.Store);
}
NSOutlook.Folder parent = com.Add(_item.Parent);
return Mapping.Wrap(parent?.Store);
}
}
public string StoreId
public string StoreID
{
get
{

View File

@ -0,0 +1,270 @@
using Acacia.Utils;
using Microsoft.Win32;
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 StoresWrapper : ComWrapper<NSOutlook.Stores>, IStores
{
/// <summary>
/// Accounts indexed by store id. Null values are allowed, if a store has been
/// determined to not be associated with an Account. This is required to determine when a store is new.
/// </summary>
private readonly Dictionary<string, AccountWrapper> _accountsByStoreId = new Dictionary<string, AccountWrapper>();
/// <summary>
/// Accounts indexed by SMTPAddress. Null values are not allowed.
/// </summary>
private readonly Dictionary<string, AccountWrapper> _accountsBySmtp = new Dictionary<string, AccountWrapper>();
public StoresWrapper(NSOutlook.Stores item) : base(item)
{
}
#region Events
public event IStores_AccountDiscovered AccountDiscovered;
public event IStores_AccountRemoved AccountRemoved;
private void OnAccountDiscovered(AccountWrapper account)
{
AccountDiscovered?.Invoke(account);
}
private void OnAccountRemoved(AccountWrapper account)
{
AccountRemoved?.Invoke(account);
}
#endregion
#region Implementation
public void Start()
{
// Check existing stores
// TODO: do this in background?
foreach(NSOutlook.Store store in _item)
{
StoreAdded(store);
}
// Register for new stores
// The store remove event is not sent, so don't bother registering for that
_item.StoreAdd += StoreAdded;
if (GlobalOptions.INSTANCE.AccountTimer)
{
// Set up timer to check for removed accounts
Util.Timed(null, Config.ACCOUNT_CHECK_INTERVAL, CheckAccountsRemoved);
}
}
/// <summary>
/// Event handler for Stores.StoreAdded event.
/// </summary>
private void StoreAdded(NSOutlook.Store s)
{
IStore store = null;
try
{
// 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 _item)
{
if (!_accountsByStoreId.ContainsKey(rawStore.StoreID))
{
store = new StoreWrapper(rawStore);
Logger.Instance.Trace(this, "New store: {0}", rawStore.DisplayName);
AccountWrapper account = TryCreateFromRegistry(store);
if (account == null)
{
// Add it to the cache so it is not evaluated again.
_accountsByStoreId.Add(store.StoreID, null);
Logger.Instance.Trace(this, "Not an account store: {0}", store.DisplayName);
store.Dispose();
}
else
{
// Account has taken ownership of the store
Logger.Instance.Trace(this, "New account store: {0}: {1}", store.DisplayName, account);
OnAccountDiscovered(account);
}
}
else ComRelease.Release(rawStore);
}
}
catch (System.Exception e)
{
Logger.Instance.Error(this, "StoreAdded Exception: {0}", e);
if (store != null)
store.Dispose();
}
}
private void CheckAccountsRemoved()
{
try
{
// Collect all the store ids
HashSet<string> stores = new HashSet<string>();
foreach (NSOutlook.Store store in _item)
{
try
{
stores.Add(store.StoreID);
}
finally
{
ComRelease.Release(store);
}
}
// Check if any relevant ones are removed
List<KeyValuePair<string, AccountWrapper>> removed = new List<KeyValuePair<string, AccountWrapper>>();
foreach (KeyValuePair<string, AccountWrapper> account in _accountsByStoreId)
{
if (!stores.Contains(account.Key))
{
Logger.Instance.Trace(this, "Store not found: {0} - {1}", account.Value, account.Key);
removed.Add(account);
}
}
// Process any removed stores
foreach (KeyValuePair<string, AccountWrapper> remove in removed)
{
Logger.Instance.Debug(this, "Account removed: {0} - {1}", remove.Value, remove.Key);
_accountsBySmtp.Remove(remove.Value.SmtpAddress);
_accountsByStoreId.Remove(remove.Key);
OnAccountRemoved(remove.Value);
}
}
catch (System.Exception e)
{
Logger.Instance.Error(this, "Exception in CheckAccountsRemoved: {0}", e);
}
}
#endregion
#region Public interface
public IStore AddFileStore(string path)
{
using (ComRelease com = new ComRelease())
{
NSOutlook.NameSpace session = com.Add(_item.Session);
// Add the store
session.AddStore(path);
// And fetch it and wrap
return Mapping.Wrap(_item[_item.Count]);
}
throw new NotImplementedException();
}
public IEnumerator<IStore> GetEnumerator()
{
foreach (NSOutlook.Store store in _item)
{
yield return Mapping.Wrap(store);
}
}
IEnumerator IEnumerable.GetEnumerator()
{
foreach (NSOutlook.Store store in _item)
{
yield return Mapping.Wrap(store);
}
}
public IEnumerable<IAccount> Accounts
{
get
{
return _accountsBySmtp.Values;
}
}
#endregion
#region Registry
/// <summary>
/// Creates the AccountWrapper for the store, from the registry.
/// </summary>
/// <param name="store">The store. Ownership is transferred to the AccountWrapper. If the account is not created, the store is NOT disposed</param>
/// <returns>The AccountWrapper, or null if no account is associated with the store</returns>
private AccountWrapper TryCreateFromRegistry(IStore store)
{
using (RegistryKey baseKey = FindRegistryKey(store))
{
if (baseKey == null)
return null;
AccountWrapper account = new AccountWrapper(baseKey.Name, store);
Register(account);
return account;
}
}
private void Register(AccountWrapper account)
{
// Register the new account
_accountsBySmtp.Add(account.SmtpAddress, account);
_accountsByStoreId.Add(account.StoreID, account);
Logger.Instance.Trace(this, "Account registered: {0} -> {1}", account.DisplayName, account.StoreID);
}
/// <summary>
/// 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(IStore store)
{
// Find the registry key by store id
using (RegistryKey key = OpenBaseKey())
{
if (key != null)
{
foreach (string subkey in key.GetSubKeyNames())
{
RegistryKey accountKey = key.OpenSubKey(subkey);
string storeId = AccountWrapper.GetStoreId(accountKey.Name);
if (storeId != null && storeId == store.StoreID)
{
return accountKey;
}
accountKey.Dispose();
}
}
}
return null;
}
private RegistryKey OpenBaseKey()
{
NSOutlook.NameSpace session = _item.Session;
try
{
string path = string.Format(OutlookConstants.REG_SUBKEY_ACCOUNTS, session.CurrentProfileName);
return OutlookRegistryUtils.OpenOutlookKey(path);
}
finally
{
ComRelease.Release(session);
}
}
#endregion
}
}

View File

@ -95,19 +95,16 @@ namespace Acacia.Stubs.OutlookWrappers
}
}
public IStore Store
public IStore GetStore()
{
get
using (ComRelease com = new ComRelease())
{
using (ComRelease com = new ComRelease())
{
NSOutlook.Folder parent = com.Add(_item.Parent);
return Mapping.Wrap(parent?.Store);
}
NSOutlook.Folder parent = com.Add(_item.Parent);
return Mapping.Wrap(parent?.Store);
}
}
public string StoreId
public string StoreID
{
get
{

View File

@ -146,7 +146,10 @@ namespace Acacia
// Start watching events
if (DebugOptions.GetOption(null, DebugOptions.WATCHER_ENABLED))
{
((AddInWrapper)Instance).Start();
Watcher.Start();
}
// Done
Logger.Instance.Debug(this, "Startup done");

View File

@ -88,5 +88,44 @@ namespace Acacia.Utils
GC.WaitForPendingFinalizers();
}
}
#region Timers
public static void Delayed(LogContext log, int millis, System.Action action)
{
RegisterTimer(log, millis, action, false);
}
public static void Timed(LogContext log, int millis, System.Action action)
{
RegisterTimer(log, millis, action, true);
}
private static void RegisterTimer(LogContext log, int millis, System.Action action, bool repeat)
{
System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
timer.Interval = millis;
timer.Tick += (s, eargs) =>
{
try
{
action();
if (!repeat)
{
timer.Enabled = false;
timer.Dispose();
}
}
catch (System.Exception e)
{
Logger.Instance.Trace(log, "Exception in timer: {0}", e);
}
};
timer.Start();
}
#endregion
}
}

View File

@ -147,9 +147,9 @@ namespace Acacia.ZPush.Connect
// TODO: it would be nice to let the system handle the SecureString for the password. However,
// when specifying credentials for an HttpClient, they are only used after a 401 is received
// on the first request, basically doubling the number of requests.
using (SecureString pass = _account.Password)
using (SecureString pass = _account.Account.Password)
{
var byteArray = Encoding.UTF8.GetBytes(_account.UserName + ":" + pass.ConvertToUnsecureString());
var byteArray = Encoding.UTF8.GetBytes(_account.Account.UserName + ":" + pass.ConvertToUnsecureString());
var header = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));
_client.DefaultRequestHeaders.Authorization = header;
}
@ -278,8 +278,8 @@ namespace Acacia.ZPush.Connect
public Response Execute(ActiveSync.RequestBase request)
{
string url = string.Format(ACTIVESYNC_URL, _account.ServerURL, _account.DeviceId,
request.Command, _account.UserName, "WindowsOutlook");
string url = string.Format(ACTIVESYNC_URL, _account.Account.ServerURL, _account.Account.DeviceId,
request.Command, _account.Account.UserName, "WindowsOutlook");
// Construct the body
WBXMLDocument doc = new WBXMLDocument();
@ -291,10 +291,10 @@ namespace Acacia.ZPush.Connect
using (HttpContent content = new ByteArrayContent(contentBody))
{
Logger.Instance.Trace(this, "Sending request: {0} -> {1}", _account.ServerURL, doc.ToXMLString());
Logger.Instance.Trace(this, "Sending request: {0} -> {1}", _account.Account.ServerURL, doc.ToXMLString());
content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.ms-sync.wbxml");
string caps = ZPushCapabilities.Client.ToString();
Logger.Instance.Trace(this, "Sending request: {0} -> {1}: {2}", _account.ServerURL, caps, doc.ToXMLString());
Logger.Instance.Trace(this, "Sending request: {0} -> {1}: {2}", _account.Account.ServerURL, caps, doc.ToXMLString());
content.Headers.Add(Constants.ZPUSH_HEADER_CLIENT_CAPABILITIES, caps);
using (HttpResponseMessage response = _client.PostAsync(url, content, _cancel).Result)
{

View File

@ -46,14 +46,14 @@ namespace Acacia.ZPush.Connect
public ResponseType Execute<ResponseType>(SoapRequest<ResponseType> request)
{
// Create the url
string url = string.Format(ACTIVESYNC_URL, _connection.Account.ServerURL, "webservice",
string url = string.Format(ACTIVESYNC_URL, _connection.Account.Account.ServerURL, "webservice",
ServiceName,
// TODO: this username is a bit of a quick hack.
request.UserName ?? _connection.Account.UserName,
request.UserName ?? _connection.Account.Account.UserName,
"webservice");
// Set up the encoding
SoapRequestEncoder encoder = new SoapRequestEncoder(_connection.Account.ServerURL, ServiceParameters, request);
SoapRequestEncoder encoder = new SoapRequestEncoder(_connection.Account.Account.ServerURL, ServiceParameters, request);
encoder.ServiceName = ServiceName;
// Execute the request
@ -85,7 +85,7 @@ namespace Acacia.ZPush.Connect
get
{
SoapParameters parameters = new SoapParameters();
parameters.Add("devid", _connection.Account.DeviceId.ToLower());
parameters.Add("devid", _connection.Account.Account.DeviceId.ToLower());
return parameters;
}
}

View File

@ -32,143 +32,41 @@ using System.Threading.Tasks;
namespace Acacia.ZPush
{
[TypeConverter(typeof(ExpandableObjectConverter))]
public class ZPushAccount : DisposableWrapper, LogContext
public class ZPushAccount : LogContext
{
#region Miscellaneous
private readonly string _regPath;
private readonly IAccount _account;
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. The new object takes ownership</param>
internal ZPushAccount(string regPath, IStore store)
internal ZPushAccount(IAccount account)
{
this._regPath = regPath;
this._store = store;
// Cache the SmtpAddress, it is used as the key
SmtpAddress = RegistryUtil.GetValueString(_regPath, OutlookConstants.REG_VAL_EMAIL, null);
this._account = account;
}
protected override void DoRelease()
{
_store.Dispose();
}
[Browsable(false)]
public IAccount Account { get { return _account; } }
public String DisplayName { get { return _account.DisplayName; } }
[Browsable(false)]
public string LogContextId
{
get
{
return "ZPushAccount(" + SmtpAddress + ")";
return "ZPushAccount(" + _account.SmtpAddress + ")";
}
}
public override string ToString()
{
return SmtpAddress;
return _account.SmtpAddress;
}
/// <summary>
/// Triggers an Outlook send/receive operation.
/// Triggers an Outlook send/receive operation for this account.
/// </summary>
public void SendReceive()
{
// TODO: ThisAddIn.Instance.SendReceive();
throw new NotImplementedException();
}
#endregion
#region Properties
[Browsable(false)]
public IStore Store
{
get
{
return _store;
}
}
public string DisplayName
{
get
{
return RegistryUtil.GetValueString(_regPath, OutlookConstants.REG_VAL_DISPLAYNAME, null);
}
}
public string SmtpAddress
{
get;
private set;
}
public string UserName
{
get
{
return RegistryUtil.GetValueString(_regPath, OutlookConstants.REG_VAL_EAS_USERNAME, null);
}
}
public string ServerURL
{
get
{
return RegistryUtil.GetValueString(_regPath, OutlookConstants.REG_VAL_EAS_SERVER, null);
}
}
public string DeviceId
{
get
{
return RegistryUtil.GetValueString(_regPath, OutlookConstants.REG_VAL_EAS_DEVICEID, null);
}
}
[Browsable(false)]
public SecureString Password
{
get
{
byte[] encrypted = (byte[])Registry.GetValue(_regPath, OutlookConstants.REG_VAL_EAS_PASSWORD, null);
return PasswordEncryption.Decrypt(encrypted);
}
}
[Browsable(false)]
public bool HasPassword
{
get { return Registry.GetValue(_regPath, OutlookConstants.REG_VAL_EAS_PASSWORD, null) != null; }
}
public string StoreID
{
get { return GetStoreId(_regPath); }
}
public static string GetStoreId(string regPath)
{
return StringUtil.BytesToHex((byte[])Registry.GetValue(regPath, OutlookConstants.REG_VAL_EAS_STOREID, null));
}
public string DomainName
{
get
{
int index = SmtpAddress.IndexOf('@');
if (index < 0)
return SmtpAddress;
else
return SmtpAddress.Substring(index + 1);
}
ThisAddIn.Instance.SendReceive(Account);
}
#endregion

View File

@ -23,21 +23,19 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NSOutlook = Microsoft.Office.Interop.Outlook;
namespace Acacia.ZPush
{
/// <summary>
/// Maintains the mapping from Outlook accounts to ZPush accounts, which
/// provide additional functionality and workarounds.
/// TODO: split into Outlook and Z-Push specific parts
/// </summary>
public class ZPushAccounts
public class ZPushAccounts : DisposableWrapper
{
private readonly ZPushWatcher _watcher;
// TODO: wrap these
private readonly NSOutlook.Application _app;
private readonly NSOutlook.NameSpace _session;
private readonly NSOutlook.Stores _stores;
private readonly IAddIn _addIn;
private readonly IStores _stores;
/// <summary>
/// ZPushAccounts indexed by SMTPAddress. Null values are not allowed.
@ -45,351 +43,126 @@ namespace Acacia.ZPush
private readonly Dictionary<string, ZPushAccount> _accountsBySmtp = new Dictionary<string, ZPushAccount>();
/// <summary>
/// ZPushAccounts indexed by store id. Null values are allowed, if a store has been
/// determined to not be associated with a ZPushAccount. This is required to determine when a store is new.
/// ZPushAccounts indexed by store id. Null values are noy allowed.
/// </summary>
private readonly Dictionary<string, ZPushAccount> _accountsByStoreId = new Dictionary<string, ZPushAccount>();
public ZPushAccounts(ZPushWatcher watcher, NSOutlook.Application app)
public ZPushAccounts(ZPushWatcher watcher, IAddIn addIn)
{
this._watcher = watcher;
this._app = app;
this._session = app.Session;
this._stores = _session.Stores;
this._addIn = addIn;
this._stores = addIn.Stores;
}
internal void Start()
protected override void DoRelease()
{
// Register for new stores
// The store remove event is not sent, so don't register for that
_stores.StoreAdd += StoreAdded;
_stores.Dispose();
}
#region Implementation
public void Start()
{
if (GlobalOptions.INSTANCE.ZPushCheck)
{
// Process existing accounts
using (ComRelease com = new ComRelease())
foreach (NSOutlook.Account account in com.Add(_session.Accounts))
foreach (IAccount account in _stores.Accounts)
{
Tasks.Task(null, "AccountCheck", () =>
{
Tasks.Task(null, "AccountCheck", () =>
{
try
{
// TODO: check if EAS account
// account gets released by GetAccount, save DisplayName for log purposes.
string displayName = account.DisplayName;
Logger.Instance.Trace(this, "Checking account: {0}", displayName);
ZPushAccount zpush = GetAccount(account);
if (zpush == null)
{
Logger.Instance.Trace(this, "Not a ZPush account: {0}", displayName);
}
else
{
Logger.Instance.Trace(this, "ZPush account: {0}", zpush);
_watcher.OnAccountDiscovered(zpush, true);
}
}
catch (System.Exception e)
{
Logger.Instance.Error(this, "Exception processing account: {0}", e);
}
});
}
AccountAdded(account);
});
}
Tasks.Task(null, "AccountCheckDone", () =>
{
_watcher.OnAccountsScanned();
if (GlobalOptions.INSTANCE.AccountTimer)
{
// Set up timer to check for removed accounts
_watcher.Timed(Config.ACCOUNT_CHECK_INTERVAL, CheckAccountsRemoved);
}
});
// Register for account changes
_stores.AccountDiscovered += AccountAdded;
_stores.AccountRemoved += AccountRemoved;
}
}
private void CheckAccountsRemoved()
private void AccountAdded(IAccount account)
{
try
{
// Collect all the store ids
HashSet<string> stores = new HashSet<string>();
foreach (NSOutlook.Store store in _stores)
{
try
{
stores.Add(store.StoreID);
}
finally
{
ComRelease.Release(store);
}
}
Logger.Instance.Trace(this, "Checking account: {0}", account);
// Check if any relevant ones are removed
List<KeyValuePair<string, ZPushAccount>> removed = new List<KeyValuePair<string, ZPushAccount>>();
foreach(KeyValuePair<string, ZPushAccount> account in _accountsByStoreId)
// Only EAS accounts can be zpush accounts
if (account.AccountType == AccountType.EAS)
{
if (!stores.Contains(account.Key))
{
Logger.Instance.Trace(this, "Store not found: {0} - {1}", account.Value, account.Key);
removed.Add(account);
}
ZPushAccount zpush = new ZPushAccount(account);
_accountsByStoreId.Add(account.StoreID, zpush);
_accountsBySmtp.Add(account.SmtpAddress, zpush);
Logger.Instance.Trace(this, "ZPush account: {0}", zpush);
_watcher.OnAccountDiscovered(zpush);
}
// Process any removed stores
foreach(KeyValuePair<string, ZPushAccount> remove in removed)
else
{
Logger.Instance.Debug(this, "Account removed: {0} - {1}", remove.Value, remove.Key);
_accountsBySmtp.Remove(remove.Value.SmtpAddress);
_accountsByStoreId.Remove(remove.Key);
_watcher.OnAccountRemoved(remove.Value);
Logger.Instance.Trace(this, "Not a ZPush account: {0}", account);
}
}
catch(System.Exception e)
catch (System.Exception e)
{
Logger.Instance.Error(this, "Exception in CheckAccountsRemoved: {0}", e);
Logger.Instance.Error(this, "Exception processing account: {0}", e);
}
}
public IEnumerable<ZPushAccount> GetAccounts()
private void AccountRemoved(IAccount account)
{
return _accountsBySmtp.Values;
_accountsBySmtp.Remove(account.SmtpAddress);
_accountsByStoreId.Remove(account.StoreID);
}
/// <summary>
/// Returns the ZPushAccount on which the folder is located.
/// </summary>
/// <returns>The ZPushAccount, or null if the folder is not on a zpush account</returns>
#endregion
#region Account access
public ZPushAccount GetAccount(IFolder folder)
{
ZPushAccount zpush = null;
using (IStore store = folder.Store)
_accountsByStoreId.TryGetValue(store.StoreID, out zpush);
return zpush;
}
public ZPushAccount GetAccount(IStore store)
{
ZPushAccount zpush = null;
_accountsByStoreId.TryGetValue(store.StoreID, out zpush);
return zpush;
}
public ZPushAccount GetAccount(IBase item)
{
if (item is IFolder)
return GetAccount((IFolder)item);
else if (item is IStore)
return GetAccount((IStore)item);
if (item.Parent != null)
return GetAccount(item.Parent);
return null;
ZPushAccount value = null;
_accountsByStoreId.TryGetValue(folder.StoreID, out value);
return value;
}
public ZPushAccount GetAccount(NSOutlook.MAPIFolder folder)
public ZPushAccount GetAccount(IAccount account)
{
using (ComRelease com = new ComRelease())
{
ZPushAccount zpush = null;
NSOutlook.Store store = com.Add(folder.Store);
string storeId = store?.StoreID;
if (storeId == null)
return null;
_accountsByStoreId.TryGetValue(storeId, out zpush);
return zpush;
}
ZPushAccount value = null;
_accountsByStoreId.TryGetValue(account.StoreID, out value);
return value;
}
public ZPushAccount GetAccount(IStore store)
{
ZPushAccount value = null;
_accountsByStoreId.TryGetValue(store.StoreID, out value);
return value;
}
public ZPushAccount GetAccount(IBase obj)
{
if (obj is IFolder)
return GetAccount((IFolder)obj);
else if (obj is IStore)
return GetAccount((IStore)obj);
if (obj.Parent != null)
return GetAccount(obj.Parent);
return null;
}
public ZPushAccount GetAccount(string smtpAddress)
{
ZPushAccount account = null;
_accountsBySmtp.TryGetValue(smtpAddress, out account);
return account;
ZPushAccount value = null;
_accountsBySmtp.TryGetValue(smtpAddress, out value);
return value;
}
/// <summary>
/// Returns the ZPush account associated with the Outlook account.
/// </summary>
/// <param name="account">The account. This function will release the handle</param>
/// <returns>The ZPushAccount, or null if not a ZPush account.</returns>
private ZPushAccount GetAccount(NSOutlook.Account account)
public IEnumerable<ZPushAccount> GetAccounts()
{
try
{
// Only EAS accounts can be zpush accounts
if (account.AccountType != NSOutlook.OlAccountType.olEas)
return null;
// Check for a cached value
ZPushAccount zpush;
if (_accountsBySmtp.TryGetValue(account.SmtpAddress, out zpush))
return zpush;
// Create a new account
return CreateFromRegistry(account);
}
finally
{
ComRelease.Release(account);
}
}
/// <summary>
/// Event handler for Stores.StoreAdded event.
/// </summary>
private void StoreAdded(NSOutlook.Store s)
{
IStore store = null;
try
{
// 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))
{
if (!_accountsByStoreId.ContainsKey(rawStore.StoreID))
{
Logger.Instance.Trace(this, "New store: {0}", rawStore.DisplayName);
store = Mapping.Wrap(rawStore);
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);
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(rawStore);
}
}
catch(System.Exception e)
{
Logger.Instance.Error(this, "StoreAdded Exception: {0}", e);
if (store != null)
store.Dispose();
}
}
#region Registry
private void Register(ZPushAccount zpush)
{
// Register the new account
_accountsBySmtp.Add(zpush.SmtpAddress, zpush);
_accountsByStoreId.Add(zpush.StoreID, zpush);
Logger.Instance.Trace(this, "Account registered: {0} -> {1}", zpush.DisplayName, zpush.Store.StoreID);
}
/// <summary>
/// Creates the ZPushAccount for the account, from the registry values.
/// </summary>
/// <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 owner
private ZPushAccount CreateFromRegistry(NSOutlook.Account account)
{
// TODO: check that caller releases account everywhere
using (ComRelease com = new ComRelease())
using (RegistryKey baseKey = FindRegistryKey(account))
{
if (baseKey == null)
throw new System.Exception("Unknown account: " + account.SmtpAddress);
// Get the store id
string storeId = ZPushAccount.GetStoreId(baseKey.Name);
// Find the store
NSOutlook.Store store = _session.GetStoreFromID(storeId);
// Done, create and register
ZPushAccount zpush = new ZPushAccount(baseKey.Name, Mapping.Wrap(store));
Register(zpush);
return zpush;
}
}
/// <summary>
/// Creates the ZPushAccount for the store, from the registry.
/// </summary>
/// <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(IStore store)
{
using (RegistryKey baseKey = FindRegistryKey(store))
{
if (baseKey == null)
return null;
ZPushAccount zpush = new ZPushAccount(baseKey.Name, store);
Register(zpush);
return zpush;
}
}
private RegistryKey OpenBaseKey()
{
NSOutlook.NameSpace session = _app.Session;
string path = string.Format(OutlookConstants.REG_SUBKEY_ACCOUNTS, session.CurrentProfileName);
ComRelease.Release(session);
return OutlookRegistryUtils.OpenOutlookKey(path);
}
/// <summary>
/// Finds the registry key for the account.
/// </summary>
/// <returns>The registry key, or null if it cannot be found</returns>
private RegistryKey FindRegistryKey(NSOutlook.Account account)
{
// Find the registry key by email adddress
using (RegistryKey key = OpenBaseKey())
{
if (key != null)
{
foreach (string subkey in key.GetSubKeyNames())
{
RegistryKey accountKey = key.OpenSubKey(subkey);
if (accountKey.GetValueString(OutlookConstants.REG_VAL_EMAIL) == account.SmtpAddress)
{
return accountKey;
}
accountKey.Dispose();
}
}
}
return null;
}
/// <summary>
/// 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(IStore store)
{
// Find the registry key by store id
using (RegistryKey key = OpenBaseKey())
{
if (key != null)
{
foreach (string subkey in key.GetSubKeyNames())
{
RegistryKey accountKey = key.OpenSubKey(subkey);
string storeId = ZPushAccount.GetStoreId(accountKey.Name);
if (storeId != null && storeId == store.StoreID)
{
return accountKey;
}
accountKey.Dispose();
}
}
}
return null;
return _accountsByStoreId.Values;
}
#endregion

View File

@ -23,7 +23,6 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NSOutlook = Microsoft.Office.Interop.Outlook;
namespace Acacia.ZPush
{
@ -88,7 +87,7 @@ namespace Acacia.ZPush
// Path found, create the store
Logger.Instance.Info(typeof(ZPushLocalStore), "Creating new store: {0}", path);
store = addIn.AddFileStore(path);
store = addIn.Stores.AddFileStore(path);
Logger.Instance.Debug(typeof(ZPushLocalStore), "Created new store: {0}", store.FilePath);
// Set the display name

View File

@ -53,7 +53,7 @@ namespace Acacia.ZPush
this._addIn = addIn;
this._app = addIn.RawApp;
Sync = new ZPushSync(this, addIn);
Accounts = new ZPushAccounts(this, _app);
Accounts = new ZPushAccounts(this, addIn);
// Need to keep a link to keep receiving events
_explorer2 = _app.ActiveExplorer();
@ -77,43 +77,6 @@ namespace Acacia.ZPush
#endregion
#region Timers
public void Delayed(int millis, System.Action action)
{
RegisterTimer(millis, action, false);
}
public void Timed(int millis, System.Action action)
{
RegisterTimer(millis, action, true);
}
private void RegisterTimer(int millis, System.Action action, bool repeat)
{
System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
timer.Interval = millis;
timer.Tick += (s, eargs) =>
{
try
{
action();
if (!repeat)
{
timer.Enabled = false;
timer.Dispose();
}
}
catch(System.Exception e)
{
Logger.Instance.Trace(this, "Exception in timer: {0}", e);
}
};
timer.Start();
}
#endregion
#region Accounts
public delegate void AccountHandler(ZPushAccount account);
@ -134,18 +97,15 @@ namespace Acacia.ZPush
/// Handles a new account.
/// </summary>
/// <param name="account">The account.</param>
/// <param name="isExisting">True if the account is an existing account, false if
/// it is a new account</param>
internal void OnAccountDiscovered(ZPushAccount account, bool isExisting)
internal void OnAccountDiscovered(ZPushAccount account)
{
// Notify any account listeners
if (AccountDiscovered != null)
AccountDiscovered(account);
AccountDiscovered?.Invoke(account);
// Register any events
HandleFolderWatchers(account);
if (account.HasPassword)
if (account.Account.HasPassword)
{
// Send an OOF request to get the OOF state and capabilities
Tasks.Task(null, "ZPushCheck: " + account.DisplayName, () =>
@ -274,7 +234,7 @@ namespace Acacia.ZPush
private void HandleFolderWatchers(ZPushAccount account)
{
// We need to keep the object alive to keep receiving events
_rootFolder = new ZPushFolder(this, account.Store.GetRootFolder());
_rootFolder = new ZPushFolder(this, account.Account.Store.GetRootFolder());
}
public void WatchFolder(FolderRegistration folder, FolderEventHandler handler, FolderEventHandler changedHandler = null)
@ -367,7 +327,7 @@ namespace Acacia.ZPush
string entryId = folder.EntryID;
using (IStore store = folder.Store)
using (IStore store = folder.GetStore())
{
foreach(DefaultFolder defaultFolderId in BLACKLISTED_MAIL_FOLDERS)
{