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 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  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.Data;
  20. using Teknik.Filters;
  21. using Teknik.Logging;
  22. using Teknik.Utilities;
  23. namespace Teknik.Areas.API.V1.Controllers
  24. {
  25. [Authorize(Policy = "WriteAPI")]
  26. public class UploadAPIv1Controller : APIv1Controller
  27. {
  28. public UploadAPIv1Controller(ILogger<Logger> logger, Config config, TeknikEntities dbContext) : base(logger, config, dbContext) { }
  29. [HttpPost]
  30. [AllowAnonymous]
  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. // Scan the file to detect a virus
  73. if (_config.UploadConfig.VirusScanEnable)
  74. {
  75. ClamClient clam = new ClamClient(_config.UploadConfig.ClamServer, _config.UploadConfig.ClamPort);
  76. clam.MaxStreamSize = maxUploadSize;
  77. ClamScanResult scanResult = await clam.SendAndScanFileAsync(model.file.OpenReadStream());
  78. switch (scanResult.Result)
  79. {
  80. case ClamScanResults.Clean:
  81. break;
  82. case ClamScanResults.VirusDetected:
  83. 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")) } });
  84. case ClamScanResults.Error:
  85. break;
  86. case ClamScanResults.Unknown:
  87. break;
  88. }
  89. }
  90. // Need to grab the contentType if it's empty
  91. if (string.IsNullOrEmpty(model.contentType))
  92. {
  93. model.contentType = model.file.ContentType;
  94. if (string.IsNullOrEmpty(model.contentType))
  95. {
  96. using (Stream fileStream = model.file.OpenReadStream())
  97. {
  98. fileStream.Seek(0, SeekOrigin.Begin);
  99. FileType fileType = fileStream.GetFileType();
  100. if (fileType != null)
  101. model.contentType = fileType.Mime;
  102. if (string.IsNullOrEmpty(model.contentType))
  103. {
  104. model.contentType = "application/octet-stream";
  105. }
  106. }
  107. }
  108. }
  109. // Check content type restrictions (Only for encrypting server side
  110. if (model.encrypt || !string.IsNullOrEmpty(model.key))
  111. {
  112. if (_config.UploadConfig.RestrictedContentTypes.Contains(model.contentType) || _config.UploadConfig.RestrictedExtensions.Contains(fileExt))
  113. {
  114. return Json(new { error = new { message = "File Type Not Allowed" } });
  115. }
  116. }
  117. // Initialize the key size and block size if empty
  118. if (model.keySize <= 0)
  119. model.keySize = _config.UploadConfig.KeySize;
  120. if (model.blockSize <= 0)
  121. model.blockSize = _config.UploadConfig.BlockSize;
  122. // Save the file data
  123. 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);
  124. if (upload != null)
  125. {
  126. string fileKey = upload.Key;
  127. // Associate this with the user if they provided an auth key
  128. if (User.Identity.IsAuthenticated)
  129. {
  130. User foundUser = UserHelper.GetUser(_dbContext, User.Identity.Name);
  131. if (foundUser != null)
  132. {
  133. upload.UserId = foundUser.UserId;
  134. _dbContext.Entry(upload).State = EntityState.Modified;
  135. _dbContext.SaveChanges();
  136. }
  137. }
  138. // Generate delete key only if asked to
  139. if (!model.genDeletionKey)
  140. {
  141. upload.DeleteKey = string.Empty;
  142. _dbContext.Entry(upload).State = EntityState.Modified;
  143. _dbContext.SaveChanges();
  144. }
  145. // remove the key if we don't want to save it
  146. if (!model.saveKey)
  147. {
  148. upload.Key = null;
  149. _dbContext.Entry(upload).State = EntityState.Modified;
  150. _dbContext.SaveChanges();
  151. }
  152. // Pull all the information together
  153. string fullUrl = Url.SubRouteUrl("u", "Upload.Download", new { file = upload.Url });
  154. var returnData = new
  155. {
  156. url = (model.saveKey || string.IsNullOrEmpty(fileKey)) ? fullUrl : fullUrl + "#" + fileKey,
  157. fileName = upload.Url,
  158. contentType = upload.ContentType,
  159. contentLength = upload.ContentLength,
  160. key = fileKey,
  161. keySize = upload.KeySize,
  162. iv = upload.IV,
  163. blockSize = upload.BlockSize,
  164. maxDownloads = upload.MaxDownloads,
  165. expirationDate = upload.ExpireDate,
  166. deletionKey = upload.DeleteKey
  167. };
  168. return Json(new { result = returnData });
  169. }
  170. return Json(new { error = new { message = "Unable to save file" } });
  171. }
  172. else
  173. {
  174. return Json(new { error = new { message = "File Too Large. Max file size is " + StringHelper.GetBytesReadable(maxUploadSize) } });
  175. }
  176. }
  177. return Json(new { error = new { message = "Invalid Upload Request" } });
  178. }
  179. return Json(new { error = new { message = "Uploads are Disabled" } });
  180. }
  181. catch (Exception ex)
  182. {
  183. return Json(new { error = new { message = "Exception: " + ex.Message } });
  184. }
  185. }
  186. }
  187. }