diff options
Diffstat (limited to 'mailnews/compose/src/nsMsgSendPart.cpp')
-rw-r--r-- | mailnews/compose/src/nsMsgSendPart.cpp | 779 |
1 files changed, 779 insertions, 0 deletions
diff --git a/mailnews/compose/src/nsMsgSendPart.cpp b/mailnews/compose/src/nsMsgSendPart.cpp new file mode 100644 index 000000000..a33dabf33 --- /dev/null +++ b/mailnews/compose/src/nsMsgSendPart.cpp @@ -0,0 +1,779 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "nsMsgLocalFolderHdrs.h" +#include "nsMsgSend.h" +#include "nsMsgSendPart.h" +#include "nsIMimeConverter.h" +#include "nsCOMPtr.h" +#include "nsIComponentManager.h" +#include "nsMsgI18N.h" +#include "nsMsgCompUtils.h" +#include "nsMsgMimeCID.h" +#include "nsMimeTypes.h" +#include "prmem.h" +#include "nsMsgPrompts.h" +#include "nsNativeCharsetUtils.h" +#include "nsNetUtil.h" +#include "nsISeekableStream.h" +#include "nsReadLine.h" +#include "nsILineInputStream.h" +#include "nsComposeStrings.h" +#include "mozilla/mailnews/MimeEncoder.h" + +static char *mime_mailto_stream_read_buffer = 0; + +int32_t nsMsgSendPart::M_counter = 0; + +nsMsgSendPart::nsMsgSendPart(nsIMsgSend* state, const char *part_charset) +{ + PL_strncpy(m_charset_name, (part_charset ? part_charset : "UTF-8"), sizeof(m_charset_name)-1); + m_charset_name[sizeof(m_charset_name)-1] = '\0'; + m_children = nullptr; + m_numchildren = 0; + // if we're not added as a child, the default part number will be "1". + m_partNum = "1"; + SetMimeDeliveryState(state); + + m_parent = nullptr; + m_buffer = nullptr; + m_type = nullptr; + m_other = nullptr; + m_strip_sensitive_headers = false; + + m_firstBlock = false; + m_needIntlConversion = false; + + m_mainpart = false; + m_just_hit_CR = false; +} + + +nsMsgSendPart::~nsMsgSendPart() +{ + for (int i=0 ; i < m_numchildren; i++) + delete m_children[i]; + + delete [] m_children; + PR_FREEIF(m_buffer); + PR_FREEIF(m_other); + PR_FREEIF(m_type); +} + +nsresult nsMsgSendPart::CopyString(char** dest, const char* src) +{ + NS_ASSERTION(src, "src null"); + + PR_FREEIF(*dest); + if (!src) + *dest = PL_strdup(""); + else + *dest = PL_strdup(src); + + return *dest? NS_OK : NS_ERROR_OUT_OF_MEMORY; +} + + +nsresult nsMsgSendPart::SetFile(nsIFile *file) +{ + m_file = file; + return NS_OK; +} + + +nsresult nsMsgSendPart::SetBuffer(const char* buffer) +{ + PR_FREEIF(m_buffer); + return CopyString(&m_buffer, buffer); +} + + +nsresult nsMsgSendPart::SetType(const char* type) +{ + PR_FREEIF(m_type); + m_type = PL_strdup(type); + return m_type ? NS_OK : NS_ERROR_OUT_OF_MEMORY; +} + + +nsresult nsMsgSendPart::SetOtherHeaders(const char* other) +{ + return CopyString(&m_other, other); +} + +nsresult nsMsgSendPart::SetMimeDeliveryState(nsIMsgSend *state) +{ + m_state = state; + if (GetNumChildren() > 0) + { + for (int i = 0; i < GetNumChildren(); i++) + { + nsMsgSendPart *part = GetChild(i); + if (part) + part->SetMimeDeliveryState(state); + } + } + return NS_OK; +} + +nsresult nsMsgSendPart::AppendOtherHeaders(const char* more) +{ + if (!m_other) + return SetOtherHeaders(more); + + if (!more || !*more) + return NS_OK; + + char* tmp = (char *) PR_Malloc(sizeof(char) * (PL_strlen(m_other) + PL_strlen(more) + 2)); + if (!tmp) + return NS_ERROR_OUT_OF_MEMORY; + + PL_strcpy(tmp, m_other); + PL_strcat(tmp, more); + PR_FREEIF(m_other); + m_other = tmp; + + return NS_OK; +} + + +nsresult nsMsgSendPart::SetMainPart(bool value) +{ + m_mainpart = value; + return NS_OK; +} + +nsresult nsMsgSendPart::AddChild(nsMsgSendPart* child) +{ + m_numchildren++; + nsMsgSendPart** tmp = new nsMsgSendPart* [m_numchildren]; + if (tmp == nullptr) return NS_ERROR_OUT_OF_MEMORY; + for (int i=0 ; i<m_numchildren-1 ; i++) { + tmp[i] = m_children[i]; + } + delete [] m_children; + m_children = tmp; + m_children[m_numchildren - 1] = child; + child->m_parent = this; + nsCString partNum(m_partNum); + partNum.Append("."); + partNum.AppendInt(m_numchildren); + child->m_partNum = partNum; + return NS_OK; +} + +nsMsgSendPart * nsMsgSendPart::DetachChild(int32_t whichOne) +{ + nsMsgSendPart *returnValue = nullptr; + + NS_ASSERTION(whichOne >= 0 && whichOne < m_numchildren, "parameter out of range"); + if (whichOne >= 0 && whichOne < m_numchildren) + { + returnValue = m_children[whichOne]; + + if (m_numchildren > 1) + { + nsMsgSendPart** tmp = new nsMsgSendPart* [m_numchildren-1]; + if (tmp != nullptr) + { + // move all the other kids over + for (int i=0 ; i<m_numchildren-1 ; i++) + { + if (i >= whichOne) + tmp[i] = m_children[i+1]; + else + tmp[i] = m_children[i]; + } + delete [] m_children; + m_children = tmp; + m_numchildren--; + } + } + else + { + delete [] m_children; + m_children = nullptr; + m_numchildren = 0; + } + } + + if (returnValue) + returnValue->m_parent = nullptr; + + return returnValue; +} + +nsMsgSendPart* nsMsgSendPart::GetChild(int32_t which) +{ + NS_ASSERTION(which >= 0 && which < m_numchildren, "parameter out of range"); + if (which >= 0 && which < m_numchildren) { + return m_children[which]; + } + return nullptr; +} + + + +nsresult nsMsgSendPart::PushBody(const char* buffer, int32_t length) +{ + nsresult status = NS_OK; + const char* encoded_data = buffer; + + if (m_encoder) + { + status = m_encoder->Write(encoded_data, length); + } + else + { + // Merely translate all linebreaks to CRLF. + const char *in = encoded_data; + const char *end = in + length; + char *buffer, *out; + + + buffer = mime_get_stream_write_buffer(); + // XXX -1 is not a valid nsresult + NS_ENSURE_TRUE(buffer, static_cast<nsresult>(-1)); + + NS_ASSERTION(encoded_data != buffer, "encoded_data == buffer"); + out = buffer; + + for (; in < end; in++) { + if (m_just_hit_CR) { + m_just_hit_CR = false; + if (*in == '\n') { + // The last thing we wrote was a CRLF from hitting a CR. + // So, we don't want to do anything from a following LF; + // we want to ignore it. + continue; + } + } + if (*in == '\r' || *in == '\n') { + /* Write out the newline. */ + *out++ = '\r'; + *out++ = '\n'; + + status = mime_write_message_body(m_state, buffer, + out - buffer); + if (NS_FAILED(status)) return status; + out = buffer; + + if (*in == '\r') { + m_just_hit_CR = true; + } + + out = buffer; + } else { + + /* Fix for bug #95985. We can't assume that all lines are shorter + than 4096 chars (MIME_BUFFER_SIZE), so we need to test + for this here. sfraser. + */ + if (out - buffer >= MIME_BUFFER_SIZE) + { + status = mime_write_message_body(m_state, buffer, out - buffer); + if (NS_FAILED(status)) return status; + + out = buffer; + } + + *out++ = *in; + } + } + + /* Flush the last line. */ + if (out > buffer) { + status = mime_write_message_body(m_state, buffer, out - buffer); + if (NS_FAILED(status)) return status; + out = buffer; + } + } + + if (encoded_data && encoded_data != buffer) { + PR_Free((char *) encoded_data); + } + + return status; +} + + +/* Partition the headers into those which apply to the message as a whole; +those which apply to the message's contents; and the Content-Type header +itself. (This relies on the fact that all body-related headers begin with +"Content-".) + + (How many header parsers are in this program now?) + */ +static nsresult +divide_content_headers(const char *headers, + char **message_headers, + char **content_headers, + char **content_type_header) +{ + const char *tail; + char *message_tail, *content_tail, *type_tail; + int L = 0; + if (headers) + L = PL_strlen(headers); + + if (L == 0) + return NS_OK; + + *message_headers = (char *)PR_Malloc(L+1); + if (!*message_headers) + return NS_ERROR_OUT_OF_MEMORY; + + *content_headers = (char *)PR_Malloc(L+1); + if (!*content_headers) { + PR_Free(*message_headers); + return NS_ERROR_OUT_OF_MEMORY; + } + + *content_type_header = (char *)PR_Malloc(L+1); + if (!*content_type_header) { + PR_Free(*message_headers); + PR_Free(*content_headers); + return NS_ERROR_OUT_OF_MEMORY; + } + + message_tail = *message_headers; + content_tail = *content_headers; + type_tail = *content_type_header; + tail = headers; + + while (*tail) + { + const char *head = tail; + char **out; + while(true) { + /* Loop until we reach a newline that is not followed by whitespace. + */ + if (tail[0] == 0 || + ((tail[0] == '\r' || tail[0] == '\n') && + !(tail[1] == ' ' || tail[1] == '\t' || tail[1] == '\n'))) + { + /* Swallow the whole newline. */ + if (tail[0] == '\r' && tail[1] == '\n') + tail++; + if (*tail) + tail++; + break; + } + tail++; + } + + /* Decide which block this header goes into. + */ + if (!PL_strncasecmp(head, "Content-Type:", 13)) + out = &type_tail; + else + if (!PL_strncasecmp(head, "Content-", 8)) + out = &content_tail; + else + out = &message_tail; + + memcpy(*out, head, (tail-head)); + *out += (tail-head); + } + + *message_tail = 0; + *content_tail = 0; + *type_tail = 0; + + if (!**message_headers) { + PR_Free(*message_headers); + *message_headers = 0; + } + + if (!**content_headers) { + PR_Free(*content_headers); + *content_headers = 0; + } + + if (!**content_type_header) { + PR_Free(*content_type_header); + *content_type_header = 0; + } + +#ifdef DEBUG + // ### mwelch Because of the extreme difficulty we've had with + // duplicate part headers, I'm going to put in an + // ASSERT here which makes sure that no duplicate + // Content-Type or Content-Transfer-Encoding headers + // leave here undetected. + const char* tmp; + if (*content_type_header) { + tmp = PL_strstr(*content_type_header, "Content-Type"); + if (tmp) { + tmp++; // get past the first occurrence + NS_ASSERTION(!PL_strstr(tmp, "Content-Type"), "Content-part already present"); + } + } + + if (*content_headers) { + tmp = PL_strstr(*content_headers, "Content-Transfer-Encoding"); + if (tmp) { + tmp++; // get past the first occurrence + NS_ASSERTION(!PL_strstr(tmp, "Content-Transfer-Encoding"), "Content-Transfert already present"); + } + } +#endif // DEBUG + + return NS_OK; +} + +#define SKIP_EMPTY_PART 1966 + +nsresult +nsMsgSendPart::Write() +{ + nsresult status = NS_OK; + char *separator = nullptr; + bool needToWriteCRLFAfterEncodedBody = false; + +#define PUSHLEN(str, length) \ + do { \ + status = mime_write_message_body(m_state, str, length); \ + if (NS_FAILED(status)) goto FAIL; \ + } while (0) \ + +#define PUSH(str) PUSHLEN(str, PL_strlen(str)) + + // rhp: Suppress the output of parts that are empty! + if ( (m_parent) && + (m_numchildren == 0) && + ( (!m_buffer) || (!*m_buffer) ) && + (!m_file) && + (!m_mainpart) ) + // XXX SKIP_EMPTY_PART (= 1966) is not a valid nsresult + return static_cast<nsresult>(SKIP_EMPTY_PART); + + if (m_mainpart && m_type && PL_strcmp(m_type, TEXT_HTML) == 0) + { + if (m_file) + { + // The "insert HTML links" code requires a memory buffer, + // so read the file into memory. + NS_ASSERTION(m_buffer == nullptr, "not-null buffer"); + int32_t length = 0; + int64_t fileSize; + if (NS_SUCCEEDED(m_file->GetFileSize(&fileSize))) + length = fileSize; + + m_buffer = (char *) PR_Malloc(sizeof(char) * (length + 1)); + if (m_buffer) + { + nsCOMPtr<nsIInputStream> inputFile; + nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(inputFile), m_file); + if (NS_SUCCEEDED(rv)) + { + uint32_t bytesRead; + rv = inputFile->Read(m_buffer, length, &bytesRead); + inputFile->Close(); + m_buffer[length] = '\0'; + } + else + PR_Free(m_buffer); + } + } + } + + if (m_parent && m_parent->m_type && + !PL_strcasecmp(m_parent->m_type, MULTIPART_DIGEST) && + m_type && + (!PL_strcasecmp(m_type, MESSAGE_RFC822) || + !PL_strcasecmp(m_type, MESSAGE_NEWS))) + { + // If we're in a multipart/digest, and this document is of type + // message/rfc822, then it's appropriate to emit no headers. + // + } + else + { + char *message_headers = 0; + char *content_headers = 0; + char *content_type_header = 0; + status = divide_content_headers(m_other, + &message_headers, + &content_headers, + &content_type_header); + if (NS_FAILED(status)) + goto FAIL; + + /* First, write out all of the headers that refer to the message + itself (From, Subject, MIME-Version, etc.) + */ + if (message_headers) + { + PUSH(message_headers); + PR_Free(message_headers); + message_headers = 0; + } + + /* Now allow the crypto library to (potentially) insert some text + (it may want to wrap the body in an envelope.) */ + if (!m_parent) { + status = m_state->BeginCryptoEncapsulation(); + if (NS_FAILED(status)) goto FAIL; + } + + /* Now make sure there's a Content-Type header. + */ + if (!content_type_header) + { + NS_ASSERTION(m_type && *m_type, "null ptr"); + bool needsCharset = mime_type_needs_charset(m_type ? m_type : TEXT_PLAIN); + if (needsCharset) + { + content_type_header = PR_smprintf("Content-Type: %s; charset=%s" CRLF, + (m_type ? m_type : TEXT_PLAIN), m_charset_name); + } + else + content_type_header = PR_smprintf("Content-Type: %s" CRLF, + (m_type ? m_type : TEXT_PLAIN)); + if (!content_type_header) + { + if (content_headers) + PR_Free(content_headers); + status = NS_ERROR_OUT_OF_MEMORY; + goto FAIL; + } + } + + /* If this is a compound object, tack a boundary string onto the + Content-Type header. this + */ + if (m_numchildren > 0) + { + int L; + char *ct2; + NS_ASSERTION(m_type, "null ptr"); + + if (!separator) + { + separator = mime_make_separator(""); + if (!separator) + { + status = NS_ERROR_OUT_OF_MEMORY; + goto FAIL; + } + } + + L = PL_strlen(content_type_header); + + if (content_type_header[L-1] == '\n') + content_type_header[--L] = 0; + if (content_type_header[L-1] == '\r') + content_type_header[--L] = 0; + + ct2 = PR_smprintf("%s;\r\n boundary=\"%s\"" CRLF, content_type_header, separator); + PR_Free(content_type_header); + if (!ct2) + { + if (content_headers) + PR_Free(content_headers); + status = NS_ERROR_OUT_OF_MEMORY; + goto FAIL; + } + + content_type_header = ct2; + } + + // Now write out the Content-Type header... + NS_ASSERTION(content_type_header && *content_type_header, "null ptr"); + PUSH(content_type_header); + PR_Free(content_type_header); + content_type_header = 0; + + /* ...followed by all of the other headers that refer to the body of + the message (Content-Transfer-Encoding, Content-Dispositon, etc.) + */ + if (content_headers) + { + PUSH(content_headers); + PR_Free(content_headers); + content_headers = 0; + } + } + + PUSH(CRLF); // A blank line, to mark the end of headers. + + m_firstBlock = true; + /* only convert if we need to tag charset */ + m_needIntlConversion = mime_type_needs_charset(m_type); + + if (m_buffer) + { + status = PushBody(m_buffer, PL_strlen(m_buffer)); + if (NS_FAILED(status)) + goto FAIL; + } + else if (m_file) + { + nsCOMPtr<nsIInputStream> inputStream; + nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), m_file); + if (NS_FAILED(rv)) + { + // mysteriously disappearing? + nsCOMPtr<nsIMsgSendReport> sendReport; + m_state->GetSendReport(getter_AddRefs(sendReport)); + if (sendReport) + { + nsAutoString error_msg; + nsMsgBuildMessageWithTmpFile(m_file, error_msg); + sendReport->SetMessage(nsIMsgSendReport::process_Current, error_msg.get(), false); + } + status = NS_MSG_UNABLE_TO_OPEN_TMP_FILE; + goto FAIL; + } + + nsCString curLine; + bool more = true; + + /* 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); + if (!mime_mailto_stream_read_buffer) + { + status = NS_ERROR_OUT_OF_MEMORY; + goto FAIL; + } + } + + char *buffer = mime_mailto_stream_read_buffer; + if (m_strip_sensitive_headers) + { + // We are attaching a message, so we should be careful to + // strip out certain sensitive internal header fields. + bool skipping = false; + nsAutoPtr<nsLineBuffer<char> > lineBuffer(new nsLineBuffer<char>); + NS_ENSURE_TRUE(lineBuffer, NS_ERROR_OUT_OF_MEMORY); + + while (more) + { + // NS_ReadLine doesn't return line termination chars. + rv = NS_ReadLine(inputStream.get(), lineBuffer.get(), curLine, &more); + + curLine.Append(CRLF); + + char *line = (char *) curLine.get(); + if (skipping) { + if (*line == ' ' || *line == '\t') + continue; + else + skipping = false; + } + + if (!PL_strncasecmp(line, "From -", 6) || + !PL_strncasecmp(line, "BCC:", 4) || + !PL_strncasecmp(line, "FCC:", 4) || + !PL_strncasecmp(line, CONTENT_LENGTH ":", CONTENT_LENGTH_LEN+1) || + !PL_strncasecmp(line, "Lines:", 6) || + !PL_strncasecmp(line, "Status:", 7) || + !PL_strncasecmp(line, X_MOZILLA_STATUS ":", X_MOZILLA_STATUS_LEN+1) || + !PL_strncasecmp(line, X_MOZILLA_STATUS2 ":", X_MOZILLA_STATUS2_LEN+1) || + !PL_strncasecmp(line, X_MOZILLA_DRAFT_INFO ":", X_MOZILLA_DRAFT_INFO_LEN+1) || + !PL_strncasecmp(line, X_MOZILLA_NEWSHOST ":", X_MOZILLA_NEWSHOST_LEN+1) || + !PL_strncasecmp(line, X_UIDL ":", X_UIDL_LEN+1) || + !PL_strncasecmp(line, "X-VM-", 5)) /* hi Kyle */ + { + skipping = true; + continue; + } + + PUSH(line); + + if (curLine.Length() == 2) { + nsCOMPtr <nsISeekableStream> seekableStream = do_QueryInterface(inputStream); + // seek back the amount of data left in the line buffer... + seekableStream->Seek(nsISeekableStream::NS_SEEK_CUR, lineBuffer->start - lineBuffer->end); + break; // Now can do normal reads for the body. + } + } + lineBuffer = nullptr; + } + + while (NS_SUCCEEDED(status)) + { + uint32_t bytesRead; + nsresult rv = inputStream->Read(buffer, MIME_BUFFER_SIZE, &bytesRead); + if (NS_FAILED(rv)) + { + nsCOMPtr<nsIMsgSendReport> sendReport; + m_state->GetSendReport(getter_AddRefs(sendReport)); + if (sendReport) + { + nsAutoString error_msg; + nsMsgBuildMessageWithFile(m_file, error_msg); + sendReport->SetMessage(nsIMsgSendReport::process_Current, error_msg.get(), false); + status = NS_MSG_UNABLE_TO_OPEN_FILE; + goto FAIL; + } + } + status = PushBody(buffer, bytesRead); + if (NS_FAILED(status)) + goto FAIL; + if (bytesRead < MIME_BUFFER_SIZE) + break; + } + } + + if (m_encoder) + { + nsresult rv = m_encoder->Flush(); + m_encoder = nullptr; + needToWriteCRLFAfterEncodedBody = !m_parent; + if (NS_FAILED(rv)) + { + // XXX -1 is not a valid nsresult + status = static_cast<nsresult>(-1); + goto FAIL; + } + } + + // + // Ok, from here we loop and drive the the output of all children + // for this message. + // + if (m_numchildren > 0) + { + bool writeSeparator = true; + + for (int i = 0 ; i < m_numchildren ; i ++) + { + if (writeSeparator) + { + PUSH(CRLF); + PUSH("--"); + + PUSH(separator); + PUSH(CRLF); + } + + status = m_children[i]->Write(); + if (NS_FAILED(status)) + goto FAIL; + + // XXX SKIP_EMPTY_PART (= 1966) is not a valid nsresult + if (status == static_cast<nsresult>(SKIP_EMPTY_PART)) + writeSeparator = false; + else + writeSeparator = true; + } + + PUSH(CRLF); + PUSH("--"); + PUSH(separator); + PUSH("--"); + PUSH(CRLF); + } + else if (needToWriteCRLFAfterEncodedBody) + PUSH(CRLF); + +FAIL: + PR_FREEIF(separator); + return status; +} + |