mirror of
https://github.com/retailcrm/Fetch.git
synced 2025-04-05 14:23:35 +03:00
Compare commits
244 commits
Author | SHA1 | Date | |
---|---|---|---|
|
8dcbff5d4c | ||
|
cd6af40196 | ||
|
521f7bcff7 | ||
|
bcaa835898 | ||
|
2f87688e3d | ||
|
370b3ed6f8 | ||
|
40025f2dea | ||
|
1c0e393b13 | ||
|
68cb173bc5 | ||
|
204aa76657 | ||
|
3374d13e3c | ||
|
c957e41478 | ||
|
f73071ac22 | ||
|
848792c24a | ||
|
229e2e8a72 | ||
|
a306ea53af | ||
|
9ccbd2a80b | ||
|
2e9989dbb3 | ||
|
dfd4cbeda0 | ||
|
29de2a7458 | ||
|
366c5dc189 | ||
|
82d18754bc | ||
|
e8c4ea7985 | ||
|
64ca07075d | ||
|
f2722da2b3 | ||
|
10954d66dd | ||
|
6cb6658526 | ||
|
3fbee968a6 | ||
|
a528129d37 | ||
|
ea3f1bbde6 | ||
|
f86c17e629 | ||
|
3448bffc17 | ||
|
a17ae8abbf | ||
|
55a2756d85 | ||
|
29bf5d835f | ||
|
2255634583 | ||
|
22ae7258c2 | ||
|
9bcd94b866 | ||
|
99f91b71b5 | ||
|
bafc9d5cd6 | ||
|
4495b9a7ca | ||
|
61ac0f64ca | ||
|
129e45793c | ||
|
db525aefff | ||
|
1ca08ba104 | ||
|
eb76a808d1 | ||
|
fe3c91d471 | ||
|
6baf7fe404 | ||
|
6a794a760f | ||
|
0a4f339fcb | ||
|
02af1096b2 | ||
|
774cfb2648 | ||
|
a3b85d5e32 | ||
|
92105be73d | ||
|
5317b9955f | ||
|
44a2080f3e | ||
|
743ceff0de | ||
|
6986ef830f | ||
|
5e11d722ad | ||
|
470cc114b0 | ||
|
d52ceb17ec | ||
|
0034aa69e9 | ||
|
234a771ea1 | ||
|
e6f3992329 | ||
|
d491510b27 | ||
|
030dade51b | ||
|
3a53e18910 | ||
|
35a756ee58 | ||
|
cce3bbcfc1 | ||
|
53b3aabca0 | ||
|
9c3fe885e8 | ||
|
c40d2ce48c | ||
|
397b748370 | ||
|
5f3a078ac5 | ||
|
1c526b989b | ||
|
ec9c2ff671 | ||
|
8d7a9c271b | ||
|
45b8a2d5e2 | ||
|
c8c61d017e | ||
|
ccae718aa1 | ||
|
6a6d909c17 | ||
|
f0b6c14632 | ||
|
545489ee10 | ||
|
6ad63d3b35 | ||
|
fd9784d14d | ||
|
0fb02cad2c | ||
|
b5b790ea75 | ||
|
a258847cac | ||
|
6f713cb97d | ||
|
413fc24ba1 | ||
|
4adad3f685 | ||
|
886a1fe82e | ||
|
ba247b861d | ||
|
ac1406f52c | ||
|
3c5402c0aa | ||
|
7b5fa936ff | ||
|
bc031ea316 | ||
|
154a84fdfa | ||
|
741a9eabc1 | ||
|
08ff0caa1c | ||
|
bcdc85d5d8 | ||
|
378bfbdbe0 | ||
|
6124cdef74 | ||
|
933cf095b8 | ||
|
cc8631581b | ||
|
928d37c25f | ||
|
56983f0365 | ||
|
4027b05fc6 | ||
|
37203c6697 | ||
|
989e77b758 | ||
|
501d5d0974 | ||
|
3e61c32056 | ||
|
d75a7e3f5c | ||
|
e8ae09423f | ||
|
40e6427cf1 | ||
|
c5ce7778ec | ||
|
04600acf8d | ||
|
c2f694303d | ||
|
dea2cfbe86 | ||
|
b406e04899 | ||
|
94f2a5fceb | ||
|
4de6c93e4d | ||
|
38dd385cb8 | ||
|
e8843a11f2 | ||
|
5e78459970 | ||
|
8d29ddeb73 | ||
|
b96c24a3e9 | ||
|
3e9400bfe0 | ||
|
6a581535e4 | ||
|
de108c0e86 | ||
|
13c7fe2d7d | ||
|
03720bdb55 | ||
|
b0c372ee6d | ||
|
5c717c9f21 | ||
|
af5161a232 | ||
|
766db0ad3a | ||
|
6377be4416 | ||
|
9334952ad2 | ||
|
8647977f38 | ||
|
fb57c942e2 | ||
|
aa5d96033f | ||
|
6f09bdc25f | ||
|
ca5a988e82 | ||
|
fa5ce2ddba | ||
|
c6c80ac45b | ||
|
db0ef08142 | ||
|
d4d7f367ba | ||
|
1790a917f4 | ||
|
90f8f7c53e | ||
|
9e49db2dd0 | ||
|
b6874c4a49 | ||
|
b8b426c045 | ||
|
05ad71a83f | ||
|
2fc04c3d56 | ||
|
f08630cde9 | ||
|
04b5a5ed33 | ||
|
8e742ef80e | ||
|
be90bb9971 | ||
|
7059e5159e | ||
|
5bcc4fd8e6 | ||
|
59e1e4169b | ||
|
9dc46cea9f | ||
|
bc5f325805 | ||
|
cab0aca17c | ||
|
52930d5d75 | ||
|
97597b1983 | ||
|
7b04684c99 | ||
|
70b982b319 | ||
|
4de41720dc | ||
|
259545731c | ||
|
9d26773f7f | ||
|
9ad874d69e | ||
|
33451f1e71 | ||
|
ec929fb20a | ||
|
b98ecaabaa | ||
|
767a9f0083 | ||
|
3c900abb37 | ||
|
1725eb2f8b | ||
|
faaff0711d | ||
|
b0d22fb76c | ||
|
8650586610 | ||
|
0bf95d78b2 | ||
|
b48ca178d7 | ||
|
7b42853eec | ||
|
88f43819ee | ||
|
ffeca296de | ||
|
56dc838df9 | ||
|
ef5f76d352 | ||
|
a2b41cd82c | ||
|
223be18517 | ||
|
30b3871e3d | ||
|
4432bd110a | ||
|
8b2a3c9647 | ||
|
4f2c2662e6 | ||
|
550f3b62e0 | ||
|
1ff3441020 | ||
|
c61e6f41f9 | ||
|
9cc77d1d14 | ||
|
cb15c2f408 | ||
|
acfc1c0890 | ||
|
7c9bc1d34f | ||
|
806572b869 | ||
|
25342a4318 | ||
|
0e38a10e97 | ||
|
f0a4e29e3d | ||
|
47bfb26b34 | ||
|
eda6cc65ab | ||
|
0bbc02b006 | ||
|
7af83a84db | ||
|
89efb38103 | ||
|
4a6811a08c | ||
|
fffa423429 | ||
|
59d5b8ffc8 | ||
|
f35a06935d | ||
|
68af0ad11f | ||
|
83a8a899be | ||
|
6ad9945817 | ||
|
10ec1b6a97 | ||
|
f7436eee00 | ||
|
9c6a14134b | ||
|
6d7f966a86 | ||
|
db1284d7e0 | ||
|
cfc04ab69b | ||
|
3749e70fa1 | ||
|
e8477204a3 | ||
|
aab81066cc | ||
|
b7b7d54966 | ||
|
662ae64b66 | ||
|
59dd786f2e | ||
|
2adbb68a54 | ||
|
623461a926 | ||
|
01f5ad02a8 | ||
|
554c22a363 | ||
|
bdfb20f610 | ||
|
6e6d66ed87 | ||
|
bcf57ce903 | ||
|
e6eb65fbd5 | ||
|
63ff9c9d3b | ||
|
5d30c7eaa1 | ||
|
13514054d9 | ||
|
63a358bcfa | ||
|
2a1c46e48d | ||
|
efa2bd9a51 | ||
|
ca5aaa6a36 |
21 changed files with 1472 additions and 207 deletions
3
.coveralls.yml
Normal file
3
.coveralls.yml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
src_dir: src
|
||||||
|
coverage_clover: build/logs/clover.xml
|
||||||
|
json_path: build/logs/coveralls-upload.json
|
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -1,7 +1,9 @@
|
||||||
|
.vagrant
|
||||||
/.idea
|
/.idea
|
||||||
/.settings
|
/.settings
|
||||||
/.buildpath
|
/.buildpath
|
||||||
/.project
|
/.project
|
||||||
/composer.lock
|
/composer.lock
|
||||||
/vendor
|
/vendor
|
||||||
/report
|
/report
|
||||||
|
/build
|
24
.travis.yml
Normal file
24
.travis.yml
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
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
|
54
CONTRIBUTING.md
Normal file
54
CONTRIBUTING.md
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
# 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
|
60
README.md
60
README.md
|
@ -1,34 +1,55 @@
|
||||||
Fetch
|
# Fetch [](https://travis-ci.org/tedious/Fetch)
|
||||||
=====
|
|
||||||
|
[](https://github.com/tedious/fetch/blob/master/LICENSE)
|
||||||
|
[](https://packagist.org/packages/tedivm/fetch)
|
||||||
|
[](https://coveralls.io/r/tedious/Fetch?branch=master)
|
||||||
|
[](https://packagist.org/packages/tedivm/fetch)
|
||||||
|
|
||||||
Fetch is a library for reading email and attachments, primarily using the POP
|
Fetch is a library for reading email and attachments, primarily using the POP
|
||||||
and IMAP protocols.
|
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.
|
|
||||||
|
|
||||||
Installation
|
## 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
|
||||||
|
```
|
||||||
|
|
||||||
The most easy way to install the library is via composer. To do so, you have to do
|
### Composer
|
||||||
the following:
|
|
||||||
|
|
||||||
php composer.phar require tedivm/fetch
|
Installing Fetch can be done through a variety of methods, although Composer is
|
||||||
|
recommended.
|
||||||
|
|
||||||
Composer will then ask you which version you want to install. Until there are stable
|
Until Fetch reaches a stable API with version 1.0 it is recommended that you
|
||||||
versions, by using "@dev" it'll install the latest version.
|
review changes before even Minor updates, although bug fixes will always be
|
||||||
|
backwards compatible.
|
||||||
|
|
||||||
Sample Usage
|
```
|
||||||
============
|
"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
|
||||||
|
|
||||||
This is just a simple code to show how to access messages by using Fetch. It uses Fetch
|
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
|
own autoload, but it can (and should be, if applicable) replaced with the one generated
|
||||||
by composer.
|
by composer.
|
||||||
|
|
||||||
require 'autoload.php';
|
|
||||||
|
|
||||||
$server = new \Fetch\Server('imap.example.com', 993);
|
$server = new \Fetch\Server('imap.example.com', 993);
|
||||||
$server->setAuthentication('dummy', 'dummy');
|
$server->setAuthentication('dummy', 'dummy');
|
||||||
|
@ -39,3 +60,8 @@ by composer.
|
||||||
foreach ($messages as $message) {
|
foreach ($messages as $message) {
|
||||||
echo "Subject: {$message->getSubject()}\nBody: {$message->getMessageBody()}\n";
|
echo "Subject: {$message->getSubject()}\nBody: {$message->getMessageBody()}\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Fetch is licensed under the BSD License. See the LICENSE file for details.
|
||||||
|
|
12
autoload.php
12
autoload.php
|
@ -9,11 +9,17 @@
|
||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
spl_autoload_register(function($class)
|
spl_autoload_register(function ($class) {
|
||||||
{
|
$base = '/src/';
|
||||||
$file = __DIR__.'/src/'.strtr($class, '\\', '/').'.php';
|
|
||||||
|
if (strpos($class, 'Fetch\Test') === 0) {
|
||||||
|
$base = '/tests/';
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = __DIR__.$base.strtr($class, '\\', '/').'.php';
|
||||||
if (file_exists($file)) {
|
if (file_exists($file)) {
|
||||||
require $file;
|
require $file;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"name": "tedivm/fetch",
|
"name": "tedivm/fetch",
|
||||||
"description": "A PHP IMAP Library",
|
"description": "A PHP IMAP Library",
|
||||||
"keywords": ["email","imap","pop3"],
|
"keywords": ["email","imap","pop3"],
|
||||||
"homepage": "http://github.com/tedivm/Fetch",
|
"homepage": "http://github.com/tedious/Fetch",
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"authors": [
|
"authors": [
|
||||||
|
@ -15,6 +15,11 @@
|
||||||
"php": ">=5.3.0",
|
"php": ">=5.3.0",
|
||||||
"ext-imap": "*"
|
"ext-imap": "*"
|
||||||
},
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"tedivm/dovecottesting": "1.2.3",
|
||||||
|
"phpunit/phpunit": "4.2.*",
|
||||||
|
"fabpot/php-cs-fixer": "0.5.*"
|
||||||
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-0": {"Fetch": "src/"}
|
"psr-0": {"Fetch": "src/"}
|
||||||
}
|
}
|
||||||
|
|
70
package.xml
70
package.xml
|
@ -1,70 +0,0 @@
|
||||||
<?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>
|
|
|
@ -11,10 +11,17 @@
|
||||||
syntaxCheck="false"
|
syntaxCheck="false"
|
||||||
bootstrap="tests/bootstrap.php"
|
bootstrap="tests/bootstrap.php"
|
||||||
>
|
>
|
||||||
|
|
||||||
<testsuites>
|
<testsuites>
|
||||||
<testsuite name="Fetch Test Suite">
|
<testsuite name="Fetch Test Suite">
|
||||||
<directory>./tests</directory>
|
<directory>./tests</directory>
|
||||||
</testsuite>
|
</testsuite>
|
||||||
</testsuites>
|
</testsuites>
|
||||||
|
<filter>
|
||||||
|
<whitelist>
|
||||||
|
<directory suffix=".php">./src/Fetch/</directory>
|
||||||
|
</whitelist>
|
||||||
|
</filter>
|
||||||
|
<logging>
|
||||||
|
<log type="coverage-text" target="php://stdout" showUncoveredFiles="false"/>
|
||||||
|
</logging>
|
||||||
</phpunit>
|
</phpunit>
|
||||||
|
|
|
@ -87,18 +87,56 @@ class Attachment
|
||||||
$this->imapStream = $message->getImapBox()->getImapStream();
|
$this->imapStream = $message->getImapBox()->getImapStream();
|
||||||
$this->structure = $structure;
|
$this->structure = $structure;
|
||||||
|
|
||||||
if (isset($partIdentifier))
|
if (isset($partIdentifier)) {
|
||||||
$this->partId = $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);
|
$parameters = Message::getParametersFromStructure($structure);
|
||||||
|
|
||||||
if (isset($parameters['filename'])) {
|
// quick fix for Content-Disposition extended notation
|
||||||
$this->filename = $parameters['filename'];
|
// name*0*=UTF-8''%D0%A...
|
||||||
} elseif (isset($parameters['name'])) {
|
// name*1*=%D0%B8...
|
||||||
$this->filename = $parameters['name'];
|
// etc
|
||||||
|
if (!empty($parameters['filename*'])) {
|
||||||
|
$this->setFileName($parameters['filename*']);
|
||||||
|
} elseif (!empty($parameters['name*'])) {
|
||||||
|
$this->setFileName($parameters['name*']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->size = $structure->bytes;
|
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->mimeType = Message::typeIdToString($structure->type);
|
$this->mimeType = Message::typeIdToString($structure->type);
|
||||||
|
|
||||||
|
@ -117,9 +155,18 @@ class Attachment
|
||||||
public function getData()
|
public function getData()
|
||||||
{
|
{
|
||||||
if (!isset($this->data)) {
|
if (!isset($this->data)) {
|
||||||
$messageBody = isset($this->partId) ?
|
if ($this->partId) {
|
||||||
imap_fetchbody($this->imapStream, $this->messageId, $this->partId, FT_UID)
|
$messageBody = imap_fetchbody($this->imapStream, $this->messageId, $this->partId, FT_UID);
|
||||||
: imap_body($this->imapStream, $this->messageId, 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 = Message::decode($messageBody, $this->encoding);
|
$messageBody = Message::decode($messageBody, $this->encoding);
|
||||||
$this->data = $messageBody;
|
$this->data = $messageBody;
|
||||||
|
@ -160,7 +207,7 @@ class Attachment
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function returns the object that contains the structure of this attachment.
|
* This function returns the object that contains the structure of this attachment.
|
||||||
*
|
*
|
||||||
* @return \stdClass
|
* @return \stdClass
|
||||||
*/
|
*/
|
||||||
public function getStructure()
|
public function getStructure()
|
||||||
|
@ -194,18 +241,46 @@ class Attachment
|
||||||
{
|
{
|
||||||
$dirname = dirname($path);
|
$dirname = dirname($path);
|
||||||
if (file_exists($path)) {
|
if (file_exists($path)) {
|
||||||
if (!is_writable($path))
|
if (!is_writable($path)) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
} elseif (!is_dir($dirname) || !is_writable($dirname)) {
|
} elseif (!is_dir($dirname) || !is_writable($dirname)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (($filePointer = fopen($path, 'w')) == false)
|
if (($filePointer = fopen($path, 'w')) == false) {
|
||||||
return 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);
|
fclose($filePointer);
|
||||||
|
|
||||||
return is_numeric($results);
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function setFileName($text)
|
||||||
|
{
|
||||||
|
$this->filename = MIME::decode($text, Message::$charset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
45
src/Fetch/MIME.php
Normal file
45
src/Fetch/MIME.php
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
<?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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -42,6 +42,13 @@ class Message
|
||||||
*/
|
*/
|
||||||
protected $imapStream;
|
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.
|
* This as an object which contains header information for the message.
|
||||||
*
|
*
|
||||||
|
@ -76,7 +83,7 @@ class Message
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected static $flagTypes = array('recent', 'flagged', 'answered', 'deleted', 'seen', 'draft');
|
protected static $flagTypes = array(self::FLAG_RECENT, self::FLAG_FLAGGED, self::FLAG_ANSWERED, self::FLAG_DELETED, self::FLAG_SEEN, self::FLAG_DRAFT);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This holds the plantext email message.
|
* This holds the plantext email message.
|
||||||
|
@ -120,6 +127,13 @@ class Message
|
||||||
*/
|
*/
|
||||||
protected $from;
|
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 contains information about the addresses the email was sent to.
|
||||||
*
|
*
|
||||||
|
@ -155,12 +169,53 @@ class Message
|
||||||
*/
|
*/
|
||||||
protected $attachments = array();
|
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.
|
* This value defines the encoding we want the email message to use.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
public static $charset = 'UTF-8//TRANSLIT';
|
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';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This constructor takes in the uid for the message and the Imap class representing the mailbox the
|
* This constructor takes in the uid for the message and the Imap class representing the mailbox the
|
||||||
|
@ -170,12 +225,14 @@ class Message
|
||||||
* @param int $messageUniqueId
|
* @param int $messageUniqueId
|
||||||
* @param Server $mailbox
|
* @param Server $mailbox
|
||||||
*/
|
*/
|
||||||
public function __construct($messageUniqueId, Server $mailbox)
|
public function __construct($messageUniqueId, Server $connection)
|
||||||
{
|
{
|
||||||
$this->imapConnection = $mailbox;
|
$this->imapConnection = $connection;
|
||||||
|
$this->mailbox = $connection->getMailBox();
|
||||||
$this->uid = $messageUniqueId;
|
$this->uid = $messageUniqueId;
|
||||||
$this->imapStream = $this->imapConnection->getImapStream();
|
$this->imapStream = $this->imapConnection->getImapStream();
|
||||||
$this->loadMessage();
|
if($this->loadMessage() !== true)
|
||||||
|
throw new \RuntimeException('Message with ID ' . $messageUniqueId . ' not found.');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -188,11 +245,19 @@ class Message
|
||||||
|
|
||||||
/* First load the message overview information */
|
/* First load the message overview information */
|
||||||
|
|
||||||
$messageOverview = $this->getOverview();
|
if(!is_object($messageOverview = $this->getOverview()))
|
||||||
|
|
||||||
$this->subject = $messageOverview->subject;
|
return false;
|
||||||
$this->date = strtotime($messageOverview->date);
|
|
||||||
$this->size = $messageOverview->size;
|
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;
|
||||||
|
|
||||||
foreach (self::$flagTypes as $flag)
|
foreach (self::$flagTypes as $flag)
|
||||||
$this->status[$flag] = ($messageOverview->$flag == 1);
|
$this->status[$flag] = ($messageOverview->$flag == 1);
|
||||||
|
@ -210,21 +275,33 @@ class Message
|
||||||
if (isset($headers->bcc))
|
if (isset($headers->bcc))
|
||||||
$this->bcc = $this->processAddressObject($headers->bcc);
|
$this->bcc = $this->processAddressObject($headers->bcc);
|
||||||
|
|
||||||
$this->from = $this->processAddressObject($headers->from);
|
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->replyTo = isset($headers->reply_to) ? $this->processAddressObject($headers->reply_to) : $this->from;
|
$this->replyTo = isset($headers->reply_to) ? $this->processAddressObject($headers->reply_to) : $this->from;
|
||||||
|
|
||||||
/* Finally load the structure itself */
|
/* Finally load the structure itself */
|
||||||
|
|
||||||
$structure = $this->getStructure();
|
$structure = $this->getStructure();
|
||||||
|
|
||||||
if (!isset($structure->parts)) {
|
if (isset($structure->parts)) {
|
||||||
// not multipart
|
|
||||||
$this->processStructure($structure);
|
|
||||||
} else {
|
|
||||||
// multipart
|
// multipart
|
||||||
foreach ($structure->parts as $id => $part)
|
foreach ($structure->parts as $id => $part)
|
||||||
$this->processStructure($part, $id + 1);
|
$this->processStructure($part, $id + 1);
|
||||||
|
} else {
|
||||||
|
// not multipart
|
||||||
|
$this->processStructure($structure);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -240,12 +317,34 @@ class Message
|
||||||
if ($forceReload || !isset($this->messageOverview)) {
|
if ($forceReload || !isset($this->messageOverview)) {
|
||||||
// returns an array, and since we just want one message we can grab the only result
|
// 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);
|
$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);
|
$this->messageOverview = array_shift($results);
|
||||||
|
if ( ! isset($this->messageOverview->date)) {
|
||||||
|
$this->messageOverview->date = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->messageOverview;
|
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
|
* 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
|
* and running them through the imap_rfc822_parse_headers function. The results are only retrieved from the server
|
||||||
|
@ -258,13 +357,18 @@ class Message
|
||||||
{
|
{
|
||||||
if ($forceReload || !isset($this->headers)) {
|
if ($forceReload || !isset($this->headers)) {
|
||||||
// raw headers (since imap_headerinfo doesn't use the unique id)
|
// raw headers (since imap_headerinfo doesn't use the unique id)
|
||||||
$rawHeaders = imap_fetchheader($this->imapStream, $this->uid, FT_UID);
|
$rawHeaders = $this->getRawHeaders();
|
||||||
|
|
||||||
// convert raw header string into a usable object
|
// convert raw header string into a usable object
|
||||||
$headerObject = imap_rfc822_parse_headers($rawHeaders);
|
$headerObject = imap_rfc822_parse_headers($rawHeaders);
|
||||||
|
|
||||||
// to keep this object as close as possible to the original header object we add the udate property
|
// to keep this object as close as possible to the original header object we add the udate property
|
||||||
$headerObject->udate = strtotime($headerObject->date);
|
if (isset($headerObject->date)) {
|
||||||
|
$headerObject->udate = strtotime($headerObject->date);
|
||||||
|
} else {
|
||||||
|
$headerObject->date = null;
|
||||||
|
$headerObject->udate = null;
|
||||||
|
}
|
||||||
|
|
||||||
$this->headers = $headerObject;
|
$this->headers = $headerObject;
|
||||||
}
|
}
|
||||||
|
@ -283,7 +387,7 @@ class Message
|
||||||
public function getStructure($forceReload = false)
|
public function getStructure($forceReload = false)
|
||||||
{
|
{
|
||||||
if ($forceReload || !isset($this->structure)) {
|
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;
|
return $this->structure;
|
||||||
|
@ -311,7 +415,7 @@ class Message
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!isset($this->plaintextMessage) && isset($this->htmlMessage)) {
|
if (!isset($this->plaintextMessage) && isset($this->htmlMessage)) {
|
||||||
$output = preg_replace('/\<br(\s*)?\/?\>/i', PHP_EOL, trim($this->htmlMessage) );
|
$output = preg_replace('/\s*\<br\s*\/?\>/i', PHP_EOL, trim($this->htmlMessage) );
|
||||||
$output = strip_tags($output);
|
$output = strip_tags($output);
|
||||||
|
|
||||||
return $output;
|
return $output;
|
||||||
|
@ -323,26 +427,45 @@ class Message
|
||||||
return false;
|
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
|
* This function returns either an array of email addresses and names or, optionally, a string that can be used in
|
||||||
* mail headers.
|
* mail headers.
|
||||||
*
|
*
|
||||||
* @param string $type Should be 'to', 'cc', 'bcc', 'from', or 'reply-to'.
|
* @param string $type Should be 'to', 'cc', 'bcc', 'from', 'sender', or 'reply-to'.
|
||||||
* @param bool $asString
|
* @param bool $asString
|
||||||
* @return array|string|bool
|
* @return array|string|bool
|
||||||
*/
|
*/
|
||||||
public function getAddresses($type, $asString = false)
|
public function getAddresses($type, $asString = false)
|
||||||
{
|
{
|
||||||
$type = ( $type == 'reply-to' ) ? 'replyTo' : $type;
|
$type = ( $type == 'reply-to' ) ? 'replyTo' : $type;
|
||||||
$addressTypes = array('to', 'cc', 'bcc', 'from', 'replyTo');
|
$addressTypes = array('to', 'cc', 'bcc', 'from', 'sender', 'replyTo');
|
||||||
|
|
||||||
if (!in_array($type, $addressTypes) || !isset($this->$type) || count($this->$type) < 1)
|
if (!in_array($type, $addressTypes) || !isset($this->$type) || count($this->$type) < 1)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
||||||
if (!$asString) {
|
if (!$asString) {
|
||||||
if ($type == 'from')
|
if ($type == 'from')
|
||||||
return $this->from[0];
|
return $this->from[0];
|
||||||
|
elseif ($type == 'sender')
|
||||||
|
return $this->sender[0];
|
||||||
|
|
||||||
return $this->$type;
|
return $this->$type;
|
||||||
} else {
|
} else {
|
||||||
|
@ -353,9 +476,12 @@ class Message
|
||||||
if (!isset($set))
|
if (!isset($set))
|
||||||
$set = true;
|
$set = true;
|
||||||
|
|
||||||
$outputString .= isset($address['name']) ?
|
$val = '<' . $address['address'] . '>';
|
||||||
$address['name'] . ' <' . $address['address'] . '>'
|
if (isset($address['name'])) {
|
||||||
: $address['address'];
|
$val = '"' . preg_replace('/\W/u', '\\\\$0', $address['name']) . '" ' . $val;
|
||||||
|
}
|
||||||
|
|
||||||
|
$outputString .= $val;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $outputString;
|
return $outputString;
|
||||||
|
@ -409,27 +535,46 @@ class Message
|
||||||
*
|
*
|
||||||
* @param \stdClass $structure
|
* @param \stdClass $structure
|
||||||
* @param string $partIdentifier
|
* @param string $partIdentifier
|
||||||
* @todoa process attachments.
|
|
||||||
*/
|
*/
|
||||||
protected function processStructure($structure, $partIdentifier = null)
|
protected function processStructure($structure, $partIdentifier = null)
|
||||||
{
|
{
|
||||||
|
if (!$structure) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$parameters = self::getParametersFromStructure($structure);
|
$parameters = self::getParametersFromStructure($structure);
|
||||||
|
|
||||||
if (isset($parameters['name']) || isset($parameters['filename'])) {
|
// quick fix for Content-Disposition extended notation
|
||||||
$attachment = new Attachment($this, $structure, $partIdentifier);
|
// name*0*=UTF-8''%D0%A...
|
||||||
$this->attachments[] = $attachment;
|
// name*1*=%D0%B8...
|
||||||
} elseif ($structure->type == 0 || $structure->type == 1) {
|
// 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);
|
||||||
|
$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) ?
|
$messageBody = isset($partIdentifier) ?
|
||||||
imap_fetchbody($this->imapStream, $this->uid, $partIdentifier, FT_UID)
|
imap_fetchbody($this->imapStream, $this->uid, $partIdentifier, FT_UID | FT_PEEK)
|
||||||
: imap_body($this->imapStream, $this->uid, FT_UID);
|
: imap_body($this->imapStream, $this->uid, FT_UID | FT_PEEK);
|
||||||
|
|
||||||
$messageBody = self::decode($messageBody, $structure->encoding);
|
$messageBody = self::decode($messageBody, $structure->encoding);
|
||||||
|
|
||||||
if (!empty($parameters['charset']) && $parameters['charset'] !== self::$charset)
|
if (!empty($parameters['charset'])) {
|
||||||
$messageBody = iconv($parameters['charset'], self::$charset, $messageBody);
|
$messageBody = self::charsetConvert($messageBody, $parameters['charset'], self::$charset) ?: $messageBody;
|
||||||
|
}
|
||||||
|
|
||||||
if (strtolower($structure->subtype) == 'plain' || $structure->type == 1) {
|
if (strtolower($structure->subtype) === 'plain' || ($structure->type == 1 && strtolower($structure->subtype) !== 'alternative')) {
|
||||||
if (isset($this->plaintextMessage)) {
|
if (isset($this->plaintextMessage)) {
|
||||||
$this->plaintextMessage .= PHP_EOL . PHP_EOL;
|
$this->plaintextMessage .= PHP_EOL . PHP_EOL;
|
||||||
} else {
|
} else {
|
||||||
|
@ -437,8 +582,7 @@ class Message
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->plaintextMessage .= trim($messageBody);
|
$this->plaintextMessage .= trim($messageBody);
|
||||||
} else {
|
} elseif (strtolower($structure->subtype) === 'html') {
|
||||||
|
|
||||||
if (isset($this->htmlMessage)) {
|
if (isset($this->htmlMessage)) {
|
||||||
$this->htmlMessage .= '<br><br>';
|
$this->htmlMessage .= '<br><br>';
|
||||||
} else {
|
} else {
|
||||||
|
@ -462,6 +606,67 @@ 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.
|
* This function takes in the message data and encoding type and returns the decoded data.
|
||||||
*
|
*
|
||||||
|
@ -471,16 +676,17 @@ class Message
|
||||||
*/
|
*/
|
||||||
public static function decode($data, $encoding)
|
public static function decode($data, $encoding)
|
||||||
{
|
{
|
||||||
if (!is_numeric($encoding))
|
if (!is_numeric($encoding)) {
|
||||||
$encoding = strtolower($encoding);
|
$encoding = strtolower($encoding);
|
||||||
|
}
|
||||||
|
|
||||||
switch ($encoding) {
|
switch (true) {
|
||||||
case 'quoted-printable':
|
case $encoding === 'quoted-printable':
|
||||||
case 4:
|
case $encoding === 4:
|
||||||
return quoted_printable_decode($data);
|
return quoted_printable_decode($data);
|
||||||
|
|
||||||
case 'base64':
|
case $encoding === 'base64':
|
||||||
case 3:
|
case $encoding === 3:
|
||||||
return base64_decode($data);
|
return base64_decode($data);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -556,11 +762,20 @@ class Message
|
||||||
$outputAddresses = array();
|
$outputAddresses = array();
|
||||||
if (is_array($addresses))
|
if (is_array($addresses))
|
||||||
foreach ($addresses as $address) {
|
foreach ($addresses as $address) {
|
||||||
$currentAddress = array();
|
if (property_exists($address, 'mailbox') && $address->mailbox != 'undisclosed-recipients') {
|
||||||
$currentAddress['address'] = $address->mailbox . '@' . $address->host;
|
$currentAddress = array();
|
||||||
if (isset($address->personal))
|
|
||||||
$currentAddress['name'] = $address->personal;
|
$host = '';
|
||||||
$outputAddresses[] = $currentAddress;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $outputAddresses;
|
return $outputAddresses;
|
||||||
|
@ -616,30 +831,43 @@ class Message
|
||||||
* @param string $flag Recent, Flagged, Answered, Deleted, Seen, Draft
|
* @param string $flag Recent, Flagged, Answered, Deleted, Seen, Draft
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function checkFlag($flag = 'flagged')
|
public function checkFlag($flag = self::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 a flag on the imap message.
|
* This function is used to enable or disable one or more flags on the imap message.
|
||||||
*
|
*
|
||||||
* @param string $flag Flagged, Answered, Deleted, Seen, Draft
|
* @param string|array $flag Flagged, Answered, Deleted, Seen, Draft
|
||||||
* @param bool $enable
|
* @param bool $enable
|
||||||
* @throws \InvalidArgumentException
|
* @throws \InvalidArgumentException
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function setFlag($flag, $enable = true)
|
public function setFlag($flag, $enable = true)
|
||||||
{
|
{
|
||||||
if (!in_array($flag, self::$flagTypes) || $flag == 'recent')
|
$flags = (is_array($flag)) ? $flag : array($flag);
|
||||||
throw new \InvalidArgumentException('Unable to set invalid flag "' . $flag . '"');
|
|
||||||
|
|
||||||
$flag = '\\' . ucfirst($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 . '"');
|
||||||
|
|
||||||
if ($enable) {
|
if ($enable) {
|
||||||
return imap_setflag_full($this->imapStream, $this->uid, $flag, ST_UID);
|
$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);
|
||||||
} else {
|
} else {
|
||||||
return imap_clearflag_full($this->imapStream, $this->uid, $flag, ST_UID);
|
return imap_clearflag_full($this->imapStream, $this->uid, $imapifiedFlag, ST_UID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -652,9 +880,16 @@ class Message
|
||||||
*/
|
*/
|
||||||
public function moveToMailBox($mailbox)
|
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);
|
$returnValue = imap_mail_copy($this->imapStream, $this->uid, $mailbox, CP_UID | CP_MOVE);
|
||||||
imap_expunge($this->imapStream);
|
imap_expunge($this->imapStream);
|
||||||
|
|
||||||
|
$this->mailbox = $mailbox;
|
||||||
|
|
||||||
|
$this->imapConnection->setMailBox($currentBox);
|
||||||
|
|
||||||
return $returnValue;
|
return $returnValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@ class Server
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $mailbox;
|
protected $mailbox = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the username used to connect to the server.
|
* This is the username used to connect to the server.
|
||||||
|
@ -93,11 +93,16 @@ class Server
|
||||||
*/
|
*/
|
||||||
protected $options = 0;
|
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
|
* This is the resource connection to the server. It is required by a number of imap based functions to specify how
|
||||||
* to connect.
|
* to connect.
|
||||||
*
|
|
||||||
* @var resource
|
|
||||||
*/
|
*/
|
||||||
protected $imapStream;
|
protected $imapStream;
|
||||||
|
|
||||||
|
@ -140,24 +145,35 @@ class Server
|
||||||
*
|
*
|
||||||
* @param string $username
|
* @param string $username
|
||||||
* @param string $password
|
* @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)
|
public function setAuthentication($username, $password, $tryFasterAuth=true)
|
||||||
{
|
{
|
||||||
$this->username = $username;
|
$this->username = $username;
|
||||||
$this->password = $password;
|
$this->password = $password;
|
||||||
|
if ($tryFasterAuth) {
|
||||||
|
$this->setParam('DISABLE_AUTHENTICATOR', array('GSSAPI','NTLM'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function sets the mailbox to connect to.
|
* This function sets the mailbox to connect to.
|
||||||
*
|
*
|
||||||
* @param string $mailbox
|
* @param string $mailbox
|
||||||
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function setMailBox($mailbox = '')
|
public function setMailBox($mailbox = '')
|
||||||
{
|
{
|
||||||
|
if (!$this->hasMailBox($mailbox)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
$this->mailbox = $mailbox;
|
$this->mailbox = $mailbox;
|
||||||
if (isset($this->imapStream)) {
|
if (isset($this->imapStream)) {
|
||||||
$this->setImapStream();
|
$this->setImapStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getMailBox()
|
public function getMailBox()
|
||||||
|
@ -213,19 +229,28 @@ class Server
|
||||||
public function setOptions($bitmask = 0)
|
public function setOptions($bitmask = 0)
|
||||||
{
|
{
|
||||||
if (!is_numeric($bitmask))
|
if (!is_numeric($bitmask))
|
||||||
throw new \Exception();
|
throw new \RuntimeException('Function requires numeric argument.');
|
||||||
|
|
||||||
$this->options = $bitmask;
|
$this->options = $bitmask;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function gets the current saved imap resource and returns it.
|
* This function is used to set connection parameters
|
||||||
*
|
*
|
||||||
* @return resource
|
* @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.
|
||||||
*/
|
*/
|
||||||
public function getImapStream()
|
public function getImapStream()
|
||||||
{
|
{
|
||||||
if (!isset($this->imapStream))
|
if (empty($this->imapStream))
|
||||||
$this->setImapStream();
|
$this->setImapStream();
|
||||||
|
|
||||||
return $this->imapStream;
|
return $this->imapStream;
|
||||||
|
@ -277,11 +302,11 @@ class Server
|
||||||
*/
|
*/
|
||||||
protected function setImapStream()
|
protected function setImapStream()
|
||||||
{
|
{
|
||||||
if (isset($this->imapStream)) {
|
if (!empty($this->imapStream)) {
|
||||||
if (!imap_reopen($this->imapStream, $this->getServerString(), $this->options, 1))
|
if (!imap_reopen($this->imapStream, $this->getServerString(), $this->options, 1))
|
||||||
throw new \RuntimeException(imap_last_error());
|
throw new \RuntimeException(imap_last_error());
|
||||||
} else {
|
} else {
|
||||||
$imapStream = imap_open($this->getServerString(), $this->username, $this->password, $this->options, 1);
|
$imapStream = @imap_open($this->getServerString(), $this->username, $this->password, $this->options, 1, $this->params);
|
||||||
|
|
||||||
if ($imapStream === false)
|
if ($imapStream === false)
|
||||||
throw new \RuntimeException(imap_last_error());
|
throw new \RuntimeException(imap_last_error());
|
||||||
|
@ -293,11 +318,22 @@ class Server
|
||||||
/**
|
/**
|
||||||
* This returns the number of messages that the current mailbox contains.
|
* This returns the number of messages that the current mailbox contains.
|
||||||
*
|
*
|
||||||
|
* @param string $mailbox
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
public function numMessages()
|
public function numMessages($mailbox='')
|
||||||
{
|
{
|
||||||
return imap_num_msg($this->getImapStream());
|
$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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -365,6 +401,40 @@ class Server
|
||||||
return $messages;
|
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.
|
* This function removes all of the messages flagged for deletion from the mailbox.
|
||||||
*
|
*
|
||||||
|
@ -384,7 +454,19 @@ class Server
|
||||||
*/
|
*/
|
||||||
public function hasMailBox($mailbox)
|
public function hasMailBox($mailbox)
|
||||||
{
|
{
|
||||||
return (boolean) imap_getmailboxes(
|
return (boolean) $this->getMailBoxDetails($mailbox);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return information about the mailbox or mailboxes
|
||||||
|
*
|
||||||
|
* @param $mailbox
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getMailBoxDetails($mailbox)
|
||||||
|
{
|
||||||
|
return imap_getmailboxes(
|
||||||
$this->getImapStream(),
|
$this->getImapStream(),
|
||||||
$this->getServerString(),
|
$this->getServerString(),
|
||||||
$this->getServerSpecification() . $mailbox
|
$this->getServerSpecification() . $mailbox
|
||||||
|
@ -402,4 +484,28 @@ class Server
|
||||||
{
|
{
|
||||||
return imap_createmailbox($this->getImapStream(), $this->getServerSpecification() . $mailbox);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
64
src/Fetch/UTF8.php
Normal file
64
src/Fetch/UTF8.php
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
<?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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,7 +11,6 @@
|
||||||
|
|
||||||
namespace Fetch\Test;
|
namespace Fetch\Test;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @package Fetch
|
* @package Fetch
|
||||||
* @author Robert Hafner <tedivm@tedivm.com>
|
* @author Robert Hafner <tedivm@tedivm.com>
|
||||||
|
@ -19,4 +18,101 @@ namespace Fetch\Test;
|
||||||
class AttachmentTest extends \PHPUnit_Framework_TestCase
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
65
tests/Fetch/Test/MIMETest.php
Normal file
65
tests/Fetch/Test/MIMETest.php
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
<?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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,7 +10,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Fetch\Test;
|
namespace Fetch\Test;
|
||||||
|
use Fetch\Message;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @package Fetch
|
* @package Fetch
|
||||||
|
@ -18,5 +18,284 @@ namespace Fetch\Test;
|
||||||
*/
|
*/
|
||||||
class MessageTest extends \PHPUnit_Framework_TestCase
|
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()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,25 +19,38 @@ use Fetch\Server;
|
||||||
*/
|
*/
|
||||||
class ServerTest extends \PHPUnit_Framework_TestCase
|
class ServerTest extends \PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
||||||
|
public static $num_messages_inbox = 12;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider flagsDataProvider
|
* @dataProvider flagsDataProvider
|
||||||
* @param string $expected server string with %host% placeholder
|
* @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 integer $port to use (needed to test behavior on port 143 and 993 from constructor)
|
||||||
* @param array $flags to set/unset ($flag => $value)
|
* @param array $flags to set/unset ($flag => $value)
|
||||||
*/
|
*/
|
||||||
public function testFlags($expected, $port, $flags)
|
public function testFlags($expected, $port, $flags)
|
||||||
{
|
{
|
||||||
$host = 'example.com';
|
$server = new Server(TESTING_SERVER_HOST, $port);
|
||||||
$server = new Server($host, $port);
|
|
||||||
|
|
||||||
foreach ($flags as $flag => $value) {
|
foreach ($flags as $flag => $value) {
|
||||||
$server->setFlag($flag, $value);
|
$server->setFlag($flag, $value);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->assertEquals(str_replace('%host%', $host, $expected), $server->getServerString());
|
$this->assertEquals(str_replace('%host%', TESTING_SERVER_HOST, $expected), $server->getServerString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function flagsDataProvider() {
|
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(
|
return array(
|
||||||
array('{%host%:143/novalidate-cert}', 143, array()),
|
array('{%host%:143/novalidate-cert}', 143, array()),
|
||||||
array('{%host%:143/validate-cert}', 143, array('validate-cert' => true)),
|
array('{%host%:143/validate-cert}', 143, array('validate-cert' => true)),
|
||||||
|
@ -54,4 +67,168 @@ class ServerTest extends \PHPUnit_Framework_TestCase
|
||||||
array('{%host%:100}', 100, array('user' => 'foo', 'user' => false)),
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
39
tests/Fetch/Test/UTF8Test.php
Normal file
39
tests/Fetch/Test/UTF8Test.php
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
<?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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,22 +9,32 @@
|
||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define('TESTING', true);
|
|
||||||
|
|
||||||
error_reporting(-1);
|
error_reporting(-1);
|
||||||
|
|
||||||
spl_autoload_register(function($class) {
|
define('TESTING', true);
|
||||||
if (0 === strpos($class, 'Fetch\\Test\\')) {
|
define('TEST_USER', 'testuser');
|
||||||
$file = __DIR__ . '/../tests/' . str_replace('\\', '/', $class) . '.php';
|
define('TEST_PASSWORD', 'applesauce');
|
||||||
if (file_exists($file)) {
|
|
||||||
require_once $file;
|
date_default_timezone_set('UTC');
|
||||||
return true;
|
|
||||||
}
|
if (getenv('TRAVIS')) {
|
||||||
} elseif (0 === strpos($class, 'Fetch\\')) {
|
define('TESTING_ENVIRONMENT', 'TRAVIS');
|
||||||
$file = __DIR__ . '/../src/' . str_replace('\\', '/', $class) . '.php';
|
define('TESTING_SERVER_HOST', '127.0.0.1');
|
||||||
if (file_exists($file)) {
|
} else {
|
||||||
require_once $file;
|
define('TESTING_ENVIRONMENT', 'VAGRANT');
|
||||||
return true;
|
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__);
|
||||||
|
}
|
||||||
|
|
17
tests/runTests.sh
Executable file
17
tests/runTests.sh
Executable file
|
@ -0,0 +1,17 @@
|
||||||
|
#/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
|
Loading…
Add table
Reference in a new issue