diff --git a/src/Microsoft.DotNet.Publishing.Targets/DeployTargets/Microsoft.Kudu.Publishing.targets b/src/Microsoft.DotNet.Publishing.Targets/DeployTargets/Microsoft.Kudu.Publishing.targets index cbcae67c94ca..e5536e79bb16 100644 --- a/src/Microsoft.DotNet.Publishing.Targets/DeployTargets/Microsoft.Kudu.Publishing.targets +++ b/src/Microsoft.DotNet.Publishing.Targets/DeployTargets/Microsoft.Kudu.Publishing.targets @@ -51,6 +51,15 @@ Copyright (C) Microsoft Corporation. All rights reserved. ParameterName="PublishSiteName" ParameterValue="$(PublishSiteName)"/> + + + + + https://{0}.scm.azurewebsites.net/api/{1} false diff --git a/src/Microsoft.DotNet.Publishing.Targets/DeployTargets/Microsoft.MSDeploy.Publishing.targets b/src/Microsoft.DotNet.Publishing.Targets/DeployTargets/Microsoft.MSDeploy.Publishing.targets index 0bd45dc59037..829a6591dc63 100644 --- a/src/Microsoft.DotNet.Publishing.Targets/DeployTargets/Microsoft.MSDeploy.Publishing.targets +++ b/src/Microsoft.DotNet.Publishing.Targets/DeployTargets/Microsoft.MSDeploy.Publishing.targets @@ -51,6 +51,18 @@ Copyright (C) Microsoft Corporation. All rights reserved. ParameterName="MsDeployServiceUrl" ParameterValue="$(MsDeployServiceUrl)"/> + + + + + + diff --git a/src/Microsoft.DotNet.Publishing.Targets/Microsoft.DotNet.Publishing.targets b/src/Microsoft.DotNet.Publishing.Targets/Microsoft.DotNet.Publishing.targets index 5d1027657b62..1d388373a01a 100644 --- a/src/Microsoft.DotNet.Publishing.Targets/Microsoft.DotNet.Publishing.targets +++ b/src/Microsoft.DotNet.Publishing.Targets/Microsoft.DotNet.Publishing.targets @@ -65,8 +65,8 @@ Copyright (C) Microsoft Corporation. All rights reserved. - - + + @@ -96,9 +96,9 @@ Copyright (C) Microsoft Corporation. All rights reserved. --> - BeforePublish; + PrePublish; CorePublish; - AfterPublish; + PostPublish; diff --git a/src/Microsoft.DotNet.Publishing.Targets/PublishProfiles/FTPProfile.pubxml b/src/Microsoft.DotNet.Publishing.Targets/PublishProfiles/FTPProfile.pubxml index b08840b9e080..0ba9b55777b9 100644 --- a/src/Microsoft.DotNet.Publishing.Targets/PublishProfiles/FTPProfile.pubxml +++ b/src/Microsoft.DotNet.Publishing.Targets/PublishProfiles/FTPProfile.pubxml @@ -14,20 +14,20 @@ by editing this MSBuild file. In order to learn more about this please visit htt - - - + - + + --> \ No newline at end of file diff --git a/src/Microsoft.DotNet.Publishing.Targets/PublishProfiles/FileSystemProfile.pubxml b/src/Microsoft.DotNet.Publishing.Targets/PublishProfiles/FileSystemProfile.pubxml index 52cd33edcdc4..e30c86199e18 100644 --- a/src/Microsoft.DotNet.Publishing.Targets/PublishProfiles/FileSystemProfile.pubxml +++ b/src/Microsoft.DotNet.Publishing.Targets/PublishProfiles/FileSystemProfile.pubxml @@ -12,16 +12,18 @@ by editing this MSBuild file. In order to learn more about this please visit htt - + + --> \ No newline at end of file diff --git a/src/Microsoft.DotNet.Publishing.Targets/PublishProfiles/KuduProfile.pubxml b/src/Microsoft.DotNet.Publishing.Targets/PublishProfiles/KuduProfile.pubxml index 2425faabd9e7..d1a65427280c 100644 --- a/src/Microsoft.DotNet.Publishing.Targets/PublishProfiles/KuduProfile.pubxml +++ b/src/Microsoft.DotNet.Publishing.Targets/PublishProfiles/KuduProfile.pubxml @@ -11,20 +11,20 @@ by editing this MSBuild file. In order to learn more about this please visit htt - - - + - + + --> \ No newline at end of file diff --git a/src/Microsoft.DotNet.Publishing.Targets/PublishProfiles/MSDeployPackageProfile.pubxml b/src/Microsoft.DotNet.Publishing.Targets/PublishProfiles/MSDeployPackageProfile.pubxml index 49ffd437e6b0..e537554f09ab 100644 --- a/src/Microsoft.DotNet.Publishing.Targets/PublishProfiles/MSDeployPackageProfile.pubxml +++ b/src/Microsoft.DotNet.Publishing.Targets/PublishProfiles/MSDeployPackageProfile.pubxml @@ -12,20 +12,20 @@ by editing this MSBuild file. In order to learn more about this please visit htt false - - - + - + + --> \ No newline at end of file diff --git a/src/Microsoft.DotNet.Publishing.Targets/PublishProfiles/MSDeployProfile.pubxml b/src/Microsoft.DotNet.Publishing.Targets/PublishProfiles/MSDeployProfile.pubxml index 17123c402822..673223e464b9 100644 --- a/src/Microsoft.DotNet.Publishing.Targets/PublishProfiles/MSDeployProfile.pubxml +++ b/src/Microsoft.DotNet.Publishing.Targets/PublishProfiles/MSDeployProfile.pubxml @@ -19,21 +19,20 @@ by editing this MSBuild file. In order to learn more about this please visit htt false - - - - - - + --> \ No newline at end of file diff --git a/src/Microsoft.DotNet.Publishing.Tasks/Microsoft.DotNet.Publishing.Tasks.csproj b/src/Microsoft.DotNet.Publishing.Tasks/Microsoft.DotNet.Publishing.Tasks.csproj index e7944b9cb7b6..77f11dbbfc8e 100644 --- a/src/Microsoft.DotNet.Publishing.Tasks/Microsoft.DotNet.Publishing.Tasks.csproj +++ b/src/Microsoft.DotNet.Publishing.Tasks/Microsoft.DotNet.Publishing.Tasks.csproj @@ -52,12 +52,27 @@ + + + + + + + - - + + SR.resx + True + True + + + ResXFileCodeGenerator + SR.Designer.cs + Designer + diff --git a/src/Microsoft.DotNet.Publishing.Tasks/MsDeploy/CommonUtility.cs b/src/Microsoft.DotNet.Publishing.Tasks/MsDeploy/CommonUtility.cs new file mode 100644 index 000000000000..4bb3ac73a1de --- /dev/null +++ b/src/Microsoft.DotNet.Publishing.Tasks/MsDeploy/CommonUtility.cs @@ -0,0 +1,1911 @@ +///-------------------------------------------------------------------------------------------- +/// CommonUtility.cs +/// +/// Common utility function +/// +/// Copyright(c) 2006 Microsoft Corporation +///-------------------------------------------------------------------------------------------- +namespace Microsoft.DotNet.Publishing.Tasks.MsDeploy +{ + using Text = System.Text; + using CultureInfo = System.Globalization.CultureInfo; + using IO = System.IO; + using RegularExpressions = System.Text.RegularExpressions; + using Win32 = Microsoft.Win32; + using Xml = System.Xml; + using Framework = Microsoft.Build.Framework; + using Utilities = Microsoft.Build.Utilities; + using System.Diagnostics; + using Generic = System.Collections.Generic; + using System.Linq; + using System.Reflection; + + internal enum PipelineMetadata + { + // + // + // + // False + // Unknown + // Run + // + // + DestinationRelativePath, + Exclude, + FromTarget, + Category, + }; + + + + internal enum ReplaceRuleMetadata + { + // + // + // + // + // + // + // + // + // + // + ObjectName, + ScopeAttributeName, + ScopeAttributeValue, + TargetAttributeName, + Match, + Replace, + }; + + + + + internal enum SkipRuleMetadata + { + // + // + // + // + // + // + // + // + // + SkipAction, + ObjectName, + AbsolutePath, + XPath, + KeyAttribute, + }; + + internal enum DeclareParameterMetadata + { + // + // + // + // + // + // + // + // + // + Kind, + Scope, + Match, + Description, + DefaultValue, + Tags, + }; + + + internal enum ExistingDeclareParameterMetadata + { + // + // + // + // + // + // + // + // + // + Kind, + Scope, + Match, + }; + + internal enum SimpleSyncParameterMetadata + { + // + // + // + // + // + Value, + } + + internal enum SqlCommandVariableMetaData + { + Value, + IsDeclared, + SourcePath, + SourcePath_RegExExcaped, + DestinationGroup + } + + + internal enum ExistingParameterValiationMetadata + { + Element, + Kind, + ValidationString, + } + internal enum SyncParameterMetadata + { + // + // + // + // + // + // + // + // + Kind, + Scope, + Match, + Value, + Description, + DefaultValue, + Tags, + }; + + internal enum ExistingSyncParameterMetadata + { + // + // + // + // + // + // + // + // + Kind, + Scope, + Match, + Value, + }; + + + + + internal class ParameterInfo + { + public string Name; + public string Value; + public ParameterInfo(string parameterName, string parameterStringValue) + { + Name = parameterName; + Value = parameterStringValue; + } + } + + internal class ProviderOption: ParameterInfo + { + public string FactoryName; + public ProviderOption(string factorName, string parameterName, string parameterStringValue): + base(parameterName, parameterStringValue) + { + FactoryName = factorName; + } + } + + + internal class ParameterInfoWithEntry : ParameterInfo + { + //Kind, + //Scope, + //Match, + //Value, + //Description, + //DefaultValue, + public string Kind; + public string Scope; + public string Match; + public string Description; + public string DefaultValue; + public string Tags; + public string Element; + public string ValidationString; + + public ParameterInfoWithEntry(string name, string value, string kind, string scope, string matchRegularExpression, string description, string defaultValue, string tags, string element, string validationString) : + base(name, value) + { + Kind = kind; + Scope = scope; + Match = matchRegularExpression; + Description = description; + DefaultValue = defaultValue; + Tags = tags; + Element = element; + ValidationString = validationString; + + } + } + + + internal static class Utility + { + static System.Collections.Generic.Dictionary m_wellKnownNamesDict = null; + static System.Collections.Generic.Dictionary m_wellKnownNamesMsdeployDict = null; + + + internal enum IISExpressMetadata + { + WebServerDirectory, WebServerManifest, WebServerAppHostConfigDirectory + } + + public static bool IsInternalMsdeployWellKnownItemMetadata(string name) + { + IISExpressMetadata iisExpressMetadata; + if (System.Enum.TryParse(name, out iisExpressMetadata)) + { + return true; + } + if (string.Compare(name, "Path", System.StringComparison.OrdinalIgnoreCase) == 0) + { + return true; + } + return IsMSBuildWellKnownItemMetadata(name); + } + + // Utility function to filter out known MSBuild Metatdata + public static bool IsMSBuildWellKnownItemMetadata(string name) + { + if (m_wellKnownNamesDict == null) + { + string[] wellKnownNames = + { + "FullPath", + "RootDir", + "Filename", + "Extension", + "RelativeDir", + "Directory", + "RecursiveDir", + "Identity", + "ModifiedTime", + "CreatedTime", + "AccessedTime", + "OriginalItemSpec", + "DefiningProjectDirectory", + "DefiningProjectDirectoryNoRoot", + "DefiningProjectExtension", + "DefiningProjectFile", + "DefiningProjectFullPath", + "DefiningProjectName" + }; + m_wellKnownNamesDict = new System.Collections.Generic.Dictionary(wellKnownNames.GetLength(0), System.StringComparer.OrdinalIgnoreCase); + + foreach (string wellKnownName in wellKnownNames) + { + m_wellKnownNamesDict.Add(wellKnownName, null); + } + } + return m_wellKnownNamesDict.ContainsKey(name); + } + + public static bool IsMsDeployWellKnownLocationInfo(string name) + { + if (m_wellKnownNamesMsdeployDict == null) + { + string[] wellKnownNames = + { + "computerName", + "wmsvc", + "userName", + "password", + "includeAcls", + "encryptPassword", + "authType", + "prefetchPayload", + }; + m_wellKnownNamesMsdeployDict = new System.Collections.Generic.Dictionary(wellKnownNames.GetLength(0), System.StringComparer.OrdinalIgnoreCase); + + foreach (string wellKnownName in wellKnownNames) + { + m_wellKnownNamesMsdeployDict.Add(wellKnownName, null); + } + } + return m_wellKnownNamesMsdeployDict.ContainsKey(name); + } + + static Text.StringBuilder m_stringBuilder = null; + + /// + /// commong utility for Clean share common builder + /// + private static Text.StringBuilder StringBuilder + { + get + { + if (m_stringBuilder == null) + { + m_stringBuilder = new System.Text.StringBuilder(1024); + } + return m_stringBuilder; + } + } + + /// + /// This is the simple share clean build. Since this is an share instance + /// make sure you don't call this on complex operation or it will be zero out unexpectly + /// Use this you need to be simple function which doesn't call any functiont that use this property + /// Sde dev10 bug 699893 + /// + public static Text.StringBuilder CleanStringBuilder + { + get + { + Text.StringBuilder sb = StringBuilder; + sb.Remove(0, sb.Length); + return sb; + } + } + + + + /// + /// Return the current machine's IIS version + /// + /// + public static uint GetInstalledMajorIisVersion() + { + uint iisMajorVersion = 0; + using (Win32.RegistryKey registryKey = Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Inetstp")) + { + if (registryKey != null) + { + iisMajorVersion = System.Convert.ToUInt16(registryKey.GetValue(@"MajorVersion", 0), CultureInfo.InvariantCulture); + } + } + return iisMajorVersion; + } + + + + /// + /// verify it is in IIS6 + /// + /// + /// + public static bool IsIis6(string verFromTarget) + { + return (verFromTarget == "6"); + } + + /// + /// Main version of IIS + /// + public enum IisMainVersion + { + NonIis = 0, + Iis6 = 6, + Iis7 = 7 + } + + /// + /// Return true if MSDeploy is installed + /// + private static bool _isMSDeployInstalled = false; + private static string _strErrorMessage = null; + public static bool IsMSDeployInstalled { + get { + if (_isMSDeployInstalled) { + return true; + } + else if (_strErrorMessage != null) { + return false; + } + else { + try { + _isMSDeployInstalled = CheckMSDeploymentVersion(); + } + catch (System.IO.FileNotFoundException ex) { + _strErrorMessage = string.Format(System.Globalization.CultureInfo.CurrentCulture,SR.VSMSDEPLOY_MSDEPLOYLOADFAIL, + SR.VSMSDEPLOY_MSDEPLOY32bit, + SR.VSMSDEPLOY_MSDEPLOY64bit, + ex.Message); + _isMSDeployInstalled = false; + } + + Debug.Assert(_isMSDeployInstalled || _strErrorMessage != null); + return _isMSDeployInstalled; + } + } + } + + /// + /// Return true if MSDeploy is installed, and report an error to task.Log if it's not + /// + public static bool CheckMSDeploymentVersion( Utilities.TaskLoggingHelper log , out string errorMessage) { + errorMessage = null; + if (!IsMSDeployInstalled) { + errorMessage = _strErrorMessage; + log.LogError(_strErrorMessage); + return false; + } + else { + return true; + } + } + + + /// + /// Utility to check the MinimumVersion of Msdeploy + /// + static string s_strMinimumVersion; + + /// + /// Helper function to determine installed MSDeploy version + /// + private static bool CheckMSDeploymentVersion() { + // Find the MinimumVersionRequirement + System.Version currentMinVersion; + if (!string.IsNullOrEmpty(s_strMinimumVersion)) + { + currentMinVersion = new System.Version(s_strMinimumVersion); + } + else + { + currentMinVersion = new System.Version(7, 1, 614); // current drop + { + string strMinimumVersion = string.Empty; + using (Win32.RegistryKey registryKeyVs = Win32.Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\VisualStudio\11.0\WebDeploy")) + { + if (registryKeyVs != null) + { + s_strMinimumVersion = registryKeyVs.GetValue(@"MinimumMsDeployVersion", string.Empty).ToString(); + } + else + { + using (Win32.RegistryKey registryKeyVsLM = Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\VisualStudio\11.0\WebDeploy")) + { + if (registryKeyVsLM != null) + { + s_strMinimumVersion = registryKeyVsLM.GetValue(@"MinimumMsDeployVersion", string.Empty).ToString(); + } + } + } + } + if (!string.IsNullOrEmpty(s_strMinimumVersion)) + { + currentMinVersion = new System.Version(strMinimumVersion); + } + else + { + s_strMinimumVersion = currentMinVersion.ToString(); + } + } + } + + System.Diagnostics.Debug.Assert(MSWebDeploymentAssembly.DynamicAssembly != null && MSWebDeploymentAssembly.DynamicAssembly.Assembly != null); + if (MSWebDeploymentAssembly.DynamicAssembly != null && MSWebDeploymentAssembly.DynamicAssembly.Assembly != null) + { + System.Reflection.AssemblyName assemblyName = MSWebDeploymentAssembly.DynamicAssembly.Assembly.GetName(); + System.Version minVersion = new System.Version(currentMinVersion.Major, currentMinVersion.Minor); + System.Version assemblyVersion = assemblyName.Version; // assembly version only accurate to the minor version + bool fMinVersionNotMeet = false; + + if (assemblyVersion < minVersion) + { + fMinVersionNotMeet = true; + } + + if (fMinVersionNotMeet) + { + _strErrorMessage = string.Format(System.Globalization.CultureInfo.CurrentCulture, SR.VSMSDEPLOY_MSDEPLOYLOADFAIL, + SR.VSMSDEPLOY_MSDEPLOY32bit, + SR.VSMSDEPLOY_MSDEPLOY64bit, + assemblyVersion, + currentMinVersion); + return false; + } + + return true; + } + else + { + _strErrorMessage = string.Format(System.Globalization.CultureInfo.CurrentCulture, SR.VSMSDEPLOY_MSDEPLOYLOADFAIL, + SR.VSMSDEPLOY_MSDEPLOY32bit, + SR.VSMSDEPLOY_MSDEPLOY64bit, + new System.Version(), + currentMinVersion); + return false; + } + } + + + /// + /// Return a search path for the data + /// + /// + /// + /// + /// + public static string GetNodeFromProjectFile(Xml.XmlDocument doc, Xml.XmlNamespaceManager xmlnsManager, + string xmlPath, string defaultNamespace) + { + if (doc == null) + return null; + + string searchPath = xmlPath; + if (!string.IsNullOrEmpty(defaultNamespace)) + { + RegularExpressions.Regex regex = new System.Text.RegularExpressions.Regex(@"([\w]+)"); + searchPath = regex.Replace(xmlPath, defaultNamespace + @":$1"); + } + + Xml.XmlNode xmlNode = doc.SelectSingleNode(searchPath, xmlnsManager); + if (xmlNode != null) + { + return xmlNode.InnerText; + } + return null; + } + + /// + /// Utility to help build the argument list from the enum type + /// + /// + /// + /// + static internal void BuildArgumentsBaseOnEnumTypeName(Framework.ITaskItem item, System.Collections.Generic.List arguments, System.Type enumType, string valueQuote) + { + string[] enumNames = System.Enum.GetNames(enumType); + foreach (string enumName in enumNames) + { + string data = item.GetMetadata(enumName); + if (!string.IsNullOrEmpty(data)) + { + string valueData = PutValueInQuote(data, valueQuote); + arguments.Add(string.Concat(enumName.ToLower(System.Globalization.CultureInfo.InvariantCulture), "=", valueData)); + } + } + } + + static internal string AlternativeQuote(string valueQuote) + { + if (string.IsNullOrEmpty(valueQuote) || valueQuote == "\"") + { + return "'"; + } + else + { + return "\""; + } + } + + static public char[] s_specialCharactersForCmd = @"&()[]{}^=;!'+,`~".ToArray(); + static internal string PutValueInQuote(string value, string quote) + { + if (string.IsNullOrEmpty(quote)) + { + if (value != null & value.IndexOfAny(s_specialCharactersForCmd) >= 0) + { + // any command line special characters, we use doubld quote by default + quote = "\""; + } + else + { + // otherwise we pick the smart one. + quote = AlternativeQuote(quote); + } + if (!string.IsNullOrEmpty(value)) + { + if (value.Contains(quote)) + { + quote = AlternativeQuote(quote); + } + } + + } + return string.Concat(quote, value, quote); + } + + + public static bool IsOneof(string source, string[] listOfItems, System.StringComparison comparsion) + { + if (listOfItems != null && !string.IsNullOrEmpty(source)) + { + foreach (string item in listOfItems) + { + if (string.Compare(source, item, comparsion) == 0) + { + return true; + } + } + } + return false; + } + + /// + /// Utiilty function to prompt commom end of Execution message for msdeploy.exe + /// + /// + /// + /// + /// + /// + public static void MsDeployExeEndOfExecuteMessage(bool bSuccess, string destType, string destRoot, Utilities.TaskLoggingHelper Log) + { + bool fNeedUnpackageHelpLink = false; + string strSucceedFailMsg; + string[] packageArchivedir = new string[] { MSDeploy.Provider.ArchiveDir, MSDeploy.Provider.Package}; + string[] ArchiveDirOnly = new string[] { MSDeploy.Provider.ArchiveDir }; + if (bSuccess) + { + if (IsOneof(destType, packageArchivedir, System.StringComparison.InvariantCultureIgnoreCase)) + { + //strip off the trailing slash, so IO.Path.GetDirectoryName/GetFileName will return values correctly + destRoot = StripOffTrailingSlashes(destRoot); + + string dir = IO.Path.GetDirectoryName(destRoot); + string dirUri = ConvertAbsPhysicalPathToAbsUriPath(dir); + if (IsOneof(destType, ArchiveDirOnly, System.StringComparison.InvariantCultureIgnoreCase)) + strSucceedFailMsg = string.Format(System.Globalization.CultureInfo.CurrentCulture, SR.VSMSDEPLOY_SucceedArchiveDir, string.IsNullOrEmpty(dirUri) ? destRoot : dirUri); + else + strSucceedFailMsg = string.Format(System.Globalization.CultureInfo.CurrentCulture, SR.VSMSDEPLOY_SucceedPackage, IO.Path.GetFileName(destRoot), string.IsNullOrEmpty(dirUri) ? destRoot : dirUri); + fNeedUnpackageHelpLink = true; + } + else + { + strSucceedFailMsg = SR.VSMSDEPLOY_SucceedDeploy; + } + } + else + { + if (IsOneof(destType, packageArchivedir, System.StringComparison.InvariantCultureIgnoreCase)) + { + strSucceedFailMsg = SR.VSMSDEPLOY_FailedPackage; + } + else + { + strSucceedFailMsg = SR.VSMSDEPLOY_FailedDeploy; + } + } + Log.LogMessage(Framework.MessageImportance.High, strSucceedFailMsg); + if (fNeedUnpackageHelpLink) + { + Log.LogMessage(Framework.MessageImportance.High, SR.VSMSDEPLOY_WebPackageHelpLinkMessage); + Log.LogMessage(Framework.MessageImportance.High, SR.VSMSDEPLOY_WebPackageHelpLink); + } + } + + /// + /// Utiilty function to prompt commom end of Execution message + /// + /// + /// + /// + /// + public static void MsDeployEndOfExecuteMessage(bool bSuccess, string destType, string destRoot, Utilities.TaskLoggingHelper Log ) + { + // Deployment.DeploymentWellKnownProvider wellKnownProvider = Deployment.DeploymentWellKnownProvider.Unknown; + System.Type DeploymentWellKnownProviderType = MSWebDeploymentAssembly.DynamicAssembly.GetType(MSDeploy.TypeName.DeploymentWellKnownProvider); + dynamic wellKnownProvider = MSWebDeploymentAssembly.DynamicAssembly.GetEnumValue(MSDeploy.TypeName.DeploymentWellKnownProvider, "Unknown"); + + if (string.Compare(destType, MSDeploy.Provider.DbDacFx, System.StringComparison.InvariantCultureIgnoreCase) != 0) + { + try + { + wellKnownProvider = System.Enum.Parse(DeploymentWellKnownProviderType, destType, true); + } + catch + { + // don't cause the failure; + } + } + bool fNeedUnpackageHelpLink = false; + string strSucceedFailMsg; + if (bSuccess) + { + if (wellKnownProvider.Equals(MSWebDeploymentAssembly.DynamicAssembly.GetEnumValue(MSDeploy.TypeName.DeploymentWellKnownProvider, MSDeploy.Provider.ArchiveDir)) || + wellKnownProvider.Equals(MSWebDeploymentAssembly.DynamicAssembly.GetEnumValue(MSDeploy.TypeName.DeploymentWellKnownProvider, MSDeploy.Provider.Package))) + { + //strip off the trailing slash, so IO.Path.GetDirectoryName/GetFileName will return values correctly + destRoot = StripOffTrailingSlashes(destRoot); + + string dir = IO.Path.GetDirectoryName(destRoot); + string dirUri = ConvertAbsPhysicalPathToAbsUriPath(dir); + if (wellKnownProvider.Equals(MSWebDeploymentAssembly.DynamicAssembly.GetEnumValue(MSDeploy.TypeName.DeploymentWellKnownProvider, MSDeploy.Provider.ArchiveDir))) + strSucceedFailMsg = string.Format(System.Globalization.CultureInfo.CurrentCulture,SR.VSMSDEPLOY_SucceedArchiveDir, string.IsNullOrEmpty(dirUri) ? destRoot : dirUri); + else + strSucceedFailMsg = string.Format(System.Globalization.CultureInfo.CurrentCulture,SR.VSMSDEPLOY_SucceedPackage, IO.Path.GetFileName(destRoot), string.IsNullOrEmpty(dirUri) ? destRoot : dirUri); + fNeedUnpackageHelpLink = true; + } + else + { + strSucceedFailMsg = SR.VSMSDEPLOY_SucceedDeploy; + } + + + } + else + { + if (wellKnownProvider.Equals(MSWebDeploymentAssembly.DynamicAssembly.GetEnumValue(MSDeploy.TypeName.DeploymentWellKnownProvider, MSDeploy.Provider.ArchiveDir)) || + wellKnownProvider.Equals(MSWebDeploymentAssembly.DynamicAssembly.GetEnumValue(MSDeploy.TypeName.DeploymentWellKnownProvider, MSDeploy.Provider.Package))) + { + strSucceedFailMsg = SR.VSMSDEPLOY_FailedPackage; + } + else + { + strSucceedFailMsg = SR.VSMSDEPLOY_FailedDeploy; + } + } + Log.LogMessage(Framework.MessageImportance.High, strSucceedFailMsg); + if (fNeedUnpackageHelpLink) + { + Log.LogMessage(Framework.MessageImportance.High, SR.VSMSDEPLOY_WebPackageHelpLinkMessage); + Log.LogMessage(Framework.MessageImportance.High, SR.VSMSDEPLOY_WebPackageHelpLink); + } + } + + static public string ConvertAbsPhysicalPathToAbsUriPath(string physicalPath) + { + string absUriPath = string.Empty; + try + { + System.Uri uri = new System.Uri(physicalPath); + if (uri.IsAbsoluteUri) + absUriPath = uri.AbsoluteUri; + } + catch { } + return absUriPath; + } + + // utility function to add the replace rule for the option + static public void AddReplaceRulesToOptions(/*Deployment.DeploymentRuleCollection*/ dynamic syncConfigRules, Framework.ITaskItem[] replaceRuleItems) + { + if (syncConfigRules != null && replaceRuleItems != null)// Dev10 bug 496639 foreach will throw the exception if the replaceRuleItem is null + { + foreach (Framework.ITaskItem item in replaceRuleItems) + { + string ruleName = item.ItemSpec; + string objectName = item.GetMetadata(MsDeploy.ReplaceRuleMetadata.ObjectName.ToString()); + string matchRegularExpression = item.GetMetadata(MsDeploy.ReplaceRuleMetadata.Match.ToString()); + string replaceWith = item.GetMetadata(MsDeploy.ReplaceRuleMetadata.Replace.ToString()); + string scopeAttributeName = item.GetMetadata(MsDeploy.ReplaceRuleMetadata.ScopeAttributeName.ToString()); + string scopeAttributeValue = item.GetMetadata(MsDeploy.ReplaceRuleMetadata.ScopeAttributeValue.ToString()); + string targetAttributeName = item.GetMetadata(MsDeploy.ReplaceRuleMetadata.TargetAttributeName.ToString()); + + + ///*Deployment.DeploymentReplaceRule*/ dynamic replaceRule = + // new Deployment.DeploymentReplaceRule(ruleName, objectName, scopeAttributeName, + // scopeAttributeValue, targetAttributeName, matchRegularExpression, replaceWith); + + + /*Deployment.DeploymentReplaceRule*/ + dynamic replaceRule = MSWebDeploymentAssembly.DynamicAssembly.CreateObject("Microsoft.Web.Deployment.DeploymentReplaceRule", + new object[]{ruleName, objectName, scopeAttributeName, + scopeAttributeValue, targetAttributeName, matchRegularExpression, replaceWith}); + + + syncConfigRules.Add(replaceRule); + } + } + } + + /// + /// utility function to enable the skip directive's enable state + /// + /// + /// + /// + /// + static internal void AdjsutSkipDirectives(/*Deployment.DeploymentBaseOptions*/ dynamic baseOptions, Generic.List stringList, bool enabled, Utilities.TaskLoggingHelper log) + { + if (stringList != null && baseOptions != null) + { + foreach (string name in stringList) + { + foreach (/*Deployment.DeploymentSkipDirective*/ dynamic skipDirective in baseOptions.SkipDirectives) + { + if (string.Compare(skipDirective.Name, name, System.StringComparison.OrdinalIgnoreCase) == 0) + { + if (skipDirective.Enabled != enabled) + { + skipDirective.Enabled = enabled; + } + log.LogMessage(string.Format(System.Globalization.CultureInfo.CurrentCulture,SR.VSMSDEPLOY_SkipDirectiveSetEnable, skipDirective.Name, enabled.ToString())); + + } + } + log.LogWarning(string.Format(System.Globalization.CultureInfo.CurrentCulture,SR.VSMSDEPLOY_UnknownSkipDirective, name)); + } + } + } + + // utility function to add the skip rule for the option + static public void AddSkipDirectiveToBaseOptions(/*Deployment.DeploymentBaseOptions*/ dynamic baseOptions , + Framework.ITaskItem[] skipRuleItems, + Generic.List enableSkipDirectiveList, + Generic.List disableSkipDirectiveList, + Utilities.TaskLoggingHelper log) + { + if (baseOptions != null && skipRuleItems != null) + { + System.Collections.Generic.List arguments = new System.Collections.Generic.List(6); + + foreach (Framework.ITaskItem item in skipRuleItems) + { + arguments.Clear(); + MsDeploy.Utility.BuildArgumentsBaseOnEnumTypeName(item, arguments, typeof(SkipRuleMetadata), "\""); + if (arguments.Count > 0) + { + string name = item.ItemSpec; + ///*Deployment.DeploymentSkipDirective*/ dynamic skipDirective = new Microsoft.Web.Deployment.DeploymentSkipDirective(name, string.Join(",", arguments.ToArray()), true); + + /*Deployment.DeploymentSkipDirective*/ + dynamic skipDirective = MSWebDeploymentAssembly.DynamicAssembly.CreateObject("Microsoft.Web.Deployment.DeploymentSkipDirective", new object[]{name, string.Join(",", arguments.ToArray()), true}); + baseOptions.SkipDirectives.Add(skipDirective); + } + } + AdjsutSkipDirectives(baseOptions, enableSkipDirectiveList, true, log); + AdjsutSkipDirectives(baseOptions, disableSkipDirectiveList, false, log); + } + } + + + /// + /// Utility to add single DeclareParameter to the list + /// + /// + /// + static public void AddDeclarParameterToOptions(/*VSMSDeploySyncOption*/ dynamic vSMSDeploySyncOption, Framework.ITaskItem item) + { + if (item != null && vSMSDeploySyncOption != null) + { + string name = item.ItemSpec; + string elemment = item.GetMetadata(MsDeploy.ExistingParameterValiationMetadata.Element.ToString()); + if (string.IsNullOrEmpty(elemment)) + elemment = "parameterEntry"; + string kind = item.GetMetadata(MsDeploy.DeclareParameterMetadata.Kind.ToString()); + string scope = item.GetMetadata(MsDeploy.DeclareParameterMetadata.Scope.ToString()); + string matchRegularExpression = item.GetMetadata(MsDeploy.DeclareParameterMetadata.Match.ToString()); + string description = item.GetMetadata(MsDeploy.DeclareParameterMetadata.Description.ToString()); + string defaultValue = item.GetMetadata(MsDeploy.DeclareParameterMetadata.DefaultValue.ToString()); + string tags = item.GetMetadata(MsDeploy.DeclareParameterMetadata.Tags.ToString()); + + dynamic deploymentSyncParameter= null; + // the following have out argument, can't use dynamic on it + // vSMSDeploySyncOption.DeclaredParameters.TryGetValue(name, out deploymentSyncParameter); + MSWebDeploymentAssembly.DeploymentTryGetValueContains(vSMSDeploySyncOption.DeclaredParameters, name, out deploymentSyncParameter); + + if (deploymentSyncParameter == null) + { + deploymentSyncParameter = + MSWebDeploymentAssembly.DynamicAssembly.CreateObject("Microsoft.Web.Deployment.DeploymentSyncParameter", new object[]{name, description, defaultValue, tags}); + vSMSDeploySyncOption.DeclaredParameters.Add(deploymentSyncParameter); + } + if (!string.IsNullOrEmpty(kind)) + { + if (string.Compare(elemment, "parameterEntry", System.StringComparison.OrdinalIgnoreCase) == 0) + { + bool fAddEntry = true; + foreach (dynamic entry in deploymentSyncParameter.Entries) + { + if (scope.Equals(entry.Scope) && + matchRegularExpression.Equals(entry.Match) && + string.Compare(entry.Kind.ToString(), kind, System.StringComparison.OrdinalIgnoreCase) == 0) + { + fAddEntry = false; + } + } + if (fAddEntry) + { + dynamic parameterEntry = MSWebDeploymentAssembly.DynamicAssembly.CreateObject("Microsoft.Web.Deployment.DeploymentSyncParameterEntry", + new object[] { kind, scope, matchRegularExpression, string.Empty }); + deploymentSyncParameter.Add(parameterEntry); + } + } + else if (string.Compare(elemment, "parameterValidation", System.StringComparison.OrdinalIgnoreCase) == 0) + { + // this is bogus assertion because by default msdeploy always setup the validation which is never be null + // System.Diagnostics.Debug.Assert(deploymentSyncParameter.Validation == null, "deploymentSyncParameter.Validation is already set"); + string validationString = item.GetMetadata(MsDeploy.ExistingParameterValiationMetadata.ValidationString.ToString()); + + object validationKindNone = MSWebDeploymentAssembly.DynamicAssembly.GetEnumValue("Microsoft.Web.Deployment.DeploymentSyncParameterValidationKind", "None"); + dynamic validationKind = validationKindNone; + System.Type validationKindType = validationKind.GetType(); + dynamic currentvalidationKind = validationKindNone; + + string[] validationKinds = kind.Split(new char[] { ',' }); + + foreach (string strValidationKind in validationKinds) + { + if (MSWebDeploymentAssembly.DynamicAssembly.TryGetEnumValue("Microsoft.Web.Deployment.DeploymentSyncParameterValidationKind", strValidationKind, out currentvalidationKind)) + { + validationKind = System.Enum.ToObject(validationKindType, ((int)(validationKind)) | ((int)(currentvalidationKind))); + } + } + // dynamic doesn't compare, since this is enum, cast to int to compare + if ((int)validationKind != (int)validationKindNone) + { + // due to the reflection the we can't + // $exception {"Cannot implicitly convert type 'object' to 'Microsoft.Web.Deployment.DeploymentSyncParameterValidation'. An explicit conversion exists (are you missing a cast?)"} System.Exception {Microsoft.CSharp.RuntimeBinder.RuntimeBinderException} + object parameterValidation = + MSWebDeploymentAssembly.DynamicAssembly.CreateObject("Microsoft.Web.Deployment.DeploymentSyncParameterValidation", new object[] {validationKind, validationString}); + SetDynamicProperty(deploymentSyncParameter, "Validation", parameterValidation); + } + } + } + } + } + + /// + /// Utility function to avoid the reflection exception when assign to a strongtype property + /// // $exception {"Cannot implicitly convert type 'object' to 'Microsoft.Web.Deployment.DeploymentSyncParameterValidation'. An explicit conversion exists (are you missing a cast?)"} System.Exception {Microsoft.CSharp.RuntimeBinder.RuntimeBinderException} + /// + /// + /// + /// + static public void SetDynamicProperty(dynamic thisObj, string propertyName, object value) + { + thisObj.GetType().GetProperty(propertyName).SetValue(thisObj, value, null); + } + + + /// + /// Utility function to add DeclareParameter in line + /// + /// + /// + static public void AddDeclareParametersToOptions(/*VSMSDeploySyncOption*/ dynamic vSMSDeploySyncOption, Framework.ITaskItem[] originalItems, bool fOptimisticPickNextDefaultValue) + { + System.Collections.Generic.IList items = MsDeploy.Utility.SortParametersTaskItems(originalItems, fOptimisticPickNextDefaultValue, MsDeploy.DeclareParameterMetadata.DefaultValue.ToString()); + if (vSMSDeploySyncOption != null && items != null) + { + foreach (Framework.ITaskItem item in items) + { + AddDeclarParameterToOptions(vSMSDeploySyncOption, item); + } + } + } + + + // MSDeploy change -- Deprecate + ///// + ///// Utility function to support DeclarParametersFromFile + ///// + ///// + ///// + static public void AddImportDeclareParametersFileOptions(/*VSMSDeploySyncOption*/ dynamic vSMSDeploySyncOption, Framework.ITaskItem[] items) + { + if (vSMSDeploySyncOption != null && items != null) + { + foreach (Framework.ITaskItem item in items) + { + string fileName = item.ItemSpec; + vSMSDeploySyncOption.DeclaredParameters.Load(fileName); + } + } + } + + static public void AddSetParametersFilesToObject(/*Deployment.DeploymentObject*/ dynamic deploymentObject, Generic.IList filenames, IVSMSDeployHost host) + { + if (deploymentObject != null && filenames != null) + { + foreach (string filename in filenames) + { + if (!string.IsNullOrEmpty(filename)) + { + try + { + deploymentObject.SyncParameters.Load(filename); + } + catch (System.Exception e) + { + if (host != null) + host.Log.LogErrorFromException(e); + else + throw; + } + } + } + } + } + + /// + /// Utility function to set SimpleeSyncParameter Name/Value + /// + /// + /// + static public void AddSimpleSetParametersVsMsDeployObject(MsDeploy.VSMSDeployObject srcVsMsDeployobject, Framework.ITaskItem[] originalItems, bool fOptimisticPickNextDefaultValue) + { + System.Collections.Generic.IList items = MsDeploy.Utility.SortParametersTaskItems(originalItems, fOptimisticPickNextDefaultValue, MsDeploy.SimpleSyncParameterMetadata.Value.ToString()); + if (srcVsMsDeployobject != null && items != null) + { + string lastItemName = string.Empty; + foreach (Framework.ITaskItem item in items) + { + string name = item.ItemSpec; + if (string.CompareOrdinal(name, lastItemName) != 0) + { + string value = item.GetMetadata(MsDeploy.SimpleSyncParameterMetadata.Value.ToString()); + srcVsMsDeployobject.SyncParameter(name, value); + lastItemName = name; + } + } + } + } + + static public void AddProviderOptions(/*Deployment.DeploymentProviderOptions*/ dynamic deploymentProviderOptions, Generic.IList providerOptions, IVSMSDeployHost host) + { + if (deploymentProviderOptions != null && providerOptions != null) + { + foreach (ProviderOption item in providerOptions) + { + string factoryName = item.FactoryName; + string name = item.Name; + string value = item.Value; + // Error handling is not required here if the providerOptions list is different from deploymentProviderOptions.ProviderSettings. + // providerOptions list contains metadata from MSBuild and this may be different from deploymentProviderOptions.ProviderSettings. + if (string.Compare(factoryName, deploymentProviderOptions.Factory.Name, System.StringComparison.OrdinalIgnoreCase) == 0) + { + dynamic setting = null; + + // deploymentProviderOptions.ProviderSettings.TryGetValue(name, out setting); + MSWebDeploymentAssembly.DeploymentTryGetValueForEach(deploymentProviderOptions.ProviderSettings, name, out setting); + if (setting != null) + { + setting.Value = value; + } + } + } + } + } + + + /// + /// Utility function to set SimpleeSyncParameter Name/Value + /// + /// + /// + static public void AddSimpleSetParametersToObject(/*Deployment.DeploymentObject*/ dynamic deploymentObject, Generic.IList parameters, IVSMSDeployHost host) + { + if (deploymentObject != null && parameters != null) + { + Generic.Dictionary nameValueDictionary = new System.Collections.Generic.Dictionary(parameters.Count, System.StringComparer.OrdinalIgnoreCase); + foreach (ParameterInfo item in parameters) + { + string name = item.Name; + string value; + if (!nameValueDictionary.TryGetValue(name, out value)) + { + value = item.Value; + } + + dynamic parameter = null; + // deploymentObject.SyncParameters.TryGetValue(name, out parameter); + MSWebDeploymentAssembly.DeploymentTryGetValueContains(deploymentObject.SyncParameters, name, out parameter); + string msg = string.Format(System.Globalization.CultureInfo.CurrentCulture,SR.VSMSDEPLOY_AddParameterIntoObject, name, value, deploymentObject.Name); + host.Log.LogMessage(msg); + if (parameter != null) + { + parameter.Value = value; + } + else + { + // Try to get error message to show. + Text.StringBuilder sb = MsDeploy.Utility.CleanStringBuilder; + foreach (dynamic param in deploymentObject.SyncParameters) + { + if (sb.Length != 0) + { + sb.Append(", "); + } + sb.Append(param.Name); + } + // To do, change this to resource + string errMessage = string.Format(System.Globalization.CultureInfo.CurrentCulture,SR.VSMSDEPLOY_UnknownParameter, name, sb.ToString()); + if (host != null) + { + throw new System.InvalidOperationException(errMessage); + } + } + } + } + } + + + /// + /// Utility function to setParameters in type, scope, match, value of SyncParameter + /// + /// + /// + static public void AddSetParametersToObject(/*Deployment.DeploymentObject*/ dynamic deploymentObject, Generic.IList parameters, IVSMSDeployHost host) + { + if (deploymentObject != null && parameters != null) + { + Generic.Dictionary nameValueDictionary = new System.Collections.Generic.Dictionary(parameters.Count, System.StringComparer.OrdinalIgnoreCase); + Generic.Dictionary entryIdentityDictionary = new System.Collections.Generic.Dictionary(parameters.Count); + + foreach (ParameterInfoWithEntry item in parameters) + { + try + { + string data = null; + if (!nameValueDictionary.TryGetValue(item.Name, out data)) + { + nameValueDictionary.Add(item.Name, item.Value); + data = item.Value; + } + + dynamic parameter = null; + dynamic parameterEntry = null; + dynamic parameterValidation = null; + if (!string.IsNullOrEmpty(item.Kind)) + { + string identityString = string.Join(";", new string []{item.Name, item.Kind, item.Scope, item.Match, item.Element, item.ValidationString}); + if (!entryIdentityDictionary.ContainsKey(identityString)) + { + if (string.Compare(item.Element, "parameterEntry", System.StringComparison.OrdinalIgnoreCase) == 0) + { + parameterEntry = MSWebDeploymentAssembly.DynamicAssembly.CreateObject("Microsoft.Web.Deployment.DeploymentSyncParameterEntry", + new object[]{item.Kind, item.Scope, item.Match, string.Empty}); + } + else if (string.Compare(item.Element, "parameterValidation", System.StringComparison.OrdinalIgnoreCase) == 0) + { + + // this is bogus assertion because by default msdeploy always setup the validation which is never be null + // System.Diagnostics.Debug.Assert(deploymentSyncParameter.Validation == null, "deploymentSyncParameter.Validation is already set"); + string validationString = item.ValidationString; + + object validationKindNone = MSWebDeploymentAssembly.DynamicAssembly.GetEnumValue("Microsoft.Web.Deployment.DeploymentSyncParameterValidationKind", "None"); + dynamic validationKind = validationKindNone; + System.Type validationKindType = validationKind.GetType(); + dynamic currentvalidationKind = validationKindNone; + + string[] validationKinds = item.Kind.Split(new char[] { ',' }); + + foreach (string strValidationKind in validationKinds) + { + if (MSWebDeploymentAssembly.DynamicAssembly.TryGetEnumValue("Microsoft.Web.Deployment.DeploymentSyncParameterValidationKind", strValidationKind, out currentvalidationKind)) + { + validationKind = System.Enum.ToObject(validationKindType, ((int)(validationKind)) | ((int)(currentvalidationKind))); + } + } + // dynamic doesn't compare, since this is enum, cast to int to compare + if ((int)validationKind != (int)validationKindNone) + { + parameterValidation = + MSWebDeploymentAssembly.DynamicAssembly.CreateObject("Microsoft.Web.Deployment.DeploymentSyncParameterValidation", new object[] { validationKind, validationString }); + } + + + + + //Microsoft.Web.Deployment.DeploymentSyncParameterValidationKind validationKind = Microsoft.Web.Deployment.DeploymentSyncParameterValidationKind.None; + //Microsoft.Web.Deployment.DeploymentSyncParameterValidationKind currentvalidationKind; + //string[] validationKinds = item.Kind.Split(new char[] { ',' }); + + //foreach (string strValidationKind in validationKinds) + //{ + // if (System.Enum.TryParse(strValidationKind, out currentvalidationKind)) + // { + // validationKind |= currentvalidationKind; + // } + //} + + //if (validationKind != Microsoft.Web.Deployment.DeploymentSyncParameterValidationKind.None) + //{ + + // parameterValidation = new Microsoft.Web.Deployment.DeploymentSyncParameterValidation(validationKind, item.ValidationString); + //} + } + entryIdentityDictionary.Add(identityString, null); + } + } + + if (!MSWebDeploymentAssembly.DeploymentTryGetValueContains(deploymentObject.SyncParameters, item.Name, out parameter)) + { + parameter = MSWebDeploymentAssembly.DynamicAssembly.CreateObject("Microsoft.Web.Deployment.DeploymentSyncParameter", + new object[] {item.Name, item.Description, item.DefaultValue, item.Tags}); + deploymentObject.SyncParameters.Add(parameter); + parameter.Value = data; + } + if (parameterEntry != null) + { + parameter.Add(parameterEntry); + } + if (parameterValidation != null) + { + // due to the reflection, compiler complain on assign a object to type without explicit convertion + // parameter.Validation = parameterValidation; + SetDynamicProperty(parameter, "Validation", parameterValidation); + } + } + catch (System.Exception e) + { + if (host != null) + host.Log.LogErrorFromException(e); + else + throw; + } + } + } + } + + + + /// + /// Utility function to setParameters in type, scope, match, value of SyncParameter + /// + /// + /// + static public void AddSetParametersVsMsDeployObject(MsDeploy.VSMSDeployObject srcVsMsDeployobject, Framework.ITaskItem[] originalItems, bool fOptimisticPickNextDefaultValue) + { + System.Collections.Generic.IList items = MsDeploy.Utility.SortParametersTaskItems(originalItems, fOptimisticPickNextDefaultValue, MsDeploy.SyncParameterMetadata.DefaultValue.ToString()); + if (srcVsMsDeployobject != null && items != null) + { + foreach (Framework.ITaskItem item in items) + { + string name = item.ItemSpec; + string kind = item.GetMetadata(MsDeploy.SyncParameterMetadata.Kind.ToString()); + string scope = item.GetMetadata(MsDeploy.SyncParameterMetadata.Scope.ToString()); + string matchRegularExpression = item.GetMetadata(MsDeploy.SyncParameterMetadata.Match.ToString()); + string value = item.GetMetadata(MsDeploy.SyncParameterMetadata.Value.ToString()); + string description = item.GetMetadata(MsDeploy.SyncParameterMetadata.Description.ToString()); + string defaultValue = item.GetMetadata(MsDeploy.SyncParameterMetadata.DefaultValue.ToString()); + string tags = item.GetMetadata(MsDeploy.SyncParameterMetadata.Tags.ToString()); + string element = item.GetMetadata(MsDeploy.ExistingParameterValiationMetadata.Element.ToString()); + if (string.IsNullOrEmpty(element)) + element = "parameterEntry"; + string validationString = item.GetMetadata(MsDeploy.ExistingParameterValiationMetadata.ValidationString.ToString()); + + + if (string.IsNullOrEmpty(value)) + { + value = defaultValue; + } + + srcVsMsDeployobject.SyncParameter(name, value, kind, scope, matchRegularExpression, description, defaultValue, tags, element, validationString); + + } + } + } + + static public void AddSetParametersFilesVsMsDeployObject(VSMSDeployObject srcVsMsDeployobject, Framework.ITaskItem[] items) + { + if (srcVsMsDeployobject != null && items != null) + { + foreach (Framework.ITaskItem item in items) + { + string filename = item.ItemSpec; + srcVsMsDeployobject.SyncParameterFile(filename); + } + } + } + + + + + static public string DumpITeaskItem(Framework.ITaskItem iTaskItem) + { + Text.StringBuilder sb = MsDeploy.Utility.CleanStringBuilder; + string itemspec = iTaskItem.ItemSpec; + sb.Append(""); + + foreach (string name in iTaskItem.MetadataNames) + { + string value = iTaskItem.GetMetadata(name); + sb.Append(@"<"); + sb.Append(name); + sb.Append(@">"); + sb.Append(value); + sb.Append(@""); + } + sb.Append(@""); + + return sb.ToString(); + } + + + + public static bool IsDeploymentWellKnownProvider(string strProvider) + { + if (string.Compare(strProvider, MSDeploy.Provider.DbDacFx, System.StringComparison.InvariantCultureIgnoreCase) == 0) + { + return true; + } + object DeploymentWellKnownProviderUnknown = MSWebDeploymentAssembly.DynamicAssembly.GetEnumValue(MSDeploy.TypeName.DeploymentWellKnownProvider , MSDeploy.Provider.Unknown) ; + object deploymentProvider = DeploymentWellKnownProviderUnknown; + try + { + deploymentProvider = MSWebDeploymentAssembly.DynamicAssembly.GetEnumValueIgnoreCase(MSDeploy.TypeName.DeploymentWellKnownProvider, strProvider); + } + catch (System.Exception) + { + } + return deploymentProvider != DeploymentWellKnownProviderUnknown; + + } + + /// + /// Utility function to remove all Empty Directory + /// + /// + internal static void RemoveAllEmptyDirectories(string dirPath, Utilities.TaskLoggingHelper Log) + { + if (!string.IsNullOrEmpty(dirPath) && IO.Directory.Exists(dirPath)) + { + IO.DirectoryInfo dirInfo = new System.IO.DirectoryInfo(dirPath); + RemoveAllEmptyDirectories(dirInfo, Log); + } + } + + internal static void RemoveAllEmptyDirectories(IO.DirectoryInfo dirinfo, Utilities.TaskLoggingHelper log) + { + if(dirinfo != null && dirinfo.Exists) + { + //Depth first search. + foreach (IO.DirectoryInfo subDirInfo in dirinfo.GetDirectories()) + { + RemoveAllEmptyDirectories(subDirInfo, log); + } + + if (dirinfo.GetFileSystemInfos().GetLength(0) == 0) + { + dirinfo.Delete(); + if (log != null) + { + log.LogMessage(Microsoft.Build.Framework.MessageImportance.Normal, string.Format(System.Globalization.CultureInfo.CurrentCulture,SR.BUILDTASK_RemoveEmptyDirectories_Deleting, dirinfo.FullName)); + } + + } + } + } + + static PriorityIndexComparer s_PriorityIndexComparer = null; + internal static PriorityIndexComparer ParameterTaskComparer + { + get + { + if (s_PriorityIndexComparer == null) + { + s_PriorityIndexComparer = new PriorityIndexComparer(); + } + return s_PriorityIndexComparer; + } + } + + public static Generic.IList SortParametersTaskItems(Framework.ITaskItem[] taskItems, bool fOptimisticPickNextNonNullDefaultValue, string PropertyName) + { + Generic.IList sortedList = SortTaskItemsByPriority(taskItems); + + if (!fOptimisticPickNextNonNullDefaultValue || string.IsNullOrEmpty(PropertyName) || taskItems == null || taskItems.GetLength(0) <= 0) + { + return sortedList; + } + else + { + Generic.List optimizedValueList = new System.Collections.Generic.List(sortedList); + + Generic.Dictionary FoundDictionary = new System.Collections.Generic.Dictionary(optimizedValueList.Count, System.StringComparer.OrdinalIgnoreCase); + + int maxCount = sortedList.Count; + int i = 0; + + while (i < maxCount) + { + int currentItemIndex = i; + Framework.ITaskItem item = optimizedValueList[i++]; + string itemSpec = item.ItemSpec; + if (FoundDictionary.ContainsKey(itemSpec)) + { + continue; // already scaned, move on to the next + } + else + { + bool fIsCurrentItemEmpty = string.IsNullOrEmpty(item.GetMetadata(PropertyName)); + if (!fIsCurrentItemEmpty) + { + FoundDictionary[itemSpec] = true; + continue; + } + else + { + int next = i; + bool found = false; + while (next < maxCount) + { + Framework.ITaskItem nextitem = optimizedValueList[next++]; + if (string.Compare(itemSpec, nextitem.ItemSpec, System.StringComparison.OrdinalIgnoreCase) == 0) + { + string itemData = nextitem.GetMetadata(PropertyName); + if (!string.IsNullOrEmpty(itemData)) + { + // Get the data from the next best data + Utilities.TaskItem newItem = new Microsoft.Build.Utilities.TaskItem(item); + newItem.SetMetadata(PropertyName, itemData); + optimizedValueList[currentItemIndex] = newItem; + FoundDictionary[itemSpec] = true; // mark that we already fond teh item; + found = true; + break; + } + } + } + if (!found) + { + FoundDictionary[itemSpec] = false; // mark that we scan through the item and not found anything + } + } + } + } + return optimizedValueList; + } + } + + + static string strMsdeployFwlink1 = @"http://go.microsoft.com/fwlink/?LinkId=178034"; + static string strMsdeployFwlink2 = @"http://go.microsoft.com/fwlink/?LinkId=178035"; + static string strMsdeployFwlink3 = @"http://go.microsoft.com/fwlink/?LinkId=178036"; + static string strMsdeployFwlink4 = @"http://go.microsoft.com/fwlink/?LinkId=178587"; + static string strMsdeployFwlink5 = @"http://go.microsoft.com/fwlink/?LinkId=178589"; + static internal string strMsdeployInstallationFwdLink = @"http://go.microsoft.com/?linkid=9278654"; + + static string[] strMsdeployFwlinks = { strMsdeployFwlink1, strMsdeployFwlink2, strMsdeployFwlink3, strMsdeployFwlink4, strMsdeployFwlink5 }; + + static int ContainMsdeployFwlink(string errorMessage, out string provider) + { + int index = -1; + provider = null; + string[][] strMsDeployFwlinksArray = { strMsdeployFwlinks}; + foreach (string[] Fwlinks in strMsDeployFwlinksArray) + { + for (int i = 0; i < Fwlinks.Length; i++) + { + string fwlink = Fwlinks[i]; + int lastIndexOfFwLink = -1; + if ( (lastIndexOfFwLink = errorMessage.LastIndexOf(fwlink, System.StringComparison.OrdinalIgnoreCase)) >= 0) + { + index = i; + if (i == 0) + { + string subError = errorMessage.Substring(0, lastIndexOfFwLink); + subError = subError.Trim(); + if ((lastIndexOfFwLink = subError.LastIndexOf(" ", System.StringComparison.Ordinal)) >= 0) + { + provider = subError.Substring(lastIndexOfFwLink + 1); + } + } + return index; // break out + } + } + } + return index; + } + + internal static bool IsType(System.Type type, System.Type checkType) + { + if (checkType!=null && + (type == checkType || type.IsSubclassOf(checkType))) + { + return true; + } + return false; + } + + internal static string EnsureTrailingSlash(string str) + { + if (str != null && !str.EndsWith("/", System.StringComparison.Ordinal)) + { + str += "/"; + } + return str; + } + internal static string EnsureTrailingBackSlash(string str) + { + if (str != null && !str.EndsWith("\\", System.StringComparison.Ordinal)) + { + str += "\\"; + } + return str; + } + + // Utility to log VsMsdeploy Exception + internal static void LogVsMsDeployException( Utilities.TaskLoggingHelper Log, System.Exception e) + { + if (e is System.Reflection.TargetInvocationException) + { + if (e.InnerException != null) + e = e.InnerException; + } + + System.Text.StringBuilder strBuilder = new System.Text.StringBuilder(e.Message.Length * 4); + System.Type t = e.GetType(); + if (IsType(t, MSWebDeploymentAssembly.DynamicAssembly.GetType( MSDeploy.TypeName.DeploymentEncryptionException))) + { + // dev10 695263 OGF: Encryption Error message needs more information for packaging + strBuilder.Append(SR.VSMSDEPLOY_EncryptionExceptionMessage); + } + else if (IsType(t,MSWebDelegationAssembly.DynamicAssembly.GetType(MSDeploy.TypeName.DeploymentException))) + { + System.Exception rootException = e; + dynamic lastDeploymentException = e; + while (rootException != null && rootException.InnerException != null) + { + rootException = rootException.InnerException; + if (IsType(rootException.GetType(), MSWebDelegationAssembly.DynamicAssembly.GetType(MSDeploy.TypeName.DeploymentException))) + lastDeploymentException = rootException; + } + + + bool isWebException = rootException is System.Net.WebException; + if (isWebException) + { + System.Net.WebException webException = rootException as System.Net.WebException; + + // 404 come in as ProtocolError + if (webException.Status == System.Net.WebExceptionStatus.ProtocolError) + { + if (webException.Message.LastIndexOf("401", System.StringComparison.Ordinal) >= 0) + { + strBuilder.Append(SR.VSMSDEPLOY_WebException401Message); + } + else if (webException.Message.LastIndexOf("404", System.StringComparison.Ordinal) >= 0) + { + strBuilder.Append(SR.VSMSDEPLOY_WebException404Message); + } + else if (webException.Message.LastIndexOf("502", System.StringComparison.Ordinal) >= 0) + { + strBuilder.Append(SR.VSMSDEPLOY_WebException502Message); + } + else if (webException.Message.LastIndexOf("550", System.StringComparison.Ordinal) >= 0) + { + strBuilder.Append(SR.VSMSDEPLOY_WebException550Message); + } + else if (webException.Message.LastIndexOf("551", System.StringComparison.Ordinal) >= 0) + { + strBuilder.Append(SR.VSMSDEPLOY_WebException551Message); + } + } + else if (webException.Status == System.Net.WebExceptionStatus.ConnectFailure) + { + strBuilder.Append(SR.VSMSDEPLOY_WebExceptionConnectFailureMessage); + } + } + else if (rootException is System.Net.Sockets.SocketException) + { + strBuilder.Append(SR.VSMSDEPLOY_WebExceptionConnectFailureMessage); + } + else + { + string strMsg = lastDeploymentException.Message; + string provider; + int index = ContainMsdeployFwlink(strMsg, out provider); + if (index >= 0) + { + object DeploymentWellKnownProviderUnknown = MSWebDeploymentAssembly.DynamicAssembly.GetEnumValue(MSDeploy.TypeName.DeploymentWellKnownProvider, MSDeploy.Provider.Unknown); + + dynamic wellKnownProvider = DeploymentWellKnownProviderUnknown; + // fwdlink1 + if (index == 0) + { + string srErrorMessage = SR.VSMSDEPLOY_MsDeployExceptionFwlink1Message; + if (provider.LastIndexOf("sql", System.StringComparison.OrdinalIgnoreCase) >= 0) + { + srErrorMessage = SR.VSMSDEPLOY_MsDeployExceptionFwlink1SQLMessage; + } + else + { + try + { + wellKnownProvider = MSWebDeploymentAssembly.DynamicAssembly.GetEnumValueIgnoreCase(MSDeploy.TypeName.DeploymentWellKnownProvider, provider); + } + catch + { + // don't cause the failure; + } + + if (wellKnownProvider.Equals(MSWebDeploymentAssembly.DynamicAssembly.GetEnumValue(MSDeploy.TypeName.DeploymentWellKnownProvider, MSDeploy.Provider.MetaKey)) + || wellKnownProvider.Equals(MSWebDeploymentAssembly.DynamicAssembly.GetEnumValue(MSDeploy.TypeName.DeploymentWellKnownProvider, MSDeploy.Provider.AppHostConfig))) + { + srErrorMessage = SR.VSMSDEPLOY_MsDeployExceptionFwlink1SiteMessage; + } + } + strBuilder.Append(string.Format(System.Globalization.CultureInfo.CurrentCulture,srErrorMessage, provider)); + } + else if (index == 1) + { + strBuilder.Append(SR.VSMSDEPLOY_MsDeployExceptionFwlink2Message); + } + else if (index == 2) + { + strBuilder.Append(SR.VSMSDEPLOY_MsDeployExceptionFwlink3Message); + } + else if (index == 3) + { + strBuilder.Append(SR.VSMSDEPLOY_MsDeployExceptionFwlink4Message); + } + else + { + System.Diagnostics.Debug.Assert(false, "fwdlink5 and above is not implemented"); + } + } + } + } + + if (e.InnerException == null) + { + strBuilder.Append(e.Message); + Log.LogError(string.Format(System.Globalization.CultureInfo.CurrentCulture,SR.VSMSDEPLOY_FailedWithException, strBuilder.ToString())); + } + else + { + // Dev10 bug we sometime need better error message to show user what do do + System.Exception currentException = e; + while (currentException != null) + { + strBuilder.Append(System.Environment.NewLine); + strBuilder.Append(currentException.Message); + currentException = currentException.InnerException; + } + Log.LogError(string.Format(System.Globalization.CultureInfo.CurrentCulture,SR.VSMSDEPLOY_FailedWithExceptionWithDetail, e.Message, strBuilder.ToString())); + } + strBuilder.Append(System.Environment.NewLine); + strBuilder.Append(e.StackTrace); + Log.LogMessage(Framework.MessageImportance.Low, strBuilder.ToString()); + } + + public static Generic.IList SortTaskItemsByPriority(Framework.ITaskItem[] taskItems) + { + int count = taskItems != null? taskItems.GetLength(0): 0; + Generic.SortedList, Framework.ITaskItem> sortedList = + new System.Collections.Generic.SortedList, Microsoft.Build.Framework.ITaskItem>(count, ParameterTaskComparer); + + for (int i = 0; i < count; i++) + { + Framework.ITaskItem iTaskItem = taskItems[i]; + string priority = iTaskItem.GetMetadata("Priority"); + int iPriority = string.IsNullOrEmpty(priority)? 0: System.Convert.ToInt32(priority, System.Globalization.CultureInfo.InvariantCulture); + sortedList.Add(new System.Collections.Generic.KeyValuePair(iPriority, i), iTaskItem); + } + return sortedList.Values; + } + internal class PriorityIndexComparer : Generic.IComparer> + { + #region IComparer> Members + public int Compare(System.Collections.Generic.KeyValuePair x, System.Collections.Generic.KeyValuePair y) + { + if (x.Key == y.Key) + { + return x.Value - y.Value; + } + else + { + return x.Key - y.Key; + } + } + #endregion + } + + static public string StripOffTrailingSlashes(string str) + { + if (!string.IsNullOrEmpty(str)) + { + while (str.EndsWith("\\", System.StringComparison.Ordinal) || str.EndsWith("/", System.StringComparison.Ordinal)) + str = str.Substring(0, str.Length - 1); + + } + return str; + } + + static public string EnsureTrailingSlashes(string rootPath, char slash) + { + string directorySeparator = new string(slash, 1); + string rootPathWithSlash = string.Concat(rootPath, rootPath.EndsWith(directorySeparator, System.StringComparison.Ordinal) ? string.Empty : directorySeparator); + return rootPathWithSlash; + } + + + static public string GetFilePathResolution(string source, string sourceRootPath) + { + if (IO.Path.IsPathRooted(source) || string.IsNullOrEmpty(sourceRootPath)) + return source; + else + return IO.Path.Combine(sourceRootPath, source); + } + + + /// + /// Utility to generate the Ipv6 string address to match with the ServerBinding string + /// Ipv6 need the have + /// + /// + /// + static internal string GetIPAddressString(System.Net.IPAddress iPAddress) + { + if (iPAddress != null) + { + if (iPAddress.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) + return iPAddress.ToString(); + else if (iPAddress.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6) + { + Text.StringBuilder stringBuilder = MsDeploy.Utility.CleanStringBuilder; + stringBuilder.Append("["); + stringBuilder.Append(iPAddress.ToString()); + stringBuilder.Append("]"); + return stringBuilder.ToString(); + } + } + return null; + } + + /// + /// Utility function to match the IPAddress with the string in IIS ServerBinding's IPAddress + /// + /// + /// + /// + static internal bool MatchOneOfIPAddress(string IISBindingIPString, System.Net.IPAddress[] iPAddresses) + { + if (!string.IsNullOrEmpty(IISBindingIPString)) + { + if (IISBindingIPString.Trim() == "*") + return true; + + foreach (System.Net.IPAddress iPAddress in iPAddresses) + { + if (string.Compare(GetIPAddressString(iPAddress), IISBindingIPString, System.StringComparison.OrdinalIgnoreCase) == 0) + return true; + } + } + return false; + } + + static internal void SetupMSWebDeployDynamicAssemblies(string strVersionsToTry, Utilities.Task task) + { + // Mark the assembly version. + // System.Version version1_1 = new System.Version("7.1"); + Generic.Dictionary versionsList = new System.Collections.Generic.Dictionary(); + if (!string.IsNullOrEmpty(strVersionsToTry)) + { + foreach (string str in strVersionsToTry.Split(';')) + { + versionsList[str] = str; + } + } + + const string MSDeploymentDllFallback = "9.0"; + versionsList[MSDeploymentDllFallback] = MSDeploymentDllFallback; + + System.Version[] versionArray = versionsList.Values.Select(p => new System.Version(p)).ToArray(); + System.Array.Sort(versionArray); + + for (int i = versionArray.GetLength(0) - 1; i >= 0 ; i--) + { + System.Version version = versionArray[i]; + try + { + MSWebDeploymentAssembly.SetVersion(version); + + System.Version webDelegationAssemblyVersion = version; + if (MSWebDeploymentAssembly.DynamicAssembly != null && MSWebDeploymentAssembly.DynamicAssembly.Assembly != null) + { + foreach (System.Reflection.AssemblyName assemblyName in MSWebDeploymentAssembly.DynamicAssembly.Assembly.GetReferencedAssemblies()) + { + if (string.Compare(assemblyName.Name, 0 , MSWebDelegationAssembly.AssemblyName, 0, MSWebDelegationAssembly.AssemblyName.Length, System.StringComparison.OrdinalIgnoreCase) == 0) + { + webDelegationAssemblyVersion = assemblyName.Version; + break; + } + } + } + + MSWebDelegationAssembly.SetVersion(webDelegationAssemblyVersion); + task.Log.LogMessage(Microsoft.Build.Framework.MessageImportance.Low, string.Format(System.Globalization.CultureInfo.CurrentCulture,SR.VSMSDEPLOY_MSDEPLOYVERSIONLOAD, task.ToString(), MSWebDeploymentAssembly.DynamicAssembly.AssemblyFullName)); + task.Log.LogMessage(Microsoft.Build.Framework.MessageImportance.Low, string.Format(System.Globalization.CultureInfo.CurrentCulture,SR.VSMSDEPLOY_MSDEPLOYVERSIONLOAD, task.ToString(), MSWebDelegationAssembly.DynamicAssembly.AssemblyFullName)); + return; + } + catch (System.Exception e) + { + task.Log.LogMessage(Microsoft.Build.Framework.MessageImportance.Low, string.Format(System.Globalization.CultureInfo.CurrentCulture,SR.BUILDTASK_FailedToLoadThisVersionMsDeployTryingTheNext, versionArray[i], e.Message)); + } + } + // if it not return by now, it is definite a error + throw new System.InvalidOperationException(string.Format(System.Globalization.CultureInfo.CurrentCulture, SR.VSMSDEPLOY_MSDEPLOYASSEMBLYLOAD_FAIL, task.ToString())); + } + + + static public string EscapeTextForMSBuildVariable(string text) + { + if (!string.IsNullOrEmpty(text) && text.IndexOfAny(@"$%".ToArray()) >= 0 ) + { + System.Text.StringBuilder stringBuilder = new Text.StringBuilder(text.Length *2); + char[] chars = text.ToCharArray(); + int i = 0; + for (i = 0; i < chars.Count() -2; i++) + { + char ch = chars[i]; + char nextch1 = chars[i + 1]; + char nextch2 = chars[i + 2]; + bool fAlreadyHandled = false; + switch(ch) + { + case '$': + if (nextch1 == '(') + { + stringBuilder.Append("%24"); + fAlreadyHandled = true; + } + break; + case '%': + if (nextch1 == '(' || ("0123456789ABCDEFabcdef".IndexOf(nextch1) >= 0 && "0123456789ABCDEFabcdef".IndexOf(nextch2) >= 0)) + { + stringBuilder.Append("%25"); + fAlreadyHandled = true; + } + break; + } + if (!fAlreadyHandled) + { + stringBuilder.Append(ch); + } + } + for (; i < chars.Count(); i++) + stringBuilder.Append(chars[i]); + return stringBuilder.ToString(); + } + return text; + } + /// + /// Given a user agant string, it appends :WTE to it if + /// the string is not null. + /// + public static string GetFullUserAgentString(string userAgent) + { + if(string.IsNullOrEmpty(userAgent)) + return null; + try + { + object[] o = typeof(Utility).Assembly.GetCustomAttributes(typeof(AssemblyFileVersionAttribute), false); + if (o.Length > 0 && o[0] is AssemblyFileVersionAttribute) + { + return string.Concat(userAgent, ":WTE", ((AssemblyFileVersionAttribute)o[0]).Version); + } + } + catch + { + Debug.Assert(false, "Error getting WTE version"); + } + return userAgent; + } + + } + + + internal static class ItemFilter + { + public delegate bool ItemMetadataFilter(Framework.ITaskItem iTaskItem); + + public static bool ItemFilterPipelineMetadata(Framework.ITaskItem item, string metadataName, string metadataValue, bool fIgnoreCase) + { + return (string.Compare(item.GetMetadata(metadataName), metadataValue, fIgnoreCase, CultureInfo.InvariantCulture) == 0); + } + + public static bool ItemFilterExcludeTrue(Framework.ITaskItem iTaskItem) + { + string metadataName = PipelineMetadata.Exclude.ToString(); + return ItemFilterPipelineMetadata(iTaskItem, metadataName, "true", true); + } + + } + + +} diff --git a/src/Microsoft.DotNet.Publishing.Tasks/MsDeploy/DynamicAssembly.cs b/src/Microsoft.DotNet.Publishing.Tasks/MsDeploy/DynamicAssembly.cs new file mode 100644 index 000000000000..d0f0e568be9f --- /dev/null +++ b/src/Microsoft.DotNet.Publishing.Tasks/MsDeploy/DynamicAssembly.cs @@ -0,0 +1,147 @@ +using System.Diagnostics; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; + +namespace Microsoft.DotNet.Publishing.Tasks.MsDeploy +{ + internal class DynamicAssembly + { + public DynamicAssembly(string assemblyName, System.Version verToLoad, string publicKeyToken) + { + AssemblyFullName = string.Format(System.Globalization.CultureInfo.CurrentCulture, "{0}, Version={1}.{2}.0.0, Culture=neutral, PublicKeyToken={3}", assemblyName, verToLoad.Major, verToLoad.Minor, publicKeyToken); + Assembly = Assembly.Load(AssemblyFullName); + Version = verToLoad; + } + + public DynamicAssembly() { } + + public string AssemblyFullName { get; set; } + public System.Version Version { get; set; } + public Assembly Assembly { get; set; } + + public System.Type GetType(string typeName) + { + System.Type type = Assembly.GetType(typeName); + Debug.Assert(type != null); + return type; + } + + public virtual System.Type TryGetType(string typeName) + { + System.Type type = Assembly.GetType(typeName); + return type; + } + + public object GetEnumValue(string enumName, string enumValue) + { + System.Type enumType = Assembly.GetType(enumName); + FieldInfo enumItem = enumType.GetField(enumValue); + object ret = enumItem.GetValue(enumType); + Debug.Assert(ret != null); + return ret; + } + + public object GetEnumValueIgnoreCase(string enumName, string enumValue) + { + System.Type enumType = Assembly.GetType(enumName); + FieldInfo enumItem = enumType.GetField(enumValue, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance); + object ret = enumItem.GetValue(enumType); + Debug.Assert(ret != null); + return ret; + } + + public bool TryGetEnumValue(string enumTypeName, string enumStrValue, out object retValue) + { + bool fGetValue = false; + retValue = System.Enum.ToObject(GetType(enumTypeName), 0); + try + { + retValue = GetEnumValueIgnoreCase(enumTypeName, enumStrValue); + fGetValue = true; + } + catch + { + } + return fGetValue; + } + + + public object CreateObject(string typeName) + { + return CreateObject(typeName, null); + } + + public object CreateObject(string typeName, object[] arguments) + { + object createdObject = null; + System.Type[] argumentTypes = null; + if (arguments == null || arguments.GetLength(0) == 0) + { + argumentTypes = System.Type.EmptyTypes; + } + else + { + argumentTypes = arguments.Select(p => p.GetType()).ToArray(); + } + System.Type typeToConstruct = Assembly.GetType(typeName); + System.Reflection.ConstructorInfo constructorInfoObj = typeToConstruct.GetConstructor(argumentTypes); + + if (constructorInfoObj == null) + { + Debug.Assert(false, "DynamicAssembly.CreateObject Failed to get the constructorInfoObject"); + } + else + { + createdObject = constructorInfoObj.Invoke(arguments); + } + Debug.Assert(createdObject != null); + return createdObject; + } + + public object CallStaticMethod(string typeName, string methodName, object[] arguments) + { + System.Type t = GetType(typeName); + return t.InvokeMember(methodName, BindingFlags.InvokeMethod, null, t, arguments, System.Globalization.CultureInfo.InvariantCulture); + } + + /// + /// Support late bind delegate + /// + /// + /// + public delegate void EventHandlerDynamicDelegate(object sender, dynamic e); + public delegate void EventHandlerEventArgsDelegate(object sender, System.EventArgs e); + internal static System.Delegate CreateEventHandlerDelegate(System.Reflection.EventInfo evt, TDelegate d) + { + var handlerType = evt.EventHandlerType; + var eventParams = handlerType.GetMethod("Invoke").GetParameters(); + + ParameterExpression[] parameters = eventParams.Select(p => Expression.Parameter(p.ParameterType, p.Name)).ToArray(); + MethodCallExpression body = Expression.Call(Expression.Constant(d), d.GetType().GetMethod("Invoke"), parameters); + var lambda = Expression.Lambda(body, parameters); + // Diagnostics.Debug.Assert(false, lambda.ToString()); + return System.Delegate.CreateDelegate(handlerType, lambda.Compile(), "Invoke", false); + } + + static public System.Delegate AddEventDeferHandler(dynamic obj, string eventName, System.Delegate deferEventHandler) + { + EventInfo eventinfo = obj.GetType().GetEvent(eventName); + System.Delegate eventHandler = DynamicAssembly.CreateEventHandlerDelegate(eventinfo, deferEventHandler); + eventinfo.AddEventHandler(obj, eventHandler); + return eventHandler; + } + + static public void AddEventHandler(dynamic obj, string eventName, System.Delegate eventHandler) + { + EventInfo eventinfo = obj.GetType().GetEvent(eventName); + eventinfo.AddEventHandler(obj, eventHandler); + } + + static public void RemoveEventHandler(dynamic obj, string eventName, System.Delegate eventHandler) + { + EventInfo eventinfo = obj.GetType().GetEvent(eventName); + eventinfo.RemoveEventHandler(obj, eventHandler); + } + } +} diff --git a/src/Microsoft.DotNet.Publishing.Tasks/MsDeploy/IVSMSDeployTask.cs b/src/Microsoft.DotNet.Publishing.Tasks/MsDeploy/IVSMSDeployTask.cs new file mode 100644 index 000000000000..7eda43c8b37e --- /dev/null +++ b/src/Microsoft.DotNet.Publishing.Tasks/MsDeploy/IVSMSDeployTask.cs @@ -0,0 +1,26 @@ +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.DotNet.Publishing.Tasks.MsDeploy +{ + internal interface IVsPublishMsBuildTaskHost + { + string TaskName { get; } + + TaskLoggingHelper Log { get; } + + IBuildEngine BuildEngine { get; } + + object GetProperty(string propertyName); + } + + + internal interface IVSMSDeployHost : IVsPublishMsBuildTaskHost + { + void PopulateOptions(/*DeploymentSyncOptions*/dynamic options); + // Update the base config setting, hookup the event. + void UpdateDeploymentBaseOptions(VSMSDeployObject srcVsMsDeployobject, VSMSDeployObject destVsMsDeployobject); + // Unhook the event + void ClearDeploymentBaseOptions(VSMSDeployObject srcVsMsDeployobject, VSMSDeployObject destVsMsDeployobject); + } +} diff --git a/src/Microsoft.DotNet.Publishing.Tasks/MsDeploy/MSDeploy.cs b/src/Microsoft.DotNet.Publishing.Tasks/MsDeploy/MSDeploy.cs new file mode 100644 index 000000000000..43302f113513 --- /dev/null +++ b/src/Microsoft.DotNet.Publishing.Tasks/MsDeploy/MSDeploy.cs @@ -0,0 +1,1063 @@ +namespace Microsoft.DotNet.Publishing.Tasks.MsDeploy +{ + using CultureInfo = System.Globalization.CultureInfo; + using IO = System.IO; + using Framework = Microsoft.Build.Framework; + using Utilities = Microsoft.Build.Utilities; + using System.Linq; + + /// + /// The MSDeploy task, which is a wrapper around msdeploy.exe + /// + public class MSDeploy : Utilities.ToolTask + { + /* + *Microsoft (R) Web Deployment Command Line Tool (MSDeploy.exe) + Version 7.1.495.0 + Copyright (c) Microsoft Corporation. All rights reserved. + + MSDeploy.exe <-verb:> <-source:> [-dest:] [args ...] + + -verb: Action to perform (required). + -source: The source object for the operation + (required). + -dest: The destination object for the operation. + -declareParam: Declares a parameter for synchronization. + -setParam: Sets a parameter for synchronization. + -disableLink: Disables the specified link extension(s). + -enableLink: Enables the specified link extension(s). + -disableRule: Disables the specified synchronization + rule(s). + -enableRule: Enables the specified synchronization rule(s). + -replace: Specifies an attribute replacement rule. + -retryAttempts The number of times a provider will retry + after a failed action (not all providers + support retrying). Defaults to 5. + -retryInterval Interval in milliseconds between retry + attempts (-retryAttempts). The default is + 1000. + -skip: Specifies an object to skip during + synchronization. + -disableSkipDirective: Disables the specified skip directive. + -enableSkipDirective: Enables the specified skip directive. + -useAdminShares When possible, use UNC admin shares for file + synchronization. + -verbose Enables more verbose output. + -whatif Displays what would have happened without + actually performing any operations. + -xpath: An XPath expression to apply to XML output. + -xml Return results in XML format. + -allowUntrusted Allow untrusted server certificate when using + SSL. + -showSecure Show secure attributes in XML output instead + of hiding them.^ + -preSync: A command to execute before the + synchronization on the destination. For + instance, net stop a service. + -postSync: A command to execute after the + synchronization on the destination. For + instance, net start a service. + * + * + * // not documented, part of IISExpress + public const string AppHostConfigDirectory = "-appHostConfigDir"; + * public const string WebServerDirectory = "-webServerDir"; + public const string WebServerManifest = "-webServerManifest"; + + + + Supported Verbs: + + dump Displays the details of the specified source + object. + migrate Migrates the source object to the destination + object. + sync Synchronizes the destination object with the + source object. + delete Deletes specified destination object. + getDependencies Retrieve dependencies for given object + getParameters Return parameters supported by object + getSystemInfo Retrieve system information associated with + given object + + format: + + provider-type=[provider-path],[provider settings],... + + Supported provider-types (and sample paths, if applicable): + + appHostConfig IIS 7+ configuration + appHostSchema IIS 7+ configuration schema + appPoolConfig IIS 7+ Application Pool + archiveDir Archive directory + auto Automatic destination + cert Certificate + comObject32 32-bit COM object + comObject64 64-bit COM object + contentPath File System Content + dbFullSql Deploy SQL database + dbMySql Deploy MySql database + delete Special source-only provider used to delete a + given destination. + fcgiExtConfig FcgiExt.ini settings or fastCgi section + configuration + gacAssembly GAC assembly + iisApp Web Application + machineConfig32 .NET 32-bit machine configuration + machineConfig64 .NET 64-bit machine configuration + manifest Custom manifest file + metaKey Metabase key + package A .zip file package + regKey Registry key + regValue Registry value + rootWebConfig32 .NET 32-bit root Web configuration + rootWebConfig64 .NET 64-bit root Web configuration + runCommand Runs a command on the destination when sync + is called. + setAcl Grant permissions + urlScanConfig UrlScan.ini settings or requestFiltering + section configuration + webServer Full IIS 7+ Web server + webServer60 Full IIS 6.0 Web server + + + Common settings (can be used with all providers): + + computerName= Name of remote computer or proxy-URL + wmsvc= Name of remote computer or proxy-URL for the Web + Management Service (WMSvc). Assumes that the + service is listening on port 8172. + authtype= Authentication scheme to use. NTLM is the + default setting. If the wmsvc option is + specified, then Basic is the default setting. + userName= User name to authenticate for remote connections + (required if using Basic authentication). + password= Password of the user for remote connections + (required if using Basic authentication). + encryptPassword= Password to use for encrypting/decrypting any + secure data. + includeAcls= If true, include ACLs in the operation (applies + to the file system, registry, and metabase). + useStatusRequest= Controls whether remote destination + synchronization status should appear + immediately. The default setting is true. + tempAgent= Temporarily install the remote agent for the + duration of a remote operation + + + */ + + private string m_exePath; + private string m_disableRule; + private string m_verb; + private string m_failureLevel; + private string m_xpath; + private string m_enableRule; + private string m_replace; + private string m_skip; + private string m_disableLink; + private string m_enableLink; + private string m_disableSkipDirective; + private string m_enableSkipDirective; + private string m_lastCommandLine; + private bool m_xml; + private bool m_whatif; + private bool m_useChecksum; + private bool m_verbose; + private bool m_allowUntrusted; + private bool m_enableTransaction; + private int m_retryAttempts; + private int m_retryInterval; + private bool m_useDoubleQuoteForValue = false; + private string m_strValueQuote = null; + + //public const string AppHostConfigDirectory = "-appHostConfigDir"; + // * public const string WebServerDirectory = "-webServerDir"; + // public const string WebServerManifest = "-webServerManifest"; + + public string WebServerAppHostConfigDirectory { get; set;} + public string WebServerDirectory { get; set; } + public string WebServerManifest { get; set;} + + + private Framework.ITaskItem[] m_sourceITaskItem = null; + private Framework.ITaskItem[] m_destITaskItem = null; + private Framework.ITaskItem[] m_replaceRuleItemsITaskItem = null; + private Framework.ITaskItem[] m_skipRuleItemsITaskItem = null; + private Framework.ITaskItem[] m_declareParameterItems = null; + private Framework.ITaskItem[] m_importDeclareParametersItems = null; + private Framework.ITaskItem[] m_simpleSetParamterItems = null; + private Framework.ITaskItem[] m_importSetParametersItems = null; + private Framework.ITaskItem[] m_setParamterItems = null; + + + private bool m_previewOnly = false; + + public class Provider + { + public static readonly string Unknown = "Unknown"; + public static readonly string Package = "Package"; + public static readonly string ArchiveDir = "ArchiveDir"; + public static readonly string DbDacFx = "DbDacFx"; + public static readonly string MetaKey = "MetaKey"; + public static readonly string AppHostConfig = "AppHostConfig"; + public static readonly string DBFullSql = "DBFullSql"; + public static readonly string DBCodeFirst = "DBCodeFirst"; + } + + public class TypeName + { + public static readonly string DeploymentWellKnownProvider = "Microsoft.Web.Deployment.DeploymentWellKnownProvider"; + public static readonly string DeploymentEncryptionException = "Microsoft.Web.Deployment.DeploymentEncryptionException"; + public static readonly string DeploymentException = "Microsoft.Web.Deployment.DeploymentException"; + } + + public class Extensions + { + public static readonly string DbFullSql = ".sql"; + public static readonly string DbDacFx = ".dacpac"; + } + + + /// + /// Location for the MSdeploy.exe path + /// + public string ExePath + { + get { + if (string.IsNullOrEmpty(m_exePath)) + { + // if path is not set, we optimize to latest version of msdeploy + using (Win32.RegistryKey registryKey = Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\IIS Extensions\MSDeploy")) + { + if (registryKey != null) + { + string[] strVersions = registryKey.GetSubKeyNames(); + if (strVersions != null) + { + int [] versions = registryKey.GetSubKeyNames().Select(p => System.Convert.ToInt32(p)).ToArray(); + System.Array.Sort(versions); + + for (int i = versions.Length -1; i >= 0; i--) + { + int version = versions[i]; + using (Win32.RegistryKey versionRegistry = registryKey.OpenSubKey(version.ToString(CultureInfo.InvariantCulture))) + { + if (versionRegistry != null) + { + m_exePath = versionRegistry.GetValue(@"InstallPath").ToString(); + if (!string.IsNullOrEmpty(m_exePath)) + { + // found the valid m_exePath + break; + } + } + } + } + } + } + } + } + return m_exePath; + } + set { m_exePath = value; } + } + + public string DisableRule + { + get { return this.m_disableRule; } + set { this.m_disableRule = value; } + } + + [Framework.Required] + public string Verb + { + get { return this.m_verb; } + set { this.m_verb = value; } + } + + [Framework.Required] + public Framework.ITaskItem[] Source + { + get { return this.m_sourceITaskItem; } + set { this.m_sourceITaskItem = value; } + } + + public Framework.ITaskItem[] Destination + { + get { return this.m_destITaskItem; } + set { this.m_destITaskItem = value; } + } + public bool WhatIf + { + get { return this.m_whatif; } + set { this.m_whatif = value; } + } + + public bool OptimisticParameterDefaultValue { get; set;} + + public bool UseChecksum + { + get { return m_useChecksum; } + set { m_useChecksum = value; } + } + + public bool AllowUntrusted + { + get { return this.m_allowUntrusted; } + set { this.m_allowUntrusted = value; } + } + + public bool Verbose + { + get { return this.m_verbose; } + set { this.m_verbose = value; } + } + public string FailureLevel + { + get { return this.m_failureLevel; } + set { this.m_failureLevel = value; } + } + public bool Xml + { + get { return this.m_xml; } + set { this.m_xml = value; } + } + public string XPath + { + get { return this.m_xpath; } + set { this.m_xpath = value; } + } + public string EnableRule + { + get { return this.m_enableRule; } + set { this.m_enableRule = value; } + } + public string Replace + { + get { return this.m_replace; } + set { this.m_replace = value; } + } + public string Skip + { + get { return this.m_skip; } + set { this.m_skip = value; } + } + public string DisableLink + { + get { return this.m_disableLink; } + set { this.m_disableLink = value; } + } + + public string EnableLink + { + get { return this.m_enableLink; } + set { this.m_enableLink = value; } + } + + public bool EnableTransaction + { + get { return this.m_enableTransaction; } + set { this.m_enableTransaction = value; } + } + public int RetryAttempts + { + get { return this.m_retryAttempts; } + set { this.m_retryAttempts = value; } + } + public int RetryInterval + { + get { return this.m_retryInterval; } + set { this.m_retryInterval = value; } + } + + public bool UseDoubleQuoteForValue + { + get { return this.m_useDoubleQuoteForValue; } + set { + this.m_useDoubleQuoteForValue = value; + m_strValueQuote = (m_useDoubleQuoteForValue)? "\"": null; + } + } + + public Framework.ITaskItem[] ReplaceRuleItems + { + get { return m_replaceRuleItemsITaskItem; } + set { this.m_replaceRuleItemsITaskItem = value; } + } + + public Framework.ITaskItem[] SkipRuleItems + { + get { return m_skipRuleItemsITaskItem; } + set { this.m_skipRuleItemsITaskItem = value; } + } + + public string DisableSkipDirective + { + get { return m_disableSkipDirective; } + set { this.m_disableSkipDirective = value; } + } + + public string EnableSkipDirective + { + get { return m_enableSkipDirective; } + set { this.m_enableSkipDirective = value; } + } + + public Framework.ITaskItem[] DeclareParameterItems + { + get { return m_declareParameterItems; } + set { this.m_declareParameterItems = value; } + } + public Framework.ITaskItem[] ImportDeclareParametersItems + { + get { return m_importDeclareParametersItems; } + set { this.m_importDeclareParametersItems = value; } + } + + + public Framework.ITaskItem[] ImportSetParametersItems + { + get { return m_importSetParametersItems; } + set { this.m_importSetParametersItems = value; } + } + + public Framework.ITaskItem[] SimpleSetParameterItems + { + get { return m_simpleSetParamterItems; } + set { this.m_simpleSetParamterItems = value; } + } + + + public Framework.ITaskItem[] SetParameterItems + { + get { return m_setParamterItems; } + set { this.m_setParamterItems = value; } + } + + public Framework.ITaskItem[] AdditionalDestinationProviderOptions {get;set;} + + string _userAgent; + public string UserAgent { + get{return _userAgent;} + set { + if(!string.IsNullOrEmpty(value)) + { + _userAgent = MsDeploy.Utility.GetFullUserAgentString(value); + } + } + } + + [Framework.Output] + public string CommandLine + { + get + { + return string.Concat("\"", GenerateFullPathToTool(), "\" ", GenerateCommandLineCommands()); + } + } + + [Framework.Output] + public string CommandLineArguments + { + get + { + return GenerateCommandLineCommands(); + } + } + + [Framework.Output] + public string MSDeployToolPath + { + get + { + return string.Concat("\"", GenerateFullPathToTool(), "\" "); + } + } + + /// + /// This enable us not to execute the msdeploy, but just have the output of PreviewMSDeploy + /// + public bool PreviewCommandLineOnly + { + get + { + return this.m_previewOnly; + } + set + { + this.m_previewOnly = value; + } + } + + // controling whether a task should be execute. + protected override bool SkipTaskExecution() + { + if (this.PreviewCommandLineOnly) + return true; + else + return base.SkipTaskExecution(); + } + + + protected override Framework.MessageImportance StandardOutputLoggingImportance + { + get + { + return Microsoft.Build.Framework.MessageImportance.High; + } + } + + /// + /// Override the Execute method to be able to send ExternalProjectStarted/Finished events. + /// + /// + public override bool Execute() + { + bool bSuccess = false; + if (this.PreviewCommandLineOnly) + { + // useful information about what was wrong with the parameters. + if (!ValidateParameters()) + { + return false; + } + Log.LogMessage(Framework.MessageImportance.Low, SR.MSDEPLOY_EXE_PreviewOnly); + return true; + } + + try + { + Log.LogMessage( Framework.MessageImportance.Normal, SR.MSDEPLOY_EXE_Start); + bSuccess = base.Execute(); + if (bSuccess) + Log.LogMessage(Framework.MessageImportance.Normal, SR.MSDEPLOY_EXE_Succeeded); + } + catch (System.Exception ex) + { + // Log Failure + Log.LogMessage(Framework.MessageImportance.High, SR.MSDEPLOY_EXE_Failed); + Log.LogErrorFromException(ex); + bSuccess = false; + } + if (bSuccess) + { + string type = string.Empty; + string path = string.Empty; + Framework.ITaskItem taskItem = null; + if (this.Destination != null && this.Destination.GetLength(0) == 1) + { + taskItem = this.Destination[0]; + } + else + { + if (this.Source != null) + taskItem = this.Source[0]; + } + if (taskItem != null) + { + type = taskItem.ItemSpec; + path = taskItem.GetMetadata("Path"); + } + MsDeploy.Utility.MsDeployExeEndOfExecuteMessage(bSuccess, type, path, Log); + } + return bSuccess; + } + + + + // utility function to add the replace rule for the option + static public void AddReplaceRulesToOptions(Utilities.CommandLineBuilder commandLineBuilder, Framework.ITaskItem[] replaceRuleItems, string valueQuoteChar) + { + if (commandLineBuilder != null + && replaceRuleItems != null)// Dev10 bug 496639 foreach will throw the exception if the replaceRuleItem is null + { + System.Collections.Generic.List arguments = new System.Collections.Generic.List(6); + + foreach (Framework.ITaskItem item in replaceRuleItems) + { + arguments.Clear(); + MsDeploy.Utility.BuildArgumentsBaseOnEnumTypeName(item, arguments, typeof(MsDeploy.ReplaceRuleMetadata), valueQuoteChar); + commandLineBuilder.AppendSwitchUnquotedIfNotNull("-replace:", arguments.Count == 0 ? null : string.Join(",", arguments.ToArray())); + } + } + } + + static public void AddSkipDirectiveToBaseOptions(Utilities.CommandLineBuilder commandLineBuilder, Framework.ITaskItem[] skipRuleItems, string valueQuoteChar) + { + if (commandLineBuilder != null + && skipRuleItems != null)// Dev10 bug 496639 foreach will throw the exception if the replaceRuleItem is null + { + System.Collections.Generic.List arguments = new System.Collections.Generic.List(6); + + foreach (Framework.ITaskItem item in skipRuleItems) + { + arguments.Clear(); + MsDeploy.Utility.BuildArgumentsBaseOnEnumTypeName(item, arguments, typeof(MsDeploy.SkipRuleMetadata), valueQuoteChar); + commandLineBuilder.AppendSwitchUnquotedIfNotNull("-skip:", arguments.Count == 0? null : string.Join(",", arguments.ToArray())); + } + } + } + + + static public void AddDeclareParameterToCommandArgument(System.Collections.Generic.List arguments, + Framework.ITaskItem item, + string valueQuote, + System.Collections.Generic.Dictionary lookupDictionary) + { + if (arguments != null && item != null && lookupDictionary != null) + { + // special for the name + arguments.Clear(); + System.Collections.Generic.List idenities = new System.Collections.Generic.List(6); + + string name = item.ItemSpec; + if (!string.IsNullOrEmpty(name)) + { + string element = item.GetMetadata(MsDeploy.ExistingParameterValiationMetadata.Element.ToString()); + if (string.IsNullOrEmpty(element)) + element = "parameterEntry"; + if (string.Compare(element, "parameterEntry", System.StringComparison.OrdinalIgnoreCase) == 0) + { + idenities.Add(name); + foreach (string dPIdentity in System.Enum.GetNames(typeof(MsDeploy.ExistingDeclareParameterMetadata))) + { + idenities.Add(item.GetMetadata(dPIdentity)); + } + + string identity = string.Join(",", idenities.ToArray()); + if (!lookupDictionary.ContainsKey(identity)) + { + string nameValue = MsDeploy.Utility.PutValueInQuote(name, valueQuote); + arguments.Add(string.Concat("name=", nameValue)); + System.Type enumType = lookupDictionary.ContainsValue(name) ? typeof(MsDeploy.ExistingDeclareParameterMetadata) : typeof(MsDeploy.DeclareParameterMetadata); + // the rest, build on the Enum Name + MsDeploy.Utility.BuildArgumentsBaseOnEnumTypeName(item, arguments, enumType, valueQuote); + lookupDictionary.Add(identity, name); + } + } + else if (string.Compare(element, "parameterValidation", System.StringComparison.OrdinalIgnoreCase) == 0) + { + System.Diagnostics.Debug.Assert(false, "msdeploy.exe doesn't support parameterValidation entry in the command line for declareParameter yet."); + } + } + } + } + + + /// + /// Utility to build DeclareParameterOptions + /// + /// + /// + static public void AddDeclareParametersOptions(Utilities.CommandLineBuilder commandLineBuilder, Framework.ITaskItem[] originalItems, string valueQuote, bool foptimisticParameterDefaultValue) + { + System.Collections.Generic.IList items = MsDeploy.Utility.SortParametersTaskItems(originalItems, foptimisticParameterDefaultValue, MsDeploy.DeclareParameterMetadata.DefaultValue.ToString()); + if (commandLineBuilder != null && items != null) + { + System.Collections.Generic.List arguments = new System.Collections.Generic.List(6); + System.Collections.Generic.Dictionary lookupDictionary = new System.Collections.Generic.Dictionary(items.Count); + + foreach (Framework.ITaskItem item in items) + { + AddDeclareParameterToCommandArgument(arguments, item, valueQuote, lookupDictionary); + commandLineBuilder.AppendSwitchUnquotedIfNotNull("-declareParam:", arguments.Count == 0 ? null : string.Join(",", arguments.ToArray())); + } + } + } + + + static public void AddImportDeclareParametersFilesOptions(Utilities.CommandLineBuilder commandLineBuilder, Framework.ITaskItem[] items) + { + AddImportParametersFilesOptions(commandLineBuilder, "-declareParamFile:", items); + } + + static public void AddImportSetParametersFilesOptions(Utilities.CommandLineBuilder commandLineBuilder, Framework.ITaskItem[] items) + { + AddImportParametersFilesOptions(commandLineBuilder, "-setParamFile:", items); + } + + static internal void AddImportParametersFilesOptions(Utilities.CommandLineBuilder commandLineBuilder, string parameterFlag, Framework.ITaskItem[] items) + { + if (commandLineBuilder != null + && !string.IsNullOrEmpty(parameterFlag) + && items != null)// Dev10 bug 496639 foreach will throw the exception if the replaceRuleItem is null + { + + foreach (Framework.ITaskItem item in items) + { + string fileName = item.ItemSpec; + if (!string.IsNullOrEmpty(fileName)) + { + commandLineBuilder.AppendSwitch(string.Concat(parameterFlag, "\"", fileName, "\"")); + } + } + } + } + + + /// + /// Utility function to set SimpleeSyncParameter Name/Value + /// + /// + /// + static public void AddSimpleSetParametersToObject(Utilities.CommandLineBuilder commandLineBuilder, Framework.ITaskItem[] originalItems, string valueQuoteChar, bool foptimisticParameterDefaultValue) + { + System.Collections.Generic.IList items = MsDeploy.Utility.SortParametersTaskItems(originalItems, foptimisticParameterDefaultValue, MsDeploy.SimpleSyncParameterMetadata.Value.ToString()); + if (commandLineBuilder != null && items != null) + { + System.Collections.Generic.List arguments = new System.Collections.Generic.List(6); + foreach (Framework.ITaskItem item in items) + { + arguments.Clear(); + + // special for name + string name = item.ItemSpec; + if (!string.IsNullOrEmpty(name)) + { + string valueData = MsDeploy.Utility.PutValueInQuote(name, valueQuoteChar); + arguments.Add(string.Concat("name=", valueData)); + } + // the rest, build on the enum name + + MsDeploy.Utility.BuildArgumentsBaseOnEnumTypeName(item, arguments, typeof(MsDeploy.SimpleSyncParameterMetadata), valueQuoteChar); + commandLineBuilder.AppendSwitchUnquotedIfNotNull("-setParam:", arguments.Count == 0 ? null : string.Join(",", arguments.ToArray())); + } + } + } + + /// + /// Utility function to setParameters in type, scope, match, value of SyncParameter + /// + /// + /// + static public void AddSetParametersToObject(Utilities.CommandLineBuilder commandLineBuilder, Framework.ITaskItem[] originalItems, string valueQuote, bool foptimisticParameterDefaultValue) + { + System.Collections.Generic.IList items = MsDeploy.Utility.SortParametersTaskItems(originalItems, foptimisticParameterDefaultValue, MsDeploy.ExistingSyncParameterMetadata.Value.ToString()); + if (commandLineBuilder != null && items != null) + { + System.Collections.Generic.List arguments = new System.Collections.Generic.List(6); + System.Collections.Generic.Dictionary lookupDictionary = new System.Collections.Generic.Dictionary(items.Count); + System.Collections.Generic.Dictionary nameValueDictionary = new System.Collections.Generic.Dictionary(items.Count,System.StringComparer.OrdinalIgnoreCase); + + foreach (Framework.ITaskItem item in items) + { + arguments.Clear(); + + System.Collections.Generic.List idenities = new System.Collections.Generic.List(6); + + string name = item.ItemSpec; + if (!string.IsNullOrEmpty(name)) + { + string element = item.GetMetadata(MsDeploy.ExistingParameterValiationMetadata.Element.ToString()); + if (string.IsNullOrEmpty(element)) + element = "parameterEntry"; + + if (string.Compare(element, "parameterEntry", System.StringComparison.OrdinalIgnoreCase) == 0) + { + + idenities.Add(name); + foreach (string dPIdentity in System.Enum.GetNames(typeof(MsDeploy.ExistingDeclareParameterMetadata))) + { + idenities.Add(item.GetMetadata(dPIdentity)); + } + + string identity = string.Join(",", idenities.ToArray()); + if (!lookupDictionary.ContainsKey(identity)) + { + string data = null; + bool fExistingName = nameValueDictionary.ContainsKey(name); + + if (nameValueDictionary.ContainsKey(name)) + { + data = nameValueDictionary[name]; + } + else + { + data = item.GetMetadata(MsDeploy.ExistingSyncParameterMetadata.Value.ToString()); + nameValueDictionary.Add(name, data); + } + + // the rest, build on the Enum Name + MsDeploy.Utility.BuildArgumentsBaseOnEnumTypeName(item, arguments, typeof(MsDeploy.ExistingDeclareParameterMetadata), valueQuote); + if (arguments.Count > 0 && !string.IsNullOrEmpty(data)) + { + arguments.Add(string.Concat(MsDeploy.ExistingSyncParameterMetadata.Value.ToString().ToLower(System.Globalization.CultureInfo.InvariantCulture), + "=", MsDeploy.Utility.PutValueInQuote(data, valueQuote))); + } + lookupDictionary.Add(identity, name); + + } + } + else if (string.Compare(element, "parameterValidation", System.StringComparison.OrdinalIgnoreCase) == 0) + { + System.Diagnostics.Debug.Assert(false, "msdeploy.exe doesn't support parameterValidation entry in the command line for declareParameter yet."); + } + } + commandLineBuilder.AppendSwitchUnquotedIfNotNull("-setParam:", arguments.Count == 0 ? null : string.Join(",", arguments.ToArray())); + } + } + } + + + + static public void AddDestinationProviderSettingToObject(Utilities.CommandLineBuilder commandLineBuilder, string dest, Framework.ITaskItem[] items, string valueQuoteChar, + Framework.ITaskItem[] additionalProviderItems, MSDeploy msdeploy) + { + //commandLineBuilder.AppendSwitchUnquotedIfNotNull("-source:", m_source); + //commandLineBuilder.AppendSwitchUnquotedIfNotNull("-dest:", m_dest); + System.Collections.Generic.List arguments = new System.Collections.Generic.List(6); + + if (items != null && items.GetLength(0) == 1) + { + Framework.ITaskItem taskItem = items[0]; + string provider = taskItem.ItemSpec; + string path = taskItem.GetMetadata("Path"); + string valueData = MsDeploy.Utility.PutValueInQuote(path, valueQuoteChar); + string setData = (string.IsNullOrEmpty(path)) ? provider : string.Concat(provider, "=", valueData); + arguments.Add(setData); + + //Commonly supported provider settings: + // computerName= Name of remote computer or proxy-URL + // wmsvc= Name of remote computer or proxy-URL for the Web + // Management Service (wmsvc) + // userName= User name to authenticate + // password= Password of user name + // encryptPassword= Password to use for encryption related operations + // includeAcls= If true, include ACLs in the operation for the + // specified path + + foreach (string name in taskItem.MetadataNames) + { + if (!MsDeploy.Utility.IsInternalMsdeployWellKnownItemMetadata(name)) + { + string value = taskItem.GetMetadata(name); + if (!string.IsNullOrEmpty(value)) + { + valueData = MsDeploy.Utility.PutValueInQuote(value, valueQuoteChar); + setData = string.Concat(name, "=", valueData); + arguments.Add(setData); + } + } + else + { + MsDeploy.Utility.IISExpressMetadata expressMetadata; + if (System.Enum.TryParse(name, out expressMetadata)) + { + string value = taskItem.GetMetadata(name); + if (!string.IsNullOrEmpty(value)) + { + switch (expressMetadata) + { + case Microsoft.DotNet.Publishing.Tasks.MsDeploy.Utility.IISExpressMetadata.WebServerAppHostConfigDirectory: + msdeploy.WebServerAppHostConfigDirectory = value; + break; + case Microsoft.DotNet.Publishing.Tasks.MsDeploy.Utility.IISExpressMetadata.WebServerDirectory: + msdeploy.WebServerDirectory = value; + break; + case Microsoft.DotNet.Publishing.Tasks.MsDeploy.Utility.IISExpressMetadata.WebServerManifest: + msdeploy.WebServerManifest = value; + break; + } + } + } + + } + } + } + // If additional parameters are specified, we add these too. the itemSpec will be something like iisApp, contentPath, etc and + // each item should have a name\value pair defined as metadata. Each provider will be written as itemSpec.Name=Value + if (additionalProviderItems != null) + { + foreach (Framework.ITaskItem item in additionalProviderItems) + { + if(!string.IsNullOrEmpty(item.ItemSpec)) + { + string settingName = item.GetMetadata("Name"); + string settingValue = item.GetMetadata("Value"); + if(!string.IsNullOrEmpty(settingName) && !string.IsNullOrEmpty(settingValue)) + { + string providerString = string.Concat(item.ItemSpec, ".", settingName, "=", settingValue); + arguments.Add(providerString); + } + } + } + } + + commandLineBuilder.AppendSwitchUnquotedIfNotNull(dest, arguments.Count == 0 ? null : string.Join(",", arguments.ToArray())); + return; + } + + + /// + /// Utility function to help to generate Switch per item + /// + /// + /// + /// + private static void GenerateSwitchPerItem(Utilities.CommandLineBuilder commandLine, string strSwitch, string args) + { + + if (!string.IsNullOrEmpty(args)) + { + foreach (string dl in args.Split(new char[] { ';' })) + { + if (!string.IsNullOrEmpty(dl)) + { + commandLine.AppendSwitchUnquotedIfNotNull(strSwitch, dl); + } + } + } + + + } + + /// + /// Generates command line arguments for msdeploy.exe + /// + protected override string GenerateCommandLineCommands() + { + Utilities.CommandLineBuilder commandLine = new Utilities.CommandLineBuilder(); + AddDestinationProviderSettingToObject(commandLine, "-source:", this.Source, m_strValueQuote, null, this ); + AddDestinationProviderSettingToObject(commandLine, "-dest:", this.Destination, m_strValueQuote, AdditionalDestinationProviderOptions, this); + + commandLine.AppendSwitchUnquotedIfNotNull("-verb:", m_verb); + commandLine.AppendSwitchUnquotedIfNotNull("-failureLevel:", m_failureLevel); + commandLine.AppendSwitchUnquotedIfNotNull("-xpath:", m_xpath); + commandLine.AppendSwitchUnquotedIfNotNull("-replace:", m_replace); + commandLine.AppendSwitchUnquotedIfNotNull("-skip:", m_skip); + + GenerateSwitchPerItem(commandLine, "-enableRule:", m_enableRule); + GenerateSwitchPerItem(commandLine, "-disableRule:", m_disableRule); + GenerateSwitchPerItem(commandLine, "-enableLink:", m_enableLink); + GenerateSwitchPerItem(commandLine, "-disableLink:", m_disableLink); + GenerateSwitchPerItem(commandLine, "-disableSkipDirective:", m_disableSkipDirective); + GenerateSwitchPerItem(commandLine, "-enableSkipDirective:", m_enableSkipDirective); + + // this allow multiple replace rule to happen, we should consider do the same thing for skip: + AddReplaceRulesToOptions(commandLine, m_replaceRuleItemsITaskItem, m_strValueQuote); + AddSkipDirectiveToBaseOptions(commandLine, m_skipRuleItemsITaskItem, m_strValueQuote); + AddImportDeclareParametersFilesOptions(commandLine, m_importDeclareParametersItems); + AddDeclareParametersOptions(commandLine, m_declareParameterItems, m_strValueQuote, OptimisticParameterDefaultValue); + + AddImportSetParametersFilesOptions(commandLine, m_importSetParametersItems); + AddSimpleSetParametersToObject(commandLine, m_simpleSetParamterItems, m_strValueQuote, OptimisticParameterDefaultValue); + AddSetParametersToObject(commandLine, m_setParamterItems, m_strValueQuote, OptimisticParameterDefaultValue); + + if (m_xml) commandLine.AppendSwitch("-xml"); + if (m_whatif) commandLine.AppendSwitch("-whatif"); + if (m_verbose) commandLine.AppendSwitch("-verbose"); + if (m_allowUntrusted) commandLine.AppendSwitch("-allowUntrusted"); + if (m_useChecksum) commandLine.AppendSwitch("-useChecksum"); + + if (m_enableTransaction) commandLine.AppendSwitch("-enableTransaction"); + if (m_retryAttempts > 0) commandLine.AppendSwitchUnquotedIfNotNull("-retryAttempts=", m_retryAttempts.ToString(CultureInfo.InvariantCulture)); + if (m_retryInterval > 0) commandLine.AppendSwitchUnquotedIfNotNull("-retryInterval=", m_retryInterval.ToString(CultureInfo.InvariantCulture)); + + if(!string.IsNullOrEmpty(UserAgent)) + { + commandLine.AppendSwitchUnquotedIfNotNull("-userAgent=", string.Concat("\"", UserAgent, "\"")); + } + + //IISExpress support + //public const string AppHostConfigDirectory = "-appHostConfigDir"; + // * public const string WebServerDirectory = "-webServerDir"; + // public const string WebServerManifest = "-webServerManifest"; + commandLine.AppendSwitchIfNotNull("-appHostConfigDir:", WebServerAppHostConfigDirectory); + commandLine.AppendSwitchIfNotNull("-webServerDir:", WebServerDirectory); + // bug in msdeploy.exe currently only take the file name + commandLine.AppendSwitchIfNotNull("-webServerManifest:", System.IO.Path.GetFileName(WebServerManifest)); + + m_lastCommandLine = commandLine.ToString(); + + // show arguments in the output + Log.LogMessage(Framework.MessageImportance.Low, string.Concat("\"", GenerateFullPathToTool(), "\" ", m_lastCommandLine)); + return m_lastCommandLine; + } + + /// + /// The name of the tool to execute + /// + protected override string ToolName + { + get { return "msdeploy.exe"; } + } + + /// + /// Determine the path to msdeploy.exe + /// + /// path to aspnet_merge.exe, null if not found + protected override string GenerateFullPathToTool() + { + string result = IO.Path.Combine(ExePath, ToolName); + + if (string.Compare(ExePath, "%MSDeployPath%", System.StringComparison.OrdinalIgnoreCase) == 0) { + // if it comes in as %msdeploypath% don't use Path.Combine because it will add a \ which is + // not necessary since reg key for MSDeployPath already contains it + result = string.Format("{0}{1}",ExePath,ToolName); + } + + return result; + } + + /// + /// Validate the task arguments, log any warnings/errors + /// + /// true if arguments are corrent enough to continue processing, false otherwise + protected override bool ValidateParameters() + { + if (this.Source != null && this.Source.GetLength(0) > 1) + { + Log.LogError(string.Format(System.Globalization.CultureInfo.CurrentCulture,SR.MSDEPLOY_InvalidSourceCount, Source.GetLength(0)), null); + return false; + } + + if (this.Destination != null && this.Destination.GetLength(0) > 1) + { + Log.LogError(string.Format(System.Globalization.CultureInfo.CurrentCulture,SR.MSDEPLOY_InvalidDestinationCount, Destination.GetLength(0)), null); + return false; + } + else + { + string[] validVerbs = null; + bool fNullDestination = false; + if (this.Destination == null || this.Destination.GetLength(0) == 0) + { + fNullDestination = true; + validVerbs = new string[] { + "dump", + "getDependencies", + "getParameters", + "getSystemInfo", + }; + } + if (this.Source == null || this.Source.GetLength(0) == 0) + { + validVerbs = new string[] { + "delete", + }; + + } + else + { + validVerbs = new string[] { + "sync", + "migrate", + }; + } + if (validVerbs != null) + { + foreach (string verb in validVerbs) + { + if (string.Compare(this.Verb, verb, System.StringComparison.OrdinalIgnoreCase) == 0) + { + return true; + } + } + } + Log.LogError(string.Format(System.Globalization.CultureInfo.CurrentCulture,SR.MSDEPLOY_InvalidVerbForTheInput, this.Verb, this.Source[0].ItemSpec, (fNullDestination) ? null : this.Destination[0].ItemSpec), null); + return false; + } + } + } +} diff --git a/src/Microsoft.DotNet.Publishing.Tasks/MsDeploy/NormalizeServiceUrl.cs b/src/Microsoft.DotNet.Publishing.Tasks/MsDeploy/NormalizeServiceUrl.cs new file mode 100644 index 000000000000..31edcf641431 --- /dev/null +++ b/src/Microsoft.DotNet.Publishing.Tasks/MsDeploy/NormalizeServiceUrl.cs @@ -0,0 +1,173 @@ +using System; +using System.Globalization; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.DotNet.Publishing.Tasks.MsDeploy +{ + ///----------------------------------------------------------------------------- + /// + /// Covnert server name to foramt like "https://:8172/msdeploy.axd" + /// Code ported (in a combining manner) from: + /// IISOOB\projects\Wdeploy\released\VS_RI4\code\MSDeploy\WDeploy.cs + /// IISOOB\projects\ui\wm\Deployment\Data\Profiles\PublishProfile.cs + /// So VS's behavior conforms to webmatrix and wdeploy as much as possible + /// + ///----------------------------------------------------------------------------- + sealed public class NormalizeServiceUrl : Task + { + private string _serviceUrl = string.Empty; + private string _resultUrl = string.Empty; + private bool _useWMSVC = false; + private bool _useRemoteAgent = false; + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Justification = "This is interface with the Msbuild method, all argument is basically pass by string")] + [Required] + public string ServiceUrl + { + get { return this._serviceUrl; } + set { this._serviceUrl = value; } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "WMSVC", Justification = "Special term that used by MSDeploy team for remote service")] + [Required] + public bool UseWMSVC + { + get { return this._useWMSVC; } + set { this._useWMSVC = value; } + } + + [Required] + public bool UseRemoteAgent + { + get { return this._useRemoteAgent; } + set { this._useRemoteAgent = value; } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Justification = "This is interface with the Msbuild method, all argument is basically pass by string")] + [Output] + public string ResultUrl + { + get { return this._resultUrl; } + } + + public override bool Execute() + { + string tempUrl = _serviceUrl; + if (!string.IsNullOrEmpty(tempUrl)) + { + tempUrl = tempUrl.Trim(); + if (_useWMSVC) + { + _resultUrl = ConstructServiceUrlForDeployThruWMSVC(tempUrl); + } + else + { + if (_useRemoteAgent) + { + _resultUrl = ConstructServiceUrlForDeployThruAgentService(tempUrl);//through remote agent + } + else + { + if (string.Compare(tempUrl, "localhost", System.StringComparison.OrdinalIgnoreCase) == 0 || + string.Compare(tempUrl, "http://localhost", System.StringComparison.OrdinalIgnoreCase) == 0) + { + _resultUrl = string.Empty;//through in proc and don't need server name at all. + } + else + { + if (tempUrl.StartsWith("localhost:", System.StringComparison.OrdinalIgnoreCase)) + { + _resultUrl = string.Concat("http://", tempUrl); + } + else if (tempUrl.StartsWith("http://localhost:", System.StringComparison.OrdinalIgnoreCase)) + { + _resultUrl = tempUrl; + } + } + } + } + return true; + } + else + return false; + } + + //ported from IISOOB\projects\ui\wm\Deployment\Data\Profiles\PublishProfile.cs + private string ConstructServiceUrlForDeployThruWMSVC(string serviceUrl) + { + const string https = "https://"; + const string http = "http://"; + const string msddepaxd = "msdeploy.axd"; + + System.UriBuilder serviceUriBuilder = null; + + // We want to try adding https:// if there is no schema. However abc:123 is parsed as a schema=abc and path=123 + // so the goal is to isolate this case and add the https:// but allow for http if the user chooses to + // since we do not allow for any schema other than http or https, it's safe to assume we can add it if none exist + try + { + if (!(serviceUrl.StartsWith(http, StringComparison.OrdinalIgnoreCase) || serviceUrl.StartsWith(https, StringComparison.OrdinalIgnoreCase))) + { + serviceUrl = string.Concat(https, serviceUrl.TrimStart()); + } + + serviceUriBuilder = new UriBuilder(serviceUrl); + } + catch (NullReferenceException) + { + return string.Empty; + } + catch (ArgumentNullException) + { + return string.Empty; + } + catch (UriFormatException) + { + return serviceUrl; + } + + // if the user did not explicitly defined a port + if (serviceUrl.IndexOf(":" + serviceUriBuilder.Port.ToString(CultureInfo.InvariantCulture), StringComparison.OrdinalIgnoreCase) == -1) + { + serviceUriBuilder.Port = 8172; + } + + // user did not explicitly set a path + if (string.IsNullOrEmpty(serviceUriBuilder.Path) || serviceUriBuilder.Path.Equals("/", StringComparison.OrdinalIgnoreCase)) + { + serviceUriBuilder.Path = msddepaxd; + } + + // user did not explicityly set the scheme + if (serviceUrl.IndexOf(serviceUriBuilder.Scheme, StringComparison.OrdinalIgnoreCase) == -1) + { + serviceUriBuilder.Scheme = https; + } + + return serviceUriBuilder.Uri.AbsoluteUri; + } + + private string ConstructServiceUrlForDeployThruAgentService(string serviceUrl) + { + System.Text.StringBuilder url = new System.Text.StringBuilder("http://"); + int iSpot = 0; + // It needs to start with http:// + // It needs to then have the computer name + // It should then be "/MSDEPLOYAGENTSERVICE" + if (serviceUrl.StartsWith("http://", System.StringComparison.OrdinalIgnoreCase)) + { + iSpot = "http://".Length; + } + url.Append(serviceUrl.Substring(iSpot)); + + int msdepSpot = serviceUrl.IndexOf("/MSDEPLOYAGENTSERVICE", System.StringComparison.OrdinalIgnoreCase); + if (msdepSpot < 0) + { + url.Append("/MSDEPLOYAGENTSERVICE"); + } + + return url.ToString(); + } + } +} diff --git a/src/Microsoft.DotNet.Publishing.Tasks/MsDeploy/VsMSDeployObject.cs b/src/Microsoft.DotNet.Publishing.Tasks/MsDeploy/VsMSDeployObject.cs new file mode 100644 index 000000000000..2cacd2d9a4f6 --- /dev/null +++ b/src/Microsoft.DotNet.Publishing.Tasks/MsDeploy/VsMSDeployObject.cs @@ -0,0 +1,578 @@ +///-------------------------------------------------------------------------------------------- +/// VSMSDeployObject.cs +/// +/// Common utility function +/// +/// Copyright(c) 2006 Microsoft Corporation +///-------------------------------------------------------------------------------------------- +namespace Microsoft.DotNet.Publishing.Tasks.MsDeploy +{ + using Generic = System.Collections.Generic; + using Diagnostics = System.Diagnostics; + // using Deployment = Microsoft.Web.Deployment; + using RegularExpressions = System.Text.RegularExpressions; + + // we need to think of a way to split the MSDeployment to other dll + // using VSMSDeploySyncOption = Deployment.DeploymentSyncOptions; + + + static class VSMSDeployObjectFactory + { + /// + /// Create a object base on msbuild task item + /// + /// + /// + public static VSMSDeployObject CreateVSMSDeployObject(Build.Framework.ITaskItem taskItem) + { + VSMSDeployObject src = new VSMSDeployObject(taskItem); + return src; + } + + /// + /// Create a simple object (no password) + /// + /// + /// + /// + public static VSMSDeployObject CreateVSMSDeployObject(string provider, string path) + { + VSMSDeployObject src = new VSMSDeployObject(provider, path); + return src; + } + + } + + /// + /// Utility class to abstract the multiple MSDeploy object for various secnario + /// It also make sure the Dispose is called properly for MSDeploy object + /// + internal static class MSDeployUtility + { + /// + /// Utility function to create DeploymentBaseOptions base on current vsMsDeployObject + /// + /// + /// + public static /*Deployment.DeploymentBaseOptions*/ dynamic CreateBaseOptions(VSMSDeployObject vSMSDeployObject) + { + // /*Deployment.DeploymentBaseOptions*/dynamic baseOptions = new Microsoft.Web.Deployment.DeploymentBaseOptions(); + /*Deployment.DeploymentBaseOptions*/ + dynamic baseOptions = MSWebDeploymentAssembly.DynamicAssembly.CreateObject("Microsoft.Web.Deployment.DeploymentBaseOptions"); + + if (vSMSDeployObject.IsLocal) + { + // do nothing + } + else if (!vSMSDeployObject.UseSeparatedCredential) + { + baseOptions.ComputerName = vSMSDeployObject.ComputerName; + } + else + { + baseOptions.ComputerName = vSMSDeployObject.ComputerName; + baseOptions.UserName = vSMSDeployObject.UserName; + baseOptions.Password = vSMSDeployObject.Password; + } + + baseOptions.PrefetchPayload = vSMSDeployObject.PrefetchPayload; + baseOptions.IncludeAcls = vSMSDeployObject.IncludeAcls; + if (!string.IsNullOrEmpty(vSMSDeployObject.AuthenticationType)) + baseOptions.AuthenticationType = vSMSDeployObject.AuthenticationType; + if (!string.IsNullOrEmpty(vSMSDeployObject.EncryptPassword)) + baseOptions.EncryptPassword = vSMSDeployObject.EncryptPassword; + + if (!string.IsNullOrEmpty(vSMSDeployObject.WebServerManifest)) + baseOptions.WebServerConfiguration.WebServerManifest = System.IO.Path.GetFileName(vSMSDeployObject.WebServerManifest); + if (!string.IsNullOrEmpty(vSMSDeployObject.WebServerDirectory)) + baseOptions.WebServerConfiguration.WebServerDirectory = vSMSDeployObject.WebServerDirectory; + + if (!string.IsNullOrEmpty(vSMSDeployObject.WebServerAppHostConfigDirectory)) + baseOptions.WebServerConfiguration.ConfigurationDirectory = vSMSDeployObject.WebServerAppHostConfigDirectory; + + + if (vSMSDeployObject.RetryInterval >= 0) + baseOptions.RetryInterval = vSMSDeployObject.RetryInterval; + if (vSMSDeployObject.RetryAttempts >= 0 ) + baseOptions.RetryAttempts = vSMSDeployObject.RetryAttempts; + + if (!string.IsNullOrEmpty(vSMSDeployObject.UserAgent)) + baseOptions.UserAgent = vSMSDeployObject.UserAgent; + + //remove duplicate items appearing in both "EnableLinks" and "DisableLinks" caused by the default value set by publish target file + Generic.List enabledLinkList = ConvertStringIntoList(vSMSDeployObject.EnableLinks); + Generic.List disabledLinkList = ConvertStringIntoList(vSMSDeployObject.DisableLinks); + foreach (string link in disabledLinkList) + { + if (LinkContainedInTheCollection(link, enabledLinkList)) + enabledLinkList.Remove(link); + } + + ChangeLinkExtensionEnableStatue(baseOptions, disabledLinkList, false); + ChangeLinkExtensionEnableStatue(baseOptions, enabledLinkList, true); + + return baseOptions; + } + + /// + /// Utility function to convert a string passed in from target file into a list + /// + /// + /// + internal static Generic.List ConvertStringIntoList(string linkExtensionsString) + { + string linkExtensionsInfo = ""; + if (!string.IsNullOrEmpty(linkExtensionsString)) + { + linkExtensionsInfo = linkExtensionsString; + string[] linksArray = linkExtensionsInfo.Split(new char[] { ';' }); + Generic.List linksList = new Generic.List(linksArray); + return linksList; + } + else + return new System.Collections.Generic.List(0); + + } + + /// + /// we can't use the method of List.Contains, as it is case sensitive, so have to write a separate comparison routine + /// + /// + /// + /// + internal static bool LinkContainedInTheCollection(string link, Generic.List linkCollection) + { + foreach (string l in linkCollection) + if (string.Compare(l, link, System.StringComparison.OrdinalIgnoreCase) == 0) + return true; + return false; + } + + /// + /// Utility function to enable a list of LinkExtensions + /// + /// + /// + /// + public static void ChangeLinkExtensionEnableStatue(/*Deployment.DeploymentBaseOptions*/ dynamic baseOptions, string listOfLinkExtensions, bool enable) + { + if (!string.IsNullOrEmpty(listOfLinkExtensions)) + { + Generic.List linkExtensionList = ConvertStringIntoList(listOfLinkExtensions); + ChangeLinkExtensionEnableStatue(baseOptions, linkExtensionList, enable); + } + } + + /// + /// Utility function to enable a list of LinkExtensions + /// + /// + /// + /// + public static void ChangeLinkExtensionEnableStatue(/*Deployment.DeploymentBaseOptions*/ dynamic baseOptions, System.Collections.Generic.List linkExtensions, bool enable) + { + if (linkExtensions!=null && linkExtensions.Count != 0) + { + foreach (string linkExtObj in linkExtensions) + { + + RegularExpressions.Regex match = new RegularExpressions.Regex(linkExtObj, System.Text.RegularExpressions.RegexOptions.IgnoreCase); + Generic.List matchedList = new Generic.List(); + + foreach (/*Deployment.DeploymentLinkExtension*/dynamic linkExtension in baseOptions.LinkExtensions) + { + if (match.IsMatch(linkExtension.Name)) + { + matchedList.Add(linkExtension); + } + } + + if (matchedList.Count > 0) + { + foreach (/*Deployment.DeploymentLinkExtension*/dynamic extension in matchedList) + { + extension.Enabled = enable; + } + } + else + { + // throw new DeploymentException(Resources.UnknownLinkExtension, disableLink); + //$Todo lmchen + //Diagnostics.Debug.Assert(false, "NYI, we should prompt user for invalid LinkExtension"); + throw new System.InvalidOperationException("UnknowLinkExtension"); + } + } + } + } + } + /// + /// Abstract interface to allow homogenious SynTo() operation to work regardless of the object + /// + internal class VSMSDeployObject + { + + public VSMSDeployObject(string provider, string root) + { + m_NameValueDictionary.Clear(); + m_root = string.IsNullOrEmpty(root) ? string.Empty : root; + + // our code path should only take a well known provider + Diagnostics.Debug.Assert(MsDeploy.Utility.IsDeploymentWellKnownProvider(provider)); + m_provider = provider; + + // maybe we should show the "secure data to display" + // for now just supress it. + if (0 == string.Compare(m_provider, MSWebDeploymentAssembly.DynamicAssembly.GetEnumValue(MSDeploy.TypeName.DeploymentWellKnownProvider, MSDeploy.Provider.DBFullSql).ToString(), System.StringComparison.InvariantCultureIgnoreCase) + || 0 == string.Compare(m_provider, MSDeploy.Provider.DbDacFx , System.StringComparison.InvariantCultureIgnoreCase)) + m_fNoDisplayRoot = true; + } + + public VSMSDeployObject(Build.Framework.ITaskItem taskItem) + { + Diagnostics.Debug.Assert(taskItem != null); + + m_provider = taskItem.ItemSpec; + m_root = taskItem.GetMetadata("Path"); + if (string.IsNullOrEmpty(m_root)) + m_root = string.Empty; + + // our code path should only take a well known provider + Diagnostics.Debug.Assert(MsDeploy.Utility.IsDeploymentWellKnownProvider(m_provider)); + + // maybe we should show the "secure data to display" + // for now just supress it. + if (0 == string.Compare(m_provider, MSWebDeploymentAssembly.DynamicAssembly.GetEnumValue(MSDeploy.TypeName.DeploymentWellKnownProvider, MSDeploy.Provider.DBFullSql).ToString(), System.StringComparison.OrdinalIgnoreCase)) + m_fNoDisplayRoot = true; + + m_NameValueDictionary.Clear(); + foreach (string name in taskItem.MetadataNames) + { + if (!MsDeploy.Utility.IsInternalMsdeployWellKnownItemMetadata(name)) + { + string value = taskItem.GetMetadata(name); + if (!string.IsNullOrEmpty(value)) + { + if (MsDeploy.Utility.IsMsDeployWellKnownLocationInfo(name)) + { + m_NameValueDictionary.Add(name, value); + } + else + { + // these are provider option + this.SetProviderOption(m_provider, name, value); + } + } + } + else + { + MsDeploy.Utility.IISExpressMetadata expressMetadata; + if (System.Enum.TryParse(name, out expressMetadata)) + { + string value = taskItem.GetMetadata(name); + if (!string.IsNullOrEmpty(value)) + { + m_NameValueDictionary.Add(expressMetadata.ToString(), value); + } + } + + } + } + + } + + public VSMSDeployObject(Build.Framework.ITaskItem taskItem, bool fNoDisplayRoot) + : this(taskItem) + { + m_fNoDisplayRoot = fNoDisplayRoot; + } + + + + + private string GetDictionaryValue(string name) + { + string value = null; + if (m_NameValueDictionary != null) + { + m_NameValueDictionary.TryGetValue(name, out value); + } + return value; + } + private void SetDictionaryValue(string name, string value) + { + Diagnostics.Debug.Assert(m_NameValueDictionary != null); + if (m_NameValueDictionary.ContainsKey(name)) + { + m_NameValueDictionary[name] = value; + } + else + { + m_NameValueDictionary.Add(name, value); + } + } + + protected string m_root = string.Empty; + protected string m_disableLinks = string.Empty; + protected string m_enableLinks = string.Empty; + protected string m_provider = "Package"; + protected bool m_fNoDisplayRoot = false; + protected int m_retryInterval = -1; + protected int m_retryAttempts = -1; + + Generic.IList m_iListParameter = new Generic.List(); + Generic.IList m_iListProviderOption = new Generic.List(); + Generic.IList m_iListParameterWithEntry = new Generic.List(); + Generic.IList m_iListSetParametersFiles = new Generic.List(); + + private System.Collections.Generic.Dictionary m_NameValueDictionary = new System.Collections.Generic.Dictionary(10, System.StringComparer.OrdinalIgnoreCase); + + protected /*Deployment.DeploymentBaseOptions*/ dynamic m_deploymentBaseOptions = null; + + public override string ToString() + { + string root = m_fNoDisplayRoot ? "******" : m_root; + return string.Format(System.Globalization.CultureInfo.CurrentCulture,SR.VSMSDEPLOY_ObjectIdentity, m_provider.ToString(), root); + } + + + // property used to call Deployment.DeploymentManager.CreateObject + public virtual string Root + { + get { return this.m_root; } + set { this.m_root = value; } + } + public virtual string Provider + { + get { return this.m_provider; } + set { this.m_provider = value; } + } + + + // property use to create the LocationInfo + public virtual bool IsLocal + { + get { return string.IsNullOrEmpty(this.ComputerName) && string.IsNullOrEmpty(this.MSDeployServiceUrl); } + + } + public virtual bool UseSeparatedCredential + { + get { return !string.IsNullOrEmpty(this.UserName); } + } + + + public virtual string DisableLinks + { + get { return this.m_disableLinks; } + set { this.m_disableLinks = value; } + } + + public virtual string EnableLinks + { + get { return this.m_enableLinks; } + set { this.m_enableLinks = value; } + } + + + + // + // -------------------------// bugbug, not supported yet + // + // + // + // + // + // + + + public virtual string ComputerName + { + get { return GetDictionaryValue("computerName"); } + set { SetDictionaryValue("computerName", value); } + } + public virtual string UserName + { + get { return GetDictionaryValue("userName"); } + set { SetDictionaryValue("userName", value); } + } + + public virtual string Password + { + get { return GetDictionaryValue("password"); } + set { SetDictionaryValue("password", value); } + } + + // Note this support is still broken for vsmsdeploy + public string MSDeployServiceUrl + { + get { + string value = GetDictionaryValue("wmsvc"); + Diagnostics.Debug.Assert(string.IsNullOrEmpty(value), "Not yet implement"); + return value; + } + set { + Diagnostics.Debug.Assert(false, "Not yet implement"); + SetDictionaryValue("wmsvc", value); + } + } + + public string AuthenticationType + { + get + { + string authType = GetDictionaryValue("authType"); + if (string.IsNullOrEmpty(authType)) + { + if (!string.IsNullOrEmpty(MSDeployServiceUrl) && string.IsNullOrEmpty(ComputerName)) + { + authType = "Basic"; + } + } + return authType; + } + + set { SetDictionaryValue("authType", value); } + } + + public string EncryptPassword + { + get { return GetDictionaryValue("encryptPassword"); } + set { SetDictionaryValue("encryptPassword", value); } + } + public bool IncludeAcls + { + get { return System.Convert.ToBoolean(GetDictionaryValue("includeAcls"), System.Globalization.CultureInfo.InvariantCulture); } + set { SetDictionaryValue("includeAcls", value.ToString()); } + } + + public bool PrefetchPayload + { + get { return System.Convert.ToBoolean(GetDictionaryValue("prefetchPayload"), System.Globalization.CultureInfo.InvariantCulture); } + set { SetDictionaryValue("prefetchPayload", value.ToString()); } + } + + + public string WebServerAppHostConfigDirectory + { + get { return GetDictionaryValue("WebServerAppHostConfigDirectory"); } + set { SetDictionaryValue("WebServerAppHostConfigDirectory", value); } + } + + public string WebServerDirectory + { + get { return GetDictionaryValue("WebServerDirectory"); } + set { SetDictionaryValue("WebServerDirectory", value); } + } + + public string WebServerManifest + { + get { return GetDictionaryValue("WebServerManifest"); } + set { SetDictionaryValue("WebServerManifest", value); } + } + + + + public int RetryAttempts + { + get { return this.m_retryAttempts; } + set { this.m_retryAttempts = value; } + } + + public int RetryInterval + { + get { return this.m_retryInterval; } + set { this.m_retryInterval = value; } + } + + public string UserAgent {get;set;} + + + + public Generic.IList Parameters + { + get { return m_iListParameter; } + } + + public Generic.IList ProviderOptions + { + get { return m_iListProviderOption; } + } + + public void SetProviderOption(string factoryName, string parameterName, string parameterStringValue) + { + m_iListProviderOption.Add(new Microsoft.DotNet.Publishing.Tasks.MsDeploy.ProviderOption(factoryName, parameterName, parameterStringValue)); + } + + public void SyncParameter(string parameterName, string parameterStringValue) + { + m_iListParameter.Add(new MsDeploy.ParameterInfo(parameterName, parameterStringValue)); + } + + + public Generic.IList EntryParameters + { + get { return m_iListParameterWithEntry; } + } + + public Generic.IList SetParametersFiles + { + get { return m_iListSetParametersFiles; } + } + + + public void SyncParameter(string name, string value, string type, string scope, string matchRegularExpression, string description, string defaultValue, string tags, string element, string validationString) + { + m_iListParameterWithEntry.Add(new MsDeploy.ParameterInfoWithEntry(name, value, type, scope, matchRegularExpression, description, defaultValue, tags, element, validationString)); + } + + public void SyncParameterFile(string filename) + { + m_iListSetParametersFiles.Add(filename); + } + + + public void ResetBaseOptions() + { + m_deploymentBaseOptions = null; + } + + public /*Deployment.DeploymentBaseOptions*/ dynamic BaseOptions + { + get + { + if (m_deploymentBaseOptions == null) + { + m_deploymentBaseOptions = MSDeployUtility.CreateBaseOptions(this); + } + return m_deploymentBaseOptions; + } + } + + + public void SyncTo(VSMSDeployObject destObject, /*VSMSDeploySyncOption*/ dynamic syncOptions, IVSMSDeployHost _host) + { + //$BUGBUG lmchen, there is only set to source provider? + // set up the provider setting + /*Deployment.DeploymentProviderOptions*/ dynamic srcProviderConfig = MSWebDeploymentAssembly.DynamicAssembly.CreateObject("Microsoft.Web.Deployment.DeploymentProviderOptions", new object[]{this.Provider.ToString()}); + srcProviderConfig.Path = this.Root; + MsDeploy.Utility.AddProviderOptions(srcProviderConfig, this.ProviderOptions, _host); + + using (/*Deployment.DeploymentObject*/ dynamic srcObj = MSWebDeploymentAssembly.DynamicAssembly.CallStaticMethod("Microsoft.Web.Deployment.DeploymentManager", "CreateObject", new object[]{srcProviderConfig, this.BaseOptions})) + { + + //$BUGBUG lmchen, there is only set to source provider? + // set up the parameter + MsDeploy.Utility.AddSetParametersFilesToObject(srcObj, this.SetParametersFiles, _host); + MsDeploy.Utility.AddSimpleSetParametersToObject(srcObj, this.Parameters, _host); + MsDeploy.Utility.AddSetParametersToObject(srcObj, this.EntryParameters, _host); + + /*Deployment.DeploymentProviderOptions*/ dynamic destProviderConfig = MSWebDeploymentAssembly.DynamicAssembly.CreateObject("Microsoft.Web.Deployment.DeploymentProviderOptions", new object[]{destObject.Provider.ToString()}); + destProviderConfig.Path = destObject.Root; + + // Setup Destination Provider otpion + MsDeploy.Utility.AddProviderOptions(destProviderConfig, destObject.ProviderOptions, _host); + + srcObj.SyncTo(destProviderConfig, destObject.BaseOptions, syncOptions); + } + } + } +} \ No newline at end of file diff --git a/src/Microsoft.DotNet.Publishing.Tasks/MsDeploy/VsMsdeploy.cs b/src/Microsoft.DotNet.Publishing.Tasks/MsDeploy/VsMsdeploy.cs new file mode 100644 index 000000000000..e66fcafa5707 --- /dev/null +++ b/src/Microsoft.DotNet.Publishing.Tasks/MsDeploy/VsMsdeploy.cs @@ -0,0 +1,1077 @@ + +namespace Microsoft.DotNet.Publishing.Tasks.MsDeploy +{ + using Diagnostics = System.Diagnostics; + using Collections = System.Collections; + using Framework = Microsoft.Build.Framework; + using Utilities = Microsoft.Build.Utilities; + using Threading = System.Threading; + using Net = System.Net; + using Cryptography = System.Security.Cryptography; + using System.Linq; + using System.Linq.Expressions; + using System.Reflection; + using System.Collections.Generic; + using Microsoft.Build.Framework; + + + /// + /// WrapperClass for Microsoft.Web.Deployment + /// + internal class MSWebDeploymentAssembly : DynamicAssembly + { + public MSWebDeploymentAssembly(System.Version verToLoad) : + base(MSWebDeploymentAssembly.AssemblyName, verToLoad, "31bf3856ad364e35") + { + } + + static public string AssemblyName { get { return "Microsoft.Web.Deployment";}} + static public MSWebDeploymentAssembly DynamicAssembly { get; set; } + static public void SetVersion(System.Version version) + { + if (DynamicAssembly == null || DynamicAssembly.Version != version) + { + DynamicAssembly = new MSWebDeploymentAssembly(version); + } + } + + /// + /// Utility function to help out on getting Deployment colleciton's tryGetMethod + /// + /// + /// + /// + /// + static public 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(), System.StringComparison.OrdinalIgnoreCase) == 0) + { + foundObject = item; + return true; + } + } + } + return false; + } + + + static public 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; + } + } + + /// + /// WrapperClass for Microsoft.Web.Delegation + /// + internal class MSWebDelegationAssembly : DynamicAssembly + { + public MSWebDelegationAssembly(System.Version verToLoad) : + base(MSWebDelegationAssembly.AssemblyName, verToLoad, "31bf3856ad364e35") + { + } + + static public string AssemblyName { get { return "Microsoft.Web.Delegation"; } } + + static public MSWebDelegationAssembly DynamicAssembly { get; set; } + static public void SetVersion(System.Version version) + { + if (DynamicAssembly == null || DynamicAssembly.Version != version) + { + DynamicAssembly = new MSWebDelegationAssembly(version); + } + } + } + + + // Microsoft.Web.Delegation + + ///-------------------------------------------------------------------- + enum DeployStatus + { + ReadyToDeploy, + Deploying, + DeployFinished, + DeployAbandoned, + DeployFailed + } + + /// + /// Encapsulte the process of interacting with MSDeploy + /// + 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; + } + + /// + /// Boolean to cancel the operation + /// (TODO: in RTM, use thread synchoronization to protect the entry(though not absoluately necessary. + /// Need consider perf hit incurred though as msdeploy's callback will reference the value frequently) + /// + 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; } + } + + /// + /// called by the msdeploy to cancel the operation + /// + /// + private bool CancelCallback() + { + return IsCancelOperation; + } + + protected /*VSMSDeploySyncOption*/ dynamic CreateOptionIfNeeded() + { + if (_option == null) + { + object option = MSWebDeploymentAssembly.DynamicAssembly.CreateObject("Microsoft.Web.Deployment.DeploymentSyncOptions"); + System.Type deploymentCancelCallbackType = MSWebDeploymentAssembly.DynamicAssembly.GetType("Microsoft.Web.Deployment.DeploymentCancelCallback"); + object cancelCallbackDelegate = System.Delegate.CreateDelegate(deploymentCancelCallbackType, this, "CancelCallback"); + + MsDeploy.Utility.SetDynamicProperty(option, "CancelCallback", cancelCallbackDelegate); + + // dynamic doesn't work with delegate. it complain on explicit cast needed from object -> DelegateType :( + // _option.CancelCallback = cancelCallbackDelegate; + _option = option; + } + return _option; + } + + + private System.Collections.Generic.Dictionary _highImportanceEventTypes = null; + private System.Collections.Generic.Dictionary GetHighImportanceEventTypes() + { + if (_highImportanceEventTypes == null) + { + _highImportanceEventTypes = new System.Collections.Generic.Dictionary(System.StringComparer.InvariantCultureIgnoreCase); ; + if (!string.IsNullOrEmpty(HighImportanceEventTypes)) + { + string[] typeNames = HighImportanceEventTypes.Split(new char[] { ';' }); // follow msbuild convention + foreach (string typeName in typeNames) + { + _highImportanceEventTypes.Add(typeName, Framework.MessageImportance.High); + } + } + } + return _highImportanceEventTypes; + } + + void TraceEventHandlerDynamic(object sender, dynamic e) + { + // throw new System.NotImplementedException(); + string msg = e.Message; + System.Diagnostics.Trace.WriteLine("MSDeploy TraceEvent Handler is called with " + msg); + LogTrace(e, GetHighImportanceEventTypes()); + //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 + // // Loger 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 + // } + //} + } + + + /// + /// Using MSDeploy API to invoke MSDeploy + /// + protected void InvokeMSdeploySync() + { + /*VSMSDeploySyncOption*/ dynamic option = CreateOptionIfNeeded(); + IsCancelOperation = false; + + _host.PopulateOptions(option); + + // you can reuse traceEventHandler if you know the function signuture is the same + System.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(); + + } + + /// + /// The end to end process to invoke MSDeploy + /// + /// + public void SyncThruMSDeploy() + { + BeforeSync(); + StartSync(); + WaitForDone(); + AfterSync(); + } + + /// + /// Encapsulate the things be done before invoke MSDeploy + /// + abstract protected void BeforeSync(); + + /// + /// Encapsulate the approach to invoke the MSDeploy (same thread or in a seperate thread; ui or without ui) + /// + abstract protected void StartSync(); + + /// + /// Encapsulate the approach to wait for the MSDeploy done + /// + abstract protected void WaitForDone(); + + /// + /// Encapsulate how to report the Trace information + /// + /// + // abstract protected void LogTrace(Deployment.DeploymentTraceEventArgs e); + + abstract protected void LogTrace(dynamic e, System.Collections.Generic.IDictionary customTypeLoging ); + + /// + /// Encapsulate the things to be done after the deploy is done + /// + abstract protected void AfterSync(); + + /// + /// constructor + /// + /// + /// + 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; + } + } + + /// + /// We create CustomBuildWithPropertiesEventArgs is for the purpose of logging verious information + /// in a IDictionary such that the MBuild handler can handle generically. + /// + [System.Serializable] + public class CustomBuildWithPropertiesEventArgs : Framework.CustomBuildEventArgs, Collections.IDictionary + { + public CustomBuildWithPropertiesEventArgs() : base() { } + public CustomBuildWithPropertiesEventArgs(string msg, string keyword, string senderName) + : base(msg, keyword, senderName) + { + } + + Collections.Specialized.HybridDictionary m_hybridDictionary = new System.Collections.Specialized.HybridDictionary(10); + #region IDictionary Members + // Delegate everything to m_hybridDictionary + + public void Add(object key, object value) + { + m_hybridDictionary.Add(key, value); + } + + public void Clear() + { + m_hybridDictionary.Clear(); + } + + public bool Contains(object key) + { + return m_hybridDictionary.Contains(key); + } + + public System.Collections.IDictionaryEnumerator GetEnumerator() + { + return m_hybridDictionary.GetEnumerator(); + } + + public bool IsFixedSize + { + get { return m_hybridDictionary.IsFixedSize; } + } + + public bool IsReadOnly + { + get { return m_hybridDictionary.IsReadOnly; } + } + + public System.Collections.ICollection Keys + { + get { return m_hybridDictionary.Keys; } + } + + public void Remove(object key) + { + m_hybridDictionary.Remove(key); + } + + public System.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(System.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 + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + #endregion + } + + + + /// + /// Deploy through msbuild in command line + /// + class VSMSDeployDriverInCmd : BaseMSDeployDriver + { + protected override void BeforeSync() + { + string strMsg = string.Format(System.Globalization.CultureInfo.CurrentCulture,SR.VSMSDEPLOY_Start, _src.ToString(), _dest.ToString()); + _host.Log.LogMessage(strMsg); + } + + + // Utility function to log all public instance property to CustomerBuildEventArgs + private static void AddAllPropertiesToCustomBuildWithPropertyEventArgs(CustomBuildWithPropertiesEventArgs cbpEventArg,System.Object obj) + { + if (obj != null) + { + System.Type thisType = obj.GetType(); + cbpEventArg.Add("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 != null) + cbpEventArg.Add(memberinfo.Name, val); + } + } + } + } + + + ///// + ///// Log Trace ifnormation in the command line + ///// + ///// + //protected override void LogTrace(Microsoft.Web.Deployment.DeploymentTraceEventArgs args) + //{ + // string strMsg = args.Message; + // string strEventType = "Trace"; + // Framework.MessageImportance messageImportance = Microsoft.Build.Framework.MessageImportance.Low; + + // if (args is Deployment.DeploymentFileSerializationEventArgs || + // args is Deployment.DeploymentPackageSerializationEventArgs || + // args is Deployment.DeploymentObjectChangedEventArgs || + // args is Deployment.DeploymentSyncParameterEventArgs ) + // { + // //promote those message only for those event + // strEventType = "Action"; + // messageImportance = Microsoft.Build.Framework.MessageImportance.High; + // } + + // if (!string.IsNullOrEmpty(strMsg)) + // { + // switch (args.EventLevel) + // { + // case System.Diagnostics.TraceLevel.Off: + // break; + // case System.Diagnostics.TraceLevel.Error: + // _host.Log.LogError(strMsg); + // break; + // case System.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 + // CustomBuildWithPropertiesEventArgs customBuildWithPropertiesEventArg = new CustomBuildWithPropertiesEventArgs(args.Message, null, TaskName); + + // customBuildWithPropertiesEventArg.Add("TaskName", TaskName); + // customBuildWithPropertiesEventArg.Add("EventType", strEventType); + // AddAllPropertiesToCustomBuildWithPropertyEventArgs(customBuildWithPropertiesEventArg, args); + // _host.BuildEngine.LogCustomEvent(customBuildWithPropertiesEventArg); + //} + + + protected override void LogTrace(dynamic args, System.Collections.Generic.IDictionary customTypeLoging) + { + string strMsg = args.Message; + string strEventType = "Trace"; + Framework.MessageImportance messageImportance = Microsoft.Build.Framework.MessageImportance.Low; + + System.Type argsT = args.GetType(); + if (MsDeploy.Utility.IsType(argsT, MSWebDeploymentAssembly.DynamicAssembly.GetType("Microsoft.Web.Deployment.DeploymentFileSerializationEventArgs")) || + MsDeploy.Utility.IsType(argsT, MSWebDeploymentAssembly.DynamicAssembly.GetType("Microsoft.Web.Deployment.DeploymentPackageSerializationEventArgs")) || + MsDeploy.Utility.IsType(argsT, MSWebDeploymentAssembly.DynamicAssembly.GetType("Microsoft.Web.Deployment.DeploymentObjectChangedEventArgs")) || + MsDeploy.Utility.IsType(argsT, MSWebDeploymentAssembly.DynamicAssembly.GetType("Microsoft.Web.Deployment.DeploymentSyncParameterEventArgs"))) + { + //promote those message only for those event + strEventType = "Action"; + messageImportance = Microsoft.Build.Framework.MessageImportance.High; + } + else if (customTypeLoging != null && customTypeLoging.ContainsKey(argsT.Name)) + { + strEventType = "Trace"; + messageImportance = customTypeLoging[argsT.Name]; + } + + if (!string.IsNullOrEmpty(strMsg)) + { + System.Diagnostics.TraceLevel level = (System.Diagnostics.TraceLevel)System.Enum.ToObject(typeof(System.Diagnostics.TraceLevel), args.EventLevel); + switch (level) + { + case System.Diagnostics.TraceLevel.Off: + break; + case System.Diagnostics.TraceLevel.Error: + _host.Log.LogError(strMsg); + break; + case System.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 + CustomBuildWithPropertiesEventArgs customBuildWithPropertiesEventArg = new CustomBuildWithPropertiesEventArgs(args.Message, null, TaskName); + + customBuildWithPropertiesEventArg.Add("TaskName", TaskName); + customBuildWithPropertiesEventArg.Add("EventType", strEventType); + AddAllPropertiesToCustomBuildWithPropertyEventArgs(customBuildWithPropertiesEventArg, args); + _host.BuildEngine.LogCustomEvent(customBuildWithPropertiesEventArg); + } + + /// + /// Invoke MSDeploy + /// + protected override void StartSync() + { + InvokeMSdeploySync(); + } + + /// + /// Wait foreve if we are in the command line + /// + protected override void WaitForDone() { } + + /// + /// Log status after the deploy is done + /// + protected override void AfterSync() + { + string strMsg = SR.VSMSDEPLOY_Succeeded; + _host.Log.LogMessage(strMsg); + } + + /// + /// construct + /// + /// + /// + /// + internal VSMSDeployDriverInCmd(VSMSDeployObject src, VSMSDeployObject dest, IVSMSDeployHost host) + : base(src, dest, host) + { + if (host.GetProperty("HighImportanceEventTypes") != null) + this.HighImportanceEventTypes = host.GetProperty("HighImportanceEventTypes").ToString(); + } + } + + + /// + /// MSBuild Task VSMSDeploy to call the object through UI or not + /// + public class VSMSDeploy : Utilities.Task, IVSMSDeployHost, Framework.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; + + //task of VSMSDeploy is used by package and publishing(transport) targets both, so we create a field to identify + //we are in package or publishing. We probably should create a separate task class for "publishing" + bool _invokedByPublish = false; + bool _allowUntrustedCert; + bool _skipExtraFilesOnServer=false; + + private Framework.ITaskItem[] m_sourceITaskItem = null; + private Framework.ITaskItem[] m_destITaskItem = null; + private Framework.ITaskItem[] m_replaceRuleItemsITaskItem = null; + private Framework.ITaskItem[] m_skipRuleItemsITaskItem = null; + private Framework.ITaskItem[] m_declareParameterItems = null; + private Framework.ITaskItem[] m_importDeclareParametersItems = null; + private Framework.ITaskItem[] m_simpleSetParamterItems = null; + private Framework.ITaskItem[] m_importSetParametersItems = null; + private Framework.ITaskItem[] m_setParamterItems = null; + + private BaseMSDeployDriver m_msdeployDriver = null; + + [Framework.Required] + public Framework.ITaskItem[] Source + { + get { return this.m_sourceITaskItem; } + set { this.m_sourceITaskItem = value; } + } + + public string HighImportanceEventTypes + { + get; + set; + } + + public Framework.ITaskItem[] Destination + { + get { return this.m_destITaskItem; } + set { this.m_destITaskItem = value; } + } + + public bool AllowUntrustedCertificate + { + get { return _allowUntrustedCert; } + set { _allowUntrustedCert = value; } + } + + public bool SkipExtraFilesOnServer + { + get { return _skipExtraFilesOnServer;} + set { _skipExtraFilesOnServer = value; } + } + + public bool InvokedByPublish + { + get { return _invokedByPublish; } + set { _invokedByPublish = 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 + [Framework.Output] + public bool Result + { + get { return _result; } + set { _result = value; } + } + + /// + /// Disable Link is a list of disable provider + /// + 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 { this._disableSkipDirective = value; } + } + + public string EnableSkipDirective + { + get { return _enableSkipDirective; } + set { this._enableSkipDirective = value; } + } + + public int RetryAttempts + { + get { return this.m_retryAttempts; } + set { this.m_retryAttempts = value; } + } + + public int RetryInterval + { + get { return this.m_retryInterval; } + set { this.m_retryInterval = value; } + } + + + public Framework.ITaskItem[] ReplaceRuleItems + { + get { return m_replaceRuleItemsITaskItem; } + set { this.m_replaceRuleItemsITaskItem = value; } + } + + + public Framework.ITaskItem[] SkipRuleItems + { + get { return m_skipRuleItemsITaskItem; } + set { this.m_skipRuleItemsITaskItem = value; } + } + + + public Framework.ITaskItem[] DeclareParameterItems + { + get { return this.m_declareParameterItems; } + set { this.m_declareParameterItems = value; } + } + + public bool OptimisticParameterDefaultValue { get; set; } + + + public Framework.ITaskItem[] ImportDeclareParametersItems + { + get { return m_importDeclareParametersItems; } + set { this.m_importDeclareParametersItems = value; } + } + + public Framework.ITaskItem[] SimpleSetParameterItems + { + get { return m_simpleSetParamterItems; } + set { this.m_simpleSetParamterItems = value; } + } + + public Framework.ITaskItem[] ImportSetParametersItems + { + get { return m_importSetParametersItems; } + set { this.m_importSetParametersItems = value; } + } + + public Framework.ITaskItem[] SetParameterItems + { + get { return m_setParamterItems; } + set { this.m_setParamterItems = 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 = MsDeploy.Utility.GetFullUserAgentString(value); + } + } + } + + public Framework.ITaskItem[] AdditionalDestinationProviderOptions {get;set;} + + public bool BuildingInsideVisualStudio + { + get; + set; + } + + public string MSDeployVersionsToTry + { + get; + set; + } + + public override bool Execute() + { + Result = false; + + try + { + MsDeploy.Utility.SetupMSWebDeployDynamicAssemblies(MSDeployVersionsToTry, this); + } + catch (System.Exception exception) + { + this.Log.LogErrorFromException(exception); + return false; // failed the task + } + + string errorMessage = null; + if (!MsDeploy.Utility.CheckMSDeploymentVersion(this.Log, out errorMessage)) + return false; + + VSMSDeployObject src = null ; + VSMSDeployObject dest = null; + + if (this.Source == null || this.Source.GetLength(0) != 1) + { + this.Log.LogError("Source must be 1 item"); + return false; + } + else + { + src = VSMSDeployObjectFactory.CreateVSMSDeployObject(this.Source[0]); + } + + if (this.Destination == null || this.Destination.GetLength(0) != 1) + { + this.Log.LogError("Destination must be 1 item"); + return false; + } + else + { + dest = VSMSDeployObjectFactory.CreateVSMSDeployObject(this.Destination[0]); + } + + //$Todo, Should we split the Disable Link to two set of setting, one for source, one for destination + src.DisableLinks = this.DisableLink; + dest.DisableLinks = this.DisableLink; + src.EnableLinks = this.EnableLink; + dest.EnableLinks = this.EnableLink; + if (this.RetryAttempts >= 0) + { + src.RetryAttempts = this.RetryAttempts; + dest.RetryAttempts = this.RetryAttempts; + } + if (this.RetryInterval >= 0) + { + src.RetryInterval = this.RetryInterval; + dest.RetryInterval = this.RetryInterval; + } + dest.UserAgent = this.UserAgent; + + // 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 (System.Exception e) + { + if (e is System.Reflection.TargetInvocationException) + { + if (e.InnerException != null) + e = e.InnerException; + } + + System.Type eType = e.GetType(); + if (MsDeploy.Utility.IsType( eType, MSWebDeploymentAssembly.DynamicAssembly.GetType("Microsoft.Web.Deployment.DeploymentCanceledException"))) + { + Log.LogMessageFromText(SR.VSMSDEPLOY_Canceled, Microsoft.Build.Framework.MessageImportance.High); + } + else if (MsDeploy.Utility.IsType(eType, MSWebDelegationAssembly.DynamicAssembly.GetType("Microsoft.Web.Deployment.DeploymentException")) + || MsDeploy.Utility.IsType(eType, MSWebDeploymentAssembly.DynamicAssembly.GetType("Microsoft.Web.Deployment.DeploymentFatalException"))) + { + MsDeploy.Utility.LogVsMsDeployException(Log, e); + } + else + { + if (!driver.IsCancelOperation) + Log.LogError(string.Format(System.Globalization.CultureInfo.CurrentCulture, SR.VSMSDEPLOY_FailedWithException, e.Message)); + } + } + + Tasks.MsDeploy.Utility.MsDeployEndOfExecuteMessage(Result, dest.Provider, dest.Root, Log); + return Result; + } + + #region IVSMSDeployHost Members + + string IVsPublishMsBuildTaskHost.TaskName + { + get { + return GetType().Name; + } + } + + Microsoft.Build.Utilities.TaskLoggingHelper IVsPublishMsBuildTaskHost.Log + { + get { + return Log; + } + } + + Microsoft.Build.Framework.IBuildEngine IVsPublishMsBuildTaskHost.BuildEngine + { + get { + return BuildEngine; + } + } + + void IVSMSDeployHost.UpdateDeploymentBaseOptions(VSMSDeployObject srcVsMsDeployobject, VSMSDeployObject destVsMsDeployobject) + { + Collections.Generic.List enableSkipDirectiveList = MSDeployUtility.ConvertStringIntoList(EnableSkipDirective); + Collections.Generic.List disableSkipDirectiveList = MSDeployUtility.ConvertStringIntoList(DisableSkipDirective); + + MsDeploy.Utility.AddSkipDirectiveToBaseOptions(srcVsMsDeployobject.BaseOptions, null, enableSkipDirectiveList, disableSkipDirectiveList, Log); + MsDeploy.Utility.AddSkipDirectiveToBaseOptions(destVsMsDeployobject.BaseOptions, null, enableSkipDirectiveList, disableSkipDirectiveList, Log); + + if (!string.IsNullOrEmpty(DeploymentTraceLevel)) + { + Diagnostics.TraceLevel deploymentTraceEventLevel = + (Diagnostics.TraceLevel)System.Enum.Parse(typeof(Diagnostics.TraceLevel), DeploymentTraceLevel, true); + srcVsMsDeployobject.BaseOptions.TraceLevel = deploymentTraceEventLevel; + destVsMsDeployobject.BaseOptions.TraceLevel = deploymentTraceEventLevel; + } + + MsDeploy.Utility.AddSetParametersFilesVsMsDeployObject(srcVsMsDeployobject, ImportSetParametersItems); + MsDeploy.Utility.AddSimpleSetParametersVsMsDeployObject(srcVsMsDeployobject, SimpleSetParameterItems, OptimisticParameterDefaultValue); + MsDeploy.Utility.AddSetParametersVsMsDeployObject(srcVsMsDeployobject, SetParameterItems, OptimisticParameterDefaultValue); + + AddAdditionalProviderOptions(destVsMsDeployobject); + } + + 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 + MsDeploy.Utility.AddReplaceRulesToOptions(option.Rules, ReplaceRuleItems); + MsDeploy.Utility.AddImportDeclareParametersFileOptions(option, ImportDeclareParametersItems); + MsDeploy.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 (System.Exception ex) + { + Diagnostics.Debug.Fail("Exception on ICancelableTask.Cancel being invoked:" + ex.Message); + } + } + + #endregion + + + public object GetProperty(string propertyName) + { + string lowerName = propertyName.ToLower(System.Globalization.CultureInfo.InvariantCulture); + switch (lowerName) + { + case "msdeployversionstotry": + return this.MSDeployVersionsToTry; + case "highimportanceeventtypes": + return this.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 (Collections.Generic.KeyNotFoundException){ } + + if (!ruleExists) + { + dynamic appOfflineRuleHanlder = MSWebDeploymentAssembly.DynamicAssembly.CreateObject(handlerType, new object[]{}); + dynamic appOfflineRule = MSWebDeploymentAssembly.DynamicAssembly.CreateObject("Microsoft.Web.Deployment.DeploymentRule", + new object[] { ruleName, appOfflineRuleHanlder }); + option.Rules.Add(appOfflineRule); + } + } + } +}