mirror of
https://git.teknik.io/Teknikode/Teknik.git
synced 2023-08-02 14:16:22 +02:00
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.
This commit is contained in:
parent
72a3d3efe7
commit
ce0ded7c55
@ -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]
|
||||
|
@ -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('__RequestVerificationToken', $('#__AjaxAntiForgeryForm input[name=__RequestVerificationToken]').val());
|
||||
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';
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', downloadDataUrl, true);
|
||||
xhr.responseType = 'arraybuffer';
|
||||
|
||||
xhr.onload = function (e) {
|
||||
if (this.status == 200) {
|
||||
lastTime = (new Date()).getTime();
|
||||
lastData = 0;
|
||||
xhr.onload = function(e) {
|
||||
if (this.status === 200) {
|
||||
decryptDownload(this.response, key, iv, decrypt);
|
||||
}
|
||||
};
|
||||
|
||||
var worker = new Worker(GenerateBlobURL(encScriptSrc));
|
||||
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) +
|
||||
']');
|
||||
}
|
||||
}
|
||||
|
||||
worker.addEventListener('message', function (e) {
|
||||
xhr.onerror = function(e) {
|
||||
setProgress(100, 'progress-bar-danger', '', 'Download Failed');
|
||||
};
|
||||
|
||||
xhr.onabort = function(e) {
|
||||
setProgress(100, 'progress-bar-warning', '', 'Download Aborted');
|
||||
};
|
||||
|
||||
xhr.send(fd);
|
||||
}
|
||||
|
||||
function decryptDownload(fileData, key, iv, decrypt) {
|
||||
// speed info
|
||||
var lastTime = (new Date()).getTime();
|
||||
var lastData = 0;
|
||||
|
||||
// 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) {
|
||||
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,55 +110,54 @@ 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) + ']');
|
||||
}
|
||||
};
|
||||
// Create a blob for the aes script
|
||||
var scriptBlob = GenerateBlobURL(aesScriptSrc);
|
||||
|
||||
xhr.onerror = function (e) {
|
||||
setProgress(100, 'progress-bar-danger', '', 'Download Failed');
|
||||
};
|
||||
// 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', '');
|
||||
|
||||
xhr.onabort = function (e) {
|
||||
setProgress(100, 'progress-bar-warning', '', 'Download Aborted');
|
||||
};
|
||||
// Convert file to blob
|
||||
if (fileType == null || fileType == '') {
|
||||
fileType = "application/octet-stream";
|
||||
}
|
||||
var blob = new Blob([fileData], { type: fileType });
|
||||
|
||||
xhr.send(fd);
|
||||
}
|
||||
else {
|
||||
setProgress(100, 'progress-bar-danger', '', 'Private Key Needed');
|
||||
// Add the file download link
|
||||
addDownloadLink(fileData, key, iv, decrypt);
|
||||
|
||||
saveAs(blob, fileName);
|
||||
}
|
||||
}
|
||||
|
||||
function addDownloadLink(fileData, key, iv, decrypt) {
|
||||
|
||||
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) {
|
||||
var progress = $('#progress-panel');
|
||||
if (progress !== null) {
|
||||
@ -121,4 +168,4 @@ function setProgress(percentage, classes, barMessage, title) {
|
||||
progress.find('#progress-bar').html(barMessage);
|
||||
progress.find('#progress-info').html(title);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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');
|
||||
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user