lockfile again

This commit is contained in:
Tim Bendt
2025-11-26 11:50:55 -05:00
parent af3c23cb6e
commit 6ceecaa69e
4461 changed files with 641349 additions and 10 deletions

View File

@@ -0,0 +1,11 @@
*.crt -crlf
*.key -crlf
*.srl -crlf
*.pub -crlf
*.priv -crlf
*.txt -crlf
# ignore directories in the git-generated distributed .zip archive
/doc/notes export-ignore
/tests export-ignore
/phpunit.xml.dist export-ignore

View File

@@ -0,0 +1,19 @@
<!-- Please fill in this template according to your issue. -->
| Q | A
| ------------------- | -----
| Bug report? | yes/no
| Feature request? | yes/no
| RFC? | yes/no
| How used? | Standalone/Symfony/3party
| Swiftmailer version | x.y.z
| PHP version | x.y.z
### Observed behaviour
<!-- What does the code do? -->
### Expected behaviour
<!-- What should the code do? -->
### Example
<!-- Example to reproduce the issue. -->

View File

@@ -0,0 +1,14 @@
<!-- Please fill in this template according to the PR you're about to submit. -->
| Q | A
| ------------- | ---
| Bug fix? | yes/no
| New feature? | yes/no
| Doc update? | yes/no
| BC breaks? | yes/no
| Deprecations? | yes/no
| Fixed tickets | #... <!-- #-prefixed issue number(s), if any -->
| License | MIT
<!-- Replace this comment by the description of your issue. -->

View File

@@ -0,0 +1,55 @@
name: tests
on:
push:
pull_request:
jobs:
linux_tests:
runs-on: ubuntu-20.04
services:
mailcatcher:
image: dockage/mailcatcher:0.7.1
ports:
- 4456:1025
strategy:
fail-fast: true
matrix:
php: ['7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1']
name: PHP ${{ matrix.php }}
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: dom, curl, libxml, mbstring, zip, intl
tools: composer:v2
coverage: none
- name: Prepare test config files
run: |
cp tests/acceptance.conf.php.default tests/acceptance.conf.php
cp tests/smoke.conf.php.default tests/smoke.conf.php
- name: Require Symfony PHPUnit Bridge 5.4 for PHP 8.1
if: ${{ matrix.php >= 8.1 }}
run: composer require symfony/phpunit-bridge:^5.4 --dev --prefer-dist --no-interaction --no-progress
- name: Install dependencies
uses: nick-invision/retry@v1
with:
timeout_minutes: 5
max_attempts: 5
command: composer update --prefer-stable --prefer-dist --no-interaction --no-progress
- name: Execute tests
run: vendor/bin/simple-phpunit --verbose
env:
SYMFONY_PHPUNIT_REMOVE_RETURN_TYPEHINT: 1

View File

@@ -0,0 +1,21 @@
<?php
return PhpCsFixer\Config::create()
->setRules([
'@Symfony' => true,
'@Symfony:risky' => true,
'@PHPUnit75Migration:risky' => true,
'php_unit_dedicate_assert' => ['target' => '5.6'],
'array_syntax' => ['syntax' => 'short'],
'php_unit_fqcn_annotation' => true,
'no_unreachable_default_argument_value' => false,
'braces' => ['allow_single_line_closure' => true],
'heredoc_to_nowdoc' => false,
'ordered_imports' => true,
'phpdoc_types_order' => ['null_adjustment' => 'always_last', 'sort_algorithm' => 'none'],
'native_function_invocation' => ['include' => ['@compiler_optimized'], 'scope' => 'all'],
'fopen_flags' => false,
])
->setRiskyAllowed(true)
->setFinder(PhpCsFixer\Finder::create()->in(__DIR__))
;

19
vendor/swiftmailer/swiftmailer/LICENSE vendored Normal file
View File

@@ -0,0 +1,19 @@
Copyright (c) 2013-2021 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.

View File

@@ -0,0 +1,44 @@
{
"name": "swiftmailer/swiftmailer",
"type": "library",
"description": "Swiftmailer, free feature-rich PHP mailer",
"keywords": ["mail","mailer","email"],
"homepage": "https://swiftmailer.symfony.com",
"license": "MIT",
"authors": [
{
"name": "Chris Corbyn"
},
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"require": {
"php": ">=7.0.0",
"egulias/email-validator": "^2.0|^3.1",
"symfony/polyfill-iconv": "^1.0",
"symfony/polyfill-mbstring": "^1.0",
"symfony/polyfill-intl-idn": "^1.10"
},
"require-dev": {
"mockery/mockery": "^1.0",
"symfony/phpunit-bridge": "^4.4|^5.4"
},
"suggest": {
"ext-intl": "Needed to support internationalized email addresses"
},
"autoload": {
"files": ["lib/swift_required.php"]
},
"autoload-dev": {
"psr-0": { "Swift_": "tests/unit" }
},
"extra": {
"branch-alias": {
"dev-master": "6.2-dev"
}
},
"minimum-stability": "dev",
"prefer-stable": true
}

View File

@@ -0,0 +1,78 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* General utility class in Swift Mailer, not to be instantiated.
*
* @author Chris Corbyn
*/
abstract class Swift
{
const VERSION = '6.3.0';
public static $initialized = false;
public static $inits = [];
/**
* Registers an initializer callable that will be called the first time
* a SwiftMailer class is autoloaded.
*
* This enables you to tweak the default configuration in a lazy way.
*
* @param mixed $callable A valid PHP callable that will be called when autoloading the first Swift class
*/
public static function init($callable)
{
self::$inits[] = $callable;
}
/**
* Internal autoloader for spl_autoload_register().
*
* @param string $class
*/
public static function autoload($class)
{
// Don't interfere with other autoloaders
if (0 !== strpos($class, 'Swift_')) {
return;
}
$path = __DIR__.'/'.str_replace('_', '/', $class).'.php';
if (!file_exists($path)) {
return;
}
require $path;
if (self::$inits && !self::$initialized) {
self::$initialized = true;
foreach (self::$inits as $init) {
\call_user_func($init);
}
}
}
/**
* Configure autoloading using Swift Mailer.
*
* This is designed to play nicely with other autoloaders.
*
* @param mixed $callable A valid PHP callable that will be called when autoloading the first Swift class
*/
public static function registerAutoload($callable = null)
{
if (null !== $callable) {
self::$inits[] = $callable;
}
spl_autoload_register(['Swift', 'autoload']);
}
}

View File

@@ -0,0 +1,25 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2018 Christian Schmidt
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Email address encoder.
*
* @author Christian Schmidt
*/
interface Swift_AddressEncoder
{
/**
* Encodes an email address.
*
* @throws Swift_AddressEncoderException if the email cannot be represented in
* the encoding implemented by this class
*/
public function encodeString(string $address): string;
}

View File

@@ -0,0 +1,50 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2018 Christian Schmidt
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* An IDN email address encoder.
*
* Encodes the domain part of an address using IDN. This is compatible will all
* SMTP servers.
*
* This encoder does not support email addresses with non-ASCII characters in
* local-part (the substring before @). To send to such addresses, use
* Swift_AddressEncoder_Utf8AddressEncoder together with
* Swift_Transport_Esmtp_SmtpUtf8Handler. Your outbound SMTP server must support
* the SMTPUTF8 extension.
*
* @author Christian Schmidt
*/
class Swift_AddressEncoder_IdnAddressEncoder implements Swift_AddressEncoder
{
/**
* Encodes the domain part of an address using IDN.
*
* @throws Swift_AddressEncoderException If local-part contains non-ASCII characters
*/
public function encodeString(string $address): string
{
$i = strrpos($address, '@');
if (false !== $i) {
$local = substr($address, 0, $i);
$domain = substr($address, $i + 1);
if (preg_match('/[^\x00-\x7F]/', $local)) {
throw new Swift_AddressEncoderException('Non-ASCII characters not supported in local-part', $address);
}
if (preg_match('/[^\x00-\x7F]/', $domain)) {
$address = sprintf('%s@%s', $local, idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46));
}
}
return $address;
}
}

View File

@@ -0,0 +1,36 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2018 Christian Schmidt
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* A UTF-8 email address encoder.
*
* Returns the email address verbatimly in UTF-8 as permitted by RFC 6531 and
* RFC 6532. It supports addresses containing non-ASCII characters in both
* local-part and domain (i.e. on both sides of @).
*
* This encoder must be used together with Swift_Transport_Esmtp_SmtpUtf8Handler
* and requires that the outbound SMTP server supports the SMTPUTF8 extension.
*
* If your outbound SMTP server does not support SMTPUTF8, use
* Swift_AddressEncoder_IdnAddressEncoder instead. This allows sending to email
* addresses with non-ASCII characters in the domain, but not in local-part.
*
* @author Christian Schmidt
*/
class Swift_AddressEncoder_Utf8AddressEncoder implements Swift_AddressEncoder
{
/**
* Returns the address verbatimly.
*/
public function encodeString(string $address): string
{
return $address;
}
}

View File

@@ -0,0 +1,32 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2018 Christian Schmidt
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* AddressEncoderException when the specified email address is in a format that
* cannot be encoded by a given address encoder.
*
* @author Christian Schmidt
*/
class Swift_AddressEncoderException extends Swift_RfcComplianceException
{
protected $address;
public function __construct(string $message, string $address)
{
parent::__construct($message);
$this->address = $address;
}
public function getAddress(): string
{
return $this->address;
}
}

View File

@@ -0,0 +1,54 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Attachment class for attaching files to a {@link Swift_Mime_SimpleMessage}.
*
* @author Chris Corbyn
*/
class Swift_Attachment extends Swift_Mime_Attachment
{
/**
* Create a new Attachment.
*
* Details may be optionally provided to the constructor.
*
* @param string|Swift_OutputByteStream $data
* @param string $filename
* @param string $contentType
*/
public function __construct($data = null, $filename = null, $contentType = null)
{
\call_user_func_array(
[$this, 'Swift_Mime_Attachment::__construct'],
Swift_DependencyContainer::getInstance()
->createDependenciesFor('mime.attachment')
);
$this->setBody($data, $contentType);
$this->setFilename($filename);
}
/**
* Create a new Attachment from a filesystem path.
*
* @param string $path
* @param string $contentType optional
*
* @return self
*/
public static function fromPath($path, $contentType = null)
{
return (new self())->setFile(
new Swift_ByteStream_FileByteStream($path),
$contentType
);
}
}

View File

@@ -0,0 +1,176 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Provides the base functionality for an InputStream supporting filters.
*
* @author Chris Corbyn
*/
abstract class Swift_ByteStream_AbstractFilterableInputStream implements Swift_InputByteStream, Swift_Filterable
{
/**
* Write sequence.
*/
protected $sequence = 0;
/**
* StreamFilters.
*
* @var Swift_StreamFilter[]
*/
private $filters = [];
/**
* A buffer for writing.
*/
private $writeBuffer = '';
/**
* Bound streams.
*
* @var Swift_InputByteStream[]
*/
private $mirrors = [];
/**
* Commit the given bytes to the storage medium immediately.
*
* @param string $bytes
*/
abstract protected function doCommit($bytes);
/**
* Flush any buffers/content with immediate effect.
*/
abstract protected function flush();
/**
* Add a StreamFilter to this InputByteStream.
*
* @param string $key
*/
public function addFilter(Swift_StreamFilter $filter, $key)
{
$this->filters[$key] = $filter;
}
/**
* Remove an already present StreamFilter based on its $key.
*
* @param string $key
*/
public function removeFilter($key)
{
unset($this->filters[$key]);
}
/**
* Writes $bytes to the end of the stream.
*
* @param string $bytes
*
* @throws Swift_IoException
*
* @return int
*/
public function write($bytes)
{
$this->writeBuffer .= $bytes;
foreach ($this->filters as $filter) {
if ($filter->shouldBuffer($this->writeBuffer)) {
return;
}
}
$this->doWrite($this->writeBuffer);
return ++$this->sequence;
}
/**
* For any bytes that are currently buffered inside the stream, force them
* off the buffer.
*
* @throws Swift_IoException
*/
public function commit()
{
$this->doWrite($this->writeBuffer);
}
/**
* Attach $is to this stream.
*
* The stream acts as an observer, receiving all data that is written.
* All {@link write()} and {@link flushBuffers()} operations will be mirrored.
*/
public function bind(Swift_InputByteStream $is)
{
$this->mirrors[] = $is;
}
/**
* Remove an already bound stream.
*
* If $is is not bound, no errors will be raised.
* If the stream currently has any buffered data it will be written to $is
* before unbinding occurs.
*/
public function unbind(Swift_InputByteStream $is)
{
foreach ($this->mirrors as $k => $stream) {
if ($is === $stream) {
if ('' !== $this->writeBuffer) {
$stream->write($this->writeBuffer);
}
unset($this->mirrors[$k]);
}
}
}
/**
* Flush the contents of the stream (empty it) and set the internal pointer
* to the beginning.
*
* @throws Swift_IoException
*/
public function flushBuffers()
{
if ('' !== $this->writeBuffer) {
$this->doWrite($this->writeBuffer);
}
$this->flush();
foreach ($this->mirrors as $stream) {
$stream->flushBuffers();
}
}
/** Run $bytes through all filters */
private function filter($bytes)
{
foreach ($this->filters as $filter) {
$bytes = $filter->filter($bytes);
}
return $bytes;
}
/** Just write the bytes to the stream */
private function doWrite($bytes)
{
$this->doCommit($this->filter($bytes));
foreach ($this->mirrors as $stream) {
$stream->write($bytes);
}
$this->writeBuffer = '';
}
}

View File

@@ -0,0 +1,178 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Allows reading and writing of bytes to and from an array.
*
* @author Chris Corbyn
*/
class Swift_ByteStream_ArrayByteStream implements Swift_InputByteStream, Swift_OutputByteStream
{
/**
* The internal stack of bytes.
*
* @var string[]
*/
private $array = [];
/**
* The size of the stack.
*
* @var int
*/
private $arraySize = 0;
/**
* The internal pointer offset.
*
* @var int
*/
private $offset = 0;
/**
* Bound streams.
*
* @var Swift_InputByteStream[]
*/
private $mirrors = [];
/**
* Create a new ArrayByteStream.
*
* If $stack is given the stream will be populated with the bytes it contains.
*
* @param mixed $stack of bytes in string or array form, optional
*/
public function __construct($stack = null)
{
if (\is_array($stack)) {
$this->array = $stack;
$this->arraySize = \count($stack);
} elseif (\is_string($stack)) {
$this->write($stack);
} else {
$this->array = [];
}
}
/**
* Reads $length bytes from the stream into a string and moves the pointer
* through the stream by $length.
*
* If less bytes exist than are requested the
* remaining bytes are given instead. If no bytes are remaining at all, boolean
* false is returned.
*
* @param int $length
*
* @return string
*/
public function read($length)
{
if ($this->offset == $this->arraySize) {
return false;
}
// Don't use array slice
$end = $length + $this->offset;
$end = $this->arraySize < $end ? $this->arraySize : $end;
$ret = '';
for (; $this->offset < $end; ++$this->offset) {
$ret .= $this->array[$this->offset];
}
return $ret;
}
/**
* Writes $bytes to the end of the stream.
*
* @param string $bytes
*/
public function write($bytes)
{
$to_add = str_split($bytes);
foreach ($to_add as $value) {
$this->array[] = $value;
}
$this->arraySize = \count($this->array);
foreach ($this->mirrors as $stream) {
$stream->write($bytes);
}
}
/**
* Not used.
*/
public function commit()
{
}
/**
* Attach $is to this stream.
*
* The stream acts as an observer, receiving all data that is written.
* All {@link write()} and {@link flushBuffers()} operations will be mirrored.
*/
public function bind(Swift_InputByteStream $is)
{
$this->mirrors[] = $is;
}
/**
* Remove an already bound stream.
*
* If $is is not bound, no errors will be raised.
* If the stream currently has any buffered data it will be written to $is
* before unbinding occurs.
*/
public function unbind(Swift_InputByteStream $is)
{
foreach ($this->mirrors as $k => $stream) {
if ($is === $stream) {
unset($this->mirrors[$k]);
}
}
}
/**
* Move the internal read pointer to $byteOffset in the stream.
*
* @param int $byteOffset
*
* @return bool
*/
public function setReadPointer($byteOffset)
{
if ($byteOffset > $this->arraySize) {
$byteOffset = $this->arraySize;
} elseif ($byteOffset < 0) {
$byteOffset = 0;
}
$this->offset = $byteOffset;
}
/**
* Flush the contents of the stream (empty it) and set the internal pointer
* to the beginning.
*/
public function flushBuffers()
{
$this->offset = 0;
$this->array = [];
$this->arraySize = 0;
foreach ($this->mirrors as $stream) {
$stream->flushBuffers();
}
}
}

View File

@@ -0,0 +1,214 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Allows reading and writing of bytes to and from a file.
*
* @author Chris Corbyn
*/
class Swift_ByteStream_FileByteStream extends Swift_ByteStream_AbstractFilterableInputStream implements Swift_FileStream
{
/** The internal pointer offset */
private $offset = 0;
/** The path to the file */
private $path;
/** The mode this file is opened in for writing */
private $mode;
/** A lazy-loaded resource handle for reading the file */
private $reader;
/** A lazy-loaded resource handle for writing the file */
private $writer;
/** If stream is seekable true/false, or null if not known */
private $seekable = null;
/**
* Create a new FileByteStream for $path.
*
* @param string $path
* @param bool $writable if true
*/
public function __construct($path, $writable = false)
{
if (empty($path)) {
throw new Swift_IoException('The path cannot be empty');
}
$this->path = $path;
$this->mode = $writable ? 'w+b' : 'rb';
}
/**
* Get the complete path to the file.
*
* @return string
*/
public function getPath()
{
return $this->path;
}
/**
* Reads $length bytes from the stream into a string and moves the pointer
* through the stream by $length.
*
* If less bytes exist than are requested the
* remaining bytes are given instead. If no bytes are remaining at all, boolean
* false is returned.
*
* @param int $length
*
* @return string|bool
*
* @throws Swift_IoException
*/
public function read($length)
{
$fp = $this->getReadHandle();
if (!feof($fp)) {
$bytes = fread($fp, $length);
$this->offset = ftell($fp);
// If we read one byte after reaching the end of the file
// feof() will return false and an empty string is returned
if ((false === $bytes || '' === $bytes) && feof($fp)) {
$this->resetReadHandle();
return false;
}
return $bytes;
}
$this->resetReadHandle();
return false;
}
/**
* Move the internal read pointer to $byteOffset in the stream.
*
* @param int $byteOffset
*
* @return bool
*/
public function setReadPointer($byteOffset)
{
if (isset($this->reader)) {
$this->seekReadStreamToPosition($byteOffset);
}
$this->offset = $byteOffset;
}
/** Just write the bytes to the file */
protected function doCommit($bytes)
{
fwrite($this->getWriteHandle(), $bytes);
$this->resetReadHandle();
}
/** Not used */
protected function flush()
{
}
/** Get the resource for reading */
private function getReadHandle()
{
if (!isset($this->reader)) {
$pointer = @fopen($this->path, 'rb');
if (!$pointer) {
throw new Swift_IoException('Unable to open file for reading ['.$this->path.']');
}
$this->reader = $pointer;
if (0 != $this->offset) {
$this->getReadStreamSeekableStatus();
$this->seekReadStreamToPosition($this->offset);
}
}
return $this->reader;
}
/** Get the resource for writing */
private function getWriteHandle()
{
if (!isset($this->writer)) {
if (!$this->writer = fopen($this->path, $this->mode)) {
throw new Swift_IoException('Unable to open file for writing ['.$this->path.']');
}
}
return $this->writer;
}
/** Force a reload of the resource for reading */
private function resetReadHandle()
{
if (isset($this->reader)) {
fclose($this->reader);
$this->reader = null;
}
}
/** Check if ReadOnly Stream is seekable */
private function getReadStreamSeekableStatus()
{
$metas = stream_get_meta_data($this->reader);
$this->seekable = $metas['seekable'];
}
/** Streams in a readOnly stream ensuring copy if needed */
private function seekReadStreamToPosition($offset)
{
if (null === $this->seekable) {
$this->getReadStreamSeekableStatus();
}
if (false === $this->seekable) {
$currentPos = ftell($this->reader);
if ($currentPos < $offset) {
$toDiscard = $offset - $currentPos;
fread($this->reader, $toDiscard);
return;
}
$this->copyReadStream();
}
fseek($this->reader, $offset, SEEK_SET);
}
/** Copy a readOnly Stream to ensure seekability */
private function copyReadStream()
{
if ($tmpFile = fopen('php://temp/maxmemory:4096', 'w+b')) {
/* We have opened a php:// Stream Should work without problem */
} elseif (\function_exists('sys_get_temp_dir') && is_writable(sys_get_temp_dir()) && ($tmpFile = tmpfile())) {
/* We have opened a tmpfile */
} else {
throw new Swift_IoException('Unable to copy the file to make it seekable, sys_temp_dir is not writable, php://memory not available');
}
$currentPos = ftell($this->reader);
fclose($this->reader);
$source = fopen($this->path, 'rb');
if (!$source) {
throw new Swift_IoException('Unable to open file for copying ['.$this->path.']');
}
fseek($tmpFile, 0, SEEK_SET);
while (!feof($source)) {
fwrite($tmpFile, fread($source, 4096));
}
fseek($tmpFile, $currentPos, SEEK_SET);
fclose($source);
$this->reader = $tmpFile;
}
}

View File

@@ -0,0 +1,52 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* @author Romain-Geissler
*/
class Swift_ByteStream_TemporaryFileByteStream extends Swift_ByteStream_FileByteStream
{
public function __construct()
{
$filePath = tempnam(sys_get_temp_dir(), 'FileByteStream');
if (false === $filePath) {
throw new Swift_IoException('Failed to retrieve temporary file name.');
}
parent::__construct($filePath, true);
}
public function getContent()
{
if (false === ($content = file_get_contents($this->getPath()))) {
throw new Swift_IoException('Failed to get temporary file content.');
}
return $content;
}
public function __destruct()
{
if (file_exists($this->getPath())) {
@unlink($this->getPath());
}
}
public function __sleep()
{
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
}
public function __wakeup()
{
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}
}

View File

@@ -0,0 +1,67 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Analyzes characters for a specific character set.
*
* @author Chris Corbyn
* @author Xavier De Cock <xdecock@gmail.com>
*/
interface Swift_CharacterReader
{
const MAP_TYPE_INVALID = 0x01;
const MAP_TYPE_FIXED_LEN = 0x02;
const MAP_TYPE_POSITIONS = 0x03;
/**
* Returns the complete character map.
*
* @param string $string
* @param int $startOffset
* @param array $currentMap
* @param mixed $ignoredChars
*
* @return int
*/
public function getCharPositions($string, $startOffset, &$currentMap, &$ignoredChars);
/**
* Returns the mapType, see constants.
*
* @return int
*/
public function getMapType();
/**
* Returns an integer which specifies how many more bytes to read.
*
* A positive integer indicates the number of more bytes to fetch before invoking
* this method again.
*
* A value of zero means this is already a valid character.
* A value of -1 means this cannot possibly be a valid character.
*
* @param int[] $bytes
* @param int $size
*
* @return int
*/
public function validateByteSequence($bytes, $size);
/**
* Returns the number of bytes which should be read to start each character.
*
* For fixed width character sets this should be the number of octets-per-character.
* For multibyte character sets this will probably be 1.
*
* @return int
*/
public function getInitialByteSize();
}

View File

@@ -0,0 +1,97 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Provides fixed-width byte sizes for reading fixed-width character sets.
*
* @author Chris Corbyn
* @author Xavier De Cock <xdecock@gmail.com>
*/
class Swift_CharacterReader_GenericFixedWidthReader implements Swift_CharacterReader
{
/**
* The number of bytes in a single character.
*
* @var int
*/
private $width;
/**
* Creates a new GenericFixedWidthReader using $width bytes per character.
*
* @param int $width
*/
public function __construct($width)
{
$this->width = $width;
}
/**
* Returns the complete character map.
*
* @param string $string
* @param int $startOffset
* @param array $currentMap
* @param mixed $ignoredChars
*
* @return int
*/
public function getCharPositions($string, $startOffset, &$currentMap, &$ignoredChars)
{
$strlen = \strlen($string);
// % and / are CPU intensive, so, maybe find a better way
$ignored = $strlen % $this->width;
$ignoredChars = $ignored ? substr($string, -$ignored) : '';
$currentMap = $this->width;
return ($strlen - $ignored) / $this->width;
}
/**
* Returns the mapType.
*
* @return int
*/
public function getMapType()
{
return self::MAP_TYPE_FIXED_LEN;
}
/**
* Returns an integer which specifies how many more bytes to read.
*
* A positive integer indicates the number of more bytes to fetch before invoking
* this method again.
*
* A value of zero means this is already a valid character.
* A value of -1 means this cannot possibly be a valid character.
*
* @param string $bytes
* @param int $size
*
* @return int
*/
public function validateByteSequence($bytes, $size)
{
$needed = $this->width - $size;
return $needed > -1 ? $needed : -1;
}
/**
* Returns the number of bytes which should be read to start each character.
*
* @return int
*/
public function getInitialByteSize()
{
return $this->width;
}
}

View File

@@ -0,0 +1,84 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Analyzes US-ASCII characters.
*
* @author Chris Corbyn
*/
class Swift_CharacterReader_UsAsciiReader implements Swift_CharacterReader
{
/**
* Returns the complete character map.
*
* @param string $string
* @param int $startOffset
* @param array $currentMap
* @param string $ignoredChars
*
* @return int
*/
public function getCharPositions($string, $startOffset, &$currentMap, &$ignoredChars)
{
$strlen = \strlen($string);
$ignoredChars = '';
for ($i = 0; $i < $strlen; ++$i) {
if ($string[$i] > "\x07F") {
// Invalid char
$currentMap[$i + $startOffset] = $string[$i];
}
}
return $strlen;
}
/**
* Returns mapType.
*
* @return int mapType
*/
public function getMapType()
{
return self::MAP_TYPE_INVALID;
}
/**
* Returns an integer which specifies how many more bytes to read.
*
* A positive integer indicates the number of more bytes to fetch before invoking
* this method again.
* A value of zero means this is already a valid character.
* A value of -1 means this cannot possibly be a valid character.
*
* @param string $bytes
* @param int $size
*
* @return int
*/
public function validateByteSequence($bytes, $size)
{
$byte = reset($bytes);
if (1 == \count($bytes) && $byte >= 0x00 && $byte <= 0x7F) {
return 0;
}
return -1;
}
/**
* Returns the number of bytes which should be read to start each character.
*
* @return int
*/
public function getInitialByteSize()
{
return 1;
}
}

View File

@@ -0,0 +1,176 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Analyzes UTF-8 characters.
*
* @author Chris Corbyn
* @author Xavier De Cock <xdecock@gmail.com>
*/
class Swift_CharacterReader_Utf8Reader implements Swift_CharacterReader
{
/** Pre-computed for optimization */
private static $length_map = [
// N=0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x0N
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x1N
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x2N
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x3N
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x4N
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x5N
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x6N
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x7N
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x8N
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x9N
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xAN
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xBN
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xCN
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xDN
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xEN
4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 0, 0, // 0xFN
];
private static $s_length_map = [
"\x00" => 1, "\x01" => 1, "\x02" => 1, "\x03" => 1, "\x04" => 1, "\x05" => 1, "\x06" => 1, "\x07" => 1,
"\x08" => 1, "\x09" => 1, "\x0a" => 1, "\x0b" => 1, "\x0c" => 1, "\x0d" => 1, "\x0e" => 1, "\x0f" => 1,
"\x10" => 1, "\x11" => 1, "\x12" => 1, "\x13" => 1, "\x14" => 1, "\x15" => 1, "\x16" => 1, "\x17" => 1,
"\x18" => 1, "\x19" => 1, "\x1a" => 1, "\x1b" => 1, "\x1c" => 1, "\x1d" => 1, "\x1e" => 1, "\x1f" => 1,
"\x20" => 1, "\x21" => 1, "\x22" => 1, "\x23" => 1, "\x24" => 1, "\x25" => 1, "\x26" => 1, "\x27" => 1,
"\x28" => 1, "\x29" => 1, "\x2a" => 1, "\x2b" => 1, "\x2c" => 1, "\x2d" => 1, "\x2e" => 1, "\x2f" => 1,
"\x30" => 1, "\x31" => 1, "\x32" => 1, "\x33" => 1, "\x34" => 1, "\x35" => 1, "\x36" => 1, "\x37" => 1,
"\x38" => 1, "\x39" => 1, "\x3a" => 1, "\x3b" => 1, "\x3c" => 1, "\x3d" => 1, "\x3e" => 1, "\x3f" => 1,
"\x40" => 1, "\x41" => 1, "\x42" => 1, "\x43" => 1, "\x44" => 1, "\x45" => 1, "\x46" => 1, "\x47" => 1,
"\x48" => 1, "\x49" => 1, "\x4a" => 1, "\x4b" => 1, "\x4c" => 1, "\x4d" => 1, "\x4e" => 1, "\x4f" => 1,
"\x50" => 1, "\x51" => 1, "\x52" => 1, "\x53" => 1, "\x54" => 1, "\x55" => 1, "\x56" => 1, "\x57" => 1,
"\x58" => 1, "\x59" => 1, "\x5a" => 1, "\x5b" => 1, "\x5c" => 1, "\x5d" => 1, "\x5e" => 1, "\x5f" => 1,
"\x60" => 1, "\x61" => 1, "\x62" => 1, "\x63" => 1, "\x64" => 1, "\x65" => 1, "\x66" => 1, "\x67" => 1,
"\x68" => 1, "\x69" => 1, "\x6a" => 1, "\x6b" => 1, "\x6c" => 1, "\x6d" => 1, "\x6e" => 1, "\x6f" => 1,
"\x70" => 1, "\x71" => 1, "\x72" => 1, "\x73" => 1, "\x74" => 1, "\x75" => 1, "\x76" => 1, "\x77" => 1,
"\x78" => 1, "\x79" => 1, "\x7a" => 1, "\x7b" => 1, "\x7c" => 1, "\x7d" => 1, "\x7e" => 1, "\x7f" => 1,
"\x80" => 0, "\x81" => 0, "\x82" => 0, "\x83" => 0, "\x84" => 0, "\x85" => 0, "\x86" => 0, "\x87" => 0,
"\x88" => 0, "\x89" => 0, "\x8a" => 0, "\x8b" => 0, "\x8c" => 0, "\x8d" => 0, "\x8e" => 0, "\x8f" => 0,
"\x90" => 0, "\x91" => 0, "\x92" => 0, "\x93" => 0, "\x94" => 0, "\x95" => 0, "\x96" => 0, "\x97" => 0,
"\x98" => 0, "\x99" => 0, "\x9a" => 0, "\x9b" => 0, "\x9c" => 0, "\x9d" => 0, "\x9e" => 0, "\x9f" => 0,
"\xa0" => 0, "\xa1" => 0, "\xa2" => 0, "\xa3" => 0, "\xa4" => 0, "\xa5" => 0, "\xa6" => 0, "\xa7" => 0,
"\xa8" => 0, "\xa9" => 0, "\xaa" => 0, "\xab" => 0, "\xac" => 0, "\xad" => 0, "\xae" => 0, "\xaf" => 0,
"\xb0" => 0, "\xb1" => 0, "\xb2" => 0, "\xb3" => 0, "\xb4" => 0, "\xb5" => 0, "\xb6" => 0, "\xb7" => 0,
"\xb8" => 0, "\xb9" => 0, "\xba" => 0, "\xbb" => 0, "\xbc" => 0, "\xbd" => 0, "\xbe" => 0, "\xbf" => 0,
"\xc0" => 2, "\xc1" => 2, "\xc2" => 2, "\xc3" => 2, "\xc4" => 2, "\xc5" => 2, "\xc6" => 2, "\xc7" => 2,
"\xc8" => 2, "\xc9" => 2, "\xca" => 2, "\xcb" => 2, "\xcc" => 2, "\xcd" => 2, "\xce" => 2, "\xcf" => 2,
"\xd0" => 2, "\xd1" => 2, "\xd2" => 2, "\xd3" => 2, "\xd4" => 2, "\xd5" => 2, "\xd6" => 2, "\xd7" => 2,
"\xd8" => 2, "\xd9" => 2, "\xda" => 2, "\xdb" => 2, "\xdc" => 2, "\xdd" => 2, "\xde" => 2, "\xdf" => 2,
"\xe0" => 3, "\xe1" => 3, "\xe2" => 3, "\xe3" => 3, "\xe4" => 3, "\xe5" => 3, "\xe6" => 3, "\xe7" => 3,
"\xe8" => 3, "\xe9" => 3, "\xea" => 3, "\xeb" => 3, "\xec" => 3, "\xed" => 3, "\xee" => 3, "\xef" => 3,
"\xf0" => 4, "\xf1" => 4, "\xf2" => 4, "\xf3" => 4, "\xf4" => 4, "\xf5" => 4, "\xf6" => 4, "\xf7" => 4,
"\xf8" => 5, "\xf9" => 5, "\xfa" => 5, "\xfb" => 5, "\xfc" => 6, "\xfd" => 6, "\xfe" => 0, "\xff" => 0,
];
/**
* Returns the complete character map.
*
* @param string $string
* @param int $startOffset
* @param array $currentMap
* @param mixed $ignoredChars
*
* @return int
*/
public function getCharPositions($string, $startOffset, &$currentMap, &$ignoredChars)
{
if (!isset($currentMap['i']) || !isset($currentMap['p'])) {
$currentMap['p'] = $currentMap['i'] = [];
}
$strlen = \strlen($string);
$charPos = \count($currentMap['p']);
$foundChars = 0;
$invalid = false;
for ($i = 0; $i < $strlen; ++$i) {
$char = $string[$i];
$size = self::$s_length_map[$char];
if (0 == $size) {
/* char is invalid, we must wait for a resync */
$invalid = true;
continue;
} else {
if (true === $invalid) {
/* We mark the chars as invalid and start a new char */
$currentMap['p'][$charPos + $foundChars] = $startOffset + $i;
$currentMap['i'][$charPos + $foundChars] = true;
++$foundChars;
$invalid = false;
}
if (($i + $size) > $strlen) {
$ignoredChars = substr($string, $i);
break;
}
for ($j = 1; $j < $size; ++$j) {
$char = $string[$i + $j];
if ($char > "\x7F" && $char < "\xC0") {
// Valid - continue parsing
} else {
/* char is invalid, we must wait for a resync */
$invalid = true;
continue 2;
}
}
/* Ok we got a complete char here */
$currentMap['p'][$charPos + $foundChars] = $startOffset + $i + $size;
$i += $j - 1;
++$foundChars;
}
}
return $foundChars;
}
/**
* Returns mapType.
*
* @return int mapType
*/
public function getMapType()
{
return self::MAP_TYPE_POSITIONS;
}
/**
* Returns an integer which specifies how many more bytes to read.
*
* A positive integer indicates the number of more bytes to fetch before invoking
* this method again.
* A value of zero means this is already a valid character.
* A value of -1 means this cannot possibly be a valid character.
*
* @param string $bytes
* @param int $size
*
* @return int
*/
public function validateByteSequence($bytes, $size)
{
if ($size < 1) {
return -1;
}
$needed = self::$length_map[$bytes[0]] - $size;
return $needed > -1 ? $needed : -1;
}
/**
* Returns the number of bytes which should be read to start each character.
*
* @return int
*/
public function getInitialByteSize()
{
return 1;
}
}

View File

@@ -0,0 +1,26 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* A factory for creating CharacterReaders.
*
* @author Chris Corbyn
*/
interface Swift_CharacterReaderFactory
{
/**
* Returns a CharacterReader suitable for the charset applied.
*
* @param string $charset
*
* @return Swift_CharacterReader
*/
public function getReaderFor($charset);
}

View File

@@ -0,0 +1,124 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Standard factory for creating CharacterReaders.
*
* @author Chris Corbyn
*/
class Swift_CharacterReaderFactory_SimpleCharacterReaderFactory implements Swift_CharacterReaderFactory
{
/**
* A map of charset patterns to their implementation classes.
*
* @var array
*/
private static $map = [];
/**
* Factories which have already been loaded.
*
* @var Swift_CharacterReaderFactory[]
*/
private static $loaded = [];
/**
* Creates a new CharacterReaderFactory.
*/
public function __construct()
{
$this->init();
}
public function __wakeup()
{
$this->init();
}
public function init()
{
if (\count(self::$map) > 0) {
return;
}
$prefix = 'Swift_CharacterReader_';
$singleByte = [
'class' => $prefix.'GenericFixedWidthReader',
'constructor' => [1],
];
$doubleByte = [
'class' => $prefix.'GenericFixedWidthReader',
'constructor' => [2],
];
$fourBytes = [
'class' => $prefix.'GenericFixedWidthReader',
'constructor' => [4],
];
// Utf-8
self::$map['utf-?8'] = [
'class' => $prefix.'Utf8Reader',
'constructor' => [],
];
//7-8 bit charsets
self::$map['(us-)?ascii'] = $singleByte;
self::$map['(iso|iec)-?8859-?[0-9]+'] = $singleByte;
self::$map['windows-?125[0-9]'] = $singleByte;
self::$map['cp-?[0-9]+'] = $singleByte;
self::$map['ansi'] = $singleByte;
self::$map['macintosh'] = $singleByte;
self::$map['koi-?7'] = $singleByte;
self::$map['koi-?8-?.+'] = $singleByte;
self::$map['mik'] = $singleByte;
self::$map['(cork|t1)'] = $singleByte;
self::$map['v?iscii'] = $singleByte;
//16 bits
self::$map['(ucs-?2|utf-?16)'] = $doubleByte;
//32 bits
self::$map['(ucs-?4|utf-?32)'] = $fourBytes;
// Fallback
self::$map['.*'] = $singleByte;
}
/**
* Returns a CharacterReader suitable for the charset applied.
*
* @param string $charset
*
* @return Swift_CharacterReader
*/
public function getReaderFor($charset)
{
$charset = strtolower(trim($charset ?? ''));
foreach (self::$map as $pattern => $spec) {
$re = '/^'.$pattern.'$/D';
if (preg_match($re, $charset)) {
if (!\array_key_exists($pattern, self::$loaded)) {
$reflector = new ReflectionClass($spec['class']);
if ($reflector->getConstructor()) {
$reader = $reflector->newInstanceArgs($spec['constructor']);
} else {
$reader = $reflector->newInstance();
}
self::$loaded[$pattern] = $reader;
}
return self::$loaded[$pattern];
}
}
}
}

View File

@@ -0,0 +1,87 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* An abstract means of reading and writing data in terms of characters as opposed
* to bytes.
*
* Classes implementing this interface may use a subsystem which requires less
* memory than working with large strings of data.
*
* @author Chris Corbyn
*/
interface Swift_CharacterStream
{
/**
* Set the character set used in this CharacterStream.
*
* @param string $charset
*/
public function setCharacterSet($charset);
/**
* Set the CharacterReaderFactory for multi charset support.
*/
public function setCharacterReaderFactory(Swift_CharacterReaderFactory $factory);
/**
* Overwrite this character stream using the byte sequence in the byte stream.
*
* @param Swift_OutputByteStream $os output stream to read from
*/
public function importByteStream(Swift_OutputByteStream $os);
/**
* Import a string a bytes into this CharacterStream, overwriting any existing
* data in the stream.
*
* @param string $string
*/
public function importString($string);
/**
* Read $length characters from the stream and move the internal pointer
* $length further into the stream.
*
* @param int $length
*
* @return string
*/
public function read($length);
/**
* Read $length characters from the stream and return a 1-dimensional array
* containing there octet values.
*
* @param int $length
*
* @return int[]
*/
public function readBytes($length);
/**
* Write $chars to the end of the stream.
*
* @param string $chars
*/
public function write($chars);
/**
* Move the internal pointer to $charOffset in the stream.
*
* @param int $charOffset
*/
public function setPointer($charOffset);
/**
* Empty the stream and reset the internal pointer.
*/
public function flushContents();
}

View File

@@ -0,0 +1,291 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* A CharacterStream implementation which stores characters in an internal array.
*
* @author Chris Corbyn
*/
class Swift_CharacterStream_ArrayCharacterStream implements Swift_CharacterStream
{
/** A map of byte values and their respective characters */
private static $charMap;
/** A map of characters and their derivative byte values */
private static $byteMap;
/** The char reader (lazy-loaded) for the current charset */
private $charReader;
/** A factory for creating CharacterReader instances */
private $charReaderFactory;
/** The character set this stream is using */
private $charset;
/** Array of characters */
private $array = [];
/** Size of the array of character */
private $array_size = [];
/** The current character offset in the stream */
private $offset = 0;
/**
* Create a new CharacterStream with the given $chars, if set.
*
* @param Swift_CharacterReaderFactory $factory for loading validators
* @param string $charset used in the stream
*/
public function __construct(Swift_CharacterReaderFactory $factory, $charset)
{
self::initializeMaps();
$this->setCharacterReaderFactory($factory);
$this->setCharacterSet($charset);
}
/**
* Set the character set used in this CharacterStream.
*
* @param string $charset
*/
public function setCharacterSet($charset)
{
$this->charset = $charset;
$this->charReader = null;
}
/**
* Set the CharacterReaderFactory for multi charset support.
*/
public function setCharacterReaderFactory(Swift_CharacterReaderFactory $factory)
{
$this->charReaderFactory = $factory;
}
/**
* Overwrite this character stream using the byte sequence in the byte stream.
*
* @param Swift_OutputByteStream $os output stream to read from
*/
public function importByteStream(Swift_OutputByteStream $os)
{
if (!isset($this->charReader)) {
$this->charReader = $this->charReaderFactory
->getReaderFor($this->charset);
}
$startLength = $this->charReader->getInitialByteSize();
while (false !== $bytes = $os->read($startLength)) {
$c = [];
for ($i = 0, $len = \strlen($bytes); $i < $len; ++$i) {
$c[] = self::$byteMap[$bytes[$i]];
}
$size = \count($c);
$need = $this->charReader
->validateByteSequence($c, $size);
if ($need > 0 &&
false !== $bytes = $os->read($need)) {
for ($i = 0, $len = \strlen($bytes); $i < $len; ++$i) {
$c[] = self::$byteMap[$bytes[$i]];
}
}
$this->array[] = $c;
++$this->array_size;
}
}
/**
* Import a string a bytes into this CharacterStream, overwriting any existing
* data in the stream.
*
* @param string $string
*/
public function importString($string)
{
$this->flushContents();
$this->write($string);
}
/**
* Read $length characters from the stream and move the internal pointer
* $length further into the stream.
*
* @param int $length
*
* @return string
*/
public function read($length)
{
if ($this->offset == $this->array_size) {
return false;
}
// Don't use array slice
$arrays = [];
$end = $length + $this->offset;
for ($i = $this->offset; $i < $end; ++$i) {
if (!isset($this->array[$i])) {
break;
}
$arrays[] = $this->array[$i];
}
$this->offset += $i - $this->offset; // Limit function calls
$chars = false;
foreach ($arrays as $array) {
$chars .= implode('', array_map('chr', $array));
}
return $chars;
}
/**
* Read $length characters from the stream and return a 1-dimensional array
* containing there octet values.
*
* @param int $length
*
* @return int[]
*/
public function readBytes($length)
{
if ($this->offset == $this->array_size) {
return false;
}
$arrays = [];
$end = $length + $this->offset;
for ($i = $this->offset; $i < $end; ++$i) {
if (!isset($this->array[$i])) {
break;
}
$arrays[] = $this->array[$i];
}
$this->offset += ($i - $this->offset); // Limit function calls
return array_merge(...$arrays);
}
/**
* Write $chars to the end of the stream.
*
* @param string $chars
*/
public function write($chars)
{
if (!isset($this->charReader)) {
$this->charReader = $this->charReaderFactory->getReaderFor(
$this->charset);
}
$startLength = $this->charReader->getInitialByteSize();
$fp = fopen('php://memory', 'w+b');
fwrite($fp, $chars);
unset($chars);
fseek($fp, 0, SEEK_SET);
$buffer = [0];
$buf_pos = 1;
$buf_len = 1;
$has_datas = true;
do {
$bytes = [];
// Buffer Filing
if ($buf_len - $buf_pos < $startLength) {
$buf = array_splice($buffer, $buf_pos);
$new = $this->reloadBuffer($fp, 100);
if ($new) {
$buffer = array_merge($buf, $new);
$buf_len = \count($buffer);
$buf_pos = 0;
} else {
$has_datas = false;
}
}
if ($buf_len - $buf_pos > 0) {
$size = 0;
for ($i = 0; $i < $startLength && isset($buffer[$buf_pos]); ++$i) {
++$size;
$bytes[] = $buffer[$buf_pos++];
}
$need = $this->charReader->validateByteSequence(
$bytes, $size);
if ($need > 0) {
if ($buf_len - $buf_pos < $need) {
$new = $this->reloadBuffer($fp, $need);
if ($new) {
$buffer = array_merge($buffer, $new);
$buf_len = \count($buffer);
}
}
for ($i = 0; $i < $need && isset($buffer[$buf_pos]); ++$i) {
$bytes[] = $buffer[$buf_pos++];
}
}
$this->array[] = $bytes;
++$this->array_size;
}
} while ($has_datas);
fclose($fp);
}
/**
* Move the internal pointer to $charOffset in the stream.
*
* @param int $charOffset
*/
public function setPointer($charOffset)
{
if ($charOffset > $this->array_size) {
$charOffset = $this->array_size;
} elseif ($charOffset < 0) {
$charOffset = 0;
}
$this->offset = $charOffset;
}
/**
* Empty the stream and reset the internal pointer.
*/
public function flushContents()
{
$this->offset = 0;
$this->array = [];
$this->array_size = 0;
}
private function reloadBuffer($fp, $len)
{
if (!feof($fp) && false !== ($bytes = fread($fp, $len))) {
$buf = [];
for ($i = 0, $len = \strlen($bytes); $i < $len; ++$i) {
$buf[] = self::$byteMap[$bytes[$i]];
}
return $buf;
}
return false;
}
private static function initializeMaps()
{
if (!isset(self::$charMap)) {
self::$charMap = [];
for ($byte = 0; $byte < 256; ++$byte) {
self::$charMap[$byte] = \chr($byte);
}
self::$byteMap = array_flip(self::$charMap);
}
}
}

View File

@@ -0,0 +1,262 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* A CharacterStream implementation which stores characters in an internal array.
*
* @author Xavier De Cock <xdecock@gmail.com>
*/
class Swift_CharacterStream_NgCharacterStream implements Swift_CharacterStream
{
/**
* The char reader (lazy-loaded) for the current charset.
*
* @var Swift_CharacterReader
*/
private $charReader;
/**
* A factory for creating CharacterReader instances.
*
* @var Swift_CharacterReaderFactory
*/
private $charReaderFactory;
/**
* The character set this stream is using.
*
* @var string
*/
private $charset;
/**
* The data's stored as-is.
*
* @var string
*/
private $datas = '';
/**
* Number of bytes in the stream.
*
* @var int
*/
private $datasSize = 0;
/**
* Map.
*
* @var mixed
*/
private $map;
/**
* Map Type.
*
* @var int
*/
private $mapType = 0;
/**
* Number of characters in the stream.
*
* @var int
*/
private $charCount = 0;
/**
* Position in the stream.
*
* @var int
*/
private $currentPos = 0;
/**
* Constructor.
*
* @param string $charset
*/
public function __construct(Swift_CharacterReaderFactory $factory, $charset)
{
$this->setCharacterReaderFactory($factory);
$this->setCharacterSet($charset);
}
/* -- Changing parameters of the stream -- */
/**
* Set the character set used in this CharacterStream.
*
* @param string $charset
*/
public function setCharacterSet($charset)
{
$this->charset = $charset;
$this->charReader = null;
$this->mapType = 0;
}
/**
* Set the CharacterReaderFactory for multi charset support.
*/
public function setCharacterReaderFactory(Swift_CharacterReaderFactory $factory)
{
$this->charReaderFactory = $factory;
}
/**
* @see Swift_CharacterStream::flushContents()
*/
public function flushContents()
{
$this->datas = null;
$this->map = null;
$this->charCount = 0;
$this->currentPos = 0;
$this->datasSize = 0;
}
/**
* @see Swift_CharacterStream::importByteStream()
*/
public function importByteStream(Swift_OutputByteStream $os)
{
$this->flushContents();
$blocks = 512;
$os->setReadPointer(0);
while (false !== ($read = $os->read($blocks))) {
$this->write($read);
}
}
/**
* @see Swift_CharacterStream::importString()
*
* @param string $string
*/
public function importString($string)
{
$this->flushContents();
$this->write($string);
}
/**
* @see Swift_CharacterStream::read()
*
* @param int $length
*
* @return string
*/
public function read($length)
{
if ($this->currentPos >= $this->charCount) {
return false;
}
$ret = false;
$length = ($this->currentPos + $length > $this->charCount) ? $this->charCount - $this->currentPos : $length;
switch ($this->mapType) {
case Swift_CharacterReader::MAP_TYPE_FIXED_LEN:
$len = $length * $this->map;
$ret = substr($this->datas,
$this->currentPos * $this->map,
$len);
$this->currentPos += $length;
break;
case Swift_CharacterReader::MAP_TYPE_INVALID:
$ret = '';
for (; $this->currentPos < $length; ++$this->currentPos) {
if (isset($this->map[$this->currentPos])) {
$ret .= '?';
} else {
$ret .= $this->datas[$this->currentPos];
}
}
break;
case Swift_CharacterReader::MAP_TYPE_POSITIONS:
$end = $this->currentPos + $length;
$end = $end > $this->charCount ? $this->charCount : $end;
$ret = '';
$start = 0;
if ($this->currentPos > 0) {
$start = $this->map['p'][$this->currentPos - 1];
}
$to = $start;
for (; $this->currentPos < $end; ++$this->currentPos) {
if (isset($this->map['i'][$this->currentPos])) {
$ret .= substr($this->datas, $start, $to - $start).'?';
$start = $this->map['p'][$this->currentPos];
} else {
$to = $this->map['p'][$this->currentPos];
}
}
$ret .= substr($this->datas, $start, $to - $start);
break;
}
return $ret;
}
/**
* @see Swift_CharacterStream::readBytes()
*
* @param int $length
*
* @return int[]
*/
public function readBytes($length)
{
$read = $this->read($length);
if (false !== $read) {
$ret = array_map('ord', str_split($read, 1));
return $ret;
}
return false;
}
/**
* @see Swift_CharacterStream::setPointer()
*
* @param int $charOffset
*/
public function setPointer($charOffset)
{
if ($this->charCount < $charOffset) {
$charOffset = $this->charCount;
}
$this->currentPos = $charOffset;
}
/**
* @see Swift_CharacterStream::write()
*
* @param string $chars
*/
public function write($chars)
{
if (!isset($this->charReader)) {
$this->charReader = $this->charReaderFactory->getReaderFor(
$this->charset);
$this->map = [];
$this->mapType = $this->charReader->getMapType();
}
$ignored = '';
$this->datas .= $chars;
$this->charCount += $this->charReader->getCharPositions(substr($this->datas, $this->datasSize), $this->datasSize, $this->map, $ignored);
if (false !== $ignored) {
$this->datasSize = \strlen($this->datas) - \strlen($ignored);
} else {
$this->datasSize = \strlen($this->datas);
}
}
}

View File

@@ -0,0 +1,63 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2009 Fabien Potencier <fabien.potencier@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Base class for Spools (implements time and message limits).
*
* @author Fabien Potencier
*/
abstract class Swift_ConfigurableSpool implements Swift_Spool
{
/** The maximum number of messages to send per flush */
private $message_limit;
/** The time limit per flush */
private $time_limit;
/**
* Sets the maximum number of messages to send per flush.
*
* @param int $limit
*/
public function setMessageLimit($limit)
{
$this->message_limit = (int) $limit;
}
/**
* Gets the maximum number of messages to send per flush.
*
* @return int The limit
*/
public function getMessageLimit()
{
return $this->message_limit;
}
/**
* Sets the time limit (in seconds) per flush.
*
* @param int $limit The limit
*/
public function setTimeLimit($limit)
{
$this->time_limit = (int) $limit;
}
/**
* Gets the time limit (in seconds) per flush.
*
* @return int The limit
*/
public function getTimeLimit()
{
return $this->time_limit;
}
}

View File

@@ -0,0 +1,387 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Dependency Injection container.
*
* @author Chris Corbyn
*/
class Swift_DependencyContainer
{
/** Constant for literal value types */
const TYPE_VALUE = 0x00001;
/** Constant for new instance types */
const TYPE_INSTANCE = 0x00010;
/** Constant for shared instance types */
const TYPE_SHARED = 0x00100;
/** Constant for aliases */
const TYPE_ALIAS = 0x01000;
/** Constant for arrays */
const TYPE_ARRAY = 0x10000;
/** Singleton instance */
private static $instance = null;
/** The data container */
private $store = [];
/** The current endpoint in the data container */
private $endPoint;
/**
* Constructor should not be used.
*
* Use {@link getInstance()} instead.
*/
public function __construct()
{
}
/**
* Returns a singleton of the DependencyContainer.
*
* @return self
*/
public static function getInstance()
{
if (!isset(self::$instance)) {
self::$instance = new self();
}
return self::$instance;
}
/**
* List the names of all items stored in the Container.
*
* @return array
*/
public function listItems()
{
return array_keys($this->store);
}
/**
* Test if an item is registered in this container with the given name.
*
* @see register()
*
* @param string $itemName
*
* @return bool
*/
public function has($itemName)
{
return \array_key_exists($itemName, $this->store)
&& isset($this->store[$itemName]['lookupType']);
}
/**
* Lookup the item with the given $itemName.
*
* @see register()
*
* @param string $itemName
*
* @return mixed
*
* @throws Swift_DependencyException If the dependency is not found
*/
public function lookup($itemName)
{
if (!$this->has($itemName)) {
throw new Swift_DependencyException('Cannot lookup dependency "'.$itemName.'" since it is not registered.');
}
switch ($this->store[$itemName]['lookupType']) {
case self::TYPE_ALIAS:
return $this->createAlias($itemName);
case self::TYPE_VALUE:
return $this->getValue($itemName);
case self::TYPE_INSTANCE:
return $this->createNewInstance($itemName);
case self::TYPE_SHARED:
return $this->createSharedInstance($itemName);
case self::TYPE_ARRAY:
return $this->createDependenciesFor($itemName);
}
}
/**
* Create an array of arguments passed to the constructor of $itemName.
*
* @param string $itemName
*
* @return array
*/
public function createDependenciesFor($itemName)
{
$args = [];
if (isset($this->store[$itemName]['args'])) {
$args = $this->resolveArgs($this->store[$itemName]['args']);
}
return $args;
}
/**
* Register a new dependency with $itemName.
*
* This method returns the current DependencyContainer instance because it
* requires the use of the fluid interface to set the specific details for the
* dependency.
*
* @see asNewInstanceOf(), asSharedInstanceOf(), asValue()
*
* @param string $itemName
*
* @return $this
*/
public function register($itemName)
{
$this->store[$itemName] = [];
$this->endPoint = &$this->store[$itemName];
return $this;
}
/**
* Specify the previously registered item as a literal value.
*
* {@link register()} must be called before this will work.
*
* @param mixed $value
*
* @return $this
*/
public function asValue($value)
{
$endPoint = &$this->getEndPoint();
$endPoint['lookupType'] = self::TYPE_VALUE;
$endPoint['value'] = $value;
return $this;
}
/**
* Specify the previously registered item as an alias of another item.
*
* @param string $lookup
*
* @return $this
*/
public function asAliasOf($lookup)
{
$endPoint = &$this->getEndPoint();
$endPoint['lookupType'] = self::TYPE_ALIAS;
$endPoint['ref'] = $lookup;
return $this;
}
/**
* Specify the previously registered item as a new instance of $className.
*
* {@link register()} must be called before this will work.
* Any arguments can be set with {@link withDependencies()},
* {@link addConstructorValue()} or {@link addConstructorLookup()}.
*
* @see withDependencies(), addConstructorValue(), addConstructorLookup()
*
* @param string $className
*
* @return $this
*/
public function asNewInstanceOf($className)
{
$endPoint = &$this->getEndPoint();
$endPoint['lookupType'] = self::TYPE_INSTANCE;
$endPoint['className'] = $className;
return $this;
}
/**
* Specify the previously registered item as a shared instance of $className.
*
* {@link register()} must be called before this will work.
*
* @param string $className
*
* @return $this
*/
public function asSharedInstanceOf($className)
{
$endPoint = &$this->getEndPoint();
$endPoint['lookupType'] = self::TYPE_SHARED;
$endPoint['className'] = $className;
return $this;
}
/**
* Specify the previously registered item as array of dependencies.
*
* {@link register()} must be called before this will work.
*
* @return $this
*/
public function asArray()
{
$endPoint = &$this->getEndPoint();
$endPoint['lookupType'] = self::TYPE_ARRAY;
return $this;
}
/**
* Specify a list of injected dependencies for the previously registered item.
*
* This method takes an array of lookup names.
*
* @see addConstructorValue(), addConstructorLookup()
*
* @return $this
*/
public function withDependencies(array $lookups)
{
$endPoint = &$this->getEndPoint();
$endPoint['args'] = [];
foreach ($lookups as $lookup) {
$this->addConstructorLookup($lookup);
}
return $this;
}
/**
* Specify a literal (non looked up) value for the constructor of the
* previously registered item.
*
* @see withDependencies(), addConstructorLookup()
*
* @param mixed $value
*
* @return $this
*/
public function addConstructorValue($value)
{
$endPoint = &$this->getEndPoint();
if (!isset($endPoint['args'])) {
$endPoint['args'] = [];
}
$endPoint['args'][] = ['type' => 'value', 'item' => $value];
return $this;
}
/**
* Specify a dependency lookup for the constructor of the previously
* registered item.
*
* @see withDependencies(), addConstructorValue()
*
* @param string $lookup
*
* @return $this
*/
public function addConstructorLookup($lookup)
{
$endPoint = &$this->getEndPoint();
if (!isset($this->endPoint['args'])) {
$endPoint['args'] = [];
}
$endPoint['args'][] = ['type' => 'lookup', 'item' => $lookup];
return $this;
}
/** Get the literal value with $itemName */
private function getValue($itemName)
{
return $this->store[$itemName]['value'];
}
/** Resolve an alias to another item */
private function createAlias($itemName)
{
return $this->lookup($this->store[$itemName]['ref']);
}
/** Create a fresh instance of $itemName */
private function createNewInstance($itemName)
{
$reflector = new ReflectionClass($this->store[$itemName]['className']);
if ($reflector->getConstructor()) {
return $reflector->newInstanceArgs(
$this->createDependenciesFor($itemName)
);
}
return $reflector->newInstance();
}
/** Create and register a shared instance of $itemName */
private function createSharedInstance($itemName)
{
if (!isset($this->store[$itemName]['instance'])) {
$this->store[$itemName]['instance'] = $this->createNewInstance($itemName);
}
return $this->store[$itemName]['instance'];
}
/** Get the current endpoint in the store */
private function &getEndPoint()
{
if (!isset($this->endPoint)) {
throw new BadMethodCallException('Component must first be registered by calling register()');
}
return $this->endPoint;
}
/** Get an argument list with dependencies resolved */
private function resolveArgs(array $args)
{
$resolved = [];
foreach ($args as $argDefinition) {
switch ($argDefinition['type']) {
case 'lookup':
$resolved[] = $this->lookupRecursive($argDefinition['item']);
break;
case 'value':
$resolved[] = $argDefinition['item'];
break;
}
}
return $resolved;
}
/** Resolve a single dependency with an collections */
private function lookupRecursive($item)
{
if (\is_array($item)) {
$collection = [];
foreach ($item as $k => $v) {
$collection[$k] = $this->lookupRecursive($v);
}
return $collection;
}
return $this->lookup($item);
}
}

View File

@@ -0,0 +1,27 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* DependencyException gets thrown when a requested dependency is missing.
*
* @author Chris Corbyn
*/
class Swift_DependencyException extends Swift_SwiftException
{
/**
* Create a new DependencyException with $message.
*
* @param string $message
*/
public function __construct($message)
{
parent::__construct($message);
}
}

View File

@@ -0,0 +1,53 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* An embedded file, in a multipart message.
*
* @author Chris Corbyn
*/
class Swift_EmbeddedFile extends Swift_Mime_EmbeddedFile
{
/**
* Create a new EmbeddedFile.
*
* Details may be optionally provided to the constructor.
*
* @param string|Swift_OutputByteStream $data
* @param string $filename
* @param string $contentType
*/
public function __construct($data = null, $filename = null, $contentType = null)
{
\call_user_func_array(
[$this, 'Swift_Mime_EmbeddedFile::__construct'],
Swift_DependencyContainer::getInstance()
->createDependenciesFor('mime.embeddedfile')
);
$this->setBody($data);
$this->setFilename($filename);
if ($contentType) {
$this->setContentType($contentType);
}
}
/**
* Create a new EmbeddedFile from a filesystem path.
*
* @param string $path
*
* @return Swift_Mime_EmbeddedFile
*/
public static function fromPath($path)
{
return (new self())->setFile(new Swift_ByteStream_FileByteStream($path));
}
}

View File

@@ -0,0 +1,28 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Interface for all Encoder schemes.
*
* @author Chris Corbyn
*/
interface Swift_Encoder extends Swift_Mime_CharsetObserver
{
/**
* Encode a given string to produce an encoded string.
*
* @param string $string
* @param int $firstLineOffset if first line needs to be shorter
* @param int $maxLineLength - 0 indicates the default length for this encoding
*
* @return string
*/
public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0);
}

View File

@@ -0,0 +1,58 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Handles Base 64 Encoding in Swift Mailer.
*
* @author Chris Corbyn
*/
class Swift_Encoder_Base64Encoder implements Swift_Encoder
{
/**
* Takes an unencoded string and produces a Base64 encoded string from it.
*
* Base64 encoded strings have a maximum line length of 76 characters.
* If the first line needs to be shorter, indicate the difference with
* $firstLineOffset.
*
* @param string $string to encode
* @param int $firstLineOffset
* @param int $maxLineLength optional, 0 indicates the default of 76 bytes
*
* @return string
*/
public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0)
{
if (0 >= $maxLineLength || 76 < $maxLineLength) {
$maxLineLength = 76;
}
$encodedString = base64_encode($string ?? '');
$firstLine = '';
if (0 != $firstLineOffset) {
$firstLine = substr(
$encodedString, 0, $maxLineLength - $firstLineOffset
)."\r\n";
$encodedString = substr(
$encodedString, $maxLineLength - $firstLineOffset
);
}
return $firstLine.trim(chunk_split($encodedString, $maxLineLength, "\r\n"));
}
/**
* Does nothing.
*/
public function charsetChanged($charset)
{
}
}

View File

@@ -0,0 +1,300 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Handles Quoted Printable (QP) Encoding in Swift Mailer.
*
* Possibly the most accurate RFC 2045 QP implementation found in PHP.
*
* @author Chris Corbyn
*/
class Swift_Encoder_QpEncoder implements Swift_Encoder
{
/**
* The CharacterStream used for reading characters (as opposed to bytes).
*
* @var Swift_CharacterStream
*/
protected $charStream;
/**
* A filter used if input should be canonicalized.
*
* @var Swift_StreamFilter
*/
protected $filter;
/**
* Pre-computed QP for HUGE optimization.
*
* @var string[]
*/
protected static $qpMap = [
0 => '=00', 1 => '=01', 2 => '=02', 3 => '=03', 4 => '=04',
5 => '=05', 6 => '=06', 7 => '=07', 8 => '=08', 9 => '=09',
10 => '=0A', 11 => '=0B', 12 => '=0C', 13 => '=0D', 14 => '=0E',
15 => '=0F', 16 => '=10', 17 => '=11', 18 => '=12', 19 => '=13',
20 => '=14', 21 => '=15', 22 => '=16', 23 => '=17', 24 => '=18',
25 => '=19', 26 => '=1A', 27 => '=1B', 28 => '=1C', 29 => '=1D',
30 => '=1E', 31 => '=1F', 32 => '=20', 33 => '=21', 34 => '=22',
35 => '=23', 36 => '=24', 37 => '=25', 38 => '=26', 39 => '=27',
40 => '=28', 41 => '=29', 42 => '=2A', 43 => '=2B', 44 => '=2C',
45 => '=2D', 46 => '=2E', 47 => '=2F', 48 => '=30', 49 => '=31',
50 => '=32', 51 => '=33', 52 => '=34', 53 => '=35', 54 => '=36',
55 => '=37', 56 => '=38', 57 => '=39', 58 => '=3A', 59 => '=3B',
60 => '=3C', 61 => '=3D', 62 => '=3E', 63 => '=3F', 64 => '=40',
65 => '=41', 66 => '=42', 67 => '=43', 68 => '=44', 69 => '=45',
70 => '=46', 71 => '=47', 72 => '=48', 73 => '=49', 74 => '=4A',
75 => '=4B', 76 => '=4C', 77 => '=4D', 78 => '=4E', 79 => '=4F',
80 => '=50', 81 => '=51', 82 => '=52', 83 => '=53', 84 => '=54',
85 => '=55', 86 => '=56', 87 => '=57', 88 => '=58', 89 => '=59',
90 => '=5A', 91 => '=5B', 92 => '=5C', 93 => '=5D', 94 => '=5E',
95 => '=5F', 96 => '=60', 97 => '=61', 98 => '=62', 99 => '=63',
100 => '=64', 101 => '=65', 102 => '=66', 103 => '=67', 104 => '=68',
105 => '=69', 106 => '=6A', 107 => '=6B', 108 => '=6C', 109 => '=6D',
110 => '=6E', 111 => '=6F', 112 => '=70', 113 => '=71', 114 => '=72',
115 => '=73', 116 => '=74', 117 => '=75', 118 => '=76', 119 => '=77',
120 => '=78', 121 => '=79', 122 => '=7A', 123 => '=7B', 124 => '=7C',
125 => '=7D', 126 => '=7E', 127 => '=7F', 128 => '=80', 129 => '=81',
130 => '=82', 131 => '=83', 132 => '=84', 133 => '=85', 134 => '=86',
135 => '=87', 136 => '=88', 137 => '=89', 138 => '=8A', 139 => '=8B',
140 => '=8C', 141 => '=8D', 142 => '=8E', 143 => '=8F', 144 => '=90',
145 => '=91', 146 => '=92', 147 => '=93', 148 => '=94', 149 => '=95',
150 => '=96', 151 => '=97', 152 => '=98', 153 => '=99', 154 => '=9A',
155 => '=9B', 156 => '=9C', 157 => '=9D', 158 => '=9E', 159 => '=9F',
160 => '=A0', 161 => '=A1', 162 => '=A2', 163 => '=A3', 164 => '=A4',
165 => '=A5', 166 => '=A6', 167 => '=A7', 168 => '=A8', 169 => '=A9',
170 => '=AA', 171 => '=AB', 172 => '=AC', 173 => '=AD', 174 => '=AE',
175 => '=AF', 176 => '=B0', 177 => '=B1', 178 => '=B2', 179 => '=B3',
180 => '=B4', 181 => '=B5', 182 => '=B6', 183 => '=B7', 184 => '=B8',
185 => '=B9', 186 => '=BA', 187 => '=BB', 188 => '=BC', 189 => '=BD',
190 => '=BE', 191 => '=BF', 192 => '=C0', 193 => '=C1', 194 => '=C2',
195 => '=C3', 196 => '=C4', 197 => '=C5', 198 => '=C6', 199 => '=C7',
200 => '=C8', 201 => '=C9', 202 => '=CA', 203 => '=CB', 204 => '=CC',
205 => '=CD', 206 => '=CE', 207 => '=CF', 208 => '=D0', 209 => '=D1',
210 => '=D2', 211 => '=D3', 212 => '=D4', 213 => '=D5', 214 => '=D6',
215 => '=D7', 216 => '=D8', 217 => '=D9', 218 => '=DA', 219 => '=DB',
220 => '=DC', 221 => '=DD', 222 => '=DE', 223 => '=DF', 224 => '=E0',
225 => '=E1', 226 => '=E2', 227 => '=E3', 228 => '=E4', 229 => '=E5',
230 => '=E6', 231 => '=E7', 232 => '=E8', 233 => '=E9', 234 => '=EA',
235 => '=EB', 236 => '=EC', 237 => '=ED', 238 => '=EE', 239 => '=EF',
240 => '=F0', 241 => '=F1', 242 => '=F2', 243 => '=F3', 244 => '=F4',
245 => '=F5', 246 => '=F6', 247 => '=F7', 248 => '=F8', 249 => '=F9',
250 => '=FA', 251 => '=FB', 252 => '=FC', 253 => '=FD', 254 => '=FE',
255 => '=FF',
];
protected static $safeMapShare = [];
/**
* A map of non-encoded ascii characters.
*
* @var string[]
*/
protected $safeMap = [];
/**
* Creates a new QpEncoder for the given CharacterStream.
*
* @param Swift_CharacterStream $charStream to use for reading characters
* @param Swift_StreamFilter $filter if input should be canonicalized
*/
public function __construct(Swift_CharacterStream $charStream, Swift_StreamFilter $filter = null)
{
$this->charStream = $charStream;
if (!isset(self::$safeMapShare[$this->getSafeMapShareId()])) {
$this->initSafeMap();
self::$safeMapShare[$this->getSafeMapShareId()] = $this->safeMap;
} else {
$this->safeMap = self::$safeMapShare[$this->getSafeMapShareId()];
}
$this->filter = $filter;
}
public function __sleep()
{
return ['charStream', 'filter'];
}
public function __wakeup()
{
if (!isset(self::$safeMapShare[$this->getSafeMapShareId()])) {
$this->initSafeMap();
self::$safeMapShare[$this->getSafeMapShareId()] = $this->safeMap;
} else {
$this->safeMap = self::$safeMapShare[$this->getSafeMapShareId()];
}
}
protected function getSafeMapShareId()
{
return static::class;
}
protected function initSafeMap()
{
foreach (array_merge(
[0x09, 0x20], range(0x21, 0x3C), range(0x3E, 0x7E)) as $byte) {
$this->safeMap[$byte] = \chr($byte);
}
}
/**
* Takes an unencoded string and produces a QP encoded string from it.
*
* QP encoded strings have a maximum line length of 76 characters.
* If the first line needs to be shorter, indicate the difference with
* $firstLineOffset.
*
* @param string $string to encode
* @param int $firstLineOffset optional
* @param int $maxLineLength optional 0 indicates the default of 76 chars
*
* @return string
*/
public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0)
{
if ($maxLineLength > 76 || $maxLineLength <= 0) {
$maxLineLength = 76;
}
$thisLineLength = $maxLineLength - $firstLineOffset;
$lines = [];
$lNo = 0;
$lines[$lNo] = '';
$currentLine = &$lines[$lNo++];
$size = $lineLen = 0;
$this->charStream->flushContents();
$this->charStream->importString($string);
// Fetching more than 4 chars at one is slower, as is fetching fewer bytes
// Conveniently 4 chars is the UTF-8 safe number since UTF-8 has up to 6
// bytes per char and (6 * 4 * 3 = 72 chars per line) * =NN is 3 bytes
while (false !== $bytes = $this->nextSequence()) {
// If we're filtering the input
if (isset($this->filter)) {
// If we can't filter because we need more bytes
while ($this->filter->shouldBuffer($bytes)) {
// Then collect bytes into the buffer
if (false === $moreBytes = $this->nextSequence(1)) {
break;
}
foreach ($moreBytes as $b) {
$bytes[] = $b;
}
}
// And filter them
$bytes = $this->filter->filter($bytes);
}
$enc = $this->encodeByteSequence($bytes, $size);
$i = strpos($enc, '=0D=0A');
$newLineLength = $lineLen + (false === $i ? $size : $i);
if ($currentLine && $newLineLength >= $thisLineLength) {
$lines[$lNo] = '';
$currentLine = &$lines[$lNo++];
$thisLineLength = $maxLineLength;
$lineLen = 0;
}
$currentLine .= $enc;
if (false === $i) {
$lineLen += $size;
} else {
// 6 is the length of '=0D=0A'.
$lineLen = $size - strrpos($enc, '=0D=0A') - 6;
}
}
return $this->standardize(implode("=\r\n", $lines));
}
/**
* Updates the charset used.
*
* @param string $charset
*/
public function charsetChanged($charset)
{
$this->charStream->setCharacterSet($charset);
}
/**
* Encode the given byte array into a verbatim QP form.
*
* @param int[] $bytes
* @param int $size
*
* @return string
*/
protected function encodeByteSequence(array $bytes, &$size)
{
$ret = '';
$size = 0;
foreach ($bytes as $b) {
if (isset($this->safeMap[$b])) {
$ret .= $this->safeMap[$b];
++$size;
} else {
$ret .= self::$qpMap[$b];
$size += 3;
}
}
return $ret;
}
/**
* Get the next sequence of bytes to read from the char stream.
*
* @param int $size number of bytes to read
*
* @return int[]
*/
protected function nextSequence($size = 4)
{
return $this->charStream->readBytes($size);
}
/**
* Make sure CRLF is correct and HT/SPACE are in valid places.
*
* @param string $string
*
* @return string
*/
protected function standardize($string)
{
$string = str_replace(["\t=0D=0A", ' =0D=0A', '=0D=0A'],
["=09\r\n", "=20\r\n", "\r\n"], $string
);
switch ($end = \ord(substr($string, -1))) {
case 0x09:
case 0x20:
$string = substr_replace($string, self::$qpMap[$end], -1);
}
return $string;
}
/**
* Make a deep copy of object.
*/
public function __clone()
{
$this->charStream = clone $this->charStream;
}
}

View File

@@ -0,0 +1,90 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Handles RFC 2231 specified Encoding in Swift Mailer.
*
* @author Chris Corbyn
*/
class Swift_Encoder_Rfc2231Encoder implements Swift_Encoder
{
/**
* A character stream to use when reading a string as characters instead of bytes.
*
* @var Swift_CharacterStream
*/
private $charStream;
/**
* Creates a new Rfc2231Encoder using the given character stream instance.
*/
public function __construct(Swift_CharacterStream $charStream)
{
$this->charStream = $charStream;
}
/**
* Takes an unencoded string and produces a string encoded according to
* RFC 2231 from it.
*
* @param string $string
* @param int $firstLineOffset
* @param int $maxLineLength optional, 0 indicates the default of 75 bytes
*
* @return string
*/
public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0)
{
$lines = [];
$lineCount = 0;
$lines[] = '';
$currentLine = &$lines[$lineCount++];
if (0 >= $maxLineLength) {
$maxLineLength = 75;
}
$this->charStream->flushContents();
$this->charStream->importString($string);
$thisLineLength = $maxLineLength - $firstLineOffset;
while (false !== $char = $this->charStream->read(4)) {
$encodedChar = rawurlencode($char);
if (0 != \strlen($currentLine)
&& \strlen($currentLine.$encodedChar) > $thisLineLength) {
$lines[] = '';
$currentLine = &$lines[$lineCount++];
$thisLineLength = $maxLineLength;
}
$currentLine .= $encodedChar;
}
return implode("\r\n", $lines);
}
/**
* Updates the charset used.
*
* @param string $charset
*/
public function charsetChanged($charset)
{
$this->charStream->setCharacterSet($charset);
}
/**
* Make a deep copy of object.
*/
public function __clone()
{
$this->charStream = clone $this->charStream;
}
}

View File

@@ -0,0 +1,64 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Generated when a command is sent over an SMTP connection.
*
* @author Chris Corbyn
*/
class Swift_Events_CommandEvent extends Swift_Events_EventObject
{
/**
* The command sent to the server.
*
* @var string
*/
private $command;
/**
* An array of codes which a successful response will contain.
*
* @var int[]
*/
private $successCodes = [];
/**
* Create a new CommandEvent for $source with $command.
*
* @param string $command
* @param array $successCodes
*/
public function __construct(Swift_Transport $source, $command, $successCodes = [])
{
parent::__construct($source);
$this->command = $command;
$this->successCodes = $successCodes;
}
/**
* Get the command which was sent to the server.
*
* @return string
*/
public function getCommand()
{
return $this->command;
}
/**
* Get the numeric response codes which indicate success for this command.
*
* @return int[]
*/
public function getSuccessCodes()
{
return $this->successCodes;
}
}

View File

@@ -0,0 +1,22 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Listens for Transports to send commands to the server.
*
* @author Chris Corbyn
*/
interface Swift_Events_CommandListener extends Swift_Events_EventListener
{
/**
* Invoked immediately following a command being sent.
*/
public function commandSent(Swift_Events_CommandEvent $evt);
}

View File

@@ -0,0 +1,38 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* The minimum interface for an Event.
*
* @author Chris Corbyn
*/
interface Swift_Events_Event
{
/**
* Get the source object of this event.
*
* @return object
*/
public function getSource();
/**
* Prevent this Event from bubbling any further up the stack.
*
* @param bool $cancel, optional
*/
public function cancelBubble($cancel = true);
/**
* Returns true if this Event will not bubble any further up the stack.
*
* @return bool
*/
public function bubbleCancelled();
}

View File

@@ -0,0 +1,70 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Interface for the EventDispatcher which handles the event dispatching layer.
*
* @author Chris Corbyn
*/
interface Swift_Events_EventDispatcher
{
/**
* Create a new SendEvent for $source and $message.
*
* @return Swift_Events_SendEvent
*/
public function createSendEvent(Swift_Transport $source, Swift_Mime_SimpleMessage $message);
/**
* Create a new CommandEvent for $source and $command.
*
* @param string $command That will be executed
* @param array $successCodes That are needed
*
* @return Swift_Events_CommandEvent
*/
public function createCommandEvent(Swift_Transport $source, $command, $successCodes = []);
/**
* Create a new ResponseEvent for $source and $response.
*
* @param string $response
* @param bool $valid If the response is valid
*
* @return Swift_Events_ResponseEvent
*/
public function createResponseEvent(Swift_Transport $source, $response, $valid);
/**
* Create a new TransportChangeEvent for $source.
*
* @return Swift_Events_TransportChangeEvent
*/
public function createTransportChangeEvent(Swift_Transport $source);
/**
* Create a new TransportExceptionEvent for $source.
*
* @return Swift_Events_TransportExceptionEvent
*/
public function createTransportExceptionEvent(Swift_Transport $source, Swift_TransportException $ex);
/**
* Bind an event listener to this dispatcher.
*/
public function bindEventListener(Swift_Events_EventListener $listener);
/**
* Dispatch the given Event to all suitable listeners.
*
* @param string $target method
*/
public function dispatchEvent(Swift_Events_EventObject $evt, $target);
}

View File

@@ -0,0 +1,18 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* An identity interface which all EventListeners must extend.
*
* @author Chris Corbyn
*/
interface Swift_Events_EventListener
{
}

View File

@@ -0,0 +1,61 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* A base Event which all Event classes inherit from.
*
* @author Chris Corbyn
*/
class Swift_Events_EventObject implements Swift_Events_Event
{
/** The source of this Event */
private $source;
/** The state of this Event (should it bubble up the stack?) */
private $bubbleCancelled = false;
/**
* Create a new EventObject originating at $source.
*
* @param object $source
*/
public function __construct($source)
{
$this->source = $source;
}
/**
* Get the source object of this event.
*
* @return object
*/
public function getSource()
{
return $this->source;
}
/**
* Prevent this Event from bubbling any further up the stack.
*/
public function cancelBubble($cancel = true)
{
$this->bubbleCancelled = $cancel;
}
/**
* Returns true if this Event will not bubble any further up the stack.
*
* @return bool
*/
public function bubbleCancelled()
{
return $this->bubbleCancelled;
}
}

View File

@@ -0,0 +1,64 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Generated when a response is received on a SMTP connection.
*
* @author Chris Corbyn
*/
class Swift_Events_ResponseEvent extends Swift_Events_EventObject
{
/**
* The overall result.
*
* @var bool
*/
private $valid;
/**
* The response received from the server.
*
* @var string
*/
private $response;
/**
* Create a new ResponseEvent for $source and $response.
*
* @param string $response
* @param bool $valid
*/
public function __construct(Swift_Transport $source, $response, $valid = false)
{
parent::__construct($source);
$this->response = $response;
$this->valid = $valid;
}
/**
* Get the response which was received from the server.
*
* @return string
*/
public function getResponse()
{
return $this->response;
}
/**
* Get the success status of this Event.
*
* @return bool
*/
public function isValid()
{
return $this->valid;
}
}

View File

@@ -0,0 +1,22 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Listens for responses from a remote SMTP server.
*
* @author Chris Corbyn
*/
interface Swift_Events_ResponseListener extends Swift_Events_EventListener
{
/**
* Invoked immediately following a response coming back.
*/
public function responseReceived(Swift_Events_ResponseEvent $evt);
}

View File

@@ -0,0 +1,126 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Generated when a message is being sent.
*
* @author Chris Corbyn
*/
class Swift_Events_SendEvent extends Swift_Events_EventObject
{
/** Sending has yet to occur */
const RESULT_PENDING = 0x0001;
/** Email is spooled, ready to be sent */
const RESULT_SPOOLED = 0x0011;
/** Sending was successful */
const RESULT_SUCCESS = 0x0010;
/** Sending worked, but there were some failures */
const RESULT_TENTATIVE = 0x0100;
/** Sending failed */
const RESULT_FAILED = 0x1000;
/**
* The Message being sent.
*
* @var Swift_Mime_SimpleMessage
*/
private $message;
/**
* Any recipients which failed after sending.
*
* @var string[]
*/
private $failedRecipients = [];
/**
* The overall result as a bitmask from the class constants.
*
* @var int
*/
private $result;
/**
* Create a new SendEvent for $source and $message.
*/
public function __construct(Swift_Transport $source, Swift_Mime_SimpleMessage $message)
{
parent::__construct($source);
$this->message = $message;
$this->result = self::RESULT_PENDING;
}
/**
* Get the Transport used to send the Message.
*
* @return Swift_Transport
*/
public function getTransport()
{
return $this->getSource();
}
/**
* Get the Message being sent.
*
* @return Swift_Mime_SimpleMessage
*/
public function getMessage()
{
return $this->message;
}
/**
* Set the array of addresses that failed in sending.
*
* @param array $recipients
*/
public function setFailedRecipients($recipients)
{
$this->failedRecipients = $recipients;
}
/**
* Get an recipient addresses which were not accepted for delivery.
*
* @return string[]
*/
public function getFailedRecipients()
{
return $this->failedRecipients;
}
/**
* Set the result of sending.
*
* @param int $result
*/
public function setResult($result)
{
$this->result = $result;
}
/**
* Get the result of this Event.
*
* The return value is a bitmask from
* {@see RESULT_PENDING, RESULT_SUCCESS, RESULT_TENTATIVE, RESULT_FAILED}
*
* @return int
*/
public function getResult()
{
return $this->result;
}
}

View File

@@ -0,0 +1,27 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Listens for Messages being sent from within the Transport system.
*
* @author Chris Corbyn
*/
interface Swift_Events_SendListener extends Swift_Events_EventListener
{
/**
* Invoked immediately before the Message is sent.
*/
public function beforeSendPerformed(Swift_Events_SendEvent $evt);
/**
* Invoked immediately after the Message is sent.
*/
public function sendPerformed(Swift_Events_SendEvent $evt);
}

View File

@@ -0,0 +1,142 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* The EventDispatcher which handles the event dispatching layer.
*
* @author Chris Corbyn
*/
class Swift_Events_SimpleEventDispatcher implements Swift_Events_EventDispatcher
{
/** A map of event types to their associated listener types */
private $eventMap = [];
/** Event listeners bound to this dispatcher */
private $listeners = [];
/**
* Create a new EventDispatcher.
*/
public function __construct()
{
$this->eventMap = [
'Swift_Events_CommandEvent' => 'Swift_Events_CommandListener',
'Swift_Events_ResponseEvent' => 'Swift_Events_ResponseListener',
'Swift_Events_SendEvent' => 'Swift_Events_SendListener',
'Swift_Events_TransportChangeEvent' => 'Swift_Events_TransportChangeListener',
'Swift_Events_TransportExceptionEvent' => 'Swift_Events_TransportExceptionListener',
];
}
/**
* Create a new SendEvent for $source and $message.
*
* @return Swift_Events_SendEvent
*/
public function createSendEvent(Swift_Transport $source, Swift_Mime_SimpleMessage $message)
{
return new Swift_Events_SendEvent($source, $message);
}
/**
* Create a new CommandEvent for $source and $command.
*
* @param string $command That will be executed
* @param array $successCodes That are needed
*
* @return Swift_Events_CommandEvent
*/
public function createCommandEvent(Swift_Transport $source, $command, $successCodes = [])
{
return new Swift_Events_CommandEvent($source, $command, $successCodes);
}
/**
* Create a new ResponseEvent for $source and $response.
*
* @param string $response
* @param bool $valid If the response is valid
*
* @return Swift_Events_ResponseEvent
*/
public function createResponseEvent(Swift_Transport $source, $response, $valid)
{
return new Swift_Events_ResponseEvent($source, $response, $valid);
}
/**
* Create a new TransportChangeEvent for $source.
*
* @return Swift_Events_TransportChangeEvent
*/
public function createTransportChangeEvent(Swift_Transport $source)
{
return new Swift_Events_TransportChangeEvent($source);
}
/**
* Create a new TransportExceptionEvent for $source.
*
* @return Swift_Events_TransportExceptionEvent
*/
public function createTransportExceptionEvent(Swift_Transport $source, Swift_TransportException $ex)
{
return new Swift_Events_TransportExceptionEvent($source, $ex);
}
/**
* Bind an event listener to this dispatcher.
*/
public function bindEventListener(Swift_Events_EventListener $listener)
{
foreach ($this->listeners as $l) {
// Already loaded
if ($l === $listener) {
return;
}
}
$this->listeners[] = $listener;
}
/**
* Dispatch the given Event to all suitable listeners.
*
* @param string $target method
*/
public function dispatchEvent(Swift_Events_EventObject $evt, $target)
{
$bubbleQueue = $this->prepareBubbleQueue($evt);
$this->bubble($bubbleQueue, $evt, $target);
}
/** Queue listeners on a stack ready for $evt to be bubbled up it */
private function prepareBubbleQueue(Swift_Events_EventObject $evt)
{
$bubbleQueue = [];
$evtClass = \get_class($evt);
foreach ($this->listeners as $listener) {
if (\array_key_exists($evtClass, $this->eventMap)
&& ($listener instanceof $this->eventMap[$evtClass])) {
$bubbleQueue[] = $listener;
}
}
return $bubbleQueue;
}
/** Bubble $evt up the stack calling $target() on each listener */
private function bubble(array &$bubbleQueue, Swift_Events_EventObject $evt, $target)
{
if (!$evt->bubbleCancelled() && $listener = array_shift($bubbleQueue)) {
$listener->$target($evt);
$this->bubble($bubbleQueue, $evt, $target);
}
}
}

View File

@@ -0,0 +1,27 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Generated when the state of a Transport is changed (i.e. stopped/started).
*
* @author Chris Corbyn
*/
class Swift_Events_TransportChangeEvent extends Swift_Events_EventObject
{
/**
* Get the Transport.
*
* @return Swift_Transport
*/
public function getTransport()
{
return $this->getSource();
}
}

View File

@@ -0,0 +1,37 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Listens for changes within the Transport system.
*
* @author Chris Corbyn
*/
interface Swift_Events_TransportChangeListener extends Swift_Events_EventListener
{
/**
* Invoked just before a Transport is started.
*/
public function beforeTransportStarted(Swift_Events_TransportChangeEvent $evt);
/**
* Invoked immediately after the Transport is started.
*/
public function transportStarted(Swift_Events_TransportChangeEvent $evt);
/**
* Invoked just before a Transport is stopped.
*/
public function beforeTransportStopped(Swift_Events_TransportChangeEvent $evt);
/**
* Invoked immediately after the Transport is stopped.
*/
public function transportStopped(Swift_Events_TransportChangeEvent $evt);
}

View File

@@ -0,0 +1,43 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Generated when a TransportException is thrown from the Transport system.
*
* @author Chris Corbyn
*/
class Swift_Events_TransportExceptionEvent extends Swift_Events_EventObject
{
/**
* The Exception thrown.
*
* @var Swift_TransportException
*/
private $exception;
/**
* Create a new TransportExceptionEvent for $transport.
*/
public function __construct(Swift_Transport $transport, Swift_TransportException $ex)
{
parent::__construct($transport);
$this->exception = $ex;
}
/**
* Get the TransportException thrown.
*
* @return Swift_TransportException
*/
public function getException()
{
return $this->exception;
}
}

View File

@@ -0,0 +1,22 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Listens for Exceptions thrown from within the Transport system.
*
* @author Chris Corbyn
*/
interface Swift_Events_TransportExceptionListener extends Swift_Events_EventListener
{
/**
* Invoked as a TransportException is thrown in the Transport system.
*/
public function exceptionThrown(Swift_Events_TransportExceptionEvent $evt);
}

View File

@@ -0,0 +1,33 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Contains a list of redundant Transports so when one fails, the next is used.
*
* @author Chris Corbyn
*/
class Swift_FailoverTransport extends Swift_Transport_FailoverTransport
{
/**
* Creates a new FailoverTransport with $transports.
*
* @param Swift_Transport[] $transports
*/
public function __construct($transports = [])
{
\call_user_func_array(
[$this, 'Swift_Transport_FailoverTransport::__construct'],
Swift_DependencyContainer::getInstance()
->createDependenciesFor('transport.failover')
);
$this->setTransports($transports);
}
}

View File

@@ -0,0 +1,208 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2009 Fabien Potencier <fabien.potencier@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Stores Messages on the filesystem.
*
* @author Fabien Potencier
* @author Xavier De Cock <xdecock@gmail.com>
*/
class Swift_FileSpool extends Swift_ConfigurableSpool
{
/** The spool directory */
private $path;
/**
* File WriteRetry Limit.
*
* @var int
*/
private $retryLimit = 10;
/**
* Create a new FileSpool.
*
* @param string $path
*
* @throws Swift_IoException
*/
public function __construct($path)
{
$this->path = $path;
if (!file_exists($this->path)) {
if (!mkdir($this->path, 0777, true)) {
throw new Swift_IoException(sprintf('Unable to create path "%s".', $this->path));
}
}
}
/**
* Tests if this Spool mechanism has started.
*
* @return bool
*/
public function isStarted()
{
return true;
}
/**
* Starts this Spool mechanism.
*/
public function start()
{
}
/**
* Stops this Spool mechanism.
*/
public function stop()
{
}
/**
* Allow to manage the enqueuing retry limit.
*
* Default, is ten and allows over 64^20 different fileNames
*
* @param int $limit
*/
public function setRetryLimit($limit)
{
$this->retryLimit = $limit;
}
/**
* Queues a message.
*
* @param Swift_Mime_SimpleMessage $message The message to store
*
* @throws Swift_IoException
*
* @return bool
*/
public function queueMessage(Swift_Mime_SimpleMessage $message)
{
$ser = serialize($message);
$fileName = $this->path.'/'.$this->getRandomString(10);
for ($i = 0; $i < $this->retryLimit; ++$i) {
/* We try an exclusive creation of the file. This is an atomic operation, it avoid locking mechanism */
$fp = @fopen($fileName.'.message', 'xb');
if (false !== $fp) {
if (false === fwrite($fp, $ser)) {
return false;
}
return fclose($fp);
} else {
/* The file already exists, we try a longer fileName */
$fileName .= $this->getRandomString(1);
}
}
throw new Swift_IoException(sprintf('Unable to create a file for enqueuing Message in "%s".', $this->path));
}
/**
* Execute a recovery if for any reason a process is sending for too long.
*
* @param int $timeout in second Defaults is for very slow smtp responses
*/
public function recover($timeout = 900)
{
foreach (new DirectoryIterator($this->path) as $file) {
$file = $file->getRealPath();
if ('.message.sending' == substr($file, -16)) {
$lockedtime = filectime($file);
if ((time() - $lockedtime) > $timeout) {
rename($file, substr($file, 0, -8));
}
}
}
}
/**
* Sends messages using the given transport instance.
*
* @param Swift_Transport $transport A transport instance
* @param string[] $failedRecipients An array of failures by-reference
*
* @return int The number of sent e-mail's
*/
public function flushQueue(Swift_Transport $transport, &$failedRecipients = null)
{
$directoryIterator = new DirectoryIterator($this->path);
/* Start the transport only if there are queued files to send */
if (!$transport->isStarted()) {
foreach ($directoryIterator as $file) {
if ('.message' == substr($file->getRealPath(), -8)) {
$transport->start();
break;
}
}
}
$failedRecipients = (array) $failedRecipients;
$count = 0;
$time = time();
foreach ($directoryIterator as $file) {
$file = $file->getRealPath();
if ('.message' != substr($file, -8)) {
continue;
}
/* We try a rename, it's an atomic operation, and avoid locking the file */
if (rename($file, $file.'.sending')) {
$message = unserialize(file_get_contents($file.'.sending'));
$count += $transport->send($message, $failedRecipients);
unlink($file.'.sending');
} else {
/* This message has just been catched by another process */
continue;
}
if ($this->getMessageLimit() && $count >= $this->getMessageLimit()) {
break;
}
if ($this->getTimeLimit() && (time() - $time) >= $this->getTimeLimit()) {
break;
}
}
return $count;
}
/**
* Returns a random string needed to generate a fileName for the queue.
*
* @param int $count
*
* @return string
*/
protected function getRandomString($count)
{
// This string MUST stay FS safe, avoid special chars
$base = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-';
$ret = '';
$strlen = \strlen($base);
for ($i = 0; $i < $count; ++$i) {
$ret .= $base[random_int(0, $strlen - 1)];
}
return $ret;
}
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* An OutputByteStream which specifically reads from a file.
*
* @author Chris Corbyn
*/
interface Swift_FileStream extends Swift_OutputByteStream
{
/**
* Get the complete path to the file.
*
* @return string
*/
public function getPath();
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Allows StreamFilters to operate on a stream.
*
* @author Chris Corbyn
*/
interface Swift_Filterable
{
/**
* Add a new StreamFilter, referenced by $key.
*
* @param string $key
*/
public function addFilter(Swift_StreamFilter $filter, $key);
/**
* Remove an existing filter using $key.
*
* @param string $key
*/
public function removeFilter($key);
}

View File

@@ -0,0 +1,22 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Message ID generator.
*/
interface Swift_IdGenerator
{
/**
* Returns a globally unique string to use for Message-ID or Content-ID.
*
* @return string
*/
public function generateId();
}

View File

@@ -0,0 +1,43 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* An image, embedded in a multipart message.
*
* @author Chris Corbyn
*/
class Swift_Image extends Swift_EmbeddedFile
{
/**
* Create a new EmbeddedFile.
*
* Details may be optionally provided to the constructor.
*
* @param string|Swift_OutputByteStream $data
* @param string $filename
* @param string $contentType
*/
public function __construct($data = null, $filename = null, $contentType = null)
{
parent::__construct($data, $filename, $contentType);
}
/**
* Create a new Image from a filesystem path.
*
* @param string $path
*
* @return self
*/
public static function fromPath($path)
{
return (new self())->setFile(new Swift_ByteStream_FileByteStream($path));
}
}

View File

@@ -0,0 +1,75 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* An abstract means of writing data.
*
* Classes implementing this interface may use a subsystem which requires less
* memory than working with large strings of data.
*
* @author Chris Corbyn
*/
interface Swift_InputByteStream
{
/**
* Writes $bytes to the end of the stream.
*
* Writing may not happen immediately if the stream chooses to buffer. If
* you want to write these bytes with immediate effect, call {@link commit()}
* after calling write().
*
* This method returns the sequence ID of the write (i.e. 1 for first, 2 for
* second, etc etc).
*
* @param string $bytes
*
* @throws Swift_IoException
*
* @return int
*/
public function write($bytes);
/**
* For any bytes that are currently buffered inside the stream, force them
* off the buffer.
*
* @throws Swift_IoException
*/
public function commit();
/**
* Attach $is to this stream.
*
* The stream acts as an observer, receiving all data that is written.
* All {@link write()} and {@link flushBuffers()} operations will be mirrored.
*
* @param Swift_InputByteStream $is
*/
public function bind(self $is);
/**
* Remove an already bound stream.
*
* If $is is not bound, no errors will be raised.
* If the stream currently has any buffered data it will be written to $is
* before unbinding occurs.
*
* @param Swift_InputByteStream $is
*/
public function unbind(self $is);
/**
* Flush the contents of the stream (empty it) and set the internal pointer
* to the beginning.
*
* @throws Swift_IoException
*/
public function flushBuffers();
}

View File

@@ -0,0 +1,28 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* I/O Exception class.
*
* @author Chris Corbyn
*/
class Swift_IoException extends Swift_SwiftException
{
/**
* Create a new IoException with $message.
*
* @param string $message
* @param int $code
*/
public function __construct($message, $code = 0, Exception $previous = null)
{
parent::__construct($message, $code, $previous);
}
}

View File

@@ -0,0 +1,104 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Provides a mechanism for storing data using two keys.
*
* @author Chris Corbyn
*/
interface Swift_KeyCache
{
/** Mode for replacing existing cached data */
const MODE_WRITE = 1;
/** Mode for appending data to the end of existing cached data */
const MODE_APPEND = 2;
/**
* Set a string into the cache under $itemKey for the namespace $nsKey.
*
* @see MODE_WRITE, MODE_APPEND
*
* @param string $nsKey
* @param string $itemKey
* @param string $string
* @param int $mode
*/
public function setString($nsKey, $itemKey, $string, $mode);
/**
* Set a ByteStream into the cache under $itemKey for the namespace $nsKey.
*
* @see MODE_WRITE, MODE_APPEND
*
* @param string $nsKey
* @param string $itemKey
* @param int $mode
*/
public function importFromByteStream($nsKey, $itemKey, Swift_OutputByteStream $os, $mode);
/**
* Provides a ByteStream which when written to, writes data to $itemKey.
*
* NOTE: The stream will always write in append mode.
* If the optional third parameter is passed all writes will go through $is.
*
* @param string $nsKey
* @param string $itemKey
* @param Swift_InputByteStream $is optional input stream
*
* @return Swift_InputByteStream
*/
public function getInputByteStream($nsKey, $itemKey, Swift_InputByteStream $is = null);
/**
* Get data back out of the cache as a string.
*
* @param string $nsKey
* @param string $itemKey
*
* @return string
*/
public function getString($nsKey, $itemKey);
/**
* Get data back out of the cache as a ByteStream.
*
* @param string $nsKey
* @param string $itemKey
* @param Swift_InputByteStream $is stream to write the data to
*/
public function exportToByteStream($nsKey, $itemKey, Swift_InputByteStream $is);
/**
* Check if the given $itemKey exists in the namespace $nsKey.
*
* @param string $nsKey
* @param string $itemKey
*
* @return bool
*/
public function hasKey($nsKey, $itemKey);
/**
* Clear data for $itemKey in the namespace $nsKey if it exists.
*
* @param string $nsKey
* @param string $itemKey
*/
public function clearKey($nsKey, $itemKey);
/**
* Clear all data in the namespace $nsKey if it exists.
*
* @param string $nsKey
*/
public function clearAll($nsKey);
}

View File

@@ -0,0 +1,197 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* A basic KeyCache backed by an array.
*
* @author Chris Corbyn
*/
class Swift_KeyCache_ArrayKeyCache implements Swift_KeyCache
{
/**
* Cache contents.
*
* @var array
*/
private $contents = [];
/**
* An InputStream for cloning.
*
* @var Swift_KeyCache_KeyCacheInputStream
*/
private $stream;
/**
* Create a new ArrayKeyCache with the given $stream for cloning to make
* InputByteStreams.
*/
public function __construct(Swift_KeyCache_KeyCacheInputStream $stream)
{
$this->stream = $stream;
}
/**
* Set a string into the cache under $itemKey for the namespace $nsKey.
*
* @see MODE_WRITE, MODE_APPEND
*
* @param string $nsKey
* @param string $itemKey
* @param string $string
* @param int $mode
*/
public function setString($nsKey, $itemKey, $string, $mode)
{
$this->prepareCache($nsKey);
switch ($mode) {
case self::MODE_WRITE:
$this->contents[$nsKey][$itemKey] = $string;
break;
case self::MODE_APPEND:
if (!$this->hasKey($nsKey, $itemKey)) {
$this->contents[$nsKey][$itemKey] = '';
}
$this->contents[$nsKey][$itemKey] .= $string;
break;
default:
throw new Swift_SwiftException('Invalid mode ['.$mode.'] used to set nsKey='.$nsKey.', itemKey='.$itemKey);
}
}
/**
* Set a ByteStream into the cache under $itemKey for the namespace $nsKey.
*
* @see MODE_WRITE, MODE_APPEND
*
* @param string $nsKey
* @param string $itemKey
* @param int $mode
*/
public function importFromByteStream($nsKey, $itemKey, Swift_OutputByteStream $os, $mode)
{
$this->prepareCache($nsKey);
switch ($mode) {
case self::MODE_WRITE:
$this->clearKey($nsKey, $itemKey);
// no break
case self::MODE_APPEND:
if (!$this->hasKey($nsKey, $itemKey)) {
$this->contents[$nsKey][$itemKey] = '';
}
while (false !== $bytes = $os->read(8192)) {
$this->contents[$nsKey][$itemKey] .= $bytes;
}
break;
default:
throw new Swift_SwiftException('Invalid mode ['.$mode.'] used to set nsKey='.$nsKey.', itemKey='.$itemKey);
}
}
/**
* Provides a ByteStream which when written to, writes data to $itemKey.
*
* NOTE: The stream will always write in append mode.
*
* @param string $nsKey
* @param string $itemKey
*
* @return Swift_InputByteStream
*/
public function getInputByteStream($nsKey, $itemKey, Swift_InputByteStream $writeThrough = null)
{
$is = clone $this->stream;
$is->setKeyCache($this);
$is->setNsKey($nsKey);
$is->setItemKey($itemKey);
if (isset($writeThrough)) {
$is->setWriteThroughStream($writeThrough);
}
return $is;
}
/**
* Get data back out of the cache as a string.
*
* @param string $nsKey
* @param string $itemKey
*
* @return string
*/
public function getString($nsKey, $itemKey)
{
$this->prepareCache($nsKey);
if ($this->hasKey($nsKey, $itemKey)) {
return $this->contents[$nsKey][$itemKey];
}
}
/**
* Get data back out of the cache as a ByteStream.
*
* @param string $nsKey
* @param string $itemKey
* @param Swift_InputByteStream $is to write the data to
*/
public function exportToByteStream($nsKey, $itemKey, Swift_InputByteStream $is)
{
$this->prepareCache($nsKey);
$is->write($this->getString($nsKey, $itemKey));
}
/**
* Check if the given $itemKey exists in the namespace $nsKey.
*
* @param string $nsKey
* @param string $itemKey
*
* @return bool
*/
public function hasKey($nsKey, $itemKey)
{
$this->prepareCache($nsKey);
return \array_key_exists($itemKey, $this->contents[$nsKey]);
}
/**
* Clear data for $itemKey in the namespace $nsKey if it exists.
*
* @param string $nsKey
* @param string $itemKey
*/
public function clearKey($nsKey, $itemKey)
{
unset($this->contents[$nsKey][$itemKey]);
}
/**
* Clear all data in the namespace $nsKey if it exists.
*
* @param string $nsKey
*/
public function clearAll($nsKey)
{
unset($this->contents[$nsKey]);
}
/**
* Initialize the namespace of $nsKey if needed.
*
* @param string $nsKey
*/
private function prepareCache($nsKey)
{
if (!\array_key_exists($nsKey, $this->contents)) {
$this->contents[$nsKey] = [];
}
}
}

View File

@@ -0,0 +1,294 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* A KeyCache which streams to and from disk.
*
* @author Chris Corbyn
*/
class Swift_KeyCache_DiskKeyCache implements Swift_KeyCache
{
/** Signal to place pointer at start of file */
const POSITION_START = 0;
/** Signal to place pointer at end of file */
const POSITION_END = 1;
/** Signal to leave pointer in whatever position it currently is */
const POSITION_CURRENT = 2;
/**
* An InputStream for cloning.
*
* @var Swift_KeyCache_KeyCacheInputStream
*/
private $stream;
/**
* A path to write to.
*
* @var string
*/
private $path;
/**
* Stored keys.
*
* @var array
*/
private $keys = [];
/**
* Create a new DiskKeyCache with the given $stream for cloning to make
* InputByteStreams, and the given $path to save to.
*
* @param string $path to save to
*/
public function __construct(Swift_KeyCache_KeyCacheInputStream $stream, $path)
{
$this->stream = $stream;
$this->path = $path;
}
/**
* Set a string into the cache under $itemKey for the namespace $nsKey.
*
* @see MODE_WRITE, MODE_APPEND
*
* @param string $nsKey
* @param string $itemKey
* @param string $string
* @param int $mode
*
* @throws Swift_IoException
*/
public function setString($nsKey, $itemKey, $string, $mode)
{
$this->prepareCache($nsKey);
switch ($mode) {
case self::MODE_WRITE:
$fp = $this->getHandle($nsKey, $itemKey, self::POSITION_START);
break;
case self::MODE_APPEND:
$fp = $this->getHandle($nsKey, $itemKey, self::POSITION_END);
break;
default:
throw new Swift_SwiftException('Invalid mode ['.$mode.'] used to set nsKey='.$nsKey.', itemKey='.$itemKey);
break;
}
fwrite($fp, $string);
$this->freeHandle($nsKey, $itemKey);
}
/**
* Set a ByteStream into the cache under $itemKey for the namespace $nsKey.
*
* @see MODE_WRITE, MODE_APPEND
*
* @param string $nsKey
* @param string $itemKey
* @param int $mode
*
* @throws Swift_IoException
*/
public function importFromByteStream($nsKey, $itemKey, Swift_OutputByteStream $os, $mode)
{
$this->prepareCache($nsKey);
switch ($mode) {
case self::MODE_WRITE:
$fp = $this->getHandle($nsKey, $itemKey, self::POSITION_START);
break;
case self::MODE_APPEND:
$fp = $this->getHandle($nsKey, $itemKey, self::POSITION_END);
break;
default:
throw new Swift_SwiftException('Invalid mode ['.$mode.'] used to set nsKey='.$nsKey.', itemKey='.$itemKey);
break;
}
while (false !== $bytes = $os->read(8192)) {
fwrite($fp, $bytes);
}
$this->freeHandle($nsKey, $itemKey);
}
/**
* Provides a ByteStream which when written to, writes data to $itemKey.
*
* NOTE: The stream will always write in append mode.
*
* @param string $nsKey
* @param string $itemKey
*
* @return Swift_InputByteStream
*/
public function getInputByteStream($nsKey, $itemKey, Swift_InputByteStream $writeThrough = null)
{
$is = clone $this->stream;
$is->setKeyCache($this);
$is->setNsKey($nsKey);
$is->setItemKey($itemKey);
if (isset($writeThrough)) {
$is->setWriteThroughStream($writeThrough);
}
return $is;
}
/**
* Get data back out of the cache as a string.
*
* @param string $nsKey
* @param string $itemKey
*
* @throws Swift_IoException
*
* @return string
*/
public function getString($nsKey, $itemKey)
{
$this->prepareCache($nsKey);
if ($this->hasKey($nsKey, $itemKey)) {
$fp = $this->getHandle($nsKey, $itemKey, self::POSITION_START);
$str = '';
while (!feof($fp) && false !== $bytes = fread($fp, 8192)) {
$str .= $bytes;
}
$this->freeHandle($nsKey, $itemKey);
return $str;
}
}
/**
* Get data back out of the cache as a ByteStream.
*
* @param string $nsKey
* @param string $itemKey
* @param Swift_InputByteStream $is to write the data to
*/
public function exportToByteStream($nsKey, $itemKey, Swift_InputByteStream $is)
{
if ($this->hasKey($nsKey, $itemKey)) {
$fp = $this->getHandle($nsKey, $itemKey, self::POSITION_START);
while (!feof($fp) && false !== $bytes = fread($fp, 8192)) {
$is->write($bytes);
}
$this->freeHandle($nsKey, $itemKey);
}
}
/**
* Check if the given $itemKey exists in the namespace $nsKey.
*
* @param string $nsKey
* @param string $itemKey
*
* @return bool
*/
public function hasKey($nsKey, $itemKey)
{
return is_file($this->path.'/'.$nsKey.'/'.$itemKey);
}
/**
* Clear data for $itemKey in the namespace $nsKey if it exists.
*
* @param string $nsKey
* @param string $itemKey
*/
public function clearKey($nsKey, $itemKey)
{
if ($this->hasKey($nsKey, $itemKey)) {
$this->freeHandle($nsKey, $itemKey);
unlink($this->path.'/'.$nsKey.'/'.$itemKey);
}
}
/**
* Clear all data in the namespace $nsKey if it exists.
*
* @param string $nsKey
*/
public function clearAll($nsKey)
{
if (\array_key_exists($nsKey, $this->keys)) {
foreach ($this->keys[$nsKey] as $itemKey => $null) {
$this->clearKey($nsKey, $itemKey);
}
if (is_dir($this->path.'/'.$nsKey)) {
rmdir($this->path.'/'.$nsKey);
}
unset($this->keys[$nsKey]);
}
}
/**
* Initialize the namespace of $nsKey if needed.
*
* @param string $nsKey
*/
private function prepareCache($nsKey)
{
$cacheDir = $this->path.'/'.$nsKey;
if (!is_dir($cacheDir)) {
if (!mkdir($cacheDir)) {
throw new Swift_IoException('Failed to create cache directory '.$cacheDir);
}
$this->keys[$nsKey] = [];
}
}
/**
* Get a file handle on the cache item.
*
* @param string $nsKey
* @param string $itemKey
* @param int $position
*
* @return resource
*/
private function getHandle($nsKey, $itemKey, $position)
{
if (!isset($this->keys[$nsKey][$itemKey])) {
$openMode = $this->hasKey($nsKey, $itemKey) ? 'r+b' : 'w+b';
$fp = fopen($this->path.'/'.$nsKey.'/'.$itemKey, $openMode);
$this->keys[$nsKey][$itemKey] = $fp;
}
if (self::POSITION_START == $position) {
fseek($this->keys[$nsKey][$itemKey], 0, SEEK_SET);
} elseif (self::POSITION_END == $position) {
fseek($this->keys[$nsKey][$itemKey], 0, SEEK_END);
}
return $this->keys[$nsKey][$itemKey];
}
private function freeHandle($nsKey, $itemKey)
{
$fp = $this->getHandle($nsKey, $itemKey, self::POSITION_CURRENT);
fclose($fp);
$this->keys[$nsKey][$itemKey] = null;
}
/**
* Destructor.
*/
public function __destruct()
{
foreach ($this->keys as $nsKey => $null) {
$this->clearAll($nsKey);
}
}
public function __wakeup()
{
$this->keys = [];
}
}

View File

@@ -0,0 +1,47 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Writes data to a KeyCache using a stream.
*
* @author Chris Corbyn
*/
interface Swift_KeyCache_KeyCacheInputStream extends Swift_InputByteStream
{
/**
* Set the KeyCache to wrap.
*/
public function setKeyCache(Swift_KeyCache $keyCache);
/**
* Set the nsKey which will be written to.
*
* @param string $nsKey
*/
public function setNsKey($nsKey);
/**
* Set the itemKey which will be written to.
*
* @param string $itemKey
*/
public function setItemKey($itemKey);
/**
* Specify a stream to write through for each write().
*/
public function setWriteThroughStream(Swift_InputByteStream $is);
/**
* Any implementation should be cloneable, allowing the clone to access a
* separate $nsKey and $itemKey.
*/
public function __clone();
}

View File

@@ -0,0 +1,113 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* A null KeyCache that does not cache at all.
*
* @author Chris Corbyn
*/
class Swift_KeyCache_NullKeyCache implements Swift_KeyCache
{
/**
* Set a string into the cache under $itemKey for the namespace $nsKey.
*
* @see MODE_WRITE, MODE_APPEND
*
* @param string $nsKey
* @param string $itemKey
* @param string $string
* @param int $mode
*/
public function setString($nsKey, $itemKey, $string, $mode)
{
}
/**
* Set a ByteStream into the cache under $itemKey for the namespace $nsKey.
*
* @see MODE_WRITE, MODE_APPEND
*
* @param string $nsKey
* @param string $itemKey
* @param int $mode
*/
public function importFromByteStream($nsKey, $itemKey, Swift_OutputByteStream $os, $mode)
{
}
/**
* Provides a ByteStream which when written to, writes data to $itemKey.
*
* NOTE: The stream will always write in append mode.
*
* @param string $nsKey
* @param string $itemKey
*
* @return Swift_InputByteStream
*/
public function getInputByteStream($nsKey, $itemKey, Swift_InputByteStream $writeThrough = null)
{
}
/**
* Get data back out of the cache as a string.
*
* @param string $nsKey
* @param string $itemKey
*
* @return string
*/
public function getString($nsKey, $itemKey)
{
}
/**
* Get data back out of the cache as a ByteStream.
*
* @param string $nsKey
* @param string $itemKey
* @param Swift_InputByteStream $is to write the data to
*/
public function exportToByteStream($nsKey, $itemKey, Swift_InputByteStream $is)
{
}
/**
* Check if the given $itemKey exists in the namespace $nsKey.
*
* @param string $nsKey
* @param string $itemKey
*
* @return bool
*/
public function hasKey($nsKey, $itemKey)
{
return false;
}
/**
* Clear data for $itemKey in the namespace $nsKey if it exists.
*
* @param string $nsKey
* @param string $itemKey
*/
public function clearKey($nsKey, $itemKey)
{
}
/**
* Clear all data in the namespace $nsKey if it exists.
*
* @param string $nsKey
*/
public function clearAll($nsKey)
{
}
}

View File

@@ -0,0 +1,123 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Writes data to a KeyCache using a stream.
*
* @author Chris Corbyn
*/
class Swift_KeyCache_SimpleKeyCacheInputStream implements Swift_KeyCache_KeyCacheInputStream
{
/** The KeyCache being written to */
private $keyCache;
/** The nsKey of the KeyCache being written to */
private $nsKey;
/** The itemKey of the KeyCache being written to */
private $itemKey;
/** A stream to write through on each write() */
private $writeThrough = null;
/**
* Set the KeyCache to wrap.
*/
public function setKeyCache(Swift_KeyCache $keyCache)
{
$this->keyCache = $keyCache;
}
/**
* Specify a stream to write through for each write().
*/
public function setWriteThroughStream(Swift_InputByteStream $is)
{
$this->writeThrough = $is;
}
/**
* Writes $bytes to the end of the stream.
*
* @param string $bytes
* @param Swift_InputByteStream $is optional
*/
public function write($bytes, Swift_InputByteStream $is = null)
{
$this->keyCache->setString(
$this->nsKey, $this->itemKey, $bytes, Swift_KeyCache::MODE_APPEND
);
if (isset($is)) {
$is->write($bytes);
}
if (isset($this->writeThrough)) {
$this->writeThrough->write($bytes);
}
}
/**
* Not used.
*/
public function commit()
{
}
/**
* Not used.
*/
public function bind(Swift_InputByteStream $is)
{
}
/**
* Not used.
*/
public function unbind(Swift_InputByteStream $is)
{
}
/**
* Flush the contents of the stream (empty it) and set the internal pointer
* to the beginning.
*/
public function flushBuffers()
{
$this->keyCache->clearKey($this->nsKey, $this->itemKey);
}
/**
* Set the nsKey which will be written to.
*
* @param string $nsKey
*/
public function setNsKey($nsKey)
{
$this->nsKey = $nsKey;
}
/**
* Set the itemKey which will be written to.
*
* @param string $itemKey
*/
public function setItemKey($itemKey)
{
$this->itemKey = $itemKey;
}
/**
* Any implementation should be cloneable, allowing the clone to access a
* separate $nsKey and $itemKey.
*/
public function __clone()
{
$this->writeThrough = null;
}
}

View File

@@ -0,0 +1,33 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Redundantly and rotationally uses several Transport implementations when sending.
*
* @author Chris Corbyn
*/
class Swift_LoadBalancedTransport extends Swift_Transport_LoadBalancedTransport
{
/**
* Creates a new LoadBalancedTransport with $transports.
*
* @param array $transports
*/
public function __construct($transports = [])
{
\call_user_func_array(
[$this, 'Swift_Transport_LoadBalancedTransport::__construct'],
Swift_DependencyContainer::getInstance()
->createDependenciesFor('transport.loadbalanced')
);
$this->setTransports($transports);
}
}

View File

@@ -0,0 +1,98 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Swift Mailer class.
*
* @author Chris Corbyn
*/
class Swift_Mailer
{
/** The Transport used to send messages */
private $transport;
/**
* Create a new Mailer using $transport for delivery.
*/
public function __construct(Swift_Transport $transport)
{
$this->transport = $transport;
}
/**
* Create a new class instance of one of the message services.
*
* For example 'mimepart' would create a 'message.mimepart' instance
*
* @param string $service
*
* @return object
*/
public function createMessage($service = 'message')
{
return Swift_DependencyContainer::getInstance()
->lookup('message.'.$service);
}
/**
* Send the given Message like it would be sent in a mail client.
*
* All recipients (with the exception of Bcc) will be able to see the other
* recipients this message was sent to.
*
* Recipient/sender data will be retrieved from the Message object.
*
* The return value is the number of recipients who were accepted for
* delivery.
*
* @param array $failedRecipients An array of failures by-reference
*
* @return int The number of successful recipients. Can be 0 which indicates failure
*/
public function send(Swift_Mime_SimpleMessage $message, &$failedRecipients = null)
{
$failedRecipients = (array) $failedRecipients;
// FIXME: to be removed in 7.0 (as transport must now start itself on send)
if (!$this->transport->isStarted()) {
$this->transport->start();
}
$sent = 0;
try {
$sent = $this->transport->send($message, $failedRecipients);
} catch (Swift_RfcComplianceException $e) {
foreach ($message->getTo() as $address => $name) {
$failedRecipients[] = $address;
}
}
return $sent;
}
/**
* Register a plugin using a known unique key (e.g. myPlugin).
*/
public function registerPlugin(Swift_Events_EventListener $plugin)
{
$this->transport->registerPlugin($plugin);
}
/**
* The Transport used to send messages.
*
* @return Swift_Transport
*/
public function getTransport()
{
return $this->transport;
}
}

View File

@@ -0,0 +1,53 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Wraps a standard PHP array in an iterator.
*
* @author Chris Corbyn
*/
class Swift_Mailer_ArrayRecipientIterator implements Swift_Mailer_RecipientIterator
{
/**
* The list of recipients.
*
* @var array
*/
private $recipients = [];
/**
* Create a new ArrayRecipientIterator from $recipients.
*/
public function __construct(array $recipients)
{
$this->recipients = $recipients;
}
/**
* Returns true only if there are more recipients to send to.
*
* @return bool
*/
public function hasNext()
{
return !empty($this->recipients);
}
/**
* Returns an array where the keys are the addresses of recipients and the
* values are the names. e.g. ('foo@bar' => 'Foo') or ('foo@bar' => NULL).
*
* @return array
*/
public function nextRecipient()
{
return array_splice($this->recipients, 0, 1);
}
}

View File

@@ -0,0 +1,32 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Provides an abstract way of specifying recipients for batch sending.
*
* @author Chris Corbyn
*/
interface Swift_Mailer_RecipientIterator
{
/**
* Returns true only if there are more recipients to send to.
*
* @return bool
*/
public function hasNext();
/**
* Returns an array where the keys are the addresses of recipients and the
* values are the names. e.g. ('foo@bar' => 'Foo') or ('foo@bar' => NULL).
*
* @return array
*/
public function nextRecipient();
}

View File

@@ -0,0 +1,110 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2011 Fabien Potencier <fabien.potencier@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Stores Messages in memory.
*
* @author Fabien Potencier
*/
class Swift_MemorySpool implements Swift_Spool
{
protected $messages = [];
private $flushRetries = 3;
/**
* Tests if this Transport mechanism has started.
*
* @return bool
*/
public function isStarted()
{
return true;
}
/**
* Starts this Transport mechanism.
*/
public function start()
{
}
/**
* Stops this Transport mechanism.
*/
public function stop()
{
}
/**
* @param int $retries
*/
public function setFlushRetries($retries)
{
$this->flushRetries = $retries;
}
/**
* Stores a message in the queue.
*
* @param Swift_Mime_SimpleMessage $message The message to store
*
* @return bool Whether the operation has succeeded
*/
public function queueMessage(Swift_Mime_SimpleMessage $message)
{
//clone the message to make sure it is not changed while in the queue
$this->messages[] = clone $message;
return true;
}
/**
* Sends messages using the given transport instance.
*
* @param Swift_Transport $transport A transport instance
* @param string[] $failedRecipients An array of failures by-reference
*
* @return int The number of sent emails
*/
public function flushQueue(Swift_Transport $transport, &$failedRecipients = null)
{
if (!$this->messages) {
return 0;
}
if (!$transport->isStarted()) {
$transport->start();
}
$count = 0;
$retries = $this->flushRetries;
while ($retries--) {
try {
while ($message = array_pop($this->messages)) {
$count += $transport->send($message, $failedRecipients);
}
} catch (Swift_TransportException $exception) {
if ($retries) {
// re-queue the message at the end of the queue to give a chance
// to the other messages to be sent, in case the failure was due to
// this message and not just the transport failing
array_unshift($this->messages, $message);
// wait half a second before we try again
usleep(500000);
} else {
throw $exception;
}
}
}
return $count;
}
}

View File

@@ -0,0 +1,279 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* The Message class for building emails.
*
* @author Chris Corbyn
*/
class Swift_Message extends Swift_Mime_SimpleMessage
{
/**
* @var Swift_Signers_HeaderSigner[]
*/
private $headerSigners = [];
/**
* @var Swift_Signers_BodySigner[]
*/
private $bodySigners = [];
/**
* @var array
*/
private $savedMessage = [];
/**
* Create a new Message.
*
* Details may be optionally passed into the constructor.
*
* @param string $subject
* @param string $body
* @param string $contentType
* @param string $charset
*/
public function __construct($subject = null, $body = null, $contentType = null, $charset = null)
{
\call_user_func_array(
[$this, 'Swift_Mime_SimpleMessage::__construct'],
Swift_DependencyContainer::getInstance()
->createDependenciesFor('mime.message')
);
if (!isset($charset)) {
$charset = Swift_DependencyContainer::getInstance()
->lookup('properties.charset');
}
$this->setSubject($subject);
$this->setBody($body);
$this->setCharset($charset);
if ($contentType) {
$this->setContentType($contentType);
}
}
/**
* Add a MimePart to this Message.
*
* @param string|Swift_OutputByteStream $body
* @param string $contentType
* @param string $charset
*
* @return $this
*/
public function addPart($body, $contentType = null, $charset = null)
{
return $this->attach((new Swift_MimePart($body, $contentType, $charset))->setEncoder($this->getEncoder()));
}
/**
* Attach a new signature handler to the message.
*
* @return $this
*/
public function attachSigner(Swift_Signer $signer)
{
if ($signer instanceof Swift_Signers_HeaderSigner) {
$this->headerSigners[] = $signer;
} elseif ($signer instanceof Swift_Signers_BodySigner) {
$this->bodySigners[] = $signer;
}
return $this;
}
/**
* Detach a signature handler from a message.
*
* @return $this
*/
public function detachSigner(Swift_Signer $signer)
{
if ($signer instanceof Swift_Signers_HeaderSigner) {
foreach ($this->headerSigners as $k => $headerSigner) {
if ($headerSigner === $signer) {
unset($this->headerSigners[$k]);
return $this;
}
}
} elseif ($signer instanceof Swift_Signers_BodySigner) {
foreach ($this->bodySigners as $k => $bodySigner) {
if ($bodySigner === $signer) {
unset($this->bodySigners[$k]);
return $this;
}
}
}
return $this;
}
/**
* Clear all signature handlers attached to the message.
*
* @return $this
*/
public function clearSigners()
{
$this->headerSigners = [];
$this->bodySigners = [];
return $this;
}
/**
* Get this message as a complete string.
*
* @return string
*/
public function toString()
{
if (empty($this->headerSigners) && empty($this->bodySigners)) {
return parent::toString();
}
$this->saveMessage();
$this->doSign();
$string = parent::toString();
$this->restoreMessage();
return $string;
}
/**
* Write this message to a {@link Swift_InputByteStream}.
*/
public function toByteStream(Swift_InputByteStream $is)
{
if (empty($this->headerSigners) && empty($this->bodySigners)) {
parent::toByteStream($is);
return;
}
$this->saveMessage();
$this->doSign();
parent::toByteStream($is);
$this->restoreMessage();
}
public function __wakeup()
{
Swift_DependencyContainer::getInstance()->createDependenciesFor('mime.message');
}
/**
* loops through signers and apply the signatures.
*/
protected function doSign()
{
foreach ($this->bodySigners as $signer) {
$altered = $signer->getAlteredHeaders();
$this->saveHeaders($altered);
$signer->signMessage($this);
}
foreach ($this->headerSigners as $signer) {
$altered = $signer->getAlteredHeaders();
$this->saveHeaders($altered);
$signer->reset();
$signer->setHeaders($this->getHeaders());
$signer->startBody();
$this->bodyToByteStream($signer);
$signer->endBody();
$signer->addSignature($this->getHeaders());
}
}
/**
* save the message before any signature is applied.
*/
protected function saveMessage()
{
$this->savedMessage = ['headers' => []];
$this->savedMessage['body'] = $this->getBody();
$this->savedMessage['children'] = $this->getChildren();
if (\count($this->savedMessage['children']) > 0 && '' != $this->getBody()) {
$this->setChildren(array_merge([$this->becomeMimePart()], $this->savedMessage['children']));
$this->setBody('');
}
}
/**
* save the original headers.
*/
protected function saveHeaders(array $altered)
{
foreach ($altered as $head) {
$lc = strtolower($head ?? '');
if (!isset($this->savedMessage['headers'][$lc])) {
$this->savedMessage['headers'][$lc] = $this->getHeaders()->getAll($head);
}
}
}
/**
* Remove or restore altered headers.
*/
protected function restoreHeaders()
{
foreach ($this->savedMessage['headers'] as $name => $savedValue) {
$headers = $this->getHeaders()->getAll($name);
foreach ($headers as $key => $value) {
if (!isset($savedValue[$key])) {
$this->getHeaders()->remove($name, $key);
}
}
}
}
/**
* Restore message body.
*/
protected function restoreMessage()
{
$this->setBody($this->savedMessage['body']);
$this->setChildren($this->savedMessage['children']);
$this->restoreHeaders();
$this->savedMessage = [];
}
/**
* Clone Message Signers.
*
* @see Swift_Mime_SimpleMimeEntity::__clone()
*/
public function __clone()
{
parent::__clone();
foreach ($this->bodySigners as $key => $bodySigner) {
$this->bodySigners[$key] = clone $bodySigner;
}
foreach ($this->headerSigners as $key => $headerSigner) {
$this->headerSigners[$key] = clone $headerSigner;
}
}
}

View File

@@ -0,0 +1,144 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* An attachment, in a multipart message.
*
* @author Chris Corbyn
*/
class Swift_Mime_Attachment extends Swift_Mime_SimpleMimeEntity
{
/** Recognized MIME types */
private $mimeTypes = [];
/**
* Create a new Attachment with $headers, $encoder and $cache.
*
* @param array $mimeTypes
*/
public function __construct(Swift_Mime_SimpleHeaderSet $headers, Swift_Mime_ContentEncoder $encoder, Swift_KeyCache $cache, Swift_IdGenerator $idGenerator, $mimeTypes = [])
{
parent::__construct($headers, $encoder, $cache, $idGenerator);
$this->setDisposition('attachment');
$this->setContentType('application/octet-stream');
$this->mimeTypes = $mimeTypes;
}
/**
* Get the nesting level used for this attachment.
*
* Always returns {@link LEVEL_MIXED}.
*
* @return int
*/
public function getNestingLevel()
{
return self::LEVEL_MIXED;
}
/**
* Get the Content-Disposition of this attachment.
*
* By default attachments have a disposition of "attachment".
*
* @return string
*/
public function getDisposition()
{
return $this->getHeaderFieldModel('Content-Disposition');
}
/**
* Set the Content-Disposition of this attachment.
*
* @param string $disposition
*
* @return $this
*/
public function setDisposition($disposition)
{
if (!$this->setHeaderFieldModel('Content-Disposition', $disposition)) {
$this->getHeaders()->addParameterizedHeader('Content-Disposition', $disposition);
}
return $this;
}
/**
* Get the filename of this attachment when downloaded.
*
* @return string
*/
public function getFilename()
{
return $this->getHeaderParameter('Content-Disposition', 'filename');
}
/**
* Set the filename of this attachment.
*
* @param string $filename
*
* @return $this
*/
public function setFilename($filename)
{
$this->setHeaderParameter('Content-Disposition', 'filename', $filename);
$this->setHeaderParameter('Content-Type', 'name', $filename);
return $this;
}
/**
* Get the file size of this attachment.
*
* @return int
*/
public function getSize()
{
return $this->getHeaderParameter('Content-Disposition', 'size');
}
/**
* Set the file size of this attachment.
*
* @param int $size
*
* @return $this
*/
public function setSize($size)
{
$this->setHeaderParameter('Content-Disposition', 'size', $size);
return $this;
}
/**
* Set the file that this attachment is for.
*
* @param string $contentType optional
*
* @return $this
*/
public function setFile(Swift_FileStream $file, $contentType = null)
{
$this->setFilename(basename($file->getPath()));
$this->setBody($file, $contentType);
if (!isset($contentType)) {
$extension = strtolower(substr($file->getPath(), strrpos($file->getPath(), '.') + 1));
if (\array_key_exists($extension, $this->mimeTypes)) {
$this->setContentType($this->mimeTypes[$extension]);
}
}
return $this;
}
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Observes changes in an Mime entity's character set.
*
* @author Chris Corbyn
*/
interface Swift_Mime_CharsetObserver
{
/**
* Notify this observer that the entity's charset has changed.
*
* @param string $charset
*/
public function charsetChanged($charset);
}

View File

@@ -0,0 +1,34 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Interface for all Transfer Encoding schemes.
*
* @author Chris Corbyn
*/
interface Swift_Mime_ContentEncoder extends Swift_Encoder
{
/**
* Encode $in to $out.
*
* @param Swift_OutputByteStream $os to read from
* @param Swift_InputByteStream $is to write to
* @param int $firstLineOffset
* @param int $maxLineLength - 0 indicates the default length for this encoding
*/
public function encodeByteStream(Swift_OutputByteStream $os, Swift_InputByteStream $is, $firstLineOffset = 0, $maxLineLength = 0);
/**
* Get the MIME name of this content encoding scheme.
*
* @return string
*/
public function getName();
}

View File

@@ -0,0 +1,101 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Handles Base 64 Transfer Encoding in Swift Mailer.
*
* @author Chris Corbyn
*/
class Swift_Mime_ContentEncoder_Base64ContentEncoder extends Swift_Encoder_Base64Encoder implements Swift_Mime_ContentEncoder
{
/**
* Encode stream $in to stream $out.
*
* @param int $firstLineOffset
*/
public function encodeByteStream(Swift_OutputByteStream $os, Swift_InputByteStream $is, $firstLineOffset = 0, $maxLineLength = 0)
{
if (0 >= $maxLineLength || 76 < $maxLineLength) {
$maxLineLength = 76;
}
$remainder = 0;
$base64ReadBufferRemainderBytes = '';
// To reduce memory usage, the output buffer is streamed to the input buffer like so:
// Output Stream => base64encode => wrap line length => Input Stream
// HOWEVER it's important to note that base64_encode() should only be passed whole triplets of data (except for the final chunk of data)
// otherwise it will assume the input data has *ended* and it will incorrectly pad/terminate the base64 data mid-stream.
// We use $base64ReadBufferRemainderBytes to carry over 1-2 "remainder" bytes from the each chunk from OutputStream and pre-pend those onto the
// chunk of bytes read in the next iteration.
// When the OutputStream is empty, we must flush any remainder bytes.
while (true) {
$readBytes = $os->read(8192);
$atEOF = (false === $readBytes);
if ($atEOF) {
$streamTheseBytes = $base64ReadBufferRemainderBytes;
} else {
$streamTheseBytes = $base64ReadBufferRemainderBytes.$readBytes;
}
$base64ReadBufferRemainderBytes = '';
$bytesLength = \strlen($streamTheseBytes);
if (0 === $bytesLength) { // no data left to encode
break;
}
// if we're not on the last block of the ouput stream, make sure $streamTheseBytes ends with a complete triplet of data
// and carry over remainder 1-2 bytes to the next loop iteration
if (!$atEOF) {
$excessBytes = $bytesLength % 3;
if (0 !== $excessBytes) {
$base64ReadBufferRemainderBytes = substr($streamTheseBytes, -$excessBytes);
$streamTheseBytes = substr($streamTheseBytes, 0, $bytesLength - $excessBytes);
}
}
$encoded = base64_encode($streamTheseBytes);
$encodedTransformed = '';
$thisMaxLineLength = $maxLineLength - $remainder - $firstLineOffset;
while ($thisMaxLineLength < \strlen($encoded)) {
$encodedTransformed .= substr($encoded, 0, $thisMaxLineLength)."\r\n";
$firstLineOffset = 0;
$encoded = substr($encoded, $thisMaxLineLength);
$thisMaxLineLength = $maxLineLength;
$remainder = 0;
}
if (0 < $remainingLength = \strlen($encoded)) {
$remainder += $remainingLength;
$encodedTransformed .= $encoded;
$encoded = null;
}
$is->write($encodedTransformed);
if ($atEOF) {
break;
}
}
}
/**
* Get the name of this encoding scheme.
* Returns the string 'base64'.
*
* @return string
*/
public function getName()
{
return 'base64';
}
}

View File

@@ -0,0 +1,121 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Handles Quoted Printable (QP) Transfer Encoding in Swift Mailer using the PHP core function.
*
* @author Lars Strojny
*/
class Swift_Mime_ContentEncoder_NativeQpContentEncoder implements Swift_Mime_ContentEncoder
{
/**
* @var string|null
*/
private $charset;
/**
* @param string|null $charset
*/
public function __construct($charset = null)
{
$this->charset = $charset ?: 'utf-8';
}
/**
* Notify this observer that the entity's charset has changed.
*
* @param string $charset
*/
public function charsetChanged($charset)
{
$this->charset = $charset;
}
/**
* Encode $in to $out.
*
* @param Swift_OutputByteStream $os to read from
* @param Swift_InputByteStream $is to write to
* @param int $firstLineOffset
* @param int $maxLineLength 0 indicates the default length for this encoding
*
* @throws RuntimeException
*/
public function encodeByteStream(Swift_OutputByteStream $os, Swift_InputByteStream $is, $firstLineOffset = 0, $maxLineLength = 0)
{
if ('utf-8' !== $this->charset) {
throw new RuntimeException(sprintf('Charset "%s" not supported. NativeQpContentEncoder only supports "utf-8"', $this->charset));
}
$string = '';
while (false !== $bytes = $os->read(8192)) {
$string .= $bytes;
}
$is->write($this->encodeString($string));
}
/**
* Get the MIME name of this content encoding scheme.
*
* @return string
*/
public function getName()
{
return 'quoted-printable';
}
/**
* Encode a given string to produce an encoded string.
*
* @param string $string
* @param int $firstLineOffset if first line needs to be shorter
* @param int $maxLineLength 0 indicates the default length for this encoding
*
* @throws RuntimeException
*
* @return string
*/
public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0)
{
if ('utf-8' !== $this->charset) {
throw new RuntimeException(sprintf('Charset "%s" not supported. NativeQpContentEncoder only supports "utf-8"', $this->charset));
}
return $this->standardize(quoted_printable_encode($string));
}
/**
* Make sure CRLF is correct and HT/SPACE are in valid places.
*
* @param string $string
*
* @return string
*/
protected function standardize($string)
{
// transform CR or LF to CRLF
$string = preg_replace('~=0D(?!=0A)|(?<!=0D)=0A~', '=0D=0A', $string);
// transform =0D=0A to CRLF
$string = str_replace(["\t=0D=0A", ' =0D=0A', '=0D=0A'], ["=09\r\n", "=20\r\n", "\r\n"], $string);
switch (\ord(substr($string, -1))) {
case 0x09:
$string = substr_replace($string, '=09', -1);
break;
case 0x20:
$string = substr_replace($string, '=20', -1);
break;
}
return $string;
}
}

View File

@@ -0,0 +1,79 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Handles the case where the email body is already encoded and you just need specify the correct
* encoding without actually changing the encoding of the body.
*
* @author Jan Flora <jf@penneo.com>
*/
class Swift_Mime_ContentEncoder_NullContentEncoder implements Swift_Mime_ContentEncoder
{
/**
* The name of this encoding scheme (probably 7bit or 8bit).
*
* @var string
*/
private $name;
/**
* Creates a new NullContentEncoder with $name (probably 7bit or 8bit).
*
* @param string $name
*/
public function __construct($name)
{
$this->name = $name;
}
/**
* Encode a given string to produce an encoded string.
*
* @param string $string
* @param int $firstLineOffset ignored
* @param int $maxLineLength ignored
*
* @return string
*/
public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0)
{
return $string;
}
/**
* Encode stream $in to stream $out.
*
* @param int $firstLineOffset ignored
* @param int $maxLineLength ignored
*/
public function encodeByteStream(Swift_OutputByteStream $os, Swift_InputByteStream $is, $firstLineOffset = 0, $maxLineLength = 0)
{
while (false !== ($bytes = $os->read(8192))) {
$is->write($bytes);
}
}
/**
* Get the name of this encoding scheme.
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Not used.
*/
public function charsetChanged($charset)
{
}
}

View File

@@ -0,0 +1,164 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Handles binary/7/8-bit Transfer Encoding in Swift Mailer.
*
* When sending 8-bit content over SMTP, you should use
* Swift_Transport_Esmtp_EightBitMimeHandler to enable the 8BITMIME SMTP
* extension.
*
* @author Chris Corbyn
*/
class Swift_Mime_ContentEncoder_PlainContentEncoder implements Swift_Mime_ContentEncoder
{
/**
* The name of this encoding scheme (probably 7bit or 8bit).
*
* @var string
*/
private $name;
/**
* True if canonical transformations should be done.
*
* @var bool
*/
private $canonical;
/**
* Creates a new PlainContentEncoder with $name (probably 7bit or 8bit).
*
* @param string $name
* @param bool $canonical if canonicalization transformation should be done
*/
public function __construct($name, $canonical = false)
{
$this->name = $name;
$this->canonical = $canonical;
}
/**
* Encode a given string to produce an encoded string.
*
* @param string $string
* @param int $firstLineOffset ignored
* @param int $maxLineLength - 0 means no wrapping will occur
*
* @return string
*/
public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0)
{
if ($this->canonical) {
$string = $this->canonicalize($string);
}
return $this->safeWordwrap($string, $maxLineLength, "\r\n");
}
/**
* Encode stream $in to stream $out.
*
* @param int $firstLineOffset ignored
* @param int $maxLineLength optional, 0 means no wrapping will occur
*/
public function encodeByteStream(Swift_OutputByteStream $os, Swift_InputByteStream $is, $firstLineOffset = 0, $maxLineLength = 0)
{
$leftOver = '';
while (false !== $bytes = $os->read(8192)) {
$toencode = $leftOver.$bytes;
if ($this->canonical) {
$toencode = $this->canonicalize($toencode);
}
$wrapped = $this->safeWordwrap($toencode, $maxLineLength, "\r\n");
$lastLinePos = strrpos($wrapped, "\r\n");
$leftOver = substr($wrapped, $lastLinePos);
$wrapped = substr($wrapped, 0, $lastLinePos);
$is->write($wrapped);
}
if (\strlen($leftOver)) {
$is->write($leftOver);
}
}
/**
* Get the name of this encoding scheme.
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Not used.
*/
public function charsetChanged($charset)
{
}
/**
* A safer (but weaker) wordwrap for unicode.
*
* @param string $string
* @param int $length
* @param string $le
*
* @return string
*/
private function safeWordwrap($string, $length = 75, $le = "\r\n")
{
if (0 >= $length) {
return $string;
}
$originalLines = explode($le, $string);
$lines = [];
$lineCount = 0;
foreach ($originalLines as $originalLine) {
$lines[] = '';
$currentLine = &$lines[$lineCount++];
//$chunks = preg_split('/(?<=[\ \t,\.!\?\-&\+\/])/', $originalLine);
$chunks = preg_split('/(?<=\s)/', $originalLine);
foreach ($chunks as $chunk) {
if (0 != \strlen($currentLine)
&& \strlen($currentLine.$chunk) > $length) {
$lines[] = '';
$currentLine = &$lines[$lineCount++];
}
$currentLine .= $chunk;
}
}
return implode("\r\n", $lines);
}
/**
* Canonicalize string input (fix CRLF).
*
* @param string $string
*
* @return string
*/
private function canonicalize($string)
{
return str_replace(
["\r\n", "\r", "\n"],
["\n", "\n", "\r\n"],
$string
);
}
}

View File

@@ -0,0 +1,134 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Handles Quoted Printable (QP) Transfer Encoding in Swift Mailer.
*
* @author Chris Corbyn
*/
class Swift_Mime_ContentEncoder_QpContentEncoder extends Swift_Encoder_QpEncoder implements Swift_Mime_ContentEncoder
{
protected $dotEscape;
/**
* Creates a new QpContentEncoder for the given CharacterStream.
*
* @param Swift_CharacterStream $charStream to use for reading characters
* @param Swift_StreamFilter $filter if canonicalization should occur
* @param bool $dotEscape if dot stuffing workaround must be enabled
*/
public function __construct(Swift_CharacterStream $charStream, Swift_StreamFilter $filter = null, $dotEscape = false)
{
$this->dotEscape = $dotEscape;
parent::__construct($charStream, $filter);
}
public function __sleep()
{
return ['charStream', 'filter', 'dotEscape'];
}
protected function getSafeMapShareId()
{
return static::class.($this->dotEscape ? '.dotEscape' : '');
}
protected function initSafeMap()
{
parent::initSafeMap();
if ($this->dotEscape) {
/* Encode . as =2e for buggy remote servers */
unset($this->safeMap[0x2e]);
}
}
/**
* Encode stream $in to stream $out.
*
* QP encoded strings have a maximum line length of 76 characters.
* If the first line needs to be shorter, indicate the difference with
* $firstLineOffset.
*
* @param Swift_OutputByteStream $os output stream
* @param Swift_InputByteStream $is input stream
* @param int $firstLineOffset
* @param int $maxLineLength
*/
public function encodeByteStream(Swift_OutputByteStream $os, Swift_InputByteStream $is, $firstLineOffset = 0, $maxLineLength = 0)
{
if ($maxLineLength > 76 || $maxLineLength <= 0) {
$maxLineLength = 76;
}
$thisLineLength = $maxLineLength - $firstLineOffset;
$this->charStream->flushContents();
$this->charStream->importByteStream($os);
$currentLine = '';
$prepend = '';
$size = $lineLen = 0;
while (false !== $bytes = $this->nextSequence()) {
// If we're filtering the input
if (isset($this->filter)) {
// If we can't filter because we need more bytes
while ($this->filter->shouldBuffer($bytes)) {
// Then collect bytes into the buffer
if (false === $moreBytes = $this->nextSequence(1)) {
break;
}
foreach ($moreBytes as $b) {
$bytes[] = $b;
}
}
// And filter them
$bytes = $this->filter->filter($bytes);
}
$enc = $this->encodeByteSequence($bytes, $size);
$i = strpos($enc, '=0D=0A');
$newLineLength = $lineLen + (false === $i ? $size : $i);
if ($currentLine && $newLineLength >= $thisLineLength) {
$is->write($prepend.$this->standardize($currentLine));
$currentLine = '';
$prepend = "=\r\n";
$thisLineLength = $maxLineLength;
$lineLen = 0;
}
$currentLine .= $enc;
if (false === $i) {
$lineLen += $size;
} else {
// 6 is the length of '=0D=0A'.
$lineLen = $size - strrpos($enc, '=0D=0A') - 6;
}
}
if (\strlen($currentLine)) {
$is->write($prepend.$this->standardize($currentLine));
}
}
/**
* Get the name of this encoding scheme.
* Returns the string 'quoted-printable'.
*
* @return string
*/
public function getName()
{
return 'quoted-printable';
}
}

View File

@@ -0,0 +1,96 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Proxy for quoted-printable content encoders.
*
* Switches on the best QP encoder implementation for current charset.
*
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
*/
class Swift_Mime_ContentEncoder_QpContentEncoderProxy implements Swift_Mime_ContentEncoder
{
/**
* @var Swift_Mime_ContentEncoder_QpContentEncoder
*/
private $safeEncoder;
/**
* @var Swift_Mime_ContentEncoder_NativeQpContentEncoder
*/
private $nativeEncoder;
/**
* @var string|null
*/
private $charset;
/**
* Constructor.
*
* @param string|null $charset
*/
public function __construct(Swift_Mime_ContentEncoder_QpContentEncoder $safeEncoder, Swift_Mime_ContentEncoder_NativeQpContentEncoder $nativeEncoder, $charset)
{
$this->safeEncoder = $safeEncoder;
$this->nativeEncoder = $nativeEncoder;
$this->charset = $charset;
}
/**
* Make a deep copy of object.
*/
public function __clone()
{
$this->safeEncoder = clone $this->safeEncoder;
$this->nativeEncoder = clone $this->nativeEncoder;
}
/**
* {@inheritdoc}
*/
public function charsetChanged($charset)
{
$this->charset = $charset;
$this->safeEncoder->charsetChanged($charset);
}
/**
* {@inheritdoc}
*/
public function encodeByteStream(Swift_OutputByteStream $os, Swift_InputByteStream $is, $firstLineOffset = 0, $maxLineLength = 0)
{
$this->getEncoder()->encodeByteStream($os, $is, $firstLineOffset, $maxLineLength);
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'quoted-printable';
}
/**
* {@inheritdoc}
*/
public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0)
{
return $this->getEncoder()->encodeString($string, $firstLineOffset, $maxLineLength);
}
/**
* @return Swift_Mime_ContentEncoder
*/
private function getEncoder()
{
return 'utf-8' === $this->charset ? $this->nativeEncoder : $this->safeEncoder;
}
}

View File

@@ -0,0 +1,65 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Handles raw Transfer Encoding in Swift Mailer.
*
* When sending 8-bit content over SMTP, you should use
* Swift_Transport_Esmtp_EightBitMimeHandler to enable the 8BITMIME SMTP
* extension.
*
* @author Sebastiaan Stok <s.stok@rollerscapes.net>
*/
class Swift_Mime_ContentEncoder_RawContentEncoder implements Swift_Mime_ContentEncoder
{
/**
* Encode a given string to produce an encoded string.
*
* @param string $string
* @param int $firstLineOffset ignored
* @param int $maxLineLength ignored
*
* @return string
*/
public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0)
{
return $string;
}
/**
* Encode stream $in to stream $out.
*
* @param int $firstLineOffset ignored
* @param int $maxLineLength ignored
*/
public function encodeByteStream(Swift_OutputByteStream $os, Swift_InputByteStream $is, $firstLineOffset = 0, $maxLineLength = 0)
{
while (false !== ($bytes = $os->read(8192))) {
$is->write($bytes);
}
}
/**
* Get the name of this encoding scheme.
*
* @return string
*/
public function getName()
{
return 'raw';
}
/**
* Not used.
*/
public function charsetChanged($charset)
{
}
}

View File

@@ -0,0 +1,41 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* An embedded file, in a multipart message.
*
* @author Chris Corbyn
*/
class Swift_Mime_EmbeddedFile extends Swift_Mime_Attachment
{
/**
* Creates a new Attachment with $headers and $encoder.
*
* @param array $mimeTypes optional
*/
public function __construct(Swift_Mime_SimpleHeaderSet $headers, Swift_Mime_ContentEncoder $encoder, Swift_KeyCache $cache, Swift_IdGenerator $idGenerator, $mimeTypes = [])
{
parent::__construct($headers, $encoder, $cache, $idGenerator, $mimeTypes);
$this->setDisposition('inline');
$this->setId($this->getId());
}
/**
* Get the nesting level of this EmbeddedFile.
*
* Returns {@see LEVEL_RELATED}.
*
* @return int
*/
public function getNestingLevel()
{
return self::LEVEL_RELATED;
}
}

View File

@@ -0,0 +1,22 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Observes changes for a Mime entity's ContentEncoder.
*
* @author Chris Corbyn
*/
interface Swift_Mime_EncodingObserver
{
/**
* Notify this observer that the observed entity's ContentEncoder has changed.
*/
public function encoderChanged(Swift_Mime_ContentEncoder $encoder);
}

View File

@@ -0,0 +1,93 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* A MIME Header.
*
* @author Chris Corbyn
*/
interface Swift_Mime_Header
{
/** Text headers */
const TYPE_TEXT = 2;
/** headers (text + params) */
const TYPE_PARAMETERIZED = 6;
/** Mailbox and address headers */
const TYPE_MAILBOX = 8;
/** Date and time headers */
const TYPE_DATE = 16;
/** Identification headers */
const TYPE_ID = 32;
/** Address path headers */
const TYPE_PATH = 64;
/**
* Get the type of Header that this instance represents.
*
* @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX
* @see TYPE_DATE, TYPE_ID, TYPE_PATH
*
* @return int
*/
public function getFieldType();
/**
* Set the model for the field body.
*
* The actual types needed will vary depending upon the type of Header.
*
* @param mixed $model
*/
public function setFieldBodyModel($model);
/**
* Set the charset used when rendering the Header.
*
* @param string $charset
*/
public function setCharset($charset);
/**
* Get the model for the field body.
*
* The return type depends on the specifics of the Header.
*
* @return mixed
*/
public function getFieldBodyModel();
/**
* Get the name of this header (e.g. Subject).
*
* The name is an identifier and as such will be immutable.
*
* @return string
*/
public function getFieldName();
/**
* Get the field body, prepared for folding into a final header value.
*
* @return string
*/
public function getFieldBody();
/**
* Get this Header rendered as a compliant string, including trailing CRLF.
*
* @return string
*/
public function toString();
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Interface for all Header Encoding schemes.
*
* @author Chris Corbyn
*/
interface Swift_Mime_HeaderEncoder extends Swift_Encoder
{
/**
* Get the MIME name of this content encoding scheme.
*
* @return string
*/
public function getName();
}

View File

@@ -0,0 +1,55 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Handles Base64 (B) Header Encoding in Swift Mailer.
*
* @author Chris Corbyn
*/
class Swift_Mime_HeaderEncoder_Base64HeaderEncoder extends Swift_Encoder_Base64Encoder implements Swift_Mime_HeaderEncoder
{
/**
* Get the name of this encoding scheme.
* Returns the string 'B'.
*
* @return string
*/
public function getName()
{
return 'B';
}
/**
* Takes an unencoded string and produces a Base64 encoded string from it.
*
* If the charset is iso-2022-jp, it uses mb_encode_mimeheader instead of
* default encodeString, otherwise pass to the parent method.
*
* @param string $string string to encode
* @param int $firstLineOffset
* @param int $maxLineLength optional, 0 indicates the default of 76 bytes
* @param string $charset
*
* @return string
*/
public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0, $charset = 'utf-8')
{
if ('iso-2022-jp' === strtolower($charset ?? '')) {
$old = mb_internal_encoding();
mb_internal_encoding('utf-8');
$newstring = mb_encode_mimeheader($string, $charset, $this->getName(), "\r\n");
mb_internal_encoding($old);
return $newstring;
}
return parent::encodeString($string, $firstLineOffset, $maxLineLength);
}
}

View File

@@ -0,0 +1,65 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Handles Quoted Printable (Q) Header Encoding in Swift Mailer.
*
* @author Chris Corbyn
*/
class Swift_Mime_HeaderEncoder_QpHeaderEncoder extends Swift_Encoder_QpEncoder implements Swift_Mime_HeaderEncoder
{
/**
* Creates a new QpHeaderEncoder for the given CharacterStream.
*
* @param Swift_CharacterStream $charStream to use for reading characters
*/
public function __construct(Swift_CharacterStream $charStream)
{
parent::__construct($charStream);
}
protected function initSafeMap()
{
foreach (array_merge(
range(0x61, 0x7A), range(0x41, 0x5A),
range(0x30, 0x39), [0x20, 0x21, 0x2A, 0x2B, 0x2D, 0x2F]
) as $byte) {
$this->safeMap[$byte] = \chr($byte);
}
}
/**
* Get the name of this encoding scheme.
*
* Returns the string 'Q'.
*
* @return string
*/
public function getName()
{
return 'Q';
}
/**
* Takes an unencoded string and produces a QP encoded string from it.
*
* @param string $string string to encode
* @param int $firstLineOffset optional
* @param int $maxLineLength optional, 0 indicates the default of 76 chars
*
* @return string
*/
public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0)
{
return str_replace([' ', '=20', "=\r\n"], ['_', '_', "\r\n"],
parent::encodeString($string, $firstLineOffset, $maxLineLength)
);
}
}

View File

@@ -0,0 +1,486 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* An abstract base MIME Header.
*
* @author Chris Corbyn
*/
abstract class Swift_Mime_Headers_AbstractHeader implements Swift_Mime_Header
{
const PHRASE_PATTERN = '(?:(?:(?:(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))*(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))|(?:(?:[ \t]*(?:\r\n))?[ \t])))?[a-zA-Z0-9!#\$%&\'\*\+\-\/=\?\^_`\{\}\|~]+(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))*(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))|(?:(?:[ \t]*(?:\r\n))?[ \t])))?)|(?:(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))*(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))|(?:(?:[ \t]*(?:\r\n))?[ \t])))?"((?:(?:[ \t]*(?:\r\n))?[ \t])?(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21\x23-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])))*(?:(?:[ \t]*(?:\r\n))?[ \t])?"(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))*(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))|(?:(?:[ \t]*(?:\r\n))?[ \t])))?))+?)';
/**
* The name of this Header.
*
* @var string
*/
private $name;
/**
* The Encoder used to encode this Header.
*
* @var Swift_Encoder
*/
private $encoder;
/**
* The maximum length of a line in the header.
*
* @var int
*/
private $lineLength = 78;
/**
* The language used in this Header.
*
* @var string
*/
private $lang;
/**
* The character set of the text in this Header.
*
* @var string
*/
private $charset = 'utf-8';
/**
* The value of this Header, cached.
*
* @var string
*/
private $cachedValue = null;
/**
* Set the character set used in this Header.
*
* @param string $charset
*/
public function setCharset($charset)
{
$this->clearCachedValueIf($charset != $this->charset);
$this->charset = $charset;
if (isset($this->encoder)) {
$this->encoder->charsetChanged($charset);
}
}
/**
* Get the character set used in this Header.
*
* @return string
*/
public function getCharset()
{
return $this->charset;
}
/**
* Set the language used in this Header.
*
* For example, for US English, 'en-us'.
* This can be unspecified.
*
* @param string $lang
*/
public function setLanguage($lang)
{
$this->clearCachedValueIf($this->lang != $lang);
$this->lang = $lang;
}
/**
* Get the language used in this Header.
*
* @return string
*/
public function getLanguage()
{
return $this->lang;
}
/**
* Set the encoder used for encoding the header.
*/
public function setEncoder(Swift_Mime_HeaderEncoder $encoder)
{
$this->encoder = $encoder;
$this->setCachedValue(null);
}
/**
* Get the encoder used for encoding this Header.
*
* @return Swift_Mime_HeaderEncoder
*/
public function getEncoder()
{
return $this->encoder;
}
/**
* Get the name of this header (e.g. charset).
*
* @return string
*/
public function getFieldName()
{
return $this->name;
}
/**
* Set the maximum length of lines in the header (excluding EOL).
*
* @param int $lineLength
*/
public function setMaxLineLength($lineLength)
{
$this->clearCachedValueIf($this->lineLength != $lineLength);
$this->lineLength = $lineLength;
}
/**
* Get the maximum permitted length of lines in this Header.
*
* @return int
*/
public function getMaxLineLength()
{
return $this->lineLength;
}
/**
* Get this Header rendered as a RFC 2822 compliant string.
*
* @return string
*
* @throws Swift_RfcComplianceException
*/
public function toString()
{
return $this->tokensToString($this->toTokens());
}
/**
* Returns a string representation of this object.
*
* @return string
*
* @see toString()
*/
public function __toString()
{
return $this->toString();
}
/**
* Set the name of this Header field.
*
* @param string $name
*/
protected function setFieldName($name)
{
$this->name = $name;
}
/**
* Produces a compliant, formatted RFC 2822 'phrase' based on the string given.
*
* @param string $string as displayed
* @param string $charset of the text
* @param bool $shorten the first line to make remove for header name
*
* @return string
*/
protected function createPhrase(Swift_Mime_Header $header, $string, $charset, Swift_Mime_HeaderEncoder $encoder = null, $shorten = false)
{
// Treat token as exactly what was given
$phraseStr = $string;
// If it's not valid
if (!preg_match('/^'.self::PHRASE_PATTERN.'$/D', $phraseStr)) {
// .. but it is just ascii text, try escaping some characters
// and make it a quoted-string
if (preg_match('/^[\x00-\x08\x0B\x0C\x0E-\x7F]*$/D', $phraseStr)) {
$phraseStr = $this->escapeSpecials($phraseStr, ['"']);
$phraseStr = '"'.$phraseStr.'"';
} else {
// ... otherwise it needs encoding
// Determine space remaining on line if first line
if ($shorten) {
$usedLength = \strlen($header->getFieldName().': ');
} else {
$usedLength = 0;
}
$phraseStr = $this->encodeWords($header, $string, $usedLength);
}
}
return $phraseStr;
}
/**
* Escape special characters in a string (convert to quoted-pairs).
*
* @param string $token
* @param string[] $include additional chars to escape
*
* @return string
*/
private function escapeSpecials($token, $include = [])
{
foreach (array_merge(['\\'], $include) as $char) {
$token = str_replace($char, '\\'.$char, $token);
}
return $token;
}
/**
* Encode needed word tokens within a string of input.
*
* @param string $input
* @param string $usedLength optional
*
* @return string
*/
protected function encodeWords(Swift_Mime_Header $header, $input, $usedLength = -1)
{
$value = '';
$tokens = $this->getEncodableWordTokens($input);
foreach ($tokens as $token) {
// See RFC 2822, Sect 2.2 (really 2.2 ??)
if ($this->tokenNeedsEncoding($token)) {
// Don't encode starting WSP
$firstChar = substr($token, 0, 1);
switch ($firstChar) {
case ' ':
case "\t":
$value .= $firstChar;
$token = substr($token, 1);
}
if (-1 == $usedLength) {
$usedLength = \strlen($header->getFieldName().': ') + \strlen($value);
}
$value .= $this->getTokenAsEncodedWord($token, $usedLength);
$header->setMaxLineLength(76); // Forcefully override
} else {
$value .= $token;
}
}
return $value;
}
/**
* Test if a token needs to be encoded or not.
*
* @param string $token
*
* @return bool
*/
protected function tokenNeedsEncoding($token)
{
return preg_match('~[\x00-\x08\x10-\x19\x7F-\xFF\r\n]~', $token);
}
/**
* Splits a string into tokens in blocks of words which can be encoded quickly.
*
* @param string $string
*
* @return string[]
*/
protected function getEncodableWordTokens($string)
{
$tokens = [];
$encodedToken = '';
// Split at all whitespace boundaries
foreach (preg_split('~(?=[\t ])~', $string ?? '') as $token) {
if ($this->tokenNeedsEncoding($token)) {
$encodedToken .= $token;
} else {
if (\strlen($encodedToken) > 0) {
$tokens[] = $encodedToken;
$encodedToken = '';
}
$tokens[] = $token;
}
}
if (\strlen($encodedToken)) {
$tokens[] = $encodedToken;
}
return $tokens;
}
/**
* Get a token as an encoded word for safe insertion into headers.
*
* @param string $token token to encode
* @param int $firstLineOffset optional
*
* @return string
*/
protected function getTokenAsEncodedWord($token, $firstLineOffset = 0)
{
// Adjust $firstLineOffset to account for space needed for syntax
$charsetDecl = $this->charset;
if (isset($this->lang)) {
$charsetDecl .= '*'.$this->lang;
}
$encodingWrapperLength = \strlen(
'=?'.$charsetDecl.'?'.$this->encoder->getName().'??='
);
if ($firstLineOffset >= 75) {
//Does this logic need to be here?
$firstLineOffset = 0;
}
$encodedTextLines = explode("\r\n",
$this->encoder->encodeString(
$token, $firstLineOffset, 75 - $encodingWrapperLength, $this->charset
) ?? ''
);
if ('iso-2022-jp' !== strtolower($this->charset ?? '')) {
// special encoding for iso-2022-jp using mb_encode_mimeheader
foreach ($encodedTextLines as $lineNum => $line) {
$encodedTextLines[$lineNum] = '=?'.$charsetDecl.
'?'.$this->encoder->getName().
'?'.$line.'?=';
}
}
return implode("\r\n ", $encodedTextLines);
}
/**
* Generates tokens from the given string which include CRLF as individual tokens.
*
* @param string $token
*
* @return string[]
*/
protected function generateTokenLines($token)
{
return preg_split('~(\r\n)~', $token ?? '', -1, PREG_SPLIT_DELIM_CAPTURE);
}
/**
* Set a value into the cache.
*
* @param string $value
*/
protected function setCachedValue($value)
{
$this->cachedValue = $value;
}
/**
* Get the value in the cache.
*
* @return string
*/
protected function getCachedValue()
{
return $this->cachedValue;
}
/**
* Clear the cached value if $condition is met.
*
* @param bool $condition
*/
protected function clearCachedValueIf($condition)
{
if ($condition) {
$this->setCachedValue(null);
}
}
/**
* Generate a list of all tokens in the final header.
*
* @param string $string The string to tokenize
*
* @return array An array of tokens as strings
*/
protected function toTokens($string = null)
{
if (null === $string) {
$string = $this->getFieldBody();
}
$tokens = [];
// Generate atoms; split at all invisible boundaries followed by WSP
foreach (preg_split('~(?=[ \t])~', $string ?? '') as $token) {
$newTokens = $this->generateTokenLines($token);
foreach ($newTokens as $newToken) {
$tokens[] = $newToken;
}
}
return $tokens;
}
/**
* Takes an array of tokens which appear in the header and turns them into
* an RFC 2822 compliant string, adding FWSP where needed.
*
* @param string[] $tokens
*
* @return string
*/
private function tokensToString(array $tokens)
{
$lineCount = 0;
$headerLines = [];
$headerLines[] = $this->name.': ';
$currentLine = &$headerLines[$lineCount++];
// Build all tokens back into compliant header
foreach ($tokens as $i => $token) {
// Line longer than specified maximum or token was just a new line
if (("\r\n" == $token) ||
($i > 0 && \strlen($currentLine.$token) > $this->lineLength)
&& 0 < \strlen($currentLine)) {
$headerLines[] = '';
$currentLine = &$headerLines[$lineCount++];
}
// Append token to the line
if ("\r\n" != $token) {
$currentLine .= $token;
}
}
// Implode with FWS (RFC 2822, 2.2.3)
return implode("\r\n", $headerLines)."\r\n";
}
/**
* Make a deep copy of object.
*/
public function __clone()
{
if ($this->encoder) {
$this->encoder = clone $this->encoder;
}
}
}

View File

@@ -0,0 +1,113 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* A Date MIME Header for Swift Mailer.
*
* @author Chris Corbyn
*/
class Swift_Mime_Headers_DateHeader extends Swift_Mime_Headers_AbstractHeader
{
/**
* Date-time value of this Header.
*
* @var DateTimeImmutable
*/
private $dateTime;
/**
* Creates a new DateHeader with $name.
*
* @param string $name of Header
*/
public function __construct($name)
{
$this->setFieldName($name);
}
/**
* Get the type of Header that this instance represents.
*
* @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX
* @see TYPE_DATE, TYPE_ID, TYPE_PATH
*
* @return int
*/
public function getFieldType()
{
return self::TYPE_DATE;
}
/**
* Set the model for the field body.
*
* @param DateTimeInterface $model
*/
public function setFieldBodyModel($model)
{
$this->setDateTime($model);
}
/**
* Get the model for the field body.
*
* @return DateTimeImmutable
*/
public function getFieldBodyModel()
{
return $this->getDateTime();
}
/**
* Get the date-time representing the Date in this Header.
*
* @return DateTimeImmutable
*/
public function getDateTime()
{
return $this->dateTime;
}
/**
* Set the date-time of the Date in this Header.
*
* If a DateTime instance is provided, it is converted to DateTimeImmutable.
*/
public function setDateTime(DateTimeInterface $dateTime)
{
$this->clearCachedValueIf($this->getCachedValue() != $dateTime->format(DateTime::RFC2822));
if ($dateTime instanceof DateTime) {
$immutable = new DateTimeImmutable('@'.$dateTime->getTimestamp());
$dateTime = $immutable->setTimezone($dateTime->getTimezone());
}
$this->dateTime = $dateTime;
}
/**
* Get the string value of the body in this Header.
*
* This is not necessarily RFC 2822 compliant since folding white space will
* not be added at this stage (see {@link toString()} for that).
*
* @see toString()
*
* @return string
*/
public function getFieldBody()
{
if (!$this->getCachedValue()) {
if (isset($this->dateTime)) {
$this->setCachedValue($this->dateTime->format(DateTime::RFC2822));
}
}
return $this->getCachedValue();
}
}

View File

@@ -0,0 +1,189 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Egulias\EmailValidator\EmailValidator;
use Egulias\EmailValidator\Validation\MessageIDValidation;
use Egulias\EmailValidator\Validation\RFCValidation;
/**
* An ID MIME Header for something like Message-ID or Content-ID.
*
* @author Chris Corbyn
*/
class Swift_Mime_Headers_IdentificationHeader extends Swift_Mime_Headers_AbstractHeader
{
/**
* The IDs used in the value of this Header.
*
* This may hold multiple IDs or just a single ID.
*
* @var string[]
*/
private $ids = [];
/**
* The strict EmailValidator.
*
* @var EmailValidator
*/
private $emailValidator;
private $addressEncoder;
/**
* Creates a new IdentificationHeader with the given $name and $id.
*
* @param string $name
*/
public function __construct($name, EmailValidator $emailValidator, Swift_AddressEncoder $addressEncoder = null)
{
$this->setFieldName($name);
$this->emailValidator = $emailValidator;
$this->addressEncoder = $addressEncoder ?? new Swift_AddressEncoder_IdnAddressEncoder();
}
/**
* Get the type of Header that this instance represents.
*
* @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX
* @see TYPE_DATE, TYPE_ID, TYPE_PATH
*
* @return int
*/
public function getFieldType()
{
return self::TYPE_ID;
}
/**
* Set the model for the field body.
*
* This method takes a string ID, or an array of IDs.
*
* @param mixed $model
*
* @throws Swift_RfcComplianceException
*/
public function setFieldBodyModel($model)
{
$this->setId($model);
}
/**
* Get the model for the field body.
*
* This method returns an array of IDs
*
* @return array
*/
public function getFieldBodyModel()
{
return $this->getIds();
}
/**
* Set the ID used in the value of this header.
*
* @param string|array $id
*
* @throws Swift_RfcComplianceException
*/
public function setId($id)
{
$this->setIds(\is_array($id) ? $id : [$id]);
}
/**
* Get the ID used in the value of this Header.
*
* If multiple IDs are set only the first is returned.
*
* @return string
*/
public function getId()
{
if (\count($this->ids) > 0) {
return $this->ids[0];
}
}
/**
* Set a collection of IDs to use in the value of this Header.
*
* @param string[] $ids
*
* @throws Swift_RfcComplianceException
*/
public function setIds(array $ids)
{
$actualIds = [];
foreach ($ids as $id) {
$this->assertValidId($id);
$actualIds[] = $id;
}
$this->clearCachedValueIf($this->ids != $actualIds);
$this->ids = $actualIds;
}
/**
* Get the list of IDs used in this Header.
*
* @return string[]
*/
public function getIds()
{
return $this->ids;
}
/**
* Get the string value of the body in this Header.
*
* This is not necessarily RFC 2822 compliant since folding white space will
* not be added at this stage (see {@see toString()} for that).
*
* @see toString()
*
* @throws Swift_RfcComplianceException
*
* @return string
*/
public function getFieldBody()
{
if (!$this->getCachedValue()) {
$angleAddrs = [];
foreach ($this->ids as $id) {
$angleAddrs[] = '<'.$this->addressEncoder->encodeString($id).'>';
}
$this->setCachedValue(implode(' ', $angleAddrs));
}
return $this->getCachedValue();
}
/**
* Throws an Exception if the id passed does not comply with RFC 2822.
*
* @param string $id
*
* @throws Swift_RfcComplianceException
*/
private function assertValidId($id)
{
$emailValidation = class_exists(MessageIDValidation::class) ? new MessageIDValidation() : new RFCValidation();
if (!$this->emailValidator->isValid($id, $emailValidation)) {
throw new Swift_RfcComplianceException('Invalid ID given <'.$id.'>');
}
}
}

View File

@@ -0,0 +1,358 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Egulias\EmailValidator\EmailValidator;
use Egulias\EmailValidator\Validation\RFCValidation;
/**
* A Mailbox Address MIME Header for something like From or Sender.
*
* @author Chris Corbyn
*/
class Swift_Mime_Headers_MailboxHeader extends Swift_Mime_Headers_AbstractHeader
{
/**
* The mailboxes used in this Header.
*
* @var string[]
*/
private $mailboxes = [];
/**
* The strict EmailValidator.
*
* @var EmailValidator
*/
private $emailValidator;
private $addressEncoder;
/**
* Creates a new MailboxHeader with $name.
*
* @param string $name of Header
*/
public function __construct($name, Swift_Mime_HeaderEncoder $encoder, EmailValidator $emailValidator, Swift_AddressEncoder $addressEncoder = null)
{
$this->setFieldName($name);
$this->setEncoder($encoder);
$this->emailValidator = $emailValidator;
$this->addressEncoder = $addressEncoder ?? new Swift_AddressEncoder_IdnAddressEncoder();
}
/**
* Get the type of Header that this instance represents.
*
* @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX
* @see TYPE_DATE, TYPE_ID, TYPE_PATH
*
* @return int
*/
public function getFieldType()
{
return self::TYPE_MAILBOX;
}
/**
* Set the model for the field body.
*
* This method takes a string, or an array of addresses.
*
* @param mixed $model
*
* @throws Swift_RfcComplianceException
*/
public function setFieldBodyModel($model)
{
$this->setNameAddresses($model);
}
/**
* Get the model for the field body.
*
* This method returns an associative array like {@link getNameAddresses()}
*
* @throws Swift_RfcComplianceException
*
* @return array
*/
public function getFieldBodyModel()
{
return $this->getNameAddresses();
}
/**
* Set a list of mailboxes to be shown in this Header.
*
* The mailboxes can be a simple array of addresses, or an array of
* key=>value pairs where (email => personalName).
* Example:
* <code>
* <?php
* //Sets two mailboxes in the Header, one with a personal name
* $header->setNameAddresses(array(
* 'chris@swiftmailer.org' => 'Chris Corbyn',
* 'mark@swiftmailer.org' //No associated personal name
* ));
* ?>
* </code>
*
* @see __construct()
* @see setAddresses()
* @see setValue()
*
* @param string|string[] $mailboxes
*
* @throws Swift_RfcComplianceException
*/
public function setNameAddresses($mailboxes)
{
$this->mailboxes = $this->normalizeMailboxes((array) $mailboxes);
$this->setCachedValue(null); //Clear any cached value
}
/**
* Get the full mailbox list of this Header as an array of valid RFC 2822 strings.
*
* Example:
* <code>
* <?php
* $header = new Swift_Mime_Headers_MailboxHeader('From',
* array('chris@swiftmailer.org' => 'Chris Corbyn',
* 'mark@swiftmailer.org' => 'Mark Corbyn')
* );
* print_r($header->getNameAddressStrings());
* // array (
* // 0 => Chris Corbyn <chris@swiftmailer.org>,
* // 1 => Mark Corbyn <mark@swiftmailer.org>
* // )
* ?>
* </code>
*
* @see getNameAddresses()
* @see toString()
*
* @throws Swift_RfcComplianceException
*
* @return string[]
*/
public function getNameAddressStrings()
{
return $this->createNameAddressStrings($this->getNameAddresses());
}
/**
* Get all mailboxes in this Header as key=>value pairs.
*
* The key is the address and the value is the name (or null if none set).
* Example:
* <code>
* <?php
* $header = new Swift_Mime_Headers_MailboxHeader('From',
* array('chris@swiftmailer.org' => 'Chris Corbyn',
* 'mark@swiftmailer.org' => 'Mark Corbyn')
* );
* print_r($header->getNameAddresses());
* // array (
* // chris@swiftmailer.org => Chris Corbyn,
* // mark@swiftmailer.org => Mark Corbyn
* // )
* ?>
* </code>
*
* @see getAddresses()
* @see getNameAddressStrings()
*
* @return string[]
*/
public function getNameAddresses()
{
return $this->mailboxes;
}
/**
* Makes this Header represent a list of plain email addresses with no names.
*
* Example:
* <code>
* <?php
* //Sets three email addresses as the Header data
* $header->setAddresses(
* array('one@domain.tld', 'two@domain.tld', 'three@domain.tld')
* );
* ?>
* </code>
*
* @see setNameAddresses()
* @see setValue()
*
* @param string[] $addresses
*
* @throws Swift_RfcComplianceException
*/
public function setAddresses($addresses)
{
$this->setNameAddresses(array_values((array) $addresses));
}
/**
* Get all email addresses in this Header.
*
* @see getNameAddresses()
*
* @return string[]
*/
public function getAddresses()
{
return array_keys($this->mailboxes);
}
/**
* Remove one or more addresses from this Header.
*
* @param string|string[] $addresses
*/
public function removeAddresses($addresses)
{
$this->setCachedValue(null);
foreach ((array) $addresses as $address) {
unset($this->mailboxes[$address]);
}
}
/**
* Get the string value of the body in this Header.
*
* This is not necessarily RFC 2822 compliant since folding white space will
* not be added at this stage (see {@link toString()} for that).
*
* @see toString()
*
* @throws Swift_RfcComplianceException
*
* @return string
*/
public function getFieldBody()
{
// Compute the string value of the header only if needed
if (null === $this->getCachedValue()) {
$this->setCachedValue($this->createMailboxListString($this->mailboxes));
}
return $this->getCachedValue();
}
/**
* Normalizes a user-input list of mailboxes into consistent key=>value pairs.
*
* @param string[] $mailboxes
*
* @return string[]
*/
protected function normalizeMailboxes(array $mailboxes)
{
$actualMailboxes = [];
foreach ($mailboxes as $key => $value) {
if (\is_string($key)) {
//key is email addr
$address = $key;
$name = $value;
} else {
$address = $value;
$name = null;
}
$this->assertValidAddress($address);
$actualMailboxes[$address] = $name;
}
return $actualMailboxes;
}
/**
* Produces a compliant, formatted display-name based on the string given.
*
* @param string $displayName as displayed
* @param bool $shorten the first line to make remove for header name
*
* @return string
*/
protected function createDisplayNameString($displayName, $shorten = false)
{
return $this->createPhrase($this, $displayName, $this->getCharset(), $this->getEncoder(), $shorten);
}
/**
* Creates a string form of all the mailboxes in the passed array.
*
* @param string[] $mailboxes
*
* @throws Swift_RfcComplianceException
*
* @return string
*/
protected function createMailboxListString(array $mailboxes)
{
return implode(', ', $this->createNameAddressStrings($mailboxes));
}
/**
* Redefine the encoding requirements for mailboxes.
*
* All "specials" must be encoded as the full header value will not be quoted
*
* @see RFC 2822 3.2.1
*
* @param string $token
*
* @return bool
*/
protected function tokenNeedsEncoding($token)
{
return preg_match('/[()<>\[\]:;@\,."]/', $token) || parent::tokenNeedsEncoding($token);
}
/**
* Return an array of strings conforming the the name-addr spec of RFC 2822.
*
* @param string[] $mailboxes
*
* @return string[]
*/
private function createNameAddressStrings(array $mailboxes)
{
$strings = [];
foreach ($mailboxes as $email => $name) {
$mailboxStr = $this->addressEncoder->encodeString($email);
if (null !== $name) {
$nameStr = $this->createDisplayNameString($name, empty($strings));
$mailboxStr = $nameStr.' <'.$mailboxStr.'>';
}
$strings[] = $mailboxStr;
}
return $strings;
}
/**
* Throws an Exception if the address passed does not comply with RFC 2822.
*
* @param string $address
*
* @throws Swift_RfcComplianceException if invalid
*/
private function assertValidAddress($address)
{
if (!$this->emailValidator->isValid($address, new RFCValidation())) {
throw new Swift_RfcComplianceException('Address in mailbox given ['.$address.'] does not comply with RFC 2822, 3.6.2.');
}
}
}

View File

@@ -0,0 +1,135 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* An OpenDKIM Specific Header using only raw header datas without encoding.
*
* @author De Cock Xavier <xdecock@gmail.com>
*
* @deprecated since SwiftMailer 6.1.0; use Swift_Signers_DKIMSigner instead.
*/
class Swift_Mime_Headers_OpenDKIMHeader implements Swift_Mime_Header
{
/**
* The value of this Header.
*
* @var string
*/
private $value;
/**
* The name of this Header.
*
* @var string
*/
private $fieldName;
/**
* @param string $name
*/
public function __construct($name)
{
$this->fieldName = $name;
}
/**
* Get the type of Header that this instance represents.
*
* @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX
* @see TYPE_DATE, TYPE_ID, TYPE_PATH
*
* @return int
*/
public function getFieldType()
{
return self::TYPE_TEXT;
}
/**
* Set the model for the field body.
*
* This method takes a string for the field value.
*
* @param string $model
*/
public function setFieldBodyModel($model)
{
$this->setValue($model);
}
/**
* Get the model for the field body.
*
* This method returns a string.
*
* @return string
*/
public function getFieldBodyModel()
{
return $this->getValue();
}
/**
* Get the (unencoded) value of this header.
*
* @return string
*/
public function getValue()
{
return $this->value;
}
/**
* Set the (unencoded) value of this header.
*
* @param string $value
*/
public function setValue($value)
{
$this->value = $value;
}
/**
* Get the value of this header prepared for rendering.
*
* @return string
*/
public function getFieldBody()
{
return $this->value;
}
/**
* Get this Header rendered as a RFC 2822 compliant string.
*
* @return string
*/
public function toString()
{
return $this->fieldName.': '.$this->value."\r\n";
}
/**
* Set the Header FieldName.
*
* @see Swift_Mime_Header::getFieldName()
*/
public function getFieldName()
{
return $this->fieldName;
}
/**
* Ignored.
*/
public function setCharset($charset)
{
}
}

View File

@@ -0,0 +1,255 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* An abstract base MIME Header.
*
* @author Chris Corbyn
*/
class Swift_Mime_Headers_ParameterizedHeader extends Swift_Mime_Headers_UnstructuredHeader
{
/**
* RFC 2231's definition of a token.
*
* @var string
*/
const TOKEN_REGEX = '(?:[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]+)';
/**
* The Encoder used to encode the parameters.
*
* @var Swift_Encoder
*/
private $paramEncoder;
/**
* The parameters as an associative array.
*
* @var string[]
*/
private $params = [];
/**
* Creates a new ParameterizedHeader with $name.
*
* @param string $name
*/
public function __construct($name, Swift_Mime_HeaderEncoder $encoder, Swift_Encoder $paramEncoder = null)
{
parent::__construct($name, $encoder);
$this->paramEncoder = $paramEncoder;
}
/**
* Get the type of Header that this instance represents.
*
* @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX
* @see TYPE_DATE, TYPE_ID, TYPE_PATH
*
* @return int
*/
public function getFieldType()
{
return self::TYPE_PARAMETERIZED;
}
/**
* Set the character set used in this Header.
*
* @param string $charset
*/
public function setCharset($charset)
{
parent::setCharset($charset);
if (isset($this->paramEncoder)) {
$this->paramEncoder->charsetChanged($charset);
}
}
/**
* Set the value of $parameter.
*
* @param string $parameter
* @param string $value
*/
public function setParameter($parameter, $value)
{
$this->setParameters(array_merge($this->getParameters(), [$parameter => $value]));
}
/**
* Get the value of $parameter.
*
* @param string $parameter
*
* @return string
*/
public function getParameter($parameter)
{
$params = $this->getParameters();
return $params[$parameter] ?? null;
}
/**
* Set an associative array of parameter names mapped to values.
*
* @param string[] $parameters
*/
public function setParameters(array $parameters)
{
$this->clearCachedValueIf($this->params != $parameters);
$this->params = $parameters;
}
/**
* Returns an associative array of parameter names mapped to values.
*
* @return string[]
*/
public function getParameters()
{
return $this->params;
}
/**
* Get the value of this header prepared for rendering.
*
* @return string
*/
public function getFieldBody() //TODO: Check caching here
{
$body = parent::getFieldBody();
foreach ($this->params as $name => $value) {
if (null !== $value) {
// Add the parameter
$body .= '; '.$this->createParameter($name, $value);
}
}
return $body;
}
/**
* Generate a list of all tokens in the final header.
*
* This doesn't need to be overridden in theory, but it is for implementation
* reasons to prevent potential breakage of attributes.
*
* @param string $string The string to tokenize
*
* @return array An array of tokens as strings
*/
protected function toTokens($string = null)
{
$tokens = parent::toTokens(parent::getFieldBody());
// Try creating any parameters
foreach ($this->params as $name => $value) {
if (null !== $value) {
// Add the semi-colon separator
$tokens[\count($tokens) - 1] .= ';';
$tokens = array_merge($tokens, $this->generateTokenLines(
' '.$this->createParameter($name, $value)
));
}
}
return $tokens;
}
/**
* Render a RFC 2047 compliant header parameter from the $name and $value.
*
* @param string $name
* @param string $value
*
* @return string
*/
private function createParameter($name, $value)
{
$origValue = $value;
$encoded = false;
// Allow room for parameter name, indices, "=" and DQUOTEs
$maxValueLength = $this->getMaxLineLength() - \strlen($name.'=*N"";') - 1;
$firstLineOffset = 0;
// If it's not already a valid parameter value...
if (!preg_match('/^'.self::TOKEN_REGEX.'$/D', $value)) {
// TODO: text, or something else??
// ... and it's not ascii
if (!preg_match('/^[\x00-\x08\x0B\x0C\x0E-\x7F]*$/D', $value)) {
$encoded = true;
// Allow space for the indices, charset and language
$maxValueLength = $this->getMaxLineLength() - \strlen($name.'*N*="";') - 1;
$firstLineOffset = \strlen(
$this->getCharset()."'".$this->getLanguage()."'"
);
}
}
// Encode if we need to
if ($encoded || \strlen($value) > $maxValueLength) {
if (isset($this->paramEncoder)) {
$value = $this->paramEncoder->encodeString(
$origValue, $firstLineOffset, $maxValueLength, $this->getCharset()
);
} else {
// We have to go against RFC 2183/2231 in some areas for interoperability
$value = $this->getTokenAsEncodedWord($origValue);
$encoded = false;
}
}
$valueLines = isset($this->paramEncoder) ? explode("\r\n", $value) : [$value];
// Need to add indices
if (\count($valueLines) > 1) {
$paramLines = [];
foreach ($valueLines as $i => $line) {
$paramLines[] = $name.'*'.$i.
$this->getEndOfParameterValue($line, true, 0 == $i);
}
return implode(";\r\n ", $paramLines);
} else {
return $name.$this->getEndOfParameterValue(
$valueLines[0], $encoded, true
);
}
}
/**
* Returns the parameter value from the "=" and beyond.
*
* @param string $value to append
* @param bool $encoded
* @param bool $firstLine
*
* @return string
*/
private function getEndOfParameterValue($value, $encoded = false, $firstLine = false)
{
if (!preg_match('/^'.self::TOKEN_REGEX.'$/D', $value)) {
$value = '"'.$value.'"';
}
$prepend = '=';
if ($encoded) {
$prepend = '*=';
if ($firstLine) {
$prepend = '*='.$this->getCharset()."'".$this->getLanguage().
"'";
}
}
return $prepend.$value;
}
}

View File

@@ -0,0 +1,153 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Egulias\EmailValidator\EmailValidator;
use Egulias\EmailValidator\Validation\RFCValidation;
/**
* A Path Header in Swift Mailer, such a Return-Path.
*
* @author Chris Corbyn
*/
class Swift_Mime_Headers_PathHeader extends Swift_Mime_Headers_AbstractHeader
{
/**
* The address in this Header (if specified).
*
* @var string
*/
private $address;
/**
* The strict EmailValidator.
*
* @var EmailValidator
*/
private $emailValidator;
private $addressEncoder;
/**
* Creates a new PathHeader with the given $name.
*
* @param string $name
*/
public function __construct($name, EmailValidator $emailValidator, Swift_AddressEncoder $addressEncoder = null)
{
$this->setFieldName($name);
$this->emailValidator = $emailValidator;
$this->addressEncoder = $addressEncoder ?? new Swift_AddressEncoder_IdnAddressEncoder();
}
/**
* Get the type of Header that this instance represents.
*
* @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX
* @see TYPE_DATE, TYPE_ID, TYPE_PATH
*
* @return int
*/
public function getFieldType()
{
return self::TYPE_PATH;
}
/**
* Set the model for the field body.
* This method takes a string for an address.
*
* @param string $model
*
* @throws Swift_RfcComplianceException
*/
public function setFieldBodyModel($model)
{
$this->setAddress($model);
}
/**
* Get the model for the field body.
* This method returns a string email address.
*
* @return mixed
*/
public function getFieldBodyModel()
{
return $this->getAddress();
}
/**
* Set the Address which should appear in this Header.
*
* @param string $address
*
* @throws Swift_RfcComplianceException
*/
public function setAddress($address)
{
if (null === $address) {
$this->address = null;
} elseif ('' == $address) {
$this->address = '';
} else {
$this->assertValidAddress($address);
$this->address = $address;
}
$this->setCachedValue(null);
}
/**
* Get the address which is used in this Header (if any).
*
* Null is returned if no address is set.
*
* @return string
*/
public function getAddress()
{
return $this->address;
}
/**
* Get the string value of the body in this Header.
*
* This is not necessarily RFC 2822 compliant since folding white space will
* not be added at this stage (see {@link toString()} for that).
*
* @see toString()
*
* @return string
*/
public function getFieldBody()
{
if (!$this->getCachedValue()) {
if (isset($this->address)) {
$address = $this->addressEncoder->encodeString($this->address);
$this->setCachedValue('<'.$address.'>');
}
}
return $this->getCachedValue();
}
/**
* Throws an Exception if the address passed does not comply with RFC 2822.
*
* @param string $address
*
* @throws Swift_RfcComplianceException If address is invalid
*/
private function assertValidAddress($address)
{
if (!$this->emailValidator->isValid($address, new RFCValidation())) {
throw new Swift_RfcComplianceException('Address set in PathHeader does not comply with addr-spec of RFC 2822.');
}
}
}

View File

@@ -0,0 +1,109 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* A Simple MIME Header.
*
* @author Chris Corbyn
*/
class Swift_Mime_Headers_UnstructuredHeader extends Swift_Mime_Headers_AbstractHeader
{
/**
* The value of this Header.
*
* @var string
*/
private $value;
/**
* Creates a new SimpleHeader with $name.
*
* @param string $name
*/
public function __construct($name, Swift_Mime_HeaderEncoder $encoder)
{
$this->setFieldName($name);
$this->setEncoder($encoder);
}
/**
* Get the type of Header that this instance represents.
*
* @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX
* @see TYPE_DATE, TYPE_ID, TYPE_PATH
*
* @return int
*/
public function getFieldType()
{
return self::TYPE_TEXT;
}
/**
* Set the model for the field body.
*
* This method takes a string for the field value.
*
* @param string $model
*/
public function setFieldBodyModel($model)
{
$this->setValue($model);
}
/**
* Get the model for the field body.
*
* This method returns a string.
*
* @return string
*/
public function getFieldBodyModel()
{
return $this->getValue();
}
/**
* Get the (unencoded) value of this header.
*
* @return string
*/
public function getValue()
{
return $this->value;
}
/**
* Set the (unencoded) value of this header.
*
* @param string $value
*/
public function setValue($value)
{
$this->clearCachedValueIf($this->value != $value);
$this->value = $value;
}
/**
* Get the value of this header prepared for rendering.
*
* @return string
*/
public function getFieldBody()
{
if (!$this->getCachedValue()) {
$this->setCachedValue(
$this->encodeWords($this, $this->value)
);
}
return $this->getCachedValue();
}
}

View File

@@ -0,0 +1,54 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Message ID generator.
*/
class Swift_Mime_IdGenerator implements Swift_IdGenerator
{
private $idRight;
/**
* @param string $idRight
*/
public function __construct($idRight)
{
$this->idRight = $idRight;
}
/**
* Returns the right-hand side of the "@" used in all generated IDs.
*
* @return string
*/
public function getIdRight()
{
return $this->idRight;
}
/**
* Sets the right-hand side of the "@" to use in all generated IDs.
*
* @param string $idRight
*/
public function setIdRight($idRight)
{
$this->idRight = $idRight;
}
/**
* @return string
*/
public function generateId()
{
// 32 hex values for the left part
return bin2hex(random_bytes(16)).'@'.$this->idRight;
}
}

View File

@@ -0,0 +1,199 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* A MIME part, in a multipart message.
*
* @author Chris Corbyn
*/
class Swift_Mime_MimePart extends Swift_Mime_SimpleMimeEntity
{
/** The format parameter last specified by the user */
protected $userFormat;
/** The charset last specified by the user */
protected $userCharset;
/** The delsp parameter last specified by the user */
protected $userDelSp;
/** The nesting level of this MimePart */
private $nestingLevel = self::LEVEL_ALTERNATIVE;
/**
* Create a new MimePart with $headers, $encoder and $cache.
*
* @param string $charset
*/
public function __construct(Swift_Mime_SimpleHeaderSet $headers, Swift_Mime_ContentEncoder $encoder, Swift_KeyCache $cache, Swift_IdGenerator $idGenerator, $charset = null)
{
parent::__construct($headers, $encoder, $cache, $idGenerator);
$this->setContentType('text/plain');
if (null !== $charset) {
$this->setCharset($charset);
}
}
/**
* Set the body of this entity, either as a string, or as an instance of
* {@link Swift_OutputByteStream}.
*
* @param mixed $body
* @param string $contentType optional
* @param string $charset optional
*
* @return $this
*/
public function setBody($body, $contentType = null, $charset = null)
{
if (isset($charset)) {
$this->setCharset($charset);
}
$body = $this->convertString($body);
parent::setBody($body, $contentType);
return $this;
}
/**
* Get the character set of this entity.
*
* @return string
*/
public function getCharset()
{
return $this->getHeaderParameter('Content-Type', 'charset');
}
/**
* Set the character set of this entity.
*
* @param string $charset
*
* @return $this
*/
public function setCharset($charset)
{
$this->setHeaderParameter('Content-Type', 'charset', $charset);
if ($charset !== $this->userCharset) {
$this->clearCache();
}
$this->userCharset = $charset;
parent::charsetChanged($charset);
return $this;
}
/**
* Get the format of this entity (i.e. flowed or fixed).
*
* @return string
*/
public function getFormat()
{
return $this->getHeaderParameter('Content-Type', 'format');
}
/**
* Set the format of this entity (flowed or fixed).
*
* @param string $format
*
* @return $this
*/
public function setFormat($format)
{
$this->setHeaderParameter('Content-Type', 'format', $format);
$this->userFormat = $format;
return $this;
}
/**
* Test if delsp is being used for this entity.
*
* @return bool
*/
public function getDelSp()
{
return 'yes' === $this->getHeaderParameter('Content-Type', 'delsp');
}
/**
* Turn delsp on or off for this entity.
*
* @param bool $delsp
*
* @return $this
*/
public function setDelSp($delsp = true)
{
$this->setHeaderParameter('Content-Type', 'delsp', $delsp ? 'yes' : null);
$this->userDelSp = $delsp;
return $this;
}
/**
* Get the nesting level of this entity.
*
* @see LEVEL_TOP, LEVEL_ALTERNATIVE, LEVEL_MIXED, LEVEL_RELATED
*
* @return int
*/
public function getNestingLevel()
{
return $this->nestingLevel;
}
/**
* Receive notification that the charset has changed on this document, or a
* parent document.
*
* @param string $charset
*/
public function charsetChanged($charset)
{
$this->setCharset($charset);
}
/** Fix the content-type and encoding of this entity */
protected function fixHeaders()
{
parent::fixHeaders();
if (\count($this->getChildren())) {
$this->setHeaderParameter('Content-Type', 'charset', null);
$this->setHeaderParameter('Content-Type', 'format', null);
$this->setHeaderParameter('Content-Type', 'delsp', null);
} else {
$this->setCharset($this->userCharset);
$this->setFormat($this->userFormat);
$this->setDelSp($this->userDelSp);
}
}
/** Set the nesting level of this entity */
protected function setNestingLevel($level)
{
$this->nestingLevel = $level;
}
/** Encode charset when charset is not utf-8 */
protected function convertString($string)
{
$charset = strtolower($this->getCharset() ?? '');
if (!\in_array($charset, ['utf-8', 'iso-8859-1', 'iso-8859-15', ''])) {
return mb_convert_encoding($string, $charset, 'utf-8');
}
return $string;
}
}

View File

@@ -0,0 +1,195 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Egulias\EmailValidator\EmailValidator;
/**
* Creates MIME headers.
*
* @author Chris Corbyn
*/
class Swift_Mime_SimpleHeaderFactory implements Swift_Mime_CharsetObserver
{
/** The HeaderEncoder used by these headers */
private $encoder;
/** The Encoder used by parameters */
private $paramEncoder;
/** Strict EmailValidator */
private $emailValidator;
/** The charset of created Headers */
private $charset;
/** Swift_AddressEncoder */
private $addressEncoder;
/**
* Creates a new SimpleHeaderFactory using $encoder and $paramEncoder.
*
* @param string|null $charset
*/
public function __construct(Swift_Mime_HeaderEncoder $encoder, Swift_Encoder $paramEncoder, EmailValidator $emailValidator, $charset = null, Swift_AddressEncoder $addressEncoder = null)
{
$this->encoder = $encoder;
$this->paramEncoder = $paramEncoder;
$this->emailValidator = $emailValidator;
$this->charset = $charset;
$this->addressEncoder = $addressEncoder ?? new Swift_AddressEncoder_IdnAddressEncoder();
}
/**
* Create a new Mailbox Header with a list of $addresses.
*
* @param string $name
* @param array|string|null $addresses
*
* @return Swift_Mime_Header
*/
public function createMailboxHeader($name, $addresses = null)
{
$header = new Swift_Mime_Headers_MailboxHeader($name, $this->encoder, $this->emailValidator, $this->addressEncoder);
if (isset($addresses)) {
$header->setFieldBodyModel($addresses);
}
$this->setHeaderCharset($header);
return $header;
}
/**
* Create a new Date header using $dateTime.
*
* @param string $name
*
* @return Swift_Mime_Header
*/
public function createDateHeader($name, DateTimeInterface $dateTime = null)
{
$header = new Swift_Mime_Headers_DateHeader($name);
if (isset($dateTime)) {
$header->setFieldBodyModel($dateTime);
}
$this->setHeaderCharset($header);
return $header;
}
/**
* Create a new basic text header with $name and $value.
*
* @param string $name
* @param string $value
*
* @return Swift_Mime_Header
*/
public function createTextHeader($name, $value = null)
{
$header = new Swift_Mime_Headers_UnstructuredHeader($name, $this->encoder);
if (isset($value)) {
$header->setFieldBodyModel($value);
}
$this->setHeaderCharset($header);
return $header;
}
/**
* Create a new ParameterizedHeader with $name, $value and $params.
*
* @param string $name
* @param string $value
* @param array $params
*
* @return Swift_Mime_Headers_ParameterizedHeader
*/
public function createParameterizedHeader($name, $value = null, $params = [])
{
$header = new Swift_Mime_Headers_ParameterizedHeader($name, $this->encoder, ('content-disposition' == strtolower($name ?? '')) ? $this->paramEncoder : null);
if (isset($value)) {
$header->setFieldBodyModel($value);
}
foreach ($params as $k => $v) {
$header->setParameter($k, $v);
}
$this->setHeaderCharset($header);
return $header;
}
/**
* Create a new ID header for Message-ID or Content-ID.
*
* @param string $name
* @param string|array $ids
*
* @return Swift_Mime_Header
*/
public function createIdHeader($name, $ids = null)
{
$header = new Swift_Mime_Headers_IdentificationHeader($name, $this->emailValidator);
if (isset($ids)) {
$header->setFieldBodyModel($ids);
}
$this->setHeaderCharset($header);
return $header;
}
/**
* Create a new Path header with an address (path) in it.
*
* @param string $name
* @param string $path
*
* @return Swift_Mime_Header
*/
public function createPathHeader($name, $path = null)
{
$header = new Swift_Mime_Headers_PathHeader($name, $this->emailValidator);
if (isset($path)) {
$header->setFieldBodyModel($path);
}
$this->setHeaderCharset($header);
return $header;
}
/**
* Notify this observer that the entity's charset has changed.
*
* @param string $charset
*/
public function charsetChanged($charset)
{
$this->charset = $charset;
$this->encoder->charsetChanged($charset);
$this->paramEncoder->charsetChanged($charset);
}
/**
* Make a deep copy of object.
*/
public function __clone()
{
$this->encoder = clone $this->encoder;
$this->paramEncoder = clone $this->paramEncoder;
$this->addressEncoder = clone $this->addressEncoder;
}
/** Apply the charset to the Header */
private function setHeaderCharset(Swift_Mime_Header $header)
{
if (isset($this->charset)) {
$header->setCharset($this->charset);
}
}
}

View File

@@ -0,0 +1,399 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* A collection of MIME headers.
*
* @author Chris Corbyn
*/
class Swift_Mime_SimpleHeaderSet implements Swift_Mime_CharsetObserver
{
/** HeaderFactory */
private $factory;
/** Collection of set Headers */
private $headers = [];
/** Field ordering details */
private $order = [];
/** List of fields which are required to be displayed */
private $required = [];
/** The charset used by Headers */
private $charset;
/**
* Create a new SimpleHeaderSet with the given $factory.
*
* @param string $charset
*/
public function __construct(Swift_Mime_SimpleHeaderFactory $factory, $charset = null)
{
$this->factory = $factory;
if (isset($charset)) {
$this->setCharset($charset);
}
}
public function newInstance()
{
return new self($this->factory);
}
/**
* Set the charset used by these headers.
*
* @param string $charset
*/
public function setCharset($charset)
{
$this->charset = $charset;
$this->factory->charsetChanged($charset);
$this->notifyHeadersOfCharset($charset);
}
/**
* Add a new Mailbox Header with a list of $addresses.
*
* @param string $name
* @param array|string $addresses
*/
public function addMailboxHeader($name, $addresses = null)
{
$this->storeHeader($name, $this->factory->createMailboxHeader($name, $addresses));
}
/**
* Add a new Date header using $dateTime.
*
* @param string $name
*/
public function addDateHeader($name, DateTimeInterface $dateTime = null)
{
$this->storeHeader($name, $this->factory->createDateHeader($name, $dateTime));
}
/**
* Add a new basic text header with $name and $value.
*
* @param string $name
* @param string $value
*/
public function addTextHeader($name, $value = null)
{
$this->storeHeader($name, $this->factory->createTextHeader($name, $value));
}
/**
* Add a new ParameterizedHeader with $name, $value and $params.
*
* @param string $name
* @param string $value
* @param array $params
*/
public function addParameterizedHeader($name, $value = null, $params = [])
{
$this->storeHeader($name, $this->factory->createParameterizedHeader($name, $value, $params));
}
/**
* Add a new ID header for Message-ID or Content-ID.
*
* @param string $name
* @param string|array $ids
*/
public function addIdHeader($name, $ids = null)
{
$this->storeHeader($name, $this->factory->createIdHeader($name, $ids));
}
/**
* Add a new Path header with an address (path) in it.
*
* @param string $name
* @param string $path
*/
public function addPathHeader($name, $path = null)
{
$this->storeHeader($name, $this->factory->createPathHeader($name, $path));
}
/**
* Returns true if at least one header with the given $name exists.
*
* If multiple headers match, the actual one may be specified by $index.
*
* @param string $name
* @param int $index
*
* @return bool
*/
public function has($name, $index = 0)
{
$lowerName = strtolower($name ?? '');
if (!\array_key_exists($lowerName, $this->headers)) {
return false;
}
if (\func_num_args() < 2) {
// index was not specified, so we only need to check that there is at least one header value set
return (bool) \count($this->headers[$lowerName]);
}
return \array_key_exists($index, $this->headers[$lowerName]);
}
/**
* Set a header in the HeaderSet.
*
* The header may be a previously fetched header via {@link get()} or it may
* be one that has been created separately.
*
* If $index is specified, the header will be inserted into the set at this
* offset.
*
* @param int $index
*/
public function set(Swift_Mime_Header $header, $index = 0)
{
$this->storeHeader($header->getFieldName(), $header, $index);
}
/**
* Get the header with the given $name.
*
* If multiple headers match, the actual one may be specified by $index.
* Returns NULL if none present.
*
* @param string $name
* @param int $index
*
* @return Swift_Mime_Header|null
*/
public function get($name, $index = 0)
{
$name = strtolower($name ?? '');
if (\func_num_args() < 2) {
if ($this->has($name)) {
$values = array_values($this->headers[$name]);
return array_shift($values);
}
} else {
if ($this->has($name, $index)) {
return $this->headers[$name][$index];
}
}
}
/**
* Get all headers with the given $name.
*
* @param string $name
*
* @return array
*/
public function getAll($name = null)
{
if (!isset($name)) {
$headers = [];
foreach ($this->headers as $collection) {
$headers = array_merge($headers, $collection);
}
return $headers;
}
$lowerName = strtolower($name ?? '');
if (!\array_key_exists($lowerName, $this->headers)) {
return [];
}
return $this->headers[$lowerName];
}
/**
* Return the name of all Headers.
*
* @return array
*/
public function listAll()
{
$headers = $this->headers;
if ($this->canSort()) {
uksort($headers, [$this, 'sortHeaders']);
}
return array_keys($headers);
}
/**
* Remove the header with the given $name if it's set.
*
* If multiple headers match, the actual one may be specified by $index.
*
* @param string $name
* @param int $index
*/
public function remove($name, $index = 0)
{
$lowerName = strtolower($name ?? '');
unset($this->headers[$lowerName][$index]);
}
/**
* Remove all headers with the given $name.
*
* @param string $name
*/
public function removeAll($name)
{
$lowerName = strtolower($name ?? '');
unset($this->headers[$lowerName]);
}
/**
* Define a list of Header names as an array in the correct order.
*
* These Headers will be output in the given order where present.
*/
public function defineOrdering(array $sequence)
{
$this->order = array_flip(array_map('strtolower', $sequence));
}
/**
* Set a list of header names which must always be displayed when set.
*
* Usually headers without a field value won't be output unless set here.
*/
public function setAlwaysDisplayed(array $names)
{
$this->required = array_flip(array_map('strtolower', $names));
}
/**
* Notify this observer that the entity's charset has changed.
*
* @param string $charset
*/
public function charsetChanged($charset)
{
$this->setCharset($charset);
}
/**
* Returns a string with a representation of all headers.
*
* @return string
*/
public function toString()
{
$string = '';
$headers = $this->headers;
if ($this->canSort()) {
uksort($headers, [$this, 'sortHeaders']);
}
foreach ($headers as $collection) {
foreach ($collection as $header) {
if ($this->isDisplayed($header) || '' != $header->getFieldBody()) {
$string .= $header->toString();
}
}
}
return $string;
}
/**
* Returns a string representation of this object.
*
* @return string
*
* @see toString()
*/
public function __toString()
{
return $this->toString();
}
/** Save a Header to the internal collection */
private function storeHeader($name, Swift_Mime_Header $header, $offset = null)
{
if (!isset($this->headers[strtolower($name ?? '')])) {
$this->headers[strtolower($name ?? '')] = [];
}
if (!isset($offset)) {
$this->headers[strtolower($name ?? '')][] = $header;
} else {
$this->headers[strtolower($name ?? '')][$offset] = $header;
}
}
/** Test if the headers can be sorted */
private function canSort()
{
return \count($this->order) > 0;
}
/** uksort() algorithm for Header ordering */
private function sortHeaders($a, $b)
{
$lowerA = strtolower($a ?? '');
$lowerB = strtolower($b ?? '');
$aPos = \array_key_exists($lowerA, $this->order) ? $this->order[$lowerA] : -1;
$bPos = \array_key_exists($lowerB, $this->order) ? $this->order[$lowerB] : -1;
if (-1 === $aPos && -1 === $bPos) {
// just be sure to be determinist here
return $a > $b ? -1 : 1;
}
if (-1 == $aPos) {
return 1;
} elseif (-1 == $bPos) {
return -1;
}
return $aPos < $bPos ? -1 : 1;
}
/** Test if the given Header is always displayed */
private function isDisplayed(Swift_Mime_Header $header)
{
return \array_key_exists(strtolower($header->getFieldName() ?? ''), $this->required);
}
/** Notify all Headers of the new charset */
private function notifyHeadersOfCharset($charset)
{
foreach ($this->headers as $headerGroup) {
foreach ($headerGroup as $header) {
$header->setCharset($charset);
}
}
}
/**
* Make a deep copy of object.
*/
public function __clone()
{
$this->factory = clone $this->factory;
foreach ($this->headers as $groupKey => $headerGroup) {
foreach ($headerGroup as $key => $header) {
$this->headers[$groupKey][$key] = clone $header;
}
}
}
}

View File

@@ -0,0 +1,642 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* The default email message class.
*
* @author Chris Corbyn
*/
class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart
{
const PRIORITY_HIGHEST = 1;
const PRIORITY_HIGH = 2;
const PRIORITY_NORMAL = 3;
const PRIORITY_LOW = 4;
const PRIORITY_LOWEST = 5;
/**
* Create a new SimpleMessage with $headers, $encoder and $cache.
*
* @param string $charset
*/
public function __construct(Swift_Mime_SimpleHeaderSet $headers, Swift_Mime_ContentEncoder $encoder, Swift_KeyCache $cache, Swift_IdGenerator $idGenerator, $charset = null)
{
parent::__construct($headers, $encoder, $cache, $idGenerator, $charset);
$this->getHeaders()->defineOrdering([
'Return-Path',
'Received',
'DKIM-Signature',
'DomainKey-Signature',
'Sender',
'Message-ID',
'Date',
'Subject',
'From',
'Reply-To',
'To',
'Cc',
'Bcc',
'MIME-Version',
'Content-Type',
'Content-Transfer-Encoding',
]);
$this->getHeaders()->setAlwaysDisplayed(['Date', 'Message-ID', 'From']);
$this->getHeaders()->addTextHeader('MIME-Version', '1.0');
$this->setDate(new DateTimeImmutable());
$this->setId($this->getId());
$this->getHeaders()->addMailboxHeader('From');
}
/**
* Always returns {@link LEVEL_TOP} for a message instance.
*
* @return int
*/
public function getNestingLevel()
{
return self::LEVEL_TOP;
}
/**
* Set the subject of this message.
*
* @param string $subject
*
* @return $this
*/
public function setSubject($subject)
{
if (!$this->setHeaderFieldModel('Subject', $subject)) {
$this->getHeaders()->addTextHeader('Subject', $subject);
}
return $this;
}
/**
* Get the subject of this message.
*
* @return string
*/
public function getSubject()
{
return $this->getHeaderFieldModel('Subject');
}
/**
* Set the date at which this message was created.
*
* @return $this
*/
public function setDate(DateTimeInterface $dateTime)
{
if (!$this->setHeaderFieldModel('Date', $dateTime)) {
$this->getHeaders()->addDateHeader('Date', $dateTime);
}
return $this;
}
/**
* Get the date at which this message was created.
*
* @return DateTimeInterface
*/
public function getDate()
{
return $this->getHeaderFieldModel('Date');
}
/**
* Set the return-path (the bounce address) of this message.
*
* @param string $address
*
* @return $this
*/
public function setReturnPath($address)
{
if (!$this->setHeaderFieldModel('Return-Path', $address)) {
$this->getHeaders()->addPathHeader('Return-Path', $address);
}
return $this;
}
/**
* Get the return-path (bounce address) of this message.
*
* @return string
*/
public function getReturnPath()
{
return $this->getHeaderFieldModel('Return-Path');
}
/**
* Set the sender of this message.
*
* This does not override the From field, but it has a higher significance.
*
* @param string $address
* @param string $name optional
*
* @return $this
*/
public function setSender($address, $name = null)
{
if (!\is_array($address) && isset($name)) {
$address = [$address => $name];
}
if (!$this->setHeaderFieldModel('Sender', (array) $address)) {
$this->getHeaders()->addMailboxHeader('Sender', (array) $address);
}
return $this;
}
/**
* Get the sender of this message.
*
* @return string
*/
public function getSender()
{
return $this->getHeaderFieldModel('Sender');
}
/**
* Add a From: address to this message.
*
* If $name is passed this name will be associated with the address.
*
* @param string $address
* @param string $name optional
*
* @return $this
*/
public function addFrom($address, $name = null)
{
$current = $this->getFrom();
$current[$address] = $name;
return $this->setFrom($current);
}
/**
* Set the from address of this message.
*
* You may pass an array of addresses if this message is from multiple people.
*
* If $name is passed and the first parameter is a string, this name will be
* associated with the address.
*
* @param string|array $addresses
* @param string $name optional
*
* @return $this
*/
public function setFrom($addresses, $name = null)
{
if (!\is_array($addresses) && isset($name)) {
$addresses = [$addresses => $name];
}
if (!$this->setHeaderFieldModel('From', (array) $addresses)) {
$this->getHeaders()->addMailboxHeader('From', (array) $addresses);
}
return $this;
}
/**
* Get the from address of this message.
*
* @return mixed
*/
public function getFrom()
{
return $this->getHeaderFieldModel('From');
}
/**
* Add a Reply-To: address to this message.
*
* If $name is passed this name will be associated with the address.
*
* @param string $address
* @param string $name optional
*
* @return $this
*/
public function addReplyTo($address, $name = null)
{
$current = $this->getReplyTo();
$current[$address] = $name;
return $this->setReplyTo($current);
}
/**
* Set the reply-to address of this message.
*
* You may pass an array of addresses if replies will go to multiple people.
*
* If $name is passed and the first parameter is a string, this name will be
* associated with the address.
*
* @param mixed $addresses
* @param string $name optional
*
* @return $this
*/
public function setReplyTo($addresses, $name = null)
{
if (!\is_array($addresses) && isset($name)) {
$addresses = [$addresses => $name];
}
if (!$this->setHeaderFieldModel('Reply-To', (array) $addresses)) {
$this->getHeaders()->addMailboxHeader('Reply-To', (array) $addresses);
}
return $this;
}
/**
* Get the reply-to address of this message.
*
* @return string
*/
public function getReplyTo()
{
return $this->getHeaderFieldModel('Reply-To');
}
/**
* Add a To: address to this message.
*
* If $name is passed this name will be associated with the address.
*
* @param string $address
* @param string $name optional
*
* @return $this
*/
public function addTo($address, $name = null)
{
$current = $this->getTo();
$current[$address] = $name;
return $this->setTo($current);
}
/**
* Set the to addresses of this message.
*
* If multiple recipients will receive the message an array should be used.
* Example: array('receiver@domain.org', 'other@domain.org' => 'A name')
*
* If $name is passed and the first parameter is a string, this name will be
* associated with the address.
*
* @param mixed $addresses
* @param string $name optional
*
* @return $this
*/
public function setTo($addresses, $name = null)
{
if (!\is_array($addresses) && isset($name)) {
$addresses = [$addresses => $name];
}
if (!$this->setHeaderFieldModel('To', (array) $addresses)) {
$this->getHeaders()->addMailboxHeader('To', (array) $addresses);
}
return $this;
}
/**
* Get the To addresses of this message.
*
* @return array
*/
public function getTo()
{
return $this->getHeaderFieldModel('To');
}
/**
* Add a Cc: address to this message.
*
* If $name is passed this name will be associated with the address.
*
* @param string $address
* @param string $name optional
*
* @return $this
*/
public function addCc($address, $name = null)
{
$current = $this->getCc();
$current[$address] = $name;
return $this->setCc($current);
}
/**
* Set the Cc addresses of this message.
*
* If $name is passed and the first parameter is a string, this name will be
* associated with the address.
*
* @param mixed $addresses
* @param string $name optional
*
* @return $this
*/
public function setCc($addresses, $name = null)
{
if (!\is_array($addresses) && isset($name)) {
$addresses = [$addresses => $name];
}
if (!$this->setHeaderFieldModel('Cc', (array) $addresses)) {
$this->getHeaders()->addMailboxHeader('Cc', (array) $addresses);
}
return $this;
}
/**
* Get the Cc address of this message.
*
* @return array
*/
public function getCc()
{
return $this->getHeaderFieldModel('Cc');
}
/**
* Add a Bcc: address to this message.
*
* If $name is passed this name will be associated with the address.
*
* @param string $address
* @param string $name optional
*
* @return $this
*/
public function addBcc($address, $name = null)
{
$current = $this->getBcc();
$current[$address] = $name;
return $this->setBcc($current);
}
/**
* Set the Bcc addresses of this message.
*
* If $name is passed and the first parameter is a string, this name will be
* associated with the address.
*
* @param mixed $addresses
* @param string $name optional
*
* @return $this
*/
public function setBcc($addresses, $name = null)
{
if (!\is_array($addresses) && isset($name)) {
$addresses = [$addresses => $name];
}
if (!$this->setHeaderFieldModel('Bcc', (array) $addresses)) {
$this->getHeaders()->addMailboxHeader('Bcc', (array) $addresses);
}
return $this;
}
/**
* Get the Bcc addresses of this message.
*
* @return array
*/
public function getBcc()
{
return $this->getHeaderFieldModel('Bcc');
}
/**
* Set the priority of this message.
*
* The value is an integer where 1 is the highest priority and 5 is the lowest.
*
* @param int $priority
*
* @return $this
*/
public function setPriority($priority)
{
$priorityMap = [
self::PRIORITY_HIGHEST => 'Highest',
self::PRIORITY_HIGH => 'High',
self::PRIORITY_NORMAL => 'Normal',
self::PRIORITY_LOW => 'Low',
self::PRIORITY_LOWEST => 'Lowest',
];
$pMapKeys = array_keys($priorityMap);
if ($priority > max($pMapKeys)) {
$priority = max($pMapKeys);
} elseif ($priority < min($pMapKeys)) {
$priority = min($pMapKeys);
}
if (!$this->setHeaderFieldModel('X-Priority',
sprintf('%d (%s)', $priority, $priorityMap[$priority]))) {
$this->getHeaders()->addTextHeader('X-Priority',
sprintf('%d (%s)', $priority, $priorityMap[$priority]));
}
return $this;
}
/**
* Get the priority of this message.
*
* The returned value is an integer where 1 is the highest priority and 5
* is the lowest.
*
* @return int
*/
public function getPriority()
{
list($priority) = sscanf($this->getHeaderFieldModel('X-Priority'),
'%[1-5]'
);
return $priority ?? 3;
}
/**
* Ask for a delivery receipt from the recipient to be sent to $addresses.
*
* @param array $addresses
*
* @return $this
*/
public function setReadReceiptTo($addresses)
{
if (!$this->setHeaderFieldModel('Disposition-Notification-To', $addresses)) {
$this->getHeaders()
->addMailboxHeader('Disposition-Notification-To', $addresses);
}
return $this;
}
/**
* Get the addresses to which a read-receipt will be sent.
*
* @return string
*/
public function getReadReceiptTo()
{
return $this->getHeaderFieldModel('Disposition-Notification-To');
}
/**
* Attach a {@link Swift_Mime_SimpleMimeEntity} such as an Attachment or MimePart.
*
* @return $this
*/
public function attach(Swift_Mime_SimpleMimeEntity $entity)
{
$this->setChildren(array_merge($this->getChildren(), [$entity]));
return $this;
}
/**
* Remove an already attached entity.
*
* @return $this
*/
public function detach(Swift_Mime_SimpleMimeEntity $entity)
{
$newChildren = [];
foreach ($this->getChildren() as $child) {
if ($entity !== $child) {
$newChildren[] = $child;
}
}
$this->setChildren($newChildren);
return $this;
}
/**
* Attach a {@link Swift_Mime_SimpleMimeEntity} and return it's CID source.
*
* This method should be used when embedding images or other data in a message.
*
* @return string
*/
public function embed(Swift_Mime_SimpleMimeEntity $entity)
{
$this->attach($entity);
return 'cid:'.$entity->getId();
}
/**
* Get this message as a complete string.
*
* @return string
*/
public function toString()
{
if (\count($children = $this->getChildren()) > 0 && '' != $this->getBody()) {
$this->setChildren(array_merge([$this->becomeMimePart()], $children));
$string = parent::toString();
$this->setChildren($children);
} else {
$string = parent::toString();
}
return $string;
}
/**
* Returns a string representation of this object.
*
* @see toString()
*
* @return string
*/
public function __toString()
{
return $this->toString();
}
/**
* Write this message to a {@link Swift_InputByteStream}.
*/
public function toByteStream(Swift_InputByteStream $is)
{
if (\count($children = $this->getChildren()) > 0 && '' != $this->getBody()) {
$this->setChildren(array_merge([$this->becomeMimePart()], $children));
parent::toByteStream($is);
$this->setChildren($children);
} else {
parent::toByteStream($is);
}
}
/** @see Swift_Mime_SimpleMimeEntity::getIdField() */
protected function getIdField()
{
return 'Message-ID';
}
/** Turn the body of this message into a child of itself if needed */
protected function becomeMimePart()
{
$part = new parent($this->getHeaders()->newInstance(), $this->getEncoder(),
$this->getCache(), $this->getIdGenerator(), $this->userCharset
);
$part->setContentType($this->userContentType);
$part->setBody($this->getBody());
$part->setFormat($this->userFormat);
$part->setDelSp($this->userDelSp);
$part->setNestingLevel($this->getTopNestingLevel());
return $part;
}
/** Get the highest nesting level nested inside this message */
private function getTopNestingLevel()
{
$highestLevel = $this->getNestingLevel();
foreach ($this->getChildren() as $child) {
$childLevel = $child->getNestingLevel();
if ($highestLevel < $childLevel) {
$highestLevel = $childLevel;
}
}
return $highestLevel;
}
}

View File

@@ -0,0 +1,826 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* A MIME entity, in a multipart message.
*
* @author Chris Corbyn
*/
class Swift_Mime_SimpleMimeEntity implements Swift_Mime_CharsetObserver, Swift_Mime_EncodingObserver
{
/** Main message document; there can only be one of these */
const LEVEL_TOP = 16;
/** An entity which nests with the same precedence as an attachment */
const LEVEL_MIXED = 256;
/** An entity which nests with the same precedence as a mime part */
const LEVEL_ALTERNATIVE = 4096;
/** An entity which nests with the same precedence as embedded content */
const LEVEL_RELATED = 65536;
/** A collection of Headers for this mime entity */
private $headers;
/** The body as a string, or a stream */
private $body;
/** The encoder that encodes the body into a streamable format */
private $encoder;
/** Message ID generator */
private $idGenerator;
/** A mime boundary, if any is used */
private $boundary;
/** Mime types to be used based on the nesting level */
private $compositeRanges = [
'multipart/mixed' => [self::LEVEL_TOP, self::LEVEL_MIXED],
'multipart/alternative' => [self::LEVEL_MIXED, self::LEVEL_ALTERNATIVE],
'multipart/related' => [self::LEVEL_ALTERNATIVE, self::LEVEL_RELATED],
];
/** A set of filter rules to define what level an entity should be nested at */
private $compoundLevelFilters = [];
/** The nesting level of this entity */
private $nestingLevel = self::LEVEL_ALTERNATIVE;
/** A KeyCache instance used during encoding and streaming */
private $cache;
/** Direct descendants of this entity */
private $immediateChildren = [];
/** All descendants of this entity */
private $children = [];
/** The maximum line length of the body of this entity */
private $maxLineLength = 78;
/** The order in which alternative mime types should appear */
private $alternativePartOrder = [
'text/plain' => 1,
'text/html' => 2,
'multipart/related' => 3,
];
/** The CID of this entity */
private $id;
/** The key used for accessing the cache */
private $cacheKey;
protected $userContentType;
/**
* Create a new SimpleMimeEntity with $headers, $encoder and $cache.
*/
public function __construct(Swift_Mime_SimpleHeaderSet $headers, Swift_Mime_ContentEncoder $encoder, Swift_KeyCache $cache, Swift_IdGenerator $idGenerator)
{
$this->cacheKey = bin2hex(random_bytes(16)); // set 32 hex values
$this->cache = $cache;
$this->headers = $headers;
$this->idGenerator = $idGenerator;
$this->setEncoder($encoder);
$this->headers->defineOrdering(['Content-Type', 'Content-Transfer-Encoding']);
// This array specifies that, when the entire MIME document contains
// $compoundLevel, then for each child within $level, if its Content-Type
// is $contentType then it should be treated as if it's level is
// $neededLevel instead. I tried to write that unambiguously! :-\
// Data Structure:
// array (
// $compoundLevel => array(
// $level => array(
// $contentType => $neededLevel
// )
// )
// )
$this->compoundLevelFilters = [
(self::LEVEL_ALTERNATIVE + self::LEVEL_RELATED) => [
self::LEVEL_ALTERNATIVE => [
'text/plain' => self::LEVEL_ALTERNATIVE,
'text/html' => self::LEVEL_RELATED,
],
],
];
$this->id = $this->idGenerator->generateId();
}
/**
* Generate a new Content-ID or Message-ID for this MIME entity.
*
* @return string
*/
public function generateId()
{
$this->setId($this->idGenerator->generateId());
return $this->id;
}
/**
* Get the {@link Swift_Mime_SimpleHeaderSet} for this entity.
*
* @return Swift_Mime_SimpleHeaderSet
*/
public function getHeaders()
{
return $this->headers;
}
/**
* Get the nesting level of this entity.
*
* @see LEVEL_TOP, LEVEL_MIXED, LEVEL_RELATED, LEVEL_ALTERNATIVE
*
* @return int
*/
public function getNestingLevel()
{
return $this->nestingLevel;
}
/**
* Get the Content-type of this entity.
*
* @return string
*/
public function getContentType()
{
return $this->getHeaderFieldModel('Content-Type');
}
/**
* Get the Body Content-type of this entity.
*
* @return string
*/
public function getBodyContentType()
{
return $this->userContentType;
}
/**
* Set the Content-type of this entity.
*
* @param string $type
*
* @return $this
*/
public function setContentType($type)
{
$this->setContentTypeInHeaders($type);
// Keep track of the value so that if the content-type changes automatically
// due to added child entities, it can be restored if they are later removed
$this->userContentType = $type;
return $this;
}
/**
* Get the CID of this entity.
*
* The CID will only be present in headers if a Content-ID header is present.
*
* @return string
*/
public function getId()
{
$tmp = (array) $this->getHeaderFieldModel($this->getIdField());
return $this->headers->has($this->getIdField()) ? current($tmp) : $this->id;
}
/**
* Set the CID of this entity.
*
* @param string $id
*
* @return $this
*/
public function setId($id)
{
if (!$this->setHeaderFieldModel($this->getIdField(), $id)) {
$this->headers->addIdHeader($this->getIdField(), $id);
}
$this->id = $id;
return $this;
}
/**
* Get the description of this entity.
*
* This value comes from the Content-Description header if set.
*
* @return string
*/
public function getDescription()
{
return $this->getHeaderFieldModel('Content-Description');
}
/**
* Set the description of this entity.
*
* This method sets a value in the Content-ID header.
*
* @param string $description
*
* @return $this
*/
public function setDescription($description)
{
if (!$this->setHeaderFieldModel('Content-Description', $description)) {
$this->headers->addTextHeader('Content-Description', $description);
}
return $this;
}
/**
* Get the maximum line length of the body of this entity.
*
* @return int
*/
public function getMaxLineLength()
{
return $this->maxLineLength;
}
/**
* Set the maximum line length of lines in this body.
*
* Though not enforced by the library, lines should not exceed 1000 chars.
*
* @param int $length
*
* @return $this
*/
public function setMaxLineLength($length)
{
$this->maxLineLength = $length;
return $this;
}
/**
* Get all children added to this entity.
*
* @return Swift_Mime_SimpleMimeEntity[]
*/
public function getChildren()
{
return $this->children;
}
/**
* Set all children of this entity.
*
* @param Swift_Mime_SimpleMimeEntity[] $children
* @param int $compoundLevel For internal use only
*
* @return $this
*/
public function setChildren(array $children, $compoundLevel = null)
{
// TODO: Try to refactor this logic
$compoundLevel = $compoundLevel ?? $this->getCompoundLevel($children);
$immediateChildren = [];
$grandchildren = [];
$newContentType = $this->userContentType;
foreach ($children as $child) {
$level = $this->getNeededChildLevel($child, $compoundLevel);
if (empty($immediateChildren)) {
//first iteration
$immediateChildren = [$child];
} else {
$nextLevel = $this->getNeededChildLevel($immediateChildren[0], $compoundLevel);
if ($nextLevel == $level) {
$immediateChildren[] = $child;
} elseif ($level < $nextLevel) {
// Re-assign immediateChildren to grandchildren
$grandchildren = array_merge($grandchildren, $immediateChildren);
// Set new children
$immediateChildren = [$child];
} else {
$grandchildren[] = $child;
}
}
}
if ($immediateChildren) {
$lowestLevel = $this->getNeededChildLevel($immediateChildren[0], $compoundLevel);
// Determine which composite media type is needed to accommodate the
// immediate children
foreach ($this->compositeRanges as $mediaType => $range) {
if ($lowestLevel > $range[0] && $lowestLevel <= $range[1]) {
$newContentType = $mediaType;
break;
}
}
// Put any grandchildren in a subpart
if (!empty($grandchildren)) {
$subentity = $this->createChild();
$subentity->setNestingLevel($lowestLevel);
$subentity->setChildren($grandchildren, $compoundLevel);
array_unshift($immediateChildren, $subentity);
}
}
$this->immediateChildren = $immediateChildren;
$this->children = $children;
$this->setContentTypeInHeaders($newContentType);
$this->fixHeaders();
$this->sortChildren();
return $this;
}
/**
* Get the body of this entity as a string.
*
* @return string
*/
public function getBody()
{
return $this->body instanceof Swift_OutputByteStream ? $this->readStream($this->body) : $this->body;
}
/**
* Set the body of this entity, either as a string, or as an instance of
* {@link Swift_OutputByteStream}.
*
* @param mixed $body
* @param string $contentType optional
*
* @return $this
*/
public function setBody($body, $contentType = null)
{
if ($body !== $this->body) {
$this->clearCache();
}
$this->body = $body;
if (null !== $contentType) {
$this->setContentType($contentType);
}
return $this;
}
/**
* Get the encoder used for the body of this entity.
*
* @return Swift_Mime_ContentEncoder
*/
public function getEncoder()
{
return $this->encoder;
}
/**
* Set the encoder used for the body of this entity.
*
* @return $this
*/
public function setEncoder(Swift_Mime_ContentEncoder $encoder)
{
if ($encoder !== $this->encoder) {
$this->clearCache();
}
$this->encoder = $encoder;
$this->setEncoding($encoder->getName());
$this->notifyEncoderChanged($encoder);
return $this;
}
/**
* Get the boundary used to separate children in this entity.
*
* @return string
*/
public function getBoundary()
{
if (!isset($this->boundary)) {
$this->boundary = '_=_swift_'.time().'_'.bin2hex(random_bytes(16)).'_=_';
}
return $this->boundary;
}
/**
* Set the boundary used to separate children in this entity.
*
* @param string $boundary
*
* @throws Swift_RfcComplianceException
*
* @return $this
*/
public function setBoundary($boundary)
{
$this->assertValidBoundary($boundary);
$this->boundary = $boundary;
return $this;
}
/**
* Receive notification that the charset of this entity, or a parent entity
* has changed.
*
* @param string $charset
*/
public function charsetChanged($charset)
{
$this->notifyCharsetChanged($charset);
}
/**
* Receive notification that the encoder of this entity or a parent entity
* has changed.
*/
public function encoderChanged(Swift_Mime_ContentEncoder $encoder)
{
$this->notifyEncoderChanged($encoder);
}
/**
* Get this entire entity as a string.
*
* @return string
*/
public function toString()
{
$string = $this->headers->toString();
$string .= $this->bodyToString();
return $string;
}
/**
* Get this entire entity as a string.
*
* @return string
*/
protected function bodyToString()
{
$string = '';
if (isset($this->body) && empty($this->immediateChildren)) {
if ($this->cache->hasKey($this->cacheKey, 'body')) {
$body = $this->cache->getString($this->cacheKey, 'body');
} else {
$body = "\r\n".$this->encoder->encodeString($this->getBody(), 0, $this->getMaxLineLength());
$this->cache->setString($this->cacheKey, 'body', $body, Swift_KeyCache::MODE_WRITE);
}
$string .= $body;
}
if (!empty($this->immediateChildren)) {
foreach ($this->immediateChildren as $child) {
$string .= "\r\n\r\n--".$this->getBoundary()."\r\n";
$string .= $child->toString();
}
$string .= "\r\n\r\n--".$this->getBoundary()."--\r\n";
}
return $string;
}
/**
* Returns a string representation of this object.
*
* @see toString()
*
* @return string
*/
public function __toString()
{
return $this->toString();
}
/**
* Write this entire entity to a {@see Swift_InputByteStream}.
*/
public function toByteStream(Swift_InputByteStream $is)
{
$is->write($this->headers->toString());
$is->commit();
$this->bodyToByteStream($is);
}
/**
* Write this entire entity to a {@link Swift_InputByteStream}.
*/
protected function bodyToByteStream(Swift_InputByteStream $is)
{
if (empty($this->immediateChildren)) {
if (isset($this->body)) {
if ($this->cache->hasKey($this->cacheKey, 'body')) {
$this->cache->exportToByteStream($this->cacheKey, 'body', $is);
} else {
$cacheIs = $this->cache->getInputByteStream($this->cacheKey, 'body');
if ($cacheIs) {
$is->bind($cacheIs);
}
$is->write("\r\n");
if ($this->body instanceof Swift_OutputByteStream) {
$this->body->setReadPointer(0);
$this->encoder->encodeByteStream($this->body, $is, 0, $this->getMaxLineLength());
} else {
$is->write($this->encoder->encodeString($this->getBody(), 0, $this->getMaxLineLength()));
}
if ($cacheIs) {
$is->unbind($cacheIs);
}
}
}
}
if (!empty($this->immediateChildren)) {
foreach ($this->immediateChildren as $child) {
$is->write("\r\n\r\n--".$this->getBoundary()."\r\n");
$child->toByteStream($is);
}
$is->write("\r\n\r\n--".$this->getBoundary()."--\r\n");
}
}
/**
* Get the name of the header that provides the ID of this entity.
*/
protected function getIdField()
{
return 'Content-ID';
}
/**
* Get the model data (usually an array or a string) for $field.
*/
protected function getHeaderFieldModel($field)
{
if ($this->headers->has($field)) {
return $this->headers->get($field)->getFieldBodyModel();
}
}
/**
* Set the model data for $field.
*/
protected function setHeaderFieldModel($field, $model)
{
if ($this->headers->has($field)) {
$this->headers->get($field)->setFieldBodyModel($model);
return true;
}
return false;
}
/**
* Get the parameter value of $parameter on $field header.
*/
protected function getHeaderParameter($field, $parameter)
{
if ($this->headers->has($field)) {
return $this->headers->get($field)->getParameter($parameter);
}
}
/**
* Set the parameter value of $parameter on $field header.
*/
protected function setHeaderParameter($field, $parameter, $value)
{
if ($this->headers->has($field)) {
$this->headers->get($field)->setParameter($parameter, $value);
return true;
}
return false;
}
/**
* Re-evaluate what content type and encoding should be used on this entity.
*/
protected function fixHeaders()
{
if (\count($this->immediateChildren)) {
$this->setHeaderParameter('Content-Type', 'boundary',
$this->getBoundary()
);
$this->headers->remove('Content-Transfer-Encoding');
} else {
$this->setHeaderParameter('Content-Type', 'boundary', null);
$this->setEncoding($this->encoder->getName());
}
}
/**
* Get the KeyCache used in this entity.
*
* @return Swift_KeyCache
*/
protected function getCache()
{
return $this->cache;
}
/**
* Get the ID generator.
*
* @return Swift_IdGenerator
*/
protected function getIdGenerator()
{
return $this->idGenerator;
}
/**
* Empty the KeyCache for this entity.
*/
protected function clearCache()
{
$this->cache->clearKey($this->cacheKey, 'body');
}
private function readStream(Swift_OutputByteStream $os)
{
$string = '';
while (false !== $bytes = $os->read(8192)) {
$string .= $bytes;
}
$os->setReadPointer(0);
return $string;
}
private function setEncoding($encoding)
{
if (!$this->setHeaderFieldModel('Content-Transfer-Encoding', $encoding)) {
$this->headers->addTextHeader('Content-Transfer-Encoding', $encoding);
}
}
private function assertValidBoundary($boundary)
{
if (!preg_match('/^[a-z0-9\'\(\)\+_\-,\.\/:=\?\ ]{0,69}[a-z0-9\'\(\)\+_\-,\.\/:=\?]$/Di', $boundary)) {
throw new Swift_RfcComplianceException('Mime boundary set is not RFC 2046 compliant.');
}
}
private function setContentTypeInHeaders($type)
{
if (!$this->setHeaderFieldModel('Content-Type', $type)) {
$this->headers->addParameterizedHeader('Content-Type', $type);
}
}
private function setNestingLevel($level)
{
$this->nestingLevel = $level;
}
private function getCompoundLevel($children)
{
$level = 0;
foreach ($children as $child) {
$level |= $child->getNestingLevel();
}
return $level;
}
private function getNeededChildLevel($child, $compoundLevel)
{
$filter = [];
foreach ($this->compoundLevelFilters as $bitmask => $rules) {
if (($compoundLevel & $bitmask) === $bitmask) {
$filter = $rules + $filter;
}
}
$realLevel = $child->getNestingLevel();
$lowercaseType = strtolower($child->getContentType() ?? '');
if (isset($filter[$realLevel]) && isset($filter[$realLevel][$lowercaseType])) {
return $filter[$realLevel][$lowercaseType];
}
return $realLevel;
}
private function createChild()
{
return new self($this->headers->newInstance(), $this->encoder, $this->cache, $this->idGenerator);
}
private function notifyEncoderChanged(Swift_Mime_ContentEncoder $encoder)
{
foreach ($this->immediateChildren as $child) {
$child->encoderChanged($encoder);
}
}
private function notifyCharsetChanged($charset)
{
$this->encoder->charsetChanged($charset);
$this->headers->charsetChanged($charset);
foreach ($this->immediateChildren as $child) {
$child->charsetChanged($charset);
}
}
private function sortChildren()
{
$shouldSort = false;
foreach ($this->immediateChildren as $child) {
// NOTE: This include alternative parts moved into a related part
if (self::LEVEL_ALTERNATIVE == $child->getNestingLevel()) {
$shouldSort = true;
break;
}
}
// Sort in order of preference, if there is one
if ($shouldSort) {
// Group the messages by order of preference
$sorted = [];
foreach ($this->immediateChildren as $child) {
$type = $child->getContentType();
$level = \array_key_exists($type, $this->alternativePartOrder) ? $this->alternativePartOrder[$type] : max($this->alternativePartOrder) + 1;
if (empty($sorted[$level])) {
$sorted[$level] = [];
}
$sorted[$level][] = $child;
}
ksort($sorted);
$this->immediateChildren = array_reduce($sorted, 'array_merge', []);
}
}
/**
* Empties it's own contents from the cache.
*/
public function __destruct()
{
if ($this->cache instanceof Swift_KeyCache) {
$this->cache->clearAll($this->cacheKey);
}
}
/**
* Make a deep copy of object.
*/
public function __clone()
{
$this->headers = clone $this->headers;
$this->encoder = clone $this->encoder;
$this->cacheKey = bin2hex(random_bytes(16)); // set 32 hex values
$children = [];
foreach ($this->children as $pos => $child) {
$children[$pos] = clone $child;
}
$this->setChildren($children);
}
public function __wakeup()
{
$this->cacheKey = bin2hex(random_bytes(16)); // set 32 hex values
$this->cache = new Swift_KeyCache_ArrayKeyCache(new Swift_KeyCache_SimpleKeyCacheInputStream());
}
}

View File

@@ -0,0 +1,45 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* A MIME part, in a multipart message.
*
* @author Chris Corbyn
*/
class Swift_MimePart extends Swift_Mime_MimePart
{
/**
* Create a new MimePart.
*
* Details may be optionally passed into the constructor.
*
* @param string $body
* @param string $contentType
* @param string $charset
*/
public function __construct($body = null, $contentType = null, $charset = null)
{
\call_user_func_array(
[$this, 'Swift_Mime_MimePart::__construct'],
Swift_DependencyContainer::getInstance()
->createDependenciesFor('mime.part')
);
if (!isset($charset)) {
$charset = Swift_DependencyContainer::getInstance()
->lookup('properties.charset');
}
$this->setBody($body);
$this->setCharset($charset);
if ($contentType) {
$this->setContentType($contentType);
}
}
}

Some files were not shown because too many files have changed in this diff Show More