diff --git a/MailLibrary/ContactList.php b/MailLibrary/ContactList.php index a408bca..2f2a362 100644 --- a/MailLibrary/ContactList.php +++ b/MailLibrary/ContactList.php @@ -11,9 +11,9 @@ class ContactList implements Iterator, Countable { /** @var Contact[] */ - protected $contacts; + protected $contacts = []; - protected $builtContacts; + protected $builtContacts = []; public function addContact($mailbox = NULL, $host = NULL, $personal = NULL, $adl = NULL) { diff --git a/MailLibrary/Drivers/ImapDriver.php b/MailLibrary/Drivers/ImapDriver.php index eb059e7..c6831a1 100644 --- a/MailLibrary/Drivers/ImapDriver.php +++ b/MailLibrary/Drivers/ImapDriver.php @@ -10,6 +10,7 @@ use greeny\MailLibrary\Mailbox; use greeny\MailLibrary\Structures\IStructure; use greeny\MailLibrary\Structures\ImapStructure; +use Nette\Utils\Strings; use greeny\MailLibrary\Mail; use DateTime; @@ -227,18 +228,21 @@ public function checkFilter($key, $value = NULL) { public function getHeaders($mailId) { $raw = imap_fetchheader($this->resource, $mailId, FT_UID); - $lines = explode("\n", $raw); + $lines = explode("\n", Strings::fixEncoding($raw)); $headers = array(); $lastHeader = NULL; + + // normalize headers foreach($lines as $line) { - if(mb_substr($line, 0, 1, 'UTF-8') === " ") { - $headers[$lastHeader] .= $line; + $firstCharacter = mb_substr($line, 0, 1, 'UTF-8'); // todo: correct assumption that string must be UTF-8 encoded? + if(preg_match('/[\pZ\pC]/u', $firstCharacter) === 1) { // search for UTF-8 whitespaces + $headers[$lastHeader] .= " " . Strings::trim($line); } else { $parts = explode(':', $line); - $name = $parts[0]; + $name = Strings::trim($parts[0]); unset($parts[0]); - $headers[$name] = implode(':', $parts); + $headers[$name] = Strings::trim(implode(':', $parts)); $lastHeader = $name; } } @@ -254,7 +258,7 @@ public function getHeaders($mailId) $text = ''; foreach($decoded as $part) { if($part->charset !== 'UTF-8' && $part->charset !== 'default') { - $text .= mb_convert_encoding($part->text, 'UTF-8', $part->charset); + $text .= @mb_convert_encoding($part->text, 'UTF-8', $part->charset); // todo: handle this more properly } else { $text .= $part->text; } diff --git a/MailLibrary/Mail.php b/MailLibrary/Mail.php index 0431948..42c1dbe 100644 --- a/MailLibrary/Mail.php +++ b/MailLibrary/Mail.php @@ -5,6 +5,9 @@ namespace greeny\MailLibrary; +use greeny\MailLibrary\Structures\IStructure; +use Nette\Utils\Strings; + class Mail { const ANSWERED = 'ANSWERED'; const BCC = 'BCC'; @@ -39,10 +42,10 @@ class Mail { const ORDER_CC = SORTCC; const ORDER_SIZE = SORTSIZE; - /** @var \greeny\MailLibrary\Connection */ + /** @var Connection */ protected $connection; - /** @var \greeny\MailLibrary\Mailbox */ + /** @var Mailbox */ protected $mailbox; /** @var int */ @@ -51,7 +54,7 @@ class Mail { /** @var array */ protected $headers = NULL; - /** @var \greeny\MailLibrary\Structures\IStructure */ + /** @var IStructure */ protected $structure = NULL; /** @var array */ @@ -78,7 +81,8 @@ public function __construct(Connection $connection, Mailbox $mailbox, $id) public function __isset($name) { $this->headers !== NULL || $this->initializeHeaders(); - return isset($this->headers[$this->formatHeaderName($name)]); + $key = $this->normalizeHeaderName($this->lowerCamelCaseToHeaderName($name)); + return isset($this->headers[$key]); } /** @@ -86,10 +90,18 @@ public function __isset($name) * * @param string $name * @return mixed + * @deprecated */ public function __get($name) { - return $this->getHeader($name); + \trigger_error(\E_USER_DEPRECATED, 'use array access with execat header name instead'); + return $this->getHeader( + $this->normalizeHeaderName($this->lowerCamelCaseToHeaderName($name)) + ); + } + + public function __set($name, $value) { + throw new \Exception('Mail headers are read-only.'); } /** @@ -124,7 +136,12 @@ public function getHeaders() public function getHeader($name) { $this->headers !== NULL || $this->initializeHeaders(); - return $this->headers[$this->formatHeaderName($name)]; + $index = $this->normalizeHeaderName($name); + if(isset($this->headers[$index])) { + return $this->headers[$index]; + } + + return NULL; } /** @@ -135,9 +152,9 @@ public function getSender() { if($from) { $contacts = $from->getContactsObjects(); return (count($contacts) ? $contacts[0] : NULL); - } else { - return NULL; } + + return NULL; } /** @@ -230,7 +247,7 @@ protected function initializeHeaders() $this->headers = array(); $this->connection->getDriver()->switchMailbox($this->mailbox->getName()); foreach($this->connection->getDriver()->getHeaders($this->id) as $key => $value) { - $this->headers[$this->formatHeaderName($key)] = $value; + $this->headers[$this->normalizeHeaderName($key)] = $value; } } @@ -247,15 +264,29 @@ protected function initializeFlags() } /** - * Formats header name (X-Received-From => xReceivedFrom) + * Formats header name (X-Received-From => x-recieved-from) * - * @param string $name + * @param string $name Header name (with dashes, valid UTF-8 string) * @return string */ - protected function formatHeaderName($name) + protected function normalizeHeaderName($name) { - return lcfirst(preg_replace_callback("~-.~", function($matches){ + return Strings::normalize(Strings::lower($name)); + } + + /** + * Converts camel cased name to normalized header name (xReceivedFrom => x-recieved-from) + * + * @param string $camelCasedName + * @return string name with dashes + */ + protected function lowerCamelCaseToHeaderName($camelCasedName) { + // todo: test this + // todo: use something like this instead http://stackoverflow.com/a/1993772 + $dashedName = lcfirst(preg_replace_callback("~-.~", function($matches){ return ucfirst(substr($matches[0], 1)); - }, $name)); + }, $camelCasedName)); + + return $this->normalizeHeaderName($dashedName); } } diff --git a/MailLibrary/loader.php b/MailLibrary/loader.php index 998517c..e11bb40 100644 --- a/MailLibrary/loader.php +++ b/MailLibrary/loader.php @@ -6,24 +6,24 @@ require_once "exceptions.php"; spl_autoload_register(function ($type) { - static $paths = array( - 'greeny\maillibrary\connection' => 'Connection.php', - 'greeny\maillibrary\mailbox' => 'Mailbox.php', - 'greeny\maillibrary\selection' => 'Selection.php', - 'greeny\maillibrary\mail' => 'Mail.php', - 'greeny\maillibrary\contactlist' => 'ContactList.php', - 'greeny\maillibrary\contact' => 'Contact.php', - 'greeny\maillibrary\attachment' => 'Attachment.php', - 'greeny\maillibrary\structures\istructure' => 'Structures/IStructure.php', - 'greeny\maillibrary\structures\imapstructure' => 'Structures/ImapStructure.php', - 'greeny\maillibrary\drivers\idriver' => 'Drivers/IDriver.php', - 'greeny\maillibrary\drivers\imapdriver' => 'Drivers/ImapDriver.php', - 'greeny\maillibrary\extensions\maillibraryextension' => 'Extensions/MailLibraryExtension.php', - ); + static $paths = array( + 'greeny\maillibrary\connection' => 'Connection.php', + 'greeny\maillibrary\mailbox' => 'Mailbox.php', + 'greeny\maillibrary\selection' => 'Selection.php', + 'greeny\maillibrary\mail' => 'Mail.php', + 'greeny\maillibrary\contactlist' => 'ContactList.php', + 'greeny\maillibrary\contact' => 'Contact.php', + 'greeny\maillibrary\attachment' => 'Attachment.php', + 'greeny\maillibrary\structures\istructure' => 'Structures/IStructure.php', + 'greeny\maillibrary\structures\imapstructure' => 'Structures/ImapStructure.php', + 'greeny\maillibrary\drivers\idriver' => 'Drivers/IDriver.php', + 'greeny\maillibrary\drivers\imapdriver' => 'Drivers/ImapDriver.php', + 'greeny\maillibrary\extensions\maillibraryextension' => 'Extensions/MailLibraryExtension.php', + ); - $type = ltrim(strtolower($type), '\\'); // PHP namespace bug #49143 + $type = ltrim(strtolower($type), '\\'); // PHP namespace bug #49143 - if (isset($paths[$type])) { - require_once __DIR__ . '/' . $paths[$type]; - } + if (isset($paths[$type])) { + require_once __DIR__ . '/' . $paths[$type]; + } }); \ No newline at end of file diff --git a/README.md b/README.md index 79fc5d9..08f3980 100644 --- a/README.md +++ b/README.md @@ -3,4 +3,17 @@ MailLibrary A PHP library for downloading mails from server. -Documentation can be found at http://greeny.github.io/MailLibrary/. \ No newline at end of file +Documentation can be found at http://greeny.github.io/MailLibrary/. + +Testing +------- + +Install dependencies using composer and then run following in library root directory. + +````cmd +# Unix +vendor\bin\tester -c tests\php-unix.ini tests + +# Windows +vendor\bin\tester -c tests\php-windows.ini tests +```` diff --git a/composer.json b/composer.json index 8b0751c..d04e0ac 100644 --- a/composer.json +++ b/composer.json @@ -12,9 +12,11 @@ "files": ["MailLibrary/loader.php"] }, "require": { - "php": ">= 5.3.0" + "php": ">= 5.3.0", + "ext-imap": "*", + "nette/utils": "~2.2" }, "require-dev": { "nette/tester": "@dev" } -} \ No newline at end of file +} diff --git a/tests/php-unix.ini b/tests/php-unix.ini new file mode 100644 index 0000000..c758598 --- /dev/null +++ b/tests/php-unix.ini @@ -0,0 +1,5 @@ +[PHP] +;extension_dir = "./ext" +extension=mbstring.so +extension=imap.so +date.timezone = "Europe/Prague" diff --git a/tests/php-windows.ini b/tests/php-windows.ini new file mode 100644 index 0000000..f601ad4 --- /dev/null +++ b/tests/php-windows.ini @@ -0,0 +1,5 @@ +[PHP] +extension_dir = "./ext" +extension=php_mbstring.dll +extension=php_imap.dll +date.timezone = "Europe/Prague"