/* -*- tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2 -*-
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* import-globals-from pippki.js */
"use strict";

/**
 * @file Implements the functionality of clientauthask.xul: a dialog that allows
 *       a user pick a client certificate for TLS client authentication.
 * @argument {String} window.arguments[0]
 *           The hostname of the server requesting client authentication.
 * @argument {String} window.arguments[1]
 *           The Organization of the server cert.
 * @argument {String} window.arguments[2]
 *           The Organization of the issuer of the server cert.
 * @argument {Number} window.arguments[3]
 *           The port of the server.
 * @argument {nsISupports} window.arguments[4]
 *           List of certificates the user can choose from, queryable to
 *           nsIArray<nsIX509Cert>.
 * @argument {nsISupports} window.arguments[5]
 *           Object to set the return values of calling the dialog on, queryable
 *           to the underlying type of ClientAuthAskReturnValues.
 */

/**
 * @typedef ClientAuthAskReturnValues
 * @type nsIWritablePropertyBag2
 * @property {Boolean} certChosen
 *           Set to true if the user chose a cert and accepted the dialog, false
 *           otherwise.
 * @property {Boolean} rememberSelection
 *           Set to true if the user wanted their cert selection to be
 *           remembered, false otherwise.
 * @property {Number} selectedIndex
 *           The index the chosen cert is at for the given cert list. Undefined
 *           value if |certChosen| is not true.
 */

const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;

const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});

/**
 * The pippki <stringbundle> element.
 * @type <stringbundle>
 */
var bundle;
/**
 * The array of certs the user can choose from.
 * @type nsIArray<nsIX509Cert>
 */
var certArray;
/**
 * The checkbox storing whether the user wants to remember the selected cert.
 * @type nsIDOMXULCheckboxElement
 */
var rememberBox;

function onLoad() {
  bundle = document.getElementById("pippki_bundle");
  let rememberSetting =
    Services.prefs.getBoolPref("security.remember_cert_checkbox_default_setting");

  rememberBox = document.getElementById("rememberBox");
  rememberBox.label = bundle.getString("clientAuthRemember");
  rememberBox.checked = rememberSetting;

  let hostname = window.arguments[0];
  let org = window.arguments[1];
  let issuerOrg = window.arguments[2];
  let port = window.arguments[3];
  let formattedOrg = bundle.getFormattedString("clientAuthMessage1", [org]);
  let formattedIssuerOrg = bundle.getFormattedString("clientAuthMessage2",
                                                     [issuerOrg]);
  let formattedHostnameAndPort =
    bundle.getFormattedString("clientAuthHostnameAndPort",
                              [hostname, port.toString()]);
  setText("hostname", formattedHostnameAndPort);
  setText("organization", formattedOrg);
  setText("issuer", formattedIssuerOrg);

  let selectElement = document.getElementById("nicknames");
  certArray = window.arguments[4].QueryInterface(Ci.nsIArray);
  for (let i = 0; i < certArray.length; i++) {
    let menuItemNode = document.createElement("menuitem");
    let cert = certArray.queryElementAt(i, Ci.nsIX509Cert);
    let nickAndSerial =
      bundle.getFormattedString("clientAuthNickAndSerial",
                                [cert.nickname, cert.serialNumber]);
    menuItemNode.setAttribute("value", i);
    menuItemNode.setAttribute("label", nickAndSerial); // This is displayed.
    selectElement.firstChild.appendChild(menuItemNode);
    if (i == 0) {
      selectElement.selectedItem = menuItemNode;
    }
  }

  setDetails();

  Services.obs.notifyObservers(document.getElementById("certAuthAsk"),
                               "cert-dialog-loaded", null);
}

/**
 * Populates the details section with information concerning the selected cert.
 */
function setDetails() {
  let index = parseInt(document.getElementById("nicknames").value);
  let cert = certArray.queryElementAt(index, Ci.nsIX509Cert);

  let detailLines = [
    bundle.getFormattedString("clientAuthIssuedTo", [cert.subjectName]),
    bundle.getFormattedString("clientAuthSerial", [cert.serialNumber]),
    bundle.getFormattedString("clientAuthValidityPeriod",
                              [cert.validity.notBeforeLocalTime,
                               cert.validity.notAfterLocalTime]),
  ];
  let keyUsages = cert.keyUsages;
  if (keyUsages) {
    detailLines.push(bundle.getFormattedString("clientAuthKeyUsages",
                                               [keyUsages]));
  }
  let emailAddresses = cert.getEmailAddresses({});
  if (emailAddresses.length > 0) {
    let joinedAddresses = emailAddresses.join(", ");
    detailLines.push(bundle.getFormattedString("clientAuthEmailAddresses",
                                               [joinedAddresses]));
  }
  detailLines.push(bundle.getFormattedString("clientAuthIssuedBy",
                                             [cert.issuerName]));
  detailLines.push(bundle.getFormattedString("clientAuthStoredOn",
                                             [cert.tokenName]));

  document.getElementById("details").value = detailLines.join("\n");
}

function onCertSelected() {
  setDetails();
}

function doOK() {
  let retVals = window.arguments[5].QueryInterface(Ci.nsIWritablePropertyBag2);
  retVals.setPropertyAsBool("certChosen", true);
  let index = parseInt(document.getElementById("nicknames").value);
  retVals.setPropertyAsUint32("selectedIndex", index);
  retVals.setPropertyAsBool("rememberSelection", rememberBox.checked);
  return true;
}

function doCancel() {
  let retVals = window.arguments[5].QueryInterface(Ci.nsIWritablePropertyBag2);
  retVals.setPropertyAsBool("certChosen", false);
  retVals.setPropertyAsBool("rememberSelection", rememberBox.checked);
  return true;
}