// --------------------------------------------------------------------------------------------------------------------
//
// 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.");
}
}
}
}