/*
    $Revision$
    $Id$

    File:               imp_shared.js
    Description:        Helper functie
    Parameters:
        import_app_key  Importfunctie key
    Context:
    Note:               ========= LET OP LET OP =========
                        Dit bestand wordt ook gebruikt door ../../utils/gen_import/gen_import.wsf
                            Daar hebben we door CScript maar heel weinig functies!
                            DUS: Geen L() functie voor locale!!!!
                             EN: Geen Server.CreateObject maar new ActiveXObject
                        Dit bestand wordt ook gebruikt door ../api/api_gen_import.asp
                        ========= LET OP LET OP =========
*/

// fileStream moet onze data bevatten
// import_app_key de import-functie
// params import_key mag/zal leeg zijn voor het eerste bestand
//        customerId: "UWVA"
//        ref_key: optioneel een key die in FAC_IMPORT_REFKEY gaat.
//        fac_home: net boven de APPL dus bijv. "d:/apps/facilitor/"
//        filepathname: optioneel: stoppen we in fac_import_filenaam
//        keep_old: oude imports van dezelfde soort van maximaal keep_old
//                  seconden worden nog even aangehouden. Default 0
//                  Nodig als je meerdere parallelle imports wilt ondersteunen
//                  (vanuit api_gen_import bijvoorbeeld)
//        keep_backup: als fac_import_app_folder gezet is wordt een backup
//                     van de import file gemaakt onder fac_import_app_folder/BACKUP2015/....
//                     Merk op: vanuit gen_import.wsf wordt keep_backup *niet* gezet
//                              omdat gen_import.wsf (nog) zelf de backup maakt
//        stylesheet: optionele overrule van de import-XSL-stylesheet


function parseCSVLine(csvLine, delimiter)
{
  var fields = csvLine.split(delimiter);
  var correctedFields = [];
  var mergedField = '';

  for (var regel in fields)
  {
    var field = fields[regel];
    if (!mergedField && field.charAt(0) !== '"')
    {
      correctedFields.push(field);
    }
    else
    {
      mergedField += mergedField ? (delimiter + field) : field;
      if (mergedField.charAt(mergedField.length - 1) === '"')
      {
        correctedFields.push(mergedField.substring(1, mergedField.length - 1));
        mergedField = '';
      }
    }
  };

  return correctedFields;
}

function impReadStream(fileStream, import_app_key, params)
{
    params = params || {};
    if (!("keep_old" in params))
        params.keep_old = 0;
    params.is_stream = ("is_stream" in params ? params.is_stream : false); // default false
    var import_key = params.import_key || -1;
    if (import_key < 0)
    {
        var this_import_where = " fac_import_app_key = " + import_app_key;
        if (params.keep_old > 0)
            this_import_where += " AND fac_import_datum_gelezen <= SYSDATE - " + parseInt(params.keep_old) + " /60/60/24";

        // Hadden we nog iets openstaan voor deze import_app_key? Dan wissen we die eerst
        // We ondersteunen namelijk geen *twee* openstaande imports van dezelfde functie
        if (params.keep_old == 0) // alleen bij ad-hoc imports, niet die via API aanroepen
        {
            var sql = "DELETE FROM fac_import"
                + " WHERE " + this_import_where
                + "   AND fac_import_datum_verwerkt IS NULL";
            Oracle.Execute(sql); // cascadeert vanzelf FAC_IMP_LOG en FAC_IMP_FILE

            // Merk op: verwerkte FAC_IMPORT records blijven eeuwig bestaan
            // TODO: Ooit saved files opruimen op basis van fac_import_backupdir

            // Ook: *alle* oude FAC_IMP_FILE voor deze import gaan nu weg
            var sql = "DELETE FROM fac_imp_file"
                    + " WHERE fac_import_key IN"
                    + "    (SELECT fac_import_key"
                    + "       FROM fac_import"
                    + "      WHERE " + this_import_where + ")";
            Oracle.Execute(sql);

            // Ook: *alle* oude FAC_IMP_CSV voor deze import gaan nu weg
            var sql = "DELETE FROM fac_imp_csv"
                    + " WHERE fac_import_key IN"
                    + "    (SELECT fac_import_key"
                    + "       FROM fac_import"
                    + "      WHERE " + this_import_where + ")";
            Oracle.Execute(sql);
        }
        // Opruimen van oude logging doen we in fac.fac_cleanup die elke dag uitgevoerd wordt dmv fac.fac_daily

        var sql = "SELECT fac_s_fac_import_key.nextval FROM DUAL";
        var oRs = Oracle.Execute(sql);
        import_key = oRs(0).Value;
        oRs.Close();

        sql = "INSERT INTO fac_import"
            + "       (fac_import_key,"
            + "       fac_import_app_key,"
            + "       fac_import_refkey,"
            + "       fac_import_filenaam,"
            + "       fac_import_datum_gelezen,"
            + "       prs_perslid_key)"
            + " VALUES( " + import_key + ", "
            +               import_app_key + ", "
            +               (params.ref_key||"NULL") + ","
            +               safe.quoted_sql(params.filepathname, 256) + ", "
            +           "SYSDATE, "
            +           (params.user_key > 0 ? params.user_key : "NULL")
            + ")";
        Oracle.Execute(sql);
    }
    else
    {   // Mocht een import-functie *echt* iets met de filenaam willen doen dan kan hij hem zelf opzoeken
        // in fac_import. Voor multi-import slaan we daartoe de laatste filenaam op.
        sql = "UPDATE fac_import"
            + "   SET fac_import_filenaam = " + safe.quoted_sql(params.filepathname,256)
            + " WHERE fac_import_key = " + import_key;
        Oracle.Execute(sql);
    }
    var sql = "SELECT i.fac_import_app_code "
            + ",      i.fac_import_app_prefix "
            + ",      i.fac_import_app_xsl "
            + ",      i.fac_import_app_charset "
            + ",      i.fac_import_app_csv"
            + ",      i.fac_import_app_delimiter"
            + ",      i.fac_import_app_folder"  // voor eventuele backup
            + "  FROM fac_import_app i"
            + " WHERE i.fac_import_app_key = " + import_app_key;
    var oRs = Oracle.Execute(sql);

    var import_app = oRs("fac_import_app_code").value;
    var import_csv = oRs("fac_import_app_csv").Value == 1;
    var import_delimiter = oRs("fac_import_app_delimiter").Value || ";";
    var procprefix = oRs("fac_import_app_prefix").value||"FAC";
    var charset = oRs("fac_import_app_charset").value;
    if (params.stylesheet)  // vanuit de API bv, die is sterker
        var xslfile = params.fac_home + "CUST/" + params.customerId + "/" + params.stylesheet;
    else
        var xslfile = oRs("fac_import_app_xsl").value?params.fac_home + "CUST/" + params.customerId + "/" + oRs("fac_import_app_xsl").value:"";
    var folder = oRs("fac_import_app_folder").Value;
    oRs.Close();

    var backDir = params.is_stream ? null : backupfolder(import_app, folder, import_key);
    // Registeren (ook al doen we er verder nog niets mee)
    sql = "UPDATE fac_import"
        + "   SET fac_import_backupdir = " + safe.quoted_sql(backDir)
        + " WHERE fac_import_key = " + import_key;
    Oracle.Execute(sql);

    if (backDir && params.keep_backup)
    {
        var oZIP = new ActiveXObject("SLNKDWF.Zip");
        oZIP.New(backDir + safe.filename(params.filepathname) + ".zip");
        fileStream.Position = 0;
        oZIP.ZipFromStream(params.filepathname, fileStream);
        oZIP.Close();
    }

    if( !fileStream.Size )   // Leeg bestand?
    {
      return { success: false,
               backDir: backDir, // Ongeldig bestand wel weg-moven
               warning: "Import error: file is empty" };
    }

    // Altijd naar win1252 converteren, dat heeft onze database ook
    fileStream.Position = 0;
    fileStream.Type = 2; // adTypeText nu
    fileStream.Charset = charset || 'Windows-1252'; // de bron

    var win1252Stream = new ActiveXObject("ADODB.Stream");
    win1252Stream.Open();
    win1252Stream.CharSet = "Windows-1252"; // het doel
    fileStream.copyTo(win1252Stream);
    //win1252Stream.SaveToFile(Server.MapPath("./win1252Stream.txt"), 2);

    if (xslfile)
    {
      // Zet xml om in string
      __Log("XML2STR: with XSL: " + xslfile);

      // Load the XML
      var source = new ActiveXObject("MSXML2.DOMDocument.6.0");
      source.async = false;
      source.resolveExternals = false; // XML-data vertrouwen we niet altijd
      source.validateOnParse = false; // maar dan kun je geen DTD's meer controleren
      source.setProperty("ProhibitDTD", false);  // niet moeilijk doen over DTD's

      var fileData; // Hier gaan we de data als string in stoppen
      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);
      }
      if( ! source.loadXML(sOut) )   // Het bestand laden
      {
        return { success: false,
                 backDir: backDir, // Ongeldig bestand wel weg-moven
                 warning: "XML error: " + source.parseError.reason + " @ " + source.parseError.line + "." + source.parseError.linepos };
      }

      // Load the XSLT
      var style = new ActiveXObject("MSXML2.DOMDocument.6.0");
      style.async = false;
      style.resolveExternals = true; // XSL kan includes hebben
      style.validateOnParse = true; // en moet correct zijn
      style.setProperty("ProhibitDTD", false);  // niet moeilijk doen over DTD's
      if( ! style.load(xslfile) ) // De stylesheet laden
      {
        return { success: false,
                 // backDir: backDir, // het import bestand is gewoon geldig dus laten staan tot iemand de xsl-error oplost
                 warning: xslfile + "\nXSL error: " + style.parseError.reason + " @ " + style.parseError.line + "." + style.parseError.linepos };
      }
      fileData = source.transformNode(style); // terugstoppen in de fileData string
      // of?:    xslproc.output = win1252Stream;
      //         xslproc.transform();

    }
    else // Geen XSL, hij is 'perfect' aangeleverd.
    {
      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 fileData = "";
      while (!win1252Stream.EOS)
      {
          fileData = fileData + win1252Stream.ReadText(ReadBytes);
      }
    }

    //
    // fileData bevat nu de geimporteerde file. Wat gaan we er mee doen
    // A) Naar Oracle (via table fac_imp_file) en daar laten verwerken
    // B) Doorsturen naar SOAP service (want daar hebben we al afhandeling gebouwd)
    //

    var read_lines = -1;
    // data naar de database naar FAC_IMP_FILE
    teller = 1;
    var sqls = [];
    //var fileDataArr = fileData.split(/[\r\n]/); // gaat mis bij CSV's met newline binnen quotes
    var fileDataArr = fileData.split(/\x0D\x0A/); // CR LF is verplicht. Unix heeft alleen LF en dat ondersteunen we niet
                                                  // (alleen LF kan wel binnen quotes voor een newline in een veld) FSN#18597
    if (fileDataArr.length == 1)
        var fileDataArr = fileData.split(/\x0A/); // Misschien alleen UNIX LF's?

    if (fileDataArr.length == 1)
        var fileDataArr = fileData.split(/\x0D/); // Misschien alleen MAC CR's?

    if (import_csv)
    {   // Importregel in fac_imp_csv opslaan per kolom.
        for (var regel in fileDataArr)
        {
            var line = fileDataArr[regel];
            if (line != "")
            {
                var cols = parseCSVLine(line, import_delimiter);
                var colsName = ["fac_import_key", "fac_imp_csv_index"];
                var safe_colsValue = [import_key, (teller)];
                for (var c=1; c<=cols.length;c++)
                {
                    if (c <= 99) // Maximaal aantal kolommen
                    {
                        colsName.push("fac_imp_csv_col" + ("00" + c).slice(-2));
                        safe_colsValue.push(safe.quoted_sql(cols[c-1]));
                        if (cols[c-1].length > 4000)
                        {
                            //__Log(cols[c-1].length);
                            //__Log(cols[c-1]);
                            sql = "BEGIN fac.imp_writelog (" + import_key + ","
                                +                          "'E',"
                                +                          safe.quoted_sql(L("lcl_imp_oversized_column").format(teller, c)) + ", NULL);"
                                + "END;"
                            Oracle.Execute(sql);
                            return { success: false,
                                     backDir: backDir, // Ongeldig bestand wel weg-moven
                                     warning: "Import error: " + L("lcl_imp_oversized_column").format(teller, c) };
                        }
                    }
                }
                var insertSql = "INSERT INTO fac_imp_csv"
                              + "( " + colsName.join(",") + ")"
                              + "VALUES"
                              + "(" + safe_colsValue.join(",") + ")";
                Oracle.Execute(insertSql);
                teller++;
            }
        }
    }
    else
    {   // Importregel in fac_imp_file opslaan.
        var sqllen = 0;
        for (var regel in fileDataArr)
        {
            var line = fileDataArr[regel];
            if (line != "")
            {
                if (line.length > 4000) {
                    sql = "BEGIN fac.imp_writelog (" + import_key + ","
                        +                          "'E',"
                        +                          safe.quoted_sql(L("lcl_imp_oversized_line").format(teller)) + ", NULL);"
                        + "END;"
                    Oracle.Execute(sql);
                    return { success: false,
                             backDir: backDir, // Ongeldig bestand wel weg-moven
                             warning: "Import error: " + L("lcl_imp_oversized_line").format(teller) };
                }
                sql = teller + ", " + safe.quoted_sql(line) + "," + import_key;
                sqllen += sql.length + 28; // grove schatting
                sqls.push(sql);
                teller = teller + 1;
            }
            if (sqllen > 10000) // empirisch bepaald: fatsoenlijke performance
            {
                var insertSql = "INSERT INTO fac_imp_file"
                              + " (fac_imp_file_index, fac_imp_file_line, fac_import_key) SELECT "
                              +  sqls.join(" FROM DUAL UNION ALL SELECT ") + " FROM DUAL";
                Oracle.Execute(insertSql);
                sqls = [];
                sqllen = 0;
            }
        }
        if (sqls.length) // de laatste
        {
            Oracle.Execute("INSERT INTO fac_imp_file"
                        + " (fac_imp_file_index, fac_imp_file_line, fac_import_key) SELECT "
                        +  sqls.join(" FROM DUAL UNION ALL SELECT ") + " FROM DUAL");
        }
    }
    __Log("Ingelezen regels: " + fileDataArr.length);

    var importProc = procprefix + "_IMPORT_" + import_app;
    var sql = "SELECT DISTINCT object_name"
            + "  FROM USER_OBJECTS"
            + " WHERE object_type = 'PROCEDURE'"
            + "   AND object_name = " + safe.quoted_sql(importProc.toUpperCase());
    var oRs = Oracle.Execute(sql);
    if (oRs.eof)
    {   //__Log("Optional import procedure not found: " + importProc);
        oRs.Close();
        var read_lines = teller-1;
    }
    else
    {
        // De _IMPORT_ proc zal doorgaans de data van FAC_IMP_FILE of FAC_IMP_CSV naar bijv. FAC_IMP_CATALOGUS overzetten
        // De latere _UPDATE_ proc zal zo'n FAC_IMP_CATALOGUS dan echt verwerken
        // (sommige _UPDATE_ procs werken rechtstreekd op FAC_IMP_CSV maar moeten dan nog steeds een dummy _IMPORT_ hebben)
        var extra_params = (params.proc_params ? params.proc_params : "");
        sql = "BEGIN " + importProc + "(" + import_key + extra_params + "); END;"
        Oracle.Execute(sql);

        var read_lines = teller-1;
        var summarytxt = "";
        if (read_lines != 2) // uitsluitend header + 1 dataregel komt bij klanten die PRSIMP per persoon doen duizenden keren per dag voor
        {
            var summarytxt = "Ingelezen regels: " + read_lines;
        }

        var read_lines = teller-1;

        if (summarytxt)
        {
            sql = "BEGIN fac.imp_writelog (" + import_key + ","
            +                       "'S',"
            +                       safe.quoted_sql(summarytxt) + ", NULL);"
            + "END;"
            Oracle.Execute(sql)
        }

    }
    return { success: true, import_key: import_key, backDir: backDir, read_lines: read_lines };
}

// import_key bevat een te verwerken key
// params: customerId: "UWVA"
function impProcessStream(import_key, params)
{
    var result = {message: "", success: false};
    var now_sql = "SELECT TO_CHAR (SYSDATE, 'DD-MM-YYYY  HH24:MI:SS') now FROM DUAL";
    var sql = "SELECT COALESCE (MAX (imp_log_key), -1) imp_log_key"
            + "  FROM imp_log"
            + " WHERE fac_import_key = " + import_key;
    var oRs = Oracle.Execute(sql);
    var last_imp_log_key = oRs("imp_log_key").Value;
    oRs.Close();

    var sql  = "SELECT ia.fac_import_app_key"
             + ",      fac_import_app_code"
             + ",      fac_import_app_prefix"
             + ",      fac_import_app_folder"
             + ",      fac_import_filenaam"
             + "  FROM fac_import_app ia"
             + ",      fac_import i"
             + " WHERE ia.fac_import_app_key = i.fac_import_app_key"
             + "   AND i.fac_import_key = " + import_key;
    var oRs = Oracle.Execute(sql);
    var import_app_key = oRs("fac_import_app_key").Value;
    var impcode = oRs("fac_import_app_code").Value;
    var prefix = oRs("fac_import_app_prefix").Value||"FAC";
    var filenaam = oRs("fac_import_filenaam").Value;
    var folder = oRs("fac_import_app_folder").Value;
    oRs.Close();

    // Start de UPDATE procedure om de database aan te passen
    var extra_params = (params.proc_params ? params.proc_params : "");
    sql = "BEGIN "
        + prefix + "_UPDATE_" + impcode
        + "(" + import_key + extra_params + ");"
        + " END;"

    Oracle.Execute(sql);

    // Optioneel kan er nog klantspecifiek wat achteraan komen in geval van Catalogus-import
    // Bijv. NFIB#21853
    if (impcode == 'CATALOGUS')
    {
        var sql = "SELECT object_type"
                + "  FROM user_objects"
                + " WHERE object_name = " + safe.quoted_sql(customerId + "_UPDATE_CATALOGUS");
        var oRs = Oracle.Execute(sql);

        if (!oRs.eof && oRs(0).Value == "PROCEDURE")
        {   // Geldige aanwezig, roep aan
            sql = "BEGIN "
                + customerId + "_UPDATE_CATALOGUS"
                + "(" + import_key + ");"
                + " END;"
            Oracle.Execute(sql);
        }
        oRs.Close();
    }

    // Nu kijken of we nog iets met flexbijlagen moeten doen
    //
    // LET OP(?): Vanuit gen_import.wsf hebben we geen S() noch allerlei
    //            andere functies die we hier gebruiken!!
    //            Dit werkt dus (vooralsnog) alleen vanuit de GUI en via SOAP!!
    //

    var flexcode = null;
    var sql = "SELECT fac_result_waarde"
            + "  FROM fac_result"
            + " WHERE fac_result_naam = 'flexcode'"
            + "   AND fac_result_sessionid = 'IMPORT:{0}'".format(import_key);
    var oRs = Oracle.Execute(sql);
    if (!oRs.Eof)
    {   // melding S undefined? We ondersteunen dit nog niet vanuit gen_import.wsf
        flexcode = oRs("fac_result_waarde").Value;
    }
    oRs.Close();
//flexcode = "MLD:995837:544:M";
    if (flexcode) // FIN:12345:18:F (12345=factuurkey, 18=kenmerkkey, F=factuur (versus R=regels)
    {
        if (!folder)
            abort_with_warning("IMPORT CONFIGURATION ERROR:\nflexcode result but FAC_IMPORT_APP_FOLDER is not set");

        if (!this.flexProps)
            INTERNAL_ERROR_FLEXCODE; // Niet ondersteund vanuit gen_import.wsf

        var backDir = backupfolder(impcode, folder, import_key);
        var res = extractAttachments(backDir + safe.filename(filenaam) + ".zip", safe.filename(filenaam), flexcode);
        if (!res.success)
            return res;
    }

    sql = "UPDATE fac_import"
        + "   SET fac_import_datum_verwerkt = SYSDATE"
        + (params.user_key > 0 ? ", prs_perslid_key = " + params.user_key : "")
        + " WHERE fac_import_key = " + import_key
        + "   AND fac_import_datum_verwerkt IS NULL";
    Oracle.Execute(sql);

    // Verder opruimen van oude data doen we in fac.fac_cleanup die elke dag uitgevoerd wordt dmv fac.fac_daily

    result.message = (typeof L != "undefined"?L("lcl_imp_inf_processed"):"Ingelezen gegevens zijn verwerkt"); // Geen L vanuit gen_import.wsf!
    result.success = true;

    var sql = "SELECT COUNT(*)"
            + "  FROM imp_log"
            + " WHERE imp_log_status = 'W'"
            + "   AND imp_log_key > " + last_imp_log_key
            + "   AND fac_import_key = " + import_key;
    var oRs = Oracle.Execute(sql);
    var cntW = oRs(0).Value;
    oRs.Close();
    if (cntW > 0) {
        result.message += "\n" + (typeof L != "undefined" ? L("lcl_imp_rap_w") : "Waarschuwingen") + ": " + cntW;
    }
    var sql = "SELECT COUNT(*)"
            + "  FROM imp_log"
            + " WHERE imp_log_status = 'E'"
            + "   AND imp_log_key > " + last_imp_log_key
            + "   AND fac_import_key = " + import_key;
    var oRs = Oracle.Execute(sql);
    var cntE = oRs(0).Value;
    oRs.Close();
    if (cntE > 0) {
        result.message += "\n" + (typeof L != "undefined" ? L("lcl_imp_rap_e") : "Fouten") + ": " + cntE;
    }

    return result;
}

function backupfolder(import_app, folder, import_key)
{
    if (!folder)
        return null;

    // <folder>\BACKUP<jaar>-<maand>\<import_app>\<fulldate>_<import_key>
    var fso = new ActiveXObject("Scripting.FileSystemObject");
    if (!fso.FolderExists(folder))
        abort_with_warning("Import error: backup folder {0} not found on server".format(folder));

    var jsDate = new Date();

    folder = folder.replace("/", "\\");
    if (folder.substr(folder.length-1) != "\\")
        folder += "\\";
    var backDir = folder + "BACKUP" + jsDate.getFullYear() + "-" + padout(jsDate.getMonth() + 1) + "\\";
    if (!fso.FolderExists(backDir))
        fso.CreateFolder(backDir);

    backDir += import_app + "\\";
    if (!fso.FolderExists(backDir))
        fso.CreateFolder(backDir);

    var dtstring = padout(jsDate.getFullYear())  + "-" + padout(jsDate.getMonth() + 1) + "-" + padout(jsDate.getDate());

    backDir += dtstring + "_" + import_key + "\\";
    if (!fso.FolderExists(backDir))
        fso.CreateFolder(backDir);

    return backDir;
}

function XMLval(xml, tag)
{
    if (!xml)
        return null;
    var xx = xml.getElementsByTagName(tag);
    if (!xx || !xx.length)
        return null;
    xx = xx[0].childNodes
    if (!xx || !xx.length)
        return null;
    return xx[0].nodeValue;
}

function add2bijlagen(pModule, pKenmerkKey, pKey, pSafefilename, params)
{
    var fso = new ActiveXObject("Scripting.FileSystemObject");
    var oCrypto = new ActiveXObject("SLNKDWF.Crypto");
    var sql = "BEGIN "
            + "  flx.setflexbijlage"
                    + "(" + safe.quoted_sql(pModule)    // MLD | RES | ....
                    + ", " + pKenmerkKey
                    + ", " + pKey
                    + ", " + safe.quoted_sql(params.AttachSubPath)
                    + ", " + "NULL" // diskfilename
                    + ", " + safe.quoted_sql(pSafefilename)
                    + ", " + fso.GetFile(params.AttachPath + pSafefilename).Size
                    + ", " + "SYSDATE"
                    + ", " + safe.quoted_sql(oCrypto.hex_sha1_file(params.AttachPath + pSafefilename))
                    + ");"
            + " END;";
    Oracle.Execute(sql);
}

function extractAttachments(safeSourceXML, filename, flexcode)
{
    __Log("Attachments uit {0} extraheren en opslaan volgens {1}".format(safeSourceXML, flexcode));
    var arr = flexcode.split(":"); // FIN:12345:18:F
    var pModule = arr[0];
    if (arr.length > 1)
        var pKey = parseInt(arr[1], 10);
    if (arr.length > 2)
        var pKenmerkKey = parseInt(arr[2], 10);
    if (arr.length > 3)
        var pNiveau = arr[3];
    var params = flexProps(pModule, pKey, pKenmerkKey, pNiveau);

    //fileStream.CharSet = charset; // Vroeg, anders krijgen we een BOM?
    var oStream = new ActiveXObject("ADODB.Stream");
    oStream.Type = 1; //adTypeBinary
    oStream.Open;
    var oZIP = new ActiveXObject("SLNKDWF.Zip");
    oZIP.Open(safeSourceXML);
    oZIP.UnzipToStream(oStream, filename);

    var xmldoc = new ActiveXObject("Msxml2.DOMDocument.6.0");
    xmldoc.async = false;
    xmldoc.resolveExternals = false; // XML-data vertrouwen we niet altijd
    xmldoc.validateOnParse = false; // maar dan kun je geen DTD's meer controleren
    xmldoc.setProperty("ProhibitDTD", false);  // niet moeilijk doen over DTD's

    oStream.Position = 0;
    if( ! xmldoc.load(oStream) ) // De XML terug laden
    {
      return { success: false, warning: filename + "\nXML error: " + xmldoc.parseError.reason + " @ " + xmldoc.parseError.line + "." + xmldoc.parseError.linepos };
    }
    oZIP.Close();
    oStream.Close();
    //
    //  Dit is het enige formaat wat we (hardcoded) ondersteunen. DocumentType en FileType negeren we
    //	<Attachment>
    //      <AttachedData>JVBERi0xLjQKJcfsjCi9<....></AttachedData>
    //	    <DocumentType>FAC</DocumentType>
    //	    <FileType>PDF</FileType>
    //	    <FileName>0123456789123_201403159.pdf</FileName>
    //  </Attachment>


    var Attachments = xmldoc.getElementsByTagName("Attachment");
    var nr_att_1 = Attachments.length;
    __Log("Variabele nr_att_1: " + nr_att_1);
    for (var i=0; i < Attachments.length; i++)
    {
        var safefilename = safe.filename(XMLval(Attachments[i], "FileName"));
        if (!params.isAllowedName(safefilename))
        {
            // TODO: Misschien ook terugkoppelen in IMP_LOG?
            __Log("Onbekende extensie upload bijlage: {0}. Bestand is niet opgeslagen.".format(safefilename));
        }
        else
        {
            __Log('Bijlage {0} mag naar {1}'.format(safefilename, params.AttachPath));
            var AttachmentData = XMLval(Attachments[i], "AttachedData");
            if (AttachmentData)
            {
                CreateFullPath(params.AttachPath);
                encodedString2File(params.AttachPath + safefilename, AttachmentData, "bin.base64");
                add2bijlagen(pModule, pKenmerkKey, pKey, safefilename, params)
                __Log("Done saving: " + params.AttachPath + safefilename);
            }
            else
                __Log("Empty file skipped: " + safefilename);
        }
    }
    if (nr_att_1 == 0){
      xmldoc.setProperty ("SelectionNamespaces", "xmlns:sales005='http://www.ketenstandaard.nl/onderhoudsopdracht/SALES/005'");
      var Attachments = xmldoc.selectNodes("//sales005:Attachment");
      var nr_att_1 = Attachments.length;
      __Log("Variabele nr_att_1: " + nr_att_1);
      for (var i=0; i < Attachments.length; i++)
      {
          var l_filenaam_node = Attachments[i].selectSingleNode("sales005:FileName");
          var l_base64_node = Attachments[i].selectSingleNode("sales005:AttachedData");
          if (l_filenaam_node && l_base64_node)
          {
            __Log("XML filename: " + l_filenaam_node.text);
            __Log("XML base64: " + l_base64_node.text);

            var safefilename = safe.filename(l_filenaam_node.text);
            __Log("safefilename: " + safefilename);
            if (!params.isAllowedName(safefilename))
            {
                // TODO: Misschien ook terugkoppelen in IMP_LOG?
                __Log("Onbekende extensie upload bijlage: {0}. Bestand is niet opgeslagen.".format(safefilename));
            }
            else
            {
                __Log('Bijlage {0} mag naar {1}'.format(safefilename, params.AttachPath));
                var AttachmentData = l_base64_node.text;
                if (AttachmentData)
                {
                    CreateFullPath(params.AttachPath);
                    encodedString2File(params.AttachPath + safefilename, AttachmentData, "bin.base64");
                    add2bijlagen(pModule, pKenmerkKey, pKey, safefilename, params)
                    __Log("Done saving: " + params.AttachPath + safefilename);
                }
                else
                    __Log("Empty file skipped: " + safefilename);
            }
          }
      }
    }


    // En ook:
    // <cac:AdditionalDocumentReference>
    //     <cbc:ID>V1700746.pdf</cbc:ID>
    //     <cbc:DocumentType>PrimaryImage</cbc:DocumentType>
    //     <cac:Attachment>
    //         <cbc:EmbeddedDocumentBinaryObject mimeCode="application/pdf">
    //           ... base64 ...
    //         </cbc:EmbeddedDocumentBinaryObject>
    //     </cac:Attachment>
    // </cac:AdditionalDocumentReference>
    //
    // filename kan ook opgegeven zijn via:
    //     <cbc:EmbeddedDocumentBinaryObject mimeCode="application/pdf" filename="1205_17180793_03002557.pdf">

    var Attachments = xmldoc.getElementsByTagName("cac:AdditionalDocumentReference");
    var nr_att_2 = Attachments.length;
    __Log("Variabele nr_att_2: " + nr_att_2);
    for (var i=0; i < Attachments.length; i++)
    {
        // Eerst maar eens het attribute 'filename' zoeken
        var filename = "";
        var Attachment = Attachments[i].getElementsByTagName("cbc:EmbeddedDocumentBinaryObject");
        if (Attachment.length)
           filename = Attachment[0].getAttribute("filename");
        if (!filename)
        {   // Als geen filenaam-attribuut aanwezig, dan proberen we element ID
          filename = XMLval(Attachments[i], "cbc:ID");
        }
        if (filename &&
            !filename.match(/\.pdf$/i) &&
            Attachment.length &&
            Attachment[0].getAttribute("mimeCode") == "application/pdf")
        {
            filename += ".pdf";
        }
        var safefilename = safe.filename(filename);
        if (!params.isAllowedName(safefilename))
        {
            // TODO: Misschien ook terugkoppelen in IMP_LOG?
            __Log("Onbekende extensie upload bijlage: {0}. Bestand is niet opgeslagen.".format(safefilename));
        }
        else
        {
            __Log('Bijlage {0} mag naar {1}'.format(safefilename, params.AttachPath));
            var AttachmentData = XMLval(Attachments[i], "cbc:EmbeddedDocumentBinaryObject");
            if (AttachmentData)
            {
                CreateFullPath(params.AttachPath);
                encodedString2File(params.AttachPath + safefilename, AttachmentData, "bin.base64");
                add2bijlagen(pModule, pKenmerkKey, pKey, safefilename, params)
                __Log("Done saving: " + params.AttachPath + safefilename);
            }
            else
                __Log("Empty file skipped: " + safefilename);
        }
    }

    // MARX#49027: PDF uit XMLfactuur (harde node) werkt niet bij INSBOU004,
    // default namespace icm method getElementsByTagName werkt niet,
    // vervangen door selectSingleNode en expliciet property 'SelectionNamespaces' zetten
    if (nr_att_1 == 0 && nr_att_2 == 0)
    {
        xmldoc.setProperty ("SelectionNamespaces", "xmlns:insbou4='http://www.gs1.nl/factuur/insbou/004'");
        var l_filenaam_node = xmldoc.selectSingleNode("//insbou4:Attachment/insbou4:FileName");
        var l_base64_node = xmldoc.selectSingleNode("//insbou4:Attachment/insbou4:AttachedData");
        // MARX#55904: PDF uit XMLfactuur (harde node) werkt niet bij SALES005
        if (!l_filenaam_node || !l_base64_node){
          xmldoc.setProperty ("SelectionNamespaces", "xmlns:sales005='http://www.ketenstandaard.nl/factuur/SALES/005'");
          var l_filenaam_node = xmldoc.selectSingleNode("//sales005:Attachment/sales005:FileName");
          var l_base64_node = xmldoc.selectSingleNode("//sales005:Attachment/sales005:AttachedData");
        }
        if (l_filenaam_node && l_base64_node){
            var l_filenaam_text = l_filenaam_node.text;
            __Log("Variabele l_filenaam_text: " + l_filenaam_text);
            var l_base64_text = l_base64_node.text;
            __Log("Variabele l_base64_text: " + l_base64_text);

            var safefilename = safe.filename(l_filenaam_text);
            if (!params.isAllowedName(safefilename))
            {
                // TODO: Misschien ook terugkoppelen in IMP_LOG?
                __Log("Onbekende extensie upload bijlage: {0}. Bestand is niet opgeslagen.".format(safefilename));
            }
            else
            {
                __Log('Bijlage {0} mag naar {1}'.format(safefilename, params.AttachPath));
                var AttachmentData = l_base64_text;
                if (AttachmentData)
                {
                    CreateFullPath(params.AttachPath);
                    encodedString2File(params.AttachPath + safefilename, AttachmentData, "bin.base64");
                    add2bijlagen(pModule, pKenmerkKey, pKey, safefilename, params)
                    __Log("Done saving: " + params.AttachPath + safefilename);
                }
                else
                    __Log("Empty file skipped: " + safefilename);
            }
        }
        else{
            //51764: PDF attachment in CDATA (ketenstandaard MessageSevice) ?
            xmldoc.setProperty ("SelectionNamespaces", "xmlns:mes='http://etim.nl/xmlschemas/messageservice/2.40'");
            var l_cdata_node = xmldoc.selectSingleNode("//mes:MsgContent");
            if (! l_cdata_node){
                __Log("Cdata MessageService/2.4 NOT found");
                xmldoc.setProperty ("SelectionNamespaces", "xmlns:mes='https://www.ketenstandaard.nl/WS/MessageService/3.0'");
                var l_cdata_node = xmldoc.selectSingleNode("//mes:MsgContent");
            }
            if (! l_cdata_node){
                __Log("Cdata MessageService/2.4 AND 3.0 NOT found");
                xmldoc.setProperty ("SelectionNamespaces", "xmlns:mes='https://www.ketenstandaard.nl/WS/MessageService/3.1'");
                var l_cdata_node = xmldoc.selectSingleNode("//mes:MsgContent");
            }
            if (! l_cdata_node){
                __Log("Cdata MessageService/2.4 AND 3.0 AND 3.1 NOT found");
                var l_cdata_node = xmldoc.selectSingleNode("//MsgContent");
            }
            if (l_cdata_node){
                __Log("Cdata found: " + l_cdata_node.text);
                xmldoc.loadXML("" + l_cdata_node.text.replace(/^\n*/, '')); // Leading \n er af voor als er een xml-declaratie vooraan staat.
                if (xmldoc.parseError.errorCode != 0)
                {
                    __DoLog("\nXML error in CDATA: " + xmldoc.parseError.reason + " @ " + xmldoc.parseError.line + "." + xmldoc.parseError.linepos);
                    return { success: false, warning: filename + "\nXML error in CDATA: " + xmldoc.parseError.reason + " @ " + xmldoc.parseError.line + "." + xmldoc.parseError.linepos };
                }

                __Log("l_cdata_node loaded in xmldoc");
                var l_filenaam_node = xmldoc.selectSingleNode("//Attachment/FileName");
                var l_base64_node = xmldoc.selectSingleNode("//Attachment/AttachedData");
                if (!l_filenaam_node || !l_base64_node){
                    xmldoc.setProperty ("SelectionNamespaces", "xmlns:insbou4='http://www.gs1.nl/factuur/insbou/004'");
                    var l_filenaam_node = xmldoc.selectSingleNode("//insbou4:Attachment/insbou4:FileName");
                    var l_base64_node = xmldoc.selectSingleNode("//insbou4:Attachment/insbou4:AttachedData");
                }
                // MARX#55904: PDF uit XMLfactuur (harde node) werkt niet bij SALES005
                if (!l_filenaam_node || !l_base64_node){
                  xmldoc.setProperty ("SelectionNamespaces", "xmlns:sales005='http://www.ketenstandaard.nl/factuur/SALES/005'");
                  var l_filenaam_node = xmldoc.selectSingleNode("//sales005:Attachment/sales005:FileName");
                  var l_base64_node = xmldoc.selectSingleNode("//sales005:Attachment/sales005:AttachedData");
                }
                if (l_filenaam_node && l_base64_node){
                    var l_filenaam_text = l_filenaam_node.text;
                    __Log("Variabele l_filenaam_text: " + l_filenaam_text);
                    var l_base64_text = l_base64_node.text;
                    __Log("Variabele l_base64_text: " + l_base64_text);
                    var safefilename = safe.filename(l_filenaam_text);
                    if (!params.isAllowedName(safefilename))
                    {
                        // TODO: Misschien ook terugkoppelen in IMP_LOG?
                        __Log("Onbekende extensie upload bijlage: {0}. Bestand is niet opgeslagen.".format(safefilename));
                    }
                    else
                    {
                        __Log('Bijlage {0} mag naar {1}'.format(safefilename, params.AttachPath));
                        var AttachmentData = l_base64_text;
                        if (AttachmentData)
                        {
                            CreateFullPath(params.AttachPath);
                            encodedString2File(params.AttachPath + safefilename, AttachmentData, "bin.base64");
                            add2bijlagen(pModule, pKenmerkKey, pKey, safefilename, params)
                            __Log("Done saving: " + params.AttachPath + safefilename);
                        }
                        else
                            __Log("Empty file skipped: " + safefilename);
                    }
                }
            }
            else{
                __Log("Cdata NOT found");
            }
        }
    }

    return { success: true };
}
