private async Task StoreDataAsync(FileIdentifier id, Stream dataStream, UploadPassword passwordSetting, CancellationToken cancellationToken) { UploadProgress progress = this.GetProgressObject(id); // Copy with progress using (Stream outputStream = this._fileWriter.OpenWriteStream(this._fileStore.GetDataFile(id))) { if (passwordSetting.Enable == true && !String.IsNullOrEmpty(passwordSetting.Password)) { using (Aes crypto = CryptoFactory.CreateCrypto(passwordSetting.Password)) { ICryptoTransform encryptor = crypto.CreateEncryptor(); CryptoMetadata.WriteMetadata(outputStream, crypto); using (CryptoStream cryptoStream = new CryptoStream(outputStream, encryptor, CryptoStreamMode.Write, true)) { await CopyStreamWithProgress(cryptoStream); } } } else { await CopyStreamWithProgress(outputStream); } } async Task CopyStreamWithProgress(Stream outputStream) { using (Stream inputStream = dataStream) { int read; byte[] buffer = new byte[4096]; while ((read = await inputStream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0) { progress.Current += read; await outputStream.WriteAsync(buffer, 0, read, cancellationToken).ConfigureAwait(false); } } } }
private async Task ProcessFormSectionAsync(UploadContext uploadContext, MultipartSection section, ContentDispositionHeaderValue contentDisposition) { string cleanName = HeaderUtilities.RemoveQuotes(contentDisposition.Name).Value; StoredMetadataFactory metadataFactory = uploadContext.MetadataFactory; UploadPassword passwordSetting = uploadContext.PasswordSetting; switch (cleanName) { case nameof(UploadModel.IsReservation): string isReservationRaw = await ReadString(); metadataFactory.SetIsReservation(Boolean.Parse(isReservationRaw)); break; case nameof(UploadModel.Expiration): string dateTimeRaw = await ReadString(); // MVC we send date as roundtrip metadataFactory.SetExpiration(DateTime.ParseExact(dateTimeRaw, "o", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind)); return; case nameof(UploadModel.FileIdentifier): string rawId = await ReadString(); FileIdentifier formId = FileIdentifier.FromString(rawId); if (formId != uploadContext.Identifier) { throw new InvalidOperationException($"ID mismatch: '{formId}' (received) != '{uploadContext.Identifier}' (expected)"); } return; case nameof(UploadModel.Password): string password = await ReadString(); metadataFactory.SetPassword(password); passwordSetting.Password = password; EnsureFileNotUploaded(); return; case nameof(UploadModel.EnablePasswordProtection): bool passwordSettingWasSet = passwordSetting.IsSet; string enablePasswordProtectionRaw = await ReadString(); bool enablePasswordProtection = String.Equals(Boolean.TrueString, enablePasswordProtectionRaw, StringComparison.OrdinalIgnoreCase); metadataFactory.SetEnablePasswordProtection(enablePasswordProtection); passwordSetting.SetEnabled(enablePasswordProtection); if (!passwordSettingWasSet) { EnsureFileNotUploaded(); } return; case nameof(UploadModel.Sender) + "." + nameof(ContactInformation.Name): string name = await ReadString(); metadataFactory.SetSenderName(name); return; case nameof(UploadModel.Sender) + "." + nameof(ContactInformation.EmailAddress): string emailAddress = await ReadString(); metadataFactory.SetSenderEmail(emailAddress); return; // Browsers don't actually send the file size in the request, but we can derive it from the Content-Length. // However, that is not very accurate and if we use some javascript to send a more accurate file size, we use that. case nameof(UploadModel.SuggestedFileSize): if (Int64.TryParse(await ReadString(), out var size) && size > 0) { this.GetProgressObject(uploadContext.Identifier).Total = size; } return; default: this._logger.LogWarning(LogEvents.UploadIncomplete, "{Identifier}: Unknown form field '{Field}'", uploadContext.Identifier, contentDisposition.Name); break; } async Task <string> ReadString() { using (StreamReader sr = new StreamReader(section.Body)) { return(await sr.ReadToEndAsync()); } } void EnsureFileNotUploaded() { bool needToValidate = !String.IsNullOrEmpty(passwordSetting.Password) && passwordSetting.Enable == true; if (needToValidate && uploadContext.HasUploadedFile) { this._logger.LogError(LogEvents.UploadPasswordAfterFileUpload, "{Identifier}: The upload password is set after the file is uploaded. The file is not encrypted. Terminating upload.", uploadContext.Identifier); throw new UploadCryptoArgumentOrderException("File uploaded before password has been set"); } } }