gecko-dev/calendar/providers/wcap/calWcapSession.js

1053 строки
39 KiB
JavaScript
Исходник Обычный вид История

/* -*- Mode: javascript; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Sun Microsystems, Inc.
* Portions created by Sun Microsystems are Copyright (C) 2006 Sun
* Microsystems, Inc. All Rights Reserved.
*
* Original Author: Daniel Boelzle (daniel.boelzle@sun.com)
*
* Contributor(s):
*
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the NPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the NPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
// globals:
var g_serverTimeDiffs = {};
var g_allSupportedTimezones = {};
function calWcapSession() {
this.wrappedJSObject = this;
this.m_observers = [];
this.m_calIdToCalendar = {};
// listen for shutdown, being logged out:
// network:offline-about-to-go-offline will be fired for
// XPCOM shutdown, too.
// xxx todo: alternatively, add shutdown notifications to cal manager
// xxx todo: how to simplify this for multiple topics?
var observerService = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
observerService.addObserver( this, "quit-application",
false /* don't hold weakly: xxx todo */ );
observerService.addObserver( this, "network:offline-about-to-go-offline",
false /* don't hold weakly: xxx todo */ );
}
calWcapSession.prototype = {
m_ifaces: [ Components.interfaces.calIWcapSession,
Components.interfaces.nsIInterfaceRequestor,
Components.interfaces.nsIClassInfo,
Components.interfaces.nsISupports ],
// nsISupports:
QueryInterface:
function( iid )
{
for each ( var iface in this.m_ifaces ) {
if (iid.equals( iface ))
return this;
}
throw Components.results.NS_ERROR_NO_INTERFACE;
},
// nsIClassInfo:
getInterfaces:
function( count )
{
count.value = this.m_ifaces.length;
return this.m_ifaces;
},
get classDescription() {
return calWcapCalendarModule.WcapSessionInfo.classDescription;
},
get contractID() {
return calWcapCalendarModule.WcapSessionInfo.contractID;
},
get classID() {
return calWcapCalendarModule.WcapSessionInfo.classID;
},
getHelperForLanguage: function( language ) { return null; },
implementationLanguage:
Components.interfaces.nsIProgrammingLanguage.JAVASCRIPT,
flags: 0,
// nsIInterfaceRequestor:
getInterface:
function( iid, instance )
{
if (iid.equals(Components.interfaces.nsIAuthPrompt)) {
// use the window watcher service to get a nsIAuthPrompt impl
return getWindowWatcher().getNewAuthPrompter(null);
}
else if (iid.equals(Components.interfaces.nsIPrompt)) {
// use the window watcher service to get a nsIPrompt impl
return getWindowWatcher().getNewPrompter(null);
}
Components.returnCode = Components.results.NS_ERROR_NO_INTERFACE;
return null;
},
toString:
function( msg )
{
var str = this.uri.spec;
if (this.m_userId != null) {
str += (", userId=" + this.userId);
}
if (this.m_sessionId == null) {
str += (getIoService().offline ? ", offline" : ", not logged in");
}
// else {
// str += (", session-id=" + this.m_sessionId);
// }
return str;
},
log:
function( msg )
{
return logMessage( this.toString(), msg );
},
logError:
function( err, context )
{
var str = ("error: " + errorToString(err));
Components.utils.reportError( this.log( str, context ) );
return str;
},
notifyError:
function( err )
{
debugger;
var str = this.logError( err );
this.notifyObservers( "onError",
[err instanceof Error ? -1 : err, str] );
},
m_observers: null,
notifyObservers:
function( func, args )
{
this.m_observers.forEach(
function( obj ) {
try {
obj[func].apply( obj, args );
}
catch (exc) {
// don't call notifyError() here:
Components.utils.reportError( exc );
}
} );
},
addObserver:
function( observer )
{
if (this.m_observers.indexOf( observer ) == -1) {
this.m_observers.push( observer );
}
},
removeObserver:
function( observer )
{
this.m_observers = this.m_observers.filter(
function(x) { return x != observer; } );
},
// nsIObserver:
observe:
function( subject, topic, data )
{
this.log( "observing: " + topic + ", data: " + data );
if (topic == "network:offline-about-to-go-offline") {
this.logout();
}
else if (topic == "quit-application") {
// xxx todo: valid upon notification?
var observerService =
Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
observerService.removeObserver(
this, "quit-application" );
observerService.removeObserver(
this, "network:offline-about-to-go-offline" );
}
},
getSupportedTimezones:
function( bRefresh )
{
var key = this.uri.hostPort;
if ((bRefresh || !g_allSupportedTimezones[key]) &&
this.m_sessionId != null)
{
var url = this.uri.spec +
"get_all_timezones.wcap?appid=mozilla-lightning" +
"&fmt-out=text%2Fcalendar&id=" + this.m_sessionId;
var str = issueSyncRequest( url );
var icalRootComp = getIcsService().parseICS( str );
if (icalRootComp == null)
throw new Error("invalid data, expected ical!");
checkWcapIcalErrno( icalRootComp );
var tzids = [];
var this_ = this;
forEachIcalComponent(
icalRootComp, "VTIMEZONE",
function( subComp ) {
try {
var tzCal = getIcsService().createIcalComponent(
"VCALENDAR" );
subComp = subComp.clone();
tzCal.addSubcomponent( subComp );
getIcsService().addTimezone( tzCal, "", "" );
tzids.push( subComp.getFirstProperty("TZID").value );
}
catch (exc) { // ignore errors:
this_.log( "error: " + exc );
}
} );
g_allSupportedTimezones[key] = tzids;
}
return g_allSupportedTimezones[key];
},
getServerTime:
function( localTime )
{
var ret = (localTime ? localTime.clone() : getTime());
ret.addDuration( this.getServerTimeDiff() );
return ret;
},
getServerTimeDiff:
function( bRefresh )
{
var key = this.uri.hostPort;
if ((bRefresh || !g_serverTimeDiffs[key]) &&
this.m_sessionId != null) {
var url = this.uri.spec +
// xxx todo: assuming same diff for all calids:
"gettime.wcap?appid=mozilla-lightning" +
"&fmt-out=text%2Fcalendar&id=" + this.m_sessionId;
// xxx todo: this is no solution!
var localTime = getTime();
var str = issueSyncRequest( url );
var icalRootComp = getIcsService().parseICS( str );
if (icalRootComp == null)
throw new Error("invalid data, expected ical!");
checkWcapIcalErrno( icalRootComp );
var serverTime = getDatetimeFromIcalProp(
icalRootComp.getFirstProperty( "X-NSCP-WCAPTIME" ) );
var diff = serverTime.subtractDate( localTime );
this.log( "server time diff is: " + diff );
g_serverTimeDiffs[key] = diff;
}
return g_serverTimeDiffs[key];
},
isSupportedTimezone:
function( tzid )
{
var tzids = this.getSupportedTimezones();
for each ( var id in tzids ) {
if (id == tzid)
return true;
}
return false;
},
getRealmName:
function( uri )
{
// xxx todo: realm names must not have a trailing slash
var realm = uri.spec;
if (realm[realm.length - 1] == '/')
realm = realm.substr(0, realm.length - 1);
return realm;
},
m_sessionId: null,
m_bNoLoginsAnymore: false,
getSessionId:
function( timedOutSessionId )
{
if (this.m_bNoLoginsAnymore) {
this.log( "login has failed, no logins anymore for this user." );
throw Components.interfaces.calIWcapErrors.WCAP_LOGIN_FAILED;
}
if (getIoService().offline) {
this.log( "in offline mode." );
throw NS_ERROR_OFFLINE;
}
if (this.m_sessionId == null || this.m_sessionId == timedOutSessionId) {
2006-07-26 19:14:28 +04:00
// sync all execution for login to UI thread, using nsIRunnable:
// change from MOZILLA_1_8_BRANCH->TRUNK: probe xxx todo: test
var eventQueueService =
Components.classes["@mozilla.org/event-queue-service;1"]
.getService(Components.interfaces.nsIEventQueueService);
2006-07-26 19:14:28 +04:00
var target; // eventQueue or eventTarget
if (eventQueueService == null) {
// we are on the TRUNK:
var threadManager =
Components.classes["@mozilla.org/thread-manager;1"]
.getService(Components.interfaces.nsIThreadManager);
target = threadManager.mainThread;
}
2006-07-26 19:14:28 +04:00
else {
target = eventQueueService.getSpecialEventQueue(
Components.interfaces.
nsIEventQueueService.UI_THREAD_EVENT_QUEUE );
}
2006-07-26 19:14:28 +04:00
var proxyObjectManager =
Components.classes["@mozilla.org/xpcomproxy;1"]
.getService(Components.interfaces.nsIProxyObjectManager);
var this_ = this;
var proxy = proxyObjectManager.getProxyForObject(
target, Components.interfaces.nsIRunnable,
{ // need to implemented QueryInterface, because object param
// is not associated with iid:
QueryInterface: function( iid ) {
if (Components.interfaces.nsIRunnable.equals(iid) ||
Components.interfaces.nsISupports.equals(iid))
return this;
throw Components.results.NS_ERROR_NO_INTERFACE;
},
// nsIRunnable:
run: function() {
if (this_.m_sessionId == null ||
this_.m_sessionId == timedOutSessionId)
{
if (timedOutSessionId != null) {
this_.m_sessionId = null;
this_.log( "session timeout; " +
"prompting to reconnect." );
var prompt =
getWindowWatcher().getNewPrompter(null);
var bundle = getBundle();
if (!prompt.confirm(
bundle.GetStringFromName(
"reconnectConfirmation.label" ),
bundle.formatStringFromName(
"reconnectConfirmation.text",
[this_.uri.hostPort], 1 ) )) {
this_.m_bNoLoginsAnymore = true;
}
}
if (!this_.m_bNoLoginsAnymore)
this_.getSessionId_();
this_.getSupportedTimezones(true /* refresh */);
this_.getServerTimeDiff(true /* refresh */);
// preread calprops for subscribed calendars:
var cals = this_.getSubscribedCalendars({});
for each ( cal in cals ) {
cal.getCalProps_(true /* async */);
}
}
}
},
Components.interfaces.nsIProxyObjectManager.INVOKE_SYNC );
// xxx todo: are rc/exceptions forwarded to current thread?
proxy.run();
}
if (this.m_sessionId == null) {
throw Components.interfaces.calIWcapErrors.WCAP_LOGIN_FAILED;
}
return this.m_sessionId;
},
getSessionId_:
function()
{
if (this.m_sessionId == null) {
var passwordManager =
Components.classes["@mozilla.org/passwordmanager;1"]
.getService(Components.interfaces.nsIPasswordManager);
var outUser = { value: this.userId };
var outPW = { value: null };
var enumerator = passwordManager.enumerator;
var realm = this.getRealmName(this.uri);
while (enumerator.hasMoreElements()) {
var pwEntry = enumerator.getNext().QueryInterface(
Components.interfaces.nsIPassword );
if (LOG_LEVEL > 1) {
this.log( "pw entry:\n\thost=" + pwEntry.host +
"\n\tuser=" + pwEntry.user );
}
if (pwEntry.host == realm) { // found an entry matching URI:
outUser.value = pwEntry.user;
outPW.value = pwEntry.password;
break;
}
}
var loginUri = this.uri.clone();
if (loginUri.scheme.toLowerCase() != "https") {
if (loginUri.port == -1) {
// no https, but no port specified
// => enforce login via https:
loginUri.scheme = "https";
}
else {
// user has specified a specific port, but no https:
// => leave it to her whether to connect...
if (!confirmUnsecureLogin( loginUri )) {
this.m_bNoLoginsAnymore = true;
this.log( "user rejected unsecure login." );
return null;
}
}
}
if (outUser.value == null || outPW.value == null) {
this.log( "no password entry found." );
}
else {
this.log( "password entry found for user " + outUser.value );
try {
this.login_( loginUri, outUser.value, outPW.value );
}
catch (exc) { // ignore silently
}
}
if (this.m_sessionId == null) {
// preparing login:
var loginText = null;
try {
loginText = this.getServerInfo( loginUri );
if (loginText == null) {
if (loginUri.scheme.toLowerCase() == "https") {
// gathering server info via https has failed,
// try http:
loginUri.scheme = "http";
loginText = this.getServerInfo( loginUri );
}
if (loginText == null) {
throw new Error(
getBundle().formatStringFromName(
"accessingServerFailedError.text",
[loginUri.hostPort], 1 ) );
}
if (this.uri.scheme.toLowerCase() == "https") {
// user specified https, so http is no option:
loginText = null;
throw new Error(
getBundle() .formatStringFromName(
"mandatoryHttpsError.text",
[loginUri.hostPort], 1 ) );
}
// http possible, ask for it:
if (confirmUnsecureLogin( loginUri )) {
if (outPW.value != null) {
// user/pw has been found previously,
// but no login was possible,
// try again using http here:
this.login_( loginUri,
outUser.value, outPW.value );
if (this.m_sessionId != null)
return this.m_sessionId;
}
}
else {
this.m_bNoLoginsAnymore = true;
this.log( "user rejected unsecure login." );
return null;
}
}
}
catch (exc) {
Components.utils.reportError( exc );
if (loginText == null) {
// accessing server or invalid protocol,
// no logins anymore:
this.m_bNoLoginsAnymore = true;
throw exc; // propagate error message
}
// else maybe user pw has changed or similar...
}
if (outPW.value != null) {
// login failed before, so try to remove from pw db:
try {
passwordManager.removeUser(
this.getRealmName(this.uri), outUser.value );
}
catch (exc) {
this.log( "error removing from pw db: " + exc );
}
}
var savePW = { value: false };
while (this.m_sessionId == null) {
var prompt = getWindowWatcher().getNewPrompter(null);
if (prompt.promptUsernameAndPassword(
getBundle().GetStringFromName("loginDialog.label"),
loginText, outUser, outPW,
getBundle().GetStringFromName(
"loginDialog.savePW.label" ),
savePW ))
{
try {
this.login_( loginUri, outUser.value, outPW.value );
}
catch (exc) {
Components.utils.reportError( exc );
// xxx todo: UI?
}
}
else { // dialog cancelled, don't login anymore:
this.m_bNoLoginsAnymore = true;
this.log( "login cancelled, will not prompt again." );
break;
}
}
if (this.m_sessionId != null && savePW.value) {
// save pw under session uri:
passwordManager.addUser( this.getRealmName(this.uri),
outUser.value, outPW.value );
}
}
}
return this.m_sessionId;
},
login_:
function( loginUri, user, pw )
{
if (this.m_sessionId != null) {
this.logout();
}
// currently, xml parsing at an early stage during process startup
// does not work reliably, so use libical parsing for now:
var str = issueSyncRequest(
loginUri.spec + "login.wcap?fmt-out=text%2Fcalendar&user=" +
encodeURIComponent(user) + "&password=" + encodeURIComponent(pw),
null /* receiverFunc */, false /* no logging */ );
var icalRootComp = getIcsService().parseICS( str );
checkWcapIcalErrno( icalRootComp );
var prop = icalRootComp.getFirstProperty( "X-NSCP-WCAP-SESSION-ID" );
if (prop == null)
throw new Error("missing X-NSCP-WCAP-SESSION-ID!");
this.m_sessionId = prop.value;
// var xml = issueSyncXMLRequest(
// loginUri.spec + "login.wcap?fmt-out=text%2Fxml&user=" +
// encodeURIComponent(user) + "&password=" + encodeURIComponent(pw) );
// checkWcapXmlErrno( xml );
// this.m_sessionId = xml.getElementsByTagName(
// "X-NSCP-WCAP-SESSION-ID" ).item(0).textContent;
this.m_userId = user;
this.log( "WCAP login succeeded." );
},
getServerInfo:
function( uri )
{
loginTextVars = [uri.hostPort];
var loginText;
var wcapVersion;
try {
// currently, xml parsing at an early stage during process startup
// does not work reliably, so use libical parsing for now:
var str = issueSyncRequest(
uri.spec + "version.wcap?fmt-out=text%2Fcalendar" );
var icalRootComp = getIcsService().parseICS( str );
if (icalRootComp == null)
throw new Error("invalid data, expected ical!");
var prop = icalRootComp.getFirstProperty( "PRODID" );
if (prop == null)
throw new Error("missing PRODID!");
loginTextVars.push( prop.value );
var prop = icalRootComp.getFirstProperty( "X-NSCP-SERVERVERSION" );
if (prop == null)
throw new Error("missing X-NSCP-SERVERVERSION!");
loginTextVars.push( prop.value );
var prop = icalRootComp.getFirstProperty( "X-NSCP-WCAPVERSION" );
if (prop == null)
throw new Error("missing X-NSCP-WCAPVERSION!");
wcapVersion = prop.value;
loginTextVars.push( wcapVersion );
// var xml = issueSyncXMLRequest(
// uri.spec + "version.wcap?fmt-out=text%2Fxml" );
// wcapVersion = xml.getElementsByTagName(
// "X-NSCP-WCAPVERSION" ).item(0).textContent;
// if (wcapVersion == undefined || wcapVersion == "")
// throw new Error("invalid response!");
// serverInfo =
// ("Server-Info: " +
// xml.getElementsByTagName(
// "iCal" ).item(0).attributes.getNamedItem(
// "prodid" ).value +
// ", v" + xml.getElementsByTagName(
// "X-NSCP-SERVERVERSION" ).item(0).textContent +
// ", WCAP v" + wcapVersion);
}
catch (exc) {
this.log( "error: " + exc );
return null;
}
if (parseInt(wcapVersion) < 2.0) {
this.log( "parsed server WCAP major: " + parseInt(wcapVersion) );
throw new Error(
getBundle("insufficientWcapVersionError.text", loginTextVars) );
}
return getBundle().formatStringFromName(
"loginDialog.text", loginTextVars, loginTextVars.length );
},
getCommandUrl:
function( wcapCommand )
{
if (this.uri == null)
throw new Error("no URI!");
// ensure established session, so userId is set;
// (calId defaults to userId) if not set:
this.getSessionId();
return (this.uri.spec + wcapCommand + ".wcap?appid=mozilla-lightning");
},
issueRequest:
function( url, issueFunc, dataConvFunc, receiverFunc )
{
var sessionId = this.getSessionId();
var this_ = this;
issueFunc(
url + ("&id=" + sessionId),
function( data ) {
var wcapResponse = new WcapResponse();
try {
try {
wcapResponse.data = dataConvFunc(
data, wcapResponse );
}
catch (exc) {
if (exc == Components.interfaces.
calIWcapErrors.WCAP_LOGIN_FAILED) /* timeout */ {
// getting a new session will throw any exception in
// this block, thus it is notified into receiverFunc
this_.getSessionId(
sessionId /* (old) timed-out session */ );
// try again:
this_.issueRequest(
url, issueFunc, dataConvFunc, receiverFunc );
return;
}
throw exc; // rethrow
}
}
catch (exc) {
// setting the request's exception will rethrow exception
// when request's data is retrieved.
wcapResponse.exception = exc;
}
receiverFunc( wcapResponse );
} );
},
issueAsyncRequest:
function( url, dataConvFunc, receiverFunc )
{
this.issueRequest(
url, issueAsyncRequest, dataConvFunc, receiverFunc );
},
issueSyncRequest:
function( url, dataConvFunc, receiverFunc )
{
var ret = null;
this.issueRequest(
url, issueSyncRequest,
dataConvFunc,
function( wcapResponse ) {
if (receiverFunc) {
receiverFunc( wcapResponse );
}
ret = wcapResponse.data; // may throw
} );
return ret;
},
// calIWcapSession:
m_uri: null,
get uri() { return this.m_uri; },
set uri( thatUri )
{
if (this.m_uri == null || thatUri == null ||
!this.m_uri.equals(thatUri))
{
this.logout();
this.m_uri = null;
if (thatUri != null) {
// sensible default for user id login:
var username = decodeURIComponent( thatUri.username );
if (username != "") {
var nColon = username.indexOf(':');
this.m_userId =
(nColon >= 0 ? username.substr(0, nColon) : username);
}
this.m_uri = thatUri.clone();
this.m_uri.userPass = "";
}
}
},
m_userId: null,
get userId() { return this.m_userId; },
2006-07-26 19:14:28 +04:00
get isLoggedIn() { return this.m_sessionId != null; },
login:
function()
{
this.logout(); // assure being logged out
this.getSessionId();
},
logout:
function()
{
if (this.m_sessionId != null) {
// although io service's offline flag is already
// set BEFORE notification (about to go offline, nsIOService.cpp).
// WTF.
var url = (this.uri.spec +
"logout.wcap?fmt-out=text%2Fxml&id=" + this.m_sessionId);
try {
checkWcapXmlErrno( issueSyncXMLRequest(url),
-1 /* logout successfull */ );
this.log( "WCAP logout succeeded." );
}
catch (exc) {
this.log( "WCAP logout failed: " + exc );
Components.utils.reportError( exc );
}
this.m_sessionId = null;
}
this.m_userId = null;
// ask next time we log in:
this.m_bNoLoginsAnymore = false;
2006-07-26 19:14:28 +04:00
if (this.uri != null) {
var this_ = this;
g_httpHosts = g_httpHosts.filter(
function(hostEntry) {
return (hostEntry.m_host != this_.uri.hostPort); } );
}
},
getWcapErrorString:
function( rc )
{
return wcapErrorToString(rc);
},
get defaultCalendar() {
2006-07-26 19:14:28 +04:00
return this.getCalendarByCalId(null);
},
set defaultCalendar(cal) {
this.m_defaultCalendar = cal;
},
m_defaultCalendar: null,
m_calIdToCalendar: {},
getCalendarByCalId:
function( calId )
{
2006-07-26 19:14:28 +04:00
var ret;
if (calId == null || this.userId == calId) {
if (this.m_defaultCalendar == null)
2006-07-26 19:14:28 +04:00
this.m_defaultCalendar = new calWcapCalendar(this.userId, this);
ret = this.m_defaultCalendar;
}
else {
var key = encodeURIComponent(calId);
2006-07-26 19:14:28 +04:00
ret = this.m_calIdToCalendar[key];
if (!ret) {
2006-07-26 19:14:28 +04:00
ret = new calWcapCalendar(calId, this);
this.m_calIdToCalendar[key] = ret;
}
}
2006-07-26 19:14:28 +04:00
return ret;
},
getCalendars:
function( out_count, bGetOwnedCals )
{
var list = this.getUserPreferences(
bGetOwnedCals ? "X-NSCP-WCAP-PREF-icsCalendarOwned"
: "X-NSCP-WCAP-PREF-icsSubscribed", {} );
var ret = [];
for each( var item in list ) {
var ar = item.split(',');
// ',', '$' are not encoded. ',' can be handled here. WTF.
for each ( a in ar ) {
var dollar = a.indexOf('$');
if (dollar >= 0) {
ret.push(
this.getCalendarByCalId( a.substring(0, dollar) ) );
}
}
}
out_count.value = ret.length;
return ret;
},
getOwnedCalendars:
function( out_count )
{
return this.getCalendars( out_count, true );
},
getSubscribedCalendars:
function( out_count )
{
return this.getCalendars( out_count, false );
},
createCalendar:
function( calId, name, bAllowDoubleBooking, bSetCalProps, bAddToSubscribed )
{
try {
var url = this.getCommandUrl( "createcalendar" );
url += ("&allowdoublebook=" + (bAllowDoubleBooking ? "1" : "0"));
url += ("&set_calprops=" + (bSetCalProps ? "1" : "0"));
url += ("&subscribe=" + (bAddToSubscribed ? "1" : "0"));
url += ("&calid=" + encodeURIComponent(calId));
// xxx todo: name undocumented!
url += ("&name=" + encodeURIComponent(name));
// xxx todo: what about categories param???
this.issueSyncRequest( url + "&fmt-out=text%2Fxml", stringToXml );
this.m_userPrefs = null; // reread prefs
return this.getCalendarByCalId( this.userId + ":" + calId );
}
catch (exc) {
this.notifyError( exc );
throw exc;
}
},
deleteCalendar:
function( calId, bRemoveFromSubscribed )
{
try {
var url = this.getCommandUrl( "deletecalendar" );
url += ("&unsubscribe=" + (bRemoveFromSubscribed ? "1" : "0"));
url += ("&calid=" + encodeURIComponent(calId));
this.issueSyncRequest( url + "&fmt-out=text%2Fxml", stringToXml );
this.m_userPrefs = null; // reread prefs
this.m_calIdToCalendar[encodeURIComponent(calId)] = null;
}
catch (exc) {
this.notifyError( exc );
throw exc;
}
},
modifyCalendarSubscriptions:
function( calIds, bSubscribe )
{
try {
var url = this.getCommandUrl(
bSubscribe ? "subscribe_calendars" : "unsubscribe_calendars" );
var calId = "";
for ( var i = 0; i < calIds.length; ++i ) {
if (i > 0)
calId += ";";
calId += encodeURIComponent(calIds[i]);
}
url += ("&calid=" + calId);
this.issueSyncRequest( url + "&fmt-out=text%2Fxml", stringToXml );
this.m_userPrefs = null; // reread prefs
}
catch (exc) {
this.notifyError( exc );
throw exc;
}
},
subscribeToCalendars:
function( count, calIds )
{
this.modifyCalendarSubscriptions( calIds, true );
},
unsubscribeFromCalendars:
function( count, calIds )
{
this.modifyCalendarSubscriptions( calIds, false );
},
m_userPrefs: null,
getUserPreferences:
function( prefName, out_count )
{
try {
if (this.m_userPrefs == null) {
var url = this.getCommandUrl( "get_userprefs" );
url += ("&userid=" + encodeURIComponent(this.userId));
this.m_userPrefs = this.issueSyncRequest(
url + "&fmt-out=text%2Fxml", stringToXml );
}
var ret = [];
var nodeList = this.m_userPrefs.getElementsByTagName(prefName);
for ( var i = 0; i < nodeList.length; ++i ) {
ret.push( trimString(nodeList.item(i).textContent) );
}
out_count.value = ret.length;
return ret;
}
catch (exc) {
this.notifyError( exc );
throw exc;
}
},
getFreeBusyTimes_resp:
function( wcapResponse, calId, iListener, requestId )
{
try {
var xml = wcapResponse.data; // first statement, may throw
if (iListener != null) {
var ret = [];
var nodeList = xml.getElementsByTagName("FB");
for ( var i = 0; i < nodeList.length; ++i ) {
var item = nodeList.item(i);
var str = item.textContent;
var slash = str.indexOf( '/' );
var start = new CalDateTime();
start.icalString = str.substr( 0, slash );
var end = new CalDateTime();
end.icalString = str.substr( slash + 1 );
var entry = {
isBusyEntry:
(item.attributes.getNamedItem("FBTYPE").nodeValue
== "BUSY"),
dtRangeStart: start,
dtRangeEnd: end
};
ret.push( entry );
}
iListener.onGetFreeBusyTimes(
Components.results.NS_OK,
requestId, calId, ret.length, ret );
}
if (LOG_LEVEL > 0) {
this.log( "getFreeBusyTimes_resp() calId=" + calId + ", " +
getWcapRequestStatusString(xml) );
}
}
catch (exc) {
const calIWcapErrors = Components.interfaces.calIWcapErrors;
switch (exc) {
case calIWcapErrors.WCAP_NO_ERRNO: // workaround
case calIWcapErrors.WCAP_ACCESS_DENIED_TO_CALENDAR:
case calIWcapErrors.WCAP_CALENDAR_DOES_NOT_EXIST:
this.log( "getFreeBusyTimes_resp() ignored: " +
errorToString(exc) ); // no error
break;
default:
this.notifyError( exc );
break;
}
if (iListener != null)
iListener.onGetFreeBusyTimes( exc, requestId, calId, 0, [] );
}
},
getFreeBusyTimes:
function( calId, rangeStart, rangeEnd, bBusyOnly, iListener,
bAsync, requestId )
{
try {
// assure DATETIMEs:
if (rangeStart != null && rangeStart.isDate) {
rangeStart = rangeStart.clone();
rangeStart.isDate = false;
}
if (rangeEnd != null && rangeEnd.isDate) {
rangeEnd = rangeEnd.clone();
rangeEnd.isDate = false;
}
var zRangeStart = getIcalUTC(rangeStart);
var zRangeEnd = getIcalUTC(rangeEnd);
this.log( "getFreeBusyTimes():\n\trangeStart=" + zRangeStart +
",\n\trangeEnd=" + zRangeEnd );
var url = this.getCommandUrl( "get_freebusy" );
url += ("&calid=" + encodeURIComponent(calId));
url += ("&busyonly=" + (bBusyOnly ? "1" : "0"));
url += ("&dtstart=" + zRangeStart);
url += ("&dtend=" + zRangeEnd);
url += "&fmt-out=text%2Fxml";
var this_ = this;
function resp( wcapResponse ) {
this_.getFreeBusyTimes_resp(
wcapResponse, calId, iListener, requestId );
}
if (bAsync)
this.issueAsyncRequest( url, stringToXml, resp );
else
this.issueSyncRequest( url, stringToXml, resp );
}
catch (exc) {
this.notifyError( exc );
if (iListener != null)
iListener.onGetFreeBusyTimes( exc, requestId, calId, 0, [] );
throw exc;
}
}
};
var g_httpHosts = [];
function confirmUnsecureLogin( uri )
{
var host = uri.hostPort;
for each ( var hostEntry in g_httpHosts ) {
if (hostEntry.m_host == host) {
return hostEntry.m_bConfirmed;
}
}
var prompt = getWindowWatcher().getNewPrompter(null);
var bundle = getBundle();
var bConfirmed = prompt.confirm(
bundle.GetStringFromName("noHttpsConfirmation.label"),
bundle.formatStringFromName("noHttpsConfirmation.text", [host], 1) );
// save decision for all calendars:
g_httpHosts.push(
{ m_host: host, m_bConfirmed: bConfirmed } );
return bConfirmed;
}