<% /*
    $Revision$
    $Id$

    File:           api.inc
    Description:    Functies voor API's
    Notes:

*/

function API_func(APIname)
{
    this.APIname = APIname || getQParam("API");
    var sql = "SELECT *"
            + " FROM fac_api"
            + " WHERE fac_api_name = " + safe.quoted_sql(this.APIname);
    var oRs = Oracle.Execute(sql);
    if (oRs.Eof)
    {
        this.error("Invalid API: " + this.APIname);
        oRs.Close();
        Response.End;
    }

    if (user_key < 0)
    {
        this.APIKEY = getQParam("APIKEY", "");
        if (!this.APIKEY && S("basic_auth_realm"))
        {
            Response.Status = "401 Unauthorized";
            Response.AddHeader("WWW-Authenticate", "Basic realm=\"" + S("basic_auth_realm") + "\"");
            Response.End;
        }
        else
        {
            var sql2 = "SELECT prs_perslid_key, prs_perslid_naam"
                    + " FROM prs_perslid"
                    + " WHERE prs_perslid_apikey = " + safe.quoted_sql(this.APIKEY)
                    + "   AND prs_perslid_verwijder IS NULL"; // Eigenlijk zou de trigger APIKEY moeten wissen bij verwijderen
            var oRs2 = Oracle.Execute(sql2);
            if (oRs2.Eof)
            {
                this.error("Invalid APIKEY: " + this.APIKEY);
                oRs2.Close();
                Response.End;
            };
            __Log("API User is: " + oRs2("prs_perslid_naam").Value);
            // JGL: komen we hier ooit? Gebeurt nu ooit dit: /* global */ user = new Perslid(user_key);
            user_key = oRs2("prs_perslid_key").Value;
            oRs2.Close();
        }
    }

    this.apidata =
    {
        APIname:          this.APIname,
        APIKEY:           this.APIKEY,
        file:             oRs("fac_api_filepath").Value,
        prs_perslid_key:  user_key,
        loglevel:         oRs("fac_api_loglevel").Value,
        errorhandling:    oRs("fac_api_errorhandling").Value,
        usrrap_key:       oRs("fac_usrrap_key").Value,
        stylesheet:       oRs("fac_api_stylesheet").Value,
        stylesheet_out:   oRs("fac_api_stylesheet_out").Value
    };

    if (this.apidata.loglevel > 1) // Zet maar op 3 als je HTML logging wilt
    {
        __Logging = this.apidata.loglevel || __Logging || 0; // Voor de rest van dit bestand
        __Log("Logging for this API ({0}) is forced on".format(this.APIname), '#f00');
    }

    try
    {
        if (oRs("fac_api_options_json").Value)
            this.apidata.options = JSON.parse(oRs("fac_api_options_json").Value);
    }
    catch (e)
    {
        __DoLog(e);
        this.error("Invalid api 'options': " + e.description);
    }
    try
    {
        if (oRs("fac_api_viewmapping_json").Value)
            this.apidata.viewmapping = JSON.parse(oRs("fac_api_viewmapping_json").Value);
    }
    catch (e)
    {
        this.error("Invalid api 'viewmapping': " + e.description);
    }

    oRs.Close();
    // Wij doen niets met eventuele prs_perslid_key; dat doet loginTry.asp maar voor ons
}

API_func.prototype.error = function (msg)
{
    if (JSON_Result && JSON) // Merk op dat 'invalid APIKEY' al door /default.asp
    {                        // is onderschept en dus niet hier komt.
      Response.Write(JSON.stringify({ success: false, message: msg }));
    }
    else
    {
        Response.Status = "500 Internal server error"; // 500_error.asp blijkt hier niet op in te grijpen
        Response.Write(safe.html(msg));
        // Op productie zie je bovenstaande Response.Write ook niet terug in Fiddler omdat
        // 'detailed error messages' uit staat. Daarom ook maar loggen voor het gemak.
        __DoLog(safe.html(msg), "#ff0000");
    }
    Response.End;
}

// LET OP: Verwacht wordt dat de JSON-code in de body utf-8 encoded is, niet windows-1252!
// (in de praktijk moet je *moeite* doen om windows-1252 te krijgen dus dit is handiger)
function RequestJSON(loglevel)
{
    var jvraag;
    if(Request.TotalBytes == 0)
    {
        return { error: "Empty body posted" }
    }

    var lngBytesCount = Request.TotalBytes;
    jvraag = BytesToStr(Request.BinaryRead(lngBytesCount));
    __Log("Vraag: " + jvraag);
    try
    {
        var vraag = myJSON.parse(jvraag);
        if (loglevel)
            __Log2File(JSON.stringify(vraag, null, 2), API.APIname + "_IN", ".json");
    }
    catch (e)
    {
        __DoLog("eval faalt met: {0}<br>{1}".format(e.description, jvraag), "ffff00");
        return null;
    }
    return vraag;
}

function RequestXML(API)
{
    var xvraag;
    if(Request.TotalBytes == 0)
    {
        return { error: "Empty body posted" }
    }

    var lngBytesCount = Request.TotalBytes;
    xvraag = BytesToStr(Request.BinaryRead(lngBytesCount));
    if (API.apidata.loglevel)
        __Log2File(xvraag, API.APIname + "_IN");

    __Log("Request body: " + xvraag);
    try
    {
        var inputXML = Server.CreateObject("MSXML2.DOMDocument.6.0");
        inputXML.loadXML(xvraag); // inputXML.load(Request) zou nog niet vertalen naar Windows-1252
    }
    catch (e)
    {
        return { error: e.description };
    }
    if (inputXML.parseError.errorCode)
    {
        return { error: inputXML.parseError.reason + " @ " + inputXML.parseError.line + "." + inputXML.parseError.linepos };
    }
    return { xml: inputXML };
}

// Eventuele input stylesheet is er overheen
function styledRequestXML(API)
{
    var xmlReq = Server.CreateObject("MSXML2.DOMDocument.6.0");
    var parsed = RequestXML(API);
    if (parsed.error)
    {
        var method = String(Request.ServerVariables("REQUEST_METHOD"));
        if (method != "POST") // gebeurt nog wel eens
        {
            __DoLog( "Weird: expected method POST, got " + method);
        }
        API.error("Error loading XML: " + parsed.error);
    }

    if (API.apidata.stylesheet)
    {
        var iXsl = Server.CreateObject("MSXML2.DOMDocument.6.0");
        iXsl.resolveExternals = true; // XSL kan includes hebben (MARX#51709)
        if( ! iXsl.load(Server.MapPath(API.apidata.stylesheet)) )
        {
            API.error("Could not load XSL " + API.apidata.stylesheet + "\n" + iXsl.parseError.reason);
        }
        var inputXML = Server.CreateObject("MSXML2.DOMDocument.6.0");
        inputXML = parsed.xml;
        inputXML.transformNodeToObject(iXsl, xmlReq);
        if (API.apidata.loglevel) __Log2File(xmlReq.xml, API.APIname + "_TUSSEN");
    }
    else
        var xmlReq = parsed.xml;

    return xmlReq;
}

function BytesToStr(bytes)
{
    var stream = Server.CreateObject("ADODB.STREAM");
    stream.type = 1;
    stream.open;
    stream.write(bytes);
    stream.position = 0;
    stream.type = 2; // Text
    stream.charset = "utf-8";

    // Mogelijk was het nog utf-8. Dat is riskant met unicode quotes (FCLT#60591)
    // Daarom expliciete conversie
    var win1252Stream = new ActiveXObject("ADODB.Stream");
    win1252Stream.Open();
    win1252Stream.CharSet = "Windows-1252"; // het doel
    stream.copyTo(win1252Stream);
    stream.close;

    win1252Stream.position = 0;

    var ReadBytes = 131072; // optimaal volgens https://docs.microsoft.com/en-us/sql/ado/reference/ado-api/readtext-method?view=sql-server-ver15
    var sOut = "";
    while (!win1252Stream.EOS)
    {
        sOut = sOut + win1252Stream.ReadText(ReadBytes);
    }
    win1252Stream.close;

    return sOut;
}

%>