// -------------------------------------------------------------------------------------------------------------------- // // Copyright (c) James South. // Licensed under the Apache License, Version 2.0. // // // The remote image service. // // -------------------------------------------------------------------------------------------------------------------- namespace ImageProcessor.Web.Services { using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Security; using System.Threading.Tasks; using ImageProcessor.Web.Helpers; /// /// The remote image service. /// public class RemoteImageService : IImageService { /// /// The prefix for the given implementation. /// private string prefix = "remote.axd"; /// /// Initializes a new instance of the class. /// public RemoteImageService() { this.Settings = new Dictionary { { "MaxBytes", "4194304" }, { "Timeout", "30000" } }; this.WhiteList = new Uri[] { }; } /// /// Gets or sets the prefix for the given implementation. /// /// This value is used as a prefix for any image requests that should use this service. /// /// public string Prefix { get { return this.prefix; } set { this.prefix = value; } } /// /// Gets a value indicating whether the image service requests files from /// the locally based file system. /// public bool IsFileLocalService { get { return false; } } /// /// Gets or sets any additional settings required by the service. /// public Dictionary Settings { get; set; } /// /// Gets or sets the white list of . /// public Uri[] WhiteList { get; set; } /// /// Gets the image using the given identifier. /// /// /// The value identifying the image to fetch. /// /// /// The array containing the image data. /// public async Task GetImage(object id) { Uri uri = new Uri(id.ToString()); // Check the url is from a whitelisted location. this.CheckSafeUrlLocation(uri); RemoteFile remoteFile = new RemoteFile(uri) { MaxDownloadSize = int.Parse(this.Settings["MaxBytes"]), TimeoutLength = int.Parse(this.Settings["Timeout"]) }; byte[] buffer = { }; // Prevent response blocking. WebResponse webResponse = await remoteFile.GetWebResponseAsync().ConfigureAwait(false); using (MemoryStream memoryStream = new MemoryStream()) { using (WebResponse response = webResponse) { using (Stream responseStream = response.GetResponseStream()) { if (responseStream != null) { responseStream.CopyTo(memoryStream); // Reset the position of the stream to ensure we're reading the correct part. memoryStream.Position = 0; buffer = memoryStream.ToArray(); } } } } return buffer; } /// /// Returns a value indicating whether the current url is in a list of safe download locations. /// /// /// The to check against. /// private void CheckSafeUrlLocation(Uri url) { string upper = url.Host.ToUpperInvariant(); // Check for root or sub domain. bool validUrl = false; foreach (Uri uri in this.WhiteList) { if (!uri.IsAbsoluteUri) { Uri rebaseUri = new Uri("http://" + uri.ToString().TrimStart(new[] { '.', '/' })); validUrl = upper.StartsWith(rebaseUri.Host.ToUpperInvariant()) || upper.EndsWith(rebaseUri.Host.ToUpperInvariant()); } else { validUrl = upper.StartsWith(uri.Host.ToUpperInvariant()) || upper.EndsWith(uri.Host.ToUpperInvariant()); } if (validUrl) { break; } } if (!validUrl) { throw new SecurityException("Application is not configured to allow remote file downloads from this domain."); } } } }