<% /*
    $Revision$
    $Id$

    File:           appl/fac/fac_verify.inc
    Description:    Verify FACILITOR installation
    Parameters:     ?XXXX optional customerId
    Context:        Manual call to check installation
    Note:           Facilitor verwacht rechten op vele packages. Onder andere (maar niet beperkt tot)
                    DBMS_RANDOM, DBMS_XMLGEN, DBMS_OUTPUT, DBMS_UTILITY, DBMS_SQL,
                    DBMS_LOB, DBMS_ASSERT, DBMS_SESSION, DBMS_OBFUSCATION_TOOLKIT,
                    UTL_RAW, UTL_MATCH
                    Dat testen we verder hier niet: als je er geen rechten op hebt is
                    er van alles invalid in de database en daar ben je al wel eerder op gewezen

                    Enige uitzondering zijn misschien utl_match.edit_distance_similarity
                    en utl_match.jaro_winkler welke niet in de database gebruikt worden, alleen asp

*/ %>
<%
    var starttime = new Date();
    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)
{
    var fname = "FACILITOR_verify_{0}_{1}_{2}.html".format(custID,  toDateString(new Date()), String(Request.ServerVariables("SERVER_NAME")));
    Response.clear;
    Response.contenttype = "application/octet-stream";
    Response.AddHeader("content-disposition","attachment; filename=" + fname);
}

// rooturl bepalen
var pad = String(Request.ServerVariables("PATH_INFO"));
var lowerpad = pad.toLowerCase();
var i1 = lowerpad.indexOf("/cust/");
if (i1 < 0)
    i1 = lowerpad.indexOf("/appl/");
if (i1 < 0)
    i1 = lowerpad.indexOf("/default.asp"); // API's
/* global */ rooturl = pad.substr(0, i1);

%>
<!DOCTYPE html>
<head>
     <meta http-equiv="Content-Type" content="text/html;charset=windows-1252">
     <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
     <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<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()
    {
        var dt = (new Date()).toISOString().replace(/[TZ]/g, " ");
        document.getElementById('clienttime').appendChild( document.createTextNode(dt) );

        writeLocalStorage();

        // 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 = "<br>500_error existingResponse found in web.config";
                        document.getElementById('result500b').innerHTML = 'Ok';
                        document.getElementById('result500b').style.color = 'green';
                    }
                    else
                    {
                        txt = "<br>500_error existingResponse not OK."
                            + "<br>In <%=safe.jsstring(Server.MapPath("../.."))%>\\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
                {
                    document.getElementById('resultAPI2P').innerHTML = 'Warning';
                    document.getElementById('resultAPI2P').style.color = 'yellow';
                    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
                {
                    document.getElementById('resultAPI2D').innerHTML = 'Warning';
                    document.getElementById('resultAPI2D').style.color = 'yellow';
                    txt = xhr3.status + ": " + xhr3.statusText;
                }
                document.getElementById('checkAPI2D').appendChild( document.createTextNode(txt) );
            }
        }
        xhr3.send(null);

        // Check for PATCH for API2
        var xhr3b = new XMLHttpRequest();
        xhr3b.open('PATCH', 'fac_verify_test.asp?checkPATCH=1');
        xhr3b.onreadystatechange = function()
        {
            var txt;
            // Ready state 4 means the request is done
            if (xhr3b.readyState === 4)
            {
                if (xhr3b.status == 200)
                {
                    txt = xhr3b.responseText;
                    document.getElementById('resultAPI2PA').innerHTML = 'Ok';
                    document.getElementById('resultAPI2PA').style.color = 'green';
                }
                else
                {
                    document.getElementById('resultAPI2PA').innerHTML = 'Warning';
                    document.getElementById('resultAPI2PA').style.color = 'yellow';
                    txt = xhr3b.status + ": " + xhr3b.statusText;
                }
                document.getElementById('checkAPI2PA').appendChild( document.createTextNode(txt) );
            }
        }
        xhr3b.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* being 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 || xhr5.status == 400) // It should not be found when .NET not installed or 'Bad request'
                {
                    txt = "Calling an aspx fails (http status " + xhr5.status + "). Good!";
                    document.getElementById('resultASPX').innerHTML = 'Ok';
                    document.getElementById('resultASPX').style.color = 'green';
                }
                else
                {
                    txt = "Calling an aspx succeeds (http status " + xhr5.status + "). 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);

        // Check for TEMP folder *not* being readable
        var xhr6 = new XMLHttpRequest();
<%
        var logfilename = "log_" + customerId + ".html";
        var filePath = shared.tempFolder();
%>
        var logurl = '../../TEMP/<%=customerId%>/log_<%=customerId%>.html'
        xhr6.open('GET', logurl);
        xhr6.onreadystatechange = function()
        {
            var txt;
            // Ready state 4 means the request is done
            if (xhr6.readyState === 4)
            {
                if (xhr6.status == 404 || xhr6.status == 400) // It should not be found or 'Bad Request'
                {
                    txt = "<br>Getting " + logurl + " file fails (http status " + xhr6.status + "). Good!";
                    document.getElementById('resultTEMP').innerHTML = 'Ok';
                    document.getElementById('resultTEMP').style.color = 'green';
                }
                else
                {
                    txt = "<br>Getting " + logurl + " file succeeds (http status " + xhr6.status + "). Not good!";
                    txt += "<br><%=safe.jsstring(Server.MapPath("../TEMP/"))%>\\web.config not working properly?"
                         + "<br>Is RequestFiltering installed in IIS?";
                    document.getElementById('resultTEMP').innerHTML = 'Error';
                    document.getElementById('resultTEMP').style.backgroundColor = 'red';
                    document.getElementById('resultTEMP').style.color = 'black';
                }
                var span = document.createElement("span");
                span.innerHTML = txt;
                document.getElementById('checkTEMP').appendChild( span );
            }
        }
        xhr6.send(null);
    }
    function reload()
    {
        var hrefBase = window.location.href;
        window.location.href = hrefBase;
    }
    function clearlog()
    {
        if (confirm("Clear logfile?"))
        {
            window.open("../shared/wislog.asp");
        }
    }
    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 div = document.createElement("div");
        div.innerHTML = output;
        document.getElementById('checkLocalStorage').appendChild( div );
    }
</script>
</head>
<%
if (Request.Form("saveas").count > 0)
{
    Response.Write("<body>");
    var notinsavedscript = "(not in saved/printed file)";
}
else
{
    Response.Write("<body onload='onload()'>");
    var notinsavedscript = "";
}

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;
    }

    Response.Write("\n<tr><td>");
    Response.Write(description);
    //Response.Write(toDateTimeString(new Date()));
    try
    {
        var res = fnCheck();
    }
    catch (e)
    {
        Response.Write("<br/><em>"+e.description+"</em>");
        Response.Write("<td style='color:red'>Internal error</td></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(" <button onClick='toggle(\"div"+cnt_id+"\")'>" + count + " lines</button>");
    }

    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+">Inform</td>");
            break;
        default:
            Response.Write("<td style='color:red'"+idtxt+">Unknown</td>");
            break;
    }
    Response.Write("</tr>");
}

function testfolder(relpath, mustbethere)
{
    return testfolderA(Server.MapPath(relpath));
}

function testfolderA(abspath, mustbethere)
{
    var present = true;
    checker(abspath + " folder",
            function ()
            {
                if (fso.FolderExists(abspath))
                {
                    return { result: resultcodes.ok,
                             message: "Owner: " + getFileOwner( abspath )
                           };
                }
                present = false;
                if (mustbethere)
                    return { result: resultcodes.error,
                             message: "Folder not found at: " + abspath};
                return { result: resultcodes.ok,
                         message: "Folder not found at: " + abspath + "<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 = abspath + "/$Verify$.tmp";
    checker(abspath + " writeable using FileSystemObject",
        function ()
        {
            try
            {
                var objText = fso.OpenTextFile(testname, 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<br>Owner: " + getFileOwner( testname ) };
        }
    )

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

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

    checker(abspath + " writeable using ADODB.Stream",
        function ()
        {
            try
            {
                var BinaryStream = Server.CreateObject("ADODB.Stream");
                BinaryStream.Type = 1; // adTypeBinary
                BinaryStream.Open();
                //BinaryStream.Write(finfo.data);
                BinaryStream.SaveToFile(safe.UNC(testname), 2); // adSaveCreateOverWrite
                //fso.DeleteFile(testname, false);
            }
            catch(e)
            {
                return { result: resultcodes.error,
                        message: e.description +  "<br>Error writing to " + testname};
            }

            return { result: resultcodes.ok, message: testname + " could be written to<br>Owner: " + getFileOwner( testname ) };
        }
    )

    checker(testname + " deleteable (ADODB.Stream)",
        function ()
        {
            try
            {
                var objText = fso.DeleteFile(testname, 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> (in <span id='duration'></span>s)<br>
               Software folder: <%= Server.HTMLEncode(Server.MapPath("../..")) %><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>
               <a href='fac_verify_bestpractises.asp'">Click</a> to verify best practices<br>
               </td>
               <td valign='top'>
                <form method='post' target='_new'>
                    <input name='saveas' type='hidden'><input type='submit' value='Save as'>
                    <input type='button' onclick='this.value="Please wait...";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("HTTP Request protocol",
            function ()
            {
                var proto = Request.ServerVariables("SERVER_PORT") == "443"? "https" : "http";
                var url = proto + "://" + Request.ServerVariables("SERVER_NAME") + rooturl;
                if (proto == 'http')
                {
                    return { result: resultcodes.warning,
                             message: url + "<br>Request protocol was not http<strong>s</strong>, this could be more secure",
                             info: proto
                           }
                }
                var msg = "<br>Certificate subject: {0}".format(Request.ServerVariables("HTTPS_SERVER_SUBJECT"))
                        + "<br>Certificate issuer: {0}".format(Request.ServerVariables("HTTPS_SERVER_ISSUER"));
                if (this.settings && S("auto_https") == 0)
                {
                    return { result: resultcodes.warning,
                             message: url + "<br>Request protocol was http<strong>s</strong>, perhaps enable auto_https?" + msg,
                             info: proto
                           }
                }
                return { result: resultcodes.ok,
                         message: url + "<br>Request protocol was http<strong>s</strong>, and auto_https is on" + msg,
                         info: proto
                       }
            }
        )

var subject = String(Request.ServerVariables("HTTPS_SERVER_SUBJECT")).split(", ");
// subject = "CN=areo.facilitor.de" of "C=NL, S=Utrecht, L=Utrecht, O=ProRail B.V., CN=fmis.testprorail.nl"
var CN="";
for (var i=0; i < subject.length; i++)
{
    if (subject[i].substr(0, 3) == 'CN=')
        CN = subject[i];
}
if (CN)
checker("Certificate checker for " + CN,
            function ()
            {
                try
                {
                    // CertStore is no license required, geen lockcode nodig
                    var oChilglob = new ActiveXObject("Chilkat_9_5_0.Global"); // voor version
                    //var txt ="Version: " + oChilglob.Version;
                    //var success = oChilglob.UnlockBundle(S("puo_chilkat_secret"));
                    //if (success != 1)
                    //    result.certificate = "Chilkat_9_5_0 Unlock failed:\n" + oChilglob.LastErrorText;
                    //else
                    {
                        var certStore = new ActiveXObject("Chilkat_9_5_0.CertStore");
                    }
                }
                catch(e)
                {
                    return { result: resultcodes.ok,
                             message: "Chilkat_9_5_0 not installed. Certificate not checked<br>" + e.description
                           }
                }

                certStore.AvoidWindowsPkAccess = 1;
                var commonname = String(CN).split("=")[1]; // "CN=*.facilitor-test.nl"
                //var success = cert.LoadByCommonName(commonname); // werkt wel voor Other People maar niet voor My of Webhosting
                //success = certStore.OpenLocalSystemStore(1); // doet altijd My
                var msg = "Looking for certificate in Local Computer/Web Hosting";
                success = certStore.OpenWindowsStore("LocalMachine", "WebHosting", 1);
                if (success == 0)
                {
                    msg += "<br>Failed to open certificate Store!<br>" + certStore.LastErrorHtml;
                    return { result: resultcodes.warning,
                             message: msg + "<br>Failed to open certificate Store!<br>" + certStore.LastErrorHtml
                           }
                }

                var cert = certStore.FindCertBySubjectCN(commonname);
                if (!cert)
                {
                    return { result: resultcodes.warning,
                             message: msg + "<br>Failed to find certificate for " + commonname
                                          + "<br>Note: Local Computer/Personal has not been checked!"
                           }
                }
                //msg += "<br>ValidFrom " + cert.ValidFromStr + "<br>ValidTo " + cert.ValidToStr;
                var dtFrom = new Date(cert.ValidFromStr);
                var dtTo = new Date(cert.ValidToStr);
                msg += "<br>Found and valid from " + toDateTimeString(dtFrom) + " to " + toDateTimeString(dtTo);
                // pas vanaf 9.5.0.85, wij hebben nog 9.5.0.83 in gebruik?
                // msg += "<br><pre>" + Server.HTMLEncode(cert.SubjectAlternativeName) + "</pre>";

                var days = Math.floor((dtTo.getTime() - new Date().getTime()) / 1000 / 3600 / 24);

                return { result: days > 28?resultcodes.ok:days > 7?resultcodes.warning:resultcodes.error,
                         message: msg,
                         info: String(days) + " days to go"
                       }
            }
        )

checker("IP addresses",
            function ()
            {
                var local = Request.ServerVariables("LOCAL_ADDR");
                var remote = Request.ServerVariables("REMOTE_ADDR");
                return { result: resultcodes.ok,
                         message: "Server ip: " + local + "<br>Client ip: " + remote,
                         info: String(remote).indexOf(":") > -1 ? "IPv6": "IPv4"
                       };
            }
        )

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 ()
            {
                try
                {
                    var WshShell = new ActiveXObject("WScript.Shell");
                    var pool_id = WshShell.ExpandEnvironmentStrings("%APP_POOL_ID%");
                }
                catch (e)
                {
                    pool_id = "Error: " + e.description;
                }
                var res = resultcodes.ok;
                var message = "Name: " + pool_id + "<br>";
                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("Site ID",
            function ()
            {
                return { result: resultcodes.ok,
                         message: "IIS Site ID: " + Request.ServerVariables("INSTANCE_ID"),
                         info: Request.ServerVariables("INSTANCE_ID")
                       };
            }
        )

if (Request.Form("mailto").Count == 0) // te traag
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)
                       };
            }
        )

function driveInfo(drvPath)
{
    res = resultcodes.unknown;
    try
    {
        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("Free diskspace Windows",
            function ()
            {
                var drvPath = "C:";
                return driveInfo(drvPath);
            }
        )
checker("Free diskspace website",
            function ()
            {
                var drvPath = Server.MapPath("../../");
                return driveInfo(drvPath);
            }
        )

checker("Integrated Single Signon/Windows authentication",
            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, info: notinsavedscript, message: txt, id: "result500" };
            }
        )

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

//------------------------------------------------------------------------------
__GROUP = "Classic ASP";
checker("Javascript",
            function ()
            {
                var txt = "ScriptEngine: " + ScriptEngine();
                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.unknown;
                return { result: res, info: notinsavedscript, message: txt, id: "resultASPX" };
            }
        )

checker("TEMP folder should not be readable by HTTP.",
            function ()
            {
                txt = "Checking TEMP folder: <span id='checkTEMP'></span>";
                res = resultcodes.unknown;
                return { result: res, info: notinsavedscript, message: txt, id: "resultTEMP" };
            }
        )

__GROUP = "Facilitor software";
//------------------------------------------------------------------------------
testfolder(rooturl + "/cust", true); // mustbethere
testfolder(rooturl + "/temp", true); // mustbethere

if (typeof __DoLog == "function")
checker("Logfile writable",
            function ()
            {
                var logfilename = "log_" + customerId + ".html";
                var filePath = shared.tempFolder();
                var fullpath = filePath + "/" + logfilename;

                var result = { result: resultcodes.ok,
                               message: fullpath
                                      + "<br><a href='fac_StreamFile.asp?logger=0' target='_new'>View</a> the logfile"
                                      + " <a href='fac_StreamFile.asp?logger=0&zipfile=1' target='_new'>zipped</a>"
                                      + " or <a href='javascript:clearlog()'>Clear</a> it."
                             };
                if (!__DoLog("Fac_verify test {0}".format(toDateTimeString(new Date())), "#0ff" ))
                {
                    result.result = resultcodes.error;
                    result.message += "<br>" + safe.html(__LogError);
                }
                if (fso.FileExists(fullpath))
                {
                    var f = fso.getFile(fullpath);
                    var dt = new Date(f.DateLastModified);
                    var sz = f.Size;
                    result.info = "{0} ({1}kB)".format(toDateTimeString(dt), Math.round(sz/1024));
                    result.message += "<br>Owner: " + getFileOwner( fullpath );
                }
                return result;
            }
        )

// 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(rooturl + "/cust/"+custID, true);
custFolder = Server.MapPath(rooturl + "/cust/"+custID);

testfile = Server.MapPath(rooturl + "/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";
if (this.settings)
{
    checker("Context",
                function ()
                {
                    return {
                                message: "flexfilespath: {0}<br>UserContext: {1}".format(S("flexfilespath"), getUserContext()),
                                result: resultcodes.ok
                           };
                }
            )

    checker("Diskspace",
                function ()
                {
                    return driveInfo(S("flexfilespath"));
                }
            )

    testfolderA(S("flexfilespath"), false); // neednotbethere
    testfolderA(S("flexfilespath") + "/cad", false); // neednotbethere

    checker("Flexfiles outside webroot",
                function ()
                {
                    var webroot = Server.MapPath("../..");
                    if (S("flexfilespath").toUpperCase().indexOf(webroot.toUpperCase()  + "\\") == 0)
                        return { result: resultcodes.error, message: "{0} should be outside {1}".format(S("flexfilespath"), webroot) };
                    else
                        return { result: resultcodes.ok, message: "{0} is outside {1}".format(S("flexfilespath"), webroot) };
                }
            )
}

testfilecust = Server.MapPath(rooturl + "/cust/"+custID+"/xsl/cust.xsl");
testfileXXXX = Server.MapPath(rooturl + "/cust/"+custID+"/xsl/" + custID + ".xsl");
checker(testfileXXXX + " file available (optional)",
            function ()
            {
                if (fso.FileExists(testfileXXXX))
                {
                    var f = fso.getFile(testfileXXXX);
                    var dt = new Date(f.DateLastModified);
                    var sz = f.Size;
                    var result = { result: resultcodes.ok, message: "Owner: " + getFileOwner(testfileXXXX) };
                    result.info = "{0} ({1}kB)".format(toDateTimeString(dt), Math.round(sz/1024));
                    return result;
                }
                if (fso.FileExists(testfilecust))
                    return { result: resultcodes.error, message: testfilecust + " must be renamed to " + custID + ".xsl"};
                return { result: resultcodes.warning, message: "File not found: " + testfileXXXX};
            }
        )

if (this.settings)
{
    checker("Hook functions defined",
            function ()
            {
                if (!S("custfunctionspath"))
                    return { result: resultcodes.ok, message: "No hook function defined in setting S(\"custfunctionspath\")" };
                var hookfile = Server.MapPath(rooturl + "/" + S("custfunctionspath"));
                if (!fso.FileExists(hookfile))
                    return { result: resultcodes.error, message: "Hookfile {0} configured but not found".format(hookfile) };
                try {
                    var hook = GetObject("script:" + hookfile);
                }
                catch (e)
                {
                    return { result: resultcodes.error,
                             message: "Error loading hookfile {0}:<br>{1}.".format(hookfile, safe.html(e.description))};
                }
                hook = null; // zorg dat de GC het object kan opruimen.
                var f = fso.getFile(hookfile);
                var dt = new Date(f.DateLastModified);
                return { result: resultcodes.ok,
                         message: "Hookfile {0} found and loadable".format(hookfile),
                         info: toDateTimeString(dt) };
            }
        )
}

__GROUP = "Settings";

var puostring = "puo_forcemailaddress,puo_forceapppushtoken,puo_forcefromaddress,puo_forceorderaddress,puo_forcemobile,puo_fclt_web_url";
if (this.settings) // (deze test werkt niet vanuit verify.asp.inc)
{
    checker("Environment custenv DEFAULTs",
            function ()
            {
                var found = [];
                var info = null;
                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 res = resultcodes.ok;
                var txt = [];
                for (var i = 0; i < overrule_setting.length; i++)
                {
                    var naam = overrule_setting[i].naam;
                    var sql = "SELECT fac_setting_type, "
                            + "       fac_setting_default"
                            + "  FROM fac_setting"
                            + " WHERE fac_setting_name = " + safe.quoted_sql(naam.toLowerCase());
                    var oRs = Oracle.Execute(sql);
                    var err = "";
                    if (oRs.EOF)
                    {
                        var err = "Not found in " + custID + " database!";
                    }
                    else
                    {
                        if (overrule_setting[i].type != oRs("fac_setting_type").Value)
                        {
                            var err = "Type mismatch. Database says: {1}".format(overrule_setting[i].type, oRs("fac_setting_type").Value);
                        }
                        if (overrule_setting[i].waarde != oRs("fac_setting_default").Value)
                        {
                            var err = "Default mismatch. Database says: {1}".format(overrule_setting[i].waarde, oRs("fac_setting_default").Value);
                        }
                    }
                    if (err)
                    {
                        var res = resultcodes.warning;
                        info = "Time to update the database or fix FAC_SET.SRC";
                    }

                    txt.push("<tr><td>" + naam + "</td><td>" + safe.html(overrule_setting[i].type) + "</td><td>" + safe.html(overrule_setting[i].waarde) + "</td><td><strong>" + safe.html(err) + "</strong></td></tr>");
                    oRs.Close();
                }
                return { result: res,
                         info: info,
                         message: found.join("<br>")
                                + "<table><tr><th>Name</th><th>Type</th><th>default</th><th></th></tr><tr><td>"
                                + txt.join("") + "</table>" };
            }
        )

    checker("Environment custenv SETTING overrules",
                function ()
                {
                    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.inform,
                                   info: "(ok for test environments)",
                                   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;
                }
            )
    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.inform,
                                   info: "(ok for test environments)",
                                   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;
                }
            )
}

function check_task(fac_version_field, descr, freq, maxago)
{
    checker(descr + " " + freq,
            function ()
            {
                var sql = "SELECT {0} FROM fac_version".format(fac_version_field);
                var oRs = Oracle.Execute(sql);
                var puo_date = oRs(fac_version_field).Value != null?new Date(oRs(fac_version_field).Value): null;
                oRs.Close();

                if (puo_date)
                {
                    var hoursago = Math.round((new Date().getTime() - puo_date.getTime()) / 100 / 60 / 60) / 10;
                    if (hoursago <= maxago)
                    {
                        var result = { result: resultcodes.ok,
                                       info: toDateTimeString(puo_date),
                                       message: "Last run: " +  toDateTimeString(puo_date)
                                                + "<br>This is {0} minutes ago".format(Math.round(hoursago * 60))
                                     };
                    }
                    else
                    {
                        var result = { result: resultcodes.warning,
                                       info: toDateTimeString(puo_date),
                                       message: "Last run: " +  toDateTimeString(puo_date)
                                                + "<br>This is {0} {1} ago, more than {2} hours".format(hoursago>48?Math.round(hoursago/2.4)/10:hoursago, hoursago>48?"days":"hours", maxago)
                                     };
                    }
                }
                else
                    result= { result: resultcodes.error,
                              message: descr + " has never been scheduled?"};

                return result;
            }
        )

}

//------------------------------------------------------------------------------
__GROUP = "Scheduled tasks";

if (this.S) // (deze test werkt niet vanuit verify.asp.inc)
{
    check_task("fac_version_putorders_date",
               "CUST\\{0}\\tasks\\putOrders.bat".format(customerId),
               "(every 5 minutes, sending notification e-mails and external orders)",
               1);
    check_task("fac_version_scheduler_date",
               "CUST\\{0}\\tasks\\gen_scheduler.bat".format(customerId),
               "(once a day, Facilitor scheduled tasks and other daily/weekly/monthly tasks)",
               24);
    check_task("fac_version_notify_date",
               "CUST\\{0}\\tasks\\gen_notify.bat".format(customerId),
               "(hourly, reminder notifications)",
               2);
    if (S("tsk_master_schema") == '*SELF*')
    {
        check_task("fac_version_tasker_date",
                "CUST\\{0}\\tasks\\gen_tasker.bat".format(customerId),
                "(permanent, starts putorders)",
                2);
    }
}

//------------------------------------------------------------------------------
__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.inform,
                                   info: "(ok for test environments)",
                                   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.error, message: "S('puo_fclt_web_url') is not set.<br>Scheduled reports and 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("External url (testing https://facilitor.nl)",
            function ()
            {
                var puo_web_url = "https://facilitor.nl/";
                var result = { result: resultcodes.ok, message: "Checking external url: " + puo_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.warning;
                        result.message += "<br>http_request response: " + http_request.status + " " + http_request.statusText;
                    }
                    else
                    {
                        result.message += "<br>" + http_request.status + " " + http_request.statusText;
                    }
                }
                catch(e)
                {
                    result.result = resultcodes.warning;
                    result.message += "<br>http_request error: " + e.description;
                    result.message += "<br>Is a firewall blocking traffic? Sending orders to external systems may fail.";
                }
                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 PUT method (updating)",
            function ()
            {
                txt = "Checking PUT: <span id='checkAPI2P'></span>";
                res = resultcodes.unknown;
                return { result: res, info: notinsavedscript, message: txt, id: "resultAPI2P" };
            }
        )

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

checker("API2 REST HTTP PATCH method (updating, SCIM only)",
            function ()
            {
                txt = "Checking PATCH: <span id='checkAPI2PA'></span>";
                res = resultcodes.unknown;
                return { result: res, info: notinsavedscript, message: txt, id: "resultAPI2PA" };
            }
        )

checker("500 error for API2",
            function ()
            {
                txt = "Checking 500-API handler: <span id='check500b'></span>";
                res = resultcodes.unknown;
                return { result: res, info: notinsavedscript, 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};
            }
        )

var orgOracle = this.Oracle; // Uit m_connections.inc?

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") };
            }
        )

if (orgOracle)
{
    Oracle = orgOracle.RealConnection;
    __GROUP = "Oracle server (m_connections.inc)";
}

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 version <strong>" + Server.HTMLEncode(server) + "</strong>",
            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 banner_full FROM v$version";
                var oRs = Oracle.Execute(sql);
                return { result: resultcodes.ok, message: oRs("banner_full").Value.replace(/\n/g, "<br>"), info: "" };
            }
        )


var sql = "SELECT SYS_CONTEXT('USERENV','SERVER_HOST') SERVER_HOST,"
        + "       SYS_CONTEXT('USERENV','SERVICE_NAME') SERVICE_NAME FROM DUAL";
var oRs = Oracle.Execute(sql);
var server =  String(oRs("SERVER_HOST").Value) + "/" + String(oRs("SERVICE_NAME").Value);
oRs.Close();

checker("Database context",
            function ()
            {
                var toCheck =
                    ['TERMINAL',
                    'LANGUAGE',
                    'SID',
                    'SESSIONID',
                    'INSTANCE',
                    'ENTRYID',
                    'ISDBA',

                    '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',
                    'SERVICE_NAME',
                    'OS_USER',
                    // 'EXTERNAL_NAME', // < Oracle 23ai
                    'AUTHENTICATED_IDENTITY',
                    'ENTERPRISE_IDENTITY',
                    'IP_ADDRESS',
                    'NETWORK_PROTOCOL',
                    'BG_JOB_ID',
                    'FG_JOB_ID',
                    // 'AUTHENTICATION_TYPE',   // < Oracle 19
                    'AUTHENTICATION_METHOD', // >= Oracle 11
                    '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";
                    try
                    {
                        var oRs = Oracle.Execute(sql);
                        var safeval = Server.HTMLEncode(String(oRs(0).Value));
                        oRs.Close();
                    }
                    catch (e)
                    {
                        var safeval = "<span style='color:red'>{0}</span>".format(Server.HTMLEncode(e.description));
                    }
                    txt = txt + "\n<tr><td>" + toCheck[i] + "</td><td>" + safeval + "</td></tr>";
                }
                txt += "</table>";
                return { result: resultcodes.ok, message: txt };
            },
            true // toggler
        )

var dbuser;
checker("Password expiration",
            function ()
            {
                var sql = "SELECT user, created, expiry_date FROM user_users";
                var oRs = Oracle.Execute(sql);
                var expire = oRs("expiry_date").Value;
                dbuser = oRs("user").Value;
                var txt = "Database user {0} was created on {1}".format(dbuser, toDateTimeString(new Date(oRs("created").Value)));
                oRs.Close();
                if (expire != null)
                {
                    var dtexpire = new Date(expire);
                    var future = Math.round((dtexpire.getTime() - new Date().getTime()) / 100 / 60 / 60 / 24) / 10;

                    txt += "<br>Password <strong>will expire {0}</strong> which is in {1} days".format(toDateTimeString(dtexpire), future);
                    if (future > 60)
                        var res = resultcodes.ok;
                    else if (future > 15)
                        var res = resultcodes.warning;
                    else
                        var res = resultcodes.error;
                }
                else
                {
                    var res = resultcodes.ok;
                    txt += "<br>Password does not expire";
                }

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

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-2021', 'DD-MM-YYYY'), 'D')) eikdag FROM DUAL";
                var oRs = Oracle.Execute(sql);
                var eikdag = oRs("eikdag").Value;
                if (eikdag == 6)
                    return { result: resultcodes.ok, message: "January 1st 2021 is a Friday<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.warning, message: "January 1st 2021 is not a Friday<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 txt;
                var sql = "SELECT PRIVILEGE FROM SESSION_PRIVS WHERE PRIVILEGE = 'CREATE VIEW'";
                var oRs = Oracle.Execute(sql);
                if (!oRs.Eof)
                {
                    res = resultcodes.ok;
                    txt = "User {0} has CREATE VIEW".format(dbuser);
                }
                else
                {
                     txt = "User {0} has no CREATE VIEW privileges, can be a problem with upgrades".format(dbuser);
                     res = resultcodes.warning;
                }
                oRs.Close();

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

checker("Long identifiers supported (>30 char)",
            function ()
            {
                var longname = "TEST_LONG_NAME_" + shared.random(35, "hex"); // 50 char totaal
                var sql = "CREATE VIEW " + longname + " AS SELECT * FROM DUAL";
                var sqlD = "DROP VIEW " + longname;
                try
                {
                    Oracle.Execute(sql);
                    Oracle.Execute(sqlD);
                    res = resultcodes.ok;
                    txt = "View {0} sucessfully created and removed".format(longname);
                }
                catch (e)
                {
                    res = resultcodes.ok; // No warning yet
                    txt = "Cannot CREATE VIEW {0}<br>{1}".format(longname, e.description);
                    txt += "<br>Facilitor does not require or use long identifiers yet but may do so in a future version";
                }

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

checker("Granted roles",
            function ()
            {
                var txt;
                var sql = "SELECT LISTAGG (GRANTED_ROLE, ', ')"
                        + " WITHIN GROUP (ORDER BY GRANTED_ROLE) grants"
                        + "  FROM user_role_privs";
                var oRs = Oracle.Execute(sql);
                res = resultcodes.ok;
                txt = "Granted roles for {0}: {1}".format(dbuser, oRs("grants").Value);
                oRs.Close();

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

/*
Lijkt bij nader inzien toch niets op te leveren
checker("Unnecessary database privileges",
            function ()
            {
                var txt;
                var sql = "SELECT * FROM SESSION_PRIVS"
                        + "\n WHERE PRIVILEGE LIKE '% ANY %'"
                        + "\n    OR PRIVILEGE LIKE '% PUBLIC %'"
                        + "\n    OR PRIVILEGE IN ('DROP USER', 'CREATE USER')";
                var oRs = Oracle.Execute(sql);
                if (oRs.Eof)
                {
                    res = resultcodes.ok;
                    txt = "No unnecessary database privileges";
                }
                else
                {
                     txt = "User {0} has unnecessary privileges. Check granted roles and<pre>{1}</pre>".format(dbuser, sql);
                     res = resultcodes.error;
                }
                oRs.Close();

                return { result: res, message: txt };
            }
        )
*/
checker("Tables analyzed?",
            function ()
            {
                var txt;
                var sql = "SELECT table_name"
                        + "  FROM user_tables"
                        + " WHERE last_analyzed IS NULL"
                        + "   AND table_name <> 'FAC_XML'" // die is temporary
                        + "   AND table_name NOT IN (SELECT object_name"
                        + "                            FROM user_objects"
                        + "                           WHERE object_type = 'TABLE' "
                        + "                             AND created > SYSDATE - 10)"
                        + " ORDER BY table_name";
                var oRs = Oracle.Execute(sql);
                if (oRs.Eof)
                {
                    var sql = "SELECT MAX(last_analyzed) last_analyzed"
                            + "  FROM user_tables";
                    var oRs2 = Oracle.Execute(sql);
                    if (oRs2("last_analyzed").Value == null)
                    {
                        txt = "Tables where never analyzed.<br>With a recently created or imported database this is normal and may resolve automatically tonight or in a few days by scheduled statistics gathering of Oracle.";
                        res = resultcodes.warning;
                    }
                    else
                    {
                        var lastAnalyze = new Date(oRs2("last_analyzed").Value);
                        var ago = Math.round((new Date().getTime() - lastAnalyze.getTime()) / 100 / 60 / 60 / 24) / 10;
                        txt = "Last time tables were analyzed was at {0} which is {1} day(s) ago.".format(toDateTimeString(lastAnalyze), ago);
                        if (ago > 10)
                        {
                            txt = txt + "<br>That is quite long ago, very low activity (is this a test environment?) or no scheduled statistics gathering by Oracle?";
                            res = resultcodes.warning;
                        }
                        else
                            res = resultcodes.ok;
                    }
                    oRs2.Close();
                }
                else
                {
                    txt = "These tables have never been analyzed. This can be very bad for the performance.<br>";
                    while (!oRs.Eof)
                    {
                        txt += ", " + oRs("table_name").Value;
                        oRs.MoveNext();
                    }
                    res = resultcodes.error;
                }
                oRs.Close();

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

//------------------------------------------------------------------------------
__GROUP = "Installed Components";
checker("SLNKDWF Installed (for Facilitor Graphics and various helper functions)",
            function ()
            {
                try
                {
                    oSLNKDWF = new ActiveXObject("SLNKDWF.About");
                }
                catch(e)
                {
                    return { result: resultcodes.warning,
                            message: e.description +  "<br>SLNKDWFx64.DLL not installed. Facilitor Graphics not available"};
                }
                var msec = oSLNKDWF.usTimer/1e3;
                var tmup = new Date(new Date().getTime() - msec);
                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: " + Math.round(oSLNKDWF.usTimer)
                        +   " ({0} days uptime since {1})".format((msec/1e3/60/60/24).toFixed(2), toDateTimeString(tmup));

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

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

checker("SLNKXLSX Installed (for native XLSX export)",
            function ()
            {
                try
                {
                    oSLNKXLSX = new ActiveXObject("SLNKXLSX.About");
                }
                catch(e)
                {
                    return { result: resultcodes.warning,
                            message: e.description +  "<br>SLNKXLSX.DLL not installed. Native Excel export not available"};
                }
                var msec = oSLNKXLSX.usTimer/1e3;
                var tmup = new Date(new Date().getTime() - msec);
                var txt ="DLLPath: " + oSLNKXLSX.DLLPath
                        + "<br>Buildtime: " + oSLNKXLSX.Buildtime
                        + "<br>VersionMajor: " + oSLNKXLSX.VersionMajor
                        + "<br>VersionMinor: " + oSLNKXLSX.VersionMinor
                        + "<br>VersionString: " + oSLNKXLSX.VersionString;

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

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

checker(safe.html(S("fg_symbolfont")) + " installed (setting S('fg_symbolfont'))",
            function ()
            {
                try
                {
                    oSLNKDWF = new ActiveXObject("SLNKDWF.About");
                }
                catch(e)
                {
                    return { result: resultcodes.warning,
                            message: e.description +  "<br>SLNKDWFx64.DLL not installed. Facilitor Graphics not available"};
                }
                var txt = "<br><img alt='Graphics drawing' src='cad_verify_font.asp' style='float:left; margin-right:10px;'>"
                        + "<img alt='Graphics drawing' src='cad_verify_font.png' style='float:left; margin-right:10px;'>"
                        + "This should show two identical images."
                        + "<br>If the left image shows two small rectangles for the coffee pot and cup the font file"
                        + "<br>appl\\FontAwesome\\webfonts\\fa-regular-400.ttf"
                        + " must be installed on the webserver by right clicking it and 'Install for all users'.";

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

checker("Chilkat Installed",
            function ()
            {
                try
                {
                    var oChilglob = new ActiveXObject("Chilkat_9_5_0.Global");
                }
                catch(e)
                {
                    return { result: resultcodes.ok,
                             message: e.description +  "<br>Chilkat_9_5_0 not installed. Order sending to SFTP not available"};
                }
                var txt ="Version: " + oChilglob.Version;
                var success = oChilglob.UnlockBundle(S("puo_chilkat_secret"));
                if (success != 1)
                    return { result: resultcodes.warn, message: txt + "Unlock failed:\n" + oChilglob.LastErrorText, info: oChilglob.Version };

                txt += "<br>UnlockStatus: {0} ({1})".format(oChilglob.UnlockStatus, ["Not unlocked. (Still in locked state.)",
                                                                                     "Unlocked with in fully-functional trial mode.",
                                                                                     "Unlocked using a valid purchased unlock code."][oChilglob.UnlockStatus]);

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

if (Request.Form("mailto").Count == 0) // te traag
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>SLNKDWFx64.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 > 300)
                        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"
                       };
            }
        )

var mailto = Request.Form("mailto").Count>0?Request.Form("mailto")(1):user.prs_perslid_email() || "to@example.com";
var mailfrom = Request.Form("mailfrom").Count>0?Request.Form("mailfrom")(1):S("puo_fromaddress") || "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):S("puo_smtpserver");
var mailrelayport = Request.Form("mailrelayport").Count>0?Request.Form("mailrelayport")(1):S("puo_smtpserverport");
var mailrelayssl = Request.Form("mailrelayssl").Count>0?Request.Form("mailrelayssl")(1):S("puo_smtpusessl");
var mailrelayuser = Request.Form("mailrelayuser").Count>0?Request.Form("mailrelayuser")(1):S("puo_sendusername");
var mailrelaypass = Request.Form("mailrelaypass").Count>0?Request.Form("mailrelaypass")(1):S("puo_sendpassword");
var puo_mode = Request.Form("puo_mode").Count>0?parseInt(Request.Form("puo_mode")(1)):0; // 0=direct smtp, 1=immediate putorders

var smsto = Request.Form("smsto").Count>0?Request.Form("smsto")(1):user.prs_perslid_mobiel();


if (this.settings) // (deze test werkt niet vanuit verify.asp.inc)
checker("IIS SMTP service installed (deprecated)",
            function ()
            {
                var pick = "c:/inetpub/mailroot/Pickup";
                if (fso.FolderExists(pick))
                    return { result: resultcodes.ok,
                             message: "Mail pickup folder found at: {0}".format(pick, S("puo_smtpserver")) };

                if (S("puo_sendusing") == 2 && S("puo_smtpserver"))
                    return { result: resultcodes.ok,
                             message: "Mail pickup folder not found at: {0}<br>but relay to <strong>{1}</strong> is configured.".format(pick, S("puo_smtpserver"))};

                return { result: resultcodes.warning,
                         message: "Mail pickup folder not found at: {0}<br>and no mail relay server configured.".format(pick)};
            }
        )

if (0) // Uitgeschakeld om IUSR blijkbaar geen rechten heeft op hMailServer.Application ('Permission denied')
checker("hMailServer installed",
            function ()
            {
                try
                {
                    var ohMail = Server.CreateObject("hMailServer.Application");
                }
                catch(e)
                {
                    return { result: resultcodes.warning,
                             message: e.description +  "<br>hMailServer not detected, incoming mail not supported"};
                }
                return { result: resultcodes.ok,
                         message: "hMailServer version {0} detected".format(ohMail.Version),
                         info: ohMail.Version
                       };
            }
        )

if (Request.Form("mailto").Count > 0 && puo_mode == 0) // Dan Direct SMTP
checker("E-mail sending",
            function ()
            {
                var txt = "Facilitor test email sent at {0} from server {1} to {2}".format(toDateTimeString(new Date()), String(Request.ServerVariables("SERVER_NAME")), mailto);
                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") = mailrelayport;
                	    if (mailrelayssl == "true" || mailrelayssl == "True"|| mailrelayssl == "1")
                	    {
                	      iConf.Fields(sch + "smtpusessl").Value = true;
                	    }
                	    if (mailrelayuser)
                	    {
                            iConf.Fields(sch + "smtpauthenticate").Value = 1; // default 0=cdoAnonymous, 1=cdoBasic, 2=cdoNTLM
                	        iConf.Fields(sch + "sendusername").Value = mailrelayuser;
                	        iConf.Fields(sch + "sendpassword").Value = mailrelaypass;
                	    }
                	}
                	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 = "<html><head></head><body>"
                                           + txt
                                           + "<br>(htmlbody)"
                                           + "<br>To: " + Server.HTMLEncode(mailto)
                                           + "<br>From: " + Server.HTMLEncode(mailfrom)
                                           + "<br>Reply: " + Server.HTMLEncode(mailreply)
                                           + "<br>Relay: " + Server.HTMLEncode(mailrelay) + ":" + mailrelayport + " SSL: " + mailrelayssl
                                           + "</body>";
                    objMail.TextBody = txt + "\n(textbody)"
                                           + "\nTo: " + Server.HTMLEncode(mailto)
                                           + "\nFrom: " + Server.HTMLEncode(mailfrom)
                                           + "\nReply: " + Server.HTMLEncode(mailreply)
                                           + "\nRelay: " + Server.HTMLEncode(mailrelay) + ":" + mailrelayport + " SSL: " + mailrelayssl;

                    objMail.Send();
                }
                catch(e)
                {
                    return { result: resultcodes.error,
                             message: "Subject: " + safe.jsstring(txt) + "<br>Error: " + e.description +  "<br>E-mail sending failed"};
                }

                txt = txt + "<script>alert('" + txt + "');</script>"
                return { result: resultcodes.ok, message: "<span style='background-color:#f8f'>{0}</span>".format(txt) };
            }
        )

if (Request.Form("mailto").Count > 0 && puo_mode == 1)
checker("E-mail sending with immediate putorders",
            function ()
            {
                var txt = "Facilitor test email sent at {0} from server {1} to {2}".format(toDateTimeString(new Date()), String(Request.ServerVariables("SERVER_NAME")), mailto);
                try
                {
                    var strBody = txt + "<br>To: " + Server.HTMLEncode(mailto)
                                      + "<br>From: " + Server.HTMLEncode(mailfrom)
                                      + "<br>Reply: " + Server.HTMLEncode(mailreply)
                                      + "<br>Immediate Putorders used";

                    params = { FromAddress: mailfrom,
                               ReplyAddress: mailreply
                             };
					putorders._puo_Oracle = orgOracle.RealConnection; // putorders vind het niet leuk dat wel Oracle vervangen hebben
                    var res = putorders.sendMail(mailto, txt, strBody, params); // res is altijd leeg
                }
                catch(e)
                {
                    return { result: resultcodes.error,
                             message: "Subject: " + safe.jsstring(txt) + "<br>Error: " + e.description +  "<br>E-mail sending failed"};
                }

                txt = txt + "<script>alert('" + txt + "');</script>"
                return { result: resultcodes.ok, message: "<span style='background-color:#f8f'>{0}</span>".format(txt) };
            }
        )

if (Request.Form("mailto").Count > 0 && puo_mode == 2)
checker("E-mail sending with scheduled putorders",
            function ()
            {
                var txt = "Facilitor test email scheduled at {0} from server {1} to {2}".format(toDateTimeString(new Date()), String(Request.ServerVariables("SERVER_NAME")), mailto);
                try
                {
                    params = { FromAddress: mailfrom,
                               ReplyAddress: mailreply
                             };


                    var sql = "BEGIN fac.putnotificationsrtprio("
                                      + user_key
                                      + ", NULL " // pto
                                      + ", " + safe.quoted_sql("FACTST")
                                      + ", " + user_key // pref
                                      + ", " + safe.quoted_sql(txt)
                                      + ", 2" // pstatus e-mail (only?)
                                      + ", " + safe.quoted_sql(mailto)
                                      + ", NULL" // pmobile
                                      + ", NULL" // geen xkey
                                      + ", 2"
                                      + ", " + safe.quoted_sql(mailfrom)
                                      + ", NULL" // geen attachments
                                      + "); END;";
                    Oracle.Execute(sql);
                }
                catch(e)
                {
                    return { result: resultcodes.error,
                             message: "Subject: " + safe.jsstring(txt) + "<br>Error: " + e.description +  "<br>E-mail sending failed"};
                }

                txt = txt + "<script>alert('" + txt + "');</script>"
                return { result: resultcodes.ok, message: "<span style='background-color:#f8f'>{0}</span>".format(txt) };
            }
        )


if (this.safe)
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 sending unavailable"};
                }

                txt  = "<a name='mailform'></a>"
                     + "<form method='post' action='#mailform'>"
                     + "      <table>"
                     + "      <tr><td><label for='mailto'>To</label></td><td><input id='mailto' name='mailto' type='text' style='width:250px' value='"+safe.htmlattr(mailto)+"'></td></tr>"
                     + "      <tr><td><label for='mailfrom'>From</label></td><td><input id='mailfrom' name='mailfrom' type='text' style='width:250px' value='"+safe.htmlattr(mailfrom)+"'></td></tr>"
                     + "      <tr><td><label for='replyto'>Reply</label></td><td><input id='replyto' name='replyto' type='text' style='width:250px' value='"+safe.htmlattr(mailreply)+"'></td></tr>"
                     + "      <tr><td><label for='puo_mode'>Method</label></td><td>"
                     +                         "<select id='puo_mode' name='puo_mode' style='width:250px'>"
                         +                         "<option value='0' " + (puo_mode==0?" selected='selected'":"") + ">Direct SMTP</option>"
                         +                         "<option value='1' " + (puo_mode==1?" selected='selected'":"") + ">Immediate Putorders</option>"
                         +                         "<option value='2' " + (puo_mode==2?" selected='selected'":"") + ">Scheduled Putorders (FACTST notification)</option>"
                     +                         "</select>"
                     + "      <input type='submit' value='Send' onclick='this.value=\"Please wait...\";'></td></tr>"
                     + "        <tr><td colspan='2'><fieldset><legend>Direct SMTP only</legend><table>"
                         + "      <tr><td><label for='mailrelay'>Relay&nbsp;server</label></td><td><input id='mailrelay' name='mailrelay' type='text' style='width:250px' value='"+safe.htmlattr(mailrelay)+"'>&nbsp;(empty&nbsp;for&nbsp;local&nbsp;IIS&nbsp;SMTP)</td></tr>"
                         + "      <tr><td><label for='mailrelayport'>Relay&nbsp;port</label></td><td><input id='mailrelayport' name='mailrelayport' type='text' style='width:100px' value='"+safe.htmlattr(mailrelayport)+"'></td></tr>"
                         + "      <tr><td><label for='mailrelayssl'>Relay&nbsp;use SSL</label></td><td><input id='mailrelayssl' name='mailrelayssl' type='text' style='width:100px' value='"+safe.htmlattr(mailrelayssl)+"'></td></tr>"
                         + "      <tr><td><label for='mailrelayuser'>Relay&nbsp;username</label></td><td><input id='mailrelayuser' name='mailrelayuser' type='text' style='width:250px' value='"+safe.htmlattr(mailrelayuser)+"'></td></tr>"
                         + "      <tr><td><label for='mailrelaypass'>Relay&nbsp;password</label></td><td><input id='mailrelaypass' name='mailrelaypass' type='password' style='width:250px' value='"+safe.htmlattr(mailrelaypass)+"'></td></tr>"
                     + "        </fieldset></td></tr></table>"
                     + "      </table>"
                     + "</form>";

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

if (Request.Form("smsto").Count > 0)
checker("SMS sending",
            function ()
            {
                var txt = "Facilitor test sms sent at {0} from {1}".format(toDateTimeString(new Date()), String(Request.ServerVariables("SERVER_NAME")));
                try
                {
					putorders._puo_Oracle = orgOracle.RealConnection; // putorders vind het niet leuk dat wel Oracle vervangen hebben
                    putorders.sendSMS(smsto, txt);
                }
                catch(e)
                {
                    return { result: resultcodes.error,
                             message: "Subject: " + txt + "<br>Error: " + e.description +  "<br>SMS sending failed"};
                }

                txt = txt + "<script>alert('" + safe.jsstring(txt) + "\\nSent to: " + safe.jsstring(Request.Form("smsto")(1)) + "');</script>"
                return { result: resultcodes.ok, message: "<span style='background-color:#f8f'>{0}</span>".format(txt) };
            }
        )
if (this.putorders && S("puo_uid") && S("puo_pwd"))
checker("Immediate SMS test using provider {0} ({1})".format(S("puo_smsprovider"), S("puo_smsprovider")==0?"wireless-services.nl":"Aareon.nl"),
            function ()
            {
                txt  = "<a name='smsform'></a>"
                     + "<form method='post' action='#smsform'>"
                     + "      <table>"
                     + "      <tr><td><label for='smsto'>To</label></td><td><input id='smsto' name='smsto' type='text' style='width:250px' placeholder='0612345678' value='"+safe.htmlattr(smsto)+"'>"
                     + "      <input type='submit' value='Send' onclick='this.value=\"Please wait...\";'></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).Value);
                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 };
            }
        )

checker("Login failures last " + S("prs_login_lockout_expire") + " minutes",
            function ()
            {
                var lockout_name = customerId + "_LOGINATTEMPTS";
                var lockout_data = Application(lockout_name);
                var txt = "No failures";
                res = resultcodes.ok;
                if (lockout_data && lockout_data != "[]")
                {
                    var txt = "<pre>" + JSON.stringify(myJSON.parse(lockout_data), null, '  ') + "</pre>";
                    res = resultcodes.warning;
                }

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

__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 (" + custID  + " only)",
            function ()
            {
                return { result: resultcodes.ok,
                         message: DumpCollection(Application.Contents, custID)
                       }
            },
            true // toggler
        )

checker("Application Variable Collection (all)",
            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
        );

checker("Environment",
            function ()
            {
                try
                {
                    var shell = new ActiveXObject("WScript.Shell")
                    var pCollection = shell.Environment("Process");
                    var colVars = new Enumerator(pCollection);
                    var lines = [];
                    for(; ! colVars.atEnd(); colVars.moveNext())
                    {
                        var line = "<tr><td colspan='2'>" + Server.HTMLEncode(String(colVars.item())) +"</td></tr>";
                        lines.push(line);
                    }
                    var sLog = "<table class='inside'>" + lines.join("\n") + "</table>";
                    return { result: resultcodes.ok,
                             message: sLog
                           }
                }
                catch(e)
                {
                    return { result: resultcodes.warning,
                             message: e.description
                           }
                }
            },
            true // toggler
        );

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

%></table><%
var duration = (new Date() - starttime);
Response.Write("<script>document.getElementById('duration').textContent = \'{0}\'</script>".format((duration / 1000).toFixed(1)));

function DumpCollection(pCollection, filter)
{
if (Request.Form("mailto").Count > 0) // te traag
return "<table class='inside'><tr><td colspan='3'>Not when emailing</td></tr></table>";

    var i;
    var lines = [];
    for (i=1; i<=pCollection.Count; i++)
    {
        var name = String(pCollection.key(i));
        if (filter && name.indexOf(filter) == -1)
            continue;
        var line = "<tr><td>" + name; // + " type: " + typeof pCollection(i) + " cons: " + pCollection(i).constructor
        if (typeof pCollection(i) != "object" || pCollection(i) === null || !pCollection.HasKeys)
        {
            var v = String(pCollection(i));
            line += "</td><td>" + Server.HTMLEncode(v).replace(/\n/g, "<br>");
             var numv = parseInt(v);
            if (!isNaN(numv)) // Als de waarde een datum lijkt dan datum parsen. Leest gemakkelijker
            {
                if (numv > 946684800000 && numv < 2145916800000) // 2000-2038
                    line += "&nbsp;<em style='font-size:smaller'>(" + toISODateTimeString(new Date(parseInt(v)), true) + ")</em>";
            }
        }
        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");

if (orgOracle)
{
    Oracle = orgOracle; // zodat ASPPAGE_END() heb netjes kan closen
}

%>
<script language="VBScript" runat="Server">

Function GetVBScriptEngineTxt
    Dim s
    s = ""
    s = "ScriptEngine: " & ScriptEngine
    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>