public IActionResult DownloadAudioFile(string projectId, string wordId, string fileName) { // if we require authorization and authentication for audio files, the frontend cannot just use the api // endpoint as the src //if (!_permissionService.IsProjectAuthorized("1", HttpContext)) //{ // return Forbid(); //} // Sanitize user input if (!Sanitization.SanitizeId(projectId) || !Sanitization.SanitizeId(wordId) || !Sanitization.SanitizeFileName(fileName)) { return(new UnsupportedMediaTypeResult()); } var filePath = FileStorage.GenerateAudioFilePath(projectId, fileName); var file = System.IO.File.OpenRead(filePath); if (file is null) { return(BadRequest("The file does not exist.")); } return(File(file, "application/octet-stream")); }
// These internal methods are extracted for unit testing internal async Task <IActionResult> ExportLiftFile(string projectId, string userId) { if (!await _permissionService.HasProjectPermission(HttpContext, Permission.ImportExport)) { return(Forbid()); } // Sanitize projectId if (!Sanitization.SanitizeId(projectId)) { return(new UnsupportedMediaTypeResult()); } // Ensure project exists var proj = await _projRepo.GetProject(projectId); if (proj is null) { return(NotFound(projectId)); } // Check if another export started if (_liftService.IsExportInProgress(userId)) { return(Conflict()); } // Store in-progress status for the export _liftService.SetExportInProgress(userId, true); try { // Ensure project has words var words = await _wordRepo.GetAllWords(projectId); if (words.Count == 0) { _liftService.SetExportInProgress(userId, false); return(BadRequest("No words to export.")); } // Export the data to a zip, read into memory, and delete zip var exportedFilepath = await CreateLiftExport(projectId); // Store the temporary path to the exported file for user to download later. _liftService.StoreExport(userId, exportedFilepath); await _notifyService.Clients.All.SendAsync("DownloadReady", userId); return(Ok(projectId)); } catch { _liftService.SetExportInProgress(userId, false); throw; } }
public async Task <IActionResult> UploadAudioFile(string projectId, string wordId, [FromForm] FileUpload fileUpload) { if (!await _permissionService.HasProjectPermission(HttpContext, Permission.WordEntry)) { return(Forbid()); } // sanitize user input if (!Sanitization.SanitizeId(projectId) || !Sanitization.SanitizeId(wordId)) { return(new UnsupportedMediaTypeResult()); } var file = fileUpload.File; if (file is null) { return(BadRequest("Null File")); } // Ensure file is not empty if (file.Length == 0) { return(BadRequest("Empty File")); } // This path should be unique even though it is only based on the Word ID because currently, a new // Word is created each time an audio file is uploaded. fileUpload.FilePath = FileStorage.GenerateAudioFilePathForWord(projectId, wordId); // Copy the file data to a new local file await using (var fs = new FileStream(fileUpload.FilePath, FileMode.Create)) { await file.CopyToAsync(fs); } // Add the relative path to the audio field var word = await _wordRepo.GetWord(projectId, wordId); if (word is null) { return(NotFound(wordId)); } word.Audio.Add(Path.GetFileName(fileUpload.FilePath)); // Update the word with new audio file await _wordService.Update(projectId, wordId, word); return(Ok(word.Id)); }
public async Task <IActionResult> CanUploadLift(string projectId) { if (!await _permissionService.HasProjectPermission(HttpContext, Permission.ImportExport)) { return(Forbid()); } // Sanitize user input if (!Sanitization.SanitizeId(projectId)) { return(new UnsupportedMediaTypeResult()); } return(Ok(await _projRepo.CanImportLift(projectId))); }
public async Task <IActionResult> DeleteAudioFile(string projectId, string wordId, string fileName) { if (!await _permissionService.HasProjectPermission(HttpContext, Permission.WordEntry)) { return(Forbid()); } // sanitize user input if (!Sanitization.SanitizeId(projectId) || !Sanitization.SanitizeId(wordId)) { return(new UnsupportedMediaTypeResult()); } var newWord = await _wordService.Delete(projectId, wordId, fileName); if (newWord is not null) { return(Ok(newWord.Id)); } return(NotFound("The project was found, but the word audio was not deleted")); }
public void TestInvalidIds(string id) { Assert.False(Sanitization.SanitizeId(id)); }
public void TestValidIds(string id) { Assert.That(Sanitization.SanitizeId(id)); }
[RequestSizeLimit(250_000_000)] // 250MB. public async Task <IActionResult> UploadLiftFile(string projectId, [FromForm] FileUpload fileUpload) { if (!await _permissionService.HasProjectPermission(HttpContext, Permission.ImportExport)) { return(Forbid()); } // Sanitize projectId if (!Sanitization.SanitizeId(projectId)) { return(new UnsupportedMediaTypeResult()); } // Ensure Lift file has not already been imported. if (!await _projRepo.CanImportLift(projectId)) { return(BadRequest("A Lift file has already been uploaded.")); } var liftStoragePath = FileStorage.GenerateLiftImportDirPath(projectId); // Clear out any files left by a failed import RobustIO.DeleteDirectoryAndContents(liftStoragePath); var file = fileUpload.File; if (file is null) { return(BadRequest("Null File")); } // Ensure file is not empty if (file.Length == 0) { return(BadRequest("Empty File")); } // Copy zip file data to a new temporary file fileUpload.FilePath = Path.GetTempFileName(); await using (var fs = new FileStream(fileUpload.FilePath, FileMode.OpenOrCreate)) { await file.CopyToAsync(fs); } // Make temporary destination for extracted files var extractDir = FileOperations.GetRandomTempDir(); // Extract the zip to new created directory. FileOperations.ExtractZipFile(fileUpload.FilePath, extractDir, true); // Check number of directories extracted var directoriesExtracted = Directory.GetDirectories(extractDir); var extractedDirPath = ""; switch (directoriesExtracted.Length) { // If there was one directory, we're good case 1: { extractedDirPath = directoriesExtracted.First(); break; } // If there were two, and there was a __MACOSX directory, ignore it case 2: { var numDirs = 0; foreach (var dir in directoriesExtracted) { if (dir.EndsWith("__MACOSX")) { Directory.Delete(dir, true); } else // This directory probably matters { extractedDirPath = dir; numDirs++; } } // Both directories seemed important if (numDirs == 2) { return(BadRequest("Your zip file should have one directory.")); } break; } // There were 0 or more than 2 directories default: { return(BadRequest( "Your zip file structure has the wrong number of directories.")); } } // Copy the extracted contents into the persistent storage location for the project. FileOperations.CopyDirectory(extractedDirPath, liftStoragePath); Directory.Delete(extractDir, true); // Search for the lift file within the extracted files var extractedLiftNameArr = Directory.GetFiles(liftStoragePath); var extractedLiftPath = Array.FindAll(extractedLiftNameArr, x => x.EndsWith(".lift")); if (extractedLiftPath.Length > 1) { return(BadRequest("More than one .lift file detected.")); } if (extractedLiftPath.Length == 0) { return(BadRequest("No lift files detected.")); } int liftParseResult; // Sets the projectId of our parser to add words to that project var liftMerger = _liftService.GetLiftImporterExporter(projectId, _wordRepo); try { // Add character set to project from ldml file var proj = await _projRepo.GetProject(projectId); if (proj is null) { return(NotFound(projectId)); } _liftService.LdmlImport( Path.Combine(liftStoragePath, "WritingSystems"), proj.VernacularWritingSystem.Bcp47, _projRepo, proj); var parser = new LiftParser <LiftObject, LiftEntry, LiftSense, LiftExample>(liftMerger); // Import words from lift file liftParseResult = parser.ReadLiftFile(extractedLiftPath.FirstOrDefault()); await liftMerger.SaveImportEntries(); } catch (Exception e) { _logger.LogError(e, $"Error importing lift file {fileUpload.Name} into project {projectId}."); return(BadRequest("Error processing the lift data. Contact support for help.")); } // Store that we have imported Lift data already for this project to signal the frontend // not to attempt to import again. var project = await _projRepo.GetProject(projectId); if (project is null) { return(NotFound(projectId)); } project.LiftImported = true; await _projRepo.Update(projectId, project); return(Ok(liftParseResult)); }