1<?php 2 3/* 4 * This file is part of the Symfony package. 5 * 6 * (c) Fabien Potencier <fabien@symfony.com> 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12namespace Symfony\Component\Mailer\Transport; 13 14use Psr\Log\LoggerInterface; 15use Symfony\Component\Mailer\Envelope; 16use Symfony\Component\Mailer\SentMessage; 17use Symfony\Component\Mailer\Transport\Smtp\SmtpTransport; 18use Symfony\Component\Mailer\Transport\Smtp\Stream\AbstractStream; 19use Symfony\Component\Mailer\Transport\Smtp\Stream\ProcessStream; 20use Symfony\Component\Mime\RawMessage; 21use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; 22 23/** 24 * SendmailTransport for sending mail through a Sendmail/Postfix (etc..) binary. 25 * 26 * Supported modes are -bs and -t, with any additional flags desired. 27 * It is advised to use -bs mode since error reporting with -t mode is not 28 * possible. 29 * 30 * Transport can be instanciated through SendmailTransportFactory or NativeTransportFactory: 31 * 32 * - SendmailTransportFactory to use most common sendmail path and recommanded options 33 * - NativeTransportFactory when configuration is set via php.ini 34 * 35 * @author Fabien Potencier <fabien@symfony.com> 36 * @author Chris Corbyn 37 */ 38class SendmailTransport extends AbstractTransport 39{ 40 private $command = '/usr/sbin/sendmail -bs'; 41 private $stream; 42 private $transport; 43 44 /** 45 * Constructor. 46 * 47 * If using -t mode you are strongly advised to include -oi or -i in the flags. 48 * For example: /usr/sbin/sendmail -oi -t 49 * -f<sender> flag will be appended automatically if one is not present. 50 * 51 * The recommended mode is "-bs" since it is interactive and failure notifications are hence possible. 52 */ 53 public function __construct(string $command = null, EventDispatcherInterface $dispatcher = null, LoggerInterface $logger = null) 54 { 55 parent::__construct($dispatcher, $logger); 56 57 if (null !== $command) { 58 if (false === strpos($command, ' -bs') && false === strpos($command, ' -t')) { 59 throw new \InvalidArgumentException(sprintf('Unsupported sendmail command flags "%s"; must be one of "-bs" or "-t" but can include additional flags.', $command)); 60 } 61 62 $this->command = $command; 63 } 64 65 $this->stream = new ProcessStream(); 66 if (false !== strpos($this->command, ' -bs')) { 67 $this->stream->setCommand($this->command); 68 $this->transport = new SmtpTransport($this->stream, $dispatcher, $logger); 69 } 70 } 71 72 public function send(RawMessage $message, Envelope $envelope = null): ?SentMessage 73 { 74 if ($this->transport) { 75 return $this->transport->send($message, $envelope); 76 } 77 78 return parent::send($message, $envelope); 79 } 80 81 public function __toString(): string 82 { 83 if ($this->transport) { 84 return (string) $this->transport; 85 } 86 87 return 'smtp://sendmail'; 88 } 89 90 protected function doSend(SentMessage $message): void 91 { 92 $this->getLogger()->debug(sprintf('Email transport "%s" starting', __CLASS__)); 93 94 $command = $this->command; 95 if (false === strpos($command, ' -f')) { 96 $command .= ' -f'.escapeshellarg($message->getEnvelope()->getSender()->getEncodedAddress()); 97 } 98 99 $chunks = AbstractStream::replace("\r\n", "\n", $message->toIterable()); 100 101 if (false === strpos($command, ' -i') && false === strpos($command, ' -oi')) { 102 $chunks = AbstractStream::replace("\n.", "\n..", $chunks); 103 } 104 105 $this->stream->setCommand($command); 106 $this->stream->initialize(); 107 foreach ($chunks as $chunk) { 108 $this->stream->write($chunk); 109 } 110 $this->stream->flush(); 111 $this->stream->terminate(); 112 113 $this->getLogger()->debug(sprintf('Email transport "%s" stopped', __CLASS__)); 114 } 115} 116