/*
 * Copyright (c) 2003-2012
 * 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.
 ***************************************************************************/

/*
 * Web service to return or display the user's current DACS identities.
 */

#ifndef lint
static const char copyright[] =
"Copyright (c) 2003-2012\n\
Distributed Systems Software.  All rights reserved.";
static const char revid[] =
  "$Id: current_credentials.c 2594 2012-10-19 17:28:49Z brachman $";
#endif

#include "dacs.h"

#ifndef UPLOAD_CONTENT_TYPE
#define UPLOAD_CONTENT_TYPE	"application/x-dacs-credentials"
#endif

#ifndef UPLOAD_FILENAME
#define UPLOAD_FILENAME		"dacs.crd"
#endif

static char *log_module_name = "dacs_current_credentials";

static int emit_xml = 1;

int
main(int argc, char **argv)
{
  int detail, i, xargc, n;
  unsigned int ncookies;
  char **xargv, *errmsg, *p, *remote_addr;
  Credentials *cr, *credentials, *selected;
  Common_status common_status;
  Cookie *cookies;
  Html_header_conf *hc;
  Kwv *kwv;

  errmsg = "Internal error";
  hc = emit_html_header_conf(NULL);
  hc->no_cache = 1;

  xargc = argc;
  xargv = argv;
  if (dacs_init(DACS_WEB_SERVICE, &argc, &argv, &kwv, &errmsg) == -1) {
  fail:
	if (test_emit_xml_format()) {
	  emit_xml_header(stdout, "dacs_current_credentials");
	  printf("<%s>\n", make_xml_root_element("dacs_current_credentials"));
	  init_common_status(&common_status, NULL, NULL, errmsg);
	  fprintf(stdout, "%s", make_xml_common_status(&common_status));
	  printf("</dacs_current_credentials>\n");
	  emit_xml_trailer(stdout);
	}
	else if (test_emit_format(EMIT_FORMAT_JSON)) {
	  emit_json_header(stdout, "dacs_current_credentials");
	  init_common_status(&common_status, NULL, NULL, errmsg);
	  printf("%s", make_json_common_status(&common_status));
	  emit_json_trailer(stdout);
	}
	else {
	  hc->title = ds_xprintf("DACS Current Credentials for Federation %s",
							 non_null(conf_val(CONF_FEDERATION_NAME)));
	  emit_html_header(stdout, hc);
	  printf("<p>dacs_current_credentials: fatal error.");
	  printf("<p>%s\n", errmsg);
	  emit_html_trailer(stdout);
	}
	exit(1);
  }

  if (should_use_argv) {
	if (argc > 1) {
	  log_msg((LOG_ERROR_LEVEL, "Bad parameter: '%s'", argv[1]));
	  log_msg((LOG_ERROR_LEVEL, "QUERY_STRING: '%s'",
			   getenv("QUERY_STRING")));

	  for (i = 0; i < xargc; i++)
		log_msg((LOG_ERROR_LEVEL, "Arg%d: %s", i, xargv[i]));
	  errmsg = "Usage: unrecognized parameter";
	  goto fail;
	}
  }

  if ((remote_addr = getenv("REMOTE_ADDR")) == NULL) {
	errmsg = "No REMOTE_ADDR found";
	goto fail;
  }

  detail = (p = kwv_lookup_value(kwv, "DETAIL")) != NULL
	&& strcaseeq(p, "yes");

  emit_xml = test_emit_xml_format();

  if (get_cookies(NULL, &cookies, &ncookies) == -1) {
	errmsg = "Cookie parse error";
	goto fail;
  }

  n = get_valid_scredentials(cookies, remote_addr, 0, &credentials,
							 &selected, NULL);

  if (test_emit_format(EMIT_FORMAT_FILE)) {
	char *bp;

	/*
	 * EXPERIMENTAL:
	 * The credentials are sent so that the browser saves them to a file
	 * or pipes them through a DACS utility to store them securely.
	 * They can subsequently be used by other user agents and applications.
	 * We must not leave credentials vulnerable.
	 * And... we could weaken the credentials before sending them
	 * ("AUTH_VALID_FOR_UPLOAD") - they would be restored after the user
	 * produces a correct password.
	 */
	if (n == 0) {
	  emit_plain_header(stdout);
	  printf("You are not authenticated within federation \"%s\"\n",
			 conf_val(CONF_FEDERATION_NAME));
	  emit_plain_trailer(stdout);
	}
	else {
	  if (n == -1) {
		errmsg = "Error getting valid selected credentials";
		goto fail;
	  }
	  /* The credentials have probably been weakened by ACS. */
	  set_valid_for(selected, AUTH_VALID_FOR_ACS);
	  if (make_set_auth_cookie_header(selected, NULL, 1, &bp) == -1) {
		errmsg = "Couldn't create cookie(s)";
		goto fail;
	  }
	  printf("Content-Type: %s\n", UPLOAD_CONTENT_TYPE);
	  printf("Content-Disposition: inline; filename=%s\n\n", UPLOAD_FILENAME);
	  /* XXX This should be encrypted or be read-only for the user */
	  printf("%s", bp);
	}
  }
  else if (emit_xml) {
	Dsvec *dsv_activity;

	dsv_activity = user_info_load(NULL);
	emit_xml_header(stdout, "dacs_current_credentials");
	printf("%s",
		   make_xml_dacs_current_credentials(selected, dsv_activity, detail));
	emit_xml_trailer(stdout);
  }
  else if (test_emit_format(EMIT_FORMAT_JSON)) {
	Dsvec *dsv_activity;

	dsv_activity = user_info_load(NULL);
	emit_json_header(stdout, NULL);
	printf("%s\n",
		   make_json_dacs_current_credentials(selected, dsv_activity, detail));
	emit_json_trailer(stdout);
  }
  else {
	if (conf_val(CONF_CSS_PATH) != NULL)
	  hc->css = ds_xprintf("%s/dacs_current_credentials.css",
						   conf_val(CONF_CSS_PATH));
	else
	  hc->css = CSS_DIR/**/"/dacs_current_credentials.css";
	hc->title = ds_xprintf("DACS Current Credentials for Federation %s",
						   non_null(conf_val(CONF_FEDERATION_NAME)));
	emit_html_header(stdout, hc);
	if (n == 0) {
	  printf("<p>You are not authenticated within federation");
	  printf(" <tt><b>%s</b></tt>.\n", conf_val(CONF_FEDERATION_NAME));
	}
	else {
	  struct tm *tm;
	  time_t auth_secs, now;
	  Dsvec *dsv_active, *dsv_activity, *dsv_auth;
	  User_info *ui;

	  time(&now);
	  printf("<p>You are authenticated within federation");
	  printf(" <span class=\"fed_name\">%s</span> as:\n",
			 conf_val(CONF_FEDERATION_NAME));
	  printf("<ol>\n");

	  dsv_activity = user_info_load(NULL);
	  for (cr = selected; cr != NULL; cr = cr->next) {
		char *ident_str, *ident_abbrev, *ident_full;

		ident_full = auth_identity(cr->federation, cr->home_jurisdiction,
								   cr->username, NULL);
		ident_abbrev = auth_identity(NULL, cr->home_jurisdiction,
									 cr->username, NULL);
		if (!streq(cr->federation, conf_val(CONF_FEDERATION_NAME)))
		  ident_str = ident_full;
		else
		  ident_str = ident_abbrev;

		printf("<li><b>%s</b>", ident_str);
		if (cr->role_str != NULL && *cr->role_str != '\0')
		  printf(" with roles <span class=\"user_roles\">%s</span>",
				 cr->role_str);
		if ((tm = localtime(&cr->auth_time)) != NULL)
		  printf("<br>at <span class=\"date\">%s</span>",
				 make_local_date_string(tm, 1));
		printf(" from <span class=\"ipaddr\">%s</span>\n", cr->ip_address);
		printf(" expires in %s\n",
			   make_approx_relative_date(cr->expires_secs - now));

		if (dsv_activity != NULL) {
		  dsv_active = user_info_active(dsv_activity, ident_full);
		  dsv_auth = user_info_last_auth(dsv_activity, ident_full);
		  if (dsv_auth != NULL && dsvec_len(dsv_auth) > 1) {
			ui = (User_info *) dsvec_ptr_index(dsv_auth, 1);
			if (strnum(ui->info.auth.auth_time, STRNUM_TIME_T, &auth_secs)
				!= -1) {
			  if ((tm = localtime(&auth_secs)) != NULL)
				printf("<br>Previous sign on was at <span class=\"date\">%s</span> from <span class=\"ipaddr\">%s</span>",
					   make_local_date_string(tm, 1), ui->info.auth.ip);
			}
		  }
		  else
			printf("<br>There is no record of a previous sign on for this identity");

		  if (dsvec_len(dsv_active) > 1) {
			time_t expires_secs;

			printf("<br>Other active sessions for this identity:<ol>");
			for (i = 0; i < dsvec_len(dsv_active) - 1; i++) {
			  ui = (User_info *) dsvec_ptr_index(dsv_active, i);
			  if (strnum(ui->info.auth.expires, STRNUM_TIME_T, &expires_secs)
				  == -1)
				expires_secs = 0;

			  if (strnum(ui->info.auth.auth_time, STRNUM_TIME_T, &auth_secs)
				  != -1) {
				if ((tm = localtime(&auth_secs)) != NULL) {
				  printf("<li>Signed on at ");
				  printf("<span class=\"date\">%s</span>",
						 make_local_date_string(tm, 1));
				  printf(" from <span class=\"ipaddr\">%s</span>",
						 ui->info.auth.ip);
				  if (expires_secs)
					printf(" expires in %s",
						   make_approx_relative_date(expires_secs - now));
				  printf("</li>\n");
				}
			  }
			}
			printf("</ol>\n");
		  }
		}
	  }
	  printf("</ol></p>\n");

	}

	emit_html_trailer(stdout);
  }

  exit(0);
}
