Compare commits

..

244 commits

Author SHA1 Message Date
Akolzin Dmitry
8dcbff5d4c
Merge pull request #9 from iyzoer/fixes-undefined-index
check date is null
2024-02-08 13:11:18 +03:00
Akolzin Dmitry
cd6af40196 check date is null 2024-02-08 13:09:20 +03:00
Akolzin Dmitry
521f7bcff7
Merge pull request #8 from iyzoer/fixes-undefined-index
fixes undefined property
2024-02-08 12:28:21 +03:00
Akolzin Dmitry
bcaa835898 fixes undefined property 2024-02-08 12:08:07 +03:00
Akolzin Dmitry
2f87688e3d
Merge pull request #7 from iyzoer/fixes-deprecations
add message property check, handling imap_fetchstructure errors
2024-02-07 16:25:46 +03:00
Akolzin Dmitry
370b3ed6f8 add message property check, handling imap_fetchstructure errors 2024-02-07 16:10:55 +03:00
Akolzin Dmitry
40025f2dea
Merge pull request #6 from iyzoer/warning-fixes
[fix] add subject property check
2024-02-06 09:40:10 +03:00
Akolzin Dmitry
1c0e393b13 [fix] add subject property check 2024-02-06 09:22:17 +03:00
Ichern
68cb173bc5
Merge pull request #4 from pm14kas/fix-structure-extended-notation
fixed attachment detection for rare case with multiline utf8 headers
2024-01-24 12:23:32 +03:00
Alexander Kozlov
204aa76657 fixed attachment detection for rare case with multiline utf8 headers 2024-01-24 11:09:35 +03:00
Ilyas Salikhov
3374d13e3c Remove doctype for Server:: to allow phpstan to detect right type on php 8.1+ 2024-01-19 14:06:42 +03:00
u_mulder
c957e41478 Add "from" header stub 2023-05-11 11:36:40 +03:00
u_mulder
f73071ac22 fix: Подавление warning при работе на php7 2023-03-07 12:34:10 +03:00
Alexey
848792c24a
Merge pull request #1 from u-mulder/mb-func-suppress-fix
Вместо подавления ошибки в mb_string-функциях ловим выпадающий эксепшен
2023-03-06 17:59:01 +03:00
u_mulder
229e2e8a72 fix: Вместо подавления ошибки в mb_string-функциях ловим выпадающий эксепшен 2023-03-06 17:48:54 +03:00
Alexey Chelnakov
a306ea53af fix Array and string offset access syntax with curly braces is no longer supported 2023-01-27 17:09:14 +03:00
Sergey Linnik
9ccbd2a80b fix for emails that contains another emails in rfc822 format 2020-04-09 10:28:44 +03:00
Ivan Lutokhin
2e9989dbb3 Add escape non-literal characters for name in address field 2019-03-28 17:33:29 +03:00
Sergey Linnik
dfd4cbeda0 add ability to support alternative charset names 2019-03-27 23:43:33 +03:00
Sergey Linnik
29de2a7458 fix converting to utf for some invalid byte sequences 2019-03-27 23:43:27 +03:00
Sergey Linnik
366c5dc189 Fix all non utf8 strings 2017-03-07 18:13:26 +03:00
Sergey Linnik
82d18754bc autofix utf8 strings 2017-01-30 19:59:18 +03:00
Sergey Linnik
e8c4ea7985 Merge branch 'patch-fix-empty-attachment-name' 2015-09-24 04:06:05 +03:00
Sergey Linnik
64ca07075d Merge branch 'patch-generalize-charset-convert' 2015-09-24 04:05:57 +03:00
Sergey Linnik
f2722da2b3 Generalize charset converting 2015-09-24 04:03:52 +03:00
Sergey Linnik
10954d66dd Fix empty attachment name in some cases 2015-09-16 17:31:43 +03:00
Sergey Linnik
6cb6658526 Check name and filename is empty 2015-08-25 23:38:12 +03:00
Robert Hafner
3fbee968a6 Merge pull request #147 from linniksa/patch-mime-decoder
Provide mime text decoder by RFC2047
2015-08-05 00:26:24 -07:00
Sergey Linnik
a528129d37 Provide mime text decoder by RFC2047
Replace imap_utf8, and add decode address name
2015-08-05 06:00:14 +03:00
Robert Hafner
ea3f1bbde6 Merge pull request #145 from tedious/supress_php_warning
Suppressed imap_open warning
2015-08-01 20:38:12 -07:00
Robert Hafner
f86c17e629 Suppressed imap_open warning (still throwing exception after testing return results) 2015-08-01 20:32:46 -07:00
Robert Hafner
3448bffc17 Corrected docblock formatting 2015-08-01 20:19:23 -07:00
Robert Hafner
a17ae8abbf Merge pull request #131 from Lewiscowles1986/patch-2
can now get number of messages in a sub-folder
2015-08-01 20:18:59 -07:00
Robert Hafner
55a2756d85 Merge pull request #133 from Lewiscowles1986/patch-3
speed up Auth on some servers
2015-08-01 20:16:22 -07:00
Robert Hafner
29bf5d835f Removed unused exception. 2015-08-01 20:14:17 -07:00
Robert Hafner
2255634583 Merge pull request #115 from dharyk/unsupported_encodings
Fix for unsupported encodings
2015-08-01 20:07:31 -07:00
Robert Hafner
22ae7258c2 Merge pull request #137 from tomsommer/patch-1
Fix case where imap_fetch_overview() fails and returns empty array
2015-07-09 14:18:46 -07:00
Robert Hafner
9bcd94b866 Merge pull request #139 from benr77/patch-1
Update docblock with correct return type
2015-07-09 14:17:57 -07:00
Robert Hafner
99f91b71b5 Merge pull request #136 from Prestaspirit/master
Add FT_PEEK in processStructure method
2015-07-09 14:17:33 -07:00
Ben Roberts
bafc9d5cd6 Update docblock with correct return type 2015-06-30 12:25:36 +01:00
Tom Sommer
4495b9a7ca Fix case where imap_fetch_overview() fails and returns empty array 2015-06-19 08:53:46 +02:00
Prestaspirit
61ac0f64ca Update Message.php
Hi,

I justa added FT_PEEK flag for the seen flag is not added automatically when calling the method processStructure while the message was not read
2015-06-11 22:01:59 +02:00
Robert Hafner
129e45793c Merge pull request #134 from orzilca/patch-1
Fixed issue #74
2015-06-06 23:10:48 -07:00
Robert Hafner
db525aefff Merge pull request #135 from tedious/delete_mailbox
Added delete mailbox method
2015-06-06 23:09:52 -07:00
Robert Hafner
1ca08ba104 Fixed PSR formatting 2015-06-06 22:48:30 -07:00
Robert Hafner
eb76a808d1 Merge pull request #129 from VasuLief/raw-headers
getRawHeaders
2015-06-06 22:46:20 -07:00
Robert Hafner
fe3c91d471 Merge pull request #128 from Lewiscowles1986/patch-1
Debian / Ubuntu installation NB added!
2015-06-06 22:44:56 -07:00
Robert Hafner
6baf7fe404 Merge pull request #125 from benr77/imap-separator
Added method to access mailbox properties - including IMAP separator character
2015-06-06 22:44:26 -07:00
Robert Hafner
6a794a760f Merge pull request #124 from benr77/undefined-variable-currentAddress
Fix for Undefined Variable: $currentAddress
2015-06-06 22:43:04 -07:00
Robert Hafner
0a4f339fcb Added delete mailbox method 2015-06-06 22:35:25 -07:00
Robert Hafner
02af1096b2 Merge pull request #121 from kevinsmith/feature/dedicated_body_getters
Add getPlainTextMessage and getHtmlMessage methods
2015-06-06 22:22:05 -07:00
Or Zilca
774cfb2648 Fixed issue #74
imap_savebody() causing server error
2015-05-26 12:24:14 +03:00
Lewis Cowles
a3b85d5e32 Update Server.php 2015-05-24 13:08:32 +01:00
Lewis Cowles
92105be73d PSR Madness
at least google led me to this for the cryptic messages... http://cs.sensiolabs.org/
2015-05-24 13:02:47 +01:00
Lewis Cowles
5317b9955f PSR getting on my nerves again 2015-05-24 12:53:50 +01:00
Lewis Cowles
44a2080f3e Update Server.php 2015-05-24 12:46:35 +01:00
Lewis Cowles
743ceff0de logic update (should be the last) 2015-05-24 12:41:39 +01:00
Lewis Cowles
6986ef830f Update MessageTest.php 2015-05-24 12:33:21 +01:00
Lewis Cowles
5e11d722ad logic update... 2015-05-24 12:27:42 +01:00
Lewis Cowles
470cc114b0 Logic modification 2015-05-24 12:19:25 +01:00
Lewis Cowles
d52ceb17ec Update MessageTest.php 2015-05-24 12:08:51 +01:00
Lewis Cowles
0034aa69e9 Update MessageTest.php 2015-05-24 12:05:01 +01:00
Lewis Cowles
234a771ea1 Update MessageTest.php 2015-05-24 11:56:59 +01:00
Lewis Cowles
e6f3992329 Update MessageTest.php 2015-05-24 11:53:23 +01:00
Lewis Cowles
d491510b27 Update MessageTest.php 2015-05-24 11:49:11 +01:00
Lewis Cowles
030dade51b More PSR BS! 2015-05-24 11:37:16 +01:00
Lewis Cowles
3a53e18910 PSR Nonsense strikes again... 2015-05-24 11:31:38 +01:00
Lewis Cowles
35a756ee58 I think this should fix the PSR check 2015-05-24 11:26:40 +01:00
Lewis Cowles
cce3bbcfc1 Auth speed boost 2015-05-24 11:11:32 +01:00
Lewis Cowles
53b3aabca0 updated test to encompass new numMessages syntax 2015-05-24 10:46:32 +01:00
Lewis Cowles
9c3fe885e8 Update ServerTest.php 2015-05-24 10:41:14 +01:00
Lewis Cowles
c40d2ce48c more accurate check with support for zero 2015-05-24 10:38:53 +01:00
Lewis Cowles
397b748370 can now get number of messages in a sub-folder 2015-05-23 23:10:58 +01:00
Dieter Funk
5f3a078ac5 psr fix 2015-05-12 15:47:32 +02:00
Ben Roberts
1c526b989b Fixed indentation 2015-05-12 14:30:03 +02:00
Ben Roberts
ec9c2ff671 Fixed indentation 2015-05-12 14:22:24 +02:00
Dieter Funk
8d7a9c271b use space instead of tabs as indentation 2015-05-12 13:53:59 +02:00
Dieter Funk
45b8a2d5e2 fixes 2015-05-12 13:50:00 +02:00
Dieter Funk
c8c61d017e added new function to fetch raw headers from mail 2015-05-07 09:51:13 +02:00
Lewis Cowles
ccae718aa1 Debian / Ubuntu installation NB added! 2015-05-06 13:57:13 +01:00
Robert Hafner
6a6d909c17 Merge pull request #126 from kevinsmith/hotfix/fix_reset_imapstream
Fix an issue where the IMAP stream isn't reloaded after it's lost.
2015-03-31 09:57:57 -07:00
Kevin Smith
f0b6c14632 Fix an issue where the IMAP stream isn't reloaded after it's lost.
If $this->imapStream is lost, it will return 0. These conditionals evalute 0 as a set variable, therefore they don't reload the stream even though they should.
2015-03-30 20:13:19 -05:00
Ben Roberts
545489ee10 Added method to access mailbox properties 2015-03-27 06:53:52 +01:00
Ben Roberts
6ad63d3b35 Ensure variable gets set correctly 2015-03-27 06:14:54 +01:00
Kevin Smith
fd9784d14d Update method names to refer specifically to body part of the message rather than the whole message itself. 2015-03-24 12:54:42 -05:00
Kevin Smith
0fb02cad2c Add getPlainTextMessage and getHtmlMessage methods. 2015-03-13 17:06:07 -05:00
Robert Hafner
b5b790ea75 Merge pull request #90 from guiguidu31300/master
UTF-8 Translation
2015-02-05 23:41:24 -08:00
Robert Hafner
a258847cac Fixed braces (psr goodness) 2015-02-05 23:39:31 -08:00
Robert Hafner
6f713cb97d Merge pull request #98 from gwarnants/master
Allow setFlags() to set multiple flags as an array + minor code reviews
2015-02-05 23:35:41 -08:00
Robert Hafner
413fc24ba1 Merge pull request #44 from dangerous/master
Allow the passing of connection parameters to imap_open()
2015-02-05 23:33:21 -08:00
Robert Hafner
4adad3f685 Merge pull request #96 from gpilla/master
Added check on Message::processAddressObject()
2015-02-05 23:29:37 -08:00
Miguel Guerreiro
886a1fe82e Fix for unsupported encodings
When using mb_convert_encoding, check identified charset against list of available encodings. If none match use US-ASCII if encoding is 7-bit; UTF-8 otherwise.

When using iconv, suppress errors and check if the function returns false
2015-01-27 10:04:47 +00:00
Robert Hafner
ba247b861d Merge pull request #111 from eleazan/patch-1
Update composer version
2015-01-08 09:25:15 -08:00
Andrés
ac1406f52c Update composer version
Add to readme the correct composer version
2015-01-08 10:57:19 +01:00
Robert Hafner
3c5402c0aa Merge pull request #110 from tedious/travis_updates
Updating travis-ci to test more versions and enable fast_finish
2015-01-07 19:50:36 -08:00
Robert Hafner
7b5fa936ff Fixed allowed_failures formatting 2015-01-07 19:41:48 -08:00
Robert Hafner
bc031ea316 Added fast_finish and more php versions 2015-01-07 19:39:27 -08:00
Robert Hafner
154a84fdfa Merge pull request #109 from tedious/release-0.6.1
Changed function names for cs consistency
2015-01-07 19:22:20 -08:00
Robert Hafner
741a9eabc1 Changed function names for cs consistency 2015-01-07 19:18:16 -08:00
Robert Hafner
08ff0caa1c Merge pull request #106 from luxifer/fix-message-decoding
Fix message decoding
2014-12-19 12:00:06 -05:00
Florent Viel
bcdc85d5d8 Revert "fix message decoding"
This reverts commit 378bfbdbe0.
2014-12-16 15:34:55 +01:00
Florent Viel
378bfbdbe0 fix message decoding 2014-12-16 15:04:08 +01:00
Florent Viel
6124cdef74 fix html fetch 2014-12-16 15:04:08 +01:00
Robert Hafner
933cf095b8 Merge pull request #63 from AaronVanGeffen/master
Making the sender address easily accessibly through Message::getAddresses
2014-12-01 21:21:22 -08:00
Aaron van Geffen
cc8631581b Make the sender address easily accessibly through Message::getAddresses('sender'). 2014-12-01 21:57:54 +01:00
Geoffray
928d37c25f minor regex optimization to convert br to eol 2014-11-01 09:35:31 +01:00
Geoffray
56983f0365 allow setFlags() to set multiple flags as an array 2014-11-01 09:11:38 +01:00
Geoffray
4027b05fc6 using class constants in stead of raw strings 2014-11-01 09:03:49 +01:00
Gustavo Pilla
37203c6697 Added check on Message::processAddressObject()
Added check if the mail addresses are really a email address on  Message::processAddressObject()

The error is when the address is for example: undisclosed-recipients
2014-10-22 19:30:19 -03:00
Guillaume RODRIGUEZ
989e77b758 UTF-8 Translation 2014-09-27 19:07:47 +02:00
Robert Hafner
501d5d0974 Merge pull request #88 from Xethron/catch-mb_convert_encoding-exceptions
Catch mb convert encoding exceptions
2014-09-24 23:44:51 -07:00
Bernhard Breytenbach
3e61c32056 Fix control_spaces PSR error 2014-09-25 08:09:52 +02:00
Robert Hafner
d75a7e3f5c Merge pull request #89 from tedious/dependency-update
Dependency Update
2014-09-24 18:54:51 -07:00
Robert Hafner
e8ae09423f Updated php-cs-fixer 2014-09-24 18:46:49 -07:00
Robert Hafner
40e6427cf1 Updated phpunit 2014-09-24 18:45:01 -07:00
Robert Hafner
c5ce7778ec Merge pull request #76 from Xethron/date-fix
Bugfix: Set overview/header date field to null if not set
2014-09-24 17:05:31 -07:00
Robert Hafner
04600acf8d Merge pull request #82 from Xethron/fix-attachment-encoding
Fix attachment encoding bug introduced in 6f09bdc2
2014-09-24 17:03:42 -07:00
Robert Hafner
c2f694303d Merge pull request #78 from Xethron/stream-filter-fix
Fix typo in stream filter
2014-09-24 17:01:48 -07:00
Robert Hafner
dea2cfbe86 Merge pull request #80 from vsychov/rfc822-messages
added attachment emails support
2014-09-24 16:59:49 -07:00
Robert Hafner
b406e04899 Merge pull request #79 from vsychov/master
fixed usage not-exists property
2014-09-24 16:57:55 -07:00
Robert Hafner
94f2a5fceb Merge pull request #84 from luxifer/imap-list
Add: listMailbox function
2014-09-24 16:56:53 -07:00
Robert Hafner
4de6c93e4d Merge pull request #73 from rejinka/master
Added a dependency
2014-09-24 16:54:28 -07:00
Bernhard Breytenbach
38dd385cb8 Catch exceptions from mb_convert_encoding and default to iconv 2014-09-17 21:56:59 +02:00
Florent Viel
e8843a11f2 cs fixer 2014-08-26 18:41:21 +02:00
Florent Viel
5e78459970 Fix: launch list test before create test 2014-08-26 18:25:15 +02:00
Florent Viel
8d29ddeb73 Add: listMailbox function 2014-08-26 17:59:05 +02:00
Bernhard Breytenbach
b96c24a3e9 Merge branch 'fix-attachment-encoding' 2014-08-25 14:30:00 +02:00
Bernhard Breytenbach
3e9400bfe0 Fix Attachment encoding problem when saving to disk
The case statement returned true for 0 == 'quoted-printable',
and base64 decoded all 7bit encoded attachments.
I have removed the string representations as an int is always
returned by PHP's imap functions for encoding type.
2014-08-25 14:29:36 +02:00
Bernhard Breytenbach
6a581535e4 Code cleanup for Attatchment::saveAs() 2014-08-25 14:15:35 +02:00
Viacheslav Sychov
de108c0e86 add attachment emails support 2014-08-15 17:55:19 +03:00
Viacheslav Sychov
13c7fe2d7d fixed usage not-exists property 2014-08-15 16:56:20 +03:00
Bernhard Breytenbach
03720bdb55 Merge pull request #2 from Xethron/stream-filter-fix
Fix typo in stream filter
2014-08-15 14:21:24 +02:00
Bernhard Breytenbach
b0c372ee6d Fix typo in stream filter 2014-08-15 14:16:34 +02:00
Bernhard Breytenbach
5c717c9f21 Merge pull request #1 from Xethron/date-fix
Bugfix: Set overview/header date field to null if not set
2014-08-12 12:29:45 +02:00
Bernhard Breytenbach
af5161a232 Bugfix: Set overview/header date field to null if not set 2014-08-05 14:52:29 +02:00
Robert Hafner
766db0ad3a Merge pull request #75 from Aeolun/master
Give Message class constants to access the imap flags
2014-07-28 09:41:13 -07:00
Bart Riepe
6377be4416 Give Message class constants to access the imap flags 2014-07-28 16:24:20 +09:00
Tony Lemke
9334952ad2 Added a dependency 2014-07-16 13:31:08 +02:00
Robert Hafner
8647977f38 Merge pull request #71 from skl/stream-save
Fix incorrect variable name
2014-06-29 14:00:18 -07:00
Stephen Lang
fb57c942e2 Fix incorrect variable name 2014-06-29 21:34:09 +01:00
Robert Hafner
aa5d96033f Merge pull request #70 from skl/stream-save
Implement memory-efficient streaming save
2014-06-29 10:56:10 -07:00
Stephen Lang
6f09bdc25f Implement memory-efficient streaming save 2014-06-29 18:00:39 +01:00
Robert Hafner
ca5a988e82 Merge pull request #66 from alexkavon/patch-2
Check to see if message is already encoded in utf8
2014-06-13 17:44:56 +02:00
Alex Kavon
fa5ce2ddba head conflicts fix 2014-06-13 09:23:30 -06:00
Alex
c6c80ac45b variable and message fix 2014-06-13 09:11:12 -06:00
Alex
db0ef08142 bug fixes, message changes 2014-06-13 09:08:09 -06:00
Alex Kavon
d4d7f367ba Updated message for $charsetFlag 2014-06-13 09:05:35 -06:00
Alex
1790a917f4 bug fixes 2014-06-13 09:00:22 -06:00
Alex Kavon
90f8f7c53e Merge pull request #2 from alexkavon/patch-2
Patch 2
2014-06-13 08:52:05 -06:00
Alex Kavon
9e49db2dd0 picky travis, added else statement 2014-06-12 19:12:31 -06:00
Alex Kavon
b6874c4a49 whitespace 2014-06-12 19:03:18 -06:00
Alex Kavon
b8b426c045 Second $charset should of been $charsetFlag 2014-06-12 18:58:03 -06:00
Alex Kavon
05ad71a83f Compromise between mb_convert_encoding and iconv 2014-06-12 18:53:14 -06:00
Alex Kavon
2fc04c3d56 Merge pull request #1 from alexkavon/patch-2
Patch 2
2014-06-12 18:49:40 -06:00
Alex Kavon
f08630cde9 Switch to mb_convert_encoding
Changing from iconv to mb_convert_encoding. $charset = 'UTF-8//TRANSLIT' is no longer needed. 'UTF-8' set as default and user may change value to whatever they wish :)
2014-06-12 13:51:27 -06:00
Alex Kavon
04b5a5ed33 Check to see if message is already encoded in utf8
Adding requirement to if statement to check if message matches UTF-8 encoding via regex. This will prevent encoding errors when converting UTF-8 to UTF-8.
2014-06-12 09:55:34 -06:00
Robert Hafner
8e742ef80e Merge pull request #60 from domibi/master
FIX: Message with no subject throw exception.
2014-06-02 09:41:35 +02:00
domibi
be90bb9971 FIX: Message with no subject throw exception.
The subject of a message overview could not be set. Now we check if the property exists, if not we set subject to null.
2014-05-31 20:44:30 +02:00
Robert Hafner
7059e5159e Updated links 2014-05-21 01:16:33 -07:00
Robert Hafner
5bcc4fd8e6 Updated homepage 2014-05-21 00:54:40 -07:00
Robert Hafner
59e1e4169b Updated dovecottesting to 1.2.3 2014-05-19 21:52:17 -07:00
Robert Hafner
9dc46cea9f Merge pull request #51 from nulpunkt/get-orderd
Implemented getting messages in an ordered fashion
2014-05-19 14:28:57 -07:00
Robert Hafner
bc5f325805 Merge pull request #56 from iampersistent/message-decode
Message decode
2014-05-07 14:55:41 -07:00
Richard Shank
cab0aca17c fix the behavior in Message:decode 2014-05-07 14:26:59 -07:00
Richard Shank
52930d5d75 update Message::decode test 2014-05-07 14:26:22 -07:00
Robert Hafner
97597b1983 updated "total downloads" badge 2014-05-04 23:15:10 -07:00
Jesper Skovgaard Nielsen
7b04684c99 Implemented getting messages in an ordered fashion 2014-04-28 09:34:21 +02:00
Robert Hafner
70b982b319 Badges update 2014-04-20 00:32:28 -07:00
Robert Hafner
4de41720dc Merge pull request #49 from tedivm/more_testing
Use Composer provided phpunit for tests
2014-04-19 23:54:20 -07:00
Robert Hafner
259545731c Added contributing file 2014-04-19 23:42:32 -07:00
Robert Hafner
9d26773f7f use bin/php-cs-fixer instead of bundle version 2014-04-19 23:42:19 -07:00
Robert Hafner
9ad874d69e Use Composer provided phpunit for tests
This makes versioning pinning easier
2014-04-19 22:01:22 -07:00
Robert Hafner
33451f1e71 Removed Pear package file
I'm to lazy to maintain pear when composer is a thing.
2014-04-16 01:33:50 -07:00
Robert Hafner
ec929fb20a Merge pull request #47 from tedivm/testing_updates
Testing Suite Updates
2014-04-16 00:58:51 -07:00
Robert Hafner
b98ecaabaa Start testing hhvm on travis-ci
Lets see if it even has imap enabled :-)
2014-04-16 00:49:16 -07:00
Robert Hafner
767a9f0083 Updated to latest version of the DovecotTesting Package 2014-04-16 00:45:44 -07:00
Robert Hafner
3c900abb37 Added cover coverage badge 2014-04-16 00:36:52 -07:00
Robert Hafner
1725eb2f8b Formatting! PSR Compliance 2014-04-16 00:35:51 -07:00
Robert Hafner
faaff0711d Added code coverage and styling tests, refactored
Added runTests.sh to wrap up a bunch of test starting functionality.
Moved SetupEnvironment.sh out of Bootstrap.php and into the runTests.sh
file.

Added Coverall support for line coverage.

Added php-cs-fixer for code styling testing.
2014-04-16 00:28:38 -07:00
David Rainsford
b0d22fb76c Allow the passing of connection parameters to imap_open()
Since version 5.3.2 of PHP, imap_open() has an optional 6th
parameter which allows you to set certain connection parameters.

Currently the only key is:

- DISABLE_AUTHENTICATOR - Disable authentication properties

see https://bugs.php.net/bug.php?id=33500

Example of use:

  $server = new Server('imap.example.com', 993);
  $server->setParam('DISABLE_AUTHENTICATOR', 'GSSAPI');

This gets rid of the following errors:
<br />
<b>Notice</b>:  Unknown: Unknown GSSAPI failure: An invalid name was
supplied (errflg=1) in <b>Unknown</b> on line <b>0</b><br />
<br />
<b>Notice</b>:  Unknown: GSSAPI mechanism status: Hostname cannot be
canonicalized (errflg=1) in <b>Unknown</b> on line <b>0</b><br />
2014-04-03 10:59:19 +11:00
Robert Hafner
8650586610 Locked in the tedivm/dovecottesting version 2014-03-27 23:31:21 -07:00
Robert Hafner
0bf95d78b2 Merge pull request #40 from bjornpost/fix-multipart-messagebody
In a multipart email messageBody() keeps headers
2014-03-14 00:08:36 -07:00
Robert Hafner
b48ca178d7 Merge pull request #41 from tedivm/testing_dovecot
Dovecot Testing Package Updates
2014-03-14 00:05:34 -07:00
Bjorn Post
7b42853eec Rewrite fix as it broke other tests. This fixes my issue and the broken tests. 2014-03-11 22:16:43 +01:00
Bjorn Post
88f43819ee Be more specific when fetching body sections, references #39 2014-03-11 20:17:02 +01:00
Robert Hafner
ffeca296de Added back distinct IP address for Travis and Vagrant 2014-02-27 11:47:17 -08:00
Robert Hafner
56dc838df9 Updated to dovecottesting 1.2
This lets us simplify our test script by not having to care about
different ip addresses.
2014-02-27 00:12:28 -08:00
Robert Hafner
ef5f76d352 Added timezone and corrected path data
Adding a timezone prevents errors on misconfigured systems when
running, and the path correction automates the running of the dovecot
testing package between tests.
2014-02-27 00:11:19 -08:00
Robert Hafner
a2b41cd82c Added test to prevent fatal error in certain error conditions 2014-02-27 00:09:56 -08:00
Robert Hafner
223be18517 Telling composer to use any of the 1.x line of the Dovecot Test Package 2014-01-20 15:37:47 -08:00
Robert Hafner
30b3871e3d Switching back to dev-master for testing 2014-01-20 15:28:21 -08:00
Robert Hafner
4432bd110a Merge pull request #33 from tedivm/testing_update
Separate Mail Server Setup into Development Package
2014-01-20 15:04:10 -08:00
Robert Hafner
8b2a3c9647 Pinned Fetch to a specific stable version of the test suite. 2014-01-20 14:59:01 -08:00
Robert Hafner
4f2c2662e6 Updated vendor path used by travis for the SetupEnvironment script 2014-01-19 00:54:07 -08:00
Robert Hafner
550f3b62e0 Updated Fetch to use the DovecotTesting repository
Rather than ship with all of these files I’ve separated out the server
provisioning code from this repository.
2014-01-19 00:51:55 -08:00
Robert Hafner
1ff3441020 Update README.md 2014-01-14 00:32:45 -08:00
Robert Hafner
c61e6f41f9 Removed autoloader reference 2013-12-18 17:02:34 -08:00
Robert Hafner
9cc77d1d14 Merge pull request #31 from tedivm/Marketing
Updated readme
2013-12-18 16:59:50 -08:00
Robert Hafner
cb15c2f408 Updated readme 2013-12-18 16:59:03 -08:00
Robert Hafner
acfc1c0890 Merge pull request #30 from tedivm/testing
Complete Rework of the Test Suite
2013-12-18 16:22:01 -08:00
Robert Hafner
7c9bc1d34f Lots more tests! 2013-12-18 16:16:33 -08:00
Robert Hafner
806572b869 Changed exception type 2013-12-18 16:16:20 -08:00
Robert Hafner
25342a4318 Rewrote "moveToMailbox" code to deal with some potential bugs
Basically if the server isn't in the same box as the message it causes
issues, so this switches to that box then back to where the server was.
2013-12-18 16:16:12 -08:00
Robert Hafner
0e38a10e97 Added comments 2013-12-18 14:32:32 -08:00
Robert Hafner
f0a4e29e3d Increased Dovecot's allowed connections for an individual IP address 2013-12-18 14:21:55 -08:00
Robert Hafner
47bfb26b34 Added tests for Expunge, getMailbox and setMailBox methods. 2013-12-18 14:21:33 -08:00
Robert Hafner
eda6cc65ab Improved error handling in Server class, added getMessageByUid method 2013-12-18 14:20:24 -08:00
Robert Hafner
0bbc02b006 Updated Message class to throw exception when passed nonexistant email ID 2013-12-18 14:19:39 -08:00
Robert Hafner
7af83a84db Added Message::getMessageBody tests, added better messages to existing assertions. 2013-12-11 23:57:25 -08:00
Robert Hafner
89efb38103 Removed unneeded cert
The current build script regenerates this each time, so it's no longer
needed.
2013-12-11 19:27:43 -08:00
Robert Hafner
4a6811a08c Added test suite for the Attachment class 2013-12-09 22:53:43 -08:00
Robert Hafner
fffa423429 Added additional Attachment test 2013-12-08 23:12:08 -08:00
Robert Hafner
59d5b8ffc8 Continued extended Message test suite 2013-12-08 22:48:50 -08:00
Robert Hafner
f35a06935d Killed first bug- Message class 'checkFlag' was returning wrong values if the message had been changed with setFlag
This was because the setFlag function was not setting the value stored
in the message object, just the one on the server.
2013-12-08 22:48:25 -08:00
Robert Hafner
68af0ad11f Added more tests for the Message class 2013-12-08 21:59:41 -08:00
Robert Hafner
83a8a899be Added Message test structure, as well as a few initial tests. 2013-12-08 18:31:27 -08:00
Robert Hafner
6ad9945817 Corrected mailbox name in test, as well as number of messages in main inbox 2013-12-08 17:17:07 -08:00
Robert Hafner
10ec1b6a97 Updated test messages
During testing I realized that the transfer of messages to the test
server did not actually occur in the right order, which made some test
results counter intuitive. I've reshuffled the messages around so
things will make more sense.
2013-12-08 17:16:44 -08:00
Robert Hafner
f7436eee00 Made the getServer function static 2013-12-05 23:42:50 -08:00
Robert Hafner
9c6a14134b Removed unfinished test 2013-12-05 23:04:25 -08:00
Robert Hafner
6d7f966a86 Continued fleshing out the Server tests 2013-12-05 23:01:53 -08:00
Robert Hafner
db1284d7e0 Updated code coverage to only include Fetch code 2013-12-05 18:01:02 -08:00
Robert Hafner
cfc04ab69b Another attempt to make travis ci like me. 2013-12-05 17:11:19 -08:00
Robert Hafner
3749e70fa1 Added new connection tests to the ServerTest class 2013-12-05 17:11:07 -08:00
Robert Hafner
e8477204a3 Making another attempt to deal with the ssl issue 2013-12-05 15:29:04 -08:00
Robert Hafner
aab81066cc Stopping/Starting Dovecot before moving ssl cert 2013-12-05 14:45:56 -08:00
Robert Hafner
b7b7d54966 Attempting to deal with Dovecot/Travis issue by supplying an ssl cert 2013-12-05 14:39:02 -08:00
Robert Hafner
662ae64b66 Attempting to see if updating apt-get will resolve build error 2013-12-05 14:24:09 -08:00
Robert Hafner
59dd786f2e Tell travis to run commands as root. 2013-12-05 14:08:32 -08:00
Robert Hafner
2adbb68a54 Updated SetupEnvironment to use TRAVIS_BUILD_DIR to identify paths 2013-12-05 14:06:51 -08:00
Robert Hafner
623461a926 Added travis.ci file 2013-12-05 14:01:18 -08:00
Robert Hafner
01f5ad02a8 Created initial working test suite
So far it just tests some flags (which it already did) and then tests
connecting to the imap server itself.

The Attachment and Message tests are just stubs right now and don't do
anything, but need those stubs to let the tests occur.
2013-12-05 13:57:34 -08:00
Robert Hafner
554c22a363 Added some useful output to the phpunit bootstrap file 2013-12-05 13:50:36 -08:00
Robert Hafner
bdfb20f610 Updated the phpunit bootstrapper to initialize the environment. 2013-12-05 12:13:25 -08:00
Robert Hafner
6e6d66ed87 Created environment setup wrapper script
This script makes setting up the environment for testing easier. It
detects that it's on travis and creates the appropriate setup right
there, or on personal machines uses vagrant. If vagrant is running is
skips the full setup and simply resets the mailbox for fast testing.
2013-12-04 23:36:48 -08:00
Robert Hafner
bcf57ce903 Updated autoloader to support test cases, and bootstrapper to support composer autoloader 2013-12-04 23:34:08 -08:00
Robert Hafner
e6eb65fbd5 Shuffled testing scripts out of Vagrant folder and into their own space. 2013-12-04 21:57:51 -08:00
Robert Hafner
63ff9c9d3b Cleaned up VagrantFile formatting. 2013-12-01 23:06:31 -08:00
Robert Hafner
5d30c7eaa1 Added ResetMail.sh to Provision.sh script, cleaned formatting. 2013-12-01 23:06:18 -08:00
Robert Hafner
13514054d9 Updated system hostname to tedivm.com 2013-12-01 23:05:55 -08:00
Robert Hafner
63a358bcfa Added "ResetMail.sh" to handle refreshing the test inboxes before and between tests 2013-12-01 23:04:55 -08:00
Robert Hafner
2a1c46e48d Added test messages in
These messages are a dump of a "Maildir" account, managed by Dovecot.
2013-12-01 22:58:00 -08:00
Robert Hafner
efa2bd9a51 Added postfix config file to Vagrant to help populate mailboxes 2013-12-01 17:48:46 -08:00
Robert Hafner
ca5aaa6a36 Initial Vagrant setup 2013-12-01 15:53:05 -08:00
21 changed files with 1472 additions and 207 deletions

3
.coveralls.yml Normal file
View file

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

4
.gitignore vendored
View file

@ -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
View 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
View 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

View file

@ -1,34 +1,55 @@
Fetch # Fetch [![Build Status](https://travis-ci.org/tedious/Fetch.svg?branch=master)](https://travis-ci.org/tedious/Fetch)
=====
[![License](http://img.shields.io/packagist/l/tedivm/fetch.svg)](https://github.com/tedious/fetch/blob/master/LICENSE)
[![Latest Stable Version](http://img.shields.io/github/release/tedious/fetch.svg)](https://packagist.org/packages/tedivm/fetch)
[![Coverage Status](http://img.shields.io/coveralls/tedious/Fetch.svg)](https://coveralls.io/r/tedious/Fetch?branch=master)
[![Total Downloads](http://img.shields.io/packagist/dt/tedivm/fetch.svg)](https://packagist.org/packages/tedivm/fetch)
Fetch 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.

View file

@ -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;
} }
}); });

View file

@ -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/"}
} }

View file

@ -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>

View file

@ -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>

View file

@ -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
View 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;
}
}

View file

@ -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;
} }
} }

View file

@ -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
View 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;
}
}

View file

@ -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);
}
} }

View 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));
}
}

View file

@ -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()
{
}
} }

View file

@ -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;
}
} }

View 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));
}
}

View file

@ -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
View 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