+ * @author Nicolas Grekas
+ * @author Chris Corbyn
+ *
+ * @internal
+ *
+ * @experimental in 4.3
+ */
+abstract class AbstractStream
+{
+ protected $stream;
+ protected $in;
+ protected $out;
+
+ public function write(string $bytes): void
+ {
+ $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->out = $this->in = null;
+ }
+
+ public function readLine(): string
+ {
+ if (feof($this->out)) {
+ return '';
+ }
+
+ $line = fgets($this->out);
+ if (0 === \strlen($line)) {
+ $metas = stream_get_meta_data($this->out);
+ if ($metas['timed_out']) {
+ throw new TransportException(sprintf('Connection to "%s" timed out.', $this->getReadConnectionDescription()));
+ }
+ }
+
+ return $line;
+ }
+
+ 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 (false !== strpos($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;
+}
diff --git a/src/Symfony/Component/Mailer/Transport/Smtp/Stream/ProcessStream.php b/src/Symfony/Component/Mailer/Transport/Smtp/Stream/ProcessStream.php
new file mode 100644
index 0000000000000..dfbf930840d89
--- /dev/null
+++ b/src/Symfony/Component/Mailer/Transport/Smtp/Stream/ProcessStream.php
@@ -0,0 +1,67 @@
+
+ *
+ * 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
+ * @author Chris Corbyn
+ *
+ * @internal
+ *
+ * @experimental in 4.3
+ */
+final class ProcessStream extends AbstractStream
+{
+ private $command;
+
+ public function setCommand(string $command)
+ {
+ $this->command = $command;
+ }
+
+ public function initialize(): void
+ {
+ $descriptorSpec = [
+ 0 => ['pipe', 'r'],
+ 1 => ['pipe', 'w'],
+ 2 => ['pipe', '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(sprintf('Process could not be started: %s.', $err));
+ }
+ $this->in = &$pipes[0];
+ $this->out = &$pipes[1];
+ }
+
+ public function terminate(): void
+ {
+ if (null !== $this->stream) {
+ fclose($this->in);
+ fclose($this->out);
+ proc_close($this->stream);
+ }
+
+ parent::terminate();
+ }
+
+ protected function getReadConnectionDescription(): string
+ {
+ return 'process '.$this->command;
+ }
+}
diff --git a/src/Symfony/Component/Mailer/Transport/Smtp/Stream/SocketStream.php b/src/Symfony/Component/Mailer/Transport/Smtp/Stream/SocketStream.php
new file mode 100644
index 0000000000000..07692b11bac71
--- /dev/null
+++ b/src/Symfony/Component/Mailer/Transport/Smtp/Stream/SocketStream.php
@@ -0,0 +1,172 @@
+
+ *
+ * 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
+ * @author Chris Corbyn
+ *
+ * @internal
+ *
+ * @experimental in 4.3
+ */
+final class SocketStream extends AbstractStream
+{
+ private $url;
+ private $host = 'localhost';
+ private $protocol = 'tcp';
+ private $port = 25;
+ private $timeout = 15;
+ private $tls = false;
+ private $sourceIp;
+ private $streamContextOptions = [];
+
+ public function setTimeout(int $timeout): self
+ {
+ $this->timeout = $timeout;
+
+ return $this;
+ }
+
+ public function getTimeout(): int
+ {
+ return $this->timeout;
+ }
+
+ /**
+ * Literal IPv6 addresses should be wrapped in square brackets.
+ */
+ public function setHost(string $host): self
+ {
+ $this->host = $host;
+
+ return $this;
+ }
+
+ public function getHost(): string
+ {
+ return $this->host;
+ }
+
+ public function setPort(int $port): self
+ {
+ $this->port = $port;
+
+ return $this;
+ }
+
+ public function getPort(): int
+ {
+ return $this->port;
+ }
+
+ /**
+ * Sets the encryption type (tls or ssl).
+ */
+ public function setEncryption(string $encryption): self
+ {
+ $encryption = strtolower($encryption);
+ if ('tls' === $encryption) {
+ $this->protocol = 'tcp';
+ $this->tls = true;
+ } else {
+ $this->protocol = $encryption;
+ $this->tls = false;
+ }
+
+ return $this;
+ }
+
+ public function isTLS(): bool
+ {
+ return $this->tls;
+ }
+
+ public function setStreamOptions(array $options): self
+ {
+ $this->streamContextOptions = $options;
+
+ return $this;
+ }
+
+ public function getStreamOptions(): array
+ {
+ return $this->streamContextOptions;
+ }
+
+ /**
+ * Sets the source IP.
+ *
+ * IPv6 addresses should be wrapped in square brackets.
+ */
+ public function setSourceIp(string $ip): self
+ {
+ $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->protocol) {
+ $this->url = $this->protocol.'://'.$this->url;
+ }
+ $options = [];
+ if ($this->sourceIp) {
+ $options['socket']['bindto'] = $this->sourceIp.':0';
+ }
+ if ($this->streamContextOptions) {
+ $options = array_merge($options, $this->streamContextOptions);
+ }
+ $streamContext = stream_context_create($options);
+ $this->stream = @stream_socket_client($this->url, $errno, $errstr, $this->timeout, STREAM_CLIENT_CONNECT, $streamContext);
+ if (false === $this->stream) {
+ throw new TransportException(sprintf('Connection could not be established with host "%s": %s (%s)', $this->url, $errstr, $errno));
+ }
+ stream_set_blocking($this->stream, true);
+ stream_set_timeout($this->stream, $this->timeout);
+ $this->in = &$this->stream;
+ $this->out = &$this->stream;
+ }
+
+ public function startTLS(): bool
+ {
+ return (bool) stream_socket_enable_crypto($this->stream, true);
+ }
+
+ public function terminate(): void
+ {
+ if (null !== $this->stream) {
+ fclose($this->stream);
+ }
+
+ parent::terminate();
+ }
+
+ protected function getReadConnectionDescription(): string
+ {
+ return $this->url;
+ }
+}
diff --git a/src/Symfony/Component/Mailer/Transport/TransportInterface.php b/src/Symfony/Component/Mailer/Transport/TransportInterface.php
new file mode 100644
index 0000000000000..852db42be78ea
--- /dev/null
+++ b/src/Symfony/Component/Mailer/Transport/TransportInterface.php
@@ -0,0 +1,35 @@
+
+ *
+ * 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\TransportExceptionInterface;
+use Symfony\Component\Mailer\SentMessage;
+use Symfony\Component\Mailer\SmtpEnvelope;
+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
+ *
+ * @experimental in 4.3
+ */
+interface TransportInterface
+{
+ /**
+ * @throws TransportExceptionInterface
+ */
+ public function send(RawMessage $message, SmtpEnvelope $envelope = null): ?SentMessage;
+}
diff --git a/src/Symfony/Component/Mailer/composer.json b/src/Symfony/Component/Mailer/composer.json
new file mode 100644
index 0000000000000..27a5db082a737
--- /dev/null
+++ b/src/Symfony/Component/Mailer/composer.json
@@ -0,0 +1,45 @@
+{
+ "name": "symfony/mailer",
+ "type": "library",
+ "description": "Symfony Mailer Component",
+ "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": "^7.1.3",
+ "psr/log": "~1.0",
+ "symfony/event-dispatcher": "^4.3",
+ "symfony/mime": "^4.3"
+ },
+ "require-dev": {
+ "symfony/amazon-mailer": "^4.3",
+ "egulias/email-validator": "^2.0",
+ "symfony/google-mailer": "^4.3",
+ "symfony/mailgun-mailer": "^4.3",
+ "symfony/mailchimp-mailer": "^4.3",
+ "symfony/postmark-mailer": "^4.3",
+ "symfony/sendgrid-mailer": "^4.3"
+ },
+ "autoload": {
+ "psr-4": { "Symfony\\Component\\Mailer\\": "" },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "minimum-stability": "dev",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.3-dev"
+ }
+ }
+}
diff --git a/src/Symfony/Component/Mailer/phpunit.xml.dist b/src/Symfony/Component/Mailer/phpunit.xml.dist
new file mode 100644
index 0000000000000..adcc4721d47a0
--- /dev/null
+++ b/src/Symfony/Component/Mailer/phpunit.xml.dist
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+ ./Tests/
+
+
+
+
+
+ ./
+
+ ./Tests
+ ./vendor
+
+
+
+
pFad - Phonifier reborn
Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy