<% /*
     $Revision$
     $Id$
*/ %>
<%

var adExecuteNoRecords = 0x80;
var _LastFacError = {}; // Deze kun je direct na een error raadplegen voor een vriendelijkere tekst
// als catchErrors is gedefinieerd (overigens alleen ondersteund voor INSERT/UPDATE/DELETE en *niet* voor SELECT)
// dan worden alle Oracle errors afgevangen en wordt bij fouten _LastFacError opgeleverd

var _all_recordset = [];
var _sql_time = 0;
var _sql_cnt = 0;

// Execute(mldUpd, catchErrors) is also supported
function SafeExec( sql, catchErrors ) {
  if (typeof sql == "object") // when calling with mldUpd object
  {
    var bindparams = sql.bindparams;
    var thesql = sql.sql;
    if (bindparams && bindparams.length) {
        return Oracle.ExecuteParam(thesql, bindparams, catchErrors);
    }
    else
    {
        sql = thesql; // gewoon doorvallen
    }
  }

  var result;
  __LogMarker = this.marker; // uit Putorders.inc
  var handle = __Log(sql, SafeExec.caller, true /* leave_open */);
  _sql_cnt ++;
  var startTime = us_timer();
  var hasRecords = !sql.match(/^(DELETE|INSERT|UPDATE|BEGIN|DECLARE|ALTER)/ig); // Waarschijnlijk alleen SELECT en WITH over

  _LastFacError = { fullMsg: "", faccode : "", friendlyMsg : "" };
  try {
    if (hasRecords)
    {
        result = this.RealConnection.Execute( sql );
        if (Application("otap_environment") == "O")
        {
            check_recordsets(); // alle gesloten recordsets verwijderen
            _all_recordset.push({ rs: result, caller: SafeExec.caller });
        }
    }
    else
    {
        if (this._inTransaction)
        {
            //if (sql.match(/^BEGIN.*END\;$/i)) // Een BEGIN ... END wordt er bij de transactie omheen geplaatst, hier weg dus
            //    sql = sql.replace(/^BEGIN(.*)END;$/i, "$1");
            if (sql.substr(0, 5) == 'BEGIN' && sql.substr(sql.length - 4) == 'END;')
            {
                sql = sql.substr(5, sql.length - 5 - 4);
                sql = sql.replace(/;\s+$/, ""); // laatste ; en spaties eraf
            }
            this._Statements.push(sql);
            __LogCloseLine(null, "<div style=\"background-color: #00FF00;\">Delayed {0} (transaction)</div>".format(this._Statements.length));
            return {};
        }
        else
            this.RealConnection.Execute( sql, null, adExecuteNoRecords); // Voorkomt out-of-cursors
        result = {}; // er moet later wel een err.friendlyMsg getest kunnen worden
    }
  } catch( e1 ) {
    __LogCloseLine(null, "FAIL", "#FF0000");
    var msg = e1.description;
    var knownError = false;
    _LastFacError = { fullMsg: msg,
                      faccode : "",
                      friendlyMsg : msg };
    if (msg.match(/^ORA-20\d{3}:(.*)\nORA-06512/i) || msg.match(/^ORA-20000/i) || msg.match(/^ORA-20004/i) || msg.match(/^ORA-00001/i) || msg.match(/^ORA-02290/i) || msg.match(/^ORA-02291/i) || msg.match(/^ORA-02292/i))
    {
        RegCode = msg.match(/^ORA-20000: (\w*)(.*)\nORA-/i);     // Zoek: ORA-20000: res_m039 ORA-06512: at "UWVA_500.ALG_T_ALG_RUIMTE_B_IU", line 71 ORA-04088: error during execution of trigger 'UWVA_500.ALG_T_ALG_RUIMTE_B_IU'
        if (!RegCode || RegCode.length == 0)
        {
            RegCode = msg.match(/^ORA-20\d{3}:(.*)\nORA-06512/i); // Zoek: (ORA-20000 - ORA-20999): ORA-06512: at .......
            if (RegCode && RegCode.length > 1)
            {
                _LastFacError.friendlyMsg = RegCode[1];
            }
        }
        if (!RegCode || RegCode.length == 0)
            RegCode = msg.match(/^ORA-20004: (\w*)(.*)\nORA-/i); // Zoek: ORA-20004: ins_m145,Normverbruik,0, ORA-06512: at .......
        if (!RegCode || RegCode.length == 0)
            RegCode = msg.match(/^ORA-00001: .* \(.*\.(\w*)\)/i) // Zoek: ORA-00001: unique constraint (UWVA_500.ALG_U_ALG_RUIMTE_UPPER_NR) violated
        if (!RegCode || RegCode.length == 0)
            RegCode = msg.match(/^ORA-02290: .* \(.*\.(\w*)\)/i) // Zoek: ORA-02290: check constraint (UWVA_500.ALG_C_ALG_RUIMTE_UPPER_NR) violated
        if (!RegCode || RegCode.length == 0)
            RegCode = msg.match(/^ORA-02291: .* \(.*\.(\w*)\)/i) // Zoek: ORA-02291: integrity constraint violated - parent key not found
        if (!RegCode || RegCode.length == 0)
            RegCode = msg.match(/^ORA-02292: .* \(.*\.(\w*)\)/i) // Zoek: ORA-02292: integrity constraint violated - child record found

        if (RegCode && RegCode.length > 1)
        {
            _LastFacError.faccode = RegCode[1];
        }
        if (msg.match(/^ORA-20000/i) && RegCode && RegCode.length > 2)
        {
            _LastFacError.friendlyMsg = RegCode[2] || _LastFacError.faccode; // een ORA-20000 met ingebouwde tekst zoals ORA-20000: alg_m999 Ruimte kan nog niet verwijderd worden. Er zijn nog lopende meldingen
            var knownError = true;
        }
        else if (msg.match(/^ORA-20001/i)) // Alleen tekst: 'ORA-20001 Deze ruimte of voorziening is niet beschikbaar. Kies een andere ruimte of een ander tijdstip!'
        {
            var knownError = true;
        }
        // We gaan proberen de tekst er bij te zoeken
        var ignoreError = /res_m999|res_m960/; // Ruimte niet beschikbaar niet proberen te vertalen
        if (_LastFacError.faccode != "" && !_LastFacError.faccode.match(ignoreError))
        {
            var sql2 = "SELECT fac_message_text FROM fac_message WHERE fac_message_code = "+safe.quoted_sql_upper(_LastFacError.faccode);
            var oRs = this.RealConnection.Execute( sql2 );
            if (!oRs.Eof)
            {
                var knownError = true;
                _LastFacError.friendlyMsg = oRs("FAC_MESSAGE_TEXT").Value + "\n(" + _LastFacError.faccode + ")";
            }
            else if (!knownError)
            {
                __SafeDoLog("TODO: <em>{0}</em> opnemen in een XXX_INI.SRC (FCLT#51673)?".format(safe.html(_LastFacError.faccode)));
            }
            oRs.Close();
        }
        if (knownError && !_LastFacError.faccode.match(ignoreError))
        {
            __Log("Bovenstaande ging fout, " + _LastFacError.faccode + " betekent:\n" + _LastFacError.friendlyMsg);
        }
        else
            __Log("Foutcode: " + _LastFacError.faccode + " niet gevonden in fac_message");
    }

    // multiline test to check for any matches
    if (RegExp(/^ORA-\d*:\s/m).test(_LastFacError.friendlyMsg))
    {
        var arr = _LastFacError.friendlyMsg.split("\n");
        var index;
        for (index = 0; index < arr.length; index++)
        {
            // only for messages that start with ORA-ddddd:
            if (RegExp(/^ORA-\d*:\s/).test(arr[index])) // prevent hacker-usable ORA- information in friendlyMsg
            {
            arr[index] = arr[index].replace(/^ORA-\d*:\s/,"").replace(/\"{1}\w*\.{0,1}\w*\"{1}.{0,1}|\({1}\w*\.{0,1}\w*\){1}.{0,1}/g,"");
    //                                      _____________              _____________________________ _____________________________
    //                remove string "ORA-ddddd: " |                                   |                             |
    //                                                                                |                             |
    // remove strings with db info "COMP_TEST"."INS_SRTKENMERK"."INS_SRTKENMERKTYPE"  |                             |
    //                                                                                                              |
    //          remove strings with constraint/trigger names enclosed in Parentheses eg (INS_T_INS_SRTKENMERK_B_IU) |
            }
        }
        _LastFacError.friendlyMsg = arr.join("\n");
    }
    if (!knownError)
    {
        if (!(__Logging & 1))
            __DoLog(sql); // alsnog
        __SafeDoLog("Database exception:<br>" + Server.HTMLEncode(e1.description).replace(/\n/g,"<br/>"), "#f00");
        dump_recordsets();

        try {
            if (Request.Form.Count>0) __DoLogForm("#FF0000");
        }
        catch(e)
        { /* er is al een Request.BinaryRead geweest */ }
    }

    if (!catchErrors)
    {
        Session("last_sql") = sql; // wordt door 500-error.asp opgepikt
        Session("last_caller") = __callerTekst(SafeExec.caller);
        if (typeof THIS_FILE == "string")
            Session("THIS_FILE") = THIS_FILE;
        throw e1; // Throw again
    }
    else
        return _LastFacError;
  }

  // Geen errors blijkbaar.
  var eofText = "";
  try {
      if( hasRecords && result.State != 0 && result.Eof) // This Eof test may take some time!
        eofText = "<b>N/R</b>";
  } catch( e3 ) {
        __LogCloseLine(null, "<div style=\"background-color: #FF0000;\">Error while getting result.Eof: " + e3.description + "</div>");
        throw e3; // Throw again
  }

  if (hasRecords && _all_recordset.length > 1)
    eofText += (eofText?"<br>":"") + "rs: " + _all_recordset.length;

  var ms_taken = (us_timer() - startTime) / 1000;
  _sql_time += ms_taken;

  // Indien ingesteld registreren we het aantal statements dat een bepaalde drempelwaarde
  // (in millisecondes) overschrijdt. Hoe, dat bepaalt een databaseprocedure.
  if (S("perfmon_threshold") > -1) {
    if (S("perfmon_threshold") < 500) S("perfmon_threshold") = 500; // daaronder wordt het riskanter en valsspelen.
    if (ms_taken > S("perfmon_threshold")) {
        var isql = "BEGIN fac.fac_perfmon_incr("+S("perfmon_threshold")+", "+ms_taken+"); END;"
        this.RealConnection.Execute( isql, null, adExecuteNoRecords);
        eofText = eofText+"<br><b>#P#</b>";
    }
  }

  __LogCloseLine(handle, eofText);

  return result;
}

function SafeOracle( ora )
{
  this.RealConnection = ora;
  this.Execute = SafeExec;

  this._inTransaction = false;
  this._Statements = [];
}

SafeOracle.prototype.rs2hash = function _rs2hash (oRs)
{
    var result = [];
    while(!oRs.Eof)
    {
        var datahash = {};
        for (var i = 0; i < oRs.Fields.Count; i++)
        {
            var v = oRs.Fields(i).Value;
            if (v != null && oRs.Fields(i).Type == 135) // adDBTimeStamp
                v = new Date(v);
            datahash[oRs.Fields(i).Name.toLowerCase()] = v;
        }
        result.push(datahash);
        oRs.MoveNext();
    }
    return result;
}
SafeOracle.prototype.Get = function _get (descfield, table, keyname, key)
{
    if (typeof key == "undefined")
    {
        key = keyname;
        keyname = table + "_KEY";
    }
    var sql = "SELECT {0} FROM {1} WHERE {2} = {3}".format(descfield, table, keyname, key);
    var oRs = this.Execute(sql);
    var res = oRs.Fields(0).Value;
    oRs.Close();
    return res;
}

SafeOracle.prototype.Transaction = function ()
{
    if (this._inTransaction)
        INTERNAL_ERROR_NESTED_TRANSACTION;
    this._inTransaction = true;
    this._Statements = [];
}
SafeOracle.prototype.Commit = function ()
{
    if (!this._inTransaction)
        INTERNAL_ERROR_NO_TRANSACTION;
    this._inTransaction = false;
    __Log("Committing transaction");
    var sql = "BEGIN \n{0};\nEND;".format(this._Statements.join(";\n\n"));
    Oracle.Execute(sql);
    this._Statements = [];
}
SafeOracle.prototype.Rollback = function ()
{
    if (!this._inTransaction)
        INTERNAL_ERROR_NO_TRANSACTION;
    this.inTransaction = false;
    this._Statements = [];
}

var adParamInput = 1;
var adParamOutput = 2;
var adParamInputOutput = 3;
var adVarChar = 200;
var adInteger = 3;
var adStateOpen = 1;
var adUseClient = 3;
var adOpenStatic = 3;
var adCmdStoredProc = 4;
var adCmdText = 1;
var adLongVarChar = 201; // Ook Oracle CLOB

// Eerste opzet parameterized queries
// Doet nog wel iets minder logging dan de reguliere Oracle.Execute
// Geintroduceerd voor bez.copy_afspraak() om de nieuwe key te kunnen krijgen
SafeOracle.prototype.ExecuteParam = function (sql, params, catchErrors)
{
    __Log(sql);

    var cmdo = new ActiveXObject("ADODB.Command")
    cmdo.CommandText = sql;
    cmdo.CommandType = adCmdText;
    cmdo.ActiveConnection = this.RealConnection;
    cmdo.NamedParameters  = true; // Lijkt hoe dan ook niet te lukken met Oracle
    var hasRecords = !sql.match(/^(DELETE|INSERT|UPDATE|BEGIN|DECLARE|ALTER)/ig); // Waarschijnlijk alleen SELECT en WITH over

    for (var i = 0; i < params.length; i++)
    {
        var itm = params[i];
        __Log("==== Binding ====");
        __Log(itm);
        var dir = itm.dir || (("val" in itm)?adParamInput:adOutput)
        if (!("len" in itm) && typeof itm.val == "string")
        {
            itm.len = itm.val.length;
        }

        var prm = cmdo.CreateParameter("", itm.typ, dir, itm.len);

        if ("val" in itm)
        {
            prm.Value = itm.val;
            if (typeof itm.val == "string")
                prm.Size = itm.val.length;
        }

        if (dir == adParamOutput || dir == adParamInputOutput) // dan onthouden
            params[i].AdoParameter = prm;

        cmdo.Parameters.Append(prm);
    }
    cmdo.prepared = false;
    _LastFacError = { fullMsg: "", faccode : "", friendlyMsg : "" };
    try
    {
        //if (hasRecords)
        var oRs = cmdo.Execute();
        // else
        // var oRs = cmdo.Execute(null, null, adExecuteNoRecords); // Voorkomt out-of-cursors maar doet bij bind-params netto geen update
    }
    catch( e1 )
    {
        __LogCloseLine(null, "FAIL", "#FF0000");
        var msg = e1.description;
        _LastFacError = { fullMsg: msg,
                          faccode : "",
                          friendlyMsg : msg };
        __Log(e1.description);
        if (!(__Logging & 1))
            __DoLog(sql); // alsnog
        __SafeDoLog("Database exception:<br>" + Server.HTMLEncode(e1.description).replace(/\n/g,"<br/>"), "#f00");

        try {
            if (Request.Form.Count>0) __DoLogForm("#FF0000");
        }
        catch(e)
        { /* er is al een Request.BinaryRead geweest */ }

        if (!catchErrors)
        {
            Session("last_sql") = sql; // wordt door 500-error.asp opgepikt
            Session("last_caller") = __callerTekst(SafeExec.caller);
            throw e1; // Throw again
        }
        else
            return _LastFacError;
    }
    for (var i = 0; i < params.length; i++)
    {
        if ("AdoParameter" in params[i])
        {
            params[i].val = params[i].AdoParameter.Value
            __Log("Out value: " + params[i].val);

        }
    }
    if (catchErrors)
        return {}; // er moet later wel een err.friendlyMsg getest kunnen worden

    return oRs;
}

function check_recordsets()
{
    for (var i = 0; i < _all_recordset.length; i++)
    {
        var rs = _all_recordset[i].rs;
        if (rs.state == 0)
        {
            _all_recordset.splice(i, 1);
            i--;
        }
    }
}

function dump_recordsets()
{
    var cnt = 0;
    for (var i =0; i < _all_recordset.length; i++)
    {
        var rs = _all_recordset[i].rs;
        var caller = _all_recordset[i].caller;
        if (rs.state == 1)
        {
            __DoLog("Unclosed recordset {0}:\n{1}".format(++cnt, rs.Source), "#ff0", caller);
        }
    }
}

var _cache_F = [];
function F(fac_functie_code)
{
    if (!(fac_functie_code in _cache_F))
    {
        var sql = "SELECT fac_functie_key "
                + "  FROM fac_functie"
                + " WHERE fac_functie_code = " + safe.quoted_sql(fac_functie_code);
        var oRs = Oracle.Execute(sql);
        _cache_F[fac_functie_code] = oRs("fac_functie_key").Value;
        oRs.Close()
    }
    return _cache_F[fac_functie_code];
}

// ===================================
// Initialize the global Oracle object
// ===================================
  if( ! this.Oracle )
  try {
        var str = Server.MapPath(custpath + "/Oracle.udl");
        var o = Server.CreateObject("ADODB.Connection");
        o.Open('File Name=' + str);
        Oracle = new SafeOracle(o);

  } catch( e1 ) {
        objFile = Server.CreateObject("Scripting.FileSystemObject");
        if (!objFile.FileExists(str))
        {
            Response.write("oracle.udl niet gevonden voor: " + customerId);
            Response.end;
        }
        __SafeDoLog(str + "<div style=\"background-color: #FF00FF;\">Cannot create database connection: " + safe.html(e1.description) + "</div>");
        __LogfileName = shared.tempFolder() + "/../log_database.html"; // van alle klanten verzamelen in hetzelfde bestand
        __SafeDoLog(str + "<div style=\"background-color: #FF00FF;\">Cannot create database connection: " + safe.html(e1.description) + "</div>");
        if (Session("page_start"))
        {
            var now = new Date();
            var start = new Date(Session("page_start"));
            var duration = Math.round((now.getTime() - start.getTime()) / 100) / 10;
            __SafeDoLog("Page started for {0} at {1} which is {2} second(s) ago".format(customerId, toISODateTimeString(start), duration));
        }
        Response.write("Fatal error, unable to open database connection:<xmp>{0}</xmp>".format(safe.html(e1.description)));
        // *Zo* fataal dat ik niet eens de AiAi handler in wil gaan.
        Response.Status = "500 Internal Server Error";
        Response.End;
        //throw e1; // Throw again, VERY fatal
  }

  // Merk op: iets als user_lang bepaling zou je eerder verwachten in common.inc
  // We zitten hier echter met een kip-ei probleem: we willen het *direct* gebruiken
  // voor fac.initsession maar kunnen het pas gebruiken als we database connectie
  // hebben. Daarom dus hier.
  var user_lang = Session("user_lang");
  var fac_lang = getQParamSafe("fac_lang", "").toUpperCase(); // overrule via param
  if (fac_lang)
  {
    if (typeof user_lang == "undefined")
        Session("user_lang") = fac_lang;
    user_lang = fac_lang; // In ieder geval voor *deze* pagina
  }

  if (!user_lang || typeof user_lang == "undefined")
  {
    var oRs = o.Execute("SELECT fac_version_lang FROM fac_version");
    user_lang = oRs("fac_version_lang").Value; // De database taal voorlopig.
    oRs.Close();
  }
  var ident = "";
  if (Session("user_key")>0)
    ident = String(Session("user_key"))
  //else
  //  ident += "<system>";
  var realuser = Session("fclt_realuser");
  if (rooturl)
    realuser += "@" + rooturl;
  var oraprefix = "BEGIN"
                + "  fac.initsession(pkey => {0}, flcode => {1}, usertimezone => {2}, realuser => {3});"
                            .format(safe.quoted_sql(ident), safe.quoted_sql(user_lang), safe.quoted_sql(Session("time_zone")), safe.quoted_sql(realuser))
                + "END;";

  o.Execute( oraprefix );
%>
