Skip to content

Commit fec86ca

Browse files
authored
Merge pull request #515 from bapcltd/issue/514
fixes for #496 & #514
2 parents b5e4cda + c7d447d commit fec86ca

File tree

8 files changed

+285
-22
lines changed

8 files changed

+285
-22
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ matrix:
3434

3535
cache:
3636
directories:
37-
- ./vendor
37+
- $HOME/.composer/cache
3838
- ./psalm/cache
3939

4040
before_script:

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
"psalm/plugin-phpunit": "^0.10.0",
4040
"roave/security-advisories": "dev-master",
4141
"sebastian/phpcpd": "^4.1",
42-
"vimeo/psalm": "^3.11"
42+
"vimeo/psalm": "^3.11.5"
4343
},
4444
"scripts": {
4545
"static-analysis": [

psalm.baseline.xml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<files psalm-version="3.11.4@58e1d8e68e5098bf4fbfdfb420c38d563f882549">
2+
<files psalm-version="3.11.5@3c60609c218d4d4b3b257728b8089094e5c6c6c2">
33
<file src="examples/get_and_parse_all_emails_without_saving_attachments.php">
44
<UnusedVariable occurrences="1">
55
<code>$mailbox</code>
@@ -13,9 +13,8 @@
1313
</DocblockTypeContradiction>
1414
</file>
1515
<file src="src/PhpImap/IncomingMail.php">
16-
<PossiblyUnusedMethod occurrences="2">
16+
<PossiblyUnusedMethod occurrences="1">
1717
<code>replaceInternalLinks</code>
18-
<code>embedImageAttachments</code>
1918
</PossiblyUnusedMethod>
2019
<PropertyTypeCoercion occurrences="1">
2120
<code>$this-&gt;dataInfo</code>
@@ -26,7 +25,7 @@
2625
<code>\in_array($imapSearchOption, $supported_options, true)</code>
2726
<code>\in_array($key, $supported_params, true)</code>
2827
</DocblockTypeContradiction>
29-
<InvalidArgument occurrences="6">
28+
<InvalidArgument occurrences="3">
3029
<code>$element-&gt;charset</code>
3130
<code>$element-&gt;charset</code>
3231
<code>$element-&gt;text</code>

src/PhpImap/IncomingMail.php

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,15 @@ public function __get(string $name): string
5757
if ('textHtml' == $name) {
5858
$type = DataPartInfo::TEXT_HTML;
5959
}
60+
if (('textPlain' === $name || 'textHtml' === $name) && isset($this->$name)) {
61+
return (string) $this->$name;
62+
}
6063
if (false === $type) {
6164
\trigger_error("Undefined property: IncomingMail::$name");
6265
}
63-
$this->$name = '';
66+
if (!isset($this->$name)) {
67+
$this->$name = '';
68+
}
6469
foreach ($this->dataInfo[$type] as $data) {
6570
$this->$name .= \trim($data->fetch());
6671
}
@@ -164,7 +169,9 @@ public function removeAttachment(string $id): bool
164169
*/
165170
public function getInternalLinksPlaceholders(): array
166171
{
167-
$match = \preg_match_all('/=["\'](ci?d:([\w\.%*@-]+))["\']/i', $this->textHtml, $matches);
172+
$fetchedHtml = (string) $this->__get('textHtml');
173+
174+
$match = \preg_match_all('/=["\'](ci?d:([\w\.%*@-]+))["\']/i', $fetchedHtml, $matches);
168175

169176
/** @psalm-var array{1:list<string>, 2:list<string>} */
170177
$matches = $matches;
@@ -200,23 +207,18 @@ public function replaceInternalLinks(string $baseUri): string
200207
*/
201208
public function embedImageAttachments(): void
202209
{
203-
/** @var string|null */
204-
$fetchedHtml = $this->textHtml;
210+
$fetchedHtml = (string) $this->__get('textHtml');
205211

206-
\preg_match_all("/\bcid:[^'\"\s]{1,256}/mi", $fetchedHtml ?? '', $matches);
212+
\preg_match_all("/\bcid:[^'\"\s]{1,256}/mi", $fetchedHtml, $matches);
207213

208-
/** @psalm-var list<list<string>> */
209-
$matches = $matches;
210-
211-
if (\count($matches)) {
214+
if (isset($matches[0]) && \is_array($matches[0]) && \count($matches[0])) {
215+
/** @var list<string> */
216+
$matches = $matches[0];
217+
$attachments = $this->getAttachments();
212218
foreach ($matches as $match) {
213-
if (!isset($match[0])) {
214-
continue;
215-
}
216-
217-
$cid = \str_replace('cid:', '', $match[0]);
219+
$cid = \str_replace('cid:', '', $match);
218220

219-
foreach ($this->getAttachments() as $attachment) {
221+
foreach ($attachments as $attachment) {
220222
if ($attachment->contentId == $cid && 'inline' == $attachment->disposition) {
221223
$contents = $attachment->getContents();
222224
$contentType = (string) $attachment->getFileInfo(FILEINFO_MIME);
@@ -230,7 +232,7 @@ public function embedImageAttachments(): void
230232
$base64encoded = \base64_encode($contents);
231233
$replacement = 'data:'.$contentType.';base64, '.$base64encoded;
232234

233-
$this->textHtml = \str_replace($match[0], $replacement, $this->textHtml);
235+
$this->textHtml = \str_replace($match, $replacement, $this->textHtml);
234236

235237
$this->removeAttachment($attachment->id);
236238
}

tests/unit/AbstractLiveMailboxTest.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@
3232
* charset?:string,
3333
* subtype?:string,
3434
* description?:string,
35+
* 'disposition.type'?:string,
36+
* 'type.parameters'?:array{name:string},
37+
* 'contents.data'?:string,
38+
* id?:string,
3539
* disposition?:array{filename:string}
3640
* }>
3741
*/

tests/unit/Fixtures/rgbkw5x1.png

74 Bytes
Loading

tests/unit/Fixtures/rgbkw5x1.webp

34 Bytes
Loading
Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
<?php
2+
/**
3+
* Live Mailbox - PHPUnit tests.
4+
*
5+
* Runs tests on a live mailbox
6+
*
7+
* @author BAPCLTD-Marv
8+
*/
9+
declare(strict_types=1);
10+
11+
namespace PhpImap;
12+
13+
use const ENCBASE64;
14+
use ParagonIE\HiddenString\HiddenString;
15+
use Throwable;
16+
use const TYPEIMAGE;
17+
use const TYPEMULTIPART;
18+
use const TYPETEXT;
19+
20+
/**
21+
* @psalm-import-type COMPOSE_ENVELOPE from AbstractLiveMailboxTest
22+
*/
23+
class LiveMailboxIssue514Test extends AbstractLiveMailboxTest
24+
{
25+
/**
26+
* @dataProvider MailBoxProvider
27+
*
28+
* @group live
29+
* @group issue-514
30+
*/
31+
public function testEmbed(
32+
HiddenString $imapPath,
33+
HiddenString $login,
34+
HiddenString $password,
35+
string $attachmentsDir,
36+
string $serverEncoding = 'UTF-8'
37+
): void {
38+
/** @var Throwable|null */
39+
$exception = null;
40+
41+
$mailboxDeleted = false;
42+
43+
/** @psalm-var COMPOSE_ENVELOPE */
44+
$envelope = [
45+
'subject' => 'barbushin/php-imap#514--'.\bin2hex(\random_bytes(16)),
46+
];
47+
48+
list($search_criteria) = $this->SubjectSearchCriteriaAndSubject($envelope);
49+
50+
$body = [
51+
[
52+
'type' => TYPEMULTIPART,
53+
],
54+
[
55+
'type' => TYPETEXT,
56+
'subtype' => 'plain',
57+
'contents.data' => 'foo',
58+
],
59+
[
60+
'type' => TYPETEXT,
61+
'subtype' => 'html',
62+
'contents.data' => \implode('', [
63+
'<img alt="png" width="5" height="1" src="cid:foo.png">',
64+
'<img alt="webp" width="5" height="1" src="cid:foo.webp">',
65+
]),
66+
],
67+
[
68+
'type' => TYPEIMAGE,
69+
'subtype' => 'png',
70+
'encoding' => ENCBASE64,
71+
'id' => 'foo.png',
72+
'description' => 'foo.png',
73+
'disposition' => ['filename' => 'foo.png'],
74+
'disposition.type' => 'inline',
75+
'type.parameters' => ['name' => 'foo.png'],
76+
'contents.data' => \base64_encode(
77+
\file_get_contents(__DIR__.'/Fixtures/rgbkw5x1.png')
78+
),
79+
],
80+
[
81+
'type' => TYPEIMAGE,
82+
'subtype' => 'webp',
83+
'encoding' => ENCBASE64,
84+
'id' => 'foo.webp',
85+
'description' => 'foo.webp',
86+
'disposition' => ['filename' => 'foo.webp'],
87+
'disposition.type' => 'inline',
88+
'type.parameters' => ['name' => 'foo.webp'],
89+
'contents.data' => \base64_encode(
90+
\file_get_contents(__DIR__.'/Fixtures/rgbkw5x1.webp')
91+
),
92+
],
93+
];
94+
95+
$message = Imap::mail_compose(
96+
$envelope,
97+
$body
98+
);
99+
100+
list($mailbox, $remove_mailbox, $path) = $this->getMailboxFromArgs([
101+
$imapPath,
102+
$login,
103+
$password,
104+
$attachmentsDir,
105+
$serverEncoding,
106+
]);
107+
108+
$result = null;
109+
110+
try {
111+
$search = $mailbox->searchMailbox($search_criteria);
112+
113+
$this->assertCount(
114+
0,
115+
$search,
116+
(
117+
'If a subject was found,'.
118+
' then the message is insufficiently unique to assert that'.
119+
' a newly-appended message was actually created.'
120+
)
121+
);
122+
123+
$mailbox->appendMessageToMailbox($message);
124+
125+
$search = $mailbox->searchMailbox($search_criteria);
126+
127+
$this->assertCount(
128+
1,
129+
$search,
130+
(
131+
'If a subject was not found, '.
132+
' then Mailbox::appendMessageToMailbox() failed'.
133+
' despite not throwing an exception.'
134+
)
135+
);
136+
137+
$result = $mailbox->getMail($search[0], false);
138+
139+
/** @var array<string, int> */
140+
$counts = [];
141+
142+
foreach ($result->getAttachments() as $attachment) {
143+
if (!isset($counts[(string) $attachment->contentId])) {
144+
$counts[(string) $attachment->contentId] = 0;
145+
}
146+
147+
++$counts[(string) $attachment->contentId];
148+
}
149+
150+
$this->assertCount(
151+
2,
152+
$counts,
153+
(
154+
'counts should only contain foo.png and foo.webp, found: '.
155+
\implode(
156+
', ',
157+
\array_keys($counts)
158+
)
159+
)
160+
);
161+
162+
foreach ($counts as $cid => $count) {
163+
$this->assertSame(
164+
1,
165+
$count,
166+
$cid.' had '.(string) $count.', expected 1.'
167+
);
168+
}
169+
170+
$this->assertSame(
171+
'foo',
172+
$result->textPlain,
173+
'plain text body did not match expected result!'
174+
);
175+
176+
$embedded = \implode('', [
177+
'<img alt="png" width="5" height="1" src="',
178+
'data:image/png; charset=binary;base64, ',
179+
$body[3]['contents.data'],
180+
'">',
181+
'<img alt="webp" width="5" height="1" src="',
182+
'data:image/webp; charset=binary;base64, ',
183+
$body[4]['contents.data'],
184+
'">',
185+
]);
186+
187+
$this->assertSame(
188+
[
189+
'foo.png' => 'cid:foo.png',
190+
'foo.webp' => 'cid:foo.webp',
191+
],
192+
$result->getInternalLinksPlaceholders(),
193+
'Internal link placeholders did not match expected result!'
194+
);
195+
196+
$replaced = \implode('', [
197+
'<img alt="png" width="5" height="1" src="',
198+
'foo.png',
199+
'">',
200+
'<img alt="webp" width="5" height="1" src="',
201+
'foo.webp',
202+
'">',
203+
]);
204+
205+
foreach ($result->getAttachments() as $attachment) {
206+
if ('foo.png' === $attachment->contentId) {
207+
$replaced = \str_replace(
208+
'foo.png',
209+
'/'.\basename($attachment->filePath),
210+
$replaced
211+
);
212+
} elseif ('foo.webp' === $attachment->contentId) {
213+
$replaced = \str_replace(
214+
'foo.webp',
215+
'/'.\basename($attachment->filePath),
216+
$replaced
217+
);
218+
}
219+
}
220+
221+
$this->assertSame(
222+
$replaced,
223+
$result->replaceInternalLinks(''),
224+
'replaced html body did not match expected result!'
225+
);
226+
227+
$this->assertSame(
228+
$body[2]['contents.data'],
229+
$result->textHtml,
230+
'unembeded html body did not match expected result!'
231+
);
232+
233+
$result->embedImageAttachments();
234+
235+
$this->assertSame(
236+
$embedded,
237+
$result->textHtml,
238+
'embeded html body did not match expected result!'
239+
);
240+
241+
$mailbox->deleteMail($search[0]);
242+
} catch (Throwable $ex) {
243+
$exception = $ex;
244+
} finally {
245+
$mailbox->switchMailbox($path->getString());
246+
247+
if (!$mailboxDeleted) {
248+
$mailbox->deleteMailbox($remove_mailbox);
249+
}
250+
251+
$mailbox->disconnect();
252+
}
253+
254+
if (null !== $exception) {
255+
throw $exception;
256+
}
257+
}
258+
}

0 commit comments

Comments
 (0)