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

/*****************************************************************************
 * COPYRIGHT AND PERMISSION NOTICE
 * 
 * Copyright (c) 2001-2003 The Queen in Right of Canada
 * 
 * All rights reserved.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation 
 * the rights to use, copy, modify, merge, publish, distribute, and/or sell
 * copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, provided that the above copyright notice(s) and this
 * permission notice appear in all copies of the Software and that both the
 * above copyright notice(s) and this permission notice appear in supporting
 * documentation.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE 
 * BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES,
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 
 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 
 * SOFTWARE.
 * 
 * Except as contained in this notice, the name of a copyright holder shall not
 * be used in advertising or otherwise to promote the sale, use or other
 * dealings in this Software without prior written authorization of the
 * copyright holder.
 ***************************************************************************/

/*
 * DACS service to return a list of jurisdictions in the federation, including
 * meta-information for each jurisdiction.
 * The primary purpose of this service it to allow user agents to present
 * menus and such for login purposes.
 */

#ifndef lint
static const char copyright[] =
"Copyright (c) 2003-2014\n\
Distributed Systems Software.  All rights reserved.";
static const char revid[] =
  "$Id: list_jurisdictions.c 2710 2014-09-04 23:10:39Z brachman $";
#endif

#include "dacs.h"

static MAYBE_UNUSED char *log_module_name = "list_jurisdictions";

#ifndef PROG

/*
 * Construct a URL.  Use DACS_URL if it looks like a URL, otherwise
 * do some guessing and interpolation - see directory_name_interpolate().
 * For possible interpolation, a hostname is formed by prepending JNAME to
 * the current jurisdiction's federation domain suffix.
 */
char *
expand_dacs_url(char *jname, char *dacs_url)
{
  char *url, *xhost, *xurl;

  if (!strncaseeq(dacs_url, "http", 4)) {
	xhost = ds_xprintf("%s.%s", strtolower(jname),
					   strtolower(conf_val(CONF_FEDERATION_DOMAIN)));
	xurl = directory_name_interpolate(dacs_url, xhost, getenv("SERVER_PORT"));
	url = ds_xprintf("%s://%s", dacs_is_https_request() ? "https" : "http",
					 xurl);
  }
  else
	url = dacs_url;

  return(url);
}

static int
do_list_jurisdictions(FILE *fp, char *jname, int key_only, char **errmsg)
{
  int i;
  char *fed_id, *fed_public_key, *fed_public_key_pem;
  char *jur_public_key, *jur_public_key_pem;
  Crypt_keys *ck;
  Jurisdiction *j;

  log_msg((LOG_TRACE_LEVEL, "JURISDICTION=\"%s\"",
		   (jname == NULL) ? "<none>" : jname));
  if (jname == NULL) {
	if (load_jurisdictions() == -1) {
	  *errmsg = "Error loading jurisdictions";
	  return(-1);
	}
	j = jurisdictions;
  }
  else if (*jname == '\0') {
	j = NULL;
	njurisdictions = 0;
  }
  else {
	if (get_jurisdiction_meta(jname, &j) == -1) {
	  *errmsg = "Cannot find requested Jurisdiction name";
	  return(-1);
	}
	njurisdictions = 1;
  }

  fed_id = NULL;
  fed_public_key_pem = NULL;
  if ((ck = crypt_keys_from_vfs(ITEM_TYPE_FEDERATION_KEYS)) != NULL) {
	if (ck->public_key_pem != NULL)
	  fed_public_key_pem = strdup(ck->public_key_pem);
	if (ck->fed_id != NULL)
	  fed_id = strdup(ck->fed_id);
	crypt_keys_free(ck);
  }

#ifndef NOTDEF
  if (j != NULL)
	jur_public_key_pem = j->public_key_pem;
#else
  /*
   * XXX currently, only this jurisdiction's public key is accessible.
   * The public keys should be incorporated in the group information that
   * is distributed amongst the jurisdictions.
   */
  jur_public_key_pem = NULL;
  if ((ck = crypt_keys_from_vfs(ITEM_TYPE_JURISDICTION_KEYS)) != NULL) {
	if (ck->public_key_pem != NULL)
	  jur_public_key_pem = strdup(ck->public_key_pem);
	crypt_keys_free(ck);
  }
#endif

  if (test_emit_xml_format()) {
	emit_xml_header(fp, "dacs_list_jurisdictions");
	fprintf(fp, "<%s", make_xml_root_element("dacs_list_jurisdictions"));
	fprintf(fp, " federation=\"%s\"", conf_val(CONF_FEDERATION_NAME));
	fprintf(fp, " domain=\"%s\"", conf_val(CONF_FEDERATION_DOMAIN));
	if (fed_id != NULL)
	  fprintf(fp, " fed_id=\"%s\"", fed_id);

	if (fed_public_key_pem != NULL) {
	  mime_encode_base64((unsigned char *) fed_public_key_pem,
						 strlen(fed_public_key_pem), &fed_public_key);
	  fprintf(fp, " fed_public_key=\"%s\"", fed_public_key);
	}

	fprintf(fp, ">\n");

	for (i = 0; i < njurisdictions; i++) {
	  fprintf(fp, "<jurisdiction jname=\"%s\"", j[i].jname);
	  fprintf(fp, " name=\"%s\"", j[i].name);
	  fprintf(fp, " alt_name=\"%s\"", j[i].alt_name);
	  fprintf(fp, " dacs_url=\"%s\"",
			  expand_dacs_url(j[i].jname, j[i].dacs_url));
	  fprintf(fp, " authenticates=\"%s\"", j[i].authenticates);
	  fprintf(fp, " prompts=\"%s\"", j[i].prompts);
	  if (j[i].auxiliary != NULL)
		fprintf(fp, " auxiliary=\"%s\"", j[i].auxiliary);

#ifndef NOTDEF
	  if (j[i].public_key_pem != NULL) {
		mime_encode_base64((unsigned char *) j[i].public_key_pem,
						   strlen(j[i].public_key_pem), &jur_public_key);
		fprintf(fp, " pub_key=\"%s\"", jur_public_key);
	  }
#else
	  if (streq(j[i].jname, conf_val(CONF_JURISDICTION_NAME))
		  && jur_public_key_pem != NULL) {
		mime_encode_base64((unsigned char *) jur_public_key_pem,
						   strlen(jur_public_key_pem), &jur_public_key);
		fprintf(fp, " pub_key=\"%s\"", jur_public_key);
	  }
#endif

	  fprintf(fp, "/>\n");
	}
	fprintf(fp, "</dacs_list_jurisdictions>\n");
	emit_xml_trailer(fp);
  }
  else if (test_emit_format(EMIT_FORMAT_JSON)) {
	emit_json_header(fp, "dacs_list_jurisdictions");
	fprintf(fp, "\"federation\":\"%s\"", conf_val(CONF_FEDERATION_NAME));
	fprintf(fp, ", \"domain\":\"%s\"", conf_val(CONF_FEDERATION_DOMAIN));
	if (fed_id != NULL)
	  fprintf(fp, ", \"fed_id\":\"%s\"", fed_id);

	if (fed_public_key_pem != NULL) {
	  mime_encode_base64((unsigned char *) fed_public_key_pem,
						 strlen(fed_public_key_pem), &fed_public_key);
	  fprintf(fp, ", \"fed_public_key\":\"%s\"", fed_public_key);
	}

	for (i = 0; i < njurisdictions; i++) {
	  if (i == 0)
		fprintf(fp, ", \"jurisdictions\": [\n");
	  else
		fprintf(fp, " },\n");
	  fprintf(fp, "{ \"jname\":\"%s\"", j[i].jname);
	  fprintf(fp, ", \"name\":\"%s\"", j[i].name);
	  fprintf(fp, ", \"alt_name\":\"%s\"", j[i].alt_name);
	  fprintf(fp, ", \"dacs_url\":\"%s\"",
			  expand_dacs_url(j[i].jname, j[i].dacs_url));
	  fprintf(fp, ", \"authenticates\":\"%s\"", j[i].authenticates);
	  fprintf(fp, ", \"prompts\":\"%s\"", j[i].prompts);
	  if (j[i].auxiliary != NULL)
		fprintf(fp, ", \"auxiliary\":\"%s\"", j[i].auxiliary);

#ifndef NOTDEF
	  if (j[i].public_key_pem != NULL) {
		mime_encode_base64((unsigned char *) j[i].public_key_pem,
						   strlen(j[i].public_key_pem), &jur_public_key);
		fprintf(fp, ", \"pub_key\":\"%s\"", jur_public_key);
	  }
#else
	  if (streq(j[i].jname, conf_val(CONF_JURISDICTION_NAME))
		  && jur_public_key_pem != NULL) {
		mime_encode_base64((unsigned char *) jur_public_key_pem,
						   strlen(jur_public_key_pem), &jur_public_key);
		fprintf(fp, ", \"pub_key\":\"%s\"", jur_public_key);
	  }
#endif
	}
	if (njurisdictions)
	  fprintf(fp, " } ]\n");
	fprintf(fp, "}\n");
	emit_json_trailer(fp);
  }
  else if (test_emit_format(EMIT_FORMAT_PHP)) {
	emit_plain_header(fp);
	for (i = 0; i < njurisdictions; i++) {
	  if (strcaseeq(j[i].authenticates, "yes"))
		fprintf(fp, "jurisdiction[]=%s&host[]=%s@%s\n",
				j[i].jname, expand_dacs_url(j[i].jname, j[i].dacs_url),
				j[i].jname);
	}
	emit_plain_trailer(stdout);
  }
  else if (test_emit_format(EMIT_FORMAT_HTML)) {
	Html_header_conf *hc;

	hc = emit_html_header_conf(NULL);
	if (conf_val(CONF_CSS_PATH) != NULL)
	  hc->css = ds_xprintf("%s/dacs_list_jurisdictions.css",
						   conf_val(CONF_CSS_PATH));
	else
	  hc->css = CSS_DIR/**/"/dacs_list_jurisdictions.css";
	hc->title = ds_xprintf("Jurisdiction List for Federation: %s",
						   conf_val(CONF_FEDERATION_NAME));

	emit_html_header(fp, hc);
	fprintf(fp, "<center>\n");
	fprintf(fp, "<h1>Jurisdiction List for Federation ");
	fprintf(fp, "\"<tt>%s</tt>\" (<tt>%s</tt>)</h1>\n",
			conf_val(CONF_FEDERATION_NAME), conf_val(CONF_FEDERATION_DOMAIN));

	if (njurisdictions) {
	  Dsvec *dsv;
	  Html_table *tab;

	  tab = html_table(NULL, NULL);
	  tab->row_class = "tr";
	  tab->auto_row_nclasses = 2;
	  tab->auto_column_class = "td";
	  dsv = dsvec_init(NULL, sizeof(char *));
	  dsvec_add_ptr(dsv, "border");
	  dsvec_add_ptr(dsv, "width=\"100%\"");
	  html_table_begin(tab, dsv, 8);
	  tab->in_header = 1;
	  html_cell(tab, "Jurisdiction<br/>Name");
	  html_cell(tab, "Full<br/>Name");
	  html_cell(tab, "Full<br/>Alt Name");
	  html_cell(tab, "URL Service Prefix");
	  html_cell(tab, "Auth?");
	  html_cell(tab, "Prompts?");
	  html_cell(tab, "Aux");
	  html_cell(tab, "Public Key");
	  html_row_end(tab);
	  tab->in_header = 0;

	  for (i = 0; i < njurisdictions; i++) {
		html_row_begin(tab);
		html_cell(tab, j[i].jname);
		html_cell(tab, j[i].name);
		html_cell(tab, j[i].alt_name);
		html_cell(tab, expand_dacs_url(j[i].jname, j[i].dacs_url));
		html_cell(tab, j[i].authenticates);
		html_cell(tab, j[i].prompts);
		html_cell(tab, (j[i].auxiliary != NULL && *j[i].auxiliary != '\0')
				  ? j[i].auxiliary : "&nbsp;");
		html_cell(tab, (j[i].public_key_pem != NULL)
				  ? j[i].public_key_pem : "&nbsp;");
		html_row_end(tab);
	  }
	  html_table_end(tab);
	  fprintf(fp, "%s", ds_buf(tab->ds));
	  html_table_free(tab);

	  fprintf(fp, "</center>\n");
	}

	if (fed_id != NULL)
	  fprintf(fp, "<p><pre class=\"fed_id\">Fed ID: %s</pre></p>\n", fed_id);

	if (fed_public_key_pem != NULL)
	  fprintf(fp, "<p><pre class=\"pub_key\">%s</pre></p>\n",
			  fed_public_key_pem);

	emit_html_trailer(fp);
  }
  else if (test_emit_format(EMIT_FORMAT_TEXT)) {
	if (dacs_app_type == DACS_WEB_SERVICE)
	  emit_plain_header(stdout);

	if (key_only) {
	  if (njurisdictions == 0) {
		if (fed_public_key_pem != NULL)
		  fprintf(fp, "%s\n", fed_public_key_pem);
	  }
	  else if (njurisdictions == 1) {
		if (jur_public_key_pem != NULL)
		  fprintf(fp, "%s\n", jur_public_key_pem);
	  }
	  else {
		/* ??? */
	  }
	}
	else {
	  if (njurisdictions == 0) {
		fprintf(fp, "Federation name: %s\n",
				conf_val(CONF_FEDERATION_NAME));
		fprintf(fp, "Federation domain: %s\n",
				conf_val(CONF_FEDERATION_DOMAIN));
		if (fed_id != NULL)
		  fprintf(fp, "Fed ID: %s\n", fed_id);

		if (fed_public_key_pem != NULL)
		  fprintf(fp, "%s\n", fed_public_key_pem);
	  }
	  else {
		for (i = 0; i < njurisdictions; i++) {
		  /* XXX add a format spec to select output fields... */
		  printf("%s\n", j[i].jname);
		}
	  }
	}

	if (dacs_app_type == DACS_WEB_SERVICE)
	  emit_plain_trailer(stdout);
  }
  else {
	*errmsg = "Unsupported output format";
	return(-1);
  }

  return(0);
}

static void
dacs_usage(void)
{

  fprintf(stderr, "Usage: dacslist %s [-f] [-k] [-j jurisdiction]\n",
		  standard_command_line_usage);

  exit(1);
}

int
dacslist_main(int argc, char **argv, int do_init, void *main_out)
{
  int i, key_only, st;
  char *errmsg, *jname, *p;
  Common_status status;
  DACS_app_type app_type;
  Kwv *kwv;

  errmsg = "internal";

  if (getenv("REMOTE_ADDR") == NULL) {
	app_type = DACS_UTILITY;
    log_module_name = "dacslist";
  }
  else {
	app_type = DACS_WEB_SERVICE;
    log_module_name = "dacs_list_jurisdictions";
  }

  if (dacs_init(app_type, &argc, &argv, &kwv, &errmsg) == -1) {
  fail:
	if (test_emit_xml_format()) {
	  emit_xml_header(stdout, "dacs_list_jurisdictions");
	  fprintf(stdout, "<%s>\n",
			  make_xml_root_element("dacs_list_jurisdictions"));
	  init_common_status(&status, NULL, NULL, errmsg);
	  fprintf(stdout, "%s", make_xml_common_status(&status));
	  fprintf(stdout, "</dacs_list_jurisdictions>\n");
	  emit_xml_trailer(stdout);
	}
    else if (test_emit_format(EMIT_FORMAT_JSON)) {
      emit_json_header(stdout, "dacs_list_jurisdictions");
      init_common_status(&status, NULL, NULL, errmsg);
      fprintf(stdout, "%s", make_json_common_status(&status));
      emit_json_trailer(stdout);
    }
	else {
	  if (dacs_app_type == DACS_WEB_SERVICE) {
		emit_plain_header(stdout);
		fprintf(stdout, "An error occurred: federation=\"%s\" error=\"%s\"\n",
				conf_val(CONF_FEDERATION_NAME), errmsg);
		emit_plain_trailer(stdout);
	  }
	  else {
		fprintf(stderr, "%s\n", errmsg);
		dacs_usage();
		/*NOTREACHED*/
	  }
	}

	exit(1);
  }

  if (app_type == DACS_UTILITY && !dacs_saw_command_line_log_level)
	log_set_level(NULL, LOG_WARN_LEVEL);

  jname = NULL;
  key_only = 0;
  if (app_type == DACS_WEB_SERVICE) {
	jname = kwv_lookup_value_null(kwv, "JURISDICTION");
	key_only = (p = kwv_lookup_value(kwv, "PUB_KEY_ONLY")) != NULL
	  && strcaseeq(p, "yes");
	if (key_only) {
	  if (jname == NULL)
		jname = conf_val(CONF_JURISDICTION_NAME);
	  if (!test_emit_format(EMIT_FORMAT_TEXT)) {
		errmsg = "PUB_KEY_ONLY argument requires FORMAT=TEXT";
		goto fail;
	  }
	}
  }

  if (should_use_argv) {
	for (i = 1; i < argc; i++) {
	  if (streq(argv[i], "-k"))
		key_only = 1;
	  else if (streq(argv[i], "-f"))
		jname = "";
	  else if (streq(argv[i], "-j")) {
		if ((jname = argv[++i]) == NULL) {
		  errmsg = "Jurisdiction name must follow -j flag";
		  goto fail;
		}
	  }
	  else {
		errmsg = "Usage: unrecognized parameter";
		goto fail;
	  }
	}

	if (key_only && jname == NULL)
	  jname = conf_val(CONF_JURISDICTION_NAME);
  }

  st = do_list_jurisdictions(stdout, jname, key_only, &errmsg);

  if (st == -1)
	goto fail;

  exit(0);
}

#else

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

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

  exit(1);
}
#endif
