/**
 * CHAP Login client-side scripts.
 *
 * These scripts provide a reference example of how to implement the modified
 * CHAP login/registration system on the client side, as described by Paul
 * Johnston: http://pajhome.org.uk/crypt/md5/auth.html
 *
 * The excellent jQuery (http://jquery.com/) JavaScript library has been used
 * in order to simplify development.
 *
 * The main use of JavaScript here is to:
 * 1) Receive the challenges from the server.
 * 2) Compute the responses and send them to the server for authentication.
 * (JavaScript is also used for some other minor UI details)
 *
 * This is accomplished in several steps.  Because the challenges are tied to
 * a specific username, the form cannot be submitted normally since the username
 * is not known ahead of time.  In order to avoid a "two-step" submission,
 * Ajax is used to hide these multiple steps.  Here is how it is done:
 *
 * After a user enters their username/password combo and submits the form,
 * the `submit` event it captured and halted.  The username value is obtained
 * from the form, and send to the server via Ajax in order to retrieve
 * the challenges associated with it.
 *
 * After the challenges are received, the responses can then be computed.
 * The responses are based on the challenges and the plaintext password.  The
 * response values are then inserted as form as hidden values using DOM
 * manipulation.  The plaintext password field is then disabled so that its
 * value will not be transmitted.  Finally, form submission is triggered in
 * order to send all the parameters to the server.
 *
 * Registration is handled in a similar manner.
 *
 * Non-JavaScript operation:
 * The login/registration system is able to function perfectly fine in the case
 * of JavaScript (or the required hashing functions) not being present.  In
 * this case, the password WILL be transmitted in plaintext to the server and
 * CHAP is not used.
 *
 * The server is able to detect whether JavaScript was used during the login
 * process or not (by checking whether certain POST parameters were set) and
 * thus is able to handle the login process differently depending on whether
 * CHAP was used or not.  Further details can be seen in the
 * `/src/demo/index.php` file.
 *
 * @author Peter Chng
 * @copyright Copyright (c) 2008, Peter Chng
 * @license http://www.opensource.org/licenses/mit-license.php The MIT License
 * @link http://unitstep.net
 */

/**
 * Login form fields, defined using CSS selectors.
 */
var loginFormSelector = 'form#login';
var loginUsernameSelector = '#username';
var loginPasswordSelector = '#password-plaintext';

/**
 * Registration form fields, defined using CSS selectors.
 */
var registerFormSelector = 'form#register';
var registerPasswordSelector = '#password-plaintext-register';


/**
 * Sanity check function to determine if the hashing functions required for
 * the modified CHAP login system are supported.
 */
function isChapLoginSupported()
{
  var test1 = sha1_vm_test();

  // Ensure that the regular expression 'blanking' works.
  // May not be needed if disabling inputs, since the value won't be sent.
  var blankingTest = "Hello World".replace(/./g, ' ') == '           ';

  var password_key = 'asdfjkl;';
  var challenge1_data = 'f50be76b6dfddce6aa7c58ea7e21431e26a76066';
  var test2 = '4a873874eebcb38096c6a300a173159b4d72fff1' ==
    hex_sha1(hex_hmac_sha1(password_key, challenge1_data));

  return (test1 && blankingTest && test2);
}

/**
 * Flag to indicate login form is ready to be submitted after
 * challenge-responses have been computed.
 */
var readyToSubmit = false;

/**
 * Flag to indicate registration form is ready to be submitted after
 * challenge-responses have been computed.
 */
var readyToRegister = false;

// This function is executed when the document is ready.
jQuery(document).ready(function()
{
  // Hide the "No Javascript" warning.
  jQuery('#javascript-disabled').hide();

  // Do not use challenge-response if not supported by the browser.
  if (isChapLoginSupported())
  {
    // CHAP Login handler.
    // This is called when the login form is submitted.
    jQuery(loginFormSelector).submit(function(event)
    {
      // Do not submit the form unless the plaintext password has been processed
      // into the proper challenge response and the next response has been
      // included.
      if (readyToSubmit)
      {
        return true;
      }
      else
      {
        // Prevent traditional form submission.
        event.preventDefault();
      }

      // Grab the username from the form and use it to obtain the challenges.
      var username = jQuery(loginUsernameSelector).val();

      /*
       * The URL to get the challenges from.  This URL should return the
       * challenges in JSON format.  In this case, the URL is based off of the
       * form's `action` attribute, but it could be anything.
       *
       * An example of the return values from this URL:
       * {'c1':'34b8b659bb2e00b5561a7e321b819f9953bbe11e','c2':'1c74c38690b145329efcb2f1d7d9dc32d71def07'}
       */
      var url = jQuery(loginFormSelector).attr('action') + '?action=challenges&username=' + username;

      // Get the challenges via Ajax, and then perform the modified CHAP login.
      // The `success` callback is executed once the response from the Ajax
      // request has been received.
      jQuery.ajax(
      {
        'url' : url,
        'type' : 'GET',
        'dataType' : 'json',
        'cache' : false,
        'success' : function(challenges, textStatus)
        {
          // Compute the responses to send based on the challenges.
          var password = jQuery(loginPasswordSelector).val();
          var response = hex_hmac_sha1(password, challenges.c1);
          var responseNext = hex_sha1(hex_hmac_sha1(password, challenges.c2));

          // Disable and blank out the password field with spaces to prevent
          // the plaintext from being submitted over the network.
          // Note that the disabled input is not submitted at all, so the
          // 'blanking out' of the password with spaces is somewhat redundant
          // and may not be necessary, and in fact, user-unfriendly.
          var blankPassword = password.replace(/./g, ' ');
          jQuery(loginPasswordSelector).val(blankPassword).attr('disabled', 'disabled');

          // Add the two fields containing the challenge-response (password)
          // and the next-response.  The presence of these two fields indicates
          // to the server that JavaScript-enabled CHAP login was used.
          jQuery(loginFormSelector).append(
            '<input type="hidden" name="password" value="' + response + '" />' +
            '<input type="hidden" name="response-next" value="' + responseNext + '" />'
          );

          // Indicate that the form is ready to submit and then submit it!
          readyToSubmit = true;
          jQuery(loginFormSelector).submit();

        },
        // Did not get back the proper response when trying to obtain the
        // challenges.
        'error': function(XMLHttpRequest, textStatus, errorThrown)
        {
          alert('There was an error!');
        },
        // Show a loading animation.
        'beforeSend' : function()
        {
          jQuery(loginFormSelector + ' .ajax-loading').show();
        },
        // Remove the animation when things are done.
        'complete' : function()
        {
          jQuery(loginFormSelector + ' .ajax-loading').hide();
        }
      });// end jQuery.ajax()
    });// end form.submit()

    // CHAP Registration handler.
    // This is called when the registration form is submitted.
    jQuery(registerFormSelector).submit(function(event)
    {
      // Do not submit the form unless the plaintext password has been processed
      // into the proper challenge response.
      if (readyToRegister)
      {
        return true;
      }
      else
      {
        // Prevent traditional form submission.
        event.preventDefault();
      }

      /*
       * The URL to get a new challenge from. This URL should return the
       * challenge in JSON format.  In this case, the URL is based off of the
       * form's `action` attribute, but it could be anything.
       *
       * An example of the return values from this URL:
       * {'c1':'c448edff7a9be5968c5217f96809daa66e5c8066'}
       */
      var url = jQuery(registerFormSelector).attr('action') + '?action=challenge';

      // Get the challenge via Ajax and then perform registration.
      // The `success` callback is executed when the response to the Ajax
      // request is received.
      jQuery.ajax({
        'url' : url,
        'type' : 'GET',
        'dataType' : 'json',
        'cache' : false,
        'success' : function(challenges, textStatus)
        {
          // Compute the challenged-hashed password and ready it for submission.
          var password = jQuery(registerPasswordSelector).val();
          var hashedPasswordResponse = hex_sha1(hex_hmac_sha1(password, challenges.c1));

          // Disable the plaintext password field to prevent submission.
          jQuery(registerPasswordSelector).attr('disabled', 'disabled');

          // Add the fields containing the hashed-password response and the
          // challenge.
          jQuery(registerFormSelector).append(
            '<input type="hidden" name="password" value="' + hashedPasswordResponse + '" />' +
            '<input type="hidden" name="challenge1" value="' + challenges.c1 + '" />'
          );

          // Indicate that the form is ready to submit and then submit it!
          readyToRegister = true;
          jQuery(registerFormSelector).submit();
        },
        // Did not get back the proper response when trying to obtain the
        // challenges.
        'error': function(XMLHttpRequest, textStatus, errorThrown)
        {
          alert('There was an error!');
        },
        'beforeSend' : function()
        {
          jQuery(registerFormSelector + ' .ajax-loading').show();
        },
        'complete' : function()
        {
          jQuery(registerFormSelector + ' .ajax-loading').hide();
        }
      });// end jQuery.ajax()
    });// end form.submit()

  }// end if (isChapLoginSupported())
});// end jQuery(document).ready()

