Bladeren bron

Added method to make uploads over a certain size direct to the download page and force a SaveAs dialog. This is to prevent non-user uploads from being spammed to host large videos/embedded data.

tags/3.0.0
Teknikode 2 jaren geleden
bovenliggende
commit
ce0ded7c55

+ 52
- 10
Teknik/Areas/Upload/Controllers/UploadController.cs Bestand weergeven

@@ -142,6 +142,7 @@ namespace Teknik.Areas.Upload.Controllers
string iv = string.Empty;
string contentType = string.Empty;
long contentLength = 0;
bool userUploaded = false;
DateTime dateUploaded = new DateTime();

using (TeknikEntities db = new TeknikEntities())
@@ -160,6 +161,7 @@ namespace Teknik.Areas.Upload.Controllers
contentType = uploads.ContentType;
contentLength = uploads.ContentLength;
dateUploaded = uploads.DateUploaded;
userUploaded = uploads.User != null;
}
else
{
@@ -176,6 +178,19 @@ namespace Teknik.Areas.Upload.Controllers
model.ContentType = contentType;
model.ContentLength = contentLength;
model.IV = iv;
model.Decrypt = true;

return View(model);
}
else if (!userUploaded && Config.UploadConfig.MaxDownloadSize < contentLength)
{
// We want to force them to the dl page due to them being over the max download size for embedded content
DownloadViewModel model = new DownloadViewModel();
model.CurrentSub = Subdomain;
model.FileName = file;
model.ContentType = contentType;
model.ContentLength = contentLength;
model.Decrypt = false;

return View(model);
}
@@ -296,9 +311,6 @@ namespace Teknik.Areas.Upload.Controllers

Response.AddHeader("Content-Disposition", cd.ToString());

// Apply content security policy for downloads
//Response.AddHeader("Content-Security-Policy", "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self'; font-src *; connect-src 'self'; media-src 'self'; child-src 'self'; form-action 'none';");

// Read in the file
FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);

@@ -341,7 +353,7 @@ namespace Teknik.Areas.Upload.Controllers

[HttpPost]
[AllowAnonymous]
public FileResult DownloadData(string file)
public ActionResult DownloadData(string file, bool decrypt)
{
if (Config.UploadConfig.DownloadEnabled)
{
@@ -354,16 +366,46 @@ namespace Teknik.Areas.Upload.Controllers
string filePath = Path.Combine(Config.UploadConfig.UploadDirectory, subDir, upload.FileName);
if (System.IO.File.Exists(filePath))
{
FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
return File(fileStream, System.Net.Mime.MediaTypeNames.Application.Octet, file);
// Notify the client the content length we'll be outputting
Response.AddHeader("Content-Length", upload.ContentLength.ToString());

// Create content disposition
var cd = new System.Net.Mime.ContentDisposition
{
FileName = upload.Url,
Inline = true
};

Response.AddHeader("Content-Disposition", cd.ToString());

// Read in the file
FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);

// If the IV is set, and Key is set, then decrypt it while sending
if (decrypt && !string.IsNullOrEmpty(upload.Key) && !string.IsNullOrEmpty(upload.IV))
{
byte[] keyBytes = Encoding.UTF8.GetBytes(upload.Key);
byte[] ivBytes = Encoding.UTF8.GetBytes(upload.IV);

return new FileGenerateResult(upload.Url,
upload.ContentType,
(response) => ResponseHelper.StreamToOutput(response, true, new AesCounterStream(fs, false, keyBytes, ivBytes), (int)upload.ContentLength, Config.UploadConfig.ChunkSize),
false);
}
else // Otherwise just send it
{
// Send the file
return new FileGenerateResult(upload.Url,
upload.ContentType,
(response) => ResponseHelper.StreamToOutput(response, true, fs, (int)upload.ContentLength, Config.UploadConfig.ChunkSize),
false);
}
}
}
Redirect(Url.SubRouteUrl("error", "Error.Http404"));
return null;
return Json(new { error = new { message = "File Does Not Exist" } });
}
}
Redirect(Url.SubRouteUrl("error", "Error.Http403"));
return null;
return Json(new { error = new { message = "Downloads are disabled" } });
}

[HttpGet]

+ 114
- 67
Teknik/Areas/Upload/Scripts/Download.js Bestand weergeven

@@ -1,42 +1,81 @@
$(document).ready(downloadFile);
$(document).ready(downloadFile);

function downloadFile() {
var key = window.location.hash.substring(1);
if (key == null || key == '') {
if (decrypt && (key == null || key == '')) {
bootbox.prompt("Enter the file's private key", function (result) {
if (result) {
key = result;
}
processDownload(key);
processDownload(key, iv, decrypt);
});
}
else {
processDownload(key);
processDownload(key, iv, decrypt);
}
}

function processDownload(key) {
if (key !== null && key !== '' && iv !== null && iv !== '') {
// speed info
var lastTime = (new Date()).getTime();
var lastData = 0;
function processDownload(key, iv, decrypt) {
// speed info
var startTime = (new Date()).getTime();

var fd = new FormData();
fd.append('file', fileName);
fd.append('decrypt', !decrypt);
fd.append('__RequestVerificationToken', $('#__AjaxAntiForgeryForm input[name=__RequestVerificationToken]').val());

var xhr = new XMLHttpRequest();
xhr.open('POST', downloadDataUrl, true);
xhr.responseType = 'arraybuffer';

xhr.onload = function(e) {
if (this.status === 200) {
decryptDownload(this.response, key, iv, decrypt);
}
};

xhr.onprogress = function (e) {
if (e.lengthComputable) {
var curTime = (new Date()).getTime();
var elapsedTime = (curTime - startTime) / 1000;
var speed = (e.loaded / elapsedTime);
var percentComplete = Math.round(e.loaded * 100 / e.total);
setProgress(percentComplete,
'progress-bar-success progress-bar-striped active',
percentComplete + '%',
'Downloading File [' +
getReadableFileSizeString(e.loaded) +
' / ' +
getReadableFileSizeString(e.total) +
' @ ' +
getReadableBandwidthString(speed * 8) +
']');
}
}

xhr.onerror = function(e) {
setProgress(100, 'progress-bar-danger', '', 'Download Failed');
};

var fd = new FormData();
fd.append('file', fileName);
fd.append('__RequestVerificationToken', $('#__AjaxAntiForgeryForm input[name=__RequestVerificationToken]').val());
xhr.onabort = function(e) {
setProgress(100, 'progress-bar-warning', '', 'Download Aborted');
};

var xhr = new XMLHttpRequest();
xhr.open('POST', downloadDataUrl, true);
xhr.responseType = 'arraybuffer';
xhr.send(fd);
}

xhr.onload = function (e) {
if (this.status == 200) {
lastTime = (new Date()).getTime();
lastData = 0;
function decryptDownload(fileData, key, iv, decrypt) {
// speed info
var lastTime = (new Date()).getTime();
var lastData = 0;

var worker = new Worker(GenerateBlobURL(encScriptSrc));
// Do we need to decrypt the download?
if (decrypt) {
if (key !== null && key !== '' && iv !== null && iv !== '') {
var worker = new Worker(GenerateBlobURL(encScriptSrc));

worker.addEventListener('message', function (e) {
worker.addEventListener('message',
function (e) {
switch (e.data.cmd) {
case 'progress':
var curTime = (new Date()).getTime();
@@ -46,11 +85,20 @@ function processDownload(key) {
lastTime = curTime;
lastData = e.data.processed;
var percentComplete = Math.round(e.data.processed * 100 / e.data.total);
setProgress(percentComplete, 'progress-bar-success progress-bar-striped active', percentComplete + '%', 'Decrypting [' + getReadableFileSizeString(e.data.processed) + ' / ' + getReadableFileSizeString(e.data.total) + ' @ ' + getReadableBandwidthString(speed * 8) + ']');
setProgress(percentComplete,
'progress-bar-success progress-bar-striped active',
percentComplete + '%',
'Decrypting [' +
getReadableFileSizeString(e.data.processed) +
' / ' +
getReadableFileSizeString(e.data.total) +
' @ ' +
getReadableBandwidthString(speed * 8) +
']');
}
break;
case 'finish':
setProgress(100, 'progress-bar-success', '', 'Complete');
setProgress(100, 'progress-bar-success', 'Complete', '');
if (fileType == null || fileType == '') {
fileType = "application/octet-stream";
}
@@ -62,53 +110,52 @@ function processDownload(key) {
}
});

worker.onerror = function (err) {
// An error occured
setProgress(100, 'progress-bar-danger', '', 'Error Occured');
}

// Create a blob for the aes script
var scriptBlob = GenerateBlobURL(aesScriptSrc);

// Execute worker with data
var objData =
{
cmd: 'decrypt',
script: scriptBlob,
key: key,
iv: iv,
chunkSize: chunkSize,
file: this.response
};
worker.postMessage(objData, [objData.file]);
worker.onerror = function (err) {
// An error occured
setProgress(100, 'progress-bar-danger', '', 'Error Occured');
}
};

xhr.onprogress = function (e) {
if (e.lengthComputable) {
var curTime = (new Date()).getTime();
var elapsedTime = (curTime - lastTime) / 1000;
var speed = ((e.loaded - lastData) / elapsedTime);
lastTime = curTime;
lastData = e.loaded;
var percentComplete = Math.round(e.loaded * 100 / e.total);
setProgress(percentComplete, 'progress-bar-success progress-bar-striped active', percentComplete + '%', 'Downloading File [' + getReadableFileSizeString(e.loaded) + ' / ' + getReadableFileSizeString(e.total) + ' @ ' + getReadableBandwidthString(speed * 8) + ']');
}
};

xhr.onerror = function (e) {
setProgress(100, 'progress-bar-danger', '', 'Download Failed');
};
// Create a blob for the aes script
var scriptBlob = GenerateBlobURL(aesScriptSrc);

// Execute worker with data
var objData =
{
cmd: 'decrypt',
script: scriptBlob,
key: key,
iv: iv,
chunkSize: chunkSize,
file: fileData
};
worker.postMessage(objData, [objData.file]);
} else {
setProgress(100, 'progress-bar-danger', '', 'Private Key Needed');
}
} else {
// We want to just prompt the file for DL
setProgress(100, 'progress-bar-success', 'Complete', '');

// Convert file to blob
if (fileType == null || fileType == '') {
fileType = "application/octet-stream";
}
var blob = new Blob([fileData], { type: fileType });

// Add the file download link
addDownloadLink(fileData, key, iv, decrypt);

saveAs(blob, fileName);
}
}

xhr.onabort = function (e) {
setProgress(100, 'progress-bar-warning', '', 'Download Aborted');
};
function addDownloadLink(fileData, key, iv, decrypt) {

xhr.send(fd);
}
else {
setProgress(100, 'progress-bar-danger', '', 'Private Key Needed');
}
var newItem = $('<button type="button" class="btn btn-default" id="reDownloadFile" >Download</button>');
newItem.click(function() {
decryptDownload(fileData, key, iv, decrypt);
});
$('#progress-panel').find('#progress-info').append(newItem);
}

function setProgress(percentage, classes, barMessage, title) {
@@ -121,4 +168,4 @@ function setProgress(percentage, classes, barMessage, title) {
progress.find('#progress-bar').html(barMessage);
progress.find('#progress-info').html(title);
}
}
}

+ 5
- 8
Teknik/Areas/Upload/Scripts/Upload.js Bestand weergeven

@@ -306,8 +306,7 @@ function encryptFile(blob, fileName, contentType, ID, callback) {
function uploadFile(data, key, iv, filetype, fileExt, fileID, encrypt)
{
// Set variables for tracking
var lastTime = (new Date()).getTime();
var lastData = 0;
var startTime = (new Date()).getTime();

var blob = new Blob([data]);
// Now we need to upload the file
@@ -323,7 +322,7 @@ function uploadFile(data, key, iv, filetype, fileExt, fileID, encrypt)
fd.append('__RequestVerificationToken', $('#__AjaxAntiForgeryForm input[name=__RequestVerificationToken]').val());

var xhr = new XMLHttpRequest();
xhr.upload.addEventListener("progress", uploadProgress.bind(null, fileID, lastTime, lastData), false);
xhr.upload.addEventListener("progress", uploadProgress.bind(null, fileID, startTime), false);
xhr.addEventListener("load", uploadComplete.bind(null, fileID, key, encrypt), false);
xhr.addEventListener("error", uploadFailed.bind(null, fileID), false);
xhr.addEventListener("abort", uploadCanceled.bind(null, fileID), false);
@@ -333,13 +332,11 @@ function uploadFile(data, key, iv, filetype, fileExt, fileID, encrypt)



function uploadProgress(fileID, lastTime, lastData, evt) {
function uploadProgress(fileID, startTime, evt) {
if (evt.lengthComputable) {
var curTime = (new Date()).getTime();
var elapsedTime = (curTime - lastTime) / 1000;
var speed = ((evt.loaded - lastData) / elapsedTime);
lastTime = curTime;
lastData = evt.loaded;
var elapsedTime = (curTime - startTime) / 1000;
var speed = (evt.loaded / elapsedTime);
var percentComplete = Math.round(evt.loaded * 100 / evt.total);
if (percentComplete == 100) {
setProgress(fileID, 100, 'progress-bar-success progress-bar-striped active', '', 'Processing Upload');

+ 1
- 2
Teknik/Areas/Upload/ViewModels/DownloadViewModel.cs Bestand weergeven

@@ -13,7 +13,6 @@ namespace Teknik.Areas.Upload.ViewModels
public string ContentType { get; set; }
public long ContentLength { get; set; }
public string IV { get; set; }
public int keySize { get; set; }
public int blockSize { get; set; }
public bool Decrypt { get; set; }
}
}

+ 1
- 0
Teknik/Areas/Upload/Views/Upload/Download.cshtml Bestand weergeven

@@ -9,6 +9,7 @@
var fileName = '@Model.FileName';
var fileType = '@Model.ContentType';
var iv = '@Model.IV';
var decrypt = @Model.Decrypt.ToString().ToLower();
var chunkSize = @(Model.Config.UploadConfig.ChunkSize);
</script>


+ 3
- 0
Utilities/Configuration/UploadConfig.cs Bestand weergeven

@@ -16,6 +16,8 @@ namespace Teknik.Configuration
public long MaxUploadSizeBasic { get; set; }
// Max Upload Size for premium users
public long MaxUploadSizePremium { get; set; }
// Gets the maximum download size before they are forced to the download page
public long MaxDownloadSize { get; set; }
// Location of the upload directory
public string UploadDirectory { get; set; }
// File Extension for saved files
@@ -46,6 +48,7 @@ namespace Teknik.Configuration
MaxUploadSize = 100000000;
MaxUploadSizeBasic = 100000000;
MaxUploadSizePremium = 100000000;
MaxDownloadSize = 100000000;
UploadDirectory = Directory.GetCurrentDirectory();
FileExtension = "enc";
UrlLength = 5;

Laden…
Annuleren
Opslaan