/*
 * Copyright (c) 2003-2012
 * Distributed Systems Software.  All rights reserved.
 * See the file LICENSE for redistribution information.
 */

/*
 * dacs_version
 * Return or display version information
 *
 * XXX Could extend to use keyword_scan() to return revid info for a
 * specified DACS program...
 */

#ifndef lint
static const char copyright[] =
"Copyright (c) 2003-2012\n\
Distributed Systems Software.  All rights reserved.";
static const char revid[] =
  "$Id: version.c 2577 2012-03-07 20:53:07Z brachman $";
#endif

#include "dacs.h"

static MAYBE_UNUSED char *log_module_name = "dacs_version";

#ifndef PROG

extern Dsvec *dacs_component_versions(void);

#ifndef DACS_OS_MACOSX
static MAYBE_UNUSED int
dummy(void)
{
  extern int version_file_cred_c;

  return(version_file_cred_c);
}
#endif

#ifndef OMIT_APACHE
#ifdef DACS_OS_MACOSX
#define _ANSI_SOURCE
#endif
#include <ap_release.h>
#endif

Dsvec *
dacs_component_versions(void)
{
  char *str;
  Dsvec *dsv;

  dsv = dsvec_init(NULL, sizeof(char *));
  str = ds_xprintf("Expat %d.%d.%d",
				   XML_MAJOR_VERSION, XML_MINOR_VERSION, XML_MICRO_VERSION);
  dsvec_add_ptr(dsv, str);
  str = ds_xprintf("%s", SSLeay_version(SSLEAY_VERSION));
  dsvec_add_ptr(dsv, str);

#ifndef OMIT_APACHE
#ifdef AP_SERVER_BASEREVISION
  {
	str = ds_xprintf("Apache %s", AP_SERVER_BASEREVISION);
	dsvec_add_ptr(dsv, str);
  }
#endif
#endif

#ifdef ENABLE_BDB
  {
	int major, minor, patch;
	extern char *db_version(int *, int *, int *);

	db_version(&major, &minor, &patch);
	str = ds_xprintf("BerkeleyDB %d.%d.%d", major, minor, patch);
	dsvec_add_ptr(dsv, str);
  }
#endif

#ifdef ENABLE_SQLITE
  {
#include <sqlite3.h>

	str = ds_xprintf("SQLite %s", SQLITE_VERSION);
	dsvec_add_ptr(dsv, str);
  }
#endif

#ifdef ENABLE_NTLM_AUTH
  {
	extern const char *samba_version_string(void);

	str = ds_xprintf("Samba %s", samba_version_string());
	dsvec_add_ptr(dsv, str);
  }
#endif

#ifdef ENABLE_LDAP_AUTH
  {
#include <ldap.h>
#include <ldap_features.h>

	str = ds_xprintf("%s %d.%d.%d", LDAP_VENDOR_NAME,
					 LDAP_VENDOR_VERSION_MAJOR, LDAP_VENDOR_VERSION_MINOR,
					 LDAP_VENDOR_VERSION_PATCH);
	dsvec_add_ptr(dsv, str);
  }
#endif

#ifdef ENABLE_GDBM
  {
	extern char *gdbm_version;

	str = ds_xprintf("%s", gdbm_version);
	dsvec_add_ptr(dsv, str);
  }
#endif

#ifdef HAVE_READLINE
  {
	extern char *rl_library_version;

	str = ds_xprintf("GNU Readline %s", rl_library_version);
	dsvec_add_ptr(dsv, str);
  }
#endif

#ifdef ENABLE_INFOCARD_AUTH
  {
#include <libxml/xmlversion.h>
#include <xmlsec/version.h>

	str = ds_xprintf("libxml %s", LIBXML_DOTTED_VERSION);
	dsvec_add_ptr(dsv, str);
	str = ds_xprintf("xmlsec %s", XMLSEC_VERSION);
	dsvec_add_ptr(dsv, str);
  }
#endif

  return(dsv);
}

char *
dacs_component_versions_string(void)
{
  int i;
  char *p;
  Ds ds;
  Dsvec *versions;

  versions = dacs_component_versions();
  ds_init(&ds);
  for (i = 0; i < dsvec_len(versions); i++) {
	p = (char *) dsvec_ptr_index(versions, i);
	ds_asprintf(&ds, "%s%s", (i > 0) ? ", " : "", p);
  }

  return(ds_buf(&ds));
}

#define NSYM(X)		\
  { #X, X }

static struct nsym {
  char *name;
  int value;
} nsyms[] = {
#ifdef DACS_OS_IS_LITTLE_ENDIAN
  NSYM(DACS_OS_IS_LITTLE_ENDIAN),
#endif
#ifdef DACS_OS_IS_BIG_ENDIAN
  NSYM(DACS_OS_IS_BIG_ENDIAN),
#endif
#ifdef ENABLE_FRAME_MEMORY
  NSYM(ENABLE_FRAME_MEMORY),
#endif
#ifdef ENABLE_APACHE_AUTH
  NSYM(ENABLE_APACHE_AUTH),
#endif
#ifdef ENABLE_CAS_AUTH
  NSYM(ENABLE_CAS_AUTH),
#endif
#ifdef ENABLE_CERT_AUTH
  NSYM(ENABLE_CERT_AUTH),
#endif
#ifdef ENABLE_GRID_AUTH
  NSYM(ENABLE_GRID_AUTH),
#endif
#ifdef ENABLE_HTTP_AUTH
  NSYM(ENABLE_HTTP_AUTH),
#endif
#ifdef ENABLE_INFOCARD_AUTH
  NSYM(ENABLE_INFOCARD_AUTH),
#endif
#ifdef ENABLE_LDAP_AUTH
  NSYM(ENABLE_LDAP_AUTH),
#endif
#ifdef ENABLE_NATIVE_AUTH
  NSYM(ENABLE_NATIVE_AUTH),
#endif
#ifdef ENABLE_NTLM_AUTH
  NSYM(ENABLE_NTLM_AUTH),
#endif
#ifdef ENABLE_PAM_AUTH
  NSYM(ENABLE_PAM_AUTH),
#endif
#ifdef ENABLE_PASSWD_AUTH
  NSYM(ENABLE_PASSWD_AUTH),
#endif
#ifdef ENABLE_SIMPLE_AUTH
  NSYM(ENABLE_SIMPLE_AUTH),
#endif
#ifdef ENABLE_TOKEN_AUTH
  NSYM(ENABLE_TOKEN_AUTH),
#endif
#ifdef ENABLE_UNIX_AUTH
  NSYM(ENABLE_UNIX_AUTH),
#endif
#ifdef ENABLE_UNIX_ROLES
  NSYM(ENABLE_UNIX_ROLES),
#endif
#ifdef ENABLE_LOCAL_ROLES
  NSYM(ENABLE_LOCAL_ROLES),
#endif
#ifdef ENABLE_FTS
  NSYM(ENABLE_FTS),
#endif
#ifdef ENABLE_BDB
  NSYM(ENABLE_BDB),
#endif
#ifdef ENABLE_SQLITE
  NSYM(ENABLE_SQLITE),
#endif
#ifdef ENABLE_NDBM
  NSYM(ENABLE_NDBM),
#endif
#ifdef ENABLE_GDBM
  NSYM(ENABLE_GDBM),
#endif
#ifdef ENABLE_SDBM
  NSYM(ENABLE_SDBM),
#endif
#ifdef ENABLE_USER_INFO
  NSYM(ENABLE_USER_INFO),
#endif
#ifdef ENABLE_ACCESS_TOKENS
  NSYM(ENABLE_ACCESS_TOKENS),
#endif
#ifdef _BSD_SOURCE
  NSYM(_BSD_SOURCE),
#endif
#ifdef _ISOC99_SOURCE
  NSYM(_ISOC99_SOURCE),
#endif
#ifdef HAVE_READLINE
  NSYM(HAVE_READLINE),
#endif
#ifdef HAVE_LOCKF
  NSYM(HAVE_LOCKF),
#endif
#ifdef HAVE_SHADOW_H
  NSYM(HAVE_SHADOW_H),
#endif
#ifdef HAVE_SHM_OPEN
  NSYM(HAVE_SHM_OPEN),
#endif
  { NULL, 0 }
};

#define ADD_SSYM(DSV, X)							\
  dsvec_add_ptr(DSV, ds_xprintf("%s=%s", #X, strquote(X, NULL)))
#define ADD_DSYM(DSV, X)							\
  dsvec_add_ptr(DSV, ds_xprintf("%s=%d", X.name, X.value))

Dsvec *
dacs_config_symbols_string(void)
{
  int i;
  Dsvec *dsv;

  dsv = dsvec_init(NULL, sizeof(char *));

#ifdef GCC_VERSION
  ADD_SSYM(dsv, GCC_VERSION);
#endif
#ifdef DACS_HOME
  ADD_SSYM(dsv, DACS_HOME);
#endif
#ifdef DACS_CONF
  ADD_SSYM(dsv, DACS_CONF);
#endif
#ifdef DACS_SITE_CONF
  ADD_SSYM(dsv, DACS_SITE_CONF);
#endif
#ifdef DACS_SBIN
  ADD_SSYM(dsv, DACS_SBIN);
#endif
#ifdef DACS_LOG
  ADD_SSYM(dsv, DACS_LOG);
#endif
#ifdef FEDERATIONS_ROOT
  ADD_SSYM(dsv, FEDERATIONS_ROOT);
#endif
#ifdef APACHE_HOME
  ADD_SSYM(dsv, APACHE_HOME);
#endif
#ifdef DACS_BINDIR
  ADD_SSYM(dsv, DACS_BINDIR);
#endif
#ifdef CGIBINDIR
  ADD_SSYM(dsv, CGIBINDIR);
#endif
#ifdef CGI_SUFFIX
  ADD_SSYM(dsv, CGI_SUFFIX);
#endif
#ifdef EXE_SUFFIX
  ADD_SSYM(dsv, EXE_SUFFIX);
#endif
#ifdef HTDOCSDIR
  ADD_SSYM(dsv, HTDOCSDIR);
#endif
#ifdef OPENSSL_PROG
  ADD_SSYM(dsv, OPENSSL_PROG);
#endif
#ifdef SENDMAIL_PROG
  ADD_SSYM(dsv, SENDMAIL_PROG);
#endif
#ifdef SENDMAIL_ARGS
  ADD_SSYM(dsv, SENDMAIL_ARGS);
#endif
#ifdef MAILER_PROG
  ADD_SSYM(dsv, MAILER_PROG);
#endif
#ifdef MAILER_ARGS
  ADD_SSYM(dsv, MAILER_ARGS);
#endif

  for (i = 0; nsyms[i].name != NULL; i++)
	ADD_DSYM(dsv, nsyms[i]);

  return(dsv);
}

static void
fmt_row(FILE *fp, int row, char *name, char *value)
{

  fprintf(fp, "<tr class=\"%s\"><td class=\"varname\">%s</td>",
		  (row % 2) ? "tr_odd" : "tr_even", name);
  fprintf(fp, "<td class=\"varvalue\">%s</td></tr>\n", value);

}

int
dacsversion_main(int argc, char **argv, int do_init, void *main_out)
{
  int i, emit_xml, xargc, rc;
  char **xargv, *errmsg, *p, *remote_addr, *versions;
  DACS_app_type app_type;
  Dsvec *conf_dsv, *dsv;
  Html_header_conf *hc;
  Kwv *kwv;

  errmsg = "internal";
  hc = emit_html_header_conf(NULL);
  hc->no_cache = 1;
  hc->bgcolor = NULL;

  if ((remote_addr = getenv("REMOTE_ADDR")) == NULL) {
	app_type = DACS_STANDALONE;
	log_module_name = "dacsversion";
	emit_xml = 0;
	rc = 0;
  }
  else {
	app_type = DACS_WEB_SERVICE;
	log_module_name = "dacs_version";
	emit_xml = 1;
	rc = 1;
  }

  versions = dacs_component_versions_string();

#ifndef NOTDEF
  dsv = keyword_scan(NULL);
#else
  {
	Dsvec *keyword_scan_mem(char *, char *);
	extern etext, edata;

	dsv = keyword_scan_mem(etext, edata);
  }
#endif

  xargc = argc;
  xargv = argv;
  if (dacs_init(app_type, &argc, &argv, &kwv, &errmsg) == -1)
	goto fail;

  if (should_use_argv) {
	if (argc != 1) {
	  log_msg((LOG_ERROR_LEVEL, "Unrecognized flag: %s", argv[1]));
	  goto fail;
	}
  }

  emit_xml = test_emit_xml_format();

  rc = 0;

 fail:
  if (emit_xml) {
	emit_xml_header(stdout, "dacs_version");
	printf("<%s", make_xml_root_element("dacs_version"));
	printf(" number=\"%s\"\n", DACS_VERSION_NUMBER);
	printf(" release=\"%s\"\n", DACS_VERSION_RELEASE);
	printf(" date=\"%s\"\n", DACS_VERSION_DATE);
	printf(" revid=\"%s\"\n", DACS_VERSION_REVID);
	printf(" build_os=\"%s\"\n", dacs_build_os_string());
	printf(" exec_os=\"%s\"\n", dacs_runtime_os_string());
	printf(" other=\"%s\">\n", versions);
	for (i = 0; dsv != NULL && (p = dsvec_ptr_index(dsv, i)) != NULL; i++)
	  printf(" <file revid=\"%s\"/>\n", p);
	printf("</dacs_version>\n");
	emit_xml_trailer(stdout);
  }
  else if (test_emit_format(EMIT_FORMAT_HTML)) {
	if (conf_val(CONF_CSS_PATH) != NULL)
	  hc->css = ds_xprintf("%s/dacs_version.css", conf_val(CONF_CSS_PATH));
	else
	  hc->css = CSS_DIR/**/"/dacs_version.css";
	hc->title = ds_xprintf("DACS Version Information for %s",
						   dacs_current_jurisdiction());
	emit_html_header(stdout, hc);

	if (rc == 0) {
	  int r;
	  Dsvec *dsv_attr;
	  Html_table *tab;

	  printf("<h1>%s</h1><p>\n", hc->title);
	  tab = html_table(NULL, NULL);
	  tab->row_class = "tr";
	  tab->auto_row_nclasses = 2;
	  tab->auto_column_class = "td";
	  dsv_attr = dsvec_init(NULL, sizeof(char *));
	  dsvec_add_ptr(dsv_attr, "width=\"100%\"");
	  html_table_begin(tab, dsv_attr, 2);

	  html_cell2(tab, "number", DACS_VERSION_NUMBER);
	  html_cell2(tab, "release", DACS_VERSION_RELEASE);
	  html_cell2(tab, "date", DACS_VERSION_DATE);
	  html_cell2(tab, "revid", DACS_VERSION_REVID);
	  html_cell2(tab, "build_platform", dacs_build_os_string());
	  html_cell2(tab, "exec_platform", dacs_runtime_os_string());
	  html_cell2(tab, "other", versions);

	  for (i = 0; dsv != NULL && (p = dsvec_ptr_index(dsv, i)) != NULL; i++)
		html_cell2(tab, "file", p);

	  if ((dsv = dacs_config_symbols_string()) != NULL) {
		for (i = 0; (p = dsvec_ptr_index(dsv, i)) != NULL; i++)
		  html_cell2(tab, "symbol", p);
	  }

	  html_table_end(tab);
	  printf("%s", ds_buf(tab->ds));
	  html_table_free(tab);

	  printf("</p>\n");
	}
	else {
	  printf("<p>dacs_version: fatal error.</p>\n");
	  printf("<p>%s</p>\n", errmsg);
	}

	emit_html_trailer(stdout);
  }
  else {
	if (verbose_level) {
	  printf("number=\"%s\"\n", DACS_VERSION_NUMBER);
	  printf("release=\"%s\"\n", DACS_VERSION_RELEASE);
	  printf("date=\"%s\"\n", DACS_VERSION_DATE);
	  printf("revid=\"%s\"\n", DACS_VERSION_REVID);
	  printf("build_platform=\"%s\"\n", dacs_build_os_string());
	  printf("exec_platform=\"%s\"\n", dacs_runtime_os_string());
	  printf("other=\"%s\"\n", versions);
	  if (verbose_level > 1) {
		for (i = 0; dsv != NULL && (p = dsvec_ptr_index(dsv, i)) != NULL; i++)
		  printf("file_revid=\"%s\"\n", p);
		if ((dsv = dacs_config_symbols_string()) != NULL) {
		  for (i = 0; (p = dsvec_ptr_index(dsv, i)) != NULL; i++)
			printf("symbol=\"%s\"\n", p);
		}
	  }
	}
	else
	  printf("%s\n", dacs_version_string());
  }

  return(rc);
}

#else

int
main(int argc, char **argv)
{
  int rc;

  if ((rc = dacsversion_main(argc, argv, 1, NULL)) == 0)
	exit(0);

  exit(1);
}
#endif
