Back to index

lightning-sunbird  0.9+nobinonly
mimei.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1998
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
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  * This Original Code has been modified by IBM Corporation. Modifications made by IBM 
00039  * described herein are Copyright (c) International Business Machines Corporation, 2000.
00040  * Modifications to Mozilla code or documentation identified per MPL Section 3.3
00041  *
00042  * Date             Modified by     Description of modification
00043  * 04/20/2000       IBM Corp.      OS/2 VisualAge build.
00044  */
00045 
00046 #include "nsCOMPtr.h"
00047 #include "mimeobj.h" /*  MimeObject (abstract)                                             */
00048 #include "mimecont.h"       /*   |--- MimeContainer (abstract)                             */
00049 #include "mimemult.h"       /*   |     |--- MimeMultipart (abstract)                */
00050 #include "mimemmix.h"       /*   |     |     |--- MimeMultipartMixed                */
00051 #include "mimemdig.h"       /*   |     |     |--- MimeMultipartDigest               */
00052 #include "mimempar.h"       /*   |     |     |--- MimeMultipartParallel                    */
00053 #include "mimemalt.h"       /*   |     |     |--- MimeMultipartAlternative          */
00054 #include "mimemrel.h"       /*   |     |     |--- MimeMultipartRelated                     */
00055 #include "mimemapl.h"       /*   |     |     |--- MimeMultipartAppleDouble          */
00056 #include "mimesun.h" /*   |     |     |--- MimeSunAttachment                        */
00057 #include "mimemsig.h"       /*   |     |     |--- MimeMultipartSigned (abstract)*/
00058 #ifdef ENABLE_SMIME
00059 #include "mimemcms.h"   /*   |     |           |---MimeMultipartSignedCMS   */
00060 #endif
00061 #include "mimecryp.h"       /*   |     |--- MimeEncrypted (abstract)                */
00062 #ifdef ENABLE_SMIME
00063 #include "mimecms.h" /*   |     |     |--- MimeEncryptedPKCS7                */
00064 #endif
00065 #include "mimemsg.h" /*   |     |--- MimeMessage                                           */
00066 #include "mimeunty.h"       /*   |     |--- MimeUntypedText                                       */
00067 #include "mimeleaf.h"       /*   |--- MimeLeaf (abstract)                                         */
00068 #include "mimetext.h"       /*   |     |--- MimeInlineText (abstract)               */
00069 #include "mimetpla.h"       /*   |     |     |--- MimeInlineTextPlain               */
00070 #include "mimethpl.h"       /*   |     |     |     |--- M.I.TextHTMLAsPlaintext */
00071 #include "mimetpfl.h"   /*   |     |     |--- MimeInlineTextPlainFlowed     */
00072 #include "mimethtm.h"       /*   |     |     |--- MimeInlineTextHTML                */
00073 #include "mimethsa.h"       /*   |     |     |     |--- M.I.TextHTMLSanitized   */
00074 #include "mimetric.h"       /*   |     |     |--- MimeInlineTextRichtext            */
00075 #include "mimetenr.h"       /*   |     |     |     |--- MimeInlineTextEnriched      */
00076 /* SUPPORTED VIA PLUGIN      |     |     |--- MimeInlineTextVCard           */
00077 #include "mimeiimg.h"       /*   |     |--- MimeInlineImage                                       */
00078 #include "mimeeobj.h"       /*   |     |--- MimeExternalObject                             */
00079 #include "mimeebod.h"       /*   |--- MimeExternalBody                                            */
00080                         /* If you add classes here,also add them to mimei.h */
00081 #include "prlog.h"
00082 #include "prmem.h"
00083 #include "prenv.h"
00084 #include "plstr.h"
00085 #include "prlink.h"
00086 #include "prprf.h"
00087 #include "mimecth.h"
00088 #include "mimebuf.h"
00089 #include "nsIServiceManager.h"
00090 #include "nsCRT.h"
00091 #include "mimemoz2.h"
00092 #include "nsIMimeContentTypeHandler.h"
00093 #include "nsIComponentManager.h"
00094 #include "nsCategoryManagerUtils.h"
00095 #include "nsXPCOMCID.h"
00096 #include "nsISimpleMimeConverter.h"
00097 #include "nsSimpleMimeConverterStub.h"
00098 #include "nsVoidArray.h"
00099 #include "nsMimeStringResources.h"
00100 #include "nsMimeTypes.h"
00101 #include "nsMsgUtils.h"
00102 #include "nsIPrefBranch.h"
00103 #include "imgILoader.h"
00104 
00105 #ifdef MOZ_THUNDERBIRD
00106 #include "nsIMsgMailNewsUrl.h"
00107 #include "nsIMsgHdr.h"
00108 #endif
00109 
00110 #ifdef MOZ_THUNDERBIRD
00111 
00112 // forward declaration
00113 void getMsgHdrForCurrentURL(MimeDisplayOptions *opts, nsIMsgDBHdr ** aMsgHdr);
00114 
00115 #endif
00116 
00117 #define       IMAP_EXTERNAL_CONTENT_HEADER "X-Mozilla-IMAP-Part"
00118 #define       EXTERNAL_ATTACHMENT_URL_HEADER "X-Mozilla-External-Attachment-URL"
00119 
00120 /* ==========================================================================
00121    Allocation and destruction
00122    ==========================================================================
00123  */
00124 static int mime_classinit(MimeObjectClass *clazz);
00125 
00126 /* 
00127  * These are the necessary defines/variables for doing
00128  * content type handlers in external plugins.
00129  */
00130 typedef struct {
00131   char        content_type[128];
00132   PRBool      force_inline_display;
00133 } cthandler_struct;
00134 
00135 nsVoidArray         *ctHandlerList = NULL;
00136 PRBool              foundIt = PR_FALSE; 
00137 PRBool              force_display = PR_FALSE;
00138 
00139 PRBool PR_CALLBACK
00140 EnumFunction(void* aElement, void *aData)
00141 {
00142   cthandler_struct    *ptr = (cthandler_struct *) aElement;
00143   char                *ctPtr = (char *)aData;
00144 
00145   if ( (!aElement) || (!aData) )
00146     return PR_TRUE;
00147 
00148   if (nsCRT::strcasecmp(ctPtr, ptr->content_type) == 0)
00149   {
00150     foundIt = PR_TRUE;
00151     force_display = ptr->force_inline_display;
00152     return PR_FALSE;
00153   }
00154 
00155   return PR_TRUE;
00156 }
00157 
00158 /*
00159  * This will return TRUE if the content_type is found in the
00160  * list, FALSE if it is not found. 
00161  */
00162 PRBool
00163 find_content_type_attribs(const char *content_type, 
00164                           PRBool     *force_inline_display)
00165 {
00166   *force_inline_display = PR_FALSE;
00167   if (!ctHandlerList)
00168     return PR_FALSE;
00169 
00170   foundIt = PR_FALSE;
00171   force_display = PR_FALSE;
00172   ctHandlerList->EnumerateForwards(EnumFunction, (void *)content_type);
00173   if (foundIt)
00174     *force_inline_display = force_display;
00175 
00176   return (foundIt);
00177 }
00178 
00179 void
00180 add_content_type_attribs(const char *content_type, 
00181                          contentTypeHandlerInitStruct  *ctHandlerInfo)
00182 {
00183   cthandler_struct    *ptr = NULL;
00184   PRBool              force_inline_display;
00185 
00186   if (find_content_type_attribs(content_type, &force_inline_display))
00187     return;
00188 
00189   if ( (!content_type) || (!ctHandlerInfo) )
00190     return;
00191 
00192   if (!ctHandlerList)
00193     ctHandlerList = new nsVoidArray();
00194 
00195   if (!ctHandlerList)
00196     return;
00197 
00198   ptr = (cthandler_struct *) PR_MALLOC(sizeof(cthandler_struct));
00199   if (!ptr)
00200     return;
00201 
00202   PL_strncpy(ptr->content_type, content_type, sizeof(ptr->content_type));
00203   ptr->force_inline_display = ctHandlerInfo->force_inline_display;
00204   ctHandlerList->AppendElement(ptr);
00205 }
00206 
00207 /* 
00208  * This routine will find all content type handler for a specifc content
00209  * type (if it exists)
00210  */
00211 PRBool
00212 force_inline_display(const char *content_type)
00213 {
00214   PRBool     force_inline_disp;
00215 
00216   find_content_type_attribs(content_type, &force_inline_disp);
00217   return (force_inline_disp);
00218 }
00219 
00220 /* 
00221  * This routine will find all content type handler for a specifc content
00222  * type (if it exists) and is defined to the nsRegistry
00223  */
00224 MimeObjectClass *
00225 mime_locate_external_content_handler(const char *content_type,   
00226                                      contentTypeHandlerInitStruct  *ctHandlerInfo)
00227 {
00228   MimeObjectClass               *newObj = NULL;
00229   char                          lookupID[256];
00230   nsCOMPtr<nsIMimeContentTypeHandler>     ctHandler;
00231   nsresult rv;
00232 
00233   PR_snprintf(lookupID, sizeof(lookupID), "@mozilla.org/mimecth;1?type=%s", content_type);
00234   
00235   ctHandler = do_CreateInstance(lookupID, &rv);
00236   if (NS_FAILED(rv) || !ctHandler) {
00237     nsCOMPtr<nsICategoryManager> catman =
00238       do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
00239     if (NS_FAILED(rv))
00240       return nsnull;
00241 
00242     nsXPIDLCString value;
00243     rv = catman->GetCategoryEntry(NS_SIMPLEMIMECONVERTERS_CATEGORY,
00244                                   content_type, getter_Copies(value));
00245     if (NS_FAILED(rv) || !value)
00246       return nsnull;
00247     rv = MIME_NewSimpleMimeConverterStub(content_type,
00248                                          getter_AddRefs(ctHandler));
00249     if (NS_FAILED(rv) || !ctHandler)
00250       return nsnull;
00251   }
00252   
00253   rv = ctHandler->CreateContentTypeHandlerClass(content_type, ctHandlerInfo, &newObj);
00254   if (NS_FAILED(rv))
00255     return nsnull;
00256 
00257   add_content_type_attribs(content_type, ctHandlerInfo);
00258   return newObj;
00259 }
00260 
00261 /* This is necessary to expose the MimeObject method outside of this DLL */
00262 int
00263 MIME_MimeObject_write(MimeObject *obj, const char *output, PRInt32 length, PRBool user_visible_p)
00264 {
00265   return MimeObject_write(obj, output, length, user_visible_p);
00266 }
00267 
00268 MimeObject *
00269 mime_new (MimeObjectClass *clazz, MimeHeaders *hdrs,
00270                 const char *override_content_type)
00271 {
00272   int size = clazz->instance_size;
00273   MimeObject *object;
00274   int status;
00275 
00276   /* Some assertions to verify that this isn't random junk memory... */
00277   NS_ASSERTION(clazz->class_name && strlen(clazz->class_name) > 0, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
00278   NS_ASSERTION(size > 0 && size < 1000, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
00279 
00280   if (!clazz->class_initialized)
00281        {
00282          status = mime_classinit(clazz);
00283          if (status < 0) return 0;
00284        }
00285 
00286   NS_ASSERTION(clazz->initialize && clazz->finalize, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
00287 
00288   if (hdrs)
00289        {
00290     hdrs = MimeHeaders_copy (hdrs);
00291          if (!hdrs) return 0;
00292        }
00293 
00294   object = (MimeObject *) PR_MALLOC(size);
00295   if (!object) return 0;
00296 
00297   memset(object, 0, size);
00298   object->clazz = clazz;
00299   object->headers = hdrs;
00300   object->dontShowAsAttachment = PR_FALSE;
00301 
00302   if (override_content_type && *override_content_type)
00303        object->content_type = nsCRT::strdup(override_content_type);
00304 
00305   status = clazz->initialize(object);
00306   if (status < 0)
00307        {
00308          clazz->finalize(object);
00309          PR_Free(object);
00310          return 0;
00311        }
00312 
00313   return object;
00314 }
00315 
00316 void
00317 mime_free (MimeObject *object)
00318 {
00319 # ifdef DEBUG__
00320   int i, size = object->clazz->instance_size;
00321   PRUint32 *array = (PRUint32*) object;
00322 # endif /* DEBUG */
00323 
00324   object->clazz->finalize(object);
00325 
00326 # ifdef DEBUG__
00327   for (i = 0; i < (size / sizeof(*array)); i++)
00328        array[i] = (PRUint32) 0xDEADBEEF;
00329 # endif /* DEBUG */
00330 
00331   PR_Free(object);
00332 }
00333 
00334 
00335 PRBool mime_is_allowed_class(const MimeObjectClass *clazz,
00336                              PRInt32 types_of_classes_to_disallow)
00337 {
00338   if (types_of_classes_to_disallow == 0)
00339     return PR_TRUE;
00340   PRBool avoid_html = (types_of_classes_to_disallow >= 1);
00341   PRBool avoid_images = (types_of_classes_to_disallow >= 2);
00342   PRBool avoid_strange_content = (types_of_classes_to_disallow >= 3);
00343   PRBool allow_only_vanilla_classes = (types_of_classes_to_disallow == 100);
00344 
00345   if (allow_only_vanilla_classes)
00346     /* A "safe" class is one that is unlikely to have security bugs or to
00347        allow security exploits or one that is essential for the usefulness
00348        of the application, even for paranoid users.
00349        What's included here is more personal judgement than following
00350        strict rules, though, unfortunately.
00351        The function returns true only for known good classes, i.e. is a
00352        "whitelist" in this case.
00353        This idea comes from Georgi Guninski.
00354     */
00355     return
00356       (
00357         clazz == (MimeObjectClass *)&mimeInlineTextPlainClass ||
00358         clazz == (MimeObjectClass *)&mimeInlineTextPlainFlowedClass ||
00359         clazz == (MimeObjectClass *)&mimeInlineTextHTMLSanitizedClass ||
00360         clazz == (MimeObjectClass *)&mimeInlineTextHTMLAsPlaintextClass ||
00361            /* The latter 2 classes bear some risk, because they use the Gecko
00362               HTML parser, but the user has the option to make an explicit
00363               choice in this case, via html_as. */
00364         clazz == (MimeObjectClass *)&mimeMultipartMixedClass ||
00365         clazz == (MimeObjectClass *)&mimeMultipartAlternativeClass ||
00366         clazz == (MimeObjectClass *)&mimeMultipartDigestClass ||
00367         clazz == (MimeObjectClass *)&mimeMultipartAppleDoubleClass ||
00368         clazz == (MimeObjectClass *)&mimeMessageClass ||
00369         clazz == (MimeObjectClass *)&mimeExternalObjectClass ||
00370                                /*    mimeUntypedTextClass? -- does uuencode */
00371 #ifdef ENABLE_SMIME
00372         clazz == (MimeObjectClass *)&mimeMultipartSignedCMSClass ||
00373         clazz == (MimeObjectClass *)&mimeEncryptedCMSClass ||
00374 #endif
00375         clazz == 0
00376       );
00377 
00378   /* Contrairy to above, the below code is a "blacklist", i.e. it
00379      *excludes* some "bad" classes. */
00380   return
00381      !(
00382         (avoid_html
00383          && (
00384               clazz == (MimeObjectClass *)&mimeInlineTextHTMLClass
00385                          /* Should not happen - we protect against that in
00386                             mime_find_class(). Still for safety... */
00387             )) ||
00388         (avoid_images
00389          && (
00390               clazz == (MimeObjectClass *)&mimeInlineImageClass
00391             )) ||
00392         (avoid_strange_content
00393          && (
00394               clazz == (MimeObjectClass *)&mimeInlineTextEnrichedClass ||
00395               clazz == (MimeObjectClass *)&mimeInlineTextRichtextClass ||
00396               clazz == (MimeObjectClass *)&mimeSunAttachmentClass ||
00397               clazz == (MimeObjectClass *)&mimeExternalBodyClass
00398             ))
00399       );
00400 }
00401 
00402 
00403 #ifdef MOZ_THUNDERBIRD
00404 
00405 void getMsgHdrForCurrentURL(MimeDisplayOptions *opts, nsIMsgDBHdr ** aMsgHdr)
00406 {
00407   *aMsgHdr = nsnull;
00408 
00409   if (!opts)
00410     return; 
00411   
00412   mime_stream_data *msd = (mime_stream_data *) (opts->stream_closure);
00413   if (!msd)
00414     return;
00415 
00416   nsIChannel *channel = msd->channel;  // note the lack of ref counting...
00417   if (channel)
00418   {
00419     nsCOMPtr<nsIURI> uri;
00420     nsCOMPtr<nsIMsgMessageUrl> msgURI;
00421     channel->GetURI(getter_AddRefs(uri));
00422     if (uri)
00423     {
00424       msgURI = do_QueryInterface(uri);
00425       if (msgURI)
00426       {
00427         msgURI->GetMessageHeader(aMsgHdr);
00428         if (*aMsgHdr)
00429           return;
00430         nsXPIDLCString rdfURI;
00431         msgURI->GetUri(getter_Copies(rdfURI));
00432         if (rdfURI.get())
00433         {
00434           nsCOMPtr<nsIMsgDBHdr> msgHdr;
00435           GetMsgDBHdrFromURI(rdfURI, getter_AddRefs(msgHdr));
00436           NS_IF_ADDREF(*aMsgHdr = msgHdr);
00437         }
00438       }
00439     }
00440   }
00441 
00442   return;
00443 }
00444 #endif
00445 
00446 MimeObjectClass *
00447 mime_find_class (const char *content_type, MimeHeaders *hdrs,
00448                              MimeDisplayOptions *opts, PRBool exact_match_p)
00449 {
00450   MimeObjectClass *clazz = 0;
00451   MimeObjectClass *tempClass = 0;
00452   contentTypeHandlerInitStruct  ctHandlerInfo;
00453 
00454   // Read some prefs
00455   nsIPrefBranch *prefBranch = GetPrefBranch(opts); 
00456   PRInt32 html_as = 0;  // def. see below
00457   PRInt32 types_of_classes_to_disallow = 0;  /* Let only a few libmime classes
00458        process incoming data. This protects from bugs (e.g. buffer overflows)
00459        and from security loopholes (e.g. allowing unchecked HTML in some
00460        obscure classes, although the user has html_as > 0).
00461        This option is mainly for the UI of html_as.
00462        0 = allow all available classes
00463        1 = Use hardcoded blacklist to avoid rendering (incoming) HTML
00464        2 = ... and images
00465        3 = ... and some other uncommon content types
00466        100 = Use hardcoded whitelist to avoid even more bugs(buffer overflows).
00467            This mode will limit the features available (e.g. uncommon
00468            attachment types and inline images) and is for paranoid users.
00469        */
00470   if (opts && opts->format_out != nsMimeOutput::nsMimeMessageFilterSniffer &&
00471                opts->format_out != nsMimeOutput::nsMimeMessageDecrypt
00472                && opts->format_out != nsMimeOutput::nsMimeMessageAttach)
00473     if (prefBranch)
00474     {
00475       prefBranch->GetIntPref("mailnews.display.html_as", &html_as);
00476       prefBranch->GetIntPref("mailnews.display.disallow_mime_handlers",
00477                             &types_of_classes_to_disallow);
00478       if (types_of_classes_to_disallow > 0 && html_as == 0)
00479            // We have non-sensical prefs. Do some fixup.
00480         html_as = 1;
00481     }
00482 
00483 #ifdef MOZ_THUNDERBIRD
00484   // first, check to see if the message has been marked as JUNK. If it has, 
00485   // then force the message to be rendered as simple.
00486   PRBool sanitizeJunkMail = PR_FALSE;
00487 
00488   // it is faster to read the pref first then figure out the msg hdr for the current url only if we have to
00489   // XXX instead of reading this pref every time, part of mime should be an observer listening to this pref change
00490   // and updating internal state accordingly. But none of the other prefs in this file seem to be doing that...=(
00491   if (prefBranch)
00492     prefBranch->GetBoolPref("mail.spam.display.sanitize", &sanitizeJunkMail);
00493 
00494   if (sanitizeJunkMail)
00495   {
00496     nsCOMPtr<nsIMsgDBHdr> msgHdr;
00497     getMsgHdrForCurrentURL(opts, getter_AddRefs(msgHdr));
00498     if (msgHdr)
00499     {
00500       nsXPIDLCString junkScoreStr;
00501       (void) msgHdr->GetStringProperty("junkscore", getter_Copies(junkScoreStr));
00502       if (html_as == 0 && junkScoreStr.get() && atoi(junkScoreStr.get()) > 50)
00503         html_as = 3; // 3 == Simple HTML
00504     } // if msgHdr
00505   } // if we are supposed to sanitize junk mail
00506 #endif
00507 
00508   /*
00509   * What we do first is check for an external content handler plugin. 
00510   * This will actually extend the mime handling by calling a routine 
00511   * which will allow us to load an external content type handler
00512   * for specific content types. If one is not found, we will drop back
00513   * to the default handler.
00514   */
00515   if ((tempClass = mime_locate_external_content_handler(content_type, &ctHandlerInfo)) != NULL)
00516   {
00517 #ifdef MOZ_THUNDERBIRD
00518       // This is a case where we only want to add this property if we are a thunderbird build AND
00519       // we have found an external mime content handler for text/calendar 
00520       // This will enable iMIP support in Lightning
00521       if ( hdrs && (!nsCRT::strncasecmp(content_type, "text/calendar", 13)))
00522       {                    
00523           char *full_content_type = MimeHeaders_get(hdrs, HEADER_CONTENT_TYPE, PR_FALSE, PR_FALSE);          
00524           if (full_content_type) 
00525           {
00526               char *imip_method = MimeHeaders_get_parameter(full_content_type, "method", NULL, NULL);
00527               nsCOMPtr<nsIMsgDBHdr> msgHdr;
00528               getMsgHdrForCurrentURL(opts, getter_AddRefs(msgHdr));
00529               if (msgHdr)
00530                 msgHdr->SetStringProperty("imip_method", (imip_method) ? imip_method : "nomethod");
00531               // PR_Free checks for null
00532               PR_Free(imip_method);                                    
00533               PR_Free(full_content_type);
00534           }          
00535       }
00536 #endif
00537 
00538     if (types_of_classes_to_disallow > 0
00539         && (!nsCRT::strncasecmp(content_type, "text/x-vcard", 12))
00540        )
00541       /* Use a little hack to prevent some dangerous plugins, which ship
00542          with Mozilla, to run.
00543          For the truely user-installed plugins, we rely on the judgement
00544          of the user. */
00545     {
00546       if (!exact_match_p)
00547         clazz = (MimeObjectClass *)&mimeExternalObjectClass; // As attachment
00548     }
00549     else
00550       clazz = (MimeObjectClass *)tempClass;
00551   }
00552   else
00553   {
00554     if (!content_type || !*content_type ||
00555         !nsCRT::strcasecmp(content_type, "text"))  /* with no / in the type */
00556       clazz = (MimeObjectClass *)&mimeUntypedTextClass;
00557     
00558     /* Subtypes of text...
00559     */
00560     else if (!nsCRT::strncasecmp(content_type,                 "text/", 5))
00561     {
00562       if      (!nsCRT::strcasecmp(content_type+5,              "html"))
00563       {
00564         if (opts
00565             && opts->format_out == nsMimeOutput::nsMimeMessageSaveAs)
00566           // SaveAs in new modes doesn't work yet.
00567         {
00568           clazz = (MimeObjectClass *)&mimeInlineTextHTMLClass;
00569           types_of_classes_to_disallow = 0;
00570         }
00571         else if (html_as == 0) // Render sender's HTML
00572           clazz = (MimeObjectClass *)&mimeInlineTextHTMLClass;
00573         else if (html_as == 1) // convert HTML to plaintext
00574           // Do a HTML->TXT->HTML conversion, see mimethpl.h.
00575           clazz = (MimeObjectClass *)&mimeInlineTextHTMLAsPlaintextClass;
00576         else if (html_as == 2) // display HTML source
00577           /* This is for the freaks. Treat HTML as plaintext,
00578              which will cause the HTML source to be displayed.
00579              Not very user-friendly, but some seem to want this. */
00580           clazz = (MimeObjectClass *)&mimeInlineTextPlainClass;
00581         else if (html_as == 3) // Sanitize
00582           // Strip all but allowed HTML
00583           clazz = (MimeObjectClass *)&mimeInlineTextHTMLSanitizedClass;
00584         else // Goofy pref
00585           /* User has an unknown pref value. Maybe he used a newer Mozilla
00586              with a new alternative to avoid HTML. Defaulting to option 1,
00587              which is less dangerous than defaulting to the raw HTML. */
00588           clazz = (MimeObjectClass *)&mimeInlineTextHTMLAsPlaintextClass;
00589       }
00590       else if (!nsCRT::strcasecmp(content_type+5,              "enriched"))
00591         clazz = (MimeObjectClass *)&mimeInlineTextEnrichedClass;
00592       else if (!nsCRT::strcasecmp(content_type+5,              "richtext"))
00593         clazz = (MimeObjectClass *)&mimeInlineTextRichtextClass;
00594       else if (!nsCRT::strcasecmp(content_type+5,              "rtf"))
00595         clazz = (MimeObjectClass *)&mimeExternalObjectClass;
00596       else if (!nsCRT::strcasecmp(content_type+5,              "plain"))
00597       {
00598         // Preliminary use the normal plain text
00599         clazz = (MimeObjectClass *)&mimeInlineTextPlainClass;
00600         
00601         if (opts && opts->format_out != nsMimeOutput::nsMimeMessageFilterSniffer
00602           && opts->format_out != nsMimeOutput::nsMimeMessageAttach)
00603         {
00604           PRBool disable_format_flowed = PR_FALSE;
00605           if (prefBranch)
00606             prefBranch->GetBoolPref("mailnews.display.disable_format_flowed_support",
00607                                     &disable_format_flowed);
00608 
00609           if(!disable_format_flowed)
00610           {
00611             // Check for format=flowed, damn, it is already stripped away from
00612             // the contenttype!
00613             // Look in headers instead even though it's expensive and clumsy
00614             // First find Content-Type:
00615             char *content_type_row =
00616               (hdrs
00617                ? MimeHeaders_get(hdrs, HEADER_CONTENT_TYPE,
00618                                  PR_FALSE, PR_FALSE)
00619                : 0);
00620             // Then the format parameter if there is one.
00621             // I would rather use a PARAM_FORMAT but I can't find the right
00622             // place to put the define. The others seems to be in net.h
00623             // but is that really really the right place? There is also
00624             // a nsMimeTypes.h but that one isn't included. Bug?
00625             char *content_type_format =
00626               (content_type_row
00627                ? MimeHeaders_get_parameter(content_type_row, "format", NULL,NULL)
00628                : 0);
00629 
00630             if (content_type_format && !nsCRT::strcasecmp(content_type_format,
00631                                                           "flowed"))
00632               clazz = (MimeObjectClass *)&mimeInlineTextPlainFlowedClass;
00633             PR_FREEIF(content_type_format);
00634             PR_FREEIF(content_type_row);
00635           }
00636         }
00637       }
00638       else if (!exact_match_p)
00639         clazz = (MimeObjectClass *)&mimeInlineTextPlainClass;
00640     }
00641     
00642     /* Subtypes of multipart...
00643     */
00644     else if (!nsCRT::strncasecmp(content_type,                 "multipart/", 10))
00645     {
00646       if      (!nsCRT::strcasecmp(content_type+10,      "alternative"))
00647         clazz = (MimeObjectClass *)&mimeMultipartAlternativeClass;
00648       else if (!nsCRT::strcasecmp(content_type+10,      "related"))
00649         clazz = (MimeObjectClass *)&mimeMultipartRelatedClass;
00650       else if (!nsCRT::strcasecmp(content_type+10,      "digest"))
00651         clazz = (MimeObjectClass *)&mimeMultipartDigestClass;
00652       else if (!nsCRT::strcasecmp(content_type+10,      "appledouble") ||
00653                !nsCRT::strcasecmp(content_type+10,      "header-set"))
00654         clazz = (MimeObjectClass *)&mimeMultipartAppleDoubleClass;
00655       else if (!nsCRT::strcasecmp(content_type+10,      "parallel"))
00656         clazz = (MimeObjectClass *)&mimeMultipartParallelClass;
00657       else if (!nsCRT::strcasecmp(content_type+10,      "mixed"))
00658         clazz = (MimeObjectClass *)&mimeMultipartMixedClass;
00659 #ifdef ENABLE_SMIME      
00660       else if (!nsCRT::strcasecmp(content_type+10,      "signed"))
00661       {
00662       /* Check that the "protocol" and "micalg" parameters are ones we
00663         know about. */
00664         char *ct = (hdrs
00665           ? MimeHeaders_get(hdrs, HEADER_CONTENT_TYPE,
00666                             PR_FALSE, PR_FALSE)
00667                     : 0);
00668         char *proto = (ct
00669           ? MimeHeaders_get_parameter(ct, PARAM_PROTOCOL, NULL, NULL)
00670           : 0);
00671         char *micalg = (ct
00672           ? MimeHeaders_get_parameter(ct, PARAM_MICALG, NULL, NULL)
00673           : 0);
00674 
00675           if (proto
00676               && (
00677                   (/* is a signature */
00678                    !nsCRT::strcasecmp(proto, APPLICATION_XPKCS7_SIGNATURE)
00679                    ||
00680                    !nsCRT::strcasecmp(proto, APPLICATION_PKCS7_SIGNATURE))
00681                   && micalg
00682                   && (!nsCRT::strcasecmp(micalg, PARAM_MICALG_MD5) ||
00683                       !nsCRT::strcasecmp(micalg, PARAM_MICALG_MD5_2) ||
00684                       !nsCRT::strcasecmp(micalg, PARAM_MICALG_SHA1) ||
00685                       !nsCRT::strcasecmp(micalg, PARAM_MICALG_SHA1_2) ||
00686                       !nsCRT::strcasecmp(micalg, PARAM_MICALG_SHA1_3) ||
00687                       !nsCRT::strcasecmp(micalg, PARAM_MICALG_SHA1_4) ||
00688                       !nsCRT::strcasecmp(micalg, PARAM_MICALG_SHA1_5) ||
00689                       !nsCRT::strcasecmp(micalg, PARAM_MICALG_MD2))))
00690             clazz = (MimeObjectClass *)&mimeMultipartSignedCMSClass;
00691           else
00692             clazz = 0;          
00693         PR_FREEIF(proto);
00694         PR_FREEIF(micalg);
00695         PR_FREEIF(ct);
00696       } 
00697 #endif
00698       
00699       if (!clazz && !exact_match_p)
00700         /* Treat all unknown multipart subtypes as "multipart/mixed" */
00701         clazz = (MimeObjectClass *)&mimeMultipartMixedClass;
00702 
00703       /* If we are sniffing a message, let's treat alternative parts as mixed */
00704       if (opts && opts->format_out == nsMimeOutput::nsMimeMessageFilterSniffer)
00705         if (clazz == (MimeObjectClass *)&mimeMultipartAlternativeClass)
00706           clazz = (MimeObjectClass *)&mimeMultipartMixedClass;
00707     }
00708     
00709     /* Subtypes of message...
00710     */
00711     else if (!nsCRT::strncasecmp(content_type,                 "message/", 8))
00712     {
00713       if      (!nsCRT::strcasecmp(content_type+8,              "rfc822") ||
00714         !nsCRT::strcasecmp(content_type+8,              "news"))
00715         clazz = (MimeObjectClass *)&mimeMessageClass;
00716       else if (!nsCRT::strcasecmp(content_type+8,              "external-body"))
00717         clazz = (MimeObjectClass *)&mimeExternalBodyClass;
00718       else if (!nsCRT::strcasecmp(content_type+8,              "partial"))
00719         /* I guess these are most useful as externals, for now... */
00720         clazz = (MimeObjectClass *)&mimeExternalObjectClass;
00721       else if (!exact_match_p)
00722         /* Treat all unknown message subtypes as "text/plain" */
00723         clazz = (MimeObjectClass *)&mimeInlineTextPlainClass;
00724     }
00725     
00726     /* The magic image types which we are able to display internally...
00727     */
00728     else if (!nsCRT::strncasecmp(content_type,          "image/", 6)) {
00729         nsCOMPtr<imgILoader> loader(do_GetService("@mozilla.org/image/loader;1"));
00730         PRBool isReg = PR_FALSE;
00731         loader->SupportImageWithMimeType(content_type, &isReg);
00732         if (isReg)
00733           clazz = (MimeObjectClass *)&mimeInlineImageClass;
00734         else
00735           clazz = (MimeObjectClass *)&mimeExternalObjectClass;
00736     }
00737     
00738 #ifdef ENABLE_SMIME
00739     else if (!nsCRT::strcasecmp(content_type, APPLICATION_XPKCS7_MIME)
00740              || !nsCRT::strcasecmp(content_type, APPLICATION_PKCS7_MIME))
00741                clazz = (MimeObjectClass *)&mimeEncryptedCMSClass;
00742 #endif
00743     /* A few types which occur in the real world and which we would otherwise
00744     treat as non-text types (which would be bad) without this special-case...
00745     */
00746     else if (!nsCRT::strcasecmp(content_type,                  APPLICATION_PGP) ||
00747              !nsCRT::strcasecmp(content_type,                  APPLICATION_PGP2))
00748       clazz = (MimeObjectClass *)&mimeInlineTextPlainClass;
00749 
00750     else if (!nsCRT::strcasecmp(content_type,                  SUN_ATTACHMENT))
00751       clazz = (MimeObjectClass *)&mimeSunAttachmentClass;
00752     
00753     /* Everything else gets represented as a clickable link.
00754     */
00755     else if (!exact_match_p)
00756       clazz = (MimeObjectClass *)&mimeExternalObjectClass;
00757 
00758     if (!mime_is_allowed_class(clazz, types_of_classes_to_disallow))
00759     {
00760       /* Do that check here (not after the if block), because we want to allow
00761          user-installed plugins. */
00762       if(!exact_match_p)
00763         clazz = (MimeObjectClass *)&mimeExternalObjectClass;
00764       else
00765         clazz = 0;
00766     }
00767   }
00768  
00769 #ifdef ENABLE_SMIME
00770   // see bug #189988
00771   if (opts && opts->format_out == nsMimeOutput::nsMimeMessageDecrypt && 
00772        (clazz != (MimeObjectClass *)&mimeEncryptedCMSClass)) {
00773     clazz = (MimeObjectClass *)&mimeExternalObjectClass;
00774   }
00775 #endif
00776 
00777   if (!exact_match_p)
00778     NS_ASSERTION(clazz, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
00779   if (!clazz) return 0;
00780 
00781   NS_ASSERTION(clazz, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
00782 
00783   if (clazz && !clazz->class_initialized)
00784        {
00785          int status = mime_classinit(clazz);
00786          if (status < 0) return 0;
00787        }
00788 
00789   return clazz;
00790 }
00791 
00792 
00793 MimeObject *
00794 mime_create (const char *content_type, MimeHeaders *hdrs,
00795                       MimeDisplayOptions *opts)
00796 {
00797   /* If there is no Content-Disposition header, or if the Content-Disposition
00798         is ``inline'', then we display the part inline (and let mime_find_class()
00799         decide how.)
00800 
00801         If there is any other Content-Disposition (either ``attachment'' or some
00802         disposition that we don't recognise) then we always display the part as
00803         an external link, by using MimeExternalObject to display it.
00804 
00805         But Content-Disposition is ignored for all containers except `message'.
00806         (including multipart/mixed, and multipart/digest.)  It's not clear if
00807         this is to spec, but from a usability standpoint, I think it's necessary.
00808    */
00809 
00810   MimeObjectClass *clazz = 0;
00811   char *content_disposition = 0;
00812   MimeObject *obj = 0;
00813   char *override_content_type = 0;
00814 
00815 
00816   /* There are some clients send out all attachments with a content-type
00817         of application/octet-stream.  So, if we have an octet-stream attachment,
00818         try to guess what type it really is based on the file extension.  I HATE
00819         that we have to do this...
00820   */
00821   if (hdrs && opts && opts->file_type_fn &&
00822 
00823          /* ### mwelch - don't override AppleSingle */
00824          (content_type ? nsCRT::strcasecmp(content_type, APPLICATION_APPLEFILE) : PR_TRUE) &&
00825          /* ## davidm Apple double shouldn't use this #$%& either. */
00826          (content_type ? nsCRT::strcasecmp(content_type, MULTIPART_APPLEDOUBLE) : PR_TRUE) &&
00827          (!content_type ||
00828           !nsCRT::strcasecmp(content_type, APPLICATION_OCTET_STREAM) ||
00829           !nsCRT::strcasecmp(content_type, UNKNOWN_CONTENT_TYPE)))
00830        {
00831          char *name = MimeHeaders_get_name(hdrs, opts);
00832          if (name)
00833               {
00834                 override_content_type = opts->file_type_fn (name, opts->stream_closure);
00835                 PR_FREEIF(name);
00836 
00837       // Of, if we got here and it is not the unknown content type from the 
00838       // file name, lets do some better checking not to inline something bad
00839       //                      
00840       if (override_content_type && (nsCRT::strcasecmp(override_content_type, UNKNOWN_CONTENT_TYPE)))
00841       {
00842         // Only inline this if it makes sense to do so!
00843         if ( (!content_type) ||
00844              (content_type && (!nsCRT::strcasecmp(content_type, UNKNOWN_CONTENT_TYPE))) )
00845         {
00846                      content_type = override_content_type;
00847         }
00848         else
00849           PR_FREEIF(override_content_type);
00850       }
00851               }
00852        }
00853 
00854   clazz = mime_find_class(content_type, hdrs, opts, PR_FALSE);
00855 
00856   NS_ASSERTION(clazz, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
00857   if (!clazz) goto FAIL;
00858 
00859   if (opts && opts->part_to_load)
00860        /* Always ignore Content-Disposition when we're loading some specific
00861           sub-part (which may be within some container that we wouldn't otherwise
00862           descend into, if the container itself had a Content-Disposition of
00863           `attachment'. */
00864        content_disposition = 0;
00865 
00866   else if (mime_subclass_p(clazz,(MimeObjectClass *)&mimeContainerClass) &&
00867                  !mime_subclass_p(clazz,(MimeObjectClass *)&mimeMessageClass))
00868        /* Ignore Content-Disposition on all containers except `message'.
00869           That is, Content-Disposition is ignored for multipart/mixed objects,
00870           but is obeyed for message/rfc822 objects. */
00871        content_disposition = 0;
00872 
00873   else
00874   {
00875     /* Check to see if the plugin should override the content disposition
00876        to make it appear inline. One example is a vcard which has a content
00877        disposition of an "attachment;" */
00878     if (force_inline_display(content_type))
00879               NS_MsgSACopy(&content_disposition, "inline");
00880     else
00881               content_disposition = (hdrs
00882                                                     ? MimeHeaders_get(hdrs, HEADER_CONTENT_DISPOSITION, PR_TRUE, PR_FALSE)
00883                                                     : 0);
00884   }
00885 
00886   if (!content_disposition || !nsCRT::strcasecmp(content_disposition, "inline"))
00887     ;  /* Use the class we've got. */
00888   else
00889   { 
00890     // 
00891     // rhp: Ok, this is a modification to try to deal with messages
00892     //      that have content disposition set to "attachment" even though
00893     //      we probably should show them inline. 
00894     //
00895     if (  (clazz != (MimeObjectClass *)&mimeInlineTextHTMLClass) &&
00896           (clazz != (MimeObjectClass *)&mimeInlineTextClass) &&
00897           (clazz != (MimeObjectClass *)&mimeInlineTextPlainClass) &&
00898           (clazz != (MimeObjectClass *)&mimeInlineTextPlainFlowedClass) &&
00899           (clazz != (MimeObjectClass *)&mimeInlineTextHTMLClass) &&
00900           (clazz != (MimeObjectClass *)&mimeInlineTextHTMLSanitizedClass) &&
00901           (clazz != (MimeObjectClass *)&mimeInlineTextHTMLAsPlaintextClass) &&
00902           (clazz != (MimeObjectClass *)&mimeInlineTextRichtextClass) &&
00903           (clazz != (MimeObjectClass *)&mimeInlineTextEnrichedClass) &&
00904           (clazz != (MimeObjectClass *)&mimeMessageClass) &&
00905           (clazz != (MimeObjectClass *)&mimeInlineImageClass) )
00906       clazz = (MimeObjectClass *)&mimeExternalObjectClass;
00907   }
00908 
00909   /* If the option `Show Attachments Inline' is off, now would be the time to change our mind... */
00910   if (opts && !opts->show_attachment_inline_p)
00911   {
00912     if (mime_subclass_p(clazz, (MimeObjectClass *)&mimeInlineTextClass))
00913     {
00914       /* It's a text type.  Write it only if it's the *first* part
00915          that we're writing, and then only if it has no "filename"
00916          specified (the assumption here being, if it has a filename,
00917          it wasn't simply typed into the text field -- it was actually
00918          an attached document.) */
00919       if (opts->state && opts->state->first_part_written_p)
00920         clazz = (MimeObjectClass *)&mimeExternalObjectClass;
00921       else
00922       {
00923         /* If there's a name, then write this as an attachment. */
00924         char *name = (hdrs ? MimeHeaders_get_name(hdrs, opts) : nsnull);
00925         if (name)
00926         {
00927           clazz = (MimeObjectClass *)&mimeExternalObjectClass;
00928           PR_Free(name);
00929         }
00930       }
00931     }
00932     else
00933       if (mime_subclass_p(clazz,(MimeObjectClass *)&mimeContainerClass) &&
00934            !mime_subclass_p(clazz,(MimeObjectClass *)&mimeMessageClass))
00935         /* Multipart subtypes are ok, except for messages; descend into
00936            multiparts, and defer judgement.
00937  
00938            Encrypted blobs are just like other containers (make the crypto
00939            layer invisible, and treat them as simple containers.  So there's
00940            no easy way to save encrypted data directly to disk; it will tend
00941            to always be wrapped inside a message/rfc822.  That's ok.) */
00942           ;
00943         else if (opts && opts->part_to_load &&
00944                   mime_subclass_p(clazz,(MimeObjectClass *)&mimeMessageClass))
00945           /* Descend into messages only if we're looking for a specific sub-part. */
00946             ;
00947           else
00948           {
00949             /* Anything else, and display it as a link (and cause subsequent
00950                text parts to also be displayed as links.) */
00951             clazz = (MimeObjectClass *)&mimeExternalObjectClass;
00952           }
00953   }
00954 
00955   PR_FREEIF(content_disposition);
00956   obj = mime_new (clazz, hdrs, content_type);
00957 
00958  FAIL:
00959 
00960   /* If we decided to ignore the content-type in the headers of this object
00961         (see above) then make sure that our new content-type is stored in the
00962         object itself.  (Or free it, if we're in an out-of-memory situation.)
00963    */
00964   if (override_content_type)
00965        {
00966          if (obj)
00967               {
00968                 PR_FREEIF(obj->content_type);
00969                 obj->content_type = override_content_type;
00970               }
00971          else
00972               {
00973                 PR_Free(override_content_type);
00974               }
00975        }
00976 
00977   return obj;
00978 }
00979 
00980 
00981 
00982 static int mime_classinit_1(MimeObjectClass *clazz, MimeObjectClass *target);
00983 
00984 static int
00985 mime_classinit(MimeObjectClass *clazz)
00986 {
00987   int status;
00988   if (clazz->class_initialized)
00989        return 0;
00990 
00991   NS_ASSERTION(clazz->class_initialize, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
00992   if (!clazz->class_initialize)
00993        return -1;
00994 
00995   /* First initialize the superclass.
00996    */
00997   if (clazz->superclass && !clazz->superclass->class_initialized)
00998        {
00999          status = mime_classinit(clazz->superclass);
01000          if (status < 0) return status;
01001        }
01002 
01003   /* Now run each of the superclass-init procedures in turn,
01004         parentmost-first. */
01005   status = mime_classinit_1(clazz, clazz);
01006   if (status < 0) return status;
01007 
01008   /* Now we're done. */
01009   clazz->class_initialized = PR_TRUE;
01010   return 0;
01011 }
01012 
01013 static int
01014 mime_classinit_1(MimeObjectClass *clazz, MimeObjectClass *target)
01015 {
01016   int status;
01017   if (clazz->superclass)
01018        {
01019          status = mime_classinit_1(clazz->superclass, target);
01020          if (status < 0) return status;
01021        }
01022   return clazz->class_initialize(target);
01023 }
01024 
01025 
01026 PRBool
01027 mime_subclass_p(MimeObjectClass *child, MimeObjectClass *parent)
01028 {
01029   if (child == parent)
01030        return PR_TRUE;
01031   else if (!child->superclass)
01032        return PR_FALSE;
01033   else
01034        return mime_subclass_p(child->superclass, parent);
01035 }
01036 
01037 PRBool
01038 mime_typep(MimeObject *obj, MimeObjectClass *clazz)
01039 {
01040   return mime_subclass_p(obj->clazz, clazz);
01041 }
01042 
01043 
01044 
01045 /* URL munging
01046  */
01047 
01048 
01049 /* Returns a string describing the location of the part (like "2.5.3").
01050    This is not a full URL, just a part-number.
01051  */
01052 char *
01053 mime_part_address(MimeObject *obj)
01054 {
01055   if (!obj->parent)
01056        return nsCRT::strdup("0");
01057   else
01058        {
01059          /* Find this object in its parent. */
01060          PRInt32 i, j = -1;
01061          char buf [20];
01062          char *higher = 0;
01063          MimeContainer *cont = (MimeContainer *) obj->parent;
01064          NS_ASSERTION(mime_typep(obj->parent,
01065                     (MimeObjectClass *)&mimeContainerClass), "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
01066          for (i = 0; i < cont->nchildren; i++)
01067               if (cont->children[i] == obj)
01068                 {
01069                      j = i+1;
01070                      break;
01071                 }
01072          if (j == -1)
01073               {
01074                 NS_ASSERTION(0, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
01075                 return 0;
01076               }
01077 
01078          PR_snprintf(buf, sizeof(buf), "%ld", j);
01079          if (obj->parent->parent)
01080               {
01081                 higher = mime_part_address(obj->parent);
01082                 if (!higher) return 0;  /* MIME_OUT_OF_MEMORY */
01083               }
01084 
01085          if (!higher)
01086               return nsCRT::strdup(buf);
01087          else
01088               {
01089                 PRUint32 slen = strlen(higher) + strlen(buf) + 3;
01090                 char *s = (char *)PR_MALLOC(slen);
01091                 if (!s)
01092                      {
01093                        PR_Free(higher);
01094                        return 0;  /* MIME_OUT_OF_MEMORY */
01095                      }
01096                 PL_strncpyz(s, higher, slen);
01097                 PL_strcatn(s, slen, ".");
01098                 PL_strcatn(s, slen, buf);
01099                 PR_Free(higher);
01100                 return s;
01101               }
01102        }
01103 }
01104 
01105 
01106 /* Returns a string describing the location of the *IMAP* part (like "2.5.3").
01107    This is not a full URL, just a part-number.
01108    This part is explicitly passed in the X-Mozilla-IMAP-Part header.
01109    Return value must be freed by the caller.
01110  */
01111 char *
01112 mime_imap_part_address(MimeObject *obj)
01113 {
01114   if (!obj || !obj->headers)
01115     return 0;
01116   else
01117     return MimeHeaders_get(obj->headers, IMAP_EXTERNAL_CONTENT_HEADER, PR_FALSE, PR_FALSE);
01118 }
01119 
01120 /* Returns a full URL if the current mime object has a EXTERNAL_ATTACHMENT_URL_HEADER
01121    header. 
01122    Return value must be freed by the caller.
01123 */
01124 char * 
01125 mime_external_attachment_url(MimeObject *obj)
01126 {
01127   if (!obj || !obj->headers)
01128     return 0;
01129   else
01130     return MimeHeaders_get(obj->headers, EXTERNAL_ATTACHMENT_URL_HEADER, PR_FALSE, PR_FALSE);
01131 }
01132 
01133 #ifdef ENABLE_SMIME
01134 /* Asks whether the given object is one of the cryptographically signed
01135    or encrypted objects that we know about.  (MimeMessageClass uses this
01136    to decide if the headers need to be presented differently.)
01137  */
01138 PRBool
01139 mime_crypto_object_p(MimeHeaders *hdrs, PRBool clearsigned_counts)
01140 {
01141   char *ct;
01142   MimeObjectClass *clazz;
01143 
01144   if (!hdrs) return PR_FALSE;
01145 
01146   ct = MimeHeaders_get (hdrs, HEADER_CONTENT_TYPE, PR_TRUE, PR_FALSE);
01147   if (!ct) return PR_FALSE;
01148 
01149   /* Rough cut -- look at the string before doing a more complex comparison. */
01150   if (nsCRT::strcasecmp(ct, MULTIPART_SIGNED) &&
01151     nsCRT::strncasecmp(ct, "application/", 12))
01152        {
01153          PR_Free(ct);
01154          return PR_FALSE;
01155        }
01156 
01157   /* It's a candidate for being a crypto object.  Let's find out for sure... */
01158   clazz = mime_find_class (ct, hdrs, 0, PR_TRUE);
01159   PR_Free(ct);
01160 
01161   if (clazz == ((MimeObjectClass *)&mimeEncryptedCMSClass))
01162        return PR_TRUE;
01163   else if (clearsigned_counts &&
01164                  clazz == ((MimeObjectClass *)&mimeMultipartSignedCMSClass))
01165        return PR_TRUE;
01166   else
01167        return PR_FALSE;
01168 }
01169 
01170 /* Whether the given object has written out the HTML version of its headers
01171    in such a way that it will have a "crypto stamp" next to the headers.  If
01172    this is true, then the child must write out its HTML slightly differently
01173    to take this into account...
01174  */
01175 PRBool
01176 mime_crypto_stamped_p(MimeObject *obj)
01177 {
01178   if (!obj) return PR_FALSE;
01179   if (mime_typep (obj, (MimeObjectClass *) &mimeMessageClass))
01180        return ((MimeMessage *) obj)->crypto_stamped_p;
01181   else
01182        return PR_FALSE;
01183 }
01184 
01185 #endif // ENABLE_SMIME
01186 
01187 /* Puts a part-number into a URL.  If append_p is true, then the part number
01188    is appended to any existing part-number already in that URL; otherwise,
01189    it replaces it.
01190  */
01191 char *
01192 mime_set_url_part(const char *url, const char *part, PRBool append_p)
01193 {
01194   const char *part_begin = 0;
01195   const char *part_end = 0;
01196   PRBool got_q = PR_FALSE;
01197   const char *s;
01198   char *result;
01199 
01200   if (!url || !part) return 0;
01201 
01202   nsCAutoString urlString(url);
01203   PRInt32 typeIndex = urlString.Find("?type=application/x-message-display");
01204   if (typeIndex != kNotFound)
01205   {
01206     urlString.Cut(typeIndex, sizeof("?type=application/x-message-display") - 1);
01207     if (urlString.CharAt(typeIndex) == '&')
01208       urlString.SetCharAt('?', typeIndex);
01209     url = urlString.get();
01210   }
01211 
01212   for (s = url; *s; s++)
01213        {
01214          if (*s == '?')
01215               {
01216                 got_q = PR_TRUE;
01217                 if (!nsCRT::strncasecmp(s, "?part=", 6))
01218                      part_begin = (s += 6);
01219               }
01220          else if (got_q && *s == '&' && !nsCRT::strncasecmp(s, "&part=", 6))
01221               part_begin = (s += 6);
01222 
01223          if (part_begin)
01224               {
01225                 for (; (*s && *s != '?' && *s != '&'); s++)
01226                      ;
01227                 part_end = s;
01228                 break;
01229           }
01230        }
01231 
01232   PRUint32 resultlen = strlen(url) + strlen(part) + 10;
01233   result = (char *) PR_MALLOC(resultlen);
01234   if (!result) return 0;
01235 
01236   if (part_begin)
01237        {
01238          if (append_p)
01239               {
01240                 memcpy(result, url, part_end - url);
01241                 result [part_end - url]     = '.';
01242                 result [part_end - url + 1] = 0;
01243               }
01244          else
01245               {
01246                 memcpy(result, url, part_begin - url);
01247                 result [part_begin - url] = 0;
01248               }
01249        }
01250   else
01251        {
01252          PL_strncpyz(result, url, resultlen);
01253          if (got_q)
01254               PL_strcatn(result, resultlen, "&part=");
01255          else
01256               PL_strcatn(result, resultlen, "?part=");
01257        }
01258 
01259   PL_strcatn(result, resultlen, part);
01260 
01261   if (part_end && *part_end)
01262        PL_strcatn(result, resultlen, part_end);
01263 
01264   /* Semi-broken kludge to omit a trailing "?part=0". */
01265   {
01266        int L = strlen(result);
01267        if (L > 6 &&
01268               (result[L-7] == '?' || result[L-7] == '&') &&
01269               !nsCRT::strcmp("part=0", result + L - 6))
01270          result[L-7] = 0;
01271   }
01272 
01273   return result;
01274 }
01275 
01276 
01277 
01278 /* Puts an *IMAP* part-number into a URL.
01279    Strips off any previous *IMAP* part numbers, since they are absolute, not relative.
01280  */
01281 char *
01282 mime_set_url_imap_part(const char *url, const char *imappart, const char *libmimepart)
01283 {
01284   char *result = 0;
01285   char *whereCurrent = PL_strstr(url, "/;section=");
01286   if (whereCurrent)
01287   {
01288          *whereCurrent = 0;
01289   }
01290        
01291   PRUint32 resultLen = strlen(url) + strlen(imappart) + strlen(libmimepart) + 17;
01292   result = (char *) PR_MALLOC(resultLen);
01293   if (!result) return 0;
01294 
01295   PL_strncpyz(result, url, resultLen);
01296   PL_strcatn(result, resultLen, "/;section=");
01297   PL_strcatn(result, resultLen, imappart);
01298   PL_strcatn(result, resultLen, "?part=");
01299   PL_strcatn(result, resultLen, libmimepart);
01300 
01301   if (whereCurrent)
01302          *whereCurrent = '/';
01303 
01304   return result;
01305 }
01306 
01307 
01308 /* Given a part ID, looks through the MimeObject tree for a sub-part whose ID
01309    number matches, and returns the MimeObject (else NULL.)
01310    (part is not a URL -- it's of the form "1.3.5".)
01311  */
01312 MimeObject *
01313 mime_address_to_part(const char *part, MimeObject *obj)
01314 {
01315   /* Note: this is an N^2 operation, but the number of parts in a message
01316         shouldn't ever be large enough that this really matters... */
01317 
01318   PRBool match;
01319 
01320   if (!part || !*part)
01321        {
01322          match = !obj->parent;
01323        }
01324   else
01325        {
01326          char *part2 = mime_part_address(obj);
01327          if (!part2) return 0;  /* MIME_OUT_OF_MEMORY */
01328          match = !nsCRT::strcmp(part, part2);
01329          PR_Free(part2);
01330        }
01331 
01332   if (match)
01333        {
01334          /* These are the droids we're looking for. */
01335          return obj;
01336        }
01337   else if (!mime_typep(obj, (MimeObjectClass *) &mimeContainerClass))
01338        {
01339          /* Not a container, pull up, pull up! */
01340          return 0;
01341        }
01342   else
01343        {
01344          PRInt32 i;
01345          MimeContainer *cont = (MimeContainer *) obj;
01346          for (i = 0; i < cont->nchildren; i++)
01347               {
01348                 MimeObject *o2 = mime_address_to_part(part, cont->children[i]);
01349                 if (o2) return o2;
01350               }
01351          return 0;
01352        }
01353 }
01354 
01355 /* Given a part ID, looks through the MimeObject tree for a sub-part whose ID
01356    number matches; if one is found, returns the Content-Name of that part.
01357    Else returns NULL.  (part is not a URL -- it's of the form "1.3.5".)
01358  */
01359 char *
01360 mime_find_content_type_of_part(const char *part, MimeObject *obj)
01361 {
01362   char *result = 0;
01363 
01364   obj = mime_address_to_part(part, obj);
01365   if (!obj) return 0;
01366 
01367   result = (obj->headers ? MimeHeaders_get(obj->headers, HEADER_CONTENT_TYPE, PR_TRUE, PR_FALSE) : 0);
01368 
01369   return result;
01370 }
01371 
01372 /* Given a part ID, looks through the MimeObject tree for a sub-part whose ID
01373    number matches; if one is found, returns the Content-Name of that part.
01374    Else returns NULL.  (part is not a URL -- it's of the form "1.3.5".)
01375  */
01376 char *
01377 mime_find_suggested_name_of_part(const char *part, MimeObject *obj)
01378 {
01379   char *result = 0;
01380 
01381   obj = mime_address_to_part(part, obj);
01382   if (!obj) return 0;
01383 
01384   result = (obj->headers ? MimeHeaders_get_name(obj->headers, obj->options) : 0);
01385 
01386   /* If this part doesn't have a name, but this part is one fork of an
01387         AppleDouble, and the AppleDouble itself has a name, then use that. */
01388   if (!result &&
01389          obj->parent &&
01390          obj->parent->headers &&
01391          mime_typep(obj->parent,
01392                              (MimeObjectClass *) &mimeMultipartAppleDoubleClass))
01393        result = MimeHeaders_get_name(obj->parent->headers, obj->options);
01394 
01395   /* Else, if this part is itself an AppleDouble, and one of its children
01396         has a name, then use that (check data fork first, then resource.) */
01397   if (!result &&
01398          mime_typep(obj, (MimeObjectClass *) &mimeMultipartAppleDoubleClass))
01399        {
01400          MimeContainer *cont = (MimeContainer *) obj;
01401          if (cont->nchildren > 1 &&
01402                 cont->children[1] &&
01403                 cont->children[1]->headers)
01404               result = MimeHeaders_get_name(cont->children[1]->headers, obj->options);
01405 
01406          if (!result &&
01407                 cont->nchildren > 0 &&
01408                 cont->children[0] &&
01409                 cont->children[0]->headers)
01410               result = MimeHeaders_get_name(cont->children[0]->headers, obj->options);
01411        }
01412 
01413   /* Ok, now we have the suggested name, if any.
01414         Now we remove any extensions that correspond to the
01415         Content-Transfer-Encoding.  For example, if we see the headers
01416 
01417               Content-Type: text/plain
01418               Content-Disposition: inline; filename=foo.text.uue
01419               Content-Transfer-Encoding: x-uuencode
01420 
01421         then we would look up (in mime.types) the file extensions which are
01422         associated with the x-uuencode encoding, find that "uue" is one of
01423         them, and remove that from the end of the file name, thus returning
01424         "foo.text" as the name.  This is because, by the time this file ends
01425         up on disk, its content-transfer-encoding will have been removed;
01426         therefore, we should suggest a file name that indicates that.
01427    */
01428   if (result && obj->encoding && *obj->encoding)
01429        {
01430          PRInt32 L = strlen(result);
01431          const char **exts = 0;
01432 
01433          /*
01434                I'd like to ask the mime.types file, "what extensions correspond
01435                to obj->encoding (which happens to be "x-uuencode") but doing that
01436                in a non-sphagetti way would require brain surgery.  So, since
01437                currently uuencode is the only content-transfer-encoding which we
01438                understand which traditionally has an extension, we just special-
01439                case it here!  Icepicks in my forehead!
01440 
01441                Note that it's special-cased in a similar way in libmsg/compose.c.
01442           */
01443          if (!nsCRT::strcasecmp(obj->encoding, ENCODING_UUENCODE))
01444               {
01445                 static const char *uue_exts[] = { "uu", "uue", 0 };
01446                 exts = uue_exts;
01447               }
01448 
01449          while (exts && *exts)
01450               {
01451                 const char *ext = *exts;
01452                 PRInt32 L2 = strlen(ext);
01453                 if (L > L2 + 1 &&                                            /* long enough */
01454                        result[L - L2 - 1] == '.' &&                   /* '.' in right place*/
01455                        !nsCRT::strcasecmp(ext, result + (L - L2)))    /* ext matches */
01456                      {
01457                        result[L - L2 - 1] = 0;          /* truncate at '.' and stop. */
01458                        break;
01459                      }
01460                 exts++;
01461               }
01462        }
01463 
01464   return result;
01465 }
01466 
01467 /* Parse the various "?" options off the URL and into the options struct.
01468  */
01469 int
01470 mime_parse_url_options(const char *url, MimeDisplayOptions *options)
01471 {
01472   const char *q;
01473   MimeHeadersState default_headers = options->headers;
01474   
01475   if (!url || !*url) return 0;
01476   if (!options) return 0;
01477   
01478   q = PL_strrchr (url, '?');
01479   if (! q) return 0;
01480   q++;
01481   while (*q)
01482   {
01483     const char *end, *value, *name_end;
01484     for (end = q; *end && *end != '&'; end++)
01485       ;
01486     for (value = q; *value != '=' && value < end; value++)
01487       ;
01488     name_end = value;
01489     if (value < end) value++;
01490     if (name_end <= q)
01491       ;
01492     else if (!nsCRT::strncasecmp ("headers", q, name_end - q))
01493     {
01494       if (end > value && !nsCRT::strncasecmp ("only", value, end-value))
01495         options->headers = MimeHeadersOnly;
01496       else if (end > value && !nsCRT::strncasecmp ("none", value, end-value))
01497         options->headers = MimeHeadersNone;      
01498       else if (end > value && !nsCRT::strncasecmp ("all", value, end - value))
01499         options->headers = MimeHeadersAll;
01500       else if (end > value && !nsCRT::strncasecmp ("some", value, end - value))
01501         options->headers = MimeHeadersSome;
01502       else if (end > value && !nsCRT::strncasecmp ("micro", value, end - value))
01503         options->headers = MimeHeadersMicro;
01504       else if (end > value && !nsCRT::strncasecmp ("cite", value, end - value))
01505         options->headers = MimeHeadersCitation;
01506       else if (end > value && !nsCRT::strncasecmp ("citation", value, end-value))
01507         options->headers = MimeHeadersCitation;
01508       else
01509         options->headers = default_headers;
01510     }
01511     else if (!nsCRT::strncasecmp ("part", q, name_end - q) && 
01512       options->format_out != nsMimeOutput::nsMimeMessageBodyQuoting)
01513     {
01514       PR_FREEIF (options->part_to_load);
01515       if (end > value)
01516       {
01517         options->part_to_load = (char *) PR_MALLOC(end - value + 1);
01518         if (!options->part_to_load)
01519           return MIME_OUT_OF_MEMORY;
01520         memcpy(options->part_to_load, value, end-value);
01521         options->part_to_load[end-value] = 0;
01522       }
01523     }
01524     else if (!nsCRT::strncasecmp ("rot13", q, name_end - q))
01525     {
01526       options->rot13_p = end <= value || !nsCRT::strncasecmp ("true", value, end - value);
01527     }
01528     
01529     q = end;
01530     if (*q)
01531       q++;
01532   }
01533   
01534   
01535 /* Compatibility with the "?part=" syntax used in the old (Mozilla 2.0)
01536         MIME parser.
01537          
01538    Basically, the problem is that the old part-numbering code was totally
01539    busted: here's a comparison of the old and new numberings with a pair
01540    of hypothetical messages (one with a single part, and one with nested
01541    containers.)
01542    NEW:      OLD:  OR:
01543    message/rfc822
01544    image/jpeg           1         0     0
01545    
01546   message/rfc822
01547   multipart/mixed      1         0     0
01548   text/plain         1.1       1     1
01549   image/jpeg         1.2       2     2
01550   message/rfc822     1.3       -     3
01551   text/plain       1.3.1     3     -
01552   message/rfc822     1.4       -     4
01553   multipart/mixed  1.4.1     4     -
01554   text/plain     1.4.1.1   4.1   -
01555   image/jpeg     1.4.1.2   4.2   -
01556   text/plain         1.5       5     5
01557 
01558  The "NEW" column is how the current code counts.  The "OLD" column is
01559  what "?part=" references would do in 3.0b4 and earlier; you'll see that
01560  you couldn't directly refer to the child message/rfc822 objects at all!
01561  But that's when it got really weird, because if you turned on
01562  "Attachments As Links" (or used a URL like "?inline=false&part=...")
01563  then you got a totally different numbering system (seen in the "OR"
01564  column.)  Gag!
01565  
01566  So, the problem is, ClariNet had been using these part numbers in their
01567  HTML news feeds, as a sleazy way of transmitting both complex HTML layouts
01568  and images using NNTP as transport, without invoking HTTP.
01569    
01570  The following clause is to provide some small amount of backward
01571  compatibility.  By looking at that table, one can see that in the new
01572  model, "part=0" has no meaning, and neither does "part=2" or "part=3"
01573  and so on.
01574      
01575  "part=1" is ambiguous between the old and new way, as is any part
01576  specification that has a "." in it.
01577        
01578  So, the compatibility hack we do here is: if the part is "0", then map
01579  that to "1".  And if the part is >= "2", then prepend "1." to it (so that
01580  we map "2" to "1.2", and "3" to "1.3".)
01581  
01582  This leaves the URLs compatible in the cases of:
01583  
01584  = single part messages
01585  = references to elements of a top-level multipart except the first
01586    
01587  and leaves them incompatible for:
01588      
01589  = the first part of a top-level multipart
01590  = all elements deeper than the outermost part
01591  
01592  Life s#$%s when you don't properly think out things that end up turning
01593  into de-facto standards...
01594  */
01595 
01596  if (options->part_to_load &&
01597    !PL_strchr(options->part_to_load, '.'))              /* doesn't contain a dot */
01598  {
01599    if (!nsCRT::strcmp(options->part_to_load, "0"))             /* 0 */
01600    {
01601      PR_Free(options->part_to_load);
01602      options->part_to_load = nsCRT::strdup("1");
01603      if (!options->part_to_load)
01604        return MIME_OUT_OF_MEMORY;
01605    }
01606    else if (nsCRT::strcmp(options->part_to_load, "1"))  /* not 1 */
01607    {
01608      const char *prefix = "1.";
01609      PRUint32 slen = strlen(options->part_to_load) + strlen(prefix) + 1;
01610      char *s = (char *) PR_MALLOC(slen);
01611      if (!s) return MIME_OUT_OF_MEMORY;
01612      PL_strncpyz(s, prefix, slen);
01613      PL_strcatn(s, slen, options->part_to_load);
01614      PR_Free(options->part_to_load);
01615      options->part_to_load = s;
01616    }
01617  }
01618  
01619   return 0;
01620 }
01621 
01622 
01623 /* Some output-generation utility functions...
01624  */
01625 
01626 int
01627 MimeOptions_write(MimeDisplayOptions *opt, const char *data, PRInt32 length,
01628                               PRBool user_visible_p)
01629 {
01630   int status = 0;
01631   void* closure = 0;
01632   if (!opt || !opt->output_fn || !opt->state)
01633        return 0;
01634 
01635   closure = opt->output_closure;
01636   if (!closure) closure = opt->stream_closure;
01637 
01638 //  PR_ASSERT(opt->state->first_data_written_p);
01639 
01640   if (opt->state->separator_queued_p && user_visible_p)
01641        {
01642          opt->state->separator_queued_p = PR_FALSE;
01643          if (opt->state->separator_suppressed_p)
01644               opt->state->separator_suppressed_p = PR_FALSE;
01645          else
01646               {
01647                 char sep[] = "<BR><HR WIDTH=\"90%\" SIZE=4><BR>";
01648                 int lstatus = opt->output_fn(sep, strlen(sep), closure);
01649                 opt->state->separator_suppressed_p = PR_FALSE;
01650                 if (lstatus < 0) return lstatus;
01651               }
01652        }
01653   if (user_visible_p)
01654        opt->state->separator_suppressed_p = PR_FALSE;
01655 
01656   if (length > 0)
01657        {
01658          status = opt->output_fn(data, length, closure);
01659          if (status < 0) return status;
01660        }
01661 
01662   return 0;
01663 }
01664 
01665 int
01666 MimeObject_write(MimeObject *obj, const char *output, PRInt32 length,
01667                              PRBool user_visible_p)
01668 {
01669   if (!obj->output_p) return 0;
01670 
01671   // if we're stripping attachments, check if any parent is not being ouput
01672   if (obj->options->format_out == nsMimeOutput::nsMimeMessageAttach)
01673   {
01674     // if true, mime generates a separator in html - we don't want that.
01675     user_visible_p = PR_FALSE;
01676 
01677     for (MimeObject *parent = obj->parent; parent; parent = parent->parent)
01678     {
01679       if (!parent->output_p)
01680         return 0;
01681     }
01682   }
01683   if (!obj->options->state->first_data_written_p)
01684   {
01685     int status = MimeObject_output_init(obj, 0);
01686     if (status < 0) return status;
01687     NS_ASSERTION(obj->options->state->first_data_written_p, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
01688   }
01689 
01690   return MimeOptions_write(obj->options, output, length, user_visible_p);
01691 }
01692 
01693 int
01694 MimeObject_write_separator(MimeObject *obj)
01695 {
01696   if (obj->options && obj->options->state)
01697     obj->options->state->separator_queued_p = PR_TRUE;
01698   return 0;
01699 }
01700 
01701 int
01702 MimeObject_output_init(MimeObject *obj, const char *content_type)
01703 {
01704   if (obj &&
01705          obj->options &&
01706          obj->options->state &&
01707          !obj->options->state->first_data_written_p)
01708        {
01709          int status;
01710          const char *charset = 0;
01711          char *name = 0, *x_mac_type = 0, *x_mac_creator = 0;
01712 
01713          if (!obj->options->output_init_fn)
01714               {
01715                 obj->options->state->first_data_written_p = PR_TRUE;
01716                 return 0;
01717               }
01718 
01719          if (obj->headers)
01720               {
01721                 char *ct;
01722                 name = MimeHeaders_get_name(obj->headers, obj->options);
01723 
01724                 ct = MimeHeaders_get(obj->headers, HEADER_CONTENT_TYPE,
01725                                                     PR_FALSE, PR_FALSE);
01726                 if (ct)
01727                      {
01728                        x_mac_type   = MimeHeaders_get_parameter(ct, PARAM_X_MAC_TYPE, NULL, NULL);
01729                        x_mac_creator= MimeHeaders_get_parameter(ct, PARAM_X_MAC_CREATOR, NULL, NULL);
01730         /* if don't have a x_mac_type and x_mac_creator, we need to try to get it from its parent */
01731         if (!x_mac_type && !x_mac_creator && obj->parent && obj->parent->headers)
01732         {
01733           char * ctp = MimeHeaders_get(obj->parent->headers, HEADER_CONTENT_TYPE, PR_FALSE, PR_FALSE);
01734           if (ctp)
01735           {
01736             x_mac_type   = MimeHeaders_get_parameter(ctp, PARAM_X_MAC_TYPE, NULL, NULL);
01737             x_mac_creator= MimeHeaders_get_parameter(ctp, PARAM_X_MAC_CREATOR, NULL, NULL);
01738             PR_Free(ctp);
01739           }
01740         }
01741                        
01742         if (!(obj->options->override_charset)) {
01743           char *charset = MimeHeaders_get_parameter(ct, "charset", nsnull, nsnull);
01744           if (charset)
01745           {
01746             PR_FREEIF(obj->options->default_charset);
01747             obj->options->default_charset = charset;
01748           }
01749         }
01750                        PR_Free(ct);
01751                      }
01752               }
01753 
01754          if (mime_typep(obj, (MimeObjectClass *) &mimeInlineTextClass))
01755               charset = ((MimeInlineText *)obj)->charset;
01756 
01757          if (!content_type)
01758               content_type = obj->content_type;
01759          if (!content_type)
01760               content_type = TEXT_PLAIN;
01761 
01762     // 
01763     // Set the charset on the channel we are dealing with so people know 
01764     // what the charset is set to. Do this for quoting/Printing ONLY! 
01765     // 
01766     extern void   PR_CALLBACK ResetChannelCharset(MimeObject *obj);
01767     if ( (obj->options) && 
01768          ( (obj->options->format_out == nsMimeOutput::nsMimeMessageQuoting) || 
01769            (obj->options->format_out == nsMimeOutput::nsMimeMessageBodyQuoting) || 
01770            (obj->options->format_out == nsMimeOutput::nsMimeMessageSaveAs) || 
01771            (obj->options->format_out == nsMimeOutput::nsMimeMessagePrintOutput) ) ) 
01772       ResetChannelCharset(obj); 
01773 
01774          status = obj->options->output_init_fn (content_type, charset, name,
01775                                                                               x_mac_type, x_mac_creator,
01776                                                                               obj->options->stream_closure);
01777          PR_FREEIF(name);
01778          PR_FREEIF(x_mac_type);
01779          PR_FREEIF(x_mac_creator);
01780          obj->options->state->first_data_written_p = PR_TRUE;
01781          return status;
01782        }
01783   return 0;
01784 }
01785 
01786 char *
01787 mime_get_base_url(const char *url)
01788 {
01789   if (!url)
01790     return nsnull;
01791 
01792   const char *s = strrchr(url, '?');
01793   if (s && !strncmp(s, "?type=application/x-message-display", sizeof("?type=application/x-message-display") - 1))
01794   {
01795     const char *nextTerm = strchr(s, '&');
01796     s = (nextTerm) ? nextTerm : s + strlen(s) - 1;
01797   }
01798   // we need to keep the ?number part of the url, or we won't know
01799   // which local message the part belongs to. 
01800   if (s && *s && *(s+1) && !strncmp(s + 1, "number=", sizeof("number=") - 1))
01801   {
01802     const char *nextTerm = strchr(++s, '&');
01803     s = (nextTerm) ? nextTerm : s + strlen(s) - 1;
01804   }
01805   char *result = (char *) PR_MALLOC(strlen(url) + 1);
01806   NS_ASSERTION(result, "out of memory");
01807   if (!result)
01808     return nsnull;
01809 
01810   memcpy(result, url, s - url);
01811   result[s - url] = 0;
01812   return result;
01813 }