home *** CD-ROM | disk | FTP | other *** search
/ PC Format (PL) 2008 February / PC_Format_022008.iso / Internet / Mozilla Thunderbird wtyczki / lightning-0.7-tb-win.xpi / js / calItipProcessor.js < prev    next >
Encoding:
Text File  |  2007-09-28  |  14.0 KB  |  388 lines

  1. /* -*- Mode: javascript; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  2. /* ***** BEGIN LICENSE BLOCK *****
  3.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4.  *
  5.  * The contents of this file are subject to the Mozilla Public License Version
  6.  * 1.1 (the "License"); you may not use this file except in compliance with
  7.  * the License. You may obtain a copy of the License at
  8.  * http://www.mozilla.org/MPL/
  9.  *
  10.  * Software distributed under the License is distributed on an "AS IS" basis,
  11.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12.  * for the specific language governing rights and limitations under the
  13.  * License.
  14.  *
  15.  * The Original Code is Simdesk Technologies code.
  16.  *
  17.  * The Initial Developer of the Original Code is Simdesk Technologies Inc.
  18.  * Portions created by the Initial Developer are Copyright (C) 2007
  19.  * the Initial Developer. All Rights Reserved.
  20.  *
  21.  * Contributor(s):
  22.  *   Clint Talbert <ctalbert.moz@gmail.com>
  23.  *   Eva Or <evaor1012@yahoo.ca>
  24.  *   Matthew Willis <lilmatt@mozilla.com>
  25.  *
  26.  * Alternatively, the contents of this file may be used under the terms of
  27.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  28.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  29.  * in which case the provisions of the GPL or the LGPL are applicable instead
  30.  * of those above. If you wish to allow use of your version of this file only
  31.  * under the terms of either the GPL or the LGPL, and not to allow others to
  32.  * use your version of this file under the terms of the MPL, indicate your
  33.  * decision by deleting the provisions above and replace them with the notice
  34.  * and other provisions required by the GPL or the LGPL. If you do not delete
  35.  * the provisions above, a recipient may use your version of this file under
  36.  * the terms of any one of the MPL, the GPL or the LGPL.
  37.  *
  38.  * ***** END LICENSE BLOCK ***** */
  39.  
  40.  
  41. // Operations on the calendar
  42. const CAL_ITIP_PROC_ADD_OP = 1;
  43. const CAL_ITIP_PROC_UPDATE_OP = 2;
  44. const CAL_ITIP_PROC_DELETE_OP = 3;
  45.  
  46. /**
  47.  * Constructor of calItipItem object
  48.  */
  49. function calItipProcessor() {
  50.     this.wrappedJSObject = this;
  51. }
  52.  
  53. calItipProcessor.prototype = {
  54.     getInterfaces: function cipGI(count) {
  55.         var ifaces = [
  56.             Components.interfaces.nsIClassInfo,
  57.             Components.interfaces.nsISupports,
  58.             Components.interfaces.calIItipProcessor
  59.         ];
  60.         count.value = ifaces.length;
  61.         return ifaces;
  62.     },
  63.  
  64.     getHelperForLanguage: function cipGHFL(aLanguage) {
  65.         return null;
  66.     },
  67.  
  68.     contractID: "@mozilla.org/calendar/itip-processor;1",
  69.     classDescription: "Calendar iTIP processor",
  70.     classID: Components.ID("{9787876b-0780-4464-8282-b7f86fb221e8}"),
  71.     implementationLanguage: Components.interfaces.nsIProgrammingLanguage.JAVASCRIPT,
  72.     flags: 0,
  73.  
  74.     QueryInterface: function cipQI(aIid) {
  75.         if (!aIid.equals(Components.interfaces.nsIClassInfo) &&
  76.             !aIid.equals(Components.interfaces.nsISupports) &&
  77.             !aIid.equals(Components.interfaces.calIItipProcessor))
  78.         {
  79.             throw Components.results.NS_ERROR_NO_INTERFACE;
  80.         }
  81.  
  82.         return this;
  83.     },
  84.  
  85.     mIsUserInvolved: false,
  86.     get isUserInvolved() {
  87.         return this.mIsUserInvolved;
  88.     },
  89.     set isUserInvolved(aValue) {
  90.         return (this.mIsUserInvolved = aValue);
  91.     },
  92.  
  93.     /**
  94.      * Processes the given calItipItem based on the settings inside it.
  95.      * @param calIItipItem  A calItipItem to process.
  96.      * @param calIOperationListener A calIOperationListener to return status
  97.      * @return boolean  Whether processing succeeded or not.
  98.      */
  99.     processItipItem: function cipPII(aItipItem, aListener) {
  100.         // Sanity check the input
  101.         if (!aItipItem) {
  102.             throw new Components.Exception("processItipItem: " +
  103.                                            "Invalid or non-existant " +
  104.                                            "itipItem passed in.",
  105.                                            Components.results.NS_ERROR_INVALID_ARG);
  106.         }
  107.  
  108.         // Clone the passed in itipItem like a sheep.
  109.         var respItipItem = aItipItem.clone();
  110.  
  111.         var recvMethod = respItipItem.receivedMethod;
  112.         respItipItem.responseMethod = this._suggestResponseMethod(recvMethod);
  113.         var respMethod = respItipItem.responseMethod;
  114.  
  115.         var autoResponse = respItipItem.autoResponse;
  116.         var targetCalendar = respItipItem.targetCalendar;
  117.  
  118.         // XXX Support for transports other than email go here.
  119.         //     For now we just assume it's email.
  120.         var transport = Components.classes["@mozilla.org/calendar/itip-transport;1?type=email"].
  121.                         createInstance(Components.interfaces.calIItipTransport);
  122.  
  123.         // Sanity checks using the first item
  124.         var itemList = respItipItem.getItemList({ });
  125.         var calItem = itemList[0];
  126.         if (!calItem) {
  127.             throw new Error ("processItipItem: " +
  128.                              "getFirstItem() found no items!");
  129.         }
  130.  
  131.         var calItemType = this._getCalItemType(calItem);
  132.         if (!calItemType) {
  133.             throw new Error ("processItipItem: " +
  134.                              "_getCalItemType() found no item type!");
  135.         }
  136.  
  137.         // Sanity check that mRespMethod is a valid response per the spec.
  138.         if (!this._isValidResponseMethod(recvMethod, respMethod, calItemType)) {
  139.             throw new Error ("processItipItem: " +
  140.                              "_isValidResponseMethod() found an invalid " +
  141.                              "response method: " + respMethod);
  142.         }
  143.  
  144.         var i = 0;
  145.         while (calItem) {
  146.             switch (recvMethod) {
  147.                 case "REQUEST":
  148.                     // Only add to calendar if we accepted invite
  149.                     var replyStat = this._getReplyStatus(calItem,
  150.                                                          transport.defaultIdentity);
  151.                     if (replyStat == "DECLINED") {
  152.                         break;
  153.                     }
  154.                     // else fall through
  155.                 case "PUBLISH":
  156.                     if (!this._processCalendarAction(calItem,
  157.                                                      CAL_ITIP_PROC_ADD_OP,
  158.                                                      targetCalendar,
  159.                                                      aListener))
  160.                     {
  161.                         throw new Error ("processItipItem: " +
  162.                                          "_processCalendarAction failed!");
  163.                     }
  164.                     break;
  165.                 case "REPLY":
  166.                 case "REFRESH":
  167.                 case "ADD":
  168.                 case "CANCEL":
  169.                 case "COUNTER":
  170.                 case "DECLINECOUNTER":
  171.                     throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  172.  
  173.                 default:
  174.                     throw new Error("processItipItem: " +
  175.                                     "Received unknown method: " +
  176.                                     recvMethod);
  177.             }
  178.             ++i;
  179.             calItem = itemList[i];
  180.         }
  181.  
  182.         // When replying, the reply must only contain the ORGANIZER and the
  183.         // status of the ATTENDEE that represents ourselves. Therefore we must
  184.         // remove all other ATTENDEEs from the itipItem we send back.
  185.         if (respMethod == "REPLY") {
  186.             // Get the id that represents me.
  187.             // XXX Note that this doesn't take into consideration invitations
  188.             //     sent to email aliases. (ex: lilmatt vs mwillis)
  189.             var me;
  190.             var idPrefix;
  191.             if (transport.type == "email") {
  192.                 me = transport.defaultIdentity;
  193.                 idPrefix = "mailto:";
  194.             } else {
  195.                 throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  196.             }
  197.  
  198.             for (var j=0; j < itemList.length; ++j) {
  199.                 var respCalItem = itemList[j];
  200.                 var attendees = respCalItem.getAttendees({});
  201.  
  202.                 for each (var attendee in attendees) {
  203.                     // Leave the ORGANIZER alone.
  204.                     if (!attendee.isOrganizer) {
  205.                         // example: mailto:joe@domain.com
  206.                         var meString = idPrefix + me;
  207.                         if (attendee.id.toLowerCase() != meString.toLowerCase()) {
  208.                             respCalItem.removeAttendee(attendee);
  209.                         }
  210.                     }
  211.                 }
  212.             }
  213.         }
  214.  
  215.         // Send the appropriate response
  216.         // figure out a good way to determine when a response is needed!
  217.         if (recvMethod != respMethod) {
  218.             transport.simpleSendResponse(respItipItem);
  219.         }
  220.  
  221.         // Yay it worked!
  222.         // XXX TODO: Actually tie this to success/failure of the transport
  223.         return true;
  224.     },
  225.  
  226.  
  227.     /**
  228.      * @return integer  The next recommended iTIP state.
  229.      */
  230.     _suggestResponseMethod: function cipSRM(aRecvMethod) {
  231.         switch (aRecvMethod) {
  232.             case "REQUEST":
  233.                 return "REPLY";
  234.  
  235.             case "REFRESH":
  236.             case "COUNTER":
  237.                 return "REQUEST";
  238.  
  239.             case "PUBLISH":
  240.             case "REPLY":
  241.             case "ADD":
  242.             case "CANCEL":
  243.             case "DECLINECOUNTER":
  244.                 return aRecvMethod;
  245.  
  246.             default:
  247.                 throw new Error("_suggestResponseMethod: " +
  248.                                 "Received unknown method: " +
  249.                                 aRecvMethod);
  250.         }
  251.     },
  252.  
  253.     /**
  254.      * Given mRecvMethod and mRespMethod, this checks that mRespMethod is
  255.      * valid according to the spec.
  256.      *
  257.      * @return boolean  Whether or not mRespMethod is valid.
  258.      */
  259.     _isValidResponseMethod: function cipIAR(aRecvMethod,
  260.                                             aRespMethod,
  261.                                             aCalItemType) {
  262.         switch (aRecvMethod) {
  263.             // We set response to ADD automatically, but if the GUI did not
  264.             // find the event the user may set it to REFRESH as per the spec.
  265.             // These are the only two valid responses.
  266.             case "ADD":
  267.                 if (!(aRespMethod == "ADD" ||
  268.                      (aRespMethod == "REFRESH" &&
  269.                      // REFRESH is not a valid response to an ADD for VJOURNAL
  270.                      (aCalItemType == Components.interfaces.calIEvent ||
  271.                       aCalItemType == Components.interfaces.calITodo))))
  272.                 {
  273.                     return false;
  274.                 }
  275.                 break;
  276.  
  277.             // Valid responses to COUNTER are REQUEST or DECLINECOUNTER.
  278.             case "COUNTER":
  279.                 if (!(aRespMethod == "REQUEST" ||
  280.                       aRespMethod == "DECLINECOUNTER"))
  281.                 {
  282.                     return false;
  283.                 }
  284.                 break;
  285.  
  286.             // Valid responses to REQUEST are:
  287.             //     REPLY   (accept or error)
  288.             //     REQUEST (delegation, inviting someone else)
  289.             //     COUNTER (propose a change)
  290.             case "REQUEST":
  291.                 if (!(aRespMethod == "REPLY" ||
  292.                       aRespMethod == "REQUEST" ||
  293.                       aRespMethod == "COUNTER"))
  294.                 {
  295.                     return false;
  296.                 }
  297.                 break;
  298.  
  299.             // REFRESH should respond with a request
  300.             case "REFRESH":
  301.                 if (aRespMethod == "REQUEST") {
  302.                     return false;
  303.                 }
  304.                 break;
  305.  
  306.             // The rest are easiest represented as:
  307.             //     (aRecvMethod != aRespMethod) == return false
  308.             case "PUBLISH":
  309.             case "CANCEL":
  310.             case "REPLY":
  311.             case "PUBLISH":
  312.             case "DECLINECOUNTER":
  313.                 if (aRespMethod != aRecvMethod) {
  314.                     return false;
  315.                 }
  316.                 break;
  317.  
  318.             default:
  319.                 throw new Error("_isValidResponseMethod: " +
  320.                                 "Received unknown method: " +
  321.                                 aRecvMethod);
  322.         }
  323.  
  324.         // If we got to here, then the combination is valid.
  325.         return true;
  326.     },
  327.  
  328.     /**
  329.      * Helper to return whether an item is an event, todo, etc.
  330.      */
  331.     _getCalItemType: function cipGCIT(aCalItem) {
  332.         if (aCalItem instanceof Components.interfaces.calIEvent) {
  333.             return Components.interfaces.calIEvent;
  334.         } else if (aCalItem instanceof Components.interfaces.calITodo) {
  335.             return Components.interfaces.calITodo;
  336.         }
  337.  
  338.         throw new Error ("_getCalItemType: " +
  339.                          "mCalItem item type is unknown");
  340.     },
  341.  
  342.     /**
  343.      * This performs the actual add/update/delete of an event on the user's
  344.      * calendar.
  345.      */
  346.     _processCalendarAction: function cipPCA(aCalItem,
  347.                                             aOperation,
  348.                                             aTargetCalendar,
  349.                                             aListener) {
  350.         switch (aOperation) {
  351.             case CAL_ITIP_PROC_ADD_OP:
  352.                 aTargetCalendar.addItem(aCalItem, aListener);
  353.  
  354.                 // XXX Change this to reflect the success or failure of adding
  355.                 //     the item to the calendar.
  356.                 return true;
  357.  
  358.             case CAL_ITIP_PROC_UPDATE_OP:
  359.             case CAL_ITIP_PROC_DELETE_OP:
  360.                 throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  361.  
  362.             default:
  363.                 throw new Error("_processCalendarAction: " +
  364.                                 "Undefined Operator: " + aOperator);
  365.         }
  366.  
  367.         // If you got to here, something went horribly, horribly wrong.
  368.         return false;
  369.     },
  370.  
  371.     /**
  372.      * Helper function to get the PARTSTAT value from the given calendar
  373.      * item for the specified attendee
  374.      */
  375.     _getReplyStatus: function cipGRS(aCalItem, aAttendeeId) {
  376.         var idPrefix = "mailto:";
  377.         var replyStatus;
  378.  
  379.         // example: mailto:joe@domain.com
  380.         var idString = idPrefix + aAttendeeId;
  381.         var attendee = aCalItem.getAttendeeById(idString);
  382.         if (attendee) {
  383.             replyStatus = attendee.participationStatus;
  384.         }
  385.         return replyStatus;
  386.     }
  387. }
  388.