From 51966d70165d3f48a228fec7c47c1d0001b19efe Mon Sep 17 00:00:00 2001 From: Guido Günther Date: Fri, 26 Apr 2013 18:42:04 +0200 Subject: Initial commit --- content/caDNSResolver.js | 99 +++++++++++++++++++++ content/calendarAutoconfig.js | 161 +++++++++++++++++++++++++++++++++++ content/calendarAutoconfigDialog.js | 11 +++ content/calendarAutoconfigDialog.xul | 32 +++++++ content/calendarOverlay.xul | 18 ++++ content/misc.js | 18 ++++ 6 files changed, 339 insertions(+) create mode 100644 content/caDNSResolver.js create mode 100644 content/calendarAutoconfig.js create mode 100644 content/calendarAutoconfigDialog.js create mode 100644 content/calendarAutoconfigDialog.xul create mode 100644 content/calendarOverlay.xul create mode 100644 content/misc.js (limited to 'content') diff --git a/content/caDNSResolver.js b/content/caDNSResolver.js new file mode 100644 index 0000000..f6b029f --- /dev/null +++ b/content/caDNSResolver.js @@ -0,0 +1,99 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * (C) 2013 Guido Günther + */ + +Components.utils.import("resource://gre/modules/ctypes.jsm"); +const GError = ctypes.StructType("GError"); +const GCancellable = ctypes.StructType("GCancellable"); +const GResolver = ctypes.StructType("GResolver"); +const GSrvTarget = ctypes.StructType("GSrvTarget"); +const gpointer = ctypes.void_t.ptr; + +var gioExist = false; +try { + var glib = ctypes.open("libglib-2.0.so.0"); + var gobject = ctypes.open("libgobject-2.0.so.0"); + var gio = ctypes.open("libgio-2.0.so.0"); + gioExist = true; +} catch (ex) { + Components.utils.reportError("Failed to load gio: " + ex); +} + +if (gioExist) { + caDNSResolver = function() { + this.gpointer = ctypes.void_t.ptr; + this._GList = new ctypes.StructType("_GList"); + this._GList.define([{data: this.gpointer}, + {next: this._GList.ptr}, + {prev: this._GList.ptr}]); + this.GList = this._GList; + + this.g_resolver_lookup_service = gio.declare( + "g_resolver_lookup_service", + ctypes.default_abi, + this.GList.ptr, // return type: GList + GResolver.ptr, // in: the resolver to use + ctypes.char.ptr, // in: the service of look for + ctypes.char.ptr, // in: the protocol the service should use + ctypes.char.ptr, // in: the domain the service is offerd in + GCancellable.ptr, // in: the cancellable + GError.ptr.ptr // out: error + ); + + this.g_srv_target_get_hostname = gio.declare( + "g_srv_target_get_hostname", + ctypes.default_abi, + ctypes.char.ptr, // return type: string + GSrvTarget.ptr // in: Record to parse + ); + + this.g_resolver_get_default = gio.declare( + "g_resolver_get_default", + ctypes.default_abi, + GResolver.ptr // return type: GResolver + ); + + this.g_resolver_free_targets = gio.declare( + "g_resolver_free_targets", + ctypes.default_abi, + ctypes.void_t, + this.GList.ptr // in: List of GSrvTargets + ); + + this._resolver = this.g_resolver_get_default(); + + this.get_srv_records = function (service, proto, domain) { + var ret = []; + var records = this.g_resolver_lookup_service(this._resolver, + service, + proto, + domain, + null, + null); + if (records.isNull()) { + Components.utils.reportError("Failed to lookup: " + service); + return []; + } else { + Components.utils.reportError("records: " + records); + // For PoC just look at the first record + // and ignore weight and prio + var tmp = records.contents.data; + var rec = ctypes.cast(tmp, GSrvTarget.ptr); + var val = this.g_srv_target_get_hostname (rec); + ret[0] = val.readString(); + Components.utils.reportError("ret: " + ret); + this.g_resolver_free_targets(records); + } + return ret; + }; + }; +} else { + caDNSResolver = function() { + this.get_srv_records = function (service, proto, domain) { + return null; + }; + }; +} diff --git a/content/calendarAutoconfig.js b/content/calendarAutoconfig.js new file mode 100644 index 0000000..8a683c0 --- /dev/null +++ b/content/calendarAutoconfig.js @@ -0,0 +1,161 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * (C) 2013 Guido Günther + */ + +Components.utils.import("resource://calendar/modules/calUtils.jsm"); + +caCalendarAutoconfig = function (aDocument, aWindow) +{ + this._document = aDocument; + this._window = aWindow; + + /* The user as uri */ + this._uri = null; + + /* The calendar server */ + this._proto = null; + this._host = null; + this._path = null; +} + +caCalendarAutoconfig.prototype = { + parse_uri: function (aUri) + { + /* Check if it's a valid URI */ + try { + var uri = cal.makeURL(aUri); + } catch (ex) { + var msg = "Invalid uri " + aUri; + caError(msg + " " + ex); + throw msg; + } + + if (uri.scheme != 'email') { + throw aUri + "not a email uri"; + } + this._domain = aUri.split('@')[1]; + }, + + /* Query DNS SRV and TXT records */ + query_dns: function() { + var proto = 'tcp'; + var service = this.type + 's'; + /* Currently not possible using a xulrunner interface: + * #14328, #545866, #735967 */ + var resolver = new caDNSResolver(); + + caDebug("Checking SRV records for " + [this._domain, + proto, + service, + ].join(" ")); + var records = resolver.get_srv_records(service, + proto, + this._domain); + caDebug("Got service records: " + records); + if (records[0]) { + this._host = records[0]; + this._proto = 'https://'; + } + + /* + * FIXME: check TXT records for path + * this._path = ... + */ + }, + + detect_calendar: function(aUri) + { + this.parse_uri(aUri); + this._uri = aUri; + this.query_dns(); + + if (this._host == null) { + this._host = this._domain; + } + if (this._path == null) { + this._path = '/.well-known/' + this.type; + } + return this.check_calendar(); + }, + + _register_calendar: function(aURL, calname) { + caDebug("Registering " + calname + " calendar for " + aURL); + var uri = cal.makeURL(aURL); + var newCal = cal.getCalendarManager().createCalendar('ics', uri); + newCal.name = calname; + cal.getCalendarManager().registerCalendar(newCal); + } +}; + +/* CalDAV */ +function caCaldavAutoconfig(document, window) { + /* FIXME: handle basic auth, parse dav properties after + * DAV:current-user-principal + * request.open("GET", url, true); + * FIXME: use principal-URL to detect "home" collections + * request.send(null); + */ + this.type = 'caldav'; +} +caCaldavAutoconfig.prototype = new caCalendarAutoconfig; +caCaldavAutoconfig.prototype.check_calendar = function() { + return false; +} + +function makeURI(aURL, aOriginCharset, aBaseURI) { + var ioService = Components.classes["@mozilla.org/network/io-service;1"] + .getService(Components.interfaces.nsIIOService); + return ioService.newURI(aURL, aOriginCharset, aBaseURI); +} + +/* WebCal */ +function caWebcalAutoconfig(document, window) { + this.type = 'webcal'; +} +caWebcalAutoconfig.prototype = new caCalendarAutoconfig; +caWebcalAutoconfig.prototype.check_calendar = function() { + this._proto = this._proto || 'https://'; + + var url = this._proto + this._host + this._path; + var calName = this._uri; + caDebug("Looking for calendar at " + url); + + var request = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"] + .createInstance(Components.interfaces.nsIXMLHttpRequest); + + request.onload = function (e) { + if (request.readyState === 4) { + if (request.status === 200) { + if (request.getResponseHeader('Content-Type') == 'text/calendar') { + this._register_calendar(url, calName); + } else { + caDebug("Not a calendar: " + request.getResponseHeader('Content-Type')); + /* FIXME: Check collection for .ics files */ + } + } else { + caDebug(request.statusText); + } + } + }.bind(this); + + request.onerror = function (e) { + caDebug("Calendar detection failed " + e); + }; + + request.open("GET", url, true); + request.send(null); +} + + +function do_calendarAutoconfig(aUri) { + caDebug(aUri); + + caldav = new caCaldavAutoconfig(document, window); + if (! caldav.detect_calendar(aUri)) { + webcal = new caWebcalAutoconfig(document, window); + webcal.detect_calendar(aUri); + } +} diff --git a/content/calendarAutoconfigDialog.js b/content/calendarAutoconfigDialog.js new file mode 100644 index 0000000..237326e --- /dev/null +++ b/content/calendarAutoconfigDialog.js @@ -0,0 +1,11 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * (C) 2013 Guido Günther + */ + +show_calendarAutoconfigDialog = function() { + window.openDialog("chrome://autoconfig/content/calendarAutoconfigDialog.xul","showmore", + "chrome",null); +} diff --git a/content/calendarAutoconfigDialog.xul b/content/calendarAutoconfigDialog.xul new file mode 100644 index 0000000..6320043 --- /dev/null +++ b/content/calendarAutoconfigDialog.xul @@ -0,0 +1,32 @@ + + + + + + + + + + + diff --git a/content/calendarOverlay.xul b/content/calendarOverlay.xul new file mode 100644 index 0000000..b58776f --- /dev/null +++ b/content/calendarOverlay.xul @@ -0,0 +1,18 @@ + + + + +