From 79f422b4c1829374e36e3df105f2fe02ffefb7c4 Mon Sep 17 00:00:00 2001 From: "mozilla%kewis.ch" Date: Mon, 28 Apr 2008 11:18:27 +0000 Subject: [PATCH] Fix bug 428392 - Accepting Events not adding event to calender - falsely reporting "not an attendee". r=ctalbert --- calendar/base/public/calICalendar.idl | 2 + calendar/base/public/calIItipItem.idl | 8 ++ calendar/base/public/calIItipTransport.idl | 11 +++ calendar/base/src/calItipItem.js | 9 +++ calendar/base/src/calItipProcessor.js | 78 ++++++++----------- calendar/base/src/calUtils.js | 14 ++-- calendar/itip/calItipEmailTransport.js | 5 ++ .../lightningTextCalendarConverter.js | 8 +- calendar/lightning/content/imip-bar.js | 59 +++++++++----- 9 files changed, 113 insertions(+), 81 deletions(-) diff --git a/calendar/base/public/calICalendar.idl b/calendar/base/public/calICalendar.idl index 0791fb32098..25a999b14ec 100644 --- a/calendar/base/public/calICalendar.idl +++ b/calendar/base/public/calICalendar.idl @@ -128,6 +128,8 @@ interface calICalendar : nsISupports * e.g. this generally applies to network calendars; * default is true (if not present). * [boolean] cache.enabled If true, the calendar is cached; default is false. + * [string] itip.transportType If the provider implements a custom calIItipTransport + * this is the "type" used in the contract id. * * The following calendar capabilities can be used to inform the UI or backend * that certain features are not supported. If not otherwise mentioned, not diff --git a/calendar/base/public/calIItipItem.idl b/calendar/base/public/calIItipItem.idl index 4d3662f5004..04d8aa250d5 100644 --- a/calendar/base/public/calIItipItem.idl +++ b/calendar/base/public/calIItipItem.idl @@ -21,6 +21,7 @@ * Contributor(s): * Clint Talbert * Matthew Willis + * Philipp Kewisch * * 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 @@ -111,6 +112,13 @@ interface calIItipItem : nsISupports */ attribute calICalendar targetCalendar; + /** + * The identity this item was received on. Helps to determine which + * attendee to manipulate. This should be the full email address of the + * attendee that is considered to be the local user. + */ + attribute AUTF8String identity; + /** * localStatus: The response that the user has made to the invitation in * this ItipItem. diff --git a/calendar/base/public/calIItipTransport.idl b/calendar/base/public/calIItipTransport.idl index 786a7618d86..f44c3544a5d 100644 --- a/calendar/base/public/calIItipTransport.idl +++ b/calendar/base/public/calIItipTransport.idl @@ -20,6 +20,7 @@ * * Contributor(s): * Clint Talbert + * Philipp Kewisch * * 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 @@ -51,9 +52,19 @@ interface calIItipTransport : nsISupports /** * Default identity for me within this transport. For example, this is * your default email address in Thunderbird. + * + * XXX This should probably go away, using the default identity is far + * from practical. Only user right now is calUtils's sendItipInvitation. + * See bug 431126. */ readonly attribute AUTF8String defaultIdentity; + /** + * Scheme to be used to prefix attendees. For example, the Email transport + * should reuturn "mailto". + */ + readonly attribute AUTF8String scheme; + /** * Sending identity. This can be set to change the "sender" identity from * defaultIdentity above. diff --git a/calendar/base/src/calItipItem.js b/calendar/base/src/calItipItem.js index 61e901421df..08688251067 100644 --- a/calendar/base/src/calItipItem.js +++ b/calendar/base/src/calItipItem.js @@ -21,6 +21,7 @@ * Contributor(s): * Clint Talbert * Matthew Willis + * Philipp Kewisch * * 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 @@ -146,6 +147,14 @@ calItipItem.prototype = { return (this.mTargetCalendar = aValue); }, + mIdentity: null, + get identity() { + return this.mIdentity; + }, + set identity(aValue) { + return (this.mIdentity = aValue); + }, + mLocalStatus: null, get localStatus() { return this.mLocalStatus; diff --git a/calendar/base/src/calItipProcessor.js b/calendar/base/src/calItipProcessor.js index 6fb6d7a54b2..a0fcd02408f 100644 --- a/calendar/base/src/calItipProcessor.js +++ b/calendar/base/src/calItipProcessor.js @@ -22,6 +22,7 @@ * Clint Talbert * Eva Or * Matthew Willis + * Philipp Kewisch * * 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 @@ -139,32 +140,29 @@ calItipProcessor.prototype = { // Check to see if we have an existing item or not, then continue // processing appropriately - var i =0; - while (calItem) { - this._isExistingItem(calItem, recvMethod, respMethod, targetCalendar, - aListener); - ++i; - calItem = itemList[i]; + for (var i = 0; i < itemList.length; i++) { + this._isExistingItem(itemList[i], aItipItem, recvMethod, respMethod, + targetCalendar, aListener); } // Send the appropriate response // figure out a good way to determine when a response is needed! if (recvMethod != respMethod) { - this._getTransport().simpleSendResponse(respItipItem); + this._getTransport(targetCalendar).simpleSendResponse(respItipItem); } }, /* Continue processing the iTip Item now that we have determined whether * there is an existing item or not. */ - _continueProcessingItem: function cipCPI(newItem, existingItem, recvMethod, respMethod, - calAction, targetCalendar, aListener) { - var transport = this._getTransport(); + _continueProcessingItem: function cipCPI(newItem, existingItem, aItipItem, + recvMethod, respMethod, calAction, + targetCalendar, aListener) { switch (recvMethod) { case "REQUEST": // Only add to calendar if we accepted invite var replyStat = this._getReplyStatus(newItem, - transport.defaultIdentity); + aItipItem.identity); if (replyStat == "DECLINED") { break; } @@ -194,6 +192,7 @@ calItipProcessor.prototype = { recvMethod); } + // TODO bug 431127: This is email specific -> Move to transport // When replying, the reply must only contain the ORGANIZER and the // status of the ATTENDEE that represents ourselves. Therefore we must // remove all other ATTENDEEs from the itipItem we send back. @@ -201,22 +200,15 @@ calItipProcessor.prototype = { // Get the id that represents me. // XXX Note that this doesn't take into consideration invitations // sent to email aliases. (ex: lilmatt vs mwillis) - var me; - var idPrefix; - if (transport.type == "email") { - me = transport.defaultIdentity; - idPrefix = "mailto:"; - } else { - throw Components.results.NS_ERROR_NOT_IMPLEMENTED; - } var attendees = newItem.getAttendees({}); + var transport = this._getTransport(newItem.calendar); for each (var attendee in attendees) { // Leave the ORGANIZER alone. if (!attendee.isOrganizer) { // example: mailto:joe@domain.com - var meString = idPrefix + me; + var meString = transport.scheme + ":" + aItipItem.identity if (attendee.id.toLowerCase() != meString.toLowerCase()) { newItem.removeAttendee(attendee); } @@ -363,7 +355,7 @@ calItipProcessor.prototype = { if (!aExistingItem) throw new Error("_processCalendarAction: Item to update not found"); - // TODO: Handle generation properly - Bug 418345 + // TODO: Handle generation properly - Bug 418345 aCalItem.generation = aExistingItem.generation; // We also have to ensure that the calendar is set properly on // the new item, or items with alarms will throw during the @@ -389,20 +381,10 @@ calItipProcessor.prototype = { * item for the specified attendee */ _getReplyStatus: function cipGRS(aCalItem, aAttendeeId) { - var idPrefix = "mailto:"; - - // example: mailto:joe@domain.com - var idString = idPrefix + aAttendeeId; + var idString = this._getTransport(aCalItem.calendar).scheme + + ":" + aAttendeeId; var attendee = aCalItem.getAttendeeById(idString); - if (!attendee) { - // Bug 420516 -- we don't support delegation yet TODO: Localize this? - throw new Error("_getReplyStatus: " + - "You are not on the list of invited attendees, delegation " + - "is not supported yet. See bug 420516 for details."); - - } - - return attendee.participationStatus; + return attendee && attendee.participationStatus; }, // A placeholder to make sure we don't try to add multiple items from the @@ -414,9 +396,8 @@ calItipProcessor.prototype = { * or not. It then calls _continueProcessingItem setting calAction and * existingItem appropirately */ - _isExistingItem: function cipIEI(aCalItem, aRecvMethod, aRespMethod, + _isExistingItem: function cipIEI(aCalItem, aItipItem, aRecvMethod, aRespMethod, aTargetCal, aListener) { - var foundItemListener = { itipProcessor: this, onOperationComplete: @@ -428,6 +409,7 @@ calItipProcessor.prototype = { this.itipProcessor._handledID = aCalItem.id; this.itipProcessor._continueProcessingItem(aCalItem, null, + aItipItem, aRecvMethod, aRespMethod, CAL_ITIP_PROC_ADD_OP, @@ -441,6 +423,7 @@ calItipProcessor.prototype = { if (aCount && aItems[0]) { this.itipProcessor._continueProcessingItem(aCalItem, aItems[0], + aItipItem, aRecvMethod, aRespMethod, CAL_ITIP_PROC_UPDATE_OP, @@ -455,23 +438,24 @@ calItipProcessor.prototype = { // Then we do not have a target calendar to search, // this is probably a DECLINE reply or some other such response, // allow it to pass through - this._continueProcessingItem(aCalItem, null, aRecvMethod, aRespMethod, - null, aTargetCal, aListener); + this._continueProcessingItem(aCalItem, null, aItipItem, aRecvMethod, + aRespMethod, null, aTargetCal, aListener); } }, /** - * Centralized location for obtaining the proper transport. This way it - * will be easy to support multiple transports in the future - * without changing the existing code too much. + * Centralized location for obtaining the proper transport. If a calendar is + * specified, the transport type is taken from the provider. If the provider + * does not specify a transport, or no calendar is specified, the default + * email transport is returned. */ - _getTransport: function cipGT() { - // XXX Support for transports other than email go here. - // For now we just assume it's email. - var transport = Components.classes["@mozilla.org/calendar/itip-transport;1?type=email"]. - createInstance(Components.interfaces.calIItipTransport); + _getTransport: function cipGT(aCalendar) { + var transportType = (aCalendar && aCalendar.getProperty("itip.transportType")) || "email"; + + var transport = Components.classes["@mozilla.org/calendar/itip-transport;1?type=" + transportType] + .getService(Components.interfaces.calIItipTransport); if (!transport) { - throw new Error("iTipProcessor cannot instantiate transport"); + throw new Error("iTipProcessor cannot instantiate transport" + (aCalendar ? "for " + aCalendar.type : "")); } return transport; } diff --git a/calendar/base/src/calUtils.js b/calendar/base/src/calUtils.js index 36bd7c505f8..df9d9000b39 100644 --- a/calendar/base/src/calUtils.js +++ b/calendar/base/src/calUtils.js @@ -2180,10 +2180,10 @@ function sendItipInvitation(aItem) { // is worked into the event dialog (which has been done in the prototype // dialog to a degree) then we are going to simply hack in some attendee // support so that we can round-trip iTIP invitations. - // Since there is no way to determine the type of transport an - // attendee requires, we default to email - var emlSvc = Components.classes["@mozilla.org/calendar/itip-transport;1?type=email"] - .createInstance(Components.interfaces.calIItipTransport); + var transportType = aItem.calendar.getProperty("itip.transportType") || "email"; + + var transport = Components.classes["@mozilla.org/calendar/itip-transport;1?type=" + transportType] + .getService(Components.interfaces.calIItipTransport); var itipItem = Components.classes["@mozilla.org/calendar/itip-item;1"] .createInstance(Components.interfaces.calIItipItem); @@ -2219,7 +2219,7 @@ function sendItipInvitation(aItem) { // For this support, we'll need a real invitation manager component. var organizer = Components.classes["@mozilla.org/calendar/attendee;1"] .createInstance(Components.interfaces.calIAttendee); - organizer.id = "mailto:" + emlSvc.defaultIdentity; + organizer.id = tranport.scheme + ":" + transport.defaultIdentity; organizer.role = "REQ-PARTICIPANT"; organizer.participationStatus = "ACCEPTED"; organizer.isOrganizer = true; @@ -2251,9 +2251,9 @@ function sendItipInvitation(aItem) { var subject = sb.formatStringFromName("itipRequestSubject", [summary], 1); var body = sb.formatStringFromName("itipRequestBody", - [emlSvc.defaultIdentity, summary], + [transport.defaultIdentity, summary], 2); // Send it! - emlSvc.sendItems(recipients.length, recipients, subject, body, itipItem); + transport.sendItems(recipients.length, recipients, subject, body, itipItem); } diff --git a/calendar/itip/calItipEmailTransport.js b/calendar/itip/calItipEmailTransport.js index 0d86d617a05..e1ab999892f 100644 --- a/calendar/itip/calItipEmailTransport.js +++ b/calendar/itip/calItipEmailTransport.js @@ -21,6 +21,7 @@ * Contributor(s): * Clint Talbert * Matthew Willis + * Philipp Kewisch * * 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 @@ -66,6 +67,10 @@ calItipEmailTransport.prototype = { return this.mDefaultIdentity.email; }, + get scheme() { + return "mailto"; + }, + mSenderAddress: null, get senderAddress() { return this.mSenderAddress; diff --git a/calendar/lightning/components/lightningTextCalendarConverter.js b/calendar/lightning/components/lightningTextCalendarConverter.js index 8b10b85e53b..7845faf058d 100644 --- a/calendar/lightning/components/lightningTextCalendarConverter.js +++ b/calendar/lightning/components/lightningTextCalendarConverter.js @@ -23,6 +23,7 @@ * Clint Talbert * Matthew Willis * Mauro Cicognini + * Philipp Kewisch * * 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 @@ -187,14 +188,7 @@ ltnMimeConverter.prototype = { var observer = Components.classes["@mozilla.org/observer-service;1"]. getService(Components.interfaces.nsIObserverService); observer.notifyObservers(null, "onItipItemCreation", 0); - } else { - // Thunderbird 1.5.x case: We have no choice but to try - // sending the iTIP item directly with the notification - var observer = Components.classes["@mozilla.org/observer-service;1"]. - getService(Components.interfaces.nsIObserverService); - observer.notifyObservers(itipItem, "onItipItemCreation", 0); } - } catch (e) { Components.utils.reportError("convertToHTML: " + "Cannot create itipItem: " + e); diff --git a/calendar/lightning/content/imip-bar.js b/calendar/lightning/content/imip-bar.js index b777e35eeea..f35b9ebadcd 100644 --- a/calendar/lightning/content/imip-bar.js +++ b/calendar/lightning/content/imip-bar.js @@ -21,6 +21,7 @@ * Contributor(s): * Clint Talbert * Matthew Willis + * Philipp Kewisch * * 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 @@ -61,10 +62,6 @@ function checkForItipItem(subject) // This property was set by LightningTextCalendarConverter.js itipItem = sinkProps.getPropertyAsInterface("itipItem", Components.interfaces.calIItipItem); - } else { - // With Thunderbird 1.5.x we have to use the subject to pass the - // iTIP item because we don't have sinkProps available. - itipItem = subject.QueryInterface(Components.interfaces.calIItipItem); } } catch (e) { // This will throw on every message viewed that doesn't have the @@ -74,6 +71,9 @@ function checkForItipItem(subject) return; } + // Get the recipient identity and save it with the itip item. + itipItem.identity = getMsgRecipient(); + // We are only called upon receipt of an invite, so ensure that isSend // is false. itipItem.isSend = false; @@ -232,9 +232,23 @@ function getMsgRecipient() if (identities.Count() == 0) { // If we were not able to retrieve identities above, then we have no // choice but to revert to the default identity - var emailSvc = Components.classes["@mozilla.org/calendar/itip-transport;1?type=email"] - .getService(Components.interfaces.calIItipTransport); - emailMap[emailSvc.defaultIdentity.toLowerCase()] = true; + var acctMgr = Components.classes["@mozilla.org/messenger/account-manager;1"] + .getService(Components.interfaces.nsIMsgAccountManager); + var identity = acctMgr.defaultAccount.defaultIdentity; + if (!identity) { + // If there isn't a default identity (i.e Local Folders is your + // default identity), then go ahead and use the first available + // identity. + var allIdentities = acctMgr.allIdentities; + if (allIdentities.Count() > 0) { + identity = allIdentities.GetElementAt(0) + .QueryInterface(Components.interfaces.nsIMsgIdentity); + } else { + // If there are no identities at all, we cannot get a recipient. + return null; + } + } + emailMap[identity.toLowerCase()] = true; } else { // Build a map of usable email addresses for (var i = 0; i < identities.Count(); i++) { @@ -304,7 +318,7 @@ function getTargetCalendar() */ function setAttendeeResponse(type, eventStatus) { - var myAddress = getMsgRecipient(); + var myAddress = gItipItem.identity if (!myAddress) { // Bug 420516 -- we don't support delegation yet TODO: Localize this? throw new Error("setAttendeeResponse: " + @@ -312,26 +326,31 @@ function setAttendeeResponse(type, eventStatus) "is not supported yet. See bug 420516 for details."); } if (type && gItipItem) { - // We set the attendee status appropriately + // Some methods need a target calendar. Prompt for it first. switch (type) { case "ACCEPTED": case "TENTATIVE": - gItipItem.setAttendeeStatus(myAddress, type); - // fall through case "REPLY": case "PUBLISH": - var targetCalendar = getTargetCalendar(); - gItipItem.targetCalendar = targetCalendar; - doResponse(eventStatus); - break; + gItipItem.targetCalendar = getTargetCalendar(); + if (!gItipItem.targetCalendar) { + // The dialog was canceled, we are done. + return; + } + } + + // Now set the attendee status and perform the iTIP action. If the + // method is not mentioned here, no further action will be taken. + switch (type) { + case "ACCEPTED": + case "TENTATIVE": case "DECLINED": gItipItem.setAttendeeStatus(myAddress, type); + // Fall through + case "REPLY": + case "PUBLISH": doResponse(eventStatus); break; - default: - // no-op. The attendee wishes to disregard the mail, so no - // further action is required. - break; } } } @@ -367,7 +386,7 @@ function doResponse(aLocalStatus) } var itipProc = Components.classes["@mozilla.org/calendar/itip-processor;1"] - .createInstance(Components.interfaces.calIItipProcessor); + .getService(Components.interfaces.calIItipProcessor); itipProc.processItipItem(gItipItem, operationListener); }