Try your search with a different keyword or use * as a wildcard.
using System.Reflection;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Nop.Core;
using Nop.Core.ComponentModel;
using Nop.Core.Configuration;
using Nop.Core.Infrastructure;
using Nop.Data.Mapping;
using Nop.Services.Plugins;
namespace Nop.Web.Framework.Infrastructure.Extensions;
///
/// Represents application part manager extensions
///
public static partial class ApplicationPartManagerExtensions
{
#region Fields
private static readonly INopFileProvider _fileProvider;
private static readonly List> _baseAppLibraries;
private static readonly Dictionary _pluginLibraries;
private static readonly Dictionary _loadedAssemblies = new();
private static readonly ReaderWriterLockSlim _locker = new();
#endregion
#region Ctor
static ApplicationPartManagerExtensions()
{
//we use the default file provider, since the DI isn't initialized yet
_fileProvider = CommonHelper.DefaultFileProvider;
_baseAppLibraries = new List>();
_pluginLibraries = new Dictionary();
//get all libraries from /bin/{version}/ directory
foreach (var file in _fileProvider.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll"))
_baseAppLibraries.Add(new KeyValuePair(_fileProvider.GetFileName(file), GetAssemblyVersion(file)));
//get all libraries from base site directory
if (!AppDomain.CurrentDomain.BaseDirectory.Equals(Environment.CurrentDirectory, StringComparison.InvariantCultureIgnoreCase))
foreach (var file in _fileProvider.GetFiles(Environment.CurrentDirectory, "*.dll"))
_baseAppLibraries.Add(new KeyValuePair(_fileProvider.GetFileName(file), GetAssemblyVersion(file)));
//get all libraries from refs directory
var refsPathName = _fileProvider.Combine(Environment.CurrentDirectory, NopPluginDefaults.RefsPathName);
if (_fileProvider.DirectoryExists(refsPathName))
foreach (var file in _fileProvider.GetFiles(refsPathName, "*.dll"))
_baseAppLibraries.Add(new KeyValuePair(_fileProvider.GetFileName(file), GetAssemblyVersion(file)));
}
#endregion
#region Properties
///
/// Gets access to information about plugins
///
private static IPluginsInfo PluginsInfo
{
get => Singleton.Instance;
set => Singleton.Instance = value;
}
#endregion
#region Utilities
private static Version GetAssemblyVersion(string filePath)
{
try
{
return AssemblyName.GetAssemblyName(filePath).Version;
}
catch (BadImageFormatException)
{
//ignore
}
return null;
}
private static void CheckCompatible(PluginDescriptor pluginDescriptor, IDictionary assemblies)
{
var refFiles = pluginDescriptor.PluginFiles.Where(file =>
!_fileProvider.GetFileName(file).Equals(_fileProvider.GetFileName(pluginDescriptor.OriginalAssemblyFile))).ToList();
foreach (var refFile in refFiles.Where(file =>
assemblies.ContainsKey(_fileProvider.GetFileName(file).ToLower())))
IsAlreadyLoaded(refFile, pluginDescriptor.SystemName);
var hasCollisions = _loadedAssemblies.Where(p =>
p.Value.References.Any(r => r.PluginName.Equals(pluginDescriptor.SystemName)))
.Any(p => p.Value.Collisions.Any());
if (hasCollisions)
{
PluginsInfo.IncompatiblePlugins.Add(pluginDescriptor.SystemName, PluginIncompatibleType.HasCollisions);
PluginsInfo.PluginDescriptors.Remove((pluginDescriptor, false));
}
}
///
/// Load and register the assembly
///
/// Application part manager
/// Path to the assembly file
/// Indicating whether to load an assembly into the load-from context, bypassing some security checks
/// Assembly
private static Assembly AddApplicationParts(ApplicationPartManager applicationPartManager, string assemblyFile, bool useUnsafeLoadAssembly)
{
//try to load a assembly
Assembly assembly;
try
{
assembly = Assembly.LoadFrom(assemblyFile);
}
catch (FileLoadException)
{
if (useUnsafeLoadAssembly)
{
//if an application has been copied from the web, it is flagged by Windows as being a web application,
//even if it resides on the local computer.You can change that designation by changing the file properties,
//or you can use the element to grant the assembly full trust.As an alternative,
//you can use the UnsafeLoadFrom method to load a local assembly that the operating system has flagged as
//having been loaded from the web.
//see http://go.microsoft.com/fwlink/?LinkId=155569 for more information.
assembly = Assembly.UnsafeLoadFrom(assemblyFile);
}
else
throw;
}
//register the plugin definition
applicationPartManager.ApplicationParts.Add(new AssemblyPart(assembly));
return assembly;
}
///
/// Perform file deploy and return loaded assembly
///
/// Application part manager
/// Path to the plugin assembly file
/// Plugin config
/// Nop file provider
/// Assembly
private static Assembly PerformFileDeploy(this ApplicationPartManager applicationPartManager,
string assemblyFile, PluginConfig pluginConfig, INopFileProvider fileProvider)
{
//ensure for proper directory structure
if (string.IsNullOrEmpty(assemblyFile) ||
string.IsNullOrEmpty(fileProvider.GetParentDirectory(assemblyFile)))
throw new InvalidOperationException(
$"The plugin directory for the {fileProvider.GetFileName(assemblyFile)} file exists in a directory outside of the allowed nopCommerce directory hierarchy");
var assembly =
AddApplicationParts(applicationPartManager, assemblyFile, pluginConfig.UseUnsafeLoadAssembly);
// delete the .deps file
if (assemblyFile.EndsWith(".dll"))
_fileProvider.DeleteFile(assemblyFile[0..^4] + ".deps.json");
if (!_pluginLibraries.ContainsKey(fileProvider.GetFileName(assemblyFile)))
_pluginLibraries.Add(fileProvider.GetFileName(assemblyFile), assembly.GetName().Version);
return assembly;
}
///
/// Check whether the assembly is already loaded
///
/// Assembly file path
/// Plugin system name
/// Result of check
private static bool IsAlreadyLoaded(string filePath, string pluginName)
{
//ignore already loaded libraries
//(we do it because not all libraries are loaded immediately after application start)
var fileName = _fileProvider.GetFileName(filePath);
if (_baseAppLibraries.Any(library => library.Key.Equals(fileName, StringComparison.InvariantCultureIgnoreCase)))
return true;
try
{
//get filename without extension
var fileNameWithoutExtension = _fileProvider.GetFileNameWithoutExtension(filePath);
if (string.IsNullOrEmpty(fileNameWithoutExtension))
throw new Exception($"Cannot get file extension for {fileName}");
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
//compare assemblies by file names
var assemblyName = (assembly.FullName ?? string.Empty).Split(',').FirstOrDefault();
if (!fileNameWithoutExtension.Equals(assemblyName, StringComparison.InvariantCultureIgnoreCase))
continue;
//loaded assembly not found
if (!_loadedAssemblies.TryGetValue(assemblyName, out var pluginLoadedAssemblyInfo))
{
//add it to the list to find collisions later
pluginLoadedAssemblyInfo = new PluginLoadedAssemblyInfo(assemblyName, GetAssemblyVersion(assembly.Location));
_loadedAssemblies.Add(assemblyName, pluginLoadedAssemblyInfo);
}
//set assembly name and plugin name for further using
pluginLoadedAssemblyInfo.References.Add((pluginName, GetAssemblyVersion(filePath)));
return true;
}
}
catch
{
// ignored
}
//nothing found
return false;
}
#endregion
#region Methods
///
/// Initialize plugins system
///
/// Application part manager
/// Plugin config
public static void InitializePlugins(this ApplicationPartManager applicationPartManager, PluginConfig pluginConfig)
{
ArgumentNullException.ThrowIfNull(applicationPartManager);
ArgumentNullException.ThrowIfNull(pluginConfig);
//perform with locked access to resources
using (new ReaderWriteLockDisposable(_locker))
{
try
{
//ensure plugins directory is created
var pluginsDirectory = _fileProvider.MapPath(NopPluginDefaults.Path);
_fileProvider.CreateDirectory(pluginsDirectory);
//ensure uploaded directory is created
var uploadedPath = _fileProvider.MapPath(NopPluginDefaults.UploadedPath);
_fileProvider.CreateDirectory(uploadedPath);
foreach (var directory in _fileProvider.GetDirectories(uploadedPath))
{
var moveTo = _fileProvider.Combine(pluginsDirectory, _fileProvider.GetDirectoryNameOnly(directory));
if (_fileProvider.DirectoryExists(moveTo))
_fileProvider.DeleteDirectory(moveTo);
_fileProvider.DirectoryMove(directory, moveTo);
}
PluginsInfo = new PluginsInfo(_fileProvider);
PluginsInfo.LoadPluginInfo();
foreach (var pluginDescriptor in PluginsInfo.PluginDescriptors.Where(p => p.needToDeploy)
.Select(p => p.pluginDescriptor))
{
var mainPluginFile = pluginDescriptor.OriginalAssemblyFile;
//try to deploy main plugin assembly
pluginDescriptor.ReferencedAssembly =
applicationPartManager.PerformFileDeploy(mainPluginFile, pluginConfig, _fileProvider);
//and then deploy all other referenced assemblies
var filesToDeploy = pluginDescriptor.PluginFiles.Where(file =>
!_fileProvider.GetFileName(file).Equals(_fileProvider.GetFileName(mainPluginFile)) &&
!IsAlreadyLoaded(file, pluginDescriptor.SystemName)).ToList();
foreach (var file in filesToDeploy)
applicationPartManager.PerformFileDeploy(file, pluginConfig, _fileProvider);
//determine a plugin type (only one plugin per assembly is allowed)
var pluginType = pluginDescriptor.ReferencedAssembly.GetTypes().FirstOrDefault(type =>
typeof(IPlugin).IsAssignableFrom(type) && !type.IsInterface && type.IsClass &&
!type.IsAbstract);
if (pluginType != default)
pluginDescriptor.PluginType = pluginType;
}
var assemblies = _baseAppLibraries.ToList();
foreach (var pluginLoadedAssemblyInfo in _loadedAssemblies)
assemblies.Add(new KeyValuePair(pluginLoadedAssemblyInfo.Key, pluginLoadedAssemblyInfo.Value.AssemblyInMemory));
foreach (var pluginLibrary in _pluginLibraries.Where(item => !assemblies.Any(p => p.Key.Equals(item.Key, StringComparison.InvariantCultureIgnoreCase))).ToList())
assemblies.Add(new KeyValuePair(pluginLibrary.Key, pluginLibrary.Value));
var inMemoryAssemblies = assemblies.GroupBy(p => p.Key).Select(p => p.First())
.ToDictionary(p => p.Key.ToLower(), p => p.Value);
foreach (var pluginDescriptor in PluginsInfo.PluginDescriptors.Where(p => !p.needToDeploy)
.Select(p => p.pluginDescriptor).ToList())
CheckCompatible(pluginDescriptor, inMemoryAssemblies);
}
catch (Exception exception)
{
//throw full exception
var message = string.Empty;
for (var inner = exception; inner != null; inner = inner.InnerException)
message = $"{message}{inner.Message}{Environment.NewLine}";
throw new Exception(message, exception);
}
PluginsInfo.AssemblyLoadedCollision = _loadedAssemblies.Select(item => item.Value)
.Where(loadedAssemblyInfo => loadedAssemblyInfo.Collisions.Any()).ToList();
//add name compatibility types from plugins
var nameCompatibilityList = PluginsInfo.PluginDescriptors.Where(pd => pd.pluginDescriptor.Installed).SelectMany(pd => pd
.pluginDescriptor.ReferencedAssembly.GetTypes().Where(type =>
typeof(INameCompatibility).IsAssignableFrom(type) && !type.IsInterface && type.IsClass &&
!type.IsAbstract));
NameCompatibilityManager.AdditionalNameCompatibilities.AddRange(nameCompatibilityList);
}
}
#endregion
}