/*
    $Id$

    File:           EventHandlers.js
    Description:    Events voor hMailserver

    Context:        Bij binnenkomende (en ook uitgaande!) e-mail
    Result:         De globale Result van hMailserver is gezet
    Note:           Doorgaans te plaatsen in "c:\Program Files (x86)\hMailServer\Events"
*/

var SXH_PROXY_SET_PROXY = 2;
function OnAcceptMessage(oClient, oMessage)
{
    var i;
    var anySuccess = false;
    Result.Value = 0;
    EventLog.write("=============================");
    // Er kunnen meerdere Recipients zijn en zelfs meerdere geldige
    // Maar pas op: als ze allemaal terugvallen op catchall telt het slechts als één!!!
    // Daarom moet je, als je dit wilt ondersteunen, voor alle adressen toch een eigen
    // account aanmaken in hMailserver!
    for (i=0; i < oMessage.Recipients.Count; i++)
    {
        if (i > 0)
            EventLog.write("----- Recipient number " + (i+1));
        OnAcceptMessageTo(oClient, oMessage, i);
        anySuccess = anySuccess || Result.Value == 0;
    }
    if (anySuccess) // als er minstens eentje lukt klagen we niet
        Result.Value = 0;
}

function rejectMessage(msg, debugmsg)
{
    EventLog.write("Rejecting with: " + msg);
    if (debugmsg)
    {
        EventLog.write(debugmsg);
        if (settings.otap_environment == "O")
        {
            msg += "== OTAP only ==\n" + debugmsg; // in OTAP=="O" ook terug in de e-mail response
        }
    }
    Result.Message = msg.replace(/\n/g, "#"); // er lijken geen newlines in te mogen zitten
    Result.Value = 2; // reject
}

var settings;
// Verwerk één bericht
function OnAcceptMessageTo(oClient, oMessage, RecipientNr)
{
    try
    {
        var v_errorhint = "getting message";
        var i;
        var fromAddr = oMessage.FromAddress;
        var from = oMessage.From;
        // var toAddr = oMessage.Recipients.Item(RecipientNr).Address; // dit zal catchall@.... zijn
        var toAddr = oMessage.Recipients.Item(RecipientNr).OriginalAddress; // heeft geen last van de catchall

        var cust = oMessage.HeaderValue("X-FCLT-cust");
        if (RecipientNr == 0)
        {
            if (cust) // van Putorders afkomstig
            {
                EventLog.write(cust + " " + oMessage.HeaderValue("X-FCLT-noticode") + " " + oMessage.HeaderValue("X-FCLT-fcltid"));
            }
            EventLog.write("From: " + from);
            if (fromAddr != from && "<" + fromAddr + ">" != from)
                EventLog.write("FromAddr: " + fromAddr);
        }
        EventLog.write("To: " + toAddr);
        if (oMessage.Size >= 1000) // is misschien het probleem
            EventLog.write("Size (kB): " + oMessage.Size);

        var domain = toAddr.split("@")[1].toLowerCase(); // Bijvoorbeeld xxxx.facilitor.nl. Daar doen we de API-call ook naar toe

        var org_emlpath = oMessage.Filename;
        var eml_filesize = eml_zonder_attachment(org_emlpath);
        var eml_filename = org_emlpath.split("\\").pop();
        var eml_filepath = org_emlpath.replace(eml_filename, "");

        var v_errorhint = "building XML";
        var xmlDoc = new ActiveXObject("MSXML2.DOMDocument.6.0");
        xmlDoc.appendChild(xmlDoc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\""))

        rootElement = xmlDoc.createElement("facilitor");
            elementMail = xmlDoc.createElement("mail");
                // velden uit https://www.hmailserver.com/documentation/v4.4/?page=com_message
                addtext(xmlDoc, elementMail, "subject",  oMessage.Subject);
                addtext(xmlDoc, elementMail, "from",     from);
                addtext(xmlDoc, elementMail, "fromaddr", fromAddr);
                addtext(xmlDoc, elementMail, "to",       toAddr);
                addtext(xmlDoc, elementMail, "RecipientNr", RecipientNr);
                addtext(xmlDoc, elementMail, "body",     oMessage.Body);
                addtext(xmlDoc, elementMail, "htmlbody", oMessage.HTMLBody);
                // Wat extra velden waar api_hmail (nog) niets mee doet maar mogelijk ooit wel
                addtext(xmlDoc, elementMail, "CC",       oMessage.CC);
                addtext(xmlDoc, elementMail, "originalto", oMessage.To); // Niet per definitie huidige Recipient
                addtext(xmlDoc, elementMail, "Charset",  oMessage.Charset);
                addtext(xmlDoc, elementMail, "Date",     oMessage.Date);
                addtext(xmlDoc, elementMail, "Size",     oMessage.Size); // Kilobyte trouwens

                addtext(xmlDoc, elementMail, "now",      new Date()); // For debugging
                addtext(xmlDoc, elementMail, "emlpath",  eml_filepath);
                addtext(xmlDoc, elementMail, "emlfile",  eml_filename);
                addtext(xmlDoc, elementMail, "emlsize",  eml_filesize);
                var spamscore = oMessage.HeaderValue("X-hMailServer-Spam"); // kan "YES" bevatten
                if (spamscore)
                {
                    addtext(xmlDoc, elementMail, "spamscore",  spamscore);
                    addtext(xmlDoc, elementMail, "spamreason1",  oMessage.HeaderValue("X-hMailServer-Reason-1"));
                    addtext(xmlDoc, elementMail, "spamreason2",  oMessage.HeaderValue("X-hMailServer-Reason-2"));
                    addtext(xmlDoc, elementMail, "spamreason3",  oMessage.HeaderValue("X-hMailServer-Reason-3"));
                    addtext(xmlDoc, elementMail, "spamreasonscore",  oMessage.HeaderValue("X-hMailServer-Reason-Score"));
                }

                elementAttachments = xmlDoc.createElement("attachments");
                for (i=0; i < oMessage.Attachments.Count; i++)
                {
                    var fileName = oMessage.Attachments.Item(i).fileName;
                    var fileSize = oMessage.Attachments.Item(i).Size;
                    if (fileName.toLowerCase() == "winmail.dat")
                    {
                        EventLog.write("Let op: bijlagen ingepakt in winmail.dat (" + fileSize + " bytes) wordt niet ondersteund door Facilitor");
                    }
                    elementAttachment = xmlDoc.createElement("attachment");
                        addtext(xmlDoc, elementAttachment, "filename", fileName);
                        addtext(xmlDoc, elementAttachment, "size", String(fileSize));
                        // Of gewoon echt de attachments invoegen? api_mldsoap.asp kan ze al opvangen
                    elementAttachments.appendChild(elementAttachment);
                }
                elementMail.appendChild(elementAttachments);

            rootElement.appendChild(elementMail);
        xmlDoc.appendChild(rootElement);

        var v_errorhint = "reading configfile";

        var fso = new ActiveXObject("Scripting.FileSystemObject")

        // Zoek de ini-file in de hmail script folder
        // Soms(!)  Ensure that hMailServer.Application.Authenticate() is called with proper login credentials.
        //var hs = new ActiveXObject("hMailServer.Scripting")
        //EventLog.write("scd:" + hs.Directory); // geeft "An error occured processing the request"
        //var inifile = hs.CurrentScriptFile.replace(/\.js$/, ".config.js");
        var inifile = "c:\\Program Files (x86)\\hMailServer\\Events\\EventHandlers.config.js"
        if (!fso.FileExists(inifile))
            var inifile = "c:\\Program Files\\hMailServer\\Events\\EventHandlers.config.js"

        var f = fso.OpenTextFile(inifile, 1); // ForReading
        var configtxt = f.ReadAll();
        var config = eval('(' + configtxt + ')')
        f.Close();
        /*global*/ settings = config.settings || {};

        var cfgdata = config[domain];
        if (!cfgdata)
        {   // Gebeurt bij uitgaande e-mail
              if (!cust)
                  EventLog.write("Mail domain '" + domain + "' not configured. Outgoing?");
    //        EventLog.write("ini:" + inifile);
    //        Result.Message = "mail domain not configured";
    //        Result.Value = 2; // reject
            return;
        }
        cfgdata.errortemplate = cfgdata.errortemplate || "Facilitor mail processing error {0}";

        // API HMAIL moet wijzen naar appl/api/api_hmail.asp
        // Filename en Size alleen voor logging doeleinden
        var xmlurl = cfgdata.url + "&hmFilename=" + (oMessage.Filename.split("\\").pop())
                                 + "&hmSizekB=" + oMessage.Size;
        var v_errorhint = "sending API_HMAIL http_request " + xmlurl;

        var http_request = new ActiveXObject("WinHTTP.WinHTTPRequest.5.1");
        if (settings.proxy)
        {
            http_request.setProxy(SXH_PROXY_SET_PROXY, settings.proxy);
        }
        http_request.open("POST", xmlurl, false); // Synchroon
        http_request.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
        http_request.send(xmlDoc.xml);
        if (http_request.status != 200)
        {
            var debugmsg = "http_request failed: " + xmlurl
                         + "\n" + http_request.status + ": " + http_request.statusText
                         + "\n" + http_request.ResponseText;
            rejectMessage(cfgdata.errortemplate.format("2"), debugmsg);
            return;
        }
        var v_errorhint = "parsing json http response";
        var api_hmail_orgdata = http_request.ResponseText; // String bewaren omdat we geen JSON.stringify hebben
        var api_hmail_data = eval('(' + api_hmail_orgdata + ')');
        if (api_hmail_data.maillog)
            EventLog.write(api_hmail_data.maillog);
        if (api_hmail_data.silentignoreemail) // spam, automatisch antwoord, onbestelbaar
        {
            return; // Ik zou graag voorkomen dat hij lokaal wordt bewaard maar dat gebeurt alleen bij bounced berichten
        }

        if (!api_hmail_data.success)
        {
            rejectMessage(api_hmail_data.message, api_hmail_data.message);
            return;
        }
        // api_hmail.asp heeft bepaald wat veilige extensies zijn en waar ze naar toe moeten
        var v_errorhint = "saving attachments";
        for (i = 0; i < api_hmail_data.attachments.length; i++)
        {
            if (api_hmail_data.attachments[i].safeurl) // Bijvoorbeeld APPL\MARX\export\BCTAdapter.asp
            {
                var sequence = api_hmail_data.attachments[i].sequence; // de onveilige zijn er uit gestript, daarom niet per se i==sequence
                var tempfilename = api_hmail_data.attachments[i].tempfilename; // dit is een volledig pad

                var objFso = new ActiveXObject("Scripting.FileSystemObject");
                if (!tempfilename)
                {
                    var TemporaryFolder = 2;
                    tempfilename = objFso.GetSpecialFolder(TemporaryFolder) + "\\" + objFso.GetTempName();
                }

                EventLog.write("bijlage tijdelijk naar " + tempfilename);
                var oAttach = oMessage.Attachments.Item(sequence);
                oAttach.SaveAs(tempfilename);

                if (!api_hmail_data.isMulti) // individueel sturen
                {
                    var objStream = new ActiveXObject("ADODB.Stream");
                    objStream.Open();
                    objStream.Type = 1; // adTypeBinary
                    objStream.LoadFromFile(tempfilename);

                    var safefilename = api_hmail_data.attachments[i].safefilename;
                    var safeurl = api_hmail_data.attachments[i].safeurl;
                    safeurl = safeurl.replace("{filename}", encodeURIComponent(safefilename));
                    var sendres = sendAttachment(safeurl, objStream, api_hmail_data, api_hmail_data.attachments[i]);
                    objStream.close();
                    EventLog.write("bijlage " + safefilename + " (" + oAttach.Size + " bytes) naar " + safeurl);
                    if (!sendres.success)
                    {
                        rejectMessage(cfgdata.errortemplate.format("3"), sendres.debugmsg + "\nBijlage is nog te vinden op: " + tempfilename);
                        return;
                    }
                }
            }
            else // waarschijnlijk opslaan in een flexfiles- of importfolder
            {
                var sequence = api_hmail_data.attachments[i].sequence; // de onveilige zijn er uit gestript, daarom niet per se i==sequence
                var safefilename = api_hmail_data.attachments[i].safefilename;
                var oAttach = oMessage.Attachments.Item(sequence);
                EventLog.write(oAttach.fileName + ' bijlage (' + oAttach.Size + ' bytes) naar ' + safefilename);
                oAttach.SaveAs(safefilename);
            }
        }
        if (api_hmail_data.isMulti)
        {
            var v_errorhint = "Sending multi attachments";
            // stuur de originele JSON die we van API_HMAIL kregen in de body mee
            // Die bevat al mooi alle interessante info
            var sendres = sendAttachment(api_hmail_data.path, api_hmail_orgdata, api_hmail_data, {});
            if (!sendres.success)
            {
                rejectMessage(cfgdata.errortemplate.format("4"), sendres.debugmsg + "\nBijlagen zijn nog te vinden in de tempfolder");
                return;
            }
        }

        var v_errorhint = "clearing tempfiles";
        if (api_hmail_data.loglevel == 0)
        {
            for (i = 0; i < api_hmail_data.attachments.length; i++)
            {
                if (api_hmail_data.attachments[i].tempfilename)
                {
                    objFso.DeleteFile(api_hmail_data.attachments[i].tempfilename);
                }
            }
            EventLog.write("Tijdelijke bijlage(s) weer verwijderd");
        }

        var v_errorhint = "clearing attachments";
        oMessage.Attachments.Clear();
        oMessage.Save();
        if (api_hmail_data.original_mail_path)
        {
            var v_errorhint = "original e-mail to " + api_hmail_data.original_mail_path + " " + api_hmail_data.original_mail_file;
            copyEml(org_emlpath, api_hmail_data.original_mail_path, api_hmail_data.original_mail_file);
        }

        EventLog.write("E-mail accepted and processed");
    }
    catch(e)
    {
        rejectMessage(cfgdata.errortemplate.format("1"), "Message processing error when " + v_errorhint + ":\n" + e.description);
    }
}

function sendAttachment(safeurl, postdata, api_hmail_data, data_attachments)
{
    EventLog.write("POSTing to: "+safeurl);
    var http_request = new ActiveXObject("WinHTTP.WinHTTPRequest.5.1");
    if (settings.proxy)
    {
        http_request.setProxy(SXH_PROXY_SET_PROXY, settings.proxy);
    }
    var lResolve = 60 * 1000; // The default value is infinite, meaning no timeout.     DNS om ip-adres te vinden
    var lConnect = 60 * 1000; // With a default timeout value of 60 seconds             Opzetten https verbinding
    var lSend    = (settings.sendtimeout || 30) * 1000; // The default value is 30 seconds.     Upload data
    var lReceive = (settings.receivetimeout || 30) * 1000; // The default value is 30 seconds.  Server processing time en download data
    http_request.SetTimeouts(lResolve, lConnect, lSend, lReceive);

    http_request.open("POST", safeurl, false, api_hmail_data.username, api_hmail_data.password);
    if ("headers" in api_hmail_data)
    {
        for (var head in api_hmail_data.headers) // api_hmail.asp heeft onze headers bepaald
            http_request.setRequestHeader(head, api_hmail_data.headers[head]);
    }
    if (data_attachments && "headers" in data_attachments)
    {
        for (var head in data_attachments.headers) // api_hmail.asp heeft onze headers bepaald
            http_request.setRequestHeader(head, data_attachments.headers[head]);
    }

    var v_errorhint = "POSTing to " + safeurl;
    http_request.send(postdata);

    if (http_request.status < 200 || http_request.status > 299)
    {
        var debugmsg = "http_request failed: " + safeurl
                     + "\n" + http_request.status + ": " + http_request.statusText
                     + "\n" + http_request.ResponseText;

        EventLog.write(debugmsg);
        return { success: false, debugmsg: debugmsg };
    }
    EventLog.write("response = " + http_request.responseText);
    return { success: true };
}

function eml_zonder_attachment(p_emlpath)
{
    try
    {
        var stm = new ActiveXObject("ADODB.Stream");
        stm.Open();
        stm.LoadFromFile(p_emlpath);
        stm.Flush();

        var msg = new ActiveXObject("CDO.Message");
        msg.DataSource.OpenObject(stm, "_Stream");
        msg.Attachments.DeleteAll();
        msg.DataSource.Save();
        stm.Close();

        var stm = new ActiveXObject("ADODB.Stream");
        stm.Open();
        stm.Type = msg.GetStream().Type;
        stm.Charset = msg.GetStream().Charset;
        msg.DataSource.SaveToObject(stm, "_Stream");
        stm.SaveToFile(p_emlpath+"x");
        stm.Close();

        var objFso = new ActiveXObject("Scripting.FileSystemObject");
        var f = objFso.GetFile(p_emlpath+"x");
        var s = f.size;
        objFso.DeleteFile(p_emlpath+"x");
    }
    catch(e)
    {
        EventLog.write("Fout bij eml zonder attachment: " + e.description);
    }
    return s;
}

function copyEml(p_emlpath, p_orgpath, p_orgfile)
{
    var copyto = p_orgpath.replace(/\\/g, "/");
    copyto = (copyto.charAt(copyto.length-1) == "/" ? copyto.slice(0, copyto.length-1) : copyto);

    var objFso = new ActiveXObject("Scripting.FileSystemObject");
    try
    {
        EventLog.write("Eml kopieren van:  "+ p_emlpath +" naar: "+ copyto +"/"+ p_orgfile);
        objFso.CopyFile(p_emlpath, copyto+"/"+p_orgfile);
    }
    catch(e)
    {
        EventLog.write("Fout bij kopieren eml: " + e.description);
    }
}

// Logging van problemen in *uitgaande* e-mail
function OnDeliveryFailed(oMessage, sRecipient, sErrorMessage)
{
    EventLog.write("============================");
    EventLog.write("DeliveryFailed " + oMessage.HeaderValue("X-FCLT-cust") + " " + oMessage.HeaderValue("X-FCLT-noticode") + " "+ oMessage.HeaderValue("X-FCLT-fcltid"));

    EventLog.write("From: " + oMessage.From);
    EventLog.write("To: " + sRecipient);
    EventLog.write("Subject: " + oMessage.Subject);
    EventLog.write("Sent: " + oMessage.Date);
    if (oMessage.Size >= 1000) // is misschien het probleem
        EventLog.write("Size (kB): " + oMessage.Size);
    // De echte foutmelding:
    // er staat een chr(13)+chr(10) in de tekst die in de logging [nl] wordt.
    // Ik wil gewoon newlines, veel leesbaarder. Ook laatste newlines er af
    EventLog.write(sErrorMessage.replace(/\x0a/g, "").replace(/(\x0d)*$/g, ""));
}

function addtext(xmlDoc, elementParent, tag, tekst)
{
    tekst = String(tekst || "");
    // Haal alle lage bytes (muv TAB (x09), LF (x0A) en CR (x0D)) er uit. Die representeren
    // geen geldig karakter in Windows-1252 of iso-8859. Ook in UTF-8 staan ze niet voor
    // tekst, mochten we dat ooit willen gaan ondersteunen.
    tekst = tekst.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]+/g, "?");

    var elementTag = xmlDoc.createElement(tag);
        var elementText = xmlDoc.createTextNode(tekst);
        elementTag.appendChild(elementText);
    elementParent.appendChild(elementTag);
}

String.prototype.format = function() {
    var args = arguments;
    return this.replace(/\{(\d+)\}/gi, function (match, d) {
        return args.length > d ? args[d] : match;
    });
};
