Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117921514
D4436.1775431535.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
10 KB
Referenced Files
None
Subscribers
None
D4436.1775431535.diff
View Options
diff --git a/lib/kolab_sync_data_calendar.php b/lib/kolab_sync_data_calendar.php
--- a/lib/kolab_sync_data_calendar.php
+++ b/lib/kolab_sync_data_calendar.php
@@ -185,20 +185,9 @@
$config = $this->getFolderConfig($event['_mailbox']);
$result = array();
- // Timezone
// Kolab Format 3.0 and xCal does support timezone per-date, but ActiveSync allows
// only one timezone per-event. We'll use timezone of the start date
- if ($event['start'] instanceof DateTime) {
- $timezone = $event['start']->getTimezone();
-
- if ($timezone && ($tz_name = $timezone->getName()) != 'UTC') {
- $tzc = kolab_sync_timezone_converter::getInstance();
-
- if ($tz_name = $tzc->encodeTimezone($tz_name, $event['start']->format('Y-m-d'))) {
- $result['timezone'] = $tz_name;
- }
- }
- }
+ $result['timezone'] = kolab_sync_timezone_converter::encodeTimezoneFromDate($event['start']);
// Calendar namespace fields
foreach ($this->mapping as $key => $name) {
diff --git a/lib/kolab_sync_data_email.php b/lib/kolab_sync_data_email.php
--- a/lib/kolab_sync_data_email.php
+++ b/lib/kolab_sync_data_email.php
@@ -117,6 +117,48 @@
}
}
+ /**
+ * Encode a globalObjId according to https://interoperability.blob.core.windows.net/files/MS-ASEMAIL/%5bMS-ASEMAIL%5d-150526.pdf 2.2.2.3
+ *
+ * @param array $data An array with the data to encode
+ *
+ * @return string the encoded globalObjId
+ */
+ public static function encodeGlobalObjId(array $data): string
+ {
+ $classid = "040000008200e00074c5b7101a82e008";
+ $uid = $data['uid'];
+ $vcalid = "vCal-Uid\1\0\0\0{$uid}\0";
+
+ $packed = pack(
+ "H32nCCPx8Va*",
+ $classid,
+ $data['year'] ?? 0,
+ $data['month'] ?? 0,
+ $data['day'] ?? 0,
+ $data['now'] ?? 0,
+ strlen($vcalid),
+ $vcalid
+ );
+
+ return base64_encode($packed);
+ }
+
+ /**
+ * Decode a globalObjId according to https://interoperability.blob.core.windows.net/files/MS-ASEMAIL/%5bMS-ASEMAIL%5d-150526.pdf 2.2.2.3
+ *
+ * @param string the encoded globalObjId
+ *
+ * @return array An array with the decoded data
+ */
+ public static function decodeGlobalObjId(string $globalObjId): array
+ {
+ $unpackString = 'H32classid/nyear/Cmonth/Cday/Pnow/x8/Vbytecount/a*data';
+ $decoded = unpack($unpackString, base64_decode($globalObjId));
+ $decoded['uid'] = substr($decoded['data'], strlen("vCal-Uid\1\0\0\0"), -1);
+ return $decoded;
+ }
+
/**
* Creates model object
*
@@ -339,6 +381,9 @@
$result['nativeBodyType'] = $message->has_html_part() ? 2 : 1;
// Message class
+ $result['messageClass'] = 'IPM.Note';
+ $result['contentClass'] = 'urn:content-classes:message';
+
if ($headers->ctype == 'multipart/signed'
&& !empty($message->parts[1])
&& $message->parts[1]->mimetype == 'application/pkcs7-signature'
@@ -348,11 +393,62 @@
else if ($headers->ctype == 'application/pkcs7-mime' || $headers->ctype == 'application/x-pkcs7-mime') {
$result['messageClass'] = 'IPM.Note.SMIME';
}
- else {
- $result['messageClass'] = 'IPM.Note';
- }
+ else if ($headers->ctype == 'multipart/alternative' || $headers->ctype == 'text/calendar') {
+ if ($event = $this->get_invitation_event_from_message($message)) {
+ $result['messageClass'] = 'IPM.Schedule.Meeting.Request';
+ $result['contentClass'] = 'urn:content-classes:calendarmessage';
+
+ $meeting = array();
+
+ $meeting['allDayEvent'] = $event['allday'] ?? null ? 1 : 0;
+ $meeting['startTime'] = $event['start'];
+ $meeting['dtStamp'] = $event['created'] ?? null;
+ $meeting['endTime'] = $event['end'] ?? null;
+
+ //TODO implement recurrences. We can't detect exceptions like this (don't know how), and the recurrences structure is different from event,
+ //so that also doesn't work like this.
+ // if (isset($event['recurrence']['EXCEPTIONS'])) {
+ // $meeting['instanceType'] = Syncroton_Model_EmailMeetingRequest::TYPE_RECURRING_EXCEPTION;
+ // $this->recurrence_from_kolab($collection, $event, $meeting);
+ // // } else if (isset($event['recurrence'])) {
+ // // $meeting['instanceType'] = Syncroton_Model_EmailMeetingRequest::TYPE_RECURRING_SINGLE;
+ // // $meeting['recurrenceId'] = set the date;
+ // } else if (isset($event['recurrence'])) {
+ // $meeting['instanceType'] = Syncroton_Model_EmailMeetingRequest::TYPE_RECURRING_MASTER;
+ // $this->recurrence_from_kolab($collection, $event, $meeting);
+ // } else {
+ // $meeting['instanceType'] = Syncroton_Model_EmailMeetingRequest::TYPE_NORMAL;
+ // }
+ $meeting['instanceType'] = Syncroton_Model_EmailMeetingRequest::TYPE_NORMAL;
+
+ $meeting['location'] = $event['location'] ?? null;
+
+ // Organizer
+ if (!empty($event['attendees'])) {
+ foreach ($event['attendees'] as $idx => $attendee) {
+ if ($attendee['role'] == 'ORGANIZER') {
+ if ($email = $attendee['email']) {
+ $meeting['organizer'] = $email;
+ }
+ break;
+ }
+ }
+ }
- $result['contentClass'] = 'urn:content-classes:message';
+ // Kolab Format 3.0 and xCal does support timezone per-date, but ActiveSync allows
+ // only one timezone per-event. We'll use timezone of the start date
+ $meeting['timeZone'] = kolab_sync_timezone_converter::encodeTimezoneFromDate($event['start']);
+ $meeting['globalObjId'] = self::encodeGlobalObjId(['uid' => $event['uid']]);
+
+ if ($event['_method'] == 'REQUEST') {
+ $meeting['meetingMessageType'] = Syncroton_Model_EmailMeetingRequest::MESSAGE_TYPE_REQUEST;
+ } else {
+ $meeting['meetingMessageType'] = Syncroton_Model_EmailMeetingRequest::MESSAGE_TYPE_NORMAL;
+ }
+
+ $result['meetingRequest'] = new Syncroton_Model_EmailMeetingRequest($meeting);
+ }
+ }
// Categories (Tags)
if (isset($this->tag_categories) && $this->tag_categories) {
@@ -1672,6 +1768,26 @@
return $out;
}
+ /**
+ * Returns calendar event data from the iTip invitation attached to a mail message
+ */
+ public function get_invitation_event_from_message($message)
+ {
+ // Parse the message and find iTip attachments
+ $libcal = libcalendaring::get_instance();
+ $libcal->mail_message_load(array('object' => $message));
+ $ical_objects = $libcal->get_mail_ical_objects();
+
+ // We support only one event in the iTip
+ foreach ($ical_objects as $mime_id => $event) {
+ if ($event['_type'] == 'event') {
+ $event['_method'] = $ical_objects->method;
+ return $event;
+ }
+ }
+ return null;
+ }
+
/**
* Returns calendar event data from the iTip invitation attached to a mail message
*/
@@ -1679,18 +1795,9 @@
{
// Get the mail message object
if ($message = $this->getObject($messageId)) {
- // Parse the message and find iTip attachments
- $libcal = libcalendaring::get_instance();
- $libcal->mail_message_load(array('object' => $message));
- $ical_objects = $libcal->get_mail_ical_objects();
-
- // We support only one event in the iTip
- foreach ($ical_objects as $mime_id => $event) {
- if ($event['_type'] == 'event') {
- return $event;
- }
- }
+ return $this->get_invitation_event_from_message($message);
}
+ return null;
}
diff --git a/lib/kolab_sync_timezone_converter.php b/lib/kolab_sync_timezone_converter.php
--- a/lib/kolab_sync_timezone_converter.php
+++ b/lib/kolab_sync_timezone_converter.php
@@ -217,6 +217,29 @@
return $this->_packTimezoneInfo($offsets);
}
+
+ /**
+ * Returns an encoded timezone representation from $date
+ *
+ * @param DateTime $date The date with the timezone to encode
+ *
+ * @return string encoded timezone
+ */
+ public static function encodeTimezoneFromDate($date)
+ {
+ if ($date instanceof DateTime) {
+ $timezone = $date->getTimezone();
+
+ if ($timezone && ($tz_name = $timezone->getName()) != 'UTC') {
+ $tzc = self::getInstance();
+ if ($tz_name = $tzc->encodeTimezone($tz_name, $date->format('Y-m-d'))) {
+ return $tz_name;
+ }
+ }
+ }
+ return null;
+ }
+
/**
* Get offsets for given timezone
*
diff --git a/tests/globalid_converter.php b/tests/globalid_converter.php
new file mode 100644
--- /dev/null
+++ b/tests/globalid_converter.php
@@ -0,0 +1,32 @@
+<?php
+
+require_once "../lib/kolab_sync_data.php";
+require_once "../lib/kolab_sync_data_email.php";
+
+class globalid_converter extends PHPUnit\Framework\TestCase
+{
+ function test_decode()
+ {
+ // https://learn.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-asemail/e7424ddc-dd10-431e-a0b7-5c794863370e
+ $input = 'BAAAAIIA4AB0xbcQGoLgCAAAAAAAAAAAAAAAAAAAAAAAAAAAMwAAAHZDYWwtVWlkAQAAAHs4MTQxMkQzQy0yQTI0LTRFOUQtQjIwRS0xMUY3QkJFOTI3OTl9AA==';
+ $output = kolab_sync_data_email::decodeGlobalObjId($input);
+
+ $this->assertSame(51, $output['bytecount']);
+ $this->assertSame('{81412D3C-2A24-4E9D-B20E-11F7BBE92799}', $output['uid']);
+
+ $encoded = kolab_sync_data_email::encodeGlobalObjId($output);
+ $this->assertSame($encoded, $input);
+
+
+ $input = 'BAAAAIIA4AB0xbcQGoLgCAfUCRDgQMnBJoXEAQAAAAAAAAAAEAAAAAvw7UtuTulOnjnjhns3jvM=';
+ $output = kolab_sync_data_email::decodeGlobalObjId($input);
+
+ $this->assertSame(16, $output['bytecount']);
+ $this->assertSame(2004, $output['year']);
+ $this->assertSame(9, $output['month']);
+ $this->assertSame(16, $output['day']);
+ //FIXME we don't currently implement non ical uids
+ // $encoded = kolab_sync_data_email::encodeGlobalObjId($output);
+ // $this->assertSame($encoded, $input);
+ }
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Apr 5, 11:25 PM (2 h, 56 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18834926
Default Alt Text
D4436.1775431535.diff (10 KB)
Attached To
Mode
D4436: Initial support for meeting request
Attached
Detach File
Event Timeline