first commit
This commit is contained in:
98
vendor/symfony/mailer/CHANGELOG.md
vendored
Normal file
98
vendor/symfony/mailer/CHANGELOG.md
vendored
Normal file
@ -0,0 +1,98 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
6.4
|
||||
---
|
||||
|
||||
* Add DSN parameter `peer_fingerprint` to verify TLS certificate fingerprint
|
||||
* Change the default port for the `mailjet+smtp` transport from 465 to 587
|
||||
|
||||
6.3
|
||||
---
|
||||
|
||||
* Add `MessageEvent::reject()` to allow rejecting an email before sending it
|
||||
* Change the default port for the `mailgun+smtp` transport from 465 to 587
|
||||
* Add `$authenticators` parameter in `EsmtpTransport` constructor and `EsmtpTransport::setAuthenticators()`
|
||||
to allow overriding of default eSMTP authenticators
|
||||
|
||||
6.2.7
|
||||
-----
|
||||
|
||||
* [BC BREAK] The following data providers for `TransportFactoryTestCase` are now static:
|
||||
`supportsProvider()`, `createProvider()`, `unsupportedSchemeProvider()`and `incompleteDsnProvider()`
|
||||
|
||||
6.2
|
||||
---
|
||||
|
||||
* Add a `mailer:test` command
|
||||
* Add `SentMessageEvent` and `FailedMessageEvent` events
|
||||
|
||||
6.1
|
||||
---
|
||||
|
||||
* Make `start()` and `stop()` methods public on `SmtpTransport`
|
||||
* Improve extensibility of `EsmtpTransport`
|
||||
|
||||
6.0
|
||||
---
|
||||
|
||||
* The `HttpTransportException` class takes a string at first argument
|
||||
|
||||
5.4
|
||||
---
|
||||
|
||||
* Enable the mailer to operate on any PSR-14-compatible event dispatcher
|
||||
|
||||
5.3
|
||||
---
|
||||
|
||||
* added the `mailer` monolog channel and set it on all transport definitions
|
||||
|
||||
5.2.0
|
||||
-----
|
||||
|
||||
* added `NativeTransportFactory` to configure a transport based on php.ini settings
|
||||
* added `local_domain`, `restart_threshold`, `restart_threshold_sleep` and `ping_threshold` options for `smtp`
|
||||
* added `command` option for `sendmail`
|
||||
|
||||
4.4.0
|
||||
-----
|
||||
|
||||
* [BC BREAK] changed the `NullTransport` DSN from `smtp://null` to `null://null`
|
||||
* [BC BREAK] renamed `SmtpEnvelope` to `Envelope`, renamed `DelayedSmtpEnvelope` to
|
||||
`DelayedEnvelope`
|
||||
* [BC BREAK] changed the syntax for failover and roundrobin DSNs
|
||||
|
||||
Before:
|
||||
|
||||
dummy://a || dummy://b (for failover)
|
||||
dummy://a && dummy://b (for roundrobin)
|
||||
|
||||
After:
|
||||
|
||||
failover(dummy://a dummy://b)
|
||||
roundrobin(dummy://a dummy://b)
|
||||
|
||||
* added support for multiple transports on a `Mailer` instance
|
||||
* [BC BREAK] removed the `auth_mode` DSN option (it is now always determined automatically)
|
||||
* STARTTLS cannot be enabled anymore (it is used automatically if TLS is disabled and the server supports STARTTLS)
|
||||
* [BC BREAK] Removed the `encryption` DSN option (use `smtps` instead)
|
||||
* Added support for the `smtps` protocol (does the same as using `smtp` and port `465`)
|
||||
* Added PHPUnit constraints
|
||||
* Added `MessageDataCollector`
|
||||
* Added `MessageEvents` and `MessageLoggerListener` to allow collecting sent emails
|
||||
* [BC BREAK] `TransportInterface` has a new `__toString()` method
|
||||
* [BC BREAK] Classes `AbstractApiTransport` and `AbstractHttpTransport` moved under `Transport` sub-namespace.
|
||||
* [BC BREAK] Transports depend on `Symfony\Contracts\EventDispatcher\EventDispatcherInterface`
|
||||
instead of `Symfony\Component\EventDispatcher\EventDispatcherInterface`.
|
||||
* Added possibility to register custom transport for dsn by implementing
|
||||
`Symfony\Component\Mailer\Transport\TransportFactoryInterface` and tagging with `mailer.transport_factory` tag in DI.
|
||||
* Added `Symfony\Component\Mailer\Test\TransportFactoryTestCase` to ease testing custom transport factories.
|
||||
* Added `SentMessage::getDebug()` and `TransportExceptionInterface::getDebug` to help debugging
|
||||
* Made `MessageEvent` final
|
||||
* add DSN parameter `verify_peer` to disable TLS peer verification for SMTP transport
|
||||
|
||||
4.3.0
|
||||
-----
|
||||
|
||||
* Added the component.
|
75
vendor/symfony/mailer/Command/MailerTestCommand.php
vendored
Normal file
75
vendor/symfony/mailer/Command/MailerTestCommand.php
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Command;
|
||||
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Mailer\Transport\TransportInterface;
|
||||
use Symfony\Component\Mime\Email;
|
||||
|
||||
/**
|
||||
* A console command to test Mailer transports.
|
||||
*/
|
||||
#[AsCommand(name: 'mailer:test', description: 'Test Mailer transports by sending an email')]
|
||||
final class MailerTestCommand extends Command
|
||||
{
|
||||
public function __construct(private TransportInterface $transport)
|
||||
{
|
||||
$this->transport = $transport;
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->addArgument('to', InputArgument::REQUIRED, 'The recipient of the message')
|
||||
->addOption('from', null, InputOption::VALUE_OPTIONAL, 'The sender of the message', 'from@example.org')
|
||||
->addOption('subject', null, InputOption::VALUE_OPTIONAL, 'The subject of the message', 'Testing transport')
|
||||
->addOption('body', null, InputOption::VALUE_OPTIONAL, 'The body of the message', 'Testing body')
|
||||
->addOption('transport', null, InputOption::VALUE_OPTIONAL, 'The transport to be used')
|
||||
->setHelp(<<<'EOF'
|
||||
The <info>%command.name%</info> command tests a Mailer transport by sending a simple email message:
|
||||
|
||||
<info>php %command.full_name% to@example.com</info>
|
||||
|
||||
You can also specify a specific transport:
|
||||
|
||||
<info>php %command.full_name% to@example.com --transport=transport_name</info>
|
||||
|
||||
Note that this command bypasses the Messenger bus if configured.
|
||||
|
||||
EOF
|
||||
);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$message = (new Email())
|
||||
->to($input->getArgument('to'))
|
||||
->from($input->getOption('from'))
|
||||
->subject($input->getOption('subject'))
|
||||
->text($input->getOption('body'))
|
||||
;
|
||||
if ($transport = $input->getOption('transport')) {
|
||||
$message->getHeaders()->addTextHeader('X-Transport', $transport);
|
||||
}
|
||||
|
||||
$this->transport->send($message);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
59
vendor/symfony/mailer/DataCollector/MessageDataCollector.php
vendored
Normal file
59
vendor/symfony/mailer/DataCollector/MessageDataCollector.php
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\DataCollector;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
|
||||
use Symfony\Component\Mailer\Event\MessageEvents;
|
||||
use Symfony\Component\Mailer\EventListener\MessageLoggerListener;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
final class MessageDataCollector extends DataCollector
|
||||
{
|
||||
private MessageEvents $events;
|
||||
|
||||
public function __construct(MessageLoggerListener $logger)
|
||||
{
|
||||
$this->events = $logger->getEvents();
|
||||
}
|
||||
|
||||
public function collect(Request $request, Response $response, ?\Throwable $exception = null): void
|
||||
{
|
||||
$this->data['events'] = $this->events;
|
||||
}
|
||||
|
||||
public function getEvents(): MessageEvents
|
||||
{
|
||||
return $this->data['events'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function base64Encode(string $data): string
|
||||
{
|
||||
return base64_encode($data);
|
||||
}
|
||||
|
||||
public function reset(): void
|
||||
{
|
||||
$this->data = [];
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return 'mailer';
|
||||
}
|
||||
}
|
98
vendor/symfony/mailer/DelayedEnvelope.php
vendored
Normal file
98
vendor/symfony/mailer/DelayedEnvelope.php
vendored
Normal file
@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer;
|
||||
|
||||
use Symfony\Component\Mailer\Exception\LogicException;
|
||||
use Symfony\Component\Mime\Address;
|
||||
use Symfony\Component\Mime\Header\Headers;
|
||||
use Symfony\Component\Mime\Message;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class DelayedEnvelope extends Envelope
|
||||
{
|
||||
private bool $senderSet = false;
|
||||
private bool $recipientsSet = false;
|
||||
private Message $message;
|
||||
|
||||
public function __construct(Message $message)
|
||||
{
|
||||
$this->message = $message;
|
||||
}
|
||||
|
||||
public function setSender(Address $sender): void
|
||||
{
|
||||
parent::setSender($sender);
|
||||
|
||||
$this->senderSet = true;
|
||||
}
|
||||
|
||||
public function getSender(): Address
|
||||
{
|
||||
if (!$this->senderSet) {
|
||||
parent::setSender(self::getSenderFromHeaders($this->message->getHeaders()));
|
||||
}
|
||||
|
||||
return parent::getSender();
|
||||
}
|
||||
|
||||
public function setRecipients(array $recipients): void
|
||||
{
|
||||
parent::setRecipients($recipients);
|
||||
|
||||
$this->recipientsSet = (bool) parent::getRecipients();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Address[]
|
||||
*/
|
||||
public function getRecipients(): array
|
||||
{
|
||||
if ($this->recipientsSet) {
|
||||
return parent::getRecipients();
|
||||
}
|
||||
|
||||
return self::getRecipientsFromHeaders($this->message->getHeaders());
|
||||
}
|
||||
|
||||
private static function getRecipientsFromHeaders(Headers $headers): array
|
||||
{
|
||||
$recipients = [];
|
||||
foreach (['to', 'cc', 'bcc'] as $name) {
|
||||
foreach ($headers->all($name) as $header) {
|
||||
foreach ($header->getAddresses() as $address) {
|
||||
$recipients[] = $address;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $recipients;
|
||||
}
|
||||
|
||||
private static function getSenderFromHeaders(Headers $headers): Address
|
||||
{
|
||||
if ($sender = $headers->get('Sender')) {
|
||||
return $sender->getAddress();
|
||||
}
|
||||
if ($return = $headers->get('Return-Path')) {
|
||||
return $return->getAddress();
|
||||
}
|
||||
if ($from = $headers->get('From')) {
|
||||
return $from->getAddresses()[0];
|
||||
}
|
||||
|
||||
throw new LogicException('Unable to determine the sender of the message.');
|
||||
}
|
||||
}
|
88
vendor/symfony/mailer/Envelope.php
vendored
Normal file
88
vendor/symfony/mailer/Envelope.php
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer;
|
||||
|
||||
use Symfony\Component\Mailer\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Mailer\Exception\LogicException;
|
||||
use Symfony\Component\Mime\Address;
|
||||
use Symfony\Component\Mime\RawMessage;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Envelope
|
||||
{
|
||||
private Address $sender;
|
||||
private array $recipients = [];
|
||||
|
||||
/**
|
||||
* @param Address[] $recipients
|
||||
*/
|
||||
public function __construct(Address $sender, array $recipients)
|
||||
{
|
||||
$this->setSender($sender);
|
||||
$this->setRecipients($recipients);
|
||||
}
|
||||
|
||||
public static function create(RawMessage $message): self
|
||||
{
|
||||
if (RawMessage::class === $message::class) {
|
||||
throw new LogicException('Cannot send a RawMessage instance without an explicit Envelope.');
|
||||
}
|
||||
|
||||
return new DelayedEnvelope($message);
|
||||
}
|
||||
|
||||
public function setSender(Address $sender): void
|
||||
{
|
||||
// to ensure deliverability of bounce emails independent of UTF-8 capabilities of SMTP servers
|
||||
if (!preg_match('/^[^@\x80-\xFF]++@/', $sender->getAddress())) {
|
||||
throw new InvalidArgumentException(sprintf('Invalid sender "%s": non-ASCII characters not supported in local-part of email.', $sender->getAddress()));
|
||||
}
|
||||
$this->sender = $sender;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Address Returns a "mailbox" as specified by RFC 2822
|
||||
* Must be converted to an "addr-spec" when used as a "MAIL FROM" value in SMTP (use getAddress())
|
||||
*/
|
||||
public function getSender(): Address
|
||||
{
|
||||
return $this->sender;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Address[] $recipients
|
||||
*/
|
||||
public function setRecipients(array $recipients): void
|
||||
{
|
||||
if (!$recipients) {
|
||||
throw new InvalidArgumentException('An envelope must have at least one recipient.');
|
||||
}
|
||||
|
||||
$this->recipients = [];
|
||||
foreach ($recipients as $recipient) {
|
||||
if (!$recipient instanceof Address) {
|
||||
throw new InvalidArgumentException(sprintf('A recipient must be an instance of "%s" (got "%s").', Address::class, get_debug_type($recipient)));
|
||||
}
|
||||
$this->recipients[] = new Address($recipient->getAddress());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Address[]
|
||||
*/
|
||||
public function getRecipients(): array
|
||||
{
|
||||
return $this->recipients;
|
||||
}
|
||||
}
|
37
vendor/symfony/mailer/Event/FailedMessageEvent.php
vendored
Normal file
37
vendor/symfony/mailer/Event/FailedMessageEvent.php
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Event;
|
||||
|
||||
use Symfony\Component\Mime\RawMessage;
|
||||
use Symfony\Contracts\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
final class FailedMessageEvent extends Event
|
||||
{
|
||||
public function __construct(
|
||||
private RawMessage $message,
|
||||
private \Throwable $error,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getMessage(): RawMessage
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
public function getError(): \Throwable
|
||||
{
|
||||
return $this->error;
|
||||
}
|
||||
}
|
105
vendor/symfony/mailer/Event/MessageEvent.php
vendored
Normal file
105
vendor/symfony/mailer/Event/MessageEvent.php
vendored
Normal file
@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Event;
|
||||
|
||||
use Symfony\Component\Mailer\Envelope;
|
||||
use Symfony\Component\Mailer\Exception\LogicException;
|
||||
use Symfony\Component\Messenger\Stamp\StampInterface;
|
||||
use Symfony\Component\Mime\RawMessage;
|
||||
use Symfony\Contracts\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Allows the transformation of a Message and the Envelope before the email is sent.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
final class MessageEvent extends Event
|
||||
{
|
||||
private RawMessage $message;
|
||||
private Envelope $envelope;
|
||||
private string $transport;
|
||||
private bool $queued;
|
||||
private bool $rejected = false;
|
||||
|
||||
/** @var StampInterface[] */
|
||||
private array $stamps = [];
|
||||
|
||||
public function __construct(RawMessage $message, Envelope $envelope, string $transport, bool $queued = false)
|
||||
{
|
||||
$this->message = $message;
|
||||
$this->envelope = $envelope;
|
||||
$this->transport = $transport;
|
||||
$this->queued = $queued;
|
||||
}
|
||||
|
||||
public function getMessage(): RawMessage
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
public function setMessage(RawMessage $message): void
|
||||
{
|
||||
$this->message = $message;
|
||||
}
|
||||
|
||||
public function getEnvelope(): Envelope
|
||||
{
|
||||
return $this->envelope;
|
||||
}
|
||||
|
||||
public function setEnvelope(Envelope $envelope): void
|
||||
{
|
||||
$this->envelope = $envelope;
|
||||
}
|
||||
|
||||
public function getTransport(): string
|
||||
{
|
||||
return $this->transport;
|
||||
}
|
||||
|
||||
public function isQueued(): bool
|
||||
{
|
||||
return $this->queued;
|
||||
}
|
||||
|
||||
public function isRejected(): bool
|
||||
{
|
||||
return $this->rejected;
|
||||
}
|
||||
|
||||
public function reject(): void
|
||||
{
|
||||
$this->rejected = true;
|
||||
$this->stopPropagation();
|
||||
}
|
||||
|
||||
public function addStamp(StampInterface $stamp): void
|
||||
{
|
||||
if (!$this->queued) {
|
||||
throw new LogicException(sprintf('Cannot call "%s()" on a message that is not meant to be queued.', __METHOD__));
|
||||
}
|
||||
|
||||
$this->stamps[] = $stamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return StampInterface[]
|
||||
*/
|
||||
public function getStamps(): array
|
||||
{
|
||||
if (!$this->queued) {
|
||||
throw new LogicException(sprintf('Cannot call "%s()" on a message that is not meant to be queued.', __METHOD__));
|
||||
}
|
||||
|
||||
return $this->stamps;
|
||||
}
|
||||
}
|
74
vendor/symfony/mailer/Event/MessageEvents.php
vendored
Normal file
74
vendor/symfony/mailer/Event/MessageEvents.php
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Event;
|
||||
|
||||
use Symfony\Component\Mime\RawMessage;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class MessageEvents
|
||||
{
|
||||
/**
|
||||
* @var MessageEvent[]
|
||||
*/
|
||||
private array $events = [];
|
||||
|
||||
/**
|
||||
* @var array<string, bool>
|
||||
*/
|
||||
private array $transports = [];
|
||||
|
||||
public function add(MessageEvent $event): void
|
||||
{
|
||||
$this->events[] = $event;
|
||||
$this->transports[$event->getTransport()] = true;
|
||||
}
|
||||
|
||||
public function getTransports(): array
|
||||
{
|
||||
return array_keys($this->transports);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MessageEvent[]
|
||||
*/
|
||||
public function getEvents(?string $name = null): array
|
||||
{
|
||||
if (null === $name) {
|
||||
return $this->events;
|
||||
}
|
||||
|
||||
$events = [];
|
||||
foreach ($this->events as $event) {
|
||||
if ($name === $event->getTransport()) {
|
||||
$events[] = $event;
|
||||
}
|
||||
}
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return RawMessage[]
|
||||
*/
|
||||
public function getMessages(?string $name = null): array
|
||||
{
|
||||
$events = $this->getEvents($name);
|
||||
$messages = [];
|
||||
foreach ($events as $event) {
|
||||
$messages[] = $event->getMessage();
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
}
|
30
vendor/symfony/mailer/Event/SentMessageEvent.php
vendored
Normal file
30
vendor/symfony/mailer/Event/SentMessageEvent.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Event;
|
||||
|
||||
use Symfony\Component\Mailer\SentMessage;
|
||||
use Symfony\Contracts\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
final class SentMessageEvent extends Event
|
||||
{
|
||||
public function __construct(private SentMessage $message)
|
||||
{
|
||||
}
|
||||
|
||||
public function getMessage(): SentMessage
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
}
|
71
vendor/symfony/mailer/EventListener/EnvelopeListener.php
vendored
Normal file
71
vendor/symfony/mailer/EventListener/EnvelopeListener.php
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\EventListener;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Mailer\Event\MessageEvent;
|
||||
use Symfony\Component\Mime\Address;
|
||||
use Symfony\Component\Mime\Message;
|
||||
|
||||
/**
|
||||
* Manipulates the Envelope of a Message.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class EnvelopeListener implements EventSubscriberInterface
|
||||
{
|
||||
private ?Address $sender = null;
|
||||
|
||||
/**
|
||||
* @var Address[]|null
|
||||
*/
|
||||
private ?array $recipients = null;
|
||||
|
||||
/**
|
||||
* @param array<Address|string> $recipients
|
||||
*/
|
||||
public function __construct(Address|string|null $sender = null, ?array $recipients = null)
|
||||
{
|
||||
if (null !== $sender) {
|
||||
$this->sender = Address::create($sender);
|
||||
}
|
||||
if (null !== $recipients) {
|
||||
$this->recipients = Address::createArray($recipients);
|
||||
}
|
||||
}
|
||||
|
||||
public function onMessage(MessageEvent $event): void
|
||||
{
|
||||
if ($this->sender) {
|
||||
$event->getEnvelope()->setSender($this->sender);
|
||||
|
||||
$message = $event->getMessage();
|
||||
if ($message instanceof Message) {
|
||||
if (!$message->getHeaders()->has('Sender') && !$message->getHeaders()->has('From')) {
|
||||
$message->getHeaders()->addMailboxHeader('Sender', $this->sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->recipients) {
|
||||
$event->getEnvelope()->setRecipients($this->recipients);
|
||||
}
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [
|
||||
// should be the last one to allow header changes by other listeners first
|
||||
MessageEvent::class => ['onMessage', -255],
|
||||
];
|
||||
}
|
||||
}
|
134
vendor/symfony/mailer/EventListener/MessageListener.php
vendored
Normal file
134
vendor/symfony/mailer/EventListener/MessageListener.php
vendored
Normal file
@ -0,0 +1,134 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\EventListener;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Mailer\Event\MessageEvent;
|
||||
use Symfony\Component\Mailer\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Mailer\Exception\RuntimeException;
|
||||
use Symfony\Component\Mime\BodyRendererInterface;
|
||||
use Symfony\Component\Mime\Header\Headers;
|
||||
use Symfony\Component\Mime\Header\MailboxListHeader;
|
||||
use Symfony\Component\Mime\Message;
|
||||
|
||||
/**
|
||||
* Manipulates the headers and the body of a Message.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class MessageListener implements EventSubscriberInterface
|
||||
{
|
||||
public const HEADER_SET_IF_EMPTY = 1;
|
||||
public const HEADER_ADD = 2;
|
||||
public const HEADER_REPLACE = 3;
|
||||
public const DEFAULT_RULES = [
|
||||
'from' => self::HEADER_SET_IF_EMPTY,
|
||||
'return-path' => self::HEADER_SET_IF_EMPTY,
|
||||
'reply-to' => self::HEADER_ADD,
|
||||
'to' => self::HEADER_SET_IF_EMPTY,
|
||||
'cc' => self::HEADER_ADD,
|
||||
'bcc' => self::HEADER_ADD,
|
||||
];
|
||||
|
||||
private ?Headers $headers;
|
||||
private array $headerRules = [];
|
||||
private ?BodyRendererInterface $renderer;
|
||||
|
||||
public function __construct(?Headers $headers = null, ?BodyRendererInterface $renderer = null, array $headerRules = self::DEFAULT_RULES)
|
||||
{
|
||||
$this->headers = $headers;
|
||||
$this->renderer = $renderer;
|
||||
foreach ($headerRules as $headerName => $rule) {
|
||||
$this->addHeaderRule($headerName, $rule);
|
||||
}
|
||||
}
|
||||
|
||||
public function addHeaderRule(string $headerName, int $rule): void
|
||||
{
|
||||
if ($rule < 1 || $rule > 3) {
|
||||
throw new InvalidArgumentException(sprintf('The "%d" rule is not supported.', $rule));
|
||||
}
|
||||
|
||||
$this->headerRules[strtolower($headerName)] = $rule;
|
||||
}
|
||||
|
||||
public function onMessage(MessageEvent $event): void
|
||||
{
|
||||
$message = $event->getMessage();
|
||||
if (!$message instanceof Message) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->setHeaders($message);
|
||||
$this->renderMessage($message);
|
||||
}
|
||||
|
||||
private function setHeaders(Message $message): void
|
||||
{
|
||||
if (!$this->headers) {
|
||||
return;
|
||||
}
|
||||
|
||||
$headers = $message->getHeaders();
|
||||
foreach ($this->headers->all() as $name => $header) {
|
||||
if (!$headers->has($name)) {
|
||||
$headers->add($header);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
switch ($this->headerRules[$name] ?? self::HEADER_SET_IF_EMPTY) {
|
||||
case self::HEADER_SET_IF_EMPTY:
|
||||
break;
|
||||
|
||||
case self::HEADER_REPLACE:
|
||||
$headers->remove($name);
|
||||
$headers->add($header);
|
||||
|
||||
break;
|
||||
|
||||
case self::HEADER_ADD:
|
||||
if (!Headers::isUniqueHeader($name)) {
|
||||
$headers->add($header);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$h = $headers->get($name);
|
||||
if (!$h instanceof MailboxListHeader) {
|
||||
throw new RuntimeException(sprintf('Unable to set header "%s".', $name));
|
||||
}
|
||||
|
||||
Headers::checkHeaderClass($header);
|
||||
foreach ($header->getAddresses() as $address) {
|
||||
$h->addAddress($address);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function renderMessage(Message $message): void
|
||||
{
|
||||
if (!$this->renderer) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->renderer->render($message);
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [
|
||||
MessageEvent::class => 'onMessage',
|
||||
];
|
||||
}
|
||||
}
|
57
vendor/symfony/mailer/EventListener/MessageLoggerListener.php
vendored
Normal file
57
vendor/symfony/mailer/EventListener/MessageLoggerListener.php
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\EventListener;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Mailer\Event\MessageEvent;
|
||||
use Symfony\Component\Mailer\Event\MessageEvents;
|
||||
use Symfony\Contracts\Service\ResetInterface;
|
||||
|
||||
/**
|
||||
* Logs Messages.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class MessageLoggerListener implements EventSubscriberInterface, ResetInterface
|
||||
{
|
||||
private MessageEvents $events;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->events = new MessageEvents();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->events = new MessageEvents();
|
||||
}
|
||||
|
||||
public function onMessage(MessageEvent $event): void
|
||||
{
|
||||
$this->events->add($event);
|
||||
}
|
||||
|
||||
public function getEvents(): MessageEvents
|
||||
{
|
||||
return $this->events;
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [
|
||||
MessageEvent::class => ['onMessage', -255],
|
||||
];
|
||||
}
|
||||
}
|
49
vendor/symfony/mailer/EventListener/MessengerTransportListener.php
vendored
Normal file
49
vendor/symfony/mailer/EventListener/MessengerTransportListener.php
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\EventListener;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Mailer\Event\MessageEvent;
|
||||
use Symfony\Component\Messenger\Stamp\TransportNamesStamp;
|
||||
use Symfony\Component\Mime\Message;
|
||||
|
||||
/**
|
||||
* Allows messages to be sent to specific Messenger transports via the "X-Bus-Transport" MIME header.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
final class MessengerTransportListener implements EventSubscriberInterface
|
||||
{
|
||||
public function onMessage(MessageEvent $event): void
|
||||
{
|
||||
if (!$event->isQueued()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$message = $event->getMessage();
|
||||
if (!$message instanceof Message || !$message->getHeaders()->has('X-Bus-Transport')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$names = $message->getHeaders()->get('X-Bus-Transport')->getBody();
|
||||
$names = array_map('trim', explode(',', $names));
|
||||
$event->addStamp(new TransportNamesStamp($names));
|
||||
$message->getHeaders()->remove('X-Bus-Transport');
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [
|
||||
MessageEvent::class => 'onMessage',
|
||||
];
|
||||
}
|
||||
}
|
21
vendor/symfony/mailer/Exception/ExceptionInterface.php
vendored
Normal file
21
vendor/symfony/mailer/Exception/ExceptionInterface.php
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Exception;
|
||||
|
||||
/**
|
||||
* Exception interface for all exceptions thrown by the component.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
interface ExceptionInterface extends \Throwable
|
||||
{
|
||||
}
|
34
vendor/symfony/mailer/Exception/HttpTransportException.php
vendored
Normal file
34
vendor/symfony/mailer/Exception/HttpTransportException.php
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Exception;
|
||||
|
||||
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class HttpTransportException extends TransportException
|
||||
{
|
||||
private ResponseInterface $response;
|
||||
|
||||
public function __construct(string $message, ResponseInterface $response, int $code = 0, ?\Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
|
||||
$this->response = $response;
|
||||
}
|
||||
|
||||
public function getResponse(): ResponseInterface
|
||||
{
|
||||
return $this->response;
|
||||
}
|
||||
}
|
19
vendor/symfony/mailer/Exception/IncompleteDsnException.php
vendored
Normal file
19
vendor/symfony/mailer/Exception/IncompleteDsnException.php
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Exception;
|
||||
|
||||
/**
|
||||
* @author Konstantin Myakshin <molodchick@gmail.com>
|
||||
*/
|
||||
class IncompleteDsnException extends InvalidArgumentException
|
||||
{
|
||||
}
|
19
vendor/symfony/mailer/Exception/InvalidArgumentException.php
vendored
Normal file
19
vendor/symfony/mailer/Exception/InvalidArgumentException.php
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Exception;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
|
||||
{
|
||||
}
|
19
vendor/symfony/mailer/Exception/LogicException.php
vendored
Normal file
19
vendor/symfony/mailer/Exception/LogicException.php
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Exception;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class LogicException extends \LogicException implements ExceptionInterface
|
||||
{
|
||||
}
|
19
vendor/symfony/mailer/Exception/RuntimeException.php
vendored
Normal file
19
vendor/symfony/mailer/Exception/RuntimeException.php
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Exception;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class RuntimeException extends \RuntimeException implements ExceptionInterface
|
||||
{
|
||||
}
|
30
vendor/symfony/mailer/Exception/TransportException.php
vendored
Normal file
30
vendor/symfony/mailer/Exception/TransportException.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Exception;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class TransportException extends RuntimeException implements TransportExceptionInterface
|
||||
{
|
||||
private string $debug = '';
|
||||
|
||||
public function getDebug(): string
|
||||
{
|
||||
return $this->debug;
|
||||
}
|
||||
|
||||
public function appendDebug(string $debug): void
|
||||
{
|
||||
$this->debug .= $debug;
|
||||
}
|
||||
}
|
22
vendor/symfony/mailer/Exception/TransportExceptionInterface.php
vendored
Normal file
22
vendor/symfony/mailer/Exception/TransportExceptionInterface.php
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Exception;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
interface TransportExceptionInterface extends ExceptionInterface
|
||||
{
|
||||
public function getDebug(): string;
|
||||
|
||||
public function appendDebug(string $debug): void;
|
||||
}
|
16
vendor/symfony/mailer/Exception/UnexpectedResponseException.php
vendored
Normal file
16
vendor/symfony/mailer/Exception/UnexpectedResponseException.php
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Exception;
|
||||
|
||||
class UnexpectedResponseException extends TransportException
|
||||
{
|
||||
}
|
101
vendor/symfony/mailer/Exception/UnsupportedSchemeException.php
vendored
Normal file
101
vendor/symfony/mailer/Exception/UnsupportedSchemeException.php
vendored
Normal file
@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Exception;
|
||||
|
||||
use Symfony\Component\Mailer\Bridge;
|
||||
use Symfony\Component\Mailer\Transport\Dsn;
|
||||
|
||||
/**
|
||||
* @author Konstantin Myakshin <molodchick@gmail.com>
|
||||
*/
|
||||
class UnsupportedSchemeException extends LogicException
|
||||
{
|
||||
private const SCHEME_TO_PACKAGE_MAP = [
|
||||
'brevo' => [
|
||||
'class' => Bridge\Brevo\Transport\BrevoTransportFactory::class,
|
||||
'package' => 'symfony/brevo-mailer',
|
||||
],
|
||||
'gmail' => [
|
||||
'class' => Bridge\Google\Transport\GmailTransportFactory::class,
|
||||
'package' => 'symfony/google-mailer',
|
||||
],
|
||||
'infobip' => [
|
||||
'class' => Bridge\Infobip\Transport\InfobipTransportFactory::class,
|
||||
'package' => 'symfony/infobip-mailer',
|
||||
],
|
||||
'mailersend' => [
|
||||
'class' => Bridge\MailerSend\Transport\MailerSendTransportFactory::class,
|
||||
'package' => 'symfony/mailersend-mailer',
|
||||
],
|
||||
'mailgun' => [
|
||||
'class' => Bridge\Mailgun\Transport\MailgunTransportFactory::class,
|
||||
'package' => 'symfony/mailgun-mailer',
|
||||
],
|
||||
'mailjet' => [
|
||||
'class' => Bridge\Mailjet\Transport\MailjetTransportFactory::class,
|
||||
'package' => 'symfony/mailjet-mailer',
|
||||
],
|
||||
'mailpace' => [
|
||||
'class' => Bridge\MailPace\Transport\MailPaceTransportFactory::class,
|
||||
'package' => 'symfony/mail-pace-mailer',
|
||||
],
|
||||
'mandrill' => [
|
||||
'class' => Bridge\Mailchimp\Transport\MandrillTransportFactory::class,
|
||||
'package' => 'symfony/mailchimp-mailer',
|
||||
],
|
||||
'ohmysmtp' => [
|
||||
'class' => Bridge\OhMySmtp\Transport\OhMySmtpTransportFactory::class,
|
||||
'package' => 'symfony/oh-my-smtp-mailer',
|
||||
],
|
||||
'postmark' => [
|
||||
'class' => Bridge\Postmark\Transport\PostmarkTransportFactory::class,
|
||||
'package' => 'symfony/postmark-mailer',
|
||||
],
|
||||
'scaleway' => [
|
||||
'class' => Bridge\Scaleway\Transport\ScalewayTransportFactory::class,
|
||||
'package' => 'symfony/scaleway-mailer',
|
||||
],
|
||||
'sendgrid' => [
|
||||
'class' => Bridge\Sendgrid\Transport\SendgridTransportFactory::class,
|
||||
'package' => 'symfony/sendgrid-mailer',
|
||||
],
|
||||
'sendinblue' => [
|
||||
'class' => Bridge\Sendinblue\Transport\SendinblueTransportFactory::class,
|
||||
'package' => 'symfony/sendinblue-mailer',
|
||||
],
|
||||
'ses' => [
|
||||
'class' => Bridge\Amazon\Transport\SesTransportFactory::class,
|
||||
'package' => 'symfony/amazon-mailer',
|
||||
],
|
||||
];
|
||||
|
||||
public function __construct(Dsn $dsn, ?string $name = null, array $supported = [])
|
||||
{
|
||||
$provider = $dsn->getScheme();
|
||||
if (false !== $pos = strpos($provider, '+')) {
|
||||
$provider = substr($provider, 0, $pos);
|
||||
}
|
||||
$package = self::SCHEME_TO_PACKAGE_MAP[$provider] ?? null;
|
||||
if ($package && !class_exists($package['class'])) {
|
||||
parent::__construct(sprintf('Unable to send emails via "%s" as the bridge is not installed. Try running "composer require %s".', $provider, $package['package']));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$message = sprintf('The "%s" scheme is not supported', $dsn->getScheme());
|
||||
if ($name && $supported) {
|
||||
$message .= sprintf('; supported schemes for mailer "%s" are: "%s"', $name, implode('", "', $supported));
|
||||
}
|
||||
|
||||
parent::__construct($message.'.');
|
||||
}
|
||||
}
|
34
vendor/symfony/mailer/Header/MetadataHeader.php
vendored
Normal file
34
vendor/symfony/mailer/Header/MetadataHeader.php
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Header;
|
||||
|
||||
use Symfony\Component\Mime\Header\UnstructuredHeader;
|
||||
|
||||
/**
|
||||
* @author Kevin Bond <kevinbond@gmail.com>
|
||||
*/
|
||||
final class MetadataHeader extends UnstructuredHeader
|
||||
{
|
||||
private string $key;
|
||||
|
||||
public function __construct(string $key, string $value)
|
||||
{
|
||||
$this->key = $key;
|
||||
|
||||
parent::__construct('X-Metadata-'.$key, $value);
|
||||
}
|
||||
|
||||
public function getKey(): string
|
||||
{
|
||||
return $this->key;
|
||||
}
|
||||
}
|
25
vendor/symfony/mailer/Header/TagHeader.php
vendored
Normal file
25
vendor/symfony/mailer/Header/TagHeader.php
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Header;
|
||||
|
||||
use Symfony\Component\Mime\Header\UnstructuredHeader;
|
||||
|
||||
/**
|
||||
* @author Kevin Bond <kevinbond@gmail.com>
|
||||
*/
|
||||
final class TagHeader extends UnstructuredHeader
|
||||
{
|
||||
public function __construct(string $value)
|
||||
{
|
||||
parent::__construct('X-Tag', $value);
|
||||
}
|
||||
}
|
19
vendor/symfony/mailer/LICENSE
vendored
Normal file
19
vendor/symfony/mailer/LICENSE
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
Copyright (c) 2019-present Fabien Potencier
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
76
vendor/symfony/mailer/Mailer.php
vendored
Normal file
76
vendor/symfony/mailer/Mailer.php
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer;
|
||||
|
||||
use Psr\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\Mailer\Event\MessageEvent;
|
||||
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
|
||||
use Symfony\Component\Mailer\Messenger\SendEmailMessage;
|
||||
use Symfony\Component\Mailer\Transport\TransportInterface;
|
||||
use Symfony\Component\Messenger\Exception\HandlerFailedException;
|
||||
use Symfony\Component\Messenger\MessageBusInterface;
|
||||
use Symfony\Component\Mime\RawMessage;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
final class Mailer implements MailerInterface
|
||||
{
|
||||
private TransportInterface $transport;
|
||||
private ?MessageBusInterface $bus;
|
||||
private ?EventDispatcherInterface $dispatcher;
|
||||
|
||||
public function __construct(TransportInterface $transport, ?MessageBusInterface $bus = null, ?EventDispatcherInterface $dispatcher = null)
|
||||
{
|
||||
$this->transport = $transport;
|
||||
$this->bus = $bus;
|
||||
$this->dispatcher = $dispatcher;
|
||||
}
|
||||
|
||||
public function send(RawMessage $message, ?Envelope $envelope = null): void
|
||||
{
|
||||
if (null === $this->bus) {
|
||||
$this->transport->send($message, $envelope);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$stamps = [];
|
||||
if (null !== $this->dispatcher) {
|
||||
// The dispatched event here has `queued` set to `true`; the goal is NOT to render the message, but to let
|
||||
// listeners do something before a message is sent to the queue.
|
||||
// We are using a cloned message as we still want to dispatch the **original** message, not the one modified by listeners.
|
||||
// That's because the listeners will run again when the email is sent via Messenger by the transport (see `AbstractTransport`).
|
||||
// Listeners should act depending on the `$queued` argument of the `MessageEvent` instance.
|
||||
$clonedMessage = clone $message;
|
||||
$clonedEnvelope = null !== $envelope ? clone $envelope : Envelope::create($clonedMessage);
|
||||
$event = new MessageEvent($clonedMessage, $clonedEnvelope, (string) $this->transport, true);
|
||||
$this->dispatcher->dispatch($event);
|
||||
$stamps = $event->getStamps();
|
||||
|
||||
if ($event->isRejected()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$this->bus->dispatch(new SendEmailMessage($message, $envelope), $stamps);
|
||||
} catch (HandlerFailedException $e) {
|
||||
foreach ($e->getWrappedExceptions() as $nested) {
|
||||
if ($nested instanceof TransportExceptionInterface) {
|
||||
throw $nested;
|
||||
}
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
30
vendor/symfony/mailer/MailerInterface.php
vendored
Normal file
30
vendor/symfony/mailer/MailerInterface.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer;
|
||||
|
||||
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
|
||||
use Symfony\Component\Mime\RawMessage;
|
||||
|
||||
/**
|
||||
* Interface for mailers able to send emails synchronous and/or asynchronous.
|
||||
*
|
||||
* Implementations must support synchronous and asynchronous sending.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
interface MailerInterface
|
||||
{
|
||||
/**
|
||||
* @throws TransportExceptionInterface
|
||||
*/
|
||||
public function send(RawMessage $message, ?Envelope $envelope = null): void;
|
||||
}
|
33
vendor/symfony/mailer/Messenger/MessageHandler.php
vendored
Normal file
33
vendor/symfony/mailer/Messenger/MessageHandler.php
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Messenger;
|
||||
|
||||
use Symfony\Component\Mailer\SentMessage;
|
||||
use Symfony\Component\Mailer\Transport\TransportInterface;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class MessageHandler
|
||||
{
|
||||
private TransportInterface $transport;
|
||||
|
||||
public function __construct(TransportInterface $transport)
|
||||
{
|
||||
$this->transport = $transport;
|
||||
}
|
||||
|
||||
public function __invoke(SendEmailMessage $message): ?SentMessage
|
||||
{
|
||||
return $this->transport->send($message->getMessage(), $message->getEnvelope());
|
||||
}
|
||||
}
|
40
vendor/symfony/mailer/Messenger/SendEmailMessage.php
vendored
Normal file
40
vendor/symfony/mailer/Messenger/SendEmailMessage.php
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Messenger;
|
||||
|
||||
use Symfony\Component\Mailer\Envelope;
|
||||
use Symfony\Component\Mime\RawMessage;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class SendEmailMessage
|
||||
{
|
||||
private RawMessage $message;
|
||||
private ?Envelope $envelope;
|
||||
|
||||
public function __construct(RawMessage $message, ?Envelope $envelope = null)
|
||||
{
|
||||
$this->message = $message;
|
||||
$this->envelope = $envelope;
|
||||
}
|
||||
|
||||
public function getMessage(): RawMessage
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
public function getEnvelope(): ?Envelope
|
||||
{
|
||||
return $this->envelope;
|
||||
}
|
||||
}
|
74
vendor/symfony/mailer/README.md
vendored
Normal file
74
vendor/symfony/mailer/README.md
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
Mailer Component
|
||||
================
|
||||
|
||||
The Mailer component helps sending emails.
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
|
||||
```
|
||||
$ composer require symfony/mailer
|
||||
```
|
||||
|
||||
```php
|
||||
use Symfony\Component\Mailer\Transport;
|
||||
use Symfony\Component\Mailer\Mailer;
|
||||
use Symfony\Component\Mime\Email;
|
||||
|
||||
$transport = Transport::fromDsn('smtp://localhost');
|
||||
$mailer = new Mailer($transport);
|
||||
|
||||
$email = (new Email())
|
||||
->from('hello@example.com')
|
||||
->to('you@example.com')
|
||||
//->cc('cc@example.com')
|
||||
//->bcc('bcc@example.com')
|
||||
//->replyTo('fabien@example.com')
|
||||
//->priority(Email::PRIORITY_HIGH)
|
||||
->subject('Time for Symfony Mailer!')
|
||||
->text('Sending emails is fun again!')
|
||||
->html('<p>See Twig integration for better HTML integration!</p>');
|
||||
|
||||
$mailer->send($email);
|
||||
```
|
||||
|
||||
To enable the Twig integration of the Mailer, require `symfony/twig-bridge` and
|
||||
set up the `BodyRenderer`:
|
||||
|
||||
```php
|
||||
use Symfony\Bridge\Twig\Mime\BodyRenderer;
|
||||
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Component\Mailer\EventListener\MessageListener;
|
||||
use Symfony\Component\Mailer\Mailer;
|
||||
use Symfony\Component\Mailer\Transport;
|
||||
use Twig\Environment as TwigEnvironment;
|
||||
|
||||
$twig = new TwigEnvironment(...);
|
||||
$messageListener = new MessageListener(null, new BodyRenderer($twig));
|
||||
|
||||
$eventDispatcher = new EventDispatcher();
|
||||
$eventDispatcher->addSubscriber($messageListener);
|
||||
|
||||
$transport = Transport::fromDsn('smtp://localhost', $eventDispatcher);
|
||||
$mailer = new Mailer($transport, null, $eventDispatcher);
|
||||
|
||||
$email = (new TemplatedEmail())
|
||||
// ...
|
||||
->htmlTemplate('emails/signup.html.twig')
|
||||
->context([
|
||||
'expiration_date' => new \DateTimeImmutable('+7 days'),
|
||||
'username' => 'foo',
|
||||
])
|
||||
;
|
||||
$mailer->send($email);
|
||||
```
|
||||
|
||||
Resources
|
||||
---------
|
||||
|
||||
* [Documentation](https://symfony.com/doc/current/mailer.html)
|
||||
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
|
||||
* [Report issues](https://github.com/symfony/symfony/issues) and
|
||||
[send Pull Requests](https://github.com/symfony/symfony/pulls)
|
||||
in the [main Symfony repository](https://github.com/symfony/symfony)
|
95
vendor/symfony/mailer/SentMessage.php
vendored
Normal file
95
vendor/symfony/mailer/SentMessage.php
vendored
Normal file
@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer;
|
||||
|
||||
use Symfony\Component\Mime\Message;
|
||||
use Symfony\Component\Mime\RawMessage;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class SentMessage
|
||||
{
|
||||
private RawMessage $original;
|
||||
private RawMessage $raw;
|
||||
private Envelope $envelope;
|
||||
private string $messageId;
|
||||
private string $debug = '';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function __construct(RawMessage $message, Envelope $envelope)
|
||||
{
|
||||
$message->ensureValidity();
|
||||
|
||||
$this->original = $message;
|
||||
$this->envelope = $envelope;
|
||||
|
||||
if ($message instanceof Message) {
|
||||
$message = clone $message;
|
||||
$headers = $message->getHeaders();
|
||||
if (!$headers->has('Message-ID')) {
|
||||
$headers->addIdHeader('Message-ID', $message->generateMessageId());
|
||||
}
|
||||
$this->messageId = $headers->get('Message-ID')->getId();
|
||||
$this->raw = new RawMessage($message->toIterable());
|
||||
} else {
|
||||
$this->raw = $message;
|
||||
}
|
||||
}
|
||||
|
||||
public function getMessage(): RawMessage
|
||||
{
|
||||
return $this->raw;
|
||||
}
|
||||
|
||||
public function getOriginalMessage(): RawMessage
|
||||
{
|
||||
return $this->original;
|
||||
}
|
||||
|
||||
public function getEnvelope(): Envelope
|
||||
{
|
||||
return $this->envelope;
|
||||
}
|
||||
|
||||
public function setMessageId(string $id): void
|
||||
{
|
||||
$this->messageId = $id;
|
||||
}
|
||||
|
||||
public function getMessageId(): string
|
||||
{
|
||||
return $this->messageId;
|
||||
}
|
||||
|
||||
public function getDebug(): string
|
||||
{
|
||||
return $this->debug;
|
||||
}
|
||||
|
||||
public function appendDebug(string $debug): void
|
||||
{
|
||||
$this->debug .= $debug;
|
||||
}
|
||||
|
||||
public function toString(): string
|
||||
{
|
||||
return $this->raw->toString();
|
||||
}
|
||||
|
||||
public function toIterable(): iterable
|
||||
{
|
||||
return $this->raw->toIterable();
|
||||
}
|
||||
}
|
65
vendor/symfony/mailer/Test/Constraint/EmailCount.php
vendored
Normal file
65
vendor/symfony/mailer/Test/Constraint/EmailCount.php
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Test\Constraint;
|
||||
|
||||
use PHPUnit\Framework\Constraint\Constraint;
|
||||
use Symfony\Component\Mailer\Event\MessageEvents;
|
||||
|
||||
final class EmailCount extends Constraint
|
||||
{
|
||||
private int $expectedValue;
|
||||
private ?string $transport;
|
||||
private bool $queued;
|
||||
|
||||
public function __construct(int $expectedValue, ?string $transport = null, bool $queued = false)
|
||||
{
|
||||
$this->expectedValue = $expectedValue;
|
||||
$this->transport = $transport;
|
||||
$this->queued = $queued;
|
||||
}
|
||||
|
||||
public function toString(): string
|
||||
{
|
||||
return sprintf('%shas %s "%d" emails', $this->transport ? $this->transport.' ' : '', $this->queued ? 'queued' : 'sent', $this->expectedValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param MessageEvents $events
|
||||
*/
|
||||
protected function matches($events): bool
|
||||
{
|
||||
return $this->expectedValue === $this->countEmails($events);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param MessageEvents $events
|
||||
*/
|
||||
protected function failureDescription($events): string
|
||||
{
|
||||
return sprintf('the Transport %s (%d %s)', $this->toString(), $this->countEmails($events), $this->queued ? 'queued' : 'sent');
|
||||
}
|
||||
|
||||
private function countEmails(MessageEvents $events): int
|
||||
{
|
||||
$count = 0;
|
||||
foreach ($events->getEvents($this->transport) as $event) {
|
||||
if (
|
||||
($this->queued && $event->isQueued())
|
||||
|| (!$this->queued && !$event->isQueued())
|
||||
) {
|
||||
++$count;
|
||||
}
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
}
|
39
vendor/symfony/mailer/Test/Constraint/EmailIsQueued.php
vendored
Normal file
39
vendor/symfony/mailer/Test/Constraint/EmailIsQueued.php
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Test\Constraint;
|
||||
|
||||
use PHPUnit\Framework\Constraint\Constraint;
|
||||
use Symfony\Component\Mailer\Event\MessageEvent;
|
||||
|
||||
final class EmailIsQueued extends Constraint
|
||||
{
|
||||
public function toString(): string
|
||||
{
|
||||
return 'is queued';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param MessageEvent $event
|
||||
*/
|
||||
protected function matches($event): bool
|
||||
{
|
||||
return $event->isQueued();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param MessageEvent $event
|
||||
*/
|
||||
protected function failureDescription($event): string
|
||||
{
|
||||
return 'the Email '.$this->toString();
|
||||
}
|
||||
}
|
117
vendor/symfony/mailer/Test/TransportFactoryTestCase.php
vendored
Normal file
117
vendor/symfony/mailer/Test/TransportFactoryTestCase.php
vendored
Normal file
@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Test;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Mailer\Exception\IncompleteDsnException;
|
||||
use Symfony\Component\Mailer\Exception\UnsupportedSchemeException;
|
||||
use Symfony\Component\Mailer\Transport\Dsn;
|
||||
use Symfony\Component\Mailer\Transport\TransportFactoryInterface;
|
||||
use Symfony\Component\Mailer\Transport\TransportInterface;
|
||||
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
|
||||
/**
|
||||
* A test case to ease testing Transport Factory.
|
||||
*
|
||||
* @author Konstantin Myakshin <molodchick@gmail.com>
|
||||
*/
|
||||
abstract class TransportFactoryTestCase extends TestCase
|
||||
{
|
||||
protected const USER = 'u$er';
|
||||
protected const PASSWORD = 'pa$s';
|
||||
|
||||
protected $dispatcher;
|
||||
protected $client;
|
||||
protected $logger;
|
||||
|
||||
abstract public function getFactory(): TransportFactoryInterface;
|
||||
|
||||
abstract public static function supportsProvider(): iterable;
|
||||
|
||||
abstract public static function createProvider(): iterable;
|
||||
|
||||
public static function unsupportedSchemeProvider(): iterable
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public static function incompleteDsnProvider(): iterable
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider supportsProvider
|
||||
*/
|
||||
public function testSupports(Dsn $dsn, bool $supports)
|
||||
{
|
||||
$factory = $this->getFactory();
|
||||
|
||||
$this->assertSame($supports, $factory->supports($dsn));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider createProvider
|
||||
*/
|
||||
public function testCreate(Dsn $dsn, TransportInterface $transport)
|
||||
{
|
||||
$factory = $this->getFactory();
|
||||
|
||||
$this->assertEquals($transport, $factory->create($dsn));
|
||||
if (str_contains('smtp', $dsn->getScheme())) {
|
||||
$this->assertStringMatchesFormat($dsn->getScheme().'://%S'.$dsn->getHost().'%S', (string) $transport);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider unsupportedSchemeProvider
|
||||
*/
|
||||
public function testUnsupportedSchemeException(Dsn $dsn, ?string $message = null)
|
||||
{
|
||||
$factory = $this->getFactory();
|
||||
|
||||
$this->expectException(UnsupportedSchemeException::class);
|
||||
if (null !== $message) {
|
||||
$this->expectExceptionMessage($message);
|
||||
}
|
||||
|
||||
$factory->create($dsn);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider incompleteDsnProvider
|
||||
*/
|
||||
public function testIncompleteDsnException(Dsn $dsn)
|
||||
{
|
||||
$factory = $this->getFactory();
|
||||
|
||||
$this->expectException(IncompleteDsnException::class);
|
||||
$factory->create($dsn);
|
||||
}
|
||||
|
||||
protected function getDispatcher(): EventDispatcherInterface
|
||||
{
|
||||
return $this->dispatcher ??= $this->createMock(EventDispatcherInterface::class);
|
||||
}
|
||||
|
||||
protected function getClient(): HttpClientInterface
|
||||
{
|
||||
return $this->client ??= $this->createMock(HttpClientInterface::class);
|
||||
}
|
||||
|
||||
protected function getLogger(): LoggerInterface
|
||||
{
|
||||
return $this->logger ??= $this->createMock(LoggerInterface::class);
|
||||
}
|
||||
}
|
188
vendor/symfony/mailer/Transport.php
vendored
Normal file
188
vendor/symfony/mailer/Transport.php
vendored
Normal file
@ -0,0 +1,188 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer;
|
||||
|
||||
use Psr\EventDispatcher\EventDispatcherInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Mailer\Bridge\Amazon\Transport\SesTransportFactory;
|
||||
use Symfony\Component\Mailer\Bridge\Brevo\Transport\BrevoTransportFactory;
|
||||
use Symfony\Component\Mailer\Bridge\Google\Transport\GmailTransportFactory;
|
||||
use Symfony\Component\Mailer\Bridge\Infobip\Transport\InfobipTransportFactory;
|
||||
use Symfony\Component\Mailer\Bridge\Mailchimp\Transport\MandrillTransportFactory;
|
||||
use Symfony\Component\Mailer\Bridge\MailerSend\Transport\MailerSendTransportFactory;
|
||||
use Symfony\Component\Mailer\Bridge\Mailgun\Transport\MailgunTransportFactory;
|
||||
use Symfony\Component\Mailer\Bridge\Mailjet\Transport\MailjetTransportFactory;
|
||||
use Symfony\Component\Mailer\Bridge\MailPace\Transport\MailPaceTransportFactory;
|
||||
use Symfony\Component\Mailer\Bridge\OhMySmtp\Transport\OhMySmtpTransportFactory;
|
||||
use Symfony\Component\Mailer\Bridge\Postmark\Transport\PostmarkTransportFactory;
|
||||
use Symfony\Component\Mailer\Bridge\Scaleway\Transport\ScalewayTransportFactory;
|
||||
use Symfony\Component\Mailer\Bridge\Sendgrid\Transport\SendgridTransportFactory;
|
||||
use Symfony\Component\Mailer\Bridge\Sendinblue\Transport\SendinblueTransportFactory;
|
||||
use Symfony\Component\Mailer\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Mailer\Exception\UnsupportedSchemeException;
|
||||
use Symfony\Component\Mailer\Transport\Dsn;
|
||||
use Symfony\Component\Mailer\Transport\FailoverTransport;
|
||||
use Symfony\Component\Mailer\Transport\NativeTransportFactory;
|
||||
use Symfony\Component\Mailer\Transport\NullTransportFactory;
|
||||
use Symfony\Component\Mailer\Transport\RoundRobinTransport;
|
||||
use Symfony\Component\Mailer\Transport\SendmailTransportFactory;
|
||||
use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransportFactory;
|
||||
use Symfony\Component\Mailer\Transport\TransportFactoryInterface;
|
||||
use Symfony\Component\Mailer\Transport\TransportInterface;
|
||||
use Symfony\Component\Mailer\Transport\Transports;
|
||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Konstantin Myakshin <molodchick@gmail.com>
|
||||
*/
|
||||
final class Transport
|
||||
{
|
||||
private const FACTORY_CLASSES = [
|
||||
BrevoTransportFactory::class,
|
||||
GmailTransportFactory::class,
|
||||
InfobipTransportFactory::class,
|
||||
MailerSendTransportFactory::class,
|
||||
MailgunTransportFactory::class,
|
||||
MailjetTransportFactory::class,
|
||||
MailPaceTransportFactory::class,
|
||||
MandrillTransportFactory::class,
|
||||
OhMySmtpTransportFactory::class,
|
||||
PostmarkTransportFactory::class,
|
||||
ScalewayTransportFactory::class,
|
||||
SendgridTransportFactory::class,
|
||||
SendinblueTransportFactory::class,
|
||||
SesTransportFactory::class,
|
||||
];
|
||||
|
||||
private iterable $factories;
|
||||
|
||||
public static function fromDsn(#[\SensitiveParameter] string $dsn, ?EventDispatcherInterface $dispatcher = null, ?HttpClientInterface $client = null, ?LoggerInterface $logger = null): TransportInterface
|
||||
{
|
||||
$factory = new self(iterator_to_array(self::getDefaultFactories($dispatcher, $client, $logger)));
|
||||
|
||||
return $factory->fromString($dsn);
|
||||
}
|
||||
|
||||
public static function fromDsns(#[\SensitiveParameter] array $dsns, ?EventDispatcherInterface $dispatcher = null, ?HttpClientInterface $client = null, ?LoggerInterface $logger = null): TransportInterface
|
||||
{
|
||||
$factory = new self(iterator_to_array(self::getDefaultFactories($dispatcher, $client, $logger)));
|
||||
|
||||
return $factory->fromStrings($dsns);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransportFactoryInterface[] $factories
|
||||
*/
|
||||
public function __construct(iterable $factories)
|
||||
{
|
||||
$this->factories = $factories;
|
||||
}
|
||||
|
||||
public function fromStrings(#[\SensitiveParameter] array $dsns): Transports
|
||||
{
|
||||
$transports = [];
|
||||
foreach ($dsns as $name => $dsn) {
|
||||
$transports[$name] = $this->fromString($dsn);
|
||||
}
|
||||
|
||||
return new Transports($transports);
|
||||
}
|
||||
|
||||
public function fromString(#[\SensitiveParameter] string $dsn): TransportInterface
|
||||
{
|
||||
[$transport, $offset] = $this->parseDsn($dsn);
|
||||
if ($offset !== \strlen($dsn)) {
|
||||
throw new InvalidArgumentException('The mailer DSN has some garbage at the end.');
|
||||
}
|
||||
|
||||
return $transport;
|
||||
}
|
||||
|
||||
private function parseDsn(#[\SensitiveParameter] string $dsn, int $offset = 0): array
|
||||
{
|
||||
static $keywords = [
|
||||
'failover' => FailoverTransport::class,
|
||||
'roundrobin' => RoundRobinTransport::class,
|
||||
];
|
||||
|
||||
while (true) {
|
||||
foreach ($keywords as $name => $class) {
|
||||
$name .= '(';
|
||||
if ($name === substr($dsn, $offset, \strlen($name))) {
|
||||
$offset += \strlen($name) - 1;
|
||||
preg_match('{\(([^()]|(?R))*\)}A', $dsn, $matches, 0, $offset);
|
||||
if (!isset($matches[0])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
++$offset;
|
||||
$args = [];
|
||||
while (true) {
|
||||
[$arg, $offset] = $this->parseDsn($dsn, $offset);
|
||||
$args[] = $arg;
|
||||
if (\strlen($dsn) === $offset) {
|
||||
break;
|
||||
}
|
||||
++$offset;
|
||||
if (')' === $dsn[$offset - 1]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return [new $class($args), $offset];
|
||||
}
|
||||
}
|
||||
|
||||
if (preg_match('{(\w+)\(}A', $dsn, $matches, 0, $offset)) {
|
||||
throw new InvalidArgumentException(sprintf('The "%s" keyword is not valid (valid ones are "%s"), ', $matches[1], implode('", "', array_keys($keywords))));
|
||||
}
|
||||
|
||||
if ($pos = strcspn($dsn, ' )', $offset)) {
|
||||
return [$this->fromDsnObject(Dsn::fromString(substr($dsn, $offset, $pos))), $offset + $pos];
|
||||
}
|
||||
|
||||
return [$this->fromDsnObject(Dsn::fromString(substr($dsn, $offset))), \strlen($dsn)];
|
||||
}
|
||||
}
|
||||
|
||||
public function fromDsnObject(Dsn $dsn): TransportInterface
|
||||
{
|
||||
foreach ($this->factories as $factory) {
|
||||
if ($factory->supports($dsn)) {
|
||||
return $factory->create($dsn);
|
||||
}
|
||||
}
|
||||
|
||||
throw new UnsupportedSchemeException($dsn);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Traversable<int, TransportFactoryInterface>
|
||||
*/
|
||||
public static function getDefaultFactories(?EventDispatcherInterface $dispatcher = null, ?HttpClientInterface $client = null, ?LoggerInterface $logger = null): \Traversable
|
||||
{
|
||||
foreach (self::FACTORY_CLASSES as $factoryClass) {
|
||||
if (class_exists($factoryClass)) {
|
||||
yield new $factoryClass($dispatcher, $client, $logger);
|
||||
}
|
||||
}
|
||||
|
||||
yield new NullTransportFactory($dispatcher, $client, $logger);
|
||||
|
||||
yield new SendmailTransportFactory($dispatcher, $client, $logger);
|
||||
|
||||
yield new EsmtpTransportFactory($dispatcher, $client, $logger);
|
||||
|
||||
yield new NativeTransportFactory($dispatcher, $client, $logger);
|
||||
}
|
||||
}
|
47
vendor/symfony/mailer/Transport/AbstractApiTransport.php
vendored
Normal file
47
vendor/symfony/mailer/Transport/AbstractApiTransport.php
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Transport;
|
||||
|
||||
use Symfony\Component\Mailer\Envelope;
|
||||
use Symfony\Component\Mailer\Exception\RuntimeException;
|
||||
use Symfony\Component\Mailer\SentMessage;
|
||||
use Symfony\Component\Mime\Address;
|
||||
use Symfony\Component\Mime\Email;
|
||||
use Symfony\Component\Mime\MessageConverter;
|
||||
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
abstract class AbstractApiTransport extends AbstractHttpTransport
|
||||
{
|
||||
abstract protected function doSendApi(SentMessage $sentMessage, Email $email, Envelope $envelope): ResponseInterface;
|
||||
|
||||
protected function doSendHttp(SentMessage $message): ResponseInterface
|
||||
{
|
||||
try {
|
||||
$email = MessageConverter::toEmail($message->getOriginalMessage());
|
||||
} catch (\Exception $e) {
|
||||
throw new RuntimeException(sprintf('Unable to send message with the "%s" transport: ', __CLASS__).$e->getMessage(), 0, $e);
|
||||
}
|
||||
|
||||
return $this->doSendApi($message, $email, $message->getEnvelope());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Address[]
|
||||
*/
|
||||
protected function getRecipients(Email $email, Envelope $envelope): array
|
||||
{
|
||||
return array_filter($envelope->getRecipients(), fn (Address $address) => false === \in_array($address, array_merge($email->getCc(), $email->getBcc()), true));
|
||||
}
|
||||
}
|
78
vendor/symfony/mailer/Transport/AbstractHttpTransport.php
vendored
Normal file
78
vendor/symfony/mailer/Transport/AbstractHttpTransport.php
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Transport;
|
||||
|
||||
use Psr\EventDispatcher\EventDispatcherInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\HttpClient\HttpClient;
|
||||
use Symfony\Component\Mailer\Exception\HttpTransportException;
|
||||
use Symfony\Component\Mailer\SentMessage;
|
||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||
|
||||
/**
|
||||
* @author Victor Bocharsky <victor@symfonycasts.com>
|
||||
*/
|
||||
abstract class AbstractHttpTransport extends AbstractTransport
|
||||
{
|
||||
protected $host;
|
||||
protected $port;
|
||||
protected $client;
|
||||
|
||||
public function __construct(?HttpClientInterface $client = null, ?EventDispatcherInterface $dispatcher = null, ?LoggerInterface $logger = null)
|
||||
{
|
||||
$this->client = $client;
|
||||
if (null === $client) {
|
||||
if (!class_exists(HttpClient::class)) {
|
||||
throw new \LogicException(sprintf('You cannot use "%s" as the HttpClient component is not installed. Try running "composer require symfony/http-client".', __CLASS__));
|
||||
}
|
||||
|
||||
$this->client = HttpClient::create();
|
||||
}
|
||||
|
||||
parent::__construct($dispatcher, $logger);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function setHost(?string $host): static
|
||||
{
|
||||
$this->host = $host;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function setPort(?int $port): static
|
||||
{
|
||||
$this->port = $port;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
abstract protected function doSendHttp(SentMessage $message): ResponseInterface;
|
||||
|
||||
protected function doSend(SentMessage $message): void
|
||||
{
|
||||
try {
|
||||
$response = $this->doSendHttp($message);
|
||||
$message->appendDebug($response->getInfo('debug') ?? '');
|
||||
} catch (HttpTransportException $e) {
|
||||
$e->appendDebug($e->getResponse()->getInfo('debug') ?? '');
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
136
vendor/symfony/mailer/Transport/AbstractTransport.php
vendored
Normal file
136
vendor/symfony/mailer/Transport/AbstractTransport.php
vendored
Normal file
@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Transport;
|
||||
|
||||
use Psr\EventDispatcher\EventDispatcherInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\NullLogger;
|
||||
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
|
||||
use Symfony\Component\Mailer\Envelope;
|
||||
use Symfony\Component\Mailer\Event\FailedMessageEvent;
|
||||
use Symfony\Component\Mailer\Event\MessageEvent;
|
||||
use Symfony\Component\Mailer\Event\SentMessageEvent;
|
||||
use Symfony\Component\Mailer\Exception\LogicException;
|
||||
use Symfony\Component\Mailer\SentMessage;
|
||||
use Symfony\Component\Mime\Address;
|
||||
use Symfony\Component\Mime\BodyRendererInterface;
|
||||
use Symfony\Component\Mime\RawMessage;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
abstract class AbstractTransport implements TransportInterface
|
||||
{
|
||||
private ?EventDispatcherInterface $dispatcher;
|
||||
private LoggerInterface $logger;
|
||||
private float $rate = 0;
|
||||
private float $lastSent = 0;
|
||||
|
||||
public function __construct(?EventDispatcherInterface $dispatcher = null, ?LoggerInterface $logger = null)
|
||||
{
|
||||
$this->dispatcher = $dispatcher;
|
||||
$this->logger = $logger ?? new NullLogger();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum number of messages to send per second (0 to disable).
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setMaxPerSecond(float $rate): static
|
||||
{
|
||||
if (0 >= $rate) {
|
||||
$rate = 0;
|
||||
}
|
||||
|
||||
$this->rate = $rate;
|
||||
$this->lastSent = 0;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function send(RawMessage $message, ?Envelope $envelope = null): ?SentMessage
|
||||
{
|
||||
$message = clone $message;
|
||||
$envelope = null !== $envelope ? clone $envelope : Envelope::create($message);
|
||||
|
||||
try {
|
||||
if (!$this->dispatcher) {
|
||||
$sentMessage = new SentMessage($message, $envelope);
|
||||
$this->doSend($sentMessage);
|
||||
|
||||
return $sentMessage;
|
||||
}
|
||||
|
||||
$event = new MessageEvent($message, $envelope, (string) $this);
|
||||
$this->dispatcher->dispatch($event);
|
||||
if ($event->isRejected()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$envelope = $event->getEnvelope();
|
||||
$message = $event->getMessage();
|
||||
|
||||
if ($message instanceof TemplatedEmail && !$message->isRendered()) {
|
||||
throw new LogicException(sprintf('You must configure a "%s" when a "%s" instance has a text or HTML template set.', BodyRendererInterface::class, get_debug_type($message)));
|
||||
}
|
||||
|
||||
$sentMessage = new SentMessage($message, $envelope);
|
||||
|
||||
try {
|
||||
$this->doSend($sentMessage);
|
||||
} catch (\Throwable $error) {
|
||||
$this->dispatcher->dispatch(new FailedMessageEvent($message, $error));
|
||||
$this->checkThrottling();
|
||||
|
||||
throw $error;
|
||||
}
|
||||
|
||||
$this->dispatcher->dispatch(new SentMessageEvent($sentMessage));
|
||||
|
||||
return $sentMessage;
|
||||
} finally {
|
||||
$this->checkThrottling();
|
||||
}
|
||||
}
|
||||
|
||||
abstract protected function doSend(SentMessage $message): void;
|
||||
|
||||
/**
|
||||
* @param Address[] $addresses
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected function stringifyAddresses(array $addresses): array
|
||||
{
|
||||
return array_map(fn (Address $a) => $a->toString(), $addresses);
|
||||
}
|
||||
|
||||
protected function getLogger(): LoggerInterface
|
||||
{
|
||||
return $this->logger;
|
||||
}
|
||||
|
||||
private function checkThrottling(): void
|
||||
{
|
||||
if (0 == $this->rate) {
|
||||
return;
|
||||
}
|
||||
|
||||
$sleep = (1 / $this->rate) - (microtime(true) - $this->lastSent);
|
||||
if (0 < $sleep) {
|
||||
$this->logger->debug(sprintf('Email transport "%s" sleeps for %.2f seconds', __CLASS__, $sleep));
|
||||
usleep((int) ($sleep * 1000000));
|
||||
}
|
||||
$this->lastSent = microtime(true);
|
||||
}
|
||||
}
|
51
vendor/symfony/mailer/Transport/AbstractTransportFactory.php
vendored
Normal file
51
vendor/symfony/mailer/Transport/AbstractTransportFactory.php
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Transport;
|
||||
|
||||
use Psr\EventDispatcher\EventDispatcherInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Mailer\Exception\IncompleteDsnException;
|
||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
|
||||
/**
|
||||
* @author Konstantin Myakshin <molodchick@gmail.com>
|
||||
*/
|
||||
abstract class AbstractTransportFactory implements TransportFactoryInterface
|
||||
{
|
||||
protected $dispatcher;
|
||||
protected $client;
|
||||
protected $logger;
|
||||
|
||||
public function __construct(?EventDispatcherInterface $dispatcher = null, ?HttpClientInterface $client = null, ?LoggerInterface $logger = null)
|
||||
{
|
||||
$this->dispatcher = $dispatcher;
|
||||
$this->client = $client;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
public function supports(Dsn $dsn): bool
|
||||
{
|
||||
return \in_array($dsn->getScheme(), $this->getSupportedSchemes());
|
||||
}
|
||||
|
||||
abstract protected function getSupportedSchemes(): array;
|
||||
|
||||
protected function getUser(Dsn $dsn): string
|
||||
{
|
||||
return $dsn->getUser() ?? throw new IncompleteDsnException('User is not set.');
|
||||
}
|
||||
|
||||
protected function getPassword(Dsn $dsn): string
|
||||
{
|
||||
return $dsn->getPassword() ?? throw new IncompleteDsnException('Password is not set.');
|
||||
}
|
||||
}
|
89
vendor/symfony/mailer/Transport/Dsn.php
vendored
Normal file
89
vendor/symfony/mailer/Transport/Dsn.php
vendored
Normal file
@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Transport;
|
||||
|
||||
use Symfony\Component\Mailer\Exception\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* @author Konstantin Myakshin <molodchick@gmail.com>
|
||||
*/
|
||||
final class Dsn
|
||||
{
|
||||
private string $scheme;
|
||||
private string $host;
|
||||
private ?string $user;
|
||||
private ?string $password;
|
||||
private ?int $port;
|
||||
private array $options;
|
||||
|
||||
public function __construct(string $scheme, string $host, ?string $user = null, #[\SensitiveParameter] ?string $password = null, ?int $port = null, array $options = [])
|
||||
{
|
||||
$this->scheme = $scheme;
|
||||
$this->host = $host;
|
||||
$this->user = $user;
|
||||
$this->password = $password;
|
||||
$this->port = $port;
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
public static function fromString(#[\SensitiveParameter] string $dsn): self
|
||||
{
|
||||
if (false === $params = parse_url($dsn)) {
|
||||
throw new InvalidArgumentException('The mailer DSN is invalid.');
|
||||
}
|
||||
|
||||
if (!isset($params['scheme'])) {
|
||||
throw new InvalidArgumentException('The mailer DSN must contain a scheme.');
|
||||
}
|
||||
|
||||
if (!isset($params['host'])) {
|
||||
throw new InvalidArgumentException('The mailer DSN must contain a host (use "default" by default).');
|
||||
}
|
||||
|
||||
$user = '' !== ($params['user'] ?? '') ? rawurldecode($params['user']) : null;
|
||||
$password = '' !== ($params['pass'] ?? '') ? rawurldecode($params['pass']) : null;
|
||||
$port = $params['port'] ?? null;
|
||||
parse_str($params['query'] ?? '', $query);
|
||||
|
||||
return new self($params['scheme'], $params['host'], $user, $password, $port, $query);
|
||||
}
|
||||
|
||||
public function getScheme(): string
|
||||
{
|
||||
return $this->scheme;
|
||||
}
|
||||
|
||||
public function getHost(): string
|
||||
{
|
||||
return $this->host;
|
||||
}
|
||||
|
||||
public function getUser(): ?string
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
public function getPassword(): ?string
|
||||
{
|
||||
return $this->password;
|
||||
}
|
||||
|
||||
public function getPort(?int $default = null): ?int
|
||||
{
|
||||
return $this->port ?? $default;
|
||||
}
|
||||
|
||||
public function getOption(string $key, mixed $default = null): mixed
|
||||
{
|
||||
return $this->options[$key] ?? $default;
|
||||
}
|
||||
}
|
41
vendor/symfony/mailer/Transport/FailoverTransport.php
vendored
Normal file
41
vendor/symfony/mailer/Transport/FailoverTransport.php
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Transport;
|
||||
|
||||
/**
|
||||
* Uses several Transports using a failover algorithm.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class FailoverTransport extends RoundRobinTransport
|
||||
{
|
||||
private ?TransportInterface $currentTransport = null;
|
||||
|
||||
protected function getNextTransport(): ?TransportInterface
|
||||
{
|
||||
if (null === $this->currentTransport || $this->isTransportDead($this->currentTransport)) {
|
||||
$this->currentTransport = parent::getNextTransport();
|
||||
}
|
||||
|
||||
return $this->currentTransport;
|
||||
}
|
||||
|
||||
protected function getInitialCursor(): int
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected function getNameSymbol(): string
|
||||
{
|
||||
return 'failover';
|
||||
}
|
||||
}
|
63
vendor/symfony/mailer/Transport/NativeTransportFactory.php
vendored
Normal file
63
vendor/symfony/mailer/Transport/NativeTransportFactory.php
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Transport;
|
||||
|
||||
use Symfony\Component\Mailer\Exception\TransportException;
|
||||
use Symfony\Component\Mailer\Exception\UnsupportedSchemeException;
|
||||
use Symfony\Component\Mailer\Transport\Smtp\SmtpTransport;
|
||||
use Symfony\Component\Mailer\Transport\Smtp\Stream\SocketStream;
|
||||
|
||||
/**
|
||||
* Factory that configures a transport (sendmail or SMTP) based on php.ini settings.
|
||||
*
|
||||
* @author Laurent VOULLEMIER <laurent.voullemier@gmail.com>
|
||||
*/
|
||||
final class NativeTransportFactory extends AbstractTransportFactory
|
||||
{
|
||||
public function create(Dsn $dsn): TransportInterface
|
||||
{
|
||||
if (!\in_array($dsn->getScheme(), $this->getSupportedSchemes(), true)) {
|
||||
throw new UnsupportedSchemeException($dsn, 'native', $this->getSupportedSchemes());
|
||||
}
|
||||
|
||||
if ($sendMailPath = ini_get('sendmail_path')) {
|
||||
return new SendmailTransport($sendMailPath, $this->dispatcher, $this->logger);
|
||||
}
|
||||
|
||||
if ('\\' !== \DIRECTORY_SEPARATOR) {
|
||||
throw new TransportException('sendmail_path is not configured in php.ini.');
|
||||
}
|
||||
|
||||
// Only for windows hosts; at this point non-windows
|
||||
// host have already thrown an exception or returned a transport
|
||||
$host = ini_get('SMTP');
|
||||
$port = (int) ini_get('smtp_port');
|
||||
|
||||
if (!$host || !$port) {
|
||||
throw new TransportException('smtp or smtp_port is not configured in php.ini.');
|
||||
}
|
||||
|
||||
$socketStream = new SocketStream();
|
||||
$socketStream->setHost($host);
|
||||
$socketStream->setPort($port);
|
||||
if (465 !== $port) {
|
||||
$socketStream->disableTls();
|
||||
}
|
||||
|
||||
return new SmtpTransport($socketStream, $this->dispatcher, $this->logger);
|
||||
}
|
||||
|
||||
protected function getSupportedSchemes(): array
|
||||
{
|
||||
return ['native'];
|
||||
}
|
||||
}
|
31
vendor/symfony/mailer/Transport/NullTransport.php
vendored
Normal file
31
vendor/symfony/mailer/Transport/NullTransport.php
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Transport;
|
||||
|
||||
use Symfony\Component\Mailer\SentMessage;
|
||||
|
||||
/**
|
||||
* Pretends messages have been sent, but just ignores them.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
final class NullTransport extends AbstractTransport
|
||||
{
|
||||
protected function doSend(SentMessage $message): void
|
||||
{
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return 'null://';
|
||||
}
|
||||
}
|
34
vendor/symfony/mailer/Transport/NullTransportFactory.php
vendored
Normal file
34
vendor/symfony/mailer/Transport/NullTransportFactory.php
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Transport;
|
||||
|
||||
use Symfony\Component\Mailer\Exception\UnsupportedSchemeException;
|
||||
|
||||
/**
|
||||
* @author Konstantin Myakshin <molodchick@gmail.com>
|
||||
*/
|
||||
final class NullTransportFactory extends AbstractTransportFactory
|
||||
{
|
||||
public function create(Dsn $dsn): TransportInterface
|
||||
{
|
||||
if ('null' === $dsn->getScheme()) {
|
||||
return new NullTransport($this->dispatcher, $this->logger);
|
||||
}
|
||||
|
||||
throw new UnsupportedSchemeException($dsn, 'null', $this->getSupportedSchemes());
|
||||
}
|
||||
|
||||
protected function getSupportedSchemes(): array
|
||||
{
|
||||
return ['null'];
|
||||
}
|
||||
}
|
125
vendor/symfony/mailer/Transport/RoundRobinTransport.php
vendored
Normal file
125
vendor/symfony/mailer/Transport/RoundRobinTransport.php
vendored
Normal file
@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Transport;
|
||||
|
||||
use Symfony\Component\Mailer\Envelope;
|
||||
use Symfony\Component\Mailer\Exception\TransportException;
|
||||
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
|
||||
use Symfony\Component\Mailer\SentMessage;
|
||||
use Symfony\Component\Mime\RawMessage;
|
||||
|
||||
/**
|
||||
* Uses several Transports using a round robin algorithm.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class RoundRobinTransport implements TransportInterface
|
||||
{
|
||||
/**
|
||||
* @var \SplObjectStorage<TransportInterface, float>
|
||||
*/
|
||||
private \SplObjectStorage $deadTransports;
|
||||
private array $transports = [];
|
||||
private int $retryPeriod;
|
||||
private int $cursor = -1;
|
||||
|
||||
/**
|
||||
* @param TransportInterface[] $transports
|
||||
*/
|
||||
public function __construct(array $transports, int $retryPeriod = 60)
|
||||
{
|
||||
if (!$transports) {
|
||||
throw new TransportException(sprintf('"%s" must have at least one transport configured.', static::class));
|
||||
}
|
||||
|
||||
$this->transports = $transports;
|
||||
$this->deadTransports = new \SplObjectStorage();
|
||||
$this->retryPeriod = $retryPeriod;
|
||||
}
|
||||
|
||||
public function send(RawMessage $message, ?Envelope $envelope = null): ?SentMessage
|
||||
{
|
||||
$exception = null;
|
||||
|
||||
while ($transport = $this->getNextTransport()) {
|
||||
try {
|
||||
return $transport->send($message, $envelope);
|
||||
} catch (TransportExceptionInterface $e) {
|
||||
$exception ??= new TransportException('All transports failed.');
|
||||
$exception->appendDebug(sprintf("Transport \"%s\": %s\n", $transport, $e->getDebug()));
|
||||
$this->deadTransports[$transport] = microtime(true);
|
||||
}
|
||||
}
|
||||
|
||||
throw $exception ?? new TransportException('No transports found.');
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->getNameSymbol().'('.implode(' ', array_map('strval', $this->transports)).')';
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates the transport list around and returns the first instance.
|
||||
*/
|
||||
protected function getNextTransport(): ?TransportInterface
|
||||
{
|
||||
if (-1 === $this->cursor) {
|
||||
$this->cursor = $this->getInitialCursor();
|
||||
}
|
||||
|
||||
$cursor = $this->cursor;
|
||||
while (true) {
|
||||
$transport = $this->transports[$cursor];
|
||||
|
||||
if (!$this->isTransportDead($transport)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ((microtime(true) - $this->deadTransports[$transport]) > $this->retryPeriod) {
|
||||
$this->deadTransports->detach($transport);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if ($this->cursor === $cursor = $this->moveCursor($cursor)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
$this->cursor = $this->moveCursor($cursor);
|
||||
|
||||
return $transport;
|
||||
}
|
||||
|
||||
protected function isTransportDead(TransportInterface $transport): bool
|
||||
{
|
||||
return $this->deadTransports->contains($transport);
|
||||
}
|
||||
|
||||
protected function getInitialCursor(): int
|
||||
{
|
||||
// the cursor initial value is randomized so that
|
||||
// when are not in a daemon, we are still rotating the transports
|
||||
return mt_rand(0, \count($this->transports) - 1);
|
||||
}
|
||||
|
||||
protected function getNameSymbol(): string
|
||||
{
|
||||
return 'roundrobin';
|
||||
}
|
||||
|
||||
private function moveCursor(int $cursor): int
|
||||
{
|
||||
return ++$cursor >= \count($this->transports) ? 0 : $cursor;
|
||||
}
|
||||
}
|
123
vendor/symfony/mailer/Transport/SendmailTransport.php
vendored
Normal file
123
vendor/symfony/mailer/Transport/SendmailTransport.php
vendored
Normal file
@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Transport;
|
||||
|
||||
use Psr\EventDispatcher\EventDispatcherInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Mailer\Envelope;
|
||||
use Symfony\Component\Mailer\SentMessage;
|
||||
use Symfony\Component\Mailer\Transport\Smtp\SmtpTransport;
|
||||
use Symfony\Component\Mailer\Transport\Smtp\Stream\AbstractStream;
|
||||
use Symfony\Component\Mailer\Transport\Smtp\Stream\ProcessStream;
|
||||
use Symfony\Component\Mime\RawMessage;
|
||||
|
||||
/**
|
||||
* SendmailTransport for sending mail through a Sendmail/Postfix (etc..) binary.
|
||||
*
|
||||
* Transport can be instantiated through SendmailTransportFactory or NativeTransportFactory:
|
||||
*
|
||||
* - SendmailTransportFactory to use most common sendmail path and recommended options
|
||||
* - NativeTransportFactory when configuration is set via php.ini
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Chris Corbyn
|
||||
*/
|
||||
class SendmailTransport extends AbstractTransport
|
||||
{
|
||||
private string $command = '/usr/sbin/sendmail -bs';
|
||||
private ProcessStream $stream;
|
||||
private ?SmtpTransport $transport = null;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* Supported modes are -bs and -t, with any additional flags desired.
|
||||
*
|
||||
* The recommended mode is "-bs" since it is interactive and failure notifications are hence possible.
|
||||
* Note that the -t mode does not support error reporting and does not support Bcc properly (the Bcc headers are not removed).
|
||||
*
|
||||
* If using -t mode, you are strongly advised to include -oi or -i in the flags (like /usr/sbin/sendmail -oi -t)
|
||||
*
|
||||
* -f<sender> flag will be appended automatically if one is not present.
|
||||
*/
|
||||
public function __construct(?string $command = null, ?EventDispatcherInterface $dispatcher = null, ?LoggerInterface $logger = null)
|
||||
{
|
||||
parent::__construct($dispatcher, $logger);
|
||||
|
||||
if (null !== $command) {
|
||||
if (!str_contains($command, ' -bs') && !str_contains($command, ' -t')) {
|
||||
throw new \InvalidArgumentException(sprintf('Unsupported sendmail command flags "%s"; must be one of "-bs" or "-t" but can include additional flags.', $command));
|
||||
}
|
||||
|
||||
$this->command = $command;
|
||||
}
|
||||
|
||||
$this->stream = new ProcessStream();
|
||||
if (str_contains($this->command, ' -bs')) {
|
||||
$this->stream->setCommand($this->command);
|
||||
$this->transport = new SmtpTransport($this->stream, $dispatcher, $logger);
|
||||
}
|
||||
}
|
||||
|
||||
public function send(RawMessage $message, ?Envelope $envelope = null): ?SentMessage
|
||||
{
|
||||
if ($this->transport) {
|
||||
return $this->transport->send($message, $envelope);
|
||||
}
|
||||
|
||||
return parent::send($message, $envelope);
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
if ($this->transport) {
|
||||
return (string) $this->transport;
|
||||
}
|
||||
|
||||
return 'smtp://sendmail';
|
||||
}
|
||||
|
||||
protected function doSend(SentMessage $message): void
|
||||
{
|
||||
$this->getLogger()->debug(sprintf('Email transport "%s" starting', __CLASS__));
|
||||
|
||||
$command = $this->command;
|
||||
|
||||
if ($recipients = $message->getEnvelope()->getRecipients()) {
|
||||
$command = str_replace(' -t', '', $command);
|
||||
}
|
||||
|
||||
if (!str_contains($command, ' -f')) {
|
||||
$command .= ' -f'.escapeshellarg($message->getEnvelope()->getSender()->getEncodedAddress());
|
||||
}
|
||||
|
||||
$chunks = AbstractStream::replace("\r\n", "\n", $message->toIterable());
|
||||
|
||||
if (!str_contains($command, ' -i') && !str_contains($command, ' -oi')) {
|
||||
$chunks = AbstractStream::replace("\n.", "\n..", $chunks);
|
||||
}
|
||||
|
||||
foreach ($recipients as $recipient) {
|
||||
$command .= ' '.escapeshellarg($recipient->getEncodedAddress());
|
||||
}
|
||||
|
||||
$this->stream->setCommand($command);
|
||||
$this->stream->initialize();
|
||||
foreach ($chunks as $chunk) {
|
||||
$this->stream->write($chunk);
|
||||
}
|
||||
$this->stream->flush();
|
||||
$this->stream->terminate();
|
||||
|
||||
$this->getLogger()->debug(sprintf('Email transport "%s" stopped', __CLASS__));
|
||||
}
|
||||
}
|
34
vendor/symfony/mailer/Transport/SendmailTransportFactory.php
vendored
Normal file
34
vendor/symfony/mailer/Transport/SendmailTransportFactory.php
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Transport;
|
||||
|
||||
use Symfony\Component\Mailer\Exception\UnsupportedSchemeException;
|
||||
|
||||
/**
|
||||
* @author Konstantin Myakshin <molodchick@gmail.com>
|
||||
*/
|
||||
final class SendmailTransportFactory extends AbstractTransportFactory
|
||||
{
|
||||
public function create(Dsn $dsn): TransportInterface
|
||||
{
|
||||
if ('sendmail+smtp' === $dsn->getScheme() || 'sendmail' === $dsn->getScheme()) {
|
||||
return new SendmailTransport($dsn->getOption('command'), $this->dispatcher, $this->logger);
|
||||
}
|
||||
|
||||
throw new UnsupportedSchemeException($dsn, 'sendmail', $this->getSupportedSchemes());
|
||||
}
|
||||
|
||||
protected function getSupportedSchemes(): array
|
||||
{
|
||||
return ['sendmail', 'sendmail+smtp'];
|
||||
}
|
||||
}
|
35
vendor/symfony/mailer/Transport/Smtp/Auth/AuthenticatorInterface.php
vendored
Normal file
35
vendor/symfony/mailer/Transport/Smtp/Auth/AuthenticatorInterface.php
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Transport\Smtp\Auth;
|
||||
|
||||
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
|
||||
use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport;
|
||||
|
||||
/**
|
||||
* An Authentication mechanism.
|
||||
*
|
||||
* @author Chris Corbyn
|
||||
*/
|
||||
interface AuthenticatorInterface
|
||||
{
|
||||
/**
|
||||
* Tries to authenticate the user.
|
||||
*
|
||||
* @throws TransportExceptionInterface
|
||||
*/
|
||||
public function authenticate(EsmtpTransport $client): void;
|
||||
|
||||
/**
|
||||
* Gets the name of the AUTH mechanism this Authenticator handles.
|
||||
*/
|
||||
public function getAuthKeyword(): string;
|
||||
}
|
65
vendor/symfony/mailer/Transport/Smtp/Auth/CramMd5Authenticator.php
vendored
Normal file
65
vendor/symfony/mailer/Transport/Smtp/Auth/CramMd5Authenticator.php
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Transport\Smtp\Auth;
|
||||
|
||||
use Symfony\Component\Mailer\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport;
|
||||
|
||||
/**
|
||||
* Handles CRAM-MD5 authentication.
|
||||
*
|
||||
* @author Chris Corbyn
|
||||
*/
|
||||
class CramMd5Authenticator implements AuthenticatorInterface
|
||||
{
|
||||
public function getAuthKeyword(): string
|
||||
{
|
||||
return 'CRAM-MD5';
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://www.ietf.org/rfc/rfc4954.txt
|
||||
*/
|
||||
public function authenticate(EsmtpTransport $client): void
|
||||
{
|
||||
$challenge = $client->executeCommand("AUTH CRAM-MD5\r\n", [334]);
|
||||
$challenge = base64_decode(substr($challenge, 4));
|
||||
$message = base64_encode($client->getUsername().' '.$this->getResponse($client->getPassword(), $challenge));
|
||||
$client->executeCommand(sprintf("%s\r\n", $message), [235]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a CRAM-MD5 response from a server challenge.
|
||||
*/
|
||||
private function getResponse(#[\SensitiveParameter] string $secret, string $challenge): string
|
||||
{
|
||||
if (!$secret) {
|
||||
throw new InvalidArgumentException('A non-empty secret is required.');
|
||||
}
|
||||
|
||||
if (\strlen($secret) > 64) {
|
||||
$secret = pack('H32', md5($secret));
|
||||
}
|
||||
|
||||
if (\strlen($secret) < 64) {
|
||||
$secret = str_pad($secret, 64, \chr(0));
|
||||
}
|
||||
|
||||
$kipad = substr($secret, 0, 64) ^ str_repeat(\chr(0x36), 64);
|
||||
$kopad = substr($secret, 0, 64) ^ str_repeat(\chr(0x5C), 64);
|
||||
|
||||
$inner = pack('H32', md5($kipad.$challenge));
|
||||
$digest = md5($kopad.$inner);
|
||||
|
||||
return $digest;
|
||||
}
|
||||
}
|
37
vendor/symfony/mailer/Transport/Smtp/Auth/LoginAuthenticator.php
vendored
Normal file
37
vendor/symfony/mailer/Transport/Smtp/Auth/LoginAuthenticator.php
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Transport\Smtp\Auth;
|
||||
|
||||
use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport;
|
||||
|
||||
/**
|
||||
* Handles LOGIN authentication.
|
||||
*
|
||||
* @author Chris Corbyn
|
||||
*/
|
||||
class LoginAuthenticator implements AuthenticatorInterface
|
||||
{
|
||||
public function getAuthKeyword(): string
|
||||
{
|
||||
return 'LOGIN';
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://www.ietf.org/rfc/rfc4954.txt
|
||||
*/
|
||||
public function authenticate(EsmtpTransport $client): void
|
||||
{
|
||||
$client->executeCommand("AUTH LOGIN\r\n", [334]);
|
||||
$client->executeCommand(sprintf("%s\r\n", base64_encode($client->getUsername())), [334]);
|
||||
$client->executeCommand(sprintf("%s\r\n", base64_encode($client->getPassword())), [235]);
|
||||
}
|
||||
}
|
35
vendor/symfony/mailer/Transport/Smtp/Auth/PlainAuthenticator.php
vendored
Normal file
35
vendor/symfony/mailer/Transport/Smtp/Auth/PlainAuthenticator.php
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Transport\Smtp\Auth;
|
||||
|
||||
use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport;
|
||||
|
||||
/**
|
||||
* Handles PLAIN authentication.
|
||||
*
|
||||
* @author Chris Corbyn
|
||||
*/
|
||||
class PlainAuthenticator implements AuthenticatorInterface
|
||||
{
|
||||
public function getAuthKeyword(): string
|
||||
{
|
||||
return 'PLAIN';
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://www.ietf.org/rfc/rfc4954.txt
|
||||
*/
|
||||
public function authenticate(EsmtpTransport $client): void
|
||||
{
|
||||
$client->executeCommand(sprintf("AUTH PLAIN %s\r\n", base64_encode($client->getUsername().\chr(0).$client->getUsername().\chr(0).$client->getPassword())), [235]);
|
||||
}
|
||||
}
|
37
vendor/symfony/mailer/Transport/Smtp/Auth/XOAuth2Authenticator.php
vendored
Normal file
37
vendor/symfony/mailer/Transport/Smtp/Auth/XOAuth2Authenticator.php
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Transport\Smtp\Auth;
|
||||
|
||||
use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport;
|
||||
|
||||
/**
|
||||
* Handles XOAUTH2 authentication.
|
||||
*
|
||||
* @author xu.li<AthenaLightenedMyPath@gmail.com>
|
||||
*
|
||||
* @see https://developers.google.com/google-apps/gmail/xoauth2_protocol
|
||||
*/
|
||||
class XOAuth2Authenticator implements AuthenticatorInterface
|
||||
{
|
||||
public function getAuthKeyword(): string
|
||||
{
|
||||
return 'XOAUTH2';
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://developers.google.com/google-apps/gmail/xoauth2_protocol#the_sasl_xoauth2_mechanism
|
||||
*/
|
||||
public function authenticate(EsmtpTransport $client): void
|
||||
{
|
||||
$client->executeCommand('AUTH XOAUTH2 '.base64_encode('user='.$client->getUsername()."\1auth=Bearer ".$client->getPassword()."\1\1")."\r\n", [235]);
|
||||
}
|
||||
}
|
228
vendor/symfony/mailer/Transport/Smtp/EsmtpTransport.php
vendored
Normal file
228
vendor/symfony/mailer/Transport/Smtp/EsmtpTransport.php
vendored
Normal file
@ -0,0 +1,228 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Transport\Smtp;
|
||||
|
||||
use Psr\EventDispatcher\EventDispatcherInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Mailer\Exception\TransportException;
|
||||
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
|
||||
use Symfony\Component\Mailer\Exception\UnexpectedResponseException;
|
||||
use Symfony\Component\Mailer\Transport\Smtp\Auth\AuthenticatorInterface;
|
||||
use Symfony\Component\Mailer\Transport\Smtp\Stream\AbstractStream;
|
||||
use Symfony\Component\Mailer\Transport\Smtp\Stream\SocketStream;
|
||||
|
||||
/**
|
||||
* Sends Emails over SMTP with ESMTP support.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Chris Corbyn
|
||||
*/
|
||||
class EsmtpTransport extends SmtpTransport
|
||||
{
|
||||
private array $authenticators = [];
|
||||
private string $username = '';
|
||||
private string $password = '';
|
||||
private array $capabilities;
|
||||
|
||||
public function __construct(string $host = 'localhost', int $port = 0, ?bool $tls = null, ?EventDispatcherInterface $dispatcher = null, ?LoggerInterface $logger = null, ?AbstractStream $stream = null, ?array $authenticators = null)
|
||||
{
|
||||
parent::__construct($stream, $dispatcher, $logger);
|
||||
|
||||
if (null === $authenticators) {
|
||||
// fallback to default authenticators
|
||||
// order is important here (roughly most secure and popular first)
|
||||
$authenticators = [
|
||||
new Auth\CramMd5Authenticator(),
|
||||
new Auth\LoginAuthenticator(),
|
||||
new Auth\PlainAuthenticator(),
|
||||
new Auth\XOAuth2Authenticator(),
|
||||
];
|
||||
}
|
||||
$this->setAuthenticators($authenticators);
|
||||
|
||||
/** @var SocketStream $stream */
|
||||
$stream = $this->getStream();
|
||||
|
||||
if (null === $tls) {
|
||||
if (465 === $port) {
|
||||
$tls = true;
|
||||
} else {
|
||||
$tls = \defined('OPENSSL_VERSION_NUMBER') && 0 === $port && 'localhost' !== $host;
|
||||
}
|
||||
}
|
||||
if (!$tls) {
|
||||
$stream->disableTls();
|
||||
}
|
||||
if (0 === $port) {
|
||||
$port = $tls ? 465 : 25;
|
||||
}
|
||||
|
||||
$stream->setHost($host);
|
||||
$stream->setPort($port);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function setUsername(string $username): static
|
||||
{
|
||||
$this->username = $username;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUsername(): string
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function setPassword(#[\SensitiveParameter] string $password): static
|
||||
{
|
||||
$this->password = $password;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPassword(): string
|
||||
{
|
||||
return $this->password;
|
||||
}
|
||||
|
||||
public function setAuthenticators(array $authenticators): void
|
||||
{
|
||||
$this->authenticators = [];
|
||||
foreach ($authenticators as $authenticator) {
|
||||
$this->addAuthenticator($authenticator);
|
||||
}
|
||||
}
|
||||
|
||||
public function addAuthenticator(AuthenticatorInterface $authenticator): void
|
||||
{
|
||||
$this->authenticators[] = $authenticator;
|
||||
}
|
||||
|
||||
public function executeCommand(string $command, array $codes): string
|
||||
{
|
||||
return [250] === $codes && str_starts_with($command, 'HELO ') ? $this->doEhloCommand() : parent::executeCommand($command, $codes);
|
||||
}
|
||||
|
||||
final protected function getCapabilities(): array
|
||||
{
|
||||
return $this->capabilities;
|
||||
}
|
||||
|
||||
private function doEhloCommand(): string
|
||||
{
|
||||
try {
|
||||
$response = $this->executeCommand(sprintf("EHLO %s\r\n", $this->getLocalDomain()), [250]);
|
||||
} catch (TransportExceptionInterface $e) {
|
||||
try {
|
||||
return parent::executeCommand(sprintf("HELO %s\r\n", $this->getLocalDomain()), [250]);
|
||||
} catch (TransportExceptionInterface $ex) {
|
||||
if (!$ex->getCode()) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
throw $ex;
|
||||
}
|
||||
}
|
||||
|
||||
$this->capabilities = $this->parseCapabilities($response);
|
||||
|
||||
/** @var SocketStream $stream */
|
||||
$stream = $this->getStream();
|
||||
// WARNING: !$stream->isTLS() is right, 100% sure :)
|
||||
// if you think that the ! should be removed, read the code again
|
||||
// if doing so "fixes" your issue then it probably means your SMTP server behaves incorrectly or is wrongly configured
|
||||
if (!$stream->isTLS() && \defined('OPENSSL_VERSION_NUMBER') && \array_key_exists('STARTTLS', $this->capabilities)) {
|
||||
$this->executeCommand("STARTTLS\r\n", [220]);
|
||||
|
||||
if (!$stream->startTLS()) {
|
||||
throw new TransportException('Unable to connect with STARTTLS.');
|
||||
}
|
||||
|
||||
$response = $this->executeCommand(sprintf("EHLO %s\r\n", $this->getLocalDomain()), [250]);
|
||||
$this->capabilities = $this->parseCapabilities($response);
|
||||
}
|
||||
|
||||
if (\array_key_exists('AUTH', $this->capabilities)) {
|
||||
$this->handleAuth($this->capabilities['AUTH']);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
private function parseCapabilities(string $ehloResponse): array
|
||||
{
|
||||
$capabilities = [];
|
||||
$lines = explode("\r\n", trim($ehloResponse));
|
||||
array_shift($lines);
|
||||
foreach ($lines as $line) {
|
||||
if (preg_match('/^[0-9]{3}[ -]([A-Z0-9-]+)((?:[ =].*)?)$/Di', $line, $matches)) {
|
||||
$value = strtoupper(ltrim($matches[2], ' ='));
|
||||
$capabilities[strtoupper($matches[1])] = $value ? explode(' ', $value) : [];
|
||||
}
|
||||
}
|
||||
|
||||
return $capabilities;
|
||||
}
|
||||
|
||||
private function handleAuth(array $modes): void
|
||||
{
|
||||
if (!$this->username) {
|
||||
return;
|
||||
}
|
||||
|
||||
$code = null;
|
||||
$authNames = [];
|
||||
$errors = [];
|
||||
$modes = array_map('strtolower', $modes);
|
||||
foreach ($this->authenticators as $authenticator) {
|
||||
if (!\in_array(strtolower($authenticator->getAuthKeyword()), $modes, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$code = null;
|
||||
$authNames[] = $authenticator->getAuthKeyword();
|
||||
try {
|
||||
$authenticator->authenticate($this);
|
||||
|
||||
return;
|
||||
} catch (UnexpectedResponseException $e) {
|
||||
$code = $e->getCode();
|
||||
|
||||
try {
|
||||
$this->executeCommand("RSET\r\n", [250]);
|
||||
} catch (TransportExceptionInterface) {
|
||||
// ignore this exception as it probably means that the server error was final
|
||||
}
|
||||
|
||||
// keep the error message, but tries the other authenticators
|
||||
$errors[$authenticator->getAuthKeyword()] = $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
if (!$authNames) {
|
||||
throw new TransportException(sprintf('Failed to find an authenticator supported by the SMTP server, which currently supports: "%s".', implode('", "', $modes)), $code ?: 504);
|
||||
}
|
||||
|
||||
$message = sprintf('Failed to authenticate on SMTP server with username "%s" using the following authenticators: "%s".', $this->username, implode('", "', $authNames));
|
||||
foreach ($errors as $name => $error) {
|
||||
$message .= sprintf(' Authenticator "%s" returned "%s".', $name, $error);
|
||||
}
|
||||
|
||||
throw new TransportException($message, $code ?: 535);
|
||||
}
|
||||
}
|
78
vendor/symfony/mailer/Transport/Smtp/EsmtpTransportFactory.php
vendored
Normal file
78
vendor/symfony/mailer/Transport/Smtp/EsmtpTransportFactory.php
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Transport\Smtp;
|
||||
|
||||
use Symfony\Component\Mailer\Transport\AbstractTransportFactory;
|
||||
use Symfony\Component\Mailer\Transport\Dsn;
|
||||
use Symfony\Component\Mailer\Transport\Smtp\Stream\SocketStream;
|
||||
use Symfony\Component\Mailer\Transport\TransportInterface;
|
||||
|
||||
/**
|
||||
* @author Konstantin Myakshin <molodchick@gmail.com>
|
||||
*/
|
||||
final class EsmtpTransportFactory extends AbstractTransportFactory
|
||||
{
|
||||
public function create(Dsn $dsn): TransportInterface
|
||||
{
|
||||
$tls = 'smtps' === $dsn->getScheme() ? true : null;
|
||||
$port = $dsn->getPort(0);
|
||||
$host = $dsn->getHost();
|
||||
|
||||
$transport = new EsmtpTransport($host, $port, $tls, $this->dispatcher, $this->logger);
|
||||
|
||||
/** @var SocketStream $stream */
|
||||
$stream = $transport->getStream();
|
||||
$streamOptions = $stream->getStreamOptions();
|
||||
|
||||
if ('' !== $dsn->getOption('verify_peer') && !filter_var($dsn->getOption('verify_peer', true), \FILTER_VALIDATE_BOOL)) {
|
||||
$streamOptions['ssl']['verify_peer'] = false;
|
||||
$streamOptions['ssl']['verify_peer_name'] = false;
|
||||
}
|
||||
|
||||
if (null !== $peerFingerprint = $dsn->getOption('peer_fingerprint')) {
|
||||
$streamOptions['ssl']['peer_fingerprint'] = $peerFingerprint;
|
||||
}
|
||||
|
||||
$stream->setStreamOptions($streamOptions);
|
||||
|
||||
if ($user = $dsn->getUser()) {
|
||||
$transport->setUsername($user);
|
||||
}
|
||||
|
||||
if ($password = $dsn->getPassword()) {
|
||||
$transport->setPassword($password);
|
||||
}
|
||||
|
||||
if (null !== ($localDomain = $dsn->getOption('local_domain'))) {
|
||||
$transport->setLocalDomain($localDomain);
|
||||
}
|
||||
|
||||
if (null !== ($maxPerSecond = $dsn->getOption('max_per_second'))) {
|
||||
$transport->setMaxPerSecond((float) $maxPerSecond);
|
||||
}
|
||||
|
||||
if (null !== ($restartThreshold = $dsn->getOption('restart_threshold'))) {
|
||||
$transport->setRestartThreshold((int) $restartThreshold, (int) $dsn->getOption('restart_threshold_sleep', 0));
|
||||
}
|
||||
|
||||
if (null !== ($pingThreshold = $dsn->getOption('ping_threshold'))) {
|
||||
$transport->setPingThreshold((int) $pingThreshold);
|
||||
}
|
||||
|
||||
return $transport;
|
||||
}
|
||||
|
||||
protected function getSupportedSchemes(): array
|
||||
{
|
||||
return ['smtp', 'smtps'];
|
||||
}
|
||||
}
|
392
vendor/symfony/mailer/Transport/Smtp/SmtpTransport.php
vendored
Normal file
392
vendor/symfony/mailer/Transport/Smtp/SmtpTransport.php
vendored
Normal file
@ -0,0 +1,392 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Transport\Smtp;
|
||||
|
||||
use Psr\EventDispatcher\EventDispatcherInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Mailer\Envelope;
|
||||
use Symfony\Component\Mailer\Exception\LogicException;
|
||||
use Symfony\Component\Mailer\Exception\TransportException;
|
||||
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
|
||||
use Symfony\Component\Mailer\Exception\UnexpectedResponseException;
|
||||
use Symfony\Component\Mailer\SentMessage;
|
||||
use Symfony\Component\Mailer\Transport\AbstractTransport;
|
||||
use Symfony\Component\Mailer\Transport\Smtp\Stream\AbstractStream;
|
||||
use Symfony\Component\Mailer\Transport\Smtp\Stream\SocketStream;
|
||||
use Symfony\Component\Mime\RawMessage;
|
||||
|
||||
/**
|
||||
* Sends emails over SMTP.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Chris Corbyn
|
||||
*/
|
||||
class SmtpTransport extends AbstractTransport
|
||||
{
|
||||
private bool $started = false;
|
||||
private int $restartThreshold = 100;
|
||||
private int $restartThresholdSleep = 0;
|
||||
private int $restartCounter = 0;
|
||||
private int $pingThreshold = 100;
|
||||
private float $lastMessageTime = 0;
|
||||
private AbstractStream $stream;
|
||||
private string $domain = '[127.0.0.1]';
|
||||
|
||||
public function __construct(?AbstractStream $stream = null, ?EventDispatcherInterface $dispatcher = null, ?LoggerInterface $logger = null)
|
||||
{
|
||||
parent::__construct($dispatcher, $logger);
|
||||
|
||||
$this->stream = $stream ?? new SocketStream();
|
||||
}
|
||||
|
||||
public function getStream(): AbstractStream
|
||||
{
|
||||
return $this->stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum number of messages to send before re-starting the transport.
|
||||
*
|
||||
* By default, the threshold is set to 100 (and no sleep at restart).
|
||||
*
|
||||
* @param int $threshold The maximum number of messages (0 to disable)
|
||||
* @param int $sleep The number of seconds to sleep between stopping and re-starting the transport
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setRestartThreshold(int $threshold, int $sleep = 0): static
|
||||
{
|
||||
$this->restartThreshold = $threshold;
|
||||
$this->restartThresholdSleep = $sleep;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minimum number of seconds required between two messages, before the server is pinged.
|
||||
* If the transport wants to send a message and the time since the last message exceeds the specified threshold,
|
||||
* the transport will ping the server first (NOOP command) to check if the connection is still alive.
|
||||
* Otherwise the message will be sent without pinging the server first.
|
||||
*
|
||||
* Do not set the threshold too low, as the SMTP server may drop the connection if there are too many
|
||||
* non-mail commands (like pinging the server with NOOP).
|
||||
*
|
||||
* By default, the threshold is set to 100 seconds.
|
||||
*
|
||||
* @param int $seconds The minimum number of seconds between two messages required to ping the server
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setPingThreshold(int $seconds): static
|
||||
{
|
||||
$this->pingThreshold = $seconds;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the local domain that will be used in HELO.
|
||||
*
|
||||
* This should be a fully-qualified domain name and should be truly the domain
|
||||
* you're using.
|
||||
*
|
||||
* If your server does not have a domain name, use the IP address. This will
|
||||
* automatically be wrapped in square brackets as described in RFC 5321,
|
||||
* section 4.1.3.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setLocalDomain(string $domain): static
|
||||
{
|
||||
if ('' !== $domain && '[' !== $domain[0]) {
|
||||
if (filter_var($domain, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4)) {
|
||||
$domain = '['.$domain.']';
|
||||
} elseif (filter_var($domain, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)) {
|
||||
$domain = '[IPv6:'.$domain.']';
|
||||
}
|
||||
}
|
||||
|
||||
$this->domain = $domain;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the domain that will be used in HELO.
|
||||
*
|
||||
* If an IP address was specified, this will be returned wrapped in square
|
||||
* brackets as described in RFC 5321, section 4.1.3.
|
||||
*/
|
||||
public function getLocalDomain(): string
|
||||
{
|
||||
return $this->domain;
|
||||
}
|
||||
|
||||
public function send(RawMessage $message, ?Envelope $envelope = null): ?SentMessage
|
||||
{
|
||||
try {
|
||||
$message = parent::send($message, $envelope);
|
||||
} catch (TransportExceptionInterface $e) {
|
||||
if ($this->started) {
|
||||
try {
|
||||
$this->executeCommand("RSET\r\n", [250]);
|
||||
} catch (TransportExceptionInterface) {
|
||||
// ignore this exception as it probably means that the server error was final
|
||||
}
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$this->checkRestartThreshold();
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
protected function parseMessageId(string $mtaResult): string
|
||||
{
|
||||
$regexps = [
|
||||
'/250 Ok (?P<id>[0-9a-f-]+)\r?$/mis',
|
||||
'/250 Ok:? queued as (?P<id>[A-Z0-9]+)\r?$/mis',
|
||||
];
|
||||
$matches = [];
|
||||
foreach ($regexps as $regexp) {
|
||||
if (preg_match($regexp, $mtaResult, $matches)) {
|
||||
return $matches['id'];
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
if ($this->stream instanceof SocketStream) {
|
||||
$name = sprintf('smtp%s://%s', ($tls = $this->stream->isTLS()) ? 's' : '', $this->stream->getHost());
|
||||
$port = $this->stream->getPort();
|
||||
if (!(25 === $port || ($tls && 465 === $port))) {
|
||||
$name .= ':'.$port;
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
return 'smtp://sendmail';
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a command against the stream, expecting the given response codes.
|
||||
*
|
||||
* @param int[] $codes
|
||||
*
|
||||
* @throws TransportException when an invalid response if received
|
||||
*/
|
||||
public function executeCommand(string $command, array $codes): string
|
||||
{
|
||||
$this->stream->write($command);
|
||||
$response = $this->getFullResponse();
|
||||
$this->assertResponseCode($response, $codes);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
protected function doSend(SentMessage $message): void
|
||||
{
|
||||
if (microtime(true) - $this->lastMessageTime > $this->pingThreshold) {
|
||||
$this->ping();
|
||||
}
|
||||
|
||||
if (!$this->started) {
|
||||
$this->start();
|
||||
}
|
||||
|
||||
try {
|
||||
$envelope = $message->getEnvelope();
|
||||
$this->doMailFromCommand($envelope->getSender()->getEncodedAddress());
|
||||
foreach ($envelope->getRecipients() as $recipient) {
|
||||
$this->doRcptToCommand($recipient->getEncodedAddress());
|
||||
}
|
||||
|
||||
$this->executeCommand("DATA\r\n", [354]);
|
||||
try {
|
||||
foreach (AbstractStream::replace("\r\n.", "\r\n..", $message->toIterable()) as $chunk) {
|
||||
$this->stream->write($chunk, false);
|
||||
}
|
||||
$this->stream->flush();
|
||||
} catch (TransportExceptionInterface $e) {
|
||||
throw $e;
|
||||
} catch (\Exception $e) {
|
||||
$this->stream->terminate();
|
||||
$this->started = false;
|
||||
$this->getLogger()->debug(sprintf('Email transport "%s" stopped', __CLASS__));
|
||||
throw $e;
|
||||
}
|
||||
$mtaResult = $this->executeCommand("\r\n.\r\n", [250]);
|
||||
$message->appendDebug($this->stream->getDebug());
|
||||
$this->lastMessageTime = microtime(true);
|
||||
|
||||
if ($mtaResult && $messageId = $this->parseMessageId($mtaResult)) {
|
||||
$message->setMessageId($messageId);
|
||||
}
|
||||
} catch (TransportExceptionInterface $e) {
|
||||
$e->appendDebug($this->stream->getDebug());
|
||||
$this->lastMessageTime = 0;
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal since version 6.1, to be made private in 7.0
|
||||
*
|
||||
* @final since version 6.1, to be made private in 7.0
|
||||
*/
|
||||
protected function doHeloCommand(): void
|
||||
{
|
||||
$this->executeCommand(sprintf("HELO %s\r\n", $this->domain), [250]);
|
||||
}
|
||||
|
||||
private function doMailFromCommand(string $address): void
|
||||
{
|
||||
$this->executeCommand(sprintf("MAIL FROM:<%s>\r\n", $address), [250]);
|
||||
}
|
||||
|
||||
private function doRcptToCommand(string $address): void
|
||||
{
|
||||
$this->executeCommand(sprintf("RCPT TO:<%s>\r\n", $address), [250, 251, 252]);
|
||||
}
|
||||
|
||||
public function start(): void
|
||||
{
|
||||
if ($this->started) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->getLogger()->debug(sprintf('Email transport "%s" starting', __CLASS__));
|
||||
|
||||
$this->stream->initialize();
|
||||
$this->assertResponseCode($this->getFullResponse(), [220]);
|
||||
$this->doHeloCommand();
|
||||
$this->started = true;
|
||||
$this->lastMessageTime = 0;
|
||||
|
||||
$this->getLogger()->debug(sprintf('Email transport "%s" started', __CLASS__));
|
||||
}
|
||||
|
||||
/**
|
||||
* Manually disconnect from the SMTP server.
|
||||
*
|
||||
* In most cases this is not necessary since the disconnect happens automatically on termination.
|
||||
* In cases of long-running scripts, this might however make sense to avoid keeping an open
|
||||
* connection to the SMTP server in between sending emails.
|
||||
*/
|
||||
public function stop(): void
|
||||
{
|
||||
if (!$this->started) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->getLogger()->debug(sprintf('Email transport "%s" stopping', __CLASS__));
|
||||
|
||||
try {
|
||||
$this->executeCommand("QUIT\r\n", [221]);
|
||||
} catch (TransportExceptionInterface) {
|
||||
} finally {
|
||||
$this->stream->terminate();
|
||||
$this->started = false;
|
||||
$this->getLogger()->debug(sprintf('Email transport "%s" stopped', __CLASS__));
|
||||
}
|
||||
}
|
||||
|
||||
private function ping(): void
|
||||
{
|
||||
if (!$this->started) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->executeCommand("NOOP\r\n", [250]);
|
||||
} catch (TransportExceptionInterface) {
|
||||
$this->stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws TransportException if a response code is incorrect
|
||||
*/
|
||||
private function assertResponseCode(string $response, array $codes): void
|
||||
{
|
||||
if (!$codes) {
|
||||
throw new LogicException('You must set the expected response code.');
|
||||
}
|
||||
|
||||
[$code] = sscanf($response, '%3d');
|
||||
$valid = \in_array($code, $codes);
|
||||
|
||||
if (!$valid || !$response) {
|
||||
$codeStr = $code ? sprintf('code "%s"', $code) : 'empty code';
|
||||
$responseStr = $response ? sprintf(', with message "%s"', trim($response)) : '';
|
||||
|
||||
throw new UnexpectedResponseException(sprintf('Expected response code "%s" but got ', implode('/', $codes)).$codeStr.$responseStr.'.', $code ?: 0);
|
||||
}
|
||||
}
|
||||
|
||||
private function getFullResponse(): string
|
||||
{
|
||||
$response = '';
|
||||
do {
|
||||
$line = $this->stream->readLine();
|
||||
$response .= $line;
|
||||
} while ($line && isset($line[3]) && ' ' !== $line[3]);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
private function checkRestartThreshold(): void
|
||||
{
|
||||
// when using sendmail via non-interactive mode, the transport is never "started"
|
||||
if (!$this->started) {
|
||||
return;
|
||||
}
|
||||
|
||||
++$this->restartCounter;
|
||||
if ($this->restartCounter < $this->restartThreshold) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->stop();
|
||||
if (0 < $sleep = $this->restartThresholdSleep) {
|
||||
$this->getLogger()->debug(sprintf('Email transport "%s" sleeps for %d seconds after stopping', __CLASS__, $sleep));
|
||||
|
||||
sleep($sleep);
|
||||
}
|
||||
$this->start();
|
||||
$this->restartCounter = 0;
|
||||
}
|
||||
|
||||
public function __sleep(): array
|
||||
{
|
||||
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function __wakeup()
|
||||
{
|
||||
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
$this->stop();
|
||||
}
|
||||
}
|
145
vendor/symfony/mailer/Transport/Smtp/Stream/AbstractStream.php
vendored
Normal file
145
vendor/symfony/mailer/Transport/Smtp/Stream/AbstractStream.php
vendored
Normal file
@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Transport\Smtp\Stream;
|
||||
|
||||
use Symfony\Component\Mailer\Exception\TransportException;
|
||||
|
||||
/**
|
||||
* A stream supporting remote sockets and local processes.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
* @author Chris Corbyn
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
abstract class AbstractStream
|
||||
{
|
||||
/** @var resource|null */
|
||||
protected $stream;
|
||||
/** @var resource|null */
|
||||
protected $in;
|
||||
/** @var resource|null */
|
||||
protected $out;
|
||||
protected $err;
|
||||
|
||||
private string $debug = '';
|
||||
|
||||
public function write(string $bytes, bool $debug = true): void
|
||||
{
|
||||
if ($debug) {
|
||||
foreach (explode("\n", trim($bytes)) as $line) {
|
||||
$this->debug .= sprintf("> %s\n", $line);
|
||||
}
|
||||
}
|
||||
|
||||
$bytesToWrite = \strlen($bytes);
|
||||
$totalBytesWritten = 0;
|
||||
while ($totalBytesWritten < $bytesToWrite) {
|
||||
$bytesWritten = @fwrite($this->in, substr($bytes, $totalBytesWritten));
|
||||
if (false === $bytesWritten || 0 === $bytesWritten) {
|
||||
throw new TransportException('Unable to write bytes on the wire.');
|
||||
}
|
||||
|
||||
$totalBytesWritten += $bytesWritten;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes the contents of the stream (empty it) and set the internal pointer to the beginning.
|
||||
*/
|
||||
public function flush(): void
|
||||
{
|
||||
fflush($this->in);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs any initialization needed.
|
||||
*/
|
||||
abstract public function initialize(): void;
|
||||
|
||||
public function terminate(): void
|
||||
{
|
||||
$this->stream = $this->err = $this->out = $this->in = null;
|
||||
}
|
||||
|
||||
public function readLine(): string
|
||||
{
|
||||
if (feof($this->out)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$line = @fgets($this->out);
|
||||
if ('' === $line || false === $line) {
|
||||
$metas = stream_get_meta_data($this->out);
|
||||
if ($metas['timed_out']) {
|
||||
throw new TransportException(sprintf('Connection to "%s" timed out.', $this->getReadConnectionDescription()));
|
||||
}
|
||||
if ($metas['eof']) {
|
||||
throw new TransportException(sprintf('Connection to "%s" has been closed unexpectedly.', $this->getReadConnectionDescription()));
|
||||
}
|
||||
if (false === $line) {
|
||||
throw new TransportException(sprintf('Unable to read from connection to "%s": ', $this->getReadConnectionDescription()).error_get_last()['message']);
|
||||
}
|
||||
}
|
||||
|
||||
$this->debug .= sprintf('< %s', $line);
|
||||
|
||||
return $line;
|
||||
}
|
||||
|
||||
public function getDebug(): string
|
||||
{
|
||||
$debug = $this->debug;
|
||||
$this->debug = '';
|
||||
|
||||
return $debug;
|
||||
}
|
||||
|
||||
public static function replace(string $from, string $to, iterable $chunks): \Generator
|
||||
{
|
||||
if ('' === $from) {
|
||||
yield from $chunks;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$carry = '';
|
||||
$fromLen = \strlen($from);
|
||||
|
||||
foreach ($chunks as $chunk) {
|
||||
if ('' === $chunk = $carry.$chunk) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (str_contains($chunk, $from)) {
|
||||
$chunk = explode($from, $chunk);
|
||||
$carry = array_pop($chunk);
|
||||
|
||||
yield implode($to, $chunk).$to;
|
||||
} else {
|
||||
$carry = $chunk;
|
||||
}
|
||||
|
||||
if (\strlen($carry) > $fromLen) {
|
||||
yield substr($carry, 0, -$fromLen);
|
||||
$carry = substr($carry, -$fromLen);
|
||||
}
|
||||
}
|
||||
|
||||
if ('' !== $carry) {
|
||||
yield $carry;
|
||||
}
|
||||
}
|
||||
|
||||
abstract protected function getReadConnectionDescription(): string;
|
||||
}
|
71
vendor/symfony/mailer/Transport/Smtp/Stream/ProcessStream.php
vendored
Normal file
71
vendor/symfony/mailer/Transport/Smtp/Stream/ProcessStream.php
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Transport\Smtp\Stream;
|
||||
|
||||
use Symfony\Component\Mailer\Exception\TransportException;
|
||||
|
||||
/**
|
||||
* A stream supporting local processes.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Chris Corbyn
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class ProcessStream extends AbstractStream
|
||||
{
|
||||
private string $command;
|
||||
|
||||
public function setCommand(string $command): void
|
||||
{
|
||||
$this->command = $command;
|
||||
}
|
||||
|
||||
public function initialize(): void
|
||||
{
|
||||
$descriptorSpec = [
|
||||
0 => ['pipe', 'r'],
|
||||
1 => ['pipe', 'w'],
|
||||
2 => ['pipe', '\\' === \DIRECTORY_SEPARATOR ? 'a' : 'w'],
|
||||
];
|
||||
$pipes = [];
|
||||
$this->stream = proc_open($this->command, $descriptorSpec, $pipes);
|
||||
stream_set_blocking($pipes[2], false);
|
||||
if ($err = stream_get_contents($pipes[2])) {
|
||||
throw new TransportException('Process could not be started: '.$err);
|
||||
}
|
||||
$this->in = &$pipes[0];
|
||||
$this->out = &$pipes[1];
|
||||
$this->err = &$pipes[2];
|
||||
}
|
||||
|
||||
public function terminate(): void
|
||||
{
|
||||
if (null !== $this->stream) {
|
||||
fclose($this->in);
|
||||
$out = stream_get_contents($this->out);
|
||||
fclose($this->out);
|
||||
$err = stream_get_contents($this->err);
|
||||
fclose($this->err);
|
||||
if (0 !== $exitCode = proc_close($this->stream)) {
|
||||
throw new TransportException('Process failed with exit code '.$exitCode.': '.$out.$err);
|
||||
}
|
||||
}
|
||||
|
||||
parent::terminate();
|
||||
}
|
||||
|
||||
protected function getReadConnectionDescription(): string
|
||||
{
|
||||
return 'process '.$this->command;
|
||||
}
|
||||
}
|
193
vendor/symfony/mailer/Transport/Smtp/Stream/SocketStream.php
vendored
Normal file
193
vendor/symfony/mailer/Transport/Smtp/Stream/SocketStream.php
vendored
Normal file
@ -0,0 +1,193 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Transport\Smtp\Stream;
|
||||
|
||||
use Symfony\Component\Mailer\Exception\TransportException;
|
||||
|
||||
/**
|
||||
* A stream supporting remote sockets.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Chris Corbyn
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class SocketStream extends AbstractStream
|
||||
{
|
||||
private string $url;
|
||||
private string $host = 'localhost';
|
||||
private int $port = 465;
|
||||
private float $timeout;
|
||||
private bool $tls = true;
|
||||
private ?string $sourceIp = null;
|
||||
private array $streamContextOptions = [];
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function setTimeout(float $timeout): static
|
||||
{
|
||||
$this->timeout = $timeout;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTimeout(): float
|
||||
{
|
||||
return $this->timeout ?? (float) \ini_get('default_socket_timeout');
|
||||
}
|
||||
|
||||
/**
|
||||
* Literal IPv6 addresses should be wrapped in square brackets.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setHost(string $host): static
|
||||
{
|
||||
$this->host = $host;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getHost(): string
|
||||
{
|
||||
return $this->host;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function setPort(int $port): static
|
||||
{
|
||||
$this->port = $port;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPort(): int
|
||||
{
|
||||
return $this->port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the TLS/SSL on the socket (disables STARTTLS).
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function disableTls(): static
|
||||
{
|
||||
$this->tls = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isTLS(): bool
|
||||
{
|
||||
return $this->tls;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function setStreamOptions(array $options): static
|
||||
{
|
||||
$this->streamContextOptions = $options;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getStreamOptions(): array
|
||||
{
|
||||
return $this->streamContextOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the source IP.
|
||||
*
|
||||
* IPv6 addresses should be wrapped in square brackets.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setSourceIp(string $ip): static
|
||||
{
|
||||
$this->sourceIp = $ip;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the IP used to connect to the destination.
|
||||
*/
|
||||
public function getSourceIp(): ?string
|
||||
{
|
||||
return $this->sourceIp;
|
||||
}
|
||||
|
||||
public function initialize(): void
|
||||
{
|
||||
$this->url = $this->host.':'.$this->port;
|
||||
if ($this->tls) {
|
||||
$this->url = 'ssl://'.$this->url;
|
||||
}
|
||||
$options = [];
|
||||
if ($this->sourceIp) {
|
||||
$options['socket']['bindto'] = $this->sourceIp.':0';
|
||||
}
|
||||
if ($this->streamContextOptions) {
|
||||
$options = array_merge($options, $this->streamContextOptions);
|
||||
}
|
||||
// do it unconditionally as it will be used by STARTTLS as well if supported
|
||||
$options['ssl']['crypto_method'] ??= \STREAM_CRYPTO_METHOD_TLS_CLIENT | \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT | \STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
|
||||
$streamContext = stream_context_create($options);
|
||||
|
||||
$timeout = $this->getTimeout();
|
||||
set_error_handler(function ($type, $msg) {
|
||||
throw new TransportException(sprintf('Connection could not be established with host "%s": ', $this->url).$msg);
|
||||
});
|
||||
try {
|
||||
$this->stream = stream_socket_client($this->url, $errno, $errstr, $timeout, \STREAM_CLIENT_CONNECT, $streamContext);
|
||||
} finally {
|
||||
restore_error_handler();
|
||||
}
|
||||
|
||||
stream_set_blocking($this->stream, true);
|
||||
stream_set_timeout($this->stream, (int) $timeout, (int) (($timeout - (int) $timeout) * 1000000));
|
||||
$this->in = &$this->stream;
|
||||
$this->out = &$this->stream;
|
||||
}
|
||||
|
||||
public function startTLS(): bool
|
||||
{
|
||||
set_error_handler(function ($type, $msg) {
|
||||
throw new TransportException('Unable to connect with STARTTLS: '.$msg);
|
||||
});
|
||||
try {
|
||||
return stream_socket_enable_crypto($this->stream, true);
|
||||
} finally {
|
||||
restore_error_handler();
|
||||
}
|
||||
}
|
||||
|
||||
public function terminate(): void
|
||||
{
|
||||
if (null !== $this->stream) {
|
||||
fclose($this->stream);
|
||||
}
|
||||
|
||||
parent::terminate();
|
||||
}
|
||||
|
||||
protected function getReadConnectionDescription(): string
|
||||
{
|
||||
return $this->url;
|
||||
}
|
||||
}
|
29
vendor/symfony/mailer/Transport/TransportFactoryInterface.php
vendored
Normal file
29
vendor/symfony/mailer/Transport/TransportFactoryInterface.php
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Transport;
|
||||
|
||||
use Symfony\Component\Mailer\Exception\IncompleteDsnException;
|
||||
use Symfony\Component\Mailer\Exception\UnsupportedSchemeException;
|
||||
|
||||
/**
|
||||
* @author Konstantin Myakshin <molodchick@gmail.com>
|
||||
*/
|
||||
interface TransportFactoryInterface
|
||||
{
|
||||
/**
|
||||
* @throws UnsupportedSchemeException
|
||||
* @throws IncompleteDsnException
|
||||
*/
|
||||
public function create(Dsn $dsn): TransportInterface;
|
||||
|
||||
public function supports(Dsn $dsn): bool;
|
||||
}
|
33
vendor/symfony/mailer/Transport/TransportInterface.php
vendored
Normal file
33
vendor/symfony/mailer/Transport/TransportInterface.php
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Transport;
|
||||
|
||||
use Symfony\Component\Mailer\Envelope;
|
||||
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
|
||||
use Symfony\Component\Mailer\SentMessage;
|
||||
use Symfony\Component\Mime\RawMessage;
|
||||
|
||||
/**
|
||||
* Interface for all mailer transports.
|
||||
*
|
||||
* When sending emails, you should prefer MailerInterface implementations
|
||||
* as they allow asynchronous sending.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
interface TransportInterface extends \Stringable
|
||||
{
|
||||
/**
|
||||
* @throws TransportExceptionInterface
|
||||
*/
|
||||
public function send(RawMessage $message, ?Envelope $envelope = null): ?SentMessage;
|
||||
}
|
75
vendor/symfony/mailer/Transport/Transports.php
vendored
Normal file
75
vendor/symfony/mailer/Transport/Transports.php
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Mailer\Transport;
|
||||
|
||||
use Symfony\Component\Mailer\Envelope;
|
||||
use Symfony\Component\Mailer\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Mailer\Exception\LogicException;
|
||||
use Symfony\Component\Mailer\SentMessage;
|
||||
use Symfony\Component\Mime\Message;
|
||||
use Symfony\Component\Mime\RawMessage;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
final class Transports implements TransportInterface
|
||||
{
|
||||
/**
|
||||
* @var array<string, TransportInterface>
|
||||
*/
|
||||
private array $transports = [];
|
||||
private TransportInterface $default;
|
||||
|
||||
/**
|
||||
* @param iterable<string, TransportInterface> $transports
|
||||
*/
|
||||
public function __construct(iterable $transports)
|
||||
{
|
||||
foreach ($transports as $name => $transport) {
|
||||
$this->default ??= $transport;
|
||||
$this->transports[$name] = $transport;
|
||||
}
|
||||
|
||||
if (!$this->transports) {
|
||||
throw new LogicException(sprintf('"%s" must have at least one transport configured.', __CLASS__));
|
||||
}
|
||||
}
|
||||
|
||||
public function send(RawMessage $message, ?Envelope $envelope = null): ?SentMessage
|
||||
{
|
||||
/** @var Message $message */
|
||||
if (RawMessage::class === $message::class || !$message->getHeaders()->has('X-Transport')) {
|
||||
return $this->default->send($message, $envelope);
|
||||
}
|
||||
|
||||
$headers = $message->getHeaders();
|
||||
$transport = $headers->get('X-Transport')->getBody();
|
||||
$headers->remove('X-Transport');
|
||||
|
||||
if (!isset($this->transports[$transport])) {
|
||||
throw new InvalidArgumentException(sprintf('The "%s" transport does not exist (available transports: "%s").', $transport, implode('", "', array_keys($this->transports))));
|
||||
}
|
||||
|
||||
try {
|
||||
return $this->transports[$transport]->send($message, $envelope);
|
||||
} catch (\Throwable $e) {
|
||||
$headers->addTextHeader('X-Transport', $transport);
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return '['.implode(',', array_keys($this->transports)).']';
|
||||
}
|
||||
}
|
47
vendor/symfony/mailer/composer.json
vendored
Normal file
47
vendor/symfony/mailer/composer.json
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
{
|
||||
"name": "symfony/mailer",
|
||||
"type": "library",
|
||||
"description": "Helps sending emails",
|
||||
"keywords": [],
|
||||
"homepage": "https://symfony.com",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=8.1",
|
||||
"egulias/email-validator": "^2.1.10|^3|^4",
|
||||
"psr/event-dispatcher": "^1",
|
||||
"psr/log": "^1|^2|^3",
|
||||
"symfony/event-dispatcher": "^5.4|^6.0|^7.0",
|
||||
"symfony/mime": "^6.2|^7.0",
|
||||
"symfony/service-contracts": "^2.5|^3"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/console": "^5.4|^6.0|^7.0",
|
||||
"symfony/http-client": "^5.4|^6.0|^7.0",
|
||||
"symfony/messenger": "^6.2|^7.0",
|
||||
"symfony/twig-bridge": "^6.2|^7.0"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/http-client-contracts": "<2.5",
|
||||
"symfony/http-kernel": "<5.4",
|
||||
"symfony/messenger": "<6.2",
|
||||
"symfony/mime": "<6.2",
|
||||
"symfony/twig-bridge": "<6.2.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Symfony\\Component\\Mailer\\": "" },
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"minimum-stability": "dev"
|
||||
}
|
Reference in New Issue
Block a user