Back to index

lightning-sunbird  0.9+nobinonly
mimemoz2.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) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 #include "prlog.h"
00039 #include "nsCOMPtr.h"
00040 #include "modlmime.h"
00041 #include "nsCRT.h"
00042 #include "mimeobj.h"
00043 #include "mimemsg.h"
00044 #include "mimetric.h"   /* for MIME_RichtextConverter */
00045 #include "mimethtm.h"
00046 #include "mimemsig.h"
00047 #include "mimemrel.h"
00048 #include "mimemalt.h"
00049 #include "mimebuf.h"
00050 #include "mimemapl.h"
00051 #include "prprf.h"
00052 #include "mimei.h"      /* for moved MimeDisplayData struct */
00053 #include "mimebuf.h"
00054 #include "prmem.h"
00055 #include "plstr.h"
00056 #include "prmem.h"
00057 #include "mimemoz2.h"
00058 #include "nsIPrefService.h"
00059 #include "nsIPrefBranch.h"
00060 #include "nsIServiceManager.h"
00061 #include "nsFileSpec.h"
00062 #include "comi18n.h"
00063 #include "nsIStringBundle.h"
00064 #include "nsString.h"
00065 #include "nsReadableUtils.h"
00066 #include "nsMimeStringResources.h"
00067 #include "nsStreamConverter.h"
00068 #include "nsIMsgSend.h"
00069 #include "nsIMsgMailNewsUrl.h"
00070 #include "nsSpecialSystemDirectory.h"
00071 #include "mozITXTToHTMLConv.h"
00072 #include "nsCExternalHandlerService.h"
00073 #include "nsIMIMEService.h"
00074 #include "nsIImapUrl.h"
00075 #include "nsMsgI18N.h"
00076 #include "nsICharsetConverterManager.h"
00077 #include "nsICharsetAlias.h"
00078 #include "nsMimeTypes.h"
00079 #include "nsIIOService.h"
00080 #include "nsIURI.h"
00081 #include "nsNetCID.h"
00082 #include "nsIMsgWindow.h"
00083 #include "nsMsgUtils.h"
00084 #include "nsIChannel.h"
00085 #include "nsICacheEntryDescriptor.h"
00086 #include "nsICacheSession.h"
00087 #include "nsITransport.h"
00088 #include "mimeebod.h"
00089 #include "mimeeobj.h"
00090 // <for functions="HTML2Plaintext,HTMLSantinize">
00091 #include "nsXPCOM.h"
00092 #include "nsParserCIID.h"
00093 #include "nsIParser.h"
00094 #include "nsIHTMLContentSink.h"
00095 #include "nsIContentSerializer.h"
00096 #include "nsLayoutCID.h"
00097 #include "nsIComponentManager.h"
00098 #include "nsReadableUtils.h"
00099 #include "nsIHTMLToTextSink.h"
00100 #include "mozISanitizingSerializer.h"
00101 // </for>
00102 
00103 
00104 static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID);
00105 // <for functions="HTML2Plaintext,HTMLSantinize">
00106 static NS_DEFINE_CID(kParserCID, NS_PARSER_CID);
00107 static NS_DEFINE_CID(kNavDTDCID, NS_CNAVDTD_CID);
00108 // </for>
00109 
00110 #ifdef HAVE_MIME_DATA_SLOT
00111 #define LOCK_LAST_CACHED_MESSAGE
00112 #endif
00113 
00114 void                 ValidateRealName(nsMsgAttachmentData *aAttach, MimeHeaders *aHdrs);
00115 
00116 static MimeHeadersState MIME_HeaderType;
00117 static PRBool MIME_WrapLongLines;
00118 static PRBool MIME_VariableWidthPlaintext;
00119 
00120 // For string bundle access routines...
00121 static nsCOMPtr<nsIStringBundle>   stringBundle = nsnull;
00122 
00124 // Attachment handling routines
00126 //
00127 MimeObject    *mime_get_main_object(MimeObject* obj);
00128 
00129 nsresult
00130 ProcessBodyAsAttachment(MimeObject *obj, nsMsgAttachmentData **data)
00131 {
00132   nsMsgAttachmentData   *tmp;
00133   PRInt32               n;
00134   char                  *disp = nsnull;
00135   char                  *charset = nsnull;
00136 
00137   // Ok, this is the special case when somebody sends an "attachment" as the body
00138   // of an RFC822 message...I really don't think this is the way this should be done.
00139   // I belive this should really be a multipart/mixed message with an empty body part,
00140   // but what can ya do...our friends to the North seem to do this.
00141   //
00142   MimeObject    *child = obj;
00143 
00144   n = 1;
00145   *data = (nsMsgAttachmentData *)PR_Malloc( (n + 1) * sizeof(nsMsgAttachmentData));
00146   if (!*data) 
00147     return NS_ERROR_OUT_OF_MEMORY;
00148 
00149   tmp = *data;
00150   memset(*data, 0, (n + 1) * sizeof(nsMsgAttachmentData));
00151   tmp->real_type = child->content_type ? nsCRT::strdup(child->content_type) : NULL;
00152   tmp->real_encoding = child->encoding ? nsCRT::strdup(child->encoding) : NULL;
00153   disp = MimeHeaders_get(child->headers, HEADER_CONTENT_DISPOSITION, PR_FALSE, PR_FALSE);
00154   tmp->real_name = MimeHeaders_get_parameter(disp, "name", &charset, NULL);
00155   if (tmp->real_name)
00156   {
00157     char *fname = NULL;
00158     fname = mime_decode_filename(tmp->real_name, charset, obj->options);
00159     nsMemory::Free(charset);
00160     if (fname && fname != tmp->real_name)
00161     {
00162       PR_Free(tmp->real_name);
00163       tmp->real_name = fname;
00164     }
00165   }
00166   else
00167   {
00168     tmp->real_name = MimeHeaders_get_name(child->headers, obj->options);
00169   }
00170 
00171   if ( (!tmp->real_name) && (tmp->real_type) && (nsCRT::strncasecmp(tmp->real_type, "text", 4)) )
00172     ValidateRealName(tmp, child->headers);
00173 
00174   char  *tmpURL = nsnull;
00175   char  *id = nsnull;
00176   char  *id_imap = nsnull;
00177 
00178   id = mime_part_address (obj);
00179   if (obj->options->missing_parts)
00180     id_imap = mime_imap_part_address (obj);
00181 
00182   if (! id)
00183   {
00184     PR_FREEIF(*data);
00185     PR_FREEIF(id_imap);
00186     return NS_ERROR_OUT_OF_MEMORY;
00187   }
00188 
00189   if (obj->options && obj->options->url)
00190   {
00191     const char  *url = obj->options->url;
00192     nsresult    rv;
00193     if (id_imap && id)
00194     {
00195       // if this is an IMAP part. 
00196       tmpURL = mime_set_url_imap_part(url, id_imap, id);
00197       rv = nsMimeNewURI(&(tmp->url), tmpURL, nsnull);
00198     }
00199     else
00200     {
00201       // This is just a normal MIME part as usual. 
00202       tmpURL = mime_set_url_part(url, id, PR_TRUE);
00203       rv = nsMimeNewURI(&(tmp->url), tmpURL, nsnull);
00204     }
00205 
00206     if ( (!tmp->url) || (NS_FAILED(rv)) )
00207     {
00208       PR_FREEIF(*data);
00209       PR_FREEIF(id);
00210       PR_FREEIF(id_imap);
00211       return NS_ERROR_OUT_OF_MEMORY;
00212     }
00213   }
00214   PR_FREEIF(id);
00215   PR_FREEIF(id_imap);
00216   PR_FREEIF(tmpURL);
00217   tmp->description = MimeHeaders_get(child->headers, HEADER_CONTENT_DESCRIPTION, PR_FALSE, PR_FALSE);
00218   return NS_OK;
00219 }
00220 
00221 PRInt32
00222 CountTotalMimeAttachments(MimeContainer *aObj)
00223 {
00224   PRInt32     i;
00225   PRInt32     rc = 0;
00226 
00227   if ( (!aObj) || (!aObj->children) || (aObj->nchildren <= 0) )
00228     return 0;
00229 
00230   if (mime_typep((MimeObject *)aObj, (MimeObjectClass *)&mimeExternalBodyClass))
00231     return 0;
00232 
00233   for (i=0; i<aObj->nchildren; i++)
00234     rc += CountTotalMimeAttachments((MimeContainer *)aObj->children[i]) + 1;
00235 
00236   return rc;
00237 }
00238 
00239 void
00240 ValidateRealName(nsMsgAttachmentData *aAttach, MimeHeaders *aHdrs)
00241 { 
00242   // Sanity.
00243   if (!aAttach)
00244     return;
00245 
00246   // Do we need to validate?
00247   if ( (aAttach->real_name) && (*(aAttach->real_name)) )
00248     return;
00249 
00250   // Internal MIME structures need not be named!
00251   if ( (!aAttach->real_type) || (aAttach->real_type && 
00252                                  !nsCRT::strncasecmp(aAttach->real_type, "multipart", 9)) )
00253     return;
00254 
00255   // Special case...if this is a enclosed RFC822 message, give it a nice
00256   // name.
00257   if (aAttach->real_type && !nsCRT::strcasecmp(aAttach->real_type, MESSAGE_RFC822))
00258   {
00259     NS_ASSERTION(aHdrs, "How comes the object's headers is null!");
00260     if (aHdrs && aHdrs->munged_subject)
00261       aAttach->real_name = PR_smprintf("%s.eml", aHdrs->munged_subject);
00262     else
00263       NS_MsgSACopy(&(aAttach->real_name), "ForwardedMessage.eml");
00264     return;
00265   }
00266 
00267   // 
00268   // Now validate any other name we have for the attachment!
00269   //
00270   if (!aAttach->real_name || *aAttach->real_name == 0)
00271   {
00272     nsString  newAttachName(NS_LITERAL_STRING("attachment"));
00273     nsresult  rv = NS_OK;
00274     nsCAutoString contentType (aAttach->real_type);
00275     PRInt32 pos = contentType.FindChar(';');
00276     if (pos > 0)
00277       contentType.Truncate(pos);
00278 
00279     nsCOMPtr<nsIMIMEService> mimeFinder (do_GetService(NS_MIMESERVICE_CONTRACTID, &rv));
00280     if (NS_SUCCEEDED(rv)) 
00281     {
00282       nsCAutoString fileExtension;
00283       rv = mimeFinder->GetPrimaryExtension(contentType, EmptyCString(), fileExtension);
00284 
00285       if (NS_SUCCEEDED(rv) && !fileExtension.IsEmpty())
00286       {
00287         newAttachName.Append(PRUnichar('.'));
00288         AppendUTF8toUTF16(fileExtension, newAttachName);
00289       }
00290     }
00291 
00292     aAttach->real_name = ToNewCString(newAttachName);
00293   }  
00294 }
00295 
00296 static  PRInt32     attIndex = 0;
00297 
00298 nsresult
00299 GenerateAttachmentData(MimeObject *object, const char *aMessageURL, MimeDisplayOptions *options,
00300                        PRBool isAnAppleDoublePart, nsMsgAttachmentData *aAttachData)
00301 {
00302   nsXPIDLCString imappart;
00303   nsXPIDLCString part;
00304   PRBool isIMAPPart;
00305   PRBool isExternalAttachment = PR_FALSE;
00306 
00307   /* be sure the object has not be marked as Not to be an attachment */
00308   if (object->dontShowAsAttachment)
00309     return NS_OK;
00310 
00311   part.Adopt(mime_part_address(object));
00312   if (part.IsEmpty()) 
00313     return NS_ERROR_OUT_OF_MEMORY;
00314 
00315   if (options->missing_parts)
00316     imappart.Adopt(mime_imap_part_address(object));
00317 
00318   char *urlSpec = nsnull;
00319   if (!imappart.IsEmpty())
00320   {
00321     isIMAPPart = PR_TRUE;
00322     urlSpec = mime_set_url_imap_part(aMessageURL, imappart.get(), part.get());
00323   }
00324   else
00325   {
00326     isIMAPPart = PR_FALSE;
00327     char *no_part_url = nsnull;
00328     if (options->part_to_load && options->format_out == nsMimeOutput::nsMimeMessageBodyDisplay)
00329       no_part_url = mime_get_base_url(aMessageURL);
00330     if (no_part_url) {
00331       urlSpec = mime_set_url_part(no_part_url, part.get(), PR_TRUE);
00332       PR_Free(no_part_url);
00333     }
00334     else
00335     {
00336       // if the mime object contains an external attachment URL, then use it, otherwise
00337       // fall back to creating an attachment url based on the message URI and the 
00338       // part number.
00339       urlSpec = mime_external_attachment_url(object);
00340       isExternalAttachment = urlSpec ? PR_TRUE : PR_FALSE;
00341       if (!urlSpec)
00342         urlSpec = mime_set_url_part(aMessageURL, part.get(), PR_TRUE);
00343     }
00344   }
00345 
00346   if (!urlSpec)
00347     return NS_ERROR_OUT_OF_MEMORY;
00348 
00349   if ((options->format_out == nsMimeOutput::nsMimeMessageBodyDisplay) && (nsCRT::strncasecmp(aMessageURL, urlSpec, strlen(urlSpec)) == 0))
00350     return NS_OK;
00351   
00352   nsMsgAttachmentData *tmp = &(aAttachData[attIndex++]);
00353   nsresult rv = nsMimeNewURI(&(tmp->url), urlSpec, nsnull);
00354 
00355        PR_FREEIF(urlSpec);
00356 
00357   if ( (NS_FAILED(rv)) || (!tmp->url) )
00358     return NS_ERROR_OUT_OF_MEMORY;
00359 
00360   tmp->real_type = object->content_type ? nsCRT::strdup(object->content_type) : nsnull;
00361   tmp->real_encoding = object->encoding ? nsCRT::strdup(object->encoding) : nsnull;
00362   tmp->isExternalAttachment = isExternalAttachment;
00363   
00364   PRInt32 i;
00365   char *charset = nsnull;
00366   char *disp = MimeHeaders_get(object->headers, HEADER_CONTENT_DISPOSITION, PR_FALSE, PR_FALSE);
00367   if (disp) 
00368   {
00369     tmp->real_name = MimeHeaders_get_parameter(disp, "filename", &charset, nsnull);
00370     if (isAnAppleDoublePart)
00371       for (i = 0; i < 2 && !tmp->real_name; i ++)
00372       {
00373         PR_FREEIF(disp);
00374         nsMemory::Free(charset);
00375         disp = MimeHeaders_get(((MimeContainer *)object)->children[i]->headers, HEADER_CONTENT_DISPOSITION, PR_FALSE, PR_FALSE);
00376         tmp->real_name = MimeHeaders_get_parameter(disp, "filename", &charset, nsnull);
00377       }
00378 
00379     if (tmp->real_name)
00380     {
00381       // check encoded type
00382       //
00383       // The parameter of Content-Disposition must use RFC 2231.
00384       // But old Netscape 4.x and Outlook Express etc. use RFC2047.
00385       // So we should parse both types.
00386 
00387       char *fname = nsnull;
00388       fname = mime_decode_filename(tmp->real_name, charset, options);
00389       nsMemory::Free(charset);
00390 
00391       if (fname && fname != tmp->real_name)
00392       {
00393         PR_FREEIF(tmp->real_name);
00394         tmp->real_name = fname;
00395       }
00396     }
00397 
00398     PR_FREEIF(disp);
00399   }
00400 
00401   disp = MimeHeaders_get(object->headers, HEADER_CONTENT_TYPE, PR_FALSE, PR_FALSE);
00402   if (disp)
00403   {
00404     tmp->x_mac_type   = MimeHeaders_get_parameter(disp, PARAM_X_MAC_TYPE, nsnull, nsnull);
00405     tmp->x_mac_creator= MimeHeaders_get_parameter(disp, PARAM_X_MAC_CREATOR, nsnull, nsnull);
00406     
00407     if (!tmp->real_name || *tmp->real_name == 0)
00408     {
00409       PR_FREEIF(tmp->real_name);
00410       tmp->real_name = MimeHeaders_get_parameter(disp, "name", &charset, nsnull);
00411       if (isAnAppleDoublePart)
00412         // the data fork is the 2nd part, and we should ALWAYS look there first for the file name
00413         for (i = 1; i >= 0 && !tmp->real_name; i --) 
00414         {
00415           PR_FREEIF(disp);
00416           nsMemory::Free(charset);
00417           disp = MimeHeaders_get(((MimeContainer *)object)->children[i]->headers, HEADER_CONTENT_TYPE, PR_FALSE, PR_FALSE);
00418           tmp->real_name = MimeHeaders_get_parameter(disp, "name", &charset, nsnull);
00419         }
00420       
00421       if (tmp->real_name)
00422       {
00423         // check encoded type
00424         //
00425         // The parameter of Content-Disposition must use RFC 2231.
00426         // But old Netscape 4.x and Outlook Express etc. use RFC2047.
00427         // So we should parse both types.
00428 
00429         char *fname = nsnull;
00430         fname = mime_decode_filename(tmp->real_name, charset, options);
00431         nsMemory::Free(charset);
00432 
00433         if (fname && fname != tmp->real_name)
00434         {
00435           PR_Free(tmp->real_name);
00436           tmp->real_name = fname;
00437         }
00438       }
00439     }
00440     PR_FREEIF(disp);
00441   }
00442 
00443   tmp->description = MimeHeaders_get(object->headers, HEADER_CONTENT_DESCRIPTION, 
00444                                      PR_FALSE, PR_FALSE);
00445 
00446   // Now, do the right thing with the name!
00447   if (!tmp->real_name && nsCRT::strcasecmp(tmp->real_type, MESSAGE_RFC822))
00448   {
00449     /* If this attachment doesn't have a name, just give it one... */
00450     tmp->real_name = MimeGetStringByID(MIME_MSG_DEFAULT_ATTACHMENT_NAME);
00451     if (tmp->real_name)
00452     {
00453       char *newName = PR_smprintf(tmp->real_name, part.get());
00454       if (newName)
00455       {
00456         PR_Free(tmp->real_name);
00457         tmp->real_name = newName;
00458       }
00459     }
00460     else
00461       tmp->real_name = mime_part_address(object);
00462   }
00463   ValidateRealName(tmp, object->headers);
00464 
00465   return NS_OK;
00466 }
00467 
00468 nsresult
00469 BuildAttachmentList(MimeObject *anObject, nsMsgAttachmentData *aAttachData, const char *aMessageURL)
00470 {
00471   nsresult              rv;
00472   PRInt32               i;
00473   MimeContainer         *cobj = (MimeContainer *) anObject;
00474 
00475   if ( (!anObject) || (!cobj->children) || (!cobj->nchildren) ||
00476        (mime_typep(anObject, (MimeObjectClass *)&mimeExternalBodyClass)))
00477     return NS_OK;
00478 
00479   for (i = 0; i < cobj->nchildren ; i++) 
00480   {
00481     MimeObject    *child = cobj->children[i];
00482 
00483     // Skip the first child if it's in fact a message body
00484     if (i == 0)                                         // it's the first child
00485       if (child->content_type)                          // and it's content-type is one of folowing...
00486         if (!nsCRT::strcasecmp (child->content_type, TEXT_PLAIN) ||
00487             !nsCRT::strcasecmp (child->content_type, TEXT_HTML) ||
00488             !nsCRT::strcasecmp (child->content_type, TEXT_MDL))
00489         {
00490           if (child->headers) // and finally, be sure it doesn't have a content-disposition: attachment 
00491           {
00492             char * disp = MimeHeaders_get (child->headers, HEADER_CONTENT_DISPOSITION, PR_TRUE, PR_FALSE);
00493             if (!disp || nsCRT::strcasecmp (disp, "attachment"))
00494               continue;
00495           }
00496           else
00497             continue;
00498         }
00499 
00500     
00501     // We should generate an attachment for leaf object only but...
00502     PRBool isALeafObject = mime_subclass_p(child->clazz, (MimeObjectClass *) &mimeLeafClass);
00503 
00504     // ...we will generate an attachment for inline message too.
00505     PRBool isAnInlineMessage = mime_typep(child, (MimeObjectClass *) &mimeMessageClass);
00506     
00507     // AppleDouble part need special care: we need to fetch the part as well its two
00508     // children for the needed info as they could be anywhere, eventually, they won't contain
00509     // a name or file name. In any case we need to build only one attachment data
00510     PRBool isAnAppleDoublePart = mime_typep(child, (MimeObjectClass *) &mimeMultipartAppleDoubleClass) &&
00511                                  ((MimeContainer *)child)->nchildren == 2;
00512 
00513     if (isALeafObject || isAnInlineMessage || isAnAppleDoublePart)
00514     {
00515       rv = GenerateAttachmentData(child, aMessageURL, anObject->options, isAnAppleDoublePart, aAttachData);
00516       NS_ENSURE_SUCCESS(rv, rv);
00517     }
00518     
00519     // Now build the attachment list for the children of our object...
00520     if (!isALeafObject && !isAnAppleDoublePart)
00521     {
00522       rv = BuildAttachmentList((MimeObject *)child, aAttachData, aMessageURL);
00523       NS_ENSURE_SUCCESS(rv, rv);
00524     }
00525   }
00526 
00527   return NS_OK;
00528 
00529 }
00530 
00531 extern "C" nsresult
00532 MimeGetAttachmentList(MimeObject *tobj, const char *aMessageURL, nsMsgAttachmentData **data)
00533 {
00534   MimeObject            *obj;
00535   MimeContainer         *cobj;
00536   PRInt32               n;
00537   PRBool                isAnInlineMessage;
00538 
00539   if (!data) 
00540     return 0;
00541   *data = nsnull;
00542 
00543   obj = mime_get_main_object(tobj);
00544   if (!obj)
00545     return 0;
00546 
00547   if (!mime_subclass_p(obj->clazz, (MimeObjectClass*) &mimeContainerClass))
00548   {
00549     if (!PL_strcasecmp(obj->content_type, MESSAGE_RFC822))
00550       return 0;
00551     else
00552       return ProcessBodyAsAttachment(obj, data);
00553   }
00554 
00555   isAnInlineMessage = mime_typep(obj, (MimeObjectClass *) &mimeMessageClass);
00556 
00557   cobj = (MimeContainer*) obj;
00558   n = CountTotalMimeAttachments(cobj);
00559   if (n <= 0) 
00560     return n;
00561 
00562   //in case of an inline message (as body), we need an extra slot for the message itself
00563   //that we will fill later...
00564   if (isAnInlineMessage)
00565     n ++;
00566 
00567   *data = (nsMsgAttachmentData *)PR_Malloc( (n + 1) * sizeof(nsMsgAttachmentData));
00568   if (!*data) 
00569     return NS_ERROR_OUT_OF_MEMORY;
00570 
00571   attIndex = 0;
00572   memset(*data, 0, (n + 1) * sizeof(nsMsgAttachmentData));
00573   
00574   // Now, build the list!
00575 
00576   nsresult rv;
00577 
00578   if (isAnInlineMessage)
00579   {
00580     rv = GenerateAttachmentData(obj, aMessageURL, obj->options, PR_FALSE, *data);
00581     NS_ENSURE_SUCCESS(rv, rv);
00582   }
00583   return BuildAttachmentList((MimeObject *) cobj, *data, aMessageURL);
00584 }
00585 
00586 extern "C" void
00587 MimeFreeAttachmentList(nsMsgAttachmentData *data)
00588 {
00589   if (data) 
00590   {
00591     nsMsgAttachmentData   *tmp;
00592     for (tmp = data ; tmp->url ; tmp++) 
00593     {
00594       /* Can't do PR_FREEIF on `const' values... */
00595       NS_IF_RELEASE(tmp->url);
00596       if (tmp->real_type) PR_Free((char *) tmp->real_type);
00597       if (tmp->real_encoding) PR_Free((char *) tmp->real_encoding);
00598       if (tmp->real_name) PR_Free((char *) tmp->real_name);
00599       if (tmp->x_mac_type) PR_Free((char *) tmp->x_mac_type);
00600       if (tmp->x_mac_creator) PR_Free((char *) tmp->x_mac_creator);
00601       if (tmp->description) PR_Free((char *) tmp->description);
00602       tmp->url = 0;
00603       tmp->real_type = 0;
00604       tmp->real_name = 0;
00605       tmp->description = 0;
00606     }
00607     PR_Free(data);
00608   }
00609 }
00610 
00611 extern "C" void
00612 NotifyEmittersOfAttachmentList(MimeDisplayOptions     *opt,
00613                                nsMsgAttachmentData    *data)
00614 {
00615   PRInt32     i = 0;
00616   struct      nsMsgAttachmentData  *tmp = data;
00617 
00618   if (!tmp) 
00619     return;
00620 
00621   while (tmp->url)
00622   {
00623     if (!tmp->real_name)
00624     {
00625       ++i;
00626       ++tmp;      
00627       continue;
00628     }
00629 
00630     nsCAutoString spec;
00631     if ( tmp->url ) 
00632       tmp->url->GetSpec(spec);
00633 
00634     mimeEmitterStartAttachment(opt, tmp->real_name, tmp->real_type, spec.get(), tmp->isExternalAttachment);
00635     mimeEmitterAddAttachmentField(opt, HEADER_X_MOZILLA_PART_URL, spec.get());
00636 
00637     if ( (opt->format_out == nsMimeOutput::nsMimeMessageQuoting) || 
00638          (opt->format_out == nsMimeOutput::nsMimeMessageBodyQuoting) || 
00639          (opt->format_out == nsMimeOutput::nsMimeMessageSaveAs) || 
00640          (opt->format_out == nsMimeOutput::nsMimeMessagePrintOutput))
00641     {
00642       mimeEmitterAddAttachmentField(opt, HEADER_CONTENT_DESCRIPTION, tmp->description);
00643       mimeEmitterAddAttachmentField(opt, HEADER_CONTENT_TYPE, tmp->real_type);
00644       mimeEmitterAddAttachmentField(opt, HEADER_CONTENT_ENCODING,    tmp->real_encoding);
00645 
00646       /* rhp - for now, just leave these here, but they are really
00647                not necessary
00648       printf("URL for Part      : %s\n", spec);
00649       printf("Real Name         : %s\n", tmp->real_name);
00650            printf("Desired Type      : %s\n", tmp->desired_type);
00651       printf("Real Type         : %s\n", tmp->real_type);
00652            printf("Real Encoding     : %s\n", tmp->real_encoding); 
00653       printf("Description       : %s\n", tmp->description);
00654       printf("Mac Type          : %s\n", tmp->x_mac_type);
00655       printf("Mac Creator       : %s\n", tmp->x_mac_creator);
00656       */
00657     }
00658 
00659     mimeEmitterEndAttachment(opt);
00660     ++i;
00661     ++tmp;
00662   }
00663   mimeEmitterEndAllAttachments(opt);
00664 }
00665 
00666 // Utility to create a nsIURI object...
00667 extern "C" nsresult 
00668 nsMimeNewURI(nsIURI** aInstancePtrResult, const char *aSpec, nsIURI *aBase)
00669 {  
00670   nsresult  res;
00671 
00672   if (nsnull == aInstancePtrResult) 
00673     return NS_ERROR_NULL_POINTER;
00674   
00675   nsCOMPtr<nsIIOService> pService(do_GetService(kIOServiceCID, &res));
00676   if (NS_FAILED(res)) 
00677     return NS_ERROR_FACTORY_NOT_REGISTERED;
00678 
00679   return pService->NewURI(nsDependentCString(aSpec), nsnull, aBase, aInstancePtrResult);
00680 }
00681 
00682 extern "C" nsresult 
00683 SetMailCharacterSetToMsgWindow(MimeObject *obj, const char *aCharacterSet)
00684 {
00685   nsresult rv = NS_OK;
00686 
00687   if (obj && obj->options)
00688   {
00689     mime_stream_data *msd = (mime_stream_data *) (obj->options->stream_closure);
00690     if (msd)
00691     {
00692       nsIChannel *channel = msd->channel;
00693       if (channel)
00694       {
00695         nsCOMPtr<nsIURI> uri;
00696         channel->GetURI(getter_AddRefs(uri));
00697         if (uri)
00698         {
00699           nsCOMPtr<nsIMsgMailNewsUrl> msgurl (do_QueryInterface(uri));
00700           if (msgurl)
00701           {
00702             nsCOMPtr<nsIMsgWindow> msgWindow;
00703             msgurl->GetMsgWindow(getter_AddRefs(msgWindow));
00704             if (msgWindow)
00705               rv = msgWindow->SetMailCharacterSet(!nsCRT::strcasecmp(aCharacterSet, "us-ascii") ?
00706                                                   "ISO-8859-1" :
00707                                                   aCharacterSet);
00708           }
00709         }
00710       }
00711     }
00712   }
00713 
00714   return rv;
00715 }
00716 
00717 static char *
00718 mime_file_type (const char *filename, void *stream_closure)
00719 {
00720   char        *retType = nsnull;
00721   char        *ext = nsnull;
00722   nsresult    rv;
00723 
00724   ext = PL_strrchr(filename, '.');
00725   if (ext)
00726   {
00727     ext++;
00728     nsCOMPtr<nsIMIMEService> mimeFinder (do_GetService(NS_MIMESERVICE_CONTRACTID, &rv));
00729     if (mimeFinder) {
00730       nsCAutoString type;
00731       mimeFinder->GetTypeFromExtension(nsDependentCString(ext), type);
00732       retType = ToNewCString(type);
00733     }
00734   }
00735 
00736   return retType;
00737 }
00738 
00739 int ConvertUsingEncoderAndDecoder(const char *stringToUse, PRInt32 inLength, 
00740                                   nsIUnicodeEncoder *encoder, nsIUnicodeDecoder *decoder, 
00741                                   char **pConvertedString, PRInt32 *outLength)
00742 {
00743   // buffer size 144 =
00744   // 72 (default line len for compose) 
00745   // times 2 (converted byte len might be larger)
00746   const int klocalbufsize = 144;
00747   // do the conversion
00748   PRUnichar *unichars;
00749   PRInt32 unicharLength;
00750   PRInt32 srcLen = inLength;
00751   PRInt32 dstLength = 0;
00752   char *dstPtr;
00753   nsresult rv;
00754 
00755   // use this local buffer if possible
00756   PRUnichar localbuf[klocalbufsize+1];
00757   if (inLength > klocalbufsize) {
00758     rv = decoder->GetMaxLength(stringToUse, srcLen, &unicharLength);
00759     // allocate temporary buffer to hold unicode string
00760     unichars = new PRUnichar[unicharLength];
00761   }
00762   else {
00763     unichars = localbuf;
00764     unicharLength = klocalbufsize+1;
00765   }
00766   if (unichars == nsnull) {
00767     rv = NS_ERROR_OUT_OF_MEMORY;
00768   }
00769   else {
00770     // convert to unicode, replacing failed chars with 0xFFFD as in
00771     // the methode used in nsXMLHttpRequest::ConvertBodyToText and nsScanner::Append
00772     // 
00773     // We will need several pass to convert the whole string if it has invalid characters
00774     // 'totalChars' is where the sum of the number of converted characters will be done
00775     // 'dataLen' is the number of character left to convert
00776     // 'outLen' is the number of characters still available in the output buffer as input of decoder->Convert
00777     // and the number of characters written in it as output.
00778     PRInt32 totalChars = 0,
00779             inBufferIndex = 0,
00780             outBufferIndex = 0;
00781     PRInt32 dataLen = srcLen,
00782             outLen = unicharLength;
00783 
00784     do {
00785       PRInt32 inBufferLength = dataLen;
00786       rv = decoder->Convert(&stringToUse[inBufferIndex],
00787                            &inBufferLength,
00788                            &unichars[outBufferIndex],
00789                            &outLen);
00790       totalChars += outLen;
00791       // Done if conversion successful
00792       if (NS_SUCCEEDED(rv))
00793           break;
00794 
00795       // We consume one byte, replace it with U+FFFD
00796       // and try the conversion again.
00797       outBufferIndex += outLen;
00798       unichars[outBufferIndex++] = PRUnichar(0xFFFD);
00799       // totalChars is updated here
00800       outLen = unicharLength - (++totalChars);
00801 
00802       inBufferIndex += inBufferLength + 1;
00803       dataLen -= inBufferLength + 1;
00804 
00805       decoder->Reset();
00806 
00807       // If there is not at least one byte available after the one we
00808       // consumed, we're done
00809     } while ( dataLen > 0 );
00810 
00811     rv = encoder->GetMaxLength(unichars, totalChars, &dstLength);
00812     // allocale an output buffer
00813     dstPtr = (char *) PR_Malloc(dstLength + 1);
00814     if (dstPtr == nsnull) {
00815       rv = NS_ERROR_OUT_OF_MEMORY;
00816     }
00817     else {
00818       PRInt32 buffLength = dstLength;
00819       // convert from unicode
00820       rv = encoder->SetOutputErrorBehavior(nsIUnicodeEncoder::kOnError_Replace, nsnull, '?');
00821       if (NS_SUCCEEDED(rv)) {
00822         rv = encoder->Convert(unichars, &totalChars, dstPtr, &dstLength);
00823         if (NS_SUCCEEDED(rv)) {
00824           PRInt32 finLen = buffLength - dstLength;
00825           rv = encoder->Finish((char *)(dstPtr+dstLength), &finLen);
00826           if (NS_SUCCEEDED(rv)) {
00827             dstLength += finLen;
00828           }
00829           dstPtr[dstLength] = '\0';
00830           *pConvertedString = dstPtr;       // set the result string
00831           *outLength = dstLength;
00832         }
00833       }
00834     }
00835     if (inLength > klocalbufsize)
00836       delete [] unichars;
00837   }
00838 
00839   return NS_SUCCEEDED(rv) ? 0 : -1;
00840 }
00841 
00842 
00843 static int
00844 mime_convert_charset (const char *input_line, PRInt32 input_length,
00845                       const char *input_charset, const char *output_charset,
00846                       char **output_ret, PRInt32 *output_size_ret,
00847                       void *stream_closure, nsIUnicodeDecoder *decoder, nsIUnicodeEncoder *encoder)
00848 {
00849   PRInt32 res = -1;
00850   char  *convertedString = NULL;
00851   PRInt32 convertedStringLen = 0;
00852   if (encoder && decoder)
00853   {
00854     res = ConvertUsingEncoderAndDecoder(input_line, input_length, encoder, decoder, &convertedString, &convertedStringLen);
00855   }
00856   if (res != 0)
00857   {
00858       *output_ret = 0;
00859       *output_size_ret = 0;
00860   }
00861   else
00862   {
00863     *output_ret = (char *) convertedString;
00864     *output_size_ret = convertedStringLen;
00865   }  
00866 
00867   return 0;
00868 }
00869 
00870 static int
00871 mime_output_fn(const char *buf, PRInt32 size, void *stream_closure)
00872 {
00873   PRUint32  written = 0;
00874   struct mime_stream_data *msd = (struct mime_stream_data *) stream_closure;
00875   if ( (!msd->pluginObj2) && (!msd->output_emitter) )
00876     return -1;
00877   
00878   // Fire pending start request
00879   ((nsStreamConverter*)msd->pluginObj2)->FirePendingStartRequest();
00880   
00881   
00882   // Now, write to the WriteBody method if this is a message body and not
00883   // a part retrevial
00884   if (!msd->options->part_to_load || msd->options->format_out == nsMimeOutput::nsMimeMessageBodyDisplay)
00885   {
00886     if (msd->output_emitter)
00887     {
00888       msd->output_emitter->WriteBody(buf, (PRUint32) size, &written);
00889     }
00890   }
00891   else
00892   {
00893     if (msd->output_emitter)
00894     {
00895       msd->output_emitter->Write(buf, (PRUint32) size, &written);
00896     }
00897   }
00898   return written;
00899 }
00900 
00901 #ifdef XP_MAC
00902 static int
00903 compose_only_output_fn(const char *buf, PRInt32 size, void *stream_closure)
00904 {
00905     return 0;
00906 }
00907 #endif
00908 
00909 extern "C" int
00910 mime_display_stream_write (nsMIMESession *stream,
00911                            const char* buf,
00912                            PRInt32 size)
00913 {
00914   struct mime_stream_data *msd = (struct mime_stream_data *) ((nsMIMESession *)stream)->data_object;
00915 
00916   MimeObject *obj = (msd ? msd->obj : 0);  
00917   if (!obj) return -1;
00918 
00919   //
00920   // Ok, now check to see if this is a display operation for a MIME Parts on Demand
00921   // enabled call.
00922   //
00923   if (msd->firstCheck)
00924   {
00925     if (msd->channel)
00926     {
00927       nsCOMPtr<nsIURI> aUri;
00928            if (NS_SUCCEEDED(msd->channel->GetURI(getter_AddRefs(aUri))))
00929       {
00930         nsCOMPtr<nsIImapUrl> imapURL = do_QueryInterface(aUri);
00931         if (imapURL)
00932         {
00933           nsImapContentModifiedType   cModified;
00934           if (NS_SUCCEEDED(imapURL->GetContentModified(&cModified)))
00935           {
00936             if ( cModified != nsImapContentModifiedTypes::IMAP_CONTENT_NOT_MODIFIED )
00937               msd->options->missing_parts = PR_TRUE;
00938           }
00939         }
00940       }
00941     }
00942 
00943     msd->firstCheck = PR_FALSE;
00944   }
00945 
00946   return obj->clazz->parse_buffer((char *) buf, size, obj);
00947 }
00948 
00949 extern "C" void 
00950 mime_display_stream_complete (nsMIMESession *stream)
00951 {
00952   struct mime_stream_data *msd = (struct mime_stream_data *) ((nsMIMESession *)stream)->data_object;
00953   MimeObject *obj = (msd ? msd->obj : 0);  
00954   if (obj)
00955   {
00956     int       status;
00957     PRBool    abortNow = PR_FALSE;
00958 
00959     if ((obj->options) && (obj->options->headers == MimeHeadersOnly))
00960       abortNow = PR_TRUE;
00961 
00962     status = obj->clazz->parse_eof(obj, abortNow);
00963     obj->clazz->parse_end(obj, (status < 0 ? PR_TRUE : PR_FALSE));
00964   
00965     //
00966     // Ok, now we are going to process the attachment data by getting all
00967     // of the attachment info and then driving the emitter with this data.
00968     //
00969     if (!msd->options->part_to_load || msd->options->format_out == nsMimeOutput::nsMimeMessageBodyDisplay)
00970     {
00971       nsMsgAttachmentData *attachments;
00972       nsresult rv = MimeGetAttachmentList(obj, msd->url_name, &attachments);
00973       if (NS_SUCCEEDED(rv))
00974       {
00975         NotifyEmittersOfAttachmentList(msd->options, attachments);
00976         MimeFreeAttachmentList(attachments);
00977       }
00978     }
00979 
00980     // Release the conversion object - this has to be done after
00981     // we finish processing data.
00982     if ( obj->options)
00983     {
00984       NS_IF_RELEASE(obj->options->conv);
00985     }
00986 
00987     // Destroy the object now.
00988     PR_ASSERT(msd->options == obj->options);
00989     mime_free(obj);
00990     obj = NULL;
00991     if (msd->options)
00992     {
00993       delete msd->options;
00994       msd->options = 0;
00995     }
00996   }
00997 
00998   if (msd->headers)
00999        MimeHeaders_free (msd->headers);
01000 
01001   if (msd->url_name)
01002          nsCRT::free(msd->url_name);
01003 
01004   if (msd->orig_url_name)
01005       nsCRT::free(msd->orig_url_name);
01006 
01007   PR_FREEIF(msd);
01008 }
01009 
01010 extern "C" void
01011 mime_display_stream_abort (nsMIMESession *stream, int status)
01012 {
01013   struct mime_stream_data *msd = (struct mime_stream_data *) ((nsMIMESession *)stream)->data_object;
01014   
01015   MimeObject *obj = (msd ? msd->obj : 0);  
01016   if (obj)
01017   {
01018     if (!obj->closed_p)
01019       obj->clazz->parse_eof(obj, PR_TRUE);
01020     if (!obj->parsed_p)
01021       obj->clazz->parse_end(obj, PR_TRUE);
01022     
01023     // Destroy code....
01024     PR_ASSERT(msd->options == obj->options);
01025     mime_free(obj);
01026     if (msd->options)
01027     {
01028       delete msd->options;
01029       msd->options = 0;
01030     }
01031   }
01032 
01033   if (msd->headers)
01034        MimeHeaders_free (msd->headers);
01035 
01036   if (msd->url_name)
01037          nsCRT::free(msd->url_name);
01038 
01039   if (msd->orig_url_name)
01040       nsCRT::free(msd->orig_url_name);
01041 
01042   PR_FREEIF(msd);
01043 }
01044 
01045 #ifdef XP_MAC
01046 static PRUint32
01047 mime_convert_chars_to_ostype(const char *osTypeStr)
01048 {
01049        if (!osTypeStr)
01050               return '????';
01051 
01052        PRUint32 result;
01053        const char *p = osTypeStr;
01054 
01055        for (result = 0; *p; p++)
01056        {
01057               char C = *p;
01058 
01059               PRInt8 unhex = ((C >= '0' && C <= '9') ? C - '0' :
01060                      ((C >= 'A' && C <= 'F') ? C - 'A' + 10 :
01061                       ((C >= 'a' && C <= 'f') ? C - 'a' + 10 : -1)));
01062               if (unhex < 0)
01063                      break;
01064               result = (result << 4) | unhex;
01065        }
01066 
01067        return result;
01068 }
01069 #endif
01070 
01071 static int
01072 mime_output_init_fn (const char *type,
01073                      const char *charset,
01074                      const char *name,
01075                      const char *x_mac_type,
01076                      const char *x_mac_creator,
01077                      void *stream_closure)
01078 {
01079   struct mime_stream_data *msd = (struct mime_stream_data *) stream_closure;
01080   
01081   // Now, all of this stream creation is done outside of libmime, so this
01082   // is just a check of the pluginObj member and returning accordingly.
01083   if (!msd->pluginObj2)
01084     return -1;
01085   else
01086     return 0;
01087 }
01088 
01089 static void   *mime_image_begin(const char *image_url, const char *content_type,
01090                               void *stream_closure);
01091 static void   mime_image_end(void *image_closure, int status);
01092 static char   *mime_image_make_image_html(void *image_data);
01093 static int    mime_image_write_buffer(const char *buf, PRInt32 size, void *image_closure);
01094 
01095 /* Interface between libmime and inline display of images: the abomination
01096    that is known as "internal-external-reconnect".
01097  */
01098 class mime_image_stream_data {
01099 public:
01100   mime_image_stream_data();
01101 
01102   struct mime_stream_data *msd;
01103   char                    *url;
01104   nsMIMESession           *istream;
01105   nsCOMPtr<nsIOutputStream> memCacheOutputStream;
01106   PRBool m_shouldCacheImage;
01107 };
01108 
01109 mime_image_stream_data::mime_image_stream_data()
01110 {
01111   url = nsnull;
01112   istream = nsnull;
01113   msd = nsnull;
01114   m_shouldCacheImage = PR_FALSE;
01115 }
01116 
01117 static void *
01118 mime_image_begin(const char *image_url, const char *content_type,
01119                  void *stream_closure)
01120 {
01121   struct mime_stream_data *msd = (struct mime_stream_data *) stream_closure;
01122   class mime_image_stream_data *mid;
01123  
01124   mid = new mime_image_stream_data;
01125   if (!mid) return nsnull;
01126 
01127 
01128   mid->msd = msd;
01129 
01130   mid->url = (char *) nsCRT::strdup(image_url);
01131   if (!mid->url)
01132   {
01133     PR_Free(mid);
01134     return nsnull;
01135   }
01136 
01137   if (msd->channel)
01138   {
01139     nsCOMPtr <nsIURI> uri;
01140     nsresult rv = msd->channel->GetURI(getter_AddRefs(uri));
01141     if (NS_SUCCEEDED(rv) && uri)
01142     {
01143       nsCOMPtr <nsIMsgMailNewsUrl> mailUrl = do_QueryInterface(uri);
01144       if (mailUrl)
01145       {
01146         nsCOMPtr<nsICacheSession> memCacheSession;
01147         mailUrl->GetImageCacheSession(getter_AddRefs(memCacheSession));
01148         if (memCacheSession)
01149         {
01150           nsCOMPtr<nsICacheEntryDescriptor> entry;
01151 
01152           // we may need to convert the image_url into just a part url - in any case,
01153           // it has to be the same as what imglib will be asking imap for later
01154           // on so that we'll find this in the memory cache.
01155           rv = memCacheSession->OpenCacheEntry(nsDependentCString(image_url), nsICache::ACCESS_READ_WRITE, nsICache::BLOCKING, getter_AddRefs(entry));
01156           nsCacheAccessMode access;
01157           if (entry)
01158           {
01159             entry->GetAccessGranted(&access);
01160 #ifdef DEBUG_bienvenu
01161             printf("Mime opening cache entry for %s access = %ld\n", image_url, access);
01162 #endif
01163             // if we only got write access, then we should fill in this cache entry
01164             if (access & nsICache::ACCESS_WRITE && !(access & nsICache::ACCESS_READ))
01165             {
01166               mailUrl->CacheCacheEntry(entry);
01167               entry->MarkValid();
01168 
01169               // remember the content type as meta data so we can pull it out in the imap code
01170               // to feed the cache entry directly to imglib w/o going through mime.
01171               entry->SetMetaDataElement("contentType", content_type);
01172 
01173               rv = entry->OpenOutputStream(0, getter_AddRefs(mid->memCacheOutputStream));
01174               if (NS_FAILED(rv)) return nsnull;
01175             }
01176           }
01177         }
01178       }
01179     }
01180   }
01181   mid->istream = (nsMIMESession *) msd->pluginObj2;
01182   return mid;
01183 }
01184 
01185 static void
01186 mime_image_end(void *image_closure, int status)
01187 {
01188   mime_image_stream_data *mid =
01189     (mime_image_stream_data *) image_closure;
01190   
01191   PR_ASSERT(mid);
01192   if (!mid) 
01193     return;
01194   if (mid->memCacheOutputStream)
01195     mid->memCacheOutputStream->Close();
01196 
01197   PR_FREEIF(mid->url);
01198   delete mid;
01199 }
01200 
01201 
01202 static char *
01203 mime_image_make_image_html(void *image_closure)
01204 {
01205   mime_image_stream_data *mid =
01206     (mime_image_stream_data *) image_closure;
01207 
01208   const char *prefix = "<P><CENTER><IMG SRC=\"";
01209   const char *suffix = "\"></CENTER><P>";
01210   const char *url;
01211   char *buf;
01212 
01213   PR_ASSERT(mid);
01214   if (!mid) return 0;
01215 
01216   /* Internal-external-reconnect only works when going to the screen. */
01217   if (!mid->istream)
01218     return nsCRT::strdup("<P><CENTER><IMG SRC=\"resource://gre/res/network/gopher-image.gif\" ALT=\"[Image]\"></CENTER><P>");
01219 
01220   if ( (!mid->url) || (!(*mid->url)) )
01221     url = "";
01222   else
01223     url = mid->url;
01224 
01225   PRUint32 buflen = strlen(prefix) + strlen(suffix) + strlen(url) + 20;
01226   buf = (char *) PR_MALLOC (buflen);
01227 
01228   if (!buf) 
01229     return 0;
01230   *buf = 0;
01231 
01232   PL_strcatn (buf, buflen, prefix);
01233   PL_strcatn (buf, buflen, url);
01234   PL_strcatn (buf, buflen, suffix);
01235   return buf;
01236 }
01237 
01238 static int
01239 mime_image_write_buffer(const char *buf, PRInt32 size, void *image_closure)
01240 {
01241   mime_image_stream_data *mid =
01242                 (mime_image_stream_data *) image_closure;
01243   struct mime_stream_data *msd = mid->msd;
01244 
01245   if ( ( (!msd->output_emitter) ) &&
01246        ( (!msd->pluginObj2)     ) )
01247     return -1;
01248 
01249   //
01250   // If we get here, we are just eating the data this time around
01251   // and the returned URL will deal with writing the data to the viewer.
01252   // Just return the size value to the caller.
01253   //
01254   if (mid->memCacheOutputStream)
01255   {
01256     PRUint32 bytesWritten;
01257     mid->memCacheOutputStream->Write(buf, size, &bytesWritten);
01258   }
01259   return size;
01260 }
01261 
01262 MimeObject*
01263 mime_get_main_object(MimeObject* obj)
01264 {
01265   MimeContainer *cobj;
01266   if (!(mime_subclass_p(obj->clazz, (MimeObjectClass*) &mimeMessageClass))) 
01267   {
01268     return obj;
01269   }
01270   cobj = (MimeContainer*) obj;
01271   if (cobj->nchildren != 1) return obj;
01272   obj = cobj->children[0];
01273   while (obj)
01274   {
01275     if ( (!mime_subclass_p(obj->clazz,
01276          (MimeObjectClass*) &mimeMultipartSignedClass)) &&
01277          (PL_strcasecmp(obj->content_type, MULTIPART_SIGNED) != 0)
01278        )
01279     {
01280         return obj;
01281     }
01282     else
01283     {
01284       if (mime_subclass_p(obj->clazz, (MimeObjectClass*)&mimeContainerClass))
01285       {
01286         // We don't care about a signed/smime object; Go inside to the 
01287         // thing that we signed or smime'ed
01288         //
01289         cobj = (MimeContainer*) obj;
01290         if (cobj->nchildren > 0)
01291           obj = cobj->children[0];
01292         else
01293           obj = nsnull;
01294       }
01295       else
01296       {
01297         // we received a message with a child object that looks like a signed
01298         // object, but it is not a subclass of mimeContainer, so let's
01299         // return the given child object.
01300         return obj;
01301       }
01302     }
01303   }
01304   return nsnull;
01305 }
01306 
01307 PRBool MimeObjectChildIsMessageBody(MimeObject *obj, 
01308                                                                 PRBool *isAlternativeOrRelated)
01309 {
01310        char *disp = 0;
01311        PRBool bRet = PR_FALSE;
01312        MimeObject *firstChild = 0;
01313        MimeContainer *container = (MimeContainer*) obj;
01314 
01315        if (isAlternativeOrRelated)
01316               *isAlternativeOrRelated = PR_FALSE;
01317 
01318        if (!container ||
01319               !mime_subclass_p(obj->clazz, 
01320                                            (MimeObjectClass*) &mimeContainerClass))
01321        {
01322               return bRet;
01323        }
01324        else if (mime_subclass_p(obj->clazz, (MimeObjectClass*)
01325                                                   &mimeMultipartRelatedClass)) 
01326        {
01327               if (isAlternativeOrRelated)
01328                      *isAlternativeOrRelated = PR_TRUE;
01329               return bRet;
01330        }
01331        else if (mime_subclass_p(obj->clazz, (MimeObjectClass*)
01332                                                   &mimeMultipartAlternativeClass))
01333        {
01334               if (isAlternativeOrRelated)
01335                      *isAlternativeOrRelated = PR_TRUE;
01336               return bRet;
01337        }
01338 
01339        if (container->children)
01340               firstChild = container->children[0];
01341        
01342        if (!firstChild || 
01343               !firstChild->content_type || 
01344               !firstChild->headers)
01345               return bRet;
01346 
01347        disp = MimeHeaders_get (firstChild->headers,
01348                                                  HEADER_CONTENT_DISPOSITION, 
01349                                                  PR_TRUE,
01350                                                  PR_FALSE);
01351        if (disp /* && !nsCRT::strcasecmp (disp, "attachment") */)
01352               bRet = PR_FALSE;
01353        else if (!nsCRT::strcasecmp (firstChild->content_type, TEXT_PLAIN) ||
01354                       !nsCRT::strcasecmp (firstChild->content_type, TEXT_HTML) ||
01355                       !nsCRT::strcasecmp (firstChild->content_type, TEXT_MDL) ||
01356                       !nsCRT::strcasecmp (firstChild->content_type, MULTIPART_ALTERNATIVE) ||
01357                       !nsCRT::strcasecmp (firstChild->content_type, MULTIPART_RELATED) ||
01358        !nsCRT::strcasecmp (firstChild->content_type, MESSAGE_NEWS) ||
01359        !nsCRT::strcasecmp (firstChild->content_type, MESSAGE_RFC822))
01360               bRet = PR_TRUE;
01361        else
01362               bRet = PR_FALSE;
01363        PR_FREEIF(disp);
01364        return bRet;
01365 }
01366 
01367 //
01368 // New Stream Converter Interface
01369 //
01370 
01371 // Get the connnection to prefs service manager 
01372 nsIPrefBranch *
01373 GetPrefBranch(MimeDisplayOptions *opt)
01374 {
01375   if (!opt) 
01376     return nsnull;
01377 
01378   return opt->m_prefBranch;
01379 }
01380 
01381 // Get the text converter...
01382 mozITXTToHTMLConv *
01383 GetTextConverter(MimeDisplayOptions *opt)
01384 {
01385   if (!opt) 
01386     return nsnull;
01387 
01388   return opt->conv;
01389 }
01390 
01391 MimeDisplayOptions::MimeDisplayOptions()
01392 {
01393   conv = nsnull;        // For text conversion...
01394   format_out = 0;   // The format out type
01395   url = nsnull;      
01396 
01397   memset(&headers,0, sizeof(headers));    
01398   fancy_headers_p = PR_FALSE;
01399 
01400   output_vcard_buttons_p = PR_FALSE;
01401 
01402   fancy_links_p = PR_FALSE;
01403 
01404   variable_width_plaintext_p = PR_FALSE;
01405   wrap_long_lines_p = PR_FALSE;
01406   rot13_p = PR_FALSE;
01407   part_to_load = nsnull;
01408 
01409   write_html_p = PR_FALSE;
01410 
01411   decrypt_p = PR_FALSE;
01412 
01413   nice_html_only_p = PR_FALSE;
01414 
01415   whattodo = 0 ;
01416   default_charset = nsnull;
01417   override_charset = PR_FALSE;
01418   force_user_charset = PR_FALSE;
01419   stream_closure = nsnull;
01420 
01421   /* For setting up the display stream, so that the MIME parser can inform
01422         the caller of the type of the data it will be getting. */
01423   output_init_fn = nsnull;
01424   output_fn = nsnull;
01425 
01426   output_closure = nsnull;
01427 
01428   charset_conversion_fn = nsnull;
01429   rfc1522_conversion_p = PR_FALSE;
01430 
01431   file_type_fn = nsnull;
01432 
01433   passwd_prompt_fn = nsnull;
01434 
01435   html_closure = nsnull;
01436 
01437   generate_header_html_fn = nsnull;
01438   generate_post_header_html_fn = nsnull;
01439   generate_footer_html_fn = nsnull;
01440   generate_reference_url_fn = nsnull;
01441   generate_mailto_url_fn = nsnull;
01442   generate_news_url_fn = nsnull;
01443 
01444   image_begin = nsnull;
01445   image_end = nsnull;
01446   image_write_buffer = nsnull;
01447   make_image_html = nsnull;
01448   state = nsnull;
01449 
01450 #ifdef MIME_DRAFTS
01451   decompose_file_p = PR_FALSE;
01452   done_parsing_outer_headers = PR_FALSE;
01453   is_multipart_msg = PR_FALSE;
01454   decompose_init_count = 0;
01455 
01456   signed_p = PR_FALSE;
01457   caller_need_root_headers = PR_FALSE; 
01458   decompose_headers_info_fn = nsnull;
01459   decompose_file_init_fn = nsnull;
01460   decompose_file_output_fn = nsnull;
01461   decompose_file_close_fn = nsnull;
01462 #endif /* MIME_DRAFTS */
01463 
01464   attachment_icon_layer_id = 0;
01465 
01466   missing_parts = PR_FALSE;
01467   show_attachment_inline_p = PR_FALSE;
01468 }
01469 
01470 MimeDisplayOptions::~MimeDisplayOptions()
01471 {
01472   PR_FREEIF(part_to_load);
01473   PR_FREEIF(default_charset);
01474 }
01476 // Bridge routines for new stream converter XP-COM interface 
01478 extern "C" void  *
01479 mime_bridge_create_display_stream(
01480                           nsIMimeEmitter      *newEmitter,
01481                           nsStreamConverter   *newPluginObj2,
01482                           nsIURI              *uri,
01483                           nsMimeOutputType    format_out,
01484                           PRUint32           whattodo,
01485                           nsIChannel          *aChannel)
01486 {
01487   int                       status = 0;
01488   MimeObject                *obj;
01489   struct mime_stream_data   *msd;
01490   nsMIMESession             *stream = 0;
01491   
01492   if (!uri)
01493     return nsnull;
01494 
01495   msd = PR_NEWZAP(struct mime_stream_data);
01496   if (!msd) 
01497     return NULL;
01498 
01499   // Assign the new mime emitter - will handle output operations
01500   msd->output_emitter = newEmitter;
01501   msd->firstCheck = PR_TRUE;
01502 
01503   // Store the URL string for this decode operation
01504   nsCAutoString urlString;
01505   nsresult rv;
01506 
01507   // Keep a hold of the channel...
01508   msd->channel = aChannel;
01509   rv = uri->GetSpec(urlString);
01510   if (NS_SUCCEEDED(rv))
01511   {
01512     if (!urlString.IsEmpty())
01513     {
01514       msd->url_name = ToNewCString(urlString);
01515       if (!(msd->url_name))
01516       {
01517         PR_FREEIF(msd);
01518         return NULL;
01519       }
01520       nsCOMPtr<nsIMsgMessageUrl> msgUrl = do_QueryInterface(uri);
01521       if (msgUrl)
01522           msgUrl->GetOriginalSpec(&msd->orig_url_name);
01523     }
01524   }
01525   
01526   msd->format_out = format_out;       // output format
01527   msd->pluginObj2 = newPluginObj2;    // the plugin object pointer 
01528   
01529   msd->options = new MimeDisplayOptions;
01530   if (!msd->options)
01531   {
01532     PR_Free(msd);
01533     return 0;
01534   }
01535 //  memset(msd->options, 0, sizeof(*msd->options));
01536   msd->options->format_out = format_out;     // output format
01537 
01538   msd->options->m_prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
01539   if (NS_FAILED(rv))
01540   {
01541     PR_FREEIF(msd);
01542     return nsnull;
01543   }
01544 
01545   // Need the text converter...
01546   rv = CallCreateInstance(MOZ_TXTTOHTMLCONV_CONTRACTID, &(msd->options->conv));
01547   if (NS_FAILED(rv))
01548   {
01549     msd->options->m_prefBranch = 0;
01550     PR_FREEIF(msd);
01551     return nsnull;
01552   }
01553 
01554   //
01555   // Set the defaults, based on the context, and the output-type.
01556   //
01557   MIME_HeaderType = MimeHeadersAll;
01558   msd->options->write_html_p = PR_TRUE;
01559   switch (format_out) 
01560   {
01561     case nsMimeOutput::nsMimeMessageSplitDisplay:   // the wrapper HTML output to produce the split header/body display
01562     case nsMimeOutput::nsMimeMessageHeaderDisplay:  // the split header/body display
01563     case nsMimeOutput::nsMimeMessageBodyDisplay:    // the split header/body display
01564       msd->options->fancy_headers_p = PR_TRUE;
01565       msd->options->output_vcard_buttons_p = PR_TRUE;
01566       msd->options->fancy_links_p = PR_TRUE;
01567       break;
01568 
01569     case nsMimeOutput::nsMimeMessageSaveAs:         // Save As operations
01570     case nsMimeOutput::nsMimeMessageQuoting:        // all HTML quoted/printed output
01571     case nsMimeOutput::nsMimeMessagePrintOutput:
01572       msd->options->fancy_headers_p = PR_TRUE;
01573       msd->options->fancy_links_p = PR_TRUE;
01574       break;
01575 
01576     case nsMimeOutput::nsMimeMessageBodyQuoting:        // only HTML body quoted output
01577       MIME_HeaderType = MimeHeadersNone;
01578       break;
01579 
01580     case nsMimeOutput::nsMimeMessageAttach:           // handling attachment storage
01581         msd->options->write_html_p = PR_FALSE;
01582         break;
01583     case nsMimeOutput::nsMimeMessageRaw:              // the raw RFC822 data (view source) and attachments
01584     case nsMimeOutput::nsMimeMessageDraftOrTemplate:  // Loading drafts & templates
01585     case nsMimeOutput::nsMimeMessageEditorTemplate:   // Loading templates into editor
01586     case nsMimeOutput::nsMimeMessageFilterSniffer:    // generating an output that can be scan by a message filter
01587       break;
01588 
01589     case nsMimeOutput::nsMimeMessageDecrypt:
01590       msd->options->decrypt_p = PR_TRUE;
01591       msd->options->write_html_p = PR_FALSE;
01592       break;
01593   }
01594 
01596   // Now, get the libmime prefs...
01598   
01599   MIME_WrapLongLines = PR_TRUE;
01600   MIME_VariableWidthPlaintext = PR_TRUE;
01601   msd->options->force_user_charset = PR_FALSE;
01602 
01603   if (msd->options->m_prefBranch)
01604   {
01605     msd->options->m_prefBranch->GetBoolPref("mail.wrap_long_lines", &MIME_WrapLongLines);
01606     msd->options->m_prefBranch->GetBoolPref("mail.fixed_width_messages", &MIME_VariableWidthPlaintext);
01607       // 
01608       // Charset overrides takes place here
01609       //
01610       // We have a bool pref (mail.force_user_charset) to deal with attachments.
01611       // 1) If true - libmime does NO conversion and just passes it through to raptor
01612       // 2) If false, then we try to use the charset of the part and if not available, 
01613       //    the charset of the root message 
01614       //
01615     msd->options->m_prefBranch->GetBoolPref("mail.force_user_charset", &(msd->options->force_user_charset));
01616     msd->options->m_prefBranch->GetBoolPref("mail.inline_attachments", &(msd->options->show_attachment_inline_p));
01617   }
01618   /* This pref is written down in with the
01619      opposite sense of what we like to use... */
01620   MIME_VariableWidthPlaintext = !MIME_VariableWidthPlaintext;
01621 
01622   msd->options->wrap_long_lines_p = MIME_WrapLongLines;
01623   msd->options->headers = MIME_HeaderType;
01624   
01625   // We need to have the URL to be able to support the various 
01626   // arguments
01627   status = mime_parse_url_options(msd->url_name, msd->options);
01628   if (status < 0)
01629   {
01630     PR_FREEIF(msd->options->part_to_load);
01631     PR_Free(msd->options);
01632     PR_Free(msd);
01633     return 0;
01634   }
01635  
01636   if (msd->options->headers == MimeHeadersMicro &&
01637      (msd->url_name == NULL || (strncmp(msd->url_name, "news:", 5) != 0 &&
01638               strncmp(msd->url_name, "snews:", 6) != 0)) )
01639     msd->options->headers = MimeHeadersMicroPlus;
01640 
01641   msd->options->url = msd->url_name;
01642   msd->options->output_init_fn        = mime_output_init_fn;
01643   
01644   msd->options->output_fn             = mime_output_fn;
01645 
01646   msd->options->whattodo          = whattodo;
01647   msd->options->charset_conversion_fn = mime_convert_charset;
01648   msd->options->rfc1522_conversion_p  = PR_TRUE;
01649   msd->options->file_type_fn          = mime_file_type;
01650   msd->options->stream_closure        = msd;
01651   msd->options->passwd_prompt_fn      = 0;
01652   
01653   msd->options->image_begin           = mime_image_begin;
01654   msd->options->image_end             = mime_image_end;
01655   msd->options->make_image_html       = mime_image_make_image_html;
01656   msd->options->image_write_buffer    = mime_image_write_buffer;
01657   
01658   msd->options->variable_width_plaintext_p = MIME_VariableWidthPlaintext;
01659 
01660   // If this is a part, then we should emit the HTML to render the data
01661   // (i.e. embedded images)
01662   if (msd->options->part_to_load && msd->options->format_out != nsMimeOutput::nsMimeMessageBodyDisplay)
01663     msd->options->write_html_p = PR_FALSE;
01664 
01665   obj = mime_new ((MimeObjectClass *)&mimeMessageClass, (MimeHeaders *) NULL, MESSAGE_RFC822);
01666   if (!obj)
01667   {
01668     delete msd->options;
01669     PR_Free(msd);
01670     return 0;
01671   }
01672 
01673   obj->options = msd->options;
01674   msd->obj = obj;
01675   
01676   /* Both of these better not be true at the same time. */
01677   PR_ASSERT(! (obj->options->decrypt_p && obj->options->write_html_p));
01678   
01679   stream = PR_NEW(nsMIMESession);
01680   if (!stream)
01681   {
01682     delete msd->options;
01683     PR_Free(msd);
01684     PR_Free(obj);
01685     return 0;
01686   }
01687   
01688   memset (stream, 0, sizeof (*stream));  
01689   stream->name           = "MIME Conversion Stream";
01690   stream->complete       = mime_display_stream_complete;
01691   stream->abort          = mime_display_stream_abort;
01692   stream->put_block      = mime_display_stream_write;
01693   stream->data_object    = msd;
01694   
01695   status = obj->clazz->initialize(obj);
01696   if (status >= 0)
01697     status = obj->clazz->parse_begin(obj);
01698   if (status < 0)
01699   {
01700     PR_Free(stream);
01701     delete msd->options;
01702     PR_Free(msd);
01703     PR_Free(obj);
01704     return 0;
01705   }
01706   
01707   return stream;
01708 }
01709 
01710 //
01711 // Emitter Wrapper Routines!
01712 //
01713 nsIMimeEmitter *
01714 GetMimeEmitter(MimeDisplayOptions *opt)
01715 {
01716   mime_stream_data  *msd = (mime_stream_data *)opt->stream_closure;
01717   if (!msd) 
01718     return NULL;
01719 
01720   nsIMimeEmitter     *ptr = (nsIMimeEmitter *)(msd->output_emitter);
01721   return ptr;
01722 }
01723 
01724 mime_stream_data *
01725 GetMSD(MimeDisplayOptions *opt)
01726 {
01727   if (!opt)
01728     return nsnull;
01729   mime_stream_data  *msd = (mime_stream_data *)opt->stream_closure;
01730   return msd;
01731 }
01732 
01733 PRBool
01734 NoEmitterProcessing(nsMimeOutputType    format_out)
01735 {
01736   if ( (format_out == nsMimeOutput::nsMimeMessageDraftOrTemplate) ||
01737        (format_out == nsMimeOutput::nsMimeMessageEditorTemplate))
01738     return PR_TRUE;
01739   else
01740     return PR_FALSE;
01741 }
01742 
01743 extern "C" nsresult
01744 mimeEmitterAddAttachmentField(MimeDisplayOptions *opt, const char *field, const char *value)
01745 {
01746   // Check for draft processing...
01747   if (NoEmitterProcessing(opt->format_out))
01748     return NS_OK;
01749 
01750   mime_stream_data  *msd = GetMSD(opt);
01751   if (!msd) 
01752     return NS_ERROR_FAILURE;
01753 
01754   if (msd->output_emitter)
01755   {
01756     nsIMimeEmitter *emitter = (nsIMimeEmitter *)msd->output_emitter;
01757     return emitter->AddAttachmentField(field, value);
01758   }
01759 
01760   return NS_ERROR_FAILURE;
01761 }
01762 
01763 extern "C" nsresult     
01764 mimeEmitterAddHeaderField(MimeDisplayOptions *opt, const char *field, const char *value)
01765 {
01766   // Check for draft processing...
01767   if (NoEmitterProcessing(opt->format_out))
01768     return NS_OK;
01769 
01770   mime_stream_data  *msd = GetMSD(opt);
01771   if (!msd) 
01772     return NS_ERROR_FAILURE;
01773 
01774   if (msd->output_emitter)
01775   {
01776     nsIMimeEmitter *emitter = (nsIMimeEmitter *)msd->output_emitter;
01777     return emitter->AddHeaderField(field, value);
01778   }
01779 
01780   return NS_ERROR_FAILURE;
01781 }
01782 
01783 extern "C" nsresult     
01784 mimeEmitterAddAllHeaders(MimeDisplayOptions *opt, const char *allheaders, const PRInt32 allheadersize)
01785 {
01786   // Check for draft processing...
01787   if (NoEmitterProcessing(opt->format_out))
01788     return NS_OK;
01789 
01790   mime_stream_data  *msd = GetMSD(opt);
01791   if (!msd) 
01792     return NS_ERROR_FAILURE;
01793 
01794   if (msd->output_emitter)
01795   {
01796     nsIMimeEmitter *emitter = (nsIMimeEmitter *)msd->output_emitter;
01797     return emitter->AddAllHeaders(allheaders, allheadersize);
01798   }
01799 
01800   return NS_ERROR_FAILURE;
01801 }
01802 
01803 extern "C" nsresult     
01804 mimeEmitterStartAttachment(MimeDisplayOptions *opt, const char *name, const char *contentType, const char *url,
01805                            PRBool aIsExternalAttachment)
01806 {
01807   // Check for draft processing...
01808   if (NoEmitterProcessing(opt->format_out))
01809     return NS_OK;
01810 
01811   mime_stream_data  *msd = GetMSD(opt);
01812   if (!msd) 
01813     return NS_ERROR_FAILURE;
01814 
01815   if (msd->output_emitter)
01816   {
01817     nsIMimeEmitter *emitter = (nsIMimeEmitter *)msd->output_emitter;
01818     return emitter->StartAttachment(name, contentType, url, aIsExternalAttachment);
01819   }
01820 
01821   return NS_ERROR_FAILURE;
01822 }
01823 
01824 extern "C" nsresult     
01825 mimeEmitterEndAttachment(MimeDisplayOptions *opt)
01826 {
01827   // Check for draft processing...
01828   if (NoEmitterProcessing(opt->format_out))
01829     return NS_OK;
01830 
01831   mime_stream_data  *msd = GetMSD(opt);
01832   if (!msd) 
01833     return NS_ERROR_FAILURE;
01834 
01835   if (msd->output_emitter)
01836   {
01837     nsIMimeEmitter *emitter = (nsIMimeEmitter *)msd->output_emitter;
01838     if (emitter)
01839       return emitter->EndAttachment();
01840     else
01841       return NS_OK;
01842   }
01843 
01844   return NS_ERROR_FAILURE;
01845 }
01846 
01847 extern "C" nsresult     
01848 mimeEmitterEndAllAttachments(MimeDisplayOptions *opt)
01849 {
01850   // Check for draft processing...
01851   if (NoEmitterProcessing(opt->format_out))
01852     return NS_OK;
01853 
01854   mime_stream_data  *msd = GetMSD(opt);
01855   if (!msd) 
01856     return NS_ERROR_FAILURE;
01857 
01858   if (msd->output_emitter)
01859   {
01860     nsIMimeEmitter *emitter = (nsIMimeEmitter *)msd->output_emitter;
01861     if (emitter)
01862       return emitter->EndAllAttachments();
01863     else
01864       return NS_OK;
01865   }
01866 
01867   return NS_ERROR_FAILURE;
01868 }
01869 
01870 extern "C" nsresult     
01871 mimeEmitterStartBody(MimeDisplayOptions *opt, PRBool bodyOnly, const char *msgID, const char *outCharset)
01872 {
01873   // Check for draft processing...
01874   if (NoEmitterProcessing(opt->format_out))
01875     return NS_OK;
01876 
01877   mime_stream_data  *msd = GetMSD(opt);
01878   if (!msd) 
01879     return NS_ERROR_FAILURE;
01880 
01881   if (msd->output_emitter)
01882   {
01883     nsIMimeEmitter *emitter = (nsIMimeEmitter *)msd->output_emitter;
01884     return emitter->StartBody(bodyOnly, msgID, outCharset);
01885   }
01886 
01887   return NS_ERROR_FAILURE;
01888 }
01889 
01890 extern "C" nsresult     
01891 mimeEmitterEndBody(MimeDisplayOptions *opt)
01892 {
01893   // Check for draft processing...
01894   if (NoEmitterProcessing(opt->format_out))
01895     return NS_OK;
01896 
01897   mime_stream_data  *msd = GetMSD(opt);
01898   if (!msd) 
01899     return NS_ERROR_FAILURE;
01900 
01901   if (msd->output_emitter)
01902   {
01903     nsIMimeEmitter *emitter = (nsIMimeEmitter *)msd->output_emitter;
01904     return emitter->EndBody();
01905   }
01906 
01907   return NS_ERROR_FAILURE;
01908 }
01909 
01910 extern "C" nsresult     
01911 mimeEmitterEndHeader(MimeDisplayOptions *opt)
01912 {
01913   // Check for draft processing...
01914   if (NoEmitterProcessing(opt->format_out))
01915     return NS_OK;
01916 
01917   mime_stream_data  *msd = GetMSD(opt);
01918   if (!msd) 
01919     return NS_ERROR_FAILURE;
01920 
01921   if (msd->output_emitter)
01922   {
01923     nsIMimeEmitter *emitter = (nsIMimeEmitter *)msd->output_emitter;
01924     return emitter->EndHeader();
01925   }
01926 
01927   return NS_ERROR_FAILURE;
01928 }
01929 
01930 extern "C" nsresult     
01931 mimeEmitterUpdateCharacterSet(MimeDisplayOptions *opt, const char *aCharset)
01932 {
01933   // Check for draft processing...
01934   if (NoEmitterProcessing(opt->format_out))
01935     return NS_OK;
01936 
01937   mime_stream_data  *msd = GetMSD(opt);
01938   if (!msd) 
01939     return NS_ERROR_FAILURE;
01940 
01941   if (msd->output_emitter)
01942   {
01943     nsIMimeEmitter *emitter = (nsIMimeEmitter *)msd->output_emitter;
01944     return emitter->UpdateCharacterSet(aCharset);
01945   }
01946 
01947   return NS_ERROR_FAILURE;
01948 }
01949 
01950 extern "C" nsresult     
01951 mimeEmitterStartHeader(MimeDisplayOptions *opt, PRBool rootMailHeader, PRBool headerOnly, const char *msgID,
01952                        const char *outCharset)
01953 {
01954   // Check for draft processing...
01955   if (NoEmitterProcessing(opt->format_out))
01956     return NS_OK;
01957 
01958   mime_stream_data  *msd = GetMSD(opt);
01959   if (!msd) 
01960     return NS_ERROR_FAILURE;
01961 
01962   if (msd->output_emitter)
01963   {
01964     nsIMimeEmitter *emitter = (nsIMimeEmitter *)msd->output_emitter;
01965     return emitter->StartHeader(rootMailHeader, headerOnly, msgID, outCharset);
01966   }
01967 
01968   return NS_ERROR_FAILURE;
01969 }
01970 
01971 
01972 extern "C" nsresult
01973 mimeSetNewURL(nsMIMESession *stream, char *url)
01974 {
01975   if ( (!stream) || (!url) || (!*url) )
01976     return NS_ERROR_FAILURE;
01977 
01978   mime_stream_data  *msd = (mime_stream_data *)stream->data_object;
01979   if (!msd)
01980     return NS_ERROR_FAILURE;
01981 
01982   char *tmpPtr = nsCRT::strdup(url);
01983   if (!tmpPtr)
01984     return NS_ERROR_FAILURE;
01985 
01986   PR_FREEIF(msd->url_name);
01987   msd->url_name = nsCRT::strdup(tmpPtr);
01988   return NS_OK;
01989 }
01990 
01991 #define     MIME_URL "chrome://messenger/locale/mime.properties"
01992 
01993 extern "C" 
01994 char *
01995 MimeGetStringByID(PRInt32 stringID)
01996 {
01997   char          *tempString = nsnull;
01998   const char    *resultString = "???";
01999   nsresult      res = NS_OK;
02000 
02001   if (!stringBundle)
02002   {
02003     char* propertyURL = NULL;
02004 
02005     propertyURL = MIME_URL;
02006 
02007     nsCOMPtr<nsIStringBundleService> sBundleService = 
02008              do_GetService(NS_STRINGBUNDLE_CONTRACTID, &res); 
02009     if (NS_SUCCEEDED(res) && (nsnull != sBundleService)) 
02010     {
02011       res = sBundleService->CreateBundle(propertyURL, getter_AddRefs(stringBundle));
02012     }
02013   }
02014 
02015   if (stringBundle)
02016   {
02017     nsXPIDLString v;
02018     res = stringBundle->GetStringFromID(stringID, getter_Copies(v));
02019 
02020     if (NS_SUCCEEDED(res)) 
02021       tempString = ToNewUTF8String(v);
02022   }
02023 
02024   if (!tempString)
02025     tempString = nsCRT::strdup(resultString);
02026 
02027   return tempString;
02028 }
02029 
02030 void PR_CALLBACK
02031 ResetChannelCharset(MimeObject *obj) 
02032 { 
02033   if (obj->options && obj->options->stream_closure && 
02034       obj->options->default_charset && obj->headers ) 
02035   { 
02036     mime_stream_data  *msd = (mime_stream_data *) (obj->options->stream_closure); 
02037     char *ct = MimeHeaders_get (obj->headers, HEADER_CONTENT_TYPE, PR_FALSE, PR_FALSE); 
02038     if ( (ct) && (msd) && (msd->channel) ) 
02039     { 
02040       char *ptr = strstr(ct, "charset="); 
02041       if (ptr)
02042       {
02043         // First, setup the channel!
02044         msd->channel->SetContentType(nsDependentCString(ct));
02045 
02046         // Second, if this is a Save As operation, then we need to convert
02047         // to override the output charset!
02048         mime_stream_data  *msd = GetMSD(obj->options);
02049         if ( (msd) && (msd->format_out == nsMimeOutput::nsMimeMessageSaveAs) )
02050         {
02051           // Extract the charset alone
02052           char  *cSet = nsnull;
02053           if (*(ptr+8) == '"')
02054             cSet = nsCRT::strdup(ptr+9);
02055           else
02056             cSet = nsCRT::strdup(ptr+8);
02057           if (cSet)
02058           {
02059             char *ptr2 = cSet;
02060             while ( (*cSet) && (*cSet != ' ') && (*cSet != ';') && 
02061                     (*cSet != nsCRT::CR) && (*cSet != nsCRT::LF) && (*cSet != '"') )
02062               ptr2++;
02063             
02064             if (*cSet) {
02065               PR_FREEIF(obj->options->default_charset);
02066               obj->options->default_charset = nsCRT::strdup(cSet);
02067               obj->options->override_charset = PR_TRUE;
02068             }
02069 
02070             PR_FREEIF(cSet);
02071           }
02072         }
02073       }
02074       PR_FREEIF(ct); 
02075     } 
02076   } 
02077 }
02078 
02080   // Function to get up mail/news fontlang 
02082 
02083 
02084 nsresult GetMailNewsFont(MimeObject *obj, PRBool styleFixed,  PRInt32 *fontPixelSize, 
02085                                    PRInt32 *fontSizePercentage, nsCString& fontLang)
02086 {
02087   nsresult rv = NS_OK;
02088 
02089   nsIPrefBranch *prefBranch = GetPrefBranch(obj->options);
02090   if (prefBranch) {
02091     MimeInlineText  *text = (MimeInlineText *) obj;
02092     nsCAutoString charset;
02093 
02094     // get a charset
02095     if (!text->initializeCharset)
02096       ((MimeInlineTextClass*)&mimeInlineTextClass)->initialize_charset(obj);
02097 
02098     if (!text->charset || !(*text->charset))
02099       charset.Assign("us-ascii");
02100     else
02101       charset.Assign(text->charset);
02102 
02103     nsCOMPtr<nsICharsetConverterManager> charSetConverterManager2;
02104     nsCOMPtr<nsIAtom> langGroupAtom;
02105     nsCAutoString prefStr;
02106 
02107     ToLowerCase(charset);
02108 
02109     charSetConverterManager2 = do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
02110     if ( NS_FAILED(rv))
02111       return rv;
02112 
02113     // get a language, e.g. x-western, ja
02114     rv = charSetConverterManager2->GetCharsetLangGroup(charset.get(), getter_AddRefs(langGroupAtom));
02115     if (NS_FAILED(rv))
02116       return rv;
02117     rv = langGroupAtom->ToUTF8String(fontLang);
02118     if (NS_FAILED(rv))
02119       return rv;
02120 
02121     // get a font size from pref
02122     prefStr.Assign(!styleFixed ? "font.size.variable." : "font.size.fixed.");
02123     prefStr.Append(fontLang);
02124     rv = prefBranch->GetIntPref(prefStr.get(), fontPixelSize);
02125     if (NS_FAILED(rv))
02126       return rv;
02127 
02128     nsCOMPtr<nsIPrefBranch> prefDefBranch;
02129     nsCOMPtr<nsIPrefService> prefSvc(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
02130     if(prefSvc)
02131       rv = prefSvc->GetDefaultBranch("", getter_AddRefs(prefDefBranch));
02132 
02133     if(!prefDefBranch)
02134       return rv;
02135 
02136     // get original font size
02137     PRInt32 originalSize;
02138     rv = prefDefBranch->GetIntPref(prefStr.get(), &originalSize);
02139     if (NS_FAILED(rv))
02140       return rv;
02141 
02142     // calculate percentage
02143     *fontSizePercentage = originalSize ? 
02144                           (PRInt32)((float)*fontPixelSize / (float)originalSize * 100) : 0;
02145 
02146   }
02147 
02148   return NS_OK;
02149 }
02150 
02151 /* This function syncronously converts an HTML document (as string)
02152    to plaintext (as string) using the Gecko converter.
02153 
02154    flags: see nsIDocumentEncoder.h
02155 */
02156 // TODO: |printf|s?
02157 /* <copy from="mozilla/parser/htmlparser/test/outsinks/Convert.cpp"
02158          author="akk"
02159          adapted-by="Ben Bucksch"
02160          comment=" 'This code would not have been possible without akk.' ;-P.
02161                    No, really. "
02162    > */
02163 nsresult
02164 HTML2Plaintext(const nsString& inString, nsString& outString,
02165                PRUint32 flags, PRUint32 wrapCol)
02166 {
02167   nsresult rv = NS_OK;
02168 
02169 #if DEBUG_BenB
02170   printf("Converting HTML to plaintext\n");
02171   char* charstar = ToNewUTF8String(inString);
02172   printf("HTML source is:\n--------------------\n%s--------------------\n",
02173          charstar);
02174   delete[] charstar;
02175 #endif
02176 
02177   // Create a parser
02178   nsCOMPtr<nsIParser> parser = do_CreateInstance(kParserCID);
02179   NS_ENSURE_TRUE(parser, NS_ERROR_FAILURE);
02180 
02181   // Create the appropriate output sink
02182   nsCOMPtr<nsIContentSink> sink =
02183                                do_CreateInstance(NS_PLAINTEXTSINK_CONTRACTID);
02184   NS_ENSURE_TRUE(sink, NS_ERROR_FAILURE);
02185 
02186   nsCOMPtr<nsIHTMLToTextSink> textSink(do_QueryInterface(sink));
02187   NS_ENSURE_TRUE(textSink, NS_ERROR_FAILURE);
02188 
02189   textSink->Initialize(&outString, flags, wrapCol);
02190 
02191   parser->SetContentSink(sink);
02192   nsCOMPtr<nsIDTD> dtd = do_CreateInstance(kNavDTDCID);
02193   NS_ENSURE_TRUE(dtd, NS_ERROR_FAILURE);
02194 
02195   parser->RegisterDTD(dtd);
02196 
02197   rv = parser->Parse(inString, 0, NS_LITERAL_CSTRING("text/html"),
02198                      PR_FALSE, PR_TRUE);
02199 
02200   // Aah! How can NS_ERROR and NS_ABORT_IF_FALSE be no-ops in release builds???
02201   if (NS_FAILED(rv))
02202   {
02203     NS_ERROR("Parse() failed!");
02204     return rv;
02205   }
02206 
02207 #if DEBUG_BenB
02208   charstar = ToNewUTF8String(outString);
02209   printf("Plaintext is:\n--------------------\n%s--------------------\n",
02210          charstar);
02211   delete[] charstar;
02212 #endif
02213 
02214   return rv;
02215 }
02216 // </copy>
02217 
02218 
02219 
02220 /* This function syncronously sanitizes an HTML document (string->string)
02221    using the Gecko ContentSink mozISanitizingHTMLSerializer.
02222 
02223    flags: currently unused
02224    allowedTags: see mozSanitizingHTMLSerializer::ParsePrefs()
02225 */
02226 // copied from HTML2Plaintext above
02227 nsresult
02228 HTMLSanitize(const nsString& inString, nsString& outString,
02229              PRUint32 flags, const nsAString& allowedTags)
02230 {
02231   nsresult rv = NS_OK;
02232 
02233 #if DEBUG_BenB
02234   printf("Sanitizing HTML\n");
02235   char* charstar = ToNewUTF8String(inString);
02236   printf("Original HTML is:\n--------------------\n%s--------------------\n",
02237          charstar);
02238   delete[] charstar;
02239 #endif
02240 
02241   // Create a parser
02242   nsCOMPtr<nsIParser> parser = do_CreateInstance(kParserCID);
02243   NS_ENSURE_TRUE(parser, NS_ERROR_FAILURE);
02244 
02245   // Create the appropriate output sink
02246   nsCOMPtr<nsIContentSink> sink =
02247                     do_CreateInstance(MOZ_SANITIZINGHTMLSERIALIZER_CONTRACTID);
02248   NS_ENSURE_TRUE(sink, NS_ERROR_FAILURE);
02249 
02250   nsCOMPtr<mozISanitizingHTMLSerializer> sanSink(do_QueryInterface(sink));
02251   NS_ENSURE_TRUE(sanSink, NS_ERROR_FAILURE);
02252 
02253   sanSink->Initialize(&outString, flags, allowedTags);
02254 
02255   parser->SetContentSink(sink);
02256   nsCOMPtr<nsIDTD> dtd = do_CreateInstance(kNavDTDCID);
02257   NS_ENSURE_TRUE(dtd, NS_ERROR_FAILURE);
02258 
02259   parser->RegisterDTD(dtd);
02260 
02261   rv = parser->Parse(inString, 0, NS_LITERAL_CSTRING("text/html"),
02262                      PR_FALSE, PR_TRUE);
02263   if (NS_FAILED(rv))
02264   {
02265     NS_ERROR("Parse() failed!");
02266     return rv;
02267   }
02268 
02269 #if DEBUG_BenB
02270   charstar = ToNewUTF8String(outString);
02271   printf("Sanitized HTML is:\n--------------------\n%s--------------------\n",
02272          charstar);
02273   delete[] charstar;
02274 #endif
02275 
02276   return rv;
02277 }