summaryrefslogtreecommitdiffstats
path: root/mailnews/compose/src/nsMsgAttachmentHandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mailnews/compose/src/nsMsgAttachmentHandler.cpp')
-rw-r--r--mailnews/compose/src/nsMsgAttachmentHandler.cpp1383
1 files changed, 1383 insertions, 0 deletions
diff --git a/mailnews/compose/src/nsMsgAttachmentHandler.cpp b/mailnews/compose/src/nsMsgAttachmentHandler.cpp
new file mode 100644
index 000000000..f36f2e29b
--- /dev/null
+++ b/mailnews/compose/src/nsMsgAttachmentHandler.cpp
@@ -0,0 +1,1383 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIPrefLocalizedString.h"
+#include "nsILineInputStream.h"
+#include "nsMsgAttachmentHandler.h"
+#include "prmem.h"
+#include "nsMsgCopy.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "nsMsgSend.h"
+#include "nsMsgCompUtils.h"
+#include "nsMsgI18N.h"
+#include "nsURLFetcher.h"
+#include "nsMimeTypes.h"
+#include "nsMsgCompCID.h"
+#include "nsIMsgMessageService.h"
+#include "nsMsgUtils.h"
+#include "nsMsgPrompts.h"
+#include "nsTextFormatter.h"
+#include "nsIPrompt.h"
+#include "nsITextToSubURI.h"
+#include "nsIURL.h"
+#include "nsIFileURL.h"
+#include "nsNetCID.h"
+#include "nsIMimeStreamConverter.h"
+#include "nsMsgMimeCID.h"
+#include "nsNetUtil.h"
+#include "nsNativeCharsetUtils.h"
+#include "nsComposeStrings.h"
+#include "nsIZipWriter.h"
+#include "nsIDirectoryEnumerator.h"
+#include "mozilla/Services.h"
+#include "mozilla/mailnews/MimeEncoder.h"
+#include "nsIPrincipal.h"
+
+///////////////////////////////////////////////////////////////////////////
+// Mac Specific Attachment Handling for AppleDouble Encoded Files
+///////////////////////////////////////////////////////////////////////////
+#ifdef XP_MACOSX
+
+#define AD_WORKING_BUFF_SIZE FILE_IO_BUFFER_SIZE
+
+extern void MacGetFileType(nsIFile *fs, bool *useDefault, char **type, char **encoding);
+
+#include "nsILocalFileMac.h"
+
+/* static */
+nsresult nsSimpleZipper::Zip(nsIFile *aInputFile, nsIFile *aOutputFile)
+{
+ // create zipwriter object
+ nsresult rv;
+ nsCOMPtr<nsIZipWriter> zipWriter = do_CreateInstance("@mozilla.org/zipwriter;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = zipWriter->Open(aOutputFile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = AddToZip(zipWriter, aInputFile, EmptyCString());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // we're done.
+ zipWriter->Close();
+ return rv;
+}
+
+/* static */
+nsresult nsSimpleZipper::AddToZip(nsIZipWriter *aZipWriter,
+ nsIFile *aFile,
+ const nsACString &aPath)
+{
+ // find out the path this file/dir should have in the zip
+ nsCString leafName;
+ aFile->GetNativeLeafName(leafName);
+ nsCString currentPath(aPath);
+ currentPath += leafName;
+
+ bool isDirectory;
+ aFile->IsDirectory(&isDirectory);
+ // append slash for a directory entry
+ if (isDirectory)
+ currentPath.Append('/');
+
+ // add the file or directory entry to the zip
+ nsresult rv = aZipWriter->AddEntryFile(currentPath, nsIZipWriter::COMPRESSION_DEFAULT, aFile, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // if it's a directory, add all its contents too
+ if (isDirectory) {
+ nsCOMPtr<nsISimpleEnumerator> e;
+ nsresult rv = aFile->GetDirectoryEntries(getter_AddRefs(e));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIDirectoryEnumerator> dirEnumerator = do_QueryInterface(e, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIFile> currentFile;
+ while (NS_SUCCEEDED(dirEnumerator->GetNextFile(getter_AddRefs(currentFile))) && currentFile) {
+ rv = AddToZip(aZipWriter, currentFile, currentPath);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+
+ return NS_OK;
+}
+#endif // XP_MACOSX
+
+//
+// Class implementation...
+//
+nsMsgAttachmentHandler::nsMsgAttachmentHandler() :
+ mRequest(nullptr),
+ mCompFields(nullptr), // Message composition fields for the sender
+ m_bogus_attachment(false),
+ m_done(false),
+ m_already_encoded_p(false),
+ m_decrypted_p(false),
+ mDeleteFile(false),
+ mMHTMLPart(false),
+ mPartUserOmissionOverride(false),
+ mMainBody(false),
+ mSendViaCloud(false),
+ mNodeIndex(-1),
+ // For analyzing the attachment file...
+ m_size(0),
+ m_unprintable_count(0),
+ m_highbit_count(0),
+ m_ctl_count(0),
+ m_null_count(0),
+ m_have_cr(0),
+ m_have_lf(0),
+ m_have_crlf(0),
+ m_prev_char_was_cr(false),
+ m_current_column(0),
+ m_max_column(0),
+ m_lines(0),
+ m_file_analyzed(false),
+
+ // Mime
+ m_encoder(nullptr)
+{
+}
+
+nsMsgAttachmentHandler::~nsMsgAttachmentHandler()
+{
+ if (mTmpFile && mDeleteFile)
+ mTmpFile->Remove(false);
+
+ if (mOutFile)
+ mOutFile->Close();
+
+ CleanupTempFile();
+}
+
+NS_IMPL_ISUPPORTS(nsMsgAttachmentHandler, nsIMsgAttachmentHandler)
+
+// nsIMsgAttachmentHandler implementation.
+
+NS_IMETHODIMP nsMsgAttachmentHandler::GetType(nsACString& aType)
+{
+ aType.Assign(m_type);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachmentHandler::GetUri(nsACString& aUri)
+{
+ nsAutoCString turl;
+ if (!mURL)
+ {
+ if (!m_uri.IsEmpty())
+ turl = m_uri;
+ }
+ else
+ {
+ nsresult rv = mURL->GetSpec(turl);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ aUri.Assign(turl);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachmentHandler::GetTmpFile(nsIFile **aTmpFile)
+{
+ NS_ENSURE_ARG_POINTER(aTmpFile);
+ if (!mTmpFile)
+ return NS_ERROR_FAILURE;
+ NS_ADDREF(*aTmpFile = mTmpFile);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachmentHandler::GetName(nsACString& aName)
+{
+ aName.Assign(m_realName);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachmentHandler::GetSize(uint32_t *aSize)
+{
+ NS_ENSURE_ARG_POINTER(aSize);
+ *aSize = m_size;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachmentHandler::GetContentId(nsACString& aContentId)
+{
+ aContentId.Assign(m_contentId);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachmentHandler::GetSendViaCloud(bool* aSendViaCloud)
+{
+ NS_ENSURE_ARG_POINTER(aSendViaCloud);
+ *aSendViaCloud = mSendViaCloud;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachmentHandler::GetCharset(nsACString& aCharset)
+{
+ aCharset.Assign(m_charset);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachmentHandler::GetEncoding(nsACString& aEncoding)
+{
+ aEncoding.Assign(m_encoding);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachmentHandler::GetAlreadyEncoded(bool* aAlreadyEncoded)
+{
+ NS_ENSURE_ARG_POINTER(aAlreadyEncoded);
+ *aAlreadyEncoded = m_already_encoded_p;
+ return NS_OK;
+}
+
+// Local methods.
+
+void
+nsMsgAttachmentHandler::CleanupTempFile()
+{
+#ifdef XP_MACOSX
+ if (mEncodedWorkingFile) {
+ mEncodedWorkingFile->Remove(false);
+ mEncodedWorkingFile = nullptr;
+ }
+#endif // XP_MACOSX
+}
+
+void
+nsMsgAttachmentHandler::AnalyzeDataChunk(const char *chunk, int32_t length)
+{
+ unsigned char *s = (unsigned char *) chunk;
+ unsigned char *end = s + length;
+ for (; s < end; s++)
+ {
+ if (*s > 126)
+ {
+ m_highbit_count++;
+ m_unprintable_count++;
+ }
+ else if (*s < ' ' && *s != '\t' && *s != '\r' && *s != '\n')
+ {
+ m_unprintable_count++;
+ m_ctl_count++;
+ if (*s == 0)
+ m_null_count++;
+ }
+
+ if (*s == '\r' || *s == '\n')
+ {
+ if (*s == '\r')
+ {
+ if (m_prev_char_was_cr)
+ m_have_cr = 1;
+ else
+ m_prev_char_was_cr = true;
+ }
+ else
+ {
+ if (m_prev_char_was_cr)
+ {
+ if (m_current_column == 0)
+ {
+ m_have_crlf = 1;
+ m_lines--;
+ }
+ else
+ m_have_cr = m_have_lf = 1;
+ m_prev_char_was_cr = false;
+ }
+ else
+ m_have_lf = 1;
+ }
+ if (m_max_column < m_current_column)
+ m_max_column = m_current_column;
+ m_current_column = 0;
+ m_lines++;
+ }
+ else
+ {
+ m_current_column++;
+ }
+ }
+ // Check one last time for the last line. This is also important if there
+ // is only one line that doesn't terminate in \n.
+ if (m_max_column < m_current_column)
+ m_max_column = m_current_column;
+}
+
+void
+nsMsgAttachmentHandler::AnalyzeSnarfedFile(void)
+{
+ char chunk[1024];
+ uint32_t numRead = 0;
+
+ if (m_file_analyzed)
+ return;
+
+ if (mTmpFile)
+ {
+ int64_t fileSize;
+ mTmpFile->GetFileSize(&fileSize);
+ m_size = (uint32_t) fileSize;
+ nsCOMPtr <nsIInputStream> inputFile;
+ nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(inputFile), mTmpFile);
+ if (NS_FAILED(rv))
+ return;
+ {
+ do
+ {
+ rv = inputFile->Read(chunk, sizeof(chunk), &numRead);
+ if (numRead)
+ AnalyzeDataChunk(chunk, numRead);
+ }
+ while (numRead && NS_SUCCEEDED(rv));
+ if (m_prev_char_was_cr)
+ m_have_cr = 1;
+
+ inputFile->Close();
+ m_file_analyzed = true;
+ }
+ }
+}
+
+//
+// Given a content-type and some info about the contents of the document,
+// decide what encoding it should have.
+//
+nsresult
+nsMsgAttachmentHandler::PickEncoding(const char *charset, nsIMsgSend *mime_delivery_state)
+{
+ nsCOMPtr<nsIPrefBranch> pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
+
+ bool needsB64 = false;
+ bool forceB64 = false;
+ bool isUsingQP = false;
+
+ if (mSendViaCloud)
+ {
+ m_encoding = ENCODING_7BIT;
+ return NS_OK;
+ }
+ if (m_already_encoded_p)
+ goto DONE;
+
+ AnalyzeSnarfedFile();
+
+ // Allow users to override our percentage-wise guess on whether
+ // the file is text or binary.
+ if (pPrefBranch)
+ pPrefBranch->GetBoolPref ("mail.file_attach_binary", &forceB64);
+
+ // If the content-type is "image/" or something else known to be binary or
+ // several flavors of newlines are present, use base64 unless we're attaching
+ // a message (so that we don't get confused by newline conversions).
+ if (!mMainBody &&
+ (forceB64 || mime_type_requires_b64_p(m_type.get()) ||
+ m_have_cr + m_have_lf + m_have_crlf != 1) &&
+ !m_type.LowerCaseEqualsLiteral(MESSAGE_RFC822))
+ {
+ needsB64 = true;
+ }
+ else
+ {
+ // Otherwise, we need to pick an encoding based on the contents of the
+ // document.
+ bool encode_p;
+ bool force_p = false;
+
+ // Force quoted-printable if the sender does not allow conversion to 7bit.
+ if (mCompFields) {
+ if (mCompFields->GetForceMsgEncoding())
+ force_p = true;
+ } else if (mime_delivery_state) {
+ if (((nsMsgComposeAndSend *)mime_delivery_state)->mCompFields->GetForceMsgEncoding()) {
+ force_p = true;
+ }
+ }
+
+ if (force_p || (m_max_column > LINELENGTH_ENCODING_THRESHOLD)) {
+ encode_p = true;
+ } else if (UseQuotedPrintable() && m_unprintable_count) {
+ encode_p = true;
+ } else if (m_null_count) {
+ // If there are nulls, we must always encode, because sendmail will
+ // blow up.
+ encode_p = true;
+ } else {
+ encode_p = false;
+ }
+
+ // MIME requires a special case that these types never be encoded.
+ if (StringBeginsWith(m_type, NS_LITERAL_CSTRING("message"),
+ nsCaseInsensitiveCStringComparator()) ||
+ StringBeginsWith(m_type, NS_LITERAL_CSTRING("multipart"),
+ nsCaseInsensitiveCStringComparator()))
+ {
+ encode_p = false;
+ if (m_desiredType.LowerCaseEqualsLiteral(TEXT_PLAIN))
+ m_desiredType.Truncate();
+ }
+
+ // If the Mail charset is multibyte, we force it to use Base64 for attachments.
+ if ((!mMainBody && charset && nsMsgI18Nmultibyte_charset(charset)) &&
+ (m_type.LowerCaseEqualsLiteral(TEXT_HTML) ||
+ m_type.LowerCaseEqualsLiteral(TEXT_MDL) ||
+ m_type.LowerCaseEqualsLiteral(TEXT_PLAIN) ||
+ m_type.LowerCaseEqualsLiteral(TEXT_RICHTEXT) ||
+ m_type.LowerCaseEqualsLiteral(TEXT_ENRICHED) ||
+ m_type.LowerCaseEqualsLiteral(TEXT_VCARD) ||
+ m_type.LowerCaseEqualsLiteral(APPLICATION_DIRECTORY) || /* text/x-vcard synonym */
+ m_type.LowerCaseEqualsLiteral(TEXT_CSS) ||
+ m_type.LowerCaseEqualsLiteral(TEXT_JSSS))) {
+ needsB64 = true;
+ } else if (charset && nsMsgI18Nstateful_charset(charset)) {
+ m_encoding = ENCODING_7BIT;
+ } else if (encode_p &&
+ m_unprintable_count > (m_size / 10)) {
+ // If the document contains more than 10% unprintable characters,
+ // then that seems like a good candidate for base64 instead of
+ // quoted-printable.
+ needsB64 = true;
+ } else if (encode_p) {
+ m_encoding = ENCODING_QUOTED_PRINTABLE;
+ isUsingQP = true;
+ } else if (m_highbit_count > 0) {
+ m_encoding = ENCODING_8BIT;
+ } else {
+ m_encoding = ENCODING_7BIT;
+ }
+ }
+
+ // Always base64 binary data.
+ if (needsB64)
+ m_encoding = ENCODING_BASE64;
+
+ // According to RFC 821 we must always have lines shorter than 998 bytes.
+ // To encode "long lines" use a CTE that will transmit shorter lines.
+ // Switch to base64 if we are not already using "quoted printable".
+
+ // We don't do this for message/rfc822 attachments, since we can't
+ // change the original Content-Transfer-Encoding of the message we're
+ // attaching. We rely on the original message complying with RFC 821,
+ // if it doesn't we won't either. Not ideal.
+ if (!m_type.LowerCaseEqualsLiteral(MESSAGE_RFC822) &&
+ m_max_column > LINELENGTH_ENCODING_THRESHOLD && !isUsingQP)
+ m_encoding = ENCODING_BASE64;
+
+ // Now that we've picked an encoding, initialize the filter.
+ NS_ASSERTION(!m_encoder, "not-null m_encoder");
+ if (m_encoding.LowerCaseEqualsLiteral(ENCODING_BASE64))
+ {
+ m_encoder = MimeEncoder::GetBase64Encoder(mime_encoder_output_fn,
+ mime_delivery_state);
+ }
+ else if (m_encoding.LowerCaseEqualsLiteral(ENCODING_QUOTED_PRINTABLE))
+ {
+ m_encoder = MimeEncoder::GetQPEncoder(mime_encoder_output_fn,
+ mime_delivery_state);
+ }
+ else
+ {
+ m_encoder = nullptr;
+ }
+
+ /* Do some cleanup for documents with unknown content type.
+ There are two issues: how they look to MIME users, and how they look to
+ non-MIME users.
+
+ If the user attaches a "README" file, which has unknown type because it
+ has no extension, we still need to send it with no encoding, so that it
+ is readable to non-MIME users.
+
+ But if the user attaches some random binary file, then base64 encoding
+ will have been chosen for it (above), and in this case, it won't be
+ immediately readable by non-MIME users. However, if we type it as
+ text/plain instead of application/octet-stream, it will show up inline
+ in a MIME viewer, which will probably be ugly, and may possibly have
+ bad charset things happen as well.
+
+ So, the heuristic we use is, if the type is unknown, then the type is
+ set to application/octet-stream for data which needs base64 (binary data)
+ and is set to text/plain for data which didn't need base64 (unencoded or
+ lightly encoded data.)
+ */
+DONE:
+ if (m_type.IsEmpty() || m_type.LowerCaseEqualsLiteral(UNKNOWN_CONTENT_TYPE))
+ {
+ if (m_already_encoded_p)
+ m_type = APPLICATION_OCTET_STREAM;
+ else if (m_encoding.LowerCaseEqualsLiteral(ENCODING_BASE64) ||
+ m_encoding.LowerCaseEqualsLiteral(ENCODING_UUENCODE))
+ m_type = APPLICATION_OCTET_STREAM;
+ else
+ m_type = TEXT_PLAIN;
+ }
+ return NS_OK;
+}
+
+nsresult
+nsMsgAttachmentHandler::PickCharset()
+{
+ if (!m_charset.IsEmpty() || !m_type.LowerCaseEqualsLiteral(TEXT_PLAIN))
+ return NS_OK;
+
+ nsCOMPtr<nsIFile> tmpFile =
+ do_QueryInterface(mTmpFile);
+ if (!tmpFile)
+ return NS_OK;
+
+ return MsgDetectCharsetFromFile(tmpFile, m_charset);
+}
+
+static nsresult
+FetcherURLDoneCallback(nsresult aStatus,
+ const nsACString &aContentType,
+ const nsACString &aCharset,
+ int32_t totalSize,
+ const char16_t* aMsg, void *tagData)
+{
+ nsMsgAttachmentHandler *ma = (nsMsgAttachmentHandler *) tagData;
+ NS_ASSERTION(ma != nullptr, "not-null mime attachment");
+
+ if (ma != nullptr)
+ {
+ ma->m_size = totalSize;
+ if (!aContentType.IsEmpty())
+ {
+#ifdef XP_MACOSX
+ //Do not change the type if we are dealing with an encoded (e.g., appledouble or zip) file
+ if (!ma->mEncodedWorkingFile)
+#else
+ // can't send appledouble on non-macs
+ if (!aContentType.EqualsLiteral("multipart/appledouble"))
+#endif
+ ma->m_type = aContentType;
+ }
+
+ if (!aCharset.IsEmpty())
+ ma->m_charset = aCharset;
+
+ return ma->UrlExit(aStatus, aMsg);
+ }
+ else
+ return NS_OK;
+}
+
+nsresult
+nsMsgAttachmentHandler::SnarfMsgAttachment(nsMsgCompFields *compFields)
+{
+ nsresult rv = NS_ERROR_INVALID_ARG;
+ nsCOMPtr <nsIMsgMessageService> messageService;
+
+ if (m_uri.Find("-message:", CaseInsensitiveCompare) != -1)
+ {
+ nsCOMPtr <nsIFile> tmpFile;
+ rv = nsMsgCreateTempFile("nsmail.tmp", getter_AddRefs(tmpFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+ mTmpFile = do_QueryInterface(tmpFile);
+ mDeleteFile = true;
+ mCompFields = compFields;
+ m_type = MESSAGE_RFC822;
+ m_overrideType = MESSAGE_RFC822;
+ if (!mTmpFile)
+ {
+ rv = NS_ERROR_FAILURE;
+ goto done;
+ }
+
+ rv = MsgNewBufferedFileOutputStream(getter_AddRefs(mOutFile), mTmpFile, -1, 00600);
+ if (NS_FAILED(rv) || !mOutFile)
+ {
+ if (m_mime_delivery_state)
+ {
+ nsCOMPtr<nsIMsgSendReport> sendReport;
+ m_mime_delivery_state->GetSendReport(getter_AddRefs(sendReport));
+ if (sendReport)
+ {
+ nsAutoString error_msg;
+ nsMsgBuildMessageWithTmpFile(mTmpFile, error_msg);
+ sendReport->SetMessage(nsIMsgSendReport::process_Current, error_msg.get(), false);
+ }
+ }
+ rv = NS_MSG_UNABLE_TO_OPEN_TMP_FILE;
+ goto done;
+ }
+
+ nsCOMPtr<nsIURLFetcher> fetcher = do_CreateInstance(NS_URLFETCHER_CONTRACTID, &rv);
+ if (NS_FAILED(rv) || !fetcher)
+ {
+ if (NS_SUCCEEDED(rv))
+ rv = NS_ERROR_UNEXPECTED;
+ goto done;
+ }
+
+ rv = fetcher->Initialize(mTmpFile, mOutFile, FetcherURLDoneCallback, this);
+ rv = GetMessageServiceFromURI(m_uri, getter_AddRefs(messageService));
+ if (NS_SUCCEEDED(rv) && messageService)
+ {
+ nsAutoCString uri(m_uri);
+ uri += (uri.FindChar('?') == kNotFound) ? '?' : '&';
+ uri.Append("fetchCompleteMessage=true");
+ nsCOMPtr<nsIStreamListener> strListener;
+ fetcher->QueryInterface(NS_GET_IID(nsIStreamListener), getter_AddRefs(strListener));
+
+ // initialize a new stream converter, that uses the strListener as its input
+ // obtain the input stream listener from the new converter,
+ // and pass the converter's input stream listener to DisplayMessage
+
+ m_mime_parser = do_CreateInstance(NS_MAILNEWS_MIME_STREAM_CONVERTER_CONTRACTID, &rv);
+ if (NS_FAILED(rv))
+ goto done;
+
+ // Set us as the output stream for HTML data from libmime...
+ nsCOMPtr<nsIMimeStreamConverter> mimeConverter = do_QueryInterface(m_mime_parser);
+ if (mimeConverter)
+ {
+ mimeConverter->SetMimeOutputType(nsMimeOutput::nsMimeMessageDecrypt);
+ mimeConverter->SetForwardInline(false);
+ mimeConverter->SetIdentity(nullptr);
+ mimeConverter->SetOriginalMsgURI(nullptr);
+ }
+
+ nsCOMPtr<nsIStreamListener> convertedListener = do_QueryInterface(m_mime_parser, &rv);
+ if (NS_FAILED(rv))
+ goto done;
+
+ nsCOMPtr<nsIURI> aURL;
+ rv = messageService->GetUrlForUri(uri.get(), getter_AddRefs(aURL), nullptr);
+ if (NS_FAILED(rv))
+ goto done;
+
+
+ nsCOMPtr<nsIPrincipal> nullPrincipal =
+ do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "CreateInstance of nullprincipal failed.");
+ if (NS_FAILED(rv))
+ goto done;
+
+ rv = NS_NewInputStreamChannel(getter_AddRefs(m_converter_channel),
+ aURL,
+ nullptr,
+ nullPrincipal,
+ nsILoadInfo::SEC_NORMAL,
+ nsIContentPolicy::TYPE_OTHER);
+ if (NS_FAILED(rv))
+ goto done;
+
+ rv = m_mime_parser->AsyncConvertData("message/rfc822", "message/rfc822",
+ strListener, m_converter_channel);
+ if (NS_FAILED(rv))
+ goto done;
+
+ nsCOMPtr<nsIURI> dummyNull;
+ rv = messageService->DisplayMessage(uri.get(), convertedListener, nullptr, nullptr, nullptr,
+ getter_AddRefs(dummyNull));
+ }
+ }
+done:
+ if (NS_FAILED(rv))
+ {
+ if (mOutFile)
+ {
+ mOutFile->Close();
+ mOutFile = nullptr;
+ }
+
+ if (mTmpFile)
+ {
+ mTmpFile->Remove(false);
+ mTmpFile = nullptr;
+ }
+ }
+
+ return rv;
+}
+
+#ifdef XP_MACOSX
+bool nsMsgAttachmentHandler::HasResourceFork(FSRef *fsRef)
+{
+ FSCatalogInfo catalogInfo;
+ OSErr err = FSGetCatalogInfo(fsRef, kFSCatInfoDataSizes + kFSCatInfoRsrcSizes, &catalogInfo, nullptr, nullptr, nullptr);
+ return (err == noErr && catalogInfo.rsrcLogicalSize != 0);
+}
+#endif
+
+nsresult
+nsMsgAttachmentHandler::SnarfAttachment(nsMsgCompFields *compFields)
+{
+ NS_ASSERTION (! m_done, "Already done");
+
+ if (!mURL)
+ return SnarfMsgAttachment(compFields);
+
+ mCompFields = compFields;
+
+ // First, get as file spec and create the stream for the
+ // temp file where we will save this data
+ nsCOMPtr <nsIFile> tmpFile;
+ nsresult rv = nsMsgCreateTempFile("nsmail.tmp", getter_AddRefs(tmpFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+ mTmpFile = do_QueryInterface(tmpFile);
+ mDeleteFile = true;
+
+ rv = MsgNewBufferedFileOutputStream(getter_AddRefs(mOutFile), mTmpFile, -1, 00600);
+ if (NS_FAILED(rv) || !mOutFile)
+ {
+ if (m_mime_delivery_state)
+ {
+ nsCOMPtr<nsIMsgSendReport> sendReport;
+ m_mime_delivery_state->GetSendReport(getter_AddRefs(sendReport));
+ if (sendReport)
+ {
+ nsAutoString error_msg;
+ nsMsgBuildMessageWithTmpFile(mTmpFile, error_msg);
+ sendReport->SetMessage(nsIMsgSendReport::process_Current, error_msg.get(), false);
+ }
+ }
+ mTmpFile->Remove(false);
+ mTmpFile = nullptr;
+ return NS_MSG_UNABLE_TO_OPEN_TMP_FILE;
+ }
+
+ nsCString sourceURISpec;
+ rv = mURL->GetSpec(sourceURISpec);
+ NS_ENSURE_SUCCESS(rv, rv);
+#ifdef XP_MACOSX
+ if (!m_bogus_attachment && StringBeginsWith(sourceURISpec, NS_LITERAL_CSTRING("file://")))
+ {
+ // Unescape the path (i.e. un-URLify it) before making a FSSpec
+ nsAutoCString filePath;
+ filePath.Adopt(nsMsgGetLocalFileFromURL(sourceURISpec.get()));
+ nsAutoCString unescapedFilePath;
+ MsgUnescapeString(filePath, 0, unescapedFilePath);
+
+ nsCOMPtr<nsIFile> sourceFile;
+ NS_NewNativeLocalFile(unescapedFilePath, true, getter_AddRefs(sourceFile));
+ if (!sourceFile)
+ return NS_ERROR_FAILURE;
+
+ // check if it is a bundle. if it is, we'll zip it.
+ // if not, we'll apple encode it (applesingle or appledouble)
+ nsCOMPtr<nsILocalFileMac> macFile(do_QueryInterface(sourceFile));
+ bool isPackage;
+ macFile->IsPackage(&isPackage);
+ if (isPackage)
+ rv = ConvertToZipFile(macFile);
+ else
+ rv = ConvertToAppleEncoding(sourceURISpec, unescapedFilePath, macFile);
+
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+#endif /* XP_MACOSX */
+
+ //
+ // Ok, here we are, we need to fire the URL off and get the data
+ // in the temp file
+ //
+ // Create a fetcher for the URL attachment...
+
+ nsCOMPtr<nsIURLFetcher> fetcher = do_CreateInstance(NS_URLFETCHER_CONTRACTID, &rv);
+ if (NS_FAILED(rv) || !fetcher)
+ {
+ if (NS_SUCCEEDED(rv))
+ return NS_ERROR_UNEXPECTED;
+ else
+ return rv;
+ }
+
+ return fetcher->FireURLRequest(mURL, mTmpFile, mOutFile, FetcherURLDoneCallback, this);
+}
+
+#ifdef XP_MACOSX
+nsresult
+nsMsgAttachmentHandler::ConvertToZipFile(nsILocalFileMac *aSourceFile)
+{
+ // append ".zip" to the real file name
+ nsAutoCString zippedName;
+ nsresult rv = aSourceFile->GetNativeLeafName(zippedName);
+ NS_ENSURE_SUCCESS(rv, rv);
+ zippedName.AppendLiteral(".zip");
+
+ // create a temporary file that we'll work on
+ nsCOMPtr <nsIFile> tmpFile;
+ rv = nsMsgCreateTempFile(zippedName.get(), getter_AddRefs(tmpFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+ mEncodedWorkingFile = do_QueryInterface(tmpFile);
+
+ // point our URL at the zipped temp file
+ NS_NewFileURI(getter_AddRefs(mURL), mEncodedWorkingFile);
+
+ // zip it!
+ rv = nsSimpleZipper::Zip(aSourceFile, mEncodedWorkingFile);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // set some metadata for this attachment, that will affect the MIME headers.
+ m_type = APPLICATION_ZIP;
+ m_realName = zippedName.get();
+
+ return NS_OK;
+}
+
+nsresult
+nsMsgAttachmentHandler::ConvertToAppleEncoding(const nsCString &aFileURI,
+ const nsCString &aFilePath,
+ nsILocalFileMac *aSourceFile)
+{
+ // convert the apple file to AppleDouble first, and then patch the
+ // address in the url.
+
+ //We need to retrieve the file type and creator...
+
+ char fileInfo[32];
+ OSType type, creator;
+
+ nsresult rv = aSourceFile->GetFileType(&type);
+ if (NS_FAILED(rv))
+ return rv;
+ PR_snprintf(fileInfo, sizeof(fileInfo), "%X", type);
+ m_xMacType = fileInfo;
+
+ rv = aSourceFile->GetFileCreator(&creator);
+ if (NS_FAILED(rv))
+ return rv;
+ PR_snprintf(fileInfo, sizeof(fileInfo), "%X", creator);
+ m_xMacCreator = fileInfo;
+
+ FSRef fsRef;
+ aSourceFile->GetFSRef(&fsRef);
+ bool sendResourceFork = HasResourceFork(&fsRef);
+
+ // if we have a resource fork, check the filename extension, maybe we don't need the resource fork!
+ if (sendResourceFork)
+ {
+ nsCOMPtr<nsIURL> fileUrl(do_CreateInstance(NS_STANDARDURL_CONTRACTID));
+ if (fileUrl)
+ {
+ rv = fileUrl->SetSpec(aFileURI);
+ if (NS_SUCCEEDED(rv))
+ {
+ nsAutoCString ext;
+ rv = fileUrl->GetFileExtension(ext);
+ if (NS_SUCCEEDED(rv) && !ext.IsEmpty())
+ {
+ sendResourceFork =
+ PL_strcasecmp(ext.get(), "TXT") &&
+ PL_strcasecmp(ext.get(), "JPG") &&
+ PL_strcasecmp(ext.get(), "GIF") &&
+ PL_strcasecmp(ext.get(), "TIF") &&
+ PL_strcasecmp(ext.get(), "HTM") &&
+ PL_strcasecmp(ext.get(), "HTML") &&
+ PL_strcasecmp(ext.get(), "ART") &&
+ PL_strcasecmp(ext.get(), "XUL") &&
+ PL_strcasecmp(ext.get(), "XML") &&
+ PL_strcasecmp(ext.get(), "CSS") &&
+ PL_strcasecmp(ext.get(), "JS");
+ }
+ }
+ }
+ }
+
+ // Only use appledouble if we aren't uuencoding.
+ if( sendResourceFork )
+ {
+ char *separator;
+
+ separator = mime_make_separator("ad");
+ if (!separator)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsCOMPtr <nsIFile> tmpFile;
+ nsresult rv = nsMsgCreateTempFile("appledouble", getter_AddRefs(tmpFile));
+ if (NS_SUCCEEDED(rv))
+ mEncodedWorkingFile = do_QueryInterface(tmpFile);
+ if (!mEncodedWorkingFile)
+ {
+ PR_FREEIF(separator);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ //
+ // RICHIE_MAC - ok, here's the deal, we have a file that we need
+ // to encode in appledouble encoding for the resource fork and put that
+ // into the mEncodedWorkingFile location. Then, we need to patch the new file
+ // spec into the array and send this as part of the 2 part appledouble/mime
+ // encoded mime part.
+ //
+ AppleDoubleEncodeObject *obj = new (AppleDoubleEncodeObject);
+ if (obj == NULL)
+ {
+ mEncodedWorkingFile = nullptr;
+ PR_FREEIF(separator);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ rv = MsgGetFileStream(mEncodedWorkingFile, getter_AddRefs(obj->fileStream));
+ if (NS_FAILED(rv) || !obj->fileStream)
+ {
+ PR_FREEIF(separator);
+ delete obj;
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ char *working_buff = (char *) PR_Malloc(AD_WORKING_BUFF_SIZE);
+ if (!working_buff)
+ {
+ PR_FREEIF(separator);
+ delete obj;
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ obj->buff = working_buff;
+ obj->s_buff = AD_WORKING_BUFF_SIZE;
+
+ //
+ // Setup all the need information on the apple double encoder.
+ //
+ ap_encode_init(&(obj->ap_encode_obj), aFilePath.get(), separator);
+
+ int32_t count;
+
+ OSErr status = noErr;
+ m_size = 0;
+ while (status == noErr)
+ {
+ status = ap_encode_next(&(obj->ap_encode_obj), obj->buff, obj->s_buff, &count);
+ if (status == noErr || status == errDone)
+ {
+ //
+ // we got the encode data, so call the next stream to write it to the disk.
+ //
+ uint32_t bytesWritten;
+ obj->fileStream->Write(obj->buff, count, &bytesWritten);
+ if (bytesWritten != (uint32_t) count)
+ status = errFileWrite;
+ }
+ }
+
+ ap_encode_end(&(obj->ap_encode_obj), (status >= 0)); // if this is true, ok, false abort
+ if (obj->fileStream)
+ obj->fileStream->Close();
+
+ PR_FREEIF(obj->buff); /* free the working buff. */
+ PR_FREEIF(obj);
+
+ nsCOMPtr <nsIURI> fileURI;
+ NS_NewFileURI(getter_AddRefs(fileURI), mEncodedWorkingFile);
+
+ nsCOMPtr<nsIFileURL> theFileURL = do_QueryInterface(fileURI, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCString newURLSpec;
+ rv = fileURI->GetSpec(newURLSpec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (newURLSpec.IsEmpty())
+ {
+ PR_FREEIF(separator);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (NS_FAILED(nsMsgNewURL(getter_AddRefs(mURL), newURLSpec.get())))
+ {
+ PR_FREEIF(separator);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // Now after conversion, also patch the types.
+ char tmp[128];
+ PR_snprintf(tmp, sizeof(tmp), MULTIPART_APPLEDOUBLE ";\r\n boundary=\"%s\"", separator);
+ PR_FREEIF(separator);
+ m_type = tmp;
+ }
+ else
+ {
+ if ( sendResourceFork )
+ {
+ // For now, just do the encoding, but in the old world we would ask the
+ // user about doing this conversion
+ printf("...we could ask the user about this conversion, but for now, nahh..\n");
+ }
+
+ bool useDefault;
+ char *macType, *macEncoding;
+ if (m_type.IsEmpty() || m_type.LowerCaseEqualsLiteral(TEXT_PLAIN))
+ {
+# define TEXT_TYPE 0x54455854 /* the characters 'T' 'E' 'X' 'T' */
+# define text_TYPE 0x74657874 /* the characters 't' 'e' 'x' 't' */
+
+ if (type != TEXT_TYPE && type != text_TYPE)
+ {
+ MacGetFileType(aSourceFile, &useDefault, &macType, &macEncoding);
+ m_type = macType;
+ }
+ }
+ // don't bother to set the types if we failed in getting the file info.
+ }
+
+ return NS_OK;
+}
+#endif // XP_MACOSX
+
+nsresult
+nsMsgAttachmentHandler::LoadDataFromFile(nsIFile *file, nsString &sigData, bool charsetConversion)
+{
+ int32_t readSize;
+ char *readBuf;
+
+ nsCOMPtr <nsIInputStream> inputFile;
+ nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(inputFile), file);
+ if (NS_FAILED(rv))
+ return NS_MSG_ERROR_WRITING_FILE;
+
+ int64_t fileSize;
+ file->GetFileSize(&fileSize);
+ readSize = (uint32_t) fileSize;
+
+ readBuf = (char *)PR_Malloc(readSize + 1);
+ if (!readBuf)
+ return NS_ERROR_OUT_OF_MEMORY;
+ memset(readBuf, 0, readSize + 1);
+
+ uint32_t bytesRead;
+ inputFile->Read(readBuf, readSize, &bytesRead);
+ inputFile->Close();
+
+ nsDependentCString cstringReadBuf(readBuf, bytesRead);
+ if (charsetConversion)
+ {
+ if (NS_FAILED(ConvertToUnicode(m_charset.get(), cstringReadBuf, sigData)))
+ CopyASCIItoUTF16(cstringReadBuf, sigData);
+ }
+ else
+ CopyASCIItoUTF16(cstringReadBuf, sigData);
+
+ PR_FREEIF(readBuf);
+ return NS_OK;
+}
+
+nsresult
+nsMsgAttachmentHandler::Abort()
+{
+ nsCOMPtr<nsIRequest> saveRequest;
+ saveRequest.swap(mRequest);
+
+ if (mTmpFile)
+ {
+ if (mDeleteFile)
+ mTmpFile->Remove(false);
+ mTmpFile = nullptr;
+ }
+
+ NS_ASSERTION(m_mime_delivery_state != nullptr, "not-null m_mime_delivery_state");
+
+ if (m_done)
+ return NS_OK;
+
+ if (saveRequest)
+ return saveRequest->Cancel(NS_ERROR_ABORT);
+ else
+ if (m_mime_delivery_state)
+ {
+ m_mime_delivery_state->SetStatus(NS_ERROR_ABORT);
+ m_mime_delivery_state->NotifyListenerOnStopSending(nullptr, NS_ERROR_ABORT, 0, nullptr);
+ }
+ return NS_OK;
+
+}
+
+nsresult
+nsMsgAttachmentHandler::UrlExit(nsresult status, const char16_t* aMsg)
+{
+ NS_ASSERTION(m_mime_delivery_state != nullptr, "not-null m_mime_delivery_state");
+
+ // Close the file, but don't delete the disk file (or the file spec.)
+ if (mOutFile)
+ {
+ mOutFile->Close();
+ mOutFile = nullptr;
+ }
+ // this silliness is because Windows nsIFile caches its file size
+ // so if an output stream writes to it, it will still return the original
+ // cached size.
+ if (mTmpFile)
+ {
+ nsCOMPtr <nsIFile> tmpFile;
+ mTmpFile->Clone(getter_AddRefs(tmpFile));
+ mTmpFile = do_QueryInterface(tmpFile);
+ }
+ mRequest = nullptr;
+
+ // First things first, we are now going to see if this is an HTML
+ // Doc and if it is, we need to see if we can determine the charset
+ // for this part by sniffing the HTML file.
+ // This is needed only when the charset is not set already.
+ // (e.g. a charset may be specified in HTTP header)
+ //
+ if (!m_type.IsEmpty() && m_charset.IsEmpty() &&
+ m_type.LowerCaseEqualsLiteral(TEXT_HTML))
+ m_charset = nsMsgI18NParseMetaCharset(mTmpFile);
+
+ nsresult mimeDeliveryStatus;
+ m_mime_delivery_state->GetStatus(&mimeDeliveryStatus);
+
+ if (mimeDeliveryStatus == NS_ERROR_ABORT)
+ status = NS_ERROR_ABORT;
+
+ // If the attachment is empty, let's call that a failure.
+ if (!m_size)
+ status = NS_ERROR_FAILURE;
+
+ if (NS_FAILED(status) && status != NS_ERROR_ABORT && NS_SUCCEEDED(mimeDeliveryStatus))
+ {
+ // At this point, we should probably ask a question to the user
+ // if we should continue without this attachment.
+ //
+ bool keepOnGoing = true;
+ nsCString turl;
+ nsString msg;
+ nsresult rv;
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::services::GetStringBundleService();
+ NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED);
+ nsCOMPtr<nsIStringBundle> bundle;
+ rv = bundleService->CreateBundle("chrome://messenger/locale/messengercompose/composeMsgs.properties", getter_AddRefs(bundle));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsMsgDeliverMode mode = nsIMsgSend::nsMsgDeliverNow;
+ m_mime_delivery_state->GetDeliveryMode(&mode);
+ nsCString params;
+ if (!m_realName.IsEmpty())
+ params = m_realName;
+ else if (NS_SUCCEEDED(mURL->GetSpec(turl)) && !turl.IsEmpty())
+ {
+ nsAutoCString unescapedUrl;
+ MsgUnescapeString(turl, 0, unescapedUrl);
+ if (unescapedUrl.IsEmpty())
+ params = turl;
+ else
+ params = unescapedUrl;
+ }
+ else
+ params.AssignLiteral("?");
+
+ NS_ConvertUTF8toUTF16 UTF16params(params);
+ const char16_t* formatParams[] = { UTF16params.get() };
+ if (mode == nsIMsgSend::nsMsgSaveAsDraft || mode == nsIMsgSend::nsMsgSaveAsTemplate)
+ bundle->FormatStringFromName(u"failureOnObjectEmbeddingWhileSaving",
+ formatParams, 1, getter_Copies(msg));
+ else
+ bundle->FormatStringFromName(u"failureOnObjectEmbeddingWhileSending",
+ formatParams, 1, getter_Copies(msg));
+
+ nsCOMPtr<nsIPrompt> aPrompt;
+ if (m_mime_delivery_state)
+ m_mime_delivery_state->GetDefaultPrompt(getter_AddRefs(aPrompt));
+ nsMsgAskBooleanQuestionByString(aPrompt, msg.get(), &keepOnGoing);
+
+ if (keepOnGoing)
+ {
+ status = NS_OK;
+ m_bogus_attachment = true; //That will cause this attachment to be ignored.
+ }
+ else
+ {
+ status = NS_ERROR_ABORT;
+ m_mime_delivery_state->SetStatus(status);
+ nsresult ignoreMe;
+ m_mime_delivery_state->Fail(status, nullptr, &ignoreMe);
+ m_mime_delivery_state->NotifyListenerOnStopSending(nullptr, status, 0, nullptr);
+ SetMimeDeliveryState(nullptr);
+ return status;
+ }
+ }
+
+ m_done = true;
+
+ //
+ // Ok, now that we have the file here on disk, we need to see if there was
+ // a need to do conversion to plain text...if so, the magic happens here,
+ // otherwise, just move on to other attachments...
+ //
+ if (NS_SUCCEEDED(status) && !m_type.LowerCaseEqualsLiteral(TEXT_PLAIN) &&
+ m_desiredType.LowerCaseEqualsLiteral(TEXT_PLAIN))
+ {
+ //
+ // Conversion to plain text desired.
+ // Now use the converter service here to do the right
+ // thing and convert this data to plain text for us!
+ //
+ nsAutoString conData;
+
+ if (NS_SUCCEEDED(LoadDataFromFile(mTmpFile, conData, true)))
+ {
+ bool flowed, delsp, formatted, disallowBreaks;
+ GetSerialiserFlags(m_charset.get(), &flowed, &delsp, &formatted, &disallowBreaks);
+
+ if (NS_SUCCEEDED(ConvertBufToPlainText(conData, flowed, delsp, formatted, disallowBreaks)))
+ {
+ if (mDeleteFile)
+ mTmpFile->Remove(false);
+
+ nsCOMPtr<nsIOutputStream> outputStream;
+ nsresult rv = MsgNewBufferedFileOutputStream(getter_AddRefs(outputStream), mTmpFile,
+ PR_WRONLY | PR_CREATE_FILE, 00600);
+
+ if (NS_SUCCEEDED(rv))
+ {
+ nsAutoCString tData;
+ if (NS_FAILED(ConvertFromUnicode(m_charset.get(), conData, tData)))
+ LossyCopyUTF16toASCII(conData, tData);
+ if (!tData.IsEmpty())
+ {
+ uint32_t bytesWritten;
+ (void) outputStream->Write(tData.get(), tData.Length(), &bytesWritten);
+ }
+ outputStream->Close();
+ // this silliness is because Windows nsIFile caches its file size
+ // so if an output stream writes to it, it will still return the original
+ // cached size.
+ if (mTmpFile)
+ {
+ nsCOMPtr <nsIFile> tmpFile;
+ mTmpFile->Clone(getter_AddRefs(tmpFile));
+ mTmpFile = do_QueryInterface(tmpFile);
+ }
+
+ }
+ }
+ }
+
+ m_type = m_desiredType;
+ m_desiredType.Truncate();
+ m_encoding.Truncate();
+ }
+
+ uint32_t pendingAttachmentCount = 0;
+ m_mime_delivery_state->GetPendingAttachmentCount(&pendingAttachmentCount);
+ NS_ASSERTION (pendingAttachmentCount > 0, "no more pending attachment");
+
+ m_mime_delivery_state->SetPendingAttachmentCount(pendingAttachmentCount - 1);
+
+ bool processAttachmentsSynchronously = false;
+ m_mime_delivery_state->GetProcessAttachmentsSynchronously(&processAttachmentsSynchronously);
+ if (NS_SUCCEEDED(status) && processAttachmentsSynchronously)
+ {
+ /* Find the next attachment which has not yet been loaded,
+ if any, and start it going.
+ */
+ uint32_t i;
+ nsMsgAttachmentHandler *next = 0;
+ nsTArray<RefPtr<nsMsgAttachmentHandler>> *attachments;
+
+ m_mime_delivery_state->GetAttachmentHandlers(&attachments);
+
+ for (i = 0; i < attachments->Length(); i++)
+ {
+ if (!(*attachments)[i]->m_done)
+ {
+ next = (*attachments)[i];
+ //
+ // rhp: We need to get a little more understanding to failed URL
+ // requests. So, at this point if most of next is NULL, then we
+ // should just mark it fetched and move on! We probably ignored
+ // this earlier on in the send process.
+ //
+ if ( (!next->mURL) && (next->m_uri.IsEmpty()) )
+ {
+ (*attachments)[i]->m_done = true;
+ (*attachments)[i]->SetMimeDeliveryState(nullptr);
+ m_mime_delivery_state->GetPendingAttachmentCount(&pendingAttachmentCount);
+ m_mime_delivery_state->SetPendingAttachmentCount(pendingAttachmentCount - 1);
+ next->mPartUserOmissionOverride = true;
+ next = nullptr;
+ continue;
+ }
+
+ break;
+ }
+ }
+
+ if (next)
+ {
+ nsresult status = next->SnarfAttachment(mCompFields);
+ if (NS_FAILED(status))
+ {
+ nsresult ignoreMe;
+ m_mime_delivery_state->Fail(status, nullptr, &ignoreMe);
+ m_mime_delivery_state->NotifyListenerOnStopSending(nullptr, status, 0, nullptr);
+ SetMimeDeliveryState(nullptr);
+ return NS_ERROR_UNEXPECTED;
+ }
+ }
+ }
+
+ m_mime_delivery_state->GetPendingAttachmentCount(&pendingAttachmentCount);
+ if (pendingAttachmentCount == 0)
+ {
+ // If this is the last attachment, then either complete the
+ // delivery (if successful) or report the error by calling
+ // the exit routine and terminating the delivery.
+ if (NS_FAILED(status))
+ {
+ nsresult ignoreMe;
+ m_mime_delivery_state->Fail(status, aMsg, &ignoreMe);
+ m_mime_delivery_state->NotifyListenerOnStopSending(nullptr, status, aMsg, nullptr);
+ SetMimeDeliveryState(nullptr);
+ return NS_ERROR_UNEXPECTED;
+ }
+ else
+ {
+ status = m_mime_delivery_state->GatherMimeAttachments ();
+ if (NS_FAILED(status))
+ {
+ nsresult ignoreMe;
+ m_mime_delivery_state->Fail(status, aMsg, &ignoreMe);
+ m_mime_delivery_state->NotifyListenerOnStopSending(nullptr, status, aMsg, nullptr);
+ SetMimeDeliveryState(nullptr);
+ return NS_ERROR_UNEXPECTED;
+ }
+ }
+ }
+ else
+ {
+ // If this is not the last attachment, but it got an error,
+ // then report that error and continue
+ if (NS_FAILED(status))
+ {
+ nsresult ignoreMe;
+ m_mime_delivery_state->Fail(status, aMsg, &ignoreMe);
+ }
+ }
+
+ SetMimeDeliveryState(nullptr);
+ return NS_OK;
+}
+
+
+nsresult
+nsMsgAttachmentHandler::GetMimeDeliveryState(nsIMsgSend** _retval)
+{
+ NS_ENSURE_ARG(_retval);
+ *_retval = m_mime_delivery_state;
+ NS_IF_ADDREF(*_retval);
+ return NS_OK;
+}
+
+nsresult
+nsMsgAttachmentHandler::SetMimeDeliveryState(nsIMsgSend* mime_delivery_state)
+{
+ /*
+ Because setting m_mime_delivery_state to null could destroy ourself as
+ m_mime_delivery_state it's our parent, we need to protect ourself against
+ that!
+
+ This extra comptr is necessary,
+ see bug http://bugzilla.mozilla.org/show_bug.cgi?id=78967
+ */
+ nsCOMPtr<nsIMsgSend> temp = m_mime_delivery_state; /* Should lock our parent until the end of the function */
+ m_mime_delivery_state = mime_delivery_state;
+ return NS_OK;
+}