summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/mime/src/nsStreamConverter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mailnews/mime/src/nsStreamConverter.cpp')
-rw-r--r--comm/mailnews/mime/src/nsStreamConverter.cpp981
1 files changed, 981 insertions, 0 deletions
diff --git a/comm/mailnews/mime/src/nsStreamConverter.cpp b/comm/mailnews/mime/src/nsStreamConverter.cpp
new file mode 100644
index 0000000000..7311295402
--- /dev/null
+++ b/comm/mailnews/mime/src/nsStreamConverter.cpp
@@ -0,0 +1,981 @@
+/* -*- 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 "nsCOMPtr.h"
+#include <stdio.h>
+#include "mimecom.h"
+#include "modmimee.h"
+#include "nscore.h"
+#include "nsStreamConverter.h"
+#include "prmem.h"
+#include "prprf.h"
+#include "prlog.h"
+#include "plstr.h"
+#include "mimemoz2.h"
+#include "nsMimeTypes.h"
+#include "nsString.h"
+#include "nsUnicharUtils.h"
+#include "nsMemory.h"
+#include "nsIPipe.h"
+#include "nsMimeStringResources.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "nsNetUtil.h"
+#include "nsIMsgQuote.h"
+#include "nsNetUtil.h"
+#include "mozITXTToHTMLConv.h"
+#include "nsIMsgMailNewsUrl.h"
+#include "nsINntpUrl.h"
+#include "nsIMsgWindow.h"
+#include "nsICategoryManager.h"
+#include "nsMsgUtils.h"
+#include "mozilla/ArrayUtils.h"
+
+#define PREF_MAIL_DISPLAY_GLYPH "mail.display_glyph"
+#define PREF_MAIL_DISPLAY_STRUCT "mail.display_struct"
+
+////////////////////////////////////////////////////////////////
+// Bridge routines for new stream converter XP-COM interface
+////////////////////////////////////////////////////////////////
+
+extern "C" void* mime_bridge_create_draft_stream(
+ nsIMimeEmitter* newEmitter, nsStreamConverter* newPluginObj2, nsIURI* uri,
+ nsMimeOutputType format_out);
+
+extern "C" void* bridge_create_stream(nsIMimeEmitter* newEmitter,
+ nsStreamConverter* newPluginObj2,
+ nsIURI* uri, nsMimeOutputType format_out,
+ uint32_t whattodo, nsIChannel* aChannel) {
+ if ((format_out == nsMimeOutput::nsMimeMessageDraftOrTemplate) ||
+ (format_out == nsMimeOutput::nsMimeMessageEditorTemplate))
+ return mime_bridge_create_draft_stream(newEmitter, newPluginObj2, uri,
+ format_out);
+ else
+ return mime_bridge_create_display_stream(newEmitter, newPluginObj2, uri,
+ format_out, whattodo, aChannel);
+}
+
+void bridge_destroy_stream(void* newStream) {
+ nsMIMESession* stream = (nsMIMESession*)newStream;
+ if (!stream) return;
+
+ PR_FREEIF(stream);
+}
+
+void bridge_set_output_type(void* bridgeStream, nsMimeOutputType aType) {
+ nsMIMESession* session = (nsMIMESession*)bridgeStream;
+
+ if (session) {
+ // BAD ASSUMPTION!!!! NEED TO CHECK aType
+ mime_stream_data* msd = (mime_stream_data*)session->data_object;
+ if (msd) msd->format_out = aType; // output format type
+ }
+}
+
+nsresult bridge_new_new_uri(void* bridgeStream, nsIURI* aURI,
+ int32_t aOutputType) {
+ nsMIMESession* session = (nsMIMESession*)bridgeStream;
+ const char** fixup_pointer = nullptr;
+
+ if (session) {
+ if (session->data_object) {
+ bool* override_charset = nullptr;
+ char** default_charset = nullptr;
+ char** url_name = nullptr;
+
+ if ((aOutputType == nsMimeOutput::nsMimeMessageDraftOrTemplate) ||
+ (aOutputType == nsMimeOutput::nsMimeMessageEditorTemplate)) {
+ mime_draft_data* mdd = (mime_draft_data*)session->data_object;
+ if (mdd->options) {
+ default_charset = &(mdd->options->default_charset);
+ override_charset = &(mdd->options->override_charset);
+ url_name = &(mdd->url_name);
+ }
+ } else {
+ mime_stream_data* msd = (mime_stream_data*)session->data_object;
+
+ if (msd->options) {
+ default_charset = &(msd->options->default_charset);
+ override_charset = &(msd->options->override_charset);
+ url_name = &(msd->url_name);
+ fixup_pointer = &(msd->options->url);
+ }
+ }
+
+ if (default_charset && override_charset && url_name) {
+ // Check whether we need to auto-detect the charset.
+ nsCOMPtr<nsIMsgI18NUrl> i18nUrl(do_QueryInterface(aURI));
+ if (i18nUrl) {
+ bool autodetectCharset = false;
+ nsresult rv = i18nUrl->GetAutodetectCharset(&autodetectCharset);
+ if (NS_SUCCEEDED(rv) && autodetectCharset) {
+ *override_charset = true;
+ *default_charset = nullptr;
+ } else {
+ *override_charset = false;
+ // Special treatment for news: URLs. Get the server default charset.
+ nsCOMPtr<nsINntpUrl> nntpURL(do_QueryInterface(aURI));
+ if (nntpURL) {
+ nsCString charset;
+ rv = nntpURL->GetCharset(charset);
+ if (NS_SUCCEEDED(rv)) {
+ *default_charset = ToNewCString(charset);
+ } else {
+ *default_charset = strdup("UTF-8");
+ }
+ } else {
+ *default_charset = strdup("UTF-8");
+ }
+ }
+ }
+ nsAutoCString urlString;
+ if (NS_SUCCEEDED(aURI->GetSpec(urlString))) {
+ if (!urlString.IsEmpty()) {
+ free(*url_name);
+ *url_name = ToNewCString(urlString);
+ if (!(*url_name)) return NS_ERROR_OUT_OF_MEMORY;
+
+ // rhp: Ugh, this is ugly...but it works.
+ if (fixup_pointer) *fixup_pointer = (const char*)*url_name;
+ }
+ }
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+static int mime_headers_callback(void* closure, MimeHeaders* headers) {
+ // We get away with this because this doesn't get called on draft operations.
+ mime_stream_data* msd = (mime_stream_data*)closure;
+
+ NS_ASSERTION(msd && headers, "null mime stream data or headers");
+ if (!msd || !headers) return 0;
+
+ NS_ASSERTION(!msd->headers, "non-null mime stream data headers");
+ msd->headers = MimeHeaders_copy(headers);
+ return 0;
+}
+
+nsresult bridge_set_mime_stream_converter_listener(
+ void* bridgeStream, nsIMimeStreamConverterListener* listener,
+ nsMimeOutputType aOutputType) {
+ nsMIMESession* session = (nsMIMESession*)bridgeStream;
+
+ if ((session) && (session->data_object)) {
+ if ((aOutputType == nsMimeOutput::nsMimeMessageDraftOrTemplate) ||
+ (aOutputType == nsMimeOutput::nsMimeMessageEditorTemplate)) {
+ mime_draft_data* mdd = (mime_draft_data*)session->data_object;
+ if (mdd->options) {
+ if (listener) {
+ mdd->options->caller_need_root_headers = true;
+ mdd->options->decompose_headers_info_fn = mime_headers_callback;
+ } else {
+ mdd->options->caller_need_root_headers = false;
+ mdd->options->decompose_headers_info_fn = nullptr;
+ }
+ }
+ } else {
+ mime_stream_data* msd = (mime_stream_data*)session->data_object;
+
+ if (msd->options) {
+ if (listener) {
+ msd->options->caller_need_root_headers = true;
+ msd->options->decompose_headers_info_fn = mime_headers_callback;
+ } else {
+ msd->options->caller_need_root_headers = false;
+ msd->options->decompose_headers_info_fn = nullptr;
+ }
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+// find a query element in a url and return a pointer to its data
+// (query must be in the form "query=")
+static const char* FindQueryElementData(const char* aUrl, const char* aQuery) {
+ if (aUrl && aQuery) {
+ size_t queryLen = 0; // we don't call strlen until we need to
+ aUrl = PL_strcasestr(aUrl, aQuery);
+ while (aUrl) {
+ if (!queryLen) queryLen = strlen(aQuery);
+ if (*(aUrl - 1) == '&' || *(aUrl - 1) == '?') return aUrl + queryLen;
+ aUrl = PL_strcasestr(aUrl + queryLen, aQuery);
+ }
+ }
+ return nullptr;
+}
+
+// case-sensitive test for string prefixing. If |string| is prefixed
+// by |prefix| then a pointer to the next character in |string| following
+// the prefix is returned. If it is not a prefix then |nullptr| is returned.
+static const char* SkipPrefix(const char* aString, const char* aPrefix) {
+ while (*aPrefix)
+ if (*aPrefix++ != *aString++) return nullptr;
+ return aString;
+}
+
+//
+// Utility routines needed by this interface
+//
+nsresult nsStreamConverter::DetermineOutputFormat(const char* aUrl,
+ nsMimeOutputType* aNewType) {
+ // sanity checking
+ NS_ENSURE_ARG_POINTER(aNewType);
+ if (!aUrl || !*aUrl) {
+ // default to html for the entire document
+ *aNewType = nsMimeOutput::nsMimeMessageQuoting;
+ mOutputFormat = "text/html";
+ return NS_OK;
+ }
+
+ // shorten the url that we test for the query strings by skipping directly
+ // to the part where the query strings begin.
+ const char* queryPart = PL_strchr(aUrl, '?');
+
+ // First, did someone pass in a desired output format. They will be able to
+ // pass in any content type (i.e. image/gif, text/html, etc...but the "/" will
+ // have to be represented via the "%2F" value
+ const char* format = FindQueryElementData(queryPart, "outformat=");
+ if (format) {
+ // NOTE: I've done a file contents search of every file (*.*) in the mozilla
+ // directory tree and there is not a single location where the string
+ // "outformat" is added to any URL. It appears that this code has been
+ // orphaned off by a change elsewhere and is no longer required. It will be
+ // removed in the future unless someone complains.
+ MOZ_ASSERT(false, "Is this code actually being used?");
+
+ while (*format == ' ') ++format;
+
+ if (*format) {
+ mOverrideFormat = "raw";
+
+ // set mOutputFormat to the supplied format, ensure that we replace any
+ // %2F strings with the slash character
+ const char* nextField = PL_strpbrk(format, "&; ");
+ mOutputFormat.Assign(format, nextField ? nextField - format : -1);
+ mOutputFormat.ReplaceSubstring("%2F", "/");
+ mOutputFormat.ReplaceSubstring("%2f", "/");
+
+ // Don't muck with this data!
+ *aNewType = nsMimeOutput::nsMimeMessageRaw;
+ return NS_OK;
+ }
+ }
+
+ // is this is a part that should just come out raw
+ const char* part = FindQueryElementData(queryPart, "part=");
+ if (part && !mToType.EqualsLiteral("application/xhtml+xml")) {
+ // default for parts
+ mOutputFormat = "raw";
+ *aNewType = nsMimeOutput::nsMimeMessageRaw;
+
+ // if we are being asked to fetch a part....it should have a
+ // content type appended to it...if it does, we want to remember
+ // that as mOutputFormat
+ const char* typeField = FindQueryElementData(queryPart, "type=");
+ if (typeField && !strncmp(typeField, "application/x-message-display",
+ sizeof("application/x-message-display") - 1)) {
+ const char* secondTypeField = FindQueryElementData(typeField, "type=");
+ if (secondTypeField) typeField = secondTypeField;
+ }
+ if (typeField) {
+ // store the real content type...mOutputFormat gets deleted later on...
+ // and make sure we only get our own value.
+ char* nextField = PL_strchr(typeField, '&');
+ mRealContentType.Assign(typeField,
+ nextField ? nextField - typeField : -1);
+ if (mRealContentType.EqualsLiteral("message/rfc822")) {
+ mRealContentType = "application/x-message-display";
+ mOutputFormat = "text/html";
+ *aNewType = nsMimeOutput::nsMimeMessageBodyDisplay;
+ } else if (mRealContentType.EqualsLiteral(
+ "application/x-message-display")) {
+ mRealContentType = "";
+ mOutputFormat = "text/html";
+ *aNewType = nsMimeOutput::nsMimeMessageBodyDisplay;
+ }
+ }
+
+ return NS_OK;
+ }
+
+ const char* emitter = FindQueryElementData(queryPart, "emitter=");
+ if (emitter) {
+ const char* remainder = SkipPrefix(emitter, "js");
+ if (remainder && (!*remainder || *remainder == '&'))
+ mOverrideFormat = "application/x-js-mime-message";
+ }
+
+ // if using the header query
+ const char* header = FindQueryElementData(queryPart, "header=");
+ if (header) {
+ struct HeaderType {
+ const char* headerType;
+ const char* outputFormat;
+ nsMimeOutputType mimeOutputType;
+ };
+
+ // place most commonly used options at the top
+ static const struct HeaderType rgTypes[] = {
+ {"filter", "text/html", nsMimeOutput::nsMimeMessageFilterSniffer},
+ {"quotebody", "text/html", nsMimeOutput::nsMimeMessageBodyQuoting},
+ {"print", "text/html", nsMimeOutput::nsMimeMessagePrintOutput},
+ {"only", "text/xml", nsMimeOutput::nsMimeMessageHeaderDisplay},
+ {"none", "text/html", nsMimeOutput::nsMimeMessageBodyDisplay},
+ {"quote", "text/html", nsMimeOutput::nsMimeMessageQuoting},
+ {"saveas", "text/html", nsMimeOutput::nsMimeMessageSaveAs},
+ {"src", "text/plain", nsMimeOutput::nsMimeMessageSource},
+ {"attach", "raw", nsMimeOutput::nsMimeMessageAttach}};
+
+ // find the requested header in table, ensure that we don't match on a
+ // prefix by checking that the following character is either null or the
+ // next query element
+ const char* remainder;
+ for (uint32_t n = 0; n < MOZ_ARRAY_LENGTH(rgTypes); ++n) {
+ remainder = SkipPrefix(header, rgTypes[n].headerType);
+ if (remainder && (*remainder == '\0' || *remainder == '&')) {
+ mOutputFormat = rgTypes[n].outputFormat;
+ *aNewType = rgTypes[n].mimeOutputType;
+ return NS_OK;
+ }
+ }
+ }
+
+ // default to html for just the body
+ mOutputFormat = "text/html";
+ *aNewType = nsMimeOutput::nsMimeMessageBodyDisplay;
+
+ return NS_OK;
+}
+
+nsresult nsStreamConverter::InternalCleanup(void) {
+ if (mBridgeStream) {
+ bridge_destroy_stream(mBridgeStream);
+ mBridgeStream = nullptr;
+ }
+
+ return NS_OK;
+}
+
+/*
+ * Inherited methods for nsMimeConverter
+ */
+nsStreamConverter::nsStreamConverter() {
+ // Init member variables...
+ mWrapperOutput = false;
+ mBridgeStream = nullptr;
+ mOutputFormat = "text/html";
+ mAlreadyKnowOutputType = false;
+ mForwardInline = false;
+ mForwardInlineFilter = false;
+ mOverrideComposeFormat = false;
+
+ mPendingRequest = nullptr;
+}
+
+nsStreamConverter::~nsStreamConverter() { InternalCleanup(); }
+
+NS_IMPL_ISUPPORTS(nsStreamConverter, nsIStreamListener, nsIRequestObserver,
+ nsIStreamConverter, nsIMimeStreamConverter)
+
+///////////////////////////////////////////////////////////////
+// nsStreamConverter definitions....
+///////////////////////////////////////////////////////////////
+
+NS_IMETHODIMP nsStreamConverter::Init(nsIURI* aURI,
+ nsIStreamListener* aOutListener,
+ nsIChannel* aChannel) {
+ NS_ENSURE_ARG_POINTER(aURI);
+
+ nsresult rv = NS_OK;
+ mOutListener = aOutListener;
+ mOutgoingChannel = aChannel;
+
+ // mscott --> we need to look at the url and figure out what the correct
+ // output type is...
+ nsMimeOutputType newType = mOutputType;
+ if (!mAlreadyKnowOutputType) {
+ nsAutoCString urlSpec;
+ rv = aURI->GetSpecIgnoringRef(urlSpec);
+ DetermineOutputFormat(urlSpec.get(), &newType);
+ mAlreadyKnowOutputType = true;
+ mOutputType = newType;
+ }
+
+ switch (newType) {
+ case nsMimeOutput::nsMimeMessageSplitDisplay: // the wrapper HTML output to
+ // produce the split
+ // header/body display
+ mWrapperOutput = true;
+ mOutputFormat = "text/html";
+ break;
+ case nsMimeOutput::nsMimeMessageHeaderDisplay: // the split header/body
+ // display
+ mOutputFormat = "text/xml";
+ break;
+ case nsMimeOutput::nsMimeMessageBodyDisplay: // the split header/body
+ // display
+ mOutputFormat = "text/html";
+ break;
+
+ case nsMimeOutput::nsMimeMessageQuoting: // all HTML quoted output
+ case nsMimeOutput::nsMimeMessageSaveAs: // Save as operation
+ case nsMimeOutput::nsMimeMessageBodyQuoting: // only HTML body quoted
+ // output
+ case nsMimeOutput::nsMimeMessagePrintOutput: // all Printing output
+ mOutputFormat = "text/html";
+ break;
+
+ case nsMimeOutput::nsMimeMessageAttach:
+ case nsMimeOutput::nsMimeMessageDecrypt:
+ case nsMimeOutput::nsMimeMessageRaw: // the raw RFC822 data and attachments
+ mOutputFormat = "raw";
+ break;
+
+ case nsMimeOutput::nsMimeMessageSource: // the raw RFC822 data (view
+ // source) and attachments
+ mOutputFormat = "text/plain";
+ mOverrideFormat = "raw";
+ break;
+
+ case nsMimeOutput::nsMimeMessageDraftOrTemplate: // Loading drafts &
+ // templates
+ mOutputFormat = "message/draft";
+ break;
+
+ case nsMimeOutput::nsMimeMessageEditorTemplate: // Loading templates into
+ // editor
+ mOutputFormat = "text/html";
+ break;
+
+ case nsMimeOutput::nsMimeMessageFilterSniffer: // output all displayable
+ // part as raw
+ mOutputFormat = "text/html";
+ break;
+
+ default:
+ NS_ERROR("this means I made a mistake in my assumptions");
+ }
+
+ // the following output channel stream is used to fake the content type for
+ // people who later call into us..
+ nsCString contentTypeToUse;
+ GetContentType(getter_Copies(contentTypeToUse));
+ // mscott --> my theory is that we don't need this fake outgoing channel.
+ // Let's use the original channel and just set our content type ontop of the
+ // original channel...
+
+ aChannel->SetContentType(contentTypeToUse);
+
+ // rv = NS_NewInputStreamChannel(getter_AddRefs(mOutgoingChannel), aURI,
+ // nullptr, contentTypeToUse, -1); if (NS_FAILED(rv))
+ // return rv;
+
+ // Set system principal for this document, which will be dynamically generated
+
+ // We will first find an appropriate emitter in the repository that supports
+ // the requested output format...note, the special exceptions are
+ // nsMimeMessageDraftOrTemplate or nsMimeMessageEditorTemplate where we don't
+ // need any emitters
+ //
+
+ if ((newType != nsMimeOutput::nsMimeMessageDraftOrTemplate) &&
+ (newType != nsMimeOutput::nsMimeMessageEditorTemplate)) {
+ nsAutoCString categoryName("@mozilla.org/messenger/mimeemitter;1?type=");
+ if (!mOverrideFormat.IsEmpty())
+ categoryName += mOverrideFormat;
+ else
+ categoryName += mOutputFormat;
+
+ nsCOMPtr<nsICategoryManager> catman =
+ do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv)) {
+ nsCString contractID;
+ catman->GetCategoryEntry("mime-emitter", categoryName, contractID);
+ if (!contractID.IsEmpty()) categoryName = contractID;
+ }
+
+ mEmitter = do_CreateInstance(categoryName.get(), &rv);
+
+ if ((NS_FAILED(rv)) || (!mEmitter)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ // initialize our emitter
+ if (mEmitter) {
+ // Now we want to create a pipe which we'll use for converting the data.
+ nsCOMPtr<nsIPipe> pipe = do_CreateInstance("@mozilla.org/pipe;1");
+ rv = pipe->Init(true, true, 4096, 8);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // These always succeed because the pipe is initialized above.
+ MOZ_ALWAYS_SUCCEEDS(pipe->GetInputStream(getter_AddRefs(mInputStream)));
+ MOZ_ALWAYS_SUCCEEDS(pipe->GetOutputStream(getter_AddRefs(mOutputStream)));
+
+ mEmitter->Initialize(aURI, aChannel, newType);
+ mEmitter->SetPipe(mInputStream, mOutputStream);
+ mEmitter->SetOutputListener(aOutListener);
+ }
+
+ uint32_t whattodo = mozITXTToHTMLConv::kURLs;
+ bool enable_emoticons = true;
+ bool enable_structs = true;
+
+ nsCOMPtr<nsIPrefBranch> pPrefBranch(
+ do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ if (pPrefBranch) {
+ rv = pPrefBranch->GetBoolPref(PREF_MAIL_DISPLAY_GLYPH, &enable_emoticons);
+ if (NS_FAILED(rv) || enable_emoticons) {
+ whattodo = whattodo | mozITXTToHTMLConv::kGlyphSubstitution;
+ }
+ rv = pPrefBranch->GetBoolPref(PREF_MAIL_DISPLAY_STRUCT, &enable_structs);
+ if (NS_FAILED(rv) || enable_structs) {
+ whattodo = whattodo | mozITXTToHTMLConv::kStructPhrase;
+ }
+ }
+
+ if (mOutputType == nsMimeOutput::nsMimeMessageSource)
+ return NS_OK;
+ else {
+ mBridgeStream =
+ bridge_create_stream(mEmitter, this, aURI, newType, whattodo, aChannel);
+ if (!mBridgeStream)
+ return NS_ERROR_OUT_OF_MEMORY;
+ else {
+ SetStreamURI(aURI);
+
+ // Do we need to setup an Mime Stream Converter Listener?
+ if (mMimeStreamConverterListener)
+ bridge_set_mime_stream_converter_listener((nsMIMESession*)mBridgeStream,
+ mMimeStreamConverterListener,
+ mOutputType);
+
+ return NS_OK;
+ }
+ }
+}
+
+NS_IMETHODIMP nsStreamConverter::GetContentType(char** aOutputContentType) {
+ if (!aOutputContentType) return NS_ERROR_NULL_POINTER;
+
+ // since this method passes a string through an IDL file we need to use
+ // nsMemory to allocate it and not strdup!
+ // (1) check to see if we have a real content type...use it first...
+ if (!mRealContentType.IsEmpty())
+ *aOutputContentType = ToNewCString(mRealContentType);
+ else if (mOutputFormat.EqualsLiteral("raw"))
+ *aOutputContentType =
+ (char*)moz_xmemdup(UNKNOWN_CONTENT_TYPE, sizeof(UNKNOWN_CONTENT_TYPE));
+ else
+ *aOutputContentType = ToNewCString(mOutputFormat);
+ return NS_OK;
+}
+
+//
+// This is the type of output operation that is being requested by libmime. The
+// types of output are specified by nsIMimeOutputType enum
+//
+nsresult nsStreamConverter::SetMimeOutputType(nsMimeOutputType aType) {
+ mAlreadyKnowOutputType = true;
+ mOutputType = aType;
+ if (mBridgeStream) bridge_set_output_type(mBridgeStream, aType);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsStreamConverter::GetMimeOutputType(
+ nsMimeOutputType* aOutFormat) {
+ nsresult rv = NS_OK;
+ if (aOutFormat)
+ *aOutFormat = mOutputType;
+ else
+ rv = NS_ERROR_NULL_POINTER;
+
+ return rv;
+}
+
+//
+// This is needed by libmime for MHTML link processing...this is the URI
+// associated with this input stream
+//
+nsresult nsStreamConverter::SetStreamURI(nsIURI* aURI) {
+ mURI = aURI;
+ if (mBridgeStream)
+ return bridge_new_new_uri((nsMIMESession*)mBridgeStream, aURI, mOutputType);
+ else
+ return NS_OK;
+}
+
+nsresult nsStreamConverter::SetMimeHeadersListener(
+ nsIMimeStreamConverterListener* listener, nsMimeOutputType aType) {
+ mMimeStreamConverterListener = listener;
+ bridge_set_mime_stream_converter_listener((nsMIMESession*)mBridgeStream,
+ listener, aType);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStreamConverter::SetForwardInline(bool aForwardInline) {
+ mForwardInline = aForwardInline;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStreamConverter::GetForwardToAddress(nsAString& aAddress) {
+ aAddress = mForwardToAddress;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStreamConverter::SetForwardToAddress(const nsAString& aAddress) {
+ mForwardToAddress = aAddress;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStreamConverter::GetOverrideComposeFormat(bool* aResult) {
+ if (!aResult) return NS_ERROR_NULL_POINTER;
+ *aResult = mOverrideComposeFormat;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStreamConverter::SetOverrideComposeFormat(bool aOverrideComposeFormat) {
+ mOverrideComposeFormat = aOverrideComposeFormat;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStreamConverter::GetForwardInline(bool* aResult) {
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = mForwardInline;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStreamConverter::GetForwardInlineFilter(bool* aResult) {
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = mForwardInlineFilter;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStreamConverter::SetForwardInlineFilter(bool aForwardInlineFilter) {
+ mForwardInlineFilter = aForwardInlineFilter;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStreamConverter::GetIdentity(nsIMsgIdentity** aIdentity) {
+ if (!aIdentity) return NS_ERROR_NULL_POINTER;
+ // We don't have an identity for the local folders account,
+ // we will return null but it is not an error!
+ NS_IF_ADDREF(*aIdentity = mIdentity);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStreamConverter::SetIdentity(nsIMsgIdentity* aIdentity) {
+ mIdentity = aIdentity;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStreamConverter::SetOriginalMsgURI(const nsACString& originalMsgURI) {
+ mOriginalMsgURI = originalMsgURI;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStreamConverter::GetOriginalMsgURI(nsACString& result) {
+ result = mOriginalMsgURI;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStreamConverter::SetOrigMsgHdr(nsIMsgDBHdr* aMsgHdr) {
+ mOrigMsgHdr = aMsgHdr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStreamConverter::GetOrigMsgHdr(nsIMsgDBHdr** aMsgHdr) {
+ if (!aMsgHdr) return NS_ERROR_NULL_POINTER;
+ NS_IF_ADDREF(*aMsgHdr = mOrigMsgHdr);
+ return NS_OK;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Methods for nsIStreamListener...
+/////////////////////////////////////////////////////////////////////////////
+//
+// Notify the client that data is available in the input stream. This
+// method is called whenever data is written into the input stream by the
+// networking library...
+//
+nsresult nsStreamConverter::OnDataAvailable(nsIRequest* request,
+ nsIInputStream* aIStream,
+ uint64_t sourceOffset,
+ uint32_t aLength) {
+ nsresult rc = NS_OK; // should this be an error instead?
+ uint32_t written;
+
+ // If this is the first time through and we are supposed to be
+ // outputting the wrapper two pane URL, then do it now.
+ if (mWrapperOutput) {
+ char outBuf[1024];
+ const char output[] =
+ "\
+<HTML>\
+<FRAMESET ROWS=\"30%%,70%%\">\
+<FRAME NAME=messageHeader SRC=\"%s?header=only\">\
+<FRAME NAME=messageBody SRC=\"%s?header=none\">\
+</FRAMESET>\
+</HTML>";
+
+ nsAutoCString url;
+ if (NS_FAILED(mURI->GetSpec(url))) return NS_ERROR_FAILURE;
+
+ PR_snprintf(outBuf, sizeof(outBuf), output, url.get(), url.get());
+
+ if (mEmitter) mEmitter->Write(nsDependentCString(outBuf), &written);
+
+ // rhp: will this stop the stream???? Not sure.
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIInputStream> stream = aIStream;
+ NS_ENSURE_TRUE(stream, NS_ERROR_NULL_POINTER);
+ char* buf = (char*)PR_Malloc(aLength);
+ if (!buf) return NS_ERROR_OUT_OF_MEMORY; /* we couldn't allocate the object */
+
+ uint32_t readLen;
+ stream->Read(buf, aLength, &readLen);
+
+ // We need to filter out any null characters else we will have a lot of
+ // trouble as we use c string everywhere in mime
+ char* readPtr;
+ char* endPtr = buf + readLen;
+
+ // First let see if the stream contains null characters
+ for (readPtr = buf; readPtr < endPtr && *readPtr; readPtr++)
+ ;
+
+ // Did we find a null character? Then, we need to cleanup the stream
+ if (readPtr < endPtr) {
+ char* writePtr = readPtr;
+ for (readPtr++; readPtr < endPtr; readPtr++) {
+ if (!*readPtr) continue;
+
+ *writePtr = *readPtr;
+ writePtr++;
+ }
+ readLen = writePtr - buf;
+ }
+
+ if (mOutputType == nsMimeOutput::nsMimeMessageSource) {
+ rc = NS_OK;
+ if (mEmitter) {
+ rc = mEmitter->Write(Substring(buf, buf + readLen), &written);
+ }
+ } else if (mBridgeStream) {
+ nsMIMESession* tSession = (nsMIMESession*)mBridgeStream;
+ // XXX Casting int to nsresult
+ rc = static_cast<nsresult>(
+ tSession->put_block((nsMIMESession*)mBridgeStream, buf, readLen));
+ }
+
+ PR_FREEIF(buf);
+ return rc;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Methods for nsIRequestObserver
+/////////////////////////////////////////////////////////////////////////////
+//
+// Notify the observer that the URL has started to load. This method is
+// called only once, at the beginning of a URL load.
+//
+nsresult nsStreamConverter::OnStartRequest(nsIRequest* request) {
+#ifdef DEBUG_rhp
+ printf("nsStreamConverter::OnStartRequest()\n");
+#endif
+
+#ifdef DEBUG_mscott
+ mConvertContentTime = PR_IntervalNow();
+#endif
+
+ // here's a little bit of hackery....
+ // since the mime converter is now between the channel
+ // and the
+ if (request) {
+ nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
+ if (channel) {
+ nsCString contentType;
+ GetContentType(getter_Copies(contentType));
+
+ channel->SetContentType(contentType);
+ }
+ }
+
+ // forward the start request to any listeners
+ if (mOutListener) {
+ if (mOutputType == nsMimeOutput::nsMimeMessageRaw) {
+ // we need to delay the on start request until we have figure out the real
+ // content type
+ mPendingRequest = request;
+ } else
+ mOutListener->OnStartRequest(request);
+ }
+
+ return NS_OK;
+}
+
+//
+// Notify the observer that the URL has finished loading. This method is
+// called once when the networking library has finished processing the
+//
+nsresult nsStreamConverter::OnStopRequest(nsIRequest* request,
+ nsresult status) {
+ // Make sure we fire any pending OnStartRequest before we do OnStop.
+ FirePendingStartRequest();
+
+ //
+ // Now complete the stream!
+ //
+ if (mBridgeStream) {
+ nsMIMESession* tSession = (nsMIMESession*)mBridgeStream;
+
+ if (mMimeStreamConverterListener) {
+ MimeHeaders** workHeaders = nullptr;
+
+ if ((mOutputType == nsMimeOutput::nsMimeMessageDraftOrTemplate) ||
+ (mOutputType == nsMimeOutput::nsMimeMessageEditorTemplate)) {
+ mime_draft_data* mdd = (mime_draft_data*)tSession->data_object;
+ if (mdd) workHeaders = &(mdd->headers);
+ } else {
+ mime_stream_data* msd = (mime_stream_data*)tSession->data_object;
+ if (msd) workHeaders = &(msd->headers);
+ }
+
+ if (workHeaders) {
+ nsresult rv;
+ nsCOMPtr<nsIMimeHeaders> mimeHeaders =
+ do_CreateInstance(NS_IMIMEHEADERS_CONTRACTID, &rv);
+
+ if (NS_SUCCEEDED(rv)) {
+ if (*workHeaders)
+ mimeHeaders->Initialize(Substring((*workHeaders)->all_headers,
+ (*workHeaders)->all_headers_fp));
+ mMimeStreamConverterListener->OnHeadersReady(mimeHeaders);
+ } else
+ mMimeStreamConverterListener->OnHeadersReady(nullptr);
+ }
+
+ mMimeStreamConverterListener = nullptr; // release our reference
+ }
+
+ tSession->complete((nsMIMESession*)mBridgeStream);
+ }
+
+ //
+ // Now complete the emitter and do necessary cleanup!
+ //
+ if (mEmitter) {
+ mEmitter->Complete();
+ }
+
+ // First close the output stream...
+ if (mOutputStream) mOutputStream->Close();
+
+ if (mOutgoingChannel) {
+ nsCOMPtr<nsILoadGroup> loadGroup;
+ mOutgoingChannel->GetLoadGroup(getter_AddRefs(loadGroup));
+ if (loadGroup) loadGroup->RemoveRequest(mOutgoingChannel, nullptr, status);
+ }
+
+ // Make sure to do necessary cleanup!
+ InternalCleanup();
+
+ // forward on top request to any listeners
+ if (mOutListener) mOutListener->OnStopRequest(request, status);
+
+ mAlreadyKnowOutputType = false;
+
+ // since we are done converting data, lets close all the objects we own...
+ // this helps us fix some circular ref counting problems we are running
+ // into...
+ Close();
+
+ // Time to return...
+ return NS_OK;
+}
+
+nsresult nsStreamConverter::Close() {
+ mOutgoingChannel = nullptr;
+ mEmitter = nullptr;
+ mOutListener = nullptr;
+ return NS_OK;
+}
+
+// nsIStreamConverter implementation
+
+// No synchronous conversion at this time.
+NS_IMETHODIMP nsStreamConverter::Convert(nsIInputStream* aFromStream,
+ const char* aFromType,
+ const char* aToType,
+ nsISupports* aCtxt,
+ nsIInputStream** _retval) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// Stream converter service calls this to initialize the actual stream converter
+// (us).
+NS_IMETHODIMP nsStreamConverter::AsyncConvertData(const char* aFromType,
+ const char* aToType,
+ nsIStreamListener* aListener,
+ nsISupports* aCtxt) {
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIMsgQuote> aMsgQuote = do_QueryInterface(aCtxt, &rv);
+ nsCOMPtr<nsIChannel> aChannel;
+
+ if (aMsgQuote) {
+ nsCOMPtr<nsIMimeStreamConverterListener> quoteListener;
+ rv = aMsgQuote->GetQuoteListener(getter_AddRefs(quoteListener));
+ if (quoteListener)
+ SetMimeHeadersListener(quoteListener, nsMimeOutput::nsMimeMessageQuoting);
+ rv = aMsgQuote->GetQuoteChannel(getter_AddRefs(aChannel));
+ } else {
+ aChannel = do_QueryInterface(aCtxt, &rv);
+ }
+
+ mFromType = aFromType;
+ mToType = aToType;
+
+ NS_ASSERTION(aChannel && NS_SUCCEEDED(rv),
+ "mailnews mime converter has to have the channel passed in...");
+ if (NS_FAILED(rv)) return rv;
+
+ nsCOMPtr<nsIURI> aUri;
+ aChannel->GetURI(getter_AddRefs(aUri));
+ return Init(aUri, aListener, aChannel);
+}
+
+NS_IMETHODIMP nsStreamConverter::FirePendingStartRequest() {
+ if (mPendingRequest && mOutListener) {
+ mOutListener->OnStartRequest(mPendingRequest);
+ mPendingRequest = nullptr;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsStreamConverter::GetConvertedType(const nsACString& aFromType,
+ nsIChannel* aChannel,
+ nsACString& aToType) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}