Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/Mail/SmtpMailer.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ public function send(Message $mail): void
{
$tmp = clone $mail;
$tmp->setHeader('Bcc', null);
if (!$tmp->getHeader('To') && !$tmp->getHeader('Cc')) {
// missing recipient headers make some mailers (e.g., sendmail) nervous -> set 'To' like many MTAs do
$tmp->setHeader('To', 'undisclosed-recipients: ;');
}

$data = $this->signer
? $this->signer->generateSignedMessage($tmp)
Expand Down
38 changes: 38 additions & 0 deletions tests/Mail/Mail.SmtpMailer.bcc.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

/**
* Test: Nette\Mail\SmtpMailer correctly handles Bcc-only message
*/

declare(strict_types=1);

use Nette\Mail\Message;
use Tester\Assert;


require __DIR__ . '/../bootstrap.php';
require __DIR__ . '/SmtpMailerTestWrapper.php';


$mail = new Message;
$mail->setFrom('Tester <tester@example.com>');
$mail->addBcc('hidden1@example.com');
$mail->addBcc('hidden2@example.com');

$mailer = new SmtpMailerTestWrapper;
$mailer->send($mail);

// check the mail was sent to all Bcc mails
list($from, $to1, $to2, $data, $body) = $mailer->getWrittenLines();
Assert::equal("MAIL FROM:<tester@example.com>", $from);
Assert::equal("RCPT TO:<hidden1@example.com>", $to1);
Assert::equal("RCPT TO:<hidden2@example.com>", $to2);
Assert::equal("DATA", $data);

// make sure no Bcc is in the body and 'To; was set to 'undisclosed-recipients'
$body = explode("\r\n", $body);
$recipientHeaders = array_values(array_filter($body, function ($line) {
return preg_match('/^(To|Cc|Bcc):/i', $line);
}));
Assert::count(1, $recipientHeaders);
Assert::equal("To: undisclosed-recipients: ;", $recipientHeaders[0]);
75 changes: 75 additions & 0 deletions tests/Mail/SmtpMailerTestWrapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

/**
* Common code for Mail test cases.
*/

declare(strict_types=1);

use Nette\Mail\SmtpMailer;
use Nette\Mail\Message;

/**
* A wrapper for SmtpMailer (derived class) that helps with tesing.
* It overrides internal connection functions so we can mock TCP communication.
* Note: this is the first implementation -- it only collects the write operations.
*/
class SmtpMailerTestWrapper extends SmtpMailer
{
private $connected = false;
private $written = [];

public function __construct() {
parent::__construct('localhost', '', '');
}

/**
* Overrides connection to mock TCP interaction.
*/
protected function connect(): void
{
if ($this->connected) {
throw new Exception("The connect() function called, but the connection was already established.");
}
$this->connected = true;
}


/**
* Terminates mocking connection.
*/
protected function disconnect(): void
{
if (!$this->connected) {
throw new Exception("The disconnect() function called, but no connection was currently established.");
}
$this->connected = false;
}


/**
* Overrides writing function so we can collect, what was actually written by the sender.
*/
protected function write(string $line, int|array|null $expectedCode = null, ?string $message = null): void
{
$this->written[] = $line;
}


/**
* Overrides reading function to mock inputs for the mailer.
*/
protected function read(): string
{
return ''; // not needed yet, may be implemented in the future
}

/**
* Return lines collected in write calls.
* @return string[]
*/
public function getWrittenLines(): array
{
return $this->written;
}
}