зеркало из https://github.com/mozilla/pjs.git
391 строка
14 KiB
JavaScript
391 строка
14 KiB
JavaScript
/* -*- 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 Lightning code.
|
|
*
|
|
* The Initial Developer of the Original Code is Simdesk Technologies Inc.
|
|
* Portions created by the Initial Developer are Copyright (C) 2006
|
|
* 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 ***** */
|
|
|
|
/**
|
|
* This bar lives inside the message window.
|
|
* Its lifetime is the lifetime of the main thunderbird message window.
|
|
*/
|
|
|
|
var gItipItem;
|
|
|
|
const onItipItem = {
|
|
observe: function observe(subject, topic, state) {
|
|
if (topic == "onItipItemCreation") {
|
|
checkForItipItem(subject);
|
|
}
|
|
}
|
|
};
|
|
|
|
function checkForItipItem(subject)
|
|
{
|
|
var itipItem;
|
|
try {
|
|
if (!subject) {
|
|
var msgUri = GetLoadedMessage();
|
|
var sinkProps = msgWindow.msgHeaderSink.properties;
|
|
// 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
|
|
// itipItem property set on it. So we eat the errors and move on.
|
|
|
|
// XXX TODO: Only swallow the errors we need to. Throw all others.
|
|
return;
|
|
}
|
|
|
|
// We are only called upon receipt of an invite, so ensure that isSend
|
|
// is false.
|
|
itipItem.isSend = false;
|
|
|
|
// XXX Get these from preferences
|
|
itipItem.autoResponse = Components.interfaces.calIItipItem.USER;
|
|
|
|
var imipMethod = getMsgImipMethod();
|
|
if (imipMethod &&
|
|
imipMethod.length != 0 &&
|
|
imipMethod.toLowerCase() != "nomethod")
|
|
{
|
|
itipItem.receivedMethod = imipMethod;
|
|
} else {
|
|
// There is no METHOD in the content-type header (spec violation).
|
|
// Fall back to using the one from the itipItem's ICS.
|
|
imipMethod = itipItem.receivedMethod;
|
|
}
|
|
|
|
gItipItem = itipItem;
|
|
|
|
// XXX Bug 351742: no S/MIME or spoofing protection yet
|
|
// handleImipSecurity(imipMethod);
|
|
|
|
setupBar(imipMethod);
|
|
}
|
|
|
|
addEventListener("messagepane-loaded", imipOnLoad, true);
|
|
addEventListener("messagepane-unloaded", imipOnUnload, true);
|
|
|
|
/**
|
|
* Add self to gMessageListeners defined in msgHdrViewOverlay.js
|
|
*/
|
|
function imipOnLoad()
|
|
{
|
|
var listener = {};
|
|
listener.onStartHeaders = onImipStartHeaders;
|
|
listener.onEndHeaders = onImipEndHeaders;
|
|
gMessageListeners.push(listener);
|
|
|
|
// Set up our observers
|
|
var observerSvc = Components.classes["@mozilla.org/observer-service;1"]
|
|
.getService(Components.interfaces.nsIObserverService);
|
|
observerSvc.addObserver(onItipItem, "onItipItemCreation", false);
|
|
}
|
|
|
|
function imipOnUnload()
|
|
{
|
|
removeEventListener("messagepane-loaded", imipOnLoad, true);
|
|
removeEventListener("messagepane-unloaded", imipOnUnload, true);
|
|
|
|
var observerSvc = Components.classes["@mozilla.org/observer-service;1"]
|
|
.getService(Components.interfaces.nsIObserverService);
|
|
observerSvc.removeObserver(onItipItem, "onItipItemCreation");
|
|
|
|
gItipItem = null;
|
|
}
|
|
|
|
function onImipStartHeaders()
|
|
{
|
|
var imipBar = document.getElementById("imip-bar");
|
|
imipBar.setAttribute("collapsed", "true");
|
|
document.getElementById("imip-button1").setAttribute("hidden", "true");
|
|
document.getElementById("imip-button2").setAttribute("hidden", "true");
|
|
|
|
// A new message is starting.
|
|
// Clear our iMIP/iTIP stuff so it doesn't contain stale information.
|
|
imipMethod = "";
|
|
gItipItem = null;
|
|
}
|
|
|
|
/**
|
|
* Required by MessageListener. no-op
|
|
*/
|
|
function onImipEndHeaders()
|
|
{
|
|
// no-op
|
|
}
|
|
|
|
function setupBar(imipMethod)
|
|
{
|
|
// XXX - Bug 348666 - Currently we only do REQUEST requests
|
|
// In the future this function will set up the proper actions
|
|
// and attributes for the buttons as based on the iMIP Method
|
|
var imipBar = document.getElementById("imip-bar");
|
|
imipBar.setAttribute("collapsed", "false");
|
|
var description = document.getElementById("imip-description");
|
|
|
|
// Bug 348666: here is where we would check if this event was already
|
|
// added to calendar or not and display correct information
|
|
|
|
if (imipMethod.toUpperCase() == "REQUEST") {
|
|
if (description.firstChild.data) {
|
|
description.firstChild.data = ltnGetString("lightning",
|
|
"imipBarRequestText");
|
|
}
|
|
|
|
var button = document.getElementById("imip-button1");
|
|
button.removeAttribute("hidden");
|
|
button.setAttribute("label", ltnGetString("lightning",
|
|
"imipAcceptInvitation.label"));
|
|
button.setAttribute("oncommand",
|
|
"setAttendeeResponse('ACCEPTED', 'CONFIRMED');");
|
|
|
|
// Create a DECLINE button
|
|
button = document.getElementById("imip-button2");
|
|
button.removeAttribute("hidden");
|
|
button.setAttribute("label", ltnGetString("lightning",
|
|
"imipDeclineInvitation.label"));
|
|
button.setAttribute("oncommand",
|
|
"setAttendeeResponse('DECLINED', 'CONFIRMED');");
|
|
} else if (imipMethod.toUpperCase() == "REPLY") {
|
|
// Bug xxxx we currently cannot process REPLY messages so just let
|
|
// the user know what this is, and don't give them any options.
|
|
if (description.firstChild.data) {
|
|
description.firstChild.data = ltnGetString("lightning",
|
|
"imipBarReplyText");
|
|
}
|
|
} else {
|
|
// Bug xxxx TBD: Something went wrong or we found a message we don't
|
|
// support yet. We can show a "This method is not supported in this
|
|
// version" or simply hide the iMIP bar at this point
|
|
if (description.firstChild.data) {
|
|
description.firstChild.data = ltnGetString("lightning",
|
|
"imipBarUnsupportedText");
|
|
}
|
|
Components.utils.reportError("Unknown imipMethod: " + imipMethod);
|
|
}
|
|
}
|
|
|
|
function getMsgImipMethod()
|
|
{
|
|
var imipMethod = "";
|
|
var msgURI = GetLoadedMessage();
|
|
var msgHdr = messenger.messageServiceFromURI(msgURI)
|
|
.messageURIToMsgHdr(msgURI);
|
|
imipMethod = msgHdr.getStringProperty("imip_method");
|
|
return imipMethod;
|
|
}
|
|
|
|
function getMsgRecipient()
|
|
{
|
|
var imipRecipient = "";
|
|
var msgURI = GetLoadedMessage();
|
|
var msgHdr = messenger.messageServiceFromURI(msgURI)
|
|
.messageURIToMsgHdr(msgURI);
|
|
|
|
// msgHdr recipients can be a comma separated list of recipients.
|
|
// We then compare against the defaultIdentity to find ourselves.
|
|
// XXX This won't always work:
|
|
// Users with multiple accounts and invites going to the non-default
|
|
// account, Users with email aliases where the defaultIdentity.email
|
|
// doesn't match the recipient, etc.
|
|
if (msgHdr) {
|
|
var recipientList = msgHdr.recipients;
|
|
// Remove any spaces
|
|
recipientList = recipientList.split(" ").join("");
|
|
recipientList = recipientList.split(",");
|
|
|
|
var emailSvc = Components.classes["@mozilla.org/calendar/itip-transport;1?type=email"]
|
|
.getService(Components.interfaces.calIItipTransport);
|
|
var me = emailSvc.defaultIdentity;
|
|
|
|
var lt;
|
|
var gt;
|
|
for each (var recipient in recipientList) {
|
|
// Deal with <foo@bar.com> style addresses
|
|
lt = recipient.indexOf("<");
|
|
gt = recipient.indexOf(">");
|
|
|
|
// I chose 6 since <a@b.c> is the shortest technically valid email
|
|
// address I could come up with.
|
|
if ((lt >= 0) && (gt >= 6)) {
|
|
recipient = recipient.substring(lt+1, gt);
|
|
}
|
|
|
|
if (recipient.toLowerCase() == me.toLowerCase()) {
|
|
imipRecipient = recipient;
|
|
}
|
|
}
|
|
}
|
|
return imipRecipient;
|
|
}
|
|
|
|
/**
|
|
* Call the calendar picker
|
|
*/
|
|
function getTargetCalendar()
|
|
{
|
|
var calendarToReturn;
|
|
var calMgr = Components.classes["@mozilla.org/calendar/manager;1"]
|
|
.getService(Components.interfaces.calICalendarManager);
|
|
var count = new Object();
|
|
var calArray = calMgr.getCalendars(count);
|
|
|
|
if (count.value == 1) {
|
|
// There's only one calendar, so it's silly to ask what calendar
|
|
// the user wants to import into.
|
|
calendarToReturn = calArray[0];
|
|
} else {
|
|
// Ask what calendar to import into
|
|
var args = new Object();
|
|
var aCal;
|
|
args.onOk = function selectCalendar(aCal) { calendarToReturn = aCal; };
|
|
args.promptText = calGetString("calendar", "importPrompt");
|
|
openDialog("chrome://calendar/content/chooseCalendarDialog.xul",
|
|
"_blank", "chrome,titlebar,modal,resizable", args);
|
|
}
|
|
return calendarToReturn;
|
|
}
|
|
|
|
/**
|
|
* Type is type of response
|
|
* event_status is an optional directive to set the Event STATUS property
|
|
*/
|
|
function setAttendeeResponse(type, eventStatus)
|
|
{
|
|
var myAddress = getMsgRecipient();
|
|
if (type && gItipItem) {
|
|
// We set the attendee status appropriately
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* doResponse performs the iTIP action for the current ItipItem that we
|
|
* parsed from the email.
|
|
* @param aLocalStatus optional parameter to set the event STATUS property.
|
|
* aLocalStatus can be empty, "TENTATIVE", "CONFIRMED", or "CANCELLED"
|
|
*/
|
|
function doResponse(aLocalStatus)
|
|
{
|
|
// calIOperationListener so that we can properly return status to the
|
|
// imip-bar
|
|
var operationListener = {
|
|
onOperationComplete:
|
|
function ooc(aCalendar, aStatus, aOperationType, aId, aDetail) {
|
|
// Call finishItipAction to set the status of the operation
|
|
finishItipAction(aOperationType, aStatus, aDetail);
|
|
},
|
|
|
|
onGetResult:
|
|
function ogr(aCalendar, aStatus, aItemType, aDetail, aCount, aItems) {
|
|
// no-op
|
|
}
|
|
};
|
|
|
|
// The spec is unclear if we must add all the items or if the
|
|
// user should get to pick which item gets added.
|
|
var targetCalendar = getTargetCalendar();
|
|
gItipItem.targetCalendar = targetCalendar;
|
|
|
|
if (aLocalStatus != null) {
|
|
gItipItem.localStatus = aLocalStatus;
|
|
}
|
|
|
|
var itipProc = Components.classes["@mozilla.org/calendar/itip-processor;1"]
|
|
.createInstance(Components.interfaces.calIItipProcessor);
|
|
|
|
itipProc.processItipItem(gItipItem, operationListener);
|
|
}
|
|
|
|
/**
|
|
* Bug 348666 (complete iTIP support) - This gives the user an indication
|
|
* that the Action occurred.
|
|
*
|
|
* In the future, this will store the status of the invitation in the
|
|
* invitation manager. This will enable us to provide the ability to request
|
|
* updates from the organizer and to suggest changes to invitations.
|
|
*
|
|
* Currently, this is called from our calIOperationListener that is sent to
|
|
* the ItipProcessor. This conveys the status of the local iTIP processing
|
|
* on your calendar. It does not convey the success or failure of sending a
|
|
* response to the ItipItem.
|
|
*/
|
|
function finishItipAction(aOperationType, aStatus, aDetail)
|
|
{
|
|
// For now, we just state the status for the user something very simple
|
|
var desc = document.getElementById("imip-description");
|
|
if (desc.firstChild != null) {
|
|
if (Components.isSuccessCode(aStatus)) {
|
|
desc.firstChild.data = ltnGetString("lightning",
|
|
"imipAddedItemToCal");
|
|
document.getElementById("imip-button1").setAttribute("hidden",
|
|
true);
|
|
document.getElementById("imip-button2").setAttribute("hidden",
|
|
true);
|
|
} else {
|
|
// Bug 348666: When we handle more iTIP methods, we need to create
|
|
// more sophisticated error handling.
|
|
document.getElementById("imip-bar").setAttribute("collapsed", true);
|
|
var msg = "Invitation could not be processed. Status: " + aStatus;
|
|
if (aDetail) {
|
|
msg += "\nDetails: " + aDetail;
|
|
}
|
|
// Defined in import-export
|
|
showError(msg);
|
|
}
|
|
}
|
|
}
|