/** * Returns all available data of a single message * * @param string $folderid * @param string $id * @param ContentParameters $contentparameters flag * * @access public * @return object(SyncObject) * @throws StatusException */ public function Fetch($folderid, $id, $contentparameters) { // override truncation $contentparameters->SetTruncation(SYNC_TRUNCATION_ALL); $msg = $this->GetMessage($folderid, $id, $contentparameters); if ($msg === false) { throw new StatusException("BackendDiff->Fetch('%s','%s'): Error, unable retrieve message from backend", SYNC_STATUS_OBJECTNOTFOUND); } return $msg; }
/** * 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; }
/** * Handles the Search command * * @param int $commandCode * * @access public * @return boolean */ public function Handle($commandCode) { $searchrange = '0'; $cpo = new ContentParameters(); if (!self::$decoder->getElementStartTag(SYNC_SEARCH_SEARCH)) { return false; } // TODO check: possible to search in other stores? if (!self::$decoder->getElementStartTag(SYNC_SEARCH_STORE)) { return false; } if (!self::$decoder->getElementStartTag(SYNC_SEARCH_NAME)) { return false; } $searchname = strtoupper(self::$decoder->getElementContent()); if (!self::$decoder->getElementEndTag()) { return false; } if (!self::$decoder->getElementStartTag(SYNC_SEARCH_QUERY)) { return false; } // check if it is a content of an element (= GAL search) // or a starttag (= mailbox or documentlibrary search) $searchquery = self::$decoder->getElementContent(); if ($searchquery && !self::$decoder->getElementEndTag()) { return false; } if ($searchquery === false) { $cpo->SetSearchName($searchname); if (self::$decoder->getElementStartTag(SYNC_SEARCH_AND)) { if (self::$decoder->getElementStartTag(SYNC_FOLDERID)) { $searchfolderid = self::$decoder->getElementContent(); $cpo->SetSearchFolderid($searchfolderid); if (!self::$decoder->getElementEndTag()) { // SYNC_FOLDERTYPE return false; } } if (self::$decoder->getElementStartTag(SYNC_FOLDERTYPE)) { $searchclass = self::$decoder->getElementContent(); $cpo->SetSearchClass($searchclass); if (!self::$decoder->getElementEndTag()) { // SYNC_FOLDERTYPE return false; } } if (self::$decoder->getElementStartTag(SYNC_FOLDERID)) { $searchfolderid = self::$decoder->getElementContent(); $cpo->SetSearchFolderid($searchfolderid); if (!self::$decoder->getElementEndTag()) { // SYNC_FOLDERTYPE return false; } } if (self::$decoder->getElementStartTag(SYNC_SEARCH_FREETEXT)) { $searchfreetext = self::$decoder->getElementContent(); $cpo->SetSearchFreeText($searchfreetext); if (!self::$decoder->getElementEndTag()) { // SYNC_SEARCH_FREETEXT return false; } } //TODO - review if (self::$decoder->getElementStartTag(SYNC_SEARCH_GREATERTHAN)) { if (self::$decoder->getElementStartTag(SYNC_POOMMAIL_DATERECEIVED)) { $datereceivedgreater = true; if (($dam = self::$decoder->getElementContent()) !== false) { $datereceivedgreater = true; if (!self::$decoder->getElementEndTag()) { return false; } } $cpo->SetSearchDateReceivedGreater($datereceivedgreater); } if (self::$decoder->getElementStartTag(SYNC_SEARCH_VALUE)) { $searchvalue = self::$decoder->getElementContent(); $cpo->SetSearchValueGreater($searchvalue); if (!self::$decoder->getElementEndTag()) { // SYNC_SEARCH_VALUE return false; } } if (!self::$decoder->getElementEndTag()) { // SYNC_SEARCH_GREATERTHAN return false; } } if (self::$decoder->getElementStartTag(SYNC_SEARCH_LESSTHAN)) { if (self::$decoder->getElementStartTag(SYNC_POOMMAIL_DATERECEIVED)) { $datereceivedless = true; if (($dam = self::$decoder->getElementContent()) !== false) { $datereceivedless = true; if (!self::$decoder->getElementEndTag()) { return false; } } $cpo->SetSearchDateReceivedLess($datereceivedless); } if (self::$decoder->getElementStartTag(SYNC_SEARCH_VALUE)) { $searchvalue = self::$decoder->getElementContent(); $cpo->SetSearchValueLess($searchvalue); if (!self::$decoder->getElementEndTag()) { // SYNC_SEARCH_VALUE return false; } } if (!self::$decoder->getElementEndTag()) { // SYNC_SEARCH_LESSTHAN return false; } } if (self::$decoder->getElementStartTag(SYNC_SEARCH_FREETEXT)) { $searchfreetext = self::$decoder->getElementContent(); $cpo->SetSearchFreeText($searchfreetext); if (!self::$decoder->getElementEndTag()) { // SYNC_SEARCH_FREETEXT return false; } } if (!self::$decoder->getElementEndTag()) { // SYNC_SEARCH_AND return false; } } elseif (self::$decoder->getElementStartTag(SYNC_SEARCH_EQUALTO)) { // linkid can be an empty tag as well as have value if (self::$decoder->getElementStartTag(SYNC_DOCUMENTLIBRARY_LINKID)) { if (($linkId = self::$decoder->getElementContent()) !== false) { $cpo->SetLinkId($linkId); if (!self::$decoder->getElementEndTag()) { // SYNC_DOCUMENTLIBRARY_LINKID return false; } } } if (self::$decoder->getElementStartTag(SYNC_SEARCH_VALUE)) { $searchvalue = self::$decoder->getElementContent(); $cpo->SetSearchValueLess($searchvalue); if (!self::$decoder->getElementEndTag()) { // SYNC_SEARCH_VALUE return false; } } if (!self::$decoder->getElementEndTag()) { // SYNC_SEARCH_EQUALTO return false; } } if (!self::$decoder->getElementEndTag()) { // SYNC_SEARCH_QUERY return false; } } if (self::$decoder->getElementStartTag(SYNC_SEARCH_OPTIONS)) { while (1) { if (self::$decoder->getElementStartTag(SYNC_SEARCH_RANGE)) { $searchrange = self::$decoder->getElementContent(); $cpo->SetSearchRange($searchrange); if (!self::$decoder->getElementEndTag()) { return false; } } if (self::$decoder->getElementStartTag(SYNC_SEARCH_REBUILDRESULTS)) { $rebuildresults = true; if (($dam = self::$decoder->getElementContent()) !== false) { $rebuildresults = true; if (!self::$decoder->getElementEndTag()) { return false; } } $cpo->SetSearchRebuildResults($rebuildresults); } if (self::$decoder->getElementStartTag(SYNC_SEARCH_DEEPTRAVERSAL)) { $deeptraversal = true; if (($dam = self::$decoder->getElementContent()) !== false) { $deeptraversal = true; if (!self::$decoder->getElementEndTag()) { return false; } } $cpo->SetSearchDeepTraversal($deeptraversal); } if (self::$decoder->getElementStartTag(SYNC_MIMESUPPORT)) { $cpo->SetMimeSupport(self::$decoder->getElementContent()); if (!self::$decoder->getElementEndTag()) { return false; } } //TODO body preferences while (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_BODYPREFERENCE)) { if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_TYPE)) { $bptype = self::$decoder->getElementContent(); $cpo->BodyPreference($bptype); if (!self::$decoder->getElementEndTag()) { return false; } } if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_TRUNCATIONSIZE)) { $cpo->BodyPreference($bptype)->SetTruncationSize(self::$decoder->getElementContent()); if (!self::$decoder->getElementEndTag()) { return false; } } if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_ALLORNONE)) { $cpo->BodyPreference($bptype)->SetAllOrNone(self::$decoder->getElementContent()); if (!self::$decoder->getElementEndTag()) { return false; } } if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_PREVIEW)) { $cpo->BodyPreference($bptype)->SetPreview(self::$decoder->getElementContent()); if (!self::$decoder->getElementEndTag()) { return false; } } if (!self::$decoder->getElementEndTag()) { return false; } } $e = self::$decoder->peek(); if ($e[EN_TYPE] == EN_TYPE_ENDTAG) { self::$decoder->getElementEndTag(); break; } } } if (!self::$decoder->getElementEndTag()) { //store return false; } if (!self::$decoder->getElementEndTag()) { //search return false; } // get SearchProvider $searchprovider = ZPush::GetSearchProvider(); $status = SYNC_SEARCHSTATUS_SUCCESS; $rows = array(); // TODO support other searches if ($searchprovider->SupportsType($searchname)) { $storestatus = SYNC_SEARCHSTATUS_STORE_SUCCESS; try { if ($searchname == ISearchProvider::SEARCH_GAL) { //get search results from the searchprovider $rows = $searchprovider->GetGALSearchResults($searchquery, $searchrange); } elseif ($searchname == ISearchProvider::SEARCH_MAILBOX) { $rows = $searchprovider->GetMailboxSearchResults($cpo); } } catch (StatusException $stex) { $storestatus = $stex->getCode(); } } else { $rows = array('searchtotal' => 0); $status = SYNC_SEARCHSTATUS_SERVERERROR; ZLog::Write(LOGLEVEL_WARN, sprintf("Searchtype '%s' is not supported.", $searchname)); self::$topCollector->AnnounceInformation(sprintf("Unsupported type '%s''", $searchname), true); } $searchprovider->Disconnect(); self::$topCollector->AnnounceInformation(sprintf("'%s' search found %d results", $searchname, $rows['searchtotal']), true); self::$encoder->startWBXML(); self::$encoder->startTag(SYNC_SEARCH_SEARCH); self::$encoder->startTag(SYNC_SEARCH_STATUS); self::$encoder->content($status); self::$encoder->endTag(); if ($status == SYNC_SEARCHSTATUS_SUCCESS) { self::$encoder->startTag(SYNC_SEARCH_RESPONSE); self::$encoder->startTag(SYNC_SEARCH_STORE); self::$encoder->startTag(SYNC_SEARCH_STATUS); self::$encoder->content($storestatus); self::$encoder->endTag(); if (isset($rows['range'])) { $searchrange = $rows['range']; unset($rows['range']); } if (isset($rows['searchtotal'])) { $searchtotal = $rows['searchtotal']; unset($rows['searchtotal']); } if ($searchname == ISearchProvider::SEARCH_GAL) { if (is_array($rows) && !empty($rows)) { foreach ($rows as $u) { self::$encoder->startTag(SYNC_SEARCH_RESULT); self::$encoder->startTag(SYNC_SEARCH_PROPERTIES); self::$encoder->startTag(SYNC_GAL_DISPLAYNAME); self::$encoder->content(isset($u[SYNC_GAL_DISPLAYNAME]) ? $u[SYNC_GAL_DISPLAYNAME] : "No name"); self::$encoder->endTag(); if (isset($u[SYNC_GAL_PHONE])) { self::$encoder->startTag(SYNC_GAL_PHONE); self::$encoder->content($u[SYNC_GAL_PHONE]); self::$encoder->endTag(); } if (isset($u[SYNC_GAL_OFFICE])) { self::$encoder->startTag(SYNC_GAL_OFFICE); self::$encoder->content($u[SYNC_GAL_OFFICE]); self::$encoder->endTag(); } if (isset($u[SYNC_GAL_TITLE])) { self::$encoder->startTag(SYNC_GAL_TITLE); self::$encoder->content($u[SYNC_GAL_TITLE]); self::$encoder->endTag(); } if (isset($u[SYNC_GAL_COMPANY])) { self::$encoder->startTag(SYNC_GAL_COMPANY); self::$encoder->content($u[SYNC_GAL_COMPANY]); self::$encoder->endTag(); } if (isset($u[SYNC_GAL_ALIAS])) { self::$encoder->startTag(SYNC_GAL_ALIAS); self::$encoder->content($u[SYNC_GAL_ALIAS]); self::$encoder->endTag(); } // Always send the firstname, even empty. Nokia needs this to display the entry self::$encoder->startTag(SYNC_GAL_FIRSTNAME); self::$encoder->content(isset($u[SYNC_GAL_FIRSTNAME]) ? $u[SYNC_GAL_FIRSTNAME] : ""); self::$encoder->endTag(); self::$encoder->startTag(SYNC_GAL_LASTNAME); self::$encoder->content(isset($u[SYNC_GAL_LASTNAME]) ? $u[SYNC_GAL_LASTNAME] : "No name"); self::$encoder->endTag(); if (isset($u[SYNC_GAL_HOMEPHONE])) { self::$encoder->startTag(SYNC_GAL_HOMEPHONE); self::$encoder->content($u[SYNC_GAL_HOMEPHONE]); self::$encoder->endTag(); } if (isset($u[SYNC_GAL_MOBILEPHONE])) { self::$encoder->startTag(SYNC_GAL_MOBILEPHONE); self::$encoder->content($u[SYNC_GAL_MOBILEPHONE]); self::$encoder->endTag(); } self::$encoder->startTag(SYNC_GAL_EMAILADDRESS); self::$encoder->content(isset($u[SYNC_GAL_EMAILADDRESS]) ? $u[SYNC_GAL_EMAILADDRESS] : ""); self::$encoder->endTag(); self::$encoder->endTag(); //result self::$encoder->endTag(); //properties } } } elseif ($searchname == ISearchProvider::SEARCH_MAILBOX) { foreach ($rows as $u) { self::$encoder->startTag(SYNC_SEARCH_RESULT); self::$encoder->startTag(SYNC_FOLDERTYPE); self::$encoder->content($u['class']); self::$encoder->endTag(); self::$encoder->startTag(SYNC_SEARCH_LONGID); self::$encoder->content($u['longid']); self::$encoder->endTag(); self::$encoder->startTag(SYNC_FOLDERID); self::$encoder->content($u['folderid']); self::$encoder->endTag(); self::$encoder->startTag(SYNC_SEARCH_PROPERTIES); $tmp = explode(":", $u['longid']); $message = self::$backend->Fetch($u['folderid'], $tmp[1], $cpo); $message->Encode(self::$encoder); self::$encoder->endTag(); //result self::$encoder->endTag(); //properties } } // it seems that android 4 requires range and searchtotal // or it won't display the search results if (isset($searchrange)) { self::$encoder->startTag(SYNC_SEARCH_RANGE); self::$encoder->content($searchrange); self::$encoder->endTag(); } if (isset($searchtotal) && $searchtotal > 0) { self::$encoder->startTag(SYNC_SEARCH_TOTAL); self::$encoder->content($searchtotal); self::$encoder->endTag(); } self::$encoder->endTag(); //store self::$encoder->endTag(); //response } self::$encoder->endTag(); //search return true; }
/** * 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; }
/** * Configures additional parameters for content selection * * @param ContentParameters $contentparameters * * @access public * @return boolean * @throws StatusException */ public function ConfigContentParameters($contentparameters) { $filtertype = $contentparameters->GetFilterType(); switch ($contentparameters->GetContentClass()) { case "Email": $this->cutoffdate = $filtertype ? Utils::GetCutOffDate($filtertype) : false; break; case "Calendar": $this->cutoffdate = $filtertype ? Utils::GetCutOffDate($filtertype) : false; break; default: case "Contacts": case "Tasks": $this->cutoffdate = false; break; } $this->contentClass = $contentparameters->GetContentClass(); }
/** * Returns all available data of a single message * * @param string $folderid * @param string $id * @param ContentParameters $contentparameters flag * * @access public * @return object(SyncObject) * @throws StatusException */ public function Fetch($folderid, $id, $contentparameters) { // get the entry id of the message $entryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($folderid), hex2bin($id)); if (!$entryid) { throw new StatusException(sprintf("BackendZarafa->Fetch('%s','%s'): Error getting entryid: 0x%X", $folderid, $id, mapi_last_hresult()), SYNC_STATUS_OBJECTNOTFOUND); } // open the message $message = mapi_msgstore_openentry($this->store, $entryid); if (!$message) { throw new StatusException(sprintf("BackendZarafa->Fetch('%s','%s'): Error, unable to open message: 0x%X", $folderid, $id, mapi_last_hresult()), SYNC_STATUS_OBJECTNOTFOUND); } // convert the mapi message into a SyncObject and return it $mapiprovider = new MAPIProvider($this->session, $this->store); // override truncation $contentparameters->SetTruncation(SYNC_TRUNCATION_ALL); // TODO check for body preferences return $mapiprovider->GetMessage($message, $contentparameters); }
/** * Called when the user moves an item on the PDA from one folder to another * * @param string $folderid id of the source folder * @param string $id id of the message * @param string $newfolderid id of the destination folder * @param ContentParameters $contentparameters * * @access public * @return boolean status of the operation * @throws StatusException could throw specific SYNC_MOVEITEMSSTATUS_* exceptions */ public function MoveMessage($folderid, $id, $newfolderid, $contentparameters) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->MoveMessage('%s','%s','%s')", $folderid, $id, $newfolderid)); $folderImapid = $this->getImapIdFromFolderId($folderid); $newfolderImapid = $this->getImapIdFromFolderId($newfolderid); if ($folderImapid == $newfolderImapid) { throw new StatusException(sprintf("BackendIMAP->MoveMessage('%s','%s','%s'): Error, destination folder is source folder. Canceling the move.", $folderid, $id, $newfolderid), SYNC_MOVEITEMSSTATUS_SAMESOURCEANDDEST); } $this->imap_reopen_folder($folderImapid); if ($this->imap_inside_cutoffdate(Utils::GetCutOffDate($contentparameters->GetFilterType()), $id)) { // read message flags $overview = @imap_fetch_overview($this->mbox, $id, FT_UID); if (!is_array($overview)) { throw new StatusException(sprintf("BackendIMAP->MoveMessage('%s','%s','%s'): Error, unable to retrieve overview of source message: %s", $folderid, $id, $newfolderid, imap_last_error()), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID); } else { // get next UID for destination folder // when moving a message we have to announce through ActiveSync the new messageID in the // destination folder. This is a "guessing" mechanism as IMAP does not inform that value. // when lots of simultaneous operations happen in the destination folder this could fail. // in the worst case the moved message is displayed twice on the mobile. $destStatus = imap_status($this->mbox, $this->server . $newfolderImapid, SA_ALL); if (!$destStatus) { throw new StatusException(sprintf("BackendIMAP->MoveMessage('%s','%s','%s'): Error, unable to open destination folder: %s", $folderid, $id, $newfolderid, imap_last_error()), SYNC_MOVEITEMSSTATUS_INVALIDDESTID); } $newid = $destStatus->uidnext; // move message $s1 = imap_mail_move($this->mbox, $id, $newfolderImapid, CP_UID); if (!$s1) { throw new StatusException(sprintf("BackendIMAP->MoveMessage('%s','%s','%s'): Error, copy to destination folder failed: %s", $folderid, $id, $newfolderid, imap_last_error()), SYNC_MOVEITEMSSTATUS_CANNOTMOVE); } // delete message in from-folder $s2 = imap_expunge($this->mbox); // open new folder $stat = $this->imap_reopen_folder($newfolderImapid); if (!$stat) { throw new StatusException(sprintf("BackendIMAP->MoveMessage('%s','%s','%s'): Error, opening the destination folder: %s", $folderid, $id, $newfolderid, imap_last_error()), SYNC_MOVEITEMSSTATUS_CANNOTMOVE); } // remove all flags $s3 = @imap_clearflag_full($this->mbox, $newid, "\\Seen \\Answered \\Flagged \\Deleted \\Draft", FT_UID); $newflags = ""; $move_to_trash = strcasecmp($newfolderImapid, $this->create_name_folder(IMAP_FOLDER_TRASH)) == 0; if ($overview[0]->seen || $move_to_trash && defined('IMAP_AUTOSEEN_ON_DELETE') && IMAP_AUTOSEEN_ON_DELETE == true) { $newflags .= "\\Seen"; } if ($overview[0]->flagged) { $newflags .= " \\Flagged"; } if ($overview[0]->answered) { $newflags .= " \\Answered"; } $s4 = @imap_setflag_full($this->mbox, $newid, $newflags, FT_UID); ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->MoveMessage('%s','%s','%s'): result s-move: '%s' s-expunge: '%s' unset-Flags: '%s' set-Flags: '%s'", $folderid, $id, $newfolderid, Utils::PrintAsString($s1), Utils::PrintAsString($s2), Utils::PrintAsString($s3), Utils::PrintAsString($s4))); // return the new id "as string" return $newid . ""; } } else { throw new StatusException(sprintf("BackendIMAP->MoveMessage(): Message is outside the sync range"), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID); } }
/** * 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; */ }
/** * Configures additional parameters used for content synchronization * * @param ContentParameters $contentparameters * * @access public * @return boolean * @throws StatusException */ public function ConfigContentParameters($contentparameters) { $filtertype = $contentparameters->GetFilterType(); switch ($contentparameters->GetContentClass()) { case "Email": $this->restriction = $filtertype || !Utils::CheckMapiExtVersion('7') ? MAPIUtils::GetEmailRestriction(Utils::GetCutOffDate($filtertype)) : false; break; case "Calendar": $this->restriction = $filtertype || !Utils::CheckMapiExtVersion('7') ? MAPIUtils::GetCalendarRestriction($this->store, Utils::GetCutOffDate($filtertype)) : false; break; default: case "Contacts": case "Tasks": $this->restriction = false; break; } $this->contentParameters = $contentparameters; }
/** * Called when the user moves an item on the PDA from one folder to another * * @param string $folderid id of the source folder * @param string $id id of the message * @param string $newfolderid id of the destination folder * @param ContentParameters $contentparameters * * @access public * @return boolean status of the operation * @throws StatusException could throw specific SYNC_MOVEITEMSSTATUS_* exceptions */ public function MoveMessage($folderid, $id, $newfolderid, $contentparameters) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->MoveMessage('%s','%s','%s')", $folderid, $id, $newfolderid)); $folderImapid = $this->getImapIdFromFolderId($folderid); // SDB: When iOS is configured to "Archive Message" (instead of Delete Message), it will send a "MoveItems" // command to the Exchange server and attempt to move it to a folder called "0/Archive". Instead, we trap that // and send it to "[Gmail]/All Mail" folder instead. Note, that when on iOS device and you trigger a move from // folder A to B, it will correctly move that email, including to all folders with "[Gmail]...". if ($newfolderid == "0/Archive") { $newfolderImapid = "[Gmail]/All Mail"; } else { $newfolderImapid = $this->getImapIdFromFolderId($newfolderid); } if ($folderImapid == $newfolderImapid) { throw new StatusException(sprintf("BackendIMAP->MoveMessage('%s','%s','%s'): Error, destination folder is source folder. Canceling the move.", $folderid, $id, $newfolderid), SYNC_MOVEITEMSSTATUS_SAMESOURCEANDDEST); } $this->imap_reopen_folder($folderImapid); if ($this->imap_inside_cutoffdate(Utils::GetCutOffDate($contentparameters->GetFilterType()), $id)) { // read message flags $overview = @imap_fetch_overview($this->mbox, $id, FT_UID); if (!is_array($overview)) { throw new StatusException(sprintf("BackendIMAP->MoveMessage('%s','%s','%s'): Error, unable to retrieve overview of source message: %s", $folderid, $id, $newfolderid, imap_last_error()), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID); } else { // get next UID for destination folder // when moving a message we have to announce through ActiveSync the new messageID in the // destination folder. This is a "guessing" mechanism as IMAP does not inform that value. // when lots of simultaneous operations happen in the destination folder this could fail. // in the worst case the moved message is displayed twice on the mobile. $destStatus = imap_status($this->mbox, $this->server . $newfolderImapid, SA_ALL); if (!$destStatus) { throw new StatusException(sprintf("BackendIMAP->MoveMessage('%s','%s','%s'): Error, unable to open destination folder: %s", $folderid, $id, $newfolderid, imap_last_error()), SYNC_MOVEITEMSSTATUS_INVALIDDESTID); } $newid = $destStatus->uidnext; // move message $s1 = imap_mail_move($this->mbox, $id, $newfolderImapid, CP_UID); if (!$s1) { throw new StatusException(sprintf("BackendIMAP->MoveMessage('%s','%s','%s'): Error, copy to destination folder failed: %s", $folderid, $id, $newfolderid, imap_last_error()), SYNC_MOVEITEMSSTATUS_CANNOTMOVE); } // delete message in from-folder $s2 = imap_expunge($this->mbox); // open new folder $stat = $this->imap_reopen_folder($newfolderImapid); if (!$s1) { throw new StatusException(sprintf("BackendIMAP->MoveMessage('%s','%s','%s'): Error, opening the destination folder: %s", $folderid, $id, $newfolderid, imap_last_error()), SYNC_MOVEITEMSSTATUS_CANNOTMOVE); } // remove all flags $s3 = @imap_clearflag_full($this->mbox, $newid, "\\Seen \\Answered \\Flagged \\Deleted \\Draft", FT_UID); $newflags = ""; if ($overview[0]->seen) { $newflags .= "\\Seen"; } if ($overview[0]->flagged) { $newflags .= " \\Flagged"; } if ($overview[0]->answered) { $newflags .= " \\Answered"; } $s4 = @imap_setflag_full($this->mbox, $newid, $newflags, FT_UID); ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->MoveMessage('%s','%s','%s'): result s-move: '%s' s-expunge: '%s' unset-Flags: '%s' set-Flags: '%s'", $folderid, $id, $newfolderid, Utils::PrintAsString($s1), Utils::PrintAsString($s2), Utils::PrintAsString($s3), Utils::PrintAsString($s4))); // return the new id "as string" return $newid . ""; } } else { throw new StatusException(sprintf("BackendIMAP->MoveMessage(): Message is outside the sync range"), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID); } }
/** * 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); } } }
/** * Configures additional parameters used for content synchronization * * @param ContentParameters $contentparameters * * @access public * @return boolean * @throws StatusException */ public function ConfigContentParameters($contentparameters) { $this->contentparameters = $contentparameters; $this->cutoffdate = Utils::GetCutOffDate($contentparameters->GetFilterType()); }
/** * 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; }
/** * 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); } } }
/** * 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; }