Back to index

lightning-sunbird  0.9+nobinonly
mimemsg.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  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nsCOMPtr.h"
00039 #include "nsIMimeEmitter.h"
00040 #include "mimemsg.h"
00041 #include "mimemoz2.h"
00042 #include "prmem.h"
00043 #include "prio.h"
00044 #include "plstr.h"
00045 #include "msgCore.h"
00046 #include "prlog.h"
00047 #include "prprf.h"
00048 #include "nsCRT.h"
00049 #include "nsMimeStringResources.h"
00050 #include "nsMimeTypes.h"
00051 #include "nsMsgMessageFlags.h"
00052 #include "nsEscape.h"
00053 #include "nsString.h"
00054 #include "mimetext.h"
00055 #include "mimecryp.h"
00056 #include "mimetpfl.h"
00057 
00058 #define MIME_SUPERCLASS mimeContainerClass
00059 MimeDefClass(MimeMessage, MimeMessageClass, mimeMessageClass,
00060                       &MIME_SUPERCLASS);
00061 
00062 static int MimeMessage_initialize (MimeObject *);
00063 static void MimeMessage_finalize (MimeObject *);
00064 static int MimeMessage_add_child (MimeObject *, MimeObject *);
00065 static int MimeMessage_parse_begin (MimeObject *);
00066 static int MimeMessage_parse_line (char *, PRInt32, MimeObject *);
00067 static int MimeMessage_parse_eof (MimeObject *, PRBool);
00068 static int MimeMessage_close_headers (MimeObject *obj);
00069 static int MimeMessage_write_headers_html (MimeObject *);
00070 static char *MimeMessage_partial_message_html(const char *data,
00071                                                                                void *closure,
00072                                                                                MimeHeaders *headers);
00073 
00074 #ifdef MOZ_SECURITY
00075 HG56268
00076 #endif /* MOZ_SECURITY */
00077 
00078 #ifdef XP_UNIX
00079 extern void MimeHeaders_do_unix_display_hook_hack(MimeHeaders *);
00080 #endif /* XP_UNIX */
00081 
00082 #if defined(DEBUG) && defined(XP_UNIX)
00083 static int MimeMessage_debug_print (MimeObject *, PRFileDesc *, PRInt32 depth);
00084 #endif
00085 
00086 extern MimeObjectClass mimeMultipartClass;
00087 
00088 static int
00089 MimeMessageClassInitialize(MimeMessageClass *clazz)
00090 {
00091   MimeObjectClass    *oclass = (MimeObjectClass *)    clazz;
00092   MimeContainerClass *cclass = (MimeContainerClass *) clazz;
00093 
00094   PR_ASSERT(!oclass->class_initialized);
00095   oclass->initialize  = MimeMessage_initialize;
00096   oclass->finalize    = MimeMessage_finalize;
00097   oclass->parse_begin = MimeMessage_parse_begin;
00098   oclass->parse_line  = MimeMessage_parse_line;
00099   oclass->parse_eof   = MimeMessage_parse_eof;
00100   cclass->add_child   = MimeMessage_add_child;
00101 
00102 #if defined(DEBUG) && defined(XP_UNIX)
00103   oclass->debug_print = MimeMessage_debug_print;
00104 #endif
00105   return 0;
00106 }
00107 
00108 
00109 static int
00110 MimeMessage_initialize (MimeObject *object)
00111 {
00112   MimeMessage *msg = (MimeMessage *)object;
00113   msg->grabSubject = PR_FALSE;
00114   msg->bodyLength = 0;
00115 
00116   return ((MimeObjectClass*)&MIME_SUPERCLASS)->initialize(object);
00117 }
00118 
00119 static void
00120 MimeMessage_finalize (MimeObject *object)
00121 {
00122   MimeMessage *msg = (MimeMessage *)object;
00123   if (msg->hdrs)
00124        MimeHeaders_free(msg->hdrs);
00125   msg->hdrs = 0;
00126   ((MimeObjectClass*)&MIME_SUPERCLASS)->finalize(object);
00127 }
00128 
00129 static int
00130 MimeMessage_parse_begin (MimeObject *obj)
00131 {
00132   MimeMessage *msg = (MimeMessage *)obj;
00133 
00134   int status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_begin(obj);
00135   if (status < 0) return status;
00136 
00137   if (obj->parent)
00138   {
00139     msg->grabSubject = PR_TRUE;
00140   }
00141 
00142   /* Messages have separators before the headers, except for the outermost
00143         message. */
00144   return MimeObject_write_separator(obj);
00145 }
00146 
00147 
00148 static int
00149 MimeMessage_parse_line (char *aLine, PRInt32 aLength, MimeObject *obj)
00150 {
00151   char * line = aLine;
00152   PRInt32 length = aLength;
00153 
00154   MimeMessage *msg = (MimeMessage *) obj;
00155   int status = 0;
00156 
00157   PR_ASSERT(line && *line);
00158   if (!line || !*line) return -1;
00159 
00160 #ifdef MOZ_SECURITY
00161   HG11013
00162 #endif /* MOZ_SECURITY */
00163 
00164   if (msg->grabSubject)
00165   {
00166     if ( (!PL_strncasecmp(line, "Subject: ", 9)) && (obj->parent) )
00167     {
00168       if ( (obj->headers) && (!obj->headers->munged_subject) )
00169       {
00170         obj->headers->munged_subject = (char *) PL_strndup(line + 9, length - 9);
00171         char *tPtr = obj->headers->munged_subject;
00172         while (*tPtr)
00173         {
00174           if ( (*tPtr == nsCRT::CR) || (*tPtr == nsCRT::LF) )
00175           {
00176             *tPtr = '\0';
00177             break;
00178           }
00179           tPtr++;
00180         }
00181       }
00182     }
00183   }
00184 
00185   /* If we already have a child object, then we're done parsing headers,
00186         and all subsequent lines get passed to the inferior object without
00187         further processing by us.  (Our parent will stop feeding us lines
00188         when this MimeMessage part is out of data.)
00189    */
00190   if (msg->container.nchildren)
00191        {
00192          MimeObject *kid = msg->container.children[0];
00193          PRBool nl;
00194          PR_ASSERT(kid);
00195          if (!kid) return -1;
00196 
00197           msg->bodyLength += length;
00198 
00199          /* Don't allow MimeMessage objects to not end in a newline, since it
00200                would be inappropriate for any following part to appear on the same
00201                line as the last line of the message.
00202 
00203                #### This assumes that the only time the `parse_line' method is
00204                called with a line that doesn't end in a newline is when that line
00205                is the last line.
00206           */
00207          nl = (length > 0 && (line[length-1] == nsCRT::CR || line[length-1] == nsCRT::LF));
00208 
00209 #ifdef MIME_DRAFTS
00210          if ( !mime_typep (kid, (MimeObjectClass*) &mimeMessageClass) &&
00211                  obj->options &&
00212                  obj->options->decompose_file_p &&
00213                  ! obj->options->is_multipart_msg &&
00214                  obj->options->decompose_file_output_fn )
00215               {
00216                 if (!obj->options->decrypt_p) {
00217         //if we are processing a flowed plain text line, we need to remove any stuffed space
00218         if (length > 0 && ' ' == *line && mime_typep(kid, (MimeObjectClass *)&mimeInlineTextPlainFlowedClass))
00219         {
00220           line ++;
00221           length --;
00222         }
00223                        status = obj->options->decompose_file_output_fn (line,
00224                                                                                                             length,
00225                                                                                             obj->options->stream_closure);
00226                        if (status < 0) return status;
00227                        if (!nl) {
00228                             status = obj->options->decompose_file_output_fn (MSG_LINEBREAK,
00229                                                                                                                  MSG_LINEBREAK_LEN,
00230                                                                                             obj->options->stream_closure);
00231                             if (status < 0) return status;
00232                        }
00233                        return status;
00234                 }
00235               }
00236 #endif /* MIME_DRAFTS */
00237 
00238 
00239          if (nl)
00240               return kid->clazz->parse_buffer (line, length, kid);
00241          else
00242               {
00243                 /* Hack a newline onto the end. */
00244                 char *s = (char *)PR_MALLOC(length + MSG_LINEBREAK_LEN + 1);
00245                 if (!s) return MIME_OUT_OF_MEMORY;
00246                 memcpy(s, line, length);
00247                 PL_strncpyz(s + length, MSG_LINEBREAK, MSG_LINEBREAK_LEN);
00248                 status = kid->clazz->parse_buffer (s, length + MSG_LINEBREAK_LEN, kid);
00249                 PR_Free(s);
00250                 return status;
00251               }
00252        }
00253 
00254   /* Otherwise we don't yet have a child object, which means we're not
00255         done parsing our headers yet.
00256    */
00257   if (!msg->hdrs)
00258        {
00259          msg->hdrs = MimeHeaders_new();
00260          if (!msg->hdrs) return MIME_OUT_OF_MEMORY;
00261        }
00262 
00263 #ifdef MIME_DRAFTS
00264   if ( obj->options &&
00265             obj->options->decompose_file_p &&
00266             ! obj->options->is_multipart_msg &&
00267             obj->options->done_parsing_outer_headers &&
00268             obj->options->decompose_file_output_fn ) 
00269   {
00270        status =  obj->options->decompose_file_output_fn( line, length,
00271                                                                                                              obj->options->stream_closure );
00272        if (status < 0) 
00273       return status;
00274   }
00275 #endif /* MIME_DRAFTS */
00276 
00277   status = MimeHeaders_parse_line(line, length, msg->hdrs);
00278   if (status < 0) return status;
00279 
00280   /* If this line is blank, we're now done parsing headers, and should
00281         examine our content-type to create our "body" part.
00282    */
00283   if (*line == nsCRT::CR || *line == nsCRT::LF)
00284        {
00285          status = MimeMessage_close_headers(obj);
00286          if (status < 0) return status;
00287        }
00288 
00289   return 0;
00290 }
00291 
00292 static int
00293 MimeMessage_close_headers (MimeObject *obj)
00294 {
00295   MimeMessage *msg = (MimeMessage *) obj;
00296   int status = 0;
00297   char *ct = 0;                    /* Content-Type header */
00298   MimeObject *body;
00299 
00300   if (msg->hdrs)
00301        {
00302          PRBool outer_p = !obj->headers; /* is this the outermost message? */
00303 
00304 
00305 #ifdef MIME_DRAFTS
00306          if (outer_p &&
00307                 obj->options &&
00308           (obj->options->decompose_file_p || obj->options->caller_need_root_headers) &&
00309                 obj->options->decompose_headers_info_fn)
00310               {
00311 #ifdef ENABLE_SMIME
00312       if (obj->options->decrypt_p && !mime_crypto_object_p (msg->hdrs, PR_FALSE))
00313         obj->options->decrypt_p = PR_FALSE;
00314 #endif /* ENABLE_SMIME */
00315       if (!obj->options->caller_need_root_headers || (obj == obj->options->state->root))
00316                      status = obj->options->decompose_headers_info_fn (
00317                                                                                      obj->options->stream_closure,
00318                                                                                                           msg->hdrs );
00319               }
00320 #endif /* MIME_DRAFTS */
00321 
00322 
00323          /* If this is the outermost message, we need to run the
00324                `generate_header' callback.  This happens here instead of
00325                in `parse_begin', because it's only now that we've parsed
00326                our headers.  However, since this is the outermost message,
00327                we have yet to write any HTML, so that's fine.
00328           */
00329          if (outer_p &&
00330                 obj->output_p &&
00331                 obj->options &&
00332                 obj->options->write_html_p &&
00333                 obj->options->generate_header_html_fn)
00334               {
00335                 int lstatus = 0;
00336                 char *html = 0;
00337 
00338                 /* The generate_header_html_fn might return HTML, so it's important
00339                       that the output stream be set up with the proper type before we
00340                       make the MimeObject_write() call below. */
00341                 if (!obj->options->state->first_data_written_p)
00342                      {
00343                        lstatus = MimeObject_output_init (obj, TEXT_HTML);
00344                        if (lstatus < 0) return lstatus;
00345                        PR_ASSERT(obj->options->state->first_data_written_p);
00346                      }
00347 
00348                 html = obj->options->generate_header_html_fn(NULL,
00349                                                                                        obj->options->html_closure,
00350                                                                                               msg->hdrs);
00351                 if (html)
00352                      {
00353                        lstatus = MimeObject_write(obj, html, strlen(html), PR_FALSE);
00354                        PR_Free(html);
00355                        if (lstatus < 0) return lstatus;
00356                      }
00357               }
00358 
00359 
00360          /* Find the content-type of the body of this message.
00361           */
00362          {
00363               PRBool ok = PR_TRUE;
00364               char *mv = MimeHeaders_get (msg->hdrs, HEADER_MIME_VERSION,
00365                                                                PR_TRUE, PR_FALSE);
00366 
00367 #ifdef REQUIRE_MIME_VERSION_HEADER
00368               /* If this is the outermost message, it must have a MIME-Version
00369                  header with the value 1.0 for us to believe what might be in
00370                  the Content-Type header.  If the MIME-Version header is not
00371                  present, we must treat this message as untyped.
00372                */
00373               ok = (mv && !nsCRT::strcmp(mv, "1.0"));
00374 #else
00375               /* #### actually, we didn't check this in Mozilla 2.0, and checking
00376                  it now could cause some compatibility nonsense, so for now, let's
00377                  just believe any Content-Type header we see.
00378                */
00379               ok = PR_TRUE;
00380 #endif
00381 
00382               if (ok)
00383                 {
00384                      ct = MimeHeaders_get (msg->hdrs, HEADER_CONTENT_TYPE, PR_TRUE, PR_FALSE);
00385 
00386                      /* If there is no Content-Type header, but there is a MIME-Version
00387                         header, then assume that this *is* in fact a MIME message.
00388                         (I've seen messages with
00389 
00390                               MIME-Version: 1.0
00391                               Content-Transfer-Encoding: quoted-printable
00392 
00393                         and no Content-Type, and we should treat those as being of type
00394                         MimeInlineTextPlain rather than MimeUntypedText.)
00395                       */
00396                      if (mv && !ct)
00397                        ct = nsCRT::strdup(TEXT_PLAIN);
00398                 }
00399 
00400               PR_FREEIF(mv);  /* done with this now. */
00401          }
00402 
00403     /* If this message has a body which is encrypted and we're going to
00404        decrypt it (whithout converting it to HTML, since decrypt_p and
00405        write_html_p are never true at the same time)
00406     */
00407     if (obj->output_p &&
00408         obj->options &&
00409         obj->options->decrypt_p
00410 #ifdef ENABLE_SMIME
00411         && !mime_crypto_object_p (msg->hdrs, PR_FALSE)
00412 #endif /* ENABLE_SMIME */
00413         )
00414     {
00415       /* The body of this message is not an encrypted object, so we need
00416          to turn off the decrypt_p flag (to prevent us from s#$%ing the
00417          body of the internal object up into one.) In this case,
00418          our output will end up being identical to our input.
00419       */
00420       obj->options->decrypt_p = PR_FALSE;
00421     }
00422 
00423          /* Emit the HTML for this message's headers.  Do this before
00424                creating the object representing the body.
00425           */
00426          if (obj->output_p &&
00427                 obj->options &&
00428                 obj->options->write_html_p)
00429               {
00430                 /* If citation headers are on, and this is not the outermost message,
00431                       turn them off. */
00432                 if (obj->options->headers == MimeHeadersCitation && !outer_p)
00433                      obj->options->headers = MimeHeadersSome;
00434 
00435                 /* Emit a normal header block. */
00436                 status = MimeMessage_write_headers_html(obj);
00437                 if (status < 0) return status;
00438               }
00439          else if (obj->output_p)
00440               {
00441                 /* Dump the headers, raw. */
00442                 status = MimeObject_write(obj, "", 0, PR_FALSE);  /* initialize */
00443                 if (status < 0) return status;
00444                 status = MimeHeaders_write_raw_headers(msg->hdrs, obj->options,
00445                                                                                      obj->options->decrypt_p);
00446                 if (status < 0) return status;
00447               }
00448 
00449 #ifdef XP_UNIX
00450          if (outer_p && obj->output_p)
00451               /* Kludge from mimehdrs.c */
00452               MimeHeaders_do_unix_display_hook_hack(msg->hdrs);
00453 #endif /* XP_UNIX */
00454        }
00455 
00456   /* Never put out a separator after a message header block. */
00457   if (obj->options && obj->options->state)
00458        obj->options->state->separator_suppressed_p = PR_TRUE;
00459 
00460 #ifdef MIME_DRAFTS
00461   if ( !obj->headers &&    /* outer most message header */
00462           obj->options && 
00463           obj->options->decompose_file_p && 
00464           ct )
00465        obj->options->is_multipart_msg = PL_strcasestr(ct, "multipart/") != NULL;
00466 #endif /* MIME_DRAFTS */
00467 
00468 
00469   body = mime_create(ct, msg->hdrs, obj->options);
00470 
00471   PR_FREEIF(ct);
00472   if (!body) return MIME_OUT_OF_MEMORY;
00473   status = ((MimeContainerClass *) obj->clazz)->add_child (obj, body);
00474   if (status < 0)
00475        {
00476          mime_free(body);
00477          return status;
00478        }
00479   
00480   // Only do this if this is a Text Object!
00481   if ( mime_typep(body, (MimeObjectClass *) &mimeInlineTextClass) )
00482   {
00483     ((MimeInlineText *) body)->needUpdateMsgWinCharset = PR_TRUE;
00484   }
00485 
00486   /* Now that we've added this new object to our list of children,
00487         start its parser going. */
00488   status = body->clazz->parse_begin(body);
00489   if (status < 0) return status;
00490 
00491   // Now notify the emitter if this is the outer most message, unless
00492   // it is a part that is not the head of the message. If it's a part, 
00493   // we need to figure out the content type/charset of the part
00494   //
00495   PRBool outer_p = !obj->headers;  /* is this the outermost message? */
00496 
00497   if ( outer_p &&
00498        (!obj->options->part_to_load || obj->options->format_out == nsMimeOutput::nsMimeMessageBodyDisplay))
00499   {
00500     // call SetMailCharacterSetToMsgWindow() to set a menu charset
00501     if (mime_typep(body, (MimeObjectClass *) &mimeInlineTextClass))
00502     {
00503       MimeInlineText  *text = (MimeInlineText *) body;
00504       if (text && text->charset && *text->charset)
00505         SetMailCharacterSetToMsgWindow(body, text->charset);
00506     }
00507 
00508     char  *msgID = MimeHeaders_get (msg->hdrs, HEADER_MESSAGE_ID,
00509                                                                                  PR_FALSE, PR_FALSE);
00510 
00511     char  *outCharset = NULL;
00512     if (!obj->options->force_user_charset)  /* Only convert if the user prefs is false */
00513       outCharset = "UTF-8";
00514 
00515     mimeEmitterStartBody(obj->options, (obj->options->headers == MimeHeadersNone), msgID, outCharset);
00516     PR_FREEIF(msgID);
00517 
00518        // setting up truncated message html fotter function
00519        char *xmoz = MimeHeaders_get(msg->hdrs, HEADER_X_MOZILLA_STATUS, PR_FALSE,
00520                                                          PR_FALSE);
00521        if (xmoz)
00522        {
00523               PRUint32 flags = 0;
00524               char dummy = 0;
00525               if (sscanf(xmoz, " %lx %c", &flags, &dummy) == 1 &&
00526                      flags & MSG_FLAG_PARTIAL)
00527               {
00528                      obj->options->html_closure = obj;
00529                      obj->options->generate_footer_html_fn =
00530                             MimeMessage_partial_message_html;
00531               }
00532               PR_FREEIF(xmoz);
00533        }
00534   }
00535 
00536   return 0;
00537 }
00538 
00539 
00540 
00541 static int 
00542 MimeMessage_parse_eof (MimeObject *obj, PRBool abort_p)
00543 {
00544   int status;
00545   PRBool outer_p;
00546   MimeMessage *msg = (MimeMessage *)obj;
00547   if (obj->closed_p) return 0;
00548   
00549   /* Run parent method first, to flush out any buffered data. */
00550   status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof(obj, abort_p);
00551   if (status < 0) return status;
00552 
00553   outer_p = !obj->headers;  /* is this the outermost message? */
00554 
00555   // Hack for messages with truncated headers (bug 244722)
00556   // If there is no empty line in a message, the parser can't figure out where
00557   // the headers end, causing parsing to hang. So we insert an extra newline
00558   // to keep it happy. This is OK, since a message without any empty lines is
00559   // broken anyway...
00560   if(outer_p && msg->hdrs && ! msg->hdrs->done_p) {
00561     MimeMessage_parse_line("\n", 1, obj);
00562   }
00563 
00564   // Once we get to the end of parsing the message, we will notify
00565   // the emitter that we are done the the body.
00566 
00567   // Mark the end of the mail body if we are actually emitting the
00568   // body of the message (i.e. not Header ONLY)
00569   if (outer_p && obj->options && obj->options->write_html_p)
00570   {
00571          if (obj->options->generate_footer_html_fn)
00572          {
00573                 mime_stream_data *msd = 
00574                        (mime_stream_data *) obj->options->stream_closure;
00575                 if (msd)
00576                 {
00577                        char *html = obj->options->generate_footer_html_fn
00578                               (msd->orig_url_name, obj->options->html_closure, msg->hdrs);
00579                        if (html)
00580                        {
00581                               int lstatus = MimeObject_write(obj, html,
00582                                                                                      strlen(html),
00583                                                                                      PR_FALSE);
00584                               PR_Free(html);
00585                               if (lstatus < 0) return lstatus;
00586                        }
00587                 }
00588          }
00589          if ((!obj->options->part_to_load  || obj->options->format_out == nsMimeOutput::nsMimeMessageBodyDisplay) && 
00590                 obj->options->headers != MimeHeadersOnly)
00591                 mimeEmitterEndBody(obj->options);
00592   }
00593 
00594 #ifdef MIME_DRAFTS
00595   if ( obj->options &&
00596           obj->options->decompose_file_p &&
00597           obj->options->done_parsing_outer_headers &&
00598           ! obj->options->is_multipart_msg &&
00599           ! mime_typep(obj, (MimeObjectClass*) &mimeEncryptedClass) &&
00600           obj->options->decompose_file_close_fn ) {
00601        status = obj->options->decompose_file_close_fn (
00602                                                                                 obj->options->stream_closure );
00603 
00604        if ( status < 0 ) return status;
00605   }
00606 #endif /* MIME_DRAFTS */
00607 
00608 
00609   /* Put out a separator after every message/rfc822 object. */
00610   if (!abort_p && !outer_p)
00611        {
00612          status = MimeObject_write_separator(obj);
00613          if (status < 0) return status;
00614        }
00615 
00616   return 0;
00617 }
00618 
00619 
00620 static int
00621 MimeMessage_add_child (MimeObject *parent, MimeObject *child)
00622 {
00623   MimeContainer *cont = (MimeContainer *) parent;
00624   PR_ASSERT(parent && child);
00625   if (!parent || !child) return -1;
00626 
00627   /* message/rfc822 containers can only have one child. */
00628   PR_ASSERT(cont->nchildren == 0);
00629   if (cont->nchildren != 0) return -1;
00630 
00631 #ifdef MIME_DRAFTS
00632   if ( parent->options &&
00633           parent->options->decompose_file_p &&
00634           ! parent->options->is_multipart_msg &&
00635           ! mime_typep(child, (MimeObjectClass*) &mimeEncryptedClass) &&
00636           parent->options->decompose_file_init_fn ) {
00637        int status = 0;
00638        status = parent->options->decompose_file_init_fn (
00639                                                                                parent->options->stream_closure,
00640                                                                                ((MimeMessage*)parent)->hdrs );
00641        if ( status < 0 ) return status;
00642   }
00643 #endif /* MIME_DRAFTS */
00644   
00645   return ((MimeContainerClass*)&MIME_SUPERCLASS)->add_child (parent, child);
00646 }
00647 
00648 // This is necessary to determine which charset to use for a reply/forward
00649 char *
00650 DetermineMailCharset(MimeMessage *msg)
00651 {
00652   char          *retCharset = nsnull;
00653   
00654   if ( (msg) && (msg->hdrs) )
00655   {
00656     char *ct = MimeHeaders_get (msg->hdrs, HEADER_CONTENT_TYPE,
00657                                 PR_FALSE, PR_FALSE);
00658     if (ct)
00659     {
00660       retCharset = MimeHeaders_get_parameter (ct, "charset", NULL, NULL);
00661       PR_Free(ct);
00662     }
00663     
00664     if (!retCharset)
00665     {
00666       // If we didn't find "Content-Type: ...; charset=XX" then look
00667       // for "X-Sun-Charset: XX" instead.  (Maybe this should be done
00668       // in MimeSunAttachmentClass, but it's harder there than here.)
00669       retCharset = MimeHeaders_get (msg->hdrs, HEADER_X_SUN_CHARSET,
00670                                     PR_FALSE, PR_FALSE);
00671     }    
00672   }
00673   
00674   if (!retCharset)
00675     return nsCRT::strdup("ISO-8859-1");
00676   else
00677     return retCharset;
00678 }
00679 
00680 static int
00681 MimeMessage_write_headers_html (MimeObject *obj)
00682 {
00683   MimeMessage     *msg = (MimeMessage *) obj;
00684   int             status;
00685 
00686 #ifdef MOZ_SECURITY
00687   HG33391
00688 #endif /* MOZ_SECURITY */  
00689 
00690   if (!obj->options || !obj->options->output_fn)
00691        return 0;
00692 
00693   PR_ASSERT(obj->output_p && obj->options->write_html_p);
00694 
00695   // To support the no header option! Make sure we are not
00696   // suppressing headers on included email messages...
00697   if ( (obj->options->headers == MimeHeadersNone) &&
00698        (obj == obj->options->state->root) )
00699   {
00700     // Ok, we are going to kick the Emitter for a StartHeader
00701     // operation ONLY WHEN THE CHARSET OF THE ORIGINAL MESSAGE IS
00702     // NOT US-ASCII ("ISO-8859-1")
00703     //
00704     // This is only to notify the emitter of the charset of the 
00705     // original message
00706     char    *mailCharset = DetermineMailCharset(msg);
00707 
00708     if ( (mailCharset) && (PL_strcasecmp(mailCharset, "US-ASCII")) &&
00709          (PL_strcasecmp(mailCharset, "ISO-8859-1")) )
00710       mimeEmitterUpdateCharacterSet(obj->options, mailCharset);
00711     PR_FREEIF(mailCharset);
00712     return 0;
00713   }
00714 
00715   if (!obj->options->state->first_data_written_p)
00716        {
00717          status = MimeObject_output_init (obj, TEXT_HTML);
00718          if (status < 0) 
00719     {
00720       mimeEmitterEndHeader(obj->options);
00721       return status;
00722     }
00723          PR_ASSERT(obj->options->state->first_data_written_p);
00724        }
00725 
00726   // Start the header parsing by the emitter
00727   char *msgID = MimeHeaders_get (msg->hdrs, HEADER_MESSAGE_ID,
00728                                                                                  PR_FALSE, PR_FALSE);
00729   PRBool outer_p = !obj->headers; /* is this the outermost message? */
00730   if (!outer_p && obj->options->format_out == nsMimeOutput::nsMimeMessageBodyDisplay &&
00731       obj->options->part_to_load)
00732   {
00733     //Maybe we are displaying a embedded message as outer part!
00734     char *id = mime_part_address(obj);
00735     if (id)
00736     {
00737       outer_p = !strcmp(id, obj->options->part_to_load);
00738       PR_Free(id);
00739     }
00740   }
00741 
00742   // Ok, we should really find out the charset of this part. We always
00743   // output UTF-8 for display, but the original charset is necessary for
00744   // reply and forward operations.
00745   //
00746   char    *mailCharset = DetermineMailCharset(msg);
00747   mimeEmitterStartHeader(obj->options, 
00748                             outer_p, 
00749                             (obj->options->headers == MimeHeadersOnly),
00750                             msgID,
00751                             mailCharset);
00752 
00753   // Change the default_charset by the charset of the original message
00754   // ONLY WHEN THE CHARSET OF THE ORIGINAL MESSAGE IS NOT US-ASCII
00755   // ("ISO-8859-1") and defailt_charset and mailCharset are different.
00756   if ( (mailCharset) && (PL_strcasecmp(mailCharset, "US-ASCII")) &&
00757        (PL_strcasecmp(mailCharset, "ISO-8859-1")) &&
00758        (PL_strcasecmp(obj->options->default_charset, mailCharset)) &&
00759        !obj->options->override_charset )
00760   {
00761     PR_Free(obj->options->default_charset);
00762     obj->options->default_charset = strdup(mailCharset);
00763   }
00764 
00765   PR_FREEIF(msgID);
00766   PR_FREEIF(mailCharset);
00767 
00768 #ifdef MOZ_SECURITY
00769     HG00919 
00770 #endif /* MOZ_SECURITY */
00771 
00772   status = MimeHeaders_write_all_headers (msg->hdrs, obj->options, PR_FALSE);
00773   if (status < 0) 
00774   {
00775     mimeEmitterEndHeader(obj->options);
00776     return status;
00777   }
00778 
00779   if (msg->crypto_stamped_p)
00780   {
00781 #ifdef MOZ_SECURITY
00782     HG11995
00783 #endif /* MOZ_SECURITY */     
00784   }
00785   else
00786   {
00787   /* If we're not writing a xlation stamp, and this is the outermost
00788   message, then now is the time to run the post_header_html_fn.
00789   (Otherwise, it will be run when the xlation-stamp is finally
00790   closed off, in MimeXlateed_emit_buffered_child() or
00791   MimeMultipartSigned_emit_child().)
00792           */
00793     if (obj->options &&
00794       obj->options->state &&
00795       obj->options->generate_post_header_html_fn &&
00796       !obj->options->state->post_header_html_run_p)
00797     {
00798       char *html = 0;
00799       PR_ASSERT(obj->options->state->first_data_written_p);
00800       html = obj->options->generate_post_header_html_fn(NULL,
00801                                           obj->options->html_closure,
00802                                           msg->hdrs);
00803       obj->options->state->post_header_html_run_p = PR_TRUE;
00804       if (html)
00805       {
00806         status = MimeObject_write(obj, html, strlen(html), PR_FALSE);
00807         PR_Free(html);
00808         if (status < 0) 
00809         {
00810           mimeEmitterEndHeader(obj->options);
00811           return status;
00812         }
00813       }
00814     }    
00815   }
00816 
00817   mimeEmitterEndHeader(obj->options);
00818 
00819   // rhp:
00820   // For now, we are going to parse the entire message, even if we are
00821   // only interested in headers...why? Well, because this is the only
00822   // way to build the attachment list. Now we will have the attachment
00823   // list in the output being created by the XML emitter. If we ever
00824   // want to go back to where we were before, just uncomment the conditional
00825   // and it will stop at header parsing.
00826   //
00827   // if (obj->options->headers == MimeHeadersOnly)
00828   //   return -1;
00829   // else
00830 
00831   return 0;
00832 }
00833 
00834 static char *
00835 MimeMessage_partial_message_html(const char *data, void *closure,
00836                                                          MimeHeaders *headers)
00837 {
00838   MimeMessage *msg = (MimeMessage *)closure;
00839   nsCAutoString orig_url(data);
00840   char *partialMsgHtml = nsnull;
00841   char *uidl = MimeHeaders_get(headers, HEADER_X_UIDL, PR_FALSE, PR_FALSE);
00842   char *msgId = MimeHeaders_get(headers, HEADER_MESSAGE_ID, PR_FALSE,
00843                                                           PR_FALSE);
00844   char *msgIdPtr = PL_strstr(msgId, "<");
00845   int msgBase;
00846 
00847   orig_url.ReplaceSubstring("mailbox-message", "mailbox");
00848   orig_url.ReplaceSubstring("#", "?number=");
00849 
00850   if (msgIdPtr)
00851     msgIdPtr++;
00852   else
00853     msgIdPtr = msgId;
00854   char *gtPtr = PL_strstr(msgIdPtr, ">");
00855   if (gtPtr)
00856     *gtPtr = 0;
00857 
00858   msgBase = (msg->bodyLength > MSG_LINEBREAK_LEN) ? MIME_MSG_PARTIAL_FMT_1 : MIME_MSG_PARTIAL_FMT2_1;
00859   char *escapedUidl = uidl ? nsEscape(uidl, url_XAlphas) : nsnull;
00860   char *escapedMsgId = msgIdPtr ? nsEscape(msgIdPtr, url_Path) : nsnull;
00861   char *fmt1 = MimeGetStringByID(msgBase);
00862   char *fmt2 = MimeGetStringByID(msgBase+1);
00863   char *fmt3 = MimeGetStringByID(msgBase+2);
00864   char *msgUrl = PR_smprintf("%s&messageid=%s&uidl=%s",
00865                              orig_url.get(), escapedMsgId, escapedUidl);
00866   partialMsgHtml = PR_smprintf("%s%s%s%s", fmt1,fmt2, msgUrl, fmt3);
00867   PR_Free(uidl);
00868   PR_Free(escapedUidl);
00869   PR_Free(msgId);
00870   PR_Free(escapedMsgId);
00871   PR_Free(msgUrl);
00872   PR_Free(fmt1);
00873   PR_Free(fmt2);
00874   PR_Free(fmt3);
00875 
00876        return partialMsgHtml;
00877 }
00878 
00879 #if defined(DEBUG) && defined(XP_UNIX)
00880 static int
00881 MimeMessage_debug_print (MimeObject *obj, PRFileDesc *stream, PRInt32 depth)
00882 {
00883   MimeMessage *msg = (MimeMessage *) obj;
00884   char *addr = mime_part_address(obj);
00885   int i;
00886   for (i=0; i < depth; i++)
00887        PR_Write(stream, "  ", 2);
00888 /*
00889   fprintf(stream, "<%s %s%s 0x%08X>\n",
00890                 obj->clazz->class_name,
00891                 addr ? addr : "???",
00892                 (msg->container.nchildren == 0 ? " (no body)" : ""),
00893                 (PRUint32) msg);
00894 */
00895   PR_FREEIF(addr);
00896 
00897 #if 0
00898   if (msg->hdrs)
00899        {
00900          char *s;
00901 
00902          depth++;
00903 
00904 # define DUMP(HEADER) \
00905          for (i=0; i < depth; i++)                                                                              \
00906         PR_Write(stream, "  ", 2);                                                                              \
00907          s = MimeHeaders_get (msg->hdrs, HEADER, PR_FALSE, PR_TRUE);
00908 
00913          PR_FREEIF(s)
00914 
00915       DUMP(HEADER_SUBJECT);
00916       DUMP(HEADER_DATE);
00917       DUMP(HEADER_FROM);
00918       DUMP(HEADER_TO);
00919       /* DUMP(HEADER_CC); */
00920       DUMP(HEADER_NEWSGROUPS);
00921       DUMP(HEADER_MESSAGE_ID);
00922 # undef DUMP
00923 
00924          PR_Write(stream, "\n", 1);
00925        }
00926 #endif
00927 
00928   PR_ASSERT(msg->container.nchildren <= 1);
00929   if (msg->container.nchildren == 1)
00930        {
00931          MimeObject *kid = msg->container.children[0];
00932          int status = kid->clazz->debug_print (kid, stream, depth+1);
00933          if (status < 0) return status;
00934        }
00935   return 0;
00936 }
00937 #endif