mailnews/mime/src/mimemoz2.cpp
author David Bienvenu <bienvenu@nventure.com>
Sun, 14 Sep 2008 08:23:21 -0700
changeset 327 98654f6296b5803008235fa5a696b81bde83ba04
parent 23 3955ec5709a6b14ca7a6241d71884f6d68a5dc2f
child 753 ea049a1644bdc5225e35393423a5f0e5bdff930d
permissions -rw-r--r--
fix 454344 nsIMimeEmitter::addAllHeaders not js safe, r=asuth, sr=neil

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is mozilla.org code.
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1998
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Pierre Phaneuf <pp@ludusdesign.com>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either of the GNU General Public License Version 2 or later (the "GPL"),
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */
#include "prlog.h"
#include "nsCOMPtr.h"
#include "modlmime.h"
#include "mimeobj.h"
#include "mimemsg.h"
#include "mimetric.h"   /* for MIME_RichtextConverter */
#include "mimethtm.h"
#include "mimemsig.h"
#include "mimemrel.h"
#include "mimemalt.h"
#include "mimebuf.h"
#include "mimemapl.h"
#include "prprf.h"
#include "mimei.h"      /* for moved MimeDisplayData struct */
#include "mimebuf.h"
#include "prmem.h"
#include "plstr.h"
#include "prmem.h"
#include "mimemoz2.h"
#include "nsIPrefService.h"
#include "nsIPrefBranch.h"
#include "nsIServiceManager.h"
#include "comi18n.h"
#include "nsIStringBundle.h"
#include "nsStringGlue.h"
#include "nsMimeStringResources.h"
#include "nsStreamConverter.h"
#include "nsIMsgSend.h"
#include "nsIMsgMailNewsUrl.h"
#include "mozITXTToHTMLConv.h"
#include "nsCExternalHandlerService.h"
#include "nsIMIMEService.h"
#include "nsIImapUrl.h"
#include "nsMsgI18N.h"
#include "nsICharsetConverterManager.h"
#include "nsICharsetAlias.h"
#include "nsMimeTypes.h"
#include "nsIIOService.h"
#include "nsIURI.h"
#include "nsNetCID.h"
#include "nsIMsgWindow.h"
#include "nsMsgUtils.h"
#include "nsIChannel.h"
#include "nsICacheEntryDescriptor.h"
#include "nsICacheSession.h"
#include "nsITransport.h"
#include "mimeebod.h"
#include "mimeeobj.h"
// <for functions="HTML2Plaintext,HTMLSantinize">
#include "nsXPCOM.h"
#include "nsParserCIID.h"
#include "nsIParser.h"
#include "nsIHTMLContentSink.h"
#include "nsIContentSerializer.h"
#include "nsLayoutCID.h"
#include "nsIComponentManager.h"
#include "nsIHTMLToTextSink.h"
#include "mozISanitizingSerializer.h"
// </for>

// <for functions="HTML2Plaintext,HTMLSantinize">
static NS_DEFINE_CID(kParserCID, NS_PARSER_CID);
// </for>

#ifdef HAVE_MIME_DATA_SLOT
#define LOCK_LAST_CACHED_MESSAGE
#endif

void                 ValidateRealName(nsMsgAttachmentData *aAttach, MimeHeaders *aHdrs);

static MimeHeadersState MIME_HeaderType;
static PRBool MIME_WrapLongLines;
static PRBool MIME_VariableWidthPlaintext;

// For string bundle access routines...
static nsCOMPtr<nsIStringBundle>   stringBundle = nsnull;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Attachment handling routines
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
MimeObject    *mime_get_main_object(MimeObject* obj);

nsresult
ProcessBodyAsAttachment(MimeObject *obj, nsMsgAttachmentData **data)
{
  nsMsgAttachmentData   *tmp;
  PRInt32               n;
  char                  *disp = nsnull;
  char                  *charset = nsnull;

  // Ok, this is the special case when somebody sends an "attachment" as the body
  // of an RFC822 message...I really don't think this is the way this should be done.
  // I belive this should really be a multipart/mixed message with an empty body part,
  // but what can ya do...our friends to the North seem to do this.
  //
  MimeObject    *child = obj;

  n = 1;
  *data = (nsMsgAttachmentData *)PR_Malloc( (n + 1) * sizeof(nsMsgAttachmentData));
  if (!*data)
    return NS_ERROR_OUT_OF_MEMORY;

  tmp = *data;
  memset(*data, 0, (n + 1) * sizeof(nsMsgAttachmentData));
  tmp->real_type = child->content_type ? strdup(child->content_type) : NULL;
  tmp->real_encoding = child->encoding ? strdup(child->encoding) : NULL;
  disp = MimeHeaders_get(child->headers, HEADER_CONTENT_DISPOSITION, PR_FALSE, PR_FALSE);
  tmp->real_name = MimeHeaders_get_parameter(disp, "name", &charset, NULL);
  if (tmp->real_name)
  {
    char *fname = NULL;
    fname = mime_decode_filename(tmp->real_name, charset, obj->options);
    nsMemory::Free(charset);
    if (fname && fname != tmp->real_name)
    {
      PR_Free(tmp->real_name);
      tmp->real_name = fname;
    }
  }
  else
  {
    tmp->real_name = MimeHeaders_get_name(child->headers, obj->options);
  }

  if ( (!tmp->real_name) && (tmp->real_type) && (PL_strncasecmp(tmp->real_type, "text", 4)) )
    ValidateRealName(tmp, child->headers);

  char  *tmpURL = nsnull;
  char  *id = nsnull;
  char  *id_imap = nsnull;

  id = mime_part_address (obj);
  if (obj->options->missing_parts)
    id_imap = mime_imap_part_address (obj);

  if (! id)
  {
    PR_FREEIF(*data);
    PR_FREEIF(id_imap);
    return NS_ERROR_OUT_OF_MEMORY;
  }

  if (obj->options && obj->options->url)
  {
    const char  *url = obj->options->url;
    nsresult    rv;
    if (id_imap && id)
    {
      // if this is an IMAP part.
      tmpURL = mime_set_url_imap_part(url, id_imap, id);
      rv = nsMimeNewURI(&(tmp->url), tmpURL, nsnull);
    }
    else
    {
      // This is just a normal MIME part as usual.
      tmpURL = mime_set_url_part(url, id, PR_TRUE);
      rv = nsMimeNewURI(&(tmp->url), tmpURL, nsnull);
    }

    if ( (!tmp->url) || (NS_FAILED(rv)) )
    {
      PR_FREEIF(*data);
      PR_FREEIF(id);
      PR_FREEIF(id_imap);
      return NS_ERROR_OUT_OF_MEMORY;
    }
  }
  PR_FREEIF(id);
  PR_FREEIF(id_imap);
  PR_FREEIF(tmpURL);
  tmp->description = MimeHeaders_get(child->headers, HEADER_CONTENT_DESCRIPTION, PR_FALSE, PR_FALSE);
  return NS_OK;
}

PRInt32
CountTotalMimeAttachments(MimeContainer *aObj)
{
  PRInt32     i;
  PRInt32     rc = 0;

  if ( (!aObj) || (!aObj->children) || (aObj->nchildren <= 0) )
    return 0;

  if (mime_typep((MimeObject *)aObj, (MimeObjectClass *)&mimeExternalBodyClass))
    return 0;

  for (i=0; i<aObj->nchildren; i++)
    rc += CountTotalMimeAttachments((MimeContainer *)aObj->children[i]) + 1;

  return rc;
}

void
ValidateRealName(nsMsgAttachmentData *aAttach, MimeHeaders *aHdrs)
{
  // Sanity.
  if (!aAttach)
    return;

  // Do we need to validate?
  if ( (aAttach->real_name) && (*(aAttach->real_name)) )
    return;

  // Internal MIME structures need not be named!
  if ( (!aAttach->real_type) || (aAttach->real_type &&
                                 !PL_strncasecmp(aAttach->real_type, "multipart", 9)) )
    return;

  // Special case...if this is a enclosed RFC822 message, give it a nice
  // name.
  if (aAttach->real_type && !PL_strcasecmp(aAttach->real_type, MESSAGE_RFC822))
  {
    NS_ASSERTION(aHdrs, "How comes the object's headers is null!");
    if (aHdrs && aHdrs->munged_subject)
      aAttach->real_name = PR_smprintf("%s.eml", aHdrs->munged_subject);
    else
      NS_MsgSACopy(&(aAttach->real_name), "ForwardedMessage.eml");
    return;
  }

  //
  // Now validate any other name we have for the attachment!
  //
  if (!aAttach->real_name || *aAttach->real_name == 0)
  {
    nsCString newAttachName(NS_LITERAL_CSTRING("attachment"));
    nsresult rv = NS_OK;
    nsCAutoString contentType (aAttach->real_type);
    PRInt32 pos = contentType.FindChar(';');
    if (pos > 0)
      contentType.SetLength(pos);

    nsCOMPtr<nsIMIMEService> mimeFinder (do_GetService(NS_MIMESERVICE_CONTRACTID, &rv));
    if (NS_SUCCEEDED(rv))
    {
      nsCAutoString fileExtension;
      rv = mimeFinder->GetPrimaryExtension(contentType, EmptyCString(), fileExtension);

      if (NS_SUCCEEDED(rv) && !fileExtension.IsEmpty())
      {
        newAttachName.Append('.');
        newAttachName.Append(fileExtension);
      }
    }

    aAttach->real_name = ToNewCString(newAttachName);
  }
}

static  PRInt32     attIndex = 0;

nsresult
GenerateAttachmentData(MimeObject *object, const char *aMessageURL, MimeDisplayOptions *options,
                       PRBool isAnAppleDoublePart, nsMsgAttachmentData *aAttachData)
{
  nsCString imappart;
  nsCString part;
  PRBool isIMAPPart;
  PRBool isExternalAttachment = PR_FALSE;

  /* be sure the object has not be marked as Not to be an attachment */
  if (object->dontShowAsAttachment)
    return NS_OK;

  part.Adopt(mime_part_address(object));
  if (part.IsEmpty())
    return NS_ERROR_OUT_OF_MEMORY;

  if (options->missing_parts)
    imappart.Adopt(mime_imap_part_address(object));

  char *urlSpec = nsnull;
  if (!imappart.IsEmpty())
  {
    isIMAPPart = PR_TRUE;
    urlSpec = mime_set_url_imap_part(aMessageURL, imappart.get(), part.get());
  }
  else
  {
    isIMAPPart = PR_FALSE;
    char *no_part_url = nsnull;
    if (options->part_to_load && options->format_out == nsMimeOutput::nsMimeMessageBodyDisplay)
      no_part_url = mime_get_base_url(aMessageURL);
    if (no_part_url) {
      urlSpec = mime_set_url_part(no_part_url, part.get(), PR_TRUE);
      PR_Free(no_part_url);
    }
    else
    {
      // if the mime object contains an external attachment URL, then use it, otherwise
      // fall back to creating an attachment url based on the message URI and the
      // part number.
      urlSpec = mime_external_attachment_url(object);
      isExternalAttachment = urlSpec ? PR_TRUE : PR_FALSE;
      if (!urlSpec)
        urlSpec = mime_set_url_part(aMessageURL, part.get(), PR_TRUE);
    }
  }

  if (!urlSpec)
    return NS_ERROR_OUT_OF_MEMORY;

  if ((options->format_out == nsMimeOutput::nsMimeMessageBodyDisplay) && (PL_strncasecmp(aMessageURL, urlSpec, strlen(urlSpec)) == 0))
    return NS_OK;

  nsMsgAttachmentData *tmp = &(aAttachData[attIndex++]);

  tmp->real_type = object->content_type ? strdup(object->content_type) : nsnull;
  tmp->real_encoding = object->encoding ? strdup(object->encoding) : nsnull;
  tmp->isExternalAttachment = isExternalAttachment;

  PRInt32 i;
  char *charset = nsnull;
  char *disp = MimeHeaders_get(object->headers, HEADER_CONTENT_DISPOSITION, PR_FALSE, PR_FALSE);
  if (disp)
  {
    tmp->real_name = MimeHeaders_get_parameter(disp, "filename", &charset, nsnull);
    if (isAnAppleDoublePart)
      for (i = 0; i < 2 && !tmp->real_name; i ++)
      {
        PR_FREEIF(disp);
        nsMemory::Free(charset);
        disp = MimeHeaders_get(((MimeContainer *)object)->children[i]->headers, HEADER_CONTENT_DISPOSITION, PR_FALSE, PR_FALSE);
        tmp->real_name = MimeHeaders_get_parameter(disp, "filename", &charset, nsnull);
      }

    if (tmp->real_name)
    {
      // check encoded type
      //
      // The parameter of Content-Disposition must use RFC 2231.
      // But old Netscape 4.x and Outlook Express etc. use RFC2047.
      // So we should parse both types.

      char *fname = nsnull;
      fname = mime_decode_filename(tmp->real_name, charset, options);
      nsMemory::Free(charset);

      if (fname && fname != tmp->real_name)
      {
        PR_FREEIF(tmp->real_name);
        tmp->real_name = fname;
      }
    }

    PR_FREEIF(disp);
  }

  disp = MimeHeaders_get(object->headers, HEADER_CONTENT_TYPE, PR_FALSE, PR_FALSE);
  if (disp)
  {
    tmp->x_mac_type   = MimeHeaders_get_parameter(disp, PARAM_X_MAC_TYPE, nsnull, nsnull);
    tmp->x_mac_creator= MimeHeaders_get_parameter(disp, PARAM_X_MAC_CREATOR, nsnull, nsnull);

    if (!tmp->real_name || *tmp->real_name == 0)
    {
      PR_FREEIF(tmp->real_name);
      tmp->real_name = MimeHeaders_get_parameter(disp, "name", &charset, nsnull);
      if (isAnAppleDoublePart)
        // the data fork is the 2nd part, and we should ALWAYS look there first for the file name
        for (i = 1; i >= 0 && !tmp->real_name; i --)
        {
          PR_FREEIF(disp);
          nsMemory::Free(charset);
          disp = MimeHeaders_get(((MimeContainer *)object)->children[i]->headers, HEADER_CONTENT_TYPE, PR_FALSE, PR_FALSE);
          tmp->real_name = MimeHeaders_get_parameter(disp, "name", &charset, nsnull);
        }

      if (tmp->real_name)
      {
        // check encoded type
        //
        // The parameter of Content-Disposition must use RFC 2231.
        // But old Netscape 4.x and Outlook Express etc. use RFC2047.
        // So we should parse both types.

        char *fname = nsnull;
        fname = mime_decode_filename(tmp->real_name, charset, options);
        nsMemory::Free(charset);

        if (fname && fname != tmp->real_name)
        {
          PR_Free(tmp->real_name);
          tmp->real_name = fname;
        }
      }
    }
    PR_FREEIF(disp);
  }

  tmp->description = MimeHeaders_get(object->headers, HEADER_CONTENT_DESCRIPTION,
                                     PR_FALSE, PR_FALSE);

  // Now, do the right thing with the name!
  if (!tmp->real_name && PL_strcasecmp(tmp->real_type, MESSAGE_RFC822))
  {
    /* If this attachment doesn't have a name, just give it one... */
    tmp->real_name = MimeGetStringByID(MIME_MSG_DEFAULT_ATTACHMENT_NAME);
    if (tmp->real_name)
    {
      char *newName = PR_smprintf(tmp->real_name, part.get());
      if (newName)
      {
        PR_Free(tmp->real_name);
        tmp->real_name = newName;
      }
    }
    else
      tmp->real_name = mime_part_address(object);
  }
  nsCString urlString(urlSpec);

  if (tmp->real_name && !tmp->isExternalAttachment)
  {
    urlString.Append("&filename=");
    urlString.Append(tmp->real_name);
#ifdef MOZILLA_INTERNAL_API
    if (tmp->real_type && !strcmp(tmp->real_type, "message/rfc822") &&
           !StringEndsWith(urlString, NS_LITERAL_CSTRING(".eml"), nsCaseInsensitiveCStringComparator()))
#else
    if (tmp->real_type && !strcmp(tmp->real_type, "message/rfc822") &&
           !StringEndsWith(urlString, NS_LITERAL_CSTRING(".eml"), CaseInsensitiveCompare))
#endif
      urlString.Append(".eml");
  }
  nsresult rv = nsMimeNewURI(&(tmp->url), urlString.get(), nsnull);

  PR_FREEIF(urlSpec);

  if ( (NS_FAILED(rv)) || (!tmp->url) )
    return NS_ERROR_OUT_OF_MEMORY;

  ValidateRealName(tmp, object->headers);

  return NS_OK;
}

nsresult
BuildAttachmentList(MimeObject *anObject, nsMsgAttachmentData *aAttachData, const char *aMessageURL)
{
  nsresult              rv;
  PRInt32               i;
  MimeContainer         *cobj = (MimeContainer *) anObject;

  if ( (!anObject) || (!cobj->children) || (!cobj->nchildren) ||
       (mime_typep(anObject, (MimeObjectClass *)&mimeExternalBodyClass)))
    return NS_OK;

  for (i = 0; i < cobj->nchildren ; i++)
  {
    MimeObject    *child = cobj->children[i];

    // Skip the first child if it's in fact a message body
    if (i == 0)                                         // it's the first child
      if (child->content_type)                          // and it's content-type is one of folowing...
        if (!PL_strcasecmp (child->content_type, TEXT_PLAIN) ||
            !PL_strcasecmp (child->content_type, TEXT_HTML) ||
            !PL_strcasecmp (child->content_type, TEXT_MDL))
        {
          if (child->headers) // and finally, be sure it doesn't have a content-disposition: attachment
          {
            char * disp = MimeHeaders_get (child->headers, HEADER_CONTENT_DISPOSITION, PR_TRUE, PR_FALSE);
            if (!disp || PL_strcasecmp (disp, "attachment"))
              continue;
          }
          else
            continue;
        }


    // We should generate an attachment for leaf object only but...
    PRBool isALeafObject = mime_subclass_p(child->clazz, (MimeObjectClass *) &mimeLeafClass);

    // ...we will generate an attachment for inline message too.
    PRBool isAnInlineMessage = mime_typep(child, (MimeObjectClass *) &mimeMessageClass);

    // AppleDouble part need special care: we need to fetch the part as well its two
    // children for the needed info as they could be anywhere, eventually, they won't contain
    // a name or file name. In any case we need to build only one attachment data
    PRBool isAnAppleDoublePart = mime_typep(child, (MimeObjectClass *) &mimeMultipartAppleDoubleClass) &&
                                 ((MimeContainer *)child)->nchildren == 2;

    if (isALeafObject || isAnInlineMessage || isAnAppleDoublePart)
    {
      rv = GenerateAttachmentData(child, aMessageURL, anObject->options, isAnAppleDoublePart, aAttachData);
      NS_ENSURE_SUCCESS(rv, rv);
    }

    // Now build the attachment list for the children of our object...
    if (!isALeafObject && !isAnAppleDoublePart)
    {
      rv = BuildAttachmentList((MimeObject *)child, aAttachData, aMessageURL);
      NS_ENSURE_SUCCESS(rv, rv);
    }
  }

  return NS_OK;

}

extern "C" nsresult
MimeGetAttachmentList(MimeObject *tobj, const char *aMessageURL, nsMsgAttachmentData **data)
{
  MimeObject            *obj;
  MimeContainer         *cobj;
  PRInt32               n;
  PRBool                isAnInlineMessage;

  if (!data)
    return 0;
  *data = nsnull;

  obj = mime_get_main_object(tobj);
  if (!obj)
    return 0;

  if (!mime_subclass_p(obj->clazz, (MimeObjectClass*) &mimeContainerClass))
  {
    if (!PL_strcasecmp(obj->content_type, MESSAGE_RFC822))
      return 0;
    else
      return ProcessBodyAsAttachment(obj, data);
  }

  isAnInlineMessage = mime_typep(obj, (MimeObjectClass *) &mimeMessageClass);

  cobj = (MimeContainer*) obj;
  n = CountTotalMimeAttachments(cobj);
  if (n <= 0)
    return n;

  //in case of an inline message (as body), we need an extra slot for the message itself
  //that we will fill later...
  if (isAnInlineMessage)
    n ++;

  *data = (nsMsgAttachmentData *)PR_Malloc( (n + 1) * sizeof(nsMsgAttachmentData));
  if (!*data)
    return NS_ERROR_OUT_OF_MEMORY;

  attIndex = 0;
  memset(*data, 0, (n + 1) * sizeof(nsMsgAttachmentData));

  // Now, build the list!

  nsresult rv;

  if (isAnInlineMessage)
  {
    rv = GenerateAttachmentData(obj, aMessageURL, obj->options, PR_FALSE, *data);
    NS_ENSURE_SUCCESS(rv, rv);
  }
  return BuildAttachmentList((MimeObject *) cobj, *data, aMessageURL);
}

extern "C" void
MimeFreeAttachmentList(nsMsgAttachmentData *data)
{
  if (data)
  {
    nsMsgAttachmentData   *tmp;
    for (tmp = data ; tmp->url ; tmp++)
    {
      /* Can't do PR_FREEIF on `const' values... */
      NS_IF_RELEASE(tmp->url);
      if (tmp->real_type) PR_Free((char *) tmp->real_type);
      if (tmp->real_encoding) PR_Free((char *) tmp->real_encoding);
      if (tmp->real_name) PR_Free((char *) tmp->real_name);
      if (tmp->x_mac_type) PR_Free((char *) tmp->x_mac_type);
      if (tmp->x_mac_creator) PR_Free((char *) tmp->x_mac_creator);
      if (tmp->description) PR_Free((char *) tmp->description);
      tmp->url = 0;
      tmp->real_type = 0;
      tmp->real_name = 0;
      tmp->description = 0;
    }
    PR_Free(data);
  }
}

extern "C" void
NotifyEmittersOfAttachmentList(MimeDisplayOptions     *opt,
                               nsMsgAttachmentData    *data)
{
  PRInt32     i = 0;
  struct      nsMsgAttachmentData  *tmp = data;

  if (!tmp)
    return;

  while (tmp->url)
  {
    if (!tmp->real_name)
    {
      ++i;
      ++tmp;
      continue;
    }

    nsCAutoString spec;
    if ( tmp->url )
      tmp->url->GetSpec(spec);

    mimeEmitterStartAttachment(opt, tmp->real_name, tmp->real_type, spec.get(), tmp->isExternalAttachment);
    mimeEmitterAddAttachmentField(opt, HEADER_X_MOZILLA_PART_URL, spec.get());

    if ( (opt->format_out == nsMimeOutput::nsMimeMessageQuoting) ||
         (opt->format_out == nsMimeOutput::nsMimeMessageBodyQuoting) ||
         (opt->format_out == nsMimeOutput::nsMimeMessageSaveAs) ||
         (opt->format_out == nsMimeOutput::nsMimeMessagePrintOutput))
    {
      mimeEmitterAddAttachmentField(opt, HEADER_CONTENT_DESCRIPTION, tmp->description);
      mimeEmitterAddAttachmentField(opt, HEADER_CONTENT_TYPE, tmp->real_type);
      mimeEmitterAddAttachmentField(opt, HEADER_CONTENT_ENCODING,    tmp->real_encoding);

      /* rhp - for now, just leave these here, but they are really
               not necessary
      printf("URL for Part      : %s\n", spec);
      printf("Real Name         : %s\n", tmp->real_name);
      printf("Desired Type      : %s\n", tmp->desired_type);
      printf("Real Type         : %s\n", tmp->real_type);
      printf("Real Encoding     : %s\n", tmp->real_encoding);
      printf("Description       : %s\n", tmp->description);
      printf("Mac Type          : %s\n", tmp->x_mac_type);
      printf("Mac Creator       : %s\n", tmp->x_mac_creator);
      */
    }

    mimeEmitterEndAttachment(opt);
    ++i;
    ++tmp;
  }
  mimeEmitterEndAllAttachments(opt);
}

// Utility to create a nsIURI object...
extern "C" nsresult
nsMimeNewURI(nsIURI** aInstancePtrResult, const char *aSpec, nsIURI *aBase)
{
  nsresult  res;

  if (nsnull == aInstancePtrResult)
    return NS_ERROR_NULL_POINTER;

  nsCOMPtr<nsIIOService> pService(do_GetService(NS_IOSERVICE_CONTRACTID, &res));
  if (NS_FAILED(res))
    return NS_ERROR_FACTORY_NOT_REGISTERED;

  return pService->NewURI(nsDependentCString(aSpec), nsnull, aBase, aInstancePtrResult);
}

extern "C" nsresult
SetMailCharacterSetToMsgWindow(MimeObject *obj, const char *aCharacterSet)
{
  nsresult rv = NS_OK;

  if (obj && obj->options)
  {
    mime_stream_data *msd = (mime_stream_data *) (obj->options->stream_closure);
    if (msd)
    {
      nsIChannel *channel = msd->channel;
      if (channel)
      {
        nsCOMPtr<nsIURI> uri;
        channel->GetURI(getter_AddRefs(uri));
        if (uri)
        {
          nsCOMPtr<nsIMsgMailNewsUrl> msgurl (do_QueryInterface(uri));
          if (msgurl)
          {
            nsCOMPtr<nsIMsgWindow> msgWindow;
            msgurl->GetMsgWindow(getter_AddRefs(msgWindow));
            if (msgWindow)
              rv = msgWindow->SetMailCharacterSet(!PL_strcasecmp(aCharacterSet, "us-ascii") ?
                                                  NS_LITERAL_CSTRING("ISO-8859-1") :
                                                  nsDependentCString(aCharacterSet));
          }
        }
      }
    }
  }

  return rv;
}

static char *
mime_file_type (const char *filename, void *stream_closure)
{
  char        *retType = nsnull;
  char        *ext = nsnull;
  nsresult    rv;

  ext = PL_strrchr(filename, '.');
  if (ext)
  {
    ext++;
    nsCOMPtr<nsIMIMEService> mimeFinder (do_GetService(NS_MIMESERVICE_CONTRACTID, &rv));
    if (mimeFinder) {
      nsCAutoString type;
      mimeFinder->GetTypeFromExtension(nsDependentCString(ext), type);
      retType = ToNewCString(type);
    }
  }

  return retType;
}

int ConvertUsingEncoderAndDecoder(const char *stringToUse, PRInt32 inLength,
                                  nsIUnicodeEncoder *encoder, nsIUnicodeDecoder *decoder,
                                  char **pConvertedString, PRInt32 *outLength)
{
  // buffer size 144 =
  // 72 (default line len for compose)
  // times 2 (converted byte len might be larger)
  const int klocalbufsize = 144;
  // do the conversion
  PRUnichar *unichars;
  PRInt32 unicharLength;
  PRInt32 srcLen = inLength;
  PRInt32 dstLength = 0;
  char *dstPtr;
  nsresult rv;

  // use this local buffer if possible
  PRUnichar localbuf[klocalbufsize+1];
  if (inLength > klocalbufsize) {
    rv = decoder->GetMaxLength(stringToUse, srcLen, &unicharLength);
    // allocate temporary buffer to hold unicode string
    unichars = new PRUnichar[unicharLength];
  }
  else {
    unichars = localbuf;
    unicharLength = klocalbufsize+1;
  }
  if (unichars == nsnull) {
    rv = NS_ERROR_OUT_OF_MEMORY;
  }
  else {
    // convert to unicode, replacing failed chars with 0xFFFD as in
    // the methode used in nsXMLHttpRequest::ConvertBodyToText and nsScanner::Append
    //
    // We will need several pass to convert the whole string if it has invalid characters
    // 'totalChars' is where the sum of the number of converted characters will be done
    // 'dataLen' is the number of character left to convert
    // 'outLen' is the number of characters still available in the output buffer as input of decoder->Convert
    // and the number of characters written in it as output.
    PRInt32 totalChars = 0,
            inBufferIndex = 0,
            outBufferIndex = 0;
    PRInt32 dataLen = srcLen,
            outLen = unicharLength;

    do {
      PRInt32 inBufferLength = dataLen;
      rv = decoder->Convert(&stringToUse[inBufferIndex],
                           &inBufferLength,
                           &unichars[outBufferIndex],
                           &outLen);
      totalChars += outLen;
      // Done if conversion successful
      if (NS_SUCCEEDED(rv))
          break;

      // We consume one byte, replace it with U+FFFD
      // and try the conversion again.
      outBufferIndex += outLen;
      unichars[outBufferIndex++] = PRUnichar(0xFFFD);
      // totalChars is updated here
      outLen = unicharLength - (++totalChars);

      inBufferIndex += inBufferLength + 1;
      dataLen -= inBufferLength + 1;

      decoder->Reset();

      // If there is not at least one byte available after the one we
      // consumed, we're done
    } while ( dataLen > 0 );

    rv = encoder->GetMaxLength(unichars, totalChars, &dstLength);
    // allocale an output buffer
    dstPtr = (char *) PR_Malloc(dstLength + 1);
    if (dstPtr == nsnull) {
      rv = NS_ERROR_OUT_OF_MEMORY;
    }
    else {
      PRInt32 buffLength = dstLength;
      // convert from unicode
      rv = encoder->SetOutputErrorBehavior(nsIUnicodeEncoder::kOnError_Replace, nsnull, '?');
      if (NS_SUCCEEDED(rv)) {
        rv = encoder->Convert(unichars, &totalChars, dstPtr, &dstLength);
        if (NS_SUCCEEDED(rv)) {
          PRInt32 finLen = buffLength - dstLength;
          rv = encoder->Finish((char *)(dstPtr+dstLength), &finLen);
          if (NS_SUCCEEDED(rv)) {
            dstLength += finLen;
          }
          dstPtr[dstLength] = '\0';
          *pConvertedString = dstPtr;       // set the result string
          *outLength = dstLength;
        }
      }
    }
    if (inLength > klocalbufsize)
      delete [] unichars;
  }

  return NS_SUCCEEDED(rv) ? 0 : -1;
}


static int
mime_convert_charset (const char *input_line, PRInt32 input_length,
                      const char *input_charset, const char *output_charset,
                      char **output_ret, PRInt32 *output_size_ret,
                      void *stream_closure, nsIUnicodeDecoder *decoder, nsIUnicodeEncoder *encoder)
{
  PRInt32 res = -1;
  char  *convertedString = NULL;
  PRInt32 convertedStringLen = 0;
  if (encoder && decoder)
  {
    res = ConvertUsingEncoderAndDecoder(input_line, input_length, encoder, decoder, &convertedString, &convertedStringLen);
  }
  if (res != 0)
  {
      *output_ret = 0;
      *output_size_ret = 0;
  }
  else
  {
    *output_ret = (char *) convertedString;
    *output_size_ret = convertedStringLen;
  }

  return 0;
}

static int
mime_output_fn(const char *buf, PRInt32 size, void *stream_closure)
{
  PRUint32  written = 0;
  struct mime_stream_data *msd = (struct mime_stream_data *) stream_closure;
  if ( (!msd->pluginObj2) && (!msd->output_emitter) )
    return -1;

  // Fire pending start request
  ((nsStreamConverter*)msd->pluginObj2)->FirePendingStartRequest();


  // Now, write to the WriteBody method if this is a message body and not
  // a part retrevial
  if (!msd->options->part_to_load || msd->options->format_out == nsMimeOutput::nsMimeMessageBodyDisplay)
  {
    if (msd->output_emitter)
    {
      msd->output_emitter->WriteBody(buf, (PRUint32) size, &written);
    }
  }
  else
  {
    if (msd->output_emitter)
    {
      msd->output_emitter->Write(buf, (PRUint32) size, &written);
    }
  }
  return written;
}

extern "C" int
mime_display_stream_write (nsMIMESession *stream,
                           const char* buf,
                           PRInt32 size)
{
  struct mime_stream_data *msd = (struct mime_stream_data *) ((nsMIMESession *)stream)->data_object;

  MimeObject *obj = (msd ? msd->obj : 0);
  if (!obj) return -1;

  //
  // Ok, now check to see if this is a display operation for a MIME Parts on Demand
  // enabled call.
  //
  if (msd->firstCheck)
  {
    if (msd->channel)
    {
      nsCOMPtr<nsIURI> aUri;
      if (NS_SUCCEEDED(msd->channel->GetURI(getter_AddRefs(aUri))))
      {
        nsCOMPtr<nsIImapUrl> imapURL = do_QueryInterface(aUri);
        if (imapURL)
        {
          nsImapContentModifiedType   cModified;
          if (NS_SUCCEEDED(imapURL->GetContentModified(&cModified)))
          {
            if ( cModified != nsImapContentModifiedTypes::IMAP_CONTENT_NOT_MODIFIED )
              msd->options->missing_parts = PR_TRUE;
          }
        }
      }
    }

    msd->firstCheck = PR_FALSE;
  }

  return obj->clazz->parse_buffer((char *) buf, size, obj);
}

extern "C" void
mime_display_stream_complete (nsMIMESession *stream)
{
  struct mime_stream_data *msd = (struct mime_stream_data *) ((nsMIMESession *)stream)->data_object;
  MimeObject *obj = (msd ? msd->obj : 0);
  if (obj)
  {
    int       status;
    PRBool    abortNow = PR_FALSE;

    if ((obj->options) && (obj->options->headers == MimeHeadersOnly))
      abortNow = PR_TRUE;

    status = obj->clazz->parse_eof(obj, abortNow);
    obj->clazz->parse_end(obj, (status < 0 ? PR_TRUE : PR_FALSE));

    //
    // Ok, now we are going to process the attachment data by getting all
    // of the attachment info and then driving the emitter with this data.
    //
    if (!msd->options->part_to_load || msd->options->format_out == nsMimeOutput::nsMimeMessageBodyDisplay)
    {
      nsMsgAttachmentData *attachments;
      nsresult rv = MimeGetAttachmentList(obj, msd->url_name, &attachments);
      if (NS_SUCCEEDED(rv))
      {
        NotifyEmittersOfAttachmentList(msd->options, attachments);
        MimeFreeAttachmentList(attachments);
      }
    }

    // Release the conversion object - this has to be done after
    // we finish processing data.
    if ( obj->options)
    {
      NS_IF_RELEASE(obj->options->conv);
    }

    // Destroy the object now.
    PR_ASSERT(msd->options == obj->options);
    mime_free(obj);
    obj = NULL;
    if (msd->options)
    {
      delete msd->options;
      msd->options = 0;
    }
  }

  if (msd->headers)
    MimeHeaders_free (msd->headers);

  if (msd->url_name)
    NS_Free(msd->url_name);

  if (msd->orig_url_name)
      NS_Free(msd->orig_url_name);

  PR_FREEIF(msd);
}

extern "C" void
mime_display_stream_abort (nsMIMESession *stream, int status)
{
  struct mime_stream_data *msd = (struct mime_stream_data *) ((nsMIMESession *)stream)->data_object;

  MimeObject *obj = (msd ? msd->obj : 0);
  if (obj)
  {
    if (!obj->closed_p)
      obj->clazz->parse_eof(obj, PR_TRUE);
    if (!obj->parsed_p)
      obj->clazz->parse_end(obj, PR_TRUE);

    // Destroy code....
    PR_ASSERT(msd->options == obj->options);
    mime_free(obj);
    if (msd->options)
    {
      delete msd->options;
      msd->options = 0;
    }
  }

  if (msd->headers)
    MimeHeaders_free (msd->headers);

  if (msd->url_name)
    NS_Free(msd->url_name);

  if (msd->orig_url_name)
      NS_Free(msd->orig_url_name);

  PR_FREEIF(msd);
}

static int
mime_output_init_fn (const char *type,
                     const char *charset,
                     const char *name,
                     const char *x_mac_type,
                     const char *x_mac_creator,
                     void *stream_closure)
{
  struct mime_stream_data *msd = (struct mime_stream_data *) stream_closure;

  // Now, all of this stream creation is done outside of libmime, so this
  // is just a check of the pluginObj member and returning accordingly.
  if (!msd->pluginObj2)
    return -1;
  else
    return 0;
}

static void   *mime_image_begin(const char *image_url, const char *content_type,
                              void *stream_closure);
static void   mime_image_end(void *image_closure, int status);
static char   *mime_image_make_image_html(void *image_data);
static int    mime_image_write_buffer(const char *buf, PRInt32 size, void *image_closure);

/* Interface between libmime and inline display of images: the abomination
   that is known as "internal-external-reconnect".
 */
class mime_image_stream_data {
public:
  mime_image_stream_data();

  struct mime_stream_data *msd;
  char                    *url;
  nsMIMESession           *istream;
  nsCOMPtr<nsIOutputStream> memCacheOutputStream;
  PRBool m_shouldCacheImage;
};

mime_image_stream_data::mime_image_stream_data()
{
  url = nsnull;
  istream = nsnull;
  msd = nsnull;
  m_shouldCacheImage = PR_FALSE;
}

static void *
mime_image_begin(const char *image_url, const char *content_type,
                 void *stream_closure)
{
  struct mime_stream_data *msd = (struct mime_stream_data *) stream_closure;
  class mime_image_stream_data *mid;

  mid = new mime_image_stream_data;
  if (!mid) return nsnull;


  mid->msd = msd;

  mid->url = (char *) strdup(image_url);
  if (!mid->url)
  {
    PR_Free(mid);
    return nsnull;
  }

  if (msd->channel)
  {
    nsCOMPtr <nsIURI> uri;
    nsresult rv = msd->channel->GetURI(getter_AddRefs(uri));
    if (NS_SUCCEEDED(rv) && uri)
    {
      nsCOMPtr <nsIMsgMailNewsUrl> mailUrl = do_QueryInterface(uri);
      if (mailUrl)
      {
        nsCOMPtr<nsICacheSession> memCacheSession;
        mailUrl->GetImageCacheSession(getter_AddRefs(memCacheSession));
        if (memCacheSession)
        {
          nsCOMPtr<nsICacheEntryDescriptor> entry;

          // we may need to convert the image_url into just a part url - in any case,
          // it has to be the same as what imglib will be asking imap for later
          // on so that we'll find this in the memory cache.
          rv = memCacheSession->OpenCacheEntry(nsDependentCString(image_url), nsICache::ACCESS_READ_WRITE, nsICache::BLOCKING, getter_AddRefs(entry));
          nsCacheAccessMode access;
          if (entry)
          {
            entry->GetAccessGranted(&access);
#ifdef DEBUG_bienvenu
            printf("Mime opening cache entry for %s access = %ld\n", image_url, access);
#endif
            // if we only got write access, then we should fill in this cache entry
            if (access & nsICache::ACCESS_WRITE && !(access & nsICache::ACCESS_READ))
            {
              mailUrl->CacheCacheEntry(entry);
              entry->MarkValid();

              // remember the content type as meta data so we can pull it out in the imap code
              // to feed the cache entry directly to imglib w/o going through mime.
              entry->SetMetaDataElement("contentType", content_type);

              rv = entry->OpenOutputStream(0, getter_AddRefs(mid->memCacheOutputStream));
              if (NS_FAILED(rv)) return nsnull;
            }
          }
        }
      }
    }
  }
  mid->istream = (nsMIMESession *) msd->pluginObj2;
  return mid;
}

static void
mime_image_end(void *image_closure, int status)
{
  mime_image_stream_data *mid =
    (mime_image_stream_data *) image_closure;

  PR_ASSERT(mid);
  if (!mid)
    return;
  if (mid->memCacheOutputStream)
    mid->memCacheOutputStream->Close();

  PR_FREEIF(mid->url);
  delete mid;
}


static char *
mime_image_make_image_html(void *image_closure)
{
  mime_image_stream_data *mid =
    (mime_image_stream_data *) image_closure;

  const char *prefix;
  /* Wouldn't it be nice if attributes were case-sensitive? */
  const char *scaledPrefix = "<P><CENTER><IMG CLASS=\"moz-attached-image\" shrinktofit=\"yes\" SRC=\"";
  const char *unscaledPrefix = "<P><CENTER><IMG CLASS=\"moz-attached-image\" SRC=\"";
  const char *suffix = "\"></CENTER><P>";
  const char *url;
  char *buf;

  PR_ASSERT(mid);
  if (!mid) return 0;

  /* Internal-external-reconnect only works when going to the screen. */
  if (!mid->istream)
    return strdup("<P><CENTER><IMG SRC=\"resource://gre/res/loading-image.gif\" ALT=\"[Image]\"></CENTER><P>");

  nsCOMPtr<nsIPrefBranch> prefBranch;
  nsCOMPtr<nsIPrefService> prefSvc(do_GetService(NS_PREFSERVICE_CONTRACTID));
  PRBool resize = PR_TRUE;

  if (prefSvc)
    prefSvc->GetBranch("", getter_AddRefs(prefBranch));
  if (prefBranch)
    prefBranch->GetBoolPref("mail.enable_automatic_image_resizing", &resize); // ignore return value
  prefix = resize ? scaledPrefix : unscaledPrefix;

  if ( (!mid->url) || (!(*mid->url)) )
    url = "";
  else
    url = mid->url;

  PRUint32 buflen = strlen(prefix) + strlen(suffix) + strlen(url) + 20;
  buf = (char *) PR_MALLOC (buflen);
  if (!buf)
    return 0;
  *buf = 0;

  PL_strcatn (buf, buflen, prefix);
  PL_strcatn (buf, buflen, url);
  PL_strcatn (buf, buflen, suffix);
  return buf;
}

static int
mime_image_write_buffer(const char *buf, PRInt32 size, void *image_closure)
{
  mime_image_stream_data *mid =
                (mime_image_stream_data *) image_closure;
  struct mime_stream_data *msd = mid->msd;

  if ( ( (!msd->output_emitter) ) &&
       ( (!msd->pluginObj2)     ) )
    return -1;

  //
  // If we get here, we are just eating the data this time around
  // and the returned URL will deal with writing the data to the viewer.
  // Just return the size value to the caller.
  //
  if (mid->memCacheOutputStream)
  {
    PRUint32 bytesWritten;
    mid->memCacheOutputStream->Write(buf, size, &bytesWritten);
  }
  return size;
}

MimeObject*
mime_get_main_object(MimeObject* obj)
{
  MimeContainer *cobj;
  if (!(mime_subclass_p(obj->clazz, (MimeObjectClass*) &mimeMessageClass)))
  {
    return obj;
  }
  cobj = (MimeContainer*) obj;
  if (cobj->nchildren != 1) return obj;
  obj = cobj->children[0];
  while (obj)
  {
    if ( (!mime_subclass_p(obj->clazz,
         (MimeObjectClass*) &mimeMultipartSignedClass)) &&
         (PL_strcasecmp(obj->content_type, MULTIPART_SIGNED) != 0)
       )
    {
        return obj;
    }
    else
    {
      if (mime_subclass_p(obj->clazz, (MimeObjectClass*)&mimeContainerClass))
      {
        // We don't care about a signed/smime object; Go inside to the
        // thing that we signed or smime'ed
        //
        cobj = (MimeContainer*) obj;
        if (cobj->nchildren > 0)
          obj = cobj->children[0];
        else
          obj = nsnull;
      }
      else
      {
        // we received a message with a child object that looks like a signed
        // object, but it is not a subclass of mimeContainer, so let's
        // return the given child object.
        return obj;
      }
    }
  }
  return nsnull;
}

PRBool MimeObjectChildIsMessageBody(MimeObject *obj,
                   PRBool *isAlternativeOrRelated)
{
  char *disp = 0;
  PRBool bRet = PR_FALSE;
  MimeObject *firstChild = 0;
  MimeContainer *container = (MimeContainer*) obj;

  if (isAlternativeOrRelated)
    *isAlternativeOrRelated = PR_FALSE;

  if (!container ||
    !mime_subclass_p(obj->clazz,
             (MimeObjectClass*) &mimeContainerClass))
  {
    return bRet;
  }
  else if (mime_subclass_p(obj->clazz, (MimeObjectClass*)
               &mimeMultipartRelatedClass))
  {
    if (isAlternativeOrRelated)
      *isAlternativeOrRelated = PR_TRUE;
    return bRet;
  }
  else if (mime_subclass_p(obj->clazz, (MimeObjectClass*)
               &mimeMultipartAlternativeClass))
  {
    if (isAlternativeOrRelated)
      *isAlternativeOrRelated = PR_TRUE;
    return bRet;
  }

  if (container->children)
    firstChild = container->children[0];

  if (!firstChild ||
    !firstChild->content_type ||
    !firstChild->headers)
    return bRet;

  disp = MimeHeaders_get (firstChild->headers,
              HEADER_CONTENT_DISPOSITION,
              PR_TRUE,
              PR_FALSE);
  if (disp /* && !PL_strcasecmp (disp, "attachment") */)
    bRet = PR_FALSE;
  else 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))
    bRet = PR_TRUE;
  else
    bRet = PR_FALSE;
  PR_FREEIF(disp);
  return bRet;
}

//
// New Stream Converter Interface
//

// Get the connnection to prefs service manager
nsIPrefBranch *
GetPrefBranch(MimeDisplayOptions *opt)
{
  if (!opt)
    return nsnull;

  return opt->m_prefBranch;
}

// Get the text converter...
mozITXTToHTMLConv *
GetTextConverter(MimeDisplayOptions *opt)
{
  if (!opt)
    return nsnull;

  return opt->conv;
}

MimeDisplayOptions::MimeDisplayOptions()
{
  conv = nsnull;        // For text conversion...
  format_out = 0;   // The format out type
  url = nsnull;

  memset(&headers,0, sizeof(headers));
  fancy_headers_p = PR_FALSE;

  output_vcard_buttons_p = PR_FALSE;

  fancy_links_p = PR_FALSE;

  variable_width_plaintext_p = PR_FALSE;
  wrap_long_lines_p = PR_FALSE;
  rot13_p = PR_FALSE;
  part_to_load = nsnull;

  write_html_p = PR_FALSE;

  decrypt_p = PR_FALSE;

  nice_html_only_p = PR_FALSE;

  whattodo = 0 ;
  default_charset = nsnull;
  override_charset = PR_FALSE;
  force_user_charset = PR_FALSE;
  stream_closure = nsnull;

  /* For setting up the display stream, so that the MIME parser can inform
   the caller of the type of the data it will be getting. */
  output_init_fn = nsnull;
  output_fn = nsnull;

  output_closure = nsnull;

  charset_conversion_fn = nsnull;
  rfc1522_conversion_p = PR_FALSE;

  file_type_fn = nsnull;

  passwd_prompt_fn = nsnull;

  html_closure = nsnull;

  generate_header_html_fn = nsnull;
  generate_post_header_html_fn = nsnull;
  generate_footer_html_fn = nsnull;
  generate_reference_url_fn = nsnull;
  generate_mailto_url_fn = nsnull;
  generate_news_url_fn = nsnull;

  image_begin = nsnull;
  image_end = nsnull;
  image_write_buffer = nsnull;
  make_image_html = nsnull;
  state = nsnull;

#ifdef MIME_DRAFTS
  decompose_file_p = PR_FALSE;
  done_parsing_outer_headers = PR_FALSE;
  is_multipart_msg = PR_FALSE;
  decompose_init_count = 0;

  signed_p = PR_FALSE;
  caller_need_root_headers = PR_FALSE;
  decompose_headers_info_fn = nsnull;
  decompose_file_init_fn = nsnull;
  decompose_file_output_fn = nsnull;
  decompose_file_close_fn = nsnull;
#endif /* MIME_DRAFTS */

  attachment_icon_layer_id = 0;

  missing_parts = PR_FALSE;
  show_attachment_inline_p = PR_FALSE;
  quote_attachment_inline_p = PR_FALSE;
  notify_nested_bodies = PR_FALSE;
}

MimeDisplayOptions::~MimeDisplayOptions()
{
  PR_FREEIF(part_to_load);
  PR_FREEIF(default_charset);
}
////////////////////////////////////////////////////////////////
// Bridge routines for new stream converter XP-COM interface
////////////////////////////////////////////////////////////////
extern "C" void  *
mime_bridge_create_display_stream(
                          nsIMimeEmitter      *newEmitter,
                          nsStreamConverter   *newPluginObj2,
                          nsIURI              *uri,
                          nsMimeOutputType    format_out,
                          PRUint32            whattodo,
                          nsIChannel          *aChannel)
{
  int                       status = 0;
  MimeObject                *obj;
  struct mime_stream_data   *msd;
  nsMIMESession             *stream = 0;

  if (!uri)
    return nsnull;

  msd = PR_NEWZAP(struct mime_stream_data);
  if (!msd)
    return NULL;

  // Assign the new mime emitter - will handle output operations
  msd->output_emitter = newEmitter;
  msd->firstCheck = PR_TRUE;

  // Store the URL string for this decode operation
  nsCAutoString urlString;
  nsresult rv;

  // Keep a hold of the channel...
  msd->channel = aChannel;
  rv = uri->GetSpec(urlString);
  if (NS_SUCCEEDED(rv))
  {
    if (!urlString.IsEmpty())
    {
      msd->url_name = ToNewCString(urlString);
      if (!(msd->url_name))
      {
        PR_FREEIF(msd);
        return NULL;
      }
      nsCOMPtr<nsIMsgMessageUrl> msgUrl = do_QueryInterface(uri);
      if (msgUrl)
          msgUrl->GetOriginalSpec(&msd->orig_url_name);
    }
  }

  msd->format_out = format_out;       // output format
  msd->pluginObj2 = newPluginObj2;    // the plugin object pointer

  msd->options = new MimeDisplayOptions;
  if (!msd->options)
  {
    PR_Free(msd);
    return 0;
  }
//  memset(msd->options, 0, sizeof(*msd->options));
  msd->options->format_out = format_out;     // output format

  msd->options->m_prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
  if (NS_FAILED(rv))
  {
    PR_FREEIF(msd);
    return nsnull;
  }

  // Need the text converter...
  rv = CallCreateInstance(MOZ_TXTTOHTMLCONV_CONTRACTID, &(msd->options->conv));
  if (NS_FAILED(rv))
  {
    msd->options->m_prefBranch = 0;
    PR_FREEIF(msd);
    return nsnull;
  }

  //
  // Set the defaults, based on the context, and the output-type.
  //
  MIME_HeaderType = MimeHeadersAll;
  msd->options->write_html_p = PR_TRUE;
  switch (format_out)
  {
    case nsMimeOutput::nsMimeMessageSplitDisplay:   // the wrapper HTML output to produce the split header/body display
    case nsMimeOutput::nsMimeMessageHeaderDisplay:  // the split header/body display
    case nsMimeOutput::nsMimeMessageBodyDisplay:    // the split header/body display
      msd->options->fancy_headers_p = PR_TRUE;
      msd->options->output_vcard_buttons_p = PR_TRUE;
      msd->options->fancy_links_p = PR_TRUE;
      break;

    case nsMimeOutput::nsMimeMessageSaveAs:         // Save As operations
    case nsMimeOutput::nsMimeMessageQuoting:        // all HTML quoted/printed output
    case nsMimeOutput::nsMimeMessagePrintOutput:
      msd->options->fancy_headers_p = PR_TRUE;
      msd->options->fancy_links_p = PR_TRUE;
      break;

    case nsMimeOutput::nsMimeMessageBodyQuoting:        // only HTML body quoted output
      MIME_HeaderType = MimeHeadersNone;
      break;

    case nsMimeOutput::nsMimeMessageAttach:           // handling attachment storage
        msd->options->write_html_p = PR_FALSE;
        break;
    case nsMimeOutput::nsMimeMessageRaw:              // the raw RFC822 data (view source) and attachments
    case nsMimeOutput::nsMimeMessageDraftOrTemplate:  // Loading drafts & templates
    case nsMimeOutput::nsMimeMessageEditorTemplate:   // Loading templates into editor
    case nsMimeOutput::nsMimeMessageFilterSniffer:    // generating an output that can be scan by a message filter
      break;

    case nsMimeOutput::nsMimeMessageDecrypt:
      msd->options->decrypt_p = PR_TRUE;
      msd->options->write_html_p = PR_FALSE;
      break;
  }

  ////////////////////////////////////////////////////////////
  // Now, get the libmime prefs...
  ////////////////////////////////////////////////////////////

  MIME_WrapLongLines = PR_TRUE;
  MIME_VariableWidthPlaintext = PR_TRUE;
  msd->options->force_user_charset = PR_FALSE;

  if (msd->options->m_prefBranch)
  {
    msd->options->m_prefBranch->GetBoolPref("mail.wrap_long_lines", &MIME_WrapLongLines);
    msd->options->m_prefBranch->GetBoolPref("mail.fixed_width_messages", &MIME_VariableWidthPlaintext);
      //
      // Charset overrides takes place here
      //
      // We have a bool pref (mail.force_user_charset) to deal with attachments.
      // 1) If true - libmime does NO conversion and just passes it through to raptor
      // 2) If false, then we try to use the charset of the part and if not available,
      //    the charset of the root message
      //
    msd->options->m_prefBranch->GetBoolPref("mail.force_user_charset", &(msd->options->force_user_charset));
    msd->options->m_prefBranch->GetBoolPref("mail.inline_attachments", &(msd->options->show_attachment_inline_p));
    msd->options->m_prefBranch->GetBoolPref("mail.reply_quote_inline", &(msd->options->quote_attachment_inline_p));
  }
  /* This pref is written down in with the
     opposite sense of what we like to use... */
  MIME_VariableWidthPlaintext = !MIME_VariableWidthPlaintext;

  msd->options->wrap_long_lines_p = MIME_WrapLongLines;
  msd->options->headers = MIME_HeaderType;

  // We need to have the URL to be able to support the various
  // arguments
  status = mime_parse_url_options(msd->url_name, msd->options);
  if (status < 0)
  {
    PR_FREEIF(msd->options->part_to_load);
    PR_Free(msd->options);
    PR_Free(msd);
    return 0;
  }

  if (msd->options->headers == MimeHeadersMicro &&
     (msd->url_name == NULL || (strncmp(msd->url_name, "news:", 5) != 0 &&
              strncmp(msd->url_name, "snews:", 6) != 0)) )
    msd->options->headers = MimeHeadersMicroPlus;

  msd->options->url = msd->url_name;
  msd->options->output_init_fn        = mime_output_init_fn;

  msd->options->output_fn             = mime_output_fn;

  msd->options->whattodo         = whattodo;
  msd->options->charset_conversion_fn = mime_convert_charset;
  msd->options->rfc1522_conversion_p  = PR_TRUE;
  msd->options->file_type_fn          = mime_file_type;
  msd->options->stream_closure        = msd;
  msd->options->passwd_prompt_fn      = 0;

  msd->options->image_begin           = mime_image_begin;
  msd->options->image_end             = mime_image_end;
  msd->options->make_image_html       = mime_image_make_image_html;
  msd->options->image_write_buffer    = mime_image_write_buffer;

  msd->options->variable_width_plaintext_p = MIME_VariableWidthPlaintext;

  // If this is a part, then we should emit the HTML to render the data
  // (i.e. embedded images)
  if (msd->options->part_to_load && msd->options->format_out != nsMimeOutput::nsMimeMessageBodyDisplay)
    msd->options->write_html_p = PR_FALSE;

  obj = mime_new ((MimeObjectClass *)&mimeMessageClass, (MimeHeaders *) NULL, MESSAGE_RFC822);
  if (!obj)
  {
    delete msd->options;
    PR_Free(msd);
    return 0;
  }

  obj->options = msd->options;
  msd->obj = obj;

  /* Both of these better not be true at the same time. */
  PR_ASSERT(! (obj->options->decrypt_p && obj->options->write_html_p));

  stream = PR_NEW(nsMIMESession);
  if (!stream)
  {
    delete msd->options;
    PR_Free(msd);
    PR_Free(obj);
    return 0;
  }

  memset (stream, 0, sizeof (*stream));
  stream->name           = "MIME Conversion Stream";
  stream->complete       = mime_display_stream_complete;
  stream->abort          = mime_display_stream_abort;
  stream->put_block      = mime_display_stream_write;
  stream->data_object    = msd;

  status = obj->clazz->initialize(obj);
  if (status >= 0)
    status = obj->clazz->parse_begin(obj);
  if (status < 0)
  {
    PR_Free(stream);
    delete msd->options;
    PR_Free(msd);
    PR_Free(obj);
    return 0;
  }

  return stream;
}

//
// Emitter Wrapper Routines!
//
nsIMimeEmitter *
GetMimeEmitter(MimeDisplayOptions *opt)
{
  mime_stream_data  *msd = (mime_stream_data *)opt->stream_closure;
  if (!msd)
    return NULL;

  nsIMimeEmitter     *ptr = (nsIMimeEmitter *)(msd->output_emitter);
  return ptr;
}

mime_stream_data *
GetMSD(MimeDisplayOptions *opt)
{
  if (!opt)
    return nsnull;
  mime_stream_data  *msd = (mime_stream_data *)opt->stream_closure;
  return msd;
}

PRBool
NoEmitterProcessing(nsMimeOutputType    format_out)
{
  if ( (format_out == nsMimeOutput::nsMimeMessageDraftOrTemplate) ||
       (format_out == nsMimeOutput::nsMimeMessageEditorTemplate))
    return PR_TRUE;
  else
    return PR_FALSE;
}

extern "C" nsresult
mimeEmitterAddAttachmentField(MimeDisplayOptions *opt, const char *field, const char *value)
{
  // Check for draft processing...
  if (NoEmitterProcessing(opt->format_out))
    return NS_OK;

  mime_stream_data  *msd = GetMSD(opt);
  if (!msd)
    return NS_ERROR_FAILURE;

  if (msd->output_emitter)
  {
    nsIMimeEmitter *emitter = (nsIMimeEmitter *)msd->output_emitter;
    return emitter->AddAttachmentField(field, value);
  }

  return NS_ERROR_FAILURE;
}

extern "C" nsresult
mimeEmitterAddHeaderField(MimeDisplayOptions *opt, const char *field, const char *value)
{
  // Check for draft processing...
  if (NoEmitterProcessing(opt->format_out))
    return NS_OK;

  mime_stream_data  *msd = GetMSD(opt);
  if (!msd)
    return NS_ERROR_FAILURE;

  if (msd->output_emitter)
  {
    nsIMimeEmitter *emitter = (nsIMimeEmitter *)msd->output_emitter;
    return emitter->AddHeaderField(field, value);
  }

  return NS_ERROR_FAILURE;
}

extern "C" nsresult
mimeEmitterAddAllHeaders(MimeDisplayOptions *opt, const char *allheaders, const PRInt32 allheadersize)
{
  // Check for draft processing...
  if (NoEmitterProcessing(opt->format_out))
    return NS_OK;

  mime_stream_data  *msd = GetMSD(opt);
  if (!msd)
    return NS_ERROR_FAILURE;

  if (msd->output_emitter)
  {
    nsIMimeEmitter *emitter = (nsIMimeEmitter *)msd->output_emitter;
    return emitter->AddAllHeaders(Substring(allheaders, 
                                            allheaders + allheadersize));
  }

  return NS_ERROR_FAILURE;
}

extern "C" nsresult
mimeEmitterStartAttachment(MimeDisplayOptions *opt, const char *name, const char *contentType, const char *url,
                           PRBool aIsExternalAttachment)
{
  // Check for draft processing...
  if (NoEmitterProcessing(opt->format_out))
    return NS_OK;

  mime_stream_data  *msd = GetMSD(opt);
  if (!msd)
    return NS_ERROR_FAILURE;

  if (msd->output_emitter)
  {
    nsIMimeEmitter *emitter = (nsIMimeEmitter *)msd->output_emitter;
    return emitter->StartAttachment(name, contentType, url, aIsExternalAttachment);
  }

  return NS_ERROR_FAILURE;
}

extern "C" nsresult
mimeEmitterEndAttachment(MimeDisplayOptions *opt)
{
  // Check for draft processing...
  if (NoEmitterProcessing(opt->format_out))
    return NS_OK;

  mime_stream_data  *msd = GetMSD(opt);
  if (!msd)
    return NS_ERROR_FAILURE;

  if (msd->output_emitter)
  {
    nsIMimeEmitter *emitter = (nsIMimeEmitter *)msd->output_emitter;
    if (emitter)
      return emitter->EndAttachment();
    else
      return NS_OK;
  }

  return NS_ERROR_FAILURE;
}

extern "C" nsresult
mimeEmitterEndAllAttachments(MimeDisplayOptions *opt)
{
  // Check for draft processing...
  if (NoEmitterProcessing(opt->format_out))
    return NS_OK;

  mime_stream_data  *msd = GetMSD(opt);
  if (!msd)
    return NS_ERROR_FAILURE;

  if (msd->output_emitter)
  {
    nsIMimeEmitter *emitter = (nsIMimeEmitter *)msd->output_emitter;
    if (emitter)
      return emitter->EndAllAttachments();
    else
      return NS_OK;
  }

  return NS_ERROR_FAILURE;
}

extern "C" nsresult
mimeEmitterStartBody(MimeDisplayOptions *opt, PRBool bodyOnly, const char *msgID, const char *outCharset)
{
  // Check for draft processing...
  if (NoEmitterProcessing(opt->format_out))
    return NS_OK;

  mime_stream_data  *msd = GetMSD(opt);
  if (!msd)
    return NS_ERROR_FAILURE;

  if (msd->output_emitter)
  {
    nsIMimeEmitter *emitter = (nsIMimeEmitter *)msd->output_emitter;
    return emitter->StartBody(bodyOnly, msgID, outCharset);
  }

  return NS_ERROR_FAILURE;
}

extern "C" nsresult
mimeEmitterEndBody(MimeDisplayOptions *opt)
{
  // Check for draft processing...
  if (NoEmitterProcessing(opt->format_out))
    return NS_OK;

  mime_stream_data  *msd = GetMSD(opt);
  if (!msd)
    return NS_ERROR_FAILURE;

  if (msd->output_emitter)
  {
    nsIMimeEmitter *emitter = (nsIMimeEmitter *)msd->output_emitter;
    return emitter->EndBody();
  }

  return NS_ERROR_FAILURE;
}

extern "C" nsresult
mimeEmitterEndHeader(MimeDisplayOptions *opt)
{
  // Check for draft processing...
  if (NoEmitterProcessing(opt->format_out))
    return NS_OK;

  mime_stream_data  *msd = GetMSD(opt);
  if (!msd)
    return NS_ERROR_FAILURE;

  if (msd->output_emitter)
  {
    nsIMimeEmitter *emitter = (nsIMimeEmitter *)msd->output_emitter;
    return emitter->EndHeader();
  }

  return NS_ERROR_FAILURE;
}

extern "C" nsresult
mimeEmitterUpdateCharacterSet(MimeDisplayOptions *opt, const char *aCharset)
{
  // Check for draft processing...
  if (NoEmitterProcessing(opt->format_out))
    return NS_OK;

  mime_stream_data  *msd = GetMSD(opt);
  if (!msd)
    return NS_ERROR_FAILURE;

  if (msd->output_emitter)
  {
    nsIMimeEmitter *emitter = (nsIMimeEmitter *)msd->output_emitter;
    return emitter->UpdateCharacterSet(aCharset);
  }

  return NS_ERROR_FAILURE;
}

extern "C" nsresult
mimeEmitterStartHeader(MimeDisplayOptions *opt, PRBool rootMailHeader, PRBool headerOnly, const char *msgID,
                       const char *outCharset)
{
  // Check for draft processing...
  if (NoEmitterProcessing(opt->format_out))
    return NS_OK;

  mime_stream_data  *msd = GetMSD(opt);
  if (!msd)
    return NS_ERROR_FAILURE;

  if (msd->output_emitter)
  {
    nsIMimeEmitter *emitter = (nsIMimeEmitter *)msd->output_emitter;
    return emitter->StartHeader(rootMailHeader, headerOnly, msgID, outCharset);
  }

  return NS_ERROR_FAILURE;
}


extern "C" nsresult
mimeSetNewURL(nsMIMESession *stream, char *url)
{
  if ( (!stream) || (!url) || (!*url) )
    return NS_ERROR_FAILURE;

  mime_stream_data  *msd = (mime_stream_data *)stream->data_object;
  if (!msd)
    return NS_ERROR_FAILURE;

  char *tmpPtr = strdup(url);
  if (!tmpPtr)
    return NS_ERROR_FAILURE;

  PR_FREEIF(msd->url_name);
  msd->url_name = strdup(tmpPtr);
  return NS_OK;
}

#define     MIME_URL "chrome://messenger/locale/mime.properties"

extern "C"
char *
MimeGetStringByID(PRInt32 stringID)
{
  char          *tempString = nsnull;
  const char    *resultString = "???";
  nsresult      res = NS_OK;

  if (!stringBundle)
  {
    const char* propertyURL = MIME_URL;

    nsCOMPtr<nsIStringBundleService> sBundleService =
             do_GetService(NS_STRINGBUNDLE_CONTRACTID, &res);
    if (NS_SUCCEEDED(res) && (nsnull != sBundleService))
    {
      res = sBundleService->CreateBundle(propertyURL, getter_AddRefs(stringBundle));
    }
  }

  if (stringBundle)
  {
    nsString v;
    res = stringBundle->GetStringFromID(stringID, getter_Copies(v));

    if (NS_SUCCEEDED(res))
      tempString = ToNewUTF8String(v);
  }

  if (!tempString)
    tempString = strdup(resultString);

  return tempString;
}

void PR_CALLBACK
ResetChannelCharset(MimeObject *obj)
{
  if (obj->options && obj->options->stream_closure &&
      obj->options->default_charset && obj->headers )
  {
    mime_stream_data  *msd = (mime_stream_data *) (obj->options->stream_closure);
    char *ct = MimeHeaders_get (obj->headers, HEADER_CONTENT_TYPE, PR_FALSE, PR_FALSE);
    if ( (ct) && (msd) && (msd->channel) )
    {
      char *ptr = strstr(ct, "charset=");
      if (ptr)
      {
        // First, setup the channel!
        msd->channel->SetContentType(nsDependentCString(ct));

        // Second, if this is a Save As operation, then we need to convert
        // to override the output charset!
        mime_stream_data  *msd = GetMSD(obj->options);
        if ( (msd) && (msd->format_out == nsMimeOutput::nsMimeMessageSaveAs) )
        {
          // Extract the charset alone
          char  *cSet = nsnull;
          if (*(ptr+8) == '"')
            cSet = strdup(ptr+9);
          else
            cSet = strdup(ptr+8);
          if (cSet)
          {
            char *ptr2 = cSet;
            while ( (*cSet) && (*cSet != ' ') && (*cSet != ';') &&
                    (*cSet != '\r') && (*cSet != '\n') && (*cSet != '"') )
              ptr2++;

            if (*cSet) {
              PR_FREEIF(obj->options->default_charset);
              obj->options->default_charset = strdup(cSet);
              obj->options->override_charset = PR_TRUE;
            }

            PR_FREEIF(cSet);
          }
        }
      }
      PR_FREEIF(ct);
    }
  }
}

  ////////////////////////////////////////////////////////////
  // Function to get up mail/news fontlang
  ////////////////////////////////////////////////////////////


nsresult GetMailNewsFont(MimeObject *obj, PRBool styleFixed,  PRInt32 *fontPixelSize,
                         PRInt32 *fontSizePercentage, nsCString& fontLang)
{
  nsresult rv = NS_OK;

  nsIPrefBranch *prefBranch = GetPrefBranch(obj->options);
  if (prefBranch) {
    MimeInlineText  *text = (MimeInlineText *) obj;
    nsCAutoString charset;

    // get a charset
    if (!text->initializeCharset)
      ((MimeInlineTextClass*)&mimeInlineTextClass)->initialize_charset(obj);

    if (!text->charset || !(*text->charset))
      charset.Assign("us-ascii");
    else
      charset.Assign(text->charset);

    nsCOMPtr<nsICharsetConverterManager> charSetConverterManager2;
    nsCOMPtr<nsIAtom> langGroupAtom;
    nsCAutoString prefStr;

    ToLowerCase(charset);

    charSetConverterManager2 = do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
    if ( NS_FAILED(rv))
      return rv;

    // get a language, e.g. x-western, ja
    rv = charSetConverterManager2->GetCharsetLangGroup(charset.get(), getter_AddRefs(langGroupAtom));
    if (NS_FAILED(rv))
      return rv;
    rv = langGroupAtom->ToUTF8String(fontLang);
    if (NS_FAILED(rv))
      return rv;

    // get a font size from pref
    prefStr.Assign(!styleFixed ? "font.size.variable." : "font.size.fixed.");
    prefStr.Append(fontLang);
    rv = prefBranch->GetIntPref(prefStr.get(), fontPixelSize);
    if (NS_FAILED(rv))
      return rv;

    nsCOMPtr<nsIPrefBranch> prefDefBranch;
    nsCOMPtr<nsIPrefService> prefSvc(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
    if(prefSvc)
      rv = prefSvc->GetDefaultBranch("", getter_AddRefs(prefDefBranch));

    if(!prefDefBranch)
      return rv;

    // get original font size
    PRInt32 originalSize;
    rv = prefDefBranch->GetIntPref(prefStr.get(), &originalSize);
    if (NS_FAILED(rv))
      return rv;

    // calculate percentage
    *fontSizePercentage = originalSize ?
                          (PRInt32)((float)*fontPixelSize / (float)originalSize * 100) : 0;

  }

  return NS_OK;
}

/* This function syncronously converts an HTML document (as string)
   to plaintext (as string) using the Gecko converter.

   flags: see nsIDocumentEncoder.h
*/
// TODO: |printf|s?
/* <copy from="mozilla/parser/htmlparser/test/outsinks/Convert.cpp"
         author="akk"
         adapted-by="Ben Bucksch"
         comment=" 'This code would not have been possible without akk.' ;-P.
                   No, really. "
   > */
nsresult
HTML2Plaintext(const nsString& inString, nsString& outString,
               PRUint32 flags, PRUint32 wrapCol)
{
  nsresult rv = NS_OK;

#if DEBUG_BenB
  printf("Converting HTML to plaintext\n");
  char* charstar = ToNewUTF8String(inString);
  printf("HTML source is:\n--------------------\n%s--------------------\n",
         charstar);
  delete[] charstar;
#endif

  // Create a parser
  nsCOMPtr<nsIParser> parser = do_CreateInstance(kParserCID);
  NS_ENSURE_TRUE(parser, NS_ERROR_FAILURE);

  // Create the appropriate output sink
  nsCOMPtr<nsIContentSink> sink =
                               do_CreateInstance(NS_PLAINTEXTSINK_CONTRACTID);
  NS_ENSURE_TRUE(sink, NS_ERROR_FAILURE);

  nsCOMPtr<nsIHTMLToTextSink> textSink(do_QueryInterface(sink));
  NS_ENSURE_TRUE(textSink, NS_ERROR_FAILURE);

  textSink->Initialize(&outString, flags, wrapCol);

  parser->SetContentSink(sink);

  rv = parser->Parse(inString, 0, NS_LITERAL_CSTRING("text/html"), PR_TRUE);

  // Aah! How can NS_ERROR and NS_ABORT_IF_FALSE be no-ops in release builds???
  if (NS_FAILED(rv))
  {
    NS_ERROR("Parse() failed!");
    return rv;
  }

#if DEBUG_BenB
  charstar = ToNewUTF8String(outString);
  printf("Plaintext is:\n--------------------\n%s--------------------\n",
         charstar);
  delete[] charstar;
#endif

  return rv;
}
// </copy>



/* This function syncronously sanitizes an HTML document (string->string)
   using the Gecko ContentSink mozISanitizingHTMLSerializer.

   flags: currently unused
   allowedTags: see mozSanitizingHTMLSerializer::ParsePrefs()
*/
// copied from HTML2Plaintext above
nsresult
HTMLSanitize(const nsString& inString, nsString& outString,
             PRUint32 flags, const nsAString& allowedTags)
{
  nsresult rv = NS_OK;

#if DEBUG_BenB
  printf("Sanitizing HTML\n");
  char* charstar = ToNewUTF8String(inString);
  printf("Original HTML is:\n--------------------\n%s--------------------\n",
         charstar);
  delete[] charstar;
#endif

  // Create a parser
  nsCOMPtr<nsIParser> parser = do_CreateInstance(kParserCID);
  NS_ENSURE_TRUE(parser, NS_ERROR_FAILURE);

  // Create the appropriate output sink
  nsCOMPtr<nsIContentSink> sink =
                    do_CreateInstance(MOZ_SANITIZINGHTMLSERIALIZER_CONTRACTID);
  NS_ENSURE_TRUE(sink, NS_ERROR_FAILURE);

  nsCOMPtr<mozISanitizingHTMLSerializer> sanSink(do_QueryInterface(sink));
  NS_ENSURE_TRUE(sanSink, NS_ERROR_FAILURE);

  sanSink->Initialize(&outString, flags, allowedTags);

  parser->SetContentSink(sink);

  rv = parser->Parse(inString, 0, NS_LITERAL_CSTRING("text/html"), PR_TRUE);
  if (NS_FAILED(rv))
  {
    NS_ERROR("Parse() failed!");
    return rv;
  }

#if DEBUG_BenB
  charstar = ToNewUTF8String(outString);
  printf("Sanitized HTML is:\n--------------------\n%s--------------------\n",
         charstar);
  delete[] charstar;
#endif

  return rv;
}