diff --git a/src/Object/Email.php b/src/Object/Email.php
index 9f7876312..008e68c4b 100644
--- a/src/Object/Email.php
+++ b/src/Object/Email.php
@@ -137,10 +137,10 @@ class Email implements IEmail
foreach ($this->additionalMailHeader as $name => $values) {
if (is_array($values)) {
foreach ($values as $value) {
- $headerString .= $name . ': ' . $value . '\n';
+ $headerString .= "$name : $value\n";
}
} else {
- $headerString .= $name . ': ' . $values . '\n';
+ $headerString .= "$name : $values\n";
}
}
return $headerString;
diff --git a/src/Util/Emailer.php b/src/Util/Emailer.php
index 0594937ec..ed6c7b331 100644
--- a/src/Util/Emailer.php
+++ b/src/Util/Emailer.php
@@ -134,6 +134,17 @@ class Emailer
return true;
}
+ // @see https://github.com/friendica/friendica/issues/9142
+ $countMessageId = 0;
+ foreach ($email->getAdditionalMailHeader() as $name => $value) {
+ if (strtolower($name) == 'message-id') {
+ $countMessageId += count($value);
+ }
+ }
+ if ($countMessageId > 0) {
+ $this->logger->warning('More than one Message-ID found - RFC violation', ['email' => $email]);
+ }
+
$email_textonly = false;
if (!empty($email->getRecipientUid())) {
$email_textonly = $this->pConfig->get($email->getRecipientUid(), 'system', 'email_textonly');
@@ -197,15 +208,34 @@ class Emailer
return true;
}
- $res = mail(
+ $res = $this->mail(
$hookdata['to'],
$hookdata['subject'],
$hookdata['body'],
$hookdata['headers'],
$hookdata['parameters']
);
+
$this->logger->debug('header ' . 'To: ' . $email->getToAddress() . '\n' . $messageHeader);
$this->logger->debug('return value ' . (($res) ? 'true' : 'false'));
+
return $res;
}
+
+ /**
+ * Wrapper around the mail() method (mainly used to overwrite for tests)
+ * @see mail()
+ *
+ * @param string $to Recipient of this mail
+ * @param string $subject Subject of this mail
+ * @param string $body Message body of this mail
+ * @param string $headers Headers of this mail
+ * @param string $parameters Additional (sendmail) parameters of this mail
+ *
+ * @return boolean true if the mail was successfully accepted for delivery, false otherwise.
+ */
+ protected function mail(string $to, string $subject, string $body, string $headers, string $parameters)
+ {
+ return mail($to, $subject, $body, $headers, $parameters);
+ }
}
diff --git a/tests/Util/EmailerSpy.php b/tests/Util/EmailerSpy.php
new file mode 100644
index 000000000..effd2ed08
--- /dev/null
+++ b/tests/Util/EmailerSpy.php
@@ -0,0 +1,23 @@
+ $to,
+ 'subject' => $subject,
+ 'body' => $body,
+ 'headers' => $headers,
+ 'parameters' => $parameters,
+ ];
+
+ return true;
+ }
+}
diff --git a/tests/Util/HookMockTrait.php b/tests/Util/HookMockTrait.php
new file mode 100644
index 000000000..68a17c5bd
--- /dev/null
+++ b/tests/Util/HookMockTrait.php
@@ -0,0 +1,30 @@
+hookMock)) {
+ $this->hookMock = \Mockery::mock('alias:' . Hook::class);
+ }
+
+ $this->hookMock
+ ->shouldReceive('callAll')
+ ->withArgs([$name, \Mockery::capture($capture)]);
+ }
+}
diff --git a/tests/src/Util/EMailerTest.php b/tests/src/Util/EMailerTest.php
new file mode 100644
index 000000000..209195f7e
--- /dev/null
+++ b/tests/src/Util/EMailerTest.php
@@ -0,0 +1,133 @@
+setUpVfsDir();
+
+ $this->config = \Mockery::mock(IConfig::class);
+ $this->config->shouldReceive('get')->withArgs(['config', 'sender_email'])->andReturn('test@friendica.local')->once();
+ $this->config->shouldReceive('get')->withArgs(['config', 'sitename', 'Friendica Social Network'])->andReturn('Friendica Social Network')->once();
+ $this->config->shouldReceive('get')->withArgs(['system', 'sendmail_params', true])->andReturn(true);
+
+ $this->pConfig = \Mockery::mock(IPConfig::class);
+ $this->l10n = \Mockery::mock(L10n::class);
+ $this->baseUrl = \Mockery::mock(BaseURL::class);
+ $this->baseUrl->shouldReceive('getHostname')->andReturn('friendica.local');
+ $this->baseUrl->shouldReceive('get')->andReturn('http://friendica.local');
+
+ $this->defaultHeaders = [];
+ }
+
+ protected function tearDown()
+ {
+ EmailerSpy::$MAIL_DATA = [];
+
+ parent::tearDown();
+ }
+
+ public function testEmail()
+ {
+ $this->pConfig->shouldReceive('get')->withArgs(['1', 'system', 'email_textonly'])->andReturn(false)->once();
+
+ $builder = new SampleMailBuilder($this->l10n, $this->baseUrl, $this->config, new NullLogger());
+
+ $testEmail = $builder
+ ->withRecipient('recipient@friendica.local')
+ ->withMessage('Test Subject', "Test MessageBold", 'Test Text')
+ ->withSender('Sender', 'sender@friendica.local')
+ ->forUser(['uid' => 1])
+ ->addHeader('Message-ID', 'first Id')
+ ->build(true);
+
+ $emailer = new EmailerSpy($this->config, $this->pConfig, $this->baseUrl, new NullLogger(), $this->l10n);
+
+ $this->assertTrue($emailer->send($testEmail));
+
+ $this->assertContains("X-Friendica-Host : friendica.local", EmailerSpy::$MAIL_DATA['headers']);
+ $this->assertContains("X-Friendica-Platform : Friendica", EmailerSpy::$MAIL_DATA['headers']);
+ $this->assertContains("List-ID : ", EmailerSpy::$MAIL_DATA['headers']);
+ $this->assertContains("List-Archive : ", EmailerSpy::$MAIL_DATA['headers']);
+ $this->assertContains("Reply-To: Sender ", EmailerSpy::$MAIL_DATA['headers']);
+ $this->assertContains("MIME-Version: 1.0", EmailerSpy::$MAIL_DATA['headers']);
+ // Base64 "Test Text"
+ $this->assertContains(chunk_split(base64_encode('Test Text')), EmailerSpy::$MAIL_DATA['body']);
+ // Base64 "Test MessageBold"
+ $this->assertContains(chunk_split(base64_encode("Test MessageBold")), EmailerSpy::$MAIL_DATA['body']);
+ $this->assertEquals("Test Subject", EmailerSpy::$MAIL_DATA['subject']);
+ $this->assertEquals("recipient@friendica.local", EmailerSpy::$MAIL_DATA['to']);
+ $this->assertEquals("-f sender@friendica.local", EmailerSpy::$MAIL_DATA['parameters']);
+ }
+
+ public function testTwoMessageIds()
+ {
+ $this->pConfig->shouldReceive('get')->withArgs(['1', 'system', 'email_textonly'])->andReturn(false)->once();
+
+ /** @var IEmail $preparedEmail */
+ $preparedEmail = null;
+ /** @var IEmail $sentEMail */
+ $sentEMail = null;
+
+ $this->mockHookCallAll('emailer_send_prepare', $preparedEmail);
+ $this->mockHookCallAll('emailer_send', $sentEMail);
+
+ $builder = new SampleMailBuilder($this->l10n, $this->baseUrl, $this->config, new NullLogger());
+
+ $testEmail = $builder
+ ->withRecipient('recipient@friendica.local')
+ ->withMessage('Test Subject', "Test MessageBold", 'Test Text')
+ ->withSender('Sender', 'sender@friendica.loca')
+ ->forUser(['uid' => 1])
+ ->addHeader('Message-ID', 'first Id')
+ ->addHeader('Message-Id', 'second Id')
+ ->build(true);
+
+ $emailer = new EmailerSpy($this->config, $this->pConfig, $this->baseUrl, new NullLogger(), $this->l10n);
+
+ // even in case there are two message ids, send the mail anyway
+ $this->assertTrue($emailer->send($testEmail));
+
+ // check case sensitive key problem
+ $this->assertArrayHasKey('Message-ID', $testEmail->getAdditionalMailHeader());
+ $this->assertArrayHasKey('Message-Id', $testEmail->getAdditionalMailHeader());
+ }
+}
diff --git a/tests/src/Util/Emailer/MailBuilderTest.php b/tests/src/Util/Emailer/MailBuilderTest.php
index 202ad587b..b89022421 100644
--- a/tests/src/Util/Emailer/MailBuilderTest.php
+++ b/tests/src/Util/Emailer/MailBuilderTest.php
@@ -29,6 +29,7 @@ use Friendica\Test\MockedTest;
use Friendica\Test\Util\SampleMailBuilder;
use Friendica\Test\Util\VFSTrait;
use Friendica\Util\EMailer\MailBuilder;
+use Mockery\MockInterface;
use Psr\Log\NullLogger;
/**
@@ -39,11 +40,11 @@ class MailBuilderTest extends MockedTest
{
use VFSTrait;
- /** @var IConfig */
+ /** @var IConfig|MockInterface */
private $config;
- /** @var L10n */
+ /** @var L10n|MockInterface */
private $l10n;
- /** @var BaseURL */
+ /** @var BaseURL|MockInterface */
private $baseUrl;
/** @var string */