<% /*
    $Revision$
    $Id$

    File:           appl/fac/fac_verify.inc
    Description:    Verify FACILITOR installation
    Parameters:     ?XXXX optional customerId
    Context:        Manual call to check installation
    Note:

    Todo: dbms_utility.get_hash_value (cad_thema.asp->UpdateLegenda)
          dbms_utility.get_time() (fac/perfmon.asp)

          DBMS_UTILITY.COMPILE_SCHEMA diverse
          DBMS_OUTPUT.put_line (staat meestal uitgecommentarieerd)
          DBMS_LOB.WRITEAPPEND (fac_pacx.src)
          DBMS_XMLGEN.ctxhandle (fac_pacx.src create_fac_usrrap_view_node)

          UTL_MATCH.EDIT_DISTANCE_SIMILARITY
          utl_match.jaro_winkler

*/ %>
<%
    Response.Expires=-1;
    custID=Session("CustomerID");
    if (!custID) custID = Request.QueryString();

// Max download testen
// Heel vroeg zodat we veilig een Response.Clear kunnen doen
var  arr = ['X'];
for (var i = 0; i < 16; i++)
    arr.push(arr.join(""));
var str = arr.join(""); // 64kB
var i = 0;
try
{
    while (true)
    {
        i++; // Veronderstel dat deze nog grotendeels lukt
        Response.Write(str);
    }
}
catch(e)
{
    Response.Clear;
    var maxDownload = i * str.length;
}

if (Request.Form("saveas").count > 0)
{
    Response.clear;
    Response.contenttype = "application/octet-stream";
    Response.AddHeader("content-disposition","attachment; filename=FACILITOR_verify_" + custID + "_" + toDateString(new Date()) + ".html");
}
%>
<head>
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
<META HTTP-EQUIV="Expires" CONTENT="-1">
<style>
    th, td {
      font-family: Verdana;
      font-size: 10pt;
      color: #000000;
      padding: 2px;
      background-color: #D6D8E8;
    }
    label { width: 50px; display: inline; }
    th { text-align: left }
    table.inside td
        {
            padding: 0px 0px 0px 5px;
            font-size: smaller;
        }
</style>
<title>FACILITOR installation verifier</title>
<script>
    function toggle(id)
    {
        var itm = document.getElementById(id);
        if (itm.style.display == 'none')
            itm.style.display = 'block';
        else
            itm.style.display = 'none';
    }
    function onload()
    {
        writeLocalStorage();

        var dt = (new Date()).toLocaleString();
        document.getElementById('clienttime').appendChild( document.createTextNode(dt) );

        // Check for 500_error.asp
        var xhr = new XMLHttpRequest();
        xhr.open('GET', 'fac_verify_test.asp?check500=1');
        xhr.onreadystatechange = function()
        {
            var txt;
            // Ready state 4 means the request is done
            if (xhr.readyState === 4)
            {
                if (xhr.status === 500)
                {
                    if (xhr.responseText == "OK500")
                    {
                        txt = "500_error installed correctly";
                        document.getElementById('result500').innerHTML = 'Ok';
                        document.getElementById('result500').style.color = 'green';
                    }
                    else
                    {
                        txt = "500_error NOT installed correctly";
                        document.getElementById('result500').innerHTML = 'Error';
                        document.getElementById('result500').style.backgroundColor = 'red';
                        document.getElementById('result500').style.color = 'black';
                    }
                }
                else
                {
                    txt = "Strange: could not verify 500_error.asp, status: " + xhr.status;
                }
                document.getElementById('check500').appendChild( document.createTextNode(txt) );
            }
        }
        xhr.send(null);

        // Check for custom 500_error.asp
        var xhrb = new XMLHttpRequest();
        xhrb.open('GET', 'fac_verify_test.asp?check500b=1');
        xhrb.onreadystatechange = function()
        {
            var txt;
            // Ready state 4 means the request is done
            if (xhrb.readyState === 4)
            {
                if (xhrb.status === 500)
                {
                    if (xhrb.responseText == "OK500B")
                    {
                        txt = "500_error existingResponse in web.config";
                        document.getElementById('result500b').innerHTML = 'Ok';
                        document.getElementById('result500b').style.color = 'green';
                    }
                    else
                    {
                        txt = "500_error existingResponse not OK."
                            + "<br>In web.config add existingResponse=\"PassThrough\" to &lt;httpErrors&gt; tag";
                        document.getElementById('result500b').innerHTML = 'Warning';
                        //document.getElementById('result500b').style.backgroundColor = 'red';
                        document.getElementById('result500b').style.color = 'yellow';
                    }
                }
                else
                {
                    txt = "Strange: could not verify 500B error, status: " + xhrb.status;
                }
                var span = document.getElementById('check500b');
                span.innerHTML = txt;
                //document.getElementById('check500b').appendChild( div );
            }
        }
        xhrb.send(null);

        // Check for PUT for API2
        var xhr2 = new XMLHttpRequest();
        xhr2.open('PUT', 'fac_verify_test.asp?checkPUT=1');
        xhr2.onreadystatechange = function()
        {
            var txt;
            // Ready state 4 means the request is done
            if (xhr2.readyState === 4)
            {
                if (xhr2.status == 200)
                {
                    txt = xhr2.responseText;
                    document.getElementById('resultAPI2P').innerHTML = 'Ok';
                    document.getElementById('resultAPI2P').style.color = 'green';
                }
                else
                {
                    txt = xhr2.status + ": " + xhr2.statusText;
                }
                document.getElementById('checkAPI2P').appendChild( document.createTextNode(txt) );
            }
        }
        xhr2.send(null);

        // Check for DELETE for API2
        var xhr3 = new XMLHttpRequest();
        xhr3.open('DELETE', 'fac_verify_test.asp?checkDELETE=1');
        xhr3.onreadystatechange = function()
        {
            var txt;
            // Ready state 4 means the request is done
            if (xhr3.readyState === 4)
            {
                if (xhr3.status == 200)
                {
                    txt = xhr3.responseText;
                    document.getElementById('resultAPI2D').innerHTML = 'Ok';
                    document.getElementById('resultAPI2D').style.color = 'green';
                }
                else
                {
                    txt = xhr3.status + ": " + xhr3.statusText;
                }
                document.getElementById('checkAPI2D').appendChild( document.createTextNode(txt) );
            }
        }
        xhr3.send(null);

        // Check for caching headers
        var xhr4 = new XMLHttpRequest();
        xhr4.open('GET', '../localscripts/date.js?x=<%=new Date().getTime()%>');
        xhr4.onreadystatechange = function()
        {
            var txt = "<no Cache-Control header with max-age>";
            // Ready state 4 means the request is done
            if (xhr4.readyState === 4)
            {
                if (xhr4.status == 200)
                {
                    var cache = xhr4.getResponseHeader("Cache-Control");
                    //var cache = xhr4.getAllResponseHeaders().replace(/\r/g, "\nq\n");
                    if (cache != null)
                    {
                        txt = "Cache: " + cache;
                        if (cache.split("max-age=").length > 1)
                        {
                            var maxage = parseInt(cache.split("max-age=")[1], 10);
                            if (maxage > 0)
                                txt += " (= " + (maxage / 3600) + " hours)";
                        }
                        document.getElementById('resultCaching').innerHTML = 'Ok';
                        document.getElementById('resultCaching').style.color = 'green';
                    }
                }
                else
                {
                    txt = xhr4.status + ": " + xhr4.statusText;
                }
                document.getElementById('checkCaching').appendChild( document.createTextNode(txt) );
            }
        }
        xhr4.send(null);

        // Check for .NET *not* beaing enabled
        var xhr5 = new XMLHttpRequest();
        xhr5.open('GET', 'fac_verify_test.aspx');
        xhr5.onreadystatechange = function()
        {
            var txt;
            // Ready state 4 means the request is done
            if (xhr5.readyState === 4)
            {
                if (xhr5.status == 404) // It should not be found when .NET not
                {
                    txt = "Calling an aspx fails. Good!";
                    document.getElementById('resultASPX').innerHTML = 'Ok';
                    document.getElementById('resultASPX').style.color = 'green';
                }
                else
                {
                    txt = "Calling an aspx succeeds. Not Good!";
                    txt += "<br>FACILITOR does not need managed code.";
                    txt += "<br>Best disable it in the IIS application pool.";
                    document.getElementById('resultASPX').innerHTML = 'Error';
                    document.getElementById('resultASPX').style.backgroundColor = 'red';
                    document.getElementById('resultASPX').style.color = 'black';
                }
                var span = document.createElement("span");
                span.innerHTML = txt;
                document.getElementById('checkASPX').appendChild( span );
            }
        }
        xhr5.send(null);
    }
    function reload()
    {
        var hrefBase = window.location.href;
        window.location.href = hrefBase;
    }
    function writeLocalStorage()
    {
        var output;
        if (localStorage) {
            if (localStorage.length) {
               output = "<table class='inside'>";
               for (var i = 0; i < localStorage.length; i++) {
                   output += "<tr><td>" + localStorage.key(i) + '</td><td>' + localStorage.getItem(localStorage.key(i)) + '</td></tr>';
               output += "</table>"
               }
            } else {
               output = 'There is no data stored for this domain.';
            }
        } else {
            output = 'Your browser does not support local storage.'
        }
        var span = document.createElement("span");
        span.innerHTML = output;
        document.getElementById('checkLocalStorage').appendChild( span );
    }
</script>
</head>
<body onload='onload()'>
<%
var osuser = String(Request.ServerVariables("REMOTE_USER"));
 if (0&&osuser == "")
 {
    Response.Status = "401 Unauthorized";
    Response.End(); // Reloads current file
}

resultcodes = { ok: "No problem",
                fatal: "No further checks possible, fix and recheck",
                error: "Error, fix and recheck",
                warning: "Warning: FACILITOR may run sub-optimal",
                inform: "Information only"
              }

function checker (description, fnCheck, toggler)
{
    if (typeof cnt_id == "undefined") cnt_id = 1;
    cnt_id ++;
    if (typeof last__GROUP == "undefined" || __GROUP != last__GROUP)
    {
        Response.Write("\n<tr><td colspan='3' style='background-color:#D6E8FF'><strong>"+__GROUP+"</strong></td></tr>");
        last__GROUP = __GROUP;
    }

    if (toggler)
        Response.Write("\n<tr onClick='toggle(\"div"+cnt_id+"\")'><td>");
    else
        Response.Write("\n<tr><td>");
    Response.Write(description);
    try
    {
        var res = fnCheck();
    }
    catch (e)
    {
        Response.Write("<br/><em>"+e.description+"</em>");
        Response.Write("<td style='color:red'>Internal error</tr></table>");
        Response.Write("<h1 style='color:red'>Verify aborted</h1>");
        Response.End;
    }

    if (toggler)
    {
        var matches = res.message.match(/<tr/g); // try to match <tr
        var count = matches?matches.length:0; // this is how many!
        Response.Write(" (click for details, " + count + " lines)");
    }

    Response.Write("<div id='div"+cnt_id+"' style='" + (toggler?"display:none;":"") + "padding-left: 20px'><em>"+(res.message||'')+"</em></div>");
    Response.Write("</td>");
    Response.Write("\n<td>"+(res.info||'')+"</td>");
    var idtxt = res.id?" id="+res.id:"";
    switch (res.result)
    {
        case resultcodes.ok:
            Response.Write("<td style='color:green'"+idtxt+">Ok</td>");
            break;
        case resultcodes.warning:
            Response.Write("<td style='color:yellow'"+idtxt+">Warning</td>");
            break;
        case resultcodes.error:
            Response.Write("<td style='background-color:red'"+idtxt+">Error</td>");
            break;
        case resultcodes.fatal:
            Response.Write("<td style='background-color:red'"+idtxt+">Error</td></tr></table>");
            Response.Write("<h1 style='background-color:red'>Verify aborted</h1>");
            Response.End;
            break;
        case resultcodes.inform:
            Response.Write("<td style='color:green'"+idtxt+">n/a</td>");
            break;
        default:
            Response.Write("<td style='color:red'"+idtxt+">Unknown</td>");
            break;
    }
    Response.Write("</tr>");
}

function testfolder(relpath, mustbethere)
{
    var present = true;
    checker(relpath + " folder present?",
            function ()
            {
                var cust = Server.MapPath(relpath);
                if (fso.FolderExists(cust)) return { result: resultcodes.ok };
                present = false;
                if (mustbethere)
                    return { result: resultcodes.error,
                             message: "Folder not found at: " + cust};
                return { result: resultcodes.ok,
                         message: "Folder not found at: " + cust + "<br>Will be created automatically when needed"};
            }
        )
    if (!present) return;

    // Controleer de schrijfrechten. Merk op dat dit niet waterdicht is. Bij licentieklanten met
    // integrated authentication kan het nog gebeuren dat jij een bestand wat je hebt aangemaakt
    // wel kunt verwijderen maar dat iemand anders dat niet kan. Daarom moet IUSR modify rechten
    // op de diverse folders.
    var testname = relpath + "/$Verify$.tmp";
    checker(relpath + " writeable",
        function ()
        {
            try
            {
                var path = Server.MapPath(testname);
                var objText = fso.OpenTextFile(path, 8, true, 0);
                objText.WriteLine("verify.asp test file");
                objText.Close();
            }
            catch(e)
            {
                return { result: resultcodes.error,
                        message: e.description +  "<br>Error writing to " + testname};
            }

            return { result: resultcodes.ok, message: testname + " could be written to"};
        }
    )

    checker(relpath + " deleteable",
        function ()
        {
            try
            {
                var path = Server.MapPath(testname);
                var objText = fso.DeleteFile(path, false);
            }
            catch(e)
            {
                return { result: resultcodes.error,
                        message: e.description +  "<br>Error deleting " + testname};
            }

            return { result: resultcodes.ok, message: testname + " could be removed"};
        }
    )

}
%><table>
<thead><tr><th align='left'><table><tr><td><b>Verify.asp $Revision$<br>
               Checking <%= toDateTimeString(new Date()) %></b><br>
               <% if (this.FCLTVersion ) Response.Write("Software version " + this.FCLTVersion + "<br>") %>
               <a href='fac_verify_sha.asp' target="_new1">Click</a> to verify files<br>
               </td>
               <td valign='top'>
                <form method='post' taget='_new'>
                    <input name='saveas' type='hidden'><input type='submit' value='Save as'>
                    <input type='button' onclick='reload()' value='Refresh'>
                </form>
               </td></tr></table>
           </th>
           <th align='left'>Information</th>
           <th align='left'>Result</th>
       </tr>
</thead>
<%
var fso = Server.CreateObject("Scripting.FileSystemObject")

__GROUP = "Webserver configuration";

checker("Session.Timeout",
            function ()
            {
                var res = resultcodes.ok;
                if (Session.Timeout < 30) // Minder dan 30 minuten?
                    res = resultcodes.warning;
                return { result: res,
                         message: "Session timeout is " + Session.Timeout + " minutes.",
                         info: String(Session.Timeout) + " minutes"
                       };
            }
        )

checker("Session.Codepage",
            function ()
            {
                txt = { 1252 : "windows-1252",
                        28605: "iso-8859-15",
                        65001: "UTF-8"}[Session.Codepage];
                txt = txt || "Onbekend";
                return { result: resultcodes.ok,
                         message: "Default codepage  " + Session.Codepage + " ("+txt+")",
                         info: String(Session.Codepage)
                       };
            }
        )

checker("Session.LCID",
            function ()
            {
                txt = { 1033: "English (US)",
                        1036: "French",
                        2048: "'Default'",
                        2057: "English (UK)",
                        1043: "Dutch (NL)",
                        2067: "Dutch (BE)"}[Session.LCID];
                txt = txt || "Onbekend";
                return { result: resultcodes.ok,
                         message: "Locale identifier  " + Session.LCID + " ("+txt+")",
                         info: String(Session.LCID)
                       };
            }
        )

checker("Server.ScriptTimeout",
            function ()
            {
                var res = resultcodes.ok;
                if (Server.ScriptTimeout < 30) // Minder dan 30 seconde?
                    res = resultcodes.warning;
                return { result: res,
                         message: "Script timeout is " + Server.ScriptTimeout + " seconds.",
                         info: String(Server.ScriptTimeout) + " seconds"
                       };
            }
        )

checker("Application pool",
            function ()
            {
                var res = resultcodes.ok;
                var message;
                var info;
                if (typeof Application("SET_INSTANCE_TIME") == "undefined")
                {
                    res = resultcodes.warning;
                    message = "Application('SET_INSTANCE_TIME') not set?";
                }
                else
                {
                    var tm_start = new Date(Application("SET_INSTANCE_TIME"));
                    message = "Application pool start: " + toDateTimeString(tm_start);
                    if (tm_start.getHours() >= 9 && tm_start.getHours() <= 17)
                    {
                        info = "During office hours?";
                        res = resultcodes.warning;
                    }
                }
                return { result: res,
                         message: message,
                         info: info
                       };
            }
        )

checker("Max download (Response Buffering Limit)",
            function ()
            {
                var res = resultcodes.ok;
                if (maxDownload < 4194304) // Minder dan 4MB?
                    res = resultcodes.warning;
                if (maxDownload < 2097152) // Minder dan 2MB?
                    res = resultcodes.error;
                return { result: res,
                         message: "Maximum download is " + maxDownload + " bytes",
                         info: String(maxDownload / 1024) + "kB" + "<br>" + (maxDownload / 1024 / 1024).toFixed(1) + "MB"
                       };
            }
        )

checker("Euro symbol &euro; in sourcecode",
            function ()
            {
                var nn = "€".charCodeAt(0);
                if (nn == 0x20AC)
                    res = resultcodes.ok;
                else
                    res = resultcodes.warning;

                return { result: res,
                         message: "Euro is read into Javascript as: 0x" + nn.toString(16),
                         info: "Euro: €"
                       };
            }
        )

if (0) checker("Charset",
            function ()
            {
                return { result: resultcodes.ok,
                         message: "Default Charset  " + Response.Charset,
                         info: String(Response.Charset)
                       };
            }
        )

checker("Free diskspace",
            function ()
            {
                res = resultcodes.unknown;
                try
                {

                    drvPath = Server.MapPath("../../");
                    //return { message: drvPath};
                    var d = fso.GetDrive(fso.GetDriveName(drvPath));
                    var totaltxt = (d.TotalSize/1024/1024/1024).toFixed(1) + " GB";
                    var free = d.AvailableSpace / d.TotalSize * 100;
                    if (free < 10)
                        free = free.toFixed(1);
                    else
                        free = Math.round(free);
                    var availtxt = (d.AvailableSpace/1024/1024/1024).toFixed(1) + " GB (" + free + "%)";

                    txt = "Drive " + drvPath.toUpperCase();
                    //txt += "<br>Volumename " + d.VolumeName;
                    //txt += "<br>Free Space: " + (d.FreeSpace/1024/1024/1024).toFixed(1) + " GB";
                    txt += "<br>Total Space: " + totaltxt
                        +  "<br>Available Space: " + availtxt;

                    if (d.AvailableSpace < 2e9 || free < 5) // Minder dan 2GB of 5% is slecht
                    {
                        res = resultcodes.error;
                        txt += "<br>Less than 2GB or less than 5%";
                    }
                    else if (d.AvailableSpace > 10e9 || free > 10) // Meer dan 10GB of meer dan 10% is ok
                    {
                        res = resultcodes.ok;
                    }
                    else // twijfel
                    {
                        res = resultcodes.warning;
                        txt += "<br>Less than 10GB or less than 10%";
                    }
                }
                catch (e)
                {
                    return { result: resultcodes.warning,
                              message: e.description,
                              info: "Could not read"};
                }
                return { result: res, message: txt, info: availtxt };
            }
        )

checker("Integrated Single Signon",
            function ()
            {
                var usr = Request.ServerVariables("REMOTE_USER");
                if (usr == "")
                    txt = "No user recognized"
                else
                    txt = "User recognized as: " + usr;
                //txt += "<br><a href='"+Request.ServerVariables("SCRIPT_NAME")+"'>Reload now</a";

                return { result: resultcodes.ok,
                         message: txt,
                         info: usr
                       };
            }
        )

checker("500_error.asp",
            function ()
            {
                txt = "Checking 500-error handler: <span id='check500'></span>";
                res = resultcodes.unknown;
                return { result: res, message: txt, id: "result500" };
            }
        )

checker("Check static content caching",
            function ()
            {
                txt = "<span id='checkCaching'></span>";
                res = resultcodes.warning;
                return { result: res, message: txt, id: "resultCaching" };
            }
        )

//------------------------------------------------------------------------------
__GROUP = "Classic ASP";
checker("Javascript",
            function ()
            {
                var txt = "ScriptEngine: " + ScriptEngine()
                        + "<br>ScriptEngineMajorVersion: " + ScriptEngineMajorVersion()
                        + "<br>ScriptEngineMinorVersion: " + ScriptEngineMinorVersion()
                        + "<br>ScriptEngineBuildVersion: " + ScriptEngineBuildVersion();
                var info = ScriptEngineMajorVersion() + "."
                         + ScriptEngineMinorVersion() + "."
                         + ScriptEngineBuildVersion()

                return { result: resultcodes.ok, message: txt, info: info };
            }
        )

checker("VBscript",
            function ()
            {
                var txt = GetVBScriptEngineTxt();
                var info = GetVBScriptEngineInfo();
                return { result: resultcodes.ok, message: txt, info: info };
            }
        )

checker("Managed code should not be enabled.",
            function ()
            {
                txt = "Checking managed code: <span id='checkASPX'></span>";
                res = resultcodes.warning;
                return { result: res, message: txt, id: "resultASPX" };
            }
        )

__GROUP = "FACILITOR software";
//------------------------------------------------------------------------------
testfolder("../../cust", true); // mustbethere
testfolder("../../temp", true); // mustbethere

// TODO: Zelf onder cust zoeken naar een (enkele) folder met een Oracle.udl?
checker("Determine customerID",
            function ()
            {
                if (String(custID).length == 4) return { result: resultcodes.ok, info: custID };
                return { result: resultcodes.fatal, message: "Cannot determine customerID. Open FACILITOR homepage once or use verify.asp?XXXX"};
            }
        )

testfolder("../../cust/"+custID, true);
custFolder = Server.MapPath("../../cust/"+custID);

testfile = Server.MapPath("../../default.asp");
checker("../default.asp file available",
            function ()
            {
                if (fso.FileExists(testfile)) return { result: resultcodes.ok };
                return { result: resultcodes.error, message: "File not found: " + testfile};
            }
        )

__GROUP = "Customer files";
testfolder("../../cust/"+custID+"/flexfiles", false); // neednotbethere
testfolder("../../cust/"+custID+"/dwf", false); // neednotbethere

__GROUP = "Settings";

var puostring = "puo_forcemailaddress,puo_forcefromaddress,puo_forceorderaddress,puo_forcemobile,puo_fclt_web_url";
if (this.settings) // (deze test werkt niet vanuit verify.asp.inc)
{
    checker("Environment custenv SETTING overrules",
                function ()
                {
                    var found = [];
                    if (fso.FileExists(Server.MapPath("../../cust/custenv.wsc")))
                        found.push("Found: cust/custenv.wsc");
                    if (fso.FileExists(Server.MapPath("../../cust/" + custID + "/custenv.wsc")))
                        found.push("Found: cust/" + custID + "/custenv.wsc");

                    var txt = [];
                    var sets = puostring.split(",");
                    for (var i in settings.overrule)
                    {
                        if (puostring.indexOf(i) < 0 || !i.match(/force/)) // die doen we apart
                            txt.push("<tr><td>" + i + "</td><td>" + safe.html(settings.get_setting(i, null, true)) + "</td><td><strong>" + safe.html(S(i)) + "</strong></td></tr>");
                    }
                    if (txt.length)
                        result = { result:
                                   resultcodes.warning,
                                   message: found.join("<br>")
                                          + "<table><tr><th>Name</th><th>Original</th><th>Overrule</th></tr><tr><td>"
                                          + txt.join("") + "</table>" };
                    else
                        result= { result: resultcodes.ok, message: "No overrules" };

                    return result;
                }
            )
    checker("Environment custenv LCL overrules",
                function ()
                {
                    var txt = [];
                    for (naam in overrule_lcl)
                    {
                        var orgtxt = "";
                        if (cache_lcls[naam])
                            orgtxt = cache_lcls[naam].orgtxt;
                        txt.push("<tr><td>" + naam + "</td><td>" + safe.html(orgtxt) + "</td><td><strong>" + safe.html(L(naam)) + "</strong></td></tr>");
                        if (cache_lcls[naam])
                            cache_lcls[naam].txt = overrule_lcl[naam];
                        else
                            cache_lcls[naam] = { txt: overrule_lcl[naam], overrulefallback: true };
                    }
                    if (txt.length)
                        result = { result:
                                   resultcodes.warning,
                                   message: "<table><tr><th>Name</th><th>Original</th><th>Overrule</th></tr><tr><td>"
                                            + txt.join("") + "</table>" };
                    else
                        result= { result: resultcodes.ok, message: "No overrules" };

                    return result;
                }
            )
}

__GROUP = "Putorders";

if (this.S) // (deze test werkt niet vanuit verify.asp.inc)
{
    checker("Force overrules",
                function ()
                {
                    var puosets = puostring.split(",");
                    var txt = [];
                    for (var i in puosets)
                    {
                        if (puosets[i].match(/force/) && S(puosets[i]))
                            txt.push(puosets[i] + "</td><td><strong>" + safe.html(S(puosets[i])) + "</strong>");
                    }
                    if (txt.length)
                        result = { result: resultcodes.warning, message: "<table><tr><td>" + txt.join("</td></tr><tr><td>") + "</td></tr></table>" };
                    else
                        result= { result: resultcodes.ok, message: "No overrules" };

                    return result;
                }
            )

    checker("Internal web url S('puo_fclt_web_url')",
                function ()
                {
                    if (!S("puo_fclt_web_url"))
                    {
                        return { result: resultcodes.warning, message: "S('puo_fclt_web_url') is not set.<br>Embedded photos in e-mails will not be available" };
                    }
                    var puo_web_url = S("puo_fclt_web_url") + "/appl/fac/fac_verify_test.asp?checkINSTANCE=1&fac_id=" + custID;
                    // Controleer of we via S("puo_fclt_web_url") op dezelfde webserver uitkomen als de huidige request
                    // Ze moeten beide dezelfde Application("SET_INSTANCE_RANDOM") hebben
                    var result = { result: resultcodes.ok, message: "Checking internal url: " + S("puo_fclt_web_url") };
                    try
                    {
                        var http_request = new ActiveXObject("Msxml2.ServerXMLHTTP.6.0");
                        http_request.open("GET", puo_web_url, false); // Synchroon
                        // Het hoeft niet echt lang te duren!
                        var lResolve = 1 * 1000;
                        var lConnect = 1 * 1000;
                        var lSend = 2 * 1000;
                        var lReceive = 2 * 1000;
                        http_request.setTimeouts(lResolve, lConnect, lSend, lReceive);
                        http_request.send();

                        if (http_request.status < 200 || http_request.status > 299)
                        {
                            result.result = resultcodes.error;
                            result.message += "<br>http_request error " + http_request.status + ": (" + http_request.statusText + ")";
                        }
                        else
                        {
                            var remote_random = http_request.responseText;
                            var this_random = Application("SET_INSTANCE_RANDOM");
                            if (remote_random != this_random)
                            {
                                result.result = resultcodes.error;
                                result.message += "<br>puo_fclt_web_url url does not point to this website? ";
                            }
                        }
                    }
                    catch(e)
                    {
                        result.result = resultcodes.error;
                        result.message += "<br>http_request error " + e.description;
                    }
                    return result;
                }
            )
}

checker("Logfiles",
            function ()
            {
                var dt = new Date();
                dt.setMonth(dt.getMonth() -1);
                var month = dt.getMonth()+1;
                var year = dt.getYear();
                txt = "putorders_" + custID + "_" + year + "_" + (month<10?"0":"") + month + ".log";
                txt += " <a href='fac_StreamFile.asp?logger=1&ago=1&zipfile=1'>scheduled</a>";
                txt += " or <a href='fac_StreamFile.asp?logger=2&ago=1&zipfile=1'> immediate</a>";
                var dt = new Date();
                var month = dt.getMonth()+1;
                var year = dt.getYear();
                txt += "<br>putorders_" + custID + "_" + year + "_" + (month<10?"0":"") + month + ".log";
                txt += " <a href='fac_StreamFile.asp?logger=1&zipfile=1'>scheduled</a>";
                txt += " or <a href='fac_StreamFile.asp?logger=2&zipfile=1'> immediate</a>";

                return { result: resultcodes.ok, message: txt };
            }
        )


//------------------------------------------------------------------------------
__GROUP = "API2 (optional)";
checker("API2 REST HTTP Verbs",
            function ()
            {
                txt = "Checking PUT: <span id='checkAPI2P'></span>";
                res = resultcodes.warning;
                return { result: res, message: txt, id: "resultAPI2P" };
            }
        )

checker("API2 REST HTTP Verbs",
            function ()
            {
                txt = "Checking DELETE: <span id='checkAPI2D'></span>";
                res = resultcodes.warning;
                return { result: res, message: txt, id: "resultAPI2D" };
            }
        )

checker("500 error for API2",
            function ()
            {
                txt = "Checking 500-API handler: <span id='check500b'></span>";
                res = resultcodes.unknown;
                return { result: res, message: txt, id: "result500b" };
            }
        )

checker("IIS URL Rewrite 2.0 (for API2)",
            function ()
            {
                var REWR = Request.QueryString("rewriterversion");
                if (REWR.Count)
                {
                    return { result: resultcodes.ok,
                             message: "REWRITER version  " + REWR,
                             info: String(REWR)
                           };
                }
                return { result: resultcodes.error,
                         message: "IIS URL Rewrite 2.0 not installed"
                       };
            }
        )


//------------------------------------------------------------------------------
__GROUP = "Oracle server";
var udlPath = custFolder+"/Oracle.udl";
checker("Oracle.udl available",
            function ()
            {
                if (fso.FileExists(udlPath)) return { result: resultcodes.ok, message: " using " + udlPath };
                return { result: resultcodes.error, message: "Oracle.udl file not found at: " + udlPath};
            }
        )

checker("Opening Oracle.udl",
            function ()
            {
                /*var*/ Oracle = Server.CreateObject("ADODB.Connection"); // wordt globaal
                Oracle.Open("File Name=" + udlPath);
                var txt = "Schema: " + Oracle.Properties("User Name") + "@" + Oracle.Properties("Data Source Name")
                        + "<br>DBMS Name: " +Oracle.Properties("DBMS Name")
                        + "<br>DBMS Version: " + Oracle.Properties("DBMS Version")
                        + "<br>ADO Version: " + Oracle.Version
                        + "<br>Provider Version: " + Oracle.Properties("Provider Version")
                        + "<br>Provider Name: " + Oracle.Properties("Provider Name")
                        + "<br>OLE DB Version: " + Oracle.Properties("OLE DB Version")

                return { result: resultcodes.ok, message: txt, info: Oracle.Properties("Provider Name") };
            }
        )

checker("ADODB Properties",
            function ()
            {
                txt = "<table class='inside'>";
                for (i=0; i<Oracle.Properties.Count; i++)
                {
                    if (Oracle.Properties(i).Name.toLowerCase() != "password")
                        txt = txt + "\n<tr><td>" + Oracle.Properties(i).Name + "</td><td>" + Oracle.Properties(i) + "</td></tr>";
                }
                txt += "</table>";
                return { result: resultcodes.ok, message: txt};
            },
            true // toggler
        )

checker("Database context",
            function ()
            {
                var toCheck =
                    ['TERMINAL',
                    'LANGUAGE',
                    'SID',
                    'SESSIONID',
                    'INSTANCE',
                    'ENTRYID',
                    'ISDBA',
                    //'SERVICE_HOST', // niet op 10g?
                    'SERVICE_NAME',

                    'NLS_TERRITORY',
                    'NLS_CURRENCY',
                    'NLS_CALENDAR',
                    'NLS_DATE_FORMAT',
                    'NLS_DATE_LANGUAGE',
                    'NLS_SORT',
                    'CURRENT_USER',
                    'CURRENT_USERID',
                    'SESSION_USER',
                    'SESSION_USERID',
                    'PROXY_USER',

                    'PROXY_USERID',
                    'DB_DOMAIN',
                    'DB_NAME',
                    'HOST',
                    'SERVER_HOST',
                    'OS_USER',
                    'EXTERNAL_NAME',
                    'IP_ADDRESS',
                    'NETWORK_PROTOCOL',
                    'BG_JOB_ID',
                    'FG_JOB_ID',
                    'AUTHENTICATION_TYPE',
                    'AUTHENTICATION_DATA',
                    'CURRENT_SQL',
                    'CLIENT_IDENTIFIER',
                    'GLOBAL_CONTEXT_MEMORY'];
                txt = "<table class='inside'>";
                for (i=0; i< toCheck.length; i++)
                {
                    sql = "SELECT SYS_CONTEXT('USERENV','"+toCheck[i]+"') FROM DUAL";
                    oRs = Oracle.Execute(sql);
                    txt = txt + "\n<tr><td>" + toCheck[i] + "</td><td>" + Server.HTMLEncode(String(oRs(0).Value)) + "</td></tr>";

                }
                txt += "</table>";
                return { result: resultcodes.ok, message: txt };
            },
            true // toggler
        )


checker("Database characterset",
            function ()
            {
                sql = "SELECT VALUE FROM V$NLS_PARAMETERS WHERE PARAMETER = 'NLS_CHARACTERSET'";
                var oRs = Oracle.Execute(sql);
                var LANGUAGE = oRs("VALUE").Value;

                switch (LANGUAGE)
                {
                    case "WE8MSWIN1252":
                    case "WE8ISO8859P15":
                        txt = "Single-byte characterset detected";
                        res = resultcodes.ok;
                        break;
                    case "WE8ISO8859P1":
                        txt = "Single-byte characterset detected\nbut it does not support the Euro character";
                        res = resultcodes.warning;
                        break;
                    case "AL32UTF8":
                    case "AL32UTF16":
                    case "AL32UTF32":
                        txt = "Multi-byte characterset detected\nThis is not supported by FACILITOR";
                        res = resultcodes.error;
                        break;
                    default: txt = "Unknown characterset detected";
                        res = resultcodes.warning;
                        break;
                }
                return { result: res, message: txt, info: LANGUAGE };
            }
        )

checker("NLS_TERRITORY",
            function ()
            {
                // Let op: fac-verify.asp heeft een 'eigen' Oracle object, niet die uit m_connections.inc
                // de BEGIN fac.initsession('NL'); END; is er dan ook nog niet over heen geweest.
                // In vroege testen was het desalniettemin met connection pooling al gecorrigeerd in het
                // de Oracle verbinding naar de database. Dat was echter met de oude MSDAORA driver, met
                // de ORAOLEDB driver heb ik het (gelukkig) niet gezien. Was eigenlijk een bug denk ik.
                var sql = "SELECT TO_NUMBER (TO_CHAR (TO_DATE('01-01-2018', 'DD-MM-YYYY'), 'D')) eikdag FROM DUAL";
                var oRs = Oracle.Execute(sql);
                var eikdag = oRs("eikdag").Value;
                if (eikdag == 2)
                    return { result: resultcodes.ok, message: "January 1st 2018 is a monday<br><span style='font-size: 9px'>This may have been corrected by connection pooling when FACILITOR has been started before.</span>", info: eikdag }
                else
                    return { result: resultcodes.ok, message: "January 1st 2018 is not a monday<br>(since DB25 not problematic)", info: eikdag }
            }
        )

checker("Guessing Webserver NLS_LANG language setting"
        +"<br><span style='font-size:10px'>Check registry HKLM\\SOFTWARE\\ORACLE\\xxx for NLS_LANG to be sure</span>",
            function ()
            {
                var sql = "SELECT UNISTR('X\\20acX') EURO FROM DUAL"; // Euro symbool: X€X
                var oRs = Oracle.Execute(sql);
                var str = String(oRs("EURO").Value);

                switch (str.charCodeAt(1))
                {
                    case 0x3F  : NLS_LANG = "US7ASCII"; break; // Waarschijnlijk NLS_LANG niet gezet
                    case 0xA4  : NLS_LANG = "WE8ISO8859P15"; break; // Eigenlijk fout!!!
                    case 0xBF  : NLS_LANG = "WE8ISO8859P1"; break; // omgekeerd vraagteken, eigenlijk fout!!!
                    case 0x20AC: NLS_LANG = "WE8MSWIN1252"; break; // Enige echt goede
                    // UTF8 lijkt terug te komen als 3 'karakters': 0xe2, 0x201a, 0xac terwijl het 0xE2 0x82 0xAC is?
                    case 0xE2  : if (str.length == 5 && str.charCodeAt(2)==0x201a && str.charCodeAt(3) == 0xAC)
                                 {  NLS_LANG = "AL32UTF8";
                                    break;
                                 }
                    default: NLS_LANG = "UNKNOWN: " + str.charCodeAt(1);
                };

                // Ik betwijfel of 28605 en 65001 ooit voorkomen
                if (   (NLS_LANG=="WE8MSWIN1252" && Session.Codepage == 1252 )
                    || (NLS_LANG=="WE8ISO8859P15" && Session.Codepage == 28605 )
                    || (NLS_LANG=="AL32UTF8" && Session.Codepage == 65001 )
                   )
                {
                    res = resultcodes.ok;
                    txt = NLS_LANG + " matches codepage " + Session.Codepage;
                }
                else
                {
                     txt = NLS_LANG + " does not match codepage " + Session.Codepage;
                     res = resultcodes.warning;
                }

                return { result: res, message: txt, info: NLS_LANG };
            }
        )

checker("Database language",
            function ()
            {
                // SYS_CONTEXT ('userenv', 'LANG') ulang is niet zinvol zonder fac.init_session
                var sql = "SELECT fac_version_lang"
                        + "  FROM fac_version";
                var oRs = Oracle.Execute(sql);
                var res = resultcodes.ok;
                var lang = oRs("fac_version_lang").Value;
                var txt = "Database native language is " + lang;
                oRs.Close();

                return { result: res, message: txt, info: lang };
            }
        )

checker("CREATE VIEW privileges",
            function ()
            {
                var sql = "SELECT PRIVILEGE FROM SESSION_PRIVS WHERE PRIVILEGE = 'CREATE VIEW'";
                var oRs = Oracle.Execute(sql);
                if (!oRs.Eof)
                {
                    res = resultcodes.ok;
                    txt = "Has CREATE VIEW";
                }
                else
                {
                     txt = "No CREATE VIEW privileges, can be a problem with upgrades";
                     res = resultcodes.warning;
                }
                oRs.Close();

                return { result: res, message: txt };
            }
        )
//------------------------------------------------------------------------------
__GROUP = "Installed Components";
checker("SLNKDWF Installed",
            function ()
            {
                try
                {
                    oSLNKDWF = new ActiveXObject("SLNKDWF.About");
                }
                catch(e)
                {
                    return { result: resultcodes.warning,
                            message: e.description +  "<br>SLNKDWF.DLL not installed. FACILITOR Graphics not available"};
                }
                var txt ="DLLPath: " + oSLNKDWF.DLLPath
                        + "<br>Buildtime: " + oSLNKDWF.Buildtime
                        + "<br>UserContext: " + oSLNKDWF.UserContext
                        + "<br>VersionMajor: " + oSLNKDWF.VersionMajor
                        + "<br>VersionMinor: " + oSLNKDWF.VersionMinor
                        + "<br>VersionString: " + oSLNKDWF.VersionString
                        + "<br>usTimer: " + oSLNKDWF.usTimer
                        +   " (" + (oSLNKDWF.usTimer/1e6/60/60/24).toFixed(2) + " days uptime)";

                if (oSLNKDWF.VersionMajor < 4 || (oSLNKDWF.VersionMajor == 4 && oSLNKDWF.VersionMinor < 16))
                {
                    txt += "<p><span style='font-style:normal'>Old version detected. Required version 4.16</p>";
                    return { result: resultcodes.error, message: txt, info: oSLNKDWF.VersionString };
                }

                return { result: resultcodes.ok, message: txt, info: oSLNKDWF.VersionString };
            }
        )

checker("PBKDF2 timing (for setting S('prs_password_hash_factor'))",
            function ()
            {
                try
                {
                    var oSLNKDWF = new ActiveXObject("SLNKDWF.About");
                }
                catch(e)
                {
                    return { result: resultcodes.warning,
                             message: e.description +  "<br>SLNKDWF.DLL not installed. PBKDF2 password hashing not available"};
                }
                var oCrypto = new ActiveXObject("SLNKDWF.Crypto");
                var workfactor = 12;
                var message = [];
                var res = { result: resultcodes.ok };
                while (1)
                {
                    var usStart = oSLNKDWF.usTimer;
                    var is_hash = oCrypto.hex_pbkdf2("password", "salt", Math.pow(2, workfactor), 20);
                    var current = "";
                    var tm = ((oSLNKDWF.usTimer - usStart)/1000).toFixed(0);
                    if (this.S && this.S("prs_password_hash_factor") == workfactor)
                    {
                        current = " (current)"
                        res.info = "Current: " + workfactor;
                        if (tm < 100)
                        {
                            res.result = resultcodes.warning;
                            current = " (current, too fast)"
                        }
                    }
                    if (tm > 50)
                    {
                        message.push("Calculating with hash_factor {0} ({1}) took {2} ms{3}".format(workfactor, Math.pow(2, workfactor), tm, current));
                    }
                    if (tm > 500)
                        break;
                    workfactor ++;
                }
                res.message = message.join("<br>");
                return res;
            }
        )

checker("MSXML2 6.0 Installed",
            function ()
            {
                try
                {
                    oDOM = new ActiveXObject("MSXML2.DOMDocument.6.0");
                }
                catch(e)
                {
                    return { result: resultcodes.error,
                            message: e.description +  "<br>MSXML2 6.0 not available. Reports will be unavailable"};
                }

                var txt = "Ok";

                return { result: resultcodes.ok, message: txt };
            }
        )

checker("FCLTISAPI",
            function ()
            {
                var ISAPI = Request.Servervariables("HTTP_FCLT_VERSION");
                if (ISAPI.Count)
                {
                    return { result: resultcodes.ok,
                             message: "FCLTISAPI version  " + ISAPI,
                             info: String(ISAPI)
                           };
                }
                return { result: resultcodes.warning,
                         message: "FCLTISAPI not installed"
                       };
            }
        )

checker("Ionic's Isapi Rewrite Filter (obsolete 2015.2)",
            function ()
            {
                var REWR = Request.Servervariables("HTTP_FCLT_REWRITER");
                if (REWR.Count)
                {
                    return { result: resultcodes.ok,
                             message: "REWRITER version  " + REWR,
                             info: String(REWR)
                           };
                }
                return { result: resultcodes.ok, // we gebruiken hem niet meer
                         message: "IIRF REWRITER not installed"
                       };
            }
        )

var mailto = Request.Form("mailto").Count>0?Request.Form("mailto")(1):"to@example.com";
var mailfrom = Request.Form("mailfrom").Count>0?Request.Form("mailfrom")(1):"from@example.com";
var mailreply = Request.Form("replyto").Count>0?Request.Form("replyto")(1):"reply@example.com";
var mailrelay = Request.Form("mailrelay").Count>0?Request.Form("mailrelay")(1):"";

if (Request.Form("mailto").Count > 0)
checker("E-mail sending",
            function ()
            {
                var txt = "FACILITOR test-email sent at: " + toDateTimeString(new Date())
                try
                {
                    var sch = "http://schemas.microsoft.com/cdo/configuration/";
                	var iConf =  new ActiveXObject("CDO.Configuration");
                	if (mailrelay)
                	{
                	    iConf.Fields(sch+"sendusing") = 2;
                	    iConf.Fields(sch+"smtpserver") = mailrelay;
                	}
                    //	iConf.Fields(sch+"smtpserverport") = 25;
                    //	iConf.Fields(sch+"smtpauthenticate") = 0; // anoniem
                	iConf.Fields.Update();

                    var objMail = new ActiveXObject("CDO.Message");
                    objMail.Configuration = iConf;

                    // set fields
                    objMail.To = mailto;
                    objMail.From = mailfrom;
                    objMail.ReplyTo = mailreply;
                    objMail.Subject = txt;
                    objMail.HtmlBody = txt + "<br>To: " + mailto+ "<br>From: " + mailfrom + "<br>Reply: " + mailreply + "<br>Relay: " + mailrelay;
                    objMail.Send();
                }
                catch(e)
                {
                    return { result: resultcodes.error,
                             message: "Subject: " + txt + "<br>Error: " + e.description +  "<br>E-mail sending failed"};
                }

                txt = txt + "<script>FcltMgr.alert('" + txt + "\\nSent to: " + Request.Form("mailto")(1) + "');</script>"
                return { result: resultcodes.ok, message: txt };
            }
        )

checker("Immediate E-mail test",
            function ()
            {
                try
                {
                    oDOM = new ActiveXObject("CDO.Message");
                }
                catch(e)
                {
                    return { result: resultcodes.error,
                            message: e.description +  "<br>CDO not available. E-mail unavailable"};
                }

                txt  = "<a name='mailform'></a>"
                     + "<form method='post' action='#mailform'>"
                     + "      <table>"
                     + "      <tr><td><label>To:</label></td><td><input name='mailto' type='text' style='width:250px' value='"+mailto+"'>"
                     + "      <input type='submit' value='Send'></td></tr>"
                     + "      <tr><td><label>From:</label></td><td><input name='mailfrom' type='text' style='width:250px' value='"+mailfrom+"'></td></tr>"
                     + "      <tr><td><label>Reply:</label></td><td><input name='replyto' type='text' style='width:250px' value='"+mailreply+"'></td></tr>"
                     + "      <tr><td><label>Relay&nbsp;server:</label></td><td><input name='mailrelay' type='text' style='width:250px' value='"+mailrelay+"'>&nbsp;(empty&nbsp;for&nbsp;local&nbsp;IIS&nbsp;SMTP)</td></tr>"
                     + "      </table>"
                     + "</form>";

                return { result: resultcodes.ok, message: txt };
            }
        )

//------------------------------------------------------------------------------
__GROUP = "Miscellaneous";
checker("Clocks", // deze zo laat mogelijk zodat als straks de clientside clock er bij wordt gezocht
                  // deze zo goed mogelijk in de buurt zit.
            function ()
            {
                var sql = "SELECT SYSTIMESTAMP FROM DUAL";
                var oRs = Oracle.Execute(sql);
                var sysdate = new Date(oRs(0));
                var webdate = new Date();
                oRs.Close();
                txt = "Oracle: " + toDateTimeString(sysdate) + ".<small>" + padout(Math.round(sysdate.getMilliseconds()/10)) + "</small><br>"
                    + "Webserver: " + toDateTimeString(webdate) + ".<small>" + padout(Math.round(webdate.getMilliseconds()/10)) + "</small><br>"
                    + "<small>Difference ora-web: " + (Math.abs(sysdate - webdate) / 1000).toFixed(2) + "s</small><br>"
                    + "Client: <span id='clienttime'></span>";

                var res = resultcodes.ok;
                if (Math.abs(sysdate - webdate) > 5*1000) // Meer dan 5 seconde? Waarschijnlijk geen synchronisatie
                    res = resultcodes.warning;
                if (Math.abs(sysdate - webdate) > 30*1000) // Meer dan 30 seconde? Risicovol
                    res = resultcodes.error;

                return { result: res, message: txt };
            }
        )

//New MSXML2.XSLTemplate40
// XML Processor  var source = Server.CreateObject("MSXML2.DOMDocument.4.0");
// Let op: geconstateerd bij installatie DENB toen MSXML.MSI nog niet was geinstalleerd:
//         rapportages deden het toen al wel maar Putorders.exe nog niet!!

__GROUP = "Client Information";
checker("Localstorage Collection",
            function ()
            {
                return {
                         result: resultcodes.ok,
                         message: "<span id='checkLocalStorage'></span>",
                         id: "resultlocalStorage"
                       }
            }
        );

__GROUP = "ASP Collections";
checker("Application Variable Collection",
            function ()
            {
                return { result: resultcodes.ok,
                         message: DumpCollection(Application.Contents)
                       }
            },
            true // toggler
        )

checker("Session Collection",
            function ()
            {
                return { result: resultcodes.ok,
                         message: DumpCollection(Session.Contents)
                       }
            },
            true // toggler
        )

checker("Form Collection",
            function ()
            {
                return { result: resultcodes.ok,
                         message: DumpCollection(Request.Form)
                       }
            },
            true // toggler
        )

checker("Servervariables Collection",
            function ()
            {
                return { result: resultcodes.ok,
                         message: DumpCollection(Request.ServerVariables)
                       }
            },
            true // toggler
        )

checker("Cookies Collection",
            function ()
            {
                return { result: resultcodes.ok,
                         message: DumpCollection(Request.Cookies)
                       }
            },
            true // toggler
        );

checker("ClientCertificate Collection",
            function ()
            {
                return { result: resultcodes.ok,
                         message: DumpCollection(Request.ClientCertificate)
                       }
            },
            true // toggler
        );

//DumpCollection(Request.QueryString,       "QueryString Collection");

%></table><%

function DumpCollection(pCollection, title)
{
    var i;
    var lines = [];
    for (i=1; i<=pCollection.Count; i++)
    {
        var line = "<tr><td>" + pCollection.key(i); // + " type: " + typeof pCollection(i) + " cons: " + pCollection(i).constructor
        if (typeof pCollection(i) != "object" || pCollection(i) === null || !pCollection.HasKeys)
        {
            line += "</td><td>" + Server.HTMLEncode(String(pCollection(i)))
        }
        else
        {
            if (pCollection(i).Count>1)
                line += "("+pCollection(i).Count+"x)"
            line += "</td><td>"
            var j;
            for (j=1; j<=pCollection(i).Count; j++)
            {
                if (j>1)
                    line += "<br>"
                if (typeof pCollection(i).key == "object")
                    line += pCollection.item(i).key(j) + ": ";
                line += Server.HTMLEncode(String(pCollection.item(i)(j)));
            }
        }
        line += "</td></tr>";
        lines.push(line);
    }
    var sLog = "<table class='inside'>" + lines.join("\n") + "</table>";
    return (sLog);
}

// Dit bestand moet geheel zelfvoorzienend worden (indien aangeroepen vanuit fac_verify.asp.inc)
// dus sommige handige serverside functies maar herhalen
function padout(number) { return (number < 10) ? "0" + number : number; }
// Noot: altijd jaar vooraan, goed voor logfiles, niet bedoeld voor presentatie
function toDateString(jsDate)
{
    return padout(jsDate.getFullYear())  + "-" + padout(jsDate.getMonth() + 1) + "-" + padout(jsDate.getDate());
}
function toDateTimeString(jsDate)
{
    return toDateString(jsDate) + " "
         + padout(jsDate.getHours()) + ":" + padout(jsDate.getMinutes())   + ":" + padout(jsDate.getSeconds());
}

//DumpCollection(Oracle.Properties,         "All Oracle properties");
%>
<script language="VBScript" runat="Server">

Function GetVBScriptEngineTxt
    Dim s
    s = ""
    s = "ScriptEngine: " & ScriptEngine
    s = s & "<br>ScriptEngineMajorVersion: " & ScriptEngineMajorVersion
    s = s & "<br>ScriptEngineMinorVersion: " & ScriptEngineMinorVersion
    s = s & "<br>ScriptEngineBuildVersion: " & ScriptEngineBuildVersion
    GetVBScriptEngineTxt = s
End Function

Function GetVBScriptEngineInfo
    Dim s
    s = "" ' Build string with necessary info.
    s = s & ScriptEngineMajorVersion & "."
    s = s & ScriptEngineMinorVersion & "."
    s = s & ScriptEngineBuildVersion
    GetVBScriptEngineInfo = s
End Function
</script>

</body>
</html>