mirror of
				https://github.com/Kopano-dev/kopano-ol-extension.git
				synced 2023-10-10 11:37:40 +00:00 
			
		
		
		
	[KOE-17] Basic infrastructure is working, still need replacement of placeholders
This commit is contained in:
		@@ -274,16 +274,17 @@
 | 
				
			|||||||
    </Compile>
 | 
					    </Compile>
 | 
				
			||||||
    <Compile Include="Features\SendAs\FeatureSendAs.cs" />
 | 
					    <Compile Include="Features\SendAs\FeatureSendAs.cs" />
 | 
				
			||||||
    <Compile Include="Features\SharedFolders\FolderTreeNode.cs" />
 | 
					    <Compile Include="Features\SharedFolders\FolderTreeNode.cs" />
 | 
				
			||||||
 | 
					    <Compile Include="Features\Signatures\FeatureSignatures.cs" />
 | 
				
			||||||
    <Compile Include="GlobalOptions.cs" />
 | 
					    <Compile Include="GlobalOptions.cs" />
 | 
				
			||||||
    <Compile Include="Logging.cs" />
 | 
					    <Compile Include="Logging.cs" />
 | 
				
			||||||
    <Compile Include="Native\IOleWindow.cs" />
 | 
					    <Compile Include="Native\IOleWindow.cs" />
 | 
				
			||||||
 | 
					    <Compile Include="Native\IOlkAccount.cs" />
 | 
				
			||||||
    <Compile Include="Native\MAPI\Binary.cs" />
 | 
					    <Compile Include="Native\MAPI\Binary.cs" />
 | 
				
			||||||
    <Compile Include="Native\MAPI\IMAPIContainer.cs" />
 | 
					    <Compile Include="Native\MAPI\IMAPIContainer.cs" />
 | 
				
			||||||
    <Compile Include="Native\MAPI\IMAPIFolder.cs" />
 | 
					    <Compile Include="Native\MAPI\IMAPIFolder.cs" />
 | 
				
			||||||
    <Compile Include="Native\MAPI\IMAPIProp.cs" />
 | 
					    <Compile Include="Native\MAPI\IMAPIProp.cs" />
 | 
				
			||||||
    <Compile Include="Native\MAPI\Property.cs" />
 | 
					    <Compile Include="Native\MAPI\Property.cs" />
 | 
				
			||||||
    <Compile Include="Native\MAPI\Restriction.cs" />
 | 
					    <Compile Include="Native\MAPI\Restriction.cs" />
 | 
				
			||||||
    <Compile Include="Native\NativeEncoder.cs" />
 | 
					 | 
				
			||||||
    <Compile Include="OutlookConstants.cs" />
 | 
					    <Compile Include="OutlookConstants.cs" />
 | 
				
			||||||
    <Compile Include="SearchQuery.cs" />
 | 
					    <Compile Include="SearchQuery.cs" />
 | 
				
			||||||
    <Compile Include="Stubs\Enums.cs" />
 | 
					    <Compile Include="Stubs\Enums.cs" />
 | 
				
			||||||
@@ -299,6 +300,8 @@
 | 
				
			|||||||
    <Compile Include="Stubs\IOutlookWindow.cs" />
 | 
					    <Compile Include="Stubs\IOutlookWindow.cs" />
 | 
				
			||||||
    <Compile Include="Stubs\IPicture.cs" />
 | 
					    <Compile Include="Stubs\IPicture.cs" />
 | 
				
			||||||
    <Compile Include="Stubs\IRecipient.cs" />
 | 
					    <Compile Include="Stubs\IRecipient.cs" />
 | 
				
			||||||
 | 
					    <Compile Include="Stubs\ISignature.cs" />
 | 
				
			||||||
 | 
					    <Compile Include="Stubs\ISignatures.cs" />
 | 
				
			||||||
    <Compile Include="Stubs\IStores.cs" />
 | 
					    <Compile Include="Stubs\IStores.cs" />
 | 
				
			||||||
    <Compile Include="Stubs\ISyncObject.cs" />
 | 
					    <Compile Include="Stubs\ISyncObject.cs" />
 | 
				
			||||||
    <Compile Include="Stubs\OutlookWrappers\AccountWrapper.cs" />
 | 
					    <Compile Include="Stubs\OutlookWrappers\AccountWrapper.cs" />
 | 
				
			||||||
@@ -312,6 +315,8 @@
 | 
				
			|||||||
    <Compile Include="Stubs\OutlookWrappers\OutlookItemWrapper.cs" />
 | 
					    <Compile Include="Stubs\OutlookWrappers\OutlookItemWrapper.cs" />
 | 
				
			||||||
    <Compile Include="Stubs\OutlookWrappers\PictureWrapper.cs" />
 | 
					    <Compile Include="Stubs\OutlookWrappers\PictureWrapper.cs" />
 | 
				
			||||||
    <Compile Include="Stubs\OutlookWrappers\RecipientWrapper.cs" />
 | 
					    <Compile Include="Stubs\OutlookWrappers\RecipientWrapper.cs" />
 | 
				
			||||||
 | 
					    <Compile Include="Stubs\OutlookWrappers\SignaturesWrapper.cs" />
 | 
				
			||||||
 | 
					    <Compile Include="Stubs\OutlookWrappers\SignatureWrapper.cs" />
 | 
				
			||||||
    <Compile Include="Stubs\OutlookWrappers\StoresWrapper.cs" />
 | 
					    <Compile Include="Stubs\OutlookWrappers\StoresWrapper.cs" />
 | 
				
			||||||
    <Compile Include="Stubs\OutlookWrappers\SyncObjectWrapper.cs" />
 | 
					    <Compile Include="Stubs\OutlookWrappers\SyncObjectWrapper.cs" />
 | 
				
			||||||
    <Compile Include="Stubs\Wrappers.cs" />
 | 
					    <Compile Include="Stubs\Wrappers.cs" />
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -74,7 +74,8 @@ namespace Acacia
 | 
				
			|||||||
        public const string ZPUSH_HEADER_CLIENT_CAPABILITIES = "X-Push-Plugin-Capabilities";
 | 
					        public const string ZPUSH_HEADER_CLIENT_CAPABILITIES = "X-Push-Plugin-Capabilities";
 | 
				
			||||||
        public const string ZPUSH_HEADER_PLUGIN = "X-Push-Plugin";
 | 
					        public const string ZPUSH_HEADER_PLUGIN = "X-Push-Plugin";
 | 
				
			||||||
        public const string ZPUSH_HEADER_VERSION = "X-Z-Push-Version";
 | 
					        public const string ZPUSH_HEADER_VERSION = "X-Z-Push-Version";
 | 
				
			||||||
        
 | 
					        public const string ZPUSH_HEADER_SIGNATURES_HASH = "X-Push-Signatures-Hash";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #endregion
 | 
					        #endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -142,6 +142,38 @@ namespace Acacia
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public class StringOption : Option<string>
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            private readonly string _defaultValue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            public StringOption(string token, string defaultValue)
 | 
				
			||||||
 | 
					            :
 | 
				
			||||||
 | 
					            base(token)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                this._defaultValue = defaultValue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            public override string GetToken(string value)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                if (value.Equals(_defaultValue))
 | 
				
			||||||
 | 
					                    return null;
 | 
				
			||||||
 | 
					                return Token + "=" + value.ToString();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            public override string GetValue(string value)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                if (string.IsNullOrEmpty(value))
 | 
				
			||||||
 | 
					                    return _defaultValue;
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    if (value.ToLower().StartsWith(Token.ToLower() + "="))
 | 
				
			||||||
 | 
					                        value = value.Substring(Token.Length + 1);
 | 
				
			||||||
 | 
					                    return value;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // General
 | 
					        // General
 | 
				
			||||||
        public static readonly BoolOption ENABLED = new BoolOption("", true);
 | 
					        public static readonly BoolOption ENABLED = new BoolOption("", true);
 | 
				
			||||||
        public static readonly BoolOption FEATURE_DISABLED_DEFAULT = new BoolOption("", false);
 | 
					        public static readonly BoolOption FEATURE_DISABLED_DEFAULT = new BoolOption("", false);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -87,7 +87,6 @@ namespace Acacia.Features.SecondaryContacts
 | 
				
			|||||||
            // So, when the folder is detected, we make it invisible and perform steps 1 and 2. We issue a warning
 | 
					            // So, when the folder is detected, we make it invisible and perform steps 1 and 2. We issue a warning
 | 
				
			||||||
            // that Outlook must be restarted. When the folder is detected again and is invisible, that means we've restarted
 | 
					            // that Outlook must be restarted. When the folder is detected again and is invisible, that means we've restarted
 | 
				
			||||||
            // At this point the name is patched and the folder is made visible. 
 | 
					            // At this point the name is patched and the folder is made visible. 
 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (!folder.AttrHidden)
 | 
					            if (!folder.AttrHidden)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                // Stage 1
 | 
					                // Stage 1
 | 
				
			||||||
@@ -113,12 +112,12 @@ namespace Acacia.Features.SecondaryContacts
 | 
				
			|||||||
                // Stage 2
 | 
					                // Stage 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // Patch the name
 | 
					                // Patch the name
 | 
				
			||||||
                 Logger.Instance.Trace(this, "Patching name");
 | 
					                Logger.Instance.Trace(this, "Patching name");
 | 
				
			||||||
                 folder.Name = strippedName;
 | 
					                folder.Name = strippedName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                 // Show it
 | 
					                // Show it
 | 
				
			||||||
                 folder.AttrHidden = false;
 | 
					                folder.AttrHidden = false;
 | 
				
			||||||
                 Logger.Instance.Debug(this, "Shown secondary contacts folder: {0}", strippedName);
 | 
					                Logger.Instance.Debug(this, "Shown secondary contacts folder: {0}", strippedName);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            Logger.Instance.Debug(this, "Patching done: {0}: {1}", strippedName, folder.AttrHidden);
 | 
					            Logger.Instance.Debug(this, "Patching done: {0}: {1}", strippedName, folder.AttrHidden);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,186 @@
 | 
				
			|||||||
 | 
					/// Copyright 2017 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 Acacia.Stubs;
 | 
				
			||||||
 | 
					using Acacia.Stubs.OutlookWrappers;
 | 
				
			||||||
 | 
					using Acacia.Utils;
 | 
				
			||||||
 | 
					using Acacia.ZPush;
 | 
				
			||||||
 | 
					using Acacia.ZPush.Connect;
 | 
				
			||||||
 | 
					using Acacia.ZPush.Connect.Soap;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Drawing;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Runtime.InteropServices;
 | 
				
			||||||
 | 
					using System.Text;
 | 
				
			||||||
 | 
					using System.Threading;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using System.Windows.Forms;
 | 
				
			||||||
 | 
					using static Acacia.DebugOptions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Acacia.Features.Signatures
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    [AcaciaOption("Provides the possibility to synchronise signatures from the server.")]
 | 
				
			||||||
 | 
					    public class FeatureSignatures : Feature
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        #region Debug options
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [AcaciaOption("The format for local names of synchronised signatures, to prevent overwriting local signatures. May contain %account% and %name%.")]
 | 
				
			||||||
 | 
					        public string SignatureLocalName
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            get { return GetOption(OPTION_SIGNATURE_LOCAL_NAME); }
 | 
				
			||||||
 | 
					            set { SetOption(OPTION_SIGNATURE_LOCAL_NAME, value); }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        private static readonly StringOption OPTION_SIGNATURE_LOCAL_NAME = new StringOption("SignatureLocalName", "%name% (KOE-%account%)");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public override void Startup()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Watcher.AccountDiscovered += Watcher_AccountDiscovered;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void Watcher_AccountDiscovered(ZPushAccount account)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            account.ConfirmedChanged += Account_ConfirmedChanged;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void Account_ConfirmedChanged(ZPushAccount account)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            // TODO: make a helper to register for all zpush accounts with specific capabilities, best even
 | 
				
			||||||
 | 
					            //       the feature's capabilities
 | 
				
			||||||
 | 
					            if (account.Confirmed == ZPushAccount.ConfirmationType.IsZPush &&
 | 
				
			||||||
 | 
					                account.Capabilities.Has("signatures"))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                Logger.Instance.Trace(this, "Checking signature hash for account {0}: {1}", account, account.ServerSignaturesHash);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Fetch signatures if there is a change
 | 
				
			||||||
 | 
					                if (account.ServerSignaturesHash != account.Account.LocalSignaturesHash)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    try
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        Logger.Instance.Debug(this, "Updating signatures: {0}", account);
 | 
				
			||||||
 | 
					                        FetchSignatures(account);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        // Store updated hash
 | 
				
			||||||
 | 
					                        account.Account.LocalSignaturesHash = account.ServerSignaturesHash;
 | 
				
			||||||
 | 
					                        Logger.Instance.Debug(this, "Updated signatures: {0}", account);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    catch (Exception e)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        Logger.Instance.Error(this, "Error fetching signatures: {0}: {1}", account, e);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					        // Prevent field assignment warnings
 | 
				
			||||||
 | 
					        #pragma warning disable 0649
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private class Signature
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            public string id;
 | 
				
			||||||
 | 
					            public string name;
 | 
				
			||||||
 | 
					            public string content;
 | 
				
			||||||
 | 
					            public bool isHTML;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private class GetSignatures
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            public Dictionary<string, Signature> all;
 | 
				
			||||||
 | 
					            public string new_message;
 | 
				
			||||||
 | 
					            public string replyforward_message;
 | 
				
			||||||
 | 
					            public string hash;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #pragma warning restore 0649
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private class GetSignaturesRequest : SoapRequest<GetSignatures>
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void FetchSignatures(ZPushAccount account)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Logger.Instance.Debug(this, "Fetching signatures for account {0}", account);
 | 
				
			||||||
 | 
					            using (ZPushConnection connection = account.Connect())
 | 
				
			||||||
 | 
					            using (ZPushWebServiceInfo infoService = connection.InfoService)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                GetSignatures result = infoService.Execute(new GetSignaturesRequest());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Store the signatures
 | 
				
			||||||
 | 
					                Dictionary<object, string> fullNames = new Dictionary<object, string>();
 | 
				
			||||||
 | 
					                using (ISignatures signatures = ThisAddIn.Instance.GetSignatures())
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    foreach (Signature signature in result.all.Values)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        string name = StoreSignature(signatures, account, signature);
 | 
				
			||||||
 | 
					                        fullNames.Add(signature.id, name);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Set default signatures if available and none are set
 | 
				
			||||||
 | 
					                if (!string.IsNullOrEmpty(result.new_message) && string.IsNullOrEmpty(account.Account.SignatureNewMessage))
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    account.Account.SignatureNewMessage = fullNames[result.new_message];
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (!string.IsNullOrEmpty(result.replyforward_message) && string.IsNullOrEmpty(account.Account.SignatureReplyForwardMessage))
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    account.Account.SignatureReplyForwardMessage = fullNames[result.replyforward_message];
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private string StoreSignature(ISignatures signatures, ZPushAccount account, Signature signatureInfo)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            string name = SignatureLocalName.ReplacePercentStrings(new Dictionary<string, string>
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                { "account", account.DisplayName },
 | 
				
			||||||
 | 
					                { "name", signatureInfo.name }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Remove any existing signature
 | 
				
			||||||
 | 
					            try
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ISignature signature = signatures.Get(name);
 | 
				
			||||||
 | 
					                if (signature != null)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    try
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        signature.Delete();
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    finally
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        signature.Dispose();
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            catch(Exception e)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                Logger.Instance.Error(this, "Unable to delete signature {0}: {1}", name, e);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Create the new signature
 | 
				
			||||||
 | 
					            using (ISignature signature = signatures.Add(name))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                signature.SetContent(signatureInfo.content, signatureInfo.isHTML ? ISignatureFormat.HTML : ISignatureFormat.Text);
 | 
				
			||||||
 | 
					                // TODO: generate text version if we get an HTML?
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return name;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,53 @@
 | 
				
			|||||||
 | 
					using Acacia.Native.MAPI;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Runtime.InteropServices;
 | 
				
			||||||
 | 
					using System.Text;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Acacia.Native
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    [StructLayout(LayoutKind.Explicit)]
 | 
				
			||||||
 | 
					    unsafe public struct ACCT_VARIANT
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        [FieldOffset(0)]
 | 
				
			||||||
 | 
					        public uint dwType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [FieldOffset(4)]
 | 
				
			||||||
 | 
					        public uint dwAlignPad;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [FieldOffset(8)]
 | 
				
			||||||
 | 
					        public char* lpszW;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [ComImport]
 | 
				
			||||||
 | 
					    [Guid("9240a6d2-af41-11d2-8c3b-00104b2a6676")]
 | 
				
			||||||
 | 
					    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 | 
				
			||||||
 | 
					    unsafe public interface IOlkAccount
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // IOlkErrorUnknown
 | 
				
			||||||
 | 
					        void IOlkErrorUnknown_GetLastError();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        void IOlkAccount_Placeholder1();
 | 
				
			||||||
 | 
					        void IOlkAccount_Placeholder2();
 | 
				
			||||||
 | 
					        void IOlkAccount_Placeholder3();
 | 
				
			||||||
 | 
					        void IOlkAccount_Placeholder4();
 | 
				
			||||||
 | 
					        void IOlkAccount_Placeholder5();
 | 
				
			||||||
 | 
					        void IOlkAccount_Placeholder6();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        void GetAccountInfo(Guid* pclsidType, int* pcCategories, Guid** prgclsidCategory);
 | 
				
			||||||
 | 
					        void GetProp(PropTag dwProp, ACCT_VARIANT *pVar);
 | 
				
			||||||
 | 
					        void SetProp(PropTag dwProp, ACCT_VARIANT *pVar);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        void IOlkAccount_Placeholder7();
 | 
				
			||||||
 | 
					        void IOlkAccount_Placeholder8();
 | 
				
			||||||
 | 
					        void IOlkAccount_Placeholder9();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        void FreeMemory(byte* pv);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        void IOlkAccount_Placeholder10();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        void SaveChanges(uint dwFlags);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -54,6 +54,15 @@ namespace Acacia.Native.MAPI
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            return SearchQuery.PropertyIdentifier.FromTag(prop, (ushort)type);
 | 
					            return SearchQuery.PropertyIdentifier.FromTag(prop, (ushort)type);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static PropTag FromInt(int v)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return new PropTag()
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                prop = (ushort)((v & 0xFFFF0000) >> 16),
 | 
				
			||||||
 | 
					                type = (PropType)(v & 0xFFFF)
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -41,6 +41,10 @@ namespace Acacia
 | 
				
			|||||||
        public const string REG_VAL_DELIVERY_STORE = "Delivery Store EntryID";
 | 
					        public const string REG_VAL_DELIVERY_STORE = "Delivery Store EntryID";
 | 
				
			||||||
        public const string REG_VAL_DELIVERY_FOLDER = "Delivery Folder EntryID";
 | 
					        public const string REG_VAL_DELIVERY_FOLDER = "Delivery Folder EntryID";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public const string REG_VAL_NEW_SIGNATURE = "New Signature";
 | 
				
			||||||
 | 
					        public const string REG_VAL_REPLY_FORWARD_SIGNATURE = "Reply-Forward Signature";
 | 
				
			||||||
 | 
					        public const string REG_VAL_CURRENT_SIGNATURE = "KOE Signature Digest";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public const string REG_VAL_NEXT_ACCOUNT_ID = "NextAccountID";
 | 
					        public const string REG_VAL_NEXT_ACCOUNT_ID = "NextAccountID";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #endregion
 | 
					        #endregion
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -49,5 +49,23 @@ namespace Acacia.Stubs
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        string DomainName { get; }
 | 
					        string DomainName { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // TODO: this is really a Z-Push thing, but it's here to store it in the registry
 | 
				
			||||||
 | 
					        string LocalSignaturesHash
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            get;
 | 
				
			||||||
 | 
					            set;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        string SignatureNewMessage
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            get;
 | 
				
			||||||
 | 
					            set;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        string SignatureReplyForwardMessage
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            get;
 | 
				
			||||||
 | 
					            set;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -86,5 +86,9 @@ namespace Acacia.Stubs
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #endregion
 | 
					        #endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ISignatures GetSignatures();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        void InUI(Action action);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										20
									
								
								src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/ISignature.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/ISignature.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Text;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Acacia.Stubs
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public enum ISignatureFormat
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        HTML,
 | 
				
			||||||
 | 
					        Text
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public interface ISignature : IDisposable
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        void Delete();
 | 
				
			||||||
 | 
					        void SetContent(string content, ISignatureFormat format);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										14
									
								
								src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/ISignatures.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/ISignatures.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Text;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Acacia.Stubs
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public interface ISignatures : IDisposable
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ISignature Get(string name);
 | 
				
			||||||
 | 
					        ISignature Add(string name);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,4 +1,7 @@
 | 
				
			|||||||
/// Copyright 2017 Kopano b.v.
 | 
					
 | 
				
			||||||
 | 
					using Acacia.Native;
 | 
				
			||||||
 | 
					using Acacia.Native.MAPI;
 | 
				
			||||||
 | 
					/// Copyright 2017 Kopano b.v.
 | 
				
			||||||
/// 
 | 
					/// 
 | 
				
			||||||
/// This program is free software: you can redistribute it and/or modify
 | 
					/// 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,
 | 
					/// it under the terms of the GNU Affero General Public License, version 3,
 | 
				
			||||||
@@ -13,26 +16,29 @@
 | 
				
			|||||||
/// along with this program.If not, see<http://www.gnu.org/licenses/>.
 | 
					/// along with this program.If not, see<http://www.gnu.org/licenses/>.
 | 
				
			||||||
/// 
 | 
					/// 
 | 
				
			||||||
/// Consult LICENSE file for details
 | 
					/// Consult LICENSE file for details
 | 
				
			||||||
 | 
					 | 
				
			||||||
using Acacia.Utils;
 | 
					using Acacia.Utils;
 | 
				
			||||||
using Microsoft.Win32;
 | 
					using Microsoft.Win32;
 | 
				
			||||||
using System;
 | 
					using System;
 | 
				
			||||||
using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
using System.ComponentModel;
 | 
					using System.ComponentModel;
 | 
				
			||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Runtime.InteropServices;
 | 
				
			||||||
using System.Security;
 | 
					using System.Security;
 | 
				
			||||||
using System.Text;
 | 
					using System.Text;
 | 
				
			||||||
 | 
					using System.Threading;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using System.Windows.Forms;
 | 
				
			||||||
 | 
					using NSOutlook = Microsoft.Office.Interop.Outlook;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Acacia.Stubs.OutlookWrappers
 | 
					namespace Acacia.Stubs.OutlookWrappers
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    [TypeConverter(typeof(ExpandableObjectConverter))]
 | 
					    [TypeConverter(typeof(ExpandableObjectConverter))]
 | 
				
			||||||
    class AccountWrapper : DisposableWrapper, IAccount, LogContext
 | 
					    class AccountWrapper : ComWrapper<NSOutlook.Application>, IAccount, LogContext
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private readonly string _regPath;
 | 
					        private readonly string _regPath;
 | 
				
			||||||
        private readonly IStore _store;
 | 
					        private readonly IStore _store;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        internal AccountWrapper(string regPath, IStore store)
 | 
					        internal AccountWrapper(NSOutlook.Application item, string regPath, IStore store) : base(item)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            this._regPath = regPath;
 | 
					            this._regPath = regPath;
 | 
				
			||||||
            this._store = store;
 | 
					            this._store = store;
 | 
				
			||||||
@@ -44,6 +50,7 @@ namespace Acacia.Stubs.OutlookWrappers
 | 
				
			|||||||
        protected override void DoRelease()
 | 
					        protected override void DoRelease()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _store.Dispose();
 | 
					            _store.Dispose();
 | 
				
			||||||
 | 
					            base.DoRelease();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Browsable(false)]
 | 
					        [Browsable(false)]
 | 
				
			||||||
@@ -163,6 +170,83 @@ namespace Acacia.Stubs.OutlookWrappers
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public string LocalSignaturesHash
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            get
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return RegistryUtil.GetValueString(_regPath, OutlookConstants.REG_VAL_CURRENT_SIGNATURE, null);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            set
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                RegistryUtil.SetValueString(_regPath, OutlookConstants.REG_VAL_CURRENT_SIGNATURE, value);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        public string SignatureNewMessage
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            get
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return RegistryUtil.GetValueString(_regPath, OutlookConstants.REG_VAL_NEW_SIGNATURE, null);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            set
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                // TODO: constant for account
 | 
				
			||||||
 | 
					                SetAccountProp(PropTag.FromInt(0x0016001F), value);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        unsafe private void SetAccountProp(PropTag propTag, string value)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            // Use IOlkAccount to notify while we're running
 | 
				
			||||||
 | 
					            // IOlkAccount can only be accessed on main thread
 | 
				
			||||||
 | 
					            ThisAddIn.Instance.InUI(() =>
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                using (ComRelease com = new ComRelease())
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    NSOutlook.Account account = com.Add(FindAccountObject());
 | 
				
			||||||
 | 
					                    IOlkAccount olk = com.Add(account.IOlkAccount);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    fixed (char* ptr = value.ToCharArray())
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        ACCT_VARIANT val = new ACCT_VARIANT()
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            dwType = (uint)PropType.UNICODE,
 | 
				
			||||||
 | 
					                            lpszW = ptr
 | 
				
			||||||
 | 
					                        };
 | 
				
			||||||
 | 
					                        olk.SetProp(propTag, &val);
 | 
				
			||||||
 | 
					                        olk.SaveChanges(0);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private NSOutlook.Account FindAccountObject()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            using (ComRelease com = new ComRelease())
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                NSOutlook.NameSpace session = com.Add(_item.Session);
 | 
				
			||||||
 | 
					                foreach(NSOutlook.Account account in session.Accounts.ComEnum(false))
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    if (account.SmtpAddress == this.SmtpAddress)
 | 
				
			||||||
 | 
					                        return account;
 | 
				
			||||||
 | 
					                    else
 | 
				
			||||||
 | 
					                        com.Add(account);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public string SignatureReplyForwardMessage
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            get
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return RegistryUtil.GetValueString(_regPath, OutlookConstants.REG_VAL_REPLY_FORWARD_SIGNATURE, null);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            set
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                SetAccountProp(PropTag.FromInt(0x0017001F), value);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #endregion
 | 
					        #endregion
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,17 +25,19 @@ using System.Diagnostics;
 | 
				
			|||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
using System.Reflection;
 | 
					using System.Reflection;
 | 
				
			||||||
using System.Text;
 | 
					using System.Text;
 | 
				
			||||||
 | 
					using System.Threading;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using System.Windows.Forms;
 | 
					using System.Windows.Forms;
 | 
				
			||||||
using NSOutlook = Microsoft.Office.Interop.Outlook;
 | 
					using NSOutlook = Microsoft.Office.Interop.Outlook;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Acacia.Stubs.OutlookWrappers
 | 
					namespace Acacia.Stubs.OutlookWrappers
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class AddInWrapper : IAddIn
 | 
					    class AddInWrapper : IAddIn
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private readonly NSOutlook.Application _app;
 | 
					        private readonly NSOutlook.Application _app;
 | 
				
			||||||
        private readonly ThisAddIn _thisAddIn;
 | 
					        private readonly ThisAddIn _thisAddIn;
 | 
				
			||||||
        private readonly StoresWrapper _stores;
 | 
					        private readonly StoresWrapper _stores;
 | 
				
			||||||
 | 
					        private readonly SynchronizationContext _sync;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public AddInWrapper(ThisAddIn thisAddIn)
 | 
					        public AddInWrapper(ThisAddIn thisAddIn)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@@ -51,6 +53,38 @@ namespace Acacia.Stubs.OutlookWrappers
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                ComRelease.Release(session);
 | 
					                ComRelease.Release(session);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // The synchronization context is needed to allow background tasks to jump back to the UI thread.
 | 
				
			||||||
 | 
					            // It's null in older versions of .Net, this fixes that
 | 
				
			||||||
 | 
					            if (SynchronizationContext.Current == null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            _sync = SynchronizationContext.Current;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ISignatures GetSignatures()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return new SignaturesWrapper();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void InUI(Action action)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Exception x = null;
 | 
				
			||||||
 | 
					            _sync.Send((_) =>
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                try
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    action();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                catch(Exception e)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    x = e;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }, null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (x != null)
 | 
				
			||||||
 | 
					                throw x;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public void SendReceive(IAccount account)
 | 
					        public void SendReceive(IAccount account)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,80 @@
 | 
				
			|||||||
 | 
					using Acacia.Utils;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.IO;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Text;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Acacia.Stubs.OutlookWrappers
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    class SignatureWrapper : DisposableWrapper, ISignature
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private static readonly string[] SUFFIXES =
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "htm", "html", "rtf", "txt"
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private static string BasePath
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            get
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Microsoft\\Signatures";
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private readonly string _name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public SignatureWrapper(string name)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            this._name = name;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        protected override void DoRelease()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        internal static ISignature FindExisting(string name)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            foreach(string suffix in SUFFIXES)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                string path = GetPath(name, suffix);
 | 
				
			||||||
 | 
					                if (new FileInfo(path).Exists)
 | 
				
			||||||
 | 
					                    return new SignatureWrapper(name);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private static string GetPath(string name, string suffix)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return Path.ChangeExtension(Path.Combine(BasePath, name), suffix);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void Delete()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            foreach (string suffix in SUFFIXES)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                string path = GetPath(_name, suffix);
 | 
				
			||||||
 | 
					                FileInfo file = new FileInfo(path);
 | 
				
			||||||
 | 
					                if (file.Exists)
 | 
				
			||||||
 | 
					                    file.Delete();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            // TODO: additional files folder? We never create it
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void SetContent(string content, ISignatureFormat format)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            // Determine suffix
 | 
				
			||||||
 | 
					            string suffix = "txt";
 | 
				
			||||||
 | 
					            switch(format)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                case ISignatureFormat.HTML: suffix = "htm"; break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Write
 | 
				
			||||||
 | 
					            string path = GetPath(_name, suffix);
 | 
				
			||||||
 | 
					            File.WriteAllText(path, content);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,38 @@
 | 
				
			|||||||
 | 
					using Acacia.Utils;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Text;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Acacia.Stubs.OutlookWrappers
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    class SignaturesWrapper : DisposableWrapper, ISignatures
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public SignaturesWrapper()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        protected override void DoRelease()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ISignature Get(string name)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return SignatureWrapper.FindExisting(name);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ISignature Add(string name)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            // Check if exists
 | 
				
			||||||
 | 
					            using (ISignature existing = SignatureWrapper.FindExisting(name))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                if (existing != null)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    throw new ArgumentException("Signature " + name + " already exists");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return new SignatureWrapper(name);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -247,7 +247,7 @@ namespace Acacia.Stubs.OutlookWrappers
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                if (baseKey == null)
 | 
					                if (baseKey == null)
 | 
				
			||||||
                    return null;
 | 
					                    return null;
 | 
				
			||||||
                AccountWrapper account = new AccountWrapper(baseKey.Name, store);
 | 
					                AccountWrapper account = new AccountWrapper(_item.Application, baseKey.Name, store);
 | 
				
			||||||
                Register(account);
 | 
					                Register(account);
 | 
				
			||||||
                return account;
 | 
					                return account;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -101,13 +101,6 @@ namespace Acacia
 | 
				
			|||||||
                int lcid = Application.LanguageSettings.get_LanguageID(Microsoft.Office.Core.MsoAppLanguageID.msoLanguageIDUI);
 | 
					                int lcid = Application.LanguageSettings.get_LanguageID(Microsoft.Office.Core.MsoAppLanguageID.msoLanguageIDUI);
 | 
				
			||||||
                Thread.CurrentThread.CurrentUICulture = new CultureInfo(lcid);
 | 
					                Thread.CurrentThread.CurrentUICulture = new CultureInfo(lcid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // The synchronization context is needed to allow background tasks to jump back to the UI thread.
 | 
					 | 
				
			||||||
                // It's null in older versions of .Net, this fixes that
 | 
					 | 
				
			||||||
                if (SynchronizationContext.Current == null)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext());
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // Create the watcher
 | 
					                // Create the watcher
 | 
				
			||||||
                Watcher = new ZPushWatcher(Instance);
 | 
					                Watcher = new ZPushWatcher(Instance);
 | 
				
			||||||
                OutlookUI.Watcher = Watcher;
 | 
					                OutlookUI.Watcher = Watcher;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,8 +43,13 @@ namespace Acacia.Utils
 | 
				
			|||||||
        public static object Convert(this Type type, object value)
 | 
					        public static object Convert(this Type type, object value)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            // For value types, null becomes a default instance
 | 
					            // For value types, null becomes a default instance
 | 
				
			||||||
            if (value == null && type.IsValueType)
 | 
					            if (value == null)
 | 
				
			||||||
                return Activator.CreateInstance(type);
 | 
					            {
 | 
				
			||||||
 | 
					                if (type.IsValueType)
 | 
				
			||||||
 | 
					                    return Activator.CreateInstance(type);
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                    return null;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Check if we need a conversion
 | 
					            // Check if we need a conversion
 | 
				
			||||||
            if (!type.IsAssignableFrom(value.GetType()))
 | 
					            if (!type.IsAssignableFrom(value.GetType()))
 | 
				
			||||||
@@ -89,11 +94,27 @@ namespace Acacia.Utils
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public static Type[] GetGenericArguments(this Type type, Type _base)
 | 
					        public static Type[] GetGenericArguments(this Type type, Type _base)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            if (type.IsGenericType && type.GetGenericTypeDefinition() == _base)
 | 
				
			||||||
 | 
					                return type.GetGenericArguments();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            IEnumerable<Type> bases = _base.IsInterface ? type.GetInterfaces() : type.AllBaseTypes();
 | 
					            IEnumerable<Type> bases = _base.IsInterface ? type.GetInterfaces() : type.AllBaseTypes();
 | 
				
			||||||
            return bases.Select(x =>
 | 
					            return bases.Select(x =>
 | 
				
			||||||
                (x.IsGenericType && x.GetGenericTypeDefinition() == _base) ? x.GetGenericArguments() : null
 | 
					                (x.IsGenericType && x.GetGenericTypeDefinition() == _base) ? x.GetGenericArguments() : null
 | 
				
			||||||
            ).FirstOrDefault();
 | 
					            ).FirstOrDefault();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static IEnumerable<string> DebugComTypeNames(object o)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            foreach (System.Reflection.Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                foreach (Type type in assembly.GetTypes())
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    if (type.IsInstanceOfType(o))
 | 
				
			||||||
 | 
					                        yield return type.FullName;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,13 +34,17 @@ namespace Acacia.Utils
 | 
				
			|||||||
            return RegToString(o);
 | 
					            return RegToString(o);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // TODO: remove, also above
 | 
					 | 
				
			||||||
        public static string GetValueString(string keyPath, string valueName, string defaultValue)
 | 
					        public static string GetValueString(string keyPath, string valueName, string defaultValue)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            object o = Registry.GetValue(keyPath, valueName, defaultValue);
 | 
					            object o = Registry.GetValue(keyPath, valueName, defaultValue);
 | 
				
			||||||
            return RegToString(o);
 | 
					            return RegToString(o);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static void SetValueString(string keyPath, string valueName, string value)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Registry.SetValue(keyPath, valueName, value);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static string RegToString(object o)
 | 
					        public static string RegToString(object o)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (o is byte[])
 | 
					            if (o is byte[])
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,6 +19,7 @@ using System.Collections.Generic;
 | 
				
			|||||||
using System.IO;
 | 
					using System.IO;
 | 
				
			||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
using System.Text;
 | 
					using System.Text;
 | 
				
			||||||
 | 
					using System.Text.RegularExpressions;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using System.Xml;
 | 
					using System.Xml;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -105,5 +106,25 @@ namespace Acacia.Utils
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        #endregion
 | 
					        #endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #region Formatting / Replacement
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static string ReplacePercentStrings(this string s, Dictionary<string, string> replacements)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return Regex.Replace(s, @"%(\w+)%", (m) => 
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                string replacement;
 | 
				
			||||||
 | 
					                var key = m.Groups[1].Value;
 | 
				
			||||||
 | 
					                if (replacements.TryGetValue(key, out replacement))
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    return Convert.ToString(replacement);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    return m.Groups[0].Value;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #endregion
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -81,7 +81,7 @@ namespace Acacia.ZPush.Connect.Soap
 | 
				
			|||||||
                return null;
 | 
					                return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Type-specific parsing
 | 
					            // Type-specific parsing
 | 
				
			||||||
            TypeHandler type = LookupType(part);
 | 
					            TypeHandler type = LookupType(part, expectedType);
 | 
				
			||||||
            return type.Deserialize(part, expectedType);
 | 
					            return type.Deserialize(part, expectedType);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -109,24 +109,43 @@ namespace Acacia.ZPush.Connect.Soap
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            public object Deserialize(XmlNode node, Type expectedType)
 | 
					            public object Deserialize(XmlNode node, Type expectedType)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (expectedType != null && _baseType != null)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    // Check if the expected type matches the type
 | 
					 | 
				
			||||||
                    if (!_baseType.IsAssignableFrom(expectedType))
 | 
					 | 
				
			||||||
                        throw new InvalidOperationException("Expected " + expectedType + ", found " + _baseType);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                object value = DeserializeContents(node, expectedType);
 | 
					                object value = DeserializeContents(node, expectedType);
 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (expectedType != null)
 | 
					                if (expectedType != null)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    // Check if it's the expected type
 | 
					                    // Try to convert it to the expected type
 | 
				
			||||||
                    return expectedType.Cast(value);
 | 
					                    return SoapConvert(expectedType, value);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                return value;
 | 
					                return value;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            protected object SoapConvert(Type type, object value)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                // Check if any conversion is needed 
 | 
				
			||||||
 | 
					                if (value != null && type.IsAssignableFrom(value.GetType()))
 | 
				
			||||||
 | 
					                    return value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (value != null)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    // Try Soap conversion
 | 
				
			||||||
 | 
					                    if (typeof(ISoapSerializable<>).IsGenericAssignableFrom(type))
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        // Get the serialization type
 | 
				
			||||||
 | 
					                        Type serializationType = type.GetGenericArguments(typeof(ISoapSerializable<>))[0];
 | 
				
			||||||
 | 
					                        if (serializationType.IsAssignableFrom(value.GetType()))
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            // Create the instance
 | 
				
			||||||
 | 
					                            return Activator.CreateInstance(type, value);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Or standard conversions
 | 
				
			||||||
 | 
					                return type.Convert(value);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            abstract protected object DeserializeContents(XmlNode node, Type expectedType);
 | 
					            abstract protected object DeserializeContents(XmlNode node, Type expectedType);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            abstract public void Serialize(string name, object value, StringBuilder s);
 | 
					            abstract public void Serialize(string name, object value, StringBuilder s);
 | 
				
			||||||
@@ -150,7 +169,7 @@ namespace Acacia.ZPush.Connect.Soap
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        private class TypeHandlerInt : TypeHandler
 | 
					        private class TypeHandlerInt : TypeHandler
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            public TypeHandlerInt() : base(SoapConstants.XMLNS_XSD, "int", typeof(int)) { }
 | 
					            public TypeHandlerInt() : base(SoapConstants.XMLNS_XSD, "int", typeof(long)) { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public override void Serialize(string name, object value, StringBuilder s)
 | 
					            public override void Serialize(string name, object value, StringBuilder s)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@@ -159,7 +178,7 @@ namespace Acacia.ZPush.Connect.Soap
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            protected override object DeserializeContents(XmlNode node, Type expectedType)
 | 
					            protected override object DeserializeContents(XmlNode node, Type expectedType)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                return int.Parse(node.InnerText);
 | 
					                return long.Parse(node.InnerText);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -230,7 +249,10 @@ namespace Acacia.ZPush.Connect.Soap
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                // Determine if the expected type is an ISoapSerializable
 | 
					                // Determine if the expected type is an ISoapSerializable
 | 
				
			||||||
                if (!typeof(ISoapSerializable<>).IsGenericAssignableFrom(expectedType))
 | 
					                if (!typeof(ISoapSerializable<>).IsGenericAssignableFrom(expectedType))
 | 
				
			||||||
                    throw new InvalidOperationException("Cannot parse type " + expectedType);
 | 
					                {
 | 
				
			||||||
 | 
					                    // Nope, try simple assignment
 | 
				
			||||||
 | 
					                    return DeserializeContentsRaw(node, expectedType);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // Get the serialization type
 | 
					                // Get the serialization type
 | 
				
			||||||
                Type serializationType = expectedType.GetGenericArguments(typeof(ISoapSerializable<>))[0];
 | 
					                Type serializationType = expectedType.GetGenericArguments(typeof(ISoapSerializable<>))[0];
 | 
				
			||||||
@@ -243,6 +265,32 @@ namespace Acacia.ZPush.Connect.Soap
 | 
				
			|||||||
                return CreateCustomInstance(values, serializationType, expectedType);
 | 
					                return CreateCustomInstance(values, serializationType, expectedType);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            private object DeserializeContentsRaw(XmlNode node, Type expectedType)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                // TODO: better error on failure
 | 
				
			||||||
 | 
					                // Get the values as a dictionary
 | 
				
			||||||
 | 
					                Dictionary<string, object> values = new Dictionary<string, object>();
 | 
				
			||||||
 | 
					                DeserializeMembers(node, expectedType, values);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // And assign them to a new instance
 | 
				
			||||||
 | 
					                return DeserializeContentsRaw(values, expectedType);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            private object DeserializeContentsRaw(Dictionary<string, object> node, Type serializationType)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                object instance = Activator.CreateInstance(serializationType);
 | 
				
			||||||
 | 
					                foreach (FieldInfo field in serializationType.GetFields())
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    object value = null;
 | 
				
			||||||
 | 
					                    if (node.TryGetValue(field.Name.ToLower(), out value))
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        value = SoapConvert(field.FieldType, value);
 | 
				
			||||||
 | 
					                        field.SetValue(instance, value);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                return instance;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            abstract protected void DeserializeMembers(XmlNode node, Type serializationType, Dictionary<string, object> values);
 | 
					            abstract protected void DeserializeMembers(XmlNode node, Type serializationType, Dictionary<string, object> values);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private object CreateCustomInstance(Dictionary<string, object> node, Type serializationType, Type finalType)
 | 
					            private object CreateCustomInstance(Dictionary<string, object> node, Type serializationType, Type finalType)
 | 
				
			||||||
@@ -255,47 +303,13 @@ namespace Acacia.ZPush.Connect.Soap
 | 
				
			|||||||
                else
 | 
					                else
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    // Initialise the serialization type
 | 
					                    // Initialise the serialization type
 | 
				
			||||||
                    instance = Activator.CreateInstance(serializationType);
 | 
					                    instance = DeserializeContentsRaw(node, serializationType);
 | 
				
			||||||
                    foreach (FieldInfo field in serializationType.GetFields())
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        object value = null;
 | 
					 | 
				
			||||||
                        if (node.TryGetValue(field.Name.ToLower(), out value))
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            value = SoapConvert(field.FieldType, value);
 | 
					 | 
				
			||||||
                            field.SetValue(instance, value);
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // Return the final type
 | 
					                // Return the final type
 | 
				
			||||||
                return Activator.CreateInstance(finalType, instance);
 | 
					                return Activator.CreateInstance(finalType, instance);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private object SoapConvert(Type type, object value)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                // Check if any conversion is needed 
 | 
					 | 
				
			||||||
                if (value != null && type.IsAssignableFrom(value.GetType()))
 | 
					 | 
				
			||||||
                    return value;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (value != null)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    // Try Soap conversion
 | 
					 | 
				
			||||||
                    if (typeof(ISoapSerializable<>).IsGenericAssignableFrom(type))
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        // Get the serialization type
 | 
					 | 
				
			||||||
                        Type serializationType = type.GetGenericArguments(typeof(ISoapSerializable<>))[0];
 | 
					 | 
				
			||||||
                        if (serializationType.IsAssignableFrom(value.GetType()))
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            // Create the instance
 | 
					 | 
				
			||||||
                            return Activator.CreateInstance(type, value);
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // Or standard conversions
 | 
					 | 
				
			||||||
                return type.Convert(value);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            public override void Serialize(string name, object value, StringBuilder s)
 | 
					            public override void Serialize(string name, object value, StringBuilder s)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                Dictionary<string, object> dict;
 | 
					                Dictionary<string, object> dict;
 | 
				
			||||||
@@ -319,6 +333,18 @@ namespace Acacia.ZPush.Connect.Soap
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            protected abstract void SerializeMembers(string name, Dictionary<string, object> fields, StringBuilder s);
 | 
					            protected abstract void SerializeMembers(string name, Dictionary<string, object> fields, StringBuilder s);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            protected virtual Type DetermineChildType(Type type, string field)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                if (type == null)
 | 
				
			||||||
 | 
					                    return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                FieldInfo prop = type.GetField(field);
 | 
				
			||||||
 | 
					                if (prop == null)
 | 
				
			||||||
 | 
					                    return null;
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                return prop.FieldType;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private class TypeHandlerStruct : TypeHandlerObject
 | 
					        private class TypeHandlerStruct : TypeHandlerObject
 | 
				
			||||||
@@ -333,7 +359,7 @@ namespace Acacia.ZPush.Connect.Soap
 | 
				
			|||||||
                foreach (XmlNode child in node.ChildNodes)
 | 
					                foreach (XmlNode child in node.ChildNodes)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    string key = child.Name.ToLower();
 | 
					                    string key = child.Name.ToLower();
 | 
				
			||||||
                    object value = DeserializeNode(child, null);
 | 
					                    object value = DeserializeNode(child, DetermineChildType(expectedType, key));
 | 
				
			||||||
                    dict.Add(key, value);
 | 
					                    dict.Add(key, value);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -345,9 +371,9 @@ namespace Acacia.ZPush.Connect.Soap
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private class TypeHandlerMap : TypeHandlerObject
 | 
					        private class TypeHandlerObjectMap : TypeHandlerObject
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            public TypeHandlerMap() : base(SoapConstants.XMLNS_APACHE, "Map")
 | 
					            public TypeHandlerObjectMap() : base(SoapConstants.XMLNS_APACHE, "Map")
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -357,7 +383,7 @@ namespace Acacia.ZPush.Connect.Soap
 | 
				
			|||||||
                foreach (XmlNode child in node.ChildNodes)
 | 
					                foreach (XmlNode child in node.ChildNodes)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    string key = (string)DeserializeNode(child.SelectSingleNode("key"), typeof(string));
 | 
					                    string key = (string)DeserializeNode(child.SelectSingleNode("key"), typeof(string));
 | 
				
			||||||
                    object value = DeserializeNode(child.SelectSingleNode("value"), null);
 | 
					                    object value = DeserializeNode(child.SelectSingleNode("value"), DetermineChildType(expectedType, key));
 | 
				
			||||||
                    dict.Add(key, value);
 | 
					                    dict.Add(key, value);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -377,10 +403,38 @@ namespace Acacia.ZPush.Connect.Soap
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        #endregion
 | 
					        #endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #region Map
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private class TypeHandlerMap<KeyType,ValueType> : TypeHandler
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            public TypeHandlerMap() : base(SoapConstants.XMLNS_SOAP_ENC, "Array", typeof(System.Collections.ICollection)) { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            protected override object DeserializeContents(XmlNode node, Type expectedType)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                Dictionary<KeyType, ValueType> map = new Dictionary<KeyType, ValueType>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                foreach (XmlNode child in node.ChildNodes)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    KeyType key = (KeyType)DeserializeNode(child.SelectSingleNode("key"), typeof(KeyType));
 | 
				
			||||||
 | 
					                    ValueType value = (ValueType)DeserializeNode(child.SelectSingleNode("value"), typeof(ValueType));
 | 
				
			||||||
 | 
					                    map.Add(key, value);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return map;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            public override void Serialize(string name, object value, StringBuilder s)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                throw new NotImplementedException();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private readonly static Dictionary<string, TypeHandler> TYPES_BY_FULL_NAME = new Dictionary<string, TypeHandler>();
 | 
					        private readonly static Dictionary<string, TypeHandler> TYPES_BY_FULL_NAME = new Dictionary<string, TypeHandler>();
 | 
				
			||||||
        private readonly static Dictionary<Type, TypeHandler> TYPES_BY_TYPE = new Dictionary<Type, TypeHandler>();
 | 
					        private readonly static Dictionary<Type, TypeHandler> TYPES_BY_TYPE = new Dictionary<Type, TypeHandler>();
 | 
				
			||||||
        private readonly static TypeHandler TYPE_HANDLER_OBJECT = new TypeHandlerMap();
 | 
					        private readonly static TypeHandler TYPE_HANDLER_OBJECT = new TypeHandlerObjectMap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        static SoapSerializer()
 | 
					        static SoapSerializer()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@@ -415,8 +469,14 @@ namespace Acacia.ZPush.Connect.Soap
 | 
				
			|||||||
            return null;
 | 
					            return null;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static TypeHandler LookupType(XmlNode part)
 | 
					        private static TypeHandler LookupType(XmlNode part, Type expectedType)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            if (expectedType != null && typeof(IDictionary<,>).IsGenericAssignableFrom(expectedType))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                Type bound = typeof(TypeHandlerMap<,>).MakeGenericType(expectedType.GetGenericArguments(typeof(IDictionary<,>)));
 | 
				
			||||||
 | 
					                return (TypeHandler)Activator.CreateInstance(bound);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            XmlAttribute typeAttr = part.Attributes["type", SoapConstants.XMLNS_XSI];
 | 
					            XmlAttribute typeAttr = part.Attributes["type", SoapConstants.XMLNS_XSI];
 | 
				
			||||||
            if (typeAttr == null)
 | 
					            if (typeAttr == null)
 | 
				
			||||||
                throw new Exception("Missing type");
 | 
					                throw new Exception("Missing type");
 | 
				
			||||||
@@ -431,6 +491,7 @@ namespace Acacia.ZPush.Connect.Soap
 | 
				
			|||||||
            TypeHandler type;
 | 
					            TypeHandler type;
 | 
				
			||||||
            if (!TYPES_BY_FULL_NAME.TryGetValue(fullName, out type))
 | 
					            if (!TYPES_BY_FULL_NAME.TryGetValue(fullName, out type))
 | 
				
			||||||
                throw new Exception("Unknown type: " + fullName);
 | 
					                throw new Exception("Unknown type: " + fullName);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return type;
 | 
					            return type;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -204,6 +204,12 @@ namespace Acacia.ZPush.Connect
 | 
				
			|||||||
                private set;
 | 
					                private set;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            public string SignaturesHash
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                get;
 | 
				
			||||||
 | 
					                private set;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private string GetStringHeader(HttpResponseMessage response, string name)
 | 
					            private string GetStringHeader(HttpResponseMessage response, string name)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                IEnumerable<string> values;
 | 
					                IEnumerable<string> values;
 | 
				
			||||||
@@ -227,6 +233,7 @@ namespace Acacia.ZPush.Connect
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                Capabilities = ZPushCapabilities.Parse(GetStringHeader(response, Constants.ZPUSH_HEADER_CAPABILITIES));
 | 
					                Capabilities = ZPushCapabilities.Parse(GetStringHeader(response, Constants.ZPUSH_HEADER_CAPABILITIES));
 | 
				
			||||||
                ZPushVersion = GetStringHeader(response, Constants.ZPUSH_HEADER_VERSION);
 | 
					                ZPushVersion = GetStringHeader(response, Constants.ZPUSH_HEADER_VERSION);
 | 
				
			||||||
 | 
					                SignaturesHash = GetStringHeader(response, Constants.ZPUSH_HEADER_SIGNATURES_HASH);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // Check for success
 | 
					                // Check for success
 | 
				
			||||||
                Success = response.IsSuccessStatusCode;
 | 
					                Success = response.IsSuccessStatusCode;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -120,6 +120,13 @@ namespace Acacia.ZPush
 | 
				
			|||||||
            private set;
 | 
					            private set;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public string ServerSignaturesHash
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            get;
 | 
				
			||||||
 | 
					            private set;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public void LinkedGABFolder(IFolder folder)
 | 
					        public void LinkedGABFolder(IFolder folder)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            GABFolderLinked = folder.EntryID;
 | 
					            GABFolderLinked = folder.EntryID;
 | 
				
			||||||
@@ -128,13 +135,14 @@ namespace Acacia.ZPush
 | 
				
			|||||||
        internal void OnConfirmationResponse(ZPushConnection.Response response)
 | 
					        internal void OnConfirmationResponse(ZPushConnection.Response response)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            Capabilities = response.Capabilities;
 | 
					            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;
 | 
					            GABFolder = response.GABName;
 | 
				
			||||||
            ZPushVersion = response.ZPushVersion;
 | 
					            ZPushVersion = response.ZPushVersion;
 | 
				
			||||||
 | 
					            ServerSignaturesHash = response.SignaturesHash;
 | 
				
			||||||
            Confirmed = Capabilities == null ? ConfirmationType.IsNotZPush : ConfirmationType.IsZPush;
 | 
					            Confirmed = Capabilities == null ? ConfirmationType.IsNotZPush : ConfirmationType.IsZPush;
 | 
				
			||||||
            Logger.Instance.Info(this, "ZPush confirmation: {0} -> {1}, {2}", Confirmed, Capabilities, GABFolder);
 | 
					            Logger.Instance.Info(this, "ZPush confirmation: {0} -> {1}, {2}", Confirmed, Capabilities, GABFolder);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (_confirmedChanged != null)
 | 
					            _confirmedChanged?.Invoke(this);
 | 
				
			||||||
                _confirmedChanged(this);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #endregion
 | 
					        #endregion
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user