/*
$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;
}