Ejemplo n.º 1
0
static int
MimeInlineTextPlain_parse_eof (MimeObject *obj, bool abort_p)
{
  int status;

  // Has this method already been called for this object?
  // In that case return.
  if (obj->closed_p) return 0;

  nsCString citationColor;
  MimeInlineTextPlain *text = (MimeInlineTextPlain *) obj;
  if (text && text->mCitationColor)
    citationColor.Adopt(text->mCitationColor);

  bool quoting = ( obj->options
    && ( obj->options->format_out == nsMimeOutput::nsMimeMessageQuoting ||
         obj->options->format_out == nsMimeOutput::nsMimeMessageBodyQuoting
       )           );  // see above

  bool rawPlainText = obj->options &&
       (obj->options->format_out == nsMimeOutput::nsMimeMessageFilterSniffer
        || obj->options->format_out == nsMimeOutput::nsMimeMessageAttach);

  /* Run parent method first, to flush out any buffered data. */
  status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof(obj, abort_p);
  if (status < 0) return status;

  if (!obj->output_p) return 0;

  if (obj->options &&
    obj->options->write_html_p &&
    obj->options->output_fn &&
    !abort_p && !rawPlainText)
  {
      MimeInlineTextPlain *text = (MimeInlineTextPlain *) obj;
      if (text->mIsSig && !quoting)
      {
        status = MimeObject_write(obj, "</div>", 6, false);  // .moz-txt-sig
        if (status < 0) return status;
      }
      status = MimeObject_write(obj, "</pre>", 6, false);
      if (status < 0) return status;
      if (!quoting)
      {
        status = MimeObject_write(obj, "</div>", 6, false);
                                        // .moz-text-plain
        if (status < 0) return status;
      }

      /* text/plain objects always have separators before and after them.
     Note that this is not the case for text/enriched objects.
     */
    status = MimeObject_write_separator(obj);
    if (status < 0) return status;
  }

  return 0;
}
Ejemplo n.º 2
0
static int
MimeMessage_parse_begin (MimeObject *obj)
{
  MimeMessage *msg = (MimeMessage *)obj;

  int status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_begin(obj);
  if (status < 0) return status;

  if (obj->parent)
  {
    msg->grabSubject = PR_TRUE;
  }

  /* Messages have separators before the headers, except for the outermost
   message. */
  return MimeObject_write_separator(obj);
}
Ejemplo n.º 3
0
static int
MimeInlineTextPlain_parse_eof (MimeObject *obj, PRBool abort_p)
{
  int status;

  // Has this method already been called for this object?
  // In that case return.
  if (obj->closed_p) return 0;

  nsXPIDLCString citationColor;
  MimeInlineTextPlain *text = (MimeInlineTextPlain *) obj;
  if (text && text->mCitationColor)
    citationColor.Adopt(text->mCitationColor);

  PRBool quoting = ( obj->options
    && ( obj->options->format_out == nsMimeOutput::nsMimeMessageQuoting ||
         obj->options->format_out == nsMimeOutput::nsMimeMessageBodyQuoting
       )           );  // see above
  
  PRBool rawPlainText = obj->options &&
       (obj->options->format_out == nsMimeOutput::nsMimeMessageFilterSniffer
        || obj->options->format_out == nsMimeOutput::nsMimeMessageAttach);

  /* Run parent method first, to flush out any buffered data. */
  status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof(obj, abort_p);
  if (status < 0) return status;

  // if this part has a name and it's not a message/rfc822, don't quote
  if (quoting && obj->headers && MimeHeaders_get_name(obj->headers, obj->options) 
      && PL_strcasecmp(obj->content_type, MESSAGE_RFC822))
    return 0;

  if (!obj->output_p) return 0;

  if (obj->options &&
	  obj->options->write_html_p &&
	  obj->options->output_fn &&
	  !abort_p && !rawPlainText)
	{
      MimeInlineTextPlain *text = (MimeInlineTextPlain *) obj;
      if (text->mIsSig && !quoting)
      {
        status = MimeObject_write(obj, "</div>", 6, PR_FALSE);  // .moz-txt-sig
        if (status < 0) return status;
      }
      status = MimeObject_write(obj, "</pre>", 6, PR_FALSE);
      if (status < 0) return status;
      if (!quoting)
      {
        status = MimeObject_write(obj, "</div>", 6, PR_FALSE);
                                        // .moz-text-plain
        if (status < 0) return status;
      }

      /* text/plain objects always have separators before and after them.
		 Note that this is not the case for text/enriched objects.
	   */
	  status = MimeObject_write_separator(obj);
	  if (status < 0) return status;
	}

  return 0;
}
Ejemplo n.º 4
0
static int
MimeInlineTextPlain_parse_begin (MimeObject *obj)
{
  int status = 0;
  PRBool quoting = ( obj->options
    && ( obj->options->format_out == nsMimeOutput::nsMimeMessageQuoting ||
         obj->options->format_out == nsMimeOutput::nsMimeMessageBodyQuoting
       )       );  // The output will be inserted in the composer as quotation
  PRBool plainHTML = quoting || (obj->options &&
       (obj->options->format_out == nsMimeOutput::nsMimeMessageSaveAs));
       // Just good(tm) HTML. No reliance on CSS.
  PRBool rawPlainText = obj->options &&
       (obj->options->format_out == nsMimeOutput::nsMimeMessageFilterSniffer
         || obj->options->format_out == nsMimeOutput::nsMimeMessageAttach);

  status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_begin(obj);
  if (status < 0) return status;

  if (!obj->output_p) return 0;

  if (obj->options &&
	  obj->options->write_html_p &&
	  obj->options->output_fn)
	{
      MimeInlineTextPlain *text = (MimeInlineTextPlain *) obj;
      text->mCiteLevel = 0;

      // Get the prefs

      // Quoting
      text->mBlockquoting = PR_TRUE; // mail.quoteasblock

      // Viewing
      text->mQuotedSizeSetting = 0;   // mail.quoted_size
      text->mQuotedStyleSetting = 0;  // mail.quoted_style
      text->mCitationColor = nsnull;  // mail.citation_color
      PRBool graphicalQuote = PR_TRUE; // mail.quoted_graphical

      nsIPrefBranch *prefBranch = GetPrefBranch(obj->options);
      if (prefBranch)
      {
        prefBranch->GetIntPref("mail.quoted_size", &(text->mQuotedSizeSetting));
        prefBranch->GetIntPref("mail.quoted_style", &(text->mQuotedStyleSetting));
        prefBranch->GetCharPref("mail.citation_color", &(text->mCitationColor));
        prefBranch->GetBoolPref("mail.quoted_graphical", &graphicalQuote);
        prefBranch->GetBoolPref("mail.quoteasblock", &(text->mBlockquoting));
      }

      if (!rawPlainText)
      {
        // Get font
        // only used for viewing (!plainHTML)
        nsCAutoString fontstyle;
        nsCAutoString fontLang;  // langgroup of the font

        // generic font-family name ( -moz-fixed for fixed font and NULL for
        // variable font ) is sufficient now that bug 105199 has been fixed.

        if (!obj->options->variable_width_plaintext_p)
          fontstyle = "font-family: -moz-fixed";

        if (nsMimeOutput::nsMimeMessageBodyDisplay == obj->options->format_out ||
            nsMimeOutput::nsMimeMessagePrintOutput == obj->options->format_out)
        {
          PRInt32 fontSize;       // default font size
          PRInt32 fontSizePercentage;   // size percentage
          nsresult rv = GetMailNewsFont(obj,
                             !obj->options->variable_width_plaintext_p,
                             &fontSize, &fontSizePercentage, fontLang);
          if (NS_SUCCEEDED(rv))
          {
            if ( ! fontstyle.IsEmpty() ) {
              fontstyle += "; ";
            }
            fontstyle += "font-size: ";
            fontstyle.AppendInt(fontSize);
            fontstyle += "px;";
          }
        }

        // Opening <div>. We currently have to add formatting here. :-(
        nsCAutoString openingDiv;
        if (!quoting)
             /* 4.x' editor can't break <div>s (e.g. to interleave comments).
                We'll add the class to the <blockquote type=cite> later. */
        {
          openingDiv = "<div class=\"moz-text-plain\"";
          if (!plainHTML)
          {
            if (obj->options->wrap_long_lines_p)
              openingDiv += " wrap=true";
            else
              openingDiv += " wrap=false";

            if (graphicalQuote)
              openingDiv += " graphical-quote=true";
            else
              openingDiv += " graphical-quote=false";

            if (!fontstyle.IsEmpty())
            {
              openingDiv += " style=\"";
              openingDiv += fontstyle;
              openingDiv += '\"';
            }
            if (!fontLang.IsEmpty())
            {
              openingDiv += " lang=\"";
              openingDiv += fontLang;
              openingDiv += '\"';
            }
          }
          openingDiv += "><pre wrap>";
        }
        else
          openingDiv = "<pre wrap>";
	    status = MimeObject_write(obj, openingDiv.get(), openingDiv.Length(), PR_FALSE);
	    if (status < 0) return status;

	    /* text/plain objects always have separators before and after them.
		   Note that this is not the case for text/enriched objects. */
	    status = MimeObject_write_separator(obj);
	    if (status < 0) return status;
    }
	}

  return 0;
}
Ejemplo n.º 5
0
static int
MimeMessage_parse_eof (MimeObject *obj, PRBool abort_p)
{
  int status;
  PRBool outer_p;
  MimeMessage *msg = (MimeMessage *)obj;
  if (obj->closed_p) return 0;

  /* Run parent method first, to flush out any buffered data. */
  status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof(obj, abort_p);
  if (status < 0) return status;

  outer_p = !obj->headers;  /* is this the outermost message? */

  // Hack for messages with truncated headers (bug 244722)
  // If there is no empty line in a message, the parser can't figure out where
  // the headers end, causing parsing to hang. So we insert an extra newline
  // to keep it happy. This is OK, since a message without any empty lines is
  // broken anyway...
  if(outer_p && msg->hdrs && ! msg->hdrs->done_p) {
    MimeMessage_parse_line("\n", 1, obj);
  }

  // Once we get to the end of parsing the message, we will notify
  // the emitter that we are done the the body.

  // Mark the end of the mail body if we are actually emitting the
  // body of the message (i.e. not Header ONLY)
  if (outer_p && obj->options && obj->options->write_html_p)
  {
    if (obj->options->generate_footer_html_fn)
    {
      mime_stream_data *msd =
        (mime_stream_data *) obj->options->stream_closure;
      if (msd)
      {
        char *html = obj->options->generate_footer_html_fn
          (msd->orig_url_name, obj->options->html_closure, msg->hdrs);
        if (html)
        {
          int lstatus = MimeObject_write(obj, html,
                         strlen(html),
                         PR_FALSE);
          PR_Free(html);
          if (lstatus < 0) return lstatus;
        }
      }
    }
    if ((!obj->options->part_to_load  || obj->options->format_out == nsMimeOutput::nsMimeMessageBodyDisplay) &&
      obj->options->headers != MimeHeadersOnly)
      mimeEmitterEndBody(obj->options);
  }

#ifdef MIME_DRAFTS
  if ( obj->options &&
     obj->options->decompose_file_p &&
     obj->options->done_parsing_outer_headers &&
     ! obj->options->is_multipart_msg &&
     ! mime_typep(obj, (MimeObjectClass*) &mimeEncryptedClass) &&
     obj->options->decompose_file_close_fn ) {
  status = obj->options->decompose_file_close_fn (
                         obj->options->stream_closure );

  if ( status < 0 ) return status;
  }
#endif /* MIME_DRAFTS */


  /* Put out a separator after every message/rfc822 object. */
  if (!abort_p && !outer_p)
  {
    status = MimeObject_write_separator(obj);
    if (status < 0) return status;
  }

  return 0;
}
Ejemplo n.º 6
0
static int
MimeEncrypted_emit_buffered_child(MimeObject *obj)
{
  MimeEncrypted *enc = (MimeEncrypted *) obj;
  int status = 0;
  char *ct = 0;
  MimeObject *body;

  NS_ASSERTION(enc->crypto_closure, "1.2 <*****@*****.**> 01 Nov 2001 17:59");

  /* Emit some HTML saying whether the signature was cool.
	 But don't emit anything if in FO_QUOTE_MESSAGE mode.

	 Also, don't emit anything if the enclosed object is itself a signed
	 object -- in the case of an encrypted object which contains a signed
	 object, we only emit the HTML once (since the normal way of encrypting
	 and signing is to nest the signature inside the crypto envelope.)
   */
  if (enc->crypto_closure &&
	  obj->options &&
	  obj->options->headers != MimeHeadersCitation &&
	  obj->options->write_html_p &&
	  obj->options->output_fn)
	  // && !mime_crypto_object_p(enc->hdrs, PR_TRUE)) // XXX fix later XXX //
	{
    char *html;
#if 0 // XXX Fix this later XXX //
	  char *html = (((MimeEncryptedClass *) obj->clazz)->crypto_generate_html
					(enc->crypto_closure));
	  if (!html) return -1; /* MK_OUT_OF_MEMORY? */
	  
    status = MimeObject_write(obj, html, nsCRT::strlen(html), PR_FALSE);
	  PR_FREEIF(html);
	  if (status < 0) return status;
#endif

	  /* Now that we have written out the crypto stamp, the outermost header
		 block is well and truly closed.  If this is in fact the outermost
		 message, then run the post_header_html_fn now.
	   */
	  if (obj->options &&
		  obj->options->state &&
		  obj->options->generate_post_header_html_fn &&
		  !obj->options->state->post_header_html_run_p)
		{
		  MimeHeaders *outer_headers = nsnull;
		  MimeObject *p;
		  for (p = obj; p->parent; p = p->parent)
			  outer_headers = p->headers;
		  NS_ASSERTION(obj->options->state->first_data_written_p, "1.2 <*****@*****.**> 01 Nov 2001 17:59");
		  html = obj->options->generate_post_header_html_fn(NULL,
													obj->options->html_closure,
															outer_headers);
		  obj->options->state->post_header_html_run_p = PR_TRUE;
		  if (html)
			{
        status = MimeObject_write(obj, html, strlen(html), PR_FALSE);
			  PR_FREEIF(html);
			  if (status < 0) return status;
			}
		}
	}
  else if (enc->crypto_closure &&
		   obj->options &&
	   obj->options->decrypt_p)
	{
	  /* Do this just to cause `mime_set_crypto_stamp' to be called, and to
		 cause the various `decode_error' and `verify_error' slots to be set:
		 we don't actually use the returned HTML, because we're not emitting
		 HTML.  It's maybe not such a good thing that the determination of
		 whether it was encrypted or not is tied up with generating HTML,
		 but oh well. */
	  char *html = (((MimeEncryptedClass *) obj->clazz)->crypto_generate_html
					(enc->crypto_closure));
	  PR_FREEIF(html);
	}

  if (enc->hdrs)
	ct = MimeHeaders_get (enc->hdrs, HEADER_CONTENT_TYPE, PR_TRUE, PR_FALSE);
  body = mime_create((ct ? ct : TEXT_PLAIN), enc->hdrs, obj->options);
  
#ifdef MIME_DRAFTS
  if (obj->options->decompose_file_p) {
	if (mime_typep (body, (MimeObjectClass*) &mimeMultipartClass) )
		obj->options->is_multipart_msg = PR_TRUE;
	else if (obj->options->decompose_file_init_fn)
		obj->options->decompose_file_init_fn(obj->options->stream_closure,
											 enc->hdrs);
  }
#endif /* MIME_DRAFTS */

  PR_FREEIF(ct);

  if (!body) return MIME_OUT_OF_MEMORY;
  status = ((MimeContainerClass *) obj->clazz)->add_child (obj, body);
  if (status < 0)
	{
	  mime_free(body);
	  return status;
	}

  /* Now that we've added this new object to our list of children,
	 start its parser going. */
  status = body->clazz->parse_begin(body);
  if (status < 0) return status;

  /* If this object (or the parent) is being output, then by definition
	 the child is as well.  (This is only necessary because this is such
	 a funny sort of container...)
   */
  if (!body->output_p &&
	  (obj->output_p ||
	   (obj->parent && obj->parent->output_p)))
	body->output_p = PR_TRUE;

  /* If the body is being written raw (not as HTML) then make sure to
	 write its headers as well. */
  if (body->output_p && obj->output_p && !obj->options->write_html_p)
	{
	  status = MimeObject_write(body, "", 0, PR_FALSE);  /* initialize */
	  if (status < 0) return status;
	  status = MimeHeaders_write_raw_headers(body->headers, obj->options,
											 PR_FALSE);
	  if (status < 0) return status;
	}

  if (enc->part_buffer)  /* part_buffer is 0 for 0-length encrypted data. */

#ifdef MIME_DRAFTS
    if (obj->options->decompose_file_p && !obj->options->is_multipart_msg)
		status = MimePartBufferRead(enc->part_buffer,
								/* The (nsresult (*) ...) cast is to turn the `void'
								   argument into `MimeObject'. */
								 ((nsresult (*) (const char *, PRInt32, void *))
								obj->options->decompose_file_output_fn),
								obj->options->stream_closure);
    else
#endif /* MIME_DRAFTS */

	status = MimePartBufferRead(enc->part_buffer,
								/* The (nsresult (*) ...) cast is to turn the `void'
								   argument into `MimeObject'. */
								 ((nsresult (*) (const char *, PRInt32, void *))
								 body->clazz->parse_buffer),
								body);
  if (status < 0) return status;

  /* The child has been fully processed.  Close it off.
   */
  status = body->clazz->parse_eof(body, PR_FALSE);
  if (status < 0) return status;

  status = body->clazz->parse_end(body, PR_FALSE);
  if (status < 0) return status;

#ifdef MIME_DRAFTS
  if (obj->options->decompose_file_p && !obj->options->is_multipart_msg)
	  obj->options->decompose_file_close_fn(obj->options->stream_closure);
#endif /* MIME_DRAFTS */

  /* Put out a separator after every encrypted object. */
  status = MimeObject_write_separator(obj);
  if (status < 0) return status;

  MimeEncrypted_cleanup (obj, PR_FALSE);

  return 0;
}
Ejemplo n.º 7
0
static int
MimeInlineImage_parse_begin (MimeObject *obj)
{
  MimeInlineImage *img = (MimeInlineImage *) obj;
  MimeInlineImageClass *clazz;

  int status;

  status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_begin(obj);
  if (status < 0) return status;

  if (!obj->output_p) return 0;

  if (!obj->options || !obj->options->output_fn ||
      // don't bother processing if the consumer doesn't want us
      //  gunking the body up.
      obj->options->write_pure_bodies)
    return 0;

  clazz = (MimeInlineImageClass *) obj->clazz;

  if (obj->options &&
    obj->options->image_begin &&
    obj->options->write_html_p &&
    obj->options->image_write_buffer)
  {
    char *html, *part, *image_url;
    const char *ct;

    part = mime_part_address(obj);
    if (!part) return MIME_OUT_OF_MEMORY;

      char *no_part_url = nullptr;
      if (obj->options->part_to_load && obj->options->format_out == nsMimeOutput::nsMimeMessageBodyDisplay)
        no_part_url = mime_get_base_url(obj->options->url);

        if (no_part_url)
        {
          image_url = mime_set_url_part(no_part_url, part, true);
          PR_Free(no_part_url);
        }
        else
          image_url = mime_set_url_part(obj->options->url, part, true);

    if (!image_url)
    {
      PR_Free(part);
      return MIME_OUT_OF_MEMORY;
    }
    PR_Free(part);

    ct = obj->content_type;
    if (!ct) ct = IMAGE_GIF;  /* Can't happen?  Close enough. */

    // Fill in content type and attachment name here.
    nsCAutoString url_with_filename(image_url);
    url_with_filename += "&type=";
    url_with_filename += ct;
    char * filename = MimeHeaders_get_name ( obj->headers, obj->options );
    if (filename)
    {
      nsCString escapedName;
      MsgEscapeString(nsDependentCString(filename), nsINetUtil::ESCAPE_URL_PATH,
                      escapedName);
      url_with_filename += "&filename=";
      url_with_filename += escapedName;
      PR_Free(filename);
    }

    // We need to separate images with HR's...
    MimeObject_write_separator(obj);

    img->image_data =
      obj->options->image_begin(url_with_filename.get(), ct, obj->options->stream_closure);
    PR_Free(image_url);

    if (!img->image_data) return MIME_OUT_OF_MEMORY;

    html = obj->options->make_image_html(img->image_data);
    if (!html) return MIME_OUT_OF_MEMORY;

    status = MimeObject_write(obj, html, strlen(html), true);
    PR_Free(html);
    if (status < 0) return status;
  }

  //
  // Now we are going to see if we should set the content type in the
  // URI for the url being run...
  //
  if (obj->options && obj->options->stream_closure && obj->content_type)
  {
    mime_stream_data  *msd = (mime_stream_data *) (obj->options->stream_closure);
    if ( (msd) && (msd->channel) )
    {
      msd->channel->SetContentType(nsDependentCString(obj->content_type));
    }
  }

  return 0;
}
Ejemplo n.º 8
0
static int
MimeMultipartSigned_emit_child (MimeObject *obj)
{
  MimeMultipartSigned *sig = (MimeMultipartSigned *) obj;
  MimeMultipart *mult = (MimeMultipart *) obj;
  MimeContainer *cont = (MimeContainer *) obj;
  int status = 0;
  MimeObject *body;

  NS_ASSERTION(sig->crypto_closure, "no crypto closure");

  /* Emit some HTML saying whether the signature was cool.
   But don't emit anything if in FO_QUOTE_MESSAGE mode.
   */
  if (obj->options &&
    obj->options->headers != MimeHeadersCitation &&
    obj->options->write_html_p &&
    obj->options->output_fn &&
    obj->options->headers != MimeHeadersCitation &&
    sig->crypto_closure)
  {
    char *html = (((MimeMultipartSignedClass *) obj->clazz)
          ->crypto_generate_html (sig->crypto_closure));
#if 0 // XXX For the moment, no HTML output. Fix this XXX //
    if (!html) return -1; /* MIME_OUT_OF_MEMORY? */

    status = MimeObject_write(obj, html, strlen(html), false);
    PR_Free(html);
    if (status < 0) return status;
#endif

    /* Now that we have written out the crypto stamp, the outermost header
     block is well and truly closed.  If this is in fact the outermost
     message, then run the post_header_html_fn now.
     */
    if (obj->options &&
      obj->options->state &&
      obj->options->generate_post_header_html_fn &&
      !obj->options->state->post_header_html_run_p)
    {
      MimeHeaders *outer_headers=nsnull;
      MimeObject *p;
      for (p = obj; p->parent; p = p->parent)
      outer_headers = p->headers;
      NS_ASSERTION(obj->options->state->first_data_written_p,
                   "should have already written some data");
      html = obj->options->generate_post_header_html_fn(NULL,
                          obj->options->html_closure,
                              outer_headers);
      obj->options->state->post_header_html_run_p = true;
      if (html)
      {
        status = MimeObject_write(obj, html, strlen(html), false);
        PR_Free(html);
        if (status < 0) return status;
      }
    }
  }


  /* Oh, this is fairly nasty.  We're skipping over our "create child" method
   and using the one our superclass defines.  Perhaps instead we should add
   a new method on this class, and initialize that method to be the
   create_child method of the superclass.  Whatever.
   */


  /* The superclass method expects to find the headers for the part that it's
   to create in mult->hdrs, so ensure that they're there. */
  NS_ASSERTION(!mult->hdrs, "shouldn't already have hdrs for multipart");
  if (mult->hdrs) MimeHeaders_free(mult->hdrs);
  mult->hdrs = sig->body_hdrs;
  sig->body_hdrs = 0;

  /* Run the superclass create_child method.
   */
  status = (((MimeMultipartClass *)(&MIME_SUPERCLASS))->create_child(obj));
  if (status < 0) return status;

  // Notify the charset of the first part.
  if (obj->options && !(obj->options->override_charset)) {
    MimeObject *firstChild = ((MimeContainer*) obj)->children[0];
    char *disposition = MimeHeaders_get (firstChild->headers,
                                         HEADER_CONTENT_DISPOSITION, 
                                         true,
                                         false);
    // check if need to show as inline
    if (!disposition)
    {
      const char *content_type = firstChild->content_type;
      if (!PL_strcasecmp (content_type, TEXT_PLAIN) ||
          !PL_strcasecmp (content_type, TEXT_HTML) ||
          !PL_strcasecmp (content_type, TEXT_MDL) ||
          !PL_strcasecmp (content_type, MULTIPART_ALTERNATIVE) ||
          !PL_strcasecmp (content_type, MULTIPART_RELATED) ||
          !PL_strcasecmp (content_type, MESSAGE_NEWS) ||
          !PL_strcasecmp (content_type, MESSAGE_RFC822)) {
        char *ct = MimeHeaders_get(mult->hdrs, HEADER_CONTENT_TYPE, false, false);
        if (ct) {
          char *cset = MimeHeaders_get_parameter (ct, "charset", NULL, NULL);
          if (cset) {
            mimeEmitterUpdateCharacterSet(obj->options, cset);
            SetMailCharacterSetToMsgWindow(obj, cset);
            PR_Free(cset);
          }
          PR_Free(ct);
        }
      }
    }
  }
  
  // The js emitter wants to know about the newly created child.  Because
  //  MimeMultipartSigned dummies out its create_child operation, the logic
  //  in MimeMultipart_parse_line that would normally provide this notification
  //  does not get to fire.
  if (obj->options && obj->options->notify_nested_bodies) {
    MimeObject *kid = ((MimeContainer*) obj)->children[0];
    // The emitter is expecting the content type with parameters; not the fully
    //  parsed thing, so get it from raw.  (We do not do it in the charset
    //  notification block that just happened because it already has complex
    //  if-checks that do not jive with us.
    char *ct = MimeHeaders_get(mult->hdrs, HEADER_CONTENT_TYPE, false,
                               false);
    mimeEmitterAddHeaderField(obj->options, HEADER_CONTENT_TYPE,
                              ct ? ct : "text/plain");
    PR_Free(ct);
    
    char *part_path = mime_part_address(kid);
    if (part_path) {
      mimeEmitterAddHeaderField(obj->options,
                                "x-jsemitter-part-path",
                                part_path);
      PR_Free(part_path);
    }
  }

  /* Retrieve the child that it created.
   */
  NS_ASSERTION(cont->nchildren == 1, "should only have one child");
  if (cont->nchildren != 1)
    return -1;
  body = cont->children[0];
  NS_ASSERTION(body, "missing body");
  if (!body)
    return -1;

#ifdef MIME_DRAFTS
  if (body->options->decompose_file_p) {
    body->options->signed_p = true;
    if (!mime_typep(body, (MimeObjectClass*)&mimeMultipartClass) &&
    body->options->decompose_file_init_fn)
    body->options->decompose_file_init_fn ( body->options->stream_closure, body->headers );
  }
#endif /* MIME_DRAFTS */

  /* If there's no part_buffer, this is a zero-length signed message? */
  if (sig->part_buffer)
  {
#ifdef MIME_DRAFTS
    if (body->options->decompose_file_p &&
      !mime_typep(body, (MimeObjectClass*)&mimeMultipartClass)  &&
      body->options->decompose_file_output_fn)
      status = MimePartBufferRead (sig->part_buffer,
                 /* The (nsresult (*) ...) cast is to turn the
                  `void' argument into `MimeObject'. */
                 ((nsresult (*) (const char *, PRInt32, void *))
                 body->options->decompose_file_output_fn),
                 body->options->stream_closure);
    else
#endif /* MIME_DRAFTS */

    status = MimePartBufferRead (sig->part_buffer,
                 /* The (nsresult (*) ...) cast is to turn the
                  `void' argument into `MimeObject'. */
                 ((nsresult (*) (const char *, PRInt32, void *))
                body->clazz->parse_buffer),
                body);
    if (status < 0) return status;
  }

  MimeMultipartSigned_cleanup(obj, false);

  /* Done parsing. */
  status = body->clazz->parse_eof(body, false);
  if (status < 0) return status;
  status = body->clazz->parse_end(body, false);
  if (status < 0) return status;

#ifdef MIME_DRAFTS
  if (body->options->decompose_file_p &&
    !mime_typep(body, (MimeObjectClass*)&mimeMultipartClass)  &&
    body->options->decompose_file_close_fn)
    body->options->decompose_file_close_fn(body->options->stream_closure);
#endif /* MIME_DRAFTS */

  /* Put out a separator after every multipart/signed object. */
  status = MimeObject_write_separator(obj);
  if (status < 0) return status;

  return 0;
}