public async Task <IActionResult> Index() { ViewBag.Title = "Upload Files"; UploadViewModel model = new UploadViewModel(); model.CurrentSub = Subdomain; model.Encrypt = false; model.ExpirationLength = 1; model.ExpirationUnit = ExpirationUnit.Days; model.MaxUploadSize = _config.UploadConfig.MaxUploadSize; if (User.Identity.IsAuthenticated) { User user = UserHelper.GetUser(_dbContext, User.Identity.Name); if (user != null) { model.Encrypt = user.UploadSettings.Encrypt; model.ExpirationLength = user.UploadSettings.ExpirationLength; model.ExpirationUnit = user.UploadSettings.ExpirationUnit; model.Vaults = user.Vaults.ToList(); model.MaxUploadSize = _config.UploadConfig.MaxUploadSizeBasic; IdentityUserInfo userInfo = await IdentityHelper.GetIdentityUserInfo(_config, User.Identity.Name); if (userInfo.AccountType == AccountType.Premium) { model.MaxUploadSize = _config.UploadConfig.MaxUploadSizePremium; } } } return(View(model)); }
public async Task <IActionResult> Upload() { ViewBag.Title = "Upload Service Help"; UploadHelpViewModel model = new UploadHelpViewModel(); model.MaxUploadSize = _config.UploadConfig.MaxUploadSize; if (User.Identity.IsAuthenticated) { User user = UserHelper.GetUser(_dbContext, User.Identity.Name); if (user != null) { model.MaxUploadSize = _config.UploadConfig.MaxUploadSizeBasic; IdentityUserInfo userInfo = await IdentityHelper.GetIdentityUserInfo(_config, User.Identity.Name); if (userInfo.AccountType == AccountType.Premium) { model.MaxUploadSize = _config.UploadConfig.MaxUploadSizePremium; } } } return(View("~/Areas/Help/Views/Help/Upload.cshtml", model)); }
public static DateTime GetLastAccountActivity(TeknikEntities db, Config config, string username, IdentityUserInfo userInfo) { try { DateTime lastActive = new DateTime(1900, 1, 1); if (UserEmailExists(config, GetUserEmailAddress(config, username))) { DateTime emailLastActive = UserEmailLastActive(config, GetUserEmailAddress(config, username)); if (lastActive < emailLastActive) { lastActive = emailLastActive; } } if (UserGitExists(config, username)) { DateTime gitLastActive = UserGitLastActive(config, username); if (lastActive < gitLastActive) { lastActive = gitLastActive; } } if (userInfo.LastSeen.HasValue) { DateTime userLastActive = userInfo.LastSeen.Value; if (lastActive < userLastActive) { lastActive = userLastActive; } } return(lastActive); } catch (Exception ex) { throw new Exception("Unable to determine last account activity.", ex); } }
private static async Task <bool> ScanUpload(Config config, TeknikEntities db, Upload upload, int totalCount, int currentCount) { bool virusDetected = false; string subDir = upload.FileName[0].ToString(); string filePath = Path.Combine(config.UploadConfig.UploadDirectory, subDir, upload.FileName); if (File.Exists(filePath)) { // If the IV is set, and Key is set, then scan it if (!string.IsNullOrEmpty(upload.Key) && !string.IsNullOrEmpty(upload.IV)) { byte[] keyBytes = Encoding.UTF8.GetBytes(upload.Key); byte[] ivBytes = Encoding.UTF8.GetBytes(upload.IV); long maxUploadSize = config.UploadConfig.MaxUploadSize; if (upload.User != null) { maxUploadSize = config.UploadConfig.MaxUploadSizeBasic; IdentityUserInfo userInfo = await IdentityHelper.GetIdentityUserInfo(config, upload.User.Username); if (userInfo.AccountType == AccountType.Premium) { maxUploadSize = config.UploadConfig.MaxUploadSizePremium; } } using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) using (AesCounterStream aesStream = new AesCounterStream(fs, false, keyBytes, ivBytes)) { ClamClient clam = new ClamClient(config.UploadConfig.ClamServer, config.UploadConfig.ClamPort); clam.MaxStreamSize = maxUploadSize; ClamScanResult scanResult = await clam.SendAndScanFileAsync(fs); switch (scanResult.Result) { case ClamScanResults.Clean: string cleanMsg = string.Format("[{0}] Clean Scan: {1}/{2} Scanned | {3} - {4}", DateTime.Now, currentCount, totalCount, upload.Url, upload.FileName); Output(cleanMsg); break; case ClamScanResults.VirusDetected: string msg = string.Format("[{0}] Virus Detected: {1} - {2} - {3}", DateTime.Now, upload.Url, upload.FileName, scanResult.InfectedFiles.First().VirusName); Output(msg); lock (scanStatsLock) { virusDetected = true; File.AppendAllLines(virusFile, new List <string> { msg }); } lock (dbLock) { string urlName = upload.Url; // Delete from the DB db.Uploads.Remove(upload); // Delete the File if (File.Exists(filePath)) { File.Delete(filePath); } // Add to transparency report if any were found Takedown report = new Takedown(); report.Requester = TAKEDOWN_REPORTER; report.RequesterContact = config.SupportEmail; report.DateRequested = DateTime.Now; report.Reason = "Malware Found"; report.ActionTaken = string.Format("Upload removed: {0}", urlName); report.DateActionTaken = DateTime.Now; db.Takedowns.Add(report); // Save Changes db.SaveChanges(); } break; case ClamScanResults.Error: string errorMsg = string.Format("[{0}] Scan Error: {1}", DateTime.Now, scanResult.RawResult); File.AppendAllLines(errorFile, new List <string> { errorMsg }); Output(errorMsg); break; case ClamScanResults.Unknown: string unkMsg = string.Format("[{0}] Unknown Scan Result: {1}", DateTime.Now, scanResult.RawResult); File.AppendAllLines(errorFile, new List <string> { unkMsg }); Output(unkMsg); break; } } } } return(virusDetected); }
public async Task <IActionResult> Upload([FromForm] UploadFileViewModel uploadFile) { try { if (_config.UploadConfig.UploadEnabled) { long maxUploadSize = _config.UploadConfig.MaxUploadSize; if (User.Identity.IsAuthenticated) { maxUploadSize = _config.UploadConfig.MaxUploadSizeBasic; IdentityUserInfo userInfo = await IdentityHelper.GetIdentityUserInfo(_config, User.Identity.Name); if (userInfo.AccountType == AccountType.Premium) { maxUploadSize = _config.UploadConfig.MaxUploadSizePremium; } } else { // Non-logged in users are defaulted to 1 day expiration uploadFile.options.ExpirationUnit = ExpirationUnit.Days; uploadFile.options.ExpirationLength = 1; } if (uploadFile.file.Length <= maxUploadSize) { // convert file to bytes long contentLength = uploadFile.file.Length; // Scan the file to detect a virus if (_config.UploadConfig.VirusScanEnable) { using (Stream fs = uploadFile.file.OpenReadStream()) { ClamClient clam = new ClamClient(_config.UploadConfig.ClamServer, _config.UploadConfig.ClamPort); clam.MaxStreamSize = maxUploadSize; ClamScanResult scanResult = await clam.SendAndScanFileAsync(fs); switch (scanResult.Result) { case ClamScanResults.Clean: break; case ClamScanResults.VirusDetected: return(Json(new { error = new { message = string.Format("Virus Detected: {0}. As per our <a href=\"{1}\">Terms of Service</a>, Viruses are not permited.", scanResult.InfectedFiles.First().VirusName, Url.SubRouteUrl("tos", "TOS.Index")) } })); case ClamScanResults.Error: return(Json(new { error = new { message = string.Format("Error scanning the file upload for viruses. {0}", scanResult.RawResult) } })); case ClamScanResults.Unknown: return(Json(new { error = new { message = string.Format("Unknown result while scanning the file upload for viruses. {0}", scanResult.RawResult) } })); } } } // Check content type restrictions (Only for encrypting server side if (!uploadFile.options.Encrypt) { if (_config.UploadConfig.RestrictedContentTypes.Contains(uploadFile.fileType) || _config.UploadConfig.RestrictedExtensions.Contains(uploadFile.fileExt)) { return(Json(new { error = new { message = "File Type Not Allowed" } })); } } using (Stream fs = uploadFile.file.OpenReadStream()) { Models.Upload upload = UploadHelper.SaveFile(_dbContext, _config, fs, uploadFile.fileType, contentLength, !uploadFile.options.Encrypt, uploadFile.options.ExpirationUnit, uploadFile.options.ExpirationLength, uploadFile.fileExt, uploadFile.iv, null, uploadFile.keySize, uploadFile.blockSize); if (upload != null) { if (User.Identity.IsAuthenticated) { Users.Models.User user = UserHelper.GetUser(_dbContext, User.Identity.Name); if (user != null) { upload.UserId = user.UserId; _dbContext.Entry(upload).State = EntityState.Modified; _dbContext.SaveChanges(); } } return(Json(new { result = new { name = upload.Url, url = Url.SubRouteUrl("u", "Upload.Download", new { file = upload.Url }), contentType = upload.ContentType, contentLength = StringHelper.GetBytesReadable(upload.ContentLength), deleteUrl = Url.SubRouteUrl("u", "Upload.DeleteByKey", new { file = upload.Url, key = upload.DeleteKey }), expirationUnit = uploadFile.options.ExpirationUnit.ToString(), expirationLength = uploadFile.options.ExpirationLength } })); } } return(Json(new { error = new { message = "Unable to upload file" } })); } else { return(Json(new { error = new { message = "File Too Large" } })); } } return(Json(new { error = new { message = "Uploads are disabled" } })); } catch (Exception ex) { return(Json(new { error = new { message = "Exception while uploading file: " + ex.GetFullMessage(true) } })); } }
public async Task <IActionResult> Download(string file) { if (_config.UploadConfig.DownloadEnabled) { ViewBag.Title = "Download " + file; string fileName = string.Empty; string url = string.Empty; string key = string.Empty; string iv = string.Empty; string contentType = string.Empty; long contentLength = 0; bool premiumAccount = false; DateTime dateUploaded = new DateTime(); Models.Upload upload = _dbContext.Uploads.Where(up => up.Url == file).FirstOrDefault(); if (upload != null) { // Check Expiration if (UploadHelper.CheckExpiration(upload)) { DeleteFile(upload); return(new StatusCodeResult(StatusCodes.Status404NotFound)); } upload.Downloads += 1; _dbContext.Entry(upload).State = EntityState.Modified; _dbContext.SaveChanges(); fileName = upload.FileName; url = upload.Url; key = upload.Key; iv = upload.IV; contentType = upload.ContentType; contentLength = upload.ContentLength; dateUploaded = upload.DateUploaded; if (User.Identity.IsAuthenticated) { IdentityUserInfo userInfo = await IdentityHelper.GetIdentityUserInfo(_config, User.Identity.Name); premiumAccount = userInfo.AccountType == AccountType.Premium; } if (!premiumAccount && upload.User != null) { IdentityUserInfo userInfo = await IdentityHelper.GetIdentityUserInfo(_config, upload.User.Username); premiumAccount = userInfo.AccountType == AccountType.Premium; } } else { return(new StatusCodeResult(StatusCodes.Status404NotFound)); } // We don't have the key, so we need to decrypt it client side if (string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(iv)) { DownloadViewModel model = new DownloadViewModel(); model.CurrentSub = Subdomain; model.FileName = file; model.ContentType = contentType; model.ContentLength = contentLength; model.IV = iv; model.Decrypt = true; return(View(model)); } else if (!premiumAccount && _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)); } else // We have the key, so that means server side decryption { // Check for the cache bool isCached = false; string modifiedSince = Request.Headers["If-Modified-Since"]; if (!string.IsNullOrEmpty(modifiedSince)) { DateTime modTime = new DateTime(); bool parsed = DateTime.TryParse(modifiedSince, out modTime); if (parsed) { if ((modTime - dateUploaded).TotalSeconds <= 1) { isCached = true; } } } if (isCached) { return(new StatusCodeResult(StatusCodes.Status304NotModified)); } else { string subDir = fileName[0].ToString(); string filePath = Path.Combine(_config.UploadConfig.UploadDirectory, subDir, fileName); long startByte = 0; long endByte = contentLength - 1; long length = contentLength; if (System.IO.File.Exists(filePath)) { #region Range Calculation // Are they downloading it by range? bool byRange = !string.IsNullOrEmpty(Request.Headers["Range"]); // We do not support ranges // check to see if we need to pass a specified range if (byRange) { long anotherStart = startByte; long anotherEnd = endByte; string[] arr_split = Request.Headers["Range"].ToString().Split(new char[] { '=' }); string range = arr_split[1]; // Make sure the client hasn't sent us a multibyte range if (range.IndexOf(",") > -1) { // (?) Shoud this be issued here, or should the first // range be used? Or should the header be ignored and // we output the whole content? Response.Headers.Add("Content-Range", "bytes " + startByte + "-" + endByte + "/" + contentLength); return(new StatusCodeResult(StatusCodes.Status416RequestedRangeNotSatisfiable)); } // If the range starts with an '-' we start from the beginning // If not, we forward the file pointer // And make sure to get the end byte if spesified if (range.StartsWith("-")) { // The n-number of the last bytes is requested anotherStart = startByte - Convert.ToInt64(range.Substring(1)); } else { arr_split = range.Split(new char[] { '-' }); anotherStart = Convert.ToInt64(arr_split[0]); long temp = 0; anotherEnd = (arr_split.Length > 1 && Int64.TryParse(arr_split[1].ToString(), out temp)) ? Convert.ToInt64(arr_split[1]) : contentLength; } /* Check the range and make sure it's treated according to the specs. * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html */ // End bytes can not be larger than $end. anotherEnd = (anotherEnd > endByte) ? endByte : anotherEnd; // Validate the requested range and return an error if it's not correct. if (anotherStart > anotherEnd || anotherStart > contentLength - 1 || anotherEnd >= contentLength) { Response.Headers.Add("Content-Range", "bytes " + startByte + "-" + endByte + "/" + contentLength); return(new StatusCodeResult(StatusCodes.Status416RequestedRangeNotSatisfiable)); } startByte = anotherStart; endByte = anotherEnd; length = endByte - startByte + 1; // Calculate new content length // Ranges are response of 206 Response.StatusCode = 206; } #endregion // Set Last Modified Response.GetTypedHeaders().LastModified = dateUploaded; // We accept ranges Response.Headers.Add("Accept-Ranges", "0-" + contentLength); // Notify the client the byte range we'll be outputting Response.Headers.Add("Content-Range", "bytes " + startByte + "-" + endByte + "/" + contentLength); // Notify the client the content length we'll be outputting Response.Headers.Add("Content-Length", length.ToString()); // Set the content type of this response Response.Headers.Add("Content-Type", contentType); // Create content disposition var cd = new System.Net.Mime.ContentDisposition { FileName = url, Inline = true }; Response.Headers.Add("Content-Disposition", cd.ToString()); // Read in the file FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read); // Reset file stream to starting position (or start of range) fs.Seek(startByte, SeekOrigin.Begin); try { // If the IV is set, and Key is set, then decrypt it while sending if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(iv)) { byte[] keyBytes = Encoding.UTF8.GetBytes(key); byte[] ivBytes = Encoding.UTF8.GetBytes(iv); return(new BufferedFileStreamResult(contentType, async(response) => await ResponseHelper.StreamToOutput(response, true, new AesCounterStream(fs, false, keyBytes, ivBytes), (int)length, _config.UploadConfig.ChunkSize), false)); } else // Otherwise just send it { // Send the file return(new BufferedFileStreamResult(contentType, async(response) => await ResponseHelper.StreamToOutput(response, true, fs, (int)length, _config.UploadConfig.ChunkSize), false)); } } catch (Exception ex) { _logger.LogWarning(ex, "Error in Download: {url}", new { url }); } } } return(new StatusCodeResult(StatusCodes.Status404NotFound)); } } return(new StatusCodeResult(StatusCodes.Status403Forbidden)); }
public async Task <IActionResult> Upload(UploadAPIv1Model model) { try { if (_config.UploadConfig.UploadEnabled) { if (model.file != null) { long maxUploadSize = _config.UploadConfig.MaxUploadSize; if (User.Identity.IsAuthenticated) { maxUploadSize = _config.UploadConfig.MaxUploadSizeBasic; IdentityUserInfo userInfo = await IdentityHelper.GetIdentityUserInfo(_config, User.Identity.Name); if (userInfo.AccountType == AccountType.Premium) { maxUploadSize = _config.UploadConfig.MaxUploadSizePremium; } } else { // Non-logged in users are defaulted to 1 day expiration model.expirationUnit = ExpirationUnit.Days; model.expirationLength = 1; } if (model.file.Length <= maxUploadSize) { // convert file to bytes string fileExt = FileHelper.GetFileExtension(model.file.FileName); long contentLength = model.file.Length; // Scan the file to detect a virus if (_config.UploadConfig.VirusScanEnable) { ClamClient clam = new ClamClient(_config.UploadConfig.ClamServer, _config.UploadConfig.ClamPort); clam.MaxStreamSize = maxUploadSize; ClamScanResult scanResult = await clam.SendAndScanFileAsync(model.file.OpenReadStream()); switch (scanResult.Result) { case ClamScanResults.Clean: break; case ClamScanResults.VirusDetected: return(Json(new { error = new { message = string.Format("Virus Detected: {0}. As per our <a href=\"{1}\">Terms of Service</a>, Viruses are not permited.", scanResult.InfectedFiles.First().VirusName, Url.SubRouteUrl("tos", "TOS.Index")) } })); case ClamScanResults.Error: break; case ClamScanResults.Unknown: break; } } // Need to grab the contentType if it's empty if (string.IsNullOrEmpty(model.contentType)) { model.contentType = model.file.ContentType; if (string.IsNullOrEmpty(model.contentType)) { using (Stream fileStream = model.file.OpenReadStream()) { fileStream.Seek(0, SeekOrigin.Begin); FileType fileType = fileStream.GetFileType(); if (fileType != null) { model.contentType = fileType.Mime; } if (string.IsNullOrEmpty(model.contentType)) { model.contentType = "application/octet-stream"; } } } } // Check content type restrictions (Only for encrypting server side if (model.encrypt || !string.IsNullOrEmpty(model.key)) { if (_config.UploadConfig.RestrictedContentTypes.Contains(model.contentType) || _config.UploadConfig.RestrictedExtensions.Contains(fileExt)) { return(Json(new { error = new { message = "File Type Not Allowed" } })); } } // Initialize the key size and block size if empty if (model.keySize <= 0) { model.keySize = _config.UploadConfig.KeySize; } if (model.blockSize <= 0) { model.blockSize = _config.UploadConfig.BlockSize; } // Save the file data Upload.Models.Upload upload = UploadHelper.SaveFile(_dbContext, _config, model.file.OpenReadStream(), model.contentType, contentLength, model.encrypt, model.expirationUnit, model.expirationLength, fileExt, model.iv, model.key, model.keySize, model.blockSize); if (upload != null) { string fileKey = upload.Key; // Associate this with the user if they provided an auth key if (User.Identity.IsAuthenticated) { User foundUser = UserHelper.GetUser(_dbContext, User.Identity.Name); if (foundUser != null) { upload.UserId = foundUser.UserId; _dbContext.Entry(upload).State = EntityState.Modified; _dbContext.SaveChanges(); } } // Generate delete key only if asked to if (!model.genDeletionKey) { upload.DeleteKey = string.Empty; _dbContext.Entry(upload).State = EntityState.Modified; _dbContext.SaveChanges(); } // remove the key if we don't want to save it if (!model.saveKey) { upload.Key = null; _dbContext.Entry(upload).State = EntityState.Modified; _dbContext.SaveChanges(); } // Pull all the information together string fullUrl = Url.SubRouteUrl("u", "Upload.Download", new { file = upload.Url }); var returnData = new { url = (model.saveKey || string.IsNullOrEmpty(fileKey)) ? fullUrl : fullUrl + "#" + fileKey, fileName = upload.Url, contentType = upload.ContentType, contentLength = upload.ContentLength, key = fileKey, keySize = upload.KeySize, iv = upload.IV, blockSize = upload.BlockSize, maxDownloads = upload.MaxDownloads, expirationDate = upload.ExpireDate, deletionKey = upload.DeleteKey }; return(Json(new { result = returnData })); } return(Json(new { error = new { message = "Unable to save file" } })); } else { return(Json(new { error = new { message = "File Too Large" } })); } } return(Json(new { error = new { message = "Invalid Upload Request" } })); } return(Json(new { error = new { message = "Uploads are Disabled" } })); } catch (Exception ex) { return(Json(new { error = new { message = "Exception: " + ex.Message } })); } }