The next generation of the Teknik Services. Written in ASP.NET. https://www.teknik.io/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

219 lines
8.7 KiB

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.IO;
using Teknik.Configuration;
using Teknik.Models;
using Teknik.Utilities;
using System.Text;
using Teknik.Utilities.Cryptography;
using Teknik.Data;
using Teknik.StorageService;
using Teknik.Logging;
using Microsoft.Extensions.Logging;
using Microsoft.EntityFrameworkCore;
namespace Teknik.Areas.Upload
{
public static class UploadHelper
{
private static object _cacheLock = new object();
private readonly static ObjectCache _uploadCache = new ObjectCache(300);
public static Models.Upload SaveFile(TeknikEntities db, Config config, Stream file, string contentType, long contentLength, bool encrypt, ExpirationUnit expirationUnit, int expirationLength)
{
return SaveFile(db, config, file, contentType, contentLength, encrypt, expirationUnit, expirationLength, string.Empty, null, null, 256, 128);
}
public static Models.Upload SaveFile(TeknikEntities db, Config config, Stream file, string contentType, long contentLength, bool encrypt, ExpirationUnit expirationUnit, int expirationLength, string fileExt)
{
return SaveFile(db, config, file, contentType, contentLength, encrypt, expirationUnit, expirationLength, fileExt, null, null, 256, 128);
}
public static Models.Upload SaveFile(TeknikEntities db, Config config, Stream file, string contentType, long contentLength, bool encrypt, ExpirationUnit expirationUnit, int expirationLength, string fileExt, string iv)
{
return SaveFile(db, config, file, contentType, contentLength, encrypt, expirationUnit, expirationLength, fileExt, iv, null, 256, 128);
}
public static Models.Upload SaveFile(TeknikEntities db, Config config, Stream file, string contentType, long contentLength, bool encrypt, ExpirationUnit expirationUnit, int expirationLength, string fileExt, string iv, string key)
{
return SaveFile(db, config, file, contentType, contentLength, encrypt, expirationUnit, expirationLength, fileExt, iv, key, 256, 128);
}
public static Models.Upload SaveFile(TeknikEntities db, Config config, Stream file, string contentType, long contentLength, bool encrypt, ExpirationUnit expirationUnit, int expirationLength, string fileExt, string iv, string key, int keySize, int blockSize)
{
var storageService = StorageServiceFactory.GetStorageService(config.UploadConfig.StorageConfig);
// Generate a unique file name that does not currently exist
var fileName = storageService.GetUniqueFileName();
// once we have the filename, lets save the file
if (encrypt)
{
// Generate a key and iv
if (string.IsNullOrEmpty(key))
key = StringHelper.RandomString(config.UploadConfig.KeySize / 8);
if (string.IsNullOrEmpty(iv))
iv = StringHelper.RandomString(config.UploadConfig.BlockSize / 8);
byte[] keyBytes = Encoding.UTF8.GetBytes(key);
byte[] ivBytes = Encoding.UTF8.GetBytes(iv);
storageService.SaveEncryptedFile(fileName, file, config.UploadConfig.ChunkSize, keyBytes, ivBytes);
}
else
{
storageService.SaveFile(fileName, file);
}
// Generate a unique url
string extension = (config.UploadConfig.IncludeExtension) ? fileExt : string.Empty;
string url = StringHelper.RandomString(config.UploadConfig.UrlLength) + extension;
while (db.Uploads.Where(u => u.Url == url).FirstOrDefault() != null)
{
url = StringHelper.RandomString(config.UploadConfig.UrlLength) + extension;
}
// Generate a deletion key
string delKey = StringHelper.RandomString(config.UploadConfig.DeleteKeyLength);
// Now we need to update the database with the new upload information
Models.Upload upload = new Models.Upload();
upload.DateUploaded = DateTime.Now;
upload.Url = url;
upload.FileName = fileName;
upload.ContentType = (!string.IsNullOrEmpty(contentType)) ? contentType : "application/octet-stream";
upload.ContentLength = contentLength;
upload.Key = key;
upload.IV = iv;
upload.KeySize = keySize;
upload.BlockSize = blockSize;
upload.DeleteKey = delKey;
if (expirationUnit == ExpirationUnit.Views)
{
upload.MaxDownloads = expirationLength;
}
else
{
switch (expirationUnit)
{
case ExpirationUnit.Minutes:
upload.ExpireDate = DateTime.Now.AddMinutes(expirationLength);
break;
case ExpirationUnit.Hours:
upload.ExpireDate = DateTime.Now.AddHours(expirationLength);
break;
case ExpirationUnit.Days:
upload.ExpireDate = DateTime.Now.AddDays(expirationLength);
break;
case ExpirationUnit.Months:
upload.ExpireDate = DateTime.Now.AddMonths(expirationLength);
break;
case ExpirationUnit.Years:
upload.ExpireDate = DateTime.Now.AddYears(expirationLength);
break;
}
}
db.Uploads.Add(upload);
db.SaveChanges();
return upload;
}
public static string GenerateDeleteKey(TeknikEntities db, Config config, string url)
{
var upload = db.Uploads.FirstOrDefault(up => up.Url == url);
if (upload != null)
{
string delKey = StringHelper.RandomString(config.UploadConfig.DeleteKeyLength);
upload.DeleteKey = delKey;
ModifyUpload(db, upload);
return delKey;
}
return null;
}
public static bool CheckExpiration(Models.Upload upload)
{
if (upload.ExpireDate != null && DateTime.Now >= upload.ExpireDate)
return true;
if (upload.MaxDownloads > 0 && upload.Downloads >= upload.MaxDownloads)
return true;
return false;
}
public static void IncrementDownloadCount(IBackgroundTaskQueue queue, Config config, string url)
{
// Fire and forget updating of the download count
queue.QueueBackgroundWorkItem(async token =>
{
var optionsBuilder = new DbContextOptionsBuilder<TeknikEntities>();
optionsBuilder.UseSqlServer(config.DbConnection);
using (TeknikEntities db = new TeknikEntities(optionsBuilder.Options))
{
var upload = GetUpload(db, url);
if (upload != null)
{
upload.Downloads++;
ModifyUpload(db, upload);
}
}
});
}
public static Models.Upload GetUpload(TeknikEntities db, string url)
{
lock (_cacheLock)
{
var upload = _uploadCache.GetObject(url, (key) => db.Uploads.FirstOrDefault(up => up.Url == key));
if (!db.Exists(upload))
db.Attach(upload);
return upload;
}
}
public static void DeleteFile(TeknikEntities db, Config config, ILogger<Logger> logger, string url)
{
var upload = GetUpload(db, url);
try
{
var storageService = StorageServiceFactory.GetStorageService(config.UploadConfig.StorageConfig);
storageService.DeleteFile(upload.FileName);
}
catch (Exception ex)
{
logger.LogError(ex, "Unable to delete file: {0}", upload.FileName);
}
// Delete from the DB
db.Uploads.Remove(upload);
db.SaveChanges();
// Remove from the cache
lock (_cacheLock)
{
_uploadCache.DeleteObject(url);
}
}
public static void ModifyUpload(TeknikEntities db, Models.Upload upload)
{
// Update the cache's copy
lock (_cacheLock)
{
_uploadCache.UpdateObject(upload.Url, upload);
}
// Update the database
db.Entry(upload).State = EntityState.Modified;
db.SaveChanges();
}
}
}