/* ***** 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 the Netscape security libraries.
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 2000
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Ian McGreer <mcgreer@netscape.com>
 *   Javier Delgadillo <javi@netscape.com>
 *   John Gardiner Myers <jgmyers@speakeasy.net>
 *   Martin v. Loewis <martin@v.loewis.de>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either 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 "prmem.h"
#include "prerror.h"
#include "prprf.h"

#include "nsNSSCertHelper.h"
#include "nsCOMPtr.h"
#include "nsNSSCertificate.h"
#include "cert.h"
#include "nsNSSCertValidity.h"
#include "nsNSSASN1Object.h"
#include "nsNSSComponent.h"
#include "nsNSSCertTrust.h"
#include "nsIDateTimeFormat.h"
#include "nsDateTimeFormatCID.h"
 
static NS_DEFINE_CID(kDateTimeFormatCID, NS_DATETIMEFORMAT_CID);
static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);

#ifndef INET6_ADDRSTRLEN
#define INET6_ADDRSTRLEN 46
#endif

/* Object Identifier constants */
#define CONST_OID static const unsigned char
#define MICROSOFT_OID 0x2b, 0x6, 0x1, 0x4, 0x1, 0x82, 0x37
#define PKIX_OID 0x2b, 0x6, 0x01, 0x05, 0x05, 0x07
CONST_OID msCertExtCerttype[]      = { MICROSOFT_OID, 20, 2};
CONST_OID msNTPrincipalName[]      = { MICROSOFT_OID, 20, 2, 3 };
CONST_OID msCertsrvCAVersion[]     = { MICROSOFT_OID, 21, 1 };
CONST_OID msNTDSReplication[]      = { MICROSOFT_OID, 25, 1 };
CONST_OID pkixLogotype[]           = { PKIX_OID, 1, 12 };

#define OI(x) { siDEROID, (unsigned char *)x, sizeof x }
#define OD(oid,desc,mech,ext) {OI(oid), SEC_OID_UNKNOWN, desc, mech, ext}
#define SEC_OID(tag) more_oids[tag].offset

static SECOidData more_oids[] = {
    /* Microsoft OIDs */
    #define MS_CERT_EXT_CERTTYPE 0
    OD( msCertExtCerttype,
        "Microsoft Certificate Template Name", 
        CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION ),

    #define MS_NT_PRINCIPAL_NAME 1
    OD( msNTPrincipalName,
        "Microsoft Principal Name", 
        CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION ),

    #define MS_CERTSERV_CA_VERSION 2
    OD( msCertsrvCAVersion,
        "Microsoft CA Version", 
        CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION ),

    #define MS_NTDS_REPLICATION 3
    OD( msNTDSReplication,
        "Microsoft Domain GUID", 
        CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION ),

    #define PKIX_LOGOTYPE 4
    OD( pkixLogotype,
        "Logotype", 
        CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION ),
};

static const unsigned int numOids = (sizeof more_oids) / (sizeof more_oids[0]);

static nsresult
GetIntValue(SECItem *versionItem, 
            unsigned long *version)
{
  SECStatus srv;

  srv = SEC_ASN1DecodeInteger(versionItem,version);
  if (srv != SECSuccess) {
    NS_ASSERTION(0,"Could not decode version of cert");
    return NS_ERROR_FAILURE;
  }
  return NS_OK;
}

static nsresult
ProcessVersion(SECItem         *versionItem,
               nsINSSComponent *nssComponent,
               nsIASN1PrintableItem **retItem)
{
  nsresult rv;
  nsAutoString text;
  nsCOMPtr<nsIASN1PrintableItem> printableItem = new nsNSSASN1PrintableItem();
  if (printableItem == nsnull)
    return NS_ERROR_OUT_OF_MEMORY;
 
  nssComponent->GetPIPNSSBundleString("CertDumpVersion", text);
  rv = printableItem->SetDisplayName(text);
  if (NS_FAILED(rv))
    return rv;

  // Now to figure out what version this certificate is.
  unsigned long version;

  if (versionItem->data) {
    rv = GetIntValue(versionItem, &version);
    if (NS_FAILED(rv))
      return rv;
  } else {
    // If there is no version present in the cert, then rfc2459
    // says we default to v1 (0)
    version = 0;
  }

  switch (version){
  case 0:
    rv = nssComponent->GetPIPNSSBundleString("CertDumpVersion1", text);
    break;
  case 1:
    rv = nssComponent->GetPIPNSSBundleString("CertDumpVersion2", text);
    break;
  case 2:
    rv = nssComponent->GetPIPNSSBundleString("CertDumpVersion3", text);
    break;
  default:
    NS_ASSERTION(0,"Bad value for cert version");
    rv = NS_ERROR_FAILURE;
  }
    
  if (NS_FAILED(rv))
    return rv;

  rv = printableItem->SetDisplayValue(text);
  if (NS_FAILED(rv))
    return rv;

  *retItem = printableItem;
  NS_ADDREF(*retItem);
  return NS_OK;
}

static nsresult 
ProcessSerialNumberDER(SECItem         *serialItem, 
                       nsINSSComponent *nssComponent,
                       nsIASN1PrintableItem **retItem)
{
  nsresult rv;
  nsAutoString text;
  nsCOMPtr<nsIASN1PrintableItem> printableItem = new nsNSSASN1PrintableItem();

  if (printableItem == nsnull)
    return NS_ERROR_OUT_OF_MEMORY;

  rv = nssComponent->GetPIPNSSBundleString("CertDumpSerialNo", text); 
  if (NS_FAILED(rv))
    return rv;

  rv = printableItem->SetDisplayName(text);
  if (NS_FAILED(rv))
    return rv;

  nsXPIDLCString serialNumber;
  serialNumber.Adopt(CERT_Hexify(serialItem, 1));
  if (serialNumber == nsnull)
    return NS_ERROR_OUT_OF_MEMORY;

  rv = printableItem->SetDisplayValue(NS_ConvertASCIItoUCS2(serialNumber));
  *retItem = printableItem;
  NS_ADDREF(*retItem);
  return rv;
}

static nsresult
GetDefaultOIDFormat(SECItem *oid,
                    nsAString &outString,
		    char separator)
{
  char buf[300];
  unsigned int len;
  int written;
    
  unsigned long val  = oid->data[0];
  unsigned int  i    = val % 40;
  val /= 40;
  written = PR_snprintf(buf, 300, "%lu%c%u", val, separator, i);
  if (written < 0)
    return NS_ERROR_FAILURE;	
  len = written;

  val = 0;
  for (i = 1; i < oid->len; ++i) {
    // In this loop, we have to parse a DER formatted 
    // If the first bit is a 1, then the integer is 
    // represented by more than one byte.  If the 
    // first bit is set then we continue on and add
    // the values of the later bytes until we get 
    // a byte without the first bit set.
    unsigned long j;

    j = oid->data[i];
    val = (val << 7) | (j & 0x7f);
    if (j & 0x80)
      continue;
    written = PR_snprintf(&buf[len], sizeof(buf)-len, "%c%lu", 
			  separator, val);
    if (written < 0)
      return NS_ERROR_FAILURE;

    len += written;
    NS_ASSERTION(len < sizeof(buf), "OID data to big to display in 300 chars.");
    val = 0;      
  }

  CopyASCIItoUTF16(buf, outString);
  return NS_OK; 
}

static nsresult
GetOIDText(SECItem *oid, nsINSSComponent *nssComponent, nsAString &text)
{ 
  nsresult rv;
  SECOidTag oidTag = SECOID_FindOIDTag(oid);
  const char *bundlekey = 0;

  switch (oidTag) {
  case SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION:
    bundlekey = "CertDumpMD2WithRSA";
    break;
  case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION:
    bundlekey = "CertDumpMD5WithRSA";
    break;
  case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION:
    bundlekey = "CertDumpSHA1WithRSA";
    break;
  case SEC_OID_PKCS1_RSA_ENCRYPTION:
    bundlekey = "CertDumpRSAEncr";
    break;
  case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION:
    bundlekey = "CertDumpSHA256WithRSA";
    break;
  case SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION:
    bundlekey = "CertDumpSHA384WithRSA";
    break;
  case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION:
    bundlekey = "CertDumpSHA512WithRSA";
    break;
  case SEC_OID_NS_CERT_EXT_CERT_TYPE:
    bundlekey = "CertDumpCertType";
    break;
  case SEC_OID_NS_CERT_EXT_BASE_URL:
    bundlekey = "CertDumpNSCertExtBaseUrl";
    break;
  case SEC_OID_NS_CERT_EXT_REVOCATION_URL:
    bundlekey = "CertDumpNSCertExtRevocationUrl";
    break;
  case SEC_OID_NS_CERT_EXT_CA_REVOCATION_URL:
    bundlekey = "CertDumpNSCertExtCARevocationUrl";
    break;
  case SEC_OID_NS_CERT_EXT_CERT_RENEWAL_URL:
    bundlekey = "CertDumpNSCertExtCertRenewalUrl";
    break;
  case SEC_OID_NS_CERT_EXT_CA_POLICY_URL:
    bundlekey = "CertDumpNSCertExtCAPolicyUrl";
    break;
  case SEC_OID_NS_CERT_EXT_SSL_SERVER_NAME:
    bundlekey = "CertDumpNSCertExtSslServerName";
    break;
  case SEC_OID_NS_CERT_EXT_COMMENT:
    bundlekey = "CertDumpNSCertExtComment";
    break;
  case SEC_OID_NS_CERT_EXT_LOST_PASSWORD_URL:
    bundlekey = "CertDumpNSCertExtLostPasswordUrl";
    break;
  case SEC_OID_NS_CERT_EXT_CERT_RENEWAL_TIME:
    bundlekey = "CertDumpNSCertExtCertRenewalTime";
    break;
  case SEC_OID_NETSCAPE_AOLSCREENNAME:
    bundlekey = "CertDumpNetscapeAolScreenname";
    break;
  case SEC_OID_AVA_COUNTRY_NAME:
    bundlekey = "CertDumpAVACountry";
    break;
  case SEC_OID_AVA_COMMON_NAME:
    bundlekey = "CertDumpAVACN";
    break;
  case SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME:
    bundlekey = "CertDumpAVAOU";
    break;
  case SEC_OID_AVA_ORGANIZATION_NAME:
    bundlekey = "CertDumpAVAOrg";
    break;
  case SEC_OID_AVA_LOCALITY:
    bundlekey = "CertDumpAVALocality";
    break;
  case SEC_OID_AVA_DN_QUALIFIER:
    bundlekey = "CertDumpAVADN";
    break;
  case SEC_OID_AVA_DC:
    bundlekey = "CertDumpAVADC";
    break;
  case SEC_OID_AVA_STATE_OR_PROVINCE:
    bundlekey = "CertDumpAVAState";
    break;
  case SEC_OID_X509_SUBJECT_DIRECTORY_ATTR:
    bundlekey = "CertDumpSubjectDirectoryAttr";
    break;
  case SEC_OID_X509_SUBJECT_KEY_ID:
    bundlekey = "CertDumpSubjectKeyID";
    break;
  case SEC_OID_X509_KEY_USAGE:
    bundlekey = "CertDumpKeyUsage";
    break;
  case SEC_OID_X509_SUBJECT_ALT_NAME:
    bundlekey = "CertDumpSubjectAltName";
    break;
  case SEC_OID_X509_ISSUER_ALT_NAME:
    bundlekey = "CertDumpIssuerAltName";
    break;
  case SEC_OID_X509_BASIC_CONSTRAINTS:
    bundlekey = "CertDumpBasicConstraints";
    break;
  case SEC_OID_X509_NAME_CONSTRAINTS:
    bundlekey = "CertDumpNameConstraints";
    break;
  case SEC_OID_X509_CRL_DIST_POINTS:
    bundlekey = "CertDumpCrlDistPoints";
    break;
  case SEC_OID_X509_CERTIFICATE_POLICIES:
    bundlekey = "CertDumpCertPolicies";
    break;
  case SEC_OID_X509_POLICY_MAPPINGS:
    bundlekey = "CertDumpPolicyMappings";
    break;
  case SEC_OID_X509_POLICY_CONSTRAINTS:
    bundlekey = "CertDumpPolicyConstraints";
    break;
  case SEC_OID_X509_AUTH_KEY_ID:
    bundlekey = "CertDumpAuthKeyID";
    break;
  case SEC_OID_X509_EXT_KEY_USAGE:
    bundlekey = "CertDumpExtKeyUsage";
    break;
  case SEC_OID_X509_AUTH_INFO_ACCESS:
    bundlekey = "CertDumpAuthInfoAccess";
    break;
  case SEC_OID_ANSIX9_DSA_SIGNATURE:
    bundlekey = "CertDumpAnsiX9DsaSignature";
    break;
  case SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST:
    bundlekey = "CertDumpAnsiX9DsaSignatureWithSha1";
    break;
  case SEC_OID_ANSIX962_ECDSA_SIGNATURE_WITH_SHA1_DIGEST:
    bundlekey = "CertDumpAnsiX962ECDsaSignatureWithSha1";
    break;
  case SEC_OID_RFC1274_UID:
    bundlekey = "CertDumpUserID";
    break;
  case SEC_OID_PKCS9_EMAIL_ADDRESS:
    bundlekey = "CertDumpPK9Email";
    break;
  case SEC_OID_ANSIX962_EC_PUBLIC_KEY:
    bundlekey = "CertDumpECPublicKey";
    break;
  /* ANSI X9.62 named elliptic curves (prime field) */
  case SEC_OID_ANSIX962_EC_PRIME192V1:
    /* same as SEC_OID_SECG_EC_SECP192r1 */
    bundlekey = "CertDumpECprime192v1";
    break;
  case SEC_OID_ANSIX962_EC_PRIME192V2:
    bundlekey = "CertDumpECprime192v2";
    break;
  case SEC_OID_ANSIX962_EC_PRIME192V3:
    bundlekey = "CertDumpECprime192v3";
    break;
  case SEC_OID_ANSIX962_EC_PRIME239V1:
    bundlekey = "CertDumpECprime239v1";
    break;
  case SEC_OID_ANSIX962_EC_PRIME239V2:
    bundlekey = "CertDumpECprime239v2";
    break;
  case SEC_OID_ANSIX962_EC_PRIME239V3:
    bundlekey = "CertDumpECprime239v3";
    break;
  case SEC_OID_ANSIX962_EC_PRIME256V1:
    /* same as SEC_OID_SECG_EC_SECP256r1 */
    bundlekey = "CertDumpECprime256v1";
    break;
  /* SECG named elliptic curves (prime field) */
  case SEC_OID_SECG_EC_SECP112R1:
    bundlekey = "CertDumpECsecp112r1";
    break;
  case SEC_OID_SECG_EC_SECP112R2:
    bundlekey = "CertDumpECsecp112r2";
    break;
  case SEC_OID_SECG_EC_SECP128R1:
    bundlekey = "CertDumpECsecp128r1";
    break;
  case SEC_OID_SECG_EC_SECP128R2:
    bundlekey = "CertDumpECsecp128r2";
    break;
  case SEC_OID_SECG_EC_SECP160K1:
    bundlekey = "CertDumpECsecp160k1";
    break;
  case SEC_OID_SECG_EC_SECP160R1:
    bundlekey = "CertDumpECsecp160r1";
    break;
  case SEC_OID_SECG_EC_SECP160R2:
    bundlekey = "CertDumpECsecp160r2";
    break;
  case SEC_OID_SECG_EC_SECP192K1:
    bundlekey = "CertDumpECsecp192k1";
    break;
  case SEC_OID_SECG_EC_SECP224K1:
    bundlekey = "CertDumpECsecp224k1";
    break;
  case SEC_OID_SECG_EC_SECP224R1:
    bundlekey = "CertDumpECsecp224r1";
    break;
  case SEC_OID_SECG_EC_SECP256K1:
    bundlekey = "CertDumpECsecp256k1";
    break;
  case SEC_OID_SECG_EC_SECP384R1:
    bundlekey = "CertDumpECsecp384r1";
    break;

  case SEC_OID_SECG_EC_SECP521R1:
    bundlekey = "CertDumpECsecp521r1";
    break;
  /* ANSI X9.62 named elliptic curves (characteristic two field) */
  case SEC_OID_ANSIX962_EC_C2PNB163V1:
    bundlekey = "CertDumpECc2pnb163v1";
    break;
  case SEC_OID_ANSIX962_EC_C2PNB163V2:
    bundlekey = "CertDumpECc2pnb163v2";
    break;
  case SEC_OID_ANSIX962_EC_C2PNB163V3:
    bundlekey = "CertDumpECc2pnb163v3";
    break;
  case SEC_OID_ANSIX962_EC_C2PNB176V1:
    bundlekey = "CertDumpECc2pnb176v1";
    break;
  case SEC_OID_ANSIX962_EC_C2TNB191V1:
    bundlekey = "CertDumpECc2tnb191v1";
    break;
  case SEC_OID_ANSIX962_EC_C2TNB191V2:
    bundlekey = "CertDumpECc2tnb191v2";
    break;
  case SEC_OID_ANSIX962_EC_C2TNB191V3:
    bundlekey = "CertDumpECc2tnb191v3";
    break;
  case SEC_OID_ANSIX962_EC_C2ONB191V4:
    bundlekey = "CertDumpECc2onb191v4";
    break;
  case SEC_OID_ANSIX962_EC_C2ONB191V5:
    bundlekey = "CertDumpECc2onb191v5";
    break;
  case SEC_OID_ANSIX962_EC_C2PNB208W1:
    bundlekey = "CertDumpECc2pnb208w1";
    break;
  case SEC_OID_ANSIX962_EC_C2TNB239V1:
    bundlekey = "CertDumpECc2tnb239v1";
    break;
  case SEC_OID_ANSIX962_EC_C2TNB239V2:
    bundlekey = "CertDumpECc2tnb239v2";
    break;
  case SEC_OID_ANSIX962_EC_C2TNB239V3:
    bundlekey = "CertDumpECc2tnb239v3";
    break;
  case SEC_OID_ANSIX962_EC_C2ONB239V4:
    bundlekey = "CertDumpECc2onb239v4";
    break;
  case SEC_OID_ANSIX962_EC_C2ONB239V5:
    bundlekey = "CertDumpECc2onb239v5";
    break;
  case SEC_OID_ANSIX962_EC_C2PNB272W1:
    bundlekey = "CertDumpECc2pnb272w1";
    break;
  case SEC_OID_ANSIX962_EC_C2PNB304W1:
    bundlekey = "CertDumpECc2pnb304w1";
    break;
  case SEC_OID_ANSIX962_EC_C2TNB359V1:
    bundlekey = "CertDumpECc2tnb359v1";
    break;
  case SEC_OID_ANSIX962_EC_C2PNB368W1:
    bundlekey = "CertDumpECc2pnb368w1";
    break;
  case SEC_OID_ANSIX962_EC_C2TNB431R1:
    bundlekey = "CertDumpECc2tnb431r1";
    break;
  /* SECG named elliptic curves (characteristic two field) */
  case SEC_OID_SECG_EC_SECT113R1:
    bundlekey = "CertDumpECsect113r1";
    break;
  case SEC_OID_SECG_EC_SECT113R2:
    bundlekey = "CertDumpECsect113r2";
    break;
  case SEC_OID_SECG_EC_SECT131R1:
    bundlekey = "CertDumpECsect131r1";
    break;
  case SEC_OID_SECG_EC_SECT131R2:
    bundlekey = "CertDumpECsect131r2";
    break;
  case SEC_OID_SECG_EC_SECT163K1:
    bundlekey = "CertDumpECsect163k1";
    break;
  case SEC_OID_SECG_EC_SECT163R1:
    bundlekey = "CertDumpECsect163r1";
    break;
  case SEC_OID_SECG_EC_SECT163R2:
    bundlekey = "CertDumpECsect163r2";
    break;
  case SEC_OID_SECG_EC_SECT193R1:
    bundlekey = "CertDumpECsect193r1";
    break;
  case SEC_OID_SECG_EC_SECT193R2:
    bundlekey = "CertDumpECsect193r2";
    break;
  case SEC_OID_SECG_EC_SECT233K1:
    bundlekey = "CertDumpECsect233k1";
    break;
  case SEC_OID_SECG_EC_SECT233R1:
    bundlekey = "CertDumpECsect233r1";
    break;
  case SEC_OID_SECG_EC_SECT239K1:
    bundlekey = "CertDumpECsect239k1";
    break;
  case SEC_OID_SECG_EC_SECT283K1:
    bundlekey = "CertDumpECsect283k1";
    break;
  case SEC_OID_SECG_EC_SECT283R1:
    bundlekey = "CertDumpECsect283r1";
    break;
  case SEC_OID_SECG_EC_SECT409K1:
    bundlekey = "CertDumpECsect409k1";
    break;
  case SEC_OID_SECG_EC_SECT409R1:
    bundlekey = "CertDumpECsect409r1";
    break;
  case SEC_OID_SECG_EC_SECT571K1:
    bundlekey = "CertDumpECsect571k1";
    break;
  case SEC_OID_SECG_EC_SECT571R1:
    bundlekey = "CertDumpECsect571r1";
    break;
  default: 
    if (oidTag == SEC_OID(MS_CERT_EXT_CERTTYPE)) {
      bundlekey = "CertDumpMSCerttype";
      break;
    }
    if (oidTag == SEC_OID(MS_CERTSERV_CA_VERSION)) {
      bundlekey = "CertDumpMSCAVersion";
      break;
    }
    if (oidTag == SEC_OID(PKIX_LOGOTYPE)) {
      bundlekey = "CertDumpLogotype";
      break;
    }
    /* fallthrough */
  }

  if (bundlekey) {
    rv = nssComponent->GetPIPNSSBundleString(bundlekey, text);
  } else {
    nsAutoString text2;
    rv = GetDefaultOIDFormat(oid, text2, ' ');
    if (NS_FAILED(rv))
      return rv;

    const PRUnichar *params[1] = {text2.get()};
    rv = nssComponent->PIPBundleFormatStringFromName("CertDumpDefOID",
                                                     params, 1, text);
  }
  return rv;  
}

#define SEPARATOR "\n"

static nsresult
ProcessRawBytes(nsINSSComponent *nssComponent, SECItem *data, nsAString &text)
{
  // This function is used to display some DER bytes
  // that we have not added support for decoding.
  // It prints the value of the byte out into a 
  // string that can later be displayed as a byte
  // string.  We place a new line after 24 bytes
  // to break up extermaly long sequence of bytes.

  nsAutoString bytelen, bitlen;
  bytelen.AppendInt(data->len);
  bitlen.AppendInt(data->len*8);

  const PRUnichar *params[2] = {bytelen.get(), bitlen.get()};
  nsresult rv = nssComponent->PIPBundleFormatStringFromName("CertDumpRawBytesHeader",
                                                            params, 2, text);
  if (NS_FAILED(rv))
    return rv;

  text.Append(NS_LITERAL_STRING(SEPARATOR).get());

  PRUint32 i;
  char buffer[5];
  for (i=0; i<data->len; i++) {
    PR_snprintf(buffer, 5, "%02x ", data->data[i]);
    AppendASCIItoUTF16(buffer, text);
    if ((i+1)%16 == 0) {
      text.Append(NS_LITERAL_STRING(SEPARATOR).get());
    }
  }
  return NS_OK;
}    

static nsresult
ProcessNSCertTypeExtensions(SECItem  *extData, 
                            nsAString &text,
                            nsINSSComponent *nssComponent)
{
  nsAutoString local;
  SECItem decoded;
  decoded.data = nsnull;
  decoded.len  = 0;
  if (SECSuccess != SEC_ASN1DecodeItem(nsnull, &decoded, 
		SEC_ASN1_GET(SEC_BitStringTemplate), extData)) {
    nssComponent->GetPIPNSSBundleString("CertDumpExtensionFailure", local);
    text.Append(local.get());
    return NS_OK;
  }
  unsigned char nsCertType = decoded.data[0];
  nsMemory::Free(decoded.data);
  if (nsCertType & NS_CERT_TYPE_SSL_CLIENT) {
    nssComponent->GetPIPNSSBundleString("VerifySSLClient", local);
    text.Append(local.get());
    text.Append(NS_LITERAL_STRING(SEPARATOR).get());
  }
  if (nsCertType & NS_CERT_TYPE_SSL_SERVER) {
    nssComponent->GetPIPNSSBundleString("VerifySSLServer", local);
    text.Append(local.get());
    text.Append(NS_LITERAL_STRING(SEPARATOR).get());
  }
  if (nsCertType & NS_CERT_TYPE_EMAIL) {
    nssComponent->GetPIPNSSBundleString("CertDumpCertTypeEmail", local);
    text.Append(local.get());
    text.Append(NS_LITERAL_STRING(SEPARATOR).get());
  }
  if (nsCertType & NS_CERT_TYPE_OBJECT_SIGNING) {
    nssComponent->GetPIPNSSBundleString("VerifyObjSign", local);
    text.Append(local.get());
    text.Append(NS_LITERAL_STRING(SEPARATOR).get());
  }
  if (nsCertType & NS_CERT_TYPE_SSL_CA) {
    nssComponent->GetPIPNSSBundleString("VerifySSLCA", local);
    text.Append(local.get());
    text.Append(NS_LITERAL_STRING(SEPARATOR).get());
  }
  if (nsCertType & NS_CERT_TYPE_EMAIL_CA) {
    nssComponent->GetPIPNSSBundleString("CertDumpEmailCA", local);
    text.Append(local.get());
    text.Append(NS_LITERAL_STRING(SEPARATOR).get());
  }
  if (nsCertType & NS_CERT_TYPE_OBJECT_SIGNING_CA) {
    nssComponent->GetPIPNSSBundleString("VerifyObjSign", local);
    text.Append(local.get());
    text.Append(NS_LITERAL_STRING(SEPARATOR).get());
  }
  return NS_OK;
}

static nsresult
ProcessKeyUsageExtension(SECItem *extData, nsAString &text,
                         nsINSSComponent *nssComponent)
{
  nsAutoString local;
  SECItem decoded;
  decoded.data = nsnull;
  decoded.len  = 0;
  if (SECSuccess != SEC_ASN1DecodeItem(nsnull, &decoded, 
				SEC_ASN1_GET(SEC_BitStringTemplate), extData)) {
    nssComponent->GetPIPNSSBundleString("CertDumpExtensionFailure", local);
    text.Append(local.get());
    return NS_OK;
  }
  unsigned char keyUsage = decoded.data[0];
  nsMemory::Free(decoded.data);  
  if (keyUsage & KU_DIGITAL_SIGNATURE) {
    nssComponent->GetPIPNSSBundleString("CertDumpKUSign", local);
    text.Append(local.get());
    text.Append(NS_LITERAL_STRING(SEPARATOR).get());
  }
  if (keyUsage & KU_NON_REPUDIATION) {
    nssComponent->GetPIPNSSBundleString("CertDumpKUNonRep", local);
    text.Append(local.get());
    text.Append(NS_LITERAL_STRING(SEPARATOR).get());
  }
  if (keyUsage & KU_KEY_ENCIPHERMENT) {
    nssComponent->GetPIPNSSBundleString("CertDumpKUEnc", local);
    text.Append(local.get());
    text.Append(NS_LITERAL_STRING(SEPARATOR).get());
  }
  if (keyUsage & KU_DATA_ENCIPHERMENT) {
    nssComponent->GetPIPNSSBundleString("CertDumpKUDEnc", local);
    text.Append(local.get());
    text.Append(NS_LITERAL_STRING(SEPARATOR).get());
  }
  if (keyUsage & KU_KEY_AGREEMENT) {
    nssComponent->GetPIPNSSBundleString("CertDumpKUKA", local);
    text.Append(local.get());
    text.Append(NS_LITERAL_STRING(SEPARATOR).get());
  }
  if (keyUsage & KU_KEY_CERT_SIGN) {
    nssComponent->GetPIPNSSBundleString("CertDumpKUCertSign", local);
    text.Append(local.get());
    text.Append(NS_LITERAL_STRING(SEPARATOR).get());
  }
  if (keyUsage & KU_CRL_SIGN) {
    nssComponent->GetPIPNSSBundleString("CertDumpKUCRLSigner", local);
    text.Append(local.get());
    text.Append(NS_LITERAL_STRING(SEPARATOR).get());
  }

  return NS_OK;
}

static nsresult
ProcessBasicConstraints(SECItem  *extData, 
                        nsAString &text,
                        nsINSSComponent *nssComponent)
{
  nsAutoString local;
  CERTBasicConstraints value;
  SECStatus rv;
  nsresult rv2;

  value.pathLenConstraint = -1;
  rv = CERT_DecodeBasicConstraintValue (&value, extData);
  if (rv != SECSuccess) {
    ProcessRawBytes(nssComponent, extData, text);
    return NS_OK;
  }
  if (value.isCA)
    rv2 = nssComponent->GetPIPNSSBundleString("CertDumpIsCA", local);
  else
    rv2 = nssComponent->GetPIPNSSBundleString("CertDumpIsNotCA", local);
  if (NS_FAILED(rv2))
    return rv2;
  text.Append(local.get());
  if (value.pathLenConstraint != -1) {
    nsAutoString depth;
    if (value.pathLenConstraint == CERT_UNLIMITED_PATH_CONSTRAINT)
      nssComponent->GetPIPNSSBundleString("CertDumpPathLenUnlimited", depth);
    else
      depth.AppendInt(value.pathLenConstraint);
    const PRUnichar *params[1] = {depth.get()};
    rv2 = nssComponent->PIPBundleFormatStringFromName("CertDumpPathLen",
                                                      params, 1, local);
    if (NS_FAILED(rv2))
      return rv2;
    text.Append(NS_LITERAL_STRING(SEPARATOR).get());
    text.Append(local.get());
  }
  return NS_OK;
}

static nsresult
ProcessExtKeyUsage(SECItem  *extData, 
                   nsAString &text,
                   nsINSSComponent *nssComponent)
{
  nsAutoString local;
  CERTOidSequence *extKeyUsage = NULL;
  SECItem **oids;
  SECItem *oid;
  nsresult rv;
  
  extKeyUsage = CERT_DecodeOidSequence(extData);
  if (extKeyUsage == NULL)
    return NS_ERROR_FAILURE;

  oids = extKeyUsage->oids;
  while (oids != NULL && *oids != NULL) {
    // For each OID, try to find a bundle string
    // of the form CertDumpEKU_<underlined-OID>
    nsAutoString oidname;
    oid = *oids;
    rv = GetDefaultOIDFormat(oid, oidname, '_');
    if (NS_FAILED(rv))
      return rv;
    nsAutoString bundlekey = NS_LITERAL_STRING("CertDumpEKU_")+ oidname;
    NS_ConvertUTF16toUTF8 bk_ascii(bundlekey);
    
    rv = nssComponent->GetPIPNSSBundleString(bk_ascii.get(), local);
    nsresult rv2 = GetDefaultOIDFormat(oid, oidname, '.');
    if (NS_FAILED(rv2))
      return rv2;
    if (NS_SUCCEEDED(rv)) {
      // display name and OID in parentheses
      text.Append(local);
      text.Append(NS_LITERAL_STRING(" ("));
      text.Append(oidname);
      text.Append(NS_LITERAL_STRING(")"));
    } else
      // If there is no bundle string, just display the OID itself
      text.Append(oidname);

    text.Append(NS_LITERAL_STRING(SEPARATOR).get());
    oids++;
  }

  CERT_DestroyOidSequence(extKeyUsage);
  return NS_OK;
}

static nsresult
ProcessRDN(CERTRDN* rdn, nsAString &finalString, nsINSSComponent *nssComponent)
{
  nsresult rv;
  CERTAVA** avas;
  CERTAVA* ava;
  SECItem *decodeItem = nsnull;
  nsString avavalue;
  nsString type;
  nsAutoString temp;
  const PRUnichar *params[2];

  avas = rdn->avas;
  while ((ava = *avas++) != 0) {
    rv = GetOIDText(&ava->type, nssComponent, type);
    if (NS_FAILED(rv))
      return rv;
    
    //This function returns a string in UTF8 format.
    decodeItem = CERT_DecodeAVAValue(&ava->value);
    if(!decodeItem) {
      return NS_ERROR_FAILURE;
    }
    avavalue = NS_ConvertUTF8toUTF16((char*)decodeItem->data, decodeItem->len);
    
    SECITEM_FreeItem(decodeItem, PR_TRUE);
    params[0] = type.get();
    params[1] = avavalue.get();
    nssComponent->PIPBundleFormatStringFromName("AVATemplate",
                                                  params, 2, temp);
    finalString += temp + NS_LITERAL_STRING("\n");
  }
  return NS_OK;
}

static nsresult
ProcessName(CERTName *name, nsINSSComponent *nssComponent, PRUnichar **value)
{
  CERTRDN** rdns;
  CERTRDN** rdn;
  nsString finalString;

  rdns = name->rdns;

  nsresult rv;
  CERTRDN **lastRdn;
  lastRdn = rdns;


  /* find last RDN */
  lastRdn = rdns;
  while (*lastRdn) lastRdn++;
  // The above whille loop will put us at the last member
  // of the array which is a NULL pointer.  So let's back
  // up one spot so that we have the last non-NULL entry in 
  // the array in preparation for traversing the 
  // RDN's (Relative Distinguished Name) in reverse oder.
  lastRdn--;
   
  /*
   * Loop over name contents in _reverse_ RDN order appending to string
   * When building the Ascii string, NSS loops over these entries in 
   * reverse order, so I will as well.  The difference is that NSS
   * will always place them in a one line string separated by commas,
   * where I want each entry on a single line.  I can't just use a comma
   * as my delimitter because it is a valid character to have in the 
   * value portion of the AVA and could cause trouble when parsing.
   */
  for (rdn = lastRdn; rdn >= rdns; rdn--) {
    rv = ProcessRDN(*rdn, finalString, nssComponent);
    if (NS_FAILED(rv))
      return rv;
  }
  *value = ToNewUnicode(finalString);    
  return NS_OK;
}

static nsresult
ProcessIA5String(SECItem  *extData, 
		 nsAString &text,
		 nsINSSComponent *nssComponent)
{
  SECItem item;
  nsAutoString local;
  if (SECSuccess != SEC_ASN1DecodeItem(nsnull, &item, 
				       SEC_ASN1_GET(SEC_IA5StringTemplate),
				       extData))
    return NS_ERROR_FAILURE;
  local.AssignASCII((char*)item.data, item.len);
  nsMemory::Free(item.data);
  text.Append(local);
  return NS_OK;
}

static nsresult
AppendBMPtoUTF16(PRArenaPool *arena,
		 unsigned char* data, unsigned int len,
		 nsAString& text)
{
  unsigned int   utf8ValLen;
  unsigned char *utf8Val;

  if (len % 2 != 0)
    return NS_ERROR_FAILURE;

  /* XXX instead of converting to and from UTF-8, it would
     be sufficient to just swap bytes, or do nothing */
  utf8ValLen = len * 3 + 1;
  utf8Val = (unsigned char*)PORT_ArenaZAlloc(arena, utf8ValLen);
  if (!PORT_UCS2_UTF8Conversion(PR_FALSE, data, len,
				utf8Val, utf8ValLen, &utf8ValLen))
    return NS_ERROR_FAILURE;
  AppendUTF8toUTF16((char*)utf8Val, text);
  return NS_OK;
}

static nsresult
ProcessBMPString(SECItem  *extData, 
		 nsAString &text,
		 nsINSSComponent *nssComponent)
{
  SECItem item;
  PRArenaPool *arena;
  nsresult rv = NS_ERROR_FAILURE;
  
  arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  if (!arena)
    return NS_ERROR_FAILURE;

  if (SECSuccess == SEC_ASN1DecodeItem(arena, &item, 
				       SEC_ASN1_GET(SEC_BMPStringTemplate),
				       extData))
    rv = AppendBMPtoUTF16(arena, item.data, item.len, text);
  PORT_FreeArena(arena, PR_FALSE);
  return rv;
}

static nsresult
ProcessGeneralName(PRArenaPool *arena,
		   CERTGeneralName *current,
		   nsAString &text,
		   nsINSSComponent *nssComponent)
{
  nsAutoString key;
  nsXPIDLString value;
  nsresult rv = NS_OK;

  switch (current->type) {
  case certOtherName: {
    SECOidTag oidTag = SECOID_FindOIDTag(&current->name.OthName.oid);
    if (oidTag == SEC_OID(MS_NT_PRINCIPAL_NAME)) {
	/* The type of this name is apparently nowhere explicitly
	   documented. However, in the generated templates, it is always
	   UTF-8. So try to decode this as UTF-8; if that fails, dump the
	   raw data. */
	SECItem decoded;
	nssComponent->GetPIPNSSBundleString("CertDumpMSNTPrincipal", key);
	if (SEC_ASN1DecodeItem(arena, &decoded, 
			       SEC_ASN1_GET(SEC_UTF8StringTemplate), 
			       &current->name.OthName.name) == SECSuccess) {
	  AppendUTF8toUTF16(nsCAutoString((char*)decoded.data, decoded.len),
			    value);
	} else {
	  ProcessRawBytes(nssComponent, &current->name.OthName.name, value);
	}
	break;
    } else if (oidTag == SEC_OID(MS_NTDS_REPLICATION)) {
	/* This should be a 16-byte GUID */
	SECItem guid;
	nssComponent->GetPIPNSSBundleString("CertDumpMSDomainGUID", key);
	if (SEC_ASN1DecodeItem(arena, &guid,
			       SEC_ASN1_GET(SEC_OctetStringTemplate),
			       &current->name.OthName.name) == SECSuccess
	    && guid.len == 16) {
	  char buf[40];
	  unsigned char *d = guid.data;
	  PR_snprintf(buf, sizeof(buf), 
		      "{%.2x%.2x%.2x%.2x-%.2x%.2x-%.2x%.2x-%.2x%.2x-%.2x%.2x%.2x%.2x%.2x%.2x}",
		      d[3], d[2], d[1], d[0], d[5], d[4], d[7], d[6],
		      d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]);
	  value.AssignASCII(buf);
	} else {
	  ProcessRawBytes(nssComponent, &current->name.OthName.name, value);
	}
    } else {
      rv = GetDefaultOIDFormat(&current->name.OthName.oid, key, ' ');
      if (NS_FAILED(rv))
	goto finish;
      ProcessRawBytes(nssComponent, &current->name.OthName.name, value);
    }
    break;
  }
  case certRFC822Name:
    nssComponent->GetPIPNSSBundleString("CertDumpRFC822Name", key);
    value.AssignASCII((char*)current->name.other.data, current->name.other.len);
    break;
  case certDNSName:
    nssComponent->GetPIPNSSBundleString("CertDumpDNSName", key);
    value.AssignASCII((char*)current->name.other.data, current->name.other.len);
    break;
  case certX400Address:
    nssComponent->GetPIPNSSBundleString("CertDumpX400Address", key);
    ProcessRawBytes(nssComponent, &current->name.other, value);
    break;
  case certDirectoryName:
    nssComponent->GetPIPNSSBundleString("CertDumpDirectoryName", key);
    rv = ProcessName(&current->name.directoryName, nssComponent, 
		     getter_Copies(value));
    if (NS_FAILED(rv))
      goto finish;
    break;
  case certEDIPartyName:
    nssComponent->GetPIPNSSBundleString("CertDumpEDIPartyName", key);
    ProcessRawBytes(nssComponent, &current->name.other, value);
    break;
  case certURI:
    nssComponent->GetPIPNSSBundleString("CertDumpURI", key);
    value.AssignASCII((char*)current->name.other.data, current->name.other.len);
    break;
  case certIPAddress:
    {
      char buf[INET6_ADDRSTRLEN];
      PRStatus status = PR_FAILURE;
      PRNetAddr addr;
      memset(&addr, 0, sizeof(addr));
      nssComponent->GetPIPNSSBundleString("CertDumpIPAddress", key);
      if (current->name.other.len == 4) {
        addr.inet.family = PR_AF_INET;
        memcpy(&addr.inet.ip, current->name.other.data, current->name.other.len);
        status = PR_NetAddrToString(&addr, buf, sizeof(buf));
      } else if (current->name.other.len == 16) {
        addr.ipv6.family = PR_AF_INET6;
        memcpy(&addr.ipv6.ip, current->name.other.data, current->name.other.len);
        status = PR_NetAddrToString(&addr, buf, sizeof(buf));
      }
      if (status == PR_SUCCESS) {
        value.AssignASCII(buf);
      } else {
        /* invalid IP address */
        ProcessRawBytes(nssComponent, &current->name.other, value);
      }
      break;
    }
  case certRegisterID:
    nssComponent->GetPIPNSSBundleString("CertDumpRegisterID", key);
    rv = GetDefaultOIDFormat(&current->name.other, value, '.');
    if (NS_FAILED(rv))
      goto finish;
    break;
  }
  text.Append(key);
  text.Append(NS_LITERAL_STRING(": "));
  text.Append(value);
  text.Append(NS_LITERAL_STRING(SEPARATOR));
 finish:
    return rv;
}

static nsresult
ProcessGeneralNames(PRArenaPool *arena,
		    CERTGeneralName *nameList,
		    nsAString &text,
		    nsINSSComponent *nssComponent)
{
  CERTGeneralName *current = nameList;
  nsresult rv;

  do {
    rv = ProcessGeneralName(arena, current, text, nssComponent);
    if (NS_FAILED(rv))
      break;
    current = CERT_GetNextGeneralName(current);
  } while (current != nameList);
  return rv;
}

static nsresult
ProcessAltName(SECItem  *extData, 
	       nsAString &text,
	       nsINSSComponent *nssComponent)
{
  nsresult rv = NS_OK;
  PRArenaPool *arena;
  CERTGeneralName *nameList;

  arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  if (!arena)
    return NS_ERROR_FAILURE;

  nameList = CERT_DecodeAltNameExtension(arena, extData);
  if (!nameList)
    goto finish;

  rv = ProcessGeneralNames(arena, nameList, text, nssComponent);

 finish:
  PORT_FreeArena(arena, PR_FALSE);
  return rv;
}

static nsresult
ProcessSubjectKeyId(SECItem  *extData, 
		    nsAString &text,
		    nsINSSComponent *nssComponent)
{
  PRArenaPool *arena;
  nsresult rv = NS_OK;
  SECItem decoded;
  nsAutoString local;

  arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  if (!arena)
    return NS_ERROR_FAILURE;

  if (SEC_QuickDERDecodeItem(arena, &decoded, 
			     SEC_ASN1_GET(SEC_OctetStringTemplate), 
			     extData) != SECSuccess) {
    rv = NS_ERROR_FAILURE;
    goto finish;
  }
  
  nssComponent->GetPIPNSSBundleString("CertDumpKeyID", local);
  text.Append(local);
  text.Append(NS_LITERAL_STRING(": "));
  ProcessRawBytes(nssComponent, &decoded, text);

 finish:
  PORT_FreeArena(arena, PR_FALSE);
  return rv;
}

static nsresult
ProcessAuthKeyId(SECItem  *extData, 
		 nsAString &text,
		 nsINSSComponent *nssComponent)
{
  CERTAuthKeyID *ret;
  PRArenaPool *arena;
  nsresult rv = NS_OK;
  nsAutoString local;

  arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  if (!arena)
    return NS_ERROR_FAILURE;

  ret = CERT_DecodeAuthKeyID (arena, extData);

  if (ret->keyID.len > 0) {
    nssComponent->GetPIPNSSBundleString("CertDumpKeyID", local);
    text.Append(local);
    text.Append(NS_LITERAL_STRING(": "));
    ProcessRawBytes(nssComponent, &ret->keyID, text);
    text.Append(NS_LITERAL_STRING(SEPARATOR));
  }

  if (ret->authCertIssuer) {
    nssComponent->GetPIPNSSBundleString("CertDumpIssuer", local);
    text.Append(local);
    text.Append(NS_LITERAL_STRING(": "));
    rv = ProcessGeneralNames(arena, ret->authCertIssuer, text, nssComponent);
    if (NS_FAILED(rv))
      goto finish;
  }

  if (ret->authCertSerialNumber.len > 0) {
    nssComponent->GetPIPNSSBundleString("CertDumpSerialNo", local);
    text.Append(local);
    text.Append(NS_LITERAL_STRING(": "));
    ProcessRawBytes(nssComponent, &ret->authCertSerialNumber, text);
  }

 finish:
  PORT_FreeArena(arena, PR_FALSE);
  return rv;
}

enum DisplayTextForm { VisibleForm, BMPForm, UTF8Form };

struct DisplayText {
  DisplayTextForm variant;
  SECItem value;
};

const SEC_ASN1Template DisplayTextTemplate[] = {
    { SEC_ASN1_CHOICE,
      offsetof(DisplayText, variant), NULL,
      sizeof(DisplayText) },
    { SEC_ASN1_IA5_STRING, 
      offsetof(DisplayText, value), NULL, VisibleForm },
    { SEC_ASN1_VISIBLE_STRING, 
      offsetof(DisplayText, value), NULL, VisibleForm },
    { SEC_ASN1_BMP_STRING, 
      offsetof(DisplayText, value), NULL, BMPForm },
    { SEC_ASN1_UTF8_STRING, 
      offsetof(DisplayText, value), NULL, UTF8Form },
    { 0 }
};

static nsresult
ProcessUserNotice(SECItem *der_notice,
		  nsAString &text,
		  nsINSSComponent *nssComponent)
{
  nsresult rv = NS_OK;
  CERTUserNotice *notice = NULL;
  SECItem **itemList;
  DisplayText display;
  PRArenaPool *arena;
  char *buf;

  arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  if (!arena)
    return NS_ERROR_FAILURE;

  notice = CERT_DecodeUserNotice(der_notice);
  /* XXX: currently, polcyxtn.c assumes that organization
     is an IA5String, whereas it really ought to be a
     choice of VisibleString, BMPString, and UTF8String.
     So if decoding of the user notice fails, it is most likely
     that the organization was encoded in an unexpected way.
     Make this function return "something" in this case,
     instead letting the decode for the entire certificate
     fail.
  */
  if (notice == NULL) {
    text.Append(NS_LITERAL_STRING("<implementation limitation>"));
    goto finish;
  }

  if (notice->noticeReference.organization.len != 0) {
    rv = ProcessIA5String(&notice->noticeReference.organization,
			  text, nssComponent);
    if (NS_FAILED(rv))
      goto finish;

    itemList = notice->noticeReference.noticeNumbers;
    while (*itemList) {
      unsigned long number;
      char buffer[60];
      if (SEC_ASN1DecodeInteger(*itemList, &number) != SECSuccess) {
	rv = NS_ERROR_FAILURE;
	goto finish;
      }
      PR_snprintf(buffer, sizeof(buffer), "%d ", number);
      AppendASCIItoUTF16(buffer, text);
      itemList++;
    }
  }
  if (notice->displayText.len != 0) {
    if (SEC_QuickDERDecodeItem(arena, &display,
			       DisplayTextTemplate,
			       &notice->displayText) != SECSuccess) {
      rv = NS_ERROR_FAILURE;
      goto finish;
    }
    switch (display.variant) {
    case VisibleForm:
      /* Need to null-terminate string before appending it. */
      buf = (char*)PORT_ArenaAlloc(arena, display.value.len+1);
      PORT_Memcpy(buf, display.value.data, display.value.len);
      buf[display.value.len] = '\0';
      text.AppendASCII(buf);
      break;
    case BMPForm:
      AppendBMPtoUTF16(arena, display.value.data, display.value.len,
		       text);
      break;
    case UTF8Form:
      buf = (char*)PORT_ArenaAlloc(arena, display.value.len+1);
      PORT_Memcpy(buf, display.value.data, display.value.len);
      buf[display.value.len] = '\0';
      AppendUTF8toUTF16(buf, text);
      break;
    }
  }
 finish:
  if (notice)
    CERT_DestroyUserNotice(notice);
  PORT_FreeArena(arena, PR_FALSE);
  return rv;
}

static nsresult
ProcessCertificatePolicies(SECItem  *extData, 
			   nsAString &text,
			   nsINSSComponent *nssComponent)
{
  CERTCertificatePolicies *policies;
  CERTPolicyInfo **policyInfos, *policyInfo;
  CERTPolicyQualifier **policyQualifiers, *policyQualifier;
  nsAutoString local;
  nsresult rv = NS_OK;

  policies = CERT_DecodeCertificatePoliciesExtension(extData);
  if ( policies == NULL )
    return NS_ERROR_FAILURE;

  policyInfos = policies->policyInfos;
  while (*policyInfos != NULL ) {
    policyInfo = *policyInfos++;
    switch (policyInfo->oid) {
    case SEC_OID_VERISIGN_USER_NOTICES:
      nssComponent->GetPIPNSSBundleString("CertDumpVerisignNotices", local);
      text.Append(local);
      break;
    default:
      GetDefaultOIDFormat(&policyInfo->policyID, local, '.');
      text.Append(local);
    }
    if (policyInfo->policyQualifiers) {
      /* Add all qualifiers on separate lines, indented */
      policyQualifiers = policyInfo->policyQualifiers;
      text.Append(NS_LITERAL_STRING(":"));
      text.Append(NS_LITERAL_STRING(SEPARATOR));
      while (*policyQualifiers != NULL) {
	text.Append(NS_LITERAL_STRING("  "));
	policyQualifier = *policyQualifiers++;
	switch(policyQualifier->oid) {
	case SEC_OID_PKIX_CPS_POINTER_QUALIFIER:
	  nssComponent->GetPIPNSSBundleString("CertDumpCPSPointer", local);
	  text.Append(local);
	  text.Append(NS_LITERAL_STRING(":"));
	  text.Append(NS_LITERAL_STRING(SEPARATOR));
	  text.Append(NS_LITERAL_STRING("    "));
	  /* The CPS pointer ought to be the cPSuri alternative
	     of the Qualifier choice. */
	  rv = ProcessIA5String(&policyQualifier->qualifierValue,
				text, nssComponent);
	  if (NS_FAILED(rv))
	    goto finish;
	  break;
	case SEC_OID_PKIX_USER_NOTICE_QUALIFIER:
	  nssComponent->GetPIPNSSBundleString("CertDumpUserNotice", local);
	  text.Append(local);
	  text.Append(NS_LITERAL_STRING(": "));
	  rv = ProcessUserNotice(&policyQualifier->qualifierValue,
				 text, nssComponent);
	  break;
	default:
	  GetDefaultOIDFormat(&policyQualifier->qualifierID, local, '.');
	  text.Append(local);
	  text.Append(NS_LITERAL_STRING(": "));
	  ProcessRawBytes(nssComponent, &policyQualifier->qualifierValue, text);
	}
	text.Append(NS_LITERAL_STRING(SEPARATOR));
      } /* while policyQualifiers */
    } /* if policyQualifiers */
    text.Append(NS_LITERAL_STRING(SEPARATOR));
  }

 finish:
  CERT_DestroyCertificatePoliciesExtension(policies);
  return rv;
}

static nsresult
ProcessCrlDistPoints(SECItem  *extData, 
		     nsAString &text,
		     nsINSSComponent *nssComponent)
{
  CERTCrlDistributionPoints *crldp;
  CRLDistributionPoint **points, *point;
  PRArenaPool *arena;
  nsresult rv = NS_OK;
  nsAutoString local;
  int reasons, comma;

  arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  if (!arena)
    return NS_ERROR_FAILURE;

  crldp = CERT_DecodeCRLDistributionPoints(arena, extData);
  if (!crldp || !crldp->distPoints) {
    rv = NS_ERROR_FAILURE;
    goto finish;
  }

  for(points = crldp->distPoints; *points; points++) {
    point = *points;
    switch (point->distPointType) {
    case generalName:
      rv = ProcessGeneralName(arena, point->distPoint.fullName,
			      text, nssComponent);
      if (NS_FAILED(rv))
	goto finish;
      break;
    case relativeDistinguishedName:
      rv = ProcessRDN(&point->distPoint.relativeName, 
		      text, nssComponent);
      if (NS_FAILED(rv))
	goto finish;
      break;
    }
    if (point->reasons.len) { 
      reasons = point->reasons.data[0];
      text.Append(NS_LITERAL_STRING(" "));
      comma = 0;
      if (reasons & RF_UNUSED) {
	nssComponent->GetPIPNSSBundleString("CertDumpUnused", local);
	text.Append(local); comma = 1;
      }
      if (reasons & RF_KEY_COMPROMISE) {
	if (comma) text.Append(NS_LITERAL_STRING(", "));
	nssComponent->GetPIPNSSBundleString("CertDumpKeyCompromise", local);
	text.Append(local); comma = 1;
      }
      if (reasons & RF_CA_COMPROMISE) {
	if (comma) text.Append(NS_LITERAL_STRING(", "));
	nssComponent->GetPIPNSSBundleString("CertDumpCACompromise", local);
	text.Append(local); comma = 1;
      }
      if (reasons & RF_AFFILIATION_CHANGED) {
	if (comma) text.Append(NS_LITERAL_STRING(", "));
	nssComponent->GetPIPNSSBundleString("CertDumpAffiliationChanged", local);
	text.Append(local); comma = 1;
      }
      if (reasons & RF_SUPERSEDED) {
	if (comma) text.Append(NS_LITERAL_STRING(", "));
	nssComponent->GetPIPNSSBundleString("CertDumpSuperseded", local);
	text.Append(local); comma = 1;
      }
      if (reasons & RF_CESSATION_OF_OPERATION) {
	if (comma) text.Append(NS_LITERAL_STRING(", "));
	nssComponent->GetPIPNSSBundleString("CertDumpCessation", local);
	text.Append(local); comma = 1;
      }
      if (reasons & RF_CERTIFICATE_HOLD) {
	if (comma) text.Append(NS_LITERAL_STRING(", "));
	nssComponent->GetPIPNSSBundleString("CertDumpHold", local);
	text.Append(local); comma = 1;
      }
      text.Append(NS_LITERAL_STRING(SEPARATOR));
    }
    if (point->crlIssuer) {
      nssComponent->GetPIPNSSBundleString("CertDumpIssuer", local);
      text.Append(local);
      text.Append(NS_LITERAL_STRING(": "));
      rv = ProcessGeneralNames(arena, point->crlIssuer,
			       text, nssComponent);
      if (NS_FAILED(rv))
	goto finish;
    }
  }
  
 finish:
  PORT_FreeArena(arena, PR_FALSE);
  return NS_OK;
}

static nsresult
ProcessAuthInfoAccess(SECItem  *extData, 
		      nsAString &text,
		      nsINSSComponent *nssComponent)
{
  CERTAuthInfoAccess **aia, *desc;
  PRArenaPool *arena;
  nsresult rv = NS_OK;
  nsAutoString local;

  arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  if (!arena)
    return NS_ERROR_FAILURE;

  aia = CERT_DecodeAuthInfoAccessExtension(arena, extData);
  if (aia == NULL)
    goto finish;

  while (*aia != NULL) {
    desc = *aia++;
    switch (SECOID_FindOIDTag(&desc->method)) {
    case SEC_OID_PKIX_OCSP:
      nssComponent->GetPIPNSSBundleString("CertDumpOCSPResponder", local);
      break;
    case SEC_OID_PKIX_CA_ISSUERS:
      nssComponent->GetPIPNSSBundleString("CertDumpCAIssuers", local);
      break;
    default:
      rv = GetDefaultOIDFormat(&desc->method, local, '.');
      if (NS_FAILED(rv))
	goto finish;
    }
    text.Append(local);
    text.Append(NS_LITERAL_STRING(": "));
    rv = ProcessGeneralName(arena, desc->location, text, nssComponent);
    if (NS_FAILED(rv))
      goto finish;
  }

 finish:
  PORT_FreeArena(arena, PR_FALSE);
  return rv;
}

static nsresult
ProcessMSCAVersion(SECItem  *extData, 
		   nsAString &text,
		   nsINSSComponent *nssComponent)
{
  unsigned long version;
  nsresult rv;
  char buf[50];
  SECItem decoded;

  if (SECSuccess != SEC_ASN1DecodeItem(nsnull, &decoded, 
				       SEC_ASN1_GET(SEC_IntegerTemplate), 
				       extData))
    /* This extension used to be an Integer when this code
       was written, but apparently isn't anymore. Display
       the raw bytes instead. */
    return ProcessRawBytes(nssComponent, extData, text);

  rv = GetIntValue(&decoded, &version);
  nsMemory::Free(decoded.data);
  if (NS_FAILED(rv))
    /* Value out of range, display raw bytes */
    return ProcessRawBytes(nssComponent, extData, text);

  /* Apparently, the encoding is <minor><major>, with 16 bits each */
  PR_snprintf(buf, sizeof(buf), "%d.%d", version & 0xFFFF, version>>16);
  text.AppendASCII(buf);
  return NS_OK;
}

static nsresult
ProcessExtensionData(SECOidTag oidTag, SECItem *extData, 
                     nsAString &text, nsINSSComponent *nssComponent)
{
  nsresult rv;
  switch (oidTag) {
  case SEC_OID_NS_CERT_EXT_CERT_TYPE:
    rv = ProcessNSCertTypeExtensions(extData, text, nssComponent);
    break;
  case SEC_OID_X509_KEY_USAGE:
    rv = ProcessKeyUsageExtension(extData, text, nssComponent);
    break;
  case SEC_OID_X509_BASIC_CONSTRAINTS:
    rv = ProcessBasicConstraints(extData, text, nssComponent);
    break;
  case SEC_OID_X509_EXT_KEY_USAGE:
    rv = ProcessExtKeyUsage(extData, text, nssComponent);
    break;
  case SEC_OID_X509_ISSUER_ALT_NAME:
  case SEC_OID_X509_SUBJECT_ALT_NAME:
    rv = ProcessAltName(extData, text, nssComponent);
    break;
  case SEC_OID_X509_SUBJECT_KEY_ID:
    rv = ProcessSubjectKeyId(extData, text, nssComponent);
    break;
  case SEC_OID_X509_AUTH_KEY_ID:
    rv = ProcessAuthKeyId(extData, text, nssComponent);
    break;
  case SEC_OID_X509_CERTIFICATE_POLICIES:
    rv = ProcessCertificatePolicies(extData, text, nssComponent);
    break;
  case SEC_OID_X509_CRL_DIST_POINTS:
    rv = ProcessCrlDistPoints(extData, text, nssComponent);
    break;
  case SEC_OID_X509_AUTH_INFO_ACCESS:
    rv = ProcessAuthInfoAccess(extData, text, nssComponent);
    break;
  case SEC_OID_NS_CERT_EXT_BASE_URL:
  case SEC_OID_NS_CERT_EXT_REVOCATION_URL:
  case SEC_OID_NS_CERT_EXT_CA_REVOCATION_URL:
  case SEC_OID_NS_CERT_EXT_CA_CERT_URL:
  case SEC_OID_NS_CERT_EXT_CERT_RENEWAL_URL:
  case SEC_OID_NS_CERT_EXT_CA_POLICY_URL:
  case SEC_OID_NS_CERT_EXT_HOMEPAGE_URL:
  case SEC_OID_NS_CERT_EXT_COMMENT:
  case SEC_OID_NS_CERT_EXT_SSL_SERVER_NAME:
  case SEC_OID_NS_CERT_EXT_LOST_PASSWORD_URL:
    rv = ProcessIA5String(extData, text, nssComponent);
    break;
  default:
    if (oidTag == SEC_OID(MS_CERT_EXT_CERTTYPE)) {
      rv = ProcessBMPString(extData, text, nssComponent);
      break;
    }
    if (oidTag == SEC_OID(MS_CERTSERV_CA_VERSION)) {
      rv = ProcessMSCAVersion(extData, text, nssComponent);
      break;
    }
    rv = ProcessRawBytes(nssComponent, extData, text);
    break; 
  }
  return rv;
}

static nsresult
ProcessSingleExtension(CERTCertExtension *extension,
                       nsINSSComponent *nssComponent,
                       nsIASN1PrintableItem **retExtension)
{
  nsAutoString text;
  GetOIDText(&extension->id, nssComponent, text);
  nsCOMPtr<nsIASN1PrintableItem>extensionItem = new nsNSSASN1PrintableItem();
  if (extensionItem == nsnull)
    return NS_ERROR_OUT_OF_MEMORY;

  extensionItem->SetDisplayName(text);
  SECOidTag oidTag = SECOID_FindOIDTag(&extension->id);
  text.Truncate();
  if (extension->critical.data != nsnull) {
    if (extension->critical.data[0]) {
      nssComponent->GetPIPNSSBundleString("CertDumpCritical", text);
    } else {
      nssComponent->GetPIPNSSBundleString("CertDumpNonCritical", text);
    }
  } else {
    nssComponent->GetPIPNSSBundleString("CertDumpNonCritical", text);
  }
  text.Append(NS_LITERAL_STRING(SEPARATOR).get());
  nsresult rv = ProcessExtensionData(oidTag, &extension->value, text, 
                                     nssComponent);
  if (NS_FAILED(rv))
    return rv;

  extensionItem->SetDisplayValue(text);
  *retExtension = extensionItem;
  NS_ADDREF(*retExtension);
  return NS_OK;
}

static nsresult
ProcessSECAlgorithmID(SECAlgorithmID *algID,
                      nsINSSComponent *nssComponent,
                      nsIASN1Sequence **retSequence)
{
  SECOidTag algOIDTag = SECOID_FindOIDTag(&algID->algorithm);
  SECItem paramsOID = { siBuffer, NULL, 0 };
  nsCOMPtr<nsIASN1Sequence> sequence = new nsNSSASN1Sequence();
  if (sequence == nsnull)
    return NS_ERROR_OUT_OF_MEMORY;

  *retSequence = nsnull;
  nsString text;
  GetOIDText(&algID->algorithm, nssComponent, text);
  if (!algID->parameters.len || algID->parameters.data[0] == nsIASN1Object::ASN1_NULL) {
    sequence->SetDisplayValue(text);
    sequence->SetIsValidContainer(PR_FALSE);
  } else {
    nsCOMPtr<nsIASN1PrintableItem> printableItem = new nsNSSASN1PrintableItem();
    if (printableItem == nsnull)
      return NS_ERROR_OUT_OF_MEMORY;

    printableItem->SetDisplayValue(text);
    nsCOMPtr<nsIMutableArray> asn1Objects;
    sequence->GetASN1Objects(getter_AddRefs(asn1Objects));
    asn1Objects->AppendElement(printableItem, PR_FALSE);
    nssComponent->GetPIPNSSBundleString("CertDumpAlgID", text);
    printableItem->SetDisplayName(text);

    printableItem = new nsNSSASN1PrintableItem();
    if (printableItem == nsnull)
      return NS_ERROR_OUT_OF_MEMORY;

    asn1Objects->AppendElement(printableItem, PR_FALSE);
    nssComponent->GetPIPNSSBundleString("CertDumpParams", text);
    printableItem->SetDisplayName(text);
    if ((algOIDTag == SEC_OID_ANSIX962_EC_PUBLIC_KEY) &&
        (algID->parameters.len > 2) && 
        (algID->parameters.data[0] == nsIASN1Object::ASN1_OBJECT_ID)) {
       paramsOID.len = algID->parameters.len - 2;
       paramsOID.data = algID->parameters.data + 2;
       GetOIDText(&paramsOID, nssComponent, text);
    } else {
       ProcessRawBytes(nssComponent, &algID->parameters,text);
    }
    printableItem->SetDisplayValue(text);
  }
  *retSequence = sequence;
  NS_ADDREF(*retSequence);
  return NS_OK;
}

static nsresult
ProcessTime(PRTime dispTime, const PRUnichar *displayName, 
            nsIASN1Sequence *parentSequence)
{
  nsresult rv;
  nsCOMPtr<nsIDateTimeFormat> dateFormatter =
     do_CreateInstance(kDateTimeFormatCID, &rv);
  if (NS_FAILED(rv)) 
    return rv;

  nsString text;
  nsString tempString;

  PRExplodedTime explodedTime;
  PR_ExplodeTime(dispTime, PR_LocalTimeParameters, &explodedTime);

  dateFormatter->FormatPRExplodedTime(nsnull, kDateFormatShort, kTimeFormatSecondsForce24Hour,
                              &explodedTime, tempString);

  text.Append(tempString);
  text.AppendLiteral("\n(");

  PRExplodedTime explodedTimeGMT;
  PR_ExplodeTime(dispTime, PR_GMTParameters, &explodedTimeGMT);

  dateFormatter->FormatPRExplodedTime(nsnull, kDateFormatShort, kTimeFormatSecondsForce24Hour,
                              &explodedTimeGMT, tempString);

  text.Append(tempString);
  text.Append(NS_LITERAL_STRING(" GMT)"));

  nsCOMPtr<nsIASN1PrintableItem> printableItem = new nsNSSASN1PrintableItem();
  if (printableItem == nsnull)
    return NS_ERROR_OUT_OF_MEMORY;

  printableItem->SetDisplayValue(text);
  printableItem->SetDisplayName(nsDependentString(displayName));
  nsCOMPtr<nsIMutableArray> asn1Objects;
  parentSequence->GetASN1Objects(getter_AddRefs(asn1Objects));
  asn1Objects->AppendElement(printableItem, PR_FALSE);
  return NS_OK;
}

static nsresult
ProcessSubjectPublicKeyInfo(CERTSubjectPublicKeyInfo *spki, 
                            nsIASN1Sequence *parentSequence,
                            nsINSSComponent *nssComponent)
{
  nsCOMPtr<nsIASN1Sequence> spkiSequence = new nsNSSASN1Sequence();

  if (spkiSequence == nsnull)
    return NS_ERROR_OUT_OF_MEMORY;

  nsString text;
  nssComponent->GetPIPNSSBundleString("CertDumpSPKI", text);
  spkiSequence->SetDisplayName(text);

  nssComponent->GetPIPNSSBundleString("CertDumpSPKIAlg", text);
  nsCOMPtr<nsIASN1Sequence> sequenceItem;
  nsresult rv = ProcessSECAlgorithmID(&spki->algorithm, nssComponent,
                                      getter_AddRefs(sequenceItem));
  if (NS_FAILED(rv))
    return rv;
  sequenceItem->SetDisplayName(text);
  nsCOMPtr<nsIMutableArray> asn1Objects;
  spkiSequence->GetASN1Objects(getter_AddRefs(asn1Objects));
  asn1Objects->AppendElement(sequenceItem, PR_FALSE);

  // The subjectPublicKey field is encoded as a bit string.
  // ProcessRawBytes expects the lenght to be in bytes, so 
  // let's convert the lenght into a temporary SECItem.
  SECItem data;
  data.data = spki->subjectPublicKey.data;
  data.len  = spki->subjectPublicKey.len / 8;
  text.Truncate();
  ProcessRawBytes(nssComponent, &data, text);
  nsCOMPtr<nsIASN1PrintableItem> printableItem = new nsNSSASN1PrintableItem();
  if (printableItem == nsnull)
    return NS_ERROR_OUT_OF_MEMORY;

  printableItem->SetDisplayValue(text);
  nssComponent->GetPIPNSSBundleString("CertDumpSubjPubKey", text);
  printableItem->SetDisplayName(text);
  asn1Objects->AppendElement(printableItem, PR_FALSE);
  
  parentSequence->GetASN1Objects(getter_AddRefs(asn1Objects));
  asn1Objects->AppendElement(spkiSequence, PR_FALSE);
  return NS_OK;
}

static nsresult
ProcessExtensions(CERTCertExtension **extensions, 
                  nsIASN1Sequence *parentSequence, 
                  nsINSSComponent *nssComponent)
{
  nsCOMPtr<nsIASN1Sequence> extensionSequence = new nsNSSASN1Sequence;
  if (extensionSequence == nsnull)
    return NS_ERROR_OUT_OF_MEMORY;

  nsString text;
  nssComponent->GetPIPNSSBundleString("CertDumpExtensions", text);
  extensionSequence->SetDisplayName(text);
  PRInt32 i;
  nsresult rv;
  nsCOMPtr<nsIASN1PrintableItem> newExtension;
  nsCOMPtr<nsIMutableArray> asn1Objects;
  extensionSequence->GetASN1Objects(getter_AddRefs(asn1Objects));
  for (i=0; extensions[i] != nsnull; i++) {
    rv = ProcessSingleExtension(extensions[i], nssComponent,
                                getter_AddRefs(newExtension));
    if (NS_FAILED(rv))
      return rv;

    asn1Objects->AppendElement(newExtension, PR_FALSE);
  }
  parentSequence->GetASN1Objects(getter_AddRefs(asn1Objects));
  asn1Objects->AppendElement(extensionSequence, PR_FALSE);
  return NS_OK;
}

static bool registered;
static SECStatus RegisterDynamicOids()
{
  unsigned int i;
  SECStatus rv = SECSuccess;

  if (registered)
    return rv;

  for (i = 0; i < numOids; i++) {
    SECOidTag tag = SECOID_AddEntry(&more_oids[i]);
    if (tag == SEC_OID_UNKNOWN) {
      rv = SECFailure;
      continue;
    }
    more_oids[i].offset = tag;
  }
  registered = true;
  return rv;
}

nsresult
nsNSSCertificate::CreateTBSCertificateASN1Struct(nsIASN1Sequence **retSequence,
                                                 nsINSSComponent *nssComponent)
{
  nsNSSShutDownPreventionLock locker;
  if (isAlreadyShutDown())
    return NS_ERROR_NOT_AVAILABLE;

  if (RegisterDynamicOids() != SECSuccess)
    return NS_ERROR_FAILURE;

  //
  //   TBSCertificate  ::=  SEQUENCE  {
  //        version         [0]  EXPLICIT Version DEFAULT v1,
  //        serialNumber         CertificateSerialNumber,
  //        signature            AlgorithmIdentifier,
  //        issuer               Name,
  //        validity             Validity,
  //        subject              Name,
  //        subjectPublicKeyInfo SubjectPublicKeyInfo,
  //        issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL,
  //                             -- If present, version shall be v2 or v3
  //        subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL,
  //                             -- If present, version shall be v2 or v3
  //        extensions      [3]  EXPLICIT Extensions OPTIONAL
  //                            -- If present, version shall be v3
  //        }
  //
  // This is the ASN1 structure we should be dealing with at this point.
  // The code in this method will assert this is the structure we're dealing
  // and then add more user friendly text for that field.
  nsCOMPtr<nsIASN1Sequence> sequence = new nsNSSASN1Sequence();
  if (sequence == nsnull)
    return NS_ERROR_OUT_OF_MEMORY;

  nsString text;
  nssComponent->GetPIPNSSBundleString("CertDumpCertificate", text);
  sequence->SetDisplayName(text);
  nsCOMPtr<nsIASN1PrintableItem> printableItem;
  
  nsCOMPtr<nsIMutableArray> asn1Objects;
  sequence->GetASN1Objects(getter_AddRefs(asn1Objects));

  nsresult rv = ProcessVersion(&mCert->version, nssComponent,
                               getter_AddRefs(printableItem));
  if (NS_FAILED(rv))
    return rv;

  asn1Objects->AppendElement(printableItem, PR_FALSE);
  
  rv = ProcessSerialNumberDER(&mCert->serialNumber, nssComponent,
                              getter_AddRefs(printableItem));

  if (NS_FAILED(rv))
    return rv;
  asn1Objects->AppendElement(printableItem, PR_FALSE);

  nsCOMPtr<nsIASN1Sequence> algID;
  rv = ProcessSECAlgorithmID(&mCert->signature,
                             nssComponent, getter_AddRefs(algID));
  if (NS_FAILED(rv))
    return rv;

  nssComponent->GetPIPNSSBundleString("CertDumpSigAlg", text);
  algID->SetDisplayName(text);
  asn1Objects->AppendElement(algID, PR_FALSE);

  nsXPIDLString value;
  ProcessName(&mCert->issuer, nssComponent, getter_Copies(value));

  printableItem = new nsNSSASN1PrintableItem();
  if (printableItem == nsnull)
    return NS_ERROR_OUT_OF_MEMORY;

  printableItem->SetDisplayValue(value);
  nssComponent->GetPIPNSSBundleString("CertDumpIssuer", text);
  printableItem->SetDisplayName(text);
  asn1Objects->AppendElement(printableItem, PR_FALSE);
  
  nsCOMPtr<nsIASN1Sequence> validitySequence = new nsNSSASN1Sequence();
  nssComponent->GetPIPNSSBundleString("CertDumpValidity", text);
  validitySequence->SetDisplayName(text);
  asn1Objects->AppendElement(validitySequence, PR_FALSE);
  nssComponent->GetPIPNSSBundleString("CertDumpNotBefore", text);
  nsCOMPtr<nsIX509CertValidity> validityData;
  GetValidity(getter_AddRefs(validityData));
  PRTime notBefore, notAfter;

  validityData->GetNotBefore(&notBefore);
  validityData->GetNotAfter(&notAfter);
  validityData = 0;
  rv = ProcessTime(notBefore, text.get(), validitySequence);
  if (NS_FAILED(rv))
    return rv;

  nssComponent->GetPIPNSSBundleString("CertDumpNotAfter", text);
  rv = ProcessTime(notAfter, text.get(), validitySequence);
  if (NS_FAILED(rv))
    return rv;

  nssComponent->GetPIPNSSBundleString("CertDumpSubject", text);

  printableItem = new nsNSSASN1PrintableItem();
  if (printableItem == nsnull)
    return NS_ERROR_OUT_OF_MEMORY;

  printableItem->SetDisplayName(text);
  ProcessName(&mCert->subject, nssComponent,getter_Copies(value));
  printableItem->SetDisplayValue(value);
  asn1Objects->AppendElement(printableItem, PR_FALSE);

  rv = ProcessSubjectPublicKeyInfo(&mCert->subjectPublicKeyInfo, sequence,
                                   nssComponent); 
  if (NS_FAILED(rv))
    return rv;
 
  SECItem data; 
  // Is there an issuerUniqueID?
  if (mCert->issuerID.data != nsnull) {
    // The issuerID is encoded as a bit string.
    // The function ProcessRawBytes expects the
    // length to be in bytes, so let's convert the
    // length in a temporary SECItem
    data.data = mCert->issuerID.data;
    data.len  = mCert->issuerID.len / 8;

    ProcessRawBytes(nssComponent, &data, text);
    printableItem = new nsNSSASN1PrintableItem();
    if (printableItem == nsnull)
      return NS_ERROR_OUT_OF_MEMORY;

    printableItem->SetDisplayValue(text);
    nssComponent->GetPIPNSSBundleString("CertDumpIssuerUniqueID", text);
    printableItem->SetDisplayName(text);
    asn1Objects->AppendElement(printableItem, PR_FALSE);
  }

  if (mCert->subjectID.data) {
    // The subjectID is encoded as a bit string.
    // The function ProcessRawBytes expects the
    // length to be in bytes, so let's convert the
    // length in a temporary SECItem
    data.data = mCert->issuerID.data;
    data.len  = mCert->issuerID.len / 8;

    ProcessRawBytes(nssComponent, &data, text);
    printableItem = new nsNSSASN1PrintableItem();
    if (printableItem == nsnull)
      return NS_ERROR_OUT_OF_MEMORY;

    printableItem->SetDisplayValue(text);
    nssComponent->GetPIPNSSBundleString("CertDumpSubjectUniqueID", text);
    printableItem->SetDisplayName(text);
    asn1Objects->AppendElement(printableItem, PR_FALSE);

  }
  if (mCert->extensions) {
    rv = ProcessExtensions(mCert->extensions, sequence, nssComponent);
    if (NS_FAILED(rv))
      return rv;
  }
  *retSequence = sequence;
  NS_ADDREF(*retSequence);  
  return NS_OK;
}

nsresult
nsNSSCertificate::CreateASN1Struct()
{
  nsNSSShutDownPreventionLock locker;
  if (isAlreadyShutDown())
    return NS_ERROR_NOT_AVAILABLE;

  nsCOMPtr<nsIASN1Sequence> sequence = new nsNSSASN1Sequence();

  mASN1Structure = sequence; 
  if (mASN1Structure == nsnull) {
    return NS_ERROR_OUT_OF_MEMORY;
  }

  nsCOMPtr<nsIMutableArray> asn1Objects;
  sequence->GetASN1Objects(getter_AddRefs(asn1Objects));
  nsXPIDLCString title;
  GetWindowTitle(getter_Copies(title));
  
  mASN1Structure->SetDisplayName(NS_ConvertUTF8toUCS2(title));
  // This sequence will be contain the tbsCertificate, signatureAlgorithm,
  // and signatureValue.
  nsresult rv;
  nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
  if (NS_FAILED(rv))
    return rv;

  rv = CreateTBSCertificateASN1Struct(getter_AddRefs(sequence),
                                      nssComponent);
  if (NS_FAILED(rv))
    return rv;

  asn1Objects->AppendElement(sequence, PR_FALSE);
  nsCOMPtr<nsIASN1Sequence> algID;

  rv = ProcessSECAlgorithmID(&mCert->signatureWrap.signatureAlgorithm, 
                             nssComponent, getter_AddRefs(algID));
  if (NS_FAILED(rv))
    return rv;
  nsString text;
  nssComponent->GetPIPNSSBundleString("CertDumpSigAlg", text);
  algID->SetDisplayName(text);
  asn1Objects->AppendElement(algID, PR_FALSE);
  nsCOMPtr<nsIASN1PrintableItem>printableItem = new nsNSSASN1PrintableItem();
  nssComponent->GetPIPNSSBundleString("CertDumpCertSig", text);
  printableItem->SetDisplayName(text);
  // The signatureWrap is encoded as a bit string.
  // The function ProcessRawBytes expects the
  // length to be in bytes, so let's convert the
  // length in a temporary SECItem
  SECItem temp;
  temp.data = mCert->signatureWrap.signature.data;
  temp.len  = mCert->signatureWrap.signature.len / 8;
  text.Truncate();
  ProcessRawBytes(nssComponent, &temp,text);
  printableItem->SetDisplayValue(text);
  asn1Objects->AppendElement(printableItem, PR_FALSE);
  return NS_OK;
}

PRUint32 
getCertType(CERTCertificate *cert)
{
  nsNSSCertTrust trust(cert->trust);
  if (cert->nickname && trust.HasAnyUser())
    return nsIX509Cert::USER_CERT;
  if (trust.HasAnyCA())
    return nsIX509Cert::CA_CERT;
  if (trust.HasPeer(PR_TRUE, PR_FALSE, PR_FALSE))
    return nsIX509Cert::SERVER_CERT;
  if (trust.HasPeer(PR_FALSE, PR_TRUE, PR_FALSE) && cert->emailAddr)
    return nsIX509Cert::EMAIL_CERT;
  if (CERT_IsCACert(cert,NULL))
    return nsIX509Cert::CA_CERT;
  if (cert->emailAddr)
    return nsIX509Cert::EMAIL_CERT;
  return nsIX509Cert::SERVER_CERT;
}

CERTCertNicknames *
getNSSCertNicknamesFromCertList(CERTCertList *certList)
{
  nsresult rv;

  nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
  if (NS_FAILED(rv))
    return nsnull;

  nsAutoString expiredString, notYetValidString;
  nsAutoString expiredStringLeadingSpace, notYetValidStringLeadingSpace;

  nssComponent->GetPIPNSSBundleString("NicknameExpired", expiredString);
  nssComponent->GetPIPNSSBundleString("NicknameNotYetValid", notYetValidString);

  expiredStringLeadingSpace.Append(NS_LITERAL_STRING(" "));
  expiredStringLeadingSpace.Append(expiredString);

  notYetValidStringLeadingSpace.Append(NS_LITERAL_STRING(" "));
  notYetValidStringLeadingSpace.Append(notYetValidString);

  NS_ConvertUCS2toUTF8 aUtf8ExpiredString(expiredStringLeadingSpace);
  NS_ConvertUCS2toUTF8 aUtf8NotYetValidString(notYetValidStringLeadingSpace);

  return CERT_NicknameStringsFromCertList(certList,
                                          NS_CONST_CAST(char*, aUtf8ExpiredString.get()),
                                          NS_CONST_CAST(char*, aUtf8NotYetValidString.get()));
  
}
