// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Security; using Microsoft.Internal.Web.Utils; using Microsoft.Web.Infrastructure; namespace System.Web.WebPages.Deployment { internal static class AssemblyUtils { // Copied from AssemblyRefs.cs private const string SharedLibPublicKey = "31bf3856ad364e35"; internal static readonly AssemblyName ThisAssemblyName = new AssemblyName(typeof(AssemblyUtils).Assembly.FullName); private static readonly Version WebPagesV1Version = new Version(1, 0, 0, 0); private static readonly string _binFileName = Path.GetFileName(ThisAssemblyName.Name) + ".dll"; // Special case MWI because it does not share the same assembly version as the rest of WebPages. private static readonly Version _mwiVersion = new AssemblyName(typeof(InfrastructureHelper).Assembly.FullName).Version; private static readonly AssemblyName _mwiAssemblyName = GetFullName("Microsoft.Web.Infrastructure", _mwiVersion); private static readonly AssemblyName[] _version1AssemblyList = new[] { _mwiAssemblyName, GetFullName("System.Web.Razor", WebPagesV1Version), GetFullName("System.Web.Helpers", WebPagesV1Version), GetFullName("System.Web.WebPages", WebPagesV1Version), GetFullName("System.Web.WebPages.Administration", WebPagesV1Version), GetFullName("System.Web.WebPages.Razor", WebPagesV1Version), GetFullName("WebMatrix.Data", WebPagesV1Version), GetFullName("WebMatrix.WebData", WebPagesV1Version) }; private static readonly AssemblyName[] _versionCurrentAssemblyList = new[] { _mwiAssemblyName, GetFullName("System.Web.Razor", ThisAssemblyName.Version), GetFullName("System.Web.Helpers", ThisAssemblyName.Version), GetFullName("System.Web.WebPages", ThisAssemblyName.Version), GetFullName("System.Web.WebPages.Administration", ThisAssemblyName.Version), GetFullName("System.Web.WebPages.Razor", ThisAssemblyName.Version), GetFullName("WebMatrix.Data", ThisAssemblyName.Version), GetFullName("WebMatrix.WebData", ThisAssemblyName.Version) }; internal static Version GetMaxWebPagesVersion() { return GetMaxWebPagesVersion(GetLoadedAssemblies()); } internal static Version GetMaxWebPagesVersion(IEnumerable loadedAssemblies) { return GetWebPagesAssemblies(loadedAssemblies).Max(c => c.Version); } internal static bool IsVersionAvailable(Version version) { return IsVersionAvailable(GetLoadedAssemblies(), version); } internal static bool IsVersionAvailable(IEnumerable loadedAssemblies, Version version) { return GetWebPagesAssemblies(loadedAssemblies).Any(c => c.Version == version); } private static IEnumerable GetWebPagesAssemblies(IEnumerable loadedAssemblies) { return (from otherName in loadedAssemblies where NamesMatch(ThisAssemblyName, otherName, matchVersion: false) select otherName); } /// /// Returns the version of a System.Web.WebPages.Deployment.dll if it is present in the bin and matches the name and /// public key token of the current assembly. /// /// Version from bin if present, null otherwise. internal static Version GetVersionFromBin(string binDirectory, IFileSystem fileSystem, Func getAssemblyNameThunk = null) { // If a version of the assembly is present both in the bin and the GAC, the GAC would win. // To work around this, we'll look for a physical file on disk with the same name as the current assembly and load it to determine the version. // Determine if the Deployment assembly is present in the bin var assemblyInBin = Path.Combine(binDirectory, _binFileName); if (fileSystem.FileExists(assemblyInBin)) { try { getAssemblyNameThunk = getAssemblyNameThunk ?? AssemblyName.GetAssemblyName; AssemblyName assemblyName = getAssemblyNameThunk(assemblyInBin); if (NamesMatch(ThisAssemblyName, assemblyName, matchVersion: false)) { return assemblyName.Version; } } catch (BadImageFormatException) { // Do nothing. } catch (SecurityException) { // Do nothing } catch (FileLoadException) { // Do nothing. } } return null; } internal static bool NamesMatch(AssemblyName left, AssemblyName right, bool matchVersion) { return Equals(left.Name, right.Name) && Equals(left.CultureInfo, right.CultureInfo) && Enumerable.SequenceEqual(left.GetPublicKeyToken(), right.GetPublicKeyToken()) && (!matchVersion || Equals(left.Version, right.Version)); } internal static IEnumerable GetLoadedAssemblies() { return AppDomain.CurrentDomain.GetAssemblies() .Select(GetAssemblyName) .ToList(); } internal static IEnumerable GetAssembliesForVersion(Version version) { if (version == WebPagesV1Version) { return _version1AssemblyList; } return _versionCurrentAssemblyList; } private static AssemblyName GetAssemblyName(Assembly assembly) { return new AssemblyName(assembly.FullName); } private static AssemblyName GetFullName(string name, Version version, string publicKeyToken) { return new AssemblyName(String.Format(CultureInfo.InvariantCulture, "{0}, Version={1}, Culture=neutral, PublicKeyToken={2}", name, version, publicKeyToken)); } internal static AssemblyName GetFullName(string name, Version version) { return GetFullName(name, version, SharedLibPublicKey); } public static IDictionary GetAssembliesMatchingOtherVersions(IDictionary> references) { var webPagesAssemblies = AssemblyUtils.GetAssembliesForVersion(AssemblyUtils.ThisAssemblyName.Version); if (webPagesAssemblies == null || !webPagesAssemblies.Any()) { return new Dictionary(0); } var matchingVersions = from item in references let matchedVersion = GetMatchingVersion(webPagesAssemblies, item.Value) where matchedVersion != null select new KeyValuePair(item.Key, matchedVersion); return matchingVersions.ToDictionary(k => k.Key, k => k.Value); } private static Version GetMatchingVersion(IEnumerable webPagesAssemblies, IEnumerable references) { // Return assemblies that match in name but not in version. var matchingVersions = from webPagesAssembly in webPagesAssemblies from referenceName in references let referencedAssembly = new AssemblyName(referenceName) where AssemblyUtils.NamesMatch(webPagesAssembly, referencedAssembly, matchVersion: false) && webPagesAssembly.Version != referencedAssembly.Version select referencedAssembly.Version; return matchingVersions.FirstOrDefault(); } } }