/// <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;
		}