Merge branch '2014.11-develop' into 2015.07
[tine20] / tine20 / Calendar / Frontend / iMIP.php
1 <?php
2 /**
3  * Tine 2.0
4  * 
5  * @package     Calendar
6  * @subpackage  Frontend
7  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
8  * @author      Cornelius Weiss <c.weiss@metaways.de>
9  * @copyright   Copyright (c) 2011-2014 Metaways Infosystems GmbH (http://www.metaways.de)
10  */
11
12 /**
13  * iMIP (RFC 6047) frontend for calendar
14  * 
15  * @package     Calendar
16  * @subpackage  Frontend
17  */
18 class Calendar_Frontend_iMIP
19 {
20     /**
21      * auto process given iMIP component 
22      * 
23      * @TODO autodelete REFRESH mails
24      * 
25      * @param  Calendar_Model_iMIP $_iMIP
26      */
27     public function autoProcess($_iMIP)
28     {
29         if ($_iMIP->method == Calendar_Model_iMIP::METHOD_COUNTER) {
30             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->DEBUG(__METHOD__ . '::' . __LINE__ . " skip auto processing of iMIP component with COUNTER method -> must always be processed manually");
31             return;
32         }
33         
34         if (! $this->getExistingEvent($_iMIP, TRUE) && $_iMIP->method != Calendar_Model_iMIP::METHOD_CANCEL) {
35             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->DEBUG(__METHOD__ . '::' . __LINE__ . " skip auto processing of iMIP component whose event is not in our db yet");
36             return;
37         }
38         
39         // update existing event details _WITHOUT_ status updates
40         return $this->_process($_iMIP);
41     }
42     
43     /**
44      * manual process iMIP component and optionally set status
45      * 
46      * @param  Calendar_Model_iMIP   $_iMIP
47      * @param  string                $_status
48      */
49     public function process($_iMIP, $_status = NULL)
50     {
51         // client spoofing protection
52         $iMIP = Tinebase_EmailUser_Factory::getInstance('Controller_Message')->getiMIP($_iMIP->getId());
53
54         return $this->_process($_iMIP, $_status);
55     }
56     
57     /**
58      * prepares iMIP component for client
59      *  
60      * @param Calendar_Model_iMIP $_iMIP
61      * @param boolean $_throwException
62      * @return Calendar_Model_iMIP
63      */
64     public function prepareComponent($_iMIP, $_throwException = false)
65     {
66         $this->_checkPreconditions($_iMIP, $_throwException);
67         
68         Calendar_Convert_Event_Json::resolveRelatedData($_iMIP->event);
69         Tinebase_Model_Container::resolveContainerOfRecord($_iMIP->event);
70         Tinebase_Model_Container::resolveContainerOfRecord($this->getExistingEvent($_iMIP));
71         
72         return $_iMIP;
73     }
74     
75     /**
76      * check precondtions
77      * 
78      * @param Calendar_Model_iMIP $_iMIP
79      * @param boolean $_throwException
80      * @param string $_status
81      * @throws Calendar_Exception_iMIP
82      * @return boolean
83      * 
84      * @todo add iMIP record to exception when it extends the Data exception
85      */
86     protected function _checkPreconditions(Calendar_Model_iMIP $_iMIP, $_throwException = FALSE, $_status = NULL)
87     {
88         if ($_iMIP->preconditionsChecked) {
89             if (empty($_iMIP->preconditions) || ! $_throwException) {
90                 return;
91             } else {
92                 throw new Calendar_Exception_iMIP('iMIP preconditions failed: ' . implode(', ', array_keys($_iMIP->preconditions)));
93             }
94         }
95         
96         $method = $_iMIP->method ? ucfirst(strtolower($_iMIP->method)) : 'MISSINGMETHOD';
97         
98         $preconditionMethodName  = '_check'     . $method . 'Preconditions';
99         if (method_exists($this, $preconditionMethodName)) {
100             $preconditionCheckSuccessful = $this->{$preconditionMethodName}($_iMIP, $_status);
101         } else {
102             $preconditionCheckSuccessful = TRUE;
103             if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ . " No preconditions check fn found for method " . $method);
104         }
105         
106         $_iMIP->preconditionsChecked = TRUE;
107         
108         if ($_throwException && ! $preconditionCheckSuccessful) {
109             throw new Calendar_Exception_iMIP('iMIP preconditions failed: ' . implode(', ', array_keys($_iMIP->preconditions)));
110         }
111         
112         return $preconditionCheckSuccessful;
113     }
114     
115     /**
116      * assemble an iMIP component in the notification flow
117      * 
118      * @todo implement
119      */
120     public function assembleComponent()
121     {
122         // cancel normal vs. recur instance
123     }
124     
125     /**
126      * process iMIP component and optionally set status
127      * 
128      * @param  Calendar_Model_iMIP   $_iMIP
129      * @param  string                $_status
130      * @return mixed
131      */
132     protected function _process($_iMIP, $_status = NULL)
133     {
134         $method                  = ucfirst(strtolower($_iMIP->method));
135         $processMethodName       = '_process'   . $method;
136         
137         if (! method_exists($this, $processMethodName)) {
138             throw new Tinebase_Exception_UnexpectedValue("Method {$_iMIP->method} not supported");
139         }
140         
141         $this->_checkPreconditions($_iMIP, TRUE, $_status);
142         $result = $this->{$processMethodName}($_iMIP, $_status);
143         
144         //clear existing event cache
145         unset($_iMIP->existing_event);
146         
147         return $result;
148     }
149     
150     /**
151      * publish precondition
152      * 
153      * @param  Calendar_Model_iMIP   $_iMIP
154      * @return boolean
155      * 
156      * @todo implement
157      */
158     protected function _checkPublishPreconditions($_iMIP)
159     {
160         $_iMIP->addFailedPrecondition(Calendar_Model_iMIP::PRECONDITION_SUPPORTED, 'processing published events is not supported yet');
161         
162         return FALSE;
163     }
164     
165     /**
166      * process publish
167      * 
168      * @param  Calendar_Model_iMIP   $_iMIP
169      * 
170      * @todo implement
171      */
172     protected function _processPublish($_iMIP)
173     {
174         // add/update event (if outdated) / no status stuff / DANGER of duplicate UIDs
175         // -  no notifications!
176     }
177     
178     /**
179      * request precondition
180      * 
181      * @param  Calendar_Model_iMIP   $_iMIP
182      * @return boolean
183      */
184     protected function _checkRequestPreconditions($_iMIP)
185     {
186         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
187             . ' Checking REQUEST preconditions of iMIP ...');
188         
189         $result  = $this->_assertOwnAttender($_iMIP, TRUE, FALSE);
190         $result &= $this->_assertOrganizer($_iMIP, TRUE, TRUE);
191         
192         $existingEvent = $this->getExistingEvent($_iMIP);
193         if ($existingEvent) {
194             $iMIPEvent = $_iMIP->getEvent();
195             $isObsoleted = false;
196             
197             if (! $existingEvent->hasExternalOrganizer() && $iMIPEvent->isObsoletedBy($existingEvent)) {
198                 $isObsoleted = true;
199             }
200             
201             else if ($iMIPEvent->external_seq < $existingEvent->external_seq) {
202                 $isObsoleted = true;
203             }
204             
205             // allow if not rescheduled
206             if ($isObsoleted && $existingEvent->isRescheduled($iMIPEvent)) {
207                 $_iMIP->addFailedPrecondition(Calendar_Model_iMIP::PRECONDITION_RECENT, "old iMIP message");
208                 $result = FALSE;
209             }
210         } else {
211             try {
212                 if ($this->getExistingEvent($_iMIP, TRUE, TRUE)) {
213                     // Event was deleted/cancelled
214                     $_iMIP->addFailedPrecondition(Calendar_Model_iMIP::PRECONDITION_NOTDELETED, "old iMIP message is deleted");
215                     $result = FALSE;
216                 }
217             } catch (Tinebase_Exception_AccessDenied $e) {
218                 // attendee was removed from the event, continue ...
219                 $_iMIP->addFailedPrecondition(Calendar_Model_iMIP::PRECONDITION_NOTCANCELLED, "event or attendee was cancelled from this event");
220                 $result = FALSE;
221             }
222         }
223         
224         return $result;
225     }
226     
227     /**
228     * returns and optionally asserts own attendee record
229     *
230     * @param  Calendar_Model_iMIP   $_iMIP
231     * @param  boolean               $_assertExistence
232     * @param  boolean               $_assertOriginator
233     * @return boolean
234     */
235     protected function _assertOwnAttender($_iMIP, $_assertExistence, $_assertOriginator)
236     {
237         $result = TRUE;
238         
239         $existingEvent = $this->getExistingEvent($_iMIP);
240         $ownAttender = Calendar_Model_Attender::getOwnAttender($existingEvent ? $existingEvent->attendee : $_iMIP->getEvent()->attendee);
241         if ($_assertExistence && ! $ownAttender) {
242             $_iMIP->addFailedPrecondition(Calendar_Model_iMIP::PRECONDITION_ATTENDEE, "processing {$_iMIP->method} for non attendee is not supported");
243             $result = FALSE;
244         }
245         
246         if ($_assertOriginator) {
247             $result &= $this->_assertOriginator($_iMIP, $ownAttender->getResolvedUser(), 'own attendee');
248         }
249         
250         return $result;
251     }
252     
253     /**
254      * assert originator
255      * 
256      * @param Calendar_Model_iMIP $_iMIP
257      * @param Addressbook_Model_Contact $_contact
258      * @param string $_who
259      * @return boolean
260      */
261     protected function _assertOriginator(Calendar_Model_iMIP $_iMIP, $_contact, $_who)
262     {
263         if ($_contact === NULL) {
264             $_iMIP->addFailedPrecondition(Calendar_Model_iMIP::PRECONDITION_ORIGINATOR, $_who . " could not be found.");
265             return FALSE;
266         }
267         
268         $contactEmails = array($_contact->email, $_contact->email_home);
269         if(! in_array($_iMIP->originator, $contactEmails)) {
270             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->DEBUG(__METHOD__ . '::' . __LINE__
271             . ' originator ' . $_iMIP->originator . ' ! in_array() '. print_r($contactEmails, TRUE));
272         
273             $_iMIP->addFailedPrecondition(Calendar_Model_iMIP::PRECONDITION_ORIGINATOR, $_who . " must be the same as originator of iMIP -> spoofing attempt?");
274             return FALSE;
275         } else {
276             return TRUE;
277         }
278     }
279     
280     /**
281      * 
282      *
283      * @param  Calendar_Model_iMIP   $_iMIP
284      * @param  bool                  $_assertExistence
285      * @param  bool                  $_assertOriginator
286      * @param  bool                  $_assertAccount
287      * @return Addressbook_Model_Contact
288      * @throws Calendar_Exception_iMIP
289      * 
290      * @todo this needs to be splitted into assertExternalOrganizer / assertInternalOrganizer
291      */
292     protected function _assertOrganizer($_iMIP, $_assertExistence, $_assertOriginator, $_assertAccount = false)
293     {
294         $result = TRUE;
295         
296         $existingEvent = $this->getExistingEvent($_iMIP);
297         $organizer = $existingEvent ? $existingEvent->resolveOrganizer() : $_iMIP->getEvent()->resolveOrganizer();
298         
299         if ($_assertExistence && ! $organizer) {
300             $_iMIP->addFailedPrecondition(Calendar_Model_iMIP::PRECONDITION_ORGANIZER, "processing {$_iMIP->method} without organizer is not possible");
301             $result = FALSE;
302         }
303         
304         // NOTE: originator might also be reply-to instead of from
305         // NOTE: originator might act on behalf of organizer ("SENT-BY    ")
306         // NOTE: an existing event might be updateable by an non organizer ("SENT-BY    ") originator
307         // NOTE: CUA might skip the SENT-BY     param => bad luck
308         /*
309         if ($_assertOriginator) {
310             $result &= $this->_assertOriginator($_iMIP, $organizer, 'organizer');
311         }
312         */
313         
314         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
315             . ' Organizer: ' . ($organizer ? print_r($organizer->toArray(), true) : 'not found'));
316         
317         // config setting overwrites method param
318         $assertAccount = Calendar_Config::getInstance()->get(Calendar_Config::DISABLE_EXTERNAL_IMIP, $_assertAccount);
319         if ($assertAccount && (! $organizer || ! $organizer->account_id)) {
320             $_iMIP->addFailedPrecondition(Calendar_Model_iMIP::PRECONDITION_ORGANIZER, "processing {$_iMIP->method} without organizer user account is not possible");
321             $result = FALSE;
322         }
323         
324         return $result;
325     }
326
327     /**
328      * find existing event by uid
329      *
330      * @param $_iMIP
331      * @param bool $_refetch
332      * @param bool $_getDeleted
333      * @return NULL|Tinebase_Record_Abstract
334      * @throws Exception
335      */
336     public function getExistingEvent($_iMIP, $_refetch = FALSE, $_getDeleted = FALSE)
337     {
338         if ($_refetch || ! $_iMIP->existing_event instanceof Calendar_Model_Event) {
339
340             $iMIPEvent = $_iMIP->getEvent();
341
342             $filters = new Calendar_Model_EventFilter(array(
343                 array('field' => 'uid',          'operator' => 'equals', 'value' => $iMIPEvent->uid),
344             ));
345             if ($_getDeleted) {
346                 $deletedFilter = new Tinebase_Model_Filter_Bool('is_deleted', 'equals', Tinebase_Model_Filter_Bool::VALUE_NOTSET);
347                 $filters->addFilter($deletedFilter);
348             }
349             $events = $this->_backend->search($filters);
350
351             // NOTE: cancelled attendees from ext. organizers don't have read grant
352             // find a better way to check grants
353             if (! $_getDeleted) {
354                 $event = $events->filter(Tinebase_Model_Grants::GRANT_READ, TRUE)->getFirstRecord();
355             }
356             Calendar_Model_Attender::resolveAttendee($event['attendee']);
357
358             $_iMIP->existing_event = $event;
359         }
360
361         return $_iMIP->existing_event;
362     }
363
364     /**
365      * process request
366      * 
367      * @param  Calendar_Model_iMIP   $_iMIP
368      * @param  string                $_status
369      */
370     protected function _processRequest($_iMIP, $_status)
371     {
372         $existingEvent = $this->getExistingEvent($_iMIP);
373         $ownAttender = Calendar_Model_Attender::getOwnAttender($existingEvent ? $existingEvent->attendee : $_iMIP->getEvent()->attendee);
374         $organizer = $existingEvent ? $existingEvent->resolveOrganizer() : $_iMIP->getEvent()->resolveOrganizer();
375         
376         // internal organizer:
377         //  - event is up to date
378         //  - status change could also be done by calendar method
379         //  - normal notifications
380         if ($organizer->account_id) {
381             if (! $existingEvent) {
382                 // organizer has an account but no event exists, it seems that event was created from a non-caldav client
383                 // do not send notifications in this case + create event in context of organizer
384                 if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__
385                         . ' Organizer has an account but no event exists!');
386                 return; // not clear how to create in the organizers context...
387                 $sendNotifications = Calendar_Controller_Event::getInstance()->sendNotifications(FALSE);
388                 $existingEvent = Calendar_Controller_MSEventFacade::getInstance()->create($_iMIP->getEvent());
389                 Calendar_Controller_Event::getInstance()->sendNotifications($sendNotifications);
390             }
391             
392             if ($_status && $_status != $ownAttender->status) {
393                 $ownAttender->status = $_status;
394                 Calendar_Controller_Event::getInstance()->attenderStatusUpdate($existingEvent, $ownAttender, $ownAttender->status_authkey);
395             }
396         }
397         
398         // external organizer:
399         else {
400             $sendNotifications = Calendar_Controller_Event::getInstance()->sendNotifications(false);
401             $event = $_iMIP->getEvent();
402             if (! $existingEvent) {
403                 if (! $event->container_id) {
404                     $event->container_id = Tinebase_Core::getPreference('Calendar')->{Calendar_Preference::DEFAULTCALENDAR};
405                 }
406                 
407                 $event = $_iMIP->event = Calendar_Controller_MSEventFacade::getInstance()->create($event);
408             } else {
409                 if ($event->external_seq > $existingEvent->external_seq && !$_status) {
410                     // no buttons pressed (just reading/updating)
411                     // updates event with .ics
412                     $event->id = $existingEvent->id;
413                     $event = $_iMIP->event = Calendar_Controller_MSEventFacade::getInstance()->update($event);
414                 } else {
415                     $event = $_iMIP->event = Calendar_Controller_MSEventFacade::getInstance()->update($existingEvent);
416                 }
417             }
418             
419             Calendar_Controller_Event::getInstance()->sendNotifications($sendNotifications);
420             
421             $ownAttender = Calendar_Model_Attender::getOwnAttender($event->attendee);
422             
423             // NOTE: we do the status update in a separate call to trigger the right notifications
424             if ($ownAttender && $_status) {
425                 $ownAttender->status = $_status;
426                 $a = Calendar_Controller_Event::getInstance()->attenderStatusUpdate($event, $ownAttender, $ownAttender->status_authkey);
427             }
428         }
429     }
430     
431     /**
432      * reply precondition
433      *
434      * @TODO an internal reply should trigger a RECENT precondition
435      * @TODO distinguish RECENT and PROCESSED preconditions?
436      * 
437      * @param  Calendar_Model_iMIP   $_iMIP
438      * @return boolean
439      */
440     protected function _checkReplyPreconditions($_iMIP)
441     {
442         $result = TRUE;
443         
444         $existingEvent = $this->getExistingEvent($_iMIP);
445         if (! $existingEvent) {
446             $_iMIP->addFailedPrecondition(Calendar_Model_iMIP::PRECONDITION_EVENTEXISTS, "cannot process REPLY to non existent/invisible event");
447             $result = FALSE;
448         }
449         
450         $iMIPAttenderIdx = $_iMIP->getEvent()->attendee instanceof Tinebase_Record_RecordSet ? array_search($_iMIP->originator, $_iMIP->getEvent()->attendee->getEmail()) : FALSE;
451         $iMIPAttender = $iMIPAttenderIdx !== FALSE ? $_iMIP->getEvent()->attendee[$iMIPAttenderIdx] : NULL;
452         $iMIPAttenderStatus = $iMIPAttender ? $iMIPAttender->status : NULL;
453         $eventAttenderIdx = $existingEvent->attendee instanceof Tinebase_Record_RecordSet ? array_search($_iMIP->originator, $existingEvent->attendee->getEmail()) : FALSE;
454         $eventAttender = $eventAttenderIdx !== FALSE ? $existingEvent->attendee[$eventAttenderIdx] : NULL;
455         $eventAttenderStatus = $eventAttender ? $eventAttender->status : NULL;
456         
457         if ($_iMIP->getEvent()->isObsoletedBy($existingEvent)) {
458             
459             // allow non RECENT replies if no reschedule and STATUS_NEEDSACTION
460             if ($eventAttenderStatus != Calendar_Model_Attender::STATUS_NEEDSACTION || $existingEvent->isRescheduled($_iMIP->getEvent())) {
461                 $_iMIP->addFailedPrecondition(Calendar_Model_iMIP::PRECONDITION_RECENT, "old iMIP message");
462                 $result = FALSE;
463             }
464         }
465         
466         if (! is_null($iMIPAttenderStatus) && $iMIPAttenderStatus == $eventAttenderStatus) {
467             $_iMIP->addFailedPrecondition(Calendar_Model_iMIP::PRECONDITION_TOPROCESS, "this REPLY was already processed");
468             $result = FALSE;
469         }
470         
471         if (! $eventAttender) {
472             $_iMIP->addFailedPrecondition(Calendar_Model_iMIP::PRECONDITION_ORIGINATOR, "originator is not attendee in existing event -> party crusher?");
473             $result = FALSE;
474         }
475         
476         if (! $iMIPAttender) {
477             $_iMIP->addFailedPrecondition(Calendar_Model_iMIP::PRECONDITION_ORIGINATOR, "originator is not attendee in iMIP transaction -> spoofing attempt?");
478             $result = FALSE;
479         }
480         
481         // TODO fix organizer account asserting
482         if (! $this->_assertOrganizer($_iMIP, TRUE, FALSE/*, $_assertAccount = TRUE */)) {
483             $result = FALSE;
484         }
485         
486         return $result;
487     }
488     
489     /**
490      * process reply
491      * 
492      * some attender replied to my request (I'm Organizer) -> update status (seq++) / send notifications!
493      * 
494      * NOTE: only external replies should be processed here
495      *       @todo check silence for internal replies
496      *       
497      * @param  Calendar_Model_iMIP   $_iMIP
498      */
499     protected function _processReply(Calendar_Model_iMIP $_iMIP)
500     {
501         // merge ics into existing event
502         $existingEvent = $this->getExistingEvent($_iMIP);
503         $event = $_iMIP->mergeEvent($existingEvent);
504         $attendee = $event->attendee[array_search($_iMIP->originator, $existingEvent->attendee->getEmail())];
505         
506         // NOTE: if current user has no rights to the calendar, status update is not applied
507         Calendar_Controller_MSEventFacade::getInstance()->attenderStatusUpdate($event, $attendee);
508     }
509     
510     /**
511     * add precondition
512     *
513     * @param  Calendar_Model_iMIP   $_iMIP
514     * @return boolean
515     *
516     * @todo implement
517     */
518     protected function _checkAddPreconditions($_iMIP)
519     {
520         $_iMIP->addFailedPrecondition(Calendar_Model_iMIP::PRECONDITION_SUPPORTED, 'processing add requests is not supported yet');
521     
522         return FALSE;
523     }
524     
525     /**
526     * process add
527     *
528     * @param  Calendar_Model_iMIP   $_iMIP
529     * 
530     * @todo implement
531     */
532     protected function _processAdd($_iMIP)
533     {
534         // organizer added a meeting/recurrance to an existing event -> update event
535         // internal organizer:
536         //  - event is up to date nothing to do
537         // external organizer:
538         //  - update event
539         //  - the iMIP is already the notification mail!
540     }
541     
542     /**
543     * cancel precondition
544     *
545     * @param  Calendar_Model_iMIP   $_iMIP
546     * @return boolean
547     */
548     protected function _checkCancelPreconditions($_iMIP)
549     {
550         $existingEvent = $this->getExistingEvent($_iMIP, FALSE, TRUE);
551         $result = TRUE;
552
553         if ($existingEvent) {
554             if ($existingEvent->is_deleted) {
555                 $_iMIP->addFailedPrecondition(Calendar_Model_iMIP::PRECONDITION_NOTDELETED, "old iMIP message is deleted");
556                 $result = FALSE;
557             } else if (! $existingEvent->hasExternalOrganizer()) {
558                 $_iMIP->addFailedPrecondition(Calendar_Model_iMIP::PRECONDITION_RECENT, "old iMIP message");
559                 $result = FALSE;
560             }
561         }
562         return $result;
563     }
564     
565     /**
566     * process cancel
567     *
568     * @param  Calendar_Model_iMIP   $_iMIP
569     * @param  string                $_status
570     */
571     protected function _processCancel($_iMIP, $_status)
572     {
573         // organizer cancelled meeting/recurrence of an existing event -> update event
574         // the iMIP is already the notification mail!
575         $existingEvent = $this->getExistingEvent($_iMIP, FALSE, TRUE);
576         $event = $_iMIP->getEvent();
577
578         if ($existingEvent) {
579             if (! $existingEvent->is_deleted) {
580                 if ($event->status == Calendar_Model_Event::STATUS_CANCELED) {
581                     // Event cancelled
582                     Calendar_Controller_MSEventFacade::getInstance()->delete($existingEvent->getId());
583                 } else {
584                     // Attendees cancelled
585                     Calendar_Controller_MSEventFacade::getInstance()->deleteAttendees($existingEvent, $event);
586                 }
587             }
588         } else {
589             // create a deleted/cancelled event
590             $sendNotifications = Calendar_Controller_Event::getInstance()->sendNotifications(FALSE);
591
592             $event = $_iMIP->event = Calendar_Controller_MSEventFacade::getInstance()->create($event);
593             Calendar_Controller_MSEventFacade::getInstance()->delete($event->getId());
594
595             Calendar_Controller_Event::getInstance()->sendNotifications($sendNotifications);
596         }
597     }
598     
599     /**
600     * refresh precondition
601     *
602     * @param  Calendar_Model_iMIP   $_iMIP
603     * @return boolean
604     *
605     * @todo implement
606     */
607     protected function _checkRefreshPreconditions($_iMIP)
608     {
609         $_iMIP->addFailedPrecondition(Calendar_Model_iMIP::PRECONDITION_SUPPORTED, 'processing REFRESH is not supported yet');
610     
611         return FALSE;
612     }
613     
614     /**
615     * process refresh
616     *
617     * @param  Calendar_Model_iMIP   $_iMIP
618     *
619     * @todo implement
620     */
621     protected function _processRefresh($_iMIP)
622     {
623         // always internal organizer
624         //  - send message
625         //  - mark iMIP message ANSWERED
626     }
627     
628     /**
629     * counter precondition
630     *
631     * @param  Calendar_Model_iMIP   $_iMIP
632     * @return boolean
633     *
634     * @todo implement
635     */
636     protected function _checkCounterPreconditions($_iMIP)
637     {
638         $_iMIP->addFailedPrecondition(Calendar_Model_iMIP::PRECONDITION_SUPPORTED, 'processing COUNTER is not supported yet');
639     
640         return FALSE;
641     }
642     
643     /**
644     * process counter
645     *
646     * @param  Calendar_Model_iMIP   $_iMIP
647     *
648     * @todo implement
649     */
650     protected function _processCounter($_iMIP)
651     {
652         // some attendee suggests to change the event
653         // status: ACCEPT => update event, send notifications to all
654         // status: DECLINE => send DECLINECOUNTER to originator
655         // mark message ANSWERED
656     }
657     
658     /**
659     * declinecounter precondition
660     *
661     * @param  Calendar_Model_iMIP   $_iMIP
662     * @return boolean
663     *
664     * @todo implement
665     */
666     protected function _checkDeclinecounterPreconditions($_iMIP)
667     {
668         $_iMIP->addFailedPrecondition(Calendar_Model_iMIP::PRECONDITION_SUPPORTED, 'processing DECLINECOUNTER is not supported yet');
669     
670         return FALSE;
671     }
672     
673     /**
674     * process declinecounter
675     *
676     * @param  Calendar_Model_iMIP   $_iMIP
677     *
678     * @todo implement
679     */
680     protected function _processDeclinecounter($_iMIP)
681     {
682         // organizer declined my counter request of an existing event -> update event
683     }
684 }