/* ***** 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
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 };

#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 Type", 
        CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION ),

    #define MS_NT_PRINCIPAL_NAME 1
    OD( msNTPrincipalName,
        "Microsoft NT User 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 ),
};

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;
  default: 
    if (oidTag == SEC_OID(MS_CERT_EXT_CERTTYPE)) {
      bundlekey = "CertDumpMSCerttype";
      break;
    }
    if (oidTag == SEC_OID(MS_CERTSERV_CA_VERSION)) {
      bundlekey = "CertDumpMSCAVersion";
      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(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.
  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("CertDumpKUCRLSign", 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;

  rv = CERT_DecodeBasicConstraintValue (&value, extData);
  if (rv != SECSuccess) {
    NS_ASSERTION(0,"Could not decode basic constraints");
    return NS_ERROR_FAILURE;
  }
  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 >= 0) {
    nsAutoString depth;
    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);
    if (NS_FAILED(rv))
      // If there is no bundle string, just display the OID itself
      rv = GetDefaultOIDFormat(oid, local, ' ');
    if (NS_FAILED(rv))
      return rv;

    text.Append(local.get());
    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(&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(&current->name.OthName.name, value);
	}
    } else {
      rv = GetDefaultOIDFormat(&current->name.OthName.oid, key, ' ');
      if (NS_FAILED(rv))
	goto finish;
      ProcessRawBytes(&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(&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(&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];
      PRNetAddr 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);
        PR_NetAddrToString(&addr, buf, sizeof(buf));
        value.AssignASCII(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);
        PR_NetAddrToString(&addr, buf, sizeof(buf));
        value.AssignASCII(buf);
      } else {
        /* invalid IP address */
        ProcessRawBytes(&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(&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(&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(&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_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(&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;
      }
    }
    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;
    }
    text.Append(NS_LITERAL_STRING(SEPARATOR));
  }
  
 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_CA_ISSUERS:
      nssComponent->GetPIPNSSBundleString("CertDumpCAIssuers", text);
      break;
    default:
      rv = GetDefaultOIDFormat(&desc->method, text, '.');
      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;
    text.Append(NS_LITERAL_STRING(SEPARATOR));
  }

 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];

  rv = GetIntValue(extData, &version);
  if (NS_FAILED(rv))
    return rv;

  PR_snprintf(buf, sizeof(buf), "0x%x", version);
  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(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)
{
  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();
    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();
    asn1Objects->AppendElement(printableItem, PR_FALSE);
    nssComponent->GetPIPNSSBundleString("CertDumpParams", text);
    printableItem->SetDisplayName(text); 
    ProcessRawBytes(&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(&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(&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(&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(&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()));
  
}
