File: System\ComponentModel\Design\DesigntimeLicenseContext.cs
Web Access
Project: src\src\libraries\System.ComponentModel.TypeConverter\src\System.ComponentModel.TypeConverter.csproj (System.ComponentModel.TypeConverter)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Reflection;
 
namespace System.ComponentModel.Design
{
    /// <summary>
    /// Provides design-time support for licensing.
    /// </summary>
    public class DesigntimeLicenseContext : LicenseContext
    {
        internal Hashtable _savedLicenseKeys = new Hashtable();
 
        /// <summary>
        /// Gets or sets the license usage mode.
        /// </summary>
        public override LicenseUsageMode UsageMode => LicenseUsageMode.Designtime;
 
        /// <summary>
        /// Gets a saved license key.
        /// </summary>
        public override string? GetSavedLicenseKey(Type type, Assembly? resourceAssembly) => null;
 
        /// <summary>
        /// Sets a saved license key.
        /// </summary>
        public override void SetSavedLicenseKey(Type type, string key)
        {
            ArgumentNullException.ThrowIfNull(type);
 
            _savedLicenseKeys[type.AssemblyQualifiedName!] = key;
        }
    }
 
    internal sealed class RuntimeLicenseContext : LicenseContext
    {
        internal Hashtable? _savedLicenseKeys;
 
        /// <summary>
        /// This method takes a file URL and converts it to a local path. The trick here is that
        /// if there is a '#' in the path, everything after this is treated as a fragment. So
        /// we need to append the fragment to the end of the path.
        /// </summary>
        private static string GetLocalPath(string fileName)
        {
            Debug.Assert(fileName != null && fileName.Length > 0, "Cannot get local path, fileName is not valid");
 
            Uri uri = new Uri(fileName);
            return uri.LocalPath + uri.Fragment;
        }
 
        [UnconditionalSuppressMessage("SingleFile", "IL3000: Avoid accessing Assembly file path when publishing as a single file",
            Justification = "Suppressing the warning until gets fixed, see https://github.com/dotnet/runtime/issues/50821")]
        public override string? GetSavedLicenseKey(Type type, Assembly? resourceAssembly)
        {
            if (_savedLicenseKeys == null || _savedLicenseKeys[type.AssemblyQualifiedName!] == null)
            {
                _savedLicenseKeys ??= new Hashtable();
                resourceAssembly ??= Assembly.GetEntryAssembly();
 
                if (resourceAssembly == null)
                {
                    // If Assembly.EntryAssembly returns null, then we will
                    // try everything.
                    foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
                    {
                        // Assemblies loaded in memory return empty string from Location.
                        string location = asm.Location;
                        if (location == string.Empty)
                            continue;
 
                        string fileName = new FileInfo(location).Name;
 
                        Stream? s = asm.GetManifestResourceStream(fileName + ".licenses");
 
                        // Since the casing may be different depending on how the assembly was loaded,
                        // we'll do a case insensitive lookup for this manifest resource stream...
                        s ??= CaseInsensitiveManifestResourceStreamLookup(asm, fileName + ".licenses");
 
                        if (s != null)
                        {
                            DesigntimeLicenseContextSerializer.Deserialize(s, fileName.ToUpperInvariant(), this);
                            break;
                        }
                    }
                }
                else
                {
                    string location = resourceAssembly.Location;
                    if (location != string.Empty)
                    {
                        string fileName = Path.GetFileName(location);
                        string licResourceName = fileName + ".licenses";
 
                        // First try the filename
                        Stream? s = resourceAssembly.GetManifestResourceStream(licResourceName);
                        if (s == null)
                        {
                            string? resolvedName = null;
                            CompareInfo comparer = CultureInfo.InvariantCulture.CompareInfo;
                            string shortAssemblyName = resourceAssembly.GetName().Name!;
                            // If the assembly has been renamed, we try our best to find a good match in the available resources
                            // by looking at the assembly name (which doesn't change even after a file rename) + ".exe.licenses" or + ".dll.licenses"
                            foreach (string existingName in resourceAssembly.GetManifestResourceNames())
                            {
                                if (comparer.Compare(existingName, licResourceName, CompareOptions.IgnoreCase) == 0 ||
                                 comparer.Compare(existingName, shortAssemblyName + ".exe.licenses", CompareOptions.IgnoreCase) == 0 ||
                                 comparer.Compare(existingName, shortAssemblyName + ".dll.licenses", CompareOptions.IgnoreCase) == 0)
                                {
                                    resolvedName = existingName;
                                    break;
                                }
                            }
                            if (resolvedName != null)
                            {
                                s = resourceAssembly.GetManifestResourceStream(resolvedName);
                            }
                        }
                        if (s != null)
                        {
                            DesigntimeLicenseContextSerializer.Deserialize(s, fileName.ToUpperInvariant(), this);
                        }
                    }
                }
            }
            return (string?)_savedLicenseKeys[type.AssemblyQualifiedName!];
        }
 
        /**
        * Looks up a .licenses file in the assembly manifest using
        * case-insensitive lookup rules. We do this because the name
        * we are attempting to locate could have different casing
        * depending on how the assembly was loaded.
        **/
        private static Stream? CaseInsensitiveManifestResourceStreamLookup(Assembly satellite, string name)
        {
            CompareInfo comparer = CultureInfo.InvariantCulture.CompareInfo;
 
            // Loop through the resource names in the assembly.
            // We try to handle the case where the assembly file name has been renamed
            // by trying to guess the original file name based on the assembly name.
            string assemblyShortName = satellite.GetName().Name!;
            foreach (string existingName in satellite.GetManifestResourceNames())
            {
                if (comparer.Compare(existingName, name, CompareOptions.IgnoreCase) == 0 ||
                    comparer.Compare(existingName, assemblyShortName + ".exe.licenses") == 0 ||
                    comparer.Compare(existingName, assemblyShortName + ".dll.licenses") == 0)
                {
                    name = existingName;
                    break;
                }
            }
 
            // Finally, attempt to return our stream based on the
            // case insensitive match we found.
            return satellite.GetManifestResourceStream(name);
        }
    }
}