/** 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 **/
/*
 * sec-activate.c
 *
 * Sets the Admin Server encryption information into DS.
 * All blame to Adam Prishtina (adam@netscape.com)
 *
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <prtypes.h>

#include <stdio.h>

#ifdef __cplusplus
extern "C" {
#endif

#include "cert.h"
#include "key.h"
#include "certdb.h"
#include "keyt.h"
#include "secport.h"
#include "libadminutil/distadm.h"
#include "libadminutil/admutil.h"
#include "libadminutil/resource.h"
#include "libadmsslutil/psetcssl.h"
#include "libadmsslutil/admsslutil.h"

#include "libadmin/libadmin.h"

#ifdef __cplusplus
}
#endif

/*
   properties file name = "sec-activate.properties"
*/
#define RESOURCE_FILE "sec-activate"

#define resource_key(a,b)   a b

/* define strings here */
#define DBT_PSET_INIT_NO_USERNAME        resource_key(RESOURCE_FILE, "1")
#define DBT_PSET_INIT_NO_USERDN          resource_key(RESOURCE_FILE, "2")
#define DBT_CMDLINE_USAGE                resource_key(RESOURCE_FILE, "3")
#define DBT_INV_SR                       resource_key(RESOURCE_FILE, "4")
#define DBT_PSET_INIT_FAILURE            resource_key(RESOURCE_FILE, "5")
#define DBT_ADMSSL_INIT_FAIL             resource_key(RESOURCE_FILE, "6")
#define DBT_PSET_CREATE_FAIL             resource_key(RESOURCE_FILE, "7")
#define DBT_CMDLINE_STATUS_USAGE         resource_key(RESOURCE_FILE, "8")
#define DBT_SUCCESS                      resource_key(RESOURCE_FILE, "9")
#define DBT_CGI_INVALID_ARGS             resource_key(RESOURCE_FILE, "10")
#define DBT_CGI_MISSING_ARGS             resource_key(RESOURCE_FILE, "11")
#define DBT_CGI_UNKNOWN_METH             resource_key(RESOURCE_FILE, "12")
#define DBT_GENERIC_PSET_FAILURE         resource_key(RESOURCE_FILE, "13")
#define DBT_PSET_ATTR_CREATE             resource_key(RESOURCE_FILE, "14")
#define DBT_PSET_ATTR_MODIFY             resource_key(RESOURCE_FILE, "15")
#define DBT_PSET_INV_ATTR                resource_key(RESOURCE_FILE, "16")
#define DBT_ADMIN_CONF_MOD               resource_key(RESOURCE_FILE, "17")

static int update_conf(char *configdir, char *file, char *name, char *val, int quoted);

Resource       *i18nResource;
char           *acceptLanguage;

char*
getResourceString(char *key) {
  return (char *)(res_getstring(i18nResource, key, acceptLanguage, NULL, 0, NULL));
}

typedef struct family_input_s family_input;
struct family_input_s {
  char *family_name;
  char *token_name;
  char *personality;
  char *activated; /* "on" or "off" */
  struct family_input_s *next;
};

/*
 * set_attribute
 * Checks if attribute exists. If it does, change the entry.
 * If it doesn't, create the entry and set it.
 */

void set_attribute(PsetHndl pset, char *name, char *value) {

  int rv;

  if(psetCheckAttribute(pset, name) == PSET_OP_FAIL) {
    /* create entry */
    rv = psetAddSingleValueAttribute(pset, name, value);
    if(rv != PSET_OP_OK)
      rpt_err(APP_ERROR, getResourceString(DBT_GENERIC_PSET_FAILURE),
              getResourceString(DBT_PSET_ATTR_CREATE), NULL);      
  }
  else {
    /* modify entry */
    rv = psetSetSingleValueAttr(pset, name, value);
    if(rv != PSET_OP_OK)
      rpt_err(APP_ERROR, getResourceString(DBT_GENERIC_PSET_FAILURE), 
              getResourceString(DBT_PSET_ATTR_MODIFY), NULL);
  }
}

static char * get_cgi_var_must(char * var, char * x, char * y) {
  char * value;
  value = get_cgi_var(var, x, y);

  if (value) {
  } else {
    char * scratch = PR_smprintf(getResourceString(DBT_CGI_MISSING_ARGS), var);
    rpt_err(INCORRECT_USAGE, scratch, 0, 0);
    PR_smprintf_free(scratch); /* never executed */
  }

  return value;
}

/* 
 * int get_family_input
 * Reads and parses cipher family input from front end.
 * Returns 0 on success, -1 on failure.
 */

void get_family_input(family_input **family_head) {
  
  family_input *family_ptr; 
  char *list_of_families;
  char *family_name;
  char *temp;
  char cgi_var_name[BIG_LINE];

  list_of_families = get_cgi_var_must("familyList", "", "");

  family_name = PORT_Strtok(list_of_families, ",");

  while (family_name) {
    if (*family_head) {
      family_ptr->next = (family_input *) malloc(sizeof(family_input));
      family_ptr = family_ptr->next;
    } else {
      *family_head = (family_input *) malloc(sizeof(family_input));
      family_ptr = *family_head;
    }
    family_ptr->next = NULL; 
    family_ptr->family_name = PORT_Strdup(family_name);
    PR_snprintf(cgi_var_name, sizeof(cgi_var_name), "%s-token", family_name);
    temp = get_cgi_var_must(cgi_var_name, "", "");
    family_ptr->token_name = PORT_Strdup(temp);
    PR_snprintf(cgi_var_name, sizeof(cgi_var_name), "%s-cert", family_name);
    temp = get_cgi_var_must(cgi_var_name, "", "");
    family_ptr->personality = PORT_Strdup(temp);
    PR_snprintf(cgi_var_name, sizeof(cgi_var_name), "%s-activated", family_name);
    temp = get_cgi_var_must(cgi_var_name, "", "");
    family_ptr->activated = PORT_Strdup(temp);
    family_name = PORT_Strtok(NULL, ",");
  }
}


/* 
 * int get_cert_nickname
 * Reads and parses cipher family input from front end
 * Returns 0 on success, -1 on failure.
 */

int get_cert_nickname(char *buf, size_t bufsize) {
  
  char *list_of_families;
  char *family_name;
  char *temp;
  char cgi_var_name[BIG_LINE];

  list_of_families = get_cgi_var("familyList", "", "");
  if(!list_of_families) {
    return -1;
  }

  family_name = PORT_Strtok(list_of_families, ",");
  while(family_name != NULL) {
    
    PR_snprintf(cgi_var_name, sizeof(cgi_var_name), "%s-cert", family_name);
    if((temp = get_cgi_var(cgi_var_name, "", "")) == NULL) {
      return -1;
    }
    PL_strncpyz(buf, temp, bufsize);
    return 0;
    /*family_name = PORT_Strtok(NULL, ",");*/
  }
  return -1;
}
/*
 * int GetSSLFamilyAttributes
 * Reads all LDAP entries relating to cipher family information.
 * Returns return_string, a string of all information found, and
 * 0 on success, -1 on failure.
 */
int GetSSLFamilyAttributes(PsetHndl pset, char **return_string) {

  AttrNameList family_list;
  int errorCode;
  char temp_return[5000];
  char temp[1000];

  char **family;
  char family_attribute[1024];
  char *token;
  char *personality;
  char *val;

  char *family_name;

  *return_string = NULL;
  strcpy(temp_return, "");

  val = psetGetAttrSingleValue(pset,
                               "configuration.nsServerSecurity",
                               &errorCode);
  if(val)
    PR_snprintf(temp, sizeof(temp), "security=%s\n", val);
  else
    PR_snprintf(temp, sizeof(temp), "security=off\n");
  PL_strcatn(temp_return, sizeof(temp_return), temp);

  if((family_list = psetGetChildren(pset, "configuration.Encryption", &errorCode))) {
    
    for (family = family_list; *family; family++) {
      
      val = NULL;
      token = NULL;
      personality = NULL;

      PR_snprintf(family_attribute, sizeof(family_attribute), "%s.nsSSLActivation", *family);        
      val =  psetGetAttrSingleValue(pset, 
                                    family_attribute, 
                                    &errorCode);
      PR_snprintf(family_attribute, sizeof(family_attribute), "%s.nsSSLToken", *family);
      token =  psetGetAttrSingleValue(pset, 
                                      family_attribute, 
                                      &errorCode);
      PR_snprintf(family_attribute, sizeof(family_attribute), "%s.nsSSLPersonalityssl", *family);
      personality =  psetGetAttrSingleValue(pset, 
                                            family_attribute, 
                                            &errorCode);

      if((!val) || (!token) || (!personality)) {
        PL_strcatn(temp_return, sizeof(temp_return), "familyList=NULL\n");
        *return_string = PORT_Strdup(temp_return);
        return -1;
      }

      family_name = strrchr(*family, '.');
      family_name++;
        
      PR_snprintf(temp, sizeof(temp), "familyList=%s\n", family_name);
      PL_strcatn(temp_return, sizeof(temp_return), temp);

      PR_snprintf(temp, sizeof(temp), "%s-activated=%s\n", family_name, val);
      PL_strcatn(temp_return, sizeof(temp_return), temp);

      PR_snprintf(temp, sizeof(temp), "%s-token=%s\n", family_name, token);
      PL_strcatn(temp_return, sizeof(temp_return), temp);

      PR_snprintf(temp, sizeof(temp), "%s-cert=%s\n", family_name, personality);
      PL_strcatn(temp_return, sizeof(temp_return), temp);
    }
  }
  PL_strcatn(temp_return, sizeof(temp_return), "familyList=NULL\n");

  /* get cipher preferences */
  
  val = NULL;
  val = psetGetAttrSingleValue(pset,
                               "configuration.encryption.nsSSL2",
                               &errorCode);
  PL_strcatn(temp_return, sizeof(temp_return), "ssl2-activated=");
  if(val)
    PL_strcatn(temp_return, sizeof(temp_return), val);
  PL_strcatn(temp_return, sizeof(temp_return), "\n");

  val = NULL;
  val = psetGetAttrSingleValue(pset,
                               "configuration.encryption.nsSSL2Ciphers",
                               &errorCode);
  PL_strcatn(temp_return, sizeof(temp_return), "ssl2=");
  if(val) 
    PL_strcatn(temp_return, sizeof(temp_return), val);
  PL_strcatn(temp_return, sizeof(temp_return), "\n");

  val = NULL;
  val = psetGetAttrSingleValue(pset,
                               "configuration.encryption.nsSSL3",
                               &errorCode);
  PL_strcatn(temp_return, sizeof(temp_return), "ssl3-activated=");
  if(val)
    PL_strcatn(temp_return, sizeof(temp_return), val);
  PL_strcatn(temp_return, sizeof(temp_return), "\n");

  val = NULL;
  val = psetGetAttrSingleValue(pset,
                               "configuration.encryption.nsSSL3Ciphers",
                               &errorCode);
  PL_strcatn(temp_return, sizeof(temp_return), "ssl3=");
  if(val)
    PL_strcatn(temp_return, sizeof(temp_return), val);
  PL_strcatn(temp_return, sizeof(temp_return), "\n");

  val = NULL;
  val = psetGetAttrSingleValue(pset,
                               "configuration.encryption.nsSSLClientAuth",
                               &errorCode);
  PL_strcatn(temp_return, sizeof(temp_return), "clientauth=");
  if(val)
    PL_strcatn(temp_return, sizeof(temp_return), val);
  PL_strcatn(temp_return, sizeof(temp_return), "\n");
  
  *return_string = PORT_Strdup(temp_return);
  return 0;
}



/*
 * int SetSSLFamilyAttributes
 * Changes or adds LDAP entries relating to cipher family information.
 * Returns 0 on success, -1 on failure.
 */

int SetSSLFamilyAttributes(PsetHndl pset, family_input *family_head,
                           char *ssl2, 
                           char *ssl3, 
                           char *ssl2_act,
                           char *ssl3_act) {

  family_input *family_ptr = family_head;
  family_input *delete_ptr;
  char family_sie_entry[BIG_LINE];
  char family_token_attr[BIG_LINE];
  char family_cert_attr[BIG_LINE];
  char family_activated_attr[BIG_LINE];

  AttrNameList list_name;
  AttributeList initlist;
  int rv, rv2;

  while(family_ptr != NULL) {
    PR_snprintf(family_sie_entry, sizeof(family_sie_entry), 
            "configuration.encryption.%s", 
            family_ptr->family_name);
    if((rv = psetCheckAttribute(pset, family_sie_entry)) == PSET_OP_FAIL) {
      /* family entry doesn't exist, create it */

      /* create encryption first if it doesn't exist */
      if((rv2 = psetCheckAttribute(pset, "configuration.encryption")) == PSET_OP_FAIL)
        {

          AttrNameList list_name2;
          AttributeList initlist2;
          list_name2 = createAttrNameList(1);
          addName(list_name2, 0, "nsEncryptionConfig");
          initlist2 = createAttributeList(9);
          addSingleValueAttribute(initlist2, 0, "nsCertfile", "blank");
          addSingleValueAttribute(initlist2, 1, "nsKeyfile", "blank");
          addSingleValueAttribute(initlist2, 2, "nsSSL2", "off"); /* by default */
          addSingleValueAttribute(initlist2, 3, "nsSSL3", "off"); /* by default */
          addSingleValueAttribute(initlist2, 4, "nsSSLSessionTimeout", "0");
          addSingleValueAttribute(initlist2, 5, "nsSSL3SessionTimeout", "0");
          addSingleValueAttribute(initlist2, 6, "nsSSLClientAuth", "off"); /* hardcoded for now */
          addSingleValueAttribute(initlist2, 7, "nsSSL2Ciphers", "blank");
          addSingleValueAttribute(initlist2, 8, "nsSSL3Ciphers", "blank");

          if(psetAddEntry(pset, "configuration", "encryption", list_name2, initlist2) != PSET_OP_OK) {
            rpt_err(APP_ERROR, getResourceString(DBT_GENERIC_PSET_FAILURE), 
                    getResourceString(DBT_PSET_ATTR_CREATE), NULL);
          }
        }
      else if(rv2 == PSET_ATTR_EXIST)
        /* it exists, but as an attribute?! */
        rpt_err(APP_ERROR, getResourceString(DBT_GENERIC_PSET_FAILURE), 
                getResourceString(DBT_PSET_INV_ATTR), NULL);

      list_name = createAttrNameList(1);
      addName(list_name, 0, "nsEncryptionModule");
      initlist = createAttributeList(3);
      addSingleValueAttribute(initlist, 0, "nsSSLToken", "blank");
      addSingleValueAttribute(initlist, 1, "nsSSLPersonalityssl", "blank");
      addSingleValueAttribute(initlist, 2, "nsSSLActivation", "blank");

      if(psetAddEntry(pset, "configuration.encryption", family_ptr->family_name, list_name, initlist) != PSET_OP_OK) {
        rpt_err(APP_ERROR, getResourceString(DBT_GENERIC_PSET_FAILURE), 
                getResourceString(DBT_PSET_ATTR_CREATE), NULL);
      }
    }
    else if(rv == PSET_ATTR_EXIST)
      /* it exists, but as an attribute?! */
      rpt_err(APP_ERROR, getResourceString(DBT_GENERIC_PSET_FAILURE), 
              getResourceString(DBT_PSET_INV_ATTR), NULL);

    PR_snprintf(family_token_attr, sizeof(family_token_attr), "%s.nsSSLToken", family_sie_entry);
    PR_snprintf(family_cert_attr, sizeof(family_cert_attr), "%s.nsSSLPersonalityssl", family_sie_entry);
    PR_snprintf(family_activated_attr, sizeof(family_activated_attr), "%s.nsSSLActivation", family_sie_entry);
    
    set_attribute(pset, family_token_attr, family_ptr->token_name);
    set_attribute(pset, family_cert_attr, family_ptr->personality);
    set_attribute(pset, family_activated_attr, family_ptr->activated);

    /* delete current object and advance pointer */
    delete_ptr = family_ptr;
    family_ptr = family_ptr->next;
    free(delete_ptr->family_name);
    free(delete_ptr->token_name);
    free(delete_ptr->personality);
    free(delete_ptr->activated);
    free(delete_ptr);
  }

  /* Record cipher prefs */
  if(ssl2)
    set_attribute(pset, "configuration.encryption.nsSSL2Ciphers", ssl2);
  if(ssl3)
    set_attribute(pset, "configuration.encryption.nsSSL3Ciphers", ssl3);
  if(ssl2_act)
    set_attribute(pset, "configuration.encryption.nsSSL2", ssl2_act);
  if(ssl3_act)
    set_attribute(pset, "configuration.encryption.nsSSL3", ssl3_act);

  return 0;
}



static char *
pwdCbk()
{
  char *pwd;

  pwd = ADM_GetPassword("Enter Admin Server Administrator password: ");
  if (!pwd) return NULL;

  admSetCachedSIEPWD(pwd);

  memset(pwd, 0, strlen(pwd));
  free(pwd);

  return admGetCachedSIEPWD();
}



int main(int argc, char *argv[])
{
  int            _ai = ADMUTIL_Init();
  PsetHndl       pset;
  char           *method;
  int            rv;
  int            err;
  int            *errp = &err;
  char           *username = 0;
  char           *localAdmin = 0;
  char           *binddn = 0;

  char *security = NULL;
  char *ssl2 = NULL;
  char *ssl3 = NULL;
  char *tls = NULL;
  char *merged_ssl3 = NULL;
  char *ssl2_act = NULL;
  char *ssl3_act = NULL;
  char *clientauth = NULL;

  char *configdir; /* where to find config files */
  char *securitydir = NULL; /* where to find security files */
  family_input *family_head = NULL;
  char error_info[BIG_LINE];
  AdmldapInfo ldapInfo = NULL;
  char *lang;

  (void)_ai; /* get rid of unused variable warning */
  memset((void *)errp, 0, sizeof(int));
  method = getenv("REQUEST_METHOD");

  if(method && *method) {
    lang=getenv("HTTP_ACCEPT_LANGUAGE");
  }
  else {
    if(!argv[1] || !argv[2]) {
      rpt_err(INCORRECT_USAGE, "Usage: sec-activate [configdir] [SSL enabled]\n[configdir] = The location of the config and security files of the target Admin Server\n[SSL enabled] = 'on' or 'off'", NULL, NULL);
    }
    lang = NULL;
  }
  i18nResource = res_find_and_init_resource(PROPERTYDIR, RESOURCE_FILE);
  if(!i18nResource)
    rpt_err(INCORRECT_USAGE, "Localized property files not found in " PROPERTYDIR, NULL, NULL);
  acceptLanguage = "en";
  if (lang) acceptLanguage = strdup(lang);

  /* Initialize PSET for reading/writing */
  /* Get UserDN and User Password */

  rv = ADM_GetUserDNString(&err, &binddn);
  if (rv < 0 || !binddn || !*binddn) {
    rv = ADM_GetCurrentUsername(&err, &username);
    if (rv < 0 || !username || !*username)
      rpt_err(ELEM_MISSING, getResourceString(DBT_PSET_INIT_NO_USERNAME), NULL, NULL);
    else {
      /* No DN, maybe it is local super */
      configdir = util_get_conf_dir();
      localAdmin = admGetLocalAdmin(configdir, &rv);
      if (localAdmin) {
        if (PORT_Strcmp(username, localAdmin))
          rpt_err(ELEM_MISSING, getResourceString(DBT_PSET_INIT_NO_USERDN), NULL, NULL);
        else {
          binddn = NULL;
        }
      }
      else
        rpt_err(ELEM_MISSING, getResourceString(DBT_PSET_INIT_NO_USERDN), NULL, NULL);
    }
  }

  if(!method || !*method) {
    /* non-UI CGI - can't call ADMSSL_InitSimple() */
    securitydir = strdup(argv[1]);
    configdir = strdup(argv[1]); /* same as securitydir */
    ldapInfo = admldapBuildInfoCbk(configdir, pwdCbk, &rv);

    if (!ldapInfo) {
      rpt_err(INCORRECT_USAGE, getResourceString(DBT_INV_SR), NULL, NULL);
    }

    rv = ADMSSL_Init(ldapInfo, SECURITYDIR, 1);
    if (rv) {
      PR_snprintf(error_info, sizeof(error_info), getResourceString(DBT_PSET_INIT_FAILURE), rv);
      rpt_err(APP_ERROR, error_info, NULL, NULL);
    }
  }
  else {
    configdir =  util_get_conf_dir();
    securitydir = util_get_security_dir();
    rv = ADMSSL_InitSimple(configdir, securitydir, 1);
    if (rv) {
      rpt_err(APP_ERROR, getResourceString(DBT_ADMSSL_INIT_FAIL), NULL, NULL);
    }
  }

  /* Initialize the pset  */

  pset = psetCreateSSL("admin-serv", 
                       configdir,
                       /* userDN */ NULL, 
                       /* passwd */ NULL,
                       /* errorcode */ &rv);

  if (!pset)
    rpt_err(APP_ERROR, getResourceString(DBT_PSET_CREATE_FAIL), NULL, NULL);

  /* Process GET/POST */

  if (!method || !*method) {
    /* non UI CGI */

    security = strdup(argv[2]);
    if((strcmp(security, "on") != 0) &&
       (strcmp(security, "off") != 0)) {
      rpt_err(INCORRECT_USAGE, getResourceString(DBT_CMDLINE_STATUS_USAGE), NULL, NULL);
    }
    /* change "security" in LDAP and adm.conf to "on"/"off" */
    set_security(pset, configdir, security);
    rpt_success(getResourceString(DBT_SUCCESS));

  }
  else if (!PORT_Strcmp(method, "GET")) {
    /* UI CGI - either upgrade or normal admin CGI */
    char *return_string;
    fprintf(stdout, "Content-type: text/html\n\n");

    /* get all variables */
    rv = GetSSLFamilyAttributes(pset, &return_string);
    fputs(return_string, stdout);
    free(return_string);
  }
  else if (!PORT_Strcmp(method, "POST")) {
    fprintf(stdout, "Content-type: text/html\n\n");
    post_begin(stdin);

    security = get_cgi_var("security", "", "");
    ssl2 = get_cgi_var("ssl2", "", "");
    ssl3 = get_cgi_var("ssl3", "", "");
    tls = get_cgi_var("tls", "", "");
    ssl2_act = get_cgi_var("ssl2-activated", "", "");
    ssl3_act = get_cgi_var("ssl3-activated", "", "");
    clientauth = get_cgi_var("clientauth", "", "");

    /* change "security" in LDAP and adm.conf to "on"/"off" */
    set_security(pset, configdir, security);

    /* change security parameters in console.conf */
    if (strcmp(security, "off")==0) {
      rv = update_conf(configdir, "console.conf", "NSSEngine", "off", 0);
      if (rv < 0) {
        rpt_err(APP_ERROR, NULL, getResourceString(DBT_ADMIN_CONF_MOD), NULL);
      }
    }
    else if (strcmp(security, "on")==0) {
      char certnickname[BIG_LINE];
      char protocols[BIG_LINE];
      char ciphers[BIG_LINE];

      /* Parse out complete family list */
      get_family_input(&family_head);

      /* set cipher family info */
      if (ssl3 && (strlen(ssl3) > 0)) {
          if (tls && (strlen(tls) > 0)) {
              merged_ssl3 = PR_smprintf("%s,%s", ssl3, tls);
          } else {
              merged_ssl3 = strdup(ssl3);
          }
      } else {
          if (tls && (strlen(tls) > 0)) {
              merged_ssl3 = strdup(tls);
          } else {
              merged_ssl3 = strdup("");
          }
      }
      SetSSLFamilyAttributes(pset, family_head, ssl2, merged_ssl3, ssl2_act, ssl3_act);

      set_attribute(pset, "configuration.encryption.nsSSLClientAuth", clientauth);

      if  (get_cert_nickname(certnickname, sizeof(certnickname)) < 0) {
        char * scratch = PR_smprintf(getResourceString(DBT_CGI_MISSING_ARGS), certnickname);
        rpt_err(ELEM_MISSING, NULL, scratch, NULL);
        PR_smprintf_free(scratch); /* never executed */
      }
      if (strlen(clientauth) == 0) {
        clientauth = (char*)"off";
      }
      rv = update_conf(configdir, "console.conf", "NSSEngine", "on", 0);
      rv = update_conf(configdir, "console.conf", "NSSNickname", certnickname, 1);

      strcpy(protocols, "");

      if (strlen(ssl2_act) > 0  && !strcmp(ssl2_act, "on"))
          strcat(protocols, "SSLv2,");
      if (strlen(ssl3_act) > 0 && !strcmp(ssl3_act, "on"))
          strcat(protocols, "SSLv3,TLSv1,");
      protocols[strlen(protocols) - 1] = '\0'; /* remove trailing comma */

      rv = update_conf(configdir, "console.conf", "NSSProtocol", protocols, 0);

      snprintf(ciphers, sizeof(ciphers), "%s,%s", ssl2, merged_ssl3);
      PR_smprintf_free(merged_ssl3);
      ciphers[sizeof(ciphers)-1] = 0;
      rv = update_conf(configdir, "console.conf", "NSSCipherSuite", ciphers, 0);

      if (!strcmp(clientauth, "on"))
        rv = update_conf(configdir, "console.conf", "NSSVerifyClient", "require", 0);
      else
        rv = update_conf(configdir, "console.conf", "NSSVerifyClient", "none", 0);

      if (rv < 0) {
        rpt_err(APP_ERROR, NULL, getResourceString(DBT_ADMIN_CONF_MOD), NULL);
      }
    } else {
      rpt_err(INCORRECT_USAGE, getResourceString(DBT_CGI_INVALID_ARGS), NULL, NULL);
    }

    rpt_success(getResourceString(DBT_SUCCESS));
  }
  else {
    fprintf(stdout, "Content-type: text/html\n\n");
    PR_snprintf(error_info, sizeof(error_info), getResourceString(DBT_CGI_UNKNOWN_METH), method);
    rpt_err(INCORRECT_USAGE, error_info, NULL, NULL);
  }

  return 0;
}

/*
 * Modify any attribute in a configuration file with a name/value pair
 * If the attribute value is NULL, remove it from the file completely.
 */
static int update_conf(char *configdir, char *file, char *name, char *val, int quoted) {

  FILE *f;
  int i, modified=0;
  static char filename[BIG_LINE];
  static char inbuf[BIG_LINE];
  static char buf[BIG_LINE];
  int linecnt=0;
  char *lines[2048];

  if (!configdir)
    return -1;

  PR_snprintf(filename, sizeof(filename), "%s/%s", configdir, file);

  f = fopen(filename, "r");
  if (f==NULL) {
    rpt_err(SYSTEM_ERROR, "Can not open file for reading", NULL, NULL);
  }
  while(fgets(inbuf, sizeof(inbuf), f) != NULL) {
    if (strncasecmp(inbuf,name,strlen(name)) == 0) { /* Line starts with the attribute name */
      if(val && *val != '\0') {
        if (quoted) {
          PR_snprintf(buf, sizeof(buf), "%s \"%s\"\n", name, val);
        } else {
          PR_snprintf(buf, sizeof(buf), "%s %s\n", name, val);
        }
        lines[linecnt++] = strdup(buf);
        modified=1;
      }
      else {
        modified=1;
      }
    }
    else {
      lines[linecnt++] = strdup(inbuf);
    }
  }
  fclose(f);

  if (!modified && (val && *val != '\0')) { /* Add the attribute name/val pair*/
    if (quoted) {
      PR_snprintf(buf, sizeof(buf), "%s \"%s\"\n", name, val);
    } else {
      PR_snprintf(buf, sizeof(buf), "%s %s\n", name, val);
    }
    lines[linecnt++] = strdup(buf);
  }

  f = fopen(filename, "w");
  if (f==NULL) {
    fclose(f);
    rpt_err(SYSTEM_ERROR, "Can not open file for writing", NULL, NULL);
  }

  for (i=0; i < linecnt; i++) {
    fprintf(f, "%s", lines[i]);
  }

  fclose(f);
    
  return 0;
    
} 
