/*
 * Copyright (c) 1997-2003  The Stanford SRP Authentication Project
 * All Rights Reserved.
 * See http://srp.stanford.edu/license.txt for details.
 *
 * Any use of this software to demo cryptographic technology other
 * than SRP must include the following acknowledgement:
 * "This software incorporates components derived from the
 *  Secure Remote Password JavaScript demo developed by
 *  Tom Wu (tjw@CS.Stanford.EDU)."
 */

/*
 * BigInteger glue layer:  If we need the applet (IE), access the various
 * java.math.BigInteger constructors through applet methods.  Otherwise
 * (Netscape) just refer to the java constructors directly.
 */

/* Is this Netscape 4.xx? */
var is_ns4 = (navigator.appName == "Netscape" && navigator.appVersion < "5");

var is_ie = (navigator.userAgent.match(/MSIE/) != null);

var is_opera = (navigator.appName == "Opera");

/* Do we need the toString() workaround (requires applet)? */
var bigint_str_workaround = is_opera;

/* Decide whether we need the helper applet or not */
var bigint_use_applet =  (typeof java === 'undefined') || bigint_str_workaround;
// (navigator.appName == "Microsoft Internet Explorer") || (!is_ns4 && navigator.platform.substr(0, 5) == "Linux")

if(!navigator.javaEnabled()) {
  //alert("Java support required for SRP Login");
}

/* Call this at the top of your script after including bigint.js */
function bigint_header() {
  return '';
}

/* Call this at the end of your webpage - it may instantiate the applet */
function bigint_footer() {
  if(bigint_use_applet) {
    return '<applet name="bigint" codebase="" code="bigint.class" width=1 height=1></applet>';
  }
  return '';
}

var b64_chr = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/";

function b64tob8(str) {
  var ret = "";
  var d;
  for(var j = 0; j < str.length; ++j) {
    d = b64_chr.indexOf(str.charAt(j));
    ret += hex_chr.charAt((d >> 3) & 7);
    ret += hex_chr.charAt(d & 7);
  }
  return ret;
}

function b8tob64(str) {
  var ret = "";
  var j = 0;
  if((str.length & 1) > 0) {
    ret += str.charAt(0);
    j = 1;
  }
  while(j < str.length) {
    ret += b64_chr.charAt(parseInt(str.substr(j, 2), 8));
    j += 2;
  }
  return ret;
}

/* Accepts radix as second argument */
function parseBigInt(str, r) {
  if(r == 64)
    return parseBigInt(b64tob8(str), 8);
  if(str.length == 0)
    str = "0";
  if(bigint_use_applet)
    return document.applets["bigint"].newBigInteger(str, r);
  else
    return new java.math.BigInteger(str, r);
}

/* Use toString() workaround if necessary */
function bigInt2StrHelper(bi, r) {
  if(bigint_str_workaround) {
    var i;
    var ret = "";
    var ca = document.applets["bigint"].toCharArray(bi, r);
    for(i = 0; i < ca.length; ++i) {
      ret += String.fromCharCode(ca[i]);
    }
    return ret;
  }
  else
    return bi.toString(r);
}

function bigInt2radix(bi, r) {
  if(r == 64)
    return b8tob64(String(bigInt2StrHelper(bi, 8)));
  else
    return bigInt2StrHelper(bi, r);
}

/*
 * Convert an 8-bit number to its two-character hex representation.
 * (hex_chr is defined in sha1.js)
 */
function hex_byte(num) {
  return hex_chr.charAt((num >> 4) & 0x0F) + hex_chr.charAt(num & 0x0F);
}


/*
 * UTF-8 encoder/decoder
 */
var hex_chr = '0123456789abcdef';
function encode_utf8( s ){ return unescape( encodeURIComponent( s ) ); }
function decode_utf8( s ){ return decodeURIComponent( escape( s ) ); }
function str2hex( s ){
  s = encode_utf8( s );
  var hex = '', num;
  for (var i=0; i<s.length; i++){
    num = s.charCodeAt( i );
    hex += hex_chr.charAt((num >> 4) & 0x0F) + hex_chr.charAt(num & 0x0F);
  }
  return hex;
}

var rng = null;

/*
 * Select a random large integer with a given byte count.
 */
function randomBigInt(bytes) {
  if(bigint_use_applet) {
    if(rng == null) {		// This may take a bit of time...
      window.status = "Initializing random number generator...";
      rng = document.applets["bigint"].newSecureRandom();
      window.status = "Done";
    }
    return document.applets["bigint"].newBigIntegerRandom(8 * bytes, rng);
  }
  else {
    if(is_ns4) {
      /* Generate a random integer using Netscape4's RNG */
      var rraw = crypto.random(bytes);
      var rhex = "";
      for(var j = 0; j < bytes; ++j) {
        rhex += hex_byte(rraw.charCodeAt(j));
      }
      return new java.math.BigInteger(rhex, 16);
    }
    else {
      if(rng == null) {
        rng = new java.security.SecureRandom();
      }
      return new java.math.BigInteger(8 * bytes, rng);
    }
  }
}


function bigint_applet_loaded(arg) {
  bigint_loader.appletLoaded(arg);
}

var bigint_loader = {

  callback : null,

  engine : null, 
    
  applet_creation : 0,
  
  applet_load_timeout : 3000,
    
    
  appletCreated : function() {
    if (this.applet_creation==2) return;
    this.applet_creation=2;
    this.loadEngine();
  },

  appletLoaded : function(arg) {
    if (this.applet_creation==2) return;
    this.applet_creation=2;
    this.loadEngine();
  },

  loadEngine : function(callback) {
    if (this.engine != null) return this.engine;
    if (typeof callback != 'undefined') this.callback = callback;
    var use_java = navigator.javaEnabled();
    if (use_java) { 
      if (bigint_use_applet) {
        if (bigint_loader.applet_creation==0) {
          this.addApplet();
          this.applet_creation=1;
          window.setTimeout('bigint_loader.appletCreated()', this.applet_load_timeout);
          return false;
        }
        try {
          if (typeof parseBigInt("1", 16) != 'undefined') {
            this.engine = "ja";
            this.callback();
          }
          else {
            use_java = false;
          }
        }
        catch (e) {
          use_java = false;
        }
      }
      else {
        try {
          if (typeof parseBigInt("1", 16) != 'undefined') {
            this.engine = "j";
            this.callback();
          }
          else {
            use_java = false;
          }
        }
        catch (e) {
          use_java = false;
        }
      }
    }
    if (!use_java) {
      var jsPath=this.getPathOfJavascriptFile('bigint.js');
      var jsDir=jsPath.substring(0, jsPath.lastIndexOf('/')+1);
      var self=this;
      yepnope([{
        load: [jsDir+"jsbn/jsbn.min.js", 
               jsDir+"jsbn/jsbn2.min.js", 
               jsDir+"jsbn/prng4.js"],
        complete: function() { yepnope([{
            load: [jsDir+"jsbn/rng.js", 
                   jsDir+"bigint_jsbn_add.js"],
            complete: function() {
              self.engine = "js";
              self.callback();
            }
        }]); }
      }]);
    }
    return false;
  },
  
  addApplet : function() {
    var jsPath=this.getPathOfJavascriptFile('bigint.js');
    var id="bigint";
    var code="bigint";
    var codebase=jsPath.substring(0, jsPath.lastIndexOf('/')+1);
    var archive="bigint.jar";
    var tag=this.appletTag(id,code,codebase,1,1,{onstart:"bigint_applet_loaded"});
    //document.write(tag);
    $('bigint_applet_div').innerHTML = tag;
  },
  
  getPathOfJavascriptFile : function(filename) {
    var scripts=document.getElementsByTagName('script');
    for (var i=0; i<scripts.length; i++) {
      var l=scripts[i].src.length;
      if (scripts[i].src.substring(l-filename.length,l)==filename) return scripts[i].src;
    }
    return null;
  },
  
  appletTag : function(id,code,codebaseOrArchive,width,height,params,jdk) {
    var codebase = codebaseOrArchive, archive;
    if (codebaseOrArchive.indexOf(".jar")>0) {
      var p = codebaseOrArchive.lastIndexOf("/");
      codebase = (p>0) ? codebaseOrArchive.substring(0, p) : "";
      archive = codebaseOrArchive.substring(p+1, codebaseOrArchive.length);
    }
    code += (code.indexOf('.class')>0) ? '' : '.class';

    var params2='';
    if(params) for(var i in params) params2+='<param name="'+i+'" value="'+params[i]+'">';
    params2 +=
      '<param name="id" value="' + id + '">'
      +'<param name="code" value="' + code + '">'
      +(archive ? '<param name="archive" value="' + archive + '">'
        : '<param name="codebase" value="' + codebase + '">')
      +'<param name="mayscript" value="true">';
      +'<param name="scriptable" value="true">'

    var tag = '<applet name="' + id + '" '
      + 'code="' + code + '" codebase="' + codebase + '" ' + (archive ? 'archive="' + archive + '" ' : '')
      + 'width='+width + ' height='+height + 'mayscript="true"' + '>'
      + params2
      + '</applet>';
    
    return tag;
  }
}


