File: Tasks\MsDeploy\VsMsdeploy.cs
Web Access
Project: ..\..\..\src\WebSdk\Publish\Tasks\Microsoft.NET.Sdk.Publish.Tasks.csproj (Microsoft.NET.Sdk.Publish.Tasks)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using Microsoft.Build.Framework;
using Microsoft.NET.Sdk.Publish.Tasks.Properties;
using Collections = System.Collections;
using Diagnostics = System.Diagnostics;
using Utilities = Microsoft.Build.Utilities;
 
namespace Microsoft.NET.Sdk.Publish.Tasks.MsDeploy
{
    /// <summary>
    /// WrapperClass for Microsoft.Web.Deployment
    /// </summary>
    internal class MSWebDeploymentAssembly : DynamicAssembly
    {
        public MSWebDeploymentAssembly(Version verToLoad) :
            base(AssemblyName, verToLoad, "31bf3856ad364e35")
        {
        }
 
        public static string AssemblyName { get { return "Microsoft.Web.Deployment"; } }
        public static MSWebDeploymentAssembly? DynamicAssembly { get; set; }
        public static void SetVersion(Version version)
        {
            if (DynamicAssembly == null || DynamicAssembly.Version != version)
            {
                DynamicAssembly = new MSWebDeploymentAssembly(version);
            }
        }
 
        /// <summary>
        /// Utility function to help out on getting Deployment collection's tryGetMethod
        /// </summary>
        /// <param name="deploymentCollection"></param>
        /// <param name="name"></param>
        /// <param name="foundObject"></param>
        /// <returns></returns>
        public static bool DeploymentTryGetValueForEach(dynamic deploymentCollection, string name, out dynamic? foundObject)
        {
            foundObject = null;
            if (deploymentCollection != null)
            {
                foreach (dynamic item in deploymentCollection)
                {
                    if (string.Compare(name, item.Name.ToString(), StringComparison.OrdinalIgnoreCase) == 0)
                    {
                        foundObject = item;
                        return true;
                    }
                }
            }
            return false;
        }
 
        public static bool DeploymentTryGetValueContains(dynamic deploymentCollection, string name, out dynamic? foundObject)
        {
            foundObject = null;
            if (deploymentCollection != null)
            {
                if (deploymentCollection.Contains(name))
                {
                    foundObject = deploymentCollection[name];
                    return true;
                }
            }
            return false;
        }
    }
 
    /// <summary>
    /// WrapperClass for Microsoft.Web.Delegation
    /// </summary>
    internal class MSWebDelegationAssembly : DynamicAssembly
    {
        public MSWebDelegationAssembly(Version verToLoad) :
            base(AssemblyName, verToLoad, "31bf3856ad364e35")
        {
        }
 
        public static string AssemblyName { get { return "Microsoft.Web.Delegation"; } }
 
        public static MSWebDelegationAssembly? DynamicAssembly { get; set; }
        public static void SetVersion(Version version)
        {
            if (DynamicAssembly == null || DynamicAssembly.Version != version)
            {
                DynamicAssembly = new MSWebDelegationAssembly(version);
            }
        }
    }
 
    // Microsoft.Web.Delegation
 
    ///--------------------------------------------------------------------
    enum DeployStatus
    {
        ReadyToDeploy,
        Deploying,
        DeployFinished,
        DeployAbandoned,
        DeployFailed
    }
 
    /// <summary>
    /// Encapsulate the process of interacting with MSDeploy
    /// </summary>
    abstract class BaseMSDeployDriver
    {
        protected VSMSDeployObject _dest;
        protected VSMSDeployObject _src;
        protected IVSMSDeployHost _host;
 
        protected /*VSMSDeploySyncOption*/ dynamic? _option;
        protected bool _isCancelOperation = false;
        protected string? _cancelMessage;
 
        public string TaskName
        {
            get
            {
                return (_host != null) ? _host.TaskName : string.Empty;
            }
        }
 
        public string? HighImportanceEventTypes
        {
            get;
            set;
        }
 
        /// <summary>
        /// Boolean to cancel the operation
        /// (TODO: in RTM, use thread synchronization to protect the entry(though not absolutely necessary.
        /// Need consider perf hit incurred though as msdeploy's callback will reference the value frequently)
        /// </summary>
        public bool IsCancelOperation
        {
            get { return _isCancelOperation; }
            set
            {
                _isCancelOperation = value;
                if (!_isCancelOperation)
                    CancelMessage = null; // reset error age
            }
        }
 
        public string? CancelMessage
        {
            get { return _cancelMessage; }
            set { _cancelMessage = value; }
        }
 
        /// <summary>
        /// called by the msdeploy to cancel the operation
        /// </summary>
        /// <returns></returns>
        private bool CancelCallback()
        {
            return IsCancelOperation;
        }
 
        protected /*VSMSDeploySyncOption*/ dynamic? CreateOptionIfNeeded()
        {
            if (_option == null)
            {
                object? option = MSWebDeploymentAssembly.DynamicAssembly?.CreateObject("Microsoft.Web.Deployment.DeploymentSyncOptions");
#if NET472
                Type? deploymentCancelCallbackType = MSWebDeploymentAssembly.DynamicAssembly?.GetType("Microsoft.Web.Deployment.DeploymentCancelCallback");
                object cancelCallbackDelegate = Delegate.CreateDelegate(deploymentCancelCallbackType, this, "CancelCallback");
 
                Utility.SetDynamicProperty(option, "CancelCallback", cancelCallbackDelegate);
 
                // dynamic doesn't work with delegate. it complain on explicit cast needed from object -> DelegateType :(
                // _option.CancelCallback = cancelCallbackDelegate;
#endif
                _option = option;
            }
            return _option;
        }
 
#if NET472
        private Dictionary<string, MessageImportance>? _highImportanceEventTypes = null;
        private Dictionary<string, MessageImportance> GetHighImportanceEventTypes()
        {
            if (_highImportanceEventTypes == null)
            {
                _highImportanceEventTypes = new Dictionary<string, MessageImportance>(StringComparer.InvariantCultureIgnoreCase); ;
                if (HighImportanceEventTypes is not null && HighImportanceEventTypes.Length != 0)
                {
                    string[] typeNames = HighImportanceEventTypes.Split(new char[] { ';' }); // follow msbuild convention
                    foreach (string typeName in typeNames)
                    {
                        _highImportanceEventTypes.Add(typeName, MessageImportance.High);
                    }
                }
            }
            return _highImportanceEventTypes;
        }
#endif
        void TraceEventHandlerDynamic(object sender, dynamic e)
        {
            // throw new System.NotImplementedException();
            string msg = e.Message;
            Diagnostics.Trace.WriteLine("MSDeploy TraceEvent Handler is called with " + msg);
#if NET472
            LogTrace(e, GetHighImportanceEventTypes());
#endif
            //try
            //{
            //    LogTrace(e);
            //}
            //catch (Framework.LoggerException loggerException)
            //{
            //    System.OperationCanceledException operationCanceledException
            //        = loggerException.InnerException as System.OperationCanceledException;
            //    if (operationCanceledException != null)
            //    {
            //        // eat this exception and set the args
            //        // Logger is the one throw this exception. we should not log again.
            //        // _option.CancelCallback();
            //        IsCancelOperation = true;
            //        CancelMessage = operationCanceledException.Message;
            //    }
            //    else
            //    {
            //        throw; // rethrow if this is not a OperationCancelException
            //    }
            //}
        }
 
        /// <summary>
        /// Using MSDeploy API to invoke MSDeploy
        /// </summary>
        protected void InvokeMSdeploySync()
        {
            /*VSMSDeploySyncOption*/
            dynamic? option = CreateOptionIfNeeded();
            IsCancelOperation = false;
 
            _host.PopulateOptions(option);
 
            // you can reuse traceEventHandler if you know the function signature is the same
            Delegate traceEventHandler = DynamicAssembly.AddEventDeferHandler(
                _src.BaseOptions,
                "Trace",
                new DynamicAssembly.EventHandlerDynamicDelegate(TraceEventHandlerDynamic));
            DynamicAssembly.AddEventHandler(_dest.BaseOptions, "Trace", traceEventHandler);
 
            _host.UpdateDeploymentBaseOptions(_src, _dest);
 
            _src.SyncTo(_dest, option, _host);
 
            _host.ClearDeploymentBaseOptions(_src, _dest);
 
            DynamicAssembly.RemoveEventHandler(_src.BaseOptions, "Trace", traceEventHandler);
            DynamicAssembly.RemoveEventHandler(_dest.BaseOptions, "Trace", traceEventHandler);
 
            _src.ResetBaseOptions();
            _dest.ResetBaseOptions();
 
        }
 
        /// <summary>
        /// The end to end process to invoke MSDeploy
        /// </summary>
        /// <returns></returns>
        public void SyncThruMSDeploy()
        {
            BeforeSync();
            StartSync();
            WaitForDone();
            AfterSync();
        }
 
        /// <summary>
        /// Encapsulate the things be done before invoke MSDeploy
        /// </summary>
        protected abstract void BeforeSync();
 
        /// <summary>
        /// Encapsulate the approach to invoke the MSDeploy (same thread or in a separate thread; ui or without ui)
        /// </summary>
        protected abstract void StartSync();
 
        /// <summary>
        /// Encapsulate the approach to wait for the MSDeploy done
        /// </summary>
        protected abstract void WaitForDone();
 
        /// <summary>
        /// Encapsulate how to report the Trace information
        /// </summary>
        /// <param name="e"></param>
        // abstract protected void LogTrace(Deployment.DeploymentTraceEventArgs e);
 
        protected abstract void LogTrace(dynamic e, IDictionary<string, MessageImportance> customTypeLoging);
 
        /// <summary>
        /// Encapsulate the things to be done after the deploy is done
        /// </summary>
        protected abstract void AfterSync();
 
        /// <summary>
        /// constructor
        /// </summary>
        /// <param name="src"></param>
        /// <param name="dest"></param>
        protected BaseMSDeployDriver(VSMSDeployObject src, VSMSDeployObject dest, IVSMSDeployHost host)
        {
            _src = src;
            _dest = dest;
            _host = host;
        }
 
        public static BaseMSDeployDriver CreateBaseMSDeployDriver(
            VSMSDeployObject src,
            VSMSDeployObject dest,
            IVSMSDeployHost host)
        {
            BaseMSDeployDriver bmd;
            bmd = new VSMSDeployDriverInCmd(src, dest, host);
            return bmd;
        }
    }
 
    /// <summary>
    /// We create CustomBuildWithPropertiesEventArgs is for the purpose of logging verious information
    /// in a IDictionary such that the MBuild handler can handle generically.
    /// </summary>
#if NET472
    [Serializable]
#endif
    public class CustomBuildWithPropertiesEventArgs : CustomBuildEventArgs, Collections.IDictionary
    {
        public CustomBuildWithPropertiesEventArgs() : base() { }
        public CustomBuildWithPropertiesEventArgs(string msg, string keyword, string senderName)
            : base(msg, keyword, senderName)
        {
        }
 
        Collections.Specialized.HybridDictionary m_hybridDictionary = new(10);
        #region IDictionary Members 
        // Delegate everything to m_hybridDictionary
 
        public void Add(object? key, object? value)
        {
            if (key is not null)
            {
                m_hybridDictionary.Add(key, value);
            }
        }
 
        public void Clear()
        {
            m_hybridDictionary.Clear();
        }
 
        public bool Contains(object key)
        {
            return m_hybridDictionary.Contains(key);
        }
 
        public Collections.IDictionaryEnumerator GetEnumerator()
        {
            return m_hybridDictionary.GetEnumerator();
        }
 
        public bool IsFixedSize
        {
            get { return m_hybridDictionary.IsFixedSize; }
        }
 
        public bool IsReadOnly
        {
            get { return m_hybridDictionary.IsReadOnly; }
        }
 
        public Collections.ICollection Keys
        {
            get { return m_hybridDictionary.Keys; }
        }
 
        public void Remove(object key)
        {
            m_hybridDictionary.Remove(key);
        }
 
        public Collections.ICollection Values
        {
            get { return m_hybridDictionary.Values; }
        }
 
        public object? this[object key]
        {
            get { return m_hybridDictionary[key]; }
            set { m_hybridDictionary[key] = value; }
        }
 
        #endregion
 
        #region ICollection Members
 
        public void CopyTo(Array array, int index)
        {
            m_hybridDictionary.CopyTo(array, index);
        }
 
        public int Count
        {
            get { return m_hybridDictionary.Count; }
        }
 
        public bool IsSynchronized
        {
            get { return m_hybridDictionary.IsSynchronized; }
        }
 
        public object SyncRoot
        {
            get { return m_hybridDictionary.SyncRoot; }
        }
 
        #endregion
 
        #region IEnumerable Members
 
        Collections.IEnumerator Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
        #endregion
    }
 
    /// <summary>
    /// Deploy through msbuild in command line
    /// </summary>
    class VSMSDeployDriverInCmd : BaseMSDeployDriver
    {
        protected override void BeforeSync()
        {
            string strMsg = string.Format(System.Globalization.CultureInfo.CurrentCulture, Resources.VSMSDEPLOY_Start, _src.ToString(), _dest.ToString());
            _host.Log.LogMessage(strMsg);
        }
 
        // Utility function to log all public instance property to CustomerBuildEventArgs 
        private static void AddAllPropertiesToCustomBuildWithPropertyEventArgs(ExtendedCustomBuildEventArgs cbpEventArg, object obj)
        {
#if NET472
            if (obj != null)
            {
                Type thisType = obj.GetType();
                if (cbpEventArg.ExtendedMetadata is not null)
                {
                    cbpEventArg.ExtendedMetadata["ArgumentType"] = thisType.ToString();
                }
                System.Reflection.MemberInfo[] arrayMemberInfo = thisType.FindMembers(System.Reflection.MemberTypes.Property, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance, null, null);
                if (arrayMemberInfo != null)
                {
                    foreach (System.Reflection.MemberInfo memberInfo in arrayMemberInfo)
                    {
                        object val = thisType.InvokeMember(memberInfo.Name, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.GetProperty, null, obj, null, System.Globalization.CultureInfo.InvariantCulture);
                        if (val is not null && cbpEventArg.ExtendedMetadata is not null)
                        {
                            cbpEventArg.ExtendedMetadata[memberInfo.Name] = val.ToString();
                        }
                    }
                }
            }
#endif
        }
 
        protected override void LogTrace(dynamic args, IDictionary<string, MessageImportance> customTypeLoging)
        {
            string strMsg = args.Message;
            string strEventType = "Trace";
            MessageImportance messageImportance = MessageImportance.Low;
 
            Type argsT = args.GetType();
            if (Utility.IsType(argsT, MSWebDeploymentAssembly.DynamicAssembly?.GetType("Microsoft.Web.Deployment.DeploymentFileSerializationEventArgs")) ||
                Utility.IsType(argsT, MSWebDeploymentAssembly.DynamicAssembly?.GetType("Microsoft.Web.Deployment.DeploymentPackageSerializationEventArgs")) ||
                Utility.IsType(argsT, MSWebDeploymentAssembly.DynamicAssembly?.GetType("Microsoft.Web.Deployment.DeploymentObjectChangedEventArgs")) ||
                Utility.IsType(argsT, MSWebDeploymentAssembly.DynamicAssembly?.GetType("Microsoft.Web.Deployment.DeploymentSyncParameterEventArgs")))
            {
                //promote those message only for those event
                strEventType = "Action";
                messageImportance = MessageImportance.High;
            }
            else if (customTypeLoging != null && customTypeLoging.ContainsKey(argsT.Name))
            {
                strEventType = "Trace";
                messageImportance = customTypeLoging[argsT.Name];
            }
 
            if (!string.IsNullOrEmpty(strMsg))
            {
                Diagnostics.TraceLevel level = (Diagnostics.TraceLevel)Enum.ToObject(typeof(Diagnostics.TraceLevel), args.EventLevel);
                switch (level)
                {
                    case Diagnostics.TraceLevel.Off:
                        break;
                    case Diagnostics.TraceLevel.Error:
                        _host.Log.LogError(strMsg);
                        break;
                    case Diagnostics.TraceLevel.Warning:
                        _host.Log.LogWarning(strMsg);
                        break;
                    default: // Is Warning is a Normal message
                        _host.Log.LogMessageFromText(strMsg, messageImportance);
                        break;
 
                }
            }
 
            // additionally we fire the Custom event for the detail information
            var customBuildWithPropertiesEventArg = new ExtendedCustomBuildEventArgs(args.GetType().ToString(), args.Message, null, TaskName)
            {
                ExtendedMetadata = new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase)
                {
                    { "TaskName", TaskName },
                    { "EventType", strEventType }
                }
            };
 
            AddAllPropertiesToCustomBuildWithPropertyEventArgs(customBuildWithPropertiesEventArg, args);
            _host.BuildEngine.LogCustomEvent(customBuildWithPropertiesEventArg);
        }
 
        /// <summary>
        /// Invoke MSDeploy
        /// </summary>
        protected override void StartSync()
        {
            InvokeMSdeploySync();
        }
 
        /// <summary>
        /// Wait forever if we are in the command line
        /// </summary>
        protected override void WaitForDone() { }
 
        /// <summary>
        /// Log status after the deploy is done
        /// </summary>
        protected override void AfterSync()
        {
            string strMsg = Resources.VSMSDEPLOY_Succeeded;
            _host.Log.LogMessage(strMsg);
        }
 
        /// <summary>
        /// construct
        /// </summary>
        /// <param name="src"></param>
        /// <param name="dest"></param>
        /// <param name="log"></param>
        internal VSMSDeployDriverInCmd(VSMSDeployObject src, VSMSDeployObject dest, IVSMSDeployHost host)
            : base(src, dest, host)
        {
            if (host.GetProperty("HighImportanceEventTypes") != null)
                HighImportanceEventTypes = host.GetProperty("HighImportanceEventTypes")?.ToString();
        }
    }
 
    /// <summary>
    /// MSBuild Task VSMSDeploy to call the object through UI or not
    /// </summary>
    public class VSMSDeploy : Task, IVSMSDeployHost, ICancelableTask
    {
        string? _disableLink;
        string? _enableLink;
        private string? _disableSkipDirective;
        private string? _enableSkipDirective;
 
        bool _result = false;
        bool _whatIf = false;
        string? _deploymentTraceLevel;
        bool _useCheckSum = false;
        private int m_retryAttempts = -1;
        private int m_retryInterval = -1;
 
        bool _allowUntrustedCert;
        bool _skipExtraFilesOnServer = false;
 
        private ITaskItem[]? m_sourceITaskItem = null;
        private ITaskItem[]? m_destITaskItem = null;
        private ITaskItem[]? m_replaceRuleItemsITaskItem = null;
        private ITaskItem[]? m_skipRuleItemsITaskItem = null;
        private ITaskItem[]? m_declareParameterItems = null;
        private ITaskItem[]? m_importDeclareParametersItems = null;
        private ITaskItem[]? m_simpleSetParameterItems = null;
        private ITaskItem[]? m_importSetParametersItems = null;
        private ITaskItem[]? m_setParameterItems = null;
 
        private BaseMSDeployDriver? m_msdeployDriver = null;
 
        [Required]
        public ITaskItem[]? Source
        {
            get { return m_sourceITaskItem; }
            set { m_sourceITaskItem = value; }
        }
 
        public string? HighImportanceEventTypes
        {
            get;
            set;
        }
 
        public ITaskItem[]? Destination
        {
            get { return m_destITaskItem; }
            set { m_destITaskItem = value; }
        }
 
        public bool AllowUntrustedCertificate
        {
            get { return _allowUntrustedCert; }
            set { _allowUntrustedCert = value; }
        }
 
        public bool SkipExtraFilesOnServer
        {
            get { return _skipExtraFilesOnServer; }
            set { _skipExtraFilesOnServer = value; }
        }
 
        public bool WhatIf
        {
            get { return _whatIf; }
            set { _whatIf = value; }
        }
 
        public string? DeploymentTraceLevel
        {
            get { return _deploymentTraceLevel; }
            set { _deploymentTraceLevel = value; }
        }
 
        public bool UseChecksum
        {
            get { return _useCheckSum; }
            set { _useCheckSum = value; }
        }
 
        //Sync result: Succeed or Fail
        [Output]
        public bool Result
        {
            get { return _result; }
            set { _result = value; }
        }
 
        /// <summary>
        /// Disable Link is a list of disable provider
        /// </summary>
        public string? DisableLink
        {
            get { return _disableLink; }
            set { _disableLink = value; }
        }
 
        public string? EnableLink
        {
            get { return _enableLink; }
            set { _enableLink = value; }
        }
 
        public string? DisableSkipDirective
        {
            get { return _disableSkipDirective; }
            set { _disableSkipDirective = value; }
        }
 
        public string? EnableSkipDirective
        {
            get { return _enableSkipDirective; }
            set { _enableSkipDirective = value; }
        }
 
        public int RetryAttempts
        {
            get { return m_retryAttempts; }
            set { m_retryAttempts = value; }
        }
 
        public int RetryInterval
        {
            get { return m_retryInterval; }
            set { m_retryInterval = value; }
        }
 
        public ITaskItem[]? ReplaceRuleItems
        {
            get { return m_replaceRuleItemsITaskItem; }
            set { m_replaceRuleItemsITaskItem = value; }
        }
 
        public ITaskItem[]? SkipRuleItems
        {
            get { return m_skipRuleItemsITaskItem; }
            set { m_skipRuleItemsITaskItem = value; }
        }
 
        public ITaskItem[]? DeclareParameterItems
        {
            get { return m_declareParameterItems; }
            set { m_declareParameterItems = value; }
        }
 
        public bool OptimisticParameterDefaultValue { get; set; }
 
        public ITaskItem[]? ImportDeclareParametersItems
        {
            get { return m_importDeclareParametersItems; }
            set { m_importDeclareParametersItems = value; }
        }
 
        public ITaskItem[]? SimpleSetParameterItems
        {
            get { return m_simpleSetParameterItems; }
            set { m_simpleSetParameterItems = value; }
        }
 
        public ITaskItem[]? ImportSetParametersItems
        {
            get { return m_importSetParametersItems; }
            set { m_importSetParametersItems = value; }
        }
 
        public ITaskItem[]? SetParameterItems
        {
            get { return m_setParameterItems; }
            set { m_setParameterItems = value; }
        }
 
        public bool EnableMSDeployBackup { get; set; }
 
        public bool EnableMSDeployAppOffline { get; set; }
 
        public bool EnableMSDeployWebConfigEncryptRule { get; set; }
 
        private string? _userAgent;
        public string? UserAgent
        {
            get { return _userAgent; }
            set
            {
                if (!string.IsNullOrEmpty(value))
                {
                    _userAgent = Utility.GetFullUserAgentString(value);
                }
            }
        }
 
        public ITaskItem[]? AdditionalDestinationProviderOptions { get; set; }
 
        public string? MSDeployVersionsToTry
        {
            get;
            set;
        }
 
        private bool AllowUntrustedCertCallback(object sp,
                System.Security.Cryptography.X509Certificates.X509Certificate cert,
                System.Security.Cryptography.X509Certificates.X509Chain chain,
                System.Net.Security.SslPolicyErrors problem)
        {
            if (AllowUntrustedCertificate)
            {
                return true;
            }
 
            return false;
        }
        private void SetupPublishRelatedProperties(ref VSMSDeployObject dest)
        {
#if NET472
            if (AllowUntrustedCertificate) 
            {
                System.Net.ServicePointManager.ServerCertificateValidationCallback
                         += new System.Net.Security.RemoteCertificateValidationCallback(AllowUntrustedCertCallback);
            }
#endif
        }
 
        public override bool Execute()
        {
            Result = false;
 
            try
            {
                Utility.SetupMSWebDeployDynamicAssemblies(MSDeployVersionsToTry, this);
            }
            catch (Exception exception)
            {
                Log.LogErrorFromException(exception);
                return false; // failed the task
            }
 
            string? errorMessage = null;
            if (!Utility.CheckMSDeploymentVersion(Log, out errorMessage))
                return false;
 
            VSMSDeployObject? src = null;
            VSMSDeployObject? dest = null;
 
            if (Source == null || Source.GetLength(0) != 1)
            {
                Log.LogError("Source must be 1 item");
                return false;
            }
            else
            {
                src = VSMSDeployObjectFactory.CreateVSMSDeployObject(Source[0]);
            }
 
            if (Destination == null || Destination.GetLength(0) != 1)
            {
                Log.LogError("Destination must be 1 item");
                return false;
            }
            else
            {
                dest = VSMSDeployObjectFactory.CreateVSMSDeployObject(Destination[0]);
                VSHostObject hostObj = new(HostObject as IEnumerable<ITaskItem>);
                string username, password;
                if (hostObj.ExtractCredentials(out username, out password))
                {
                    dest.UserName = username;
                    dest.Password = password;
                }
            }
 
            //$Todo, Should we split the Disable Link to two set of setting, one for source, one for destination
            src.DisableLinks = DisableLink ?? string.Empty;
            dest.DisableLinks = DisableLink ?? string.Empty;
            src.EnableLinks = EnableLink ?? string.Empty;
            dest.EnableLinks = EnableLink ?? string.Empty;
            if (RetryAttempts >= 0)
            {
                src.RetryAttempts = RetryAttempts;
                dest.RetryAttempts = RetryAttempts;
            }
            if (RetryInterval >= 0)
            {
                src.RetryInterval = RetryInterval;
                dest.RetryInterval = RetryInterval;
            }
            dest.UserAgent = UserAgent;
 
            SetupPublishRelatedProperties(ref dest);
 
            // change to use when we have MSDeploy implement the dispose method 
            BaseMSDeployDriver driver = BaseMSDeployDriver.CreateBaseMSDeployDriver(src, dest, this);
            m_msdeployDriver = driver;
            try
            {
                driver.SyncThruMSDeploy();
                Result = !driver.IsCancelOperation;
            }
            catch (Exception e)
            {
                if (e is System.Reflection.TargetInvocationException)
                {
                    if (e.InnerException != null)
                        e = e.InnerException;
                }
 
                Type eType = e.GetType();
                if (Utility.IsType(eType, MSWebDeploymentAssembly.DynamicAssembly?.GetType("Microsoft.Web.Deployment.DeploymentCanceledException")))
                {
                    Log.LogMessageFromText(Resources.VSMSDEPLOY_Canceled, MessageImportance.High);
                }
                else if (Utility.IsType(eType, MSWebDelegationAssembly.DynamicAssembly?.GetType("Microsoft.Web.Deployment.DeploymentException"))
                    || Utility.IsType(eType, MSWebDeploymentAssembly.DynamicAssembly?.GetType("Microsoft.Web.Deployment.DeploymentFatalException")))
                {
                    Utility.LogVsMsDeployException(Log, e);
                }
                else
                {
                    if (!driver.IsCancelOperation)
                        Log.LogError(string.Format(System.Globalization.CultureInfo.CurrentCulture, Resources.VSMSDEPLOY_FailedWithException, e.Message));
                }
            }
            finally
            {
#if NET472

                if (AllowUntrustedCertificate)
                    System.Net.ServicePointManager.ServerCertificateValidationCallback
                        -= new System.Net.Security.RemoteCertificateValidationCallback(AllowUntrustedCertCallback);
#endif
            }
 
            Utility.MsDeployEndOfExecuteMessage(Result, dest.Provider, dest.Root, Log);
            return Result;
        }
 
        #region IVSMSDeployHost Members
 
        string IVsPublishMsBuildTaskHost.TaskName
        {
            get
            {
                return GetType().Name;
            }
        }
 
        Utilities.TaskLoggingHelper IVsPublishMsBuildTaskHost.Log
        {
            get
            {
                return Log;
            }
        }
 
        IBuildEngine IVsPublishMsBuildTaskHost.BuildEngine
        {
            get
            {
                return BuildEngine;
            }
        }
 
        /// <summary>
        /// Sample for skipping directories
        //   <ItemGroup>
        //        <MsDeploySkipRules Include = "SkippingWWWRoot" >
        //            <ObjectName>dirPath</ ObjectName >
        //            <AbsolutePath>wwwroot</ AbsolutePath >
        //        </MsDeploySkipRules>
        //    </ ItemGroup >
        /// </summary>
        void IVSMSDeployHost.UpdateDeploymentBaseOptions(VSMSDeployObject srcVsMsDeployobject, VSMSDeployObject destVsMsDeployobject)
        {
            List<string> enableSkipDirectiveList = MSDeployUtility.ConvertStringIntoList(EnableSkipDirective);
            List<string> disableSkipDirectiveList = MSDeployUtility.ConvertStringIntoList(DisableSkipDirective);
 
            VSHostObject hostObject = new(HostObject as IEnumerable<ITaskItem>);
            ITaskItem[]? srcSkipItems, destSkipsItems;
 
            // Add FileSkip rules from Host Object
            hostObject.GetFileSkips(out srcSkipItems, out destSkipsItems);
            Utility.AddSkipDirectiveToBaseOptions(srcVsMsDeployobject.BaseOptions, srcSkipItems, enableSkipDirectiveList, disableSkipDirectiveList, Log);
            Utility.AddSkipDirectiveToBaseOptions(destVsMsDeployobject.BaseOptions, destSkipsItems, enableSkipDirectiveList, disableSkipDirectiveList, Log);
 
            //Add CustomSkip Rules + AppDataSkipRules
            GetCustomAndAppDataSkips(out srcSkipItems, out destSkipsItems);
            Utility.AddSkipDirectiveToBaseOptions(srcVsMsDeployobject.BaseOptions, srcSkipItems, enableSkipDirectiveList, disableSkipDirectiveList, Log);
            Utility.AddSkipDirectiveToBaseOptions(destVsMsDeployobject.BaseOptions, destSkipsItems, enableSkipDirectiveList, disableSkipDirectiveList, Log);
 
            if (!string.IsNullOrEmpty(DeploymentTraceLevel))
            {
                Diagnostics.TraceLevel deploymentTraceEventLevel =
                    (Diagnostics.TraceLevel)Enum.Parse(typeof(Diagnostics.TraceLevel), DeploymentTraceLevel, true);
                if (srcVsMsDeployobject.BaseOptions is not null)
                {
                    srcVsMsDeployobject.BaseOptions.TraceLevel = deploymentTraceEventLevel;
                }
                if (destVsMsDeployobject.BaseOptions is not null)
                {
                    destVsMsDeployobject.BaseOptions.TraceLevel = deploymentTraceEventLevel;
                }
            }
 
            Utility.AddSetParametersFilesVsMsDeployObject(srcVsMsDeployobject, ImportSetParametersItems);
            Utility.AddSimpleSetParametersVsMsDeployObject(srcVsMsDeployobject, SimpleSetParameterItems, OptimisticParameterDefaultValue);
            Utility.AddSetParametersVsMsDeployObject(srcVsMsDeployobject, SetParameterItems, OptimisticParameterDefaultValue);
 
            AddAdditionalProviderOptions(destVsMsDeployobject);
        }
        private void GetCustomAndAppDataSkips(out ITaskItem[]? srcSkips, out ITaskItem[]? destSkips)
        {
            srcSkips = null;
            destSkips = null;
 
            if (SkipRuleItems != null)
            {
                IEnumerable<ITaskItem> items;
 
                items = from item in SkipRuleItems
                        where (string.IsNullOrEmpty(item.GetMetadata(VSMsDeployTaskHostObject.SkipApplyMetadataName)) ||
                               item.GetMetadata(VSMsDeployTaskHostObject.SkipApplyMetadataName) == VSMsDeployTaskHostObject.SourceDeployObject)
                        select item;
                srcSkips = items.ToArray();
 
                items = from item in SkipRuleItems
                        where (string.IsNullOrEmpty(item.GetMetadata(VSMsDeployTaskHostObject.SkipApplyMetadataName)) ||
                               item.GetMetadata(VSMsDeployTaskHostObject.SkipApplyMetadataName) == VSMsDeployTaskHostObject.DestinationDeployObject)
                        select item;
 
                destSkips = items.ToArray();
            }
        }
 
        private void AddAdditionalProviderOptions(VSMSDeployObject destVsMsDeployobject)
        {
            if (AdditionalDestinationProviderOptions != null)
            {
                foreach (ITaskItem item in AdditionalDestinationProviderOptions)
                {
                    if (!string.IsNullOrEmpty(item.ItemSpec))
                    {
                        string settingName = item.GetMetadata("Name");
                        string settingValue = item.GetMetadata("Value");
                        if (!string.IsNullOrEmpty(settingName) && !string.IsNullOrEmpty(settingValue))
                            destVsMsDeployobject.BaseOptions?.AddDefaultProviderSetting(item.ItemSpec, settingName, settingValue);
                    }
                }
            }
        }
 
        void IVSMSDeployHost.ClearDeploymentBaseOptions(VSMSDeployObject srcVsMsDeployobject, VSMSDeployObject destVsMsDeployobject)
        {
            // Nothing to do here
        }
 
        void IVSMSDeployHost.PopulateOptions(/*Microsoft.Web.Deployment.DeploymentSyncOptions*/ dynamic option)
        {
            option.WhatIf = WhatIf;
            // Add the replace rules, we should consider doing the same thing for the skip rule
            Utility.AddReplaceRulesToOptions(option.Rules, ReplaceRuleItems);
            Utility.AddImportDeclareParametersFileOptions(option, ImportDeclareParametersItems);
            Utility.AddDeclareParametersToOptions(option, DeclareParameterItems, OptimisticParameterDefaultValue);
 
            option.UseChecksum = UseChecksum;
            option.DoNotDelete = SkipExtraFilesOnServer;
            if (EnableMSDeployBackup == false)
            {
                // We need to remove the BackupRule to work around bug DevDiv: 478647. We try catch in case
                // the rule isn't there and webdeploy throws. The documentation doesn't say what the exceptions are and the function
                // is void.
                try
                {
                    option.Rules.Remove("BackupRule");
                }
                catch
                {
                }
            }
 
            if (EnableMSDeployAppOffline)
            {
                AddOptionRule(option, "AppOffline", "Microsoft.Web.Deployment.AppOfflineRuleHandler");
            }
 
            if (EnableMSDeployWebConfigEncryptRule)
            {
                AddOptionRule(option, "EncryptWebConfig", "Microsoft.Web.Deployment.EncryptWebConfigRuleHandler");
            }
        }
 
        #endregion
 
        #region ICancelableTask Members
 
        public void Cancel()
        {
            try
            {
                if (m_msdeployDriver != null)
                {
                    //[TODO: in RTM make sure we can cancel even "m_msdeployDriver" can be null, meaning vsmsdeploy task has not initialized the deploy driver to sync]
                    //Currently there is a very slim chance that users can't cancel it if the cancel action falls into this time frame 
                    m_msdeployDriver.IsCancelOperation = true;
                }
            }
            catch (Exception ex)
            {
                Diagnostics.Debug.Fail("Exception on ICancelableTask.Cancel being invoked:" + ex.Message);
            }
        }
 
        #endregion
 
        public object? GetProperty(string propertyName)
        {
#if NET472
            string lowerName = propertyName.ToLower(System.Globalization.CultureInfo.InvariantCulture);
#else
            string lowerName = propertyName.ToLower();
#endif
            switch (lowerName)
            {
                case "msdeployversionstotry":
                    return MSDeployVersionsToTry;
                case "highimportanceeventtypes":
                    return HighImportanceEventTypes;
                default:
                    break;
            }
            return null;
        }
 
        public void AddOptionRule(/*Microsoft.Web.Deployment.DeploymentSyncOptions*/ dynamic option, string ruleName, string handlerType)
        {
            bool ruleExists = false;
            try
            {
                object existingRule = option.Rules[ruleName];
                ruleExists = true;
            }
            catch (KeyNotFoundException) { }
 
            if (!ruleExists)
            {
                dynamic? appOfflineRuleHandler = MSWebDeploymentAssembly.DynamicAssembly?.CreateObject(handlerType, new object[] { });
                if (appOfflineRuleHandler is not null)
                {
                    dynamic? appOfflineRule = MSWebDeploymentAssembly.DynamicAssembly?.CreateObject("Microsoft.Web.Deployment.DeploymentRule",
                        new object[] { ruleName, appOfflineRuleHandler });
                    option.Rules.Add(appOfflineRule);
                }
            }
        }
    }
}