diff --git a/calendar/providers/wcap/calWcapCalendar.js b/calendar/providers/wcap/calWcapCalendar.js index 4bae28e305c1..f14de6c8c87a 100644 --- a/calendar/providers/wcap/calWcapCalendar.js +++ b/calendar/providers/wcap/calWcapCalendar.js @@ -37,9 +37,10 @@ * * ***** END LICENSE BLOCK ***** */ -function calWcapCalendar() { +function calWcapCalendar( calId, session ) { this.wrappedJSObject = this; - this.m_observers = []; + this.m_calId = calId; + this.m_session = session; } calWcapCalendar.prototype = { m_ifaces: [ Components.interfaces.calIWcapCalendar, @@ -99,15 +100,9 @@ calWcapCalendar.prototype = { toString: function() { - var str; - if (this.m_session == null) { - str = "no session"; - } - else { - str = this.session.toString(); - if (this.m_calId != null && this.m_calId != this.userId) - str += (", calId=" + this.m_calId); - } + var str = this.session.toString(); + if (this.calId != this.session.userId) + str += (", calId=" + this.calId); return str; }, log: @@ -149,65 +144,31 @@ calWcapCalendar.prototype = { get readOnly() { return (this.m_bReadOnly || !this.isOwnedCalendar); }, set readOnly( bReadOnly ) { this.m_bReadOnly = bReadOnly; }, - m_uri: null, - get uri() { return this.m_uri }, - set uri( thatUri ) - { - if (this.m_uri == null || thatUri == null || - !this.m_uri.equals(thatUri)) - { - if (this.m_session != null) { - this.m_session.logout(); - this.m_session = null; - } - this.m_uri = null; - this.m_calId = null; - if (thatUri != null) { - this.m_uri = thatUri.clone(); - this.m_calId = decodeURIComponent( thatUri.username ); - if (this.m_calId == "") { - this.m_calId = null; - } - } - this.refresh(); - } + // xxx todo: will potentially vanish from calICalendar: + get uri() { return this.session.uri; }, + set uri( thatUri ) { + this.session.uri = thatUri; + this.m_calProps = null; }, - - 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 ); - } - } ); + function( func, args ) { + this.session.notifyObservers( func, args ); }, - addObserver: - function( observer ) - { - if (this.m_observers.indexOf( observer ) == -1) { - this.m_observers.push( observer ); - } + function( observer ) { + this.session.addObserver( observer ); }, - removeObserver: - function( observer ) - { - this.m_observers = this.m_observers.filter( - function(x) { return x != observer; } ); + function( observer ) { + this.session.removeObserver( observer ); }, // xxx todo: batch currently not used startBatch: function() { this.notifyObservers( "onStartBatch", [] ); }, endBatch: function() { this.notifyObservers( "onEndBatch", [] ); }, + // xxx todo: rework like in + // https://bugzilla.mozilla.org/show_bug.cgi?id=257428 m_bSuppressAlarms: true /* xxx todo: off for now until all problems are solved */, get suppressAlarms() { @@ -218,345 +179,85 @@ calWcapCalendar.prototype = { }, get canRefresh() { return false; }, - - refresh: - function() - { + refresh: function() { // no-op this.log("refresh()"); }, - getCommandUrl: - function( wcapCommand ) - { - if (this.uri == null) - throw new Error("no URI!"); - var session = this.session; - // ensure established session, so userId is set; - // (calId defaults to userId) if not set: - session.getSessionId(); - return (session.uri.spec + wcapCommand + - ".wcap?appid=mozilla-lightning"); - }, - - issueRequest: - function( url, issueFunc, dataConvFunc, receiverFunc ) - { - var sessionId = this.session.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_.session.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; - }, - - m_session: null, - get session() { - if (this.m_session == null) { - this.m_session = getSession( this.uri ); - } - return this.m_session; - }, - // calIWcapCalendar: - getWcapErrorString: - function( rc ) - { - return wcapErrorToString(rc); - }, + m_session: null, + get session() { return this.m_session; }, - // xxx todo: which userId is used when for offline scheduling? - // if not logged in, no calId/userId is known... => UI m_calId: null, get calId() { - var userId = this.userId; // assure being logged in + var userId = this.session.userId; // assure being logged in return this.m_calId || userId; }, + // xxx todo: for now to make subscriptions context menu work, + // will vanish when UI has been revised and every subscribed + // calendar has its own calICalendar object... + // poking calId, so default calendar will then behave + // like a subscribed one... set calId( id ) { this.log( "setting calId to " + id ); this.m_calId = id; }, - get userId() { return this.session.userId; }, + + get description() { + var ar = this.getCalendarProperties("X-NSCP-CALPROPS-DESCRIPTION", {}); + if (ar.length < 1) { + return this.calId; // fallback + } + return ar[0]; + }, get isOwnedCalendar() { - return (this.calId == this.userId || - this.calId.indexOf(this.userId + ":") == 0); - }, - - 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 ); - return (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 ); - } - catch (exc) { - this.notifyError( exc ); - throw exc; - } - }, - - getCalIds: - function( out_count, bGetOwnedCals ) - { - try { - var url = this.getCommandUrl( - bGetOwnedCals ? "list" : "list_subscribed" ); - var ret = []; - var xml = this.issueSyncRequest( - url + "&fmt-out=text%2Fxml", stringToXml ); - var nodeList = xml.getElementsByTagName( - bGetOwnedCals ? "X-S1CS-CALPROPS-OWNED-CALENDAR" - : "X-S1CS-CALPROPS-SUBSCRIBED-CALENDAR" ); - for ( var i = 0; i < nodeList.length; ++i ) { - ret.push( nodeList.item(i).textContent ); - } - out_count.value = ret.length; - return ret; - } - catch (exc) { - this.notifyError( exc ); - throw exc; - } - }, - - getOwnedCalendars: - function( out_count ) - { - return this.getCalIds( out_count, true ); - }, - - getSubscribedCalendars: - function( out_count ) - { - return this.getCalIds( out_count, false ); - }, - - 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 ); - } - catch (exc) { - this.notifyError( exc ); - throw exc; - } - }, - - subscribeToCalendars: - function( count, calIds ) - { - this.modifyCalendarSubscriptions( calIds, true ); - }, - - unsubscribeFromCalendars: - function( count, calIds ) - { - this.modifyCalendarSubscriptions( calIds, false ); - }, - - 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 userId = this.session.userId; + return (this.calId == userId || + this.calId.indexOf(userId + ":") == 0); }, m_calProps: null, - m_calPropsCalid: null, getCalendarProperties: - function( propName, calId, out_count ) + function( propName, out_count ) + { + this.getCalProps_(false /* !async: waits for response */); + var ret = []; + if (this.m_calProps != null) { + var nodeList = this.m_calProps.getElementsByTagName(propName); + for ( var i = 0; i < nodeList.length; ++i ) { + ret.push( trimString(nodeList.item(i).textContent) ); + } + } + out_count.value = ret.length; + return ret; + }, + getCalProps_: + function( bAsync ) { try { - if (calId.length == 0) { - calId = this.calId; + if (this.m_calProps == null) { + var url = this.session.getCommandUrl( "get_calprops" ); + url += ("&calid=" + encodeURIComponent(this.calId)); + url += "&fmt-out=text%2Fxml"; + var this_ = this; + function resp( wcapResponse ) { + try { + // first statement, may throw: + var xml = wcapResponse.data; + if (this_.m_calProps == null) + this_.m_calProps = xml; + } + catch (exc) { + this_.notifyError( exc ); + } + } + if (bAsync) + this.session.issueAsyncRequest( url, stringToXml, resp ); + else + this.session.issueSyncRequest( url, stringToXml, resp ); } - if (this.m_calPropsCalid != calId) { - var url = this.getCommandUrl( "get_calprops" ); - url += ("&calid=" + encodeURIComponent(calId)); - this.m_calProps = this.issueSyncRequest( - url + "&fmt-out=text%2Fxml", stringToXml ); - this.m_calPropsCalid = calId; - } - var ret = []; - var nodeList = this.m_calProps.getElementsByTagName( propName ); - for ( var i = 0; i < nodeList.length; ++i ) { - ret.push( nodeList.item(i).textContent ); - } - out_count.value = ret.length; - return ret; } catch (exc) { this.notifyError( exc ); @@ -565,13 +266,12 @@ calWcapCalendar.prototype = { }, get defaultTimezone() { - var tzid = this.getCalendarProperties("X-NSCP-CALPROPS-TZID", "", {}); + var tzid = this.getCalendarProperties("X-NSCP-CALPROPS-TZID", {}); if (tzid.length < 1) { return "UTC"; // fallback } return tzid[0]; }, - // set defaultTimezone( tzid ) { // if (this.readOnly) // throw Components.interfaces.calIErrors.CAL_IS_READONLY; @@ -590,6 +290,6 @@ calWcapCalendar.prototype = { } else // is ok (supported): return tzid; - } + } }; diff --git a/calendar/providers/wcap/calWcapCalendarItems.js b/calendar/providers/wcap/calWcapCalendarItems.js index 09bd8cee130c..b7e3c72e7109 100644 --- a/calendar/providers/wcap/calWcapCalendarItems.js +++ b/calendar/providers/wcap/calWcapCalendarItems.js @@ -194,8 +194,8 @@ calWcapCalendar.prototype.getRecurrenceParams = function( calWcapCalendar.prototype.getStoreUrl = function( item ) { var bIsEvent = isEvent(item); - var url = this.getCommandUrl( bIsEvent ? "storeevents" - : "storetodos" ); + var url = this.session.getCommandUrl( bIsEvent ? "storeevents" + : "storetodos" ); url += "&fetch=1&compressed=1&recurring=1"; url += ("&calid=" + encodeURIComponent(this.calId)); @@ -292,7 +292,7 @@ calWcapCalendar.prototype.getStoreUrl = function( item ) forceRSVP = true; } else { - var userId = this.userId; + var userId = this.session.userId; if (userId == null) userId = this.calId; // fallback var i = 0; @@ -478,7 +478,7 @@ calWcapCalendar.prototype.adoptItem = function( item, iListener ) url += "&storetype=1"; var this_ = this; - this.issueAsyncRequest( + this.session.issueAsyncRequest( url + "&fmt-out=text%2Fcalendar", stringToIcal, function( wcapResponse ) { this_.adoptItem_resp( wcapResponse, iListener ); @@ -573,7 +573,7 @@ calWcapCalendar.prototype.modifyItem = function( } var this_ = this; - this.issueAsyncRequest( + this.session.issueAsyncRequest( url + "&fmt-out=text%2Fcalendar", stringToIcal, function( wcapResponse ) { this_.modifyItem_resp( wcapResponse, @@ -634,7 +634,7 @@ calWcapCalendar.prototype.deleteItem = function( item, iListener ) if (item.id == null) throw new Error("no item id!"); - var url = this.getCommandUrl( + var url = this.session.getCommandUrl( isEvent(item) ? "deleteevents_by_id" : "deletetodos_by_id" ); url += ("&calid=" + encodeURIComponent(this.calId)); url += ("&uid=" + item.id); @@ -653,7 +653,7 @@ calWcapCalendar.prototype.deleteItem = function( item, iListener ) } var this_ = this; - this.issueAsyncRequest( + this.session.issueAsyncRequest( url + "&fmt-out=text%2Fxml", stringToXml, function( wcapResponse ) { this_.deleteItem_resp( wcapResponse, item, iListener ); @@ -885,14 +885,14 @@ calWcapCalendar.prototype.getItem = function( id, iListener ) params += ("&uid=" + id); try { // most common: event - this.issueSyncRequest( - this.getCommandUrl( "fetchevents_by_id" ) + params, + this.session.issueSyncRequest( + this.session.getCommandUrl( "fetchevents_by_id" ) + params, stringToIcal, syncResponseFunc ); } catch (exc) { // try again, may be a task: - this.issueSyncRequest( - this.getCommandUrl( "fetchtodos_by_id" ) + params, + this.session.issueSyncRequest( + this.session.getCommandUrl( "fetchtodos_by_id" ) + params, stringToIcal, syncResponseFunc ); } } @@ -960,7 +960,7 @@ calWcapCalendar.prototype.getItems = function( ",\n\trangeStart=" + zRangeStart + ",\n\trangeEnd=" + zRangeEnd ); try { - var url = this.getCommandUrl( "fetchcomponents_by_range" ); + var url = this.session.getCommandUrl( "fetchcomponents_by_range" ); url += ("&calid=" + encodeURIComponent(this.calId)); url += "&compressed=1&recurring=1"; @@ -998,7 +998,7 @@ calWcapCalendar.prototype.getItems = function( url += ("&dtend=" + zRangeEnd); var this_ = this; - this.issueAsyncRequest( + this.session.issueAsyncRequest( url + "&fmt-out=text%2Fcalendar", stringToIcal, function( wcapResponse ) { this_.getItems_resp( wcapResponse, @@ -1160,10 +1160,10 @@ calWcapCalendar.prototype.syncChangesTo = function( if (dtFrom == null) { this.log( "syncChangesTo(): doing initial sync." ); syncState.acquire(); - var url = this.getCommandUrl( "fetchcomponents_by_range" ); + var url = this.session.getCommandUrl( "fetchcomponents_by_range" ); url += ("&compressed=1&recurring=1&calid=" + encodeURIComponent(this.calId)); - this.issueAsyncRequest( + this.session.issueAsyncRequest( url + "&fmt-out=text%2Fcalendar", stringToIcal, function( wcapResponse ) { this_.syncChangesTo_resp( @@ -1186,8 +1186,8 @@ calWcapCalendar.prototype.syncChangesTo = function( encodeURIComponent(this.calId)); params += ("&fmt-out=text%2Fcalendar&dtstart=" + zdtFrom); syncState.acquire(); - this.issueAsyncRequest( - this.getCommandUrl( "fetchcomponents_by_lastmod" ) + params, + this.session.issueAsyncRequest( + this.session.getCommandUrl("fetchcomponents_by_lastmod")+params, stringToIcal, function( wcapResponse ) { this_.syncChangesTo_resp( @@ -1215,8 +1215,8 @@ calWcapCalendar.prototype.syncChangesTo = function( var deleteItemListener = new FinishListener( Components.interfaces.calIOperationListener.DELETE, syncState ); syncState.acquire(); - this.issueAsyncRequest( - this.getCommandUrl( "fetch_deletedcomponents" ) + params, + this.session.issueAsyncRequest( + this.session.getCommandUrl("fetch_deletedcomponents") + params, stringToIcal, function( wcapResponse ) { this_.syncChangesTo_resp( diff --git a/calendar/providers/wcap/calWcapCalendarModule.js b/calendar/providers/wcap/calWcapCalendarModule.js index 60f9d2ac1956..6e3169c0ff1e 100644 --- a/calendar/providers/wcap/calWcapCalendarModule.js +++ b/calendar/providers/wcap/calWcapCalendarModule.js @@ -45,6 +45,12 @@ var calWcapCalendarModule = { classID: Components.ID("{CF4D93E5-AF79-451a-95F3-109055B32EF0}") }, + WcapSessionInfo: { + classDescription: "Sun Java System Calendar Server WCAP Session", + contractID: "@mozilla.org/calendar/session;1?type=wcap", + classID: Components.ID("{CBF803FD-4469-4999-AE39-367AF1C7B077}") + }, + registerSelf: function( compMgr, fileSpec, location, type ) { @@ -55,6 +61,11 @@ var calWcapCalendarModule = { this.WcapCalendarInfo.classDescription, this.WcapCalendarInfo.contractID, fileSpec, location, type ); + compMgr.registerFactoryLocation( + this.WcapSessionInfo.classID, + this.WcapSessionInfo.classDescription, + this.WcapSessionInfo.contractID, + fileSpec, location, type ); }, m_scriptsLoaded: false, @@ -95,12 +106,16 @@ var calWcapCalendarModule = { throw Components.results.NS_ERROR_NO_AGGREGATION; var cal; switch (CACHE) { - case "memory": - case "storage": - cal = new calWcapCachedCalendar(); - break; +// unsupported until fixed: +// case "memory": +// case "storage": +// cal = new calWcapCachedCalendar(); +// break; default: - cal = new calWcapCalendar(); + cal = new calWcapCalendar( + null /* calId: indicates default calendar */, + new calWcapSession() ); + cal.session.defaultCalendar = cal; break; } return cal.QueryInterface( iid ); diff --git a/calendar/providers/wcap/calWcapRequest.js b/calendar/providers/wcap/calWcapRequest.js index 9202e38ef495..3eb8e3c5ff2f 100644 --- a/calendar/providers/wcap/calWcapRequest.js +++ b/calendar/providers/wcap/calWcapRequest.js @@ -147,6 +147,7 @@ function issueAsyncRequest( url, receiverFunc ) logMessage( "issueAsyncRequest( \"" + url + "\" )", "opening channel." ); var channel = getIoService().newChannel( url, "" /* charset */, null /* baseURI */ ); + channel.loadFlags |= Components.interfaces.nsIRequest.LOAD_BYPASS_CACHE; loader.init( channel, reader, null /* context */, 0 /* segment size */ ); } @@ -180,6 +181,7 @@ function issueSyncRequest( url, receiverFunc, bLogging ) } var channel = getIoService().newChannel( url, "" /* charset */, null /* baseURI */ ); + channel.loadFlags |= Components.interfaces.nsIRequest.LOAD_BYPASS_CACHE; var stream = channel.open(); if (bLogging && LOG_LEVEL > 1) { logMessage( "issueSyncRequest( \"" + url + "\" )", diff --git a/calendar/providers/wcap/calWcapSession.js b/calendar/providers/wcap/calWcapSession.js index c504bb781d99..44bb69e386cc 100644 --- a/calendar/providers/wcap/calWcapSession.js +++ b/calendar/providers/wcap/calWcapSession.js @@ -41,36 +41,75 @@ var g_serverTimeDiffs = {}; var g_allSupportedTimezones = {}; -function calWcapSession( thatUri ) { +function calWcapSession() { this.wrappedJSObject = this; - // 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 = ""; - + 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); + .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_uri: null, - m_sessionId: null, - m_userId: null, - m_bNoLoginsAnymore: false, + m_ifaces: [ Components.interfaces.calIWcapSession, + Components.interfaces.nsIInterfaceRequestor, + Components.interfaces.nsIClassInfo, + Components.interfaces.nsISupports ], - get uri() { return this.m_uri; }, + // 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 ) @@ -92,17 +131,51 @@ calWcapSession.prototype = { { return logMessage( this.toString(), msg ); }, - - // nsISupports: - QueryInterface: - function( iid ) + logError: + function( err, context ) { - if (iid.equals(Components.interfaces.nsIObserver) || - iid.equals(Components.interfaces.nsISupports)) { - return this; + 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 ); } - else - throw Components.results.NS_ERROR_NO_INTERFACE; + }, + + removeObserver: + function( observer ) + { + this.m_observers = this.m_observers.filter( + function(x) { return x != observer; } ); }, // nsIObserver: @@ -125,8 +198,6 @@ calWcapSession.prototype = { } }, - get userId() { return this.m_userId; }, - getSupportedTimezones: function( bRefresh ) { @@ -219,6 +290,9 @@ calWcapSession.prototype = { return realm; }, + m_sessionId: null, + m_bNoLoginsAnymore: false, + getSessionId: function( timedOutSessionId ) { @@ -267,6 +341,11 @@ calWcapSession.prototype = { 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 */); + } } } catch (exc) { @@ -289,7 +368,7 @@ calWcapSession.prototype = { Components.classes["@mozilla.org/passwordmanager;1"] .getService(Components.interfaces.nsIPasswordManager); - var outUser = { value: this.m_userId }; + var outUser = { value: this.userId }; var outPW = { value: null }; var enumerator = passwordManager.enumerator; @@ -332,7 +411,7 @@ calWcapSession.prototype = { else { this.log( "password entry found for user " + outUser.value ); try { - this.login( loginUri, outUser.value, outPW.value ); + this.login_( loginUri, outUser.value, outPW.value ); } catch (exc) { // ignore silently } @@ -370,8 +449,8 @@ calWcapSession.prototype = { // user/pw has been found previously, // but no login was possible, // try again using http here: - this.login( loginUri, - outUser.value, outPW.value ); + this.login_( loginUri, + outUser.value, outPW.value ); if (this.m_sessionId != null) return this.m_sessionId; } @@ -416,7 +495,7 @@ calWcapSession.prototype = { savePW )) { try { - this.login( loginUri, outUser.value, outPW.value ); + this.login_( loginUri, outUser.value, outPW.value ); } catch (exc) { Components.utils.reportError( exc ); @@ -439,6 +518,34 @@ calWcapSession.prototype = { 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 ) @@ -496,33 +603,112 @@ calWcapSession.prototype = { "loginDialog.text", loginTextVars, loginTextVars.length ); }, - login: - function( loginUri, user, pw ) + getCommandUrl: + function( wcapCommand ) { - 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; + 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 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." ); + 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; }, + + login: + function() + { + this.logout(); // assure being logged out + this.getSessionId(); }, logout: @@ -544,18 +730,278 @@ calWcapSession.prototype = { Components.utils.reportError( exc ); } this.m_sessionId = null; - this.m_userId = null; } + this.m_userId = null; // ask next time we log in: var this_ = this; g_httpHosts = g_httpHosts.filter( function(hostEntry) { return (hostEntry.m_host != this_.uri.hostPort); } ); this.m_bNoLoginsAnymore = false; + }, + + getWcapErrorString: + function( rc ) + { + return wcapErrorToString(rc); + }, + + get defaultCalendar() { + return this.getCalendarByCalId(this.userId); + }, + set defaultCalendar(cal) { + this.m_defaultCalendar = cal; + }, + + m_defaultCalendar: null, + m_calIdToCalendar: {}, + getCalendarByCalId: + function( calId ) + { + if (calId == null || this.userId == calId) { + if (this.m_defaultCalendar == null) + this.m_defaultCalendar = new calWcapCalendar(this.userId); + return this.m_defaultCalendar; + } + else { + var key = encodeURIComponent(calId); + var ret = this.m_calIdToCalendar[key]; + if (!ret) { + ret = new calWcapCalendar( calId, this ); + this.m_calIdToCalendar[key] = ret; + } + 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; + } } }; -g_httpHosts = []; +var g_httpHosts = []; function confirmUnsecureLogin( uri ) { var host = uri.hostPort; @@ -575,15 +1021,3 @@ function confirmUnsecureLogin( uri ) return bConfirmed; } -g_sessions = {}; -function getSession( uri ) -{ - var session = g_sessions[uri.spec]; - if (!session) { - logMessage( "getSession()", "entering session for uri=" + uri.spec ); - var session = new calWcapSession( uri ); - g_sessions[uri.spec] = session; - } - return session; -} - diff --git a/calendar/providers/wcap/calWcapUtils.js b/calendar/providers/wcap/calWcapUtils.js index 8851a2f41f7b..43cb9d573ace 100644 --- a/calendar/providers/wcap/calWcapUtils.js +++ b/calendar/providers/wcap/calWcapUtils.js @@ -299,6 +299,11 @@ function forEachIcalComponent( icalRootComp, componentType, func, maxResult ) } } +function trimString( str ) +{ + return str.replace( /(^\s+|\s+$)/g, "" ); +} + function getTime() { var ret = new CalDateTime(); diff --git a/calendar/providers/wcap/public/Makefile.in b/calendar/providers/wcap/public/Makefile.in index 95f90d721c3b..f2cedd1e54cd 100644 --- a/calendar/providers/wcap/public/Makefile.in +++ b/calendar/providers/wcap/public/Makefile.in @@ -51,6 +51,7 @@ XPIDLSRCS = calIWcapCalendar.idl \ calIWcapErrors.idl \ calIWcapFreeBusyEntry.idl \ calIWcapFreeBusyListener.idl \ + calIWcapSession.idl \ $(NULL) include $(topsrcdir)/config/rules.mk diff --git a/calendar/providers/wcap/public/calIWcapCalendar.idl b/calendar/providers/wcap/public/calIWcapCalendar.idl index 1e8a8972c7dc..902d2d858622 100644 --- a/calendar/providers/wcap/public/calIWcapCalendar.idl +++ b/calendar/providers/wcap/public/calIWcapCalendar.idl @@ -38,9 +38,8 @@ * ***** END LICENSE BLOCK ***** */ #include "calICalendar.idl" -#include "calIWcapFreeBusyListener.idl" #include "calIDateTime.idl" -#include "calIICSService.idl" +interface calIWcapSession; /** Adds WCAP specific capabilities. */ @@ -48,164 +47,46 @@ interface calIWcapCalendar : calICalendar { /** - * Current userId; if the user is not logged in, a dialog will prompt for. + * User session this calendar instance belongs to. */ - readonly attribute string userId; + readonly attribute calIWcapSession session; + + /** + * Calendar description. + */ + readonly attribute string description; /** * Current calId the calendar instance acts on; defaults to userId. */ - attribute string calId; + /*readonly xxx todo*/ attribute string calId; /** * Whether the currently selected calendar belongs to user. */ readonly attribute boolean isOwnedCalendar; +// /** +// * Gets or sets this calendar's (calId) default timezone. +// */ +// attribute string defaultTimezone; + /** - * Gets or sets this calendar's (calId) default timezone. - */ - readonly attribute string defaultTimezone; - - /** - * Gets calendar properties for specified calId. + * Gets calendar properties. * An error is notified to all registered calIObservers, then thrown. * * @param propName property name (e.g. X-S1CS-CALPROPS-COMMON-NAME) - * @param calId a calid, "mailto:rfc822addr" or - * empty string (=> current user's calId) * @return array of property values */ void getCalendarProperties( in string propName, - in string calId, out unsigned long count, [array, size_is(count), retval] out string properties ); - /** - * Gets a text for an error code. - * - * @param rc error code defined in calIWcapErrors - * @return error string - * @exception NS_ERROR_INVALID_ARG - */ - string getWcapErrorString( in unsigned long rc ); - - /** - * Creates a new calendar for user. - * An error is notified to all registered calIObservers, then thrown. - * - * @param calId calendar's calId (portion); - * without user's id, e.g. "test-cal". - * valid characters for the calId parameter are: - * - Alphabet characters (A-Z, a-z) - * - Numeric characters (0-9) - * - Three special characters - * - Dash (-) - * - Underscore (_) - * - Period (.) - * @param name calendar's name, e.g. "My Work Cal" - * @param bAllowDoubleBooking whether double booking (events/todos) is - * allowed - * @param bSetCalProps whether properties of the new calendar are set - * @param bAddToSubscribed allows a user to specify if the newly - * created calendar should be added to the - * user’s subscribed calendar list - * @return calId of created calendar - */ - string createCalendar( - in string calId, - in string name, - in boolean bAllowDoubleBooking, - in boolean bSetCalProps, - in boolean bAddToSubscribed ); - - /** - * Deletes a calendar. - * An error is notified to all registered calIObservers, then thrown. - * - * @param calId full calId (incl. ":") - * @param bRemoveFromSubscribed whether calendar ought to be removed - * from subscription list - */ - void deleteCalendar( - in string calId, - in boolean bRemoveFromSubscribed ); - - /** - * Gets own calendars. - * An error is notified to all registered calIObservers, then thrown. - * - * @return owned calendars (string array of "calId1$Description1", ...) - */ - void getOwnedCalendars( - out unsigned long count, - [array, size_is(count), retval] out string ownCalendars ); - - /** - * Gets subscribed calendars. - * An error is notified to all registered calIObservers, then thrown. - * - * @return subscribed calendars (string array of "calId1$Description1",...) - */ - void getSubscribedCalendars( - out unsigned long count, - [array, size_is(count), retval] out string subscribedCalendars ); - - /** - * Subscribe to calendar(s). - * An error is notified to all registered calIObservers, then thrown. - * - * @param count number of calIds - * @param calIds array of calIds (calid or "mailto:rfc822addr") - */ - void subscribeToCalendars( - in unsigned long count, - [array, size_is(count)] in string calIds ); - - /** - * Unsubscribe from calendar(s). - * An error is notified to all registered calIObservers, then thrown. - * - * @param count number of calIds - * @param calIds array of calIds (calid or "mailto:rfc822addr") - */ - void unsubscribeFromCalendars( - in unsigned long count, - [array, size_is(count)] in string calIds ); - - /** - * Gets free-busy entries for calid. - * Results are notifies to passed listener instance. - * An error is notified to all registered calIObservers and - * to calIWcapFreeBusyListener::onGetFreeBusyTimes with rc != NS_OK. - * Additionally, when an error occurs within getFreeBusyTimes, - * the error is also thrown. - * - * @param calId a calid or "mailto:rfc822addr" - * @param dtRangeStart start time of free-busy search - * @param dtRangeEnd end time of free-busy search - * @param bBusyOnly whether to return busy entries only - * @param iListener listener receiving results - * @param bAsync whether the listener receives results asynchronously - * @param requestId request id to distinguish asynchronous requests - */ - void getFreeBusyTimes( - in string calId, - in calIDateTime dtRangeStart, - in calIDateTime dtRangeEnd, - in boolean bBusyOnly, - in calIWcapFreeBusyListener iListener, - in boolean bAsync, - in unsigned long requestId ); - - /** xxx todo: to be moved to calIOperationListener? - */ - const unsigned long SYNC = 5; - /* xxx todo: additional filters sensible for calICalendar, too? claiming bits 24-30 for now. */ + /** * Scope: Attendee * The event or todo is an invitation from another @@ -249,7 +130,13 @@ interface calIWcapCalendar : calICalendar * user to other invitees, and is currently awaiting. */ const unsigned long ITEM_FILTER_REQUEST_WAITFORREPLY = 1 << 30; + + /* xxx todo sync feature: separate into own interface? */ + /** xxx todo: to be moved to calIOperationListener? + */ + const unsigned long SYNC = 5; + /** * Syncs in changes since time dtFrom. * The passed iListener receives a diff --git a/calendar/providers/wcap/public/calIWcapFreeBusyListener.idl b/calendar/providers/wcap/public/calIWcapFreeBusyListener.idl index 3136d717b04c..24fae23efbfc 100644 --- a/calendar/providers/wcap/public/calIWcapFreeBusyListener.idl +++ b/calendar/providers/wcap/public/calIWcapFreeBusyListener.idl @@ -38,6 +38,7 @@ * ***** END LICENSE BLOCK ***** */ #include "calIWcapFreeBusyEntry.idl" +#include "calIWcapCalendar.idl" [scriptable, uuid(4498E0D0-04EC-43a3-92E0-560667696CF3)] interface calIWcapFreeBusyListener : nsISupports diff --git a/calendar/providers/wcap/public/calIWcapSession.idl b/calendar/providers/wcap/public/calIWcapSession.idl new file mode 100755 index 000000000000..f16e76f03dfb --- /dev/null +++ b/calendar/providers/wcap/public/calIWcapSession.idl @@ -0,0 +1,228 @@ +/* -*- Mode: C++; 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 ***** */ + +#include "calIWcapCalendar.idl" +#include "calIWcapFreeBusyListener.idl" + +/** + * WCAP session. + */ +[scriptable, uuid(477B4534-C297-40a1-ADF2-5A7E2A81816A)] +interface calIWcapSession : nsISupports +{ + /** + * Setting this URI causes the session to be disconnected. + */ + attribute nsIURI uri; + + /** + * User that has established this session. + * Reading this attribute prompts for login if the session has not yet + * been established. + * An error is notified to all registered calIObservers, then thrown. + */ + readonly attribute string userId; + + /** + * Explicitly performs a session establishment. + * Commonly not needed, because any attempt to get a calendar instance + * will establish a session automatically. + * If the user is alread logged in, she will be logged out before. + * UI will prompt for a userId and password. + * An error is notified to all registered calIObservers, then thrown. + */ + void login(); + + /** + * Explicitly performs a session logout. + * Commonly not needed, because the user will be logged out upon + * "network:offline-about-to-go-offline" and "quit-application" + * automatically. + * An error is notified to all registered calIObservers, then thrown. + */ + void logout(); + + /** + * Gets a text for an error code. + * + * @param rc error code defined in calIWcapErrors + * @return error string + * @exception NS_ERROR_INVALID_ARG + */ + string getWcapErrorString( in unsigned long rc ); + + /** + * The user's default calendar. + * An error is notified to all registered calIObservers, then thrown. + */ + readonly attribute calIWcapCalendar defaultCalendar; + + /** + * Gets a calendar instance for the passed calId using this session. + * An error is notified to all registered calIObservers, then thrown. + * + * @param calId full calId (incl. ":") + * @return calendar instance + */ + calIWcapCalendar getCalendarByCalId( in string calId ); + + /** + * Gets calendars where the user is the primary owner + * (including default calendar). + * An error is notified to all registered calIObservers, then thrown. + * + * @return array of owned calendars + */ + void getOwnedCalendars( + out unsigned long count, + [array, size_is(count), retval] out calIWcapCalendar ownedCals ); + + /** + * Gets subscribed calendars (may include calendars where the user + * is the primary owner). + * An error is notified to all registered calIObservers, then thrown. + * + * @return array of subscribed calendars + */ + void getSubscribedCalendars( + out unsigned long count, + [array, size_is(count), retval] out calIWcapCalendar subscribedCals ); + + /** + * Creates a new calendar for the session's user. + * An error is notified to all registered calIObservers, then thrown. + * + * @param calId calendar's calId (portion); + * without user's id, e.g. "test-cal". + * valid characters for the calId parameter are: + * - Alphabet characters (A-Z, a-z) + * - Numeric characters (0-9) + * - Three special characters + * - Dash (-) + * - Underscore (_) + * - Period (.) + * @param name calendar's name, e.g. "My Work Cal" + * @param bAllowDoubleBooking whether double booking (events/todos) is + * allowed + * @param bSetCalProps whether properties of the new calendar are set + * @param bAddToSubscribed allows a user to specify if the newly + * created calendar should be added to the + * user’s subscribed calendar list + * @return created calendar + */ + calIWcapCalendar createCalendar( + in string calId, + in string name, + in boolean bAllowDoubleBooking, + in boolean bSetCalProps, + in boolean bAddToSubscribed ); + + /** + * Deletes a calendar. + * An error is notified to all registered calIObservers, then thrown. + * + * @param calId full calId (incl. ":") + * @param bRemoveFromSubscribed whether calendar ought to be removed + * from subscription list + */ + void deleteCalendar( + in string calId, + in boolean bRemoveFromSubscribed ); + + /** + * Subscribe to calendar(s). + * An error is notified to all registered calIObservers, then thrown. + * + * @param count number of calIds + * @param calIds array of calIds (calid or "mailto:rfc822addr") + */ + void subscribeToCalendars( + in unsigned long count, + [array, size_is(count)] in string calIds ); + + /** + * Unsubscribe from calendar(s). + * An error is notified to all registered calIObservers, then thrown. + * + * @param count number of calIds + * @param calIds array of calIds (calid or "mailto:rfc822addr") + */ + void unsubscribeFromCalendars( + in unsigned long count, + [array, size_is(count)] in string calIds ); + + /** + * Gets the user's preferences. + * An error is notified to all registered calIObservers, then thrown. + * + * @param prefName preference name + * @return array of preference values + */ + void getUserPreferences( + in string prefName, + out unsigned long count, + [array, size_is(count), retval] out string properties ); + + /* xxx todo freebusy: separate into own interface? */ + /** + * Gets free-busy entries for calid. + * Results are notifies to passed listener instance. + * An error is notified to all registered calIObservers and + * to calIWcapFreeBusyListener::onGetFreeBusyTimes with rc != NS_OK. + * Additionally, when an error occurs within getFreeBusyTimes, + * the error is also thrown. + * + * @param calId a calid or "mailto:rfc822addr" + * @param dtRangeStart start time of free-busy search + * @param dtRangeEnd end time of free-busy search + * @param bBusyOnly whether to return busy entries only + * @param iListener listener receiving results + * @param bAsync whether the listener receives results asynchronously + * @param requestId request id to distinguish asynchronous requests + */ + void getFreeBusyTimes( + in string calId, + in calIDateTime dtRangeStart, + in calIDateTime dtRangeEnd, + in boolean bBusyOnly, + in calIWcapFreeBusyListener iListener, + in boolean bAsync, + in unsigned long requestId ); +}; +