Skip to content

Commit f7c17fa

Browse files
krulis-martindg
authored andcommitted
SmtpMailer: adds 'To: undisclosed-recipients' header when a mail is with only Bcc recipients (#92)
1 parent fe6ce27 commit f7c17fa

File tree

3 files changed

+118
-0
lines changed

3 files changed

+118
-0
lines changed

src/Mail/SmtpMailer.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ public function send(Message $mail): void
7373
{
7474
$tmp = clone $mail;
7575
$tmp->setHeader('Bcc', null);
76+
if (!$tmp->getHeader('To') && !$tmp->getHeader('Cc')) {
77+
// missing recipient headers make some mailers (e.g., sendmail) nervous -> set 'To' like many MTAs do
78+
$tmp->setHeader('To', 'undisclosed-recipients: ;');
79+
}
7680

7781
$data = $this->signer
7882
? $this->signer->generateSignedMessage($tmp)
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
/**
4+
* Test: Nette\Mail\SmtpMailer correctly handles Bcc-only message
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
use Nette\Mail\Message;
10+
use Tester\Assert;
11+
12+
13+
require __DIR__ . '/../bootstrap.php';
14+
require __DIR__ . '/SmtpMailerTestWrapper.php';
15+
16+
17+
$mail = new Message;
18+
$mail->setFrom('Tester <[email protected]>');
19+
$mail->addBcc('[email protected]');
20+
$mail->addBcc('[email protected]');
21+
22+
$mailer = new SmtpMailerTestWrapper;
23+
$mailer->send($mail);
24+
25+
// check the mail was sent to all Bcc mails
26+
[$from, $to1, $to2, $data, $body] = $mailer->getWrittenLines();
27+
Assert::equal('MAIL FROM:<[email protected]>', $from);
28+
Assert::equal('RCPT TO:<[email protected]>', $to1);
29+
Assert::equal('RCPT TO:<[email protected]>', $to2);
30+
Assert::equal('DATA', $data);
31+
32+
// make sure no Bcc is in the body and 'To; was set to 'undisclosed-recipients'
33+
$body = explode("\r\n", $body);
34+
$recipientHeaders = array_values(array_filter($body, fn($line) => preg_match('/^(To|Cc|Bcc):/i', $line)));
35+
Assert::count(1, $recipientHeaders);
36+
Assert::equal('To: undisclosed-recipients: ;', $recipientHeaders[0]);
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?php
2+
3+
/**
4+
* Common code for Mail test cases.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
use Nette\Mail\SmtpMailer;
10+
11+
/**
12+
* A wrapper for SmtpMailer (derived class) that helps with tesing.
13+
* It overrides internal connection functions so we can mock TCP communication.
14+
* Note: this is the first implementation -- it only collects the write operations.
15+
*/
16+
class SmtpMailerTestWrapper extends SmtpMailer
17+
{
18+
private $connected = false;
19+
private $written = [];
20+
21+
22+
public function __construct()
23+
{
24+
parent::__construct('localhost', '', '');
25+
}
26+
27+
28+
/**
29+
* Overrides connection to mock TCP interaction.
30+
*/
31+
protected function connect(): void
32+
{
33+
if ($this->connected) {
34+
throw new Exception('The connect() function called, but the connection was already established.');
35+
}
36+
$this->connected = true;
37+
}
38+
39+
40+
/**
41+
* Terminates mocking connection.
42+
*/
43+
protected function disconnect(): void
44+
{
45+
if (!$this->connected) {
46+
throw new Exception('The disconnect() function called, but no connection was currently established.');
47+
}
48+
$this->connected = false;
49+
}
50+
51+
52+
/**
53+
* Overrides writing function so we can collect, what was actually written by the sender.
54+
*/
55+
protected function write(string $line, int|array|null $expectedCode = null, ?string $message = null): void
56+
{
57+
$this->written[] = $line;
58+
}
59+
60+
61+
/**
62+
* Overrides reading function to mock inputs for the mailer.
63+
*/
64+
protected function read(): string
65+
{
66+
return ''; // not needed yet, may be implemented in the future
67+
}
68+
69+
70+
/**
71+
* Return lines collected in write calls.
72+
* @return string[]
73+
*/
74+
public function getWrittenLines(): array
75+
{
76+
return $this->written;
77+
}
78+
}

0 commit comments

Comments
 (0)