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.

UploadAPIv1Controller.cs 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Threading.Tasks;
  6. using Microsoft.AspNetCore.Authorization;
  7. using Microsoft.AspNetCore.Mvc;
  8. using Microsoft.EntityFrameworkCore;
  9. using Microsoft.Extensions.Logging;
  10. using MimeDetective;
  11. using MimeDetective.Extensions;
  12. using nClam;
  13. using Teknik.Areas.API.Controllers;
  14. using Teknik.Areas.API.V1.Models;
  15. using Teknik.Areas.Upload;
  16. using Teknik.Areas.Users.Models;
  17. using Teknik.Areas.Users.Utility;
  18. using Teknik.Configuration;
  19. using Teknik.ContentScanningService;
  20. using Teknik.Data;
  21. using Teknik.Filters;
  22. using Teknik.Logging;
  23. using Teknik.Utilities;
  24. namespace Teknik.Areas.API.V1.Controllers
  25. {
  26. [Authorize(Policy = "WriteAPI")]
  27. public class UploadAPIv1Controller : APIv1Controller
  28. {
  29. public UploadAPIv1Controller(ILogger<Logger> logger, Config config, TeknikEntities dbContext) : base(logger, config, dbContext) { }
  30. [HttpPost]
  31. [TrackPageView]
  32. public async Task<IActionResult> Upload(UploadAPIv1Model model)
  33. {
  34. try
  35. {
  36. if (_config.UploadConfig.UploadEnabled)
  37. {
  38. if (model.file != null)
  39. {
  40. long maxUploadSize = _config.UploadConfig.MaxUploadSize;
  41. if (User.Identity.IsAuthenticated)
  42. {
  43. maxUploadSize = _config.UploadConfig.MaxUploadSizeBasic;
  44. long maxTotalSize = _config.UploadConfig.MaxTotalSizeBasic;
  45. IdentityUserInfo userInfo = await IdentityHelper.GetIdentityUserInfo(_config, User.Identity.Name);
  46. if (userInfo.AccountType == AccountType.Premium)
  47. {
  48. maxUploadSize = _config.UploadConfig.MaxUploadSizePremium;
  49. maxTotalSize = _config.UploadConfig.MaxTotalSizePremium;
  50. }
  51. // Check account total limits
  52. var user = UserHelper.GetUser(_dbContext, User.Identity.Name);
  53. if (user.UploadSettings.MaxUploadStorage != null)
  54. maxTotalSize = user.UploadSettings.MaxUploadStorage.Value;
  55. var userUploadSize = user.Uploads.Sum(u => u.ContentLength);
  56. if (userUploadSize + model.file.Length > maxTotalSize)
  57. {
  58. return Json(new { error = new { message = string.Format("Account storage limit exceeded: {0} / {1}", StringHelper.GetBytesReadable(userUploadSize + model.file.Length), StringHelper.GetBytesReadable(maxTotalSize)) } });
  59. }
  60. }
  61. else
  62. {
  63. // Non-logged in users are defaulted to 1 day expiration
  64. model.expirationUnit = ExpirationUnit.Days;
  65. model.expirationLength = 1;
  66. }
  67. if (model.file.Length <= maxUploadSize)
  68. {
  69. // convert file to bytes
  70. string fileExt = FileHelper.GetFileExtension(model.file.FileName);
  71. long contentLength = model.file.Length;
  72. using (var fs = model.file.OpenReadStream())
  73. {
  74. ScanResult scanResult = null;
  75. // Scan the file to detect a virus
  76. if (_config.UploadConfig.ClamConfig.Enabled)
  77. {
  78. var clamScanner = new ClamScanner(_config);
  79. scanResult = await clamScanner.ScanFile(fs);
  80. }
  81. // Scan the files against an endpoint based on hash
  82. if (_config.UploadConfig.HashScanConfig.Enabled && (scanResult == null || scanResult.ResultType == ScanResultType.Clean))
  83. {
  84. var hashScanner = new HashScanner(_config);
  85. scanResult = await hashScanner.ScanFile(fs);
  86. }
  87. switch (scanResult?.ResultType)
  88. {
  89. case ScanResultType.Clean:
  90. break;
  91. case ScanResultType.VirusDetected:
  92. 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.RawResult, Url.SubRouteUrl("tos", "TOS.Index")) } });
  93. case ScanResultType.ChildPornography:
  94. return Json(new { error = new { message = string.Format("Child Pornography Detected: As per our <a href=\"{0}\">Terms of Service</a>, Child Pornography is strictly not permited.", Url.SubRouteUrl("tos", "TOS.Index")) } });
  95. case ScanResultType.Error:
  96. return Json(new { error = new { message = string.Format("Error scanning the file upload. {0}", scanResult.RawResult) } });
  97. case ScanResultType.Unknown:
  98. return Json(new { error = new { message = string.Format("Unknown result while scanning the file upload. {0}", scanResult.RawResult) } });
  99. }
  100. // Need to grab the contentType if it's empty
  101. if (string.IsNullOrEmpty(model.contentType))
  102. {
  103. model.contentType = model.file.ContentType;
  104. if (string.IsNullOrEmpty(model.contentType))
  105. {
  106. fs.Seek(0, SeekOrigin.Begin);
  107. FileType fileType = fs.GetFileType();
  108. if (fileType != null)
  109. model.contentType = fileType.Mime;
  110. if (string.IsNullOrEmpty(model.contentType))
  111. {
  112. model.contentType = "application/octet-stream";
  113. }
  114. }
  115. }
  116. // Check content type restrictions (Only for encrypting server side
  117. if (model.encrypt || !string.IsNullOrEmpty(model.key))
  118. {
  119. if (_config.UploadConfig.RestrictedContentTypes.Contains(model.contentType) || _config.UploadConfig.RestrictedExtensions.Contains(fileExt))
  120. {
  121. return Json(new { error = new { message = "File Type Not Allowed" } });
  122. }
  123. }
  124. // Initialize the key size and block size if empty
  125. if (model.keySize <= 0)
  126. model.keySize = _config.UploadConfig.KeySize;
  127. if (model.blockSize <= 0)
  128. model.blockSize = _config.UploadConfig.BlockSize;
  129. // Save the file data
  130. Upload.Models.Upload upload = UploadHelper.SaveFile(_dbContext, _config, fs, model.contentType, contentLength, model.encrypt, model.expirationUnit, model.expirationLength, fileExt, model.iv, model.key, model.keySize, model.blockSize);
  131. if (upload != null)
  132. {
  133. string fileKey = upload.Key;
  134. // Associate this with the user if they provided an auth key
  135. if (User.Identity.IsAuthenticated)
  136. {
  137. User foundUser = UserHelper.GetUser(_dbContext, User.Identity.Name);
  138. if (foundUser != null)
  139. {
  140. upload.UserId = foundUser.UserId;
  141. _dbContext.Entry(upload).State = EntityState.Modified;
  142. _dbContext.SaveChanges();
  143. }
  144. }
  145. // Generate delete key only if asked to
  146. if (!model.genDeletionKey)
  147. {
  148. upload.DeleteKey = string.Empty;
  149. _dbContext.Entry(upload).State = EntityState.Modified;
  150. _dbContext.SaveChanges();
  151. }
  152. // remove the key if we don't want to save it
  153. if (!model.saveKey)
  154. {
  155. upload.Key = null;
  156. _dbContext.Entry(upload).State = EntityState.Modified;
  157. _dbContext.SaveChanges();
  158. }
  159. // Pull all the information together
  160. string fullUrl = Url.SubRouteUrl("u", "Upload.Download", new { file = upload.Url });
  161. var returnData = new
  162. {
  163. url = (model.saveKey || string.IsNullOrEmpty(fileKey)) ? fullUrl : fullUrl + "#" + fileKey,
  164. fileName = upload.Url,
  165. contentType = upload.ContentType,
  166. contentLength = upload.ContentLength,
  167. key = fileKey,
  168. keySize = upload.KeySize,
  169. iv = upload.IV,
  170. blockSize = upload.BlockSize,
  171. maxDownloads = upload.MaxDownloads,
  172. expirationDate = upload.ExpireDate,
  173. deletionKey = upload.DeleteKey
  174. };
  175. return Json(new { result = returnData });
  176. }
  177. }
  178. return Json(new { error = new { message = "Unable to save file" } });
  179. }
  180. else
  181. {
  182. return Json(new { error = new { message = "File Too Large. Max file size is " + StringHelper.GetBytesReadable(maxUploadSize) } });
  183. }
  184. }
  185. return Json(new { error = new { message = "Invalid Upload Request" } });
  186. }
  187. return Json(new { error = new { message = "Uploads are Disabled" } });
  188. }
  189. catch (Exception ex)
  190. {
  191. return Json(new { error = new { message = "Exception: " + ex.Message } });
  192. }
  193. }
  194. }
  195. }