/** * Returns the actual SyncXXX object type. * * @param string $folderid id of the parent folder * @param string $id id of the message * @param ContentParameters $contentparameters parameters of the requested message (truncation, mimesupport etc) * * @access public * @return object/false false if the message could not be retrieved */ public function GetMessage($folder, $id, $contentparameters) { $folderid = $folder->serverid; ZLog::Write(LOGLEVEL_DEBUG, 'OXEmailSync::GetMessage(' . $folderid . ', ' . $id . ', ..)'); /* public $to; public $cc; public $from; public $subject; public $threadtopic; public $datereceived; public $displayto; public $importance; public $read; public $attachments; public $mimetruncated; public $mimedata; public $mimesize; public $bodytruncated; public $bodysize; public $body; public $messageclass; public $meetingrequest; public $reply_to; // AS 2.5 prop public $internetcpid; // AS 12.0 props public $asbody; public $asattachments; public $flag; public $contentclass; public $nativebodytype; // AS 14.0 props public $umcallerid; public $umusernotes; public $conversationid; public $conversationindex; public $lastverbexecuted; //possible values unknown, reply to sender, reply to all, forward public $lastverbexectime; public $receivedasbcc; public $sender; */ $bodypreference = $contentparameters->GetBodyPreference(); if ($bodypreference !== false) { $bpReturnType = Utils::GetBodyPreferenceBestMatch($bodypreference); } ZLog::Write(LOGLEVEL_DEBUG, 'OXEmailSync::GetMessage(' . $folderid . ', ' . $id . '): bpReturnType: ' . $bpReturnType); $output = new SyncMail(); $response = $this->OXConnector->OXreqGET('/ajax/mail', array('action' => 'get', 'session' => $this->OXConnector->getSession(), 'folder' => $folderid, 'id' => $id, 'unseen' => 'true', 'timezone' => 'UTC')); foreach ($response["data"]["to"] as &$to) { $output->to[] = $to[1]; } foreach ($response["data"]["from"] as &$from) { $output->from = $from[1]; } $output->reply_to = array(); //OX 0 - no prio, 2 - high, 1 - most important, 3 - normal, 5 - lowest //AS 0 - low, 1 - normal, 2 - important $normalPrio = array(0, 3); $highPrio = array(1, 2); $lowPrio = array(4, 5); if (in_array($response["data"]["priority"], $normalPrio)) { $output->importance = 1; ZLog::Write(LOGLEVEL_DEBUG, 'OXEmailSync::GetMessage(' . $folderid . ', ' . $id . '): Priority is "Normal"'); } else { if (in_array($response["data"]["priority"], $highPrio)) { $output->importance = 2; ZLog::Write(LOGLEVEL_DEBUG, 'OXEmailSync::GetMessage(' . $folderid . ', ' . $id . '): Priority is "High"'); } else { if (in_array($response["data"]["priority"], $lowPrio)) { $output->importance = 0; ZLog::Write(LOGLEVEL_DEBUG, 'OXEmailSync::GetMessage(' . $folderid . ', ' . $id . '): Priority is "Low"'); } } } $output->subject = $response["data"]["subject"]; // Though the unseen key is not in data we can assume the messages returned are in fact unseen // because of the "unseen" => true parameter to OX API (?) $output->read = array_key_exists("unseen", $response["data"]) && $response["data"]["unseen"] == "true" ? false : false; $output->datereceived = $this->OXUtils->timestampOXtoPHP($response["data"]["received_date"]); foreach ($response["data"]["attachments"] as $attachment) { ZLog::Write(LOGLEVEL_DEBUG, 'OXEmailSync::GetMessage(' . $folderid . ', ' . $id . '): Attachment "' . $attachment['id'] . '" has Contenttype "' . $attachment['content_type'] . '"'); // Extract text/html and text/plain parts: $textPlain = ""; $textHtml = ""; if ($attachment['content_type'] == "text/plain") { $textPlain = html_entity_decode(str_replace("<br>", "\n", $attachment['content']), ENT_NOQUOTES, 'UTF-8'); // OX sends the Umlauts as HTML-Tags. So we mus convert them :(. } else { if ($attachment['content_type'] == "text/html") { $textHtml = $attachment['content']; } } } // Does our client understand AS-Version >= 12.0 ? if (Request::GetProtocolVersion() >= 12.0 && $bpReturnType == SYNC_BODYPREFERENCE_MIME) { $output->asbody = new SyncBaseBody(); // For MIME-Message we need to get the Mail in "raw": $MIMEresponse = $this->OXConnector->OXreqGET('/ajax/mail', array('action' => 'get', 'session' => $this->OXConnector->getSession(), 'folder' => $folderid, 'id' => $id, 'unseen' => 'true', 'src' => 'true', 'timezone' => 'UTC')); ZLog::Write(LOGLEVEL_DEBUG, 'OXEmailSync::GetMessage(' . $folderid . ', ' . $id . '): MIME-Response: "' . print_r($MIMEresponse, true)); $output->asbody->data = $MIMEresponse["data"]; $output->asbody->type = SYNC_BODYPREFERENCE_MIME; $output->nativebodytype = SYNC_BODYPREFERENCE_MIME; $output->asbody->estimatedDataSize = strlen($output->asbody->data); $output->messageclass = "IPM.Note"; $output->internetcpid = INTERNET_CPID_UTF8; $output->contentclass = "urn:content-classes:message"; $truncsize = Utils::GetTruncSize($contentparameters->GetTruncation()); if (strlen($output->asbody->data) > $truncsize) { $output->asbody->data = Utils::Utf8_truncate($output->asbody->data, $truncsize); $output->asbody->truncated = 1; } } else { if (Request::GetProtocolVersion() >= 12.0 && !empty($textHtml) && $bpReturnType == SYNC_BODYPREFERENCE_HTML) { // HTML-Mails: $output->asbody = new SyncBaseBody(); $output->asbody->data = $textHtml; $output->asbody->type = SYNC_BODYPREFERENCE_HTML; $output->nativebodytype = SYNC_BODYPREFERENCE_HTML; $output->asbody->estimatedDataSize = strlen($output->asbody->data); $output->messageclass = "IPM.Note"; $output->internetcpid = INTERNET_CPID_UTF8; $output->contentclass = "urn:content-classes:message"; $truncsize = Utils::GetTruncSize($contentparameters->GetTruncation()); if (strlen($output->asbody->data) > $truncsize) { $output->asbody->data = Utils::Utf8_truncate($output->asbody->data, $truncsize); $output->asbody->truncated = 1; } } else { if (Request::GetProtocolVersion() >= 12.0 && !empty($textPlain)) { // Text-Mails: $output->asbody = new SyncBaseBody(); $output->asbody->data = $textPlain; // $textHtml; $output->asbody->type = SYNC_BODYPREFERENCE_PLAIN; $output->nativebodytype = SYNC_BODYPREFERENCE_PLAIN; $output->asbody->estimatedDataSize = strlen($output->asbody->data); $bpo = $contentparameters->BodyPreference($output->asbody->type); if (Request::GetProtocolVersion() >= 14.0 && $bpo->GetPreview()) { $output->asbody->preview = Utils::Utf8_truncate(Utils::ConvertHtmlToText($plainBody), $bpo->GetPreview()); } } else { // Default action is to only send the textPlain-Part via AS2.5 $output->body = $textPlain; $output->nativebodytype = SYNC_BODYPREFERENCE_PLAIN; } } } // ZLog::Write(LOGLEVEL_DEBUG, 'OXEmailSync::GetMessage(' . $folderid . ', ' . $id . ') Output: ' . print_r($output, true)); return $output; }
/** * Sets the message body * * @param MAPIMessage $mapimessage * @param ContentParameters $contentparameters * @param SyncObject $message */ private function setMessageBody($mapimessage, $contentparameters, &$message) { //get the available body preference types $bpTypes = $contentparameters->GetBodyPreference(); if ($bpTypes !== false) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("BodyPreference types: %s", implode(', ', $bpTypes))); //do not send mime data if the client requests it if ($contentparameters->GetMimeSupport() == SYNC_MIMESUPPORT_NEVER && ($key = array_search(SYNC_BODYPREFERENCE_MIME, $bpTypes) !== false)) { unset($bpTypes[$key]); ZLog::Write(LOGLEVEL_DEBUG, sprintf("Remove mime body preference type because the device required no mime support. BodyPreference types: %s", implode(', ', $bpTypes))); } //get the best fitting preference type $bpReturnType = Utils::GetBodyPreferenceBestMatch($bpTypes); ZLog::Write(LOGLEVEL_DEBUG, sprintf("GetBodyPreferenceBestMatch: %d", $bpReturnType)); $bpo = $contentparameters->BodyPreference($bpReturnType); ZLog::Write(LOGLEVEL_DEBUG, sprintf("bpo: truncation size:'%d', allornone:'%d', preview:'%d'", $bpo->GetTruncationSize(), $bpo->GetAllOrNone(), $bpo->GetPreview())); $this->setMessageBodyForType($mapimessage, $bpReturnType, $message); //only set the truncation size data if device set it in request if ($bpo->GetTruncationSize() != false && $bpReturnType != SYNC_BODYPREFERENCE_MIME && $message->asbody->estimatedDataSize > $bpo->GetTruncationSize() && $contentparameters->GetTruncation() != SYNC_TRUNCATION_ALL) { $message->asbody->data = Utils::Utf8_truncate($message->asbody->data, $bpo->GetTruncationSize()); $message->asbody->truncated = 1; } // set the preview or windows phones won't show the preview of an email if (Request::GetProtocolVersion() >= 14.0 && $bpo->GetPreview()) { $message->asbody->preview = Utils::Utf8_truncate(MAPIUtils::readPropStream($mapimessage, PR_BODY), $bpo->GetPreview()); } } else { // Override 'body' for truncation $truncsize = Utils::GetTruncSize($contentparameters->GetTruncation()); $this->setMessageBodyForType($mapimessage, SYNC_BODYPREFERENCE_PLAIN, $message); if ($message->bodysize > $truncsize) { $message->body = Utils::Utf8_truncate($message->body, $truncsize); $message->bodytruncated = 1; } if (!isset($message->body) || strlen($message->body) == 0) { $message->body = " "; } if ($contentparameters->GetMimeSupport() == SYNC_MIMESUPPORT_ALWAYS) { //set the html body for iphone in AS 2.5 version $this->imtoinet($mapimessage, $message); } } }
protected function GetTask($id, $contentparameters) { $truncsize = Utils::GetTruncSize($contentparameters->GetTruncation()); $tasks = $this->models->execute_kw(ODOO_DB, $this->uid, $this->password, 'project.task', 'search_read', [[['user_id', '=', $this->uid], ['id', '=', intval(substr($id, 5))]]], ['fields' => ['stage_id', 'tag_ids', 'description', 'date_last_stage_update', 'date_deadline', 'priority', 'date_start', 'name']]); if (!count($tasks)) { $message = new SyncTask(); $message->deleted = 1; return $message; } $task = $tasks[0]; $stage = $this->models->execute_kw(ODOO_DB, $this->uid, $this->password, 'project.task.type', 'read', [$task['stage_id'][0]], ['fields' => []]); ZLog::Write(LOGLEVEL_DEBUG, 'Odoo::GetMessage: $stage = (' . print_r($stage, true)) . ')'; if (count($stage) == 0) { return false; } $categories = []; if (isset($task['tag_ids'])) { $categories = $this->models->execute_kw(ODOO_DB, $this->uid, $this->password, 'project.tags', 'read', [$task['tag_ids']], ['fields' => []]); } if (isset($task['categ_ids'])) { $categories = $this->models->execute_kw(ODOO_DB, $this->uid, $this->password, 'project.tags', 'read', [$task['categ_ids']], ['fields' => []]); } ZLog::Write(LOGLEVEL_DEBUG, 'Odoo::GetMessage: $categories = (' . print_r($categories, true)) . ')'; $message = new SyncTask(); $body = $task['description']; $message->bodytruncated = false; if (strlen($body) > $truncsize) { $body = Utils::Utf8_truncate($body, $truncsize); $message->bodytruncated = true; } $message->body = str_replace("\n", "\r\n", str_replace("\r", "", $body)); $message->asbody = new SyncBaseBody(); $message->asbody->data = $body; $message->complete = 0; if ($stage['fold']) { $message->complete = 1; $message->datecompleted = strtotime($task['date_last_stage_update']); } if ($task['date_deadline']) { $message->duedate = strtotime($task['date_deadline']); $message->utcduedate = strtotime($task['date_deadline']); } $message->importance = intval($task['priority']); $message->sensitivity = 0; $message->startdate = strtotime($task['date_start']); $message->utcstartdate = strtotime($task['date_start']); $message->subject = $task['name']; $message->categories = array_map(function ($category) { return $category['name']; }, $categories); ZLog::Write(LOGLEVEL_DEBUG, 'Odoo::GetMessage: $message = (' . print_r($message, true) . ')'); return $message; }
/** * Convert a iCAL VTodo to ActiveSync format * @param string $data * @param ContentParameters $contentparameters */ private function _ParseVTodoToAS($data, $contentparameters) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->_ParseVTodoToAS(): Parsing VTodo")); $truncsize = Utils::GetTruncSize($contentparameters->GetTruncation()); $message = new SyncTask(); $ical = new iCalComponent($data); $vtodos = $ical->GetComponents("VTODO"); //Should only loop once foreach ($vtodos as $vtodo) { $message = $this->_ParseVTodoToSyncObject($vtodo, $message, $truncsize); } return $message; }
/** * Returns the actual SyncXXX object type. * * @param string $folderid id of the parent folder * @param string $id id of the message * @param ContentParameters $contentparameters parameters of the requested message (truncation, mimesupport etc) * * @access public * @return object/false false if the message could not be retrieved */ public function GetMessage($folderid, $id, $contentparameters) { $truncsize = Utils::GetTruncSize($contentparameters->GetTruncation()); $mimesupport = $contentparameters->GetMimeSupport(); $bodypreference = $contentparameters->GetBodyPreference(); /* fmbiete's contribution r1528, ZP-320 */ ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMessage('%s','%s')", $folderid, $id)); $folderImapid = $this->getImapIdFromFolderId($folderid); // Get flags, etc $stat = $this->StatMessage($folderid, $id); if ($stat) { $this->imap_reopenFolder($folderImapid); $mail = @imap_fetchheader($this->mbox, $id, FT_UID) . @imap_body($this->mbox, $id, FT_PEEK | FT_UID); $mobj = new Mail_mimeDecode($mail); $message = $mobj->decode(array('decode_headers' => true, 'decode_bodies' => true, 'include_bodies' => true, 'charset' => 'utf-8')); /* BEGIN fmbiete's contribution r1528, ZP-320 */ $output = new SyncMail(); //Select body type preference $bpReturnType = SYNC_BODYPREFERENCE_PLAIN; if ($bodypreference !== false) { $bpReturnType = Utils::GetBodyPreferenceBestMatch($bodypreference); // changed by mku ZP-330 } ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMessage - getBodyPreferenceBestMatch: %d", $bpReturnType)); //Get body data $this->getBodyRecursive($message, "plain", $plainBody); $this->getBodyRecursive($message, "html", $htmlBody); if ($plainBody == "") { $plainBody = Utils::ConvertHtmlToText($htmlBody); } $htmlBody = str_replace("\n", "\r\n", str_replace("\r", "", $htmlBody)); $plainBody = str_replace("\n", "\r\n", str_replace("\r", "", $plainBody)); if (Request::GetProtocolVersion() >= 12.0) { $output->asbody = new SyncBaseBody(); switch ($bpReturnType) { case SYNC_BODYPREFERENCE_PLAIN: $output->asbody->data = $plainBody; break; case SYNC_BODYPREFERENCE_HTML: if ($htmlBody == "") { $output->asbody->data = $plainBody; $bpReturnType = SYNC_BODYPREFERENCE_PLAIN; } else { $output->asbody->data = $htmlBody; } break; case SYNC_BODYPREFERENCE_MIME: //We don't need to create a new MIME mail, we already have one!! $output->asbody->data = $mail; break; case SYNC_BODYPREFERENCE_RTF: ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->GetMessage RTF Format NOT CHECKED"); $output->asbody->data = base64_encode($plainBody); break; } // truncate body, if requested if (strlen($output->asbody->data) > $truncsize) { $output->asbody->data = Utils::Utf8_truncate($output->asbody->data, $truncsize); $output->asbody->truncated = 1; } $output->asbody->type = $bpReturnType; $output->nativebodytype = $bpReturnType; $output->asbody->estimatedDataSize = strlen($output->asbody->data); $bpo = $contentparameters->BodyPreference($output->asbody->type); if (Request::GetProtocolVersion() >= 14.0 && $bpo->GetPreview()) { $output->asbody->preview = Utils::Utf8_truncate(Utils::ConvertHtmlToText($plainBody), $bpo->GetPreview()); } else { $output->asbody->truncated = 0; } } else { // ASV_2.5 $output->bodytruncated = 0; /* BEGIN fmbiete's contribution r1528, ZP-320 */ if ($bpReturnType == SYNC_BODYPREFERENCE_MIME) { if (strlen($mail) > $truncsize) { $output->mimedata = Utils::Utf8_truncate($mail, $truncsize); $output->mimetruncated = 1; } else { $output->mimetruncated = 0; $output->mimedata = $mail; } $output->mimesize = strlen($output->mimedata); } else { // truncate body, if requested if (strlen($plainBody) > $truncsize) { $output->body = Utils::Utf8_truncate($plainBody, $truncsize); $output->bodytruncated = 1; } else { $output->body = $plainBody; $output->bodytruncated = 0; } $output->bodysize = strlen($output->body); } /* END fmbiete's contribution r1528, ZP-320 */ } $output->datereceived = isset($message->headers["date"]) ? $this->cleanupDate($message->headers["date"]) : null; $output->messageclass = "IPM.Note"; $output->subject = isset($message->headers["subject"]) ? $message->headers["subject"] : ""; $output->read = $stat["flags"]; $output->from = isset($message->headers["from"]) ? $message->headers["from"] : null; /* BEGIN fmbiete's contribution r1528, ZP-320 */ if (isset($message->headers["thread-topic"])) { $output->threadtopic = $message->headers["thread-topic"]; } // Language Code Page ID: http://msdn.microsoft.com/en-us/library/windows/desktop/dd317756%28v=vs.85%29.aspx $output->internetcpid = INTERNET_CPID_UTF8; if (Request::GetProtocolVersion() >= 12.0) { $output->contentclass = "urn:content-classes:message"; } /* END fmbiete's contribution r1528, ZP-320 */ $Mail_RFC822 = new Mail_RFC822(); $toaddr = $ccaddr = $replytoaddr = array(); if (isset($message->headers["to"])) { $toaddr = $Mail_RFC822->parseAddressList($message->headers["to"]); } if (isset($message->headers["cc"])) { $ccaddr = $Mail_RFC822->parseAddressList($message->headers["cc"]); } if (isset($message->headers["reply_to"])) { $replytoaddr = $Mail_RFC822->parseAddressList($message->headers["reply_to"]); } $output->to = array(); $output->cc = array(); $output->reply_to = array(); foreach (array("to" => $toaddr, "cc" => $ccaddr, "reply_to" => $replytoaddr) as $type => $addrlist) { foreach ($addrlist as $addr) { $address = $addr->mailbox . "@" . $addr->host; $name = $addr->personal; if (!isset($output->displayto) && $name != "") { $output->displayto = $name; } if ($name == "" || $name == $address) { $fulladdr = w2u($address); } else { if (substr($name, 0, 1) != '"' && substr($name, -1) != '"') { $fulladdr = "\"" . w2u($name) . "\" <" . w2u($address) . ">"; } else { $fulladdr = w2u($name) . " <" . w2u($address) . ">"; } } array_push($output->{$type}, $fulladdr); } } // convert mime-importance to AS-importance if (isset($message->headers["x-priority"])) { $mimeImportance = preg_replace("/\\D+/", "", $message->headers["x-priority"]); //MAIL 1 - most important, 3 - normal, 5 - lowest //AS 0 - low, 1 - normal, 2 - important if ($mimeImportance > 3) { $output->importance = 0; } if ($mimeImportance == 3) { $output->importance = 1; } if ($mimeImportance < 3) { $output->importance = 2; } } else { /* fmbiete's contribution r1528, ZP-320 */ $output->importance = 1; } // Attachments are not needed for MIME messages if ($bpReturnType != SYNC_BODYPREFERENCE_MIME && isset($message->parts)) { $mparts = $message->parts; for ($i = 0; $i < count($mparts); $i++) { $part = $mparts[$i]; //recursively add parts if ($part->ctype_primary == "multipart" && ($part->ctype_secondary == "mixed" || $part->ctype_secondary == "alternative" || $part->ctype_secondary == "related")) { foreach ($part->parts as $spart) { $mparts[] = $spart; } continue; } //add part as attachment if it's disposition indicates so or if it is not a text part if (isset($part->disposition) && ($part->disposition == "attachment" || $part->disposition == "inline") || isset($part->ctype_primary) && $part->ctype_primary != "text") { if (isset($part->d_parameters['filename'])) { $attname = $part->d_parameters['filename']; } else { if (isset($part->ctype_parameters['name'])) { $attname = $part->ctype_parameters['name']; } else { if (isset($part->headers['content-description'])) { $attname = $part->headers['content-description']; } else { $attname = "unknown attachment"; } } } /* BEGIN fmbiete's contribution r1528, ZP-320 */ if (Request::GetProtocolVersion() >= 12.0) { if (!isset($output->asattachments) || !is_array($output->asattachments)) { $output->asattachments = array(); } $attachment = new SyncBaseAttachment(); $attachment->estimatedDataSize = isset($part->d_parameters['size']) ? $part->d_parameters['size'] : isset($part->body) ? strlen($part->body) : 0; $attachment->displayname = $attname; $attachment->filereference = $folderid . ":" . $id . ":" . $i; $attachment->method = 1; //Normal attachment $attachment->contentid = isset($part->headers['content-id']) ? str_replace("<", "", str_replace(">", "", $part->headers['content-id'])) : ""; if (isset($part->disposition) && $part->disposition == "inline") { $attachment->isinline = 1; } else { $attachment->isinline = 0; } array_push($output->asattachments, $attachment); } else { //ASV_2.5 if (!isset($output->attachments) || !is_array($output->attachments)) { $output->attachments = array(); } $attachment = new SyncAttachment(); $attachment->attsize = isset($part->d_parameters['size']) ? $part->d_parameters['size'] : isset($part->body) ? strlen($part->body) : 0; $attachment->displayname = $attname; $attachment->attname = $folderid . ":" . $id . ":" . $i; $attachment->attmethod = 1; $attachment->attoid = isset($part->headers['content-id']) ? str_replace("<", "", str_replace(">", "", $part->headers['content-id'])) : ""; array_push($output->attachments, $attachment); } /* END fmbiete's contribution r1528, ZP-320 */ } } } // unset mimedecoder & mail unset($mobj); unset($mail); return $output; } return false; }
public function GetMessage($folderid, $id, $contentparameters) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendLDAP->GetMessage('%s','%s')", $folderid, $id)); $truncsize = Utils::GetTruncSize($contentparameters->GetTruncation()); $base_dns = explode("|", LDAP_BASE_DNS); foreach ($base_dns as $base_dn) { $folder = explode(":", $base_dn); if ($folder[0] == $folderid) { $base_dn = str_replace('%u', $this->user, $folder[1]); $result_id = ldap_list($this->ldap_link, $base_dn, "(entryUUID=" . $id . ")"); if ($result_id) { $entry_id = ldap_first_entry($this->ldap_link, $result_id); if ($entry_id) { return $this->_ParseLDAPMessage($result_id, $entry_id, $truncsize); } } } } }
/** * Returns the actual SyncXXX object type. * * @param string $folderid id of the parent folder * @param string $id id of the message * @param ContentParameters $contentparameters parameters of the requested message (truncation, mimesupport etc) * * @access public * @return object/false false if the message could not be retrieved */ public function GetMessage($folderid, $id, $contentparameters) { $truncsize = Utils::GetTruncSize($contentparameters->GetTruncation()); $mimesupport = $contentparameters->GetMimeSupport(); $bodypreference = $contentparameters->GetBodyPreference(); /* fmbiete's contribution r1528, ZP-320 */ ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMessage('%s','%s')", $folderid, $id)); $folderImapid = $this->getImapIdFromFolderId($folderid); // Get flags, etc $stat = $this->StatMessage($folderid, $id); if ($stat) { $this->imap_reopen_folder($folderImapid); $mail = @imap_fetchheader($this->mbox, $id, FT_UID) . @imap_body($this->mbox, $id, FT_PEEK | FT_UID); if (empty($mail)) { throw new StatusException(sprintf("BackendIMAP->GetMessage(): Error, message not found, maybe was moved"), SYNC_ITEMOPERATIONSSTATUS_INVALIDATT); } $mobj = new Mail_mimeDecode($mail); $message = $mobj->decode(array('decode_headers' => true, 'decode_bodies' => true, 'include_bodies' => true, 'charset' => 'utf-8')); /* BEGIN fmbiete's contribution r1528, ZP-320 */ $output = new SyncMail(); //Select body type preference $bpReturnType = SYNC_BODYPREFERENCE_PLAIN; if ($bodypreference !== false) { $bpReturnType = Utils::GetBodyPreferenceBestMatch($bodypreference); // changed by mku ZP-330 } ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMessage - getBodyPreferenceBestMatch: %d", $bpReturnType)); if (is_smime($message)) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMessage - Message is SMIME, forcing to work with MIME")); $bpReturnType = SYNC_BODYPREFERENCE_MIME; } //Get body data Mail_mimeDecode::getBodyRecursive($message, "plain", $plainBody); Mail_mimeDecode::getBodyRecursive($message, "html", $htmlBody); if ($plainBody == "") { $plainBody = Utils::ConvertHtmlToText($htmlBody); } $htmlBody = str_replace("\n", "\r\n", str_replace("\r", "", $htmlBody)); $plainBody = str_replace("\n", "\r\n", str_replace("\r", "", $plainBody)); if (Request::GetProtocolVersion() >= 12.0) { $output->asbody = new SyncBaseBody(); switch ($bpReturnType) { case SYNC_BODYPREFERENCE_PLAIN: $output->asbody->data = $plainBody; break; case SYNC_BODYPREFERENCE_HTML: if ($htmlBody == "") { $output->asbody->data = $plainBody; $bpReturnType = SYNC_BODYPREFERENCE_PLAIN; } else { $output->asbody->data = $htmlBody; } break; case SYNC_BODYPREFERENCE_MIME: if (is_smime($message)) { $output->asbody->data = $mail; } else { $output->asbody->data = build_mime_message($message); } break; case SYNC_BODYPREFERENCE_RTF: ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->GetMessage RTF Format NOT CHECKED"); $output->asbody->data = base64_encode($plainBody); break; } // truncate body, if requested, but never truncate MIME messages if ($bpReturnType !== SYNC_BODYPREFERENCE_MIME && strlen($output->asbody->data) > $truncsize) { $output->asbody->data = Utils::Utf8_truncate($output->asbody->data, $truncsize); $output->asbody->truncated = 1; } else { $output->asbody->truncated = 0; } $output->asbody->type = $bpReturnType; if ($bpReturnType == SYNC_BODYPREFERENCE_MIME) { $output->nativebodytype = SYNC_BODYPREFERENCE_PLAIN; // http://msdn.microsoft.com/en-us/library/ee220018%28v=exchg.80%29.aspx } else { $output->nativebodytype = $bpReturnType; } $output->asbody->estimatedDataSize = strlen($output->asbody->data); $bpo = $contentparameters->BodyPreference($output->asbody->type); if (Request::GetProtocolVersion() >= 14.0 && $bpo->GetPreview()) { $output->asbody->preview = Utils::Utf8_truncate(Utils::ConvertHtmlToText($plainBody), $bpo->GetPreview()); } } else { // ASV_2.5 $output->bodytruncated = 0; /* BEGIN fmbiete's contribution r1528, ZP-320 */ if ($bpReturnType == SYNC_BODYPREFERENCE_MIME) { // truncate body, if requested, but never truncate MIME messages $output->mimetruncated = 0; $output->mimedata = $mail; $output->mimesize = strlen($output->mimedata); } else { // truncate body, if requested if (strlen($plainBody) > $truncsize) { $output->body = Utils::Utf8_truncate($plainBody, $truncsize); $output->bodytruncated = 1; } else { $output->body = $plainBody; $output->bodytruncated = 0; } $output->bodysize = strlen($output->body); } /* END fmbiete's contribution r1528, ZP-320 */ } $output->datereceived = isset($message->headers["date"]) ? $this->cleanupDate($message->headers["date"]) : null; if (is_smime($message)) { $output->messageclass = "IPM.Note.SMIME.MultipartSigned"; } else { $output->messageclass = "IPM.Note"; } $output->subject = isset($message->headers["subject"]) ? $message->headers["subject"] : ""; $output->read = $stat["flags"]; $output->from = isset($message->headers["from"]) ? $message->headers["from"] : null; /* BEGIN fmbiete's contribution r1528, ZP-320 */ if (isset($message->headers["thread-topic"])) { $output->threadtopic = $message->headers["thread-topic"]; /* //FIXME: Conversation support, get conversationid and conversationindex good values if (Request::GetProtocolVersion() >= 14.0) { // since the conversationid must be unique for a thread we could use the threadtopic in base64 minus the == $output->conversationid = strtoupper(str_replace("=", "", base64_encode($output->threadtopic))); if (isset($message->headers["thread-index"])) $output->conversationindex = strtoupper($message->headers["thread-index"]); } */ } else { $output->threadtopic = $output->subject; } // Language Code Page ID: http://msdn.microsoft.com/en-us/library/windows/desktop/dd317756%28v=vs.85%29.aspx $output->internetcpid = INTERNET_CPID_UTF8; if (Request::GetProtocolVersion() >= 12.0) { $output->contentclass = "urn:content-classes:message"; $output->flag = new SyncMailFlags(); if (isset($stat["star"]) && $stat["star"]) { //flagstatus 0: clear, 1: complete, 2: active $output->flag->flagstatus = SYNC_FLAGSTATUS_ACTIVE; //flagtype: for follow up $output->flag->flagtype = "FollowUp"; } else { $output->flag->flagstatus = SYNC_FLAGSTATUS_CLEAR; } } /* END fmbiete's contribution r1528, ZP-320 */ $Mail_RFC822 = new Mail_RFC822(); $toaddr = $ccaddr = $replytoaddr = array(); if (isset($message->headers["to"])) { $toaddr = $Mail_RFC822->parseAddressList($message->headers["to"]); } if (isset($message->headers["cc"])) { $ccaddr = $Mail_RFC822->parseAddressList($message->headers["cc"]); } if (isset($message->headers["reply-to"])) { $replytoaddr = $Mail_RFC822->parseAddressList($message->headers["reply-to"]); } $output->to = array(); $output->cc = array(); $output->reply_to = array(); foreach (array("to" => $toaddr, "cc" => $ccaddr, "reply_to" => $replytoaddr) as $type => $addrlist) { if ($addrlist === false) { //If we couldn't parse the addresslist we put the raw header (decoded) if ($type == "reply_to") { array_push($output->{$type}, $message->headers["reply-to"]); } else { array_push($output->{$type}, $message->headers[$type]); } } else { foreach ($addrlist as $addr) { if (isset($addr->mailbox) && isset($addr->host) && isset($addr->personal)) { $address = $addr->mailbox . "@" . $addr->host; $name = $addr->personal; if (!isset($output->displayto) && $name != "") { $output->displayto = $name; } if ($name == "" || $name == $address) { $fulladdr = $address; } else { if (substr($name, 0, 1) != '"' && substr($name, -1) != '"') { $fulladdr = "\"" . $name . "\" <" . $address . ">"; } else { $fulladdr = $name . " <" . $address . ">"; } } array_push($output->{$type}, $fulladdr); } } } } // convert mime-importance to AS-importance if (isset($message->headers["x-priority"])) { $mimeImportance = preg_replace("/\\D+/", "", $message->headers["x-priority"]); //MAIL 1 - most important, 3 - normal, 5 - lowest //AS 0 - low, 1 - normal, 2 - important if ($mimeImportance > 3) { $output->importance = 0; } elseif ($mimeImportance == 3) { $output->importance = 1; } elseif ($mimeImportance < 3) { $output->importance = 2; } } else { /* fmbiete's contribution r1528, ZP-320 */ $output->importance = 1; } // Attachments are also needed for MIME messages if (isset($message->parts)) { $mparts = $message->parts; for ($i = 0; $i < count($mparts); $i++) { $part = $mparts[$i]; //recursively add parts if (isset($part->ctype_primary) && $part->ctype_primary == "multipart" && (isset($part->ctype_secondary) && ($part->ctype_secondary == "mixed" || $part->ctype_secondary == "alternative" || $part->ctype_secondary == "related"))) { foreach ($part->parts as $spart) { $mparts[] = $spart; } continue; } if (is_calendar($part)) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMessage - text/calendar part found, trying to convert")); $output->meetingrequest = new SyncMeetingRequest(); $this->parseMeetingCalendar($part, $output); } else { //add part as attachment if it's disposition indicates so or if it is not a text part if (isset($part->disposition) && ($part->disposition == "attachment" || $part->disposition == "inline") || isset($part->ctype_primary) && $part->ctype_primary != "text") { if (isset($part->d_parameters['filename'])) { $attname = $part->d_parameters['filename']; } else { if (isset($part->ctype_parameters['name'])) { $attname = $part->ctype_parameters['name']; } else { if (isset($part->headers['content-description'])) { $attname = $part->headers['content-description']; } else { $attname = "unknown attachment"; } } } /* BEGIN fmbiete's contribution r1528, ZP-320 */ if (Request::GetProtocolVersion() >= 12.0) { if (!isset($output->asattachments) || !is_array($output->asattachments)) { $output->asattachments = array(); } $attachment = new SyncBaseAttachment(); $attachment->estimatedDataSize = isset($part->d_parameters['size']) ? $part->d_parameters['size'] : isset($part->body) ? strlen($part->body) : 0; $attachment->displayname = $attname; $attachment->filereference = $folderid . ":" . $id . ":" . $i; $attachment->method = 1; //Normal attachment $attachment->contentid = isset($part->headers['content-id']) ? str_replace("<", "", str_replace(">", "", $part->headers['content-id'])) : ""; if (isset($part->disposition) && $part->disposition == "inline") { $attachment->isinline = 1; // We try to fix the name for the inline file. // FIXME: This is a dirty hack as the used in the Zarafa backend, if you have a better method let me know! if (isset($part->ctype_primary) && isset($part->ctype_secondary)) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMessage - Guessing extension for inline attachment [primary_type %s secondary_type %s]", $part->ctype_primary, $part->ctype_secondary)); if (isset(BackendIMAP::$mimeTypes[$part->ctype_primary . '/' . $part->ctype_secondary])) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMessage - primary_type %s secondary_type %s", $part->ctype_primary, $part->ctype_secondary)); $attachment->displayname = "inline_" . $i . "." . BackendIMAP::$mimeTypes[$part->ctype_primary . '/' . $part->ctype_secondary]; } else { ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMessage - no extension found in /etc/mime.types'!!")); } } else { ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMessage - no primary_type or secondary_type")); } } else { $attachment->isinline = 0; } array_push($output->asattachments, $attachment); } else { //ASV_2.5 if (!isset($output->attachments) || !is_array($output->attachments)) { $output->attachments = array(); } $attachment = new SyncAttachment(); $attachment->attsize = isset($part->d_parameters['size']) ? $part->d_parameters['size'] : isset($part->body) ? strlen($part->body) : 0; $attachment->displayname = $attname; $attachment->attname = $folderid . ":" . $id . ":" . $i; $attachment->attmethod = 1; $attachment->attoid = isset($part->headers['content-id']) ? str_replace("<", "", str_replace(">", "", $part->headers['content-id'])) : ""; array_push($output->attachments, $attachment); } /* END fmbiete's contribution r1528, ZP-320 */ } } } } // unset mimedecoder & mail unset($mobj); unset($mail); return $output; } return false; }
/** * Returns the actual SyncXXX object type. * * @param string $folderid id of the parent folder * @param string $id id of the message * @param ContentParameters $contentparameters parameters of the requested message (truncation, mimesupport etc) * * @access public * @return object/false false if the message could not be retrieved */ public function GetMessage($folderid, $id, $contentparameters) { $truncsize = Utils::GetTruncSize($contentparameters->GetTruncation()); $mimesupport = $contentparameters->GetMimeSupport(); $bodypreference = $contentparameters->GetBodyPreference(); /* fmbiete's contribution r1528, ZP-320 */ ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMessage('%s', '%s', '%s')", $folderid, $id, implode(",", $bodypreference))); $folderImapid = $this->getImapIdFromFolderId($folderid); // Get flags, etc $stat = $this->StatMessage($folderid, $id); if ($stat) { $this->imap_reopen_folder($folderImapid); $mail = @imap_fetchheader($this->mbox, $id, FT_UID) . @imap_body($this->mbox, $id, FT_PEEK | FT_UID); if (empty($mail)) { throw new StatusException(sprintf("BackendIMAP->GetMessage(): Error, message not found, maybe was moved"), SYNC_ITEMOPERATIONSSTATUS_INVALIDATT); } $mobj = new Mail_mimeDecode($mail); $message = $mobj->decode(array('decode_headers' => true, 'decode_bodies' => true, 'include_bodies' => true, 'rfc_822bodies' => true, 'charset' => 'utf-8')); $is_multipart = is_multipart($message); $is_smime = is_smime($message); $is_encrypted = $is_smime ? is_encrypted($message) : false; ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMessage(): Message is multipart: %d, smime: %d, smime encrypted: %d", $is_multipart, $is_smime, $is_encrypted)); //Select body type preference $bpReturnType = SYNC_BODYPREFERENCE_PLAIN; if ($bodypreference !== false) { $bpReturnType = Utils::GetBodyPreferenceBestMatch($bodypreference); // changed by mku ZP-330 } ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMessage(): getBodyPreferenceBestMatch: %d", $bpReturnType)); // Prefered format is MIME -OR- message is SMIME -OR- the device supports MIME (iPhone) and doesn't really understand HTML if ($bpReturnType == SYNC_BODYPREFERENCE_MIME || $is_smime || in_array(SYNC_BODYPREFERENCE_MIME, $bodypreference)) { $bpReturnType = SYNC_BODYPREFERENCE_MIME; } // We need the text body even though MIME is used, for the preview $textBody = ""; Mail_mimeDecode::getBodyRecursive($message, "html", $textBody, true); if (strlen($textBody) > 0) { if ($bpReturnType != SYNC_BODYPREFERENCE_MIME) { $bpReturnType = SYNC_BODYPREFERENCE_HTML; } } else { Mail_mimeDecode::getBodyRecursive($message, "plain", $textBody, true); if ($bpReturnType != SYNC_BODYPREFERENCE_MIME) { $bpReturnType = SYNC_BODYPREFERENCE_PLAIN; } } ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMessage(): after thinking a bit we will use: %d", $bpReturnType)); $output = new SyncMail(); if (Request::GetProtocolVersion() >= 12.0) { $output->asbody = new SyncBaseBody(); switch ($bpReturnType) { case SYNC_BODYPREFERENCE_PLAIN: $output->asbody->data = $textBody; break; case SYNC_BODYPREFERENCE_HTML: $output->asbody->data = $textBody; break; case SYNC_BODYPREFERENCE_MIME: if ($is_smime) { $output->asbody->data = $mail; if ($is_encrypted) { // #190, KD 2015-06-04 - If message body is encrypted we'd drop it as data should only be in the attachment but... there's no good way to let only headers through... $truncsize = 500; } } else { $output->asbody->data = build_mime_message($message); } break; case SYNC_BODYPREFERENCE_RTF: ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->GetMessage(): RTF Format NOT CHECKED"); $output->asbody->data = base64_encode($textBody); break; } // truncate body, if requested. // MIME should not be truncated, but encrypted messages are truncated always to a minimal fixed size if (($bpReturnType !== SYNC_BODYPREFERENCE_MIME || $is_encrypted) && strlen($output->asbody->data) > $truncsize) { $output->asbody->data = Utils::Utf8_truncate($output->asbody->data, $truncsize); $output->asbody->truncated = 1; } else { $output->asbody->truncated = 0; } $output->asbody->type = $bpReturnType; if ($bpReturnType == SYNC_BODYPREFERENCE_MIME) { // NativeBodyType can be only (1 => PLAIN, 2 => HTML, 3 => RTF). MIME uses 1 $output->nativebodytype = SYNC_BODYPREFERENCE_PLAIN; } else { $output->nativebodytype = $bpReturnType; } $output->asbody->estimatedDataSize = strlen($output->asbody->data); $bpo = $contentparameters->BodyPreference($output->asbody->type); if (Request::GetProtocolVersion() >= 14.0 && $bpo->GetPreview()) { $output->asbody->preview = Utils::Utf8_truncate(Utils::ConvertHtmlToText($textBody), $bpo->GetPreview()); } } else { // ASV_2.5 //DEPRECATED : very old devices, and incomplete code $output->bodytruncated = 0; /* BEGIN fmbiete's contribution r1528, ZP-320 */ if ($bpReturnType == SYNC_BODYPREFERENCE_MIME) { // truncate body, if requested, but never truncate MIME messages $output->mimetruncated = 0; $output->mimedata = $mail; $output->mimesize = strlen($output->mimedata); } else { // truncate body, if requested if (strlen($textBody) > $truncsize) { $output->body = Utils::Utf8_truncate($textBody, $truncsize); $output->bodytruncated = 1; } else { $output->body = $textBody; $output->bodytruncated = 0; } $output->bodysize = strlen($output->body); } /* END fmbiete's contribution r1528, ZP-320 */ } unset($textBody); $output->datereceived = isset($message->headers["date"]) ? $this->cleanupDate($message->headers["date"]) : null; if ($is_smime) { // #190, KD 2015-06-04 - Add Encrypted (and possibly signed) to the classifications emitted if ($is_encrypted) { $output->messageclass = "IPM.Note.SMIME"; } else { $output->messageclass = "IPM.Note.SMIME.MultipartSigned"; } } else { $output->messageclass = "IPM.Note"; } $output->subject = isset($message->headers["subject"]) ? $message->headers["subject"] : ""; $output->read = $stat["flags"]; $output->from = isset($message->headers["from"]) ? $message->headers["from"] : null; /* BEGIN fmbiete's contribution r1528, ZP-320 */ if (isset($message->headers["thread-topic"])) { $output->threadtopic = $message->headers["thread-topic"]; /* //FIXME: Conversation support, get conversationid and conversationindex good values if (Request::GetProtocolVersion() >= 14.0) { // since the conversationid must be unique for a thread we could use the threadtopic in base64 minus the == $output->conversationid = strtoupper(str_replace("=", "", base64_encode($output->threadtopic))); if (isset($message->headers["thread-index"])) $output->conversationindex = strtoupper($message->headers["thread-index"]); } */ } else { $output->threadtopic = $output->subject; } // Language Code Page ID: http://msdn.microsoft.com/en-us/library/windows/desktop/dd317756%28v=vs.85%29.aspx $output->internetcpid = INTERNET_CPID_UTF8; if (Request::GetProtocolVersion() >= 12.0) { $output->contentclass = "urn:content-classes:message"; $output->flag = new SyncMailFlags(); if (isset($stat["star"]) && $stat["star"]) { //flagstatus 0: clear, 1: complete, 2: active $output->flag->flagstatus = SYNC_FLAGSTATUS_ACTIVE; //flagtype: for follow up $output->flag->flagtype = "FollowUp"; } else { $output->flag->flagstatus = SYNC_FLAGSTATUS_CLEAR; } } /* END fmbiete's contribution r1528, ZP-320 */ $Mail_RFC822 = new Mail_RFC822(); $toaddr = $ccaddr = $replytoaddr = array(); if (isset($message->headers["to"])) { $toaddr = $Mail_RFC822->parseAddressList($message->headers["to"]); } if (isset($message->headers["cc"])) { $ccaddr = $Mail_RFC822->parseAddressList($message->headers["cc"]); } if (isset($message->headers["reply-to"])) { $replytoaddr = $Mail_RFC822->parseAddressList($message->headers["reply-to"]); } $output->to = array(); $output->cc = array(); $output->reply_to = array(); foreach (array("to" => $toaddr, "cc" => $ccaddr, "reply_to" => $replytoaddr) as $type => $addrlist) { if ($addrlist === false) { //If we couldn't parse the addresslist we put the raw header (decoded) if ($type == "reply_to") { array_push($output->{$type}, $message->headers["reply-to"]); } else { array_push($output->{$type}, $message->headers[$type]); } } else { foreach ($addrlist as $addr) { // If the address was a group we have "groupname" and "addresses" atributes if (isset($addr->addresses)) { if (count($addr->addresses) == 0) { // readd the empty group delimiter array_push($output->{$type}, sprintf("%s:;", $addr->groupname)); if (!isset($output->displayto) && strlen($addr->groupname) > 0) { $output->displayto = $addr->groupname; } } else { foreach ($addr->addresses as $addr_group) { $name = $this->add_address_to_list($output->{$type}, $addr_group); if (!isset($output->displayto) && strlen($name) > 0) { $output->displayto = $name; } } } } else { // Not a group $name = $this->add_address_to_list($output->{$type}, $addr); if (!isset($output->displayto) && strlen($name) > 0) { $output->displayto = $name; } } } } } // convert mime-importance to AS-importance if (isset($message->headers["x-priority"])) { $mimeImportance = preg_replace("/\\D+/", "", $message->headers["x-priority"]); //MAIL 1 - most important, 3 - normal, 5 - lowest //AS 0 - low, 1 - normal, 2 - important if ($mimeImportance > 3) { $output->importance = 0; } elseif ($mimeImportance == 3) { $output->importance = 1; } elseif ($mimeImportance < 3) { $output->importance = 2; } } else { /* fmbiete's contribution r1528, ZP-320 */ $output->importance = 1; } // Attachments are also needed for MIME messages if (isset($message->parts)) { $mparts = $message->parts; for ($i = 0; $i < count($mparts); $i++) { $part = $mparts[$i]; //recursively add parts if (isset($part->ctype_primary) && $part->ctype_primary == "multipart" && (isset($part->ctype_secondary) && ($part->ctype_secondary == "mixed" || $part->ctype_secondary == "alternative" || $part->ctype_secondary == "related"))) { foreach ($part->parts as $spart) { $mparts[] = $spart; } continue; } if (is_calendar($part)) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMessage(): text/calendar part found, trying to convert")); $output->meetingrequest = new SyncMeetingRequest(); $this->parseMeetingCalendar($part, $output); } else { //add part as attachment if it's disposition indicates so or if it is not a text part if (isset($part->disposition) && ($part->disposition == "attachment" || $part->disposition == "inline") || isset($part->ctype_primary) && $part->ctype_primary != "text") { if (isset($part->d_parameters['filename'])) { $attname = $part->d_parameters['filename']; } else { if (isset($part->ctype_parameters['name'])) { $attname = $part->ctype_parameters['name']; } else { if (isset($part->headers['content-description'])) { $attname = $part->headers['content-description']; } else { $attname = "unknown attachment"; } } } /* BEGIN fmbiete's contribution r1528, ZP-320 */ if (Request::GetProtocolVersion() >= 12.0) { if (!isset($output->asattachments) || !is_array($output->asattachments)) { $output->asattachments = array(); } $attachment = new SyncBaseAttachment(); $attachment->estimatedDataSize = isset($part->d_parameters['size']) ? $part->d_parameters['size'] : isset($part->body) ? strlen($part->body) : 0; $attachment->displayname = $attname; $attachment->filereference = $folderid . ":" . $id . ":" . $i; $attachment->method = 1; //Normal attachment $attachment->contentid = isset($part->headers['content-id']) ? str_replace("<", "", str_replace(">", "", $part->headers['content-id'])) : ""; if (isset($part->disposition) && $part->disposition == "inline") { $attachment->isinline = 1; // #209 - KD 2015-06-16 If we got a filename use it, otherwise guess if (!isset($part->filename)) { // We try to fix the name for the inline file. // FIXME: This is a dirty hack as the used in the Zarafa backend, if you have a better method let me know! if (isset($part->ctype_primary) && isset($part->ctype_secondary)) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMessage(): Guessing extension for inline attachment [primary_type %s secondary_type %s]", $part->ctype_primary, $part->ctype_secondary)); if (isset(BackendIMAP::$mimeTypes[$part->ctype_primary . '/' . $part->ctype_secondary])) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMessage(): primary_type %s secondary_type %s", $part->ctype_primary, $part->ctype_secondary)); $attachment->displayname = "inline_" . $i . "." . BackendIMAP::$mimeTypes[$part->ctype_primary . '/' . $part->ctype_secondary]; } else { ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMessage(): no extension found in '%s'!!", SYSTEM_MIME_TYPES_MAPPING)); } } else { ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMessage(): no primary_type or secondary_type")); } } } else { $attachment->isinline = 0; } array_push($output->asattachments, $attachment); } else { //ASV_2.5 if (!isset($output->attachments) || !is_array($output->attachments)) { $output->attachments = array(); } $attachment = new SyncAttachment(); $attachment->attsize = isset($part->d_parameters['size']) ? $part->d_parameters['size'] : isset($part->body) ? strlen($part->body) : 0; $attachment->displayname = $attname; $attachment->attname = $folderid . ":" . $id . ":" . $i; $attachment->attmethod = 1; $attachment->attoid = isset($part->headers['content-id']) ? str_replace("<", "", str_replace(">", "", $part->headers['content-id'])) : ""; array_push($output->attachments, $attachment); } /* END fmbiete's contribution r1528, ZP-320 */ } } } } unset($message); unset($mobj); unset($mail); return $output; } return false; }
/** * Returns the actual SyncXXX object type. * * @param string $folderid id of the parent folder * @param string $id id of the message * @param ContentParameters $contentparameters parameters of the requested message (truncation, mimesupport etc) * * @access public * @return object/false false if the message could not be retrieved */ public function GetMessage($folderid, $id, $contentparameters) { $truncsize = Utils::GetTruncSize($contentparameters->GetTruncation()); $mimesupport = $contentparameters->GetMimeSupport(); ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMessage('%s','%s')", $folderid, $id)); $folderImapid = $this->getImapIdFromFolderId($folderid); // Get flags, etc $stat = $this->StatMessage($folderid, $id); if ($stat) { $this->imap_reopenFolder($folderImapid); $mail = @imap_fetchheader($this->mbox, $id, FT_UID) . @imap_body($this->mbox, $id, FT_PEEK | FT_UID); $mobj = new Mail_mimeDecode($mail); $message = $mobj->decode(array('decode_headers' => true, 'decode_bodies' => true, 'include_bodies' => true, 'charset' => 'utf-8')); $output = new SyncMail(); $body = $this->getBody($message); $output->bodysize = strlen($body); // truncate body, if requested if (strlen($body) > $truncsize) { $body = Utils::Utf8_truncate($body, $truncsize); $output->bodytruncated = 1; } else { $body = $body; $output->bodytruncated = 0; } $body = str_replace("\n", "\r\n", str_replace("\r", "", $body)); $output->body = $body; $output->datereceived = isset($message->headers["date"]) ? $this->cleanupDate($message->headers["date"]) : null; $output->messageclass = "IPM.Note"; $output->subject = isset($message->headers["subject"]) ? $message->headers["subject"] : ""; $output->read = $stat["flags"]; $output->from = isset($message->headers["from"]) ? $message->headers["from"] : null; $Mail_RFC822 = new Mail_RFC822(); $toaddr = $ccaddr = $replytoaddr = array(); if (isset($message->headers["to"])) { $toaddr = $Mail_RFC822->parseAddressList($message->headers["to"]); } if (isset($message->headers["cc"])) { $ccaddr = $Mail_RFC822->parseAddressList($message->headers["cc"]); } if (isset($message->headers["reply_to"])) { $replytoaddr = $Mail_RFC822->parseAddressList($message->headers["reply_to"]); } $output->to = array(); $output->cc = array(); $output->reply_to = array(); foreach (array("to" => $toaddr, "cc" => $ccaddr, "reply_to" => $replytoaddr) as $type => $addrlist) { foreach ($addrlist as $addr) { $address = $addr->mailbox . "@" . $addr->host; $name = $addr->personal; if (!isset($output->displayto) && $name != "") { $output->displayto = $name; } if ($name == "" || $name == $address) { $fulladdr = w2u($address); } else { if (substr($name, 0, 1) != '"' && substr($name, -1) != '"') { $fulladdr = "\"" . w2u($name) . "\" <" . w2u($address) . ">"; } else { $fulladdr = w2u($name) . " <" . w2u($address) . ">"; } } array_push($output->{$type}, $fulladdr); } } // convert mime-importance to AS-importance if (isset($message->headers["x-priority"])) { $mimeImportance = preg_replace("/\\D+/", "", $message->headers["x-priority"]); if ($mimeImportance > 3) { $output->importance = 0; } if ($mimeImportance == 3) { $output->importance = 1; } if ($mimeImportance < 3) { $output->importance = 2; } } // Attachments are only searched in the top-level part if (isset($message->parts)) { $mparts = $message->parts; for ($i = 0; $i < count($mparts); $i++) { $part = $mparts[$i]; //recursively add parts if ($part->ctype_primary == "multipart" && ($part->ctype_secondary == "mixed" || $part->ctype_secondary == "alternative" || $part->ctype_secondary == "related")) { foreach ($part->parts as $spart) { $mparts[] = $spart; } continue; } //add part as attachment if it's disposition indicates so or if it is not a text part if (isset($part->disposition) && ($part->disposition == "attachment" || $part->disposition == "inline") || isset($part->ctype_primary) && $part->ctype_primary != "text") { if (!isset($output->attachments) || !is_array($output->attachments)) { $output->attachments = array(); } $attachment = new SyncAttachment(); if (isset($part->body)) { $attachment->attsize = strlen($part->body); } if (isset($part->d_parameters['filename'])) { $attname = $part->d_parameters['filename']; } else { if (isset($part->ctype_parameters['name'])) { $attname = $part->ctype_parameters['name']; } else { if (isset($part->headers['content-description'])) { $attname = $part->headers['content-description']; } else { $attname = "unknown attachment"; } } } $attachment->displayname = $attname; $attachment->attname = $folderid . ":" . $id . ":" . $i; $attachment->attmethod = 1; $attachment->attoid = isset($part->headers['content-id']) ? $part->headers['content-id'] : ""; array_push($output->attachments, $attachment); } } } // unset mimedecoder & mail unset($mobj); unset($mail); return $output; } return false; }
/** * Returns the actual SyncXXX object type. * * @param string $folderid id of the parent folder * @param string $id id of the message * @param ContentParameters $contentparameters parameters of the requested message (truncation, mimesupport etc) * * @access public * @return object/false false if the message could not be retrieved */ public function GetMessage($folderid, $id, $contentparameters) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->GetMessage('%s', '%s')", $folderid, $id)); $message = false; $addressbookId = $this->getAddressbookIdFromVcard($id); $vcardId = $this->getVcardId($id); $addressbookUrl = $this->getAddressbookFromId($addressbookId); if ($addressbookUrl !== false) { $xml_vcard = false; try { $this->server->set_url($addressbookUrl); $xml_vcard = $this->server->get_xml_vcard($vcardId); } catch (Exception $ex) { ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->GetMessage - Error getting vcard '%s' in '%s': %s", $vcardId, $addressbookId, $ex->getMessage())); } if ($xml_vcard !== false) { $truncsize = Utils::GetTruncSize($contentparameters->GetTruncation()); $xml_data = new SimpleXMLElement($xml_vcard); $message = $this->ParseFromVCard($xml_data->element[0]->vcard->__toString(), $truncsize); } } if ($message === false) { ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->GetMessage(): vCard not found")); } return $message; }
/** * Sets the message body. * * @param MAPIMessage $mapimessage * @param ContentParameters $contentparameters * @param SyncObject $message */ private function setMessageBody($mapimessage, $contentparameters, &$message) { //get the available body preference types $bpTypes = $contentparameters->GetBodyPreference(); if ($bpTypes !== false) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("BodyPreference types: %s", implode(', ', $bpTypes))); //do not send mime data if the client requests it if ($contentparameters->GetMimeSupport() == SYNC_MIMESUPPORT_NEVER && ($key = array_search(SYNC_BODYPREFERENCE_MIME, $bpTypes) !== false)) { unset($bpTypes[$key]); ZLog::Write(LOGLEVEL_DEBUG, sprintf("Remove mime body preference type because the device required no mime support. BodyPreference types: %s", implode(', ', $bpTypes))); } //get the best fitting preference type $bpReturnType = Utils::GetBodyPreferenceBestMatch($bpTypes); ZLog::Write(LOGLEVEL_DEBUG, sprintf("GetBodyPreferenceBestMatch: %d", $bpReturnType)); $bpo = $contentparameters->BodyPreference($bpReturnType); ZLog::Write(LOGLEVEL_DEBUG, sprintf("bpo: truncation size:'%d', allornone:'%d', preview:'%d'", $bpo->GetTruncationSize(), $bpo->GetAllOrNone(), $bpo->GetPreview())); $this->setMessageBodyForType($mapimessage, $bpReturnType, $message); //only set the truncation size data if device set it in request if ($bpo->GetTruncationSize() != false && $bpReturnType != SYNC_BODYPREFERENCE_MIME && $message->asbody->estimatedDataSize > $bpo->GetTruncationSize()) { // Truncated plaintext requests are used on iOS for the preview in the email list. All images and links should be removed - see https://jira.z-hub.io/browse/ZP-1025 if ($bpReturnType == SYNC_BODYPREFERENCE_PLAIN) { ZLog::Write(LOGLEVEL_DEBUG, "MAPIProvider->setMessageBody(): truncated plain-text body requested, stripping all links and images"); // Get more data because of the filtering it's most probably going down in size. It's going to be truncated to the correct size below. $plainbody = stream_get_contents($message->asbody->data, $bpo->GetTruncationSize() * 3); $message->asbody->data = StringStreamWrapper::Open(preg_replace('/<http(s){0,1}:\\/\\/.*?>/i', '', $plainbody)); } // truncate data stream ftruncate($message->asbody->data, $bpo->GetTruncationSize()); $message->asbody->truncated = 1; } // set the preview or windows phones won't show the preview of an email if (Request::GetProtocolVersion() >= 14.0 && $bpo->GetPreview()) { $message->asbody->preview = Utils::Utf8_truncate(MAPIUtils::readPropStream($mapimessage, PR_BODY), $bpo->GetPreview()); } } else { // Override 'body' for truncation $truncsize = Utils::GetTruncSize($contentparameters->GetTruncation()); $this->setMessageBodyForType($mapimessage, SYNC_BODYPREFERENCE_PLAIN, $message); if ($message->bodysize > $truncsize) { $message->body = Utils::Utf8_truncate($message->body, $truncsize); $message->bodytruncated = 1; } if (!isset($message->body) || strlen($message->body) == 0) { $message->body = " "; } if ($contentparameters->GetMimeSupport() == SYNC_MIMESUPPORT_ALWAYS) { //set the html body for iphone in AS 2.5 version $this->imtoinet($mapimessage, $message); } } }
/** * Convert a VCard to ActiveSync format * @param vcard $data * @param ContentParameters $contentparameters * @return SyncContact */ private function _ParseVCardToAS($data, $contentparameters) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->_ParseVCardToAS(vCard[%s])", $data)); $truncsize = Utils::GetTruncSize($contentparameters->GetTruncation()); $vCard = new vCard(false, $data); if (count($vCard) != 1) { ZLog::Write(LOGLEVEL_WARN, sprintf("BackendCardDAV->_ParseVCardToAS(): Error parsing vCard[%s]", $data)); return false; } $card = $vCard->access(); $mapping = array('fn' => 'fileas', 'n' => array('LastName' => 'lastname', 'FirstName' => 'firstname'), 'tel' => array('home' => 'homephonenumber', 'cell' => 'mobilephonenumber', 'work' => 'businessphonenumber', 'fax' => 'businessfaxnumber', 'pager' => 'pagernumber'), 'email' => array('work' => 'email1address', 'home' => 'email2address'), 'url' => array('work' => 'webpage', 'home' => 'webpage'), 'bday' => 'birthday', 'title' => 'jobtitle', 'note' => 'body', 'org' => array('Name' => 'companyname', 'Unit1' => 'department'), 'adr' => array('work' => array('POBox' => '', 'ExtendedAddress' => '', 'StreetAddress' => 'businessstreet', 'Locality' => 'businesscity', 'Region' => 'businessstate', 'PostalCode' => 'businesspostalcode', 'Country' => 'businesscountry'), 'home' => array('POBox' => '', 'ExtendedAddress' => '', 'StreetAddress' => 'homestreet', 'Locality' => 'homecity', 'Region' => 'homestate', 'PostalCode' => 'homepostalcode', 'Country' => 'homecountry')), 'photo' => array('jpeg' => 'picture'), 'x-aim' => 'imaddress'); $message = new SyncContact(); foreach ($mapping as $vcard_attribute => $ms_attribute) { //ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->_ParseVCardToAS(vCard[%s] => ms[%s])", $vcard_attribute, $ms_attribute)); if (empty($card[$vcard_attribute])) { continue; } if (is_array($card[$vcard_attribute])) { // tel;email;url;org foreach ($card[$vcard_attribute] as $key => $value) { if (empty($value) || empty($ms_attribute[$key])) { continue; } // adr if (is_array($value)) { foreach ($value as $adrkey => $adrvalue) { if (empty($adrvalue) || empty($ms_attribute[$key][$adrkey])) { continue; } $message->{$ms_attribute}[$key][$adrkey] = $adrvalue; } } else { $message->{$ms_attribute}[$key] = $value; } } } else { if ($vcard_attribute === "note" && !empty($card[$vcard_attribute])) { $body = $card[$vcard_attribute]; // truncate body, if requested if (strlen($body) > $truncsize) { $body = Utils::Utf8_truncate($body, $truncsize); $message->bodytruncated = 1; } else { $body = $body; $message->bodytruncated = 0; } $body = str_replace("\n", "\r\n", str_replace("\r", "", $body)); $message->body = $body; $message->bodysize = strlen($body); } else { if ($vcard_attribute === "bday" && !empty($card[$vcard_attribute])) { $tz = date_default_timezone_get(); date_default_timezone_set('UTC'); $message->{$ms_attribute} = strtotime($card[$vcard_attribute]); date_default_timezone_set($tz); } else { $message->{$ms_attribute} = $card[$vcard_attribute]; } } } } if (isset($card['nickname']) && !empty($card['nickname']) && is_array($card['nickname'])) { $message->nickname = $card['nickname'][0]; } if (isset($card['categories']) && !empty($card['categories'])) { $message->categories = implode(',', $card['categories']); $itemTagNames = array(); for ($i = 0; $i < count($card['categories']); $i++) { //print $card['categories'][$i] ."\n"; $itemTagNames[] = $card['categories'][$i]; } //print "message->categories=". $card['categories'] ."\n"; $message->categories = $itemTagNames; } ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->_ParseVCardToAS(vCard fileas [%s])", $message->fileas)); return $message; /* $mapping = array( 'FN' => 'fileas', 'N' => 'lastname;firstname', 'NICKNAME' => 'nickname', // Only one in active Sync 'TEL;TYPE=home' => 'homephonenumber', 'TEL;TYPE=cell' => 'mobilephonenumber', 'TEL;TYPE=work' => 'businessphonenumber', 'TEL;TYPE=fax' => 'businessfaxnumber', 'TEL;TYPE=pager' => 'pagernumber', 'EMAIL;TYPE=work' => 'email1address', 'EMAIL;TYPE=home' => 'email2address', 'URL;TYPE=home' => 'webpage', // does not exist in ActiveSync 'URL;TYPE=work' => 'webpage', 'BDAY' => 'birthday', // 'ROLE' => 'jobtitle', iOS take it as 'TITLE' Does not make sense? 'TITLE' => 'jobtitle', 'NOTE' => 'body', 'ORG' => 'companyname;department', 'ADR;TYPE=work' => ';;businessstreet;businesscity;businessstate;businesspostalcode;businesscountry', 'ADR;TYPE=home' => ';;homestreet;homecity;homestate;homepostalcode;homecountry', // 'PHOTO;VALUE=BINARY;TYPE=JPEG;ENCODING=B' => 'picture', // Need improve parsing waiting on vCard parser to suport photo 'PHOTO;ENCODING=BASE64;TYPE=JPEG' => 'picture', 'CATEGORIES' => 'categories', // Can not create categorie on iOS, test with hotmail.com and no sync or view of categories? 'X-AIM' => 'imaddress', ); $message = new SyncContact(); //$message->lastname = "unknow"; //$message->firstname = "unknow"; //$message->fileas = "unknow"; // Removing/changing inappropriate newlines, i.e., all CRs or multiple newlines are changed to a single newline $data = str_replace("\x00", '', $data); $data = str_replace("\r\n", "\n", $data); $data = str_replace("\r", "\n", $data); $data = preg_replace('/(\n)([ \t])/i', '', $data); // Joining multiple lines that are split with a hard wrap and indicated by an equals sign at the end of line // Generate a photo issue //$data = str_replace("=\n", '', $data); // Joining multiple lines that are split with a soft wrap (space or tab on the beginning of the next line) $data = str_replace(array("\n ", "\n\t"), '', $data); $lines = explode("\n", $data); foreach($lines as $line) { if (trim($line) == '') { continue; } $pos = strpos($line, ':'); if ($pos === false) { continue; } $vcardparse = explode(":", $line); if (!isset($vcardparse[0]) || !isset($vcardparse[1]) || empty($vcardparse[0]) || empty($vcardparse[1]) ) { continue; } foreach ($mapping as $vcard => $ms) { if ((string)$vcardparse[0] === (string)$vcard) { if ( ($vcardparse[0] === 'N') || ($vcardparse[0] === 'ORG') || (strstr($vcardparse[0],'ADR') != false) ) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->_ParseVCardToAS(vCard N - ORG - ADR)")); $parts = explode(";", $vcardparse[1]); $value = explode(";", $ms); for ($i=0;$i<sizeof($parts);$i++) { if (empty($value[$i]) || empty($parts[$i])) { continue; } ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->_ParseVCardToAS(vCard [%s])", $parts[$i])); $message->$value[$i] = (string)$parts[$i]; } } else if ($vcardparse[0] === 'CATEGORIES') { ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->_ParseVCardToAS(vCard CATEGORIES)")); $message->$ms = explode(',', $vcardparse[1]); } else if ($vcardparse[0] === 'NOTE') { ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->_ParseVCardToAS(vCard NOTE)")); $body = (string)$vcardparse[1]; // truncate body, if requested if(strlen($body) > $truncsize) { $body = Utils::Utf8_truncate($body, $truncsize); $message->bodytruncated = 1; $message->bodysize = $truncsize; } else { $message->bodytruncated = 0; $message->bodysize = strlen($body); } $body = str_replace("\n","\r\n", str_replace("\r","",$body)); $message->body = $body; } else if ($vcardparse[0] === 'PHOTO') { ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->_ParseVCardToAS(vCard PHOTO)")); // Check for base64 encode so not need //$message->picture = base64_encode($vcardparse[1]); //$message->picture = imap_binary($vcardparse[1]); $message->picture = $vcardparse[1]; } else if ($vcardparse[0] === 'BDAY') { ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->_ParseVCardToAS(vCard BDAY)")); $tz = date_default_timezone_get(); date_default_timezone_set('UTC'); $message->$ms = strtotime($vcardparse[1]); date_default_timezone_set($tz); } else { $message->$ms = (string)$vcardparse[1]; } } } } ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->_ParseVCardToAS(vCard fileas [%s])", $message->fileas)); return $message; */ }
/** * Returns the actual SyncXXX object type. * * @param string $folderid id of the parent folder * @param string $id id of the message * @param ContentParameters $contentparameters parameters of the requested message (truncation, mimesupport etc) * * @access public * @return object/false false if the message could not be retrieved */ public function GetMessage($folderid, $id, $contentparameters) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->GetMessage('%s', '%s')", $folderid, $id)); $message = false; //TODO: change folderid $xml_vcard = false; try { $xml_vcard = $this->server->get_xml_vcard($id); } catch (Exception $ex) { ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->GetMessage - Error getting vcard: %s", $ex->getMessage())); } if ($xml_vcard === false) { ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->GetMessage(): getting vCard")); } else { $truncsize = Utils::GetTruncSize($contentparameters->GetTruncation()); $xml_data = new SimpleXMLElement($xml_vcard); $message = $this->ParseFromVCard($xml_data->element[0]->vcard->__toString(), $truncsize); } return $message; }