Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions MailLibrary/ContactList.php
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
41 changes: 26 additions & 15 deletions MailLibrary/Drivers/ImapDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
}
}
Expand All @@ -254,14 +258,14 @@ 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;
}
}

$headers[$key] = trim($text);
} else if(in_array(strtolower($key), self::$contactHeaders)) {
} /*else if(in_array(strtolower($key), self::$contactHeaders)) {
$contacts = imap_rfc822_parse_adrlist(imap_utf8(trim($header)), 'UNKNOWN_HOST');
$list = new ContactList();
foreach($contacts as $contact) {
Expand All @@ -274,7 +278,7 @@ public function getHeaders($mailId)
}
$list->build();
$headers[$key] = $list;
} else {
}*/ else {
$headers[$key] = trim(imap_utf8($header));
}
}
Expand All @@ -297,22 +301,29 @@ public function getStructure($mailId, Mailbox $mailbox)
* Gets part of body
*
* @param int $mailId
* @param array $data
* @param array $data requires id and encoding keys
* @return string
* @throws \greeny\MailLibrary\DriverException
*/
public function getBody($mailId, array $data)
{
$body = array();
foreach($data as $part) {
$data = ($part['id'] == 0) ? imap_body($this->resource, $mailId, FT_UID | FT_PEEK) : imap_fetchbody($this->resource, $mailId, $part['id'], FT_UID | FT_PEEK);
assert(is_array($part));
$dataMessage = ($part['id'] === 0) ? @imap_body($this->resource, $mailId, FT_UID | FT_PEEK) : @imap_fetchbody($this->resource, $mailId, $part['id'], FT_UID | FT_PEEK);
if($dataMessage === FALSE) {
throw new DriverException("Cannot read given message part - " . error_get_last()["message"]);
}
$encoding = $part['encoding'];
if($encoding === ImapStructure::ENCODING_BASE64) {
$data = base64_decode($data);
$dataMessage = base64_decode($dataMessage);
} else if($encoding === ImapStructure::ENCODING_QUOTED_PRINTABLE) {
$data = quoted_printable_decode($data);
$dataMessage = quoted_printable_decode($dataMessage);
}

$body[] = $data;
// todo: other encodings?

$body[] = $dataMessage;
}
return implode('\n\n', $body);
}
Expand Down Expand Up @@ -381,7 +392,7 @@ public function setFlag($mailId, $flag, $value)
* @throws DriverException
*/
public function copyMail($mailId, $toMailbox) {
if(!imap_mail_copy($this->resource, $mailId, $this->server . $this->encodeMailboxName($toMailbox), CP_UID)) {
if(!imap_mail_copy($this->resource, $mailId, /*$this->server .*/ $this->encodeMailboxName($toMailbox), CP_UID)) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be some way how to configure this.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my comment in PR

throw new DriverException("Cannot copy mail to mailbox '$toMailbox': ".imap_last_error());
}
}
Expand All @@ -393,7 +404,7 @@ public function copyMail($mailId, $toMailbox) {
* @throws DriverException
*/
public function moveMail($mailId, $toMailbox) {
if(!imap_mail_move($this->resource, $mailId, $this->server . $this->encodeMailboxName($toMailbox), CP_UID)) {
if(!imap_mail_move($this->resource, $mailId, /*$this->server .*/ $this->encodeMailboxName($toMailbox), CP_UID)) {
throw new DriverException("Cannot copy mail to mailbox '$toMailbox': ".imap_last_error());
}
}
Expand Down
49 changes: 37 additions & 12 deletions MailLibrary/Mail.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@

namespace greeny\MailLibrary;

use greeny\MailLibrary\Structures\IStructure;
use Nette\Utils\Strings;

class Mail {
const ANSWERED = 'ANSWERED';
const BCC = 'BCC';
Expand Down Expand Up @@ -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 */
Expand All @@ -51,7 +54,7 @@ class Mail {
/** @var array */
protected $headers = NULL;

/** @var \greeny\MailLibrary\Structures\IStructure */
/** @var IStructure */
protected $structure = NULL;

/** @var array */
Expand All @@ -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));
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is proposal. Basic idea is that magic methods uses magic name translation into normalized header name and getHeader() is more accurate (less magic included).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my comment in PR

return isset($this->headers[$key]);
}

/**
Expand All @@ -89,7 +93,9 @@ public function __isset($name)
*/
public function __get($name)
{
return $this->getHeader($name);
return $this->getHeader(
$this->normalizeHeaderName($this->lowerCamelCaseToHeaderName($name))
);
}

/**
Expand Down Expand Up @@ -124,7 +130,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];
} else {
return NULL;
}
}

/**
Expand Down Expand Up @@ -230,7 +241,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;
}
}

Expand All @@ -247,15 +258,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);
}
}
36 changes: 18 additions & 18 deletions MailLibrary/loader.php
Original file line number Diff line number Diff line change
Expand Up @@ -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];
}
});
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,17 @@ MailLibrary

A PHP library for downloading mails from server.

Documentation can be found at http://greeny.github.io/MailLibrary/.
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
````
6 changes: 4 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
}
5 changes: 5 additions & 0 deletions tests/php-unix.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[PHP]
;extension_dir = "./ext"
extension=mbstring.so
extension=imap.so
date.timezone = "Europe/Prague"
5 changes: 5 additions & 0 deletions tests/php-windows.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[PHP]
extension_dir = "./ext"
extension=php_mbstring.dll
extension=php_imap.dll
date.timezone = "Europe/Prague"