summaryrefslogtreecommitdiffstats
path: root/mailnews/mime/src/nsStreamConverter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mailnews/mime/src/nsStreamConverter.cpp')
-rw-r--r--mailnews/mime/src/nsStreamConverter.cpp1157
1 files changed, 1157 insertions, 0 deletions
diff --git a/mailnews/mime/src/nsStreamConverter.cpp b/mailnews/mime/src/nsStreamConverter.cpp
new file mode 100644
index 000000000..0d1781498
--- /dev/null
+++ b/mailnews/mime/src/nsStreamConverter.cpp
@@ -0,0 +1,1157 @@
+/* -*- 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 "nsIComponentManager.h"
+#include "nsIURL.h"
+#include "nsStringGlue.h"
+#include "nsUnicharUtils.h"
+#include "nsIServiceManager.h"
+#include "nsMemory.h"
+#include "nsIPipe.h"
+#include "nsMimeStringResources.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "nsNetUtil.h"
+#include "nsIMsgQuote.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsNetUtil.h"
+#include "mozITXTToHTMLConv.h"
+#include "nsIMsgMailNewsUrl.h"
+#include "nsIMsgWindow.h"
+#include "nsICategoryManager.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIAsyncInputStream.h"
+#include "nsIAsyncOutputStream.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) )
+ {
+ //
+ // set the default charset to be the folder charset if we have one associated with
+ // this url...
+ nsCOMPtr<nsIMsgI18NUrl> i18nUrl (do_QueryInterface(aURI));
+ if (i18nUrl)
+ {
+ nsCString charset;
+
+ // check to see if we have a charset override...and if we do, set that field appropriately too...
+ nsresult rv = i18nUrl->GetCharsetOverRide(getter_Copies(charset));
+ if (NS_SUCCEEDED(rv) && !charset.IsEmpty() ) {
+ *override_charset = true;
+ *default_charset = ToNewCString(charset);
+ }
+ else
+ {
+ i18nUrl->GetFolderCharset(getter_Copies(charset));
+ if (!charset.IsEmpty())
+ *default_charset = ToNewCString(charset);
+ }
+
+ // if there is no manual override and a folder charset exists
+ // then check if we have a folder level override
+ if (!(*override_charset) && *default_charset && **default_charset)
+ {
+ bool folderCharsetOverride;
+ rv = i18nUrl->GetFolderCharsetOverride(&folderCharsetOverride);
+ if (NS_SUCCEEDED(rv) && folderCharsetOverride)
+ *override_charset = true;
+
+ // notify the default to msgWindow (for the menu check mark)
+ // do not set the default in case of nsMimeMessageDraftOrTemplate
+ // or nsMimeMessageEditorTemplate because it is already set
+ // when the message is displayed and doing it again may overwrite
+ // the correct MIME charset parsed from the message header
+ if (aOutputType != nsMimeOutput::nsMimeMessageDraftOrTemplate &&
+ aOutputType != nsMimeOutput::nsMimeMessageEditorTemplate)
+ {
+ nsCOMPtr<nsIMsgMailNewsUrl> msgurl (do_QueryInterface(aURI));
+ if (msgurl)
+ {
+ nsCOMPtr<nsIMsgWindow> msgWindow;
+ msgurl->GetMsgWindow(getter_AddRefs(msgWindow));
+ if (msgWindow)
+ {
+ msgWindow->SetMailCharacterSet(nsDependentCString(*default_charset));
+ msgWindow->SetCharsetOverride(*override_charset);
+ }
+ }
+ }
+
+ // if the pref says always override and no manual override then set the folder charset to override
+ if (!*override_charset) {
+ nsCOMPtr<nsIPrefBranch> pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ if (pPrefBranch)
+ {
+ bool force_override;
+ rv = pPrefBranch->GetBoolPref("mailnews.force_charset_override", &force_override);
+ if (NS_SUCCEEDED(rv) && force_override)
+ {
+ *override_charset = true;
+ }
+ }
+ }
+ }
+ }
+ nsAutoCString urlString;
+ if (NS_SUCCEEDED(aURI->GetSpec(urlString)))
+ {
+ if (!urlString.IsEmpty())
+ {
+ NS_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);
+ MsgReplaceSubstring(mOutputFormat, "%2F", "/");
+ MsgReplaceSubstring(mOutputFormat, "%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.Equals("application/vnd.mozilla.xul+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.Equals("message/rfc822"))
+ {
+ mRealContentType = "application/x-message-display";
+ mOutputFormat = "text/html";
+ *aNewType = nsMimeOutput::nsMimeMessageBodyDisplay;
+ }
+ else if (mRealContentType.Equals("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;
+ mPendingContext = 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;
+
+ // 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->GetSpec(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.get(), getter_Copies(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.Equals("raw"))
+ *aOutputContentType = (char *) nsMemory::Clone(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!
+ */
+ *aIdentity = mIdentity;
+ NS_IF_ADDREF(*aIdentity);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStreamConverter::SetIdentity(nsIMsgIdentity * aIdentity)
+{
+ mIdentity = aIdentity;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStreamConverter::SetOriginalMsgURI(const char * originalMsgURI)
+{
+ mOriginalMsgURI = originalMsgURI;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStreamConverter::GetOriginalMsgURI(char ** result)
+{
+ if (!result) return NS_ERROR_NULL_POINTER;
+ *result = ToNewCString(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 whenver data is written into the input stream by the
+// networking library...
+//
+nsresult
+nsStreamConverter::OnDataAvailable(nsIRequest *request,
+ nsISupports *ctxt,
+ nsIInputStream *aIStream,
+ uint64_t sourceOffset,
+ uint32_t aLength)
+{
+ nsresult rc=NS_OK; // should this be an error instead?
+ uint32_t readLen = aLength;
+ 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;
+ }
+
+ char *buf = (char *)PR_Malloc(aLength);
+ if (!buf)
+ return NS_ERROR_OUT_OF_MEMORY; /* we couldn't allocate the object */
+
+ readLen = aLength;
+ aIStream->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, nsISupports *ctxt)
+{
+#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;
+ mPendingContext = ctxt;
+ }
+ else
+ mOutListener->OnStartRequest(request, ctxt);
+ }
+
+ 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, nsISupports *ctxt, nsresult status)
+{
+ // Make sure we fire any pending OnStartRequest before we do OnStop.
+ FirePendingStartRequest();
+#ifdef DEBUG_rhp
+ printf("nsStreamConverter::OnStopRequest()\n");
+#endif
+
+ //
+ // 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();
+
+ // Make sure to do necessary cleanup!
+ InternalCleanup();
+
+#if 0
+ // print out the mime timing information BEFORE we flush to layout
+ // otherwise we'll be including layout information.
+ printf("Time Spent in mime: %d ms\n", PR_IntervalToMilliseconds(PR_IntervalNow() - mConvertContentTime));
+#endif
+
+ // forward on top request to any listeners
+ if (mOutListener)
+ mOutListener->OnStopRequest(request, ctxt, 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 syncronous 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, mPendingContext);
+ mPendingRequest = nullptr;
+ mPendingContext = nullptr;
+ }
+ return NS_OK;
+}