Ejemplo n.º 1
0
static int
MimeExternalBody_parse_line (const char *line, PRInt32 length, MimeObject *obj)
{
  MimeExternalBody *bod = (MimeExternalBody *) obj;
  int status = 0;

  NS_ASSERTION(line && *line, "1.1 <*****@*****.**> 19 Mar 1999 12:00");
  if (!line || !*line) return -1;

  if (!obj->output_p) return 0;

  /* If we're supposed to write this object, but aren't supposed to convert
   it to HTML, simply pass it through unaltered. */
  if (obj->options &&
    !obj->options->write_html_p &&
    obj->options->output_fn)
  return MimeObject_write(obj, line, length, PR_TRUE);


  /* If we already have a `body' then we're done parsing headers, and all
   subsequent lines get tacked onto the body. */
  if (bod->body)
  {
    int L = strlen(bod->body);
    char *new_str = (char *)PR_Realloc(bod->body, L + length + 1);
    if (!new_str) return MIME_OUT_OF_MEMORY;
    bod->body = new_str;
    memcpy(bod->body + L, line, length);
    bod->body[L + length] = 0;
    return 0;
  }

  /* Otherwise we don't yet have a body, which means we're not done parsing
   our headers.
   */
  if (!bod->hdrs)
  {
    bod->hdrs = MimeHeaders_new();
    if (!bod->hdrs) return MIME_OUT_OF_MEMORY;
  }

  status = MimeHeaders_parse_line(line, length, bod->hdrs);
  if (status < 0) return status;

  /* If this line is blank, we're now done parsing headers, and should
   create a dummy body to show that.  Gag.
   */
  if (*line == '\r' || *line == '\n')
  {
    bod->body = strdup("");
    if (!bod->body) return MIME_OUT_OF_MEMORY;
  }

  return 0;
}
Ejemplo n.º 2
0
nsresult nsMimeHeaders::Initialize(const char * aAllHeaders, PRInt32 allHeadersSize)
{
  /* just in case we want to reuse the object, cleanup...*/
  if (mHeaders)
    MimeHeaders_free(mHeaders);

  mHeaders = MimeHeaders_new();
  if (mHeaders)
    return MimeHeaders_parse_line(aAllHeaders, allHeadersSize, mHeaders);

  return NS_ERROR_OUT_OF_MEMORY;
}
Ejemplo n.º 3
0
static int
MimeHandleDecryptedOutputLine (char *line, PRInt32 length, MimeObject *obj)
{
  /* Largely the same as MimeMessage_parse_line (the other MIME container
	 type which contains exactly one child.)
   */
  MimeEncrypted *enc = (MimeEncrypted *) obj;
  int status = 0;

  if (!line || !*line) return -1;

  /* If we're supposed to write this object, but aren't supposed to convert
	 it to HTML, simply pass it through unaltered. */
  if (obj->output_p &&
	  obj->options &&
	  !obj->options->write_html_p &&
	  obj->options->output_fn)
	return MimeObject_write(obj, line, length, PR_TRUE);

  /* If we already have a child object in the buffer, then we're done parsing
	 headers, and all subsequent lines get passed to the inferior object
	 without further processing by us.  (Our parent will stop feeding us
	 lines when this MimeMessage part is out of data.)
   */
  if (enc->part_buffer)
	return MimePartBufferWrite (enc->part_buffer, line, length);

  /* Otherwise we don't yet have a child object in the buffer, which means
	 we're not done parsing our headers yet.
   */
  if (!enc->hdrs)
	{
	  enc->hdrs = MimeHeaders_new();
	  if (!enc->hdrs) return MIME_OUT_OF_MEMORY;
	}

  status = MimeHeaders_parse_line(line, length, enc->hdrs);
  if (status < 0) return status;

  /* If this line is blank, we're now done parsing headers, and should
	 examine our content-type to create our "body" part.
   */
  if (*line == nsCRT::CR || *line == nsCRT::LF)
	{
	  status = MimeEncrypted_close_headers(obj);
	  if (status < 0) return status;
	}

  return 0;
}
Ejemplo n.º 4
0
static int
MimeMessage_parse_line (const char *aLine, PRInt32 aLength, MimeObject *obj)
{
  const char * line = aLine;
  PRInt32 length = aLength;

  MimeMessage *msg = (MimeMessage *) obj;
  int status = 0;

  PR_ASSERT(line && *line);
  if (!line || !*line) return -1;

#ifdef MOZ_SECURITY
  HG11013
#endif /* MOZ_SECURITY */

  if (msg->grabSubject)
  {
    if ( (!PL_strncasecmp(line, "Subject: ", 9)) && (obj->parent) )
    {
      if ( (obj->headers) && (!obj->headers->munged_subject) )
      {
        obj->headers->munged_subject = (char *) PL_strndup(line + 9, length - 9);
        char *tPtr = obj->headers->munged_subject;
        while (*tPtr)
        {
          if ( (*tPtr == '\r') || (*tPtr == '\n') )
          {
            *tPtr = '\0';
            break;
          }
          tPtr++;
        }
      }
    }
  }

  /* If we already have a child object, then we're done parsing headers,
   and all subsequent lines get passed to the inferior object without
   further processing by us.  (Our parent will stop feeding us lines
   when this MimeMessage part is out of data.)
   */
  if (msg->container.nchildren)
  {
    MimeObject *kid = msg->container.children[0];
    PRBool nl;
    PR_ASSERT(kid);
    if (!kid) return -1;

          msg->bodyLength += length;

    /* Don't allow MimeMessage objects to not end in a newline, since it
     would be inappropriate for any following part to appear on the same
     line as the last line of the message.

     #### This assumes that the only time the `parse_line' method is
     called with a line that doesn't end in a newline is when that line
     is the last line.
     */
    nl = (length > 0 && (line[length-1] == '\r' || line[length-1] == '\n'));

#ifdef MIME_DRAFTS
    if ( !mime_typep (kid, (MimeObjectClass*) &mimeMessageClass) &&
       obj->options &&
       obj->options->decompose_file_p &&
       ! obj->options->is_multipart_msg &&
       obj->options->decompose_file_output_fn )
    {
      if (!obj->options->decrypt_p) {
        //if we are processing a flowed plain text line, we need to remove any stuffed space
        if (length > 0 && ' ' == *line && mime_typep(kid, (MimeObjectClass *)&mimeInlineTextPlainFlowedClass))
        {
          line ++;
          length --;
        }
        status = obj->options->decompose_file_output_fn (line,
                                 length,
                           obj->options->stream_closure);
        if (status < 0) return status;
        if (!nl) {
        status = obj->options->decompose_file_output_fn (MSG_LINEBREAK,
                                 MSG_LINEBREAK_LEN,
                           obj->options->stream_closure);
        if (status < 0) return status;
        }
        return status;
      }
    }
#endif /* MIME_DRAFTS */


    if (nl)
    return kid->clazz->parse_buffer (line, length, kid);
    else
    {
      /* Hack a newline onto the end. */
      char *s = (char *)PR_MALLOC(length + MSG_LINEBREAK_LEN + 1);
      if (!s) return MIME_OUT_OF_MEMORY;
      memcpy(s, line, length);
      PL_strncpyz(s + length, MSG_LINEBREAK, MSG_LINEBREAK_LEN);
      status = kid->clazz->parse_buffer (s, length + MSG_LINEBREAK_LEN, kid);
      PR_Free(s);
      return status;
    }
  }

  /* Otherwise we don't yet have a child object, which means we're not
   done parsing our headers yet.
   */
  if (!msg->hdrs)
  {
    msg->hdrs = MimeHeaders_new();
    if (!msg->hdrs) return MIME_OUT_OF_MEMORY;
  }

#ifdef MIME_DRAFTS
  if ( obj->options &&
       obj->options->decompose_file_p &&
       ! obj->options->is_multipart_msg &&
       obj->options->done_parsing_outer_headers &&
       obj->options->decompose_file_output_fn )
  {
    status =  obj->options->decompose_file_output_fn( line, length,
                                                      obj->options->stream_closure );
    if (status < 0)
      return status;
  }
#endif /* MIME_DRAFTS */

  status = MimeHeaders_parse_line(line, length, msg->hdrs);
  if (status < 0) return status;

  /* If this line is blank, we're now done parsing headers, and should
   examine our content-type to create our "body" part.
   */
  if (*line == '\r' || *line == '\n')
  {
    status = MimeMessage_close_headers(obj);
    if (status < 0) return status;
  }

  return 0;
}
Ejemplo n.º 5
0
static int MimeUntypedText_open_subpart(MimeObject *obj,
                                        MimeUntypedTextSubpartType ttype,
                                        const char *type, const char *enc,
                                        const char *name, const char *desc) {
  MimeUntypedText *uty = (MimeUntypedText *)obj;
  int status = 0;
  char *h = 0;

  if (!type || !*type || !PL_strcasecmp(type, UNKNOWN_CONTENT_TYPE))
    type = APPLICATION_OCTET_STREAM;
  if (enc && !*enc) enc = 0;
  if (desc && !*desc) desc = 0;
  if (name && !*name) name = 0;

  if (uty->open_subpart) {
    status = MimeUntypedText_close_subpart(obj);
    if (status < 0) return status;
  }
  NS_ASSERTION(!uty->open_subpart, "no open subpart");
  NS_ASSERTION(!uty->open_hdrs, "no open headers");

  /* To make one of these implicitly-typed sub-objects, we make up a fake
   header block, containing only the minimum number of MIME headers needed.
   We could do most of this (Type and Encoding) by making a null header
   block, and simply setting obj->content_type and obj->encoding; but making
   a fake header block is better for two reasons: first, it means that
   something will actually be displayed when in `Show All Headers' mode;
   and second, it's the only way to communicate the filename parameter,
   aside from adding a new slot to MimeObject (which is something to be
   avoided when possible.)
   */

  uty->open_hdrs = MimeHeaders_new();
  if (!uty->open_hdrs) return MIME_OUT_OF_MEMORY;

  uint32_t hlen = strlen(type) + (enc ? strlen(enc) : 0) +
                  (desc ? strlen(desc) : 0) + (name ? strlen(name) : 0) + 100;
  h = (char *)PR_MALLOC(hlen);
  if (!h) return MIME_OUT_OF_MEMORY;

  PL_strncpyz(h, HEADER_CONTENT_TYPE ": ", hlen);
  PL_strcatn(h, hlen, type);
  PL_strcatn(h, hlen, MSG_LINEBREAK);
  status = MimeHeaders_parse_line(h, strlen(h), uty->open_hdrs);
  if (status < 0) goto FAIL;

  if (enc) {
    PL_strncpyz(h, HEADER_CONTENT_TRANSFER_ENCODING ": ", hlen);
    PL_strcatn(h, hlen, enc);
    PL_strcatn(h, hlen, MSG_LINEBREAK);
    status = MimeHeaders_parse_line(h, strlen(h), uty->open_hdrs);
    if (status < 0) goto FAIL;
  }

  if (desc) {
    PL_strncpyz(h, HEADER_CONTENT_DESCRIPTION ": ", hlen);
    PL_strcatn(h, hlen, desc);
    PL_strcatn(h, hlen, MSG_LINEBREAK);
    status = MimeHeaders_parse_line(h, strlen(h), uty->open_hdrs);
    if (status < 0) goto FAIL;
  }
  if (name) {
    PL_strncpyz(h, HEADER_CONTENT_DISPOSITION ": inline; filename=\"", hlen);
    PL_strcatn(h, hlen, name);
    PL_strcatn(h, hlen, "\"" MSG_LINEBREAK);
    status = MimeHeaders_parse_line(h, strlen(h), uty->open_hdrs);
    if (status < 0) goto FAIL;
  }

  /* push out a blank line. */
  PL_strncpyz(h, MSG_LINEBREAK, hlen);
  status = MimeHeaders_parse_line(h, strlen(h), uty->open_hdrs);
  if (status < 0) goto FAIL;

  /* Create a child... */
  {
    bool horrid_kludge = (obj->options && obj->options->state &&
                          obj->options->state->first_part_written_p);
    if (horrid_kludge) obj->options->state->first_part_written_p = false;

    uty->open_subpart = mime_create(type, uty->open_hdrs, obj->options);

    if (horrid_kludge) obj->options->state->first_part_written_p = true;

    if (!uty->open_subpart) {
      status = MIME_OUT_OF_MEMORY;
      goto FAIL;
    }
  }

  /* Add it to the list... */
  status =
      ((MimeContainerClass *)obj->clazz)->add_child(obj, uty->open_subpart);
  if (status < 0) {
    mime_free(uty->open_subpart);
    uty->open_subpart = 0;
    goto FAIL;
  }

  /* And start its parser going. */
  status = uty->open_subpart->clazz->parse_begin(uty->open_subpart);
  if (status < 0) {
    /* MimeContainer->finalize will take care of shutting it down now. */
    uty->open_subpart = 0;
    goto FAIL;
  }

  uty->type = ttype;

FAIL:
  PR_FREEIF(h);

  if (status < 0 && uty->open_hdrs) {
    MimeHeaders_free(uty->open_hdrs);
    uty->open_hdrs = 0;
  }

  return status;
}
Ejemplo n.º 6
0
static int
MimeExternalBody_parse_eof (MimeObject *obj, bool abort_p)
{
  int status = 0;
  MimeExternalBody *bod = (MimeExternalBody *) 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;

#ifdef XP_MACOSX
  if (obj->parent && mime_typep(obj->parent,
                                (MimeObjectClass*) &mimeMultipartAppleDoubleClass))
    goto done;
#endif /* XP_MACOSX */

  if (!abort_p &&
      obj->output_p &&
      obj->options &&
      obj->options->write_html_p)
  {
    bool all_headers_p = obj->options->headers == MimeHeadersAll;
    MimeDisplayOptions *newopt = obj->options;  /* copy it */

    char *ct = MimeHeaders_get(obj->headers, HEADER_CONTENT_TYPE,
                               PR_FALSE, PR_FALSE);
    char *at, *lexp, *size, *perm;
    char *url, *dir, *mode, *name, *site, *svr, *subj;
    char *h = 0, *lname = 0, *lurl = 0, *body = 0;
    MimeHeaders *hdrs = 0;

    if (!ct) return MIME_OUT_OF_MEMORY;

    at   = MimeHeaders_get_parameter(ct, "access-type", NULL, NULL);
    lexp  = MimeHeaders_get_parameter(ct, "expiration", NULL, NULL);
    size = MimeHeaders_get_parameter(ct, "size", NULL, NULL);
    perm = MimeHeaders_get_parameter(ct, "permission", NULL, NULL);
    dir  = MimeHeaders_get_parameter(ct, "directory", NULL, NULL);
    mode = MimeHeaders_get_parameter(ct, "mode", NULL, NULL);
    name = MimeHeaders_get_parameter(ct, "name", NULL, NULL);
    site = MimeHeaders_get_parameter(ct, "site", NULL, NULL);
    svr  = MimeHeaders_get_parameter(ct, "server", NULL, NULL);
    subj = MimeHeaders_get_parameter(ct, "subject", NULL, NULL);
    url  = MimeHeaders_get_parameter(ct, "url", NULL, NULL);
    PR_FREEIF(ct);

    /* the *internal* content-type */
    ct = MimeHeaders_get(bod->hdrs, HEADER_CONTENT_TYPE,
                         PR_TRUE, PR_FALSE);
						 
    PRUint32 hlen = ((at ? strlen(at) : 0) +
                    (lexp ? strlen(lexp) : 0) +
                    (size ? strlen(size) : 0) +
                    (perm ? strlen(perm) : 0) +
                    (dir ? strlen(dir) : 0) +
                    (mode ? strlen(mode) : 0) +
                    (name ? strlen(name) : 0) +
                    (site ? strlen(site) : 0) +
                    (svr ? strlen(svr) : 0) +
                    (subj ? strlen(subj) : 0) +
                    (ct ? strlen(ct) : 0) +
                    (url ? strlen(url) : 0) + 100);
					
	h = (char *) PR_MALLOC(hlen);
    if (!h)
    {
      status = MIME_OUT_OF_MEMORY;
      goto FAIL;
    }

    /* If there's a URL parameter, remove all whitespace from it.
      (The URL parameter to one of these headers is stored with
       lines broken every 40 characters or less; it's assumed that
       all significant whitespace was URL-hex-encoded, and all the
       rest of it was inserted just to keep the lines short.)
      */
    if (url)
    {
      char *in, *out;
      for (in = url, out = url; *in; in++)
        if (!IS_SPACE(*in))
          *out++ = *in;
      *out = 0;
    }

    hdrs = MimeHeaders_new();
    if (!hdrs)
    {
      status = MIME_OUT_OF_MEMORY;
      goto FAIL;
    }

# define FROB(STR,VAR) \
    if (VAR) \
    { \
      PL_strncpyz(h, STR ": ", hlen); \
        PL_strcatn(h, hlen, VAR); \
          PL_strcatn(h, hlen, MSG_LINEBREAK); \
            status = MimeHeaders_parse_line(h, strlen(h), hdrs); \
              if (status < 0) goto FAIL; \
    }
    FROB("Access-Type",  at);
    FROB("URL",      url);
    FROB("Site",      site);
    FROB("Server",    svr);
    FROB("Directory",    dir);
    FROB("Name",      name);
    FROB("Type",      ct);
    FROB("Size",      size);
    FROB("Mode",      mode);
    FROB("Permission",  perm);
    FROB("Expiration",  lexp);
    FROB("Subject",    subj);
# undef FROB
    PL_strncpyz(h, MSG_LINEBREAK, hlen);
    status = MimeHeaders_parse_line(h, strlen(h), hdrs);
    if (status < 0) goto FAIL;

    lurl = MimeExternalBody_make_url(ct, at, lexp, size, perm, dir, mode,
                                     name, url, site, svr, subj, bod->body);
    if (lurl)
    {
      lname = MimeGetStringByID(MIME_MSG_LINK_TO_DOCUMENT);
    }
    else
    {
      lname = MimeGetStringByID(MIME_MSG_DOCUMENT_INFO);
      all_headers_p = PR_TRUE;
    }

    all_headers_p = PR_TRUE;  /* #### just do this all the time? */

    if (bod->body && all_headers_p)
    {
      char *s = bod->body;
      while (IS_SPACE(*s)) s++;
      if (*s)
      {
        char *s2;
        const char *pre = "<P><PRE>";
        const char *suf = "</PRE>";
        PRInt32 i;
        for(i = strlen(s)-1; i >= 0 && IS_SPACE(s[i]); i--)
          s[i] = 0;
        s2 = MsgEscapeHTML(s);
        if (!s2) goto FAIL;
        body = (char *) PR_MALLOC(strlen(pre) + strlen(s2) +
                                  strlen(suf) + 1);
        if (!body)
        {
          NS_Free(s2);
          goto FAIL;
        }
        PL_strcpy(body, pre);
        PL_strcat(body, s2);
        PL_strcat(body, suf);
      }
    }

    newopt->fancy_headers_p = PR_TRUE;
    newopt->headers = (all_headers_p ? MimeHeadersAll : MimeHeadersSome);

FAIL:
      if (hdrs)
        MimeHeaders_free(hdrs);
    PR_FREEIF(h);
    PR_FREEIF(lname);
    PR_FREEIF(lurl);
    PR_FREEIF(body);
    PR_FREEIF(ct);
    PR_FREEIF(at);
    PR_FREEIF(lexp);
    PR_FREEIF(size);
    PR_FREEIF(perm);
    PR_FREEIF(dir);
    PR_FREEIF(mode);
    PR_FREEIF(name);
    PR_FREEIF(url);
    PR_FREEIF(site);
    PR_FREEIF(svr);
    PR_FREEIF(subj);
  }

#ifdef XP_MACOSX
done:
#endif

    return status;
}
Ejemplo n.º 7
0
static int
MimeMultipart_parse_line (const char *line, PRInt32 length, MimeObject *obj)
{
  MimeMultipart *mult = (MimeMultipart *) obj;
  int status = 0;
  MimeMultipartBoundaryType boundary;

  NS_ASSERTION(line && *line, "empty line in multipart parse_line");
  if (!line || !*line) return -1;

  NS_ASSERTION(!obj->closed_p, "obj shouldn't already be closed");
  if (obj->closed_p) return -1;

  /* If we're supposed to write this object, but aren't supposed to convert
     it to HTML, simply pass it through unaltered. */
  if (obj->output_p &&
    obj->options &&
    !obj->options->write_html_p &&
    obj->options->output_fn
          && obj->options->format_out != nsMimeOutput::nsMimeMessageAttach)
  return MimeObject_write(obj, line, length, PR_TRUE);


  if (mult->state == MimeMultipartEpilogue)  /* already done */
    boundary = MimeMultipartBoundaryTypeNone;
  else
    boundary = ((MimeMultipartClass *)obj->clazz)->check_boundary(obj, line,
                                                                  length);

  if (boundary == MimeMultipartBoundaryTypeTerminator ||
    boundary == MimeMultipartBoundaryTypeSeparator)
  {
  /* Match!  Close the currently-open part, move on to the next
     state, and discard this line.
   */
    PRBool endOfPart = (mult->state != MimeMultipartPreamble);
    if (endOfPart)
      status = ((MimeMultipartClass *)obj->clazz)->close_child(obj);
    if (status < 0) return status;
    
    if (boundary == MimeMultipartBoundaryTypeTerminator)
      mult->state = MimeMultipartEpilogue;
    else
    {
      mult->state = MimeMultipartHeaders;
      
      /* Reset the header parser for this upcoming part. */
      NS_ASSERTION(!mult->hdrs, "mult->hdrs should be null here");
      if (mult->hdrs)
        MimeHeaders_free(mult->hdrs);
      mult->hdrs = MimeHeaders_new();
      if (!mult->hdrs)
        return MIME_OUT_OF_MEMORY;
      if (obj->options && obj->options->state &&
          obj->options->state->partsToStrip.Length() > 0)
      {
        nsCAutoString newPart(mime_part_address(obj));
        MimeContainer *container = (MimeContainer*) obj; 
        newPart.Append('.');
        newPart.AppendInt(container->nchildren + 1);
        obj->options->state->strippingPart = PR_FALSE;
        // check if this is a sub-part of a part we're stripping.
        for (PRUint32 partIndex = 0; partIndex < obj->options->state->partsToStrip.Length(); partIndex++)
        {
          nsCString &curPartToStrip = obj->options->state->partsToStrip[partIndex];
          if (newPart.Find(curPartToStrip) == 0 && (newPart.Length() == curPartToStrip.Length() || newPart.CharAt(curPartToStrip.Length()) == '.'))
          {
            obj->options->state->strippingPart = PR_TRUE;
            if (partIndex < obj->options->state->detachToFiles.Length())
              obj->options->state->detachedFilePath = obj->options->state->detachToFiles[partIndex];
            break;
          }
        }
      }
    }
    
    // if stripping out attachments, write the boundary line. Otherwise, return
    // to ignore it.
    if (obj->options && obj->options->format_out == nsMimeOutput::nsMimeMessageAttach)
    {
      // Because MimeMultipart_parse_child_line strips out the 
      // the CRLF of the last line before the end of a part, we need to add that
      // back in here.
      if (endOfPart)
        MimeWriteAString(obj, NS_LITERAL_CSTRING(MSG_LINEBREAK));

      status = MimeObject_write(obj, line, length, PR_TRUE);
    }
    return 0;
  }

  /* Otherwise, this isn't a boundary string.  So do whatever it is we
   should do with this line (parse it as a header, feed it to the
   child part, ignore it, etc.) */

  switch (mult->state)
  {
    case MimeMultipartPreamble:
    case MimeMultipartEpilogue:
      /* Ignore this line. */
      break;

    case MimeMultipartHeaders:
    /* Parse this line as a header for the sub-part. */
    {
      status = MimeHeaders_parse_line(line, length, mult->hdrs);
      if (status < 0) return status;
      
      // If this line is blank, we're now done parsing headers, and should
      // now examine the content-type to create this "body" part.
      //
      if (*line == '\r' || *line == '\n')
      {
        if (obj->options && obj->options->state &&
            obj->options->state->strippingPart)
        {
          PRBool detachingPart = obj->options->state->detachedFilePath.Length() > 0;

          nsCAutoString fileName;
          fileName.Adopt(MimeHeaders_get_name(mult->hdrs, obj->options));
          if (detachingPart)
          {
            char *contentType = MimeHeaders_get(mult->hdrs, "Content-Type", PR_FALSE, PR_FALSE);
            if (contentType)
            {
              MimeWriteAString(obj, NS_LITERAL_CSTRING("Content-Type: "));
              MimeWriteAString(obj, nsDependentCString(contentType));
              PR_Free(contentType);
            }
            MimeWriteAString(obj, NS_LITERAL_CSTRING(MSG_LINEBREAK));
            MimeWriteAString(obj, NS_LITERAL_CSTRING("Content-Disposition: attachment; filename=\""));
            MimeWriteAString(obj, fileName);
            MimeWriteAString(obj, NS_LITERAL_CSTRING("\""MSG_LINEBREAK));
            MimeWriteAString(obj, NS_LITERAL_CSTRING("X-Mozilla-External-Attachment-URL: "));
            MimeWriteAString(obj, obj->options->state->detachedFilePath);
            MimeWriteAString(obj, NS_LITERAL_CSTRING(MSG_LINEBREAK));
            MimeWriteAString(obj, NS_LITERAL_CSTRING("X-Mozilla-Altered: AttachmentDetached; date=\""));
          }
          else
          {
            nsCAutoString header("Content-Type: text/x-moz-deleted; name=\"Deleted: ");
            header.Append(fileName);
            status = MimeWriteAString(obj, header);
            if (status < 0) 
              return status;
            status = MimeWriteAString(obj, NS_LITERAL_CSTRING("\""MSG_LINEBREAK"Content-Transfer-Encoding: 8bit"MSG_LINEBREAK));
            MimeWriteAString(obj, NS_LITERAL_CSTRING("Content-Disposition: inline; filename=\"Deleted: "));
            MimeWriteAString(obj, fileName);
            MimeWriteAString(obj, NS_LITERAL_CSTRING("\""MSG_LINEBREAK"X-Mozilla-Altered: AttachmentDeleted; date=\""));
          }
          nsCString result;
          char timeBuffer[128];
          PRExplodedTime now;
          PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &now);
          PR_FormatTimeUSEnglish(timeBuffer, sizeof(timeBuffer),
                                 "%a %b %d %H:%M:%S %Y",
                                 &now);
          MimeWriteAString(obj, nsDependentCString(timeBuffer));
          MimeWriteAString(obj, NS_LITERAL_CSTRING("\""MSG_LINEBREAK));
          MimeWriteAString(obj, NS_LITERAL_CSTRING(MSG_LINEBREAK"You deleted an attachment from this message. The original MIME headers for the attachment were:"MSG_LINEBREAK));
          MimeHeaders_write_raw_headers(mult->hdrs, obj->options, PR_FALSE);
        }
        status = ((MimeMultipartClass *) obj->clazz)->create_child(obj);
        if (status < 0) return status;
        NS_ASSERTION(mult->state != MimeMultipartHeaders,
                     "mult->state shouldn't be MimeMultipartHeaders");

        // Ok, at this point, we need to examine the headers and see if there
        // is a special charset (i.e. non US-ASCII) for this message. If so, 
        // we need to tell the emitter that this is the case for use in in any
        // possible reply or forward operation.
        //
        PRBool isBody = PR_FALSE;
        PRBool isAlternative = PR_FALSE;

        MimeContainer *container = (MimeContainer*) obj; 
        // check if we're stripping the part of this newly created child.
        if (container->children && container->nchildren > 0)
        {
          MimeObject *kid = container->children[container->nchildren-1];
          if (kid->output_p)
            kid->output_p = !(obj->options && obj->options->state &&
                              obj->options->state->strippingPart);
        }
        if (container->children && container->nchildren == 1)
        {
          PRBool isAlternativeOrRelated = PR_FALSE;
          isBody = MimeObjectChildIsMessageBody(obj, &isAlternativeOrRelated);

          // MimeObjectChildIsMessageBody returns false for "multipart/related"
          // but we want to use the first part charset if that's a body.
          // I don't want to change the behavior of MimeObjectChildIsMessageBody
          // which is used by other places, so do the body check here.
          if (!isBody && 
              isAlternativeOrRelated &&
              mime_subclass_p(obj->clazz, (MimeObjectClass*) &mimeMultipartRelatedClass))
          {
            MimeObject *firstChild = container->children[0];
            char *disposition = MimeHeaders_get (firstChild->headers,
                                                 HEADER_CONTENT_DISPOSITION, 
                                                 PR_TRUE,
                                                 PR_FALSE);
            if (!disposition)
            {
              if (!PL_strcasecmp (firstChild->content_type, TEXT_PLAIN) ||
                  !PL_strcasecmp (firstChild->content_type, TEXT_HTML) ||
                  !PL_strcasecmp (firstChild->content_type, TEXT_MDL) ||
                  !PL_strcasecmp (firstChild->content_type, MULTIPART_ALTERNATIVE) ||
                  !PL_strcasecmp (firstChild->content_type, MULTIPART_RELATED) ||
                  !PL_strcasecmp (firstChild->content_type, MESSAGE_NEWS) ||
                  !PL_strcasecmp (firstChild->content_type, MESSAGE_RFC822))
                isBody = PR_TRUE;
            }
          }
        }
        else 
          isAlternative = mime_subclass_p(obj->clazz, (MimeObjectClass*) &mimeMultipartAlternativeClass);

        // If "multipart/alternative" or the first part is a message body
        // then we should check for a charset and notify the emitter  
        // if one exists.
        if (obj->options &&
            ((isAlternative && mult->state != MimeMultipartSkipPartLine) ||
             isBody || obj->options->notify_nested_bodies))
        {
          {
            char *ct = MimeHeaders_get(mult->hdrs, HEADER_CONTENT_TYPE, PR_FALSE, PR_FALSE);
            if (ct)
            {
              if (obj->options->notify_nested_bodies)
                mimeEmitterAddHeaderField(obj->options, HEADER_CONTENT_TYPE,
                                          ct);
             char *cset = MimeHeaders_get_parameter (ct, "charset", NULL, NULL);
             if (cset)
             {
                mimeEmitterUpdateCharacterSet(obj->options, cset);
                if (!(obj->options->override_charset))
                  // Also set this charset to msgWindow
                  SetMailCharacterSetToMsgWindow(obj, cset);
              }

              PR_FREEIF(ct);
              PR_FREEIF(cset);
            }
            // no content type means text/plain.
            else if (obj->options->notify_nested_bodies)
            {
              mimeEmitterAddHeaderField(obj->options, HEADER_CONTENT_TYPE,
                                        "text/plain");
            }
            if (obj->options->notify_nested_bodies && container->nchildren)
            {
              MimeObject *kid = container->children[container->nchildren-1];
              char *part_path = mime_part_address(kid);
              if (part_path)
              {
                mimeEmitterAddHeaderField(obj->options,
                                          "x-jsemitter-part-path",
                                          part_path);
                PR_Free(part_path);
              }
            }
          }
        }
      }
      break;
    }

    case MimeMultipartPartFirstLine:
      /* Hand this line off to the sub-part. */
      status = (((MimeMultipartClass *) obj->clazz)->parse_child_line(obj,
                                                  line, length, PR_TRUE));
      if (status < 0) return status;
      mult->state = MimeMultipartPartLine;
      break;

    case MimeMultipartPartLine:
      /* Hand this line off to the sub-part. */
      status = (((MimeMultipartClass *) obj->clazz)->parse_child_line(obj,
                  line, length, PR_FALSE));
      if (status < 0) return status;
      break;

    case MimeMultipartSkipPartLine:
      /* we are skipping that part, therefore just ignore the line */
      break;

    default:
      NS_ERROR("unexpected state in parse line");
      return -1;
  }

  if (obj->options &&
      obj->options->format_out == nsMimeOutput::nsMimeMessageAttach &&
      (!(obj->options->state && obj->options->state->strippingPart) &&
      mult->state != MimeMultipartPartLine))
      return MimeObject_write(obj, line, length, PR_FALSE);
  return 0;
}