/// <summary> /// Decrypts a single attachment /// </summary> /// <returns> /// Returns true if the attachment was handled successfully (decrypted correctly or skipped). Returns /// false if decryption should be cancelled for the request /// </returns> public DecryptResult DecryptAttachment(ref Attachment attachment, IContentEncryptionUi ui) { IContentEncryption encryption = null; if (_encryptionMap.TryGetValue(GetEncryptionMapUniqueID(attachment), out encryption)) { // Check encryptor is for the correct file if (attachment.FileName != encryption.Name) { _encryptionMap.Remove(attachment.Id); encryption = CreateEncryptor(attachment); if (encryption == null) { return DecryptResult.Cancel; } _encryptionMap.Add(attachment.Id, encryption); } ui.ModifyPassword = encryption.ModifyPassword; ui.OpenPassword = encryption.OpenPassword; } else { encryption = CreateEncryptor(attachment); } if (encryption == null) { // Unhandled decryption format, or unencrypted file. Exit with success return DecryptResult.Ok; } // TODO: AP: messy bool keepTrying = true; encryption.AllowDefaultPassword = true; while (keepTrying) { try { DecryptResult result = encryption.Decrypt(ui); switch (result) { case DecryptResult.Cancel: case DecryptResult.Skip: return result; case DecryptResult.Ok: keepTrying = false; bool wasReadOnly = attachment.File.WasReadOnly; attachment.File = new FCS.Lite.Interface.File(attachment.File.FileName, attachment.File.DisplayName, attachment.Id);// Re-create the binary data attachment.File.WasReadOnly = wasReadOnly; encryption.WasReadOnly = wasReadOnly; attachment.File.Password = ui.OpenPassword; break; } } catch (UnauthorizedAccessException ex) { ShortTermPasswordCache.Instance.ClearFor(attachment); // User passed in incorrect passwords Logger.LogError(string.Format("Failed to decrypt attachment \"{0}\" - Incorrect password", attachment.Name)); Logger.LogError(ex); switch (ui.OnIncorrectPassword(encryption)) { case DecryptionErrorAction.Cancel: return DecryptResult.Cancel; case DecryptionErrorAction.Skip: return DecryptResult.Skip; case DecryptionErrorAction.Retry: ui.ModifyPassword = string.Empty; ui.OpenPassword = string.Empty; encryption.AllowDefaultPassword = false; encryption.ClearPasswords(); break; } } catch (System.Threading.ThreadAbortException) { // We are aware that this exception can be thrown. // We abort the engine thread if user presses "Send" before discovery is complete. // Calling "Abort" causes ThreadAbortException. // So nothing to worry about. ; } catch (Exception ex) { Logger.LogError(ex); System.Runtime.InteropServices.COMException comex = null; if (ex is System.Runtime.InteropServices.COMException) comex = (System.Runtime.InteropServices.COMException)ex; if (comex != null && (uint)(comex.ErrorCode) == 0x800a11fd) { LastErrorText = "Unable to clean or convert - file is password-protected and marked as Final."; } else { // Some problem occurred during encryption string errorTxt = string.Format("Failed to decrypt attachment \"{0}\"", attachment.Name); Logger.LogError(errorTxt); LastErrorText = errorTxt; } switch (ui.OnDecryptionError(encryption)) { case DecryptionErrorAction.Cancel: return DecryptResult.Cancel; case DecryptionErrorAction.Skip: return DecryptResult.Skip; case DecryptionErrorAction.Retry: ui.ModifyPassword = string.Empty; ui.OpenPassword = string.Empty; encryption.ClearPasswords(); break; } } } // Decryption succeeded - associate the decryptor with the attachment, so it can be // re-encrypted after scanning ui.ModifyPassword = string.Empty; ui.OpenPassword = string.Empty; string uniqueID = GetEncryptionMapUniqueID(attachment); AssociateEncryptorWithData(uniqueID, encryption); AddEncryptionProperty(ref attachment, encryption); return DecryptResult.Ok; }