/* $Revision$ $Id$ File: Exchange.js Description: Exchange EWS koppeling Dit bestand maakt voor één ruimte-mailbox Arguments(0) de xml-bestanden met calenderitems aan sinds de laatste sync-actie, Arguments(1) Parameters (0) e-mail adres van de zaal (1) "EXCHFULL" calendarview-import (in de toekomst tot config.fullfuture) alle andere waarden: synchronisatie-import (voorheen werd hier de syncstate in meegegeven) */ var fso = new ActiveXObject("Scripting.FileSystemObject"); var zaalemail = WScript.Arguments(0); var import_app_key = WScript.Arguments(1); // Gebruik EXCHFULL voor alles in bepaalde periode var inifile = ".\\exchange.config.js"; var f = fso.OpenTextFile(inifile, 1); // ForReading var config = eval('(' + f.ReadAll() + ')') f.Close(); config.loglevel = config.loglevel || 0; WScript.Echo("Connecting to " + config.endpointurl) // --- // kunnen we niets mee doExchange(config.endpointurl, "", "roomlist.xml"); if (!Date.prototype.toISOString) { Date.prototype.toISOString = function() { function pad(n) { return n < 10 ? '0' + n : n } return this.getUTCFullYear() + '-' + pad(this.getUTCMonth() + 1) + '-' + pad(this.getUTCDate()) + 'T' + pad(this.getUTCHours()) + ':' + pad(this.getUTCMinutes()) + ':' + pad(this.getUTCSeconds()) + 'Z'; }; } var oCrypto = new ActiveXObject("SLNKDWF.Crypto"); if (import_app_key != "EXCHFULL") { var udl = "../Oracle.udl"; var Oracle = new ActiveXObject("ADODB.Connection"); Oracle.Open('File Name='+udl); var sql = "SELECT res_ruimte_syncstate" + " FROM res_ruimte" + " WHERE res_ruimte_verwijder IS NULL" + " AND res_ruimte_extern_id = '" + zaalemail + "'"; var oRs = Oracle.Execute(sql); var zaalsync = oRs("res_ruimte_syncstate").Value||""; oRs.Close(); var sha = oCrypto.hex_sha1(zaalsync); // Gemakkelijker verschillen te zien WScript.Echo("\n\n==== Room: " + zaalemail + "\nOld sync hash: " + sha); var soapRequest = '' + ' ' + ' IdOnly' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + zaalemail + '' + ' ' + ' ' + ' ' + ' ' + zaalsync + '' + ' ' + config.maxchange + '' + ' NormalItems' + ''; } else { var dtFrom = new Date(); dtFrom.setHours(0, 0, 0, 0); var dtTo = new Date(dtFrom); dtFrom.setDate(dtFrom.getDate() - config.fullpast); dtTo.setDate(dtTo.getDate() + config.fullfuture); WScript.Echo("Full syncing from " + dtFrom.toISOString() + " to " + dtTo.toISOString() + " (" + (config.fullfuture + config.fullpast) + " days)"); var soapRequest = '' + ' ' + ' IdOnly' + ' ' + ' ' + ' ' + ' ' + ' ' //+ ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + zaalemail + '' + ' ' + ' ' + ' ' + ''; } var room_id = safefilename(zaalemail); var exch = doExchange(config.endpointurl, soapRequest, null); if (!exch) WScript.Quit(1); var xmlDoc = exch.doc; var rescode = exch.response; if (exch.response != 'NoError') { WScript.Echo("Geen NoError responsecode in response: " + exch.response); WScript.Quit(1); } if (import_app_key != "EXCHFULL") { var newsyncstate = xmlDoc.selectSingleNode("//m:SyncState"); var sha = oCrypto.hex_sha1(newsyncstate.text); // Gemakkelijker verschillen te zien WScript.Echo("New syncstate: " + newsyncstate.text + "\nHash: " + sha + "\nlength: " + newsyncstate.text.length); } var calItems = xmlDoc.selectNodes("//t:CalendarItem"); if (calItems.length == 0) { WScript.Echo("No calitems, no need to update syncstate"); WScript.Quit(1); } // Stukje robuustheid // Eerst alle *andere* sync_*.xml bestanden verwijderen om te voorkomen dat // per ongeluk de verkeerde syncstate in onze ruimte terecht komt try { fso.DeleteFile(config.xmlfolder + "Sync_*.xml"); // Als hierboven geen files gevonden zijn komen we in de exception // en niet in onderstaande echo. WScript.Echo("Oude syncfile is verwijderd."); } catch(e) { // Neem aan dat gelukkig geen files zijn gevonden } __Log2File("Sync_" + room_id + ".xml", xmlDoc.xml); // moet straks gesynced WScript.Echo("Fetching " + calItems.length + " calendar items"); var curDate = new Date(); for (var i = 0; i < calItems.length; i++) { var calItem = calItems[i]; var Id = calItem.selectSingleNode("t:ItemId").getAttribute("Id"); var ChangeKey = calItem.selectSingleNode("t:ItemId").getAttribute("ChangeKey"); var start = calItem.selectSingleNode("t:Start").text; var type = calItem.selectSingleNode("t:CalendarItemType").text; //var subject = calItem.selectSingleNode("t:CalendarItemType").text; var cal_id = room_id + "_" + i; WScript.Echo(" " + cal_id + ": " + start + " type: " + type); if (type == "Single" || type == "Occurrence" || type == "Exception") { var startDate = internal_parsedate(null, start); if (startDate.getTime() < curDate.getTime()) { WScript. Echo(" Appointment in the past; skip it."); continue; } var fname = "CalItem_" + cal_id + ".xml"; getCalenderItem('', fname); } else if (type == "RecurringMaster") // Haal Occurences en Exceptions op { getCalenderItem('', "receiveMasterItem_" + cal_id + ".xml"); // However the compromise with CalendarView is NO restrictions are permitted besides Start and End Date. None. // Dat zou nog redelijk zijn als je het tot een specifieke Recurring master kon beperken. Dat kan echter niet. // Dan maar op index ophalen for (var j = 1; j <= config.maxrecurring; j++) { var rec_id = cal_id + "_" + j; var fname = "CalItem_" + rec_id + ".xml"; // Wel XML ophalen, maar nog niet wegschrijven; eerst checken of het een toekomstige appointment betreft var exch = getCalenderItem('', null); var xmlDoc = exch.doc; WScript.Echo(" Child: " + rec_id + " " + exch.response); if (exch.response == 'ErrorCalendarOccurrenceIndexIsOutOfRecurrenceRange') { WScript.Echo(" Tijd om te stoppen"); break; } if (exch.response == 'ErrorCalendarOccurrenceIsDeletedFromRecurrence') { WScript.Echo(" Deleted negeren we"); continue; } if (exch.response == 'ErrorAccessDenied') { WScript.Echo(" Access Denied negeren we"); continue; } var start = xmlDoc.selectSingleNode("//t:Start").text; var type = xmlDoc.selectSingleNode("//t:CalendarItemType").text; var startDate = internal_parsedate(null, start); if (startDate.getTime() < curDate.getTime()) { WScript. Echo(" Appointment in the past; skip it."); continue; } // Toekomstig: save __Log2File(fname, xmlDoc.xml); WScript.Echo(" Start: " + start + " " + type); } } } function getCalenderItem(filter, fname) { var soapRequest = '' + ' ' + ' AllProperties' // TODO: Dit kan heel traag zijn. Alleen opvragen wat gewenst is + ' ' + ' ' // 35=PidLidCleanGlobalObjectId + ' ' // 3=PidLidGlobalObjectId + ' ' // 40=PidLidOldLocation + ' ' + ' ' + ' ' + filter // Dit schijnen er ook meer te mogen zijn + ' ' + ''; return exch = doExchange(config.endpointurl, soapRequest, fname); } function doExchange(url, body, fname) { var xmlDoc = doSOAP(url, body, fname); if (!xmlDoc) return null; var strQuery = "s:Envelope/s:Body/m:ResponseMessages/m:SyncFolderItemsResponseMessage/m:ResponseCode"; var strQuery = "//m:ResponseCode"; var rescode = xmlDoc.selectSingleNode(strQuery); if (!rescode) { WScript.Echo("Geen responsecode in response"); WScript.Quit(1); } return { response: rescode.text, doc: xmlDoc }; } function doSOAP(url, body, fname) { var envsoap = '' + ' ' + ' ' + ' ' + ' ' + body + ' ' + ''; var res = doHTTP("POST", url, envsoap); if (!res) return null; if (fname) __Log2File(fname, res.responseText); var xmlDoc = new ActiveXObject("MSXML2.DOMDocument.6.0"); xmlDoc.loadXML(res.responseText); xmlDoc.setProperty("SelectionLanguage", "XPath"); xmlDoc.setProperty("SelectionNamespaces", 'xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"'); return xmlDoc; } function doHTTP(method, url, body) { //var SXH_PROXY_SET_PROXY = 2; var SXH_OPTION_IGNORE_SERVER_SSL_CERT_ERROR_FLAGS = 2; var SXH_SERVER_CERT_IGNORE_ALL_SERVER_ERRORS = 0x3300; var objXMLHTTP = new ActiveXObject("MSXML2.ServerXMLHTTP.6.0"); // objXMLHTTP.setProxy(SXH_PROXY_SET_PROXY, "127.0.0.1:8888"); objXMLHTTP.open(method, url, false, config.username, config.password); objXMLHTTP.setRequestHeader("Content-Type", "text/xml; charset=utf-8") objXMLHTTP.setOption(SXH_OPTION_IGNORE_SERVER_SSL_CERT_ERROR_FLAGS, SXH_SERVER_CERT_IGNORE_ALL_SERVER_ERRORS); if (config.loglevel > 0) __Log2File("request.xml", body); objXMLHTTP.send(body); if (objXMLHTTP.status >= 200 && objXMLHTTP.status <= 299) { return objXMLHTTP; } // else: er is iets fout __Log2File("response.xml", objXMLHTTP.responseText); WScript.Echo(objXMLHTTP.status); WScript.Echo(objXMLHTTP.statusText); WScript.Echo(objXMLHTTP.responseText); return null; } function safefilename (naam) // geen 'lage' karakters en geen (back)slashes, *,%,<,>, '"', ':', ';' '?' and '|' of '+' { return naam.replace(/[\x00-\x1F|\/|\\|\*|\%\<\>\"\:\;\?\|\+]+/g, "_"); // " syntax highlight correctie } function __Log2File(log_file, data) { var utf8Stream = new ActiveXObject("ADODB.Stream"); utf8Stream.Open(); utf8Stream.Type = 2; utf8Stream.CharSet = "utf-8"; utf8Stream.WriteText(data); utf8Stream.SaveToFile(config.xmlfolder + safefilename(log_file), 2); utf8Stream.Close(); } // From MyJSON in shared.inc function internal_parsedate(key, value) { var a; if (typeof value === 'string') { a = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); if (a) { return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], +a[5], +a[6])); } } return value; }