/* -*- Mode: C++; tab-width: 4; 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 "nsMsgSend.h" #include "prmem.h" #include "nsMsgLocalFolderHdrs.h" #include "nsMsgSendPart.h" #include "nsMsgBaseCID.h" #include "nsMsgNewsCID.h" #include "nsISmtpService.h" // for actually sending the message... #include "nsINntpService.h" // for actually posting the message... #include "nsIMsgMailSession.h" #include "nsIMsgIdentity.h" #include "nsIPrefService.h" #include "nsIPrefBranch.h" #include "nsIMsgMailNewsUrl.h" #include "nsMsgCompUtils.h" #include "nsMsgI18N.h" #include "nsICharsetConverterManager.h" #include "nsIMsgSendListener.h" #include "nsIMsgCopyServiceListener.h" #include "nsIFile.h" #include "nsIURL.h" #include "nsNetUtil.h" #include "nsIFileURL.h" #include "nsMsgCopy.h" #include "nsUnicharUtils.h" #include "nsMsgPrompts.h" #include "nsIDOMHTMLBodyElement.h" #include "nsIDOMHTMLImageElement.h" #include "nsIDOMHTMLLinkElement.h" #include "nsIDOMHTMLAnchorElement.h" #include "nsCExternalHandlerService.h" #include "nsIMIMEService.h" #include "nsIDocument.h" #include "nsIDOMDocument.h" #include "nsMsgCompCID.h" #include "nsIAbAddressCollector.h" #include "nsAbBaseCID.h" #include "nsCOMPtr.h" #include "mozITXTToHTMLConv.h" #include "nsIMsgStatusFeedback.h" #include "nsIMsgWindow.h" #include "nsTextFormatter.h" #include "nsIPrompt.h" #include "nsMailHeaders.h" #include "nsIDocShell.h" #include "nsMimeTypes.h" #include "nsISmtpUrl.h" #include "nsIInterfaceRequestor.h" #include "nsIInterfaceRequestorUtils.h" #include "nsIEditorMailSupport.h" #include "nsIDocumentEncoder.h" // for editor output flags #include "nsILoadGroup.h" #include "nsMsgSendReport.h" #include "nsNetCID.h" #include "nsError.h" #include "nsMsgUtils.h" #include "nsIMsgMdnGenerator.h" #include "nsISmtpServer.h" #include "nsIRDFService.h" #include "nsRDFCID.h" #include "nsIMsgAccountManager.h" #include "nsNativeCharsetUtils.h" #include "nsIAbCard.h" #include "nsIMsgAttachment.h" #include "nsIMsgProgress.h" #include "nsIMsgMessageService.h" #include "nsIMsgHdr.h" #include "nsIMsgFolder.h" #include "nsComposeStrings.h" #include "nsStringGlue.h" #include "nsMsgUtils.h" #include "nsIArray.h" #include "nsArrayUtils.h" #include "mozilla/Services.h" #include "mozilla/Attributes.h" #include "mozilla/mailnews/MimeEncoder.h" #include "mozilla/mailnews/MimeHeaderParser.h" #include "nsIMutableArray.h" #include "nsIMsgFilterService.h" #include "nsIMsgProtocolInfo.h" #include "mozIDOMWindow.h" #include "mozilla/Preferences.h" using namespace mozilla; using namespace mozilla::mailnews; static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID); #define PREF_MAIL_SEND_STRUCT "mail.send_struct" #define PREF_MAIL_STRICTLY_MIME "mail.strictly_mime" #define PREF_MAIL_MESSAGE_WARNING_SIZE "mailnews.message_warning_size" #define PREF_MAIL_COLLECT_EMAIL_ADDRESS_OUTGOING "mail.collect_email_address_outgoing" #define ATTR_MOZ_DO_NOT_SEND "moz-do-not-send" enum { kDefaultMode = (PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE) }; static bool mime_use_quoted_printable_p = false; // // Ugh, we need to do this currently to access this boolean. // bool UseQuotedPrintable(void) { return mime_use_quoted_printable_p; } /* This function will parse a list of email addresses and groups and just * return a list of email addresses (recipient) * * The input could be: * [recipient | group] *[,recipient | group] * * The group syntax is: * group-name:[recipient *[,recipient]]; * * the output will be: * recipient *[, recipient] * * As the result will always be equal or smaller than the input string, * the extraction will be made in place. Don't need to create a new buffer. */ static nsresult StripOutGroupNames(char * addresses) { char aChar; char * readPtr = addresses; // current read position char * writePtr = addresses; // current write position char * previousSeparator = addresses; // remember last time we wrote a recipient separator char * endPtr = addresses + PL_strlen(addresses); bool quoted = false; // indicate if we are between double quote bool group = false; // indicate if we found a group prefix bool atFound = false; // indicate if we found an @ in the current recipient. group name should not have an @ while (readPtr < endPtr) { aChar = *readPtr; readPtr ++; switch(aChar) { case '\\': if (*readPtr == '"') //ignore escaped quote readPtr ++; continue; case '"': quoted = !quoted; break; case '@': if (!quoted) atFound = true; break; case ':': if (!quoted && !atFound) { // ok, we found a group name // let's backup the write cursor to remove the group name writePtr = previousSeparator + 1; group = true; continue; } break; case ';': if (quoted || !group) break; else group = false; //end of the group, act like a recipient separator now... /* NO BREAK */ MOZ_FALLTHROUGH; case ',': if (!quoted) { atFound = false; //let check if we already have a comma separator in the output string if (writePtr > addresses && *(writePtr - 1) == ',') writePtr --; *writePtr = ','; previousSeparator = writePtr; writePtr ++; continue; } break; } *writePtr = aChar; writePtr ++; } if (writePtr > addresses && *(writePtr - 1) == ',') writePtr --; *writePtr = '\0'; return NS_OK; } // This private class just provides us an external URL listener, with callback functionality. class MsgDeliveryListener : public nsIUrlListener { public: MsgDeliveryListener(nsIMsgSend *aMsgSend, bool inIsNewsDelivery); NS_DECL_ISUPPORTS NS_DECL_NSIURLLISTENER private: virtual ~MsgDeliveryListener(); nsCOMPtr mMsgSend; bool mIsNewsDelivery; }; NS_IMPL_ISUPPORTS(MsgDeliveryListener, nsIUrlListener) MsgDeliveryListener::MsgDeliveryListener(nsIMsgSend *aMsgSend, bool inIsNewsDelivery) { mMsgSend = aMsgSend; mIsNewsDelivery = inIsNewsDelivery; } MsgDeliveryListener::~MsgDeliveryListener() { } NS_IMETHODIMP MsgDeliveryListener::OnStartRunningUrl(nsIURI *url) { if (mMsgSend) mMsgSend->NotifyListenerOnStartSending(nullptr, 0); return NS_OK; } NS_IMETHODIMP MsgDeliveryListener::OnStopRunningUrl(nsIURI *url, nsresult aExitCode) { if (url) { nsCOMPtr mailUrl = do_QueryInterface(url); if (mailUrl) mailUrl->UnRegisterListener(this); } // Let mMsgSend sort out the OnStopSending notification - it knows more about // the messages than we do. if (mMsgSend) mMsgSend->SendDeliveryCallback(url, mIsNewsDelivery, aExitCode); return NS_OK; } /* the following macro actually implement addref, release and query interface for our component. */ NS_IMPL_ISUPPORTS(nsMsgComposeAndSend, nsIMsgSend, nsIMsgOperationListener, nsISupportsWeakReference) nsMsgComposeAndSend::nsMsgComposeAndSend() : m_messageKey(nsMsgKey_None) { mGUINotificationEnabled = true; mAbortInProcess = false; mMultipartRelatedAttachmentCount = -1; mSendMailAlso = false; m_dont_deliver_p = false; m_deliver_mode = nsMsgDeliverNow; m_pre_snarfed_attachments_p = false; m_digest_p = false; m_be_synchronous_p = false; m_attachment1_type = 0; m_attachment1_encoding = 0; m_attachment1_body = 0; m_attachment1_body_length = 0; m_attachment_count = 0; m_attachment_pending_count = 0; m_status = NS_OK; m_plaintext = nullptr; m_related_part = nullptr; m_related_body_part = nullptr; mOriginalHTMLBody = nullptr; mNeedToPerformSecondFCC = false; mPerformingSecondFCC = false; mPreloadedAttachmentCount = 0; mRemoteAttachmentCount = 0; mCompFieldLocalAttachments = 0; mCompFieldRemoteAttachments = 0; mMessageWarningSize = 0; mSendReport = new nsMsgSendReport(); } nsMsgComposeAndSend::~nsMsgComposeAndSend() { PR_Free(m_attachment1_type); PR_Free(m_attachment1_encoding); PR_Free(m_attachment1_body); PR_Free(mOriginalHTMLBody); if (m_plaintext) { if (m_plaintext->mTmpFile) m_plaintext->mTmpFile->Remove(false); m_plaintext = nullptr; } if (mHTMLFile) mHTMLFile->Remove(false); if (mCopyFile) mCopyFile->Remove(false); if (mCopyFile2) mCopyFile2->Remove(false); if (mTempFile && !mReturnFile) mTempFile->Remove(false); m_attachments.Clear(); } NS_IMETHODIMP nsMsgComposeAndSend::GetDefaultPrompt(nsIPrompt ** aPrompt) { NS_ENSURE_ARG(aPrompt); *aPrompt = nullptr; nsresult rv = NS_OK; if (mParentWindow) { rv = mParentWindow->GetPrompter(aPrompt); if (NS_SUCCEEDED(rv) && *aPrompt) return NS_OK; } /* If we cannot find a prompter, try the mail3Pane window */ nsCOMPtr msgWindow; nsCOMPtr mailSession (do_GetService(NS_MSGMAILSESSION_CONTRACTID)); if (mailSession) { mailSession->GetTopmostMsgWindow(getter_AddRefs(msgWindow)); if (msgWindow) rv = msgWindow->GetPromptDialog(aPrompt); } return rv; } nsresult nsMsgComposeAndSend::GetNotificationCallbacks(nsIInterfaceRequestor** aCallbacks) { // TODO: stop using mail3pane window! nsCOMPtr msgWindow; nsCOMPtr mailSession(do_GetService(NS_MSGMAILSESSION_CONTRACTID)); mailSession->GetTopmostMsgWindow(getter_AddRefs(msgWindow)); if (msgWindow) { nsCOMPtr docShell; msgWindow->GetRootDocShell(getter_AddRefs(docShell)); nsCOMPtr ir(do_QueryInterface(docShell)); nsCOMPtr notificationCallbacks; msgWindow->GetNotificationCallbacks(getter_AddRefs(notificationCallbacks)); if (notificationCallbacks) { nsCOMPtr aggregrateIR; MsgNewInterfaceRequestorAggregation(notificationCallbacks, ir, getter_AddRefs(aggregrateIR)); ir = aggregrateIR; } if (ir) { NS_ADDREF(*aCallbacks = ir); return NS_OK; } } return NS_ERROR_FAILURE; } static char *mime_mailto_stream_read_buffer = 0; static char *mime_mailto_stream_write_buffer = 0; char * mime_get_stream_write_buffer(void) { if (!mime_mailto_stream_write_buffer) mime_mailto_stream_write_buffer = (char *) PR_Malloc(MIME_BUFFER_SIZE); return mime_mailto_stream_write_buffer; } static bool isEmpty(const char* aString) { return (!aString) || (!*aString); } void nsMsgComposeAndSend::GenerateMessageId() { if (isEmpty(mCompFields->GetMessageId())) { if (isEmpty(mCompFields->GetTo()) && isEmpty(mCompFields->GetCc()) && isEmpty(mCompFields->GetBcc()) && !isEmpty(mCompFields->GetNewsgroups())) { bool generateNewsMessageId = false; mUserIdentity->GetBoolAttribute("generate_news_message_id", &generateNewsMessageId); if (!generateNewsMessageId) return; } char* msgID = msg_generate_message_id(mUserIdentity); mCompFields->SetMessageId(msgID); PR_Free(msgID); } } // Don't I18N this line...this is per the spec! #define MIME_MULTIPART_BLURB "This is a multi-part message in MIME format." /* All of the desired attachments have been written to individual temp files, and we know what's in them. Now we need to make a final temp file of the actual mail message, containing all of the other files after having been encoded as appropriate. */ NS_IMETHODIMP nsMsgComposeAndSend::GatherMimeAttachments() { bool shouldDeleteDeliveryState = true; nsresult status; uint32_t i; PRFileDesc *in_file = 0; char *buffer = 0; nsString msg; bool body_is_us_ascii = true; bool isUsingQP = false; nsMsgSendPart* toppart = nullptr; // The very top most container of the message // that we are going to send. nsMsgSendPart* mainbody = nullptr; // The leaf node that contains the text of the // message we're going to contain. nsMsgSendPart* maincontainer = nullptr; // The direct child of toppart that will // contain the mainbody. If mainbody is // the same as toppart, then this is // also the same. But if mainbody is // to end up somewhere inside of a // multipart/alternative or a // multipart/related, then this is that // multipart object. nsMsgSendPart* plainpart = nullptr; // If we converted HTML into plaintext, // the message or child containing the plaintext // goes here. (Need to use this to determine // what headers to append/set to the main // message body.) uint32_t multipartRelatedCount = GetMultipartRelatedCount(); // The number of related part we will have to generate nsCOMPtr promptObject; // only used if we have to show an alert here.... GetDefaultPrompt(getter_AddRefs(promptObject)); char *hdrs = 0; bool maincontainerISrelatedpart = false; const char * toppart_type = nullptr; status = m_status; if (NS_FAILED(status)) goto FAIL; if (!m_attachment1_type) { m_attachment1_type = PL_strdup(TEXT_PLAIN); if (!m_attachment1_type) goto FAILMEM; } nsresult rv; // If we have a text/html main part, and we need a plaintext attachment, then // we'll do so now. This is an asynchronous thing, so we'll kick it off and // count on getting back here when it finishes. if (m_plaintext == nullptr && (mCompFields->GetForcePlainText() || mCompFields->GetUseMultipartAlternative()) && m_attachment1_body && PL_strcmp(m_attachment1_type, TEXT_HTML) == 0) { // // If we get here, we have an HTML body, but we really need to send // a text/plain message, so we will write the HTML out to a disk file, // fire off another URL request for this local disk file and that will // take care of the conversion... // rv = nsMsgCreateTempFile("nsemail.html", getter_AddRefs(mHTMLFile)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr tempfile; rv = MsgNewBufferedFileOutputStream(getter_AddRefs(tempfile), mHTMLFile, -1, 00600); if (NS_FAILED(rv)) { if (mSendReport) { nsAutoString error_msg; nsMsgBuildMessageWithTmpFile(mHTMLFile, error_msg); mSendReport->SetMessage(nsIMsgSendReport::process_Current, error_msg.get(), false); } status = NS_MSG_UNABLE_TO_OPEN_TMP_FILE; goto FAIL; } if (mOriginalHTMLBody) { uint32_t origLen = strlen(mOriginalHTMLBody); uint32_t n; nsresult rv = tempfile->Write(mOriginalHTMLBody, origLen, &n); if (NS_FAILED(rv) || n != origLen) { status = NS_MSG_ERROR_WRITING_FILE; goto FAIL; } } if (NS_FAILED(tempfile->Flush())) { status = NS_MSG_ERROR_WRITING_FILE; goto FAIL; } tempfile->Close(); m_plaintext = new nsMsgAttachmentHandler; if (!m_plaintext) goto FAILMEM; m_plaintext->SetMimeDeliveryState(this); m_plaintext->m_bogus_attachment = true; nsAutoCString tempURL; rv = NS_GetURLSpecFromFile(mHTMLFile, tempURL); if (NS_FAILED(rv) || NS_FAILED(nsMsgNewURL(getter_AddRefs(m_plaintext->mURL), tempURL.get()))) { m_plaintext = nullptr; goto FAILMEM; } m_plaintext->m_type = TEXT_HTML; m_plaintext->m_charset = mCompFields->GetCharacterSet(); m_plaintext->m_desiredType = TEXT_PLAIN; m_attachment_pending_count ++; status = m_plaintext->SnarfAttachment(mCompFields); if (NS_FAILED(status)) goto FAIL; if (m_attachment_pending_count > 0) return NS_OK; } /* Kludge to avoid having to allocate memory on the toy computers... */ buffer = mime_get_stream_write_buffer(); if (! buffer) goto FAILMEM; NS_ASSERTION (m_attachment_pending_count == 0, "m_attachment_pending_count != 0"); mComposeBundle->GetStringFromName(u"assemblingMessage", getter_Copies(msg)); SetStatusMessage( msg ); /* First, open the message file. */ rv = nsMsgCreateTempFile("nsemail.eml", getter_AddRefs(mTempFile)); NS_ENSURE_SUCCESS(rv, rv); rv = MsgNewBufferedFileOutputStream(getter_AddRefs(mOutputFile), mTempFile, -1, 00600); if (NS_FAILED(rv)) { status = NS_MSG_UNABLE_TO_OPEN_TMP_FILE; if (mSendReport) { nsAutoString error_msg; nsMsgBuildMessageWithTmpFile(mTempFile, error_msg); mSendReport->SetMessage(nsIMsgSendReport::process_Current, error_msg.get(), false); } goto FAIL; } // generate a message id, if necessary GenerateMessageId( ); mainbody = new nsMsgSendPart(this, mCompFields->GetCharacterSet()); if (!mainbody) goto FAILMEM; mainbody->SetMainPart(true); mainbody->SetType(m_attachment1_type ? m_attachment1_type : TEXT_PLAIN); NS_ASSERTION(mainbody->GetBuffer() == nullptr, "not-null buffer"); status = mainbody->SetBuffer(m_attachment1_body ? m_attachment1_body : ""); if (NS_FAILED(status)) goto FAIL; // Determine the encoding of the main message body before we free it. PR_FREEIF(m_attachment1_encoding); if (m_attachment1_body) mCompFields->GetBodyIsAsciiOnly(&body_is_us_ascii); if (!mCompFields->GetForceMsgEncoding() && (body_is_us_ascii || nsMsgI18Nstateful_charset(mCompFields->GetCharacterSet()))) { m_attachment1_encoding = PL_strdup (ENCODING_7BIT); } else if (mime_use_quoted_printable_p) { m_attachment1_encoding = PL_strdup (ENCODING_QUOTED_PRINTABLE); isUsingQP = true; } else { m_attachment1_encoding = PL_strdup (ENCODING_8BIT); } // Make sure the lines don't get to long. if (m_attachment1_body) { uint32_t max_column = 0; uint32_t cur_column = 0; for (char* c = m_attachment1_body; *c; c++) { if (*c == '\n') { if (cur_column > max_column) max_column = cur_column; cur_column = 0; } else if (*c != '\r') { cur_column++; } } // Check one last time for the last line. if (cur_column > max_column) max_column = cur_column; if (max_column > LINELENGTH_ENCODING_THRESHOLD && !isUsingQP) { // To encode "long lines" use a CTE that will transmit shorter lines. // Switch to base64 if we are not already using "quoted printable". PR_FREEIF(m_attachment1_encoding); m_attachment1_encoding = PL_strdup (ENCODING_BASE64); } } PR_FREEIF (m_attachment1_body); maincontainer = mainbody; // If we were given a pre-saved collection of HTML and contained images, // then we want mainbody to point to the HTML lump therein. if (m_related_part) { // If m_related_part is of type text/html, set both maincontainer // and mainbody to point to it. If m_related_part is multipart/related, // however, set mainbody to be the first child within m_related_part. delete mainbody; // No matter what, maincontainer points to the outermost related part. maincontainer = m_related_part; maincontainerISrelatedpart = true; mainbody = m_related_part->GetChild(0); mainbody->SetMainPart(true); } if (m_plaintext) { // // OK. We have a plaintext version of the main body that we want to // send instead of or with the text/html. Shove it in. // plainpart = new nsMsgSendPart(this, mCompFields->GetCharacterSet()); if (!plainpart) goto FAILMEM; status = plainpart->SetType(TEXT_PLAIN); if (NS_FAILED(status)) goto FAIL; status = plainpart->SetFile(m_plaintext->mTmpFile); if (NS_FAILED(status)) goto FAIL; m_plaintext->mMainBody = true; // Determine Content-Transfer-Encoding for the attachments. m_plaintext->PickEncoding(mCompFields->GetCharacterSet(), this); const char *charset = mCompFields->GetCharacterSet(); hdrs = mime_generate_attachment_headers(m_plaintext->m_type.get(), nullptr, m_plaintext->m_encoding.get(), m_plaintext->m_description.get(), m_plaintext->m_xMacType.get(), m_plaintext->m_xMacCreator.get(), nullptr, 0, m_digest_p, m_plaintext, charset, charset, body_is_us_ascii, nullptr, true); if (!hdrs) goto FAILMEM; status = plainpart->SetOtherHeaders(hdrs); PR_Free(hdrs); hdrs = nullptr; if (NS_FAILED(status)) goto FAIL; if (mCompFields->GetUseMultipartAlternative()) { nsMsgSendPart* htmlpart = maincontainer; maincontainer = new nsMsgSendPart(this); if (!maincontainer) goto FAILMEM; // Setup the maincontainer stuff... status = maincontainer->SetType(MULTIPART_ALTERNATIVE); if (NS_FAILED(status)) goto FAIL; status = maincontainer->AddChild(plainpart); if (NS_FAILED(status)) goto FAIL; status = maincontainer->AddChild(htmlpart); if (NS_FAILED(status)) goto FAIL; // Create the encoder for the plaintext part here, // because we aren't the main part (attachment1). // (This, along with the rest of the routine, should really // be restructured so that no special treatment is given to // the main body text that came in. Best to put attachment1_text // etc. into a nsMsgSendPart, then reshuffle the parts. Sigh.) if (m_plaintext->m_encoding.LowerCaseEqualsLiteral(ENCODING_QUOTED_PRINTABLE)) { plainpart->SetEncoder(MimeEncoder::GetQPEncoder( mime_encoder_output_fn, this)); } else if (m_plaintext->m_encoding.LowerCaseEqualsLiteral(ENCODING_BASE64)) { plainpart->SetEncoder(MimeEncoder::GetBase64Encoder( mime_encoder_output_fn, this)); } } else { delete maincontainer; if (maincontainerISrelatedpart) m_related_part = nullptr; // in that case, m_related_part == maincontainer which we have just deleted! maincontainer = plainpart; mainbody = maincontainer; PR_FREEIF(m_attachment1_type); m_attachment1_type = PL_strdup(TEXT_PLAIN); if (!m_attachment1_type) goto FAILMEM; // Override attachment1_encoding here. We do this blindly since we are // sending plaintext only at this point. PR_FREEIF(m_attachment1_encoding); m_attachment1_encoding = ToNewCString(m_plaintext->m_encoding); } } // check if we need to encapsulate the message in a multipart/mixed or multipart/digest if (m_attachment_count > multipartRelatedCount) { toppart = new nsMsgSendPart(this); if (!toppart) goto FAILMEM; status = toppart->SetType(m_digest_p ? MULTIPART_DIGEST : MULTIPART_MIXED); if (NS_FAILED(status)) goto FAIL; status = toppart->AddChild(maincontainer); if (NS_FAILED(status)) goto FAIL; } else toppart = maincontainer; // Is the top part a multipart container? // can't use m_attachment_count because it's not reliable for that // instead use type of main part. See bug #174396 toppart_type = toppart->GetType(); // GetType return directly the member variable, don't free it! if (!m_crypto_closure && toppart_type && !PL_strncasecmp(toppart_type, "multipart/", 10)) { status = toppart->SetBuffer(MIME_MULTIPART_BLURB); if (NS_FAILED(status)) goto FAIL; } { nsCOMPtr outputHeaders = do_CreateInstance(NS_ISTRUCTUREDHEADERS_CONTRACTID); status = mime_generate_headers(mCompFields, m_deliver_mode, outputHeaders); if (NS_FAILED(status)) goto FAIL; // Convert the blocks of headers into a single string for emission. nsAutoCString headers; outputHeaders->BuildMimeText(headers); // If we converted HTML into plaintext, the plaintext part (plainpart) // already has its content-type and content-transfer-encoding ("other") // headers set. // // In the specific case where such a plaintext part is the top-level message // part (iff an HTML message is being sent as text only and no other // attachments exist) we want to preserve the original plainpart headers, // since they contain accurate transfer encoding and Mac type/creator // information. // // So, in the above case we append the main message headers, otherwise we // overwrite whatever headers may have existed. if (plainpart && plainpart == toppart) status = toppart->AppendOtherHeaders(headers.get()); else status = toppart->SetOtherHeaders(headers.get()); } if (NS_FAILED(status)) goto FAIL; // Set up the first part (user-typed.) For now, do it even if the first // part is empty; we need to add things to skip it if this part is empty. // Set up encoder for the first part (message body.) // NS_ASSERTION(!m_attachment1_encoder, "not-null m_attachment1_encoder"); if (!PL_strcasecmp(m_attachment1_encoding, ENCODING_BASE64)) { m_attachment1_encoder = MimeEncoder::GetBase64Encoder( mime_encoder_output_fn, this); } else if (!PL_strcasecmp(m_attachment1_encoding, ENCODING_QUOTED_PRINTABLE)) { m_attachment1_encoder = MimeEncoder::GetQPEncoder(mime_encoder_output_fn, this); } // If we converted HTML into plaintext, the plaintext part // already has its type/encoding headers set. So, in the specific // case where such a plaintext part is the main message body // (iff an HTML message is being sent as text only) // we want to avoid generating type/encoding/digest headers; // in all other cases, generate such headers here. // // We really want to set up headers as a dictionary of some sort // so that we need not worry about duplicate header lines. // if ((!plainpart) || (plainpart != mainbody)) { const char *charset = mCompFields->GetCharacterSet(); hdrs = mime_generate_attachment_headers (m_attachment1_type, nullptr, m_attachment1_encoding, 0, 0, 0, 0, 0, m_digest_p, nullptr, /* no "ma"! */ charset, charset, mCompFields->GetBodyIsAsciiOnly(), nullptr, true); if (!hdrs) goto FAILMEM; status = mainbody->AppendOtherHeaders(hdrs); if (NS_FAILED(status)) goto FAIL; } PR_FREEIF(hdrs); mainbody->SetEncoder(m_attachment1_encoder.forget()); // // Now we need to process attachments and slot them in the // correct hierarchy. // if (m_attachment_count > 0) { // Kludge to avoid having to allocate memory on the toy computers... if (! mime_mailto_stream_read_buffer) mime_mailto_stream_read_buffer = (char *) PR_Malloc (MIME_BUFFER_SIZE); buffer = mime_mailto_stream_read_buffer; if (! buffer) goto FAILMEM; // Gather all of the attachments for this message that are NOT // part of an enclosed MHTML message! for (i = 0; i < m_attachment_count; i++) { nsMsgAttachmentHandler *ma = m_attachments[i]; if (!ma->mMHTMLPart) PreProcessPart(ma, toppart); } // // If we have a m_related_part as a container for children, then we have to // tack on these children for the part // if (m_related_part) { for (i = 0; i < m_attachment_count; i++) { // // look for earlier part with the same content id. If we find it, // need to remember the mapping between our node index and the // part num of the earlier part. int32_t nodeIndex = m_attachments[i]->mNodeIndex; if (nodeIndex != -1) { for (uint32_t j = 0; j < i; j++) { if (m_attachments[j]->mNodeIndex != -1 && m_attachments[j]->m_contentId.Equals(m_attachments[i]->m_contentId)) m_partNumbers[nodeIndex] = m_partNumbers[m_attachments[j]->mNodeIndex]; } } // rhp: This is here because we could get here after saying OK // to a lot of prompts about not being able to fetch this part! // if (m_attachments[i]->mPartUserOmissionOverride) continue; // Now, we need to add this part to the m_related_part member so the // message will be generated correctly. if (m_attachments[i]->mMHTMLPart) PreProcessPart(m_attachments[i], m_related_part); } } } // Tell the user we are creating the message... mComposeBundle->GetStringFromName(u"creatingMailMessage", getter_Copies(msg)); SetStatusMessage( msg ); // OK, now actually write the structure we've carefully built up. status = toppart->Write(); if (NS_FAILED(status)) goto FAIL; /* Close down encryption stream */ if (m_crypto_closure) { status = m_crypto_closure->FinishCryptoEncapsulation(false, mSendReport); m_crypto_closure = nullptr; if (NS_FAILED(status)) goto FAIL; } if (mOutputFile) { if (NS_FAILED(mOutputFile->Flush())) { status = NS_MSG_ERROR_WRITING_FILE; goto FAIL; } mOutputFile->Close(); mOutputFile = nullptr; // mTempFile is stale because we wrote to it. Get another copy to refresh. nsCOMPtr tempFileCopy; mTempFile->Clone(getter_AddRefs(tempFileCopy)); mTempFile = tempFileCopy; tempFileCopy = nullptr; /* If we don't do this check...ZERO length files can be sent */ int64_t fileSize; rv = mTempFile->GetFileSize(&fileSize); if (NS_FAILED(rv) || fileSize == 0) { status = NS_MSG_ERROR_WRITING_FILE; goto FAIL; } } mComposeBundle->GetStringFromName(u"assemblingMessageDone", getter_Copies(msg)); SetStatusMessage(msg); if (m_dont_deliver_p && mListener) { // // Need to ditch the file spec here so that we don't delete the // file, since in this case, the caller wants the file // mReturnFile = mTempFile; mTempFile = nullptr; if (!mReturnFile) NotifyListenerOnStopSending(nullptr, NS_ERROR_OUT_OF_MEMORY, nullptr, nullptr); else { NotifyListenerOnStopSending(nullptr, NS_OK, nullptr, mReturnFile); } } else { status = DeliverMessage(); if (NS_SUCCEEDED(status)) shouldDeleteDeliveryState = false; } goto FAIL; FAILMEM: status = NS_ERROR_OUT_OF_MEMORY; FAIL: if (toppart) delete toppart; toppart = nullptr; mainbody = nullptr; maincontainer = nullptr; if (in_file) { PR_Close (in_file); in_file = nullptr; } if (shouldDeleteDeliveryState) { if (NS_FAILED(status)) { m_status = status; nsresult ignoreMe; Fail(status, nullptr, &ignoreMe); } } return status; } int32_t nsMsgComposeAndSend::PreProcessPart(nsMsgAttachmentHandler *ma, nsMsgSendPart *toppart) // The very top most container of the message { nsresult status; char *hdrs = 0; nsMsgSendPart *part = nullptr; // If this was one of those dead parts from a quoted web page, // then just return safely. // if (ma->m_bogus_attachment) return 0; // If at this point we *still* don't have a content-type, then // we're never going to get one. if (ma->m_type.IsEmpty()) ma->m_type = UNKNOWN_CONTENT_TYPE; ma->PickEncoding(mCompFields->GetCharacterSet(), this); ma->PickCharset(); part = new nsMsgSendPart(this); if (!part) return 0; status = toppart->AddChild(part); // Remember the part number if it has a node index. if (ma->mNodeIndex != -1) m_partNumbers[ma->mNodeIndex] = part->m_partNum; if (NS_FAILED(status)) return 0; status = part->SetType(ma->m_type.get()); if (NS_FAILED(status)) return 0; if (ma->mSendViaCloud) ma->m_encoding = ENCODING_7BIT; nsCString turl; if (!ma->mURL) { if (!ma->m_uri.IsEmpty()) turl = ma->m_uri; } else { status = ma->mURL->GetSpec(turl); if (NS_FAILED(status)) return 0; } nsCString type(ma->m_type); nsCString realName(ma->m_realName); // for cloud attachments, make the part an html part with no name, // so we don't show it as an attachment. if (ma->mSendViaCloud) { type.Assign("application/octet-stream"); realName.Truncate(); } hdrs = mime_generate_attachment_headers (type.get(), ma->m_typeParam.get(), ma->m_encoding.get(), ma->m_description.get(), ma->m_xMacType.get(), ma->m_xMacCreator.get(), realName.get(), turl.get(), m_digest_p, ma, ma->m_charset.get(), // rhp - this needs // to be the charset // we determine from // the file or none // at all! mCompFields->GetCharacterSet(), false, // bodyIsAsciiOnly to false // for attachments ma->m_contentId.get(), false); if (!hdrs) return 0; status = part->SetOtherHeaders(hdrs); PR_FREEIF(hdrs); if (ma->mSendViaCloud) { nsCString urlSpec; status = ma->mURL->GetSpec(urlSpec); if (NS_FAILED(status)) return 0; // Need to add some headers so that libmime can restore the cloud info // when loading a draft message. nsCString draftInfo(HEADER_X_MOZILLA_CLOUD_PART": cloudFile; url="); draftInfo.Append(ma->mCloudUrl.get()); // don't leak user file paths or account keys to recipients. if (m_deliver_mode == nsMsgSaveAsDraft) { draftInfo.Append("; provider="); draftInfo.Append(ma->mCloudProviderKey.get()); draftInfo.Append("; file="); draftInfo.Append(urlSpec.get()); } draftInfo.Append("; name="); draftInfo.Append(ma->m_realName.get()); draftInfo.Append(CRLF); part->AppendOtherHeaders(draftInfo.get()); part->SetType("application/octet-stream"); part->SetBuffer(""); } if (NS_FAILED(status)) return 0; status = part->SetFile(ma->mTmpFile); if (NS_FAILED(status)) return 0; if (ma->m_encoder) { part->SetEncoder(ma->m_encoder.forget()); } ma->m_current_column = 0; if (ma->m_type.LowerCaseEqualsLiteral(MESSAGE_RFC822) || ma->m_type.LowerCaseEqualsLiteral(MESSAGE_NEWS)) { part->SetStripSensitiveHeaders(true); } return 1; } # define FROB(X) \ if (X && *X) \ { \ if (*recipients) PL_strcat(recipients, ","); \ PL_strcat(recipients, X); \ } nsresult nsMsgComposeAndSend::BeginCryptoEncapsulation () { // Try to create a secure compose object. If we can create it, then query to see // if we need to use it for this send transaction. nsresult rv = NS_OK; nsCOMPtr secureCompose; secureCompose = do_CreateInstance(NS_MSGCOMPOSESECURE_CONTRACTID, &rv); // it's not an error scenario of there is secure compose if (NS_FAILED(rv)) return NS_OK; if (secureCompose) { bool requiresEncryptionWork = false; secureCompose->RequiresCryptoEncapsulation(mUserIdentity, mCompFields, &requiresEncryptionWork); if (requiresEncryptionWork) { m_crypto_closure = secureCompose; // bah i'd like to move the following blurb into the implementation of BeginCryptoEncapsulation; however // the apis for nsIMsgComposeField just aren't rich enough. It requires the implementor to jump through way // too many string conversions.... char * recipients = (char *) PR_MALLOC((mCompFields->GetTo() ? strlen(mCompFields->GetTo()) : 0) + (mCompFields->GetCc() ? strlen(mCompFields->GetCc()) : 0) + (mCompFields->GetBcc() ? strlen(mCompFields->GetBcc()) : 0) + (mCompFields->GetNewsgroups() ? strlen(mCompFields->GetNewsgroups()) : 0) + 20); if (!recipients) return NS_ERROR_OUT_OF_MEMORY; *recipients = 0; FROB(mCompFields->GetTo()) FROB(mCompFields->GetCc()) FROB(mCompFields->GetBcc()) FROB(mCompFields->GetNewsgroups()) // end section of code I'd like to move to the implementor..... rv = m_crypto_closure->BeginCryptoEncapsulation(mOutputFile, recipients, mCompFields, mUserIdentity, mSendReport, (m_deliver_mode == nsMsgSaveAsDraft)); PR_FREEIF(recipients); } } return rv; } nsresult mime_write_message_body(nsIMsgSend *state, const char *buf, uint32_t size) { NS_ENSURE_ARG_POINTER(state); nsCOMPtr output; nsCOMPtr crypto_closure; state->GetOutputStream(getter_AddRefs(output)); if (!output) return NS_MSG_ERROR_WRITING_FILE; state->GetCryptoclosure(getter_AddRefs(crypto_closure)); if (crypto_closure) { // Copy to new null-terminated string so JS glue doesn't crash when // MimeCryptoWriteBlock() is implemented in JS. nsCString bufWithNull; bufWithNull.Assign(buf, size); return crypto_closure->MimeCryptoWriteBlock(bufWithNull.get(), size); } uint32_t n; nsresult rv = output->Write(buf, size, &n); if (NS_FAILED(rv) || n != size) { return NS_MSG_ERROR_WRITING_FILE; } return NS_OK; } nsresult mime_encoder_output_fn(const char *buf, int32_t size, void *closure) { nsMsgComposeAndSend *state = (nsMsgComposeAndSend *) closure; return mime_write_message_body (state, (char *) buf, (uint32_t)size); } nsresult nsMsgComposeAndSend::GetEmbeddedObjectInfo(nsIDOMNode *node, nsMsgAttachmentData *attachment, bool *acceptObject) { NS_ENSURE_ARG_POINTER(node); NS_ENSURE_ARG_POINTER(attachment); NS_ENSURE_ARG_POINTER(acceptObject); // GetEmbeddedObjectInfo will determine if we need to attach the source of the // embedded object with the message. The decision is made automatically unless // the attribute moz-do-not-send has been set to true or false. nsresult rv = NS_OK; // Reset this structure to null! *acceptObject = false; // We're only interested in body, image, link and anchors which are all // elements. nsCOMPtr domElement = do_QueryInterface(node); if (!domElement) return NS_OK; bool isImage = false; nsAutoString mozDoNotSendAttr; domElement->GetAttribute(NS_LITERAL_STRING(ATTR_MOZ_DO_NOT_SEND), mozDoNotSendAttr); // Only empty or moz-do-not-send="false" may be accepted later. if (!(mozDoNotSendAttr.IsEmpty() || mozDoNotSendAttr.LowerCaseEqualsLiteral("false"))) return NS_OK; // Now, we know the types of objects this node can be, so we will do // our query interface here and see what we come up with nsCOMPtr body = (do_QueryInterface(node)); // XXX convert to use nsIImageLoadingContent? nsCOMPtr image = (do_QueryInterface(node)); nsCOMPtr link = (do_QueryInterface(node)); nsCOMPtr anchor = (do_QueryInterface(node)); // First, try to see if the body as a background image if (body) { nsAutoString tUrl; if (NS_SUCCEEDED(body->GetBackground(tUrl))) { nsAutoCString turlC; CopyUTF16toUTF8(tUrl, turlC); if (NS_FAILED(nsMsgNewURL(getter_AddRefs(attachment->m_url), turlC.get()))) return NS_OK; } isImage = true; } else if (image) // Is this an image? { nsString tUrl; nsString tName; nsString tDesc; // Create the URI if (NS_FAILED(image->GetSrc(tUrl))) return NS_ERROR_FAILURE; if (tUrl.IsEmpty()) return NS_OK; nsAutoCString turlC; CopyUTF16toUTF8(tUrl, turlC); if (NS_FAILED(nsMsgNewURL(getter_AddRefs(attachment->m_url), turlC.get()))) { // Well, the first time failed...which means we probably didn't get // the full path name... // nsIDOMDocument *ownerDocument = nullptr; node->GetOwnerDocument(&ownerDocument); if (ownerDocument) { nsIDocument *doc = nullptr; if (NS_FAILED(ownerDocument->QueryInterface(NS_GET_IID(nsIDocument),(void**)&doc)) || !doc) return NS_ERROR_OUT_OF_MEMORY; nsAutoCString spec; nsIURI *uri = doc->GetDocumentURI(); if (!uri) return NS_ERROR_OUT_OF_MEMORY; rv = uri->GetSpec(spec); NS_ENSURE_SUCCESS(rv, rv); // Ok, now get the path to the root doc and tack on the name we // got from the GetSrc() call.... NS_ConvertUTF8toUTF16 workURL(spec); int32_t loc = workURL.RFindChar('/'); if (loc >= 0) workURL.SetLength(loc+1); workURL.Append(tUrl); NS_ConvertUTF16toUTF8 workurlC(workURL); if (NS_FAILED(nsMsgNewURL(getter_AddRefs(attachment->m_url), workurlC.get()))) return NS_OK; // Continue and send it without this image. } } isImage = true; rv = image->GetName(tName); NS_ENSURE_SUCCESS(rv, rv); LossyCopyUTF16toASCII(tName, attachment->m_realName); rv = image->GetLongDesc(tDesc); NS_ENSURE_SUCCESS(rv, rv); attachment->m_description = NS_LossyConvertUTF16toASCII(tDesc); // XXX i18n } else if (link) // Is this a link? { nsString tUrl; // Create the URI rv = link->GetHref(tUrl); NS_ENSURE_SUCCESS(rv, rv); if (tUrl.IsEmpty()) return NS_OK; nsAutoCString turlC; CopyUTF16toUTF8(tUrl, turlC); if (NS_FAILED(nsMsgNewURL(getter_AddRefs(attachment->m_url), turlC.get()))) return NS_OK; } else if (anchor) { nsString tUrl; nsString tName; // Create the URI rv = anchor->GetHref(tUrl); NS_ENSURE_SUCCESS(rv, rv); if (tUrl.IsEmpty()) return NS_OK; nsAutoCString turlC; CopyUTF16toUTF8(tUrl, turlC); // This can fail since the URL might not be recognised, for example: // Some Name if (NS_FAILED(nsMsgNewURL(getter_AddRefs(attachment->m_url), turlC.get()))) return NS_OK; rv = anchor->GetName(tName); NS_ENSURE_SUCCESS(rv, rv); LossyCopyUTF16toASCII(tName, attachment->m_realName); } else { // If we get here, we got something we didn't expect! // Just try to continue and send it without this thing. return NS_OK; } // Before going further, check what scheme we're dealing with. Files need to // be converted to data URLs during composition. "Attaching" means // sending as a cid: part instead of original URL. bool isHttp = (NS_SUCCEEDED(attachment->m_url->SchemeIs("http", &isHttp)) && isHttp) || (NS_SUCCEEDED(attachment->m_url->SchemeIs("https", &isHttp)) && isHttp); // Attach (= create cid: part) http resources if moz-do-not-send is set to // "false". Special processing for images: We attach if the preference says so. // Note that moz-do-not-send="true" is already processed above so the preference // doesn't override this. if (isHttp) { *acceptObject = (isImage && Preferences::GetBool("mail.compose.attach_http_images", false)) || mozDoNotSendAttr.LowerCaseEqualsLiteral("false"); return NS_OK; } bool isData = (NS_SUCCEEDED(attachment->m_url->SchemeIs("data", &isData)) && isData); bool isNews = (NS_SUCCEEDED(attachment->m_url->SchemeIs("news", &isNews)) && isNews) || (NS_SUCCEEDED(attachment->m_url->SchemeIs("snews", &isNews)) && isNews) || (NS_SUCCEEDED(attachment->m_url->SchemeIs("nntp", &isNews)) && isNews); // Attach (= create cid: part) data resources if moz-do-not-send is not // specified or set to "false". if (isData || isNews) { *acceptObject = mozDoNotSendAttr.IsEmpty() || mozDoNotSendAttr.LowerCaseEqualsLiteral("false"); return NS_OK; } return NS_OK; } uint32_t nsMsgComposeAndSend::GetMultipartRelatedCount(bool forceToBeCalculated /*=false*/) { nsresult rv = NS_OK; uint32_t count; if (mMultipartRelatedAttachmentCount != -1 && !forceToBeCalculated) return (uint32_t)mMultipartRelatedAttachmentCount; //First time here, let's calculate the correct number of related part we need to generate mMultipartRelatedAttachmentCount = 0; if (mEditor) { nsCOMPtr mailEditor (do_QueryInterface(mEditor)); if (!mailEditor) return 0; rv = mailEditor->GetEmbeddedObjects(getter_AddRefs(mEmbeddedObjectList)); if (NS_FAILED(rv)) return 0; } if (!mEmbeddedObjectList) return 0; if (NS_SUCCEEDED(mEmbeddedObjectList->GetLength(&count))) { if (count > 0) { // preallocate space for part numbers m_partNumbers.SetLength(count); // Let parse the list to count the number of valid objects. BTW, we can remove the others from the list RefPtr attachment(new nsMsgAttachmentData); int32_t i; nsCOMPtr node; for (i = count - 1, count = 0; i >= 0; i --) { // Reset this structure to null! // now we need to get the element in the array and do the magic // to process this element. // node = do_QueryElementAt(mEmbeddedObjectList, i, &rv); bool acceptObject = false; if (node) { rv = GetEmbeddedObjectInfo(node, attachment, &acceptObject); } else // outlook import case { nsCOMPtr imageData = do_QueryElementAt(mEmbeddedObjectList, i, &rv); if (!imageData) continue; acceptObject = true; } if (NS_SUCCEEDED(rv) && acceptObject) count ++; } } mMultipartRelatedAttachmentCount = (int32_t)count; return count; } else return 0; } nsresult nsMsgComposeAndSend::GetBodyFromEditor() { // // Now we have to fix up and get the HTML from the editor. After we // get the HTML data, we need to store it in the m_attachment_1_body // member variable after doing the necessary charset conversion. // // // Query the editor, get the body of HTML! // uint32_t flags = nsIDocumentEncoder::OutputFormatted | nsIDocumentEncoder::OutputNoFormattingInPre | nsIDocumentEncoder::OutputDisallowLineBreaking; nsAutoString bodyStr; char16_t* bodyText = nullptr; nsresult rv; char16_t *origHTMLBody = nullptr; // Ok, get the body...the DOM should have been whacked with // Content ID's already if (mEditor) mEditor->OutputToString(NS_LITERAL_STRING(TEXT_HTML), flags, bodyStr); else bodyStr = NS_ConvertASCIItoUTF16(m_attachment1_body); // If we really didn't get a body, just return NS_OK if (bodyStr.IsEmpty()) return NS_OK; bodyText = ToNewUnicode(bodyStr); if (!bodyText) return NS_ERROR_OUT_OF_MEMORY; // If we are forcing this to be plain text, we should not be // doing this conversion. bool doConversion = true; if ( (mCompFields) && mCompFields->GetForcePlainText() ) doConversion = false; if (doConversion) { nsCOMPtr conv = do_CreateInstance(MOZ_TXTTOHTMLCONV_CONTRACTID, &rv); if (NS_SUCCEEDED(rv)) { uint32_t whattodo = mozITXTToHTMLConv::kURLs; bool enable_structs = false; nsCOMPtr pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID)); if (pPrefBranch) { rv = pPrefBranch->GetBoolPref(PREF_MAIL_SEND_STRUCT, &enable_structs); if (enable_structs) whattodo = whattodo | mozITXTToHTMLConv::kStructPhrase; } char16_t* wresult; rv = conv->ScanHTML(bodyText, whattodo, &wresult); if (NS_SUCCEEDED(rv)) { // Save the original body for possible attachment as plain text // We should have what the user typed in stored in mOriginalHTMLBody origHTMLBody = bodyText; bodyText = wresult; } } } nsCString attachment1_body; // Convert body to mail charset nsCString outCString; const char *aCharset = mCompFields->GetCharacterSet(); if (aCharset && *aCharset) { rv = nsMsgI18NConvertFromUnicode(aCharset, nsDependentString(bodyText), outCString, false, true); bool isAsciiOnly = NS_IsAscii(outCString.get()) && !nsMsgI18Nstateful_charset(mCompFields->GetCharacterSet()); if (mCompFields->GetForceMsgEncoding()) isAsciiOnly = false; mCompFields->SetBodyIsAsciiOnly(isAsciiOnly); // If the body contains characters outside the current mail charset, // convert to UTF-8. if (NS_ERROR_UENC_NOMAPPING == rv) { bool needToCheckCharset; mCompFields->GetNeedToCheckCharset(&needToCheckCharset); if (needToCheckCharset) { // Just use UTF-8 and be done with it // unless disable_fallback_to_utf8 is set for this charset. bool disableFallback = false; nsCOMPtr prefBranch (do_GetService(NS_PREFSERVICE_CONTRACTID, &rv)); if (prefBranch) { nsCString prefName("mailnews.disable_fallback_to_utf8."); prefName.Append(aCharset); prefBranch->GetBoolPref(prefName.get(), &disableFallback); } if (!disableFallback) { CopyUTF16toUTF8(nsDependentString(bodyText), outCString); mCompFields->SetCharacterSet("UTF-8"); } } } if (NS_SUCCEEDED(rv)) attachment1_body = outCString; // If we have an origHTMLBody that is not null, this means that it is // different than the bodyText because of formatting conversions. Because of // this we need to do the charset conversion on this part separately if (origHTMLBody) { nsCString newBody; rv = nsMsgI18NConvertFromUnicode(aCharset, nsDependentString(origHTMLBody), newBody, false, true); if (NS_SUCCEEDED(rv)) { mOriginalHTMLBody = ToNewCString(newBody); } } else { mOriginalHTMLBody = ToNewCString(attachment1_body); } NS_Free(bodyText); //Don't need it anymore } else return NS_ERROR_FAILURE; rv = SnarfAndCopyBody(attachment1_body, TEXT_HTML); return rv; } // // This is the routine that does the magic of generating the body and the // attachments for the multipart/related email message. // typedef struct { nsIDOMNode *node; char *url; } domSaveStruct; nsresult nsMsgComposeAndSend::ProcessMultipartRelated(int32_t *aMailboxCount, int32_t *aNewsCount) { uint32_t multipartCount = GetMultipartRelatedCount(); nsresult rv = NS_OK; uint32_t i; int32_t j = -1; uint32_t k; int32_t duplicateOf; domSaveStruct *domSaveArray = nullptr; if (!mEmbeddedObjectList) return NS_ERROR_MIME_MPART_ATTACHMENT_ERROR; RefPtr attachment(new nsMsgAttachmentData); int32_t locCount = -1; if (multipartCount > 0) { domSaveArray = (domSaveStruct *)PR_MALLOC(sizeof(domSaveStruct) * multipartCount); if (!domSaveArray) return NS_ERROR_MIME_MPART_ATTACHMENT_ERROR; memset(domSaveArray, 0, sizeof(domSaveStruct) * multipartCount); } nsCOMPtr node; for (i = mPreloadedAttachmentCount; i < (mPreloadedAttachmentCount + multipartCount);) { // Ok, now we need to get the element in the array and do the magic // to process this element. // locCount++; mEmbeddedObjectList->QueryElementAt(locCount, NS_GET_IID(nsIDOMNode), getter_AddRefs(node)); if (node) { bool acceptObject = false; rv = GetEmbeddedObjectInfo(node, attachment, &acceptObject); NS_ENSURE_SUCCESS(rv, NS_ERROR_MIME_MPART_ATTACHMENT_ERROR); if (!acceptObject) continue; nsString nodeValue; node->GetNodeValue(nodeValue); LossyCopyUTF16toASCII(nodeValue, m_attachments[i]->m_contentId); } else { nsCOMPtr imageData = do_QueryElementAt(mEmbeddedObjectList, locCount, &rv); if (!imageData) return NS_ERROR_MIME_MPART_ATTACHMENT_ERROR; imageData->GetUri(getter_AddRefs(attachment->m_url)); if (!attachment->m_url) return NS_ERROR_MIME_MPART_ATTACHMENT_ERROR; imageData->GetCid(m_attachments[i]->m_contentId); imageData->GetName(attachment->m_realName); } // MUST set this to get placed in the correct part of the message m_attachments[i]->mMHTMLPart = true; m_attachments[i]->mDeleteFile = true; m_attachments[i]->m_done = false; m_attachments[i]->SetMimeDeliveryState(this); m_attachments[i]->mNodeIndex = locCount; j++; domSaveArray[j].node = node; // check if we have alreay attached this object, don't need to attach it twice duplicateOf = -1; for (k = mPreloadedAttachmentCount; k < i; k++) { bool isEqual = false; NS_ASSERTION(attachment->m_url, "null attachment url!"); if (attachment->m_url) (void)attachment->m_url->Equals(m_attachments[k]->mURL, &isEqual); if (isEqual) { duplicateOf = k; break; } } if (duplicateOf == -1) { // // Now we have to get all of the interesting information from // the nsIDOMNode we have in hand... m_attachments[i]->mURL = attachment->m_url; m_attachments[i]->m_overrideType = attachment->m_realType; m_attachments[i]->m_overrideEncoding = attachment->m_realEncoding; m_attachments[i]->m_desiredType = attachment->m_desiredType; m_attachments[i]->m_description = attachment->m_description; m_attachments[i]->m_realName = attachment->m_realName; m_attachments[i]->m_xMacType = attachment->m_xMacType; m_attachments[i]->m_xMacCreator = attachment->m_xMacCreator; m_attachments[i]->m_charset = mCompFields->GetCharacterSet(); m_attachments[i]->m_encoding = ENCODING_7BIT; if (m_attachments[i]->mURL) msg_pick_real_name(m_attachments[i], nullptr, mCompFields->GetCharacterSet()); if (m_attachments[i]->m_contentId.IsEmpty()) { // // Next, generate a content id for use with this part // nsCString email; mUserIdentity->GetEmail(email); m_attachments[i]->m_contentId = mime_gen_content_id(locCount+1, email.get()); } // // Start counting the attachments which are going to come from mail folders // and from NNTP servers. // if (m_attachments[i]->mURL) { nsIURI *uri = m_attachments[i]->mURL; bool match = false; if ((NS_SUCCEEDED(uri->SchemeIs("mailbox", &match)) && match) || (NS_SUCCEEDED(uri->SchemeIs("imap", &match)) && match)) (*aMailboxCount)++; else if ((NS_SUCCEEDED(uri->SchemeIs("news", &match)) && match) || (NS_SUCCEEDED(uri->SchemeIs("snews", &match)) && match)) (*aNewsCount)++; else { // Additional account types need a mechanism to report that they are // message protocols. If there is an nsIMsgProtocolInfo component // registered for this scheme, we'll consider it a mailbox // attachment. nsAutoCString contractID; contractID.Assign( NS_LITERAL_CSTRING("@mozilla.org/messenger/protocol/info;1")); nsAutoCString scheme; uri->GetScheme(scheme); contractID.Append(scheme); nsCOMPtr msgProtocolInfo = do_CreateInstance(contractID.get()); if (msgProtocolInfo) (*aMailboxCount)++; } } } else { m_attachments[i]->m_contentId = m_attachments[duplicateOf]->m_contentId; m_attachments[i]->SetMimeDeliveryState(nullptr); } // // Ok, while we are here, we should whack the DOM with the generated // Content-ID for this object. This will be necessary for generating // the HTML we need. // nsString domURL; if (!m_attachments[duplicateOf == -1 ? i : duplicateOf]->m_contentId.IsEmpty()) { nsString newSpec(NS_LITERAL_STRING("cid:")); newSpec.AppendASCII(m_attachments[duplicateOf == -1 ? i : duplicateOf]->m_contentId.get()); // Now, we know the types of objects this node can be, so we will do // our query interface here and see what we come up with nsCOMPtr body = (do_QueryInterface(domSaveArray[j].node)); nsCOMPtr image = (do_QueryInterface(domSaveArray[j].node)); nsCOMPtr link = (do_QueryInterface(domSaveArray[j].node)); nsCOMPtr anchor = (do_QueryInterface(domSaveArray[j].node)); if (anchor) { anchor->GetHref(domURL); anchor->SetHref(newSpec); } else if (link) { link->GetHref(domURL); link->SetHref(newSpec); } else if (image) { image->GetSrc(domURL); image->SetSrc(newSpec); } else if (body) { body->GetBackground(domURL); body->SetBackground(newSpec); } if (!domURL.IsEmpty()) domSaveArray[j].url = ToNewCString(NS_LossyConvertUTF16toASCII(domURL)); } i++; } rv = GetBodyFromEditor(); // // Ok, now we need to un-whack the DOM or we have a screwed up document on // Send failure. // for (i = 0; i < multipartCount; i++) { if ( (!domSaveArray[i].node) || (!domSaveArray[i].url) ) continue; // Now, we know the types of objects this node can be, so we will do // our query interface here and see what we come up with nsCOMPtr body = (do_QueryInterface(domSaveArray[i].node)); nsCOMPtr image = (do_QueryInterface(domSaveArray[i].node)); nsCOMPtr link = (do_QueryInterface(domSaveArray[i].node)); nsCOMPtr anchor = (do_QueryInterface(domSaveArray[i].node)); // STRING USE WARNING: hoisting the following conversion might save code-space, since it happens along every path if (anchor) anchor->SetHref(NS_ConvertASCIItoUTF16(domSaveArray[i].url)); else if (link) link->SetHref(NS_ConvertASCIItoUTF16(domSaveArray[i].url)); else if (image) image->SetSrc(NS_ConvertASCIItoUTF16(domSaveArray[i].url)); else if (body) body->SetBackground(NS_ConvertASCIItoUTF16(domSaveArray[i].url)); free(domSaveArray[i].url); } PR_FREEIF(domSaveArray); // // Now, we have to create that first child node for the multipart // message that holds the body as well as the attachment handler // for this body part. // // If we ONLY have multipart objects, then we don't need the container // for the multipart section... // m_related_part = new nsMsgSendPart(this); if (!m_related_part) return NS_ERROR_OUT_OF_MEMORY; m_related_part->SetMimeDeliveryState(this); m_related_part->SetType(MULTIPART_RELATED); // We are now going to use the m_related_part as a way to store the // MHTML message for this email. // m_related_body_part = new nsMsgSendPart(this); if (!m_related_body_part) return NS_ERROR_OUT_OF_MEMORY; // Set the body contents... m_related_body_part->SetBuffer(m_attachment1_body); m_related_body_part->SetType(m_attachment1_type); m_related_part->AddChild(m_related_body_part); return rv; } nsresult nsMsgComposeAndSend::CountCompFieldAttachments() { //Reset the counters mCompFieldLocalAttachments = 0; mCompFieldRemoteAttachments = 0; //Get the attachments array nsCOMPtr attachments; mCompFields->GetAttachments(getter_AddRefs(attachments)); if (!attachments) return NS_OK; nsresult rv; // Parse the attachments array bool moreAttachments; nsCString url; nsCOMPtr element; while (NS_SUCCEEDED(attachments->HasMoreElements(&moreAttachments)) && moreAttachments) { rv = attachments->GetNext(getter_AddRefs(element)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr attachment = do_QueryInterface(element, &rv); if (NS_SUCCEEDED(rv) && attachment) { attachment->GetUrl(url); if (!url.IsEmpty()) { // Check to see if this is a file URL, if so, don't retrieve // like a remote URL... if (nsMsgIsLocalFile(url.get())) mCompFieldLocalAttachments++; else // This is a remote URL... mCompFieldRemoteAttachments++; } } } return NS_OK; } // // Since we are at the head of the list, we start from ZERO. // nsresult nsMsgComposeAndSend::AddCompFieldLocalAttachments() { // If none, just return... if (mCompFieldLocalAttachments <= 0) return NS_OK; //Get the attachments array nsCOMPtr attachments; mCompFields->GetAttachments(getter_AddRefs(attachments)); if (!attachments) return NS_OK; uint32_t newLoc = 0; nsresult rv; nsCString url; //Parse the attachments array bool moreAttachments; nsCOMPtr element; while (NS_SUCCEEDED(attachments->HasMoreElements(&moreAttachments)) && moreAttachments) { rv = attachments->GetNext(getter_AddRefs(element)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr attachment = do_QueryInterface(element, &rv); if (NS_SUCCEEDED(rv) && attachment) { bool sendViaCloud = false; attachment->GetSendViaCloud(&sendViaCloud); m_attachments[newLoc]->mSendViaCloud = sendViaCloud; attachment->GetUrl(url); if (!url.IsEmpty()) { bool sendViaCloud; attachment->GetSendViaCloud(&sendViaCloud); if (sendViaCloud) { nsCString cloudProviderKey; // We'd like to output a part for the attachment, just an html part // with information about how to download the attachment. // m_attachments[newLoc]->m_done = true; attachment->GetHtmlAnnotation(m_attachments[newLoc]->mHtmlAnnotation); m_attachments[newLoc]->m_type.AssignLiteral("text/html"); attachment->GetCloudProviderKey(m_attachments[newLoc]->mCloudProviderKey); attachment->GetContentLocation(m_attachments[newLoc]->mCloudUrl); } // Just look for local file:// attachments and do the right thing. if (nsMsgIsLocalFile(url.get())) { // // Now we have to setup the m_attachments entry for the file:// // URL that is passed in... // m_attachments[newLoc]->mDeleteFile = false; nsMsgNewURL(getter_AddRefs(m_attachments[newLoc]->mURL), url.get()); if (m_attachments[newLoc]->mTmpFile) { if (m_attachments[newLoc]->mDeleteFile) m_attachments[newLoc]->mTmpFile->Remove(false); m_attachments[newLoc]->mTmpFile =nullptr; } nsresult rv; nsCOMPtr ioService = mozilla::services::GetIOService(); NS_ENSURE_TRUE(ioService, NS_ERROR_UNEXPECTED); nsCOMPtr uri; rv = ioService->NewURI(url, nullptr, nullptr, getter_AddRefs(uri)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr fileURL = do_QueryInterface(uri); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr fileURLFile; fileURL->GetFile(getter_AddRefs(fileURLFile)); m_attachments[newLoc]->mTmpFile = do_QueryInterface(fileURLFile); m_attachments[newLoc]->mDeleteFile = false; if (m_attachments[newLoc]->mURL) { nsAutoString proposedName; attachment->GetName(proposedName); msg_pick_real_name(m_attachments[newLoc], proposedName.get(), mCompFields->GetCharacterSet()); } // Now, most importantly, we need to figure out what the content type is for // this attachment...If we can't, then just make it application/octet-stream #ifdef MAC_OSX //Mac always need to snarf the file to figure out how to send it, maybe we need to use apple double... // unless caller has already set the content type, in which case, trust them. bool mustSnarfAttachment = true; #else bool mustSnarfAttachment = false; #endif if (sendViaCloud) mustSnarfAttachment = false; attachment->GetContentType(getter_Copies(m_attachments[newLoc]->m_type)); if (m_attachments[newLoc]->m_type.IsEmpty()) { nsresult rv = NS_OK; nsCOMPtr mimeFinder (do_GetService(NS_MIMESERVICE_CONTRACTID, &rv)); if (NS_SUCCEEDED(rv) && mimeFinder) { nsCOMPtr fileUrl(do_CreateInstance(NS_STANDARDURL_CONTRACTID)); if (fileUrl) { nsAutoCString fileExt; //First try using the real file name rv = fileUrl->SetFileName(m_attachments[newLoc]->m_realName); if (NS_SUCCEEDED(rv)) { rv = fileUrl->GetFileExtension(fileExt); if (NS_SUCCEEDED(rv) && !fileExt.IsEmpty()) { nsAutoCString type; mimeFinder->GetTypeFromExtension(fileExt, type); #ifndef XP_MACOSX if (!type.Equals("multipart/appledouble")) // can't do apple double on non-macs #endif m_attachments[newLoc]->m_type = type; } } //Then try using the url if we still haven't figured out the content type if (m_attachments[newLoc]->m_type.IsEmpty()) { rv = fileUrl->SetSpec(url); if (NS_SUCCEEDED(rv)) { rv = fileUrl->GetFileExtension(fileExt); if (NS_SUCCEEDED(rv) && !fileExt.IsEmpty()) { nsAutoCString type; mimeFinder->GetTypeFromExtension(fileExt, type); #ifndef XP_MACOSX if (!type.Equals("multipart/appledouble")) // can't do apple double on non-macs #endif m_attachments[newLoc]->m_type = type; // rtf and vcs files may look like text to sniffers, // but they're not human readable. if (type.IsEmpty() && !fileExt.IsEmpty() && (MsgLowerCaseEqualsLiteral(fileExt, "rtf") || MsgLowerCaseEqualsLiteral(fileExt, "vcs"))) m_attachments[newLoc]->m_type = APPLICATION_OCTET_STREAM; } } } } } } else { attachment->GetContentTypeParam(getter_Copies(m_attachments[newLoc]->m_typeParam)); mustSnarfAttachment = false; } //We need to snarf the file to figure out how to send it only if we don't have a content type... if (mustSnarfAttachment || m_attachments[newLoc]->m_type.IsEmpty()) { m_attachments[newLoc]->m_done = false; m_attachments[newLoc]->SetMimeDeliveryState(this); } else { m_attachments[newLoc]->m_done = true; m_attachments[newLoc]->SetMimeDeliveryState(nullptr); } // For local files, if they are HTML docs and we don't have a charset, we should // sniff the file and see if we can figure it out. if (!m_attachments[newLoc]->m_type.IsEmpty()) { if (m_attachments[newLoc]->m_type.LowerCaseEqualsLiteral(TEXT_HTML)) { char *tmpCharset = (char *)nsMsgI18NParseMetaCharset(m_attachments[newLoc]->mTmpFile); if (tmpCharset[0] != '\0') m_attachments[newLoc]->m_charset = tmpCharset; } } attachment->GetMacType(getter_Copies(m_attachments[newLoc]->m_xMacType)); attachment->GetMacCreator(getter_Copies(m_attachments[newLoc]->m_xMacCreator)); ++newLoc; } } } } return NS_OK; } nsresult nsMsgComposeAndSend::AddCompFieldRemoteAttachments(uint32_t aStartLocation, int32_t *aMailboxCount, int32_t *aNewsCount) { // If none, just return... if (mCompFieldRemoteAttachments <= 0) return NS_OK; //Get the attachments array nsCOMPtr attachments; mCompFields->GetAttachments(getter_AddRefs(attachments)); if (!attachments) return NS_OK; uint32_t newLoc = aStartLocation; nsresult rv; bool moreAttachments; nsCString url; nsCOMPtr element; while (NS_SUCCEEDED(attachments->HasMoreElements(&moreAttachments)) && moreAttachments) { rv = attachments->GetNext(getter_AddRefs(element)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr attachment = do_QueryInterface(element, &rv); if (NS_SUCCEEDED(rv) && attachment) { attachment->GetUrl(url); if (!url.IsEmpty()) { // Just look for files that are NOT local file attachments and do // the right thing. if (! nsMsgIsLocalFile(url.get())) { // Check for message attachment, see nsMsgMailNewsUrl::GetIsMessageUri. nsCOMPtr nsiuri = do_CreateInstance(NS_STANDARDURL_CONTRACTID); NS_ENSURE_STATE(nsiuri); nsiuri->SetSpec(url); nsAutoCString scheme; nsiuri->GetScheme(scheme); bool isAMessageAttachment = StringEndsWith(scheme, NS_LITERAL_CSTRING("-message")); m_attachments[newLoc]->mDeleteFile = true; m_attachments[newLoc]->m_done = false; m_attachments[newLoc]->SetMimeDeliveryState(this); if (!isAMessageAttachment) nsMsgNewURL(getter_AddRefs(m_attachments[newLoc]->mURL), url.get()); m_attachments[newLoc]->m_encoding = ENCODING_7BIT; attachment->GetMacType(getter_Copies(m_attachments[newLoc]->m_xMacType)); attachment->GetMacCreator(getter_Copies(m_attachments[newLoc]->m_xMacCreator)); /* Count up attachments which are going to come from mail folders and from NNTP servers. */ bool do_add_attachment = false; if (isAMessageAttachment) { do_add_attachment = true; if (!PL_strncasecmp(url.get(), "news-message://", 15)) (*aNewsCount)++; else (*aMailboxCount)++; m_attachments[newLoc]->m_uri = url; m_attachments[newLoc]->mURL = nullptr; } else do_add_attachment = (nullptr != m_attachments[newLoc]->mURL); m_attachments[newLoc]->mSendViaCloud = false; if (do_add_attachment) { nsAutoString proposedName; attachment->GetName(proposedName); msg_pick_real_name(m_attachments[newLoc], proposedName.get(), mCompFields->GetCharacterSet()); ++newLoc; } } } } } return NS_OK; } nsresult nsMsgComposeAndSend::HackAttachments(nsIArray *attachments, nsIArray *preloadedAttachments) { // // First, count the total number of attachments we are going to process // for this operation! This is a little more complicated than you might // think because we have a few ways to specify attachments. Via the nsMsgAttachmentData // as well as the composition fields. // CountCompFieldAttachments(); // Count the preloaded attachments! mPreloadedAttachmentCount = 0; // For now, manually add the local attachments in the comp field! mPreloadedAttachmentCount += mCompFieldLocalAttachments; uint32_t numAttachments = 0, numPreloadedAttachments = 0; if (attachments) attachments->GetLength(&numAttachments); if (preloadedAttachments) preloadedAttachments->GetLength(&numPreloadedAttachments); mPreloadedAttachmentCount += numPreloadedAttachments; // Count the attachments we have to go retrieve! Keep in mind, that these // will be APPENDED to the current list of URL's that we have gathered if // this is a multpart/related send operation mRemoteAttachmentCount = GetMultipartRelatedCount(); // For now, manually add the remote attachments in the comp field! mRemoteAttachmentCount += mCompFieldRemoteAttachments; mRemoteAttachmentCount += numAttachments; m_attachment_count = mPreloadedAttachmentCount + mRemoteAttachmentCount; uint32_t i; // counter for location in attachment array... // Now create the array of attachment handlers... for (i = 0; i < m_attachment_count; i++) { RefPtr handler = new nsMsgAttachmentHandler; m_attachments.AppendElement(handler); } // // First, we need to attach the files that are defined in the comp fields... if (NS_FAILED(AddCompFieldLocalAttachments())) return NS_ERROR_INVALID_ARG; // Now handle the preloaded attachments... if (numPreloadedAttachments > 0) { // These are attachments which have already been downloaded to tmp files. // We merely need to point the internal attachment data at those tmp // files. m_pre_snarfed_attachments_p = true; for (i = mCompFieldLocalAttachments; i < mPreloadedAttachmentCount; i++) { nsCOMPtr attachedFile = do_QueryElementAt(preloadedAttachments, i); if (!attachedFile) continue; /* These attachments are already "snarfed". */ m_attachments[i]->mDeleteFile = false; m_attachments[i]->SetMimeDeliveryState(nullptr); m_attachments[i]->m_done = true; attachedFile->GetOrigUrl(getter_AddRefs(m_attachments[i]->mURL)); attachedFile->GetType(m_attachments[i]->m_type); // Set it to the compose fields for a default... m_attachments[i]->m_charset = mCompFields->GetCharacterSet(); // If we still don't have a content type, we should really try sniff one out! if (m_attachments[i]->m_type.IsEmpty()) m_attachments[i]->PickEncoding(mCompFields->GetCharacterSet(), this); // For local files, if they are HTML docs and we don't have a charset, we should // sniff the file and see if we can figure it out. if (!m_attachments[i]->m_type.IsEmpty()) { nsCOMPtr tmpFile; attachedFile->GetTmpFile(getter_AddRefs(tmpFile)); if (m_attachments[i]->m_type.LowerCaseEqualsLiteral(TEXT_HTML) && tmpFile) { char *tmpCharset = (char *)nsMsgI18NParseMetaCharset(tmpFile); if (tmpCharset[0] != '\0') m_attachments[i]->m_charset = tmpCharset; } } attachedFile->GetDescription(m_attachments[i]->m_description); attachedFile->GetRealName(m_attachments[i]->m_realName); attachedFile->GetXMacType(m_attachments[i]->m_xMacType); attachedFile->GetXMacCreator(m_attachments[i]->m_xMacCreator); attachedFile->GetEncoding(m_attachments[i]->m_encoding); if (m_attachments[i]->mTmpFile) { if (m_attachments[i]->mDeleteFile) m_attachments[i]->mTmpFile->Remove(false); m_attachments[i]->mTmpFile = nullptr; } attachedFile->GetTmpFile(getter_AddRefs(m_attachments[i]->mTmpFile)); attachedFile->GetSize(&m_attachments[i]->m_size); attachedFile->GetUnprintableCount(&m_attachments[i]->m_unprintable_count); attachedFile->GetHighbitCount(&m_attachments[i]->m_highbit_count); attachedFile->GetCtlCount(&m_attachments[i]->m_ctl_count); attachedFile->GetNullCount(&m_attachments[i]->m_null_count); attachedFile->GetMaxLineLength(&m_attachments[i]->m_max_column); /* If the attachment has an encoding, and it's not one of the "null" encodings, then keep it. */ if (!m_attachments[i]->m_encoding.IsEmpty() && !m_attachments[i]->m_encoding.LowerCaseEqualsLiteral(ENCODING_7BIT) && !m_attachments[i]->m_encoding.LowerCaseEqualsLiteral(ENCODING_8BIT) && !m_attachments[i]->m_encoding.LowerCaseEqualsLiteral(ENCODING_BINARY)) m_attachments[i]->m_already_encoded_p = true; if (m_attachments[i]->mURL) msg_pick_real_name(m_attachments[i], nullptr, mCompFields->GetCharacterSet()); } } // First, handle the multipart related attachments if any... // int32_t mailbox_count = 0, news_count = 0; int32_t multipartRelatedCount = GetMultipartRelatedCount(); if (multipartRelatedCount > 0) { nsresult rv = ProcessMultipartRelated(&mailbox_count, &news_count); if (NS_FAILED(rv)) { // The destructor will take care of the m_attachment array return rv; } } // // Now add the comp field remote attachments... // if (NS_FAILED( AddCompFieldRemoteAttachments( (mPreloadedAttachmentCount + multipartRelatedCount), &mailbox_count, &news_count) )) return NS_ERROR_INVALID_ARG; // // Now deal remote attachments and attach multipart/related attachments (url's and such..) // first! // if (attachments) { int32_t locCount = -1; for (i = (mPreloadedAttachmentCount + GetMultipartRelatedCount() + mCompFieldRemoteAttachments); i < m_attachment_count; i++) { locCount++; nsCOMPtr attachment(do_QueryElementAt(attachments, i)); if (!attachment) continue; m_attachments[i]->mDeleteFile = true; m_attachments[i]->m_done = false; m_attachments[i]->SetMimeDeliveryState(this); attachment->GetUrl(getter_AddRefs(m_attachments[i]->mURL)); attachment->GetRealType(m_attachments[i]->m_overrideType); m_attachments[i]->m_charset = mCompFields->GetCharacterSet(); attachment->GetRealEncoding(m_attachments[i]->m_overrideEncoding); attachment->GetDesiredType(m_attachments[i]->m_desiredType); attachment->GetDescription(m_attachments[i]->m_description); attachment->GetRealName(m_attachments[i]->m_realName); attachment->GetXMacType(m_attachments[i]->m_xMacType); attachment->GetXMacCreator(m_attachments[i]->m_xMacCreator); m_attachments[i]->m_encoding = ENCODING_7BIT; // real name is set in the case of vcard so don't change it. XXX STILL NEEDED? // m_attachments[i]->m_real_name = 0; /* Count up attachments which are going to come from mail folders and from NNTP servers. */ if (m_attachments[i]->mURL) { nsIURI *uri = m_attachments[i]->mURL; bool match = false; if ((NS_SUCCEEDED(uri->SchemeIs("mailbox", &match)) && match) || (NS_SUCCEEDED(uri->SchemeIs("imap", &match)) && match)) mailbox_count++; else if ((NS_SUCCEEDED(uri->SchemeIs("news", &match)) && match) || (NS_SUCCEEDED(uri->SchemeIs("snews", &match)) && match)) news_count++; else { // Additional account types need a mechanism to report that they are // message protocols. If there is an nsIMsgProtocolInfo component // registered for this scheme, we'll consider it a mailbox // attachment. nsAutoCString contractID; contractID.Assign( NS_LITERAL_CSTRING("@mozilla.org/messenger/protocol/info;1")); nsAutoCString scheme; uri->GetScheme(scheme); contractID.Append(scheme); nsCOMPtr msgProtocolInfo = do_CreateInstance(contractID.get()); if (msgProtocolInfo) mailbox_count++; } if (uri) msg_pick_real_name(m_attachments[i], nullptr, mCompFields->GetCharacterSet()); } } } bool needToCallGatherMimeAttachments = true; if (m_attachment_count > 0) { // If there is more than one mailbox URL, or more than one NNTP url, // do the load in serial rather than parallel, for efficiency. if (mailbox_count > 1 || news_count > 1) m_be_synchronous_p = true; m_attachment_pending_count = m_attachment_count; // Start the URL attachments loading (eventually, an exit routine will // call the done_callback). for (i = 0; i < m_attachment_count; i++) { if (m_attachments[i]->m_done || m_attachments[i]->mSendViaCloud) { m_attachment_pending_count--; continue; } // // IF we get here and the URL is NULL, just dec the pending count and move on!!! // if ( (!m_attachments[i]->mURL) && (!m_attachments[i]->m_uri.Length()) ) { m_attachments[i]->m_bogus_attachment = true; m_attachments[i]->m_done = true; m_attachments[i]->SetMimeDeliveryState(nullptr); m_attachment_pending_count--; continue; } // // This only returns a failure code if NET_GetURL was not called // (and thus no exit routine was or will be called.) // // Display some feedback to user... nsString msg; nsAutoString attachmentFileName; NS_ConvertUTF8toUTF16 params(m_attachments[i]->m_realName); const char16_t *formatParams[1]; if (!params.IsEmpty()) { formatParams[0] = params.get(); } else if (m_attachments[i]->mURL) { nsCString asciiSpec; m_attachments[i]->mURL->GetAsciiSpec(asciiSpec); attachmentFileName.AssignASCII(asciiSpec.get()); formatParams[0] = attachmentFileName.get(); } mComposeBundle->FormatStringFromName(u"gatheringAttachment", formatParams, 1, getter_Copies(msg)); if (!msg.IsEmpty()) { SetStatusMessage(msg); } /* As SnarfAttachment will call GatherMimeAttachments when it will be done (this is an async process), we need to avoid to call it ourself. */ needToCallGatherMimeAttachments = false; nsresult status = m_attachments[i]->SnarfAttachment(mCompFields); if (NS_FAILED(status)) { nsString errorMsg; nsresult rv = ConvertToUnicode(nsMsgI18NFileSystemCharset(), m_attachments[i]->m_realName, attachmentFileName); if (attachmentFileName.IsEmpty() && m_attachments[i]->mURL) { nsCString asciiSpec; m_attachments[i]->mURL->GetAsciiSpec(asciiSpec); attachmentFileName.AssignASCII(asciiSpec.get()); rv = NS_OK; } if (NS_SUCCEEDED(rv)) { nsCOMPtr bundle; const char16_t *params[] = { attachmentFileName.get() }; mComposeBundle->FormatStringFromName(u"errorAttachingFile", params, 1, getter_Copies(errorMsg)); mSendReport->SetMessage(nsIMsgSendReport::process_Current, errorMsg.get(), false); mSendReport->SetError(nsIMsgSendReport::process_Current, NS_MSG_ERROR_ATTACHING_FILE, false); } return NS_MSG_ERROR_ATTACHING_FILE; } if (m_be_synchronous_p) break; } } // If no attachments - finish now (this will call the done_callback). if (needToCallGatherMimeAttachments) return GatherMimeAttachments(); return NS_OK; } nsresult nsMsgComposeAndSend::InitCompositionFields(nsMsgCompFields *fields, const nsACString &aOriginalMsgURI, MSG_ComposeType aType) { nsresult rv = NS_OK; const char *pStr = nullptr; mCompFields = new nsMsgCompFields(); if (!mCompFields) return NS_ERROR_OUT_OF_MEMORY; const char *cset = fields->GetCharacterSet(); // Make sure charset is sane... if (!cset || !*cset) { mCompFields->SetCharacterSet("UTF-8"); } else { mCompFields->SetCharacterSet(fields->GetCharacterSet()); } // Now, we will look for a URI defined as the default FCC pref. If this is set, // then SetFcc will use this value. The FCC field is a URI for the server that // will hold the "Sent" folder...the // // First, look at what was passed in via the "fields" structure...if that was // set then use it, otherwise, fall back to what is set in the prefs... // // But even before that, pay attention to the new OVERRIDE pref that will cancel // any and all copy operations! // bool doFcc = true; rv = mUserIdentity->GetDoFcc(&doFcc); if (!doFcc) { // If the identity pref "fcc" is set to false, then we will not do // any FCC operation! mCompFields->SetFcc(""); } else { bool useDefaultFCC = true; const char *fieldsFCC = fields->GetFcc(); if (fieldsFCC && *fieldsFCC) { if (PL_strcasecmp(fieldsFCC, "nocopy://") == 0) { useDefaultFCC = false; mCompFields->SetFcc(""); } else { nsCOMPtr folder; GetExistingFolder(nsDependentCString(fieldsFCC), getter_AddRefs(folder)); if (folder) { useDefaultFCC = false; mCompFields->SetFcc(mime_fix_header(fieldsFCC)); } } } // We use default FCC setting if it's not set or was set to an invalid folder. if (useDefaultFCC) { // Only check whether the user wants the message in the original message // folder if the msgcomptype is some kind of a reply. if (!aOriginalMsgURI.IsEmpty() && ( aType == nsIMsgCompType::Reply || aType == nsIMsgCompType::ReplyAll || aType == nsIMsgCompType::ReplyToGroup || aType == nsIMsgCompType::ReplyToList || aType == nsIMsgCompType::ReplyToSender || aType == nsIMsgCompType::ReplyToSenderAndGroup || aType == nsIMsgCompType::ReplyWithTemplate ) ) { nsCOMPtr accountManager = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv); if (NS_SUCCEEDED(rv)) { nsCOMPtr msgHdr; rv = GetMsgDBHdrFromURI(PromiseFlatCString(aOriginalMsgURI).get(), getter_AddRefs(msgHdr)); if (NS_SUCCEEDED(rv)) { nsCOMPtr folder; msgHdr->GetFolder(getter_AddRefs(folder)); if (NS_SUCCEEDED(rv)) { bool canFileMessages; rv = folder->GetCanFileMessages(&canFileMessages); if (NS_SUCCEEDED(rv) && canFileMessages) { nsCOMPtr incomingServer; rv = folder->GetServer(getter_AddRefs(incomingServer)); if (NS_SUCCEEDED(rv)) { nsCString incomingServerType; rv = incomingServer->GetCharValue("type", incomingServerType); // Exclude RSS accounts, as they falsely report // 'canFileMessages' = true if (NS_SUCCEEDED(rv) && !incomingServerType.Equals("rss")) { bool fccReplyFollowsParent; rv = mUserIdentity->GetFccReplyFollowsParent( &fccReplyFollowsParent); if (NS_SUCCEEDED(rv) && fccReplyFollowsParent) { nsCString folderURI; rv = folder->GetURI(folderURI); if (NS_SUCCEEDED(rv)) { mCompFields->SetFcc(folderURI.get()); useDefaultFCC = false; } } } } } } } } } if (useDefaultFCC) { nsCString uri; GetFolderURIFromUserPrefs(nsMsgDeliverNow, mUserIdentity, uri); mCompFields->SetFcc(MsgLowerCaseEqualsLiteral(uri, "nocopy://") ? "" : uri.get()); } } } // // Deal with an additional FCC operation for this email. // const char *fieldsFCC2 = fields->GetFcc2(); if ( (fieldsFCC2) && (*fieldsFCC2) ) { if (PL_strcasecmp(fieldsFCC2, "nocopy://") == 0) { mCompFields->SetFcc2(""); mNeedToPerformSecondFCC = false; } else { mCompFields->SetFcc2(fieldsFCC2); mNeedToPerformSecondFCC = true; } } // Copy the main bodies of headers over. rv = mCompFields->AddAllHeaders(fields); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr srcAttachments; fields->GetAttachments(getter_AddRefs(srcAttachments)); if (srcAttachments) { bool moreAttachments; nsCOMPtr element; while (NS_SUCCEEDED(srcAttachments->HasMoreElements(&moreAttachments)) && moreAttachments) { rv = srcAttachments->GetNext(getter_AddRefs(element)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr attachment = do_QueryInterface(element, &rv); NS_ENSURE_SUCCESS(rv, rv); mCompFields->AddAttachment(attachment); } } AddDefaultCustomHeaders(); AddMailFollowupToHeader(); AddMailReplyToHeader(); if (aType == nsIMsgCompType::ForwardInline || aType == nsIMsgCompType::ForwardAsAttachment) AddXForwardedMessageIdHeader(); pStr = fields->GetPriority(); if (pStr) mCompFields->SetPriority((char *) pStr); mCompFields->SetAttachVCard(fields->GetAttachVCard()); mCompFields->SetForcePlainText(fields->GetForcePlainText()); mCompFields->SetUseMultipartAlternative(fields->GetUseMultipartAlternative()); int32_t receiptType = nsIMsgMdnGenerator::eDntType; fields->GetReceiptHeaderType(&receiptType); mCompFields->SetReturnReceipt(fields->GetReturnReceipt()); mCompFields->SetAttachmentReminder(fields->GetAttachmentReminder()); mCompFields->SetDeliveryFormat(fields->GetDeliveryFormat()); mCompFields->SetContentLanguage(fields->GetContentLanguage()); mCompFields->SetReceiptHeaderType(receiptType); mCompFields->SetDSN(fields->GetDSN()); mCompFields->SetBodyIsAsciiOnly(fields->GetBodyIsAsciiOnly()); mCompFields->SetForceMsgEncoding(fields->GetForceMsgEncoding()); nsCOMPtr secInfo; fields->GetSecurityInfo(getter_AddRefs(secInfo)); mCompFields->SetSecurityInfo(secInfo); bool needToCheckCharset; fields->GetNeedToCheckCharset(&needToCheckCharset); mCompFields->SetNeedToCheckCharset(needToCheckCharset); if ( m_deliver_mode != nsMsgSaveAsDraft && m_deliver_mode != nsMsgSaveAsTemplate ) { // Check the fields for legitimacy... return mime_sanity_check_fields ( mCompFields->GetFrom(), mCompFields->GetReplyTo(), mCompFields->GetTo(), mCompFields->GetCc(), mCompFields->GetBcc(), mCompFields->GetFcc(), mCompFields->GetNewsgroups(), mCompFields->GetFollowupTo(), mCompFields->GetSubject(), mCompFields->GetReferences(), mCompFields->GetOrganization(), ""); } return NS_OK; } // Add default headers to outgoing messages see Bug #61520 // mail.identity..headers pref is a comma separated value of pref names // containging headers to add headers are stored in // mail.identity..header.
grab all the headers, mime encode // them and add them to the other custom headers. nsresult nsMsgComposeAndSend::AddDefaultCustomHeaders() { nsCString headersList; // get names of prefs containing headers to add nsresult rv = mUserIdentity->GetCharAttribute("headers", headersList); if (NS_SUCCEEDED(rv) && !headersList.IsEmpty()) { int32_t start = 0; int32_t end = 0; int32_t len = 0; while (end != -1) { end = headersList.FindChar(',', start); if (end == -1) { len = headersList.Length() - start; } else { len = end - start; } // grab the name of the current header pref nsAutoCString headerName("header."); headerName.Append(Substring(headersList, start, len)); start = end + 1; nsCString headerVal; rv = mUserIdentity->GetCharAttribute(headerName.get(), headerVal); if (NS_SUCCEEDED(rv)) { int32_t colonIdx = headerVal.FindChar(':'); if (colonIdx > 0) { // check that the header is *most likely* valid. nsCString name(Substring(headerVal, 0, colonIdx)); mCompFields->SetRawHeader(name.get(), Substring(headerVal, colonIdx + 1), nullptr); } } } } return rv; } // Add Mail-Followup-To header // See bug #204339 and http://cr.yp.to/proto/replyto.html for details nsresult nsMsgComposeAndSend::AddMailFollowupToHeader() { nsresult rv; // If there's already a Mail-Followup-To header, don't need to do anything. nsAutoCString mftHeader; mCompFields->GetRawHeader(HEADER_MAIL_FOLLOWUP_TO, mftHeader); if (!mftHeader.IsEmpty()) { return NS_OK; } // Get list of subscribed mailing lists nsAutoCString mailing_lists; rv = mUserIdentity->GetCharAttribute("subscribed_mailing_lists", mailing_lists); // Stop here if this list is missing or empty if (NS_FAILED(rv) || mailing_lists.IsEmpty()) return NS_OK; // Get a list of all recipients excluding bcc nsDependentCString to(mCompFields->GetTo()); nsDependentCString cc(mCompFields->GetCc()); nsAutoCString recipients; if (to.IsEmpty() && cc.IsEmpty()) // We have bcc recipients only, so we don't add the Mail-Followup-To header return NS_OK; if (!to.IsEmpty() && cc.IsEmpty()) recipients = to; else if (to.IsEmpty() && !cc.IsEmpty()) recipients = cc; else { recipients.Assign(to); recipients.AppendLiteral(", "); recipients.Append(cc); } // Remove duplicate addresses in recipients nsAutoCString recipients_no_dups; RemoveDuplicateAddresses(recipients, EmptyCString(), recipients_no_dups); // Remove subscribed mailing lists from recipients... nsAutoCString recipients_without_mailing_lists; RemoveDuplicateAddresses(recipients_no_dups, mailing_lists, recipients_without_mailing_lists); // ... If the result is equal to the input, we don't write to a subscribed // mailing list and therefore we don't add Mail-Followup-To if (recipients_no_dups == recipients_without_mailing_lists) return NS_OK; // Set Mail-Followup-To return mCompFields->SetRawHeader(HEADER_MAIL_FOLLOWUP_TO, recipients, mCompFields->GetCharacterSet()); } // Add Mail-Reply-To header // See bug #204339 and http://cr.yp.to/proto/replyto.html for details nsresult nsMsgComposeAndSend::AddMailReplyToHeader() { nsresult rv; // If there's already a Mail-Reply-To header, don't need to do anything. nsAutoCString mrtHeader; mCompFields->GetRawHeader(HEADER_MAIL_REPLY_TO, mrtHeader); if (!mrtHeader.IsEmpty()) return NS_OK; // Get list of reply-to mangling mailing lists nsAutoCString mailing_lists; rv = mUserIdentity->GetCharAttribute("replyto_mangling_mailing_lists", mailing_lists); // Stop here if this list is missing or empty if (NS_FAILED(rv) || mailing_lists.IsEmpty()) return NS_OK; // MRT will be set if the recipients of the message contains at least one // of the addresses in mailing_lists or if mailing_lists has '*' as first // character. The latter case gives the user an easy way to always set // the MRT header. Notice that this behaviour wouldn't make sense for MFT // in AddMailFollowupToHeader() above. if (mailing_lists[0] != '*') { // Get a list of all recipients excluding bcc nsDependentCString to(mCompFields->GetTo()); nsDependentCString cc(mCompFields->GetCc()); nsAutoCString recipients; if (to.IsEmpty() && cc.IsEmpty()) // We have bcc recipients only, so we don't add the Mail-Reply-To header return NS_OK; if (!to.IsEmpty() && cc.IsEmpty()) recipients = to; else if (to.IsEmpty() && !cc.IsEmpty()) recipients = cc; else { recipients.Assign(to); recipients.AppendLiteral(", "); recipients.Append(cc); } // Remove duplicate addresses in recipients nsAutoCString recipients_no_dups; RemoveDuplicateAddresses(recipients, EmptyCString(), recipients_no_dups); // Remove reply-to mangling mailing lists from recipients... nsAutoCString recipients_without_mailing_lists; RemoveDuplicateAddresses(recipients_no_dups, mailing_lists, recipients_without_mailing_lists); // ... If the result is equal to the input, none of the recipients // occure in the MRT addresses and therefore we stop here. if (recipients_no_dups == recipients_without_mailing_lists) return NS_OK; } // Set Mail-Reply-To nsAutoCString replyTo, mailReplyTo; replyTo = mCompFields->GetReplyTo(); if (replyTo.IsEmpty()) mailReplyTo = mCompFields->GetFrom(); else mailReplyTo = replyTo; mCompFields->SetRawHeader(HEADER_MAIL_REPLY_TO, mailReplyTo, mCompFields->GetCharacterSet()); return NS_OK; } nsresult nsMsgComposeAndSend::AddXForwardedMessageIdHeader() { return mCompFields->SetRawHeader("X-Forwarded-Message-Id", nsDependentCString(mCompFields->GetReferences()), nullptr); } nsresult nsMsgComposeAndSend::SnarfAndCopyBody(const nsACString &attachment1_body, const char *attachment1_type) { // // If we are here, then just process the body from what was // passed in the attachment1_body field. // // strip out whitespaces from the end of body ONLY. nsAutoCString body(attachment1_body); body.Trim(" ", false, true); if (body.Length() > 0) { m_attachment1_body = ToNewCString(body); if (!m_attachment1_body) { return NS_ERROR_OUT_OF_MEMORY; } m_attachment1_body_length = body.Length(); } PR_FREEIF(m_attachment1_type); m_attachment1_type = PL_strdup (attachment1_type); PR_FREEIF(m_attachment1_encoding); m_attachment1_encoding = PL_strdup (ENCODING_8BIT); return NS_OK; } nsresult nsMsgComposeAndSend::Init( nsIMsgIdentity *aUserIdentity, const char *aAccountKey, nsMsgCompFields *fields, nsIFile *sendFile, bool digest_p, bool dont_deliver_p, nsMsgDeliverMode mode, nsIMsgDBHdr *msgToReplace, const char *attachment1_type, const nsACString &attachment1_body, nsIArray *attachments, nsIArray *preloaded_attachments, const char *password, const nsACString &aOriginalMsgURI, MSG_ComposeType aType) { nsresult rv = NS_OK; //Let make sure we retreive the correct number of related parts. It may have changed since last time GetMultipartRelatedCount(true); nsString msg; if (!mComposeBundle) { nsCOMPtr bundleService = mozilla::services::GetStringBundleService(); NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED); nsCOMPtr bundle; rv = bundleService->CreateBundle("chrome://messenger/locale/messengercompose/composeMsgs.properties", getter_AddRefs(mComposeBundle)); NS_ENSURE_SUCCESS(rv, rv); } // Tell the user we are assembling the message... mComposeBundle->GetStringFromName(u"assemblingMailInformation", getter_Copies(msg)); SetStatusMessage(msg); if (mSendReport) mSendReport->SetCurrentProcess(nsIMsgSendReport::process_BuildMessage); // // The Init() method should initialize a send operation for full // blown create and send operations as well as just the "send a file" // operations. // m_dont_deliver_p = dont_deliver_p; m_deliver_mode = mode; mMsgToReplace = msgToReplace; mUserIdentity = aUserIdentity; mAccountKey = aAccountKey; NS_ASSERTION(mUserIdentity, "Got null identity!\n"); if (!mUserIdentity) return NS_ERROR_UNEXPECTED; // // First sanity check the composition fields parameter and // see if we should continue // if (!fields) return NS_ERROR_OUT_OF_MEMORY; m_digest_p = digest_p; // // Needed for mime encoding! // bool strictly_mime = true; nsCOMPtr pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID)); if (pPrefBranch) { rv = pPrefBranch->GetBoolPref(PREF_MAIL_STRICTLY_MIME, &strictly_mime); rv = pPrefBranch->GetIntPref(PREF_MAIL_MESSAGE_WARNING_SIZE, (int32_t *) &mMessageWarningSize); } nsCOMPtr secureCompose = do_CreateInstance(NS_MSGCOMPOSESECURE_CONTRACTID, &rv); // It's not an error scenario if there is no secure compose. // The S/MIME extension may be unavailable. if (NS_SUCCEEDED(rv) && secureCompose) { bool requiresEncryptionWork = false; rv = secureCompose->RequiresCryptoEncapsulation(aUserIdentity, fields, &requiresEncryptionWork); NS_ENSURE_SUCCESS(rv, rv); if (requiresEncryptionWork) { strictly_mime = true; // RFC2633 3.1.3 doesn't require multipart/signed entities to have // transfer encoding applied for ascii, but do it anyway to make sure // the content (e.g. line endings) isn't mangled along the way. fields->SetForceMsgEncoding(true); } } nsMsgMIMESetConformToStandard(strictly_mime); mime_use_quoted_printable_p = strictly_mime; rv = InitCompositionFields(fields, aOriginalMsgURI, aType); if (NS_FAILED(rv)) return rv; // // At this point, if we are only creating this object to do // send operations on externally created RFC822 disk files, // make sure we have setup the appropriate nsIFile and // move on with life. // // // First check to see if we are doing a send operation on an external file // or creating the file itself. // if (sendFile) { mTempFile = sendFile; return NS_OK; } // Ok, now watch me pull a rabbit out of my hat....what we need // to do here is figure out what the body will be. If this is a // MHTML request, then we need to do some processing of the document // and figure out what we need to package along with this message // to send. See ProcessMultipartRelated() for further details. // // // If we don't have an editor, then we won't be doing multipart related processing // for the body, so make a copy of the one passed in. // if (!mEditor) { SnarfAndCopyBody(attachment1_body, attachment1_type); mOriginalHTMLBody = ToNewCString(attachment1_body); } else if (GetMultipartRelatedCount() == 0) // Only do this if there are not embedded objects { rv = GetBodyFromEditor(); if (NS_FAILED(rv)) return rv; } mSmtpPassword = password; return HackAttachments(attachments, preloaded_attachments); } NS_IMETHODIMP nsMsgComposeAndSend::SendDeliveryCallback(nsIURI *aUrl, bool inIsNewsDelivery, nsresult aExitCode) { if (inIsNewsDelivery) { if (NS_FAILED(aExitCode)) if (aExitCode != NS_ERROR_ABORT && !NS_IS_MSG_ERROR(aExitCode)) aExitCode = NS_ERROR_POST_FAILED; DeliverAsNewsExit(aUrl, aExitCode); } else { if (NS_FAILED(aExitCode)) { #ifdef __GNUC__ // Temporary workaroung until bug 783526 is fixed. #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wswitch" #endif switch (aExitCode) { case NS_ERROR_UNKNOWN_HOST: case NS_ERROR_UNKNOWN_PROXY_HOST: aExitCode = NS_ERROR_SMTP_SEND_FAILED_UNKNOWN_SERVER; break; case NS_ERROR_CONNECTION_REFUSED: case NS_ERROR_PROXY_CONNECTION_REFUSED: aExitCode = NS_ERROR_SMTP_SEND_FAILED_REFUSED; break; case NS_ERROR_NET_INTERRUPT: aExitCode = NS_ERROR_SMTP_SEND_FAILED_INTERRUPTED; break; case NS_ERROR_NET_TIMEOUT: case NS_ERROR_NET_RESET: aExitCode = NS_ERROR_SMTP_SEND_FAILED_TIMEOUT; break; case NS_ERROR_SMTP_PASSWORD_UNDEFINED: // nothing to do, just keep the code break; default: if (aExitCode != NS_ERROR_ABORT && !NS_IS_MSG_ERROR(aExitCode)) aExitCode = NS_ERROR_SMTP_SEND_FAILED_UNKNOWN_REASON; break; } #ifdef __GNUC__ #pragma GCC diagnostic pop #endif } DeliverAsMailExit(aUrl, aExitCode); } return aExitCode; } nsresult nsMsgComposeAndSend::DeliverMessage() { if (mSendProgress) { bool canceled = false; mSendProgress->GetProcessCanceledByUser(&canceled); if (canceled) return NS_ERROR_ABORT; } bool mail_p = ((mCompFields->GetTo() && *mCompFields->GetTo()) || (mCompFields->GetCc() && *mCompFields->GetCc()) || (mCompFields->GetBcc() && *mCompFields->GetBcc())); bool news_p = mCompFields->GetNewsgroups() && *(mCompFields->GetNewsgroups()); NS_ASSERTION(!( m_deliver_mode != nsMsgSaveAsDraft && m_deliver_mode != nsMsgSaveAsTemplate) || (mail_p || news_p), "message without destination"); if (m_deliver_mode == nsMsgQueueForLater || m_deliver_mode == nsMsgDeliverBackground || m_deliver_mode == nsMsgSaveAsDraft || m_deliver_mode == nsMsgSaveAsTemplate) return SendToMagicFolder(m_deliver_mode); // // Ok, we are about to send the file that we have built up...but what // if this is a mongo email...we should have a way to warn the user that // they are about to do something they may not want to do. // int64_t fileSize; nsresult rv = mTempFile->GetFileSize(&fileSize); if (NS_FAILED(rv)) return NS_ERROR_FAILURE; if ((mMessageWarningSize > 0) && (fileSize > mMessageWarningSize) && (mGUINotificationEnabled)) { bool abortTheSend = false; nsString msg; nsAutoString formattedFileSize; FormatFileSize(fileSize, true, formattedFileSize); const char16_t* params[] = { formattedFileSize.get() }; mComposeBundle->FormatStringFromName(u"largeMessageSendWarning", params, 1, getter_Copies(msg)); if (!msg.IsEmpty()) { nsCOMPtr prompt; GetDefaultPrompt(getter_AddRefs(prompt)); nsMsgAskBooleanQuestionByString(prompt, msg.get(), &abortTheSend); if (!abortTheSend) { nsresult ignoreMe; Fail(NS_ERROR_BUT_DONT_SHOW_ALERT, msg.get(), &ignoreMe); return NS_ERROR_FAILURE; } } } if (news_p) { if (mail_p) mSendMailAlso = true; return DeliverFileAsNews(); /* will call DeliverFileAsMail if it needs to */ } else if (mail_p) return DeliverFileAsMail(); else return NS_ERROR_UNEXPECTED; return NS_OK; } nsresult nsMsgComposeAndSend::DeliverFileAsMail() { char *buf, *buf2; buf = (char *) PR_Malloc ((mCompFields->GetTo() ? PL_strlen (mCompFields->GetTo()) + 10 : 0) + (mCompFields->GetCc() ? PL_strlen (mCompFields->GetCc()) + 10 : 0) + (mCompFields->GetBcc() ? PL_strlen (mCompFields->GetBcc()) + 10 : 0) + 10); if (mSendReport) mSendReport->SetCurrentProcess(nsIMsgSendReport::process_SMTP); nsCOMPtr promptObject; GetDefaultPrompt(getter_AddRefs(promptObject)); if (!buf) { nsresult ignoreMe; Fail(NS_ERROR_OUT_OF_MEMORY, nullptr, &ignoreMe); NotifyListenerOnStopSending(nullptr, NS_ERROR_OUT_OF_MEMORY, nullptr, nullptr); return NS_ERROR_OUT_OF_MEMORY; } bool collectOutgoingAddresses = true; nsCOMPtr pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID)); if (pPrefBranch) pPrefBranch->GetBoolPref(PREF_MAIL_COLLECT_EMAIL_ADDRESS_OUTGOING, &collectOutgoingAddresses); nsCOMPtr addressCollector = do_GetService(NS_ABADDRESSCOLLECTOR_CONTRACTID); bool collectAddresses = (collectOutgoingAddresses && addressCollector); uint32_t sendFormat = nsIAbPreferMailFormat::unknown; // this code is not ready yet // see bug #44494 for more details // so for now, just pass in nsIAbPreferMailFormat::unknown // which will have no effect on the "prefers" attribute in the ab #if 0 bool forcePlainText = mCompFields->GetForcePlainText(); bool useMultipartAlternative = mCompFields->GetUseMultipartAlternative(); // see GenericSendMessage() in MsgComposeCommands.js for the reverse logic // if we choose to send both (html and plain) remember html. if (forcePlainText && !useMultipartAlternative) { // for now, don't remember the "plaintext" decision. // we could get in here because while sending html mail // the body was "convertible", but that doesn't mean // we intended to force plain text here. // so for now, use "unknown" which will have no effect on the // "prefers" attribute in the ab. // see bug #245520 for more details // sendFormat = nsIAbPreferMailFormat::plaintext; sendFormat = nsIAbPreferMailFormat::unknown; } else if (!forcePlainText) sendFormat = nsIAbPreferMailFormat::html; else NS_ERROR("unknown send format, should not happen"); #endif PL_strcpy (buf, ""); buf2 = buf + PL_strlen (buf); if (mCompFields->GetTo() && *mCompFields->GetTo()) { PL_strcat (buf2, mCompFields->GetTo()); if (addressCollector) addressCollector->CollectAddress(nsCString(mCompFields->GetTo()), collectAddresses /* create card if one doesn't exist */, sendFormat); } if (mCompFields->GetCc() && *mCompFields->GetCc()) { if (*buf2) PL_strcat (buf2, ","); PL_strcat (buf2, mCompFields->GetCc()); if (addressCollector) addressCollector->CollectAddress(nsCString(mCompFields->GetCc()), collectAddresses /* create card if one doesn't exist */, sendFormat); } if (mCompFields->GetBcc() && *mCompFields->GetBcc()) { if (*buf2) PL_strcat (buf2, ","); PL_strcat (buf2, mCompFields->GetBcc()); if (addressCollector) addressCollector->CollectAddress(nsCString(mCompFields->GetBcc()), collectAddresses /* create card if one doesn't exist */, sendFormat); } // We need undo groups to keep only the addresses nsresult rv = StripOutGroupNames(buf); NS_ENSURE_SUCCESS(rv, rv); // Ok, now MIME II encode this to prevent 8bit problems... char *convbuf = nsMsgI18NEncodeMimePartIIStr(buf, true, mCompFields->GetCharacterSet(), 0, nsMsgMIMEGetConformToStandard()); if (convbuf) { // MIME-PartII conversion PR_FREEIF(buf); buf = convbuf; } nsCString escaped_buf; MsgEscapeString(nsDependentCString(buf), nsINetUtil::ESCAPE_URL_PATH, escaped_buf); if (!escaped_buf.IsEmpty()) { NS_Free(buf); buf = ToNewCString(escaped_buf); } nsCOMPtr smtpService(do_GetService(NS_SMTPSERVICE_CONTRACTID, &rv)); if (NS_SUCCEEDED(rv) && smtpService) { MsgDeliveryListener *deliveryListener = new MsgDeliveryListener(this, false); if (!deliveryListener) return NS_ERROR_OUT_OF_MEMORY; // we used to get the prompt from the compose window and we'd pass that in // to the smtp protocol as the prompt to use. But when you send a message, // we dismiss the compose window.....so you are parenting off of a window that // isn't there. To have it work correctly I think we want the alert dialogs to be modal // to the top most mail window...after all, that's where we are going to be sending status // update information too.... nsCOMPtr callbacks; GetNotificationCallbacks(getter_AddRefs(callbacks)); // Tell the user we are sending the message! nsString msg; mComposeBundle->GetStringFromName(u"sendingMessage", getter_Copies(msg)); SetStatusMessage(msg); nsCOMPtr msgStatus (do_QueryInterface(mSendProgress)); // if the sendProgress isn't set, let's use the member variable. if (!msgStatus) msgStatus = do_QueryInterface(mStatusFeedback); nsCOMPtr runningUrl; rv = smtpService->SendMailMessage(mTempFile, buf, mUserIdentity, mSmtpPassword.get(), deliveryListener, msgStatus, callbacks, mCompFields->GetDSN(), getter_AddRefs(runningUrl), getter_AddRefs(mRunningRequest)); // set envid on the returned URL if (NS_SUCCEEDED(rv)) { nsCOMPtr smtpUrl(do_QueryInterface(runningUrl, &rv)); if (NS_SUCCEEDED(rv)) smtpUrl->SetDsnEnvid(nsDependentCString(mCompFields->GetMessageId())); } } PR_FREEIF(buf); // free the buf because we are done with it.... return rv; } nsresult nsMsgComposeAndSend::DeliverFileAsNews() { nsresult rv = NS_OK; if (!(mCompFields->GetNewsgroups())) return rv; if (mSendReport) mSendReport->SetCurrentProcess(nsIMsgSendReport::process_NNTP); nsCOMPtr promptObject; GetDefaultPrompt(getter_AddRefs(promptObject)); nsCOMPtr nntpService(do_GetService(NS_NNTPSERVICE_CONTRACTID, &rv)); if (NS_SUCCEEDED(rv) && nntpService) { MsgDeliveryListener *deliveryListener = new MsgDeliveryListener(this, true); if (!deliveryListener) return NS_ERROR_OUT_OF_MEMORY; // Tell the user we are posting the message! nsString msg; mComposeBundle->GetStringFromName(u"postingMessage", getter_Copies(msg)); SetStatusMessage(msg); nsCOMPtr mailSession = do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); // JFD TODO: we should use GetDefaultPrompt instead nsCOMPtr msgWindow; rv = mailSession->GetTopmostMsgWindow(getter_AddRefs(msgWindow)); // see bug #163139 // we might not have a msg window if only the compose window is open. if(NS_FAILED(rv)) msgWindow = nullptr; rv = nntpService->PostMessage(mTempFile, mCompFields->GetNewsgroups(), mAccountKey.get(), deliveryListener, msgWindow, nullptr); if (NS_FAILED(rv)) return rv; } return rv; } NS_IMETHODIMP nsMsgComposeAndSend::Fail(nsresult aFailureCode, const char16_t *aErrorMsg, nsresult *aResult) { NS_ENSURE_ARG_POINTER(aResult); *aResult = aFailureCode; if (NS_FAILED(aFailureCode)) { nsCOMPtr prompt; GetDefaultPrompt(getter_AddRefs(prompt)); if (mSendReport) { int32_t process; if (NS_SUCCEEDED(mSendReport->GetCurrentProcess(&process)) && process == nsIMsgSendReport::process_Current) { // currentProcess isn't set yet, so we need another value. mSendReport->SetCurrentProcess(nsIMsgSendReport::process_BuildMessage); } mSendReport->SetError(nsIMsgSendReport::process_Current, aFailureCode, false); mSendReport->SetMessage(nsIMsgSendReport::process_Current, aErrorMsg, false); mSendReport->DisplayReport(prompt, true, true, aResult); } else { if (aFailureCode != NS_ERROR_BUT_DONT_SHOW_ALERT) nsMsgDisplayMessageByName(prompt, u"sendFailed"); } } if (NS_SUCCEEDED(m_status)) m_status = NS_ERROR_BUT_DONT_SHOW_ALERT; //Stop any pending process... Abort(); return NS_OK; } nsresult nsMsgComposeAndSend::FormatStringWithSMTPHostNameByName(const char16_t* aMsgName, char16_t **aString) { NS_ENSURE_ARG(aString); nsresult rv; nsCOMPtr smtpService(do_GetService(NS_SMTPSERVICE_CONTRACTID, &rv)); NS_ENSURE_SUCCESS(rv,rv); // Get the smtp hostname and format the string. nsCString smtpHostName; nsCOMPtr smtpServer; rv = smtpService->GetServerByIdentity(mUserIdentity, getter_AddRefs(smtpServer)); if (NS_SUCCEEDED(rv)) smtpServer->GetHostname(smtpHostName); nsAutoString hostStr; CopyASCIItoUTF16(smtpHostName, hostStr); const char16_t *params[] = { hostStr.get() }; if (NS_SUCCEEDED(rv)) mComposeBundle->FormatStringFromName(aMsgName, params, 1, aString); return rv; } void nsMsgComposeAndSend::DoDeliveryExitProcessing(nsIURI * aUri, nsresult aExitCode, bool aCheckForMail) { // If we fail on the news delivery, no sense in going on so just notify // the user and exit. if (NS_FAILED(aExitCode)) { const char16_t* exitString = errorStringNameForErrorCode(aExitCode); nsString eMsg; if (aExitCode == NS_ERROR_SMTP_SEND_FAILED_UNKNOWN_SERVER || aExitCode == NS_ERROR_SMTP_SEND_FAILED_UNKNOWN_REASON || aExitCode == NS_ERROR_SMTP_SEND_FAILED_REFUSED || aExitCode == NS_ERROR_SMTP_SEND_FAILED_INTERRUPTED || aExitCode == NS_ERROR_SMTP_SEND_FAILED_TIMEOUT || aExitCode == NS_ERROR_SMTP_PASSWORD_UNDEFINED || aExitCode == NS_ERROR_SMTP_AUTH_FAILURE || aExitCode == NS_ERROR_SMTP_AUTH_GSSAPI || aExitCode == NS_ERROR_SMTP_AUTH_MECH_NOT_SUPPORTED || aExitCode == NS_ERROR_SMTP_AUTH_NOT_SUPPORTED || aExitCode == NS_ERROR_SMTP_AUTH_CHANGE_ENCRYPT_TO_PLAIN_NO_SSL || aExitCode == NS_ERROR_SMTP_AUTH_CHANGE_ENCRYPT_TO_PLAIN_SSL || aExitCode == NS_ERROR_SMTP_AUTH_CHANGE_PLAIN_TO_ENCRYPT || aExitCode == NS_ERROR_STARTTLS_FAILED_EHLO_STARTTLS) { FormatStringWithSMTPHostNameByName(exitString, getter_Copies(eMsg)); } else { mComposeBundle->GetStringFromName(exitString, getter_Copies(eMsg)); } Fail(aExitCode, eMsg.get(), &aExitCode); NotifyListenerOnStopSending(nullptr, aExitCode, nullptr, nullptr); return; } if (aCheckForMail) { if ((mCompFields->GetTo() && *mCompFields->GetTo()) || (mCompFields->GetCc() && *mCompFields->GetCc()) || (mCompFields->GetBcc() && *mCompFields->GetBcc())) { // If we're sending this news message to mail as well, start it now. // Completion and further errors will be handled there. DeliverFileAsMail(); return; } } // // Tell the listeners that we are done with the sending operation... // NotifyListenerOnStopSending(mCompFields->GetMessageId(), aExitCode, nullptr, nullptr); // If we hit here, we are done with delivery! // // Just call the DoFCC() method and if that fails, then we should just // cleanup and get out. If DoFCC "succeeds", then all that means is the // async copy operation has been started and we will be notified later // when it is done. DON'T cleanup until the copy is complete and don't // notify the listeners with OnStop() until we are done. // // For now, we don't need to do anything here, but the code will stay this // way until later... // DoFcc(); } NS_IMETHODIMP nsMsgComposeAndSend::DeliverAsMailExit(nsIURI *aUrl, nsresult aExitCode) { DoDeliveryExitProcessing(aUrl, aExitCode, false); return NS_OK; } NS_IMETHODIMP nsMsgComposeAndSend::DeliverAsNewsExit(nsIURI *aUrl, nsresult aExitCode) { DoDeliveryExitProcessing(aUrl, aExitCode, mSendMailAlso); return NS_OK; } bool nsMsgComposeAndSend::CanSaveMessagesToFolder(const char *folderURL) { nsresult rv; nsCOMPtr rdf(do_GetService("@mozilla.org/rdf/rdf-service;1", &rv)); if (NS_FAILED(rv)) return false; nsCOMPtr resource; rv = rdf->GetResource(nsDependentCString(folderURL), getter_AddRefs(resource)); if (NS_FAILED(rv)) return false; nsCOMPtr thisFolder; thisFolder = do_QueryInterface(resource, &rv); if (NS_FAILED(rv) || !thisFolder) return false; nsCOMPtr server; rv = thisFolder->GetServer(getter_AddRefs(server)); if (NS_FAILED(rv) || !server) return false; // See if we are allowed to save/file msgs to this folder. bool canSave; rv = server->GetCanFileMessagesOnServer(&canSave); return canSave; } // // Now, start the appropriate copy operation. // nsresult nsMsgComposeAndSend::DoFcc() { // // Just cleanup and return success if we're not allowed to save msgs to FCC folder. // const char* fcc = mCompFields->GetFcc(); if (!fcc || !*fcc || !CanSaveMessagesToFolder(fcc)) { // It is the caller's responsibility to say we've stopped sending, so just // let the listeners know we're not doing a copy. NotifyListenerOnStopCopy(NS_OK); // For closure of compose window... return NS_OK; } if (mSendReport) mSendReport->SetCurrentProcess(nsIMsgSendReport::process_Copy); // // If we are here, then we need to save off the FCC file to save and // start the copy operation. MimeDoFCC() will take care of all of this // for us. // nsresult rv = MimeDoFCC(mTempFile, nsMsgDeliverNow, mCompFields->GetBcc(), mCompFields->GetFcc(), mCompFields->GetNewspostUrl()); if (NS_FAILED(rv)) { // // If we hit here, the copy operation FAILED and we should at least tell the // user that it did fail but the send operation has already succeeded. // NotifyListenerOnStopCopy(rv); } return rv; } NS_IMETHODIMP nsMsgComposeAndSend::NotifyListenerOnStartSending(const char *aMsgID, uint32_t aMsgSize) { if (mListener) mListener->OnStartSending(aMsgID, aMsgSize); return NS_OK; } NS_IMETHODIMP nsMsgComposeAndSend::NotifyListenerOnProgress(const char *aMsgID, uint32_t aProgress, uint32_t aProgressMax) { if (mListener) mListener->OnProgress(aMsgID, aProgress, aProgressMax); return NS_OK; } NS_IMETHODIMP nsMsgComposeAndSend::NotifyListenerOnStatus(const char *aMsgID, const char16_t *aMsg) { if (mListener) mListener->OnStatus(aMsgID, aMsg); return NS_OK; } NS_IMETHODIMP nsMsgComposeAndSend::NotifyListenerOnStopSending(const char *aMsgID, nsresult aStatus, const char16_t *aMsg, nsIFile *returnFile) { if (mListener != nullptr) mListener->OnStopSending(aMsgID, aStatus, aMsg, returnFile); return NS_OK; } NS_IMETHODIMP nsMsgComposeAndSend::NotifyListenerOnStartCopy() { nsCOMPtr copyListener; if (mListener) { copyListener = do_QueryInterface(mListener); if (copyListener) copyListener->OnStartCopy(); } return NS_OK; } NS_IMETHODIMP nsMsgComposeAndSend::NotifyListenerOnProgressCopy(uint32_t aProgress, uint32_t aProgressMax) { nsCOMPtr copyListener; if (mListener) { copyListener = do_QueryInterface(mListener); if (copyListener) copyListener->OnProgress(aProgress, aProgressMax); } return NS_OK; } NS_IMETHODIMP nsMsgComposeAndSend::SetMessageKey(nsMsgKey aMessageKey) { m_messageKey = aMessageKey; return NS_OK; } NS_IMETHODIMP nsMsgComposeAndSend::GetMessageKey(nsMsgKey *aMessageKey) { *aMessageKey = m_messageKey; return NS_OK; } NS_IMETHODIMP nsMsgComposeAndSend::GetFolderUri(nsACString &aFolderUri) { aFolderUri = m_folderName; return NS_OK; } NS_IMETHODIMP nsMsgComposeAndSend::GetPartForDomIndex(int32_t aDomIndex, nsACString &aPartNum) { aPartNum = m_partNumbers.SafeElementAt(aDomIndex, EmptyCString()); return NS_OK; } NS_IMETHODIMP nsMsgComposeAndSend::GetMessageId(nsACString& aMessageId) { nsresult rv = NS_OK; if (mCompFields) aMessageId = mCompFields->GetMessageId(); else rv = NS_ERROR_NULL_POINTER; return rv; } NS_IMETHODIMP nsMsgComposeAndSend::NotifyListenerOnStopCopy(nsresult aStatus) { // This is one per copy so make sure we clean this up first. mCopyObj = nullptr; // Set a status message... nsString msg; if (NS_SUCCEEDED(aStatus)) mComposeBundle->GetStringFromName(u"copyMessageComplete", getter_Copies(msg)); else mComposeBundle->GetStringFromName(u"copyMessageFailed", getter_Copies(msg)); SetStatusMessage(msg); nsCOMPtr prompt; GetDefaultPrompt(getter_AddRefs(prompt)); if (NS_FAILED(aStatus)) { nsresult rv; nsCOMPtr bundleService = mozilla::services::GetStringBundleService(); NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED); nsCOMPtr bundle; rv = bundleService->CreateBundle("chrome://messenger/locale/messengercompose/composeMsgs.properties", getter_AddRefs(bundle)); NS_ENSURE_SUCCESS(rv, rv); nsString msg; const char16_t *formatStrings[] = { mSavedToFolderName.get() }; rv = bundle->FormatStringFromName(u"errorSavingMsg", formatStrings, 1, getter_Copies(msg)); if (NS_SUCCEEDED(rv)) { bool retry = false; nsMsgAskBooleanQuestionByString(prompt, msg.get(), &retry, nullptr); if (retry) { mSendProgress = nullptr; // this was cancelled, so we need to clear it. return SendToMagicFolder(m_deliver_mode); } } // We failed, and the user decided not to retry. So we're just going to // fail out. However, give Fail a success code so that it doesn't prompt // the user a second time as they already know about the failure. Fail(NS_OK, nullptr, &aStatus); } if (NS_SUCCEEDED(aStatus) && !mPerformingSecondFCC && m_messageKey != nsMsgKey_None && (m_deliver_mode == nsMsgDeliverNow || m_deliver_mode == nsMsgSendUnsent)) { nsresult rv = FilterSentMessage(); if (NS_FAILED(rv)) OnStopOperation(rv); return rv; } return MaybePerformSecondFCC(aStatus); } nsresult nsMsgComposeAndSend::FilterSentMessage() { if (mSendReport) mSendReport->SetCurrentProcess(nsIMsgSendReport::process_Filter); nsCOMPtr folder; nsresult rv = GetExistingFolder(m_folderName, getter_AddRefs(folder)); if (NS_FAILED(rv)) return rv; nsCOMPtr msgHdr; rv = folder->GetMessageHeader(m_messageKey, getter_AddRefs(msgHdr)); if (NS_FAILED(rv)) return rv; nsCOMPtr msgArray(do_CreateInstance(NS_ARRAY_CONTRACTID, &rv)); if (NS_FAILED(rv)) return rv; nsCOMPtr filterSvc = do_GetService(NS_MSGFILTERSERVICE_CONTRACTID, &rv); if (NS_FAILED(rv)) return rv; rv = msgArray->AppendElement(msgHdr, false); if (NS_FAILED(rv)) return rv; nsCOMPtr msgWindow; if (mSendProgress) mSendProgress->GetMsgWindow(getter_AddRefs(msgWindow)); return filterSvc->ApplyFilters(nsMsgFilterType::PostOutgoing, msgArray, folder, msgWindow, this); } NS_IMETHODIMP nsMsgComposeAndSend::OnStopOperation(nsresult aStatus) { // Set a status message... nsString msg; if (NS_SUCCEEDED(aStatus)) mComposeBundle->GetStringFromName(u"filterMessageComplete", getter_Copies(msg)); else mComposeBundle->GetStringFromName(u"filterMessageFailed", getter_Copies(msg)); SetStatusMessage(msg); if (NS_FAILED(aStatus)) { nsresult rv = mComposeBundle->GetStringFromName(u"errorFilteringMsg", getter_Copies(msg)); if (NS_SUCCEEDED(rv)) { nsCOMPtr prompt; GetDefaultPrompt(getter_AddRefs(prompt)); nsMsgDisplayMessageByString(prompt, msg.get(), nullptr); } // We failed, however, give Fail a success code so that it doesn't prompt // the user a second time as they already know about the failure. Fail(NS_OK, nullptr, &aStatus); } return MaybePerformSecondFCC(aStatus); } nsresult nsMsgComposeAndSend::MaybePerformSecondFCC(nsresult aStatus) { // Ok, now to support a second copy operation, we need to figure // out which copy request just finished. If the user has requested // a second copy operation, then we need to fire that off, but if they // just wanted a single copy operation, we can tell everyone we are done // and move on with life. Only do the second copy if the first one worked. // if ( NS_SUCCEEDED(aStatus) && (mNeedToPerformSecondFCC) ) { if (mSendReport) mSendReport->SetCurrentProcess(nsIMsgSendReport::process_FCC); mNeedToPerformSecondFCC = false; mPerformingSecondFCC = true; const char *fcc2 = mCompFields->GetFcc2(); if (fcc2 && *fcc2) { nsresult rv = MimeDoFCC(mTempFile, nsMsgDeliverNow, mCompFields->GetBcc(), fcc2, mCompFields->GetNewspostUrl()); if (NS_FAILED(rv)) Fail(rv, nullptr, &aStatus); else return NS_OK; } } // If we are here, its real cleanup time! if (mListener) { nsCOMPtr copyListener = do_QueryInterface(mListener); if (copyListener) copyListener->OnStopCopy(aStatus); } return aStatus; } /* This is the main driving function of this module. It generates a document of type message/rfc822, which contains the stuff provided. The first few arguments are the standard header fields that the generated document should have. `other_random_headers' is a string of additional headers that should be inserted beyond the standard ones. If provided, it is just tacked on to the end of the header block, so it should have newlines at the end of each line, shouldn't have blank lines, multi-line headers should be properly continued, etc. `digest_p' says that most of the documents we are attaching are themselves messages, and so we should generate a multipart/digest container instead of multipart/mixed. (It's a minor difference.) The full text of the first attachment is provided via `attachment1_type' and `attachment1_body'. These may all be 0 if all attachments are provided externally. Subsequent attachments are provided as URLs to load, described in the nsMsgAttachmentData structures. If `dont_deliver_p' is false, then we actually deliver the message to the SMTP and/or NNTP server, and the message_delivery_done_callback will be invoked with the status. If `dont_deliver_p' is true, then we just generate the message, we don't actually deliver it, and the message_delivery_done_callback will be called with the name of the generated file. The callback is responsible for both freeing the file name string, and deleting the file when it is done with it. If an error occurred, then `status' will be negative and `error_message' may be an error message to display. If status is non- negative, then `error_message' contains the file name (this is kind of a kludge...) */ NS_IMETHODIMP nsMsgComposeAndSend::CreateAndSendMessage( nsIEditor *aEditor, nsIMsgIdentity *aUserIdentity, const char *aAccountKey, nsIMsgCompFields *fields, bool digest_p, bool dont_deliver_p, nsMsgDeliverMode mode, nsIMsgDBHdr *msgToReplace, const char *attachment1_type, const nsACString &attachment1_body, nsIArray *attachments, nsIArray *preloaded_attachments, mozIDOMWindowProxy *parentWindow, nsIMsgProgress *progress, nsIMsgSendListener *aListener, const char *password, const nsACString &aOriginalMsgURI, MSG_ComposeType aType ) { nsresult rv; /* First thing to do is to reset the send errors report */ mSendReport->Reset(); mSendReport->SetDeliveryMode(mode); mParentWindow = do_QueryInterface(parentWindow); mSendProgress = progress; mListener = aListener; // Set the editor for MHTML operations if necessary if (aEditor) mEditor = aEditor; rv = Init(aUserIdentity, aAccountKey, (nsMsgCompFields *)fields, nullptr, digest_p, dont_deliver_p, mode, msgToReplace, attachment1_type, attachment1_body, attachments, preloaded_attachments, password, aOriginalMsgURI, aType); if (NS_FAILED(rv) && mSendReport) mSendReport->SetError(nsIMsgSendReport::process_Current, rv, false); return rv; } NS_IMETHODIMP nsMsgComposeAndSend::CreateRFC822Message( nsIMsgIdentity *aUserIdentity, nsIMsgCompFields *aFields, const char *aMsgType, const nsACString &aMsgBody, bool aIsDraft, nsIArray *aAttachments, nsIArray *aEmbeddedObjects, nsIMsgSendListener *aListener ) { nsresult rv; nsMsgDeliverMode mode = aIsDraft ? nsIMsgSend::nsMsgSaveAsDraft : nsIMsgSend::nsMsgDeliverNow; /* First thing to do is to reset the send errors report */ mSendReport->Reset(); mSendReport->SetDeliveryMode(mode); mParentWindow = nullptr; mSendProgress = nullptr; mListener = aListener; mEmbeddedObjectList = aEmbeddedObjects; rv = Init(aUserIdentity, nullptr, (nsMsgCompFields *)aFields, nullptr, false, true, mode, nullptr, aMsgType, aMsgBody, nullptr, aAttachments, nullptr, EmptyCString(), nsIMsgCompType::New); if (NS_FAILED(rv) && mSendReport) mSendReport->SetError(nsIMsgSendReport::process_Current, rv, false); return rv; } nsresult nsMsgComposeAndSend::SendMessageFile( nsIMsgIdentity *aUserIndentity, const char *aAccountKey, nsIMsgCompFields *fields, nsIFile *sendIFile, bool deleteSendFileOnCompletion, bool digest_p, nsMsgDeliverMode mode, nsIMsgDBHdr *msgToReplace, nsIMsgSendListener *aListener, nsIMsgStatusFeedback *aStatusFeedback, const char *password ) { NS_ENSURE_ARG_POINTER(fields); NS_ENSURE_ARG_POINTER(sendIFile); nsresult rv; /* First thing to do is to reset the send errors report */ mSendReport->Reset(); mSendReport->SetDeliveryMode(mode); mStatusFeedback = aStatusFeedback; // // First check to see if the external file we are sending is a valid file. // bool exists; if (NS_FAILED(sendIFile->Exists(&exists))) return NS_ERROR_INVALID_ARG; if (!exists) return NS_ERROR_INVALID_ARG; // Setup the listener... mListener = aListener; // Should we delete the temp file when done? if (!deleteSendFileOnCompletion) mReturnFile = sendIFile; rv = Init(aUserIndentity, aAccountKey, (nsMsgCompFields *)fields, sendIFile, digest_p, false, mode, msgToReplace, nullptr, EmptyCString(), nullptr, nullptr, password, EmptyCString(), nsIMsgCompType::New); if (NS_SUCCEEDED(rv)) rv = DeliverMessage(); if (NS_FAILED(rv) && mSendReport) mSendReport->SetError(nsIMsgSendReport::process_Current, rv, false); return rv; } // // Send the message to the magic folder, and runs the completion/failure // callback. // nsresult nsMsgComposeAndSend::SendToMagicFolder(nsMsgDeliverMode mode) { nsresult rv = MimeDoFCC(mTempFile, mode, mCompFields->GetBcc(), mCompFields->GetFcc(), mCompFields->GetNewspostUrl()); // // The caller of MimeDoFCC needs to deal with failure. // if (NS_FAILED(rv)) rv = NotifyListenerOnStopCopy(rv); return rv; } char* nsMsgGetEnvelopeLine(void) { static char result[75] = ""; PRExplodedTime now; char buffer[128] = ""; // Generate envelope line in format of: From - Sat Apr 18 20:01:49 1998 // // Use PR_FormatTimeUSEnglish() to format the date in US English format, // then figure out what our local GMT offset is, and append it (since // PR_FormatTimeUSEnglish() can't do that.) Generate four digit years as // per RFC 1123 (superceding RFC 822.) // PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &now); PR_FormatTimeUSEnglish(buffer, sizeof(buffer), "%a %b %d %H:%M:%S %Y", &now); // This value must be in ctime() format, with English abbreviations. // PL_strftime("... %c ...") is no good, because it is localized. // PL_strcpy(result, "From - "); PL_strcpy(result + 7, buffer); PL_strcpy(result + 7 + 24, CRLF); return result; } #define ibuffer_size FILE_IO_BUFFER_SIZE nsresult nsMsgComposeAndSend::MimeDoFCC(nsIFile *input_file, nsMsgDeliverMode mode, const char *bcc_header, const char *fcc_header, const char *news_url) { nsresult status = NS_OK; char *ibuffer = nullptr; uint32_t n; bool folderIsLocal = true; nsCString turi; char16_t *printfString = nullptr; nsString msg; nsCOMPtr folder; // Before continuing, just check the user has not cancel the operation if (mSendProgress) { bool canceled = false; mSendProgress->GetProcessCanceledByUser(&canceled); if (canceled) return NS_ERROR_ABORT; else mSendProgress->OnProgressChange(nullptr, nullptr, 0, 0, 0, -1); } // // Ok, this is here to keep track of this for 2 copy operations... // if (mCopyFile) { mCopyFile2 = mCopyFile; mCopyFile = nullptr; } // // Create the file that will be used for the copy service! // nsresult rv = nsMsgCreateTempFile("nscopy.tmp", getter_AddRefs(mCopyFile)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr tempOutfile; rv = MsgNewBufferedFileOutputStream(getter_AddRefs(tempOutfile), mCopyFile, -1, 00600); if (NS_FAILED(rv)) { if (mSendReport) { nsAutoString error_msg; nsMsgBuildMessageWithTmpFile(mCopyFile, error_msg); mSendReport->SetMessage(nsIMsgSendReport::process_Current, error_msg.get(), false); } status = NS_MSG_UNABLE_TO_OPEN_TMP_FILE; mCopyFile = nullptr; return status; } // // Get our files ready... // nsCOMPtr inputFile; rv = NS_NewLocalFileInputStream(getter_AddRefs(inputFile), input_file); if (NS_FAILED(rv)) { if (mSendReport) { nsAutoString error_msg; nsMsgBuildMessageWithFile(input_file, error_msg); mSendReport->SetMessage(nsIMsgSendReport::process_Current, error_msg.get(), false); } status = NS_MSG_UNABLE_TO_OPEN_FILE; goto FAIL; } // now the buffers... ibuffer = (char *) PR_Malloc(ibuffer_size); if (!ibuffer) { status = NS_ERROR_OUT_OF_MEMORY; goto FAIL; } // // First, we we need to put a Berkeley "From - " delimiter at the head of // the file for parsing... // if (fcc_header && *fcc_header) GetExistingFolder(nsDependentCString(fcc_header), getter_AddRefs(folder)); if ((mode == nsMsgDeliverNow || mode == nsMsgSendUnsent) && folder) turi = fcc_header; else GetFolderURIFromUserPrefs(mode, mUserIdentity, turi); status = MessageFolderIsLocal(mUserIdentity, mode, turi.get(), &folderIsLocal); if (NS_FAILED(status)) goto FAIL; // Tell the user we are copying the message... mComposeBundle->GetStringFromName(u"copyMessageStart", getter_Copies(msg)); if (!msg.IsEmpty()) { nsCOMPtr rdfService = do_GetService(kRDFServiceCID); if (rdfService) { nsCOMPtr res; rdfService->GetResource(turi, getter_AddRefs(res)); nsCOMPtr folder = do_QueryInterface(res); if (folder) folder->GetName(mSavedToFolderName); } if (!mSavedToFolderName.IsEmpty()) printfString = nsTextFormatter::smprintf(msg.get(), mSavedToFolderName.get()); else printfString = nsTextFormatter::smprintf(msg.get(), "?"); if (printfString) { SetStatusMessage(nsDependentString(printfString)); PR_Free(printfString); } } if (folderIsLocal) { char *envelopeLine = nsMsgGetEnvelopeLine(); uint32_t len = PL_strlen(envelopeLine); rv = tempOutfile->Write(envelopeLine, len, &n); if (NS_FAILED(rv) || n != len) { status = NS_ERROR_FAILURE; goto FAIL; } } // // Write out an X-Mozilla-Status header. // // This is required for the queue file, so that we can overwrite it once // the messages have been delivered, and so that the nsMsgMessageFlags::Queued bit // is set. // // For FCC files, we don't necessarily need one, but we might as well put // one in so that it's marked as read already. // // // Need to add these lines for POP3 ONLY! IMAP servers will handle // this status information for summary file regeneration for us. if ((mode == nsMsgQueueForLater || mode == nsMsgSaveAsDraft || mode == nsMsgSaveAsTemplate || mode == nsMsgDeliverNow || mode == nsMsgSendUnsent || mode == nsMsgDeliverBackground) && folderIsLocal) { char *buf = 0; uint16_t flags = 0; // for save as draft and send later, we want to leave the message as unread. // See Bug #198087 // Messages sent with mode nsMsgDeliverBackground must not have the Queued // flag sent so that they get picked up by the background send function. if (mode == nsMsgQueueForLater) flags |= nsMsgMessageFlags::Queued; else if (mode != nsMsgSaveAsDraft && mode != nsMsgDeliverBackground) flags |= nsMsgMessageFlags::Read; buf = PR_smprintf(X_MOZILLA_STATUS_FORMAT CRLF, flags); if (buf) { uint32_t len = PL_strlen(buf); rv = tempOutfile->Write(buf, len, &n); PR_Free(buf); if (NS_FAILED(rv) || n != len) { status = NS_ERROR_FAILURE; goto FAIL; } } uint32_t flags2 = 0; if (mode == nsMsgSaveAsTemplate) flags2 |= nsMsgMessageFlags::Template; if (mode == nsMsgDeliverNow || mode == nsMsgSendUnsent) { flags2 &= ~nsMsgMessageFlags::MDNReportNeeded; flags2 |= nsMsgMessageFlags::MDNReportSent; } buf = PR_smprintf(X_MOZILLA_STATUS2_FORMAT CRLF, flags2); if (buf) { uint32_t len = PL_strlen(buf); rv = tempOutfile->Write(buf, len, &n); PR_Free(buf); if (NS_FAILED(rv) || n != len) { status = NS_ERROR_FAILURE; goto FAIL; } } tempOutfile->Write(X_MOZILLA_KEYWORDS, sizeof(X_MOZILLA_KEYWORDS) - 1, &n); } // Write out the FCC and BCC headers. // When writing to the Queue file, we *must* write the FCC and BCC // headers, or else that information would be lost. Because, when actually // delivering the message (with "deliver now") we do FCC/BCC right away; // but when queueing for later delivery, we do FCC/BCC at delivery-time. // // The question remains of whether FCC and BCC should be written into normal // BCC folders (like the Sent Mail folder.) // // For FCC, there seems no point to do that; it's not information that one // would want to refer back to. // // For BCC, the question isn't as clear. On the one hand, if I send someone // a BCC'ed copy of the message, and save a copy of it for myself (with FCC) // I might want to be able to look at that message later and see the list of // people to whom I had BCC'ed it. // // On the other hand, the contents of the BCC header is sensitive // information, and should perhaps not be stored at all. // // Thus the consultation of the #define SAVE_BCC_IN_FCC_FILE. // // (Note that, if there is a BCC header present in a message in some random // folder, and that message is forwarded to someone, then the attachment // code will strip out the BCC header before forwarding it.) // if ((mode == nsMsgQueueForLater || mode == nsMsgDeliverBackground || mode == nsMsgSaveAsDraft || mode == nsMsgSaveAsTemplate) && fcc_header && *fcc_header) { int32_t L = PL_strlen(fcc_header) + 20; char *buf = (char *) PR_Malloc (L); if (!buf) { status = NS_ERROR_OUT_OF_MEMORY; goto FAIL; } PR_snprintf(buf, L-1, "FCC: %s" CRLF, fcc_header); uint32_t len = PL_strlen(buf); rv = tempOutfile->Write(buf, len, &n); if (NS_FAILED(rv) || n != len) { status = NS_ERROR_FAILURE; goto FAIL; } } // // Ok, now I want to get the identity key and write it out if this is for a // nsMsgQueueForLater operation! // if ((nsMsgQueueForLater == mode || nsMsgSaveAsDraft == mode || nsMsgDeliverBackground == mode || nsMsgSaveAsTemplate == mode) && mUserIdentity) { char *buf = nullptr; nsCString key; if (NS_SUCCEEDED(mUserIdentity->GetKey(key)) && !key.IsEmpty()) { buf = PR_smprintf(HEADER_X_MOZILLA_IDENTITY_KEY ": %s" CRLF, key.get()); if (buf) { uint32_t len = strlen(buf); rv = tempOutfile->Write(buf, len, &n); PR_Free(buf); if (NS_FAILED(rv) || n != len) { status = NS_ERROR_FAILURE; goto FAIL; } } } if (!mAccountKey.IsEmpty()) { buf = PR_smprintf(HEADER_X_MOZILLA_ACCOUNT_KEY ": %s" CRLF, mAccountKey.get()); if (buf) { uint32_t len = strlen(buf); rv = tempOutfile->Write(buf, len, &n); PR_Free(buf); if (NS_FAILED(rv) || n != len) { status = NS_ERROR_FAILURE; goto FAIL; } } } } if (bcc_header && *bcc_header #ifndef SAVE_BCC_IN_FCC_FILE && (mode == MSG_QueueForLater || mode == MSG_SaveAsDraft || mode == MSG_SaveAsTemplate) #endif ) { char *convBcc; convBcc = nsMsgI18NEncodeMimePartIIStr(bcc_header, true, mCompFields->GetCharacterSet(), sizeof("BCC: "), nsMsgMIMEGetConformToStandard()); int32_t L = strlen(convBcc ? convBcc : bcc_header) + 20; char *buf = (char *) PR_Malloc (L); if (!buf) { status = NS_ERROR_OUT_OF_MEMORY; goto FAIL; } PR_snprintf(buf, L-1, "BCC: %s" CRLF, convBcc ? convBcc : bcc_header); uint32_t len = strlen(buf); rv = tempOutfile->Write(buf, len, &n); PR_Free(buf); PR_Free(convBcc); if (NS_FAILED(rv) || n != len) { status = NS_ERROR_FAILURE; goto FAIL; } } // // Write out the X-Mozilla-News-Host header. // This is done only when writing to the queue file, not the FCC file. // We need this to complement the "Newsgroups" header for the case of // queueing a message for a non-default news host. // // Convert a URL like "snews://host:123/" to the form "host:123/secure" // or "news://user@host:222" to simply "host:222". // if ((mode == nsMsgQueueForLater || mode == nsMsgSaveAsDraft || mode == nsMsgSaveAsTemplate || mode == nsMsgDeliverBackground) && news_url && *news_url) { bool secure_p = (news_url[0] == 's' || news_url[0] == 'S'); char *orig_hap = nsMsgParseURLHost (news_url); char *host_and_port = orig_hap; if (host_and_port) { // There may be authinfo at the front of the host - it could be of // the form "user:password@host:port", so take off everything before // the first at-sign. We don't want to store authinfo in the queue // folder, I guess, but would want it to be re-prompted-for at // delivery-time. // char *at = PL_strchr (host_and_port, '@'); if (at) host_and_port = at + 1; } if ((host_and_port && *host_and_port) || !secure_p) { char *line = PR_smprintf(X_MOZILLA_NEWSHOST ": %s%s" CRLF, host_and_port ? host_and_port : "", secure_p ? "/secure" : ""); PR_FREEIF(orig_hap); if (!line) { status = NS_ERROR_OUT_OF_MEMORY; goto FAIL; } uint32_t len = PL_strlen(line); rv = tempOutfile->Write(line, len, &n); PR_Free(line); if (NS_FAILED(rv) || n != len) { status = NS_ERROR_FAILURE; goto FAIL; } } PR_Free(orig_hap); } // // Read from the message file, and write to the FCC or Queue file. // There are two tricky parts: the first is that the message file // uses CRLF, and the FCC file should use LINEBREAK. The second // is that the message file may have lines beginning with "From " // but the FCC file must have those lines mangled. // // It's unfortunate that we end up writing the FCC file a line // at a time, but it's the easiest way... // uint64_t available; rv = inputFile->Available(&available); NS_ENSURE_SUCCESS(rv, rv); while (available > 0) { // check *ibuffer in case that ibuffer isn't big enough uint32_t readCount; rv = inputFile->Read(ibuffer, ibuffer_size, &readCount); if (NS_FAILED(rv) || readCount == 0 || *ibuffer == 0) { status = NS_ERROR_FAILURE; goto FAIL; } rv = tempOutfile->Write(ibuffer, readCount, &n); if (NS_FAILED(rv) || n != readCount) // write failed { status = NS_MSG_ERROR_WRITING_FILE; goto FAIL; } rv = inputFile->Available(&available); NS_ENSURE_SUCCESS(rv, rv); } FAIL: PR_Free(ibuffer); if (NS_FAILED(tempOutfile->Flush())) status = NS_MSG_ERROR_WRITING_FILE; tempOutfile->Close(); if (inputFile) inputFile->Close(); // here we should clone mCopyFile, since it has changed on disk. nsCOMPtr clonedFile; mCopyFile->Clone(getter_AddRefs(clonedFile)); mCopyFile = clonedFile; // When we get here, we have to see if we have been successful so far. // If we have, then we should start up the async copy service operation. // If we weren't successful, then we should just return the error and // bail out. if (NS_SUCCEEDED(status)) { // If we are here, time to start the async copy service operation! status = StartMessageCopyOperation(mCopyFile, mode, turi); } return status; } // // This is pretty much a wrapper to the functionality that lives in the // nsMsgCopy class // nsresult nsMsgComposeAndSend::StartMessageCopyOperation(nsIFile *aFile, nsMsgDeliverMode mode, const nsCString& dest_uri) { mCopyObj = new nsMsgCopy(); if (!mCopyObj) return NS_ERROR_OUT_OF_MEMORY; // // Actually, we need to pick up the proper folder from the prefs and not // default to the default "Flagged" folder choices // nsresult rv; if (!dest_uri.IsEmpty()) m_folderName = dest_uri; else GetFolderURIFromUserPrefs(mode, mUserIdentity, m_folderName); if (mListener) mListener->OnGetDraftFolderURI(m_folderName.get()); rv = mCopyObj->StartCopyOperation(mUserIdentity, aFile, mode, this, m_folderName.get(), mMsgToReplace); return rv; } //I'm getting this each time without holding onto the feedback so that 3 pane windows can be closed //without any chance of crashing due to holding onto a deleted feedback. nsresult nsMsgComposeAndSend::SetStatusMessage(const nsString &aMsgString) { if (mSendProgress) mSendProgress->OnStatusChange(nullptr, nullptr, NS_OK, aMsgString.get()); return NS_OK; } // For GUI notification... nsresult nsMsgComposeAndSend::SetGUINotificationState(bool aEnableFlag) { mGUINotificationEnabled = aEnableFlag; return NS_OK; } /* readonly attribute nsIMsgSendReport sendReport; */ NS_IMETHODIMP nsMsgComposeAndSend::GetSendReport(nsIMsgSendReport * *aSendReport) { NS_ENSURE_ARG_POINTER(aSendReport); NS_IF_ADDREF(*aSendReport = mSendReport); return NS_OK; } nsresult nsMsgComposeAndSend::Abort() { uint32_t i; nsresult rv; if (mAbortInProcess) return NS_OK; mAbortInProcess = true; if (m_plaintext) rv = m_plaintext->Abort(); for (i = 0; i < m_attachment_count; i ++) { nsMsgAttachmentHandler *ma = m_attachments[i]; if (ma) rv = ma->Abort(); } /* stop the current running url */ if (mRunningRequest) { mRunningRequest->Cancel(NS_ERROR_ABORT); mRunningRequest = nullptr; } if (mCopyObj) { nsCOMPtr copyService = do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); copyService->NotifyCompletion(mCopyFile, mCopyObj->mDstFolder, NS_ERROR_ABORT); } mAbortInProcess = false; return NS_OK; } NS_IMETHODIMP nsMsgComposeAndSend::GetProcessAttachmentsSynchronously(bool *_retval) { NS_ENSURE_ARG(_retval); *_retval = m_be_synchronous_p; return NS_OK; } NS_IMETHODIMP nsMsgComposeAndSend::GetAttachmentHandlers(nsTArray> **_retval) { NS_ENSURE_ARG(_retval); *_retval = &m_attachments; return NS_OK; } NS_IMETHODIMP nsMsgComposeAndSend::GetAttachmentCount(uint32_t *aAttachmentCount) { NS_ENSURE_ARG(aAttachmentCount); *aAttachmentCount = m_attachment_count; return NS_OK; } NS_IMETHODIMP nsMsgComposeAndSend::GetPendingAttachmentCount(uint32_t *aPendingAttachmentCount) { NS_ENSURE_ARG(aPendingAttachmentCount); *aPendingAttachmentCount = m_attachment_pending_count; return NS_OK; } NS_IMETHODIMP nsMsgComposeAndSend::SetPendingAttachmentCount(uint32_t aPendingAttachmentCount) { m_attachment_pending_count = aPendingAttachmentCount; return NS_OK; } NS_IMETHODIMP nsMsgComposeAndSend::GetDeliveryMode(nsMsgDeliverMode *aDeliveryMode) { NS_ENSURE_ARG(aDeliveryMode); *aDeliveryMode = m_deliver_mode; return NS_OK; } NS_IMETHODIMP nsMsgComposeAndSend::GetProgress(nsIMsgProgress **_retval) { NS_ENSURE_ARG(_retval); NS_IF_ADDREF(*_retval = mSendProgress); return NS_OK; } NS_IMETHODIMP nsMsgComposeAndSend::GetOutputStream(nsIOutputStream **_retval) { NS_ENSURE_ARG(_retval); NS_IF_ADDREF(*_retval = mOutputFile); return NS_OK; } /* [noscript] attribute nsIURI runningURL; */ NS_IMETHODIMP nsMsgComposeAndSend::GetRunningRequest(nsIRequest **request) { NS_ENSURE_ARG(request); NS_IF_ADDREF(*request = mRunningRequest); return NS_OK; } NS_IMETHODIMP nsMsgComposeAndSend::SetRunningRequest(nsIRequest *request) { mRunningRequest = request; return NS_OK; } NS_IMETHODIMP nsMsgComposeAndSend::GetStatus(nsresult *aStatus) { NS_ENSURE_ARG(aStatus); *aStatus = m_status; return NS_OK; } NS_IMETHODIMP nsMsgComposeAndSend::SetStatus(nsresult aStatus) { m_status = aStatus; return NS_OK; } NS_IMETHODIMP nsMsgComposeAndSend::GetCryptoclosure(nsIMsgComposeSecure ** aCryptoclosure) { NS_ENSURE_ARG(aCryptoclosure); NS_IF_ADDREF(*aCryptoclosure = m_crypto_closure); return NS_OK; } NS_IMETHODIMP nsMsgComposeAndSend::SetCryptoclosure(nsIMsgComposeSecure * aCryptoclosure) { m_crypto_closure = aCryptoclosure; return NS_OK; } NS_IMETHODIMP nsMsgComposeAndSend::GetSendCompFields(nsIMsgCompFields** aCompFields) { NS_ENSURE_ARG_POINTER(aCompFields); nsCOMPtr qiCompFields(mCompFields); NS_ENSURE_STATE(qiCompFields); qiCompFields.forget(aCompFields); return NS_OK; } NS_IMETHODIMP nsMsgComposeAndSend::GetSendBody(nsAString& aBody) { nsCString charSet; if (mCompFields) mCompFields->GetCharacterSet(getter_Copies(charSet)); if (!m_attachment1_body) { aBody.Truncate(); return NS_OK; } return ConvertToUnicode(charSet.get(), m_attachment1_body, aBody); } NS_IMETHODIMP nsMsgComposeAndSend::GetSendBodyType(nsACString& aBodyType) { if (m_attachment1_type && *m_attachment1_type) aBodyType.Assign(nsDependentCString(m_attachment1_type)); return NS_OK; } NS_IMETHODIMP nsMsgComposeAndSend::GetIdentity(nsIMsgIdentity **aIdentity) { NS_ENSURE_ARG_POINTER(aIdentity); *aIdentity = mUserIdentity; NS_IF_ADDREF(*aIdentity); return NS_OK; } NS_IMETHODIMP nsMsgComposeAndSend::GetAttachment(uint32_t aIndex, nsIMsgAttachmentHandler **aAttachment) { NS_ENSURE_ARG_POINTER(aAttachment); if (aIndex >= m_attachment_count) return NS_ERROR_ILLEGAL_VALUE; *aAttachment = m_attachments[aIndex]; NS_IF_ADDREF(*aAttachment); return NS_OK; } NS_IMETHODIMP nsMsgComposeAndSend::SetSavedToFolderName(const nsAString& aName) { mSavedToFolderName.Assign(aName); return NS_OK; } NS_IMETHODIMP nsMsgComposeAndSend::GetSavedToFolderName(nsAString& aName) { aName.Assign(mSavedToFolderName); return NS_OK; } NS_IMETHODIMP nsMsgComposeAndSend::SetDontDeliver(bool aDontDeliver) { m_dont_deliver_p = aDontDeliver; return NS_OK; } NS_IMETHODIMP nsMsgComposeAndSend::GetDontDeliver(bool *aDontDeliver) { NS_ENSURE_ARG_POINTER(aDontDeliver); *aDontDeliver = m_dont_deliver_p; return NS_OK; } NS_IMPL_ISUPPORTS(nsMsgAttachmentData, nsIMsgAttachmentData) nsMsgAttachmentData::nsMsgAttachmentData() : m_size(0), m_sizeExternalStr("-1"), m_isExternalAttachment(false), m_isExternalLinkAttachment(false), m_isDownloaded(false), m_hasFilename(false), m_displayableInline(false) { } nsMsgAttachmentData::~nsMsgAttachmentData() { } NS_IMETHODIMP nsMsgAttachmentData::GetUrl(nsIURI **aUrl) { NS_ENSURE_ARG_POINTER(aUrl); NS_IF_ADDREF(*aUrl = m_url); return NS_OK; } NS_IMETHODIMP nsMsgAttachmentData::SetUrl(nsIURI *aUrl) { m_url = aUrl; return NS_OK; } NS_IMETHODIMP nsMsgAttachmentData::GetDesiredType(nsACString &aDesiredType) { aDesiredType = m_desiredType; return NS_OK; } NS_IMETHODIMP nsMsgAttachmentData::SetDesiredType(const nsACString &aDesiredType) { m_desiredType = aDesiredType; return NS_OK; } NS_IMETHODIMP nsMsgAttachmentData::GetRealType(nsACString &aRealType) { aRealType = m_realType; return NS_OK; } NS_IMETHODIMP nsMsgAttachmentData::SetRealType(const nsACString &aRealType) { m_realType = aRealType; return NS_OK; } NS_IMETHODIMP nsMsgAttachmentData::GetRealEncoding(nsACString &aRealEncoding) { aRealEncoding = m_realEncoding; return NS_OK; } NS_IMETHODIMP nsMsgAttachmentData::SetRealEncoding(const nsACString &aRealEncoding) { m_realEncoding = aRealEncoding; return NS_OK; } NS_IMETHODIMP nsMsgAttachmentData::GetRealName(nsACString &aRealName) { aRealName = m_realName; return NS_OK; } NS_IMETHODIMP nsMsgAttachmentData::SetRealName(const nsACString &aRealName) { m_realName = aRealName; return NS_OK; } NS_IMETHODIMP nsMsgAttachmentData::GetDescription(nsACString &aDescription) { aDescription = m_description; return NS_OK; } NS_IMETHODIMP nsMsgAttachmentData::SetDescription(const nsACString &aDescription) { m_description = aDescription; return NS_OK; } NS_IMETHODIMP nsMsgAttachmentData::GetXMacType(nsACString & aXMacType) { aXMacType = m_xMacType; return NS_OK; } NS_IMETHODIMP nsMsgAttachmentData::SetXMacType(const nsACString & aXMacType) { m_xMacType = aXMacType; return NS_OK; } NS_IMETHODIMP nsMsgAttachmentData::GetXMacCreator(nsACString & aXMacCreator) { aXMacCreator = m_xMacCreator; return NS_OK; } NS_IMETHODIMP nsMsgAttachmentData::SetXMacCreator(const nsACString & aXMacCreator) { m_xMacCreator = aXMacCreator; return NS_OK; } NS_IMPL_ISUPPORTS(nsMsgAttachedFile, nsIMsgAttachedFile) nsMsgAttachedFile::nsMsgAttachedFile() : m_size(0), m_unprintableCount(0), m_highbitCount(0), m_ctlCount(0), m_nullCount(0), m_maxLineLength(0) { } nsMsgAttachedFile::~nsMsgAttachedFile() { } NS_IMETHODIMP nsMsgAttachedFile::GetOrigUrl(nsIURI **aOrigUrl) { NS_ENSURE_ARG_POINTER(aOrigUrl); NS_IF_ADDREF(*aOrigUrl = m_origUrl); return NS_OK; } NS_IMETHODIMP nsMsgAttachedFile::SetOrigUrl(nsIURI *aOrigUrl) { m_origUrl = aOrigUrl; return NS_OK; } NS_IMETHODIMP nsMsgAttachedFile::GetTmpFile(nsIFile **aTmpFile) { NS_ENSURE_ARG_POINTER(aTmpFile); NS_IF_ADDREF(*aTmpFile = m_tmpFile); return NS_OK; } NS_IMETHODIMP nsMsgAttachedFile::SetTmpFile(nsIFile *aTmpFile) { m_tmpFile = aTmpFile; return NS_OK; } NS_IMETHODIMP nsMsgAttachedFile::GetType(nsACString &aType) { aType = m_type; return NS_OK; } NS_IMETHODIMP nsMsgAttachedFile::SetType(const nsACString &aType) { m_type = aType; return NS_OK; } NS_IMETHODIMP nsMsgAttachedFile::GetEncoding(nsACString &aEncoding) { aEncoding = m_encoding; return NS_OK; } NS_IMETHODIMP nsMsgAttachedFile::SetEncoding(const nsACString &aEncoding) { m_encoding = aEncoding; return NS_OK; } NS_IMETHODIMP nsMsgAttachedFile::GetDescription(nsACString &aDescription) { aDescription = m_description; return NS_OK; } NS_IMETHODIMP nsMsgAttachedFile::SetDescription(const nsACString &aDescription) { m_description = aDescription; return NS_OK; } NS_IMETHODIMP nsMsgAttachedFile::GetCloudPartInfo(nsACString &aCloudPartInfo) { aCloudPartInfo = m_cloudPartInfo; return NS_OK; } NS_IMETHODIMP nsMsgAttachedFile::SetCloudPartInfo(const nsACString &aCloudPartInfo) { m_cloudPartInfo = aCloudPartInfo; return NS_OK; } NS_IMETHODIMP nsMsgAttachedFile::GetXMacType(nsACString & aXMacType) { aXMacType = m_xMacType; return NS_OK; } NS_IMETHODIMP nsMsgAttachedFile::SetXMacType(const nsACString & aXMacType) { m_xMacType = aXMacType; return NS_OK; } NS_IMETHODIMP nsMsgAttachedFile::GetXMacCreator(nsACString & aXMacCreator) { aXMacCreator = m_xMacCreator; return NS_OK; } NS_IMETHODIMP nsMsgAttachedFile::SetXMacCreator(const nsACString & aXMacCreator) { m_xMacCreator = aXMacCreator; return NS_OK; } NS_IMETHODIMP nsMsgAttachedFile::GetRealName(nsACString & aRealName) { aRealName = m_realName; return NS_OK; } NS_IMETHODIMP nsMsgAttachedFile::SetRealName(const nsACString & aRealName) { m_realName = aRealName; return NS_OK; } NS_IMETHODIMP nsMsgAttachedFile::GetSize(uint32_t *aSize) { NS_ENSURE_ARG_POINTER(aSize); *aSize = m_size; return NS_OK; } NS_IMETHODIMP nsMsgAttachedFile::SetSize(uint32_t aSize) { m_size = aSize; return NS_OK; } NS_IMETHODIMP nsMsgAttachedFile::GetUnprintableCount(uint32_t *aUnprintableCount) { NS_ENSURE_ARG_POINTER(aUnprintableCount); *aUnprintableCount = m_unprintableCount; return NS_OK; } NS_IMETHODIMP nsMsgAttachedFile::SetUnprintableCount(uint32_t aUnprintableCount) { m_unprintableCount = aUnprintableCount; return NS_OK; } NS_IMETHODIMP nsMsgAttachedFile::GetHighbitCount(uint32_t *aHighbitCount) { NS_ENSURE_ARG_POINTER(aHighbitCount); *aHighbitCount = m_highbitCount; return NS_OK; } NS_IMETHODIMP nsMsgAttachedFile::SetHighbitCount(uint32_t aHighbitCount) { m_highbitCount = aHighbitCount; return NS_OK; } NS_IMETHODIMP nsMsgAttachedFile::GetCtlCount(uint32_t *aCtlCount) { NS_ENSURE_ARG_POINTER(aCtlCount); *aCtlCount = m_ctlCount; return NS_OK; } NS_IMETHODIMP nsMsgAttachedFile::SetCtlCount(uint32_t aCtlCount) { m_ctlCount = aCtlCount; return NS_OK; } NS_IMETHODIMP nsMsgAttachedFile::GetNullCount(uint32_t *aNullCount) { NS_ENSURE_ARG_POINTER(aNullCount); *aNullCount = m_nullCount; return NS_OK; } NS_IMETHODIMP nsMsgAttachedFile::SetNullCount(uint32_t aNullCount) { m_nullCount = aNullCount; return NS_OK; } NS_IMETHODIMP nsMsgAttachedFile::GetMaxLineLength(uint32_t *aMaxLineLength) { NS_ENSURE_ARG_POINTER(aMaxLineLength); *aMaxLineLength = m_maxLineLength; return NS_OK; } NS_IMETHODIMP nsMsgAttachedFile::SetMaxLineLength(uint32_t aMaxLineLength) { m_maxLineLength = aMaxLineLength; return NS_OK; }