diff options
author | Joas Schilling <coding@schilljs.com> | 2016-10-17 13:21:44 +0300 |
---|---|---|
committer | Joas Schilling <coding@schilljs.com> | 2016-10-17 13:22:12 +0300 |
commit | 4531545c6102a06fee7ea9a31f65375a866c257c (patch) | |
tree | 607948d33f01d70960fe591900da6200d5119578 /lib/Mailbox.php | |
parent | 4d6f7675470ccc2657cca7f17578c0e207fcd0ec (diff) |
Use PSR-4 loading for normal files
Signed-off-by: Joas Schilling <coding@schilljs.com>
Diffstat (limited to 'lib/Mailbox.php')
-rw-r--r-- | lib/Mailbox.php | 482 |
1 files changed, 482 insertions, 0 deletions
diff --git a/lib/Mailbox.php b/lib/Mailbox.php new file mode 100644 index 000000000..cf8036083 --- /dev/null +++ b/lib/Mailbox.php @@ -0,0 +1,482 @@ +<?php + +/** + * @author Christoph Wurst <christoph@winzerhof-wurst.at> + * @author Clement Wong <mail@clement.hk> + * @author Jan-Christoph Borchardt <hey@jancborchardt.net> + * @author Lukas Reschke <lukas@owncloud.com> + * @author matiasdelellis <mati86dl@gmail.com> + * @author Robin McCorkell <rmccorkell@karoshi.org.uk> + * @author Thomas Imbreckx <zinks@iozero.be> + * @author Thomas I <thomas@oatr.be> + * @author Thomas Mueller <thomas.mueller@tmit.eu> + * @author Thomas Müller <thomas.mueller@tmit.eu> + * @author Maximilian Zellhofer <max.zellhofer@gmail.com> + * + * Mail + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OCA\Mail; + +use Horde_Imap_Client; +use Horde_Imap_Client_Exception; +use Horde_Imap_Client_Fetch_Query; +use Horde_Imap_Client_Ids; +use Horde_Imap_Client_Mailbox; +use Horde_Imap_Client_Search_Query; +use Horde_Imap_Client_Socket; +use OCA\Mail\Model\IMAPMessage; +use OCA\Mail\Service\IMailBox; + +class Mailbox implements IMailBox { + + /** + * @var Horde_Imap_Client_Socket + */ + protected $conn; + + /** + * @var array + */ + private $attributes; + + /** + * @var string + */ + private $specialRole; + + /** + * @var string + */ + private $displayName; + + /** + * @var string + */ + private $delimiter; + + /** + * @var Horde_Imap_Client_Mailbox + */ + protected $mailBox; + + /** + * @param Horde_Imap_Client_Socket $conn + * @param Horde_Imap_Client_Mailbox $mailBox + * @param array $attributes + * @param string $delimiter + */ + public function __construct($conn, $mailBox, $attributes, $delimiter='/') { + $this->conn = $conn; + $this->mailBox = $mailBox; + $this->attributes = $attributes; + $this->delimiter = $delimiter; + $this->getSpecialRoleFromAttributes(); + if ($this->specialRole === null) { + $this->guessSpecialRole(); + } + $this->makeDisplayName(); + } + + /** + * @param integer $from + * @param integer $count + * @param string $filter + */ + private function getSearchIds($from, $count, $filter) { + if ($filter instanceof Horde_Imap_Client_Search_Query) { + $query = $filter; + } else { + $query = new Horde_Imap_Client_Search_Query(); + if ($filter) { + $query->text($filter, false); + } + } + if ($this->getSpecialRole() !== 'trash') { + $query->flag(Horde_Imap_Client::FLAG_DELETED, false); + } + + try { + $result = $this->conn->search($this->mailBox, $query, ['sort' => [Horde_Imap_Client::SORT_DATE]]); + } catch (Horde_Imap_Client_Exception $e) { + // maybe the server's advertisment of SORT was a fake + // see https://github.com/nextcloud/mail/issues/50 + // try again without SORT + return $this->getFetchIds($from, $count); + } + + $ids = array_reverse($result['match']->ids); + if ($from >= 0 && $count >= 0) { + $ids = array_slice($ids, $from, $count); + } + return new \Horde_Imap_Client_Ids($ids, false); + } + + /** + * @param integer $from + * @param integer $count + */ + private function getFetchIds($from, $count) { + $q = new Horde_Imap_Client_Fetch_Query(); + $q->uid(); + $q->imapDate(); + + $result = $this->conn->fetch($this->mailBox, $q); + $uidMap = []; + foreach ($result as $r) { + $uidMap[$r->getUid()] = $r->getImapDate()->getTimeStamp(); + } + // sort by time + uasort($uidMap, function($a, $b) { + return $a < $b; + }); + if ($from >= 0 && $count >= 0) { + $uidMap = array_slice($uidMap, $from, $count, true); + } + return new \Horde_Imap_Client_Ids(array_keys($uidMap), false); + } + + public function getMessages($from = 0, $count = 2, $filter = '') { + if (!$this->conn->capability->query('SORT') && (is_null($filter) || $filter === '')) { + $ids = $this->getFetchIds($from, $count); + } else { + $ids = $this->getSearchIds($from, $count, $filter); + } + + $headers = []; + + $fetch_query = new Horde_Imap_Client_Fetch_Query(); + $fetch_query->envelope(); + $fetch_query->flags(); + $fetch_query->size(); + $fetch_query->uid(); + $fetch_query->imapDate(); + $fetch_query->structure(); + + $headers = array_merge($headers, [ + 'importance', + 'list-post', + 'x-priority' + ]); + $headers[] = 'content-type'; + + $fetch_query->headers('imp', $headers, [ + 'cache' => true, + 'peek' => true + ]); + + $options = ['ids' => $ids]; + // $list is an array of Horde_Imap_Client_Data_Fetch objects. + $headers = $this->conn->fetch($this->mailBox, $fetch_query, $options); + + ob_start(); // fix for Horde warnings + $messages = []; + foreach ($headers->ids() as $message_id) { + $header = $headers[$message_id]; + $message = new IMAPMessage($this->conn, $this->mailBox, $message_id, $header); + $messages[] = $message->jsonSerialize(); + } + ob_get_clean(); + + // sort by time + usort($messages, function($a, $b) { + return $a['dateInt'] < $b['dateInt']; + }); + + return $messages; + } + + /** + * @return array + */ + public function attributes() { + return $this->attributes; + } + + /** + * @param string $messageId + * @param bool $loadHtmlMessageBody + * @return IMAPMessage + */ + public function getMessage($messageId, $loadHtmlMessageBody = false) { + return new IMAPMessage($this->conn, $this->mailBox, $messageId, null, $loadHtmlMessageBody); + } + + /** + * @param int $flags + * @return array + */ + public function getStatus($flags = \Horde_Imap_Client::STATUS_ALL) { + return $this->conn->status($this->mailBox, $flags); + } + + /** + * @return int + */ + public function getTotalMessages() { + $status = $this->getStatus(\Horde_Imap_Client::STATUS_MESSAGES); + return (int) $status['messages']; + } + + protected function makeDisplayName() { + $parts = explode($this->delimiter, $this->mailBox->utf8, 2); + + if (count($parts) > 1) { + $displayName = $parts[1]; + } elseif (strtolower($this->mailBox->utf8) === 'inbox') { + $displayName = 'Inbox'; + } else { + $displayName = $this->mailBox->utf8; + } + + $this->displayName = $displayName; + } + + public function getFolderId() { + return $this->mailBox->utf8; + } + + /** + * @return string + */ + public function getParent() { + $folderId = $this->getFolderId(); + $parts = explode($this->delimiter, $folderId, 2); + + if (count($parts) > 1) { + return $parts[0]; + } + + return null; + } + + /** + * @return string + */ + public function getSpecialRole() { + return $this->specialRole; + } + + /** + * @return string + */ + public function getDisplayName() { + return $this->displayName; + } + + /** + * @param string $displayName + */ + public function setDisplayName($displayName) { + $this->displayName = $displayName; + } + + /** + * @param integer $accountId + * @return array + */ + public function serialize($accountId, $status = null) { + $displayName = $this->getDisplayName(); + try { + if (is_null($status)) { + $status = $this->getStatus(); + } + $total = $status['messages']; + $specialRole = $this->getSpecialRole(); + $unseen = ($specialRole === 'trash') ? 0 : $status['unseen']; + $isEmpty = ($total === 0); + $noSelect = in_array('\\noselect', $this->attributes); + $parentId = $this->getParent(); + $parentId = ($parentId !== null) ? base64_encode($parentId) : null; + return [ + 'id' => base64_encode($this->getFolderId()), + 'parent' => $parentId, + 'name' => $displayName, + 'specialRole' => $specialRole, + 'unseen' => $unseen, + 'total' => $total, + 'isEmpty' => $isEmpty, + 'accountId' => $accountId, + 'noSelect' => $noSelect, + 'uidvalidity' => $status['uidvalidity'], + 'uidnext' => $status['uidnext'], + 'delimiter' => $this->delimiter + ]; + } catch (Horde_Imap_Client_Exception $e) { + return [ + 'id' => base64_encode($this->getFolderId()), + 'parent' => null, + 'name' => $displayName, + 'specialRole' => null, + 'unseen' => 0, + 'total' => 0, + 'error' => $e->getMessage(), + 'isEmpty' => true, + 'accountId' => $accountId, + 'noSelect' => true + ]; + } + } + /** + * Get the special use role of the mailbox + * + * This method reads the attributes sent by the server + * + */ + protected function getSpecialRoleFromAttributes() { + /* + * @todo: support multiple attributes on same folder + * "any given server or message store may support + * any combination of the attributes" + * https://tools.ietf.org/html/rfc6154 + */ + $result = null; + if (is_array($this->attributes)) { + /* Convert attributes to lowercase, because gmail + * returns them as lowercase (eg. \trash and not \Trash) + */ + $specialUseAttributes = [ + strtolower(Horde_Imap_Client::SPECIALUSE_ALL), + strtolower(Horde_Imap_Client::SPECIALUSE_ARCHIVE), + strtolower(Horde_Imap_Client::SPECIALUSE_DRAFTS), + strtolower(Horde_Imap_Client::SPECIALUSE_FLAGGED), + strtolower(Horde_Imap_Client::SPECIALUSE_JUNK), + strtolower(Horde_Imap_Client::SPECIALUSE_SENT), + strtolower(Horde_Imap_Client::SPECIALUSE_TRASH) + ]; + + $attributes = array_map(function($n) { + return strtolower($n); + }, $this->attributes); + + foreach ($specialUseAttributes as $attr) { + if (in_array($attr, $attributes)) { + $result = ltrim($attr, '\\'); + break; + } + } + + } + + $this->specialRole = $result; + } + + /** + * Assign a special role to this mailbox based on its name + */ + protected function guessSpecialRole() { + + $specialFoldersDict = [ + 'inbox' => ['inbox'], + 'sent' => ['sent', 'sent items', 'sent messages', 'sent-mail', 'sentmail'], + 'drafts' => ['draft', 'drafts'], + 'archive' => ['archive', 'archives'], + 'trash' => ['deleted messages', 'trash'], + 'junk' => ['junk', 'spam', 'bulk mail'], + ]; + + $lowercaseExplode = explode($this->delimiter, $this->getFolderId(), 2); + $lowercaseId = strtolower(array_pop($lowercaseExplode)); + $result = null; + foreach ($specialFoldersDict as $specialRole => $specialNames) { + if (in_array($lowercaseId, $specialNames)) { + $result = $specialRole; + break; + } + } + + $this->specialRole = $result; + } + + /** + * @param int $messageId + * @param string $attachmentId + * @return Attachment + */ + public function getAttachment($messageId, $attachmentId) { + return new Attachment($this->conn, $this->mailBox, $messageId, $attachmentId); + } + + /** + * @param string $rawBody + * @param array $flags + */ + public function saveMessage($rawBody, $flags = []) { + + $this->conn->append($this->mailBox, [ + [ + 'data' => $rawBody, + 'flags' => $flags + ] + ]); + } + + /** + * Save draft + * + * @param string $rawBody + * @return int UID of the saved draft + */ + public function saveDraft($rawBody) { + + $uids = $this->conn->append($this->mailBox, [ + [ + 'data' => $rawBody, + 'flags' => [ + Horde_Imap_Client::FLAG_DRAFT, + Horde_Imap_Client::FLAG_SEEN + ] + ] + ]); + return $uids->current(); + } + + /** + * @param int $uid + * @param string $flag + * @param boolean $add + */ + public function setMessageFlag($uid, $flag, $add) { + $options = [ + 'ids' => new Horde_Imap_Client_Ids($uid) + ]; + if ($add) { + $options['add'] = [$flag]; + } else { + $options['remove'] = [$flag]; + } + $this->conn->store($this->mailBox, $options); + } + + /** + * @param $fromUid + * @param $toUid + * @return array + */ + public function getMessagesSince($fromUid, $toUid) { + $query = new Horde_Imap_Client_Search_Query(); + $query->ids(new Horde_Imap_Client_Ids("$fromUid:$toUid")); + return $this->getMessages(-1, -1, $query); + } + + /** + * @return Horde_Imap_Client_Mailbox + */ + public function getHordeMailBox() { + return $this->mailBox; + } + +} |