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

/*
 * JSON (JavaScript Object Notation) formatted messages
 * http://www.json.org
 * RFC 4627
 */

#ifndef lint
static const char copyright[] =
"Copyright (c) 2003-2013\n\
Distributed Systems Software.  All rights reserved.";
static const char revid[] =
  "$Id: commonjson.c 2656 2013-07-02 20:15:57Z brachman $";
#endif

#include "auth.h"

static char *log_module_name = "commonjson";

char *
make_json_common_status(Common_status *status)
{
  Ds ds;

  ds_init(&ds);
  ds_asprintf(&ds, "{ \"common_status\":\n");
  ds_asprintf(&ds, " { \"context\":\"%s\"",
			  status->context == NULL ? "" : status->context);
  ds_asprintf(&ds, ", \"code\":\"%s\"",
			  status->code == NULL ? "" : status->code);
  ds_asprintf(&ds, ", \"message\":\"%s\"",
			  status->message == NULL ? ""
			  : xml_attribute_value_encode(status->message, '"'));
  ds_asprintf(&ds, " }\n");
  ds_asprintf(&ds, "}");

  return(ds_buf(&ds));
}

char *
make_json_roles_reply(Roles_reply *rr)
{
  Ds ds;

  ds_init(&ds);
  ds_asprintf(&ds, "{ \"roles_reply\":\n");
  if (rr->ok != NULL) {
	ds_asprintf(&ds, " { \"ok\": { \"roles\":\"%s\" } }\n",
				rr->ok->roles == NULL ? "" : rr->ok->roles);
  }
  else if (rr->failed != NULL)
	ds_asprintf(&ds, "{ \"failed\": { \"reason\":\"%s\" } }\n",
				rr->failed->reason);
  else if (rr->status != NULL)
	ds_asprintf(&ds, "%s", make_json_common_status(rr->status));
  else {
	/* ??? */
	return(NULL);
  }

  ds_asprintf(&ds, " }");

  return(ds_buf(&ds));
}

char *
make_json_auth_reply_ok(Auth_reply_ok *ok)
{
  Ds *ds;

  ds = ds_init(NULL);
  ds_asprintf(ds, "{ \"auth_reply\":");
  ds_asprintf(ds, " { \"ok\": { \"username\":\"%s\"", ok->username);
  if (ok->lifetime != NULL && ok->lifetime[0] != '\0')
	ds_asprintf(ds, ", \"lifetime\":\"%s\"", ok->lifetime);
  ds_asprintf(ds, " }\n");
  if (ok->roles_reply != NULL)
	ds_asprintf(ds, " %s", make_json_roles_reply(ok->roles_reply));
  ds_asprintf(ds, "} }");

  return(ds_buf(ds));
}

char *
make_json_auth_reply_failed(char *username, char *redirect_url)
{
  int need_comma;
  Ds *ds;

  ds = ds_init(NULL);
  ds_asprintf(ds, "{ \"auth_reply\":\n");
  ds_asprintf(ds, " { \"failed\": {");
  need_comma = 0;
  if (username != NULL && username[0] != '\0') {
	ds_asprintf(ds, " \"username\":\"%s\"", username);
    need_comma = 1;
  }
  if (redirect_url != NULL && redirect_url[0] != '\0')
	ds_asprintf(ds, "%s \"redirect_url\":\"%s\"",
		need_comma ? "," : "", redirect_url);
  ds_asprintf(ds, " }\n");
  ds_asprintf(ds, "} }");

  return(ds_buf(ds));
}

char *
make_json_auth_reply_status(char *context, char *code, char *message)
{
  Common_status status;
  Ds *ds;

  ds = ds_init(NULL);
  ds_asprintf(ds, "{ \"auth_reply\":");
  init_common_status(&status, context, code, message);
  ds_asprintf(ds, " %s", make_json_common_status(&status));
  ds_asprintf(ds, "}");

  return(ds_buf(ds));
}

char *
make_json_auth_reply_prompt(char *prompt)
{
  Ds *ds;

  ds = ds_init(NULL);
  ds_asprintf(ds, "{ \"auth_reply\":");
  ds_asprintf(ds, " %s", prompt);
  ds_asprintf(ds, "}");

  return(ds_buf(ds));
}

/*
 * Use CREDENTIALS to compose a JSON credentials string.
 * Store the string in CREDENTIALS_STR.
 * Return the length of the string.
 *
 * Note that the order of attribute specifications in a start-tag or
 * empty-element tag is not significant.  For added security, we might
 * shuffle their order.  Currently, some parsing routines insist on a
 * particular order, however.
 */
int
make_json_credentials(Credentials *cr, char **credentials_str)
{
  char *auth_style_str;
  Ds s;

  ds_init_size(&s, 128);
  ds_asprintf(&s, "{ \"credentials\":");
  ds_asprintf(&s, " { \"federation\":\"%s\"", cr->federation);
  ds_asprintf(&s, ", \"username\":\"%s\"", cr->username);
  ds_asprintf(&s, ", \"jurisdiction\":\"%s\"", cr->home_jurisdiction);
  ds_asprintf(&s, ", \"ip\":\"%s\"", cr->ip_address);
  ds_asprintf(&s, ", \"roles\":\"%s\"", cr->role_str);
  ds_asprintf(&s, ", \"auth_time\":\"%lu\"", cr->auth_time);
  ds_asprintf(&s, ", \"expires\":\"%lu\"", cr->expires_secs);
  auth_style_str = auth_style_to_string(cr->auth_style);
  ds_asprintf(&s, ", \"auth_style\":\"%s\"", auth_style_str);
  ds_asprintf(&s, ", \"unique\":\"%s\"", cr->unique);
  ds_asprintf(&s, ", \"version\":\"%s\"", cr->version);
  ds_asprintf(&s, ", \"valid_for\":\"%s\"", cr->valid_for);
  if (cr->imported_by != NULL)
	ds_asprintf(&s, ", \"imported_by\":\"%s\"", cr->imported_by);
  if (cr->ua_hash != NULL)
	ds_asprintf(&s, ", \"ua_hash\":\"%s\"", cr->ua_hash);
  s.exact_flag = 1;
  ds_asprintf(&s, " } }");

  *credentials_str = (char *) s.buf;

  return(ds_len(&s));
}

char *
make_json_dacs_current_credentials(Credentials *selected, Dsvec *dsv_activity,
								   int detailed)
{
  Credentials *cr;
  Ds s;

  ds_init(&s);
  ds_asprintf(&s, "{ \"dacs_current_credentials\":");
  ds_asprintf(&s, " { \"federation_name\":\"%s\"",
			  conf_val(CONF_FEDERATION_NAME));
  ds_asprintf(&s, ", \"federation_domain\":\"%s\"",
			  conf_val(CONF_FEDERATION_DOMAIN));

  if (selected != NULL)
	ds_asprintf(&s, ", \"credentials\": [ ");

  for (cr = selected; cr != NULL; cr = cr->next) {
	/*
	 * If expires_secs is zero, then these credentials have been
	 * deleted (refer to signout.c).
	 */
	if (cr->expires_secs == 0)
	  continue;
	if (cr != selected)
	  ds_asprintf(&s, ",\n");

	ds_asprintf(&s, "{ \"federation\":\"%s\"", cr->federation);
	ds_asprintf(&s, ", \"jurisdiction\":\"%s\"", cr->home_jurisdiction);
	ds_asprintf(&s, ", \"name\":\"%s\"", cr->username);
	ds_asprintf(&s, ", \"roles\":\"%s\"", cr->role_str);
	ds_asprintf(&s, ", \"auth_style\":\"%s\"",
				auth_style_to_string(cr->auth_style));
	ds_asprintf(&s, ", \"cookie_name\":\"%s\"", cr->cookie_name);

	if (detailed) {
	  struct tm *tm;

	  ds_asprintf(&s, ", \"valid_for\":\"%s\"", cr->valid_for);
	  if (cr->imported_by != NULL)
		ds_asprintf(&s, ", \"imported_by\":\"%s\"", cr->imported_by);
	  ds_asprintf(&s, ", \"ip_address\":\"%s\"", cr->ip_address);
	  ds_asprintf(&s, ", \"version\":\"%s\"", cr->version);
	  tm = localtime(&cr->auth_time);
	  ds_asprintf(&s, ", \"auth_time\":\"%d (%s)\"",
				  cr->auth_time, make_short_local_date_string(tm));
	  tm = localtime(&cr->expires_secs);
	  ds_asprintf(&s, ", \"expires_secs\":\"%d (%s)\"",
				  cr->expires_secs, make_short_local_date_string(tm));
	  if (cr->ua_hash != NULL)
		ds_asprintf(&s, ", \"ua_hash\":\"%s\"", cr->ua_hash);
	  
	  if (dsv_activity == NULL)
		ds_asprintf(&s, " }\n");
	  else {
		int i;
		char *ident_full;
		Dsvec *dsv_active, *dsv_auth;
		User_info *ui;

		ident_full = auth_identity(cr->federation, cr->home_jurisdiction,
								   cr->username, NULL);

		ds_asprintf(&s, " }");
		dsv_active = user_info_active(dsv_activity, ident_full);
		dsv_auth = user_info_last_auth(dsv_activity, ident_full);
		if (dsvec_len(dsv_auth) > 1) {
		  ui = (User_info *) dsvec_ptr_index(dsv_auth, 1);
		  ds_asprintf(&s, ",\n");
		  ds_asprintf(&s,
					  "{ \"previous_auth\": \"auth_time\":\"%s\", \"ip_address\":\"%s\" }",
					  ui->info.auth.auth_time, ui->info.auth.ip);
		}

		if (dsvec_len(dsv_active) > 1) {
		  ds_asprintf(&s, ",\n{ \"active_auth\": [\n");
		  for (i = 0; i < dsvec_len(dsv_active) - 1; i++) {
			if (i)
			  ds_asprintf(&s, ",\n");
			ui = (User_info *) dsvec_ptr_index(dsv_active, i);
			ds_asprintf(&s,
						"{ \"auth_time\":\"%s\", \"ip_address\":\"%s\" }",
						ui->info.auth.auth_time, ui->info.auth.ip);
		  }
		  ds_asprintf(&s, " ] }\n");
		}
	  }
	}
	else
	  ds_asprintf(&s, " }");
  }

  if (selected != NULL)
	ds_asprintf(&s, " ]");
  ds_asprintf(&s, " } }");

  return(ds_buf(&s));
}
