/// Copyright 2018 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. /// /// Consult LICENSE file for details using Acacia.Stubs; using Acacia.Stubs.OutlookWrappers; using Acacia.Utils; using Acacia.ZPush.Connect; using Microsoft.Win32; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Security; using System.Text; using System.Threading; using System.Threading.Tasks; using Acacia.Native; using Acacia.Native.MAPI; using NSOutlook = Microsoft.Office.Interop.Outlook; namespace Acacia.ZPush { [TypeConverter(typeof(ExpandableObjectConverter))] public class ZPushAccount : LogContext { #region Miscellaneous private readonly ZPushAccounts _zPushAccounts; private readonly IAccount _account; internal ZPushAccount(ZPushAccounts zPushAccounts, IAccount account) { this._zPushAccounts = zPushAccounts; this._account = account; } [Browsable(false)] public IAccount Account { get { return _account; } } public string DisplayName { get { return _account.SmtpAddress; } } public string DeviceId { get { return _account.DeviceId; } } [Browsable(false)] public string LogContextId { get { return "ZPushAccount(" + _account.SmtpAddress + ")"; } } public override string ToString() { return _account.SmtpAddress; } #endregion #region Identification and capabilities public enum ConfirmationType { Unknown, IsZPush, IsNotZPush } public ConfirmationType Confirmed { get; private set; } public delegate void ConfirmationHandler(ZPushAccount account); private ConfirmationHandler _confirmedChanged; public event ConfirmationHandler ConfirmedChanged { add { _confirmedChanged += value; if (Confirmed != ConfirmationType.Unknown) value(this); } remove { _confirmedChanged -= value; } } public ZPushCapabilities Capabilities { get; private set; } public string GABFolder { get; private set; } public string GABFolderLinked { get; private set; } public ZPushVersion ZPushVersion { get; private set; } public string ServerSignaturesHash { get; private set; } public void LinkedGABFolder(IFolder folder) { GABFolderLinked = folder.EntryID; } internal void OnConfirmationResponse(ZPushConnection.Response response) { Capabilities = response.Capabilities; // TODO: move these properties to the features? Though it's nice to have them here for the debug dialog GABFolder = response.GABName; ZPushVersion = ZPushVersion.FromString(response.ZPushVersion); ServerSignaturesHash = response.SignaturesHash; Confirmed = Capabilities == null ? ConfirmationType.IsNotZPush : ConfirmationType.IsZPush; Logger.Instance.Info(this, "ZPush confirmation: {0} -> {1}, {2}, {3} -> {4}", Confirmed, Capabilities, GABFolder, response.ZPushVersion, ZPushVersion); _confirmedChanged?.Invoke(this); } #endregion #region Connections /// /// Creates a new connection to the server for this account. /// /// If specified, a cancellation token for the connection. /// The connection. The caller must dispose this when no longer needed. public ZPushConnection Connect(CancellationToken? cancel = null) { return new ZPushConnection(this, null); } #endregion #region Feature-specific data private readonly ConcurrentDictionary _featureData = new ConcurrentDictionary(); private string FeatureKey(Features.Feature feature, string key) { return feature.Name + ":" + key; } /// /// Retrieves feature-specific data. /// /// The type of the data. /// The feature owning the data. /// A key identifying the data. This is unique per feature. /// The data, or null if no data was found. public DataType GetFeatureData(Features.Feature feature, string key) { object val = null; _featureData.TryGetValue(FeatureKey(feature, key), out val); return (DataType)val; } /// /// Sets feature-specific data on the account. This can be used to cache data for an account /// that will only be updated periodically. /// /// The feature owning the data. /// A key indentifying the data. This is unique per feature. /// The data. Specify null to remove the entry. public void SetFeatureData(Features.Feature feature, string key, object data) { if (data == null) { object dummy; _featureData.TryRemove(FeatureKey(feature, key), out dummy); } else { _featureData[FeatureKey(feature, key)] = data; } } #endregion #region Account sharing public string ShareFor { get { return RegistryUtil.GetValueString(Account.RegistryBaseKey, OutlookConstants.REG_VAL_KOE_SHARE_FOR, null); } } public bool IsShare { get { return ShareFor != null; } } public string ShareUserName { get { if (ShareFor == null) return null; int index = Account.UserName.IndexOf("#"); if (index < 0) return null; return Account.UserName.Substring(index + 1); } } [Browsable(false)] public ZPushAccount ShareForAccount { get { if (ShareFor == null) return null; return _zPushAccounts.GetAccount(ShareFor); } } [Browsable(false)] public ZPushAccount[] SharedAccounts { get { if (ShareFor != null) return new ZPushAccount[0]; List shares = new List(); foreach (ZPushAccount account in _zPushAccounts.GetAccounts()) { if (account == this) continue; if (account.ShareFor != null && account.ShareFor == this.Account.SmtpAddress) shares.Add(account); } return shares.ToArray(); } } public ZPushAccount FindSharedAccount(string username) { if (ShareFor != null) return null; List shares = new List(); foreach (ZPushAccount account in _zPushAccounts.GetAccounts()) { if (account == this) continue; if (account.ShareFor != null && account.ShareFor == this.Account.SmtpAddress && account.ShareUserName == username) return account; } return null; } public bool ShowReminders { get { return RegistryUtil.GetValueDword(Account.RegistryBaseKey, OutlookConstants.REG_VAL_SHOW_REMINDERS, 1) != 0; } set { RegistryUtil.SetValueDword(Account.RegistryBaseKey, OutlookConstants.REG_VAL_SHOW_REMINDERS, value ? 1 : 0); } } #endregion #region Signatures public string LocalSignaturesHash { get { return RegistryUtil.GetValueString(Account.RegistryBaseKey, OutlookConstants.REG_VAL_CURRENT_SIGNATURE, null); } set { RegistryUtil.SetValueString(Account.RegistryBaseKey, OutlookConstants.REG_VAL_CURRENT_SIGNATURE, value); } } public string SignatureNewMessage { get { return RegistryUtil.GetValueString(Account.RegistryBaseKey, OutlookConstants.REG_VAL_NEW_SIGNATURE, null); } set { Account.SetAccountProp(OutlookConstants.PROP_NEW_MESSAGE_SIGNATURE, value); } } public string SignatureReplyForwardMessage { get { return RegistryUtil.GetValueString(Account.RegistryBaseKey, OutlookConstants.REG_VAL_REPLY_FORWARD_SIGNATURE, null); } set { Account.SetAccountProp(OutlookConstants.PROP_REPLY_SIGNATURE, value); } } #endregion #region Sync time frame public SyncTimeFrame SyncTimeFrame { get { int val = RegistryUtil.GetValueDword(Account.RegistryBaseKey, OutlookConstants.REG_VAL_SYNC_TIMEFRAME, -1); // Check for default (Outlook) values if (val < 0) { if (EASSyncOneMonth) return SyncTimeFrame.MONTH_1; return SyncTimeFrame.ALL; } SyncTimeFrame frame = (SyncTimeFrame)val; // If the timeframe exceeds one month, but Outlook is set to one month, only one month will be synced. if (!frame.IsOneMonthOrLess() && EASSyncOneMonth) return SyncTimeFrame.MONTH_1; return frame; } set { if (value != SyncTimeFrame) { // Set the outlook property EASSyncOneMonth = value.IsOneMonthOrLess(); // And the registry value RegistryUtil.SetValueDword(Account.RegistryBaseKey, OutlookConstants.REG_VAL_SYNC_TIMEFRAME, (int)value); } } } private bool EASSyncOneMonth { get { return RegistryUtil.GetValueDword(Account.RegistryBaseKey, OutlookConstants.REG_VAL_SYNC_SLIDER, -1) == 1; } set { if (value != EASSyncOneMonth) { Account.SetAccountProp(OutlookConstants.PROP_SYNC_1_MONTH, value ? (uint)1 : (uint)0); } } } #endregion #region Send as private const string PREFIX_SEND_AS_SYNC = "KOE SendAs Sync "; private const string PREFIX_SEND_AS_BACKEND = "KOE SendAs Backend "; public void SetSendAsAddress(BackendId id, string sendAsAddress) { _account[PREFIX_SEND_AS_BACKEND + id] = sendAsAddress; } public string GetSendAsAddress(BackendId id) { return _account[PREFIX_SEND_AS_BACKEND + id]; } public void SetSendAsAddress(SyncId id, string sendAsAddress) { _account[PREFIX_SEND_AS_SYNC + id] = sendAsAddress; } public string GetSendAsAddress(SyncId id) { return _account[PREFIX_SEND_AS_SYNC + id]; } #endregion } }