protected function _keepOrganizerEvents($_eventObject)
 {
     $accountId = $_eventObject->account->getId();
     $contactId = $_eventObject->account->contact_id;
     $contact = null;
     $newContact = null;
     $contactEmail = null;
     if ($_eventObject->keepAsContact()) {
         try {
             $contact = Addressbook_Controller_Contact::getInstance()->get($contactId);
             $contactEmail = $contact->getPreferedEmailAddress();
         } catch (Tinebase_Exception_NotFound $tenf) {
             // ignore
             $contactEmail = $_eventObject->account->accountEmailAddress;
         }
     } else {
         $contactEmail = $_eventObject->account->accountEmailAddress;
     }
     if (null === $contact) {
         $newContact = Calendar_Model_Attender::resolveEmailToContact(array('email' => $contactEmail));
     }
     $eventController = Calendar_Controller_Event::getInstance();
     $oldState = $eventController->doContainerACLChecks(false);
     // delete all events where our deletee is organizer and that are private (no matter if they have attendees or not)
     /*$filter = new Calendar_Model_EventFilter(array(
           array('field' => 'class', 'operator' => 'equals', 'value' => Calendar_Model_Event::CLASS_PRIVATE),
           array('field' => 'organizer', 'operator' => 'equals', 'value' => $contactId),
       ));
       $eventController->deleteByFilter($filter);*/
     // delete all events where our deletee is organizer and that dont have any additional attenders except the organizer / deletee himself
     $filter = new Calendar_Model_EventFilter(array(array('field' => 'organizer', 'operator' => 'equals', 'value' => $contactId), array('field' => 'attender', 'operator' => 'notHasSomeExcept', 'value' => array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => $contactId))));
     $eventController->deleteByFilter($filter);
     $eventController->doContainerACLChecks($oldState);
     // get all personal containers
     $containers = Tinebase_Container::getInstance()->getPersonalContainer($accountId, $this->getDefaultModel(), $accountId, '*', true);
     if ($containers->count() > 0) {
         // take the first one and make it an invitation container
         $container = $containers->getByIndex(0);
         $this->convertToInvitationContainer($container, $contactEmail);
         // if there are more than 1 container, move contents to invitation container, then delete them
         $i = 1;
         while ($containers->count() > 1) {
             $moveContainer = $containers->getByIndex($i);
             $containers->offsetUnset($i++);
             //move $moveContainer content to $container
             $eventController->getBackend()->moveEventsToContainer($moveContainer, $container);
             //delete $moveContainer
             Tinebase_Container::getInstance()->deleteContainer($moveContainer, true);
         }
     }
     // replace old contactId with newContact->getId()
     if (null !== $newContact) {
         $eventController->getBackend()->replaceContactId($contactId, $newContact->getId());
     }
 }
Ejemplo n.º 2
0
 /**
  * parse VEVENT part of VCALENDAR
  * 
  * @param  Sabre_VObject_Component  $_vevent  the VEVENT to parse
  * @param  Calendar_Model_Event     $_event   the Tine 2.0 event to update
  */
 protected function _convertVevent(Sabre_VObject_Component $_vevent, Calendar_Model_Event $_event)
 {
     $event = $_event;
     $newAttendees = array();
     // unset supported fields
     foreach ($this->_supportedFields as $field) {
         if ($field == 'alarms') {
             $event->{$field} = new Tinebase_Record_RecordSet('Tinebase_Model_Alarm');
         } else {
             $event->{$field} = null;
         }
     }
     foreach ($_vevent->children() as $property) {
         switch ($property->name) {
             case 'CREATED':
             case 'DTSTAMP':
                 // do nothing
                 break;
             case 'LAST-MODIFIED':
                 $event->last_modified_time = new Tinebase_DateTime($property->value);
                 break;
             case 'ATTENDEE':
                 $newAttendees[] = $this->_getAttendee($property);
                 break;
             case 'CLASS':
                 if (in_array($property->value, array(Calendar_Model_Event::CLASS_PRIVATE, Calendar_Model_Event::CLASS_PUBLIC))) {
                     $event->class = $property->value;
                 } else {
                     $event->class = Calendar_Model_Event::CLASS_PUBLIC;
                 }
                 break;
             case 'DTEND':
                 if (isset($property['VALUE']) && strtoupper($property['VALUE']) == 'DATE') {
                     // all day event
                     $event->is_all_day_event = true;
                     $dtend = $this->_convertToTinebaseDateTime($property, TRUE);
                     // whole day events ends at 23:59:59 in Tine 2.0 but 00:00 the next day in vcalendar
                     $dtend->subSecond(1);
                 } else {
                     $event->is_all_day_event = false;
                     $dtend = $this->_convertToTinebaseDateTime($property);
                 }
                 $event->dtend = $dtend;
                 break;
             case 'DTSTART':
                 if (isset($property['VALUE']) && strtoupper($property['VALUE']) == 'DATE') {
                     // all day event
                     $event->is_all_day_event = true;
                     $dtstart = $this->_convertToTinebaseDateTime($property, TRUE);
                 } else {
                     $event->is_all_day_event = false;
                     $dtstart = $this->_convertToTinebaseDateTime($property);
                 }
                 $event->originator_tz = $dtstart->getTimezone()->getName();
                 $event->dtstart = $dtstart;
                 break;
             case 'SEQUENCE':
                 $event->seq = $property->value;
                 break;
             case 'DESCRIPTION':
             case 'LOCATION':
             case 'UID':
             case 'SUMMARY':
                 $key = strtolower($property->name);
                 $event->{$key} = $property->value;
                 break;
             case 'ORGANIZER':
                 if (preg_match('/mailto:(?P<email>.*)/i', $property->value, $matches)) {
                     // it's not possible to change the organizer by spec
                     if (empty($event->organizer)) {
                         $name = isset($property['CN']) ? $property['CN']->value : $matches['email'];
                         $contact = Calendar_Model_Attender::resolveEmailToContact(array('email' => $matches['email'], 'lastName' => $name));
                         $event->organizer = $contact->getId();
                     }
                     // Lightning attaches organizer ATTENDEE properties to ORGANIZER property and does not add an ATTENDEE for the organizer
                     if (isset($property['PARTSTAT'])) {
                         $newAttendees[] = $this->_getAttendee($property);
                     }
                 }
                 break;
             case 'RECURRENCE-ID':
                 // original start of the event
                 $event->recurid = $this->_convertToTinebaseDateTime($property);
                 // convert recurrence id to utc
                 $event->recurid->setTimezone('UTC');
                 break;
             case 'RRULE':
                 $event->rrule = $property->value;
                 // convert date format
                 $event->rrule = preg_replace_callback('/UNTIL=([\\dTZ]+)(?=;?)/', function ($matches) {
                     if (strlen($matches[1]) < 10) {
                         $dtUntil = date_create($matches[1], new DateTimeZone((string) Tinebase_Core::get(Tinebase_Core::USERTIMEZONE)));
                         $dtUntil->setTimezone(new DateTimeZone('UTC'));
                     } else {
                         $dtUntil = date_create($matches[1]);
                     }
                     return 'UNTIL=' . $dtUntil->format(Tinebase_Record_Abstract::ISO8601LONG);
                 }, $event->rrule);
                 // remove additional days from BYMONTHDAY property
                 $event->rrule = preg_replace('/(BYMONTHDAY=)([\\d]+)([,\\d]+)/', '$1$2', $event->rrule);
                 // process exceptions
                 if (isset($_vevent->EXDATE)) {
                     $exdates = new Tinebase_Record_RecordSet('Calendar_Model_Event');
                     foreach ($_vevent->EXDATE as $exdate) {
                         foreach ($exdate->getDateTimes() as $exception) {
                             if (isset($exdate['VALUE']) && strtoupper($exdate['VALUE']) == 'DATE') {
                                 $recurid = new Tinebase_DateTime($exception->format(Tinebase_Record_Abstract::ISO8601LONG), (string) Tinebase_Core::get(Tinebase_Core::USERTIMEZONE));
                             } else {
                                 $recurid = new Tinebase_DateTime($exception->format(Tinebase_Record_Abstract::ISO8601LONG), $exception->getTimezone());
                             }
                             $recurid->setTimezone(new DateTimeZone('UTC'));
                             $eventException = new Calendar_Model_Event(array('recurid' => $recurid, 'is_deleted' => true));
                             $exdates->addRecord($eventException);
                         }
                     }
                     $event->exdate = $exdates;
                 }
                 break;
             case 'TRANSP':
                 if (in_array($property->value, array(Calendar_Model_Event::TRANSP_OPAQUE, Calendar_Model_Event::TRANSP_TRANSP))) {
                     $event->transp = $property->value;
                 } else {
                     $event->transp = Calendar_Model_Event::TRANSP_TRANSP;
                 }
                 break;
             case 'UID':
                 // it's not possible to change the uid by spec
                 if (!empty($event->uid)) {
                     continue;
                 }
                 $event->uid = $property->value;
                 break;
             case 'VALARM':
                 foreach ($property as $valarm) {
                     switch (strtoupper($valarm->TRIGGER['VALUE']->value)) {
                         # TRIGGER;VALUE=DATE-TIME:20111031T130000Z
                         case 'DATE-TIME':
                             //@TODO fixme
                             $alarmTime = new Tinebase_DateTime($valarm->TRIGGER->value);
                             $alarmTime->setTimezone('UTC');
                             $alarm = new Tinebase_Model_Alarm(array('alarm_time' => $alarmTime, 'minutes_before' => 'custom', 'model' => 'Calendar_Model_Event'));
                             $event->alarms->addRecord($alarm);
                             break;
                             # TRIGGER;VALUE=DURATION:-PT1H15M
                         # TRIGGER;VALUE=DURATION:-PT1H15M
                         case 'DURATION':
                         default:
                             $alarmTime = $this->_convertToTinebaseDateTime($_vevent->DTSTART);
                             $alarmTime->setTimezone('UTC');
                             preg_match('/(?P<invert>[+-]?)(?P<spec>P.*)/', $valarm->TRIGGER->value, $matches);
                             $duration = new DateInterval($matches['spec']);
                             $duration->invert = !!($matches['invert'] === '-');
                             $alarm = new Tinebase_Model_Alarm(array('alarm_time' => $alarmTime->add($duration), 'minutes_before' => $duration->format('%d') * 60 * 24 + $duration->format('%h') * 60 + $duration->format('%i'), 'model' => 'Calendar_Model_Event'));
                             $event->alarms->addRecord($alarm);
                             break;
                     }
                 }
                 break;
             case 'CATEGORIES':
                 // @todo handle categories
                 break;
             case 'X-MOZ-LASTACK':
                 $lastAck = $this->_convertToTinebaseDateTime($property);
                 break;
             case 'X-MOZ-SNOOZE-TIME':
                 $snoozeTime = $this->_convertToTinebaseDateTime($property);
                 break;
             default:
                 break;
         }
     }
     // merge old and new attendees
     Calendar_Model_Attender::emailsToAttendee($event, $newAttendees);
     if (($ownAttendee = Calendar_Model_Attender::getOwnAttender($event->attendee)) !== null) {
         if (isset($lastAck)) {
             $ownAttendee->alarm_ack_time = $lastAck;
         }
         if (isset($snoozeTime)) {
             $ownAttendee->alarm_snooze_time = $snoozeTime;
         }
     }
     if (empty($event->seq)) {
         $event->seq = 0;
     }
     if (empty($event->class)) {
         $event->class = Calendar_Model_Event::CLASS_PUBLIC;
     }
     // convert all datetime fields to UTC
     $event->setTimezone('UTC');
 }
 /**
  * testLongEmailAddress
  * 
  * @see 0009066: iCal .ics file import fails
  * @see 0011000: Cannot accept invitation to meeting when organiser email is too long
  */
 public function testLongEmailAddress()
 {
     $attenderData = array('email' => '5:an_Opportunities/Conferenceroom/CMT_Marsta@corpmx01.external.atlascopco.com', 'n_family' => 'Opportunities/Conferenceroom/CMT', 'n_given' => '5:an');
     $contact = Calendar_Model_Attender::resolveEmailToContact($attenderData);
     $this->assertEquals(strtolower($attenderData['email']), $contact->email);
 }
 /**
  * testRemoveAutogeneratedContacts
  * 
  * @see 0010257: add cli function for deleting autogenerated contacts
  */
 public function testRemoveAutogeneratedContacts()
 {
     if (!Tinebase_Application::getInstance()->isInstalled('Calendar')) {
         $this->markTestSkipped('only works with Calendar app');
     }
     $attenderEmail = '*****@*****.**';
     $attenderData = array('email' => $attenderEmail);
     Calendar_Model_Attender::resolveEmailToContact($attenderData);
     $opts = new Zend_Console_Getopt('abp:');
     $opts->setArguments(array());
     $this->_cli->removeAutogeneratedContacts($opts);
     $filter = new Addressbook_Model_ContactFilter(array(array('field' => 'email', 'operator' => 'equals', 'value' => $attenderEmail)));
     $result = Addressbook_Controller_Contact::getInstance()->search($filter);
     $this->assertEquals(0, count($result), 'should not find autogenerated contact any more: ' . print_r($result->toArray(), true));
 }
 /**
  * parse VEVENT part of VCALENDAR
  * 
  * @param  \Sabre\VObject\Component\VEvent  $vevent  the VEVENT to parse
  * @param  Calendar_Model_Event             $event   the Tine 2.0 event to update
  * @param  array                            $options
  */
 protected function _convertVevent(\Sabre\VObject\Component\VEvent $vevent, Calendar_Model_Event $event, $options)
 {
     if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) {
         Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' vevent ' . $vevent->serialize());
     }
     $newAttendees = array();
     $attachments = new Tinebase_Record_RecordSet('Tinebase_Model_Tree_Node');
     $event->alarms = new Tinebase_Record_RecordSet('Tinebase_Model_Alarm');
     $skipFieldsIfOnlyBasicData = array('ATTENDEE', 'UID', 'ORGANIZER', 'VALARM', 'ATTACH', 'CATEGORIES');
     foreach ($vevent->children() as $property) {
         if (isset($this->_options['onlyBasicData']) && $this->_options['onlyBasicData'] && in_array((string) $property->name, $skipFieldsIfOnlyBasicData)) {
             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) {
                 Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Skipping ' . $property->name . ' (using option onlyBasicData)');
             }
             continue;
         }
         switch ($property->name) {
             case 'CREATED':
             case 'DTSTAMP':
                 if (!isset($options[self::OPTION_USE_SERVER_MODLOG]) || $options[self::OPTION_USE_SERVER_MODLOG] !== true) {
                     $event->{$property->name == 'CREATED' ? 'creation_time' : 'last_modified_time'} = $this->_convertToTinebaseDateTime($property);
                 }
                 break;
             case 'LAST-MODIFIED':
                 $event->last_modified_time = new Tinebase_DateTime($property->getValue());
                 break;
             case 'ATTENDEE':
                 $newAttendee = $this->_getAttendee($property);
                 if ($newAttendee) {
                     $newAttendees[] = $newAttendee;
                 }
                 break;
             case 'CLASS':
                 if (in_array($property->getValue(), array(Calendar_Model_Event::CLASS_PRIVATE, Calendar_Model_Event::CLASS_PUBLIC))) {
                     $event->class = $property->getValue();
                 } else {
                     $event->class = Calendar_Model_Event::CLASS_PUBLIC;
                 }
                 break;
             case 'STATUS':
                 if (in_array($property->getValue(), array(Calendar_Model_Event::STATUS_CONFIRMED, Calendar_Model_Event::STATUS_TENTATIVE, Calendar_Model_Event::STATUS_CANCELED))) {
                     $event->status = $property->getValue();
                 } else {
                     if ($property->getValue() == 'CANCELLED') {
                         $event->status = Calendar_Model_Event::STATUS_CANCELED;
                     } else {
                         $event->status = Calendar_Model_Event::STATUS_CONFIRMED;
                     }
                 }
                 break;
             case 'DTEND':
                 if (isset($property['VALUE']) && strtoupper($property['VALUE']) == 'DATE') {
                     // all day event
                     $event->is_all_day_event = true;
                     $dtend = $this->_convertToTinebaseDateTime($property, TRUE);
                     // whole day events ends at 23:59:59 in Tine 2.0 but 00:00 the next day in vcalendar
                     $dtend->subSecond(1);
                 } else {
                     $event->is_all_day_event = false;
                     $dtend = $this->_convertToTinebaseDateTime($property);
                 }
                 $event->dtend = $dtend;
                 break;
             case 'DTSTART':
                 if (isset($property['VALUE']) && strtoupper($property['VALUE']) == 'DATE') {
                     // all day event
                     $event->is_all_day_event = true;
                     $dtstart = $this->_convertToTinebaseDateTime($property, TRUE);
                 } else {
                     $event->is_all_day_event = false;
                     $dtstart = $this->_convertToTinebaseDateTime($property);
                 }
                 $event->originator_tz = $dtstart->getTimezone()->getName();
                 $event->dtstart = $dtstart;
                 break;
             case 'SEQUENCE':
                 if (!isset($options[self::OPTION_USE_SERVER_MODLOG]) || $options[self::OPTION_USE_SERVER_MODLOG] !== true) {
                     $event->seq = $property->getValue();
                 }
                 // iMIP only
                 $event->external_seq = $property->getValue();
                 break;
             case 'DESCRIPTION':
             case 'LOCATION':
             case 'SUMMARY':
                 $key = strtolower($property->name);
                 $value = $property->getValue();
                 $event->{$key} = $value;
                 break;
             case 'ORGANIZER':
                 $email = null;
                 if (!empty($property['EMAIL'])) {
                     $email = $property['EMAIL'];
                 } elseif (preg_match('/mailto:(?P<email>.*)/i', $property->getValue(), $matches)) {
                     $email = $matches['email'];
                 }
                 if ($email !== null) {
                     // it's not possible to change the organizer by spec
                     if (empty($event->organizer)) {
                         $name = isset($property['CN']) ? $property['CN']->getValue() : $email;
                         $contact = Calendar_Model_Attender::resolveEmailToContact(array('email' => $email, 'lastName' => $name));
                         $event->organizer = $contact->getId();
                     }
                     // Lightning attaches organizer ATTENDEE properties to ORGANIZER property and does not add an ATTENDEE for the organizer
                     if (isset($property['PARTSTAT'])) {
                         $newAttendees[] = $this->_getAttendee($property);
                     }
                 }
                 break;
             case 'RECURRENCE-ID':
                 // original start of the event
                 $event->recurid = $this->_convertToTinebaseDateTime($property);
                 // convert recurrence id to utc
                 $event->recurid->setTimezone('UTC');
                 break;
             case 'RRULE':
                 $rruleString = $property->getValue();
                 // convert date format
                 $rruleString = preg_replace_callback('/UNTIL=([\\dTZ]+)(?=;?)/', function ($matches) {
                     $dtUntil = Calendar_Convert_Event_VCalendar_Abstract::getUTCDateFromStringInUsertime($matches[1]);
                     return 'UNTIL=' . $dtUntil->format(Tinebase_Record_Abstract::ISO8601LONG);
                 }, $rruleString);
                 // remove additional days from BYMONTHDAY property (BYMONTHDAY=11,15 => BYMONTHDAY=11)
                 $rruleString = preg_replace('/(BYMONTHDAY=)([\\d]+),([,\\d]+)/', '$1$2', $rruleString);
                 $event->rrule = $rruleString;
                 // process exceptions
                 if (isset($vevent->EXDATE)) {
                     $exdates = new Tinebase_Record_RecordSet('Calendar_Model_Event');
                     foreach ($vevent->EXDATE as $exdate) {
                         foreach ($exdate->getDateTimes() as $exception) {
                             if (isset($exdate['VALUE']) && strtoupper($exdate['VALUE']) == 'DATE') {
                                 $recurid = new Tinebase_DateTime($exception->format(Tinebase_Record_Abstract::ISO8601LONG), (string) Tinebase_Core::getUserTimezone());
                             } else {
                                 $recurid = new Tinebase_DateTime($exception->format(Tinebase_Record_Abstract::ISO8601LONG), $exception->getTimezone());
                             }
                             $recurid->setTimezone(new DateTimeZone('UTC'));
                             $eventException = new Calendar_Model_Event(array('recurid' => $recurid, 'is_deleted' => true));
                             $exdates->addRecord($eventException);
                         }
                     }
                     $event->exdate = $exdates;
                 }
                 break;
             case 'TRANSP':
                 if (in_array($property->getValue(), array(Calendar_Model_Event::TRANSP_OPAQUE, Calendar_Model_Event::TRANSP_TRANSP))) {
                     $event->transp = $property->getValue();
                 } else {
                     $event->transp = Calendar_Model_Event::TRANSP_TRANSP;
                 }
                 break;
             case 'UID':
                 // it's not possible to change the uid by spec
                 if (!empty($event->uid)) {
                     continue;
                 }
                 $event->uid = $property->getValue();
                 break;
             case 'VALARM':
                 $this->_parseAlarm($event, $property, $vevent);
                 break;
             case 'CATEGORIES':
                 $tags = Tinebase_Model_Tag::resolveTagNameToTag($property->getParts(), 'Calendar');
                 if (!isset($event->tags)) {
                     $event->tags = $tags;
                 } else {
                     $event->tags->merge($tags);
                 }
                 break;
             case 'ATTACH':
                 $name = (string) $property['FILENAME'];
                 $managedId = (string) $property['MANAGED-ID'];
                 $value = (string) $property['VALUE'];
                 $attachment = NULL;
                 $readFromURL = false;
                 $url = '';
                 if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) {
                     Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' attachment found: ' . $name . ' ' . $managedId);
                 }
                 if ($managedId) {
                     $attachment = $event->attachments instanceof Tinebase_Record_RecordSet ? $event->attachments->filter('hash', $property['MANAGED-ID'])->getFirstRecord() : NULL;
                     // NOTE: we might miss a attachment here for the following reasons
                     //       1. client reuses a managed id (we are server):
                     //          We havn't observerd this yet. iCal client reuse manged id's
                     //          from base events in exceptions but this is covered as we
                     //          initialize new exceptions with base event attachments
                     //
                     //          When a client reuses a managed id it's not clear yet if
                     //          this managed id needs to be in the same series/calendar/server
                     //
                     //          As we use the object hash the managed id might be used in the
                     //          same files with different names. We need to evaluate the name
                     //          (if attached) in this case as well.
                     //
                     //       2. server send his managed id (we are client)
                     //          * we need to download the attachment (here?)
                     //          * we need to have a mapping externalid / internalid (where?)
                     if (!$attachment) {
                         $readFromURL = true;
                         $url = $property->getValue();
                     } else {
                         $attachments->addRecord($attachment);
                     }
                 } elseif ('URI' === $value) {
                     /*
                     * ATTACH;VALUE=URI:https://server.com/calendars/__uids__/0AA0
                      3A3B-F7B6-459A-AB3E-4726E53637D0/dropbox/4971F93F-8657-412B-841A-A0FD913
                      9CD61.dropbox/Canada.png
                     */
                     $url = $property->getValue();
                     $urlParts = parse_url($url);
                     $host = $urlParts['host'];
                     $name = pathinfo($urlParts['path'], PATHINFO_BASENAME);
                     // iCal 10.7 places URI before uploading
                     if (parse_url(Tinebase_Core::getHostname(), PHP_URL_HOST) != $host) {
                         $readFromURL = true;
                     }
                 } else {
                     // @TODO: implement (check if add / update / update is needed)
                     if (Tinebase_Core::isLogLevel(Zend_Log::WARN)) {
                         Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' attachment found that could not be imported due to missing managed id');
                     }
                 }
                 if ($readFromURL) {
                     if (preg_match('#^(https?://)(.*)$#', str_replace(array("\n", "\r"), '', $url), $matches)) {
                         // we are client and found an external hosted attachment that we need to import
                         $userCredentialCache = Tinebase_Core::getUserCredentialCache();
                         $url = $matches[1] . $userCredentialCache->username . ':' . $userCredentialCache->password . '@' . $matches[2];
                         $attachmentInfo = $matches[1] . $matches[2] . ' ' . $name . ' ' . $managedId;
                         if (Tinebase_Helper::urlExists($url)) {
                             if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) {
                                 Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Downloading attachment: ' . $attachmentInfo);
                             }
                             $stream = fopen($url, 'r');
                             $attachment = new Tinebase_Model_Tree_Node(array('name' => rawurldecode($name), 'type' => Tinebase_Model_Tree_Node::TYPE_FILE, 'contenttype' => (string) $property['FMTTYPE'], 'tempFile' => $stream), true);
                             $attachments->addRecord($attachment);
                         } else {
                             if (Tinebase_Core::isLogLevel(Zend_Log::WARN)) {
                                 Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' Url not found (got 404): ' . $attachmentInfo . ' - Skipping attachment');
                             }
                         }
                     } else {
                         if (Tinebase_Core::isLogLevel(Zend_Log::WARN)) {
                             Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' Attachment found with malformed url: ' . $url);
                         }
                     }
                 }
                 break;
             case 'X-MOZ-LASTACK':
                 $lastAck = $this->_convertToTinebaseDateTime($property);
                 break;
             case 'X-MOZ-SNOOZE-TIME':
                 $snoozeTime = $this->_convertToTinebaseDateTime($property);
                 break;
             default:
                 // thunderbird saves snooze time for recurring event occurrences in properties with names like this -
                 // we just assume that the event/recur series has only one snooze time
                 if (preg_match('/^X-MOZ-SNOOZE-TIME-[0-9]+$/', $property->name)) {
                     $snoozeTime = $this->_convertToTinebaseDateTime($property);
                     if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) {
                         Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Found snooze time for recur occurrence: ' . $snoozeTime->toString());
                     }
                 }
                 break;
         }
     }
     // NOTE: X-CALENDARSERVER-ACCESS overwrites CLASS
     if (isset($vevent->{'X-CALENDARSERVER-ACCESS'})) {
         $event->class = $vevent->{'X-CALENDARSERVER-ACCESS'} == 'PUBLIC' ? Calendar_Model_Event::CLASS_PUBLIC : Calendar_Model_Event::CLASS_PRIVATE;
     }
     if (isset($lastAck)) {
         Calendar_Controller_Alarm::setAcknowledgeTime($event->alarms, $lastAck);
     }
     if (isset($snoozeTime)) {
         Calendar_Controller_Alarm::setSnoozeTime($event->alarms, $snoozeTime);
     }
     // merge old and new attendee
     Calendar_Model_Attender::emailsToAttendee($event, $newAttendees);
     if (empty($event->seq)) {
         $event->seq = 1;
     }
     if (empty($event->class)) {
         $event->class = Calendar_Model_Event::CLASS_PUBLIC;
     }
     $this->_manageAttachmentsFromClient($event, $attachments);
     if (empty($event->dtend)) {
         if (empty($event->dtstart)) {
             throw new Tinebase_Exception_UnexpectedValue("Got event without dtstart and dtend");
         }
         // TODO find out duration (see TRIGGER DURATION)
         //             if (isset($vevent->DURATION)) {
         //             }
         if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) {
             Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ . ' Got event without dtend. Assuming 30 minutes duration');
         }
         $event->dtend = clone $event->dtstart;
         $event->dtend->addMinute(30);
     }
     $this->_manageAttachmentsFromClient($event, $attachments);
     // convert all datetime fields to UTC
     $event->setTimezone('UTC');
 }