// /////////////////////////////////////////////////////////////////////////////
//
// PMP_COMMON.JS - Contains JavaScript used by other PMP_XXX.JS files.
// Dependencies: none 
// Author: Manuel Schwarze, PKI Development 
// Department: GISM, PKI 
// 
// /////////////////////////////////////////////////////////////////////////////
// 
// $Workfile: pmp_common.js $
// $Revision: 3 $
// $Date: 10.08.05 17:48 $
// $Author: Schwama8 $
//
// /////////////////////////////////////////////////////////////////////////////

//
// Statics
//

// ////////////////////////////////////////////////////////////////////////////
// THE FOLLOWING STATIC VARIABLES CAN BE CHANGED ON A SITE-TO-SITE BASIS.
// ////////////////////////////////////////////////////////////////////////////

// Switch for showing debug alerts (must be false in production environment)
var DEBUG_ALERTS = false;

// PMP Authentication URLs for posting of credentials and errors to the
// PMP Server (usually this does not get changed)
// (must match configuration in Apache httpd.conf file)
var URL_PMP_CUSTOM_AUTH = new AuthType(1, "/authentication/customauth", "&nbsp;&nbsp;Custom Authentication&nbsp;&nbsp;");
var URL_PMP_BASIC_AUTH = new AuthType(2, "/authentication/basicauth", "&nbsp;&nbsp;Simple Authentication&nbsp;&nbsp;");
var URL_PMP_FORM_AUTH = new AuthType(4, "/authentication/formbasedauth", "&nbsp;&nbsp;Username/Password Authentication&nbsp;&nbsp;");
var URL_PMP_NTLM_AUTH = new AuthType(8, "/authentication/ntlmauth", "&nbsp;&nbsp;Windows Account Authentication&nbsp;&nbsp;");
var URL_PMP_SSL_AUTH = new AuthType(16, "/authentication/sslauth", "&nbsp;&nbsp;Certificate-based SSL Authentication&nbsp;&nbsp;");
var URL_PMP_ENTRUST_AUTH = new AuthType(32, "/authentication/entrustauth", "&nbsp;&nbsp;Certificate-based Entrust PKI Authentication&nbsp;&nbsp;");

// ////////////////////////////////////////////////////////////////////////////
// DO NOT CHANGE THE FOLLOWING STATIC CONSTANTS AS THEY ARE PMP SPECIFIC.
// ////////////////////////////////////////////////////////////////////////////

// Possible authentication types
var PMP_AUTH_TYPES = new Array(
    URL_PMP_CUSTOM_AUTH, 
    URL_PMP_BASIC_AUTH, 
    URL_PMP_FORM_AUTH, 
    URL_PMP_NTLM_AUTH, 
    URL_PMP_SSL_AUTH, 
    URL_PMP_ENTRUST_AUTH);

// Cookie names
var COOKIE_PMP_SESSION = "PMP_SESSION";
var COOKIE_PMP_CHALLENGE = "PMP_CHALLENGE";
var COOKIE_PMP_ERROR = "PMP_ERROR";
var COOKIE_PMP_USERNAME = "PMP_USERNAME";
var COOKIE_PMP_REMEMBER_USERNAME = "PMP_REMEMBER";

// URL parameters
var URL_PARAM_SITE = "Site";
var URL_PARAM_REALM = "Realm";
var URL_PARAM_REDIRECT = "Redirect";
var URL_PARAM_AUTHTYPES = "authTypes";
var URL_PARAM_ERROR = "Error";

// System information
var IS = new System();

// 
// Static code (do not change)
//
var searchParams = new Array;
sToParams(location.href, searchParams);

// =============================================================================

//
// Public Methods (called from any HTML page)
//

//
// Class to define Authentication types.
// Value Bitmask: Bitflag to identify an authentication type.
// Value URL: Absolute URL path for PMP interface.
// Value Description: Verbal description of authentication type.
// Value Event: JavaScript call for authentication with a type.
// Param bitmask: Bitflag to identify an authentication type.
// Param url: Absolute URL for PMP interface.
// Param description: Verbal description of authentication type.
//
function AuthType(bitmask, url, description) 
{ 
    this.Bitmask = bitmask;
    this.URL = url;
    this.Description = description; 
    this.Event = "javascript:AuthenticateWithType(" + bitmask + ");";
}

//
// Class to define URL parameter / value pairs
// Value name: Name of the URL parameter
// Value value: Value of the URL parameter
// Param n: Name of the URL parameter
// Param v: Value of the URL parameter
function Param(n, v) 
{ 
    this.name=n;
    this.value=v; 
}

//
// Class to determines information about the browser the user is using. 
// This class just have to be instantiated ones. It will set class variables 
// for System variable IS. The following variables are set, incl. type:
// int IS.major
// float IS.minor
// boolean IS.nav
// boolean IS.nav2
// boolean IS.nav3
// boolean IS.nav4
// boolean IS.nav4up
// boolean IS.navonly
// boolean IS.nav6
// boolean IS.nav6up
// boolean IS.firefox
// boolean IS.gecko
// boolean IS.ie
// boolean IS.ie3
// boolean IS.ie4
// boolean IS.ie4up
// boolean IS.ie5
// boolean IS.ie5_5
// boolean IS.ie5up
// boolean IS.ie5_5up
// boolean IS.ie6
// boolean IS.ie6up
// float IS.js
//
function System()
{   
     // convert all characters to lowercase to simplify testing
     var agt=navigator.userAgent.toLowerCase();
    
     // *** BROWSER VERSION ***
     // Note: On IE5, these return 4, so use IS.ie5up to detect IE5.
    
     this.major = parseInt(navigator.appVersion);
     this.minor = parseFloat(navigator.appVersion);
    
     // Note: Opera and WebTV spoof Navigator. We do strict client detection.
     // If you want to allow spoofing, take out the tests for opera and webtv.
     this.nav  = ((agt.indexOf('mozilla')!=-1) && (agt.indexOf('spoofer')==-1)
                 && (agt.indexOf('compatible') == -1) && (agt.indexOf('opera')==-1)
                 && (agt.indexOf('webtv')==-1) && (agt.indexOf('hotjava')==-1));
     this.nav2 = (this.nav && (this.major == 2));
     this.nav3 = (this.nav && (this.major == 3));
     this.nav4 = (this.nav && (this.major == 4));
     this.nav4up = (this.nav && (this.major >= 4));
     this.navonly      = (this.nav && ((agt.indexOf(";nav") != -1) ||
                           (agt.indexOf("; nav") != -1)) );
     this.nav6 = (this.nav && (this.major == 5));
     this.nav6up = (this.nav && (this.major >= 5));
     this.firefox = (this.nav && (this.major >= 5) && (agt.indexOf("firefox")!=-1));
     this.gecko = (agt.indexOf('gecko') != -1);
    
    
     this.ie     = ((agt.indexOf("msie") != -1) && (agt.indexOf("opera") == -1));
     this.ie3    = (this.ie && (this.major < 4));
     this.ie4    = (this.ie && (this.major == 4) && (agt.indexOf("msie 4")!=-1) );
     this.ie4up  = (this.ie  && (this.major >= 4));
     this.ie5    = (this.ie && (this.major == 4) && (agt.indexOf("msie 5.0")!=-1) );
     this.ie5_5  = (this.ie && (this.major == 4) && (agt.indexOf("msie 5.5") !=-1));
     this.ie5up  = (this.ie  && !this.ie3 && !this.ie4);
     this.ie5_5up =(this.ie && !this.ie3 && !this.ie4 && !this.ie5);
     this.ie6    = (this.ie && (this.major == 4) && (agt.indexOf("msie 6.")!=-1) );
     this.ie6up  = (this.ie  && !this.ie3 && !this.ie4 && !this.ie5 && !this.ie5_5);
    
     // *** JAVASCRIPT VERSION CHECK ***
     if (this.nav2 || this.ie3) this.js = 1.0;
     else if (this.nav3) this.js = 1.1;
     else if (this.opera5up) this.js = 1.3;
     else if (this.opera) this.js = 1.1;
     else if ((this.nav4 && (this.minor <= 4.05)) || this.ie4) this.js = 1.2;
     else if ((this.nav4 && (this.minor > 4.05)) || this.ie5) this.js = 1.3;
     else if (this.hotjava3up) this.js = 1.4;
     else if (this.nav6 || this.gecko) this.js = 1.5;
     // NOTE: In the future, update this code when newer versions of JS
     // are released. For now, we try to provide some upward compatibility
     // so that future versions of Nav and IE will show they are at
     // *least* JS 1.x capable. Always check for JS version compatibility
     // with > or >=.
     else if (this.nav6up) this.js = 1.5;
     // note ie5up on mac is 1.4
     else if (this.ie5up) this.js = 1.3
    
     // HACK: no idea for other browsers; always check for JS version with > or
     // >=
     else this.js = 0.0;
}


//
// Returns the URL parameter with the specified name.
// Param name: Name of the URL parameter
// Return: Value of the URL parameter or empty string, if not present.
//
function GetParam(name) 
{ 
    return getParam(searchParams, name); 
}

//
// Adds or replaces a URL parameter with the specified name and value.
// Param name: Name of the URL parameter to be added or replaced
// Param value: Value of the URL parameter
//
function SetParam(name, value) 
{ 
    setParam(searchParams, name, value);  
}

//
// Removes a URL parameter with the specified name from the list of parameters.
// Param name: Name of the URL parameter to be removed
//
function ClearParam(name) 
{ 
    setParam(searchParams, name, null); 
}

//
// Returns the URL parameter with the specified name and removes it from the 
// list.
// Param name: Name of the URL parameter to be retrieved and removed.
// Return: Value of the URL parameter or empty string, if not present.
//
function PullParam(name) 
{ 
    var str = GetParam(name); 
    ClearParam(name); 
    return str; 
}

//
// Removes all URL parameters from the list.
//
function ClearAllParams() 
{ 
    searchParams = new Array(); 
}

//
// Adds all known and registered URL parameters to the specified URL. This
// URL should not contain any parameters yet.
// Param url: URL the parameters shall be added to.
// Return: URL with encoded parameters
//
function AddParams(url) 
{ 
    return url + paramsToS(searchParams, needIE4Fix(url)); 
}

// =============================================================================

// 
// Private Methods (called internally from JavaScript)
//

//
// Converts a string with URL parameters to Param objects an populates the
// passed in parameter array. The array is emptied before new parameters are
// added.
// Param s: String with parameters and values
// Param p: Array with Param objects (in / out)
//
function sToParams(s, p) 
{
    p.length = 0;
    if (!s || s == '') return;
    
    var i = s.indexOf('?') + 1;
    var bEsc = false;
    
    if (i <= 0) 
    {
        i = s.indexOf('%3F');
        if (i < 0) return;
        i += 3;
        bEsc = true;
    }
    
    var j;
    var bDone = false;
    var nSkip;
    
    while (!bDone) 
    {
        j = s.indexOf('&', i); 
        nSkip = 1;
        if (j < 0) 
        {
            j = s.indexOf('%26', i);
            nSkip = 3;
            if (j < 0) 
            {
                j = s.length; 
                bDone = true; 
            }
        }
        
        str = s.substring(i,j);
        k = str.indexOf('='); 
        if (k < 0) k = str.length-1;
        pm = new Param(decode(str.substring(0, k)), decode(str.substring(k + 1, str.length)));
        p[p.length] = pm;
        i = j + nSkip;
    }
}

//
// Converts the passed in array of Param objects to a string with URL parameters
// and escapes them before. If the second parameter is true, the question mark
// starting the parameter row gets also escaped.
// Param p: Array with Param objects
// Param bEnc: True to escape the first ? the parameter string is started with.
//             False otherwise.
//
function paramsToS(p, bEnc) 
{
    var s = (bEnc ? '%3F' : '?');
    var bFirst = true;
    
    for (var i = 0; i < p.length; i++)
    {
        if (p[i].name != '') 
        { 
            s += (bFirst ? '' : '&') + encode(p[i].name) + '=' + encode(p[i].value); 
            bFirst = false;
        }
    }
    
    return (s == (bEnc ? '%3F' : '?')) ? '' : s;
}

//
// Returns the Param object with the specified name from the passed in array
// of Param objects.
// Param p: Array with Param objects
// Param name: Name of the URL parameter
// Return: Param object, if found. Otherwise null.
//
function getPm(p, name) 
{
    var pm = null;

    for (i = 0; i < p.length; i++) 
    {
        if (p[i].name == name) 
            pm = p[i];
    }
    
    return pm;
}

//
// Returns the value of the URL paramater with the specified name from the 
// passed in array of Param objects.
// Param p: Array with Param objects
// Param name: Name of the URL parameter
// Return: Value of parameter, if found. Otherwise empty string.
//
function getParam(p, name) 
{
    pm = getPm(p, name);
    return (pm ? pm.value : '');
}


//
// Adds or replaces a URL parameter in the passed in array of Param objects
// with the specified name and value.
// Param p: Array with Param objects
// Param name: Name of the URL parameter to be added or replaced
// Param value: Value of the URL parameter
//
function setParam(p, name, val) 
{
    pm = getPm(p, name);
    
    if (pm) 
    {
        if (val) pm.value = val;
        else pm.name = '';
    }
    else if (val) 
    { 
        pm = new Param(name, val); 
        p[p.length]=pm; 
    }
}

//
// Determines, if a fix for appending URL parameters is needed that is
// browser specific.
// Param url: URL to append parameters to
// Return: True, if a fix is needed, false otherwise.
//
function needIE4Fix(url) 
{   
    return (IS.ie4 && 
        location.href.indexOf('file:') == 0 && 
        url.indexOf(':') < 0); 
}

//
// Returns the value of the specified cookie. If it could not be found, it
// returns an empty string. The cookie value gets unescaped.
// Param CookieName: Name of the cookie to be determined.
// Return: Cookie value or empty string.
//
function getCookie(CookieName) 
{
    var CookieString = document.cookie;
    var CookieSet = CookieString.split (';');
    var SetSize = CookieSet.length;
    var CookiePieces;
    var ReturnValue = '';
    var x = 0;

    for (x = 0; ((x < SetSize) && (ReturnValue == "")); x++)
    {
        CookiePieces = CookieSet[x].split ('=');

        if (CookiePieces[0].substring (0,1) == ' ') 
            CookiePieces[0] = CookiePieces[0].substring (1, CookiePieces[0].length);

        if (CookiePieces[0] == CookieName)
            ReturnValue = CookiePieces[1];
    }  
  
    ReturnValue = decode(ReturnValue);
  
    if (DEBUG_ALERTS)
        alert("getCookie(" + CookieName + ") = " + ReturnValue);
  
    return ReturnValue;
}

//
// Sets the value of the specified cookie. An old value is overwritten.
// The cookie value gets escaped. It is valied for one year.
// Param strCookieName: Name of the cookie to be set.
// Param strCookieValue: Value of the cookie. Pass an empty string to reset the
// cookie value.
//
function setCookie(strCookieName, strCookieValue)
{ 
    var dtexpires = new Date();
    dtexpires.setYear(dtexpires.getFullYear() + 1);
    setFullCookie(strCookieName, strCookieValue, null, null, dtexpires.toGMTString());
}

//
// Sets the value of the specified cookie. An old value is overwritten.
// The cookie value gets escaped. It is valied for one year.
// Param strCookieName: Name of the cookie to be set.
// Param strCookieValue: Value of the cookie. Pass an empty string to reset the
// cookie value.
// Param strDomain: Domain of the cookie. Can be null for current domain.
// Param strPath: Path of the cookie. Can be null for whole domain.
// Param strExpiryGMTDate: Expiry date of the cookie in GMT format. Can be null.
//
function setFullCookie(strCookieName, strCookieValue, strDomain, strPath, strExpiryGMTDate)
{ 
    var newCookie = strCookieName + "=" + encode (strCookieValue) + 
        (strDomain != null ? ("; domain=" + strDomain) : "") + 
        (strPath != null ? ("; path=" + strPath) : "; path=/") + 
        (strExpiryGMTDate != null ? ("; expires=" + strExpiryGMTDate) : "");
    
    if (newCookie.length <= 4096)
        document.cookie = newCookie;
    
    if (DEBUG_ALERTS)
        alert("SetCookie " + newCookie);
}

//
// Decodes an HTML encoded string by also converting plus signs (+) to spaces.
// Param strEncoded HTML encoded string. Can be null.
// Return: Decoded string or null.
//
function decode(strEncoded)
{
    if (strEncoded == null)
        return null;
    
    
    var strDecoded1 = strEncoded.replace(/\+/g, " ");
    var strDecoded2 = unescape(strDecoded1);
    
    if (DEBUG_ALERTS)
        alert("Decode: '" + strEncoded + "' => '" + strDecoded1 + "' => '" + strDecoded2 + "'");
        
    return strDecoded2;
}

//
// Encodes a plain string with HTML encoding by converting spaces to plus 
// signs (+).
// Param strPlain Plain string. Can be null.
// Return: HTML encoded string or null.
//
function encode(strPlain)
{
    if (strPlain == null)
        return null;
 
    var strEncoded1 = escape(strPlain);
    var strEncoded2 = strEncoded1.replace(/ /g, "+");
 
    if (DEBUG_ALERTS)
        alert("Encode: '" + strPlain + "' => '" + strEncoded1 + "' => '" + strEncoded2 + "'");
     
    return strEncoded2;
}