@@ -1,16 +1,20 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Web; | |||
using System.Web.Mvc; | |||
using Teknik.Areas.Upload.Models; | |||
using Teknik.Areas.Upload.ViewModels; | |||
using Teknik.Controllers; | |||
using Teknik.Models; | |||
namespace Teknik.Areas.Upload.Controllers | |||
{ | |||
public class UploadController : DefaultController | |||
{ | |||
private TeknikEntities db = new TeknikEntities(); | |||
// GET: Upload/Upload | |||
[HttpGet] | |||
[AllowAnonymous] | |||
@@ -19,33 +23,90 @@ namespace Teknik.Areas.Upload.Controllers | |||
return View(new UploadViewModel()); | |||
} | |||
// User did not supply key | |||
[HttpGet] | |||
[HttpPost] | |||
[AllowAnonymous] | |||
public ActionResult Download(string url) | |||
[ValidateAntiForgeryToken] | |||
public ActionResult Upload(string fileType, string iv, HttpPostedFileWrapper data) | |||
{ | |||
return View(new UploadViewModel()); | |||
if (data.ContentLength <= Config.UploadConfig.MaxUploadSize) | |||
{ | |||
Models.Upload upload = Uploader.SaveFile(data, fileType, iv); | |||
if (upload != null) | |||
{ | |||
return Json(new { result = new { name = upload.Url, url = Url.SubRouteUrl("upload", "Upload.Download", new { file = upload.Url }) } }, "text/plain"); | |||
} | |||
return Json(new { error = "Unable to upload file" }); | |||
} | |||
else | |||
{ | |||
return Json(new { error = "File Too Large" }); | |||
} | |||
} | |||
// User supplied key | |||
// User did not supply key | |||
[HttpGet] | |||
[AllowAnonymous] | |||
public ActionResult Download(string url, string key) | |||
public ActionResult Download(string file) | |||
{ | |||
return View(new UploadViewModel()); | |||
Models.Upload upload = db.Uploads.Where(up => up.Url == file).FirstOrDefault(); | |||
if (upload != null) | |||
{ | |||
// We don't have the key, so we need to decrypt it client side | |||
if (upload.Key == null) | |||
{ | |||
DownloadViewModel model = new DownloadViewModel(); | |||
model.FileName = file; | |||
model.ContentType = upload.ContentType; | |||
model.Key = upload.Key; | |||
model.IV = upload.IV; | |||
return View(model); | |||
} | |||
else | |||
{ | |||
// decrypt it server side! Weee | |||
return View(); | |||
} | |||
} | |||
else | |||
{ | |||
return RedirectToRoute("Error.Http404"); | |||
} | |||
} | |||
[HttpPost] | |||
[AllowAnonymous] | |||
[ValidateAntiForgeryToken] | |||
public ActionResult Upload(string fileType, string iv, HttpPostedFileWrapper data) | |||
public FileResult DownloadData(string file) | |||
{ | |||
Models.Upload upload = Uploader.SaveFile(data, fileType, iv); | |||
Models.Upload upload = db.Uploads.Where(up => up.Url == file).FirstOrDefault(); | |||
if (upload != null) | |||
{ | |||
return Json(new { result = new { name = upload.Url, url = Url.SubRouteUrl("upload", "Upload.Download.Key", new { file = upload.Url, key = "{key}" }), keyVar = "{key}" } }, "text/plain"); | |||
string filePath = Path.Combine(Config.UploadConfig.UploadDirectory, upload.FileName); | |||
if (System.IO.File.Exists(filePath)) | |||
{ | |||
byte[] buffer; | |||
FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read); | |||
try | |||
{ | |||
int length = (int)fileStream.Length; // get file length | |||
buffer = new byte[length]; // create buffer | |||
int count; // actual number of bytes read | |||
int sum = 0; // total number of bytes read | |||
// read until Read method returns 0 (end of the stream has been reached) | |||
while ((count = fileStream.Read(buffer, sum, length - sum)) > 0) | |||
sum += count; // sum is a buffer offset for next reading | |||
} | |||
finally | |||
{ | |||
fileStream.Close(); | |||
} | |||
return File(buffer, System.Net.Mime.MediaTypeNames.Application.Octet, file); | |||
} | |||
} | |||
return Json(new { error = "Unable to upload file" }); | |||
RedirectToAction("Http404", "Error", new { area = "Errors", exception = new Exception("File Not Found") }); | |||
return null; | |||
} | |||
[HttpPost] |
@@ -0,0 +1,55 @@ | |||
$(document).ready(downloadFile); | |||
function downloadFile() { | |||
var fd = new FormData(); | |||
fd.append('file', fileName); | |||
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) { | |||
var worker = new Worker(encScriptSrc); | |||
worker.addEventListener('message', function (e) { | |||
switch (e.data.cmd) { | |||
case 'progress': | |||
var percentComplete = Math.round(e.data.processed * 100 / e.data.total); | |||
$("#progress").children('.progress-bar').css('width', (percentComplete * (2 / 5)) + 20 + '%'); | |||
$("#progress").children('.progress-bar').html(percentComplete + '% Decrypted'); | |||
break; | |||
case 'finish': | |||
var blob = new Blob([e.data.buffer], { type: fileType }); | |||
var url = (window.webkitURL || window.URL).createObjectURL(blob); | |||
location.href = url; // <-- Download! | |||
// DO SOMETHING | |||
break; | |||
} | |||
}); | |||
worker.onerror = function (err) { | |||
// An error occured | |||
$("#progress").children('.progress-bar').css('width', '100%'); | |||
$("#progress").children('.progress-bar').removeClass('progress-bar-success'); | |||
$("#progress").children('.progress-bar').addClass('progress-bar-danger'); | |||
$("#progress").children('.progress-bar').html('Error Occured'); | |||
} | |||
// Execute worker with data | |||
var objData = | |||
{ | |||
cmd: 'decrypt', | |||
script: aesScriptSrc, | |||
key: key, | |||
iv: iv, | |||
chunkSize: chunkSize, | |||
file: this.response | |||
}; | |||
worker.postMessage(objData, [objData.file]); | |||
} | |||
}; | |||
xhr.send(fd); | |||
} |
@@ -1,88 +1,85 @@ | |||
self.addEventListener('message', function (e) { | |||
importScripts(e.data.script); | |||
var bytes = new Uint8Array(e.data.file); | |||
var startByte = 0; | |||
var endByte = 0; | |||
var prog = []; | |||
var key = CryptoJS.enc.Utf8.parse(e.data.key); | |||
var iv = CryptoJS.enc.Utf8.parse(e.data.iv); | |||
// Create aes encryptor object | |||
var aesCrypto; | |||
switch (e.data.cmd) { | |||
case 'encrypt': | |||
var bytes = new Uint8Array(e.data.file); | |||
var startByte = 0; | |||
var endByte = 0; | |||
var prog = []; | |||
var key = CryptoJS.enc.Utf8.parse(e.data.key); | |||
var iv = CryptoJS.enc.Utf8.parse(e.data.iv); | |||
// Create aes encryptor object | |||
var aesEncryptor = CryptoJS.algo.AES.createEncryptor(key, { iv: iv, mode: CryptoJS.mode.CTR, padding: CryptoJS.pad.NoPadding }); | |||
while (startByte <= (bytes.length - 1)) { | |||
// Set the end byte | |||
endByte = startByte + e.data.chunkSize; | |||
if (endByte > bytes.length - 1) | |||
{ | |||
endByte = bytes.length - 1; | |||
} | |||
// Grab current set of bytes | |||
var curBytes = bytes.subarray(startByte, endByte); | |||
//var b64encoded = btoa(String.fromCharCode.apply(null, curBytes)); | |||
var wordArray = CryptoJS.lib.WordArray.create(curBytes) | |||
// encrypt the passed in file data | |||
var enc = aesEncryptor.process(wordArray); | |||
// Convert and add to current array buffer | |||
var encStr = enc.toString(CryptoJS.enc.Base64); // to string | |||
prog.pushArray(_base64ToArray(encStr)); | |||
// Send an update on progress | |||
var objData = | |||
{ | |||
cmd: 'progress', | |||
processed: endByte, | |||
total: bytes.length - 1 | |||
}; | |||
self.postMessage(objData); | |||
// Set the next start as the current end | |||
startByte = endByte + 1; | |||
} | |||
//then finalize | |||
var encFinal = aesEncryptor.finalize(); | |||
var finalStr = encFinal.toString(CryptoJS.enc.Base64); // to final string | |||
prog.pushArray(_base64ToArray(finalStr)); | |||
var objData = | |||
{ | |||
cmd: 'progress', | |||
processed: bytes.length - 1, | |||
total: bytes.length - 1 | |||
}; | |||
// convert array to ArrayBuffer | |||
var arBuf = _arrayToArrayBuffer(prog); | |||
//throw JSON.stringify({ dataLength: prog.length, len: bytes.length, finalLength: arBuf.byteLength }) | |||
// Now package it into a mesage to send home | |||
var objData = | |||
{ | |||
cmd: 'finish', | |||
encrypted: arBuf | |||
}; | |||
self.postMessage(objData, [objData.encrypted]); | |||
aesCrypto = CryptoJS.algo.AES.createEncryptor(key, { iv: iv, mode: CryptoJS.mode.CTR, padding: CryptoJS.pad.NoPadding }); | |||
break; | |||
case 'decrypt': | |||
// decrypt the passed in file data | |||
var decrypted = CryptoJS.AES.decrypt(e.data.file, e.data.key, { iv: e.data.iv }); | |||
var fileText = decrypted.toString(); | |||
self.postMessage(fileText); | |||
aesCrypto = CryptoJS.algo.AES.createDecryptor(key, { iv: iv, mode: CryptoJS.mode.CTR, padding: CryptoJS.pad.NoPadding }); | |||
break; | |||
default: | |||
break; | |||
} | |||
while (startByte <= (bytes.length - 1)) { | |||
// Set the end byte | |||
endByte = startByte + e.data.chunkSize; | |||
if (endByte > bytes.length - 1) { | |||
endByte = bytes.length - 1; | |||
} | |||
// Grab current set of bytes | |||
var curBytes = bytes.subarray(startByte, endByte); | |||
//var b64encoded = btoa(String.fromCharCode.apply(null, curBytes)); | |||
var wordArray = CryptoJS.lib.WordArray.create(curBytes) | |||
// encrypt the passed in file data | |||
var enc = aesCrypto.process(wordArray); | |||
// Convert and add to current array buffer | |||
var encStr = enc.toString(CryptoJS.enc.Base64); // to string | |||
prog.pushArray(_base64ToArray(encStr)); | |||
// Send an update on progress | |||
var objData = | |||
{ | |||
cmd: 'progress', | |||
processed: endByte, | |||
total: bytes.length - 1 | |||
}; | |||
self.postMessage(objData); | |||
// Set the next start as the current end | |||
startByte = endByte + 1; | |||
} | |||
//then finalize | |||
var encFinal = aesCrypto.finalize(); | |||
var finalStr = encFinal.toString(CryptoJS.enc.Base64); // to final string | |||
prog.pushArray(_base64ToArray(finalStr)); | |||
var objData = | |||
{ | |||
cmd: 'progress', | |||
processed: bytes.length - 1, | |||
total: bytes.length - 1 | |||
}; | |||
// convert array to ArrayBuffer | |||
var arBuf = _arrayToArrayBuffer(prog); | |||
//throw JSON.stringify({ dataLength: prog.length, len: bytes.length, finalLength: arBuf.byteLength }) | |||
// Now package it into a mesage to send home | |||
var objData = | |||
{ | |||
cmd: 'finish', | |||
buffer: arBuf | |||
}; | |||
self.postMessage(objData, [objData.buffer]); | |||
}, false); | |||
function _arrayToArrayBuffer(array) { |
@@ -53,6 +53,13 @@ function linkRemove(selector, fileID) { | |||
}); | |||
} | |||
function linkCancel(selector, fileID) { | |||
$(selector).click(function () { | |||
$('#link-' + fileID).remove(); | |||
return false; | |||
}); | |||
} | |||
var fileCount = 0; | |||
var dropZone = new Dropzone(document.body, { | |||
@@ -84,11 +91,31 @@ var dropZone = new Dropzone(document.body, { | |||
</div> \ | |||
</div> \ | |||
</div> \ | |||
<div class="panel-footer" id="link-footer-' + fileID + '"> \ | |||
<div class="row"> \ | |||
<div class="col-sm-12 text-center"> \ | |||
<button type="button" class="btn btn-default btn-sm" id="remove-link-' + fileID + '">Remove From List</button> \ | |||
</div> \ | |||
</div> \ | |||
</div> \ | |||
</div> \ | |||
'); | |||
// Encrypt the file | |||
encryptFile(file, uploadFile); | |||
linkRemove('#remove-link-' + fileID + '', fileID); | |||
// Check the file size | |||
if (file.size <= maxUploadSize) { | |||
// Encrypt the file and upload it | |||
encryptFile(file, uploadFile); | |||
} | |||
else | |||
{ | |||
// An error occured | |||
$("#progress-" + fileID).children('.progress-bar').css('width', '100%'); | |||
$("#progress-" + fileID).children('.progress-bar').removeClass('progress-bar-success'); | |||
$("#progress-" + fileID).children('.progress-bar').addClass('progress-bar-danger'); | |||
$("#progress-" + fileID).children('.progress-bar').html('File Too Large'); | |||
} | |||
this.removeFile(file); | |||
} | |||
}); | |||
@@ -108,6 +135,13 @@ function encryptFile(file, callback) { | |||
var keyStr = randomString(24, '#aA'); | |||
var ivStr = randomString(24, '#aA'); | |||
// Let's grab the header and get the file type | |||
var arr = (new Uint8Array(e.target.result)).subarray(0, 4); | |||
var header = ""; | |||
for (var i = 0; i < arr.length; i++) { | |||
header += arr[i].toString(16); | |||
} | |||
var worker = new Worker(encScriptSrc); | |||
worker.addEventListener('message', function (e) { | |||
@@ -121,7 +155,7 @@ function encryptFile(file, callback) { | |||
case 'finish': | |||
if (callback != null) { | |||
// Finish | |||
callback(e.data.encrypted, keyStr, ivStr, filetype, fileID); | |||
callback(e.data.buffer, keyStr, ivStr, filetype, fileID); | |||
} | |||
break; | |||
} | |||
@@ -194,13 +228,10 @@ function uploadComplete(fileID, key, evt) { | |||
obj = JSON.parse(evt.target.responseText); | |||
var name = obj.result.name; | |||
var fullName = decodeURIComponent(obj.result.url); | |||
var keyVar = decodeURIComponent(obj.result.keyVar); | |||
fullName = fullName.replace(keyVar, key); | |||
$('#progress-' + fileID).children('.progress-bar').css('width', '100%'); | |||
$('#progress-' + fileID).children('.progress-bar').html('Complete'); | |||
$('#upload-link-' + fileID).html('<p><a href="' + fullName + '" target="_blank" class="alert-link">' + fullName + '</a></p>'); | |||
$('#link-' + fileID).append(' \ | |||
<div class="panel-footer"> \ | |||
$('#upload-link-' + fileID).html('<p><a href="' + fullName + '#' + key + '" target="_blank" class="alert-link">' + fullName + '#' + key + '</a></p>'); | |||
$('#link-footer-' + fileID).html(' \ | |||
<div class="row"> \ | |||
<div class="col-sm-4 text-center"> \ | |||
<button type="button" class="btn btn-default btn-sm" id="save-key-link-' + fileID + '">Save Key On Server</button> \ | |||
@@ -212,7 +243,6 @@ function uploadComplete(fileID, key, evt) { | |||
<button type="button" class="btn btn-default btn-sm" id="remove-link-' + fileID + '">Remove From List</button> \ | |||
</div> \ | |||
</div> \ | |||
</div> \ | |||
'); | |||
linkSaveKey('#save-key-link-' + fileID + '', name, key, fileID); | |||
linkUploadDelete('#generate-delete-link-' + fileID + '', name); | |||
@@ -232,3 +262,25 @@ function uploadCanceled(fileID, evt) { | |||
$("#progress-" + fileID).children('.progress-bar').addClass('progress-bar-warning'); | |||
$('#progress-' + fileID).children('.progress-bar').html('Upload Canceled'); | |||
} | |||
function GetMIMEType(header) | |||
{ | |||
var type = ""; | |||
switch (header) { | |||
case "89504e47": | |||
type = "image/png"; | |||
break; | |||
case "47494638": | |||
type = "image/gif"; | |||
break; | |||
case "ffd8ffe0": | |||
case "ffd8ffe1": | |||
case "ffd8ffe2": | |||
type = "image/jpeg"; | |||
break; | |||
default: | |||
type = "unknown"; // Or you can use the blob.type as fallback | |||
break; | |||
} | |||
return type; | |||
} |
@@ -29,13 +29,6 @@ namespace Teknik.Areas.Upload | |||
new { controller = "Upload", action = "Download", url = string.Empty }, | |||
new[] { typeof(Controllers.UploadController).Namespace } | |||
); | |||
context.MapSubdomainRoute( | |||
"Upload.Download.Key", | |||
"dev", | |||
"Upload/{file}/{key}", | |||
new { controller = "Upload", action = "Download", url = string.Empty }, | |||
new[] { typeof(Controllers.UploadController).Namespace } | |||
); | |||
context.MapSubdomainRoute( | |||
"Upload.Delete", | |||
"dev", | |||
@@ -111,8 +104,9 @@ namespace Teknik.Areas.Upload | |||
BundleTable.Bundles.Add(new ScriptBundle("~/bundles/upload").Include( | |||
"~/Scripts/Dropzone/dropzone.js", | |||
"~/Areas/Upload/Scripts/Upload.js", | |||
"~/Scripts/bootbox/bootbox.min.js", | |||
"~/Scripts/Crypto-js/aes.js")); | |||
"~/Scripts/bootbox/bootbox.min.js")); | |||
BundleTable.Bundles.Add(new ScriptBundle("~/bundles/download").Include( | |||
"~/Areas/Upload/Scripts/Download.js")); | |||
BundleTable.Bundles.Add(new ScriptBundle("~/bundles/cryptoWorker").Include( | |||
"~/Areas/Upload/Scripts/EncryptionWorker.js")); | |||
BundleTable.Bundles.Add(new ScriptBundle("~/bundles/crypto").Include( |
@@ -50,6 +50,7 @@ namespace Teknik.Areas.Upload | |||
upload.IV = iv; | |||
db.Uploads.Add(upload); | |||
db.SaveChanges(); | |||
return upload; | |||
} |
@@ -0,0 +1,16 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Web; | |||
using Teknik.ViewModels; | |||
namespace Teknik.Areas.Upload.ViewModels | |||
{ | |||
public class DownloadViewModel : ViewModelBase | |||
{ | |||
public string FileName { get; set; } | |||
public string ContentType { get; set; } | |||
public string Key { get; set; } | |||
public string IV { get; set; } | |||
} | |||
} |
@@ -1 +1,29 @@ | |||
@model Teknik.Areas.Upload.ViewModels.UploadViewModel | |||
@model Teknik.Areas.Upload.ViewModels.DownloadViewModel | |||
<script> | |||
var encScriptSrc = '@Scripts.Url("~/bundles/cryptoWorker")'; | |||
var aesScriptSrc = '@Scripts.Url("~/bundles/crypto")'; | |||
var downloadDataUrl = '@Url.SubRouteUrl("upload", "Upload.Action", new { action = "DownloadData" })'; | |||
var fileName = '@Model.FileName'; | |||
var fileType = '@Model.ContentType'; | |||
var key = window.location.hash.substring(1); | |||
if (key == null) | |||
{ | |||
key = '@((Model.Key != null) ? Model.Key : string.Empty)'; | |||
} | |||
var iv = '@Model.IV'; | |||
var chunkSize = @(Model.Config.UploadConfig.ChunkSize); | |||
</script> | |||
<!-- Add UI for downloading info --> | |||
<div class="container"> | |||
<div class="row"> | |||
<div class="col-sm-12"> | |||
<div class="progress" id="progress"> | |||
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 0%">0%</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
@Scripts.Render("~/bundles/download") |
@@ -6,8 +6,8 @@ | |||
var generateDeleteKeyURL = '@Url.SubRouteUrl("upload", "Upload.Action", new { action= "GenerateDeleteKey" })'; | |||
var saveKeyToServerURL = '@Url.SubRouteUrl("upload", "Upload.Action", new { action= "SaveFileKey" })'; | |||
var uploadFileURL = '@Url.SubRouteUrl("upload", "Upload.Action", new { action = "Upload" })'; | |||
var maxUploadSize = @(Model.Config.UploadConfig.MaxUploadSize / 100000); | |||
var chunkSize = @(Model.Config.UploadConfig.ChunkSize); | |||
var maxUploadSize = @Model.Config.UploadConfig.MaxUploadSize; | |||
var chunkSize = @Model.Config.UploadConfig.ChunkSize; | |||
</script> | |||
@Styles.Render("~/Content/upload") | |||
@@ -47,7 +47,14 @@ | |||
<div class="container" id="upload-links"> | |||
</div> | |||
<br /> | |||
<div class="well text-center">Each file is encrypted on upload using an AES-256-CTR cipher. If you wish to view the file decrypted, you must use the direct Teknik link.</div> | |||
<div class="well text-center"> | |||
<p> | |||
Each file is encrypted on upload using an AES-256-CTR cipher. If you wish to view the file decrypted, you must use the direct Teknik link. | |||
</p> | |||
<p> | |||
The maximum file size per upload is <b>@Utility.GetBytesReadable(Model.Config.UploadConfig.MaxUploadSize)</b> | |||
</p> | |||
</div> | |||
<div class="text-center"> | |||
Useful Tools: <a href="http://git.teknik.io/Teknikode/Tools/src/master/Upload">Upload Scripts and Utilities</a> | <a href="https://github.com/jschx/poomf">Poomf Uploader</a> | |||
<br /> |
@@ -88,5 +88,51 @@ namespace Teknik | |||
return result; | |||
} | |||
public static string GetBytesReadable(long i) | |||
{ | |||
// Get absolute value | |||
long absolute_i = (i < 0 ? -i : i); | |||
// Determine the suffix and readable value | |||
string suffix; | |||
double readable; | |||
if (absolute_i >= 0x1000000000000000) // Exabyte | |||
{ | |||
suffix = "EB"; | |||
readable = (i >> 50); | |||
} | |||
else if (absolute_i >= 0x4000000000000) // Petabyte | |||
{ | |||
suffix = "PB"; | |||
readable = (i >> 40); | |||
} | |||
else if (absolute_i >= 0x10000000000) // Terabyte | |||
{ | |||
suffix = "TB"; | |||
readable = (i >> 30); | |||
} | |||
else if (absolute_i >= 0x40000000) // Gigabyte | |||
{ | |||
suffix = "GB"; | |||
readable = (i >> 20); | |||
} | |||
else if (absolute_i >= 0x100000) // Megabyte | |||
{ | |||
suffix = "MB"; | |||
readable = (i >> 10); | |||
} | |||
else if (absolute_i >= 0x400) // Kilobyte | |||
{ | |||
suffix = "KB"; | |||
readable = i; | |||
} | |||
else | |||
{ | |||
return i.ToString("0 B"); // Byte | |||
} | |||
// Divide by 1024 to get fractional value | |||
readable = (readable / 1024); | |||
// Return formatted number with suffix | |||
return readable.ToString("0.### ") + suffix; | |||
} | |||
} | |||
} |
@@ -170,6 +170,7 @@ | |||
<Compile Include="Areas\Upload\Models\Upload.cs" /> | |||
<Compile Include="Areas\Upload\UploadAreaRegistration.cs" /> | |||
<Compile Include="Areas\Upload\Uploader.cs" /> | |||
<Compile Include="Areas\Upload\ViewModels\DownloadViewModel.cs" /> | |||
<Compile Include="Areas\Upload\ViewModels\UploadViewModel.cs" /> | |||
<Compile Include="Configuration\BlogConfig.cs" /> | |||
<Compile Include="Configuration\Config.cs" /> | |||
@@ -209,6 +210,7 @@ | |||
<Content Include="Areas\Home\Content\Home.css" /> | |||
<Content Include="Areas\Home\Scripts\Home.js" /> | |||
<Content Include="Areas\Profile\Scripts\Profile.js" /> | |||
<Content Include="Areas\Upload\Scripts\Download.js" /> | |||
<Content Include="Scripts\Crypto-js\aes.js" /> | |||
<Content Include="Areas\Upload\Scripts\EncryptionWorker.js" /> | |||
<Content Include="Areas\Upload\Scripts\Upload.js" /> |
@@ -27,7 +27,9 @@ | |||
<modules> | |||
<remove name="FormsAuthentication" /> | |||
<add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" /> | |||
<add name="PerfModule" type="Teknik.Modules.PerformanceMonitorModule, Teknik" /> | |||
<add name="PerfModule" type="Teknik.Modules.PerformanceMonitorModule, Teknik" /> | |||
<remove name="UrlRoutingModule-4.0" /> | |||
<add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" preCondition="" /> | |||
</modules> | |||
<staticContent> | |||
<mimeMap fileExtension="woff" mimeType="application/font-woff" /> |