Back to index

lightning-sunbird  0.9+nobinonly
nsStreamConverter.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *   Brodie Thiesfield <brofield@jellycan.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 #include "nsCOMPtr.h"
00040 #include <stdio.h>
00041 #include "mimecom.h"
00042 #include "modmimee.h"
00043 #include "nscore.h"
00044 #include "nsStreamConverter.h"
00045 #include "comi18n.h"
00046 #include "prmem.h"
00047 #include "prprf.h"
00048 #include "prlog.h"
00049 #include "plstr.h"
00050 #include "mimemoz2.h"
00051 #include "nsMimeTypes.h"
00052 #include "nsIComponentManager.h"
00053 #include "nsIURL.h"
00054 #include "nsString.h"
00055 #include "nsReadableUtils.h"
00056 #include "nsUnicharUtils.h"
00057 #include "nsIServiceManager.h"
00058 #include "nsXPIDLString.h"
00059 #include "nsMemory.h"
00060 #include "nsIPipe.h"
00061 #include "nsMimeStringResources.h"
00062 #include "nsIPrefService.h"
00063 #include "nsIPrefBranch.h"
00064 #include "nsNetUtil.h"
00065 #include "nsIMsgQuote.h"
00066 #include "nsIScriptSecurityManager.h"
00067 #include "nsNetUtil.h"
00068 #include "mozITXTToHTMLConv.h"
00069 #include "nsIMsgMailNewsUrl.h"
00070 #include "nsIMsgWindow.h"
00071 #include "nsStreamConverter.h"
00072 #include "nsICategoryManager.h"
00073 #include "nsIInterfaceRequestor.h"
00074 #include "nsIInterfaceRequestorUtils.h"
00075 #include "nsIAsyncInputStream.h"
00076 #include "nsIAsyncOutputStream.h"
00077 
00078 #define PREF_MAIL_DISPLAY_GLYPH "mail.display_glyph"
00079 #define PREF_MAIL_DISPLAY_STRUCT "mail.display_struct"
00080 
00082 // Bridge routines for new stream converter XP-COM interface 
00084 
00085 extern "C" void  *
00086 mime_bridge_create_draft_stream(nsIMimeEmitter      *newEmitter,
00087                                 nsStreamConverter   *newPluginObj2,
00088                                 nsIURI              *uri,
00089                                 nsMimeOutputType    format_out);
00090 
00091 extern "C" void  *
00092 bridge_create_stream(nsIMimeEmitter      *newEmitter,
00093                      nsStreamConverter   *newPluginObj2,
00094                      nsIURI              *uri,
00095                      nsMimeOutputType    format_out,
00096                      PRUint32            whattodo,
00097                      nsIChannel          *aChannel)
00098 {
00099   if  ( (format_out == nsMimeOutput::nsMimeMessageDraftOrTemplate) ||
00100         (format_out == nsMimeOutput::nsMimeMessageEditorTemplate) )
00101     return mime_bridge_create_draft_stream(newEmitter, newPluginObj2, uri, format_out);
00102   else 
00103     return mime_bridge_create_display_stream(newEmitter, newPluginObj2, uri, format_out, whattodo,
00104                                              aChannel);
00105 }
00106 
00107 void
00108 bridge_destroy_stream(void *newStream)
00109 {
00110   nsMIMESession     *stream = (nsMIMESession *)newStream;
00111   if (!stream)
00112     return;
00113   
00114   PR_FREEIF(stream);
00115 }
00116 
00117 void          
00118 bridge_set_output_type(void *bridgeStream, nsMimeOutputType aType)
00119 {
00120   nsMIMESession *session = (nsMIMESession *)bridgeStream;
00121 
00122   if (session)
00123   {
00124     // BAD ASSUMPTION!!!! NEED TO CHECK aType
00125     struct mime_stream_data *msd = (struct mime_stream_data *)session->data_object;
00126     if (msd)
00127       msd->format_out = aType;     // output format type
00128   }
00129 }
00130 
00131 nsresult
00132 bridge_new_new_uri(void *bridgeStream, nsIURI *aURI, PRInt32 aOutputType)
00133 {
00134   nsMIMESession *session = (nsMIMESession *)bridgeStream;
00135   const char    **fixup_pointer = nsnull;
00136 
00137   if (session)
00138   {
00139     if (session->data_object)
00140     {
00141       PRBool   *override_charset = nsnull;
00142       char    **default_charset = nsnull;
00143       char    **url_name = nsnull;
00144 
00145       if  ( (aOutputType == nsMimeOutput::nsMimeMessageDraftOrTemplate) ||
00146             (aOutputType == nsMimeOutput::nsMimeMessageEditorTemplate) )
00147       {
00148         struct mime_draft_data *mdd = (struct mime_draft_data *)session->data_object;
00149         if (mdd->options)
00150         {
00151           default_charset = &(mdd->options->default_charset);
00152           override_charset = &(mdd->options->override_charset);
00153           url_name = &(mdd->url_name);
00154         }
00155       }
00156       else
00157       {
00158         struct mime_stream_data *msd = (struct mime_stream_data *)session->data_object;
00159 
00160         if (msd->options)
00161         {
00162           default_charset = &(msd->options->default_charset);
00163           override_charset = &(msd->options->override_charset);
00164           url_name = &(msd->url_name);
00165           fixup_pointer = &(msd->options->url);
00166         }
00167       }
00168 
00169       if ( (default_charset) && (override_charset) && (url_name) )
00170       {
00171         // 
00172         // set the default charset to be the folder charset if we have one associated with
00173         // this url...
00174         nsCOMPtr<nsIMsgI18NUrl> i18nUrl (do_QueryInterface(aURI));
00175         if (i18nUrl)
00176         {
00177           nsXPIDLCString charset;
00178 
00179           // check to see if we have a charset override...and if we do, set that field appropriately too...
00180           nsresult rv = i18nUrl->GetCharsetOverRide(getter_Copies(charset));
00181           if (NS_SUCCEEDED(rv) && !charset.IsEmpty() ) {
00182             *override_charset = PR_TRUE;
00183             *default_charset = ToNewCString(charset);
00184           }
00185           else
00186           {
00187             i18nUrl->GetFolderCharset(getter_Copies(charset));
00188             if (!charset.IsEmpty())
00189               *default_charset = ToNewCString(charset);
00190           }
00191 
00192           // if there is no manual override and a folder charset exists
00193           // then check if we have a folder level override
00194           if (!(*override_charset) && *default_charset && **default_charset)
00195           {
00196             PRBool folderCharsetOverride;
00197             rv = i18nUrl->GetFolderCharsetOverride(&folderCharsetOverride);
00198             if (NS_SUCCEEDED(rv) && folderCharsetOverride)
00199               *override_charset = PR_TRUE;
00200 
00201             // notify the default to msgWindow (for the menu check mark)
00202             // do not set the default in case of nsMimeMessageDraftOrTemplate
00203             // or nsMimeMessageEditorTemplate because it is already set 
00204             // when the message is displayed and doing it again may overwrite 
00205             // the correct MIME charset parsed from the message header
00206             if (aOutputType != nsMimeOutput::nsMimeMessageDraftOrTemplate &&
00207                 aOutputType != nsMimeOutput::nsMimeMessageEditorTemplate)
00208             {
00209               nsCOMPtr<nsIMsgMailNewsUrl> msgurl (do_QueryInterface(aURI));
00210               if (msgurl)
00211               {
00212                 nsCOMPtr<nsIMsgWindow> msgWindow;
00213                 msgurl->GetMsgWindow(getter_AddRefs(msgWindow));
00214                 if (msgWindow)
00215                 {
00216                   msgWindow->SetMailCharacterSet(*default_charset);
00217                   msgWindow->SetCharsetOverride(*override_charset);
00218                 }
00219               }
00220             }
00221 
00222             // if the pref says always override and no manual override then set the folder charset to override
00223             if (!*override_charset) {
00224               nsCOMPtr<nsIPrefBranch> pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
00225               if (pPrefBranch)
00226               {
00227                 PRBool  force_override;
00228                 rv = pPrefBranch->GetBoolPref("mailnews.force_charset_override", &force_override);
00229                 if (NS_SUCCEEDED(rv) && force_override) 
00230                 {
00231                   *override_charset = PR_TRUE;
00232                 }
00233               }
00234             }
00235           }
00236         }
00237         nsCAutoString urlString;
00238         if (NS_SUCCEEDED(aURI->GetSpec(urlString)))
00239         {
00240           if (!urlString.IsEmpty())
00241           {
00242             CRTFREEIF(*url_name);
00243             *url_name = ToNewCString(urlString);
00244             if (!(*url_name))
00245               return NS_ERROR_OUT_OF_MEMORY;
00246 
00247             // rhp: Ugh, this is ugly...but it works.
00248             if (fixup_pointer)
00249               *fixup_pointer = (const char *)*url_name;
00250           }
00251         }
00252       }
00253     }
00254   }
00255 
00256   return NS_OK;
00257 }
00258 
00259 static int
00260 mime_headers_callback ( void *closure, MimeHeaders *headers )
00261 {
00262   // We get away with this because this doesn't get called on draft operations.
00263   struct mime_stream_data *msd = (struct mime_stream_data *)closure;
00264 
00265   PR_ASSERT ( msd && headers );
00266   if ( !msd || ! headers ) 
00267     return 0;
00268 
00269   PR_ASSERT ( msd->headers == NULL );
00270   msd->headers = MimeHeaders_copy ( headers );
00271   return 0;
00272 }
00273 
00274 nsresult          
00275 bridge_set_mime_stream_converter_listener(void *bridgeStream, nsIMimeStreamConverterListener* listener,
00276                                           nsMimeOutputType aOutputType)
00277 {
00278   nsMIMESession *session = (nsMIMESession *)bridgeStream;
00279 
00280   if ( (session) && (session->data_object) )
00281   {
00282     if  ( (aOutputType == nsMimeOutput::nsMimeMessageDraftOrTemplate) ||
00283           (aOutputType == nsMimeOutput::nsMimeMessageEditorTemplate) )
00284     {
00285       struct mime_draft_data *mdd = (struct mime_draft_data *)session->data_object;
00286       if (mdd->options)
00287       {
00288         if (listener)
00289         {
00290           mdd->options->caller_need_root_headers = PR_TRUE;
00291           mdd->options->decompose_headers_info_fn = mime_headers_callback;
00292         }
00293         else
00294         {
00295           mdd->options->caller_need_root_headers = PR_FALSE;
00296           mdd->options->decompose_headers_info_fn = nsnull;
00297         }
00298       }
00299     }
00300     else
00301     {
00302       struct mime_stream_data *msd = (struct mime_stream_data *)session->data_object;
00303       
00304       if (msd->options)
00305       {
00306         if (listener)
00307         {
00308           msd->options->caller_need_root_headers = PR_TRUE;
00309           msd->options->decompose_headers_info_fn = mime_headers_callback;
00310         }
00311         else
00312         {
00313           msd->options->caller_need_root_headers = PR_FALSE;
00314           msd->options->decompose_headers_info_fn = nsnull;
00315         }
00316       }
00317     }
00318   }
00319 
00320   return NS_OK;
00321 }
00322 
00323 // find a query element in a url and return a pointer to its data
00324 // (query must be in the form "query=")
00325 static const char * 
00326 FindQueryElementData(const char * aUrl, const char * aQuery)
00327 {
00328   if (aUrl && aQuery)
00329   {
00330     size_t queryLen = 0; // we don't call strlen until we need to
00331     aUrl = PL_strcasestr(aUrl, aQuery);
00332     while (aUrl)
00333     {
00334       if (!queryLen) 
00335         queryLen = strlen(aQuery);
00336       if (*(aUrl-1) == '&' || *(aUrl-1) == '?')
00337         return aUrl + queryLen;
00338       aUrl = PL_strcasestr(aUrl + queryLen, aQuery);
00339     }
00340   }
00341   return nsnull;
00342 }
00343 
00344 // case-sensitive test for string prefixing. If |string| is prefixed
00345 // by |prefix| then a pointer to the next character in |string| following
00346 // the prefix is returned. If it is not a prefix then |nsnull| is returned.
00347 static const char * 
00348 SkipPrefix(const char *aString, const char *aPrefix)
00349 {
00350   while (*aPrefix)
00351     if (*aPrefix++ != *aString++)
00352        return nsnull;
00353   return aString;
00354 }
00355 
00356 //
00357 // Utility routines needed by this interface
00358 //
00359 nsresult
00360 nsStreamConverter::DetermineOutputFormat(const char *aUrl, nsMimeOutputType *aNewType)
00361 {
00362   // sanity checking
00363   NS_ENSURE_ARG_POINTER(aNewType);
00364   if (!aUrl || !*aUrl)
00365   {
00366     // default to html for the entire document
00367     *aNewType = nsMimeOutput::nsMimeMessageQuoting;
00368     mOutputFormat = "text/html";
00369     return NS_OK;
00370   }
00371 
00372   // shorten the url that we test for the query strings by skipping directly
00373   // to the part where the query strings begin.
00374   const char *queryPart = PL_strchr(aUrl, '?');
00375 
00376   // First, did someone pass in a desired output format. They will be able to
00377   // pass in any content type (i.e. image/gif, text/html, etc...but the "/" will
00378   // have to be represented via the "%2F" value
00379   const char *format = FindQueryElementData(queryPart, "outformat=");
00380   if (format)
00381   {
00382     //NOTE: I've done a file contents search of every file (*.*) in the mozilla 
00383     // directory tree and there is not a single location where the string "outformat"
00384     // is added to any URL. It appears that this code has been orphaned off by a change 
00385     // elsewhere and is no longer required. It will be removed in the future unless 
00386     // someone complains.
00387     NS_ABORT_IF_FALSE(PR_FALSE, "Is this code actually being used?");
00388 
00389     while (*format == ' ')
00390       ++format;
00391 
00392     if (*format)
00393     {
00394       mOverrideFormat = "raw";
00395 
00396       // set mOutputFormat to the supplied format, ensure that we replace any
00397       // %2F strings with the slash character
00398       const char *nextField = PL_strpbrk(format, "&; ");
00399       mOutputFormat.Assign(format, nextField ? nextField - format : -1);
00400       mOutputFormat.ReplaceSubstring("%2F", "/");
00401       mOutputFormat.ReplaceSubstring("%2f", "/");
00402   
00403       // Don't muck with this data!
00404       *aNewType = nsMimeOutput::nsMimeMessageRaw;
00405       return NS_OK;
00406     }
00407   }
00408 
00409   // is this is a part that should just come out raw
00410   const char *part = FindQueryElementData(queryPart, "part=");
00411   if (part && !mToType.Equals("application/vnd.mozilla.xul+xml"))
00412   {
00413     // default for parts
00414     mOutputFormat = "raw";
00415     *aNewType = nsMimeOutput::nsMimeMessageRaw;
00416 
00417     // if we are being asked to fetch a part....it should have a 
00418     // content type appended to it...if it does, we want to remember
00419     // that as mOutputFormat
00420     const char * typeField = FindQueryElementData(queryPart, "type=");
00421     if (typeField && !strncmp(typeField, "application/x-message-display", sizeof("application/x-message-display") - 1))
00422     {
00423       const char *secondTypeField = FindQueryElementData(typeField, "type=");
00424       if (secondTypeField)
00425         typeField = secondTypeField;
00426     }
00427     if (typeField)
00428     {
00429       // store the real content type...mOutputFormat gets deleted later on...
00430       // and make sure we only get our own value.
00431       char *nextField = PL_strchr(typeField, '&');
00432       mRealContentType.Assign(typeField, nextField ? nextField - typeField : -1);
00433 
00434       if (mRealContentType.LowerCaseEqualsLiteral("message/rfc822"))
00435       {
00436         mRealContentType = "application/x-message-display";
00437         mOutputFormat = "text/html";
00438         *aNewType = nsMimeOutput::nsMimeMessageBodyDisplay;
00439       }
00440       else if (mRealContentType.LowerCaseEqualsLiteral("application/x-message-display"))
00441       {
00442         mRealContentType = "";
00443         mOutputFormat = "text/html";
00444         *aNewType = nsMimeOutput::nsMimeMessageBodyDisplay;
00445       }
00446     }
00447 
00448     return NS_OK;
00449   }
00450 
00451   // if using the header query
00452   const char *header = FindQueryElementData(queryPart, "header=");
00453   if (header)
00454   {
00455     struct HeaderType {
00456       const char * headerType;
00457       const char * outputFormat;
00458       nsMimeOutputType mimeOutputType;
00459     };
00460 
00461     // place most commonly used options at the top
00462     static const struct HeaderType rgTypes[] = 
00463     {
00464       { "filter",    "text/html",  nsMimeOutput::nsMimeMessageFilterSniffer },
00465       { "quotebody", "text/html",  nsMimeOutput::nsMimeMessageBodyQuoting },
00466       { "print",     "text/html",  nsMimeOutput::nsMimeMessagePrintOutput },
00467       { "only",      "text/xml",   nsMimeOutput::nsMimeMessageHeaderDisplay },
00468       { "none",      "text/html",  nsMimeOutput::nsMimeMessageBodyDisplay },
00469       { "quote",     "text/html",  nsMimeOutput::nsMimeMessageQuoting },
00470       { "saveas",    "text/html",  nsMimeOutput::nsMimeMessageSaveAs },
00471       { "src",       "text/plain", nsMimeOutput::nsMimeMessageSource },
00472       { "attach",    "raw",        nsMimeOutput::nsMimeMessageAttach }
00473     };
00474 
00475     // find the requested header in table, ensure that we don't match on a prefix
00476     // by checking that the following character is either null or the next query element
00477     const char * remainder;
00478     for (int n = 0; n < NS_ARRAY_LENGTH(rgTypes); ++n)
00479     {
00480       remainder = SkipPrefix(header, rgTypes[n].headerType);
00481       if (remainder && (*remainder == '\0' || *remainder == '&'))
00482       {
00483         mOutputFormat = rgTypes[n].outputFormat;
00484         *aNewType = rgTypes[n].mimeOutputType;
00485         return NS_OK;
00486       }
00487     }
00488   }
00489 
00490   // default to html for just the body
00491   mOutputFormat = "text/html";
00492   *aNewType = nsMimeOutput::nsMimeMessageBodyDisplay;
00493 
00494   return NS_OK;
00495 }
00496 
00497 nsresult 
00498 nsStreamConverter::InternalCleanup(void)
00499 {
00500   if (mBridgeStream)
00501   {
00502     bridge_destroy_stream(mBridgeStream);
00503     mBridgeStream = nsnull;
00504   }
00505 
00506   return NS_OK;
00507 }
00508 
00509 /* 
00510  * Inherited methods for nsMimeConverter
00511  */
00512 nsStreamConverter::nsStreamConverter()
00513 {
00514   // Init member variables...
00515   mWrapperOutput = PR_FALSE;
00516   mBridgeStream = NULL;
00517   mOutputFormat = "text/html";
00518   mAlreadyKnowOutputType = PR_FALSE;
00519   mForwardInline = PR_FALSE;
00520   
00521   mPendingRequest = nsnull;
00522   mPendingContext = nsnull;
00523 }
00524 
00525 nsStreamConverter::~nsStreamConverter()
00526 {
00527   InternalCleanup();
00528 }
00529 
00530 NS_IMPL_THREADSAFE_ADDREF(nsStreamConverter)
00531 NS_IMPL_THREADSAFE_RELEASE(nsStreamConverter)
00532 
00533 NS_INTERFACE_MAP_BEGIN(nsStreamConverter)
00534    NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStreamListener)
00535    NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
00536    NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
00537    NS_INTERFACE_MAP_ENTRY(nsIStreamConverter)
00538    NS_INTERFACE_MAP_ENTRY(nsIMimeStreamConverter)
00539 NS_INTERFACE_MAP_END
00540 
00542 // nsStreamConverter definitions....
00544 
00545 NS_IMETHODIMP nsStreamConverter::Init(nsIURI *aURI, nsIStreamListener * aOutListener, nsIChannel *aChannel)
00546 {
00547   NS_ENSURE_ARG_POINTER(aURI);
00548 
00549   nsresult rv = NS_OK; 
00550   mOutListener = aOutListener;
00551 
00552   // mscott --> we need to look at the url and figure out what the correct output type is...
00553   nsMimeOutputType newType = mOutputType;
00554   if (!mAlreadyKnowOutputType)
00555   {
00556     nsCAutoString urlSpec;
00557     rv = aURI->GetSpec(urlSpec);
00558     DetermineOutputFormat(urlSpec.get(), &newType);
00559     mAlreadyKnowOutputType = PR_TRUE;
00560     mOutputType = newType;  
00561   }
00562   
00563   switch (newType)
00564   {
00565     case nsMimeOutput::nsMimeMessageSplitDisplay:    // the wrapper HTML output to produce the split header/body display
00566       mWrapperOutput = PR_TRUE;
00567       mOutputFormat = "text/html";
00568       break;
00569     case nsMimeOutput::nsMimeMessageHeaderDisplay:   // the split header/body display
00570       mOutputFormat = "text/xml";
00571       break;
00572     case nsMimeOutput::nsMimeMessageBodyDisplay:   // the split header/body display
00573       mOutputFormat = "text/html";
00574       break;
00575       
00576     case nsMimeOutput::nsMimeMessageQuoting:      // all HTML quoted output
00577     case nsMimeOutput::nsMimeMessageSaveAs:       // Save as operation
00578     case nsMimeOutput::nsMimeMessageBodyQuoting:  // only HTML body quoted output
00579     case nsMimeOutput::nsMimeMessagePrintOutput:  // all Printing output
00580       mOutputFormat = "text/html";
00581       break;
00582       
00583     case nsMimeOutput::nsMimeMessageAttach:
00584     case nsMimeOutput::nsMimeMessageDecrypt:  
00585     case nsMimeOutput::nsMimeMessageRaw:              // the raw RFC822 data and attachments
00586       mOutputFormat = "raw";
00587       break;
00588       
00589     case nsMimeOutput::nsMimeMessageSource:      // the raw RFC822 data (view source) and attachments
00590       mOutputFormat = "text/plain";
00591       mOverrideFormat = "raw";
00592       break;
00593       
00594     case nsMimeOutput::nsMimeMessageDraftOrTemplate:       // Loading drafts & templates
00595       mOutputFormat = "message/draft";
00596       break;
00597       
00598     case nsMimeOutput::nsMimeMessageEditorTemplate:       // Loading templates into editor
00599       mOutputFormat = "text/html";
00600       break;
00601 
00602     case nsMimeOutput::nsMimeMessageFilterSniffer: // output all displayable part as raw 
00603       mOutputFormat = "text/html";
00604       break;
00605 
00606     default:
00607       NS_ASSERTION(0, "this means I made a mistake in my assumptions");
00608   }
00609   
00610   
00611   // the following output channel stream is used to fake the content type for people who later
00612   // call into us..
00613   nsXPIDLCString contentTypeToUse;
00614   GetContentType(getter_Copies(contentTypeToUse));
00615   // mscott --> my theory is that we don't need this fake outgoing channel. Let's use the
00616   // original channel and just set our content type ontop of the original channel...
00617 
00618   aChannel->SetContentType(contentTypeToUse);
00619 
00620   //rv = NS_NewInputStreamChannel(getter_AddRefs(mOutgoingChannel), aURI, nsnull, contentTypeToUse, -1);
00621   //if (NS_FAILED(rv)) 
00622   //    return rv;
00623   
00624   // Set system principal for this document, which will be dynamically generated 
00625   
00626   // We will first find an appropriate emitter in the repository that supports 
00627   // the requested output format...note, the special exceptions are nsMimeMessageDraftOrTemplate
00628   // or nsMimeMessageEditorTemplate where we don't need any emitters
00629   //
00630   
00631   if ( (newType != nsMimeOutput::nsMimeMessageDraftOrTemplate) && 
00632     (newType != nsMimeOutput::nsMimeMessageEditorTemplate) )
00633   {
00634     nsCAutoString categoryName ("@mozilla.org/messenger/mimeemitter;1?type=");
00635     if (!mOverrideFormat.IsEmpty())
00636       categoryName += mOverrideFormat;
00637     else
00638       categoryName += mOutputFormat;
00639     
00640     nsCOMPtr<nsICategoryManager> catman = do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
00641     if (NS_SUCCEEDED(rv))
00642     {
00643       nsXPIDLCString contractID;
00644       catman->GetCategoryEntry("mime-emitter", categoryName.get(), getter_Copies(contractID));
00645       if (!contractID.IsEmpty())
00646         categoryName = contractID;
00647     }
00648 
00649     mEmitter = do_CreateInstance(categoryName.get(), &rv);
00650 
00651     if ((NS_FAILED(rv)) || (!mEmitter))
00652     {
00653       return NS_ERROR_OUT_OF_MEMORY;
00654     }
00655   }
00656     
00657   // now we want to create a pipe which we'll use for converting the data...
00658   rv = NS_NewPipe(getter_AddRefs(mInputStream), getter_AddRefs(mOutputStream),
00659                   NS_STREAM_CONVERTER_SEGMENT_SIZE,
00660                   /* PR_UINT32_MAX */  NS_STREAM_CONVERTER_BUFFER_SIZE, 
00661                   PR_TRUE, PR_TRUE);
00662   
00663   // initialize our emitter
00664   if (NS_SUCCEEDED(rv) && mEmitter)
00665   {
00666     mEmitter->Initialize(aURI, aChannel, newType);
00667     mEmitter->SetPipe(mInputStream, mOutputStream);
00668     mEmitter->SetOutputListener(aOutListener);
00669   }
00670   
00671   PRUint32 whattodo = mozITXTToHTMLConv::kURLs;
00672   PRBool enable_emoticons = PR_TRUE;
00673   PRBool enable_structs = PR_TRUE;
00674 
00675   nsCOMPtr<nsIPrefBranch> pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
00676   if (pPrefBranch)
00677   {
00678     rv = pPrefBranch->GetBoolPref(PREF_MAIL_DISPLAY_GLYPH,&enable_emoticons);
00679     if (NS_FAILED(rv) || enable_emoticons) 
00680     {
00681       whattodo = whattodo | mozITXTToHTMLConv::kGlyphSubstitution;
00682     }
00683     rv = pPrefBranch->GetBoolPref(PREF_MAIL_DISPLAY_STRUCT,&enable_structs);
00684     if (NS_FAILED(rv) || enable_structs) 
00685     {
00686       whattodo = whattodo | mozITXTToHTMLConv::kStructPhrase;
00687     }
00688   }
00689 
00690   if (mOutputType == nsMimeOutput::nsMimeMessageSource)
00691     return NS_OK;
00692   else
00693   {
00694     mBridgeStream = bridge_create_stream(mEmitter, this, aURI, newType, whattodo, aChannel);
00695     if (!mBridgeStream)
00696       return NS_ERROR_OUT_OF_MEMORY;
00697     else
00698     {
00699       SetStreamURI(aURI);
00700 
00701       //Do we need to setup an Mime Stream Converter Listener?
00702       if (mMimeStreamConverterListener)
00703         bridge_set_mime_stream_converter_listener((nsMIMESession *)mBridgeStream, mMimeStreamConverterListener, mOutputType);
00704 
00705       return NS_OK;
00706     }
00707   }
00708 }
00709 
00710 NS_IMETHODIMP nsStreamConverter::GetContentType(char **aOutputContentType)
00711 {
00712   if (!aOutputContentType)
00713     return NS_ERROR_NULL_POINTER;
00714 
00715   // since this method passes a string through an IDL file we need to use nsMemory to allocate it 
00716   // and not nsCRT::strdup!
00717   //  (1) check to see if we have a real content type...use it first...
00718   if (!mRealContentType.IsEmpty())
00719     *aOutputContentType = ToNewCString(mRealContentType);
00720   else if (mOutputFormat.LowerCaseEqualsLiteral("raw"))
00721   {
00722     *aOutputContentType = (char *) nsMemory::Clone(UNKNOWN_CONTENT_TYPE, sizeof(UNKNOWN_CONTENT_TYPE));
00723   }
00724   else
00725     *aOutputContentType = ToNewCString(mOutputFormat);
00726   return NS_OK;
00727 }
00728 
00729 // 
00730 // This is the type of output operation that is being requested by libmime. The types
00731 // of output are specified by nsIMimeOutputType enum
00732 // 
00733 nsresult 
00734 nsStreamConverter::SetMimeOutputType(nsMimeOutputType aType)
00735 {
00736   mAlreadyKnowOutputType = PR_TRUE;
00737   mOutputType = aType;
00738   if (mBridgeStream)
00739     bridge_set_output_type(mBridgeStream, aType);
00740   return NS_OK;
00741 }
00742 
00743 NS_IMETHODIMP nsStreamConverter::GetMimeOutputType(nsMimeOutputType *aOutFormat)
00744 {
00745   nsresult rv = NS_OK;
00746   if (aOutFormat)
00747     *aOutFormat = mOutputType;
00748   else
00749     rv = NS_ERROR_NULL_POINTER;
00750 
00751   return rv;
00752 }
00753 
00754 // 
00755 // This is needed by libmime for MHTML link processing...this is the URI associated
00756 // with this input stream
00757 // 
00758 nsresult 
00759 nsStreamConverter::SetStreamURI(nsIURI *aURI)
00760 {
00761   mURI = aURI;
00762   if (mBridgeStream)
00763     return bridge_new_new_uri((nsMIMESession *)mBridgeStream, aURI, mOutputType);
00764   else
00765     return NS_OK;
00766 }
00767 
00768 nsresult
00769 nsStreamConverter::SetMimeHeadersListener(nsIMimeStreamConverterListener *listener, nsMimeOutputType aType)
00770 {
00771    mMimeStreamConverterListener = listener;
00772    bridge_set_mime_stream_converter_listener((nsMIMESession *)mBridgeStream, listener, aType);
00773    return NS_OK;
00774 }
00775 
00776 NS_IMETHODIMP
00777 nsStreamConverter::SetForwardInline(PRBool forwardInline)
00778 {
00779   mForwardInline = forwardInline;
00780   return NS_OK;
00781 }
00782 
00783 NS_IMETHODIMP
00784 nsStreamConverter::GetForwardInline(PRBool *result)
00785 {
00786   if (!result) return NS_ERROR_NULL_POINTER;
00787   *result = mForwardInline;
00788   return NS_OK;
00789 }
00790 
00791 NS_IMETHODIMP
00792 nsStreamConverter::GetIdentity(nsIMsgIdentity * *aIdentity)
00793 {
00794   if (!aIdentity) return NS_ERROR_NULL_POINTER;
00795   /*
00796   We don't have an identity for the local folders account,
00797     we will return null but it is not an error!
00798   */
00799     *aIdentity = mIdentity;
00800     NS_IF_ADDREF(*aIdentity);
00801   return NS_OK;
00802 }
00803 
00804 NS_IMETHODIMP
00805 nsStreamConverter::SetIdentity(nsIMsgIdentity * aIdentity)
00806 {
00807   mIdentity = aIdentity;
00808   return NS_OK;
00809 }
00810 
00811 NS_IMETHODIMP
00812 nsStreamConverter::SetOriginalMsgURI(const char * originalMsgURI)
00813 {
00814   mOriginalMsgURI = originalMsgURI;
00815   return NS_OK;
00816 }
00817 
00818 NS_IMETHODIMP
00819 nsStreamConverter::GetOriginalMsgURI(char ** result)
00820 {
00821   if (!result) return NS_ERROR_NULL_POINTER;
00822   *result = ToNewCString(mOriginalMsgURI);
00823   return NS_OK;
00824 }
00825 
00827 // Methods for nsIStreamListener...
00829 //
00830 // Notify the client that data is available in the input stream.  This
00831 // method is called whenver data is written into the input stream by the
00832 // networking library...
00833 //
00834 nsresult 
00835 nsStreamConverter::OnDataAvailable(nsIRequest     *request, 
00836                                    nsISupports    *ctxt, 
00837                                    nsIInputStream *aIStream, 
00838                                    PRUint32       sourceOffset, 
00839                                    PRUint32       aLength)
00840 {
00841   nsresult        rc=NS_OK;     // should this be an error instead?
00842   PRUint32        readLen = aLength;
00843   PRUint32        written;
00844 
00845   // If this is the first time through and we are supposed to be 
00846   // outputting the wrapper two pane URL, then do it now.
00847   if (mWrapperOutput)
00848   {
00849     char        outBuf[1024];
00850 const char output[] = "\
00851 <HTML>\
00852 <FRAMESET ROWS=\"30%%,70%%\">\
00853 <FRAME NAME=messageHeader SRC=\"%s?header=only\">\
00854 <FRAME NAME=messageBody SRC=\"%s?header=none\">\
00855 </FRAMESET>\
00856 </HTML>";
00857 
00858     nsCAutoString url;
00859     if (NS_FAILED(mURI->GetSpec(url)))
00860       return NS_ERROR_FAILURE;
00861   
00862     PR_snprintf(outBuf, sizeof(outBuf), output, url.get(), url.get());
00863     
00864     if (mEmitter)
00865       mEmitter->Write(outBuf, strlen(outBuf), &written);
00866 
00867     // rhp: will this stop the stream???? Not sure.    
00868     return NS_ERROR_FAILURE;
00869   }
00870 
00871   char *buf = (char *)PR_Malloc(aLength);
00872   if (!buf)
00873     return NS_ERROR_OUT_OF_MEMORY; /* we couldn't allocate the object */
00874 
00875   readLen = aLength;
00876   aIStream->Read(buf, aLength, &readLen);
00877 
00878   // We need to filter out any null characters else we will have a lot of trouble
00879   // as we use c string everywhere in mime
00880   char * readPtr;
00881   char * endPtr = buf + readLen;
00882 
00883   // First let see if the stream contains null characters
00884   for (readPtr = buf; readPtr < endPtr && *readPtr; readPtr ++)
00885     ;
00886 
00887   // Did we find a null character? Then, we need to cleanup the stream
00888   if (readPtr < endPtr)
00889   {
00890     char * writePtr = readPtr;
00891     for (readPtr ++; readPtr < endPtr; readPtr ++)
00892     {
00893       if (!*readPtr)
00894         continue;
00895 
00896       *writePtr = *readPtr;
00897       writePtr ++;
00898     }
00899     readLen = writePtr - buf;
00900   }
00901 
00902   if (mOutputType == nsMimeOutput::nsMimeMessageSource)
00903   {
00904     rc = NS_OK;
00905     if (mEmitter)
00906       rc = mEmitter->Write(buf, readLen, &written);
00907   }
00908   else if (mBridgeStream)
00909   {
00910     nsMIMESession   *tSession = (nsMIMESession *) mBridgeStream;
00911     rc = tSession->put_block((nsMIMESession *)mBridgeStream, buf, readLen);
00912   }
00913 
00914   PR_FREEIF(buf);
00915   return rc;
00916 }
00917 
00919 // Methods for nsIRequestObserver 
00921 //
00922 // Notify the observer that the URL has started to load.  This method is
00923 // called only once, at the beginning of a URL load.
00924 //
00925 nsresult 
00926 nsStreamConverter::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
00927 {
00928 #ifdef DEBUG_rhp
00929     printf("nsStreamConverter::OnStartRequest()\n");
00930 #endif
00931 
00932 #ifdef DEBUG_mscott
00933   mConvertContentTime = PR_IntervalNow();
00934 #endif
00935 
00936   // here's a little bit of hackery....
00937   // since the mime converter is now between the channel
00938   // and the 
00939   if (request)
00940   {
00941     nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
00942     if (channel)
00943     {
00944       nsXPIDLCString contentType;
00945       GetContentType(getter_Copies(contentType));
00946 
00947       channel->SetContentType(contentType);
00948     }
00949   }
00950 
00951   // forward the start request to any listeners                                 
00952   if (mOutListener) 
00953   {
00954     if (mOutputType == nsMimeOutput::nsMimeMessageRaw)
00955     {
00956       //we need to delay the on start request until we have figure out the real content type
00957       mPendingRequest = request;
00958       mPendingContext = ctxt;
00959     }
00960     else
00961       mOutListener->OnStartRequest(request, ctxt);
00962   }
00963 
00964   return NS_OK;                                                                 
00965 }
00966 
00967 //
00968 // Notify the observer that the URL has finished loading.  This method is 
00969 // called once when the networking library has finished processing the 
00970 //
00971 nsresult 
00972 nsStreamConverter::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult status)
00973 {
00974 #ifdef DEBUG_rhp
00975     printf("nsStreamConverter::OnStopRequest()\n");
00976 #endif
00977 
00978   //
00979   // Now complete the stream!
00980   //
00981   if (mBridgeStream)
00982   {
00983     nsMIMESession   *tSession = (nsMIMESession *) mBridgeStream;
00984     
00985     if (mMimeStreamConverterListener)
00986     {
00987 
00988       MimeHeaders    **workHeaders = nsnull;
00989 
00990       if  ( (mOutputType == nsMimeOutput::nsMimeMessageDraftOrTemplate) ||
00991             (mOutputType == nsMimeOutput::nsMimeMessageEditorTemplate) )
00992       {
00993         struct mime_draft_data *mdd = (struct mime_draft_data *)tSession->data_object;
00994         if (mdd)
00995           workHeaders = &(mdd->headers);
00996       }
00997       else
00998       {
00999         struct mime_stream_data *msd = (struct mime_stream_data *)tSession->data_object;
01000         if (msd)
01001           workHeaders = &(msd->headers);
01002       }
01003 
01004       if (workHeaders)
01005       {
01006         nsresult rv;
01007         nsCOMPtr<nsIMimeHeaders> mimeHeaders = do_CreateInstance(NS_IMIMEHEADERS_CONTRACTID, &rv);
01008         
01009         if (NS_SUCCEEDED(rv))
01010         {
01011           if (*workHeaders)
01012             mimeHeaders->Initialize((*workHeaders)->all_headers, (*workHeaders)->all_headers_fp);
01013           mMimeStreamConverterListener->OnHeadersReady(mimeHeaders);
01014         }
01015         else
01016           mMimeStreamConverterListener->OnHeadersReady(nsnull);
01017       }
01018 
01019       mMimeStreamConverterListener = nsnull; // release our reference
01020     }
01021     
01022     tSession->complete((nsMIMESession *)mBridgeStream);
01023   }
01024 
01025   // 
01026   // Now complete the emitter and do necessary cleanup!
01027   //
01028   if (mEmitter)    
01029   {
01030     mEmitter->Complete();
01031   }
01032 
01033   // First close the output stream...
01034   if (mOutputStream)
01035     mOutputStream->Close();
01036 
01037   // Make sure to do necessary cleanup!
01038   InternalCleanup();
01039 
01040 #ifdef DEBUG_mscott
01041   // print out the mime timing information BEFORE we flush to layout
01042   // otherwise we'll be including layout information.
01043   printf("Time Spent in mime:    %d ms\n", PR_IntervalToMilliseconds(PR_IntervalNow() - mConvertContentTime));
01044 #endif
01045 
01046   // forward on top request to any listeners
01047   if (mOutListener)
01048     mOutListener->OnStopRequest(request, ctxt, status);
01049     
01050 
01051   mAlreadyKnowOutputType = PR_FALSE;
01052 
01053   // since we are done converting data, lets close all the objects we own...
01054   // this helps us fix some circular ref counting problems we are running into...
01055   Close(); 
01056 
01057   // Time to return...
01058   return NS_OK;
01059 }
01060 
01061 nsresult nsStreamConverter::Close()
01062 {
01063   mOutgoingChannel = nsnull;
01064   mEmitter = nsnull;
01065   mOutListener = nsnull;
01066   return NS_OK;
01067 }
01068 
01069 // nsIStreamConverter implementation
01070 
01071 // No syncronous conversion at this time.
01072 NS_IMETHODIMP nsStreamConverter::Convert(nsIInputStream  *aFromStream,
01073                                          const char *aFromType,
01074                                          const char *aToType,
01075                                          nsISupports     *aCtxt, 
01076                                          nsIInputStream **_retval) 
01077 {
01078     return NS_ERROR_NOT_IMPLEMENTED;
01079 }
01080 
01081 // Stream converter service calls this to initialize the actual stream converter (us).
01082 NS_IMETHODIMP nsStreamConverter::AsyncConvertData(const char   *aFromType, 
01083                                                   const char   *aToType,
01084                                                   nsIStreamListener *aListener, 
01085                                                   nsISupports       *aCtxt) 
01086 {
01087   nsresult rv = NS_OK;
01088   nsCOMPtr<nsIMsgQuote> aMsgQuote = do_QueryInterface(aCtxt, &rv);
01089   nsCOMPtr<nsIChannel> aChannel;
01090 
01091   if (aMsgQuote)
01092   {
01093     nsCOMPtr<nsIMimeStreamConverterListener> quoteListener;
01094     rv = aMsgQuote->GetQuoteListener(getter_AddRefs(quoteListener));
01095     if (quoteListener)
01096       SetMimeHeadersListener(quoteListener, nsMimeOutput::nsMimeMessageQuoting);
01097     rv = aMsgQuote->GetQuoteChannel(getter_AddRefs(aChannel));
01098   }
01099   else
01100   {
01101     aChannel = do_QueryInterface(aCtxt, &rv);
01102   }
01103 
01104   mFromType = aFromType;
01105   mToType = aToType;
01106 
01107   NS_ASSERTION(aChannel && NS_SUCCEEDED(rv), "mailnews mime converter has to have the channel passed in...");
01108   if (NS_FAILED(rv)) return rv;
01109 
01110   nsCOMPtr<nsIURI> aUri;
01111   aChannel->GetURI(getter_AddRefs(aUri));
01112   return Init(aUri, aListener, aChannel);
01113 }
01114 
01115 NS_IMETHODIMP nsStreamConverter::FirePendingStartRequest()
01116 {
01117   if (mPendingRequest && mOutListener)
01118   {
01119        mOutListener->OnStartRequest(mPendingRequest, mPendingContext);
01120     mPendingRequest = nsnull;
01121     mPendingContext = nsnull; 
01122   }
01123   return NS_OK;
01124 }