Fixes up security issues with MediaUploadController, removes Delete since we shouldn't be arbitrarily deleting any media files without referencing media items, fixes up disposables and naming conventions.

This commit is contained in:
Shannon
2013-08-28 10:15:43 +10:00
parent 0cb084b9d1
commit f03180e6f8

View File

@@ -7,12 +7,15 @@ using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Runtime.Serialization;
using System.Web;
using System.Web.Http;
using System.Web.Script.Serialization;
using Umbraco.Core;
using Umbraco.Web.Mvc;
using Umbraco.Web.WebApi;
using Umbraco.Web.WebApi.Filters;
using Constants = Umbraco.Core.Constants;
namespace Umbraco.Web.Editors
{
@@ -22,242 +25,265 @@ namespace Umbraco.Web.Editors
*/
[PluginController("UmbracoApi")]
public class MediaUploadController : UmbracoApiController
[UmbracoApplicationAuthorizeAttribute(Constants.Applications.Media)]
public class MediaUploadController : UmbracoAuthorizedApiController
{
private readonly JavaScriptSerializer _js = new JavaScriptSerializer { MaxJsonLength = 41943040 };
private readonly string _storageRoot = Core.IO.IOHelper.MapPath(Core.IO.SystemDirectories.Media);
#region Get
private HttpResponseMessage DownloadFileContent()
{
private readonly JavaScriptSerializer _js = new JavaScriptSerializer { MaxJsonLength = 41943040 };
private readonly string _storageRoot = Core.IO.IOHelper.MapPath( Core.IO.SystemDirectories.Media );
public bool _isReusable { get { return false; } }
public string Meh()
var filename = HttpContext.Current.Request["f"];
var filePath = _storageRoot + filename;
if (File.Exists(filePath))
{
return "wo";
}
#region Get
private HttpResponseMessage DownloadFileContent()
{
var filename = HttpContext.Current.Request["f"];
var filePath = _storageRoot + filename;
if (File.Exists(filePath))
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StreamContent(new FileStream(filePath, FileMode.Open, FileAccess.Read));
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StreamContent(new FileStream(filePath, FileMode.Open, FileAccess.Read));
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = filename
};
return response;
}
return ControllerContext.Request.CreateErrorResponse(HttpStatusCode.NotFound, "");
}
private HttpResponseMessage DownloadFileList()
{
var files =
new DirectoryInfo(_storageRoot)
.GetFiles("*", SearchOption.TopDirectoryOnly)
.Where(f => !f.Attributes.HasFlag(FileAttributes.Hidden))
.Select(f => new FilesStatus(f))
.ToArray();
HttpContext.Current.Response.AppendHeader("Content-Disposition", "inline; filename=\"files.json\"");
return ControllerContext.Request.CreateResponse(HttpStatusCode.OK, _js.Serialize(files));
}
public HttpResponseMessage Get()
{
if (!string.IsNullOrEmpty(HttpContext.Current.Request["f"]))
{
return DownloadFileContent();
}
return DownloadFileList();
}
#endregion
#region Post & Put
public HttpResponseMessage Post()
{
return UploadFile(HttpContext.Current);
}
public HttpResponseMessage Put()
{
return UploadFile(HttpContext.Current);
}
private HttpResponseMessage UploadFile(HttpContext context)
{
var statuses = new List<FilesStatus>();
var headers = context.Request.Headers;
if (string.IsNullOrEmpty(headers["X-File-Name"]))
{
UploadWholeFile(context, statuses);
}
else
{
UploadPartialFile(headers["X-File-Name"], context, statuses);
}
return WriteJsonIframeSafe(context, statuses);
}
private HttpResponseMessage WriteJsonIframeSafe(HttpContext context, List<FilesStatus> statuses)
{
context.Response.AddHeader("Vary", "Accept");
var response = new HttpResponseMessage()
{
Content = new StringContent(_js.Serialize(statuses.ToArray()))
FileName = filename
};
if (context.Request["HTTP_ACCEPT"].Contains("application/json"))
{
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
}
else
{
response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/plain");
}
return response;
}
// Upload partial file
private void UploadPartialFile(string fileName, HttpContext context, List<FilesStatus> statuses)
{
if (context.Request.Files.Count != 1) throw new HttpRequestValidationException("Attempt to upload chunked file containing more than one fragment per request");
var inputStream = context.Request.Files[0].InputStream;
var fullName = _storageRoot + Path.GetFileName(fileName);
using (var fs = new FileStream(fullName, FileMode.Append, FileAccess.Write))
{
var buffer = new byte[1024];
var l = inputStream.Read(buffer, 0, 1024);
while (l > 0)
{
fs.Write(buffer, 0, l);
l = inputStream.Read(buffer, 0, 1024);
}
fs.Flush();
fs.Close();
}
statuses.Add(new FilesStatus(new FileInfo(fullName)));
}
// Upload entire file
private void UploadWholeFile(HttpContext context, List<FilesStatus> statuses)
{
var folderId = context.Request.Form["currentFolder"];
for (int i = 0; i < context.Request.Files.Count; i++)
{
var file = context.Request.Files[i];
string fullPath = _storageRoot + Path.GetFileName(file.FileName);
Directory.CreateDirectory(_storageRoot);
file.SaveAs(fullPath);
string fullName = Path.GetFileName(file.FileName);
statuses.Add(new FilesStatus(fullName, file.ContentLength, fullPath));
}
}
#endregion
#region Delete
public HttpResponseMessage Delete()
{
var filePath = _storageRoot + HttpContext.Current.Request["f"];
if (File.Exists(filePath))
{
File.Delete(filePath);
}
return ControllerContext.Request.CreateResponse(HttpStatusCode.OK, "");
}
#endregion
return ControllerContext.Request.CreateErrorResponse(HttpStatusCode.NotFound, "");
}
#region FileStatus
public class FilesStatus
private HttpResponseMessage DownloadFileList()
{
public const string HandlerPath = "/";
var files =
new DirectoryInfo(_storageRoot)
.GetFiles("*", SearchOption.TopDirectoryOnly)
.Where(f => f.Attributes.HasFlag(FileAttributes.Hidden) == false)
.Select(f => new FilesStatus(f))
.ToArray();
HttpContext.Current.Response.AppendHeader("Content-Disposition", "inline; filename=\"files.json\"");
return ControllerContext.Request.CreateResponse(HttpStatusCode.OK, _js.Serialize(files));
}
public string group { get; set; }
public string name { get; set; }
public string type { get; set; }
public int size { get; set; }
public string progress { get; set; }
public string url { get; set; }
public string thumbnail_url { get; set; }
public string delete_url { get; set; }
public string delete_type { get; set; }
public string error { get; set; }
public FilesStatus()
public HttpResponseMessage Get()
{
var http = TryGetHttpContext();
if (http.Success == false)
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}
public FilesStatus(FileInfo fileInfo)
if (string.IsNullOrEmpty(http.Result.Request["f"]) == false)
{
SetValues(fileInfo.Name, (int)fileInfo.Length, fileInfo.FullName);
return DownloadFileContent();
}
return DownloadFileList();
}
#endregion
#region Post & Put
public HttpResponseMessage Post()
{
var http = TryGetHttpContext();
return http.Success == false
? Request.CreateResponse(HttpStatusCode.NotFound)
: UploadFile(http.Result);
}
public HttpResponseMessage Put()
{
var http = TryGetHttpContext();
return http.Success == false
? Request.CreateResponse(HttpStatusCode.NotFound)
: UploadFile(http.Result);
}
private HttpResponseMessage UploadFile(HttpContextBase context)
{
var statuses = new List<FilesStatus>();
var headers = context.Request.Headers;
if (string.IsNullOrEmpty(headers["X-File-Name"]))
{
UploadWholeFile(context, statuses);
}
else
{
UploadPartialFile(headers["X-File-Name"], context, statuses);
}
public FilesStatus(string fileName, int fileLength, string fullPath)
{
SetValues(fileName, fileLength, fullPath);
}
return WriteJsonIframeSafe(context, statuses);
}
private void SetValues(string fileName, int fileLength, string fullPath)
private HttpResponseMessage WriteJsonIframeSafe(HttpContextBase context, List<FilesStatus> statuses)
{
context.Response.AddHeader("Vary", "Accept");
var response = new HttpResponseMessage()
{
name = fileName;
type = "image/png";
size = fileLength;
progress = "1.0";
url = HandlerPath + "api/Upload?f=" + fileName;
delete_url = HandlerPath + "api/Upload?f=" + fileName;
delete_type = "DELETE";
var ext = Path.GetExtension(fullPath);
var fileSize = ConvertBytesToMegabytes(new FileInfo(fullPath).Length);
if (fileSize > 3 || !IsImage(ext))
Content = new StringContent(_js.Serialize(statuses.ToArray()))
};
if (context.Request["HTTP_ACCEPT"].Contains("application/json"))
{
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
}
else
{
response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/plain");
}
return response;
}
// Upload partial file
private void UploadPartialFile(string fileName, HttpContextBase context, List<FilesStatus> statuses)
{
if (context.Request.Files.Count != 1) throw new HttpRequestValidationException("Attempt to upload chunked file containing more than one fragment per request");
var inputStream = context.Request.Files[0].InputStream;
var fullName = _storageRoot + Path.GetFileName(fileName);
using (var fs = new FileStream(fullName, FileMode.Append, FileAccess.Write))
{
var buffer = new byte[1024];
var l = inputStream.Read(buffer, 0, 1024);
while (l > 0)
{
thumbnail_url = "/Content/img/generalFile.png";
}
else
{
thumbnail_url = @"data:image/png;base64," + EncodeFile(fullPath);
fs.Write(buffer, 0, l);
l = inputStream.Read(buffer, 0, 1024);
}
fs.Flush();
fs.Close();
}
statuses.Add(new FilesStatus(new FileInfo(fullName)));
}
private bool IsImage(string ext)
{
return ext == ".gif" || ext == ".jpg" || ext == ".png";
}
// Upload entire file
private void UploadWholeFile(HttpContextBase context, List<FilesStatus> statuses)
{
var folderId = context.Request.Form["currentFolder"];
private string EncodeFile(string fileName)
{
byte[] bytes;
using (Image image = Image.FromFile(fileName))
{
var ratioX = (double)80 / image.Width;
var ratioY = (double)80 / image.Height;
var ratio = Math.Min(ratioX, ratioY);
var newWidth = (int)(image.Width * ratio);
var newHeight = (int)(image.Height * ratio);
var newImage = new Bitmap(newWidth, newHeight);
Graphics.FromImage(newImage).DrawImage(image, 0, 0, newWidth, newHeight);
ImageConverter converter = new ImageConverter();
bytes = (byte[])converter.ConvertTo(newImage, typeof(byte[]));
newImage.Dispose();
}
return Convert.ToBase64String(bytes);
}
private static double ConvertBytesToMegabytes(long bytes)
for (int i = 0; i < context.Request.Files.Count; i++)
{
return (bytes / 1024f) / 1024f;
var file = context.Request.Files[i];
string fullPath = _storageRoot + Path.GetFileName(file.FileName);
Directory.CreateDirectory(_storageRoot);
file.SaveAs(fullPath);
string fullName = Path.GetFileName(file.FileName);
statuses.Add(new FilesStatus(fullName, file.ContentLength, fullPath));
}
}
#endregion
}
#region FileStatus
[DataContract(Name = "fileStatus", Namespace = "")]
internal class FilesStatus
{
public const string HandlerPath = "/";
[DataMember(Name = "group")]
public string Group { get; set; }
[DataMember(Name = "name")]
public string Name { get; set; }
[DataMember(Name = "type")]
public string Type { get; set; }
[DataMember(Name = "size")]
public int Size { get; set; }
[DataMember(Name = "progress")]
public string Progress { get; set; }
[DataMember(Name = "url")]
public string Url { get; set; }
[DataMember(Name = "thumbnail_url")]
public string ThumbnailUrl { get; set; }
[DataMember(Name = "delete_url")]
public string DeleteUrl { get; set; }
[DataMember(Name = "delete_type")]
public string DeleteType { get; set; }
[DataMember(Name = "error")]
public string Error { get; set; }
public FilesStatus()
{
}
public FilesStatus(FileInfo fileInfo)
{
SetValues(fileInfo.Name, (int)fileInfo.Length, fileInfo.FullName);
}
public FilesStatus(string fileName, int fileLength, string fullPath)
{
SetValues(fileName, fileLength, fullPath);
}
private void SetValues(string fileName, int fileLength, string fullPath)
{
Name = fileName;
Type = "image/png";
Size = fileLength;
Progress = "1.0";
Url = HandlerPath + "api/Upload?f=" + fileName;
DeleteUrl = HandlerPath + "api/Upload?f=" + fileName;
DeleteType = "DELETE";
var ext = Path.GetExtension(fullPath);
var fileSize = ConvertBytesToMegabytes(new FileInfo(fullPath).Length);
if (fileSize > 3 || !IsImage(ext))
{
ThumbnailUrl = "/Content/img/generalFile.png";
}
else
{
ThumbnailUrl = @"data:image/png;base64," + EncodeFile(fullPath);
}
}
private static bool IsImage(string ext)
{
return ext.InvariantEquals(".gif") || ext.InvariantEquals(".jpg") || ext.InvariantEquals(".png");
}
private static string EncodeFile(string fileName)
{
byte[] bytes = null;
using (var image = Image.FromFile(fileName))
{
var ratioX = (double)80 / image.Width;
var ratioY = (double)80 / image.Height;
var ratio = Math.Min(ratioX, ratioY);
var newWidth = (int)(image.Width * ratio);
var newHeight = (int)(image.Height * ratio);
using (var newImage = new Bitmap(newWidth, newHeight))
{
using (var g = Graphics.FromImage(newImage))
{
g.DrawImage(image, 0, 0, newWidth, newHeight);
var converter = new ImageConverter();
bytes = (byte[])converter.ConvertTo(newImage, typeof(byte[]));
}
}
}
if (bytes == null)
{
throw new InvalidOperationException("Could not read the bytes from the file as an image");
}
return Convert.ToBase64String(bytes);
}
private static double ConvertBytesToMegabytes(long bytes)
{
return (bytes / 1024f) / 1024f;
}
}
#endregion
}