// --------------------------------------------------------------------------------------------------------------------
//
// 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
}
}