Author: Michael J Rubinsky (mrubinsk@horde.org)
Inheritance: extends Horde_ActiveSync_Message_Appointment
Example #1
0
 public function testEncodingSimpleExceptions()
 {
     $this->markTestSkipped('Needs updated fixture.');
     $l = new Horde_Test_Log();
     $logger = $l->getLogger();
     //$logger = new Horde_Log_Logger(new Horde_Log_Handler_Stream(fopen('/tmp/test.log', 'a')));
     // Every other week recurrence, on thursday, no end.
     $r = new Horde_Date_Recurrence('2011-12-01T15:00:00');
     $r->setRecurType(Horde_Date_Recurrence::RECUR_WEEKLY);
     $r->setRecurInterval(2);
     $r->setRecurOnDay(Horde_Date::MASK_THURSDAY);
     $r->addException(2011, 12, 29);
     $e = new Horde_ActiveSync_Message_Exception();
     $d = new Horde_Date('2011-12-29T15:00:00');
     $e->setExceptionStartTime($d);
     $e->deleted = true;
     $appt = new Horde_ActiveSync_Message_Appointment(array('logger' => $logger));
     $appt->setSubject('Event Title');
     $appt->setBody('Event Description');
     $appt->setLocation('Philadelphia, PA');
     $start = new Horde_Date('2011-12-01T15:00:00');
     $appt->setDatetime(array('start' => $start, 'end' => new Horde_Date('2011-12-01T16:00:00'), 'allday' => false));
     $appt->setTimezone($start);
     $appt->setSensitivity(Horde_ActiveSync_Message_Appointment::SENSITIVITY_PERSONAL);
     $appt->setBusyStatus(Horde_ActiveSync_Message_Appointment::BUSYSTATUS_BUSY);
     $appt->setDTStamp($start->timestamp());
     $appt->setRecurrence($r);
     $appt->addException($e);
     $stream = fopen('php://memory', 'w+');
     $encoder = new Horde_ActiveSync_Wbxml_Encoder($stream);
     $encoder->setLogger($logger);
     $encoder->startTag(Horde_ActiveSync::SYNC_DATA);
     $appt->encodeStream($encoder);
     $encoder->endTag();
     $fixture = file_get_contents(__DIR__ . '/fixtures/simpleexception.wbxml');
     rewind($stream);
     $results = stream_get_contents($stream);
     fclose($stream);
     $this->assertEquals($fixture, $results);
 }
Example #2
0
 /**
  * Export this event as a MS ActiveSync Message
  *
  * @param array $options  Options:
  *   - protocolversion: (float)  The EAS version to support
  *                      DEFAULT: 2.5
  *   - bodyprefs: (array)  A BODYPREFERENCE array.
  *                DEFAULT: none (No body prefs enforced).
  *   - truncation: (integer)  Truncate event body to this length
  *                 DEFAULT: none (No truncation).
  *
  * @return Horde_ActiveSync_Message_Appointment
  */
 public function toASAppointment(array $options = array())
 {
     global $prefs, $registry;
     $message = new Horde_ActiveSync_Message_Appointment(array('logger' => $GLOBALS['injector']->getInstance('Horde_Log_Logger'), 'protocolversion' => $options['protocolversion']));
     if (!$this->isPrivate()) {
         // Handle body/truncation
         if (!empty($options['bodyprefs'])) {
             if (Horde_String::length($this->description) > 0) {
                 $bp = $options['bodyprefs'];
                 $note = new Horde_ActiveSync_Message_AirSyncBaseBody();
                 // No HTML supported. Always use plaintext.
                 $note->type = Horde_ActiveSync::BODYPREF_TYPE_PLAIN;
                 if (isset($bp[Horde_ActiveSync::BODYPREF_TYPE_PLAIN]['truncationsize'])) {
                     $truncation = $bp[Horde_ActiveSync::BODYPREF_TYPE_PLAIN]['truncationsize'];
                 } elseif (isset($bp[Horde_ActiveSync::BODYPREF_TYPE_HTML])) {
                     $truncation = $bp[Horde_ActiveSync::BODYPREF_TYPE_HTML]['truncationsize'];
                     $this->description = Horde_Text_Filter::filter($this->description, 'Text2html', array('parselevel' => Horde_Text_Filter_Text2html::MICRO));
                 } else {
                     $truncation = false;
                 }
                 if ($truncation && Horde_String::length($this->description) > $truncation) {
                     $note->data = Horde_String::substr($this->desciption, 0, $truncation);
                     $note->truncated = 1;
                 } else {
                     $note->data = $this->description;
                 }
                 $note->estimateddatasize = Horde_String::length($this->description);
                 $message->airsyncbasebody = $note;
             }
         } else {
             $message->setBody($this->description);
         }
         $message->setLocation($this->location);
     }
     $message->setSubject($this->getTitle());
     $message->setDatetime(array('start' => $this->start, 'end' => $this->end, 'allday' => $this->isAllDay()));
     $message->setTimezone($this->start);
     // Organizer
     if (count($this->attendees)) {
         if ($this->creator == $registry->getAuth()) {
             $as_ident = $prefs->getValue('activesync_identity') == 'horde' ? $prefs->getValue('default_identity') : $prefs->getValue('activesync_identity');
             $name = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Identity')->create($this->creator)->getValue('fullname', $as_ident);
             $email = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Identity')->create($this->creator)->getValue('from_addr', $as_ident);
         } else {
             $name = Kronolith::getUserName($this->creator);
             $email = Kronolith::getUserEmail($this->creator);
         }
         $message->setOrganizer(array('name' => $name, 'email' => $email));
     }
     // Privacy
     $message->setSensitivity($this->private ? Horde_ActiveSync_Message_Appointment::SENSITIVITY_PRIVATE : Horde_ActiveSync_Message_Appointment::SENSITIVITY_NORMAL);
     // Busy Status
     switch ($this->status) {
         case Kronolith::STATUS_CANCELLED:
             $status = Horde_ActiveSync_Message_Appointment::BUSYSTATUS_FREE;
             break;
         case Kronolith::STATUS_CONFIRMED:
             $status = Horde_ActiveSync_Message_Appointment::BUSYSTATUS_BUSY;
             break;
         case Kronolith::STATUS_TENTATIVE:
             $status = Horde_ActiveSync_Message_Appointment::BUSYSTATUS_TENTATIVE;
         case Kronolith::STATUS_FREE:
         case Kronolith::STATUS_NONE:
             $status = Horde_ActiveSync_Message_Appointment::BUSYSTATUS_FREE;
     }
     $message->setBusyStatus($status);
     // DTStamp
     $message->setDTStamp($_SERVER['REQUEST_TIME']);
     // Recurrence
     if ($this->recurs()) {
         $message->setRecurrence($this->recurrence, $GLOBALS['prefs']->getValue('week_start_monday'));
         /* Exceptions are tricky. Exceptions, even those that represent
          * deleted instances of a recurring event, must be added. To do this
          * we query the storage for all the events that represent exceptions
          * (those with the baseid == $this->uid) and then remove the
          * exceptionoriginaldate from the list of exceptions we know about.
          * Any dates left in this list when we are done, must represent
          * deleted instances of this recurring event.*/
         if (!empty($this->recurrence) && ($exceptions = $this->recurrence->getExceptions())) {
             $results = $this->boundExceptions();
             foreach ($results as $exception) {
                 $e = new Horde_ActiveSync_Message_Exception(array('protocolversion' => $options['protocolversion']));
                 $e->setDateTime(array('start' => $exception->start, 'end' => $exception->end, 'allday' => $exception->isAllDay()));
                 // The start time of the *original* recurring event
                 $e->setExceptionStartTime($exception->exceptionoriginaldate);
                 $originaldate = $exception->exceptionoriginaldate->format('Ymd');
                 $key = array_search($originaldate, $exceptions);
                 if ($key !== false) {
                     unset($exceptions[$key]);
                 }
                 // Remaining properties that could be different
                 $e->setSubject($exception->getTitle());
                 if (!$exception->isPrivate()) {
                     $e->setLocation($exception->location);
                     $e->setBody($exception->description);
                 }
                 $e->setSensitivity($exception->private ? Horde_ActiveSync_Message_Appointment::SENSITIVITY_PRIVATE : Horde_ActiveSync_Message_Appointment::SENSITIVITY_NORMAL);
                 $e->setReminder($exception->alarm);
                 $e->setDTStamp($_SERVER['REQUEST_TIME']);
                 if ($options['protocolversion'] > Horde_ActiveSync::VERSION_TWELVEONE) {
                     switch ($exception->status) {
                         case Kronolith::STATUS_TENTATIVE:
                             $e->responsetype = Horde_ActiveSync_Message_Appointment::RESPONSE_TENTATIVE;
                             break;
                         case Kronolith::STATUS_NONE:
                             $e->responsetype = Horde_ActiveSync_Message_Appointment::RESPONSE_NORESPONSE;
                             break;
                         case Kronolith::STATUS_CONFIRMED:
                             $e->responsetype = Horde_ActiveSync_Message_Appointment::RESPONSE_ACCEPTED;
                             break;
                         default:
                             $e->responsetype = Horde_ActiveSync_Message_Appointment::RESPONSE_NONE;
                     }
                 }
                 // Tags/Categories
                 if (!$exception->isPrivate()) {
                     foreach ($exception->tags as $tag) {
                         $e->addCategory($tag);
                     }
                 }
                 $message->addexception($e);
             }
             // Any dates left in $exceptions must be deleted exceptions
             foreach ($exceptions as $deleted) {
                 $e = new Horde_ActiveSync_Message_Exception(array('protocolversion' => $options['protocolversion']));
                 // Kronolith stores the date only, but some AS clients need
                 // the datetime.
                 list($year, $month, $mday) = sscanf($deleted, '%04d%02d%02d');
                 $st = clone $this->start;
                 $st->year = $year;
                 $st->month = $month;
                 $st->mday = $mday;
                 $e->setExceptionStartTime($st);
                 $e->deleted = true;
                 $message->addException($e);
             }
         }
     }
     // Attendees
     if (!$this->isPrivate() && count($this->attendees)) {
         $message->setMeetingStatus(Horde_ActiveSync_Message_Appointment::MEETING_IS_MEETING);
         foreach ($this->attendees as $email => $properties) {
             $attendee = new Horde_ActiveSync_Message_Attendee(array('protocolversion' => $options['protocolversion']));
             $adr_obj = new Horde_Mail_Rfc822_Address($email);
             $attendee->name = $adr_obj->label;
             $attendee->email = $adr_obj->bare_address;
             // AS only has required or optional, and only EAS Version > 2.5
             if ($options['protocolversion'] > Horde_ActiveSync::VERSION_TWOFIVE) {
                 $attendee->type = $properties['attendance'] !== Kronolith::PART_REQUIRED ? Horde_ActiveSync_Message_Attendee::TYPE_OPTIONAL : Horde_ActiveSync_Message_Attendee::TYPE_REQUIRED;
                 switch ($properties['response']) {
                     case Kronolith::RESPONSE_NONE:
                         $attendee->status = Horde_ActiveSync_Message_Attendee::STATUS_NORESPONSE;
                         break;
                     case Kronolith::RESPONSE_ACCEPTED:
                         $attendee->status = Horde_ActiveSync_Message_Attendee::STATUS_ACCEPT;
                         break;
                     case Kronolith::RESPONSE_DECLINED:
                         $attendee->status = Horde_ActiveSync_Message_Attendee::STATUS_DECLINE;
                         break;
                     case Kronolith::RESPONSE_TENTATIVE:
                         $attendee->status = Horde_ActiveSync_Message_Attendee::STATUS_TENTATIVE;
                         break;
                     default:
                         $attendee->status = Horde_ActiveSync_Message_Attendee::STATUS_UNKNOWN;
                 }
             }
             $message->addAttendee($attendee);
         }
     } else {
         $message->setMeetingStatus(Horde_ActiveSync_Message_Appointment::MEETING_NOT_MEETING);
     }
     // Resources
     if ($options['protocolversion'] > Horde_ActiveSync::VERSION_TWOFIVE) {
         $r = $this->getResources();
         foreach ($r as $id => $data) {
             $resource = Kronolith::getDriver('Resource')->getResource($id);
             // EAS *REQUIRES* an email field for Resources. If it is missing
             // a number of clients will fail, losing push.
             if ($resource->get('email')) {
                 $attendee = new Horde_ActiveSync_Message_Attendee(array('protocolversion' => $options['protocolversion']));
                 $attendee->email = $resource->get('email');
                 $attendee->type = Horde_ActiveSync_Message_Attendee::TYPE_RESOURCE;
                 $attendee->name = $data['name'];
                 $attendee->status = $data['response'];
                 $message->addAttendee($attendee);
             }
         }
     }
     // Reminder
     if ($this->alarm) {
         $message->setReminder($this->alarm);
     }
     // Categories (tags)
     if (!$this->isPrivate()) {
         foreach ($this->tags as $tag) {
             $message->addCategory($tag);
         }
     }
     // EAS 14
     if ($options['protocolversion'] > Horde_ActiveSync::VERSION_TWELVEONE) {
         // We don't track the actual responses we sent to other's invitations.
         // Set this based on the status flag.
         switch ($this->status) {
             case Kronolith::STATUS_TENTATIVE:
                 $message->responsetype = Horde_ActiveSync_Message_Appointment::RESPONSE_TENTATIVE;
                 break;
             case Kronolith::STATUS_NONE:
                 $message->responsetype = Horde_ActiveSync_Message_Appointment::RESPONSE_NORESPONSE;
                 break;
             case Kronolith::STATUS_CONFIRMED:
                 $message->responsetype = Horde_ActiveSync_Message_Appointment::RESPONSE_ACCEPTED;
                 break;
             default:
                 $message->responsetype = Horde_ActiveSync_Message_Appointment::RESPONSE_NONE;
         }
     }
     // 14.1
     if ($options['protocolversion'] >= Horde_ActiveSync::VERSION_FOURTEENONE) {
         $message->onlinemeetingexternallink = $this->url;
     }
     return $message;
 }
Example #3
0
 /**
  * Export this event as a MS ActiveSync Message
  *
  * @param array $options  Options:
  *   - protocolversion: (float)  The EAS version to support
  *                      DEFAULT: 2.5
  *   - bodyprefs: (array)  A BODYPREFERENCE array.
  *                DEFAULT: none (No body prefs enforced).
  *   - truncation: (integer)  Truncate event body to this length
  *                 DEFAULT: none (No truncation).
  *
  * @return Horde_ActiveSync_Message_Appointment
  */
 public function toASAppointment(array $options = array())
 {
     global $prefs, $registry;
     // @todo This should be a required option.
     if (empty($options['protocolversion'])) {
         $options['protocolversion'] = 2.5;
     }
     $message = new Horde_ActiveSync_Message_Appointment(array('logger' => $GLOBALS['injector']->getInstance('Horde_Log_Logger'), 'protocolversion' => $options['protocolversion']));
     if (!$this->isPrivate()) {
         // Handle body/truncation
         if (!empty($options['bodyprefs'])) {
             if (Horde_String::length($this->description) > 0) {
                 $bp = $options['bodyprefs'];
                 $note = new Horde_ActiveSync_Message_AirSyncBaseBody();
                 // No HTML supported. Always use plaintext.
                 $note->type = Horde_ActiveSync::BODYPREF_TYPE_PLAIN;
                 if (isset($bp[Horde_ActiveSync::BODYPREF_TYPE_PLAIN]['truncationsize'])) {
                     $truncation = $bp[Horde_ActiveSync::BODYPREF_TYPE_PLAIN]['truncationsize'];
                 } elseif (isset($bp[Horde_ActiveSync::BODYPREF_TYPE_HTML])) {
                     $truncation = $bp[Horde_ActiveSync::BODYPREF_TYPE_HTML]['truncationsize'];
                     $this->description = Horde_Text_Filter::filter($this->description, 'Text2html', array('parselevel' => Horde_Text_Filter_Text2html::MICRO));
                 } else {
                     $truncation = false;
                 }
                 if ($truncation && Horde_String::length($this->description) > $truncation) {
                     $note->data = Horde_String::substr($this->description, 0, $truncation);
                     $note->truncated = 1;
                 } else {
                     $note->data = $this->description;
                 }
                 $note->estimateddatasize = Horde_String::length($this->description);
                 $message->airsyncbasebody = $note;
             }
         } else {
             $message->setBody($this->description);
         }
         if ($options['protocolversion'] >= Horde_ActiveSync::VERSION_SIXTEEN && !empty($this->location)) {
             $message->location = new Horde_ActiveSync_Message_AirSyncBaseLocation(array('logger' => $GLOBALS['injector']->getInstance('Horde_Log_Logger'), 'protocolversion' => $options['protocolversion']));
             // @todo - worth it to try to get full city/country etc...
             // from geotagging service if available??
             $message->location->displayname = $this->location;
         } else {
             $message->setLocation($this->location);
         }
     }
     $message->setSubject($this->getTitle());
     $message->alldayevent = $this->isAllDay();
     $st = clone $this->start;
     $et = clone $this->end;
     if ($this->isAllDay()) {
         // EAS requires all day to be from 12:00 to 12:00.
         if ($this->start->hour != 0 || $this->start->min != 0 || $this->start->sec != 0) {
             $st->hour = 0;
             $st->min = 0;
             $st->sec = 0;
         }
         // For end it's a bit trickier. If it's 11:59pm, bump it up to 12:00
         // am of the next day. Otherwise, if it's not 12:00am, make it 12:00
         // am of the same day. This *shouldn't* happen, but protect against
         // issues with EAS just in case.
         if ($this->end->hour != 0 || $this->end->min != 0 || $this->end->sec != 0) {
             if ($this->end->hour == 23 && $this->end->min == 59) {
                 $et->mday++;
             }
             $et->hour = 0;
             $et->min = 0;
             $et->sec = 0;
         }
     }
     $message->starttime = $st;
     $message->endtime = $et;
     $message->setTimezone($this->start);
     // Organizer
     $attendees = $this->attendees;
     $skipOrganizer = null;
     if ($this->organizer) {
         $message->setOrganizer(array('email' => $this->organizer));
     } elseif (count($attendees)) {
         if ($this->creator == $registry->getAuth()) {
             $as_ident = $prefs->getValue('activesync_identity') == 'horde' ? $prefs->getValue('default_identity') : $prefs->getValue('activesync_identity');
             $name = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Identity')->create($this->creator)->getValue('fullname', $as_ident);
             $email = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Identity')->create($this->creator)->getValue('from_addr', $as_ident);
         } else {
             $name = Kronolith::getUserName($this->creator);
             $email = Kronolith::getUserEmail($this->creator);
         }
         $message->setOrganizer(array('name' => $name, 'email' => $email));
         $skipOrganizer = $email;
     }
     // Privacy
     $message->setSensitivity($this->private ? Horde_ActiveSync_Message_Appointment::SENSITIVITY_PRIVATE : Horde_ActiveSync_Message_Appointment::SENSITIVITY_NORMAL);
     // Busy Status
     // This is the *busy* status of the time for this meeting. This is NOT
     // the Kronolith_Event::status or the attendance response for this
     // meeting. Kronolith does not (yet) support sepcifying the busy status
     // of the event time separate from the STATUS_FREE value of the
     // Kronolith_Event::status field, so for now we map these values the
     // best we can by assuming that STATUS_CONFIRMED meetings should always
     // show as BUSYSTATUS_BUSY etc...
     switch ($this->status) {
         case Kronolith::STATUS_CANCELLED:
         case Kronolith::STATUS_FREE:
         case Kronolith::STATUS_NONE:
             $status = Horde_ActiveSync_Message_Appointment::BUSYSTATUS_FREE;
             break;
         case Kronolith::STATUS_CONFIRMED:
             $status = Horde_ActiveSync_Message_Appointment::BUSYSTATUS_BUSY;
             break;
         case Kronolith::STATUS_TENTATIVE:
             $status = Horde_ActiveSync_Message_Appointment::BUSYSTATUS_TENTATIVE;
     }
     $message->setBusyStatus($status);
     // DTStamp
     $message->setDTStamp($_SERVER['REQUEST_TIME']);
     // Recurrence
     if ($this->recurs()) {
         $message->setRecurrence($this->recurrence, $GLOBALS['prefs']->getValue('week_start_monday'));
         /* Exceptions are tricky. Exceptions, even those that represent
          * deleted instances of a recurring event, must be added. To do this
          * we query the storage for all the events that represent exceptions
          * (those with the baseid == $this->uid) and then remove the
          * exceptionoriginaldate from the list of exceptions we know about.
          * Any dates left in this list when we are done, must represent
          * deleted instances of this recurring event.*/
         if (!empty($this->recurrence) && ($exceptions = $this->recurrence->getExceptions())) {
             $results = $this->boundExceptions();
             foreach ($results as $exception) {
                 $e = new Horde_ActiveSync_Message_Exception(array('protocolversion' => $options['protocolversion']));
                 $e->setDateTime(array('start' => $exception->start, 'end' => $exception->end, 'allday' => $exception->isAllDay()));
                 // The start time of the *original* recurring event.
                 // EAS < 16.0 uses 'exceptionstarttime'. Otherwise it's
                 // 'instanceid'.
                 if ($options['protocolversion'] < Horde_ActiveSync::VERSION_SIXTEEN) {
                     $e->setExceptionStartTime($exception->exceptionoriginaldate);
                 } else {
                     $e->instanceid = $exception->exceptionoriginaldate;
                 }
                 $originaldate = $exception->exceptionoriginaldate->format('Ymd');
                 $key = array_search($originaldate, $exceptions);
                 if ($key !== false) {
                     unset($exceptions[$key]);
                 }
                 // Remaining properties that could be different
                 $e->setSubject($exception->getTitle());
                 if (!$exception->isPrivate()) {
                     $e->setLocation($exception->location);
                     $e->setBody($exception->description);
                 }
                 $e->setSensitivity($exception->private ? Horde_ActiveSync_Message_Appointment::SENSITIVITY_PRIVATE : Horde_ActiveSync_Message_Appointment::SENSITIVITY_NORMAL);
                 $e->setReminder($exception->alarm);
                 $e->setDTStamp($_SERVER['REQUEST_TIME']);
                 if ($options['protocolversion'] > Horde_ActiveSync::VERSION_TWELVEONE) {
                     switch ($exception->status) {
                         case Kronolith::STATUS_TENTATIVE:
                             $e->responsetype = Horde_ActiveSync_Message_Appointment::RESPONSE_TENTATIVE;
                             break;
                         case Kronolith::STATUS_NONE:
                             $e->responsetype = Horde_ActiveSync_Message_Appointment::RESPONSE_NORESPONSE;
                             break;
                         case Kronolith::STATUS_CONFIRMED:
                             $e->responsetype = Horde_ActiveSync_Message_Appointment::RESPONSE_ACCEPTED;
                             break;
                         default:
                             $e->responsetype = Horde_ActiveSync_Message_Appointment::RESPONSE_NONE;
                     }
                 }
                 // Tags/Categories
                 if (!$exception->isPrivate()) {
                     foreach ($exception->tags as $tag) {
                         $e->addCategory($tag);
                     }
                 }
                 $message->addexception($e);
             }
             // Any dates left in $exceptions must be deleted exceptions
             foreach ($exceptions as $deleted) {
                 $e = new Horde_ActiveSync_Message_Exception(array('protocolversion' => $options['protocolversion']));
                 // Kronolith stores the date only, but some AS clients need
                 // the datetime.
                 list($year, $month, $mday) = sscanf($deleted, '%04d%02d%02d');
                 $st = clone $this->start;
                 $st->year = $year;
                 $st->month = $month;
                 $st->mday = $mday;
                 if ($options['protocolversion'] < Horde_ActiveSync::VERSION_SIXTEEN) {
                     $e->setExceptionStartTime($st);
                 } else {
                     $e->instanceid = $st;
                 }
                 $e->deleted = true;
                 $message->addException($e);
             }
         }
     }
     // Attendees
     if (!$this->isPrivate() && count($attendees)) {
         $message->setMeetingStatus($this->status == Kronolith::STATUS_CANCELLED ? Horde_ActiveSync_Message_Appointment::MEETING_CANCELLED : Horde_ActiveSync_Message_Appointment::MEETING_IS_MEETING);
         foreach ($attendees as $attendee) {
             if ($skipOrganizer && $attendee->email == $skipOrganizer) {
                 continue;
             }
             $attendeeAS = new Horde_ActiveSync_Message_Attendee(array('protocolversion' => $options['protocolversion']));
             $attendeeAS->name = $attendee->addressObject->label;
             $attendeeAS->email = $attendee->addressObject->bare_address;
             // AS only has required or optional, and only EAS Version > 2.5
             if ($options['protocolversion'] > Horde_ActiveSync::VERSION_TWOFIVE) {
                 $attendeeAS->type = $attendee->role !== Kronolith::PART_REQUIRED ? Horde_ActiveSync_Message_Attendee::TYPE_OPTIONAL : Horde_ActiveSync_Message_Attendee::TYPE_REQUIRED;
                 switch ($attendee->response) {
                     case Kronolith::RESPONSE_NONE:
                         $attendeeAS->status = Horde_ActiveSync_Message_Attendee::STATUS_NORESPONSE;
                         break;
                     case Kronolith::RESPONSE_ACCEPTED:
                         $attendeeAS->status = Horde_ActiveSync_Message_Attendee::STATUS_ACCEPT;
                         break;
                     case Kronolith::RESPONSE_DECLINED:
                         $attendeeAS->status = Horde_ActiveSync_Message_Attendee::STATUS_DECLINE;
                         break;
                     case Kronolith::RESPONSE_TENTATIVE:
                         $attendeeAS->status = Horde_ActiveSync_Message_Attendee::STATUS_TENTATIVE;
                         break;
                     default:
                         $attendeeAS->status = Horde_ActiveSync_Message_Attendee::STATUS_UNKNOWN;
                 }
             }
             $message->addAttendee($attendeeAS);
         }
     } elseif ($this->status == Kronolith::STATUS_CANCELLED) {
         $message->setMeetingStatus(Horde_ActiveSync_Message_Appointment::MEETING_CANCELLED);
     } else {
         $message->setMeetingStatus(Horde_ActiveSync_Message_Appointment::MEETING_NOT_MEETING);
     }
     // Resources
     if ($options['protocolversion'] > Horde_ActiveSync::VERSION_TWOFIVE) {
         $r = $this->getResources();
         foreach ($r as $id => $data) {
             $resource = Kronolith::getDriver('Resource')->getResource($id);
             // EAS *REQUIRES* an email field for Resources. If it is missing
             // a number of clients will fail, losing push.
             if ($resource->get('email')) {
                 $attendeeAS = new Horde_ActiveSync_Message_Attendee(array('protocolversion' => $options['protocolversion']));
                 $attendeeAS->email = $resource->get('email');
                 $attendeeAS->type = Horde_ActiveSync_Message_Attendee::TYPE_RESOURCE;
                 $attendeeAS->name = $data['name'];
                 $attendeeAS->status = $data['response'];
                 $message->addAttendee($attendeeAS);
             }
         }
     }
     // Reminder
     if ($this->alarm) {
         $message->setReminder($this->alarm);
     }
     // Categories (tags)
     if (!$this->isPrivate()) {
         foreach ($this->tags as $tag) {
             $message->addCategory($tag);
         }
     }
     // EAS 14, and only if it is a meeting.
     if ($options['protocolversion'] > Horde_ActiveSync::VERSION_TWELVEONE && $message->getMeetingStatus() == Horde_ActiveSync_Message_Appointment::MEETING_IS_MEETING) {
         // Are we the
         if (empty($this->organizer) && $this->creator == $registry->getAuth()) {
             $message->responsetype = Horde_ActiveSync_Message_Appointment::RESPONSE_ORGANIZER;
         }
         // We don't track the actual responses we sent to other's invitations.
         // Set this based on the status flag.
         switch ($this->status) {
             case Kronolith::STATUS_TENTATIVE:
                 $message->responsetype = Horde_ActiveSync_Message_Appointment::RESPONSE_TENTATIVE;
                 break;
             case Kronolith::STATUS_NONE:
                 $message->responsetype = Horde_ActiveSync_Message_Appointment::RESPONSE_NORESPONSE;
                 break;
             case Kronolith::STATUS_CONFIRMED:
                 $message->responsetype = Horde_ActiveSync_Message_Appointment::RESPONSE_ACCEPTED;
                 break;
             default:
                 $message->responsetype = Horde_ActiveSync_Message_Appointment::RESPONSE_NONE;
         }
     }
     // 14.1
     if ($options['protocolversion'] >= Horde_ActiveSync::VERSION_FOURTEENONE) {
         $message->onlinemeetingexternallink = $this->url;
     }
     // 16.0
     if ($options['protocolversion'] >= Horde_ActiveSync::VERSION_SIXTEEN) {
         $files = $this->listFiles();
         if (count($files)) {
             foreach ($files as $file) {
                 $atc = new Horde_ActiveSync_Message_AirSyncBaseAttachment(array('logger' => $GLOBALS['injector']->getInstance('Horde_Log_Logger'), 'protocolversion' => $options['protocolversion']));
                 $atc->displayname = $file['name'];
                 $atc->attname = $this->_getEASFileReference($file['name']);
                 $atc->attmethod = Horde_ActiveSync_Message_AirSyncBaseAttachment::ATT_TYPE_NORMAL;
                 $atc->attsize = $file['size'];
                 $message->addAttachment($atc);
             }
         }
     }
     return $message;
 }