├── .gitignore ├── README.md ├── tests ├── MailLibrary │ ├── Connection.connect.phpt │ ├── Mail.headers.body.phpt │ ├── Selection.getMailIds.phpt │ ├── Connection.mailboxes.phpt │ ├── Driver.filters.phpt │ └── TestDriver.php └── bootstrap.php ├── MailLibrary ├── Structures │ ├── IStructure.php │ └── ImapStructure.php ├── exceptions.php ├── Mailbox.php ├── Extensions │ └── MailLibraryExtension.php ├── Attachment.php ├── loader.php ├── Contact.php ├── ContactList.php ├── Drivers │ ├── IDriver.php │ └── ImapDriver.php ├── Connection.php ├── Selection.php └── Mail.php ├── composer.json └── LICENCE /.gitignore: -------------------------------------------------------------------------------- 1 | # PhpStorm stuff 2 | .idea 3 | 4 | # Composer 5 | vendor 6 | composer.lock 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Client 2 | ====== 3 | 4 | A PHP library for downloading mails from server. 5 | 6 | Documentation can be found at http://php-mail-client.github.io/Client/. 7 | -------------------------------------------------------------------------------- /tests/MailLibrary/Connection.connect.phpt: -------------------------------------------------------------------------------- 1 | connect(); 8 | 9 | Assert::equal(TRUE, $connection->isConnected()); -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | getMailbox('x')->getMails()->fetchAll()[1]; 8 | 9 | Assert::equal(array('name' => md5(1), 'id' => 1), $mail->getHeaders()); 10 | Assert::equal(md5(1), $mail->name); 11 | Assert::equal(str_repeat('body', 10), $mail->getBody()); 12 | Assert::equal(str_repeat('textbody', 10), $mail->getTextBody()); 13 | Assert::equal(str_repeat('htmlbody', 10), $mail->getHtmlBody()); -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "php-mail-client/client", 3 | "description": "Full featured PHP mail client. Create, edit, delete, move and set flags to messages with ease! Drivers for IMAP4 and Microsoft Exchange Web Services available.", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "Tomáš Blatný", 8 | "email": "blatny.tomas@seznam.cz", 9 | "homepage": "http://tomasblatny.eu" 10 | } 11 | ], 12 | "autoload": { 13 | "files": [ 14 | "MailLibrary/loader.php" 15 | ] 16 | }, 17 | "require": { 18 | "php": ">= 5.4.0" 19 | }, 20 | "require-dev": { 21 | "nette/tester": "@dev" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/MailLibrary/Selection.getMailIds.phpt: -------------------------------------------------------------------------------- 1 | getMailbox('x'); 9 | 10 | Assert::equal(array(1 => new Mail($connection, $mailbox, 1), 2 => new Mail($connection, $mailbox, 2)), $mailbox->getMails()->fetchAll()); 11 | Assert::equal(array(1 => new Mail($connection, $mailbox, 1)), $mailbox->getMails()->where(Mail::FROM, 'mom')->fetchAll()); 12 | Assert::equal(new Mail($connection, $mailbox, 1), $mailbox->getMails()->where(Mail::FROM, 'mom')[1]); -------------------------------------------------------------------------------- /MailLibrary/Mailbox.php: -------------------------------------------------------------------------------- 1 | connection = $connection; 22 | $this->name = $name; 23 | } 24 | 25 | public function getName() 26 | { 27 | return $this->name; 28 | } 29 | 30 | public function getMails() 31 | { 32 | return new Selection($this->connection, $this); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/MailLibrary/Connection.mailboxes.phpt: -------------------------------------------------------------------------------- 1 | deleteMailbox('x'); 9 | Assert::equal(array(), $connection->getMailboxes()); 10 | 11 | $mailbox = new Mailbox($connection, 'x'); 12 | $created = $connection->createMailbox('x'); 13 | Assert::equal($mailbox, $created); 14 | Assert::equal(array('x' => $mailbox), $connection->getMailboxes()); 15 | 16 | $mailbox = new Mailbox($connection, 'y'); 17 | $renamed = $connection->renameMailbox('x', 'y'); 18 | Assert::equal($mailbox, $renamed); 19 | Assert::equal(array('y' => $mailbox), $connection->getMailboxes()); 20 | 21 | $switched = $connection->switchMailbox('y'); 22 | Assert::equal($mailbox, $switched); 23 | 24 | $connection->deleteMailbox('y'); 25 | Assert::equal(array(), $connection->getMailboxes()); -------------------------------------------------------------------------------- /MailLibrary/Extensions/MailLibraryExtension.php: -------------------------------------------------------------------------------- 1 | getConfig(array( 14 | 'imap' => array( 15 | 'username' => '', 16 | 'password' => '', 17 | 'host' => 'localhost', 18 | 'port' => 993, 19 | 'ssl' => 'true', 20 | ), 21 | )); 22 | 23 | $config = $config['imap']; 24 | 25 | $builder = $this->getContainerBuilder(); 26 | 27 | $builder->addDefinition($this->prefix('connection')) 28 | ->setClass('greeny\\MailLibrary\\Connection'); 29 | 30 | $builder->addDefinition($this->prefix('imap')) 31 | ->setClass('greeny\\MailLibrary\\Drivers\\ImapDriver', array( 32 | $config['username'], 33 | $config['password'], 34 | $config['host'], 35 | $config['port'], 36 | $config['ssl'], 37 | )); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /MailLibrary/Attachment.php: -------------------------------------------------------------------------------- 1 | name = $name; 21 | $this->content = $content; 22 | $this->type = $type; 23 | } 24 | 25 | /** 26 | * @return string 27 | */ 28 | public function getName() 29 | { 30 | return $this->name; 31 | } 32 | 33 | /** 34 | * @param string $filename 35 | * @return bool 36 | */ 37 | public function saveAs($filename) 38 | { 39 | return file_put_contents($filename, $this->content) !== FALSE; 40 | } 41 | 42 | /** 43 | * @return string 44 | */ 45 | public function getContent() 46 | { 47 | return $this->content; 48 | } 49 | 50 | /** 51 | * @return string 52 | */ 53 | public function getType() 54 | { 55 | return $this->type; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 php-mail-client 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tests/MailLibrary/Driver.filters.phpt: -------------------------------------------------------------------------------- 1 | getDriver(); 8 | 9 | /** 10 | * Helper class for storing data 11 | */ 12 | class TestFilter { 13 | public $key; 14 | public $exception; 15 | public $value; 16 | 17 | public function __construct($key, $value, $exception) 18 | { 19 | $this->key = $key; 20 | $this->value = $value; 21 | $this->exception = $exception; 22 | } 23 | } 24 | 25 | $exceptions = array( 26 | new TestFilter('ABC', NULL, "Invalid filter key 'ABC'."), 27 | new TestFilter('SUBJECT', NULL, "Invalid value type for filter 'SUBJECT', expected string, got NULL."), 28 | new TestFilter('SINCE', NULL, "Invalid value type for filter 'SINCE', expected DateTime or timestamp, or textual representation of date, got NULL."), 29 | new TestFilter('SEEN', NULL, "Invalid value type for filter 'SEEN', expected bool, got NULL."), 30 | new TestFilter('OLD', TRUE, "Cannot assign value to filter 'OLD'."), 31 | ); 32 | 33 | foreach($exceptions as $exception) { 34 | Assert::exception(function()use($driver, $exception){ 35 | $driver->checkFilter($exception->key, $exception->value); 36 | }, '\\greeny\\MailLibrary\\DriverException', $exception->exception); 37 | } -------------------------------------------------------------------------------- /MailLibrary/loader.php: -------------------------------------------------------------------------------- 1 | 'Connection.php', 11 | 'greeny\maillibrary\mailbox' => 'Mailbox.php', 12 | 'greeny\maillibrary\selection' => 'Selection.php', 13 | 'greeny\maillibrary\mail' => 'Mail.php', 14 | 'greeny\maillibrary\contactlist' => 'ContactList.php', 15 | 'greeny\maillibrary\contact' => 'Contact.php', 16 | 'greeny\maillibrary\attachment' => 'Attachment.php', 17 | 'greeny\maillibrary\structures\istructure' => 'Structures/IStructure.php', 18 | 'greeny\maillibrary\structures\imapstructure' => 'Structures/ImapStructure.php', 19 | 'greeny\maillibrary\drivers\idriver' => 'Drivers/IDriver.php', 20 | 'greeny\maillibrary\drivers\imapdriver' => 'Drivers/ImapDriver.php', 21 | 'greeny\maillibrary\extensions\maillibraryextension' => 'Extensions/MailLibraryExtension.php', 22 | ); 23 | 24 | $type = ltrim(strtolower($type), '\\'); // PHP namespace bug #49143 25 | 26 | if (isset($paths[$type])) { 27 | require_once __DIR__ . '/' . $paths[$type]; 28 | } 29 | }); -------------------------------------------------------------------------------- /MailLibrary/Contact.php: -------------------------------------------------------------------------------- 1 | mailbox = $mailbox; 29 | $this->host = $host; 30 | $this->personal = $personal; 31 | $this->adl = $adl; 32 | } 33 | 34 | /** 35 | * @return string 36 | */ 37 | public function __toString() 38 | { 39 | $address = $this->getName() ? "\"" . $this->getName(). "\" " : ""; 40 | $address .= $this->getAdl() ? $this->getAdl().":" : ""; 41 | $address .= "<".$this->getEmail().">"; 42 | return $address; 43 | } 44 | 45 | /** 46 | * @return string 47 | */ 48 | public function getEmail() 49 | { 50 | return $this->mailbox."@".$this->host; 51 | } 52 | 53 | /** 54 | * @return string 55 | */ 56 | public function getName() 57 | { 58 | return $this->personal; 59 | } 60 | 61 | /** 62 | * @return string 63 | */ 64 | public function getAdl() 65 | { 66 | return $this->adl; 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /MailLibrary/ContactList.php: -------------------------------------------------------------------------------- 1 | contacts[] = new Contact($mailbox,$host,$personal,$adl); 21 | } 22 | 23 | public function build() 24 | { 25 | $return = array(); 26 | foreach($this->contacts as $contact) { 27 | $return[] = $contact->__toString(); 28 | } 29 | $this->builtContacts = $return; 30 | } 31 | 32 | public function getContacts() 33 | { 34 | return $this->builtContacts; 35 | } 36 | 37 | /** 38 | * @return array 39 | */ 40 | public function getContactsObjects() 41 | { 42 | return $this->contacts; 43 | } 44 | 45 | public function __toString() 46 | { 47 | return implode(', ', $this->builtContacts); 48 | } 49 | 50 | public function current() 51 | { 52 | return current($this->builtContacts); 53 | } 54 | 55 | public function next() 56 | { 57 | next($this->builtContacts); 58 | } 59 | 60 | public function key() 61 | { 62 | return key($this->builtContacts); 63 | } 64 | 65 | public function valid() 66 | { 67 | $key = key($this->builtContacts); 68 | return ($key !== NULL && $key !== FALSE); 69 | } 70 | 71 | public function rewind() 72 | { 73 | reset($this->builtContacts); 74 | } 75 | 76 | /** 77 | * @return int 78 | */ 79 | public function count() 80 | { 81 | return count($this->builtContacts); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /MailLibrary/Drivers/IDriver.php: -------------------------------------------------------------------------------- 1 | value 86 | */ 87 | function getHeaders($mailId); 88 | 89 | /** 90 | * Creates structure for mail 91 | * @param int $mailId 92 | * @param Mailbox $mailbox 93 | * @return IStructure 94 | */ 95 | function getStructure($mailId, Mailbox $mailbox); 96 | 97 | /** 98 | * Gets part of body 99 | * @param int $mailId 100 | * @param array $data 101 | * @return string 102 | */ 103 | function getBody($mailId, array $data); 104 | 105 | /** 106 | * Gets flags for mail 107 | * @param int $mailId 108 | * @return array 109 | */ 110 | function getFlags($mailId); 111 | 112 | /** 113 | * Sets one flag for mail 114 | * @param int $mailId 115 | * @param string $flag 116 | * @param bool $value 117 | * @throws DriverException 118 | */ 119 | function setFlag($mailId, $flag, $value); 120 | 121 | /** 122 | * Copies mail to another mailbox 123 | * @param int $mailId 124 | * @param string $toMailbox 125 | * @throws DriverException 126 | */ 127 | function copyMail($mailId, $toMailbox); 128 | 129 | /** 130 | * Moves mail to another mailbox 131 | * @param int $mailId 132 | * @param string $toMailbox 133 | * @throws DriverException 134 | */ 135 | function moveMail($mailId, $toMailbox); 136 | 137 | /** 138 | * Deletes mail 139 | * @param int $mailId 140 | * @throws DriverException 141 | */ 142 | function deleteMail($mailId); 143 | } -------------------------------------------------------------------------------- /MailLibrary/Connection.php: -------------------------------------------------------------------------------- 1 | driver = $driver; 26 | } 27 | 28 | /** 29 | * @return bool 30 | */ 31 | public function isConnected() 32 | { 33 | return $this->connected; 34 | } 35 | 36 | /** 37 | * Connects to the server 38 | * @return Connection 39 | * @throws ConnectionException 40 | */ 41 | public function connect() 42 | { 43 | if(!$this->connected) { 44 | try { 45 | $this->driver->connect(); 46 | $this->connected = TRUE; 47 | } catch(DriverException $e) { 48 | throw new ConnectionException("Cannot connect to server.", $e->getCode(), $e); 49 | } 50 | } 51 | return $this; 52 | } 53 | 54 | /** 55 | * @return IDriver 56 | */ 57 | public function getDriver() 58 | { 59 | return $this->driver; 60 | } 61 | 62 | /** 63 | * Flushes changes to server 64 | * @return Connection 65 | * @throws DriverException 66 | */ 67 | public function flush() 68 | { 69 | $this->connected || $this->connect(); 70 | $this->driver->flush(); 71 | return $this; 72 | } 73 | 74 | /** 75 | * Gets all mailboxes 76 | * @return Mailbox[] 77 | */ 78 | public function getMailboxes() 79 | { 80 | $this->mailboxes !== NULL || $this->initializeMailboxes(); 81 | return $this->mailboxes; 82 | } 83 | 84 | /** 85 | * Gets mailbox by name 86 | * @param $name 87 | * @return Mailbox 88 | * @throws ConnectionException 89 | */ 90 | public function getMailbox($name) 91 | { 92 | $this->mailboxes !== NULL || $this->initializeMailboxes(); 93 | if(isset($this->mailboxes[$name])) { 94 | return $this->mailboxes[$name]; 95 | } else { 96 | throw new ConnectionException("Mailbox '$name' does not exist."); 97 | } 98 | } 99 | 100 | /** 101 | * Creates mailbox 102 | * @param string $name 103 | * @return Mailbox 104 | * @throws DriverException 105 | */ 106 | public function createMailbox($name) 107 | { 108 | $this->connected || $this->connect(); 109 | $this->driver->createMailbox($name); 110 | $this->mailboxes = NULL; 111 | return $this->getMailbox($name); 112 | } 113 | 114 | /** 115 | * Renames mailbox 116 | * @param string $from 117 | * @param string $to 118 | * @return Mailbox 119 | * @throws DriverException 120 | */ 121 | public function renameMailbox($from, $to) 122 | { 123 | $this->connected || $this->connect(); 124 | $this->driver->renameMailbox($from, $to); 125 | $this->mailboxes = NULL; 126 | return $this->getMailbox($to); 127 | } 128 | 129 | /** 130 | * Deletes mailbox 131 | * @param string $name 132 | * @throws DriverException 133 | */ 134 | public function deleteMailbox($name) 135 | { 136 | $this->connected || $this->connect(); 137 | $this->driver->deleteMailbox($name); 138 | $this->mailboxes = NULL; 139 | } 140 | 141 | /** 142 | * Switches currently using mailbox 143 | * @param string $name 144 | * @throws DriverException 145 | * @return Mailbox 146 | */ 147 | public function switchMailbox($name) 148 | { 149 | $this->connected || $this->connect(); 150 | $this->driver->switchMailbox($name); 151 | return $this->getMailbox($name); 152 | } 153 | 154 | /** 155 | * Initializes mailboxes 156 | */ 157 | protected function initializeMailboxes() 158 | { 159 | $this->connected || $this->connect(); 160 | $this->mailboxes = array(); 161 | foreach($this->driver->getMailboxes() as $name) { 162 | $this->mailboxes[$name] = new Mailbox($this, $name); 163 | } 164 | } 165 | } -------------------------------------------------------------------------------- /tests/MailLibrary/TestDriver.php: -------------------------------------------------------------------------------- 1 | '%bANSWERED', 14 | Mail::BCC => 'BCC "%s"', 15 | Mail::BEFORE => 'BEFORE "%d"', 16 | Mail::BODY => 'BODY "%s"', 17 | Mail::CC => 'CC "%s"', 18 | Mail::DELETED => '%bDELETED', 19 | Mail::FLAGGED => '%bFLAGGED', 20 | Mail::FROM => 'FROM "%s"', 21 | Mail::KEYWORD => 'KEYWORD "%s"', 22 | Mail::NEW_MESSAGES => 'NEW', 23 | Mail::NOT_KEYWORD => 'UNKEYWORD "%s"', 24 | Mail::OLD_MESSAGES => 'OLD', 25 | Mail::ON => 'ON "%d"', 26 | Mail::RECENT => 'RECENT', 27 | Mail::SEEN => '%bSEEN', 28 | Mail::SINCE => 'SINCE "%d"', 29 | Mail::SUBJECT => 'SUBJECT "%s"', 30 | Mail::TEXT => 'TEXT "%s"', 31 | Mail::TO => 'TO "%s"', 32 | ); 33 | 34 | protected $mailboxes = array('x'); 35 | 36 | public function connect() {} 37 | 38 | public function flush() {} 39 | 40 | public function getMailboxes() 41 | { 42 | return $this->mailboxes; 43 | } 44 | 45 | public function createMailbox($name) 46 | { 47 | $this->mailboxes[] = $name; 48 | } 49 | 50 | public function renameMailbox($from, $to) 51 | { 52 | foreach($this->mailboxes as $key => $mailbox) { 53 | if($mailbox == $from) { 54 | $this->mailboxes[$key] = $to; 55 | return; 56 | } 57 | } 58 | } 59 | 60 | public function deleteMailbox($name) 61 | { 62 | foreach($this->mailboxes as $key => $mailbox) { 63 | if($mailbox == $name) { 64 | unset($this->mailboxes[$key]); 65 | return; 66 | } 67 | } 68 | } 69 | 70 | public function switchMailbox($name) {} 71 | 72 | public function getMailIds(array $filters, $limit = 0, $offset = 0, $orderBy = Mail::ORDER_DATE, $orderType = 'ASC') 73 | { 74 | if(count($filters)) return array(1); 75 | else return array(1, 2); 76 | } 77 | 78 | public function checkFilter($key, $value = NULL) 79 | { 80 | if(!in_array($key, array_keys(self::$filterTable))) { 81 | throw new DriverException("Invalid filter key '$key'."); 82 | } 83 | $filtered = self::$filterTable[$key]; 84 | if(strpos($filtered, '%s') !== FALSE) { 85 | if(!is_string($value)) { 86 | throw new DriverException("Invalid value type for filter '$key', expected string, got ".gettype($value)."."); 87 | } 88 | } else if(strpos($filtered, '%d') !== FALSE) { 89 | if(!($value instanceof DateTime) && !is_int($value) && !strtotime($value)) { 90 | throw new DriverException("Invalid value type for filter '$key', expected DateTime or timestamp, or textual representation of date, got ".gettype($value)."."); 91 | } 92 | } else if(strpos($filtered, '%b') !== FALSE) { 93 | if(!is_bool($value)) { 94 | throw new DriverException("Invalid value type for filter '$key', expected bool, got ".gettype($value)."."); 95 | } 96 | } else if($value !== NULL) { 97 | throw new DriverException("Cannot assign value to filter '$key'."); 98 | } 99 | } 100 | 101 | public function getHeaders($mailId) 102 | { 103 | return array( 104 | 'name' => md5($mailId), 105 | 'id' => $mailId, 106 | ); 107 | } 108 | 109 | public function getStructure($mailId, \greeny\MailLibrary\Mailbox $mailbox) 110 | { 111 | return new TestStructure(); 112 | } 113 | 114 | public function getBody($mailId, array $partIds) 115 | { 116 | return str_repeat($mailId, 10); 117 | } 118 | 119 | public function getFlags($mailId) 120 | { 121 | 122 | } 123 | 124 | function setFlag($mailId, $flag, $value) 125 | { 126 | 127 | } 128 | 129 | function copyMail($mailId, $toMailbox) 130 | { 131 | 132 | } 133 | 134 | function moveMail($mailId, $toMailbox) 135 | { 136 | 137 | } 138 | 139 | function deleteMail($mailId) 140 | { 141 | 142 | } 143 | } 144 | 145 | class TestStructure implements IStructure { 146 | public function getBody() 147 | { 148 | return str_repeat('body', 10); 149 | } 150 | 151 | /** 152 | * @return string 153 | */ 154 | public function getHtmlBody() 155 | { 156 | return str_repeat('htmlbody', 10); 157 | } 158 | 159 | /** 160 | * @return string 161 | */ 162 | public function getTextBody() 163 | { 164 | return str_repeat('textbody', 10); 165 | } 166 | 167 | /** 168 | * @return \greeny\MailLibrary\Attachment[] 169 | */ 170 | public function getAttachments() {} 171 | } -------------------------------------------------------------------------------- /MailLibrary/Structures/ImapStructure.php: -------------------------------------------------------------------------------- 1 | 'text', 31 | self::TYPE_MULTIPART => 'multipart', 32 | self::TYPE_MESSAGE => 'message', 33 | self::TYPE_APPLICATION => 'application', 34 | self::TYPE_AUDIO => 'audio', 35 | self::TYPE_IMAGE => 'image', 36 | self::TYPE_VIDEO => 'video', 37 | self::TYPE_OTHER => 'other', 38 | ); 39 | 40 | /** @var \greeny\MailLibrary\Drivers\ImapDriver */ 41 | protected $driver; 42 | 43 | /** @var int */ 44 | protected $id; 45 | 46 | /** @var array */ 47 | protected $htmlBodyIds = array(); 48 | 49 | /** @var array */ 50 | protected $textBodyIds = array(); 51 | 52 | /** @var array */ 53 | protected $attachmentsIds = array(); 54 | 55 | /** @var string */ 56 | protected $htmlBody = NULL; 57 | 58 | /** @var string */ 59 | protected $textBody = NULL; 60 | 61 | /** @var Attachment[] */ 62 | protected $attachments = NULL; 63 | 64 | /** @var Mailbox */ 65 | protected $mailbox; 66 | 67 | /** 68 | * @param ImapDriver $driver 69 | * @param object $structure 70 | * @param int $mailId 71 | * @param Mailbox $mailbox 72 | */ 73 | public function __construct(ImapDriver $driver, $structure, $mailId, Mailbox $mailbox) 74 | { 75 | $this->driver = $driver; 76 | $this->id = $mailId; 77 | $this->mailbox = $mailbox; 78 | if(!isset($structure->parts)) { 79 | $this->addStructurePart($structure, '0'); 80 | } else { 81 | foreach((array)$structure->parts as $id => $part) { 82 | $this->addStructurePart($part, (string) ($id + 1)); 83 | } 84 | } 85 | } 86 | 87 | /** 88 | * @return string 89 | */ 90 | public function getBody() 91 | { 92 | return count($this->htmlBodyIds) ? $this->getHtmlBody() : $this->getTextBody(); 93 | } 94 | 95 | /** 96 | * @return string 97 | */ 98 | public function getHtmlBody() 99 | { 100 | if($this->htmlBody === NULL) { 101 | $this->driver->switchMailbox($this->mailbox->getName()); 102 | return $this->htmlBody = $this->driver->getBody($this->id, $this->htmlBodyIds); 103 | } else { 104 | return $this->htmlBody; 105 | } 106 | } 107 | 108 | /** 109 | * @return string 110 | */ 111 | public function getTextBody() 112 | { 113 | if($this->textBody === NULL) { 114 | $this->driver->switchMailbox($this->mailbox->getName()); 115 | return $this->textBody = $this->driver->getBody($this->id, $this->textBodyIds); 116 | } else { 117 | return $this->textBody; 118 | } 119 | } 120 | 121 | /** 122 | * @return Attachment[] 123 | */ 124 | public function getAttachments() 125 | { 126 | $this->driver->switchMailbox($this->mailbox->getName()); 127 | if($this->attachments === NULL) { 128 | $this->attachments = array(); 129 | foreach($this->attachmentsIds as $attachmentData) { 130 | $this->attachments[] = new Attachment($attachmentData['name'], $this->driver->getBody($this->id, array($attachmentData)), $attachmentData['type']); 131 | } 132 | } 133 | return $this->attachments; 134 | } 135 | 136 | protected function addStructurePart($structure, $partId) 137 | { 138 | $type = $structure->type; 139 | $encoding = isset($structure->encoding) ? $structure->encoding : 'UTF-8'; 140 | $subtype = $structure->ifsubtype ? $structure->subtype : 'PLAIN'; 141 | 142 | $parameters = array(); 143 | if($structure->ifparameters) { 144 | foreach($structure->parameters as $parameter) { 145 | $parameters[strtolower($parameter->attribute)] = $parameter->value; 146 | } 147 | } 148 | if($structure->ifdparameters) { 149 | foreach($structure->dparameters as $parameter) { 150 | $parameters[strtolower($parameter->attribute)] = $parameter->value; 151 | } 152 | } 153 | 154 | if(isset($parameters['filename']) || isset($parameters['name'])) { 155 | $this->attachmentsIds[] = array( 156 | 'id' => $partId, 157 | 'encoding' => $encoding, 158 | 'name' => isset($parameters['filename']) ? $parameters['filename'] : $parameters['name'], 159 | 'type' => self::$typeTable[$type]. '/' . $subtype, 160 | ); 161 | } else if($type === self::TYPE_TEXT) { 162 | if($subtype === 'HTML') { 163 | $this->htmlBodyIds[] = array('id' => $partId, 'encoding' => $encoding); 164 | } else if($subtype === 'PLAIN') { 165 | $this->textBodyIds[] = array('id' => $partId, 'encoding' => $encoding); 166 | } 167 | } 168 | 169 | if(isset($structure->parts)) { 170 | foreach((array)$structure->parts as $id => $part) { 171 | $this->addStructurePart($part, (string)($partId.'.'.($id+1))); 172 | } 173 | } 174 | } 175 | } 176 | 177 | -------------------------------------------------------------------------------- /MailLibrary/Selection.php: -------------------------------------------------------------------------------- 1 | connection = $connection; 44 | $this->mailbox = $mailbox; 45 | } 46 | 47 | /** 48 | * Adds condition to selection 49 | * 50 | * @param string $key 51 | * @param string $value 52 | * @return $this 53 | */ 54 | public function where($key, $value = NULL) 55 | { 56 | $this->connection->getDriver()->checkFilter($key, $value); 57 | $this->filters[] = array('key' => $key, 'value' => $value); 58 | return $this; 59 | } 60 | 61 | /** 62 | * Adds limit (like SQL) 63 | * 64 | * @param int $limit 65 | * @return $this 66 | * @throws InvalidFilterValueException 67 | */ 68 | public function limit($limit) 69 | { 70 | $limit = (int) $limit; 71 | if($limit < 0) { 72 | throw new InvalidFilterValueException("Limit must be bigger or equal to 0, '$limit' given."); 73 | } 74 | $this->limit = $limit; 75 | return $this; 76 | } 77 | 78 | /** 79 | * Adds offset (like SQL) 80 | * 81 | * @param $offset 82 | * @return $this 83 | * @throws InvalidFilterValueException 84 | */ 85 | public function offset($offset) 86 | { 87 | $offset = (int) $offset; 88 | if($offset < 0) { 89 | throw new InvalidFilterValueException("Offset must be bigger or equal to 0, '$offset' given."); 90 | } 91 | $this->offset = $offset; 92 | return $this; 93 | } 94 | 95 | /** 96 | * Simplifies paginating. 97 | * 98 | * @param $page 99 | * @param $itemsPerPage 100 | * @return $this 101 | * @throws InvalidFilterValueException 102 | */ 103 | public function page($page, $itemsPerPage) 104 | { 105 | $page = (int) $page; 106 | $itemsPerPage = (int) $itemsPerPage; 107 | if($page <= 0) { 108 | throw new InvalidFilterValueException("Page must be at least 1, '$page' given."); 109 | } 110 | if($itemsPerPage <= 0) { 111 | throw new InvalidFilterValueException("Items per page must be at least 1, '$itemsPerPage' given."); 112 | } 113 | $this->offset(($page - 1) * $itemsPerPage); 114 | $this->limit($itemsPerPage); 115 | return $this; 116 | } 117 | 118 | public function order($by, $type = 'ASC') 119 | { 120 | $type = strtoupper($type); 121 | if(!in_array($type, array('ASC', 'DESC'))) { 122 | throw new InvalidFilterValueException("Sort type must be ASC or DESC, '$type' given."); 123 | } 124 | $this->orderBy = $by; 125 | $this->orderType = $type; 126 | return $this; 127 | } 128 | 129 | /** 130 | * Counts mails 131 | * 132 | * @return int 133 | */ 134 | public function countMails() 135 | { 136 | $this->mails !== NULL || $this->fetchMails(); 137 | return count($this->mails); 138 | } 139 | 140 | /** 141 | * Gets all mails filtered by conditions 142 | * 143 | * @return Mail[] 144 | */ 145 | public function fetchAll() 146 | { 147 | $this->mails !== NULL || $this->fetchMails(); 148 | return $this->mails; 149 | } 150 | 151 | /** 152 | * Fetches mail ids from server 153 | */ 154 | protected function fetchMails() 155 | { 156 | $this->connection->getDriver()->switchMailbox($this->mailbox->getName()); 157 | $ids = $this->connection->getDriver()->getMailIds($this->filters, $this->limit, $this->offset, $this->orderBy, $this->orderType); 158 | $i = 0; 159 | $this->mails = array(); 160 | $this->iterator = 0; 161 | $this->mailIndexes = array(); 162 | foreach($ids as $id) { 163 | $this->mails[$id] = new Mail($this->connection, $this->mailbox, $id); 164 | $this->mailIndexes[$i++] = $id; 165 | } 166 | } 167 | 168 | // INTERFACE ArrayAccess 169 | 170 | /** 171 | * @param int $offset 172 | * @return bool 173 | */ 174 | public function offsetExists($offset) 175 | { 176 | $this->mails !== NULL || $this->fetchMails(); 177 | return isset($this->mails[$offset]); 178 | } 179 | 180 | /** 181 | * @param int $offset 182 | * @throws MailboxException 183 | * @return Mail 184 | */ 185 | public function offsetGet($offset) 186 | { 187 | $this->mails !== NULL || $this->fetchMails(); 188 | if(isset($this->mails[$offset])) { 189 | return $this->mails[$offset]; 190 | } else { 191 | throw new MailboxException("There is no email with id '$offset'."); 192 | } 193 | } 194 | 195 | /** 196 | * @param int $offset 197 | * @param mixed $value 198 | * @throws MailboxException 199 | */ 200 | public function offsetSet($offset, $value) 201 | { 202 | throw new MailboxException("Cannot set a readonly mail."); 203 | } 204 | 205 | /** 206 | * @param int $offset 207 | * @throws MailboxException 208 | */ 209 | public function offsetUnset($offset) 210 | { 211 | throw new MailboxException("Cannot unset a readonly mail."); 212 | } 213 | 214 | // INTERFACE Countable 215 | 216 | /** 217 | * @return int 218 | */ 219 | public function count() 220 | { 221 | return $this->countMails(); 222 | } 223 | 224 | // INTERFACE Iterator 225 | 226 | /** 227 | * @return Mail 228 | */ 229 | public function current() 230 | { 231 | return $this->mails[$this->mailIndexes[$this->iterator]]; 232 | } 233 | 234 | public function next() 235 | { 236 | $this->iterator++; 237 | } 238 | 239 | /** 240 | * @return int 241 | */ 242 | public function key() 243 | { 244 | return $this->mailIndexes[$this->iterator]; 245 | } 246 | 247 | /** 248 | * @return bool 249 | */ 250 | public function valid() 251 | { 252 | return isset($this->mailIndexes[$this->iterator]); 253 | } 254 | 255 | public function rewind() 256 | { 257 | $this->mails !== NULL || $this->fetchMails(); 258 | $this->iterator = 0; 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /MailLibrary/Mail.php: -------------------------------------------------------------------------------- 1 | connection = $connection; 68 | $this->mailbox = $mailbox; 69 | $this->id = $id; 70 | } 71 | 72 | /** 73 | * Header checker 74 | * 75 | * @param $name 76 | * @return bool 77 | */ 78 | public function __isset($name) 79 | { 80 | $this->headers !== NULL || $this->initializeHeaders(); 81 | return isset($this->headers[$this->formatHeaderName($name)]); 82 | } 83 | 84 | /** 85 | * Header getter 86 | * 87 | * @param string $name 88 | * @return mixed 89 | */ 90 | public function __get($name) 91 | { 92 | return $this->getHeader($name); 93 | } 94 | 95 | /** 96 | * @return int 97 | */ 98 | public function getId() 99 | { 100 | return $this->id; 101 | } 102 | 103 | /** 104 | * @return Mailbox 105 | */ 106 | public function getMailbox() 107 | { 108 | return $this->mailbox; 109 | } 110 | 111 | /** 112 | * @return string[] 113 | */ 114 | public function getHeaders() 115 | { 116 | $this->headers !== NULL || $this->initializeHeaders(); 117 | return $this->headers; 118 | } 119 | 120 | /** 121 | * @param string $name 122 | * @return string 123 | */ 124 | public function getHeader($name) 125 | { 126 | $this->headers !== NULL || $this->initializeHeaders(); 127 | return $this->headers[$this->formatHeaderName($name)]; 128 | } 129 | 130 | /** 131 | * @return Contact|null 132 | */ 133 | public function getSender() { 134 | $from = $this->getHeader('from'); 135 | if($from) { 136 | $contacts = $from->getContactsObjects(); 137 | return (count($contacts) ? $contacts[0] : NULL); 138 | } else { 139 | return NULL; 140 | } 141 | } 142 | 143 | /** 144 | * @return string 145 | */ 146 | public function getBody() 147 | { 148 | $this->structure !== NULL || $this->initializeStructure(); 149 | return $this->structure->getBody(); 150 | } 151 | 152 | /** 153 | * @return string 154 | */ 155 | public function getHtmlBody() 156 | { 157 | $this->structure !== NULL || $this->initializeStructure(); 158 | return $this->structure->getHtmlBody(); 159 | } 160 | 161 | /** 162 | * @return string 163 | */ 164 | public function getTextBody() 165 | { 166 | $this->structure !== NULL || $this->initializeStructure(); 167 | return $this->structure->getTextBody(); 168 | } 169 | 170 | /** 171 | * @return Attachment[] 172 | */ 173 | public function getAttachments() 174 | { 175 | $this->structure !== NULL || $this->initializeStructure(); 176 | return $this->structure->getAttachments(); 177 | } 178 | 179 | /** 180 | * @return array 181 | */ 182 | public function getFlags() 183 | { 184 | $this->flags !== NULL || $this->initializeFlags(); 185 | return $this->flags; 186 | } 187 | 188 | public function setFlags(array $flags, $autoFlush = FALSE) 189 | { 190 | $this->connection->getDriver()->switchMailbox($this->mailbox->getName()); 191 | foreach(array( 192 | Mail::FLAG_ANSWERED, 193 | Mail::FLAG_DELETED, 194 | Mail::FLAG_DELETED, 195 | Mail::FLAG_FLAGGED, 196 | Mail::FLAG_SEEN, 197 | ) as $flag) { 198 | if(isset($flags[$flag])) { 199 | $this->connection->getDriver()->setFlag($this->id, $flag, $flags[$flag]); 200 | } 201 | } 202 | if($autoFlush) { 203 | $this->connection->getDriver()->flush(); 204 | } 205 | } 206 | 207 | public function move($toMailbox) 208 | { 209 | $this->connection->getDriver()->switchMailbox($this->mailbox->getName()); 210 | $this->connection->getDriver()->moveMail($this->id, $toMailbox); 211 | } 212 | 213 | public function copy($toMailbox) 214 | { 215 | $this->connection->getDriver()->switchMailbox($this->mailbox->getName()); 216 | $this->connection->getDriver()->copyMail($this->id, $toMailbox); 217 | } 218 | 219 | public function delete() 220 | { 221 | $this->connection->getDriver()->switchMailbox($this->mailbox->getName()); 222 | $this->connection->getDriver()->deleteMail($this->id); 223 | } 224 | 225 | /** 226 | * Initializes headers 227 | */ 228 | protected function initializeHeaders() 229 | { 230 | $this->headers = array(); 231 | $this->connection->getDriver()->switchMailbox($this->mailbox->getName()); 232 | foreach($this->connection->getDriver()->getHeaders($this->id) as $key => $value) { 233 | $this->headers[$this->formatHeaderName($key)] = $value; 234 | } 235 | } 236 | 237 | protected function initializeStructure() 238 | { 239 | $this->connection->getDriver()->switchMailbox($this->mailbox->getName()); 240 | $this->structure = $this->connection->getDriver()->getStructure($this->id, $this->mailbox); 241 | } 242 | 243 | protected function initializeFlags() 244 | { 245 | $this->connection->getDriver()->switchMailbox($this->mailbox->getName()); 246 | $this->flags = $this->connection->getDriver()->getFlags($this->id); 247 | } 248 | 249 | /** 250 | * Formats header name (X-Received-From => xReceivedFrom) 251 | * 252 | * @param string $name 253 | * @return string 254 | */ 255 | protected function formatHeaderName($name) 256 | { 257 | return lcfirst(preg_replace_callback("~-.~", function($matches){ 258 | return ucfirst(substr($matches[0], 1)); 259 | }, $name)); 260 | } 261 | } 262 | -------------------------------------------------------------------------------- /MailLibrary/Drivers/ImapDriver.php: -------------------------------------------------------------------------------- 1 | '%bANSWERED', 35 | Mail::BCC => 'BCC "%s"', 36 | Mail::BEFORE => 'BEFORE "%d"', 37 | Mail::BODY => 'BODY "%s"', 38 | Mail::CC => 'CC "%s"', 39 | Mail::DELETED => '%bDELETED', 40 | Mail::FLAGGED => '%bFLAGGED', 41 | Mail::FROM => 'FROM "%s"', 42 | Mail::KEYWORD => 'KEYWORD "%s"', 43 | Mail::NEW_MESSAGES => 'NEW', 44 | Mail::NOT_KEYWORD => 'UNKEYWORD "%s"', 45 | Mail::OLD_MESSAGES => 'OLD', 46 | Mail::ON => 'ON "%d"', 47 | Mail::RECENT => 'RECENT', 48 | Mail::SEEN => '%bSEEN', 49 | Mail::SINCE => 'SINCE "%d"', 50 | Mail::SUBJECT => 'SUBJECT "%s"', 51 | Mail::TEXT => 'TEXT "%s"', 52 | Mail::TO => 'TO "%s"', 53 | ); 54 | 55 | protected static $contactHeaders = array( 56 | 'to', 57 | 'from', 58 | 'cc', 59 | 'bcc', 60 | ); 61 | 62 | public function __construct($username, $password, $host, $port = 993, $ssl = TRUE) 63 | { 64 | $ssl = $ssl ? '/ssl' : '/novalidate-cert'; 65 | $this->server = '{'.$host.':'.$port.'/imap'.$ssl.'}'; 66 | $this->username = $username; 67 | $this->password = $password; 68 | } 69 | 70 | /** 71 | * Connects to server 72 | * 73 | * @throws DriverException if connecting fails 74 | */ 75 | public function connect() 76 | { 77 | if(!$this->resource = @imap_open($this->server, $this->username, $this->password, CL_EXPUNGE)) { // @ - to allow throwing exceptions 78 | throw new DriverException("Cannot connect to IMAP server: " . imap_last_error()); 79 | } 80 | } 81 | 82 | /** 83 | * Flushes changes to server 84 | * 85 | * @throws DriverException if flushing fails 86 | */ 87 | public function flush() 88 | { 89 | imap_expunge($this->resource); 90 | } 91 | 92 | /** 93 | * Gets all mailboxes 94 | * 95 | * @return array of string 96 | * @throws DriverException 97 | */ 98 | public function getMailboxes() 99 | { 100 | $mailboxes = array(); 101 | $foo = imap_list($this->resource, $this->server, '*'); 102 | if(!$foo) { 103 | throw new DriverException("Cannot get mailboxes from server: " . imap_last_error()); 104 | } 105 | foreach($foo as $mailbox) { 106 | $mailboxes[] = mb_convert_encoding(str_replace($this->server, '', $mailbox), 'UTF8', 'UTF7-IMAP'); 107 | } 108 | return $mailboxes; 109 | } 110 | 111 | /** 112 | * Creates new mailbox 113 | * 114 | * @param string $name 115 | * @throws DriverException 116 | */ 117 | public function createMailbox($name) 118 | { 119 | if(!imap_createmailbox($this->resource, $this->server . $name)) { 120 | throw new DriverException("Cannot create mailbox '$name': " . imap_last_error()); 121 | } 122 | } 123 | 124 | /** 125 | * Renames mailbox 126 | * 127 | * @param string $from 128 | * @param string $to 129 | * @throws DriverException 130 | */ 131 | public function renameMailbox($from, $to) 132 | { 133 | if(!imap_renamemailbox($this->resource, $this->server . $from, $this->server . $to)) { 134 | throw new DriverException("Cannot rename mailbox from '$from' to '$to': " . imap_last_error()); 135 | } 136 | } 137 | 138 | /** 139 | * Deletes mailbox 140 | * 141 | * @param string $name 142 | * @throws DriverException 143 | */ 144 | public function deleteMailbox($name) 145 | { 146 | if(!imap_deletemailbox($this->resource, $this->server . $name)) { 147 | throw new DriverException("Cannot delete mailbox '$name': " . imap_last_error()); 148 | } 149 | } 150 | 151 | /** 152 | * Switches current mailbox 153 | * 154 | * @param string $name 155 | * @throws DriverException 156 | */ 157 | public function switchMailbox($name) 158 | { 159 | if($name !== $this->currentMailbox) { 160 | $this->flush(); 161 | if(!imap_reopen($this->resource, $this->server . $name)) { 162 | throw new DriverException("Cannot switch to mailbox '$name': " . imap_last_error()); 163 | } 164 | $this->currentMailbox = $name; 165 | } 166 | } 167 | 168 | /** 169 | * Finds UIDs of mails by filter 170 | * 171 | * @param array $filters 172 | * @param int $limit 173 | * @param int $offset 174 | * @param int $orderBy 175 | * @param string $orderType 176 | * @throws \greeny\MailLibrary\DriverException 177 | * @return array of UIDs 178 | */ 179 | public function getMailIds(array $filters, $limit = 0, $offset = 0, $orderBy = Mail::ORDER_DATE, $orderType = 'ASC') 180 | { 181 | $filter = $this->buildFilters($filters); 182 | 183 | $orderType = $orderType === 'ASC' ? 1 : 0; 184 | 185 | if(!is_array($ids = imap_sort($this->resource, $orderBy, $orderType, SE_UID | SE_NOPREFETCH, $filter, 'UTF-8'))) { 186 | throw new DriverException("Cannot get mails: " . imap_last_error()); 187 | } 188 | 189 | return $limit === 0 ? $ids : array_slice($ids, $offset, $limit); 190 | } 191 | 192 | /** 193 | * Checks if filter is applicable for this driver 194 | * 195 | * @param string $key 196 | * @param mixed $value 197 | * @throws DriverException 198 | */ 199 | public function checkFilter($key, $value = NULL) { 200 | if(!in_array($key, array_keys(self::$filterTable))) { 201 | throw new DriverException("Invalid filter key '$key'."); 202 | } 203 | $filtered = self::$filterTable[$key]; 204 | if(strpos($filtered, '%s') !== FALSE) { 205 | if(!is_string($value)) { 206 | throw new DriverException("Invalid value type for filter '$key', expected string, got ".gettype($value)."."); 207 | } 208 | } else if(strpos($filtered, '%d') !== FALSE) { 209 | if(!($value instanceof DateTime) && !is_int($value) && !strtotime($value)) { 210 | throw new DriverException("Invalid value type for filter '$key', expected DateTime or timestamp, or textual representation of date, got ".gettype($value)."."); 211 | } 212 | } else if(strpos($filtered, '%b') !== FALSE) { 213 | if(!is_bool($value)) { 214 | throw new DriverException("Invalid value type for filter '$key', expected bool, got ".gettype($value)."."); 215 | } 216 | } else if($value !== NULL) { 217 | throw new DriverException("Cannot assign value to filter '$key'."); 218 | } 219 | } 220 | 221 | /** 222 | * Gets mail headers 223 | * 224 | * @param int $mailId 225 | * @return array of name => value 226 | */ 227 | public function getHeaders($mailId) 228 | { 229 | $raw = imap_fetchheader($this->resource, $mailId, FT_UID); 230 | $lines = explode("\n", $raw); 231 | $headers = array(); 232 | $lastHeader = NULL; 233 | foreach($lines as $line) { 234 | if(mb_substr($line, 0, 1, 'UTF-8') === " ") { 235 | $headers[$lastHeader] .= $line; 236 | } else { 237 | $parts = explode(':', $line); 238 | $name = $parts[0]; 239 | unset($parts[0]); 240 | 241 | $headers[$name] = implode(':', $parts); 242 | $lastHeader = $name; 243 | } 244 | } 245 | 246 | foreach($headers as $key => $header) { 247 | if(trim($key) === '') { 248 | unset($headers[$key]); 249 | continue; 250 | } 251 | if(strtolower($key) === 'subject') { 252 | $decoded = imap_mime_header_decode($header); 253 | 254 | $text = ''; 255 | foreach($decoded as $part) { 256 | if($part->charset !== 'UTF-8' && $part->charset !== 'default') { 257 | $text .= mb_convert_encoding($part->text, 'UTF-8', $part->charset); 258 | } else { 259 | $text .= $part->text; 260 | } 261 | } 262 | 263 | $headers[$key] = trim($text); 264 | } else if(in_array(strtolower($key), self::$contactHeaders)) { 265 | $contacts = imap_rfc822_parse_adrlist(imap_utf8(trim($header)), 'UNKNOWN_HOST'); 266 | $list = new ContactList(); 267 | foreach($contacts as $contact) { 268 | $list->addContact( 269 | isset($contact->mailbox) ? $contact->mailbox : NULL, 270 | isset($contact->host) ? $contact->host : NULL, 271 | isset($contact->personal) ? $contact->personal : NULL, 272 | isset($contact->adl) ? $contact->adl : NULL 273 | ); 274 | } 275 | $list->build(); 276 | $headers[$key] = $list; 277 | } else { 278 | $headers[$key] = trim(imap_utf8($header)); 279 | } 280 | } 281 | return $headers; 282 | } 283 | 284 | /** 285 | * Creates structure for mail 286 | * 287 | * @param int $mailId 288 | * @param Mailbox $mailbox 289 | * @return IStructure 290 | */ 291 | public function getStructure($mailId, Mailbox $mailbox) 292 | { 293 | return new ImapStructure($this, imap_fetchstructure($this->resource, $mailId, FT_UID), $mailId, $mailbox); 294 | } 295 | 296 | /** 297 | * Gets part of body 298 | * 299 | * @param int $mailId 300 | * @param array $data 301 | * @return string 302 | */ 303 | public function getBody($mailId, array $data) 304 | { 305 | $body = array(); 306 | foreach($data as $part) { 307 | $data = ($part['id'] == 0) ? imap_body($this->resource, $mailId, FT_UID | FT_PEEK) : imap_fetchbody($this->resource, $mailId, $part['id'], FT_UID | FT_PEEK); 308 | $encoding = $part['encoding']; 309 | if($encoding === ImapStructure::ENCODING_BASE64) { 310 | $data = base64_decode($data); 311 | } else if($encoding === ImapStructure::ENCODING_QUOTED_PRINTABLE) { 312 | $data = quoted_printable_decode($data); 313 | } 314 | 315 | $body[] = $data; 316 | } 317 | return implode('\n\n', $body); 318 | } 319 | 320 | /** 321 | * Gets flags for mail 322 | * 323 | * @param $mailId 324 | * @return array 325 | */ 326 | public function getFlags($mailId) 327 | { 328 | $data = imap_fetch_overview($this->resource, (string)$mailId, FT_UID); 329 | reset($data); 330 | $data = current($data); 331 | $return = array( 332 | Mail::FLAG_ANSWERED => FALSE, 333 | Mail::FLAG_DELETED => FALSE, 334 | Mail::FLAG_DRAFT => FALSE, 335 | Mail::FLAG_FLAGGED => FALSE, 336 | Mail::FLAG_SEEN => FALSE, 337 | ); 338 | if($data->answered) { 339 | $return[Mail::FLAG_ANSWERED] = TRUE; 340 | } 341 | if($data->deleted) { 342 | $return[Mail::FLAG_DELETED] = TRUE; 343 | } 344 | if($data->draft) { 345 | $return[Mail::FLAG_DRAFT] = TRUE; 346 | } 347 | if($data->flagged) { 348 | $return[Mail::FLAG_FLAGGED] = TRUE; 349 | } 350 | if($data->seen) { 351 | $return[Mail::FLAG_SEEN] = TRUE; 352 | } 353 | return $return; 354 | } 355 | 356 | /** 357 | * Sets one flag for mail 358 | * 359 | * @param int $mailId 360 | * @param string $flag 361 | * @param bool $value 362 | * @throws DriverException 363 | */ 364 | public function setFlag($mailId, $flag, $value) 365 | { 366 | if($value) { 367 | if(!imap_setflag_full($this->resource, $mailId, $flag, ST_UID)) { 368 | throw new DriverException("Cannot set flag '$flag': ".imap_last_error()); 369 | } 370 | } else { 371 | if(!imap_clearflag_full($this->resource, $mailId, $flag, ST_UID)) { 372 | throw new DriverException("Cannot unset flag '$flag': ".imap_last_error()); 373 | } 374 | } 375 | } 376 | 377 | /** 378 | * Copies mail to another mailbox 379 | * @param int $mailId 380 | * @param string $toMailbox 381 | * @throws DriverException 382 | */ 383 | public function copyMail($mailId, $toMailbox) { 384 | if(!imap_mail_copy($this->resource, $mailId, $this->server . $this->encodeMailboxName($toMailbox), CP_UID)) { 385 | throw new DriverException("Cannot copy mail to mailbox '$toMailbox': ".imap_last_error()); 386 | } 387 | } 388 | 389 | /** 390 | * Moves mail to another mailbox 391 | * @param int $mailId 392 | * @param string $toMailbox 393 | * @throws DriverException 394 | */ 395 | public function moveMail($mailId, $toMailbox) { 396 | if(!imap_mail_move($this->resource, $mailId, $this->server . $this->encodeMailboxName($toMailbox), CP_UID)) { 397 | throw new DriverException("Cannot copy mail to mailbox '$toMailbox': ".imap_last_error()); 398 | } 399 | } 400 | 401 | /** 402 | * Deletes mail 403 | * @param int $mailId 404 | * @throws DriverException 405 | */ 406 | public function deleteMail($mailId) { 407 | if(!imap_delete($this->resource, $mailId, FT_UID)) { 408 | throw new DriverException("Cannot delete mail: ".imap_last_error()); 409 | } 410 | } 411 | 412 | /** 413 | * Builds filter string from filters 414 | * 415 | * @param array $filters 416 | * @return string 417 | */ 418 | protected function buildFilters(array $filters) 419 | { 420 | $return = array(); 421 | foreach($filters as $filter) { 422 | $key = self::$filterTable[$filter['key']]; 423 | $value = $filter['value']; 424 | 425 | if(strpos($key, '%s') !== FALSE) { 426 | $data = str_replace('%s', str_replace('"', '', (string)$value), $key); 427 | } else if(strpos($key, '%d') !== FALSE) { 428 | if($value instanceof DateTime) { 429 | $timestamp = $value->getTimestamp(); 430 | } else if(is_string($value)) { 431 | $timestamp = strtotime($value) ?: Time(); 432 | } else { 433 | $timestamp = (int)$value; 434 | } 435 | $data = str_replace('%d', date("d M Y", $timestamp), $key); 436 | } else if(strpos($key, '%b') !== FALSE) { 437 | $data = str_replace('%b', ((bool)$value ? '' : 'UN'), $key); 438 | } else { 439 | $data = $key; 440 | } 441 | $return[] = $data; 442 | } 443 | return implode(' ', $return); 444 | } 445 | 446 | /** 447 | * Builds list from ids array 448 | * 449 | * @param array $ids 450 | * @return string 451 | */ 452 | protected function buildIdList(array $ids) 453 | { 454 | sort($ids); 455 | return implode(',', $ids); 456 | } 457 | 458 | /** 459 | * Converts mailbox name encoding as defined in IMAP RFC 2060. 460 | * 461 | * @param $name 462 | * @return string 463 | */ 464 | protected function encodeMailboxName($name) 465 | { 466 | return mb_convert_encoding($name, 'UTF7-IMAP', 'UTF-8'); 467 | } 468 | 469 | } 470 | --------------------------------------------------------------------------------