// -------------------------------------------------------------------------------------------------------------------- // // Copyright (c) James South. // Licensed under the Apache License, Version 2.0. // // // Encapsulates methods to allow the retrieval of ImageProcessor settings. // // // -------------------------------------------------------------------------------------------------------------------- namespace ImageProcessor.Web.Configuration { using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Web; using System.Web.Compilation; using ImageProcessor.Common.Extensions; using ImageProcessor.Processors; using ImageProcessor.Web.Helpers; using ImageProcessor.Web.Processors; /// /// Encapsulates methods to allow the retrieval of ImageProcessor settings. /// /// public sealed class ImageProcessorConfiguration { #region Fields /// /// Whether the process is running in 64bit mode. Used for calling the correct dllimport method. /// Clunky I know but I couldn't get dynamic methods to work. /// private static readonly bool Is64Bit = Environment.Is64BitProcess; /// /// A new instance Initializes a new instance of the class. /// with lazy initialization. /// private static readonly Lazy Lazy = new Lazy(() => new ImageProcessorConfiguration()); /// /// A collection of the elements /// for available plugins. /// private static readonly ConcurrentDictionary> PluginSettings = new ConcurrentDictionary>(); /// /// A collection of the processing presets defined in the configuration. /// for available plugins. /// private static readonly ConcurrentDictionary PresetSettings = new ConcurrentDictionary(); /// /// The processing configuration section from the current application configuration. /// private static ImageProcessingSection imageProcessingSection; /// /// The cache configuration section from the current application configuration. /// private static ImageCacheSection imageCacheSection; /// /// The security configuration section from the current application configuration. /// private static ImageSecuritySection imageSecuritySection; #endregion #region Constructors /// /// Prevents a default instance of the class from being created. /// private ImageProcessorConfiguration() { this.EnsureNativeBinariesLoaded(); this.LoadGraphicsProcessors(); } #endregion #region Properties /// /// Gets the current instance of the class. /// public static ImageProcessorConfiguration Instance { get { return Lazy.Value; } } /// /// Gets the list of available GraphicsProcessors. /// public IList GraphicsProcessors { get; private set; } /// /// Gets a value indicating whether to preserve exif meta data. /// public bool PreserveExifMetaData { get { return GetImageProcessingSection().PreserveExifMetaData; } } #region Caching /// /// Gets the maximum number of days to store images in the cache. /// public int MaxCacheDays { get { return GetImageCacheSection().MaxDays; } } /// /// Gets or the virtual path of the cache folder. /// /// The virtual path of the cache folder. public string VirtualCachePath { get { return GetImageCacheSection().VirtualPath; } } #endregion #region Security /// /// Gets a list of white listed url[s] that images can be downloaded from. /// public Uri[] RemoteFileWhiteList { get { return GetImageSecuritySection().WhiteList.Cast().Select(x => x.Url).ToArray(); } } /// /// Gets a list of image extensions for url[s] with no extension. /// public ImageSecuritySection.SafeUrl[] RemoteFileWhiteListExtensions { get { return GetImageSecuritySection().WhiteList.Cast().ToArray(); } } /// /// Gets a value indicating whether the current application is allowed to download remote files. /// public bool AllowRemoteDownloads { get { return GetImageSecuritySection().AllowRemoteDownloads; } } /// /// Gets the maximum length to wait in milliseconds before throwing an error requesting a remote file. /// public int Timeout { get { return GetImageSecuritySection().Timeout; } } /// /// Gets the maximum allowable size in bytes of e remote file to process. /// public int MaxBytes { get { return GetImageSecuritySection().MaxBytes; } } /// /// Gets the remote prefix for external files for the application. /// public string RemotePrefix { get { return GetImageSecuritySection().RemotePrefix; } } #endregion #endregion #region Methods /// /// Returns the processing instructions matching the preset defined in the configuration. /// /// /// The name of the plugin to get the settings for. /// /// /// The the processing instructions. /// public string GetPresetSettings(string name) { return PresetSettings.GetOrAdd( name, n => { ImageProcessingSection.PresetElement presetElement = GetImageProcessingSection() .Presets .Cast() .FirstOrDefault(x => x.Name == n); return presetElement != null ? presetElement.Value : null; }); } /// /// Returns the for the given plugin. /// /// /// The name of the plugin to get the settings for. /// /// /// The for the given plugin. /// public Dictionary GetPluginSettings(string name) { return PluginSettings.GetOrAdd( name, n => { ImageProcessingSection.PluginElement pluginElement = GetImageProcessingSection() .Plugins .Cast() .FirstOrDefault(x => x.Name == n); Dictionary settings; if (pluginElement != null) { settings = pluginElement.Settings .Cast() .ToDictionary(setting => setting.Key, setting => setting.Value); } else { settings = new Dictionary(); } return settings; }); } /// /// Retrieves the processing configuration section from the current application configuration. /// /// The processing configuration section from the current application configuration. private static ImageProcessingSection GetImageProcessingSection() { return imageProcessingSection ?? (imageProcessingSection = ImageProcessingSection.GetConfiguration()); } /// /// Retrieves the caching configuration section from the current application configuration. /// /// The caching configuration section from the current application configuration. private static ImageCacheSection GetImageCacheSection() { return imageCacheSection ?? (imageCacheSection = ImageCacheSection.GetConfiguration()); } /// /// Retrieves the security configuration section from the current application configuration. /// /// The security configuration section from the current application configuration. private static ImageSecuritySection GetImageSecuritySection() { return imageSecuritySection ?? (imageSecuritySection = ImageSecuritySection.GetConfiguration()); } /// /// Gets the list of available GraphicsProcessors. /// private void LoadGraphicsProcessors() { if (this.GraphicsProcessors == null) { if (GetImageProcessingSection().Plugins.AutoLoadPlugins) { Type type = typeof(IWebGraphicsProcessor); try { // Build a list of native IGraphicsProcessor instances. List availableTypes = BuildManager.GetReferencedAssemblies() .Cast() .SelectMany(s => s.GetLoadableTypes()) .Where(t => type.IsAssignableFrom(t) && t.IsClass && !t.IsAbstract) .ToList(); // Create them and add. this.GraphicsProcessors = availableTypes.Select(x => (Activator.CreateInstance(x) as IWebGraphicsProcessor)).ToList(); // Add the available settings. foreach (IWebGraphicsProcessor webProcessor in this.GraphicsProcessors) { webProcessor.Processor.Settings = this.GetPluginSettings(webProcessor.GetType().Name); } } catch (ReflectionTypeLoadException) { this.LoadGraphicsProcessorsFromConfiguration(); } } else { this.LoadGraphicsProcessorsFromConfiguration(); } } } /// /// Loads graphics processors from configuration. /// /// /// Thrown when an cannot be loaded. /// private void LoadGraphicsProcessorsFromConfiguration() { ImageProcessingSection.PluginElementCollection pluginConfigs = imageProcessingSection.Plugins; this.GraphicsProcessors = new List(); foreach (ImageProcessingSection.PluginElement pluginConfig in pluginConfigs) { Type type = Type.GetType(pluginConfig.Type); if (type == null) { throw new TypeLoadException("Couldn't load IWebGraphicsProcessor: " + pluginConfig.Type); } this.GraphicsProcessors.Add(Activator.CreateInstance(type) as IWebGraphicsProcessor); } // Add the available settings. foreach (IWebGraphicsProcessor webProcessor in this.GraphicsProcessors) { webProcessor.Processor.Settings = this.GetPluginSettings(webProcessor.GetType().Name); } } /// /// Ensures that the native binaries are loaded. /// private void EnsureNativeBinariesLoaded() { string binary = Is64Bit ? "libwebp64.dll" : "libwebp32.dll"; string sourcePath = HttpContext.Current.Server.MapPath("~/bin"); string targetPath = new Uri(Assembly.GetExecutingAssembly().Location).LocalPath; IntPtr pointer = IntPtr.Zero; // Shadow copy the native binaries. sourcePath = Path.Combine(sourcePath, binary); targetPath = Path.GetFullPath(Path.Combine(targetPath, "..\\" + binary)); File.Copy(sourcePath, targetPath, true); try { // Load the binary into memory. pointer = NativeMethods.LoadLibrary(targetPath); } catch (Exception ex) { Debug.WriteLine(ex.Message); } if (pointer == IntPtr.Zero) { throw new ApplicationException("Cannot open " + binary); } } #endregion } }