Compare commits

..

No commits in common. "master" and "v0.4.1" have entirely different histories.

21 changed files with 207 additions and 1559 deletions

View file

@ -1,3 +0,0 @@
src_dir: src
coverage_clover: build/logs/clover.xml
json_path: build/logs/coveralls-upload.json

4
.gitignore vendored
View file

@ -1,9 +1,7 @@
.vagrant
/.idea
/.settings
/.buildpath
/.project
/composer.lock
/vendor
/report
/build
/report

View file

@ -1,24 +0,0 @@
language: php
php:
- 5.3
- 5.4
- 5.5
- 5.6
- hhvm
- hhvm-nightly
before_script:
- composer self-update && composer install --dev
- vendor/tedivm/dovecottesting/SetupEnvironment.sh
script: ./tests/runTests.sh
after_script:
- php vendor/bin/coveralls -v
matrix:
fast_finish: true
allow_failures:
- php: hhvm
- php: hhvm-nightly

View file

@ -1,54 +0,0 @@
# Contributions Welcome!
Pull Requests and Community Contributions are the bread and butter of open source software. Every contribution- from bug
reports to feature requests, typos to full new features- are greatly appreciated.
## Important Guidelines
* One Item Per Pull Request or Issue. This makes it much easier to review code and merge it back in, and prevents issues
with one request from blocking another.
* Code Coverage is extremely important, and pull requests are much more likely to be accepted if testing is also improved.
New code should be properly tested, and all tests must pass.
* Read the LICENSE document and make sure you understand it, because your code is going to be released under it.
* Be prepared to make revisions. Don't be discouraged if you're asked to make changes, as that is just another step
towards refining the code and getting it merged back in.
* Remember to add the relevant documentation, particular the docblock comments.
## Code Styling
This project follows the PSR standards set forth by the [PHP Framework Interop Group](http://www.php-fig.org/).
* [PSR-0: Class and file naming conventions](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md)
* [PSR-1: Basic coding standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md)
* [PSR-2: Coding style guide](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)
All code most follow these standards to be accepted. The easiest way to accomplish this is to run php-cs-fixer once the
new changes are finished. The php-cs-fixer package is installed as a development dependency of this project.
composer install --dev
vendor/bin/php-cs-fixer fix ./ --level="all" -vv
## Running the test suite
First install dependencies using Composer. It's important to include the dev packages:
composer install --dev
The "runTests.sh" script runs the full test suite- phpunit, php-cs-fixer, as well as any environmental setup:
tests/runTests.sh
To call phpunit directly:
vendor/bin/phpunit
To call php-cs-fixer directly:
vendor/bin/php-cs-fixer fix ./ --level="all" -vv --dry-run

View file

@ -1,55 +1,34 @@
# Fetch [![Build Status](https://travis-ci.org/tedious/Fetch.svg?branch=master)](https://travis-ci.org/tedious/Fetch)
[![License](http://img.shields.io/packagist/l/tedivm/fetch.svg)](https://github.com/tedious/fetch/blob/master/LICENSE)
[![Latest Stable Version](http://img.shields.io/github/release/tedious/fetch.svg)](https://packagist.org/packages/tedivm/fetch)
[![Coverage Status](http://img.shields.io/coveralls/tedious/Fetch.svg)](https://coveralls.io/r/tedious/Fetch?branch=master)
[![Total Downloads](http://img.shields.io/packagist/dt/tedivm/fetch.svg)](https://packagist.org/packages/tedivm/fetch)
Fetch
=====
Fetch is a library for reading email and attachments, primarily using the POP
and IMAP protocols.
Fetch was part of a large project, Mortar, which is hosted on Google Code. It is
currently being migrated over to Github, as well as being updated to support
modern php features. While the project is in flux I encourage you to try it out,
but be careful about using it in a production environment without proper
testing.
## Installing
> N.b. A note on Ubuntu 14.04 (probably other Debian-based / Apt managed systems), the install of php5-imap does not enable the extension for CLI (possibly others as well), which can cause composer to report fetch requires ext-imap
```
sudo ln -s /etc/php5/mods-available/imap.ini /etc/php5/cli/conf.d/30-imap.ini
```
Installation
============
### Composer
The most easy way to install the library is via composer. To do so, you have to do
the following:
Installing Fetch can be done through a variety of methods, although Composer is
recommended.
php composer.phar require tedivm/fetch
Until Fetch reaches a stable API with version 1.0 it is recommended that you
review changes before even Minor updates, although bug fixes will always be
backwards compatible.
Composer will then ask you which version you want to install. Until there are stable
versions, by using "@dev" it'll install the latest version.
```
"require": {
"tedivm/fetch": "0.6.*"
}
```
### Pear
Fetch is also available through Pear.
```
$ pear channel-discover pear.tedivm.com
$ pear install tedivm/Fetch
```
### Github
Releases of Fetch are available on [Github](https://github.com/tedious/Fetch/releases).
## Sample Usage
Sample Usage
============
This is just a simple code to show how to access messages by using Fetch. It uses Fetch
own autoload, but it can (and should be, if applicable) replaced with the one generated
by composer.
require 'autoload.php';
$server = new \Fetch\Server('imap.example.com', 993);
$server->setAuthentication('dummy', 'dummy');
@ -60,8 +39,3 @@ by composer.
foreach ($messages as $message) {
echo "Subject: {$message->getSubject()}\nBody: {$message->getMessageBody()}\n";
}
## License
Fetch is licensed under the BSD License. See the LICENSE file for details.

View file

@ -9,17 +9,11 @@
* file that was distributed with this source code.
*/
spl_autoload_register(function ($class) {
$base = '/src/';
if (strpos($class, 'Fetch\Test') === 0) {
$base = '/tests/';
}
$file = __DIR__.$base.strtr($class, '\\', '/').'.php';
spl_autoload_register(function($class)
{
$file = __DIR__.'/src/'.strtr($class, '\\', '/').'.php';
if (file_exists($file)) {
require $file;
return true;
}
});

View file

@ -2,7 +2,7 @@
"name": "tedivm/fetch",
"description": "A PHP IMAP Library",
"keywords": ["email","imap","pop3"],
"homepage": "http://github.com/tedious/Fetch",
"homepage": "http://github.com/tedivm/Fetch",
"type": "library",
"license": "BSD-3-Clause",
"authors": [
@ -12,14 +12,8 @@
}
],
"require": {
"php": ">=5.3.0",
"ext-imap": "*"
"php": ">=5.3.0"
},
"require-dev": {
"tedivm/dovecottesting": "1.2.3",
"phpunit/phpunit": "4.2.*",
"fabpot/php-cs-fixer": "0.5.*"
},
"autoload": {
"psr-0": {"Fetch": "src/"}
}

70
package.xml Normal file
View file

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="UTF-8"?>
<package packagerversion="1.9.4" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
<name>Fetch</name>
<channel>pear.tedivm.com</channel>
<summary>A PHP IMAP Library</summary>
<description>Fetch is a OOP Library that utilized the IMAP Extension for manipulating email and inboxes.</description>
<lead>
<name>Robert Hafner</name>
<user>tedivm</user>
<email>tedivm@tedivm.com</email>
<active>yes</active>
</lead>
<date>2012-11-25</date>
<time>23:19:16</time>
<version>
<release>0.4.1</release>
<api>0.4.1</api>
</version>
<stability>
<release>alpha</release>
<api>alpha</api>
</stability>
<license uri="http://www.opensource.org/licenses/bsd-license.php">BSD Style</license>
<notes>
-
</notes>
<contents>
<dir baseinstalldir="/" name="/">
<file baseinstalldir="/" md5sum="aa29e2803ede52f7a7fc72c3f81d0d24" name="src/Fetch/Attachment.php" role="php" />
<file baseinstalldir="/" md5sum="c76addeca63ab68b96139fbeb5360595" name="src/Fetch/Message.php" role="php" />
<file baseinstalldir="/" md5sum="fb619248da9039016029c565ade38ef2" name="src/Fetch/Server.php" role="php" />
<file baseinstalldir="/" md5sum="29ca91f2fab5be26e82e068d352d35e4" name="tests/bootstrap.php" role="test" />
<file baseinstalldir="/" md5sum="4a2ac5dab1f6ac50bb8d23a89b3d7fa1" name="tests/Fetch/Test/AttachmentTest.php" role="test" />
<file baseinstalldir="/" md5sum="93aa1dfc13bf71b65ebe8ab984fe13cb" name="tests/Fetch/Test/MessageTest.php" role="test" />
<file baseinstalldir="/" md5sum="b80a4c7d372abcab271c5b211579bcc8" name="tests/Fetch/Test/ServerTest.php" role="test" />
<file baseinstalldir="/" md5sum="b6f9cec64c464148284c7e87d46b0d86" name="autoload.php" role="php" />
<file baseinstalldir="/" md5sum="28ef12b4e82e5fb81125eef74096adb5" name="composer.json" role="data" />
<file baseinstalldir="/" md5sum="0a8fd596034db85a8ae22c1d2330edfb" name="LICENSE" role="doc" />
<file baseinstalldir="/" md5sum="65a7e6bd0be62f07792b832160168dea" name="README.md" role="data" />
</dir>
</contents>
<dependencies>
<required>
<php>
<min>5.3.0</min>
</php>
<pearinstaller>
<min>1.4.0</min>
</pearinstaller>
</required>
</dependencies>
<phprelease />
<changelog>
<release>
<version>
<release>0.4.1</release>
<api>0.4.1</api>
</version>
<stability>
<release>alpha</release>
<api>alpha</api>
</stability>
<date>2012-11-25</date>
<license uri="http://www.opensource.org/licenses/bsd-license.php">BSD Style</license>
<notes>
-
</notes>
</release>
</changelog>
</package>

View file

@ -1,27 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
bootstrap="tests/bootstrap.php"
>
<testsuites>
<testsuite name="Fetch Test Suite">
<directory>./tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">./src/Fetch/</directory>
</whitelist>
</filter>
<logging>
<log type="coverage-text" target="php://stdout" showUncoveredFiles="false"/>
</logging>
</phpunit>

View file

@ -87,56 +87,18 @@ class Attachment
$this->imapStream = $message->getImapBox()->getImapStream();
$this->structure = $structure;
if (isset($partIdentifier)) {
if (isset($partIdentifier))
$this->partId = $partIdentifier;
// message in message
if ($this->structure->type === 2 && strtoupper($this->structure->subtype) === 'RFC822') {
$this->filename = 'message.eml';
$header = imap_fetchmime($this->imapStream, $this->messageId, $this->partId, FT_UID);
if (strtolower(Message::$charset) === 'utf-8') {
try {
$hObject = imap_rfc822_parse_headers($header);
if (property_exists($hObject, 'subject')) {
$subject = MIME::decode($hObject->subject, Message::$charset);
$subject = preg_replace('#\s+#', ' ', $subject);
$subject = preg_replace('#^(.{0,50})#u', '$1', $subject);
if ($subject) {
$this->filename = $subject . '.eml';
}
}
} catch (\Throwable $e) {
}
}
}
}
$parameters = Message::getParametersFromStructure($structure);
// quick fix for Content-Disposition extended notation
// name*0*=UTF-8''%D0%A...
// name*1*=%D0%B8...
// etc
if (!empty($parameters['filename*'])) {
$this->setFileName($parameters['filename*']);
} elseif (!empty($parameters['name*'])) {
$this->setFileName($parameters['name*']);
if (isset($parameters['filename'])) {
$this->filename = $parameters['filename'];
} elseif (isset($parameters['name'])) {
$this->filename = $parameters['name'];
}
if (!empty($parameters['filename'])) {
$this->setFileName($parameters['filename']);
} elseif (!empty($parameters['name'])) {
$this->setFileName($parameters['name']);
}
if (property_exists($structure, 'bytes')) {
$this->size = $structure->bytes;
}
$this->size = $structure->bytes;
$this->mimeType = Message::typeIdToString($structure->type);
@ -155,18 +117,9 @@ class Attachment
public function getData()
{
if (!isset($this->data)) {
if ($this->partId) {
$messageBody = imap_fetchbody($this->imapStream, $this->messageId, $this->partId, FT_UID);
// message in message
if ($this->structure->type === 2 && strtoupper($this->structure->subtype) === 'RFC822') {
$header = imap_fetchmime($this->imapStream, $this->messageId, $this->partId, FT_UID);
return $this->data = $header . $messageBody;
}
} else {
$messageBody = imap_body($this->imapStream, $this->messageId, FT_UID);
}
$messageBody = isset($this->partId) ?
imap_fetchbody($this->imapStream, $this->messageId, $this->partId, FT_UID)
: imap_body($this->imapStream, $this->messageId, FT_UID);
$messageBody = Message::decode($messageBody, $this->encoding);
$this->data = $messageBody;
@ -205,16 +158,6 @@ class Attachment
return $this->size;
}
/**
* This function returns the object that contains the structure of this attachment.
*
* @return \stdClass
*/
public function getStructure()
{
return $this->structure;
}
/**
* This function saves the attachment to the passed directory, keeping the original name of the file.
*
@ -241,46 +184,18 @@ class Attachment
{
$dirname = dirname($path);
if (file_exists($path)) {
if (!is_writable($path)) {
if (!is_writable($path))
return false;
}
} elseif (!is_dir($dirname) || !is_writable($dirname)) {
return false;
}
if (($filePointer = fopen($path, 'w')) == false) {
if (($filePointer = fopen($path, 'w')) == false)
return false;
}
switch ($this->encoding) {
case 3: //base64
$streamFilter = stream_filter_append($filePointer, 'convert.base64-decode', STREAM_FILTER_WRITE);
break;
case 4: //quoted-printable
$streamFilter = stream_filter_append($filePointer, 'convert.quoted-printable-decode', STREAM_FILTER_WRITE);
break;
default:
$streamFilter = null;
}
// Fix an issue causing server to throw an error
// See: https://github.com/tedious/Fetch/issues/74 for more details
$fetch = imap_fetchbody($this->imapStream, $this->messageId, $this->partId ?: 1, FT_UID);
$result = imap_savebody($this->imapStream, $filePointer, $this->messageId, $this->partId ?: 1, FT_UID);
if ($streamFilter) {
stream_filter_remove($streamFilter);
}
$results = fwrite($filePointer, $this->getData());
fclose($filePointer);
return $result;
}
protected function setFileName($text)
{
$this->filename = MIME::decode($text, Message::$charset);
return is_numeric($results);
}
}

View file

@ -1,45 +0,0 @@
<?php
/*
* This file is part of the Fetch package.
*
* (c) Robert Hafner <tedivm@tedivm.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Fetch;
/**
* This library is a wrapper around the Imap library functions included in php.
*
* @package Fetch
* @author Robert Hafner <tedivm@tedivm.com>
* @author Sergey Linnik <linniksa@gmail.com>
*/
final class MIME
{
/**
* @param string $text
* @param string $targetCharset
*
* @return string
*/
public static function decode($text, $targetCharset = 'utf-8')
{
if (null === $text) {
return null;
}
$result = '';
foreach (imap_mime_header_decode($text) as $word) {
$ch = 'default' === $word->charset ? 'ascii' : $word->charset;
$result .= Message::charsetConvert($word->text, $ch, $targetCharset) ?: $word->text;
}
return $result;
}
}

View file

@ -42,13 +42,6 @@ class Message
*/
protected $imapStream;
/**
* This as an string which contains raw header information for the message.
*
* @var string
*/
protected $rawHeaders;
/**
* This as an object which contains header information for the message.
*
@ -83,7 +76,7 @@ class Message
*
* @var string
*/
protected static $flagTypes = array(self::FLAG_RECENT, self::FLAG_FLAGGED, self::FLAG_ANSWERED, self::FLAG_DELETED, self::FLAG_SEEN, self::FLAG_DRAFT);
protected static $flagTypes = array('recent', 'flagged', 'answered', 'deleted', 'seen', 'draft');
/**
* This holds the plantext email message.
@ -128,33 +121,19 @@ class Message
protected $from;
/**
* This is an array containing information about the address the email was sent from.
*
* @var string
*/
protected $sender;
/**
* This is an array of arrays that contains information about the addresses the email was sent to.
* This is an array of arrays that contain information about the addresses the email was cc'd to.
*
* @var array
*/
protected $to;
/**
* This is an array of arrays that contains information about the addresses the email was cc'd to.
* This is an array of arrays that contain information about the addresses the email was cc'd to.
*
* @var array
*/
protected $cc;
/**
* This is an array of arrays that contains information about the addresses the email was bcc'd to.
*
* @var array
*/
protected $bcc;
/**
* This is an array of arrays that contain information about the addresses that should receive replies to the email.
*
@ -169,53 +148,12 @@ class Message
*/
protected $attachments = array();
/**
* Contains the mailbox that the message resides in.
*
* @var string
*/
protected $mailbox;
/**
* This value defines the encoding we want the email message to use.
*
* @var string
*/
public static $charset = 'UTF-8';
/**
* This value defines the flag set for encoding if the mb_convert_encoding
* function can't be found, and in this case iconv encoding will be used.
*
* @var string
*/
public static $charsetFlag = '//TRANSLIT';
public static $charsetRenames = array(
'ks_c_5601-1987' => 'CP949',
);
/**
* These constants can be used to easily access available flags
*/
const FLAG_RECENT = 'recent';
const FLAG_FLAGGED = 'flagged';
const FLAG_ANSWERED = 'answered';
const FLAG_DELETED = 'deleted';
const FLAG_SEEN = 'seen';
const FLAG_DRAFT = 'draft';
/**
* When there's no "from" header in the message, code
* ```
* $val = '<' . $address['address'] . '>';
* ```
* in `self::getAddresses()` causes error.
*
* To avoid this error, if "from" header not found
* then it will be stored as array with this value
*/
const NO_FROM_HEADER_STUB_VALUE = 'unknown_sender';
public static $charset = 'UTF-8//TRANSLIT';
/**
* This constructor takes in the uid for the message and the Imap class representing the mailbox the
@ -225,14 +163,12 @@ class Message
* @param int $messageUniqueId
* @param Server $mailbox
*/
public function __construct($messageUniqueId, Server $connection)
public function __construct($messageUniqueId, Server $mailbox)
{
$this->imapConnection = $connection;
$this->mailbox = $connection->getMailBox();
$this->imapConnection = $mailbox;
$this->uid = $messageUniqueId;
$this->imapStream = $this->imapConnection->getImapStream();
if($this->loadMessage() !== true)
throw new \RuntimeException('Message with ID ' . $messageUniqueId . ' not found.');
$this->loadMessage();
}
/**
@ -245,19 +181,11 @@ class Message
/* First load the message overview information */
if(!is_object($messageOverview = $this->getOverview()))
$messageOverview = $this->getOverview();
return false;
if (property_exists($messageOverview, 'subject')) {
$this->subject = MIME::decode($messageOverview->subject, self::$charset);
}
if (property_exists($messageOverview, 'date') && null !== $messageOverview->date) {
$this->date = strtotime($messageOverview->date);
}
$this->size = $messageOverview->size;
$this->subject = $messageOverview->subject;
$this->date = strtotime($messageOverview->date);
$this->size = $messageOverview->size;
foreach (self::$flagTypes as $flag)
$this->status[$flag] = ($messageOverview->$flag == 1);
@ -272,36 +200,21 @@ class Message
if (isset($headers->cc))
$this->cc = $this->processAddressObject($headers->cc);
if (isset($headers->bcc))
$this->bcc = $this->processAddressObject($headers->bcc);
if (isset($headers->sender))
$this->sender = $this->processAddressObject($headers->sender);
$this->from = isset($headers->from)
? $this->processAddressObject($headers->from)
: array(
array(
'address' => self::NO_FROM_HEADER_STUB_VALUE,
),
)
;
$this->from = $this->processAddressObject($headers->from);
$this->replyTo = isset($headers->reply_to) ? $this->processAddressObject($headers->reply_to) : $this->from;
/* Finally load the structure itself */
$structure = $this->getStructure();
if (isset($structure->parts)) {
if (!isset($structure->parts)) {
// not multipart
$this->processStructure($structure);
} else {
// multipart
foreach ($structure->parts as $id => $part)
$this->processStructure($part, $id + 1);
} else {
// not multipart
$this->processStructure($structure);
}
return true;
}
/**
@ -317,34 +230,12 @@ class Message
if ($forceReload || !isset($this->messageOverview)) {
// returns an array, and since we just want one message we can grab the only result
$results = imap_fetch_overview($this->imapStream, $this->uid, FT_UID);
if ( sizeof($results) == 0 ) {
throw new \RuntimeException('Error fetching overview');
}
$this->messageOverview = array_shift($results);
if ( ! isset($this->messageOverview->date)) {
$this->messageOverview->date = null;
}
}
return $this->messageOverview;
}
/**
* This function returns an object containing the raw headers of the message.
*
* @param bool $forceReload
* @return string
*/
public function getRawHeaders($forceReload = false)
{
if ($forceReload || !isset($this->rawHeaders)) {
// raw headers (since imap_headerinfo doesn't use the unique id)
$this->rawHeaders = imap_fetchheader($this->imapStream, $this->uid, FT_UID);
}
return $this->rawHeaders;
}
/**
* This function returns an object containing the headers of the message. This is done by taking the raw headers
* and running them through the imap_rfc822_parse_headers function. The results are only retrieved from the server
@ -357,18 +248,13 @@ class Message
{
if ($forceReload || !isset($this->headers)) {
// raw headers (since imap_headerinfo doesn't use the unique id)
$rawHeaders = $this->getRawHeaders();
$rawHeaders = imap_fetchheader($this->imapStream, $this->uid, FT_UID);
// convert raw header string into a usable object
$headerObject = imap_rfc822_parse_headers($rawHeaders);
// to keep this object as close as possible to the original header object we add the udate property
if (isset($headerObject->date)) {
$headerObject->udate = strtotime($headerObject->date);
} else {
$headerObject->date = null;
$headerObject->udate = null;
}
$headerObject->udate = strtotime($headerObject->date);
$this->headers = $headerObject;
}
@ -387,7 +273,7 @@ class Message
public function getStructure($forceReload = false)
{
if ($forceReload || !isset($this->structure)) {
$this->structure = @imap_fetchstructure($this->imapStream, $this->uid, FT_UID);
$this->structure = imap_fetchstructure($this->imapStream, $this->uid, FT_UID);
}
return $this->structure;
@ -415,8 +301,7 @@ class Message
}
} else {
if (!isset($this->plaintextMessage) && isset($this->htmlMessage)) {
$output = preg_replace('/\s*\<br\s*\/?\>/i', PHP_EOL, trim($this->htmlMessage) );
$output = strip_tags($output);
$output = strip_tags($this->htmlMessage);
return $output;
} elseif (isset($this->plaintextMessage)) {
@ -427,45 +312,25 @@ class Message
return false;
}
/**
* This function returns the plain text body of the email or false if not present.
* @return string|bool Returns false if not present
*/
public function getPlainTextBody()
{
return isset($this->plaintextMessage) ? $this->plaintextMessage : false;
}
/**
* This function returns the HTML body of the email or false if not present.
* @return string|bool Returns false if not present
*/
public function getHtmlBody()
{
return isset($this->htmlMessage) ? $this->htmlMessage : false;
}
/**
* This function returns either an array of email addresses and names or, optionally, a string that can be used in
* mail headers.
*
* @param string $type Should be 'to', 'cc', 'bcc', 'from', 'sender', or 'reply-to'.
* @param string $type Should be 'to', 'cc', 'from', or 'reply-to'.
* @param bool $asString
* @return array|string|bool
*/
public function getAddresses($type, $asString = false)
{
$type = ( $type == 'reply-to' ) ? 'replyTo' : $type;
$addressTypes = array('to', 'cc', 'bcc', 'from', 'sender', 'replyTo');
$addressTypes = array('to', 'cc', 'from', 'reply-to');
if (!in_array($type, $addressTypes) || !isset($this->$type) || count($this->$type) < 1)
return false;
if (!$asString) {
if ($type == 'from')
return $this->from[0];
elseif ($type == 'sender')
return $this->sender[0];
return $this->$type;
} else {
@ -476,12 +341,9 @@ class Message
if (!isset($set))
$set = true;
$val = '<' . $address['address'] . '>';
if (isset($address['name'])) {
$val = '"' . preg_replace('/\W/u', '\\\\$0', $address['name']) . '" ' . $val;
}
$outputString .= $val;
$outputString .= isset($address['name']) ?
$address['name'] . ' <' . $address['address'] . '>'
: $address['address'];
}
return $outputString;
@ -505,7 +367,7 @@ class Message
*/
public function getSubject()
{
return isset($this->subject) ? $this->subject : null;
return $this->subject;
}
/**
@ -535,46 +397,27 @@ class Message
*
* @param \stdClass $structure
* @param string $partIdentifier
* @todoa process attachments.
*/
protected function processStructure($structure, $partIdentifier = null)
{
if (!$structure) {
return;
}
$parameters = self::getParametersFromStructure($structure);
// quick fix for Content-Disposition extended notation
// name*0*=UTF-8''%D0%A...
// name*1*=%D0%B8...
// etc
if (empty($parameters['name']) && !empty($parameters['name*'])) {
$parameters['name'] = $parameters['name*'];
}
if (empty($parameters['filename']) && !empty($parameters['filename*'])) {
$parameters['filename'] = $parameters['filename*'];
}
if (!empty($parameters['name']) || !empty($parameters['filename'])) {
$attachment = new Attachment($this, $structure, $partIdentifier);
if (isset($parameters['name']) || isset($parameters['filename'])) {
$attachment = new Attachment($this, $structure, $partIdentifier);
$this->attachments[] = $attachment;
} else if (strtoupper($structure->subtype) === 'RFC822') {
$attachment = new Attachment($this, $structure, $partIdentifier);
$this->attachments[] = $attachment;
// do not process subparts (maybe it should be for all attachments?)
return;
} elseif ($structure->type == 0 || $structure->type == 1) {
$messageBody = isset($partIdentifier) ?
imap_fetchbody($this->imapStream, $this->uid, $partIdentifier, FT_UID | FT_PEEK)
: imap_body($this->imapStream, $this->uid, FT_UID | FT_PEEK);
imap_fetchbody($this->imapStream, $this->uid, $partIdentifier, FT_UID)
: imap_body($this->imapStream, $this->uid, FT_UID);
$messageBody = self::decode($messageBody, $structure->encoding);
if (!empty($parameters['charset'])) {
$messageBody = self::charsetConvert($messageBody, $parameters['charset'], self::$charset) ?: $messageBody;
}
if (!empty($parameters['charset']) && $parameters['charset'] !== self::$charset)
$messageBody = iconv($parameters['charset'], self::$charset, $messageBody);
if (strtolower($structure->subtype) === 'plain' || ($structure->type == 1 && strtolower($structure->subtype) !== 'alternative')) {
if (strtolower($structure->subtype) == 'plain' || $structure->type == 1) {
if (isset($this->plaintextMessage)) {
$this->plaintextMessage .= PHP_EOL . PHP_EOL;
} else {
@ -582,7 +425,8 @@ class Message
}
$this->plaintextMessage .= trim($messageBody);
} elseif (strtolower($structure->subtype) === 'html') {
} else {
if (isset($this->htmlMessage)) {
$this->htmlMessage .= '<br><br>';
} else {
@ -606,67 +450,6 @@ class Message
}
}
/**
* @param string $text
* @param string $from
* @param string $to
*
* @return string|null
*/
public static function charsetConvert($text, $from, $to = null)
{
if (!$text) {
return '';
}
if (null === $to) {
$to = self::$charset;
}
$from = strtolower($from);
$to = strtolower($to);
if (isset(self::$charsetRenames[$from])) {
$from = self::$charsetRenames[$from];
}
if ($from === $to) {
if ($to === 'utf-8') {
return UTF8::fix($text);
}
return $text;
}
$converted = null;
if (!$converted && function_exists('mb_convert_encoding')) {
// `@` оставлена для совместимости с php7, так как в этой
// версии выкидывается warning, который надо подавлять
try {
if (@mb_check_encoding($text, $from)) {
$converted = @mb_convert_encoding($text, $to, $from);
}
} catch (\ValueError $e) {
// noop
}
}
if (!$converted && function_exists('iconv')) {
// Для `iconv` @ пока работает
$converted = @iconv($from, $to . self::$charsetFlag, $text);
}
if ($converted) {
return $converted;
}
if ($to === 'utf-8') {
return UTF8::fix($text);
}
return null;
}
/**
* This function takes in the message data and encoding type and returns the decoded data.
*
@ -676,17 +459,16 @@ class Message
*/
public static function decode($data, $encoding)
{
if (!is_numeric($encoding)) {
if (!is_numeric($encoding))
$encoding = strtolower($encoding);
}
switch (true) {
case $encoding === 'quoted-printable':
case $encoding === 4:
switch ($encoding) {
case 'quoted-printable':
case 4:
return quoted_printable_decode($data);
case $encoding === 'base64':
case $encoding === 3:
case 'base64':
case 3:
return base64_decode($data);
default:
@ -762,20 +544,11 @@ class Message
$outputAddresses = array();
if (is_array($addresses))
foreach ($addresses as $address) {
if (property_exists($address, 'mailbox') && $address->mailbox != 'undisclosed-recipients') {
$currentAddress = array();
$host = '';
if (property_exists($address, 'host')) {
$host = $address->host;
}
$currentAddress['address'] = $address->mailbox . '@' . $host;
if (isset($address->personal)) {
$currentAddress['name'] = MIME::decode($address->personal, self::$charset);
}
$outputAddresses[] = $currentAddress;
}
$currentAddress = array();
$currentAddress['address'] = $address->mailbox . '@' . $address->host;
if (isset($address->personal))
$currentAddress['name'] = $address->personal;
$outputAddresses[] = $currentAddress;
}
return $outputAddresses;
@ -831,43 +604,30 @@ class Message
* @param string $flag Recent, Flagged, Answered, Deleted, Seen, Draft
* @return bool
*/
public function checkFlag($flag = self::FLAG_FLAGGED)
public function checkFlag($flag = 'flagged')
{
return (isset($this->status[$flag]) && $this->status[$flag] === true);
return (isset($this->status[$flag]) && $this->status[$flag] == true);
}
/**
* This function is used to enable or disable one or more flags on the imap message.
* This function is used to enable or disable a flag on the imap message.
*
* @param string|array $flag Flagged, Answered, Deleted, Seen, Draft
* @param string $flag Flagged, Answered, Deleted, Seen, Draft
* @param bool $enable
* @throws \InvalidArgumentException
* @return bool
*/
public function setFlag($flag, $enable = true)
{
$flags = (is_array($flag)) ? $flag : array($flag);
if (!in_array($flag, self::$flagTypes) || $flag == 'recent')
throw new \InvalidArgumentException('Unable to set invalid flag "' . $flag . '"');
foreach ($flags as $i => $flag) {
$flag = ltrim(strtolower($flag), '\\');
if (!in_array($flag, self::$flagTypes) || $flag == self::FLAG_RECENT)
throw new \InvalidArgumentException('Unable to set invalid flag "' . $flag . '"');
$flag = '\\' . ucfirst($flag);
if ($enable) {
$this->status[$flag] = true;
} else {
unset($this->status[$flag]);
}
$flags[$i] = $flag;
}
$imapifiedFlag = '\\'.implode(' \\', array_map('ucfirst', $flags));
if ($enable === true) {
return imap_setflag_full($this->imapStream, $this->uid, $imapifiedFlag, ST_UID);
if ($enable) {
return imap_setflag_full($this->imapStream, $this->uid, $flag, ST_UID);
} else {
return imap_clearflag_full($this->imapStream, $this->uid, $imapifiedFlag, ST_UID);
return imap_clearflag_full($this->imapStream, $this->uid, $flag, ST_UID);
}
}
@ -880,16 +640,6 @@ class Message
*/
public function moveToMailBox($mailbox)
{
$currentBox = $this->imapConnection->getMailBox();
$this->imapConnection->setMailBox($this->mailbox);
$returnValue = imap_mail_copy($this->imapStream, $this->uid, $mailbox, CP_UID | CP_MOVE);
imap_expunge($this->imapStream);
$this->mailbox = $mailbox;
$this->imapConnection->setMailBox($currentBox);
return $returnValue;
return imap_mail_copy($this->imapStream, $this->uid, $mailbox, CP_UID | CP_MOVE);
}
}

View file

@ -54,7 +54,7 @@ class Server
*
* @var string
*/
protected $mailbox = '';
protected $mailbox;
/**
* This is the username used to connect to the server.
@ -93,16 +93,11 @@ class Server
*/
protected $options = 0;
/**
* This is the set of connection parameters
*
* @var array
*/
protected $params = array();
/**
* This is the resource connection to the server. It is required by a number of imap based functions to specify how
* to connect.
*
* @var resource
*/
protected $imapStream;
@ -145,35 +140,24 @@ class Server
*
* @param string $username
* @param string $password
* @param bool $tryFasterAuth tries to auth faster by disabling GSSAPI & NTLM auth methods (set to false if you use either of these auth methods)
*/
public function setAuthentication($username, $password, $tryFasterAuth=true)
public function setAuthentication($username, $password)
{
$this->username = $username;
$this->password = $password;
if ($tryFasterAuth) {
$this->setParam('DISABLE_AUTHENTICATOR', array('GSSAPI','NTLM'));
}
}
/**
* This function sets the mailbox to connect to.
*
* @param string $mailbox
* @return bool
* @param string $mailbox
*/
public function setMailBox($mailbox = '')
{
if (!$this->hasMailBox($mailbox)) {
return false;
}
$this->mailbox = $mailbox;
if (isset($this->imapStream)) {
$this->setImapStream();
}
return true;
}
public function getMailBox()
@ -195,27 +179,21 @@ class Server
return;
if (isset(self::$exclusiveFlags[$flag])) {
$kill = self::$exclusiveFlags[$flag];
$kill = $flag;
} elseif ($index = array_search($flag, self::$exclusiveFlags)) {
$kill = $index;
}
if (isset($kill) && false !== $index = array_search($kill, $this->flags))
unset($this->flags[$index]);
if (isset($kill) && isset($this->flags[$kill]))
unset($this->flags[$kill]);
$index = array_search($flag, $this->flags);
if (isset($value) && $value !== true) {
if ($value == false && $index !== false) {
unset($this->flags[$index]);
} elseif ($value != false) {
$match = preg_grep('/' . $flag . '/', $this->flags);
if (reset($match)) {
$this->flags[key($match)] = $flag . '=' . $value;
} else {
$this->flags[] = $flag . '=' . $value;
}
if ($value == false) {
unset($this->flags[$flag]);
} else {
$this->flags[] = $flag . '=' . $value;
}
} elseif ($index === false) {
} else {
$this->flags[] = $flag;
}
}
@ -229,28 +207,19 @@ class Server
public function setOptions($bitmask = 0)
{
if (!is_numeric($bitmask))
throw new \RuntimeException('Function requires numeric argument.');
throw new \Exception();
$this->options = $bitmask;
}
/**
* This function is used to set connection parameters
*
* @param string $key
* @param string $value
*/
public function setParam($key, $value)
{
$this->params[$key] = $value;
}
/**
* This function gets the current saved imap resource and returns it.
*
* @return resource
*/
public function getImapStream()
{
if (empty($this->imapStream))
if (!isset($this->imapStream))
$this->setImapStream();
return $this->imapStream;
@ -302,11 +271,11 @@ class Server
*/
protected function setImapStream()
{
if (!empty($this->imapStream)) {
if (isset($this->imapStream)) {
if (!imap_reopen($this->imapStream, $this->getServerString(), $this->options, 1))
throw new \RuntimeException(imap_last_error());
} else {
$imapStream = @imap_open($this->getServerString(), $this->username, $this->password, $this->options, 1, $this->params);
$imapStream = imap_open($this->getServerString(), $this->username, $this->password, $this->options, 1);
if ($imapStream === false)
throw new \RuntimeException(imap_last_error());
@ -318,22 +287,11 @@ class Server
/**
* This returns the number of messages that the current mailbox contains.
*
* @param string $mailbox
* @return int
*/
public function numMessages($mailbox='')
public function numMessages()
{
$cnt = 0;
if ($mailbox==='') {
$cnt = imap_num_msg($this->getImapStream());
} elseif ($this->hasMailbox($mailbox) && $mailbox !== '') {
$oldMailbox = $this->getMailBox();
$this->setMailbox($mailbox);
$cnt = $this->numMessages();
$this->setMailbox($oldMailbox);
}
return ((int) $cnt);
return imap_num_msg($this->getImapStream());
}
/**
@ -401,40 +359,6 @@ class Server
return $messages;
}
/**
* Returns the emails in the current mailbox as an array of ImapMessage objects
* ordered by some ordering
*
* @see http://php.net/manual/en/function.imap-sort.php
* @param int $orderBy
* @param bool $reverse
* @param int $limit
* @return Message[]
*/
public function getOrderedMessages($orderBy, $reverse, $limit)
{
$msgIds = imap_sort($this->getImapStream(), $orderBy, $reverse ? 1 : 0, SE_UID);
return array_map(array($this, 'getMessageByUid'), array_slice($msgIds, 0, $limit));
}
/**
* Returns the requested email or false if it is not found.
*
* @param int $uid
* @return Message|bool
*/
public function getMessageByUid($uid)
{
try {
$message = new \Fetch\Message($uid, $this);
return $message;
} catch (\Exception $e) {
return false;
}
}
/**
* This function removes all of the messages flagged for deletion from the mailbox.
*
@ -454,19 +378,7 @@ class Server
*/
public function hasMailBox($mailbox)
{
return (boolean) $this->getMailBoxDetails($mailbox);
}
/**
* Return information about the mailbox or mailboxes
*
* @param $mailbox
*
* @return array
*/
public function getMailBoxDetails($mailbox)
{
return imap_getmailboxes(
return (boolean) imap_getmailboxes(
$this->getImapStream(),
$this->getServerString(),
$this->getServerSpecification() . $mailbox
@ -484,28 +396,4 @@ class Server
{
return imap_createmailbox($this->getImapStream(), $this->getServerSpecification() . $mailbox);
}
/**
* List available mailboxes
*
* @param string $pattern
*
* @return array
*/
public function listMailBoxes($pattern = '*')
{
return imap_list($this->getImapStream(), $this->getServerSpecification(), $pattern);
}
/**
* Deletes the given mailbox.
*
* @param $mailbox
*
* @return bool
*/
public function deleteMailBox($mailbox)
{
return imap_deletemailbox($this->getImapStream(), $this->getServerSpecification() . $mailbox);
}
}

View file

@ -1,64 +0,0 @@
<?php
namespace Fetch;
final class UTF8
{
public static function fix($text)
{
if (!is_string($text) || !$text) {
return $text;
}
if (\function_exists('mb_convert_encoding')) {
if ($val = @mb_convert_encoding($text, 'utf-8', 'utf-8')) {
return $val;
}
}
$buf = '';
for ($i = 0, $max = strlen($text); $i < $max; $i++) {
$c1 = $text[$i];
if ($c1 <= "\x7F") { // single byte
$buf .= $c1;
} elseif ($c1 <= "\xC1") { // single byte (invalid)
$buf .= '?';
} elseif ($c1 <= "\xDF") { // 2 byte
$c2 = $i + 1 >= $max ? "\x00" : $text[$i + 1];
if ($c2 >= "\x80" && $c2 <= "\xBF") {
$buf .= $c1 . $c2;
$i += 1;
} else {
$buf .= '?';
}
} elseif ($c1 <= "\xEF") { // 3 bytes
$c2 = $i + 1 >= $max ? "\x00" : $text[$i + 1];
$c3 = $i + 2 >= $max ? "\x00" : $text[$i + 2];
if ($c2 >= "\x80" && $c2 <= "\xbf" && $c3 >= "\x80" && $c3 <= "\xbf") {
$buf .= $c1 . $c2 . $c3;
$i += 2;
} else {
$buf .= '?';
}
} else if ($c1 <= "\xF4") { //Should be converted to UTF8, if it's not UTF8 already
$c2 = $i + 1 >= $max ? "\x00" : $text[$i + 1];
$c3 = $i + 2 >= $max ? "\x00" : $text[$i + 2];
$c4 = $i + 3 >= $max ? "\x00" : $text[$i + 3];
if ($c2 >= "\x80" && $c2 <= "\xbf" && $c3 >= "\x80" && $c3 <= "\xbf" && $c4 >= "\x80" && $c4 <= "\xbf") {
$buf .= $c1 . $c2 . $c3 . $c4;
$i += 3;
} else {
$buf .= '?';
}
} else { // invalid
$buf .= '?';
}
}
return $buf;
}
}

View file

@ -11,6 +11,7 @@
namespace Fetch\Test;
/**
* @package Fetch
* @author Robert Hafner <tedivm@tedivm.com>
@ -18,101 +19,4 @@ namespace Fetch\Test;
class AttachmentTest extends \PHPUnit_Framework_TestCase
{
public static function getAttachments($MessageId)
{
$server = ServerTest::getServer();
$message = new \Fetch\Message($MessageId, $server);
$attachments = $message->getAttachments();
$returnAttachments = array();
foreach($attachments as $attachment)
$returnAttachments[$attachment->getFileName()] = $attachment;
return $returnAttachments;
}
public function testGetData()
{
$attachments = static::getAttachments('6');
$attachment_RCA = $attachments['RCA_Indian_Head_test_pattern.JPG.zip'];
$md5_RCA = '3e9b6f02551590a7bcfff5d50b5b7b20';
$this->assertEquals($md5_RCA, md5($attachment_RCA->getData()));
$attachment_TestCard = $attachments['Test_card.png.zip'];
$md5_TestCard = '94c40bd83fbfa03b29bf1811f9aaccea';
$this->assertEquals($md5_TestCard, md5($attachment_TestCard->getData()));
}
public function testGetMimeType()
{
$attachments = static::getAttachments('6');
$attachment_RCA = $attachments['RCA_Indian_Head_test_pattern.JPG.zip'];
$mimetype_RCA = 'application/zip';
$this->assertEquals($mimetype_RCA, $attachment_RCA->getMimeType());
$attachment_TestCard = $attachments['Test_card.png.zip'];
$mimetype_TestCard = 'application/zip';
$this->assertEquals($mimetype_TestCard, $attachment_TestCard->getMimeType());
}
public function testGetSize()
{
$attachments = static::getAttachments('6');
$attachment_RCA = $attachments['RCA_Indian_Head_test_pattern.JPG.zip'];
$size_RCA = 378338;
$this->assertEquals($size_RCA, $attachment_RCA->getSize());
$attachment_TestCard = $attachments['Test_card.png.zip'];
$size_TestCard = 32510;
$this->assertEquals($size_TestCard, $attachment_TestCard->getSize());
}
public function testGetStructure()
{
$attachments = static::getAttachments('6');
$attachment_RCA = $attachments['RCA_Indian_Head_test_pattern.JPG.zip'];
$structure_RCA = $attachment_RCA->getStructure();
$this->assertObjectHasAttribute('type', $structure_RCA);
$this->assertEquals(3, $structure_RCA->type);
$this->assertObjectHasAttribute('subtype', $structure_RCA);
$this->assertEquals('ZIP', $structure_RCA->subtype);
$this->assertObjectHasAttribute('bytes', $structure_RCA);
$this->assertEquals(378338, $structure_RCA->bytes);
}
public function testSaveToDirectory()
{
$attachments = static::getAttachments('6');
$attachment_RCA = $attachments['RCA_Indian_Head_test_pattern.JPG.zip'];
$tmpdir = rtrim(sys_get_temp_dir(), '/') . '/';
$filepath = $tmpdir . 'RCA_Indian_Head_test_pattern.JPG.zip';
$this->assertTrue($attachment_RCA->saveToDirectory($tmpdir));
$this->assertFileExists($filepath);
$this->assertEquals(md5(file_get_contents($filepath)), md5($attachment_RCA->getData()));
$attachments = static::getAttachments('6');
$attachment_RCA = $attachments['RCA_Indian_Head_test_pattern.JPG.zip'];
$this->assertFalse($attachment_RCA->saveToDirectory('/'), 'Returns false when attempting to save without filesystem permission.');
$attachments = static::getAttachments('6');
$attachment_RCA = $attachments['RCA_Indian_Head_test_pattern.JPG.zip'];
$this->assertFalse($attachment_RCA->saveToDirectory($filepath), 'Returns false when attempting to save over a file.');
}
public static function tearDownAfterClass()
{
$tmpdir = rtrim(sys_get_temp_dir(), '/') . '/';
$filepath = $tmpdir . 'RCA_Indian_Head_test_pattern.JPG.zip';
unlink($filepath);
}
}

View file

@ -1,65 +0,0 @@
<?php
/*
* This file is part of the Fetch library.
*
* (c) Robert Hafner <tedivm@tedivm.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Fetch\Test;
use Fetch\MIME;
/**
* @package Fetch
* @author Robert Hafner <tedivm@tedivm.com>
* @author Sergey Linnik <linniksa@gmail.com>
*/
class MIMETest extends \PHPUnit_Framework_TestCase
{
public function decodeData()
{
return array(
array(null, null),
array('Just text', 'Just text'),
array('Keith Moore <moore@cs.utk.edu>', '=?US-ASCII?Q?Keith_Moore?= <moore@cs.utk.edu>'),
array('Keld Jørn Simonsen <keld@dkuug.dk>', '=?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?= <keld@dkuug.dk>'),
array('André Pirard <PIRARD@vm1.ulg.ac.be>', '=?ISO-8859-1?Q?Andr=E9?= Pirard <PIRARD@vm1.ulg.ac.be>'),
array(
'If you can read this you understand the example.',
'=?ISO-8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?='
. PHP_EOL .
'=?ISO-8859-2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?='
),
array(
'ab11 ? ?????.jpg',
"\x61\x62\x31\x31\x20\x97\x20\x3f\x3f\x3f\x3f\x3f\x2e\x6a\x70\x67",
),
array(
'?????? ?????.pdf',
'=?UTF-8?B?' .base64_encode("\xCF\xF0\xE8\xEC\xE5\xF0 \xEF\xEB\xE0\xED\xE0\x2E\x70\x64\x66") . '?=',
),
array(
' (ИСТРЕБИТЕЛЬ ЛЕТАЮЩИХ НАСЕКОМЫХ "БАРГУЗИН" КП ДИЛЕР - 2019г)',
' =?ks_c_5601-1987?B?ICisqqyzrLSssqymrKKsqqy0rKasray+IKytrKastKyhrMCsu6yq?=' .
' =?ks_c_5601-1987?B?rLcgrK+soayzrKasrKywrK6svay3ICKsoqyhrLKspKy1?=' .
' =?ks_c_5601-1987?B?rKmsqqyvIiCsrKyxIKylrKqsraymrLIgLSAyMDE5rNQp?=',
),
);
}
/**
* @dataProvider decodeData
*
* @param string $expected
* @param string $text
* @param string $charset
*/
public function testDecode($expected, $text, $charset = 'UTF-8')
{
self::assertSame($expected, MIME::decode($text, $charset));
}
}

View file

@ -10,7 +10,7 @@
*/
namespace Fetch\Test;
use Fetch\Message;
/**
* @package Fetch
@ -18,284 +18,5 @@ use Fetch\Message;
*/
class MessageTest extends \PHPUnit_Framework_TestCase
{
public static function getMessage($id)
{
$server = ServerTest::getServer();
return new \Fetch\Message($id, $server);
}
public function testConstructMessage()
{
$message = static::getMessage(3);
$this->assertInstanceOf('\Fetch\Message', $message);
}
public function testGetOverview()
{
$message = static::getMessage(3);
$overview = $message->getOverview();
$this->assertEquals('Welcome', $overview->subject, 'Subject available from overview');
$this->assertEquals('tedivm@tedivm.com', $overview->from, 'From available from overview');
$this->assertEquals('testuser@tedivm.com', $overview->to, 'To available from overview');
$this->assertEquals(1465, $overview->size, 'Size available from overview');
$this->assertEquals(0, $overview->flagged, 'Flagged available from overview');
$this->assertEquals(1, $overview->seen, 'Seen available from overview');
}
public function testGetHeaders()
{
$message = static::getMessage(3);
$headers = $message->getHeaders();
$this->assertEquals('Sun, 1 Dec 2013 21:14:03 -0800 (PST)', $headers->date, 'Headers contain the right date.');
$this->assertEquals('testuser@tedivm.com', $headers->toaddress, 'Headers contain toaddress.');
$this->assertEquals('tedivm@tedivm.com', $headers->fromaddress, 'Headers contain fromaddress');
}
public function testGetStructure()
{
}
public function testGetMessageBody()
{
// easiest way to deal with php encoding issues is simply not to.
$plaintextTest = 'f9377a89c9c935463a2b35c92dd61042';
$convertedHtmlTest = '11498bcf191900d634ff8772a64ca523';
$pureHtmlTest = '6a366ddecf080199284146d991d52169';
$message = static::getMessage(3);
$messageNonHTML = $message->getMessageBody();
$this->assertEquals($plaintextTest, md5($messageNonHTML), 'Message returns as plaintext.');
$messageHTML = $message->getMessageBody(true);
$this->assertEquals($convertedHtmlTest, md5($messageHTML), 'Message converts from plaintext to HTML when requested.');
$message = static::getMessage(4);
$messageHTML = $message->getMessageBody(true);
$this->assertEquals($pureHtmlTest, md5($messageHTML), 'Message returns as HTML.');
}
public function testGetPlainTextBody()
{
// easiest way to deal with php encoding issues is simply not to.
$plaintextTest1 = 'f9377a89c9c935463a2b35c92dd61042';
$plaintextTest2 = '0b8fc9b534a1789f1071f996f238a07a';
$plaintextTest3 = 'd41d8cd98f00b204e9800998ecf8427e';
$message = static::getMessage(3);
$messagePlainText = $message->getPlainTextBody();
$this->assertEquals($plaintextTest1, md5($messagePlainText), 'Message returns as plaintext.');
$message = static::getMessage(4);
$messagePlainText = $message->getPlainTextBody();
$this->assertEquals($plaintextTest2, md5($messagePlainText), 'Message returns as plaintext.');
$message = static::getMessage(6);
$messagePlainText = $message->getPlainTextBody();
$this->assertEquals($plaintextTest3, md5($messagePlainText), 'Message does not return as plaintext.');
}
public function testGetHtmlBody()
{
// easiest way to deal with php encoding issues is simply not to.
$HtmlTest1 = 'd41d8cd98f00b204e9800998ecf8427e';
$HtmlTest2 = '6a366ddecf080199284146d991d52169';
$message = static::getMessage(3);
$messageHtml = $message->getHtmlBody();
$this->assertEquals($HtmlTest1, md5($messageHtml), 'Message does not return as HTML.');
$message = static::getMessage(4);
$messageHtml = $message->getHtmlBody();
$this->assertEquals($HtmlTest2, md5($messageHtml), 'Message returns as HTML.');
}
public function testGetAddresses()
{
$message = static::getMessage(3);
$addresses = $message->getAddresses('to');
$this->assertEquals('testuser@tedivm.com', $addresses[0]['address'], 'Retrieving to user from address array.');
$addressString = $message->getAddresses('to', true);
$this->assertEquals('testuser@tedivm.com', $addressString, 'Returning To address as string.');
$addresses = $message->getAddresses('from');
$this->assertEquals('tedivm@tedivm.com', $addresses['address'], 'Returning From address as an address array.');
$addressString = $message->getAddresses('from', true);
$this->assertEquals('tedivm@tedivm.com', $addressString, 'Returning From address as string.');
}
public function testGetDate()
{
$message = static::getMessage(3);
$this->assertEquals(1385961243, $message->getDate(), 'Returns date as timestamp.');
}
public function testGetSubject()
{
$message = static::getMessage(3);
$this->assertEquals('Welcome', $message->getSubject(), 'Returns Subject.');
}
public function testDelete()
{
}
public function testGetImapBox()
{
$server = ServerTest::getServer();
$message = new \Fetch\Message('3', $server);
$this->assertEquals($server, $message->getImapBox(), 'getImapBox returns Server used to create Message.');
}
public function testGetUid()
{
$message = static::getMessage('3');
$this->assertEquals(3, $message->getUid(), 'Message returns UID');
}
public function testGetAttachments()
{
$messageWithoutAttachments = static::getMessage('3');
$this->assertFalse($messageWithoutAttachments->getAttachments(), 'getAttachments returns false when no attachments present.');
$messageWithAttachments = static::getMessage('6');
$attachments = $messageWithAttachments->getAttachments();
$this->assertCount(2, $attachments);
foreach($attachments as $attachment)
$this->assertInstanceOf('\Fetch\Attachment', $attachment, 'getAttachments returns Fetch\Attachment objects.');
$attachment = $messageWithAttachments->getAttachments('Test_card.png.zip');
$this->assertInstanceOf('\Fetch\Attachment', $attachment, 'getAttachment returns specified Fetch\Attachment object.');
}
public function testCheckFlag()
{
$message = static::getMessage('3');
$this->assertFalse($message->checkFlag('flagged'));
$this->assertTrue($message->checkFlag('seen'));
}
public function testSetFlag()
{
$message = static::getMessage('3');
$this->assertFalse($message->checkFlag('answered'), 'Message is not answered.');
$this->assertTrue($message->setFlag('answered'), 'setFlag returned true.');
$this->assertTrue($message->checkFlag('answered'), 'Message was successfully answered.');
$this->assertTrue($message->setFlag('answered', false), 'setFlag returned true.');
$this->assertFalse($message->checkFlag('answered'), 'Message was successfully unanswered.');
$message = static::getMessage('2');
$this->assertFalse($message->checkFlag('flagged'), 'Message is not flagged.');
$this->assertTrue($message->setFlag('flagged'), 'setFlag returned true.');
$this->assertTrue($message->checkFlag('flagged'), 'Message was successfully flagged.');
$message = static::getMessage('2');
$this->assertTrue($message->setFlag('flagged', false), 'setFlag returned true.');
$this->assertFalse($message->checkFlag('flagged'), 'Message was successfully unflagged.');
}
public function testMoveToMailbox()
{
$server = ServerTest::getServer();
// Testing by moving message from "Test Folder" to "Sent"
// Count Test Folder
$testFolderNumStart = $server->numMessages('Test Folder');
$server->setMailbox('Test Folder');
$this->assertEquals($testFolderNumStart, $server->numMessages(), 'Server presents consistent information between numMessages when mailbox set and directly queried for number of messages');
// Get message from Test Folder
$message = current($server->getMessages(1));
$this->assertInstanceOf('\Fetch\Message', $message, 'Server returned Message.');
// Switch to Sent folder, count messages
$sentFolderNumStart = $server->numMessages('Sent');
$server->setMailbox('Sent');
$this->assertEquals($sentFolderNumStart, $server->numMessages(), 'Server presents consistent information between numMessages when mailbox set and directly queried for number of messages');
// Switch to "Flagged" folder in order to test that function properly returns to it
$this->assertTrue($server->setMailBox('Flagged Email'));
// Move the message!
$this->assertTrue($message->moveToMailBox('Sent'));
// Make sure we're still in the same folder
$this->assertEquals('Flagged Email', $server->getMailBox(), 'Returned Server back to right mailbox.');
$this->assertAttributeEquals('Sent', 'mailbox', $message, 'Message mailbox changed to new location.');
// Make sure Test Folder lost a message
$this->assertTrue($server->setMailBox('Test Folder'));
$this->assertEquals($testFolderNumStart - 1, $server->numMessages(), 'Message moved out of Test Folder.');
// Make sure Sent folder gains one
$this->assertTrue($server->setMailBox('Sent'));
$this->assertEquals($sentFolderNumStart + 1, $server->numMessages(), 'Message moved into Sent Folder.');
}
public function testCharsetConvert()
{
$this->assertSame('Привет', Message::charsetConvert(
implode(array_map('chr', array(0xF0, 0xD2, 0xC9, 0xD7, 0xC5, 0xD4))),
'koi8-r',
'utf-8'
));
$this->assertSame('test', Message::charsetConvert('test', 'unk1', 'unk1'), 'Same charsets not try converting');
$this->assertSame('', Message::charsetConvert('', 'unk1', 'unk1'), 'Empty text not try converting');
$this->assertSame(null, Message::charsetConvert('test', 'unk1', 'utf-8'), 'Null when source charset is unknown');
$this->assertSame(null, Message::charsetConvert('test', 'utf-8', 'unk1'), 'Null when destination charset is unknown');
}
public function testDecode()
{
$quotedPrintableDecoded = "Now's the time for all folk to come to the aid of their country.";
$quotedPrintable = <<<'ENCODE'
Now's the time =
for all folk to come=
to the aid of their country.
ENCODE;
$this->assertEquals($quotedPrintableDecoded, Message::decode($quotedPrintable, 'quoted-printable'), 'Decodes quoted printable');
$this->assertEquals($quotedPrintableDecoded, Message::decode($quotedPrintable, 4), 'Decodes quoted printable');
$testString = 'This is a test string';
$base64 = base64_encode($testString);
$this->assertEquals($testString, Message::decode($base64, 'base64'), 'Decodes quoted base64');
$this->assertEquals($testString, Message::decode($base64, 3), 'Decodes quoted base64');
$notEncoded = '> w - www.somesite.com.au<http://example.com/track/click.php?u=30204369&id=af4110cab28e464cb0702723bc84b3f3>';
$this->assertEquals($notEncoded, Message::decode($notEncoded, 0), 'Nothing to decode');
}
public function testTypeIdToString()
{
$types = array();
$types[0] = 'text';
$types[1] = 'multipart';
$types[2] = 'message';
$types[3] = 'application';
$types[4] = 'audio';
$types[5] = 'image';
$types[6] = 'video';
$types[7] = 'other';
$types[8] = 'other';
$types[32] = 'other';
foreach($types as $id => $type)
$this->assertEquals($type, Message::typeIdToString($id));
}
public function testGetParametersFromStructure()
{
}
}

View file

@ -11,7 +11,6 @@
namespace Fetch\Test;
use Fetch\Server;
/**
* @package Fetch
@ -19,216 +18,5 @@ use Fetch\Server;
*/
class ServerTest extends \PHPUnit_Framework_TestCase
{
public static $num_messages_inbox = 12;
/**
* @dataProvider flagsDataProvider
* @param string $expected server string with %host% placeholder
* @param integer $port to use (needed to test behavior on port 143 and 993 from constructor)
* @param array $flags to set/unset ($flag => $value)
*/
public function testFlags($expected, $port, $flags)
{
$server = new Server(TESTING_SERVER_HOST, $port);
foreach ($flags as $flag => $value) {
$server->setFlag($flag, $value);
}
$this->assertEquals(str_replace('%host%', TESTING_SERVER_HOST, $expected), $server->getServerString());
}
public function testFlagOverwrite()
{
$server = static::getServer();
$server->setFlag('TestFlag', 'true');
$this->assertAttributeContains('TestFlag=true', 'flags', $server);
$server->setFlag('TestFlag', 'false');
$this->assertAttributeContains('TestFlag=false', 'flags', $server);
}
public function flagsDataProvider()
{
return array(
array('{%host%:143/novalidate-cert}', 143, array()),
array('{%host%:143/validate-cert}', 143, array('validate-cert' => true)),
array('{%host%:143}', 143, array('novalidate-cert' => false)),
array('{%host%:993/ssl}', 993, array()),
array('{%host%:993}', 993, array('ssl' => false)),
array('{%host%:100/tls}', 100, array('tls' => true)),
array('{%host%:100/tls}', 100, array('tls' => true, 'tls' => true)),
array('{%host%:100/notls}', 100, array('tls' => true, 'notls' => true)),
array('{%host%:100}', 100, array('ssl' => true, 'ssl' => false)),
array('{%host%:100/user=foo}', 100, array('user' => 'foo')),
array('{%host%:100/user=foo}', 100, array('user' => 'foo', 'user' => 'foo')),
array('{%host%:100/user=bar}', 100, array('user' => 'foo', 'user' => 'bar')),
array('{%host%:100}', 100, array('user' => 'foo', 'user' => false)),
);
}
/**
* @dataProvider connectionDataProvider
* @param integer $port to use (needed to test behavior on port 143 and 993 from constructor)
* @param array $flags to set/unset ($flag => $value)
* @param string $message Assertion message
*/
public function testConnection($port, $flags, $message)
{
$server = new Server(TESTING_SERVER_HOST, $port);
$server->setAuthentication(TEST_USER, TEST_PASSWORD);
foreach ($flags as $flag => $value) {
$server->setFlag($flag, $value);
}
$imapSteam = $server->getImapStream();
$this->assertInternalType('resource', $imapSteam, $message);
}
public function connectionDataProvider()
{
return array(
array(143, array(), 'Connects with default settings.'),
array(993, array('novalidate-cert' => true), 'Connects over SSL (self signed).'),
);
}
public function testNumMessages()
{
$server = static::getServer();
$numMessages = $server->numMessages();
$this->assertEquals(self::$num_messages_inbox, $numMessages);
$this->assertEquals(0, $server->numMessages( 'DOESNOTEXIST'.time() ) );
}
public function testGetMessages()
{
$server = static::getServer();
$messages = $server->getMessages(5);
$this->assertCount(5, $messages, 'Five messages returned');
foreach ($messages as $message) {
$this->assertInstanceOf('\Fetch\Message', $message, 'Returned values are Messages');
}
}
public function testGetMessagesOrderedByDateAsc()
{
$server = static::getServer();
$messages = $server->getOrderedMessages(SORTDATE, false, 2);
$this->assertCount(2, $messages, 'Two messages returned');
$this->assertGreaterThan($messages[0]->getDate(), $messages[1]->getDate(), 'Messages in ascending order');
}
public function testGetMessagesOrderedByDateDesc()
{
$server = static::getServer();
$messages = $server->getOrderedMessages(SORTDATE, true, 2);
$this->assertCount(2, $messages, 'Two messages returned');
$this->assertLessThan($messages[0]->getDate(), $messages[1]->getDate(), 'Messages in descending order');
}
public function testGetMailBox()
{
$server = static::getServer();
$this->assertEquals('', $server->getMailBox());
$this->assertTrue($server->setMailBox('Sent'));
$this->assertEquals('Sent', $server->getMailBox());
}
public function testSetMailBox()
{
$server = static::getServer();
$this->assertTrue($server->setMailBox('Sent'));
$this->assertEquals('Sent', $server->getMailBox());
$this->assertTrue($server->setMailBox('Flagged Email'));
$this->assertEquals('Flagged Email', $server->getMailBox());
$this->assertFalse($server->setMailBox('Cheese'));
$this->assertTrue($server->setMailBox(''));
$this->assertEquals('', $server->getMailBox());
}
public function testHasMailBox()
{
$server = static::getServer();
$this->assertTrue($server->hasMailBox('Sent'), 'Has mailbox "Sent"');
$this->assertTrue($server->hasMailBox('Flagged Email'), 'Has mailbox "Flagged Email"');
$this->assertFalse($server->hasMailBox('Cheese'), 'Does not have mailbox "Cheese"');
}
public function testListMailBoxes()
{
$server = static::getServer();
$spec = sprintf('{%s:143/novalidate-cert}', TESTING_SERVER_HOST);
$list = $server->listMailboxes('*');
$this->assertContains($spec.'Sent', $list, 'Has mailbox "Sent"');
$this->assertNotContains($spec.'Cheese', $list, 'Does not have mailbox "Cheese"');
}
public function testCreateMailbox()
{
$server = static::getServer();
$this->assertFalse($server->hasMailBox('Cheese'), 'Does not have mailbox "Cheese"');
$this->assertTrue($server->createMailBox('Cheese'), 'createMailbox returns true.');
$this->assertTrue($server->hasMailBox('Cheese'), 'Mailbox "Cheese" was created');
}
public function testDeleteMailbox()
{
$server = static::getServer();
$this->assertTrue($server->hasMailBox('Cheese'), 'Does have mailbox "Cheese"');
$this->assertTrue($server->deleteMailBox('Cheese'), 'deleteMailBox returns true.');
$this->assertFalse($server->hasMailBox('Cheese'), 'Mailbox "Cheese" was deleted');
}
/**
* @expectedException \RuntimeException
*/
public function testSetOptionsException()
{
$server = static::getServer();
$server->setOptions('purple');
}
public function testSetOptions()
{
$server = static::getServer();
$server->setOptions(5);
$this->assertAttributeEquals(5, 'options', $server);
}
public function testExpunge()
{
$server = static::getServer();
$message = $server->getMessageByUid(12);
$this->assertInstanceOf('\Fetch\Message', $message, 'Message exists');
$message->delete();
$this->assertInstanceOf('\Fetch\Message', $server->getMessageByUid(12), 'Message still present after being deleted but before being expunged.');
$server->expunge();
$this->assertFalse($server->getMessageByUid(12), 'Message successfully expunged');
}
public static function getServer()
{
$server = new Server(TESTING_SERVER_HOST, 143);
$server->setAuthentication(TEST_USER, TEST_PASSWORD);
return $server;
}
}

View file

@ -1,39 +0,0 @@
<?php
namespace Fetch\Test;
use Fetch\UTF8;
/**
* @package Fetch
* @author Robert Hafner <tedivm@tedivm.com>
* @author Sergey Linnik <linniksa@gmail.com>
*/
class UTF8Test extends \PHPUnit_Framework_TestCase
{
public function dataFix()
{
return array(
array(
'ab11 ? ?????.jpg',
"\x61\x62\x31\x31\x20\x97\x20\x3f\x3f\x3f\x3f\x3f\x2e\x6a\x70\x67",
),
array(
' ??????????????',
base64_decode('IKytrKastKyhrMCsu6yq'),
)
);
}
/**
* @dataProvider dataFix
*
* @param string $expected
* @param string $text
* @param string $charset
*/
public function testFix($expected, $text)
{
self::assertSame($expected, UTF8::fix($text));
}
}

View file

@ -9,32 +9,22 @@
* file that was distributed with this source code.
*/
define('TESTING', true);
error_reporting(-1);
define('TESTING', true);
define('TEST_USER', 'testuser');
define('TEST_PASSWORD', 'applesauce');
date_default_timezone_set('UTC');
if (getenv('TRAVIS')) {
define('TESTING_ENVIRONMENT', 'TRAVIS');
define('TESTING_SERVER_HOST', '127.0.0.1');
} else {
define('TESTING_ENVIRONMENT', 'VAGRANT');
define('TESTING_SERVER_HOST', '172.31.1.2');
}
$filename = __DIR__ .'/../vendor/autoload.php';
if (!file_exists($filename)) {
echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" . PHP_EOL;
echo " You need to execute `composer install` before running the tests. " . PHP_EOL;
echo " Vendors are required for complete test execution. " . PHP_EOL;
echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" . PHP_EOL . PHP_EOL;
$filename = __DIR__ .'/../autoload.php';
require_once $filename;
} else {
$loader = require $filename;
$loader->add('Fetch\\Test', __DIR__);
}
spl_autoload_register(function($class) {
if (0 === strpos($class, 'Fetch\\Test\\')) {
$file = __DIR__ . '/../tests/' . str_replace('\\', '/', $class) . '.php';
if (file_exists($file)) {
require_once $file;
return true;
}
} elseif (0 === strpos($class, 'Fetch\\')) {
$file = __DIR__ . '/../src/' . str_replace('\\', '/', $class) . '.php';
if (file_exists($file)) {
require_once $file;
return true;
}
}
});

View file

@ -1,17 +0,0 @@
#/usr/bin/env/sh
set -e
if [ ! -n "$TRAVIS" ]; then
./vendor/tedivm/dovecottesting/SetupEnvironment.sh
sleep 5
fi
echo 'Running unit tests.'
./vendor/bin/phpunit --verbose --coverage-clover build/logs/clover.xml
echo ''
echo ''
echo ''
echo 'Testing for Coding Styling Compliance.'
echo 'All code should follow PSR standards.'
./vendor/bin/php-cs-fixer fix ./ --level="all" -vv --dry-run