/** BEGIN COPYRIGHT BLOCK
 * Copyright (C) 2001 Sun Microsystems, Inc.  Used by permission.
 * Copyright (C) 2005 Red Hat, Inc.
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; version 2
 * of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 * END COPYRIGHT BLOCK **/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <prtypes.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef __cplusplus
extern "C" {
#endif
#include "nss.h"
#include "pkcs11.h"
#include "ssl.h"
#include "cert.h"
#include "certdb.h"
#include "certt.h"
#include "key.h"
#include "secport.h"
#include "secder.h"
#include "plstr.h"
#include "prsystem.h"

#include "pk11func.h"
#include "base64.h"
#include "secoidt.h"
#include "secoid.h"
#include "cryptohi.h"
#include "prerror.h"
#include "secmod.h"
#include "seccomon.h"
#include "secasn1t.h"
#include "secder.h"
#include "secasn1.h"

#include "prio.h"
#include "prtypes.h"

#include "libadminutil/admutil.h"
#include "libadminutil/distadm.h"
#include "libadminutil/resource.h"
#include "libadmsslutil/admsslutil.h"
#include "libadmsslutil/certmgt.h"
#include "libadmsslutil/psetcssl.h"

#include "cgicommon.h"

#include "libadmin/libadmin.h"

#ifdef __cplusplus
}
#endif

#define DEFAULT_KEY_BITS    2048
#define MAX_KEY_BITS        4096

#define SUBJECT_NEW "Certificate request"
#define SUBJECT_OLD "Certificate renewal"

#define CERTREQ_NEW "NEW"
#define CERTREQ_OLD "RENEWAL"

#define TOTAL_BITS 180

/* Define parameters for key generation */

/* 2^16+1=0x10001  you can obtain more information about
   public exponent at WWW.RSA.COM */
#define DEFAULT_PUBLIC_EXPONENT          0x10001


/*#ifdef XP_UNIX*/
#define HEADER "-----BEGIN CERTIFICATE-----"
#define FOOTER "-----END CERTIFICATE-----"
#define HEADERREQUEST "-----BEGIN %s CERTIFICATE REQUEST-----\n"
#define FOOTERREQUEST "\n-----END %s CERTIFICATE REQUEST-----\n"
#define X509CRLHEADER "-----BEGIN X509 CRL-----"
#define X509CRLFOOTER "-----END X509 CRL-----"
#define CRLHEADER "-----BEGIN CERTIFICATE REVOCATION LIST-----\n"
#define CRLFOOTER "-----END CERTIFICATE REVOCATION LIST-----\n"
#define NEWCRLHEADER "-----BEGIN CRL-----"
#define NEWCRLFOOTER "-----END CRL-----"
/*#else
#define HEADER "-----BEGIN CERTIFICATE----\n"
#define FOOTER "\n-----END CERTIFICATE-----\n"
#define HEADERREQUEST "-----BEGIN %s CERTIFICATE REQUEST-----\n"
#define FOOTERREQUEST "\n-----END %s CERTIFICATE REQUEST-----\r\n"
#endif*/

int expired = 0;
CERTCertDBHandle *certdb = NULL;
char line[BIG_LINE];

static char *securitydir; /* based on the sie - security dir for ds or as */

#define RESOURCE_FILE "security"

/* main resource string */
#define DBT_PARAMETERMISSING      resource_key(RESOURCE_FILE, "1")
#define DBT_MISSING_THIS          resource_key(RESOURCE_FILE, "2")
#define DBT_INTERNAL_ERROR        resource_key(RESOURCE_FILE, "3")
#define DBT_ALLOCATE_ERROR        resource_key(RESOURCE_FILE, "4")
#define DBT_NO_OP                 resource_key(RESOURCE_FILE, "5")
#define DBT_OPEN_KEYDB_FAIL       resource_key(RESOURCE_FILE, "6")
#define DBT_OPEN_CERTDB_FAIL      resource_key(RESOURCE_FILE, "7")
#define DBT_INVALID_PWD           resource_key(RESOURCE_FILE, "9")
#define DBT_ERROR_OCCURED         resource_key(RESOURCE_FILE, "10")
#define DBT_READ_ALIAS            resource_key(RESOURCE_FILE, "11")

/* cert install */
#define DBT_NO_SLOT               resource_key(RESOURCE_FILE, "20")
#define DBT_DECODE_FAIL           resource_key(RESOURCE_FILE, "21")
#define DBT_NO_PRIVATE_KEY        resource_key(RESOURCE_FILE, "22")
#define DBT_INSTALL_FAIL          resource_key(RESOURCE_FILE, "23")
#define DBT_NO_PRIVATE_KEY_WHY    resource_key(RESOURCE_FILE, "24")
#define DBT_INVALID_DER_CERT      resource_key(RESOURCE_FILE, "25")
#define DBT_NO_CERT_IN_PACKAGE    resource_key(RESOURCE_FILE, "26")
#define DBT_INVALID_CA_CERT       resource_key(RESOURCE_FILE, "27")
#define DBT_CERT_CHAIN_ERR        resource_key(RESOURCE_FILE, "28")

/* cert request */
#define DBT_INVALID_KEY_PAIR     resource_key(RESOURCE_FILE, "30")
#define DBT_CSR_GEN_FAIL         resource_key(RESOURCE_FILE, "31")
#define DBT_DN_CONVERT_FAIL      resource_key(RESOURCE_FILE, "32")
#define DBT_PUB_KEY_GEN_FAIL     resource_key(RESOURCE_FILE, "33")

/* cert op */
#define DBT_CERT_NOT_FOUND       resource_key(RESOURCE_FILE, "40")
#define DBT_TRUST_SET_FAIL       resource_key(RESOURCE_FILE, "41")
#define DBT_CERT_LIST_FAIL       resource_key(RESOURCE_FILE, "42")
#define DBT_CERT_DELETE_FAIL     resource_key(RESOURCE_FILE, "43")
#define DBT_CRL_DELETE_FAIL      resource_key(RESOURCE_FILE, "44")
#define DBT_CRL_CKL_NOT_FOUND    resource_key(RESOURCE_FILE, "45")

/* module operation */
#define DBT_NO_FILE_EXISTS       resource_key(RESOURCE_FILE, "50")
#define DBT_MODUTIL_FAILURE      resource_key(RESOURCE_FILE, "51")
#define DBT_SUCCESS              resource_key(RESOURCE_FILE, "52")
#define DBT_MISSING_FILE         resource_key(RESOURCE_FILE, "53")
#define DBT_MISSING_FORMAT       resource_key(RESOURCE_FILE, "54")
#define DBT_INVALID_FORMAT       resource_key(RESOURCE_FILE, "55")
#define DBT_MISSING_MODULE_NAME  resource_key(RESOURCE_FILE, "56")
#define DBT_SUCCESS_REMOVED      resource_key(RESOURCE_FILE, "57")
#define DBT_FAIL_REMOVED         resource_key(RESOURCE_FILE, "58")
#define DBT_ADDITIONAL_INFO      resource_key(RESOURCE_FILE, "59")

/* possible parameters */
#define DBT_SIE                  resource_key(RESOURCE_FILE, "70")
#define DBT_PASSWORD             resource_key(RESOURCE_FILE, "71")
#define DBT_DN                   resource_key(RESOURCE_FILE, "72")
#define DBT_OP                   resource_key(RESOURCE_FILE, "73")
#define DBT_DER_CERT             resource_key(RESOURCE_FILE, "74")
#define DBT_CERT_NAME            resource_key(RESOURCE_FILE, "75")
#define DBT_TOKEN_NAME           resource_key(RESOURCE_FILE, "76")
#define DBT_TRUST                resource_key(RESOURCE_FILE, "77")
#define DBT_INSTALL_OP           resource_key(RESOURCE_FILE, "78")
#define DBT_CERTIFICATE_TYPE     resource_key(RESOURCE_FILE, "79")
#define DBT_CRL_NAME             resource_key(RESOURCE_FILE, "80")
#define DBT_MODULE_OP_TYPE       resource_key(RESOURCE_FILE, "81")
#define DBT_OLD_PWD              resource_key(RESOURCE_FILE, "82")
#define DBT_NEW_PWD              resource_key(RESOURCE_FILE, "83")
#define DBT_CONFIRM_PWD          resource_key(RESOURCE_FILE, "84")
#define DBT_SESSION_DATA         resource_key(RESOURCE_FILE, "85")
#define DBT_OLD_SERVER_ROOT      resource_key(RESOURCE_FILE, "86")
#define DBT_CERT_FINGERPRINT          resource_key(RESOURCE_FILE, "87")

/* change password and init pin*/
#define DBT_FAIL_CHANG_PWD       resource_key(RESOURCE_FILE, "100")
#define DBT_INVALID_OLD_PWD      resource_key(RESOURCE_FILE, "101")
#define DBT_INVALID_CONFIRM_PWD  resource_key(RESOURCE_FILE, "102")
#define DBT_INIT_FAIL            resource_key(RESOURCE_FILE, "103")

/* crl/ckl */
#define DBT_DECODE_CRL_ERROR     resource_key(RESOURCE_FILE, "110")
#define DBT_REMOVE_ERROR         resource_key(RESOURCE_FILE, "111")
#define DBT_INSTALL_ERROR        resource_key(RESOURCE_FILE, "112")
#define DBT_INVALID_CRL          resource_key(RESOURCE_FILE, "113")

/* key and certificate migration */
#define DBT_ALIAS                resource_key(RESOURCE_FILE, "120")
#define DBT_KEY_OR_CERT_NOTFOUND resource_key(RESOURCE_FILE, "121")
#define DBT_UNABLE_TO_MIGRATE    resource_key(RESOURCE_FILE, "122")
#define DBT_INVALID_SERVER_ROOT  resource_key(RESOURCE_FILE, "123")
#define DBT_MIGRATION_NOT_SUPPORTED  resource_key(RESOURCE_FILE, "124")

/* more module codes */
#define DBT_INVALID_OP           resource_key(RESOURCE_FILE, "150")

Resource       *i18nResource;
Resource       *i18nResource_common;
char           *acceptLanguage;

static char * cur_pwd = 0; /* for use in changePassword and getPassword_cb */

static char*
getResourceString(char *key) {
  if (key && !strncmp(key, COMMON_RESOURCE_FILE, strlen(COMMON_RESOURCE_FILE))) {
    return (char *)(res_getstring(i18nResource_common, key, acceptLanguage, NULL, 0, NULL));
  } else {
    return (char *)(res_getstring(i18nResource, key, acceptLanguage, NULL, 0, NULL));
  }
}

static char * getParameter(char *key, char *keyName) {

  PR_snprintf(line, sizeof(line), getResourceString(DBT_MISSING_THIS), keyName);

  return get_cgi_var(key, getResourceString(DBT_PARAMETERMISSING), line);
}

static void closeAllSecurityDB() {
  /* close all db */
  if (NSS_IsInitialized()) {
    SSL_ClearSessionCache();
  }
  if (NSS_Shutdown()) {
    PR_snprintf(line, sizeof(line), getResourceString(CMN_NSS_SHUTDOWN_ERROR),
                PR_GetError(),
                SSL_Strerror(PR_GetError()) ? SSL_Strerror(PR_GetError()) : "unknown");
    rpt_warning(GENERAL_FAILURE, line, NULL, NULL);
  }
}

static void errorAllocateMem() {
  rpt_err(MEMORY_ERROR, 
          getResourceString(DBT_INTERNAL_ERROR), 
          getResourceString(DBT_ALLOCATE_ERROR), 
          NULL);
}

static void errorRpt(int type, char* detailInfo) {
  closeAllSecurityDB();
  rpt_err(type, 
          getResourceString(DBT_ERROR_OCCURED), 
          detailInfo, 
          NULL);
}

static void errorRptExtended(int type, char* detail, char* extra) {
  closeAllSecurityDB();
  rpt_err(type, 
          getResourceString(DBT_ERROR_OCCURED), 
          detail, 
          extra);
}

/* return true if all of the chars in s are valid chars for use in
   a secmod module name.  This string is passed to the shell in
   double quotes, so the user should not have to pass in any odd
   chars or quoting.  Look for chars which would indicate someone
   is attempting something fishy.
*/
static int
is_valid_name_string(const char *s)
{
  int isvalid = 1;
  for(;isvalid && s && *s; ++s) {
    isvalid = (isalnum(*s) || (*s == ' ') || (*s == '.') || (*s == ',') ||
	       (*s == '+') || (*s == '=') || (*s == '@') || (*s == '-') ||
	       (*s == '_'));
  }
  return isvalid;
}

/*
** Convert a der-encoded integer to a hex printable string form
*/
static char *
Hexify(SECItem *i)
{
  unsigned char *cp, *end;
  char *rv, *o;

  /* Stuff copied from libsec/securl.c */
  char *hex = "0123456789ABCDEF";

  if (!i->len) {
    return PORT_Strdup("00");
  }

  rv = o = (char*) PORT_Alloc(i->len * 3);
  if (!rv) return rv;

  cp = i->data;
  end = cp + i->len;
  while (cp < end) {
    unsigned char ch = *cp++;
    *o++ = hex[(ch >> 4) & 0xf];
    *o++ = hex[ch & 0xf];
    if (cp != end) {
      *o++ = ':';
    } else {
      *o++ = 0;
    }
  }
  return rv;
}

/* Get a representative string for a given SECName */
PR_IMPLEMENT(char *) constructNameDesc(CERTName * name)
{
  char * s;

  s = CERT_GetCommonName(name);
  if (s == 0) {
    s = CERT_GetOrgUnitName(name);
    if (s == 0) {
      s = CERT_GetOrgName(name);
      if (s == 0) {
        CERT_GetLocalityName(name);
        if (s == 0) {
          s = CERT_GetStateName(name);
          if (s == 0) s = "";
        }
      }
    }
  }

  return PORT_Strdup(s);
}

/* HCL call pack function so we can pass back password */
static char *
getPassword_cb(PK11SlotInfo* slot, PRBool retry, void* wincx) {

  char *pwd=0;
  char *tokenname = PK11_GetTokenName(slot);
  char tokenpwd_var[BIG_LINE];

  /* The client might supply a password for a specific token. */
  PR_snprintf(tokenpwd_var, sizeof(tokenpwd_var), "%s.keypwd", tokenname);
  
  if (cur_pwd) {
     pwd = cur_pwd;
  }

  if (!pwd) {
    pwd = get_cgi_var(tokenpwd_var, NULL, NULL);
  }

  if (!pwd) {
    pwd =  get_cgi_var("keypwd", NULL, NULL);
  }

  if (!pwd) {
     char extra[BIG_LINE];
     PR_snprintf(extra, sizeof(extra), "SEC_Token: %s\nSEC_Error: NO_PASSWORD\n", tokenname);
     errorRptExtended(ELEM_MISSING, getResourceString(DBT_PASSWORD), extra);
     return 0;
  }

  if (PK11_CheckUserPassword(slot, pwd) != SECSuccess) {
    if (cur_pwd) {
      ;/* calling routine will handle this case */
    } else {
      char extra[BIG_LINE], detail[BIG_LINE];
      PR_snprintf(extra, sizeof(extra), "SEC_Token: %s\nSEC_Error: INVALID_PASSWORD\n", tokenname);
      PR_snprintf(detail, sizeof(detail), getResourceString(DBT_INVALID_PWD), tokenname);
      errorRptExtended(INCORRECT_USAGE, detail, extra);
      return 0;
    }
  }

  /* do not relogin if this is a retry */
  return retry ? 0 : pwd;
}

static void printCertUsageInfo(char* description, SECCertUsage usage, CERTCertificate* cert) {
  if (SECSuccess==CERT_VerifyCertNow(certdb, cert,
                                     PR_TRUE, usage, NULL)) {
    fprintf(stdout, "%s", description);
  }
}

static char *
formatDateTime(SECItem *timechoice)
{
  PRTime decodedTime = 0;

  /* decode the time into the integral value */
  if (SECSuccess != DER_DecodeTimeChoice(&decodedTime, timechoice)) {
    return NULL;
  }
  switch (timechoice->type) {
  case siUTCTime:
    return DER_UTCTimeToAscii(timechoice);
    break;
  case siGeneralizedTime:
    return CERT_GenTime2FormattedAscii(decodedTime, "%a %b %d %H:%M:%S %Y");
    break;
  default:
    return NULL;
  }

  return NULL;
}
/*
 * Fingerprint (MD5) for a cert
 */
static char *getMD5Fingerprint(CERTCertificate *cert) {

    unsigned char fingerprint[16];   /* result of MD5, always 16 bytes */
    SECItem fpitem;

    PK11_HashBuf(SEC_OID_MD5, fingerprint, cert->derCert.data, cert->derCert.len);

    fpitem.data = fingerprint;
    fpitem.len = sizeof(fingerprint);

    return Hexify(&fpitem);
}

/* for some reason if I try to print a null
   string on solaris, the program will crash...
   so I need to post process any string and filter
   out null string
*/
static char* processNullString(char* str) {
  return (str==NULL?(char *)"(null)":str);
}

/*
 * With the FHS work, plus the injunction against
 * paths in CGI parameters, we should get the
 * securitydir (key/cert db file dir) from the server config,
 * using the pset interface
 */
static char *
getSecurityDir(AdmldapInfo info, const char *sie)
{
  int rval = 0;
  char *securitydir = NULL;
  PsetHndl pset;
  char *configdir = util_get_conf_dir();

  if (!PL_strncasecmp(sie, "admin-serv", strlen("admin-serv"))) {
    return PL_strdup(util_get_security_dir()); /* same as admin server security dir */
  }

#ifdef DUMP_CACHE_INFO
  if (NSS_IsInitialized()) {
    printf("nss_DumpCertificateCacheInfo 6\n");
    nss_DumpCertificateCacheInfo();
  }
#endif
  /* create pset with this dn */
  pset = psetCreateSSL((char *)sie, configdir, NULL, NULL, &rval);
#ifdef DUMP_CACHE_INFO
  if (NSS_IsInitialized()) {
    printf("nss_DumpCertificateCacheInfo 7\n");
    nss_DumpCertificateCacheInfo();
  }
#endif
  if (pset && ((rval == PSET_OP_OK) || (rval == PSET_LOCAL_OPEN_FAIL))) { /* only admin-serv uses local file */
    securitydir = psetGetAttrSingleValue(pset, "configuration.encryption.certdir", &rval);
    if (!securitydir && util_psetHasObjectClass(pset, DSOBJECTCLASS)) {
      char *host = psetGetAttrSingleValue(pset, "serverHostName", &rval);
      char *sport = psetGetAttrSingleValue(pset, "nsServerPort", &rval);
      char *ssecport = psetGetAttrSingleValue(pset, "nsSecureServerPort", &rval);
      char *ssecurity = psetGetAttrSingleValue(pset, "nsServerSecurity", &rval);
      int port, security;
      char *user = NULL;
      char *binddn = NULL;
      char *bindpw = admldapGetSIEPWD(info);
      int freebindpw = 1;

      ADM_GetUserDNString(&rval, &user);
      if (!user) {
	ADM_GetCurrentUsername(&rval, &user);
      }
      /* if user is just attr val, get dn */
      binddn = admldapGetUserDN(info, user);
      if (!bindpw) {
	freebindpw = 0;
	ADM_GetCurrentPassword(&rval, &bindpw);
      }
      security = (ssecurity && !PL_strcasecmp(ssecurity, "on")) ? 1 : 0;
      if (security) {
	if (ssecport) {
	  port = atoi(ssecport);
	} else {
	  port = 636;
	}
      } else {
	if (sport) {
	  port = atoi(sport);
	} else {
	  port = 389;
	}
      }

      psetDelete(pset);
#ifdef DUMP_CACHE_INFO
      if (NSS_IsInitialized()) {
        printf("nss_DumpCertificateCacheInfo 8\n");
        nss_DumpCertificateCacheInfo();
      }
#endif
      pset = psetRealCreateSSL(info, host, port, security, DSCONFIGENTRY,
			       binddn, bindpw, NULL, &rval);
#ifdef DUMP_CACHE_INFO
      if (NSS_IsInitialized()) {
        printf("nss_DumpCertificateCacheInfo 9\n");
        nss_DumpCertificateCacheInfo();
      }
#endif
      securitydir = psetGetAttrSingleValue(pset, DSSECURITYDIR, &rval);
      PL_strfree(host);
      PL_strfree(sport);
      PL_strfree(ssecport);
      PL_strfree(ssecurity);
      PL_strfree(binddn);
      if (freebindpw) {
        memset(bindpw, 0, strlen(bindpw));
        PL_strfree(bindpw);
      }
    }
  }
  psetDelete(pset);
#ifdef DUMP_CACHE_INFO
  if (NSS_IsInitialized()) {
    printf("nss_DumpCertificateCacheInfo 10\n");
    nss_DumpCertificateCacheInfo();
  }
#endif
  return securitydir;
}


/*
 * Print cert attributes
 */
static SECStatus printCert(CERTCertificate * cert,
                           PRBool showDetail, char* forcePrint_CertType)
{
    CERTCertTrust trust;
    int flags = 0;
    SECStatus rv;
    int trustBit = 0;
    PRBool isCA = PR_FALSE;
    unsigned int rettype;

    /* get trust bit */
    rv = CERT_GetCertTrust(cert, &trust);
    if (rv == SECSuccess) {
        flags = trust.sslFlags;
    } else {
        flags = 0;
    }

    /* determine what type of certificate this is */
    if (flags & CERTDB_INVISIBLE_CA) {
        /* since it is invisible, should not allow user to see it. */
        goto looser;
    } else if ((flags & CERTDB_USER) == CERTDB_USER)  {
       fprintf(stdout, "<SERVER>\n");
    /*} else if ((flags & CERTDB_VALID_CA) == CERTDB_VALID_CA) {*/
    } else if (CERT_IsCACert(cert, &rettype) == PR_TRUE) {

        fprintf(stdout, "<CA>\n");
        isCA = PR_TRUE;
    } else if (forcePrint_CertType != NULL) {
        fprintf(stdout, "<%s>\n", forcePrint_CertType);
    }  else {
        return SECSuccess;
        /*fprintf(stdout, "<CA>\n");*/
    } 

    /* print out nickname */
   fprintf(stdout, "\t<NICKNAME>%s</NICKNAME>\n", processNullString(cert->nickname));

    /* print out subject (recipient) info */
    if (showDetail || ((flags & CERTDB_USER) == CERTDB_USER)){
       fprintf(stdout, "\t<SUBJECT>\n\t\t<CN>%s</CN>\n\t\t<OU>%s</OU>\n\t\t<O>%s</O>\n\t\t<L>%s</L>\n\t\t<ST>%s</ST>\n\t\t<C>%s</C>\n\t\t<EMAIL>%s</EMAIL>\n\t</SUBJECT>\n", 
               processNullString(CERT_GetCommonName(&cert->subject)),
               processNullString(CERT_GetOrgUnitName(&cert->subject)),
               processNullString(CERT_GetOrgName(&cert->subject)),
               processNullString(CERT_GetLocalityName(&cert->subject)),
               processNullString(CERT_GetStateName(&cert->subject)),
               processNullString(CERT_GetCountryName(&cert->subject)),
               processNullString(CERT_GetCertEmailAddress(&cert->subject)));

       /*print out subject dn*/
       fprintf(stdout, "\t<SUBJECT_DN>%s</SUBJECT_DN>\n", processNullString(CERT_NameToAscii(&cert->subject)));
    }

    /* print out issuer (CA) info */
    if (showDetail ||  ((flags & CERTDB_USER) == CERTDB_USER)){
       fprintf(stdout, "\t<ISSUER>\n\t\t<CN>%s</CN>\n\t\t<OU>%s</OU>\n\t\t<O>%s</O>\n\t\t<L>%s</L>\n\t\t<ST>%s</ST>\n\t\t<C>%s</C>\n\t\t<EMAIL>%s</EMAIL>\n\t</ISSUER>\n",
               processNullString(CERT_GetCommonName(&cert->issuer)),
               processNullString(CERT_GetOrgUnitName(&cert->issuer)),
               processNullString(CERT_GetOrgName(&cert->issuer)),
               processNullString(CERT_GetLocalityName(&cert->issuer)),
               processNullString(CERT_GetStateName(&cert->issuer)),
               processNullString(CERT_GetCountryName(&cert->issuer)),
               processNullString(CERT_GetCertEmailAddress(&cert->issuer)));

       /*print out issuer dn*/
       fprintf(stdout, "\t<ISSUER_DN>%s</ISSUER_DN>\n", processNullString(CERT_NameToAscii(&cert->issuer)));
    }

    /* print der cert */
    if (showDetail){
      fprintf(stdout, "\t<DER_CERT>%s<DER_CERT>\n", BTOA_DataToAscii((unsigned char *)(cert->derCert.data),
                                                                     (unsigned int)(cert->derCert.len)));
    }

    /* print trust information */
    if ((flags & CERTDB_VALID_CA) |
        (flags & CERTDB_TRUSTED_CA) |
        (flags & CERTDB_TRUSTED_CLIENT_CA)) {
        if (flags & CERTDB_TRUSTED_CA) {
            trustBit |= CERTDB_TRUSTED_CA;
        } 
        if (flags & CERTDB_TRUSTED_CLIENT_CA) {
            trustBit |= CERTDB_TRUSTED_CLIENT_CA;
        } 
    }


   fprintf(stdout, "\t<TRUST>%d</TRUST>\n\t<AFTERDATE>%s</AFTERDATE>\n",
           trustBit,
           processNullString((char*)formatDateTime(&cert->validity.notAfter)));

   fprintf(stdout, "\t<FINGERPRINT>%s</FINGERPRINT>\n", processNullString(getMD5Fingerprint(cert)));

    if (showDetail) {
       fprintf(stdout, "\t<BEFOREDATE>%s</BEFOREDATE>\n", 
               processNullString((char*)formatDateTime(&cert->validity.notBefore)));

       fprintf(stdout, "\t<SERIAL>%s</SERIAL>\n", processNullString(Hexify((&cert->serialNumber))));

       fprintf(stdout, "\t<VERSION>%s</VERSION>\n", processNullString(Hexify(&cert->version)));

        /* key related info */
        {
            SECKEYPublicKey *pubk = CERT_ExtractPublicKey(cert);
            int keyType = SECKEY_GetPublicKeyType(pubk);

            if (keyType == nullKey) {
               fprintf(stdout, "\t<KEYTYPE>NULL</KEYTYPE>\n");
            } else if (keyType == rsaKey) {
               fprintf(stdout, "\t<KEYTYPE>RSA</KEYTYPE>\n");
            } else if (keyType == dsaKey) {
               fprintf(stdout, "\t<KEYTYPE>DSA</KEYTYPE>\n");
            } else if (keyType == fortezzaKey) {
               fprintf(stdout, "\t<KEYTYPE>FORTEZZA</KEYTYPE>\n");
            } else if (keyType == dhKey) {
               fprintf(stdout, "\t<KEYTYPE>DH</KEYTYPE>\n");
            } else if (keyType == keaKey) {
               fprintf(stdout, "\t<KEYTYPE>KEA</KEYTYPE>\n");
            }

           fprintf(stdout, "\t<KEYSTRENGTH>%d bits</KEYSTRENGTH>\n", SECKEY_PublicKeyStrength(pubk)*8);


           fprintf(stdout, "\t<SIGNATURE>%s</SIGNATURE>\n", 
                   processNullString((char*)SECOID_FindOIDTagDescription(
                   SECOID_GetAlgorithmTag(&cert->signature))));

           fprintf(stdout, "\t<ALGORITHM>%s</ALGORITHM>\n", 
                   processNullString((char*)SECOID_FindOIDTagDescription(
                   SECOID_GetAlgorithmTag(&(SECKEY_CreateSubjectPublicKeyInfo(pubk)->algorithm)))));
        }

        /* shihcm: 
           the usage check disable below are the one that might cause NSS to crash.
           I don't know why, but some cert will cause a crash if we verify those
           usage.
           bug - http://scopus/bugsplat/show_bug.cgi?id=379853 */
        fprintf(stdout, "\t<PURPOSE>\n");
        printCertUsageInfo("\t\t<SSLClient></SSLClient>\n", certUsageSSLClient, cert);
        printCertUsageInfo("\t\t<SSLServer></SSLServer>\n", certUsageSSLServer, cert);
        /*printCertUsageInfo("\t\t<SSLServerWithStepUp></SSLServerWithStepUp>\n", certUsageSSLServerWithStepUp, cert);*/
        printCertUsageInfo("\t\t<SSLCA></SSLCA>\n", certUsageSSLCA, cert);
        printCertUsageInfo("\t\t<EmailSigner></EmailSigner>\n",certUsageEmailSigner, cert);
        printCertUsageInfo("\t\t<EmailRecipient></EmailRecipient>\n", certUsageEmailRecipient, cert);
        printCertUsageInfo("\t\t<ObjectSigner></ObjectSigner>\n", certUsageObjectSigner, cert);
        /*printCertUsageInfo("\t\t<UserCertImport></UserCertImport>\n", certUsageUserCertImport, cert);*/
        /*printCertUsageInfo("\t\t<VerifyCA></VerifyCA>\n", certUsageVerifyCA, cert);*/
        /*printCertUsageInfo("\t\t<ProtectedObjectSigner></ProtectedObjectSigner>\n", certUsageProtectedObjectSigner, cert);*/
        /*printCertUsageInfo("\t\t<StatusResponder></StatusResponder>\n", certUsageStatusResponder, cert);*/
        /*printCertUsageInfo("\t\t<AnyCA></AnyCA>\n", certUsageAnyCA, cert);*/
        fprintf(stdout, "\t</PURPOSE>\n");

        {

            /* print our cert chain */
            int count = 1;
            CERTCertificate *issuerCert = cert;
            fprintf(stdout, "\t<CERT_CHAIN>\n");
            /*leaf cert*/
            fprintf(stdout, "\t\t<CERT0>%s</CERT0>\n", processNullString(CERT_GetCommonName(&cert->subject)));
            /*fprintf(stdout, "\t\t<%s>%s</%s>\n", CERT_GetCommonName(&cert->subject), cert->nickname ,CERT_GetCommonName(&cert->subject));*/

            while (SECITEM_CompareItem(&issuerCert->derIssuer, &issuerCert->derSubject) != SECEqual) {
                issuerCert = CERT_FindCertIssuer(issuerCert, PR_Now(), certUsageAnyCA);
                if (NULL != issuerCert) {
                    fprintf(stdout, "\t\t<CERT%d>%s</CERT%d>\n", count, processNullString(CERT_GetCommonName(&issuerCert->issuer)), count);
                    count++;
                } else {
                    fprintf(stdout, "\t\t<CERT%d>BROKEN_CERTIFICATE_CHAIN</CERT%d>\n", count, count);
                    break;
                }

                /*fprintf(stdout, "\t\t<%s>%s</%s>\n", CERT_GetCommonName(&issuerCert->subject), issuerCert->nickname ,CERT_GetCommonName(&issuerCert->subject));*/
            }
            fprintf(stdout, "\t</CERT_CHAIN>\n");
        }
    }

    if ((flags & CERTDB_USER)==CERTDB_USER) {
       fprintf(stdout, "</SERVER>\n");
    }  else if (isCA == PR_TRUE) {
       fprintf(stdout, "</CA>\n");
    } else if (forcePrint_CertType != NULL) {
        fprintf(stdout, "<%s>\n", forcePrint_CertType);
    }

looser:
    return SECSuccess;
}


/*
 * Print CRL/CKL information
 */
static void showCRLRow(CERTSignedCrl *crl, PRBool detail, int list_type)
{

  /* display crl or ckl */
  if (!crl) {
	  return;
  }

  fprintf(stdout, "\t<NAME>%s</NAME>\n", constructNameDesc(&crl->crl.name));
  if (detail) {
    fprintf(stdout, "\t<ISSUER>\n");    
    fprintf(stdout, "\t\t<CN>%s</CN>\n", CERT_GetCommonName(&crl->crl.name));
    fprintf(stdout, "\t\t<O>%s</O>\n", CERT_GetOrgName(&crl->crl.name));
    fprintf(stdout, "\t\t<OU>%s</OU>\n", CERT_GetOrgUnitName(&crl->crl.name));
    fprintf(stdout, "\t\t<L>%s</L>\n", CERT_GetLocalityName(&crl->crl.name));
    fprintf(stdout, "\t\t<ST>%s</ST>\n", CERT_GetStateName(&crl->crl.name));
    fprintf(stdout, "\t\t<C>%s</C>\n", CERT_GetCountryName(&crl->crl.name));
    fprintf(stdout, "\t</ISSUER>\n");    

    fprintf(stdout, "\t<ISSUER_DN>%s</ISSUER_DN>\n", processNullString(CERT_NameToAscii(&crl->crl.name)));

    /*fprintf(stdout, "\t<SERIAL>%s</SERIAL>\n", processNullString(Hexify(&crl->crl->serialNumber)));*/
       
    {
      int version;
      switch ((int)DER_GetInteger(&(crl->crl.version))) {
      case SEC_CRL_VERSION_1: version = 1; break;
      case SEC_CRL_VERSION_2: version = 2; break;
      default: version = (int)(DER_GetInteger(&(crl->crl.version))+1); break;
      }
      fprintf(stdout, "\t<VERSION>%d</VERSION>\n", version);
    }

    fprintf(stdout, "\t<SIGNATURE>%s</SIGNATURE>\n", 
            processNullString((char*)SECOID_FindOIDTagDescription(
                                                                  SECOID_GetAlgorithmTag(&crl->crl.signatureAlg))));

    if (crl->crl.entries != NULL) {
      int x = 0;

      while (1) {
        CERTCrlEntry *entry = crl->crl.entries[x];

        if (entry != NULL) {
          fprintf(stdout, "\t<ENTRY%d>\n", x);
          fprintf(stdout, "\t\t<SERIAL_NUMBER>%s</SERIAL_NUMBER>\n", processNullString(Hexify(&entry->serialNumber)));
          fprintf(stdout, "\t\t<REVOKE_DATE>%s</REVOKE_DATE>\n",formatDateTime(&entry->revocationDate));
          fprintf(stdout, "\t</ENTRY%d>\n", x);
          x++;
        } else {
          break;
        }
      }
    }
  }

  fprintf(stdout, "\t<LAST_UPDATE>%s</LAST_UPDATE>\n\t<NEXT_UPDATE>%s</NEXT_UPDATE>\n\t<TYPE>%s</TYPE>\n", 
          formatDateTime(&crl->crl.lastUpdate),
          formatDateTime(&crl->crl.nextUpdate),
          list_type==SEC_CRL_TYPE?"CRL":"CKL");

}

/*
 * Print CRL/CKL information
 */
void showCRL(CERTCertDBHandle *handle, int list_type)
{

  CERTCrlHeadNode *CrlListHead;
  CERTCrlNode *node;
  SECStatus rv;

  /* obtain a crl/ckl list */
  rv = SEC_LookupCrls(handle, &CrlListHead, list_type);
  if(rv != SECSuccess) return;


  /* traverse crl/ckl list */
  for(node = CrlListHead->first; node != NULL; node = node->next) {
    if (list_type == SEC_CRL_TYPE) {
      fprintf(stdout, "<CRL>\n");
    } else if (list_type == SEC_KRL_TYPE) {
      fprintf(stdout, "<CKL>\n");
    }

    showCRLRow(node->crl, PR_FALSE, list_type);

    if (list_type == SEC_CRL_TYPE) {
      fprintf(stdout, "</CRL>\n");
    } else if (list_type == SEC_KRL_TYPE) {
      fprintf(stdout, "</CKL>\n");
    }
  }
}


/*
 * Find cert by its MD5 Fingerprint.
 * The cert nickname is not guaranted to be unique. An existing cert
 * should be always identified by it's fingerprint (bug 558903).
 */
static CERTCertificate *findCertByFingerprint(char *fingerprint) {

  CERTCertList *certList;
  CERTCertListNode *cln;
  CERTCertificate *cert = NULL;

  certList = PK11_ListCerts(PK11CertListUnique, NULL);
  if (certList == NULL) {
    errorRpt(GENERAL_FAILURE, getResourceString(DBT_CERT_LIST_FAIL));
  }

  for (cln = CERT_LIST_HEAD(certList); !CERT_LIST_END(cln,certList);
       cln = CERT_LIST_NEXT(cln)) {

       char *fingerprint2 = processNullString(getMD5Fingerprint(cln->cert));

       /* Output the cert if it belongs to this token */
       if (strcmp(fingerprint, fingerprint2) == 0) {
         cert = CERT_DupCertificate(cln->cert);
         break;
       }
  }
  CERT_DestroyCertList(certList);
  return cert;
}

/*
 * Llist all the certificate under the specified token
 */
static void listCert(char* tokenName) {
  /* int expired = 0; */

  CERTCertList *certList;
  CERTCertListNode *cln;
  PK11SlotInfo *slot = PK11_FindSlotByName(tokenName);
  PK11SlotInfo *internal_slot;
  char *internalTokenName;

  if (!slot) {
	  errorRpt(GENERAL_FAILURE, getResourceString(DBT_TOKEN_NAME));
	  return;
  }

  if (PK11_IsInternal(slot)) {
	  internal_slot = slot;
  } else {
	  internal_slot = PK11_GetInternalKeySlot();
	  if (!internal_slot) {
		  errorRpt(GENERAL_FAILURE, getResourceString(DBT_INIT_FAIL));
		  return;
	  }
  }

  internalTokenName =  PK11_GetTokenName(internal_slot);

  if (PK11_NeedUserInit(internal_slot) == PR_TRUE) {
	  fprintf(stdout, "<NEEDINIT_INTERNAL>TRUE</NEEDINIT_INTERNAL>\n");
  } else {
	  fprintf(stdout, "<NEEDINIT_INTERNAL>FALSE</NEEDINIT_INTERNAL>\n");
  }

  certList = PK11_ListCerts(PK11CertListUnique, NULL);
  if (certList == NULL) {
    errorRpt(GENERAL_FAILURE, getResourceString(DBT_CERT_LIST_FAIL));
  }
  for (cln = CERT_LIST_HEAD(certList); !CERT_LIST_END(cln,certList);
       cln = CERT_LIST_NEXT(cln)) {

       char *certTokenName=NULL;

       if (cln->cert->slot == NULL) {
           certTokenName = internalTokenName;
       }
       else {
           certTokenName = PK11_GetTokenName(cln->cert->slot);
       }

       /* Output the cert if it belongs to this token */
       if (strcmp(tokenName, certTokenName) == 0) {
           printCert(cln->cert, /*showDetail=*/PR_FALSE, NULL);
       }
       /* 
        * List "Builtin Object Token" as if it is the internal token 
        * This is a special NSS read-only token for storing predefined CA certs
        */ 
       else if ((strcmp(tokenName, internalTokenName) == 0) &&
                (strcmp(certTokenName, "Builtin Object Token") == 0)) {
           printCert(cln->cert, /*showDetail=*/PR_FALSE, NULL);
       }
  }
  CERT_DestroyCertList(certList);

  if (PK11_IsInternal(slot)) {
    showCRL(certdb, SEC_CRL_TYPE);
    showCRL(certdb, SEC_KRL_TYPE);
  }

  if (slot != internal_slot) {
	  PK11_FreeSlot(internal_slot);
  }
  PK11_FreeSlot(slot);

}
 
/*
 * Find a certificate and print in detail mode.
 */
static void printCertDetail(char *certFingerprint) {
  CERTCertificate* cert = findCertByFingerprint(certFingerprint);
  /* see if certificate if found */
  if (!cert) {
    errorRpt(GENERAL_FAILURE, getResourceString(DBT_CERT_NOT_FOUND));
  }
  /* if found print it out */
  printCert(cert, /*showDetail=*/PR_TRUE, NULL);
  CERT_DestroyCertificate(cert);
}

/*
 * Set the trust bit of a certificate
 */
static void setTrust(char *certFingerprint, int trust) {
  CERTCertTrust certTrust;
  CERTCertificate* cert = findCertByFingerprint(certFingerprint);

  /* see if certificate if found */
  if (cert) {
  } else {
    errorRpt(GENERAL_FAILURE, getResourceString(DBT_CERT_NOT_FOUND));
  }

  memset((char *)&certTrust, 0, sizeof(certTrust));

  /* obtain the trust bit */
  if (cert->trust) {
    certTrust = *cert->trust;
  }

  /* set client trusted ca bit */
  if (CERTDB_TRUSTED_CLIENT_CA & trust) {
    certTrust.sslFlags |= CERTDB_TRUSTED_CLIENT_CA;
  } else if (certTrust.sslFlags & CERTDB_TRUSTED_CLIENT_CA) {
    certTrust.sslFlags ^= CERTDB_TRUSTED_CLIENT_CA;
  }

  /* set trusted ca bit */
  if (CERTDB_TRUSTED_CA & trust) {
    certTrust.sslFlags |= CERTDB_TRUSTED_CA;
  } else  if (certTrust.sslFlags & CERTDB_TRUSTED_CA) {
    certTrust.sslFlags ^= CERTDB_TRUSTED_CA;
  }

  /* commit the trust bit change */
  if (CERT_ChangeCertTrust(certdb, cert, &certTrust) != SECSuccess) {
    errorRpt(GENERAL_FAILURE, getResourceString(DBT_TRUST_SET_FAIL));
  }
    
#if DEBUG
  printCert(cert, /*showDetail=*/PR_FALSE, NULL);
#endif
  CERT_DestroyCertificate(cert);
}

/*
 * Initialize NSS
 */
static void securityInitialization(char* securitydir) {

  PRUint32 flags = 0;
  char *db_name;
  int const minPwdLen = 8;
  int const pwdRequired = 1;

  /* PKSC11 module must be configured before NSS is initialized */
  db_name =  strdup("internal (software)              ");
  PK11_ConfigurePKCS11(NULL,NULL,NULL,db_name,NULL,NULL,NULL,NULL,
                       minPwdLen, pwdRequired);

  /* init NSS */
  if (NSS_Initialize(securitydir, NULL, NULL, SECMOD_DB, flags)) {
    PR_snprintf(line, sizeof(line), getResourceString(CMN_SSL_INIT_ERROR),
                securitydir ? securitydir : "(null)", PR_GetError(),
                SSL_Strerror(PR_GetError()) ? SSL_Strerror(PR_GetError()) : "unknown");
    errorRpt(GENERAL_FAILURE, line);
  }

  /* Set certdb handle */
  certdb = CERT_GetDefaultCertDB();

  /* set password function */
  PK11_SetPasswordFunc(getPassword_cb);

  /* Set domestic policy, there is no more export builds */
  NSS_SetDomesticPolicy();
}

/*
 * Generate a certificate request for the given private key
 * 
 */
static SECItem*
generateCertificateRequest(SECKEYPrivateKey* privateKey, SECKEYPublicKey* pubKey, char* subjectName)
{

  SECItem *der                      = NULL;
  SECItem *result                   = NULL;
  CERTName* certName                = NULL;
  CERTSubjectPublicKeyInfo* keyInfo = NULL;
  CERTCertificateRequest* request   = NULL;
  PRArenaPool *arena                = NULL;
  PRBool      error                 = PR_FALSE;
  char *line;
  char *sSignAlgo                   = NULL;
  int signAlgo                      = 0;
  /*DebugBreak();*/
  /* convert subject name(DN) */
  certName = CERT_AsciiToName(subjectName);
  if (!certName) {
    line = getResourceString(DBT_DN_CONVERT_FAIL);
    error = PR_TRUE;
    goto loser;
  }

  keyInfo = SECKEY_CreateSubjectPublicKeyInfo(pubKey);
  if (!keyInfo) {
    line = getResourceString(DBT_PUB_KEY_GEN_FAIL);
    error = PR_TRUE;
    goto loser;
  }

            
  /* Create certificate request blob */
  request = CERT_CreateCertificateRequest(certName, keyInfo, NULL);
  if (!request) {
    line = getResourceString(DBT_CSR_GEN_FAIL);
    error = PR_TRUE;
    goto loser;
  }

  arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  if (!arena) {
    line = getResourceString(DBT_ALLOCATE_ERROR);
    error = PR_TRUE;
    goto loser;
  }

  result = (SECItem*) PORT_ZAlloc(sizeof(SECItem));

  /* Encode the result will get a "request blob" */
  der = (SECItem *)SEC_ASN1EncodeItem(arena, result, request, SEC_ASN1_GET(CERT_CertificateRequestTemplate));

  /* Determine the signing algorithm to use.  We default
  * to SHA-1 and support SHA-256, SHA-384, and SHA-512. */
  sSignAlgo = get_cgi_var("signingalgo", NULL, NULL);

  if (!sSignAlgo || !PORT_Strcmp(sSignAlgo, "SHA-1")) {
    signAlgo = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION;
  } else if (!PORT_Strcmp(sSignAlgo, "SHA-256")) {
    signAlgo = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION;
  } else if (!PORT_Strcmp(sSignAlgo, "SHA-384")) {
    signAlgo = SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION;
  } else if (!PORT_Strcmp(sSignAlgo, "SHA-512")) {
    signAlgo = SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION;
  } else {
    /* Unknown algorithm, so just use the default. */
    signAlgo = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION;
  }

  /* Sign certificate request(the blob) with private key */
  if (SEC_DerSignData(arena, result, der->data, der->len, privateKey, signAlgo) != SECSuccess)  {
    rpt_err(GENERAL_FAILURE, 
            getResourceString(DBT_INTERNAL_ERROR), 
            getResourceString(DBT_CSR_GEN_FAIL), 
            NULL);
  }

  /* destroy all */
 loser:
  if (privateKey) SECKEY_DestroyPrivateKey(privateKey);
  if (pubKey)     SECKEY_DestroyPublicKey(pubKey);
  if (keyInfo)    SECKEY_DestroySubjectPublicKeyInfo(keyInfo);
  if (request)    CERT_DestroyCertificateRequest(request);
  if (certName)   CERT_DestroyName(certName);

  /*for debug purpose */
  /*fprintf(stdout, "%s\n", BTOA_ConvertItemToAscii(result));*/

  if (error == PR_TRUE) {
    /*{
      char tmpLine[BIG_LINE];

      PR_GetErrorText(tmpLine);
      PR_snprintf(line, sizeof(line), "%d:%s", PR_GetError(), tmpLine);
      }*/

    errorRpt(GENERAL_FAILURE, line);
  }

  return result;
}


/* Generate an RSA key pair */
static SECKEYPrivateKey*
generateKey(SECKEYPublicKey** publicKey, char* tokenName)
{
  SECKEYPrivateKey* privateKey  = NULL;
  PK11RSAGenParams params;
  unsigned char *rand_buffer             = NULL;
  PK11SlotInfo  *slot;


  /* Add the secure info to the context before we create the key. */
  /* Do it here so that we can zero it immediately and keep it in */
  /* memory for as little time as possible. */
  /*get_random_user_input();*/

  rand_buffer = ( unsigned char *) PORT_Alloc(TOTAL_BITS*sizeof(char));
  /* This function will try to seed it self with good random value if possible */
  PK11_GenerateRandom(rand_buffer, TOTAL_BITS);

  /* This function assume you going to pass in a random number generateor */
  /* will use this method if HCL can't provide a good random number generator */
  PK11_RandomUpdate(rand_buffer, TOTAL_BITS);

  /*slot = PK11_GetInternalKeySlot();*/
  /*DebugBreak();*/

  slot = PK11_FindSlotByName(tokenName);
  if (slot == NULL) {
    goto loser;
  }

  /*private key can't be generated until you call PK11_Authenticate.  If password is set to null, or not
    set at all then it doen't matter if you call PK11_Authenticate or not*/
  PK11_Authenticate(slot, PR_FALSE, 0);

  /* generate key pair */
  {
    char *sKeySize = get_cgi_var("keysize", NULL, NULL);
    int keySize = 0;
    if (sKeySize) {
      keySize = atoi(sKeySize);
    }

    if (keySize > MAX_KEY_BITS) {
      params.keySizeInBits = MAX_KEY_BITS;
    } else if (keySize <= 0) {
      params.keySizeInBits = DEFAULT_KEY_BITS;
    } else {
      params.keySizeInBits = keySize;
    }
  }

  params.pe            = DEFAULT_PUBLIC_EXPONENT;
  privateKey = PK11_GenerateKeyPair(slot, CKM_RSA_PKCS_KEY_PAIR_GEN, &params, publicKey, PR_TRUE, PR_TRUE, 0);

  /*PK11_ImportPublicKey(slot, publicKey);*/

 loser:
  if (privateKey==NULL) {
    PR_snprintf(line, sizeof(line), "%d:%s", PR_GetError(), PR_ErrorToString(PR_GetError(), PR_LANGUAGE_EN));

    rpt_err(GENERAL_FAILURE, 
            getResourceString(DBT_INTERNAL_ERROR), 
            getResourceString(DBT_INVALID_KEY_PAIR), 
            line);
  }

  return privateKey;
}

/* generate an CSR, dn most be provided */
static void generateCSR(char* tokenName) {

  int _new = (get_cgi_var("renewal", NULL, NULL) == NULL);
  /* need to find dn here */
  char *DN = getParameter("dn",getResourceString(DBT_DN));
  SECKEYPrivateKey *privateKey;
  SECKEYPublicKey* publicKey    = NULL;

  /*DebugBreak();*/
  /* Generate key pair */
  /* to do token here */
  privateKey = generateKey(&publicKey, tokenName);

  /* can be done in place */
  unescape_entities(DN); /* e.g. convert &quot; to " */
  /* since DN now contains unescaped entities, it must never be displayed */
  fprintf(stdout, HEADERREQUEST, _new?CERTREQ_NEW:CERTREQ_OLD);
  fprintf(stdout, "%s", BTOA_ConvertItemToAscii(generateCertificateRequest(privateKey, publicKey, DN)));
  fprintf(stdout, FOOTERREQUEST, _new?CERTREQ_NEW:CERTREQ_OLD);
}

/* this is where we collected chained certificate, this function
   will get call once for every certificate that was found in the DER
   cert package */
static SECStatus
collect_certs(void *arg, SECItem **certs, int numcerts)
{
  CERTDERCerts *collectArgs;
  SECItem *cert;
  SECStatus rv;
    
  collectArgs = (CERTDERCerts *)arg;
    
  collectArgs->numcerts = numcerts;

  collectArgs->rawCerts = (SECItem*)PORT_ArenaZAlloc(collectArgs->arena, sizeof(SECItem) * numcerts);
    
  if ( collectArgs->rawCerts == NULL ) {
    return(SECFailure);
  }
    
  cert = collectArgs->rawCerts;
    
  while ( numcerts-- ) {
    rv = SECITEM_CopyItem(collectArgs->arena, cert, *certs);
    if ( rv != SECSuccess) {
      return(SECFailure);
    }
    cert++;
    certs++;
  }
  return(SECSuccess);
}

/*
 * Decode a DER certificate
 */
static CERTDERCerts* decodeDERCert(char *derCertBase64) {
  SECStatus rv;
  PRArenaPool *arena;
  CERTDERCerts *collectArgs;
  char *DERCert = NULL;
  int DERCertLen = PORT_Strlen(derCertBase64);

  /* allocate memory to store the decoded certificate */
  arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  if ( arena == NULL ) {
    errorAllocateMem();
  }
        
  collectArgs = (CERTDERCerts *)PORT_ArenaZAlloc(arena, sizeof(CERTDERCerts));
  if ( collectArgs == NULL ) {
    errorAllocateMem();
  }

  collectArgs->arena = arena;

  /*pre process to filter out all the junks, this will leave us only
    with begin cert and end cert header*/
  {
    char* begin = (char*) PORT_Strstr((const char*)derCertBase64, HEADER);
    char* end   = (char*) PORT_Strstr((const char*)derCertBase64, FOOTER);

    if ((begin == NULL) || (end == NULL)) {
      /*return error message*/
      errorRpt(INCORRECT_USAGE, getResourceString(DBT_INVALID_DER_CERT));
    } else {
      end += PORT_Strlen(FOOTER);
      *end = '\0';
    }

    DERCertLen=PORT_Strlen(begin)+1; /* +1 for trailing null */
    DERCert = (char*)PORT_ZAlloc(DERCertLen);

    strcpy(DERCert, begin);
  }

  /* decoded the packaged DER certificate */
  rv = CERT_DecodeCertPackage(DERCert, DERCertLen, collect_certs, (void *)collectArgs);

  if (rv != SECSuccess) {
     errorRpt(GENERAL_FAILURE, getResourceString(DBT_DECODE_FAIL));
  }
  if (collectArgs->numcerts == 0) {
     errorRpt(APP_ERROR, getResourceString(DBT_NO_CERT_IN_PACKAGE));
  }
  return collectArgs;
}

/*
 * Decode and display a DER certificate.
 */
static void printDERCert(int isCACert) {
  SECStatus rv;
  char *derCertBase64 = getParameter("dercert",getResourceString(DBT_DER_CERT));
  CERTDERCerts *collectArgs = decodeDERCert(derCertBase64);

  if (collectArgs->numcerts > 1) {
    errorRpt(INCORRECT_USAGE, getResourceString(DBT_CERT_CHAIN_ERR));
  }

  /* Print cert information */
  {
    char *certType = (isCACert) ? (char *)"CA" : (char *)"SERVER";
    CERTCertificate **retCerts;
    PRBool keepCerts = PR_FALSE;
    PRBool caOnly = PR_FALSE;
    char *nickname = NULL;

    /*add all cert to temp */
    rv = CERT_ImportCerts(certdb, certUsageSSLServer,
                     collectArgs->numcerts,  &collectArgs->rawCerts,
                     &retCerts, keepCerts,
                     caOnly, nickname);

    if (rv == SECSuccess) {
      printCert(retCerts[collectArgs->numcerts-1], /*showDetail=*/PR_TRUE, certType);
    } else {
      PR_snprintf(line, sizeof(line), "%d:%s", PR_GetError(),
                  PR_ErrorToString(PR_GetError(), PR_LANGUAGE_EN));

      /* if unable to import report error */
      rpt_err(SYSTEM_ERROR,
              getResourceString(DBT_INTERNAL_ERROR),
              getResourceString(DBT_INSTALL_FAIL),
              line);
    }
  }
}

/*
 * Install a server certificate.
 */
static void 
installServerCert(char *tokenName, char *certname)
{
  SECStatus rv;
  CERTCertificate *cert;
  CERTCertTrust trust; 
  PK11SlotInfo *slot = PK11_FindSlotByName(tokenName);

  /* need to decode der cert */
  char *derCertBase64 =  getParameter("dercert",getResourceString(DBT_DER_CERT));
  CERTDERCerts *collectArgs = decodeDERCert(derCertBase64);

  /* if slot can not be found, then certificate can not be added */
  if (slot == NULL) {
    errorRpt(GENERAL_FAILURE, getResourceString(DBT_NO_SLOT));
  }

  /* import just the first certificate */
  {
    CERTCertificate **retCerts;
    unsigned int ncerts = 1;
    PRBool keepCerts = PR_FALSE;
    PRBool caOnly = PR_FALSE;
    char *nickname = certname;

    rv = CERT_ImportCerts(certdb, certUsageSSLServer,
                     ncerts,  &collectArgs->rawCerts,
                     &retCerts, keepCerts,
                     caOnly, nickname);

    if (rv != SECSuccess) {
        goto bail;
    }

    cert = retCerts[0];
  }

  /*
   * if the user didn't specify certificate name then
   * Try using fields from the subject name as the certificate name
   */
  if (!certname) {
    certname = constructNameDesc(&cert->subject);
  }

  /* remove leading space in certificate name */
  if (certname) {
    while (isspace(*certname)) ++certname;
  }

  /* A server cert must have a matching private key in the keydb */
  {
    /*check to see if certificate has a matching private key under the key db*/
    SECKEYPrivateKey *key = PK11_FindKeyByDERCert(slot, cert, NULL);
    if (!key) {
          PR_snprintf(line, sizeof(line), getResourceString(DBT_NO_PRIVATE_KEY_WHY), tokenName);
          rpt_err(INCORRECT_USAGE, 
                getResourceString(DBT_NO_PRIVATE_KEY), 
                line,
                NULL);
    }
  }

  /* set thust bits */
  memset((char *)&trust, 0, sizeof(trust));
  trust.sslFlags = CERTDB_USER;;
  cert->trust = &trust;

  /* import certificate to the PKCS11 module */
  rv  = PK11_ImportCertForKeyToSlot(slot, cert, certname, PR_TRUE, 0);

bail:
  if (rv != SECSuccess) {
    PR_snprintf(line, sizeof(line), "%d:%s", PR_GetError(),
                PR_ErrorToString(PR_GetError(), PR_LANGUAGE_EN));

    /* if unable to import report error */
    rpt_err(SYSTEM_ERROR,
            getResourceString(DBT_INTERNAL_ERROR),
            getResourceString(DBT_INSTALL_FAIL),
            line);
  }
}

/*
 * Install a CA cert and set its trust
 */
static void 
installCACert(char *tokenName, char *certname)
{
    /* need to decode der cert */
    CERTCertificate *cert;
    char *derCertBase64 = NULL;
    CERTDERCerts *collectArgs = NULL;
    PK11SlotInfo *slot = NULL;
    CERTCertificate **retCerts = 0;
    PRBool keepCerts = PR_TRUE;
    PRBool caOnly = PR_TRUE;
    char *nickname = certname;
    char *truststr = NULL;
    char *endptr = NULL;
    int trustflag;
    int trustedCA;
    SECStatus rc = 0;

    derCertBase64 = getParameter("dercert",getResourceString(DBT_DER_CERT));
    collectArgs = decodeDERCert(derCertBase64);

    truststr = getParameter("trust_flag",getResourceString(DBT_TRUST));
    trustflag = strtol(truststr, &endptr, 0);
    if (tokenName) {
        slot = PK11_FindSlotByName(tokenName);
    } else {
        slot = PK11_GetInternalKeySlot();
    }
    /* remove leading space in certificate name */
    if (certname) {
        while (isspace(*certname)) ++certname;
    }

    /* Import CA Cert and set trust */
    if ((*truststr == '\0') || !endptr || (*endptr != '\0')) {
        /* invalid trust flags */
        errorRpt(GENERAL_FAILURE, getResourceString(DBT_TRUST_SET_FAIL));
    }
    trustedCA = (trustflag & CERTDB_TRUSTED_CA);
    rc = CERT_ImportCerts(certdb, (trustedCA ? certUsageSSLCA : certUsageAnyCA),
                     collectArgs->numcerts,  &collectArgs->rawCerts,
                     &retCerts, keepCerts, caOnly, nickname);

    if (rc != SECSuccess) {
        goto bail;
    }

    CERT_FindCertByDERCert(certdb, collectArgs->rawCerts);
    cert = retCerts[0];
    rc  = PK11_ImportCert(slot, cert, CK_INVALID_HANDLE, certname, PR_FALSE);

bail:
    if (rc != SECSuccess) {
        PR_snprintf(line, sizeof(line), "%d:%s", PR_GetError(),
                    PR_ErrorToString(PR_GetError(), PR_LANGUAGE_EN));

        /* if unable to import report error */
        rpt_err(SYSTEM_ERROR, getResourceString(DBT_INTERNAL_ERROR), 
                getResourceString(DBT_INSTALL_FAIL), line);
    }

    if(CK_INVALID_HANDLE == PK11_FindCertInSlot(slot, cert, NULL)) {
        errorRpt(GENERAL_FAILURE, getResourceString(DBT_INSTALL_FAIL));
    } 

    setTrust(processNullString(getMD5Fingerprint(retCerts[0])), trustflag);
}


/*
 * Check if internal token needs to be initialized.
 */
static void needInit() {
  PK11SlotInfo  *slot = PK11_GetInternalKeySlot();
  char *tokenName = get_cgi_var("tokenname", NULL, NULL);
  
  if (tokenName != NULL) {
      slot= PK11_FindSlotByName(tokenName);
  }

  if (slot && (PK11_NeedUserInit(slot) == PR_TRUE)) {
    /*errorRpt(INCORRECT_USAGE, getResourceString(DBT_INVALID_CONFIRM_PWD));*/
    rpt_success("TRUE");
  } else {
    rpt_success("FALSE");
  }
}

/*
 * Initialize internal token's (key3.db) password
 */
static void initPin(char* keypwd, char* confirm) {
  PK11SlotInfo *slot= PK11_GetInternalKeySlot();
  char *tokenName = get_cgi_var("tokenname", NULL, NULL);
  
  if (tokenName != NULL) {
      slot= PK11_FindSlotByName(tokenName);
  }

  /* set password */
  if (PORT_Strcmp(keypwd, confirm)) {
    errorRpt(INCORRECT_USAGE, getResourceString(DBT_INVALID_CONFIRM_PWD));
  }


  if (!slot || (PK11_InitPin(slot, 0,  keypwd) != SECSuccess)) {
    errorRpt(APP_ERROR, getResourceString(DBT_INIT_FAIL));
  }
}

/*
 * Delete a certificate
 */
static void deleteCertificate(char *certFingerprint) {
  CERTCertificate* cert = findCertByFingerprint(certFingerprint);

  if (!cert) {
    errorRpt(GENERAL_FAILURE, getResourceString(DBT_CERT_NOT_FOUND));
  }

  /* Need to call DestroyTokenObject in case it is a cert in an external slot */ 
  if (cert->slot) {
      if (PK11_DestroyTokenObject(cert->slot, 
             PK11_FindCertInSlot(cert->slot, cert, NULL)) == SECFailure) {

          errorRpt(GENERAL_FAILURE, getResourceString(DBT_CERT_DELETE_FAIL));
      }
  }
  else if (SEC_DeletePermCertificate(cert) == SECFailure) {
    errorRpt(GENERAL_FAILURE, getResourceString(DBT_CERT_DELETE_FAIL));
  }
}

/*
 * Delete crl/krl
 */
static void deleteCRL(char* crlname, char* type) {
  int list_type = (type && !PORT_Strcmp(type, "CKL"))? SEC_KRL_TYPE : SEC_CRL_TYPE;
  CERTSignedCrl* crl  = cmgFindCrlByName(certdb, crlname, list_type);
  if (!crl) {
    errorRpt(GENERAL_FAILURE, getResourceString(DBT_CRL_CKL_NOT_FOUND));
  }

  if (SEC_DeletePermCRL(crl) == SECFailure) {
    errorRpt(GENERAL_FAILURE, getResourceString(DBT_CRL_DELETE_FAIL));
  }
}

/*
 * install crl/krl from file
 */
static void installCRL(char* filename, char* type) {
  unsigned char *ascii = NULL;
  int asciiLen = 0;
  CERTSignedCrl *signed_crl, *excrl, *crl_rv = NULL;
  SECItem derCrl;
  char msg[BIG_LINE];
  char full_path[PATH_MAX];

  int list_type = (type && !PORT_Strcmp(type, "CKL"))? SEC_KRL_TYPE : SEC_CRL_TYPE;
  if (!filename || !*filename ||
      !util_is_valid_path_string(filename) ||
      !util_verify_file_or_dir(securitydir, PR_FILE_DIRECTORY, filename, -1, PR_FILE_FILE)) {
	  /* invalid file */
      PR_snprintf(msg, sizeof(msg), getResourceString(DBT_NO_FILE_EXISTS), filename);
      errorRpt(FILE_ERROR, msg);
  }

  {/*try open the file*/
    FILE *f;

    PR_snprintf(full_path, sizeof(full_path), "%s%c%s", securitydir, FILE_PATHSEP, filename);
    
    if( !(f = fopen(full_path, "rb")) )  {
      PR_snprintf(msg, sizeof(msg), getResourceString(DBT_NO_FILE_EXISTS), full_path);
      errorRpt(FILE_ERROR, msg);
    }  else  {
      int size;
#define BLOCK_SIZE 2048
      char buf[BLOCK_SIZE];

      while (!feof(f)) {
        size= fread(buf,1,BLOCK_SIZE,f);
        if (size == 0) break;
        if (ascii) {
          ascii = (unsigned char*) PORT_Realloc(ascii, asciiLen + size);
        } else {
          ascii = (unsigned char*) PORT_Alloc(size);
        }
        memcpy(ascii+asciiLen, buf, size);
        asciiLen += size;
      }
      ascii = (unsigned char*) PORT_Realloc(ascii, asciiLen + 1);
      ascii[asciiLen] = '\0'; /* null terminate string */

      fclose(f);
    }  
  }



  derCrl.data = ascii;
  derCrl.len = asciiLen;


  /*pre process to filter out all the junks, this will leave us only
    with begin cert crl and end crl footer
    this part of code is to handle the case user enter a base64 encoded
    ascii text file.
  */
  {
    int headerlen = 0;
    char *DERCert = NULL;
    char* begin = (char*) PORT_Strstr((const char*)ascii, CRLHEADER);
    char* end   = (char*) PORT_Strstr((const char*)ascii, CRLFOOTER);

    /* Check for the alternate CRL header and footer format */
    if (begin == NULL) {
        begin = (char*) PORT_Strstr((const char*)ascii, X509CRLHEADER);
        headerlen = strlen(X509CRLHEADER);
        if (begin == NULL) {
            begin = (char*) PORT_Strstr((const char*)ascii, NEWCRLHEADER);
            headerlen = strlen(NEWCRLHEADER);
        }
    } else {
        headerlen = strlen(CRLHEADER);
    }

    if (end == NULL) {
        end = (char*) PORT_Strstr((const char*)ascii, X509CRLFOOTER);
        if (end == NULL) {
            end = (char*) PORT_Strstr((const char*)ascii, NEWCRLFOOTER);
        }
    }

    if ((begin != NULL) && (end != NULL)) {
      /* chop the footer off */
      *end = '\0';

      /* don't copy the header */
      DERCert = (char*)PORT_ZAlloc(PORT_Strlen(begin) - headerlen + 1);
      strcpy(DERCert, (begin + headerlen));

      if ( SECFailure == ATOB_ConvertAsciiToItem(&derCrl, DERCert) ) {
        errorRpt(SYSTEM_ERROR, getResourceString(DBT_DECODE_CRL_ERROR));
      }
    } else {
      PR_snprintf(msg, sizeof(msg), getResourceString(DBT_INVALID_CRL), full_path);
      errorRpt(FILE_ERROR, msg);
    }
  }

  signed_crl = CERT_DecodeDERCrl(NULL, &derCrl, list_type);

  if ( signed_crl == NULL ) {
    errorRpt(SYSTEM_ERROR, getResourceString(DBT_DECODE_CRL_ERROR));
  }

  if (get_cgi_var("installmethod", NULL, NULL) == NULL) {
    /* Decode/View only */
    showCRLRow(signed_crl, PR_TRUE, list_type);
    return;
  }


  /*remove old crl*/
  {
    /*char *name;*/
    excrl = (CERTSignedCrl *)SEC_FindCrlByDERCert((CERTCertDBHandle *)certdb,
                                                  &derCrl, list_type);
    if (excrl) {
      if (SEC_DeletePermCRL(excrl) != SECSuccess) {
        errorRpt(SYSTEM_ERROR, getResourceString(DBT_REMOVE_ERROR));
      }
    }
  }

  crl_rv = (CERTSignedCrl *)SEC_NewCrl(certdb, NULL, &derCrl, list_type);
    
  if (!crl_rv) {
    errorRpt(APP_ERROR, getResourceString(DBT_INSTALL_ERROR));
  }
}

/*
 * List pkcs 11 modules
 */
void listModule() {

  SECMODModuleList *mlp;
  SECMODModuleList *modules;
  SECMODListLock *moduleLock;

  modules = SECMOD_GetDefaultModuleList();
  moduleLock = SECMOD_GetDefaultModuleListLock();

  SECMOD_GetReadLock(moduleLock);

  fprintf(stdout, "<MODULES>\n");
  for(mlp = modules; mlp != NULL; mlp = mlp->next) {
    fprintf(stdout, "\t<%s></%s>\n", 
            mlp->module->commonName, 
            mlp->module->commonName);
  }
  fprintf(stdout, "</MODULES>\n");

  SECMOD_ReleaseReadLock(moduleLock);
}


/* Returns true if the cert is a user cert and has been printed */
static int printNickName(int certIdx, CERTCertificate *cert) {

  char *cert_name = cert->nickname;
  CERTCertTrust trust;
  int flags = 0;
  SECStatus rv;

  if (!cert_name) {
    cert_name = constructNameDesc(&cert->subject);
    if (!cert_name) {
      return 0;
    }
  }

  rv = CERT_GetCertTrust(cert, &trust);
  if (rv == SECSuccess) {
    flags = trust.sslFlags;
  }


  if(flags & CERTDB_USER) {
    fprintf(stdout, "\t\t\t<CERT%d>%s</CERT%d>\n", certIdx, cert_name, certIdx);
    return 1;
  }

  return 0;
}

/*
 * List server certs per token. Do not show tokens without certs.
 */
static void listCertsPerToken(const char *pkcs11TokenFamily, PK11SlotList* pk11SlotList) {
  PK11SlotListElement *token;
  CERTCertList *certList;
  CERTCertListNode *cln;
  char *internalTokenName =  PK11_GetTokenName(PK11_GetInternalKeySlot());
  int totalCertCount=0;

  if(pk11SlotList->head) {

    fprintf(stdout, "\t<%s>\n", pkcs11TokenFamily);

    certList = PK11_ListCerts(PK11CertListUser, NULL);

    for(token = pk11SlotList->head; certList && token; token = token->next) {

      int tokenCertCount = 0;
      char *tokenName = PK11_GetTokenName(token->slot);

      for (cln = CERT_LIST_HEAD(certList); !CERT_LIST_END(cln,certList);
           cln = CERT_LIST_NEXT(cln)) {

           char *certTokenName=NULL;

           /*
            * Print cert if it is in this (cln) token; For certs on the internal
            * token, the cert reference to a slot/token is NULL.
            */
           if (cln->cert->slot == NULL) {
               certTokenName = internalTokenName;
           }
           else {
               certTokenName = PK11_GetTokenName(cln->cert->slot);
           }

           /* Output the cert if it belongs to this token */
           if (strcmp(tokenName, certTokenName) == 0) {
               if (tokenCertCount == 0) {
                   fprintf(stdout, "\t\t<%s>\n", PK11_GetTokenName(token->slot));
               }
               if (printNickName(tokenCertCount, cln->cert)) {
                   tokenCertCount++;
                   totalCertCount++;
               }
           }
      }

      if (tokenCertCount > 0) {
          fprintf(stdout, "\t\t</%s>\n", PK11_GetTokenName(token->slot));
      }
    }

    /* Output at least the intrnal token name if there are no certs */
    if (totalCertCount == 0) {
      fprintf(stdout, "\t\t<%s>\n", internalTokenName);
      fprintf(stdout, "\t\t</%s>\n", internalTokenName);
    }

    fprintf(stdout, "\t</%s>\n", pkcs11TokenFamily);
  } 
}

/*
 * List all the possible tokens resides under any pkcs11 device
 */
void listToken() {
  PK11SlotList *list;

  fprintf(stdout, "<TOKENLIST>\n");        

#ifdef NS_DOMESTIC
  if(SECMOD_IsModulePresent(PUBLIC_CIPHER_FORTEZZA_FLAG))
    fprintf(stdout, "\t<SECURITY>fortezza</SECURITY>\n");
  else
    fprintf(stdout, "\t<SECURITY>domestic</SECURITY>\n");
#else
  fprintf(stdout, "\t<SECURITY>export</SECURITY>\n");
#endif /* NS_DOMESTIC */

  /*RSA token list*/
  list = PK11_GetAllTokens(CKM_RSA_PKCS, PR_FALSE, PR_FALSE, NULL);
  listCertsPerToken("RSA_TOKEN", list);
  PK11_FreeSlotList(list);
  list = PK11_GetAllTokens(CKM_SKIPJACK_CBC64, PR_FALSE, PR_FALSE, NULL);
  listCertsPerToken("FORTEZZA_TOKEN", list);
  PK11_FreeSlotList(list);

  fprintf(stdout, "</TOKENLIST>\n");        
}

/*
 * List all the possible tokens resides under any pkcs11 device
 */
void tokenInfo() {
    PK11SlotList* slotList;
    PK11SlotListElement *slot;

    slotList = PK11_GetAllTokens(CKM_RSA_PKCS, PR_FALSE, PR_FALSE, NULL);

    fprintf(stdout, "<TOKENINFO>\n");

    for(slot = slotList->head; slot != NULL; slot = slot->next) {

        fprintf(stdout, "\t<%s>\n", PK11_GetTokenName(slot->slot));

        fprintf(stdout, "\t\t<MODULE>%s</MODULE>\n", PK11_GetModule(slot->slot)->commonName);

        fprintf(stdout, "\t\t<INTERNAL>%s</INTERNAL>\n",
            PK11_IsInternal(slot->slot) ? "TRUE" : "FALSE");
        fprintf(stdout, "\t\t<HARDWARE>%s</HARDWARE>\n",
            PK11_IsHW(slot->slot) ? "TRUE" : "FALSE");
        fprintf(stdout, "\t\t<READONLY>%s</READONLY>\n",
            PK11_IsReadOnly(slot->slot) ? "TRUE" : "FALSE");
        fprintf(stdout, "\t\t<NEED_LOGIN>%s</NEED_LOGIN>\n",
            PK11_NeedLogin(slot->slot) ? "TRUE" : "FALSE");
        fprintf(stdout, "\t\t<FRIENDLY>%s</FRIENDLY>\n",
            PK11_IsFriendly(slot->slot) ? "TRUE" : "FALSE");
        fprintf(stdout, "\t\t<NEED_USER_INIT>%s</NEED_USER_INIT>\n",
            PK11_NeedUserInit(slot->slot) ? "TRUE" : "FALSE");

        fprintf(stdout, "\t</%s>\n", PK11_GetTokenName(slot->slot));
    }

    fprintf(stdout, "</TOKENINFO>\n");   
    PK11_FreeSlotList(slotList);
}

/*
 * Add/remove pkcs 11 module from the secmod db
 */
static void moduleOperation(char* op) {
  const char *binary = "modutil"; /* PATH and LD_LIBRARY_PATH must already be set correctly */
  const char *install_dir = LIBDIR;
  char *filename, *filetype, *dllname;
  char cmd[BIG_LINE] = "";
  char msg[BIG_LINE];

  dllname = get_cgi_var("dllname", NULL, NULL);

  if (!PORT_Strcmp(op, "remove")) {
    if (!dllname || !*dllname || !is_valid_name_string(dllname)) {
      PR_snprintf(msg, sizeof(msg), getResourceString(DBT_MISSING_MODULE_NAME));
      rpt_err(INCORRECT_USAGE, msg, NULL, NULL);
    }
    PR_snprintf(cmd, sizeof(cmd), "%s -dbdir %s -force -nocertdb -delete \"%s\" 2>&1",
            binary,
            securitydir,
            dllname);

  } else if (!PORT_Strcmp(op, "add")) {
   
    filename = getParameter("filename",getResourceString(DBT_MISSING_FILE));
    filetype = getParameter("format",getResourceString(DBT_MISSING_FORMAT));

    /* see if filename exists in securitydir */
    if(!util_is_valid_path_string(filename) ||
       !util_verify_file_or_dir(securitydir, PR_FILE_DIRECTORY, filename, -1, PR_FILE_FILE)) {
      PR_snprintf(msg, sizeof(msg), getResourceString(DBT_NO_FILE_EXISTS), filename);
      rpt_err(FILE_ERROR, msg, NULL, NULL);
    }

    /* Bail if the format wasn't specified. */
    if (!filetype) {
        PR_snprintf(msg, sizeof(msg), getResourceString(DBT_MISSING_FORMAT));
        rpt_err(INCORRECT_USAGE, msg, NULL, NULL);
    }

    /* dllname is the internal name of the module - the user must
       have specified a valid name */
    if((filetype && (!PORT_Strcmp(filetype, "dll"))) &&
       (!dllname || !PORT_Strcmp(dllname, "") || !is_valid_name_string(dllname))) {
      PR_snprintf(msg, sizeof(msg), getResourceString(DBT_MISSING_MODULE_NAME));
      rpt_err(INCORRECT_USAGE, msg, NULL, NULL);
    }
        
    /*
     * NOTE: The UI version of installing PKCS #11 modules will
     *       NOT check jar signatures (hence "-noverify").
     *       If the administrator really wants to do this,
     *       run modutil from the command line.
     */

    if(!PORT_Strcmp(filetype, "jar"))
      PR_snprintf(cmd, sizeof(cmd), "%s -jar %s -installdir %s -dbdir %s -force -nocertdb 2>&1",
              binary,
              filename,
              install_dir,
              securitydir);
    else if (!PORT_Strcmp(filetype, "dll"))
      PR_snprintf(cmd, sizeof(cmd), "%s -dbdir %s -add \"%s\" -libfile %s -force -nocertdb 2>&1",
              binary,
              securitydir,
              dllname,
              filename);

    else {
      PR_snprintf(msg, sizeof(msg), getResourceString(DBT_INVALID_FORMAT));
      rpt_err(INCORRECT_USAGE, msg, NULL, NULL);
    }

  } else { /* invalid op */
    PR_snprintf(msg, sizeof(msg), getResourceString(DBT_INVALID_OP), op);
    rpt_err(INCORRECT_USAGE, msg, NULL, NULL);
  }

  if(*cmd && (system(cmd) != 0)) {
    if (!PORT_Strcmp(op, "add")) {
      PR_snprintf(msg, sizeof(msg), getResourceString(DBT_MODUTIL_FAILURE), filename);
    } else if (!PORT_Strcmp(op, "remove")) {
      PR_snprintf(msg, sizeof(msg), getResourceString(DBT_FAIL_REMOVED));
    }

    rpt_err(APP_ERROR, msg, NULL, NULL);
  }
}

/*
 * Change password for a token
 */
static void changePassword(char* oldPwd, char* newPwd, char* confirmPwd) {
  SECStatus rv;
  PK11SlotInfo *slot= PK11_GetInternalKeySlot();
  char *tokenName = get_cgi_var("tokenname", NULL, NULL);
  
  if (tokenName != NULL) {
      slot= PK11_FindSlotByName(tokenName);
  }

  if (slot == NULL) {
    errorRpt(GENERAL_FAILURE, getResourceString(DBT_NO_SLOT));
  }

  if(PORT_Strcmp(newPwd, confirmPwd))  {
    errorRpt(INCORRECT_USAGE, getResourceString(DBT_INVALID_CONFIRM_PWD));
  }

  cur_pwd = PORT_Strdup(oldPwd);

  rv = PK11_Authenticate(slot, PR_FALSE, 0);

  if (rv != SECSuccess) {
    errorRpt(INCORRECT_USAGE, getResourceString(DBT_INVALID_OLD_PWD));
  }

  rv = PK11_ChangePW(slot, oldPwd, newPwd);
    
  if (rv != SECSuccess) {
    char err_text[BIG_LINE];
    PR_snprintf(err_text, sizeof(err_text),  getResourceString(DBT_FAIL_CHANG_PWD), PR_GetError());
    errorRpt(APP_ERROR, err_text);
  }
}

/* Makes a list of the aliases installed on machine. */
static void listAlias()
{
  errorRpt(INCORRECT_USAGE, getResourceString(DBT_MIGRATION_NOT_SUPPORTED));
}

/* migration via CGI is no longer supported - migration is performed
   as part of install/upgrade or as a separate command line utility */
static void keyCertMigrate() {
  errorRpt(INCORRECT_USAGE, getResourceString(DBT_MIGRATION_NOT_SUPPORTED));
}

int main(int argc, char *argv[])
{
  /* cgi env setup */
  char *m = NULL;
  char msg[BIG_LINE];
  AdmldapInfo ldapInfo; /* our config */
  int rc = 0;
  char *sie;
  char *configdir = NULL;
  const char *secdir = NULL;

#if 0
  CGI_Debug("security");
#endif
  ADMUTIL_Init();
  m = getenv("REQUEST_METHOD");
  configdir = util_get_conf_dir();
  secdir = util_get_security_dir();

  /*setup i18n stuff*/
  {
    char   *lang=getenv("HTTP_ACCEPT_LANGUAGE");
    i18nResource = res_find_and_init_resource(PROPERTYDIR, RESOURCE_FILE);
    i18nResource_common = res_find_and_init_resource(PROPERTYDIR, COMMON_RESOURCE_FILE);
    acceptLanguage = "en";
    if (lang) acceptLanguage = strdup(lang);

    /*nss 2.8 suppose to handle universal string...*/
    /*PORT_SetUCS4_UTF8ConversionFunction(MC_nlsConvertUCS4UTF8);*/
  }

  /*standard html header*/
  fprintf(stdout, "Content-type: text/html\n\n");
  fflush(stdout);

  /* This cgi only handle post request*/
  if(!m || PORT_Strcmp(m, "POST"))  {
    return 0;
  }

  post_begin(stdin);

  sie = getParameter("sie", getResourceString(DBT_SIE));
  ldapInfo = admldapBuildInfoOnly(configdir, &rc);
  if (rc || !ldapInfo) {
        PR_snprintf(line, sizeof(line), getResourceString(CMN_CONFIGPROBLEM), configdir ? configdir : "(null)", rc);
        errorRpt(FILE_ERROR, line);
  }

#ifdef DUMP_CACHE_INFO
  if (NSS_IsInitialized()) {
    printf("nss_DumpCertificateCacheInfo 1\n");
    nss_DumpCertificateCacheInfo();
  }
#endif

  if(ADMSSL_InitSimple((char *)configdir, (char *)secdir, 1)) {
      PR_snprintf(line, sizeof(line), getResourceString(CMN_SSL_INIT_ERROR),
                  secdir ? secdir : "(null)", PR_GetError(),
                  SSL_Strerror(PR_GetError()) ? SSL_Strerror(PR_GetError()) : "unknown");
      errorRpt(SYSTEM_ERROR, line);
  }

  securitydir = getSecurityDir(ldapInfo, sie);
  {
    char* operation = getParameter("formop",getResourceString(DBT_OP));

    if (!operation) {
      /* if we got here it means front end pass back
	 a bogus operation string */
      PR_snprintf(line, sizeof(line), getResourceString(DBT_NO_OP), operation);
      errorRpt(INCORRECT_USAGE, line);
    } else if (!PORT_Strcmp(operation, "MIGRATE_DB")) {
      keyCertMigrate();
    } else {
      /*only initialize db when we are not trying to migrate
        if we initialize db then we won't be able to migrate because
        security library will hog up the file and we can not remove it.*/
      if (!util_verify_file_or_dir(securitydir, PR_FILE_DIRECTORY, NULL, 0, PR_FILE_FILE)) {
        PR_snprintf(msg, sizeof(msg), getResourceString(DBT_NO_FILE_EXISTS),
		    securitydir ? securitydir : "(null)");
        errorRpt(INCORRECT_USAGE, msg);
      }

	  /* we got here, we think sie is a valid prefix */
      /* close all db - we may have had to open the admin server key/cert db
	     in order to use LDAPS before - now have to shut down NSS and reinit */
      closeAllSecurityDB();
      securityInitialization(securitydir);

      if (!PORT_Strcmp(operation, "LIST_CERTIFICATE")) {
        /* list cert */
        listCert(getParameter("tokenname",getResourceString(DBT_TOKEN_NAME)));
        /* fprintf(stdout, "total_cert:%d",num_of_certs++); */

      } else if (!PORT_Strcmp(operation, "CHANGE_TRUST")) {
		char *endptr = NULL;
		char *truststr = getParameter("trust_flag",getResourceString(DBT_TRUST));
		int trustflag = strtol(truststr, &endptr, 0);
		if ((*truststr == '\0') || !endptr || (*endptr != '\0')) {
			/* invalid trust flags */
			errorRpt(GENERAL_FAILURE, getResourceString(DBT_TRUST_SET_FAIL));
		}
		
        /* change trust */
        setTrust(getParameter("certfingerprint",getResourceString(DBT_CERT_FINGERPRINT)), 
                 trustflag);

      } else if (!PORT_Strcmp(operation, "FIND_CERTIFICATE")) {
        /* view cert */
		printCertDetail(getParameter("certfingerprint",getResourceString(DBT_CERT_FINGERPRINT)));

      } else if (!PORT_Strcmp(operation, "GENERATE_CSR")) {
        /* generate a CSR (certificate signing request) */
        generateCSR(getParameter("tokenname",getResourceString(DBT_TOKEN_NAME)));

      } else if (!PORT_Strcmp(operation, "INSTALL_CERT")) {

        int viewOnly = (get_cgi_var("installmethod", NULL, NULL) == NULL);
        /* certtype=0  - server certificate,  certtype=1  - ca certificate */
        char *certType = getParameter("certtype",getResourceString(DBT_CERTIFICATE_TYPE));
        int   isCACert = (atoi(certType) == 1);

        if (viewOnly) {
          printDERCert(isCACert); /* show decoded certificate */
        }
        else { /* install a certificate */

          char *certName = get_cgi_var("certname", NULL, NULL);
          char *tokenName = 
                    getParameter("tokenname",getResourceString(DBT_TOKEN_NAME));
          if (isCACert) {
              installCACert(tokenName, certName);
          } else {
             installServerCert(tokenName, certName);
          }
        }

      } else if (!PORT_Strcmp(operation, "NEED_INIT")) {
        /* see if the db need init (set the password) */
        needInit();

      } else if (!PORT_Strcmp(operation, "INIT_PIN")) {
        /* initialize internal token pin */

        /* We fetch the paramters here for the benefit of the Console.  If we call
         * getParameter() as an argument to initPin, the calling order causes the
         * error message that is returned to be out of order with the display in
         * the UI. */
        char *newpwd = getParameter("newpwd",getResourceString(DBT_PASSWORD));
        char *confirmpwd = getParameter("confirmpwd",getResourceString(DBT_PASSWORD));

        initPin(newpwd, confirmpwd);

      } else if (!PORT_Strcmp(operation, "DELETE_CACERT")) {
        /* remove a ca certificate */
        deleteCertificate(getParameter("certfingerprint",getResourceString(DBT_CERT_FINGERPRINT)));

      } else if (!PORT_Strcmp(operation, "DELETE_CRL_CKL")) {
        /* remove a crl or ckl certificate */
        deleteCRL(getParameter("crlname", getResourceString(DBT_CRL_NAME)), 
                  get_cgi_var("list_type", NULL, NULL));

      } else if (!PORT_Strcmp(operation, "LIST_MODULE")) {
        /* list all loaded pkcs#11 modules */
        listModule();

      } else if (!PORT_Strcmp(operation, "LIST_TOKEN")) {
        /* list all avaliable security device*/
        listToken();

      } else if (!PORT_Strcmp(operation, "TOKEN_INFO")) {
        /* get info on all available tokens */
        tokenInfo();

      } else if (!PORT_Strcmp(operation, "MODULE_OPERATION")) {
        /* operate on pkcs#11 module */
        /* current there is only 2 op type, add or delete */
        moduleOperation(getParameter("op_type", getResourceString(DBT_MODULE_OP_TYPE)));

      } else if (!PORT_Strcmp(operation, "CHANGE_PASSWORD")) {
        /* change internal db password */
        /* currently change password only apply to key3.db, and we do
           not deal with change password for external token.  User can
           do that via the software that comes with the hardware */

        /* We fetch the paramters here for the benefit of the Console.  If we call
         * getParameter() as an argument to initPin, the calling order causes the
         * error message that is returned to be out of order with the display in
         * the UI. */
        char *oldpwd = getParameter("oldpwd", getResourceString(DBT_OLD_PWD));
        char *newpwd = getParameter("newpwd", getResourceString(DBT_NEW_PWD));
        char *confirmpwd =  getParameter("confirmpwd", getResourceString(DBT_CONFIRM_PWD));

        changePassword(oldpwd, newpwd, confirmpwd);

      } else if (!PORT_Strcmp(operation, "INSTALL_CRL_CKL")) {
        /* install a crl/ckl certificate */
        installCRL(getParameter("filename", getResourceString(DBT_MISSING_FILE)),
                   get_cgi_var("list_type", NULL, NULL));

      } else if (!PORT_Strcmp(operation, "FIND_CRL_CKL")) {
        /* print detail information of a crl/ckl */
        char *type = get_cgi_var("list_type", NULL, NULL);
        fprintf(stdout, "<%s>\n", type?type:"");
        showCRLRow(cmgFindCrlByName(certdb, 
                                    getParameter("crlname", getResourceString(DBT_MISSING_FILE)), 
                                    (type && !PORT_Strcmp(type, "CKL"))? SEC_KRL_TYPE : SEC_CRL_TYPE), 
                   PR_TRUE, 
                   ((type && !PORT_Strcmp("CRL", type))?SEC_CRL_TYPE:SEC_KRL_TYPE));
        fprintf(stdout, "</%s>\n", type);

      } else if (!PORT_Strcmp(operation, "LIST_ALIAS")) {
        /* list old server root's alias */
        listAlias();

      } else {
        /* if we got here it means front end pass back
           a bogus operation string */
        PR_snprintf(line, sizeof(line), getResourceString(DBT_NO_OP), operation);
        errorRpt(INCORRECT_USAGE, line);
      }
      /* close all db */
      closeAllSecurityDB();
    }
  }

  /* report success since no error occured */
  rpt_success(NULL);

  return 0;
}
