Back to index

lightning-sunbird  0.9+nobinonly
nsMimeBaseEmitter.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; 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) 1999
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *   Henrik Gemal <mozilla@gemal.dk>
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 
00040 #include "nsCOMPtr.h"
00041 #include "nsXPIDLString.h"
00042 #include "nsReadableUtils.h"
00043 #include <stdio.h>
00044 #include "nsMimeBaseEmitter.h"
00045 #include "nsMailHeaders.h"
00046 #include "nscore.h"
00047 #include "nsIPrefService.h"
00048 #include "nsIPrefBranch.h"
00049 #include "nsIServiceManager.h"
00050 #include "nsEscape.h"
00051 #include "prmem.h"
00052 #include "nsEmitterUtils.h"
00053 #include "nsFileStream.h"
00054 #include "nsMimeStringResources.h"
00055 #include "msgCore.h"
00056 #include "nsIMsgHeaderParser.h"
00057 #include "nsIComponentManager.h"
00058 #include "nsEmitterUtils.h"
00059 #include "nsFileSpec.h"
00060 #include "nsIRegistry.h"
00061 #include "nsIMimeStreamConverter.h"
00062 #include "nsIMimeConverter.h"
00063 #include "nsMsgMimeCID.h"
00064 #include "prlog.h"
00065 #include "prprf.h"
00066 #include "nsIMimeHeaders.h"
00067 #include "nsIMsgWindow.h"
00068 #include "nsIMsgMailNewsUrl.h"
00069 
00070 static PRLogModuleInfo * gMimeEmitterLogModule = nsnull;
00071 
00072 #define   MIME_HEADER_URL      "chrome://messenger/locale/mimeheader.properties"
00073 #define   MIME_URL             "chrome://messenger/locale/mime.properties"
00074 
00075 NS_IMPL_THREADSAFE_ADDREF(nsMimeBaseEmitter)
00076 NS_IMPL_THREADSAFE_RELEASE(nsMimeBaseEmitter)
00077 
00078 NS_INTERFACE_MAP_BEGIN(nsMimeBaseEmitter)
00079    NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIMimeEmitter)
00080    NS_INTERFACE_MAP_ENTRY(nsIMimeEmitter)
00081    NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
00082 NS_INTERFACE_MAP_END
00083 
00084 nsMimeBaseEmitter::nsMimeBaseEmitter()
00085 {
00086   // Initialize data output vars...
00087   mFirstHeaders = PR_TRUE;
00088 
00089   mBufferMgr = nsnull;
00090   mTotalWritten = 0;
00091   mTotalRead = 0;
00092   mInputStream = nsnull;
00093   mOutStream = nsnull;
00094   mOutListener = nsnull;
00095   mChannel = nsnull;
00096 
00097   // Display output control vars...
00098   mDocHeader = PR_FALSE;
00099   m_stringBundle = nsnull;
00100   mURL = nsnull;
00101   mHeaderDisplayType = nsMimeHeaderDisplayTypes::NormalHeaders;
00102 
00103   // Setup array for attachments
00104   mAttachCount = 0;
00105   mAttachArray = new nsVoidArray();
00106   mCurrentAttachment = nsnull;
00107 
00108   // Header cache...
00109   mHeaderArray = new nsVoidArray();
00110 
00111   // Embedded Header Cache...
00112   mEmbeddedHeaderArray = nsnull;
00113 
00114   // HTML Header Data...
00115 //  mHTMLHeaders = "";
00116 //  mCharset = "";
00117 
00118   // Init the body...
00119   mBodyStarted = PR_FALSE;
00120 //  mBody = "";
00121 
00122   // This is needed for conversion of I18N Strings...
00123   mUnicodeConverter = do_GetService(NS_MIME_CONVERTER_CONTRACTID);
00124 
00125   if (!gMimeEmitterLogModule)
00126     gMimeEmitterLogModule = PR_NewLogModule("MIME");
00127 
00128   // Do prefs last since we can live without this if it fails...
00129   nsCOMPtr<nsIPrefBranch> pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
00130   if (pPrefBranch)
00131     pPrefBranch->GetIntPref("mail.show_headers", &mHeaderDisplayType);
00132 }
00133 
00134 nsMimeBaseEmitter::~nsMimeBaseEmitter(void)
00135 {
00136   PRInt32 i;
00137 
00138   // Delete the buffer manager...
00139   if (mBufferMgr)
00140   {
00141     delete mBufferMgr;
00142     mBufferMgr = nsnull;
00143   }
00144 
00145   // Clean up the attachment array structures...
00146   if (mAttachArray)
00147   {
00148     for (i=0; i<mAttachArray->Count(); i++)
00149     {
00150       attachmentInfoType *attachInfo = (attachmentInfoType *)mAttachArray->ElementAt(i);
00151       if (!attachInfo)
00152         continue;
00153     
00154       PR_FREEIF(attachInfo->contentType);
00155       PR_FREEIF(attachInfo->displayName);
00156       PR_FREEIF(attachInfo->urlSpec);
00157       PR_FREEIF(attachInfo);
00158     }
00159     delete mAttachArray;
00160   }
00161 
00162   // Cleanup allocated header arrays...
00163   CleanupHeaderArray(mHeaderArray);
00164   mHeaderArray = nsnull;
00165 
00166   CleanupHeaderArray(mEmbeddedHeaderArray);
00167   mEmbeddedHeaderArray = nsnull;
00168 }
00169 
00170 NS_IMETHODIMP nsMimeBaseEmitter::GetInterface(const nsIID & aIID, void * *aInstancePtr)
00171 {
00172   NS_ENSURE_ARG_POINTER(aInstancePtr);
00173   return QueryInterface(aIID, aInstancePtr);
00174 }
00175 
00176 void
00177 nsMimeBaseEmitter::CleanupHeaderArray(nsVoidArray *aArray)
00178 {
00179   if (!aArray)
00180     return;
00181 
00182   for (PRInt32 i=0; i<aArray->Count(); i++)
00183   {
00184     headerInfoType *headerInfo = (headerInfoType *)aArray->ElementAt(i);
00185     if (!headerInfo)
00186       continue;
00187     
00188     PR_FREEIF(headerInfo->name);
00189     PR_FREEIF(headerInfo->value);
00190     PR_FREEIF(headerInfo);
00191   }
00192 
00193   delete aArray;
00194 }
00195 
00196 static PRInt32 MapHeaderNameToID(const char *header)
00197 {
00198   // emitter passes UPPERCASE for header names
00199   if (!strcmp(header, "DATE"))
00200     return MIME_MHTML_DATE;
00201   else if (!strcmp(header, "FROM"))
00202     return MIME_MHTML_FROM;
00203   else if (!strcmp(header, "SUBJECT"))
00204     return MIME_MHTML_SUBJECT;
00205   else if (!strcmp(header, "TO"))
00206     return MIME_MHTML_TO;
00207   else if (!strcmp(header, "SENDER"))
00208     return MIME_MHTML_SENDER;
00209   else if (!strcmp(header, "RESENT-TO"))
00210     return MIME_MHTML_RESENT_TO;
00211   else if (!strcmp(header, "RESENT-SENDER"))
00212     return MIME_MHTML_RESENT_SENDER;
00213   else if (!strcmp(header, "RESENT-FROM"))
00214     return MIME_MHTML_RESENT_FROM;
00215   else if (!strcmp(header, "RESENT-CC"))
00216     return MIME_MHTML_RESENT_CC;
00217   else if (!strcmp(header, "REPLY-TO"))
00218     return MIME_MHTML_REPLY_TO;
00219   else if (!strcmp(header, "REFERENCES"))
00220     return MIME_MHTML_REFERENCES;
00221   else if (!strcmp(header, "NEWSGROUPS"))
00222     return MIME_MHTML_NEWSGROUPS;
00223   else if (!strcmp(header, "MESSAGE-ID"))
00224     return MIME_MHTML_MESSAGE_ID;
00225   else if (!strcmp(header, "FOLLOWUP-TO"))
00226     return MIME_MHTML_FOLLOWUP_TO;
00227   else if (!strcmp(header, "CC"))
00228     return MIME_MHTML_CC;
00229   else if (!strcmp(header, "ORGANIZATION"))
00230     return MIME_MHTML_ORGANIZATION;
00231   else if (!strcmp(header, "BCC"))
00232     return MIME_MHTML_BCC;
00233 
00234   return 0;
00235 }
00236 
00237 char *
00238 nsMimeBaseEmitter::MimeGetStringByName(const char *aHeaderName)
00239 {
00240        nsresult res = NS_OK;
00241 
00242        if (!m_headerStringBundle)
00243        {
00244               static const char propertyURL[] = MIME_HEADER_URL;
00245 
00246               nsCOMPtr<nsIStringBundleService> sBundleService = 
00247                        do_GetService(NS_STRINGBUNDLE_CONTRACTID, &res); 
00248               if (NS_SUCCEEDED(res) && (nsnull != sBundleService)) 
00249               {
00250                      res = sBundleService->CreateBundle(propertyURL, getter_AddRefs(m_headerStringBundle));
00251               }
00252        }
00253 
00254        if (m_headerStringBundle)
00255        {
00256     nsXPIDLString val;
00257 
00258     res = m_headerStringBundle->GetStringFromName(NS_ConvertASCIItoUCS2(aHeaderName).get(), 
00259                                                   getter_Copies(val));
00260 
00261     if (NS_FAILED(res)) 
00262       return nsnull;
00263     
00264     // Here we need to return a new copy of the string
00265     // This returns a UTF-8 string so the caller needs to perform a conversion 
00266     // if this is used as UCS-2 (e.g. cannot do nsString(utfStr);
00267     //
00268     return ToNewUTF8String(val);
00269        }
00270        else
00271        {
00272     return nsnull;
00273        }
00274 }
00275 
00276 char *
00277 nsMimeBaseEmitter::MimeGetStringByID(PRInt32 aID)
00278 {
00279   nsresult res = NS_OK;
00280 
00281   if (!m_stringBundle)
00282   {
00283     static const char propertyURL[] = MIME_URL;
00284 
00285     nsCOMPtr<nsIStringBundleService> sBundleService = 
00286                             do_GetService(NS_STRINGBUNDLE_CONTRACTID, &res); 
00287     if (NS_SUCCEEDED(res)) 
00288       res = sBundleService->CreateBundle(propertyURL, getter_AddRefs(m_stringBundle));
00289   }
00290 
00291   if (m_stringBundle)
00292   {
00293     nsXPIDLString val;
00294 
00295     res = m_stringBundle->GetStringFromID(aID, getter_Copies(val));
00296 
00297     if (NS_FAILED(res)) 
00298       return nsnull;
00299 
00300     return ToNewUTF8String(val);
00301   }
00302   else
00303     return nsnull;
00304 }
00305 
00306 // 
00307 // This will search a string bundle (eventually) to find a descriptive header 
00308 // name to match what was found in the mail message. aHeaderName is passed in
00309 // in all caps and a dropback default name is provided. The caller needs to free
00310 // the memory returned by this function.
00311 //
00312 char *
00313 nsMimeBaseEmitter::LocalizeHeaderName(const char *aHeaderName, const char *aDefaultName)
00314 {
00315   char *retVal = nsnull;
00316 
00317   // prefer to use translated strings if not for quoting
00318   if (mFormat != nsMimeOutput::nsMimeMessageQuoting &&
00319       mFormat != nsMimeOutput::nsMimeMessageBodyQuoting)
00320   {
00321     // map name to id and get the translated string
00322     PRInt32 id = MapHeaderNameToID(aHeaderName);
00323     if (id > 0)
00324       retVal = MimeGetStringByID(id);
00325   }
00326   
00327   // get the string from the other bundle (usually not translated)
00328   if (!retVal)
00329     retVal = MimeGetStringByName(aHeaderName);
00330 
00331   if (retVal)
00332     return retVal;
00333   else
00334     return nsCRT::strdup(aDefaultName);
00335 }
00336 
00338 // nsMimeBaseEmitter Interface
00340 NS_IMETHODIMP
00341 nsMimeBaseEmitter::SetPipe(nsIInputStream * aInputStream, nsIOutputStream *outStream)
00342 {
00343   mInputStream = aInputStream;
00344   mOutStream = outStream;
00345   return NS_OK;
00346 }
00347 
00348 // Note - these is setup only...you should not write
00349 // anything to the stream since these may be image data
00350 // output streams, etc...
00351 NS_IMETHODIMP       
00352 nsMimeBaseEmitter::Initialize(nsIURI *url, nsIChannel * aChannel, PRInt32 aFormat)
00353 {
00354   // set the url
00355   mURL = url;
00356   mChannel = aChannel;
00357 
00358   // Create rebuffering object
00359   if (mBufferMgr)
00360   {
00361     delete mBufferMgr;
00362   }
00363   mBufferMgr = new MimeRebuffer();
00364 
00365   // Counters for output stream
00366   mTotalWritten = 0;
00367   mTotalRead = 0;
00368   mFormat = aFormat;
00369 
00370   return NS_OK;
00371 }
00372 
00373 NS_IMETHODIMP
00374 nsMimeBaseEmitter::SetOutputListener(nsIStreamListener *listener)
00375 {
00376   mOutListener = listener;
00377   return NS_OK;
00378 }
00379 
00380 NS_IMETHODIMP
00381 nsMimeBaseEmitter::GetOutputListener(nsIStreamListener **listener)
00382 {
00383   if (listener)
00384   {
00385     *listener = mOutListener;
00386     NS_IF_ADDREF(*listener);
00387   }
00388   return NS_OK;
00389 }
00390 
00391 
00392 // Attachment handling routines
00393 nsresult
00394 nsMimeBaseEmitter::StartAttachment(const char *name, const char *contentType, const char *url,
00395                                    PRBool aIsExternalAttachment)
00396 {
00397   // Ok, now we will setup the attachment info 
00398   mCurrentAttachment = (attachmentInfoType *) PR_NEWZAP(attachmentInfoType);
00399   if ( (mCurrentAttachment) && mAttachArray)
00400   {
00401     ++mAttachCount;
00402 
00403     mCurrentAttachment->displayName = nsCRT::strdup(name);
00404     mCurrentAttachment->urlSpec = nsCRT::strdup(url);
00405     mCurrentAttachment->contentType = nsCRT::strdup(contentType);
00406     mCurrentAttachment->isExternalAttachment = aIsExternalAttachment;
00407   }
00408 
00409   return NS_OK;
00410 }
00411 
00412 nsresult
00413 nsMimeBaseEmitter::EndAttachment()
00414 {
00415   // Ok, add the attachment info to the attachment array...
00416   if ( (mCurrentAttachment) && (mAttachArray) )
00417   {
00418     mAttachArray->AppendElement(mCurrentAttachment);
00419     mCurrentAttachment = nsnull;
00420   }
00421 
00422   return NS_OK;
00423 }
00424 
00425 nsresult
00426 nsMimeBaseEmitter::EndAllAttachments()
00427 {
00428        return NS_OK;
00429 }
00430 
00431 NS_IMETHODIMP
00432 nsMimeBaseEmitter::AddAttachmentField(const char *field, const char *value)
00433 {
00434   return NS_OK;
00435 }
00436 
00437 NS_IMETHODIMP
00438 nsMimeBaseEmitter::UtilityWrite(const char *buf)
00439 {
00440   PRInt32     tmpLen = strlen(buf);
00441   PRUint32    written;
00442 
00443   Write(buf, tmpLen, &written);
00444 
00445   return NS_OK;
00446 }
00447 
00448 NS_IMETHODIMP
00449 nsMimeBaseEmitter::UtilityWriteCRLF(const char *buf)
00450 {
00451   PRInt32     tmpLen = strlen(buf);
00452   PRUint32    written;
00453 
00454   Write(buf, tmpLen, &written);
00455   Write(CRLF, 2, &written);
00456 
00457   return NS_OK;
00458 }
00459 
00460 NS_IMETHODIMP
00461 nsMimeBaseEmitter::Write(const char *buf, PRUint32 size, PRUint32 *amountWritten)
00462 {
00463   unsigned int        written = 0;
00464   nsresult rv = NS_OK;
00465   PRUint32            needToWrite;
00466 
00467 #ifdef DEBUG_BenB
00468   // If you want to see libmime output...
00469   printf("%s", buf);
00470 #endif
00471 
00472   PR_LOG(gMimeEmitterLogModule, PR_LOG_ALWAYS, (buf));
00473   //
00474   // Make sure that the buffer we are "pushing" into has enough room
00475   // for the write operation. If not, we have to buffer, return, and get
00476   // it on the next time through
00477   //
00478   *amountWritten = 0;
00479 
00480   needToWrite = mBufferMgr->GetSize();
00481   // First, handle any old buffer data...
00482   if (needToWrite > 0)
00483   {
00484     rv = WriteHelper(mBufferMgr->GetBuffer(), needToWrite, &written);
00485 
00486     mTotalWritten += written;
00487     mBufferMgr->ReduceBuffer(written);
00488     *amountWritten = written;
00489 
00490     // if we couldn't write all the old data, buffer the new data
00491     // and return
00492     if (mBufferMgr->GetSize() > 0)
00493     {
00494       mBufferMgr->IncreaseBuffer(buf, size);
00495       return rv;
00496     }
00497   }
00498 
00499 
00500   // if we get here, we are dealing with new data...try to write
00501   // and then do the right thing...
00502   rv = WriteHelper(buf, size, &written);
00503   *amountWritten = written;
00504   mTotalWritten += written;
00505 
00506   if (written < size)
00507     mBufferMgr->IncreaseBuffer(buf+written, (size-written));
00508 
00509   return rv;
00510 }
00511 
00512 nsresult
00513 nsMimeBaseEmitter::WriteHelper(const char *buf, PRUint32 count, PRUint32 *countWritten)
00514 {
00515   nsresult rv;
00516 
00517   rv = mOutStream->Write(buf, count, countWritten);
00518   if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
00519     // pipe is full, push contents of pipe to listener...
00520     PRUint32 avail;
00521     rv = mInputStream->Available(&avail);
00522     if (NS_SUCCEEDED(rv) && avail) {
00523       mOutListener->OnDataAvailable(mChannel, mURL, mInputStream, 0, avail);
00524 
00525       // try writing again...
00526       rv = mOutStream->Write(buf, count, countWritten);
00527     }
00528   }
00529   return rv;
00530 }
00531 
00532 //
00533 // Find a cached header! Note: Do NOT free this value!
00534 //
00535 const char *
00536 nsMimeBaseEmitter::GetHeaderValue(const char  *aHeaderName)
00537 {
00538   PRInt32     i;
00539   char        *retVal = nsnull;
00540   nsVoidArray *array = mDocHeader? mHeaderArray : mEmbeddedHeaderArray;
00541 
00542   if (!array)
00543     return nsnull;
00544 
00545   for (i = 0; i < array->Count(); i++)
00546   {
00547     headerInfoType *headerInfo = (headerInfoType *)array->ElementAt(i);
00548     if ( (!headerInfo) || (!headerInfo->name) || (!(*headerInfo->name)) )
00549       continue;
00550     
00551     if (!nsCRT::strcasecmp(aHeaderName, headerInfo->name))
00552     {
00553       retVal = headerInfo->value;
00554       break;
00555     }
00556   }
00557 
00558   return retVal;
00559 }
00560 
00561 //
00562 // This is called at the start of the header block for all header information in ANY
00563 // AND ALL MESSAGES (yes, quoted, attached, etc...) 
00564 //
00565 // NOTE: This will be called even when headers are will not follow. This is
00566 // to allow us to be notified of the charset of the original message. This is
00567 // important for forward and reply operations
00568 //
00569 NS_IMETHODIMP
00570 nsMimeBaseEmitter::StartHeader(PRBool rootMailHeader, PRBool headerOnly, const char *msgID,
00571                                const char *outCharset)
00572 {
00573   mDocHeader = rootMailHeader;
00574 
00575   // If this is not the mail messages header, then we need to create 
00576   // the mEmbeddedHeaderArray structure for use with this internal header 
00577   // structure.
00578   if (!mDocHeader)
00579   {
00580     if (mEmbeddedHeaderArray)
00581       CleanupHeaderArray(mEmbeddedHeaderArray);
00582 
00583     mEmbeddedHeaderArray = new nsVoidArray();
00584     if (!mEmbeddedHeaderArray)
00585       return NS_ERROR_OUT_OF_MEMORY;
00586   }
00587 
00588   // If the main doc, check on updated character set
00589   if (mDocHeader)
00590     UpdateCharacterSet(outCharset);
00591 
00592   mCharset.AssignWithConversion(outCharset);
00593   return NS_OK; 
00594 }
00595 
00596 // Ok, if we are here, and we have a aCharset passed in that is not
00597 // UTF-8 or US-ASCII, then we should tag the mChannel member with this
00598 // charset. This is because replying to messages with specified charset's
00599 // need to be tagged as that charset by default.
00600 //
00601 NS_IMETHODIMP
00602 nsMimeBaseEmitter::UpdateCharacterSet(const char *aCharset)
00603 {
00604   if ( (aCharset) && (PL_strcasecmp(aCharset, "US-ASCII")) &&
00605         (PL_strcasecmp(aCharset, "ISO-8859-1")) &&
00606         (PL_strcasecmp(aCharset, "UTF-8")) )
00607   {
00608     nsCAutoString contentType;
00609     
00610     if (NS_SUCCEEDED(mChannel->GetContentType(contentType)) && !contentType.IsEmpty())
00611     {
00612       char *cBegin = contentType.BeginWriting();
00613 
00614       const char *cPtr = PL_strcasestr(cBegin, "charset=");
00615 
00616       if (cPtr)
00617       {
00618         char  *ptr = cBegin;
00619         while (*ptr)
00620         {
00621           if ( (*ptr == ' ') || (*ptr == ';') ) 
00622           {
00623             if ((ptr + 1) >= cPtr)
00624             {
00625               *ptr = '\0';
00626               break;
00627             }
00628           }
00629 
00630           ++ptr;
00631         }
00632       }
00633 
00634       // have to set content-type since it could have an embedded null byte
00635       mChannel->SetContentType(nsDependentCString(cBegin));
00636       mChannel->SetContentCharset(nsDependentCString(aCharset));
00637     }
00638   }
00639 
00640   return NS_OK;
00641 }
00642 
00643 //
00644 // This will be called for every header field regardless if it is in an
00645 // internal body or the outer message. 
00646 //
00647 NS_IMETHODIMP
00648 nsMimeBaseEmitter::AddHeaderField(const char *field, const char *value)
00649 {
00650   if ( (!field) || (!value) )
00651     return NS_OK;
00652 
00653   nsVoidArray   *tPtr;
00654   if (mDocHeader)
00655     tPtr = mHeaderArray;
00656   else
00657     tPtr = mEmbeddedHeaderArray;
00658 
00659   // This is a header so we need to cache and output later.
00660   // Ok, now we will setup the header info for the header array!
00661   headerInfoType  *ptr = (headerInfoType *) PR_NEWZAP(headerInfoType);
00662   if ( (ptr) && tPtr)
00663   {
00664     ptr->name = nsCRT::strdup(field);
00665     ptr->value = nsCRT::strdup(value);
00666     tPtr->AppendElement(ptr);
00667   }
00668 
00669   return NS_OK;
00670 }
00671 
00672 NS_IMETHODIMP
00673 nsMimeBaseEmitter::AddAllHeaders(const char *allheaders, 
00674                                  const PRInt32 allheadersize)
00675 {
00676   if (mDocHeader) //We want to set only the main headers of a message, not the potentially embedded one
00677   {
00678     nsresult rv;
00679     nsCOMPtr<nsIMsgMailNewsUrl> msgurl (do_QueryInterface(mURL));
00680     if (msgurl)
00681     {
00682         nsCOMPtr<nsIMimeHeaders> mimeHeaders = do_CreateInstance(NS_IMIMEHEADERS_CONTRACTID, &rv);
00683         NS_ENSURE_SUCCESS(rv,rv);
00684         mimeHeaders->Initialize(allheaders, allheadersize);
00685         msgurl->SetMimeHeaders(mimeHeaders);
00686     }
00687   }
00688   return NS_OK;
00689 }
00690 
00692 // The following code is responsible for formatting headers in a manner that is
00693 // identical to the normal XUL output.
00695 
00696 nsresult
00697 nsMimeBaseEmitter::WriteHeaderFieldHTML(const char *field, const char *value)
00698 {
00699   char  *newValue = nsnull;
00700 
00701   if ( (!field) || (!value) )
00702     return NS_OK;
00703 
00704   //
00705   // This is a check to see what the pref is for header display. If
00706   // We should only output stuff that corresponds with that setting.
00707   //
00708   if (!EmitThisHeaderForPrefSetting(mHeaderDisplayType, field))
00709     return NS_OK;
00710 
00711   if ( (mUnicodeConverter) && (mFormat != nsMimeOutput::nsMimeMessageSaveAs) )
00712   {
00713     nsXPIDLCString tValue;
00714 
00715     // we're going to need a converter to convert
00716     nsresult rv = mUnicodeConverter->DecodeMimeHeader(value, getter_Copies(tValue));
00717     if (NS_SUCCEEDED(rv) && tValue)
00718     {
00719       newValue = nsEscapeHTML(tValue);
00720     }
00721     else
00722     {
00723       newValue = nsEscapeHTML(value);
00724     }
00725   }
00726   else
00727   {
00728     newValue = nsCRT::strdup(value);
00729   }
00730 
00731   if (!newValue)
00732     return NS_OK;
00733 
00734   mHTMLHeaders.Append("<tr>");
00735   mHTMLHeaders.Append("<td>");
00736 
00737   if (mFormat == nsMimeOutput::nsMimeMessageSaveAs)
00738     mHTMLHeaders.Append("<b>");
00739   else
00740     mHTMLHeaders.Append("<div class=\"headerdisplayname\" style=\"display:inline;\">");
00741 
00742   // Here is where we are going to try to L10N the tagName so we will always
00743   // get a field name next to an emitted header value. Note: Default will always
00744   // be the name of the header itself.
00745   //
00746   nsCAutoString  newTagName(field);
00747   newTagName.CompressWhitespace(PR_TRUE, PR_TRUE);
00748   ToUpperCase(newTagName);
00749 
00750   char *l10nTagName = LocalizeHeaderName(newTagName.get(), field);
00751   if ( (!l10nTagName) || (!*l10nTagName) )
00752     mHTMLHeaders.Append(field);
00753   else
00754   {
00755     mHTMLHeaders.Append(l10nTagName);
00756     PR_FREEIF(l10nTagName);
00757   }
00758 
00759   mHTMLHeaders.Append(": ");
00760   if (mFormat == nsMimeOutput::nsMimeMessageSaveAs)
00761     mHTMLHeaders.Append("</b>");
00762   else
00763     mHTMLHeaders.Append("</div>");
00764 
00765   // Now write out the actual value itself and move on!
00766   //
00767   mHTMLHeaders.Append(newValue);
00768   mHTMLHeaders.Append("</td>");
00769 
00770   mHTMLHeaders.Append("</tr>");
00771 
00772   PR_FREEIF(newValue);
00773   return NS_OK;
00774 }
00775 
00776 nsresult
00777 nsMimeBaseEmitter::WriteHeaderFieldHTMLPrefix()
00778 {
00779   if ( 
00780       ( (mFormat == nsMimeOutput::nsMimeMessageSaveAs) && (mFirstHeaders) ) ||
00781       ( (mFormat == nsMimeOutput::nsMimeMessagePrintOutput) && (mFirstHeaders) )
00782      )
00783      /* DO NOTHING */ ;   // rhp: Do nothing...leaving the conditional like this so its 
00784                           //      easier to see the logic of what is going on. 
00785   else
00786     mHTMLHeaders.Append("<br><hr width=\"90%\" size=4><br>");
00787 
00788   mFirstHeaders = PR_FALSE;
00789   return NS_OK;
00790 }
00791 
00792 nsresult
00793 nsMimeBaseEmitter::WriteHeaderFieldHTMLPostfix()
00794 {
00795   mHTMLHeaders.Append("<br>");
00796   return NS_OK;
00797 }
00798 
00799 NS_IMETHODIMP
00800 nsMimeBaseEmitter::WriteHTMLHeaders()
00801 {
00802   WriteHeaderFieldHTMLPrefix();
00803 
00804   // Start with the subject, from date info!
00805   DumpSubjectFromDate();
00806 
00807   // Continue with the to and cc headers
00808   DumpToCC();
00809 
00810   // Do the rest of the headers, but these will only be written if
00811   // the user has the "show all headers" pref set
00812   if (mHeaderDisplayType == nsMimeHeaderDisplayTypes::AllHeaders)
00813     DumpRestOfHeaders();
00814 
00815   WriteHeaderFieldHTMLPostfix();
00816 
00817   // Now, we need to either append the headers we built up to the 
00818   // overall body or output to the stream.
00819   UtilityWriteCRLF(mHTMLHeaders.get());
00820 
00821   mHTMLHeaders = "";
00822 
00823   return NS_OK;
00824 }
00825 
00826 nsresult
00827 nsMimeBaseEmitter::DumpSubjectFromDate()
00828 {
00829   mHTMLHeaders.Append("<table border=0 cellspacing=0 cellpadding=0 width=\"100%\" class=\"header-part1\">");
00830 
00831     // This is the envelope information
00832     OutputGenericHeader(HEADER_SUBJECT);
00833     OutputGenericHeader(HEADER_FROM);
00834     OutputGenericHeader(HEADER_DATE);
00835 
00836     // If we are Quoting a message, then we should dump the To: also
00837     if ( ( mFormat == nsMimeOutput::nsMimeMessageQuoting ) ||
00838          ( mFormat == nsMimeOutput::nsMimeMessageBodyQuoting ) )
00839       OutputGenericHeader(HEADER_TO);
00840 
00841   mHTMLHeaders.Append("</table>");
00842  
00843   return NS_OK;
00844 }
00845 
00846 nsresult
00847 nsMimeBaseEmitter::DumpToCC()
00848 {
00849   const char * toField = GetHeaderValue(HEADER_TO);
00850   const char * ccField = GetHeaderValue(HEADER_CC);
00851   const char * bccField = GetHeaderValue(HEADER_BCC);
00852   const char * newsgroupField = GetHeaderValue(HEADER_NEWSGROUPS);
00853 
00854   // only dump these fields if we have at least one of them! When displaying news
00855   // messages that didn't have a To or Cc field, we'd always get an empty box
00856   // which looked weird.
00857   if (toField || ccField || bccField || newsgroupField)
00858   {
00859     mHTMLHeaders.Append("<table border=0 cellspacing=0 cellpadding=0 width=\"100%\" class=\"header-part2\">");
00860 
00861     if (toField)
00862       WriteHeaderFieldHTML(HEADER_TO, toField);
00863     if (ccField)
00864       WriteHeaderFieldHTML(HEADER_CC, ccField);
00865     if (bccField)
00866       WriteHeaderFieldHTML(HEADER_BCC, bccField);
00867     if (newsgroupField)
00868       WriteHeaderFieldHTML(HEADER_NEWSGROUPS, newsgroupField);
00869 
00870     mHTMLHeaders.Append("</table>");
00871   }
00872 
00873   return NS_OK;
00874 }
00875 
00876 nsresult
00877 nsMimeBaseEmitter::DumpRestOfHeaders()
00878 {
00879   PRInt32     i;
00880   nsVoidArray *array = mDocHeader? mHeaderArray : mEmbeddedHeaderArray;
00881 
00882   mHTMLHeaders.Append("<table border=0 cellspacing=0 cellpadding=0 width=\"100%\" class=\"header-part3\">");
00883   
00884   for (i = 0; i < array->Count(); i++)
00885   {
00886     headerInfoType *headerInfo = (headerInfoType *)array->ElementAt(i);
00887     if ( (!headerInfo) || (!headerInfo->name) || (!(*headerInfo->name)) ||
00888       (!headerInfo->value) || (!(*headerInfo->value)))
00889       continue;
00890     
00891     if ( (!nsCRT::strcasecmp(HEADER_SUBJECT, headerInfo->name)) ||
00892       (!nsCRT::strcasecmp(HEADER_DATE, headerInfo->name)) ||
00893       (!nsCRT::strcasecmp(HEADER_FROM, headerInfo->name)) ||
00894       (!nsCRT::strcasecmp(HEADER_TO, headerInfo->name)) ||
00895       (!nsCRT::strcasecmp(HEADER_CC, headerInfo->name)) )
00896       continue;
00897     
00898     WriteHeaderFieldHTML(headerInfo->name, headerInfo->value);
00899   }
00900   
00901   mHTMLHeaders.Append("</table>");
00902   return NS_OK;
00903 }
00904 
00905 nsresult
00906 nsMimeBaseEmitter::OutputGenericHeader(const char *aHeaderVal)
00907 {
00908   const char *val = GetHeaderValue(aHeaderVal);
00909 
00910   if (val)
00911     return WriteHeaderFieldHTML(aHeaderVal, val);
00912 
00913   return NS_ERROR_FAILURE;
00914 }
00915 
00919 // These are the methods that should be implemented by the child class!
00923 
00924 //
00925 // This should be implemented by the child class if special processing
00926 // needs to be done when the entire message is read.
00927 //
00928 NS_IMETHODIMP
00929 nsMimeBaseEmitter::Complete()
00930 {
00931   // If we are here and still have data to write, we should try
00932   // to flush it...if we try and fail, we should probably return
00933   // an error!
00934   PRUint32      written;
00935 
00936   nsresult rv = NS_OK;
00937   while ( NS_SUCCEEDED(rv) && (mBufferMgr) && (mBufferMgr->GetSize() > 0))
00938     rv = Write("", 0, &written);
00939 
00940   if (mOutListener)
00941   {
00942     PRUint32 bytesInStream = 0;
00943     nsresult rv2 = mInputStream->Available(&bytesInStream);
00944          NS_ASSERTION(NS_SUCCEEDED(rv2), "Available failed");
00945     if (bytesInStream)
00946     {
00947       nsCOMPtr<nsIRequest> request = do_QueryInterface(mChannel);
00948       rv2 = mOutListener->OnDataAvailable(request, mURL, mInputStream, 0, bytesInStream);
00949     }
00950   }
00951 
00952   return NS_OK;
00953 }
00954 
00955 //
00956 // This needs to do the right thing with the stored information. It only 
00957 // has to do the output functions, this base class will take care of the 
00958 // memory cleanup
00959 //
00960 NS_IMETHODIMP
00961 nsMimeBaseEmitter::EndHeader()
00962 {
00963   return NS_OK;
00964 }
00965 
00966 // body handling routines
00967 NS_IMETHODIMP
00968 nsMimeBaseEmitter::StartBody(PRBool bodyOnly, const char *msgID, const char *outCharset)
00969 {
00970   return NS_OK;
00971 }
00972 
00973 NS_IMETHODIMP
00974 nsMimeBaseEmitter::WriteBody(const char *buf, PRUint32 size, PRUint32 *amountWritten)
00975 {
00976   return NS_OK;
00977 }
00978 
00979 NS_IMETHODIMP
00980 nsMimeBaseEmitter::EndBody()
00981 {
00982   return NS_OK;
00983 }