/** 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 **/
/*
 * dsconfig.c -- CGI configuration update handler -- directory gateway
 *
 * Copyright (c) 1996 Netscape Communications Corp.
 * All rights reserved.
 */

 /*
 * Default Directory Server setting get/set. Read/modify DS ldap url from adm.conf
 *
 * Get Operation
 * in:  op=getconfig
 * out: dsconfig.host=<host from DS ldap url>
 * out: dsconfig.port=<port from DS ldap url>
 * out: dsconfig.basedn=<dn from DS ldap url>
 * out: dsconfig.ssl= 'true' if ldaps used, 'false' if ldap used
 *
 * Set Operation
 * in:op=setconfig&dsconfig.host=<host>&dsconfig.port=<port>&
 * dsconfig.ssl=<true|false>
 *   1) modify DS ldap url in adm.conf with supplied parameters
 */

#include "libadmin/libadmin.h"
#include "string.h"

#include "libadminutil/resource.h"
#include "libadminutil/admutil.h"
#include "libadminutil/distadm.h"

#include "libadmsslutil/psetcssl.h"
#include "libadmsslutil/admsslutil.h"
#include "libadmsslutil/certmgt.h"
#include "cert.h"

#include "config.h"

/*#ifdef XP_WIN32
#define strcasecmp stricmp
#define strncasecmp _strnicmp
#endif*/

static void handle_getconfig();
static void handle_setconfig();
static char *create_new_ldapurl(char *new_host, int new_port, char *new_basedn, int new_ssl);
static void update_adm_conf(char *ldapURL, char *sieDN, char *isieDN);
static char *nonull_value(char *str);


/*
 * Logging function
 */
static FILE * logfp;
static int log_enabled = 0;
static void logMsg(char *format, ...) {
  char logfile[512];

  if (!log_enabled) return;

  if (!util_get_log_dir()) {
      return;
  }

  if (logfp==NULL) {
    PR_snprintf(logfile, sizeof(logfile), "%s/dsconfig.dbg", util_get_log_dir());
    logfp = fopen(logfile, "w");
  }

  if (logfp != NULL) {
    va_list ap;
    va_start(ap,format);
    vfprintf(logfp,format,ap);
    va_end(ap);
    fflush(logfp);
  }
}

/*
 * i18n conversions defines and function;
 * properties file  = "dsconfig.properties"
 */
#define RESOURCE_FILE "dsconfig"
#define resource_key(a,b)   a b

#define DBT_NO_METHOD 			resource_key(RESOURCE_FILE, "1")
#define DBT_UNKNOWN_METHOD 		resource_key(RESOURCE_FILE, "2")
#define DBT_NO_QUERY_STRING 		resource_key(RESOURCE_FILE, "3")
#define DBT_NO_OPERATION 		resource_key(RESOURCE_FILE, "4")
#define DBT_UNKNOWN_OPERATION		resource_key(RESOURCE_FILE, "5")
#define DBT_BAD_PORT			resource_key(RESOURCE_FILE, "9")
#define DBT_NO_CERTDB	       	resource_key(RESOURCE_FILE, "10")
#define DBT_OPEN_CERTDB	       	resource_key(RESOURCE_FILE, "11")
#define DBT_BAD_SSL       		resource_key(RESOURCE_FILE, "12")
#define DBT_OPEN_ADM_RD     		resource_key(RESOURCE_FILE, "16")
#define DBT_NO_LDAPURL  		resource_key(RESOURCE_FILE, "17")
#define DBT_BAD_LDAPURL  		resource_key(RESOURCE_FILE, "18")
#define DBT_OPEN_ADM_WR     		resource_key(RESOURCE_FILE, "19")
#define DBT_NO_SIE     		        resource_key(RESOURCE_FILE, "20")
#define DBT_NO_ISIE     		resource_key(RESOURCE_FILE, "21")


static  char *acceptLanguage = "en";
static  Resource *i18nResource = NULL;

static void i18nInit() {
  i18nResource = res_find_and_init_resource(PROPERTYDIR, RESOURCE_FILE);

  if (getenv("HTTP_ACCEPT_LANGUAGE")) {
    acceptLanguage = getenv("HTTP_ACCEPT_LANGUAGE");
  }
}
static const char *i18nMsg(char *msgid, char *defaultMsg) {
  const char *msg=NULL;
  static char msgbuf[BUFSIZ]; /* ok - not threaded code */

  if (i18nResource) {
    msg = res_getstring(i18nResource, msgid, acceptLanguage, msgbuf, sizeof(msgbuf), NULL);
  }
  if (msg == NULL) {
    msg = (const char*)defaultMsg;
  }
  return msg;
}

static char * nonull_value(char *str) {
  return (str!=NULL) ? str : (char *)"";
}

/*
 *  Main
 */
int main(int argc, char *argv[])
{
  int _ai=ADMUTIL_Init();

  char *method = getenv("REQUEST_METHOD");
  char *qs = 0, *op=0;
  char error_info[128];

  logMsg(" In %s\n", argv[0]);

  (void)_ai; /* get rid of unused variable warning */
  i18nInit();

  /* GET or POST method */
  if (!method || !*method) {
    /* non UI CGI */
    rpt_err(SYSTEM_ERROR, i18nMsg(DBT_NO_METHOD,"No method is specified"), NULL, NULL);
  }
  else if (!strcmp(method, "GET")) {
    /* UI CGI - either upgrade or normal admin CGI */
    qs = getenv("QUERY_STRING");

    if (!qs || !*qs) {
      rpt_err(INCORRECT_USAGE, i18nMsg(DBT_NO_QUERY_STRING ,"NO QUERY_STRING DATA"), NULL, NULL);
    }
    else {
      get_begin(qs);
    }
  }
  else if (!strcmp(method, "POST")) {
    post_begin(stdin);
  }
  else {
    PR_snprintf(error_info, sizeof(error_info), i18nMsg(DBT_UNKNOWN_METHOD,"Unknown Method (%s)"), method);
    rpt_err(INCORRECT_USAGE, error_info, NULL, NULL);
  }

  logMsg("method=%s\n", method);

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

  logMsg("op=%s\n", op);

  if (op == NULL) {
    rpt_err(INCORRECT_USAGE, i18nMsg(DBT_NO_OPERATION,"No operation is defined"), NULL, NULL);
  }
  if (strcmp(op,"getconfig") == 0) {
    handle_getconfig();
  }
  else if (strcmp(op,"setconfig") == 0) {
    handle_setconfig();
  }
  else {
    PR_snprintf(error_info, sizeof(error_info), i18nMsg(DBT_UNKNOWN_OPERATION,"Unknown Operation (%s)"), op);
    rpt_err(INCORRECT_USAGE, error_info, NULL, NULL);
  }

  return 0;
}

static char *
get_ldap_url()
{
  char *ldapurl = NULL;
  int errorcode = 0;
  AdmldapInfo admInfo = NULL;
  char *configdir = util_get_conf_dir();

  logMsg("In get_ldap_url\n");
  admInfo = admldapBuildInfoOnly(configdir, &errorcode);
  if (!admInfo || errorcode) {
	  logMsg("Could not get ldap info from config - %d", errorcode);
	  return NULL;
  }
  ldapurl = admldapGetDirectoryURL(admInfo); /* returns a copy */
  destroyAdmldap(admInfo);

  logMsg("Finished get_ldap_url, url is %s\n", ldapurl);

  return ldapurl;
}

/*
 * Return current ldap url setting
 */
static void handle_getconfig()
{
  int		rc;
  LDAPURLDesc	*ludp;
  char *ldapurl = NULL;
  int ssl;

  logMsg("In handle_getconfig\n");

  ldapurl = get_ldap_url();
  logMsg("baseurl=%s\n", ldapurl);

  if (( rc = util_ldap_url_parse( ldapurl, &ludp, 0, &ssl )) != 0 ) {
	char error_info[128];
	PR_snprintf(error_info, sizeof(error_info),
				i18nMsg(DBT_BAD_LDAPURL, "LDAP URL (%s) is invalid"), ldapurl);
    logMsg("ldap_url_parse(%s) failed, rc=%d\n", ldapurl, rc);
    rpt_err(SYSTEM_ERROR, error_info, NULL, NULL);
  }
  else {

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

    fprintf(stdout, "dsconfig.host:%s\n", nonull_value(ludp->lud_host));
    logMsg("dsconfig.host:%s\n", nonull_value(ludp->lud_host));

    fprintf(stdout, "dsconfig.port:%d\n", ludp->lud_port);
    logMsg("dsconfig.port:%d\n", ludp->lud_port);

    fprintf(stdout, "dsconfig.ssl:%s\n", ssl ? "true" : "false");
    logMsg("dsconfig.ssl:%s\n", ssl ? "true" : "false");


    fprintf(stdout, "dsconfig.basedn:%s\n", nonull_value(ludp->lud_dn));
    logMsg("dsconfig.basedn:%s\n", nonull_value(ludp->lud_dn));

    fprintf(stdout, "NMC_Status: 0\n");

  }
  PL_strfree(ldapurl);
}

/*
 * Modify ldap url setting
 */
static void handle_setconfig() {
  char *sieDN=NULL, *isieDN=NULL, *host=NULL, *basedn=NULL, *ldapURL=NULL, *port_s=NULL, *ssl_s=NULL;
  int ssl=-1, port=-1;


  sieDN  = get_cgi_var( "dsconfig.sieDN", NULL, NULL );
  isieDN = get_cgi_var( "dsconfig.isieDN", NULL, NULL );
  host   = get_cgi_var( "dsconfig.host", NULL, NULL );
  port_s = get_cgi_var( "dsconfig.port", NULL, NULL );
  ssl_s  = get_cgi_var( "dsconfig.ssl", NULL, NULL );

  if (port_s != NULL) {
    port   = atoi( port_s);
    if (port < 1 || port > 65535) {
      rpt_err(INCORRECT_USAGE, i18nMsg(DBT_BAD_PORT,"Bad value for dsconfig.port"), NULL, NULL);
    }
  }
  if (ssl_s != NULL) {
    if (strcasecmp(ssl_s, "true") == 0) {
      ssl = 1;
    }
    else if (strcasecmp(ssl_s, "false") == 0) {
      ssl = 0;
    }
    else {
      rpt_err(INCORRECT_USAGE, i18nMsg(DBT_BAD_SSL,"Bad value for dsconfig.ssl"), NULL, NULL);
    }
  }

  ldapURL = create_new_ldapurl(host, port, basedn, ssl);

  logMsg("new ldap url = %s", nonull_value(ldapURL));

  if (ldapURL != NULL) {
    update_adm_conf(ldapURL, sieDN, isieDN);

    fprintf(stdout, "Content-type: text/html\n\n");
    fprintf(stdout, "NMC_Status: 0\n");
  }
}


/*
 * Create new ldap url
 */
static char* create_new_ldapurl(char *new_host, int new_port, char *new_basedn, int new_ssl)
{
  int		rc;
  LDAPURLDesc	*ludp;
  int sslflag;
  char url[BIG_LINE];
  char *curldapurl = NULL;
  char *host, *basedn, *ssl;
  int port;

  curldapurl = get_ldap_url();
  logMsg("baseurl=%s\n", curldapurl);

  if (( rc = util_ldap_url_parse( curldapurl, &ludp, 0, &sslflag )) != 0 ) {
    logMsg("ldap_url_parse(%s) failed, rc=%d\n", curldapurl, rc);
    rpt_err(SYSTEM_ERROR, i18nMsg(DBT_BAD_LDAPURL,"Bad ldap url in adm.conf"), NULL, NULL);
  }

  else {

    host = (new_host != NULL) ? new_host : ludp->lud_host;
    port = (new_port != -1) ? new_port : ludp->lud_port;
    basedn = (new_basedn != NULL) ? new_basedn : ludp->lud_dn;
    sslflag = (new_ssl != -1) ? new_ssl : sslflag;
    ssl = (sslflag) ? (char *)"s" : (char *)"";

    PR_snprintf(url, sizeof(url), "ldap%s://%s:%d/%s", ssl, host, port, basedn);
  }
  PL_strfree(curldapurl);
  return strdup(url);
}

/*
 * Modify adm.conf
 */
static void update_adm_conf(char *ldapURL, char *sieDN, char *isieDN) {
  AdmldapInfo admInfo = NULL;
  int errorcode = 0;
  char *configdir = util_get_conf_dir();

  /* get a handle to the current one */
  admInfo = admldapBuildInfoOnly(configdir, &errorcode);
  if (!admInfo || errorcode) {
    rpt_err(SYSTEM_ERROR, i18nMsg(DBT_OPEN_ADM_RD, "Can not open adm.conf for reading"), NULL, NULL);
  }

  if (ldapURL) {
	if (admldapSetDirectoryURL(admInfo, ldapURL)) {
	  char error_info[128];
	  PR_snprintf(error_info, sizeof(error_info),
				  i18nMsg(DBT_BAD_LDAPURL, "LDAP URL (%s) is invalid"), ldapURL);
	  rpt_err(SYSTEM_ERROR, error_info, NULL, NULL);
	}
  }

  if (sieDN && admldapSetSIEDN(admInfo, sieDN)) {
    rpt_err(SYSTEM_ERROR, i18nMsg(DBT_NO_SIE, "Parameter sie: not found in adm.conf"), NULL, NULL);
  }

  if (isieDN && admldapSetISIEDN(admInfo, isieDN)) {
    rpt_err(SYSTEM_ERROR, i18nMsg(DBT_NO_ISIE, "Parameter isie: not found in adm.conf"), NULL, NULL);
  }

  if (admldapWriteInfoFile(admInfo)) {
    rpt_err(SYSTEM_ERROR, i18nMsg(DBT_OPEN_ADM_WR, "Can not open adm.conf for writing"), NULL, NULL);
  }
  destroyAdmldap(admInfo);
}
