Changeset View
Standalone View
lib/kolab_sync_data_email.php
Show First 20 Lines • Show All 112 Lines • ▼ Show 20 Lines | public function __construct(Syncroton_Model_IDevice $device, DateTime $syncTimeStamp) | ||||
$this->ext_devices[] = 'windowsoutlook15'; | $this->ext_devices[] = 'windowsoutlook15'; | ||||
if ($this->asversion >= 14) { | if ($this->asversion >= 14) { | ||||
$this->tag_categories = true; | $this->tag_categories = true; | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* 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 | * Creates model object | ||||
* | * | ||||
* @param Syncroton_Model_SyncCollection $collection Collection data | * @param Syncroton_Model_SyncCollection $collection Collection data | ||||
* @param string $serverId Local entry identifier | * @param string $serverId Local entry identifier | ||||
* | * | ||||
* @return Syncroton_Model_Email Email object | * @return Syncroton_Model_Email Email object | ||||
*/ | */ | ||||
public function getEntry(Syncroton_Model_SyncCollection $collection, $serverId) | public function getEntry(Syncroton_Model_SyncCollection $collection, $serverId) | ||||
▲ Show 20 Lines • Show All 205 Lines • ▼ Show 20 Lines | // $result['ConversationIndex'] = 'CA2CFA8A23'; | ||||
// add Body element to the result | // add Body element to the result | ||||
$result['body'] = $this->setBody($messageBody, $body_params); | $result['body'] = $this->setBody($messageBody, $body_params); | ||||
// original body type | // original body type | ||||
// @TODO: get this value from getMessageBody() | // @TODO: get this value from getMessageBody() | ||||
$result['nativeBodyType'] = $message->has_html_part() ? 2 : 1; | $result['nativeBodyType'] = $message->has_html_part() ? 2 : 1; | ||||
// Message class | // Message class | ||||
$result['messageClass'] = 'IPM.Note'; | |||||
$result['contentClass'] = 'urn:content-classes:message'; | |||||
if ($headers->ctype == 'multipart/signed' | if ($headers->ctype == 'multipart/signed' | ||||
&& !empty($message->parts[1]) | && !empty($message->parts[1]) | ||||
&& $message->parts[1]->mimetype == 'application/pkcs7-signature' | && $message->parts[1]->mimetype == 'application/pkcs7-signature' | ||||
) { | ) { | ||||
$result['messageClass'] = 'IPM.Note.SMIME.MultipartSigned'; | $result['messageClass'] = 'IPM.Note.SMIME.MultipartSigned'; | ||||
} | } | ||||
machniak: This line is redundant. | |||||
else if ($headers->ctype == 'application/pkcs7-mime' || $headers->ctype == 'application/x-pkcs7-mime') { | else if ($headers->ctype == 'application/pkcs7-mime' || $headers->ctype == 'application/x-pkcs7-mime') { | ||||
$result['messageClass'] = 'IPM.Note.SMIME'; | $result['messageClass'] = 'IPM.Note.SMIME'; | ||||
} | } | ||||
else { | else if ($event = $this->get_invitation_event_from_message($message)) { | ||||
$result['messageClass'] = 'IPM.Note'; | $result['messageClass'] = 'IPM.Schedule.Meeting.Request'; | ||||
$result['contentClass'] = 'urn:content-classes:calendarmessage'; | |||||
Not Done Inline ActionsI suppose we could remove these if conditions. machniak: I suppose we could remove these if conditions. | |||||
Done Inline ActionsI don't think we should for performance reasons. mollekopf: I don't think we should for performance reasons. | |||||
Not Done Inline ActionsI think get_invitation_event_from_message() is smart enough to not have a performance penalty when there's no invitation. My point is that it has a more sophisticated way to determine the invitation part. So, we'd have parity between syncroton and webclient in this regard. machniak: I think `get_invitation_event_from_message()` is smart enough to not have a performance penalty… | |||||
Done Inline ActionsI think you might be right. mollekopf: I think you might be right. | |||||
$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, | |||||
Done Inline ActionsYeah. Fill in the details. machniak: Yeah. Fill in the details. | |||||
//so that also doesn't work like this. | |||||
Not Done Inline ActionsThe fact an event has exceptions does not mean this specific invitation is an exception. If the invitation includes exceptions I'd say it's definitely not an exception. machniak: The fact an event has exceptions does not mean this specific invitation is an exception. If the… | |||||
Done Inline ActionsRight, I confused it with how we store exceptions separately in storage, but that is more a recurrenceId thing and doesn't apply to invitations (I think?). mollekopf: Right, I confused it with how we store exceptions separately in storage, but that is more a… | |||||
// if (isset($event['recurrence']['EXCEPTIONS'])) { | |||||
// $meeting['instanceType'] = Syncroton_Model_EmailMeetingRequest::TYPE_RECURRING_EXCEPTION; | |||||
Not Done Inline ActionsMeetingRequest has a different structure than Event. E.g. it has Recurrences property, but the later has recurrence. Look at 4th argument of recurrence_from_kolab(), but it's not that simple, I'm afraid. machniak: MeetingRequest has a different structure than Event. E.g. it has `Recurrences` property, but… | |||||
Done Inline ActionsI'm just not going to implement recurrences until we see a need to, so I'll comment/remove this entire section. mollekopf: I'm just not going to implement recurrences until we see a need to, so I'll comment/remove this… | |||||
// $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 | |||||
Done Inline ActionsI suppose we should use kolab_sync_data_calendar::getEntry() or just de-duplicate/use some code from there. machniak: I suppose we should use `kolab_sync_data_calendar::getEntry()` or just de-duplicate/use some… | |||||
if (!empty($event['attendees'])) { | |||||
foreach ($event['attendees'] as $idx => $attendee) { | |||||
if ($attendee['role'] == 'ORGANIZER') { | |||||
if ($email = $attendee['email']) { | |||||
$meeting['organizer'] = $email; | |||||
} | |||||
break; | |||||
} | |||||
} | |||||
} | } | ||||
Done Inline ActionsRedundant. machniak: Redundant. | |||||
$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']]); | |||||
//TODO handle other methods | |||||
if ($event['_method'] == 'REQUEST') { | |||||
$meeting['meetingMessageType'] = Syncroton_Model_EmailMeetingRequest::MESSAGE_TYPE_REQUEST; | |||||
} else { | |||||
Not Done Inline ActionsMy point was that the else could be removed. I also wonder what we should do when method=CANCEL (or REPLY). machniak: My point was that the `else` could be removed. I also wonder what we should do when… | |||||
$meeting['meetingMessageType'] = Syncroton_Model_EmailMeetingRequest::MESSAGE_TYPE_NORMAL; | |||||
} | |||||
$result['meetingRequest'] = new Syncroton_Model_EmailMeetingRequest($meeting); | |||||
} | |||||
// Categories (Tags) | // Categories (Tags) | ||||
if (isset($this->tag_categories) && $this->tag_categories) { | if (isset($this->tag_categories) && $this->tag_categories) { | ||||
// convert kolab tags into categories | // convert kolab tags into categories | ||||
$result['categories'] = $this->getKolabTags($message); | $result['categories'] = $this->getKolabTags($message); | ||||
} | } | ||||
// attachments | // attachments | ||||
$attachments = array_merge($message->attachments, $message->inline_parts); | $attachments = array_merge($message->attachments, $message->inline_parts); | ||||
Done Inline ActionsThe property might be unset, then itt defaults to MESSAGE_TYPE_NORMAL. machniak: The property might be unset, then itt defaults to MESSAGE_TYPE_NORMAL. | |||||
Done Inline ActionsThat's the idea (per spec 0 is the default). mollekopf: That's the idea (per spec 0 is the default). | |||||
if (!empty($attachments)) { | if (!empty($attachments)) { | ||||
$result['attachments'] = array(); | $result['attachments'] = array(); | ||||
foreach ($attachments as $attachment) { | foreach ($attachments as $attachment) { | ||||
$att = array(); | $att = array(); | ||||
$filename = rcube_charset::clean($attachment->filename); | $filename = rcube_charset::clean($attachment->filename); | ||||
if (empty($filename) && $attachment->mimetype == 'text/html') { | if (empty($filename) && $attachment->mimetype == 'text/html') { | ||||
▲ Show 20 Lines • Show All 1,297 Lines • ▼ Show 20 Lines | protected static function wrap_and_quote($text, $length = 72) | ||||
} | } | ||||
return $out; | return $out; | ||||
} | } | ||||
/** | /** | ||||
* Returns calendar event data from the iTip invitation attached to a mail message | * Returns calendar event data from the iTip invitation attached to a mail message | ||||
*/ | */ | ||||
public function get_invitation_event($messageId) | public function get_invitation_event_from_message($message) | ||||
{ | { | ||||
// Get the mail message object | |||||
if ($message = $this->getObject($messageId)) { | |||||
// Parse the message and find iTip attachments | // Parse the message and find iTip attachments | ||||
$libcal = libcalendaring::get_instance(); | $libcal = libcalendaring::get_instance(); | ||||
$libcal->mail_message_load(array('object' => $message)); | $libcal->mail_message_load(array('object' => $message)); | ||||
$ical_objects = $libcal->get_mail_ical_objects(); | $ical_objects = $libcal->get_mail_ical_objects(); | ||||
// We support only one event in the iTip | // We support only one event in the iTip | ||||
foreach ($ical_objects as $mime_id => $event) { | foreach ($ical_objects as $mime_id => $event) { | ||||
if ($event['_type'] == 'event') { | if ($event['_type'] == 'event') { | ||||
$event['_method'] = $ical_objects->method; | |||||
return $event; | return $event; | ||||
} | } | ||||
} | } | ||||
return null; | |||||
} | } | ||||
/** | |||||
* Returns calendar event data from the iTip invitation attached to a mail message | |||||
*/ | |||||
public function get_invitation_event($messageId) | |||||
{ | |||||
// Get the mail message object | |||||
if ($message = $this->getObject($messageId)) { | |||||
return $this->get_invitation_event_from_message($message); | |||||
} | |||||
return null; | |||||
} | } | ||||
private function mem_check($need) | private function mem_check($need) | ||||
{ | { | ||||
$mem_limit = parse_bytes(ini_get('memory_limit')); | $mem_limit = parse_bytes(ini_get('memory_limit')); | ||||
$memory = static::$memory_accumulated; | $memory = static::$memory_accumulated; | ||||
return $mem_limit > 0 && $memory + $need > $mem_limit ? false : true; | return $mem_limit > 0 && $memory + $need > $mem_limit ? false : true; | ||||
▲ Show 20 Lines • Show All 62 Lines • Show Last 20 Lines |
This line is redundant.