bug 334685 adding iMIP/iTIP support r=dbo
git-svn-id: svn://10.0.0.236/trunk@221473 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
55
mozilla/calendar/base/public/calIItipProcessor.idl
Normal file
55
mozilla/calendar/base/public/calIItipProcessor.idl
Normal file
@@ -0,0 +1,55 @@
|
||||
/* -*- Mode: idl; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 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 Simdesk Technologies code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Simdesk Technologies Inc.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2007
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Clint Talbert <ctalbert.moz@gmail.com>
|
||||
* Eva Or <evaor1012@yahoo.ca>
|
||||
* Matthew Willis <lilmatt@mozilla.com>
|
||||
*
|
||||
* 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 MPL, 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 MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface calIItipItem;
|
||||
interface calIOperationListener;
|
||||
|
||||
[scriptable, uuid(9787876b-0780-4464-8282-b7f86fb221e8)]
|
||||
interface calIItipProcessor : nsISupports
|
||||
{
|
||||
/**
|
||||
* Processes the given calItipItem based on the settings inside it.
|
||||
* @param in calIItipItem itipItem A calItipItem to process.
|
||||
* @param in calIIOperationListener An operation Listener to report status
|
||||
* @return PRBool Whether processing succeeded or not.
|
||||
*/
|
||||
boolean processItipItem(in calIItipItem itipItem, in calIOperationListener aListener);
|
||||
};
|
||||
103
mozilla/calendar/base/public/calIItipTransport.idl
Normal file
103
mozilla/calendar/base/public/calIItipTransport.idl
Normal file
@@ -0,0 +1,103 @@
|
||||
/* -*- Mode: idl; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 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 Simdesk Technologies code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Simdesk Technologies Inc.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2007
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Clint Talbert <ctalbert.moz@gmail.com>
|
||||
*
|
||||
* 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 MPL, 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 MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface calIItipItem;
|
||||
interface calIAttendee;
|
||||
interface calIDateTime;
|
||||
|
||||
/**
|
||||
* calIItipTransport is a generic transport interface that is implemented
|
||||
* by transports (eg: email, XMPP, etc.) wishing to send calIItipItems
|
||||
*/
|
||||
[scriptable, uuid(d9e439da-1014-42f9-be40-aa7e878b8a7d)]
|
||||
interface calIItipTransport : nsISupports
|
||||
{
|
||||
/**
|
||||
* Default identity for me within this transport. For example, this is
|
||||
* your default email address in Thunderbird.
|
||||
*/
|
||||
readonly attribute AUTF8String defaultIdentity;
|
||||
|
||||
/**
|
||||
* Sending identity. This can be set to change the "sender" identity from
|
||||
* defaultIdentity above.
|
||||
*/
|
||||
attribute AUTF8String senderAddress;
|
||||
|
||||
/**
|
||||
* Type of the transport: email, xmpp, etc.
|
||||
*/
|
||||
readonly attribute AUTF8String type;
|
||||
|
||||
/**
|
||||
* Pass the transport an itipItem and have it figure out what to do with
|
||||
* it based on the itipItem's methods.
|
||||
*/
|
||||
void simpleSendResponse(in calIItipItem item);
|
||||
|
||||
/**
|
||||
* Sends a calIItipItem to the recipients using the specified title and
|
||||
* alternative representation. If a calIItipItem is attached, then an ICS
|
||||
* representation of those objects are generated and attached to the email.
|
||||
* If the calIItipItem is null, then the item(s) is sent without any
|
||||
* text/calendar mime part.
|
||||
* @param count size of recipient array
|
||||
* @param recipientArray array of recipients
|
||||
* @param title title of the exchange
|
||||
* @param altrep an alternative representation (usually text)
|
||||
in case the recipient cannot handle the
|
||||
text/calendar mime-type
|
||||
* @param calIItipItem set of calIItems encapsulated as calIItipItems
|
||||
*/
|
||||
void sendItems(in PRUint32 count,
|
||||
[array, size_is(count)] in calIAttendee recipientArray,
|
||||
in AUTF8String title,
|
||||
in AUTF8String altrep,
|
||||
in calIItipItem item);
|
||||
|
||||
/**
|
||||
* Checks the configured transport for any incoming messages with
|
||||
* text/calendar mime parts (invitations) that are more recent than the
|
||||
* specified date. It returns a calIItipItem object containing the found
|
||||
* items
|
||||
* @param searchStart Defines on what date to start the search.
|
||||
* @return calIItipItem The items it finds in a calIItipItem object.
|
||||
*/
|
||||
calIItipItem checkForInvitations(in calIDateTime searchStart);
|
||||
};
|
||||
384
mozilla/calendar/base/src/calItipProcessor.js
Normal file
384
mozilla/calendar/base/src/calItipProcessor.js
Normal file
@@ -0,0 +1,384 @@
|
||||
/* -*- Mode: javascript; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 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 Simdesk Technologies code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Simdesk Technologies Inc.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2007
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Clint Talbert <ctalbert.moz@gmail.com>
|
||||
* Eva Or <evaor1012@yahoo.ca>
|
||||
* Matthew Willis <lilmatt@mozilla.com>
|
||||
*
|
||||
* 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 MPL, 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 MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
|
||||
// Operations on the calendar
|
||||
const CAL_ITIP_PROC_ADD_OP = 1;
|
||||
const CAL_ITIP_PROC_UPDATE_OP = 2;
|
||||
const CAL_ITIP_PROC_DELETE_OP = 3;
|
||||
|
||||
/**
|
||||
* Constructor of calItipItem object
|
||||
*/
|
||||
function calItipProcessor() {
|
||||
this.wrappedJSObject = this;
|
||||
}
|
||||
|
||||
calItipProcessor.prototype = {
|
||||
getInterfaces: function cipGI(count) {
|
||||
var ifaces = [
|
||||
Ci.nsIClassInfo,
|
||||
Ci.nsISupports,
|
||||
Ci.calIItipProcessor
|
||||
];
|
||||
count.value = ifaces.length;
|
||||
return ifaces;
|
||||
},
|
||||
|
||||
getHelperForLanguage: function cipGHFL(aLanguage) {
|
||||
return null;
|
||||
},
|
||||
|
||||
contractID: "@mozilla.org/calendar/itip-processor;1",
|
||||
classDescription: "Calendar iTIP processor",
|
||||
classID: Components.ID("{9787876b-0780-4464-8282-b7f86fb221e8}"),
|
||||
implementationLanguage: Ci.nsIProgrammingLanguage.JAVASCRIPT,
|
||||
flags: 0,
|
||||
|
||||
QueryInterface: function cipQI(aIid) {
|
||||
if (!aIid.equals(Ci.nsIClassInfo) && !aIid.equals(Ci.nsISupports) &&
|
||||
!aIid.equals(Ci.calIItipProcessor))
|
||||
{
|
||||
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
mIsUserInvolved: false,
|
||||
get isUserInvolved() {
|
||||
return this.mIsUserInvolved;
|
||||
},
|
||||
set isUserInvolved(aValue) {
|
||||
return (this.mIsUserInvolved = aValue);
|
||||
},
|
||||
|
||||
mRecvMethod: null,
|
||||
mRespMethod: null,
|
||||
mAutoResponse: null,
|
||||
mTargetCalendar: null,
|
||||
mCalItem: null,
|
||||
mCalItemType: null,
|
||||
|
||||
/**
|
||||
* Processes the given calItipItem based on the settings inside it.
|
||||
* @param calIItipItem A calItipItem to process.
|
||||
* @param calIOperationListener A calIOperationListener to return status
|
||||
* @return boolean Whether processing succeeded or not.
|
||||
*/
|
||||
processItipItem: function cipPII(aItipItem, aListener) {
|
||||
// Sanity check the input
|
||||
if (!aItipItem) {
|
||||
throw new Components.Exception("processItipItem: " +
|
||||
"Invalid or non-existant " +
|
||||
"itipItem passed in.",
|
||||
Components.results.NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
|
||||
// Clone the passed in itipItem like a sheep.
|
||||
var respItipItem = aItipItem.clone();
|
||||
|
||||
this.mRecvMethod = respItipItem.receivedMethod;
|
||||
respItipItem.responseMethod = this._suggestResponseMethod(respItipItem);
|
||||
this.mRespMethod = respItipItem.responseMethod;
|
||||
|
||||
this.mAutoResponse = respItipItem.autoResponse;
|
||||
this.mTargetCalendar = respItipItem.targetCalendar;
|
||||
|
||||
// XXX Support for transports other than email go here.
|
||||
// For now we just assume it's email.
|
||||
var transport = Cc["@mozilla.org/calendar/itip-transport;1?type=email"].
|
||||
createInstance(Ci.calIItipTransport);
|
||||
|
||||
// Sanity checks using the first item
|
||||
var itemList = respItipItem.getItemList({ });
|
||||
this.mCalItem = itemList[0];
|
||||
if (!this.mCalItem) {
|
||||
throw new Error ("processItipItem: " +
|
||||
"getFirstItem() found no items!");
|
||||
}
|
||||
|
||||
this.mCalItemType = this._getCalItemType(this.mCalItem);
|
||||
if (!this.mCalItemType) {
|
||||
throw new Error ("processItipItem: " +
|
||||
"_getCalItemType() found no item type!");
|
||||
}
|
||||
|
||||
// Sanity check that mRespMethod is a valid response per the spec.
|
||||
if (!this._isValidResponseMethod()) {
|
||||
throw new Error ("processItipItem: " +
|
||||
"_isValidResponseMethod() found an invalid " +
|
||||
"response method: " + mRespMethod);
|
||||
}
|
||||
|
||||
var i = 0;
|
||||
while (this.mCalItem) {
|
||||
switch (this.mRecvMethod) {
|
||||
case "REQUEST":
|
||||
// Only add to calendar if we accepted invite
|
||||
var replyStat = this._getReplyStatus(this.mCalItem,
|
||||
transport.defaultIdentity);
|
||||
if (replyStat != "DECLINED") {
|
||||
if (!this._processCalendarAction(this.mCalItem,
|
||||
CAL_ITIP_PROC_ADD_OP,
|
||||
aListener))
|
||||
{
|
||||
throw new Error ("processItipItem: " +
|
||||
"_processCalendarAction failed!");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "PUBLISH":
|
||||
case "REPLY":
|
||||
case "REFRESH":
|
||||
case "ADD":
|
||||
case "CANCEL":
|
||||
case "COUNTER":
|
||||
case "DECLINECOUNTER":
|
||||
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
||||
|
||||
default:
|
||||
throw new Error("processItipItem: " +
|
||||
"Received unknown method: " +
|
||||
this.mRecvMethod);
|
||||
}
|
||||
++i;
|
||||
this.mCalItem = itemList[i];
|
||||
}
|
||||
|
||||
// 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.
|
||||
if (this.mRespMethod == "REPLY") {
|
||||
// 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;
|
||||
}
|
||||
|
||||
for (var j=0; j < itemList.length; ++j) {
|
||||
var respCalItem = itemList[j];
|
||||
var attendees = respCalItem.getAttendees({});
|
||||
|
||||
for each (var attendee in attendees) {
|
||||
// Leave the ORGANIZER alone.
|
||||
if (!attendee.isOrganizer) {
|
||||
// example: mailto:joe@domain.com
|
||||
var meString = idPrefix + me;
|
||||
if (attendee.id.toLowerCase() != meString.toLowerCase()) {
|
||||
respCalItem.removeAttendee(attendee);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Send the appropriate response
|
||||
transport.simpleSendResponse(respItipItem);
|
||||
|
||||
// Yay it worked!
|
||||
// XXX TODO: Actually tie this to success/failure of the transport
|
||||
return true;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @return integer The next recommended iTIP state.
|
||||
*/
|
||||
_suggestResponseMethod: function cipSRM() {
|
||||
switch (this.mRecvMethod) {
|
||||
case "REQUEST":
|
||||
return "REPLY";
|
||||
|
||||
case "REFRESH":
|
||||
case "COUNTER":
|
||||
return "REQUEST";
|
||||
|
||||
case "PUBLISH":
|
||||
case "REPLY":
|
||||
case "ADD":
|
||||
case "CANCEL":
|
||||
case "DECLINECOUNTER":
|
||||
return this.mRecvMethod;
|
||||
|
||||
default:
|
||||
throw new Error("_suggestResponseMethod: " +
|
||||
"Received unknown method: " +
|
||||
this.mRecvMethod);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Given mRecvMethod and mRespMethod, this checks that mRespMethod is
|
||||
* valid according to the spec.
|
||||
*
|
||||
* @return boolean Whether or not mRespMethod is valid.
|
||||
*/
|
||||
_isValidResponseMethod: function cipIAR() {
|
||||
switch (this.mRecvMethod) {
|
||||
// We set response to ADD automatically, but if the GUI did not
|
||||
// find the event the user may set it to REFRESH as per the spec.
|
||||
// These are the only two valid responses.
|
||||
case "ADD":
|
||||
if (!(this.mRespMethod == "ADD" ||
|
||||
(this.mRespMethod == "REFRESH" &&
|
||||
// REFRESH is not a valid response to an ADD for VJOURNAL
|
||||
(this.mCalItemType == Ci.calIEvent ||
|
||||
this.mCalItemType == Ci.calITodo))))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
// Valid responses to COUNTER are REQUEST or DECLINECOUNTER.
|
||||
case "COUNTER":
|
||||
if (!(this.mRespMethod == "REQUEST" ||
|
||||
this.mRespMethod == "DECLINECOUNTER"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
// Valid responses to REQUEST are:
|
||||
// REPLY (accept or error)
|
||||
// REQUEST (delegation, inviting someone else)
|
||||
// COUNTER (propose a change)
|
||||
case "REQUEST":
|
||||
if (!(this.mRespMethod == "REPLY" ||
|
||||
this.mRespMethod == "REQUEST" ||
|
||||
this.mRespMethod == "COUNTER"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
// REFRESH should respond with a request
|
||||
case "REFRESH":
|
||||
if (this.mRespMethod == "REQUEST") {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
// The rest are easiest represented as:
|
||||
// (mRecvMethod != mRespMethod) == return false
|
||||
case "PUBLISH":
|
||||
case "CANCEL":
|
||||
case "REPLY":
|
||||
case "PUBLISH":
|
||||
case "DECLINECOUNTER":
|
||||
if (this.mRespMethod != this.mRecvMethod) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error("_isValidResponseMethod: " +
|
||||
"Received unknown method: " +
|
||||
this.mRecvMethod);
|
||||
}
|
||||
|
||||
// If we got to here, then the combination is valid.
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper to return whether an item is an event, todo, etc.
|
||||
*/
|
||||
_getCalItemType: function cipGCIT() {
|
||||
if (this.mCalItem instanceof Ci.calIEvent) {
|
||||
return Ci.calIEvent;
|
||||
} else if (aCalItem instanceof Ci.calITodo) {
|
||||
return Ci.calITodo;
|
||||
}
|
||||
|
||||
throw new Error ("_getCalItemType: " +
|
||||
"mCalItem item type is unknown");
|
||||
},
|
||||
|
||||
/**
|
||||
* This performs the actual add/update/delete of an event on the user's
|
||||
* calendar.
|
||||
*/
|
||||
_processCalendarAction: function cipPCA(aCalItem, aOperation, aListener) {
|
||||
switch (aOperation) {
|
||||
case CAL_ITIP_PROC_ADD_OP:
|
||||
// We assume all adds take place on the target calendar
|
||||
this.mTargetCalendar.addItem(aCalItem, aListener);
|
||||
|
||||
// XXX Change this to reflect the success or failure of adding
|
||||
// the item to the calendar.
|
||||
return true;
|
||||
|
||||
case CAL_ITIP_PROC_UPDATE_OP:
|
||||
case CAL_ITIP_PROC_DELETE_OP:
|
||||
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
||||
|
||||
default:
|
||||
throw new Error("_processCalendarAction: " +
|
||||
"Undefined Operator: " + aOperator);
|
||||
}
|
||||
|
||||
// If you got to here, something went horribly, horribly wrong.
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper function to get the PARTSTAT value from the given calendar
|
||||
* item for the specified attendee
|
||||
*/
|
||||
_getReplyStatus: function cipGRS(aCalItem, aAttendeeId) {
|
||||
var idPrefix = "mailto:";
|
||||
var replyStatus;
|
||||
|
||||
// example: mailto:joe@domain.com
|
||||
var idString = idPrefix + aAttendeeId;
|
||||
var attendee = aCalItem.getAttendeeById(idString);
|
||||
if (attendee) {
|
||||
replyStatus = attendee.participationStatus;
|
||||
}
|
||||
return replyStatus;
|
||||
}
|
||||
}
|
||||
49
mozilla/calendar/itip/Makefile.in
Normal file
49
mozilla/calendar/itip/Makefile.in
Normal file
@@ -0,0 +1,49 @@
|
||||
#
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 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 Simdesk Technologies Code.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Simdesk Technologies.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2007
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Clint Talbert <ctalbert.moz@gmail.com>
|
||||
#
|
||||
# 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 MPL, 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 MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
DEPTH = ../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
MODULE = calItipEmailTransport
|
||||
|
||||
EXTRA_COMPONENTS = calItipEmailTransport.js
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
428
mozilla/calendar/itip/calItipEmailTransport.js
Normal file
428
mozilla/calendar/itip/calItipEmailTransport.js
Normal file
@@ -0,0 +1,428 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 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 Simdesk Technologies code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Simdesk Technologies.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2007
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Clint Talbert <ctalbert.moz@gmail.com>
|
||||
* Matthew Willis <lilmatt@mozilla.com>
|
||||
*
|
||||
* 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 MPL, 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 MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/**
|
||||
* Constructor of calItipEmailTransport object
|
||||
*/
|
||||
function calItipEmailTransport() {
|
||||
this.wrappedJSObject = this;
|
||||
this._initEmailTransport();
|
||||
}
|
||||
|
||||
calItipEmailTransport.prototype = {
|
||||
|
||||
QueryInterface: function cietQI(aIid) {
|
||||
if (!aIid.equals(Components.interfaces.nsISupports) &&
|
||||
!aIid.equals(Components.interfaces.calIItipTransport))
|
||||
{
|
||||
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
mHasXpcomMail: false,
|
||||
mAccountMgrSvc: null,
|
||||
mDefaultAccount: null,
|
||||
mDefaultSmtpServer: null,
|
||||
|
||||
mDefaultIdentity: null,
|
||||
get defaultIdentity() {
|
||||
return this.mDefaultIdentity.email;
|
||||
},
|
||||
|
||||
mSenderAddress: null,
|
||||
get senderAddress() {
|
||||
return this.mSenderAddress;
|
||||
},
|
||||
set senderAddress(aValue) {
|
||||
return (this.mSenderAddress = aValue);
|
||||
},
|
||||
|
||||
get type() {
|
||||
return "email";
|
||||
},
|
||||
|
||||
/**
|
||||
* Pass the transport an itipItem and have it figure out what to do with
|
||||
* it based on the itipItem's methods.
|
||||
*/
|
||||
simpleSendResponse: function cietSSR(aItem) {
|
||||
var sbs = Components.classes["@mozilla.org/intl/stringbundle;1"].
|
||||
getService(Components.interfaces.nsIStringBundleService);
|
||||
var sb = sbs.createBundle("chrome://lightning/locale/lightning.properties");
|
||||
|
||||
var itemList = aItem.getItemList({ });
|
||||
|
||||
// Get my participation status
|
||||
var myPartStat;
|
||||
var foundMyself = false;
|
||||
var attendee = itemList[0].getAttendeeById("mailto:" +
|
||||
this.mDefaultIdentity.email);
|
||||
if (attendee) {
|
||||
myPartStat = attendee.participationStatus;
|
||||
foundMyself = true;
|
||||
}
|
||||
|
||||
if (!foundMyself) {
|
||||
// I didn't find myself in the attendee list.
|
||||
//
|
||||
// This can happen when invitations are sent to email aliases
|
||||
// instead of mDefaultIdentity.email (ex: lilmatt vs. mwillis),
|
||||
// or if an invitation is sent to a listserv.
|
||||
//
|
||||
// We'll need to make more decisions regarding how to handle this
|
||||
// in the future. (ex: Prompt? Find myself in list?) For now, we
|
||||
// just don't send a response.
|
||||
return;
|
||||
}
|
||||
|
||||
var name = this.mDefaultIdentity.email;
|
||||
if (this.mDefaultIdentity.fullName) {
|
||||
name = this.mDefaultIdentity.fullName + " <" + name + ">";
|
||||
}
|
||||
|
||||
var summary;
|
||||
if (itemList[0].getProperty("SUMMARY")) {
|
||||
summary = itemList[0].getProperty("SUMMARY");
|
||||
} else {
|
||||
summary = "";
|
||||
}
|
||||
var subj = sb.formatStringFromName("itipReplySubject", [summary], 1);
|
||||
|
||||
// Generate proper body from my participation status
|
||||
var body;
|
||||
dump("\n\nthis is partstat: " + myPartStat + "\n");
|
||||
if (myPartStat == "DECLINED") {
|
||||
body = sb.formatStringFromName("itipReplyBodyDecline",
|
||||
[name], 1);
|
||||
} else {
|
||||
body = sb.formatStringFromName("itipReplyBodyAccept",
|
||||
[name], 1);
|
||||
}
|
||||
|
||||
var recipients = [itemList[0].organizer];
|
||||
|
||||
this.sendItems(recipients.length, recipients, subj, body, aItem);
|
||||
},
|
||||
|
||||
sendItems: function cietSI(aCount, aRecipients, aSubject, aBody, aItem) {
|
||||
LOG("sendItems: Sending Email...");
|
||||
if (this.mHasXpcomMail) {
|
||||
this._sendXpcomMail(aCount, aRecipients, aSubject, aBody, aItem);
|
||||
} else {
|
||||
// Sunbird case: Call user's default mailer on system.
|
||||
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
},
|
||||
|
||||
checkForInvitations: function cietCFI(searchStart) {
|
||||
// We only need to do trigger a check for incoming invitations if we
|
||||
// are not Thunderbird.
|
||||
if (!this.mHasXpcomMail) {
|
||||
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
},
|
||||
|
||||
_initEmailTransport: function cietIES() {
|
||||
try {
|
||||
this.mAccountMgrSvc =
|
||||
Components.classes["@mozilla.org/messenger/account-manager;1"].
|
||||
getService(Components.interfaces.nsIMsgAccountManager);
|
||||
|
||||
this.mDefaultAccount = this.mAccountMgrSvc.defaultAccount;
|
||||
this.mDefaultIdentity = this.mDefaultAccount.defaultIdentity;
|
||||
|
||||
var smtpSvc = Components.classes["@mozilla.org/messengercompose/smtp;1"].
|
||||
getService(Components.interfaces.nsISmtpService);
|
||||
this.mSmtpServer = smtpSvc.defaultServer;
|
||||
|
||||
} catch (ex) {
|
||||
// Then we must resort to operating system specific means
|
||||
this.mHasXpcomMail = false;
|
||||
}
|
||||
|
||||
// But if none of that threw then we can send an email through XPCOM.
|
||||
this.mHasXpcomMail = true;
|
||||
LOG("initEmailService: hasXpcomMail: " + this.mHasXpcomMail);
|
||||
},
|
||||
|
||||
_sendXpcomMail: function cietSXM(aCount, aToList, aSubject, aBody, aItem) {
|
||||
// Save calItipItem to a temporary file.
|
||||
LOG("sendXpcomMail: Creating temp file for attachment.");
|
||||
var msgAttachment = Components.classes["@mozilla.org/messengercompose/attachment;1"].
|
||||
createInstance(Components.interfaces.nsIMsgAttachment);
|
||||
msgAttachment.url = this._createTempIcsFile(aItem);
|
||||
if (!msgAttachment.url) {
|
||||
LOG("sendXpcomMail: No writeable path in profile!");
|
||||
// XXX Is there an "out of disk space" error?
|
||||
throw Components.results.NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
msgAttachment.name = "calendar.ics";
|
||||
msgAttachment.contentType = "text/calendar";
|
||||
msgAttachment.contentTypeParam = "method=" + aItem.responseMethod;
|
||||
// Destroy the attachment after sending
|
||||
msgAttachment.temporary = true;
|
||||
|
||||
// compose fields for message
|
||||
var composeFields = Components.classes["@mozilla.org/messengercompose/composefields;1"].
|
||||
createInstance(Components.interfaces.nsIMsgCompFields);
|
||||
composeFields.useMultipartAlternative = true;
|
||||
composeFields.characterSet = "UTF-8";
|
||||
// TODO: xxx make this use the currently selected mail account, if
|
||||
// possible, and default to the default account if the selection
|
||||
// is unclear.
|
||||
var toList = "";
|
||||
for each (var recipient in aToList) {
|
||||
// Strip leading "mailto:" if it exists.
|
||||
var rId = recipient.id.replace(/^mailto:/i, "");
|
||||
|
||||
// Prevent trailing commas.
|
||||
if (toList.length > 0) {
|
||||
toList += ",";
|
||||
}
|
||||
|
||||
// Add this recipient id to the list.
|
||||
toList += rId;
|
||||
}
|
||||
composeFields.to = toList;
|
||||
composeFields.from = this.mDefaultIdentity.email;
|
||||
composeFields.replyTo = this.mDefaultIdentity.replyTo;
|
||||
composeFields.subject = aSubject;
|
||||
composeFields.body = aBody;
|
||||
composeFields.addAttachment(msgAttachment);
|
||||
|
||||
// Message paramaters
|
||||
var composeParams = Components.classes["@mozilla.org/messengercompose/composeparams;1"].
|
||||
createInstance(Components.interfaces.nsIMsgComposeParams);
|
||||
composeParams.composeFields = composeFields;
|
||||
// TODO: xxx: Make this a pref or read the default pref
|
||||
composeParams.format = Components.interfaces.nsIMsgCompFormat.PlainText;
|
||||
composeParams.type = Components.interfaces.nsIMsgCompType.New;
|
||||
|
||||
var composeService = Components.classes["@mozilla.org/messengercompose;1"].
|
||||
getService(Components.interfaces.nsIMsgComposeService);
|
||||
switch (aItem.autoResponse) {
|
||||
case (Components.interfaces.calIItipItem.USER):
|
||||
LOG("sendXpcomMail: Found USER autoResponse type.");
|
||||
|
||||
// Open a compose window
|
||||
var url = "chrome://messenger/content/messengercompose/messengercompose.xul"
|
||||
composeService.OpenComposeWindowWithParams(url, composeParams);
|
||||
break;
|
||||
case (Components.interfaces.calIItipItem.AUTO):
|
||||
LOG("sendXpcomMail: Found AUTO autoResponse type.");
|
||||
|
||||
var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"].
|
||||
getService(Components.interfaces.nsIWindowWatcher);
|
||||
var win = ww.activeWindow;
|
||||
var msgCompose = Components.classes["@mozilla.org/messengercompose/compose;1"].
|
||||
createInstance(Components.interfaces.nsIMsgCompose);
|
||||
msgCompose.Initialize(win, composeParams);
|
||||
// Fourth param is message window, fifth param is progress.
|
||||
// TODO: xxx decide on whether or not we want progress window
|
||||
msgCompose.SendMsg(Components.interfaces.nsIMsgCompDeliverMode.Now,
|
||||
this.mDefaultIdentity,
|
||||
this.mDefaultAccount.key,
|
||||
null,
|
||||
null);
|
||||
break;
|
||||
case (Components.interfaces.calIItipItem.NONE):
|
||||
LOG("sendXpcomMail: Found NONE autoResponse type.");
|
||||
|
||||
// No response
|
||||
break;
|
||||
default:
|
||||
// Unknown autoResponse type
|
||||
throw new Error("sendXpcomMail: " +
|
||||
"Unknown autoResponse type: " +
|
||||
aItem.autoResponse);
|
||||
}
|
||||
},
|
||||
|
||||
_createTempIcsFile: function cietCTIF(aItem) {
|
||||
var path;
|
||||
var itemList = aItem.getItemList({ });
|
||||
// This is a workaround until bug 353369 is fixed.
|
||||
// Without it, we cannot roundtrip the METHOD property, so we must
|
||||
// re-add it to the ICS data as we serialize it.
|
||||
//
|
||||
// Look at the implicit assumption in the code at:
|
||||
// http://lxr.mozilla.org/seamonkey/source/calendar/base/src/calEvent.js#162
|
||||
// and it's easy to see why.
|
||||
itemList[0].setProperty("METHOD", aItem.responseMethod);
|
||||
var calText = "";
|
||||
for (var i = 0; i < itemList.length; i++) {
|
||||
calText += itemList[i].icalString;
|
||||
}
|
||||
|
||||
LOG("ICS to be emailed: " + calText);
|
||||
|
||||
try {
|
||||
var dirUtils = Components.classes["@mozilla.org/file/directory_service;1"].
|
||||
createInstance(Components.interfaces.nsIProperties);
|
||||
var tempFile = dirUtils.get("TmpD", Components.interfaces.nsIFile);
|
||||
tempFile.append("itipTemp");
|
||||
tempFile.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0600);
|
||||
|
||||
var outputStream = Components.classes["@mozilla.org/network/file-output-stream;1"].
|
||||
createInstance(Components.interfaces.nsIFileOutputStream);
|
||||
var utf8CalText = this._convertFromUnicode("UTF-8", calText);
|
||||
|
||||
// Let's write the file - constants from file-utils.js
|
||||
const MODE_WRONLY = 0x02;
|
||||
const MODE_CREATE = 0x08;
|
||||
const MODE_TRUNCATE = 0x20;
|
||||
outputStream.init(tempFile,
|
||||
MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE,
|
||||
0600, 0);
|
||||
outputStream.write(utf8CalText, utf8CalText.length);
|
||||
outputStream.close();
|
||||
|
||||
var ioService = Components.classes["@mozilla.org/network/io-service;1"].
|
||||
getService(Components.interfaces.nsIIOService);
|
||||
path = ioService.newFileURI(tempFile).spec;
|
||||
} catch (ex) {
|
||||
LOG("createTempItipFile failed! " + ex);
|
||||
path = null;
|
||||
}
|
||||
LOG("createTempItipFile path: " + path);
|
||||
return path;
|
||||
},
|
||||
|
||||
_convertFromUnicode: function cietCFU(aCharset, aSrc) {
|
||||
var unicodeConverter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].
|
||||
createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
|
||||
unicodeConverter.charset = aCharset;
|
||||
return unicodeConverter.ConvertFromUnicode(aSrc);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/****
|
||||
**** module registration
|
||||
****/
|
||||
|
||||
var calItipEmailTransportModule = {
|
||||
|
||||
mCID: Components.ID("{d4d7b59e-c9e0-4a7a-b5e8-5958f85515f0}"),
|
||||
mContractID: "@mozilla.org/calendar/itip-transport;1?type=email",
|
||||
|
||||
mUtilsLoaded: false,
|
||||
loadUtils: function itipEmailLoadUtils() {
|
||||
if (this.mUtilsLoaded)
|
||||
return;
|
||||
|
||||
const jssslContractID = "@mozilla.org/moz/jssubscript-loader;1";
|
||||
const jssslIID = Components.interfaces.mozIJSSubScriptLoader;
|
||||
|
||||
const iosvcContractID = "@mozilla.org/network/io-service;1";
|
||||
const iosvcIID = Components.interfaces.nsIIOService;
|
||||
|
||||
var loader = Components.classes[jssslContractID].getService(jssslIID);
|
||||
var iosvc = Components.classes[iosvcContractID].getService(iosvcIID);
|
||||
|
||||
// Note that unintuitively, __LOCATION__.parent == .
|
||||
// We expect to find utils in ./../js
|
||||
var appdir = __LOCATION__.parent.parent;
|
||||
appdir.append("js");
|
||||
var scriptName = "calUtils.js";
|
||||
|
||||
var f = appdir.clone();
|
||||
f.append(scriptName);
|
||||
|
||||
try {
|
||||
var fileurl = iosvc.newFileURI(f);
|
||||
loader.loadSubScript(fileurl.spec, this.__parent__.__parent__);
|
||||
} catch (e) {
|
||||
dump("Error while loading " + fileurl.spec + "\n");
|
||||
throw e;
|
||||
}
|
||||
|
||||
this.mUtilsLoaded = true;
|
||||
},
|
||||
|
||||
registerSelf: function (compMgr, fileSpec, location, type) {
|
||||
compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
|
||||
compMgr.registerFactoryLocation(this.mCID,
|
||||
"Calendar iTIP Email Transport",
|
||||
this.mContractID,
|
||||
fileSpec,
|
||||
location,
|
||||
type);
|
||||
},
|
||||
|
||||
getClassObject: function (compMgr, cid, iid) {
|
||||
if (!cid.equals(this.mCID))
|
||||
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||
|
||||
if (!iid.equals(Components.interfaces.nsIFactory))
|
||||
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
||||
|
||||
this.loadUtils();
|
||||
|
||||
return this.mFactory;
|
||||
},
|
||||
|
||||
mFactory: {
|
||||
createInstance: function (outer, iid) {
|
||||
if (outer != null)
|
||||
throw Components.results.NS_ERROR_NO_AGGREGATION;
|
||||
return (new calItipEmailTransport()).QueryInterface(iid);
|
||||
}
|
||||
},
|
||||
|
||||
canUnload: function(compMgr) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
function NSGetModule(compMgr, fileSpec) {
|
||||
return calItipEmailTransportModule;
|
||||
}
|
||||
|
||||
function LOG(aString) {
|
||||
if (!getPrefSafe("calendar.itip.email.log", false)) {
|
||||
return;
|
||||
}
|
||||
var consoleService = Components.classes["@mozilla.org/consoleservice;1"].
|
||||
getService(Components.interfaces.nsIConsoleService);
|
||||
consoleService.logStringMessage(aString);
|
||||
dump(aString + "\n");
|
||||
}
|
||||
Reference in New Issue
Block a user